Commit b8b79a0f authored by Thiago A. Correa's avatar Thiago A. Correa Committed by Qt by Nokia
Browse files

Fix error when inserting to tables with datetime fields with QODBC



SQL Server 10 introduced stricter rules for TIMESTAMP validation,
making it necessary to specify the decimal digits.
Other databases might do the same as well, so this patch introduces
a check for the TIMESTAMP column size and adjusts the decimal digits
parameter as needed.

Task-number: QTBUG-2192

Change-Id: If6d798c6c928ebda75bc474e49a07fbbfbe5816c
Reviewed-by: default avatarMark Brand <mabrand@mabrand.nl>
parent 5762da38
......@@ -54,6 +54,7 @@
#include <qstringlist.h>
#include <qvarlengtharray.h>
#include <qvector.h>
#include <qmath.h>
#include <QDebug>
#include <QSqlQuery>
......@@ -115,7 +116,7 @@ class QODBCDriverPrivate
public:
enum DefaultCase{Lower, Mixed, Upper, Sensitive};
QODBCDriverPrivate()
: hEnv(0), hDbc(0), unicode(false), useSchema(false), disconnectCount(0), isMySqlServer(false),
: hEnv(0), hDbc(0), unicode(false), useSchema(false), disconnectCount(0), datetime_precision(19), isMySqlServer(false),
isMSSqlServer(false), isFreeTDSDriver(false), hasSQLFetchScroll(true),
hasMultiResultSets(false), isQuoteInitialized(false), quote(QLatin1Char('"'))
{
......@@ -127,6 +128,7 @@ public:
bool unicode;
bool useSchema;
int disconnectCount;
int datetime_precision;
bool isMySqlServer;
bool isMSSqlServer;
bool isFreeTDSDriver;
......@@ -139,6 +141,7 @@ public:
void checkHasSQLFetchScroll();
void checkHasMultiResults();
void checkSchemaUsage();
void checkDateTimePrecision();
bool setConnectionOptions(const QString& connOpts);
void splitTableQualifier(const QString &qualifier, QString &catalog,
QString &schema, QString &table);
......@@ -1395,14 +1398,25 @@ bool QODBCResult::exec()
dt->hour = qdt.time().hour();
dt->minute = qdt.time().minute();
dt->second = qdt.time().second();
dt->fraction = qdt.time().msec() * 1000000;
int precision = d->driverPrivate->datetime_precision - 20; // (20 includes a separating period)
if (precision <= 0) {
dt->fraction = 0;
} else {
dt->fraction = qdt.time().msec() * 1000000;
// (How many leading digits do we want to keep? With SQL Server 2005, this should be 3: 123000000)
int keep = (int)qPow(10.0, 9 - qMin(9, precision));
dt->fraction /= keep * keep;
}
r = SQLBindParameter(d->hStmt,
i + 1,
qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
SQL_C_TIMESTAMP,
SQL_TIMESTAMP,
19,
0,
d->driverPrivate->datetime_precision,
precision,
(void *) dt,
0,
*ind == SQL_NULL_DATA ? ind : NULL);
......@@ -1887,6 +1901,7 @@ bool QODBCDriver::open(const QString & db,
d->checkSqlServer();
d->checkHasSQLFetchScroll();
d->checkHasMultiResults();
d->checkDateTimePrecision();
setOpen(true);
setOpenError(false);
if(d->isMSSqlServer) {
......@@ -2122,6 +2137,29 @@ void QODBCDriverPrivate::checkHasMultiResults()
#endif
}
void QODBCDriverPrivate::checkDateTimePrecision()
{
SQLINTEGER columnSize;
SQLHANDLE hStmt;
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
if (r != SQL_SUCCESS) {
return;
}
r = SQLGetTypeInfo(hStmt, SQL_TIMESTAMP);
if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
r = SQLFetch(hStmt);
if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO )
{
if (SQLGetData(hStmt, 3, SQL_INTEGER, &columnSize, sizeof(columnSize), 0) == SQL_SUCCESS) {
datetime_precision = (int)columnSize;
}
}
}
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
QSqlResult *QODBCDriver::createResult() const
{
return new QODBCResult(this, d);
......
......@@ -219,6 +219,8 @@ private slots:
void QTBUG_16967(); //clean close
void QTBUG_23895_data() { generic_data("QSQLITE"); }
void QTBUG_23895(); //sqlite boolean type
void QTBUG_2192_data() { generic_data(); }
void QTBUG_2192();
void sqlite_constraint_data() { generic_data("QSQLITE"); }
void sqlite_constraint();
......@@ -341,7 +343,8 @@ void tst_QSqlQuery::dropTestTables( QSqlDatabase db )
<< qTableName( "task_250026", __FILE__ )
<< qTableName( "task_234422", __FILE__ )
<< qTableName("test141895", __FILE__)
<< qTableName("qtest_oraOCINumber", __FILE__);
<< qTableName("qtest_oraOCINumber", __FILE__)
<< qTableName( "bug2192", __FILE__);
if ( db.driverName().startsWith("QPSQL") )
tablenames << qTableName("task_233829", __FILE__);
......@@ -3357,6 +3360,29 @@ void tst_QSqlQuery::QTBUG_23895()
QVERIFY(!q.next());
}
void tst_QSqlQuery::QTBUG_2192()
{
QFETCH( QString, dbName );
QSqlDatabase db = QSqlDatabase::database( dbName );
CHECK_DATABASE( db );
{
const QString tableName(qTableName("bug2192", __FILE__));
tst_Databases::safeDropTable( db, tableName );
QSqlQuery q(db);
QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (dt DATETIME)"));
QVERIFY_SQL(q, prepare("INSERT INTO " + tableName + " (dt) VALUES (?)"));
q.bindValue(0, QVariant(QDateTime(QDate(2012, 7, 4), QTime(23, 59, 59, 999))));
QVERIFY_SQL(q, exec());
// Check if value was stored with at least second precision.
QVERIFY_SQL(q, exec("SELECT dt FROM " + tableName));
QVERIFY_SQL(q, next());
QVERIFY(q.value(0).toDateTime().msecsTo(QDateTime(QDate(2012, 7, 4), QTime(23, 59, 59, 999))) < 1000 );
}
}
void tst_QSqlQuery::oraOCINumber()
{
QFETCH( QString, dbName );
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment