/src/sql/drivers/psql/qsql_psql.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 1372 lines · 1188 code · 112 blank · 72 comment · 211 complexity · f314a5240eeeda5c11609ba86e2a8726 MD5 · raw file

  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
  4. ** All rights reserved.
  5. ** Contact: Nokia Corporation (qt-info@nokia.com)
  6. **
  7. ** This file is part of the QtSql module of the Qt Toolkit.
  8. **
  9. ** $QT_BEGIN_LICENSE:LGPL$
  10. ** GNU Lesser General Public License Usage
  11. ** This file may be used under the terms of the GNU Lesser General Public
  12. ** License version 2.1 as published by the Free Software Foundation and
  13. ** appearing in the file LICENSE.LGPL included in the packaging of this
  14. ** file. Please review the following information to ensure the GNU Lesser
  15. ** General Public License version 2.1 requirements will be met:
  16. ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  17. **
  18. ** In addition, as a special exception, Nokia gives you certain additional
  19. ** rights. These rights are described in the Nokia Qt LGPL Exception
  20. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  21. **
  22. ** GNU General Public License Usage
  23. ** Alternatively, this file may be used under the terms of the GNU General
  24. ** Public License version 3.0 as published by the Free Software Foundation
  25. ** and appearing in the file LICENSE.GPL included in the packaging of this
  26. ** file. Please review the following information to ensure the GNU General
  27. ** Public License version 3.0 requirements will be met:
  28. ** http://www.gnu.org/copyleft/gpl.html.
  29. **
  30. ** Other Usage
  31. ** Alternatively, this file may be used in accordance with the terms and
  32. ** conditions contained in a signed written agreement between you and Nokia.
  33. **
  34. **
  35. **
  36. **
  37. **
  38. ** $QT_END_LICENSE$
  39. **
  40. ****************************************************************************/
  41. #include "qsql_psql.h"
  42. #include <qcoreapplication.h>
  43. #include <qvariant.h>
  44. #include <qdatetime.h>
  45. #include <qregexp.h>
  46. #include <qsqlerror.h>
  47. #include <qsqlfield.h>
  48. #include <qsqlindex.h>
  49. #include <qsqlrecord.h>
  50. #include <qsqlquery.h>
  51. #include <qsocketnotifier.h>
  52. #include <qstringlist.h>
  53. #include <qmutex.h>
  54. #include <libpq-fe.h>
  55. #include <pg_config.h>
  56. #include <stdlib.h>
  57. #include <math.h>
  58. // below code taken from an example at http://www.gnu.org/software/hello/manual/autoconf/Function-Portability.html
  59. #ifndef isnan
  60. # define isnan(x) \
  61. (sizeof (x) == sizeof (long double) ? isnan_ld (x) \
  62. : sizeof (x) == sizeof (double) ? isnan_d (x) \
  63. : isnan_f (x))
  64. static inline int isnan_f (float x) { return x != x; }
  65. static inline int isnan_d (double x) { return x != x; }
  66. static inline int isnan_ld (long double x) { return x != x; }
  67. #endif
  68. #ifndef isinf
  69. # define isinf(x) \
  70. (sizeof (x) == sizeof (long double) ? isinf_ld (x) \
  71. : sizeof (x) == sizeof (double) ? isinf_d (x) \
  72. : isinf_f (x))
  73. static inline int isinf_f (float x) { return isnan (x - x); }
  74. static inline int isinf_d (double x) { return isnan (x - x); }
  75. static inline int isinf_ld (long double x) { return isnan (x - x); }
  76. #endif
  77. // workaround for postgres defining their OIDs in a private header file
  78. #define QBOOLOID 16
  79. #define QINT8OID 20
  80. #define QINT2OID 21
  81. #define QINT4OID 23
  82. #define QNUMERICOID 1700
  83. #define QFLOAT4OID 700
  84. #define QFLOAT8OID 701
  85. #define QABSTIMEOID 702
  86. #define QRELTIMEOID 703
  87. #define QDATEOID 1082
  88. #define QTIMEOID 1083
  89. #define QTIMETZOID 1266
  90. #define QTIMESTAMPOID 1114
  91. #define QTIMESTAMPTZOID 1184
  92. #define QOIDOID 2278
  93. #define QBYTEAOID 17
  94. #define QREGPROCOID 24
  95. #define QXIDOID 28
  96. #define QCIDOID 29
  97. /* This is a compile time switch - if PQfreemem is declared, the compiler will use that one,
  98. otherwise it'll run in this template */
  99. template <typename T>
  100. inline void PQfreemem(T *t, int = 0) { free(t); }
  101. Q_DECLARE_METATYPE(PGconn*)
  102. Q_DECLARE_METATYPE(PGresult*)
  103. QT_BEGIN_NAMESPACE
  104. inline void qPQfreemem(void *buffer)
  105. {
  106. PQfreemem(buffer);
  107. }
  108. class QPSQLDriverPrivate
  109. {
  110. public:
  111. QPSQLDriverPrivate() : connection(0), isUtf8(false), pro(QPSQLDriver::Version6), sn(0) {}
  112. PGconn *connection;
  113. bool isUtf8;
  114. QPSQLDriver::Protocol pro;
  115. QSocketNotifier *sn;
  116. QStringList seid;
  117. void appendTables(QStringList &tl, QSqlQuery &t, QChar type);
  118. };
  119. void QPSQLDriverPrivate::appendTables(QStringList &tl, QSqlQuery &t, QChar type)
  120. {
  121. QString query;
  122. if (pro >= QPSQLDriver::Version73) {
  123. query = QString::fromLatin1("select pg_class.relname, pg_namespace.nspname from pg_class "
  124. "left join pg_namespace on (pg_class.relnamespace = pg_namespace.oid) "
  125. "where (pg_class.relkind = '%1') and (pg_class.relname !~ '^Inv') "
  126. "and (pg_class.relname !~ '^pg_') "
  127. "and (pg_namespace.nspname != 'information_schema') ").arg(type);
  128. } else {
  129. query = QString::fromLatin1("select relname, null from pg_class where (relkind = '%1') "
  130. "and (relname !~ '^Inv') "
  131. "and (relname !~ '^pg_') ").arg(type);
  132. }
  133. t.exec(query);
  134. while (t.next()) {
  135. QString schema = t.value(1).toString();
  136. if (schema.isEmpty() || schema == QLatin1String("public"))
  137. tl.append(t.value(0).toString());
  138. else
  139. tl.append(t.value(0).toString().prepend(QLatin1Char('.')).prepend(schema));
  140. }
  141. }
  142. class QPSQLResultPrivate
  143. {
  144. public:
  145. QPSQLResultPrivate(QPSQLResult *qq): q(qq), driver(0), result(0), currentSize(-1), preparedQueriesEnabled(false) {}
  146. QPSQLResult *q;
  147. const QPSQLDriverPrivate *driver;
  148. PGresult *result;
  149. int currentSize;
  150. bool preparedQueriesEnabled;
  151. QString preparedStmtId;
  152. bool processResults();
  153. };
  154. static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
  155. const QPSQLDriverPrivate *p)
  156. {
  157. const char *s = PQerrorMessage(p->connection);
  158. QString msg = p->isUtf8 ? QString::fromUtf8(s) : QString::fromLocal8Bit(s);
  159. return QSqlError(QLatin1String("QPSQL: ") + err, msg, type);
  160. }
  161. bool QPSQLResultPrivate::processResults()
  162. {
  163. if (!result)
  164. return false;
  165. int status = PQresultStatus(result);
  166. if (status == PGRES_TUPLES_OK) {
  167. q->setSelect(true);
  168. q->setActive(true);
  169. currentSize = PQntuples(result);
  170. return true;
  171. } else if (status == PGRES_COMMAND_OK) {
  172. q->setSelect(false);
  173. q->setActive(true);
  174. currentSize = -1;
  175. return true;
  176. }
  177. q->setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
  178. "Unable to create query"), QSqlError::StatementError, driver));
  179. return false;
  180. }
  181. static QVariant::Type qDecodePSQLType(int t)
  182. {
  183. QVariant::Type type = QVariant::Invalid;
  184. switch (t) {
  185. case QBOOLOID:
  186. type = QVariant::Bool;
  187. break;
  188. case QINT8OID:
  189. type = QVariant::LongLong;
  190. break;
  191. case QINT2OID:
  192. case QINT4OID:
  193. case QOIDOID:
  194. case QREGPROCOID:
  195. case QXIDOID:
  196. case QCIDOID:
  197. type = QVariant::Int;
  198. break;
  199. case QNUMERICOID:
  200. case QFLOAT4OID:
  201. case QFLOAT8OID:
  202. type = QVariant::Double;
  203. break;
  204. case QABSTIMEOID:
  205. case QRELTIMEOID:
  206. case QDATEOID:
  207. type = QVariant::Date;
  208. break;
  209. case QTIMEOID:
  210. case QTIMETZOID:
  211. type = QVariant::Time;
  212. break;
  213. case QTIMESTAMPOID:
  214. case QTIMESTAMPTZOID:
  215. type = QVariant::DateTime;
  216. break;
  217. case QBYTEAOID:
  218. type = QVariant::ByteArray;
  219. break;
  220. default:
  221. type = QVariant::String;
  222. break;
  223. }
  224. return type;
  225. }
  226. static void qDeallocatePreparedStmt(QPSQLResultPrivate *d)
  227. {
  228. const QString stmt = QLatin1String("DEALLOCATE ") + d->preparedStmtId;
  229. PGresult *result = PQexec(d->driver->connection,
  230. d->driver->isUtf8 ? stmt.toUtf8().constData()
  231. : stmt.toLocal8Bit().constData());
  232. if (PQresultStatus(result) != PGRES_COMMAND_OK)
  233. qWarning("Unable to free statement: %s", PQerrorMessage(d->driver->connection));
  234. PQclear(result);
  235. d->preparedStmtId.clear();
  236. }
  237. QPSQLResult::QPSQLResult(const QPSQLDriver* db, const QPSQLDriverPrivate* p)
  238. : QSqlResult(db)
  239. {
  240. d = new QPSQLResultPrivate(this);
  241. d->driver = p;
  242. d->preparedQueriesEnabled = db->hasFeature(QSqlDriver::PreparedQueries);
  243. }
  244. QPSQLResult::~QPSQLResult()
  245. {
  246. cleanup();
  247. if (d->preparedQueriesEnabled && !d->preparedStmtId.isNull())
  248. qDeallocatePreparedStmt(d);
  249. delete d;
  250. }
  251. QVariant QPSQLResult::handle() const
  252. {
  253. return QVariant::fromValue(d->result);
  254. }
  255. void QPSQLResult::cleanup()
  256. {
  257. if (d->result)
  258. PQclear(d->result);
  259. d->result = 0;
  260. setAt(QSql::BeforeFirstRow);
  261. d->currentSize = -1;
  262. setActive(false);
  263. }
  264. bool QPSQLResult::fetch(int i)
  265. {
  266. if (!isActive())
  267. return false;
  268. if (i < 0)
  269. return false;
  270. if (i >= d->currentSize)
  271. return false;
  272. if (at() == i)
  273. return true;
  274. setAt(i);
  275. return true;
  276. }
  277. bool QPSQLResult::fetchFirst()
  278. {
  279. return fetch(0);
  280. }
  281. bool QPSQLResult::fetchLast()
  282. {
  283. return fetch(PQntuples(d->result) - 1);
  284. }
  285. QVariant QPSQLResult::data(int i)
  286. {
  287. if (i >= PQnfields(d->result)) {
  288. qWarning("QPSQLResult::data: column %d out of range", i);
  289. return QVariant();
  290. }
  291. int ptype = PQftype(d->result, i);
  292. QVariant::Type type = qDecodePSQLType(ptype);
  293. const char *val = PQgetvalue(d->result, at(), i);
  294. if (PQgetisnull(d->result, at(), i))
  295. return QVariant(type);
  296. switch (type) {
  297. case QVariant::Bool:
  298. return QVariant((bool)(val[0] == 't'));
  299. case QVariant::String:
  300. return d->driver->isUtf8 ? QString::fromUtf8(val) : QString::fromAscii(val);
  301. case QVariant::LongLong:
  302. if (val[0] == '-')
  303. return QString::fromLatin1(val).toLongLong();
  304. else
  305. return QString::fromLatin1(val).toULongLong();
  306. case QVariant::Int:
  307. return atoi(val);
  308. case QVariant::Double:
  309. if (ptype == QNUMERICOID) {
  310. if (numericalPrecisionPolicy() != QSql::HighPrecision) {
  311. QVariant retval;
  312. bool convert;
  313. double dbl=QString::fromAscii(val).toDouble(&convert);
  314. if (numericalPrecisionPolicy() == QSql::LowPrecisionInt64)
  315. retval = (qlonglong)dbl;
  316. else if (numericalPrecisionPolicy() == QSql::LowPrecisionInt32)
  317. retval = (int)dbl;
  318. else if (numericalPrecisionPolicy() == QSql::LowPrecisionDouble)
  319. retval = dbl;
  320. if (!convert)
  321. return QVariant();
  322. return retval;
  323. }
  324. return QString::fromAscii(val);
  325. }
  326. return QString::fromAscii(val).toDouble();
  327. case QVariant::Date:
  328. if (val[0] == '\0') {
  329. return QVariant(QDate());
  330. } else {
  331. #ifndef QT_NO_DATESTRING
  332. return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate));
  333. #else
  334. return QVariant(QString::fromLatin1(val));
  335. #endif
  336. }
  337. case QVariant::Time: {
  338. const QString str = QString::fromLatin1(val);
  339. #ifndef QT_NO_DATESTRING
  340. if (str.isEmpty())
  341. return QVariant(QTime());
  342. if (str.at(str.length() - 3) == QLatin1Char('+') || str.at(str.length() - 3) == QLatin1Char('-'))
  343. // strip the timezone
  344. // TODO: fix this when timestamp support comes into QDateTime
  345. return QVariant(QTime::fromString(str.left(str.length() - 3), Qt::ISODate));
  346. return QVariant(QTime::fromString(str, Qt::ISODate));
  347. #else
  348. return QVariant(str);
  349. #endif
  350. }
  351. case QVariant::DateTime: {
  352. QString dtval = QString::fromLatin1(val);
  353. #ifndef QT_NO_DATESTRING
  354. if (dtval.length() < 10)
  355. return QVariant(QDateTime());
  356. // remove the timezone
  357. // TODO: fix this when timestamp support comes into QDateTime
  358. if (dtval.at(dtval.length() - 3) == QLatin1Char('+') || dtval.at(dtval.length() - 3) == QLatin1Char('-'))
  359. dtval.chop(3);
  360. // milliseconds are sometimes returned with 2 digits only
  361. if (dtval.at(dtval.length() - 3).isPunct())
  362. dtval += QLatin1Char('0');
  363. if (dtval.isEmpty())
  364. return QVariant(QDateTime());
  365. else
  366. return QVariant(QDateTime::fromString(dtval, Qt::ISODate));
  367. #else
  368. return QVariant(dtval);
  369. #endif
  370. }
  371. case QVariant::ByteArray: {
  372. size_t len;
  373. unsigned char *data = PQunescapeBytea((unsigned char*)val, &len);
  374. QByteArray ba((const char*)data, len);
  375. qPQfreemem(data);
  376. return QVariant(ba);
  377. }
  378. default:
  379. case QVariant::Invalid:
  380. qWarning("QPSQLResult::data: unknown data type");
  381. }
  382. return QVariant();
  383. }
  384. bool QPSQLResult::isNull(int field)
  385. {
  386. PQgetvalue(d->result, at(), field);
  387. return PQgetisnull(d->result, at(), field);
  388. }
  389. bool QPSQLResult::reset (const QString& query)
  390. {
  391. cleanup();
  392. if (!driver())
  393. return false;
  394. if (!driver()->isOpen() || driver()->isOpenError())
  395. return false;
  396. d->result = PQexec(d->driver->connection,
  397. d->driver->isUtf8 ? query.toUtf8().constData()
  398. : query.toLocal8Bit().constData());
  399. return d->processResults();
  400. }
  401. int QPSQLResult::size()
  402. {
  403. return d->currentSize;
  404. }
  405. int QPSQLResult::numRowsAffected()
  406. {
  407. return QString::fromLatin1(PQcmdTuples(d->result)).toInt();
  408. }
  409. QVariant QPSQLResult::lastInsertId() const
  410. {
  411. if (isActive()) {
  412. Oid id = PQoidValue(d->result);
  413. if (id != InvalidOid)
  414. return QVariant(id);
  415. }
  416. return QVariant();
  417. }
  418. QSqlRecord QPSQLResult::record() const
  419. {
  420. QSqlRecord info;
  421. if (!isActive() || !isSelect())
  422. return info;
  423. int count = PQnfields(d->result);
  424. for (int i = 0; i < count; ++i) {
  425. QSqlField f;
  426. if (d->driver->isUtf8)
  427. f.setName(QString::fromUtf8(PQfname(d->result, i)));
  428. else
  429. f.setName(QString::fromLocal8Bit(PQfname(d->result, i)));
  430. f.setType(qDecodePSQLType(PQftype(d->result, i)));
  431. int len = PQfsize(d->result, i);
  432. int precision = PQfmod(d->result, i);
  433. // swap length and precision if length == -1
  434. if (len == -1 && precision > -1) {
  435. len = precision - 4;
  436. precision = -1;
  437. }
  438. f.setLength(len);
  439. f.setPrecision(precision);
  440. f.setSqlType(PQftype(d->result, i));
  441. info.append(f);
  442. }
  443. return info;
  444. }
  445. void QPSQLResult::virtual_hook(int id, void *data)
  446. {
  447. Q_ASSERT(data);
  448. switch (id) {
  449. default:
  450. QSqlResult::virtual_hook(id, data);
  451. }
  452. }
  453. static QString qReplacePlaceholderMarkers(const QString &query)
  454. {
  455. const int originalLength = query.length();
  456. bool inQuote = false;
  457. int markerIdx = 0;
  458. QString result;
  459. result.reserve(originalLength + 23);
  460. for (int i = 0; i < originalLength; ++i) {
  461. const QChar ch = query.at(i);
  462. if (ch == QLatin1Char('?') && !inQuote) {
  463. result += QLatin1Char('$');
  464. result += QString::number(++markerIdx);
  465. } else {
  466. if (ch == QLatin1Char('\''))
  467. inQuote = !inQuote;
  468. result += ch;
  469. }
  470. }
  471. result.squeeze();
  472. return result;
  473. }
  474. static QString qCreateParamString(const QVector<QVariant> boundValues, const QSqlDriver *driver)
  475. {
  476. if (boundValues.isEmpty())
  477. return QString();
  478. QString params;
  479. QSqlField f;
  480. for (int i = 0; i < boundValues.count(); ++i) {
  481. const QVariant &val = boundValues.at(i);
  482. f.setType(val.type());
  483. if (val.isNull())
  484. f.clear();
  485. else
  486. f.setValue(val);
  487. if(!params.isNull())
  488. params.append(QLatin1String(", "));
  489. params.append(driver->formatValue(f));
  490. }
  491. return params;
  492. }
  493. Q_GLOBAL_STATIC(QMutex, qMutex)
  494. QString qMakePreparedStmtId()
  495. {
  496. qMutex()->lock();
  497. static unsigned int qPreparedStmtCount = 0;
  498. QString id = QLatin1String("qpsqlpstmt_") + QString::number(++qPreparedStmtCount, 16);
  499. qMutex()->unlock();
  500. return id;
  501. }
  502. bool QPSQLResult::prepare(const QString &query)
  503. {
  504. if (!d->preparedQueriesEnabled)
  505. return QSqlResult::prepare(query);
  506. cleanup();
  507. if (!d->preparedStmtId.isEmpty())
  508. qDeallocatePreparedStmt(d);
  509. const QString stmtId = qMakePreparedStmtId();
  510. const QString stmt = QString::fromLatin1("PREPARE %1 AS ").arg(stmtId).append(qReplacePlaceholderMarkers(query));
  511. PGresult *result = PQexec(d->driver->connection,
  512. d->driver->isUtf8 ? stmt.toUtf8().constData()
  513. : stmt.toLocal8Bit().constData());
  514. if (PQresultStatus(result) != PGRES_COMMAND_OK) {
  515. setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
  516. "Unable to prepare statement"), QSqlError::StatementError, d->driver));
  517. PQclear(result);
  518. d->preparedStmtId.clear();
  519. return false;
  520. }
  521. PQclear(result);
  522. d->preparedStmtId = stmtId;
  523. return true;
  524. }
  525. bool QPSQLResult::exec()
  526. {
  527. if (!d->preparedQueriesEnabled)
  528. return QSqlResult::exec();
  529. cleanup();
  530. QString stmt;
  531. const QString params = qCreateParamString(boundValues(), d->q->driver());
  532. if (params.isEmpty())
  533. stmt = QString::fromLatin1("EXECUTE %1").arg(d->preparedStmtId);
  534. else
  535. stmt = QString::fromLatin1("EXECUTE %1 (%2)").arg(d->preparedStmtId).arg(params);
  536. d->result = PQexec(d->driver->connection,
  537. d->driver->isUtf8 ? stmt.toUtf8().constData()
  538. : stmt.toLocal8Bit().constData());
  539. return d->processResults();
  540. }
  541. ///////////////////////////////////////////////////////////////////
  542. static bool setEncodingUtf8(PGconn* connection)
  543. {
  544. PGresult* result = PQexec(connection, "SET CLIENT_ENCODING TO 'UNICODE'");
  545. int status = PQresultStatus(result);
  546. PQclear(result);
  547. return status == PGRES_COMMAND_OK;
  548. }
  549. static void setDatestyle(PGconn* connection)
  550. {
  551. PGresult* result = PQexec(connection, "SET DATESTYLE TO 'ISO'");
  552. int status = PQresultStatus(result);
  553. if (status != PGRES_COMMAND_OK)
  554. qWarning("%s", PQerrorMessage(connection));
  555. PQclear(result);
  556. }
  557. static QPSQLDriver::Protocol qMakePSQLVersion(int vMaj, int vMin)
  558. {
  559. switch (vMaj) {
  560. case 6:
  561. return QPSQLDriver::Version6;
  562. case 7:
  563. {
  564. switch (vMin) {
  565. case 1:
  566. return QPSQLDriver::Version71;
  567. case 3:
  568. return QPSQLDriver::Version73;
  569. case 4:
  570. return QPSQLDriver::Version74;
  571. default:
  572. return QPSQLDriver::Version7;
  573. }
  574. break;
  575. }
  576. case 8:
  577. {
  578. switch (vMin) {
  579. case 1:
  580. return QPSQLDriver::Version81;
  581. case 2:
  582. return QPSQLDriver::Version82;
  583. case 3:
  584. return QPSQLDriver::Version83;
  585. case 4:
  586. return QPSQLDriver::Version84;
  587. default:
  588. return QPSQLDriver::Version8;
  589. }
  590. break;
  591. }
  592. case 9:
  593. return QPSQLDriver::Version9;
  594. break;
  595. default:
  596. break;
  597. }
  598. return QPSQLDriver::VersionUnknown;
  599. }
  600. static QPSQLDriver::Protocol getPSQLVersion(PGconn* connection)
  601. {
  602. QPSQLDriver::Protocol serverVersion = QPSQLDriver::Version6;
  603. PGresult* result = PQexec(connection, "select version()");
  604. int status = PQresultStatus(result);
  605. if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) {
  606. QString val = QString::fromAscii(PQgetvalue(result, 0, 0));
  607. QRegExp rx(QLatin1String("(\\d+)\\.(\\d+)"));
  608. rx.setMinimal(true); // enforce non-greedy RegExp
  609. if (rx.indexIn(val) != -1) {
  610. int vMaj = rx.cap(1).toInt();
  611. int vMin = rx.cap(2).toInt();
  612. serverVersion = qMakePSQLVersion(vMaj, vMin);
  613. #ifdef PG_MAJORVERSION
  614. if (rx.indexIn(QLatin1String(PG_MAJORVERSION)) != -1) {
  615. vMaj = rx.cap(1).toInt();
  616. vMin = rx.cap(2).toInt();
  617. }
  618. QPSQLDriver::Protocol clientVersion = qMakePSQLVersion(vMaj, vMin);
  619. if (serverVersion >= QPSQLDriver::Version9 && clientVersion < QPSQLDriver::Version9) {
  620. //Client version before QPSQLDriver::Version9 only supports escape mode for bytea type,
  621. //but bytea format is set to hex by default in PSQL 9 and above. So need to force the
  622. //server use the old escape mode when connects to the new server with old client library.
  623. result = PQexec(connection, "SET bytea_output=escape; ");
  624. status = PQresultStatus(result);
  625. } else if (serverVersion == QPSQLDriver::VersionUnknown) {
  626. serverVersion = clientVersion;
  627. if (serverVersion != QPSQLDriver::VersionUnknown)
  628. qWarning("The server version of this PostgreSQL is unknown, falling back to the client version.");
  629. }
  630. #endif
  631. }
  632. }
  633. PQclear(result);
  634. //keep the old behavior unchanged
  635. if (serverVersion == QPSQLDriver::VersionUnknown)
  636. serverVersion = QPSQLDriver::Version6;
  637. if (serverVersion < QPSQLDriver::Version71) {
  638. qWarning("This version of PostgreSQL is not supported and may not work.");
  639. }
  640. return serverVersion;
  641. }
  642. QPSQLDriver::QPSQLDriver(QObject *parent)
  643. : QSqlDriver(parent)
  644. {
  645. init();
  646. }
  647. QPSQLDriver::QPSQLDriver(PGconn *conn, QObject *parent)
  648. : QSqlDriver(parent)
  649. {
  650. init();
  651. d->connection = conn;
  652. if (conn) {
  653. d->pro = getPSQLVersion(d->connection);
  654. setOpen(true);
  655. setOpenError(false);
  656. }
  657. }
  658. void QPSQLDriver::init()
  659. {
  660. d = new QPSQLDriverPrivate();
  661. }
  662. QPSQLDriver::~QPSQLDriver()
  663. {
  664. if (d->connection)
  665. PQfinish(d->connection);
  666. delete d;
  667. }
  668. QVariant QPSQLDriver::handle() const
  669. {
  670. return QVariant::fromValue(d->connection);
  671. }
  672. bool QPSQLDriver::hasFeature(DriverFeature f) const
  673. {
  674. switch (f) {
  675. case Transactions:
  676. case QuerySize:
  677. case LastInsertId:
  678. case LowPrecisionNumbers:
  679. case EventNotifications:
  680. return true;
  681. case PreparedQueries:
  682. case PositionalPlaceholders:
  683. return d->pro >= QPSQLDriver::Version82;
  684. case BatchOperations:
  685. case NamedPlaceholders:
  686. case SimpleLocking:
  687. case FinishQuery:
  688. case MultipleResultSets:
  689. return false;
  690. case BLOB:
  691. return d->pro >= QPSQLDriver::Version71;
  692. case Unicode:
  693. return d->isUtf8;
  694. }
  695. return false;
  696. }
  697. /*
  698. Quote a string for inclusion into the connection string
  699. \ -> \\
  700. ' -> \'
  701. surround string by single quotes
  702. */
  703. static QString qQuote(QString s)
  704. {
  705. s.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
  706. s.replace(QLatin1Char('\''), QLatin1String("\\'"));
  707. s.append(QLatin1Char('\'')).prepend(QLatin1Char('\''));
  708. return s;
  709. }
  710. bool QPSQLDriver::open(const QString & db,
  711. const QString & user,
  712. const QString & password,
  713. const QString & host,
  714. int port,
  715. const QString& connOpts)
  716. {
  717. if (isOpen())
  718. close();
  719. QString connectString;
  720. if (!host.isEmpty())
  721. connectString.append(QLatin1String("host=")).append(qQuote(host));
  722. if (!db.isEmpty())
  723. connectString.append(QLatin1String(" dbname=")).append(qQuote(db));
  724. if (!user.isEmpty())
  725. connectString.append(QLatin1String(" user=")).append(qQuote(user));
  726. if (!password.isEmpty())
  727. connectString.append(QLatin1String(" password=")).append(qQuote(password));
  728. if (port != -1)
  729. connectString.append(QLatin1String(" port=")).append(qQuote(QString::number(port)));
  730. // add any connect options - the server will handle error detection
  731. if (!connOpts.isEmpty()) {
  732. QString opt = connOpts;
  733. opt.replace(QLatin1Char(';'), QLatin1Char(' '), Qt::CaseInsensitive);
  734. connectString.append(QLatin1Char(' ')).append(opt);
  735. }
  736. d->connection = PQconnectdb(connectString.toLocal8Bit().constData());
  737. if (PQstatus(d->connection) == CONNECTION_BAD) {
  738. setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
  739. setOpenError(true);
  740. PQfinish(d->connection);
  741. d->connection = 0;
  742. return false;
  743. }
  744. d->pro = getPSQLVersion(d->connection);
  745. d->isUtf8 = setEncodingUtf8(d->connection);
  746. setDatestyle(d->connection);
  747. setOpen(true);
  748. setOpenError(false);
  749. return true;
  750. }
  751. void QPSQLDriver::close()
  752. {
  753. if (isOpen()) {
  754. d->seid.clear();
  755. if (d->sn) {
  756. disconnect(d->sn, SIGNAL(activated(int)), this, SLOT(_q_handleNotification(int)));
  757. delete d->sn;
  758. d->sn = 0;
  759. }
  760. if (d->connection)
  761. PQfinish(d->connection);
  762. d->connection = 0;
  763. setOpen(false);
  764. setOpenError(false);
  765. }
  766. }
  767. QSqlResult *QPSQLDriver::createResult() const
  768. {
  769. return new QPSQLResult(this, d);
  770. }
  771. bool QPSQLDriver::beginTransaction()
  772. {
  773. if (!isOpen()) {
  774. qWarning("QPSQLDriver::beginTransaction: Database not open");
  775. return false;
  776. }
  777. PGresult* res = PQexec(d->connection, "BEGIN");
  778. if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
  779. PQclear(res);
  780. setLastError(qMakeError(tr("Could not begin transaction"),
  781. QSqlError::TransactionError, d));
  782. return false;
  783. }
  784. PQclear(res);
  785. return true;
  786. }
  787. bool QPSQLDriver::commitTransaction()
  788. {
  789. if (!isOpen()) {
  790. qWarning("QPSQLDriver::commitTransaction: Database not open");
  791. return false;
  792. }
  793. PGresult* res = PQexec(d->connection, "COMMIT");
  794. bool transaction_failed = false;
  795. // XXX
  796. // This hack is used to tell if the transaction has succeeded for the protocol versions of
  797. // PostgreSQL below. For 7.x and other protocol versions we are left in the dark.
  798. // This hack can dissapear once there is an API to query this sort of information.
  799. if (d->pro == QPSQLDriver::Version8 ||
  800. d->pro == QPSQLDriver::Version81 ||
  801. d->pro == QPSQLDriver::Version82 ||
  802. d->pro == QPSQLDriver::Version83 ||
  803. d->pro == QPSQLDriver::Version84 ||
  804. d->pro == QPSQLDriver::Version9) {
  805. transaction_failed = qstrcmp(PQcmdStatus(res), "ROLLBACK") == 0;
  806. }
  807. if (!res || PQresultStatus(res) != PGRES_COMMAND_OK || transaction_failed) {
  808. PQclear(res);
  809. setLastError(qMakeError(tr("Could not commit transaction"),
  810. QSqlError::TransactionError, d));
  811. return false;
  812. }
  813. PQclear(res);
  814. return true;
  815. }
  816. bool QPSQLDriver::rollbackTransaction()
  817. {
  818. if (!isOpen()) {
  819. qWarning("QPSQLDriver::rollbackTransaction: Database not open");
  820. return false;
  821. }
  822. PGresult* res = PQexec(d->connection, "ROLLBACK");
  823. if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
  824. setLastError(qMakeError(tr("Could not rollback transaction"),
  825. QSqlError::TransactionError, d));
  826. PQclear(res);
  827. return false;
  828. }
  829. PQclear(res);
  830. return true;
  831. }
  832. QStringList QPSQLDriver::tables(QSql::TableType type) const
  833. {
  834. QStringList tl;
  835. if (!isOpen())
  836. return tl;
  837. QSqlQuery t(createResult());
  838. t.setForwardOnly(true);
  839. if (type & QSql::Tables)
  840. d->appendTables(tl, t, QLatin1Char('r'));
  841. if (type & QSql::Views)
  842. d->appendTables(tl, t, QLatin1Char('v'));
  843. if (type & QSql::SystemTables) {
  844. t.exec(QLatin1String("select relname from pg_class where (relkind = 'r') "
  845. "and (relname like 'pg_%') "));
  846. while (t.next())
  847. tl.append(t.value(0).toString());
  848. }
  849. return tl;
  850. }
  851. static void qSplitTableName(QString &tablename, QString &schema)
  852. {
  853. int dot = tablename.indexOf(QLatin1Char('.'));
  854. if (dot == -1)
  855. return;
  856. schema = tablename.left(dot);
  857. tablename = tablename.mid(dot + 1);
  858. }
  859. QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
  860. {
  861. QSqlIndex idx(tablename);
  862. if (!isOpen())
  863. return idx;
  864. QSqlQuery i(createResult());
  865. QString stmt;
  866. QString tbl = tablename;
  867. QString schema;
  868. qSplitTableName(tbl, schema);
  869. if (isIdentifierEscaped(tbl, QSqlDriver::TableName))
  870. tbl = stripDelimiters(tbl, QSqlDriver::TableName);
  871. else
  872. tbl = tbl.toLower();
  873. if (isIdentifierEscaped(schema, QSqlDriver::TableName))
  874. schema = stripDelimiters(schema, QSqlDriver::TableName);
  875. else
  876. schema = schema.toLower();
  877. switch(d->pro) {
  878. case QPSQLDriver::Version6:
  879. stmt = QLatin1String("select pg_att1.attname, int(pg_att1.atttypid), pg_cl.relname "
  880. "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
  881. "where pg_cl.relname = '%1_pkey' "
  882. "and pg_cl.oid = pg_ind.indexrelid "
  883. "and pg_att2.attrelid = pg_ind.indexrelid "
  884. "and pg_att1.attrelid = pg_ind.indrelid "
  885. "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] "
  886. "order by pg_att2.attnum");
  887. break;
  888. case QPSQLDriver::Version7:
  889. case QPSQLDriver::Version71:
  890. stmt = QLatin1String("select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname "
  891. "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
  892. "where pg_cl.relname = '%1_pkey' "
  893. "and pg_cl.oid = pg_ind.indexrelid "
  894. "and pg_att2.attrelid = pg_ind.indexrelid "
  895. "and pg_att1.attrelid = pg_ind.indrelid "
  896. "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] "
  897. "order by pg_att2.attnum");
  898. break;
  899. case QPSQLDriver::Version73:
  900. case QPSQLDriver::Version74:
  901. case QPSQLDriver::Version8:
  902. case QPSQLDriver::Version81:
  903. case QPSQLDriver::Version82:
  904. case QPSQLDriver::Version83:
  905. case QPSQLDriver::Version84:
  906. case QPSQLDriver::Version9:
  907. stmt = QLatin1String("SELECT pg_attribute.attname, pg_attribute.atttypid::int, "
  908. "pg_class.relname "
  909. "FROM pg_attribute, pg_class "
  910. "WHERE %1 pg_class.oid IN "
  911. "(SELECT indexrelid FROM pg_index WHERE indisprimary = true AND indrelid IN "
  912. " (SELECT oid FROM pg_class WHERE relname = '%2')) "
  913. "AND pg_attribute.attrelid = pg_class.oid "
  914. "AND pg_attribute.attisdropped = false "
  915. "ORDER BY pg_attribute.attnum");
  916. if (schema.isEmpty())
  917. stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid) AND"));
  918. else
  919. stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
  920. "pg_namespace where pg_namespace.nspname = '%1') AND ").arg(schema));
  921. break;
  922. case QPSQLDriver::VersionUnknown:
  923. qFatal("PSQL version is unknown");
  924. break;
  925. }
  926. i.exec(stmt.arg(tbl));
  927. while (i.isActive() && i.next()) {
  928. QSqlField f(i.value(0).toString(), qDecodePSQLType(i.value(1).toInt()));
  929. idx.append(f);
  930. idx.setName(i.value(2).toString());
  931. }
  932. return idx;
  933. }
  934. QSqlRecord QPSQLDriver::record(const QString& tablename) const
  935. {
  936. QSqlRecord info;
  937. if (!isOpen())
  938. return info;
  939. QString tbl = tablename;
  940. QString schema;
  941. qSplitTableName(tbl, schema);
  942. if (isIdentifierEscaped(tbl, QSqlDriver::TableName))
  943. tbl = stripDelimiters(tbl, QSqlDriver::TableName);
  944. else
  945. tbl = tbl.toLower();
  946. if (isIdentifierEscaped(schema, QSqlDriver::TableName))
  947. schema = stripDelimiters(schema, QSqlDriver::TableName);
  948. else
  949. schema = schema.toLower();
  950. QString stmt;
  951. switch(d->pro) {
  952. case QPSQLDriver::Version6:
  953. stmt = QLatin1String("select pg_attribute.attname, int(pg_attribute.atttypid), "
  954. "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
  955. "int(pg_attribute.attrelid), pg_attribute.attnum "
  956. "from pg_class, pg_attribute "
  957. "where pg_class.relname = '%1' "
  958. "and pg_attribute.attnum > 0 "
  959. "and pg_attribute.attrelid = pg_class.oid ");
  960. break;
  961. case QPSQLDriver::Version7:
  962. stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
  963. "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
  964. "pg_attribute.attrelid::int, pg_attribute.attnum "
  965. "from pg_class, pg_attribute "
  966. "where pg_class.relname = '%1' "
  967. "and pg_attribute.attnum > 0 "
  968. "and pg_attribute.attrelid = pg_class.oid ");
  969. break;
  970. case QPSQLDriver::Version71:
  971. stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
  972. "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
  973. "pg_attrdef.adsrc "
  974. "from pg_class, pg_attribute "
  975. "left join pg_attrdef on (pg_attrdef.adrelid = "
  976. "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
  977. "where pg_class.relname = '%1' "
  978. "and pg_attribute.attnum > 0 "
  979. "and pg_attribute.attrelid = pg_class.oid "
  980. "order by pg_attribute.attnum ");
  981. break;
  982. case QPSQLDriver::Version73:
  983. case QPSQLDriver::Version74:
  984. case QPSQLDriver::Version8:
  985. case QPSQLDriver::Version81:
  986. case QPSQLDriver::Version82:
  987. case QPSQLDriver::Version83:
  988. case QPSQLDriver::Version84:
  989. case QPSQLDriver::Version9:
  990. stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
  991. "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
  992. "pg_attrdef.adsrc "
  993. "from pg_class, pg_attribute "
  994. "left join pg_attrdef on (pg_attrdef.adrelid = "
  995. "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
  996. "where %1 "
  997. "and pg_class.relname = '%2' "
  998. "and pg_attribute.attnum > 0 "
  999. "and pg_attribute.attrelid = pg_class.oid "
  1000. "and pg_attribute.attisdropped = false "
  1001. "order by pg_attribute.attnum ");
  1002. if (schema.isEmpty())
  1003. stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid)"));
  1004. else
  1005. stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
  1006. "pg_namespace where pg_namespace.nspname = '%1')").arg(schema));
  1007. break;
  1008. case QPSQLDriver::VersionUnknown:
  1009. qFatal("PSQL version is unknown");
  1010. break;
  1011. }
  1012. QSqlQuery query(createResult());
  1013. query.exec(stmt.arg(tbl));
  1014. if (d->pro >= QPSQLDriver::Version71) {
  1015. while (query.next()) {
  1016. int len = query.value(3).toInt();
  1017. int precision = query.value(4).toInt();
  1018. // swap length and precision if length == -1
  1019. if (len == -1 && precision > -1) {
  1020. len = precision - 4;
  1021. precision = -1;
  1022. }
  1023. QString defVal = query.value(5).toString();
  1024. if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\''))
  1025. defVal = defVal.mid(1, defVal.length() - 2);
  1026. QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt()));
  1027. f.setRequired(query.value(2).toBool());
  1028. f.setLength(len);
  1029. f.setPrecision(precision);
  1030. f.setDefaultValue(defVal);
  1031. f.setSqlType(query.value(1).toInt());
  1032. info.append(f);
  1033. }
  1034. } else {
  1035. // Postgres < 7.1 cannot handle outer joins
  1036. while (query.next()) {
  1037. QString defVal;
  1038. QString stmt2 = QLatin1String("select pg_attrdef.adsrc from pg_attrdef where "
  1039. "pg_attrdef.adrelid = %1 and pg_attrdef.adnum = %2 ");
  1040. QSqlQuery query2(createResult());
  1041. query2.exec(stmt2.arg(query.value(5).toInt()).arg(query.value(6).toInt()));
  1042. if (query2.isActive() && query2.next())
  1043. defVal = query2.value(0).toString();
  1044. if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\''))
  1045. defVal = defVal.mid(1, defVal.length() - 2);
  1046. int len = query.value(3).toInt();
  1047. int precision = query.value(4).toInt();
  1048. // swap length and precision if length == -1
  1049. if (len == -1 && precision > -1) {
  1050. len = precision - 4;
  1051. precision = -1;
  1052. }
  1053. QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt()));
  1054. f.setRequired(query.value(2).toBool());
  1055. f.setLength(len);
  1056. f.setPrecision(precision);
  1057. f.setDefaultValue(defVal);
  1058. f.setSqlType(query.value(1).toInt());
  1059. info.append(f);
  1060. }
  1061. }
  1062. return info;
  1063. }
  1064. QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
  1065. {
  1066. QString r;
  1067. if (field.isNull()) {
  1068. r = QLatin1String("NULL");
  1069. } else {
  1070. switch (field.type()) {
  1071. case QVariant::DateTime:
  1072. #ifndef QT_NO_DATESTRING
  1073. if (field.value().toDateTime().isValid()) {
  1074. QDate dt = field.value().toDateTime().date();
  1075. QTime tm = field.value().toDateTime().time();
  1076. // msecs need to be right aligned otherwise psql
  1077. // interpretes them wrong
  1078. r = QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-')
  1079. + QString::number(dt.month()) + QLatin1Char('-')
  1080. + QString::number(dt.day()) + QLatin1Char(' ')
  1081. + tm.toString() + QLatin1Char('.')
  1082. + QString::number(tm.msec()).rightJustified(3, QLatin1Char('0'))
  1083. + QLatin1Char('\'');
  1084. } else {
  1085. r = QLatin1String("NULL");
  1086. }
  1087. #else
  1088. r = QLatin1String("NULL");
  1089. #endif // QT_NO_DATESTRING
  1090. break;
  1091. case QVariant::Time:
  1092. #ifndef QT_NO_DATESTRING
  1093. if (field.value().toTime().isValid()) {
  1094. r = QLatin1Char('\'') + field.value().toTime().toString(QLatin1String("hh:mm:ss.zzz")) + QLatin1Char('\'');
  1095. } else
  1096. #endif
  1097. {
  1098. r = QLatin1String("NULL");
  1099. }
  1100. break;
  1101. case QVariant::String:
  1102. {
  1103. // Escape '\' characters
  1104. r = QSqlDriver::formatValue(field, trimStrings);
  1105. r.replace(QLatin1String("\\"), QLatin1String("\\\\"));
  1106. break;
  1107. }
  1108. case QVariant::Bool:
  1109. if (field.value().toBool())
  1110. r = QLatin1String("TRUE");
  1111. else
  1112. r = QLatin1String("FALSE");
  1113. break;
  1114. case QVariant::ByteArray: {
  1115. QByteArray ba(field.value().toByteArray());
  1116. size_t len;
  1117. #if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 80200
  1118. unsigned char *data = PQescapeByteaConn(d->connection, (unsigned char*)ba.constData(), ba.size(), &len);
  1119. #else
  1120. unsigned char *data = PQescapeBytea((unsigned char*)ba.constData(), ba.size(), &len);
  1121. #endif
  1122. r += QLatin1Char('\'');
  1123. r += QLatin1String((const char*)data);
  1124. r += QLatin1Char('\'');
  1125. qPQfreemem(data);
  1126. break;
  1127. }
  1128. case QVariant::Double: {
  1129. double val = field.value().toDouble();
  1130. if (isnan(val))
  1131. r = QLatin1String("'NaN'");
  1132. else {
  1133. int res = isinf(val);
  1134. if (res == 1)
  1135. r = QLatin1String("'Infinity'");
  1136. else if (res == -1)
  1137. r = QLatin1String("'-Infinity'");
  1138. else
  1139. r = QSqlDriver::formatValue(field, trimStrings);
  1140. }
  1141. break;
  1142. }
  1143. default:
  1144. r = QSqlDriver::formatValue(field, trimStrings);
  1145. break;
  1146. }
  1147. }
  1148. return r;
  1149. }
  1150. QString QPSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
  1151. {
  1152. QString res = identifier;
  1153. if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
  1154. res.replace(QLatin1Char('"'), QLatin1String("\"\""));
  1155. res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
  1156. res.replace(QLatin1Char('.'), QLatin1String("\".\""));
  1157. }
  1158. return res;
  1159. }
  1160. bool QPSQLDriver::isOpen() const
  1161. {
  1162. return PQstatus(d->connection) == CONNECTION_OK;
  1163. }
  1164. QPSQLDriver::Protocol QPSQLDriver::protocol() const
  1165. {
  1166. return d->pro;
  1167. }
  1168. bool QPSQLDriver::subscribeToNotificationImplementation(const QString &name)
  1169. {
  1170. if (!isOpen()) {
  1171. qWarning("QPSQLDriver::subscribeToNotificationImplementation: database not open.");
  1172. return false;
  1173. }
  1174. if (d->seid.contains(name)) {
  1175. qWarning("QPSQLDriver::subscribeToNotificationImplementation: already subscribing to '%s'.",
  1176. qPrintable(name));
  1177. return false;
  1178. }
  1179. int socket = PQsocket(d->connection);
  1180. if (socket) {
  1181. QString query = QLatin1String("LISTEN ") + escapeIdentifier(name, QSqlDriver::TableName);
  1182. if (PQresultStatus(PQexec(d->connection,
  1183. d->isUtf8 ? query.toUtf8().constData()
  1184. : query.toLocal8Bit().constData())
  1185. ) != PGRES_COMMAND_OK) {
  1186. setLastError(qMakeError(tr("Unable to subscribe"), QSqlError::StatementError, d));
  1187. return false;
  1188. }
  1189. if (!d->sn) {
  1190. d->sn = new QSocketNotifier(socket, QSocketNotifier::Read);
  1191. connect(d->sn, SIGNAL(activated(int)), this, SLOT(_q_handleNotification(int)));
  1192. }
  1193. }
  1194. d->seid << name;
  1195. return true;
  1196. }
  1197. bool QPSQLDriver::unsubscribeFromNotificationImplementation(const QString &name)
  1198. {
  1199. if (!isOpen()) {
  1200. qWarning("QPSQLDriver::unsubscribeFromNotificationImplementation: database not open.");
  1201. return false;
  1202. }
  1203. if (!d->seid.contains(name)) {
  1204. qWarning("QPSQLDriver::unsubscribeFromNotificationImplementation: not subscribed to '%s'.",
  1205. qPrintable(name));
  1206. return false;
  1207. }
  1208. QString query = QLatin1String("UNLISTEN ") + escapeIdentifier(name, QSqlDriver::TableName);
  1209. if (PQresultStatus(PQexec(d->connection,
  1210. d->isUtf8 ? query.toUtf8().constData()
  1211. : query.toLocal8Bit().constData())
  1212. ) != PGRES_COMMAND_OK) {
  1213. setLastError(qMakeError(tr("Unable to unsubscribe"), QSqlError::StatementError, d));
  1214. return false;
  1215. }
  1216. d->seid.removeAll(name);
  1217. if (d->seid.isEmpty()) {
  1218. disconnect(d->sn, SIGNAL(activated(int)), this, SLOT(_q_handleNotification(int)));
  1219. delete d->sn;
  1220. d->sn = 0;
  1221. }
  1222. return true;
  1223. }
  1224. QStringList QPSQLDriver::subscribedToNotificationsImplementation() const
  1225. {
  1226. return d->seid;
  1227. }
  1228. void QPSQLDriver::_q_handleNotification(int)
  1229. {
  1230. PQconsumeInput(d->connection);
  1231. PGnotify *notify = 0;
  1232. while((notify = PQnotifies(d->connection)) != 0) {
  1233. QString name(QLatin1String(notify->relname));
  1234. if (d->seid.contains(name))
  1235. emit notification(name);
  1236. else
  1237. qWarning("QPSQLDriver: received notification for '%s' which isn't subscribed to.",
  1238. qPrintable(name));
  1239. qPQfreemem(notify);
  1240. }
  1241. }
  1242. QT_END_NAMESPACE