Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qsql_psql.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_psql_p.h"
5
6#include <qcoreapplication.h>
7#include <qvariant.h>
8#include <qdatetime.h>
10#include <qsqlerror.h>
11#include <qsqlfield.h>
12#include <qsqlindex.h>
13#include <qsqlrecord.h>
14#include <qsqlquery.h>
15#include <qsocketnotifier.h>
16#include <qstringlist.h>
17#include <qlocale.h>
18#include <QtSql/private/qsqlresult_p.h>
19#include <QtSql/private/qsqldriver_p.h>
20#include <QtCore/private/qlocale_tools_p.h>
21
22#include <queue>
23
24#include <libpq-fe.h>
25#include <pg_config.h>
26
27#include <cmath>
28
29// workaround for postgres defining their OIDs in a private header file
30#define QBOOLOID 16
31#define QINT8OID 20
32#define QINT2OID 21
33#define QINT4OID 23
34#define QNUMERICOID 1700
35#define QFLOAT4OID 700
36#define QFLOAT8OID 701
37#define QABSTIMEOID 702
38#define QRELTIMEOID 703
39#define QDATEOID 1082
40#define QTIMEOID 1083
41#define QTIMETZOID 1266
42#define QTIMESTAMPOID 1114
43#define QTIMESTAMPTZOID 1184
44#define QOIDOID 2278
45#define QBYTEAOID 17
46#define QREGPROCOID 24
47#define QXIDOID 28
48#define QCIDOID 29
49
50#define QBITOID 1560
51#define QVARBITOID 1562
52
53#define VARHDRSZ 4
54
55/* This is a compile time switch - if PQfreemem is declared, the compiler will use that one,
56 otherwise it'll run in this template */
57template <typename T>
58inline void PQfreemem(T *t, int = 0) { free(t); }
59
62
65
67
68using namespace Qt::StringLiterals;
69
70inline void qPQfreemem(void *buffer)
71{
73}
74
75/* Missing declaration of PGRES_SINGLE_TUPLE for PSQL below 9.2 */
76#if !defined PG_VERSION_NUM || PG_VERSION_NUM-0 < 90200
77static const int PGRES_SINGLE_TUPLE = 9;
78#endif
79
80typedef int StatementId;
82
84
85class QPSQLResult final : public QSqlResult
86{
87 Q_DECLARE_PRIVATE(QPSQLResult)
88
89public:
92
93 QVariant handle() const override;
94 void virtual_hook(int id, void *data) override;
95
96protected:
97 void cleanup();
98 bool fetch(int i) override;
99 bool fetchFirst() override;
100 bool fetchLast() override;
101 bool fetchNext() override;
102 bool nextResult() override;
103 QVariant data(int i) override;
104 bool isNull(int field) override;
105 bool reset(const QString &query) override;
106 int size() override;
107 int numRowsAffected() override;
108 QSqlRecord record() const override;
109 QVariant lastInsertId() const override;
110 bool prepare(const QString &query) override;
111 bool exec() override;
112};
113
115{
116 Q_DECLARE_PUBLIC(QPSQLDriver)
117public:
119
121 PGconn *connection = nullptr;
122 QSocketNotifier *sn = nullptr;
125 int stmtCount = 0;
126 mutable bool pendingNotifyCheck = false;
127 bool hasBackslashEscape = false;
128
130 PGresult *exec(const char *stmt);
131 PGresult *exec(const QString &stmt);
132 StatementId sendQuery(const QString &stmt);
133 bool setSingleRowMode() const;
134 PGresult *getResult(StatementId stmtId) const;
135 void finishQuery(StatementId stmtId);
136 void discardResults() const;
138 void checkPendingNotifications() const;
140 bool setEncodingUtf8();
141 void setDatestyle();
142 void setByteaOutput();
145};
146
148{
149 const QString query =
150 QStringLiteral("SELECT pg_class.relname, pg_namespace.nspname FROM pg_class "
151 "LEFT JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid) "
152 "WHERE (pg_class.relkind = '") + type +
153 QStringLiteral("') AND (pg_class.relname !~ '^Inv') "
154 "AND (pg_class.relname !~ '^pg_') "
155 "AND (pg_namespace.nspname != 'information_schema')");
156 t.exec(query);
157 while (t.next()) {
158 QString schema = t.value(1).toString();
159 if (schema.isEmpty() || schema == "public"_L1)
160 tl.append(t.value(0).toString());
161 else
162 tl.append(t.value(0).toString().prepend(u'.').prepend(schema));
163 }
164}
165
167{
168 // PQexec() silently discards any prior query results that the application didn't eat.
169 PGresult *result = PQexec(connection, stmt);
172 return result;
173}
174
176{
177 return exec(stmt.toUtf8().constData());
178}
179
181{
182 // Discard any prior query results that the application didn't eat.
183 // This is required for PQsendQuery()
185 const int result = PQsendQuery(connection, stmt.toUtf8().constData());
187 return currentStmtId;
188}
189
191{
192 // Activates single-row mode for last sent query, see:
193 // https://www.postgresql.org/docs/9.2/static/libpq-single-row-mode.html
194 // This method should be called immediately after the sendQuery() call.
195#if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 90200
196 return PQsetSingleRowMode(connection) > 0;
197#else
198 return false;
199#endif
200}
201
203{
204 // Make sure the results of stmtId weren't discaded. This might
205 // happen for forward-only queries if somebody executed another
206 // SQL query on the same db connection.
207 if (stmtId != currentStmtId) {
208 // If you change the following warning, remember to update it
209 // on sql-driver.html page too.
210 qWarning("QPSQLDriver::getResult: Query results lost - "
211 "probably discarded on executing another SQL query.");
212 return nullptr;
213 }
214 PGresult *result = PQgetResult(connection);
216 return result;
217}
218
220{
221 if (stmtId != InvalidStatementId && stmtId == currentStmtId) {
224 }
225}
226
228{
229 while (PGresult *result = PQgetResult(connection))
230 PQclear(result);
231}
232
234{
235 int stmtId = ++stmtCount;
236 if (stmtId <= 0)
237 stmtId = stmtCount = 1;
238 return stmtId;
239}
240
242{
243 Q_Q(const QPSQLDriver);
244 if (seid.size() && !pendingNotifyCheck) {
245 pendingNotifyCheck = true;
246 QMetaObject::invokeMethod(const_cast<QPSQLDriver*>(q), "_q_handleNotification", Qt::QueuedConnection);
247 }
248}
249
251{
252 Q_DECLARE_PUBLIC(QPSQLResult)
253public:
256
257 QString fieldSerial(qsizetype i) const override { return u'$' + QString::number(i + 1); }
259
260 std::queue<PGresult*> nextResultSets;
262 PGresult *result = nullptr;
264 int currentSize = -1;
265 bool canFetchMoreRows = false;
267
268 bool processResults();
269};
270
272 const QPSQLDriverPrivate *p, PGresult *result = nullptr)
273{
274 const char *s = PQerrorMessage(p->connection);
276 QString errorCode;
277 if (result) {
278 errorCode = QString::fromLatin1(PQresultErrorField(result, PG_DIAG_SQLSTATE));
279 msg += QString::fromLatin1("(%1)").arg(errorCode);
280 }
281 return QSqlError("QPSQL: "_L1 + err, msg, type, errorCode);
282}
283
285{
286 Q_Q(QPSQLResult);
287 if (!result) {
288 q->setSelect(false);
289 q->setActive(false);
290 currentSize = -1;
291 canFetchMoreRows = false;
292 if (stmtId != drv_d_func()->currentStmtId) {
293 q->setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
294 "Query results lost - probably discarded on executing "
295 "another SQL query."), QSqlError::StatementError, drv_d_func(), result));
296 }
297 return false;
298 }
299 int status = PQresultStatus(result);
300 switch (status) {
301 case PGRES_TUPLES_OK:
302 q->setSelect(true);
303 q->setActive(true);
304 currentSize = q->isForwardOnly() ? -1 : PQntuples(result);
305 canFetchMoreRows = false;
306 return true;
308 q->setSelect(true);
309 q->setActive(true);
310 currentSize = -1;
311 canFetchMoreRows = true;
312 return true;
313 case PGRES_COMMAND_OK:
314 q->setSelect(false);
315 q->setActive(true);
316 currentSize = -1;
317 canFetchMoreRows = false;
318 return true;
319 default:
320 break;
321 }
322 q->setSelect(false);
323 q->setActive(false);
324 currentSize = -1;
325 canFetchMoreRows = false;
326 q->setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
327 "Unable to create query"), QSqlError::StatementError, drv_d_func(), result));
328 return false;
329}
330
332{
334 switch (t) {
335 case QBOOLOID:
336 type = QMetaType::Bool;
337 break;
338 case QINT8OID:
339 type = QMetaType::LongLong;
340 break;
341 case QINT2OID:
342 case QINT4OID:
343 case QOIDOID:
344 case QREGPROCOID:
345 case QXIDOID:
346 case QCIDOID:
347 type = QMetaType::Int;
348 break;
349 case QNUMERICOID:
350 case QFLOAT4OID:
351 case QFLOAT8OID:
352 type = QMetaType::Double;
353 break;
354 case QABSTIMEOID:
355 case QRELTIMEOID:
356 case QDATEOID:
357 type = QMetaType::QDate;
358 break;
359 case QTIMEOID:
360 case QTIMETZOID:
361 type = QMetaType::QTime;
362 break;
363 case QTIMESTAMPOID:
364 case QTIMESTAMPTZOID:
365 type = QMetaType::QDateTime;
366 break;
367 case QBYTEAOID:
368 type = QMetaType::QByteArray;
369 break;
370 default:
371 type = QMetaType::QString;
372 break;
373 }
374 return QMetaType(type);
375}
376
378{
379 if (drv_d_func()) {
380 const QString stmt = QStringLiteral("DEALLOCATE ") + preparedStmtId;
381 PGresult *result = drv_d_func()->exec(stmt);
382
383 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
384 const QString msg = QString::fromUtf8(PQerrorMessage(drv_d_func()->connection));
385 qWarning("Unable to free statement: %ls", qUtf16Printable(msg));
386 }
387 PQclear(result);
388 }
390}
391
394{
395 Q_D(QPSQLResult);
396 d->preparedQueriesEnabled = db->hasFeature(QSqlDriver::PreparedQueries);
397}
398
400{
401 Q_D(QPSQLResult);
402 cleanup();
403
404 if (d->preparedQueriesEnabled && !d->preparedStmtId.isNull())
405 d->deallocatePreparedStmt();
406}
407
409{
410 Q_D(const QPSQLResult);
411 return QVariant::fromValue(d->result);
412}
413
415{
416 Q_D(QPSQLResult);
417 if (d->result)
418 PQclear(d->result);
419 d->result = nullptr;
420 while (!d->nextResultSets.empty()) {
421 PQclear(d->nextResultSets.front());
422 d->nextResultSets.pop();
423 }
424 if (d->stmtId != InvalidStatementId) {
425 if (d->drv_d_func())
426 d->drv_d_func()->finishQuery(d->stmtId);
427 }
428 d->stmtId = InvalidStatementId;
430 d->currentSize = -1;
431 d->canFetchMoreRows = false;
432 setActive(false);
433}
434
436{
437 Q_D(const QPSQLResult);
438 if (!isActive())
439 return false;
440 if (i < 0)
441 return false;
442 if (at() == i)
443 return true;
444
445 if (isForwardOnly()) {
446 if (i < at())
447 return false;
448 bool ok = true;
449 while (ok && i > at())
450 ok = fetchNext();
451 return ok;
452 }
453
454 if (i >= d->currentSize)
455 return false;
456 setAt(i);
457 return true;
458}
459
461{
462 Q_D(const QPSQLResult);
463 if (!isActive())
464 return false;
465 if (at() == 0)
466 return true;
467
468 if (isForwardOnly()) {
469 if (at() == QSql::BeforeFirstRow) {
470 // First result has been already fetched by exec() or
471 // nextResult(), just check it has at least one row.
472 if (d->result && PQntuples(d->result) > 0) {
473 setAt(0);
474 return true;
475 }
476 }
477 return false;
478 }
479
480 return fetch(0);
481}
482
484{
485 Q_D(const QPSQLResult);
486 if (!isActive())
487 return false;
488
489 if (isForwardOnly()) {
490 // Cannot seek to last row in forwardOnly mode, so we have to use brute force
491 int i = at();
492 if (i == QSql::AfterLastRow)
493 return false;
494 if (i == QSql::BeforeFirstRow)
495 i = 0;
496 while (fetchNext())
497 ++i;
498 setAt(i);
499 return true;
500 }
501
502 return fetch(d->currentSize - 1);
503}
504
506{
507 Q_D(QPSQLResult);
508 if (!isActive())
509 return false;
510
511 const int currentRow = at(); // Small optimalization
512 if (currentRow == QSql::BeforeFirstRow)
513 return fetchFirst();
514 if (currentRow == QSql::AfterLastRow)
515 return false;
516
517 if (isForwardOnly()) {
518 if (!d->canFetchMoreRows)
519 return false;
520 PQclear(d->result);
521 d->result = d->drv_d_func()->getResult(d->stmtId);
522 if (!d->result) {
524 "Unable to get result"), QSqlError::StatementError, d->drv_d_func(), d->result));
525 d->canFetchMoreRows = false;
526 return false;
527 }
528 int status = PQresultStatus(d->result);
529 switch (status) {
531 // Fetched next row of current result set
532 Q_ASSERT(PQntuples(d->result) == 1);
533 Q_ASSERT(d->canFetchMoreRows);
534 setAt(currentRow + 1);
535 return true;
536 case PGRES_TUPLES_OK:
537 // In single-row mode PGRES_TUPLES_OK means end of current result set
538 Q_ASSERT(PQntuples(d->result) == 0);
539 d->canFetchMoreRows = false;
540 return false;
541 default:
543 "Unable to get result"), QSqlError::StatementError, d->drv_d_func(), d->result));
544 d->canFetchMoreRows = false;
545 return false;
546 }
547 }
548
549 if (currentRow + 1 >= d->currentSize)
550 return false;
551 setAt(currentRow + 1);
552 return true;
553}
554
556{
557 Q_D(QPSQLResult);
558 if (!isActive())
559 return false;
560
562
563 if (isForwardOnly()) {
564 if (d->canFetchMoreRows) {
565 // Skip all rows from current result set
566 while (d->result && PQresultStatus(d->result) == PGRES_SINGLE_TUPLE) {
567 PQclear(d->result);
568 d->result = d->drv_d_func()->getResult(d->stmtId);
569 }
570 d->canFetchMoreRows = false;
571 // Check for unexpected errors
572 if (d->result && PQresultStatus(d->result) == PGRES_FATAL_ERROR)
573 return d->processResults();
574 }
575 // Fetch first result from next result set
576 if (d->result)
577 PQclear(d->result);
578 d->result = d->drv_d_func()->getResult(d->stmtId);
579 return d->processResults();
580 }
581
582 if (d->result)
583 PQclear(d->result);
584 d->result = nullptr;
585 if (!d->nextResultSets.empty()) {
586 d->result = d->nextResultSets.front();
587 d->nextResultSets.pop();
588 }
589 return d->processResults();
590}
591
593{
594 Q_D(const QPSQLResult);
595 if (i >= PQnfields(d->result)) {
596 qWarning("QPSQLResult::data: column %d out of range", i);
597 return QVariant();
598 }
599 const int currentRow = isForwardOnly() ? 0 : at();
600 int ptype = PQftype(d->result, i);
602 if (PQgetisnull(d->result, currentRow, i))
603 return QVariant(type, nullptr);
604 const char *val = PQgetvalue(d->result, currentRow, i);
605 switch (type.id()) {
606 case QMetaType::Bool:
607 return QVariant((bool)(val[0] == 't'));
608 case QMetaType::QString:
609 return QString::fromUtf8(val);
610 case QMetaType::LongLong:
611 if (val[0] == '-')
613 else
615 case QMetaType::Int:
616 return atoi(val);
617 case QMetaType::Double: {
618 if (ptype == QNUMERICOID) {
620 return QString::fromLatin1(val);
621 }
622 bool ok;
623 double dbl = qstrtod(val, nullptr, &ok);
624 if (!ok) {
625 if (qstricmp(val, "NaN") == 0)
626 dbl = qQNaN();
627 else if (qstricmp(val, "Infinity") == 0)
628 dbl = qInf();
629 else if (qstricmp(val, "-Infinity") == 0)
630 dbl = -qInf();
631 else
632 return QVariant();
633 }
634 if (ptype == QNUMERICOID) {
636 return QVariant((qlonglong)dbl);
638 return QVariant((int)dbl);
640 return QVariant(dbl);
641 }
642 return dbl;
643 }
644 case QMetaType::QDate:
645#if QT_CONFIG(datestring)
646 return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate));
647#else
649#endif
650 case QMetaType::QTime:
651#if QT_CONFIG(datestring)
652 return QVariant(QTime::fromString(QString::fromLatin1(val), Qt::ISODate));
653#else
655#endif
656 case QMetaType::QDateTime:
657#if QT_CONFIG(datestring)
658 return QVariant(QDateTime::fromString(QString::fromLatin1(val),
659 Qt::ISODate).toLocalTime());
660#else
662#endif
663 case QMetaType::QByteArray: {
664 size_t len;
665 unsigned char *data = PQunescapeBytea(reinterpret_cast<const unsigned char *>(val), &len);
666 QByteArray ba(reinterpret_cast<const char *>(data), len);
668 return QVariant(ba);
669 }
670 default:
671 qWarning("QPSQLResult::data: unknown data type");
672 }
673 return QVariant();
674}
675
676bool QPSQLResult::isNull(int field)
677{
678 Q_D(const QPSQLResult);
679 const int currentRow = isForwardOnly() ? 0 : at();
680 return PQgetisnull(d->result, currentRow, field);
681}
682
684{
685 Q_D(QPSQLResult);
686 cleanup();
687 if (!driver())
688 return false;
689 if (!driver()->isOpen() || driver()->isOpenError())
690 return false;
691
692 d->stmtId = d->drv_d_func()->sendQuery(query);
693 if (d->stmtId == InvalidStatementId) {
695 "Unable to send query"), QSqlError::StatementError, d->drv_d_func()));
696 return false;
697 }
698
699 if (isForwardOnly())
700 setForwardOnly(d->drv_d_func()->setSingleRowMode());
701
702 d->result = d->drv_d_func()->getResult(d->stmtId);
703 if (!isForwardOnly()) {
704 // Fetch all result sets right away
705 while (PGresult *nextResultSet = d->drv_d_func()->getResult(d->stmtId))
706 d->nextResultSets.push(nextResultSet);
707 }
708 return d->processResults();
709}
710
712{
713 Q_D(const QPSQLResult);
714 return d->currentSize;
715}
716
718{
719 Q_D(const QPSQLResult);
720 const char *tuples = PQcmdTuples(d->result);
721 return QByteArray::fromRawData(tuples, qstrlen(tuples)).toInt();
722}
723
725{
726 Q_D(const QPSQLResult);
727 if (d->drv_d_func()->pro >= QPSQLDriver::Version8_1) {
728 QSqlQuery qry(driver()->createResult());
729 // Most recent sequence value obtained from nextval
730 if (qry.exec(QStringLiteral("SELECT lastval();")) && qry.next())
731 return qry.value(0);
732 } else if (isActive()) {
733 Oid id = PQoidValue(d->result);
734 if (id != InvalidOid)
735 return QVariant(id);
736 }
737 return QVariant();
738}
739
741{
742 Q_D(const QPSQLResult);
744 if (!isActive() || !isSelect())
745 return info;
746
747 int count = PQnfields(d->result);
748 QSqlField f;
749 for (int i = 0; i < count; ++i) {
750 f.setName(QString::fromUtf8(PQfname(d->result, i)));
751 const int tableOid = PQftable(d->result, i);
752 // WARNING: We cannot execute any other SQL queries on
753 // the same db connection while forward-only mode is active
754 // (this would discard all results of forward-only query).
755 // So we just skip this...
756 if (tableOid != InvalidOid && !isForwardOnly()) {
757 auto &tableName = d->drv_d_func()->oidToTable[tableOid];
758 if (tableName.isEmpty()) {
759 QSqlQuery qry(driver()->createResult());
760 if (qry.exec(QStringLiteral("SELECT relname FROM pg_class WHERE pg_class.oid = %1")
761 .arg(tableOid)) && qry.next()) {
762 tableName = qry.value(0).toString();
763 }
764 }
765 f.setTableName(tableName);
766 } else {
767 f.setTableName(QString());
768 }
769 int ptype = PQftype(d->result, i);
770 f.setMetaType(qDecodePSQLType(ptype));
771 f.setValue(QVariant(f.metaType())); // only set in setType() when it's invalid before
772 int len = PQfsize(d->result, i);
773 int precision = PQfmod(d->result, i);
774
775 switch (ptype) {
776 case QTIMESTAMPOID:
777 case QTIMESTAMPTZOID:
778 precision = 3;
779 break;
780
781 case QNUMERICOID:
782 if (precision != -1) {
783 len = (precision >> 16);
784 precision = ((precision - VARHDRSZ) & 0xffff);
785 }
786 break;
787 case QBITOID:
788 case QVARBITOID:
789 len = precision;
790 precision = -1;
791 break;
792 default:
793 if (len == -1 && precision >= VARHDRSZ) {
795 precision = -1;
796 }
797 }
798
799 f.setLength(len);
800 f.setPrecision(precision);
801 f.setSqlType(ptype);
802 info.append(f);
803 }
804 return info;
805}
806
808{
809 Q_ASSERT(data);
811}
812
813static QString qCreateParamString(const QList<QVariant> &boundValues, const QSqlDriver *driver)
814{
815 if (boundValues.isEmpty())
816 return QString();
817
819 QSqlField f;
820 for (const QVariant &val : boundValues) {
821 f.setMetaType(val.metaType());
823 f.clear();
824 else
825 f.setValue(val);
826 if (!params.isNull())
827 params.append(", "_L1);
828 params.append(driver->formatValue(f));
829 }
830 return params;
831}
832
834{
835 Q_CONSTINIT static QBasicAtomicInt qPreparedStmtCount = Q_BASIC_ATOMIC_INITIALIZER(0);
836 QString id = QStringLiteral("qpsqlpstmt_") + QString::number(qPreparedStmtCount.fetchAndAddRelaxed(1) + 1, 16);
837 return id;
838}
839
841{
842 Q_D(QPSQLResult);
843 if (!d->preparedQueriesEnabled)
845
846 cleanup();
847
848 if (!d->preparedStmtId.isEmpty())
849 d->deallocatePreparedStmt();
850
851 const QString stmtId = qMakePreparedStmtId();
852 const QString stmt = QStringLiteral("PREPARE %1 AS ").arg(stmtId).append(d->positionalToNamedBinding(query));
853
854 PGresult *result = d->drv_d_func()->exec(stmt);
855
856 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
858 "Unable to prepare statement"), QSqlError::StatementError, d->drv_d_func(), result));
859 PQclear(result);
860 d->preparedStmtId.clear();
861 return false;
862 }
863
864 PQclear(result);
865 d->preparedStmtId = stmtId;
866 return true;
867}
868
870{
871 Q_D(QPSQLResult);
872 if (!d->preparedQueriesEnabled)
873 return QSqlResult::exec();
874
875 cleanup();
876
877 QString stmt;
879 if (params.isEmpty())
880 stmt = QStringLiteral("EXECUTE %1").arg(d->preparedStmtId);
881 else
882 stmt = QStringLiteral("EXECUTE %1 (%2)").arg(d->preparedStmtId, params);
883
884 d->stmtId = d->drv_d_func()->sendQuery(stmt);
885 if (d->stmtId == InvalidStatementId) {
887 "Unable to send query"), QSqlError::StatementError, d->drv_d_func()));
888 return false;
889 }
890
891 if (isForwardOnly())
892 setForwardOnly(d->drv_d_func()->setSingleRowMode());
893
894 d->result = d->drv_d_func()->getResult(d->stmtId);
895 if (!isForwardOnly()) {
896 // Fetch all result sets right away
897 while (PGresult *nextResultSet = d->drv_d_func()->getResult(d->stmtId))
898 d->nextResultSets.push(nextResultSet);
899 }
900 return d->processResults();
901}
902
904
906{
907 PGresult *result = exec("SET CLIENT_ENCODING TO 'UNICODE'");
908 int status = PQresultStatus(result);
909 PQclear(result);
910 return status == PGRES_COMMAND_OK;
911}
912
914{
915 PGresult *result = exec("SET DATESTYLE TO 'ISO'");
916 int status = PQresultStatus(result);
917 if (status != PGRES_COMMAND_OK)
918 qWarning() << QString::fromUtf8(PQerrorMessage(connection));
919 PQclear(result);
920}
921
923{
924 if (pro >= QPSQLDriver::Version9) {
925 // Server version before QPSQLDriver::Version9 only supports escape mode for bytea type,
926 // but bytea format is set to hex by default in PSQL 9 and above. So need to force the
927 // server to use the old escape mode when connects to the new server.
928 PGresult *result = exec("SET bytea_output TO escape");
929 int status = PQresultStatus(result);
930 if (status != PGRES_COMMAND_OK)
931 qWarning() << QString::fromUtf8(PQerrorMessage(connection));
932 PQclear(result);
933 }
934}
935
937{
938 // standard_conforming_strings option introduced in 8.2
939 // http://www.postgresql.org/docs/8.2/static/runtime-config-compatible.html
941 hasBackslashEscape = true;
942 } else {
943 hasBackslashEscape = false;
944 PGresult *result = exec(QStringLiteral("SELECT '\\\\' x"));
945 int status = PQresultStatus(result);
946 if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK)
947 if (QString::fromLatin1(PQgetvalue(result, 0, 0)) == "\\"_L1)
948 hasBackslashEscape = true;
949 PQclear(result);
950 }
951}
952
953static QPSQLDriver::Protocol qMakePSQLVersion(int vMaj, int vMin)
954{
955 switch (vMaj) {
956 case 6:
958 case 7:
959 {
960 switch (vMin) {
961 case 1:
963 case 3:
965 case 4:
967 default:
969 }
970 break;
971 }
972 case 8:
973 {
974 switch (vMin) {
975 case 1:
977 case 2:
979 case 3:
981 case 4:
983 default:
985 }
986 break;
987 }
988 case 9:
989 {
990 switch (vMin) {
991 case 1:
993 case 2:
995 case 3:
997 case 4:
999 case 5:
1001 case 6:
1003 default:
1004 return QPSQLDriver::Version9;
1005 }
1006 break;
1007 }
1008 case 10:
1010 case 11:
1012 case 12:
1014 default:
1015 if (vMaj > 12)
1017 break;
1018 }
1020}
1021
1023{
1024 const QRegularExpression rx(QStringLiteral("(\\d+)(?:\\.(\\d+))?"));
1025 const QRegularExpressionMatch match = rx.match(versionString);
1026 if (match.hasMatch()) {
1027 // Beginning with PostgreSQL version 10, a major release is indicated by
1028 // increasing the first part of the version, e.g. 10 to 11.
1029 // Before version 10, a major release was indicated by increasing either
1030 // the first or second part of the version number, e.g. 9.5 to 9.6.
1031 int vMaj = match.capturedView(1).toInt();
1032 int vMin;
1033 if (vMaj >= 10) {
1034 vMin = 0;
1035 } else {
1036 if (match.capturedView(2).isEmpty())
1038 vMin = match.capturedView(2).toInt();
1039 }
1040 return qMakePSQLVersion(vMaj, vMin);
1041 }
1042
1044}
1045
1047{
1049 PGresult *result = exec("SELECT version()");
1050 int status = PQresultStatus(result);
1051 if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) {
1052 serverVersion = qFindPSQLVersion(
1053 QString::fromLatin1(PQgetvalue(result, 0, 0)));
1054 }
1055 PQclear(result);
1056
1057 QPSQLDriver::Protocol clientVersion =
1058#if defined(PG_MAJORVERSION)
1059 qFindPSQLVersion(PG_MAJORVERSION ""_L1);
1060#elif defined(PG_VERSION)
1061 qFindPSQLVersion(PG_VERSION ""_L1);
1062#else
1064#endif
1065
1066 if (serverVersion == QPSQLDriver::VersionUnknown) {
1067 serverVersion = clientVersion;
1068 if (serverVersion != QPSQLDriver::VersionUnknown)
1069 qWarning("The server version of this PostgreSQL is unknown, falling back to the client version.");
1070 }
1071
1072 // Keep the old behavior unchanged
1073 if (serverVersion == QPSQLDriver::VersionUnknown)
1074 serverVersion = QPSQLDriver::Version6;
1075
1076 if (serverVersion < QPSQLDriver::Version7_3) {
1077 qWarning("This version of PostgreSQL is not supported and may not work.");
1078 }
1079
1080 return serverVersion;
1081}
1082
1085{
1086}
1087
1090{
1091 Q_D(QPSQLDriver);
1092 d->connection = conn;
1093 if (conn) {
1094 d->pro = d->getPSQLVersion();
1095 d->detectBackslashEscape();
1096 setOpen(true);
1097 setOpenError(false);
1098 }
1099}
1100
1102{
1103 Q_D(QPSQLDriver);
1104 if (d->connection)
1105 PQfinish(d->connection);
1106}
1107
1109{
1110 Q_D(const QPSQLDriver);
1111 return QVariant::fromValue(d->connection);
1112}
1113
1115{
1116 Q_D(const QPSQLDriver);
1117 switch (f) {
1118 case Transactions:
1119 case QuerySize:
1120 case LastInsertId:
1122 case EventNotifications:
1123 case MultipleResultSets:
1124 case BLOB:
1125 case Unicode:
1126 return true;
1127 case PreparedQueries:
1129 return d->pro >= QPSQLDriver::Version8_2;
1130 case BatchOperations:
1131 case NamedPlaceholders:
1132 case SimpleLocking:
1133 case FinishQuery:
1134 case CancelQuery:
1135 return false;
1136 }
1137 return false;
1138}
1139
1140/*
1141 Quote a string for inclusion into the connection string
1142 \ -> \\
1143 ' -> \'
1144 surround string by single quotes
1145 */
1147{
1148 s.replace(u'\\', "\\\\"_L1);
1149 s.replace(u'\'', "\\'"_L1);
1150 s.append(u'\'').prepend(u'\'');
1151 return s;
1152}
1153
1155 const QString &user,
1156 const QString &password,
1157 const QString &host,
1158 int port,
1159 const QString &connOpts)
1160{
1161 Q_D(QPSQLDriver);
1162 close();
1163 QString connectString;
1164 if (!host.isEmpty())
1165 connectString.append("host="_L1).append(qQuote(host));
1166 if (!db.isEmpty())
1167 connectString.append(" dbname="_L1).append(qQuote(db));
1168 if (!user.isEmpty())
1169 connectString.append(" user="_L1).append(qQuote(user));
1170 if (!password.isEmpty())
1171 connectString.append(" password="_L1).append(qQuote(password));
1172 if (port != -1)
1173 connectString.append(" port="_L1).append(qQuote(QString::number(port)));
1174
1175 // add any connect options - the server will handle error detection
1176 if (!connOpts.isEmpty()) {
1177 QString opt = connOpts;
1178 opt.replace(';'_L1, ' '_L1, Qt::CaseInsensitive);
1179 connectString.append(u' ').append(opt);
1180 }
1181
1182 d->connection = PQconnectdb(std::move(connectString).toLocal8Bit().constData());
1183 if (PQstatus(d->connection) == CONNECTION_BAD) {
1184 setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
1185 setOpenError(true);
1186 PQfinish(d->connection);
1187 d->connection = nullptr;
1188 return false;
1189 }
1190
1191 d->pro = d->getPSQLVersion();
1192 d->detectBackslashEscape();
1193 if (!d->setEncodingUtf8()) {
1194 setLastError(qMakeError(tr("Unable to set client encoding to 'UNICODE'"), QSqlError::ConnectionError, d));
1195 setOpenError(true);
1196 PQfinish(d->connection);
1197 d->connection = nullptr;
1198 return false;
1199 }
1200 d->setDatestyle();
1201 d->setByteaOutput();
1202
1203 setOpen(true);
1204 setOpenError(false);
1205 return true;
1206}
1207
1209{
1210 Q_D(QPSQLDriver);
1211
1212 d->seid.clear();
1213 if (d->sn) {
1214 disconnect(d->sn, SIGNAL(activated(QSocketDescriptor)), this, SLOT(_q_handleNotification()));
1215 delete d->sn;
1216 d->sn = nullptr;
1217 }
1218
1219 if (d->connection)
1220 PQfinish(d->connection);
1221 d->connection = nullptr;
1222 setOpen(false);
1223 setOpenError(false);
1224}
1225
1227{
1228 return new QPSQLResult(this);
1229}
1230
1232{
1233 Q_D(QPSQLDriver);
1234 if (!isOpen()) {
1235 qWarning("QPSQLDriver::beginTransaction: Database not open");
1236 return false;
1237 }
1238 PGresult *res = d->exec("BEGIN");
1239 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
1240 setLastError(qMakeError(tr("Could not begin transaction"),
1242 PQclear(res);
1243 return false;
1244 }
1245 PQclear(res);
1246 return true;
1247}
1248
1250{
1251 Q_D(QPSQLDriver);
1252 if (!isOpen()) {
1253 qWarning("QPSQLDriver::commitTransaction: Database not open");
1254 return false;
1255 }
1256 PGresult *res = d->exec("COMMIT");
1257
1258 bool transaction_failed = false;
1259
1260 // XXX
1261 // This hack is used to tell if the transaction has succeeded for the protocol versions of
1262 // PostgreSQL below. For 7.x and other protocol versions we are left in the dark.
1263 // This hack can disappear once there is an API to query this sort of information.
1264 if (d->pro >= QPSQLDriver::Version8) {
1265 transaction_failed = qstrcmp(PQcmdStatus(res), "ROLLBACK") == 0;
1266 }
1267
1268 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK || transaction_failed) {
1269 setLastError(qMakeError(tr("Could not commit transaction"),
1271 PQclear(res);
1272 return false;
1273 }
1274 PQclear(res);
1275 return true;
1276}
1277
1279{
1280 Q_D(QPSQLDriver);
1281 if (!isOpen()) {
1282 qWarning("QPSQLDriver::rollbackTransaction: Database not open");
1283 return false;
1284 }
1285 PGresult *res = d->exec("ROLLBACK");
1286 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
1287 setLastError(qMakeError(tr("Could not rollback transaction"),
1289 PQclear(res);
1290 return false;
1291 }
1292 PQclear(res);
1293 return true;
1294}
1295
1297{
1298 Q_D(const QPSQLDriver);
1299 QStringList tl;
1300 if (!isOpen())
1301 return tl;
1303 t.setForwardOnly(true);
1304
1305 if (type & QSql::Tables)
1306 const_cast<QPSQLDriverPrivate*>(d)->appendTables(tl, t, u'r');
1307 if (type & QSql::Views)
1308 const_cast<QPSQLDriverPrivate*>(d)->appendTables(tl, t, u'v');
1309 if (type & QSql::SystemTables) {
1310 t.exec(QStringLiteral("SELECT relname FROM pg_class WHERE (relkind = 'r') "
1311 "AND (relname LIKE 'pg_%') "));
1312 while (t.next())
1313 tl.append(t.value(0).toString());
1314 }
1315
1316 return tl;
1317}
1318
1319static void qSplitTableName(QString &tablename, QString &schema)
1320{
1321 qsizetype dot = tablename.indexOf(u'.');
1322 if (dot == -1)
1323 return;
1324 schema = tablename.left(dot);
1325 tablename = tablename.mid(dot + 1);
1326}
1327
1329{
1330 QSqlIndex idx(tablename);
1331 if (!isOpen())
1332 return idx;
1334
1335 QString tbl = tablename;
1336 QString schema;
1337 qSplitTableName(tbl, schema);
1338 schema = stripDelimiters(schema, QSqlDriver::TableName);
1340
1341 QString stmt = QStringLiteral("SELECT pg_attribute.attname, pg_attribute.atttypid::int, "
1342 "pg_class.relname "
1343 "FROM pg_attribute, pg_class "
1344 "WHERE %1 pg_class.oid IN "
1345 "(SELECT indexrelid FROM pg_index WHERE indisprimary = true AND indrelid IN "
1346 "(SELECT oid FROM pg_class WHERE relname = '%2')) "
1347 "AND pg_attribute.attrelid = pg_class.oid "
1348 "AND pg_attribute.attisdropped = false "
1349 "ORDER BY pg_attribute.attnum");
1350 if (schema.isEmpty())
1351 stmt = stmt.arg(QStringLiteral("pg_table_is_visible(pg_class.oid) AND"));
1352 else
1353 stmt = stmt.arg(QStringLiteral("pg_class.relnamespace = (SELECT oid FROM "
1354 "pg_namespace WHERE pg_namespace.nspname = '%1') AND").arg(schema));
1355
1356 i.exec(stmt.arg(tbl));
1357 while (i.isActive() && i.next()) {
1358 QSqlField f(i.value(0).toString(), qDecodePSQLType(i.value(1).toInt()), tablename);
1359 idx.append(f);
1360 idx.setName(i.value(2).toString());
1361 }
1362 return idx;
1363}
1364
1366{
1368 if (!isOpen())
1369 return info;
1370
1371 QString tbl = tablename;
1372 QString schema;
1373 qSplitTableName(tbl, schema);
1374 schema = stripDelimiters(schema, QSqlDriver::TableName);
1376
1377 const QString adsrc = protocol() < Version8
1378 ? QStringLiteral("pg_attrdef.adsrc")
1379 : QStringLiteral("pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid)");
1380 const QString nspname = schema.isEmpty()
1381 ? QStringLiteral("pg_table_is_visible(pg_class.oid)")
1382 : QStringLiteral("pg_class.relnamespace = (SELECT oid FROM "
1383 "pg_namespace WHERE pg_namespace.nspname = '%1')").arg(schema);
1384 const QString stmt =
1385 QStringLiteral("SELECT pg_attribute.attname, pg_attribute.atttypid::int, "
1386 "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
1387 "%1 "
1388 "FROM pg_class, pg_attribute "
1389 "LEFT JOIN pg_attrdef ON (pg_attrdef.adrelid = "
1390 "pg_attribute.attrelid AND pg_attrdef.adnum = pg_attribute.attnum) "
1391 "WHERE %2 "
1392 "AND pg_class.relname = '%3' "
1393 "AND pg_attribute.attnum > 0 "
1394 "AND pg_attribute.attrelid = pg_class.oid "
1395 "AND pg_attribute.attisdropped = false "
1396 "ORDER BY pg_attribute.attnum").arg(adsrc, nspname, tbl);
1397
1399 query.exec(stmt);
1400 while (query.next()) {
1401 int len = query.value(3).toInt();
1402 int precision = query.value(4).toInt();
1403 // swap length and precision if length == -1
1404 if (len == -1 && precision > -1) {
1405 len = precision - 4;
1406 precision = -1;
1407 }
1408 QString defVal = query.value(5).toString();
1409 if (!defVal.isEmpty() && defVal.at(0) == u'\'') {
1410 const qsizetype end = defVal.lastIndexOf(u'\'');
1411 if (end > 0)
1412 defVal = defVal.mid(1, end - 1);
1413 }
1414 QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt()), tablename);
1415 f.setRequired(query.value(2).toBool());
1416 f.setLength(len);
1417 f.setPrecision(precision);
1418 f.setDefaultValue(defVal);
1419 f.setSqlType(query.value(1).toInt());
1420 info.append(f);
1421 }
1422
1423 return info;
1424}
1425
1426template <class FloatType>
1428{
1429 if (qIsNaN(val))
1430 *target = QStringLiteral("'NaN'");
1431 else if (qIsInf(val))
1432 *target = (val < 0) ? QStringLiteral("'-Infinity'") : QStringLiteral("'Infinity'");
1433}
1434
1435QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
1436{
1437 Q_D(const QPSQLDriver);
1438 const auto nullStr = [](){ return QStringLiteral("NULL"); };
1439 QString r;
1440 if (field.isNull()) {
1441 r = nullStr();
1442 } else {
1443 switch (field.metaType().id()) {
1444 case QMetaType::QDateTime:
1445#if QT_CONFIG(datestring)
1446 if (field.value().toDateTime().isValid()) {
1447 // we force the value to be considered with a timezone information, and we force it to be UTC
1448 // this is safe since postgresql stores only the UTC value and not the timezone offset (only used
1449 // while parsing), so we have correct behavior in both case of with timezone and without tz
1450 r = QStringLiteral("TIMESTAMP WITH TIME ZONE ") + u'\'' +
1451 QLocale::c().toString(field.value().toDateTime().toUTC(), u"yyyy-MM-ddThh:mm:ss.zzz") +
1452 u'Z' + u'\'';
1453 } else {
1454 r = nullStr();
1455 }
1456#else
1457 r = nullStr();
1458#endif // datestring
1459 break;
1460 case QMetaType::QTime:
1461#if QT_CONFIG(datestring)
1462 if (field.value().toTime().isValid()) {
1463 r = u'\'' + field.value().toTime().toString(u"hh:mm:ss.zzz") + u'\'';
1464 } else
1465#endif
1466 {
1467 r = nullStr();
1468 }
1469 break;
1470 case QMetaType::QString:
1471 r = QSqlDriver::formatValue(field, trimStrings);
1472 if (d->hasBackslashEscape)
1473 r.replace(u'\\', "\\\\"_L1);
1474 break;
1475 case QMetaType::Bool:
1476 if (field.value().toBool())
1477 r = QStringLiteral("TRUE");
1478 else
1479 r = QStringLiteral("FALSE");
1480 break;
1481 case QMetaType::QByteArray: {
1482 QByteArray ba(field.value().toByteArray());
1483 size_t len;
1484#if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 80200
1485 unsigned char *data = PQescapeByteaConn(d->connection, (const unsigned char*)ba.constData(), ba.size(), &len);
1486#else
1487 unsigned char *data = PQescapeBytea((const unsigned char*)ba.constData(), ba.size(), &len);
1488#endif
1489 r += u'\'';
1490 r += QLatin1StringView((const char*)data);
1491 r += u'\'';
1493 break;
1494 }
1495 case QMetaType::Float:
1497 if (r.isEmpty())
1498 r = QSqlDriver::formatValue(field, trimStrings);
1499 break;
1500 case QMetaType::Double:
1502 if (r.isEmpty())
1503 r = QSqlDriver::formatValue(field, trimStrings);
1504 break;
1505 case QMetaType::QUuid:
1506 r = u'\'' + field.value().toString() + u'\'';
1507 break;
1508 default:
1509 r = QSqlDriver::formatValue(field, trimStrings);
1510 break;
1511 }
1512 }
1513 return r;
1514}
1515
1517{
1518 QString res = identifier;
1519 if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"') ) {
1520 res.replace(u'"', "\"\""_L1);
1521 res.replace(u'.', "\".\""_L1);
1522 res = u'"' + res + u'"';
1523 }
1524 return res;
1525}
1526
1528{
1529 Q_D(const QPSQLDriver);
1530 return PQstatus(d->connection) == CONNECTION_OK;
1531}
1532
1534{
1535 Q_D(const QPSQLDriver);
1536 return d->pro;
1537}
1538
1540{
1541 Q_D(QPSQLDriver);
1542 if (!isOpen()) {
1543 qWarning("QPSQLDriver::subscribeToNotificationImplementation: database not open.");
1544 return false;
1545 }
1546
1547 const bool alreadyContained = d->seid.contains(name);
1548 int socket = PQsocket(d->connection);
1549 if (socket) {
1550 // Add the name to the list of subscriptions here so that QSQLDriverPrivate::exec knows
1551 // to check for notifications immediately after executing the LISTEN. If it has already
1552 // been subscribed then LISTEN Will do nothing. But we do the call anyway in case the
1553 // connection was lost and this is a re-subscription.
1554 if (!alreadyContained)
1555 d->seid << name;
1557 PGresult *result = d->exec(query);
1558 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
1559 if (!alreadyContained)
1560 d->seid.removeLast();
1561 setLastError(qMakeError(tr("Unable to subscribe"), QSqlError::StatementError, d, result));
1562 PQclear(result);
1563 return false;
1564 }
1565 PQclear(result);
1566
1567 if (!d->sn) {
1569 connect(d->sn, SIGNAL(activated(QSocketDescriptor)), this, SLOT(_q_handleNotification()));
1570 }
1571 } else {
1572 qWarning("QPSQLDriver::subscribeToNotificationImplementation: PQsocket didn't return a valid socket to listen on");
1573 return false;
1574 }
1575
1576 return true;
1577}
1578
1580{
1581 Q_D(QPSQLDriver);
1582 if (!isOpen()) {
1583 qWarning("QPSQLDriver::unsubscribeFromNotificationImplementation: database not open.");
1584 return false;
1585 }
1586
1587 if (!d->seid.contains(name)) {
1588 qWarning("QPSQLDriver::unsubscribeFromNotificationImplementation: not subscribed to '%ls'.",
1590 return false;
1591 }
1592
1594 PGresult *result = d->exec(query);
1595 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
1596 setLastError(qMakeError(tr("Unable to unsubscribe"), QSqlError::StatementError, d, result));
1597 PQclear(result);
1598 return false;
1599 }
1600 PQclear(result);
1601
1602 d->seid.removeAll(name);
1603
1604 if (d->seid.isEmpty()) {
1605 disconnect(d->sn, SIGNAL(activated(QSocketDescriptor)), this, SLOT(_q_handleNotification()));
1606 delete d->sn;
1607 d->sn = nullptr;
1608 }
1609
1610 return true;
1611}
1612
1614{
1615 Q_D(const QPSQLDriver);
1616 return d->seid;
1617}
1618
1619void QPSQLDriver::_q_handleNotification()
1620{
1621 Q_D(QPSQLDriver);
1622 d->pendingNotifyCheck = false;
1623 PQconsumeInput(d->connection);
1624
1625 PGnotify *notify = nullptr;
1626 while ((notify = PQnotifies(d->connection)) != nullptr) {
1627 QString name(QLatin1StringView(notify->relname));
1628 if (d->seid.contains(name)) {
1629 QString payload;
1630#if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 70400
1631 if (notify->extra)
1632 payload = QString::fromUtf8(notify->extra);
1633#endif
1634 QSqlDriver::NotificationSource source = (notify->be_pid == PQbackendPID(d->connection)) ? QSqlDriver::SelfSource : QSqlDriver::OtherSource;
1635 emit notification(name, source, payload);
1636 }
1637 else
1638 qWarning("QPSQLDriver: received notification for '%ls' which isn't subscribed to.",
1640
1641 qPQfreemem(notify);
1642 }
1643}
1644
\inmodule QtCore
Definition qbytearray.h:57
qulonglong toULongLong(bool *ok=nullptr, int base=10) const
Returns the byte array converted to an {unsigned long long} using base base, which is ten by default.
qlonglong toLongLong(bool *ok=nullptr, int base=10) const
Returns the byte array converted to a {long long} using base base, which is ten by default.
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
int toInt(bool *ok=nullptr, int base=10) const
Returns the byte array converted to an int using base base, which is ten by default.
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:394
\inmodule QtCore
Definition qchar.h:48
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
QDateTime toUTC() const
Returns a copy of this datetime converted to UTC.
bool isValid() const
Returns true if this datetime represents a definite moment, otherwise false.
\inmodule QtCore
Definition qhash.h:818
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
static QLocale c()
Returns a QLocale object initialized to the "C" locale.
Definition qlocale.h:1103
QString toString(qlonglong i) const
Returns a localized string representation of i.
Definition qlocale.cpp:1962
\inmodule QtCore
Definition qmetatype.h:320
int id(int=0) const
Definition qmetatype.h:454
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
void appendTables(QStringList &tl, QSqlQuery &t, QChar type)
void discardResults() const
PGresult * getResult(StatementId stmtId) const
QSocketNotifier * sn
void detectBackslashEscape()
PGresult * exec(const char *stmt)
void finishQuery(StatementId stmtId)
QPSQLDriver::Protocol getPSQLVersion()
StatementId generateStatementId()
StatementId sendQuery(const QString &stmt)
bool setSingleRowMode() const
void checkPendingNotifications() const
QPSQLDriver::Protocol pro
StatementId currentStmtId
QHash< int, QString > oidToTable
QVariant handle() const override
Returns the low-level database handle wrapped in a QVariant or an invalid variant if there is no hand...
bool beginTransaction() override
This function is called to begin a transaction.
QSqlIndex primaryIndex(const QString &tablename) const override
Returns the primary index for table tableName.
QSqlResult * createResult() const override
Creates an empty SQL result on the database.
QPSQLDriver(QObject *parent=nullptr)
bool commitTransaction() override
This function is called to commit a transaction.
bool unsubscribeFromNotification(const QString &name) override
This function is called to unsubscribe from event notifications from the database.
QStringList tables(QSql::TableType) const override
Returns a list of the names of the tables in the database.
bool hasFeature(DriverFeature f) const override
Returns true if the driver supports feature feature; otherwise returns false.
bool rollbackTransaction() override
This function is called to rollback a transaction.
bool isOpen() const override
Returns true if the database connection is open; otherwise returns false.
QStringList subscribedToNotifications() const override
Returns a list of the names of the event notifications that are currently subscribed to.
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...
QString escapeIdentifier(const QString &identifier, IdentifierType type) const override
Returns the identifier escaped according to the database rules.
Protocol protocol() const
bool subscribeToNotification(const QString &name) override
This function is called to subscribe to event notifications from the database.
QSqlRecord record(const QString &tablename) const override
Returns a QSqlRecord populated with the names of the fields in table tableName.
void close() override
Derived classes must reimplement this pure virtual function in order to close the database connection...
@ UnknownLaterVersion
Definition qsql_psql_p.h:61
QString formatValue(const QSqlField &field, bool trimStrings) const override
Returns a string representation of the field value for the database.
QString fieldSerial(qsizetype i) const override
void deallocatePreparedStmt()
std::queue< PGresult * > nextResultSets
StatementId stmtId
bool reset(const QString &query) override
Sets the result to use the SQL statement query for subsequent data retrieval.
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...
void cleanup()
QVariant lastInsertId() const override
Returns the object ID of the most recent inserted row if the database supports it.
bool nextResult() override
bool isNull(int field) override
Returns true if the field at position index in the current row is null; otherwise returns false.
QPSQLResult(const QPSQLDriver *db)
bool fetch(int i) override
Positions the result to an arbitrary (zero-based) row index.
bool fetchLast() override
Positions the result to the last record (last row) in the result.
QSqlRecord record() const override
Returns the current record if the query is active; otherwise returns an empty QSqlRecord.
bool fetchNext() override
Positions the result to the next available record (row) in the result.
QVariant data(int i) override
Returns the data for field index in the current row as a QVariant.
bool prepare(const QString &query) override
Prepares the given query for execution; the query will normally use placeholders so that it can be ex...
QVariant handle() const override
Returns the low-level database handle for this result set wrapped in a QVariant or an invalid QVarian...
int numRowsAffected() override
Returns the number of rows affected by the last query executed, or -1 if it cannot be determined or i...
bool fetchFirst() override
Positions the result to the first record (row 0) in the result.
void virtual_hook(int id, void *data) override
bool exec() override
Executes the query, returning true if successful; otherwise returns false.
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
\inmodule QtCore
\inmodule QtCore
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
NotificationSource
This enum contains a list of SQL notification sources.
Definition qsqldriver.h:42
virtual void setLastError(const QSqlError &e)
This function is used to set the value of the last error, error, that occurred on the database.
void notification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload)
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
void setName(const QString &name)
Sets the name of the index to name.
Definition qsqlindex.cpp:90
void append(const QSqlField &field)
Appends the field field to the list of indexed fields.
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
static bool isVariantNull(const QVariant &variant)
QSqlResultPrivate(QSqlResult *q, const QSqlDriver *drv)
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.
virtual void virtual_hook(int id, void *data)
int at() const
Returns the current (zero-based) row position of the result.
virtual bool prepare(const QString &query)
Prepares the given query for execution; the query will normally use placeholders so that it can be ex...
virtual bool exec()
Executes the query, returning true if successful; otherwise returns false.
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 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.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
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 & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
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
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
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 arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
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
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
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
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 & prepend(QChar c)
Definition qstring.h:411
bool isValid() const
Returns true if the time is valid; otherwise returns false.
\inmodule QtCore
Definition qvariant.h:64
QDateTime toDateTime() const
Returns the variant as a QDateTime if the variant has userType() \l QMetaType::QDateTime,...
double toDouble(bool *ok=nullptr) const
Returns the variant as a double if the variant has userType() \l QMetaType::Double,...
QTime toTime() const
Returns the variant as a QTime if the variant has userType() \l QMetaType::QTime, \l QMetaType::QDate...
float toFloat(bool *ok=nullptr) const
Returns the variant as a float if the variant has userType() \l QMetaType::Double,...
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:531
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
QStyleOptionButton opt
@ 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
@ 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.
@ ISODate
@ CaseInsensitive
@ QueuedConnection
#define Q_BASIC_ATOMIC_INITIALIZER(a)
size_t qstrlen(const char *str)
Q_CORE_EXPORT int qstricmp(const char *, const char *)
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
DBusConnection * connection
EGLOutputPortEXT port
bool qIsNaN(qfloat16 f) noexcept
Definition qfloat16.h:238
bool qIsInf(qfloat16 f) noexcept
Definition qfloat16.h:237
double qstrtod(const char *s00, char const **se, bool *ok)
#define qWarning
Definition qlogging.h:162
#define Q_DECLARE_OPAQUE_POINTER(POINTER)
Definition qmetatype.h:1496
#define Q_DECLARE_METATYPE(TYPE)
Definition qmetatype.h:1504
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qInf()
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qQNaN()
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLboolean r
[2]
GLuint GLuint end
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLenum GLuint buffer
GLenum type
GLenum target
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLsizei GLsizei GLchar * source
void ** params
GLboolean reset
GLenum query
GLuint res
GLuint GLfloat * val
GLenum GLsizei len
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 GLint GLint * precision
static qreal dot(const QPointF &a, const QPointF &b)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QSqlError qMakeError(const QString &err, QSqlError::ErrorType type, const QDB2DriverPrivate *p)
Definition qsql_db2.cpp:203
#define QTIMEOID
Definition qsql_psql.cpp:40
#define QFLOAT4OID
Definition qsql_psql.cpp:35
void PQfreemem(T *t, int=0)
Definition qsql_psql.cpp:58
#define QOIDOID
Definition qsql_psql.cpp:44
#define QXIDOID
Definition qsql_psql.cpp:47
static QSqlError qMakeError(const QString &err, QSqlError::ErrorType type, const QPSQLDriverPrivate *p, PGresult *result=nullptr)
void assignSpecialPsqlFloatValue(FloatType val, QString *target)
#define QINT2OID
Definition qsql_psql.cpp:32
static QPSQLDriver::Protocol qFindPSQLVersion(const QString &versionString)
#define QTIMESTAMPTZOID
Definition qsql_psql.cpp:43
static QMetaType qDecodePSQLType(int t)
#define QNUMERICOID
Definition qsql_psql.cpp:34
#define QINT8OID
Definition qsql_psql.cpp:31
#define VARHDRSZ
Definition qsql_psql.cpp:53
void qPQfreemem(void *buffer)
Definition qsql_psql.cpp:70
#define QINT4OID
Definition qsql_psql.cpp:33
#define QFLOAT8OID
Definition qsql_psql.cpp:36
#define QDATEOID
Definition qsql_psql.cpp:39
static QPSQLDriver::Protocol qMakePSQLVersion(int vMaj, int vMin)
#define QVARBITOID
Definition qsql_psql.cpp:51
int StatementId
Definition qsql_psql.cpp:80
#define QTIMESTAMPOID
Definition qsql_psql.cpp:42
static QString qCreateParamString(const QList< QVariant > &boundValues, const QSqlDriver *driver)
#define QRELTIMEOID
Definition qsql_psql.cpp:38
#define QTIMETZOID
Definition qsql_psql.cpp:41
QString qMakePreparedStmtId()
static const StatementId InvalidStatementId
Definition qsql_psql.cpp:81
#define QREGPROCOID
Definition qsql_psql.cpp:46
static void qSplitTableName(QString &tablename, QString &schema)
#define QBITOID
Definition qsql_psql.cpp:50
#define QBYTEAOID
Definition qsql_psql.cpp:45
static const int PGRES_SINGLE_TUPLE
Definition qsql_psql.cpp:77
static QString qQuote(QString s)
#define QCIDOID
Definition qsql_psql.cpp:48
#define QABSTIMEOID
Definition qsql_psql.cpp:37
#define QBOOLOID
Definition qsql_psql.cpp:30
struct pg_conn PGconn
Definition qsql_psql_p.h:26
struct pg_result PGresult
Definition qsql_psql_p.h:27
#define Q_DECLARE_SQLDRIVER_PRIVATE(Class)
SSL_CTX int(*) void arg)
#define qUtf16Printable(string)
Definition qstring.h:1403
static char * toLocal8Bit(char *out, QStringView in, QStringConverter::State *state)
#define QStringLiteral(str)
#define tr(X)
#define emit
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
ptrdiff_t qsizetype
Definition qtypes.h:70
qint64 qlonglong
Definition qtypes.h:58
QByteArray ba
[0]
QFileInfo info(fileName)
[8]
myObject disconnect()
[26]
QTcpSocket * socket
[1]
QMimeDatabase db
[0]
p rx()++
dialog exec()
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent