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