src/sql/drivers/mysql/qsql_mysql.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtSql module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qsql_mysql.h"
       
    43 
       
    44 #include <qcoreapplication.h>
       
    45 #include <qvariant.h>
       
    46 #include <qdatetime.h>
       
    47 #include <qsqlerror.h>
       
    48 #include <qsqlfield.h>
       
    49 #include <qsqlindex.h>
       
    50 #include <qsqlquery.h>
       
    51 #include <qsqlrecord.h>
       
    52 #include <qstringlist.h>
       
    53 #include <qtextcodec.h>
       
    54 #include <qvector.h>
       
    55 
       
    56 #include <qdebug.h>
       
    57 
       
    58 #ifdef Q_OS_WIN32
       
    59 // comment the next line out if you want to use MySQL/embedded on Win32 systems.
       
    60 // note that it will crash if you don't statically link to the mysql/e library!
       
    61 # define Q_NO_MYSQL_EMBEDDED
       
    62 #endif
       
    63 
       
    64 Q_DECLARE_METATYPE(MYSQL_RES*)
       
    65 Q_DECLARE_METATYPE(MYSQL*)
       
    66 
       
    67 #if MYSQL_VERSION_ID >= 40108
       
    68 Q_DECLARE_METATYPE(MYSQL_STMT*)
       
    69 #endif
       
    70 
       
    71 #if MYSQL_VERSION_ID >= 40100
       
    72 #  define Q_CLIENT_MULTI_STATEMENTS CLIENT_MULTI_STATEMENTS
       
    73 #else
       
    74 #  define Q_CLIENT_MULTI_STATEMENTS 0
       
    75 #endif
       
    76 
       
    77 QT_BEGIN_NAMESPACE
       
    78 
       
    79 class QMYSQLDriverPrivate
       
    80 {
       
    81 public:
       
    82     QMYSQLDriverPrivate() : mysql(0),
       
    83 #ifndef QT_NO_TEXTCODEC
       
    84         tc(QTextCodec::codecForLocale()),
       
    85 #else
       
    86         tc(0),
       
    87 #endif
       
    88         preparedQuerysEnabled(false) {}
       
    89     MYSQL *mysql;
       
    90     QTextCodec *tc;
       
    91 
       
    92     bool preparedQuerysEnabled;
       
    93 };
       
    94 
       
    95 static inline QString toUnicode(QTextCodec *tc, const char *str)
       
    96 {
       
    97 #ifdef QT_NO_TEXTCODEC
       
    98     Q_UNUSED(tc);
       
    99     return QString::fromLatin1(str);
       
   100 #else
       
   101     return tc->toUnicode(str);
       
   102 #endif
       
   103 }
       
   104 
       
   105 static inline QString toUnicode(QTextCodec *tc, const char *str, int length)
       
   106 {
       
   107 #ifdef QT_NO_TEXTCODEC
       
   108     Q_UNUSED(tc);
       
   109     return QString::fromLatin1(str, length);
       
   110 #else
       
   111     return tc->toUnicode(str, length);
       
   112 #endif
       
   113 }
       
   114 
       
   115 static inline QByteArray fromUnicode(QTextCodec *tc, const QString &str)
       
   116 {
       
   117 #ifdef QT_NO_TEXTCODEC
       
   118     Q_UNUSED(tc);
       
   119     return str.toLatin1();
       
   120 #else
       
   121     return tc->fromUnicode(str);
       
   122 #endif
       
   123 }
       
   124 
       
   125 static inline QVariant qDateFromString(const QString &val)
       
   126 {
       
   127 #ifdef QT_NO_DATESTRING
       
   128     Q_UNUSED(val);
       
   129     return QVariant(val);
       
   130 #else
       
   131     if (val.isEmpty())
       
   132         return QVariant(QDate());
       
   133     return QVariant(QDate::fromString(val, Qt::ISODate));
       
   134 #endif
       
   135 }
       
   136 
       
   137 static inline QVariant qTimeFromString(const QString &val)
       
   138 {
       
   139 #ifdef QT_NO_DATESTRING
       
   140     Q_UNUSED(val);
       
   141     return QVariant(val);
       
   142 #else
       
   143     if (val.isEmpty())
       
   144         return QVariant(QTime());
       
   145     return QVariant(QTime::fromString(val, Qt::ISODate));
       
   146 #endif
       
   147 }
       
   148 
       
   149 static inline QVariant qDateTimeFromString(QString &val)
       
   150 {
       
   151 #ifdef QT_NO_DATESTRING
       
   152     Q_UNUSED(val);
       
   153     return QVariant(val);
       
   154 #else
       
   155     if (val.isEmpty())
       
   156         return QVariant(QDateTime());
       
   157     if (val.length() == 14)
       
   158         // TIMESTAMPS have the format yyyyMMddhhmmss
       
   159         val.insert(4, QLatin1Char('-')).insert(7, QLatin1Char('-')).insert(10,
       
   160                     QLatin1Char('T')).insert(13, QLatin1Char(':')).insert(16, QLatin1Char(':'));
       
   161     return QVariant(QDateTime::fromString(val, Qt::ISODate));
       
   162 #endif
       
   163 }
       
   164 
       
   165 class QMYSQLResultPrivate : public QObject
       
   166 {
       
   167     Q_OBJECT
       
   168 public:
       
   169     QMYSQLResultPrivate(const QMYSQLDriver* dp, const QMYSQLResult* d) : driver(dp), result(0), q(d), 
       
   170         rowsAffected(0), hasBlobs(false)
       
   171 #if MYSQL_VERSION_ID >= 40108
       
   172         , stmt(0), meta(0), inBinds(0), outBinds(0)
       
   173 #endif
       
   174         , preparedQuery(false)
       
   175         {
       
   176             connect(dp, SIGNAL(destroyed()), this, SLOT(driverDestroyed()));
       
   177         }
       
   178 
       
   179     const QMYSQLDriver* driver;
       
   180     MYSQL_RES *result;
       
   181     MYSQL_ROW row;
       
   182     const QMYSQLResult* q;
       
   183 
       
   184     int rowsAffected;
       
   185 
       
   186     bool bindInValues();
       
   187     void bindBlobs();
       
   188 
       
   189     bool hasBlobs;
       
   190     struct QMyField
       
   191     {
       
   192         QMyField()
       
   193             : outField(0), nullIndicator(false), bufLength(0ul),
       
   194               myField(0), type(QVariant::Invalid)
       
   195         {}
       
   196         char *outField;
       
   197         my_bool nullIndicator;
       
   198         ulong bufLength;
       
   199         MYSQL_FIELD *myField;
       
   200         QVariant::Type type;
       
   201     };
       
   202 
       
   203     QVector<QMyField> fields;
       
   204 
       
   205 #if MYSQL_VERSION_ID >= 40108
       
   206     MYSQL_STMT* stmt;
       
   207     MYSQL_RES* meta;
       
   208 
       
   209     MYSQL_BIND *inBinds;
       
   210     MYSQL_BIND *outBinds;
       
   211 #endif
       
   212 
       
   213     bool preparedQuery;
       
   214 
       
   215 private Q_SLOTS:
       
   216     void driverDestroyed() { driver = NULL; }
       
   217 };
       
   218 
       
   219 #ifndef QT_NO_TEXTCODEC
       
   220 static QTextCodec* codec(MYSQL* mysql)
       
   221 {
       
   222 #if MYSQL_VERSION_ID >= 32321
       
   223     QTextCodec* heuristicCodec = QTextCodec::codecForName(mysql_character_set_name(mysql));
       
   224     if (heuristicCodec)
       
   225         return heuristicCodec;
       
   226 #endif
       
   227     return QTextCodec::codecForLocale();
       
   228 }
       
   229 #endif // QT_NO_TEXTCODEC
       
   230 
       
   231 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
       
   232                             const QMYSQLDriverPrivate* p)
       
   233 {
       
   234     const char *cerr = p->mysql ? mysql_error(p->mysql) : 0;
       
   235     return QSqlError(QLatin1String("QMYSQL: ") + err,
       
   236                      p->tc ? toUnicode(p->tc, cerr) : QString::fromLatin1(cerr),
       
   237                      type, mysql_errno(p->mysql));
       
   238 }
       
   239 
       
   240 
       
   241 static QVariant::Type qDecodeMYSQLType(int mysqltype, uint flags)
       
   242 {
       
   243     QVariant::Type type;
       
   244     switch (mysqltype) {
       
   245     case FIELD_TYPE_TINY :
       
   246     case FIELD_TYPE_SHORT :
       
   247     case FIELD_TYPE_LONG :
       
   248     case FIELD_TYPE_INT24 :
       
   249         type = (flags & UNSIGNED_FLAG) ? QVariant::UInt : QVariant::Int;
       
   250         break;
       
   251     case FIELD_TYPE_YEAR :
       
   252         type = QVariant::Int;
       
   253         break;
       
   254     case FIELD_TYPE_LONGLONG :
       
   255         type = (flags & UNSIGNED_FLAG) ? QVariant::ULongLong : QVariant::LongLong;
       
   256         break;
       
   257     case FIELD_TYPE_FLOAT :
       
   258     case FIELD_TYPE_DOUBLE :
       
   259     case FIELD_TYPE_DECIMAL :
       
   260 #if defined(FIELD_TYPE_NEWDECIMAL)
       
   261     case FIELD_TYPE_NEWDECIMAL:
       
   262 #endif
       
   263         type = QVariant::Double;
       
   264         break;
       
   265     case FIELD_TYPE_DATE :
       
   266         type = QVariant::Date;
       
   267         break;
       
   268     case FIELD_TYPE_TIME :
       
   269         type = QVariant::Time;
       
   270         break;
       
   271     case FIELD_TYPE_DATETIME :
       
   272     case FIELD_TYPE_TIMESTAMP :
       
   273         type = QVariant::DateTime;
       
   274         break;
       
   275     case FIELD_TYPE_STRING :
       
   276     case FIELD_TYPE_VAR_STRING :
       
   277     case FIELD_TYPE_BLOB :
       
   278     case FIELD_TYPE_TINY_BLOB :
       
   279     case FIELD_TYPE_MEDIUM_BLOB :
       
   280     case FIELD_TYPE_LONG_BLOB :
       
   281         type = (flags & BINARY_FLAG) ? QVariant::ByteArray : QVariant::String;
       
   282         break;
       
   283     default:
       
   284     case FIELD_TYPE_ENUM :
       
   285     case FIELD_TYPE_SET :
       
   286         type = QVariant::String;
       
   287         break;
       
   288     }
       
   289     return type;
       
   290 }
       
   291 
       
   292 static QSqlField qToField(MYSQL_FIELD *field, QTextCodec *tc)
       
   293 {
       
   294     QSqlField f(toUnicode(tc, field->name),
       
   295                 qDecodeMYSQLType(int(field->type), field->flags));
       
   296     f.setRequired(IS_NOT_NULL(field->flags));
       
   297     f.setLength(field->length);
       
   298     f.setPrecision(field->decimals);
       
   299     f.setSqlType(field->type);
       
   300     f.setAutoValue(field->flags & AUTO_INCREMENT_FLAG);
       
   301     return f;
       
   302 }
       
   303 
       
   304 #if MYSQL_VERSION_ID >= 40108
       
   305 
       
   306 static QSqlError qMakeStmtError(const QString& err, QSqlError::ErrorType type,
       
   307                             MYSQL_STMT* stmt)
       
   308 {
       
   309     const char *cerr = mysql_stmt_error(stmt);
       
   310     return QSqlError(QLatin1String("QMYSQL3: ") + err,
       
   311                      QString::fromLatin1(cerr),
       
   312                      type, mysql_stmt_errno(stmt));
       
   313 }
       
   314 
       
   315 static bool qIsBlob(int t)
       
   316 {
       
   317     return t == MYSQL_TYPE_TINY_BLOB
       
   318            || t == MYSQL_TYPE_BLOB
       
   319            || t == MYSQL_TYPE_MEDIUM_BLOB
       
   320            || t == MYSQL_TYPE_LONG_BLOB;
       
   321 }
       
   322 
       
   323 void QMYSQLResultPrivate::bindBlobs()
       
   324 {
       
   325     int i;
       
   326     MYSQL_FIELD *fieldInfo;
       
   327     MYSQL_BIND *bind;
       
   328 
       
   329     for(i = 0; i < fields.count(); ++i) {
       
   330         fieldInfo = fields.at(i).myField;
       
   331         if (qIsBlob(inBinds[i].buffer_type) && meta && fieldInfo) {
       
   332             bind = &inBinds[i];
       
   333             bind->buffer_length = fieldInfo->max_length;
       
   334             delete[] static_cast<char*>(bind->buffer);
       
   335             bind->buffer = new char[fieldInfo->max_length];
       
   336             fields[i].outField = static_cast<char*>(bind->buffer);
       
   337         }
       
   338     }
       
   339 }
       
   340 
       
   341 bool QMYSQLResultPrivate::bindInValues()
       
   342 {
       
   343     MYSQL_BIND *bind;
       
   344     char *field;
       
   345     int i = 0;
       
   346 
       
   347     if (!meta)
       
   348         meta = mysql_stmt_result_metadata(stmt);
       
   349     if (!meta)
       
   350         return false;
       
   351 
       
   352     fields.resize(mysql_num_fields(meta));
       
   353 
       
   354     inBinds = new MYSQL_BIND[fields.size()];
       
   355     memset(inBinds, 0, fields.size() * sizeof(MYSQL_BIND));
       
   356 
       
   357     MYSQL_FIELD *fieldInfo;
       
   358 
       
   359     while((fieldInfo = mysql_fetch_field(meta))) {
       
   360         QMyField &f = fields[i];
       
   361         f.myField = fieldInfo;
       
   362 
       
   363         f.type = qDecodeMYSQLType(fieldInfo->type, fieldInfo->flags);
       
   364         if (qIsBlob(fieldInfo->type)) {
       
   365             // the size of a blob-field is available as soon as we call
       
   366             // mysql_stmt_store_result()
       
   367             // after mysql_stmt_exec() in QMYSQLResult::exec()
       
   368             fieldInfo->length = 0;
       
   369             hasBlobs = true;
       
   370         } else {
       
   371             fieldInfo->type = MYSQL_TYPE_STRING;
       
   372         }
       
   373         bind = &inBinds[i];
       
   374         field = new char[fieldInfo->length + 1];
       
   375         memset(field, 0, fieldInfo->length + 1);
       
   376 
       
   377         bind->buffer_type = fieldInfo->type;
       
   378         bind->buffer = field;
       
   379         bind->buffer_length = f.bufLength = fieldInfo->length + 1;
       
   380         bind->is_null = &f.nullIndicator;
       
   381         bind->length = &f.bufLength;
       
   382         f.outField=field;
       
   383 
       
   384         ++i;
       
   385     }
       
   386     return true;
       
   387 }
       
   388 #endif
       
   389 
       
   390 QMYSQLResult::QMYSQLResult(const QMYSQLDriver* db)
       
   391 : QSqlResult(db)
       
   392 {
       
   393     d = new QMYSQLResultPrivate(db, this);
       
   394 }
       
   395 
       
   396 QMYSQLResult::~QMYSQLResult()
       
   397 {
       
   398     cleanup();
       
   399     delete d;
       
   400 }
       
   401 
       
   402 QVariant QMYSQLResult::handle() const
       
   403 {
       
   404 #if MYSQL_VERSION_ID >= 40108
       
   405     if(d->preparedQuery)
       
   406         return d->meta ? qVariantFromValue(d->meta) : qVariantFromValue(d->stmt);
       
   407     else
       
   408 #endif
       
   409         return qVariantFromValue(d->result);
       
   410 }
       
   411 
       
   412 void QMYSQLResult::cleanup()
       
   413 {
       
   414     if (d->result)
       
   415         mysql_free_result(d->result);
       
   416 
       
   417 // must iterate trough leftover result sets from multi-selects or stored procedures
       
   418 // if this isn't done subsequent queries will fail with "Commands out of sync"
       
   419 #if MYSQL_VERSION_ID >= 40100
       
   420     while (d->driver && d->driver->d->mysql && mysql_next_result(d->driver->d->mysql) == 0) {
       
   421         MYSQL_RES *res = mysql_store_result(d->driver->d->mysql);
       
   422         if (res)
       
   423             mysql_free_result(res);
       
   424     }
       
   425 #endif
       
   426 
       
   427 #if MYSQL_VERSION_ID >= 40108
       
   428     if (d->stmt) {
       
   429         if (mysql_stmt_close(d->stmt))
       
   430             qWarning("QMYSQLResult::cleanup: unable to free statement handle");
       
   431         d->stmt = 0;
       
   432     }
       
   433 
       
   434     if (d->meta) {
       
   435         mysql_free_result(d->meta);
       
   436         d->meta = 0;
       
   437     }
       
   438 
       
   439     int i;
       
   440     for (i = 0; i < d->fields.count(); ++i)
       
   441         delete[] d->fields[i].outField;
       
   442 
       
   443     if (d->outBinds) {
       
   444         delete[] d->outBinds;
       
   445         d->outBinds = 0;
       
   446     }
       
   447 
       
   448     if (d->inBinds) {
       
   449         delete[] d->inBinds;
       
   450         d->inBinds = 0;
       
   451     }
       
   452 #endif
       
   453 
       
   454     d->hasBlobs = false;
       
   455     d->fields.clear();
       
   456     d->result = NULL;
       
   457     d->row = NULL;
       
   458     setAt(-1);
       
   459     setActive(false);
       
   460 }
       
   461 
       
   462 bool QMYSQLResult::fetch(int i)
       
   463 {
       
   464     if(!d->driver)
       
   465         return false;
       
   466     if (isForwardOnly()) { // fake a forward seek
       
   467         if (at() < i) {
       
   468             int x = i - at();
       
   469             while (--x && fetchNext()) {};
       
   470             return fetchNext();
       
   471         } else {
       
   472             return false;
       
   473         }
       
   474     }
       
   475     if (at() == i)
       
   476         return true;
       
   477     if (d->preparedQuery) {
       
   478 #if MYSQL_VERSION_ID >= 40108
       
   479         mysql_stmt_data_seek(d->stmt, i);
       
   480 
       
   481         int nRC = mysql_stmt_fetch(d->stmt);
       
   482         if (nRC) {
       
   483 #ifdef MYSQL_DATA_TRUNCATED
       
   484             if (nRC == 1 || nRC == MYSQL_DATA_TRUNCATED)
       
   485 #else
       
   486             if (nRC == 1)
       
   487 #endif
       
   488                 setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
       
   489                          "Unable to fetch data"), QSqlError::StatementError, d->stmt));
       
   490             return false;
       
   491         }
       
   492 #else
       
   493         return false;
       
   494 #endif
       
   495     } else {
       
   496         mysql_data_seek(d->result, i);
       
   497         d->row = mysql_fetch_row(d->result);
       
   498         if (!d->row)
       
   499             return false;
       
   500     }
       
   501 
       
   502     setAt(i);
       
   503     return true;
       
   504 }
       
   505 
       
   506 bool QMYSQLResult::fetchNext()
       
   507 {
       
   508     if(!d->driver)
       
   509         return false;
       
   510     if (d->preparedQuery) {
       
   511 #if MYSQL_VERSION_ID >= 40108
       
   512         if (mysql_stmt_fetch(d->stmt))
       
   513             return false;
       
   514 #else
       
   515         return false;
       
   516 #endif
       
   517     } else {
       
   518     d->row = mysql_fetch_row(d->result);
       
   519     if (!d->row)
       
   520         return false;
       
   521     }
       
   522     setAt(at() + 1);
       
   523     return true;
       
   524 }
       
   525 
       
   526 bool QMYSQLResult::fetchLast()
       
   527 {
       
   528     if(!d->driver)
       
   529         return false;
       
   530     if (isForwardOnly()) { // fake this since MySQL can't seek on forward only queries
       
   531         bool success = fetchNext(); // did we move at all?
       
   532         while (fetchNext()) {};
       
   533         return success;
       
   534     }
       
   535 
       
   536     my_ulonglong numRows;
       
   537     if (d->preparedQuery) {
       
   538 #if MYSQL_VERSION_ID >= 40108
       
   539         numRows = mysql_stmt_num_rows(d->stmt);
       
   540 #else
       
   541         numRows = 0;
       
   542 #endif
       
   543     } else {
       
   544         numRows = mysql_num_rows(d->result);
       
   545     }
       
   546     if (at() == int(numRows))
       
   547         return true;
       
   548     if (!numRows)
       
   549         return false;
       
   550     return fetch(numRows - 1);
       
   551 }
       
   552 
       
   553 bool QMYSQLResult::fetchFirst()
       
   554 {
       
   555     if (at() == 0)
       
   556         return true;
       
   557 
       
   558     if (isForwardOnly())
       
   559         return (at() == QSql::BeforeFirstRow) ? fetchNext() : false;
       
   560     return fetch(0);
       
   561 }
       
   562 
       
   563 QVariant QMYSQLResult::data(int field)
       
   564 {
       
   565 
       
   566     if (!isSelect() || field >= d->fields.count()) {
       
   567         qWarning("QMYSQLResult::data: column %d out of range", field);
       
   568         return QVariant();
       
   569     }
       
   570 
       
   571     if (!d->driver)
       
   572         return QVariant();
       
   573 
       
   574     int fieldLength = 0;
       
   575     const QMYSQLResultPrivate::QMyField &f = d->fields.at(field);
       
   576     QString val;
       
   577     if (d->preparedQuery) {
       
   578         if (f.nullIndicator)
       
   579             return QVariant(f.type);
       
   580 
       
   581         if (f.type != QVariant::ByteArray)
       
   582             val = toUnicode(d->driver->d->tc, f.outField, f.bufLength);
       
   583     } else {
       
   584         if (d->row[field] == NULL) {
       
   585             // NULL value
       
   586             return QVariant(f.type);
       
   587         }
       
   588         fieldLength = mysql_fetch_lengths(d->result)[field];
       
   589         if (f.type != QVariant::ByteArray)
       
   590             val = toUnicode(d->driver->d->tc, d->row[field], fieldLength);
       
   591     }
       
   592 
       
   593     switch(f.type) {
       
   594     case QVariant::LongLong:
       
   595         return QVariant(val.toLongLong());
       
   596     case QVariant::ULongLong:
       
   597         return QVariant(val.toULongLong());
       
   598     case QVariant::Int:
       
   599         return QVariant(val.toInt());
       
   600     case QVariant::UInt:
       
   601         return QVariant(val.toUInt());
       
   602     case QVariant::Double: {
       
   603         QVariant v;
       
   604         bool ok=false;
       
   605         double dbl = val.toDouble(&ok);
       
   606         switch(numericalPrecisionPolicy()) {
       
   607             case QSql::LowPrecisionInt32:
       
   608                 v=QVariant(dbl).toInt();
       
   609                 break;
       
   610             case QSql::LowPrecisionInt64:
       
   611                 v = QVariant(dbl).toLongLong();
       
   612                 break;
       
   613             case QSql::LowPrecisionDouble:
       
   614                 v = QVariant(dbl);
       
   615                 break;
       
   616             case QSql::HighPrecision:
       
   617             default:
       
   618                 v = val;
       
   619                 ok = true;
       
   620                 break;
       
   621         }
       
   622         if(ok)
       
   623             return v;
       
   624         else
       
   625             return QVariant();
       
   626     }
       
   627         return QVariant(val.toDouble());
       
   628     case QVariant::Date:
       
   629         return qDateFromString(val);
       
   630     case QVariant::Time:
       
   631         return qTimeFromString(val);
       
   632     case QVariant::DateTime:
       
   633         return qDateTimeFromString(val);
       
   634     case QVariant::ByteArray: {
       
   635 
       
   636         QByteArray ba;
       
   637         if (d->preparedQuery) {
       
   638             ba = QByteArray(f.outField, f.bufLength);
       
   639         } else {
       
   640             ba = QByteArray(d->row[field], fieldLength);
       
   641         }
       
   642         return QVariant(ba);
       
   643     }
       
   644     default:
       
   645     case QVariant::String:
       
   646         return QVariant(val);
       
   647     }
       
   648     qWarning("QMYSQLResult::data: unknown data type");
       
   649     return QVariant();
       
   650 }
       
   651 
       
   652 bool QMYSQLResult::isNull(int field)
       
   653 {
       
   654    if (d->preparedQuery)
       
   655        return d->fields.at(field).nullIndicator;
       
   656    else
       
   657        return d->row[field] == NULL;
       
   658 }
       
   659 
       
   660 bool QMYSQLResult::reset (const QString& query)
       
   661 {
       
   662     if (!driver() || !driver()->isOpen() || driver()->isOpenError() || !d->driver)
       
   663         return false;
       
   664 
       
   665     d->preparedQuery = false;
       
   666 
       
   667     cleanup();
       
   668 
       
   669     const QByteArray encQuery(fromUnicode(d->driver->d->tc, query));
       
   670     if (mysql_real_query(d->driver->d->mysql, encQuery.data(), encQuery.length())) {
       
   671         setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to execute query"),
       
   672                      QSqlError::StatementError, d->driver->d));
       
   673         return false;
       
   674     }
       
   675     d->result = mysql_store_result(d->driver->d->mysql);
       
   676     if (!d->result && mysql_field_count(d->driver->d->mysql) > 0) {
       
   677         setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to store result"),
       
   678                     QSqlError::StatementError, d->driver->d));
       
   679         return false;
       
   680     }
       
   681     int numFields = mysql_field_count(d->driver->d->mysql);
       
   682     setSelect(numFields != 0);
       
   683     d->fields.resize(numFields);
       
   684     d->rowsAffected = mysql_affected_rows(d->driver->d->mysql);
       
   685 
       
   686     if (isSelect()) {
       
   687         for(int i = 0; i < numFields; i++) {
       
   688             MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
       
   689             d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
       
   690         }
       
   691         setAt(QSql::BeforeFirstRow);
       
   692     }
       
   693     setActive(true);
       
   694     return isActive();
       
   695 }
       
   696 
       
   697 int QMYSQLResult::size()
       
   698 {
       
   699     if (d->driver && isSelect())
       
   700         if (d->preparedQuery)
       
   701 #if MYSQL_VERSION_ID >= 40108
       
   702             return mysql_stmt_num_rows(d->stmt);
       
   703 #else
       
   704             return -1;
       
   705 #endif
       
   706         else
       
   707             return int(mysql_num_rows(d->result));
       
   708     else
       
   709         return -1;
       
   710 }
       
   711 
       
   712 int QMYSQLResult::numRowsAffected()
       
   713 {
       
   714     return d->rowsAffected;
       
   715 }
       
   716 
       
   717 QVariant QMYSQLResult::lastInsertId() const
       
   718 {
       
   719     if (!isActive() || !d->driver)
       
   720         return QVariant();
       
   721 
       
   722     if (d->preparedQuery) {
       
   723 #if MYSQL_VERSION_ID >= 40108
       
   724         quint64 id = mysql_stmt_insert_id(d->stmt);
       
   725         if (id)
       
   726             return QVariant(id);
       
   727 #endif
       
   728     } else {
       
   729         quint64 id = mysql_insert_id(d->driver->d->mysql);
       
   730         if (id)
       
   731             return QVariant(id);
       
   732     }
       
   733     return QVariant();
       
   734 }
       
   735 
       
   736 QSqlRecord QMYSQLResult::record() const
       
   737 {
       
   738     QSqlRecord info;
       
   739     MYSQL_RES *res;
       
   740     if (!isActive() || !isSelect() || !d->driver)
       
   741         return info;
       
   742 
       
   743 #if MYSQL_VERSION_ID >= 40108
       
   744     res = d->preparedQuery ? d->meta : d->result;
       
   745 #else
       
   746     res = d->result;
       
   747 #endif
       
   748 
       
   749     if (!mysql_errno(d->driver->d->mysql)) {
       
   750         mysql_field_seek(res, 0);
       
   751         MYSQL_FIELD* field = mysql_fetch_field(res);
       
   752         while(field) {
       
   753             info.append(qToField(field, d->driver->d->tc));
       
   754             field = mysql_fetch_field(res);
       
   755         }
       
   756     }
       
   757     mysql_field_seek(res, 0);
       
   758     return info;
       
   759 }
       
   760 
       
   761 bool QMYSQLResult::nextResult()
       
   762 {
       
   763     if(!d->driver)
       
   764         return false;
       
   765 #if MYSQL_VERSION_ID >= 40100 
       
   766     setAt(-1);
       
   767     setActive(false);
       
   768 
       
   769     if (d->result && isSelect())
       
   770         mysql_free_result(d->result);
       
   771     d->result = 0;
       
   772     setSelect(false);
       
   773    
       
   774     for (int i = 0; i < d->fields.count(); ++i)
       
   775         delete[] d->fields[i].outField;
       
   776     d->fields.clear();
       
   777 
       
   778     int status = mysql_next_result(d->driver->d->mysql);
       
   779     if (status > 0) {
       
   780         setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to execute next query"),
       
   781                      QSqlError::StatementError, d->driver->d));
       
   782         return false;
       
   783     } else if (status == -1) {
       
   784         return false;   // No more result sets
       
   785     }
       
   786 
       
   787     d->result = mysql_store_result(d->driver->d->mysql);
       
   788     int numFields = mysql_field_count(d->driver->d->mysql);
       
   789     if (!d->result && numFields > 0) {
       
   790         setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to store next result"),
       
   791                      QSqlError::StatementError, d->driver->d));
       
   792         return false;
       
   793     }
       
   794 
       
   795     setSelect(numFields > 0);
       
   796     d->fields.resize(numFields);
       
   797     d->rowsAffected = mysql_affected_rows(d->driver->d->mysql);
       
   798 
       
   799     if (isSelect()) {
       
   800         for (int i = 0; i < numFields; i++) {
       
   801             MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
       
   802             d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
       
   803         }
       
   804     }
       
   805 
       
   806     setActive(true);
       
   807     return true;
       
   808 #else
       
   809     return false;
       
   810 #endif
       
   811 }
       
   812 
       
   813 void QMYSQLResult::virtual_hook(int id, void *data)
       
   814 {
       
   815     switch (id) {
       
   816     case QSqlResult::NextResult:
       
   817         Q_ASSERT(data);
       
   818         *static_cast<bool*>(data) = nextResult();
       
   819         break;
       
   820     default:
       
   821         QSqlResult::virtual_hook(id, data);
       
   822     }
       
   823 }
       
   824 
       
   825 
       
   826 #if MYSQL_VERSION_ID >= 40108
       
   827 
       
   828 static MYSQL_TIME *toMySqlDate(QDate date, QTime time, QVariant::Type type)
       
   829 {
       
   830     Q_ASSERT(type == QVariant::Time || type == QVariant::Date
       
   831              || type == QVariant::DateTime);
       
   832 
       
   833     MYSQL_TIME *myTime = new MYSQL_TIME;
       
   834     memset(myTime, 0, sizeof(MYSQL_TIME));
       
   835 
       
   836     if (type == QVariant::Time || type == QVariant::DateTime) {
       
   837         myTime->hour = time.hour();
       
   838         myTime->minute = time.minute();
       
   839         myTime->second = time.second();
       
   840         myTime->second_part = time.msec();
       
   841     }
       
   842     if (type == QVariant::Date || type == QVariant::DateTime) {
       
   843         myTime->year = date.year();
       
   844         myTime->month = date.month();
       
   845         myTime->day = date.day();
       
   846     }
       
   847 
       
   848     return myTime;
       
   849 }
       
   850 
       
   851 bool QMYSQLResult::prepare(const QString& query)
       
   852 {
       
   853     if(!d->driver)
       
   854         return false;
       
   855 #if MYSQL_VERSION_ID >= 40108
       
   856     cleanup();
       
   857     if (!d->driver->d->preparedQuerysEnabled)
       
   858         return QSqlResult::prepare(query);
       
   859 
       
   860     int r;
       
   861 
       
   862     if (query.isEmpty())
       
   863         return false;
       
   864 
       
   865     if (!d->stmt)
       
   866         d->stmt = mysql_stmt_init(d->driver->d->mysql);
       
   867     if (!d->stmt) {
       
   868         setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to prepare statement"),
       
   869                      QSqlError::StatementError, d->driver->d));
       
   870         return false;
       
   871     }
       
   872 
       
   873     const QByteArray encQuery(fromUnicode(d->driver->d->tc, query));
       
   874     r = mysql_stmt_prepare(d->stmt, encQuery.constData(), encQuery.length());
       
   875     if (r != 0) {
       
   876         setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
       
   877                      "Unable to prepare statement"), QSqlError::StatementError, d->stmt));
       
   878         cleanup();
       
   879         return false;
       
   880     }
       
   881 
       
   882     if (mysql_stmt_param_count(d->stmt) > 0) {// allocate memory for outvalues
       
   883         d->outBinds = new MYSQL_BIND[mysql_stmt_param_count(d->stmt)];
       
   884     }
       
   885 
       
   886     setSelect(d->bindInValues());
       
   887     d->preparedQuery = true;
       
   888     return true;
       
   889 #else
       
   890     return false;
       
   891 #endif
       
   892 }
       
   893 
       
   894 bool QMYSQLResult::exec()
       
   895 {
       
   896     if (!d->driver)
       
   897         return false;
       
   898     if (!d->preparedQuery)
       
   899         return QSqlResult::exec();
       
   900     if (!d->stmt)
       
   901         return false;
       
   902 
       
   903     int r = 0;
       
   904     MYSQL_BIND* currBind;
       
   905     QVector<MYSQL_TIME *> timeVector;
       
   906     QVector<QByteArray> stringVector;
       
   907     QVector<my_bool> nullVector;
       
   908 
       
   909     const QVector<QVariant> values = boundValues();
       
   910 
       
   911     r = mysql_stmt_reset(d->stmt);
       
   912     if (r != 0) {
       
   913         setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
       
   914                      "Unable to reset statement"), QSqlError::StatementError, d->stmt));
       
   915         return false;
       
   916     }
       
   917 
       
   918     if (mysql_stmt_param_count(d->stmt) > 0 &&
       
   919         mysql_stmt_param_count(d->stmt) == (uint)values.count()) {
       
   920 
       
   921         nullVector.resize(values.count());
       
   922         for (int i = 0; i < values.count(); ++i) {
       
   923             const QVariant &val = boundValues().at(i);
       
   924             void *data = const_cast<void *>(val.constData());
       
   925 
       
   926             currBind = &d->outBinds[i];
       
   927 
       
   928             nullVector[i] = static_cast<my_bool>(val.isNull());
       
   929             currBind->is_null = &nullVector[i];
       
   930             currBind->length = 0;
       
   931             currBind->is_unsigned = 0;
       
   932 
       
   933             switch (val.type()) {
       
   934                 case QVariant::ByteArray:
       
   935                     currBind->buffer_type = MYSQL_TYPE_BLOB;
       
   936                     currBind->buffer = const_cast<char *>(val.toByteArray().constData());
       
   937                     currBind->buffer_length = val.toByteArray().size();
       
   938                     break;
       
   939 
       
   940                 case QVariant::Time:
       
   941                 case QVariant::Date:
       
   942                 case QVariant::DateTime: {
       
   943                     MYSQL_TIME *myTime = toMySqlDate(val.toDate(), val.toTime(), val.type());
       
   944                     timeVector.append(myTime);
       
   945 
       
   946                     currBind->buffer = myTime;
       
   947                     switch(val.type()) {
       
   948                     case QVariant::Time:
       
   949                         currBind->buffer_type = MYSQL_TYPE_TIME;
       
   950                         myTime->time_type = MYSQL_TIMESTAMP_TIME;
       
   951                         break;
       
   952                     case QVariant::Date:
       
   953                         currBind->buffer_type = MYSQL_TYPE_DATE;
       
   954                         myTime->time_type = MYSQL_TIMESTAMP_DATE;
       
   955                         break;
       
   956                     case QVariant::DateTime:
       
   957                         currBind->buffer_type = MYSQL_TYPE_DATETIME;
       
   958                         myTime->time_type = MYSQL_TIMESTAMP_DATETIME;
       
   959                         break;
       
   960                     default:
       
   961                         break;
       
   962                     }
       
   963                     currBind->buffer_length = sizeof(MYSQL_TIME);
       
   964                     currBind->length = 0;
       
   965                     break; }
       
   966                 case QVariant::UInt:
       
   967                 case QVariant::Int:
       
   968                 case QVariant::Bool:
       
   969                     currBind->buffer_type = MYSQL_TYPE_LONG;
       
   970                     currBind->buffer = data;
       
   971                     currBind->buffer_length = sizeof(int);
       
   972                     currBind->is_unsigned = (val.type() != QVariant::Int);
       
   973                     break;
       
   974                 case QVariant::Double:
       
   975                     currBind->buffer_type = MYSQL_TYPE_DOUBLE;
       
   976                     currBind->buffer = data;
       
   977                     currBind->buffer_length = sizeof(double);
       
   978                     break;
       
   979                 case QVariant::LongLong:
       
   980                 case QVariant::ULongLong:
       
   981                     currBind->buffer_type = MYSQL_TYPE_LONGLONG;
       
   982                     currBind->buffer = data;
       
   983                     currBind->buffer_length = sizeof(qint64);
       
   984                     currBind->is_unsigned = (val.type() == QVariant::ULongLong);
       
   985                     break;
       
   986                 case QVariant::String:
       
   987                 default: {
       
   988                     QByteArray ba = fromUnicode(d->driver->d->tc, val.toString());
       
   989                     stringVector.append(ba);
       
   990                     currBind->buffer_type = MYSQL_TYPE_STRING;
       
   991                     currBind->buffer = const_cast<char *>(ba.constData());
       
   992                     currBind->buffer_length = ba.length();
       
   993                     break; }
       
   994             }
       
   995         }
       
   996 
       
   997         r = mysql_stmt_bind_param(d->stmt, d->outBinds);
       
   998         if (r != 0) {
       
   999             setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
       
  1000                          "Unable to bind value"), QSqlError::StatementError, d->stmt));
       
  1001             qDeleteAll(timeVector);
       
  1002             return false;
       
  1003         }
       
  1004     }
       
  1005     r = mysql_stmt_execute(d->stmt);
       
  1006 
       
  1007     qDeleteAll(timeVector);
       
  1008 
       
  1009     if (r != 0) {
       
  1010         setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
       
  1011                      "Unable to execute statement"), QSqlError::StatementError, d->stmt));
       
  1012         return false;
       
  1013     }
       
  1014     //if there is meta-data there is also data
       
  1015     setSelect(d->meta);
       
  1016 
       
  1017     d->rowsAffected = mysql_stmt_affected_rows(d->stmt);
       
  1018 
       
  1019     if (isSelect()) {
       
  1020         my_bool update_max_length = true;
       
  1021 
       
  1022         r = mysql_stmt_bind_result(d->stmt, d->inBinds);
       
  1023         if (r != 0) {
       
  1024             setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
       
  1025                          "Unable to bind outvalues"), QSqlError::StatementError, d->stmt));
       
  1026             return false;
       
  1027         }
       
  1028         if (d->hasBlobs)
       
  1029             mysql_stmt_attr_set(d->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &update_max_length);
       
  1030 
       
  1031         r = mysql_stmt_store_result(d->stmt);
       
  1032         if (r != 0) {
       
  1033             setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
       
  1034                          "Unable to store statement results"), QSqlError::StatementError, d->stmt));
       
  1035             return false;
       
  1036         }
       
  1037 
       
  1038         if (d->hasBlobs) {
       
  1039             // mysql_stmt_store_result() with STMT_ATTR_UPDATE_MAX_LENGTH set to true crashes
       
  1040             // when called without a preceding call to mysql_stmt_bind_result()
       
  1041             // in versions < 4.1.8
       
  1042             d->bindBlobs();
       
  1043             r = mysql_stmt_bind_result(d->stmt, d->inBinds);
       
  1044             if (r != 0) {
       
  1045                 setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
       
  1046                              "Unable to bind outvalues"), QSqlError::StatementError, d->stmt));
       
  1047                 return false;
       
  1048             }
       
  1049         }
       
  1050         setAt(QSql::BeforeFirstRow);
       
  1051     }
       
  1052     setActive(true);
       
  1053     return true;
       
  1054 }
       
  1055 #endif
       
  1056 /////////////////////////////////////////////////////////
       
  1057 
       
  1058 static int qMySqlConnectionCount = 0;
       
  1059 static bool qMySqlInitHandledByUser = false;
       
  1060 
       
  1061 static void qLibraryInit()
       
  1062 {
       
  1063 #ifndef Q_NO_MYSQL_EMBEDDED
       
  1064 # if MYSQL_VERSION_ID >= 40000
       
  1065     if (qMySqlInitHandledByUser || qMySqlConnectionCount > 1)
       
  1066         return;
       
  1067 
       
  1068 # if (MYSQL_VERSION_ID >= 40110 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50003
       
  1069     if (mysql_library_init(0, 0, 0)) {
       
  1070 # else
       
  1071     if (mysql_server_init(0, 0, 0)) {
       
  1072 # endif
       
  1073         qWarning("QMYSQLDriver::qServerInit: unable to start server.");
       
  1074     }
       
  1075 # endif // MYSQL_VERSION_ID
       
  1076 #endif // Q_NO_MYSQL_EMBEDDED
       
  1077 }
       
  1078 
       
  1079 static void qLibraryEnd()
       
  1080 {
       
  1081 #ifndef Q_NO_MYSQL_EMBEDDED
       
  1082 # if MYSQL_VERSION_ID > 40000
       
  1083 #  if (MYSQL_VERSION_ID >= 40110 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50003
       
  1084     mysql_library_end();
       
  1085 #  else
       
  1086     mysql_server_end();
       
  1087 #  endif
       
  1088 # endif
       
  1089 #endif
       
  1090 }
       
  1091 
       
  1092 QMYSQLDriver::QMYSQLDriver(QObject * parent)
       
  1093     : QSqlDriver(parent)
       
  1094 {
       
  1095     init();
       
  1096     qLibraryInit();
       
  1097 }
       
  1098 
       
  1099 /*!
       
  1100     Create a driver instance with the open connection handle, \a con.
       
  1101     The instance's parent (owner) is \a parent.
       
  1102 */
       
  1103 
       
  1104 QMYSQLDriver::QMYSQLDriver(MYSQL * con, QObject * parent)
       
  1105     : QSqlDriver(parent)
       
  1106 {
       
  1107     init();
       
  1108     if (con) {
       
  1109         d->mysql = (MYSQL *) con;
       
  1110 #ifndef QT_NO_TEXTCODEC
       
  1111         d->tc = codec(con);
       
  1112 #endif
       
  1113         setOpen(true);
       
  1114         setOpenError(false);
       
  1115         if (qMySqlConnectionCount == 1)
       
  1116             qMySqlInitHandledByUser = true;
       
  1117     } else {
       
  1118         qLibraryInit();
       
  1119     }
       
  1120 }
       
  1121 
       
  1122 void QMYSQLDriver::init()
       
  1123 {
       
  1124     d = new QMYSQLDriverPrivate();
       
  1125     d->mysql = 0;
       
  1126     qMySqlConnectionCount++;
       
  1127 }
       
  1128 
       
  1129 QMYSQLDriver::~QMYSQLDriver()
       
  1130 {
       
  1131     qMySqlConnectionCount--;
       
  1132     if (qMySqlConnectionCount == 0 && !qMySqlInitHandledByUser)
       
  1133         qLibraryEnd();
       
  1134     delete d;
       
  1135 }
       
  1136 
       
  1137 bool QMYSQLDriver::hasFeature(DriverFeature f) const
       
  1138 {
       
  1139     switch (f) {
       
  1140     case Transactions:
       
  1141 // CLIENT_TRANSACTION should be defined in all recent mysql client libs > 3.23.34
       
  1142 #ifdef CLIENT_TRANSACTIONS
       
  1143         if (d->mysql) {
       
  1144             if ((d->mysql->server_capabilities & CLIENT_TRANSACTIONS) == CLIENT_TRANSACTIONS)
       
  1145                 return true;
       
  1146         }
       
  1147 #endif
       
  1148         return false;
       
  1149     case NamedPlaceholders:
       
  1150     case BatchOperations:
       
  1151     case SimpleLocking:
       
  1152     case EventNotifications:
       
  1153     case FinishQuery:
       
  1154         return false;
       
  1155     case QuerySize:
       
  1156     case BLOB:
       
  1157     case LastInsertId:
       
  1158     case Unicode:
       
  1159     case LowPrecisionNumbers:
       
  1160         return true;
       
  1161     case PreparedQueries:
       
  1162     case PositionalPlaceholders:
       
  1163 #if MYSQL_VERSION_ID >= 40108
       
  1164         return d->preparedQuerysEnabled;
       
  1165 #else
       
  1166         return false;
       
  1167 #endif
       
  1168     case MultipleResultSets:
       
  1169 #if MYSQL_VERSION_ID >= 40100
       
  1170         return true;
       
  1171 #else
       
  1172         return false;
       
  1173 #endif
       
  1174     }
       
  1175     return false;
       
  1176 }
       
  1177 
       
  1178 static void setOptionFlag(uint &optionFlags, const QString &opt)
       
  1179 {
       
  1180     if (opt == QLatin1String("CLIENT_COMPRESS"))
       
  1181         optionFlags |= CLIENT_COMPRESS;
       
  1182     else if (opt == QLatin1String("CLIENT_FOUND_ROWS"))
       
  1183         optionFlags |= CLIENT_FOUND_ROWS;
       
  1184     else if (opt == QLatin1String("CLIENT_IGNORE_SPACE"))
       
  1185         optionFlags |= CLIENT_IGNORE_SPACE;
       
  1186     else if (opt == QLatin1String("CLIENT_INTERACTIVE"))
       
  1187         optionFlags |= CLIENT_INTERACTIVE;
       
  1188     else if (opt == QLatin1String("CLIENT_NO_SCHEMA"))
       
  1189         optionFlags |= CLIENT_NO_SCHEMA;
       
  1190     else if (opt == QLatin1String("CLIENT_ODBC"))
       
  1191         optionFlags |= CLIENT_ODBC;
       
  1192     else if (opt == QLatin1String("CLIENT_SSL"))
       
  1193         optionFlags |= CLIENT_SSL;
       
  1194     else
       
  1195         qWarning("QMYSQLDriver::open: Unknown connect option '%s'", opt.toLocal8Bit().constData());
       
  1196 }
       
  1197 
       
  1198 bool QMYSQLDriver::open(const QString& db,
       
  1199                          const QString& user,
       
  1200                          const QString& password,
       
  1201                          const QString& host,
       
  1202                          int port,
       
  1203                          const QString& connOpts)
       
  1204 {
       
  1205     if (isOpen())
       
  1206         close();
       
  1207 
       
  1208     /* This is a hack to get MySQL's stored procedure support working.
       
  1209        Since a stored procedure _may_ return multiple result sets,
       
  1210        we have to enable CLIEN_MULTI_STATEMENTS here, otherwise _any_
       
  1211        stored procedure call will fail.
       
  1212     */
       
  1213     unsigned int optionFlags = Q_CLIENT_MULTI_STATEMENTS;
       
  1214     const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
       
  1215     QString unixSocket;
       
  1216 #if MYSQL_VERSION_ID >= 50000
       
  1217     my_bool reconnect=false;
       
  1218 #endif
       
  1219 
       
  1220     // extract the real options from the string
       
  1221     for (int i = 0; i < opts.count(); ++i) {
       
  1222         QString tmp(opts.at(i).simplified());
       
  1223         int idx;
       
  1224         if ((idx = tmp.indexOf(QLatin1Char('='))) != -1) {
       
  1225             QString val = tmp.mid(idx + 1).simplified();
       
  1226             QString opt = tmp.left(idx).simplified();
       
  1227             if (opt == QLatin1String("UNIX_SOCKET"))
       
  1228                 unixSocket = val;
       
  1229 #if MYSQL_VERSION_ID >= 50000
       
  1230             else if (opt == QLatin1String("MYSQL_OPT_RECONNECT")) {
       
  1231                 if (val == QLatin1String("TRUE") || val == QLatin1String("1") || val.isEmpty())
       
  1232                     reconnect = true;
       
  1233             }
       
  1234 #endif
       
  1235             else if (val == QLatin1String("TRUE") || val == QLatin1String("1"))
       
  1236                 setOptionFlag(optionFlags, tmp.left(idx).simplified());
       
  1237             else
       
  1238                 qWarning("QMYSQLDriver::open: Illegal connect option value '%s'",
       
  1239                          tmp.toLocal8Bit().constData());
       
  1240         } else {
       
  1241             setOptionFlag(optionFlags, tmp);
       
  1242         }
       
  1243     }
       
  1244 
       
  1245     if ((d->mysql = mysql_init((MYSQL*) 0)) &&
       
  1246             mysql_real_connect(d->mysql,
       
  1247                                host.isNull() ? static_cast<const char *>(0)
       
  1248                                              : host.toLocal8Bit().constData(),
       
  1249                                user.isNull() ? static_cast<const char *>(0)
       
  1250                                              : user.toLocal8Bit().constData(),
       
  1251                                password.isNull() ? static_cast<const char *>(0)
       
  1252                                                  : password.toLocal8Bit().constData(),
       
  1253                                db.isNull() ? static_cast<const char *>(0)
       
  1254                                            : db.toLocal8Bit().constData(),
       
  1255                                (port > -1) ? port : 0,
       
  1256                                unixSocket.isNull() ? static_cast<const char *>(0)
       
  1257                                            : unixSocket.toLocal8Bit().constData(),
       
  1258                                optionFlags))
       
  1259     {
       
  1260         if (!db.isEmpty() && mysql_select_db(d->mysql, db.toLocal8Bit().constData())) {
       
  1261             setLastError(qMakeError(tr("Unable to open database '") + db +
       
  1262                          QLatin1Char('\''), QSqlError::ConnectionError, d));
       
  1263             mysql_close(d->mysql);
       
  1264             setOpenError(true);
       
  1265             return false;
       
  1266         }
       
  1267 #if MYSQL_VERSION_ID >= 50000
       
  1268         if(reconnect)
       
  1269             mysql_options(d->mysql, MYSQL_OPT_RECONNECT, &reconnect);
       
  1270 #endif
       
  1271     } else {
       
  1272         setLastError(qMakeError(tr("Unable to connect"),
       
  1273                      QSqlError::ConnectionError, d));
       
  1274         mysql_close(d->mysql);
       
  1275         d->mysql = NULL;
       
  1276         setOpenError(true);
       
  1277         return false;
       
  1278     }
       
  1279 
       
  1280 #if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007
       
  1281     // force the communication to be utf8
       
  1282     mysql_set_character_set(d->mysql, "utf8");
       
  1283 #endif
       
  1284 #ifndef QT_NO_TEXTCODEC
       
  1285     d->tc = codec(d->mysql);
       
  1286 #endif
       
  1287 
       
  1288 #if MYSQL_VERSION_ID >= 40108
       
  1289     d->preparedQuerysEnabled = mysql_get_client_version() >= 40108
       
  1290                         && mysql_get_server_version(d->mysql) >= 40100;
       
  1291 #else
       
  1292     d->preparedQuerysEnabled = false;
       
  1293 #endif
       
  1294 
       
  1295 #ifndef QT_NO_THREAD
       
  1296     mysql_thread_init();
       
  1297 #endif
       
  1298 
       
  1299 
       
  1300     setOpen(true);
       
  1301     setOpenError(false);
       
  1302     return true;
       
  1303 }
       
  1304 
       
  1305 void QMYSQLDriver::close()
       
  1306 {
       
  1307     if (isOpen()) {
       
  1308 #ifndef QT_NO_THREAD
       
  1309         mysql_thread_end();
       
  1310 #endif
       
  1311         mysql_close(d->mysql);
       
  1312         d->mysql = NULL;
       
  1313         setOpen(false);
       
  1314         setOpenError(false);
       
  1315     }
       
  1316 }
       
  1317 
       
  1318 QSqlResult *QMYSQLDriver::createResult() const
       
  1319 {
       
  1320     return new QMYSQLResult(this);
       
  1321 }
       
  1322 
       
  1323 QStringList QMYSQLDriver::tables(QSql::TableType type) const
       
  1324 {
       
  1325     QStringList tl;
       
  1326 #if MYSQL_VERSION_ID >= 40100
       
  1327     if( mysql_get_server_version(d->mysql) < 50000)
       
  1328     {
       
  1329 #endif
       
  1330         if (!isOpen())
       
  1331             return tl;
       
  1332         if (!(type & QSql::Tables))
       
  1333             return tl;
       
  1334 
       
  1335         MYSQL_RES* tableRes = mysql_list_tables(d->mysql, NULL);
       
  1336         MYSQL_ROW row;
       
  1337         int i = 0;
       
  1338         while (tableRes) {
       
  1339             mysql_data_seek(tableRes, i);
       
  1340             row = mysql_fetch_row(tableRes);
       
  1341             if (!row)
       
  1342                 break;
       
  1343             tl.append(toUnicode(d->tc, row[0]));
       
  1344             i++;
       
  1345         }
       
  1346         mysql_free_result(tableRes);
       
  1347 #if MYSQL_VERSION_ID >= 40100
       
  1348     } else {
       
  1349         QSqlQuery q(createResult());
       
  1350         if(type & QSql::Tables) {
       
  1351             q.exec(QLatin1String("select table_name from information_schema.tables where table_type = 'BASE TABLE'"));
       
  1352             while(q.next())
       
  1353                 tl.append(q.value(0).toString());
       
  1354         }
       
  1355         if(type & QSql::Views) {
       
  1356             q.exec(QLatin1String("select table_name from information_schema.tables where table_type = 'VIEW'"));
       
  1357             while(q.next())
       
  1358                 tl.append(q.value(0).toString());
       
  1359         }
       
  1360     }
       
  1361 #endif
       
  1362     return tl;
       
  1363 }
       
  1364 
       
  1365 QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const
       
  1366 {
       
  1367     QSqlIndex idx;
       
  1368     bool prepQ;
       
  1369     if (!isOpen())
       
  1370         return idx;
       
  1371 
       
  1372     QSqlQuery i(createResult());
       
  1373     QString stmt(QLatin1String("show index from %1;"));
       
  1374     QSqlRecord fil = record(tablename);
       
  1375     i.exec(stmt.arg(tablename));
       
  1376     while (i.isActive() && i.next()) {
       
  1377         if (i.value(2).toString() == QLatin1String("PRIMARY")) {
       
  1378             idx.append(fil.field(i.value(4).toString()));
       
  1379             idx.setCursorName(i.value(0).toString());
       
  1380             idx.setName(i.value(2).toString());
       
  1381         }
       
  1382     }
       
  1383 
       
  1384     return idx;
       
  1385 }
       
  1386 
       
  1387 QSqlRecord QMYSQLDriver::record(const QString& tablename) const
       
  1388 {
       
  1389     QString table=tablename;
       
  1390     if(isIdentifierEscaped(table, QSqlDriver::TableName))
       
  1391         table = stripDelimiters(table, QSqlDriver::TableName);
       
  1392 
       
  1393     QSqlRecord info;
       
  1394     if (!isOpen())
       
  1395         return info;
       
  1396     MYSQL_RES* r = mysql_list_fields(d->mysql, table.toLocal8Bit().constData(), 0);
       
  1397     if (!r) {
       
  1398         return info;
       
  1399     }
       
  1400     MYSQL_FIELD* field;
       
  1401 
       
  1402     while ((field = mysql_fetch_field(r)))
       
  1403         info.append(qToField(field, d->tc));
       
  1404     mysql_free_result(r);
       
  1405     return info;
       
  1406 }
       
  1407 
       
  1408 QVariant QMYSQLDriver::handle() const
       
  1409 {
       
  1410     return qVariantFromValue(d->mysql);
       
  1411 }
       
  1412 
       
  1413 bool QMYSQLDriver::beginTransaction()
       
  1414 {
       
  1415 #ifndef CLIENT_TRANSACTIONS
       
  1416     return false;
       
  1417 #endif
       
  1418     if (!isOpen()) {
       
  1419         qWarning("QMYSQLDriver::beginTransaction: Database not open");
       
  1420         return false;
       
  1421     }
       
  1422     if (mysql_query(d->mysql, "BEGIN WORK")) {
       
  1423         setLastError(qMakeError(tr("Unable to begin transaction"),
       
  1424                                 QSqlError::StatementError, d));
       
  1425         return false;
       
  1426     }
       
  1427     return true;
       
  1428 }
       
  1429 
       
  1430 bool QMYSQLDriver::commitTransaction()
       
  1431 {
       
  1432 #ifndef CLIENT_TRANSACTIONS
       
  1433     return false;
       
  1434 #endif
       
  1435     if (!isOpen()) {
       
  1436         qWarning("QMYSQLDriver::commitTransaction: Database not open");
       
  1437         return false;
       
  1438     }
       
  1439     if (mysql_query(d->mysql, "COMMIT")) {
       
  1440         setLastError(qMakeError(tr("Unable to commit transaction"),
       
  1441                                 QSqlError::StatementError, d));
       
  1442         return false;
       
  1443     }
       
  1444     return true;
       
  1445 }
       
  1446 
       
  1447 bool QMYSQLDriver::rollbackTransaction()
       
  1448 {
       
  1449 #ifndef CLIENT_TRANSACTIONS
       
  1450     return false;
       
  1451 #endif
       
  1452     if (!isOpen()) {
       
  1453         qWarning("QMYSQLDriver::rollbackTransaction: Database not open");
       
  1454         return false;
       
  1455     }
       
  1456     if (mysql_query(d->mysql, "ROLLBACK")) {
       
  1457         setLastError(qMakeError(tr("Unable to rollback transaction"),
       
  1458                                 QSqlError::StatementError, d));
       
  1459         return false;
       
  1460     }
       
  1461     return true;
       
  1462 }
       
  1463 
       
  1464 QString QMYSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
       
  1465 {
       
  1466     QString r;
       
  1467     if (field.isNull()) {
       
  1468         r = QLatin1String("NULL");
       
  1469     } else {
       
  1470         switch(field.type()) {
       
  1471         case QVariant::String:
       
  1472             // Escape '\' characters
       
  1473             r = QSqlDriver::formatValue(field, trimStrings);
       
  1474             r.replace(QLatin1String("\\"), QLatin1String("\\\\"));
       
  1475             break;
       
  1476         case QVariant::ByteArray:
       
  1477             if (isOpen()) {
       
  1478                 const QByteArray ba = field.value().toByteArray();
       
  1479                 // buffer has to be at least length*2+1 bytes
       
  1480                 char* buffer = new char[ba.size() * 2 + 1];
       
  1481                 int escapedSize = int(mysql_real_escape_string(d->mysql, buffer,
       
  1482                                       ba.data(), ba.size()));
       
  1483                 r.reserve(escapedSize + 3);
       
  1484                 r.append(QLatin1Char('\'')).append(toUnicode(d->tc, buffer)).append(QLatin1Char('\''));
       
  1485                 delete[] buffer;
       
  1486                 break;
       
  1487             } else {
       
  1488                 qWarning("QMYSQLDriver::formatValue: Database not open");
       
  1489             }
       
  1490             // fall through
       
  1491         default:
       
  1492             r = QSqlDriver::formatValue(field, trimStrings);
       
  1493         }
       
  1494     }
       
  1495     return r;
       
  1496 }
       
  1497 
       
  1498 QString QMYSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
       
  1499 {
       
  1500     QString res = identifier;
       
  1501     if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('`')) && !identifier.endsWith(QLatin1Char('`')) ) {
       
  1502         res.prepend(QLatin1Char('`')).append(QLatin1Char('`'));
       
  1503         res.replace(QLatin1Char('.'), QLatin1String("`.`"));
       
  1504     }
       
  1505     return res;
       
  1506 }
       
  1507 
       
  1508 bool QMYSQLDriver::isIdentifierEscapedImplementation(const QString &identifier, IdentifierType type) const
       
  1509 {
       
  1510     Q_UNUSED(type);
       
  1511     return identifier.size() > 2
       
  1512         && identifier.startsWith(QLatin1Char('`')) //left delimited
       
  1513         && identifier.endsWith(QLatin1Char('`')); //right delimited
       
  1514 }
       
  1515 
       
  1516 QT_END_NAMESPACE
       
  1517 
       
  1518 #include "qsql_mysql.moc"