1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation ([email protected])
|
---|
6 | **
|
---|
7 | ** This file is part of the QtSql module of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
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 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at [email protected].
|
---|
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 | static bool qIsInteger(int t)
|
---|
324 | {
|
---|
325 | return t == MYSQL_TYPE_TINY
|
---|
326 | || t == MYSQL_TYPE_SHORT
|
---|
327 | || t == MYSQL_TYPE_LONG
|
---|
328 | || t == MYSQL_TYPE_LONGLONG
|
---|
329 | || t == MYSQL_TYPE_INT24;
|
---|
330 | }
|
---|
331 |
|
---|
332 |
|
---|
333 | void QMYSQLResultPrivate::bindBlobs()
|
---|
334 | {
|
---|
335 | int i;
|
---|
336 | MYSQL_FIELD *fieldInfo;
|
---|
337 | MYSQL_BIND *bind;
|
---|
338 |
|
---|
339 | for(i = 0; i < fields.count(); ++i) {
|
---|
340 | fieldInfo = fields.at(i).myField;
|
---|
341 | if (qIsBlob(inBinds[i].buffer_type) && meta && fieldInfo) {
|
---|
342 | bind = &inBinds[i];
|
---|
343 | bind->buffer_length = fieldInfo->max_length;
|
---|
344 | delete[] static_cast<char*>(bind->buffer);
|
---|
345 | bind->buffer = new char[fieldInfo->max_length];
|
---|
346 | fields[i].outField = static_cast<char*>(bind->buffer);
|
---|
347 | }
|
---|
348 | }
|
---|
349 | }
|
---|
350 |
|
---|
351 | bool QMYSQLResultPrivate::bindInValues()
|
---|
352 | {
|
---|
353 | MYSQL_BIND *bind;
|
---|
354 | char *field;
|
---|
355 | int i = 0;
|
---|
356 |
|
---|
357 | if (!meta)
|
---|
358 | meta = mysql_stmt_result_metadata(stmt);
|
---|
359 | if (!meta)
|
---|
360 | return false;
|
---|
361 |
|
---|
362 | fields.resize(mysql_num_fields(meta));
|
---|
363 |
|
---|
364 | inBinds = new MYSQL_BIND[fields.size()];
|
---|
365 | memset(inBinds, 0, fields.size() * sizeof(MYSQL_BIND));
|
---|
366 |
|
---|
367 | MYSQL_FIELD *fieldInfo;
|
---|
368 |
|
---|
369 | while((fieldInfo = mysql_fetch_field(meta))) {
|
---|
370 | QMyField &f = fields[i];
|
---|
371 | f.myField = fieldInfo;
|
---|
372 |
|
---|
373 | f.type = qDecodeMYSQLType(fieldInfo->type, fieldInfo->flags);
|
---|
374 | if (qIsBlob(fieldInfo->type)) {
|
---|
375 | // the size of a blob-field is available as soon as we call
|
---|
376 | // mysql_stmt_store_result()
|
---|
377 | // after mysql_stmt_exec() in QMYSQLResult::exec()
|
---|
378 | fieldInfo->length = 0;
|
---|
379 | hasBlobs = true;
|
---|
380 | } else {
|
---|
381 | // fieldInfo->length specifies the display width, which may be too
|
---|
382 | // small to hold valid integer values (see
|
---|
383 | // http://dev.mysql.com/doc/refman/5.0/en/numeric-types.html ), so
|
---|
384 | // always use the MAX_BIGINT_WIDTH for integer types
|
---|
385 | if (qIsInteger(fieldInfo->type)) {
|
---|
386 | fieldInfo->length = MAX_BIGINT_WIDTH;
|
---|
387 | }
|
---|
388 | fieldInfo->type = MYSQL_TYPE_STRING;
|
---|
389 | }
|
---|
390 | bind = &inBinds[i];
|
---|
391 | field = new char[fieldInfo->length + 1];
|
---|
392 | memset(field, 0, fieldInfo->length + 1);
|
---|
393 |
|
---|
394 | bind->buffer_type = fieldInfo->type;
|
---|
395 | bind->buffer = field;
|
---|
396 | bind->buffer_length = f.bufLength = fieldInfo->length + 1;
|
---|
397 | bind->is_null = &f.nullIndicator;
|
---|
398 | bind->length = &f.bufLength;
|
---|
399 | f.outField=field;
|
---|
400 |
|
---|
401 | ++i;
|
---|
402 | }
|
---|
403 | return true;
|
---|
404 | }
|
---|
405 | #endif
|
---|
406 |
|
---|
407 | QMYSQLResult::QMYSQLResult(const QMYSQLDriver* db)
|
---|
408 | : QSqlResult(db)
|
---|
409 | {
|
---|
410 | d = new QMYSQLResultPrivate(db, this);
|
---|
411 | }
|
---|
412 |
|
---|
413 | QMYSQLResult::~QMYSQLResult()
|
---|
414 | {
|
---|
415 | cleanup();
|
---|
416 | delete d;
|
---|
417 | }
|
---|
418 |
|
---|
419 | QVariant QMYSQLResult::handle() const
|
---|
420 | {
|
---|
421 | #if MYSQL_VERSION_ID >= 40108
|
---|
422 | if(d->preparedQuery)
|
---|
423 | return d->meta ? qVariantFromValue(d->meta) : qVariantFromValue(d->stmt);
|
---|
424 | else
|
---|
425 | #endif
|
---|
426 | return qVariantFromValue(d->result);
|
---|
427 | }
|
---|
428 |
|
---|
429 | void QMYSQLResult::cleanup()
|
---|
430 | {
|
---|
431 | if (d->result)
|
---|
432 | mysql_free_result(d->result);
|
---|
433 |
|
---|
434 | // must iterate trough leftover result sets from multi-selects or stored procedures
|
---|
435 | // if this isn't done subsequent queries will fail with "Commands out of sync"
|
---|
436 | #if MYSQL_VERSION_ID >= 40100
|
---|
437 | while (d->driver && d->driver->d->mysql && mysql_next_result(d->driver->d->mysql) == 0) {
|
---|
438 | MYSQL_RES *res = mysql_store_result(d->driver->d->mysql);
|
---|
439 | if (res)
|
---|
440 | mysql_free_result(res);
|
---|
441 | }
|
---|
442 | #endif
|
---|
443 |
|
---|
444 | #if MYSQL_VERSION_ID >= 40108
|
---|
445 | if (d->stmt) {
|
---|
446 | if (mysql_stmt_close(d->stmt))
|
---|
447 | qWarning("QMYSQLResult::cleanup: unable to free statement handle");
|
---|
448 | d->stmt = 0;
|
---|
449 | }
|
---|
450 |
|
---|
451 | if (d->meta) {
|
---|
452 | mysql_free_result(d->meta);
|
---|
453 | d->meta = 0;
|
---|
454 | }
|
---|
455 |
|
---|
456 | int i;
|
---|
457 | for (i = 0; i < d->fields.count(); ++i)
|
---|
458 | delete[] d->fields[i].outField;
|
---|
459 |
|
---|
460 | if (d->outBinds) {
|
---|
461 | delete[] d->outBinds;
|
---|
462 | d->outBinds = 0;
|
---|
463 | }
|
---|
464 |
|
---|
465 | if (d->inBinds) {
|
---|
466 | delete[] d->inBinds;
|
---|
467 | d->inBinds = 0;
|
---|
468 | }
|
---|
469 | #endif
|
---|
470 |
|
---|
471 | d->hasBlobs = false;
|
---|
472 | d->fields.clear();
|
---|
473 | d->result = NULL;
|
---|
474 | d->row = NULL;
|
---|
475 | setAt(-1);
|
---|
476 | setActive(false);
|
---|
477 | }
|
---|
478 |
|
---|
479 | bool QMYSQLResult::fetch(int i)
|
---|
480 | {
|
---|
481 | if(!d->driver)
|
---|
482 | return false;
|
---|
483 | if (isForwardOnly()) { // fake a forward seek
|
---|
484 | if (at() < i) {
|
---|
485 | int x = i - at();
|
---|
486 | while (--x && fetchNext()) {};
|
---|
487 | return fetchNext();
|
---|
488 | } else {
|
---|
489 | return false;
|
---|
490 | }
|
---|
491 | }
|
---|
492 | if (at() == i)
|
---|
493 | return true;
|
---|
494 | if (d->preparedQuery) {
|
---|
495 | #if MYSQL_VERSION_ID >= 40108
|
---|
496 | mysql_stmt_data_seek(d->stmt, i);
|
---|
497 |
|
---|
498 | int nRC = mysql_stmt_fetch(d->stmt);
|
---|
499 | if (nRC) {
|
---|
500 | #ifdef MYSQL_DATA_TRUNCATED
|
---|
501 | if (nRC == 1 || nRC == MYSQL_DATA_TRUNCATED)
|
---|
502 | #else
|
---|
503 | if (nRC == 1)
|
---|
504 | #endif
|
---|
505 | setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
|
---|
506 | "Unable to fetch data"), QSqlError::StatementError, d->stmt));
|
---|
507 | return false;
|
---|
508 | }
|
---|
509 | #else
|
---|
510 | return false;
|
---|
511 | #endif
|
---|
512 | } else {
|
---|
513 | mysql_data_seek(d->result, i);
|
---|
514 | d->row = mysql_fetch_row(d->result);
|
---|
515 | if (!d->row)
|
---|
516 | return false;
|
---|
517 | }
|
---|
518 |
|
---|
519 | setAt(i);
|
---|
520 | return true;
|
---|
521 | }
|
---|
522 |
|
---|
523 | bool QMYSQLResult::fetchNext()
|
---|
524 | {
|
---|
525 | if(!d->driver)
|
---|
526 | return false;
|
---|
527 | if (d->preparedQuery) {
|
---|
528 | #if MYSQL_VERSION_ID >= 40108
|
---|
529 | int nRC = mysql_stmt_fetch(d->stmt);
|
---|
530 | if (nRC) {
|
---|
531 | #ifdef MYSQL_DATA_TRUNCATED
|
---|
532 | if (nRC == 1 || nRC == MYSQL_DATA_TRUNCATED)
|
---|
533 | #else
|
---|
534 | if (nRC == 1)
|
---|
535 | #endif // MYSQL_DATA_TRUNCATED
|
---|
536 | setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
|
---|
537 | "Unable to fetch data"), QSqlError::StatementError, d->stmt));
|
---|
538 | return false;
|
---|
539 | }
|
---|
540 | #else
|
---|
541 | return false;
|
---|
542 | #endif
|
---|
543 | } else {
|
---|
544 | d->row = mysql_fetch_row(d->result);
|
---|
545 | if (!d->row)
|
---|
546 | return false;
|
---|
547 | }
|
---|
548 | setAt(at() + 1);
|
---|
549 | return true;
|
---|
550 | }
|
---|
551 |
|
---|
552 | bool QMYSQLResult::fetchLast()
|
---|
553 | {
|
---|
554 | if(!d->driver)
|
---|
555 | return false;
|
---|
556 | if (isForwardOnly()) { // fake this since MySQL can't seek on forward only queries
|
---|
557 | bool success = fetchNext(); // did we move at all?
|
---|
558 | while (fetchNext()) {};
|
---|
559 | return success;
|
---|
560 | }
|
---|
561 |
|
---|
562 | my_ulonglong numRows;
|
---|
563 | if (d->preparedQuery) {
|
---|
564 | #if MYSQL_VERSION_ID >= 40108
|
---|
565 | numRows = mysql_stmt_num_rows(d->stmt);
|
---|
566 | #else
|
---|
567 | numRows = 0;
|
---|
568 | #endif
|
---|
569 | } else {
|
---|
570 | numRows = mysql_num_rows(d->result);
|
---|
571 | }
|
---|
572 | if (at() == int(numRows))
|
---|
573 | return true;
|
---|
574 | if (!numRows)
|
---|
575 | return false;
|
---|
576 | return fetch(numRows - 1);
|
---|
577 | }
|
---|
578 |
|
---|
579 | bool QMYSQLResult::fetchFirst()
|
---|
580 | {
|
---|
581 | if (at() == 0)
|
---|
582 | return true;
|
---|
583 |
|
---|
584 | if (isForwardOnly())
|
---|
585 | return (at() == QSql::BeforeFirstRow) ? fetchNext() : false;
|
---|
586 | return fetch(0);
|
---|
587 | }
|
---|
588 |
|
---|
589 | QVariant QMYSQLResult::data(int field)
|
---|
590 | {
|
---|
591 |
|
---|
592 | if (!isSelect() || field >= d->fields.count()) {
|
---|
593 | qWarning("QMYSQLResult::data: column %d out of range", field);
|
---|
594 | return QVariant();
|
---|
595 | }
|
---|
596 |
|
---|
597 | if (!d->driver)
|
---|
598 | return QVariant();
|
---|
599 |
|
---|
600 | int fieldLength = 0;
|
---|
601 | const QMYSQLResultPrivate::QMyField &f = d->fields.at(field);
|
---|
602 | QString val;
|
---|
603 | if (d->preparedQuery) {
|
---|
604 | if (f.nullIndicator)
|
---|
605 | return QVariant(f.type);
|
---|
606 |
|
---|
607 | if (f.type != QVariant::ByteArray)
|
---|
608 | val = toUnicode(d->driver->d->tc, f.outField, f.bufLength);
|
---|
609 | } else {
|
---|
610 | if (d->row[field] == NULL) {
|
---|
611 | // NULL value
|
---|
612 | return QVariant(f.type);
|
---|
613 | }
|
---|
614 | fieldLength = mysql_fetch_lengths(d->result)[field];
|
---|
615 | if (f.type != QVariant::ByteArray)
|
---|
616 | val = toUnicode(d->driver->d->tc, d->row[field], fieldLength);
|
---|
617 | }
|
---|
618 |
|
---|
619 | switch(f.type) {
|
---|
620 | case QVariant::LongLong:
|
---|
621 | return QVariant(val.toLongLong());
|
---|
622 | case QVariant::ULongLong:
|
---|
623 | return QVariant(val.toULongLong());
|
---|
624 | case QVariant::Int:
|
---|
625 | return QVariant(val.toInt());
|
---|
626 | case QVariant::UInt:
|
---|
627 | return QVariant(val.toUInt());
|
---|
628 | case QVariant::Double: {
|
---|
629 | QVariant v;
|
---|
630 | bool ok=false;
|
---|
631 | double dbl = val.toDouble(&ok);
|
---|
632 | switch(numericalPrecisionPolicy()) {
|
---|
633 | case QSql::LowPrecisionInt32:
|
---|
634 | v=QVariant(dbl).toInt();
|
---|
635 | break;
|
---|
636 | case QSql::LowPrecisionInt64:
|
---|
637 | v = QVariant(dbl).toLongLong();
|
---|
638 | break;
|
---|
639 | case QSql::LowPrecisionDouble:
|
---|
640 | v = QVariant(dbl);
|
---|
641 | break;
|
---|
642 | case QSql::HighPrecision:
|
---|
643 | default:
|
---|
644 | v = val;
|
---|
645 | ok = true;
|
---|
646 | break;
|
---|
647 | }
|
---|
648 | if(ok)
|
---|
649 | return v;
|
---|
650 | else
|
---|
651 | return QVariant();
|
---|
652 | }
|
---|
653 | return QVariant(val.toDouble());
|
---|
654 | case QVariant::Date:
|
---|
655 | return qDateFromString(val);
|
---|
656 | case QVariant::Time:
|
---|
657 | return qTimeFromString(val);
|
---|
658 | case QVariant::DateTime:
|
---|
659 | return qDateTimeFromString(val);
|
---|
660 | case QVariant::ByteArray: {
|
---|
661 |
|
---|
662 | QByteArray ba;
|
---|
663 | if (d->preparedQuery) {
|
---|
664 | ba = QByteArray(f.outField, f.bufLength);
|
---|
665 | } else {
|
---|
666 | ba = QByteArray(d->row[field], fieldLength);
|
---|
667 | }
|
---|
668 | return QVariant(ba);
|
---|
669 | }
|
---|
670 | default:
|
---|
671 | case QVariant::String:
|
---|
672 | return QVariant(val);
|
---|
673 | }
|
---|
674 | qWarning("QMYSQLResult::data: unknown data type");
|
---|
675 | return QVariant();
|
---|
676 | }
|
---|
677 |
|
---|
678 | bool QMYSQLResult::isNull(int field)
|
---|
679 | {
|
---|
680 | if (d->preparedQuery)
|
---|
681 | return d->fields.at(field).nullIndicator;
|
---|
682 | else
|
---|
683 | return d->row[field] == NULL;
|
---|
684 | }
|
---|
685 |
|
---|
686 | bool QMYSQLResult::reset (const QString& query)
|
---|
687 | {
|
---|
688 | if (!driver() || !driver()->isOpen() || driver()->isOpenError() || !d->driver)
|
---|
689 | return false;
|
---|
690 |
|
---|
691 | d->preparedQuery = false;
|
---|
692 |
|
---|
693 | cleanup();
|
---|
694 |
|
---|
695 | const QByteArray encQuery(fromUnicode(d->driver->d->tc, query));
|
---|
696 | if (mysql_real_query(d->driver->d->mysql, encQuery.data(), encQuery.length())) {
|
---|
697 | setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to execute query"),
|
---|
698 | QSqlError::StatementError, d->driver->d));
|
---|
699 | return false;
|
---|
700 | }
|
---|
701 | d->result = mysql_store_result(d->driver->d->mysql);
|
---|
702 | if (!d->result && mysql_field_count(d->driver->d->mysql) > 0) {
|
---|
703 | setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to store result"),
|
---|
704 | QSqlError::StatementError, d->driver->d));
|
---|
705 | return false;
|
---|
706 | }
|
---|
707 | int numFields = mysql_field_count(d->driver->d->mysql);
|
---|
708 | setSelect(numFields != 0);
|
---|
709 | d->fields.resize(numFields);
|
---|
710 | d->rowsAffected = mysql_affected_rows(d->driver->d->mysql);
|
---|
711 |
|
---|
712 | if (isSelect()) {
|
---|
713 | for(int i = 0; i < numFields; i++) {
|
---|
714 | MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
|
---|
715 | d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
|
---|
716 | }
|
---|
717 | setAt(QSql::BeforeFirstRow);
|
---|
718 | }
|
---|
719 | setActive(true);
|
---|
720 | return isActive();
|
---|
721 | }
|
---|
722 |
|
---|
723 | int QMYSQLResult::size()
|
---|
724 | {
|
---|
725 | if (d->driver && isSelect())
|
---|
726 | if (d->preparedQuery)
|
---|
727 | #if MYSQL_VERSION_ID >= 40108
|
---|
728 | return mysql_stmt_num_rows(d->stmt);
|
---|
729 | #else
|
---|
730 | return -1;
|
---|
731 | #endif
|
---|
732 | else
|
---|
733 | return int(mysql_num_rows(d->result));
|
---|
734 | else
|
---|
735 | return -1;
|
---|
736 | }
|
---|
737 |
|
---|
738 | int QMYSQLResult::numRowsAffected()
|
---|
739 | {
|
---|
740 | return d->rowsAffected;
|
---|
741 | }
|
---|
742 |
|
---|
743 | QVariant QMYSQLResult::lastInsertId() const
|
---|
744 | {
|
---|
745 | if (!isActive() || !d->driver)
|
---|
746 | return QVariant();
|
---|
747 |
|
---|
748 | if (d->preparedQuery) {
|
---|
749 | #if MYSQL_VERSION_ID >= 40108
|
---|
750 | quint64 id = mysql_stmt_insert_id(d->stmt);
|
---|
751 | if (id)
|
---|
752 | return QVariant(id);
|
---|
753 | #endif
|
---|
754 | } else {
|
---|
755 | quint64 id = mysql_insert_id(d->driver->d->mysql);
|
---|
756 | if (id)
|
---|
757 | return QVariant(id);
|
---|
758 | }
|
---|
759 | return QVariant();
|
---|
760 | }
|
---|
761 |
|
---|
762 | QSqlRecord QMYSQLResult::record() const
|
---|
763 | {
|
---|
764 | QSqlRecord info;
|
---|
765 | MYSQL_RES *res;
|
---|
766 | if (!isActive() || !isSelect() || !d->driver)
|
---|
767 | return info;
|
---|
768 |
|
---|
769 | #if MYSQL_VERSION_ID >= 40108
|
---|
770 | res = d->preparedQuery ? d->meta : d->result;
|
---|
771 | #else
|
---|
772 | res = d->result;
|
---|
773 | #endif
|
---|
774 |
|
---|
775 | if (!mysql_errno(d->driver->d->mysql)) {
|
---|
776 | mysql_field_seek(res, 0);
|
---|
777 | MYSQL_FIELD* field = mysql_fetch_field(res);
|
---|
778 | while(field) {
|
---|
779 | info.append(qToField(field, d->driver->d->tc));
|
---|
780 | field = mysql_fetch_field(res);
|
---|
781 | }
|
---|
782 | }
|
---|
783 | mysql_field_seek(res, 0);
|
---|
784 | return info;
|
---|
785 | }
|
---|
786 |
|
---|
787 | bool QMYSQLResult::nextResult()
|
---|
788 | {
|
---|
789 | if(!d->driver)
|
---|
790 | return false;
|
---|
791 | #if MYSQL_VERSION_ID >= 40100
|
---|
792 | setAt(-1);
|
---|
793 | setActive(false);
|
---|
794 |
|
---|
795 | if (d->result && isSelect())
|
---|
796 | mysql_free_result(d->result);
|
---|
797 | d->result = 0;
|
---|
798 | setSelect(false);
|
---|
799 |
|
---|
800 | for (int i = 0; i < d->fields.count(); ++i)
|
---|
801 | delete[] d->fields[i].outField;
|
---|
802 | d->fields.clear();
|
---|
803 |
|
---|
804 | int status = mysql_next_result(d->driver->d->mysql);
|
---|
805 | if (status > 0) {
|
---|
806 | setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to execute next query"),
|
---|
807 | QSqlError::StatementError, d->driver->d));
|
---|
808 | return false;
|
---|
809 | } else if (status == -1) {
|
---|
810 | return false; // No more result sets
|
---|
811 | }
|
---|
812 |
|
---|
813 | d->result = mysql_store_result(d->driver->d->mysql);
|
---|
814 | int numFields = mysql_field_count(d->driver->d->mysql);
|
---|
815 | if (!d->result && numFields > 0) {
|
---|
816 | setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to store next result"),
|
---|
817 | QSqlError::StatementError, d->driver->d));
|
---|
818 | return false;
|
---|
819 | }
|
---|
820 |
|
---|
821 | setSelect(numFields > 0);
|
---|
822 | d->fields.resize(numFields);
|
---|
823 | d->rowsAffected = mysql_affected_rows(d->driver->d->mysql);
|
---|
824 |
|
---|
825 | if (isSelect()) {
|
---|
826 | for (int i = 0; i < numFields; i++) {
|
---|
827 | MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
|
---|
828 | d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
|
---|
829 | }
|
---|
830 | }
|
---|
831 |
|
---|
832 | setActive(true);
|
---|
833 | return true;
|
---|
834 | #else
|
---|
835 | return false;
|
---|
836 | #endif
|
---|
837 | }
|
---|
838 |
|
---|
839 | void QMYSQLResult::virtual_hook(int id, void *data)
|
---|
840 | {
|
---|
841 | switch (id) {
|
---|
842 | case QSqlResult::NextResult:
|
---|
843 | Q_ASSERT(data);
|
---|
844 | *static_cast<bool*>(data) = nextResult();
|
---|
845 | break;
|
---|
846 | default:
|
---|
847 | QSqlResult::virtual_hook(id, data);
|
---|
848 | }
|
---|
849 | }
|
---|
850 |
|
---|
851 |
|
---|
852 | #if MYSQL_VERSION_ID >= 40108
|
---|
853 |
|
---|
854 | static MYSQL_TIME *toMySqlDate(QDate date, QTime time, QVariant::Type type)
|
---|
855 | {
|
---|
856 | Q_ASSERT(type == QVariant::Time || type == QVariant::Date
|
---|
857 | || type == QVariant::DateTime);
|
---|
858 |
|
---|
859 | MYSQL_TIME *myTime = new MYSQL_TIME;
|
---|
860 | memset(myTime, 0, sizeof(MYSQL_TIME));
|
---|
861 |
|
---|
862 | if (type == QVariant::Time || type == QVariant::DateTime) {
|
---|
863 | myTime->hour = time.hour();
|
---|
864 | myTime->minute = time.minute();
|
---|
865 | myTime->second = time.second();
|
---|
866 | myTime->second_part = time.msec();
|
---|
867 | }
|
---|
868 | if (type == QVariant::Date || type == QVariant::DateTime) {
|
---|
869 | myTime->year = date.year();
|
---|
870 | myTime->month = date.month();
|
---|
871 | myTime->day = date.day();
|
---|
872 | }
|
---|
873 |
|
---|
874 | return myTime;
|
---|
875 | }
|
---|
876 |
|
---|
877 | bool QMYSQLResult::prepare(const QString& query)
|
---|
878 | {
|
---|
879 | if(!d->driver)
|
---|
880 | return false;
|
---|
881 | #if MYSQL_VERSION_ID >= 40108
|
---|
882 | cleanup();
|
---|
883 | if (!d->driver->d->preparedQuerysEnabled)
|
---|
884 | return QSqlResult::prepare(query);
|
---|
885 |
|
---|
886 | int r;
|
---|
887 |
|
---|
888 | if (query.isEmpty())
|
---|
889 | return false;
|
---|
890 |
|
---|
891 | if (!d->stmt)
|
---|
892 | d->stmt = mysql_stmt_init(d->driver->d->mysql);
|
---|
893 | if (!d->stmt) {
|
---|
894 | setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to prepare statement"),
|
---|
895 | QSqlError::StatementError, d->driver->d));
|
---|
896 | return false;
|
---|
897 | }
|
---|
898 |
|
---|
899 | const QByteArray encQuery(fromUnicode(d->driver->d->tc, query));
|
---|
900 | r = mysql_stmt_prepare(d->stmt, encQuery.constData(), encQuery.length());
|
---|
901 | if (r != 0) {
|
---|
902 | setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
|
---|
903 | "Unable to prepare statement"), QSqlError::StatementError, d->stmt));
|
---|
904 | cleanup();
|
---|
905 | return false;
|
---|
906 | }
|
---|
907 |
|
---|
908 | if (mysql_stmt_param_count(d->stmt) > 0) {// allocate memory for outvalues
|
---|
909 | d->outBinds = new MYSQL_BIND[mysql_stmt_param_count(d->stmt)];
|
---|
910 | }
|
---|
911 |
|
---|
912 | setSelect(d->bindInValues());
|
---|
913 | d->preparedQuery = true;
|
---|
914 | return true;
|
---|
915 | #else
|
---|
916 | return false;
|
---|
917 | #endif
|
---|
918 | }
|
---|
919 |
|
---|
920 | bool QMYSQLResult::exec()
|
---|
921 | {
|
---|
922 | if (!d->driver)
|
---|
923 | return false;
|
---|
924 | if (!d->preparedQuery)
|
---|
925 | return QSqlResult::exec();
|
---|
926 | if (!d->stmt)
|
---|
927 | return false;
|
---|
928 |
|
---|
929 | int r = 0;
|
---|
930 | MYSQL_BIND* currBind;
|
---|
931 | QVector<MYSQL_TIME *> timeVector;
|
---|
932 | QVector<QByteArray> stringVector;
|
---|
933 | QVector<my_bool> nullVector;
|
---|
934 |
|
---|
935 | const QVector<QVariant> values = boundValues();
|
---|
936 |
|
---|
937 | r = mysql_stmt_reset(d->stmt);
|
---|
938 | if (r != 0) {
|
---|
939 | setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
|
---|
940 | "Unable to reset statement"), QSqlError::StatementError, d->stmt));
|
---|
941 | return false;
|
---|
942 | }
|
---|
943 |
|
---|
944 | if (mysql_stmt_param_count(d->stmt) > 0 &&
|
---|
945 | mysql_stmt_param_count(d->stmt) == (uint)values.count()) {
|
---|
946 |
|
---|
947 | nullVector.resize(values.count());
|
---|
948 | for (int i = 0; i < values.count(); ++i) {
|
---|
949 | const QVariant &val = boundValues().at(i);
|
---|
950 | void *data = const_cast<void *>(val.constData());
|
---|
951 |
|
---|
952 | currBind = &d->outBinds[i];
|
---|
953 |
|
---|
954 | nullVector[i] = static_cast<my_bool>(val.isNull());
|
---|
955 | currBind->is_null = &nullVector[i];
|
---|
956 | currBind->length = 0;
|
---|
957 | currBind->is_unsigned = 0;
|
---|
958 |
|
---|
959 | switch (val.type()) {
|
---|
960 | case QVariant::ByteArray:
|
---|
961 | currBind->buffer_type = MYSQL_TYPE_BLOB;
|
---|
962 | currBind->buffer = const_cast<char *>(val.toByteArray().constData());
|
---|
963 | currBind->buffer_length = val.toByteArray().size();
|
---|
964 | break;
|
---|
965 |
|
---|
966 | case QVariant::Time:
|
---|
967 | case QVariant::Date:
|
---|
968 | case QVariant::DateTime: {
|
---|
969 | MYSQL_TIME *myTime = toMySqlDate(val.toDate(), val.toTime(), val.type());
|
---|
970 | timeVector.append(myTime);
|
---|
971 |
|
---|
972 | currBind->buffer = myTime;
|
---|
973 | switch(val.type()) {
|
---|
974 | case QVariant::Time:
|
---|
975 | currBind->buffer_type = MYSQL_TYPE_TIME;
|
---|
976 | myTime->time_type = MYSQL_TIMESTAMP_TIME;
|
---|
977 | break;
|
---|
978 | case QVariant::Date:
|
---|
979 | currBind->buffer_type = MYSQL_TYPE_DATE;
|
---|
980 | myTime->time_type = MYSQL_TIMESTAMP_DATE;
|
---|
981 | break;
|
---|
982 | case QVariant::DateTime:
|
---|
983 | currBind->buffer_type = MYSQL_TYPE_DATETIME;
|
---|
984 | myTime->time_type = MYSQL_TIMESTAMP_DATETIME;
|
---|
985 | break;
|
---|
986 | default:
|
---|
987 | break;
|
---|
988 | }
|
---|
989 | currBind->buffer_length = sizeof(MYSQL_TIME);
|
---|
990 | currBind->length = 0;
|
---|
991 | break; }
|
---|
992 | case QVariant::UInt:
|
---|
993 | case QVariant::Int:
|
---|
994 | case QVariant::Bool:
|
---|
995 | currBind->buffer_type = MYSQL_TYPE_LONG;
|
---|
996 | currBind->buffer = data;
|
---|
997 | currBind->buffer_length = sizeof(int);
|
---|
998 | currBind->is_unsigned = (val.type() != QVariant::Int);
|
---|
999 | break;
|
---|
1000 | case QVariant::Double:
|
---|
1001 | currBind->buffer_type = MYSQL_TYPE_DOUBLE;
|
---|
1002 | currBind->buffer = data;
|
---|
1003 | currBind->buffer_length = sizeof(double);
|
---|
1004 | break;
|
---|
1005 | case QVariant::LongLong:
|
---|
1006 | case QVariant::ULongLong:
|
---|
1007 | currBind->buffer_type = MYSQL_TYPE_LONGLONG;
|
---|
1008 | currBind->buffer = data;
|
---|
1009 | currBind->buffer_length = sizeof(qint64);
|
---|
1010 | currBind->is_unsigned = (val.type() == QVariant::ULongLong);
|
---|
1011 | break;
|
---|
1012 | case QVariant::String:
|
---|
1013 | default: {
|
---|
1014 | QByteArray ba = fromUnicode(d->driver->d->tc, val.toString());
|
---|
1015 | stringVector.append(ba);
|
---|
1016 | currBind->buffer_type = MYSQL_TYPE_STRING;
|
---|
1017 | currBind->buffer = const_cast<char *>(ba.constData());
|
---|
1018 | currBind->buffer_length = ba.length();
|
---|
1019 | break; }
|
---|
1020 | }
|
---|
1021 | }
|
---|
1022 |
|
---|
1023 | r = mysql_stmt_bind_param(d->stmt, d->outBinds);
|
---|
1024 | if (r != 0) {
|
---|
1025 | setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
|
---|
1026 | "Unable to bind value"), QSqlError::StatementError, d->stmt));
|
---|
1027 | qDeleteAll(timeVector);
|
---|
1028 | return false;
|
---|
1029 | }
|
---|
1030 | }
|
---|
1031 | r = mysql_stmt_execute(d->stmt);
|
---|
1032 |
|
---|
1033 | qDeleteAll(timeVector);
|
---|
1034 |
|
---|
1035 | if (r != 0) {
|
---|
1036 | setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
|
---|
1037 | "Unable to execute statement"), QSqlError::StatementError, d->stmt));
|
---|
1038 | return false;
|
---|
1039 | }
|
---|
1040 | //if there is meta-data there is also data
|
---|
1041 | setSelect(d->meta);
|
---|
1042 |
|
---|
1043 | d->rowsAffected = mysql_stmt_affected_rows(d->stmt);
|
---|
1044 |
|
---|
1045 | if (isSelect()) {
|
---|
1046 | my_bool update_max_length = true;
|
---|
1047 |
|
---|
1048 | r = mysql_stmt_bind_result(d->stmt, d->inBinds);
|
---|
1049 | if (r != 0) {
|
---|
1050 | setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
|
---|
1051 | "Unable to bind outvalues"), QSqlError::StatementError, d->stmt));
|
---|
1052 | return false;
|
---|
1053 | }
|
---|
1054 | if (d->hasBlobs)
|
---|
1055 | mysql_stmt_attr_set(d->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &update_max_length);
|
---|
1056 |
|
---|
1057 | r = mysql_stmt_store_result(d->stmt);
|
---|
1058 | if (r != 0) {
|
---|
1059 | setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
|
---|
1060 | "Unable to store statement results"), QSqlError::StatementError, d->stmt));
|
---|
1061 | return false;
|
---|
1062 | }
|
---|
1063 |
|
---|
1064 | if (d->hasBlobs) {
|
---|
1065 | // mysql_stmt_store_result() with STMT_ATTR_UPDATE_MAX_LENGTH set to true crashes
|
---|
1066 | // when called without a preceding call to mysql_stmt_bind_result()
|
---|
1067 | // in versions < 4.1.8
|
---|
1068 | d->bindBlobs();
|
---|
1069 | r = mysql_stmt_bind_result(d->stmt, d->inBinds);
|
---|
1070 | if (r != 0) {
|
---|
1071 | setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
|
---|
1072 | "Unable to bind outvalues"), QSqlError::StatementError, d->stmt));
|
---|
1073 | return false;
|
---|
1074 | }
|
---|
1075 | }
|
---|
1076 | setAt(QSql::BeforeFirstRow);
|
---|
1077 | }
|
---|
1078 | setActive(true);
|
---|
1079 | return true;
|
---|
1080 | }
|
---|
1081 | #endif
|
---|
1082 | /////////////////////////////////////////////////////////
|
---|
1083 |
|
---|
1084 | static int qMySqlConnectionCount = 0;
|
---|
1085 | static bool qMySqlInitHandledByUser = false;
|
---|
1086 |
|
---|
1087 | static void qLibraryInit()
|
---|
1088 | {
|
---|
1089 | #ifndef Q_NO_MYSQL_EMBEDDED
|
---|
1090 | # if MYSQL_VERSION_ID >= 40000
|
---|
1091 | if (qMySqlInitHandledByUser || qMySqlConnectionCount > 1)
|
---|
1092 | return;
|
---|
1093 |
|
---|
1094 | # if (MYSQL_VERSION_ID >= 40110 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50003
|
---|
1095 | if (mysql_library_init(0, 0, 0)) {
|
---|
1096 | # else
|
---|
1097 | if (mysql_server_init(0, 0, 0)) {
|
---|
1098 | # endif
|
---|
1099 | qWarning("QMYSQLDriver::qServerInit: unable to start server.");
|
---|
1100 | }
|
---|
1101 | # endif // MYSQL_VERSION_ID
|
---|
1102 | #endif // Q_NO_MYSQL_EMBEDDED
|
---|
1103 | }
|
---|
1104 |
|
---|
1105 | static void qLibraryEnd()
|
---|
1106 | {
|
---|
1107 | #ifndef Q_NO_MYSQL_EMBEDDED
|
---|
1108 | # if MYSQL_VERSION_ID > 40000
|
---|
1109 | # if (MYSQL_VERSION_ID >= 40110 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50003
|
---|
1110 | mysql_library_end();
|
---|
1111 | # else
|
---|
1112 | mysql_server_end();
|
---|
1113 | # endif
|
---|
1114 | # endif
|
---|
1115 | #endif
|
---|
1116 | }
|
---|
1117 |
|
---|
1118 | QMYSQLDriver::QMYSQLDriver(QObject * parent)
|
---|
1119 | : QSqlDriver(parent)
|
---|
1120 | {
|
---|
1121 | init();
|
---|
1122 | qLibraryInit();
|
---|
1123 | }
|
---|
1124 |
|
---|
1125 | /*!
|
---|
1126 | Create a driver instance with the open connection handle, \a con.
|
---|
1127 | The instance's parent (owner) is \a parent.
|
---|
1128 | */
|
---|
1129 |
|
---|
1130 | QMYSQLDriver::QMYSQLDriver(MYSQL * con, QObject * parent)
|
---|
1131 | : QSqlDriver(parent)
|
---|
1132 | {
|
---|
1133 | init();
|
---|
1134 | if (con) {
|
---|
1135 | d->mysql = (MYSQL *) con;
|
---|
1136 | #ifndef QT_NO_TEXTCODEC
|
---|
1137 | d->tc = codec(con);
|
---|
1138 | #endif
|
---|
1139 | setOpen(true);
|
---|
1140 | setOpenError(false);
|
---|
1141 | if (qMySqlConnectionCount == 1)
|
---|
1142 | qMySqlInitHandledByUser = true;
|
---|
1143 | } else {
|
---|
1144 | qLibraryInit();
|
---|
1145 | }
|
---|
1146 | }
|
---|
1147 |
|
---|
1148 | void QMYSQLDriver::init()
|
---|
1149 | {
|
---|
1150 | d = new QMYSQLDriverPrivate();
|
---|
1151 | d->mysql = 0;
|
---|
1152 | qMySqlConnectionCount++;
|
---|
1153 | }
|
---|
1154 |
|
---|
1155 | QMYSQLDriver::~QMYSQLDriver()
|
---|
1156 | {
|
---|
1157 | qMySqlConnectionCount--;
|
---|
1158 | if (qMySqlConnectionCount == 0 && !qMySqlInitHandledByUser)
|
---|
1159 | qLibraryEnd();
|
---|
1160 | delete d;
|
---|
1161 | }
|
---|
1162 |
|
---|
1163 | bool QMYSQLDriver::hasFeature(DriverFeature f) const
|
---|
1164 | {
|
---|
1165 | switch (f) {
|
---|
1166 | case Transactions:
|
---|
1167 | // CLIENT_TRANSACTION should be defined in all recent mysql client libs > 3.23.34
|
---|
1168 | #ifdef CLIENT_TRANSACTIONS
|
---|
1169 | if (d->mysql) {
|
---|
1170 | if ((d->mysql->server_capabilities & CLIENT_TRANSACTIONS) == CLIENT_TRANSACTIONS)
|
---|
1171 | return true;
|
---|
1172 | }
|
---|
1173 | #endif
|
---|
1174 | return false;
|
---|
1175 | case NamedPlaceholders:
|
---|
1176 | case BatchOperations:
|
---|
1177 | case SimpleLocking:
|
---|
1178 | case EventNotifications:
|
---|
1179 | case FinishQuery:
|
---|
1180 | return false;
|
---|
1181 | case QuerySize:
|
---|
1182 | case BLOB:
|
---|
1183 | case LastInsertId:
|
---|
1184 | case Unicode:
|
---|
1185 | case LowPrecisionNumbers:
|
---|
1186 | return true;
|
---|
1187 | case PreparedQueries:
|
---|
1188 | case PositionalPlaceholders:
|
---|
1189 | #if MYSQL_VERSION_ID >= 40108
|
---|
1190 | return d->preparedQuerysEnabled;
|
---|
1191 | #else
|
---|
1192 | return false;
|
---|
1193 | #endif
|
---|
1194 | case MultipleResultSets:
|
---|
1195 | #if MYSQL_VERSION_ID >= 40100
|
---|
1196 | return true;
|
---|
1197 | #else
|
---|
1198 | return false;
|
---|
1199 | #endif
|
---|
1200 | }
|
---|
1201 | return false;
|
---|
1202 | }
|
---|
1203 |
|
---|
1204 | static void setOptionFlag(uint &optionFlags, const QString &opt)
|
---|
1205 | {
|
---|
1206 | if (opt == QLatin1String("CLIENT_COMPRESS"))
|
---|
1207 | optionFlags |= CLIENT_COMPRESS;
|
---|
1208 | else if (opt == QLatin1String("CLIENT_FOUND_ROWS"))
|
---|
1209 | optionFlags |= CLIENT_FOUND_ROWS;
|
---|
1210 | else if (opt == QLatin1String("CLIENT_IGNORE_SPACE"))
|
---|
1211 | optionFlags |= CLIENT_IGNORE_SPACE;
|
---|
1212 | else if (opt == QLatin1String("CLIENT_INTERACTIVE"))
|
---|
1213 | optionFlags |= CLIENT_INTERACTIVE;
|
---|
1214 | else if (opt == QLatin1String("CLIENT_NO_SCHEMA"))
|
---|
1215 | optionFlags |= CLIENT_NO_SCHEMA;
|
---|
1216 | else if (opt == QLatin1String("CLIENT_ODBC"))
|
---|
1217 | optionFlags |= CLIENT_ODBC;
|
---|
1218 | else if (opt == QLatin1String("CLIENT_SSL"))
|
---|
1219 | optionFlags |= CLIENT_SSL;
|
---|
1220 | else
|
---|
1221 | qWarning("QMYSQLDriver::open: Unknown connect option '%s'", opt.toLocal8Bit().constData());
|
---|
1222 | }
|
---|
1223 |
|
---|
1224 | bool QMYSQLDriver::open(const QString& db,
|
---|
1225 | const QString& user,
|
---|
1226 | const QString& password,
|
---|
1227 | const QString& host,
|
---|
1228 | int port,
|
---|
1229 | const QString& connOpts)
|
---|
1230 | {
|
---|
1231 | if (isOpen())
|
---|
1232 | close();
|
---|
1233 |
|
---|
1234 | /* This is a hack to get MySQL's stored procedure support working.
|
---|
1235 | Since a stored procedure _may_ return multiple result sets,
|
---|
1236 | we have to enable CLIEN_MULTI_STATEMENTS here, otherwise _any_
|
---|
1237 | stored procedure call will fail.
|
---|
1238 | */
|
---|
1239 | unsigned int optionFlags = Q_CLIENT_MULTI_STATEMENTS;
|
---|
1240 | const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
|
---|
1241 | QString unixSocket;
|
---|
1242 | #if MYSQL_VERSION_ID >= 50000
|
---|
1243 | my_bool reconnect=false;
|
---|
1244 | #endif
|
---|
1245 |
|
---|
1246 | // extract the real options from the string
|
---|
1247 | for (int i = 0; i < opts.count(); ++i) {
|
---|
1248 | QString tmp(opts.at(i).simplified());
|
---|
1249 | int idx;
|
---|
1250 | if ((idx = tmp.indexOf(QLatin1Char('='))) != -1) {
|
---|
1251 | QString val = tmp.mid(idx + 1).simplified();
|
---|
1252 | QString opt = tmp.left(idx).simplified();
|
---|
1253 | if (opt == QLatin1String("UNIX_SOCKET"))
|
---|
1254 | unixSocket = val;
|
---|
1255 | #if MYSQL_VERSION_ID >= 50000
|
---|
1256 | else if (opt == QLatin1String("MYSQL_OPT_RECONNECT")) {
|
---|
1257 | if (val == QLatin1String("TRUE") || val == QLatin1String("1") || val.isEmpty())
|
---|
1258 | reconnect = true;
|
---|
1259 | }
|
---|
1260 | #endif
|
---|
1261 | else if (val == QLatin1String("TRUE") || val == QLatin1String("1"))
|
---|
1262 | setOptionFlag(optionFlags, tmp.left(idx).simplified());
|
---|
1263 | else
|
---|
1264 | qWarning("QMYSQLDriver::open: Illegal connect option value '%s'",
|
---|
1265 | tmp.toLocal8Bit().constData());
|
---|
1266 | } else {
|
---|
1267 | setOptionFlag(optionFlags, tmp);
|
---|
1268 | }
|
---|
1269 | }
|
---|
1270 |
|
---|
1271 | if ((d->mysql = mysql_init((MYSQL*) 0)) &&
|
---|
1272 | mysql_real_connect(d->mysql,
|
---|
1273 | host.isNull() ? static_cast<const char *>(0)
|
---|
1274 | : host.toLocal8Bit().constData(),
|
---|
1275 | user.isNull() ? static_cast<const char *>(0)
|
---|
1276 | : user.toLocal8Bit().constData(),
|
---|
1277 | password.isNull() ? static_cast<const char *>(0)
|
---|
1278 | : password.toLocal8Bit().constData(),
|
---|
1279 | db.isNull() ? static_cast<const char *>(0)
|
---|
1280 | : db.toLocal8Bit().constData(),
|
---|
1281 | (port > -1) ? port : 0,
|
---|
1282 | unixSocket.isNull() ? static_cast<const char *>(0)
|
---|
1283 | : unixSocket.toLocal8Bit().constData(),
|
---|
1284 | optionFlags))
|
---|
1285 | {
|
---|
1286 | if (!db.isEmpty() && mysql_select_db(d->mysql, db.toLocal8Bit().constData())) {
|
---|
1287 | setLastError(qMakeError(tr("Unable to open database '") + db +
|
---|
1288 | QLatin1Char('\''), QSqlError::ConnectionError, d));
|
---|
1289 | mysql_close(d->mysql);
|
---|
1290 | setOpenError(true);
|
---|
1291 | return false;
|
---|
1292 | }
|
---|
1293 | #if MYSQL_VERSION_ID >= 50000
|
---|
1294 | if(reconnect)
|
---|
1295 | mysql_options(d->mysql, MYSQL_OPT_RECONNECT, &reconnect);
|
---|
1296 | #endif
|
---|
1297 | } else {
|
---|
1298 | setLastError(qMakeError(tr("Unable to connect"),
|
---|
1299 | QSqlError::ConnectionError, d));
|
---|
1300 | mysql_close(d->mysql);
|
---|
1301 | d->mysql = NULL;
|
---|
1302 | setOpenError(true);
|
---|
1303 | return false;
|
---|
1304 | }
|
---|
1305 |
|
---|
1306 | #if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007
|
---|
1307 | // force the communication to be utf8
|
---|
1308 | mysql_set_character_set(d->mysql, "utf8");
|
---|
1309 | #endif
|
---|
1310 | #ifndef QT_NO_TEXTCODEC
|
---|
1311 | d->tc = codec(d->mysql);
|
---|
1312 | #endif
|
---|
1313 |
|
---|
1314 | #if MYSQL_VERSION_ID >= 40108
|
---|
1315 | d->preparedQuerysEnabled = mysql_get_client_version() >= 40108
|
---|
1316 | && mysql_get_server_version(d->mysql) >= 40100;
|
---|
1317 | #else
|
---|
1318 | d->preparedQuerysEnabled = false;
|
---|
1319 | #endif
|
---|
1320 |
|
---|
1321 | #ifndef QT_NO_THREAD
|
---|
1322 | mysql_thread_init();
|
---|
1323 | #endif
|
---|
1324 |
|
---|
1325 |
|
---|
1326 | setOpen(true);
|
---|
1327 | setOpenError(false);
|
---|
1328 | return true;
|
---|
1329 | }
|
---|
1330 |
|
---|
1331 | void QMYSQLDriver::close()
|
---|
1332 | {
|
---|
1333 | if (isOpen()) {
|
---|
1334 | #ifndef QT_NO_THREAD
|
---|
1335 | mysql_thread_end();
|
---|
1336 | #endif
|
---|
1337 | mysql_close(d->mysql);
|
---|
1338 | d->mysql = NULL;
|
---|
1339 | setOpen(false);
|
---|
1340 | setOpenError(false);
|
---|
1341 | }
|
---|
1342 | }
|
---|
1343 |
|
---|
1344 | QSqlResult *QMYSQLDriver::createResult() const
|
---|
1345 | {
|
---|
1346 | return new QMYSQLResult(this);
|
---|
1347 | }
|
---|
1348 |
|
---|
1349 | QStringList QMYSQLDriver::tables(QSql::TableType type) const
|
---|
1350 | {
|
---|
1351 | QStringList tl;
|
---|
1352 | #if MYSQL_VERSION_ID >= 40100
|
---|
1353 | if( mysql_get_server_version(d->mysql) < 50000)
|
---|
1354 | {
|
---|
1355 | #endif
|
---|
1356 | if (!isOpen())
|
---|
1357 | return tl;
|
---|
1358 | if (!(type & QSql::Tables))
|
---|
1359 | return tl;
|
---|
1360 |
|
---|
1361 | MYSQL_RES* tableRes = mysql_list_tables(d->mysql, NULL);
|
---|
1362 | MYSQL_ROW row;
|
---|
1363 | int i = 0;
|
---|
1364 | while (tableRes) {
|
---|
1365 | mysql_data_seek(tableRes, i);
|
---|
1366 | row = mysql_fetch_row(tableRes);
|
---|
1367 | if (!row)
|
---|
1368 | break;
|
---|
1369 | tl.append(toUnicode(d->tc, row[0]));
|
---|
1370 | i++;
|
---|
1371 | }
|
---|
1372 | mysql_free_result(tableRes);
|
---|
1373 | #if MYSQL_VERSION_ID >= 40100
|
---|
1374 | } else {
|
---|
1375 | QSqlQuery q(createResult());
|
---|
1376 | if(type & QSql::Tables) {
|
---|
1377 | q.exec(QLatin1String("select table_name from information_schema.tables where table_type = 'BASE TABLE'"));
|
---|
1378 | while(q.next())
|
---|
1379 | tl.append(q.value(0).toString());
|
---|
1380 | }
|
---|
1381 | if(type & QSql::Views) {
|
---|
1382 | q.exec(QLatin1String("select table_name from information_schema.tables where table_type = 'VIEW'"));
|
---|
1383 | while(q.next())
|
---|
1384 | tl.append(q.value(0).toString());
|
---|
1385 | }
|
---|
1386 | }
|
---|
1387 | #endif
|
---|
1388 | return tl;
|
---|
1389 | }
|
---|
1390 |
|
---|
1391 | QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const
|
---|
1392 | {
|
---|
1393 | QSqlIndex idx;
|
---|
1394 | if (!isOpen())
|
---|
1395 | return idx;
|
---|
1396 |
|
---|
1397 | QSqlQuery i(createResult());
|
---|
1398 | QString stmt(QLatin1String("show index from %1;"));
|
---|
1399 | QSqlRecord fil = record(tablename);
|
---|
1400 | i.exec(stmt.arg(tablename));
|
---|
1401 | while (i.isActive() && i.next()) {
|
---|
1402 | if (i.value(2).toString() == QLatin1String("PRIMARY")) {
|
---|
1403 | idx.append(fil.field(i.value(4).toString()));
|
---|
1404 | idx.setCursorName(i.value(0).toString());
|
---|
1405 | idx.setName(i.value(2).toString());
|
---|
1406 | }
|
---|
1407 | }
|
---|
1408 |
|
---|
1409 | return idx;
|
---|
1410 | }
|
---|
1411 |
|
---|
1412 | QSqlRecord QMYSQLDriver::record(const QString& tablename) const
|
---|
1413 | {
|
---|
1414 | QString table=tablename;
|
---|
1415 | if(isIdentifierEscaped(table, QSqlDriver::TableName))
|
---|
1416 | table = stripDelimiters(table, QSqlDriver::TableName);
|
---|
1417 |
|
---|
1418 | QSqlRecord info;
|
---|
1419 | if (!isOpen())
|
---|
1420 | return info;
|
---|
1421 | MYSQL_RES* r = mysql_list_fields(d->mysql, table.toLocal8Bit().constData(), 0);
|
---|
1422 | if (!r) {
|
---|
1423 | return info;
|
---|
1424 | }
|
---|
1425 | MYSQL_FIELD* field;
|
---|
1426 |
|
---|
1427 | while ((field = mysql_fetch_field(r)))
|
---|
1428 | info.append(qToField(field, d->tc));
|
---|
1429 | mysql_free_result(r);
|
---|
1430 | return info;
|
---|
1431 | }
|
---|
1432 |
|
---|
1433 | QVariant QMYSQLDriver::handle() const
|
---|
1434 | {
|
---|
1435 | return qVariantFromValue(d->mysql);
|
---|
1436 | }
|
---|
1437 |
|
---|
1438 | bool QMYSQLDriver::beginTransaction()
|
---|
1439 | {
|
---|
1440 | #ifndef CLIENT_TRANSACTIONS
|
---|
1441 | return false;
|
---|
1442 | #endif
|
---|
1443 | if (!isOpen()) {
|
---|
1444 | qWarning("QMYSQLDriver::beginTransaction: Database not open");
|
---|
1445 | return false;
|
---|
1446 | }
|
---|
1447 | if (mysql_query(d->mysql, "BEGIN WORK")) {
|
---|
1448 | setLastError(qMakeError(tr("Unable to begin transaction"),
|
---|
1449 | QSqlError::StatementError, d));
|
---|
1450 | return false;
|
---|
1451 | }
|
---|
1452 | return true;
|
---|
1453 | }
|
---|
1454 |
|
---|
1455 | bool QMYSQLDriver::commitTransaction()
|
---|
1456 | {
|
---|
1457 | #ifndef CLIENT_TRANSACTIONS
|
---|
1458 | return false;
|
---|
1459 | #endif
|
---|
1460 | if (!isOpen()) {
|
---|
1461 | qWarning("QMYSQLDriver::commitTransaction: Database not open");
|
---|
1462 | return false;
|
---|
1463 | }
|
---|
1464 | if (mysql_query(d->mysql, "COMMIT")) {
|
---|
1465 | setLastError(qMakeError(tr("Unable to commit transaction"),
|
---|
1466 | QSqlError::StatementError, d));
|
---|
1467 | return false;
|
---|
1468 | }
|
---|
1469 | return true;
|
---|
1470 | }
|
---|
1471 |
|
---|
1472 | bool QMYSQLDriver::rollbackTransaction()
|
---|
1473 | {
|
---|
1474 | #ifndef CLIENT_TRANSACTIONS
|
---|
1475 | return false;
|
---|
1476 | #endif
|
---|
1477 | if (!isOpen()) {
|
---|
1478 | qWarning("QMYSQLDriver::rollbackTransaction: Database not open");
|
---|
1479 | return false;
|
---|
1480 | }
|
---|
1481 | if (mysql_query(d->mysql, "ROLLBACK")) {
|
---|
1482 | setLastError(qMakeError(tr("Unable to rollback transaction"),
|
---|
1483 | QSqlError::StatementError, d));
|
---|
1484 | return false;
|
---|
1485 | }
|
---|
1486 | return true;
|
---|
1487 | }
|
---|
1488 |
|
---|
1489 | QString QMYSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
|
---|
1490 | {
|
---|
1491 | QString r;
|
---|
1492 | if (field.isNull()) {
|
---|
1493 | r = QLatin1String("NULL");
|
---|
1494 | } else {
|
---|
1495 | switch(field.type()) {
|
---|
1496 | case QVariant::String:
|
---|
1497 | // Escape '\' characters
|
---|
1498 | r = QSqlDriver::formatValue(field, trimStrings);
|
---|
1499 | r.replace(QLatin1String("\\"), QLatin1String("\\\\"));
|
---|
1500 | break;
|
---|
1501 | case QVariant::ByteArray:
|
---|
1502 | if (isOpen()) {
|
---|
1503 | const QByteArray ba = field.value().toByteArray();
|
---|
1504 | // buffer has to be at least length*2+1 bytes
|
---|
1505 | char* buffer = new char[ba.size() * 2 + 1];
|
---|
1506 | int escapedSize = int(mysql_real_escape_string(d->mysql, buffer,
|
---|
1507 | ba.data(), ba.size()));
|
---|
1508 | r.reserve(escapedSize + 3);
|
---|
1509 | r.append(QLatin1Char('\'')).append(toUnicode(d->tc, buffer)).append(QLatin1Char('\''));
|
---|
1510 | delete[] buffer;
|
---|
1511 | break;
|
---|
1512 | } else {
|
---|
1513 | qWarning("QMYSQLDriver::formatValue: Database not open");
|
---|
1514 | }
|
---|
1515 | // fall through
|
---|
1516 | default:
|
---|
1517 | r = QSqlDriver::formatValue(field, trimStrings);
|
---|
1518 | }
|
---|
1519 | }
|
---|
1520 | return r;
|
---|
1521 | }
|
---|
1522 |
|
---|
1523 | QString QMYSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
|
---|
1524 | {
|
---|
1525 | QString res = identifier;
|
---|
1526 | if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('`')) && !identifier.endsWith(QLatin1Char('`')) ) {
|
---|
1527 | res.prepend(QLatin1Char('`')).append(QLatin1Char('`'));
|
---|
1528 | res.replace(QLatin1Char('.'), QLatin1String("`.`"));
|
---|
1529 | }
|
---|
1530 | return res;
|
---|
1531 | }
|
---|
1532 |
|
---|
1533 | bool QMYSQLDriver::isIdentifierEscapedImplementation(const QString &identifier, IdentifierType type) const
|
---|
1534 | {
|
---|
1535 | Q_UNUSED(type);
|
---|
1536 | return identifier.size() > 2
|
---|
1537 | && identifier.startsWith(QLatin1Char('`')) //left delimited
|
---|
1538 | && identifier.endsWith(QLatin1Char('`')); //right delimited
|
---|
1539 | }
|
---|
1540 |
|
---|
1541 | QT_END_NAMESPACE
|
---|
1542 |
|
---|
1543 | #include "qsql_mysql.moc"
|
---|