1/****************************************************************************
2**
3** Copyright (C) 2022 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QtSql/QtSql>
31
32#include <numeric>
33
34#include "../qsqldatabase/tst_databases.h"
35
36const QString qtest(qTableName(prefix: "qtest", __FILE__, db: QSqlDatabase()));
37
38class tst_QSqlQuery : public QObject
39{
40 Q_OBJECT
41
42public:
43 tst_QSqlQuery();
44 virtual ~tst_QSqlQuery();
45
46public slots:
47 void initTestCase();
48 void cleanupTestCase();
49 void init();
50 void cleanup();
51
52private slots:
53 void value_data() { generic_data(); }
54 void value();
55 void isValid_data() { generic_data(); }
56 void isValid();
57 void isActive_data() { generic_data(); }
58 void isActive();
59 void isSelect_data() { generic_data(); }
60 void isSelect();
61 void numRowsAffected_data() { generic_data(); }
62 void numRowsAffected();
63 void size_data() { generic_data(); }
64 void size();
65 void isNull_data() { generic_data(); }
66 void isNull();
67 void query_exec_data() { generic_data(); }
68 void query_exec();
69 void execErrorRecovery_data() { generic_data(); }
70 void execErrorRecovery();
71 void prematureExec_data() { generic_data(); }
72 void prematureExec();
73 void first_data() { generic_data(); }
74 void first();
75 void next_data() { generic_data(); }
76 void next();
77 void prev_data() { generic_data(); }
78 void prev();
79 void last_data() { generic_data(); }
80 void last();
81 void seek_data() { generic_data(); }
82 void seek();
83 void transaction_data() { generic_data(); }
84 void transaction();
85 void record_data() { generic_data(); }
86 void record();
87 void record_sqlite_data() { generic_data(engine: "QSQLITE"); }
88 void record_sqlite();
89 void finish_data() { generic_data(); }
90 void finish();
91 void sqlite_finish_data() { generic_data(engine: "QSQLITE"); }
92 void sqlite_finish();
93 void nextResult_data() { generic_data(); }
94 void nextResult();
95
96 // forwardOnly mode need special treatment
97 void forwardOnly_data() { generic_data(); }
98 void forwardOnly();
99 void forwardOnlyMultipleResultSet_data() { generic_data(); }
100 void forwardOnlyMultipleResultSet();
101 void psql_forwardOnlyQueryResultsLost_data() { generic_data(engine: "QPSQL"); }
102 void psql_forwardOnlyQueryResultsLost();
103
104 // bug specific tests
105 void tds_bitField_data() { generic_data(engine: "QTDS"); }
106 void tds_bitField();
107 void oci_nullBlob_data() { generic_data(engine: "QOCI"); }
108 void oci_nullBlob();
109 void blob_data() { generic_data(); }
110 void blob();
111 void oci_rawField_data() { generic_data(engine: "QOCI"); }
112 void oci_rawField();
113 void precision_data() { generic_data(); }
114 void precision();
115 void nullResult_data() { generic_data(); }
116 void nullResult();
117 void joins_data() { generic_data(); }
118 void joins();
119 void outValues_data() { generic_data(); }
120 void outValues();
121 void char1Select_data() { generic_data(); }
122 void char1Select();
123 void char1SelectUnicode_data() { generic_data(); }
124 void char1SelectUnicode();
125 void synonyms_data() { generic_data(); }
126 void synonyms();
127 void oraOutValues_data() { generic_data(engine: "QOCI"); }
128 void oraOutValues();
129 void mysql_outValues_data() { generic_data(engine: "QMYSQL"); }
130 void mysql_outValues();
131 void oraClob_data() { generic_data(engine: "QOCI"); }
132 void oraClob();
133 void oraClobBatch_data() { generic_data(engine: "QOCI"); }
134 void oraClobBatch();
135 void oraLong_data() { generic_data(engine: "QOCI"); }
136 void oraLong();
137 void oraOCINumber_data() { generic_data(engine: "QOCI"); }
138 void oraOCINumber();
139 void outValuesDB2_data() { generic_data(engine: "QDB2"); }
140 void outValuesDB2();
141 void storedProceduresIBase_data() {generic_data(engine: "QIBASE"); }
142 void storedProceduresIBase();
143 void oraRowId_data() { generic_data(engine: "QOCI"); }
144 void oraRowId();
145 void prepare_bind_exec_data() { generic_data(); }
146 void prepare_bind_exec();
147 void prepared_select_data() { generic_data(); }
148 void prepared_select();
149 void sqlServerLongStrings_data() { generic_data(); }
150 void sqlServerLongStrings();
151 void invalidQuery_data() { generic_data(); }
152 void invalidQuery();
153 void batchExec_data() { generic_data(); }
154 void batchExec();
155 void QTBUG_43874_data() { generic_data(); }
156 void QTBUG_43874();
157 void oraArrayBind_data() { generic_data(engine: "QOCI"); }
158 void oraArrayBind();
159 void lastInsertId_data() { generic_data(); }
160 void lastInsertId();
161 void lastQuery_data() { generic_data(); }
162 void lastQuery();
163 void lastQueryTwoQueries_data() { generic_data(); }
164 void lastQueryTwoQueries();
165 void bindBool_data() { generic_data(); }
166 void bindBool();
167 void psql_bindWithDoubleColonCastOperator_data() { generic_data(engine: "QPSQL"); }
168 void psql_bindWithDoubleColonCastOperator();
169 void psql_specialFloatValues_data() { generic_data(engine: "QPSQL"); }
170 void psql_specialFloatValues();
171 void queryOnInvalidDatabase_data() { generic_data(); }
172 void queryOnInvalidDatabase();
173 void createQueryOnClosedDatabase_data() { generic_data(); }
174 void createQueryOnClosedDatabase();
175 void seekForwardOnlyQuery_data() { generic_data(); }
176 void seekForwardOnlyQuery();
177 void reExecutePreparedForwardOnlyQuery_data() { generic_data(); }
178 void reExecutePreparedForwardOnlyQuery();
179 void blobsPreparedQuery_data() { generic_data(); }
180 void blobsPreparedQuery();
181 void emptyTableNavigate_data() { generic_data(); }
182 void emptyTableNavigate();
183 void timeStampParsing_data() { generic_data(); }
184 void timeStampParsing();
185 void sqliteVirtualTable_data() { generic_data(engine: "QSQLITE"); }
186 void sqliteVirtualTable();
187 void mysql_timeType_data() { generic_data(engine: "QMYSQL"); }
188 void mysql_timeType();
189 void ibase_executeBlock_data() { generic_data(engine: "QIBASE"); }
190 void ibase_executeBlock();
191
192 void task_217003_data() { generic_data(); }
193 void task_217003();
194
195 void task_250026_data() { generic_data(engine: "QODBC"); }
196 void task_250026();
197 void crashQueryOnCloseDatabase();
198
199 void task_233829_data() { generic_data(engine: "QPSQL"); }
200 void task_233829();
201
202 void QTBUG_12477_data() { generic_data(engine: "QPSQL"); }
203 void QTBUG_12477();
204
205 void sqlServerReturn0_data() { generic_data(); }
206 void sqlServerReturn0();
207
208 void QTBUG_551_data() { generic_data(engine: "QOCI"); }
209 void QTBUG_551();
210
211 void QTBUG_5251_data() { generic_data(engine: "QPSQL"); }
212 void QTBUG_5251();
213 void QTBUG_6421_data() { generic_data(engine: "QOCI"); }
214 void QTBUG_6421();
215 void QTBUG_6618_data() { generic_data(engine: "QODBC"); }
216 void QTBUG_6618();
217 void QTBUG_6852_data() { generic_data(engine: "QMYSQL"); }
218 void QTBUG_6852();
219 void QTBUG_5765_data() { generic_data(engine: "QMYSQL"); }
220 void QTBUG_5765();
221 void QTBUG_12186_data() { generic_data(engine: "QSQLITE"); }
222 void QTBUG_12186();
223 void QTBUG_14132_data() { generic_data(engine: "QOCI"); }
224 void QTBUG_14132();
225 void QTBUG_18435_data() { generic_data(engine: "QODBC"); }
226 void QTBUG_18435();
227 void QTBUG_21884_data() { generic_data(engine: "QSQLITE"); }
228 void QTBUG_21884();
229 void QTBUG_16967_data() { generic_data(engine: "QSQLITE"); }
230 void QTBUG_16967(); //clean close
231 void QTBUG_23895_data() { generic_data(engine: "QSQLITE"); }
232 void QTBUG_23895(); //sqlite boolean type
233 void QTBUG_14904_data() { generic_data(engine: "QSQLITE"); }
234 void QTBUG_14904();
235
236 void QTBUG_2192_data() { generic_data(); }
237 void QTBUG_2192();
238
239 void QTBUG_36211_data() { generic_data(engine: "QPSQL"); }
240 void QTBUG_36211();
241
242 void QTBUG_53969_data() { generic_data(engine: "QMYSQL"); }
243 void QTBUG_53969();
244
245 void gisPointDatatype_data() { generic_data(engine: "QMYSQL"); }
246 void gisPointDatatype();
247
248 void sqlite_constraint_data() { generic_data(engine: "QSQLITE"); }
249 void sqlite_constraint();
250
251 void sqlite_real_data() { generic_data(engine: "QSQLITE"); }
252 void sqlite_real();
253
254 void aggregateFunctionTypes_data() { generic_data(); }
255 void aggregateFunctionTypes();
256
257 void integralTypesMysql_data() { generic_data(engine: "QMYSQL"); }
258 void integralTypesMysql();
259
260 void QTBUG_57138_data() { generic_data(engine: "QSQLITE"); }
261 void QTBUG_57138();
262
263 void QTBUG_73286_data() { generic_data(engine: "QODBC"); }
264 void QTBUG_73286();
265
266 void dateTime_data();
267 void dateTime();
268
269 void ibaseArray_data() { generic_data(engine: "QIBASE"); }
270 void ibaseArray();
271
272private:
273 // returns all database connections
274 void generic_data(const QString &engine=QString());
275 void dropTestTables( QSqlDatabase db );
276 void createTestTables( QSqlDatabase db );
277 void populateTestTables( QSqlDatabase db );
278
279 tst_Databases dbs;
280};
281
282tst_QSqlQuery::tst_QSqlQuery()
283{
284}
285
286tst_QSqlQuery::~tst_QSqlQuery()
287{
288}
289
290void tst_QSqlQuery::initTestCase()
291{
292 QVERIFY(dbs.open());
293
294 for ( QStringList::ConstIterator it = dbs.dbNames.begin(); it != dbs.dbNames.end(); ++it ) {
295 QSqlDatabase db = QSqlDatabase::database(connectionName: ( *it ) );
296 CHECK_DATABASE( db );
297 dropTestTables( db ); //in case of leftovers
298 createTestTables( db );
299 populateTestTables( db );
300 }
301}
302
303void tst_QSqlQuery::cleanupTestCase()
304{
305 for ( QStringList::ConstIterator it = dbs.dbNames.begin(); it != dbs.dbNames.end(); ++it ) {
306 QSqlDatabase db = QSqlDatabase::database(connectionName: ( *it ) );
307 CHECK_DATABASE( db );
308 dropTestTables( db );
309 }
310
311 dbs.close();
312}
313
314void tst_QSqlQuery::init()
315{
316}
317
318void tst_QSqlQuery::cleanup()
319{
320 if (QTest::currentTestFunction() == QLatin1String("crashQueryOnCloseDatabase"))
321 return;
322 QFETCH( QString, dbName );
323 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
324 CHECK_DATABASE( db );
325 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
326 if ( QTest::currentTestFunction() == QLatin1String( "numRowsAffected" )
327 || QTest::currentTestFunction() == QLatin1String( "transactions" )
328 || QTest::currentTestFunction() == QLatin1String( "size" )
329 || QTest::currentTestFunction() == QLatin1String( "isActive" )
330 || QTest::currentTestFunction() == QLatin1String( "lastInsertId" ) ) {
331 populateTestTables( db );
332 }
333
334 if (QTest::currentTestFailed() && (dbType == QSqlDriver::Oracle || db.driverName().startsWith(s: "QODBC"))) {
335 //since Oracle ODBC totally craps out on error, we init again
336 db.close();
337 db.open();
338 }
339}
340
341void tst_QSqlQuery::generic_data(const QString& engine)
342{
343 if ( dbs.fillTestTable(driverPrefix: engine) == 0 ) {
344 if(engine.isEmpty())
345 QSKIP( "No database drivers are available in this Qt configuration");
346 else
347 QSKIP( (QString("No database drivers of type %1 are available in this Qt configuration").arg(engine)).toLocal8Bit());
348 }
349}
350
351void tst_QSqlQuery::dropTestTables( QSqlDatabase db )
352{
353 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
354 QStringList tablenames;
355 // drop all the table in case a testcase failed
356 tablenames << qtest
357 << qTableName(prefix: "qtest_null", __FILE__, db)
358 << qTableName(prefix: "qtest_blob", __FILE__, db)
359 << qTableName(prefix: "qtest_bittest", __FILE__, db)
360 << qTableName(prefix: "qtest_nullblob", __FILE__, db)
361 << qTableName(prefix: "qtest_rawtest", __FILE__, db)
362 << qTableName(prefix: "qtest_precision", __FILE__, db)
363 << qTableName(prefix: "qtest_prepare", __FILE__, db)
364 << qTableName(prefix: "qtestj1", __FILE__, db)
365 << qTableName(prefix: "qtestj2", __FILE__, db)
366 << qTableName(prefix: "char1Select", __FILE__, db)
367 << qTableName(prefix: "char1SU", __FILE__, db)
368 << qTableName(prefix: "qxmltest", __FILE__, db)
369 << qTableName(prefix: "qtest_exerr", __FILE__, db)
370 << qTableName(prefix: "qtest_empty", __FILE__, db)
371 << qTableName(prefix: "clobby", __FILE__, db)
372 << qTableName(prefix: "bindtest", __FILE__, db)
373 << qTableName(prefix: "more_results", __FILE__, db)
374 << qTableName(prefix: "blobstest", __FILE__, db)
375 << qTableName(prefix: "oraRowId", __FILE__, db)
376 << qTableName(prefix: "bug43874", __FILE__, db)
377 << qTableName(prefix: "bug6421", __FILE__, db).toUpper()
378 << qTableName(prefix: "bug5765", __FILE__, db)
379 << qTableName(prefix: "bug6852", __FILE__, db)
380 << qTableName(prefix: "bug21884", __FILE__, db)
381 << qTableName(prefix: "bug23895", __FILE__, db)
382 << qTableName(prefix: "qtest_lockedtable", __FILE__, db)
383 << qTableName(prefix: "Planet", __FILE__, db)
384 << qTableName(prefix: "task_250026", __FILE__, db)
385 << qTableName(prefix: "task_234422", __FILE__, db)
386 << qTableName(prefix: "test141895", __FILE__, db)
387 << qTableName(prefix: "qtest_oraOCINumber", __FILE__, db)
388 << qTableName(prefix: "bug2192", __FILE__, db)
389 << qTableName(prefix: "tst_record", __FILE__, db);
390
391 if (dbType == QSqlDriver::PostgreSQL)
392 tablenames << qTableName(prefix: "task_233829", __FILE__, db);
393
394 if (dbType == QSqlDriver::SQLite)
395 tablenames << qTableName(prefix: "record_sqlite", __FILE__, db);
396
397 if (dbType == QSqlDriver::MSSqlServer || dbType == QSqlDriver::Oracle)
398 tablenames << qTableName(prefix: "qtest_longstr", __FILE__, db);
399
400 if (dbType == QSqlDriver::MSSqlServer)
401 db.exec(query: "DROP PROCEDURE " + qTableName(prefix: "test141895_proc", __FILE__, db));
402
403 if (dbType == QSqlDriver::MySqlServer)
404 db.exec(query: "DROP PROCEDURE IF EXISTS "+ qTableName(prefix: "bug6852_proc", __FILE__, db));
405
406 tst_Databases::safeDropTables( db, tableNames: tablenames );
407
408 if (dbType == QSqlDriver::Oracle) {
409 QSqlQuery q( db );
410 q.exec(query: "DROP PACKAGE " + qTableName(prefix: "pkg", __FILE__, db));
411 }
412}
413
414void tst_QSqlQuery::createTestTables( QSqlDatabase db )
415{
416 QSqlQuery q( db );
417 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
418 if (dbType == QSqlDriver::MySqlServer)
419 // ### stupid workaround until we find a way to hardcode this
420 // in the MySQL server startup script
421 q.exec( query: "set table_type=innodb" );
422 else if (dbType == QSqlDriver::PostgreSQL)
423 QVERIFY_SQL( q, exec("set client_min_messages='warning'"));
424
425 if (dbType == QSqlDriver::PostgreSQL)
426 QVERIFY_SQL( q, exec( "create table " + qtest + " (id serial NOT NULL, t_varchar varchar(20), t_char char(20), primary key(id)) WITH OIDS" ) );
427 else
428 QVERIFY_SQL( q, exec( "create table " + qtest + " (id int "+tst_Databases::autoFieldName(db) +" NOT NULL, t_varchar varchar(20), t_char char(20), primary key(id))" ) );
429
430 if (dbType == QSqlDriver::MSSqlServer || dbType == QSqlDriver::Sybase)
431 QVERIFY_SQL(q, exec("create table " + qTableName("qtest_null", __FILE__, db) + " (id int null, t_varchar varchar(20) null)"));
432 else
433 QVERIFY_SQL(q, exec("create table " + qTableName("qtest_null", __FILE__, db) + " (id int, t_varchar varchar(20))"));
434}
435
436void tst_QSqlQuery::populateTestTables( QSqlDatabase db )
437{
438 QSqlQuery q( db );
439 const QString qtest_null(qTableName( prefix: "qtest_null", __FILE__, db));
440 q.exec( query: "delete from " + qtest );
441 QVERIFY_SQL( q, exec( "insert into " + qtest + " values (1, 'VarChar1', 'Char1')" ) );
442 QVERIFY_SQL( q, exec( "insert into " + qtest + " values (2, 'VarChar2', 'Char2')" ) );
443 QVERIFY_SQL( q, exec( "insert into " + qtest + " values (3, 'VarChar3', 'Char3')" ) );
444 QVERIFY_SQL( q, exec( "insert into " + qtest + " values (4, 'VarChar4', 'Char4')" ) );
445 QVERIFY_SQL( q, exec( "insert into " + qtest + " values (5, 'VarChar5', 'Char5')" ) );
446
447 q.exec( query: "delete from " + qtest_null );
448 QVERIFY_SQL( q, exec( "insert into " + qtest_null + " values (0, NULL)" ) );
449 QVERIFY_SQL( q, exec( "insert into " + qtest_null + " values (1, 'n')" ) );
450 QVERIFY_SQL( q, exec( "insert into " + qtest_null + " values (2, 'i')" ) );
451 QVERIFY_SQL( q, exec( "insert into " + qtest_null + " values (3, NULL)" ) );
452}
453
454// There were problems with char fields of size 1
455void tst_QSqlQuery::char1Select()
456{
457 QFETCH( QString, dbName );
458 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
459 CHECK_DATABASE( db );
460
461 {
462 QSqlQuery q( db );
463 const QString tbl = qTableName(prefix: "char1Select", __FILE__, db);
464 q.exec( query: "drop table " + tbl);
465 QVERIFY_SQL(q, exec("create table " + tbl + " (id char(1))"));
466 QVERIFY_SQL(q, exec("insert into " + tbl + " values ('a')"));
467 QVERIFY_SQL(q, exec("select * from " + tbl));
468 QVERIFY( q.next() );
469 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
470 if (dbType == QSqlDriver::Interbase)
471 QCOMPARE( q.value( 0 ).toString().left( 1 ), QString( "a" ) );
472 else
473 QCOMPARE( q.value( 0 ).toString(), QString( "a" ) );
474
475 QVERIFY( !q.next() );
476 }
477}
478
479void tst_QSqlQuery::char1SelectUnicode()
480{
481 QFETCH( QString, dbName );
482 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
483 CHECK_DATABASE( db );
484 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
485 if (dbType == QSqlDriver::DB2)
486 QSKIP("Needs someone with more Unicode knowledge than I have to fix");
487
488 if ( db.driver()->hasFeature( f: QSqlDriver::Unicode ) ) {
489 QString uniStr( QChar(0x0915) ); // DEVANAGARI LETTER KA
490 QSqlQuery q( db );
491 QString createQuery;
492 const QString char1SelectUnicode(qTableName(prefix: "char1SU", __FILE__, db));
493
494 if (dbType == QSqlDriver::MSSqlServer)
495 createQuery = "create table " + char1SelectUnicode + "(id nchar(1))";
496 else if (dbType == QSqlDriver::DB2 || dbType == QSqlDriver::Oracle || dbType == QSqlDriver::PostgreSQL)
497 createQuery = "create table " + char1SelectUnicode + " (id char(3))";
498 else if (dbType == QSqlDriver::Interbase)
499 createQuery = "create table " + char1SelectUnicode +
500 " (id char(1) character set unicode_fss)";
501 else if (dbType == QSqlDriver::MySqlServer)
502 createQuery = "create table " + char1SelectUnicode + " (id char(1)) "
503 "default character set 'utf8'";
504 else
505 createQuery = "create table " + char1SelectUnicode + " (id char(1))";
506
507 QVERIFY_SQL( q, exec( createQuery ) );
508
509 QVERIFY_SQL( q, prepare( "insert into " + char1SelectUnicode + " values(?)" ) );
510
511 q.bindValue( pos: 0, val: uniStr );
512
513 QVERIFY_SQL( q, exec() );
514
515 QVERIFY_SQL( q, exec( "select * from " + char1SelectUnicode ) );
516
517 QVERIFY( q.next() );
518
519 if ( !q.value( i: 0 ).toString().isEmpty() )
520 QCOMPARE( q.value( 0 ).toString()[ 0 ].unicode(), uniStr[0].unicode() );
521
522 QCOMPARE( q.value( 0 ).toString().trimmed(), uniStr );
523
524 QVERIFY( !q.next() );
525 }
526 else
527 QSKIP( "Database not unicode capable");
528}
529
530void tst_QSqlQuery::oraRowId()
531{
532 QFETCH( QString, dbName );
533 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
534 CHECK_DATABASE( db );
535 const QString oraRowId(qTableName(prefix: "oraRowId", __FILE__, db));
536
537 QSqlQuery q( db );
538 QVERIFY_SQL( q, exec( "select rowid from " + qtest ) );
539 QVERIFY( q.next() );
540 QCOMPARE( q.value( 0 ).type(), QVariant::String );
541 QVERIFY( !q.value( 0 ).toString().isEmpty() );
542
543 QVERIFY_SQL( q, exec( "create table " + oraRowId + " (id char(1))" ) );
544
545 QVERIFY_SQL( q, exec( "insert into " + oraRowId + " values('a')" ) );
546 QVariant v1 = q.lastInsertId();
547 QVERIFY( v1.isValid() );
548
549 QVERIFY_SQL( q, exec( "insert into " + oraRowId + " values('b')" ) );
550 QVariant v2 = q.lastInsertId();
551 QVERIFY( v2.isValid() );
552
553 QVERIFY_SQL( q, prepare( "select * from " + oraRowId + " where rowid = ?" ) );
554 q.addBindValue( val: v1 );
555 QVERIFY_SQL( q, exec() );
556 QVERIFY( q.next() );
557 QCOMPARE( q.value( 0 ).toString(), QString( "a" ) );
558
559 q.addBindValue( val: v2 );
560 QVERIFY_SQL( q, exec() );
561 QVERIFY( q.next() );
562 QCOMPARE( q.value( 0 ).toString(), QString( "b" ) );
563}
564
565void tst_QSqlQuery::mysql_outValues()
566{
567 QFETCH( QString, dbName );
568 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
569 CHECK_DATABASE( db );
570 const QString hello(qTableName(prefix: "hello", __FILE__, db)), qtestproc(qTableName(prefix: "qtestproc", __FILE__, db));
571
572 QSqlQuery q( db );
573
574 q.exec( query: "drop function " + hello );
575
576 QVERIFY_SQL(q, exec("create function " + hello + " (s char(20)) returns varchar(50) READS SQL DATA return concat('Hello ', s)"));
577
578 QVERIFY_SQL( q, exec( "select " + hello + "('world')" ) );
579 QVERIFY_SQL( q, next() );
580
581 QCOMPARE( q.value( 0 ).toString(), QString( "Hello world" ) );
582
583 QVERIFY_SQL( q, prepare( "select " + hello + "('harald')" ) );
584 QVERIFY_SQL( q, exec() );
585 QVERIFY_SQL( q, next() );
586
587 QCOMPARE( q.value( 0 ).toString(), QString( "Hello harald" ) );
588
589 QVERIFY_SQL( q, exec( "drop function " + hello ) );
590
591 q.exec( query: "drop procedure " + qtestproc );
592
593 QVERIFY_SQL( q, exec( "create procedure " + qtestproc + " () "
594 "BEGIN select * from " + qtest + " order by id; END" ) );
595 QVERIFY_SQL( q, exec( "call " + qtestproc + "()" ) );
596 QVERIFY_SQL( q, next() );
597 QCOMPARE( q.value( 1 ).toString(), QString( "VarChar1" ) );
598
599 QVERIFY_SQL( q, exec( "drop procedure " + qtestproc ) );
600
601 QVERIFY_SQL( q, exec( "create procedure " + qtestproc + " (OUT param1 INT) "
602 "BEGIN set param1 = 42; END" ) );
603
604 QVERIFY_SQL( q, exec( "call " + qtestproc + " (@out)" ) );
605 QVERIFY_SQL( q, exec( "select @out" ) );
606 QCOMPARE( q.record().fieldName( 0 ), QString( "@out" ) );
607 QVERIFY_SQL( q, next() );
608 QCOMPARE( q.value( 0 ).toInt(), 42 );
609
610 QVERIFY_SQL( q, exec( "drop procedure " + qtestproc ) );
611}
612
613void tst_QSqlQuery::bindBool()
614{
615 // QTBUG-27763: bool value got converted to int 127 by mysql driver because sizeof(bool) < sizeof(int).
616 // The problem was the way the bool value from the application was handled. For our purposes here, it
617 // doesn't matter whether the column type is BOOLEAN or INT. All DBMSs have INT, and this usually
618 // works for this test. Postresql is an exception because its INT type does not accept BOOLEAN
619 // values and its BOOLEAN columns do not accept INT values.
620 QFETCH( QString, dbName );
621 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
622 CHECK_DATABASE( db );
623 QSqlQuery q(db);
624 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
625 const QString tableName(qTableName(prefix: "bindBool", __FILE__, db));
626
627 q.exec(query: "DROP TABLE " + tableName);
628 QString colType = dbType == QSqlDriver::PostgreSQL ? QLatin1String("BOOLEAN") : QLatin1String("INT");
629 QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id INT, flag " + colType + " NOT NULL, PRIMARY KEY(id))"));
630
631 for (int i = 0; i < 2; ++i) {
632 bool flag = i;
633 q.prepare(query: "INSERT INTO " + tableName + " (id, flag) VALUES(:id, :flag)");
634 q.bindValue(placeholder: ":id", val: i);
635 q.bindValue(placeholder: ":flag", val: flag);
636 QVERIFY_SQL(q, exec());
637 }
638
639 QVERIFY_SQL(q, exec("SELECT id, flag FROM " + tableName + " ORDER BY id"));
640 for (int i = 0; i < 2; ++i) {
641 bool flag = i;
642 QVERIFY_SQL(q, next());
643 QCOMPARE(q.value(0).toInt(), i);
644 QCOMPARE(q.value(1).toBool(), flag);
645 }
646 QVERIFY_SQL(q, prepare("SELECT flag FROM " + tableName + " WHERE flag = :filter"));
647 const bool filter = true;
648 q.bindValue(placeholder: ":filter", val: filter);
649 QVERIFY_SQL(q, exec());
650 QVERIFY_SQL(q, next());
651 QCOMPARE(q.value(0).toBool(), filter);
652 QFAIL_SQL(q, next());
653 QVERIFY_SQL(q, exec("DROP TABLE " + tableName));
654}
655
656void tst_QSqlQuery::oraOutValues()
657{
658 QFETCH( QString, dbName );
659 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
660 CHECK_DATABASE( db );
661 const QString tst_outValues(qTableName(prefix: "tst_outValues", __FILE__, db));
662
663 if ( !db.driver()->hasFeature( f: QSqlDriver::PreparedQueries ) )
664 QSKIP( "Test requires prepared query support");
665
666 QSqlQuery q( db );
667
668 q.setForwardOnly( true );
669
670 /*** outvalue int ***/
671 QVERIFY_SQL( q, exec( "create or replace procedure " + tst_outValues + "(x out int) is\n"
672 "begin\n"
673 " x := 42;\n"
674 "end;\n" ) );
675 QVERIFY( q.prepare( "call " + tst_outValues + "(?)" ) );
676 q.addBindValue( val: 0, type: QSql::Out );
677 QVERIFY_SQL( q, exec() );
678 QCOMPARE( q.boundValue( 0 ).toInt(), 42 );
679
680 // bind a null value, make sure the OCI driver resets the null flag
681 q.addBindValue( val: QVariant( QVariant::Int ), type: QSql::Out );
682 QVERIFY_SQL( q, exec() );
683 QCOMPARE( q.boundValue( 0 ).toInt(), 42 );
684 QVERIFY( !q.boundValue( 0 ).isNull() );
685
686 /*** outvalue varchar ***/
687 QVERIFY_SQL( q, exec( "create or replace procedure " + tst_outValues + "(x out varchar) is\n"
688 "begin\n"
689 " x := 'blah';\n"
690 "end;\n" ) );
691 QVERIFY( q.prepare( "call " + tst_outValues + "(?)" ) );
692 QString s1( "12345" );
693 s1.reserve( asize: 512 );
694 q.addBindValue( val: s1, type: QSql::Out );
695 QVERIFY_SQL( q, exec() );
696 QCOMPARE( q.boundValue( 0 ).toString(), QString( "blah" ) );
697
698 /*** in/outvalue numeric ***/
699 QVERIFY_SQL( q, exec( "create or replace procedure " + tst_outValues + "(x in out numeric) is\n"
700 "begin\n"
701 " x := x + 10;\n"
702 "end;\n" ) );
703 QVERIFY( q.prepare( "call " + tst_outValues + "(?)" ) );
704 q.addBindValue( val: 10, type: QSql::Out );
705 QVERIFY_SQL( q, exec() );
706 QCOMPARE( q.boundValue( 0 ).toInt(), 20 );
707
708 /*** in/outvalue varchar ***/
709 QVERIFY_SQL( q, exec( "create or replace procedure " + tst_outValues + "(x in out varchar) is\n"
710 "begin\n"
711 " x := 'homer';\n"
712 "end;\n" ) );
713 QVERIFY( q.prepare( "call " + tst_outValues + "(?)" ) );
714 q.addBindValue( val: QString( "maggy" ), type: QSql::Out );
715 QVERIFY_SQL( q, exec() );
716 QCOMPARE( q.boundValue( 0 ).toString(), QString( "homer" ) );
717
718 /*** in/outvalue varchar ***/
719 QVERIFY_SQL( q, exec( "create or replace procedure " + tst_outValues + "(x in out varchar) is\n"
720 "begin\n"
721 " x := NULL;\n"
722 "end;\n" ) );
723 QVERIFY( q.prepare( "call " + tst_outValues + "(?)" ) );
724 q.addBindValue( val: QString( "maggy" ), type: QSql::Out );
725 QVERIFY_SQL( q, exec() );
726 QVERIFY( q.boundValue( 0 ).isNull() );
727
728 /*** in/outvalue int ***/
729 QVERIFY_SQL( q, exec( "create or replace procedure " + tst_outValues + "(x in out int) is\n"
730 "begin\n"
731 " x := NULL;\n"
732 "end;\n" ) );
733 QVERIFY( q.prepare( "call " + tst_outValues + "(?)" ) );
734 q.addBindValue( val: 42, type: QSql::Out );
735 QVERIFY_SQL( q, exec() );
736 QVERIFY( q.boundValue( 0 ).isNull() );
737
738 /*** in/outvalue varchar ***/
739 QVERIFY_SQL( q, exec( "create or replace procedure " + tst_outValues + "(x in varchar, y out varchar) is\n"
740 "begin\n"
741 " y := x||'bubulalakikikokololo';\n"
742 "end;\n" ) );
743 QVERIFY( q.prepare( "call " + tst_outValues + "(?, ?)" ) );
744 q.addBindValue( val: QString( "fifi" ), type: QSql::In );
745 QString out;
746 out.reserve( asize: 50 );
747 q.addBindValue( val: out, type: QSql::Out );
748 QVERIFY_SQL( q, exec() );
749 QCOMPARE( q.boundValue( 1 ).toString(), QString( "fifibubulalakikikokololo" ) );
750
751 /*** in/outvalue date ***/
752 QVERIFY_SQL(q, exec("create or replace procedure " + tst_outValues + "(x in date, y out date) is\n"
753 "begin\n"
754 " y := x;\n"
755 "end;\n"));
756 QVERIFY(q.prepare("call " + tst_outValues + "(?, ?)"));
757 const QDate date = QDate::currentDate();
758 q.addBindValue(val: date, type: QSql::In);
759 q.addBindValue(val: QVariant(QDate()), type: QSql::Out);
760 QVERIFY_SQL(q, exec());
761 QCOMPARE(q.boundValue(1).toDate(), date);
762
763 /*** in/outvalue timestamp ***/
764 QVERIFY_SQL(q, exec("create or replace procedure " + tst_outValues + "(x in timestamp, y out timestamp) is\n"
765 "begin\n"
766 " y := x;\n"
767 "end;\n"));
768 QVERIFY(q.prepare("call " + tst_outValues + "(?, ?)"));
769 const QDateTime dt = QDateTime::currentDateTime();
770 q.addBindValue(val: dt, type: QSql::In);
771 q.addBindValue(val: QVariant(QVariant::DateTime), type: QSql::Out);
772 QVERIFY_SQL(q, exec());
773 QCOMPARE(q.boundValue(1).toDateTime(), dt);
774}
775
776void tst_QSqlQuery::oraClob()
777{
778 QFETCH( QString, dbName );
779 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
780 CHECK_DATABASE( db );
781 const QString clobby(qTableName(prefix: "clobby", __FILE__, db));
782
783 QSqlQuery q( db );
784
785 // simple short string
786 QVERIFY_SQL( q, exec( "create table " + clobby + "(id int primary key, cl clob, bl blob)" ) );
787 QVERIFY_SQL( q, prepare( "insert into " + clobby + " (id, cl, bl) values(?, ?, ?)" ) );
788 q.addBindValue( val: 1 );
789 q.addBindValue( val: "bubu" );
790 q.addBindValue( val: QByteArray("bubu") );
791 QVERIFY_SQL( q, exec() );
792
793 QVERIFY_SQL( q, exec( "select bl, cl from " + clobby + " where id = 1" ) );
794 QVERIFY( q.next() );
795 QCOMPARE( q.value( 0 ).toString(), QString( "bubu" ) );
796 QCOMPARE( q.value( 1 ).toString(), QString( "bubu" ) );
797
798 // simple short string with binding
799 QVERIFY_SQL( q, prepare( "insert into " + clobby + " (id, cl, bl) values(?, ?, ?)" ) );
800 q.addBindValue( val: 2 );
801 q.addBindValue( val: "lala", type: QSql::Binary );
802 q.addBindValue( val: QByteArray("lala"), type: QSql::Binary );
803 QVERIFY_SQL( q, exec() );
804
805 QVERIFY_SQL( q, exec( "select bl, cl from " + clobby + " where id = 2" ) );
806 QVERIFY( q.next() );
807 QCOMPARE( q.value( 0 ).toString(), QString( "lala" ) );
808 QCOMPARE( q.value( 1 ).toString(), QString( "lala" ) );
809
810 // loooong string
811 QString loong;
812 loong.fill( c: QLatin1Char( 'A' ), size: 25000 );
813 QVERIFY_SQL( q, prepare( "insert into " + clobby + " (id, cl, bl) values(?, ?, ?)" ) );
814 q.addBindValue( val: 3 );
815 q.addBindValue( val: loong, type: QSql::Binary );
816 q.addBindValue( val: loong.toLatin1(), type: QSql::Binary );
817 QVERIFY_SQL( q, exec() );
818
819 QVERIFY_SQL( q, exec( "select bl, cl from " + clobby + " where id = 3" ) );
820 QVERIFY( q.next() );
821 QCOMPARE( q.value( 0 ).toString().count(), loong.count() );
822 QVERIFY( q.value( 0 ).toString() == loong );
823 QCOMPARE( q.value( 1 ).toByteArray().count(), loong.toLatin1().count() );
824 QVERIFY( q.value( 1 ).toByteArray() == loong.toLatin1() );
825}
826
827void tst_QSqlQuery::oraClobBatch()
828{
829 QFETCH(QString, dbName);
830 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
831 CHECK_DATABASE(db);
832 const QString clobBatch(qTableName(prefix: "clobBatch", __FILE__, db));
833 tst_Databases::safeDropTables(db, tableNames: { clobBatch });
834 QSqlQuery q(db);
835 QVERIFY_SQL(q, exec("create table " + clobBatch + "(cl clob)"));
836
837 const QString longString(USHRT_MAX + 1, QLatin1Char('A'));
838 QVERIFY_SQL(q, prepare("insert into " + clobBatch + " (cl) values(:cl)"));
839 const QVariantList vars = { longString };
840 q.addBindValue(val: vars);
841 QVERIFY_SQL(q, execBatch());
842
843 QVERIFY_SQL(q, exec("select cl from " + clobBatch));
844 QVERIFY(q.next());
845 QCOMPARE(q.value(0).toString().count(), longString.size());
846 QVERIFY(q.value(0).toString() == longString);
847}
848
849void tst_QSqlQuery::storedProceduresIBase()
850{
851 QFETCH( QString, dbName );
852 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
853 CHECK_DATABASE( db );
854
855 QSqlQuery q( db );
856 q.exec(query: "drop procedure " + qTableName(prefix: "TESTPROC", __FILE__, db));
857
858 QVERIFY_SQL(q, exec("create procedure " + qTableName("TESTPROC", __FILE__, db) +
859 " RETURNS (x integer, y varchar(20)) "
860 "AS BEGIN "
861 " x = 42; "
862 " y = 'Hello Anders'; "
863 "END" ) );
864
865 QVERIFY_SQL(q, prepare("execute procedure " + qTableName("TestProc", __FILE__, db)));
866 QVERIFY_SQL( q, exec() );
867
868 // check for a valid result set
869 QSqlRecord rec = q.record();
870 QCOMPARE( rec.count(), 2 );
871 QCOMPARE( rec.fieldName( 0 ).toUpper(), QString( "X" ) );
872 QCOMPARE( rec.fieldName( 1 ).toUpper(), QString( "Y" ) );
873
874 // the first next shall suceed
875 QVERIFY_SQL( q, next() );
876 QCOMPARE( q.value( 0 ).toInt(), 42 );
877 QCOMPARE( q.value( 1 ).toString(), QString( "Hello Anders" ) );
878
879 // the second next shall fail
880 QVERIFY( !q.next() );
881
882 q.exec(query: "drop procedure " + qTableName(prefix: "TestProc", __FILE__, db));
883}
884
885void tst_QSqlQuery::outValuesDB2()
886{
887 QFETCH( QString, dbName );
888 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
889 CHECK_DATABASE( db );
890
891 if ( !db.driver()->hasFeature( f: QSqlDriver::PreparedQueries ) )
892 QSKIP( "Test requires prepared query support");
893
894 QSqlQuery q( db );
895
896 q.setForwardOnly( true );
897
898 q.exec(query: "drop procedure " + qTableName(prefix: "tst_outValues", __FILE__, db)); //non-fatal
899 QVERIFY_SQL( q, exec( "CREATE PROCEDURE " + qTableName("tst_outValues", __FILE__, db) +
900 " (OUT x int, OUT x2 double, OUT x3 char(20))\n"
901 "LANGUAGE SQL\n"
902 "P1: BEGIN\n"
903 " SET x = 42;\n"
904 " SET x2 = 4.2;\n"
905 " SET x3 = 'Homer';\n"
906 "END P1" ) );
907
908 QVERIFY_SQL(q, prepare("call " + qTableName("tst_outValues", __FILE__, db) + "(?, ?, ?)"));
909
910 q.addBindValue( val: 0, type: QSql::Out );
911 q.addBindValue( val: 0.0, type: QSql::Out );
912 q.addBindValue( val: "Simpson", type: QSql::Out );
913
914 QVERIFY_SQL( q, exec() );
915
916 QCOMPARE( q.boundValue( 0 ).toInt(), 42 );
917 QCOMPARE( q.boundValue( 1 ).toDouble(), 4.2 );
918 QCOMPARE( q.boundValue( 2 ).toString().trimmed(), QString( "Homer" ) );
919}
920
921void tst_QSqlQuery::outValues()
922{
923 QFETCH( QString, dbName );
924 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
925 CHECK_DATABASE( db );
926 const QString tst_outValues(qTableName(prefix: "tst_outValues", __FILE__, db));
927
928 if ( !db.driver()->hasFeature( f: QSqlDriver::PreparedQueries ) )
929 QSKIP( "Test requires prepared query support");
930
931 QSqlQuery q( db );
932
933 q.setForwardOnly( true );
934 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
935 if (dbType == QSqlDriver::Oracle) {
936 QVERIFY_SQL( q, exec( "create or replace procedure " + tst_outValues + "(x out int) is\n"
937 "begin\n"
938 " x := 42;\n"
939 "end;\n" ) );
940 QVERIFY( q.prepare( "call " + tst_outValues + "(?)" ) );
941 } else if (dbType == QSqlDriver::DB2) {
942 q.exec( query: "drop procedure " + tst_outValues ); //non-fatal
943 QVERIFY_SQL( q, exec( "CREATE PROCEDURE " + tst_outValues + " (OUT x int)\n"
944 "LANGUAGE SQL\n"
945 "P1: BEGIN\n"
946 " SET x = 42;\n"
947 "END P1" ) );
948 QVERIFY( q.prepare( "call " + tst_outValues + "(?)" ) );
949 } else if (dbType == QSqlDriver::MSSqlServer) {
950 q.exec( query: "drop procedure " + tst_outValues ); //non-fatal
951 QVERIFY_SQL( q, exec( "create procedure " + tst_outValues + " (@x int out) as\n"
952 "begin\n"
953 " set @x = 42\n"
954 "end\n" ) );
955 QVERIFY( q.prepare( "{call " + tst_outValues + "(?)}" ) );
956 } else
957 QSKIP( "Don't know how to create a stored procedure for this database server, please fix this test");
958
959 q.addBindValue( val: 0, type: QSql::Out );
960
961 QVERIFY_SQL( q, exec() );
962
963 QCOMPARE( q.boundValue( 0 ).toInt(), 42 );
964}
965
966void tst_QSqlQuery::blob()
967{
968 static const int BLOBSIZE = 1024 * 10;
969 static const int BLOBCOUNT = 2;
970
971 QFETCH( QString, dbName );
972 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
973 CHECK_DATABASE( db );
974
975 if ( !db.driver()->hasFeature( f: QSqlDriver::BLOB ) )
976 QSKIP( "DBMS not BLOB capable");
977
978 //don' make it too big otherwise sybase and mysql will complain
979 QByteArray ba( BLOBSIZE, 0 );
980
981 for (int i = 0; i < ba.size(); ++i)
982 ba[i] = i % 256;
983
984 QSqlQuery q( db );
985
986 q.setForwardOnly( true );
987
988 QString queryString = QString("create table " + qTableName(prefix: "qtest_blob", __FILE__, db) +
989 " (id int not null primary key, t_blob %1)" ).arg( a: tst_Databases::blobTypeName( db, blobSize: BLOBSIZE ) );
990 QVERIFY_SQL( q, exec( queryString ) );
991
992 QVERIFY_SQL(q, prepare("insert into " + qTableName("qtest_blob", __FILE__, db) + " (id, t_blob) values (?, ?)"));
993
994 for (int i = 0; i < BLOBCOUNT; ++i) {
995 q.addBindValue( val: i );
996 q.addBindValue( val: ba );
997 QVERIFY_SQL( q, exec() );
998 }
999
1000 QVERIFY_SQL(q, exec("select * from " + qTableName("qtest_blob", __FILE__, db)));
1001
1002 for (int i = 0; i < BLOBCOUNT; ++i) {
1003 QVERIFY( q.next() );
1004 QByteArray res = q.value( i: 1 ).toByteArray();
1005 QVERIFY2( res.size() >= ba.size(),
1006 QString( "array sizes differ, expected %1, got %2" ).arg( ba.size() ).arg( res.size() ).toLatin1() );
1007
1008 for (int i2 = 0; i2 < ba.size(); ++i2) {
1009 if ( res[i2] != ba[i2] )
1010 QFAIL( QString( "ByteArrays differ at position %1, expected %2, got %3" ).arg(
1011 i2 ).arg(( int )( unsigned char )ba[i2] ).arg(( int )( unsigned char )res[i2] ).toLatin1() );
1012 }
1013 }
1014}
1015
1016void tst_QSqlQuery::value()
1017{
1018 QFETCH( QString, dbName );
1019 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1020 CHECK_DATABASE( db );
1021 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
1022 QSqlQuery q( db );
1023 QVERIFY_SQL( q, exec( "select id, t_varchar, t_char from " + qtest + " order by id" ) );
1024 int i = 1;
1025
1026 while ( q.next() ) {
1027 QCOMPARE( q.value( 0 ).toInt(), i );
1028 QCOMPARE( q.value( "id" ).toInt(), i );
1029
1030 if (dbType == QSqlDriver::Interbase)
1031 QVERIFY( q.value( 1 ).toString().startsWith( "VarChar" + QString::number( i ) ) );
1032 else if ( q.value( i: 1 ).toString().endsWith(c: QLatin1Char(' ')))
1033 QCOMPARE( q.value( 1 ).toString(), ( "VarChar" + QString::number( i ) + " " ) );
1034 else
1035 QCOMPARE( q.value( 1 ).toString(), ( "VarChar" + QString::number( i ) ) );
1036
1037 if (dbType == QSqlDriver::Interbase)
1038 QVERIFY( q.value( 2 ).toString().startsWith( "Char" + QString::number( i ) ) );
1039 else if (!q.value( i: 2 ).toString().endsWith(c: QLatin1Char(' ')))
1040 QCOMPARE( q.value( 2 ).toString(), ( "Char" + QString::number( i ) ) );
1041 else
1042 QCOMPARE( q.value( 2 ).toString(), ( "Char" + QString::number( i ) + " " ) );
1043
1044 i++;
1045 }
1046}
1047
1048#define SETUP_RECORD_TABLE \
1049 do { \
1050 QVERIFY_SQL(q, exec("CREATE TABLE " + tst_record + " (id integer, extra varchar(50))")); \
1051 for (int i = 0; i < 3; ++i) \
1052 QVERIFY_SQL(q, exec(QString("INSERT INTO " + tst_record + " VALUES(%1, 'extra%1')").arg(i))); \
1053 } while (0)
1054
1055#define CHECK_RECORD \
1056 do { \
1057 QVERIFY_SQL(q, exec(QString("select %1.id, %1.t_varchar, %1.t_char, %2.id, %2.extra from %1, %2 where " \
1058 "%1.id = %2.id order by %1.id").arg(lowerQTest).arg(tst_record))); \
1059 QCOMPARE(q.record().fieldName(0).toLower(), QString("id")); \
1060 QCOMPARE(q.record().field(0).tableName().toLower(), lowerQTest); \
1061 QCOMPARE(q.record().fieldName(1).toLower(), QString("t_varchar")); \
1062 QCOMPARE(q.record().field(1).tableName().toLower(), lowerQTest); \
1063 QCOMPARE(q.record().fieldName(2).toLower(), QString("t_char")); \
1064 QCOMPARE(q.record().field(2).tableName().toLower(), lowerQTest); \
1065 QCOMPARE(q.record().fieldName(3).toLower(), QString("id")); \
1066 QCOMPARE(q.record().field(3).tableName().toLower(), tst_record); \
1067 QCOMPARE(q.record().fieldName(4).toLower(), QString("extra")); \
1068 QCOMPARE(q.record().field(4).tableName().toLower(), tst_record); \
1069 } while (0)
1070
1071void tst_QSqlQuery::record()
1072{
1073 QFETCH( QString, dbName );
1074 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1075 CHECK_DATABASE( db );
1076
1077 QSqlQuery q( db );
1078 QVERIFY( q.record().isEmpty() );
1079 QVERIFY_SQL( q, exec( "select id, t_varchar, t_char from " + qtest + " order by id" ) );
1080 QCOMPARE( q.record().fieldName( 0 ).toLower(), QString( "id" ) );
1081 QCOMPARE( q.record().fieldName( 1 ).toLower(), QString( "t_varchar" ) );
1082 QCOMPARE( q.record().fieldName( 2 ).toLower(), QString( "t_char" ) );
1083 QCOMPARE(q.record().value(0), QVariant(q.record().field(0).type()));
1084 QCOMPARE(q.record().value(1), QVariant(q.record().field(1).type()));
1085 QCOMPARE(q.record().value(2), QVariant(q.record().field(2).type()));
1086
1087 QVERIFY( q.next() );
1088 QVERIFY( q.next() );
1089
1090 QCOMPARE( q.record().fieldName( 0 ).toLower(), QString( "id" ) );
1091 QCOMPARE( q.value( 0 ).toInt(), 2 );
1092
1093 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
1094 if (dbType == QSqlDriver::Oracle)
1095 QSKIP("Getting the tablename is not supported in Oracle");
1096 const auto lowerQTest = qtest.toLower();
1097 for (int i = 0; i < 3; ++i)
1098 QCOMPARE(q.record().field(i).tableName().toLower(), lowerQTest);
1099 q.clear();
1100 const auto tst_record = qTableName(prefix: "tst_record", __FILE__, db, escape: false).toLower();
1101 SETUP_RECORD_TABLE;
1102 CHECK_RECORD;
1103 q.clear();
1104
1105 // Recreate the tables, in a different order
1106 const QStringList tables = { qtest, tst_record, qTableName(prefix: "qtest_null", __FILE__, db) };
1107 tst_Databases::safeDropTables(db, tableNames: tables);
1108 SETUP_RECORD_TABLE;
1109 createTestTables(db);
1110 populateTestTables(db);
1111 CHECK_RECORD;
1112}
1113
1114void tst_QSqlQuery::isValid()
1115{
1116 QFETCH( QString, dbName );
1117 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1118 CHECK_DATABASE( db );
1119
1120 QSqlQuery q( db );
1121 QVERIFY( !q.isValid() );
1122 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1123 QVERIFY( q.first() );
1124 QVERIFY( q.isValid() );
1125}
1126
1127void tst_QSqlQuery::isActive()
1128{
1129 QFETCH( QString, dbName );
1130 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1131 CHECK_DATABASE( db );
1132
1133 QSqlQuery q( db );
1134 QVERIFY( !q.isActive() );
1135 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1136 QVERIFY( q.isActive() );
1137 QVERIFY( q.last() );
1138
1139 if ( !tst_Databases::isMSAccess( db ) )
1140 // Access is stupid enough to let you scroll over boundaries
1141 QVERIFY( !q.next() );
1142
1143 QVERIFY( q.isActive() );
1144
1145 QVERIFY_SQL( q, exec( "insert into " + qtest + " values (41, 'VarChar41', 'Char41')" ) );
1146
1147 QVERIFY( q.isActive() );
1148
1149 QVERIFY_SQL( q, exec( "update " + qtest + " set id = 42 where id = 41" ) );
1150
1151 QVERIFY( q.isActive() );
1152
1153 QVERIFY_SQL( q, exec( "delete from " + qtest + " where id = 42" ) );
1154
1155 QVERIFY( q.isActive() );
1156
1157 QVERIFY_SQL( q, exec( "delete from " + qtest + " where id = 42" ) );
1158
1159 QVERIFY( q.isActive() );
1160}
1161
1162void tst_QSqlQuery::numRowsAffected()
1163{
1164 QFETCH( QString, dbName );
1165 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1166 CHECK_DATABASE( db );
1167
1168 QSqlQuery q( db );
1169 QCOMPARE( q.numRowsAffected(), -1 );
1170
1171 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1172 int i = 0;
1173
1174 while ( q.next() )
1175 ++i;
1176
1177 if ( q.numRowsAffected() == -1 || q.numRowsAffected() == 0 )
1178 QSKIP("Database doesn't support numRowsAffected");
1179
1180 if ( q.numRowsAffected() != -1 && q.numRowsAffected() != 0 && q.numRowsAffected() != i ) {
1181 // the value is undefined for SELECT, this check is just here for curiosity
1182 qDebug( msg: "Expected numRowsAffected to be -1, 0 or %d, got %d", i, q.numRowsAffected() );
1183 }
1184
1185 QVERIFY_SQL( q, exec( "update " + qtest + " set id = 100 where id = 1" ) );
1186
1187 QCOMPARE( q.numRowsAffected(), 1 );
1188 QCOMPARE( q.numRowsAffected(), 1 ); // yes, we check twice
1189
1190 QVERIFY_SQL( q, exec( "update " + qtest + " set id = id + 100" ) );
1191 QCOMPARE( q.numRowsAffected(), i );
1192 QCOMPARE( q.numRowsAffected(), i ); // yes, we check twice
1193
1194 QVERIFY_SQL( q, prepare( "update " + qtest + " set id = id + :newid" ) );
1195 q.bindValue(placeholder: ":newid", val: 100);
1196 QVERIFY_SQL( q, exec() );
1197 QCOMPARE( q.numRowsAffected(), i );
1198 QCOMPARE( q.numRowsAffected(), i ); // yes, we check twice
1199
1200 QVERIFY_SQL( q, prepare( "update " + qtest + " set id = id + :newid where NOT(1 = 1)" ) );
1201 q.bindValue(placeholder: ":newid", val: 100);
1202 QVERIFY_SQL( q, exec() );
1203 QCOMPARE( q.numRowsAffected(), 0 );
1204 QCOMPARE( q.numRowsAffected(), 0 ); // yes, we check twice
1205
1206 QVERIFY_SQL( q, exec( "insert into " + qtest + " values (42000, 'homer', 'marge')" ) );
1207 QCOMPARE( q.numRowsAffected(), 1 );
1208 QCOMPARE( q.numRowsAffected(), 1 ); // yes, we check twice
1209
1210 QSqlQuery q2( db );
1211 QVERIFY_SQL( q2, exec( "insert into " + qtest + " values (42001, 'homer', 'marge')" ) );
1212
1213 if ( !db.driverName().startsWith( s: "QSQLITE2" ) ) {
1214 // SQLite 2.x accumulates changed rows in nested queries. See task 33794
1215 QCOMPARE( q2.numRowsAffected(), 1 );
1216 QCOMPARE( q2.numRowsAffected(), 1 ); // yes, we check twice
1217 }
1218}
1219
1220void tst_QSqlQuery::size()
1221{
1222 QFETCH( QString, dbName );
1223 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1224 CHECK_DATABASE( db );
1225
1226 QSqlQuery q( db );
1227 QCOMPARE( q.size(), -1 );
1228
1229 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1230 int i = 0;
1231
1232 while ( q.next() )
1233 ++i;
1234
1235 if ( db.driver()->hasFeature( f: QSqlDriver::QuerySize ) ) {
1236 QCOMPARE( q.size(), i );
1237 QCOMPARE( q.size(), i ); // yes, twice
1238 } else {
1239 QCOMPARE( q.size(), -1 );
1240 QCOMPARE( q.size(), -1 ); // yes, twice
1241 }
1242
1243 QSqlQuery q2( "select * from " + qtest, db );
1244
1245 if ( db.driver()->hasFeature( f: QSqlDriver::QuerySize ) )
1246 QCOMPARE( q.size(), i );
1247 else
1248 QCOMPARE( q.size(), -1 );
1249
1250 q2.clear();
1251
1252 QVERIFY_SQL( q, exec( "update " + qtest + " set id = 100 where id = 1" ) );
1253 QCOMPARE( q.size(), -1 );
1254 QCOMPARE( q.size(), -1 ); // yes, twice
1255}
1256
1257void tst_QSqlQuery::isSelect()
1258{
1259 QFETCH( QString, dbName );
1260 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1261 CHECK_DATABASE( db );
1262
1263 QSqlQuery q( db );
1264 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1265 QVERIFY( q.isSelect() );
1266
1267 QVERIFY_SQL( q, exec( "update " + qtest + " set id = 1 where id = 1" ) );
1268 QVERIFY( q.isSelect() == false );
1269}
1270
1271void tst_QSqlQuery::first()
1272{
1273 QFETCH( QString, dbName );
1274 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1275 CHECK_DATABASE( db );
1276
1277 QSqlQuery q( db );
1278 QVERIFY( q.at() == QSql::BeforeFirstRow );
1279 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1280 QVERIFY( q.last() );
1281 QVERIFY_SQL( q, first() );
1282 QVERIFY( q.at() == 0 );
1283}
1284
1285void tst_QSqlQuery::next()
1286{
1287 QFETCH( QString, dbName );
1288 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1289 CHECK_DATABASE( db );
1290
1291 QSqlQuery q( db );
1292 QVERIFY( q.at() == QSql::BeforeFirstRow );
1293 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1294 QVERIFY( q.first() );
1295 QVERIFY( q.next() );
1296 QVERIFY( q.at() == 1 );
1297}
1298
1299void tst_QSqlQuery::prev()
1300{
1301 QFETCH( QString, dbName );
1302 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1303 CHECK_DATABASE( db );
1304
1305 QSqlQuery q( db );
1306 QVERIFY( q.at() == QSql::BeforeFirstRow );
1307 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1308 QVERIFY( q.first() );
1309 QVERIFY( q.next() );
1310 QVERIFY( q.previous() );
1311 QVERIFY( q.at() == 0 );
1312}
1313
1314void tst_QSqlQuery::last()
1315{
1316 QFETCH( QString, dbName );
1317 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1318 CHECK_DATABASE( db );
1319
1320 QSqlQuery q( db );
1321 QCOMPARE( q.at(), int( QSql::BeforeFirstRow ) );
1322 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1323 int i = 0;
1324
1325 while ( q.next() )
1326 i++;
1327
1328 QCOMPARE( q.at(), int( QSql::AfterLastRow ) );
1329
1330 QVERIFY( q.last() );
1331
1332 QSet<int> validReturns(QSet<int>() << -1 << i-1);
1333 QVERIFY( validReturns.contains(q.at()) );
1334
1335 QSqlQuery q2( "select * from " + qtest, db );
1336
1337 QVERIFY( q2.last() );
1338
1339 QVERIFY( validReturns.contains(q.at()) );
1340}
1341
1342void tst_QSqlQuery::seek()
1343{
1344 QFETCH( QString, dbName );
1345 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1346 CHECK_DATABASE( db );
1347 QSqlQuery q( db );
1348 QVERIFY( q.at() == QSql::BeforeFirstRow );
1349 QVERIFY_SQL( q, exec( QString( "select id from %1 order by id" ).arg( qtest ) ) );
1350
1351 // NB! The order of the calls below are important!
1352 QVERIFY( q.last() );
1353 QVERIFY( !q.seek( QSql::BeforeFirstRow ) );
1354 QCOMPARE( q.at(), int( QSql::BeforeFirstRow ) );
1355 QVERIFY( q.seek( 0 ) );
1356 QCOMPARE( q.at(), 0 );
1357 QCOMPARE( q.value( 0 ).toInt(), 1 );
1358
1359 QVERIFY( q.seek( 1 ) );
1360 QCOMPARE( q.at(), 1 );
1361 QCOMPARE( q.value( 0 ).toInt(), 2 );
1362
1363 QVERIFY( q.seek( 3 ) );
1364 QCOMPARE( q.at(), 3 );
1365 QCOMPARE( q.value( 0 ).toInt(), 4 );
1366
1367 QVERIFY( q.seek( -2, true ) );
1368 QCOMPARE( q.at(), 1 );
1369 QVERIFY( q.seek( 0 ) );
1370 QCOMPARE( q.at(), 0 );
1371 QCOMPARE( q.value( 0 ).toInt(), 1 );
1372
1373 QVERIFY(!q.seek(QSql::BeforeFirstRow));
1374 QCOMPARE(q.at(), int(QSql::BeforeFirstRow));
1375 QVERIFY(q.seek(1, true));
1376 QCOMPARE(q.at(), 0);
1377 QCOMPARE(q.value(0).toInt(), 1);
1378
1379 qint32 count = 1;
1380 while (q.next()) ++count;
1381
1382 QCOMPARE(q.at(), int(QSql::AfterLastRow));
1383
1384 if (!q.isForwardOnly()) {
1385 QVERIFY(q.seek(-1, true));
1386 QCOMPARE(q.at(), count - 1);
1387 QCOMPARE(q.value(0).toInt(), count);
1388 } else {
1389 QVERIFY(!q.seek(-1, true));
1390 QCOMPARE(q.at(), int(QSql::AfterLastRow));
1391 }
1392}
1393
1394void tst_QSqlQuery::seekForwardOnlyQuery()
1395{
1396 QFETCH( QString, dbName );
1397 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1398 CHECK_DATABASE( db );
1399
1400 QSqlQuery q( db );
1401 q.setForwardOnly( false );
1402 QVERIFY( !q.isForwardOnly() );
1403
1404 QVERIFY( q.at() == QSql::BeforeFirstRow );
1405 QVERIFY_SQL( q, exec( QString( "select id from %1 order by id" ).arg( qtest ) ) );
1406
1407 QSqlRecord rec;
1408
1409 // NB! The order of the calls below are important!
1410 QVERIFY( q.seek( 0 ) );
1411 QCOMPARE( q.at(), 0 );
1412 rec = q.record();
1413 QCOMPARE( rec.value( 0 ).toInt(), 1 );
1414
1415 QVERIFY( q.seek( 1 ) );
1416 QCOMPARE( q.at(), 1 );
1417 rec = q.record();
1418 QCOMPARE( rec.value( 0 ).toInt(), 2 );
1419
1420 // Make a jump!
1421 QVERIFY( q.seek( 3 ) );
1422 QCOMPARE( q.at(), 3 );
1423 rec = q.record();
1424 QCOMPARE( rec.value( 0 ).toInt(), 4 );
1425
1426 // Last record in result set
1427 QVERIFY( q.seek( 4 ) );
1428 QCOMPARE( q.at(), 4 );
1429 rec = q.record();
1430 QCOMPARE( rec.value( 0 ).toInt(), 5 );
1431}
1432
1433// tests the forward only mode;
1434void tst_QSqlQuery::forwardOnly()
1435{
1436 QFETCH( QString, dbName );
1437 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1438 CHECK_DATABASE( db );
1439
1440 QSqlQuery q( db );
1441 q.setForwardOnly( true );
1442 QVERIFY( q.isForwardOnly() );
1443 QVERIFY( q.at() == QSql::BeforeFirstRow );
1444 QVERIFY_SQL( q, exec( "select * from " + qtest + " order by id" ) );
1445 if (!q.isForwardOnly())
1446 QSKIP("DBMS doesn't support forward-only queries");
1447 QVERIFY( q.at() == QSql::BeforeFirstRow );
1448 QVERIFY( q.first() );
1449 QCOMPARE( q.at(), 0 );
1450 QCOMPARE( q.value( 0 ).toInt(), 1 );
1451 QVERIFY( q.next() );
1452 QCOMPARE( q.at(), 1 );
1453 QCOMPARE( q.value( 0 ).toInt(), 2 );
1454 QVERIFY( q.next() );
1455 QCOMPARE( q.at(), 2 );
1456 QCOMPARE( q.value( 0 ).toInt(), 3 );
1457
1458 // lets make some mistakes to see how robust it is
1459 QTest::ignoreMessage( type: QtWarningMsg, message: "QSqlQuery::seek: cannot seek backwards in a forward only query" );
1460 QVERIFY( q.first() == false );
1461 QCOMPARE( q.at(), 2 );
1462 QCOMPARE( q.value( 0 ).toInt(), 3 );
1463 QTest::ignoreMessage( type: QtWarningMsg, message: "QSqlQuery::seek: cannot seek backwards in a forward only query" );
1464 QVERIFY( q.previous() == false );
1465 QCOMPARE( q.at(), 2 );
1466 QCOMPARE( q.value( 0 ).toInt(), 3 );
1467 QVERIFY( q.next() );
1468 QCOMPARE( q.at(), 3 );
1469 QCOMPARE( q.value( 0 ).toInt(), 4 );
1470
1471 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1472 int i = 0;
1473
1474 while ( q.next() )
1475 i++;
1476
1477 QVERIFY( q.at() == QSql::AfterLastRow );
1478
1479 QSqlQuery q2 = q;
1480
1481 QVERIFY( q2.isForwardOnly() );
1482
1483 QVERIFY_SQL( q, exec( "select * from " + qtest + " order by id" ) );
1484
1485 QVERIFY( q.isForwardOnly() );
1486
1487 QVERIFY( q2.isForwardOnly() );
1488
1489 QCOMPARE( q.at(), int( QSql::BeforeFirstRow ) );
1490
1491 QVERIFY_SQL( q, seek( 3 ) );
1492
1493 QCOMPARE( q.at(), 3 );
1494
1495 QCOMPARE( q.value( 0 ).toInt(), 4 );
1496
1497 QTest::ignoreMessage( type: QtWarningMsg, message: "QSqlQuery::seek: cannot seek backwards in a forward only query" );
1498
1499 QVERIFY( q.seek( 0 ) == false );
1500
1501 QCOMPARE( q.value( 0 ).toInt(), 4 );
1502
1503 QCOMPARE( q.at(), 3 );
1504
1505 QVERIFY( q.last() );
1506
1507 QCOMPARE( q.at(), i-1 );
1508
1509 QTest::ignoreMessage( type: QtWarningMsg, message: "QSqlQuery::seek: cannot seek backwards in a forward only query" );
1510
1511 QVERIFY( q.first() == false );
1512
1513 QCOMPARE( q.at(), i-1 );
1514
1515 QVERIFY( q.next() == false );
1516
1517 QCOMPARE( q.at(), int( QSql::AfterLastRow ) );
1518}
1519
1520void tst_QSqlQuery::forwardOnlyMultipleResultSet()
1521{
1522 QFETCH(QString, dbName);
1523 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1524 CHECK_DATABASE(db);
1525
1526 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
1527
1528 if (!db.driver()->hasFeature(f: QSqlDriver::MultipleResultSets))
1529 QSKIP("DBMS doesn't support multiple result sets");
1530
1531 QSqlQuery q(db);
1532 q.setForwardOnly(true);
1533 QVERIFY_SQL(q, exec("select id, t_varchar from " + qtest + " order by id;" // 1.
1534 "select id, t_varchar, t_char from " + qtest + " where id<4 order by id;" // 2.
1535 "update " + qtest + " set t_varchar='VarChar555' where id=5;" // 3.
1536 "select * from " + qtest + " order by id;" // 4.
1537 "select * from " + qtest + " where id=5 order by id;" // 5.
1538 "select * from " + qtest + " where id=-1 order by id;" // 6.
1539 "select * from " + qtest + " order by id")); // 7.
1540
1541 if (!q.isForwardOnly())
1542 QSKIP("DBMS doesn't support forward-only queries");
1543
1544 // 1. Result set with 2 columns and 5 rows
1545 QVERIFY(q.at() == QSql::BeforeFirstRow);
1546 QCOMPARE(q.isActive(), true);
1547 QCOMPARE(q.isSelect(), true);
1548
1549 // Test record() of first result set
1550 QSqlRecord record = q.record();
1551 QCOMPARE(record.count(), 2);
1552 QCOMPARE(record.indexOf("id"), 0);
1553 QCOMPARE(record.indexOf("t_varchar"), 1);
1554 if (dbType != QSqlDriver::PostgreSQL) { // tableName() is not available in forward-only mode of QPSQL
1555 QCOMPARE(record.field(0).tableName(), qtest); // BUG: This fails for Microsoft SQL Server 2016 (QODBC), need fix
1556 QCOMPARE(record.field(1).tableName(), qtest);
1557 }
1558
1559 // Test navigation
1560 QVERIFY(q.first());
1561 QCOMPARE(q.at(), 0);
1562 QCOMPARE(q.value(0).toInt(), 1);
1563
1564 QVERIFY(q.next());
1565 QCOMPARE(q.at(), 1);
1566 QCOMPARE(q.value(0).toInt(), 2);
1567
1568 QVERIFY(q.seek(3));
1569 QCOMPARE(q.at(), 3);
1570 QCOMPARE(q.value(0).toInt(), 4);
1571
1572 QTest::ignoreMessage(type: QtWarningMsg, message: "QSqlQuery::seek: cannot seek backwards in a forward only query");
1573 QVERIFY(q.first() == false);
1574 QCOMPARE(q.at(), 3);
1575 QCOMPARE(q.value(0).toInt(), 4);
1576
1577 QTest::ignoreMessage(type: QtWarningMsg, message: "QSqlQuery::seek: cannot seek backwards in a forward only query");
1578 QVERIFY(q.previous() == false);
1579 QCOMPARE(q.at(), 3);
1580 QCOMPARE(q.value(0).toInt(), 4);
1581
1582 QTest::ignoreMessage(type: QtWarningMsg, message: "QSqlQuery::seek: cannot seek backwards in a forward only query");
1583 QVERIFY(q.seek(1) == false);
1584 QCOMPARE(q.at(), 3);
1585 QCOMPARE(q.value(0).toInt(), 4);
1586
1587 QVERIFY(q.last());
1588 QCOMPARE(q.at(), 4);
1589
1590 // Try move after last row
1591 QVERIFY(q.next() == false);
1592 QCOMPARE(q.at(), QSql::AfterLastRow);
1593 QCOMPARE(q.isActive(), true);
1594
1595 // 2. Result set with 3 columns and 3 rows
1596 QVERIFY(q.nextResult());
1597 QVERIFY(q.at() == QSql::BeforeFirstRow);
1598 QCOMPARE(q.isActive(), true);
1599 QCOMPARE(q.isSelect(), true);
1600
1601 // Test record() of second result set
1602 record = q.record();
1603 QCOMPARE(record.count(), 3);
1604 QCOMPARE(record.indexOf("id"), 0);
1605 QCOMPARE(record.indexOf("t_varchar"), 1);
1606 QCOMPARE(record.indexOf("t_char"), 2);
1607
1608 // Test iteration
1609 QVERIFY(q.at() == QSql::BeforeFirstRow);
1610 int index = 0;
1611 while (q.next()) {
1612 QCOMPARE(q.at(), index);
1613 QCOMPARE(q.value(0).toInt(), index+1);
1614 index++;
1615 }
1616 QVERIFY(q.at() == QSql::AfterLastRow);
1617 QCOMPARE(index, 3);
1618
1619 // 3. Update statement
1620 QVERIFY(q.nextResult());
1621 QVERIFY(q.at() == QSql::BeforeFirstRow);
1622 QCOMPARE(q.isActive(), true);
1623 QCOMPARE(q.isSelect(), false);
1624 QCOMPARE(q.numRowsAffected(), 1);
1625
1626 // 4. Result set with 5 rows
1627 QVERIFY(q.nextResult());
1628 QVERIFY(q.at() == QSql::BeforeFirstRow);
1629 QCOMPARE(q.isActive(), true);
1630 QCOMPARE(q.isSelect(), true);
1631
1632 // Test forward seek(n)
1633 QVERIFY(q.seek(2));
1634 QCOMPARE(q.at(), 2);
1635 QCOMPARE(q.value(0).toInt(), 3);
1636
1637 // Test value(string)
1638 QCOMPARE(q.value("id").toInt(), 3);
1639 QCOMPARE(q.value("t_varchar").toString(), "VarChar3");
1640 QCOMPARE(q.value("t_char").toString().trimmed(), "Char3");
1641
1642 // Next 2 rows of current result set will be
1643 // discarded by next call of nextResult()
1644
1645 // 5. Result set with 1 row
1646 QVERIFY(q.nextResult());
1647 QVERIFY(q.at() == QSql::BeforeFirstRow);
1648 QCOMPARE(q.isActive(), true);
1649 QCOMPARE(q.isSelect(), true);
1650 QVERIFY(q.first());
1651 QCOMPARE(q.at(), 0);
1652 QCOMPARE(q.value(0).toInt(), 5);
1653 QVERIFY(q.next() == false);
1654 QVERIFY(q.at() == QSql::AfterLastRow);
1655
1656 // 6. Result set without rows
1657 QVERIFY(q.nextResult());
1658 QVERIFY(q.at() == QSql::BeforeFirstRow);
1659 QCOMPARE(q.isActive(), true);
1660 QCOMPARE(q.isSelect(), true);
1661 QVERIFY(q.next() == false);
1662
1663 // 7. Result set with 5 rows
1664 QVERIFY(q.nextResult());
1665 QVERIFY(q.at() == QSql::BeforeFirstRow);
1666 QCOMPARE(q.isActive(), true);
1667 QCOMPARE(q.isSelect(), true);
1668
1669 // Just skip it, so we move after last result set.
1670 QVERIFY(q.nextResult() == false);
1671 QVERIFY(q.at() == QSql::BeforeFirstRow);
1672 QCOMPARE(q.isActive(), false);
1673
1674 // See if we can execute another query
1675 QVERIFY_SQL(q, exec("select id from " + qtest + " where id=5"));
1676 QVERIFY(q.at() == QSql::BeforeFirstRow);
1677 QCOMPARE(q.isActive(), true);
1678 QCOMPARE(q.isSelect(), true);
1679 QVERIFY(q.first());
1680 QCOMPARE(q.at(), 0);
1681 QCOMPARE(q.value("id").toInt(), 5);
1682 QCOMPARE(q.record().count(), 1);
1683 QCOMPARE(q.record().indexOf("id"), 0);
1684}
1685
1686void tst_QSqlQuery::psql_forwardOnlyQueryResultsLost()
1687{
1688 QFETCH(QString, dbName);
1689 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1690 CHECK_DATABASE(db);
1691
1692 QSqlQuery q1(db);
1693 q1.setForwardOnly(true);
1694 QVERIFY_SQL(q1, exec("select * from " + qtest + " where id<=3 order by id"));
1695 if (!q1.isForwardOnly())
1696 QSKIP("DBMS doesn't support forward-only queries");
1697
1698 // Read first row of q1
1699 QVERIFY(q1.next());
1700 QCOMPARE(q1.at(), 0);
1701 QCOMPARE(q1.value(0).toInt(), 1);
1702
1703 // Executing another query on the same db connection
1704 // will cause the query results of q1 to be lost.
1705 QSqlQuery q2(db);
1706 q2.setForwardOnly(true);
1707 QVERIFY_SQL(q2, exec("select * from " + qtest + " where id>3 order by id"));
1708
1709 QTest::ignoreMessage(type: QtWarningMsg, message: "QPSQLDriver::getResult: Query results lost - "
1710 "probably discarded on executing another SQL query.");
1711
1712 // Reading next row of q1 will not possible.
1713 QVERIFY(!q1.next());
1714 QCOMPARE(q1.at(), QSql::AfterLastRow);
1715 QVERIFY(q1.lastError().type() != QSqlError::NoError);
1716
1717 // See if we can read rows from q2
1718 QVERIFY(q2.seek(1));
1719 QCOMPARE(q2.at(), 1);
1720 QCOMPARE(q2.value(0).toInt(), 5);
1721}
1722
1723void tst_QSqlQuery::query_exec()
1724{
1725 QFETCH( QString, dbName );
1726 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1727 CHECK_DATABASE( db );
1728
1729 QSqlQuery q( db );
1730 QVERIFY( !q.isValid() );
1731 QVERIFY( !q.isActive() );
1732 QVERIFY_SQL( q, exec( "select * from " + qtest ) );
1733 QVERIFY( q.isActive() );
1734 QVERIFY( q.next() );
1735 QVERIFY( q.isValid() );
1736}
1737
1738void tst_QSqlQuery::isNull()
1739{
1740 QFETCH( QString, dbName );
1741 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1742 CHECK_DATABASE( db );
1743
1744 QSqlQuery q( db );
1745 QVERIFY_SQL(q, exec("select id, t_varchar from " + qTableName("qtest_null", __FILE__, db) + " order by id"));
1746 QVERIFY( q.next() );
1747 QVERIFY( !q.isNull( 0 ) );
1748 QVERIFY(!q.isNull("id"));
1749 QVERIFY( q.isNull( 1 ) );
1750 QVERIFY(q.isNull("t_varchar"));
1751 QCOMPARE( q.value( 0 ).toInt(), 0 );
1752 QCOMPARE( q.value( 1 ).toString(), QString() );
1753 QVERIFY( !q.value( 0 ).isNull() );
1754 QVERIFY( q.value( 1 ).isNull() );
1755
1756 QVERIFY( q.next() );
1757 QVERIFY( !q.isNull( 0 ) );
1758 QVERIFY(!q.isNull("id"));
1759 QVERIFY( !q.isNull( 1 ) );
1760 QVERIFY(!q.isNull("t_varchar"));
1761
1762 // For a non existent field, it should be returning true.
1763 QVERIFY(q.isNull(2));
1764 QVERIFY(q.isNull("unknown"));
1765}
1766
1767/*! TDS specific BIT field test */
1768void tst_QSqlQuery::tds_bitField()
1769{
1770 QFETCH( QString, dbName );
1771 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1772 CHECK_DATABASE( db );
1773 const QString tableName = qTableName(prefix: "qtest_bittest", __FILE__, db);
1774 QSqlQuery q( db );
1775
1776 QVERIFY_SQL(q, exec("create table " + tableName + " (bitty bit)"));
1777
1778 QVERIFY_SQL(q, exec("insert into " + tableName + " values (0)"));
1779
1780 QVERIFY_SQL(q, exec("insert into " + tableName + " values (1)"));
1781
1782 QVERIFY_SQL(q, exec("select bitty from " + tableName));
1783
1784 QVERIFY( q.next() );
1785
1786 QVERIFY( q.value( 0 ).toInt() == 0 );
1787
1788 QVERIFY( q.next() );
1789
1790 QVERIFY( q.value( 0 ).toInt() == 1 );
1791}
1792
1793
1794/*! Oracle specific NULL BLOB test */
1795void tst_QSqlQuery::oci_nullBlob()
1796{
1797 QFETCH( QString, dbName );
1798 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1799 CHECK_DATABASE( db );
1800 const QString qtest_nullblob(qTableName(prefix: "qtest_nullblob", __FILE__, db));
1801
1802 QSqlQuery q( db );
1803 QVERIFY_SQL( q, exec( "create table " + qtest_nullblob + " (id int primary key, bb blob)" ) );
1804 QVERIFY_SQL( q, exec( "insert into " + qtest_nullblob + " values (0, EMPTY_BLOB())" ) );
1805 QVERIFY_SQL( q, exec( "insert into " + qtest_nullblob + " values (1, NULL)" ) );
1806 QVERIFY_SQL( q, exec( "insert into " + qtest_nullblob + " values (2, 'aabbcc00112233445566')" ) );
1807 // necessary otherwise oracle will bombard you with internal errors
1808 q.setForwardOnly( true );
1809 QVERIFY_SQL( q, exec( "select * from " + qtest_nullblob + " order by id" ) );
1810
1811 QVERIFY( q.next() );
1812 QCOMPARE(( int )q.value( 1 ).toByteArray().size(), 0 );
1813 QVERIFY( !q.isNull( 1 ) );
1814
1815 QVERIFY( q.next() );
1816 QCOMPARE(( int )q.value( 1 ).toByteArray().size(), 0 );
1817 QVERIFY( q.isNull( 1 ) );
1818
1819 QVERIFY( q.next() );
1820 QCOMPARE(( int )q.value( 1 ).toByteArray().size(), 10 );
1821 QVERIFY( !q.isNull( 1 ) );
1822}
1823
1824/* Oracle specific RAW field test */
1825void tst_QSqlQuery::oci_rawField()
1826{
1827 QFETCH( QString, dbName );
1828 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1829 CHECK_DATABASE( db );
1830 const QString qtest_rawtest(qTableName(prefix: "qtest_rawtest", __FILE__, db));
1831
1832 QSqlQuery q( db );
1833 q.setForwardOnly( true );
1834 QVERIFY_SQL( q, exec( "create table " + qtest_rawtest +
1835 " (id int, col raw(20))" ) );
1836 QVERIFY_SQL( q, exec( "insert into " + qtest_rawtest + " values (0, NULL)" ) );
1837 QVERIFY_SQL( q, exec( "insert into " + qtest_rawtest + " values (1, '00aa1100ddeeff')" ) );
1838 QVERIFY_SQL( q, exec( "select col from " + qtest_rawtest + " order by id" ) );
1839 QVERIFY( q.next() );
1840 QVERIFY( q.isNull( 0 ) );
1841 QCOMPARE(( int )q.value( 0 ).toByteArray().size(), 0 );
1842 QVERIFY( q.next() );
1843 QVERIFY( !q.isNull( 0 ) );
1844 QCOMPARE(( int )q.value( 0 ).toByteArray().size(), 7 );
1845}
1846
1847// test whether we can fetch values with more than DOUBLE precision
1848// note that SQLite highest precision is that of a double, although
1849// you can define field with higher precision
1850void tst_QSqlQuery::precision()
1851{
1852 QFETCH( QString, dbName );
1853 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1854 CHECK_DATABASE( db );
1855 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
1856 if (dbType == QSqlDriver::Interbase)
1857 QSKIP("DB unable to store high precision");
1858
1859 const auto oldPrecision = db.driver()->numericalPrecisionPolicy();
1860 db.driver()->setNumericalPrecisionPolicy(QSql::HighPrecision);
1861 const QString qtest_precision(qTableName(prefix: "qtest_precision", __FILE__, db));
1862 static const QLatin1String precStr("1.2345678901234567891");
1863
1864 {
1865 // need a new scope for SQLITE
1866 QSqlQuery q( db );
1867
1868 q.exec(query: "drop table " + qtest_precision);
1869 if (tst_Databases::isMSAccess(db))
1870 QVERIFY_SQL(q, exec("CREATE TABLE " + qtest_precision + " (col1 number)"));
1871 else
1872 QVERIFY_SQL(q, exec("CREATE TABLE " + qtest_precision + " (col1 numeric(21, 20))"));
1873
1874 QVERIFY_SQL(q, exec("INSERT INTO " + qtest_precision + " (col1) VALUES (" + precStr + ")"));
1875 QVERIFY_SQL(q, exec("SELECT * FROM " + qtest_precision));
1876 QVERIFY(q.next());
1877 const QString val = q.value(i: 0).toString();
1878 if (!val.startsWith(s: precStr)) {
1879 int i = 0;
1880 while (i < val.size() && precStr[i] != 0 && precStr[i] == val[i].toLatin1())
1881 i++;
1882
1883 // TDS has crappy precisions by default
1884 if (dbType == QSqlDriver::Sybase) {
1885 if (i < 18)
1886 QWARN("TDS didn't return the right precision");
1887 } else {
1888 QWARN(QString(tst_Databases::dbToString(db) + " didn't return the right precision (" +
1889 QString::number(i) + " out of 21), " + val).toUtf8());
1890 }
1891 }
1892 } // SQLITE scope
1893 db.driver()->setNumericalPrecisionPolicy(oldPrecision);
1894}
1895
1896void tst_QSqlQuery::nullResult()
1897{
1898 QFETCH( QString, dbName );
1899 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1900 CHECK_DATABASE( db );
1901
1902 QSqlQuery q( db );
1903 QVERIFY_SQL( q, exec( "select * from " + qtest + " where id > 50000" ) );
1904
1905 if ( q.driver()->hasFeature( f: QSqlDriver::QuerySize ) )
1906 QCOMPARE( q.size(), 0 );
1907
1908 QVERIFY( q.next() == false );
1909
1910 QVERIFY( q.first() == false );
1911 QVERIFY( q.last() == false );
1912 QVERIFY( q.previous() == false );
1913 QVERIFY( q.seek( 10 ) == false );
1914 QVERIFY( q.seek( 0 ) == false );
1915}
1916
1917// this test is just an experiment to see whether we can do query-based transactions
1918// the real transaction test is in tst_QSqlDatabase
1919void tst_QSqlQuery::transaction()
1920{
1921 // query based transaction is not really possible with Qt
1922 QSKIP( "only tested manually by trained staff");
1923
1924 QFETCH( QString, dbName );
1925 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
1926 CHECK_DATABASE( db );
1927 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
1928 if ( !db.driver()->hasFeature( f: QSqlDriver::Transactions ) )
1929 QSKIP( "DBMS not transaction capable");
1930
1931 // this is the standard SQL
1932 QString startTransactionStr( "start transaction" );
1933
1934 if (dbType == QSqlDriver::MySqlServer)
1935 startTransactionStr = "begin work";
1936
1937 QSqlQuery q( db );
1938
1939 QSqlQuery q2( db );
1940
1941 // test a working transaction
1942 q.exec( query: startTransactionStr );
1943
1944 QVERIFY_SQL( q, exec( "insert into" + qtest + " values (40, 'VarChar40', 'Char40')" ) );
1945
1946 QVERIFY_SQL( q, exec( "select * from" + qtest + " where id = 40" ) );
1947
1948 QVERIFY( q.next() );
1949
1950 QCOMPARE( q.value( 0 ).toInt(), 40 );
1951
1952 QVERIFY_SQL( q, exec( "commit" ) );
1953
1954 QVERIFY_SQL( q, exec( "select * from" + qtest + " where id = 40" ) );
1955
1956 QVERIFY( q.next() );
1957
1958 QCOMPARE( q.value( 0 ).toInt(), 40 );
1959
1960 // test a rollback
1961 q.exec( query: startTransactionStr );
1962
1963 QVERIFY_SQL( q, exec( "insert into" + qtest + " values (41, 'VarChar41', 'Char41')" ) );
1964
1965 QVERIFY_SQL( q, exec( "select * from" + qtest + " where id = 41" ) );
1966
1967 QVERIFY( q.next() );
1968
1969 QCOMPARE( q.value( 0 ).toInt(), 41 );
1970
1971 if ( !q.exec( query: "rollback" ) ) {
1972 if (dbType == QSqlDriver::MySqlServer) {
1973 qDebug( msg: "MySQL: %s", qPrintable(tst_Databases::printError( q.lastError() ) ));
1974 QSKIP( "MySQL transaction failed "); //non-fatal
1975 } else
1976 QFAIL( "Could not rollback transaction: " + tst_Databases::printError( q.lastError() ) );
1977 }
1978
1979 QVERIFY_SQL( q, exec( "select * from" + qtest + " where id = 41" ) );
1980
1981 QVERIFY( q.next() == false );
1982
1983 // test concurrent access
1984 q.exec( query: startTransactionStr );
1985 QVERIFY_SQL( q, exec( "insert into" + qtest + " values (42, 'VarChar42', 'Char42')" ) );
1986 QVERIFY_SQL( q, exec( "select * from" + qtest + " where id = 42" ) );
1987 QVERIFY( q.next() );
1988 QCOMPARE( q.value( 0 ).toInt(), 42 );
1989
1990 QVERIFY_SQL( q2, exec( "select * from" + qtest + " where id = 42" ) );
1991
1992 if ( q2.next() )
1993 qDebug(msg: "DBMS '%s' doesn't support query based transactions with concurrent access",
1994 qPrintable(tst_Databases::dbToString( db )));
1995
1996 QVERIFY_SQL( q, exec( "commit" ) );
1997
1998 QVERIFY_SQL( q2, exec( "select * from" + qtest + " where id = 42" ) );
1999
2000 QVERIFY( q2.next() );
2001
2002 QCOMPARE( q2.value( 0 ).toInt(), 42 );
2003}
2004
2005void tst_QSqlQuery::joins()
2006{
2007 QFETCH( QString, dbName );
2008 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2009 CHECK_DATABASE( db );
2010 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
2011 const QString qtestj1(qTableName(prefix: "qtestj1", __FILE__, db)), qtestj2(qTableName(prefix: "qtestj2", __FILE__, db));
2012
2013 if (dbType == QSqlDriver::Oracle || dbType == QSqlDriver::Sybase
2014 || dbType == QSqlDriver::Interbase || db.driverName().startsWith(s: "QODBC"))
2015 // Oracle broken beyond recognition - cannot outer join on more than
2016 // one table.
2017 QSKIP( "DBMS cannot understand standard SQL");
2018
2019 QSqlQuery q( db );
2020
2021 QVERIFY_SQL( q, exec( "create table " + qtestj1 + " (id1 int, id2 int)" ) );
2022 QVERIFY_SQL( q, exec( "create table " + qtestj2 + " (id int, name varchar(20))" ) );
2023 QVERIFY_SQL( q, exec( "insert into " + qtestj1 + " values (1, 1)" ) );
2024 QVERIFY_SQL( q, exec( "insert into " + qtestj1 + " values (1, 2)" ) );
2025 QVERIFY_SQL( q, exec( "insert into " + qtestj2 + " values(1, 'trenton')" ) );
2026 QVERIFY_SQL( q, exec( "insert into " + qtestj2 + " values(2, 'marius')" ) );
2027
2028 QVERIFY_SQL( q, exec( "select qtestj1.id1, qtestj1.id2, qtestj2.id, qtestj2.name, qtestj3.id, qtestj3.name "
2029 "from " + qtestj1 + " qtestj1 left outer join " + qtestj2 +
2030 " qtestj2 on (qtestj1.id1 = qtestj2.id) "
2031 "left outer join " + qtestj2 + " as qtestj3 on (qtestj1.id2 = qtestj3.id)" ) );
2032
2033 QVERIFY( q.next() );
2034 QCOMPARE( q.value( 0 ).toInt(), 1 );
2035 QCOMPARE( q.value( 1 ).toInt(), 1 );
2036 QCOMPARE( q.value( 2 ).toInt(), 1 );
2037 QCOMPARE( q.value( 3 ).toString(), QString( "trenton" ) );
2038 QCOMPARE( q.value( 4 ).toInt(), 1 );
2039 QCOMPARE( q.value( 5 ).toString(), QString( "trenton" ) );
2040
2041 QVERIFY( q.next() );
2042 QCOMPARE( q.value( 0 ).toInt(), 1 );
2043 QCOMPARE( q.value( 1 ).toInt(), 2 );
2044 QCOMPARE( q.value( 2 ).toInt(), 1 );
2045 QCOMPARE( q.value( 3 ).toString(), QString( "trenton" ) );
2046 QCOMPARE( q.value( 4 ).toInt(), 2 );
2047 QCOMPARE( q.value( 5 ).toString(), QString( "marius" ) );
2048}
2049
2050void tst_QSqlQuery::synonyms()
2051{
2052 QFETCH( QString, dbName );
2053 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2054 CHECK_DATABASE( db );
2055
2056 QSqlQuery q(db);
2057 QVERIFY_SQL( q, exec("select a.id, a.t_char, a.t_varchar from " + qtest + " a where a.id = 1") );
2058 QVERIFY( q.next() );
2059 QCOMPARE( q.value( 0 ).toInt(), 1 );
2060 QCOMPARE( q.value( 1 ).toString().trimmed(), QString( "Char1" ) );
2061 QCOMPARE( q.value( 2 ).toString().trimmed(), QString( "VarChar1" ) );
2062
2063 QSqlRecord rec = q.record();
2064 QCOMPARE(( int )rec.count(), 3 );
2065 QCOMPARE( rec.field( 0 ).name().toLower(), QString( "id" ) );
2066 QCOMPARE( rec.field( 1 ).name().toLower(), QString( "t_char" ) );
2067 QCOMPARE( rec.field( 2 ).name().toLower(), QString( "t_varchar" ) );
2068}
2069
2070// It doesn't make sense to split this into several tests
2071void tst_QSqlQuery::prepare_bind_exec()
2072{
2073 QFETCH( QString, dbName );
2074 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2075 CHECK_DATABASE( db );
2076 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
2077 const QString qtest_prepare(qTableName(prefix: "qtest_prepare", __FILE__, db));
2078
2079 if (dbType == QSqlDriver::DB2)
2080 QSKIP("Needs someone with more Unicode knowledge than I have to fix");
2081
2082 {
2083 // new scope for SQLITE
2084 static const QString utf8str = QString::fromUtf8( str: "काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥" );
2085
2086 static const QString values[6] = { "Harry", "Trond", "Mark", "Ma?rk", "?", ":id" };
2087
2088 bool useUnicode = db.driver()->hasFeature( f: QSqlDriver::Unicode );
2089
2090 QSqlQuery q( db );
2091 QString createQuery;
2092 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
2093 if (dbType == QSqlDriver::PostgreSQL)
2094 QVERIFY_SQL( q, exec("set client_min_messages='warning'"));
2095
2096 if (dbType == QSqlDriver::MSSqlServer || dbType == QSqlDriver::Sybase)
2097 createQuery = "create table " + qtest_prepare + " (id int primary key, name nvarchar(200) null, name2 nvarchar(200) null)";
2098 else if (dbType == QSqlDriver::MySqlServer && useUnicode)
2099 createQuery = "create table " + qtest_prepare + " (id int not null primary key, name varchar(200) character set utf8, name2 varchar(200) character set utf8)";
2100 else
2101 createQuery = "create table " + qtest_prepare + " (id int not null primary key, name varchar(200), name2 varchar(200))";
2102
2103 q.exec(query: "drop table " + qtest_prepare);
2104 QVERIFY_SQL( q, exec( createQuery ) );
2105
2106 QVERIFY( q.prepare( "insert into " + qtest_prepare + " (id, name) values (:id, :name)" ) );
2107 int i;
2108
2109 for ( i = 0; i < 6; ++i ) {
2110 q.bindValue( placeholder: ":name", val: values[i] );
2111 q.bindValue( placeholder: ":id", val: i );
2112 QVERIFY_SQL( q, exec() );
2113 QMap<QString, QVariant> m = q.boundValues();
2114 QCOMPARE(( int ) m.count(), 2 );
2115 QCOMPARE( m[":name"].toString(), values[i] );
2116 QCOMPARE( m[":id"].toInt(), i );
2117 }
2118
2119 q.bindValue( placeholder: ":id", val: 8 );
2120
2121 QVERIFY_SQL( q, exec() );
2122
2123 if ( useUnicode ) {
2124 q.bindValue( placeholder: ":id", val: 7 );
2125 q.bindValue( placeholder: ":name", val: utf8str );
2126 QVERIFY_SQL( q, exec() );
2127 }
2128
2129 QVERIFY_SQL( q, exec( "SELECT * FROM " + qtest_prepare + " order by id" ) );
2130
2131 for ( i = 0; i < 6; ++i ) {
2132 QVERIFY( q.next() );
2133 QCOMPARE( q.value( 0 ).toInt(), i );
2134 QCOMPARE( q.value( 1 ).toString().trimmed(), values[ i ] );
2135 }
2136
2137 if ( useUnicode ) {
2138 QVERIFY_SQL( q, next() );
2139 QCOMPARE( q.value( 0 ).toInt(), 7 );
2140 QCOMPARE( q.value( 1 ).toString(), utf8str );
2141 }
2142
2143 QVERIFY_SQL( q, next() );
2144
2145 QCOMPARE( q.value( 0 ).toInt(), 8 );
2146 QCOMPARE( q.value( 1 ).toString(), values[5] );
2147
2148 QVERIFY( q.prepare( "insert into " + qtest_prepare + " (id, name) values (:id, 'Bart')" ) );
2149 q.bindValue( placeholder: ":id", val: 99 );
2150 QVERIFY_SQL( q, exec() );
2151 q.bindValue( placeholder: ":id", val: 100 );
2152 QVERIFY_SQL( q, exec() );
2153 QVERIFY( q.exec( "select * from " + qtest_prepare + " where id > 98 order by id" ) );
2154
2155 for ( i = 99; i <= 100; ++i ) {
2156 QVERIFY( q.next() );
2157 QCOMPARE( q.value( 0 ).toInt(), i );
2158 QCOMPARE( q.value( 1 ).toString().trimmed(), QString( "Bart" ) );
2159 }
2160
2161 /*** SELECT stuff ***/
2162 QVERIFY( q.prepare( "select * from " + qtest_prepare + " where id = :id" ) );
2163
2164 for ( i = 0; i < 6; ++i ) {
2165 q.bindValue( placeholder: ":id", val: i );
2166 QVERIFY_SQL( q, exec() );
2167 QVERIFY_SQL( q, next() );
2168 QCOMPARE( q.value( 0 ).toInt(), i );
2169 QCOMPARE( q.value( 1 ).toString().trimmed(), values[ i ] );
2170 QSqlRecord rInf = q.record();
2171 QCOMPARE(( int )rInf.count(), 3 );
2172 QCOMPARE( rInf.field( 0 ).name().toUpper(), QString( "ID" ) );
2173 QCOMPARE( rInf.field( 1 ).name().toUpper(), QString( "NAME" ) );
2174 QVERIFY( !q.next() );
2175 }
2176
2177 QVERIFY_SQL( q, exec( "DELETE FROM " + qtest_prepare ) );
2178
2179 /*** Below we test QSqlQuery::boundValues() with position arguments.
2180 * Due to the fact that the name of a positional argument is not
2181 * specified by the Qt docs, we only test that the QMap contains
2182 * the correct values and that QSqlResult::boundValueName returns
2183 * the key that corrosponds to the correct value. ***/
2184 QVERIFY( q.prepare( "insert into " + qtest_prepare + " (id, name) values (?, ?)" ) );
2185 q.bindValue( pos: 0, val: 0 );
2186 q.bindValue( pos: 1, val: values[ 0 ] );
2187 QCOMPARE( q.boundValues().size(), 2 );
2188 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 0);
2189 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[0]);
2190 QVERIFY_SQL( q, exec() );
2191 QCOMPARE( q.boundValues().size(), 2 );
2192 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 0);
2193 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[0]);
2194
2195 q.addBindValue( val: 1 );
2196 q.addBindValue( val: values[ 1 ] );
2197 QCOMPARE( q.boundValues().size(), 2 );
2198 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 1);
2199 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[1]);
2200 QVERIFY_SQL( q, exec() );
2201 QCOMPARE( q.boundValues().size(), 2 );
2202 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 1);
2203 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[1]);
2204
2205 q.addBindValue( val: 2 );
2206 q.addBindValue( val: values[ 2 ] );
2207 QCOMPARE( q.boundValues().size(), 2 );
2208 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 2);
2209 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[2]);
2210 QVERIFY_SQL( q, exec() );
2211 QCOMPARE( q.boundValues().size(), 2 );
2212 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 2);
2213 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[2]);
2214
2215 q.addBindValue( val: 3 );
2216 q.addBindValue( val: values[ 3 ] );
2217 QCOMPARE( q.boundValues().size(), 2 );
2218 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 3);
2219 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[3]);
2220 QVERIFY_SQL( q, exec() );
2221 QCOMPARE( q.boundValues().size(), 2 );
2222 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 3);
2223 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[3]);
2224
2225 q.addBindValue( val: 4 );
2226 q.addBindValue( val: values[ 4 ] );
2227 QCOMPARE( q.boundValues().size(), 2 );
2228 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 4);
2229 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[4]);
2230 QVERIFY_SQL( q, exec() );
2231 QCOMPARE( q.boundValues().size(), 2 );
2232 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 4);
2233 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[4]);
2234
2235 q.bindValue( pos: 1, val: values[ 5 ] );
2236 q.bindValue( pos: 0, val: 5 );
2237 QCOMPARE( q.boundValues().size(), 2 );
2238 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 5);
2239 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[5]);
2240 QVERIFY_SQL( q, exec() );
2241 QCOMPARE( q.boundValues().size(), 2 );
2242 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 5);
2243 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), values[5]);
2244
2245 q.bindValue( pos: 0, val: 6 );
2246 q.bindValue( pos: 1, val: QString() );
2247 QCOMPARE( q.boundValues().size(), 2 );
2248 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 6);
2249 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), QString());
2250 QVERIFY_SQL( q, exec() );
2251 QCOMPARE( q.boundValues().size(), 2 );
2252 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 6);
2253 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), QString());
2254
2255 if ( db.driver()->hasFeature( f: QSqlDriver::Unicode ) ) {
2256 q.bindValue( pos: 0, val: 7 );
2257 q.bindValue( pos: 1, val: utf8str );
2258 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 7);
2259 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), utf8str);
2260 QVERIFY_SQL( q, exec() );
2261 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 7);
2262 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toString(), utf8str);
2263 }
2264
2265 // Test binding more placeholders than the query contains placeholders
2266 q.addBindValue(val: 8);
2267 q.addBindValue(val: 9);
2268 q.addBindValue(val: 10);
2269 QCOMPARE(q.boundValues().size(), 3);
2270 QCOMPARE(q.boundValues()[q.result()->boundValueName(0)].toInt(), 8);
2271 QCOMPARE(q.boundValues()[q.result()->boundValueName(1)].toInt(), 9);
2272 QCOMPARE(q.boundValues()[q.result()->boundValueName(2)].toInt(), 10);
2273 QFAIL_SQL(q, exec());
2274
2275 QVERIFY_SQL( q, exec( "SELECT * FROM " + qtest_prepare + " order by id" ) );
2276
2277 for ( i = 0; i < 6; ++i ) {
2278 QVERIFY( q.next() );
2279 QCOMPARE( q.value( 0 ).toInt(), i );
2280 QCOMPARE( q.value( 1 ).toString().trimmed(), values[ i ] );
2281 }
2282
2283 QVERIFY( q.next() );
2284
2285 QCOMPARE( q.value( 0 ).toInt(), 6 );
2286 QVERIFY( q.isNull( 1 ) );
2287
2288 if ( useUnicode ) {
2289 QVERIFY( q.next() );
2290 QCOMPARE( q.value( 0 ).toInt(), 7 );
2291 QCOMPARE( q.value( 1 ).toString(), utf8str );
2292 }
2293
2294 QVERIFY( q.prepare( "insert into " + qtest_prepare + " (id, name) values (?, 'Bart')" ) );
2295
2296 q.bindValue( pos: 0, val: 99 );
2297 QVERIFY_SQL( q, exec() );
2298 q.addBindValue( val: 100 );
2299 QVERIFY_SQL( q, exec() );
2300 QVERIFY( q.exec( "select * from " + qtest_prepare + " where id > 98 order by id" ) );
2301
2302 for ( i = 99; i <= 100; ++i ) {
2303 QVERIFY( q.next() );
2304 QCOMPARE( q.value( 0 ).toInt(), i );
2305 QCOMPARE( q.value( 1 ).toString().trimmed(), QString( "Bart" ) );
2306 }
2307
2308 /* insert a duplicate id and make sure the db bails out */
2309 QVERIFY( q.prepare( "insert into " + qtest_prepare + " (id, name) values (?, ?)" ) );
2310
2311 q.addBindValue( val: 99 );
2312
2313 q.addBindValue( val: "something silly" );
2314
2315 QVERIFY( !q.exec() );
2316
2317 QVERIFY( q.lastError().isValid() );
2318
2319 QVERIFY( !q.isActive() );
2320
2321 QVERIFY( q.prepare( "insert into " + qtest_prepare + " (id, name, name2) values (:id, :name, :name)" ) );
2322 for ( i = 101; i < 103; ++i ) {
2323 q.bindValue( placeholder: ":id", val: i );
2324 q.bindValue( placeholder: ":name", val: "name" );
2325 QVERIFY( q.exec() );
2326 }
2327
2328 // Test for QTBUG-6420
2329 QVERIFY( q.exec( "select * from " + qtest_prepare + " where id > 100 order by id" ) );
2330 QVERIFY( q.next() );
2331 QCOMPARE( q.value(0).toInt(), 101 );
2332 QCOMPARE( q.value(1).toString(), QString("name") );
2333 QCOMPARE( q.value(2).toString(), QString("name") );
2334
2335 // Test that duplicated named placeholders before the next unique one works correctly - QTBUG-65150
2336 QVERIFY(q.prepare("insert into " + qtest_prepare + " (id, name, name2) values (:id, :id, :name)"));
2337 for (i = 104; i < 106; ++i) {
2338 q.bindValue(placeholder: ":id", val: i);
2339 q.bindValue(placeholder: ":name", val: "name");
2340 QVERIFY(q.exec());
2341 }
2342 QVERIFY(q.exec("select * from " + qtest_prepare + " where id > 103 order by id"));
2343 QVERIFY(q.next());
2344 QCOMPARE(q.value(0).toInt(), 104);
2345 QCOMPARE(q.value(1).toString(), QString("104"));
2346 QCOMPARE(q.value(2).toString(), QString("name"));
2347
2348 // Test that duplicated named placeholders in any order
2349 QVERIFY(q.prepare("insert into " + qtest_prepare + " (id, name, name2) values (:id, :name, :id)"));
2350 for (i = 107; i < 109; ++i) {
2351 q.bindValue(placeholder: ":id", val: i);
2352 q.bindValue(placeholder: ":name", val: "name");
2353 QVERIFY(q.exec());
2354 }
2355 QVERIFY(q.exec("select * from " + qtest_prepare + " where id > 106 order by id"));
2356 QVERIFY(q.next());
2357 QCOMPARE(q.value(0).toInt(), 107);
2358 QCOMPARE(q.value(1).toString(), QString("name"));
2359 QCOMPARE(q.value(2).toString(), QString("107"));
2360
2361 // Test just duplicated placeholders
2362 QVERIFY(q.prepare("insert into " + qtest_prepare + " (id, name, name2) values (110, :name, :name)"));
2363 q.bindValue(placeholder: ":name", val: "name");
2364 QVERIFY_SQL(q, exec());
2365 QVERIFY(q.exec("select * from " + qtest_prepare + " where id > 109 order by id"));
2366 QVERIFY(q.next());
2367 QCOMPARE(q.value(0).toInt(), 110);
2368 QCOMPARE(q.value(1).toString(), QString("name"));
2369 QCOMPARE(q.value(2).toString(), QString("name"));
2370 } // end of SQLite scope
2371}
2372
2373void tst_QSqlQuery::prepared_select()
2374{
2375 QFETCH( QString, dbName );
2376 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2377 CHECK_DATABASE( db );
2378
2379 QSqlQuery q( db );
2380 QVERIFY_SQL( q, prepare( "select a.id, a.t_char, a.t_varchar from " + qtest + " a where a.id = ?" ) );
2381
2382 q.bindValue( pos: 0, val: 1 );
2383 QVERIFY_SQL( q, exec() );
2384 QCOMPARE( q.at(), ( int )QSql::BeforeFirstRow );
2385 QVERIFY( q.next() );
2386 QCOMPARE( q.value( 0 ).toInt(), 1 );
2387
2388 q.bindValue( pos: 0, val: 2 );
2389 QVERIFY_SQL( q, exec() );
2390 QCOMPARE( q.at(), ( int )QSql::BeforeFirstRow );
2391 QVERIFY( q.next() );
2392 QCOMPARE( q.value( 0 ).toInt(), 2 );
2393
2394 q.bindValue( pos: 0, val: 3 );
2395 QVERIFY_SQL( q, exec() );
2396 QCOMPARE( q.at(), ( int )QSql::BeforeFirstRow );
2397 QVERIFY( q.next() );
2398 QCOMPARE( q.value( 0 ).toInt(), 3 );
2399
2400 QVERIFY_SQL( q, prepare( "select a.id, a.t_char, a.t_varchar from " + qtest + " a where a.id = ?" ) );
2401 QCOMPARE( q.at(), ( int )QSql::BeforeFirstRow );
2402 QVERIFY( !q.first() );
2403}
2404
2405void tst_QSqlQuery::sqlServerLongStrings()
2406{
2407 QFETCH( QString, dbName );
2408 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2409 CHECK_DATABASE( db );
2410
2411 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
2412 if (dbType != QSqlDriver::MSSqlServer)
2413 QSKIP( "SQL Server specific test");
2414
2415 QSqlQuery q( db );
2416
2417 QVERIFY_SQL(q, exec("CREATE TABLE " + qTableName("qtest_longstr", __FILE__, db) + " (id int primary key, longstring ntext)"));
2418
2419 QVERIFY_SQL(q, prepare("INSERT INTO " + qTableName("qtest_longstr", __FILE__, db) + " VALUES (?, ?)"));
2420
2421 q.addBindValue( val: 0 );
2422
2423 q.addBindValue( val: QString::fromLatin1( str: "bubu" ) );
2424
2425 QVERIFY_SQL( q, exec() );
2426
2427 QString testStr;
2428
2429 testStr.fill( c: QLatin1Char( 'a' ), size: 85000 );
2430
2431 q.addBindValue( val: 1 );
2432
2433 q.addBindValue( val: testStr );
2434
2435 QVERIFY_SQL( q, exec() );
2436
2437 QVERIFY_SQL(q, exec("select * from " + qTableName( "qtest_longstr", __FILE__, db)));
2438
2439 QVERIFY_SQL( q, next() );
2440
2441 QCOMPARE( q.value( 0 ).toInt(), 0 );
2442
2443 QCOMPARE( q.value( 1 ).toString(), QString::fromLatin1( "bubu" ) );
2444
2445 QVERIFY_SQL( q, next() );
2446
2447 QCOMPARE( q.value( 0 ).toInt(), 1 );
2448
2449 QCOMPARE( q.value( 1 ).toString(), testStr );
2450}
2451
2452void tst_QSqlQuery::invalidQuery()
2453{
2454 QFETCH( QString, dbName );
2455 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2456 CHECK_DATABASE( db );
2457 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
2458 QSqlQuery q( db );
2459
2460 QVERIFY( !q.exec() );
2461
2462 QVERIFY( !q.exec( "blahfasel" ) );
2463 QVERIFY( q.lastError().type() != QSqlError::NoError );
2464 QVERIFY( !q.next() );
2465 QVERIFY( !q.isActive() );
2466
2467 if (dbType != QSqlDriver::Oracle && dbType != QSqlDriver::DB2 && !db.driverName().startsWith(s: "QODBC")) {
2468 // oracle and db2 just prepares everything without complaining
2469 if ( db.driver()->hasFeature( f: QSqlDriver::PreparedQueries ) )
2470 QVERIFY( !q.prepare( "blahfasel" ) );
2471 }
2472
2473 QVERIFY( !q.exec() );
2474
2475 QVERIFY( !q.isActive() );
2476 QVERIFY( !q.next() );
2477}
2478
2479void tst_QSqlQuery::batchExec()
2480{
2481 QFETCH( QString, dbName );
2482 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2483 CHECK_DATABASE( db );
2484
2485 QSqlQuery q( db );
2486 const QString tableName = qTableName(prefix: "qtest_batch", __FILE__, db);
2487 tst_Databases::safeDropTable(db, tableName);
2488 QVERIFY_SQL(q, exec(QStringLiteral("create table ") + tableName +
2489 QStringLiteral(" (id int, name varchar(20), dt date, num numeric(8, 4), "
2490 "dtstamp TIMESTAMP(3), extraId int, extraName varchar(20))")));
2491
2492 const QVariantList intCol = { 1, 2, QVariant(QVariant::Int) };
2493 const QVariantList charCol = { QStringLiteral("harald"), QStringLiteral("boris"),
2494 QVariant(QVariant::String) };
2495 const QDateTime currentDateTime = QDateTime(QDateTime::currentDateTime());
2496 const QVariantList dateCol = { currentDateTime.date(), currentDateTime.date().addDays(days: -1),
2497 QVariant(QVariant::Date) };
2498 const QVariantList numCol = { 2.3, 3.4, QVariant(QVariant::Double) };
2499 const QVariantList timeStampCol = { currentDateTime, currentDateTime.addDays(days: -1),
2500 QVariant(QVariant::DateTime) };
2501
2502 // Test with positional placeholders
2503 QVERIFY_SQL(q, prepare(QStringLiteral("insert into ") + tableName +
2504 QStringLiteral(" (id, name, dt, num, dtstamp, extraId, extraName) values "
2505 "(?, ?, ?, ?, ?, ?, ?)")));
2506 q.addBindValue(val: intCol);
2507 q.addBindValue( val: charCol );
2508 q.addBindValue( val: dateCol );
2509 q.addBindValue( val: numCol );
2510 q.addBindValue(val: timeStampCol);
2511 q.addBindValue(val: intCol);
2512 q.addBindValue(val: charCol);
2513
2514 QVERIFY_SQL( q, execBatch() );
2515 QVERIFY_SQL(q, exec(QStringLiteral("select id, name, dt, num, dtstamp, "
2516 "extraId, extraName from ") + tableName));
2517
2518 for (int i = 0; i < intCol.size(); ++i) {
2519 QVERIFY(q.next());
2520 QCOMPARE(q.value(0).toInt(), intCol.at(i));
2521 QCOMPARE(q.value(1).toString(), charCol.at(i));
2522 QCOMPARE(q.value(2).toDate(), dateCol.at(i));
2523 QCOMPARE(q.value(3).toDouble(), numCol.at(i));
2524 if (tst_Databases::getDatabaseType(db) == QSqlDriver::MySqlServer && timeStampCol.at(i).isNull()) {
2525 QEXPECT_FAIL("", "This appears to be a bug in MySQL as it converts null datetimes to the "
2526 "current datetime for a timestamp field", Continue);
2527 }
2528 QCOMPARE(q.value(4).toDateTime(), timeStampCol.at(i));
2529 QCOMPARE(q.value(5).toInt(), intCol.at(i));
2530 QCOMPARE(q.value(6).toString(), charCol.at(i));
2531 }
2532
2533 // Empty table ready for retesting with duplicated named placeholders
2534 QVERIFY_SQL(q, exec(QStringLiteral("delete from ") + tableName));
2535 QVERIFY_SQL(q, prepare(QStringLiteral("insert into ") + tableName +
2536 QStringLiteral(" (id, name, dt, num, dtstamp, extraId, extraName) "
2537 "values (:id, :name, :dt, :num, :dtstamp, :id, :name)")));
2538 q.bindValue(placeholder: ":id", val: intCol);
2539 q.bindValue(placeholder: ":name", val: charCol);
2540 q.bindValue(placeholder: ":dt", val: dateCol);
2541 q.bindValue(placeholder: ":num", val: numCol);
2542 q.bindValue(placeholder: ":dtstamp", val: timeStampCol);
2543
2544 QVERIFY_SQL(q, execBatch());
2545 QVERIFY_SQL(q, exec(QStringLiteral("select id, name, dt, num, dtstamp, extraId, extraName from ") +
2546 tableName));
2547
2548 for (int i = 0; i < intCol.size(); ++i) {
2549 QVERIFY(q.next());
2550 QCOMPARE(q.value(0).toInt(), intCol.at(i));
2551 QCOMPARE(q.value(1).toString(), charCol.at(i));
2552 QCOMPARE(q.value(2).toDate(), dateCol.at(i));
2553 QCOMPARE(q.value(3).toDouble(), numCol.at(i));
2554 if (tst_Databases::getDatabaseType(db) == QSqlDriver::MySqlServer && timeStampCol.at(i).isNull()) {
2555 QEXPECT_FAIL("", "This appears to be a bug in MySQL as it converts null datetimes to the "
2556 "current datetime for a timestamp field", Continue);
2557 }
2558 QCOMPARE(q.value(4).toDateTime(), timeStampCol.at(i));
2559 QCOMPARE(q.value(5).toInt(), intCol.at(i));
2560 QCOMPARE(q.value(6).toString(), charCol.at(i));
2561 }
2562
2563 // Only test the prepared stored procedure approach where the driver has support
2564 // for batch operations as this will not work without it
2565 if (db.driver()->hasFeature(f: QSqlDriver::BatchOperations)) {
2566 const QString procName = qTableName(prefix: "qtest_batch_proc", __FILE__, db);
2567 QVERIFY_SQL(q, exec("create or replace procedure " + procName +
2568 " (x in timestamp, y out timestamp) is\n"
2569 "begin\n"
2570 " y := x;\n"
2571 "end;\n"));
2572 QVERIFY(q.prepare("call " + procName + "(?, ?)"));
2573 q.addBindValue(val: timeStampCol, type: QSql::In);
2574 QVariantList emptyDateTimes;
2575 emptyDateTimes.reserve(alloc: timeStampCol.size());
2576 for (int i = 0; i < timeStampCol.size(); i++)
2577 emptyDateTimes << QVariant(QDateTime());
2578 q.addBindValue(val: emptyDateTimes, type: QSql::Out);
2579 QVERIFY_SQL(q, execBatch());
2580 QCOMPARE(q.boundValue(1).toList(), timeStampCol);
2581 }
2582}
2583
2584void tst_QSqlQuery::QTBUG_43874()
2585{
2586 QFETCH(QString, dbName);
2587 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
2588 CHECK_DATABASE(db);
2589
2590 QSqlQuery q(db);
2591 const QString tableName = qTableName(prefix: "bug43874", __FILE__, db);
2592
2593 QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id INT)"));
2594 QVERIFY_SQL(q, prepare("INSERT INTO " + tableName + " (id) VALUES (?)"));
2595
2596 for (int i = 0; i < 2; ++i) {
2597 QVariantList ids;
2598 ids << i;
2599 q.addBindValue(val: ids);
2600 QVERIFY_SQL(q, execBatch());
2601 }
2602 QVERIFY_SQL(q, exec("SELECT id FROM " + tableName + " ORDER BY id"));
2603
2604 QVERIFY(q.next());
2605 QCOMPARE(q.value(0).toInt(), 0);
2606
2607 QVERIFY(q.next());
2608 QCOMPARE(q.value(0).toInt(), 1);
2609}
2610
2611void tst_QSqlQuery::oraArrayBind()
2612{
2613 QFETCH( QString, dbName );
2614 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2615 CHECK_DATABASE( db );
2616
2617 if ( !db.driver()->hasFeature( f: QSqlDriver::BatchOperations ) )
2618 QSKIP( "Database can't do BatchOperations");
2619
2620 QSqlQuery q( db );
2621
2622 QVERIFY_SQL( q, exec( "CREATE OR REPLACE PACKAGE ora_array_test "
2623 "IS "
2624 "TYPE names_type IS TABLE OF VARCHAR(64) NOT NULL INDEX BY BINARY_INTEGER; "
2625 "names_tab names_type; "
2626 "PROCEDURE set_name(name_in IN VARCHAR2, row_in in INTEGER); "
2627 "PROCEDURE get_name(row_in IN INTEGER, str_out OUT VARCHAR2); "
2628 "PROCEDURE get_table(tbl OUT names_type); "
2629 "PROCEDURE set_table(tbl IN names_type); "
2630 "END ora_array_test; " ) );
2631
2632 QVERIFY_SQL( q, exec( "CREATE OR REPLACE PACKAGE BODY ora_array_test "
2633 "IS "
2634 "PROCEDURE set_name(name_in IN VARCHAR2, row_in in INTEGER) "
2635 "IS "
2636 "BEGIN "
2637 "names_tab(row_in) := name_in; "
2638 "END set_name; "
2639
2640 "PROCEDURE get_name(row_in IN INTEGER, str_out OUT VARCHAR2) "
2641 "IS "
2642 "BEGIN "
2643 "str_out := names_tab(row_in); "
2644 "END get_name; "
2645
2646 "PROCEDURE get_table(tbl OUT names_type) "
2647 "IS "
2648 "BEGIN "
2649 "tbl:=names_tab; "
2650 "END get_table; "
2651
2652 "PROCEDURE set_table(tbl IN names_type) "
2653 "IS "
2654 "BEGIN "
2655 "names_tab := tbl; "
2656 "END set_table; "
2657 "END ora_array_test; " ) );
2658
2659 QVariantList list;
2660
2661 list << QString( "lorem" ) << QString( "ipsum" ) << QString( "dolor" ) << QString( "sit" ) << QString( "amet" );
2662
2663 QVERIFY_SQL( q, prepare( "BEGIN "
2664 "ora_array_test.set_table(?); "
2665 "END;" ) );
2666
2667 q.bindValue( pos: 0, val: list, type: QSql::In );
2668
2669 QVERIFY_SQL( q, execBatch( QSqlQuery::ValuesAsColumns ) );
2670
2671 QVERIFY_SQL( q, prepare( "BEGIN "
2672 "ora_array_test.get_table(?); "
2673 "END;" ) );
2674
2675 list.clear();
2676
2677 list << QString( 64,' ' ) << QString( 64,' ' ) << QString( 64,' ' ) << QString( 64,' ' ) << QString( 64,' ' );
2678
2679 q.bindValue( pos: 0, val: list, type: QSql::Out );
2680
2681 QVERIFY_SQL( q, execBatch( QSqlQuery::ValuesAsColumns ) );
2682
2683 QVariantList out_list = q.boundValue( pos: 0 ).toList();
2684
2685 QCOMPARE( out_list.at( 0 ).toString(), QString( "lorem" ) );
2686
2687 QCOMPARE( out_list.at( 1 ).toString(), QString( "ipsum" ) );
2688
2689 QCOMPARE( out_list.at( 2 ).toString(), QString( "dolor" ) );
2690
2691 QCOMPARE( out_list.at( 3 ).toString(), QString( "sit" ) );
2692
2693 QCOMPARE( out_list.at( 4 ).toString(), QString( "amet" ) );
2694
2695 QVERIFY_SQL( q, exec( "DROP PACKAGE ora_array_test" ) );
2696}
2697
2698/*
2699 Tests that QSqlDatabase::record and QSqlQuery::record returns the same thing
2700 otherwise our models get confused.
2701 */
2702void tst_QSqlQuery::record_sqlite()
2703{
2704 QFETCH( QString, dbName );
2705 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2706 CHECK_DATABASE( db );
2707
2708 QSqlQuery q( db );
2709
2710 QVERIFY_SQL(q, exec("create table " + qTableName("record_sqlite", __FILE__, db) + "(id integer primary key, name varchar, title int)"));
2711
2712 QSqlRecord rec = db.record(tablename: qTableName(prefix: "record_sqlite", __FILE__, db));
2713
2714 QCOMPARE( rec.count(), 3 );
2715 QCOMPARE( rec.field( 0 ).type(), QVariant::Int );
2716 QCOMPARE( rec.field( 1 ).type(), QVariant::String );
2717 QCOMPARE( rec.field( 2 ).type(), QVariant::Int );
2718
2719 /* important - select from an empty table */
2720 QVERIFY_SQL(q, exec("select id, name, title from " + qTableName("record_sqlite", __FILE__, db)));
2721
2722 rec = q.record();
2723 QCOMPARE( rec.count(), 3 );
2724 QCOMPARE( rec.field( 0 ).type(), QVariant::Int );
2725 QCOMPARE( rec.field( 1 ).type(), QVariant::String );
2726 QCOMPARE( rec.field( 2 ).type(), QVariant::Int );
2727}
2728
2729void tst_QSqlQuery::oraLong()
2730{
2731 QFETCH( QString, dbName );
2732 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2733 CHECK_DATABASE( db );
2734
2735 QSqlQuery q( db );
2736
2737 QString aLotOfText( 127000, QLatin1Char( 'H' ) );
2738
2739 QVERIFY_SQL(q, exec("create table " + qTableName("qtest_longstr", __FILE__, db) + " (id int primary key, astr long)"));
2740 QVERIFY_SQL(q, prepare("insert into " + qTableName("qtest_longstr", __FILE__, db) + " (id, astr) values (?, ?)"));
2741 q.addBindValue( val: 1 );
2742 q.addBindValue( val: aLotOfText );
2743 QVERIFY_SQL( q, exec() );
2744
2745 QVERIFY_SQL(q, exec("select id,astr from " + qTableName("qtest_longstr", __FILE__, db)));
2746
2747 QVERIFY( q.next() );
2748 QCOMPARE( q.value( 0 ).toInt(), 1 );
2749 QCOMPARE( q.value( 1 ).toString(), aLotOfText );
2750}
2751
2752void tst_QSqlQuery::execErrorRecovery()
2753{
2754 QFETCH( QString, dbName );
2755 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2756 CHECK_DATABASE( db );
2757
2758 QSqlQuery q( db );
2759
2760 const QString tbl = qTableName(prefix: "qtest_exerr", __FILE__, db);
2761 q.exec(query: "drop table " + tbl);
2762 QVERIFY_SQL(q, exec("create table " + tbl + " (id int not null primary key)"));
2763 QVERIFY_SQL(q, prepare("insert into " + tbl + " values (?)" ));
2764
2765 q.addBindValue( val: 1 );
2766 QVERIFY_SQL( q, exec() );
2767
2768 q.addBindValue( val: 1 ); // binding the same pkey - should fail
2769 QVERIFY( !q.exec() );
2770
2771 q.addBindValue( val: 2 ); // this should work again
2772 QVERIFY_SQL( q, exec() );
2773}
2774
2775void tst_QSqlQuery::prematureExec()
2776{
2777 QFETCH(QString, dbName);
2778 // We only want the engine name, for addDatabase():
2779 int cut = dbName.indexOf(c: QChar('@'));
2780 if (cut < 0)
2781 QSKIP("Failed to parse database type out of name");
2782 dbName.truncate(pos: cut);
2783 cut = dbName.indexOf(c: QChar('_'));
2784 if (cut >= 0)
2785 dbName = dbName.mid(position: cut + 1);
2786
2787 auto db = QSqlDatabase::addDatabase(type: dbName);
2788 QSqlQuery q(db);
2789
2790 QTest::ignoreMessage(type: QtWarningMsg,
2791 message: "QSqlDatabasePrivate::removeDatabase: connection "
2792 "'qt_sql_default_connection' is still in use, all "
2793 "queries will cease to work.");
2794 QTest::ignoreMessage(type: QtWarningMsg,
2795 message: "QSqlDatabasePrivate::addDatabase: duplicate connection name "
2796 "'qt_sql_default_connection', old connection removed.");
2797 auto otherDb = QSqlDatabase::addDatabase(type: dbName);
2798
2799 QTest::ignoreMessage(type: QtWarningMsg, message: "QSqlQuery::exec: called before driver has been set up");
2800 // QTBUG-100037: shouldn't crash !
2801 QVERIFY(!q.exec("select stuff from TheVoid"));
2802}
2803
2804void tst_QSqlQuery::lastInsertId()
2805{
2806 QFETCH( QString, dbName );
2807 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2808 CHECK_DATABASE( db );
2809
2810 if ( !db.driver()->hasFeature( f: QSqlDriver::LastInsertId ) )
2811 QSKIP( "Database doesn't support lastInsertId");
2812
2813 QSqlQuery q( db );
2814
2815 // PostgreSQL >= 8.1 relies on lastval() which does not work if a value is
2816 // manually inserted to the serial field, so we create a table specifically
2817 if (tst_Databases::getDatabaseType(db) == QSqlDriver::PostgreSQL) {
2818 const auto tst_lastInsertId = qTableName(prefix: "tst_lastInsertId", __FILE__, db);
2819 tst_Databases::safeDropTable(db, tableName: tst_lastInsertId);
2820 QVERIFY_SQL(q, exec(QStringLiteral("create table ") + tst_lastInsertId +
2821 QStringLiteral(" (id serial not null, t_varchar "
2822 "varchar(20), t_char char(20), primary key(id))")));
2823 QVERIFY_SQL(q, exec(QStringLiteral("insert into ") + tst_lastInsertId +
2824 QStringLiteral(" (t_varchar, t_char) values "
2825 "('VarChar41', 'Char41')")));
2826 } else {
2827 QVERIFY_SQL(q, exec(QStringLiteral("insert into ") + qtest +
2828 QStringLiteral(" values (41, 'VarChar41', 'Char41')")));
2829 }
2830 QVariant v = q.lastInsertId();
2831
2832 QVERIFY( v.isValid() );
2833}
2834
2835void tst_QSqlQuery::lastQuery()
2836{
2837 QFETCH( QString, dbName );
2838 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2839 CHECK_DATABASE( db );
2840
2841 QSqlQuery q( db );
2842 QString sql = "select * from " + qtest;
2843 QVERIFY_SQL( q, exec( sql ) );
2844 QCOMPARE( q.lastQuery(), sql );
2845 QCOMPARE( q.executedQuery(), sql );
2846}
2847
2848void tst_QSqlQuery::lastQueryTwoQueries()
2849{
2850 QFETCH(QString, dbName);
2851 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
2852 CHECK_DATABASE(db);
2853
2854 QSqlQuery q(db);
2855
2856 QString sql = QLatin1String("select * from ") + qtest;
2857 QVERIFY_SQL(q, exec(sql));
2858 QCOMPARE(q.lastQuery(), sql);
2859 QCOMPARE(q.executedQuery(), sql);
2860
2861 sql = QLatin1String("select id from ") + qtest;
2862 QVERIFY_SQL(q, exec(sql));
2863 QCOMPARE(q.lastQuery(), sql);
2864 QCOMPARE(q.executedQuery(), sql);
2865}
2866
2867void tst_QSqlQuery::psql_bindWithDoubleColonCastOperator()
2868{
2869 QFETCH( QString, dbName );
2870 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2871 CHECK_DATABASE( db );
2872
2873 const QString tablename(qTableName(prefix: "bindtest", __FILE__, db));
2874
2875 QSqlQuery q( db );
2876
2877 QVERIFY_SQL( q, exec( "create table " + tablename + " (id1 int, id2 int, id3 int, fld1 int, fld2 int)" ) );
2878 QVERIFY_SQL( q, exec( "insert into " + tablename + " values (1, 2, 3, 10, 5)" ) );
2879
2880 QVERIFY_SQL( q, prepare( "select sum((fld1 - fld2)::int) from " + tablename + " where id1 = :myid1 and id2 =:myid2 and id3=:myid3" ) );
2881 q.bindValue( placeholder: ":myid1", val: 1 );
2882 q.bindValue( placeholder: ":myid2", val: 2 );
2883 q.bindValue( placeholder: ":myid3", val: 3 );
2884
2885 QVERIFY_SQL( q, exec() );
2886 QVERIFY_SQL( q, next() );
2887
2888 // the positional placeholders are converted to named placeholders in executedQuery()
2889 if (db.driver()->hasFeature(f: QSqlDriver::PreparedQueries))
2890 QCOMPARE(q.executedQuery(), QString("select sum((fld1 - fld2)::int) from " + tablename + " where id1 = :myid1 and id2 =:myid2 and id3=:myid3"));
2891 else
2892 QCOMPARE(q.executedQuery(), QString("select sum((fld1 - fld2)::int) from " + tablename + " where id1 = 1 and id2 =2 and id3=3"));
2893}
2894
2895void tst_QSqlQuery::psql_specialFloatValues()
2896{
2897 if (!std::numeric_limits<float>::has_quiet_NaN)
2898 QSKIP("Platform does not have quiet_NaN");
2899 if (!std::numeric_limits<float>::has_infinity)
2900 QSKIP("Platform does not have infinity");
2901
2902 QFETCH( QString, dbName );
2903 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2904
2905 CHECK_DATABASE( db );
2906 QSqlQuery query(db);
2907 const QString tableName = qTableName(prefix: "floattest", __FILE__, db);
2908 QVERIFY_SQL( query, exec("create table " + tableName + " (value float)" ) );
2909 QVERIFY_SQL(query, prepare("insert into " + tableName + " values(:value)") );
2910
2911 QVariantList data;
2912 data << QVariant(double(42.42))
2913 << QVariant(std::numeric_limits<double>::quiet_NaN())
2914 << QVariant(std::numeric_limits<double>::infinity())
2915 << QVariant(float(42.42))
2916 << QVariant(std::numeric_limits<float>::quiet_NaN())
2917 << QVariant(std::numeric_limits<float>::infinity());
2918
2919 foreach (const QVariant &v, data) {
2920 query.bindValue(placeholder: ":value", val: v);
2921 QVERIFY_SQL( query, exec() );
2922 }
2923
2924 QVERIFY_SQL( query, exec("drop table " + tableName) );
2925}
2926
2927/* For task 157397: Using QSqlQuery with an invalid QSqlDatabase
2928 does not set the last error of the query.
2929 This test function will output some warnings, that's ok.
2930*/
2931void tst_QSqlQuery::queryOnInvalidDatabase()
2932{
2933 {
2934 QTest::ignoreMessage( type: QtWarningMsg, message: "QSqlDatabase: INVALID driver not loaded" );
2935 QSqlDatabase db = QSqlDatabase::addDatabase( type: "INVALID", connectionName: "invalidConnection" );
2936 QVERIFY2( db.lastError().isValid(),
2937 qPrintable( QString( "db.lastError().isValid() should be true!" ) ) );
2938
2939 QTest::ignoreMessage( type: QtWarningMsg, message: "QSqlQuery::exec: database not open" );
2940 QSqlQuery query( "SELECT 1 AS ID", db );
2941 QVERIFY2( query.lastError().isValid(),
2942 qPrintable( QString( "query.lastError().isValid() should be true!" ) ) );
2943 }
2944
2945 QSqlDatabase::removeDatabase( connectionName: "invalidConnection" );
2946
2947 {
2948 QSqlDatabase db = QSqlDatabase::database( connectionName: "this connection does not exist" );
2949 QTest::ignoreMessage( type: QtWarningMsg, message: "QSqlQuery::exec: database not open" );
2950 QSqlQuery query( "SELECT 1 AS ID", db );
2951 QVERIFY2( query.lastError().isValid(),
2952 qPrintable( QString( "query.lastError().isValid() should be true!" ) ) );
2953 }
2954}
2955
2956/* For task 159138: Error on instantiating a sql-query before explicitly
2957 opening the database. This is something we don't support, so this isn't
2958 really a bug. However some of the drivers are nice enough to support it.
2959*/
2960void tst_QSqlQuery::createQueryOnClosedDatabase()
2961{
2962 QFETCH( QString, dbName );
2963 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2964 CHECK_DATABASE( db );
2965 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
2966 // Only supported by these drivers
2967
2968 if (dbType != QSqlDriver::PostgreSQL && dbType != QSqlDriver::Oracle
2969 && dbType != QSqlDriver::MySqlServer && dbType != QSqlDriver::DB2)
2970 QSKIP( "Test is specific for PostgreSQL, Oracle, MySql and DB2");
2971
2972 db.close();
2973
2974 QSqlQuery q( db );
2975 db.open();
2976 QVERIFY_SQL( q, exec( QString( "select * from %1 where id = 1" ).arg( qtest ) ) );
2977
2978 QVERIFY_SQL( q, next() );
2979 QCOMPARE( q.value( 0 ).toInt(), 1 );
2980 QCOMPARE( q.value( 1 ).toString().trimmed(), QLatin1String( "VarChar1" ) );
2981 QCOMPARE( q.value( 2 ).toString().trimmed(), QLatin1String( "Char1" ) );
2982
2983 db.close();
2984 QVERIFY2( !q.exec( QString( "select * from %1 where id = 1" ).arg( qtest ) ),
2985 qPrintable( QString( "This can't happen! The query should not have been executed!" ) ) );
2986}
2987
2988void tst_QSqlQuery::reExecutePreparedForwardOnlyQuery()
2989{
2990 QFETCH( QString, dbName );
2991 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
2992 CHECK_DATABASE( db );
2993
2994 QSqlQuery q( db );
2995 q.setForwardOnly( true );
2996
2997 QVERIFY_SQL( q, prepare( QString( "SELECT id, t_varchar, t_char FROM %1 WHERE id = :id" ).arg( qtest ) ) );
2998 q.bindValue( placeholder: ":id", val: 1 );
2999 QVERIFY_SQL( q, exec() );
3000
3001 // Do something, like iterate over the result, or skip to the end
3002 QVERIFY_SQL( q, last() );
3003
3004 QVERIFY_SQL( q, exec() );
3005 /* This was broken with SQLite because the cache size was set to 0 in the 2nd execute.
3006 When forwardOnly is set we don't cahce the entire result, but we do cache the current row
3007 but this requires the cache size to be equal to the column count.
3008 */
3009 QVERIFY_SQL( q, next() );
3010 QCOMPARE( q.value( 0 ).toInt(), 1 );
3011 QCOMPARE( q.value( 1 ).toString().trimmed(), QString( "VarChar1" ) );
3012 QCOMPARE( q.value( 2 ).toString().trimmed(), QString( "Char1" ) );
3013}
3014
3015void tst_QSqlQuery::finish()
3016{
3017 QFETCH( QString, dbName );
3018 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3019 CHECK_DATABASE( db );
3020
3021 QSqlQuery q( db );
3022 QVERIFY_SQL( q, prepare( "SELECT id FROM " + qtest + " WHERE id = ?" ) );
3023
3024 int id = 4;
3025 q.bindValue( pos: 0, val: id );
3026 QVERIFY_SQL( q, exec() );
3027 QVERIFY( q.isActive() );
3028 QVERIFY_SQL( q, next() );
3029 QCOMPARE( q.value( 0 ).toInt(), id );
3030
3031 q.finish();
3032 QVERIFY( !q.isActive() ); // query is now inactive
3033 QCOMPARE( q.boundValue( 0 ).toInt(), id ); // bound values are retained
3034
3035 QVERIFY_SQL( q, exec() ); // no prepare necessary
3036 QVERIFY( q.isActive() );
3037 QVERIFY_SQL( q, next() );
3038 QCOMPARE( q.value( 0 ).toInt(), id );
3039
3040 q.finish();
3041 QVERIFY( !q.isActive() );
3042
3043 QVERIFY_SQL( q, exec( "SELECT id FROM " + qtest + " WHERE id = 1" ) );
3044 QVERIFY( q.isActive() );
3045 QVERIFY_SQL( q, next() );
3046 QCOMPARE( q.value( 0 ).toInt(), 1 );
3047 QCOMPARE( q.record().count(), 1 );
3048}
3049
3050void tst_QSqlQuery::sqlite_finish()
3051{
3052 QFETCH( QString, dbName );
3053 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3054 CHECK_DATABASE( db );
3055
3056 if ( db.databaseName().startsWith( c: ':' ) )
3057 QSKIP( "This test requires a database on the filesystem, not in-memory");
3058
3059 {
3060 QSqlDatabase db2 = QSqlDatabase::addDatabase( type: "QSQLITE", connectionName: "sqlite_finish_sqlite" );
3061 db2.setDatabaseName( db.databaseName() );
3062 QVERIFY_SQL( db2, open() );
3063
3064 const QString tableName(qTableName(prefix: "qtest_lockedtable", __FILE__, db));
3065 QSqlQuery q( db );
3066
3067 tst_Databases::safeDropTable( db, tableName );
3068 q.exec( query: "CREATE TABLE " + tableName + " (pk_id INTEGER PRIMARY KEY, whatever TEXT)" );
3069 q.exec( query: "INSERT INTO " + tableName + " values(1, 'whatever')" );
3070 q.exec( query: "INSERT INTO " + tableName + " values(2, 'whatever more')" );
3071
3072 // This creates a read-lock in the database
3073 QVERIFY_SQL( q, exec( "SELECT * FROM " + tableName + " WHERE pk_id = 1 or pk_id = 2" ) );
3074 QVERIFY_SQL( q, next() );
3075
3076 // The DELETE will fail because of the read-lock
3077 QSqlQuery q2( db2 );
3078 QVERIFY( !q2.exec( "DELETE FROM " + tableName + " WHERE pk_id=2" ) );
3079 QCOMPARE( q2.numRowsAffected(), -1 );
3080
3081 // The DELETE will succeed now because finish() removes the lock
3082 q.finish();
3083 QVERIFY_SQL( q2, exec( "DELETE FROM " + tableName + " WHERE pk_id=2" ) );
3084 QCOMPARE( q2.numRowsAffected(), 1 );
3085
3086 tst_Databases::safeDropTable( db, tableName );
3087 }
3088
3089 QSqlDatabase::removeDatabase( connectionName: "sqlite_finish_sqlite" );
3090}
3091
3092void tst_QSqlQuery::nextResult()
3093{
3094 QFETCH( QString, dbName );
3095 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3096 CHECK_DATABASE( db );
3097 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
3098 if (!db.driver()->hasFeature(f: QSqlDriver::MultipleResultSets))
3099 QSKIP("DBMS does not support multiple result sets");
3100
3101 QSqlQuery q( db );
3102 const QString tableName(qTableName(prefix: "more_results", __FILE__, db));
3103
3104 QVERIFY_SQL( q, exec( "CREATE TABLE " + tableName + " (id integer, text varchar(20), num numeric(6, 3), empty varchar(10));" ) );
3105
3106 QVERIFY_SQL( q, exec( "INSERT INTO " + tableName + " VALUES(1, 'one', 1.1, '');" ) );
3107
3108 QVERIFY_SQL( q, exec( "INSERT INTO " + tableName + " VALUES(2, 'two', 2.2, '');" ) );
3109
3110 QVERIFY_SQL( q, exec( "INSERT INTO " + tableName + " VALUES(3, 'three', 3.3, '');" ) );
3111
3112 QVERIFY_SQL( q, exec( "INSERT INTO " + tableName + " VALUES(4, 'four', 4.4, '');" ) );
3113
3114 QStringList tstStrings;
3115
3116 tstStrings << "one" << "two" << "three" << "four";
3117
3118 // Query that returns only one result set, nothing special about this
3119 QVERIFY_SQL( q, exec( QString( "SELECT * FROM %1;" ).arg( tableName ) ) );
3120
3121 QVERIFY( q.next() ); // Move to first row of the result set
3122
3123 QVERIFY( !q.nextResult() ); // No more result sets are available
3124
3125 QVERIFY( !q.isActive() ); // So the query is no longer active
3126
3127 QVERIFY( !q.next() ); // ... and no data is available as the call
3128
3129 // to nextResult() discarded the result set
3130
3131 // Query that returns two result sets (batch sql)
3132 // When working with multiple result sets SQL Server insists on non-scrollable cursors
3133 if (db.driverName().startsWith(s: "QODBC"))
3134 q.setForwardOnly( true );
3135
3136 QVERIFY_SQL( q, exec( "SELECT id FROM " + tableName + "; SELECT text, num FROM " + tableName + ';' ) );
3137
3138 QCOMPARE( q.record().count(), 1 ); // Check that the meta data is as expected
3139
3140 QCOMPARE( q.record().field( 0 ).name().toUpper(), QString( "ID" ) );
3141
3142 QCOMPARE( q.record().field( 0 ).type(), QVariant::Int );
3143
3144 QVERIFY( q.nextResult() ); // Discards first result set and move to the next
3145
3146 QCOMPARE( q.record().count(), 2 ); // New meta data should be available
3147
3148 QCOMPARE( q.record().field( 0 ).name().toUpper(), QString( "TEXT" ) );
3149
3150 QCOMPARE( q.record().field( 0 ).type(), QVariant::String );
3151
3152 QCOMPARE( q.record().field( 1 ).name().toUpper(), QString( "NUM" ) );
3153 QCOMPARE(q.record().field(1).type(), QVariant::Double);
3154
3155 QVERIFY( q.next() ); // Move to first row of the second result set
3156
3157 QFAIL_SQL(q, nextResult()); // No more result sets after this
3158
3159 QVERIFY( !q.isActive() ); // So the query is no longer active
3160
3161 QVERIFY( !q.next() ); // ... and no data is available as the call to
3162
3163 // nextResult() discarded the result set
3164
3165 // Query that returns one result set, a count of affected rows and then another result set
3166 QString query1 = QString( "SELECT id, text, num, empty FROM %1 WHERE id <= 3" ).arg( a: tableName );
3167
3168 QString query2 = QString( "UPDATE %1 SET empty = 'Yatta!'" ).arg( a: tableName );
3169
3170 QString query3 = QString( "SELECT id, empty FROM %1 WHERE id <=2" ).arg( a: tableName );
3171
3172 QVERIFY_SQL( q, exec( QString( "%1; %2; %3;" ).arg( query1 ).arg( query2 ).arg( query3 ) ) );
3173
3174 // Check result set returned by first statement
3175 QVERIFY( q.isSelect() ); // The first statement is a select
3176
3177 for ( int i = 0; i < 3; i++ ) {
3178 QVERIFY_SQL( q, next() );
3179 QCOMPARE( q.value( 0 ).toInt(), 1+i );
3180 QCOMPARE( q.value( 1 ).toString(), tstStrings.at( i ) );
3181 QCOMPARE( q.value( 2 ).toDouble(), 1.1*( i+1 ) );
3182 QVERIFY( q.value( 3 ).toString().isEmpty() );
3183 }
3184
3185 QVERIFY_SQL( q, nextResult() );
3186
3187 QVERIFY( !q.isSelect() ); // The second statement isn't a SELECT
3188 QVERIFY( !q.next() ); // ... so no result set is available
3189 QCOMPARE( q.numRowsAffected(), 4 ); // 4 rows was affected by the UPDATE
3190
3191 // Check result set returned by third statement
3192 QVERIFY_SQL( q, nextResult() );
3193 QVERIFY( q.isSelect() ); // The third statement is a SELECT
3194
3195 for ( int i = 0; i < 2; i++ ) {
3196 QVERIFY_SQL( q, next() );
3197 QCOMPARE( q.value( 0 ).toInt(), 1+i );
3198 QCOMPARE( q.value( 1 ).toString(), QString( "Yatta!" ) );
3199 }
3200
3201 // Stored procedure with multiple result sets
3202 const QString procName(qTableName(prefix: "proc_more_res", __FILE__, db));
3203
3204 if (dbType == QSqlDriver::PostgreSQL)
3205 q.exec(query: QString("DROP FUNCTION %1(refcursor, refcursor);").arg(a: procName));
3206 else
3207 q.exec(query: QString("DROP PROCEDURE %1;").arg(a: procName));
3208
3209 if (dbType == QSqlDriver::MySqlServer)
3210 QVERIFY_SQL( q, exec( QString( "CREATE PROCEDURE %1()"
3211 "\nBEGIN"
3212 "\nSELECT id, text FROM %2;"
3213 "\nSELECT empty, num, text, id FROM %3;"
3214 "\nEND" ).arg( procName ).arg( tableName ).arg( tableName ) ) );
3215 else if (dbType == QSqlDriver::DB2)
3216 QVERIFY_SQL( q, exec( QString( "CREATE PROCEDURE %1()"
3217 "\nRESULT SETS 2"
3218 "\nLANGUAGE SQL"
3219 "\np1:BEGIN"
3220 "\nDECLARE cursor1 CURSOR WITH RETURN FOR SELECT id, text FROM %2;"
3221 "\nDECLARE cursor2 CURSOR WITH RETURN FOR SELECT empty, num, text, id FROM %3;"
3222 "\nOPEN cursor1;"
3223 "\nOPEN cursor2;"
3224 "\nEND p1" ).arg( procName ).arg( tableName ).arg( tableName ) ) );
3225 else if (dbType == QSqlDriver::PostgreSQL)
3226 QVERIFY_SQL(q, exec(QString("CREATE FUNCTION %1(ref1 refcursor, ref2 refcursor)"
3227 "\nRETURNS SETOF refcursor AS $$"
3228 "\nBEGIN"
3229 "\nOPEN ref1 FOR SELECT id, text FROM %2;"
3230 "\nRETURN NEXT ref1;"
3231 "\nOPEN ref2 FOR SELECT empty, num, text, id FROM %2;"
3232 "\nRETURN NEXT ref2;"
3233 "\nEND;"
3234 "\n$$ LANGUAGE plpgsql").arg(procName).arg(tableName)));
3235 else
3236 QVERIFY_SQL( q, exec( QString( "CREATE PROCEDURE %1"
3237 "\nAS"
3238 "\nSELECT id, text FROM %2"
3239 "\nSELECT empty, num, text, id FROM %3" ).arg( procName ).arg( tableName ).arg( tableName ) ) );
3240
3241 if (dbType == QSqlDriver::MySqlServer || dbType == QSqlDriver::DB2) {
3242 q.setForwardOnly( true );
3243 QVERIFY_SQL( q, exec( QString( "CALL %1()" ).arg( procName ) ) );
3244 } else if (dbType == QSqlDriver::PostgreSQL) {
3245 // Returning multiple result sets from PostgreSQL stored procedure:
3246 // http://sqlines.com/postgresql/how-to/return_result_set_from_stored_procedure
3247 QVERIFY_SQL(q, exec(QString("BEGIN;"
3248 "SELECT %1('cur1', 'cur2');"
3249 "FETCH ALL IN cur1;"
3250 "FETCH ALL IN cur2;"
3251 "COMMIT;").arg(procName)));
3252 } else {
3253 QVERIFY_SQL( q, exec( QString( "EXEC %1" ).arg( procName ) ) );
3254 }
3255
3256 if (dbType == QSqlDriver::PostgreSQL) {
3257 // First result set - start of transaction
3258 QVERIFY(!q.isSelect());
3259 QCOMPARE(q.numRowsAffected(), 0);
3260 QVERIFY(q.nextResult());
3261 // Second result set contains cursor names
3262 QVERIFY(q.isSelect());
3263 QVERIFY(q.next());
3264 QCOMPARE(q.value(0).toString(), "cur1");
3265 QVERIFY(q.next());
3266 QCOMPARE(q.value(0).toString(), "cur2");
3267 QVERIFY(q.nextResult());
3268 }
3269
3270 for ( int i = 0; i < 4; i++ ) {
3271 QVERIFY_SQL( q, next() );
3272 QCOMPARE( q.value( 0 ).toInt(), i+1 );
3273 QCOMPARE( q.value( 1 ).toString(), tstStrings.at( i ) );
3274 }
3275
3276 QVERIFY_SQL( q, nextResult() );
3277
3278 QVERIFY_SQL( q, isActive() );
3279
3280 for ( int i = 0; i < 4; i++ ) {
3281 QVERIFY_SQL( q, next() );
3282 QCOMPARE( q.value( 0 ).toString(), QString( "Yatta!" ) );
3283 QCOMPARE( q.value( 1 ).toDouble(), 1.1*( 1+i ) );
3284 QCOMPARE( q.value( 2 ).toString(), tstStrings.at( i ) );
3285 QCOMPARE( q.value( 3 ).toInt(), 1+i );
3286 }
3287
3288 // MySQL also counts the CALL itself as a result
3289 if (dbType == QSqlDriver::MySqlServer) {
3290 QVERIFY( q.nextResult() );
3291 QVERIFY( !q.isSelect() ); // ... but it's not a select
3292 QCOMPARE( q.numRowsAffected(), 0 ); // ... and no rows are affected (at least not with this procedure)
3293 }
3294 if (dbType == QSqlDriver::PostgreSQL) {
3295 // Last result set - commit transaction
3296 QVERIFY(q.nextResult());
3297 QVERIFY(!q.isSelect());
3298 QCOMPARE(q.numRowsAffected(), 0);
3299 }
3300
3301 QVERIFY( !q.nextResult() );
3302
3303 QVERIFY( !q.isActive() );
3304
3305 if (dbType == QSqlDriver::PostgreSQL)
3306 q.exec(query: QString("DROP FUNCTION %1(refcursor, refcursor);").arg(a: procName));
3307 else
3308 q.exec(query: QString("DROP PROCEDURE %1;").arg(a: procName));
3309}
3310
3311
3312// For task 190311. Problem: Truncation happens on the 2nd execution if that BLOB is larger
3313// than the BLOB on the 1st execution. This was only for MySQL, but the test is general
3314// enough to be run with all backends.
3315void tst_QSqlQuery::blobsPreparedQuery()
3316{
3317 QFETCH( QString, dbName );
3318 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3319 CHECK_DATABASE( db );
3320
3321 if ( !db.driver()->hasFeature( f: QSqlDriver::BLOB ) || !db.driver()->hasFeature( f: QSqlDriver::PreparedQueries ) )
3322 QSKIP( "DBMS does not support BLOBs or prepared queries");
3323
3324 const QString tableName(qTableName(prefix: "blobstest", __FILE__, db));
3325
3326 QSqlQuery q( db );
3327 q.setForwardOnly( true ); // This is needed to make the test work with DB2.
3328 QString shortBLOB( "abc" );
3329 QString longerBLOB( "abcdefghijklmnopqrstuvxyz¿äëïöü¡ " );
3330
3331 // In PostgreSQL a BLOB is not called a BLOB, but a BYTEA! :-)
3332 // ... and in SQL Server it can be called a lot, but IMAGE will do.
3333 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
3334 QString typeName( "BLOB" );
3335 if (dbType == QSqlDriver::PostgreSQL)
3336 typeName = "BYTEA";
3337 else if (dbType == QSqlDriver::MSSqlServer)
3338 typeName = "IMAGE";
3339
3340 QVERIFY_SQL( q, exec( QString( "CREATE TABLE %1(id INTEGER, data %2)" ).arg( tableName ).arg( typeName ) ) );
3341 q.prepare( query: QString( "INSERT INTO %1(id, data) VALUES(:id, :data)" ).arg( a: tableName ) );
3342 q.bindValue( placeholder: ":id", val: 1 );
3343 q.bindValue( placeholder: ":data", val: shortBLOB );
3344 QVERIFY_SQL( q, exec() );
3345
3346 q.bindValue( placeholder: ":id", val: 2 );
3347 q.bindValue( placeholder: ":data", val: longerBLOB );
3348 QVERIFY_SQL( q, exec() );
3349
3350 // Two executions and result sets
3351 q.prepare( query: QString( "SELECT data FROM %1 WHERE id = ?" ).arg( a: tableName ) );
3352 q.bindValue( pos: 0, val: QVariant( 1 ) );
3353 QVERIFY_SQL( q, exec() );
3354 QVERIFY_SQL( q, next() );
3355 QCOMPARE( q.value( 0 ).toString(), shortBLOB );
3356
3357 q.bindValue( pos: 0, val: QVariant( 2 ) );
3358 QVERIFY_SQL( q, exec() );
3359 QVERIFY_SQL( q, next() );
3360 QCOMPARE( q.value( 0 ).toString().toUtf8(), longerBLOB.toUtf8() );
3361
3362 // Only one execution and result set
3363 q.prepare( query: QString( "SELECT id, data FROM %1 ORDER BY id" ).arg( a: tableName ) );
3364 QVERIFY_SQL( q, exec() );
3365 QVERIFY_SQL( q, next() );
3366 QCOMPARE( q.value( 1 ).toString(), shortBLOB );
3367 QVERIFY_SQL( q, next() );
3368 QCOMPARE( q.value( 1 ).toString(), longerBLOB );
3369}
3370
3371// There were problems with navigating past the end of a table returning an error on mysql
3372void tst_QSqlQuery::emptyTableNavigate()
3373{
3374 QFETCH( QString, dbName );
3375 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3376 CHECK_DATABASE( db );
3377
3378 {
3379 QSqlQuery q( db );
3380 const QString tbl = qTableName(prefix: "qtest_empty", __FILE__, db);
3381 q.exec(query: "drop table " + tbl);
3382 QVERIFY_SQL(q, exec("create table " + tbl + " (id char(10))"));
3383 QVERIFY_SQL(q, prepare("select * from " + tbl));
3384 QVERIFY_SQL( q, exec() );
3385 QVERIFY( !q.next() );
3386 QCOMPARE( q.lastError().isValid(), false );
3387 }
3388}
3389
3390void tst_QSqlQuery::timeStampParsing()
3391{
3392 QFETCH(QString, dbName);
3393 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3394 CHECK_DATABASE(db);
3395 const QString tableName(qTableName(prefix: "timeStampParsing", __FILE__, db));
3396 tst_Databases::safeDropTable(db, tableName);
3397 QSqlQuery q(db);
3398 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
3399 if (dbType == QSqlDriver::PostgreSQL) {
3400 QVERIFY_SQL(q, exec(QStringLiteral("CREATE TABLE ") + tableName + QStringLiteral("("
3401 "id serial NOT NULL, "
3402 "datefield timestamp, primary key(id));")));
3403 } else if (dbType == QSqlDriver::MySqlServer) {
3404 QVERIFY_SQL(q, exec(QStringLiteral("CREATE TABLE ") + tableName + QStringLiteral("("
3405 "id integer NOT NULL AUTO_INCREMENT,"
3406 "datefield timestamp, primary key(id));")));
3407 } else {
3408 QVERIFY_SQL(q, exec(QStringLiteral("CREATE TABLE ") + tableName + QStringLiteral("("
3409 "\"id\" integer NOT NULL PRIMARY KEY AUTOINCREMENT,"
3410 "\"datefield\" timestamp);")));
3411 }
3412 QVERIFY_SQL(q, exec(
3413 QStringLiteral("INSERT INTO ") + tableName + QStringLiteral(" (datefield) VALUES (current_timestamp);"
3414 )));
3415 QVERIFY_SQL(q, exec(QStringLiteral("SELECT * FROM ") + tableName));
3416 while (q.next())
3417 QVERIFY(q.value(1).toDateTime().isValid());
3418}
3419
3420void tst_QSqlQuery::task_217003()
3421{
3422 QFETCH( QString, dbName );
3423 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3424 CHECK_DATABASE( db );
3425 QSqlQuery q( db );
3426 const QString Planet(qTableName( prefix: "Planet", __FILE__, db));
3427
3428 q.exec(query: "drop table " + Planet);
3429 QVERIFY_SQL( q, exec( "create table " + Planet + " (Name varchar(20))" ) );
3430 QVERIFY_SQL( q, exec( "insert into " + Planet + " VALUES ('Mercury')" ) );
3431 QVERIFY_SQL( q, exec( "insert into " + Planet + " VALUES ('Venus')" ) );
3432 QVERIFY_SQL( q, exec( "insert into " + Planet + " VALUES ('Earth')" ) );
3433 QVERIFY_SQL( q, exec( "insert into " + Planet + " VALUES ('Mars')" ) );
3434
3435 QVERIFY_SQL( q, exec( "SELECT Name FROM " + Planet ) );
3436 QVERIFY_SQL( q, seek( 3 ) );
3437 QCOMPARE( q.value( 0 ).toString(), QString( "Mars" ) );
3438 QVERIFY_SQL( q, seek( 1 ) );
3439 QCOMPARE( q.value( 0 ).toString(), QString( "Venus" ) );
3440 QVERIFY_SQL( q, exec( "SELECT Name FROM " + Planet ) );
3441 QVERIFY_SQL( q, seek( 3 ) );
3442 QCOMPARE( q.value( 0 ).toString(), QString( "Mars" ) );
3443 QVERIFY_SQL( q, seek( 0 ) );
3444 QCOMPARE( q.value( 0 ).toString(), QString( "Mercury" ) );
3445 QVERIFY_SQL( q, seek( 1 ) );
3446 QCOMPARE( q.value( 0 ).toString(), QString( "Venus" ) );
3447}
3448
3449void tst_QSqlQuery::task_250026()
3450{
3451 QString data258, data1026;
3452 QFETCH( QString, dbName );
3453 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3454 CHECK_DATABASE( db );
3455 QSqlQuery q( db );
3456
3457 const QString tableName(qTableName(prefix: "task_250026", __FILE__, db));
3458
3459 if ( !q.exec( query: "create table " + tableName + " (longfield varchar(1100))" ) ) {
3460 qDebug() << "Error" << q.lastError();
3461 QSKIP( "Db doesn't support \"1100\" as a size for fields");
3462 }
3463
3464 data258.fill( c: 'A', size: 258 );
3465 data1026.fill( c: 'A', size: 1026 );
3466 QVERIFY_SQL( q, prepare( "insert into " + tableName + "(longfield) VALUES (:longfield)" ) );
3467 q.bindValue( placeholder: ":longfield", val: data258 );
3468 QVERIFY_SQL( q, exec() );
3469 q.bindValue( placeholder: ":longfield", val: data1026 );
3470 QVERIFY_SQL( q, exec() );
3471 QVERIFY_SQL( q, exec( "select * from " + tableName ) );
3472 QVERIFY_SQL( q, next() );
3473 QCOMPARE( q.value( 0 ).toString().length(), data258.length() );
3474 QVERIFY_SQL( q, next() );
3475 QCOMPARE( q.value( 0 ).toString().length(), data1026.length() );
3476}
3477
3478void tst_QSqlQuery::crashQueryOnCloseDatabase()
3479{
3480 for (const auto &dbName : qAsConst(t&: dbs.dbNames)) {
3481 QSqlDatabase clonedDb = QSqlDatabase::cloneDatabase(
3482 other: QSqlDatabase::database(connectionName: dbName), connectionName: "crashTest");
3483 qDebug() << "Testing crash in sqlquery dtor for driver" << clonedDb.driverName();
3484 QVERIFY(clonedDb.open());
3485 QSqlQuery q(clonedDb);
3486 clonedDb.close();
3487 QSqlDatabase::removeDatabase(connectionName: "crashTest");
3488 }
3489}
3490
3491void tst_QSqlQuery::task_233829()
3492{
3493 QFETCH( QString, dbName );
3494 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3495 CHECK_DATABASE( db );
3496
3497 QSqlQuery q( db );
3498 const QString tableName(qTableName(prefix: "task_233829", __FILE__, db));
3499 QVERIFY_SQL(q,exec("CREATE TABLE " + tableName + "(dbl1 double precision,dbl2 double precision) without oids;"));
3500
3501 QString queryString("INSERT INTO " + tableName +"(dbl1, dbl2) VALUES(?,?)");
3502
3503 double k = 0.0;
3504 QVERIFY_SQL(q,prepare(queryString));
3505 q.bindValue(pos: 0,val: 0.0 / k); // nan
3506 q.bindValue(pos: 1,val: 0.0 / k); // nan
3507 QVERIFY_SQL(q,exec());
3508}
3509
3510void tst_QSqlQuery::QTBUG_12477()
3511{
3512 QFETCH(QString, dbName);
3513 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3514 CHECK_DATABASE(db);
3515 if (!db.driverName().startsWith(s: "QPSQL"))
3516 QSKIP("PostgreSQL specific test");
3517
3518 QSqlQuery q(db);
3519 QVERIFY_SQL(q, exec("SELECT 1::bit, '10101010000111101101'::varbit, "
3520 "'10101111011'::varbit(15), '22222.20'::numeric(16,2), "
3521 "'333333'::numeric(18), '444444'::numeric"));
3522 QVERIFY_SQL(q, next());
3523 QSqlRecord r = q.record();
3524 QSqlField f;
3525
3526 f = r.field(i: 0);
3527 QCOMPARE(f.length(), 1);
3528 QCOMPARE(f.precision(), -1);
3529
3530 f = r.field(i: 1);
3531 QCOMPARE(f.length(), -1);
3532 QCOMPARE(f.precision(), -1);
3533
3534 f = r.field(i: 2);
3535 QCOMPARE(f.length(), 15);
3536 QCOMPARE(f.precision(), -1);
3537
3538 f = r.field(i: 3);
3539 QCOMPARE(f.length(), 16);
3540 QCOMPARE(f.precision(), 2);
3541
3542 f = r.field(i: 4);
3543 QCOMPARE(f.length(), 18);
3544 QCOMPARE(f.precision(), 0);
3545
3546 f = r.field(i: 5);
3547 QCOMPARE(f.length(), -1);
3548 QCOMPARE(f.precision(), -1);
3549}
3550
3551void tst_QSqlQuery::sqlServerReturn0()
3552{
3553 QFETCH( QString, dbName );
3554 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3555 CHECK_DATABASE( db );
3556 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
3557 if (dbType != QSqlDriver::MSSqlServer)
3558 QSKIP("SQL Server specific test");
3559
3560 const QString tableName(qTableName(prefix: "test141895", __FILE__, db)), procName(qTableName(prefix: "test141895_proc", __FILE__, db));
3561 QSqlQuery q( db );
3562 q.exec(query: "DROP TABLE " + tableName);
3563 q.exec(query: "DROP PROCEDURE " + procName);
3564 QVERIFY_SQL(q, exec("CREATE TABLE "+tableName+" (id integer)"));
3565 QVERIFY_SQL(q, exec("INSERT INTO "+tableName+" (id) VALUES (1)"));
3566 QVERIFY_SQL(q, exec("INSERT INTO "+tableName+" (id) VALUES (2)"));
3567 QVERIFY_SQL(q, exec("INSERT INTO "+tableName+" (id) VALUES (2)"));
3568 QVERIFY_SQL(q, exec("INSERT INTO "+tableName+" (id) VALUES (3)"));
3569 QVERIFY_SQL(q, exec("INSERT INTO "+tableName+" (id) VALUES (1)"));
3570 QVERIFY_SQL(q, exec("CREATE PROCEDURE "+procName+
3571 " AS "
3572 "SELECT * FROM "+tableName+" WHERE ID = 2 "
3573 "RETURN 0"));
3574
3575 QVERIFY_SQL(q, exec("{CALL " + procName + QLatin1Char('}')));
3576
3577 QVERIFY_SQL(q, next());
3578}
3579
3580void tst_QSqlQuery::QTBUG_551()
3581{
3582 QFETCH( QString, dbName );
3583 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3584 CHECK_DATABASE( db );
3585 QSqlQuery q(db);
3586 const QString pkgname(qTableName(prefix: "pkg", __FILE__, db));
3587 QVERIFY_SQL(q, exec("CREATE OR REPLACE PACKAGE "+pkgname+" IS \n\
3588 \n\
3589 TYPE IntType IS TABLE OF INTEGER INDEX BY BINARY_INTEGER;\n\
3590 TYPE VCType IS TABLE OF VARCHAR2(60) INDEX BY BINARY_INTEGER;\n\
3591 PROCEDURE P (Inp IN IntType, Outp OUT VCType);\n\
3592 END "+ pkgname + QLatin1Char(';')));
3593
3594 QVERIFY_SQL(q, exec("CREATE OR REPLACE PACKAGE BODY "+pkgname+" IS\n\
3595 PROCEDURE P (Inp IN IntType, Outp OUT VCType)\n\
3596 IS\n\
3597 BEGIN\n\
3598 Outp(1) := '1. Value is ' ||TO_CHAR(Inp(1));\n\
3599 Outp(2) := '2. Value is ' ||TO_CHAR(Inp(2));\n\
3600 Outp(3) := '3. Value is ' ||TO_CHAR(Inp(3));\n\
3601 END p;\n\
3602 END " + pkgname + QLatin1Char(';')));
3603
3604 QVariantList inLst, outLst, res_outLst;
3605
3606 q.prepare(query: "begin "+pkgname+".p(:inp, :outp); end;");
3607
3608 QString StVal;
3609 StVal.reserve(asize: 60);
3610
3611 // loading arrays
3612 for (int Cnt=0; Cnt < 3; Cnt++) {
3613 inLst << Cnt;
3614 outLst << StVal;
3615 }
3616
3617 q.bindValue(placeholder: ":inp", val: inLst);
3618 q.bindValue(placeholder: ":outp", val: outLst, type: QSql::Out);
3619
3620 QVERIFY_SQL(q, execBatch(QSqlQuery::ValuesAsColumns) );
3621 res_outLst = qvariant_cast<QVariantList>(v: q.boundValues()[":outp"]);
3622 QCOMPARE(res_outLst[0].toString(), QLatin1String("1. Value is 0"));
3623 QCOMPARE(res_outLst[1].toString(), QLatin1String("2. Value is 1"));
3624 QCOMPARE(res_outLst[2].toString(), QLatin1String("3. Value is 2"));
3625}
3626
3627void tst_QSqlQuery::QTBUG_12186()
3628{
3629 QFETCH( QString, dbName );
3630 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3631
3632 // make sure that query.boundValues() returns the values in the right order even for more than 16 placeholders
3633 QSqlQuery query(db);
3634 query.prepare(query: "INSERT INTO person (col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12, col13, col14, col15, col16, col17, col18) "
3635 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
3636
3637 QList<QVariant> values;
3638
3639 for (int i = 0; i < 18; ++i)
3640 values << i;
3641
3642 foreach (QVariant v, values)
3643 query.bindValue(pos: v.toInt(), val: v);
3644
3645 QCOMPARE(query.boundValues().values(), values);
3646}
3647
3648void tst_QSqlQuery::QTBUG_14132()
3649{
3650 QFETCH( QString, dbName );
3651 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3652 CHECK_DATABASE( db );
3653 QSqlQuery q(db);
3654 const QString procedureName(qTableName(prefix: "procedure", __FILE__, db));
3655 QVERIFY_SQL(q, exec("CREATE OR REPLACE PROCEDURE "+ procedureName + " (outStr OUT varchar2) \n\
3656 is \n\
3657 begin \n\
3658 outStr := 'OUTSTRING'; \n\
3659 end;"));
3660 QString placeholder = "XXXXXXXXX";
3661 QVERIFY(q.prepare("CALL "+procedureName+"(?)"));
3662 q.addBindValue(val: placeholder, type: QSql::Out);
3663 QVERIFY_SQL(q, exec());
3664 QCOMPARE(q.boundValue(0).toString(), QLatin1String("OUTSTRING"));
3665}
3666
3667void tst_QSqlQuery::QTBUG_18435()
3668{
3669 QFETCH(QString, dbName);
3670 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3671 CHECK_DATABASE(db);
3672 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
3673 if (dbType != QSqlDriver::MSSqlServer || !db.driverName().startsWith(s: "QODBC"))
3674 QSKIP("SQL Server specific test");
3675
3676 QSqlQuery q(db);
3677 QString procName(qTableName(prefix: "qtbug_18435_proc", __FILE__, db));
3678
3679 q.exec(query: "DROP PROCEDURE " + procName);
3680 const QString stmt =
3681 "CREATE PROCEDURE " + procName + " @key nvarchar(50) OUTPUT AS\n"
3682 "BEGIN\n"
3683 " SET NOCOUNT ON\n"
3684 " SET @key = 'TEST'\n"
3685 "END\n";
3686
3687 QVERIFY_SQL(q, exec(stmt));
3688 QVERIFY_SQL(q, prepare("{CALL "+ procName +"(?)}"));
3689 const QString testStr = "0123";
3690 q.bindValue(pos: 0, val: testStr, type: QSql::Out);
3691 QVERIFY_SQL(q, exec());
3692 QCOMPARE(q.boundValue(0).toString(), QLatin1String("TEST"));
3693
3694 QVERIFY_SQL(q, exec("DROP PROCEDURE " + procName));
3695}
3696
3697void tst_QSqlQuery::QTBUG_5251()
3698{
3699 // Since QSqlTableModel will escape the identifiers, we need to escape
3700 // them for databases that are case sensitive
3701 QFETCH( QString, dbName );
3702 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3703 CHECK_DATABASE( db );
3704 const QString timetest(qTableName(prefix: "timetest", __FILE__, db));
3705 tst_Databases::safeDropTable(db, tableName: timetest);
3706 QSqlQuery q(db);
3707 QVERIFY_SQL(q, exec(QStringLiteral("CREATE TABLE ") + timetest + QStringLiteral(" (t TIME)")));
3708 QVERIFY_SQL(q, exec(QStringLiteral("INSERT INTO ") + timetest +
3709 QStringLiteral(" VALUES ('1:2:3.666')")));
3710
3711 QSqlTableModel timetestModel(0,db);
3712 timetestModel.setEditStrategy(QSqlTableModel::OnManualSubmit);
3713 timetestModel.setTable(timetest);
3714 QVERIFY_SQL(timetestModel, select());
3715
3716 QCOMPARE(timetestModel.record(0).field(0).value().toTime().toString("HH:mm:ss.zzz"), QString("01:02:03.666"));
3717 QVERIFY_SQL(timetestModel,setData(timetestModel.index(0, 0), QTime(0,12,34,500)));
3718 QCOMPARE(timetestModel.record(0).field(0).value().toTime().toString("HH:mm:ss.zzz"), QString("00:12:34.500"));
3719 QVERIFY_SQL(timetestModel, submitAll());
3720 QCOMPARE(timetestModel.record(0).field(0).value().toTime().toString("HH:mm:ss.zzz"), QString("00:12:34.500"));
3721
3722 QVERIFY_SQL(q, exec(QStringLiteral("UPDATE ") + timetest +
3723 QStringLiteral(" SET t = '0:11:22.33'")));
3724 QVERIFY_SQL(timetestModel, select());
3725 QCOMPARE(timetestModel.record(0).field(0).value().toTime().toString("HH:mm:ss.zzz"), QString("00:11:22.330"));
3726
3727}
3728
3729void tst_QSqlQuery::QTBUG_6421()
3730{
3731 QFETCH( QString, dbName );
3732 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3733 CHECK_DATABASE( db );
3734
3735 QSqlQuery q(db);
3736 const QString tableName(qTableName(prefix: "bug6421", __FILE__, db).toUpper());
3737
3738 QVERIFY_SQL(q, exec("create table "+tableName+"(COL1 char(10), COL2 char(10), COL3 char(10))"));
3739 QVERIFY_SQL(q, exec("create index INDEX1 on "+tableName+" (COL1 desc)"));
3740 QVERIFY_SQL(q, exec("create index INDEX2 on "+tableName+" (COL2 desc)"));
3741 QVERIFY_SQL(q, exec("create index INDEX3 on "+tableName+" (COL3 desc)"));
3742 q.setForwardOnly(true);
3743 QVERIFY_SQL(q, exec("select COLUMN_EXPRESSION from ALL_IND_EXPRESSIONS where TABLE_NAME='" + tableName + QLatin1Char('\'')));
3744 QVERIFY_SQL(q, next());
3745 QCOMPARE(q.value(0).toString(), QLatin1String("\"COL1\""));
3746 QVERIFY_SQL(q, next());
3747 QCOMPARE(q.value(0).toString(), QLatin1String("\"COL2\""));
3748 QVERIFY_SQL(q, next());
3749 QCOMPARE(q.value(0).toString(), QLatin1String("\"COL3\""));
3750}
3751
3752void tst_QSqlQuery::QTBUG_6618()
3753{
3754 QFETCH( QString, dbName );
3755 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3756 CHECK_DATABASE( db );
3757 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
3758 if (dbType != QSqlDriver::MSSqlServer)
3759 QSKIP("SQL Server specific test");
3760
3761 QSqlQuery q(db);
3762 q.exec(query: "drop procedure " + qTableName(prefix: "tst_raiseError", __FILE__, db)); //non-fatal
3763 QString errorString;
3764 for (int i=0;i<110;i++)
3765 errorString+="reallylong";
3766 errorString+=" error";
3767 QVERIFY_SQL(q, exec("create procedure " + qTableName("tst_raiseError", __FILE__, db) + " as\n"
3768 "begin\n"
3769 " raiserror('" + errorString + "', 16, 1)\n"
3770 "end\n" ));
3771 q.exec(query: "{call " + qTableName(prefix: "tst_raiseError", __FILE__, db) + QLatin1Char('}'));
3772 QVERIFY(q.lastError().text().contains(errorString));
3773}
3774
3775void tst_QSqlQuery::QTBUG_6852()
3776{
3777 QFETCH( QString, dbName );
3778 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3779 CHECK_DATABASE( db );
3780 QSqlQuery q(db);
3781 const QString tableName(qTableName(prefix: "bug6852", __FILE__, db)), procName(qTableName(prefix: "bug6852_proc", __FILE__, db));
3782
3783 QVERIFY_SQL(q, exec("DROP PROCEDURE IF EXISTS "+procName));
3784 QVERIFY_SQL(q, exec("CREATE TABLE "+tableName+"(\n"
3785 "MainKey INT NOT NULL,\n"
3786 "OtherTextCol VARCHAR(45) NOT NULL,\n"
3787 "PRIMARY KEY(`MainKey`))"));
3788 QVERIFY_SQL(q, exec("INSERT INTO "+tableName+" VALUES(0, \"Disabled\")"));
3789 QVERIFY_SQL(q, exec("INSERT INTO "+tableName+" VALUES(5, \"Error Only\")"));
3790 QVERIFY_SQL(q, exec("INSERT INTO "+tableName+" VALUES(10, \"Enabled\")"));
3791 QVERIFY_SQL(q, exec("INSERT INTO "+tableName+" VALUES(15, \"Always\")"));
3792 QVERIFY_SQL(q, exec("CREATE PROCEDURE "+procName+"()\n"
3793 "READS SQL DATA\n"
3794 "BEGIN\n"
3795 " SET @st = 'SELECT MainKey, OtherTextCol from "+tableName+"';\n"
3796 " PREPARE stmt from @st;\n"
3797 " EXECUTE stmt;\n"
3798 "END;"));
3799
3800 QVERIFY_SQL(q, exec("CALL "+procName+"()"));
3801 QVERIFY_SQL(q, next());
3802 QCOMPARE(q.value(0).toInt(), 0);
3803 QCOMPARE(q.value(1).toString(), QLatin1String("Disabled"));
3804}
3805
3806void tst_QSqlQuery::QTBUG_5765()
3807{
3808 QFETCH( QString, dbName );
3809 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
3810 CHECK_DATABASE( db );
3811 QSqlQuery q(db);
3812 const QString tableName(qTableName(prefix: "bug5765", __FILE__, db));
3813
3814 QVERIFY_SQL(q, exec("CREATE TABLE "+tableName+"(testval TINYINT(1) DEFAULT 0)"));
3815 q.prepare(query: "INSERT INTO "+tableName+" SET testval = :VALUE");
3816 q.bindValue(placeholder: ":VALUE", val: 1);
3817 QVERIFY_SQL(q, exec());
3818 q.bindValue(placeholder: ":VALUE", val: 12);
3819 QVERIFY_SQL(q, exec());
3820 q.bindValue(placeholder: ":VALUE", val: 123);
3821 QVERIFY_SQL(q, exec());
3822 QString sql="select testval from "+tableName;
3823 QVERIFY_SQL(q, exec(sql));
3824 QVERIFY_SQL(q, next());
3825 QCOMPARE(q.value(0).toInt(), 1);
3826 QVERIFY_SQL(q, next());
3827 QCOMPARE(q.value(0).toInt(), 12);
3828 QVERIFY_SQL(q, next());
3829 QCOMPARE(q.value(0).toInt(), 123);
3830 QVERIFY_SQL(q, prepare(sql));
3831 QVERIFY_SQL(q, exec());
3832 QVERIFY_SQL(q, next());
3833 QCOMPARE(q.value(0).toInt(), 1);
3834 QVERIFY_SQL(q, next());
3835 QCOMPARE(q.value(0).toInt(), 12);
3836 QVERIFY_SQL(q, next());
3837 QCOMPARE(q.value(0).toInt(), 123);
3838}
3839
3840/**
3841* This test case tests multiple statements in one execution.
3842* Sqlite driver doesn't support multiple statement at one time.
3843* If more than one statement is given, the exec or prepare function
3844* return failure to the client.
3845*/
3846void tst_QSqlQuery::QTBUG_21884()
3847{
3848 QFETCH(QString, dbName);
3849 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3850 CHECK_DATABASE(db);
3851
3852 QSqlQuery q(db);
3853
3854 QStringList stList;
3855 QString tableName(qTableName(prefix: "bug21884", __FILE__, db));
3856 stList << "create table " + tableName + "(id integer primary key, note string)";
3857 stList << "select * from " + tableName + QLatin1Char(';');
3858 stList << "select * from " + tableName + "; \t\n\r";
3859 stList << "drop table " + tableName;
3860
3861
3862 foreach (const QString& st, stList) {
3863 QVERIFY_SQL(q, exec(st));
3864 }
3865
3866 foreach (const QString& st, stList) {
3867 QVERIFY_SQL(q, prepare(st));
3868 QVERIFY_SQL(q, exec());
3869 }
3870
3871 stList.clear();
3872 stList << "create table " + tableName + "(id integer primary key); select * from " + tableName;
3873 stList << "create table " + tableName + "(id integer primary key); syntax error!;";
3874 stList << "create table " + tableName + "(id integer primary key);;";
3875 stList << "create table " + tableName + "(id integer primary key);\'\"\a\b\b\v";
3876
3877 foreach (const QString&st , stList) {
3878 QVERIFY2(!q.prepare(st), qPrintable(QString("the statement is expected to fail! ") + st));
3879 QVERIFY2(!q.exec(st), qPrintable(QString("the statement is expected to fail! ") + st));
3880 }
3881}
3882
3883/**
3884 * This test case test sqlite driver close function. Sqlite driver should close cleanly
3885 * even if there is still outstanding prepared statement.
3886 */
3887void tst_QSqlQuery::QTBUG_16967()
3888{
3889 QSqlQuery q2;
3890 QFETCH(QString, dbName);
3891 {
3892 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3893 CHECK_DATABASE(db);
3894 db.close();
3895 QCOMPARE(db.lastError().type(), QSqlError::NoError);
3896 }
3897 {
3898 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3899 CHECK_DATABASE(db);
3900 QSqlQuery q(db);
3901 q2 = q;
3902 q.prepare(query: "CREATE TABLE t1 (id INTEGER PRIMARY KEY, str TEXT);");
3903 db.close();
3904 QCOMPARE(db.lastError().type(), QSqlError::NoError);
3905 }
3906 {
3907 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3908 CHECK_DATABASE(db);
3909 QSqlQuery q(db);
3910 q2 = q;
3911 q2.prepare(query: "CREATE TABLE t1 (id INTEGER PRIMARY KEY, str TEXT);");
3912 q2.exec();
3913 db.close();
3914 QCOMPARE(db.lastError().type(), QSqlError::NoError);
3915 }
3916 {
3917 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3918 CHECK_DATABASE(db);
3919 QSqlQuery q(db);
3920 q2 = q;
3921 q.exec(query: "INSERT INTO t1 (id, str) VALUES(1, \"test1\");");
3922 db.close();
3923 QCOMPARE(db.lastError().type(), QSqlError::NoError);
3924 }
3925 {
3926 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3927 CHECK_DATABASE(db);
3928 QSqlQuery q(db);
3929 q2 = q;
3930 q.exec(query: "SELECT * FROM t1;");
3931 db.close();
3932 QCOMPARE(db.lastError().type(), QSqlError::NoError);
3933 }
3934}
3935
3936/**
3937 * In SQLite when a boolean value is bound to a placeholder, it should be converted
3938 * into integer 0/1 rather than text "false"/"true". According to documentation,
3939 * SQLite does not have separate Boolean storage class. Instead, Boolean values are
3940 * stored as integers.
3941 */
3942void tst_QSqlQuery::QTBUG_23895()
3943{
3944 QFETCH(QString, dbName);
3945 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
3946 CHECK_DATABASE(db);
3947
3948 QSqlQuery q(db);
3949
3950 QString tableName(qTableName(prefix: "bug23895", __FILE__, db));
3951 q.prepare(query: "create table " + tableName + "(id integer primary key, val1 bool, val2 boolean)");
3952 QVERIFY_SQL(q, exec());
3953 q.prepare(query: "insert into " + tableName + "(id, val1, val2) values(?, ?, ?);");
3954 q.addBindValue(val: 1);
3955 q.addBindValue(val: true);
3956 q.addBindValue(val: false);
3957 QVERIFY_SQL(q, exec());
3958
3959 QString sql="select * from " + tableName;
3960 QVERIFY_SQL(q, exec(sql));
3961 QVERIFY_SQL(q, next());
3962
3963 QCOMPARE(q.record().field(0).type(), QVariant::Int);
3964 QCOMPARE(q.value(0).type(), QVariant::LongLong);
3965 QCOMPARE(q.value(0).toInt(), 1);
3966 QCOMPARE(q.record().field(1).type(), QVariant::Bool);
3967 QCOMPARE(q.value(1).type(), QVariant::LongLong);
3968 QCOMPARE(q.value(1).toBool(), true);
3969 QCOMPARE(q.record().field(2).type(), QVariant::Bool);
3970 QCOMPARE(q.value(2).type(), QVariant::LongLong);
3971 QCOMPARE(q.value(2).toBool(), false);
3972
3973 q.prepare(query: "insert into " + tableName + "(id, val1, val2) values(?, ?, ?);");
3974 q.addBindValue(val: 2);
3975 q.addBindValue(val: false);
3976 q.addBindValue(val: false);
3977 QVERIFY_SQL(q, exec());
3978
3979 sql="select * from " + tableName + " where val1";
3980 QVERIFY_SQL(q, exec(sql));
3981 QVERIFY_SQL(q, next());
3982 QCOMPARE(q.value(0).toInt(), 1);
3983 QVERIFY(!q.next());
3984
3985 sql="select * from " + tableName + " where not val2";
3986 QVERIFY_SQL(q, exec(sql));
3987 QVERIFY_SQL(q, next());
3988 QCOMPARE(q.value(0).toInt(), 1);
3989 QVERIFY_SQL(q, next());
3990 QCOMPARE(q.value(0).toInt(), 2);
3991 QVERIFY(!q.next());
3992}
3993
3994/**
3995 * Test for aliases with dots
3996 */
3997void tst_QSqlQuery::QTBUG_14904()
3998{
3999 QFETCH(QString, dbName);
4000 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4001 CHECK_DATABASE(db);
4002
4003 QSqlQuery q(db);
4004
4005 QString tableName(qTableName(prefix: "bug14904", __FILE__, db));
4006 tst_Databases::safeDropTable( db, tableName );
4007
4008 q.prepare(query: "create table " + tableName + "(val1 bool)");
4009 QVERIFY_SQL(q, exec());
4010 q.prepare(query: "insert into " + tableName + "(val1) values(?);");
4011 q.addBindValue(val: true);
4012 QVERIFY_SQL(q, exec());
4013
4014 QString sql="select val1 AS value1 from " + tableName;
4015 QVERIFY_SQL(q, exec(sql));
4016 QVERIFY_SQL(q, next());
4017
4018 QCOMPARE(q.record().indexOf("value1"), 0);
4019 QCOMPARE(q.record().field(0).type(), QVariant::Bool);
4020 QCOMPARE(q.value(0).toBool(), true);
4021
4022 sql="select val1 AS 'value.one' from " + tableName;
4023 QVERIFY_SQL(q, exec(sql));
4024 QVERIFY_SQL(q, next());
4025 QCOMPARE(q.record().indexOf("value.one"), 0); // was -1 before bug fix
4026 QCOMPARE(q.record().field(0).type(), QVariant::Bool);
4027 QCOMPARE(q.value(0).toBool(), true);
4028}
4029
4030void tst_QSqlQuery::QTBUG_2192()
4031{
4032 QFETCH( QString, dbName );
4033 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
4034 CHECK_DATABASE( db );
4035 {
4036 const QString tableName(qTableName(prefix: "bug2192", __FILE__, db));
4037 tst_Databases::safeDropTable( db, tableName );
4038
4039 QSqlQuery q(db);
4040 QVERIFY_SQL(q, exec(QString("CREATE TABLE " + tableName + " (dt %1)").arg(tst_Databases::dateTimeTypeName(db))));
4041
4042 QDateTime dt = QDateTime(QDate(2012, 7, 4), QTime(23, 59, 59, 999));
4043 QVERIFY_SQL(q, prepare("INSERT INTO " + tableName + " (dt) VALUES (?)"));
4044 q.bindValue(pos: 0, val: dt);
4045 QVERIFY_SQL(q, exec());
4046
4047 QVERIFY_SQL(q, exec("SELECT dt FROM " + tableName));
4048 QVERIFY_SQL(q, next());
4049
4050 // Check if retrieved value preserves reported precision
4051 int precision = qMax(a: 0, b: q.record().field(name: "dt").precision());
4052 qint64 diff = qAbs(t: q.value(i: 0).toDateTime().msecsTo(dt));
4053 qint64 keep = qMin(a: 1000LL, b: qRound64(d: qPow(x: 10.0, y: precision)));
4054 QVERIFY(diff <= 1000 - keep);
4055 }
4056}
4057
4058void tst_QSqlQuery::QTBUG_36211()
4059{
4060 QFETCH( QString, dbName );
4061 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
4062 CHECK_DATABASE( db );
4063 if (tst_Databases::getDatabaseType(db) == QSqlDriver::PostgreSQL) {
4064 const QString tableName(qTableName(prefix: "bug36211", __FILE__, db));
4065 tst_Databases::safeDropTable( db, tableName );
4066
4067 QSqlQuery q(db);
4068 QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (dtwtz timestamptz, dtwotz timestamp)").arg(tableName)));
4069
4070#if QT_CONFIG(timezone)
4071 QTimeZone l_tzBrazil("America/Sao_Paulo");
4072 QTimeZone l_tzChina("Asia/Shanghai");
4073 QVERIFY(l_tzBrazil.isValid());
4074 QVERIFY(l_tzChina.isValid());
4075 QDateTime dt = QDateTime(QDate(2014, 10, 30), QTime(14, 12, 02, 357));
4076 QVERIFY_SQL(q, prepare("INSERT INTO " + tableName + " (dtwtz, dtwotz) VALUES (:dt, :dt)"));
4077 q.bindValue(placeholder: ":dt", val: dt);
4078 QVERIFY_SQL(q, exec());
4079 q.bindValue(placeholder: ":dt", val: dt.toTimeZone(toZone: l_tzBrazil));
4080 QVERIFY_SQL(q, exec());
4081 q.bindValue(placeholder: ":dt", val: dt.toTimeZone(toZone: l_tzChina));
4082 QVERIFY_SQL(q, exec());
4083
4084 QVERIFY_SQL(q, exec("SELECT dtwtz, dtwotz FROM " + tableName));
4085
4086 for (int i = 0; i < 3; ++i) {
4087 QVERIFY_SQL(q, next());
4088
4089 for (int j = 0; j < 2; ++j) {
4090 // Check if retrieved value preserves reported precision
4091 int precision = qMax(a: 0, b: q.record().field(i: j).precision());
4092 qint64 diff = qAbs(t: q.value(i: j).toDateTime().msecsTo(dt));
4093 qint64 keep = qMin(a: 1000LL, b: qRound64(d: qPow(x: 10.0, y: precision)));
4094 QVERIFY(diff <= 1000 - keep);
4095 }
4096 }
4097#endif
4098 }
4099}
4100
4101void tst_QSqlQuery::QTBUG_53969()
4102{
4103 QFETCH( QString, dbName );
4104 QVector<int> values = QVector<int>() << 10 << 20 << 127 << 128 << 1, tableValues;
4105 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
4106 CHECK_DATABASE( db );
4107 tableValues.reserve(asize: values.size());
4108 if (tst_Databases::getDatabaseType(db) == QSqlDriver::MySqlServer) {
4109 const QString tableName(qTableName(prefix: "bug53969", __FILE__, db));
4110 tst_Databases::safeDropTable( db, tableName );
4111
4112 QSqlQuery q(db);
4113 QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (id INT AUTO_INCREMENT PRIMARY KEY, "
4114 "test_number TINYINT(3) UNSIGNED)")
4115 .arg(tableName)));
4116
4117 QVERIFY_SQL(q, prepare("INSERT INTO " + tableName + " (test_number) VALUES (:value)"));
4118
4119 QVector<int>::iterator begin = values.begin(), end = values.end(), it;
4120 for (it = begin; it != end; ++it) {
4121 q.bindValue(placeholder: ":value", val: *it);
4122 QVERIFY_SQL(q, exec());
4123 }
4124
4125 QVERIFY_SQL(q, prepare("SELECT test_number FROM " + tableName));
4126 QVERIFY_SQL(q, exec());
4127
4128 while (q.next()) {
4129 bool ok;
4130 tableValues.push_back(t: q.value(i: 0).toUInt(ok: &ok));
4131 QVERIFY(ok);
4132 }
4133 QCOMPARE(values, tableValues);
4134 }
4135}
4136
4137void tst_QSqlQuery::gisPointDatatype()
4138{
4139 QFETCH(QString, dbName);
4140 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4141 CHECK_DATABASE(db);
4142
4143 QSqlQuery sqlQuery(db);
4144 const auto tableName = qTableName(prefix: "qtbug72140", __FILE__, db);
4145 tst_Databases::safeDropTable(db, tableName);
4146 QString sqlCommand = QStringLiteral("CREATE TABLE %1 (`lonlat_point` POINT NULL) ENGINE = InnoDB;").arg(a: tableName);
4147 QVERIFY(sqlQuery.exec(sqlCommand));
4148 sqlCommand = QStringLiteral("INSERT INTO %1(lonlat_point) VALUES(ST_GeomFromText('POINT(1 1)'));").arg(a: tableName);
4149 QVERIFY(sqlQuery.exec(sqlCommand));
4150 sqlCommand = QStringLiteral("SELECT * FROM %1;").arg(a: tableName);
4151 QVERIFY(sqlQuery.exec(sqlCommand));
4152 QCOMPARE(sqlQuery.record().field(0).type(), QVariant::Type::ByteArray);
4153 QVERIFY(sqlQuery.next());
4154}
4155
4156void tst_QSqlQuery::oraOCINumber()
4157{
4158 QFETCH( QString, dbName );
4159 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
4160 CHECK_DATABASE( db );
4161 const QString qtest_oraOCINumber(qTableName(prefix: "qtest_oraOCINumber", __FILE__, db));
4162
4163 QSqlQuery q( db );
4164 q.setForwardOnly( true );
4165 QVERIFY_SQL( q, exec( "create table " + qtest_oraOCINumber +
4166 " (col1 number(20), col2 number(20))" ) );
4167 QVERIFY(q.prepare("insert into " + qtest_oraOCINumber + " values (?, ?)"));
4168 QVariantList col1Values;
4169 QVariantList col2Values;
4170 col1Values << (qulonglong)(1)
4171 << (qulonglong)(0)
4172 << (qulonglong)(INT_MAX)
4173 << (qulonglong)(UINT_MAX)
4174 << (qulonglong)(LONG_MAX)
4175 << (qulonglong)(ULONG_MAX)
4176 << (qulonglong)(LLONG_MAX)
4177 << (qulonglong)(ULLONG_MAX);
4178
4179 col2Values << (qlonglong)(1)
4180 << (qlonglong)(0)
4181 << (qlonglong)(-1)
4182 << (qlonglong)(LONG_MAX)
4183 << (qlonglong)(LONG_MIN)
4184 << (qlonglong)(ULONG_MAX)
4185 << (qlonglong)(LLONG_MAX)
4186 << (qlonglong)(LLONG_MIN);
4187
4188 q.addBindValue(val: col1Values);
4189 q.addBindValue(val: col2Values);
4190 QVERIFY(q.execBatch());
4191 QString sqlStr = "select * from " + qtest_oraOCINumber + " where col1 = :bindValue0 AND col2 = :bindValue1";
4192 QVERIFY(q.prepare(sqlStr));
4193
4194 q.bindValue(placeholder: ":bindValue0", val: (qulonglong)(1), type: QSql::InOut);
4195 q.bindValue(placeholder: ":bindValue1", val: (qlonglong)(1), type: QSql::InOut);
4196
4197 QVERIFY_SQL( q, exec() );
4198 QVERIFY( q.next() );
4199 QCOMPARE(q.boundValue( 0 ).toULongLong(), qulonglong(1));
4200 QCOMPARE(q.boundValue( 1 ).toLongLong(), (qlonglong)(1));
4201
4202 q.bindValue(placeholder: ":bindValue0", val: (qulonglong)(0), type: QSql::InOut);
4203 q.bindValue(placeholder: ":bindValue1", val: (qlonglong)(0), type: QSql::InOut);
4204 QVERIFY_SQL( q, exec() );
4205
4206 QVERIFY( q.next() );
4207 QCOMPARE(q.boundValue( 0 ).toULongLong(), (qulonglong)(0));
4208 QCOMPARE(q.boundValue( 1 ).toLongLong(), (qlonglong)(0));
4209
4210 q.bindValue(placeholder: ":bindValue0", val: (qulonglong)(INT_MAX), type: QSql::InOut);
4211 q.bindValue(placeholder: ":bindValue1", val: (qlonglong)(-1), type: QSql::InOut);
4212 QVERIFY_SQL( q, exec() );
4213
4214 QVERIFY( q.next() );
4215 QCOMPARE(q.boundValue( 0 ).toULongLong(), (qulonglong)(INT_MAX));
4216 QCOMPARE(q.boundValue( 1 ).toLongLong(), (qlonglong)(-1));
4217
4218 q.bindValue(placeholder: ":bindValue0", val: (qulonglong)(UINT_MAX), type: QSql::InOut);
4219 q.bindValue(placeholder: ":bindValue1", val: (qlonglong)(LONG_MAX), type: QSql::InOut);
4220 QVERIFY_SQL( q, exec() );
4221
4222 QVERIFY( q.next() );
4223 QCOMPARE(q.boundValue( 0 ).toULongLong(), (qulonglong)(UINT_MAX));
4224 QCOMPARE(q.boundValue( 1 ).toLongLong(), (qlonglong)(LONG_MAX));
4225
4226 q.bindValue(placeholder: ":bindValue0", val: (qulonglong)(LONG_MAX), type: QSql::InOut);
4227 q.bindValue(placeholder: ":bindValue1", val: (qlonglong)(LONG_MIN), type: QSql::InOut);
4228 QVERIFY_SQL( q, exec() );
4229
4230 QVERIFY( q.next() );
4231 QCOMPARE(q.boundValue( 0 ).toULongLong(), (qulonglong)(LONG_MAX));
4232 QCOMPARE(q.boundValue( 1 ).toLongLong(), (qlonglong)(LONG_MIN));
4233
4234 q.bindValue(placeholder: ":bindValue0", val: (qulonglong)(ULONG_MAX), type: QSql::InOut);
4235 q.bindValue(placeholder: ":bindValue1", val: (qlonglong)(ULONG_MAX), type: QSql::InOut);
4236 QVERIFY_SQL( q, exec() );
4237
4238 QVERIFY( q.next() );
4239 QCOMPARE(q.boundValue( 0 ).toULongLong(), (qulonglong)(ULONG_MAX));
4240 QCOMPARE(q.boundValue( 1 ).toLongLong(), (qlonglong)(ULONG_MAX));
4241
4242 q.bindValue(placeholder: ":bindValue0", val: (qulonglong)(LLONG_MAX), type: QSql::InOut);
4243 q.bindValue(placeholder: ":bindValue1", val: (qlonglong)(LLONG_MAX), type: QSql::InOut);
4244 QVERIFY_SQL( q, exec() );
4245
4246 QVERIFY( q.next() );
4247 QCOMPARE(q.boundValue( 0 ).toULongLong(), (qulonglong)(LLONG_MAX));
4248 QCOMPARE(q.boundValue( 1 ).toLongLong(), (qlonglong)(LLONG_MAX));
4249
4250 q.bindValue(placeholder: ":bindValue0", val: (qulonglong)(ULLONG_MAX), type: QSql::InOut);
4251 q.bindValue(placeholder: ":bindValue1", val: (qlonglong)(LLONG_MIN), type: QSql::InOut);
4252 QVERIFY_SQL( q, exec() );
4253
4254 QVERIFY( q.next() );
4255 QCOMPARE(q.boundValue( 0 ).toULongLong(), (qulonglong)(ULLONG_MAX));
4256 QCOMPARE(q.boundValue( 1 ).toLongLong(), (qlonglong)(LLONG_MIN));
4257
4258}
4259
4260void tst_QSqlQuery::sqlite_constraint()
4261{
4262 QFETCH( QString, dbName );
4263 QSqlDatabase db = QSqlDatabase::database( connectionName: dbName );
4264 CHECK_DATABASE( db );
4265
4266 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
4267 if (dbType != QSqlDriver::SQLite)
4268 QSKIP("Sqlite3 specific test");
4269
4270 QSqlQuery q(db);
4271 const QString trigger(qTableName(prefix: "test_constraint", __FILE__, db));
4272
4273 QVERIFY_SQL(q, exec("CREATE TEMP TRIGGER "+trigger+" BEFORE DELETE ON "+qtest+
4274 "\nFOR EACH ROW "
4275 "\nBEGIN"
4276 "\n SELECT RAISE(ABORT, 'Raised Abort successfully');"
4277 "\nEND;"
4278 ));
4279
4280 QVERIFY(!q.exec("DELETE FROM "+qtest));
4281 QCOMPARE(q.lastError().databaseText(), QLatin1String("Raised Abort successfully"));
4282}
4283
4284void tst_QSqlQuery::sqlite_real()
4285{
4286 QFETCH(QString, dbName);
4287 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4288 CHECK_DATABASE(db);
4289 const QString tableName(qTableName(prefix: "sqliterealtype", __FILE__, db));
4290 tst_Databases::safeDropTable( db, tableName );
4291
4292 QSqlQuery q(db);
4293 QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id INTEGER, realVal REAL)"));
4294 QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id, realVal) VALUES (1, 2.3)"));
4295 QVERIFY_SQL(q, exec("SELECT realVal FROM " + tableName));
4296 QVERIFY(q.next());
4297 QCOMPARE(q.value(0).toDouble(), 2.3);
4298 QCOMPARE(q.record().field(0).type(), QVariant::Double);
4299
4300 q.prepare(query: "INSERT INTO " + tableName + " (id, realVal) VALUES (?, ?)");
4301 QVariant var((double)5.6);
4302 q.addBindValue(val: 4);
4303 q.addBindValue(val: var);
4304 QVERIFY_SQL(q, exec());
4305
4306 QVERIFY_SQL(q, exec("SELECT realVal FROM " + tableName + " WHERE ID=4"));
4307 QVERIFY(q.next());
4308 QCOMPARE(q.value(0).toDouble(), 5.6);
4309}
4310
4311void tst_QSqlQuery::aggregateFunctionTypes()
4312{
4313 QFETCH(QString, dbName);
4314 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4315 CHECK_DATABASE(db);
4316 QVariant::Type intType = QVariant::Int;
4317 QVariant::Type sumType = intType;
4318 QVariant::Type countType = intType;
4319 // QPSQL uses LongLong for manipulation of integers
4320 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
4321 if (dbType == QSqlDriver::PostgreSQL) {
4322 sumType = countType = QVariant::LongLong;
4323 } else if (dbType == QSqlDriver::Oracle) {
4324 intType = sumType = countType = QVariant::Double;
4325 } else if (dbType == QSqlDriver::MySqlServer) {
4326 sumType = QVariant::Double;
4327 countType = QVariant::LongLong;
4328 }
4329 {
4330 const QString tableName(qTableName(prefix: "numericFunctionsWithIntValues", __FILE__, db));
4331 tst_Databases::safeDropTable( db, tableName );
4332
4333 QSqlQuery q(db);
4334 QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id INTEGER)"));
4335
4336 // First test without any entries
4337 QVERIFY_SQL(q, exec("SELECT SUM(id) FROM " + tableName));
4338 QVERIFY(q.next());
4339 if (dbType == QSqlDriver::SQLite)
4340 QCOMPARE(q.record().field(0).type(), QVariant::Invalid);
4341 else
4342 QCOMPARE(q.record().field(0).type(), sumType);
4343
4344 QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id) VALUES (1)"));
4345 QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id) VALUES (2)"));
4346
4347 QVERIFY_SQL(q, exec("SELECT SUM(id) FROM " + tableName));
4348 QVERIFY(q.next());
4349 QCOMPARE(q.value(0).toInt(), 3);
4350 QCOMPARE(q.record().field(0).type(), sumType);
4351
4352 QVERIFY_SQL(q, exec("SELECT AVG(id) FROM " + tableName));
4353 QVERIFY(q.next());
4354 if (dbType == QSqlDriver::SQLite || dbType == QSqlDriver::PostgreSQL || dbType == QSqlDriver::MySqlServer
4355 || dbType == QSqlDriver::Oracle) {
4356 QCOMPARE(q.value(0).toDouble(), 1.5);
4357 QCOMPARE(q.record().field(0).type(), QVariant::Double);
4358 } else {
4359 QCOMPARE(q.value(0).toInt(), 1);
4360 QCOMPARE(q.record().field(0).type(), QVariant::Int);
4361 }
4362
4363 QVERIFY_SQL(q, exec("SELECT COUNT(id) FROM " + tableName));
4364 QVERIFY(q.next());
4365 QCOMPARE(q.value(0).toInt(), 2);
4366 QCOMPARE(q.record().field(0).type(), countType);
4367
4368 QVERIFY_SQL(q, exec("SELECT MIN(id) FROM " + tableName));
4369 QVERIFY(q.next());
4370 QCOMPARE(q.value(0).toInt(), 1);
4371 QCOMPARE(q.record().field(0).type(), intType);
4372
4373 QVERIFY_SQL(q, exec("SELECT MAX(id) FROM " + tableName));
4374 QVERIFY(q.next());
4375 QCOMPARE(q.value(0).toInt(), 2);
4376 QCOMPARE(q.record().field(0).type(), intType);
4377 }
4378 {
4379 const QString tableName(qTableName(prefix: "numericFunctionsWithDoubleValues", __FILE__, db));
4380 tst_Databases::safeDropTable( db, tableName );
4381
4382 QSqlQuery q(db);
4383 QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id REAL)"));
4384
4385 // First test without any entries
4386 QVERIFY_SQL(q, exec("SELECT SUM(id) FROM " + tableName));
4387 QVERIFY(q.next());
4388 if (dbType == QSqlDriver::SQLite)
4389 QCOMPARE(q.record().field(0).type(), QVariant::Invalid);
4390 else
4391 QCOMPARE(q.record().field(0).type(), QVariant::Double);
4392
4393 QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id) VALUES (1.5)"));
4394 QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id) VALUES (2.5)"));
4395
4396 QVERIFY_SQL(q, exec("SELECT SUM(id) FROM " + tableName));
4397 QVERIFY(q.next());
4398 QCOMPARE(q.value(0).toDouble(), 4.0);
4399 QCOMPARE(q.record().field(0).type(), QVariant::Double);
4400
4401 QVERIFY_SQL(q, exec("SELECT AVG(id) FROM " + tableName));
4402 QVERIFY(q.next());
4403 QCOMPARE(q.value(0).toDouble(), 2.0);
4404 QCOMPARE(q.record().field(0).type(), QVariant::Double);
4405
4406 QVERIFY_SQL(q, exec("SELECT COUNT(id) FROM " + tableName));
4407 QVERIFY(q.next());
4408 QCOMPARE(q.value(0).toInt(), 2);
4409 QCOMPARE(q.record().field(0).type(), countType);
4410
4411 QVERIFY_SQL(q, exec("SELECT MIN(id) FROM " + tableName));
4412 QVERIFY(q.next());
4413 QCOMPARE(q.value(0).toDouble(), 1.5);
4414 QCOMPARE(q.record().field(0).type(), QVariant::Double);
4415
4416 QVERIFY_SQL(q, exec("SELECT MAX(id) FROM " + tableName));
4417 QVERIFY(q.next());
4418 QCOMPARE(q.value(0).toDouble(), 2.5);
4419 QCOMPARE(q.record().field(0).type(), QVariant::Double);
4420
4421 QString field = "id";
4422
4423 // PSQL does not have the round() function with real type
4424 if (dbType == QSqlDriver::PostgreSQL) {
4425 field += "::NUMERIC";
4426 }
4427
4428 QVERIFY_SQL(q, exec("SELECT ROUND(" + field + ", 1) FROM " + tableName + " WHERE id=1.5"));
4429 QVERIFY(q.next());
4430 QCOMPARE(q.value(0).toDouble(), 1.5);
4431 QCOMPARE(q.record().field(0).type(), QVariant::Double);
4432
4433 QVERIFY_SQL(q, exec("SELECT ROUND(" + field + ", 0) FROM " + tableName + " WHERE id=2.5"));
4434 QVERIFY(q.next());
4435 if (dbType == QSqlDriver::MySqlServer)
4436 QCOMPARE(q.value(0).toDouble(), 2.0);
4437 else
4438 QCOMPARE(q.value(0).toDouble(), 3.0);
4439 QCOMPARE(q.record().field(0).type(), QVariant::Double);
4440 }
4441 {
4442 const QString tableName(qTableName(prefix: "stringFunctions", __FILE__, db));
4443 tst_Databases::safeDropTable( db, tableName );
4444
4445 QSqlQuery q(db);
4446 QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id INTEGER, txt VARCHAR(50))"));
4447
4448 QVERIFY_SQL(q, exec("SELECT MAX(txt) FROM " + tableName));
4449 QVERIFY(q.next());
4450 if (dbType == QSqlDriver::SQLite)
4451 QCOMPARE(q.record().field(0).type(), QVariant::Invalid);
4452 else
4453 QCOMPARE(q.record().field(0).type(), QVariant::String);
4454
4455 QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id, txt) VALUES (1, 'lower')"));
4456 QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id, txt) VALUES (2, 'upper')"));
4457
4458 QVERIFY_SQL(q, exec("SELECT MAX(txt) FROM " + tableName));
4459 QVERIFY(q.next());
4460 QCOMPARE(q.value(0).toString(), QLatin1String("upper"));
4461 QCOMPARE(q.record().field(0).type(), QVariant::String);
4462 }
4463}
4464
4465template<typename T>
4466void runIntegralTypesMysqlTest(QSqlDatabase &db, const QString &tableName,
4467 const QString &type, bool withPreparedStatement,
4468 const QVector<T> &values)
4469{
4470 QVector<QVariant> variantValues;
4471 variantValues.reserve(asize: values.size());
4472
4473 QSqlQuery q(db);
4474 QVERIFY_SQL(q, exec("DROP TABLE IF EXISTS " + tableName));
4475 QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id " + type + ')'));
4476
4477 if (withPreparedStatement) {
4478 QVERIFY_SQL(q, prepare("INSERT INTO " + tableName + " (id) VALUES (?)"));
4479 }
4480 for (int i = 0; i < values.size(); ++i) {
4481 const T v = values.at(i);
4482 if (withPreparedStatement) {
4483 q.bindValue(0, v);
4484 QVERIFY_SQL(q, exec());
4485 } else {
4486 QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id) VALUES (" + QString::number(v) + QLatin1Char(')')));
4487 }
4488 variantValues.append(QVariant::fromValue(v));
4489 }
4490
4491 // ensure we can read them back properly
4492 if (withPreparedStatement) {
4493 QVERIFY_SQL(q, prepare("SELECT id FROM " + tableName));
4494 QVERIFY_SQL(q, exec());
4495 } else {
4496 QVERIFY_SQL(q, exec("SELECT id FROM " + tableName));
4497 }
4498 QVector<T> actualValues;
4499 QVector<QVariant> actualVariantValues;
4500 actualValues.reserve(values.size());
4501 while (q.next()) {
4502 QVariant value = q.value(i: 0);
4503 actualVariantValues << value;
4504 actualValues << value.value<T>();
4505 QVERIFY(actualVariantValues.last().userType() != qMetaTypeId<char>());
4506 QVERIFY(actualVariantValues.last().userType() != qMetaTypeId<signed char>());
4507 QVERIFY(actualVariantValues.last().userType() != qMetaTypeId<unsigned char>());
4508 }
4509 QCOMPARE(actualValues, values);
4510 QCOMPARE(actualVariantValues, variantValues);
4511}
4512
4513template<typename T>
4514void runIntegralTypesMysqlTest(QSqlDatabase &db, const QString &tableName,
4515 const QString &type, const bool withPreparedStatement,
4516 const T min = std::numeric_limits<T>::min(),
4517 const T max = std::numeric_limits<T>::max())
4518{
4519 // insert some values
4520 const int steps = 20;
4521 const T increment = (max / steps - min / steps);
4522 QVector<T> values;
4523 values.reserve(steps);
4524 T v = min;
4525 for (int i = 0; i < steps; ++i, v += increment)
4526 values.append(v);
4527 runIntegralTypesMysqlTest(db, tableName, type, withPreparedStatement, values);
4528}
4529
4530void tst_QSqlQuery::integralTypesMysql()
4531{
4532 QFETCH(QString, dbName);
4533 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4534 CHECK_DATABASE(db);
4535
4536 const QVector<bool> boolValues = QVector<bool>() << false << true;
4537 for (int i = 0; i < 2; ++i) {
4538 const bool withPreparedStatement = (i == 1);
4539 runIntegralTypesMysqlTest<bool>(db, tableName: "tinyInt1Test", type: "TINYINT(1)", withPreparedStatement, values: boolValues);
4540 runIntegralTypesMysqlTest<bool>(db, tableName: "unsignedTinyInt1Test", type: "TINYINT(1) UNSIGNED", withPreparedStatement, values: boolValues);
4541 runIntegralTypesMysqlTest<qint8>(db, tableName: "tinyIntTest", type: "TINYINT", withPreparedStatement);
4542 runIntegralTypesMysqlTest<quint8>(db, tableName: "unsignedTinyIntTest", type: "TINYINT UNSIGNED", withPreparedStatement);
4543 runIntegralTypesMysqlTest<qint16>(db, tableName: "smallIntTest", type: "SMALLINT", withPreparedStatement);
4544 runIntegralTypesMysqlTest<quint16>(db, tableName: "unsignedSmallIntTest", type: "SMALLINT UNSIGNED", withPreparedStatement);
4545 runIntegralTypesMysqlTest<qint32>(db, tableName: "mediumIntTest", type: "MEDIUMINT", withPreparedStatement, min: -(1 << 23), max: (1 << 23) - 1);
4546 runIntegralTypesMysqlTest<quint32>(db, tableName: "unsignedMediumIntTest", type: "MEDIUMINT UNSIGNED", withPreparedStatement, min: 0, max: (1 << 24) - 1);
4547 runIntegralTypesMysqlTest<qint32>(db, tableName: "intTest", type: "INT", withPreparedStatement);
4548 runIntegralTypesMysqlTest<quint32>(db, tableName: "unsignedIntTest", type: "INT UNSIGNED", withPreparedStatement);
4549 runIntegralTypesMysqlTest<qint64>(db, tableName: "bigIntTest", type: "BIGINT", withPreparedStatement);
4550 runIntegralTypesMysqlTest<quint64>(db, tableName: "unsignedBigIntTest", type: "BIGINT UNSIGNED", withPreparedStatement);
4551 }
4552}
4553
4554void tst_QSqlQuery::QTBUG_57138()
4555{
4556 QDateTime utc = QDateTime(QDate(2150, 1, 5), QTime(14, 0, 0, 123), Qt::UTC);
4557 QDateTime localtime = QDateTime(QDate(2150, 1, 5), QTime(14, 0, 0, 123), Qt::LocalTime);
4558 QDateTime tzoffset = QDateTime(QDate(2150, 1, 5), QTime(14, 0, 0, 123), Qt::OffsetFromUTC, 3600);
4559
4560 QFETCH(QString, dbName);
4561 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4562 CHECK_DATABASE(db);
4563
4564 QSqlQuery create(db);
4565 QString tableName = qTableName(prefix: "qtbug57138", __FILE__, db);
4566 tst_Databases::safeDropTable(db, tableName);
4567
4568 QVERIFY_SQL(create, exec("create table " + tableName + " (id int, dt_utc datetime, dt_lt datetime, dt_tzoffset datetime)"));
4569 QVERIFY_SQL(create, prepare("insert into " + tableName + " (id, dt_utc, dt_lt, dt_tzoffset) values (?, ?, ?, ?)"));
4570
4571 create.addBindValue(val: 0);
4572 create.addBindValue(val: utc);
4573 create.addBindValue(val: localtime);
4574 create.addBindValue(val: tzoffset);
4575
4576 QVERIFY_SQL(create, exec());
4577
4578 QSqlQuery q(db);
4579 q.prepare(query: "SELECT dt_utc, dt_lt, dt_tzoffset FROM " + tableName + " WHERE id = ?");
4580 q.addBindValue(val: 0);
4581
4582 QVERIFY_SQL(q, exec());
4583 QVERIFY(q.next());
4584
4585 QCOMPARE(q.value(0).toDateTime(), utc);
4586 QCOMPARE(q.value(1).toDateTime(), localtime);
4587 QCOMPARE(q.value(2).toDateTime(), tzoffset);
4588}
4589
4590void tst_QSqlQuery::QTBUG_73286()
4591{
4592 QFETCH(QString, dbName);
4593 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4594 CHECK_DATABASE(db);
4595
4596 QSqlQuery create(db);
4597 QString tableName = qTableName(prefix: "qtbug73286", __FILE__, db);
4598 tst_Databases::safeDropTable(db, tableName);
4599
4600 QVERIFY_SQL(create, exec("create table " + tableName + " (dec2 decimal(4,2), dec0 decimal(20,0), dec3 decimal(20,3))"));
4601 QVERIFY_SQL(create, prepare("insert into " + tableName + " (dec2, dec0, dec3) values (?, ?, ?)"));
4602
4603 create.addBindValue(val: "99.99");
4604 create.addBindValue(val: "12345678901234567890");
4605 create.addBindValue(val: "12345678901234567.890");
4606
4607 QVERIFY_SQL(create, exec());
4608
4609 QSqlQuery q(db);
4610 q.prepare(query: "SELECT dec2, dec0, dec3 FROM " + tableName);
4611 q.setNumericalPrecisionPolicy(QSql::HighPrecision);
4612
4613 QVERIFY_SQL(q, exec());
4614 QVERIFY(q.next());
4615
4616 QCOMPARE(q.value(0).toString(), "99.99");
4617 QCOMPARE(q.value(1).toString(), "12345678901234567890");
4618 QCOMPARE(q.value(2).toString(), "12345678901234567.890");
4619}
4620
4621void tst_QSqlQuery::dateTime_data()
4622{
4623 QTest::addColumn<QString>(name: "dbName");
4624 QTest::addColumn<QString>(name: "tableName");
4625 QTest::addColumn<QString>(name: "createTableString");
4626 QTest::addColumn<QList<QDateTime> >(name: "initialDateTimes");
4627 QTest::addColumn<QList<QDateTime> >(name: "expectedDateTimes");
4628
4629#if QT_CONFIG(timezone)
4630 // Using time zones which are highly unlikely to be the same as the testing machine's one
4631 // as it could pass as a result despite it.
4632 // +8.5 hours from UTC to North Korea
4633 const QTimeZone afterUTCTimeZone(30600);
4634 // -8 hours from UTC to Belize
4635 const QTimeZone beforeUTCTimeZone(-28800);
4636 const QTimeZone utcTimeZone("UTC");
4637
4638 const QDateTime dtWithAfterTZ(QDate(2015, 5, 18), QTime(4, 26, 30, 500), afterUTCTimeZone);
4639 const QDateTime dtWithBeforeTZ(QDate(2015, 5, 18), QTime(4, 26, 30, 500), beforeUTCTimeZone);
4640 const QDateTime dtWithUTCTZ(QDate(2015, 5, 18), QTime(4, 26, 30, 500), utcTimeZone);
4641#endif
4642 const QDateTime dt(QDate(2015, 5, 18), QTime(4, 26, 30));
4643 const QDateTime dtWithMS(QDate(2015, 5, 18), QTime(4, 26, 30, 500));
4644 const QList<QDateTime> dateTimes = {
4645 dt, dtWithMS,
4646#if QT_CONFIG(timezone)
4647 dtWithAfterTZ, dtWithBeforeTZ, dtWithUTCTZ
4648#endif
4649 };
4650 const QList<QDateTime> expectedDateTimesLocalTZ = {
4651 dt, dtWithMS,
4652#if QT_CONFIG(timezone)
4653 dtWithAfterTZ.toLocalTime(), dtWithBeforeTZ.toLocalTime(), dtWithUTCTZ.toLocalTime()
4654#endif
4655 };
4656 const QList<QDateTime> expectedTimeStampDateTimes = {
4657 dt, dtWithMS,
4658#if QT_CONFIG(timezone)
4659 dtWithMS, dtWithMS, dtWithMS
4660#endif
4661 };
4662 const QList<QDateTime> expectedDateTimes = {
4663 dt, dt,
4664#if QT_CONFIG(timezone)
4665 dt, dt, dt
4666#endif
4667 };
4668
4669 for (const QString &dbName : qAsConst(t&: dbs.dbNames)) {
4670 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4671 if (!db.isValid())
4672 continue;
4673 const QString tableNameTSWithTimeZone(qTableName(prefix: "dateTimeTSWithTimeZone", __FILE__, db));
4674 const QString tableNameTSWithLocalTimeZone(qTableName(prefix: "dateTimeTSWithLocalTimeZone", __FILE__, db));
4675 const QString tableNameTS(qTableName(prefix: "dateTimeTS", __FILE__, db));
4676 const QString tableNameDate(qTableName(prefix: "dateTimeDate", __FILE__, db));
4677 QTest::newRow(dataTag: QString(dbName + " timestamp with time zone").toLatin1())
4678 << dbName << tableNameTSWithTimeZone
4679 << QStringLiteral(" (dt TIMESTAMP WITH TIME ZONE)")
4680 << dateTimes << dateTimes;
4681 QTest::newRow(dataTag: QString(dbName + " timestamp with local time zone").toLatin1())
4682 << dbName << tableNameTSWithTimeZone
4683 << QStringLiteral(" (dt TIMESTAMP WITH LOCAL TIME ZONE)")
4684 << dateTimes << expectedDateTimesLocalTZ;
4685 QTest::newRow(dataTag: QString(dbName + "timestamp").toLatin1())
4686 << dbName << tableNameTS << QStringLiteral(" (dt TIMESTAMP(3))")
4687 << dateTimes << expectedTimeStampDateTimes;
4688 QTest::newRow(dataTag: QString(dbName + "date").toLatin1())
4689 << dbName << tableNameDate << QStringLiteral(" (dt DATE)")
4690 << dateTimes << expectedDateTimes;
4691 }
4692}
4693
4694void tst_QSqlQuery::dateTime()
4695{
4696 QFETCH(QString, dbName);
4697 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4698 CHECK_DATABASE(db);
4699
4700 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
4701 if (dbType != QSqlDriver::Oracle)
4702 QSKIP("Implemented only for Oracle");
4703
4704 QFETCH(QString, tableName);
4705 QFETCH(QString, createTableString);
4706 QFETCH(QList<QDateTime>, initialDateTimes);
4707 QFETCH(QList<QDateTime>, expectedDateTimes);
4708
4709 tst_Databases::safeDropTable(db, tableName);
4710
4711 QSqlQuery q(db);
4712 QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + createTableString));
4713 for (const QDateTime &dt : qAsConst(t&: initialDateTimes)) {
4714 QVERIFY_SQL(q, prepare("INSERT INTO " + tableName + " values(:dt)"));
4715 q.bindValue(placeholder: ":dt", val: dt);
4716 QVERIFY_SQL(q, exec());
4717 }
4718 QVERIFY_SQL(q, exec("SELECT * FROM " + tableName));
4719 for (const QDateTime &dt : qAsConst(t&: expectedDateTimes)) {
4720 QVERIFY(q.next());
4721 QCOMPARE(q.value(0).toDateTime(), dt);
4722 }
4723}
4724
4725void tst_QSqlQuery::sqliteVirtualTable()
4726{
4727 // Virtual tables can behave differently when it comes to prepared
4728 // queries, so we need to check these explicitly
4729 QFETCH(QString, dbName);
4730 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4731 CHECK_DATABASE(db);
4732 const auto tableName = qTableName(prefix: "sqliteVirtual", __FILE__, db);
4733 QSqlQuery qry(db);
4734 QVERIFY_SQL(qry, exec("create virtual table " + tableName + " using fts3(id, name)"));
4735
4736 // Delibrately malform the query to try and provoke a potential crash situation
4737 QVERIFY_SQL(qry, prepare("select * from " + tableName + " where name match '?'"));
4738 qry.addBindValue(val: "Andy");
4739 QVERIFY(!qry.exec());
4740
4741 QVERIFY_SQL(qry, prepare("insert into " + tableName + "(id, name) VALUES (?, ?)"));
4742 qry.addBindValue(val: 1);
4743 qry.addBindValue(val: "Andy");
4744 QVERIFY_SQL(qry, exec());
4745
4746 QVERIFY_SQL(qry, exec("select * from " + tableName));
4747 QVERIFY(qry.next());
4748 QCOMPARE(qry.value(0).toInt(), 1);
4749 QCOMPARE(qry.value(1).toString(), "Andy");
4750
4751 QVERIFY_SQL(qry, prepare("insert into " + tableName + "(id, name) values (:id, :name)"));
4752 qry.bindValue(placeholder: ":id", val: 2);
4753 qry.bindValue(placeholder: ":name", val: "Peter");
4754 QVERIFY_SQL(qry, exec());
4755
4756 QVERIFY_SQL(qry, prepare("select * from " + tableName + " where name match ?"));
4757 qry.addBindValue(val: "Peter");
4758 QVERIFY_SQL(qry, exec());
4759 QVERIFY(qry.next());
4760 QCOMPARE(qry.value(0).toInt(), 2);
4761 QCOMPARE(qry.value(1).toString(), "Peter");
4762}
4763
4764void tst_QSqlQuery::mysql_timeType()
4765{
4766 // The TIME data type is different to the standard with MySQL as it has a range of
4767 // '-838:59:59' to '838:59:59'.
4768 QFETCH(QString, dbName);
4769 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4770 CHECK_DATABASE(db);
4771 const auto tableName = qTableName(prefix: "mysqlTimeType", __FILE__, db);
4772 tst_Databases::safeDropTables(db, tableNames: { tableName });
4773 QSqlQuery qry(db);
4774 QVERIFY_SQL(qry, exec("create table " + tableName + " (t time(6))"));
4775
4776 // MySQL will convert days into hours and add them together so 17 days 11 hours becomes 419 hours
4777 const QStringList timeData = { "-838:59:59.000000", "-123:45:56.789", "000:00:00.0", "123:45:56.789",
4778 "838:59:59.000000", "15:50", "12", "1213", "0 1:2:3", "17 11:22:33" };
4779 const QStringList resultTimeData = { "-838:59:59.000000", "-123:45:56.789000", "00:00:00.000000",
4780 "123:45:56.789000", "838:59:59.000000", "15:50:00.000000", "00:00:12.000000", "00:12:13.000000",
4781 "01:02:03.000000", "419:22:33.000000" };
4782 for (const QString &time : timeData)
4783 QVERIFY_SQL(qry, exec("insert into " + tableName + " (t) VALUES ('" + time + "')"));
4784
4785 QVERIFY_SQL(qry, exec("select * from " + tableName));
4786 for (const QString &time : qAsConst(t: resultTimeData)) {
4787 QVERIFY(qry.next());
4788 QCOMPARE(qry.value(0).toString(), time);
4789 }
4790
4791 QVERIFY_SQL(qry, exec("delete from " + tableName));
4792 for (const QString &time : timeData) {
4793 QVERIFY_SQL(qry, prepare("insert into " + tableName + " (t) VALUES (:time)"));
4794 qry.bindValue(pos: 0, val: time);
4795 QVERIFY_SQL(qry, exec());
4796 }
4797 QVERIFY_SQL(qry, exec("select * from " + tableName));
4798 for (const QString &time : resultTimeData) {
4799 QVERIFY(qry.next());
4800 QCOMPARE(qry.value(0).toString(), time);
4801 }
4802
4803 QVERIFY_SQL(qry, exec("delete from " + tableName));
4804 const QList<QTime> qTimeBasedData = { QTime(), QTime(1, 2, 3, 4), QTime(0, 0, 0, 0), QTime(23,59,59,999) };
4805 for (const QTime &time : qTimeBasedData) {
4806 QVERIFY_SQL(qry, prepare("insert into " + tableName + " (t) VALUES (:time)"));
4807 qry.bindValue(pos: 0, val: time);
4808 QVERIFY_SQL(qry, exec());
4809 }
4810 QVERIFY_SQL(qry, exec("select * from " + tableName));
4811 for (const QTime &time : qTimeBasedData) {
4812 QVERIFY(qry.next());
4813 QCOMPARE(qry.value(0).toTime(), time);
4814 }
4815}
4816
4817void tst_QSqlQuery::ibaseArray()
4818{
4819 QFETCH(QString, dbName);
4820 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4821 CHECK_DATABASE(db);
4822
4823 const auto arrayTable = qTableName(prefix: "ibasearray", __FILE__, db);
4824 tst_Databases::safeDropTable(db, tableName: arrayTable);
4825 QSqlQuery qry(db);
4826 QVERIFY_SQL(qry, exec("create table " + arrayTable + " (intData int[0:4], longData bigint[5], "
4827 "charData varchar(255)[5])"));
4828 QVERIFY_SQL(qry, prepare("insert into " + arrayTable + " (intData, longData, charData) "
4829 "values(?, ?, ?)"));
4830 const auto intArray = QVariant{QVariantList{1, 2, 3, 4711, 815}};
4831 const auto charArray = QVariant{QVariantList{"AAA", "BBB", "CCC", "DDD", "EEE"}};
4832 qry.bindValue(pos: 0, val: intArray);
4833 qry.bindValue(pos: 1, val: intArray);
4834 qry.bindValue(pos: 2, val: charArray);
4835 QVERIFY_SQL(qry, exec());
4836 QVERIFY_SQL(qry, exec("select * from " + arrayTable));
4837 QVERIFY(qry.next());
4838 QCOMPARE(qry.value(0).toList(), intArray.toList());
4839 QCOMPARE(qry.value(1).toList(), intArray.toList());
4840 QCOMPARE(qry.value(2).toList(), charArray.toList());
4841}
4842
4843void tst_QSqlQuery::ibase_executeBlock()
4844{
4845 QFETCH(QString, dbName);
4846 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
4847 CHECK_DATABASE(db);
4848 QSqlQuery qry(db);
4849 QVERIFY_SQL(qry, prepare("execute block (x double precision = ?, y double precision = ?) "
4850 "returns (total double precision) "
4851 "as "
4852 "begin "
4853 "total = :x + :y; "
4854 "suspend; "
4855 "end"));
4856 qry.bindValue(pos: 0, val: 2);
4857 qry.bindValue(pos: 1, val: 2);
4858 QVERIFY_SQL(qry, exec());
4859 QVERIFY(qry.next());
4860 QCOMPARE(qry.value(0).toInt(), 4);
4861}
4862
4863QTEST_MAIN( tst_QSqlQuery )
4864#include "tst_qsqlquery.moc"
4865

source code of qtbase/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp