diff options
Diffstat (limited to 'tests/auto/sql/kernel')
26 files changed, 1461 insertions, 1192 deletions
diff --git a/tests/auto/sql/kernel/CMakeLists.txt b/tests/auto/sql/kernel/CMakeLists.txt index da9577aea0..d51cb75f31 100644 --- a/tests/auto/sql/kernel/CMakeLists.txt +++ b/tests/auto/sql/kernel/CMakeLists.txt @@ -1,14 +1,14 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from kernel.pro. - add_subdirectory(qsqlfield) add_subdirectory(qsqldatabase) add_subdirectory(qsqlerror) add_subdirectory(qsqldriver) +add_subdirectory(qsqlindex) add_subdirectory(qsqlquery) add_subdirectory(qsqlrecord) add_subdirectory(qsqlthread) add_subdirectory(qsql) add_subdirectory(qsqlresult) +add_subdirectory(qvfssql) diff --git a/tests/auto/sql/kernel/qsql/CMakeLists.txt b/tests/auto/sql/kernel/qsql/CMakeLists.txt index 2a74dc059b..8e0448a786 100644 --- a/tests/auto/sql/kernel/qsql/CMakeLists.txt +++ b/tests/auto/sql/kernel/qsql/CMakeLists.txt @@ -1,12 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from qsql.pro. - ##################################################################### ## tst_qsql Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsql LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsql SOURCES tst_qsql.cpp diff --git a/tests/auto/sql/kernel/qsql/tst_qsql.cpp b/tests/auto/sql/kernel/qsql/tst_qsql.cpp index 1d6b271f44..7b6e260ebf 100644 --- a/tests/auto/sql/kernel/qsql/tst_qsql.cpp +++ b/tests/auto/sql/kernel/qsql/tst_qsql.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -19,11 +19,6 @@ class tst_QSql : public QObject { Q_OBJECT -public: - tst_QSql(); - virtual ~tst_QSql(); - - public slots: void initTestCase(); void cleanupTestCase(); @@ -41,15 +36,6 @@ private slots: }; /****************** General Qt SQL Module tests *****************/ - -tst_QSql::tst_QSql() -{ -} - -tst_QSql::~tst_QSql() -{ -} - void tst_QSql::initTestCase() { } @@ -80,7 +66,7 @@ void tst_QSql::basicDriverTest() tst_Databases dbs; QVERIFY(dbs.open()); - foreach (const QString& dbName, dbs.dbNames) { + for (const QString &dbName : std::as_const(dbs.dbNames)) { QSqlDatabase db = QSqlDatabase::database(dbName); QVERIFY_SQL(db, isValid()); @@ -125,16 +111,20 @@ void tst_QSql::open() QVERIFY(dbs.open()); if (count == -1) // first iteration: see how many dbs are open - count = (int) dbs.dbNames.count(); + count = (int) dbs.dbNames.size(); else // next iterations: make sure all are opened again - QCOMPARE(count, (int)dbs.dbNames.count()); + QCOMPARE(count, (int)dbs.dbNames.size()); dbs.close(); } } void tst_QSql::openInvalid() { + int argc = 1; + char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; + QCoreApplication app(argc, argv, false); + QSqlDatabase db; QVERIFY(!db.open()); @@ -150,7 +140,7 @@ void tst_QSql::concurrentAccess() tst_Databases dbs; QVERIFY(dbs.open()); - foreach (const QString& dbName, dbs.dbNames) { + for (const QString &dbName : std::as_const(dbs.dbNames)) { QSqlDatabase db = QSqlDatabase::database(dbName); QVERIFY(db.isValid()); if (tst_Databases::isMSAccess(db)) @@ -182,7 +172,7 @@ void tst_QSql::openErrorRecovery() QVERIFY(dbs.addDbs()); if (dbs.dbNames.isEmpty()) QSKIP("No database drivers installed"); - foreach (const QString& dbName, dbs.dbNames) { + for (const QString &dbName : std::as_const(dbs.dbNames)) { QSqlDatabase db = QSqlDatabase::database(dbName, false); CHECK_DATABASE(db); diff --git a/tests/auto/sql/kernel/qsqldatabase/CMakeLists.txt b/tests/auto/sql/kernel/qsqldatabase/CMakeLists.txt index 2fefb3b587..97dbf94af7 100644 --- a/tests/auto/sql/kernel/qsqldatabase/CMakeLists.txt +++ b/tests/auto/sql/kernel/qsqldatabase/CMakeLists.txt @@ -1,12 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from qsqldatabase.pro. - ##################################################################### ## tst_qsqldatabase Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsqldatabase LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsqldatabase SOURCES tst_qsqldatabase.cpp diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_databases.h b/tests/auto/sql/kernel/qsqldatabase/tst_databases.h index 5b0283d285..5a10bde814 100644 --- a/tests/auto/sql/kernel/qsqldatabase/tst_databases.h +++ b/tests/auto/sql/kernel/qsqldatabase/tst_databases.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only /* possible connection parameters */ #ifndef TST_DATABASES_H @@ -9,6 +9,7 @@ #include <QSqlDriver> #include <QSqlError> #include <QSqlQuery> +#include <QSqlRecord> #include <QRegularExpression> #include <QRegularExpressionMatch> #include <QDir> @@ -19,9 +20,13 @@ #include <QJsonArray> #include <QJsonObject> #include <QJsonDocument> +#include <QSysInfo> +#include <QVersionNumber> #include <QtSql/private/qsqldriver_p.h> #include <QTest> +using namespace Qt::StringLiterals; + #define CHECK_DATABASE( db ) \ if ( !db.isValid() ) { qFatal( "db is Invalid" ); } @@ -31,88 +36,26 @@ #define DBMS_SPECIFIC(db, driver) \ if (!db.driverName().startsWith(driver)) { QSKIP(driver " specific test"); } -// ### use QSystem::hostName if it is integrated in qtest/main -static QString qGetHostName() -{ - static QString hostname; - - if (hostname.isEmpty()) { - hostname = QSysInfo::machineHostName(); - hostname.replace(QLatin1Char( '.' ), QLatin1Char( '_' )); - hostname.replace(QLatin1Char( '-' ), QLatin1Char( '_' )); - } - - return hostname; -} - -inline QString fixupTableName(const QString &tableName, QSqlDatabase db) -{ - QString tbName = tableName; - // On Oracle we are limited to 30 character tablenames - QSqlDriverPrivate *d = static_cast<QSqlDriverPrivate *>(QObjectPrivate::get(db.driver())); - if (d && d->dbmsType == QSqlDriver::Oracle) - tbName.truncate(30); - // On Interbase we are limited to 31 character tablenames - if (d && d->dbmsType == QSqlDriver::Interbase) - tbName.truncate(31); - return tbName; -} - // to prevent nameclashes on our database server, each machine // will use its own set of table names. Call this function to get // "tablename_hostname" inline static QString qTableName(const QString &prefix, const char *sourceFileName, QSqlDatabase db, bool escape = true) { - const auto tableStr = fixupTableName(QString(QLatin1String("dbtst") + db.driverName() + "_" + - prefix + QString::number(qHash(QLatin1String(sourceFileName) + - "_" + qGetHostName().replace("-", "_")), 16)), db); + const auto hash = qHash(QLatin1String(sourceFileName) + '_' + + QSysInfo::machineHostName().replace('-', '_')); + auto tableStr = QLatin1String("dbtst") + db.driverName() + '_' + prefix + + QString::number(hash, 16); + // Oracle & Interbase/Firebird have a limit on the tablename length + QSqlDriver *drv = db.driver(); + if (drv) + tableStr.truncate(drv->maximumIdentifierLength(QSqlDriver::TableName)); return escape ? db.driver()->escapeIdentifier(tableStr, QSqlDriver::TableName) : tableStr; } -inline static QString qTableName(const QString& prefix, QSqlDatabase db) -{ - QString tableStr; - if (db.driverName().toLower().contains("ODBC")) - tableStr += QLatin1String("_odbc"); - return fixupTableName(QString(db.driver()->escapeIdentifier(prefix + tableStr + QLatin1Char('_') + - qGetHostName(), QSqlDriver::TableName)),db); -} - -inline static bool testWhiteSpaceNames( const QString &name ) -{ -/* return name.startsWith( "QPSQL" ) - || name.startsWith( "QODBC" ) - || name.startsWith( "QSQLITE" ) - || name.startsWith( "QMYSQL" );*/ - return name != QLatin1String("QSQLITE2"); -} - -inline static QString toHex( const QString& binary ) -{ - QString str; - static char const hexchars[] = "0123456789ABCDEF"; - - for ( int i = 0; i < binary.size(); i++ ) { - ushort code = binary.at(i).unicode(); - str += (QChar)(hexchars[ (code >> 12) & 0x0F ]); - str += (QChar)(hexchars[ (code >> 8) & 0x0F ]); - str += (QChar)(hexchars[ (code >> 4) & 0x0F ]); - str += (QChar)(hexchars[ code & 0x0F ]); - } - - return str; -} - - class tst_Databases { - public: - tst_Databases(): counter( 0 ) - { - } - ~tst_Databases() { close(); @@ -120,83 +63,84 @@ public: // returns a testtable consisting of the names of all database connections if // driverPrefix is empty, otherwise only those that start with driverPrefix. - int fillTestTable( const QString& driverPrefix = QString() ) const + int fillTestTable(const QString &driverPrefix = QString()) const { - QTest::addColumn<QString>( "dbName" ); + QTest::addColumn<QString>("dbName"); int count = 0; - for ( int i = 0; i < dbNames.count(); ++i ) { - QSqlDatabase db = QSqlDatabase::database( dbNames.at( i ) ); - - if ( !db.isValid() ) + for (const auto &dbName : std::as_const(dbNames)) { + QSqlDatabase db = QSqlDatabase::database(dbName); + if (!db.isValid()) continue; - - if ( driverPrefix.isEmpty() || db.driverName().startsWith( driverPrefix ) ) { - QTest::newRow( dbNames.at( i ).toLatin1() ) << dbNames.at( i ); + if (driverPrefix.isEmpty() || db.driverName().startsWith(driverPrefix)) { + QTest::newRow(dbName.toLatin1()) << dbName; ++count; } } - return count; } - int fillTestTableWithStrategies( const QString& driverPrefix = QString() ) const + int fillTestTableWithStrategies(const QString &driverPrefix = QString()) const { - QTest::addColumn<QString>( "dbName" ); - QTest::addColumn<int>("submitpolicy_i"); + QTest::addColumn<QString>("dbName"); + QTest::addColumn<QSqlTableModel::EditStrategy>("submitpolicy"); int count = 0; - for ( int i = 0; i < dbNames.count(); ++i ) { - QSqlDatabase db = QSqlDatabase::database( dbNames.at( i ) ); - - if ( !db.isValid() ) + for (const auto &dbName : std::as_const(dbNames)) { + QSqlDatabase db = QSqlDatabase::database(dbName); + if (!db.isValid()) continue; if ( driverPrefix.isEmpty() || db.driverName().startsWith( driverPrefix ) ) { - QTest::newRow( QString("%1 [field]").arg(dbNames.at( i )).toLatin1() ) << dbNames.at( i ) << (int)QSqlTableModel::OnFieldChange; - QTest::newRow( QString("%1 [row]").arg(dbNames.at( i )).toLatin1() ) << dbNames.at( i ) << (int)QSqlTableModel::OnRowChange; - QTest::newRow( QString("%1 [manual]").arg(dbNames.at( i )).toLatin1() ) << dbNames.at( i ) << (int)QSqlTableModel::OnManualSubmit; + QTest::newRow(QString("%1 [field]").arg(dbName).toLatin1() ) << dbName << QSqlTableModel::OnFieldChange; + QTest::newRow(QString("%1 [row]").arg(dbName).toLatin1() ) << dbName << QSqlTableModel::OnRowChange; + QTest::newRow(QString("%1 [manual]").arg(dbName).toLatin1() ) << dbName << QSqlTableModel::OnManualSubmit; ++count; } } - return count; } - void addDb( const QString& driver, const QString& dbName, - const QString& user = QString(), const QString& passwd = QString(), - const QString& host = QString(), int port = -1, const QString params = QString() ) + void addDb(const QString &driver, const QString &dbName, + const QString &user = QString(), const QString &passwd = QString(), + const QString &host = QString(), int port = -1, const QString ¶ms = QString()) { - QSqlDatabase db; - - if ( !QSqlDatabase::drivers().contains( driver ) ) { + if (!QSqlDatabase::drivers().contains(driver)) { qWarning() << "Driver" << driver << "is not installed"; return; } // construct a stupid unique name - QString cName = QString::number( counter++ ) + QLatin1Char('_') + driver + QLatin1Char('@'); + QString cName = QString::number(counter++) + QLatin1Char('_') + driver + QLatin1Char('@'); cName += host.isEmpty() ? dbName : host; - if ( port > 0 ) - cName += QLatin1Char(':') + QString::number( port ); - - db = QSqlDatabase::addDatabase( driver, cName ); + if (port > 0) + cName += QLatin1Char(':') + QString::number(port); + + QString opts = params; + if (driver == "QSQLITE") { + // Since the database for sqlite is generated at runtime it's always + // available, but we use QTempDir so it's always in a different + // location. Thus, let's ignore the path completely. + cName = "SQLite"; + qInfo("SQLite will use the database located at %ls", qUtf16Printable(dbName)); + opts += QStringLiteral(";QSQLITE_ENABLE_NON_ASCII_CASE_FOLDING"); + } - if ( !db.isValid() ) { + auto db = QSqlDatabase::addDatabase(driver, cName); + if (!db.isValid()) { qWarning( "Could not create database object" ); return; } - db.setDatabaseName( dbName ); - - db.setUserName( user ); - db.setPassword( passwd ); - db.setHostName( host ); - db.setPort( port ); - db.setConnectOptions( params ); - dbNames.append( cName ); + db.setDatabaseName(dbName); + db.setUserName(user); + db.setPassword(passwd); + db.setHostName(host); + db.setPort(port); + db.setConnectOptions(opts); + dbNames.append(cName); } bool addDbs() @@ -231,10 +175,9 @@ public: qWarning() << "No entries in " + f.fileName(); } else { const QJsonArray entriesA = entriesV.toArray(); - QJsonArray::const_iterator it = entriesA.constBegin(); - while (it != entriesA.constEnd()) { - if ((*it).isObject()) { - const QJsonObject object = (*it).toObject(); + for (const auto &elem : entriesA) { + if (elem.isObject()) { + const QJsonObject object = elem.toObject(); addDb(object.value(QStringLiteral("driver")).toString(), object.value(QStringLiteral("name")).toString(), object.value(QStringLiteral("username")).toString(), @@ -244,13 +187,12 @@ public: object.value(QStringLiteral("parameters")).toString()); added = true; } - ++it; } } } QTemporaryDir *sqLiteDir = dbDir(); if (sqLiteDir) { - addDb(QStringLiteral("QSQLITE"), QDir::toNativeSeparators(sqLiteDir->path() + QStringLiteral("/foo.db"))); + addDb(QStringLiteral("QSQLITE"), QDir::toNativeSeparators(sqLiteDir->path() + QStringLiteral("/sqlite.db"))); added = true; } return added; @@ -262,17 +204,18 @@ public: if (!addDbs()) return false; - QStringList::Iterator it = dbNames.begin(); + auto it = dbNames.begin(); + while (it != dbNames.end()) { + const auto &dbName = *it; + QSqlDatabase db = QSqlDatabase::database(dbName, false ); + qDebug() << "Opening:" << dbName; - while ( it != dbNames.end() ) { - QSqlDatabase db = QSqlDatabase::database(( *it ), false ); - qDebug() << "Opening:" << (*it); - - if ( db.isValid() && !db.isOpen() ) { - if ( !db.open() ) { - qWarning( "tst_Databases: Unable to open %s on %s:\n%s", qPrintable( db.driverName() ), qPrintable( *it ), qPrintable( db.lastError().databaseText() ) ); + if (db.isValid() && !db.isOpen()) { + if (!db.open()) { + qWarning("tst_Databases: Unable to open %s on %s:\n%s", qPrintable(db.driverName()), + qPrintable(dbName), qPrintable(db.lastError().databaseText())); // well... opening failed, so we just ignore the server, maybe it is not running - it = dbNames.erase( it ); + it = dbNames.erase(it); } else { ++it; } @@ -283,57 +226,51 @@ public: void close() { - for ( QStringList::Iterator it = dbNames.begin(); it != dbNames.end(); ++it ) { + for (const auto &dbName : std::as_const(dbNames)) { { - QSqlDatabase db = QSqlDatabase::database(( *it ), false ); - - if ( db.isValid() && db.isOpen() ) + QSqlDatabase db = QSqlDatabase::database(dbName, false); + if (db.isValid() && db.isOpen()) db.close(); } - - QSqlDatabase::removeDatabase(( *it ) ); + QSqlDatabase::removeDatabase(dbName); } - dbNames.clear(); } // for debugging only: outputs the connection as string - static QString dbToString( const QSqlDatabase db ) + static QString dbToString(const QSqlDatabase &db) { QString res = db.driverName() + QLatin1Char('@'); - if ( db.driverName().startsWith( "QODBC" ) || db.driverName().startsWith( "QOCI" ) ) { + if (db.driverName().startsWith("QODBC") || db.driverName().startsWith("QOCI")) res += db.databaseName(); - } else { + else res += db.hostName(); - } - if ( db.port() > 0 ) { - res += QLatin1Char(':') + QString::number( db.port() ); - } + if (db.port() > 0) + res += QLatin1Char(':') + QString::number(db.port()); return res; } // drop a table only if it exists to prevent warnings - static void safeDropTables( QSqlDatabase db, const QStringList& tableNames ) + static void safeDropTables(const QSqlDatabase &db, const QStringList &tableNames) { - bool wasDropped; - QSqlQuery q( db ); - QStringList dbtables=db.tables(); + QSqlQuery q(db); + QStringList dbtables = db.tables(); QSqlDriver::DbmsType dbType = getDatabaseType(db); - foreach(const QString &tableName, tableNames) + for (const QString &tableName : tableNames) { - wasDropped = true; - QString table=tableName; - if ( db.driver()->isIdentifierEscaped(table, QSqlDriver::TableName)) + bool wasDropped = true; + QString table = tableName; + if (db.driver()->isIdentifierEscaped(table, QSqlDriver::TableName)) table = db.driver()->stripDelimiters(table, QSqlDriver::TableName); - if ( dbtables.contains( table, Qt::CaseInsensitive ) ) { - foreach(const QString &table2, dbtables.filter(table, Qt::CaseInsensitive)) { - if(table2.compare(table.section('.', -1, -1), Qt::CaseInsensitive) == 0) { - table=db.driver()->escapeIdentifier(table2, QSqlDriver::TableName); - if (dbType == QSqlDriver::PostgreSQL) + if (dbtables.contains(table, Qt::CaseInsensitive)) { + for (const QString &table2 : dbtables.filter(table, Qt::CaseInsensitive)) { + if (table2.compare(table.section('.', -1, -1), Qt::CaseInsensitive) == 0) { + table = db.driver()->escapeIdentifier(table2, QSqlDriver::TableName); + if (dbType == QSqlDriver::PostgreSQL || dbType == QSqlDriver::MimerSQL) wasDropped = q.exec( "drop table " + table + " cascade"); else wasDropped = q.exec( "drop table " + table); @@ -341,7 +278,7 @@ public: } } } - if ( !wasDropped ) { + if (!wasDropped) { qWarning() << dbToString(db) << "unable to drop table" << tableName << ':' << q.lastError(); // qWarning() << "last query:" << q.lastQuery(); // qWarning() << "dbtables:" << dbtables; @@ -350,38 +287,31 @@ public: } } - static void safeDropTable( QSqlDatabase db, const QString& tableName ) + static void safeDropViews(const QSqlDatabase &db, const QStringList &viewNames) { - safeDropTables(db, QStringList() << tableName); - } + if (isMSAccess(db)) // Access is sooo stupid. + safeDropTables(db, viewNames); - static void safeDropViews( QSqlDatabase db, const QStringList &viewNames ) - { - if ( isMSAccess( db ) ) // Access is sooo stupid. - safeDropTables( db, viewNames ); - - bool wasDropped; - QSqlQuery q( db ); - QStringList dbtables=db.tables(QSql::Views); - - foreach(QString viewName, viewNames) + QSqlQuery q(db); + QStringList dbtables = db.tables(QSql::Views); + for (const QString &viewName : viewNames) { - wasDropped = true; - QString view=viewName; - if ( db.driver()->isIdentifierEscaped(view, QSqlDriver::TableName)) + bool wasDropped = true; + QString view = viewName; + if (db.driver()->isIdentifierEscaped(view, QSqlDriver::TableName)) view = db.driver()->stripDelimiters(view, QSqlDriver::TableName); - if ( dbtables.contains( view, Qt::CaseInsensitive ) ) { - foreach(const QString &view2, dbtables.filter(view, Qt::CaseInsensitive)) { - if(view2.compare(view.section('.', -1, -1), Qt::CaseInsensitive) == 0) { - view=db.driver()->escapeIdentifier(view2, QSqlDriver::TableName); - wasDropped = q.exec( "drop view " + view); + if (dbtables.contains(view, Qt::CaseInsensitive)) { + for (const QString &view2 : dbtables.filter(view, Qt::CaseInsensitive)) { + if (view2.compare(view.section('.', -1, -1), Qt::CaseInsensitive) == 0) { + view = db.driver()->escapeIdentifier(view2, QSqlDriver::TableName); + wasDropped = q.exec("drop view " + view); dbtables.removeAll(view); } } } - if ( !wasDropped ) + if (!wasDropped) qWarning() << dbToString(db) << "unable to drop view" << viewName << ':' << q.lastError(); // << "\nlast query:" << q.lastQuery() // << "\ndbtables:" << dbtables @@ -389,14 +319,9 @@ public: } } - static void safeDropView( QSqlDatabase db, const QString& tableName ) - { - safeDropViews(db, QStringList() << tableName); - } - // returns the type name of the blob datatype for the database db. // blobSize is only used if the db doesn't have a generic blob type - static QString blobTypeName( QSqlDatabase db, int blobSize = 10000 ) + static QString blobTypeName(const QSqlDatabase &db, int blobSize = 10000) { const QSqlDriver::DbmsType dbType = getDatabaseType(db); if (dbType == QSqlDriver::MySqlServer) @@ -425,19 +350,35 @@ public: return "blob"; } - static QString dateTimeTypeName(QSqlDatabase db) + static QString dateTimeTypeName(const QSqlDatabase &db) { const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); if (dbType == QSqlDriver::PostgreSQL) return QLatin1String("timestamptz"); if (dbType == QSqlDriver::Oracle && getOraVersion(db) >= 9) return QLatin1String("timestamp(0)"); - if (dbType == QSqlDriver::Interbase) + if (dbType == QSqlDriver::Interbase || dbType == QSqlDriver::MimerSQL) return QLatin1String("timestamp"); return QLatin1String("datetime"); } - static QString autoFieldName( QSqlDatabase db ) + static QString timeTypeName(const QSqlDatabase &db) + { + const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); + if (dbType == QSqlDriver::Oracle && getOraVersion(db) >= 9) + return QLatin1String("timestamp(0)"); + return QLatin1String("time"); + } + + static QString dateTypeName(const QSqlDatabase &db) + { + const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); + if (dbType == QSqlDriver::Oracle && getOraVersion(db) >= 9) + return QLatin1String("timestamp(0)"); + return QLatin1String("date"); + } + + static QString autoFieldName(const QSqlDatabase &db) { const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); if (dbType == QSqlDriver::MySqlServer) @@ -452,43 +393,35 @@ public: return QString(); } - static QByteArray printError( const QSqlError& err ) + static QByteArray printError(const QSqlError &err) { QString result; if (!err.nativeErrorCode().isEmpty()) - result += '(' + err.nativeErrorCode() + ") "; - result += '\''; - if(!err.driverText().isEmpty()) + result += u'(' + err.nativeErrorCode() + ") "; + result += u'\''; + if (!err.driverText().isEmpty()) result += err.driverText() + "' || '"; - result += err.databaseText() + QLatin1Char('\''); + result += err.databaseText() + u'\''; return result.toLocal8Bit(); } - static QByteArray printError( const QSqlError& err, const QSqlDatabase& db ) + static QByteArray printError(const QSqlError &err, const QSqlDatabase &db) { - QString result(dbToString(db) + ": "); - if (!err.nativeErrorCode().isEmpty()) - result += '(' + err.nativeErrorCode() + ") "; - result += '\''; - if(!err.driverText().isEmpty()) - result += err.driverText() + "' || '"; - result += err.databaseText() + QLatin1Char('\''); - return result.toLocal8Bit(); + return dbToString(db).toLocal8Bit() + ": " + printError(err); } - static QSqlDriver::DbmsType getDatabaseType(QSqlDatabase db) + static QSqlDriver::DbmsType getDatabaseType(const QSqlDatabase &db) { - QSqlDriverPrivate *d = static_cast<QSqlDriverPrivate *>(QObjectPrivate::get(db.driver())); - return d->dbmsType; + return db.driver()->dbmsType(); } - static bool isMSAccess( QSqlDatabase db ) + static bool isMSAccess(const QSqlDatabase &db) { return db.databaseName().contains( "Access Driver", Qt::CaseInsensitive ); } // -1 on fail, else Oracle version - static int getOraVersion( QSqlDatabase db ) + static int getOraVersion(const QSqlDatabase &db) { int ver = -1; QSqlQuery q( "SELECT banner FROM v$version", db ); @@ -507,28 +440,18 @@ public: return ver; } - static QString getMySqlVersion( const QSqlDatabase &db ) + static QVersionNumber getIbaseEngineVersion(const QSqlDatabase &db) { QSqlQuery q(db); - q.exec( "select version()" ); - if(q.next()) - return q.value( 0 ).toString(); - else - return QString(); - } - - static QString getPSQLVersion( const QSqlDatabase &db ) - { - QSqlQuery q(db); - q.exec( "select version()" ); - if(q.next()) - return q.value( 0 ).toString(); - else - return QString(); + q.exec("SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION') as version from rdb$database;"_L1); + q.next(); + auto record = q.record(); + auto version = QVersionNumber::fromString(record.value(0).toString()); + return version; } QStringList dbNames; - int counter; + int counter = 0; private: QTemporaryDir *dbDir() @@ -546,5 +469,64 @@ private: QScopedPointer<QTemporaryDir> m_dbDir; }; +class TableScope +{ +public: + TableScope(const QSqlDatabase &db, const QString &fullTableName) + : m_db(db) + , m_tableName(fullTableName) + { + tst_Databases::safeDropTables(m_db, {m_tableName}); + } + TableScope(const QSqlDatabase &db, const char *tableName, const char *file, bool escape = true) + : TableScope(db, qTableName(tableName, file, db, escape)) + { + } + + ~TableScope() + { + tst_Databases::safeDropTables(m_db, {m_tableName}); + } + + QString tableName() const + { + return m_tableName; + } +private: + QSqlDatabase m_db; + QString m_tableName; +}; + +class ProcScope +{ +public: + ProcScope(const QSqlDatabase &db, const char *procName, const char *file) + : m_db(db), + m_procName(qTableName(procName, file, db)) + { + cleanup(); + } + ~ProcScope() + { + cleanup(); + } + QString name() const + { + return m_procName; + } +protected: + void cleanup() + { + QSqlQuery q(m_db); + if (m_db.driverName() == "QIBASE") + q.exec("DROP PROCEDURE " + m_procName); + else + q.exec("DROP PROCEDURE IF EXISTS " + m_procName); + } +private: + QSqlDatabase m_db; + const QString m_procName; +}; + #endif diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp index 69f65ad9ef..19afacf6f9 100644 --- a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp +++ b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -28,8 +28,7 @@ class tst_QSqlDatabase : public QObject Q_OBJECT public: - tst_QSqlDatabase(); - virtual ~tst_QSqlDatabase(); + using QObject::QObject; public slots: void initTestCase(); @@ -50,8 +49,6 @@ private slots: void eventNotification_data() { generic_data(); } void eventNotification(); void addDatabase(); - void errorReporting_data(); - void errorReporting(); void cloneDatabase_data() { generic_data(); } void cloneDatabase(); @@ -62,8 +59,6 @@ private slots: void recordPSQL(); void recordOCI_data() { generic_data("QOCI"); } void recordOCI(); - void recordTDS_data() { generic_data("QTDS"); } - void recordTDS(); void recordDB2_data() { generic_data("QDB2"); } void recordDB2(); void recordSQLite_data() { generic_data("QSQLITE"); } @@ -109,6 +104,8 @@ private slots: void infinityAndNan(); void multipleThreads_data() { generic_data(); } void multipleThreads(); + void moveToThread_data() { generic_data(); } + void moveToThread(); void db2_valueCacheUpdate_data() { generic_data("QDB2"); } void db2_valueCacheUpdate(); @@ -137,8 +134,6 @@ private slots: void ibase_numericFields(); // For task 125053 void ibase_fetchBlobs_data() { generic_data("QIBASE"); } void ibase_fetchBlobs(); // For task 143471 - void ibase_useCustomCharset_data() { generic_data("QIBASE"); } - void ibase_useCustomCharset(); // For task 134608 void ibase_procWithoutReturnValues_data() { generic_data("QIBASE"); } // For task 165423 void ibase_procWithoutReturnValues(); void ibase_procWithReturnValues_data() { generic_data("QIBASE"); } // For task 177530 @@ -182,13 +177,13 @@ private slots: void sqlite_check_json1(); private: - void createTestTables(QSqlDatabase db); - void dropTestTables(QSqlDatabase db); - void populateTestTables(QSqlDatabase db); + void createTestTables(const QSqlDatabase &db); + void dropTestTables(const QSqlDatabase &db); + void populateTestTables(const QSqlDatabase &db); void generic_data(const QString &engine=QString()); - void testRecord(const FieldDef fieldDefs[], const QSqlRecord& inf, QSqlDatabase db); - void commonFieldTest(const FieldDef fieldDefs[], QSqlDatabase, const int); + void testRecord(const FieldDef fieldDefs[], const QSqlRecord& inf, const QSqlDatabase &db); + void commonFieldTest(const FieldDef fieldDefs[], const QSqlDatabase &db, const int fieldCount); tst_Databases dbs; }; @@ -223,11 +218,11 @@ struct FieldDef { // creates a table out of the FieldDefs and returns the number of fields // excluding the primary key field -static int createFieldTable(const FieldDef fieldDefs[], QSqlDatabase db) +static int createFieldTable(const FieldDef fieldDefs[], const QSqlDatabase &db) { QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); const QString tableName = qTableName("qtestfields", __FILE__, db); - tst_Databases::safeDropTable(db, tableName); + tst_Databases::safeDropTables(db, {tableName}); QSqlQuery q(db); // construct a create table statement consisting of all fieldtypes QString qs = "create table " + tableName; @@ -258,22 +253,7 @@ static int createFieldTable(const FieldDef fieldDefs[], QSqlDatabase db) return i; } -bool driverQuotedCaseSensitive(QSqlDatabase db) -{ - // On Interbase it will be case sensitive if it was created with quotes - QSqlDriverPrivate *d = static_cast<QSqlDriverPrivate *>(QObjectPrivate::get(db.driver())); - return (d && d->dbmsType == QSqlDriver::Interbase); -} - -tst_QSqlDatabase::tst_QSqlDatabase() -{ -} - -tst_QSqlDatabase::~tst_QSqlDatabase() -{ -} - -void tst_QSqlDatabase::createTestTables(QSqlDatabase db) +void tst_QSqlDatabase::createTestTables(const QSqlDatabase &db) { if (!db.isValid()) return; @@ -300,16 +280,14 @@ void tst_QSqlDatabase::createTestTables(QSqlDatabase db) " (id integer not null, t_varchar varchar(40) not null, " "t_char char(40), t_numeric numeric(6, 3), primary key (id, t_varchar))")); } - if (testWhiteSpaceNames(db.driverName())) { - QString qry = "create table " + qTableName("qtest test", __FILE__, db) - + '(' - + db.driver()->escapeIdentifier(QLatin1String("test test"), QSqlDriver::FieldName) - + " int not null primary key)"; - QVERIFY_SQL(q, exec(qry)); - } + QString qry = "create table " + qTableName("qtest test", __FILE__, db) + + '(' + + db.driver()->escapeIdentifier(QLatin1String("test test"), QSqlDriver::FieldName) + + " int not null primary key)"; + QVERIFY_SQL(q, exec(qry)); } -void tst_QSqlDatabase::dropTestTables(QSqlDatabase db) +void tst_QSqlDatabase::dropTestTables(const QSqlDatabase &db) { if (!db.isValid()) return; @@ -321,31 +299,11 @@ void tst_QSqlDatabase::dropTestTables(QSqlDatabase db) } // drop the view first, otherwise we'll get dependency problems - tst_Databases::safeDropViews(db, QStringList() << qTableName("qtest_view", __FILE__, db) << qTableName("qtest_view2", __FILE__, db)); - const QString qtestTable = qTableName("qtest", __FILE__, db); - QStringList tableNames; - tableNames << qtestTable - << qTableName("qtest test", __FILE__, db) - << qTableName("qtestfields", __FILE__, db) - << qTableName("qtestalter", __FILE__, db) - << qTableName("qtest_temp", __FILE__, db) - << qTableName("qtest_bigint", __FILE__, db) - << qTableName("qtest_xmltype", __FILE__, db) - << qTableName("latin1table", __FILE__, db) - << qTableName("qtest_sqlguid", __FILE__, db) - << qTableName("batable", __FILE__, db) - << qTableName("qtest_prec", __FILE__, db) - << qTableName("uint", __FILE__, db) - << qTableName("strings", __FILE__, db) - << qTableName("numericfields", __FILE__, db) - << qTableName("qtest_ibaseblobs", __FILE__, db) - << qTableName("qtestBindBool", __FILE__, db) - << qTableName("testqGetString", __FILE__, db) - << qTableName("qtest_sqlguid", __FILE__, db) - << qTableName("uint_table", __FILE__, db) - << qTableName("uint_test", __FILE__, db) - << qTableName("bug_249059", __FILE__, db) - << qTableName("regexp_test", __FILE__, db); + tst_Databases::safeDropViews(db, {qTableName("qtest_view", __FILE__, db), + qTableName("qtest_view2", __FILE__, db)}); + QStringList tableNames = {qTableName("qtest", __FILE__, db), + qTableName("qtest test", __FILE__, db), + qTableName("qtestfields", __FILE__, db)}; QSqlQuery q(0, db); if (dbType == QSqlDriver::PostgreSQL) { @@ -353,11 +311,8 @@ void tst_QSqlDatabase::dropTestTables(QSqlDatabase db) q.exec("drop schema " + qTableName("qtestScHeMa", __FILE__, db) + " cascade"); } - if (testWhiteSpaceNames(db.driverName())) { - tableNames << db.driver()->escapeIdentifier(qTableName("qtest test", __FILE__, db), - QSqlDriver::TableName); - } - + tableNames << db.driver()->escapeIdentifier(qTableName("qtest test", __FILE__, db), + QSqlDriver::TableName); tst_Databases::safeDropTables(db, tableNames); if (dbType == QSqlDriver::Oracle) { @@ -368,7 +323,7 @@ void tst_QSqlDatabase::dropTestTables(QSqlDatabase db) } } -void tst_QSqlDatabase::populateTestTables(QSqlDatabase db) +void tst_QSqlDatabase::populateTestTables(const QSqlDatabase &db) { if (!db.isValid()) return; @@ -440,36 +395,6 @@ void tst_QSqlDatabase::addDatabase() QVERIFY(!QSqlDatabase::contains("INVALID_CONNECTION")); } -void tst_QSqlDatabase::errorReporting_data() -{ - QTest::addColumn<QString>("driver"); - - QTest::newRow("QTDS") << QString::fromLatin1("QTDS"); - QTest::newRow("QTDS7") << QString::fromLatin1("QTDS7"); -} - -void tst_QSqlDatabase::errorReporting() -{ - QFETCH(QString, driver); - - if (!QSqlDatabase::drivers().contains(driver)) - QSKIP(QString::fromLatin1("Database driver %1 not available").arg(driver).toLocal8Bit().constData()); - - const QString dbName = QLatin1String("errorReportingDb-") + driver; - QSqlDatabase db = QSqlDatabase::addDatabase(driver, dbName); - - db.setHostName(QLatin1String("127.0.0.1")); - db.setDatabaseName(QLatin1String("NonExistantDatabase")); - db.setUserName(QLatin1String("InvalidUser")); - db.setPassword(QLatin1String("IncorrectPassword")); - - QVERIFY(!db.open()); - - db = QSqlDatabase(); - - QSqlDatabase::removeDatabase(dbName); -} - void tst_QSqlDatabase::open() { QFETCH(QString, dbName); @@ -509,7 +434,7 @@ void tst_QSqlDatabase::tables() bool tempTables = false; QSqlQuery q(db); - if (!q.exec("CREATE VIEW " + qtest_view + " as select * from " + qtest)) { + if (!q.exec("CREATE VIEW " + qtest_view + " as select * from " + db.driver()->escapeIdentifier(qtest, QSqlDriver::TableName))) { qDebug("DBMS '%s' cannot handle VIEWs: %s", qPrintable(tst_Databases::dbToString(db)), qPrintable(tst_Databases::printError(q.lastError()))); @@ -562,34 +487,21 @@ void tst_QSqlDatabase::whitespaceInIdentifiers() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); + const auto metaTypeToCheck = dbType == QSqlDriver::Oracle + ? QMetaType(QMetaType::Double) : QMetaType(QMetaType::Int); - if (testWhiteSpaceNames(db.driverName())) { - const bool isCaseSensitive = driverQuotedCaseSensitive(db); - const auto tableName(qTableName("qtest test", __FILE__, db, isCaseSensitive)); - if (isCaseSensitive) { - QVERIFY(db.tables().contains(db.driver()->stripDelimiters(tableName, QSqlDriver::TableName))); - } else { - QVERIFY(db.tables().contains(tableName, Qt::CaseInsensitive)); - } + const auto tableName(qTableName("qtest test", __FILE__, db, true)); + QVERIFY(db.tables().contains(db.driver()->stripDelimiters(tableName, QSqlDriver::TableName))); - QSqlRecord rec = db.record(tableName); - QCOMPARE(rec.count(), 1); - QCOMPARE(rec.fieldName(0), QString("test test")); - if (dbType == QSqlDriver::Oracle) - QCOMPARE(rec.field(0).metaType(), QMetaType(QMetaType::Double)); - else - QCOMPARE(rec.field(0).metaType(), QMetaType(QMetaType::Int)); + QSqlRecord rec = db.record(tableName); + QCOMPARE(rec.count(), 1); + QCOMPARE(rec.fieldName(0), QString("test test")); + QCOMPARE(rec.field(0).metaType(), metaTypeToCheck); - QSqlIndex idx = db.primaryIndex(tableName); - QCOMPARE(idx.count(), 1); - QCOMPARE(idx.fieldName(0), QString("test test")); - if (dbType == QSqlDriver::Oracle) - QCOMPARE(idx.field(0).metaType(), QMetaType(QMetaType::Double)); - else - QCOMPARE(idx.field(0).metaType(), QMetaType(QMetaType::Int)); - } else { - QSKIP("DBMS does not support whitespaces in identifiers"); - } + QSqlIndex idx = db.primaryIndex(tableName); + QCOMPARE(idx.count(), 1); + QCOMPARE(idx.fieldName(0), QString("test test")); + QCOMPARE(idx.field(0).metaType(), metaTypeToCheck); } void tst_QSqlDatabase::alterTable() @@ -597,13 +509,11 @@ void tst_QSqlDatabase::alterTable() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString qtestalter(qTableName("qtestalter", __FILE__, db)); - const auto noEscapeAlterTable = qTableName("qtestalter", __FILE__, db, false); - const bool isCaseSensitive = driverQuotedCaseSensitive(db); + TableScope ts(db, "qtestalter", __FILE__); QSqlQuery q(db); - QVERIFY_SQL(q, exec("create table " + qtestalter + " (F1 char(20), F2 char(20), F3 char(20))")); - QSqlRecord rec = db.record(isCaseSensitive ? qtestalter : noEscapeAlterTable); + QVERIFY_SQL(q, exec("create table " + ts.tableName() + " (F1 char(20), F2 char(20), F3 char(20))")); + QSqlRecord rec = db.record(ts.tableName()); QCOMPARE((int)rec.count(), 3); int i; @@ -611,18 +521,18 @@ void tst_QSqlDatabase::alterTable() QCOMPARE(rec.field(i).name().toUpper(), QString("F%1").arg(i + 1)); } - if (!q.exec("alter table " + qtestalter + " drop column F2")) { + if (!q.exec("alter table " + ts.tableName() + " drop column F2")) { QSKIP("DBMS doesn't support dropping columns in ALTER TABLE statement"); } - rec = db.record(isCaseSensitive ? qtestalter : noEscapeAlterTable); + rec = db.record(ts.tableName()); - QCOMPARE((int)rec.count(), 2); + QCOMPARE(rec.count(), 2); QCOMPARE(rec.field(0).name().toUpper(), QString("F1")); QCOMPARE(rec.field(1).name().toUpper(), QString("F3")); - q.exec("select * from " + qtestalter); + q.exec("select * from " + ts.tableName()); } #if 0 @@ -650,12 +560,11 @@ void tst_QSqlDatabase::record() } #endif -void tst_QSqlDatabase::testRecord(const FieldDef fieldDefs[], const QSqlRecord& inf, QSqlDatabase db) +void tst_QSqlDatabase::testRecord(const FieldDef fieldDefs[], const QSqlRecord &inf, const QSqlDatabase &db) { - int i = 0; if (!tst_Databases::autoFieldName(db).isEmpty()) // Currently only MySQL is tested - QVERIFY2(inf.field(i).isAutoValue(), qPrintable(inf.field(i).name() + " should be reporting as an autovalue")); - for (i = 0; !fieldDefs[ i ].typeName.isNull(); ++i) { + QVERIFY2(inf.field(0).isAutoValue(), qPrintable(inf.field(0).name() + " should be reporting as an autovalue")); + for (int i = 0; !fieldDefs[ i ].typeName.isNull(); ++i) { QCOMPARE(inf.field(i+1).name().toUpper(), fieldDefs[ i ].fieldName().toUpper()); if (inf.field(i+1).metaType().id() != fieldDefs[ i ].type) { QFAIL(qPrintable(QString(" Expected: '%1' Received: '%2' for field %3 in testRecord").arg( @@ -670,13 +579,12 @@ void tst_QSqlDatabase::testRecord(const FieldDef fieldDefs[], const QSqlRecord& } // non-dbms specific tests -void tst_QSqlDatabase::commonFieldTest(const FieldDef fieldDefs[], QSqlDatabase db, const int fieldCount) +void tst_QSqlDatabase::commonFieldTest(const FieldDef fieldDefs[], const QSqlDatabase &db, const int fieldCount) { CHECK_DATABASE(db); - QStringList tableNames = { qTableName("qtestfields", __FILE__, db) }; - if (!driverQuotedCaseSensitive(db)) - tableNames << qTableName("qtestfields", __FILE__, db, false); + const QStringList tableNames = { qTableName("qtestfields", __FILE__, db), + qTableName("qtestfields", __FILE__, db, false) }; for (const QString &table : tableNames) { QSqlRecord rec = db.record(table); @@ -688,43 +596,6 @@ void tst_QSqlDatabase::commonFieldTest(const FieldDef fieldDefs[], QSqlDatabase QVERIFY_SQL(q, exec("select * from " + tableNames.at(0))); } -void tst_QSqlDatabase::recordTDS() -{ - QFETCH(QString, dbName); - QSqlDatabase db = QSqlDatabase::database(dbName); - CHECK_DATABASE(db); - - static const FieldDef fieldDefs[] = { - FieldDef("tinyint", QMetaType::Int, 255), - FieldDef("smallint", QMetaType::Int, 32767), - FieldDef("int", QMetaType::Int, 2147483647), - FieldDef("numeric(10,9)", QMetaType::Double, 1.23456789), - FieldDef("decimal(10,9)", QMetaType::Double, 1.23456789), - FieldDef("float(4)", QMetaType::Double, 1.23456789), - FieldDef("double precision", QMetaType::Double, 1.23456789), - FieldDef("real", QMetaType::Double, 1.23456789), - FieldDef("smallmoney", QMetaType::Double, 100.42), - FieldDef("money", QMetaType::Double, 200.42), - // accuracy is that of a minute - FieldDef("smalldatetime", QMetaType::QDateTime, QDateTime(QDate::currentDate(), QTime(1, 2, 0, 0))), - // accuracy is that of a second - FieldDef("datetime", QMetaType::QDateTime, QDateTime(QDate::currentDate(), QTime(1, 2, 3, 0))), - FieldDef("char(20)", QMetaType::QString, "blah1"), - FieldDef("varchar(20)", QMetaType::QString, "blah2"), - FieldDef("nchar(20)", QMetaType::QString, "blah3"), - FieldDef("nvarchar(20)", QMetaType::QString, "blah4"), - FieldDef("text", QMetaType::QString, "blah5"), - FieldDef("bit", QMetaType::Int, 1, false), - - FieldDef() - }; - - const int fieldCount = createFieldTable(fieldDefs, db); - QVERIFY(fieldCount > 0); - - commonFieldTest(fieldDefs, db, fieldCount); -} - void tst_QSqlDatabase::recordOCI() { bool hasTimeStamp = false; @@ -871,22 +742,6 @@ void tst_QSqlDatabase::recordMySQL() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - FieldDef bin10, varbin10; - int major = tst_Databases::getMySqlVersion( db ).section( QChar('.'), 0, 0 ).toInt(); - int minor = tst_Databases::getMySqlVersion( db ).section( QChar('.'), 1, 1 ).toInt(); - int revision = tst_Databases::getMySqlVersion( db ).section( QChar('.'), 2, 2 ).toInt(); - int vernum = (major << 16) + (minor << 8) + revision; - - /* The below is broken in mysql below 5.0.15 - see http://dev.mysql.com/doc/refman/5.0/en/binary-varbinary.html - specifically: Before MySQL 5.0.15, the pad value is space. Values are right-padded - with space on insert, and trailing spaces are removed on select. - */ - if( vernum >= ((5 << 16) + 15) ) { - bin10 = FieldDef("binary(10)", QMetaType::QByteArray, QString("123abc ")); - varbin10 = FieldDef("varbinary(10)", QMetaType::QByteArray, QString("123abcv ")); - } - static QDateTime dt(QDate::currentDate(), QTime(1, 2, 3, 0)); static const FieldDef fieldDefs[] = { FieldDef("tinyint", QMetaType::Char, 127), @@ -1168,28 +1023,37 @@ void tst_QSqlDatabase::bigIntField() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); - const QString qtest_bigint(qTableName("qtest_bigint", __FILE__, db)); + QString queryString; + switch (dbType) { + case QSqlDriver::MySqlServer: + queryString = "create table %1 (id int, t_s64bit bigint, t_u64bit bigint unsigned)"; + break; + case QSqlDriver::DB2: + case QSqlDriver::Interbase: + case QSqlDriver::MimerSQL: + case QSqlDriver::MSSqlServer: + case QSqlDriver::PostgreSQL: + queryString = "create table %1 (id int, t_s64bit bigint, t_u64bit bigint)"; + break; + case QSqlDriver::Oracle: + case QSqlDriver::SQLite: + queryString = "create table %1 (id int, t_s64bit int, t_u64bit int)"; + break; + case QSqlDriver::Sybase: + case QSqlDriver::UnknownDbms: + break; + } + if (queryString.isEmpty()) + QSKIP("no 64 bit integer support"); + + TableScope ts(db, "qtest_bigint", __FILE__); QSqlQuery q(db); q.setForwardOnly(true); - if (dbType == QSqlDriver::Oracle) q.setNumericalPrecisionPolicy(QSql::LowPrecisionInt64); - - if (dbType == QSqlDriver::MySqlServer) { - QVERIFY_SQL(q, exec("create table " + qtest_bigint + " (id int, t_s64bit bigint, t_u64bit bigint unsigned)")); - } else if (dbType == QSqlDriver::PostgreSQL - || dbType == QSqlDriver::DB2 - || dbType == QSqlDriver::MSSqlServer) { - QVERIFY_SQL(q, exec("create table " + qtest_bigint + "(id int, t_s64bit bigint, t_u64bit bigint)")); - } else if (dbType == QSqlDriver::Oracle) { - QVERIFY_SQL(q, exec("create table " + qtest_bigint + " (id int, t_s64bit int, t_u64bit int)")); - //} else if (dbType == QSqlDriver::Interbase) { - // QVERIFY_SQL(q, exec("create table " + qtest_bigint + " (id int, t_s64bit int64, t_u64bit int64)")); - } else { - QSKIP("no 64 bit integer support"); - } - QVERIFY(q.prepare("insert into " + qtest_bigint + " values (?, ?, ?)")); + QVERIFY_SQL(q, exec(queryString.arg(ts.tableName()))); + QVERIFY(q.prepare("insert into " + ts.tableName() + " values (?, ?, ?)")); qlonglong ll = Q_INT64_C(9223372036854775807); qulonglong ull = Q_UINT64_C(18446744073709551615); @@ -1213,7 +1077,7 @@ void tst_QSqlDatabase::bigIntField() q.bindValue(2, (qlonglong) ull); QVERIFY_SQL(q, exec()); } - QVERIFY(q.exec("select * from " + qtest_bigint + " order by id")); + QVERIFY(q.exec("select * from " + ts.tableName() + " order by id")); QVERIFY(q.next()); QCOMPARE(q.value(1).toDouble(), (double)ll); QCOMPARE(q.value(1).toLongLong(), ll); @@ -1242,34 +1106,44 @@ void tst_QSqlDatabase::caseSensivity() cs = true; } - QSqlRecord rec = db.record(qTableName("qtest", __FILE__, db, driverQuotedCaseSensitive(db))); - QVERIFY((int)rec.count() > 0); + QSqlRecord rec = db.record(qTableName("qtest", __FILE__, db)); + QVERIFY(rec.count() > 0); if (!cs) { - rec = db.record(qTableName("QTEST", __FILE__, db, false).toUpper()); - QVERIFY((int)rec.count() > 0); - rec = db.record(qTableName("qTesT", __FILE__, db, false)); - QVERIFY((int)rec.count() > 0); + rec = db.record(qTableName("QTEST", __FILE__, db, false).toUpper()); + QVERIFY(rec.count() > 0); + rec = db.record(qTableName("qTesT", __FILE__, db, false)); + QVERIFY(rec.count() > 0); } - rec = db.primaryIndex(qTableName("qtest", __FILE__, db, driverQuotedCaseSensitive(db))); - QVERIFY((int)rec.count() > 0); + rec = db.primaryIndex(qTableName("qtest", __FILE__, db)); + QVERIFY(rec.count() > 0); if (!cs) { - rec = db.primaryIndex(qTableName("QTEST", __FILE__, db, false).toUpper()); - QVERIFY((int)rec.count() > 0); - rec = db.primaryIndex(qTableName("qTesT", __FILE__, db, false)); - QVERIFY((int)rec.count() > 0); + rec = db.primaryIndex(qTableName("QTEST", __FILE__, db, false).toUpper()); + QVERIFY(rec.count() > 0); + rec = db.primaryIndex(qTableName("qTesT", __FILE__, db, false)); + QVERIFY(rec.count() > 0); } // Explicit test for case sensitive table creation without quoting + TableScope ts(db, "NoQuotes", __FILE__, false); QSqlQuery qry(db); - const auto noQuotesTable = qTableName("NoQuotes", __FILE__, db, false); - tst_Databases::safeDropTable(db, noQuotesTable); - QVERIFY_SQL(qry, exec("CREATE TABLE " + noQuotesTable + " (id INTEGER)")); - QVERIFY_SQL(qry, exec("INSERT INTO " + noQuotesTable + " VALUES(1)")); - QVERIFY_SQL(qry, exec("SELECT * FROM " + noQuotesTable)); + QVERIFY_SQL(qry, exec("CREATE TABLE " + ts.tableName() + " (id INTEGER)")); + QVERIFY_SQL(qry, exec("INSERT INTO " + ts.tableName() + " VALUES(1)")); + QVERIFY_SQL(qry, exec("SELECT * FROM " + ts.tableName())); QVERIFY_SQL(qry, next()); QCOMPARE(qry.value(0).toInt(), 1); - rec = db.record(cs ? noQuotesTable.toLower() : noQuotesTable); + // QMYSQLDriver::record() is using a mysql function instead of a query, so quoting + // will not help when the table names are not stored lowercase. + if (dbType == QSqlDriver::MySqlServer) { + QVERIFY_SQL(qry, exec("SHOW GLOBAL VARIABLES LIKE 'lower_case_table_names'")); + QVERIFY_SQL(qry, next()); + cs = qry.value(1).toInt() != 0; + } + if (dbType == QSqlDriver::Interbase) { + rec = db.record(ts.tableName().toUpper()); + } else { + rec = db.record(cs ? ts.tableName().toLower() : ts.tableName()); + } QVERIFY(rec.count() > 0); } @@ -1375,24 +1249,24 @@ void tst_QSqlDatabase::psql_escapeBytea() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "batable", __FILE__); const char dta[4] = {'\x71', '\x14', '\x32', '\x81'}; QByteArray ba(dta, 4); QSqlQuery q(db); - const QString tableName(qTableName("batable", __FILE__, db)); - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (ba bytea)").arg(tableName))); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (ba bytea)").arg(ts.tableName()))); QSqlQuery iq(db); - QVERIFY_SQL(iq, prepare(QString("INSERT INTO %1 VALUES (?)").arg(tableName))); + QVERIFY_SQL(iq, prepare(QString("INSERT INTO %1 VALUES (?)").arg(ts.tableName()))); iq.bindValue(0, QVariant(ba)); QVERIFY_SQL(iq, exec()); - QVERIFY_SQL(q, exec(QString("SELECT ba FROM %1").arg(tableName))); + QVERIFY_SQL(q, exec(QString("SELECT ba FROM %1").arg(ts.tableName()))); QVERIFY_SQL(q, next()); QByteArray res = q.value(0).toByteArray(); - int i = 0; + qsizetype i = 0; for (; i < ba.size(); ++i){ if (ba[i] != res[i]) break; @@ -1406,13 +1280,13 @@ void tst_QSqlDatabase::psql_bug249059() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "bug_249059", __FILE__); QSqlQuery q(db); - const QString tableName(qTableName("bug_249059", __FILE__, db)); - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (dt timestamp, t time)").arg(tableName))); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (dt timestamp, t time)").arg(ts.tableName()))); QSqlQuery iq(db); - QVERIFY_SQL(iq, prepare(QString("INSERT INTO %1 VALUES (?, ?)").arg(tableName))); + QVERIFY_SQL(iq, prepare(QString("INSERT INTO %1 VALUES (?, ?)").arg(ts.tableName()))); iq.bindValue(0, QVariant(QString("2001-09-09 04:05:06.789 -5:00"))); iq.bindValue(1, QVariant(QString("04:05:06.789 -5:00"))); QVERIFY_SQL(iq, exec()); @@ -1420,7 +1294,7 @@ void tst_QSqlDatabase::psql_bug249059() iq.bindValue(1, QVariant(QString("04:05:06.789 +5:00"))); QVERIFY_SQL(iq, exec()); - QVERIFY_SQL(q, exec(QString("SELECT dt, t FROM %1").arg(tableName))); + QVERIFY_SQL(q, exec(QString("SELECT dt, t FROM %1").arg(ts.tableName()))); QVERIFY_SQL(q, next()); QDateTime dt1=q.value(0).toDateTime(); QTime t1=q.value(1).toTime(); @@ -1433,17 +1307,15 @@ void tst_QSqlDatabase::psql_bug249059() QCOMPARE(t1, t2); } -// This test should be rewritten to work with Oracle as well - or the Oracle driver -// should be fixed to make this test pass (handle overflows) void tst_QSqlDatabase::precisionPolicy() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); -// DBMS_SPECIFIC(db, "QPSQL"); + TableScope ts(db, "qtest_prec", __FILE__); + const auto &tableName = ts.tableName(); QSqlQuery q(db); - const QString tableName(qTableName("qtest_prec", __FILE__, db)); if(!db.driver()->hasFeature(QSqlDriver::LowPrecisionNumbers)) QSKIP("Driver or database doesn't support setting precision policy"); @@ -1536,11 +1408,10 @@ void tst_QSqlDatabase::infinityAndNan() QSKIP("checking for infinity/nan currently only works for PostgreSQL"); QSqlQuery q(db); - const QString tableName(qTableName("infititytest", __FILE__, db)); - tst_Databases::safeDropTables(db, {tableName}); - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (id smallint, val double precision)").arg(tableName))); + TableScope ts(db, "infititytest", __FILE__); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (id smallint, val double precision)").arg(ts.tableName()))); - QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES (?, ?)").arg(tableName))); + QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES (?, ?)").arg(ts.tableName()))); q.bindValue(0, 1); q.bindValue(1, qQNaN()); @@ -1552,7 +1423,7 @@ void tst_QSqlDatabase::infinityAndNan() q.bindValue(1, -qInf()); QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec(QString("SELECT val FROM %1 ORDER BY id").arg(tableName))); + QVERIFY_SQL(q, exec(QString("SELECT val FROM %1 ORDER BY id").arg(ts.tableName()))); QVERIFY_SQL(q, next()); QVERIFY(qIsNaN(q.value(0).toDouble())); @@ -1575,14 +1446,14 @@ void tst_QSqlDatabase::mysqlOdbc_unsignedIntegers() if (tst_Databases::getDatabaseType(db) != QSqlDriver::MySqlServer || !db.driverName().startsWith("QODBC")) QSKIP("MySQL through ODBC-driver specific test"); + TableScope ts(db, "uint", __FILE__); QSqlQuery q(db); - const QString tableName(qTableName("uint", __FILE__, db)); - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (foo integer(10) unsigned, bar integer(10))").arg(tableName))); - QVERIFY_SQL(q, exec(QString("INSERT INTO %1 VALUES (-4000000000, -4000000000)").arg(tableName))); - QVERIFY_SQL(q, exec(QString("INSERT INTO %1 VALUES (4000000000, 4000000000)").arg(tableName))); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (foo integer(10) unsigned, bar integer(10))").arg(ts.tableName()))); + QVERIFY_SQL(q, exec(QString("INSERT INTO %1 VALUES (-4000000000, -4000000000)").arg(ts.tableName()))); + QVERIFY_SQL(q, exec(QString("INSERT INTO %1 VALUES (4000000000, 4000000000)").arg(ts.tableName()))); - QVERIFY_SQL(q, exec(QString("SELECT foo, bar FROM %1").arg(tableName))); + QVERIFY_SQL(q, exec(QString("SELECT foo, bar FROM %1").arg(ts.tableName()))); QVERIFY(q.next()); QCOMPARE(q.value(0).toString(), QString("0")); QCOMPARE(q.value(1).toString(), QString("-2147483648")); @@ -1599,13 +1470,13 @@ void tst_QSqlDatabase::accessOdbc_strings() if (!tst_Databases::isMSAccess(db)) QSKIP("MS Access specific test"); + TableScope ts(db, "strings", __FILE__); QSqlQuery q(db); - const QString tableName(qTableName("strings", __FILE__, db)); QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (aStr memo, bStr memo, cStr memo, dStr memo" - ", eStr memo, fStr memo, gStr memo, hStr memo)").arg(tableName))); + ", eStr memo, fStr memo, gStr memo, hStr memo)").arg(ts.tableName()))); - QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES (?, ?, ?, ?, ?, ?, ?, ?)").arg(tableName))); + QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES (?, ?, ?, ?, ?, ?, ?, ?)").arg(ts.tableName()))); QString aStr, bStr, cStr, dStr, eStr, fStr, gStr, hStr; q.bindValue(0, aStr.fill('A', 32)); @@ -1619,7 +1490,7 @@ void tst_QSqlDatabase::accessOdbc_strings() QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec(QString("SELECT aStr, bStr, cStr, dStr, eStr, fStr, gStr, hStr FROM %1").arg(tableName))); + QVERIFY_SQL(q, exec(QString("SELECT aStr, bStr, cStr, dStr, eStr, fStr, gStr, hStr FROM %1").arg(ts.tableName()))); q.next(); QCOMPARE(q.value(0).toString(), aStr); QCOMPARE(q.value(1).toString(), bStr); @@ -1637,9 +1508,10 @@ void tst_QSqlDatabase::ibase_numericFields() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "numericfields", __FILE__); + const auto &tableName = ts.tableName(); QSqlQuery q(db); - const QString tableName(qTableName("numericfields", __FILE__, db)); QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (id int not null, num1 NUMERIC(2,1), " "num2 NUMERIC(5,2), num3 NUMERIC(10,3), " "num4 NUMERIC(18,4))").arg(tableName))); @@ -1710,8 +1582,9 @@ void tst_QSqlDatabase::ibase_fetchBlobs() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "qtest_ibaseblobs", __FILE__); + const auto &tableName = ts.tableName(); - const QString tableName(qTableName("qtest_ibaseblobs", __FILE__, db)); QSqlQuery q(db); QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (blob1 BLOB segment size 256)").arg(tableName))); @@ -1742,13 +1615,11 @@ void tst_QSqlDatabase::ibase_procWithoutReturnValues() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + ProcScope ps(db, "qtest_proc1", __FILE__); QSqlQuery q(db); - const QString procName(qTableName("qtest_proc1", __FILE__, db)); - q.exec(QString("drop procedure %1").arg(procName)); - QVERIFY_SQL(q, exec("CREATE PROCEDURE " + procName + " (str VARCHAR(10))\nAS BEGIN\nstr='test';\nEND;")); - QVERIFY_SQL(q, exec(QString("execute procedure %1('qtest')").arg(procName))); - q.exec(QString("drop procedure %1").arg(procName)); + QVERIFY_SQL(q, exec("CREATE PROCEDURE " + ps.name() + " (str VARCHAR(10))\nAS BEGIN\nstr='test';\nEND;")); + QVERIFY_SQL(q, exec(QString("execute procedure %1('qtest')").arg(ps.name()))); } void tst_QSqlDatabase::ibase_procWithReturnValues() @@ -1756,12 +1627,10 @@ void tst_QSqlDatabase::ibase_procWithReturnValues() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - - const QString procName(qTableName("qtest_proc2", __FILE__, db)); + ProcScope ps(db, "qtest_proc2", __FILE__); QSqlQuery q(db); - q.exec(QString("drop procedure %1").arg(procName)); - QVERIFY_SQL(q, exec("CREATE PROCEDURE " + procName + " (" + QVERIFY_SQL(q, exec("CREATE PROCEDURE " + ps.name() + " (" "\nABC INTEGER)" "\nRETURNS (" "\nRESULT INTEGER)" @@ -1772,13 +1641,13 @@ void tst_QSqlDatabase::ibase_procWithReturnValues() "\nend")); // Interbase procedures can be executed in two ways: EXECUTE PROCEDURE or SELECT - QVERIFY_SQL(q, exec(QString("execute procedure %1(123)").arg(procName))); + QVERIFY_SQL(q, exec(QString("execute procedure %1(123)").arg(ps.name()))); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toInt(), 1230); - QVERIFY_SQL(q, exec(QString("select result from %1(456)").arg(procName))); + QVERIFY_SQL(q, exec(QString("select result from %1(456)").arg(ps.name()))); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toInt(), 4560); - QVERIFY_SQL(q, prepare(QLatin1String("execute procedure ")+procName+QLatin1String("(?)"))); + QVERIFY_SQL(q, prepare(QLatin1String("execute procedure ") + ps.name() + QLatin1String("(?)"))); q.bindValue(0, 123); QVERIFY_SQL(q, exec()); QVERIFY_SQL(q, next()); @@ -1787,8 +1656,6 @@ void tst_QSqlDatabase::ibase_procWithReturnValues() QVERIFY_SQL(q, exec()); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toInt(), 4560); - - q.exec(QString("drop procedure %1").arg(procName)); } void tst_QSqlDatabase::formatValueTrimStrings() @@ -1845,13 +1712,13 @@ void tst_QSqlDatabase::odbc_bindBoolean() QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); if (dbType == QSqlDriver::MySqlServer) QSKIP("MySql has inconsistent behaviour of bit field type across versions."); + TableScope ts(db, "qtestBindBool", __FILE__); QSqlQuery q(db); - const QString tableName = qTableName("qtestBindBool", __FILE__, db); - QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + "(id int, boolvalue bit)")); + QVERIFY_SQL(q, exec("CREATE TABLE " + ts.tableName() + "(id int, boolvalue bit)")); // Bind and insert - QVERIFY_SQL(q, prepare("INSERT INTO " + tableName + " VALUES(?, ?)")); + QVERIFY_SQL(q, prepare("INSERT INTO " + ts.tableName() + " VALUES(?, ?)")); q.bindValue(0, 1); q.bindValue(1, true); QVERIFY_SQL(q, exec()); @@ -1860,7 +1727,7 @@ void tst_QSqlDatabase::odbc_bindBoolean() QVERIFY_SQL(q, exec()); // Retrive - QVERIFY_SQL(q, exec("SELECT id, boolvalue FROM " + tableName + " ORDER BY id")); + QVERIFY_SQL(q, exec("SELECT id, boolvalue FROM " + ts.tableName() + " ORDER BY id")); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toInt(), 1); QCOMPARE(q.value(1).toBool(), true); @@ -1874,7 +1741,8 @@ void tst_QSqlDatabase::odbc_testqGetString() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString testqGetString(qTableName("testqGetString", __FILE__, db)); + TableScope ts(db, "testqGetString", __FILE__); + const auto &testqGetString = ts.tableName(); QSqlQuery q(db); QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); @@ -1929,31 +1797,6 @@ void tst_QSqlDatabase::mysql_multiselect() QVERIFY_SQL(q, exec("SELECT * FROM " + qtest)); } -void tst_QSqlDatabase::ibase_useCustomCharset() -{ - QFETCH(QString, dbName); - QSqlDatabase db = QSqlDatabase::database(dbName); - CHECK_DATABASE(db); - QString nonlatin1string("��"); - - db.close(); - db.setConnectOptions("ISC_DPB_LC_CTYPE=Latin1"); - db.open(); - - const QString tableName(qTableName("latin1table", __FILE__, db)); - - QSqlQuery q(db); - QEXPECT_FAIL("", "Currently fails, potentially due to invalid test - needs further " - "investigation - QTBUG-85828", Abort); - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(text VARCHAR(6) CHARACTER SET Latin1)").arg(tableName))); - QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES(?)").arg(tableName))); - q.addBindValue(nonlatin1string); - QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec(QString("SELECT text FROM %1").arg(tableName))); - QVERIFY_SQL(q, next()); - QCOMPARE(toHex(q.value(0).toString()), toHex(nonlatin1string)); -} - void tst_QSqlDatabase::oci_serverDetach() { QFETCH(QString, dbName); @@ -1979,8 +1822,9 @@ void tst_QSqlDatabase::oci_xmltypeSupport() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "qtest_xmltype", __FILE__); + const auto &tableName = ts.tableName(); - const QString tableName(qTableName("qtest_xmltype", __FILE__, db)); QString xml("<?xml version=\"1.0\"?>\n<TABLE_NAME>MY_TABLE</TABLE_NAME>\n"); QSqlQuery q(db); @@ -2058,14 +1902,14 @@ void tst_QSqlDatabase::odbc_uniqueidentifier() if (dbType != QSqlDriver::MSSqlServer) QSKIP("SQL Server (ODBC) specific test"); - const QString tableName(qTableName("qtest_sqlguid", __FILE__, db)); + TableScope ts(db, "qtest_sqlguid", __FILE__); QString guid = QString("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"); QString invalidGuid = QString("GAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"); QSqlQuery q(db); - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(id uniqueidentifier)").arg(tableName))); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(id uniqueidentifier)").arg(ts.tableName()))); - q.prepare(QString("INSERT INTO %1 VALUES(?)").arg(tableName));; + q.prepare(QString("INSERT INTO %1 VALUES(?)").arg(ts.tableName())); q.addBindValue(guid); QVERIFY_SQL(q, exec()); @@ -2074,7 +1918,7 @@ void tst_QSqlDatabase::odbc_uniqueidentifier() Continue); QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec(QString("SELECT id FROM %1").arg(tableName))); + QVERIFY_SQL(q, exec(QString("SELECT id FROM %1").arg(ts.tableName()))); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toString(), guid); } @@ -2100,20 +1944,20 @@ void tst_QSqlDatabase::odbc_uintfield() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "uint_table", __FILE__); - const QString tableName(qTableName("uint_table", __FILE__, db)); - unsigned int val = 4294967295U; + constexpr auto val = std::numeric_limits<unsigned int>::max(); QSqlQuery q(db); if ( tst_Databases::isMSAccess( db ) ) - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(num number)").arg(tableName))); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(num number)").arg(ts.tableName()))); else - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(num numeric(10))").arg(tableName))); - q.prepare(QString("INSERT INTO %1 VALUES(?)").arg(tableName)); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(num numeric(10))").arg(ts.tableName()))); + q.prepare(QString("INSERT INTO %1 VALUES(?)").arg(ts.tableName())); q.addBindValue(val); QVERIFY_SQL(q, exec()); - q.exec(QString("SELECT num FROM %1").arg(tableName)); + q.exec(QString("SELECT num FROM %1").arg(ts.tableName())); if (q.next()) QCOMPARE(q.value(0).toUInt(), val); } @@ -2170,38 +2014,32 @@ void tst_QSqlDatabase::eventNotificationIBase() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); - if (db.driverName().compare(QLatin1String("QIBASE"), Qt::CaseInsensitive)) - QSKIP("QIBASE specific test"); CHECK_DATABASE(db); - const QString procedureName(qTableName("posteventProc", __FILE__, db)); + ProcScope ps(db, "posteventProc", __FILE__); QSqlDriver *driver=db.driver(); - QVERIFY_SQL(*driver, subscribeToNotification(procedureName)); + QVERIFY_SQL(*driver, subscribeToNotification(ps.name())); QTest::qWait(300); // Interbase needs some time to call the driver callback. db.transaction(); // InterBase events are posted from within transactions. QSqlQuery q(db); - q.exec(QString("DROP PROCEDURE %1").arg(procedureName)); - q.exec(QString("CREATE PROCEDURE %1\nAS BEGIN\nPOST_EVENT '%1';\nEND;").arg(procedureName)); - q.exec(QString("EXECUTE PROCEDURE %1").arg(procedureName)); - QSignalSpy spy(driver, QOverload<const QString &, QSqlDriver::NotificationSource, const QVariant &>::of(&QSqlDriver::notification)); + q.exec(QString("CREATE PROCEDURE %1\nAS BEGIN\nPOST_EVENT '%1';\nEND;").arg(ps.name())); + q.exec(QString("EXECUTE PROCEDURE %1").arg(ps.name())); + QSignalSpy spy(driver, &QSqlDriver::notification); db.commit(); // No notifications are posted until the transaction is committed. // Interbase needs some time to post the notification and call the driver callback. // This happends from another thread, and we have to process events in order for the // event handler in the driver to be executed and emit the notification signal. - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QList<QVariant> arguments = spy.takeFirst(); - QCOMPARE(arguments.at(0).toString(), procedureName); - QVERIFY_SQL(*driver, unsubscribeFromNotification(procedureName)); - q.exec(QString("DROP PROCEDURE %1").arg(procedureName)); + QCOMPARE(arguments.at(0).toString(), ps.name()); + QVERIFY_SQL(*driver, unsubscribeFromNotification(ps.name())); } void tst_QSqlDatabase::eventNotificationPSQL() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); - if (db.driverName().compare(QLatin1String("QPSQL"), Qt::CaseInsensitive)) - QSKIP("QPSQL specific test"); CHECK_DATABASE(db); QSqlQuery query(db); @@ -2209,9 +2047,9 @@ void tst_QSqlDatabase::eventNotificationPSQL() QString payload = "payload"; QSqlDriver *driver = db.driver(); QVERIFY_SQL(*driver, subscribeToNotification(procedureName)); - QSignalSpy spy(driver, QOverload<const QString &, QSqlDriver::NotificationSource, const QVariant &>::of(&QSqlDriver::notification)); + QSignalSpy spy(driver, &QSqlDriver::notification); query.exec(QString("NOTIFY \"%1\", '%2'").arg(procedureName).arg(payload)); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toString(), procedureName); QCOMPARE(qvariant_cast<QSqlDriver::NotificationSource>(arguments.at(1)), QSqlDriver::SelfSource); @@ -2223,26 +2061,23 @@ void tst_QSqlDatabase::eventNotificationSQLite() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); - if (db.driverName().compare(QLatin1String("QSQLITE"), Qt::CaseInsensitive)) - QSKIP("QSQLITE specific test"); CHECK_DATABASE(db); - const QString tableName(qTableName("sqlitnotifytest", __FILE__, db)); - const auto noEscapeTableName(qTableName("sqlitnotifytest", __FILE__, db, false)); - tst_Databases::safeDropTable(db, tableName); + TableScope ts(db, "sqlitnotifytest", __FILE__); + TableScope tsEscape(db, "sqlitnotifytest", __FILE__, false); QSqlDriver *driver = db.driver(); - QSignalSpy spy(driver, QOverload<const QString &, QSqlDriver::NotificationSource, const QVariant &>::of(&QSqlDriver::notification)); + QSignalSpy spy(driver, &QSqlDriver::notification); QSqlQuery q(db); - QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id INTEGER, realVal REAL)")); - driver->subscribeToNotification(noEscapeTableName); - QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id, realVal) VALUES (1, 2.3)")); - QTRY_COMPARE(spy.count(), 1); + QVERIFY_SQL(q, exec("CREATE TABLE " + ts.tableName() + " (id INTEGER, realVal REAL)")); + driver->subscribeToNotification(tsEscape.tableName()); + QVERIFY_SQL(q, exec("INSERT INTO " + ts.tableName() + " (id, realVal) VALUES (1, 2.3)")); + QTRY_COMPARE(spy.size(), 1); QList<QVariant> arguments = spy.takeFirst(); - QCOMPARE(arguments.at(0).toString(), noEscapeTableName); - driver->unsubscribeFromNotification(noEscapeTableName); - QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id, realVal) VALUES (1, 2.3)")); - QTRY_COMPARE(spy.count(), 0); + QCOMPARE(arguments.at(0).toString(), tsEscape.tableName()); + driver->unsubscribeFromNotification(tsEscape.tableName()); + QVERIFY_SQL(q, exec("INSERT INTO " + ts.tableName() + " (id, realVal) VALUES (1, 2.3)")); + QTRY_COMPARE(spy.size(), 0); } void tst_QSqlDatabase::sqlite_bindAndFetchUInt() @@ -2250,16 +2085,13 @@ void tst_QSqlDatabase::sqlite_bindAndFetchUInt() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - if (db.driverName().startsWith("QSQLITE2")) - QSKIP("SQLite3 specific test"); - + TableScope ts(db, "uint_test", __FILE__); QSqlQuery q(db); - const QString tableName(qTableName("uint_test", __FILE__, db)); - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(uint_field UNSIGNED INTEGER)").arg(tableName))); - QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES(?)").arg(tableName))); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(uint_field UNSIGNED INTEGER)").arg(ts.tableName()))); + QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES(?)").arg(ts.tableName()))); q.addBindValue(4000000000U); QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec(QString("SELECT uint_field FROM %1").arg(tableName))); + QVERIFY_SQL(q, exec(QString("SELECT uint_field FROM %1").arg(ts.tableName()))); QVERIFY_SQL(q, next()); // All integers in SQLite are signed, so even though we bound the value @@ -2366,24 +2198,21 @@ void tst_QSqlDatabase::sqlite_enableRegexp() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - if (db.driverName().startsWith("QSQLITE2")) - QSKIP("SQLite3 specific test"); - db.close(); db.setConnectOptions("QSQLITE_ENABLE_REGEXP"); QVERIFY_SQL(db, open()); + TableScope ts(db, "regexp_test", __FILE__); QSqlQuery q(db); - const QString tableName(qTableName("regexp_test", __FILE__, db)); - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(text TEXT)").arg(tableName))); - QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES(?)").arg(tableName))); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(text TEXT)").arg(ts.tableName()))); + QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES(?)").arg(ts.tableName()))); q.addBindValue("a0"); QVERIFY_SQL(q, exec()); q.addBindValue("a1"); QVERIFY_SQL(q, exec()); QVERIFY_SQL(q, exec(QString("SELECT text FROM %1 WHERE text REGEXP 'a[^0]' " - "ORDER BY text").arg(tableName))); + "ORDER BY text").arg(ts.tableName()))); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toString(), QString("a1")); QFAIL_SQL(q, next()); @@ -2416,13 +2245,12 @@ void tst_QSqlDatabase::sqlite_check_json1() QSqlQuery q(db); const QString json1("{\"id\":1}"); - const QString tableName(qTableName("sqlite_check_json1", __FILE__, db)); - tst_Databases::safeDropTable(db, tableName); - QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(text TEXT)").arg(tableName))); - QVERIFY_SQL(q, exec(QString("INSERT INTO %1 VALUES(json('%2'))").arg(tableName, json1))); - QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES(?)").arg(tableName))); + TableScope ts(db, "sqlite_check_json1", __FILE__); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(text TEXT)").arg(ts.tableName()))); + QVERIFY_SQL(q, exec(QString("INSERT INTO %1 VALUES(json('%2'))").arg(ts.tableName(), json1))); + QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES(?)").arg(ts.tableName()))); q.addBindValue("json('{\"id\":2}')"); - QVERIFY_SQL(q, prepare(QString("SELECT * from %1 WHERE text = json('%2')").arg(tableName, json1))); + QVERIFY_SQL(q, prepare(QString("SELECT * from %1 WHERE text = json('%2')").arg(ts.tableName(), json1))); QVERIFY_SQL(q, exec()); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toString(), json1); @@ -2434,6 +2262,10 @@ void tst_QSqlDatabase::cloneDatabase() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + const auto wrapup = qScopeGuard([&]() { + QSqlDatabase::removeDatabase("clonedDatabase"); + QSqlDatabase::removeDatabase("clonedDatabaseCopy"); + }); { QSqlDatabase clonedDatabase = QSqlDatabase::cloneDatabase(db, "clonedDatabase"); QCOMPARE(clonedDatabase.databaseName(), db.databaseName()); @@ -2475,6 +2307,7 @@ public slots: QSqlDatabase invalidDb = QSqlDatabase::database("invalid"); QVERIFY(!invalidDb.isValid()); + const auto wrapup = qScopeGuard([&]() { QSqlDatabase::removeDatabase("CloneDB"); }); { QSqlDatabase clonedDatabase = QSqlDatabase::cloneDatabase(dbName, "CloneDB"); QVERIFY(!clonedDatabase.isOpen()); @@ -2504,5 +2337,32 @@ void tst_QSqlDatabase::multipleThreads() QTRY_VERIFY(t.isFinished()); } +void tst_QSqlDatabase::moveToThread() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + auto clonedDb = QSqlDatabase::cloneDatabase(db, "clonedDb"); + auto mainThread = QThread::currentThread(); + CHECK_DATABASE(db); + QCOMPARE(db.currentThread(), mainThread); + QCOMPARE(clonedDb.currentThread(), mainThread); + std::unique_ptr<QThread> t(QThread::create([&] { + db.moveToThread(mainThread); + QThread::currentThread()->exit(); + })); + db.moveToThread(t.get()); + QCOMPARE(db.currentThread(), t.get()); + QCOMPARE(clonedDb.currentThread(), mainThread); + t->start(); + QTRY_VERIFY(t->isRunning()); + QTRY_VERIFY(t->wait(30000)); + QCOMPARE(db.currentThread(), mainThread); + QCOMPARE(clonedDb.currentThread(), mainThread); + db = QSqlDatabase(); + clonedDb = QSqlDatabase(); + QSqlDatabase::removeDatabase("clonedDb"); +} + + QTEST_MAIN(tst_QSqlDatabase) #include "tst_qsqldatabase.moc" diff --git a/tests/auto/sql/kernel/qsqldriver/CMakeLists.txt b/tests/auto/sql/kernel/qsqldriver/CMakeLists.txt index d4ae3fb133..21dd3a6417 100644 --- a/tests/auto/sql/kernel/qsqldriver/CMakeLists.txt +++ b/tests/auto/sql/kernel/qsqldriver/CMakeLists.txt @@ -1,12 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from qsqldriver.pro. - ##################################################################### ## tst_qsqldriver Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsqldriver LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsqldriver SOURCES tst_qsqldriver.cpp diff --git a/tests/auto/sql/kernel/qsqldriver/tst_qsqldriver.cpp b/tests/auto/sql/kernel/qsqldriver/tst_qsqldriver.cpp index 3a6e16020f..fb8d804843 100644 --- a/tests/auto/sql/kernel/qsqldriver/tst_qsqldriver.cpp +++ b/tests/auto/sql/kernel/qsqldriver/tst_qsqldriver.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -35,6 +35,7 @@ static bool driverSupportsDefaultValues(QSqlDriver::DbmsType dbType) case QSqlDriver::SQLite: case QSqlDriver::PostgreSQL: case QSqlDriver::Oracle: + case QSqlDriver::MySqlServer: return true; default: break; @@ -52,47 +53,48 @@ void tst_QSqlDriver::initTestCase_data() void tst_QSqlDriver::recreateTestTables(QSqlDatabase db) { QSqlQuery q(db); - const QString relTEST1(qTableName("relTEST1", __FILE__, db)); + const QString tableName(qTableName("relTEST1", __FILE__, db)); + tst_Databases::safeDropTables(db, {tableName}); QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); if (dbType == QSqlDriver::PostgreSQL) QVERIFY_SQL( q, exec("set client_min_messages='warning'")); - tst_Databases::safeDropTable( db, relTEST1 ); QString doubleField; if (dbType == QSqlDriver::SQLite) doubleField = "more_data double"; else if (dbType == QSqlDriver::Oracle) doubleField = "more_data number(8,7)"; - else if (dbType == QSqlDriver::PostgreSQL) + else if (dbType == QSqlDriver::PostgreSQL || dbType == QSqlDriver::MimerSQL) doubleField = "more_data double precision"; else if (dbType == QSqlDriver::Interbase) doubleField = "more_data numeric(8,7)"; else doubleField = "more_data double(8,7)"; const QString defValue(driverSupportsDefaultValues(dbType) ? QStringLiteral("DEFAULT 'defaultVal'") : QString()); - QVERIFY_SQL( q, exec("create table " + relTEST1 + + QVERIFY_SQL( q, exec("create table " + tableName + " (id int not null primary key, name varchar(20) " + defValue + ", title_key int, another_title_key int, " + doubleField + QLatin1Char(')'))); - QVERIFY_SQL( q, exec("insert into " + relTEST1 + " values(1, 'harry', 1, 2, 1.234567)")); - QVERIFY_SQL( q, exec("insert into " + relTEST1 + " values(2, 'trond', 2, 1, 8.901234)")); - QVERIFY_SQL( q, exec("insert into " + relTEST1 + " values(3, 'vohi', 1, 2, 5.678901)")); - QVERIFY_SQL( q, exec("insert into " + relTEST1 + " values(4, 'boris', 2, 2, 2.345678)")); + QVERIFY_SQL( q, exec("insert into " + tableName + " values(1, 'harry', 1, 2, 1.234567)")); + QVERIFY_SQL( q, exec("insert into " + tableName + " values(2, 'trond', 2, 1, 8.901234)")); + QVERIFY_SQL( q, exec("insert into " + tableName + " values(3, 'vohi', 1, 2, 5.678901)")); + QVERIFY_SQL( q, exec("insert into " + tableName + " values(4, 'boris', 2, 2, 2.345678)")); } void tst_QSqlDriver::initTestCase() { - foreach (const QString &dbname, dbs.dbNames) + for (const QString &dbname : std::as_const(dbs.dbNames)) recreateTestTables(QSqlDatabase::database(dbname)); } void tst_QSqlDriver::cleanupTestCase() { - foreach (const QString &dbName, dbs.dbNames) { + for (const QString &dbName : std::as_const(dbs.dbNames)) { QSqlDatabase db = QSqlDatabase::database(dbName); - tst_Databases::safeDropTable(db, qTableName("relTEST1", __FILE__, db)); + QStringList tables = {qTableName("relTEST1", __FILE__, db)}; const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); if (dbType == QSqlDriver::Oracle) - tst_Databases::safeDropTable(db, qTableName("clobTable", __FILE__, db)); + tables.push_back(qTableName("clobTable", __FILE__, db)); + tst_Databases::safeDropTables(db, tables); } dbs.close(); } @@ -111,7 +113,7 @@ void tst_QSqlDriver::record() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - QString tablename(qTableName("relTEST1", __FILE__, db)); + QString tablename(qTableName("relTEST1", __FILE__, db, false)); QStringList fields; fields << "id" << "name" << "title_key" << "another_title_key" << "more_data"; @@ -125,14 +127,20 @@ void tst_QSqlDriver::record() QCOMPARE(rec.field(1).length(), 20); if (dbType == QSqlDriver::Interbase || dbType == QSqlDriver::Oracle || dbType == QSqlDriver::DB2) - for(int i = 0; i < fields.count(); ++i) + for(int i = 0; i < fields.size(); ++i) fields[i] = fields[i].toUpper(); - for (int i = 0; i < fields.count(); ++i) + for (int i = 0; i < fields.size(); ++i) QCOMPARE(rec.fieldName(i), fields[i]); - if (driverSupportsDefaultValues(dbType)) - QCOMPARE(rec.field(QStringLiteral("name")).defaultValue().toString(), QStringLiteral("defaultVal")); + if (driverSupportsDefaultValues(dbType)) { + auto defVal = rec.field(QStringLiteral("name")).defaultValue().toString(); + if (dbType == QSqlDriver::MySqlServer && defVal.startsWith('\'') && defVal.endsWith('\'')) { + qDebug() << "MariaDB 10.6 default string value is escaped:" << defVal; + defVal = defVal.mid(1, defVal.size() - 2); + } + QCOMPARE(defVal, QStringLiteral("defaultVal")); + } if (dbType == QSqlDriver::Oracle || dbType == QSqlDriver::DB2) tablename = tablename.toUpper(); @@ -145,7 +153,7 @@ void tst_QSqlDriver::record() QCOMPARE(rec.count(), 5); } - for (int i = 0; i < fields.count(); ++i) + for (int i = 0; i < fields.size(); ++i) QCOMPARE(rec.fieldName(i), fields[i]); if (dbType == QSqlDriver::Interbase || dbType == QSqlDriver::Oracle || dbType == QSqlDriver::DB2) @@ -155,8 +163,9 @@ void tst_QSqlDriver::record() //check that we can't get records using incorrect tablename casing that's been quoted rec = db.driver()->record(db.driver()->escapeIdentifier(tablename,QSqlDriver::TableName)); - if (dbType == QSqlDriver::MySqlServer || dbType == QSqlDriver::SQLite || dbType == QSqlDriver::Sybase - || dbType == QSqlDriver::MSSqlServer || tst_Databases::isMSAccess(db)) + if (dbType == QSqlDriver::MySqlServer || dbType == QSqlDriver::SQLite + || dbType == QSqlDriver::Sybase || dbType == QSqlDriver::MSSqlServer + || tst_Databases::isMSAccess(db) || dbType == QSqlDriver::MimerSQL) QCOMPARE(rec.count(), 5); //mysql, sqlite and tds will match else QCOMPARE(rec.count(), 0); @@ -169,7 +178,7 @@ void tst_QSqlDriver::primaryIndex() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - QString tablename(qTableName("relTEST1", __FILE__, db)); + QString tablename(qTableName("relTEST1", __FILE__, db, false)); //check that we can get primary index using unquoted mixed case table name QSqlIndex index = db.driver()->primaryIndex(tablename); QCOMPARE(index.count(), 1); @@ -205,22 +214,23 @@ void tst_QSqlDriver::primaryIndex() tablename = tablename.toUpper(); index = db.driver()->primaryIndex(db.driver()->escapeIdentifier(tablename, QSqlDriver::TableName)); - if (dbType == QSqlDriver::MySqlServer || dbType == QSqlDriver::SQLite || dbType == QSqlDriver::Sybase - || dbType == QSqlDriver::MSSqlServer || tst_Databases::isMSAccess(db)) + if (dbType == QSqlDriver::MySqlServer || dbType == QSqlDriver::SQLite + || dbType == QSqlDriver::Sybase || dbType == QSqlDriver::MSSqlServer + || tst_Databases::isMSAccess(db) || dbType == QSqlDriver::MimerSQL) QCOMPARE(index.count(), 1); //mysql will always find the table name regardless of casing else QCOMPARE(index.count(), 0); // Test getting a primary index for a table with a clob in it - QTBUG-64427 if (dbType == QSqlDriver::Oracle) { - const QString clobTable(qTableName("clobTable", __FILE__, db)); + TableScope ts(db, "clobTable", __FILE__); QSqlQuery qry(db); - QVERIFY_SQL(qry, exec("CREATE TABLE " + clobTable + " (id INTEGER, clobField CLOB)")); - QVERIFY_SQL(qry, exec("CREATE UNIQUE INDEX " + clobTable + "IDX ON " + clobTable + " (id)")); - QVERIFY_SQL(qry, exec("ALTER TABLE " + clobTable + " ADD CONSTRAINT " + clobTable + + QVERIFY_SQL(qry, exec("CREATE TABLE " + ts.tableName() + " (id INTEGER, clobField CLOB)")); + QVERIFY_SQL(qry, exec("CREATE UNIQUE INDEX " + ts.tableName() + "IDX ON " + ts.tableName() + " (id)")); + QVERIFY_SQL(qry, exec("ALTER TABLE " + ts.tableName() + " ADD CONSTRAINT " + ts.tableName() + "PK PRIMARY KEY(id)")); - QVERIFY_SQL(qry, exec("ALTER TABLE " + clobTable + " MODIFY (id NOT NULL ENABLE)")); - const QSqlIndex primaryIndex = db.driver()->primaryIndex(clobTable); + QVERIFY_SQL(qry, exec("ALTER TABLE " + ts.tableName() + " MODIFY (id NOT NULL ENABLE)")); + const QSqlIndex primaryIndex = db.driver()->primaryIndex(ts.tableName()); QCOMPARE(primaryIndex.count(), 1); QCOMPARE(primaryIndex.fieldName(0), QStringLiteral("ID")); } diff --git a/tests/auto/sql/kernel/qsqlerror/CMakeLists.txt b/tests/auto/sql/kernel/qsqlerror/CMakeLists.txt index a56f4dc8ae..b604399ef1 100644 --- a/tests/auto/sql/kernel/qsqlerror/CMakeLists.txt +++ b/tests/auto/sql/kernel/qsqlerror/CMakeLists.txt @@ -1,12 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from qsqlerror.pro. - ##################################################################### ## tst_qsqlerror Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsqlerror LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsqlerror SOURCES tst_qsqlerror.cpp diff --git a/tests/auto/sql/kernel/qsqlerror/tst_qsqlerror.cpp b/tests/auto/sql/kernel/qsqlerror/tst_qsqlerror.cpp index c83867fcc4..2be7d08cf8 100644 --- a/tests/auto/sql/kernel/qsqlerror/tst_qsqlerror.cpp +++ b/tests/auto/sql/kernel/qsqlerror/tst_qsqlerror.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -108,12 +108,16 @@ void tst_QSqlError::moveOperator() void tst_QSqlError::operators() { - QSqlError error1(QString(), QString(), QSqlError::NoError); - QSqlError error2(QString(), QString(), QSqlError::NoError); - QSqlError error3(QString(), QString(), QSqlError::UnknownError); + QSqlError error1(QStringLiteral("a"), QStringLiteral("b"), QSqlError::NoError, QStringLiteral("ec1")); + QSqlError error2(QStringLiteral("c"), QStringLiteral("d"), QSqlError::NoError, QStringLiteral("ec1")); + QSqlError error3(QString(), QString(), QSqlError::UnknownError, QStringLiteral("ec1")); + QSqlError error4(QString(), QString(), QSqlError::NoError, QStringLiteral("ec2")); + QSqlError error5(QString(), QString(), QSqlError::UnknownError, QStringLiteral("ec2")); QCOMPARE(error1, error2); QVERIFY(error1 != error3); + QVERIFY(error1 != error4); + QVERIFY(error4 != error5); } void tst_QSqlError::qtbug_74575() diff --git a/tests/auto/sql/kernel/qsqlfield/CMakeLists.txt b/tests/auto/sql/kernel/qsqlfield/CMakeLists.txt index 465dfc7dc4..2fef227201 100644 --- a/tests/auto/sql/kernel/qsqlfield/CMakeLists.txt +++ b/tests/auto/sql/kernel/qsqlfield/CMakeLists.txt @@ -1,12 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from qsqlfield.pro. - ##################################################################### ## tst_qsqlfield Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsqlfield LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsqlfield SOURCES tst_qsqlfield.cpp diff --git a/tests/auto/sql/kernel/qsqlfield/tst_qsqlfield.cpp b/tests/auto/sql/kernel/qsqlfield/tst_qsqlfield.cpp index 79c774630d..5e012ba39c 100644 --- a/tests/auto/sql/kernel/qsqlfield/tst_qsqlfield.cpp +++ b/tests/auto/sql/kernel/qsqlfield/tst_qsqlfield.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -39,6 +39,7 @@ private slots: void clear(); void setTableName_data(); void setTableName(); + void moveSemantics(); }; // Testing get/set functions @@ -344,5 +345,24 @@ void tst_QSqlField::setTableName() QCOMPARE(field.tableName(), tableName); } +void tst_QSqlField::moveSemantics() +{ + QSqlField field("test", QMetaType(QMetaType::QString), "testTable"); + QSqlField empty; + field.setValue("string"); + auto moved = std::move(field); + // `field` is now partially-formed + + // moving transfers state: + QCOMPARE(moved.value().toString(), QLatin1String("string")); + + // moved-from objects can be assigned-to: + field = empty; + QVERIFY(field.value().isNull()); + + // moved-from object can be destroyed: + moved = std::move(field); +} + QTEST_MAIN(tst_QSqlField) #include "tst_qsqlfield.moc" diff --git a/tests/auto/sql/kernel/qsqlindex/CMakeLists.txt b/tests/auto/sql/kernel/qsqlindex/CMakeLists.txt new file mode 100644 index 0000000000..3373948b5f --- /dev/null +++ b/tests/auto/sql/kernel/qsqlindex/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qsqlindex Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsqlindex LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qsqlindex + SOURCES + tst_qsqlindex.cpp + LIBRARIES + Qt::CorePrivate + Qt::Sql + Qt::SqlPrivate +) diff --git a/tests/auto/sql/kernel/qsqlindex/tst_qsqlindex.cpp b/tests/auto/sql/kernel/qsqlindex/tst_qsqlindex.cpp new file mode 100644 index 0000000000..2f44359133 --- /dev/null +++ b/tests/auto/sql/kernel/qsqlindex/tst_qsqlindex.cpp @@ -0,0 +1,126 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QtSql/QtSql> + +#include <QtCore/QDateTime> +#include <QtCore/QTimeZone> + +#include <numeric> + +#include "../qsqldatabase/tst_databases.h" + +using namespace Qt::StringLiterals; + +QString qtest; + +class tst_QSqlIndex : public QObject +{ + Q_OBJECT + +public: + tst_QSqlIndex(); + +private slots: + void construction_data(); + void construction(); + void assignment_data(); + void assignment(); + void basicFunctions(); +}; + +tst_QSqlIndex::tst_QSqlIndex() +{ +} + +void tst_QSqlIndex::construction_data() +{ + QTest::addColumn<QSqlIndex>("sqlIndex"); + QTest::addColumn<QString>("cursorName"); + QTest::addColumn<QString>("name"); + + const QString cursorName("cusorName"_L1); + const QString name("name"_L1); + QSqlIndex sqlIndex(cursorName, name); + QTest::newRow("ctor1") << QSqlIndex() << QString() << QString(); + QTest::newRow("ctor2") << sqlIndex << cursorName << name; + QTest::newRow("copy ctor") << QSqlIndex(sqlIndex) << cursorName << name; + QTest::newRow("move ctor") << QSqlIndex(std::move(sqlIndex)) << cursorName << name; +} + +void tst_QSqlIndex::construction() +{ + QFETCH(QSqlIndex, sqlIndex); + QFETCH(QString, cursorName); + QFETCH(QString, name); + + QCOMPARE(sqlIndex.cursorName(), cursorName); + QCOMPARE(sqlIndex.name(), name); + QCOMPARE(sqlIndex.isDescending(0), false); + QCOMPARE(sqlIndex.count(), 0); +} + +void tst_QSqlIndex::assignment_data() +{ + QTest::addColumn<QSqlIndex>("sqlIndex"); + QTest::addColumn<QString>("cursorName"); + QTest::addColumn<QString>("name"); + + const QString cursorName("cusorName"_L1); + const QString name("name"_L1); + QSqlIndex sqlIndex(cursorName, name); + QSqlIndex sqlIndex1 = sqlIndex; + QSqlIndex sqlIndex2 = std::move(sqlIndex); + sqlIndex = std::move(sqlIndex2); + QTest::newRow("copy assignment") << sqlIndex1 << cursorName << name; + QTest::newRow("move assignment") << sqlIndex << cursorName << name; +} + +void tst_QSqlIndex::assignment() +{ + QFETCH(QSqlIndex, sqlIndex); + QFETCH(QString, cursorName); + QFETCH(QString, name); + + QCOMPARE(sqlIndex.cursorName(), cursorName); + QCOMPARE(sqlIndex.name(), name); + QCOMPARE(sqlIndex.isDescending(0), false); + QCOMPARE(sqlIndex.count(), 0); +} + +void tst_QSqlIndex::basicFunctions() +{ + QSqlIndex sqlIndex("cursorName"_L1, "name"_L1); + const QSqlField f1("field1"_L1, QMetaType(QMetaType::UInt), "table1"_L1); + const QSqlField f2("field2"_L1, QMetaType(QMetaType::Double), "table2"_L1); + + QCOMPARE(sqlIndex.cursorName(), "cursorName"_L1); + sqlIndex.setCursorName("updatedCursorName"_L1); + QCOMPARE(sqlIndex.name(), "name"_L1); + sqlIndex.setName("updatedName"_L1); + QCOMPARE(sqlIndex.cursorName(), "updatedCursorName"_L1); + QCOMPARE(sqlIndex.name(), "updatedName"_L1); + + sqlIndex.append(f1); + QCOMPARE(sqlIndex.count(), 1); + QCOMPARE(sqlIndex.isDescending(0), false); + + sqlIndex.append(f2, true); + QCOMPARE(sqlIndex.count(), 2); + QCOMPARE(sqlIndex.isDescending(0), false); + QCOMPARE(sqlIndex.isDescending(1), true); + + sqlIndex.setDescending(0, true); + sqlIndex.setDescending(1, false); + sqlIndex.setDescending(2, true); + QCOMPARE(sqlIndex.count(), 2); + QCOMPARE(sqlIndex.isDescending(0), true); + QCOMPARE(sqlIndex.isDescending(1), false); + + QCOMPARE(sqlIndex.field(0), f1); + QCOMPARE(sqlIndex.field(1), f2); +} + +QTEST_MAIN(tst_QSqlIndex) +#include "tst_qsqlindex.moc" diff --git a/tests/auto/sql/kernel/qsqlquery/CMakeLists.txt b/tests/auto/sql/kernel/qsqlquery/CMakeLists.txt index 7be6b0071a..e5a5b2b2f8 100644 --- a/tests/auto/sql/kernel/qsqlquery/CMakeLists.txt +++ b/tests/auto/sql/kernel/qsqlquery/CMakeLists.txt @@ -1,12 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from qsqlquery.pro. - ##################################################################### ## tst_qsqlquery Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsqlquery LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsqlquery SOURCES tst_qsqlquery.cpp diff --git a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp index b4ad75286b..56fb5cd05f 100644 --- a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp +++ b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp @@ -1,9 +1,12 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtSql/QtSql> +#include <QtCore/QDateTime> +#include <QtCore/QTimeZone> + #include <numeric> #include "../qsqldatabase/tst_databases.h" @@ -55,8 +58,6 @@ private slots: void last(); void seek_data() { generic_data(); } void seek(); - void transaction_data() { generic_data(); } - void transaction(); void record_data() { generic_data(); } void record(); void record_sqlite_data() { generic_data("QSQLITE"); } @@ -76,9 +77,10 @@ private slots: void psql_forwardOnlyQueryResultsLost_data() { generic_data("QPSQL"); } void psql_forwardOnlyQueryResultsLost(); + void positionalBindingEnabled_data() { generic_data(); } + void positionalBindingEnabled(); + // Bug-specific tests: - void tds_bitField_data() { generic_data("QTDS"); } - void tds_bitField(); void oci_nullBlob_data() { generic_data("QOCI"); } void oci_nullBlob(); void blob_data() { generic_data(); } @@ -171,11 +173,11 @@ private slots: void task_250026(); void crashQueryOnCloseDatabase(); - void task_233829_data() { generic_data("QPSQL"); } - void task_233829(); + void testNaN_data() { generic_data("QPSQL"); } + void testNaN(); - void QTBUG_12477_data() { generic_data("QPSQL"); } - void QTBUG_12477(); + void psqlNumericMetadata_data() { generic_data("QPSQL"); } + void psqlNumericMetadata(); void sqlServerReturn0_data() { generic_data(); } void sqlServerReturn0(); @@ -226,6 +228,9 @@ private slots: void sqlite_real_data() { generic_data("QSQLITE"); } void sqlite_real(); + void prepared_query_json_row_data() { generic_data(); } + void prepared_query_json_row(); + void aggregateFunctionTypes_data() { generic_data(); } void aggregateFunctionTypes(); @@ -238,12 +243,23 @@ private slots: void QTBUG_73286_data() { generic_data("QODBC"); } void QTBUG_73286(); + void insertVarChar1_data() { generic_data("QODBC"); } + void insertVarChar1(); + void dateTime_data(); void dateTime(); void ibaseArray_data() { generic_data("QIBASE"); } void ibaseArray(); + void ibaseDateTimeWithTZ_data(); + void ibaseDateTimeWithTZ(); + void ibaseTimeStampTzArray_data() { generic_data("QIBASE"); } + void ibaseTimeStampTzArray(); + + void psqlJsonOperator_data() { generic_data("QPSQL"); } + void psqlJsonOperator(); + // Double addDatabase() with same name leaves system in a state that breaks // invalidQuery() if run later; so put this one last ! void prematureExec_data() { generic_data(); } @@ -330,55 +346,8 @@ void tst_QSqlQuery::dropTestTables(QSqlDatabase db) // Drop all the table in case a testcase failed: tablenames << qtest << qTableName("qtest_null", __FILE__, db) - << qTableName("qtest_writenull", __FILE__, db) - << qTableName("qtest_blob", __FILE__, db) - << qTableName("qtest_bittest", __FILE__, db) - << qTableName("qtest_nullblob", __FILE__, db) - << qTableName("qtest_rawtest", __FILE__, db) - << qTableName("qtest_precision", __FILE__, db) - << qTableName("qtest_prepare", __FILE__, db) - << qTableName("qtestj1", __FILE__, db) - << qTableName("qtestj2", __FILE__, db) - << qTableName("char1Select", __FILE__, db) - << qTableName("char1SU", __FILE__, db) - << qTableName("qxmltest", __FILE__, db) - << qTableName("qtest_exerr", __FILE__, db) - << qTableName("qtest_empty", __FILE__, db) - << qTableName("clobby", __FILE__, db) - << qTableName("bindtest", __FILE__, db) - << qTableName("more_results", __FILE__, db) - << qTableName("blobstest", __FILE__, db) - << qTableName("oraRowId", __FILE__, db) - << qTableName("bug43874", __FILE__, db) - << qTableName("bug6421", __FILE__, db).toUpper() - << qTableName("bug5765", __FILE__, db) - << qTableName("bug6852", __FILE__, db) - << qTableName("bug21884", __FILE__, db) - << qTableName("bug23895", __FILE__, db) - << qTableName("qtest_lockedtable", __FILE__, db) - << qTableName("Planet", __FILE__, db) - << qTableName("task_250026", __FILE__, db) - << qTableName("task_234422", __FILE__, db) - << qTableName("test141895", __FILE__, db) - << qTableName("qtest_oraOCINumber", __FILE__, db) - << qTableName("bug2192", __FILE__, db) << qTableName("tst_record", __FILE__, db); - if (dbType == QSqlDriver::PostgreSQL) - tablenames << qTableName("task_233829", __FILE__, db); - - if (dbType == QSqlDriver::SQLite) - tablenames << qTableName("record_sqlite", __FILE__, db); - - if (dbType == QSqlDriver::MSSqlServer || dbType == QSqlDriver::Oracle) - tablenames << qTableName("qtest_longstr", __FILE__, db); - - if (dbType == QSqlDriver::MSSqlServer) - db.exec("DROP PROCEDURE " + qTableName("test141895_proc", __FILE__, db)); - - if (dbType == QSqlDriver::MySqlServer) - db.exec("DROP PROCEDURE IF EXISTS " + qTableName("bug6852_proc", __FILE__, db)); - tst_Databases::safeDropTables(db, tablenames); if (dbType == QSqlDriver::Oracle) { @@ -401,7 +370,7 @@ void tst_QSqlQuery::createTestTables(QSqlDatabase db) if (dbType == QSqlDriver::PostgreSQL) { QVERIFY_SQL(q, exec(QLatin1String( "create table %1 (id serial NOT NULL, t_varchar varchar(20), " - "t_char char(20), primary key(id)) WITH OIDS").arg(qtest))); + "t_char char(20), primary key(id))").arg(qtest))); } else { QVERIFY_SQL(q, exec(QLatin1String( "create table %1 (id int %2 NOT NULL, t_varchar varchar(20), " @@ -446,12 +415,11 @@ void tst_QSqlQuery::char1Select() CHECK_DATABASE(db); { + TableScope ts(db, "char1Select", __FILE__); QSqlQuery q(db); - const QString tbl = qTableName("char1Select", __FILE__, db); - q.exec("drop table " + tbl); - QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id char(1))").arg(tbl))); - QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values ('a')").arg(tbl))); - QVERIFY_SQL(q, exec("select * from " + tbl)); + QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id char(1))").arg(ts.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values ('a')").arg(ts.tableName()))); + QVERIFY_SQL(q, exec("select * from " + ts.tableName())); QVERIFY(q.next()); if (tst_Databases::getDatabaseType(db) == QSqlDriver::Interbase) QCOMPARE(q.value(0).toString().left(1), u"a"); @@ -475,11 +443,12 @@ void tst_QSqlQuery::char1SelectUnicode() QSKIP("Database not unicode capable"); QString uniStr(QChar(0x0915)); // DEVANAGARI LETTER KA + TableScope ts(db, "char1SU", __FILE__); QSqlQuery q(db); QLatin1String createQuery; - const QString char1SelectUnicode(qTableName("char1SU", __FILE__, db)); switch (dbType) { + case QSqlDriver::MimerSQL: case QSqlDriver::MSSqlServer: createQuery = QLatin1String("create table %1(id nchar(1))"); break; @@ -499,12 +468,12 @@ void tst_QSqlQuery::char1SelectUnicode() break; } - QVERIFY_SQL(q, exec(createQuery.arg(char1SelectUnicode))); - QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 values(?)").arg(char1SelectUnicode))); + QVERIFY_SQL(q, exec(createQuery.arg(ts.tableName()))); + QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 values(?)").arg(ts.tableName()))); q.bindValue(0, uniStr); QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec("select * from " + char1SelectUnicode)); + QVERIFY_SQL(q, exec("select * from " + ts.tableName())); QVERIFY(q.next()); if (!q.value(0).toString().isEmpty()) @@ -519,7 +488,8 @@ void tst_QSqlQuery::oraRowId() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString oraRowId(qTableName("oraRowId", __FILE__, db)); + TableScope ts(db, "oraRowId", __FILE__); + const auto &oraRowId = ts.tableName(); QSqlQuery q(db); QVERIFY_SQL(q, exec("select rowid from " + qtest)); @@ -555,7 +525,7 @@ void tst_QSqlQuery::mysql_outValues() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); const QString hello(qTableName("hello", __FILE__, db)); - const QString qtestproc(qTableName("qtestproc", __FILE__, db)); + ProcScope ps(db, "qtestproc", __FILE__); QSqlQuery q(db); @@ -577,25 +547,22 @@ void tst_QSqlQuery::mysql_outValues() QCOMPARE(q.value(0).toString(), u"Hello harald"); QVERIFY_SQL(q, exec("drop function " + hello)); - q.exec("drop procedure " + qtestproc); QVERIFY_SQL(q, exec(QLatin1String("create procedure %1 () BEGIN " - "select * from %2 order by id; END").arg(qtestproc, qtest))); - QVERIFY_SQL(q, exec(QLatin1String("call %1()").arg(qtestproc))); + "select * from %2 order by id; END").arg(ps.name(), qtest))); + QVERIFY_SQL(q, exec(QLatin1String("call %1()").arg(ps.name()))); QVERIFY_SQL(q, next()); QCOMPARE(q.value(1).toString(), u"VarChar1"); - QVERIFY_SQL(q, exec("drop procedure " + qtestproc)); + QVERIFY_SQL(q, exec("drop procedure " + ps.name())); QVERIFY_SQL(q, exec(QLatin1String("create procedure %1 (OUT param1 INT) " - "BEGIN set param1 = 42; END").arg(qtestproc))); + "BEGIN set param1 = 42; END").arg(ps.name()))); - QVERIFY_SQL(q, exec(QLatin1String("call %1 (@out)").arg(qtestproc))); + QVERIFY_SQL(q, exec(QLatin1String("call %1 (@out)").arg(ps.name()))); QVERIFY_SQL(q, exec("select @out")); QCOMPARE(q.record().fieldName(0), u"@out"); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toInt(), 42); - - QVERIFY_SQL(q, exec("drop procedure " + qtestproc)); } void tst_QSqlQuery::bindBool() @@ -778,7 +745,8 @@ void tst_QSqlQuery::oraClob() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString clobby(qTableName("clobby", __FILE__, db)); + TableScope ts(db, "clobby", __FILE__); + const auto &clobby = ts.tableName(); QSqlQuery q(db); @@ -854,19 +822,15 @@ void tst_QSqlQuery::storedProceduresIBase() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + ProcScope ps(db, "TESTPROC", __FILE__); QSqlQuery q(db); - const auto procName = qTableName("TESTPROC", __FILE__, db); - q.exec("drop procedure " + procName); - QVERIFY_SQL(q, exec(QLatin1String("create procedure %1 RETURNS (x integer, y varchar(20)) " "AS BEGIN " " x = 42; " " y = 'Hello Anders'; " - "END").arg(procName))); - const auto tidier = qScopeGuard([&]() { q.exec("drop procedure " + procName); }); - - QVERIFY_SQL(q, prepare("execute procedure " + procName)); + "END").arg(ps.name()))); + QVERIFY_SQL(q, prepare("execute procedure " + ps.name())); QVERIFY_SQL(q, exec()); // Check for a valid result set: @@ -893,12 +857,11 @@ void tst_QSqlQuery::outValuesDB2() if (!db.driver()->hasFeature(QSqlDriver::PreparedQueries)) QSKIP("Test requires prepared query support"); - QSqlQuery q(db); + ProcScope ps(db, "tst_outValues", __FILE__); + QSqlQuery q(db); q.setForwardOnly(true); - const QString procName = qTableName("tst_outValues", __FILE__, db); - q.exec("drop procedure " + procName); // non-fatal QVERIFY_SQL(q, exec(QLatin1String("CREATE PROCEDURE %1 " "(OUT x int, OUT x2 double, OUT x3 char(20))\n" "LANGUAGE SQL\n" @@ -906,9 +869,9 @@ void tst_QSqlQuery::outValuesDB2() " SET x = 42;\n" " SET x2 = 4.2;\n" " SET x3 = 'Homer';\n" - "END P1").arg(procName))); + "END P1").arg(ps.name()))); - QVERIFY_SQL(q, prepare(QLatin1String("call %1(?, ?, ?)").arg(procName))); + QVERIFY_SQL(q, prepare(QLatin1String("call %1(?, ?, ?)").arg(ps.name()))); q.addBindValue(0, QSql::Out); q.addBindValue(0.0, QSql::Out); @@ -926,11 +889,11 @@ void tst_QSqlQuery::outValues() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString tst_outValues(qTableName("tst_outValues", __FILE__, db)); if (!db.driver()->hasFeature(QSqlDriver::PreparedQueries)) QSKIP("Test requires prepared query support"); + ProcScope ps(db, "tst_outValues", __FILE__); QSqlQuery q(db); q.setForwardOnly(true); @@ -944,7 +907,6 @@ void tst_QSqlQuery::outValues() caller = QLatin1String("call %1(?)"); break; case QSqlDriver::DB2: - q.exec("drop procedure " + tst_outValues); // non-fatal creator = QLatin1String("CREATE PROCEDURE %1 (OUT x int)\n" "LANGUAGE SQL\n" "P1: BEGIN\n" @@ -953,7 +915,6 @@ void tst_QSqlQuery::outValues() caller = QLatin1String("call %1(?)"); break; case QSqlDriver::MSSqlServer: - q.exec("drop procedure " + tst_outValues); // non-fatal creator = QLatin1String("create procedure %1 (@x int out) as\n" "begin\n" " set @x = 42\n" @@ -964,8 +925,8 @@ void tst_QSqlQuery::outValues() QSKIP("Don't know how to create a stored procedure for this database server, " "please fix this test"); } - QVERIFY_SQL(q, exec(creator.arg(tst_outValues))); - QVERIFY(q.prepare(caller.arg(tst_outValues))); + QVERIFY_SQL(q, exec(creator.arg(ps.name()))); + QVERIFY(q.prepare(caller.arg(ps.name()))); q.addBindValue(0, QSql::Out); @@ -991,15 +952,15 @@ void tst_QSqlQuery::blob() for (int i = 0; i < ba.size(); ++i) ba[i] = i % 256; + TableScope ts(db, "qtest_blob", __FILE__); QSqlQuery q(db); q.setForwardOnly(true); - const QString tableName = qTableName("qtest_blob", __FILE__, db); QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id int not null primary key, t_blob %2)") - .arg(tableName, tst_Databases::blobTypeName(db, BLOBSIZE)))); + .arg(ts.tableName(), tst_Databases::blobTypeName(db, BLOBSIZE)))); QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 (id, t_blob) values (?, ?)") - .arg(tableName))); + .arg(ts.tableName()))); for (int i = 0; i < BLOBCOUNT; ++i) { q.addBindValue(i); @@ -1007,7 +968,7 @@ void tst_QSqlQuery::blob() QVERIFY_SQL(q, exec()); } - QVERIFY_SQL(q, exec("select * from " + tableName)); + QVERIFY_SQL(q, exec("select * from " + ts.tableName())); for (int i = 0; i < BLOBCOUNT; ++i) { QVERIFY(q.next()); @@ -1017,10 +978,10 @@ void tst_QSqlQuery::blob() "array sizes differ, expected (at least) %" PRIdQSIZETYPE ", got %" PRIdQSIZETYPE, ba.size(), res.size()))); - for (int i2 = 0; i2 < ba.size(); ++i2) { + for (qsizetype i2 = 0; i2 < ba.size(); ++i2) { if (res[i2] != ba[i2]) { QFAIL(qPrintable(QString::asprintf( - "ByteArrays differ at position %d, expected %hhu, got %hhu", + "ByteArrays differ at position %lld, expected %hhu, got %hhu", i2, ba[i2], res[i2]))); } } @@ -1107,8 +1068,9 @@ void tst_QSqlQuery::record() QCOMPARE(q.record().fieldName(0).toLower(), u"id"); QCOMPARE(q.value(0).toInt(), 2); - if (tst_Databases::getDatabaseType(db) == QSqlDriver::Oracle) - QSKIP("Getting the tablename is not supported in Oracle"); + if (tst_Databases::getDatabaseType(db) == QSqlDriver::Oracle + || tst_Databases::getDatabaseType(db) == QSqlDriver::MimerSQL) + QSKIP("Getting the tablename is not supported in Oracle and Mimer SQL"); const auto lowerQTest = qtest.toLower(); for (int i = 0; i < 3; ++i) @@ -1224,12 +1186,8 @@ void tst_QSqlQuery::numRowsAffected() QSqlQuery q2(db); QVERIFY_SQL(q2, exec(QLatin1String("insert into %1 values (42001, 'homer', 'marge')") .arg(qtest))); - - if (!db.driverName().startsWith("QSQLITE2")) { - // SQLite 2.x accumulates changed rows in nested queries. See task 33794 - QCOMPARE(q2.numRowsAffected(), 1); - QCOMPARE(q2.numRowsAffected(), 1); // yes, we check twice - } + QCOMPARE(q2.numRowsAffected(), 1); + QCOMPARE(q2.numRowsAffected(), 1); // yes, we check twice } void tst_QSqlQuery::size() @@ -1776,25 +1734,24 @@ void tst_QSqlQuery::writeNull() CHECK_DATABASE(db); const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); - QSqlQuery q(db); - const QString tableName = qTableName("qtest_writenull", __FILE__, db); - // The test data table is already used, so use a local hash to exercise the various - // cases from the QSqlResultPrivate::isVariantNull helper. Only PostgreSQL supports - // QUuid. + // cases from the QSqlResultPrivate::isVariantNull helper. Only PostgreSQL and Mimer SQL + // supports QUuid. QMultiHash<QString, QVariant> nullableTypes = { - {"varchar(20)", u"not null"_s}, - {"varchar(20)", "not null"_ba}, - {"date", QDateTime::currentDateTime()}, - {"date", QDate::currentDate()}, - {"date", QTime::currentTime()}, + { "varchar(20)", u"not null"_s }, + { "varchar(20)", "not null"_ba }, + { tst_Databases::dateTimeTypeName(db), QDateTime::currentDateTime() }, + { tst_Databases::dateTypeName(db), QDate::currentDate() }, + { tst_Databases::timeTypeName(db), QTime::currentTime() }, }; if (dbType == QSqlDriver::PostgreSQL) nullableTypes["uuid"] = QUuid::createUuid(); + if (dbType == QSqlDriver::MimerSQL) + nullableTypes["builtin.uuid"] = QUuid::createUuid(); // Helper to count rows with null values in the data column. // Since QSqlDriver::QuerySize might not be supported, we have to count anyway - const auto countRowsWithNull = [&]{ + const auto countRowsWithNull = [&](QSqlQuery &q, const QString &tableName){ q.exec(QLatin1String("select id, data from %1 where data is null").arg(tableName)); int size = 0; while (q.next()) @@ -1803,37 +1760,37 @@ void tst_QSqlQuery::writeNull() }; for (const auto &nullableType : nullableTypes.keys()) { - const auto tableGuard = qScopeGuard([&]{ - q.exec("drop table " + tableName); - }); + TableScope ts(db, "qtest_writenull", __FILE__); + QSqlQuery q(db); + const QVariant nonNullValue = nullableTypes.value(nullableType); // some useful diagnostic output in case of any test failure auto errorHandler = qScopeGuard([&]{ qWarning() << "Test failure for data type" << nonNullValue.metaType().name(); - q.exec("select id, data from " + tableName); + q.exec("select id, data from " + ts.tableName()); while (q.next()) qWarning() << q.value(0) << q.value(1); }); QString createQuery = QLatin1String("create table %3 (id int, data %1%2)") .arg(nullableType, dbType == QSqlDriver::MSSqlServer || dbType == QSqlDriver::Sybase ? " null" : "", - tableName); + ts.tableName()); QVERIFY_SQL(q, exec(createQuery)); int expectedNullCount = 0; // Verify that inserting a non-null value works: - QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 values(:id, :data)").arg(tableName))); + QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 values(:id, :data)").arg(ts.tableName()))); q.bindValue(":id", expectedNullCount); q.bindValue(":data", nonNullValue); QVERIFY_SQL(q, exec()); - QCOMPARE(countRowsWithNull(), expectedNullCount); + QCOMPARE(countRowsWithNull(q, ts.tableName()), expectedNullCount); // Verify that inserting using a null QVariant produces a null entry in the database: - QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 values(:id, :data)").arg(tableName))); + QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 values(:id, :data)").arg(ts.tableName()))); q.bindValue(":id", ++expectedNullCount); q.bindValue(":data", QVariant()); QVERIFY_SQL(q, exec()); - QCOMPARE(countRowsWithNull(), expectedNullCount); + QCOMPARE(countRowsWithNull(q, ts.tableName()), expectedNullCount); // Verify that writing a null-value (but not a null-variant) produces a // null entry in the database: @@ -1848,57 +1805,36 @@ void tst_QSqlQuery::writeNull() const QVariant nullValueVariant(nullableMetaType, defaultData); QVERIFY(!nullValueVariant.isNull()); - QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 values(:id, :data)").arg(tableName))); + QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 values(:id, :data)").arg(ts.tableName()))); q.bindValue(":id", ++expectedNullCount); q.bindValue(":data", nullValueVariant); QVERIFY_SQL(q, exec()); - QCOMPARE(countRowsWithNull(), expectedNullCount); + QCOMPARE(countRowsWithNull(q, ts.tableName()), expectedNullCount); // All tests passed for this type if we got here, so don't print diagnostics: errorHandler.dismiss(); } } -// TDS-specific BIT field test: -void tst_QSqlQuery::tds_bitField() -{ - QFETCH(QString, dbName); - QSqlDatabase db = QSqlDatabase::database(dbName); - CHECK_DATABASE(db); - const QString tableName = qTableName("qtest_bittest", __FILE__, db); - QSqlQuery q(db); - - QVERIFY_SQL(q, exec(QLatin1String("create table %1 (bitty bit)").arg(tableName))); - QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (0)").arg(tableName))); - QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (1)").arg(tableName))); - QVERIFY_SQL(q, exec("select bitty from " + tableName)); - - QVERIFY(q.next()); - QCOMPARE(q.value(0).toInt(), 0); - - QVERIFY(q.next()); - QCOMPARE(q.value(0).toInt(), 1); -} - // Oracle-specific NULL BLOB test: void tst_QSqlQuery::oci_nullBlob() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString qtest_nullblob(qTableName("qtest_nullblob", __FILE__, db)); + TableScope ts(db, "qtest_nullblob", __FILE__); QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id int primary key, bb blob)") - .arg(qtest_nullblob))); + .arg(ts.tableName()))); QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (0, EMPTY_BLOB())") - .arg(qtest_nullblob))); - QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (1, NULL)").arg(qtest_nullblob))); + .arg(ts.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (1, NULL)").arg(ts.tableName()))); QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (2, 'aabbcc00112233445566')") - .arg(qtest_nullblob))); + .arg(ts.tableName()))); // Necessary otherwise Oracle will bombard you with internal errors: q.setForwardOnly(true); - QVERIFY_SQL(q, exec(QLatin1String("select * from %1 order by id").arg(qtest_nullblob))); + QVERIFY_SQL(q, exec(QLatin1String("select * from %1 order by id").arg(ts.tableName()))); QVERIFY(q.next()); QVERIFY(q.value(1).toByteArray().isEmpty()); @@ -1919,15 +1855,15 @@ void tst_QSqlQuery::oci_rawField() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString qtest_rawtest(qTableName("qtest_rawtest", __FILE__, db)); + TableScope ts(db, "qtest_rawtest", __FILE__); QSqlQuery q(db); q.setForwardOnly(true); - QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id int, col raw(20))").arg(qtest_rawtest))); - QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (0, NULL)").arg(qtest_rawtest))); + QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id int, col raw(20))").arg(ts.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (0, NULL)").arg(ts.tableName()))); QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (1, '00aa1100ddeeff')") - .arg(qtest_rawtest))); - QVERIFY_SQL(q, exec(QLatin1String("select col from %1 order by id").arg(qtest_rawtest))); + .arg(ts.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("select col from %1 order by id").arg(ts.tableName()))); QVERIFY(q.next()); QVERIFY(q.isNull(0)); QVERIFY(q.value(0).toByteArray().isEmpty()); @@ -1953,22 +1889,21 @@ void tst_QSqlQuery::precision() }); db.driver()->setNumericalPrecisionPolicy(QSql::HighPrecision); - const QString qtest_precision(qTableName("qtest_precision", __FILE__, db)); + TableScope ts(db, "qtest_precision", __FILE__); static const QLatin1String precStr("1.2345678901234567891"); { // need a new scope for SQLITE QSqlQuery q(db); - q.exec("drop table " + qtest_precision); QVERIFY_SQL(q, exec(QLatin1String(tst_Databases::isMSAccess(db) ? "CREATE TABLE %1 (col1 number)" : "CREATE TABLE %1 (col1 numeric(21, 20))") - .arg(qtest_precision))); + .arg(ts.tableName()))); QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO %1 (col1) VALUES (%2)") - .arg(qtest_precision, precStr))); - QVERIFY_SQL(q, exec("SELECT * FROM " + qtest_precision)); + .arg(ts.tableName(), precStr))); + QVERIFY_SQL(q, exec("SELECT * FROM " + ts.tableName())); QVERIFY(q.next()); const QString val = q.value(0).toString(); if (!val.startsWith(precStr)) { @@ -2008,113 +1943,35 @@ void tst_QSqlQuery::nullResult() QVERIFY(!q.seek(0)); } -// This test is just an experiment to see whether we can do query-based transactions. -// The real transaction test is in tst_QSqlDatabase. -void tst_QSqlQuery::transaction() -{ - // Query-based transaction is not really possible with Qt: - QSKIP("only tested manually by trained staff"); - - QFETCH(QString, dbName); - QSqlDatabase db = QSqlDatabase::database(dbName); - CHECK_DATABASE(db); - const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); - if (!db.driver()->hasFeature(QSqlDriver::Transactions)) - QSKIP("DBMS not transaction-capable"); - - // This is the standard SQL: - QString startTransactionStr("start transaction"); - - if (dbType == QSqlDriver::MySqlServer) - startTransactionStr = "begin work"; - - QSqlQuery q(db); - QSqlQuery q2(db); - - // Test a working transaction: - q.exec(startTransactionStr); - QVERIFY_SQL(q, exec(QLatin1String("insert into%1 values (40, 'VarChar40', 'Char40')") - .arg(qtest))); - QVERIFY_SQL(q, exec(QLatin1String("select * from%1 where id = 40").arg(qtest))); - - QVERIFY(q.next()); - QCOMPARE(q.value(0).toInt(), 40); - - QVERIFY_SQL(q, exec("commit")); - QVERIFY_SQL(q, exec(QLatin1String("select * from%1 where id = 40").arg(qtest))); - - QVERIFY(q.next()); - QCOMPARE(q.value(0).toInt(), 40); - - // Test a rollback: - q.exec(startTransactionStr); - QVERIFY_SQL(q, exec(QLatin1String("insert into%1 values (41, 'VarChar41', 'Char41')") - .arg(qtest))); - QVERIFY_SQL(q, exec(QLatin1String("select * from%1 where id = 41").arg(qtest))); - - QVERIFY(q.next()); - QCOMPARE(q.value(0).toInt(), 41); - - if (!q.exec("rollback")) { - if (dbType == QSqlDriver::MySqlServer) { - qDebug("MySQL: %s", qPrintable(tst_Databases::printError(q.lastError()))); - QSKIP("MySQL transaction failed "); // non-fatal - } else { - QFAIL("Could not rollback transaction: " + tst_Databases::printError(q.lastError())); - } - } - QVERIFY_SQL(q, exec(QLatin1String("select * from%1 where id = 41").arg(qtest))); - QVERIFY(!q.next()); - - // Test concurrent access: - q.exec(startTransactionStr); - QVERIFY_SQL(q, exec(QLatin1String("insert into%1 values (42, 'VarChar42', 'Char42')") - .arg(qtest))); - QVERIFY_SQL(q, exec(QLatin1String("select * from%1 where id = 42").arg(qtest))); - QVERIFY(q.next()); - QCOMPARE(q.value(0).toInt(), 42); - - QVERIFY_SQL(q2, exec(QLatin1String("select * from%1 where id = 42").arg(qtest))); - if (q2.next()) - qDebug("DBMS '%s' doesn't support query based transactions with concurrent access", - qPrintable(tst_Databases::dbToString(db))); - - QVERIFY_SQL(q, exec("commit")); - QVERIFY_SQL(q2, exec(QLatin1String("select * from%1 where id = 42").arg(qtest))); - - QVERIFY(q2.next()); - QCOMPARE(q2.value(0).toInt(), 42); -} - void tst_QSqlQuery::joins() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); - const QString qtestj1(qTableName("qtestj1", __FILE__, db)); - const QString qtestj2(qTableName("qtestj2", __FILE__, db)); if (dbType == QSqlDriver::Oracle || dbType == QSqlDriver::Sybase || dbType == QSqlDriver::Interbase || db.driverName().startsWith("QODBC")) { // Oracle broken beyond recognition - cannot outer join on more than one table: QSKIP("DBMS cannot understand standard SQL"); } + TableScope j1(db, "qtestj1", __FILE__); + TableScope j2(db, "qtestj2", __FILE__); QSqlQuery q(db); - QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id1 int, id2 int)").arg(qtestj1))); - QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id int, name varchar(20))").arg(qtestj2))); - QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (1, 1)").arg(qtestj1))); - QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (1, 2)").arg(qtestj1))); - QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values(1, 'trenton')").arg(qtestj2))); - QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values(2, 'marius')").arg(qtestj2))); + QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id1 int, id2 int)").arg(j1.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id int, name varchar(20))").arg(j2.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (1, 1)").arg(j1.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (1, 2)").arg(j1.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values(1, 'trenton')").arg(j2.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values(2, 'marius')").arg(j2.tableName()))); QVERIFY_SQL(q, exec(QLatin1String( "select qtestj1.id1, qtestj1.id2, qtestj2.id, qtestj2.name, " "qtestj3.id, qtestj3.name from %1 qtestj1 left outer join %2 qtestj2 " "on (qtestj1.id1 = qtestj2.id) left outer join %2 as qtestj3 " - "on (qtestj1.id2 = qtestj3.id)").arg(qtestj1, qtestj2))); + "on (qtestj1.id2 = qtestj3.id)").arg(j1.tableName(), j2.tableName()))); QVERIFY(q.next()); QCOMPARE(q.value(0).toInt(), 1); @@ -2162,7 +2019,8 @@ void tst_QSqlQuery::prepare_bind_exec() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); - const QString qtest_prepare(qTableName("qtest_prepare", __FILE__, db)); + TableScope ts(db, "qtest_prepare", __FILE__); + const auto &qtest_prepare = ts.tableName(); if (dbType == QSqlDriver::DB2) QSKIP("Needs someone with more Unicode knowledge than I have to fix"); @@ -2185,6 +2043,7 @@ void tst_QSqlQuery::prepare_bind_exec() switch (dbType) { case QSqlDriver::MSSqlServer: case QSqlDriver::Sybase: + case QSqlDriver::MimerSQL: createQuery = QLatin1String("create table %1 (id int primary key, " "name nvarchar(200) null, name2 nvarchar(200) null)"); break; @@ -2202,20 +2061,23 @@ void tst_QSqlQuery::prepare_bind_exec() break; } - q.exec("drop table " + qtest_prepare); QVERIFY_SQL(q, exec(createQuery.arg(qtest_prepare))); QVERIFY(q.prepare(QLatin1String("insert into %1 (id, name) values (:id, :name)") .arg(qtest_prepare))); - int i; - - for (i = 0; i < 6; ++i) { + for (int i = 0; i < 6; ++i) { q.bindValue(":name", values[i]); q.bindValue(":id", i); QVERIFY_SQL(q, exec()); const QVariantList m = q.boundValues(); - QCOMPARE(m.count(), qsizetype(2)); + QCOMPARE(m.size(), qsizetype(2)); QCOMPARE(m.at(0).toInt(), i); QCOMPARE(m.at(1).toString(), values[i]); + const QStringList n = q.boundValueNames(); + QCOMPARE(n.size(), 2); + QCOMPARE(n.at(0), ":id"); + QCOMPARE(n.at(1), ":name"); + QCOMPARE(q.boundValueName(0), ":id"); + QCOMPARE(q.boundValueName(1), ":name"); } q.bindValue(":id", 8); @@ -2228,7 +2090,7 @@ void tst_QSqlQuery::prepare_bind_exec() } QVERIFY_SQL(q, exec(QLatin1String("SELECT * FROM %1 order by id").arg(qtest_prepare))); - for (i = 0; i < 6; ++i) { + for (int i = 0; i < 6; ++i) { QVERIFY(q.next()); QCOMPARE(q.value(0).toInt(), i); QCOMPARE(q.value(1).toString().trimmed(), values[i]); @@ -2253,7 +2115,7 @@ void tst_QSqlQuery::prepare_bind_exec() QVERIFY(q.exec(QLatin1String("select * from %1 where id > 98 order by id") .arg(qtest_prepare))); - for (i = 99; i <= 100; ++i) { + for (int i = 99; i <= 100; ++i) { QVERIFY(q.next()); QCOMPARE(q.value(0).toInt(), i); QCOMPARE(q.value(1).toString().trimmed(), u"Bart"); @@ -2262,7 +2124,7 @@ void tst_QSqlQuery::prepare_bind_exec() /*** SELECT stuff ***/ QVERIFY(q.prepare(QLatin1String("select * from %1 where id = :id").arg(qtest_prepare))); - for (i = 0; i < 6; ++i) { + for (int i = 0; i < 6; ++i) { q.bindValue(":id", i); QVERIFY_SQL(q, exec()); QVERIFY_SQL(q, next()); @@ -2375,7 +2237,7 @@ void tst_QSqlQuery::prepare_bind_exec() QFAIL_SQL(q, exec()); QVERIFY_SQL(q, exec(QLatin1String("SELECT * FROM %1 order by id").arg(qtest_prepare))); - for (i = 0; i < 6; ++i) { + for (int i = 0; i < 6; ++i) { QVERIFY(q.next()); QCOMPARE(q.value(0).toInt(), i); QCOMPARE(q.value(1).toString().trimmed(), values[i]); @@ -2401,7 +2263,7 @@ void tst_QSqlQuery::prepare_bind_exec() QVERIFY(q.exec(QLatin1String("select * from %1 where id > 98 order by id") .arg(qtest_prepare))); - for (i = 99; i <= 100; ++i) { + for (int i = 99; i <= 100; ++i) { QVERIFY(q.next()); QCOMPARE(q.value(0).toInt(), i); QCOMPARE(q.value(1).toString().trimmed(), u"Bart"); @@ -2420,7 +2282,7 @@ void tst_QSqlQuery::prepare_bind_exec() QVERIFY(q.prepare(QLatin1String( "insert into %1 (id, name, name2) values (:id, :name, :name)") .arg(qtest_prepare))); - for (i = 101; i < 103; ++i) { + for (int i = 101; i < 103; ++i) { q.bindValue(":id", i); q.bindValue(":name", "name"); QVERIFY(q.exec()); @@ -2438,7 +2300,7 @@ void tst_QSqlQuery::prepare_bind_exec() // works correctly - QTBUG-65150 QVERIFY(q.prepare(QLatin1String("insert into %1 (id, name, name2) values (:id, :id, :name)") .arg(qtest_prepare))); - for (i = 104; i < 106; ++i) { + for (int i = 104; i < 106; ++i) { q.bindValue(":id", i); q.bindValue(":name", "name"); QVERIFY(q.exec()); @@ -2453,7 +2315,7 @@ void tst_QSqlQuery::prepare_bind_exec() // Test that duplicated named placeholders in any order QVERIFY(q.prepare(QLatin1String("insert into %1 (id, name, name2) values (:id, :name, :id)") .arg(qtest_prepare))); - for (i = 107; i < 109; ++i) { + for (int i = 107; i < 109; ++i) { q.bindValue(":id", i); q.bindValue(":name", "name"); QVERIFY(q.exec()); @@ -2522,12 +2384,12 @@ void tst_QSqlQuery::sqlServerLongStrings() if (tst_Databases::getDatabaseType(db) != QSqlDriver::MSSqlServer) QSKIP("Test is specific to SQL Server"); + TableScope ts(db, "qtest_longstr", __FILE__); QSqlQuery q(db); - const QString tableName = qTableName("qtest_longstr", __FILE__, db); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (id int primary key, longstring ntext)") - .arg(tableName))); - QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 VALUES (?, ?)").arg(tableName))); + .arg(ts.tableName()))); + QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 VALUES (?, ?)").arg(ts.tableName()))); q.addBindValue(0); q.addBindValue(u"bubu"_s); @@ -2538,7 +2400,7 @@ void tst_QSqlQuery::sqlServerLongStrings() q.addBindValue(1); q.addBindValue(testStr); QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec("select * from " + tableName)); + QVERIFY_SQL(q, exec("select * from " + ts.tableName())); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toInt(), 0); @@ -2583,9 +2445,8 @@ void tst_QSqlQuery::batchExec() CHECK_DATABASE(db); QSqlQuery q(db); - const QString tableName = qTableName("qtest_batch", __FILE__, db); - tst_Databases::safeDropTable(db, tableName); - + TableScope ts(db, "qtest_batch", __FILE__); + const auto &tableName = ts.tableName(); const auto dbType = tst_Databases::getDatabaseType(db); QLatin1String timeStampString(dbType == QSqlDriver::Interbase ? "TIMESTAMP" : "TIMESTAMP (3)"); @@ -2625,7 +2486,7 @@ void tst_QSqlQuery::batchExec() QCOMPARE(q.value(0).toInt(), intCol.at(i)); QCOMPARE(q.value(1).toString(), charCol.at(i)); QCOMPARE(q.value(2).toDate(), dateCol.at(i)); - QCOMPARE(q.value(3).toDouble(), numCol.at(i)); + QVERIFY(qFuzzyCompare(q.value(3).toDouble(), numCol.at(i).toDouble())); if (tst_Databases::getDatabaseType(db) == QSqlDriver::MySqlServer && timeStampCol.at(i).isNull()) { QEXPECT_FAIL("", "This appears to be a bug in MySQL as it converts null datetimes to " @@ -2657,7 +2518,7 @@ void tst_QSqlQuery::batchExec() QCOMPARE(q.value(0).toInt(), intCol.at(i)); QCOMPARE(q.value(1).toString(), charCol.at(i)); QCOMPARE(q.value(2).toDate(), dateCol.at(i)); - QCOMPARE(q.value(3).toDouble(), numCol.at(i)); + QVERIFY(qFuzzyCompare(q.value(3).toDouble(), numCol.at(i).toDouble())); if (tst_Databases::getDatabaseType(db) == QSqlDriver::MySqlServer && timeStampCol.at(i).isNull()) { QEXPECT_FAIL("", "This appears to be a bug in MySQL as it converts null datetimes to " @@ -2669,8 +2530,9 @@ void tst_QSqlQuery::batchExec() } // Only test the prepared stored procedure approach where the driver has support - // for batch operations as this will not work without it - if (db.driver()->hasFeature(QSqlDriver::BatchOperations)) { + // for batch operations as this will not work without it. + // Currently Mimer SQL cannot use output parameters with procedures in batch operations. + if (dbType != QSqlDriver::MimerSQL && db.driver()->hasFeature(QSqlDriver::BatchOperations)) { const QString procName = qTableName("qtest_batch_proc", __FILE__, db); QVERIFY_SQL(q, exec(QLatin1String( "create or replace procedure %1 (x in timestamp, y out timestamp) " @@ -2695,19 +2557,19 @@ void tst_QSqlQuery::QTBUG_43874() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "bug43874", __FILE__); QSqlQuery q(db); - const QString tableName = qTableName("bug43874", __FILE__, db); - QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (id INT)").arg(tableName))); - QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 (id) VALUES (?)").arg(tableName))); + QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (id INT)").arg(ts.tableName()))); + QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 (id) VALUES (?)").arg(ts.tableName()))); for (int i = 0; i < 2; ++i) { const QVariantList ids = { i }; q.addBindValue(ids); QVERIFY_SQL(q, execBatch()); } - QVERIFY_SQL(q, exec(QLatin1String("SELECT id FROM %1 ORDER BY id").arg(tableName))); + QVERIFY_SQL(q, exec(QLatin1String("SELECT id FROM %1 ORDER BY id").arg(ts.tableName()))); QVERIFY(q.next()); QCOMPARE(q.value(0).toInt(), 0); @@ -2802,14 +2664,14 @@ void tst_QSqlQuery::record_sqlite() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "record_sqlite"); QSqlQuery q(db); - const QString tableName = qTableName("record_sqlite", __FILE__, db); QVERIFY_SQL(q, exec(QLatin1String( "create table %1(id integer primary key, name varchar, title int)") - .arg(tableName))); + .arg(ts.tableName()))); - QSqlRecord rec = db.record(tableName); + QSqlRecord rec = db.record(ts.tableName()); QCOMPARE(rec.count(), 3); QCOMPARE(rec.field(0).metaType().id(), QMetaType::Int); @@ -2817,7 +2679,7 @@ void tst_QSqlQuery::record_sqlite() QCOMPARE(rec.field(2).metaType().id(), QMetaType::Int); // Important - select from an empty table: - QVERIFY_SQL(q, exec("select id, name, title from " + tableName)); + QVERIFY_SQL(q, exec("select id, name, title from " + ts.tableName())); rec = q.record(); QCOMPARE(rec.count(), 3); @@ -2832,20 +2694,19 @@ void tst_QSqlQuery::oraLong() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - QSqlQuery q(db); - QString aLotOfText(127000, QLatin1Char('H')); - const QString tableName = qTableName("qtest_longstr", __FILE__, db); + TableScope ts(db, "qtest_longstr", __FILE__); + QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id int primary key, astr long)") - .arg(tableName))); + .arg(ts.tableName()))); QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 (id, astr) values (?, ?)") - .arg(tableName))); + .arg(ts.tableName()))); q.addBindValue(1); q.addBindValue(aLotOfText); QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec("select id,astr from " + tableName)); + QVERIFY_SQL(q, exec("select id,astr from " + ts.tableName())); QVERIFY(q.next()); QCOMPARE(q.value(0).toInt(), 1); @@ -2858,12 +2719,11 @@ void tst_QSqlQuery::execErrorRecovery() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - QSqlQuery q(db); + TableScope ts(db, "qtest_exerr", __FILE__); - const QString tbl = qTableName("qtest_exerr", __FILE__, db); - q.exec("drop table " + tbl); - QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id int not null primary key)").arg(tbl))); - QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 values (?)").arg(tbl))); + QSqlQuery q(db); + QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id int not null primary key)").arg(ts.tableName()))); + QVERIFY_SQL(q, prepare(QLatin1String("insert into %1 values (?)").arg(ts.tableName()))); q.addBindValue(1); QVERIFY_SQL(q, exec()); @@ -2920,13 +2780,12 @@ void tst_QSqlQuery::lastInsertId() // PostgreSQL >= 8.1 relies on lastval() which does not work if a value is // manually inserted to the serial field, so we create a table specifically if (tst_Databases::getDatabaseType(db) == QSqlDriver::PostgreSQL) { - const auto tst_lastInsertId = qTableName("tst_lastInsertId", __FILE__, db); - tst_Databases::safeDropTable(db, tst_lastInsertId); + TableScope ts(db, "tst_lastInsertId", __FILE__); QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id serial not null, t_varchar " "varchar(20), t_char char(20), primary key(id))") - .arg(tst_lastInsertId))); + .arg(ts.tableName()))); QVERIFY_SQL(q, exec(QLatin1String("insert into %1 (t_varchar, t_char) values " - "('VarChar41', 'Char41')").arg(tst_lastInsertId))); + "('VarChar41', 'Char41')").arg(ts.tableName()))); } else { QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (41, 'VarChar41', 'Char41')") .arg(qtest))); @@ -2972,18 +2831,18 @@ void tst_QSqlQuery::psql_bindWithDoubleColonCastOperator() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString tablename(qTableName("bindtest", __FILE__, db)); + TableScope ts(db, "bindtest", __FILE__); QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String( "create table %1 (id1 int, id2 int, id3 int, fld1 int, fld2 int)") - .arg(tablename))); - QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (1, 2, 3, 10, 5)").arg(tablename))); + .arg(ts.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("insert into %1 values (1, 2, 3, 10, 5)").arg(ts.tableName()))); // Insert tableName last to let the other %-tokens' numbering match what they're replaced with: const auto queryTemplate = QLatin1String("select sum((fld1 - fld2)::int) from %4 where " "id1 = %1 and id2 =%2 and id3=%3"); - const QString query = queryTemplate.arg(":myid1", ":myid2", ":myid3", tablename); + const QString query = queryTemplate.arg(":myid1", ":myid2", ":myid3", ts.tableName()); QVERIFY_SQL(q, prepare(query)); q.bindValue(":myid1", 1); q.bindValue(":myid2", 2); @@ -2994,7 +2853,7 @@ void tst_QSqlQuery::psql_bindWithDoubleColonCastOperator() // The positional placeholders are converted to named placeholders in executedQuery() const QString expected = db.driver()->hasFeature(QSqlDriver::PreparedQueries) - ? query : queryTemplate.arg("1", "2", "3", tablename); + ? query : queryTemplate.arg("1", "2", "3", ts.tableName()); QCOMPARE(q.executedQuery(), expected); } @@ -3010,9 +2869,9 @@ void tst_QSqlQuery::psql_specialFloatValues() CHECK_DATABASE(db); QSqlQuery query(db); - const QString tableName = qTableName("floattest", __FILE__, db); - QVERIFY_SQL(query, exec(QLatin1String("create table %1 (value float)").arg(tableName))); - QVERIFY_SQL(query, prepare(QLatin1String("insert into %1 values(:value)").arg(tableName))); + TableScope ts(db, "floattest", __FILE__); + QVERIFY_SQL(query, exec(QLatin1String("create table %1 (value float)").arg(ts.tableName()))); + QVERIFY_SQL(query, prepare(QLatin1String("insert into %1 values(:value)").arg(ts.tableName()))); const QVariant data[] = { QVariant(double(42.42)), @@ -3027,8 +2886,6 @@ void tst_QSqlQuery::psql_specialFloatValues() query.bindValue(":value", v); QVERIFY_SQL(query, exec()); } - - QVERIFY_SQL(query, exec("drop table " + tableName)); } /* For task 157397: Using QSqlQuery with an invalid QSqlDatabase @@ -3177,11 +3034,10 @@ void tst_QSqlQuery::sqlite_finish() db2.setDatabaseName(db.databaseName()); QVERIFY_SQL(db2, open()); - const QString tableName(qTableName("qtest_lockedtable", __FILE__, db)); - const auto wrapup = qScopeGuard([&]() { tst_Databases::safeDropTable(db, tableName); }); + TableScope ts(db, "qtest_lockedtable", __FILE__); + const auto &tableName = ts.tableName(); QSqlQuery q(db); - tst_Databases::safeDropTable(db, tableName); q.exec(QLatin1String("CREATE TABLE %1 (pk_id INTEGER PRIMARY KEY, whatever TEXT)") .arg(tableName)); q.exec(QLatin1String("INSERT INTO %1 values(1, 'whatever')").arg(tableName)); @@ -3214,7 +3070,8 @@ void tst_QSqlQuery::nextResult() QSKIP("DBMS does not support multiple result sets"); QSqlQuery q(db); - const QString tableName(qTableName("more_results", __FILE__, db)); + TableScope ts(db, "more_results", __FILE__); + const auto &tableName = ts.tableName(); QVERIFY_SQL(q, exec(QLatin1String( "CREATE TABLE %1 (id integer, text varchar(20), " @@ -3441,7 +3298,7 @@ void tst_QSqlQuery::blobsPreparedQuery() QSKIP("DBMS does not support BLOBs or prepared queries"); } - const QString tableName(qTableName("blobstest", __FILE__, db)); + TableScope ts(db, "blobstest", __FILE__); QSqlQuery q(db); q.setForwardOnly(true); // This is needed to make the test work with DB2. @@ -3455,8 +3312,8 @@ void tst_QSqlQuery::blobsPreparedQuery() : dbType == QSqlDriver::MSSqlServer ? "IMAGE" : "BLOB"); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1(id INTEGER, data %2)") - .arg(tableName, typeName))); - q.prepare(QLatin1String("INSERT INTO %1(id, data) VALUES(:id, :data)").arg(tableName)); + .arg(ts.tableName(), typeName))); + q.prepare(QLatin1String("INSERT INTO %1(id, data) VALUES(:id, :data)").arg(ts.tableName())); q.bindValue(":id", 1); q.bindValue(":data", shortBLOB); QVERIFY_SQL(q, exec()); @@ -3466,7 +3323,7 @@ void tst_QSqlQuery::blobsPreparedQuery() QVERIFY_SQL(q, exec()); // Two executions and result sets - q.prepare(QLatin1String("SELECT data FROM %1 WHERE id = ?").arg(tableName)); + q.prepare(QLatin1String("SELECT data FROM %1 WHERE id = ?").arg(ts.tableName())); q.bindValue(0, QVariant(1)); QVERIFY_SQL(q, exec()); QVERIFY_SQL(q, next()); @@ -3478,7 +3335,7 @@ void tst_QSqlQuery::blobsPreparedQuery() QCOMPARE(q.value(0).toString().toUtf8(), longerBLOB.toUtf8()); // Only one execution and result set - q.prepare(QLatin1String("SELECT id, data FROM %1 ORDER BY id").arg(tableName)); + q.prepare(QLatin1String("SELECT id, data FROM %1 ORDER BY id").arg(ts.tableName())); QVERIFY_SQL(q, exec()); QVERIFY_SQL(q, next()); QCOMPARE(q.value(1).toString(), shortBLOB); @@ -3494,11 +3351,11 @@ void tst_QSqlQuery::emptyTableNavigate() CHECK_DATABASE(db); { + TableScope ts(db, "qtest_empty", __FILE__); QSqlQuery q(db); - const QString tbl = qTableName("qtest_empty", __FILE__, db); - q.exec("drop table " + tbl); - QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id char(10))").arg(tbl))); - QVERIFY_SQL(q, prepare("select * from " + tbl)); + q.exec("drop table " + ts.tableName()); + QVERIFY_SQL(q, exec(QLatin1String("create table %1 (id char(10))").arg(ts.tableName()))); + QVERIFY_SQL(q, prepare("select * from " + ts.tableName())); QVERIFY_SQL(q, exec()); QVERIFY(!q.next()); QVERIFY(!q.lastError().isValid()); @@ -3510,8 +3367,7 @@ void tst_QSqlQuery::timeStampParsing() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString tableName(qTableName("timeStampParsing", __FILE__, db)); - tst_Databases::safeDropTable(db, tableName); + TableScope ts(db, "timeStampParsing", __FILE__); QSqlQuery q(db); QLatin1String creator; switch (tst_Databases::getDatabaseType(db)) { @@ -3527,16 +3383,30 @@ void tst_QSqlQuery::timeStampParsing() // Since there is no auto-increment feature in Interbase we allow it to be null creator = QLatin1String("CREATE TABLE %1(id integer, datefield timestamp);"); break; + case QSqlDriver::MimerSQL: + creator = QLatin1String("CREATE UNIQUE SEQUENCE timeStampParsing_seq"); + QVERIFY_SQL(q, exec(creator)); + creator = QLatin1String("CREATE TABLE %1(id integer NOT NULL default next value " + "for timeStampParsing_seq, " + "datefield timestamp, primary key(id));"); + break; default: creator = QLatin1String("CREATE TABLE %1(" "\"id\" integer NOT NULL PRIMARY KEY AUTOINCREMENT, " "\"datefield\" timestamp);"); break; } - QVERIFY_SQL(q, exec(creator.arg(tableName))); - QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO %1 (datefield) VALUES (current_timestamp);") - .arg(tableName))); - QVERIFY_SQL(q, exec(QLatin1String("SELECT * FROM ") + tableName)); + QVERIFY_SQL(q, exec(creator.arg(ts.tableName()))); + QLatin1String currentTimestamp; + if (tst_Databases::getDatabaseType(db) == QSqlDriver::MimerSQL) + currentTimestamp = QLatin1String("localtimestamp"); + else + currentTimestamp = QLatin1String("current_timestamp"); + QVERIFY_SQL(q, + exec(QLatin1String("INSERT INTO %1 (datefield) VALUES (%2);") + .arg(ts.tableName()) + .arg(currentTimestamp))); + QVERIFY_SQL(q, exec(QLatin1String("SELECT * FROM ") + ts.tableName())); while (q.next()) QVERIFY(q.value(1).toDateTime().isValid()); } @@ -3546,10 +3416,10 @@ void tst_QSqlQuery::task_217003() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - QSqlQuery q(db); - const QString planets = qTableName("Planet", __FILE__, db); + TableScope ts(db, "Planet", __FILE__); + const auto &planets = ts.tableName(); - q.exec("drop table " + planets); + QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("create table %1 (Name varchar(20))").arg(planets))); QVERIFY_SQL(q, exec(QLatin1String("insert into %1 VALUES ('Mercury')").arg(planets))); QVERIFY_SQL(q, exec(QLatin1String("insert into %1 VALUES ('Venus')").arg(planets))); @@ -3575,11 +3445,10 @@ void tst_QSqlQuery::task_250026() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - QSqlQuery q(db); - - const QString tableName(qTableName("task_250026", __FILE__, db)); + TableScope ts(db, "task_250026", __FILE__); - if (!q.exec(QLatin1String("create table %1 (longfield varchar(1100))").arg(tableName))) { + QSqlQuery q(db); + if (!q.exec(QLatin1String("create table %1 (longfield varchar(1100))").arg(ts.tableName()))) { qDebug() << "Error" << q.lastError(); QSKIP("Db doesn't support \"1100\" as a size for fields"); } @@ -3587,12 +3456,12 @@ void tst_QSqlQuery::task_250026() const QString data258(258, QLatin1Char('A')); const QString data1026(1026, QLatin1Char('A')); QVERIFY_SQL(q, prepare(QLatin1String("insert into %1(longfield) VALUES (:longfield)") - .arg(tableName))); + .arg(ts.tableName()))); q.bindValue(":longfield", data258); QVERIFY_SQL(q, exec()); q.bindValue(":longfield", data1026); QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec("select * from " + tableName)); + QVERIFY_SQL(q, exec("select * from " + ts.tableName())); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toString().size(), data258.size()); QVERIFY_SQL(q, next()); @@ -3601,7 +3470,7 @@ void tst_QSqlQuery::task_250026() void tst_QSqlQuery::crashQueryOnCloseDatabase() { - for (const auto &dbName : qAsConst(dbs.dbNames)) { + for (const auto &dbName : std::as_const(dbs.dbNames)) { const auto tidier = qScopeGuard([]() { QSqlDatabase::removeDatabase("crashTest"); }); // Note: destruction of clonedDb needs to happen before we call removeDatabase. QSqlDatabase clonedDb = QSqlDatabase::cloneDatabase( @@ -3613,19 +3482,19 @@ void tst_QSqlQuery::crashQueryOnCloseDatabase() } } -void tst_QSqlQuery::task_233829() +void tst_QSqlQuery::testNaN() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "testNaN", __FILE__); QSqlQuery q(db); - const QString tableName(qTableName("task_233829", __FILE__, db)); QVERIFY_SQL(q, exec(QLatin1String( "CREATE TABLE %1(dbl1 double precision,dbl2 double precision) " - "without oids;").arg(tableName))); + "without oids;").arg(ts.tableName()))); const QString queryString = - QLatin1String("INSERT INTO %1(dbl1, dbl2) VALUES(?,?)").arg(tableName); + QLatin1String("INSERT INTO %1(dbl1, dbl2) VALUES(?,?)").arg(ts.tableName()); const double nan = qQNaN(); QVERIFY_SQL(q, prepare(queryString)); @@ -3634,13 +3503,11 @@ void tst_QSqlQuery::task_233829() QVERIFY_SQL(q, exec()); } -void tst_QSqlQuery::QTBUG_12477() +void tst_QSqlQuery::psqlNumericMetadata() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - if (!db.driverName().startsWith("QPSQL")) - QSKIP("PostgreSQL-specific test"); QSqlQuery q(db); QVERIFY_SQL(q, exec("SELECT 1::bit, '10101010000111101101'::varbit, " @@ -3683,11 +3550,10 @@ void tst_QSqlQuery::sqlServerReturn0() if (tst_Databases::getDatabaseType(db) != QSqlDriver::MSSqlServer) QSKIP("Test is specific to SQL Server"); - const QString tableName(qTableName("test141895", __FILE__, db)); - const QString procName(qTableName("test141895_proc", __FILE__, db)); + ProcScope ps(db, "test141895_proc", __FILE__); + TableScope ts(db, "test141895", __FILE__); + const auto &tableName = ts.tableName(); QSqlQuery q(db); - q.exec("DROP TABLE " + tableName); - q.exec("DROP PROCEDURE " + procName); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (id integer)").arg(tableName))); QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO %1 (id) VALUES (1)").arg(tableName))); QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO %1 (id) VALUES (2)").arg(tableName))); @@ -3695,10 +3561,10 @@ void tst_QSqlQuery::sqlServerReturn0() QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO %1 (id) VALUES (3)").arg(tableName))); QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO %1 (id) VALUES (1)").arg(tableName))); QVERIFY_SQL(q, exec(QLatin1String("CREATE PROCEDURE %1 AS " - "SELECT * FROM %1 WHERE ID = 2 " - "RETURN 0").arg(tableName))); + "SELECT * FROM %2 WHERE ID = 2 " + "RETURN 0").arg(ps.name(), ts.tableName()))); - QVERIFY_SQL(q, exec(QLatin1String("{CALL %1}").arg(procName))); + QVERIFY_SQL(q, exec(QLatin1String("{CALL %1}").arg(ps.name()))); QVERIFY_SQL(q, next()); } @@ -3796,24 +3662,21 @@ void tst_QSqlQuery::QTBUG_18435() if (dbType != QSqlDriver::MSSqlServer || !db.driverName().startsWith("QODBC")) QSKIP("Test is specific to SQL Server"); + ProcScope ps(db, "qtbug_18435_proc", __FILE__); QSqlQuery q(db); - QString procName(qTableName("qtbug_18435_proc", __FILE__, db)); - q.exec("DROP PROCEDURE " + procName); const QString stmt = QLatin1String("CREATE PROCEDURE %1 @key nvarchar(50) OUTPUT AS\n" "BEGIN\n" " SET NOCOUNT ON\n" " SET @key = 'TEST'\n" - "END\n").arg(procName); + "END\n").arg(ps.name()); QVERIFY_SQL(q, exec(stmt)); - QVERIFY_SQL(q, prepare(QLatin1String("{CALL %1(?)}").arg(procName))); + QVERIFY_SQL(q, prepare(QLatin1String("{CALL %1(?)}").arg(ps.name()))); const QString testStr = "0123"; q.bindValue(0, testStr, QSql::Out); QVERIFY_SQL(q, exec()); QCOMPARE(q.boundValue(0).toString(), QLatin1String("TEST")); - - QVERIFY_SQL(q, exec("DROP PROCEDURE " + procName)); } void tst_QSqlQuery::QTBUG_5251() @@ -3823,15 +3686,14 @@ void tst_QSqlQuery::QTBUG_5251() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString timetest(qTableName("timetest", __FILE__, db)); - tst_Databases::safeDropTable(db, timetest); + TableScope ts(db, "timetest", __FILE__); QSqlQuery q(db); - QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (t TIME)").arg(timetest))); - QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO VALUES ('1:2:3.666')").arg(timetest))); + QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (t TIME)").arg(ts.tableName()))); + QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO %1 VALUES ('1:2:3.666')").arg(ts.tableName()))); QSqlTableModel timetestModel(0, db); timetestModel.setEditStrategy(QSqlTableModel::OnManualSubmit); - timetestModel.setTable(timetest); + timetestModel.setTable(ts.tableName()); QVERIFY_SQL(timetestModel, select()); QCOMPARE(timetestModel.record(0).field(0).value().toTime().toString("HH:mm:ss.zzz"), @@ -3843,7 +3705,7 @@ void tst_QSqlQuery::QTBUG_5251() QCOMPARE(timetestModel.record(0).field(0).value().toTime().toString("HH:mm:ss.zzz"), u"00:12:34.500"); - QVERIFY_SQL(q, exec(QLatin1String("UPDATE %1 SET t = '0:11:22.33'").arg(timetest))); + QVERIFY_SQL(q, exec(QLatin1String("UPDATE %1 SET t = '0:11:22.33'").arg(ts.tableName()))); QVERIFY_SQL(timetestModel, select()); QCOMPARE(timetestModel.record(0).field(0).value().toTime().toString("HH:mm:ss.zzz"), u"00:11:22.330"); @@ -3854,9 +3716,10 @@ void tst_QSqlQuery::QTBUG_6421() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + const QString tableName(qTableName("bug6421", __FILE__, db).toUpper()); + TableScope ts(db, tableName); QSqlQuery q(db); - const QString tableName(qTableName("bug6421", __FILE__, db).toUpper()); QVERIFY_SQL(q, exec(QLatin1String( "create table %1(COL1 char(10), COL2 char(10), COL3 char(10))") @@ -3884,9 +3747,8 @@ void tst_QSqlQuery::QTBUG_6618() if (tst_Databases::getDatabaseType(db) != QSqlDriver::MSSqlServer) QSKIP("Test is specific to SQL Server"); + ProcScope ps(db, "tst_raiseError", __FILE__); QSqlQuery q(db); - const QString procedureName = qTableName("tst_raiseError", __FILE__, db); - q.exec("drop procedure " + procedureName); // non-fatal QString errorString; for (int i = 0; i < 110; ++i) errorString += "reallylong"; @@ -3894,8 +3756,8 @@ void tst_QSqlQuery::QTBUG_6618() QVERIFY_SQL(q, exec(QLatin1String("create procedure %1 as\n" "begin\n" " raiserror('%2', 16, 1)\n" - "end\n").arg(procedureName, errorString))); - q.exec(QLatin1String("{call %1}").arg(procedureName)); + "end\n").arg(ps.name(), errorString))); + q.exec(QLatin1String("{call %1}").arg(ps.name())); QVERIFY(q.lastError().text().contains(errorString)); } @@ -3904,11 +3766,11 @@ void tst_QSqlQuery::QTBUG_6852() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - QSqlQuery q(db); - const QString tableName(qTableName("bug6852", __FILE__, db)); - const QString procName(qTableName("bug6852_proc", __FILE__, db)); + TableScope ts(db, "bug6852", __FILE__); + const auto &tableName = ts.tableName(); + ProcScope ps(db, "bug6852_proc", __FILE__); - QVERIFY_SQL(q, exec("DROP PROCEDURE IF EXISTS " + procName)); + QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1(\n" "MainKey INT NOT NULL,\n" "OtherTextCol VARCHAR(45) NOT NULL,\n" @@ -3923,9 +3785,9 @@ void tst_QSqlQuery::QTBUG_6852() " SET @st = 'SELECT MainKey, OtherTextCol from %2';\n" " PREPARE stmt from @st;\n" " EXECUTE stmt;\n" - "END;").arg(procName, tableName))); + "END;").arg(ps.name(), tableName))); - QVERIFY_SQL(q, exec(QLatin1String("CALL %1()").arg(procName))); + QVERIFY_SQL(q, exec(QLatin1String("CALL %1()").arg(ps.name()))); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toInt(), 0); QCOMPARE(q.value(1).toString(), QLatin1String("Disabled")); @@ -3936,19 +3798,19 @@ void tst_QSqlQuery::QTBUG_5765() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - QSqlQuery q(db); - const QString tableName(qTableName("bug5765", __FILE__, db)); + TableScope ts(db, "bug5765", __FILE__); + QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1(testval TINYINT(1) DEFAULT 0)") - .arg(tableName))); - q.prepare(QLatin1String("INSERT INTO %1 SET testval = :VALUE").arg(tableName)); + .arg(ts.tableName()))); + q.prepare(QLatin1String("INSERT INTO %1 SET testval = :VALUE").arg(ts.tableName())); q.bindValue(":VALUE", 1); QVERIFY_SQL(q, exec()); q.bindValue(":VALUE", 12); QVERIFY_SQL(q, exec()); q.bindValue(":VALUE", 123); QVERIFY_SQL(q, exec()); - QString sql = "select testval from " + tableName; + QString sql = "select testval from " + ts.tableName(); QVERIFY_SQL(q, exec(sql)); QVERIFY_SQL(q, next()); QCOMPARE(q.value(0).toInt(), 1); @@ -3975,9 +3837,10 @@ void tst_QSqlQuery::QTBUG_21884() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "bug21884", __FILE__); + const auto &tableName = ts.tableName(); QSqlQuery q(db); - QString tableName(qTableName("bug21884", __FILE__, db)); { const QString good[] = { @@ -4074,10 +3937,11 @@ void tst_QSqlQuery::QTBUG_23895() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "bug23895", __FILE__); + const auto &tableName = ts.tableName(); QSqlQuery q(db); - QString tableName(qTableName("bug23895", __FILE__, db)); q.prepare(QLatin1String("create table %1(id integer primary key, val1 bool, val2 boolean)") .arg(tableName)); QVERIFY_SQL(q, exec()); @@ -4128,19 +3992,16 @@ void tst_QSqlQuery::QTBUG_14904() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "bug14904", __FILE__); QSqlQuery q(db); - - QString tableName(qTableName("bug14904", __FILE__, db)); - tst_Databases::safeDropTable(db, tableName); - - q.prepare(QLatin1String("create table %1(val1 bool)").arg(tableName)); + q.prepare(QLatin1String("create table %1(val1 bool)").arg(ts.tableName())); QVERIFY_SQL(q, exec()); - q.prepare(QLatin1String("insert into %1(val1) values(?);").arg(tableName)); + q.prepare(QLatin1String("insert into %1(val1) values(?);").arg(ts.tableName())); q.addBindValue(true); QVERIFY_SQL(q, exec()); - QString sql = "select val1 AS value1 from " + tableName; + QString sql = "select val1 AS value1 from " + ts.tableName(); QVERIFY_SQL(q, exec(sql)); QVERIFY_SQL(q, next()); @@ -4148,7 +4009,7 @@ void tst_QSqlQuery::QTBUG_14904() QCOMPARE(q.record().field(0).metaType().id(), QMetaType::Bool); QVERIFY(q.value(0).toBool()); - sql = "select val1 AS 'value.one' from " + tableName; + sql = "select val1 AS 'value.one' from " + ts.tableName(); QVERIFY_SQL(q, exec(sql)); QVERIFY_SQL(q, next()); QCOMPARE(q.record().indexOf("value.one"), 0); // Was -1 before bug fix. @@ -4162,19 +4023,17 @@ void tst_QSqlQuery::QTBUG_2192() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); { - const QString tableName(qTableName("bug2192", __FILE__, db)); - tst_Databases::safeDropTable(db, tableName); - + TableScope ts(db, "bug2192", __FILE__); QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (dt %2)") - .arg(tableName, tst_Databases::dateTimeTypeName(db)))); + .arg(ts.tableName(), tst_Databases::dateTimeTypeName(db)))); QDateTime dt = QDateTime(QDate(2012, 7, 4), QTime(23, 59, 59, 999)); - QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 (dt) VALUES (?)").arg(tableName))); + QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 (dt) VALUES (?)").arg(ts.tableName()))); q.bindValue(0, dt); QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec("SELECT dt FROM " + tableName)); + QVERIFY_SQL(q, exec("SELECT dt FROM " + ts.tableName())); QVERIFY_SQL(q, next()); // Check if retrieved value preserves reported precision @@ -4191,12 +4050,10 @@ void tst_QSqlQuery::QTBUG_36211() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); if (tst_Databases::getDatabaseType(db) == QSqlDriver::PostgreSQL) { - const QString tableName(qTableName("bug36211", __FILE__, db)); - tst_Databases::safeDropTable(db, tableName); - + TableScope ts(db, "bug36211", __FILE__); QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (dtwtz timestamptz, dtwotz timestamp)") - .arg(tableName))); + .arg(ts.tableName()))); #if QT_CONFIG(timezone) QTimeZone l_tzBrazil("America/Sao_Paulo"); @@ -4205,7 +4062,7 @@ void tst_QSqlQuery::QTBUG_36211() QVERIFY(l_tzChina.isValid()); QDateTime dt = QDateTime(QDate(2014, 10, 30), QTime(14, 12, 02, 357)); QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 (dtwtz, dtwotz) VALUES (:dt, :dt)") - .arg(tableName))); + .arg(ts.tableName()))); q.bindValue(":dt", dt); QVERIFY_SQL(q, exec()); q.bindValue(":dt", dt.toTimeZone(l_tzBrazil)); @@ -4213,7 +4070,7 @@ void tst_QSqlQuery::QTBUG_36211() q.bindValue(":dt", dt.toTimeZone(l_tzChina)); QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec("SELECT dtwtz, dtwotz FROM " + tableName)); + QVERIFY_SQL(q, exec("SELECT dtwtz, dtwotz FROM " + ts.tableName())); for (int i = 0; i < 3; ++i) { QVERIFY_SQL(q, next()); @@ -4239,23 +4096,21 @@ void tst_QSqlQuery::QTBUG_53969() CHECK_DATABASE(db); tableValues.reserve(values.size()); if (tst_Databases::getDatabaseType(db) == QSqlDriver::MySqlServer) { - const QString tableName(qTableName("bug53969", __FILE__, db)); - tst_Databases::safeDropTable(db, tableName); - + TableScope ts(db, "bug53969", __FILE__); QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (id INT AUTO_INCREMENT PRIMARY KEY, " "test_number TINYINT(3) UNSIGNED)") - .arg(tableName))); + .arg(ts.tableName()))); QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 (test_number) VALUES (:value)") - .arg(tableName))); + .arg(ts.tableName()))); for (int value : values) { q.bindValue(":value", value); QVERIFY_SQL(q, exec()); } - QVERIFY_SQL(q, prepare("SELECT test_number FROM " + tableName)); + QVERIFY_SQL(q, prepare("SELECT test_number FROM " + ts.tableName())); QVERIFY_SQL(q, exec()); while (q.next()) { @@ -4272,17 +4127,16 @@ void tst_QSqlQuery::gisPointDatatype() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "qtbug72140", __FILE__); QSqlQuery sqlQuery(db); - const auto tableName = qTableName("qtbug72140", __FILE__, db); - tst_Databases::safeDropTable(db, tableName); QVERIFY(sqlQuery.exec(QLatin1String( "CREATE TABLE %1 (`lonlat_point` POINT NULL) ENGINE = InnoDB;") - .arg(tableName))); + .arg(ts.tableName()))); QVERIFY(sqlQuery.exec(QLatin1String( "INSERT INTO %1(lonlat_point) VALUES(ST_GeomFromText('POINT(1 1)'));") - .arg(tableName))); - QVERIFY(sqlQuery.exec(QLatin1String("SELECT * FROM %1;").arg(tableName))); + .arg(ts.tableName()))); + QVERIFY(sqlQuery.exec(QLatin1String("SELECT * FROM %1;").arg(ts.tableName()))); QCOMPARE(sqlQuery.record().field(0).metaType().id(), QMetaType::QByteArray); QVERIFY(sqlQuery.next()); } @@ -4292,13 +4146,13 @@ void tst_QSqlQuery::oraOCINumber() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString qtest_oraOCINumber(qTableName("qtest_oraOCINumber", __FILE__, db)); + TableScope ts(db, "qtest_oraOCINumber", __FILE__); QSqlQuery q(db); q.setForwardOnly(true); QVERIFY_SQL(q, exec(QLatin1String("create table %1 (col1 number(20), col2 number(20))") - .arg(qtest_oraOCINumber))); - QVERIFY(q.prepare(QLatin1String("insert into %1 values (?, ?)").arg(qtest_oraOCINumber))); + .arg(ts.tableName()))); + QVERIFY(q.prepare(QLatin1String("insert into %1 values (?, ?)").arg(ts.tableName()))); const QVariantList col1Values = { qulonglong(1), qulonglong(0), qulonglong(INT_MAX), qulonglong(UINT_MAX), @@ -4315,7 +4169,7 @@ void tst_QSqlQuery::oraOCINumber() QVERIFY(q.execBatch()); QVERIFY(q.prepare(QLatin1String( "select * from %1 where col1 = :bindValue0 AND col2 = :bindValue1") - .arg(qtest_oraOCINumber))); + .arg(ts.tableName()))); q.bindValue(":bindValue0", qulonglong(1), QSql::InOut); q.bindValue(":bindValue1", qlonglong(1), QSql::InOut); @@ -4387,8 +4241,6 @@ void tst_QSqlQuery::sqlite_constraint() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - if (tst_Databases::getDatabaseType(db) != QSqlDriver::SQLite) - QSKIP("SQLite3-specific test"); QSqlQuery q(db); const QString trigger(qTableName("test_constraint", __FILE__, db)); @@ -4408,30 +4260,64 @@ void tst_QSqlQuery::sqlite_real() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const QString tableName(qTableName("sqliterealtype", __FILE__, db)); - tst_Databases::safeDropTable(db, tableName); + TableScope ts(db, "sqliterealtype", __FILE__); QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (id INTEGER, realVal REAL)") - .arg(tableName))); + .arg(ts.tableName()))); QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO %1 (id, realVal) VALUES (1, 2.3)") - .arg(tableName))); - QVERIFY_SQL(q, exec("SELECT realVal FROM " + tableName)); + .arg(ts.tableName()))); + QVERIFY_SQL(q, exec("SELECT realVal FROM " + ts.tableName())); QVERIFY(q.next()); QCOMPARE(q.value(0).toDouble(), 2.3); QCOMPARE(q.record().field(0).metaType().id(), QMetaType::Double); - q.prepare(QLatin1String("INSERT INTO %1 (id, realVal) VALUES (?, ?)").arg(tableName)); + q.prepare(QLatin1String("INSERT INTO %1 (id, realVal) VALUES (?, ?)").arg(ts.tableName())); QVariant var((double)5.6); q.addBindValue(4); q.addBindValue(var); QVERIFY_SQL(q, exec()); - QVERIFY_SQL(q, exec(QLatin1String("SELECT realVal FROM %1 WHERE ID=4").arg(tableName))); + QVERIFY_SQL(q, exec(QLatin1String("SELECT realVal FROM %1 WHERE ID=4").arg(ts.tableName()))); QVERIFY(q.next()); QCOMPARE(q.value(0).toDouble(), 5.6); } +void tst_QSqlQuery::prepared_query_json_row() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + if (tst_Databases::getDatabaseType(db) != QSqlDriver::MySqlServer && + tst_Databases::getDatabaseType(db) != QSqlDriver::PostgreSQL) { + QSKIP("PostgreSQL / MySQL specific test"); + } + + TableScope ts(db, "tableWithJsonRow", __FILE__); + QSqlQuery q(db); + const QLatin1String vals[] = {QLatin1String("{\"certificateNumber\": \"CERT-001\"}"), + QLatin1String("{\"certificateNumber\": \"CERT-002\"}")}; + QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (id INTEGER, value JSON)").arg(ts.tableName()))); + for (const QLatin1String &json : vals) { + QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO %1 (id, value) VALUES (1, '%2')") + .arg(ts.tableName(), json))); + } + + QVERIFY_SQL(q, prepare(QLatin1String("SELECT id, value FROM %1 WHERE id = ?").arg(ts.tableName()))); + q.addBindValue(1); + QVERIFY_SQL(q, exec()); + + size_t iCount = 0; + while (q.next()) { + QVERIFY(iCount < sizeof(vals)); + const int id = q.value(0).toInt(); + const QByteArray json = q.value(1).toByteArray(); + QCOMPARE(id, 1); + QCOMPARE(json, vals[iCount].data()); + ++iCount; + } +} + void tst_QSqlQuery::aggregateFunctionTypes() { QFETCH(QString, dbName); @@ -4442,7 +4328,8 @@ void tst_QSqlQuery::aggregateFunctionTypes() int countType = intType; // QPSQL uses LongLong for manipulation of integers const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); - if (dbType == QSqlDriver::PostgreSQL || dbType == QSqlDriver::Interbase) { + if (dbType == QSqlDriver::PostgreSQL || dbType == QSqlDriver::Interbase + || dbType == QSqlDriver::MimerSQL) { sumType = countType = QMetaType::LongLong; } else if (dbType == QSqlDriver::Oracle) { intType = sumType = countType = QMetaType::Double; @@ -4451,8 +4338,8 @@ void tst_QSqlQuery::aggregateFunctionTypes() countType = QMetaType::LongLong; } { - const QString tableName(qTableName("numericFunctionsWithIntValues", __FILE__, db)); - tst_Databases::safeDropTable(db, tableName); + TableScope ts(db, "numericFunctionsWithIntValues", __FILE__); + const auto &tableName = ts.tableName(); QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (id INTEGER)").arg(tableName))); @@ -4501,8 +4388,8 @@ void tst_QSqlQuery::aggregateFunctionTypes() QCOMPARE(q.record().field(0).metaType().id(), intType); } { - const QString tableName(qTableName("numericFunctionsWithDoubleValues", __FILE__, db)); - tst_Databases::safeDropTable(db, tableName); + TableScope ts(db, "numericFunctionsWithDoubleValues", __FILE__); + const auto &tableName = ts.tableName(); QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (id REAL)").arg(tableName))); @@ -4565,15 +4452,15 @@ void tst_QSqlQuery::aggregateFunctionTypes() QCOMPARE(q.record().field(0).metaType().id(), QMetaType::Double); } { - const QString tableName(qTableName("stringFunctions", __FILE__, db)); - tst_Databases::safeDropTable(db, tableName); + TableScope ts(db, "stringFunctions", __FILE__); + const auto &tableName = ts.tableName(); QSqlQuery q(db); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (id INTEGER, txt VARCHAR(50))") .arg(tableName))); QVERIFY_SQL(q, exec("SELECT MAX(txt) FROM " + tableName)); - QVERIFY(q.next()); + QVERIFY_SQL(q, next()); if (dbType == QSqlDriver::SQLite) QCOMPARE(q.record().field(0).metaType().id(), QMetaType::UnknownType); else @@ -4588,6 +4475,19 @@ void tst_QSqlQuery::aggregateFunctionTypes() QVERIFY(q.next()); QCOMPARE(q.value(0).toString(), QLatin1String("upper")); QCOMPARE(q.record().field(0).metaType().id(), QMetaType::QString); + + QVERIFY_SQL(q, exec(QLatin1String("DELETE FROM %1").arg(tableName))); + QVERIFY_SQL(q, exec(QString::fromUtf8("INSERT INTO %1 (id, txt) VALUES (1, 'löW€RÄ')") + .arg(tableName))); + QVERIFY_SQL(q, exec("SELECT LOWER(txt) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toString(), QString::fromUtf8("löw€rä")); + QCOMPARE(q.record().field(0).metaType().id(), QMetaType::QString); + + QVERIFY_SQL(q, exec("SELECT UPPER(txt) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toString(), QString::fromUtf8("LÖW€RÄ")); + QCOMPARE(q.record().field(0).metaType().id(), QMetaType::QString); } } @@ -4597,9 +4497,9 @@ void runIntegralTypesMysqlTest(QSqlDatabase &db, const QString &tableName, const { QList<QVariant> variantValues; variantValues.reserve(values.size()); + TableScope ts(db, tableName); QSqlQuery q(db); - QVERIFY_SQL(q, exec("DROP TABLE IF EXISTS " + tableName)); QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %2 (id %1)").arg(type, tableName))); if (withPreparedStatement) { @@ -4685,29 +4585,35 @@ void tst_QSqlQuery::integralTypesMysql() runIntegralTypesMysqlTest<qint64>(db, "bigIntTest", "BIGINT", withPreparedStatement); runIntegralTypesMysqlTest<quint64>(db, "unsignedBigIntTest", "BIGINT UNSIGNED", withPreparedStatement); + runIntegralTypesMysqlTest<quint64>(db, "bitmask_7", "BIT(7)", withPreparedStatement, 0, + (1LL << 7) - 1); + runIntegralTypesMysqlTest<quint64>(db, "bitmask_31", "BIT(31)", withPreparedStatement, 0, + (1LL << 31) - 1); + runIntegralTypesMysqlTest<quint64>(db, "bitmask_33", "BIT(33)", withPreparedStatement, 0, + (1LL << 33) - 1); + runIntegralTypesMysqlTest<quint64>(db, "bitmask_64", "BIT(64)", withPreparedStatement); } } void tst_QSqlQuery::QTBUG_57138() { - const QDateTime utc = QDateTime(QDate(2150, 1, 5), QTime(14, 0, 0, 123), Qt::UTC); - const QDateTime localtime = QDateTime(QDate(2150, 1, 5), QTime(14, 0, 0, 123), Qt::LocalTime); - const QDateTime tzoffset = QDateTime(QDate(2150, 1, 5), QTime(14, 0, 0, 123), - Qt::OffsetFromUTC, 3600); + const QDateTime utc(QDate(2150, 1, 5), QTime(14, 0, 0, 123), QTimeZone::UTC); + const QDateTime localtime(QDate(2150, 1, 5), QTime(14, 0, 0, 123)); + const QDateTime tzoffset(QDate(2150, 1, 5), QTime(14, 0, 0, 123), + QTimeZone::fromSecondsAheadOfUtc(3600)); QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "qtbug57138", __FILE__); QSqlQuery create(db); - QString tableName = qTableName("qtbug57138", __FILE__, db); - tst_Databases::safeDropTable(db, tableName); QVERIFY_SQL(create, exec(QLatin1String( "create table %1 (id int, dt_utc datetime, dt_lt datetime, " - "dt_tzoffset datetime)").arg(tableName))); + "dt_tzoffset datetime)").arg(ts.tableName()))); QVERIFY_SQL(create, prepare(QLatin1String("insert into %1 (id, dt_utc, dt_lt, dt_tzoffset) " - "values (?, ?, ?, ?)").arg(tableName))); + "values (?, ?, ?, ?)").arg(ts.tableName()))); create.addBindValue(0); create.addBindValue(utc); @@ -4717,7 +4623,7 @@ void tst_QSqlQuery::QTBUG_57138() QSqlQuery q(db); q.prepare(QLatin1String("SELECT dt_utc, dt_lt, dt_tzoffset FROM %1 WHERE id = ?") - .arg(tableName)); + .arg(ts.tableName())); q.addBindValue(0); QVERIFY_SQL(q, exec()); @@ -4733,17 +4639,15 @@ void tst_QSqlQuery::QTBUG_73286() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + TableScope ts(db, "qtbug73286", __FILE__); QSqlQuery create(db); - QString tableName = qTableName("qtbug73286", __FILE__, db); - tst_Databases::safeDropTable(db, tableName); - QVERIFY_SQL(create, exec(QLatin1String( "create table %1 (dec2 decimal(4,2), dec0 decimal(20,0), " - "dec3 decimal(20,3))").arg(tableName))); + "dec3 decimal(20,3))").arg(ts.tableName()))); QVERIFY_SQL(create, prepare(QLatin1String( "insert into %1 (dec2, dec0, dec3) values (?, ?, ?)") - .arg(tableName))); + .arg(ts.tableName()))); create.addBindValue("99.99"); create.addBindValue("12345678901234567890"); @@ -4752,7 +4656,7 @@ void tst_QSqlQuery::QTBUG_73286() QVERIFY_SQL(create, exec()); QSqlQuery q(db); - q.prepare("SELECT dec2, dec0, dec3 FROM " + tableName); + q.prepare("SELECT dec2, dec0, dec3 FROM " + ts.tableName()); q.setNumericalPrecisionPolicy(QSql::HighPrecision); QVERIFY_SQL(q, exec()); @@ -4763,6 +4667,22 @@ void tst_QSqlQuery::QTBUG_73286() QCOMPARE(q.value(2).toString(), "12345678901234567.890"); } +void tst_QSqlQuery::insertVarChar1() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlQuery q(db); + TableScope ts(db, "testtable", __FILE__); + QVERIFY_SQL(q, exec(QLatin1String("CREATE TABLE %1 (smallcol VARCHAR(1))").arg(ts.tableName()))); + QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 (smallcol) VALUES (?)").arg(ts.tableName()))); + QSqlField smallCol("smallcol"); + smallCol.setValue(QVariant(QString(QChar('F')))); + q.bindValue(0, smallCol.value()); + QVERIFY_SQL(q, exec()); +} + void tst_QSqlQuery::dateTime_data() { if (dbs.dbNames.isEmpty()) @@ -4814,13 +4734,11 @@ void tst_QSqlQuery::dateTime_data() #endif }; - for (const QString &dbName : qAsConst(dbs.dbNames)) { + for (const QString &dbName : std::as_const(dbs.dbNames)) { QSqlDatabase db = QSqlDatabase::database(dbName); if (!db.isValid()) continue; const QString tableNameTSWithTimeZone(qTableName("dateTimeTSWithTimeZone", __FILE__, db)); - const QString tableNameTSWithLocalTimeZone(qTableName("dateTimeTSWithLocalTimeZone", - __FILE__, db)); const QString tableNameTS(qTableName("dateTimeTS", __FILE__, db)); const QString tableNameDate(qTableName("dateTimeDate", __FILE__, db)); QTest::newRow(QString(dbName + " timestamp with time zone").toLatin1()) @@ -4853,17 +4771,93 @@ void tst_QSqlQuery::dateTime() QFETCH(QList<QDateTime>, initialDateTimes); QFETCH(QList<QDateTime>, expectedDateTimes); - tst_Databases::safeDropTable(db, tableName); + TableScope ts(db, tableName); QSqlQuery q(db); QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + createTableString)); - for (const QDateTime &dt : qAsConst(initialDateTimes)) { + for (const QDateTime &dt : std::as_const(initialDateTimes)) { + QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 values(:dt)").arg(tableName))); + q.bindValue(":dt", dt); + QVERIFY_SQL(q, exec()); + } + QVERIFY_SQL(q, exec("SELECT * FROM " + tableName)); + for (const QDateTime &dt : std::as_const(expectedDateTimes)) { + QVERIFY(q.next()); + QCOMPARE(q.value(0).toDateTime(), dt); + } +} + +void tst_QSqlQuery::ibaseDateTimeWithTZ_data() +{ + if (dbs.dbNames.isEmpty()) + QSKIP("No database drivers are available in this Qt configuration"); + + QTest::addColumn<QString>("dbName"); + QTest::addColumn<QString>("tableName"); + QTest::addColumn<QList<QDateTime> >("initialDateTimes"); + QTest::addColumn<QList<QDateTime> >("expectedDateTimes"); + +#if QT_CONFIG(timezone) + const QTimeZone afterUTCTimeZone("Asia/Hong_Kong"); + const QTimeZone beforeUTCTimeZone("America/Los_Angeles"); + const QDateTime dtWithAfterTZ(QDate(2015, 5, 18), QTime(4, 26, 30, 500), afterUTCTimeZone); + const QDateTime dtWithBeforeTZ(QDate(2015, 5, 18), QTime(4, 26, 30, 500), beforeUTCTimeZone); + const QTimeZone utcTimeZone("UTC"); + const QDateTime dtWithUTCTZ(QDate(2015, 5, 18), QTime(4, 26, 30, 500), utcTimeZone); +#endif // QT_CONFIG(timezone) + const QDateTime dtLocalTZ(QDateTime::currentDateTime()); + + const QList<QDateTime> dateTimes = { +#if QT_CONFIG(timezone) + dtWithAfterTZ, + dtWithBeforeTZ, + dtWithUTCTZ, +#endif // QT_CONFIG(timezone) + dtLocalTZ + }; + + for (const QString &dbName : std::as_const(dbs.dbNames)) { + QSqlDatabase db = QSqlDatabase::database(dbName); + if (!db.isValid()) + continue; + + const QString tableNameTSWithTimeZone(qTableName("dateTimeTSWithTZ", __FILE__, db)); + + QTest::newRow(QString(dbName + " timestamp with time zone").toLatin1()) + << dbName + << tableNameTSWithTimeZone + << dateTimes + << dateTimes; + } +} + +void tst_QSqlQuery::ibaseDateTimeWithTZ() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + if (tst_Databases::getDatabaseType(db) != QSqlDriver::Interbase) + QSKIP("Implemented only for Interbase"); + + if (tst_Databases::getIbaseEngineVersion(db).majorVersion() < 4) + QSKIP("Time zone support only implemented for firebird engine version 4 and greater"); + + QFETCH(QString, tableName); + TableScope ts(db, tableName); + + QSqlQuery q(db); + QVERIFY_SQL(q, exec(QString("CREATE TABLE " + tableName + "(dt timestamp with time zone)"))); + + QFETCH(QList<QDateTime>, initialDateTimes); + QFETCH(QList<QDateTime>, expectedDateTimes); + + for (const QDateTime &dt : std::as_const(initialDateTimes)) { QVERIFY_SQL(q, prepare(QLatin1String("INSERT INTO %1 values(:dt)").arg(tableName))); q.bindValue(":dt", dt); QVERIFY_SQL(q, exec()); } QVERIFY_SQL(q, exec("SELECT * FROM " + tableName)); - for (const QDateTime &dt : qAsConst(expectedDateTimes)) { + for (const QDateTime &dt : std::as_const(expectedDateTimes)) { QVERIFY(q.next()); QCOMPARE(q.value(0).toDateTime(), dt); } @@ -4919,8 +4913,8 @@ void tst_QSqlQuery::mysql_timeType() QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const auto tableName = qTableName("mysqlTimeType", __FILE__, db); - tst_Databases::safeDropTables(db, { tableName }); + TableScope ts(db, "mysqlTimeType", __FILE__); + const auto &tableName = ts.tableName(); QSqlQuery qry(db); QVERIFY_SQL(qry, exec(QLatin1String("create table %1 (t time(6))").arg(tableName))); @@ -4981,14 +4975,13 @@ void tst_QSqlQuery::ibaseArray() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); - const auto arrayTable = qTableName("ibasearray", __FILE__, db); - tst_Databases::safeDropTable(db, arrayTable); + TableScope ts(db, "ibasearray", __FILE__); QSqlQuery qry(db); QVERIFY_SQL(qry, exec(QLatin1String( "create table %1 (intData int[0:4], longData bigint[5], " - "charData varchar(255)[5], boolData boolean[2])").arg(arrayTable))); + "charData varchar(255)[5], boolData boolean[2])").arg(ts.tableName()))); QVERIFY_SQL(qry, prepare(QLatin1String("insert into %1 (intData, longData, charData, boolData)" - " values(?, ?, ?, ?)").arg(arrayTable))); + " values(?, ?, ?, ?)").arg(ts.tableName()))); const auto intArray = QVariant{QVariantList{1, 2, 3, 4711, 815}}; const auto charArray = QVariant{QVariantList{"AAA", "BBB", "CCC", "DDD", "EEE"}}; const auto boolArray = QVariant{QVariantList{true, false}}; @@ -4997,7 +4990,7 @@ void tst_QSqlQuery::ibaseArray() qry.bindValue(2, charArray); qry.bindValue(3, boolArray); QVERIFY_SQL(qry, exec()); - QVERIFY_SQL(qry, exec("select * from " + arrayTable)); + QVERIFY_SQL(qry, exec("select * from " + ts.tableName())); QVERIFY(qry.next()); QCOMPARE(qry.value(0).toList(), intArray.toList()); QCOMPARE(qry.value(1).toList(), intArray.toList()); @@ -5005,6 +4998,42 @@ void tst_QSqlQuery::ibaseArray() QCOMPARE(qry.value(3).toList(), boolArray.toList()); } +void tst_QSqlQuery::ibaseTimeStampTzArray() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + if (tst_Databases::getIbaseEngineVersion(db).majorVersion() < 4) + QSKIP("Time zone support only implemented for firebird engine version 4 and greater"); + + TableScope ts(db, "ibasetstzarray", __FILE__); + QSqlQuery qry(db); + QVERIFY_SQL(qry, exec(QLatin1String( + "create table %1 (timeStampData timestamp with time zone[0:4])").arg(ts.tableName()))); + QVERIFY_SQL(qry, prepare(QLatin1String("insert into %1 (timeStampData)" + " values(?)").arg(ts.tableName()))); +#if QT_CONFIG(timezone) + const QDateTime dtWithAfterTZ(QDate(2015, 5, 18), QTime(4, 26, 30, 500), QTimeZone("Asia/Hong_Kong")); + const QDateTime dtWithBeforeTZ(QDate(2015, 5, 18), QTime(4, 26, 30, 500), QTimeZone("America/Los_Angeles")); + const QDateTime dtWithUTCTZ(QDate(2015, 5, 18), QTime(4, 26, 30, 500), QTimeZone("UTC")); + const QDateTime dtLocalTZ(QDateTime::currentDateTime()); + const QDateTime dtWithMETTZ(QDate(2015, 5, 18), QTime(4, 26, 30, 500), QTimeZone("MET")); + + + const auto timeStampData = QVariant{QVariantList{dtWithAfterTZ, + dtWithBeforeTZ, + dtWithUTCTZ, + dtLocalTZ, + dtWithMETTZ}}; + qry.bindValue(0, timeStampData); + QVERIFY_SQL(qry, exec()); + QVERIFY_SQL(qry, exec("select * from " + ts.tableName())); + QVERIFY(qry.next()); + QCOMPARE(qry.value(0).toList(), timeStampData.toList()); +#endif // QT_CONFIG(timezone) +} + void tst_QSqlQuery::ibase_executeBlock() { QFETCH(QString, dbName); @@ -5025,5 +5054,83 @@ void tst_QSqlQuery::ibase_executeBlock() QCOMPARE(qry.value(0).toInt(), 4); } +void tst_QSqlQuery::positionalBindingEnabled() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); + CHECK_DATABASE(db); + TableScope ts(db, "positionalBinding", __FILE__); + const QString &tableName = ts.tableName(); + + QSqlQuery qry(db); + QVERIFY_SQL(qry, exec("CREATE TABLE " + tableName + " (integer_col integer)")); + QVERIFY_SQL(qry, exec("INSERT INTO " + tableName + "(integer_col) VALUES(42)")); + + qry.setPositionalBindingEnabled(true); + QCOMPARE(qry.isPositionalBindingEnabled(), true); + QVERIFY_SQL(qry, prepare("SELECT integer_col FROM " + tableName + " WHERE integer_col = :integer_val")); + qry.bindValue(":integer_val", 42); + QVERIFY_SQL(qry, exec()); + QVERIFY_SQL(qry, next()); + QCOMPARE(qry.value(0).toInt(), 42); + QVERIFY_SQL(qry, prepare("SELECT integer_col FROM " + tableName + " WHERE integer_col = ?")); + qry.bindValue(0, 42); + QVERIFY_SQL(qry, exec()); + QVERIFY_SQL(qry, next()); + QCOMPARE(qry.value(0).toInt(), 42); + + qry.setPositionalBindingEnabled(false); + QCOMPARE(qry.isPositionalBindingEnabled(), false); + QVERIFY_SQL(qry, prepare("SELECT integer_col FROM " + tableName + " WHERE integer_col = :integer_val")); + qry.bindValue(":integer_val", 42); + QVERIFY_SQL(qry, exec()); + QVERIFY_SQL(qry, next()); + QCOMPARE(qry.value(0).toInt(), 42); + // the next query will only work when the underlying database support question mark notation natively + if (dbType == QSqlDriver::PostgreSQL) { + QVERIFY(!qry.prepare("SELECT integer_col FROM " + tableName + " WHERE integer_col = ?")); + qry.bindValue(0, 42); + QVERIFY(!qry.exec()); + QVERIFY(!qry.next()); + } else { + QVERIFY_SQL(qry, prepare("SELECT integer_col FROM " + tableName + " WHERE integer_col = ?")); + qry.bindValue(0, 42); + QVERIFY_SQL(qry, exec()); + QVERIFY_SQL(qry, next()); + QCOMPARE(qry.value(0).toInt(), 42); + } +} + +void tst_QSqlQuery::psqlJsonOperator() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + TableScope ts(db, "qTableName", __FILE__); + const QString &tableName = ts.tableName(); + + QSqlQuery qry(db); + qry.setPositionalBindingEnabled(false); // don't allow / handle '?' as placeholder + QVERIFY_SQL(qry, exec("CREATE TABLE " + tableName + " (integer_col integer, json_col jsonb)")); + QVERIFY_SQL(qry, exec("INSERT INTO " + tableName + "(integer_col, json_col) VALUES(42, '{\"a\": [1, 2]}')")); + QVERIFY_SQL(qry, exec("INSERT INTO " + tableName + "(integer_col, json_col) VALUES(43, '{\"b\": [3, 4]}')")); + + QVERIFY_SQL(qry, prepare("SELECT integer_col, json_col FROM " + tableName + " WHERE json_col @? '$.a[*] ? (@ == 1)' and integer_col = :int")); + qry.bindValue(":int", 42); + QVERIFY_SQL(qry, exec()); + QVERIFY_SQL(qry, next()); + QCOMPARE(qry.value(0).toInt(), 42); + QCOMPARE(qry.value(1).toByteArray(), "{\"a\": [1, 2]}"); + + QVERIFY_SQL(qry, prepare("SELECT integer_col, json_col FROM " + tableName + " WHERE json_col ? 'b' and integer_col = :int")); + qry.bindValue(":int", 43); + QVERIFY_SQL(qry, exec()); + QVERIFY_SQL(qry, next()); + QCOMPARE(qry.value(0).toInt(), 43); + QCOMPARE(qry.value(1).toByteArray(), "{\"b\": [3, 4]}"); +} + + QTEST_MAIN(tst_QSqlQuery) #include "tst_qsqlquery.moc" diff --git a/tests/auto/sql/kernel/qsqlrecord/CMakeLists.txt b/tests/auto/sql/kernel/qsqlrecord/CMakeLists.txt index aa9e0a417d..18d46669db 100644 --- a/tests/auto/sql/kernel/qsqlrecord/CMakeLists.txt +++ b/tests/auto/sql/kernel/qsqlrecord/CMakeLists.txt @@ -1,12 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from qsqlrecord.pro. - ##################################################################### ## tst_qsqlrecord Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsqlrecord LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsqlrecord SOURCES tst_qsqlrecord.cpp diff --git a/tests/auto/sql/kernel/qsqlrecord/tst_qsqlrecord.cpp b/tests/auto/sql/kernel/qsqlrecord/tst_qsqlrecord.cpp index 478dec6ac4..6aeae86d7d 100644 --- a/tests/auto/sql/kernel/qsqlrecord/tst_qsqlrecord.cpp +++ b/tests/auto/sql/kernel/qsqlrecord/tst_qsqlrecord.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -14,12 +14,7 @@ class tst_QSqlRecord : public QObject { -Q_OBJECT - -public: - tst_QSqlRecord(); - virtual ~tst_QSqlRecord(); - + Q_OBJECT public slots: void init(); @@ -45,28 +40,14 @@ private slots: void clearValues(); void clear(); void append(); + void moveSemantics(); private: - QSqlRecord* rec; - QSqlField* fields[ NUM_FIELDS ]; + std::unique_ptr<QSqlRecord> rec; + std::array<std::unique_ptr<QSqlField>, NUM_FIELDS> fields; void createTestRecord(); }; -tst_QSqlRecord::tst_QSqlRecord() -{ - rec = 0; - for ( int i = 0; i < NUM_FIELDS; ++i ) - fields[ i ] = 0; -} - -tst_QSqlRecord::~tst_QSqlRecord() -{ - delete rec; - for ( int i = 0; i < NUM_FIELDS; ++i ) - delete fields[ i ]; - rec = 0; -} - void tst_QSqlRecord::init() { cleanup(); @@ -74,31 +55,26 @@ void tst_QSqlRecord::init() void tst_QSqlRecord::cleanup() { - delete rec; - for ( int i = 0; i < NUM_FIELDS; ++i ) { - delete fields[ i ]; - fields[ i ] = 0; - } - rec = 0; + rec = nullptr; + for (auto &field : fields) + field = nullptr; } void tst_QSqlRecord::createTestRecord() { - delete rec; - rec = new QSqlRecord(); - fields[0] = new QSqlField(QStringLiteral("string"), QMetaType(QMetaType::QString), QStringLiteral("stringtable")); - fields[1] = new QSqlField(QStringLiteral("int"), QMetaType(QMetaType::Int), QStringLiteral("inttable")); - fields[2] = new QSqlField(QStringLiteral("double"), QMetaType(QMetaType::Double), QStringLiteral("doubletable")); - fields[3] = new QSqlField(QStringLiteral("bool"), QMetaType(QMetaType::Bool)); - for ( int i = 0; i < NUM_FIELDS; ++i ) - rec->append( *(fields[ i ] ) ); + rec = std::make_unique<QSqlRecord>(); + fields[0] = std::make_unique<QSqlField>(QStringLiteral("string"), QMetaType(QMetaType::QString), QStringLiteral("stringtable")); + fields[1] = std::make_unique<QSqlField>(QStringLiteral("int"), QMetaType(QMetaType::Int), QStringLiteral("inttable")); + fields[2] = std::make_unique<QSqlField>(QStringLiteral("double"), QMetaType(QMetaType::Double), QStringLiteral("doubletable")); + fields[3] = std::make_unique<QSqlField>(QStringLiteral("bool"), QMetaType(QMetaType::Bool)); + for (const auto &field : fields) + rec->append(*field); } void tst_QSqlRecord::append() { - delete rec; - rec = new QSqlRecord(); + rec = std::make_unique<QSqlRecord>(); rec->append(QSqlField("string", QMetaType(QMetaType::QString), QStringLiteral("stringtable"))); QCOMPARE( rec->field( 0 ).name(), (QString) "string" ); QCOMPARE(rec->field(0).tableName(), QStringLiteral("stringtable")); @@ -157,10 +133,7 @@ void tst_QSqlRecord::clearValues() QFETCH( double, dval ); QFETCH( int, bval ); - if(rec) - delete rec; - - rec = new QSqlRecord(); + rec = std::make_unique<QSqlRecord>(); rec->append( QSqlField( "string", QMetaType(QMetaType::QString) ) ); QCOMPARE( rec->field(0).name(), (QString) "string" ); QVERIFY( !rec->isEmpty() ); @@ -200,8 +173,8 @@ void tst_QSqlRecord::clearValues() void tst_QSqlRecord::contains() { createTestRecord(); - for ( int i = 0; i < NUM_FIELDS; ++i ) - QVERIFY( rec->contains( fields[ i ]->name() ) ); + for (const auto &field : fields) + QVERIFY(rec->contains(field->name())); QVERIFY( !rec->contains( "__Harry__" ) ); } @@ -233,8 +206,8 @@ void tst_QSqlRecord::fieldName() { createTestRecord(); - for ( int i = 0; i < NUM_FIELDS; ++i ) - QVERIFY( rec->field( (fields[ i ] )->name() ) == *( fields[ i ] ) ); + for (const auto &field : fields) + QVERIFY(rec->field(field->name()) == *field); QVERIFY( rec->fieldName( NUM_FIELDS ).isNull() ); } @@ -412,8 +385,7 @@ void tst_QSqlRecord::setValue() { int i; - delete rec; - rec = new QSqlRecord(); + rec = std::make_unique<QSqlRecord>(); rec->append( QSqlField( "string", QMetaType(QMetaType::QString) ) ); QCOMPARE( rec->field( 0 ).name(), (QString) "string" ); QVERIFY( !rec->isEmpty() ); @@ -478,5 +450,24 @@ void tst_QSqlRecord::value() QCOMPARE(rec2.value("string").toString(), QLatin1String("Harry")); } +void tst_QSqlRecord::moveSemantics() +{ + QSqlRecord rec, empty; + rec.append(QSqlField("string", QMetaType(QMetaType::QString))); + rec.setValue("string", "Harry"); + auto moved = std::move(rec); + // `rec` is not partially-formed + + // moving transfers state: + QCOMPARE(moved.value("string").toString(), QLatin1String("Harry")); + + // moved-from objects can be assigned-to: + rec = empty; + QVERIFY(rec.value("string").isNull()); + + // moved-from object can be destroyed: + moved = std::move(rec); +} + QTEST_MAIN(tst_QSqlRecord) #include "tst_qsqlrecord.moc" diff --git a/tests/auto/sql/kernel/qsqlresult/CMakeLists.txt b/tests/auto/sql/kernel/qsqlresult/CMakeLists.txt index b132e3365d..1fc475b144 100644 --- a/tests/auto/sql/kernel/qsqlresult/CMakeLists.txt +++ b/tests/auto/sql/kernel/qsqlresult/CMakeLists.txt @@ -1,12 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from qsqlresult.pro. - ##################################################################### ## tst_qsqlresult Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsqlresult LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsqlresult SOURCES testsqldriver.h diff --git a/tests/auto/sql/kernel/qsqlresult/testsqldriver.h b/tests/auto/sql/kernel/qsqlresult/testsqldriver.h index 79be0dc230..072e683d68 100644 --- a/tests/auto/sql/kernel/qsqlresult/testsqldriver.h +++ b/tests/auto/sql/kernel/qsqlresult/testsqldriver.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTSQLDRIVER_H #define TESTSQLDRIVER_H diff --git a/tests/auto/sql/kernel/qsqlresult/tst_qsqlresult.cpp b/tests/auto/sql/kernel/qsqlresult/tst_qsqlresult.cpp index 6ef3ad4d19..da438dcfd6 100644 --- a/tests/auto/sql/kernel/qsqlresult/tst_qsqlresult.cpp +++ b/tests/auto/sql/kernel/qsqlresult/tst_qsqlresult.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtSql/QtSql> diff --git a/tests/auto/sql/kernel/qsqlthread/CMakeLists.txt b/tests/auto/sql/kernel/qsqlthread/CMakeLists.txt index 21565aee26..98108528e6 100644 --- a/tests/auto/sql/kernel/qsqlthread/CMakeLists.txt +++ b/tests/auto/sql/kernel/qsqlthread/CMakeLists.txt @@ -1,12 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from qsqlthread.pro. - ##################################################################### ## tst_qsqlthread Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsqlthread LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsqlthread SOURCES tst_qsqlthread.cpp diff --git a/tests/auto/sql/kernel/qsqlthread/tst_qsqlthread.cpp b/tests/auto/sql/kernel/qsqlthread/tst_qsqlthread.cpp index d868cf0197..0bebb7edd5 100644 --- a/tests/auto/sql/kernel/qsqlthread/tst_qsqlthread.cpp +++ b/tests/auto/sql/kernel/qsqlthread/tst_qsqlthread.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -258,18 +258,16 @@ void tst_QSqlThread::generic_data(const QString& engine) void tst_QSqlThread::dropTestTables() { - for (int i = 0; i < dbs.dbNames.count(); ++i) { - QSqlDatabase db = QSqlDatabase::database(dbs.dbNames.at(i)); - QSqlQuery q(db); - - tst_Databases::safeDropTables(db, QStringList() << qtest << qTableName("qtest2", __FILE__, db) << qTableName("emptytable", __FILE__, db)); + for (const auto &dbName : dbs.dbNames) { + QSqlDatabase db = QSqlDatabase::database(dbName); + tst_Databases::safeDropTables(db, { qtest, qTableName("qtest2", __FILE__, db), qTableName("emptytable", __FILE__, db) }); } } void tst_QSqlThread::createTestTables() { - for (int i = 0; i < dbs.dbNames.count(); ++i) { - QSqlDatabase db = QSqlDatabase::database(dbs.dbNames.at(i)); + for (const auto &dbName : dbs.dbNames) { + QSqlDatabase db = QSqlDatabase::database(dbName); QSqlQuery q(db); QVERIFY_SQL(q, exec("create table " + qtest @@ -285,8 +283,8 @@ void tst_QSqlThread::createTestTables() void tst_QSqlThread::repopulateTestTables() { - for (int i = 0; i < dbs.dbNames.count(); ++i) { - QSqlDatabase db = QSqlDatabase::database(dbs.dbNames.at(i)); + for (const auto &dbName : dbs.dbNames) { + QSqlDatabase db = QSqlDatabase::database(dbName); QSqlQuery q(db); QVERIFY_SQL(q, exec("delete from " + qtest)); diff --git a/tests/auto/sql/kernel/qvfssql/CMakeLists.txt b/tests/auto/sql/kernel/qvfssql/CMakeLists.txt new file mode 100644 index 0000000000..7c66f055a6 --- /dev/null +++ b/tests/auto/sql/kernel/qvfssql/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qsqlfield Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qvfssql LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qvfssql + SOURCES + tst_qvfssql.cpp + LIBRARIES + Qt::SqlPrivate +) + +set(qvfssql_resource_files + "sample.db" +) + +qt_internal_add_resource(tst_qvfssql "tst_qvfssql" + PREFIX + "/ro/" + FILES + ${qvfssql_resource_files} +) diff --git a/tests/auto/sql/kernel/qvfssql/sample.db b/tests/auto/sql/kernel/qvfssql/sample.db Binary files differnew file mode 100644 index 0000000000..56e6427e3c --- /dev/null +++ b/tests/auto/sql/kernel/qvfssql/sample.db diff --git a/tests/auto/sql/kernel/qvfssql/tst_qvfssql.cpp b/tests/auto/sql/kernel/qvfssql/tst_qvfssql.cpp new file mode 100644 index 0000000000..0dbdf7e60a --- /dev/null +++ b/tests/auto/sql/kernel/qvfssql/tst_qvfssql.cpp @@ -0,0 +1,94 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> + +#include <qsqldatabase.h> +#include <qstandardpaths.h> + +#include "../qsqldatabase/tst_databases.h" + +using namespace Qt::StringLiterals; + +class tst_QVfsSql : public QObject +{ + Q_OBJECT +private slots: + void testRoDb(); + void testRwDb(); +}; + +void tst_QVfsSql::testRoDb() +{ + QVERIFY(QSqlDatabase::drivers().contains("QSQLITE"_L1)); + QSqlDatabase::addDatabase("QSQLITE"_L1, "ro_db"_L1); + QSqlDatabase db = QSqlDatabase::database("ro_db"_L1, false); + QVERIFY_SQL(db, isValid()); + db.setDatabaseName(":/ro/sample.db"_L1); + + db.setConnectOptions("QSQLITE_USE_QT_VFS"_L1); + QVERIFY(!db.open()); // can not open as the QSQLITE_OPEN_READONLY attribute is missing + + db.setConnectOptions("QSQLITE_USE_QT_VFS;QSQLITE_OPEN_READONLY"_L1); + QVERIFY_SQL(db, open()); + + QStringList tables = db.tables(); + QSqlQuery q{db}; + for (auto table : {"reltest1"_L1, "reltest2"_L1, "reltest3"_L1, "reltest4"_L1, "reltest5"_L1}) { + QVERIFY(tables.contains(table)); + QVERIFY_SQL(q, exec("select * from " + table)); + QVERIFY(q.next()); + } + QVERIFY_SQL(q, exec("select * from reltest1 where id = 4"_L1)); + QVERIFY_SQL(q, first()); + QVERIFY(q.value(0).toInt() == 4); + QVERIFY(q.value(1).toString() == "boris"_L1); + QVERIFY(q.value(2).toInt() == 2); + QVERIFY(q.value(3).toInt() == 2); +} + +void tst_QVfsSql::testRwDb() +{ + QSqlDatabase::addDatabase("QSQLITE"_L1, "rw_db"_L1); + QSqlDatabase db = QSqlDatabase::database("rw_db"_L1, false); + QVERIFY_SQL(db, isValid()); + const auto dbPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/test_qt_vfs.db"_L1; + db.setDatabaseName(dbPath); + QFile::remove(dbPath); + + db.setConnectOptions("QSQLITE_USE_QT_VFS;QSQLITE_OPEN_READONLY"_L1); + QVERIFY(!db.open()); // can not open as the QSQLITE_OPEN_READONLY attribute is set and the file is missing + + db.setConnectOptions("QSQLITE_USE_QT_VFS"_L1); + QVERIFY_SQL(db, open()); + + QVERIFY(db.tables().isEmpty()); + QSqlQuery q{db}; + QVERIFY_SQL(q, exec("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, val INTEGER)"_L1)); + QVERIFY_SQL(q, exec("BEGIN"_L1)); + for (int i = 0; i < 1000; ++i) { + q.prepare("INSERT INTO test (val) VALUES (:val)"_L1); + q.bindValue(":val"_L1, i); + QVERIFY_SQL(q, exec()); + } + QVERIFY_SQL(q, exec("COMMIT"_L1)); + QVERIFY_SQL(q, exec("SELECT val FROM test ORDER BY val"_L1)); + for (int i = 0; i < 1000; ++i) { + QVERIFY_SQL(q, next()); + QCOMPARE(q.value(0).toInt() , i); + } + QVERIFY_SQL(q, exec("DELETE FROM test WHERE val < 500"_L1)); + auto fileSize = QFileInfo{dbPath}.size(); + QVERIFY_SQL(q, exec("VACUUM"_L1)); + QVERIFY(QFileInfo{dbPath}.size() < fileSize); // TEST xTruncate VFS + QVERIFY_SQL(q, exec("SELECT val FROM test ORDER BY val"_L1)); + for (int i = 500; i < 1000; ++i) { + QVERIFY_SQL(q, next()); + QCOMPARE(q.value(0).toInt() , i); + } + db.close(); + QFile::remove(dbPath); +} + +QTEST_MAIN(tst_QVfsSql) +#include "tst_qvfssql.moc" |