summaryrefslogtreecommitdiffstats
path: root/src/plugins/sqldrivers/psql/qsql_psql.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/sqldrivers/psql/qsql_psql.cpp')
-rw-r--r--src/plugins/sqldrivers/psql/qsql_psql.cpp584
1 files changed, 393 insertions, 191 deletions
diff --git a/src/plugins/sqldrivers/psql/qsql_psql.cpp b/src/plugins/sqldrivers/psql/qsql_psql.cpp
index 6ba32a0c71..6f105f79ca 100644
--- a/src/plugins/sqldrivers/psql/qsql_psql.cpp
+++ b/src/plugins/sqldrivers/psql/qsql_psql.cpp
@@ -125,6 +125,14 @@ inline void qPQfreemem(void *buffer)
PQfreemem(buffer);
}
+/* Missing declaration of PGRES_SINGLE_TUPLE for PSQL below 9.2 */
+#if !defined PG_VERSION_NUM || PG_VERSION_NUM-0 < 90200
+static const int PGRES_SINGLE_TUPLE = 9;
+#endif
+
+typedef int StatementId;
+static const StatementId InvalidStatementId = 0;
+
class QPSQLResultPrivate;
class QPSQLResult: public QSqlResult
@@ -135,23 +143,25 @@ public:
QPSQLResult(const QPSQLDriver *db);
~QPSQLResult();
- QVariant handle() const Q_DECL_OVERRIDE;
- void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
+ QVariant handle() const override;
+ void virtual_hook(int id, void *data) override;
protected:
void cleanup();
- bool fetch(int i) Q_DECL_OVERRIDE;
- bool fetchFirst() Q_DECL_OVERRIDE;
- bool fetchLast() Q_DECL_OVERRIDE;
- QVariant data(int i) Q_DECL_OVERRIDE;
- bool isNull(int field) Q_DECL_OVERRIDE;
- bool reset (const QString &query) Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
- QVariant lastInsertId() const Q_DECL_OVERRIDE;
- bool prepare(const QString &query) Q_DECL_OVERRIDE;
- bool exec() Q_DECL_OVERRIDE;
+ bool fetch(int i) override;
+ bool fetchFirst() override;
+ bool fetchLast() override;
+ bool fetchNext() override;
+ bool nextResult() override;
+ QVariant data(int i) override;
+ bool isNull(int field) override;
+ bool reset (const QString &query) override;
+ int size() override;
+ int numRowsAffected() override;
+ QSqlRecord record() const override;
+ QVariant lastInsertId() const override;
+ bool prepare(const QString &query) override;
+ bool exec() override;
};
class QPSQLDriverPrivate : public QSqlDriverPrivate
@@ -164,7 +174,9 @@ public:
pro(QPSQLDriver::Version6),
sn(0),
pendingNotifyCheck(false),
- hasBackslashEscape(false)
+ hasBackslashEscape(false),
+ stmtCount(0),
+ currentStmtId(InvalidStatementId)
{ dbmsType = QSqlDriver::PostgreSQL; }
PGconn *connection;
@@ -174,10 +186,19 @@ public:
QStringList seid;
mutable bool pendingNotifyCheck;
bool hasBackslashEscape;
+ int stmtCount;
+ StatementId currentStmtId;
void appendTables(QStringList &tl, QSqlQuery &t, QChar type);
- PGresult * exec(const char * stmt) const;
- PGresult * exec(const QString & stmt) const;
+ PGresult *exec(const char *stmt);
+ PGresult *exec(const QString &stmt);
+ StatementId sendQuery(const QString &stmt);
+ bool setSingleRowMode() const;
+ PGresult *getResult(StatementId stmtId) const;
+ void finishQuery(StatementId stmtId);
+ void discardResults() const;
+ StatementId generateStatementId();
+ void checkPendingNotifications() const;
QPSQLDriver::Protocol getPSQLVersion();
bool setEncodingUtf8();
void setDatestyle();
@@ -187,18 +208,11 @@ public:
void QPSQLDriverPrivate::appendTables(QStringList &tl, QSqlQuery &t, QChar type)
{
- QString query;
- if (pro >= QPSQLDriver::Version7_3) {
- query = QString::fromLatin1("select pg_class.relname, pg_namespace.nspname from pg_class "
- "left join pg_namespace on (pg_class.relnamespace = pg_namespace.oid) "
- "where (pg_class.relkind = '%1') and (pg_class.relname !~ '^Inv') "
- "and (pg_class.relname !~ '^pg_') "
- "and (pg_namespace.nspname != 'information_schema') ").arg(type);
- } else {
- query = QString::fromLatin1("select relname, null from pg_class where (relkind = '%1') "
- "and (relname !~ '^Inv') "
- "and (relname !~ '^pg_') ").arg(type);
- }
+ QString query = QString::fromLatin1("select pg_class.relname, pg_namespace.nspname from pg_class "
+ "left join pg_namespace on (pg_class.relnamespace = pg_namespace.oid) "
+ "where (pg_class.relkind = '%1') and (pg_class.relname !~ '^Inv') "
+ "and (pg_class.relname !~ '^pg_') "
+ "and (pg_namespace.nspname != 'information_schema')").arg(type);
t.exec(query);
while (t.next()) {
QString schema = t.value(1).toString();
@@ -209,20 +223,89 @@ void QPSQLDriverPrivate::appendTables(QStringList &tl, QSqlQuery &t, QChar type)
}
}
-PGresult * QPSQLDriverPrivate::exec(const char * stmt) const
+PGresult *QPSQLDriverPrivate::exec(const char *stmt)
{
- Q_Q(const QPSQLDriver);
+ // PQexec() silently discards any prior query results that the application didn't eat.
PGresult *result = PQexec(connection, stmt);
- if (seid.size() && !pendingNotifyCheck) {
- pendingNotifyCheck = true;
- QMetaObject::invokeMethod(const_cast<QPSQLDriver*>(q), "_q_handleNotification", Qt::QueuedConnection, Q_ARG(int,0));
+ currentStmtId = result ? generateStatementId() : InvalidStatementId;
+ checkPendingNotifications();
+ return result;
+}
+
+PGresult *QPSQLDriverPrivate::exec(const QString &stmt)
+{
+ return exec((isUtf8 ? stmt.toUtf8() : stmt.toLocal8Bit()).constData());
+}
+
+StatementId QPSQLDriverPrivate::sendQuery(const QString &stmt)
+{
+ // Discard any prior query results that the application didn't eat.
+ // This is required for PQsendQuery()
+ discardResults();
+ const int result = PQsendQuery(connection,
+ (isUtf8 ? stmt.toUtf8() : stmt.toLocal8Bit()).constData());
+ currentStmtId = result ? generateStatementId() : InvalidStatementId;
+ return currentStmtId;
+}
+
+bool QPSQLDriverPrivate::setSingleRowMode() const
+{
+ // Activates single-row mode for last sent query, see:
+ // https://www.postgresql.org/docs/9.2/static/libpq-single-row-mode.html
+ // This method should be called immediately after the sendQuery() call.
+#if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 90200
+ return PQsetSingleRowMode(connection) > 0;
+#else
+ return false;
+#endif
+}
+
+PGresult *QPSQLDriverPrivate::getResult(StatementId stmtId) const
+{
+ // Make sure the results of stmtId weren't discaded. This might
+ // happen for forward-only queries if somebody executed another
+ // SQL query on the same db connection.
+ if (stmtId != currentStmtId) {
+ // If you change the following warning, remember to update it
+ // on sql-driver.html page too.
+ qWarning("QPSQLDriver::getResult: Query results lost - "
+ "probably discarded on executing another SQL query.");
+ return nullptr;
}
+ PGresult *result = PQgetResult(connection);
+ checkPendingNotifications();
return result;
}
-PGresult * QPSQLDriverPrivate::exec(const QString & stmt) const
+void QPSQLDriverPrivate::finishQuery(StatementId stmtId)
+{
+ if (stmtId != InvalidStatementId && stmtId == currentStmtId) {
+ discardResults();
+ currentStmtId = InvalidStatementId;
+ }
+}
+
+void QPSQLDriverPrivate::discardResults() const
+{
+ while (PGresult *result = PQgetResult(connection))
+ PQclear(result);
+}
+
+StatementId QPSQLDriverPrivate::generateStatementId()
{
- return exec(isUtf8 ? stmt.toUtf8().constData() : stmt.toLocal8Bit().constData());
+ int stmtId = ++stmtCount;
+ if (stmtId <= 0)
+ stmtId = stmtCount = 1;
+ return stmtId;
+}
+
+void QPSQLDriverPrivate::checkPendingNotifications() const
+{
+ Q_Q(const QPSQLDriver);
+ if (seid.size() && !pendingNotifyCheck) {
+ pendingNotifyCheck = true;
+ QMetaObject::invokeMethod(const_cast<QPSQLDriver*>(q), "_q_handleNotification", Qt::QueuedConnection, Q_ARG(int,0));
+ }
}
class QPSQLResultPrivate : public QSqlResultPrivate
@@ -234,14 +317,19 @@ public:
: QSqlResultPrivate(q, drv),
result(0),
currentSize(-1),
+ canFetchMoreRows(false),
+ stmtId(InvalidStatementId),
preparedQueriesEnabled(false)
{ }
- QString fieldSerial(int i) const Q_DECL_OVERRIDE { return QLatin1Char('$') + QString::number(i + 1); }
+ QString fieldSerial(int i) const override { return QLatin1Char('$') + QString::number(i + 1); }
void deallocatePreparedStmt();
PGresult *result;
+ QList<PGresult*> nextResultSets;
int currentSize;
+ bool canFetchMoreRows;
+ StatementId stmtId;
bool preparedQueriesEnabled;
QString preparedStmtId;
@@ -264,21 +352,45 @@ static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
bool QPSQLResultPrivate::processResults()
{
Q_Q(QPSQLResult);
- if (!result)
+ if (!result) {
+ q->setSelect(false);
+ q->setActive(false);
+ currentSize = -1;
+ canFetchMoreRows = false;
+ if (stmtId != drv_d_func()->currentStmtId) {
+ q->setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
+ "Query results lost - probably discarded on executing "
+ "another SQL query."), QSqlError::StatementError, drv_d_func(), result));
+ }
return false;
-
+ }
int status = PQresultStatus(result);
- if (status == PGRES_TUPLES_OK) {
+ switch (status) {
+ case PGRES_TUPLES_OK:
q->setSelect(true);
q->setActive(true);
- currentSize = PQntuples(result);
+ currentSize = q->isForwardOnly() ? -1 : PQntuples(result);
+ canFetchMoreRows = false;
+ return true;
+ case PGRES_SINGLE_TUPLE:
+ q->setSelect(true);
+ q->setActive(true);
+ currentSize = -1;
+ canFetchMoreRows = true;
return true;
- } else if (status == PGRES_COMMAND_OK) {
+ case PGRES_COMMAND_OK:
q->setSelect(false);
q->setActive(true);
currentSize = -1;
+ canFetchMoreRows = false;
return true;
+ default:
+ break;
}
+ q->setSelect(false);
+ q->setActive(false);
+ currentSize = -1;
+ canFetchMoreRows = false;
q->setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
"Unable to create query"), QSqlError::StatementError, drv_d_func(), result));
return false;
@@ -368,9 +480,15 @@ void QPSQLResult::cleanup()
Q_D(QPSQLResult);
if (d->result)
PQclear(d->result);
- d->result = 0;
+ d->result = nullptr;
+ while (!d->nextResultSets.isEmpty())
+ PQclear(d->nextResultSets.takeFirst());
+ if (d->stmtId != InvalidStatementId)
+ d->drv_d_func()->finishQuery(d->stmtId);
+ d->stmtId = InvalidStatementId;
setAt(QSql::BeforeFirstRow);
d->currentSize = -1;
+ d->canFetchMoreRows = false;
setActive(false);
}
@@ -381,23 +499,150 @@ bool QPSQLResult::fetch(int i)
return false;
if (i < 0)
return false;
- if (i >= d->currentSize)
- return false;
if (at() == i)
return true;
+
+ if (isForwardOnly()) {
+ if (i < at())
+ return false;
+ bool ok = true;
+ while (ok && i > at())
+ ok = fetchNext();
+ return ok;
+ }
+
+ if (i >= d->currentSize)
+ return false;
setAt(i);
return true;
}
bool QPSQLResult::fetchFirst()
{
+ Q_D(const QPSQLResult);
+ if (!isActive())
+ return false;
+ if (at() == 0)
+ return true;
+
+ if (isForwardOnly()) {
+ if (at() == QSql::BeforeFirstRow) {
+ // First result has been already fetched by exec() or
+ // nextResult(), just check it has at least one row.
+ if (d->result && PQntuples(d->result) > 0) {
+ setAt(0);
+ return true;
+ }
+ }
+ return false;
+ }
+
return fetch(0);
}
bool QPSQLResult::fetchLast()
{
Q_D(const QPSQLResult);
- return fetch(PQntuples(d->result) - 1);
+ if (!isActive())
+ return false;
+
+ if (isForwardOnly()) {
+ // Cannot seek to last row in forwardOnly mode, so we have to use brute force
+ int i = at();
+ if (i == QSql::AfterLastRow)
+ return false;
+ if (i == QSql::BeforeFirstRow)
+ i = 0;
+ while (fetchNext())
+ ++i;
+ setAt(i);
+ return true;
+ }
+
+ return fetch(d->currentSize - 1);
+}
+
+bool QPSQLResult::fetchNext()
+{
+ Q_D(QPSQLResult);
+ if (!isActive())
+ return false;
+
+ const int currentRow = at(); // Small optimalization
+ if (currentRow == QSql::BeforeFirstRow)
+ return fetchFirst();
+ if (currentRow == QSql::AfterLastRow)
+ return false;
+
+ if (isForwardOnly()) {
+ if (!d->canFetchMoreRows)
+ return false;
+ PQclear(d->result);
+ d->result = d->drv_d_func()->getResult(d->stmtId);
+ if (!d->result) {
+ setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
+ "Unable to get result"), QSqlError::StatementError, d->drv_d_func(), d->result));
+ d->canFetchMoreRows = false;
+ return false;
+ }
+ int status = PQresultStatus(d->result);
+ switch (status) {
+ case PGRES_SINGLE_TUPLE:
+ // Fetched next row of current result set
+ Q_ASSERT(PQntuples(d->result) == 1);
+ Q_ASSERT(d->canFetchMoreRows);
+ setAt(currentRow + 1);
+ return true;
+ case PGRES_TUPLES_OK:
+ // In single-row mode PGRES_TUPLES_OK means end of current result set
+ Q_ASSERT(PQntuples(d->result) == 0);
+ d->canFetchMoreRows = false;
+ return false;
+ default:
+ setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
+ "Unable to get result"), QSqlError::StatementError, d->drv_d_func(), d->result));
+ d->canFetchMoreRows = false;
+ return false;
+ }
+ }
+
+ if (currentRow + 1 >= d->currentSize)
+ return false;
+ setAt(currentRow + 1);
+ return true;
+}
+
+bool QPSQLResult::nextResult()
+{
+ Q_D(QPSQLResult);
+ if (!isActive())
+ return false;
+
+ setAt(QSql::BeforeFirstRow);
+
+ if (isForwardOnly()) {
+ if (d->canFetchMoreRows) {
+ // Skip all rows from current result set
+ while (d->result && PQresultStatus(d->result) == PGRES_SINGLE_TUPLE) {
+ PQclear(d->result);
+ d->result = d->drv_d_func()->getResult(d->stmtId);
+ }
+ d->canFetchMoreRows = false;
+ // Check for unexpected errors
+ if (d->result && PQresultStatus(d->result) == PGRES_FATAL_ERROR)
+ return d->processResults();
+ }
+ // Fetch first result from next result set
+ if (d->result)
+ PQclear(d->result);
+ d->result = d->drv_d_func()->getResult(d->stmtId);
+ return d->processResults();
+ }
+
+ if (d->result)
+ PQclear(d->result);
+ d->result = d->nextResultSets.isEmpty() ? nullptr : d->nextResultSets.takeFirst();
+ return d->processResults();
}
QVariant QPSQLResult::data(int i)
@@ -407,11 +652,12 @@ QVariant QPSQLResult::data(int i)
qWarning("QPSQLResult::data: column %d out of range", i);
return QVariant();
}
+ const int currentRow = isForwardOnly() ? 0 : at();
int ptype = PQftype(d->result, i);
QVariant::Type type = qDecodePSQLType(ptype);
- const char *val = PQgetvalue(d->result, at(), i);
- if (PQgetisnull(d->result, at(), i))
+ if (PQgetisnull(d->result, currentRow, i))
return QVariant(type);
+ const char *val = PQgetvalue(d->result, currentRow, i);
switch (type) {
case QVariant::Bool:
return QVariant((bool)(val[0] == 't'));
@@ -495,8 +741,8 @@ QVariant QPSQLResult::data(int i)
bool QPSQLResult::isNull(int field)
{
Q_D(const QPSQLResult);
- PQgetvalue(d->result, at(), field);
- return PQgetisnull(d->result, at(), field);
+ const int currentRow = isForwardOnly() ? 0 : at();
+ return PQgetisnull(d->result, currentRow, field);
}
bool QPSQLResult::reset (const QString& query)
@@ -507,7 +753,23 @@ bool QPSQLResult::reset (const QString& query)
return false;
if (!driver()->isOpen() || driver()->isOpenError())
return false;
- d->result = d->drv_d_func()->exec(query);
+
+ d->stmtId = d->drv_d_func()->sendQuery(query);
+ if (d->stmtId == InvalidStatementId) {
+ setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
+ "Unable to send query"), QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+
+ if (isForwardOnly())
+ setForwardOnly(d->drv_d_func()->setSingleRowMode());
+
+ d->result = d->drv_d_func()->getResult(d->stmtId);
+ if (!isForwardOnly()) {
+ // Fetch all result sets right away
+ while (PGresult *nextResultSet = d->drv_d_func()->getResult(d->stmtId))
+ d->nextResultSets.append(nextResultSet);
+ }
return d->processResults();
}
@@ -553,6 +815,18 @@ QSqlRecord QPSQLResult::record() const
f.setName(QString::fromUtf8(PQfname(d->result, i)));
else
f.setName(QString::fromLocal8Bit(PQfname(d->result, i)));
+
+ // WARNING: We cannot execute any other SQL queries on
+ // the same db connection while forward-only mode is active
+ // (this would discard all results of forward-only query).
+ // So we just skip this...
+ if (!isForwardOnly()) {
+ QSqlQuery qry(driver()->createResult());
+ if (qry.exec(QStringLiteral("SELECT relname FROM pg_class WHERE pg_class.oid = %1")
+ .arg(PQftable(d->result, i))) && qry.next()) {
+ f.setTableName(qry.value(0).toString());
+ }
+ }
int ptype = PQftype(d->result, i);
f.setType(qDecodePSQLType(ptype));
int len = PQfsize(d->result, i);
@@ -670,8 +944,22 @@ bool QPSQLResult::exec()
else
stmt = QString::fromLatin1("EXECUTE %1 (%2)").arg(d->preparedStmtId, params);
- d->result = d->drv_d_func()->exec(stmt);
+ d->stmtId = d->drv_d_func()->sendQuery(stmt);
+ if (d->stmtId == InvalidStatementId) {
+ setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
+ "Unable to send query"), QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+
+ if (isForwardOnly())
+ setForwardOnly(d->drv_d_func()->setSingleRowMode());
+ d->result = d->drv_d_func()->getResult(d->stmtId);
+ if (!isForwardOnly()) {
+ // Fetch all result sets right away
+ while (PGresult *nextResultSet = d->drv_d_func()->getResult(d->stmtId))
+ d->nextResultSets.append(nextResultSet);
+ }
return d->processResults();
}
@@ -890,6 +1178,8 @@ bool QPSQLDriver::hasFeature(DriverFeature f) const
case LastInsertId:
case LowPrecisionNumbers:
case EventNotifications:
+ case MultipleResultSets:
+ case BLOB:
return true;
case PreparedQueries:
case PositionalPlaceholders:
@@ -898,11 +1188,8 @@ bool QPSQLDriver::hasFeature(DriverFeature f) const
case NamedPlaceholders:
case SimpleLocking:
case FinishQuery:
- case MultipleResultSets:
case CancelQuery:
return false;
- case BLOB:
- return d->pro >= QPSQLDriver::Version7_1;
case Unicode:
return d->isUtf8;
}
@@ -999,7 +1286,7 @@ QSqlResult *QPSQLDriver::createResult() const
bool QPSQLDriver::beginTransaction()
{
- Q_D(const QPSQLDriver);
+ Q_D(QPSQLDriver);
if (!isOpen()) {
qWarning("QPSQLDriver::beginTransaction: Database not open");
return false;
@@ -1096,12 +1383,10 @@ static void qSplitTableName(QString &tablename, QString &schema)
QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
{
- Q_D(const QPSQLDriver);
QSqlIndex idx(tablename);
if (!isOpen())
return idx;
QSqlQuery i(createResult());
- QString stmt;
QString tbl = tablename;
QString schema;
@@ -1117,46 +1402,24 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
else
schema = std::move(schema).toLower();
- if (d->pro == QPSQLDriver::Version6) {
- stmt = QLatin1String("select pg_att1.attname, int(pg_att1.atttypid), pg_cl.relname "
- "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
- "where pg_cl.relname = '%1_pkey' "
- "and pg_cl.oid = pg_ind.indexrelid "
- "and pg_att2.attrelid = pg_ind.indexrelid "
- "and pg_att1.attrelid = pg_ind.indrelid "
- "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] "
- "order by pg_att2.attnum");
- } else if (d->pro == QPSQLDriver::Version7 || d->pro == QPSQLDriver::Version7_1) {
- stmt = QLatin1String("select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname "
- "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
- "where pg_cl.relname = '%1_pkey' "
- "and pg_cl.oid = pg_ind.indexrelid "
- "and pg_att2.attrelid = pg_ind.indexrelid "
- "and pg_att1.attrelid = pg_ind.indrelid "
- "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] "
- "order by pg_att2.attnum");
- } else if (d->pro >= QPSQLDriver::Version7_3) {
- stmt = QLatin1String("SELECT pg_attribute.attname, pg_attribute.atttypid::int, "
- "pg_class.relname "
- "FROM pg_attribute, pg_class "
- "WHERE %1 pg_class.oid IN "
- "(SELECT indexrelid FROM pg_index WHERE indisprimary = true AND indrelid IN "
- " (SELECT oid FROM pg_class WHERE relname = '%2')) "
- "AND pg_attribute.attrelid = pg_class.oid "
- "AND pg_attribute.attisdropped = false "
- "ORDER BY pg_attribute.attnum");
- if (schema.isEmpty())
- stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid) AND"));
- else
- stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
- "pg_namespace where pg_namespace.nspname = '%1') AND ").arg(schema));
- } else {
- qFatal("QPSQLDriver::primaryIndex(tablename): unknown PSQL version, query statement not set");
- }
+ QString stmt = QLatin1String("SELECT pg_attribute.attname, pg_attribute.atttypid::int, "
+ "pg_class.relname "
+ "FROM pg_attribute, pg_class "
+ "WHERE %1 pg_class.oid IN "
+ "(SELECT indexrelid FROM pg_index WHERE indisprimary = true AND indrelid IN "
+ "(SELECT oid FROM pg_class WHERE relname = '%2')) "
+ "AND pg_attribute.attrelid = pg_class.oid "
+ "AND pg_attribute.attisdropped = false "
+ "ORDER BY pg_attribute.attnum");
+ if (schema.isEmpty())
+ stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid) AND"));
+ else
+ stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
+ "pg_namespace where pg_namespace.nspname = '%1') AND").arg(schema));
i.exec(stmt.arg(tbl));
while (i.isActive() && i.next()) {
- QSqlField f(i.value(0).toString(), qDecodePSQLType(i.value(1).toInt()));
+ QSqlField f(i.value(0).toString(), qDecodePSQLType(i.value(1).toInt()), tablename);
idx.append(f);
idx.setName(i.value(2).toString());
}
@@ -1165,7 +1428,6 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
QSqlRecord QPSQLDriver::record(const QString& tablename) const
{
- Q_D(const QPSQLDriver);
QSqlRecord info;
if (!isOpen())
return info;
@@ -1184,105 +1446,44 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const
else
schema = std::move(schema).toLower();
- QString stmt;
- if (d->pro == QPSQLDriver::Version6) {
- stmt = QLatin1String("select pg_attribute.attname, int(pg_attribute.atttypid), "
- "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
- "int(pg_attribute.attrelid), pg_attribute.attnum "
- "from pg_class, pg_attribute "
- "where pg_class.relname = '%1' "
- "and pg_attribute.attnum > 0 "
- "and pg_attribute.attrelid = pg_class.oid ");
- } else if (d->pro == QPSQLDriver::Version7) {
- stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
- "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
- "pg_attribute.attrelid::int, pg_attribute.attnum "
- "from pg_class, pg_attribute "
- "where pg_class.relname = '%1' "
- "and pg_attribute.attnum > 0 "
- "and pg_attribute.attrelid = pg_class.oid ");
- } else if (d->pro == QPSQLDriver::Version7_1) {
- stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
- "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
- "pg_attrdef.adsrc "
- "from pg_class, pg_attribute "
- "left join pg_attrdef on (pg_attrdef.adrelid = "
- "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
- "where pg_class.relname = '%1' "
- "and pg_attribute.attnum > 0 "
- "and pg_attribute.attrelid = pg_class.oid "
- "order by pg_attribute.attnum ");
- } else if (d->pro >= QPSQLDriver::Version7_3) {
- stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
- "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
- "pg_attrdef.adsrc "
- "from pg_class, pg_attribute "
- "left join pg_attrdef on (pg_attrdef.adrelid = "
- "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
- "where %1 "
- "and pg_class.relname = '%2' "
- "and pg_attribute.attnum > 0 "
- "and pg_attribute.attrelid = pg_class.oid "
- "and pg_attribute.attisdropped = false "
- "order by pg_attribute.attnum ");
- if (schema.isEmpty())
- stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid)"));
- else
- stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
- "pg_namespace where pg_namespace.nspname = '%1')").arg(schema));
- } else {
- qFatal("QPSQLDriver::record(tablename): unknown PSQL version, query statement not set");
- }
+ QString stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
+ "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
+ "pg_attrdef.adsrc "
+ "from pg_class, pg_attribute "
+ "left join pg_attrdef on (pg_attrdef.adrelid = "
+ "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
+ "where %1 "
+ "and pg_class.relname = '%2' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid "
+ "and pg_attribute.attisdropped = false "
+ "order by pg_attribute.attnum");
+ if (schema.isEmpty())
+ stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid)"));
+ else
+ stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
+ "pg_namespace where pg_namespace.nspname = '%1')").arg(schema));
QSqlQuery query(createResult());
query.exec(stmt.arg(tbl));
- if (d->pro >= QPSQLDriver::Version7_1) {
- while (query.next()) {
- int len = query.value(3).toInt();
- int precision = query.value(4).toInt();
- // swap length and precision if length == -1
- if (len == -1 && precision > -1) {
- len = precision - 4;
- precision = -1;
- }
- QString defVal = query.value(5).toString();
- if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\''))
- defVal = defVal.mid(1, defVal.length() - 2);
- QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt()));
- f.setRequired(query.value(2).toBool());
- f.setLength(len);
- f.setPrecision(precision);
- f.setDefaultValue(defVal);
- f.setSqlType(query.value(1).toInt());
- info.append(f);
- }
- } else {
- // Postgres < 7.1 cannot handle outer joins
- while (query.next()) {
- QString defVal;
- QString stmt2 = QLatin1String("select pg_attrdef.adsrc from pg_attrdef where "
- "pg_attrdef.adrelid = %1 and pg_attrdef.adnum = %2 ");
- QSqlQuery query2(createResult());
- query2.exec(stmt2.arg(query.value(5).toInt()).arg(query.value(6).toInt()));
- if (query2.isActive() && query2.next())
- defVal = query2.value(0).toString();
- if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\''))
- defVal = defVal.mid(1, defVal.length() - 2);
- int len = query.value(3).toInt();
- int precision = query.value(4).toInt();
- // swap length and precision if length == -1
- if (len == -1 && precision > -1) {
- len = precision - 4;
- precision = -1;
- }
- QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt()));
- f.setRequired(query.value(2).toBool());
- f.setLength(len);
- f.setPrecision(precision);
- f.setDefaultValue(defVal);
- f.setSqlType(query.value(1).toInt());
- info.append(f);
+ while (query.next()) {
+ int len = query.value(3).toInt();
+ int precision = query.value(4).toInt();
+ // swap length and precision if length == -1
+ if (len == -1 && precision > -1) {
+ len = precision - 4;
+ precision = -1;
}
+ QString defVal = query.value(5).toString();
+ if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\''))
+ defVal = defVal.mid(1, defVal.length() - 2);
+ QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt()), tablename);
+ f.setRequired(query.value(2).toBool());
+ f.setLength(len);
+ f.setPrecision(precision);
+ f.setDefaultValue(defVal);
+ f.setSqlType(query.value(1).toInt());
+ info.append(f);
}
return info;
@@ -1430,6 +1631,7 @@ bool QPSQLDriver::subscribeToNotification(const QString &name)
QString query = QLatin1String("LISTEN ") + escapeIdentifier(name, QSqlDriver::TableName);
PGresult *result = d->exec(query);
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+ d->seid.removeLast();
setLastError(qMakeError(tr("Unable to subscribe"), QSqlError::StatementError, d, result));
PQclear(result);
return false;