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