1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the sql module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
#include "qsql_mysql.h"
31
#include <qcoreapplication.h>
33
#include <qdatetime.h>
34
#include <qsqlerror.h>
35
#include <qsqlfield.h>
36
#include <qsqlindex.h>
37
#include <qsqlquery.h>
38
#include <qsqlrecord.h>
39
#include <qstringlist.h>
40
#include <qtextcodec.h>
46
// comment the next line out if you want to use MySQL/embedded on Win32 systems.
47
// note that it will crash if you don't statically link to the mysql/e library!
48
# define Q_NO_MYSQL_EMBEDDED
51
Q_DECLARE_METATYPE(MYSQL_RES*)
52
Q_DECLARE_METATYPE(MYSQL*)
54
#if MYSQL_VERSION_ID >= 40108
55
Q_DECLARE_METATYPE(MYSQL_STMT*)
58
class QMYSQLDriverPrivate
61
QMYSQLDriverPrivate() : mysql(0), tc(0), preparedQuerys(false), preparedQuerysEnabled(false) {}
66
bool preparedQuerysEnabled;
69
class QMYSQLResultPrivate : public QMYSQLDriverPrivate
72
QMYSQLResultPrivate() : QMYSQLDriverPrivate(), result(0), tc(QTextCodec::codecForLocale()),
73
rowsAffected(0), hasBlobs(false)
74
#if MYSQL_VERSION_ID >= 40108
75
, stmt(0), meta(0), inBinds(0), outBinds(0)
92
: outField(0), nullIndicator(false), bufLength(0ul),
93
myField(0), type(QVariant::Invalid)
96
my_bool nullIndicator;
102
QVector<QMyField> fields;
104
#if MYSQL_VERSION_ID >= 40108
109
MYSQL_BIND *outBinds;
113
static QTextCodec* codec(MYSQL* mysql)
115
#if MYSQL_VERSION_ID >= 32321
116
QTextCodec* heuristicCodec = QTextCodec::codecForName(mysql_character_set_name(mysql));
118
return heuristicCodec;
120
return QTextCodec::codecForLocale();
123
static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
124
const QMYSQLDriverPrivate* p)
126
const char *cerr = mysql_error(p->mysql);
127
return QSqlError(QLatin1String("QMYSQL: ") + err,
128
p->tc ? p->tc->toUnicode(cerr) : QString::fromLatin1(cerr),
129
type, mysql_errno(p->mysql));
133
static QVariant::Type qDecodeMYSQLType(int mysqltype, uint flags)
137
case FIELD_TYPE_TINY :
138
case FIELD_TYPE_SHORT :
139
case FIELD_TYPE_LONG :
140
case FIELD_TYPE_INT24 :
141
type = (flags & UNSIGNED_FLAG) ? QVariant::UInt : QVariant::Int;
143
case FIELD_TYPE_YEAR :
144
type = QVariant::Int;
146
case FIELD_TYPE_LONGLONG :
147
type = (flags & UNSIGNED_FLAG) ? QVariant::ULongLong : QVariant::LongLong;
149
case FIELD_TYPE_DECIMAL :
150
case FIELD_TYPE_FLOAT :
151
case FIELD_TYPE_DOUBLE :
152
type = QVariant::Double;
154
case FIELD_TYPE_DATE :
155
type = QVariant::Date;
157
case FIELD_TYPE_TIME :
158
type = QVariant::Time;
160
case FIELD_TYPE_DATETIME :
161
case FIELD_TYPE_TIMESTAMP :
162
type = QVariant::DateTime;
164
case FIELD_TYPE_BLOB :
165
case FIELD_TYPE_TINY_BLOB :
166
case FIELD_TYPE_MEDIUM_BLOB :
167
case FIELD_TYPE_LONG_BLOB :
168
type = QVariant::ByteArray;
171
case FIELD_TYPE_ENUM :
172
case FIELD_TYPE_SET :
173
case FIELD_TYPE_STRING :
174
case FIELD_TYPE_VAR_STRING :
175
type = QVariant::String;
181
static QSqlField qToField(MYSQL_FIELD *field, QTextCodec *tc)
183
QSqlField f(tc->toUnicode(field->name),
184
qDecodeMYSQLType(int(field->type), field->flags));
185
f.setRequired(IS_NOT_NULL(field->flags));
186
f.setLength(field->length);
187
f.setPrecision(field->decimals);
188
f.setSqlType(field->type);
192
#if MYSQL_VERSION_ID >= 40108
194
static QSqlError qMakeStmtError(const QString& err, QSqlError::ErrorType type,
197
const char *cerr = mysql_stmt_error(stmt);
198
return QSqlError(QLatin1String("QMYSQL3: ") + err,
199
QString::fromLatin1(cerr),
200
type, mysql_stmt_errno(stmt));
203
static bool qIsBlob(int t)
205
return t == MYSQL_TYPE_TINY_BLOB
206
|| t == MYSQL_TYPE_BLOB
207
|| t == MYSQL_TYPE_MEDIUM_BLOB
208
|| t == MYSQL_TYPE_LONG_BLOB;
211
void QMYSQLResultPrivate::bindBlobs()
214
MYSQL_FIELD *fieldInfo;
218
for(i = 0; i < fields.count(); ++i) {
219
fieldInfo = fields.at(i).myField;
220
if (qIsBlob(inBinds[i].buffer_type) && meta && fieldInfo) {
222
bind->buffer_length = fieldInfo->max_length;
223
delete static_cast<char*>(bind->buffer);
224
bind->buffer = new char[fieldInfo->max_length];
225
fields[i].outField = static_cast<char*>(bind->buffer);
226
bind->buffer_type = MYSQL_TYPE_STRING;
231
bool QMYSQLResultPrivate::bindInValues()
238
meta = mysql_stmt_result_metadata(stmt);
242
fields.resize(mysql_num_fields(meta));
244
inBinds = new MYSQL_BIND[fields.size()];
246
MYSQL_FIELD *fieldInfo;
248
while((fieldInfo = mysql_fetch_field(meta))) {
249
QMyField &f = fields[i];
250
f.myField = fieldInfo;
251
if (fieldInfo->type == FIELD_TYPE_DECIMAL)
252
f.type = QVariant::String;
254
f.type = qDecodeMYSQLType(fieldInfo->type, fieldInfo->flags);
256
if (qIsBlob(fieldInfo->type)) {
257
// the size of a blob-field is available as soon as we call
258
// mysql_stmt_store_result()
259
// after mysql_stmt_exec() in QMYSQLResult::exec()
260
fieldInfo->length = 0;
263
fieldInfo->type = MYSQL_TYPE_STRING;
266
field = new char[fieldInfo->length + 1];
267
memset(field, 0, fieldInfo->length + 1);
269
bind->buffer_type = fieldInfo->type;
270
bind->buffer = field;
271
bind->buffer_length = f.bufLength = fieldInfo->length + 1;
272
bind->is_null = &f.nullIndicator;
273
bind->length = &f.bufLength;
282
QMYSQLResult::QMYSQLResult(const QMYSQLDriver* db)
285
d = new QMYSQLResultPrivate();
286
d->mysql = db->d->mysql;
288
d->preparedQuerysEnabled = db->d->preparedQuerysEnabled;
291
QMYSQLResult::~QMYSQLResult()
297
QVariant QMYSQLResult::handle() const
299
#if MYSQL_VERSION_ID >= 40108
300
return d->meta ? qVariantFromValue(d->meta) : qVariantFromValue(d->stmt);
302
return qVariantFromValue(d->result);
306
void QMYSQLResult::cleanup()
309
mysql_free_result(d->result);
311
#if MYSQL_VERSION_ID >= 40108
313
if (mysql_stmt_close(d->stmt))
314
qWarning("QMYSQLResult::cleanup: unable to free statement handle");
319
mysql_free_result(d->meta);
324
for (i = 0; i < d->fields.count(); ++i)
325
delete[] d->fields[i].outField;
328
delete[] d->outBinds;
337
// for(i = 0; i < d->outFields.size(); ++i)
338
// delete[] d->outFields.at(i);
347
d->preparedQuerys = d->preparedQuerysEnabled;
350
bool QMYSQLResult::fetch(int i)
352
if (isForwardOnly()) { // fake a forward seek
355
while (--x && fetchNext());
363
if (d->preparedQuerys) {
364
#if MYSQL_VERSION_ID >= 40108
365
mysql_stmt_data_seek(d->stmt, i);
367
if (mysql_stmt_fetch(d->stmt)) {
368
setLastError(qMakeStmtError(QCoreApplication::tr("QMYSQLResult",
369
"Unable to fetch data"), QSqlError::StatementError, d->stmt));
376
mysql_data_seek(d->result, i);
377
d->row = mysql_fetch_row(d->result);
386
bool QMYSQLResult::fetchNext()
388
if (d->preparedQuerys) {
389
#if MYSQL_VERSION_ID >= 40108
390
if (mysql_stmt_fetch(d->stmt))
396
d->row = mysql_fetch_row(d->result);
404
bool QMYSQLResult::fetchLast()
406
if (isForwardOnly()) { // fake this since MySQL can't seek on forward only queries
407
bool success = fetchNext(); // did we move at all?
412
my_ulonglong numRows;
413
if (d->preparedQuerys) {
414
#if MYSQL_VERSION_ID >= 40108
415
numRows = mysql_stmt_num_rows(d->stmt);
420
numRows = mysql_num_rows(d->result);
422
if (at() == int(numRows))
426
return fetch(numRows - 1);
429
bool QMYSQLResult::fetchFirst()
435
return (at() == QSql::BeforeFirstRow) ? fetchNext() : false;
439
QVariant QMYSQLResult::data(int field)
442
if (!isSelect() || field >= d->fields.count()) {
443
qWarning("QMYSQLResult::data: column %d out of range", field);
447
const QMYSQLResultPrivate::QMyField &f = d->fields.at(field);
449
if (d->preparedQuerys) {
451
return QVariant(f.type);
453
if (f.type != QVariant::ByteArray)
454
val = d->tc->toUnicode(f.outField);
456
if (d->row[field] == NULL) {
458
return QVariant(f.type);
460
if (f.type != QVariant::ByteArray)
461
val = d->tc->toUnicode(d->row[field]);
465
case QVariant::LongLong:
466
return QVariant(val.toLongLong());
467
case QVariant::ULongLong:
468
return QVariant(val.toULongLong());
470
return QVariant(val.toInt());
472
return QVariant(val.toUInt());
473
case QVariant::Double:
474
return QVariant(val.toDouble());
477
return QVariant(QDate());
479
return QVariant(QDate::fromString(val, Qt::ISODate) );
483
return QVariant(QTime());
485
return QVariant(QTime::fromString(val, Qt::ISODate));
487
case QVariant::DateTime:
489
return QVariant(QDateTime());
490
if (val.length() == 14)
491
// TIMESTAMPS have the format yyyyMMddhhmmss
492
val.insert(4, QLatin1Char('-')).insert(7, QLatin1Char('-')).insert(10,
493
QLatin1Char('T')).insert(13, QLatin1Char(':')).insert(16, QLatin1Char(':'));
494
return QVariant(QDateTime::fromString(val, Qt::ISODate));
495
case QVariant::ByteArray: {
498
if (d->preparedQuerys) {
499
ba = QByteArray(f.outField, f.bufLength);
501
unsigned long* fl = mysql_fetch_lengths(d->result);
502
ba = QByteArray(d->row[field], fl[field]);
507
case QVariant::String:
508
return QVariant(val);
510
qWarning("QMYSQLResult::data: unknown data type");
514
bool QMYSQLResult::isNull(int field)
516
if (d->preparedQuerys)
517
return d->fields.at(field).nullIndicator;
519
return d->row[field] == NULL;
522
bool QMYSQLResult::reset (const QString& query)
524
if (!driver() || !driver()->isOpen() || driver()->isOpenError())
528
d->preparedQuerys = false;
530
const QByteArray encQuery(d->tc->fromUnicode(query));
531
if (mysql_real_query(d->mysql, encQuery.data(), encQuery.length())) {
532
setLastError(qMakeError(QCoreApplication::tr("QMYSQLResult", "Unable to execute query"),
533
QSqlError::StatementError, d));
536
d->result = mysql_store_result(d->mysql);
537
if (!d->result && mysql_field_count(d->mysql) > 0) {
538
setLastError(qMakeError(QCoreApplication::tr("QMYSQLResult", "Unable to store result"),
539
QSqlError::StatementError, d));
542
int numFields = mysql_field_count(d->mysql);
543
setSelect(numFields != 0);
544
d->fields.resize(numFields);
545
d->rowsAffected = mysql_affected_rows(d->mysql);
547
for(int i = 0; i < numFields; i++) {
548
MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
549
if (field->type == FIELD_TYPE_DECIMAL)
550
d->fields[i].type = QVariant::String;
552
d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
559
int QMYSQLResult::size()
562
if (d->preparedQuerys)
563
#if MYSQL_VERSION_ID >= 40108
564
return int(mysql_stmt_num_rows(d->stmt));
569
return int(mysql_num_rows(d->result));
574
int QMYSQLResult::numRowsAffected()
576
return d->rowsAffected;
579
QVariant QMYSQLResult::lastInsertId() const
584
if (d->preparedQuerys) {
585
#if MYSQL_VERSION_ID >= 40108
586
quint64 id = mysql_stmt_insert_id(d->stmt);
591
quint64 id = mysql_insert_id(d->mysql);
598
QSqlRecord QMYSQLResult::record() const
602
if (!isActive() || !isSelect())
605
#if MYSQL_VERSION_ID >= 40108
606
res = d->preparedQuerys ? d->meta : d->result;
611
if (!mysql_errno(d->mysql)) {
612
mysql_field_seek(res, 0);
613
MYSQL_FIELD* field = mysql_fetch_field(res);
615
info.append(qToField(field, d->tc));
616
field = mysql_fetch_field(res);
619
mysql_field_seek(res, 0);
624
#if MYSQL_VERSION_ID >= 40108
626
static MYSQL_TIME *toMySqlDate(QDate date, QTime time, QVariant::Type type)
628
Q_ASSERT(type == QVariant::Time || type == QVariant::Date
629
|| type == QVariant::DateTime);
631
MYSQL_TIME *myTime = new MYSQL_TIME;
632
memset(myTime, 0, sizeof(MYSQL_TIME));
634
if (type == QVariant::Time || type == QVariant::DateTime) {
635
myTime->hour = time.hour();
636
myTime->minute = time.minute();
637
myTime->second = time.second();
638
myTime->second_part = time.msec();
640
if (type == QVariant::Date || type == QVariant::DateTime) {
641
myTime->year = date.year();
642
myTime->month = date.month();
643
myTime->day = date.day();
649
bool QMYSQLResult::prepare(const QString& query)
652
if (!d->preparedQuerys)
653
return QSqlResult::prepare(query);
661
d->stmt = mysql_stmt_init(d->mysql);
663
setLastError(qMakeError(QCoreApplication::tr("QMYSQLResult", "Unable to prepare statement"),
664
QSqlError::StatementError, d));
668
const QByteArray encQuery(d->tc->fromUnicode(query));
669
r = mysql_stmt_prepare(d->stmt, encQuery.constData(), encQuery.length());
671
setLastError(qMakeStmtError(QCoreApplication::tr("QMYSQLResult",
672
"Unable to prepare statement"), QSqlError::StatementError, d->stmt));
677
if (mysql_stmt_param_count(d->stmt) > 0) {// allocate memory for outvalues
678
d->outBinds = new MYSQL_BIND[mysql_stmt_param_count(d->stmt)];
681
setSelect(d->bindInValues());
685
bool QMYSQLResult::exec()
687
if (!d->preparedQuerys)
688
return QSqlResult::exec();
693
MYSQL_BIND* currBind;
694
QVector<MYSQL_TIME *> timeVector;
695
QVector<QByteArray> stringVector;
696
QVector<my_bool> nullVector;
698
const QVector<QVariant> values = boundValues();
700
r = mysql_stmt_reset(d->stmt);
702
setLastError(qMakeStmtError(QCoreApplication::tr("QMYSQLResult",
703
"Unable to reset statement"), QSqlError::StatementError, d->stmt));
707
if (mysql_stmt_param_count(d->stmt) > 0 &&
708
mysql_stmt_param_count(d->stmt) == (uint)values.count()) {
710
nullVector.resize(values.count());
711
for (int i = 0; i < values.count(); ++i) {
712
const QVariant &val = boundValues().at(i);
713
void *data = const_cast<void *>(val.constData());
715
currBind = &d->outBinds[i];
717
nullVector[i] = static_cast<my_bool>(val.isNull());
718
currBind->is_null = &nullVector[i];
719
currBind->length = 0;
721
switch (val.type()) {
722
case QVariant::ByteArray:
723
currBind->buffer_type = MYSQL_TYPE_BLOB;
724
currBind->buffer = const_cast<char *>(val.toByteArray().constData());
725
currBind->buffer_length = val.toByteArray().size();
730
case QVariant::DateTime: {
731
MYSQL_TIME *myTime = toMySqlDate(val.toDate(), val.toTime(), val.type());
732
timeVector.append(myTime);
734
currBind->buffer = myTime;
737
currBind->buffer_type = MYSQL_TYPE_TIME;
738
myTime->time_type = MYSQL_TIMESTAMP_TIME;
741
currBind->buffer_type = MYSQL_TYPE_DATE;
742
myTime->time_type = MYSQL_TIMESTAMP_DATE;
744
case QVariant::DateTime:
745
currBind->buffer_type = MYSQL_TYPE_DATETIME;
746
myTime->time_type = MYSQL_TIMESTAMP_DATETIME;
751
currBind->buffer_length = sizeof(MYSQL_TIME);
752
currBind->length = 0;
756
currBind->buffer_type = MYSQL_TYPE_LONG;
757
currBind->buffer = data;
758
currBind->buffer_length = sizeof(uint);
759
currBind->is_unsigned = (val.type() == QVariant::UInt);
761
case QVariant::Double:
762
currBind->buffer_type = MYSQL_TYPE_DOUBLE;
763
currBind->buffer = data;
764
currBind->buffer_length = sizeof(double);
765
currBind->is_unsigned = 0;
767
case QVariant::LongLong:
768
case QVariant::ULongLong:
769
currBind->buffer_type = MYSQL_TYPE_LONGLONG;
770
currBind->buffer = data;
771
currBind->buffer_length = sizeof(qint64);
772
currBind->is_unsigned = (val.type() == QVariant::ULongLong);
774
case QVariant::String:
776
QByteArray ba = d->tc->fromUnicode(val.toString());
777
stringVector.append(ba);
778
currBind->buffer_type = MYSQL_TYPE_STRING;
779
currBind->buffer = const_cast<char *>(ba.constData());
780
currBind->buffer_length = ba.length();
781
currBind->is_unsigned = 0;
786
r = mysql_stmt_bind_param(d->stmt, d->outBinds);
788
setLastError(qMakeStmtError(QCoreApplication::tr("QMYSQLResult",
789
"Unable to bind value"), QSqlError::StatementError, d->stmt));
790
qDeleteAll(timeVector);
794
r = mysql_stmt_execute(d->stmt);
796
qDeleteAll(timeVector);
799
setLastError(qMakeStmtError(QCoreApplication::tr("QMYSQLResult",
800
"Unable to execute statement"), QSqlError::StatementError, d->stmt));
803
//if there is meta-data there is also data
806
d->rowsAffected = mysql_stmt_affected_rows(d->stmt);
809
my_bool update_max_length = true;
811
r = mysql_stmt_bind_result(d->stmt, d->inBinds);
813
setLastError(qMakeStmtError(QCoreApplication::tr("QMYSQLResult",
814
"Unable to bind outvalues"), QSqlError::StatementError, d->stmt));
818
mysql_stmt_attr_set(d->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &update_max_length);
820
r = mysql_stmt_store_result(d->stmt);
822
setLastError(qMakeStmtError(QCoreApplication::tr("QMYSQLResult",
823
"Unable to store statement results"), QSqlError::StatementError, d->stmt));
828
// mysql_stmt_store_result() with STMT_ATTR_UPDATE_MAX_LENGTH set to true crashes
829
// when called without a preceding call to mysql_stmt_bind_result()
830
// in versions < 4.1.8
832
r = mysql_stmt_bind_result(d->stmt, d->inBinds);
834
setLastError(qMakeStmtError(QCoreApplication::tr("QMYSQLResult",
835
"Unable to bind outvalues"), QSqlError::StatementError, d->stmt));
839
setAt(QSql::BeforeFirstRow);
845
/////////////////////////////////////////////////////////
847
static void qServerInit()
849
#ifndef Q_NO_MYSQL_EMBEDDED
850
# if MYSQL_VERSION_ID >= 40000
851
static bool init = false;
855
// this should only be called once
856
// has no effect on client/server library
857
// but is vital for the embedded lib
858
if (mysql_server_init(0, 0, 0)) {
859
qWarning("QMYSQLDriver::qServerInit: unable to start server.");
862
# endif // MYSQL_VERSION_ID
863
#endif // Q_NO_MYSQL_EMBEDDED
866
QMYSQLDriver::QMYSQLDriver(QObject * parent)
874
Create a driver instance with the open connection handle, \a con.
875
The instance's parent (owner) is \a parent.
878
QMYSQLDriver::QMYSQLDriver(MYSQL * con, QObject * parent)
883
d->mysql = (MYSQL *) con;
892
void QMYSQLDriver::init()
894
d = new QMYSQLDriverPrivate();
898
QMYSQLDriver::~QMYSQLDriver()
901
#ifndef Q_NO_MYSQL_EMBEDDED
902
# if MYSQL_VERSION_ID > 40000
908
bool QMYSQLDriver::hasFeature(DriverFeature f) const
912
// CLIENT_TRANSACTION should be defined in all recent mysql client libs > 3.23.34
913
#ifdef CLIENT_TRANSACTIONS
915
if ((d->mysql->server_capabilities & CLIENT_TRANSACTIONS) == CLIENT_TRANSACTIONS)
926
#if MYSQL_VERSION_ID >= 40108
927
case PreparedQueries:
928
case PositionalPlaceholders:
929
return d->preparedQuerysEnabled;
936
static void setOptionFlag(uint &optionFlags, const QString &opt)
938
if (opt == QLatin1String("CLIENT_COMPRESS"))
939
optionFlags |= CLIENT_COMPRESS;
940
else if (opt == QLatin1String("CLIENT_FOUND_ROWS"))
941
optionFlags |= CLIENT_FOUND_ROWS;
942
else if (opt == QLatin1String("CLIENT_IGNORE_SPACE"))
943
optionFlags |= CLIENT_IGNORE_SPACE;
944
else if (opt == QLatin1String("CLIENT_INTERACTIVE"))
945
optionFlags |= CLIENT_INTERACTIVE;
946
else if (opt == QLatin1String("CLIENT_NO_SCHEMA"))
947
optionFlags |= CLIENT_NO_SCHEMA;
948
else if (opt == QLatin1String("CLIENT_ODBC"))
949
optionFlags |= CLIENT_ODBC;
950
else if (opt == QLatin1String("CLIENT_SSL"))
951
optionFlags |= CLIENT_SSL;
953
qWarning("QMYSQLDriver::open: Unknown connect option '%s'", opt.toLocal8Bit().constData());
956
bool QMYSQLDriver::open(const QString& db,
958
const QString& password,
961
const QString& connOpts)
966
unsigned int optionFlags = 0;
967
const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
969
// extract the real options from the string
970
for (int i = 0; i < opts.count(); ++i) {
971
QString tmp(opts.at(i).simplified());
973
if ((idx = tmp.indexOf(QLatin1Char('='))) != -1) {
974
QString val(tmp.mid(idx + 1).simplified());
975
if (val == QLatin1String("TRUE") || val == QLatin1String("1"))
976
setOptionFlag(optionFlags, tmp.left(idx).simplified());
978
qWarning("QMYSQLDriver::open: Illegal connect option value '%s'",
979
tmp.toLocal8Bit().constData());
981
setOptionFlag(optionFlags, tmp);
985
if ((d->mysql = mysql_init((MYSQL*) 0)) &&
986
mysql_real_connect(d->mysql,
987
host.toLocal8Bit().constData(),
988
user.toLocal8Bit().constData(),
989
password.toLocal8Bit().constData(),
990
db.isNull() ? "" : db.toLocal8Bit().constData(),
991
(port > -1) ? port : 0,
995
if (mysql_select_db(d->mysql, db.toLocal8Bit().constData())) {
996
setLastError(qMakeError(tr("Unable open database '") + db +
997
QLatin1Char('\''), QSqlError::ConnectionError, d));
998
mysql_close(d->mysql);
1003
setLastError(qMakeError(tr("Unable to connect"),
1004
QSqlError::ConnectionError, d));
1005
mysql_close(d->mysql);
1009
d->tc = codec(d->mysql);
1011
#if MYSQL_VERSION_ID >= 40108
1012
d->preparedQuerysEnabled = mysql_get_client_version() >= 40108
1013
&& mysql_get_server_version(d->mysql) >= 40100;
1015
d->preparedQuerysEnabled = false;
1019
setOpenError(false);
1023
void QMYSQLDriver::close()
1026
mysql_close(d->mysql);
1028
setOpenError(false);
1032
QSqlResult *QMYSQLDriver::createResult() const
1034
return new QMYSQLResult(this);
1037
QStringList QMYSQLDriver::tables(QSql::TableType type) const
1042
if (!(type & QSql::Tables))
1045
MYSQL_RES* tableRes = mysql_list_tables(d->mysql, NULL);
1049
mysql_data_seek(tableRes, i);
1050
row = mysql_fetch_row(tableRes);
1053
tl.append(d->tc->toUnicode(row[0]));
1056
mysql_free_result(tableRes);
1060
QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const
1067
prepQ = d->preparedQuerys;
1068
d->preparedQuerys = false;
1070
QSqlQuery i(createResult());
1071
QString stmt(QLatin1String("show index from %1;"));
1072
QSqlRecord fil = record(tablename);
1073
i.exec(stmt.arg(tablename));
1074
while (i.isActive() && i.next()) {
1075
if (i.value(2).toString() == QLatin1String("PRIMARY")) {
1076
idx.append(fil.field(i.value(4).toString()));
1077
idx.setCursorName(i.value(0).toString());
1078
idx.setName(i.value(2).toString());
1082
d->preparedQuerys = prepQ;
1086
QSqlRecord QMYSQLDriver::record(const QString& tablename) const
1091
MYSQL_RES* r = mysql_list_fields(d->mysql, tablename.toLocal8Bit().constData(), 0);
1096
while ((field = mysql_fetch_field(r)))
1097
info.append(qToField(field, d->tc));
1098
mysql_free_result(r);
1102
QVariant QMYSQLDriver::handle() const
1104
return qVariantFromValue(d->mysql);
1107
bool QMYSQLDriver::beginTransaction()
1109
#ifndef CLIENT_TRANSACTIONS
1113
qWarning("QMYSQLDriver::beginTransaction: Database not open");
1116
if (mysql_query(d->mysql, "BEGIN WORK")) {
1117
setLastError(qMakeError(tr("Unable to begin transaction"),
1118
QSqlError::StatementError, d));
1124
bool QMYSQLDriver::commitTransaction()
1126
#ifndef CLIENT_TRANSACTIONS
1130
qWarning("QMYSQLDriver::commitTransaction: Database not open");
1133
if (mysql_query(d->mysql, "COMMIT")) {
1134
setLastError(qMakeError(tr("Unable to commit transaction"),
1135
QSqlError::StatementError, d));
1141
bool QMYSQLDriver::rollbackTransaction()
1143
#ifndef CLIENT_TRANSACTIONS
1147
qWarning("QMYSQLDriver::rollbackTransaction: Database not open");
1150
if (mysql_query(d->mysql, "ROLLBACK")) {
1151
setLastError(qMakeError(tr("Unable to rollback transaction"),
1152
QSqlError::StatementError, d));
1158
QString QMYSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
1161
if (field.isNull()) {
1162
r = QLatin1String("NULL");
1164
switch(field.type()) {
1165
case QVariant::ByteArray: {
1167
const QByteArray ba = field.value().toByteArray();
1168
// buffer has to be at least length*2+1 bytes
1169
char* buffer = new char[ba.size() * 2 + 1];
1170
int escapedSize = int(mysql_escape_string(buffer, ba.data(), ba.size()));
1171
r.reserve(escapedSize + 3);
1172
r.append(QLatin1Char('\'')).append(d->tc->toUnicode(buffer)).append(QLatin1Char('\''));
1176
case QVariant::String:
1177
// Escape '\' characters
1178
r = QSqlDriver::formatValue(field, trimStrings);
1179
r.replace(QLatin1String("\\"), QLatin1String("\\\\"));
1182
r = QSqlDriver::formatValue(field, trimStrings);