diff options
Diffstat (limited to 'src/plugins/sqldrivers/psql/qsql_psql.cpp')
-rw-r--r-- | src/plugins/sqldrivers/psql/qsql_psql.cpp | 584 |
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; |