Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsql_odbc.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsql_odbc_p.h"
5#include <qsqlrecord.h>
6
7#if defined (Q_OS_WIN32)
8#include <qt_windows.h>
9#endif
10#include <qcoreapplication.h>
11#include <qdatetime.h>
12#include <qlist.h>
13#include <qloggingcategory.h>
14#include <qmath.h>
15#include <qsqlerror.h>
16#include <qsqlfield.h>
17#include <qsqlindex.h>
18#include <qstringconverter.h>
19#include <qstringlist.h>
20#include <qvariant.h>
21#include <qvarlengtharray.h>
22#include <QDebug>
23#include <QSqlQuery>
24#include <QtSql/private/qsqldriver_p.h>
25#include <QtSql/private/qsqlresult_p.h>
26#include "private/qtools_p.h"
27
29
30static Q_LOGGING_CATEGORY(lcOdbc, "qt.sql.odbc")
31
32using namespace Qt::StringLiterals;
33
34// non-standard ODBC SQL data type from SQL Server sometimes used instead of SQL_TIME
35#ifndef SQL_SS_TIME2
36#define SQL_SS_TIME2 (-154)
37#endif
38
39// undefine this to prevent initial check of the ODBC driver
40#define ODBC_CHECK_DRIVER
41
42static constexpr int COLNAMESIZE = 256;
43static constexpr SQLSMALLINT TABLENAMESIZE = 128;
44//Map Qt parameter types to ODBC types
45static constexpr SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
46
48{
49public:
50 SqlStmtHandle(SQLHANDLE hDbc)
51 {
52 SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &stmtHandle);
53 }
55 {
56 if (stmtHandle != SQL_NULL_HSTMT)
57 SQLFreeHandle(SQL_HANDLE_STMT, stmtHandle);
58 }
59 SQLHANDLE handle() const
60 {
61 return stmtHandle;
62 }
63 bool isValid() const
64 {
65 return stmtHandle != SQL_NULL_HSTMT;
66 }
67 SQLHANDLE stmtHandle = SQL_NULL_HSTMT;
68};
69
70template<typename C, int SIZE = sizeof(SQLTCHAR)>
71inline static QString fromSQLTCHAR(const C &input, qsizetype size = -1)
72{
73 // Remove any trailing \0 as some drivers misguidedly append one
74 qsizetype realsize = qMin(size, input.size());
75 if (realsize > 0 && input[realsize - 1] == 0)
76 realsize--;
77 if constexpr (SIZE == 1)
78 return QString::fromUtf8(reinterpret_cast<const char *>(input.constData()), realsize);
79 else if constexpr (SIZE == 2)
80 return QString::fromUtf16(reinterpret_cast<const char16_t *>(input.constData()), realsize);
81 else if constexpr (SIZE == 4)
82 return QString::fromUcs4(reinterpret_cast<const char32_t *>(input.constData()), realsize);
83 else
85 "Don't know how to handle sizeof(SQLTCHAR) != 1/2/4");
86}
87
88template<int SIZE = sizeof(SQLTCHAR)>
90{
91 if constexpr (SIZE == 1)
93 else if constexpr (SIZE == 2)
95 else if constexpr (SIZE == 4)
97 else
99 "Don't know how to handle sizeof(SQLTCHAR) != 1/2/4");
100}
101
102inline static QVarLengthArray<SQLTCHAR> toSQLTCHAR(QStringView input)
103{
104 QVarLengthArray<SQLTCHAR> result;
106 result.resize(enc.requiredSpace(input.size()));
107 const auto end = enc.appendToBuffer(reinterpret_cast<char *>(result.data()), input);
108 result.resize((end - reinterpret_cast<char *>(result.data())) / sizeof(SQLTCHAR));
109 return result;
110}
111
113{
114 Q_DECLARE_PUBLIC(QODBCDriver)
115
116public:
119
120 SQLHANDLE hEnv = nullptr;
121 SQLHANDLE hDbc = nullptr;
122
125 bool unicode = false;
126 bool useSchema = false;
127 bool isFreeTDSDriver = false;
128 bool hasSQLFetchScroll = true;
129 bool hasMultiResultSets = false;
130
131 bool checkDriver() const;
132 void checkUnicode();
133 void checkDBMS();
136 void checkSchemaUsage();
138 void checkDefaultCase();
139 bool setConnectionOptions(const QString& connOpts);
140 void splitTableQualifier(const QString &qualifier, QString &catalog,
141 QString &schema, QString &table) const;
142 QString adjustCase(const QString&) const;
144 SQLRETURN sqlFetchNext(const SqlStmtHandle &hStmt) const;
145 SQLRETURN sqlFetchNext(SQLHANDLE hStmt) const;
146private:
147 bool isQuoteInitialized = false;
148 QChar quote = u'"';
149 DefaultCase m_defaultCase = DefaultCase::Mixed;
150};
151
153
155{
156 Q_DECLARE_PRIVATE(QODBCResult)
157
158public:
159 QODBCResult(const QODBCDriver *db);
160 virtual ~QODBCResult();
161
162 bool prepare(const QString &query) override;
163 bool exec() override;
164
165 QVariant lastInsertId() const override;
166 QVariant handle() const override;
167
168protected:
169 bool fetchNext() override;
170 bool fetchFirst() override;
171 bool fetchLast() override;
172 bool fetchPrevious() override;
173 bool fetch(int i) override;
174 bool reset(const QString &query) override;
175 QVariant data(int field) override;
176 bool isNull(int field) override;
177 int size() override;
178 int numRowsAffected() override;
179 QSqlRecord record() const override;
180 void virtual_hook(int id, void *data) override;
181 void detachFromResultSet() override;
182 bool nextResult() override;
183};
184
186{
187 Q_DECLARE_PUBLIC(QODBCResult)
188
189public:
193 {
194 unicode = drv_d_func()->unicode;
195 useSchema = drv_d_func()->useSchema;
196 disconnectCount = drv_d_func()->disconnectCount;
197 hasSQLFetchScroll = drv_d_func()->hasSQLFetchScroll;
198 }
199
200 inline void clearValues()
202
203 SQLHANDLE dpEnv() const { return drv_d_func() ? drv_d_func()->hEnv : 0;}
204 SQLHANDLE dpDbc() const { return drv_d_func() ? drv_d_func()->hDbc : 0;}
205 SQLHANDLE hStmt = nullptr;
206
211 bool hasSQLFetchScroll = true;
212 bool unicode = false;
213 bool useSchema = false;
214
215 bool isStmtHandleValid() const;
217};
218
220{
221 return drv_d_func() && disconnectCount == drv_d_func()->disconnectCount;
222}
223
225{
226 disconnectCount = drv_d_func() ? drv_d_func()->disconnectCount : 0;
227}
228
235static QList<DiagRecord> qWarnODBCHandle(int handleType, SQLHANDLE handle)
236{
237 SQLINTEGER nativeCode = 0;
238 SQLSMALLINT msgLen = 0;
239 SQLSMALLINT i = 1;
240 SQLRETURN r = SQL_NO_DATA;
241 QVarLengthArray<SQLTCHAR, SQL_SQLSTATE_SIZE + 1> state(SQL_SQLSTATE_SIZE + 1);
242 QVarLengthArray<SQLTCHAR, SQL_MAX_MESSAGE_LENGTH + 1> description(SQL_MAX_MESSAGE_LENGTH + 1);
243 QList<DiagRecord> result;
244
245 if (!handle)
246 return result;
247 do {
248 r = SQLGetDiagRec(handleType,
249 handle,
250 i,
251 state.data(),
252 &nativeCode,
253 description.data(),
254 description.size(),
255 &msgLen);
256 if (msgLen >= description.size()) {
257 description.resize(msgLen + 1); // incl. \0 termination
258 continue;
259 }
260 if (SQL_SUCCEEDED(r)) {
261 result.push_back({fromSQLTCHAR(description, msgLen),
263 QString::number(nativeCode)});
264 } else if (r == SQL_ERROR || r == SQL_INVALID_HANDLE) {
265 break;
266 }
267 ++i;
268 } while (r != SQL_NO_DATA);
269 return result;
270}
271
272static QList<DiagRecord> qODBCWarn(const SQLHANDLE hStmt,
273 const SQLHANDLE envHandle = nullptr,
274 const SQLHANDLE pDbC = nullptr)
275{
276 QList<DiagRecord> result;
277 result.append(qWarnODBCHandle(SQL_HANDLE_ENV, envHandle));
278 result.append(qWarnODBCHandle(SQL_HANDLE_DBC, pDbC));
279 result.append(qWarnODBCHandle(SQL_HANDLE_STMT, hStmt));
280 return result;
281}
282
283static QList<DiagRecord> qODBCWarn(const QODBCResultPrivate *odbc)
284{
285 return qODBCWarn(odbc->hStmt, odbc->dpEnv(), odbc->dpDbc());
286}
287
288static QList<DiagRecord> qODBCWarn(const QODBCDriverPrivate *odbc)
289{
290 return qODBCWarn(nullptr, odbc->hEnv, odbc->hDbc);
291}
292
293static DiagRecord combineRecords(const QList<DiagRecord> &records)
294{
295 const auto add = [](const DiagRecord &a, const DiagRecord &b) {
296 return DiagRecord{a.description + u' ' + b.description,
297 a.sqlState + u';' + b.sqlState,
298 a.errorCode + u';' + b.errorCode};
299 };
300 if (records.isEmpty())
301 return {};
302 return std::accumulate(std::next(records.begin()), records.end(), records.front(), add);
303}
304
307 const QList<DiagRecord> &records)
308{
309 if (records.empty())
310 return QSqlError("QODBC: unknown error"_L1, {}, type, {});
311 const auto combined = combineRecords(records);
312 return QSqlError("QODBC: "_L1 + err, combined.description + ", "_L1 + combined.sqlState, type,
313 combined.errorCode);
314}
315
316static QString errorStringFromDiagRecords(const QList<DiagRecord>& records)
317{
318 const auto combined = combineRecords(records);
319 return combined.description;
320}
321
322template<class T>
323static void qSqlWarning(const QString &message, T &&val)
324{
325 const auto addMsg = errorStringFromDiagRecords(qODBCWarn(val));
326 if (addMsg.isEmpty())
327 qCWarning(lcOdbc) << message;
328 else
329 qCWarning(lcOdbc) << message << "\tError:" << addMsg;
330}
331
332static QSqlError qMakeError(const QString &err,
334 const QODBCResultPrivate *p)
335{
336 return errorFromDiagRecords(err, type, qODBCWarn(p));
337}
338
339static QSqlError qMakeError(const QString &err,
341 const QODBCDriverPrivate *p)
342{
343 return errorFromDiagRecords(err, type, qODBCWarn(p));
344}
345
346static QMetaType qDecodeODBCType(SQLSMALLINT sqltype, bool isSigned = true)
347{
349 switch (sqltype) {
350 case SQL_DECIMAL:
351 case SQL_NUMERIC:
352 case SQL_FLOAT: // 24 or 53 bits precision
353 case SQL_DOUBLE:// 53 bits
354 type = QMetaType::Double;
355 break;
356 case SQL_REAL: // 24 bits
357 type = QMetaType::Float;
358 break;
359 case SQL_SMALLINT:
360 type = isSigned ? QMetaType::Short : QMetaType::UShort;
361 break;
362 case SQL_INTEGER:
363 case SQL_BIT:
364 type = isSigned ? QMetaType::Int : QMetaType::UInt;
365 break;
366 case SQL_TINYINT:
367 type = QMetaType::UInt;
368 break;
369 case SQL_BIGINT:
370 type = isSigned ? QMetaType::LongLong : QMetaType::ULongLong;
371 break;
372 case SQL_BINARY:
373 case SQL_VARBINARY:
374 case SQL_LONGVARBINARY:
375 type = QMetaType::QByteArray;
376 break;
377 case SQL_DATE:
378 case SQL_TYPE_DATE:
379 type = QMetaType::QDate;
380 break;
381 case SQL_SS_TIME2:
382 case SQL_TIME:
383 case SQL_TYPE_TIME:
384 type = QMetaType::QTime;
385 break;
386 case SQL_TIMESTAMP:
387 case SQL_TYPE_TIMESTAMP:
388 type = QMetaType::QDateTime;
389 break;
390 case SQL_WCHAR:
391 case SQL_WVARCHAR:
392 case SQL_WLONGVARCHAR:
393 type = QMetaType::QString;
394 break;
395 case SQL_CHAR:
396 case SQL_VARCHAR:
397#if (ODBCVER >= 0x0350)
398 case SQL_GUID:
399#endif
400 case SQL_LONGVARCHAR:
401 type = QMetaType::QString;
402 break;
403 default:
404 type = QMetaType::QByteArray;
405 break;
406 }
407 return QMetaType(type);
408}
409
410template <typename CT>
411static QVariant getStringDataImpl(SQLHANDLE hStmt, SQLUSMALLINT column, qsizetype colSize, SQLSMALLINT targetType)
412{
413 QString fieldVal;
414 SQLRETURN r = SQL_ERROR;
415 SQLLEN lengthIndicator = 0;
416 QVarLengthArray<CT> buf(colSize);
417 while (true) {
418 r = SQLGetData(hStmt,
419 column + 1,
420 targetType,
421 SQLPOINTER(buf.data()), SQLINTEGER(buf.size() * sizeof(CT)),
422 &lengthIndicator);
423 if (SQL_SUCCEEDED(r)) {
424 if (lengthIndicator == SQL_NULL_DATA) {
425 return {};
426 }
427 // starting with ODBC Native Client 2012, SQL_NO_TOTAL is returned
428 // instead of the length (which sometimes was wrong in older versions)
429 // see link for more info: http://msdn.microsoft.com/en-us/library/jj219209.aspx
430 // if length indicator equals SQL_NO_TOTAL, indicating that
431 // more data can be fetched, but size not known, collect data
432 // and fetch next block
433 if (lengthIndicator == SQL_NO_TOTAL) {
434 fieldVal += fromSQLTCHAR<QVarLengthArray<CT>, sizeof(CT)>(buf, buf.size());
435 continue;
436 }
437 // if SQL_SUCCESS_WITH_INFO is returned, indicating that
438 // more data can be fetched, the length indicator does NOT
439 // contain the number of bytes returned - it contains the
440 // total number of bytes that CAN be fetched
441 const qsizetype rSize = (r == SQL_SUCCESS_WITH_INFO)
442 ? buf.size()
443 : qsizetype(lengthIndicator / sizeof(CT));
444 fieldVal += fromSQLTCHAR<QVarLengthArray<CT>, sizeof(CT)>(buf, rSize);
445 // lengthIndicator does not contain the termination character
446 if (lengthIndicator < SQLLEN((buf.size() - 1) * sizeof(CT))) {
447 // workaround for Drivermanagers that don't return SQL_NO_DATA
448 break;
449 }
450 } else if (r == SQL_NO_DATA) {
451 break;
452 } else {
453 qSqlWarning("QODBC::getStringData: Error while fetching data"_L1, hStmt);
454 return {};
455 }
456 }
457 return fieldVal;
458}
459
460static QVariant qGetStringData(SQLHANDLE hStmt, SQLUSMALLINT column, int colSize, bool unicode)
461{
462 if (colSize <= 0) {
463 colSize = 256; // default Prealloc size of QVarLengthArray
464 } else if (colSize > 65536) { // limit buffer size to 64 KB
465 colSize = 65536;
466 } else {
467 colSize++; // make sure there is room for more than the 0 termination
468 }
469 return unicode ? getStringDataImpl<SQLTCHAR>(hStmt, column, colSize, SQL_C_TCHAR)
470 : getStringDataImpl<SQLCHAR>(hStmt, column, colSize, SQL_C_CHAR);
471}
472
473static QVariant qGetBinaryData(SQLHANDLE hStmt, int column)
474{
475 QByteArray fieldVal;
476 SQLSMALLINT colNameLen;
477 SQLSMALLINT colType;
478 SQLULEN colSize;
479 SQLSMALLINT colScale;
480 SQLSMALLINT nullable;
481 SQLLEN lengthIndicator = 0;
482 SQLRETURN r = SQL_ERROR;
483
484 QVarLengthArray<SQLTCHAR, COLNAMESIZE> colName(COLNAMESIZE);
485
486 r = SQLDescribeCol(hStmt,
487 column + 1,
488 colName.data(), SQLSMALLINT(colName.size()),
489 &colNameLen,
490 &colType,
491 &colSize,
492 &colScale,
493 &nullable);
494 if (r != SQL_SUCCESS)
495 qSqlWarning(("QODBC::qGetBinaryData: Unable to describe column %1"_L1)
496 .arg(QString::number(column)), hStmt);
497 // SQLDescribeCol may return 0 if size cannot be determined
498 if (!colSize)
499 colSize = 255;
500 else if (colSize > 65536) // read the field in 64 KB chunks
501 colSize = 65536;
502 fieldVal.resize(colSize);
503 ulong read = 0;
504 while (true) {
505 r = SQLGetData(hStmt,
506 column+1,
507 SQL_C_BINARY,
508 const_cast<char *>(fieldVal.constData() + read),
509 colSize,
510 &lengthIndicator);
511 if (!SQL_SUCCEEDED(r))
512 break;
513 if (lengthIndicator == SQL_NULL_DATA)
514 return QVariant(QMetaType(QMetaType::QByteArray));
515 if (lengthIndicator > SQLLEN(colSize) || lengthIndicator == SQL_NO_TOTAL) {
516 read += colSize;
517 colSize = 65536;
518 } else {
519 read += lengthIndicator;
520 }
521 if (r == SQL_SUCCESS) { // the whole field was read in one chunk
522 fieldVal.resize(read);
523 break;
524 }
525 fieldVal.resize(fieldVal.size() + colSize);
526 }
527 return fieldVal;
528}
529
530static QVariant qGetIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
531{
532 SQLINTEGER intbuf = 0;
533 SQLLEN lengthIndicator = 0;
534 SQLRETURN r = SQLGetData(hStmt,
535 column+1,
536 isSigned ? SQL_C_SLONG : SQL_C_ULONG,
537 (SQLPOINTER)&intbuf,
538 sizeof(intbuf),
539 &lengthIndicator);
540 if (!SQL_SUCCEEDED(r))
541 return QVariant();
542 if (lengthIndicator == SQL_NULL_DATA)
543 return QVariant(QMetaType::fromType<int>());
544 if (isSigned)
545 return int(intbuf);
546 else
547 return uint(intbuf);
548}
549
550static QVariant qGetDoubleData(SQLHANDLE hStmt, int column)
551{
552 SQLDOUBLE dblbuf;
553 SQLLEN lengthIndicator = 0;
554 SQLRETURN r = SQLGetData(hStmt,
555 column+1,
556 SQL_C_DOUBLE,
557 (SQLPOINTER) &dblbuf,
558 0,
559 &lengthIndicator);
560 if (!SQL_SUCCEEDED(r)) {
561 return QVariant();
562 }
563 if (lengthIndicator == SQL_NULL_DATA)
564 return QVariant(QMetaType::fromType<double>());
565
566 return (double) dblbuf;
567}
568
569
570static QVariant qGetBigIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
571{
572 SQLBIGINT lngbuf = 0;
573 SQLLEN lengthIndicator = 0;
574 SQLRETURN r = SQLGetData(hStmt,
575 column+1,
576 isSigned ? SQL_C_SBIGINT : SQL_C_UBIGINT,
577 (SQLPOINTER) &lngbuf,
578 sizeof(lngbuf),
579 &lengthIndicator);
580 if (!SQL_SUCCEEDED(r))
581 return QVariant();
582 if (lengthIndicator == SQL_NULL_DATA)
583 return QVariant(QMetaType::fromType<qlonglong>());
584
585 if (isSigned)
586 return qint64(lngbuf);
587 else
588 return quint64(lngbuf);
589}
590
591static bool isAutoValue(const SQLHANDLE hStmt, int column)
592{
593 SQLLEN nNumericAttribute = 0; // Check for auto-increment
594 const SQLRETURN r = ::SQLColAttribute(hStmt, column + 1, SQL_DESC_AUTO_UNIQUE_VALUE,
595 0, 0, 0, &nNumericAttribute);
596 if (!SQL_SUCCEEDED(r)) {
597 qSqlWarning(("QODBC::isAutoValue: Unable to get autovalue attribute for column %1"_L1)
598 .arg(QString::number(column)), hStmt);
599 return false;
600 }
601 return nNumericAttribute != SQL_FALSE;
602}
603
604// creates a QSqlField from a valid hStmt generated
605// by SQLColumns. The hStmt has to point to a valid position.
606static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, const QODBCDriverPrivate* p)
607{
608 QString fname = qGetStringData(hStmt, 3, -1, p->unicode).toString();
609 int type = qGetIntData(hStmt, 4).toInt(); // column type
610 QSqlField f(fname, qDecodeODBCType(type, p));
611 QVariant var = qGetIntData(hStmt, 6);
612 f.setLength(var.isNull() ? -1 : var.toInt()); // column size
613 var = qGetIntData(hStmt, 8).toInt();
614 f.setPrecision(var.isNull() ? -1 : var.toInt()); // precision
615 int required = qGetIntData(hStmt, 10).toInt(); // nullable-flag
616 // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
617 if (required == SQL_NO_NULLS)
618 f.setRequired(true);
619 else if (required == SQL_NULLABLE)
620 f.setRequired(false);
621 // else we don't know
622 return f;
623}
624
626{
627 SQLSMALLINT colNameLen;
628 SQLSMALLINT colType;
629 SQLULEN colSize;
630 SQLSMALLINT colScale;
631 SQLSMALLINT nullable;
632 SQLRETURN r = SQL_ERROR;
633 QVarLengthArray<SQLTCHAR, COLNAMESIZE> colName(COLNAMESIZE);
634 r = SQLDescribeCol(p->hStmt,
635 i+1,
636 colName.data(), SQLSMALLINT(colName.size()),
637 &colNameLen,
638 &colType,
639 &colSize,
640 &colScale,
641 &nullable);
642
643 if (r != SQL_SUCCESS) {
644 qSqlWarning(("QODBC::qMakeFieldInfo: Unable to describe column %1"_L1)
645 .arg(QString::number(i)), p);
646 return QSqlField();
647 }
648
649 SQLLEN unsignedFlag = SQL_FALSE;
650 r = SQLColAttribute (p->hStmt,
651 i + 1,
652 SQL_DESC_UNSIGNED,
653 0,
654 0,
655 0,
656 &unsignedFlag);
657 if (r != SQL_SUCCESS) {
658 qSqlWarning(("QODBC::qMakeFieldInfo: Unable to get column attributes for column %1"_L1)
659 .arg(QString::number(i)), p);
660 }
661
662 const QString qColName(fromSQLTCHAR(colName, colNameLen));
663 // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
664 QMetaType type = qDecodeODBCType(colType, unsignedFlag == SQL_FALSE);
665 QSqlField f(qColName, type);
666 f.setLength(colSize == 0 ? -1 : int(colSize));
667 f.setPrecision(colScale == 0 ? -1 : int(colScale));
668 if (nullable == SQL_NO_NULLS)
669 f.setRequired(true);
670 else if (nullable == SQL_NULLABLE)
671 f.setRequired(false);
672 // else we don't know
673 f.setAutoValue(isAutoValue(p->hStmt, i));
674 QVarLengthArray<SQLTCHAR, TABLENAMESIZE> tableName(TABLENAMESIZE);
675 SQLSMALLINT tableNameLen;
676 r = SQLColAttribute(p->hStmt,
677 i + 1,
678 SQL_DESC_BASE_TABLE_NAME,
679 tableName.data(),
680 SQLSMALLINT(tableName.size() * sizeof(SQLTCHAR)), // SQLColAttribute needs/returns size in bytes
681 &tableNameLen,
682 0);
683 if (r == SQL_SUCCESS)
684 f.setTableName(fromSQLTCHAR(tableName, tableNameLen / sizeof(SQLTCHAR)));
685 return f;
686}
687
688static size_t qGetODBCVersion(const QString &connOpts)
689{
690 if (connOpts.contains("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC3"_L1, Qt::CaseInsensitive))
691 return SQL_OV_ODBC3;
692 return SQL_OV_ODBC2;
693}
694
696{
697 if (!isQuoteInitialized) {
698 SQLTCHAR driverResponse[4];
699 SQLSMALLINT length;
700 int r = SQLGetInfo(hDbc,
701 SQL_IDENTIFIER_QUOTE_CHAR,
702 &driverResponse,
703 sizeof(driverResponse),
704 &length);
705 if (SQL_SUCCEEDED(r))
706 quote = QChar(driverResponse[0]);
707 else
708 quote = u'"';
709 isQuoteInitialized = true;
710 }
711 return quote;
712}
713
715{
716 return sqlFetchNext(hStmt.handle());
717}
718
719SQLRETURN QODBCDriverPrivate::sqlFetchNext(SQLHANDLE hStmt) const
720{
722 return SQLFetchScroll(hStmt, SQL_FETCH_NEXT, 0);
723 return SQLFetch(hStmt);
724}
725
726static SQLRETURN qt_string_SQLSetConnectAttr(SQLHDBC handle, SQLINTEGER attr, QStringView val)
727{
728 auto encoded = toSQLTCHAR(val);
729 return SQLSetConnectAttr(handle, attr,
730 encoded.data(),
731 SQLINTEGER(encoded.size() * sizeof(SQLTCHAR))); // size in bytes
732}
733
734
736{
737 // Set any connection attributes
738 SQLRETURN r = SQL_SUCCESS;
739 for (const auto connOpt : QStringTokenizer{connOpts, u';'}) {
740 int idx;
741 if ((idx = connOpt.indexOf(u'=')) == -1) {
742 qSqlWarning(("QODBCDriver::open: Illegal connect option value '%1'"_L1)
743 .arg(connOpt), this);
744 continue;
745 }
746 const auto opt(connOpt.left(idx));
747 const auto val(connOpt.mid(idx + 1).trimmed());
748 SQLUINTEGER v = 0;
749
750 r = SQL_SUCCESS;
751 if (opt == "SQL_ATTR_ACCESS_MODE"_L1) {
752 if (val == "SQL_MODE_READ_ONLY"_L1) {
753 v = SQL_MODE_READ_ONLY;
754 } else if (val == "SQL_MODE_READ_WRITE"_L1) {
755 v = SQL_MODE_READ_WRITE;
756 } else {
757 qSqlWarning(("QODBCDriver::open: Unknown option value '%1'"_L1)
758 .arg(val), this);
759 continue;
760 }
761 r = SQLSetConnectAttr(hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) size_t(v), 0);
762 } else if (opt == "SQL_ATTR_CONNECTION_TIMEOUT"_L1) {
763 v = val.toUInt();
764 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) size_t(v), 0);
765 } else if (opt == "SQL_ATTR_LOGIN_TIMEOUT"_L1) {
766 v = val.toUInt();
767 r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) size_t(v), 0);
768 } else if (opt == "SQL_ATTR_CURRENT_CATALOG"_L1) {
769 r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, val);
770 } else if (opt == "SQL_ATTR_METADATA_ID"_L1) {
771 if (val == "SQL_TRUE"_L1) {
772 v = SQL_TRUE;
773 } else if (val == "SQL_FALSE"_L1) {
774 v = SQL_FALSE;
775 } else {
776 qSqlWarning(("QODBCDriver::open: Unknown option value '%1'"_L1)
777 .arg(val), this);
778 continue;
779 }
780 r = SQLSetConnectAttr(hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) size_t(v), 0);
781 } else if (opt == "SQL_ATTR_PACKET_SIZE"_L1) {
782 v = val.toUInt();
783 r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) size_t(v), 0);
784 } else if (opt == "SQL_ATTR_TRACEFILE"_L1) {
785 r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, val);
786 } else if (opt == "SQL_ATTR_TRACE"_L1) {
787 if (val == "SQL_OPT_TRACE_OFF"_L1) {
788 v = SQL_OPT_TRACE_OFF;
789 } else if (val == "SQL_OPT_TRACE_ON"_L1) {
790 v = SQL_OPT_TRACE_ON;
791 } else {
792 qSqlWarning(("QODBCDriver::open: Unknown option value '%1'"_L1)
793 .arg(val), this);
794 continue;
795 }
796 r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACE, (SQLPOINTER) size_t(v), 0);
797 } else if (opt == "SQL_ATTR_CONNECTION_POOLING"_L1) {
798 if (val == "SQL_CP_OFF"_L1)
799 v = SQL_CP_OFF;
800 else if (val == "SQL_CP_ONE_PER_DRIVER"_L1)
801 v = SQL_CP_ONE_PER_DRIVER;
802 else if (val == "SQL_CP_ONE_PER_HENV"_L1)
803 v = SQL_CP_ONE_PER_HENV;
804 else if (val == "SQL_CP_DEFAULT"_L1)
805 v = SQL_CP_DEFAULT;
806 else {
807 qSqlWarning(("QODBCDriver::open: Unknown option value '%1'"_L1)
808 .arg(val), this);
809 continue;
810 }
811 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER) size_t(v), 0);
812 } else if (opt == "SQL_ATTR_CP_MATCH"_L1) {
813 if (val == "SQL_CP_STRICT_MATCH"_L1)
814 v = SQL_CP_STRICT_MATCH;
815 else if (val == "SQL_CP_RELAXED_MATCH"_L1)
816 v = SQL_CP_RELAXED_MATCH;
817 else if (val == "SQL_CP_MATCH_DEFAULT"_L1)
818 v = SQL_CP_MATCH_DEFAULT;
819 else {
820 qSqlWarning(("QODBCDriver::open: Unknown option value '%1'"_L1)
821 .arg(val), this);
822 continue;
823 }
824 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CP_MATCH, (SQLPOINTER) size_t(v), 0);
825 } else if (opt == "SQL_ATTR_ODBC_VERSION"_L1) {
826 // Already handled in QODBCDriver::open()
827 continue;
828 } else {
829 qSqlWarning(("QODBCDriver::open: Unknown connection attribute '%1'"_L1)
830 .arg(opt), this);
831 }
832 if (!SQL_SUCCEEDED(r))
833 qSqlWarning(("QODBCDriver::open: Unable to set connection attribute '%1'"_L1)
834 .arg(opt), this);
835 }
836 return true;
837}
838
840 QString &schema, QString &table) const
841{
842 Q_Q(const QODBCDriver);
843 const auto adjustName = [&](const QString &name) {
844 if (q->isIdentifierEscaped(name, QSqlDriver::TableName))
845 return q->stripDelimiters(name, QSqlDriver::TableName);
846 return adjustCase(name);
847 };
848 catalog.clear();
849 schema.clear();
850 table.clear();
851 if (!useSchema) {
852 table = adjustName(qualifier);
853 return;
854 }
855 const QList<QStringView> l = QStringView(qualifier).split(u'.');
856 switch (l.count()) {
857 case 1:
858 table = adjustName(qualifier);
859 break;
860 case 2:
861 schema = adjustName(l.at(0).toString());
862 table = adjustName(l.at(1).toString());
863 break;
864 case 3:
865 catalog = adjustName(l.at(0).toString());
866 schema = adjustName(l.at(1).toString());
867 table = adjustName(l.at(2).toString());
868 break;
869 default:
870 qSqlWarning(("QODBCDriver::splitTableQualifier: Unable to split table qualifier '%1'"_L1)
871 .arg(qualifier), this);
872 break;
873 }
874}
875
877{
878 m_defaultCase = DefaultCase::Mixed; //arbitrary case if driver cannot be queried
879 SQLUSMALLINT casing;
880 SQLRETURN r = SQLGetInfo(hDbc,
881 SQL_IDENTIFIER_CASE,
882 &casing,
883 sizeof(casing),
884 NULL);
885 if (r == SQL_SUCCESS) {
886 switch (casing) {
887 case SQL_IC_UPPER:
888 m_defaultCase = DefaultCase::Upper;
889 break;
890 case SQL_IC_LOWER:
891 m_defaultCase = DefaultCase::Lower;
892 break;
893 case SQL_IC_SENSITIVE:
894 m_defaultCase = DefaultCase::Sensitive;
895 break;
896 case SQL_IC_MIXED:
897 m_defaultCase = DefaultCase::Mixed;
898 break;
899 }
900 }
901}
902
903/*
904 Adjust the casing of an identifier to match what the
905 database engine would have done to it.
906*/
908{
909 switch (m_defaultCase) {
911 return identifier.toLower();
913 return identifier.toUpper();
916 break;
917 }
918 return identifier;
919}
920
922
927
929{
930 Q_D(QODBCResult);
931 if (d->hStmt && d->isStmtHandleValid() && driver() && driver()->isOpen()) {
932 SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
933 if (r != SQL_SUCCESS)
934 qSqlWarning(("QODBCResult: Unable to free statement handle "_L1), d);
935 }
936}
937
939{
940 Q_D(QODBCResult);
941 setActive(false);
943 d->rInf.clear();
944 d->fieldCache.clear();
945 d->fieldCacheIdx = 0;
946
947 // Always reallocate the statement handle - the statement attributes
948 // are not reset if SQLFreeStmt() is called which causes some problems.
949 SQLRETURN r;
950 if (d->hStmt && d->isStmtHandleValid()) {
951 r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
952 if (r != SQL_SUCCESS) {
953 qSqlWarning("QODBCResult::reset: Unable to free statement handle"_L1, d);
954 return false;
955 }
956 }
957 r = SQLAllocHandle(SQL_HANDLE_STMT,
958 d->dpDbc(),
959 &d->hStmt);
960 if (r != SQL_SUCCESS) {
961 qSqlWarning("QODBCResult::reset: Unable to allocate statement handle"_L1, d);
962 return false;
963 }
964
965 d->updateStmtHandleState();
966
967 if (isForwardOnly()) {
968 r = SQLSetStmtAttr(d->hStmt,
969 SQL_ATTR_CURSOR_TYPE,
970 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
971 SQL_IS_UINTEGER);
972 } else {
973 r = SQLSetStmtAttr(d->hStmt,
974 SQL_ATTR_CURSOR_TYPE,
975 (SQLPOINTER)SQL_CURSOR_STATIC,
976 SQL_IS_UINTEGER);
977 }
978 if (!SQL_SUCCEEDED(r)) {
980 "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. "
981 "Please check your ODBC driver configuration"), QSqlError::StatementError, d));
982 return false;
983 }
984
985 {
986 auto encoded = toSQLTCHAR(query);
987 r = SQLExecDirect(d->hStmt,
988 encoded.data(),
989 SQLINTEGER(encoded.size()));
990 }
991 if (!SQL_SUCCEEDED(r) && r!= SQL_NO_DATA) {
993 "Unable to execute statement"), QSqlError::StatementError, d));
994 return false;
995 }
996
997 SQLULEN isScrollable = 0;
998 r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, 0);
999 if (SQL_SUCCEEDED(r))
1000 setForwardOnly(isScrollable == SQL_NONSCROLLABLE);
1001
1002 SQLSMALLINT count = 0;
1003 SQLNumResultCols(d->hStmt, &count);
1004 if (count) {
1005 setSelect(true);
1006 for (SQLSMALLINT i = 0; i < count; ++i) {
1007 d->rInf.append(qMakeFieldInfo(d, i));
1008 }
1009 d->fieldCache.resize(count);
1010 } else {
1011 setSelect(false);
1012 }
1013 setActive(true);
1014
1015 return true;
1016}
1017
1019{
1020 Q_D(QODBCResult);
1021 if (!driver()->isOpen())
1022 return false;
1023
1024 if (isForwardOnly() && i < at())
1025 return false;
1026 if (i == at())
1027 return true;
1028 d->clearValues();
1029 int actualIdx = i + 1;
1030 if (actualIdx <= 0) {
1032 return false;
1033 }
1034 SQLRETURN r;
1035 if (isForwardOnly()) {
1036 bool ok = true;
1037 while (ok && i > at())
1038 ok = fetchNext();
1039 return ok;
1040 } else {
1041 r = SQLFetchScroll(d->hStmt,
1042 SQL_FETCH_ABSOLUTE,
1043 actualIdx);
1044 }
1045 if (r != SQL_SUCCESS) {
1046 if (r != SQL_NO_DATA)
1048 "Unable to fetch"), QSqlError::ConnectionError, d));
1049 return false;
1050 }
1051 setAt(i);
1052 return true;
1053}
1054
1056{
1057 Q_D(QODBCResult);
1058 SQLRETURN r;
1059 d->clearValues();
1060
1061 if (d->hasSQLFetchScroll)
1062 r = SQLFetchScroll(d->hStmt,
1063 SQL_FETCH_NEXT,
1064 0);
1065 else
1066 r = SQLFetch(d->hStmt);
1067
1068 if (!SQL_SUCCEEDED(r)) {
1069 if (r != SQL_NO_DATA)
1071 "Unable to fetch next"), QSqlError::ConnectionError, d));
1072 return false;
1073 }
1074 setAt(at() + 1);
1075 return true;
1076}
1077
1079{
1080 Q_D(QODBCResult);
1082 return false;
1083 SQLRETURN r;
1084 d->clearValues();
1085 if (isForwardOnly()) {
1086 return fetchNext();
1087 }
1088 r = SQLFetchScroll(d->hStmt,
1089 SQL_FETCH_FIRST,
1090 0);
1091 if (r != SQL_SUCCESS) {
1092 if (r != SQL_NO_DATA)
1094 "Unable to fetch first"), QSqlError::ConnectionError, d));
1095 return false;
1096 }
1097 setAt(0);
1098 return true;
1099}
1100
1102{
1103 Q_D(QODBCResult);
1104 if (isForwardOnly())
1105 return false;
1106 SQLRETURN r;
1107 d->clearValues();
1108 r = SQLFetchScroll(d->hStmt,
1109 SQL_FETCH_PRIOR,
1110 0);
1111 if (r != SQL_SUCCESS) {
1112 if (r != SQL_NO_DATA)
1114 "Unable to fetch previous"), QSqlError::ConnectionError, d));
1115 return false;
1116 }
1117 setAt(at() - 1);
1118 return true;
1119}
1120
1122{
1123 Q_D(QODBCResult);
1124 SQLRETURN r;
1125 d->clearValues();
1126
1127 if (isForwardOnly()) {
1128 // cannot seek to last row in forwardOnly mode, so we have to use brute force
1129 int i = at();
1130 if (i == QSql::AfterLastRow)
1131 return false;
1132 if (i == QSql::BeforeFirstRow)
1133 i = 0;
1134 while (fetchNext())
1135 ++i;
1136 setAt(i);
1137 return true;
1138 }
1139
1140 r = SQLFetchScroll(d->hStmt,
1141 SQL_FETCH_LAST,
1142 0);
1143 if (r != SQL_SUCCESS) {
1144 if (r != SQL_NO_DATA)
1146 "Unable to fetch last"), QSqlError::ConnectionError, d));
1147 return false;
1148 }
1149 SQLULEN currRow = 0;
1150 r = SQLGetStmtAttr(d->hStmt,
1151 SQL_ROW_NUMBER,
1152 &currRow,
1153 SQL_IS_INTEGER,
1154 0);
1155 if (r != SQL_SUCCESS)
1156 return false;
1157 setAt(currRow-1);
1158 return true;
1159}
1160
1162{
1163 Q_D(QODBCResult);
1164 if (field >= d->rInf.count() || field < 0) {
1165 qSqlWarning(("QODBCResult::data: column %1 out of range"_L1)
1166 .arg(QString::number(field)), d);
1167 return QVariant();
1168 }
1169 if (field < d->fieldCacheIdx)
1170 return d->fieldCache.at(field);
1171
1172 SQLRETURN r(0);
1173 SQLLEN lengthIndicator = 0;
1174
1175 for (int i = d->fieldCacheIdx; i <= field; ++i) {
1176 // some servers do not support fetching column n after we already
1177 // fetched column n+1, so cache all previous columns here
1178 const QSqlField info = d->rInf.field(i);
1179 switch (info.metaType().id()) {
1180 case QMetaType::LongLong:
1181 d->fieldCache[i] = qGetBigIntData(d->hStmt, i);
1182 break;
1183 case QMetaType::ULongLong:
1184 d->fieldCache[i] = qGetBigIntData(d->hStmt, i, false);
1185 break;
1186 case QMetaType::Int:
1187 case QMetaType::Short:
1188 d->fieldCache[i] = qGetIntData(d->hStmt, i);
1189 break;
1190 case QMetaType::UInt:
1191 case QMetaType::UShort:
1192 d->fieldCache[i] = qGetIntData(d->hStmt, i, false);
1193 break;
1194 case QMetaType::QDate:
1195 DATE_STRUCT dbuf;
1196 r = SQLGetData(d->hStmt,
1197 i + 1,
1198 SQL_C_DATE,
1199 (SQLPOINTER)&dbuf,
1200 0,
1201 &lengthIndicator);
1202 if (SQL_SUCCEEDED(r) && (lengthIndicator != SQL_NULL_DATA))
1203 d->fieldCache[i] = QVariant(QDate(dbuf.year, dbuf.month, dbuf.day));
1204 else
1205 d->fieldCache[i] = QVariant(QMetaType::fromType<QDate>());
1206 break;
1207 case QMetaType::QTime:
1208 TIME_STRUCT tbuf;
1209 r = SQLGetData(d->hStmt,
1210 i + 1,
1211 SQL_C_TIME,
1212 (SQLPOINTER)&tbuf,
1213 0,
1214 &lengthIndicator);
1215 if (SQL_SUCCEEDED(r) && (lengthIndicator != SQL_NULL_DATA))
1216 d->fieldCache[i] = QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second));
1217 else
1218 d->fieldCache[i] = QVariant(QMetaType::fromType<QTime>());
1219 break;
1220 case QMetaType::QDateTime:
1221 TIMESTAMP_STRUCT dtbuf;
1222 r = SQLGetData(d->hStmt,
1223 i + 1,
1224 SQL_C_TIMESTAMP,
1225 (SQLPOINTER)&dtbuf,
1226 0,
1227 &lengthIndicator);
1228 if (SQL_SUCCEEDED(r) && (lengthIndicator != SQL_NULL_DATA))
1229 d->fieldCache[i] = QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day),
1230 QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000)));
1231 else
1232 d->fieldCache[i] = QVariant(QMetaType::fromType<QDateTime>());
1233 break;
1234 case QMetaType::QByteArray:
1235 d->fieldCache[i] = qGetBinaryData(d->hStmt, i);
1236 break;
1237 case QMetaType::QString:
1238 d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), d->unicode);
1239 break;
1240 case QMetaType::Double:
1241 switch(numericalPrecisionPolicy()) {
1243 d->fieldCache[i] = qGetIntData(d->hStmt, i);
1244 break;
1246 d->fieldCache[i] = qGetBigIntData(d->hStmt, i);
1247 break;
1249 d->fieldCache[i] = qGetDoubleData(d->hStmt, i);
1250 break;
1252 const int extra = info.precision() > 0 ? 1 : 0;
1253 d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length() + extra, false);
1254 break;
1255 }
1256 break;
1257 default:
1258 d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), false);
1259 break;
1260 }
1261 d->fieldCacheIdx = field + 1;
1262 }
1263 return d->fieldCache[field];
1264}
1265
1266bool QODBCResult::isNull(int field)
1267{
1268 Q_D(const QODBCResult);
1269 if (field < 0 || field >= d->fieldCache.size())
1270 return true;
1271 if (field >= d->fieldCacheIdx) {
1272 // since there is no good way to find out whether the value is NULL
1273 // without fetching the field we'll fetch it here.
1274 // (data() also sets the NULL flag)
1275 data(field);
1276 }
1277 return d->fieldCache.at(field).isNull();
1278}
1279
1281{
1282 return -1;
1283}
1284
1286{
1287 Q_D(QODBCResult);
1288 SQLLEN affectedRowCount = 0;
1289 SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount);
1290 if (r == SQL_SUCCESS)
1291 return affectedRowCount;
1292 qSqlWarning("QODBCResult::numRowsAffected: Unable to count affected rows"_L1, d);
1293 return -1;
1294}
1295
1297{
1298 Q_D(QODBCResult);
1299 setActive(false);
1301 SQLRETURN r;
1302
1303 d->rInf.clear();
1304 if (d->hStmt && d->isStmtHandleValid()) {
1305 r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
1306 if (r != SQL_SUCCESS) {
1307 qSqlWarning("QODBCResult::prepare: Unable to close statement"_L1, d);
1308 return false;
1309 }
1310 }
1311 r = SQLAllocHandle(SQL_HANDLE_STMT,
1312 d->dpDbc(),
1313 &d->hStmt);
1314 if (r != SQL_SUCCESS) {
1315 qSqlWarning("QODBCResult::prepare: Unable to allocate statement handle"_L1, d);
1316 return false;
1317 }
1318
1319 d->updateStmtHandleState();
1320
1321 if (isForwardOnly()) {
1322 r = SQLSetStmtAttr(d->hStmt,
1323 SQL_ATTR_CURSOR_TYPE,
1324 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1325 SQL_IS_UINTEGER);
1326 } else {
1327 r = SQLSetStmtAttr(d->hStmt,
1328 SQL_ATTR_CURSOR_TYPE,
1329 (SQLPOINTER)SQL_CURSOR_STATIC,
1330 SQL_IS_UINTEGER);
1331 }
1332 if (!SQL_SUCCEEDED(r)) {
1334 "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. "
1335 "Please check your ODBC driver configuration"), QSqlError::StatementError, d));
1336 return false;
1337 }
1338
1339 {
1340 auto encoded = toSQLTCHAR(query);
1341 r = SQLPrepare(d->hStmt,
1342 encoded.data(),
1343 SQLINTEGER(encoded.size()));
1344 }
1345
1346 if (r != SQL_SUCCESS) {
1348 "Unable to prepare statement"), QSqlError::StatementError, d));
1349 return false;
1350 }
1351 return true;
1352}
1353
1355{
1356 Q_D(QODBCResult);
1357 setActive(false);
1359 d->rInf.clear();
1360 d->fieldCache.clear();
1361 d->fieldCacheIdx = 0;
1362
1363 if (!d->hStmt) {
1364 qSqlWarning("QODBCResult::exec: No statement handle available"_L1, d);
1365 return false;
1366 }
1367
1368 if (isSelect())
1369 SQLCloseCursor(d->hStmt);
1370
1372 QByteArrayList tmpStorage(values.count(), QByteArray()); // targets for SQLBindParameter()
1373 QVarLengthArray<SQLLEN, 32> indicators(values.count(), 0);
1374
1375 // bind parameters - only positional binding allowed
1376 SQLRETURN r;
1377 for (qsizetype i = 0; i < values.count(); ++i) {
1378 if (bindValueType(i) & QSql::Out)
1379 values[i].detach();
1380 const QVariant &val = values.at(i);
1381 SQLLEN *ind = &indicators[i];
1383 *ind = SQL_NULL_DATA;
1384 switch (val.typeId()) {
1385 case QMetaType::QDate: {
1386 QByteArray &ba = tmpStorage[i];
1387 ba.resize(sizeof(DATE_STRUCT));
1388 DATE_STRUCT *dt = (DATE_STRUCT *)const_cast<char *>(ba.constData());
1389 QDate qdt = val.toDate();
1390 dt->year = qdt.year();
1391 dt->month = qdt.month();
1392 dt->day = qdt.day();
1393 r = SQLBindParameter(d->hStmt,
1394 i + 1,
1396 SQL_C_DATE,
1397 SQL_DATE,
1398 0,
1399 0,
1400 (void *) dt,
1401 0,
1402 *ind == SQL_NULL_DATA ? ind : NULL);
1403 break; }
1404 case QMetaType::QTime: {
1405 QByteArray &ba = tmpStorage[i];
1406 ba.resize(sizeof(TIME_STRUCT));
1407 TIME_STRUCT *dt = (TIME_STRUCT *)const_cast<char *>(ba.constData());
1408 QTime qdt = val.toTime();
1409 dt->hour = qdt.hour();
1410 dt->minute = qdt.minute();
1411 dt->second = qdt.second();
1412 r = SQLBindParameter(d->hStmt,
1413 i + 1,
1415 SQL_C_TIME,
1416 SQL_TIME,
1417 0,
1418 0,
1419 (void *) dt,
1420 0,
1421 *ind == SQL_NULL_DATA ? ind : NULL);
1422 break; }
1423 case QMetaType::QDateTime: {
1424 QByteArray &ba = tmpStorage[i];
1425 ba.resize(sizeof(TIMESTAMP_STRUCT));
1426 TIMESTAMP_STRUCT *dt = reinterpret_cast<TIMESTAMP_STRUCT *>(const_cast<char *>(ba.constData()));
1427 const QDateTime qdt = val.toDateTime();
1428 const QDate qdate = qdt.date();
1429 const QTime qtime = qdt.time();
1430 dt->year = qdate.year();
1431 dt->month = qdate.month();
1432 dt->day = qdate.day();
1433 dt->hour = qtime.hour();
1434 dt->minute = qtime.minute();
1435 dt->second = qtime.second();
1436 // (20 includes a separating period)
1437 const int precision = d->drv_d_func()->datetimePrecision - 20;
1438 if (precision <= 0) {
1439 dt->fraction = 0;
1440 } else {
1441 dt->fraction = qtime.msec() * 1000000;
1442
1443 // (How many leading digits do we want to keep? With SQL Server 2005, this should be 3: 123000000)
1444 int keep = (int)qPow(10.0, 9 - qMin(9, precision));
1445 dt->fraction = (dt->fraction / keep) * keep;
1446 }
1447
1448 r = SQLBindParameter(d->hStmt,
1449 i + 1,
1451 SQL_C_TIMESTAMP,
1452 SQL_TIMESTAMP,
1453 d->drv_d_func()->datetimePrecision,
1454 precision,
1455 (void *) dt,
1456 0,
1457 *ind == SQL_NULL_DATA ? ind : NULL);
1458 break; }
1459 case QMetaType::Int:
1460 r = SQLBindParameter(d->hStmt,
1461 i + 1,
1463 SQL_C_SLONG,
1464 SQL_INTEGER,
1465 0,
1466 0,
1467 const_cast<void *>(val.constData()),
1468 0,
1469 *ind == SQL_NULL_DATA ? ind : NULL);
1470 break;
1471 case QMetaType::UInt:
1472 r = SQLBindParameter(d->hStmt,
1473 i + 1,
1475 SQL_C_ULONG,
1476 SQL_NUMERIC,
1477 15,
1478 0,
1479 const_cast<void *>(val.constData()),
1480 0,
1481 *ind == SQL_NULL_DATA ? ind : NULL);
1482 break;
1483 case QMetaType::Short:
1484 r = SQLBindParameter(d->hStmt,
1485 i + 1,
1487 SQL_C_SSHORT,
1488 SQL_SMALLINT,
1489 0,
1490 0,
1491 const_cast<void *>(val.constData()),
1492 0,
1493 *ind == SQL_NULL_DATA ? ind : NULL);
1494 break;
1495 case QMetaType::UShort:
1496 r = SQLBindParameter(d->hStmt,
1497 i + 1,
1499 SQL_C_USHORT,
1500 SQL_NUMERIC,
1501 15,
1502 0,
1503 const_cast<void *>(val.constData()),
1504 0,
1505 *ind == SQL_NULL_DATA ? ind : NULL);
1506 break;
1507 case QMetaType::Double:
1508 r = SQLBindParameter(d->hStmt,
1509 i + 1,
1511 SQL_C_DOUBLE,
1512 SQL_DOUBLE,
1513 0,
1514 0,
1515 const_cast<void *>(val.constData()),
1516 0,
1517 *ind == SQL_NULL_DATA ? ind : NULL);
1518 break;
1519 case QMetaType::Float:
1520 r = SQLBindParameter(d->hStmt,
1521 i + 1,
1523 SQL_C_FLOAT,
1524 SQL_REAL,
1525 0,
1526 0,
1527 const_cast<void *>(val.constData()),
1528 0,
1529 *ind == SQL_NULL_DATA ? ind : NULL);
1530 break;
1531 case QMetaType::LongLong:
1532 r = SQLBindParameter(d->hStmt,
1533 i + 1,
1535 SQL_C_SBIGINT,
1536 SQL_BIGINT,
1537 0,
1538 0,
1539 const_cast<void *>(val.constData()),
1540 0,
1541 *ind == SQL_NULL_DATA ? ind : NULL);
1542 break;
1543 case QMetaType::ULongLong:
1544 r = SQLBindParameter(d->hStmt,
1545 i + 1,
1547 SQL_C_UBIGINT,
1548 SQL_BIGINT,
1549 0,
1550 0,
1551 const_cast<void *>(val.constData()),
1552 0,
1553 *ind == SQL_NULL_DATA ? ind : NULL);
1554 break;
1555 case QMetaType::QByteArray:
1556 if (*ind != SQL_NULL_DATA) {
1557 *ind = val.toByteArray().size();
1558 }
1559 r = SQLBindParameter(d->hStmt,
1560 i + 1,
1562 SQL_C_BINARY,
1563 SQL_LONGVARBINARY,
1564 val.toByteArray().size(),
1565 0,
1566 const_cast<char *>(val.toByteArray().constData()),
1567 val.toByteArray().size(),
1568 ind);
1569 break;
1570 case QMetaType::Bool:
1571 r = SQLBindParameter(d->hStmt,
1572 i + 1,
1574 SQL_C_BIT,
1575 SQL_BIT,
1576 0,
1577 0,
1578 const_cast<void *>(val.constData()),
1579 0,
1580 *ind == SQL_NULL_DATA ? ind : NULL);
1581 break;
1582 case QMetaType::QString:
1583 if (d->unicode) {
1584 QByteArray &ba = tmpStorage[i];
1585 {
1586 const auto encoded = toSQLTCHAR(val.toString());
1587 ba = QByteArray(reinterpret_cast<const char *>(encoded.data()),
1588 encoded.size() * sizeof(SQLTCHAR));
1589 }
1590
1591 if (*ind != SQL_NULL_DATA)
1592 *ind = ba.size();
1593
1594 if (bindValueType(i) & QSql::Out) {
1595 r = SQLBindParameter(d->hStmt,
1596 i + 1,
1598 SQL_C_TCHAR,
1599 ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1600 0, // god knows... don't change this!
1601 0,
1602 const_cast<char *>(ba.constData()), // don't detach
1603 ba.size(),
1604 ind);
1605 break;
1606 }
1607 r = SQLBindParameter(d->hStmt,
1608 i + 1,
1610 SQL_C_TCHAR,
1611 ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1612 ba.size(),
1613 0,
1614 const_cast<char *>(ba.constData()), // don't detach
1615 ba.size(),
1616 ind);
1617 break;
1618 }
1619 else
1620 {
1621 QByteArray &str = tmpStorage[i];
1622 str = val.toString().toUtf8();
1623 if (*ind != SQL_NULL_DATA)
1624 *ind = str.length();
1625 int strSize = str.length();
1626
1627 r = SQLBindParameter(d->hStmt,
1628 i + 1,
1630 SQL_C_CHAR,
1631 strSize > 254 ? SQL_LONGVARCHAR : SQL_VARCHAR,
1632 strSize,
1633 0,
1634 const_cast<char *>(str.constData()),
1635 strSize,
1636 ind);
1637 break;
1638 }
1639 Q_FALLTHROUGH();
1640 default: {
1641 QByteArray &ba = tmpStorage[i];
1642 if (*ind != SQL_NULL_DATA)
1643 *ind = ba.size();
1644 r = SQLBindParameter(d->hStmt,
1645 i + 1,
1647 SQL_C_BINARY,
1648 SQL_VARBINARY,
1649 ba.length() + 1,
1650 0,
1651 const_cast<char *>(ba.constData()),
1652 ba.length() + 1,
1653 ind);
1654 break; }
1655 }
1656 if (r != SQL_SUCCESS) {
1657 qSqlWarning("QODBCResult::exec: unable to bind variable:"_L1, d);
1659 "Unable to bind variable"), QSqlError::StatementError, d));
1660 return false;
1661 }
1662 }
1663 r = SQLExecute(d->hStmt);
1664 if (!SQL_SUCCEEDED(r) && r != SQL_NO_DATA) {
1665 qSqlWarning("QODBCResult::exec: Unable to execute statement:"_L1, d);
1667 "Unable to execute statement"), QSqlError::StatementError, d));
1668 return false;
1669 }
1670
1671 SQLULEN isScrollable = 0;
1672 r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, 0);
1673 if (SQL_SUCCEEDED(r))
1674 setForwardOnly(isScrollable == SQL_NONSCROLLABLE);
1675
1676 SQLSMALLINT count = 0;
1677 SQLNumResultCols(d->hStmt, &count);
1678 if (count) {
1679 setSelect(true);
1680 for (SQLSMALLINT i = 0; i < count; ++i) {
1681 d->rInf.append(qMakeFieldInfo(d, i));
1682 }
1683 d->fieldCache.resize(count);
1684 } else {
1685 setSelect(false);
1686 }
1687 setActive(true);
1688
1689
1690 //get out parameters
1691 if (!hasOutValues())
1692 return true;
1693
1694 for (qsizetype i = 0; i < values.count(); ++i) {
1695 switch (values.at(i).typeId()) {
1696 case QMetaType::QDate: {
1697 DATE_STRUCT ds = *((DATE_STRUCT *)const_cast<char *>(tmpStorage.at(i).constData()));
1698 values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
1699 break; }
1700 case QMetaType::QTime: {
1701 TIME_STRUCT dt = *((TIME_STRUCT *)const_cast<char *>(tmpStorage.at(i).constData()));
1702 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
1703 break; }
1704 case QMetaType::QDateTime: {
1705 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT*)
1706 const_cast<char *>(tmpStorage.at(i).constData()));
1707 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
1708 QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
1709 break; }
1710 case QMetaType::Bool:
1711 case QMetaType::Short:
1712 case QMetaType::UShort:
1713 case QMetaType::Int:
1714 case QMetaType::UInt:
1715 case QMetaType::Float:
1716 case QMetaType::Double:
1717 case QMetaType::QByteArray:
1718 case QMetaType::LongLong:
1719 case QMetaType::ULongLong:
1720 //nothing to do
1721 break;
1722 case QMetaType::QString:
1723 if (d->unicode) {
1724 if (bindValueType(i) & QSql::Out) {
1725 const QByteArray &bytes = tmpStorage.at(i);
1726 const auto strSize = bytes.size() / sizeof(SQLTCHAR);
1727 QVarLengthArray<SQLTCHAR> string(strSize);
1728 memcpy(string.data(), bytes.data(), strSize * sizeof(SQLTCHAR));
1729 values[i] = fromSQLTCHAR(string);
1730 }
1731 break;
1732 }
1733 Q_FALLTHROUGH();
1734 default: {
1735 if (bindValueType(i) & QSql::Out)
1736 values[i] = tmpStorage.at(i);
1737 break; }
1738 }
1739 if (indicators[i] == SQL_NULL_DATA)
1740 values[i] = QVariant(values[i].metaType());
1741 }
1742 return true;
1743}
1744
1746{
1747 Q_D(const QODBCResult);
1748 if (!isActive() || !isSelect())
1749 return QSqlRecord();
1750 return d->rInf;
1751}
1752
1754{
1755 Q_D(const QODBCResult);
1756 QString sql;
1757
1758 switch (driver()->dbmsType()) {
1760 case QSqlDriver::Sybase:
1761 sql = "SELECT @@IDENTITY;"_L1;
1762 break;
1764 sql = "SELECT LAST_INSERT_ID();"_L1;
1765 break;
1767 sql = "SELECT lastval();"_L1;
1768 break;
1769 default:
1770 break;
1771 }
1772
1773 if (!sql.isEmpty()) {
1774 QSqlQuery qry(driver()->createResult());
1775 if (qry.exec(sql) && qry.next())
1776 return qry.value(0);
1777
1778 qSqlWarning("QODBCResult::lastInsertId: Unable to get lastInsertId"_L1, d);
1779 } else {
1780 qSqlWarning("QODBCResult::lastInsertId: not implemented for this DBMS"_L1, d);
1781 }
1782
1783 return QVariant();
1784}
1785
1787{
1788 Q_D(const QODBCResult);
1789 return QVariant(QMetaType::fromType<SQLHANDLE>(), &d->hStmt);
1790}
1791
1793{
1794 Q_D(QODBCResult);
1795 setActive(false);
1797 d->rInf.clear();
1798 d->fieldCache.clear();
1799 d->fieldCacheIdx = 0;
1800 setSelect(false);
1801
1802 SQLRETURN r = SQLMoreResults(d->hStmt);
1803 if (r != SQL_SUCCESS) {
1804 if (r == SQL_SUCCESS_WITH_INFO) {
1805 qSqlWarning("QODBCResult::nextResult:"_L1, d);
1806 } else {
1807 if (r != SQL_NO_DATA)
1809 "Unable to fetch last"), QSqlError::ConnectionError, d));
1810 return false;
1811 }
1812 }
1813
1814 SQLSMALLINT count = 0;
1815 SQLNumResultCols(d->hStmt, &count);
1816 if (count) {
1817 setSelect(true);
1818 for (SQLSMALLINT i = 0; i < count; ++i) {
1819 d->rInf.append(qMakeFieldInfo(d, i));
1820 }
1821 d->fieldCache.resize(count);
1822 } else {
1823 setSelect(false);
1824 }
1825 setActive(true);
1826
1827 return true;
1828}
1829
1831{
1833}
1834
1836{
1837 Q_D(QODBCResult);
1838 if (d->hStmt)
1839 SQLCloseCursor(d->hStmt);
1840}
1841
1843
1844
1846 : QSqlDriver(*new QODBCDriverPrivate, parent)
1847{
1848}
1849
1850QODBCDriver::QODBCDriver(SQLHANDLE env, SQLHANDLE con, QObject *parent)
1851 : QSqlDriver(*new QODBCDriverPrivate, parent)
1852{
1853 Q_D(QODBCDriver);
1854 d->hEnv = env;
1855 d->hDbc = con;
1856 if (env && con) {
1857 setOpen(true);
1858 setOpenError(false);
1859 }
1860}
1861
1863{
1864 cleanup();
1865}
1866
1868{
1869 Q_D(const QODBCDriver);
1870 switch (f) {
1871 case Transactions: {
1872 if (!d->hDbc)
1873 return false;
1874 SQLUSMALLINT txn;
1875 SQLSMALLINT t;
1876 int r = SQLGetInfo(d->hDbc,
1877 (SQLUSMALLINT)SQL_TXN_CAPABLE,
1878 &txn,
1879 sizeof(txn),
1880 &t);
1881 if (r != SQL_SUCCESS || txn == SQL_TC_NONE)
1882 return false;
1883 else
1884 return true;
1885 }
1886 case Unicode:
1887 return d->unicode;
1888 case PreparedQueries:
1890 case FinishQuery:
1892 return true;
1893 case QuerySize:
1894 case NamedPlaceholders:
1895 case BatchOperations:
1896 case SimpleLocking:
1897 case EventNotifications:
1898 case CancelQuery:
1899 return false;
1900 case LastInsertId:
1901 return (d->dbmsType == MSSqlServer)
1902 || (d->dbmsType == Sybase)
1903 || (d->dbmsType == MySqlServer)
1904 || (d->dbmsType == PostgreSQL);
1905 case MultipleResultSets:
1906 return d->hasMultiResultSets;
1907 case BLOB: {
1908 if (d->dbmsType == MySqlServer)
1909 return true;
1910 else
1911 return false;
1912 }
1913 }
1914 return false;
1915}
1916
1918 const QString & user,
1919 const QString & password,
1920 const QString &,
1921 int,
1922 const QString& connOpts)
1923{
1924 const auto ensureEscaped = [](QString arg) -> QString {
1925 QChar quoteChar;
1926 if (arg.startsWith(u'"'))
1927 quoteChar = u'\'';
1928 else if (arg.startsWith(u'\''))
1929 quoteChar = u'"';
1930 else if (arg.contains(u';'))
1931 quoteChar = u'"';
1932 else
1933 return arg;
1934 return quoteChar + arg + quoteChar;
1935 };
1936 Q_D(QODBCDriver);
1937 if (isOpen())
1938 close();
1939 SQLRETURN r;
1940 r = SQLAllocHandle(SQL_HANDLE_ENV,
1941 SQL_NULL_HANDLE,
1942 &d->hEnv);
1943 if (!SQL_SUCCEEDED(r)) {
1944 qSqlWarning("QODBCDriver::open: Unable to allocate environment"_L1, d);
1945 setOpenError(true);
1946 return false;
1947 }
1948 r = SQLSetEnvAttr(d->hEnv,
1949 SQL_ATTR_ODBC_VERSION,
1950 (SQLPOINTER)qGetODBCVersion(connOpts),
1951 SQL_IS_UINTEGER);
1952 r = SQLAllocHandle(SQL_HANDLE_DBC,
1953 d->hEnv,
1954 &d->hDbc);
1955 if (!SQL_SUCCEEDED(r)) {
1956 qSqlWarning("QODBCDriver::open: Unable to allocate connection"_L1, d);
1957 setOpenError(true);
1958 cleanup();
1959 return false;
1960 }
1961
1962 if (!d->setConnectionOptions(connOpts)) {
1963 cleanup();
1964 return false;
1965 }
1966
1967 // Create the connection string
1968 QString connQStr;
1969 // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
1970 if (db.contains(".dsn"_L1, Qt::CaseInsensitive))
1971 connQStr = "FILEDSN="_L1 + ensureEscaped(db);
1972 else if (db.contains("DRIVER="_L1, Qt::CaseInsensitive)
1973 || db.contains("SERVER="_L1, Qt::CaseInsensitive))
1974 connQStr = db;
1975 else
1976 connQStr = "DSN="_L1 + ensureEscaped(db);
1977
1978 if (!user.isEmpty())
1979 connQStr += ";UID="_L1 + ensureEscaped(user);
1980 if (!password.isEmpty())
1981 connQStr += ";PWD="_L1 + ensureEscaped(password);
1982
1983 SQLSMALLINT cb;
1984 QVarLengthArray<SQLTCHAR, 1024> connOut(1024);
1985 {
1986 auto encoded = toSQLTCHAR(connQStr);
1987 r = SQLDriverConnect(d->hDbc,
1988 nullptr,
1989 encoded.data(), SQLSMALLINT(encoded.size()),
1990 connOut.data(), SQLSMALLINT(connOut.size()),
1991 &cb,
1992 /*SQL_DRIVER_NOPROMPT*/0);
1993 }
1994
1995 if (!SQL_SUCCEEDED(r)) {
1996 setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
1997 setOpenError(true);
1998 cleanup();
1999 return false;
2000 }
2001
2002 if (!d->checkDriver()) {
2003 setLastError(qMakeError(tr("Unable to connect - Driver doesn't support all "
2004 "functionality required"), QSqlError::ConnectionError, d));
2005 setOpenError(true);
2006 cleanup();
2007 return false;
2008 }
2009
2010 d->checkUnicode();
2011 d->checkSchemaUsage();
2012 d->checkDBMS();
2013 d->checkHasSQLFetchScroll();
2014 d->checkHasMultiResults();
2015 d->checkDateTimePrecision();
2016 d->checkDefaultCase();
2017 setOpen(true);
2018 setOpenError(false);
2019 if (d->dbmsType == MSSqlServer) {
2021 i.exec("SET QUOTED_IDENTIFIER ON"_L1);
2022 }
2023 return true;
2024}
2025
2027{
2028 cleanup();
2029 setOpen(false);
2030 setOpenError(false);
2031}
2032
2033void QODBCDriver::cleanup()
2034{
2035 Q_D(QODBCDriver);
2036 SQLRETURN r;
2037
2038 if (d->hDbc) {
2039 // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
2040 if (isOpen()) {
2041 r = SQLDisconnect(d->hDbc);
2042 if (r != SQL_SUCCESS)
2043 qSqlWarning("QODBCDriver::disconnect: Unable to disconnect datasource"_L1, d);
2044 else
2045 d->disconnectCount++;
2046 }
2047
2048 r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
2049 if (r != SQL_SUCCESS)
2050 qSqlWarning("QODBCDriver::cleanup: Unable to free connection handle"_L1, d);
2051 d->hDbc = 0;
2052 }
2053
2054 if (d->hEnv) {
2055 r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
2056 if (r != SQL_SUCCESS)
2057 qSqlWarning("QODBCDriver::cleanup: Unable to free environment handle"_L1, d);
2058 d->hEnv = 0;
2059 }
2060}
2061
2062// checks whether the server can return char, varchar and longvarchar
2063// as two byte unicode characters
2065{
2066 SQLRETURN r;
2067 SQLUINTEGER fFunc;
2068
2069 unicode = false;
2070 r = SQLGetInfo(hDbc,
2071 SQL_CONVERT_CHAR,
2072 (SQLPOINTER)&fFunc,
2073 sizeof(fFunc),
2074 NULL);
2075 if (SQL_SUCCEEDED(r) && (fFunc & SQL_CVT_WCHAR)) {
2076 unicode = true;
2077 return;
2078 }
2079
2080 r = SQLGetInfo(hDbc,
2081 SQL_CONVERT_VARCHAR,
2082 (SQLPOINTER)&fFunc,
2083 sizeof(fFunc),
2084 NULL);
2085 if (SQL_SUCCEEDED(r) && (fFunc & SQL_CVT_WVARCHAR)) {
2086 unicode = true;
2087 return;
2088 }
2089
2090 r = SQLGetInfo(hDbc,
2091 SQL_CONVERT_LONGVARCHAR,
2092 (SQLPOINTER)&fFunc,
2093 sizeof(fFunc),
2094 NULL);
2095 if (SQL_SUCCEEDED(r) && (fFunc & SQL_CVT_WLONGVARCHAR)) {
2096 unicode = true;
2097 return;
2098 }
2099
2100 SqlStmtHandle hStmt(hDbc);
2101 // for databases which do not return something useful in SQLGetInfo and are picky about a
2102 // 'SELECT' statement without 'FROM' but support VALUE(foo) statement like e.g. DB2 or Oracle
2103 const std::array<QStringView, 3> statements = {
2104 u"select 'test'",
2105 u"values('test')",
2106 u"select 'test' from dual",
2107 };
2108 for (const auto &statement : statements) {
2109 auto encoded = toSQLTCHAR(statement);
2110 r = SQLExecDirect(hStmt.handle(), encoded.data(), SQLINTEGER(encoded.size()));
2111 if (r == SQL_SUCCESS)
2112 break;
2113 }
2114 if (r == SQL_SUCCESS) {
2115 r = SQLFetch(hStmt.handle());
2116 if (r == SQL_SUCCESS) {
2117 QVarLengthArray<SQLWCHAR, 10> buffer(10);
2118 r = SQLGetData(hStmt.handle(), 1, SQL_C_WCHAR, buffer.data(),
2119 buffer.size() * sizeof(SQLWCHAR), NULL);
2120 if (r == SQL_SUCCESS && fromSQLTCHAR(buffer) == "test"_L1) {
2121 unicode = true;
2122 }
2123 }
2124 }
2125}
2126
2128{
2129#ifdef ODBC_CHECK_DRIVER
2130 static constexpr SQLUSMALLINT reqFunc[] = {
2131 SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
2132 SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
2133 SQL_API_SQLGETINFO, SQL_API_SQLTABLES
2134 };
2135
2136 // these functions are optional
2137 static constexpr SQLUSMALLINT optFunc[] = {
2138 SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT
2139 };
2140
2141 SQLRETURN r;
2142 SQLUSMALLINT sup;
2143
2144 // check the required functions
2145 for (const SQLUSMALLINT func : reqFunc) {
2146
2147 r = SQLGetFunctions(hDbc, func, &sup);
2148
2149 if (r != SQL_SUCCESS) {
2150 qSqlWarning("QODBCDriver::checkDriver: Cannot get list of supported functions"_L1, this);
2151 return false;
2152 }
2153 if (sup == SQL_FALSE) {
2154 qSqlWarning(("QODBCDriver::checkDriver: Driver doesn't support all needed "
2155 "functionality (func id %1).\nPlease look at the Qt SQL Module "
2156 "Driver documentation for more information."_L1)
2157 .arg(QString::number(func)), this);
2158 return false;
2159 }
2160 }
2161
2162 // these functions are optional and just generate a warning
2163 for (const SQLUSMALLINT func : optFunc) {
2164
2165 r = SQLGetFunctions(hDbc, func, &sup);
2166
2167 if (r != SQL_SUCCESS) {
2168 qSqlWarning("QODBCDriver::checkDriver: Cannot get list of supported functions"_L1, this);
2169 return false;
2170 }
2171 if (sup == SQL_FALSE) {
2172 qSqlWarning(("QODBCDriver::checkDriver: Driver doesn't support some "
2173 "non-critical functions (func id %1)."_L1)
2174 .arg(QString::number(func)), this);
2175 return true;
2176 }
2177 }
2178#endif //ODBC_CHECK_DRIVER
2179
2180 return true;
2181}
2182
2184{
2185 SQLRETURN r;
2186 SQLUINTEGER val;
2187
2188 r = SQLGetInfo(hDbc,
2189 SQL_SCHEMA_USAGE,
2190 (SQLPOINTER) &val,
2191 sizeof(val),
2192 NULL);
2193 if (SQL_SUCCEEDED(r))
2194 useSchema = (val != 0);
2195}
2196
2198{
2199 SQLRETURN r;
2200 QVarLengthArray<SQLTCHAR, 200> serverString(200);
2201 SQLSMALLINT t;
2202
2203 r = SQLGetInfo(hDbc,
2204 SQL_DBMS_NAME,
2205 serverString.data(),
2206 SQLSMALLINT(serverString.size() * sizeof(SQLTCHAR)),
2207 &t);
2208 if (SQL_SUCCEEDED(r)) {
2209 const QString serverType = fromSQLTCHAR(serverString, t / sizeof(SQLTCHAR));
2210 if (serverType.contains("PostgreSQL"_L1, Qt::CaseInsensitive))
2212 else if (serverType.contains("Oracle"_L1, Qt::CaseInsensitive))
2214 else if (serverType.contains("MySql"_L1, Qt::CaseInsensitive))
2216 else if (serverType.contains("Microsoft SQL Server"_L1, Qt::CaseInsensitive))
2218 else if (serverType.contains("Sybase"_L1, Qt::CaseInsensitive))
2220 }
2221 r = SQLGetInfo(hDbc,
2222 SQL_DRIVER_NAME,
2223 serverString.data(),
2224 SQLSMALLINT(serverString.size() * sizeof(SQLTCHAR)),
2225 &t);
2226 if (SQL_SUCCEEDED(r)) {
2227 const QString serverType = fromSQLTCHAR(serverString, t / sizeof(SQLTCHAR));
2228 isFreeTDSDriver = serverType.contains("tdsodbc"_L1, Qt::CaseInsensitive);
2230 }
2231}
2232
2234{
2235 SQLUSMALLINT sup;
2236 SQLRETURN r = SQLGetFunctions(hDbc, SQL_API_SQLFETCHSCROLL, &sup);
2237 if ((!SQL_SUCCEEDED(r)) || sup != SQL_TRUE) {
2238 hasSQLFetchScroll = false;
2239 qSqlWarning("QODBCDriver::checkHasSQLFetchScroll: Driver doesn't support "
2240 "scrollable result sets, use forward only mode for queries"_L1, this);
2241 }
2242}
2243
2245{
2246 QVarLengthArray<SQLTCHAR, 2> driverResponse(2);
2247 SQLSMALLINT length;
2248 SQLRETURN r = SQLGetInfo(hDbc,
2249 SQL_MULT_RESULT_SETS,
2250 driverResponse.data(),
2251 SQLSMALLINT(driverResponse.size() * sizeof(SQLTCHAR)),
2252 &length);
2253 if (SQL_SUCCEEDED(r))
2254 hasMultiResultSets = fromSQLTCHAR(driverResponse, length / sizeof(SQLTCHAR)).startsWith(u'Y');
2255}
2256
2258{
2259 SQLINTEGER columnSize;
2260 SqlStmtHandle hStmt(hDbc);
2261
2262 if (!hStmt.isValid())
2263 return;
2264
2265 SQLRETURN r = SQLGetTypeInfo(hStmt.handle(), SQL_TIMESTAMP);
2266 if (SQL_SUCCEEDED(r)) {
2267 r = SQLFetch(hStmt.handle());
2268 if (SQL_SUCCEEDED(r)) {
2269 if (SQLGetData(hStmt.handle(), 3, SQL_INTEGER, &columnSize, sizeof(columnSize), 0) == SQL_SUCCESS)
2270 datetimePrecision = (int)columnSize;
2271 }
2272 }
2273}
2274
2276{
2277 return new QODBCResult(this);
2278}
2279
2281{
2282 Q_D(QODBCDriver);
2283 if (!isOpen()) {
2284 qSqlWarning("QODBCDriver::beginTransaction: Database not open"_L1, d);
2285 return false;
2286 }
2287 SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
2288 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
2289 SQL_ATTR_AUTOCOMMIT,
2290 (SQLPOINTER)size_t(ac),
2291 sizeof(ac));
2292 if (r != SQL_SUCCESS) {
2293 setLastError(qMakeError(tr("Unable to disable autocommit"),
2295 return false;
2296 }
2297 return true;
2298}
2299
2301{
2302 Q_D(QODBCDriver);
2303 if (!isOpen()) {
2304 qSqlWarning("QODBCDriver::commitTransaction: Database not open"_L1, d);
2305 return false;
2306 }
2307 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
2308 d->hDbc,
2309 SQL_COMMIT);
2310 if (r != SQL_SUCCESS) {
2311 setLastError(qMakeError(tr("Unable to commit transaction"),
2313 return false;
2314 }
2315 return endTrans();
2316}
2317
2319{
2320 Q_D(QODBCDriver);
2321 if (!isOpen()) {
2322 qSqlWarning("QODBCDriver::rollbackTransaction: Database not open"_L1, d);
2323 return false;
2324 }
2325 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
2326 d->hDbc,
2327 SQL_ROLLBACK);
2328 if (r != SQL_SUCCESS) {
2329 setLastError(qMakeError(tr("Unable to rollback transaction"),
2331 return false;
2332 }
2333 return endTrans();
2334}
2335
2336bool QODBCDriver::endTrans()
2337{
2338 Q_D(QODBCDriver);
2339 SQLUINTEGER ac(SQL_AUTOCOMMIT_ON);
2340 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
2341 SQL_ATTR_AUTOCOMMIT,
2342 (SQLPOINTER)size_t(ac),
2343 sizeof(ac));
2344 if (r != SQL_SUCCESS) {
2345 setLastError(qMakeError(tr("Unable to enable autocommit"), QSqlError::TransactionError, d));
2346 return false;
2347 }
2348 return true;
2349}
2350
2352{
2353 Q_D(const QODBCDriver);
2354 QStringList tl;
2355 if (!isOpen())
2356 return tl;
2357
2358 SqlStmtHandle hStmt(d->hDbc);
2359 if (!hStmt.isValid()) {
2360 qSqlWarning("QODBCDriver::tables: Unable to allocate handle"_L1, d);
2361 return tl;
2362 }
2363 SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
2364 SQL_ATTR_CURSOR_TYPE,
2365 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2366 SQL_IS_UINTEGER);
2367 QStringList tableType;
2368 if (type & QSql::Tables)
2369 tableType += "TABLE"_L1;
2370 if (type & QSql::Views)
2371 tableType += "VIEW"_L1;
2373 tableType += "SYSTEM TABLE"_L1;
2374 if (tableType.isEmpty())
2375 return tl;
2376
2377 {
2378 auto joinedTableTypeString = toSQLTCHAR(tableType.join(u','));
2379
2380 r = SQLTables(hStmt.handle(),
2381 nullptr, 0,
2382 nullptr, 0,
2383 nullptr, 0,
2384 joinedTableTypeString.data(), joinedTableTypeString.size());
2385 }
2386
2387 if (r != SQL_SUCCESS)
2388 qSqlWarning("QODBCDriver::tables Unable to execute table list"_L1,
2389 hStmt.handle());
2390
2391 r = d->sqlFetchNext(hStmt);
2392 if (!SQL_SUCCEEDED(r) && r != SQL_NO_DATA) {
2393 qSqlWarning("QODBCDriver::tables failed to retrieve table/view list"_L1,
2394 hStmt.handle());
2395 return QStringList();
2396 }
2397
2398 while (r == SQL_SUCCESS) {
2399 tl.append(qGetStringData(hStmt.handle(), 2, -1, d->unicode).toString());
2400 r = d->sqlFetchNext(hStmt);
2401 }
2402
2403 return tl;
2404}
2405
2407{
2408 Q_D(const QODBCDriver);
2409 QSqlIndex index(tablename);
2410 if (!isOpen())
2411 return index;
2412 bool usingSpecialColumns = false;
2413 QSqlRecord rec = record(tablename);
2414
2415 SqlStmtHandle hStmt(d->hDbc);
2416 if (!hStmt.isValid()) {
2417 qSqlWarning("QODBCDriver::primaryIndex: Unable to allocate handle"_L1, d);
2418 return index;
2419 }
2420 QString catalog, schema, table;
2421 d->splitTableQualifier(tablename, catalog, schema, table);
2422
2423 SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
2424 SQL_ATTR_CURSOR_TYPE,
2425 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2426 SQL_IS_UINTEGER);
2427 {
2428 auto c = toSQLTCHAR(catalog);
2429 auto s = toSQLTCHAR(schema);
2430 auto t = toSQLTCHAR(table);
2431 r = SQLPrimaryKeys(hStmt.handle(),
2432 catalog.isEmpty() ? nullptr : c.data(), c.size(),
2433 schema.isEmpty() ? nullptr : s.data(), s.size(),
2434 t.data(), t.size());
2435 }
2436
2437 // if the SQLPrimaryKeys() call does not succeed (e.g the driver
2438 // does not support it) - try an alternative method to get hold of
2439 // the primary index (e.g MS Access and FoxPro)
2440 if (r != SQL_SUCCESS) {
2441 auto c = toSQLTCHAR(catalog);
2442 auto s = toSQLTCHAR(schema);
2443 auto t = toSQLTCHAR(table);
2444 r = SQLSpecialColumns(hStmt.handle(),
2445 SQL_BEST_ROWID,
2446 catalog.isEmpty() ? nullptr : c.data(), c.size(),
2447 schema.isEmpty() ? nullptr : s.data(), s.size(),
2448 t.data(), t.size(),
2449 SQL_SCOPE_CURROW,
2450 SQL_NULLABLE);
2451
2452 if (r != SQL_SUCCESS) {
2453 qSqlWarning("QODBCDriver::primaryIndex: Unable to execute primary key list"_L1,
2454 hStmt.handle());
2455 } else {
2456 usingSpecialColumns = true;
2457 }
2458 }
2459
2460 r = d->sqlFetchNext(hStmt);
2461
2462 int fakeId = 0;
2463 QString cName, idxName;
2464 // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
2465 while (r == SQL_SUCCESS) {
2466 if (usingSpecialColumns) {
2467 cName = qGetStringData(hStmt.handle(), 1, -1, d->unicode).toString(); // column name
2468 idxName = QString::number(fakeId++); // invent a fake index name
2469 } else {
2470 cName = qGetStringData(hStmt.handle(), 3, -1, d->unicode).toString(); // column name
2471 idxName = qGetStringData(hStmt.handle(), 5, -1, d->unicode).toString(); // pk index name
2472 }
2473 index.append(rec.field(cName));
2474 index.setName(idxName);
2475
2476 r = d->sqlFetchNext(hStmt);
2477 }
2478 return index;
2479}
2480
2482{
2483 Q_D(const QODBCDriver);
2484 QSqlRecord fil;
2485 if (!isOpen())
2486 return fil;
2487
2488 SqlStmtHandle hStmt(d->hDbc);
2489 if (!hStmt.isValid()) {
2490 qSqlWarning("QODBCDriver::record: Unable to allocate handle"_L1, d);
2491 return fil;
2492 }
2493
2494 QString catalog, schema, table;
2495 d->splitTableQualifier(tablename, catalog, schema, table);
2496
2497 SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
2498 SQL_ATTR_CURSOR_TYPE,
2499 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2500 SQL_IS_UINTEGER);
2501 {
2502 auto c = toSQLTCHAR(catalog);
2503 auto s = toSQLTCHAR(schema);
2504 auto t = toSQLTCHAR(table);
2505 r = SQLColumns(hStmt.handle(),
2506 catalog.isEmpty() ? nullptr : c.data(), c.size(),
2507 schema.isEmpty() ? nullptr : s.data(), s.size(),
2508 t.data(), t.size(),
2509 nullptr,
2510 0);
2511 }
2512 if (r != SQL_SUCCESS)
2513 qSqlWarning("QODBCDriver::record: Unable to execute column list"_L1, hStmt.handle());
2514
2515 r = d->sqlFetchNext(hStmt);
2516 // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
2517 while (r == SQL_SUCCESS) {
2518 fil.append(qMakeFieldInfo(hStmt.handle(), d));
2519 r = d->sqlFetchNext(hStmt);
2520 }
2521 return fil;
2522}
2523
2525 bool trimStrings) const
2526{
2527 QString r;
2528 if (field.isNull()) {
2529 r = "NULL"_L1;
2530 } else if (field.metaType().id() == QMetaType::QDateTime) {
2531 // Use an escape sequence for the datetime fields
2532 const QDateTime dateTime = field.value().toDateTime();
2533 if (dateTime.isValid()) {
2534 const QDate dt = dateTime.date();
2535 const QTime tm = dateTime.time();
2536 // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
2537 r = "{ ts '"_L1 +
2538 QString::number(dt.year()) + u'-' +
2539 QString::number(dt.month()).rightJustified(2, u'0', true) +
2540 u'-' +
2541 QString::number(dt.day()).rightJustified(2, u'0', true) +
2542 u' ' +
2543 tm.toString() +
2544 "' }"_L1;
2545 } else
2546 r = "NULL"_L1;
2547 } else if (field.metaType().id() == QMetaType::QByteArray) {
2548 const QByteArray ba = field.value().toByteArray();
2549 r.reserve((ba.size() + 1) * 2);
2550 r = "0x"_L1;
2551 for (const char c : ba) {
2552 const uchar s = uchar(c);
2555 }
2556 } else {
2557 r = QSqlDriver::formatValue(field, trimStrings);
2558 }
2559 return r;
2560}
2561
2563{
2564 Q_D(const QODBCDriver);
2565 return QVariant(QMetaType::fromType<SQLHANDLE>(), &d->hDbc);
2566}
2567
2569{
2570 Q_D(const QODBCDriver);
2571 QChar quote = const_cast<QODBCDriverPrivate*>(d)->quoteChar();
2572 QString res = identifier;
2573 if (!identifier.isEmpty() && !identifier.startsWith(quote) && !identifier.endsWith(quote) ) {
2574 const QString quoteStr(quote);
2575 res.replace(quote, quoteStr + quoteStr);
2576 res.replace(u'.', quoteStr + u'.' + quoteStr);
2577 res = quote + res + quote;
2578 }
2579 return res;
2580}
2581
2583{
2584 Q_D(const QODBCDriver);
2585 QChar quote = const_cast<QODBCDriverPrivate*>(d)->quoteChar();
2586 return identifier.size() > 2
2587 && identifier.startsWith(quote) //left delimited
2588 && identifier.endsWith(quote); //right delimited
2589}
2590
2592
2593#include "moc_qsql_odbc_p.cpp"
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
void reserve(qsizetype size)
Attempts to allocate memory for at least size bytes.
Definition qbytearray.h:634
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
qsizetype length() const noexcept
Same as size().
Definition qbytearray.h:499
char at(qsizetype i) const
Returns the byte at index position i in the byte array.
Definition qbytearray.h:600
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
\inmodule QtCore
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
\inmodule QtCore\reentrant
Definition qdatetime.h:283
QTime time() const
Returns the time part of the datetime.
bool isValid() const
Returns true if this datetime represents a definite moment, otherwise false.
QDate date() const
Returns the date part of the datetime.
\inmodule QtCore \reentrant
Definition qdatetime.h:29
int year() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QList< T > & fill(parameter_type t, qsizetype size=-1)
Definition qlist.h:903
\inmodule QtCore
Definition qmetatype.h:341
int id(int=0) const
Definition qmetatype.h:475
bool setConnectionOptions(const QString &connOpts)
void checkHasSQLFetchScroll()
void splitTableQualifier(const QString &qualifier, QString &catalog, QString &schema, QString &table) const
SQLRETURN sqlFetchNext(const SqlStmtHandle &hStmt) const
bool checkDriver() const
QString adjustCase(const QString &) const
void checkDateTimePrecision()
QString formatValue(const QSqlField &field, bool trimStrings) const override
Returns a string representation of the field value for the database.
QSqlResult * createResult() const override
Creates an empty SQL result on the database.
QODBCDriver(QObject *parent=nullptr)
QString escapeIdentifier(const QString &identifier, IdentifierType type) const override
Returns the identifier escaped according to the database rules.
virtual ~QODBCDriver()
QVariant handle() const override
Returns the low-level database handle wrapped in a QVariant or an invalid variant if there is no hand...
bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const override
Returns whether identifier is escaped according to the database rules.
bool beginTransaction() override
This function is called to begin a transaction.
void close() override
Derived classes must reimplement this pure virtual function in order to close the database connection...
bool rollbackTransaction() override
This function is called to rollback a transaction.
QSqlIndex primaryIndex(const QString &tablename) const override
Returns the primary index for table tableName.
bool hasFeature(DriverFeature f) const override
Returns true if the driver supports feature feature; otherwise returns false.
bool open(const QString &db, const QString &user, const QString &password, const QString &host, int port, const QString &connOpts) override
Derived classes must reimplement this pure virtual function to open a database connection on database...
bool commitTransaction() override
This function is called to commit a transaction.
QSqlRecord record(const QString &tablename) const override
Returns a QSqlRecord populated with the names of the fields in table tableName.
QStringList tables(QSql::TableType) const override
Returns a list of the names of the tables in the database.
SQLHANDLE dpDbc() const
bool isStmtHandleValid() const
SQLHANDLE dpEnv() const
QVariantList fieldCache
void updateStmtHandleState()
bool prepare(const QString &query) override
Prepares the given query for execution; the query will normally use placeholders so that it can be ex...
int numRowsAffected() override
Returns the number of rows affected by the last query executed, or -1 if it cannot be determined or i...
QVariant data(int field) override
Returns the data for field index in the current row as a QVariant.
virtual ~QODBCResult()
QSqlRecord record() const override
Returns the current record if the query is active; otherwise returns an empty QSqlRecord.
bool nextResult() override
int size() override
Returns the size of the SELECT result, or -1 if it cannot be determined or if the query is not a SELE...
bool fetchFirst() override
Positions the result to the first record (row 0) in the result.
bool exec() override
Executes the query, returning true if successful; otherwise returns false.
void detachFromResultSet() override
QVariant lastInsertId() const override
Returns the object ID of the most recent inserted row if the database supports it.
void virtual_hook(int id, void *data) override
bool reset(const QString &query) override
Sets the result to use the SQL statement query for subsequent data retrieval.
bool fetchLast() override
Positions the result to the last record (last row) in the result.
bool isNull(int field) override
Returns true if the field at position index in the current row is null; otherwise returns false.
bool fetchPrevious() override
Positions the result to the previous record (row) in the result.
bool fetchNext() override
Positions the result to the next available record (row) in the result.
bool fetch(int i) override
Positions the result to an arbitrary (zero-based) row index.
QODBCResult(const QODBCDriver *db)
QVariant handle() const override
Returns the low-level database handle for this result set wrapped in a QVariant or an invalid QVarian...
\inmodule QtCore
Definition qobject.h:103
QSqlDriverPrivate(QSqlDriver::DbmsType type=QSqlDriver::UnknownDbms)
QSqlDriver::DbmsType dbmsType
The QSqlDriver class is an abstract base class for accessing specific SQL databases.
Definition qsqldriver.h:26
virtual QString formatValue(const QSqlField &field, bool trimStrings=false) const
Returns a string representation of the field value for the database.
IdentifierType
This enum contains a list of SQL identifier types.
Definition qsqldriver.h:41
DriverFeature
This enum contains a list of features a driver might support.
Definition qsqldriver.h:33
@ PositionalPlaceholders
Definition qsqldriver.h:34
@ LowPrecisionNumbers
Definition qsqldriver.h:35
@ EventNotifications
Definition qsqldriver.h:36
@ PreparedQueries
Definition qsqldriver.h:33
@ NamedPlaceholders
Definition qsqldriver.h:34
@ BatchOperations
Definition qsqldriver.h:35
@ MultipleResultSets
Definition qsqldriver.h:36
virtual void setLastError(const QSqlError &e)
This function is used to set the value of the last error, error, that occurred on the database.
virtual bool isOpen() const
Returns true if the database connection is open; otherwise returns false.
virtual void setOpenError(bool e)
This function sets the open error state of the database to error.
virtual void setOpen(bool o)
This function sets the open state of the database to open.
The QSqlError class provides SQL database error information.
Definition qsqlerror.h:17
ErrorType
This enum type describes the context in which the error occurred, e.g., a connection error,...
Definition qsqlerror.h:19
@ StatementError
Definition qsqlerror.h:22
@ TransactionError
Definition qsqlerror.h:23
@ ConnectionError
Definition qsqlerror.h:21
The QSqlField class manipulates the fields in SQL database tables and views.
Definition qsqlfield.h:19
QMetaType metaType
Definition qsqlfield.h:28
bool isNull() const
Returns true if the field's value is NULL; otherwise returns false.
QVariant value
Definition qsqlfield.h:24
The QSqlIndex class provides functions to manipulate and describe database indexes.
Definition qsqlindex.h:18
The QSqlQuery class provides a means of executing and manipulating SQL statements.
Definition qsqlquery.h:24
The QSqlRecord class encapsulates a database record.
Definition qsqlrecord.h:20
QSqlField field(int i) const
Returns the field at position index.
static bool isVariantNull(const QVariant &variant)
The QSqlResult class provides an abstract interface for accessing data from specific SQL databases.
Definition qsqlresult.h:22
bool isForwardOnly() const
Returns true if you can only scroll forward through the result set; otherwise returns false.
QSql::ParamType bindValueType(const QString &placeholder) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
virtual void virtual_hook(int id, void *data)
int at() const
Returns the current (zero-based) row position of the result.
bool isSelect() const
Returns true if the current result is from a SELECT statement; otherwise returns false.
virtual void setAt(int at)
This function is provided for derived classes to set the internal (zero-based) row position to index.
virtual void setSelect(bool s)
This function is provided for derived classes to indicate whether or not the current statement is a S...
bool hasOutValues() const
Returns true if at least one of the query's bound values is a QSql::Out or a QSql::InOut; otherwise r...
virtual void setActive(bool a)
This function is provided for derived classes to set the internal active state to active.
QVariantList & boundValues(QT6_DECL_NEW_OVERLOAD)
QSql::NumericalPrecisionPolicy numericalPrecisionPolicy() const
virtual void setLastError(const QSqlError &e)
This function is provided for derived classes to set the last error to error.
const QSqlDriver * driver() const
Returns the driver associated with the result.
virtual void setForwardOnly(bool forward)
Sets forward only mode to forward.
bool isActive() const
Returns true if the result has records to be retrieved; otherwise returns false.
Encoding
\value Utf8 Create a converter to or from UTF-8 \value Utf16 Create a converter to or from UTF-16.
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
Q_CORE_EXPORT QList< QStringView > split(QStringView sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the view into substring views wherever sep occurs, and returns the list of those string views.
Definition qstring.cpp:8249
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
static QString fromUtf16(const char16_t *, qsizetype size=-1)
Definition qstring.cpp:6045
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromUcs4(const char32_t *, qsizetype size=-1)
Definition qstring.cpp:6077
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1246
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5506
QString toLower() const &
Definition qstring.h:435
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1369
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
QByteArray toUtf8() const &
Definition qstring.h:634
QString toUpper() const &
Definition qstring.h:439
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
\inmodule QtCore \reentrant
Definition qdatetime.h:215
int hour() const
Returns the hour part (0 to 23) of the time.
\inmodule QtCore
Definition qvariant.h:65
QDateTime toDateTime() const
Returns the variant as a QDateTime if the variant has userType() \l QMetaType::QDateTime,...
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
bool isNull() const
Returns true if this is a null variant, false otherwise.
QByteArray toByteArray() const
Returns the variant as a QByteArray if the variant has userType() \l QMetaType::QByteArray or \l QMet...
SqlStmtHandle(SQLHANDLE hDbc)
Definition qsql_odbc.cpp:50
SQLHANDLE handle() const
Definition qsql_odbc.cpp:59
bool isValid() const
Definition qsql_odbc.cpp:63
SQLHANDLE stmtHandle
Definition qsql_odbc.cpp:67
#define this
Definition dialogs.cpp:9
QString str
[2]
QStyleOptionButton opt
else opt state
[0]
@ AfterLastRow
Definition qtsqlglobal.h:22
@ BeforeFirstRow
Definition qtsqlglobal.h:21
@ SystemTables
Definition qtsqlglobal.h:37
@ Views
Definition qtsqlglobal.h:38
@ Tables
Definition qtsqlglobal.h:36
@ InOut
Definition qtsqlglobal.h:29
@ LowPrecisionInt32
Definition qtsqlglobal.h:44
@ LowPrecisionDouble
Definition qtsqlglobal.h:46
@ LowPrecisionInt64
Definition qtsqlglobal.h:45
@ HighPrecision
Definition qtsqlglobal.h:48
Combined button and popup list for selecting options.
constexpr char toHexLower(char32_t value) noexcept
Definition qtools_p.h:32
Definition qcompare.h:63
@ CaseInsensitive
#define Q_FALLTHROUGH()
QList< QString > QStringList
Constructs a string list that contains the given string, str.
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define SIZE(large, small, mini)
auto qPow(T1 x, T2 y)
Definition qmath.h:180
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLenum GLsizei GLsizei GLint * values
[15]
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLuint64 GLenum void * handle
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum GLuint buffer
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint64 GLenum handleType
GLuint GLsizei const GLchar * message
GLuint name
GLenum GLenum GLsizei void GLsizei void * column
GLdouble s
[6]
Definition qopenglext.h:235
GLboolean reset
GLenum query
GLenum func
Definition qopenglext.h:663
GLuint res
const GLubyte * c
GLuint GLfloat * val
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLenum GLenum input
GLenum GLenum GLsizei void * table
GLenum GLint GLint * precision
GLsizei const GLchar *const * string
[0]
Definition qopenglext.h:694
static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
static QSqlField qMakeFieldInfo(const QDB2ResultPrivate *d, int i)
Definition qsql_db2.cpp:276
static void qSqlWarning(const QString &message, const QDB2DriverPrivate *d)
Definition qsql_db2.cpp:191
static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool &isNull)
Definition qsql_db2.cpp:371
static QSqlError qMakeError(const QString &err, QSqlError::ErrorType type, const QDB2DriverPrivate *p)
Definition qsql_db2.cpp:203
#define SQL_SS_TIME2
Definition qsql_odbc.cpp:36
QStringConverter::Encoding encodingForSqlTChar()
Definition qsql_odbc.cpp:89
static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, const QODBCDriverPrivate *p)
static bool isAutoValue(const SQLHANDLE hStmt, int column)
static SQLRETURN qt_string_SQLSetConnectAttr(SQLHDBC handle, SQLINTEGER attr, QStringView val)
static QVariant qGetStringData(SQLHANDLE hStmt, SQLUSMALLINT column, int colSize, bool unicode)
static DiagRecord combineRecords(const QList< DiagRecord > &records)
static QList< DiagRecord > qODBCWarn(const SQLHANDLE hStmt, const SQLHANDLE envHandle=nullptr, const SQLHANDLE pDbC=nullptr)
static QMetaType qDecodeODBCType(SQLSMALLINT sqltype, bool isSigned=true)
static QString fromSQLTCHAR(const C &input, qsizetype size=-1)
Definition qsql_odbc.cpp:71
static QVariant qGetBinaryData(SQLHANDLE hStmt, int column)
static constexpr int COLNAMESIZE
Definition qsql_odbc.cpp:42
static QVariant qGetBigIntData(SQLHANDLE hStmt, int column, bool isSigned=true)
static QString errorStringFromDiagRecords(const QList< DiagRecord > &records)
static QSqlError errorFromDiagRecords(const QString &err, QSqlError::ErrorType type, const QList< DiagRecord > &records)
static QSqlError qMakeError(const QString &err, QSqlError::ErrorType type, const QODBCResultPrivate *p)
static QVariant qGetDoubleData(SQLHANDLE hStmt, int column)
static QVariant getStringDataImpl(SQLHANDLE hStmt, SQLUSMALLINT column, qsizetype colSize, SQLSMALLINT targetType)
static constexpr SQLSMALLINT qParamType[4]
Definition qsql_odbc.cpp:45
static QVarLengthArray< SQLTCHAR > toSQLTCHAR(QStringView input)
static void qSqlWarning(const QString &message, T &&val)
static QVariant qGetIntData(SQLHANDLE hStmt, int column, bool isSigned=true)
static constexpr SQLSMALLINT TABLENAMESIZE
Definition qsql_odbc.cpp:43
static QList< DiagRecord > qWarnODBCHandle(int handleType, SQLHANDLE handle)
static size_t qGetODBCVersion(const QString &connOpts)
#define Q_DECLARE_SQLDRIVER_PRIVATE(Class)
SSL_CTX int void * arg
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
#define tr(X)
unsigned char uchar
Definition qtypes.h:32
unsigned long ulong
Definition qtypes.h:35
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
static QString quote(const QString &str)
ReturnedValue read(const char *data)
QByteArray ba
[0]
QObject::connect nullptr
QMimeDatabase db
[0]
QDateTime dateTime
[12]
QHostInfo info
[0]
QString description
QString errorCode
QString sqlState
\inmodule QtCore \reentrant
Definition qchar.h:18