summaryrefslogtreecommitdiffstats
path: root/src/plugins/sqldrivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/sqldrivers')
-rw-r--r--src/plugins/sqldrivers/.cmake.conf2
-rw-r--r--src/plugins/sqldrivers/.prev_configure.cmake77
-rw-r--r--src/plugins/sqldrivers/CMakeLists.txt30
-rw-r--r--src/plugins/sqldrivers/configure.cmake10
-rw-r--r--src/plugins/sqldrivers/configure.json201
-rw-r--r--src/plugins/sqldrivers/db2/CMakeLists.txt10
-rw-r--r--src/plugins/sqldrivers/db2/main.cpp44
-rw-r--r--src/plugins/sqldrivers/db2/qsql_db2.cpp246
-rw-r--r--src/plugins/sqldrivers/db2/qsql_db2_p.h40
-rw-r--r--src/plugins/sqldrivers/ibase/CMakeLists.txt8
-rw-r--r--src/plugins/sqldrivers/ibase/main.cpp44
-rw-r--r--src/plugins/sqldrivers/ibase/qsql_ibase.cpp457
-rw-r--r--src/plugins/sqldrivers/ibase/qsql_ibase_p.h40
-rw-r--r--src/plugins/sqldrivers/mimer/CMakeLists.txt24
-rw-r--r--src/plugins/sqldrivers/mimer/README6
-rw-r--r--src/plugins/sqldrivers/mimer/main.cpp33
-rw-r--r--src/plugins/sqldrivers/mimer/mimer.json5
-rw-r--r--src/plugins/sqldrivers/mimer/qsql_mimer.cpp1607
-rw-r--r--src/plugins/sqldrivers/mimer/qsql_mimer.h50
-rw-r--r--src/plugins/sqldrivers/mysql/CMakeLists.txt9
-rw-r--r--src/plugins/sqldrivers/mysql/main.cpp45
-rw-r--r--src/plugins/sqldrivers/mysql/qsql_mysql.cpp801
-rw-r--r--src/plugins/sqldrivers/mysql/qsql_mysql_p.h40
-rw-r--r--src/plugins/sqldrivers/oci/CMakeLists.txt10
-rw-r--r--src/plugins/sqldrivers/oci/main.cpp46
-rw-r--r--src/plugins/sqldrivers/oci/qsql_oci.cpp389
-rw-r--r--src/plugins/sqldrivers/oci/qsql_oci_p.h43
-rw-r--r--src/plugins/sqldrivers/odbc/CMakeLists.txt11
-rw-r--r--src/plugins/sqldrivers/odbc/main.cpp44
-rw-r--r--src/plugins/sqldrivers/odbc/qsql_odbc.cpp1379
-rw-r--r--src/plugins/sqldrivers/odbc/qsql_odbc_p.h40
-rw-r--r--src/plugins/sqldrivers/psql/CMakeLists.txt11
-rw-r--r--src/plugins/sqldrivers/psql/main.cpp44
-rw-r--r--src/plugins/sqldrivers/psql/qsql_psql.cpp283
-rw-r--r--src/plugins/sqldrivers/psql/qsql_psql_p.h40
-rw-r--r--src/plugins/sqldrivers/qt_cmdline.cmake8
-rw-r--r--src/plugins/sqldrivers/sqlite/CMakeLists.txt35
-rw-r--r--src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp357
-rw-r--r--src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h47
-rw-r--r--src/plugins/sqldrivers/sqlite/qsql_sqlite_vfs.cpp258
-rw-r--r--src/plugins/sqldrivers/sqlite/qsql_sqlite_vfs_p.h21
-rw-r--r--src/plugins/sqldrivers/sqlite/smain.cpp49
42 files changed, 4157 insertions, 2787 deletions
diff --git a/src/plugins/sqldrivers/.cmake.conf b/src/plugins/sqldrivers/.cmake.conf
index 213991f589..10bc1fd407 100644
--- a/src/plugins/sqldrivers/.cmake.conf
+++ b/src/plugins/sqldrivers/.cmake.conf
@@ -1 +1 @@
-set(QT_REPO_MODULE_VERSION "6.1.0")
+set(QT_REPO_MODULE_VERSION "6.8.0")
diff --git a/src/plugins/sqldrivers/.prev_configure.cmake b/src/plugins/sqldrivers/.prev_configure.cmake
deleted file mode 100644
index d742018b9f..0000000000
--- a/src/plugins/sqldrivers/.prev_configure.cmake
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-#### Inputs
-
-# input sqlite
-set(INPUT_sqlite "undefined" CACHE STRING "")
-set_property(CACHE INPUT_sqlite PROPERTY STRINGS undefined qt system)
-
-
-
-#### Libraries
-
-qt_find_package(DB2 PROVIDED_TARGETS DB2::DB2 MODULE_NAME sqldrivers QMAKE_LIB db2)
-qt_find_package(MySQL PROVIDED_TARGETS MySQL::MySQL MODULE_NAME sqldrivers QMAKE_LIB mysql)
-qt_find_package(PostgreSQL PROVIDED_TARGETS PostgreSQL::PostgreSQL MODULE_NAME sqldrivers QMAKE_LIB psql)
-qt_find_package(Oracle PROVIDED_TARGETS Oracle::OCI MODULE_NAME sqldrivers QMAKE_LIB oci)
-qt_find_package(ODBC PROVIDED_TARGETS ODBC::ODBC MODULE_NAME sqldrivers QMAKE_LIB odbc)
-qt_find_package(SQLite3 PROVIDED_TARGETS SQLite::SQLite3 MODULE_NAME sqldrivers QMAKE_LIB sqlite3)
-if(NOT WIN32 AND QT_FEATURE_system_zlib)
- qt_add_qmake_lib_dependency(sqlite3 zlib)
-endif()
-
-
-#### Tests
-
-
-
-#### Features
-
-qt_feature("sql-db2" PRIVATE
- LABEL "DB2 (IBM)"
- CONDITION DB2_FOUND
-)
-qt_feature("sql-ibase" PRIVATE
- LABEL "InterBase"
- CONDITION libs.ibase OR FIXME
-)
-qt_feature("sql-mysql" PRIVATE
- LABEL "MySql"
- CONDITION MySQL_FOUND
-)
-qt_feature("sql-oci" PRIVATE
- LABEL "OCI (Oracle)"
- CONDITION Oracle_FOUND
-)
-qt_feature("sql-odbc" PRIVATE
- LABEL "ODBC"
- CONDITION QT_FEATURE_datestring AND ODBC_FOUND
-)
-qt_feature("sql-psql" PRIVATE
- LABEL "PostgreSQL"
- CONDITION PostgreSQL_FOUND
-)
-qt_feature("sql-sqlite" PRIVATE
- LABEL "SQLite"
- CONDITION QT_FEATURE_datestring
-)
-qt_feature("system-sqlite" PRIVATE
- LABEL " Using system provided SQLite"
- AUTODETECT OFF
- CONDITION QT_FEATURE_sql_sqlite AND SQLite3_FOUND
-)
-qt_configure_add_summary_section(NAME "Qt Sql Drivers")
-qt_configure_add_summary_entry(ARGS "sql-db2")
-qt_configure_add_summary_entry(ARGS "sql-ibase")
-qt_configure_add_summary_entry(ARGS "sql-mysql")
-qt_configure_add_summary_entry(ARGS "sql-oci")
-qt_configure_add_summary_entry(ARGS "sql-odbc")
-qt_configure_add_summary_entry(ARGS "sql-psql")
-qt_configure_add_summary_entry(ARGS "sql-sqlite")
-qt_configure_add_summary_entry(ARGS "system-sqlite")
-qt_configure_end_summary_section() # end of "Qt Sql Drivers" section
-qt_configure_add_report_entry(
- TYPE WARNING
- MESSAGE "Qt does not support compiling the Oracle database driver with MinGW, due to lack of such support from Oracle. Consider disabling the Oracle driver, as the current build will most likely fail."
- CONDITION WIN32 AND NOT MSVC AND QT_FEATURE_sql_oci
-)
diff --git a/src/plugins/sqldrivers/CMakeLists.txt b/src/plugins/sqldrivers/CMakeLists.txt
index f333dfd413..43abb00ad1 100644
--- a/src/plugins/sqldrivers/CMakeLists.txt
+++ b/src/plugins/sqldrivers/CMakeLists.txt
@@ -1,9 +1,17 @@
-# Generated from sqldrivers.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
-# special case begin
cmake_minimum_required(VERSION 3.16)
if (NOT CMAKE_PROJECT_NAME STREQUAL "QtBase" AND NOT CMAKE_PROJECT_NAME STREQUAL "Qt")
include(.cmake.conf)
+ # Store initial build type (if any is specified) to be read by
+ # qt_internal_set_cmake_build_type().
+ # See qt_internal_set_cmake_build_type() for details.
+ if(DEFINED CACHE{CMAKE_BUILD_TYPE})
+ set(__qt_internal_standalone_project_cmake_build_type_before_project_call
+ "${CMAKE_BUILD_TYPE}")
+ endif()
+
project(QSQLiteDriverPlugins
VERSION "${QT_REPO_MODULE_VERSION}"
DESCRIPTION "Qt6 SQL driver plugins"
@@ -16,9 +24,6 @@ if (NOT CMAKE_PROJECT_NAME STREQUAL "QtBase" AND NOT CMAKE_PROJECT_NAME STREQUAL
Sql
)
qt_prepare_standalone_project()
-
- # needed by qt_internal_add_plugin
- add_custom_target(qt_plugins)
else()
qt_internal_upgrade_cmake_policies()
endif()
@@ -39,7 +44,7 @@ include(configure.cmake)
qt_feature_module_end(NO_MODULE)
-if(QT_FEATURE_sql_psql)
+if(QT_FEATURE_sql_psql AND QT_FEATURE_regularexpression)
add_subdirectory(psql)
endif()
@@ -51,10 +56,6 @@ if(QT_FEATURE_sql_odbc)
add_subdirectory(odbc)
endif()
-if(QT_FEATURE_sql_tds)
-# TODO add_subdirectory(tds)
-endif()
-
if(QT_FEATURE_sql_oci)
add_subdirectory(oci)
endif()
@@ -67,15 +68,14 @@ if(QT_FEATURE_sql_sqlite)
add_subdirectory(sqlite)
endif()
-if(QT_FEATURE_sql_sqlite2)
-# TODO add_subdirectory(sqlite2)
-endif()
-
if(QT_FEATURE_sql_ibase)
add_subdirectory(ibase)
endif()
+if(QT_FEATURE_sql_mimer)
+ add_subdirectory(mimer)
+endif()
+
if(NOT CMAKE_PROJECT_NAME STREQUAL "QtBase" AND NOT CMAKE_PROJECT_NAME STREQUAL "Qt")
qt_print_feature_summary()
endif()
-# special case end
diff --git a/src/plugins/sqldrivers/configure.cmake b/src/plugins/sqldrivers/configure.cmake
index 25384f3c52..534ac020d8 100644
--- a/src/plugins/sqldrivers/configure.cmake
+++ b/src/plugins/sqldrivers/configure.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#### Inputs
@@ -17,6 +20,7 @@ qt_find_package(Oracle PROVIDED_TARGETS Oracle::OCI MODULE_NAME sqldrivers QMAKE
qt_find_package(ODBC PROVIDED_TARGETS ODBC::ODBC MODULE_NAME sqldrivers QMAKE_LIB odbc)
qt_find_package(SQLite3 PROVIDED_TARGETS SQLite::SQLite3 MODULE_NAME sqldrivers QMAKE_LIB sqlite3)
qt_find_package(Interbase PROVIDED_TARGETS Interbase::Interbase MODULE_NAME sqldrivers QMAKE_LIB ibase) # special case
+qt_find_package(Mimer PROVIDED_TARGETS MimerSQL::MimerSQL MODULE_NAME sqldrivers QMAKE_LIB mimer)
if(NOT WIN32 AND QT_FEATURE_system_zlib)
qt_add_qmake_lib_dependency(sqlite3 zlib)
endif()
@@ -61,6 +65,11 @@ qt_feature("system-sqlite" PRIVATE
AUTODETECT OFF
CONDITION QT_FEATURE_sql_sqlite AND SQLite3_FOUND
)
+qt_feature("sql-mimer" PRIVATE
+ LABEL "Mimer"
+ CONDITION Mimer_FOUND
+)
+
qt_configure_add_summary_section(NAME "Qt Sql Drivers")
qt_configure_add_summary_entry(ARGS "sql-db2")
qt_configure_add_summary_entry(ARGS "sql-ibase")
@@ -70,6 +79,7 @@ qt_configure_add_summary_entry(ARGS "sql-odbc")
qt_configure_add_summary_entry(ARGS "sql-psql")
qt_configure_add_summary_entry(ARGS "sql-sqlite")
qt_configure_add_summary_entry(ARGS "system-sqlite")
+qt_configure_add_summary_entry(ARGS "sql-mimer")
qt_configure_end_summary_section() # end of "Qt Sql Drivers" section
qt_configure_add_report_entry(
TYPE WARNING
diff --git a/src/plugins/sqldrivers/configure.json b/src/plugins/sqldrivers/configure.json
deleted file mode 100644
index f738e58a00..0000000000
--- a/src/plugins/sqldrivers/configure.json
+++ /dev/null
@@ -1,201 +0,0 @@
-{
- "module": "sqldrivers",
- "depends": [
- "core"
- ],
- "testDir": "../../../config.tests",
-
- "commandline": {
- "assignments": {
- "MYSQL_PATH": "mysql.prefix"
- },
- "options": {
- "mysql_config": "string",
- "psql_config": "string",
- "sqlite": { "type": "enum", "name": "system-sqlite", "values": { "qt": "no", "system": "yes" } },
- "sql-db2": "boolean",
- "sql-ibase": "boolean",
- "sql-mysql": "boolean",
- "sql-oci": "boolean",
- "sql-odbc": "boolean",
- "sql-psql": "boolean",
- "sql-sqlite": "boolean",
- "plugin-sql-db2": { "type": "void", "name": "sql-db2" },
- "plugin-sql-ibase": { "type": "void", "name": "sql-ibase" },
- "plugin-sql-mysql": { "type": "void", "name": "sql-mysql" },
- "plugin-sql-oci": { "type": "void", "name": "sql-oci" },
- "plugin-sql-odbc": { "type": "void", "name": "sql-odbc" },
- "plugin-sql-psql": { "type": "void", "name": "sql-psql" },
- "plugin-sql-sqlite": { "type": "void", "name": "sql-sqlite" }
- }
- },
-
- "libraries": {
- "db2": {
- "label": "DB2 (IBM)",
- "test": {},
- "headers": [ "sqlcli.h", "sqlcli1.h" ],
- "sources": [
- { "libs": "-ldb2cli", "condition": "config.win32" },
- { "libs": "-ldb2", "condition": "!config.win32" }
- ]
- },
- "ibase": {
- "label": "InterBase",
- "test": {},
- "headers": "ibase.h",
- "sources": [
- { "libs": "-lgds32_ms", "condition": "config.win32" },
- { "libs": "-lgds", "condition": "!config.win32" }
- ]
- },
- "mysql": {
- "label": "MySQL",
- "test": {
- "head": [
- "#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)",
- "# include <windows.h>",
- "#endif"
- ],
- "main": "mysql_get_client_version();"
- },
- "headers": "mysql.h",
- "sources": [
- { "type": "mysqlConfig", "query": "--libs_r", "cleanlibs": true },
- { "type": "mysqlConfig", "query": "--libs", "cleanlibs": true },
- { "type": "mysqlConfig", "query": "--libs_r", "cleanlibs": false },
- { "type": "mysqlConfig", "query": "--libs", "cleanlibs": false },
- { "libs": "-lmysqlclient_r", "condition": "!config.win32" },
- { "libs": "-llibmariadb", "condition": "config.win32" },
- { "libs": "-llibmysql", "condition": "config.win32" },
- { "libs": "-lmariadb", "condition": "!config.win32" },
- { "libs": "-lmysqlclient", "condition": "!config.win32" }
- ]
- },
- "psql": {
- "label": "PostgreSQL",
- "test": {
- "main": [
- "PQescapeBytea(0, 0, 0);",
- "PQunescapeBytea(0, 0);"
- ]
- },
- "headers": "libpq-fe.h",
- "sources": [
- { "type": "pkgConfig", "args": "libpq" },
- { "type": "psqlConfig" },
- { "type": "psqlEnv", "libs": "-llibpq -lws2_32 -ladvapi32", "condition": "config.win32" },
- { "type": "psqlEnv", "libs": "-lpq", "condition": "!config.win32" }
- ]
- },
- "oci": {
- "label": "OCI (Oracle)",
- "test": {},
- "headers": "oci.h",
- "sources": [
- { "libs": "-loci", "condition": "config.win32" },
- { "libs": "-lclntsh", "condition": "!config.win32" }
- ]
- },
- "odbc": {
- "label": "ODBC",
- "test": {
- "head": [
- "#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)",
- "# include <windows.h>",
- "#endif"
- ],
- "main": [
- "SQLHANDLE env;",
- "SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);"
- ]
- },
- "headers": [ "sql.h", "sqlext.h" ],
- "sources": [
- { "libs": "-lodbc32", "condition": "config.win32" },
- { "libs": "-liodbc", "condition": "config.darwin" },
- { "libs": "-lodbc", "condition": "!config.win32 && !config.darwin" }
- ]
- },
- "sqlite3": {
- "label": "SQLite (version 3)",
- "export": "sqlite",
- "test": {
- "main": "sqlite3_open_v2(0, 0, 0, 0);"
- },
- "headers": "sqlite3.h",
- "sources": [
- { "type": "pkgConfig", "args": "sqlite3" },
- "-lsqlite3"
- ],
- "use": [
- { "lib": "zlib", "condition": "!config.win32 && features.system-zlib" }
- ]
- }
- },
-
- "tests": {
- },
-
- "features": {
- "sql-db2": {
- "label": "DB2 (IBM)",
- "condition": "libs.db2",
- "output": [ "privateFeature" ]
- },
- "sql-ibase": {
- "label": "InterBase",
- "condition": "libs.ibase",
- "output": [ "privateFeature" ]
- },
- "sql-mysql": {
- "label": "MySql",
- "condition": "libs.mysql",
- "output": [ "privateFeature" ]
- },
- "sql-oci": {
- "label": "OCI (Oracle)",
- "condition": "libs.oci",
- "output": [ "privateFeature" ]
- },
- "sql-odbc": {
- "label": "ODBC",
- "condition": "features.datestring && libs.odbc",
- "output": [ "privateFeature" ]
- },
- "sql-psql": {
- "label": "PostgreSQL",
- "condition": "libs.psql",
- "output": [ "privateFeature" ]
- },
- "sql-sqlite": {
- "label": "SQLite",
- "condition": "features.datestring",
- "output": [ "privateFeature" ]
- },
- "system-sqlite": {
- "label": " Using system provided SQLite",
- "autoDetect": false,
- "condition": "features.sql-sqlite && libs.sqlite3",
- "output": [ "privateFeature" ]
- }
- },
-
- "report": [
- {
- "type": "warning",
- "condition": "config.win32 && !config.msvc && features.sql-oci",
- "message": "Qt does not support compiling the Oracle database driver with MinGW, due to lack of such support from Oracle. Consider disabling the Oracle driver, as the current build will most likely fail."
- }
- ],
-
- "summary": [
- {
- "section": "Qt Sql Drivers",
- "entries": [
- "sql-db2", "sql-ibase", "sql-mysql", "sql-oci", "sql-odbc", "sql-psql",
- "sql-sqlite", "system-sqlite"
- ]
- }
- ]
-}
diff --git a/src/plugins/sqldrivers/db2/CMakeLists.txt b/src/plugins/sqldrivers/db2/CMakeLists.txt
index 6d662434ae..119a5b53c9 100644
--- a/src/plugins/sqldrivers/db2/CMakeLists.txt
+++ b/src/plugins/sqldrivers/db2/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from db2.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## QDB2DriverPlugin Plugin:
@@ -6,7 +7,7 @@
qt_internal_add_plugin(QDB2DriverPlugin
OUTPUT_NAME qsqldb2
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_db2.cpp qsql_db2_p.h
@@ -20,9 +21,6 @@ qt_internal_add_plugin(QDB2DriverPlugin
Qt::SqlPrivate
)
-#### Keys ignored in scope 1:.:.:db2.pro:<TRUE>:
-# OTHER_FILES = "db2.json"
-
## Scopes:
#####################################################################
@@ -30,3 +28,5 @@ qt_internal_extend_target(QDB2DriverPlugin CONDITION (TEST_architecture_arch STR
DEFINES
ODBC64
)
+
+qt_internal_force_macos_intel_arch(QDB2DriverPlugin)
diff --git a/src/plugins/sqldrivers/db2/main.cpp b/src/plugins/sqldrivers/db2/main.cpp
index 97338b8eef..b5aa5a6492 100644
--- a/src/plugins/sqldrivers/db2/main.cpp
+++ b/src/plugins/sqldrivers/db2/main.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qsqldriverplugin.h>
#include <qstringlist.h>
@@ -43,6 +7,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QDB2DriverPlugin : public QSqlDriverPlugin
{
Q_OBJECT
@@ -61,7 +27,7 @@ QDB2DriverPlugin::QDB2DriverPlugin()
QSqlDriver* QDB2DriverPlugin::create(const QString &name)
{
- if (name == QLatin1String("QDB2")) {
+ if (name == "QDB2"_L1) {
QDB2Driver* driver = new QDB2Driver();
return driver;
}
diff --git a/src/plugins/sqldrivers/db2/qsql_db2.cpp b/src/plugins/sqldrivers/db2/qsql_db2.cpp
index e819d0ea86..9b6a06c378 100644
--- a/src/plugins/sqldrivers/db2/qsql_db2.cpp
+++ b/src/plugins/sqldrivers/db2/qsql_db2.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsql_db2_p.h"
#include <qcoreapplication.h>
@@ -50,6 +14,7 @@
#include <QDebug>
#include <QtSql/private/qsqldriver_p.h>
#include <QtSql/private/qsqlresult_p.h>
+#include "private/qtools_p.h"
#if defined(Q_CC_BOR)
// DB2's sqlsystm.h (included through sqlcli1.h) defines the SQL_BIGINT_TYPE
@@ -65,6 +30,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static const int COLNAMESIZE = 255;
// Based on what is mentioned in the documentation here:
// https://www.ibm.com/support/knowledgecenter/en/SSEPEK_10.0.0/sqlref/src/tpc/db2z_limits.html
@@ -77,10 +44,13 @@ class QDB2DriverPrivate : public QSqlDriverPrivate
Q_DECLARE_PUBLIC(QDB2Driver)
public:
- QDB2DriverPrivate() : QSqlDriverPrivate(), hEnv(0), hDbc(0) { dbmsType = QSqlDriver::DB2; }
- SQLHANDLE hEnv;
- SQLHANDLE hDbc;
+ QDB2DriverPrivate() : QSqlDriverPrivate(QSqlDriver::DB2) {}
+ SQLHANDLE hEnv = 0;
+ SQLHANDLE hDbc = 0;
QString user;
+
+ void qSplitTableQualifier(const QString &qualifier, QString &catalog,
+ QString &schema, QString &table) const;
};
class QDB2ResultPrivate;
@@ -188,7 +158,7 @@ static QString qDB2Warn(const QDB2DriverPrivate* d, QStringList *errorCodes = nu
errorCode = 0;
}
if (!error.isEmpty())
- error += QLatin1Char(' ');
+ error += u' ';
error += qWarnDB2Handle(SQL_HANDLE_DBC, d->hDbc, &errorCode);
if (errorCodes && errorCode != 0)
*errorCodes << QString::number(errorCode);
@@ -204,14 +174,14 @@ static QString qDB2Warn(const QDB2ResultPrivate* d, QStringList *errorCodes = nu
errorCode = 0;
}
if (!error.isEmpty())
- error += QLatin1Char(' ');
+ error += u' ';
error += qWarnDB2Handle(SQL_HANDLE_DBC, d->drv_d_func()->hDbc, &errorCode);
if (errorCodes && errorCode != 0) {
*errorCodes << QString::number(errorCode);
errorCode = 0;
}
if (!error.isEmpty())
- error += QLatin1Char(' ');
+ error += u' ';
error += qWarnDB2Handle(SQL_HANDLE_STMT, d->hStmt, &errorCode);
if (errorCodes && errorCode != 0)
*errorCodes << QString::number(errorCode);
@@ -236,7 +206,7 @@ static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
QStringList errorCodes;
const QString error = qDB2Warn(p, &errorCodes);
return QSqlError(QStringLiteral("QDB2: ") + err, error, type,
- errorCodes.join(QLatin1Char(';')));
+ errorCodes.join(u';'));
}
static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
@@ -245,7 +215,7 @@ static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
QStringList errorCodes;
const QString error = qDB2Warn(p, &errorCodes);
return QSqlError(QStringLiteral("QDB2: ") + err, error, type,
- errorCodes.join(QLatin1Char(';')));
+ errorCodes.join(u';'));
}
static QMetaType qDecodeDB2Type(SQLSMALLINT sqltype)
@@ -335,7 +305,6 @@ static QSqlField qMakeFieldInfo(const QDB2ResultPrivate* d, int i)
// else required is unknown
f.setLength(colSize == 0 ? -1 : int(colSize));
f.setPrecision(colScale == 0 ? -1 : int(colScale));
- f.setSqlType(int(colType));
SQLTCHAR tableName[TABLENAMESIZE];
SQLSMALLINT tableNameLen;
r = SQLColAttribute(d->hStmt, i + 1, SQL_DESC_BASE_TABLE_NAME, tableName,
@@ -495,34 +464,27 @@ static QByteArray qGetBinaryData(SQLHANDLE hStmt, int column, SQLLEN& lengthIndi
return fieldVal;
}
-static void qSplitTableQualifier(const QString & qualifier, QString * catalog,
- QString * schema, QString * table)
+void QDB2DriverPrivate::qSplitTableQualifier(const QString &qualifier, QString &catalog,
+ QString &schema, QString &table) const
{
- if (!catalog || !schema || !table)
- return;
- QStringList l = qualifier.split(QLatin1Char('.'));
- if (l.count() > 3)
- return; // can't possibly be a valid table qualifier
- int i = 0, n = l.count();
- if (n == 1) {
- *table = qualifier;
- } else {
- for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
- if (n == 3) {
- if (i == 0)
- *catalog = *it;
- else if (i == 1)
- *schema = *it;
- else if (i == 2)
- *table = *it;
- } else if (n == 2) {
- if (i == 0)
- *schema = *it;
- else if (i == 1)
- *table = *it;
- }
- i++;
- }
+ const QList<QStringView> l = QStringView(qualifier).split(u'.');
+ switch (l.count()) {
+ case 1:
+ table = qualifier;
+ break;
+ case 2:
+ schema = l.at(0).toString();
+ table = l.at(1).toString();
+ break;
+ case 3:
+ catalog = l.at(0).toString();
+ schema = l.at(1).toString();
+ table = l.at(2).toString();
+ break;
+ default:
+ qSqlWarning(QString::fromLatin1("QODBCDriver::splitTableQualifier: Unable to split table qualifier '%1'")
+ .arg(qualifier), this);
+ break;
}
}
@@ -542,7 +504,6 @@ static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt)
// else we don't know.
f.setLength(qGetIntData(hStmt, 6, isNull)); // column size
f.setPrecision(qGetIntData(hStmt, 8, isNull)); // precision
- f.setSqlType(type);
return f;
}
@@ -554,13 +515,13 @@ static bool qMakeStatement(QDB2ResultPrivate* d, bool forwardOnly, bool setForwa
d->drv_d_func()->hDbc,
&d->hStmt);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QDB2Result::reset: Unable to allocate statement handle"), d);
+ qSqlWarning("QDB2Result::reset: Unable to allocate statement handle"_L1, d);
return false;
}
} else {
r = SQLFreeStmt(d->hStmt, SQL_CLOSE);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QDB2Result::reset: Unable to close statement handle"), d);
+ qSqlWarning("QDB2Result::reset: Unable to close statement handle"_L1, d);
return false;
}
}
@@ -581,8 +542,8 @@ static bool qMakeStatement(QDB2ResultPrivate* d, bool forwardOnly, bool setForwa
}
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
qSqlWarning(QString::fromLatin1("QDB2Result::reset: Unable to set %1 attribute.").arg(
- forwardOnly ? QLatin1String("SQL_CURSOR_FORWARD_ONLY")
- : QLatin1String("SQL_CURSOR_STATIC")), d);
+ forwardOnly ? "SQL_CURSOR_FORWARD_ONLY"_L1
+ : "SQL_CURSOR_STATIC"_L1), d);
return false;
}
return true;
@@ -607,8 +568,7 @@ QDB2Result::~QDB2Result()
if (d->hStmt) {
SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
- + QString::number(r), d);
+ qSqlWarning("QDB2Driver: Unable to free statement handle "_L1 + QString::number(r), d);
}
}
@@ -697,7 +657,7 @@ bool QDB2Result::exec()
for (i = 0; i < values.count(); ++i) {
// bind parameters - only positional binding allowed
SQLLEN *ind = &indicators[i];
- if (values.at(i).isNull())
+ if (QSqlResultPrivate::isVariantNull(values.at(i)))
*ind = SQL_NULL_DATA;
if (bindValueType(i) & QSql::Out)
values[i].detach();
@@ -808,11 +768,11 @@ bool QDB2Result::exec()
break; }
case QMetaType::QString:
{
- QString str(values.at(i).toString());
+ const QString str(values.at(i).toString());
if (*ind != SQL_NULL_DATA)
*ind = str.length() * sizeof(QChar);
if (bindValueType(i) & QSql::Out) {
- QByteArray ba((char*)str.utf16(), str.capacity() * sizeof(QChar));
+ QByteArray ba((char *)str.data(), str.capacity() * sizeof(QChar));
r = SQLBindParameter(d->hStmt,
i + 1,
qParamType[bindValueType(i) & 3],
@@ -910,7 +870,7 @@ bool QDB2Result::exec()
break; }
case QMetaType::Int:
case QMetaType::Double:
- case QMetaType::ByteArray:
+ case QMetaType::QByteArray:
break;
case QMetaType::QString:
if (bindValueType(i) & QSql::Out)
@@ -1159,7 +1119,7 @@ int QDB2Result::numRowsAffected()
if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
return affectedRowCount;
else
- qSqlWarning(QLatin1String("QDB2Result::numRowsAffected: Unable to count affected rows"), d);
+ qSqlWarning("QDB2Result::numRowsAffected: Unable to count affected rows"_L1, d);
return -1;
}
@@ -1254,7 +1214,7 @@ bool QDB2Driver::open(const QString& db, const QString& user, const QString& pas
SQL_NULL_HANDLE,
&d->hEnv);
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
- qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate environment"), d);
+ qSqlWarning("QDB2Driver::open: Unable to allocate environment"_L1, d);
setOpenError(true);
return false;
}
@@ -1263,18 +1223,18 @@ bool QDB2Driver::open(const QString& db, const QString& user, const QString& pas
d->hEnv,
&d->hDbc);
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
- qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate connection"), d);
+ qSqlWarning("QDB2Driver::open: Unable to allocate connection"_L1, d);
setOpenError(true);
return false;
}
QString protocol;
// Set connection attributes
- const QStringList opts(connOpts.split(QLatin1Char(';'), Qt::SkipEmptyParts));
+ const QStringList opts(connOpts.split(u';', Qt::SkipEmptyParts));
for (int i = 0; i < opts.count(); ++i) {
const QString tmp(opts.at(i));
int idx;
- if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
+ if ((idx = tmp.indexOf(u'=')) == -1) {
qWarning("QDB2Driver::open: Illegal connect option value '%s'",
tmp.toLocal8Bit().constData());
continue;
@@ -1285,10 +1245,10 @@ bool QDB2Driver::open(const QString& db, const QString& user, const QString& pas
SQLULEN v = 0;
r = SQL_SUCCESS;
- if (opt == QLatin1String("SQL_ATTR_ACCESS_MODE")) {
- if (val == QLatin1String("SQL_MODE_READ_ONLY")) {
+ if (opt == "SQL_ATTR_ACCESS_MODE"_L1) {
+ if (val == "SQL_MODE_READ_ONLY"_L1) {
v = SQL_MODE_READ_ONLY;
- } else if (val == QLatin1String("SQL_MODE_READ_WRITE")) {
+ } else if (val == "SQL_MODE_READ_WRITE"_L1) {
v = SQL_MODE_READ_WRITE;
} else {
qWarning("QDB2Driver::open: Unknown option value '%s'",
@@ -1296,10 +1256,10 @@ bool QDB2Driver::open(const QString& db, const QString& user, const QString& pas
continue;
}
r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_ACCESS_MODE, reinterpret_cast<SQLPOINTER>(v), 0);
- } else if (opt == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
+ } else if (opt == "SQL_ATTR_LOGIN_TIMEOUT"_L1) {
v = val.toUInt();
r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_LOGIN_TIMEOUT, reinterpret_cast<SQLPOINTER>(v), 0);
- } else if (opt.compare(QLatin1String("PROTOCOL"), Qt::CaseInsensitive) == 0) {
+ } else if (opt.compare("PROTOCOL"_L1, Qt::CaseInsensitive) == 0) {
protocol = tmp;
}
else {
@@ -1312,15 +1272,15 @@ bool QDB2Driver::open(const QString& db, const QString& user, const QString& pas
}
if (protocol.isEmpty())
- protocol = QLatin1String("PROTOCOL=TCPIP");
+ protocol = "PROTOCOL=TCPIP"_L1;
if (port < 0 )
port = 50000;
QString connQStr;
- connQStr = protocol + QLatin1String(";DATABASE=") + db + QLatin1String(";HOSTNAME=") + host
- + QLatin1String(";PORT=") + QString::number(port) + QLatin1String(";UID=") + user
- + QLatin1String(";PWD=") + password;
+ connQStr = protocol + ";DATABASE="_L1 + db + ";HOSTNAME="_L1 + host
+ + ";PORT="_L1 + QString::number(port) + ";UID="_L1 + user
+ + ";PWD="_L1 + password;
SQLTCHAR connOut[SQL_MAX_OPTION_STRING_LENGTH];
@@ -1356,18 +1316,18 @@ void QDB2Driver::close()
if (isOpen()) {
r = SQLDisconnect(d->hDbc);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QDB2Driver::close: Unable to disconnect datasource"), d);
+ qSqlWarning("QDB2Driver::close: Unable to disconnect datasource"_L1, d);
}
r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free connection handle"), d);
+ qSqlWarning("QDB2Driver::close: Unable to free connection handle"_L1, d);
d->hDbc = 0;
}
if (d->hEnv) {
r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free environment handle"), d);
+ qSqlWarning("QDB2Driver::close: Unable to free environment handle"_L1, d);
d->hEnv = 0;
}
setOpen(false);
@@ -1388,7 +1348,7 @@ QSqlRecord QDB2Driver::record(const QString& tableName) const
SQLHANDLE hStmt;
QString catalog, schema, table;
- qSplitTableQualifier(tableName, &catalog, &schema, &table);
+ d->qSplitTableQualifier(tableName, catalog, schema, table);
if (schema.isEmpty())
schema = d->user;
@@ -1411,7 +1371,7 @@ QSqlRecord QDB2Driver::record(const QString& tableName) const
d->hDbc,
&hStmt);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QDB2Driver::record: Unable to allocate handle"), d);
+ qSqlWarning("QDB2Driver::record: Unable to allocate handle"_L1, d);
return fil;
}
@@ -1434,7 +1394,7 @@ QSqlRecord QDB2Driver::record(const QString& tableName) const
0);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QDB2Driver::record: Unable to execute column list"), d);
+ qSqlWarning("QDB2Driver::record: Unable to execute column list"_L1, d);
r = SQLFetchScroll(hStmt,
SQL_FETCH_NEXT,
0);
@@ -1449,7 +1409,7 @@ QSqlRecord QDB2Driver::record(const QString& tableName) const
r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
+ qSqlWarning("QDB2Driver: Unable to free statement handle "_L1
+ QString::number(r), d);
return fil;
@@ -1468,7 +1428,7 @@ QStringList QDB2Driver::tables(QSql::TableType type) const
d->hDbc,
&hStmt);
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
- qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to allocate handle"), d);
+ qSqlWarning("QDB2Driver::tables: Unable to allocate handle"_L1, d);
return tl;
}
r = SQLSetStmtAttr(hStmt,
@@ -1478,11 +1438,11 @@ QStringList QDB2Driver::tables(QSql::TableType type) const
QString tableType;
if (type & QSql::Tables)
- tableType += QLatin1String("TABLE,");
+ tableType += "TABLE,"_L1;
if (type & QSql::Views)
- tableType += QLatin1String("VIEW,");
+ tableType += "VIEW,"_L1;
if (type & QSql::SystemTables)
- tableType += QLatin1String("SYSTEM TABLE,");
+ tableType += "SYSTEM TABLE,"_L1;
if (tableType.isEmpty())
return tl;
tableType.chop(1);
@@ -1498,7 +1458,7 @@ QStringList QDB2Driver::tables(QSql::TableType type) const
tableType.length());
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
- qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to execute table list"), d);
+ qSqlWarning("QDB2Driver::tables: Unable to execute table list"_L1, d);
r = SQLFetchScroll(hStmt,
SQL_FETCH_NEXT,
0);
@@ -1513,7 +1473,7 @@ QStringList QDB2Driver::tables(QSql::TableType type) const
user = user.toUpper();
if (userVal != user)
- fieldVal = userVal + QLatin1Char('.') + fieldVal;
+ fieldVal = userVal + u'.' + fieldVal;
tl.append(fieldVal);
r = SQLFetchScroll(hStmt,
SQL_FETCH_NEXT,
@@ -1522,7 +1482,7 @@ QStringList QDB2Driver::tables(QSql::TableType type) const
r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to free statement handle ")
+ qSqlWarning("QDB2Driver::tables: Unable to free statement handle "_L1
+ QString::number(r), d);
return tl;
}
@@ -1540,11 +1500,11 @@ QSqlIndex QDB2Driver::primaryIndex(const QString& tablename) const
d->hDbc,
&hStmt);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QDB2Driver::primaryIndex: Unable to list primary key"), d);
+ qSqlWarning("QDB2Driver::primaryIndex: Unable to list primary key"_L1, d);
return index;
}
QString catalog, schema, table;
- qSplitTableQualifier(tablename, &catalog, &schema, &table);
+ d->qSplitTableQualifier(tablename, catalog, schema, table);
if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
catalog = stripDelimiters(catalog, QSqlDriver::TableName);
@@ -1591,7 +1551,7 @@ QSqlIndex QDB2Driver::primaryIndex(const QString& tablename) const
}
r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
if (r!= SQL_SUCCESS)
- qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
+ qSqlWarning("QDB2Driver: Unable to free statement handle "_L1
+ QString::number(r), d);
return index;
}
@@ -1685,7 +1645,7 @@ bool QDB2Driver::setAutoCommit(bool autoCommit)
QString QDB2Driver::formatValue(const QSqlField &field, bool trimStrings) const
{
if (field.isNull())
- return QLatin1String("NULL");
+ return "NULL"_L1;
switch (field.metaType().id()) {
case QMetaType::QDateTime: {
@@ -1694,32 +1654,32 @@ QString QDB2Driver::formatValue(const QSqlField &field, bool trimStrings) const
QDate dt = field.value().toDateTime().date();
QTime tm = field.value().toDateTime().time();
// Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
- return QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-')
- + QString::number(dt.month()) + QLatin1Char('-')
- + QString::number(dt.day()) + QLatin1Char('-')
- + QString::number(tm.hour()) + QLatin1Char('.')
- + QString::number(tm.minute()).rightJustified(2, QLatin1Char('0'), true)
- + QLatin1Char('.')
- + QString::number(tm.second()).rightJustified(2, QLatin1Char('0'), true)
- + QLatin1Char('.')
- + QString::number(tm.msec() * 1000).rightJustified(6, QLatin1Char('0'), true)
- + QLatin1Char('\'');
+ return u'\'' + QString::number(dt.year()) + u'-'
+ + QString::number(dt.month()) + u'-'
+ + QString::number(dt.day()) + u'-'
+ + QString::number(tm.hour()) + u'.'
+ + QString::number(tm.minute()).rightJustified(2, u'0', true)
+ + u'.'
+ + QString::number(tm.second()).rightJustified(2, u'0', true)
+ + u'.'
+ + QString::number(tm.msec() * 1000).rightJustified(6, u'0', true)
+ + u'\'';
} else {
- return QLatin1String("NULL");
+ return "NULL"_L1;
}
}
case QMetaType::QByteArray: {
- QByteArray ba = field.value().toByteArray();
- QString res;
- res += QLatin1String("BLOB(X'");
- static const char hexchars[] = "0123456789abcdef";
- for (int i = 0; i < ba.size(); ++i) {
- uchar s = (uchar) ba[i];
- res += QLatin1Char(hexchars[s >> 4]);
- res += QLatin1Char(hexchars[s & 0x0f]);
+ const QByteArray ba = field.value().toByteArray();
+ QString r;
+ r.reserve(ba.size() * 2 + 9);
+ r += "BLOB(X'"_L1;
+ for (const char c : ba) {
+ const uchar s = uchar(c);
+ r += QLatin1Char(QtMiscUtils::toHexLower(s >> 4));
+ r += QLatin1Char(QtMiscUtils::toHexLower(s & 0x0f));
}
- res += QLatin1String("')");
- return res;
+ r += "')"_L1;
+ return r;
}
default:
return QSqlDriver::formatValue(field, trimStrings);
@@ -1735,12 +1695,14 @@ QVariant QDB2Driver::handle() const
QString QDB2Driver::escapeIdentifier(const QString &identifier, IdentifierType) const
{
QString res = identifier;
- if (!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
- res.replace(QLatin1Char('"'), QLatin1String("\"\""));
- res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
- res.replace(QLatin1Char('.'), QLatin1String("\".\""));
+ if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"') ) {
+ res.replace(u'"', "\"\""_L1);
+ res.replace(u'.', "\".\""_L1);
+ res = u'"' + res + u'"';
}
return res;
}
QT_END_NAMESPACE
+
+#include "moc_qsql_db2_p.cpp"
diff --git a/src/plugins/sqldrivers/db2/qsql_db2_p.h b/src/plugins/sqldrivers/db2/qsql_db2_p.h
index abe65c4208..9e580352b2 100644
--- a/src/plugins/sqldrivers/db2/qsql_db2_p.h
+++ b/src/plugins/sqldrivers/db2/qsql_db2_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSQL_DB2_H
#define QSQL_DB2_H
diff --git a/src/plugins/sqldrivers/ibase/CMakeLists.txt b/src/plugins/sqldrivers/ibase/CMakeLists.txt
index 5f200597f5..b8f2d2561f 100644
--- a/src/plugins/sqldrivers/ibase/CMakeLists.txt
+++ b/src/plugins/sqldrivers/ibase/CMakeLists.txt
@@ -1,14 +1,20 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_internal_add_plugin(QIBaseDriverPlugin
OUTPUT_NAME qsqlibase
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_ibase.cpp qsql_ibase_p.h
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
+ QT_NO_CONTEXTLESS_CONNECT
LIBRARIES
Interbase::Interbase
Qt::Core
Qt::CorePrivate
Qt::SqlPrivate)
+
+qt_internal_force_macos_intel_arch(QIBaseDriverPlugin)
diff --git a/src/plugins/sqldrivers/ibase/main.cpp b/src/plugins/sqldrivers/ibase/main.cpp
index 9f34899d8c..24e9dc0864 100644
--- a/src/plugins/sqldrivers/ibase/main.cpp
+++ b/src/plugins/sqldrivers/ibase/main.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qsqldriverplugin.h>
#include <qstringlist.h>
@@ -43,6 +7,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QIBaseDriverPlugin : public QSqlDriverPlugin
{
Q_OBJECT
@@ -61,7 +27,7 @@ QIBaseDriverPlugin::QIBaseDriverPlugin()
QSqlDriver* QIBaseDriverPlugin::create(const QString &name)
{
- if (name == QLatin1String("QIBASE")) {
+ if (name == "QIBASE"_L1) {
QIBaseDriver* driver = new QIBaseDriver();
return driver;
}
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
index 0f2cfd73bf..bf41362018 100644
--- a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
+++ b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
@@ -1,63 +1,32 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsql_ibase_p.h"
-#include <qcoreapplication.h>
-#include <qdatetime.h>
-#include <qdeadlinetimer.h>
-#include <qdebug.h>
-#include <qlist.h>
-#include <qmutex.h>
-#include <qsqlerror.h>
-#include <qsqlfield.h>
-#include <qsqlindex.h>
-#include <qsqlquery.h>
-#include <qvariant.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qtimezone.h>
+#include <QtCore/qdeadlinetimer.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtSql/qsqlerror.h>
+#include <QtSql/qsqlfield.h>
+#include <QtSql/qsqlindex.h>
+#include <QtSql/qsqlquery.h>
#include <QtSql/private/qsqlcachedresult_p.h>
#include <QtSql/private/qsqldriver_p.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
-#include <QVarLengthArray>
+#include <mutex>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
#define FBVERSION SQL_DIALECT_V6
#ifndef SQLDA_CURRENT_VERSION
@@ -69,7 +38,15 @@ QT_BEGIN_NAMESPACE
#define blr_boolean_dtype blr_bool
#endif
-enum { QIBaseChunkSize = SHRT_MAX / 2 };
+constexpr qsizetype QIBaseChunkSize = SHRT_MAX / 2;
+
+#if (FB_API_VER >= 40)
+typedef QMap<quint16, QByteArray> QFbTzIdToIanaIdMap;
+typedef QMap<QByteArray, quint16> QIanaIdToFbTzIdMap;
+Q_GLOBAL_STATIC(QFbTzIdToIanaIdMap, qFbTzIdToIanaIdMap)
+Q_GLOBAL_STATIC(QIanaIdToFbTzIdMap, qIanaIdToFbTzIdMap)
+std::once_flag initTZMappingFlag;
+#endif
static bool getIBaseError(QString& msg, const ISC_STATUS* status, ISC_LONG &sqlcode)
{
@@ -81,7 +58,7 @@ static bool getIBaseError(QString& msg, const ISC_STATUS* status, ISC_LONG &sqlc
char buf[512];
while(fb_interpret(buf, 512, &status)) {
if (!msg.isEmpty())
- msg += QLatin1String(" - ");
+ msg += " - "_L1;
msg += QString::fromUtf8(buf);
}
return true;
@@ -118,6 +95,9 @@ static void initDA(XSQLDA *sqlda)
case SQL_FLOAT:
case SQL_DOUBLE:
case SQL_TIMESTAMP:
+#if (FB_API_VER >= 40)
+ case SQL_TIMESTAMP_TZ:
+#endif
case SQL_TYPE_TIME:
case SQL_TYPE_DATE:
case SQL_TEXT:
@@ -155,7 +135,7 @@ static void delDA(XSQLDA *&sqlda)
delete [] sqlda->sqlvar[i].sqldata;
}
free(sqlda);
- sqlda = 0;
+ sqlda = nullptr;
}
static QMetaType::Type qIBaseTypeName(int iType, bool hasScale)
@@ -172,6 +152,9 @@ static QMetaType::Type qIBaseTypeName(int iType, bool hasScale)
case blr_sql_date:
return QMetaType::QDate;
case blr_timestamp:
+#if (FB_API_VER >= 40)
+ case blr_timestamp_tz:
+#endif
return QMetaType::QDateTime;
case blr_blob:
return QMetaType::QByteArray;
@@ -207,6 +190,9 @@ static QMetaType::Type qIBaseTypeName2(int iType, bool hasScale)
case SQL_DOUBLE:
return QMetaType::Double;
case SQL_TIMESTAMP:
+#if (FB_API_VER >= 40)
+ case SQL_TIMESTAMP_TZ:
+#endif
return QMetaType::QDateTime;
case SQL_TYPE_TIME:
return QMetaType::QTime;
@@ -241,12 +227,45 @@ static QDateTime fromTimeStamp(char *buffer)
// have to demangle the structure ourselves because isc_decode_time
// strips the msecs
- t = t.addMSecs(int(((ISC_TIMESTAMP*)buffer)->timestamp_time / 10));
- d = bd.addDays(int(((ISC_TIMESTAMP*)buffer)->timestamp_date));
-
+ auto timebuf = reinterpret_cast<ISC_TIMESTAMP*>(buffer);
+ t = t.addMSecs(static_cast<int>(timebuf->timestamp_time / 10));
+ d = bd.addDays(timebuf->timestamp_date);
return QDateTime(d, t);
}
+#if (FB_API_VER >= 40)
+QDateTime fromTimeStampTz(char *buffer)
+{
+ static const QDate bd(1858, 11, 17);
+ QTime t(0, 0);
+ QDate d;
+
+ // have to demangle the structure ourselves because isc_decode_time
+ // strips the msecs
+ auto timebuf = reinterpret_cast<ISC_TIMESTAMP_TZ*>(buffer);
+ t = t.addMSecs(static_cast<int>(timebuf->utc_timestamp.timestamp_time / 10));
+ d = bd.addDays(timebuf->utc_timestamp.timestamp_date);
+ quint16 fpTzID = timebuf->time_zone;
+
+ QByteArray timeZoneName = qFbTzIdToIanaIdMap()->value(fpTzID);
+ if (!timeZoneName.isEmpty())
+ return QDateTime(d, t, QTimeZone(timeZoneName));
+ else
+ return {};
+}
+
+ISC_TIMESTAMP_TZ toTimeStampTz(const QDateTime &dt)
+{
+ static const QTime midnight(0, 0, 0, 0);
+ static const QDate basedate(1858, 11, 17);
+ ISC_TIMESTAMP_TZ ts;
+ ts.utc_timestamp.timestamp_time = midnight.msecsTo(dt.time()) * 10;
+ ts.utc_timestamp.timestamp_date = basedate.daysTo(dt.date());
+ ts.time_zone = qIanaIdToFbTzIdMap()->value(dt.timeZone().id().simplified(), 0);
+ return ts;
+}
+#endif
+
static ISC_TIME toTime(QTime t)
{
static const QTime midnight(0, 0, 0, 0);
@@ -315,6 +334,34 @@ public:
return true;
}
+#if (FB_API_VER >= 40)
+ void initTZMappingCache()
+ {
+ Q_Q(QIBaseDriver);
+ QSqlQuery qry(q->createResult());
+ qry.setForwardOnly(true);
+ qry.exec(QString("select * from RDB$TIME_ZONES"_L1));
+ if (qry.lastError().type()) {
+ q->setLastError(QSqlError(
+ QCoreApplication::translate("QIBaseDriver",
+ "failed to query time zone mapping from system table"),
+ qry.lastError().databaseText(),
+ QSqlError::StatementError,
+ qry.lastError().nativeErrorCode()));
+
+ return;
+ }
+
+ while (qry.next()) {
+ auto record = qry.record();
+ quint16 fbTzId = record.value(0).value<quint16>();
+ QByteArray ianaId = record.value(1).toByteArray().simplified();
+ qFbTzIdToIanaIdMap()->insert(fbTzId, ianaId);
+ qIanaIdToFbTzIdMap()->insert(ianaId, fbTzId);
+ }
+ }
+#endif
+
public:
isc_db_handle ibase;
isc_tr_handle trans;
@@ -385,9 +432,9 @@ public:
bool isSelect();
QVariant fetchBlob(ISC_QUAD *bId);
- bool writeBlob(int i, const QByteArray &ba);
+ bool writeBlob(qsizetype iPos, const QByteArray &ba);
QVariant fetchArray(int pos, ISC_QUAD *arr);
- bool writeArray(int i, const QList<QVariant> &list);
+ bool writeArray(qsizetype i, const QList<QVariant> &list);
public:
ISC_STATUS status[20];
isc_tr_handle trans;
@@ -407,8 +454,8 @@ QIBaseResultPrivate::QIBaseResultPrivate(QIBaseResult *q, const QIBaseDriver *dr
localTransaction(!drv_d_func()->ibase),
stmt(0),
ibase(drv_d_func()->ibase),
- sqlda(0),
- inda(0),
+ sqlda(nullptr),
+ inda(nullptr),
queryType(-1)
{
}
@@ -432,20 +479,20 @@ void QIBaseResultPrivate::cleanup()
q->cleanup();
}
-bool QIBaseResultPrivate::writeBlob(int i, const QByteArray &ba)
+bool QIBaseResultPrivate::writeBlob(qsizetype iPos, const QByteArray &ba)
{
isc_blob_handle handle = 0;
- ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[i].sqldata;
+ ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[iPos].sqldata;
isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
if (!isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to create BLOB"),
QSqlError::StatementError)) {
- int i = 0;
+ qsizetype i = 0;
while (i < ba.size()) {
- isc_put_segment(status, &handle, qMin(ba.size() - i, int(QIBaseChunkSize)),
- const_cast<char*>(ba.data()) + i);
+ isc_put_segment(status, &handle, qMin(ba.size() - i, QIBaseChunkSize),
+ ba.data() + i);
if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to write BLOB")))
return false;
- i += qMin(ba.size() - i, int(QIBaseChunkSize));
+ i += qMin(ba.size() - i, QIBaseChunkSize);
}
}
isc_close_blob(status, &handle);
@@ -465,7 +512,7 @@ QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId)
QByteArray ba;
int chunkSize = QIBaseChunkSize;
ba.resize(chunkSize);
- int read = 0;
+ qsizetype read = 0;
while (isc_get_segment(status, &handle, &len, chunkSize, ba.data() + read) == 0 || status[1] == isc_segment) {
read += len;
ba.resize(read + chunkSize);
@@ -487,7 +534,7 @@ QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId)
}
template<typename T>
-static QList<QVariant> toList(char** buf, int count, T* = nullptr)
+static QList<QVariant> toList(char** buf, int count)
{
QList<QVariant> res;
for (int i = 0; i < count; ++i) {
@@ -526,7 +573,7 @@ static char* readArrayBuffer(QList<QVariant>& list, char *buffer, short curDim,
}
break; }
case blr_long:
- valList = toList<int>(&buffer, numElements[dim], static_cast<int *>(0));
+ valList = toList<int>(&buffer, numElements[dim]);
break;
case blr_short:
valList = toList<short>(&buffer, numElements[dim]);
@@ -546,8 +593,16 @@ static char* readArrayBuffer(QList<QVariant>& list, char *buffer, short curDim,
buffer += sizeof(ISC_TIMESTAMP);
}
break;
+#if (FB_API_VER >= 40)
+ case blr_timestamp_tz:
+ for (int i = 0; i < numElements[dim]; ++i) {
+ valList.append(fromTimeStampTz(buffer));
+ buffer += sizeof(ISC_TIMESTAMP_TZ);
+ }
+ break;
+#endif
case blr_sql_time:
- for(int i = 0; i < numElements[dim]; ++i) {
+ for (int i = 0; i < numElements[dim]; ++i) {
valList.append(fromTime(buffer));
buffer += sizeof(ISC_TIME);
}
@@ -626,9 +681,8 @@ QVariant QIBaseResultPrivate::fetchArray(int pos, ISC_QUAD *arr)
template<typename T>
static char* fillList(char *buffer, const QList<QVariant> &list, T* = nullptr)
{
- for (int i = 0; i < list.size(); ++i) {
- T val;
- val = qvariant_cast<T>(list.at(i));
+ for (const auto &elem : list) {
+ T val = qvariant_cast<T>(elem);
memcpy(buffer, &val, sizeof(T));
buffer += sizeof(T);
}
@@ -638,11 +692,9 @@ static char* fillList(char *buffer, const QList<QVariant> &list, T* = nullptr)
template<>
char* fillList<float>(char *buffer, const QList<QVariant> &list, float*)
{
- for (int i = 0; i < list.size(); ++i) {
- double val;
- float val2 = 0;
- val = qvariant_cast<double>(list.at(i));
- val2 = (float)val;
+ for (const auto &elem : list) {
+ double val = qvariant_cast<double>(elem);
+ float val2 = (float)val;
memcpy(buffer, &val2, sizeof(float));
buffer += sizeof(float);
}
@@ -677,7 +729,6 @@ static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
QMetaType::Type type, short curDim, ISC_ARRAY_DESC *arrayDesc,
QString& error)
{
- int i;
ISC_ARRAY_BOUND *bounds = arrayDesc->array_desc_bounds;
short dim = arrayDesc->array_desc_dimensions - 1;
@@ -685,21 +736,21 @@ static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
bounds[curDim].array_bound_lower + 1);
if (list.size() != elements) { // size mismatch
- error = QLatin1String("Expected size: %1. Supplied size: %2");
- error = QLatin1String("Array size mismatch. Fieldname: %1 ")
+ error = "Expected size: %1. Supplied size: %2"_L1;
+ error = "Array size mismatch. Fieldname: %1 "_L1
+ error.arg(elements).arg(list.size());
return 0;
}
if (curDim != dim) {
- for(i = 0; i < list.size(); ++i) {
+ for (const auto &elem : list) {
- if (list.at(i).typeId() != QMetaType::QVariantList) { // dimensions mismatch
- error = QLatin1String("Array dimensons mismatch. Fieldname: %1");
+ if (elem.typeId() != QMetaType::QVariantList) { // dimensions mismatch
+ error = "Array dimensons mismatch. Fieldname: %1"_L1;
return 0;
}
- buffer = createArrayBuffer(buffer, list.at(i).toList(), type, curDim + 1,
+ buffer = createArrayBuffer(buffer, elem.toList(), type, curDim + 1,
arrayDesc, error);
if (!buffer)
return 0;
@@ -726,29 +777,40 @@ static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
buffer = fillList<quint64>(buffer, list);
break;
case QMetaType::QString:
- for (i = 0; i < list.size(); ++i)
- buffer = qFillBufferWithString(buffer, list.at(i).toString(),
+ for (const auto &elem : list)
+ buffer = qFillBufferWithString(buffer, elem.toString(),
arrayDesc->array_desc_length,
arrayDesc->array_desc_dtype == blr_varying,
true);
break;
case QMetaType::QDate:
- for (i = 0; i < list.size(); ++i) {
- *((ISC_DATE*)buffer) = toDate(list.at(i).toDate());
+ for (const auto &elem : list) {
+ *((ISC_DATE*)buffer) = toDate(elem.toDate());
buffer += sizeof(ISC_DATE);
}
break;
case QMetaType::QTime:
- for (i = 0; i < list.size(); ++i) {
- *((ISC_TIME*)buffer) = toTime(list.at(i).toTime());
+ for (const auto &elem : list) {
+ *((ISC_TIME*)buffer) = toTime(elem.toTime());
buffer += sizeof(ISC_TIME);
}
break;
-
case QMetaType::QDateTime:
- for (i = 0; i < list.size(); ++i) {
- *((ISC_TIMESTAMP*)buffer) = toTimeStamp(list.at(i).toDateTime());
- buffer += sizeof(ISC_TIMESTAMP);
+ for (const auto &elem : list) {
+ switch (arrayDesc->array_desc_dtype) {
+ case blr_timestamp:
+ *((ISC_TIMESTAMP*)buffer) = toTimeStamp(elem.toDateTime());
+ buffer += sizeof(ISC_TIMESTAMP);
+ break;
+#if (FB_API_VER >= 40)
+ case blr_timestamp_tz:
+ *((ISC_TIMESTAMP_TZ*)buffer) = toTimeStampTz(elem.toDateTime());
+ buffer += sizeof(ISC_TIMESTAMP_TZ);
+ break;
+#endif
+ default:
+ break;
+ }
}
break;
case QMetaType::Bool:
@@ -761,7 +823,7 @@ static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
return buffer;
}
-bool QIBaseResultPrivate::writeArray(int column, const QList<QVariant> &list)
+bool QIBaseResultPrivate::writeArray(qsizetype column, const QList<QVariant> &list)
{
Q_Q(QIBaseResult);
QString error;
@@ -778,7 +840,6 @@ bool QIBaseResultPrivate::writeArray(int column, const QList<QVariant> &list)
short arraySize = 1;
ISC_LONG bufLen;
- QList<QVariant> subList = list;
short dimensions = desc.array_desc_dimensions;
for(int i = 0; i < dimensions; ++i) {
@@ -798,16 +859,16 @@ bool QIBaseResultPrivate::writeArray(int column, const QList<QVariant> &list)
ba.resize(int(bufLen));
if (list.size() > arraySize) {
- error = QLatin1String("Array size missmatch: size of %1 is %2, size of provided list is %3");
- error = error.arg(QLatin1String(sqlname)).arg(arraySize).arg(list.size());
- q->setLastError(QSqlError(error, QLatin1String(""), QSqlError::StatementError));
+ error = "Array size mismatch: size of %1 is %2, size of provided list is %3"_L1;
+ error = error.arg(QLatin1StringView(sqlname)).arg(arraySize).arg(list.size());
+ q->setLastError(QSqlError(error, ""_L1, QSqlError::StatementError));
return false;
}
if (!createArrayBuffer(ba.data(), list,
qIBaseTypeName(desc.array_desc_dtype, inda->sqlvar[column].sqlscale < 0),
0, &desc, error)) {
- q->setLastError(QSqlError(error.arg(QLatin1String(sqlname)), QLatin1String(""),
+ q->setLastError(QSqlError(error.arg(QLatin1StringView(sqlname)), ""_L1,
QSqlError::StatementError));
return false;
}
@@ -880,7 +941,7 @@ QIBaseResult::QIBaseResult(const QIBaseDriver *db)
bool QIBaseResult::prepare(const QString& query)
{
Q_D(QIBaseResult);
-// qDebug("prepare: %s", qPrintable(query));
+// qDebug("prepare: %ls", qUtf16Printable(query));
if (!driver() || !driver()->isOpen() || driver()->isOpenError())
return false;
d->cleanup();
@@ -953,7 +1014,6 @@ bool QIBaseResult::prepare(const QString& query)
return true;
}
-
bool QIBaseResult::exec()
{
Q_D(QIBaseResult);
@@ -968,23 +1028,20 @@ bool QIBaseResult::exec()
setAt(QSql::BeforeFirstRow);
if (d->inda) {
- QList<QVariant>& values = boundValues();
- int i;
+ const QList<QVariant> &values = boundValues();
if (values.count() > d->inda->sqld) {
- qWarning() << QLatin1String("QIBaseResult::exec: Parameter mismatch, expected") <<
- d->inda->sqld << QLatin1String(", got") << values.count() <<
- QLatin1String("parameters");
+ qWarning() << "QIBaseResult::exec: Parameter mismatch, expected"_L1 <<
+ d->inda->sqld << ", got"_L1 << values.count() <<
+ "parameters"_L1;
return false;
}
- int para = 0;
- for (i = 0; i < values.count(); ++i) {
- para = i;
+ for (qsizetype para = 0; para < values.count(); ++para) {
if (!d->inda->sqlvar[para].sqldata)
// skip unknown datatypes
continue;
- const QVariant val(values[i]);
+ const QVariant &val = values[para];
if (d->inda->sqlvar[para].sqltype & 1) {
- if (val.isNull()) {
+ if (QSqlResultPrivate::isVariantNull(val)) {
// set null indicator
*(d->inda->sqlvar[para].sqlind) = -1;
// and set the value to 0, otherwise it would count as empty string.
@@ -994,6 +1051,12 @@ bool QIBaseResult::exec()
}
// a value of 0 means non-null.
*(d->inda->sqlvar[para].sqlind) = 0;
+ } else {
+ if (QSqlResultPrivate::isVariantNull(val)) {
+ qWarning() << "QIBaseResult::exec: Null value replaced by default (zero)"_L1
+ << "value for type of column"_L1 << d->inda->sqlvar[para].ownname
+ << ", which is not nullable."_L1;
+ }
}
switch(d->inda->sqlvar[para].sqltype & ~1) {
case SQL_INT64:
@@ -1030,6 +1093,11 @@ bool QIBaseResult::exec()
case SQL_TIMESTAMP:
*((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime());
break;
+#if (FB_API_VER >= 40)
+ case SQL_TIMESTAMP_TZ:
+ *((ISC_TIMESTAMP_TZ*)d->inda->sqlvar[para].sqldata) = toTimeStampTz(val.toDateTime());
+ break;
+#endif
case SQL_TYPE_TIME:
*((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime());
break;
@@ -1213,6 +1281,11 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
case SQL_BOOLEAN:
row[idx] = QVariant(bool((*(bool*)buf)));
break;
+#if (FB_API_VER >= 40)
+ case SQL_TIMESTAMP_TZ:
+ row[idx] = fromTimeStampTz(buf);
+ break;
+#endif
default:
// unknown type - don't even try to fetch
row[idx] = QVariant();
@@ -1369,11 +1442,11 @@ QSqlRecord QIBaseResult::record() const
if (v.sqlscale < 0) {
QSqlQuery q(driver()->createResult());
q.setForwardOnly(true);
- q.exec(QLatin1String("select b.RDB$FIELD_PRECISION, b.RDB$FIELD_SCALE, b.RDB$FIELD_LENGTH, a.RDB$NULL_FLAG "
+ q.exec("select b.RDB$FIELD_PRECISION, b.RDB$FIELD_SCALE, b.RDB$FIELD_LENGTH, a.RDB$NULL_FLAG "
"FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
"WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
- "AND a.RDB$RELATION_NAME = '") + QString::fromLatin1(v.relname, v.relname_length) + QLatin1String("' "
- "AND a.RDB$FIELD_NAME = '") + QString::fromLatin1(v.sqlname, v.sqlname_length) + QLatin1String("' "));
+ "AND a.RDB$RELATION_NAME = '"_L1 + QString::fromLatin1(v.relname, v.relname_length) + "' "
+ "AND a.RDB$FIELD_NAME = '"_L1 + QString::fromLatin1(v.sqlname, v.sqlname_length) + "' "_L1);
if (q.first()) {
if (v.sqlscale < 0) {
f.setLength(q.value(0).toInt());
@@ -1385,7 +1458,6 @@ QSqlRecord QIBaseResult::record() const
f.setRequiredStatus(q.value(3).toBool() ? QSqlField::Required : QSqlField::Optional);
}
}
- f.setSqlType(v.sqltype);
rec.append(f);
}
return rec;
@@ -1441,27 +1513,27 @@ bool QIBaseDriver::hasFeature(DriverFeature f) const
return false;
}
-bool QIBaseDriver::open(const QString & db,
- const QString & user,
- const QString & password,
- const QString & host,
- int port,
- const QString & connOpts)
+bool QIBaseDriver::open(const QString &db,
+ const QString &user,
+ const QString &password,
+ const QString &host,
+ int port,
+ const QString &connOpts)
{
Q_D(QIBaseDriver);
if (isOpen())
close();
- const QStringList opts(connOpts.split(QLatin1Char(';'), Qt::SkipEmptyParts));
+ const auto opts(QStringView(connOpts).split(u';', Qt::SkipEmptyParts));
QByteArray role;
- for (int i = 0; i < opts.count(); ++i) {
- QString tmp(opts.at(i).simplified());
- int idx;
- if ((idx = tmp.indexOf(QLatin1Char('='))) != -1) {
- QString val = tmp.mid(idx + 1).simplified();
- QString opt = tmp.left(idx).simplified();
- if (opt.toUpper() == QLatin1String("ISC_DPB_SQL_ROLE_NAME")) {
+ for (const auto &opt : opts) {
+ const auto tmp(opt.trimmed());
+ qsizetype idx;
+ if ((idx = tmp.indexOf(u'=')) != -1) {
+ const auto val = tmp.mid(idx + 1).trimmed();
+ const auto opt = tmp.left(idx).trimmed().toString();
+ if (opt.toUpper() == "ISC_DPB_SQL_ROLE_NAME"_L1) {
role = val.toLocal8Bit();
role.truncate(255);
}
@@ -1499,7 +1571,7 @@ bool QIBaseDriver::open(const QString & db,
QString ldb;
if (!host.isEmpty())
- ldb += host + portString + QLatin1Char(':');
+ ldb += host + portString + u':';
ldb += db;
isc_attach_database(d->status, 0, const_cast<char *>(ldb.toLocal8Bit().constData()),
&d->ibase, ba.size(), ba.data());
@@ -1511,6 +1583,14 @@ bool QIBaseDriver::open(const QString & db,
setOpen(true);
setOpenError(false);
+#if (FB_API_VER >= 40)
+ std::call_once(initTZMappingFlag, [d](){ d->initTZMappingCache(); });
+ if (lastError().isValid())
+ {
+ setOpen(true);
+ return false;
+ }
+#endif
return true;
}
@@ -1529,13 +1609,6 @@ void QIBaseDriver::close()
qFreeEventBuffer(eBuffer);
}
d->eventBuffers.clear();
-
-#if defined(FB_API_VER)
- // TODO check whether this workaround for Firebird crash is still needed
- QDeadlineTimer timer(500);
- while (!timer.hasExpired())
- QCoreApplication::processEvents();
-#endif
}
isc_detach_database(d->status, &d->ibase);
@@ -1600,28 +1673,28 @@ QStringList QIBaseDriver::tables(QSql::TableType type) const
QString typeFilter;
if (type == QSql::SystemTables) {
- typeFilter += QLatin1String("RDB$SYSTEM_FLAG != 0");
+ typeFilter += "RDB$SYSTEM_FLAG != 0"_L1;
} else if (type == (QSql::SystemTables | QSql::Views)) {
- typeFilter += QLatin1String("RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL");
+ typeFilter += "RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL"_L1;
} else {
if (!(type & QSql::SystemTables))
- typeFilter += QLatin1String("RDB$SYSTEM_FLAG = 0 AND ");
+ typeFilter += "RDB$SYSTEM_FLAG = 0 AND "_L1;
if (!(type & QSql::Views))
- typeFilter += QLatin1String("RDB$VIEW_BLR IS NULL AND ");
+ typeFilter += "RDB$VIEW_BLR IS NULL AND "_L1;
if (!(type & QSql::Tables))
- typeFilter += QLatin1String("RDB$VIEW_BLR IS NOT NULL AND ");
+ typeFilter += "RDB$VIEW_BLR IS NOT NULL AND "_L1;
if (!typeFilter.isEmpty())
typeFilter.chop(5);
}
if (!typeFilter.isEmpty())
- typeFilter.prepend(QLatin1String("where "));
+ typeFilter.prepend("where "_L1);
QSqlQuery q(createResult());
q.setForwardOnly(true);
- if (!q.exec(QLatin1String("select rdb$relation_name from rdb$relations ") + typeFilter))
+ if (!q.exec("select rdb$relation_name from rdb$relations "_L1 + typeFilter))
return res;
- while(q.next())
- res << q.value(0).toString().simplified();
+ while (q.next())
+ res << q.value(0).toString().simplified();
return res;
}
@@ -1632,19 +1705,15 @@ QSqlRecord QIBaseDriver::record(const QString& tablename) const
if (!isOpen())
return rec;
+ const QString table = stripDelimiters(tablename, QSqlDriver::TableName);
QSqlQuery q(createResult());
q.setForwardOnly(true);
- QString table = tablename;
- if (isIdentifierEscaped(table, QSqlDriver::TableName))
- table = stripDelimiters(table, QSqlDriver::TableName);
- else
- table = table.toUpper();
- q.exec(QLatin1String("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, "
+ q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, "
"b.RDB$FIELD_SCALE, b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
"FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
"WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
- "AND a.RDB$RELATION_NAME = '") + table + QLatin1String("' "
- "ORDER BY a.RDB$FIELD_POSITION"));
+ "AND a.RDB$RELATION_NAME = '"_L1 + table + "' "
+ "ORDER BY a.RDB$FIELD_POSITION"_L1);
while (q.next()) {
int type = q.value(1).toInt();
@@ -1658,7 +1727,6 @@ QSqlRecord QIBaseDriver::record(const QString& tablename) const
f.setPrecision(0);
}
f.setRequired(q.value(5).toInt() > 0);
- f.setSqlType(type);
rec.append(f);
}
@@ -1671,23 +1739,18 @@ QSqlIndex QIBaseDriver::primaryIndex(const QString &table) const
if (!isOpen())
return index;
- QString tablename = table;
- if (isIdentifierEscaped(tablename, QSqlDriver::TableName))
- tablename = stripDelimiters(tablename, QSqlDriver::TableName);
- else
- tablename = tablename.toUpper();
-
+ const QString tablename = stripDelimiters(table, QSqlDriver::TableName);
QSqlQuery q(createResult());
q.setForwardOnly(true);
- q.exec(QLatin1String("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE, d.RDB$FIELD_SCALE "
+ q.exec("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE, d.RDB$FIELD_SCALE "
"FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d "
"WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
- "AND a.RDB$RELATION_NAME = '") + tablename +
- QLatin1String(" 'AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
+ "AND a.RDB$RELATION_NAME = '"_L1 + tablename +
+ " 'AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
"AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME "
"AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME "
"AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE "
- "ORDER BY b.RDB$FIELD_POSITION"));
+ "ORDER BY b.RDB$FIELD_POSITION"_L1);
while (q.next()) {
QSqlField field(q.value(1).toString().simplified(),
@@ -1706,36 +1769,36 @@ QString QIBaseDriver::formatValue(const QSqlField &field, bool trimStrings) cons
case QMetaType::QDateTime: {
QDateTime datetime = field.value().toDateTime();
if (datetime.isValid())
- return QLatin1Char('\'') + QString::number(datetime.date().year()) + QLatin1Char('-') +
- QString::number(datetime.date().month()) + QLatin1Char('-') +
- QString::number(datetime.date().day()) + QLatin1Char(' ') +
- QString::number(datetime.time().hour()) + QLatin1Char(':') +
- QString::number(datetime.time().minute()) + QLatin1Char(':') +
- QString::number(datetime.time().second()) + QLatin1Char('.') +
- QString::number(datetime.time().msec()).rightJustified(3, QLatin1Char('0'), true) +
- QLatin1Char('\'');
+ return u'\'' + QString::number(datetime.date().year()) + u'-' +
+ QString::number(datetime.date().month()) + u'-' +
+ QString::number(datetime.date().day()) + u' ' +
+ QString::number(datetime.time().hour()) + u':' +
+ QString::number(datetime.time().minute()) + u':' +
+ QString::number(datetime.time().second()) + u'.' +
+ QString::number(datetime.time().msec()).rightJustified(3, u'0', true) +
+ u'\'';
else
- return QLatin1String("NULL");
+ return "NULL"_L1;
}
case QMetaType::QTime: {
QTime time = field.value().toTime();
if (time.isValid())
- return QLatin1Char('\'') + QString::number(time.hour()) + QLatin1Char(':') +
- QString::number(time.minute()) + QLatin1Char(':') +
- QString::number(time.second()) + QLatin1Char('.') +
- QString::number(time.msec()).rightJustified(3, QLatin1Char('0'), true) +
- QLatin1Char('\'');
+ return u'\'' + QString::number(time.hour()) + u':' +
+ QString::number(time.minute()) + u':' +
+ QString::number(time.second()) + u'.' +
+ QString::number(time.msec()).rightJustified(3, u'0', true) +
+ u'\'';
else
- return QLatin1String("NULL");
+ return "NULL"_L1;
}
case QMetaType::QDate: {
QDate date = field.value().toDate();
if (date.isValid())
- return QLatin1Char('\'') + QString::number(date.year()) + QLatin1Char('-') +
- QString::number(date.month()) + QLatin1Char('-') +
- QString::number(date.day()) + QLatin1Char('\'');
+ return u'\'' + QString::number(date.year()) + u'-' +
+ QString::number(date.month()) + u'-' +
+ QString::number(date.day()) + u'\'';
else
- return QLatin1String("NULL");
+ return "NULL"_L1;
}
default:
return QSqlDriver::formatValue(field, trimStrings);
@@ -1776,8 +1839,8 @@ bool QIBaseDriver::subscribeToNotification(const QString &name)
}
if (d->eventBuffers.contains(name)) {
- qWarning("QIBaseDriver::subscribeToNotificationImplementation: already subscribing to '%s'.",
- qPrintable(name));
+ qWarning("QIBaseDriver::subscribeToNotificationImplementation: already subscribing to '%ls'.",
+ qUtf16Printable(name));
return false;
}
@@ -1823,8 +1886,8 @@ bool QIBaseDriver::unsubscribeFromNotification(const QString &name)
}
if (!d->eventBuffers.contains(name)) {
- qWarning("QIBaseDriver::QIBaseSubscriptionState not subscribed to '%s'.",
- qPrintable(name));
+ qWarning("QIBaseDriver::QIBaseSubscriptionState not subscribed to '%ls'.",
+ qUtf16Printable(name));
return false;
}
@@ -1879,8 +1942,8 @@ void QIBaseDriver::qHandleEventNotification(void *updatedResultBuffer)
(&qEventCallback)),
eBuffer->resultBuffer);
if (Q_UNLIKELY(status[0] == 1 && status[1])) {
- qCritical("QIBaseDriver::qHandleEventNotification: could not resubscribe to '%s'",
- qPrintable(i.key()));
+ qCritical("QIBaseDriver::qHandleEventNotification: could not resubscribe to '%ls'",
+ qUtf16Printable(i.key()));
}
return;
@@ -1891,10 +1954,10 @@ void QIBaseDriver::qHandleEventNotification(void *updatedResultBuffer)
QString QIBaseDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
{
QString res = identifier;
- if (!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
- res.replace(QLatin1Char('"'), QLatin1String("\"\""));
- res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
- res.replace(QLatin1Char('.'), QLatin1String("\".\""));
+ if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"') ) {
+ res.replace(u'"', "\"\""_L1);
+ res.replace(u'.', "\".\""_L1);
+ res = u'"' + res + u'"';
}
return res;
}
@@ -1906,3 +1969,5 @@ int QIBaseDriver::maximumIdentifierLength(IdentifierType type) const
}
QT_END_NAMESPACE
+
+#include "moc_qsql_ibase_p.cpp"
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase_p.h b/src/plugins/sqldrivers/ibase/qsql_ibase_p.h
index 9109c2b40f..86c7ae1241 100644
--- a/src/plugins/sqldrivers/ibase/qsql_ibase_p.h
+++ b/src/plugins/sqldrivers/ibase/qsql_ibase_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSQL_IBASE_H
#define QSQL_IBASE_H
diff --git a/src/plugins/sqldrivers/mimer/CMakeLists.txt b/src/plugins/sqldrivers/mimer/CMakeLists.txt
new file mode 100644
index 0000000000..303af7120c
--- /dev/null
+++ b/src/plugins/sqldrivers/mimer/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Generated from mimer.pro.
+
+#####################################################################
+## MIMERSQLDriverPlugin Plugin:
+#####################################################################
+
+qt_internal_add_plugin(QMimerSQLDriverPlugin
+ OUTPUT_NAME qsqlmimer
+ PLUGIN_TYPE sqldrivers
+ SOURCES
+ main.cpp
+ qsql_mimer.cpp qsql_mimer.h
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ QT_NO_CONTEXTLESS_CONNECT
+ LIBRARIES
+ MimerSQL::MimerSQL
+ Qt::Core
+ Qt::SqlPrivate
+)
+
+#### Keys ignored in scope 1:.:.:mimer.pro:<TRUE>:
+# OTHER_FILES = "mimer.json"
diff --git a/src/plugins/sqldrivers/mimer/README b/src/plugins/sqldrivers/mimer/README
new file mode 100644
index 0000000000..02e4c3e162
--- /dev/null
+++ b/src/plugins/sqldrivers/mimer/README
@@ -0,0 +1,6 @@
+You will need the Mimer SQL development headers and libraries installed before
+compiling this plugin. qsql_mimer.h contains an include to mimerapi.h that is
+needed for the driver to compile.
+
+See the Qt SQL documentation for more information on compiling Qt SQL driver
+plugins.
diff --git a/src/plugins/sqldrivers/mimer/main.cpp b/src/plugins/sqldrivers/mimer/main.cpp
new file mode 100644
index 0000000000..560b7da7c7
--- /dev/null
+++ b/src/plugins/sqldrivers/mimer/main.cpp
@@ -0,0 +1,33 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2022 Mimer Information Technology
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qsql_mimer.h"
+
+#include <qsqldriverplugin.h>
+#include <qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+class QMimerSQLDriverPlugin : public QSqlDriverPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSqlDriverFactoryInterface" FILE "mimer.json")
+public:
+ QMimerSQLDriverPlugin();
+ QSqlDriver *create(const QString &) override;
+};
+
+QMimerSQLDriverPlugin::QMimerSQLDriverPlugin() : QSqlDriverPlugin() { }
+
+QSqlDriver *QMimerSQLDriverPlugin::create(const QString &name)
+{
+ if (name == "QMIMER"_L1)
+ return new QMimerSQLDriver;
+ return nullptr;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/sqldrivers/mimer/mimer.json b/src/plugins/sqldrivers/mimer/mimer.json
new file mode 100644
index 0000000000..fba96b765d
--- /dev/null
+++ b/src/plugins/sqldrivers/mimer/mimer.json
@@ -0,0 +1,5 @@
+{
+ "Keys": [
+ "QMIMER"
+ ]
+}
diff --git a/src/plugins/sqldrivers/mimer/qsql_mimer.cpp b/src/plugins/sqldrivers/mimer/qsql_mimer.cpp
new file mode 100644
index 0000000000..a08e45d76c
--- /dev/null
+++ b/src/plugins/sqldrivers/mimer/qsql_mimer.cpp
@@ -0,0 +1,1607 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2022 Mimer Information Technology
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include <qcoreapplication.h>
+#include <qvariant.h>
+#include <qmetatype.h>
+#include <qdatetime.h>
+#include <qsqlerror.h>
+#include <qsqlfield.h>
+#include <qsqlindex.h>
+#include <qsqlrecord.h>
+#include <qsqlquery.h>
+#include <qsocketnotifier.h>
+#include <qstringlist.h>
+#include <qlocale.h>
+#if defined(Q_OS_WIN32)
+# include <QtCore/qt_windows.h>
+#endif
+#include <QtSql/private/qsqlresult_p.h>
+#include <QtSql/private/qsqldriver_p.h>
+#include "qsql_mimer.h"
+
+#define MIMER_DEFAULT_DATATYPE 1000
+
+Q_DECLARE_OPAQUE_POINTER(MimerSession)
+Q_DECLARE_METATYPE(MimerSession)
+
+Q_DECLARE_OPAQUE_POINTER(MimerStatement)
+Q_DECLARE_METATYPE(MimerStatement)
+
+QT_BEGIN_NAMESPACE
+
+enum class MimerColumnTypes {
+ Binary,
+ Clob,
+ Blob,
+ String,
+ Int,
+ Numeric,
+ Long,
+ Float,
+ Double,
+ Boolean,
+ Uuid,
+ Date,
+ Time,
+ Timestamp,
+ Unknown
+};
+
+using namespace Qt::StringLiterals;
+
+class QMimerSQLResultPrivate;
+
+class QMimerSQLResult final : public QSqlResult
+{
+ Q_DECLARE_PRIVATE(QMimerSQLResult)
+public:
+ QMimerSQLResult(const QMimerSQLDriver *db);
+ virtual ~QMimerSQLResult() override;
+ QVariant handle() const override;
+ static constexpr int genericError = -1;
+ static constexpr int lobChunkMaxSizeSet = 1048500;
+ static constexpr int lobChunkMaxSizeFetch = 65536;
+ static constexpr int maxStackStringSize = 200;
+ static constexpr int maxTimeStringSize = 18;
+ static constexpr int maxDateStringSize = 10;
+ static constexpr int maxTimestampStringSize = 29;
+
+private:
+ void cleanup();
+ bool fetch(int i) override;
+ bool fetchFirst() override;
+ bool fetchLast() override;
+ bool fetchNext() override;
+ QVariant data(int i) override;
+ bool isNull(int index) override;
+ bool reset(const QString &query) override;
+ int size() override;
+ int numRowsAffected() override;
+ QSqlRecord record() const override;
+ bool prepare(const QString &query) override;
+ bool execBatch(bool arrayBind = false) override;
+ bool exec() override;
+ qint64 currentRow();
+ QVariant lastInsertId() const override;
+};
+
+class QMimerSQLDriverPrivate final : public QSqlDriverPrivate
+{
+ Q_DECLARE_PUBLIC(QMimerSQLDriver)
+public:
+ QMimerSQLDriverPrivate() : QSqlDriverPrivate(QSqlDriver::MimerSQL), sessionhandle(nullptr) { }
+ MimerSession sessionhandle;
+ QString dbName;
+ QString dbUser;
+ void splitTableQualifier(const QString &qualifier, QString *schema, QString *table) const;
+};
+
+class QMimerSQLResultPrivate : public QSqlResultPrivate
+{
+ Q_DECLARE_PUBLIC(QMimerSQLResult)
+public:
+ Q_DECLARE_SQLDRIVER_PRIVATE(QMimerSQLDriver)
+ QMimerSQLResultPrivate(QMimerSQLResult *q, const QMimerSQLDriver *drv)
+ : QSqlResultPrivate(q, drv),
+ statementhandle(nullptr),
+ lobhandle(nullptr),
+ rowsAffected(0),
+ preparedQuery(false),
+ openCursor(false),
+ openStatement(false),
+ executedStatement(false),
+ callWithOut(false),
+ execBatch(false),
+ currentRow(QSql::BeforeFirstRow)
+ {
+ }
+ MimerStatement statementhandle;
+ MimerLob lobhandle;
+ int rowsAffected;
+ bool preparedQuery;
+ bool openCursor;
+ bool openStatement;
+ bool executedStatement;
+ bool callWithOut;
+ bool execBatch;
+ qint64 currentSize = -1;
+ qint64 currentRow; // Only used when forwardOnly()
+ QVector<QVariant> batch_vector;
+};
+
+static QSqlError qMakeError(const QString &err, const int errCode, QSqlError::ErrorType type,
+ const QMimerSQLDriverPrivate *p)
+{
+ QString msg;
+ if (p) {
+ size_t str_len;
+ int e_code;
+ int rc;
+ str_len = (rc = MimerGetError(p->sessionhandle, &e_code, NULL, 0)) + 1;
+ if (!MIMER_SUCCEEDED(rc)) {
+ msg = QCoreApplication::translate("QMimerSQL", "No Mimer SQL error for code %1")
+ .arg(errCode);
+ } else {
+ QVarLengthArray<wchar_t> tmp_buff((qsizetype)str_len);
+ if (!MIMER_SUCCEEDED(
+ rc = MimerGetError(p->sessionhandle, &e_code, tmp_buff.data(), str_len)))
+ msg = QCoreApplication::translate("QMimerSQL", "No Mimer SQL error for code %1")
+ .arg(errCode);
+ else
+ msg = QString::fromWCharArray(tmp_buff.data());
+ }
+ } else {
+ msg = QCoreApplication::translate("QMimerSQL", "Generic Mimer SQL error");
+ }
+
+ return QSqlError("QMIMER: "_L1 + err, msg, type, QString::number(errCode));
+}
+
+static QString msgCouldNotGet(const char *type, int column)
+{
+ //: Data type, column
+ return QCoreApplication::translate("QMimerSQLResult",
+ "Could not get %1, column %2").arg(QLatin1StringView(type)).arg(column);
+}
+
+static QString msgCouldNotSet(const char *type, int column)
+{
+ //: Data type, parameter
+ return QCoreApplication::translate("QMimerSQLResult",
+ "Could not set %1, parameter %2").arg(QLatin1StringView(type)).arg(column);
+}
+
+QMimerSQLDriver::QMimerSQLDriver(QObject *parent) : QSqlDriver(*new QMimerSQLDriverPrivate, parent)
+{
+}
+
+QMimerSQLDriver::QMimerSQLDriver(MimerSession *conn, QObject *parent)
+ : QSqlDriver(*new QMimerSQLDriverPrivate, parent)
+{
+ Q_D(QMimerSQLDriver);
+ if (conn)
+ d->sessionhandle = *conn;
+}
+
+QMimerSQLDriver::~QMimerSQLDriver()
+{
+ close();
+}
+
+QMimerSQLResult::QMimerSQLResult(const QMimerSQLDriver *db)
+ : QSqlResult(*new QMimerSQLResultPrivate(this, db))
+{
+ Q_D(QMimerSQLResult);
+ d->preparedQuery = db->hasFeature(QSqlDriver::PreparedQueries);
+}
+
+QMimerSQLResult::~QMimerSQLResult()
+{
+ cleanup();
+}
+
+static MimerColumnTypes mimerMapColumnTypes(int32_t t)
+{
+ switch (t) {
+ case MIMER_BINARY:
+ case MIMER_BINARY_VARYING:
+ return MimerColumnTypes::Binary;
+ case MIMER_BLOB:
+ case MIMER_NATIVE_BLOB:
+ return MimerColumnTypes::Blob;
+ case MIMER_CLOB:
+ case MIMER_NCLOB:
+ case MIMER_NATIVE_CLOB:
+ case MIMER_NATIVE_NCLOB:
+ return MimerColumnTypes::Clob;
+ case MIMER_DATE:
+ return MimerColumnTypes::Date;
+ case MIMER_TIME:
+ return MimerColumnTypes::Time;
+ case MIMER_TIMESTAMP:
+ return MimerColumnTypes::Timestamp;
+ case MIMER_INTERVAL_DAY:
+ case MIMER_INTERVAL_DAY_TO_HOUR:
+ case MIMER_INTERVAL_DAY_TO_MINUTE:
+ case MIMER_INTERVAL_DAY_TO_SECOND:
+ case MIMER_INTERVAL_HOUR:
+ case MIMER_INTERVAL_HOUR_TO_MINUTE:
+ case MIMER_INTERVAL_HOUR_TO_SECOND:
+ case MIMER_INTERVAL_MINUTE:
+ case MIMER_INTERVAL_MINUTE_TO_SECOND:
+ case MIMER_INTERVAL_MONTH:
+ case MIMER_INTERVAL_SECOND:
+ case MIMER_INTERVAL_YEAR:
+ case MIMER_INTERVAL_YEAR_TO_MONTH:
+ case MIMER_NCHAR:
+ case MIMER_CHARACTER:
+ case MIMER_CHARACTER_VARYING:
+ case MIMER_NCHAR_VARYING:
+ case MIMER_UTF8:
+ case MIMER_DEFAULT_DATATYPE:
+ return MimerColumnTypes::String;
+ case MIMER_INTEGER:
+ case MIMER_DECIMAL:
+ case MIMER_FLOAT:
+ return MimerColumnTypes::Numeric;
+ case MIMER_BOOLEAN:
+ return MimerColumnTypes::Boolean;
+ case MIMER_T_BIGINT:
+ case MIMER_T_UNSIGNED_BIGINT:
+ case MIMER_NATIVE_BIGINT_NULLABLE:
+ case MIMER_NATIVE_BIGINT:
+ return MimerColumnTypes::Long;
+ case MIMER_NATIVE_REAL_NULLABLE:
+ case MIMER_NATIVE_REAL:
+ case MIMER_T_REAL:
+ return MimerColumnTypes::Float;
+ case MIMER_T_FLOAT:
+ case MIMER_NATIVE_DOUBLE_NULLABLE:
+ case MIMER_NATIVE_DOUBLE:
+ case MIMER_T_DOUBLE:
+ return MimerColumnTypes::Double;
+ case MIMER_NATIVE_INTEGER:
+ case MIMER_NATIVE_INTEGER_NULLABLE:
+ case MIMER_NATIVE_SMALLINT_NULLABLE:
+ case MIMER_NATIVE_SMALLINT:
+ case MIMER_T_INTEGER:
+ case MIMER_T_SMALLINT:
+ return MimerColumnTypes::Int;
+ case MIMER_UUID:
+ return MimerColumnTypes::Uuid;
+ default:
+ qWarning() << "QMimerSQLDriver::mimerMapColumnTypes: Unknown data type: " << t;
+ }
+ return MimerColumnTypes::Unknown;
+}
+
+static QMetaType::Type qDecodeMSQLType(int32_t t)
+{
+ switch (t) {
+ case MIMER_BINARY:
+ case MIMER_BINARY_VARYING:
+ case MIMER_BLOB:
+ case MIMER_NATIVE_BLOB:
+ return QMetaType::QByteArray;
+ case MIMER_CLOB:
+ case MIMER_NCLOB:
+ case MIMER_NATIVE_CLOB:
+ case MIMER_NATIVE_NCLOB:
+ case MIMER_INTERVAL_DAY:
+ case MIMER_DECIMAL:
+ case MIMER_INTERVAL_DAY_TO_HOUR:
+ case MIMER_INTERVAL_DAY_TO_MINUTE:
+ case MIMER_INTERVAL_DAY_TO_SECOND:
+ case MIMER_INTERVAL_HOUR:
+ case MIMER_INTERVAL_HOUR_TO_MINUTE:
+ case MIMER_INTERVAL_HOUR_TO_SECOND:
+ case MIMER_INTERVAL_MINUTE:
+ case MIMER_INTERVAL_MINUTE_TO_SECOND:
+ case MIMER_INTERVAL_MONTH:
+ case MIMER_INTERVAL_SECOND:
+ case MIMER_INTERVAL_YEAR:
+ case MIMER_INTERVAL_YEAR_TO_MONTH:
+ case MIMER_NCHAR:
+ case MIMER_CHARACTER:
+ case MIMER_CHARACTER_VARYING:
+ case MIMER_NCHAR_VARYING:
+ case MIMER_UTF8:
+ case MIMER_DEFAULT_DATATYPE:
+ case MIMER_INTEGER:
+ case MIMER_FLOAT:
+ return QMetaType::QString;
+ case MIMER_BOOLEAN:
+ return QMetaType::Bool;
+ case MIMER_T_BIGINT:
+ case MIMER_T_UNSIGNED_BIGINT:
+ case MIMER_NATIVE_BIGINT_NULLABLE:
+ case MIMER_NATIVE_BIGINT:
+ return QMetaType::LongLong;
+ case MIMER_NATIVE_REAL_NULLABLE:
+ case MIMER_NATIVE_REAL:
+ case MIMER_T_REAL:
+ return QMetaType::Float;
+ case MIMER_T_FLOAT:
+ case MIMER_NATIVE_DOUBLE_NULLABLE:
+ case MIMER_NATIVE_DOUBLE:
+ case MIMER_T_DOUBLE:
+ return QMetaType::Double;
+ case MIMER_NATIVE_INTEGER_NULLABLE:
+ case MIMER_T_INTEGER:
+ case MIMER_NATIVE_INTEGER:
+ return QMetaType::Int;
+ case MIMER_NATIVE_SMALLINT_NULLABLE:
+ case MIMER_T_SMALLINT:
+ return QMetaType::Int;
+ case MIMER_DATE:
+ return QMetaType::QDate;
+ case MIMER_TIME:
+ return QMetaType::QTime;
+ break;
+ case MIMER_TIMESTAMP:
+ return QMetaType::QDateTime;
+ case MIMER_UUID:
+ return QMetaType::QUuid;
+ default:
+ qWarning() << "QMimerSQLDriver::qDecodeMSQLType: Unknown data type: " << t;
+ return QMetaType::UnknownType;
+ }
+}
+
+static int32_t qLookupMimDataType(QStringView s)
+{
+ if (s == u"BINARY")
+ return MIMER_BINARY;
+ if (s == u"BINARY VARYING")
+ return MIMER_BINARY_VARYING;
+ if (s == u"BINARY LARGE OBJECT")
+ return MIMER_BLOB;
+ if (s == u"CHARACTER LARGE OBJECT")
+ return MIMER_CLOB;
+ if (s == u"NATIONAL CHAR LARGE OBJECT")
+ return MIMER_NCLOB;
+ if (s == u"INTERVAL DAY")
+ return MIMER_INTERVAL_DAY;
+ if (s == u"DECIMAL")
+ return MIMER_DECIMAL;
+ if (s == u"INTERVAL DAY TO HOUR")
+ return MIMER_INTERVAL_DAY_TO_HOUR;
+ if (s == u"INTERVAL DAY TO MINUTE")
+ return MIMER_INTERVAL_DAY_TO_MINUTE;
+ if (s == u"INTERVAL DAY TO SECOND")
+ return MIMER_INTERVAL_DAY_TO_SECOND;
+ if (s == u"INTERVAL HOUR")
+ return MIMER_INTERVAL_HOUR;
+ if (s == u"INTERVAL HOUR TO MINUTE")
+ return MIMER_INTERVAL_HOUR_TO_MINUTE;
+ if (s == u"INTERVAL HOUR TO SECOND")
+ return MIMER_INTERVAL_HOUR_TO_SECOND;
+ if (s == u"INTERVAL MINUTE")
+ return MIMER_INTERVAL_MINUTE;
+ if (s == u"INTERVAL MINUTE TO SECOND")
+ return MIMER_INTERVAL_MINUTE_TO_SECOND;
+ if (s == u"INTERVAL MONTH")
+ return MIMER_INTERVAL_MONTH;
+ if (s == u"INTERVAL SECOND")
+ return MIMER_INTERVAL_SECOND;
+ if (s == u"INTERVAL YEAR")
+ return MIMER_INTERVAL_YEAR;
+ if (s == u"INTERVAL YEAR TO MONTH")
+ return MIMER_INTERVAL_YEAR_TO_MONTH;
+ if (s == u"NATIONAL CHARACTER")
+ return MIMER_NCHAR;
+ if (s == u"CHARACTER")
+ return MIMER_CHARACTER;
+ if (s == u"CHARACTER VARYING")
+ return MIMER_CHARACTER_VARYING;
+ if (s == u"NATIONAL CHARACTER VARYING")
+ return MIMER_NCHAR_VARYING;
+ if (s == u"UTF-8")
+ return MIMER_UTF8;
+ if (s == u"BOOLEAN")
+ return MIMER_BOOLEAN;
+ if (s == u"BIGINT")
+ return MIMER_T_BIGINT;
+ if (s == u"REAL")
+ return MIMER_T_REAL;
+ if (s == u"FLOAT")
+ return MIMER_T_FLOAT;
+ if (s == u"DOUBLE PRECISION")
+ return MIMER_T_DOUBLE;
+ if (s == u"INTEGER")
+ return MIMER_T_INTEGER;
+ if (s == u"SMALLINT")
+ return MIMER_T_SMALLINT;
+ if (s == u"DATE")
+ return MIMER_DATE;
+ if (s == u"TIME")
+ return MIMER_TIME;
+ if (s == u"TIMESTAMP")
+ return MIMER_TIMESTAMP;
+ if (s == u"BUILTIN.UUID")
+ return MIMER_UUID;
+ if (s == u"USER-DEFINED")
+ return MIMER_DEFAULT_DATATYPE;
+ qWarning() << "QMimerSQLDriver::qLookupMimDataType: Unhandled data type: " << s;
+ return MIMER_DEFAULT_DATATYPE;
+}
+
+QVariant QMimerSQLResult::handle() const
+{
+ Q_D(const QMimerSQLResult);
+ return QVariant::fromValue(d->statementhandle);
+}
+
+void QMimerSQLResult::cleanup()
+{
+ Q_D(QMimerSQLResult);
+ if (!driver() || !driver()->isOpen()) {
+ d->openCursor = false;
+ d->openStatement = false;
+ return;
+ }
+ if (d->openCursor) {
+ const int32_t err = MimerCloseCursor(d->statementhandle);
+ if (!MIMER_SUCCEEDED(err))
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Could not close cursor"), err,
+ QSqlError::StatementError, d->drv_d_func()));
+ d->openCursor = false;
+ }
+ if (d->openStatement) {
+ const int32_t err = MimerEndStatement(&d->statementhandle);
+ if (!MIMER_SUCCEEDED(err))
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Could not close statement"),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ d->openStatement = false;
+ }
+ d->currentSize = -1;
+}
+
+qint64 QMimerSQLResult::currentRow()
+{
+ Q_D(const QMimerSQLResult);
+ return d->currentRow;
+}
+
+bool QMimerSQLResult::fetch(int i)
+{
+ Q_D(const QMimerSQLResult);
+ int32_t err = 0;
+ if (!isActive() || !isSelect())
+ return false;
+ if (i == at())
+ return true;
+ if (i < 0)
+ return false;
+
+ if (isForwardOnly() && i < at())
+ return false;
+
+ if (isForwardOnly()) {
+ bool rc;
+ do {
+ rc = fetchNext();
+ } while (rc && currentRow() < i);
+ return rc;
+ } else {
+ err = MimerFetchScroll(d->statementhandle, MIMER_ABSOLUTE, i + 1);
+ if (err == MIMER_NO_DATA)
+ return false;
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(QCoreApplication::translate("QMimerSQLResult", "Fetch did not succeed"),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ setAt(MimerCurrentRow(d->statementhandle) - 1);
+ return true;
+}
+
+bool QMimerSQLResult::fetchFirst()
+{
+ Q_D(const QMimerSQLResult);
+ int32_t err = 0;
+ if (!isActive() || !isSelect())
+ return false;
+ if (isForwardOnly()) {
+ if (currentRow() < 0)
+ return fetchNext();
+ else if (currentRow() == 0)
+ setAt(0);
+ else
+ return false;
+ } else {
+ err = MimerFetchScroll(d->statementhandle, MIMER_FIRST, 0);
+ if (MIMER_SUCCEEDED(err) && err != MIMER_NO_DATA)
+ setAt(0);
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Fetch first did not succeed"), err,
+ QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ if (err == MIMER_NO_DATA)
+ return false;
+ return true;
+}
+
+bool QMimerSQLResult::fetchLast()
+{
+ Q_D(const QMimerSQLResult);
+ int32_t err = 0;
+ int row = 0;
+ if (!isActive() || !isSelect())
+ return false;
+ if (isForwardOnly()) {
+ bool rc;
+ do {
+ rc = fetchNext();
+ } while (rc);
+
+ return currentRow() >= 0;
+ } else {
+ err = MimerFetchScroll(d->statementhandle, static_cast<std::int32_t>(MIMER_LAST), 0);
+ if (err == MIMER_NO_DATA)
+ return false;
+ if (MIMER_SUCCEEDED(err)) {
+ row = MimerCurrentRow(d->statementhandle) - 1;
+ } else {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult:", "Fetch last did not succeed"),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ }
+
+ if (row < 0) {
+ setAt(QSql::BeforeFirstRow);
+ return false;
+ } else {
+ setAt(row);
+ return true;
+ }
+}
+
+bool QMimerSQLResult::fetchNext()
+{
+ Q_D(QMimerSQLResult);
+ int32_t err = 0;
+ if (!isActive() || !isSelect())
+ return false;
+ if (isForwardOnly())
+ err = MimerFetch(d->statementhandle);
+ else
+ err = MimerFetchScroll(d->statementhandle, MIMER_NEXT, 0);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Could not fetch next row"), err,
+ QSqlError::StatementError, d->drv_d_func()));
+ if (isForwardOnly())
+ d->currentRow = QSql::BeforeFirstRow;
+ return false;
+ }
+ if (err == MIMER_NO_DATA)
+ return false;
+ if (isForwardOnly())
+ setAt(++d->currentRow);
+ else
+ setAt(MimerCurrentRow(d->statementhandle) - 1);
+ return true;
+}
+
+QVariant QMimerSQLResult::data(int i)
+{
+ Q_D(QMimerSQLResult);
+ int32_t err;
+ int32_t mType;
+ if (d->callWithOut) {
+ if (i >= MimerParameterCount(d->statementhandle)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult:", "Column %1 out of range")
+ .arg(i),
+ genericError, QSqlError::StatementError, nullptr));
+ return QVariant();
+ }
+ mType = MimerParameterType(d->statementhandle, static_cast<std::int16_t>(i) + 1);
+ } else {
+ if (i >= MimerColumnCount(d->statementhandle)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult:", "Column %1 out of range")
+ .arg(i),
+ genericError, QSqlError::StatementError, nullptr));
+ return QVariant();
+ }
+ mType = MimerColumnType(d->statementhandle, static_cast<std::int16_t>(i) + 1);
+ }
+ const QMetaType::Type type = qDecodeMSQLType(mType);
+ const MimerColumnTypes mimDataType = mimerMapColumnTypes(mType);
+ err = MimerIsNull(d->statementhandle, static_cast<std::int16_t>(i) + 1);
+ if (err > 0) {
+ return QVariant(QMetaType(type), nullptr);
+ } else {
+ switch (mimDataType) {
+ case MimerColumnTypes::Date: {
+ wchar_t dateString_w[maxDateStringSize + 1];
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i) + 1, dateString_w,
+ sizeof(dateString_w) / sizeof(dateString_w[0]));
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("date", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return QDate::fromString(QString::fromWCharArray(dateString_w), "yyyy-MM-dd"_L1);
+ }
+ case MimerColumnTypes::Time: {
+ wchar_t timeString_w[maxTimeStringSize + 1];
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i) + 1, timeString_w,
+ sizeof(timeString_w) / sizeof(timeString_w[0]));
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("time", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ QString timeString = QString::fromWCharArray(timeString_w);
+ QString timeFormatString = "HH:mm:ss"_L1;
+ if (timeString.size() > 8) {
+ timeFormatString.append(".zzz"_L1);
+ timeString = timeString.left(12);
+ }
+ return QTime::fromString(timeString, timeFormatString);
+ }
+ case MimerColumnTypes::Timestamp: {
+ wchar_t dateTimeString_w[maxTimestampStringSize + 1];
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i) + 1,
+ dateTimeString_w,
+ sizeof(dateTimeString_w) / sizeof(dateTimeString_w[0]));
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotGet("date time", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ QString dateTimeString = QString::fromWCharArray(dateTimeString_w);
+ QString dateTimeFormatString = "yyyy-MM-dd HH:mm:ss"_L1;
+ if (dateTimeString.size() > 19) {
+ dateTimeFormatString.append(".zzz"_L1);
+ dateTimeString = dateTimeString.left(23);
+ }
+ return QDateTime::fromString(dateTimeString, dateTimeFormatString);
+ }
+ case MimerColumnTypes::Int: {
+ int resInt;
+ err = MimerGetInt32(d->statementhandle, static_cast<std::int16_t>(i) + 1, &resInt);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("int32", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return resInt;
+ }
+ case MimerColumnTypes::Long: {
+ int64_t resLongLong;
+ err = MimerGetInt64(d->statementhandle, static_cast<std::int16_t>(i) + 1, &resLongLong);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("int64", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return QString::number(resLongLong).toLongLong();
+ }
+ case MimerColumnTypes::Boolean: {
+ err = MimerGetBoolean(d->statementhandle, static_cast<std::int16_t>(i) + 1);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotGet("boolean", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return err == 1;
+ }
+ case MimerColumnTypes::Float: {
+ float resFloat;
+ err = MimerGetFloat(d->statementhandle, static_cast<std::int16_t>(i) + 1, &resFloat);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("float", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return resFloat;
+ }
+ case MimerColumnTypes::Double: {
+ double resDouble;
+ err = MimerGetDouble(d->statementhandle, static_cast<std::int16_t>(i) + 1, &resDouble);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotGet("double", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ switch (numericalPrecisionPolicy()) {
+ case QSql::LowPrecisionInt32:
+ return static_cast<std::int32_t>(resDouble);
+ case QSql::LowPrecisionInt64:
+ return static_cast<qint64>(resDouble);
+ case QSql::LowPrecisionDouble:
+ return static_cast<qreal>(resDouble);
+ case QSql::HighPrecision:
+ return QString::number(resDouble, 'g', 17);
+ }
+ return QVariant(QMetaType(type), nullptr);
+ }
+ case MimerColumnTypes::Binary: {
+ QByteArray byteArray;
+ // Get size
+ err = MimerGetBinary(d->statementhandle, static_cast<std::int16_t>(i) + 1, NULL, 0);
+ if (MIMER_SUCCEEDED(err)) {
+ byteArray.resize(err);
+ err = MimerGetBinary(d->statementhandle, static_cast<std::int16_t>(i) + 1,
+ byteArray.data(), err);
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotGet("binary", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return byteArray;
+ }
+ case MimerColumnTypes::Blob: {
+ QByteArray byteArray;
+ size_t size;
+ err = MimerGetLob(d->statementhandle, static_cast<std::int16_t>(i) + 1, &size,
+ &d->lobhandle);
+ if (MIMER_SUCCEEDED(err)) {
+ constexpr size_t maxSize = lobChunkMaxSizeFetch;
+ QVarLengthArray<char> blobchar(lobChunkMaxSizeFetch);
+ byteArray.reserve(size);
+ size_t left_to_return = size;
+ while (left_to_return > 0) {
+ const size_t bytesToReceive =
+ left_to_return <= maxSize ? left_to_return : maxSize;
+ err = MimerGetBlobData(&d->lobhandle, blobchar.data(), bytesToReceive);
+ byteArray.append(QByteArray::fromRawData(blobchar.data(), bytesToReceive));
+ left_to_return -= bytesToReceive;
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("BLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ }
+ } else {
+ setLastError(qMakeError(msgCouldNotGet("BLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return byteArray;
+ }
+ case MimerColumnTypes::Numeric:
+ case MimerColumnTypes::String: {
+ wchar_t resString_w[maxStackStringSize + 1];
+ // Get size
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i) + 1, resString_w,
+ 0);
+ if (MIMER_SUCCEEDED(err)) {
+ int size = err;
+ if (err <= maxStackStringSize) { // For smaller strings, use a small buffer for
+ // efficiency
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i) + 1,
+ resString_w, maxStackStringSize + 1);
+ if (MIMER_SUCCEEDED(err))
+ return QString::fromWCharArray(resString_w);
+ } else { // For larger strings, dynamically allocate memory
+ QVarLengthArray<wchar_t> largeResString_w(size + 1);
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i) + 1,
+ largeResString_w.data(), size + 1);
+ if (MIMER_SUCCEEDED(err))
+ return QString::fromWCharArray(largeResString_w.data());
+ }
+ }
+ setLastError(qMakeError(msgCouldNotGet(
+ mimDataType == MimerColumnTypes::Numeric ? "numeric" : "string", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ case MimerColumnTypes::Clob: {
+ size_t size;
+ err = MimerGetLob(d->statementhandle, static_cast<std::int16_t>(i) + 1, &size,
+ &d->lobhandle);
+ if (MIMER_SUCCEEDED(err)) {
+ constexpr size_t maxSize = lobChunkMaxSizeFetch;
+ QVarLengthArray<wchar_t> clobstring_w(lobChunkMaxSizeFetch + 1);
+
+ size_t left_to_return = size;
+ QString returnString;
+ while (left_to_return > 0) {
+ const size_t bytesToReceive =
+ left_to_return <= maxSize ? left_to_return : maxSize;
+ err = MimerGetNclobData(&d->lobhandle, clobstring_w.data(), bytesToReceive + 1);
+ returnString.append(QString::fromWCharArray(clobstring_w.data()));
+ left_to_return -= bytesToReceive;
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("CLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ }
+ return returnString;
+ }
+ setLastError(qMakeError(msgCouldNotGet("CLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ case MimerColumnTypes::Uuid: {
+ unsigned char uuidChar[16];
+ err = MimerGetUUID(d->statementhandle, static_cast<std::int16_t>(i) + 1, uuidChar);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("UUID", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ const QByteArray uuidByteArray = QByteArray(reinterpret_cast<char *>(uuidChar), 16);
+ return QUuid::fromRfc4122(uuidByteArray);
+ }
+ case MimerColumnTypes::Unknown:
+ default:
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Unknown data type %1").arg(i),
+ genericError, QSqlError::StatementError, nullptr));
+ }
+ return QVariant(QMetaType(type), nullptr);
+ }
+}
+
+bool QMimerSQLResult::isNull(int index)
+{
+ Q_D(const QMimerSQLResult);
+ const int32_t rc = MimerIsNull(d->statementhandle, static_cast<std::int16_t>(index) + 1);
+ if (!MIMER_SUCCEEDED(rc)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Could not check null, column %1")
+ .arg(index),
+ rc, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ return rc != 0;
+}
+
+bool QMimerSQLResult::reset(const QString &query)
+{
+ if (!prepare(query))
+ return false;
+ return exec();
+}
+
+int QMimerSQLResult::size()
+{
+ Q_D(QMimerSQLResult);
+ if (!isActive() || !isSelect() || isForwardOnly())
+ return -1;
+
+ if (d->currentSize != -1)
+ return d->currentSize;
+
+ const int currentRow = MimerCurrentRow(d->statementhandle);
+ MimerFetchScroll(d->statementhandle, static_cast<std::int32_t>(MIMER_LAST), 0);
+ int size = MimerCurrentRow(d->statementhandle);
+ if (!MIMER_SUCCEEDED(size))
+ size = -1;
+ MimerFetchScroll(d->statementhandle, MIMER_ABSOLUTE, currentRow);
+ d->currentSize = size;
+ return size;
+}
+
+int QMimerSQLResult::numRowsAffected()
+{
+ Q_D(const QMimerSQLResult);
+ return d->rowsAffected;
+}
+
+QSqlRecord QMimerSQLResult::record() const
+{
+ Q_D(const QMimerSQLResult);
+ QSqlRecord rec;
+ if (!isActive() || !isSelect() || !driver())
+ return rec;
+ QSqlField field;
+ const int colSize = MimerColumnCount(d->statementhandle);
+ for (int i = 0; i < colSize; i++) {
+ wchar_t colName_w[100];
+ MimerColumnName(d->statementhandle, static_cast<std::int16_t>(i) + 1, colName_w,
+ sizeof(colName_w) / sizeof(colName_w[0]));
+ field.setName(QString::fromWCharArray(colName_w));
+ const int32_t mType = MimerColumnType(d->statementhandle, static_cast<std::int16_t>(i) + 1);
+ const QMetaType::Type type = qDecodeMSQLType(mType);
+ field.setMetaType(QMetaType(type));
+ field.setValue(QVariant(field.metaType()));
+ // field.setPrecision(); Should be implemented once the Mimer API can give this
+ // information.
+ // field.setLength(); Should be implemented once the Mimer API can give
+ // this information.
+ rec.insert(i, field);
+ }
+ return rec;
+}
+
+bool QMimerSQLResult::prepare(const QString &query)
+{
+ Q_D(QMimerSQLResult);
+ int32_t err;
+ if (!driver())
+ return false;
+ if (!d->preparedQuery)
+ return QSqlResult::prepare(query);
+ if (query.isEmpty())
+ return false;
+ cleanup();
+ const int option = isForwardOnly() ? MIMER_FORWARD_ONLY : MIMER_SCROLLABLE;
+ err = MimerBeginStatement8(d->drv_d_func()->sessionhandle, query.toUtf8().constData(), option,
+ &d->statementhandle);
+ if (err == MIMER_STATEMENT_CANNOT_BE_PREPARED) {
+ err = MimerExecuteStatement8(d->drv_d_func()->sessionhandle, query.toUtf8().constData());
+ if (MIMER_SUCCEEDED(err)) {
+ d->executedStatement = true;
+ d->openCursor = false;
+ d->openStatement = false;
+ return true;
+ }
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(QCoreApplication::translate("QMimerSQLResult",
+ "Could not prepare/execute statement"),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ d->openStatement = true;
+ return true;
+}
+
+bool QMimerSQLResult::exec()
+{
+ Q_D(QMimerSQLResult);
+ int32_t err;
+ if (!driver())
+ return false;
+ if (!d->preparedQuery)
+ return QSqlResult::exec();
+ if (d->executedStatement) {
+ d->executedStatement = false;
+ return true;
+ }
+ if (d->openCursor) {
+ setAt(QSql::BeforeFirstRow);
+ err = MimerCloseCursor(d->statementhandle);
+ d->openCursor = false;
+ d->currentSize = -1;
+ }
+ QVector<QVariant> &values = boundValues();
+ if (d->execBatch)
+ values = d->batch_vector;
+ int mimParamCount = MimerParameterCount(d->statementhandle);
+ if (!MIMER_SUCCEEDED(mimParamCount))
+ mimParamCount = 0;
+ if (mimParamCount != values.size()) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Wrong number of parameters"),
+ genericError, QSqlError::StatementError, nullptr));
+ return false;
+ }
+ for (int i = 0; i < mimParamCount; i++) {
+ if (bindValueType(i) == QSql::Out) {
+ d->callWithOut = true;
+ continue;
+ }
+ const QVariant &val = values.at(i);
+ if (QSqlResultPrivate::isVariantNull(val) || val.isNull() || val.toString().isNull()) {
+ err = MimerSetNull(d->statementhandle, i + 1);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("null", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ continue;
+ }
+
+ const int mimParamType = MimerParameterType(d->statementhandle, i + 1);
+ const MimerColumnTypes mimDataType = mimerMapColumnTypes(mimParamType);
+ switch (mimDataType) {
+ case MimerColumnTypes::Int: {
+ bool convertOk;
+ err = MimerSetInt32(d->statementhandle, i + 1, val.toInt(&convertOk));
+ if (!convertOk || !MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("int32", i),
+ convertOk ? err : genericError, QSqlError::StatementError,
+ convertOk ? d->drv_d_func() : nullptr));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Long: {
+ bool convertOk;
+ err = MimerSetInt64(d->statementhandle, i + 1, val.toLongLong(&convertOk));
+ if (!convertOk || !MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("int64", i),
+ convertOk ? err : genericError, QSqlError::StatementError,
+ convertOk ? d->drv_d_func() : nullptr));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Float: {
+ bool convertOk;
+ err = MimerSetFloat(d->statementhandle, i + 1, val.toFloat(&convertOk));
+ if (!convertOk || !MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("float", i),
+ convertOk ? err : genericError, QSqlError::StatementError,
+ convertOk ? d->drv_d_func() : nullptr));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Double: {
+ bool convertOk;
+ err = MimerSetDouble(d->statementhandle, i + 1, val.toDouble(&convertOk));
+ if (!convertOk || !MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("double", i),
+ convertOk ? err : genericError, QSqlError::StatementError,
+ convertOk ? d->drv_d_func() : nullptr));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Binary: {
+ const QByteArray binArr = val.toByteArray();
+ size_t size = static_cast<std::size_t>(binArr.size());
+ err = MimerSetBinary(d->statementhandle, i + 1, binArr.data(), size);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("binary", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Boolean: {
+ err = MimerSetBoolean(d->statementhandle, i + 1, val.toBool() == true ? 1 : 0);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("boolean", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Uuid: {
+ const QByteArray uuidArray =
+ QByteArray::fromHex(val.toUuid().toString(QUuid::WithoutBraces).toLatin1());
+ const unsigned char *uuid =
+ reinterpret_cast<const unsigned char *>(uuidArray.constData());
+ err = MimerSetUUID(d->statementhandle, i + 1, uuid);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("UUID", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Numeric:
+ case MimerColumnTypes::String: {
+ QByteArray string_b = val.toString().trimmed().toUtf8();
+ const char *string_u = string_b.constData();
+ err = MimerSetString8(d->statementhandle, i + 1, string_u);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet(
+ mimDataType == MimerColumnTypes::Numeric ? "numeric" : "string", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Date: {
+ err = MimerSetString8(d->statementhandle, i + 1, val.toString().toUtf8().constData());
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("date", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Time: {
+ QString timeFormatString = "hh:mm:ss"_L1;
+ const QTime timeVal = val.toTime();
+ if (timeVal.msec() > 0)
+ timeFormatString.append(".zzz"_L1);
+ err = MimerSetString8(d->statementhandle, i + 1,
+ timeVal.toString(timeFormatString).toUtf8().constData());
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("time", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Timestamp: {
+ QString dateTimeFormatString = "yyyy-MM-dd hh:mm:ss"_L1;
+ const QDateTime dateTimeVal = val.toDateTime();
+ if (dateTimeVal.time().msec() > 0)
+ dateTimeFormatString.append(".zzz"_L1);
+ err = MimerSetString8(
+ d->statementhandle, i + 1,
+ val.toDateTime().toString(dateTimeFormatString).toUtf8().constData());
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotSet("datetime", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Blob: {
+ QByteArray blobArr = val.toByteArray();
+ const char *blobData = blobArr.constData();
+ qsizetype size = blobArr.size();
+ err = MimerSetLob(d->statementhandle, i + 1, size, &d->lobhandle);
+ if (MIMER_SUCCEEDED(err)) {
+ qsizetype maxSize = lobChunkMaxSizeSet;
+ if (size > maxSize) {
+ qsizetype left_to_send = size;
+ for (qsizetype k = 0; left_to_send > 0; k++) {
+ if (left_to_send <= maxSize) {
+ err = MimerSetBlobData(&d->lobhandle, &blobData[k * maxSize],
+ left_to_send);
+ left_to_send = 0;
+ } else {
+ err = MimerSetBlobData(&d->lobhandle, &blobData[k * maxSize], maxSize);
+ left_to_send = left_to_send - maxSize;
+ }
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("BLOB byte array", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ } else {
+ err = MimerSetBlobData(&d->lobhandle, blobArr, size);
+ }
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotSet("BLOB byte array", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Clob: {
+ QByteArray string_b = val.toString().trimmed().toUtf8();
+ const char *string_u = string_b.constData();
+ size_t size_c = 1;
+ size_t size = 0;
+ while (string_u[size++])
+ if ((string_u[size] & 0xc0) != 0x80)
+ size_c++;
+ err = MimerSetLob(d->statementhandle, i + 1, size_c, &d->lobhandle);
+ if (MIMER_SUCCEEDED(err)) {
+ constexpr size_t maxSize = lobChunkMaxSizeSet;
+ if (size > maxSize) {
+ size_t left_to_send = size;
+ size_t pos = 0;
+ uint step_back = 0;
+ while (left_to_send > 0 && step_back < maxSize) {
+ step_back = 0;
+ if (left_to_send <= maxSize) {
+ err = MimerSetNclobData8(&d->lobhandle, &string_u[pos], left_to_send);
+ left_to_send = 0;
+ } else {
+ // Check that we don't split a multi-byte utf-8 characters
+ while (pos + maxSize - step_back > 0
+ && (string_u[pos + maxSize - step_back] & 0xc0) == 0x80)
+ step_back++;
+ err = MimerSetNclobData8(&d->lobhandle, &string_u[pos],
+ maxSize - step_back);
+ left_to_send = left_to_send - maxSize + step_back;
+ pos += maxSize - step_back;
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotSet("CLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ }
+ } else {
+ err = MimerSetNclobData8(&d->lobhandle, string_u, size);
+ }
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("CLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Unknown:
+ default:
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Unknown datatype, parameter %1")
+ .arg(i),
+ genericError, QSqlError::StatementError, nullptr));
+ return false;
+ }
+ }
+ if (d->execBatch)
+ return true;
+ err = MimerExecute(d->statementhandle);
+ if (MIMER_SUCCEEDED(err)) {
+ d->rowsAffected = err;
+ int k = 0;
+ for (qsizetype i = 0; i < values.size(); i++) {
+ if (bindValueType(i) == QSql::Out || bindValueType(i) == QSql::InOut) {
+ bindValue(i, data(k), QSql::In);
+ k++;
+ }
+ }
+ d->callWithOut = false;
+ }
+ setSelect(false);
+ if (MIMER_SEQUENCE_ERROR == err) {
+ err = MimerOpenCursor(d->statementhandle);
+ d->rowsAffected = err;
+ d->openCursor = true;
+ d->currentRow = QSql::BeforeFirstRow;
+ setSelect(true);
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(QCoreApplication::translate("QMimerSQLResult",
+ "Could not execute statement/open cursor"),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ setActive(true);
+ return true;
+}
+
+bool QMimerSQLResult::execBatch(bool arrayBind)
+{
+ Q_D(QMimerSQLResult);
+ Q_UNUSED(arrayBind);
+ int32_t err;
+ const QVector<QVariant> values = boundValues();
+
+ // Check that we only have input parameters. Currently
+ // we can only handle batch operations without output parameters.
+ for (qsizetype i = 0; i < values.first().toList().size(); i++)
+ if (bindValueType(i) == QSql::Out || bindValueType(i) == QSql::InOut) {
+ setLastError(qMakeError(QCoreApplication::translate(
+ "QMimerSQLResult",
+ "Only input parameters can be used in batch operations"),
+ genericError, QSqlError::StatementError, nullptr));
+ d->execBatch = false;
+ return false;
+ }
+ d->execBatch = true;
+ for (qsizetype i = 0; i < values.first().toList().size(); i++) {
+ for (qsizetype j = 0; j < values.size(); j++)
+ d->batch_vector.append(values.at(j).toList().at(i));
+ exec();
+ if (i != (values.at(0).toList().size() - 1)) {
+ err = MimerAddBatch(d->statementhandle);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ //: %1 is the batch number
+ QCoreApplication::translate("QMimerSQLResult", "Could not add batch %1")
+ .arg(i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ d->execBatch = false;
+ return false;
+ }
+ }
+ d->batch_vector.clear();
+ }
+ d->execBatch = false;
+ err = MimerExecute(d->statementhandle);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Could not execute batch"), err,
+ QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ return true;
+}
+
+QVariant QMimerSQLResult::lastInsertId() const
+{
+ Q_D(const QMimerSQLResult);
+ int64_t lastSequence;
+ const int32_t err = MimerGetSequenceInt64(d->statementhandle, &lastSequence);
+ if (!MIMER_SUCCEEDED(err))
+ return QVariant(QMetaType(QMetaType::LongLong), nullptr);
+ return QVariant(qint64(lastSequence));
+}
+
+bool QMimerSQLDriver::hasFeature(DriverFeature f) const
+{
+ switch (f) {
+ case NamedPlaceholders: // Is true in reality but Qt parses Sql statement...
+ case EventNotifications:
+ case LowPrecisionNumbers:
+ case MultipleResultSets:
+ case SimpleLocking:
+ case CancelQuery:
+ return false;
+ case FinishQuery:
+ case LastInsertId:
+ case Transactions:
+ case QuerySize:
+ case BLOB:
+ case Unicode:
+ case PreparedQueries:
+ case PositionalPlaceholders:
+ case BatchOperations:
+ return true;
+ }
+ return true;
+}
+
+bool QMimerSQLDriver::open(const QString &db, const QString &user, const QString &password,
+ const QString &host, int port, const QString &connOpts)
+{
+ Q_D(QMimerSQLDriver);
+ Q_UNUSED(host);
+ Q_UNUSED(port);
+ Q_UNUSED(connOpts);
+ if (isOpen())
+ close();
+ const int32_t err = MimerBeginSession8(db.toUtf8().constData(), user.toUtf8().constData(),
+ password.toUtf8().constData(), &d->sessionhandle);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLDriver", "Could not connect to database")
+ + " "_L1 + db,
+ err, QSqlError::ConnectionError, nullptr));
+ setOpenError(true);
+ return false;
+ }
+ d->dbUser = user;
+ d->dbName = db;
+ setOpen(true);
+ setOpenError(false);
+ return true;
+}
+
+void QMimerSQLDriver::close()
+{
+ Q_D(QMimerSQLDriver);
+ if (isOpen()) {
+ const int end_err = MimerEndSession(&d->sessionhandle);
+ if (MIMER_SUCCEEDED(end_err)) {
+ setOpen(false);
+ setOpenError(false);
+ }
+ }
+}
+
+QSqlResult *QMimerSQLDriver::createResult() const
+{
+ return new QMimerSQLResult(this);
+}
+
+QStringList QMimerSQLDriver::tables(QSql::TableType type) const
+{
+ QStringList tl;
+ if (!isOpen())
+ return tl;
+ QSqlQuery t(createResult());
+ QString sql;
+ switch (type) {
+ case QSql::Tables: {
+ sql = "select table_name from information_schema.tables where "
+ "table_type=\'BASE TABLE\' AND table_schema = CURRENT_USER"_L1;
+ break;
+ }
+ case QSql::SystemTables: {
+ sql = "select table_name from information_schema.tables where "
+ "table_type=\'BASE TABLE\' AND table_schema = \'SYSTEM\'"_L1;
+ break;
+ }
+ case QSql::Views: {
+ sql = "select table_name from information_schema.tables where "
+ "table_type=\'VIEW\' AND table_schema = CURRENT_USER"_L1;
+ break;
+ }
+ case QSql::AllTables: {
+ sql = "select table_name from information_schema.tables where "
+ "(table_type=\'VIEW\' or table_type=\'BASE TABLE\')"
+ " AND (table_schema = CURRENT_USER OR table_schema =\'SYSTEM\')"_L1;
+ break;
+ }
+ default:
+ break;
+ }
+ if (sql.length() > 0) {
+ t.exec(sql);
+ while (t.next())
+ tl.append(t.value(0).toString());
+ }
+ return tl;
+}
+
+QSqlIndex QMimerSQLDriver::primaryIndex(const QString &tablename) const
+{
+ Q_D(const QMimerSQLDriver);
+ if (!isOpen())
+ return QSqlIndex();
+ QString table = tablename;
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+ QSqlIndex index(tablename);
+ QSqlQuery t(createResult());
+ QString schema;
+ QString qualifiedName = table;
+ d->splitTableQualifier(qualifiedName, &schema, &table);
+ QString sql =
+ "select information_schema.ext_access_paths.column_name,"
+ "case when data_type = 'INTERVAL' then 'INTERVAL '|| interval_type "
+ "when data_type = 'INTEGER' and numeric_precision > 10 then 'BIGINT' "
+ "when data_type = 'INTEGER' and numeric_precision <= 10 AND NUMERIC_PRECISION > 5 "
+ "then 'INTEGER' when data_type = 'INTEGER' and numeric_precision <= 5 then 'SMALLINT' "
+ "else upper(data_type) end as data_type "
+ "from information_schema.ext_access_paths full outer join "
+ "information_schema.columns on information_schema.ext_access_paths.column_name = "
+ "information_schema.columns.column_name and "
+ "information_schema.ext_access_paths.table_name = "
+ "information_schema.columns.table_name where "
+ "information_schema.ext_access_paths.table_name = \'"_L1;
+ sql.append(table)
+ .append("\' and index_type = \'PRIMARY KEY\'"_L1);
+ if (schema.length() == 0)
+ sql.append(" and table_schema = CURRENT_USER"_L1);
+ else
+ sql.append(" and table_schema = \'"_L1).append(schema).append("\'"_L1);
+
+ if (!t.exec(sql))
+ return QSqlIndex();
+ int i = 0;
+ while (t.next()) {
+ QSqlField field(t.value(0).toString(),
+ QMetaType(qDecodeMSQLType(qLookupMimDataType(t.value(1).toString()))),
+ tablename);
+ index.insert(i, field);
+ index.setName(t.value(0).toString());
+ i++;
+ }
+ return index;
+}
+
+QSqlRecord QMimerSQLDriver::record(const QString &tablename) const
+{
+ Q_D(const QMimerSQLDriver);
+ if (!isOpen())
+ return QSqlRecord();
+ QSqlRecord rec;
+ QSqlQuery t(createResult());
+ QString qualifiedName = tablename;
+ if (isIdentifierEscaped(qualifiedName, QSqlDriver::TableName))
+ qualifiedName = stripDelimiters(qualifiedName, QSqlDriver::TableName);
+ QString schema, table;
+ d->splitTableQualifier(qualifiedName, &schema, &table);
+
+ QString sql =
+ "select column_name, case when data_type = 'INTERVAL' then 'INTERVAL '|| interval_type "
+ "when data_type = 'INTEGER' and numeric_precision > 10 then 'BIGINT' "
+ "when data_type = 'INTEGER' and numeric_precision <= 10 AND numeric_precision > 5 "
+ "then 'INTEGER' when data_type = 'INTEGER' and numeric_precision <= 5 then 'SMALLINT' "
+ "else UPPER(data_type) end as data_type, case when is_nullable = 'YES' then false else "
+ "true end as required, "
+ "coalesce(numeric_precision, coalesce(datetime_precision,coalesce(interval_precision, "
+ "-1))) as prec from information_schema.columns where table_name = \'"_L1;
+ if (schema.length() == 0)
+ sql.append(table).append("\' and table_schema = CURRENT_USER"_L1);
+ else
+ sql.append(table).append("\' and table_schema = \'"_L1).append(schema).append("\'"_L1);
+ sql.append(" order by ordinal_position"_L1);
+ if (!t.exec(sql))
+ return QSqlRecord();
+
+ while (t.next()) {
+ QSqlField field(t.value(0).toString(),
+ QMetaType(qDecodeMSQLType(qLookupMimDataType(t.value(1).toString()))),
+ tablename);
+ field.setRequired(t.value(3).toBool());
+ if (t.value(3).toInt() != -1)
+ field.setPrecision(t.value(3).toInt());
+ rec.append(field);
+ }
+
+ return rec;
+}
+
+QVariant QMimerSQLDriver::handle() const
+{
+ Q_D(const QMimerSQLDriver);
+ return QVariant::fromValue(d->sessionhandle);
+}
+
+QString QMimerSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
+{
+ Q_UNUSED(type);
+ QString res = identifier;
+ if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"')) {
+ res.replace(u'"', "\"\""_L1);
+ res = u'"' + res + u'"';
+ res.replace(u'.', "\".\""_L1);
+ }
+ return res;
+}
+
+bool QMimerSQLDriver::beginTransaction()
+{
+ Q_D(const QMimerSQLDriver);
+ const int32_t err = MimerBeginTransaction(d->sessionhandle, MIMER_TRANS_READWRITE);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLDriver", "Could not start transaction"), err,
+ QSqlError::TransactionError, d));
+ return false;
+ }
+ return true;
+}
+
+bool QMimerSQLDriver::commitTransaction()
+{
+ Q_D(const QMimerSQLDriver);
+ const int32_t err = MimerEndTransaction(d->sessionhandle, MIMER_COMMIT);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLDriver", "Could not commit transaction"), err,
+ QSqlError::TransactionError, d));
+ return false;
+ }
+ return true;
+}
+
+bool QMimerSQLDriver::rollbackTransaction()
+{
+ Q_D(const QMimerSQLDriver);
+ const int32_t err = MimerEndTransaction(d->sessionhandle, MIMER_ROLLBACK);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLDriver", "Could not roll back transaction"),
+ err, QSqlError::TransactionError, d));
+ return false;
+ }
+ return true;
+}
+
+void QMimerSQLDriverPrivate::splitTableQualifier(const QString &qualifiedName, QString *schema,
+ QString *table) const
+{
+ const QList<QStringView> l = QStringView(qualifiedName).split(u'.');
+ int n = l.count();
+ if (n > 2) {
+ return; // can't possibly be a valid table qualifier
+ } else if (n == 1) {
+ *schema = QString();
+ *table = l.at(0).toString();
+ } else {
+ *schema = l.at(0).toString();
+ *table = l.at(1).toString();
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsql_mimer.cpp"
diff --git a/src/plugins/sqldrivers/mimer/qsql_mimer.h b/src/plugins/sqldrivers/mimer/qsql_mimer.h
new file mode 100644
index 0000000000..03ad8f21f2
--- /dev/null
+++ b/src/plugins/sqldrivers/mimer/qsql_mimer.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2022 Mimer Information Technology
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QSQL_MIMER_H
+#define QSQL_MIMER_H
+
+#include <QtSql/qsqldriver.h>
+#include <QUuid>
+#include <mimerapi.h>
+
+#ifdef QT_PLUGIN
+# define Q_EXPORT_SQLDRIVER_MIMER
+#else
+# define Q_EXPORT_SQLDRIVER_MIMER Q_SQL_EXPORT
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QMimerSQLDriverPrivate;
+
+class Q_EXPORT_SQLDRIVER_MIMER QMimerSQLDriver : public QSqlDriver
+{
+ friend class QMimerSQLResultPrivate;
+ Q_DECLARE_PRIVATE(QMimerSQLDriver)
+ Q_OBJECT
+public:
+ explicit QMimerSQLDriver(QObject *parent = nullptr);
+ explicit QMimerSQLDriver(MimerSession *conn, QObject *parent = nullptr);
+ ~QMimerSQLDriver() override;
+ bool hasFeature(DriverFeature f) const override;
+ bool open(const QString &db, const QString &user, const QString &password, const QString &host,
+ int port, const QString &connOpts) override;
+ void close() override;
+ QSqlResult *createResult() const override;
+ QStringList tables(QSql::TableType type) const override;
+ QSqlIndex primaryIndex(const QString &tablename) const override;
+ QSqlRecord record(const QString &tablename) const override;
+ QVariant handle() const override;
+ QString escapeIdentifier(const QString &identifier, IdentifierType type) const override;
+protected:
+ bool beginTransaction() override;
+ bool commitTransaction() override;
+ bool rollbackTransaction() override;
+
+private:
+};
+
+QT_END_NAMESPACE
+
+#endif // QSQL_MIMER
diff --git a/src/plugins/sqldrivers/mysql/CMakeLists.txt b/src/plugins/sqldrivers/mysql/CMakeLists.txt
index 8e8244acb3..2e3d028584 100644
--- a/src/plugins/sqldrivers/mysql/CMakeLists.txt
+++ b/src/plugins/sqldrivers/mysql/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from mysql.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## QMYSQLDriverPlugin Plugin:
@@ -6,13 +7,14 @@
qt_internal_add_plugin(QMYSQLDriverPlugin
OUTPUT_NAME qsqlmysql
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_mysql.cpp qsql_mysql_p.h
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
+ QT_NO_CONTEXTLESS_CONNECT
LIBRARIES
MySQL::MySQL
Qt::Core
@@ -20,5 +22,4 @@ qt_internal_add_plugin(QMYSQLDriverPlugin
Qt::SqlPrivate
)
-#### Keys ignored in scope 1:.:.:mysql.pro:<TRUE>:
-# OTHER_FILES = "mysql.json"
+qt_internal_force_macos_intel_arch(QMYSQLDriverPlugin)
diff --git a/src/plugins/sqldrivers/mysql/main.cpp b/src/plugins/sqldrivers/mysql/main.cpp
index f7d56dd1c9..2c10c4d348 100644
--- a/src/plugins/sqldrivers/mysql/main.cpp
+++ b/src/plugins/sqldrivers/mysql/main.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qsqldriverplugin.h>
#include <qstringlist.h>
@@ -43,6 +7,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QMYSQLDriverPlugin : public QSqlDriverPlugin
{
Q_OBJECT
@@ -61,8 +27,7 @@ QMYSQLDriverPlugin::QMYSQLDriverPlugin()
QSqlDriver* QMYSQLDriverPlugin::create(const QString &name)
{
- if (name == QLatin1String("QMYSQL") ||
- name == QLatin1String("QMARIADB")) {
+ if (name == "QMYSQL"_L1 || name == "QMARIADB"_L1) {
QMYSQLDriver* driver = new QMYSQLDriver();
return driver;
}
diff --git a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
index 3092a7f772..37d638a9e2 100644
--- a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
+++ b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsql_mysql_p.h"
#include <qcoreapplication.h>
#include <qvariant.h>
+#include <qvarlengtharray.h>
#include <qdatetime.h>
#include <qdebug.h>
#include <qfile.h>
@@ -51,6 +16,7 @@
#include <qsqlquery.h>
#include <qsqlrecord.h>
#include <qstringlist.h>
+#include <qtimezone.h>
#include <QtSql/private/qsqldriver_p.h>
#include <QtSql/private/qsqlresult_p.h>
@@ -64,12 +30,30 @@ Q_DECLARE_METATYPE(MYSQL_RES*)
Q_DECLARE_METATYPE(MYSQL*)
Q_DECLARE_METATYPE(MYSQL_STMT*)
+// MYSQL_TYPE_JSON was introduced with MySQL 5.7.9
+#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID < 50709
+#define MYSQL_TYPE_JSON 245
+#endif
+
// MySQL above version 8 removed my_bool typedef while MariaDB kept it,
// by redefining it we can regain source compatibility.
using my_bool = decltype(mysql_stmt_bind_result(nullptr, nullptr));
+// this is a copy of the old MYSQL_TIME before an additional integer was added in
+// 8.0.27.0. This kills the sanity check during retrieving this struct from mysql
+// when another libmysql version is used during runtime than during compile time
+struct QT_MYSQL_TIME
+{
+ unsigned int year, month, day, hour, minute, second;
+ unsigned long second_part; /**< microseconds */
+ my_bool neg;
+ enum enum_mysql_timestamp_type time_type;
+};
+
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QMYSQLDriverPrivate : public QSqlDriverPrivate
{
Q_DECLARE_PUBLIC(QMYSQLDriver)
@@ -78,6 +62,7 @@ public:
QMYSQLDriverPrivate() : QSqlDriverPrivate(QSqlDriver::MySqlServer)
{}
MYSQL *mysql = nullptr;
+ QString dbName;
bool preparedQuerysEnabled = false;
};
@@ -113,14 +98,45 @@ static inline QVariant qDateTimeFromString(QString &val)
#else
if (val.isEmpty())
return QVariant(QDateTime());
- if (val.length() == 14)
- // TIMESTAMPS have the format yyyyMMddhhmmss
- val.insert(4, QLatin1Char('-')).insert(7, QLatin1Char('-')).insert(10,
- QLatin1Char('T')).insert(13, QLatin1Char(':')).insert(16, QLatin1Char(':'));
+
+ // TIMESTAMPS have either the format "yyyyMMddhhmmss" or "yyyy-MM-dd
+ // hh:mm:ss". QDateTime::fromString() can convert the latter, but not the
+ // former, so adapt it if necessary.
+ if (val.size() == 14)
+ val.insert(4, u'-').insert(7, u'-').insert(10, u'T').insert(13, u':').insert(16, u':');
+
+ if (!val.endsWith(u'Z'))
+ val.append(u'Z'); // make UTC
return QVariant(QDateTime::fromString(val, Qt::ISODate));
#endif
}
+// check if this client and server version of MySQL/MariaDB support prepared statements
+static inline bool checkPreparedQueries(MYSQL *mysql)
+{
+ std::unique_ptr<MYSQL_STMT, decltype(&mysql_stmt_close)> stmt(mysql_stmt_init(mysql), &mysql_stmt_close);
+ if (!stmt)
+ return false;
+
+ static const char dummyQuery[] = "SELECT ? + ?";
+ if (mysql_stmt_prepare(stmt.get(), dummyQuery, sizeof(dummyQuery) - 1))
+ return false;
+
+ return mysql_stmt_param_count(stmt.get()) == 2;
+}
+
+// used with prepared queries and bound arguments
+static inline void setUtcTimeZone(MYSQL *mysql)
+{
+ std::unique_ptr<MYSQL_STMT, decltype(&mysql_stmt_close)> stmt(mysql_stmt_init(mysql), &mysql_stmt_close);
+ if (!stmt)
+ return;
+
+ static const char query[] = "SET time_zone = '+00:00'";
+ if (mysql_stmt_prepare(stmt.get(), query, sizeof(query) - 1))
+ mysql_stmt_execute(stmt.get());
+}
+
class QMYSQLResultPrivate;
class QMYSQLResult : public QSqlResult
@@ -172,7 +188,7 @@ public:
struct QMyField
{
char *outField = nullptr;
- MYSQL_FIELD *myField = nullptr;
+ const MYSQL_FIELD *myField = nullptr;
QMetaType type = QMetaType();
my_bool nullIndicator = false;
ulong bufLength = 0ul;
@@ -191,68 +207,70 @@ public:
bool preparedQuery = false;
};
-static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
- const QMYSQLDriverPrivate* p)
+static QSqlError qMakeError(const QString &err, QSqlError::ErrorType type,
+ const QMYSQLDriverPrivate *p)
{
- const char *cerr = p->mysql ? mysql_error(p->mysql) : 0;
- return QSqlError(QLatin1String("QMYSQL: ") + err,
+ const char *cerr = p->mysql ? mysql_error(p->mysql) : nullptr;
+ return QSqlError("QMYSQL: "_L1 + err,
QString::fromUtf8(cerr),
type, QString::number(mysql_errno(p->mysql)));
}
-static QMetaType qDecodeMYSQLType(int mysqltype, uint flags)
+static QMetaType qDecodeMYSQLType(enum_field_types mysqltype, uint flags)
{
QMetaType::Type type;
switch (mysqltype) {
- case FIELD_TYPE_TINY :
+ case MYSQL_TYPE_TINY:
type = (flags & UNSIGNED_FLAG) ? QMetaType::UChar : QMetaType::Char;
break;
- case FIELD_TYPE_SHORT :
+ case MYSQL_TYPE_SHORT:
type = (flags & UNSIGNED_FLAG) ? QMetaType::UShort : QMetaType::Short;
break;
- case FIELD_TYPE_LONG :
- case FIELD_TYPE_INT24 :
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_INT24:
type = (flags & UNSIGNED_FLAG) ? QMetaType::UInt : QMetaType::Int;
break;
- case FIELD_TYPE_YEAR :
+ case MYSQL_TYPE_YEAR:
type = QMetaType::Int;
break;
- case FIELD_TYPE_LONGLONG :
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_LONGLONG:
type = (flags & UNSIGNED_FLAG) ? QMetaType::ULongLong : QMetaType::LongLong;
break;
- case FIELD_TYPE_FLOAT :
- case FIELD_TYPE_DOUBLE :
- case FIELD_TYPE_DECIMAL :
-#if defined(FIELD_TYPE_NEWDECIMAL)
- case FIELD_TYPE_NEWDECIMAL:
-#endif
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
type = QMetaType::Double;
break;
- case FIELD_TYPE_DATE :
+ case MYSQL_TYPE_DATE:
type = QMetaType::QDate;
break;
- case FIELD_TYPE_TIME :
+ case MYSQL_TYPE_TIME:
// A time field can be within the range '-838:59:59' to '838:59:59' so
// use QString instead of QTime since QTime is limited to 24 hour clock
type = QMetaType::QString;
break;
- case FIELD_TYPE_DATETIME :
- case FIELD_TYPE_TIMESTAMP :
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
type = QMetaType::QDateTime;
break;
- case FIELD_TYPE_STRING :
- case FIELD_TYPE_VAR_STRING :
- case FIELD_TYPE_BLOB :
- case FIELD_TYPE_TINY_BLOB :
- case FIELD_TYPE_MEDIUM_BLOB :
- case FIELD_TYPE_LONG_BLOB :
- case FIELD_TYPE_GEOMETRY :
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_JSON:
type = (flags & BINARY_FLAG) ? QMetaType::QByteArray : QMetaType::QString;
break;
- default:
- case FIELD_TYPE_ENUM :
- case FIELD_TYPE_SET :
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ type = QMetaType::QString;
+ break;
+ default: // needed because there are more enum values which are not available in all headers
type = QMetaType::QString;
break;
}
@@ -262,31 +280,38 @@ static QMetaType qDecodeMYSQLType(int mysqltype, uint flags)
static QSqlField qToField(MYSQL_FIELD *field)
{
QSqlField f(QString::fromUtf8(field->name),
- qDecodeMYSQLType(int(field->type), field->flags),
+ qDecodeMYSQLType(field->type, field->flags),
QString::fromUtf8(field->table));
f.setRequired(IS_NOT_NULL(field->flags));
f.setLength(field->length);
f.setPrecision(field->decimals);
- f.setSqlType(field->type);
f.setAutoValue(field->flags & AUTO_INCREMENT_FLAG);
return f;
}
-static QSqlError qMakeStmtError(const QString& err, QSqlError::ErrorType type,
- MYSQL_STMT* stmt)
+static QSqlError qMakeStmtError(const QString &err, QSqlError::ErrorType type,
+ MYSQL_STMT *stmt)
{
const char *cerr = mysql_stmt_error(stmt);
- return QSqlError(QLatin1String("QMYSQL: ") + err,
+ return QSqlError("QMYSQL: "_L1 + err,
QString::fromLatin1(cerr),
type, QString::number(mysql_stmt_errno(stmt)));
}
-static bool qIsBlob(int t)
+static bool qIsBlob(enum_field_types t)
{
return t == MYSQL_TYPE_TINY_BLOB
|| t == MYSQL_TYPE_BLOB
|| t == MYSQL_TYPE_MEDIUM_BLOB
- || t == MYSQL_TYPE_LONG_BLOB;
+ || t == MYSQL_TYPE_LONG_BLOB
+ || t == MYSQL_TYPE_JSON;
+}
+
+static bool qIsTimeOrDate(enum_field_types t)
+{
+ // *not* MYSQL_TYPE_TIME because its range is bigger than QTime
+ // (see above)
+ return t == MYSQL_TYPE_DATE || t == MYSQL_TYPE_DATETIME || t == MYSQL_TYPE_TIMESTAMP;
}
static bool qIsInteger(int t)
@@ -297,16 +322,17 @@ static bool qIsInteger(int t)
|| t == QMetaType::LongLong || t == QMetaType::ULongLong;
}
-void QMYSQLResultPrivate::bindBlobs()
+static inline bool qIsBitfield(enum_field_types type)
{
- int i;
- MYSQL_FIELD *fieldInfo;
- MYSQL_BIND *bind;
+ return type == MYSQL_TYPE_BIT;
+}
- for(i = 0; i < fields.count(); ++i) {
- fieldInfo = fields.at(i).myField;
+void QMYSQLResultPrivate::bindBlobs()
+{
+ for (int i = 0; i < fields.size(); ++i) {
+ const MYSQL_FIELD *fieldInfo = fields.at(i).myField;
if (qIsBlob(inBinds[i].buffer_type) && meta && fieldInfo) {
- bind = &inBinds[i];
+ MYSQL_BIND *bind = &inBinds[i];
bind->buffer_length = fieldInfo->max_length;
delete[] static_cast<char*>(bind->buffer);
bind->buffer = new char[fieldInfo->max_length];
@@ -322,43 +348,42 @@ bool QMYSQLResultPrivate::bindInValues()
if (!meta)
return false;
- MYSQL_BIND *bind;
- char *field;
- int i = 0;
fields.resize(mysql_num_fields(meta));
inBinds = new MYSQL_BIND[fields.size()];
memset(inBinds, 0, fields.size() * sizeof(MYSQL_BIND));
- MYSQL_FIELD *fieldInfo;
+ const MYSQL_FIELD *fieldInfo;
+ int i = 0;
while((fieldInfo = mysql_fetch_field(meta))) {
+ MYSQL_BIND *bind = &inBinds[i];
+
QMyField &f = fields[i];
f.myField = fieldInfo;
-
+ bind->buffer_length = f.bufLength = fieldInfo->length + 1;
+ bind->buffer_type = fieldInfo->type;
f.type = qDecodeMYSQLType(fieldInfo->type, fieldInfo->flags);
if (qIsBlob(fieldInfo->type)) {
// the size of a blob-field is available as soon as we call
// mysql_stmt_store_result()
// after mysql_stmt_exec() in QMYSQLResult::exec()
- fieldInfo->length = 0;
+ bind->buffer_length = f.bufLength = 0;
hasBlobs = true;
+ } else if (qIsTimeOrDate(fieldInfo->type)) {
+ bind->buffer_length = f.bufLength = sizeof(QT_MYSQL_TIME);
} else if (qIsInteger(f.type.id())) {
- fieldInfo->length = 8;
+ bind->buffer_length = f.bufLength = 8;
} else {
- fieldInfo->type = MYSQL_TYPE_STRING;
+ bind->buffer_type = MYSQL_TYPE_STRING;
}
- bind = &inBinds[i];
- field = new char[fieldInfo->length + 1];
- memset(field, 0, fieldInfo->length + 1);
- bind->buffer_type = fieldInfo->type;
- bind->buffer = field;
- bind->buffer_length = f.bufLength = fieldInfo->length + 1;
bind->is_null = &f.nullIndicator;
bind->length = &f.bufLength;
bind->is_unsigned = fieldInfo->flags & UNSIGNED_FLAG ? 1 : 0;
- f.outField=field;
+
+ char *field = bind->buffer_length ? new char[bind->buffer_length + 1]{} : nullptr;
+ bind->buffer = f.outField = field;
++i;
}
@@ -390,7 +415,7 @@ void QMYSQLResult::cleanup()
if (d->result)
mysql_free_result(d->result);
-// must iterate trough leftover result sets from multi-selects or stored procedures
+// must iterate through leftover result sets from multi-selects or stored procedures
// if this isn't done subsequent queries will fail with "Commands out of sync"
while (driver() && d->drv_d_func()->mysql && mysql_next_result(d->drv_d_func()->mysql) == 0) {
MYSQL_RES *res = mysql_store_result(d->drv_d_func()->mysql);
@@ -409,9 +434,8 @@ void QMYSQLResult::cleanup()
d->meta = 0;
}
- int i;
- for (i = 0; i < d->fields.count(); ++i)
- delete[] d->fields[i].outField;
+ for (const QMYSQLResultPrivate::QMyField &f : std::as_const(d->fields))
+ delete[] f.outField;
if (d->outBinds) {
delete[] d->outBinds;
@@ -425,8 +449,8 @@ void QMYSQLResult::cleanup()
d->hasBlobs = false;
d->fields.clear();
- d->result = NULL;
- d->row = NULL;
+ d->result = nullptr;
+ d->row = nullptr;
setAt(-1);
setActive(false);
}
@@ -501,12 +525,7 @@ bool QMYSQLResult::fetchLast()
return success;
}
- my_ulonglong numRows;
- if (d->preparedQuery) {
- numRows = mysql_stmt_num_rows(d->stmt);
- } else {
- numRows = mysql_num_rows(d->result);
- }
+ my_ulonglong numRows = d->preparedQuery ? mysql_stmt_num_rows(d->stmt) : mysql_num_rows(d->result);
if (at() == int(numRows))
return true;
if (!numRows)
@@ -524,10 +543,24 @@ bool QMYSQLResult::fetchFirst()
return fetch(0);
}
+static inline uint64_t
+qDecodeBitfield(const QMYSQLResultPrivate::QMyField &f, const char *outField)
+{
+ // byte-aligned length
+ const auto numBytes = (f.myField->length + 7) / 8;
+ uint64_t val = 0;
+ for (unsigned long i = 0; i < numBytes && outField; ++i) {
+ uint64_t tmp = static_cast<uint8_t>(outField[i]);
+ val <<= 8;
+ val |= tmp;
+ }
+ return val;
+}
+
QVariant QMYSQLResult::data(int field)
{
Q_D(QMYSQLResult);
- if (!isSelect() || field >= d->fields.count()) {
+ if (!isSelect() || field >= d->fields.size()) {
qWarning("QMYSQLResult::data: column %d out of range", field);
return QVariant();
}
@@ -535,14 +568,15 @@ QVariant QMYSQLResult::data(int field)
if (!driver())
return QVariant();
- int fieldLength = 0;
+ my_ulonglong fieldLength = 0;
const QMYSQLResultPrivate::QMyField &f = d->fields.at(field);
QString val;
if (d->preparedQuery) {
if (f.nullIndicator)
return QVariant(f.type);
-
- if (qIsInteger(f.type.id())) {
+ if (qIsBitfield(f.myField->type)) {
+ return QVariant::fromValue(qDecodeBitfield(f, f.outField));
+ } else if (qIsInteger(f.type.id())) {
QVariant variant(f.type, f.outField);
// we never want to return char variants here, see QTBUG-53397
if (f.type.id() == QMetaType::UChar)
@@ -550,16 +584,33 @@ QVariant QMYSQLResult::data(int field)
else if (f.type.id() == QMetaType::Char)
return variant.toInt();
return variant;
+ } else if (qIsTimeOrDate(f.myField->type) && f.bufLength >= sizeof(QT_MYSQL_TIME)) {
+ auto t = reinterpret_cast<const QT_MYSQL_TIME *>(f.outField);
+ QDate date;
+ QTime time;
+ if (f.type.id() != QMetaType::QTime)
+ date = QDate(t->year, t->month, t->day);
+ if (f.type.id() != QMetaType::QDate)
+ time = QTime(t->hour, t->minute, t->second, t->second_part / 1000);
+ if (f.type.id() == QMetaType::QDateTime)
+ return QDateTime(date, time, QTimeZone::UTC);
+ else if (f.type.id() == QMetaType::QDate)
+ return date;
+ else
+ return time;
}
if (f.type.id() != QMetaType::QByteArray)
val = QString::fromUtf8(f.outField, f.bufLength);
} else {
- if (d->row[field] == NULL) {
+ if (d->row[field] == nullptr) {
// NULL value
return QVariant(f.type);
}
+ if (qIsBitfield(f.myField->type))
+ return QVariant::fromValue(qDecodeBitfield(f, d->row[field]));
+
fieldLength = mysql_fetch_lengths(d->result)[field];
if (f.type.id() != QMetaType::QByteArray)
@@ -629,12 +680,12 @@ QVariant QMYSQLResult::data(int field)
bool QMYSQLResult::isNull(int field)
{
Q_D(const QMYSQLResult);
- if (field < 0 || field >= d->fields.count())
+ if (field < 0 || field >= d->fields.size())
return true;
if (d->preparedQuery)
return d->fields.at(field).nullIndicator;
else
- return d->row[field] == NULL;
+ return d->row[field] == nullptr;
}
bool QMYSQLResult::reset (const QString& query)
@@ -648,7 +699,7 @@ bool QMYSQLResult::reset (const QString& query)
cleanup();
const QByteArray encQuery = query.toUtf8();
- if (mysql_real_query(d->drv_d_func()->mysql, encQuery.data(), encQuery.length())) {
+ if (mysql_real_query(d->drv_d_func()->mysql, encQuery.data(), encQuery.size())) {
setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to execute query"),
QSqlError::StatementError, d->drv_d_func()));
return false;
@@ -668,6 +719,7 @@ bool QMYSQLResult::reset (const QString& query)
for(int i = 0; i < numFields; i++) {
MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
+ d->fields[i].myField = field;
}
setAt(QSql::BeforeFirstRow);
}
@@ -733,7 +785,7 @@ QSqlRecord QMYSQLResult::record() const
if (!mysql_errno(d->drv_d_func()->mysql)) {
mysql_field_seek(res, 0);
MYSQL_FIELD* field = mysql_fetch_field(res);
- while(field) {
+ while (field) {
info.append(qToField(field));
field = mysql_fetch_field(res);
}
@@ -756,8 +808,8 @@ bool QMYSQLResult::nextResult()
d->result = 0;
setSelect(false);
- for (int i = 0; i < d->fields.count(); ++i)
- delete[] d->fields[i].outField;
+ for (const QMYSQLResultPrivate::QMyField &f : std::as_const(d->fields))
+ delete[] f.outField;
d->fields.clear();
int status = mysql_next_result(d->drv_d_func()->mysql);
@@ -770,7 +822,7 @@ bool QMYSQLResult::nextResult()
}
d->result = mysql_store_result(d->drv_d_func()->mysql);
- int numFields = mysql_field_count(d->drv_d_func()->mysql);
+ unsigned int numFields = mysql_field_count(d->drv_d_func()->mysql);
if (!d->result && numFields > 0) {
setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to store next result"),
QSqlError::StatementError, d->drv_d_func()));
@@ -782,9 +834,10 @@ bool QMYSQLResult::nextResult()
d->rowsAffected = mysql_affected_rows(d->drv_d_func()->mysql);
if (isSelect()) {
- for (int i = 0; i < numFields; i++) {
- MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
+ for (unsigned int i = 0; i < numFields; i++) {
+ MYSQL_FIELD *field = mysql_fetch_field_direct(d->result, i);
d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
+ d->fields[i].myField = field;
}
}
@@ -797,29 +850,6 @@ void QMYSQLResult::virtual_hook(int id, void *data)
QSqlResult::virtual_hook(id, data);
}
-static MYSQL_TIME *toMySqlDate(QDate date, QTime time, int type)
-{
- Q_ASSERT(type == QMetaType::QTime || type == QMetaType::QDate
- || type == QMetaType::QDateTime);
-
- MYSQL_TIME *myTime = new MYSQL_TIME;
- memset(myTime, 0, sizeof(MYSQL_TIME));
-
- if (type == QMetaType::QTime || type == QMetaType::QDateTime) {
- myTime->hour = time.hour();
- myTime->minute = time.minute();
- myTime->second = time.second();
- myTime->second_part = time.msec() * 1000;
- }
- if (type == QMetaType::QDate || type == QMetaType::QDateTime) {
- myTime->year = date.year();
- myTime->month = date.month();
- myTime->day = date.day();
- }
-
- return myTime;
-}
-
bool QMYSQLResult::prepare(const QString& query)
{
Q_D(QMYSQLResult);
@@ -844,7 +874,7 @@ bool QMYSQLResult::prepare(const QString& query)
}
const QByteArray encQuery = query.toUtf8();
- r = mysql_stmt_prepare(d->stmt, encQuery.constData(), encQuery.length());
+ r = mysql_stmt_prepare(d->stmt, encQuery.constData(), encQuery.size());
if (r != 0) {
setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
"Unable to prepare statement"), QSqlError::StatementError, d->stmt));
@@ -852,9 +882,9 @@ bool QMYSQLResult::prepare(const QString& query)
return false;
}
- if (mysql_stmt_param_count(d->stmt) > 0) {// allocate memory for outvalues
- d->outBinds = new MYSQL_BIND[mysql_stmt_param_count(d->stmt)];
- }
+ const auto paramCount = mysql_stmt_param_count(d->stmt);
+ if (paramCount > 0) // allocate memory for outvalues
+ d->outBinds = new MYSQL_BIND[paramCount]();
setSelect(d->bindInValues());
d->preparedQuery = true;
@@ -872,7 +902,7 @@ bool QMYSQLResult::exec()
return false;
int r = 0;
- QList<MYSQL_TIME *> timeVector;
+ QList<QT_MYSQL_TIME *> timeVector;
QList<QByteArray> stringVector;
QList<my_bool> nullVector;
@@ -885,17 +915,16 @@ bool QMYSQLResult::exec()
return false;
}
- if (mysql_stmt_param_count(d->stmt) > 0 &&
- mysql_stmt_param_count(d->stmt) == (uint)values.count()) {
-
- nullVector.resize(values.count());
- for (int i = 0; i < values.count(); ++i) {
+ const unsigned long paramCount = mysql_stmt_param_count(d->stmt);
+ if (paramCount > 0 && paramCount == static_cast<size_t>(values.size())) {
+ nullVector.resize(values.size());
+ for (qsizetype i = 0; i < values.size(); ++i) {
const QVariant &val = boundValues().at(i);
void *data = const_cast<void *>(val.constData());
MYSQL_BIND* currBind = &d->outBinds[i];
- nullVector[i] = static_cast<my_bool>(val.isNull());
+ nullVector[i] = static_cast<my_bool>(QSqlResultPrivate::isVariantNull(val));
currBind->is_null = &nullVector[i];
currBind->length = 0;
currBind->is_unsigned = 0;
@@ -910,27 +939,41 @@ bool QMYSQLResult::exec()
case QMetaType::QTime:
case QMetaType::QDate:
case QMetaType::QDateTime: {
- MYSQL_TIME *myTime = toMySqlDate(val.toDate(), val.toTime(), val.userType());
+ auto myTime = new QT_MYSQL_TIME{};
timeVector.append(myTime);
-
currBind->buffer = myTime;
- switch (val.userType()) {
- case QMetaType::QTime:
+
+ QDate date;
+ QTime time;
+ int type = val.userType();
+ if (type == QMetaType::QTime) {
+ time = val.toTime();
currBind->buffer_type = MYSQL_TYPE_TIME;
myTime->time_type = MYSQL_TIMESTAMP_TIME;
- break;
- case QMetaType::QDate:
+ } else if (type == QMetaType::QDate) {
+ date = val.toDate();
currBind->buffer_type = MYSQL_TYPE_DATE;
myTime->time_type = MYSQL_TIMESTAMP_DATE;
- break;
- case QMetaType::QDateTime:
+ } else {
+ QDateTime dt = val.toDateTime().toUTC();
+ date = dt.date();
+ time = dt.time();
currBind->buffer_type = MYSQL_TYPE_DATETIME;
myTime->time_type = MYSQL_TIMESTAMP_DATETIME;
- break;
- default:
- break;
}
- currBind->buffer_length = sizeof(MYSQL_TIME);
+
+ if (type == QMetaType::QTime || type == QMetaType::QDateTime) {
+ myTime->hour = time.hour();
+ myTime->minute = time.minute();
+ myTime->second = time.second();
+ myTime->second_part = time.msec() * 1000;
+ }
+ if (type == QMetaType::QDate || type == QMetaType::QDateTime) {
+ myTime->year = date.year();
+ myTime->month = date.month();
+ myTime->day = date.day();
+ }
+ currBind->buffer_length = sizeof(QT_MYSQL_TIME);
currBind->length = 0;
break; }
case QMetaType::UInt:
@@ -964,12 +1007,16 @@ bool QMYSQLResult::exec()
stringVector.append(ba);
currBind->buffer_type = MYSQL_TYPE_STRING;
currBind->buffer = const_cast<char *>(ba.constData());
- currBind->buffer_length = ba.length();
+ currBind->buffer_length = ba.size();
break; }
}
}
+#if defined(MARIADB_VERSION_ID) || MYSQL_VERSION_ID < 80300
r = mysql_stmt_bind_param(d->stmt, d->outBinds);
+#else
+ r = mysql_stmt_bind_named_param(d->stmt, d->outBinds, paramCount, nullptr);
+#endif
if (r != 0) {
setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
"Unable to bind value"), QSqlError::StatementError, d->stmt));
@@ -1076,7 +1123,7 @@ QMYSQLDriver::QMYSQLDriver(MYSQL * con, QObject * parent)
Q_D(QMYSQLDriver);
init();
if (con) {
- d->mysql = (MYSQL *) con;
+ d->mysql = con;
setOpen(true);
setOpenError(false);
if (qMySqlConnectionCount == 1)
@@ -1132,167 +1179,240 @@ bool QMYSQLDriver::hasFeature(DriverFeature f) const
return false;
}
-static void setOptionFlag(uint &optionFlags, const QString &opt)
+static void setOptionFlag(uint &optionFlags, QStringView opt)
{
- if (opt == QLatin1String("CLIENT_COMPRESS"))
+ if (opt == "CLIENT_COMPRESS"_L1)
optionFlags |= CLIENT_COMPRESS;
- else if (opt == QLatin1String("CLIENT_FOUND_ROWS"))
+ else if (opt == "CLIENT_FOUND_ROWS"_L1)
optionFlags |= CLIENT_FOUND_ROWS;
- else if (opt == QLatin1String("CLIENT_IGNORE_SPACE"))
+ else if (opt == "CLIENT_IGNORE_SPACE"_L1)
optionFlags |= CLIENT_IGNORE_SPACE;
- else if (opt == QLatin1String("CLIENT_INTERACTIVE"))
+ else if (opt == "CLIENT_INTERACTIVE"_L1)
optionFlags |= CLIENT_INTERACTIVE;
- else if (opt == QLatin1String("CLIENT_NO_SCHEMA"))
+ else if (opt == "CLIENT_NO_SCHEMA"_L1)
optionFlags |= CLIENT_NO_SCHEMA;
- else if (opt == QLatin1String("CLIENT_ODBC"))
+ else if (opt == "CLIENT_ODBC"_L1)
optionFlags |= CLIENT_ODBC;
- else if (opt == QLatin1String("CLIENT_SSL"))
- qWarning("QMYSQLDriver: SSL_KEY, SSL_CERT and SSL_CA should be used instead of CLIENT_SSL.");
+ else if (opt == "CLIENT_SSL"_L1)
+ qWarning("QMYSQLDriver: MYSQL_OPT_SSL_KEY, MYSQL_OPT_SSL_CERT and MYSQL_OPT_SSL_CA should be used instead of CLIENT_SSL.");
else
qWarning("QMYSQLDriver::open: Unknown connect option '%s'", opt.toLocal8Bit().constData());
}
-bool QMYSQLDriver::open(const QString& db,
- const QString& user,
- const QString& password,
- const QString& host,
- int port,
- const QString& connOpts)
+static bool setOptionString(MYSQL *mysql, mysql_option option, QStringView v)
+{
+ return mysql_options(mysql, option, v.toUtf8().constData()) == 0;
+}
+
+static bool setOptionInt(MYSQL *mysql, mysql_option option, QStringView v)
+{
+ bool bOk;
+ const auto val = v.toInt(&bOk);
+ return bOk ? mysql_options(mysql, option, &val) == 0 : false;
+}
+
+static bool setOptionBool(MYSQL *mysql, mysql_option option, QStringView v)
+{
+ bool val = (v.isEmpty() || v == "TRUE"_L1 || v == "1"_L1);
+ return mysql_options(mysql, option, &val) == 0;
+}
+
+// MYSQL_OPT_SSL_MODE was introduced with MySQL 5.7.11
+#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 50711 && !defined(MARIADB_VERSION_ID)
+static bool setOptionSslMode(MYSQL *mysql, mysql_option option, QStringView v)
+{
+ mysql_ssl_mode sslMode = SSL_MODE_DISABLED;
+ if (v == "DISABLED"_L1 || v == "SSL_MODE_DISABLED"_L1)
+ sslMode = SSL_MODE_DISABLED;
+ else if (v == "PREFERRED"_L1 || v == "SSL_MODE_PREFERRED"_L1)
+ sslMode = SSL_MODE_PREFERRED;
+ else if (v == "REQUIRED"_L1 || v == "SSL_MODE_REQUIRED"_L1)
+ sslMode = SSL_MODE_REQUIRED;
+ else if (v == "VERIFY_CA"_L1 || v == "SSL_MODE_VERIFY_CA"_L1)
+ sslMode = SSL_MODE_VERIFY_CA;
+ else if (v == "VERIFY_IDENTITY"_L1 || v == "SSL_MODE_VERIFY_IDENTITY"_L1)
+ sslMode = SSL_MODE_VERIFY_IDENTITY;
+ else
+ qWarning() << "Unknown ssl mode '" << v << "' - using SSL_MODE_DISABLED";
+ return mysql_options(mysql, option, &sslMode) == 0;
+}
+#endif
+
+static bool setOptionProtocol(MYSQL *mysql, mysql_option option, QStringView v)
+{
+ mysql_protocol_type proto = MYSQL_PROTOCOL_DEFAULT;
+ if (v == "TCP"_L1 || v == "MYSQL_PROTOCOL_TCP"_L1)
+ proto = MYSQL_PROTOCOL_TCP;
+ else if (v == "SOCKET"_L1 || v == "MYSQL_PROTOCOL_SOCKET"_L1)
+ proto = MYSQL_PROTOCOL_SOCKET;
+ else if (v == "PIPE"_L1 || v == "MYSQL_PROTOCOL_PIPE"_L1)
+ proto = MYSQL_PROTOCOL_PIPE;
+ else if (v == "MEMORY"_L1 || v == "MYSQL_PROTOCOL_MEMORY"_L1)
+ proto = MYSQL_PROTOCOL_MEMORY;
+ else if (v == "DEFAULT"_L1 || v == "MYSQL_PROTOCOL_DEFAULT"_L1)
+ proto = MYSQL_PROTOCOL_DEFAULT;
+ else
+ qWarning() << "Unknown protocol '" << v << "' - using MYSQL_PROTOCOL_DEFAULT";
+ return mysql_options(mysql, option, &proto) == 0;
+}
+
+bool QMYSQLDriver::open(const QString &db,
+ const QString &user,
+ const QString &password,
+ const QString &host,
+ int port,
+ const QString &connOpts)
{
Q_D(QMYSQLDriver);
if (isOpen())
close();
+ if (!(d->mysql = mysql_init(nullptr))) {
+ setLastError(qMakeError(tr("Unable to allocate a MYSQL object"),
+ QSqlError::ConnectionError, d));
+ setOpenError(true);
+ return false;
+ }
+
+ typedef bool (*SetOptionFunc)(MYSQL*, mysql_option, QStringView);
+ struct mysqloptions {
+ QLatin1StringView key;
+ mysql_option option;
+ SetOptionFunc func;
+ };
+ const mysqloptions options[] = {
+ {"SSL_KEY"_L1, MYSQL_OPT_SSL_KEY, setOptionString},
+ {"SSL_CERT"_L1, MYSQL_OPT_SSL_CERT, setOptionString},
+ {"SSL_CA"_L1, MYSQL_OPT_SSL_CA, setOptionString},
+ {"SSL_CAPATH"_L1, MYSQL_OPT_SSL_CAPATH, setOptionString},
+ {"SSL_CIPHER"_L1, MYSQL_OPT_SSL_CIPHER, setOptionString},
+ {"MYSQL_OPT_SSL_KEY"_L1, MYSQL_OPT_SSL_KEY, setOptionString},
+ {"MYSQL_OPT_SSL_CERT"_L1, MYSQL_OPT_SSL_CERT, setOptionString},
+ {"MYSQL_OPT_SSL_CA"_L1, MYSQL_OPT_SSL_CA, setOptionString},
+ {"MYSQL_OPT_SSL_CAPATH"_L1, MYSQL_OPT_SSL_CAPATH, setOptionString},
+ {"MYSQL_OPT_SSL_CIPHER"_L1, MYSQL_OPT_SSL_CIPHER, setOptionString},
+ {"MYSQL_OPT_SSL_CRL"_L1, MYSQL_OPT_SSL_CRL, setOptionString},
+ {"MYSQL_OPT_SSL_CRLPATH"_L1, MYSQL_OPT_SSL_CRLPATH, setOptionString},
+#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 50710
+ {"MYSQL_OPT_TLS_VERSION"_L1, MYSQL_OPT_TLS_VERSION, setOptionString},
+#endif
+#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 50711 && !defined(MARIADB_VERSION_ID)
+ {"MYSQL_OPT_SSL_MODE"_L1, MYSQL_OPT_SSL_MODE, setOptionSslMode},
+#endif
+ {"MYSQL_OPT_CONNECT_TIMEOUT"_L1, MYSQL_OPT_CONNECT_TIMEOUT, setOptionInt},
+ {"MYSQL_OPT_READ_TIMEOUT"_L1, MYSQL_OPT_READ_TIMEOUT, setOptionInt},
+ {"MYSQL_OPT_WRITE_TIMEOUT"_L1, MYSQL_OPT_WRITE_TIMEOUT, setOptionInt},
+ {"MYSQL_OPT_RECONNECT"_L1, MYSQL_OPT_RECONNECT, setOptionBool},
+ {"MYSQL_OPT_LOCAL_INFILE"_L1, MYSQL_OPT_LOCAL_INFILE, setOptionInt},
+ {"MYSQL_OPT_PROTOCOL"_L1, MYSQL_OPT_PROTOCOL, setOptionProtocol},
+ {"MYSQL_SHARED_MEMORY_BASE_NAME"_L1, MYSQL_SHARED_MEMORY_BASE_NAME, setOptionString},
+ };
+ auto trySetOption = [&](const QStringView &key, const QStringView &value) -> bool {
+ for (const mysqloptions &opt : options) {
+ if (key == opt.key) {
+ if (!opt.func(d->mysql, opt.option, value)) {
+ qWarning("QMYSQLDriver::open: Could not set connect option value '%s' to '%s'",
+ key.toLocal8Bit().constData(), value.toLocal8Bit().constData());
+ }
+ return true;
+ }
+ }
+ return false;
+ };
+
/* This is a hack to get MySQL's stored procedure support working.
Since a stored procedure _may_ return multiple result sets,
we have to enable CLIEN_MULTI_STATEMENTS here, otherwise _any_
stored procedure call will fail.
*/
unsigned int optionFlags = CLIENT_MULTI_STATEMENTS;
- const QStringList opts(connOpts.split(QLatin1Char(';'), Qt::SkipEmptyParts));
+ const QList<QStringView> opts(QStringView(connOpts).split(u';', Qt::SkipEmptyParts));
QString unixSocket;
- QString sslCert;
- QString sslCA;
- QString sslKey;
- QString sslCAPath;
- QString sslCipher;
- my_bool reconnect=false;
- uint connectTimeout = 0;
- uint readTimeout = 0;
- uint writeTimeout = 0;
// extract the real options from the string
- for (int i = 0; i < opts.count(); ++i) {
- QString tmp(opts.at(i).simplified());
- int idx;
- if ((idx = tmp.indexOf(QLatin1Char('='))) != -1) {
- QString val = tmp.mid(idx + 1).simplified();
- QString opt = tmp.left(idx).simplified();
- if (opt == QLatin1String("UNIX_SOCKET"))
- unixSocket = val;
- else if (opt == QLatin1String("MYSQL_OPT_RECONNECT")) {
- if (val == QLatin1String("TRUE") || val == QLatin1String("1") || val.isEmpty())
- reconnect = true;
- } else if (opt == QLatin1String("MYSQL_OPT_CONNECT_TIMEOUT"))
- connectTimeout = val.toInt();
- else if (opt == QLatin1String("MYSQL_OPT_READ_TIMEOUT"))
- readTimeout = val.toInt();
- else if (opt == QLatin1String("MYSQL_OPT_WRITE_TIMEOUT"))
- writeTimeout = val.toInt();
- else if (opt == QLatin1String("SSL_KEY"))
- sslKey = val;
- else if (opt == QLatin1String("SSL_CERT"))
- sslCert = val;
- else if (opt == QLatin1String("SSL_CA"))
- sslCA = val;
- else if (opt == QLatin1String("SSL_CAPATH"))
- sslCAPath = val;
- else if (opt == QLatin1String("SSL_CIPHER"))
- sslCipher = val;
- else if (val == QLatin1String("TRUE") || val == QLatin1String("1"))
- setOptionFlag(optionFlags, tmp.left(idx).simplified());
+ for (const auto &option : opts) {
+ const QStringView sv = QStringView(option).trimmed();
+ qsizetype idx;
+ if ((idx = sv.indexOf(u'=')) != -1) {
+ const QStringView key = sv.left(idx).trimmed();
+ const QStringView val = sv.mid(idx + 1).trimmed();
+ if (trySetOption(key, val))
+ continue;
+ else if (key == "UNIX_SOCKET"_L1)
+ unixSocket = val.toString();
+ else if (val == "TRUE"_L1 || val == "1"_L1)
+ setOptionFlag(optionFlags, key);
else
qWarning("QMYSQLDriver::open: Illegal connect option value '%s'",
- tmp.toLocal8Bit().constData());
+ sv.toLocal8Bit().constData());
} else {
- setOptionFlag(optionFlags, tmp);
+ setOptionFlag(optionFlags, sv);
}
}
- if (!(d->mysql = mysql_init((MYSQL*) 0))) {
- setLastError(qMakeError(tr("Unable to allocate a MYSQL object"),
- QSqlError::ConnectionError, d));
- setOpenError(true);
- return false;
- }
-
- if (!sslKey.isNull() || !sslCert.isNull() || !sslCA.isNull() ||
- !sslCAPath.isNull() || !sslCipher.isNull()) {
- mysql_ssl_set(d->mysql,
- sslKey.isNull() ? static_cast<const char *>(0)
- : QFile::encodeName(sslKey).constData(),
- sslCert.isNull() ? static_cast<const char *>(0)
- : QFile::encodeName(sslCert).constData(),
- sslCA.isNull() ? static_cast<const char *>(0)
- : QFile::encodeName(sslCA).constData(),
- sslCAPath.isNull() ? static_cast<const char *>(0)
- : QFile::encodeName(sslCAPath).constData(),
- sslCipher.isNull() ? static_cast<const char *>(0)
- : sslCipher.toLocal8Bit().constData());
+ // try utf8 with non BMP first, utf8 (BMP only) if that fails
+ static const char wanted_charsets[][8] = { "utf8mb4", "utf8" };
+#ifdef MARIADB_VERSION_ID
+ MARIADB_CHARSET_INFO *cs = nullptr;
+ for (const char *p : wanted_charsets) {
+ cs = mariadb_get_charset_by_name(p);
+ if (cs) {
+ d->mysql->charset = cs;
+ break;
+ }
}
+#else
+ // dummy
+ struct {
+ const char *csname;
+ } *cs = nullptr;
+#endif
- if (connectTimeout != 0)
- mysql_options(d->mysql, MYSQL_OPT_CONNECT_TIMEOUT, &connectTimeout);
- if (readTimeout != 0)
- mysql_options(d->mysql, MYSQL_OPT_READ_TIMEOUT, &readTimeout);
- if (writeTimeout != 0)
- mysql_options(d->mysql, MYSQL_OPT_WRITE_TIMEOUT, &writeTimeout);
MYSQL *mysql = mysql_real_connect(d->mysql,
- host.isNull() ? static_cast<const char *>(0)
- : host.toLocal8Bit().constData(),
- user.isNull() ? static_cast<const char *>(0)
- : user.toLocal8Bit().constData(),
- password.isNull() ? static_cast<const char *>(0)
- : password.toLocal8Bit().constData(),
- db.isNull() ? static_cast<const char *>(0)
- : db.toLocal8Bit().constData(),
+ host.isNull() ? nullptr : host.toUtf8().constData(),
+ user.isNull() ? nullptr : user.toUtf8().constData(),
+ password.isNull() ? nullptr : password.toUtf8().constData(),
+ db.isNull() ? nullptr : db.toUtf8().constData(),
(port > -1) ? port : 0,
- unixSocket.isNull() ? static_cast<const char *>(0)
- : unixSocket.toLocal8Bit().constData(),
+ unixSocket.isNull() ? nullptr : unixSocket.toUtf8().constData(),
optionFlags);
- // try utf8 with non BMP first, utf8 (BMP only) if that fails
- if (mysql_set_character_set(d->mysql, "utf8mb4"))
- if (mysql_set_character_set(d->mysql, "utf8"))
- qWarning() << "MySQL: Unable to set the client character set to utf8.";
-
- if (mysql == d->mysql) {
- if (!db.isEmpty() && mysql_select_db(d->mysql, db.toLocal8Bit().constData())) {
- setLastError(qMakeError(tr("Unable to open database '%1'").arg(db), QSqlError::ConnectionError, d));
- mysql_close(d->mysql);
- setOpenError(true);
- return false;
- }
- if (reconnect)
- mysql_options(d->mysql, MYSQL_OPT_RECONNECT, &reconnect);
- } else {
+ if (mysql != d->mysql) {
setLastError(qMakeError(tr("Unable to connect"),
QSqlError::ConnectionError, d));
mysql_close(d->mysql);
- d->mysql = NULL;
+ d->mysql = nullptr;
setOpenError(true);
return false;
}
- // force the communication to be utf8mb4 (only utf8mb4 supports 4-byte characters)
- if (mysql_set_character_set(d->mysql, "utf8mb4")) {
- // this failed, try forcing it to utf (BMP only)
- if (mysql_set_character_set(d->mysql, "utf8"))
- qWarning() << "MySQL: Unable to set the client character set to utf8.";
+ // now ask the server to match the charset we selected
+ if (!cs || mysql_set_character_set(d->mysql, cs->csname) != 0) {
+ bool ok = false;
+ for (const char *p : wanted_charsets) {
+ if (mysql_set_character_set(d->mysql, p) == 0) {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok)
+ qWarning("MySQL: Unable to set the client character set to utf8 (\"%s\"). Using '%s' instead.",
+ mysql_error(d->mysql),
+ mysql_character_set_name(d->mysql));
+ }
+
+ if (!db.isEmpty() && mysql_select_db(d->mysql, db.toUtf8().constData())) {
+ setLastError(qMakeError(tr("Unable to open database '%1'").arg(db), QSqlError::ConnectionError, d));
+ mysql_close(d->mysql);
+ setOpenError(true);
+ return false;
}
- d->preparedQuerysEnabled = mysql_get_client_version() >= 40108
- && mysql_get_server_version(d->mysql) >= 40100;
+ d->preparedQuerysEnabled = checkPreparedQueries(d->mysql);
+ d->dbName = db;
+
+ if (d->preparedQuerysEnabled)
+ setUtcTimeZone(d->mysql);
#if QT_CONFIG(thread)
mysql_thread_init();
@@ -1311,7 +1431,8 @@ void QMYSQLDriver::close()
mysql_thread_end();
#endif
mysql_close(d->mysql);
- d->mysql = NULL;
+ d->mysql = nullptr;
+ d->dbName.clear();
setOpen(false);
setOpenError(false);
}
@@ -1328,14 +1449,14 @@ QStringList QMYSQLDriver::tables(QSql::TableType type) const
QStringList tl;
QSqlQuery q(createResult());
if (type & QSql::Tables) {
- QString sql = QLatin1String("select table_name from information_schema.tables where table_schema = '") + QLatin1String(d->mysql->db) + QLatin1String("' and table_type = 'BASE TABLE'");
+ QString sql = "select table_name from information_schema.tables where table_schema = '"_L1 + d->dbName + "' and table_type = 'BASE TABLE'"_L1;
q.exec(sql);
while (q.next())
tl.append(q.value(0).toString());
}
if (type & QSql::Views) {
- QString sql = QLatin1String("select table_name from information_schema.tables where table_schema = '") + QLatin1String(d->mysql->db) + QLatin1String("' and table_type = 'VIEW'");
+ QString sql = "select table_name from information_schema.tables where table_schema = '"_L1 + d->dbName + "' and table_type = 'VIEW'"_L1;
q.exec(sql);
while (q.next())
@@ -1344,18 +1465,18 @@ QStringList QMYSQLDriver::tables(QSql::TableType type) const
return tl;
}
-QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const
+QSqlIndex QMYSQLDriver::primaryIndex(const QString &tablename) const
{
QSqlIndex idx;
if (!isOpen())
return idx;
QSqlQuery i(createResult());
- QString stmt(QLatin1String("show index from %1;"));
+ QString stmt("show index from %1;"_L1);
QSqlRecord fil = record(tablename);
i.exec(stmt.arg(escapeIdentifier(tablename, QSqlDriver::TableName)));
while (i.isActive() && i.next()) {
- if (i.value(2).toString() == QLatin1String("PRIMARY")) {
+ if (i.value(2).toString() == "PRIMARY"_L1) {
idx.append(fil.field(i.value(4).toString()));
idx.setCursorName(i.value(0).toString());
idx.setName(i.value(2).toString());
@@ -1365,26 +1486,41 @@ QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const
return idx;
}
-QSqlRecord QMYSQLDriver::record(const QString& tablename) const
+QSqlRecord QMYSQLDriver::record(const QString &tablename) const
{
Q_D(const QMYSQLDriver);
- QString table=tablename;
- if (isIdentifierEscaped(table, QSqlDriver::TableName))
- table = stripDelimiters(table, QSqlDriver::TableName);
-
- QSqlRecord info;
if (!isOpen())
- return info;
- MYSQL_RES* r = mysql_list_fields(d->mysql, table.toLocal8Bit().constData(), 0);
- if (!r) {
- return info;
+ return {};
+ QSqlQuery i(createResult());
+ QString stmt("SELECT * FROM %1 LIMIT 0"_L1);
+ i.exec(stmt.arg(escapeIdentifier(tablename, QSqlDriver::TableName)));
+ auto r = i.record();
+ if (r.isEmpty())
+ return r;
+ // no binding of WHERE possible with MySQL
+ // escaping on WHERE clause does not work, so use mysql_real_escape_string()
+ stmt = "SELECT column_name, column_default FROM information_schema.columns WHERE table_name = '%1'"_L1;
+ const auto baTableName = tablename.toUtf8();
+ QVarLengthArray<char> tableNameQuoted(baTableName.size() * 2 + 1);
+#if defined(MARIADB_VERSION_ID)
+ const auto len = mysql_real_escape_string(d->mysql, tableNameQuoted.data(),
+ baTableName.data(), baTableName.size());
+#else
+ const auto len = mysql_real_escape_string_quote(d->mysql, tableNameQuoted.data(),
+ baTableName.data(), baTableName.size(), '\'');
+#endif
+ if (i.exec(stmt.arg(QString::fromUtf8(tableNameQuoted.data(), len)))) {
+ while (i.next()) {
+ const auto colName = i.value(0).toString();
+ const auto recordIdx = r.indexOf(colName);
+ if (recordIdx >= 0) {
+ auto field = r.field(recordIdx);
+ field.setDefaultValue(i.value(1));
+ r.replace(recordIdx, field);
+ }
+ }
}
- MYSQL_FIELD* field;
-
- while ((field = mysql_fetch_field(r)))
- info.append(qToField(field));
- mysql_free_result(r);
- return info;
+ return r;
}
QVariant QMYSQLDriver::handle() const
@@ -1452,23 +1588,34 @@ QString QMYSQLDriver::formatValue(const QSqlField &field, bool trimStrings) cons
case QMetaType::QString:
// Escape '\' characters
r = QSqlDriver::formatValue(field, trimStrings);
- r.replace(QLatin1String("\\"), QLatin1String("\\\\"));
+ r.replace("\\"_L1, "\\\\"_L1);
break;
case QMetaType::QByteArray:
if (isOpen()) {
const QByteArray ba = field.value().toByteArray();
// buffer has to be at least length*2+1 bytes
- char* buffer = new char[ba.size() * 2 + 1];
- int escapedSize = int(mysql_real_escape_string(d->mysql, buffer,
- ba.data(), ba.size()));
+ QVarLengthArray<char, 512> buffer(ba.size() * 2 + 1);
+ auto escapedSize = mysql_real_escape_string(d->mysql, buffer.data(), ba.data(), ba.size());
r.reserve(escapedSize + 3);
- r.append(QLatin1Char('\'')).append(QString::fromUtf8(buffer)).append(QLatin1Char('\''));
- delete[] buffer;
+ r = u'\'' + QString::fromUtf8(buffer.data(), escapedSize) + u'\'';
break;
} else {
qWarning("QMYSQLDriver::formatValue: Database not open");
}
- // fall through
+ Q_FALLTHROUGH();
+ case QMetaType::QDateTime:
+ if (QDateTime dt = field.value().toDateTime(); dt.isValid()) {
+ // MySQL format doesn't like the "Z" at the end, but does allow
+ // "+00:00" starting in version 8.0.19. However, if we got here,
+ // it's because the MySQL server is too old for prepared queries
+ // in the first place, so it won't understand timezones either.
+ r = u'\'' +
+ dt.date().toString(Qt::ISODate) +
+ u'T' +
+ dt.time().toString(Qt::ISODate) +
+ u'\'';
+ }
+ break;
default:
r = QSqlDriver::formatValue(field, trimStrings);
}
@@ -1479,9 +1626,9 @@ QString QMYSQLDriver::formatValue(const QSqlField &field, bool trimStrings) cons
QString QMYSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
{
QString res = identifier;
- if (!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('`')) && !identifier.endsWith(QLatin1Char('`')) ) {
- res.prepend(QLatin1Char('`')).append(QLatin1Char('`'));
- res.replace(QLatin1Char('.'), QLatin1String("`.`"));
+ if (!identifier.isEmpty() && !identifier.startsWith(u'`') && !identifier.endsWith(u'`')) {
+ res.replace(u'.', "`.`"_L1);
+ res = u'`' + res + u'`';
}
return res;
}
@@ -1490,8 +1637,10 @@ bool QMYSQLDriver::isIdentifierEscaped(const QString &identifier, IdentifierType
{
Q_UNUSED(type);
return identifier.size() > 2
- && identifier.startsWith(QLatin1Char('`')) //left delimited
- && identifier.endsWith(QLatin1Char('`')); //right delimited
+ && identifier.startsWith(u'`') //left delimited
+ && identifier.endsWith(u'`'); //right delimited
}
QT_END_NAMESPACE
+
+#include "moc_qsql_mysql_p.cpp"
diff --git a/src/plugins/sqldrivers/mysql/qsql_mysql_p.h b/src/plugins/sqldrivers/mysql/qsql_mysql_p.h
index 9ccc8f4e4f..ae3334d35e 100644
--- a/src/plugins/sqldrivers/mysql/qsql_mysql_p.h
+++ b/src/plugins/sqldrivers/mysql/qsql_mysql_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSQL_MYSQL_H
#define QSQL_MYSQL_H
diff --git a/src/plugins/sqldrivers/oci/CMakeLists.txt b/src/plugins/sqldrivers/oci/CMakeLists.txt
index c6e38f4cdf..66c4219905 100644
--- a/src/plugins/sqldrivers/oci/CMakeLists.txt
+++ b/src/plugins/sqldrivers/oci/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from oci.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## QOCIDriverPlugin Plugin:
@@ -6,7 +7,7 @@
qt_internal_add_plugin(QOCIDriverPlugin
OUTPUT_NAME qsqloci
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_oci.cpp qsql_oci_p.h
@@ -20,9 +21,6 @@ qt_internal_add_plugin(QOCIDriverPlugin
Qt::SqlPrivate
)
-#### Keys ignored in scope 1:.:.:oci.pro:<TRUE>:
-# OTHER_FILES = "oci.json"
-
## Scopes:
#####################################################################
@@ -30,3 +28,5 @@ qt_internal_extend_target(QOCIDriverPlugin CONDITION APPLE
LINK_OPTIONS
"-Wl,-flat_namespace,-U,_environ"
)
+
+qt_internal_force_macos_intel_arch(QOCIDriverPlugin)
diff --git a/src/plugins/sqldrivers/oci/main.cpp b/src/plugins/sqldrivers/oci/main.cpp
index db273bfb04..6cc0062671 100644
--- a/src/plugins/sqldrivers/oci/main.cpp
+++ b/src/plugins/sqldrivers/oci/main.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qsqldriverplugin.h>
#include <qstringlist.h>
@@ -43,6 +7,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QOCIDriverPlugin : public QSqlDriverPlugin
{
Q_OBJECT
@@ -51,7 +17,7 @@ class QOCIDriverPlugin : public QSqlDriverPlugin
public:
QOCIDriverPlugin();
- QSqlDriver* create(const QString &);
+ QSqlDriver* create(const QString &) override;
};
QOCIDriverPlugin::QOCIDriverPlugin()
@@ -61,7 +27,7 @@ QOCIDriverPlugin::QOCIDriverPlugin()
QSqlDriver* QOCIDriverPlugin::create(const QString &name)
{
- if (name == QLatin1String("QOCI")) {
+ if (name == "QOCI"_L1) {
QOCIDriver* driver = new QOCIDriver();
return driver;
}
diff --git a/src/plugins/sqldrivers/oci/qsql_oci.cpp b/src/plugins/sqldrivers/oci/qsql_oci.cpp
index 1566428f26..68ec672734 100644
--- a/src/plugins/sqldrivers/oci/qsql_oci.cpp
+++ b/src/plugins/sqldrivers/oci/qsql_oci.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsql_oci_p.h"
@@ -55,7 +19,9 @@
#include <QtSql/private/qsqlcachedresult_p.h>
#include <QtSql/private/qsqldriver_p.h>
#include <qstringlist.h>
+#if QT_CONFIG(timezone)
#include <qtimezone.h>
+#endif
#include <qvariant.h>
#include <qvarlengtharray.h>
@@ -64,14 +30,7 @@
#define _int64 __int64
#endif
-
#include <oci.h>
-#ifdef max
-#undef max
-#endif
-#ifdef min
-#undef min
-#endif
#include <stdlib.h>
@@ -92,6 +51,8 @@ Q_DECLARE_METATYPE(OCIStmt*)
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
enum { QOCIEncoding = 2002 }; // AL16UTF16LE
#else
@@ -172,8 +133,8 @@ QOCIDateTime::QOCIDateTime(OCIEnv *env, OCIError *err, const QDateTime &dt)
if (dt.isValid()) {
const QDate date = dt.date();
const QTime time = dt.time();
- // Zone in +hh:mm format (stripping UTC prefix from OffsetName)
- QString timeZone = dt.timeZone().displayName(dt, QTimeZone::OffsetName).mid(3);
+ // Zone in +hh:mm format
+ const QString timeZone = dt.toString("ttt"_L1);
const OraText *tz = reinterpret_cast<const OraText *>(timeZone.utf16());
OCIDateTimeConstruct(env, err, dateTime, date.year(), date.month(), date.day(), time.hour(),
time.minute(), time.second(), time.msec() * 1000000,
@@ -202,7 +163,7 @@ QDateTime QOCIDateTime::fromOCIDateTime(OCIEnv *env, OCIError *err, OCIDateTime
secondsOffset = -secondsOffset;
// OCIDateTimeGetTime gives "fractions of second" as nanoseconds
return QDateTime(QDate(year, month, day), QTime(hour, minute, second, nsec / 1000000),
- Qt::OffsetFromUTC, secondsOffset);
+ QTimeZone::fromSecondsAheadOfUtc(secondsOffset));
}
struct TempStorage {
@@ -222,15 +183,17 @@ class QOCIDriverPrivate : public QSqlDriverPrivate
public:
QOCIDriverPrivate();
- OCIEnv *env;
- OCISvcCtx *svc;
- OCIServer *srvhp;
- OCISession *authp;
- OCIError *err;
- bool transaction;
- int serverVersion;
- int prefetchRows;
- int prefetchMem;
+ OCIEnv *env = nullptr;
+ OCISvcCtx *svc = nullptr;
+ OCIServer *srvhp = nullptr;
+ OCISession *authp = nullptr;
+ OCITrans *trans = nullptr;
+ OCIError *err = nullptr;
+ ub4 authMode = OCI_DEFAULT;
+ bool transaction = false;
+ int serverVersion = -1;
+ int prefetchRows = -1;
+ int prefetchMem = QOCI_PREFETCH_MEM;
QString user;
void allocErrorHandle();
@@ -271,11 +234,11 @@ public:
QOCIResultPrivate(QOCIResult *q, const QOCIDriver *drv);
~QOCIResultPrivate();
- QOCICols *cols;
+ QOCICols *cols = nullptr;
OCIEnv *env;
- OCIError *err;
+ OCIError *err = nullptr;
OCISvcCtx *&svc;
- OCIStmt *sql;
+ OCIStmt *sql = nullptr;
bool transaction;
int serverVersion;
int prefetchRows, prefetchMem;
@@ -457,6 +420,7 @@ int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, in
break;
}
} // fall through for OUT values
+ Q_FALLTHROUGH();
default: {
if (val.typeId() >= QMetaType::User) {
if (val.canConvert<QOCIRowIdPointer>() && !isOutValue(pos)) {
@@ -514,7 +478,7 @@ int QOCIResultPrivate::bindValues(QVariantList &values, IndicatorArray &indicato
OCIBind * hbnd = nullptr; // Oracle handles these automatically
sb2 *indPtr = &indicators[i];
- *indPtr = val.isNull() ? -1 : 0;
+ *indPtr = QSqlResultPrivate::isVariantNull(val) ? -1 : 0;
bindValue(sql, &hbnd, err, i, val, indPtr, &tmpSizes[i], tmpStorage);
}
@@ -572,20 +536,19 @@ void QOCIResultPrivate::outValues(QVariantList &values, IndicatorArray &indicato
QOCIDriverPrivate::QOCIDriverPrivate()
- : QSqlDriverPrivate(), env(0), svc(0), srvhp(0), authp(0), err(0), transaction(false),
- serverVersion(-1), prefetchRows(-1), prefetchMem(QOCI_PREFETCH_MEM)
+ : QSqlDriverPrivate()
{
dbmsType = QSqlDriver::Oracle;
}
void QOCIDriverPrivate::allocErrorHandle()
{
+ Q_ASSERT(!err);
int r = OCIHandleAlloc(env,
reinterpret_cast<void **>(&err),
OCI_HTYPE_ERROR,
- 0,
- 0);
- if (r != 0)
+ 0, nullptr);
+ if (r != OCI_SUCCESS)
qWarning("QOCIDriver: unable to allocate error handle");
}
@@ -654,15 +617,15 @@ QSqlError qMakeError(const QString& errString, QSqlError::ErrorType type, OCIErr
QMetaType qDecodeOCIType(const QString& ocitype, QSql::NumericalPrecisionPolicy precisionPolicy)
{
int type = QMetaType::UnknownType;
- if (ocitype == QLatin1String("VARCHAR2") || ocitype == QLatin1String("VARCHAR")
- || ocitype.startsWith(QLatin1String("INTERVAL"))
- || ocitype == QLatin1String("CHAR") || ocitype == QLatin1String("NVARCHAR2")
- || ocitype == QLatin1String("NCHAR"))
+ if (ocitype == "VARCHAR2"_L1 || ocitype == "VARCHAR"_L1
+ || ocitype.startsWith("INTERVAL"_L1)
+ || ocitype == "CHAR"_L1 || ocitype == "NVARCHAR2"_L1
+ || ocitype == "NCHAR"_L1)
type = QMetaType::QString;
- else if (ocitype == QLatin1String("NUMBER")
- || ocitype == QLatin1String("FLOAT")
- || ocitype == QLatin1String("BINARY_FLOAT")
- || ocitype == QLatin1String("BINARY_DOUBLE")) {
+ else if (ocitype == "NUMBER"_L1
+ || ocitype == "FLOAT"_L1
+ || ocitype == "BINARY_FLOAT"_L1
+ || ocitype == "BINARY_DOUBLE"_L1) {
switch(precisionPolicy) {
case QSql::LowPrecisionInt32:
type = QMetaType::Int;
@@ -679,16 +642,15 @@ QMetaType qDecodeOCIType(const QString& ocitype, QSql::NumericalPrecisionPolicy
break;
}
}
- else if (ocitype == QLatin1String("LONG") || ocitype == QLatin1String("NCLOB")
- || ocitype == QLatin1String("CLOB"))
+ else if (ocitype == "LONG"_L1 || ocitype == "NCLOB"_L1 || ocitype == "CLOB"_L1)
type = QMetaType::QByteArray;
- else if (ocitype == QLatin1String("RAW") || ocitype == QLatin1String("LONG RAW")
- || ocitype == QLatin1String("ROWID") || ocitype == QLatin1String("BLOB")
- || ocitype == QLatin1String("CFILE") || ocitype == QLatin1String("BFILE"))
+ else if (ocitype == "RAW"_L1 || ocitype == "LONG RAW"_L1
+ || ocitype == "ROWID"_L1 || ocitype == "BLOB"_L1
+ || ocitype == "CFILE"_L1 || ocitype == "BFILE"_L1)
type = QMetaType::QByteArray;
- else if (ocitype == QLatin1String("DATE") || ocitype.startsWith(QLatin1String("TIME")))
+ else if (ocitype == "DATE"_L1 || ocitype.startsWith("TIME"_L1))
type = QMetaType::QDateTime;
- else if (ocitype == QLatin1String("UNDEFINED"))
+ else if (ocitype == "UNDEFINED"_L1)
type = QMetaType::UnknownType;
if (type == QMetaType::UnknownType)
qWarning("qDecodeOCIType: unknown type: %s", ocitype.toLocal8Bit().constData());
@@ -776,7 +738,6 @@ static QSqlField qFromOraInf(const OraFieldInfo &ofi)
f.setLength(ofi.oraPrecision == 0 ? 38 : int(ofi.oraPrecision));
f.setPrecision(ofi.oraScale);
- f.setSqlType(int(ofi.oraType));
return f;
}
@@ -1372,7 +1333,7 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVariantList &boundValues, bool a
// not a list - create a deep-copy of the single value
QOCIBatchColumn &singleCol = columns[i];
singleCol.indicators = new sb2[1];
- *singleCol.indicators = boundValues.at(i).isNull() ? -1 : 0;
+ *singleCol.indicators = QSqlResultPrivate::isVariantNull(boundValues.at(i)) ? -1 : 0;
r = d->bindValue(d->sql, &singleCol.bindh, d->err, i,
boundValues.at(i), singleCol.indicators, &tmpSizes[i], tmpStorage);
@@ -1469,7 +1430,7 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVariantList &boundValues, bool a
for (uint row = 0; row < col.recordCount; ++row) {
const QVariant &val = boundValues.at(i).toList().at(row);
- if (val.isNull() && !d->isOutValue(i)) {
+ if (QSqlResultPrivate::isVariantNull(val) && !d->isOutValue(i)) {
columns[i].indicators[row] = -1;
columns[i].lengths[row] = 0;
} else {
@@ -1482,6 +1443,7 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVariantList &boundValues, bool a
columns[i].lengths[row] = columns[i].maxLen;
QOCIDateTime *date = new QOCIDateTime(d->env, d->err, val.toDateTime());
*reinterpret_cast<OCIDateTime**>(dataPtr) = date->dateTime;
+ tmpStorage.dateTimes.append(date);
break;
}
case QMetaType::Int:
@@ -1597,7 +1559,7 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVariantList &boundValues, bool a
}
}
- //finaly we can execute
+ //finally we can execute
r = OCIStmtExecute(d->svc, d->sql, d->err,
arrayBind ? 1 : columns[0].recordCount,
0, NULL, NULL,
@@ -1838,22 +1800,19 @@ void QOCICols::getValues(QVariantList &v, int index)
QOCIResultPrivate::QOCIResultPrivate(QOCIResult *q, const QOCIDriver *drv)
: QSqlCachedResultPrivate(q, drv),
- cols(0),
env(drv_d_func()->env),
- err(0),
svc(const_cast<OCISvcCtx*&>(drv_d_func()->svc)),
- sql(0),
transaction(drv_d_func()->transaction),
serverVersion(drv_d_func()->serverVersion),
prefetchRows(drv_d_func()->prefetchRows),
prefetchMem(drv_d_func()->prefetchMem)
{
+ Q_ASSERT(!err);
int r = OCIHandleAlloc(env,
reinterpret_cast<void **>(&err),
OCI_HTYPE_ERROR,
- 0,
- 0);
- if (r != 0)
+ 0, nullptr);
+ if (r != OCI_SUCCESS)
qWarning("QOCIResult: unable to alloc error handle");
}
@@ -1861,9 +1820,11 @@ QOCIResultPrivate::~QOCIResultPrivate()
{
delete cols;
- int r = OCIHandleFree(err, OCI_HTYPE_ERROR);
- if (r != 0)
+ if (sql && OCIHandleFree(sql, OCI_HTYPE_STMT) != OCI_SUCCESS)
qWarning("~QOCIResult: unable to free statement handle");
+
+ if (OCIHandleFree(err, OCI_HTYPE_ERROR) != OCI_SUCCESS)
+ qWarning("~QOCIResult: unable to free error report handle");
}
@@ -1876,12 +1837,6 @@ QOCIResult::QOCIResult(const QOCIDriver *db)
QOCIResult::~QOCIResult()
{
- Q_D(QOCIResult);
- if (d->sql) {
- int r = OCIHandleFree(d->sql, OCI_HTYPE_STMT);
- if (r != 0)
- qWarning("~QOCIResult: unable to free statement handle");
- }
}
QVariant QOCIResult::handle() const
@@ -1977,12 +1932,14 @@ bool QOCIResult::prepare(const QString& query)
QSqlResult::prepare(query);
delete d->cols;
- d->cols = 0;
+ d->cols = nullptr;
QSqlCachedResult::cleanup();
if (d->sql) {
r = OCIHandleFree(d->sql, OCI_HTYPE_STMT);
- if (r != OCI_SUCCESS)
+ if (r == OCI_SUCCESS)
+ d->sql = nullptr;
+ else
qOraWarning("QOCIResult::prepare: unable to free statement handle:", d->err);
}
if (query.isEmpty())
@@ -1990,8 +1947,7 @@ bool QOCIResult::prepare(const QString& query)
r = OCIHandleAlloc(d->env,
reinterpret_cast<void **>(&d->sql),
OCI_HTYPE_STMT,
- 0,
- 0);
+ 0, nullptr);
if (r != OCI_SUCCESS) {
qOraWarning("QOCIResult::prepare: unable to alloc statement:", d->err);
setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
@@ -2230,29 +2186,37 @@ bool QOCIDriver::hasFeature(DriverFeature f) const
static void qParseOpts(const QString &options, QOCIDriverPrivate *d)
{
- const QStringList opts(options.split(QLatin1Char(';'), Qt::SkipEmptyParts));
- for (int i = 0; i < opts.count(); ++i) {
- const QString tmp(opts.at(i));
- int idx;
- if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
+ const QVector<QStringView> opts(QStringView(options).split(u';', Qt::SkipEmptyParts));
+ for (const auto tmp : opts) {
+ qsizetype idx;
+ if ((idx = tmp.indexOf(u'=')) == -1) {
qWarning("QOCIDriver::parseArgs: Invalid parameter: '%s'",
tmp.toLocal8Bit().constData());
continue;
}
- const QString opt = tmp.left(idx);
- const QString val = tmp.mid(idx + 1).simplified();
+ const QStringView opt = tmp.left(idx);
+ const QStringView val = tmp.mid(idx + 1).trimmed();
bool ok;
- if (opt == QLatin1String("OCI_ATTR_PREFETCH_ROWS")) {
+ if (opt == "OCI_ATTR_PREFETCH_ROWS"_L1) {
d->prefetchRows = val.toInt(&ok);
if (!ok)
d->prefetchRows = -1;
- } else if (opt == QLatin1String("OCI_ATTR_PREFETCH_MEMORY")) {
+ } else if (opt == "OCI_ATTR_PREFETCH_MEMORY"_L1) {
d->prefetchMem = val.toInt(&ok);
if (!ok)
d->prefetchMem = -1;
+ } else if (opt == "OCI_AUTH_MODE"_L1) {
+ if (val == "OCI_SYSDBA"_L1) {
+ d->authMode = OCI_SYSDBA;
+ } else if (val == "OCI_SYSOPER"_L1) {
+ d->authMode = OCI_SYSOPER;
+ } else if (val != "OCI_DEFAULT"_L1) {
+ qWarning("QOCIDriver::parseArgs: Unsupported value for OCI_AUTH_MODE: '%s'",
+ val.toLocal8Bit().constData());
+ }
} else {
- qWarning ("QOCIDriver::parseArgs: Invalid parameter: '%s'",
- opt.toLocal8Bit().constData());
+ qWarning("QOCIDriver::parseArgs: Invalid parameter: '%s'",
+ opt.toLocal8Bit().constData());
}
}
}
@@ -2279,34 +2243,46 @@ bool QOCIDriver::open(const QString & db,
QString::fromLatin1("(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=%1)(Port=%2))"
"(CONNECT_DATA=(SID=%3)))").arg(hostname).arg((port > -1 ? port : 1521)).arg(db);
- r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->srvhp), OCI_HTYPE_SERVER, 0, 0);
- if (r == OCI_SUCCESS)
- r = OCIServerAttach(d->srvhp, d->err, reinterpret_cast<const OraText *>(connectionString.utf16()),
+ Q_ASSERT(!d->srvhp);
+ r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->srvhp), OCI_HTYPE_SERVER, 0, nullptr);
+ if (r == OCI_SUCCESS) {
+ r = OCIServerAttach(d->srvhp, d->err,
+ reinterpret_cast<const OraText *>(connectionString.utf16()),
connectionString.length() * sizeof(QChar), OCI_DEFAULT);
- if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO)
- r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->svc), OCI_HTYPE_SVCCTX, 0, 0);
+ }
+ Q_ASSERT(!d->svc);
+ if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO) {
+ r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->svc), OCI_HTYPE_SVCCTX,
+ 0, nullptr);
+ }
if (r == OCI_SUCCESS)
r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->srvhp, 0, OCI_ATTR_SERVER, d->err);
- if (r == OCI_SUCCESS)
- r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->authp), OCI_HTYPE_SESSION, 0, 0);
- if (r == OCI_SUCCESS)
+ Q_ASSERT(!d->authp);
+ if (r == OCI_SUCCESS) {
+ r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->authp), OCI_HTYPE_SESSION,
+ 0, nullptr);
+ }
+ if (r == OCI_SUCCESS) {
r = OCIAttrSet(d->authp, OCI_HTYPE_SESSION, const_cast<ushort *>(user.utf16()),
user.length() * sizeof(QChar), OCI_ATTR_USERNAME, d->err);
- if (r == OCI_SUCCESS)
+ }
+ if (r == OCI_SUCCESS) {
r = OCIAttrSet(d->authp, OCI_HTYPE_SESSION, const_cast<ushort *>(password.utf16()),
password.length() * sizeof(QChar), OCI_ATTR_PASSWORD, d->err);
-
- OCITrans* trans;
- if (r == OCI_SUCCESS)
- r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&trans), OCI_HTYPE_TRANS, 0, 0);
+ }
+ Q_ASSERT(!d->trans);
+ if (r == OCI_SUCCESS) {
+ r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->trans), OCI_HTYPE_TRANS,
+ 0, nullptr);
+ }
if (r == OCI_SUCCESS)
- r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, trans, 0, OCI_ATTR_TRANS, d->err);
+ r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->trans, 0, OCI_ATTR_TRANS, d->err);
if (r == OCI_SUCCESS) {
if (user.isEmpty() && password.isEmpty())
- r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_EXT, OCI_DEFAULT);
+ r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_EXT, d->authMode);
else
- r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_RDBMS, OCI_DEFAULT);
+ r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_RDBMS, d->authMode);
}
if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO)
r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->authp, 0, OCI_ATTR_SESSION, d->err);
@@ -2314,12 +2290,18 @@ bool QOCIDriver::open(const QString & db,
if (r != OCI_SUCCESS) {
setLastError(qMakeError(tr("Unable to logon"), QSqlError::ConnectionError, d->err));
setOpenError(true);
+ if (d->trans)
+ OCIHandleFree(d->trans, OCI_HTYPE_TRANS);
+ d->trans = nullptr;
if (d->authp)
OCIHandleFree(d->authp, OCI_HTYPE_SESSION);
- d->authp = 0;
+ d->authp = nullptr;
+ if (d->svc)
+ OCIHandleFree(d->svc, OCI_HTYPE_SVCCTX);
+ d->svc = nullptr;
if (d->srvhp)
OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER);
- d->srvhp = 0;
+ d->srvhp = nullptr;
return false;
}
@@ -2336,7 +2318,7 @@ bool QOCIDriver::open(const QString & db,
QString versionStr;
versionStr = QString(reinterpret_cast<const QChar *>(vertxt));
#if QT_CONFIG(regularexpression)
- auto match = QRegularExpression(QLatin1String("([0-9]+)\\.[0-9\\.]+[0-9]")).match(versionStr);
+ auto match = QRegularExpression("([0-9]+)\\.[0-9\\.]+[0-9]"_L1).match(versionStr);
if (match.hasMatch())
d->serverVersion = match.captured(1).toInt();
#endif
@@ -2359,12 +2341,14 @@ void QOCIDriver::close()
OCISessionEnd(d->svc, d->err, d->authp, OCI_DEFAULT);
OCIServerDetach(d->srvhp, d->err, OCI_DEFAULT);
+ OCIHandleFree(d->trans, OCI_HTYPE_TRANS);
+ d->trans = nullptr;
OCIHandleFree(d->authp, OCI_HTYPE_SESSION);
- d->authp = 0;
- OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER);
- d->srvhp = 0;
+ d->authp = nullptr;
OCIHandleFree(d->svc, OCI_HTYPE_SVCCTX);
- d->svc = 0;
+ d->svc = nullptr;
+ OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER);
+ d->srvhp = nullptr;
setOpen(false);
setOpenError(false);
}
@@ -2452,18 +2436,18 @@ static QString make_where_clause(const QString &user, Expression e)
"WMSYS",
};
static const char joinC[][4] = { "or" , "and" };
- static constexpr QLatin1Char bang[] = { QLatin1Char(' '), QLatin1Char('!') };
+ static constexpr char16_t bang[] = { u' ', u'!' };
- const QLatin1String join(joinC[e]);
+ const QLatin1StringView join(joinC[e]);
QString result;
result.reserve(sizeof sysUsers / sizeof *sysUsers *
// max-sizeof(owner != <sysuser> and )
(9 + sizeof *sysUsers + 5));
for (const auto &sysUser : sysUsers) {
- const QLatin1String l1(sysUser);
+ const QLatin1StringView l1(sysUser);
if (l1 != user)
- result += QLatin1String("owner ") + bang[e] + QLatin1String("= '") + l1 + QLatin1String("' ") + join + QLatin1Char(' ');
+ result += "owner "_L1 + bang[e] + "= '"_L1 + l1 + "' "_L1 + join + u' ';
}
result.chop(join.size() + 2); // remove final " <join> "
@@ -2488,58 +2472,58 @@ QStringList QOCIDriver::tables(QSql::TableType type) const
QSqlQuery t(createResult());
t.setForwardOnly(true);
if (type & QSql::Tables) {
- const QLatin1String tableQuery("select owner, table_name from all_tables where ");
+ const auto tableQuery = "select owner, table_name from all_tables where "_L1;
const QString where = make_where_clause(user, AndExpression);
t.exec(tableQuery + where);
while (t.next()) {
if (t.value(0).toString().toUpper() != user.toUpper())
- tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
+ tl.append(t.value(0).toString() + u'.' + t.value(1).toString());
else
tl.append(t.value(1).toString());
}
// list all table synonyms as well
- const QLatin1String synonymQuery("select owner, synonym_name from all_synonyms where ");
+ const auto synonymQuery = "select owner, synonym_name from all_synonyms where "_L1;
t.exec(synonymQuery + where);
while (t.next()) {
if (t.value(0).toString() != d->user)
- tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
+ tl.append(t.value(0).toString() + u'.' + t.value(1).toString());
else
tl.append(t.value(1).toString());
}
}
if (type & QSql::Views) {
- const QLatin1String query("select owner, view_name from all_views where ");
+ const auto query = "select owner, view_name from all_views where "_L1;
const QString where = make_where_clause(user, AndExpression);
t.exec(query + where);
while (t.next()) {
if (t.value(0).toString().toUpper() != d->user.toUpper())
- tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
+ tl.append(t.value(0).toString() + u'.' + t.value(1).toString());
else
tl.append(t.value(1).toString());
}
}
if (type & QSql::SystemTables) {
- t.exec(QLatin1String("select table_name from dictionary"));
+ t.exec("select table_name from dictionary"_L1);
while (t.next()) {
tl.append(t.value(0).toString());
}
- const QLatin1String tableQuery("select owner, table_name from all_tables where ");
+ const auto tableQuery = "select owner, table_name from all_tables where "_L1;
const QString where = make_where_clause(user, OrExpression);
t.exec(tableQuery + where);
while (t.next()) {
if (t.value(0).toString().toUpper() != user.toUpper())
- tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
+ tl.append(t.value(0).toString() + u'.' + t.value(1).toString());
else
tl.append(t.value(1).toString());
}
// list all table synonyms as well
- const QLatin1String synonymQuery("select owner, synonym_name from all_synonyms where ");
+ const auto synonymQuery = "select owner, synonym_name from all_synonyms where "_L1;
t.exec(synonymQuery + where);
while (t.next()) {
if (t.value(0).toString() != d->user)
- tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
+ tl.append(t.value(0).toString() + u'.' + t.value(1).toString());
else
tl.append(t.value(1).toString());
}
@@ -2550,7 +2534,7 @@ QStringList QOCIDriver::tables(QSql::TableType type) const
void qSplitTableAndOwner(const QString & tname, QString * tbl,
QString * owner)
{
- int i = tname.indexOf(QLatin1Char('.')); // prefixed with owner?
+ qsizetype i = tname.indexOf(u'.'); // prefixed with owner?
if (i != -1) {
*tbl = tname.right(tname.length() - i - 1);
*owner = tname.left(i);
@@ -2569,14 +2553,13 @@ QSqlRecord QOCIDriver::record(const QString& tablename) const
QSqlQuery t(createResult());
// using two separate queries for this is A LOT faster than using
// eg. a sub-query on the sys.synonyms table
- QString stmt(QLatin1String("select column_name, data_type, data_length, "
+ QString stmt("select column_name, data_type, data_length, "
"data_precision, data_scale, nullable, data_default%1"
- "from all_tab_columns a "
- "where a.table_name=%2"));
+ "from all_tab_columns a "_L1);
if (d->serverVersion >= 9)
- stmt = stmt.arg(QLatin1String(", char_length "));
+ stmt = stmt.arg(", char_length "_L1);
else
- stmt = stmt.arg(QLatin1String(" "));
+ stmt = stmt.arg(" "_L1);
bool buildRecordInfo = false;
QString table, owner, tmpStmt;
qSplitTableAndOwner(tablename, &table, &owner);
@@ -2586,7 +2569,7 @@ QSqlRecord QOCIDriver::record(const QString& tablename) const
else
table = table.toUpper();
- tmpStmt = stmt.arg(QLatin1Char('\'') + table + QLatin1Char('\''));
+ tmpStmt = stmt + "where a.table_name='"_L1 + table + u'\'';
if (owner.isEmpty()) {
owner = d->user;
}
@@ -2596,15 +2579,12 @@ QSqlRecord QOCIDriver::record(const QString& tablename) const
else
owner = owner.toUpper();
- tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\'');
+ tmpStmt += " and a.owner='"_L1 + owner + u'\'';
t.setForwardOnly(true);
t.exec(tmpStmt);
if (!t.next()) { // try and see if the tablename is a synonym
- stmt = stmt + QLatin1String(" join all_synonyms b "
- "on a.owner=b.table_owner and a.table_name=b.table_name "
- "where b.owner='") + owner +
- QLatin1String("' and b.synonym_name='") + table +
- QLatin1Char('\'');
+ stmt = stmt + " join all_synonyms b on a.owner=b.table_owner and a.table_name=b.table_name "
+ "where b.owner='"_L1 + owner + "' and b.synonym_name='"_L1 + table + u'\'';
t.setForwardOnly(true);
t.exec(stmt);
if (t.next())
@@ -2612,13 +2592,13 @@ QSqlRecord QOCIDriver::record(const QString& tablename) const
} else {
buildRecordInfo = true;
}
- QStringList keywords = QStringList() << QLatin1String("NUMBER") << QLatin1String("FLOAT") << QLatin1String("BINARY_FLOAT")
- << QLatin1String("BINARY_DOUBLE");
+ QStringList keywords = QStringList() << "NUMBER"_L1 << "FLOAT"_L1 << "BINARY_FLOAT"_L1
+ << "BINARY_DOUBLE"_L1;
if (buildRecordInfo) {
do {
QMetaType ty = qDecodeOCIType(t.value(1).toString(), t.numericalPrecisionPolicy());
QSqlField f(t.value(0).toString(), ty);
- f.setRequired(t.value(5).toString() == QLatin1String("N"));
+ f.setRequired(t.value(5).toString() == "N"_L1);
f.setPrecision(t.value(4).toInt());
if (d->serverVersion >= 9 && (ty.id() == QMetaType::QString) && !t.isNull(3) && !keywords.contains(t.value(1).toString())) {
// Oracle9: data_length == size in bytes, char_length == amount of characters
@@ -2640,11 +2620,11 @@ QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const
if (!isOpen())
return idx;
QSqlQuery t(createResult());
- QString stmt(QLatin1String("select b.column_name, b.index_name, a.table_name, a.owner "
+ QString stmt("select b.column_name, b.index_name, a.table_name, a.owner "
"from all_constraints a, all_ind_columns b "
"where a.constraint_type='P' "
"and b.index_name = a.index_name "
- "and b.index_owner = a.owner"));
+ "and b.index_owner = a.owner"_L1);
bool buildIndex = false;
QString table, owner, tmpStmt;
@@ -2655,7 +2635,7 @@ QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const
else
table = table.toUpper();
- tmpStmt = stmt + QLatin1String(" and a.table_name='") + table + QLatin1Char('\'');
+ tmpStmt = stmt + " and a.table_name='"_L1 + table + u'\'';
if (owner.isEmpty()) {
owner = d->user;
}
@@ -2665,13 +2645,13 @@ QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const
else
owner = owner.toUpper();
- tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\'');
+ tmpStmt += " and a.owner='"_L1 + owner + u'\'';
t.setForwardOnly(true);
t.exec(tmpStmt);
if (!t.next()) {
- stmt += QLatin1String(" and a.table_name=(select tname from sys.synonyms "
- "where sname='") + table + QLatin1String("' and creator=a.owner)");
+ stmt += " and a.table_name=(select tname from sys.synonyms where sname='"_L1
+ + table + "' and creator=a.owner)"_L1;
t.setForwardOnly(true);
t.exec(stmt);
if (t.next()) {
@@ -2686,10 +2666,10 @@ QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const
tt.setForwardOnly(true);
idx.setName(t.value(1).toString());
do {
- tt.exec(QLatin1String("select data_type from all_tab_columns where table_name='") +
- t.value(2).toString() + QLatin1String("' and column_name='") +
- t.value(0).toString() + QLatin1String("' and owner='") +
- owner + QLatin1Char('\''));
+ tt.exec("select data_type from all_tab_columns where table_name='"_L1 +
+ t.value(2).toString() + "' and column_name='"_L1 +
+ t.value(0).toString() + "' and owner='"_L1 +
+ owner + u'\'');
if (!tt.next()) {
return QSqlIndex();
}
@@ -2703,21 +2683,21 @@ QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const
QString QOCIDriver::formatValue(const QSqlField &field, bool trimStrings) const
{
- switch (field.typeID()) {
+ switch (field.metaType().id()) {
case QMetaType::QDateTime: {
QDateTime datetime = field.value().toDateTime();
QString datestring;
if (datetime.isValid()) {
- datestring = QLatin1String("TO_DATE('") + QString::number(datetime.date().year())
- + QLatin1Char('-')
- + QString::number(datetime.date().month()) + QLatin1Char('-')
- + QString::number(datetime.date().day()) + QLatin1Char(' ')
- + QString::number(datetime.time().hour()) + QLatin1Char(':')
- + QString::number(datetime.time().minute()) + QLatin1Char(':')
+ datestring = "TO_DATE('"_L1 + QString::number(datetime.date().year())
+ + u'-'
+ + QString::number(datetime.date().month()) + u'-'
+ + QString::number(datetime.date().day()) + u' '
+ + QString::number(datetime.time().hour()) + u':'
+ + QString::number(datetime.time().minute()) + u':'
+ QString::number(datetime.time().second())
- + QLatin1String("','YYYY-MM-DD HH24:MI:SS')");
+ + "','YYYY-MM-DD HH24:MI:SS')"_L1;
} else {
- datestring = QLatin1String("NULL");
+ datestring = "NULL"_L1;
}
return datestring;
}
@@ -2725,13 +2705,13 @@ QString QOCIDriver::formatValue(const QSqlField &field, bool trimStrings) const
QDateTime datetime = field.value().toDateTime();
QString datestring;
if (datetime.isValid()) {
- datestring = QLatin1String("TO_DATE('")
- + QString::number(datetime.time().hour()) + QLatin1Char(':')
- + QString::number(datetime.time().minute()) + QLatin1Char(':')
+ datestring = "TO_DATE('"_L1
+ + QString::number(datetime.time().hour()) + u':'
+ + QString::number(datetime.time().minute()) + u':'
+ QString::number(datetime.time().second())
- + QLatin1String("','HH24:MI:SS')");
+ + "','HH24:MI:SS')"_L1;
} else {
- datestring = QLatin1String("NULL");
+ datestring = "NULL"_L1;
}
return datestring;
}
@@ -2739,12 +2719,12 @@ QString QOCIDriver::formatValue(const QSqlField &field, bool trimStrings) const
QDate date = field.value().toDate();
QString datestring;
if (date.isValid()) {
- datestring = QLatin1String("TO_DATE('") + QString::number(date.year()) +
- QLatin1Char('-') +
- QString::number(date.month()) + QLatin1Char('-') +
- QString::number(date.day()) + QLatin1String("','YYYY-MM-DD')");
+ datestring = "TO_DATE('"_L1 + QString::number(date.year()) +
+ u'-' +
+ QString::number(date.month()) + u'-' +
+ QString::number(date.day()) + "','YYYY-MM-DD')"_L1;
} else {
- datestring = QLatin1String("NULL");
+ datestring = "NULL"_L1;
}
return datestring;
}
@@ -2764,11 +2744,20 @@ QString QOCIDriver::escapeIdentifier(const QString &identifier, IdentifierType t
{
QString res = identifier;
if (!identifier.isEmpty() && !isIdentifierEscaped(identifier, type)) {
- res.replace(QLatin1Char('"'), QLatin1String("\"\""));
- res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
- res.replace(QLatin1Char('.'), QLatin1String("\".\""));
+ res.replace(u'"', "\"\""_L1);
+ res.replace(u'.', "\".\""_L1);
+ res = u'"' + res + u'"';
}
return res;
}
+int QOCIDriver::maximumIdentifierLength(IdentifierType type) const
+{
+ Q_D(const QOCIDriver);
+ Q_UNUSED(type);
+ return d->serverVersion > 12 ? 128 : 30;
+}
+
QT_END_NAMESPACE
+
+#include "moc_qsql_oci_p.cpp"
diff --git a/src/plugins/sqldrivers/oci/qsql_oci_p.h b/src/plugins/sqldrivers/oci/qsql_oci_p.h
index a3366abb77..91fef9de7b 100644
--- a/src/plugins/sqldrivers/oci/qsql_oci_p.h
+++ b/src/plugins/sqldrivers/oci/qsql_oci_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSQL_OCI_H
#define QSQL_OCI_H
@@ -78,7 +42,7 @@ public:
explicit QOCIDriver(QObject *parent = nullptr);
QOCIDriver(OCIEnv *env, OCISvcCtx *ctx, QObject *parent = nullptr);
~QOCIDriver();
- bool hasFeature(DriverFeature f) const;
+ bool hasFeature(DriverFeature f) const override;
bool open(const QString &db,
const QString &user,
const QString &password,
@@ -94,6 +58,7 @@ public:
bool trimStrings) const override;
QVariant handle() const override;
QString escapeIdentifier(const QString &identifier, IdentifierType) const override;
+ int maximumIdentifierLength(IdentifierType type) const override;
protected:
bool beginTransaction() override;
diff --git a/src/plugins/sqldrivers/odbc/CMakeLists.txt b/src/plugins/sqldrivers/odbc/CMakeLists.txt
index 99a6b9104d..7812aced2c 100644
--- a/src/plugins/sqldrivers/odbc/CMakeLists.txt
+++ b/src/plugins/sqldrivers/odbc/CMakeLists.txt
@@ -1,20 +1,22 @@
-# Generated from odbc.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## QODBCDriverPlugin Plugin:
#####################################################################
-qt_find_package(ODBC) # special case
+qt_find_package(ODBC)
qt_internal_add_plugin(QODBCDriverPlugin
OUTPUT_NAME qsqlodbc
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_odbc.cpp qsql_odbc_p.h
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
+ QT_NO_CONTEXTLESS_CONNECT
LIBRARIES
ODBC::ODBC
Qt::Core
@@ -22,9 +24,6 @@ qt_internal_add_plugin(QODBCDriverPlugin
Qt::SqlPrivate
)
-#### Keys ignored in scope 1:.:.:odbc.pro:<TRUE>:
-# OTHER_FILES = "odbc.json"
-
## Scopes:
#####################################################################
diff --git a/src/plugins/sqldrivers/odbc/main.cpp b/src/plugins/sqldrivers/odbc/main.cpp
index 92478ecbd1..0310be0c64 100644
--- a/src/plugins/sqldrivers/odbc/main.cpp
+++ b/src/plugins/sqldrivers/odbc/main.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qsqldriverplugin.h>
#include <qstringlist.h>
@@ -43,6 +7,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QODBCDriverPlugin : public QSqlDriverPlugin
{
Q_OBJECT
@@ -61,7 +27,7 @@ QODBCDriverPlugin::QODBCDriverPlugin()
QSqlDriver* QODBCDriverPlugin::create(const QString &name)
{
- if (name == QLatin1String("QODBC")) {
+ if (name == "QODBC"_L1) {
QODBCDriver* driver = new QODBCDriver();
return driver;
}
diff --git a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
index 5a77c26b62..22493f6029 100644
--- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsql_odbc_p.h"
#include <qsqlrecord.h>
@@ -50,6 +14,7 @@
#include <qsqlerror.h>
#include <qsqlfield.h>
#include <qsqlindex.h>
+#include <qstringconverter.h>
#include <qstringlist.h>
#include <qvariant.h>
#include <qvarlengtharray.h>
@@ -57,59 +22,87 @@
#include <QSqlQuery>
#include <QtSql/private/qsqldriver_p.h>
#include <QtSql/private/qsqlresult_p.h>
+#include "private/qtools_p.h"
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+// non-standard ODBC SQL data type from SQL Server sometimes used instead of SQL_TIME
+#ifndef SQL_SS_TIME2
+#define SQL_SS_TIME2 (-154)
+#endif
+
// undefine this to prevent initial check of the ODBC driver
#define ODBC_CHECK_DRIVER
-static const int COLNAMESIZE = 256;
-static const SQLSMALLINT TABLENAMESIZE = 128;
+static constexpr int COLNAMESIZE = 256;
+static constexpr SQLSMALLINT TABLENAMESIZE = 128;
//Map Qt parameter types to ODBC types
-static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
+static constexpr SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
-inline static QString fromSQLTCHAR(const QVarLengthArray<SQLTCHAR>& input, qsizetype size=-1)
+class SqlStmtHandle
{
- QString result;
+public:
+ SqlStmtHandle(SQLHANDLE hDbc = SQL_NULL_HSTMT)
+ {
+ SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &stmtHandle);
+ }
+ ~SqlStmtHandle()
+ {
+ if (stmtHandle != SQL_NULL_HSTMT)
+ SQLFreeHandle(SQL_HANDLE_STMT, stmtHandle);
+ }
+ SQLHANDLE handle() const
+ {
+ return stmtHandle;
+ }
+ bool isValid() const
+ {
+ return stmtHandle != SQL_NULL_HSTMT;
+ }
+ SQLHANDLE stmtHandle = SQL_NULL_HSTMT;
+};
+template<typename C, int SIZE = sizeof(SQLTCHAR)>
+inline static QString fromSQLTCHAR(const C &input, qsizetype size = -1)
+{
// Remove any trailing \0 as some drivers misguidedly append one
- int realsize = qMin(size, input.size());
- if (realsize > 0 && input[realsize-1] == 0)
+ qsizetype realsize = qMin(size, input.size());
+ if (realsize > 0 && input[realsize - 1] == 0)
realsize--;
- switch(sizeof(SQLTCHAR)) {
- case 1:
- result=QString::fromUtf8((const char *)input.constData(), realsize);
- break;
- case 2:
- result = QString::fromUtf16(reinterpret_cast<const char16_t *>(input.constData()), realsize);
- break;
- case 4:
- result = QString::fromUcs4(reinterpret_cast<const char32_t *>(input.constData()), realsize);
- break;
- default:
- qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle this.", int(sizeof(SQLTCHAR)));
- }
- return result;
+ if constexpr (SIZE == 1)
+ return QString::fromUtf8(reinterpret_cast<const char *>(input.constData()), realsize);
+ else if constexpr (SIZE == 2)
+ return QString::fromUtf16(reinterpret_cast<const char16_t *>(input.constData()), realsize);
+ else if constexpr (SIZE == 4)
+ return QString::fromUcs4(reinterpret_cast<const char32_t *>(input.constData()), realsize);
+ else
+ static_assert(QtPrivate::value_dependent_false<SIZE>(),
+ "Don't know how to handle sizeof(SQLTCHAR) != 1/2/4");
+}
+
+template<int SIZE = sizeof(SQLTCHAR)>
+QStringConverter::Encoding encodingForSqlTChar()
+{
+ if constexpr (SIZE == 1)
+ return QStringConverter::Utf8;
+ else if constexpr (SIZE == 2)
+ return QStringConverter::Utf16;
+ else if constexpr (SIZE == 4)
+ return QStringConverter::Utf32;
+ else
+ static_assert(QtPrivate::value_dependent_false<SIZE>(),
+ "Don't know how to handle sizeof(SQLTCHAR) != 1/2/4");
}
inline static QVarLengthArray<SQLTCHAR> toSQLTCHAR(const QString &input)
{
QVarLengthArray<SQLTCHAR> result;
- result.resize(input.size());
- switch(sizeof(SQLTCHAR)) {
- case 1:
- memcpy(result.data(), input.toUtf8().data(), input.size());
- break;
- case 2:
- memcpy(result.data(), input.unicode(), input.size() * 2);
- break;
- case 4:
- memcpy(result.data(), input.toUcs4().data(), input.size() * 4);
- break;
- default:
- qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle this.", int(sizeof(SQLTCHAR)));
- }
- result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't.
+ QStringEncoder enc(encodingForSqlTChar());
+ result.resize(enc.requiredSpace(input.size()));
+ const auto end = enc.appendToBuffer(reinterpret_cast<char *>(result.data()), input);
+ result.resize((end - reinterpret_cast<char *>(result.data())) / sizeof(SQLTCHAR));
return result;
}
@@ -118,7 +111,7 @@ class QODBCDriverPrivate : public QSqlDriverPrivate
Q_DECLARE_PUBLIC(QODBCDriver)
public:
- enum DefaultCase {Lower, Mixed, Upper, Sensitive};
+ enum class DefaultCase {Lower, Mixed, Upper, Sensitive};
using QSqlDriverPrivate::QSqlDriverPrivate;
SQLHANDLE hEnv = nullptr;
@@ -139,15 +132,18 @@ public:
void checkHasMultiResults();
void checkSchemaUsage();
void checkDateTimePrecision();
+ void checkDefaultCase();
bool setConnectionOptions(const QString& connOpts);
void splitTableQualifier(const QString &qualifier, QString &catalog,
- QString &schema, QString &table);
- DefaultCase defaultCase() const;
+ QString &schema, QString &table) const;
QString adjustCase(const QString&) const;
QChar quoteChar();
+ SQLRETURN sqlFetchNext(const SqlStmtHandle &hStmt) const;
+ SQLRETURN sqlFetchNext(SQLHANDLE hStmt) const;
private:
bool isQuoteInitialized = false;
- QChar quote = QLatin1Char('"');
+ QChar quote = u'"';
+ DefaultCase m_defaultCase = DefaultCase::Mixed;
};
class QODBCResultPrivate;
@@ -227,118 +223,121 @@ void QODBCResultPrivate::updateStmtHandleState()
disconnectCount = drv_d_func() ? drv_d_func()->disconnectCount : 0;
}
-static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode = nullptr)
+struct DiagRecord
+{
+ QString description;
+ QString sqlState;
+ QString errorCode;
+};
+static QList<DiagRecord> qWarnODBCHandle(int handleType, SQLHANDLE handle)
{
- SQLINTEGER nativeCode_ = 0;
+ SQLINTEGER nativeCode = 0;
SQLSMALLINT msgLen = 0;
+ SQLSMALLINT i = 1;
SQLRETURN r = SQL_NO_DATA;
- SQLTCHAR state_[SQL_SQLSTATE_SIZE+1];
- QVarLengthArray<SQLTCHAR> description_(SQL_MAX_MESSAGE_LENGTH);
- QString result;
- int i = 1;
+ QVarLengthArray<SQLTCHAR, SQL_SQLSTATE_SIZE + 1> state(SQL_SQLSTATE_SIZE + 1);
+ QVarLengthArray<SQLTCHAR, SQL_MAX_MESSAGE_LENGTH + 1> description(SQL_MAX_MESSAGE_LENGTH + 1);
+ QList<DiagRecord> result;
- description_[0] = 0;
+ if (!handle)
+ return result;
do {
r = SQLGetDiagRec(handleType,
handle,
i,
- state_,
- &nativeCode_,
- 0,
- 0,
+ state.data(),
+ &nativeCode,
+ description.data(),
+ description.size(),
&msgLen);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && msgLen > 0)
- description_.resize(msgLen+1);
- r = SQLGetDiagRec(handleType,
- handle,
- i,
- state_,
- &nativeCode_,
- description_.data(),
- description_.size(),
- &msgLen);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
- if (nativeCode)
- *nativeCode = nativeCode_;
- const QString tmpstore = fromSQLTCHAR(description_, msgLen);
- if (result != tmpstore) {
- if (!result.isEmpty())
- result += QLatin1Char(' ');
- result += tmpstore;
- }
+ if (msgLen >= description.size()) {
+ description.resize(msgLen + 1); // incl. \0 termination
+ continue;
+ }
+ if (SQL_SUCCEEDED(r)) {
+ result.push_back({fromSQLTCHAR(description, msgLen),
+ fromSQLTCHAR(state),
+ QString::number(nativeCode)});
} else if (r == SQL_ERROR || r == SQL_INVALID_HANDLE) {
- return result;
+ break;
}
++i;
} while (r != SQL_NO_DATA);
return result;
}
-static QString qODBCWarn(const SQLHANDLE hStmt, const SQLHANDLE envHandle = 0,
- const SQLHANDLE pDbC = 0, int *nativeCode = nullptr)
+static QList<DiagRecord> qODBCWarn(const SQLHANDLE hStmt,
+ const SQLHANDLE envHandle = nullptr,
+ const SQLHANDLE pDbC = nullptr)
{
- QString result;
- if (envHandle)
- result += qWarnODBCHandle(SQL_HANDLE_ENV, envHandle, nativeCode);
- if (pDbC) {
- const QString dMessage = qWarnODBCHandle(SQL_HANDLE_DBC, pDbC, nativeCode);
- if (!dMessage.isEmpty()) {
- if (!result.isEmpty())
- result += QLatin1Char(' ');
- result += dMessage;
- }
- }
- if (hStmt) {
- const QString hMessage = qWarnODBCHandle(SQL_HANDLE_STMT, hStmt, nativeCode);
- if (!hMessage.isEmpty()) {
- if (!result.isEmpty())
- result += QLatin1Char(' ');
- result += hMessage;
- }
- }
+ QList<DiagRecord> result;
+ result.append(qWarnODBCHandle(SQL_HANDLE_ENV, envHandle));
+ result.append(qWarnODBCHandle(SQL_HANDLE_DBC, pDbC));
+ result.append(qWarnODBCHandle(SQL_HANDLE_STMT, hStmt));
return result;
}
-static QString qODBCWarn(const QODBCResultPrivate* odbc, int *nativeCode = nullptr)
+static QList<DiagRecord> qODBCWarn(const QODBCResultPrivate *odbc)
{
- return qODBCWarn(odbc->hStmt, odbc->dpEnv(), odbc->dpDbc(), nativeCode);
+ return qODBCWarn(odbc->hStmt, odbc->dpEnv(), odbc->dpDbc());
}
-static QString qODBCWarn(const QODBCDriverPrivate* odbc, int *nativeCode = nullptr)
+static QList<DiagRecord> qODBCWarn(const QODBCDriverPrivate *odbc)
{
- return qODBCWarn(0, odbc->hEnv, odbc->hDbc, nativeCode);
+ return qODBCWarn(nullptr, odbc->hEnv, odbc->hDbc);
}
-static void qSqlWarning(const QString& message, const QODBCResultPrivate* odbc)
+static DiagRecord combineRecords(const QList<DiagRecord> &records)
{
- qWarning() << message << "\tError:" << qODBCWarn(odbc);
+ const auto add = [](const DiagRecord &a, const DiagRecord &b) {
+ return DiagRecord{a.description + u' ' + b.description,
+ a.sqlState + u';' + b.sqlState,
+ a.errorCode + u';' + b.errorCode};
+ };
+ if (records.isEmpty())
+ return {};
+ return std::accumulate(std::next(records.begin()), records.end(), records.front(), add);
}
-static void qSqlWarning(const QString &message, const QODBCDriverPrivate *odbc)
+static QSqlError errorFromDiagRecords(const QString &err,
+ QSqlError::ErrorType type,
+ const QList<DiagRecord> &records)
{
- qWarning() << message << "\tError:" << qODBCWarn(odbc);
+ if (records.empty())
+ return QSqlError("QODBC: unknown error"_L1, {}, type, {});
+ const auto combined = combineRecords(records);
+ return QSqlError("QODBC: "_L1 + err, combined.description + ", "_L1 + combined.sqlState, type,
+ combined.errorCode);
}
-static void qSqlWarning(const QString &message, const SQLHANDLE hStmt)
+static QString errorStringFromDiagRecords(const QList<DiagRecord>& records)
{
- qWarning() << message << "\tError:" << qODBCWarn(hStmt);
+ const auto combined = combineRecords(records);
+ return combined.description;
}
-static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, const QODBCResultPrivate* p)
+template<class T>
+static void qSqlWarning(const QString &message, T &&val)
{
- int nativeCode = -1;
- QString message = qODBCWarn(p, &nativeCode);
- return QSqlError(QLatin1String("QODBC: ") + err, message, type,
- nativeCode != -1 ? QString::number(nativeCode) : QString());
+ const auto addMsg = errorStringFromDiagRecords(qODBCWarn(val));
+ if (addMsg.isEmpty())
+ qWarning() << message;
+ else
+ qWarning() << message << "\tError:" << addMsg;
+}
+
+static QSqlError qMakeError(const QString &err,
+ QSqlError::ErrorType type,
+ const QODBCResultPrivate *p)
+{
+ return errorFromDiagRecords(err, type, qODBCWarn(p));
}
-static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
- const QODBCDriverPrivate* p)
+static QSqlError qMakeError(const QString &err,
+ QSqlError::ErrorType type,
+ const QODBCDriverPrivate *p)
{
- int nativeCode = -1;
- QString message = qODBCWarn(p, &nativeCode);
- return QSqlError(QLatin1String("QODBC: ") + err, message, type,
- nativeCode != -1 ? QString::number(nativeCode) : QString());
+ return errorFromDiagRecords(err, type, qODBCWarn(p));
}
static QMetaType qDecodeODBCType(SQLSMALLINT sqltype, bool isSigned = true)
@@ -376,6 +375,7 @@ static QMetaType qDecodeODBCType(SQLSMALLINT sqltype, bool isSigned = true)
case SQL_TYPE_DATE:
type = QMetaType::QDate;
break;
+ case SQL_SS_TIME2:
case SQL_TIME:
case SQL_TYPE_TIME:
type = QMetaType::QTime;
@@ -404,119 +404,69 @@ static QMetaType qDecodeODBCType(SQLSMALLINT sqltype, bool isSigned = true)
return QMetaType(type);
}
-static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool unicode = false)
+template <typename CT>
+static QVariant getStringDataImpl(SQLHANDLE hStmt, SQLUSMALLINT column, qsizetype colSize, SQLSMALLINT targetType)
{
QString fieldVal;
SQLRETURN r = SQL_ERROR;
SQLLEN lengthIndicator = 0;
-
- // NB! colSize must be a multiple of 2 for unicode enabled DBs
- if (colSize <= 0) {
- colSize = 256;
- } else if (colSize > 65536) { // limit buffer size to 64 KB
- colSize = 65536;
- } else {
- colSize++; // make sure there is room for more than the 0 termination
- }
- if (unicode) {
+ QVarLengthArray<CT> buf(colSize);
+ while (true) {
r = SQLGetData(hStmt,
- column+1,
- SQL_C_TCHAR,
- NULL,
- 0,
- &lengthIndicator);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
- colSize = int(lengthIndicator / sizeof(SQLTCHAR) + 1);
- QVarLengthArray<SQLTCHAR> buf(colSize);
- memset(buf.data(), 0, colSize*sizeof(SQLTCHAR));
- while (true) {
- r = SQLGetData(hStmt,
- column+1,
- SQL_C_TCHAR,
- (SQLPOINTER)buf.data(),
- colSize*sizeof(SQLTCHAR),
- &lengthIndicator);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
- if (lengthIndicator == SQL_NULL_DATA) {
- fieldVal.clear();
- break;
- }
- // starting with ODBC Native Client 2012, SQL_NO_TOTAL is returned
- // instead of the length (which sometimes was wrong in older versions)
- // see link for more info: http://msdn.microsoft.com/en-us/library/jj219209.aspx
- // if length indicator equals SQL_NO_TOTAL, indicating that
- // more data can be fetched, but size not known, collect data
- // and fetch next block
- if (lengthIndicator == SQL_NO_TOTAL) {
- fieldVal += fromSQLTCHAR(buf, colSize);
- continue;
- }
- // if SQL_SUCCESS_WITH_INFO is returned, indicating that
- // more data can be fetched, the length indicator does NOT
- // contain the number of bytes returned - it contains the
- // total number of bytes that CAN be fetched
- int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : int(lengthIndicator / sizeof(SQLTCHAR));
- fieldVal += fromSQLTCHAR(buf, rSize);
- if (lengthIndicator < SQLLEN(colSize*sizeof(SQLTCHAR))) {
- // workaround for Drivermanagers that don't return SQL_NO_DATA
- break;
- }
- } else if (r == SQL_NO_DATA) {
- break;
- } else {
- qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
- fieldVal.clear();
- break;
+ column + 1,
+ targetType,
+ SQLPOINTER(buf.data()), SQLINTEGER(buf.size() * sizeof(CT)),
+ &lengthIndicator);
+ if (SQL_SUCCEEDED(r)) {
+ if (lengthIndicator == SQL_NULL_DATA) {
+ return {};
}
- }
- } else {
- r = SQLGetData(hStmt,
- column+1,
- SQL_C_CHAR,
- NULL,
- 0,
- &lengthIndicator);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
- colSize = lengthIndicator + 1;
- QVarLengthArray<SQLCHAR> buf(colSize);
- while (true) {
- r = SQLGetData(hStmt,
- column+1,
- SQL_C_CHAR,
- (SQLPOINTER)buf.data(),
- colSize,
- &lengthIndicator);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
- if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
- fieldVal.clear();
- break;
- }
- // if SQL_SUCCESS_WITH_INFO is returned, indicating that
- // more data can be fetched, the length indicator does NOT
- // contain the number of bytes returned - it contains the
- // total number of bytes that CAN be fetched
- qsizetype rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator;
- // Remove any trailing \0 as some drivers misguidedly append one
- int realsize = qMin(rSize, buf.size());
- if (realsize > 0 && buf[realsize - 1] == 0)
- realsize--;
- fieldVal += QString::fromUtf8(reinterpret_cast<const char *>(buf.constData()), realsize);
- if (lengthIndicator < SQLLEN(colSize)) {
- // workaround for Drivermanagers that don't return SQL_NO_DATA
- break;
- }
- } else if (r == SQL_NO_DATA) {
- break;
- } else {
- qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
- fieldVal.clear();
+ // starting with ODBC Native Client 2012, SQL_NO_TOTAL is returned
+ // instead of the length (which sometimes was wrong in older versions)
+ // see link for more info: http://msdn.microsoft.com/en-us/library/jj219209.aspx
+ // if length indicator equals SQL_NO_TOTAL, indicating that
+ // more data can be fetched, but size not known, collect data
+ // and fetch next block
+ if (lengthIndicator == SQL_NO_TOTAL) {
+ fieldVal += fromSQLTCHAR<QVarLengthArray<CT>, sizeof(CT)>(buf, buf.size());
+ continue;
+ }
+ // if SQL_SUCCESS_WITH_INFO is returned, indicating that
+ // more data can be fetched, the length indicator does NOT
+ // contain the number of bytes returned - it contains the
+ // total number of bytes that CAN be fetched
+ const qsizetype rSize = (r == SQL_SUCCESS_WITH_INFO)
+ ? buf.size()
+ : qsizetype(lengthIndicator / sizeof(CT));
+ fieldVal += fromSQLTCHAR<QVarLengthArray<CT>, sizeof(CT)>(buf, rSize);
+ // lengthIndicator does not contain the termination character
+ if (lengthIndicator < SQLLEN((buf.size() - 1) * sizeof(CT))) {
+ // workaround for Drivermanagers that don't return SQL_NO_DATA
break;
}
+ } else if (r == SQL_NO_DATA) {
+ break;
+ } else {
+ qSqlWarning("QODBC::getStringData: Error while fetching data"_L1, hStmt);
+ return {};
}
}
return fieldVal;
}
+static QVariant qGetStringData(SQLHANDLE hStmt, SQLUSMALLINT column, int colSize, bool unicode)
+{
+ if (colSize <= 0) {
+ colSize = 256; // default Prealloc size of QVarLengthArray
+ } else if (colSize > 65536) { // limit buffer size to 64 KB
+ colSize = 65536;
+ } else {
+ colSize++; // make sure there is room for more than the 0 termination
+ }
+ return unicode ? getStringDataImpl<SQLTCHAR>(hStmt, column, colSize, SQL_C_TCHAR)
+ : getStringDataImpl<SQLCHAR>(hStmt, column, colSize, SQL_C_CHAR);
+}
+
static QVariant qGetBinaryData(SQLHANDLE hStmt, int column)
{
QByteArray fieldVal;
@@ -528,19 +478,19 @@ static QVariant qGetBinaryData(SQLHANDLE hStmt, int column)
SQLLEN lengthIndicator = 0;
SQLRETURN r = SQL_ERROR;
- QVarLengthArray<SQLTCHAR> colName(COLNAMESIZE);
+ QVarLengthArray<SQLTCHAR, COLNAMESIZE> colName(COLNAMESIZE);
r = SQLDescribeCol(hStmt,
column + 1,
- colName.data(),
- COLNAMESIZE,
+ colName.data(), SQLSMALLINT(colName.size()),
&colNameLen,
&colType,
&colSize,
&colScale,
&nullable);
if (r != SQL_SUCCESS)
- qWarning() << "qGetBinaryData: Unable to describe column" << column;
+ qSqlWarning(("QODBC::qGetBinaryData: Unable to describe column %1"_L1)
+ .arg(QString::number(column)), hStmt);
// SQLDescribeCol may return 0 if size cannot be determined
if (!colSize)
colSize = 255;
@@ -555,7 +505,7 @@ static QVariant qGetBinaryData(SQLHANDLE hStmt, int column)
const_cast<char *>(fieldVal.constData() + read),
colSize,
&lengthIndicator);
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
+ if (!SQL_SUCCEEDED(r))
break;
if (lengthIndicator == SQL_NULL_DATA)
return QVariant(QMetaType(QMetaType::QByteArray));
@@ -584,7 +534,7 @@ static QVariant qGetIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
(SQLPOINTER)&intbuf,
sizeof(intbuf),
&lengthIndicator);
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
+ if (!SQL_SUCCEEDED(r))
return QVariant();
if (lengthIndicator == SQL_NULL_DATA)
return QVariant(QMetaType::fromType<int>());
@@ -604,7 +554,7 @@ static QVariant qGetDoubleData(SQLHANDLE hStmt, int column)
(SQLPOINTER) &dblbuf,
0,
&lengthIndicator);
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+ if (!SQL_SUCCEEDED(r)) {
return QVariant();
}
if (lengthIndicator == SQL_NULL_DATA)
@@ -624,7 +574,7 @@ static QVariant qGetBigIntData(SQLHANDLE hStmt, int column, bool isSigned = true
(SQLPOINTER) &lngbuf,
sizeof(lngbuf),
&lengthIndicator);
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
+ if (!SQL_SUCCEEDED(r))
return QVariant();
if (lengthIndicator == SQL_NULL_DATA)
return QVariant(QMetaType::fromType<qlonglong>());
@@ -640,28 +590,25 @@ static bool isAutoValue(const SQLHANDLE hStmt, int column)
SQLLEN nNumericAttribute = 0; // Check for auto-increment
const SQLRETURN r = ::SQLColAttribute(hStmt, column + 1, SQL_DESC_AUTO_UNIQUE_VALUE,
0, 0, 0, &nNumericAttribute);
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
- qSqlWarning(QStringLiteral("qMakeField: Unable to get autovalue attribute for column ")
- + QString::number(column), hStmt);
+ if (!SQL_SUCCEEDED(r)) {
+ qSqlWarning(("QODBC::isAutoValue: Unable to get autovalue attribute for column %1"_L1)
+ .arg(QString::number(column)), hStmt);
return false;
}
return nNumericAttribute != SQL_FALSE;
}
-static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMessage);
-
// creates a QSqlField from a valid hStmt generated
// by SQLColumns. The hStmt has to point to a valid position.
static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, const QODBCDriverPrivate* p)
{
- QString fname = qGetStringData(hStmt, 3, -1, p->unicode);
+ QString fname = qGetStringData(hStmt, 3, -1, p->unicode).toString();
int type = qGetIntData(hStmt, 4).toInt(); // column type
QSqlField f(fname, qDecodeODBCType(type, p));
QVariant var = qGetIntData(hStmt, 6);
f.setLength(var.isNull() ? -1 : var.toInt()); // column size
var = qGetIntData(hStmt, 8).toInt();
f.setPrecision(var.isNull() ? -1 : var.toInt()); // precision
- f.setSqlType(type);
int required = qGetIntData(hStmt, 10).toInt(); // nullable-flag
// required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
if (required == SQL_NO_NULLS)
@@ -672,16 +619,7 @@ static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, const QODBCDriverPrivate*
return f;
}
-static QSqlField qMakeFieldInfo(const QODBCResultPrivate* p, int i )
-{
- QString errorMessage;
- const QSqlField result = qMakeFieldInfo(p->hStmt, i, &errorMessage);
- if (!errorMessage.isEmpty())
- qSqlWarning(errorMessage, p);
- return result;
-}
-
-static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMessage)
+static QSqlField qMakeFieldInfo(const QODBCResultPrivate *p, int i)
{
SQLSMALLINT colNameLen;
SQLSMALLINT colType;
@@ -689,12 +627,10 @@ static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMess
SQLSMALLINT colScale;
SQLSMALLINT nullable;
SQLRETURN r = SQL_ERROR;
- QVarLengthArray<SQLTCHAR> colName(COLNAMESIZE);
- errorMessage->clear();
- r = SQLDescribeCol(hStmt,
+ QVarLengthArray<SQLTCHAR, COLNAMESIZE> colName(COLNAMESIZE);
+ r = SQLDescribeCol(p->hStmt,
i+1,
- colName.data(),
- (SQLSMALLINT)COLNAMESIZE,
+ colName.data(), SQLSMALLINT(colName.size()),
&colNameLen,
&colType,
&colSize,
@@ -702,12 +638,13 @@ static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMess
&nullable);
if (r != SQL_SUCCESS) {
- *errorMessage = QStringLiteral("qMakeField: Unable to describe column ") + QString::number(i);
+ qSqlWarning(("QODBC::qMakeFieldInfo: Unable to describe column %1"_L1)
+ .arg(QString::number(i)), p);
return QSqlField();
}
SQLLEN unsignedFlag = SQL_FALSE;
- r = SQLColAttribute (hStmt,
+ r = SQLColAttribute (p->hStmt,
i + 1,
SQL_DESC_UNSIGNED,
0,
@@ -715,15 +652,14 @@ static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMess
0,
&unsignedFlag);
if (r != SQL_SUCCESS) {
- qSqlWarning(QStringLiteral("qMakeField: Unable to get column attributes for column ")
- + QString::number(i), hStmt);
+ qSqlWarning(("QODBC::qMakeFieldInfo: Unable to get column attributes for column %1"_L1)
+ .arg(QString::number(i)), p);
}
const QString qColName(fromSQLTCHAR(colName, colNameLen));
// nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
QMetaType type = qDecodeODBCType(colType, unsignedFlag == SQL_FALSE);
QSqlField f(qColName, type);
- f.setSqlType(colType);
f.setLength(colSize == 0 ? -1 : int(colSize));
f.setPrecision(colScale == 0 ? -1 : int(colScale));
if (nullable == SQL_NO_NULLS)
@@ -731,19 +667,24 @@ static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMess
else if (nullable == SQL_NULLABLE)
f.setRequired(false);
// else we don't know
- f.setAutoValue(isAutoValue(hStmt, i));
- QVarLengthArray<SQLTCHAR> tableName(TABLENAMESIZE);
+ f.setAutoValue(isAutoValue(p->hStmt, i));
+ QVarLengthArray<SQLTCHAR, TABLENAMESIZE> tableName(TABLENAMESIZE);
SQLSMALLINT tableNameLen;
- r = SQLColAttribute(hStmt, i + 1, SQL_DESC_BASE_TABLE_NAME, tableName.data(),
- TABLENAMESIZE, &tableNameLen, 0);
+ r = SQLColAttribute(p->hStmt,
+ i + 1,
+ SQL_DESC_BASE_TABLE_NAME,
+ tableName.data(),
+ SQLSMALLINT(tableName.size() * sizeof(SQLTCHAR)), // SQLColAttribute needs/returns size in bytes
+ &tableNameLen,
+ 0);
if (r == SQL_SUCCESS)
- f.setTableName(fromSQLTCHAR(tableName, tableNameLen));
+ f.setTableName(fromSQLTCHAR(tableName, tableNameLen / sizeof(SQLTCHAR)));
return f;
}
static size_t qGetODBCVersion(const QString &connOpts)
{
- if (connOpts.contains(QLatin1String("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC3"), Qt::CaseInsensitive))
+ if (connOpts.contains("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC3"_L1, Qt::CaseInsensitive))
return SQL_OV_ODBC3;
return SQL_OV_ODBC2;
}
@@ -758,26 +699,47 @@ QChar QODBCDriverPrivate::quoteChar()
&driverResponse,
sizeof(driverResponse),
&length);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+ if (SQL_SUCCEEDED(r))
quote = QChar(driverResponse[0]);
else
- quote = QLatin1Char('"');
+ quote = u'"';
isQuoteInitialized = true;
}
return quote;
}
+SQLRETURN QODBCDriverPrivate::sqlFetchNext(const SqlStmtHandle &hStmt) const
+{
+ return sqlFetchNext(hStmt.handle());
+}
+
+SQLRETURN QODBCDriverPrivate::sqlFetchNext(SQLHANDLE hStmt) const
+{
+ if (hasSQLFetchScroll)
+ return SQLFetchScroll(hStmt, SQL_FETCH_NEXT, 0);
+ return SQLFetch(hStmt);
+}
+
+static SQLRETURN qt_string_SQLSetConnectAttr(SQLHDBC handle, SQLINTEGER attr, const QString &val)
+{
+ auto encoded = toSQLTCHAR(val);
+ return SQLSetConnectAttr(handle, attr,
+ encoded.data(),
+ SQLINTEGER(encoded.size() * sizeof(SQLTCHAR))); // size in bytes
+}
+
bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
{
// Set any connection attributes
- const QStringList opts(connOpts.split(QLatin1Char(';'), Qt::SkipEmptyParts));
+ const QStringList opts(connOpts.split(u';', Qt::SkipEmptyParts));
SQLRETURN r = SQL_SUCCESS;
for (int i = 0; i < opts.count(); ++i) {
const QString tmp(opts.at(i));
int idx;
- if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
- qWarning() << "QODBCDriver::open: Illegal connect option value '" << tmp << '\'';
+ if ((idx = tmp.indexOf(u'=')) == -1) {
+ qSqlWarning(("QODBCDriver::open: Illegal connect option value '%1'"_L1)
+ .arg(tmp), this);
continue;
}
const QString opt(tmp.left(idx));
@@ -785,158 +747,156 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
SQLUINTEGER v = 0;
r = SQL_SUCCESS;
- if (opt.toUpper() == QLatin1String("SQL_ATTR_ACCESS_MODE")) {
- if (val.toUpper() == QLatin1String("SQL_MODE_READ_ONLY")) {
+ if (opt.toUpper() == "SQL_ATTR_ACCESS_MODE"_L1) {
+ if (val.toUpper() == "SQL_MODE_READ_ONLY"_L1) {
v = SQL_MODE_READ_ONLY;
- } else if (val.toUpper() == QLatin1String("SQL_MODE_READ_WRITE")) {
+ } else if (val.toUpper() == "SQL_MODE_READ_WRITE"_L1) {
v = SQL_MODE_READ_WRITE;
} else {
- qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
+ qSqlWarning(("QODBCDriver::open: Unknown option value '%1'"_L1)
+ .arg(val), this);
continue;
}
r = SQLSetConnectAttr(hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) size_t(v), 0);
- } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_TIMEOUT")) {
+ } else if (opt.toUpper() == "SQL_ATTR_CONNECTION_TIMEOUT"_L1) {
v = val.toUInt();
r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) size_t(v), 0);
- } else if (opt.toUpper() == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
+ } else if (opt.toUpper() == "SQL_ATTR_LOGIN_TIMEOUT"_L1) {
v = val.toUInt();
r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) size_t(v), 0);
- } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CURRENT_CATALOG")) {
- val.utf16(); // 0 terminate
- r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG,
- toSQLTCHAR(val).data(),
- SQLINTEGER(val.length() * sizeof(SQLTCHAR)));
- } else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) {
- if (val.toUpper() == QLatin1String("SQL_TRUE")) {
+ } else if (opt.toUpper() == "SQL_ATTR_CURRENT_CATALOG"_L1) {
+ r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, val);
+ } else if (opt.toUpper() == "SQL_ATTR_METADATA_ID"_L1) {
+ if (val.toUpper() == "SQL_TRUE"_L1) {
v = SQL_TRUE;
- } else if (val.toUpper() == QLatin1String("SQL_FALSE")) {
+ } else if (val.toUpper() == "SQL_FALSE"_L1) {
v = SQL_FALSE;
} else {
- qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
+ qSqlWarning(("QODBCDriver::open: Unknown option value '%1'"_L1)
+ .arg(val), this);
continue;
}
r = SQLSetConnectAttr(hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) size_t(v), 0);
- } else if (opt.toUpper() == QLatin1String("SQL_ATTR_PACKET_SIZE")) {
+ } else if (opt.toUpper() == "SQL_ATTR_PACKET_SIZE"_L1) {
v = val.toUInt();
r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) size_t(v), 0);
- } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACEFILE")) {
- val.utf16(); // 0 terminate
- r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE,
- toSQLTCHAR(val).data(),
- SQLINTEGER(val.length() * sizeof(SQLTCHAR)));
- } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) {
- if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) {
+ } else if (opt.toUpper() == "SQL_ATTR_TRACEFILE"_L1) {
+ r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, val);
+ } else if (opt.toUpper() == "SQL_ATTR_TRACE"_L1) {
+ if (val.toUpper() == "SQL_OPT_TRACE_OFF"_L1) {
v = SQL_OPT_TRACE_OFF;
- } else if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_ON")) {
+ } else if (val.toUpper() == "SQL_OPT_TRACE_ON"_L1) {
v = SQL_OPT_TRACE_ON;
} else {
- qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
+ qSqlWarning(("QODBCDriver::open: Unknown option value '%1'"_L1)
+ .arg(val), this);
continue;
}
r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACE, (SQLPOINTER) size_t(v), 0);
- } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_POOLING")) {
- if (val == QLatin1String("SQL_CP_OFF"))
+ } else if (opt.toUpper() == "SQL_ATTR_CONNECTION_POOLING"_L1) {
+ if (val == "SQL_CP_OFF"_L1)
v = SQL_CP_OFF;
- else if (val.toUpper() == QLatin1String("SQL_CP_ONE_PER_DRIVER"))
+ else if (val.toUpper() == "SQL_CP_ONE_PER_DRIVER"_L1)
v = SQL_CP_ONE_PER_DRIVER;
- else if (val.toUpper() == QLatin1String("SQL_CP_ONE_PER_HENV"))
+ else if (val.toUpper() == "SQL_CP_ONE_PER_HENV"_L1)
v = SQL_CP_ONE_PER_HENV;
- else if (val.toUpper() == QLatin1String("SQL_CP_DEFAULT"))
+ else if (val.toUpper() == "SQL_CP_DEFAULT"_L1)
v = SQL_CP_DEFAULT;
else {
- qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
+ qSqlWarning(("QODBCDriver::open: Unknown option value '%1'"_L1)
+ .arg(val), this);
continue;
}
r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER) size_t(v), 0);
- } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CP_MATCH")) {
- if (val.toUpper() == QLatin1String("SQL_CP_STRICT_MATCH"))
+ } else if (opt.toUpper() == "SQL_ATTR_CP_MATCH"_L1) {
+ if (val.toUpper() == "SQL_CP_STRICT_MATCH"_L1)
v = SQL_CP_STRICT_MATCH;
- else if (val.toUpper() == QLatin1String("SQL_CP_RELAXED_MATCH"))
+ else if (val.toUpper() == "SQL_CP_RELAXED_MATCH"_L1)
v = SQL_CP_RELAXED_MATCH;
- else if (val.toUpper() == QLatin1String("SQL_CP_MATCH_DEFAULT"))
+ else if (val.toUpper() == "SQL_CP_MATCH_DEFAULT"_L1)
v = SQL_CP_MATCH_DEFAULT;
else {
- qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
+ qSqlWarning(("QODBCDriver::open: Unknown option value '%1'"_L1)
+ .arg(val), this);
continue;
}
r = SQLSetConnectAttr(hDbc, SQL_ATTR_CP_MATCH, (SQLPOINTER) size_t(v), 0);
- } else if (opt.toUpper() == QLatin1String("SQL_ATTR_ODBC_VERSION")) {
+ } else if (opt.toUpper() == "SQL_ATTR_ODBC_VERSION"_L1) {
// Already handled in QODBCDriver::open()
continue;
} else {
- qWarning() << "QODBCDriver::open: Unknown connection attribute '" << opt << '\'';
+ qSqlWarning(("QODBCDriver::open: Unknown connection attribute '%1'"_L1)
+ .arg(opt), this);
}
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
- qSqlWarning(QString::fromLatin1("QODBCDriver::open: Unable to set connection attribute'%1'").arg(
- opt), this);
+ if (!SQL_SUCCEEDED(r))
+ qSqlWarning(("QODBCDriver::open: Unable to set connection attribute '%1'"_L1)
+ .arg(opt), this);
}
return true;
}
-void QODBCDriverPrivate::splitTableQualifier(const QString & qualifier, QString &catalog,
- QString &schema, QString &table)
+void QODBCDriverPrivate::splitTableQualifier(const QString &qualifier, QString &catalog,
+ QString &schema, QString &table) const
{
+ Q_Q(const QODBCDriver);
+ const auto adjustName = [&](const QString &name) {
+ if (q->isIdentifierEscaped(name, QSqlDriver::TableName))
+ return q->stripDelimiters(name, QSqlDriver::TableName);
+ return adjustCase(name);
+ };
+ catalog.clear();
+ schema.clear();
+ table.clear();
if (!useSchema) {
- table = qualifier;
+ table = adjustName(qualifier);
return;
}
- QStringList l = qualifier.split(QLatin1Char('.'));
- if (l.count() > 3)
- return; // can't possibly be a valid table qualifier
- int i = 0, n = l.count();
- if (n == 1) {
- table = qualifier;
- } else {
- for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
- if (n == 3) {
- if (i == 0) {
- catalog = *it;
- } else if (i == 1) {
- schema = *it;
- } else if (i == 2) {
- table = *it;
- }
- } else if (n == 2) {
- if (i == 0) {
- schema = *it;
- } else if (i == 1) {
- table = *it;
- }
- }
- i++;
- }
+ const QList<QStringView> l = QStringView(qualifier).split(u'.');
+ switch (l.count()) {
+ case 1:
+ table = adjustName(qualifier);
+ break;
+ case 2:
+ schema = adjustName(l.at(0).toString());
+ table = adjustName(l.at(1).toString());
+ break;
+ case 3:
+ catalog = adjustName(l.at(0).toString());
+ schema = adjustName(l.at(1).toString());
+ table = adjustName(l.at(2).toString());
+ break;
+ default:
+ qSqlWarning(("QODBCDriver::splitTableQualifier: Unable to split table qualifier '%1'"_L1)
+ .arg(qualifier), this);
+ break;
}
}
-QODBCDriverPrivate::DefaultCase QODBCDriverPrivate::defaultCase() const
+void QODBCDriverPrivate::checkDefaultCase()
{
- DefaultCase ret;
+ m_defaultCase = DefaultCase::Mixed; //arbitrary case if driver cannot be queried
SQLUSMALLINT casing;
- int r = SQLGetInfo(hDbc,
- SQL_IDENTIFIER_CASE,
- &casing,
- sizeof(casing),
- NULL);
- if ( r != SQL_SUCCESS)
- ret = Mixed;//arbitrary case if driver cannot be queried
- else {
+ SQLRETURN r = SQLGetInfo(hDbc,
+ SQL_IDENTIFIER_CASE,
+ &casing,
+ sizeof(casing),
+ NULL);
+ if (r == SQL_SUCCESS) {
switch (casing) {
- case (SQL_IC_UPPER):
- ret = Upper;
- break;
- case (SQL_IC_LOWER):
- ret = Lower;
- break;
- case (SQL_IC_SENSITIVE):
- ret = Sensitive;
- break;
- case (SQL_IC_MIXED):
- default:
- ret = Mixed;
- break;
+ case SQL_IC_UPPER:
+ m_defaultCase = DefaultCase::Upper;
+ break;
+ case SQL_IC_LOWER:
+ m_defaultCase = DefaultCase::Lower;
+ break;
+ case SQL_IC_SENSITIVE:
+ m_defaultCase = DefaultCase::Sensitive;
+ break;
+ case SQL_IC_MIXED:
+ m_defaultCase = DefaultCase::Mixed;
+ break;
}
}
- return ret;
}
/*
@@ -945,20 +905,16 @@ QODBCDriverPrivate::DefaultCase QODBCDriverPrivate::defaultCase() const
*/
QString QODBCDriverPrivate::adjustCase(const QString &identifier) const
{
- QString ret = identifier;
- switch(defaultCase()) {
- case (Lower):
- ret = identifier.toLower();
- break;
- case (Upper):
- ret = identifier.toUpper();
- break;
- case(Mixed):
- case(Sensitive):
- default:
- ret = identifier;
+ switch (m_defaultCase) {
+ case DefaultCase::Lower:
+ return identifier.toLower();
+ case DefaultCase::Upper:
+ return identifier.toUpper();
+ case DefaultCase::Mixed:
+ case DefaultCase::Sensitive:
+ break;
}
- return ret;
+ return identifier;
}
////////////////////////////////////////////////////////////////////////////
@@ -974,8 +930,7 @@ QODBCResult::~QODBCResult()
if (d->hStmt && d->isStmtHandleValid() && driver() && driver()->isOpen()) {
SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ")
- + QString::number(r), d);
+ qSqlWarning(("QODBCResult: Unable to free statement handle "_L1), d);
}
}
@@ -994,7 +949,7 @@ bool QODBCResult::reset (const QString& query)
if (d->hStmt && d->isStmtHandleValid()) {
r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QODBCResult::reset: Unable to free statement handle"), d);
+ qSqlWarning("QODBCResult::reset: Unable to free statement handle"_L1, d);
return false;
}
}
@@ -1002,7 +957,7 @@ bool QODBCResult::reset (const QString& query)
d->dpDbc(),
&d->hStmt);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QODBCResult::reset: Unable to allocate statement handle"), d);
+ qSqlWarning("QODBCResult::reset: Unable to allocate statement handle"_L1, d);
return false;
}
@@ -1019,17 +974,20 @@ bool QODBCResult::reset (const QString& query)
(SQLPOINTER)SQL_CURSOR_STATIC,
SQL_IS_UINTEGER);
}
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+ if (!SQL_SUCCEEDED(r)) {
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
"QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. "
"Please check your ODBC driver configuration"), QSqlError::StatementError, d));
return false;
}
- r = SQLExecDirect(d->hStmt,
- toSQLTCHAR(query).data(),
- (SQLINTEGER) query.length());
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) {
+ {
+ auto encoded = toSQLTCHAR(query);
+ r = SQLExecDirect(d->hStmt,
+ encoded.data(),
+ SQLINTEGER(encoded.size()));
+ }
+ if (!SQL_SUCCEEDED(r) && r!= SQL_NO_DATA) {
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
"Unable to execute statement"), QSqlError::StatementError, d));
return false;
@@ -1037,14 +995,14 @@ bool QODBCResult::reset (const QString& query)
SQLULEN isScrollable = 0;
r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, 0);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+ if (SQL_SUCCEEDED(r))
setForwardOnly(isScrollable == SQL_NONSCROLLABLE);
SQLSMALLINT count = 0;
SQLNumResultCols(d->hStmt, &count);
if (count) {
setSelect(true);
- for (int i = 0; i < count; ++i) {
+ for (SQLSMALLINT i = 0; i < count; ++i) {
d->rInf.append(qMakeFieldInfo(d, i));
}
d->fieldCache.resize(count);
@@ -1106,7 +1064,7 @@ bool QODBCResult::fetchNext()
else
r = SQLFetch(d->hStmt);
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+ if (!SQL_SUCCEEDED(r)) {
if (r != SQL_NO_DATA)
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
"Unable to fetch next"), QSqlError::ConnectionError, d));
@@ -1203,7 +1161,8 @@ QVariant QODBCResult::data(int field)
{
Q_D(QODBCResult);
if (field >= d->rInf.count() || field < 0) {
- qWarning() << "QODBCResult::data: column" << field << "out of range";
+ qSqlWarning(("QODBCResult::data: column %1 out of range"_L1)
+ .arg(QString::number(field)), d);
return QVariant();
}
if (field < d->fieldCacheIdx)
@@ -1239,7 +1198,7 @@ QVariant QODBCResult::data(int field)
(SQLPOINTER)&dbuf,
0,
&lengthIndicator);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
+ if (SQL_SUCCEEDED(r) && (lengthIndicator != SQL_NULL_DATA))
d->fieldCache[i] = QVariant(QDate(dbuf.year, dbuf.month, dbuf.day));
else
d->fieldCache[i] = QVariant(QMetaType::fromType<QDate>());
@@ -1252,7 +1211,7 @@ QVariant QODBCResult::data(int field)
(SQLPOINTER)&tbuf,
0,
&lengthIndicator);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
+ if (SQL_SUCCEEDED(r) && (lengthIndicator != SQL_NULL_DATA))
d->fieldCache[i] = QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second));
else
d->fieldCache[i] = QVariant(QMetaType::fromType<QTime>());
@@ -1265,7 +1224,7 @@ QVariant QODBCResult::data(int field)
(SQLPOINTER)&dtbuf,
0,
&lengthIndicator);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
+ if (SQL_SUCCEEDED(r) && (lengthIndicator != SQL_NULL_DATA))
d->fieldCache[i] = QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day),
QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000)));
else
@@ -1295,7 +1254,7 @@ QVariant QODBCResult::data(int field)
}
break;
default:
- d->fieldCache[i] = QVariant(qGetStringData(d->hStmt, i, info.length(), false));
+ d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), false);
break;
}
d->fieldCacheIdx = field + 1;
@@ -1308,7 +1267,7 @@ bool QODBCResult::isNull(int field)
Q_D(const QODBCResult);
if (field < 0 || field >= d->fieldCache.size())
return true;
- if (field <= d->fieldCacheIdx) {
+ if (field >= d->fieldCacheIdx) {
// since there is no good way to find out whether the value is NULL
// without fetching the field we'll fetch it here.
// (data() also sets the NULL flag)
@@ -1329,8 +1288,7 @@ int QODBCResult::numRowsAffected()
SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount);
if (r == SQL_SUCCESS)
return affectedRowCount;
- else
- qSqlWarning(QLatin1String("QODBCResult::numRowsAffected: Unable to count affected rows"), d);
+ qSqlWarning("QODBCResult::numRowsAffected: Unable to count affected rows"_L1, d);
return -1;
}
@@ -1345,7 +1303,7 @@ bool QODBCResult::prepare(const QString& query)
if (d->hStmt && d->isStmtHandleValid()) {
r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QODBCResult::prepare: Unable to close statement"), d);
+ qSqlWarning("QODBCResult::prepare: Unable to close statement"_L1, d);
return false;
}
}
@@ -1353,7 +1311,7 @@ bool QODBCResult::prepare(const QString& query)
d->dpDbc(),
&d->hStmt);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QODBCResult::prepare: Unable to allocate statement handle"), d);
+ qSqlWarning("QODBCResult::prepare: Unable to allocate statement handle"_L1, d);
return false;
}
@@ -1370,16 +1328,19 @@ bool QODBCResult::prepare(const QString& query)
(SQLPOINTER)SQL_CURSOR_STATIC,
SQL_IS_UINTEGER);
}
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+ if (!SQL_SUCCEEDED(r)) {
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
"QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. "
"Please check your ODBC driver configuration"), QSqlError::StatementError, d));
return false;
}
- r = SQLPrepare(d->hStmt,
- toSQLTCHAR(query).data(),
- (SQLINTEGER) query.length());
+ {
+ auto encoded = toSQLTCHAR(query);
+ r = SQLPrepare(d->hStmt,
+ encoded.data(),
+ SQLINTEGER(encoded.size()));
+ }
if (r != SQL_SUCCESS) {
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
@@ -1399,7 +1360,7 @@ bool QODBCResult::exec()
d->fieldCacheIdx = 0;
if (!d->hStmt) {
- qSqlWarning(QLatin1String("QODBCResult::exec: No statement handle available"), d);
+ qSqlWarning("QODBCResult::exec: No statement handle available"_L1, d);
return false;
}
@@ -1407,19 +1368,17 @@ bool QODBCResult::exec()
SQLCloseCursor(d->hStmt);
QVariantList &values = boundValues();
- QByteArrayList tmpStorage(values.count(), QByteArray()); // holds temporary buffers
- QVarLengthArray<SQLLEN, 32> indicators(values.count());
- memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN));
+ QByteArrayList tmpStorage(values.count(), QByteArray()); // targets for SQLBindParameter()
+ QVarLengthArray<SQLLEN, 32> indicators(values.count(), 0);
// bind parameters - only positional binding allowed
- int i;
SQLRETURN r;
- for (i = 0; i < values.count(); ++i) {
+ for (qsizetype i = 0; i < values.count(); ++i) {
if (bindValueType(i) & QSql::Out)
values[i].detach();
const QVariant &val = values.at(i);
SQLLEN *ind = &indicators[i];
- if (val.isNull())
+ if (QSqlResultPrivate::isVariantNull(val))
*ind = SQL_NULL_DATA;
switch (val.typeId()) {
case QMetaType::QDate: {
@@ -1622,36 +1581,36 @@ bool QODBCResult::exec()
case QMetaType::QString:
if (d->unicode) {
QByteArray &ba = tmpStorage[i];
- QString str = val.toString();
+ {
+ const auto encoded = toSQLTCHAR(val.toString());
+ ba = QByteArray(reinterpret_cast<const char *>(encoded.data()),
+ encoded.size() * sizeof(SQLTCHAR));
+ }
+
if (*ind != SQL_NULL_DATA)
- *ind = str.length() * sizeof(SQLTCHAR);
- const qsizetype strSize = str.length() * sizeof(SQLTCHAR);
+ *ind = ba.size();
if (bindValueType(i) & QSql::Out) {
- const QVarLengthArray<SQLTCHAR> a(toSQLTCHAR(str));
- ba = QByteArray((const char *)a.constData(), int(a.size() * sizeof(SQLTCHAR)));
r = SQLBindParameter(d->hStmt,
i + 1,
qParamType[bindValueType(i) & QSql::InOut],
SQL_C_TCHAR,
- strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
+ ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
0, // god knows... don't change this!
0,
- ba.data(),
+ const_cast<char *>(ba.constData()), // don't detach
ba.size(),
ind);
break;
}
- ba = QByteArray(reinterpret_cast<const char *>(toSQLTCHAR(str).constData()),
- int(strSize));
r = SQLBindParameter(d->hStmt,
i + 1,
qParamType[bindValueType(i) & QSql::InOut],
SQL_C_TCHAR,
- strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
- strSize,
+ ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
+ ba.size(),
0,
- const_cast<char *>(ba.constData()),
+ const_cast<char *>(ba.constData()), // don't detach
ba.size(),
ind);
break;
@@ -1676,7 +1635,7 @@ bool QODBCResult::exec()
ind);
break;
}
- // fall through
+ Q_FALLTHROUGH();
default: {
QByteArray &ba = tmpStorage[i];
if (*ind != SQL_NULL_DATA)
@@ -1694,15 +1653,15 @@ bool QODBCResult::exec()
break; }
}
if (r != SQL_SUCCESS) {
- qWarning() << "QODBCResult::exec: unable to bind variable:" << qODBCWarn(d);
+ qSqlWarning("QODBCResult::exec: unable to bind variable:"_L1, d);
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
"Unable to bind variable"), QSqlError::StatementError, d));
return false;
}
}
r = SQLExecute(d->hStmt);
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
- qWarning() << "QODBCResult::exec: Unable to execute statement:" << qODBCWarn(d);
+ if (!SQL_SUCCEEDED(r) && r != SQL_NO_DATA) {
+ qSqlWarning("QODBCResult::exec: Unable to execute statement:"_L1, d);
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
"Unable to execute statement"), QSqlError::StatementError, d));
return false;
@@ -1710,14 +1669,14 @@ bool QODBCResult::exec()
SQLULEN isScrollable = 0;
r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, 0);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+ if (SQL_SUCCEEDED(r))
setForwardOnly(isScrollable == SQL_NONSCROLLABLE);
SQLSMALLINT count = 0;
SQLNumResultCols(d->hStmt, &count);
if (count) {
setSelect(true);
- for (int i = 0; i < count; ++i) {
+ for (SQLSMALLINT i = 0; i < count; ++i) {
d->rInf.append(qMakeFieldInfo(d, i));
}
d->fieldCache.resize(count);
@@ -1731,7 +1690,7 @@ bool QODBCResult::exec()
if (!hasOutValues())
return true;
- for (i = 0; i < values.count(); ++i) {
+ for (qsizetype i = 0; i < values.count(); ++i) {
switch (values.at(i).typeId()) {
case QMetaType::QDate: {
DATE_STRUCT ds = *((DATE_STRUCT *)const_cast<char *>(tmpStorage.at(i).constData()));
@@ -1762,14 +1721,15 @@ bool QODBCResult::exec()
case QMetaType::QString:
if (d->unicode) {
if (bindValueType(i) & QSql::Out) {
- const QByteArray &first = tmpStorage.at(i);
- QVarLengthArray<SQLTCHAR> array;
- array.append((const SQLTCHAR *)first.constData(), first.size());
- values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR));
+ const QByteArray &bytes = tmpStorage.at(i);
+ const auto strSize = bytes.size() / sizeof(SQLTCHAR);
+ QVarLengthArray<SQLTCHAR> string(strSize);
+ memcpy(string.data(), bytes.data(), strSize * sizeof(SQLTCHAR));
+ values[i] = fromSQLTCHAR(string);
}
break;
}
- // fall through
+ Q_FALLTHROUGH();
default: {
if (bindValueType(i) & QSql::Out)
values[i] = tmpStorage.at(i);
@@ -1797,13 +1757,13 @@ QVariant QODBCResult::lastInsertId() const
switch (driver()->dbmsType()) {
case QSqlDriver::MSSqlServer:
case QSqlDriver::Sybase:
- sql = QLatin1String("SELECT @@IDENTITY;");
+ sql = "SELECT @@IDENTITY;"_L1;
break;
case QSqlDriver::MySqlServer:
- sql = QLatin1String("SELECT LAST_INSERT_ID();");
+ sql = "SELECT LAST_INSERT_ID();"_L1;
break;
case QSqlDriver::PostgreSQL:
- sql = QLatin1String("SELECT lastval();");
+ sql = "SELECT lastval();"_L1;
break;
default:
break;
@@ -1814,9 +1774,9 @@ QVariant QODBCResult::lastInsertId() const
if (qry.exec(sql) && qry.next())
return qry.value(0);
- qSqlWarning(QLatin1String("QODBCResult::lastInsertId: Unable to get lastInsertId"), d);
+ qSqlWarning("QODBCResult::lastInsertId: Unable to get lastInsertId"_L1, d);
} else {
- qSqlWarning(QLatin1String("QODBCResult::lastInsertId: not implemented for this DBMS"), d);
+ qSqlWarning("QODBCResult::lastInsertId: not implemented for this DBMS"_L1, d);
}
return QVariant();
@@ -1841,9 +1801,7 @@ bool QODBCResult::nextResult()
SQLRETURN r = SQLMoreResults(d->hStmt);
if (r != SQL_SUCCESS) {
if (r == SQL_SUCCESS_WITH_INFO) {
- int nativeCode = -1;
- QString message = qODBCWarn(d, &nativeCode);
- qWarning() << "QODBCResult::nextResult():" << message;
+ qSqlWarning("QODBCResult::nextResult:"_L1, d);
} else {
if (r != SQL_NO_DATA)
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
@@ -1856,7 +1814,7 @@ bool QODBCResult::nextResult()
SQLNumResultCols(d->hStmt, &count);
if (count) {
setSelect(true);
- for (int i = 0; i < count; ++i) {
+ for (SQLSMALLINT i = 0; i < count; ++i) {
d->rInf.append(qMakeFieldInfo(d, i));
}
d->fieldCache.resize(count);
@@ -1962,6 +1920,18 @@ bool QODBCDriver::open(const QString & db,
int,
const QString& connOpts)
{
+ const auto ensureEscaped = [](QString arg) -> QString {
+ QChar quoteChar;
+ if (arg.startsWith(u'"'))
+ quoteChar = u'\'';
+ else if (arg.startsWith(u'\''))
+ quoteChar = u'"';
+ else if (arg.contains(u';'))
+ quoteChar = u'"';
+ else
+ return arg;
+ return quoteChar + arg + quoteChar;
+ };
Q_D(QODBCDriver);
if (isOpen())
close();
@@ -1969,8 +1939,8 @@ bool QODBCDriver::open(const QString & db,
r = SQLAllocHandle(SQL_HANDLE_ENV,
SQL_NULL_HANDLE,
&d->hEnv);
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
- qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate environment"), d);
+ if (!SQL_SUCCEEDED(r)) {
+ qSqlWarning("QODBCDriver::open: Unable to allocate environment"_L1, d);
setOpenError(true);
return false;
}
@@ -1981,8 +1951,8 @@ bool QODBCDriver::open(const QString & db,
r = SQLAllocHandle(SQL_HANDLE_DBC,
d->hEnv,
&d->hDbc);
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
- qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate connection"), d);
+ if (!SQL_SUCCEEDED(r)) {
+ qSqlWarning("QODBCDriver::open: Unable to allocate connection"_L1, d);
setOpenError(true);
cleanup();
return false;
@@ -1996,32 +1966,32 @@ bool QODBCDriver::open(const QString & db,
// Create the connection string
QString connQStr;
// support the "DRIVER={SQL SERVER};SERVER=blah" syntax
- if (db.contains(QLatin1String(".dsn"), Qt::CaseInsensitive))
- connQStr = QLatin1String("FILEDSN=") + db;
- else if (db.contains(QLatin1String("DRIVER="), Qt::CaseInsensitive)
- || db.contains(QLatin1String("SERVER="), Qt::CaseInsensitive))
- connQStr = db;
+ if (db.contains(".dsn"_L1, Qt::CaseInsensitive))
+ connQStr = "FILEDSN="_L1 + ensureEscaped(db);
+ else if (db.contains("DRIVER="_L1, Qt::CaseInsensitive)
+ || db.contains("SERVER="_L1, Qt::CaseInsensitive))
+ connQStr = ensureEscaped(db);
else
- connQStr = QLatin1String("DSN=") + db;
+ connQStr = "DSN="_L1 + ensureEscaped(db);
if (!user.isEmpty())
- connQStr += QLatin1String(";UID=") + user;
+ connQStr += ";UID="_L1 + ensureEscaped(user);
if (!password.isEmpty())
- connQStr += QLatin1String(";PWD=") + password;
+ connQStr += ";PWD="_L1 + ensureEscaped(password);
SQLSMALLINT cb;
- QVarLengthArray<SQLTCHAR> connOut(1024);
- memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
- r = SQLDriverConnect(d->hDbc,
- NULL,
- toSQLTCHAR(connQStr).data(),
- (SQLSMALLINT)connQStr.length(),
- connOut.data(),
- 1024,
- &cb,
- /*SQL_DRIVER_NOPROMPT*/0);
-
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+ QVarLengthArray<SQLTCHAR, 1024> connOut(1024);
+ {
+ auto encoded = toSQLTCHAR(connQStr);
+ r = SQLDriverConnect(d->hDbc,
+ nullptr,
+ encoded.data(), SQLSMALLINT(encoded.size()),
+ connOut.data(), SQLSMALLINT(connOut.size()),
+ &cb,
+ /*SQL_DRIVER_NOPROMPT*/0);
+ }
+
+ if (!SQL_SUCCEEDED(r)) {
setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
setOpenError(true);
cleanup();
@@ -2042,11 +2012,12 @@ bool QODBCDriver::open(const QString & db,
d->checkHasSQLFetchScroll();
d->checkHasMultiResults();
d->checkDateTimePrecision();
+ d->checkDefaultCase();
setOpen(true);
setOpenError(false);
if (d->dbmsType == MSSqlServer) {
QSqlQuery i(createResult());
- i.exec(QLatin1String("SET QUOTED_IDENTIFIER ON"));
+ i.exec("SET QUOTED_IDENTIFIER ON"_L1);
}
return true;
}
@@ -2068,21 +2039,21 @@ void QODBCDriver::cleanup()
if (isOpen()) {
r = SQLDisconnect(d->hDbc);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QODBCDriver::disconnect: Unable to disconnect datasource"), d);
+ qSqlWarning("QODBCDriver::disconnect: Unable to disconnect datasource"_L1, d);
else
d->disconnectCount++;
}
r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free connection handle"), d);
+ qSqlWarning("QODBCDriver::cleanup: Unable to free connection handle"_L1, d);
d->hDbc = 0;
}
if (d->hEnv) {
r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free environment handle"), d);
+ qSqlWarning("QODBCDriver::cleanup: Unable to free environment handle"_L1, d);
d->hEnv = 0;
}
}
@@ -2100,7 +2071,7 @@ void QODBCDriverPrivate::checkUnicode()
(SQLPOINTER)&fFunc,
sizeof(fFunc),
NULL);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WCHAR)) {
+ if (SQL_SUCCEEDED(r) && (fFunc & SQL_CVT_WCHAR)) {
unicode = true;
return;
}
@@ -2110,7 +2081,7 @@ void QODBCDriverPrivate::checkUnicode()
(SQLPOINTER)&fFunc,
sizeof(fFunc),
NULL);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WVARCHAR)) {
+ if (SQL_SUCCEEDED(r) && (fFunc & SQL_CVT_WVARCHAR)) {
unicode = true;
return;
}
@@ -2120,74 +2091,86 @@ void QODBCDriverPrivate::checkUnicode()
(SQLPOINTER)&fFunc,
sizeof(fFunc),
NULL);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WLONGVARCHAR)) {
+ if (SQL_SUCCEEDED(r) && (fFunc & SQL_CVT_WLONGVARCHAR)) {
unicode = true;
return;
}
- SQLHANDLE hStmt;
- r = SQLAllocHandle(SQL_HANDLE_STMT,
- hDbc,
- &hStmt);
- r = SQLExecDirect(hStmt, toSQLTCHAR(QLatin1String("select 'test'")).data(), SQL_NTS);
+ SqlStmtHandle hStmt(hDbc);
+ // for databases which do not return something useful in SQLGetInfo and are picky about a
+ // 'SELECT' statement without 'FROM' but support VALUE(foo) statement like e.g. DB2 or Oracle
+ const auto statements = {
+ "select 'test'"_L1,
+ "values('test')"_L1,
+ "select 'test' from dual"_L1,
+ };
+ for (const auto &statement : statements) {
+ auto encoded = toSQLTCHAR(statement);
+ r = SQLExecDirect(hStmt.handle(), encoded.data(), SQLINTEGER(encoded.size()));
+ if (r == SQL_SUCCESS)
+ break;
+ }
if (r == SQL_SUCCESS) {
- r = SQLFetch(hStmt);
+ r = SQLFetch(hStmt.handle());
if (r == SQL_SUCCESS) {
- QVarLengthArray<SQLWCHAR> buffer(10);
- r = SQLGetData(hStmt, 1, SQL_C_WCHAR, buffer.data(), buffer.size() * sizeof(SQLWCHAR), NULL);
- if (r == SQL_SUCCESS && fromSQLTCHAR(buffer) == QLatin1String("test")) {
+ QVarLengthArray<SQLWCHAR, 10> buffer(10);
+ r = SQLGetData(hStmt.handle(), 1, SQL_C_WCHAR, buffer.data(),
+ buffer.size() * sizeof(SQLWCHAR), NULL);
+ if (r == SQL_SUCCESS && fromSQLTCHAR(buffer) == "test"_L1) {
unicode = true;
}
}
}
- r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
bool QODBCDriverPrivate::checkDriver() const
{
#ifdef ODBC_CHECK_DRIVER
- static const SQLUSMALLINT reqFunc[] = {
+ static constexpr SQLUSMALLINT reqFunc[] = {
SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
- SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0
+ SQL_API_SQLGETINFO, SQL_API_SQLTABLES
};
// these functions are optional
- static const SQLUSMALLINT optFunc[] = {
- SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0
+ static constexpr SQLUSMALLINT optFunc[] = {
+ SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT
};
SQLRETURN r;
SQLUSMALLINT sup;
- int i;
// check the required functions
- for (i = 0; reqFunc[i] != 0; ++i) {
+ for (const SQLUSMALLINT func : reqFunc) {
- r = SQLGetFunctions(hDbc, reqFunc[i], &sup);
+ r = SQLGetFunctions(hDbc, func, &sup);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
+ qSqlWarning("QODBCDriver::checkDriver: Cannot get list of supported functions"_L1, this);
return false;
}
if (sup == SQL_FALSE) {
- qWarning () << "QODBCDriver::open: Warning - Driver doesn't support all needed functionality (" << reqFunc[i] <<
- ").\nPlease look at the Qt SQL Module Driver documentation for more information.";
+ qSqlWarning(("QODBCDriver::checkDriver: Driver doesn't support all needed "
+ "functionality (func id %1).\nPlease look at the Qt SQL Module "
+ "Driver documentation for more information."_L1)
+ .arg(QString::number(func)), this);
return false;
}
}
// these functions are optional and just generate a warning
- for (i = 0; optFunc[i] != 0; ++i) {
+ for (const SQLUSMALLINT func : optFunc) {
- r = SQLGetFunctions(hDbc, optFunc[i], &sup);
+ r = SQLGetFunctions(hDbc, func, &sup);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
+ qSqlWarning("QODBCDriver::checkDriver: Cannot get list of supported functions"_L1, this);
return false;
}
if (sup == SQL_FALSE) {
- qWarning() << "QODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (" << optFunc[i] << ')';
+ qSqlWarning(("QODBCDriver::checkDriver: Driver doesn't support some "
+ "non-critical functions (func id %1)."_L1)
+ .arg(QString::number(func)), this);
return true;
}
}
@@ -2206,33 +2189,32 @@ void QODBCDriverPrivate::checkSchemaUsage()
(SQLPOINTER) &val,
sizeof(val),
NULL);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+ if (SQL_SUCCEEDED(r))
useSchema = (val != 0);
}
void QODBCDriverPrivate::checkDBMS()
{
SQLRETURN r;
- QVarLengthArray<SQLTCHAR> serverString(200);
+ QVarLengthArray<SQLTCHAR, 200> serverString(200);
SQLSMALLINT t;
- memset(serverString.data(), 0, serverString.size() * sizeof(SQLTCHAR));
r = SQLGetInfo(hDbc,
SQL_DBMS_NAME,
serverString.data(),
SQLSMALLINT(serverString.size() * sizeof(SQLTCHAR)),
&t);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+ if (SQL_SUCCEEDED(r)) {
const QString serverType = fromSQLTCHAR(serverString, t / sizeof(SQLTCHAR));
- if (serverType.contains(QLatin1String("PostgreSQL"), Qt::CaseInsensitive))
+ if (serverType.contains("PostgreSQL"_L1, Qt::CaseInsensitive))
dbmsType = QSqlDriver::PostgreSQL;
- else if (serverType.contains(QLatin1String("Oracle"), Qt::CaseInsensitive))
+ else if (serverType.contains("Oracle"_L1, Qt::CaseInsensitive))
dbmsType = QSqlDriver::Oracle;
- else if (serverType.contains(QLatin1String("MySql"), Qt::CaseInsensitive))
+ else if (serverType.contains("MySql"_L1, Qt::CaseInsensitive))
dbmsType = QSqlDriver::MySqlServer;
- else if (serverType.contains(QLatin1String("Microsoft SQL Server"), Qt::CaseInsensitive))
+ else if (serverType.contains("Microsoft SQL Server"_L1, Qt::CaseInsensitive))
dbmsType = QSqlDriver::MSSqlServer;
- else if (serverType.contains(QLatin1String("Sybase"), Qt::CaseInsensitive))
+ else if (serverType.contains("Sybase"_L1, Qt::CaseInsensitive))
dbmsType = QSqlDriver::Sybase;
}
r = SQLGetInfo(hDbc,
@@ -2240,9 +2222,9 @@ void QODBCDriverPrivate::checkDBMS()
serverString.data(),
SQLSMALLINT(serverString.size() * sizeof(SQLTCHAR)),
&t);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+ if (SQL_SUCCEEDED(r)) {
const QString serverType = fromSQLTCHAR(serverString, t / sizeof(SQLTCHAR));
- isFreeTDSDriver = serverType.contains(QLatin1String("tdsodbc"), Qt::CaseInsensitive);
+ isFreeTDSDriver = serverType.contains("tdsodbc"_L1, Qt::CaseInsensitive);
unicode = unicode && !isFreeTDSDriver;
}
}
@@ -2251,46 +2233,42 @@ void QODBCDriverPrivate::checkHasSQLFetchScroll()
{
SQLUSMALLINT sup;
SQLRETURN r = SQLGetFunctions(hDbc, SQL_API_SQLFETCHSCROLL, &sup);
- if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || sup != SQL_TRUE) {
+ if ((!SQL_SUCCEEDED(r)) || sup != SQL_TRUE) {
hasSQLFetchScroll = false;
- qWarning("QODBCDriver::checkHasSQLFetchScroll: Warning - Driver doesn't support scrollable result sets, use forward only mode for queries");
+ qSqlWarning("QODBCDriver::checkHasSQLFetchScroll: Driver doesn't support "
+ "scrollable result sets, use forward only mode for queries"_L1, this);
}
}
void QODBCDriverPrivate::checkHasMultiResults()
{
- QVarLengthArray<SQLTCHAR> driverResponse(2);
+ QVarLengthArray<SQLTCHAR, 2> driverResponse(2);
SQLSMALLINT length;
SQLRETURN r = SQLGetInfo(hDbc,
SQL_MULT_RESULT_SETS,
driverResponse.data(),
SQLSMALLINT(driverResponse.size() * sizeof(SQLTCHAR)),
&length);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
- hasMultiResultSets = fromSQLTCHAR(driverResponse, length/sizeof(SQLTCHAR)).startsWith(QLatin1Char('Y'));
+ if (SQL_SUCCEEDED(r))
+ hasMultiResultSets = fromSQLTCHAR(driverResponse, length / sizeof(SQLTCHAR)).startsWith(u'Y');
}
void QODBCDriverPrivate::checkDateTimePrecision()
{
SQLINTEGER columnSize;
- SQLHANDLE hStmt;
+ SqlStmtHandle hStmt(hDbc);
- SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
- if (r != SQL_SUCCESS) {
+ if (!hStmt.isValid())
return;
- }
- r = SQLGetTypeInfo(hStmt, SQL_TIMESTAMP);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
- r = SQLFetch(hStmt);
- if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO )
- {
- if (SQLGetData(hStmt, 3, SQL_INTEGER, &columnSize, sizeof(columnSize), 0) == SQL_SUCCESS) {
+ SQLRETURN r = SQLGetTypeInfo(hStmt.handle(), SQL_TIMESTAMP);
+ if (SQL_SUCCEEDED(r)) {
+ r = SQLFetch(hStmt.handle());
+ if (SQL_SUCCEEDED(r)) {
+ if (SQLGetData(hStmt.handle(), 3, SQL_INTEGER, &columnSize, sizeof(columnSize), 0) == SQL_SUCCESS)
datetimePrecision = (int)columnSize;
- }
}
}
- SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
QSqlResult *QODBCDriver::createResult() const
@@ -2302,7 +2280,7 @@ bool QODBCDriver::beginTransaction()
{
Q_D(QODBCDriver);
if (!isOpen()) {
- qWarning("QODBCDriver::beginTransaction: Database not open");
+ qSqlWarning("QODBCDriver::beginTransaction: Database not open"_L1, d);
return false;
}
SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
@@ -2322,7 +2300,7 @@ bool QODBCDriver::commitTransaction()
{
Q_D(QODBCDriver);
if (!isOpen()) {
- qWarning("QODBCDriver::commitTransaction: Database not open");
+ qSqlWarning("QODBCDriver::commitTransaction: Database not open"_L1, d);
return false;
}
SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
@@ -2340,7 +2318,7 @@ bool QODBCDriver::rollbackTransaction()
{
Q_D(QODBCDriver);
if (!isOpen()) {
- qWarning("QODBCDriver::rollbackTransaction: Database not open");
+ qSqlWarning("QODBCDriver::rollbackTransaction: Database not open"_L1, d);
return false;
}
SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
@@ -2375,71 +2353,52 @@ QStringList QODBCDriver::tables(QSql::TableType type) const
QStringList tl;
if (!isOpen())
return tl;
- SQLHANDLE hStmt;
- SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
- d->hDbc,
- &hStmt);
- if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QODBCDriver::tables: Unable to allocate handle"), d);
+ SqlStmtHandle hStmt(d->hDbc);
+ if (!hStmt.isValid()) {
+ qSqlWarning("QODBCDriver::tables: Unable to allocate handle"_L1, d);
return tl;
}
- r = SQLSetStmtAttr(hStmt,
- SQL_ATTR_CURSOR_TYPE,
- (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
- SQL_IS_UINTEGER);
+ SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER);
QStringList tableType;
if (type & QSql::Tables)
- tableType += QLatin1String("TABLE");
+ tableType += "TABLE"_L1;
if (type & QSql::Views)
- tableType += QLatin1String("VIEW");
+ tableType += "VIEW"_L1;
if (type & QSql::SystemTables)
- tableType += QLatin1String("SYSTEM TABLE");
+ tableType += "SYSTEM TABLE"_L1;
if (tableType.isEmpty())
return tl;
- QString joinedTableTypeString = tableType.join(QLatin1Char(','));
+ {
+ auto joinedTableTypeString = toSQLTCHAR(tableType.join(u','));
- r = SQLTables(hStmt,
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- 0,
- toSQLTCHAR(joinedTableTypeString).data(),
- joinedTableTypeString.length() /* characters, not bytes */);
+ r = SQLTables(hStmt.handle(),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr, 0,
+ joinedTableTypeString.data(), joinedTableTypeString.size());
+ }
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d);
+ qSqlWarning("QODBCDriver::tables Unable to execute table list"_L1,
+ hStmt.handle());
- if (d->hasSQLFetchScroll)
- r = SQLFetchScroll(hStmt,
- SQL_FETCH_NEXT,
- 0);
- else
- r = SQLFetch(hStmt);
-
- if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
- qWarning() << "QODBCDriver::tables failed to retrieve table/view list: (" << r << "," << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ")";
+ r = d->sqlFetchNext(hStmt);
+ if (!SQL_SUCCEEDED(r) && r != SQL_NO_DATA) {
+ qSqlWarning("QODBCDriver::tables failed to retrieve table/view list"_L1,
+ hStmt.handle());
return QStringList();
}
while (r == SQL_SUCCESS) {
- QString fieldVal = qGetStringData(hStmt, 2, -1, d->unicode);
- tl.append(fieldVal);
-
- if (d->hasSQLFetchScroll)
- r = SQLFetchScroll(hStmt,
- SQL_FETCH_NEXT,
- 0);
- else
- r = SQLFetch(hStmt);
+ tl.append(qGetStringData(hStmt.handle(), 2, -1, d->unicode).toString());
+ r = d->sqlFetchNext(hStmt);
}
- r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
- if (r!= SQL_SUCCESS)
- qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
return tl;
}
@@ -2452,98 +2411,69 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
bool usingSpecialColumns = false;
QSqlRecord rec = record(tablename);
- SQLHANDLE hStmt;
- SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
- d->hDbc,
- &hStmt);
- if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to list primary key"), d);
+ SqlStmtHandle hStmt(d->hDbc);
+ if (!hStmt.isValid()) {
+ qSqlWarning("QODBCDriver::primaryIndex: Unable to allocate handle"_L1, d);
return index;
}
QString catalog, schema, table;
- const_cast<QODBCDriverPrivate*>(d)->splitTableQualifier(tablename, catalog, schema, table);
-
- if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
- catalog = stripDelimiters(catalog, QSqlDriver::TableName);
- else
- catalog = d->adjustCase(catalog);
+ d->splitTableQualifier(tablename, catalog, schema, table);
- if (isIdentifierEscaped(schema, QSqlDriver::TableName))
- schema = stripDelimiters(schema, QSqlDriver::TableName);
- else
- schema = d->adjustCase(schema);
-
- if (isIdentifierEscaped(table, QSqlDriver::TableName))
- table = stripDelimiters(table, QSqlDriver::TableName);
- else
- table = d->adjustCase(table);
-
- r = SQLSetStmtAttr(hStmt,
- SQL_ATTR_CURSOR_TYPE,
- (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
- SQL_IS_UINTEGER);
- r = SQLPrimaryKeys(hStmt,
- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
- catalog.length(),
- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
- schema.length(),
- toSQLTCHAR(table).data(),
- table.length() /* in characters, not in bytes */);
+ SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER);
+ {
+ auto c = toSQLTCHAR(catalog);
+ auto s = toSQLTCHAR(schema);
+ auto t = toSQLTCHAR(table);
+ r = SQLPrimaryKeys(hStmt.handle(),
+ catalog.isEmpty() ? nullptr : c.data(), c.size(),
+ schema.isEmpty() ? nullptr : s.data(), s.size(),
+ t.data(), t.size());
+ }
// if the SQLPrimaryKeys() call does not succeed (e.g the driver
// does not support it) - try an alternative method to get hold of
// the primary index (e.g MS Access and FoxPro)
if (r != SQL_SUCCESS) {
- r = SQLSpecialColumns(hStmt,
- SQL_BEST_ROWID,
- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
- catalog.length(),
- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
- schema.length(),
- toSQLTCHAR(table).data(),
- table.length(),
- SQL_SCOPE_CURROW,
- SQL_NULLABLE);
+ auto c = toSQLTCHAR(catalog);
+ auto s = toSQLTCHAR(schema);
+ auto t = toSQLTCHAR(table);
+ r = SQLSpecialColumns(hStmt.handle(),
+ SQL_BEST_ROWID,
+ catalog.isEmpty() ? nullptr : c.data(), c.size(),
+ schema.isEmpty() ? nullptr : s.data(), s.size(),
+ t.data(), t.size(),
+ SQL_SCOPE_CURROW,
+ SQL_NULLABLE);
if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d);
+ qSqlWarning("QODBCDriver::primaryIndex: Unable to execute primary key list"_L1,
+ hStmt.handle());
} else {
usingSpecialColumns = true;
}
}
- if (d->hasSQLFetchScroll)
- r = SQLFetchScroll(hStmt,
- SQL_FETCH_NEXT,
- 0);
- else
- r = SQLFetch(hStmt);
+ r = d->sqlFetchNext(hStmt);
int fakeId = 0;
QString cName, idxName;
// Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
while (r == SQL_SUCCESS) {
if (usingSpecialColumns) {
- cName = qGetStringData(hStmt, 1, -1, d->unicode); // column name
+ cName = qGetStringData(hStmt.handle(), 1, -1, d->unicode).toString(); // column name
idxName = QString::number(fakeId++); // invent a fake index name
} else {
- cName = qGetStringData(hStmt, 3, -1, d->unicode); // column name
- idxName = qGetStringData(hStmt, 5, -1, d->unicode); // pk index name
+ cName = qGetStringData(hStmt.handle(), 3, -1, d->unicode).toString(); // column name
+ idxName = qGetStringData(hStmt.handle(), 5, -1, d->unicode).toString(); // pk index name
}
index.append(rec.field(cName));
index.setName(idxName);
- if (d->hasSQLFetchScroll)
- r = SQLFetchScroll(hStmt,
- SQL_FETCH_NEXT,
- 0);
- else
- r = SQLFetch(hStmt);
-
+ r = d->sqlFetchNext(hStmt);
}
- r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
- if (r!= SQL_SUCCESS)
- qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
return index;
}
@@ -2554,72 +2484,39 @@ QSqlRecord QODBCDriver::record(const QString& tablename) const
if (!isOpen())
return fil;
- SQLHANDLE hStmt;
- QString catalog, schema, table;
- const_cast<QODBCDriverPrivate*>(d)->splitTableQualifier(tablename, catalog, schema, table);
-
- if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
- catalog = stripDelimiters(catalog, QSqlDriver::TableName);
- else
- catalog = d->adjustCase(catalog);
-
- if (isIdentifierEscaped(schema, QSqlDriver::TableName))
- schema = stripDelimiters(schema, QSqlDriver::TableName);
- else
- schema = d->adjustCase(schema);
+ SqlStmtHandle hStmt;
+ if (!hStmt.isValid()) {
+ qSqlWarning("QODBCDriver::record: Unable to allocate handle"_L1, d);
+ return fil;
+ }
- if (isIdentifierEscaped(table, QSqlDriver::TableName))
- table = stripDelimiters(table, QSqlDriver::TableName);
- else
- table = d->adjustCase(table);
+ QString catalog, schema, table;
+ d->splitTableQualifier(tablename, catalog, schema, table);
- SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
- d->hDbc,
- &hStmt);
- if (r != SQL_SUCCESS) {
- qSqlWarning(QLatin1String("QODBCDriver::record: Unable to allocate handle"), d);
- return fil;
+ SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER);
+ {
+ auto c = toSQLTCHAR(catalog);
+ auto s = toSQLTCHAR(schema);
+ auto t = toSQLTCHAR(table);
+ r = SQLColumns(hStmt.handle(),
+ catalog.isEmpty() ? nullptr : c.data(), c.size(),
+ schema.isEmpty() ? nullptr : s.data(), s.size(),
+ t.data(), t.size(),
+ nullptr,
+ 0);
}
- r = SQLSetStmtAttr(hStmt,
- SQL_ATTR_CURSOR_TYPE,
- (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
- SQL_IS_UINTEGER);
- r = SQLColumns(hStmt,
- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
- catalog.length(),
- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
- schema.length(),
- toSQLTCHAR(table).data(),
- table.length(),
- NULL,
- 0);
if (r != SQL_SUCCESS)
- qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d);
-
- if (d->hasSQLFetchScroll)
- r = SQLFetchScroll(hStmt,
- SQL_FETCH_NEXT,
- 0);
- else
- r = SQLFetch(hStmt);
+ qSqlWarning("QODBCDriver::record: Unable to execute column list"_L1, hStmt.handle());
+ r = d->sqlFetchNext(hStmt);
// Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
while (r == SQL_SUCCESS) {
-
- fil.append(qMakeFieldInfo(hStmt, d));
-
- if (d->hasSQLFetchScroll)
- r = SQLFetchScroll(hStmt,
- SQL_FETCH_NEXT,
- 0);
- else
- r = SQLFetch(hStmt);
+ fil.append(qMakeFieldInfo(hStmt.handle(), d));
+ r = d->sqlFetchNext(hStmt);
}
-
- r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
- if (r!= SQL_SUCCESS)
- qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ") + QString::number(r), d);
-
return fil;
}
@@ -2628,33 +2525,32 @@ QString QODBCDriver::formatValue(const QSqlField &field,
{
QString r;
if (field.isNull()) {
- r = QLatin1String("NULL");
+ r = "NULL"_L1;
} else if (field.metaType().id() == QMetaType::QDateTime) {
// Use an escape sequence for the datetime fields
if (field.value().toDateTime().isValid()){
QDate dt = field.value().toDateTime().date();
QTime tm = field.value().toDateTime().time();
// Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
- r = QLatin1String("{ ts '") +
- QString::number(dt.year()) + QLatin1Char('-') +
- QString::number(dt.month()).rightJustified(2, QLatin1Char('0'), true) +
- QLatin1Char('-') +
- QString::number(dt.day()).rightJustified(2, QLatin1Char('0'), true) +
- QLatin1Char(' ') +
+ r = "{ ts '"_L1 +
+ QString::number(dt.year()) + u'-' +
+ QString::number(dt.month()).rightJustified(2, u'0', true) +
+ u'-' +
+ QString::number(dt.day()).rightJustified(2, u'0', true) +
+ u' ' +
tm.toString() +
- QLatin1String("' }");
+ "' }"_L1;
} else
- r = QLatin1String("NULL");
+ r = "NULL"_L1;
} else if (field.metaType().id() == QMetaType::QByteArray) {
- QByteArray ba = field.value().toByteArray();
- QString res;
- static const char hexchars[] = "0123456789abcdef";
- for (int i = 0; i < ba.size(); ++i) {
- uchar s = (uchar) ba[i];
- res += QLatin1Char(hexchars[s >> 4]);
- res += QLatin1Char(hexchars[s & 0x0f]);
+ const QByteArray ba = field.value().toByteArray();
+ r.reserve((ba.size() + 1) * 2);
+ r = "0x"_L1;
+ for (const char c : ba) {
+ const uchar s = uchar(c);
+ r += QLatin1Char(QtMiscUtils::toHexLower(s >> 4));
+ r += QLatin1Char(QtMiscUtils::toHexLower(s & 0x0f));
}
- r = QLatin1String("0x") + res;
} else {
r = QSqlDriver::formatValue(field, trimStrings);
}
@@ -2673,9 +2569,10 @@ QString QODBCDriver::escapeIdentifier(const QString &identifier, IdentifierType)
QChar quote = const_cast<QODBCDriverPrivate*>(d)->quoteChar();
QString res = identifier;
if (!identifier.isEmpty() && !identifier.startsWith(quote) && !identifier.endsWith(quote) ) {
- res.replace(quote, QString(quote)+QString(quote));
- res.prepend(quote).append(quote);
- res.replace(QLatin1Char('.'), QString(quote)+QLatin1Char('.')+QString(quote));
+ const QString quoteStr(quote);
+ res.replace(quote, quoteStr + quoteStr);
+ res.replace(u'.', quoteStr + u'.' + quoteStr);
+ res = quote + res + quote;
}
return res;
}
diff --git a/src/plugins/sqldrivers/odbc/qsql_odbc_p.h b/src/plugins/sqldrivers/odbc/qsql_odbc_p.h
index ccd0206f38..23703b5d03 100644
--- a/src/plugins/sqldrivers/odbc/qsql_odbc_p.h
+++ b/src/plugins/sqldrivers/odbc/qsql_odbc_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSQL_ODBC_H
#define QSQL_ODBC_H
diff --git a/src/plugins/sqldrivers/psql/CMakeLists.txt b/src/plugins/sqldrivers/psql/CMakeLists.txt
index b151932da7..2f55ab4950 100644
--- a/src/plugins/sqldrivers/psql/CMakeLists.txt
+++ b/src/plugins/sqldrivers/psql/CMakeLists.txt
@@ -1,6 +1,7 @@
-# Generated from psql.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
-qt_find_package(PostgreSQL) # special case
+qt_find_package(PostgreSQL)
#####################################################################
## QPSQLDriverPlugin Plugin:
@@ -8,13 +9,14 @@ qt_find_package(PostgreSQL) # special case
qt_internal_add_plugin(QPSQLDriverPlugin
OUTPUT_NAME qsqlpsql
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_psql.cpp qsql_psql_p.h
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
+ QT_NO_CONTEXTLESS_CONNECT
LIBRARIES
PostgreSQL::PostgreSQL
Qt::Core
@@ -22,9 +24,6 @@ qt_internal_add_plugin(QPSQLDriverPlugin
Qt::SqlPrivate
)
-#### Keys ignored in scope 1:.:.:psql.pro:<TRUE>:
-# OTHER_FILES = "psql.json"
-
# PostgreSQL delivers header files that are not a part of PostgreSQL itself. When precompiled
# headers are processed, MinGW uses 'pthread.h' from the PostgreSQL installation directory.
# As result, we disable precompile headers for the plugin.
diff --git a/src/plugins/sqldrivers/psql/main.cpp b/src/plugins/sqldrivers/psql/main.cpp
index c31e6f64b7..e473fe1edc 100644
--- a/src/plugins/sqldrivers/psql/main.cpp
+++ b/src/plugins/sqldrivers/psql/main.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qsqldriverplugin.h>
#include <qstringlist.h>
@@ -43,6 +7,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QPSQLDriverPlugin : public QSqlDriverPlugin
{
Q_OBJECT
@@ -61,7 +27,7 @@ QPSQLDriverPlugin::QPSQLDriverPlugin()
QSqlDriver* QPSQLDriverPlugin::create(const QString &name)
{
- if (name == QLatin1String("QPSQL"))
+ if (name == "QPSQL"_L1)
return new QPSQLDriver;
return nullptr;
}
diff --git a/src/plugins/sqldrivers/psql/qsql_psql.cpp b/src/plugins/sqldrivers/psql/qsql_psql.cpp
index 4c3d6ca13f..7b9521fec6 100644
--- a/src/plugins/sqldrivers/psql/qsql_psql.cpp
+++ b/src/plugins/sqldrivers/psql/qsql_psql.cpp
@@ -1,47 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsql_psql_p.h"
#include <qcoreapplication.h>
#include <qvariant.h>
#include <qdatetime.h>
+#include <qloggingcategory.h>
#include <qregularexpression.h>
#include <qsqlerror.h>
#include <qsqlfield.h>
@@ -101,6 +66,10 @@ Q_DECLARE_METATYPE(PGresult*)
QT_BEGIN_NAMESPACE
+static Q_LOGGING_CATEGORY(lcPsql, "qt.sql.postgresql")
+
+using namespace Qt::StringLiterals;
+
inline void qPQfreemem(void *buffer)
{
PQfreemem(buffer);
@@ -108,11 +77,11 @@ inline void qPQfreemem(void *buffer)
/* Missing declaration of PGRES_SINGLE_TUPLE for PSQL below 9.2 */
#if !defined PG_VERSION_NUM || PG_VERSION_NUM-0 < 90200
-static const int PGRES_SINGLE_TUPLE = 9;
+static constexpr int PGRES_SINGLE_TUPLE = 9;
#endif
typedef int StatementId;
-static const StatementId InvalidStatementId = 0;
+static constexpr StatementId InvalidStatementId = 0;
class QPSQLResultPrivate;
@@ -156,10 +125,9 @@ public:
QSocketNotifier *sn = nullptr;
QPSQLDriver::Protocol pro = QPSQLDriver::Version6;
StatementId currentStmtId = InvalidStatementId;
- int stmtCount = 0;
+ StatementId stmtCount = InvalidStatementId;
mutable bool pendingNotifyCheck = false;
bool hasBackslashEscape = false;
- bool isUtf8 = false;
void appendTables(QStringList &tl, QSqlQuery &t, QChar type);
PGresult *exec(const char *stmt);
@@ -175,6 +143,7 @@ public:
bool setEncodingUtf8();
void setDatestyle();
void setByteaOutput();
+ void setUtcTimeZone();
void detectBackslashEscape();
mutable QHash<int, QString> oidToTable;
};
@@ -191,10 +160,10 @@ void QPSQLDriverPrivate::appendTables(QStringList &tl, QSqlQuery &t, QChar type)
t.exec(query);
while (t.next()) {
QString schema = t.value(1).toString();
- if (schema.isEmpty() || schema == QLatin1String("public"))
+ if (schema.isEmpty() || schema == "public"_L1)
tl.append(t.value(0).toString());
else
- tl.append(t.value(0).toString().prepend(QLatin1Char('.')).prepend(schema));
+ tl.append(t.value(0).toString().prepend(u'.').prepend(schema));
}
}
@@ -209,7 +178,7 @@ PGresult *QPSQLDriverPrivate::exec(const char *stmt)
PGresult *QPSQLDriverPrivate::exec(const QString &stmt)
{
- return exec((isUtf8 ? stmt.toUtf8() : stmt.toLocal8Bit()).constData());
+ return exec(stmt.toUtf8().constData());
}
StatementId QPSQLDriverPrivate::sendQuery(const QString &stmt)
@@ -217,8 +186,7 @@ StatementId QPSQLDriverPrivate::sendQuery(const QString &stmt)
// Discard any prior query results that the application didn't eat.
// This is required for PQsendQuery()
discardResults();
- const int result = PQsendQuery(connection,
- (isUtf8 ? stmt.toUtf8() : stmt.toLocal8Bit()).constData());
+ const int result = PQsendQuery(connection, stmt.toUtf8().constData());
currentStmtId = result ? generateStatementId() : InvalidStatementId;
return currentStmtId;
}
@@ -243,8 +211,8 @@ PGresult *QPSQLDriverPrivate::getResult(StatementId stmtId) const
if (stmtId != currentStmtId) {
// If you change the following warning, remember to update it
// on sql-driver.html page too.
- qWarning("QPSQLDriver::getResult: Query results lost - "
- "probably discarded on executing another SQL query.");
+ qCWarning(lcPsql, "QPSQLDriver::getResult: Query results lost - "
+ "probably discarded on executing another SQL query.");
return nullptr;
}
PGresult *result = PQgetResult(connection);
@@ -268,7 +236,7 @@ void QPSQLDriverPrivate::discardResults() const
StatementId QPSQLDriverPrivate::generateStatementId()
{
- int stmtId = ++stmtCount;
+ StatementId stmtId = ++stmtCount;
if (stmtId <= 0)
stmtId = stmtCount = 1;
return stmtId;
@@ -279,18 +247,18 @@ void QPSQLDriverPrivate::checkPendingNotifications() const
Q_Q(const QPSQLDriver);
if (seid.size() && !pendingNotifyCheck) {
pendingNotifyCheck = true;
- QMetaObject::invokeMethod(const_cast<QPSQLDriver*>(q), "_q_handleNotification", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(const_cast<QPSQLDriver*>(q), &QPSQLDriver::_q_handleNotification, Qt::QueuedConnection);
}
}
-class QPSQLResultPrivate : public QSqlResultPrivate
+class QPSQLResultPrivate final : public QSqlResultPrivate
{
Q_DECLARE_PUBLIC(QPSQLResult)
public:
Q_DECLARE_SQLDRIVER_PRIVATE(QPSQLDriver)
using QSqlResultPrivate::QSqlResultPrivate;
- QString fieldSerial(int i) const override { return QLatin1Char('$') + QString::number(i + 1); }
+ QString fieldSerial(qsizetype i) const override { return QString("$%1"_L1).arg(i + 1); }
void deallocatePreparedStmt();
std::queue<PGresult*> nextResultSets;
@@ -308,13 +276,13 @@ static QSqlError qMakeError(const QString &err, QSqlError::ErrorType type,
const QPSQLDriverPrivate *p, PGresult *result = nullptr)
{
const char *s = PQerrorMessage(p->connection);
- QString msg = p->isUtf8 ? QString::fromUtf8(s) : QString::fromLocal8Bit(s);
+ QString msg = QString::fromUtf8(s);
QString errorCode;
if (result) {
errorCode = QString::fromLatin1(PQresultErrorField(result, PG_DIAG_SQLSTATE));
msg += QString::fromLatin1("(%1)").arg(errorCode);
}
- return QSqlError(QLatin1String("QPSQL: ") + err, msg, type, errorCode);
+ return QSqlError("QPSQL: "_L1 + err, msg, type, errorCode);
}
bool QPSQLResultPrivate::processResults()
@@ -416,8 +384,10 @@ void QPSQLResultPrivate::deallocatePreparedStmt()
const QString stmt = QStringLiteral("DEALLOCATE ") + preparedStmtId;
PGresult *result = drv_d_func()->exec(stmt);
- if (PQresultStatus(result) != PGRES_COMMAND_OK)
- qWarning("Unable to free statement: %s", PQerrorMessage(drv_d_func()->connection));
+ if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+ const QString msg = QString::fromUtf8(PQerrorMessage(drv_d_func()->connection));
+ qCWarning(lcPsql, "Unable to free statement: %ls.", qUtf16Printable(msg));
+ }
PQclear(result);
}
preparedStmtId.clear();
@@ -627,7 +597,7 @@ QVariant QPSQLResult::data(int i)
{
Q_D(const QPSQLResult);
if (i >= PQnfields(d->result)) {
- qWarning("QPSQLResult::data: column %d out of range", i);
+ qCWarning(lcPsql, "QPSQLResult::data: column %d out of range.", i);
return QVariant();
}
const int currentRow = isForwardOnly() ? 0 : at();
@@ -640,7 +610,7 @@ QVariant QPSQLResult::data(int i)
case QMetaType::Bool:
return QVariant((bool)(val[0] == 't'));
case QMetaType::QString:
- return d->drv_d_func()->isUtf8 ? QString::fromUtf8(val) : QString::fromLatin1(val);
+ return QString::fromUtf8(val);
case QMetaType::LongLong:
if (val[0] == '-')
return QByteArray::fromRawData(val, qstrlen(val)).toLongLong();
@@ -675,23 +645,21 @@ QVariant QPSQLResult::data(int i)
}
return dbl;
}
- case QMetaType::QDate:
#if QT_CONFIG(datestring)
+ case QMetaType::QDate:
return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate));
-#else
- return QVariant(QString::fromLatin1(val));
-#endif
case QMetaType::QTime:
-#if QT_CONFIG(datestring)
return QVariant(QTime::fromString(QString::fromLatin1(val), Qt::ISODate));
+ case QMetaType::QDateTime: {
+ QString tzString(QString::fromLatin1(val));
+ if (!tzString.endsWith(u'Z'))
+ tzString.append(u'Z'); // make UTC
+ return QVariant(QDateTime::fromString(tzString, Qt::ISODate));
+ }
#else
- return QVariant(QString::fromLatin1(val));
-#endif
+ case QMetaType::QDate:
+ case QMetaType::QTime:
case QMetaType::QDateTime:
-#if QT_CONFIG(datestring)
- return QVariant(QDateTime::fromString(QString::fromLatin1(val),
- Qt::ISODate).toLocalTime());
-#else
return QVariant(QString::fromLatin1(val));
#endif
case QMetaType::QByteArray: {
@@ -702,7 +670,7 @@ QVariant QPSQLResult::data(int i)
return QVariant(ba);
}
default:
- qWarning("QPSQLResult::data: unknown data type");
+ qCWarning(lcPsql, "QPSQLResult::data: unhandled data type %d.", type.id());
}
return QVariant();
}
@@ -781,10 +749,7 @@ QSqlRecord QPSQLResult::record() const
int count = PQnfields(d->result);
QSqlField f;
for (int i = 0; i < count; ++i) {
- if (d->drv_d_func()->isUtf8)
- f.setName(QString::fromUtf8(PQfname(d->result, i)));
- else
- f.setName(QString::fromLocal8Bit(PQfname(d->result, i)));
+ f.setName(QString::fromUtf8(PQfname(d->result, i)));
const int tableOid = PQftable(d->result, i);
// WARNING: We cannot execute any other SQL queries on
// the same db connection while forward-only mode is active
@@ -835,7 +800,6 @@ QSqlRecord QPSQLResult::record() const
f.setLength(len);
f.setPrecision(precision);
- f.setSqlType(ptype);
info.append(f);
}
return info;
@@ -856,12 +820,12 @@ static QString qCreateParamString(const QList<QVariant> &boundValues, const QSql
QSqlField f;
for (const QVariant &val : boundValues) {
f.setMetaType(val.metaType());
- if (val.isNull())
+ if (QSqlResultPrivate::isVariantNull(val))
f.clear();
else
f.setValue(val);
if (!params.isNull())
- params.append(QLatin1String(", "));
+ params.append(", "_L1);
params.append(driver->formatValue(f));
}
return params;
@@ -869,7 +833,7 @@ static QString qCreateParamString(const QList<QVariant> &boundValues, const QSql
QString qMakePreparedStmtId()
{
- static QBasicAtomicInt qPreparedStmtCount = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QBasicAtomicInt qPreparedStmtCount = Q_BASIC_ATOMIC_INITIALIZER(0);
QString id = QStringLiteral("qpsqlpstmt_") + QString::number(qPreparedStmtCount.fetchAndAddRelaxed(1) + 1, 16);
return id;
}
@@ -952,7 +916,7 @@ void QPSQLDriverPrivate::setDatestyle()
PGresult *result = exec("SET DATESTYLE TO 'ISO'");
int status = PQresultStatus(result);
if (status != PGRES_COMMAND_OK)
- qWarning("%s", PQerrorMessage(connection));
+ qCWarning(lcPsql) << QString::fromUtf8(PQerrorMessage(connection));
PQclear(result);
}
@@ -965,11 +929,20 @@ void QPSQLDriverPrivate::setByteaOutput()
PGresult *result = exec("SET bytea_output TO escape");
int status = PQresultStatus(result);
if (status != PGRES_COMMAND_OK)
- qWarning("%s", PQerrorMessage(connection));
+ qCWarning(lcPsql) << QString::fromUtf8(PQerrorMessage(connection));
PQclear(result);
}
}
+void QPSQLDriverPrivate::setUtcTimeZone()
+{
+ PGresult *result = exec("SET TIME ZONE 'UTC'");
+ int status = PQresultStatus(result);
+ if (status != PGRES_COMMAND_OK)
+ qCWarning(lcPsql) << QString::fromUtf8(PQerrorMessage(connection));
+ PQclear(result);
+}
+
void QPSQLDriverPrivate::detectBackslashEscape()
{
// standard_conforming_strings option introduced in 8.2
@@ -981,7 +954,7 @@ void QPSQLDriverPrivate::detectBackslashEscape()
PGresult *result = exec(QStringLiteral("SELECT '\\\\' x"));
int status = PQresultStatus(result);
if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK)
- if (QString::fromLatin1(PQgetvalue(result, 0, 0)) == QLatin1String("\\"))
+ if (QString::fromLatin1(PQgetvalue(result, 0, 0)) == "\\"_L1)
hasBackslashEscape = true;
PQclear(result);
}
@@ -1093,9 +1066,9 @@ QPSQLDriver::Protocol QPSQLDriverPrivate::getPSQLVersion()
QPSQLDriver::Protocol clientVersion =
#if defined(PG_MAJORVERSION)
- qFindPSQLVersion(QLatin1String(PG_MAJORVERSION));
+ qFindPSQLVersion(PG_MAJORVERSION ""_L1);
#elif defined(PG_VERSION)
- qFindPSQLVersion(QLatin1String(PG_VERSION));
+ qFindPSQLVersion(PG_VERSION ""_L1);
#else
QPSQLDriver::VersionUnknown;
#endif
@@ -1103,16 +1076,16 @@ QPSQLDriver::Protocol QPSQLDriverPrivate::getPSQLVersion()
if (serverVersion == QPSQLDriver::VersionUnknown) {
serverVersion = clientVersion;
if (serverVersion != QPSQLDriver::VersionUnknown)
- qWarning("The server version of this PostgreSQL is unknown, falling back to the client version.");
+ qCWarning(lcPsql, "The server version of this PostgreSQL is unknown, "
+ "falling back to the client version.");
}
// Keep the old behavior unchanged
if (serverVersion == QPSQLDriver::VersionUnknown)
serverVersion = QPSQLDriver::Version6;
- if (serverVersion < QPSQLDriver::Version7_3) {
- qWarning("This version of PostgreSQL is not supported and may not work.");
- }
+ if (serverVersion < QPSQLDriver::Version7_3)
+ qCWarning(lcPsql, "This version of PostgreSQL is not supported and may not work.");
return serverVersion;
}
@@ -1138,8 +1111,7 @@ QPSQLDriver::QPSQLDriver(PGconn *conn, QObject *parent)
QPSQLDriver::~QPSQLDriver()
{
Q_D(QPSQLDriver);
- if (d->connection)
- PQfinish(d->connection);
+ PQfinish(d->connection);
}
QVariant QPSQLDriver::handle() const
@@ -1159,6 +1131,7 @@ bool QPSQLDriver::hasFeature(DriverFeature f) const
case EventNotifications:
case MultipleResultSets:
case BLOB:
+ case Unicode:
return true;
case PreparedQueries:
case PositionalPlaceholders:
@@ -1169,8 +1142,6 @@ bool QPSQLDriver::hasFeature(DriverFeature f) const
case FinishQuery:
case CancelQuery:
return false;
- case Unicode:
- return d->isUtf8;
}
return false;
}
@@ -1183,9 +1154,9 @@ bool QPSQLDriver::hasFeature(DriverFeature f) const
*/
static QString qQuote(QString s)
{
- s.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
- s.replace(QLatin1Char('\''), QLatin1String("\\'"));
- s.append(QLatin1Char('\'')).prepend(QLatin1Char('\''));
+ s.replace(u'\\', "\\\\"_L1);
+ s.replace(u'\'', "\\'"_L1);
+ s.append(u'\'').prepend(u'\'');
return s;
}
@@ -1200,21 +1171,21 @@ bool QPSQLDriver::open(const QString &db,
close();
QString connectString;
if (!host.isEmpty())
- connectString.append(QLatin1String("host=")).append(qQuote(host));
+ connectString.append("host="_L1).append(qQuote(host));
if (!db.isEmpty())
- connectString.append(QLatin1String(" dbname=")).append(qQuote(db));
+ connectString.append(" dbname="_L1).append(qQuote(db));
if (!user.isEmpty())
- connectString.append(QLatin1String(" user=")).append(qQuote(user));
+ connectString.append(" user="_L1).append(qQuote(user));
if (!password.isEmpty())
- connectString.append(QLatin1String(" password=")).append(qQuote(password));
+ connectString.append(" password="_L1).append(qQuote(password));
if (port != -1)
- connectString.append(QLatin1String(" port=")).append(qQuote(QString::number(port)));
+ connectString.append(" port="_L1).append(qQuote(QString::number(port)));
// add any connect options - the server will handle error detection
if (!connOpts.isEmpty()) {
QString opt = connOpts;
- opt.replace(QLatin1Char(';'), QLatin1Char(' '), Qt::CaseInsensitive);
- connectString.append(QLatin1Char(' ')).append(opt);
+ opt.replace(';'_L1, ' '_L1, Qt::CaseInsensitive);
+ connectString.append(u' ').append(opt);
}
d->connection = PQconnectdb(std::move(connectString).toLocal8Bit().constData());
@@ -1228,9 +1199,16 @@ bool QPSQLDriver::open(const QString &db,
d->pro = d->getPSQLVersion();
d->detectBackslashEscape();
- d->isUtf8 = d->setEncodingUtf8();
+ if (!d->setEncodingUtf8()) {
+ setLastError(qMakeError(tr("Unable to set client encoding to 'UNICODE'"), QSqlError::ConnectionError, d));
+ setOpenError(true);
+ PQfinish(d->connection);
+ d->connection = nullptr;
+ return false;
+ }
d->setDatestyle();
d->setByteaOutput();
+ d->setUtcTimeZone();
setOpen(true);
setOpenError(false);
@@ -1243,13 +1221,12 @@ void QPSQLDriver::close()
d->seid.clear();
if (d->sn) {
- disconnect(d->sn, SIGNAL(activated(QSocketDescriptor)), this, SLOT(_q_handleNotification()));
+ disconnect(d->sn, &QSocketNotifier::activated, this, &QPSQLDriver::_q_handleNotification);
delete d->sn;
d->sn = nullptr;
}
- if (d->connection)
- PQfinish(d->connection);
+ PQfinish(d->connection);
d->connection = nullptr;
setOpen(false);
setOpenError(false);
@@ -1264,7 +1241,7 @@ bool QPSQLDriver::beginTransaction()
{
Q_D(QPSQLDriver);
if (!isOpen()) {
- qWarning("QPSQLDriver::beginTransaction: Database not open");
+ qCWarning(lcPsql, "QPSQLDriver::beginTransaction: Database not open.");
return false;
}
PGresult *res = d->exec("BEGIN");
@@ -1282,7 +1259,7 @@ bool QPSQLDriver::commitTransaction()
{
Q_D(QPSQLDriver);
if (!isOpen()) {
- qWarning("QPSQLDriver::commitTransaction: Database not open");
+ qCWarning(lcPsql, "QPSQLDriver::commitTransaction: Database not open.");
return false;
}
PGresult *res = d->exec("COMMIT");
@@ -1292,7 +1269,7 @@ bool QPSQLDriver::commitTransaction()
// XXX
// This hack is used to tell if the transaction has succeeded for the protocol versions of
// PostgreSQL below. For 7.x and other protocol versions we are left in the dark.
- // This hack can dissapear once there is an API to query this sort of information.
+ // This hack can disappear once there is an API to query this sort of information.
if (d->pro >= QPSQLDriver::Version8) {
transaction_failed = qstrcmp(PQcmdStatus(res), "ROLLBACK") == 0;
}
@@ -1311,7 +1288,7 @@ bool QPSQLDriver::rollbackTransaction()
{
Q_D(QPSQLDriver);
if (!isOpen()) {
- qWarning("QPSQLDriver::rollbackTransaction: Database not open");
+ qCWarning(lcPsql, "QPSQLDriver::rollbackTransaction: Database not open.");
return false;
}
PGresult *res = d->exec("ROLLBACK");
@@ -1335,9 +1312,9 @@ QStringList QPSQLDriver::tables(QSql::TableType type) const
t.setForwardOnly(true);
if (type & QSql::Tables)
- const_cast<QPSQLDriverPrivate*>(d)->appendTables(tl, t, QLatin1Char('r'));
+ const_cast<QPSQLDriverPrivate*>(d)->appendTables(tl, t, u'r');
if (type & QSql::Views)
- const_cast<QPSQLDriverPrivate*>(d)->appendTables(tl, t, QLatin1Char('v'));
+ const_cast<QPSQLDriverPrivate*>(d)->appendTables(tl, t, u'v');
if (type & QSql::SystemTables) {
t.exec(QStringLiteral("SELECT relname FROM pg_class WHERE (relkind = 'r') "
"AND (relname LIKE 'pg_%') "));
@@ -1350,7 +1327,7 @@ QStringList QPSQLDriver::tables(QSql::TableType type) const
static void qSplitTableName(QString &tablename, QString &schema)
{
- int dot = tablename.indexOf(QLatin1Char('.'));
+ qsizetype dot = tablename.indexOf(u'.');
if (dot == -1)
return;
schema = tablename.left(dot);
@@ -1438,8 +1415,8 @@ QSqlRecord QPSQLDriver::record(const QString &tablename) const
precision = -1;
}
QString defVal = query.value(5).toString();
- if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\'')) {
- const int end = defVal.lastIndexOf(QLatin1Char('\''));
+ if (!defVal.isEmpty() && defVal.at(0) == u'\'') {
+ const qsizetype end = defVal.lastIndexOf(u'\'');
if (end > 0)
defVal = defVal.mid(1, end - 1);
}
@@ -1448,7 +1425,6 @@ QSqlRecord QPSQLDriver::record(const QString &tablename) const
f.setLength(len);
f.setPrecision(precision);
f.setDefaultValue(defVal);
- f.setSqlType(query.value(1).toInt());
info.append(f);
}
@@ -1473,36 +1449,32 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
r = nullStr();
} else {
switch (field.metaType().id()) {
- case QMetaType::QDateTime:
-#if QT_CONFIG(datestring)
- if (field.value().toDateTime().isValid()) {
+ case QMetaType::QDateTime: {
+ const auto dt = field.value().toDateTime();
+ if (dt.isValid()) {
// we force the value to be considered with a timezone information, and we force it to be UTC
// this is safe since postgresql stores only the UTC value and not the timezone offset (only used
// while parsing), so we have correct behavior in both case of with timezone and without tz
- r = QStringLiteral("TIMESTAMP WITH TIME ZONE ") + QLatin1Char('\'') +
- QLocale::c().toString(field.value().toDateTime().toUTC(), u"yyyy-MM-ddThh:mm:ss.zzz") +
- QLatin1Char('Z') + QLatin1Char('\'');
+ r = QStringLiteral("TIMESTAMP WITH TIME ZONE ") + u'\'' +
+ QLocale::c().toString(dt.toUTC(), u"yyyy-MM-ddThh:mm:ss.zzz") +
+ u'Z' + u'\'';
} else {
r = nullStr();
}
-#else
- r = nullStr();
-#endif // datestring
break;
- case QMetaType::QTime:
-#if QT_CONFIG(datestring)
- if (field.value().toTime().isValid()) {
- r = QLatin1Char('\'') + field.value().toTime().toString(u"hh:mm:ss.zzz") + QLatin1Char('\'');
- } else
-#endif
- {
+ }
+ case QMetaType::QTime: {
+ const auto t = field.value().toTime();
+ if (t.isValid())
+ r = u'\'' + QLocale::c().toString(t, u"hh:mm:ss.zzz") + u'\'';
+ else
r = nullStr();
- }
break;
+ }
case QMetaType::QString:
r = QSqlDriver::formatValue(field, trimStrings);
if (d->hasBackslashEscape)
- r.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
+ r.replace(u'\\', "\\\\"_L1);
break;
case QMetaType::Bool:
if (field.value().toBool())
@@ -1518,9 +1490,9 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
#else
unsigned char *data = PQescapeBytea((const unsigned char*)ba.constData(), ba.size(), &len);
#endif
- r += QLatin1Char('\'');
- r += QLatin1String((const char*)data);
- r += QLatin1Char('\'');
+ r += u'\'';
+ r += QLatin1StringView((const char*)data);
+ r += u'\'';
qPQfreemem(data);
break;
}
@@ -1535,7 +1507,7 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
r = QSqlDriver::formatValue(field, trimStrings);
break;
case QMetaType::QUuid:
- r = QLatin1Char('\'') + field.value().toString() + QLatin1Char('\'');
+ r = u'\'' + field.value().toString() + u'\'';
break;
default:
r = QSqlDriver::formatValue(field, trimStrings);
@@ -1548,10 +1520,10 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
QString QPSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
{
QString res = identifier;
- if (!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
- res.replace(QLatin1Char('"'), QLatin1String("\"\""));
- res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
- res.replace(QLatin1Char('.'), QLatin1String("\".\""));
+ if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"') ) {
+ res.replace(u'"', "\"\""_L1);
+ res.replace(u'.', "\".\""_L1);
+ res = u'"' + res + u'"';
}
return res;
}
@@ -1572,7 +1544,7 @@ bool QPSQLDriver::subscribeToNotification(const QString &name)
{
Q_D(QPSQLDriver);
if (!isOpen()) {
- qWarning("QPSQLDriver::subscribeToNotificationImplementation: database not open.");
+ qCWarning(lcPsql, "QPSQLDriver::subscribeToNotification: Database not open.");
return false;
}
@@ -1597,11 +1569,12 @@ bool QPSQLDriver::subscribeToNotification(const QString &name)
PQclear(result);
if (!d->sn) {
- d->sn = new QSocketNotifier(socket, QSocketNotifier::Read);
- connect(d->sn, SIGNAL(activated(QSocketDescriptor)), this, SLOT(_q_handleNotification()));
+ d->sn = new QSocketNotifier(socket, QSocketNotifier::Read, this);
+ connect(d->sn, &QSocketNotifier::activated, this, &QPSQLDriver::_q_handleNotification);
}
} else {
- qWarning("QPSQLDriver::subscribeToNotificationImplementation: PQsocket didn't return a valid socket to listen on");
+ qCWarning(lcPsql, "QPSQLDriver::subscribeToNotificationImplementation: "
+ "PQsocket didn't return a valid socket to listen on.");
return false;
}
@@ -1612,13 +1585,13 @@ bool QPSQLDriver::unsubscribeFromNotification(const QString &name)
{
Q_D(QPSQLDriver);
if (!isOpen()) {
- qWarning("QPSQLDriver::unsubscribeFromNotificationImplementation: database not open.");
+ qCWarning(lcPsql, "QPSQLDriver::unsubscribeFromNotification: Database not open.");
return false;
}
if (!d->seid.contains(name)) {
- qWarning("QPSQLDriver::unsubscribeFromNotificationImplementation: not subscribed to '%s'.",
- qPrintable(name));
+ qCWarning(lcPsql, "QPSQLDriver::unsubscribeFromNotification: not subscribed to '%ls'.",
+ qUtf16Printable(name));
return false;
}
@@ -1634,7 +1607,7 @@ bool QPSQLDriver::unsubscribeFromNotification(const QString &name)
d->seid.removeAll(name);
if (d->seid.isEmpty()) {
- disconnect(d->sn, SIGNAL(activated(QSocketDescriptor)), this, SLOT(_q_handleNotification()));
+ disconnect(d->sn, &QSocketNotifier::activated, this, &QPSQLDriver::_q_handleNotification);
delete d->sn;
d->sn = nullptr;
}
@@ -1656,22 +1629,24 @@ void QPSQLDriver::_q_handleNotification()
PGnotify *notify = nullptr;
while ((notify = PQnotifies(d->connection)) != nullptr) {
- QString name(QLatin1String(notify->relname));
+ QString name(QLatin1StringView(notify->relname));
if (d->seid.contains(name)) {
QString payload;
#if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 70400
if (notify->extra)
- payload = d->isUtf8 ? QString::fromUtf8(notify->extra) : QString::fromLatin1(notify->extra);
+ payload = QString::fromUtf8(notify->extra);
#endif
QSqlDriver::NotificationSource source = (notify->be_pid == PQbackendPID(d->connection)) ? QSqlDriver::SelfSource : QSqlDriver::OtherSource;
emit notification(name, source, payload);
}
else
- qWarning("QPSQLDriver: received notification for '%s' which isn't subscribed to.",
- qPrintable(name));
+ qCWarning(lcPsql, "QPSQLDriver: received notification for '%ls' which isn't subscribed to.",
+ qUtf16Printable(name));
qPQfreemem(notify);
}
}
QT_END_NAMESPACE
+
+#include "moc_qsql_psql_p.cpp"
diff --git a/src/plugins/sqldrivers/psql/qsql_psql_p.h b/src/plugins/sqldrivers/psql/qsql_psql_p.h
index 22c0761667..4ff83bee21 100644
--- a/src/plugins/sqldrivers/psql/qsql_psql_p.h
+++ b/src/plugins/sqldrivers/psql/qsql_psql_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSQL_PSQL_H
#define QSQL_PSQL_H
diff --git a/src/plugins/sqldrivers/qt_cmdline.cmake b/src/plugins/sqldrivers/qt_cmdline.cmake
index 91b6f9f767..2bb1fc64eb 100644
--- a/src/plugins/sqldrivers/qt_cmdline.cmake
+++ b/src/plugins/sqldrivers/qt_cmdline.cmake
@@ -1,6 +1,9 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_commandline_option(mysql_config TYPE string)
qt_commandline_option(psql_config TYPE string)
-qt_commandline_option(sqlite TYPE enum NAME system-sqlite MAPPING qt no system yes)
+qt_commandline_option(sqlite CONTROLS_FEATURE TYPE enum NAME system-sqlite MAPPING qt no system yes)
qt_commandline_option(sql-db2 TYPE boolean)
qt_commandline_option(sql-ibase TYPE boolean)
qt_commandline_option(sql-mysql TYPE boolean)
@@ -8,6 +11,7 @@ qt_commandline_option(sql-oci TYPE boolean)
qt_commandline_option(sql-odbc TYPE boolean)
qt_commandline_option(sql-psql TYPE boolean)
qt_commandline_option(sql-sqlite TYPE boolean)
+qt_commandline_option(sql-mimer TYPE boolean)
qt_commandline_option(plugin-sql-db2 TYPE void NAME sql-db2)
qt_commandline_option(plugin-sql-ibase TYPE void NAME sql-ibase)
qt_commandline_option(plugin-sql-mysql TYPE void NAME sql-mysql)
@@ -15,4 +19,4 @@ qt_commandline_option(plugin-sql-oci TYPE void NAME sql-oci)
qt_commandline_option(plugin-sql-odbc TYPE void NAME sql-odbc)
qt_commandline_option(plugin-sql-psql TYPE void NAME sql-psql)
qt_commandline_option(plugin-sql-sqlite TYPE void NAME sql-sqlite)
-qt_commandline_assignment(MYSQL_PATH mysql.prefix)
+qt_commandline_option(plugin-sql-mimer TYPE void NAME sql-mimer)
diff --git a/src/plugins/sqldrivers/sqlite/CMakeLists.txt b/src/plugins/sqldrivers/sqlite/CMakeLists.txt
index ad39f1547b..4203a5c437 100644
--- a/src/plugins/sqldrivers/sqlite/CMakeLists.txt
+++ b/src/plugins/sqldrivers/sqlite/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from sqlite.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## QSQLiteDriverPlugin Plugin:
@@ -6,23 +7,21 @@
qt_internal_add_plugin(QSQLiteDriverPlugin
OUTPUT_NAME qsqlite
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
qsql_sqlite.cpp qsql_sqlite_p.h
+ qsql_sqlite_vfs.cpp qsql_sqlite_vfs_p.h
smain.cpp
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
+ QT_NO_CONTEXTLESS_CONNECT
LIBRARIES
Qt::Core
Qt::CorePrivate
Qt::SqlPrivate
)
-#### Keys ignored in scope 1:.:.:sqlite.pro:<TRUE>:
-# OTHER_FILES = "sqlite.json"
-# QT_FOR_CONFIG = "sqldrivers-private"
-
## Scopes:
#####################################################################
@@ -31,12 +30,19 @@ qt_internal_extend_target(QSQLiteDriverPlugin CONDITION QT_FEATURE_system_sqlite
SQLite::SQLite3
)
-# special case begin
if (NOT QT_FEATURE_system_sqlite)
# On newer compilers compiling sqlite.c produces warnings
qt_disable_warnings(QSQLiteDriverPlugin)
endif()
-# special case end
+
+if(QT_FEATURE_system_sqlite)
+ qt_internal_force_macos_intel_arch(QSQLiteDriverPlugin)
+endif()
+
+qt_internal_extend_target(QSQLiteDriverPlugin CONDITION NOT QT_FEATURE_system_sqlite AND VXWORKS
+ DEFINES
+ SQLITE_OS_UNIX=1
+)
qt_internal_extend_target(QSQLiteDriverPlugin CONDITION NOT QT_FEATURE_system_sqlite
SOURCES
@@ -45,8 +51,11 @@ qt_internal_extend_target(QSQLiteDriverPlugin CONDITION NOT QT_FEATURE_system_sq
SQLITE_ENABLE_COLUMN_METADATA
SQLITE_ENABLE_FTS3
SQLITE_ENABLE_FTS3_PARENTHESIS
+ SQLITE_ENABLE_FTS4
SQLITE_ENABLE_FTS5
+ SQLITE_ENABLE_GEOPOLY
SQLITE_ENABLE_JSON1
+ SQLITE_ENABLE_MATH_FUNCTIONS
SQLITE_ENABLE_RTREE
SQLITE_OMIT_COMPLETE
INCLUDE_DIRECTORIES
@@ -78,6 +87,10 @@ qt_internal_extend_target(QSQLiteDriverPlugin CONDITION UNIX AND NOT QT_FEATURE_
HAVE_USLEEP=1
)
+qt_internal_extend_target(QSQLiteDriverPlugin CONDITION INTEGRITY
+ COMPILE_OPTIONS -include qplatformdefs.h
+)
+
qt_internal_extend_target(QSQLiteDriverPlugin CONDITION QT_FEATURE_dlopen AND NOT QT_FEATURE_system_sqlite
LIBRARIES
${CMAKE_DL_LIBS}
@@ -88,5 +101,7 @@ qt_internal_extend_target(QSQLiteDriverPlugin CONDITION NOT QT_FEATURE_dlopen AN
SQLITE_OMIT_LOAD_EXTENSION
)
-#### Keys ignored in scope 12:.:../../../3rdparty:../../../3rdparty/sqlite.pri:INTEGRITY:
-# QMAKE_CFLAGS = "-include" "qplatformdefs.h"
+qt_internal_extend_target(QSQLiteDriverPlugin CONDITION NOT QT_FEATURE_thread AND NOT QT_FEATURE_system_sqlite
+ DEFINES
+ SQLITE_THREADSAFE=0
+)
diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
index bd12b4f3aa..469afcac83 100644
--- a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
+++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsql_sqlite_p.h"
@@ -74,33 +38,22 @@ Q_DECLARE_METATYPE(sqlite3_stmt*)
QT_BEGIN_NAMESPACE
-static QString _q_escapeIdentifier(const QString &identifier)
-{
- QString res = identifier;
- if (!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"'))) {
- res.replace(QLatin1Char('"'), QLatin1String("\"\""));
- res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
- res.replace(QLatin1Char('.'), QLatin1String("\".\""));
- }
- return res;
-}
+using namespace Qt::StringLiterals;
static int qGetColumnType(const QString &tpName)
{
const QString typeName = tpName.toLower();
- if (typeName == QLatin1String("integer")
- || typeName == QLatin1String("int"))
+ if (typeName == "integer"_L1 || typeName == "int"_L1)
return QMetaType::Int;
- if (typeName == QLatin1String("double")
- || typeName == QLatin1String("float")
- || typeName == QLatin1String("real")
- || typeName.startsWith(QLatin1String("numeric")))
+ if (typeName == "double"_L1
+ || typeName == "float"_L1
+ || typeName == "real"_L1
+ || typeName.startsWith("numeric"_L1))
return QMetaType::Double;
- if (typeName == QLatin1String("blob"))
+ if (typeName == "blob"_L1)
return QMetaType::QByteArray;
- if (typeName == QLatin1String("boolean")
- || typeName == QLatin1String("bool"))
+ if (typeName == "boolean"_L1 || typeName == "bool"_L1)
return QMetaType::Bool;
return QMetaType::QString;
}
@@ -145,11 +98,64 @@ class QSQLiteDriverPrivate : public QSqlDriverPrivate
public:
inline QSQLiteDriverPrivate() : QSqlDriverPrivate(QSqlDriver::SQLite) {}
+ bool isIdentifierEscaped(QStringView identifier) const;
+ QSqlIndex getTableInfo(QSqlQuery &query, const QString &tableName,
+ bool onlyPIndex = false) const;
+
sqlite3 *access = nullptr;
QList<QSQLiteResult *> results;
QStringList notificationid;
};
+bool QSQLiteDriverPrivate::isIdentifierEscaped(QStringView identifier) const
+{
+ return identifier.size() > 2
+ && ((identifier.startsWith(u'"') && identifier.endsWith(u'"'))
+ || (identifier.startsWith(u'`') && identifier.endsWith(u'`'))
+ || (identifier.startsWith(u'[') && identifier.endsWith(u']')));
+}
+
+QSqlIndex QSQLiteDriverPrivate::getTableInfo(QSqlQuery &query, const QString &tableName,
+ bool onlyPIndex) const
+{
+ Q_Q(const QSQLiteDriver);
+ QString schema;
+ QString table = q->escapeIdentifier(tableName, QSqlDriver::TableName);
+ const auto indexOfSeparator = table.indexOf(u'.');
+ if (indexOfSeparator > -1) {
+ auto leftName = QStringView{table}.first(indexOfSeparator);
+ auto rightName = QStringView{table}.sliced(indexOfSeparator + 1);
+ if (isIdentifierEscaped(leftName) && isIdentifierEscaped(rightName)) {
+ schema = leftName.toString() + u'.';
+ table = rightName.toString();
+ }
+ }
+
+ query.exec("PRAGMA "_L1 + schema + "table_info ("_L1 + table + u')');
+ QSqlIndex ind;
+ while (query.next()) {
+ bool isPk = query.value(5).toInt();
+ if (onlyPIndex && !isPk)
+ continue;
+ QString typeName = query.value(2).toString().toLower();
+ QString defVal = query.value(4).toString();
+ if (!defVal.isEmpty() && defVal.at(0) == u'\'') {
+ const int end = defVal.lastIndexOf(u'\'');
+ if (end > 0)
+ defVal = defVal.mid(1, end - 1);
+ }
+
+ QSqlField fld(query.value(1).toString(), QMetaType(qGetColumnType(typeName)), tableName);
+ if (isPk && (typeName == "integer"_L1))
+ // INTEGER PRIMARY KEY fields are auto-generated in sqlite
+ // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY!
+ fld.setAutoValue(true);
+ fld.setRequired(query.value(3).toInt() != 0);
+ fld.setDefaultValue(defVal);
+ ind.append(fld);
+ }
+ return ind;
+}
class QSQLiteResultPrivate : public QSqlCachedResultPrivate
{
@@ -189,7 +195,7 @@ void QSQLiteResultPrivate::finalize()
return;
sqlite3_finalize(stmt);
- stmt = 0;
+ stmt = nullptr;
}
void QSQLiteResultPrivate::initColumns(bool emptyResultset)
@@ -204,10 +210,10 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
for (int i = 0; i < nCols; ++i) {
QString colName = QString(reinterpret_cast<const QChar *>(
sqlite3_column_name16(stmt, i))
- ).remove(QLatin1Char('"'));
+ ).remove(u'"');
const QString tableName = QString(reinterpret_cast<const QChar *>(
sqlite3_column_table_name16(stmt, i))
- ).remove(QLatin1Char('"'));
+ ).remove(u'"');
// must use typeName for resolving the type to match QSqliteDriver::record
QString typeName = QString(reinterpret_cast<const QChar *>(
sqlite3_column_decltype16(stmt, i)));
@@ -241,7 +247,6 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
}
QSqlField fld(colName, QMetaType(fieldType), tableName);
- fld.setSqlType(stp);
rInf.append(fld);
}
}
@@ -254,7 +259,7 @@ bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int i
// already fetched
Q_ASSERT(!initialFetch);
skipRow = false;
- for(int i=0;i<firstRow.count();i++)
+ for(int i=0;i<firstRow.size();i++)
values[i]=firstRow[i];
return skippedStatus;
}
@@ -413,10 +418,10 @@ bool QSQLiteResult::execBatch(bool arrayBind)
Q_D(QSqlResult);
QScopedValueRollback<QList<QVariant>> valuesScope(d->values);
QList<QVariant> values = d->values;
- if (values.count() == 0)
+ if (values.size() == 0)
return false;
- for (int i = 0; i < values.at(0).toList().count(); ++i) {
+ for (int i = 0; i < values.at(0).toList().size(); ++i) {
d->values.clear();
QScopedValueRollback<QHash<QString, QList<int>>> indexesScope(d->indexes);
auto it = d->indexes.constBegin();
@@ -450,16 +455,16 @@ bool QSQLiteResult::exec()
}
int paramCount = sqlite3_bind_parameter_count(d->stmt);
- bool paramCountIsValid = paramCount == values.count();
+ bool paramCountIsValid = paramCount == values.size();
#if (SQLITE_VERSION_NUMBER >= 3003011)
// In the case of the reuse of a named placeholder
// We need to check explicitly that paramCount is greater than or equal to 1, as sqlite
// can end up in a case where for virtual tables it returns 0 even though it
// has parameters
- if (paramCount >= 1 && paramCount < values.count()) {
+ if (paramCount >= 1 && paramCount < values.size()) {
const auto countIndexes = [](int counter, const QList<int> &indexList) {
- return counter + indexList.length();
+ return counter + indexList.size();
};
const int bindParamCount = std::accumulate(d->indexes.cbegin(),
@@ -467,7 +472,7 @@ bool QSQLiteResult::exec()
0,
countIndexes);
- paramCountIsValid = bindParamCount == values.count();
+ paramCountIsValid = bindParamCount == values.size();
// When using named placeholders, it will reuse the index for duplicated
// placeholders. So we need to ensure the QList has only one instance of
// each value as SQLite will do the rest for us.
@@ -494,9 +499,9 @@ bool QSQLiteResult::exec()
if (paramCountIsValid) {
for (int i = 0; i < paramCount; ++i) {
res = SQLITE_OK;
- const QVariant value = values.at(i);
+ const QVariant &value = values.at(i);
- if (value.isNull()) {
+ if (QSqlResultPrivate::isVariantNull(value)) {
res = sqlite3_bind_null(d->stmt, i + 1);
} else {
switch (value.userType()) {
@@ -519,7 +524,7 @@ bool QSQLiteResult::exec()
case QMetaType::QDateTime: {
const QDateTime dateTime = value.toDateTime();
const QString str = dateTime.toString(Qt::ISODateWithMs);
- res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(),
+ res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
int(str.size() * sizeof(ushort)),
SQLITE_TRANSIENT);
break;
@@ -527,7 +532,7 @@ bool QSQLiteResult::exec()
case QMetaType::QTime: {
const QTime time = value.toTime();
const QString str = time.toString(u"hh:mm:ss.zzz");
- res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(),
+ res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
int(str.size() * sizeof(ushort)),
SQLITE_TRANSIENT);
break;
@@ -535,14 +540,14 @@ bool QSQLiteResult::exec()
case QMetaType::QString: {
// lifetime of string == lifetime of its qvariant
const QString *str = static_cast<const QString*>(value.constData());
- res = sqlite3_bind_text16(d->stmt, i + 1, str->utf16(),
+ res = sqlite3_bind_text16(d->stmt, i + 1, str->unicode(),
int(str->size()) * sizeof(QChar),
SQLITE_STATIC);
break; }
default: {
- QString str = value.toString();
+ const QString str = value.toString();
// SQLITE_TRANSIENT makes sure that sqlite buffers the data
- res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(),
+ res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
int(str.size()) * sizeof(QChar),
SQLITE_TRANSIENT);
break; }
@@ -656,6 +661,30 @@ static void _q_regexp_cleanup(void *cache)
}
#endif
+static void _q_lower(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+ if (Q_UNLIKELY(argc != 1)) {
+ sqlite3_result_text(context, nullptr, 0, nullptr);
+ return;
+ }
+ const QString lower = QString::fromUtf8(
+ reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))).toLower();
+ const QByteArray ba = lower.toUtf8();
+ sqlite3_result_text(context, ba.data(), ba.size(), SQLITE_TRANSIENT);
+}
+
+static void _q_upper(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+ if (Q_UNLIKELY(argc != 1)) {
+ sqlite3_result_text(context, nullptr, 0, nullptr);
+ return;
+ }
+ const QString upper = QString::fromUtf8(
+ reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))).toUpper();
+ const QByteArray ba = upper.toUtf8();
+ sqlite3_result_text(context, ba.data(), ba.size(), SQLITE_TRANSIENT);
+}
+
QSQLiteDriver::QSQLiteDriver(QObject * parent)
: QSqlDriver(*new QSQLiteDriverPrivate, parent)
{
@@ -722,38 +751,47 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
bool openReadOnlyOption = false;
bool openUriOption = false;
bool useExtendedResultCodes = true;
+ bool useQtVfs = false;
+ bool useQtCaseFolding = false;
+ bool openNoFollow = false;
#if QT_CONFIG(regularexpression)
- static const QLatin1String regexpConnectOption = QLatin1String("QSQLITE_ENABLE_REGEXP");
+ static const auto regexpConnectOption = "QSQLITE_ENABLE_REGEXP"_L1;
bool defineRegexp = false;
int regexpCacheSize = 25;
#endif
- const auto opts = QStringView{conOpts}.split(QLatin1Char(';'));
+ const auto opts = QStringView{conOpts}.split(u';', Qt::SkipEmptyParts);
for (auto option : opts) {
option = option.trimmed();
- if (option.startsWith(QLatin1String("QSQLITE_BUSY_TIMEOUT"))) {
+ if (option.startsWith("QSQLITE_BUSY_TIMEOUT"_L1)) {
option = option.mid(20).trimmed();
- if (option.startsWith(QLatin1Char('='))) {
+ if (option.startsWith(u'=')) {
bool ok;
const int nt = option.mid(1).trimmed().toInt(&ok);
if (ok)
timeOut = nt;
}
- } else if (option == QLatin1String("QSQLITE_OPEN_READONLY")) {
+ } else if (option == "QSQLITE_USE_QT_VFS"_L1) {
+ useQtVfs = true;
+ } else if (option == "QSQLITE_OPEN_READONLY"_L1) {
openReadOnlyOption = true;
- } else if (option == QLatin1String("QSQLITE_OPEN_URI")) {
+ } else if (option == "QSQLITE_OPEN_URI"_L1) {
openUriOption = true;
- } else if (option == QLatin1String("QSQLITE_ENABLE_SHARED_CACHE")) {
+ } else if (option == "QSQLITE_ENABLE_SHARED_CACHE"_L1) {
sharedCache = true;
- } else if (option == QLatin1String("QSQLITE_NO_USE_EXTENDED_RESULT_CODES")) {
+ } else if (option == "QSQLITE_NO_USE_EXTENDED_RESULT_CODES"_L1) {
useExtendedResultCodes = false;
+ } else if (option == "QSQLITE_ENABLE_NON_ASCII_CASE_FOLDING"_L1) {
+ useQtCaseFolding = true;
+ } else if (option == "QSQLITE_OPEN_NOFOLLOW"_L1) {
+ openNoFollow = true;
}
#if QT_CONFIG(regularexpression)
else if (option.startsWith(regexpConnectOption)) {
option = option.mid(regexpConnectOption.size()).trimmed();
if (option.isEmpty()) {
defineRegexp = true;
- } else if (option.startsWith(QLatin1Char('='))) {
+ } else if (option.startsWith(u'=')) {
bool ok = false;
const int cacheSize = option.mid(1).trimmed().toInt(&ok);
if (ok) {
@@ -764,16 +802,25 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
}
}
#endif
+ else
+ qWarning("Unsupported option '%ls'", qUtf16Printable(option.toString()));
}
int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));
openMode |= (sharedCache ? SQLITE_OPEN_SHAREDCACHE : SQLITE_OPEN_PRIVATECACHE);
if (openUriOption)
openMode |= SQLITE_OPEN_URI;
+ if (openNoFollow) {
+#if defined(SQLITE_OPEN_NOFOLLOW)
+ openMode |= SQLITE_OPEN_NOFOLLOW;
+#else
+ qWarning("SQLITE_OPEN_NOFOLLOW not supported with the SQLite version %s", sqlite3_libversion());
+#endif
+ }
openMode |= SQLITE_OPEN_NOMUTEX;
- const int res = sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, nullptr);
+ const int res = sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, useQtVfs ? "QtVFS" : nullptr);
if (res == SQLITE_OK) {
sqlite3_busy_timeout(d->access, timeOut);
@@ -788,6 +835,12 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
nullptr, &_q_regexp_cleanup);
}
#endif
+ if (useQtCaseFolding) {
+ sqlite3_create_function_v2(d->access, "lower", 1, SQLITE_UTF8, nullptr,
+ &_q_lower, nullptr, nullptr, nullptr);
+ sqlite3_create_function_v2(d->access, "upper", 1, SQLITE_UTF8, nullptr,
+ &_q_upper, nullptr, nullptr, nullptr);
+ }
return true;
} else {
setLastError(qMakeError(d->access, tr("Error opening database"),
@@ -796,7 +849,7 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
if (d->access) {
sqlite3_close(d->access);
- d->access = 0;
+ d->access = nullptr;
}
return false;
@@ -807,10 +860,10 @@ void QSQLiteDriver::close()
{
Q_D(QSQLiteDriver);
if (isOpen()) {
- for (QSQLiteResult *result : qAsConst(d->results))
+ for (QSQLiteResult *result : std::as_const(d->results))
result->d_func()->finalize();
- if (d->access && (d->notificationid.count() > 0)) {
+ if (d->access && (d->notificationid.size() > 0)) {
d->notificationid.clear();
sqlite3_update_hook(d->access, nullptr, nullptr);
}
@@ -819,7 +872,7 @@ void QSQLiteDriver::close()
if (res != SQLITE_OK)
setLastError(qMakeError(d->access, tr("Error closing database"), QSqlError::ConnectionError, res));
- d->access = 0;
+ d->access = nullptr;
setOpen(false);
setOpenError(false);
}
@@ -836,7 +889,7 @@ bool QSQLiteDriver::beginTransaction()
return false;
QSqlQuery q(createResult());
- if (!q.exec(QLatin1String("BEGIN"))) {
+ if (!q.exec("BEGIN"_L1)) {
setLastError(QSqlError(tr("Unable to begin transaction"),
q.lastError().databaseText(), QSqlError::TransactionError));
return false;
@@ -851,7 +904,7 @@ bool QSQLiteDriver::commitTransaction()
return false;
QSqlQuery q(createResult());
- if (!q.exec(QLatin1String("COMMIT"))) {
+ if (!q.exec("COMMIT"_L1)) {
setLastError(QSqlError(tr("Unable to commit transaction"),
q.lastError().databaseText(), QSqlError::TransactionError));
return false;
@@ -866,7 +919,7 @@ bool QSQLiteDriver::rollbackTransaction()
return false;
QSqlQuery q(createResult());
- if (!q.exec(QLatin1String("ROLLBACK"))) {
+ if (!q.exec("ROLLBACK"_L1)) {
setLastError(QSqlError(tr("Unable to rollback transaction"),
q.lastError().databaseText(), QSqlError::TransactionError));
return false;
@@ -884,14 +937,14 @@ QStringList QSQLiteDriver::tables(QSql::TableType type) const
QSqlQuery q(createResult());
q.setForwardOnly(true);
- QString sql = QLatin1String("SELECT name FROM sqlite_master WHERE %1 "
- "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1");
+ QString sql = "SELECT name FROM sqlite_master WHERE %1 "
+ "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"_L1;
if ((type & QSql::Tables) && (type & QSql::Views))
- sql = sql.arg(QLatin1String("type='table' OR type='view'"));
+ sql = sql.arg("type='table' OR type='view'"_L1);
else if (type & QSql::Tables)
- sql = sql.arg(QLatin1String("type='table'"));
+ sql = sql.arg("type='table'"_L1);
else if (type & QSql::Views)
- sql = sql.arg(QLatin1String("type='view'"));
+ sql = sql.arg("type='view'"_L1);
else
sql.clear();
@@ -902,74 +955,32 @@ QStringList QSQLiteDriver::tables(QSql::TableType type) const
if (type & QSql::SystemTables) {
// there are no internal tables beside this one:
- res.append(QLatin1String("sqlite_master"));
+ res.append("sqlite_master"_L1);
}
return res;
}
-static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false)
-{
- QString schema;
- QString table(tableName);
- int indexOfSeparator = tableName.indexOf(QLatin1Char('.'));
- if (indexOfSeparator > -1) {
- schema = tableName.left(indexOfSeparator).append(QLatin1Char('.'));
- table = tableName.mid(indexOfSeparator + 1);
- }
- q.exec(QLatin1String("PRAGMA ") + schema + QLatin1String("table_info (") + _q_escapeIdentifier(table) + QLatin1Char(')'));
-
- QSqlIndex ind;
- while (q.next()) {
- bool isPk = q.value(5).toInt();
- if (onlyPIndex && !isPk)
- continue;
- QString typeName = q.value(2).toString().toLower();
- QString defVal = q.value(4).toString();
- if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\'')) {
- const int end = defVal.lastIndexOf(QLatin1Char('\''));
- if (end > 0)
- defVal = defVal.mid(1, end - 1);
- }
-
- QSqlField fld(q.value(1).toString(), QMetaType(qGetColumnType(typeName)), tableName);
- if (isPk && (typeName == QLatin1String("integer")))
- // INTEGER PRIMARY KEY fields are auto-generated in sqlite
- // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY!
- fld.setAutoValue(true);
- fld.setRequired(q.value(3).toInt() != 0);
- fld.setDefaultValue(defVal);
- ind.append(fld);
- }
- return ind;
-}
-
-QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const
+QSqlIndex QSQLiteDriver::primaryIndex(const QString &tablename) const
{
+ Q_D(const QSQLiteDriver);
if (!isOpen())
return QSqlIndex();
- QString table = tblname;
- if (isIdentifierEscaped(table, QSqlDriver::TableName))
- table = stripDelimiters(table, QSqlDriver::TableName);
-
QSqlQuery q(createResult());
q.setForwardOnly(true);
- return qGetTableInfo(q, table, true);
+ return d->getTableInfo(q, tablename, true);
}
-QSqlRecord QSQLiteDriver::record(const QString &tbl) const
+QSqlRecord QSQLiteDriver::record(const QString &tablename) const
{
+ Q_D(const QSQLiteDriver);
if (!isOpen())
return QSqlRecord();
- QString table = tbl;
- if (isIdentifierEscaped(table, QSqlDriver::TableName))
- table = stripDelimiters(table, QSqlDriver::TableName);
-
QSqlQuery q(createResult());
q.setForwardOnly(true);
- return qGetTableInfo(q, table);
+ return d->getTableInfo(q, tablename);
}
QVariant QSQLiteDriver::handle() const
@@ -980,8 +991,52 @@ QVariant QSQLiteDriver::handle() const
QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
{
+ Q_D(const QSQLiteDriver);
+ if (identifier.isEmpty() || isIdentifierEscaped(identifier, type))
+ return identifier;
+
+ const auto indexOfSeparator = identifier.indexOf(u'.');
+ if (indexOfSeparator > -1) {
+ auto leftName = QStringView{identifier}.first(indexOfSeparator);
+ auto rightName = QStringView{identifier}.sliced(indexOfSeparator + 1);
+ const QStringView leftEnclose = d->isIdentifierEscaped(leftName) ? u"" : u"\"";
+ const QStringView rightEnclose = d->isIdentifierEscaped(rightName) ? u"" : u"\"";
+ if (leftEnclose.isEmpty() || rightEnclose.isEmpty())
+ return (leftEnclose + leftName + leftEnclose + u'.' + rightEnclose + rightName
+ + rightEnclose);
+ }
+ return u'"' + identifier + u'"';
+}
+
+bool QSQLiteDriver::isIdentifierEscaped(const QString &identifier, IdentifierType type) const
+{
+ Q_D(const QSQLiteDriver);
Q_UNUSED(type);
- return _q_escapeIdentifier(identifier);
+ return d->isIdentifierEscaped(QStringView{identifier});
+}
+
+QString QSQLiteDriver::stripDelimiters(const QString &identifier, IdentifierType type) const
+{
+ Q_D(const QSQLiteDriver);
+ const auto indexOfSeparator = identifier.indexOf(u'.');
+ if (indexOfSeparator > -1) {
+ auto leftName = QStringView{identifier}.first(indexOfSeparator);
+ auto rightName = QStringView{identifier}.sliced(indexOfSeparator + 1);
+ const auto leftEscaped = d->isIdentifierEscaped(leftName);
+ const auto rightEscaped = d->isIdentifierEscaped(rightName);
+ if (leftEscaped || rightEscaped) {
+ if (leftEscaped)
+ leftName = leftName.sliced(1).chopped(1);
+ if (rightEscaped)
+ rightName = rightName.sliced(1).chopped(1);
+ return leftName + u'.' + rightName;
+ }
+ }
+
+ if (isIdentifierEscaped(identifier, type))
+ return identifier.mid(1, identifier.size() - 2);
+
+ return identifier;
}
static void handle_sqlite_callback(void *qobj,int aoperation, char const *adbname, char const *atablename,
@@ -1005,13 +1060,13 @@ bool QSQLiteDriver::subscribeToNotification(const QString &name)
}
if (d->notificationid.contains(name)) {
- qWarning("Already subscribing to '%s'.", qPrintable(name));
+ qWarning("Already subscribing to '%ls'.", qUtf16Printable(name));
return false;
}
//sqlite supports only one notification callback, so only the first is registered
d->notificationid << name;
- if (d->notificationid.count() == 1)
+ if (d->notificationid.size() == 1)
sqlite3_update_hook(d->access, &handle_sqlite_callback, reinterpret_cast<void *> (this));
return true;
@@ -1026,7 +1081,7 @@ bool QSQLiteDriver::unsubscribeFromNotification(const QString &name)
}
if (!d->notificationid.contains(name)) {
- qWarning("Not subscribed to '%s'.", qPrintable(name));
+ qWarning("Not subscribed to '%ls'.", qUtf16Printable(name));
return false;
}
@@ -1051,3 +1106,5 @@ void QSQLiteDriver::handleNotification(const QString &tableName, qint64 rowid)
}
QT_END_NAMESPACE
+
+#include "moc_qsql_sqlite_p.cpp"
diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h b/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h
index c7952bca9a..db6b76cb69 100644
--- a/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h
+++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSQL_SQLITE_H
#define QSQL_SQLITE_H
@@ -90,9 +54,12 @@ public:
QStringList tables(QSql::TableType) const override;
QSqlRecord record(const QString& tablename) const override;
- QSqlIndex primaryIndex(const QString &table) const override;
+ QSqlIndex primaryIndex(const QString &tablename) const override;
QVariant handle() const override;
- QString escapeIdentifier(const QString &identifier, IdentifierType) const override;
+
+ QString escapeIdentifier(const QString &identifier, IdentifierType type) const override;
+ bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const override;
+ QString stripDelimiters(const QString &identifier, IdentifierType type) const override;
bool subscribeToNotification(const QString &name) override;
bool unsubscribeFromNotification(const QString &name) override;
diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite_vfs.cpp b/src/plugins/sqldrivers/sqlite/qsql_sqlite_vfs.cpp
new file mode 100644
index 0000000000..bbba3cd14f
--- /dev/null
+++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite_vfs.cpp
@@ -0,0 +1,258 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsql_sqlite_vfs_p.h"
+
+#include <QFile>
+
+#include <limits.h> // defines PATH_MAX on unix
+#include <sqlite3.h>
+#include <stdio.h> // defines FILENAME_MAX everywhere
+
+#ifndef PATH_MAX
+# define PATH_MAX FILENAME_MAX
+#endif
+#if SQLITE_VERSION_NUMBER < 3040000
+typedef const char *sqlite3_filename;
+#endif
+
+namespace {
+struct Vfs : sqlite3_vfs {
+ sqlite3_vfs *pVfs;
+ sqlite3_io_methods ioMethods;
+};
+
+struct File : sqlite3_file {
+ class QtFile : public QFile {
+ public:
+ QtFile(const QString &name, bool removeOnClose)
+ : QFile(name)
+ , removeOnClose(removeOnClose)
+ {}
+
+ ~QtFile() override
+ {
+ if (removeOnClose)
+ remove();
+ }
+ private:
+ bool removeOnClose;
+ };
+ QtFile *pFile;
+};
+
+
+int xClose(sqlite3_file *sfile)
+{
+ auto file = static_cast<File *>(sfile);
+ delete file->pFile;
+ file->pFile = nullptr;
+ return SQLITE_OK;
+}
+
+int xRead(sqlite3_file *sfile, void *ptr, int iAmt, sqlite3_int64 iOfst)
+{
+ auto file = static_cast<File *>(sfile);
+ if (!file->pFile->seek(iOfst))
+ return SQLITE_IOERR_READ;
+
+ auto sz = file->pFile->read(static_cast<char *>(ptr), iAmt);
+ if (sz < iAmt) {
+ memset(static_cast<char *>(ptr) + sz, 0, size_t(iAmt - sz));
+ return SQLITE_IOERR_SHORT_READ;
+ }
+ return SQLITE_OK;
+}
+
+int xWrite(sqlite3_file *sfile, const void *data, int iAmt, sqlite3_int64 iOfst)
+{
+ auto file = static_cast<File *>(sfile);
+ if (!file->pFile->seek(iOfst))
+ return SQLITE_IOERR_SEEK;
+ return file->pFile->write(reinterpret_cast<const char*>(data), iAmt) == iAmt ? SQLITE_OK : SQLITE_IOERR_WRITE;
+}
+
+int xTruncate(sqlite3_file *sfile, sqlite3_int64 size)
+{
+ auto file = static_cast<File *>(sfile);
+ return file->pFile->resize(size) ? SQLITE_OK : SQLITE_IOERR_TRUNCATE;
+}
+
+int xSync(sqlite3_file *sfile, int /*flags*/)
+{
+ static_cast<File *>(sfile)->pFile->flush();
+ return SQLITE_OK;
+}
+
+int xFileSize(sqlite3_file *sfile, sqlite3_int64 *pSize)
+{
+ auto file = static_cast<File *>(sfile);
+ *pSize = file->pFile->size();
+ return SQLITE_OK;
+}
+
+// No lock/unlock for QFile, QLockFile doesn't work for me
+
+int xLock(sqlite3_file *, int) { return SQLITE_OK; }
+
+int xUnlock(sqlite3_file *, int) { return SQLITE_OK; }
+
+int xCheckReservedLock(sqlite3_file *, int *pResOut)
+{
+ *pResOut = 0;
+ return SQLITE_OK;
+}
+
+int xFileControl(sqlite3_file *, int, void *) { return SQLITE_NOTFOUND; }
+
+int xSectorSize(sqlite3_file *)
+{
+ return 4096;
+}
+
+int xDeviceCharacteristics(sqlite3_file *)
+{
+ return 0; // no SQLITE_IOCAP_XXX
+}
+
+int xOpen(sqlite3_vfs *svfs, sqlite3_filename zName, sqlite3_file *sfile,
+ int flags, int *pOutFlags)
+{
+ auto vfs = static_cast<Vfs *>(svfs);
+ auto file = static_cast<File *>(sfile);
+ memset(file, 0, sizeof(File));
+ QIODeviceBase::OpenMode mode = QIODeviceBase::NotOpen;
+ if (!zName || (flags & SQLITE_OPEN_MEMORY))
+ return SQLITE_PERM;
+ if ((flags & SQLITE_OPEN_READONLY) &&
+ !(flags & SQLITE_OPEN_READWRITE) &&
+ !(flags & SQLITE_OPEN_CREATE) &&
+ !(flags & SQLITE_OPEN_DELETEONCLOSE)) {
+ mode |= QIODeviceBase::OpenModeFlag::ReadOnly;
+ } else {
+ /*
+ ** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction
+ ** with the [SQLITE_OPEN_CREATE] flag, which are both directly
+ ** analogous to the O_EXCL and O_CREAT flags of the POSIX open()
+ ** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the
+ ** SQLITE_OPEN_CREATE, is used to indicate that file should always
+ ** be created, and that it is an error if it already exists.
+ ** It is <i>not</i> used to indicate the file should be opened
+ ** for exclusive access.
+ */
+ if ((flags & SQLITE_OPEN_CREATE) && (flags & SQLITE_OPEN_EXCLUSIVE))
+ mode |= QIODeviceBase::OpenModeFlag::NewOnly;
+
+ if (flags & SQLITE_OPEN_READWRITE)
+ mode |= QIODeviceBase::OpenModeFlag::ReadWrite;
+ }
+
+ file->pMethods = &vfs->ioMethods;
+ file->pFile = new File::QtFile(QString::fromUtf8(zName), bool(flags & SQLITE_OPEN_DELETEONCLOSE));
+ if (!file->pFile->open(mode))
+ return SQLITE_CANTOPEN;
+ if (pOutFlags)
+ *pOutFlags = flags;
+
+ return SQLITE_OK;
+}
+
+int xDelete(sqlite3_vfs *, const char *zName, int)
+{
+ return QFile::remove(QString::fromUtf8(zName)) ? SQLITE_OK : SQLITE_ERROR;
+}
+
+int xAccess(sqlite3_vfs */*svfs*/, const char *zName, int flags, int *pResOut)
+{
+ *pResOut = 0;
+ switch (flags) {
+ case SQLITE_ACCESS_EXISTS:
+ case SQLITE_ACCESS_READ:
+ *pResOut = QFile::exists(QString::fromUtf8(zName));
+ break;
+ default:
+ break;
+ }
+ return SQLITE_OK;
+}
+
+int xFullPathname(sqlite3_vfs *, const char *zName, int nOut, char *zOut)
+{
+ if (!zName)
+ return SQLITE_ERROR;
+
+ int i = 0;
+ for (;zName[i] && i < nOut; ++i)
+ zOut[i] = zName[i];
+
+ if (i >= nOut)
+ return SQLITE_ERROR;
+
+ zOut[i] = '\0';
+ return SQLITE_OK;
+}
+
+int xRandomness(sqlite3_vfs *svfs, int nByte, char *zOut)
+{
+ auto vfs = static_cast<Vfs *>(svfs)->pVfs;
+ return vfs->xRandomness(vfs, nByte, zOut);
+}
+
+int xSleep(sqlite3_vfs *svfs, int microseconds)
+{
+ auto vfs = static_cast<Vfs *>(svfs)->pVfs;
+ return vfs->xSleep(vfs, microseconds);
+}
+
+int xCurrentTime(sqlite3_vfs *svfs, double *zOut)
+{
+ auto vfs = static_cast<Vfs *>(svfs)->pVfs;
+ return vfs->xCurrentTime(vfs, zOut);
+}
+
+int xGetLastError(sqlite3_vfs *, int, char *)
+{
+ return 0;
+}
+
+int xCurrentTimeInt64(sqlite3_vfs *svfs, sqlite3_int64 *zOut)
+{
+ auto vfs = static_cast<Vfs *>(svfs)->pVfs;
+ return vfs->xCurrentTimeInt64(vfs, zOut);
+}
+} // namespace {
+
+void register_qt_vfs()
+{
+ static Vfs vfs;
+ memset(&vfs, 0, sizeof(Vfs));
+ vfs.iVersion = 1;
+ vfs.szOsFile = sizeof(File);
+ vfs.mxPathname = PATH_MAX;
+ vfs.zName = "QtVFS";
+ vfs.xOpen = &xOpen;
+ vfs.xDelete = &xDelete;
+ vfs.xAccess = &xAccess;
+ vfs.xFullPathname = &xFullPathname;
+ vfs.xRandomness = &xRandomness;
+ vfs.xSleep = &xSleep;
+ vfs.xCurrentTime = &xCurrentTime;
+ vfs.xGetLastError = &xGetLastError;
+ vfs.xCurrentTimeInt64 = &xCurrentTimeInt64;
+ vfs.pVfs = sqlite3_vfs_find(nullptr);
+ vfs.ioMethods.iVersion = 1;
+ vfs.ioMethods.xClose = &xClose;
+ vfs.ioMethods.xRead = &xRead;
+ vfs.ioMethods.xWrite = &xWrite;
+ vfs.ioMethods.xTruncate = &xTruncate;
+ vfs.ioMethods.xSync = &xSync;
+ vfs.ioMethods.xFileSize = &xFileSize;
+ vfs.ioMethods.xLock = &xLock;
+ vfs.ioMethods.xUnlock = &xUnlock;
+ vfs.ioMethods.xCheckReservedLock = &xCheckReservedLock;
+ vfs.ioMethods.xFileControl = &xFileControl;
+ vfs.ioMethods.xSectorSize = &xSectorSize;
+ vfs.ioMethods.xDeviceCharacteristics = &xDeviceCharacteristics;
+
+ sqlite3_vfs_register(&vfs, 0);
+}
diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite_vfs_p.h b/src/plugins/sqldrivers/sqlite/qsql_sqlite_vfs_p.h
new file mode 100644
index 0000000000..56024b3ecb
--- /dev/null
+++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite_vfs_p.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSQL_SQLITE_VFS_H
+#define QSQL_SQLITE_VFS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+void register_qt_vfs();
+
+
+#endif // QSQL_SQLITE_VFS_H
diff --git a/src/plugins/sqldrivers/sqlite/smain.cpp b/src/plugins/sqldrivers/sqlite/smain.cpp
index 092813990c..0d201c38d3 100644
--- a/src/plugins/sqldrivers/sqlite/smain.cpp
+++ b/src/plugins/sqldrivers/sqlite/smain.cpp
@@ -1,48 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qsqldriverplugin.h>
#include <qstringlist.h>
#include "qsql_sqlite_p.h"
+#include "qsql_sqlite_vfs_p.h"
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QSQLiteDriverPlugin : public QSqlDriverPlugin
{
Q_OBJECT
@@ -57,15 +24,17 @@ public:
QSQLiteDriverPlugin::QSQLiteDriverPlugin()
: QSqlDriverPlugin()
{
+ register_qt_vfs();
}
QSqlDriver* QSQLiteDriverPlugin::create(const QString &name)
{
- if (name == QLatin1String("QSQLITE")) {
+ if (name == "QSQLITE"_L1) {
QSQLiteDriver* driver = new QSQLiteDriver();
return driver;
}
- return 0;
+
+ return nullptr;
}
QT_END_NAMESPACE