?? qsql_psql.cpp
字號:
/******************************************************************************** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.**** This file is part of the QtSql module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "qsql_psql.h"#include <qcoreapplication.h>#include <qvariant.h>#include <qdatetime.h>#include <qregexp.h>#include <qsqlerror.h>#include <qsqlfield.h>#include <qsqlindex.h>#include <qsqlrecord.h>#include <qsqlquery.h>#include <qstringlist.h>#include <libpq-fe.h>#include <stdlib.h>#include <math.h>// workaround for postgres defining their OIDs in a private header file#define QBOOLOID 16#define QINT8OID 20#define QINT2OID 21#define QINT4OID 23#define QNUMERICOID 1700#define QFLOAT4OID 700#define QFLOAT8OID 701#define QABSTIMEOID 702#define QRELTIMEOID 703#define QDATEOID 1082#define QTIMEOID 1083#define QTIMETZOID 1266#define QTIMESTAMPOID 1114#define QTIMESTAMPTZOID 1184#define QOIDOID 2278#define QBYTEAOID 17#define QREGPROCOID 24#define QXIDOID 28#define QCIDOID 29Q_DECLARE_METATYPE(PGconn*)Q_DECLARE_METATYPE(PGresult*)/* This is a compile time switch - if PQfreemem is declared, the compiler will use that one, otherwise it'll run in this template */template <typename T>inline void PQfreemem(T *t, int = 0) { free(t); }inline void qPQfreemem(void *buffer){ PQfreemem(buffer);}class QPSQLDriverPrivate{public: QPSQLDriverPrivate(): connection(0), isUtf8(false), pro(QPSQLDriver::Version6) {} PGconn *connection; bool isUtf8; QPSQLDriver::Protocol pro; void appendTables(QStringList &tl, QSqlQuery &t, QChar type);};void QPSQLDriverPrivate::appendTables(QStringList &tl, QSqlQuery &t, QChar type){ QString query; if (pro >= QPSQLDriver::Version73) { 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 = 'r') " "and (relname !~ '^Inv') " "and (relname !~ '^pg_') "); } t.exec(query); while (t.next()) { QString schema = t.value(1).toString(); if (schema.isEmpty() || schema == QLatin1String("public")) tl.append(t.value(0).toString()); else tl.append(t.value(0).toString().prepend(QLatin1Char('.')).prepend(schema)); }}class QPSQLResultPrivate{public: QPSQLResultPrivate(QPSQLResult *qq): q(qq), driver(0), result(0), currentSize(-1) {} QPSQLResult *q; const QPSQLDriverPrivate *driver; PGresult *result; int currentSize; bool processResults();};static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, const QPSQLDriverPrivate *p){ const char *s = PQerrorMessage(p->connection); QString msg = p->isUtf8 ? QString::fromUtf8(s) : QString::fromLocal8Bit(s); return QSqlError(QLatin1String("QPSQL: ") + err, msg, type);}bool QPSQLResultPrivate::processResults(){ if (!result) return false; int status = PQresultStatus(result); if (status == PGRES_TUPLES_OK) { q->setSelect(true); q->setActive(true); currentSize = PQntuples(result); return true; } else if (status == PGRES_COMMAND_OK) { q->setSelect(false); q->setActive(true); currentSize = -1; return true; } q->setLastError(qMakeError(QCoreApplication::translate("QPSQLResult", "Unable to create query"), QSqlError::StatementError, driver)); return false;}static QVariant::Type qDecodePSQLType(int t){ QVariant::Type type = QVariant::Invalid; switch (t) { case QBOOLOID: type = QVariant::Bool; break; case QINT8OID: type = QVariant::LongLong; break; case QINT2OID: case QINT4OID: case QOIDOID: case QREGPROCOID: case QXIDOID: case QCIDOID: type = QVariant::Int; break; case QNUMERICOID: case QFLOAT4OID: case QFLOAT8OID: type = QVariant::Double; break; case QABSTIMEOID: case QRELTIMEOID: case QDATEOID: type = QVariant::Date; break; case QTIMEOID: case QTIMETZOID: type = QVariant::Time; break; case QTIMESTAMPOID: case QTIMESTAMPTZOID: type = QVariant::DateTime; break; case QBYTEAOID: type = QVariant::ByteArray; break; default: type = QVariant::String; break; } return type;}QPSQLResult::QPSQLResult(const QPSQLDriver* db, const QPSQLDriverPrivate* p) : QSqlResult(db){ d = new QPSQLResultPrivate(this); d->driver = p;}QPSQLResult::~QPSQLResult(){ cleanup(); delete d;}QVariant QPSQLResult::handle() const{ return qVariantFromValue(d->result);}void QPSQLResult::cleanup(){ if (d->result) PQclear(d->result); d->result = 0; setAt(QSql::BeforeFirstRow); d->currentSize = -1; setActive(false);}bool QPSQLResult::fetch(int i){ if (!isActive()) return false; if (i < 0) return false; if (i >= d->currentSize) return false; if (at() == i) return true; setAt(i); return true;}bool QPSQLResult::fetchFirst(){ return fetch(0);}bool QPSQLResult::fetchLast(){ return fetch(PQntuples(d->result) - 1);}QVariant QPSQLResult::data(int i){ if (i >= PQnfields(d->result)) { qWarning("QPSQLResult::data: column %d out of range", i); return QVariant(); } 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)) return QVariant(type); switch (type) { case QVariant::Bool: return QVariant((bool)(val[0] == 't')); case QVariant::String: return d->driver->isUtf8 ? QString::fromUtf8(val) : QString::fromAscii(val); case QVariant::LongLong: if (val[0] == '-') return QString::fromLatin1(val).toLongLong(); else return QString::fromLatin1(val).toULongLong(); case QVariant::Int: return atoi(val); case QVariant::Double: if (ptype == QNUMERICOID) return QString::fromAscii(val); return strtod(val, 0); case QVariant::Date: if (val[0] == '\0') { return QVariant(QDate()); } else {#ifndef QT_NO_DATESTRING return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate));#else return QVariant(QString::fromLatin1(val));#endif } case QVariant::Time: { const QString str = QString::fromLatin1(val);#ifndef QT_NO_DATESTRING if (str.isEmpty()) return QVariant(QTime()); if (str.at(str.length() - 3) == QLatin1Char('+')) // strip the timezone return QVariant(QTime::fromString(str.left(str.length() - 3), Qt::ISODate)); return QVariant(QTime::fromString(str, Qt::ISODate));#else return QVariant(str);#endif } case QVariant::DateTime: { QString dtval = QString::fromLatin1(val);#ifndef QT_NO_DATESTRING if (dtval.length() < 10) return QVariant(QDateTime()); // remove the timezone if (dtval.at(dtval.length() - 3) == QLatin1Char('+')) dtval.chop(3); // milliseconds are sometimes returned with 2 digits only if (dtval.at(dtval.length() - 3).isPunct()) dtval += QLatin1Char('0'); if (dtval.isEmpty()) return QVariant(QDateTime()); else return QVariant(QDateTime::fromString(dtval, Qt::ISODate));#else return QVariant(dtval);#endif } case QVariant::ByteArray: { size_t len; unsigned char *data = PQunescapeBytea((unsigned char*)val, &len); QByteArray ba((const char*)data, len); qPQfreemem(data); return QVariant(ba); } default: case QVariant::Invalid: qWarning("QPSQLResult::data: unknown data type"); } return QVariant();}bool QPSQLResult::isNull(int field){ PQgetvalue(d->result, at(), field); return PQgetisnull(d->result, at(), field);}bool QPSQLResult::reset (const QString& query){ cleanup(); if (!driver()) return false; if (!driver()->isOpen() || driver()->isOpenError()) return false; d->result = PQexec(d->driver->connection, d->driver->isUtf8 ? query.toUtf8().constData() : query.toLocal8Bit().constData()); return d->processResults();}int QPSQLResult::size(){ return d->currentSize;}int QPSQLResult::numRowsAffected(){ return QString::fromLatin1(PQcmdTuples(d->result)).toInt();}QVariant QPSQLResult::lastInsertId() const{ if (isActive()) { Oid id = PQoidValue(d->result); if (id != InvalidOid) return QVariant(id); } return QVariant();}QSqlRecord QPSQLResult::record() const{ QSqlRecord info; if (!isActive() || !isSelect()) return info; int count = PQnfields(d->result); for (int i = 0; i < count; ++i) { QSqlField f; if (d->driver->isUtf8) f.setName(QString::fromUtf8(PQfname(d->result, i))); else f.setName(QString::fromLocal8Bit(PQfname(d->result, i))); f.setType(qDecodePSQLType(PQftype(d->result, i))); int len = PQfsize(d->result, i); int precision = PQfmod(d->result, i); // swap length and precision if length == -1 if (len == -1 && precision > -1) { len = precision - 4; precision = -1; } f.setLength(len); f.setPrecision(precision); f.setSqlType(PQftype(d->result, i)); info.append(f); } return info;}///////////////////////////////////////////////////////////////////static bool setEncodingUtf8(PGconn* connection){ PGresult* result = PQexec(connection, "SET CLIENT_ENCODING TO 'UNICODE'"); int status = PQresultStatus(result); PQclear(result); return status == PGRES_COMMAND_OK;}static void setDatestyle(PGconn* connection){ PGresult* result = PQexec(connection, "SET DATESTYLE TO 'ISO'"); int status = PQresultStatus(result); if (status != PGRES_COMMAND_OK) qWarning("%s", PQerrorMessage(connection)); PQclear(result);}static QPSQLDriver::Protocol getPSQLVersion(PGconn* connection){ PGresult* result = PQexec(connection, "select version()"); int status = PQresultStatus(result); if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) { QString val = QString::fromAscii(PQgetvalue(result, 0, 0)); PQclear(result); QRegExp rx(QLatin1String("(\\d+)\\.(\\d+)")); rx.setMinimal(true); // enforce non-greedy RegExp if (rx.indexIn(val) != -1) { int vMaj = rx.cap(1).toInt(); int vMin = rx.cap(2).toInt(); if (vMaj < 6) { qWarning("This version of PostgreSQL is not supported and may not work."); return QPSQLDriver::Version6; } if (vMaj == 6) { return QPSQLDriver::Version6; } else if (vMaj == 7) { if (vMin < 1) return QPSQLDriver::Version7; else if (vMin < 3) return QPSQLDriver::Version71; } return QPSQLDriver::Version73; } } else { qWarning("This version of PostgreSQL is not supported and may not work."); } return QPSQLDriver::Version6;}QPSQLDriver::QPSQLDriver(QObject *parent) : QSqlDriver(parent){ init();}QPSQLDriver::QPSQLDriver(PGconn * conn, QObject * parent) : QSqlDriver(parent){ init(); d->connection = conn;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -