fix binding of bool type in mysql driver
authorMark Brand <mabrand@mabrand.nl>
Tue, 30 Oct 2012 09:51:25 +0000 (10:51 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Thu, 8 Nov 2012 23:37:42 +0000 (00:37 +0100)
MYSQL_TYPE_TINY should be used for binding bool input value.
MYSQL_TYPE_LONG might be too big for bool, resulting in bools being
saved in the database as int 127. The problem was not specific to
the vendor's BOOL column type.

http://dev.mysql.com/doc/refman/5.0/en/numeric-type-overview.html
http://dev.mysql.com/doc/refman/5.0/en/c-api-prepared-statement-type-codes.html

Added generic autotest to make sure that binding bool works. All
drivers should pass this test.

Task-number: QTBUG-27763
Change-Id: I4e69f8e3b32fffb702ec9fa8a80ff5c50dea954b
Reviewed-by: Andy Shaw <andy.shaw@digia.com>
Reviewed-by: Mark Brand <mabrand@mabrand.nl>
src/sql/drivers/mysql/qsql_mysql.cpp
tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp

index 6cc9edf..182b0a4 100644 (file)
@@ -987,11 +987,16 @@ bool QMYSQLResult::exec()
                     break; }
                 case QVariant::UInt:
                 case QVariant::Int:
-                case QVariant::Bool:
                     currBind->buffer_type = MYSQL_TYPE_LONG;
                     currBind->buffer = data;
                     currBind->buffer_length = sizeof(int);
                     currBind->is_unsigned = (val.type() != QVariant::Int);
+                break;
+                case QVariant::Bool:
+                    currBind->buffer_type = MYSQL_TYPE_TINY;
+                    currBind->buffer = data;
+                    currBind->buffer_length = sizeof(bool);
+                    currBind->is_unsigned = false;
                     break;
                 case QVariant::Double:
                     currBind->buffer_type = MYSQL_TYPE_DOUBLE;
index 26768f2..5cb7c27 100644 (file)
@@ -161,6 +161,8 @@ private slots:
     void lastInsertId();
     void lastQuery_data() { generic_data(); }
     void lastQuery();
+    void bindBool_data() { generic_data(); }
+    void bindBool();
     void bindWithDoubleColonCastOperator_data() { generic_data(); }
     void bindWithDoubleColonCastOperator();
     void queryOnInvalidDatabase_data() { generic_data(); }
@@ -575,6 +577,39 @@ void tst_QSqlQuery::mysqlOutValues()
     QVERIFY_SQL( q, exec( "drop procedure " + qtestproc ) );
 }
 
+void tst_QSqlQuery::bindBool()
+{
+    // QTBUG-27763: bool value got converted to int 127 by mysql driver becuase sizeof(bool) < sizeof(int).
+    // The problem was the way the bool value from the application was handled. It doesn't matter
+    // whether the table column type is BOOL or INT. Use INT here because all DBMSs have it and all
+    // should pass this test.
+    QFETCH( QString, dbName );
+    QSqlDatabase db = QSqlDatabase::database( dbName );
+    CHECK_DATABASE( db );
+    QSqlQuery q(db);
+
+    const QString tableName(qTableName( "bindBool", __FILE__ ));
+    q.exec("DROP TABLE " + tableName);
+    QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id INT, flag INT NOT NULL, PRIMARY KEY(id))"));
+
+    for (int i = 0; i < 2; ++i) {
+        bool flag = i;
+        q.prepare("INSERT INTO " + tableName + " (id, flag) VALUES(:id, :flag)");
+        q.bindValue(":id", i);
+        q.bindValue(":flag", flag);
+        QVERIFY_SQL(q, exec());
+    }
+
+    QVERIFY_SQL(q, exec("SELECT id, flag FROM " + tableName));
+    for (int i = 0; i < 2; ++i) {
+        bool flag = i;
+        QVERIFY_SQL(q, next());
+        QCOMPARE(q.value(0).toInt(), i);
+        QCOMPARE(q.value(1).toBool(), flag);
+    }
+    QVERIFY_SQL(q, exec("DROP TABLE " + tableName));
+}
+
 void tst_QSqlQuery::oraOutValues()
 {
     QFETCH( QString, dbName );