📄 qsql_psql.cpp
字号:
/******************************************************************************** Implementation of PostgreSQL driver classes**** Created : 001103**** Copyright (C) 1992-2002 Trolltech AS. All rights reserved.**** This file is part of the sql module of the Qt GUI Toolkit.**** This file may be distributed under the terms of the Q Public License** as defined by Trolltech AS of Norway and appearing in the file** LICENSE.QPL included in the packaging of this file.**** This file may be distributed and/or modified under the terms of the** GNU General Public License version 2 as published by the Free Software** Foundation and appearing in the file LICENSE.GPL included in the** packaging of this file.**** Licensees holding valid Qt Enterprise Edition licenses may use this** file in accordance with the Qt Commercial License Agreement provided** with the Software.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.**** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for** information about Qt Commercial License Agreements.** See http://www.trolltech.com/qpl/ for QPL licensing information.** See http://www.trolltech.com/gpl/ for GPL licensing information.**** Contact info@trolltech.com if any conditions of this licensing are** not clear to you.************************************************************************/#include "qsql_psql.h"#include <private/qsqlextension_p.h>#include <math.h>#include <qpointarray.h>#include <qsqlrecord.h>#include <qregexp.h>#include <qdatetime.h>// PostgreSQL header <utils/elog.h> included by <postgres.h> redefines DEBUG.#if defined(DEBUG)# undef DEBUG#endif#include <postgres.h>#include <libpq/libpq-fs.h>// PostgreSQL header <catalog/pg_type.h> redefines errno erroneously.#if defined(errno)# undef errno#endif#define errno qt_psql_errno#include <catalog/pg_type.h>#undef errnoQPtrDict<QSqlDriverExtension> *qSqlDriverExtDict();QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict();class QPSQLPrivate{public: QPSQLPrivate():connection(0), result(0), isUtf8(FALSE) {} PGconn *connection; PGresult *result; bool isUtf8;};class QPSQLDriverExtension : public QSqlDriverExtension{public: QPSQLDriverExtension( QPSQLDriver *dri ) : QSqlDriverExtension(), driver(dri) { } ~QPSQLDriverExtension() {} bool isOpen() const;private: QPSQLDriver *driver;};bool QPSQLDriverExtension::isOpen() const{ return PQstatus( driver->connection() ) == CONNECTION_OK;}class QPSQLOpenExtension : public QSqlOpenExtension{public: QPSQLOpenExtension( QPSQLDriver *dri ) : QSqlOpenExtension(), driver(dri) { } ~QPSQLOpenExtension() {} bool open( const QString& db, const QString& user, const QString& password, const QString& host, int port, const QString& connOpts );private: QPSQLDriver *driver;};bool QPSQLOpenExtension::open( const QString& db, const QString& user, const QString& password, const QString& host, int port, const QString& connOpts ){ return driver->open( db, user, password, host, port, connOpts );}static QSqlError qMakeError( const QString& err, int type, const QPSQLPrivate* p ){ return QSqlError("QPSQL: " + err, QString(PQerrorMessage( p->connection )), type);}static QVariant::Type qDecodePSQLType( int t ){ QVariant::Type type = QVariant::Invalid; switch ( t ) { case BOOLOID : type = QVariant::Bool; break; case INT8OID : type = QVariant::LongLong; break; case INT2OID : // case INT2VECTOROID : // 7.x case INT4OID : type = QVariant::Int; break; case NUMERICOID : case FLOAT4OID : case FLOAT8OID : type = QVariant::Double; break; case ABSTIMEOID : case RELTIMEOID : case DATEOID : type = QVariant::Date; break; case TIMEOID :#ifdef TIMETZOID // 7.x case TIMETZOID :#endif type = QVariant::Time; break; case TIMESTAMPOID :#ifdef DATETIMEOID // Postgres 6.x datetime workaround. // DATETIMEOID == TIMESTAMPOID (only the names have changed) case DATETIMEOID :#endif#ifdef TIMESTAMPTZOID // Postgres 7.2 workaround // TIMESTAMPTZOID == TIMESTAMPOID == DATETIMEOID case TIMESTAMPTZOID :#endif type = QVariant::DateTime; break; // case ZPBITOID : // 7.x // case VARBITOID : // 7.x case OIDOID : case BYTEAOID : type = QVariant::ByteArray; break; case REGPROCOID : case TIDOID : case XIDOID : case CIDOID : // case OIDVECTOROID : // 7.x case UNKNOWNOID : // case TINTERVALOID : // 7.x type = QVariant::Invalid; break; default: case CHAROID : case BPCHAROID : // case LZTEXTOID : // 7.x case VARCHAROID : case TEXTOID : case NAMEOID : case CASHOID : case INETOID : case CIDROID : case CIRCLEOID : type = QVariant::String; break; } return type;}QVariant::Type qFieldType( QPSQLPrivate* p, int i ){ QVariant::Type type = qDecodePSQLType( PQftype( p->result, i ) ); return type;}QPSQLResult::QPSQLResult( const QPSQLDriver* db, const QPSQLPrivate* p ): QSqlResult( db ), currentSize( 0 ){ d = new QPSQLPrivate(); (*d) = (*p);}QPSQLResult::~QPSQLResult(){ cleanup(); delete d;}PGresult* QPSQLResult::result(){ return d->result;}void QPSQLResult::cleanup(){ if ( d->result ) PQclear( d->result ); d->result = 0; setAt( -1 ); currentSize = 0; setActive( FALSE );}bool QPSQLResult::fetch( int i ){ if ( !isActive() ) return FALSE; if ( i < 0 ) return FALSE; if ( i >= 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 );}// some Postgres conversionsstatic QPoint pointFromString( const QString& s){ // format '(x,y)' int pivot = s.find( ',' ); if ( pivot != -1 ) { int x = s.mid( 1, pivot-1 ).toInt(); int y = s.mid( pivot+1, s.length()-pivot-2 ).toInt(); return QPoint( x, y ) ; } else return QPoint();}QDate qDateFromUInt( uint dt ){ int y,m,d; QDate::julianToGregorian( dt, y, m, d ); return QDate( y, m, d );}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 QString val = ( d->isUtf8 && ptype != BYTEAOID ) ? QString::fromUtf8( PQgetvalue( d->result, at(), i ) ) : QString::fromLocal8Bit( PQgetvalue( d->result, at(), i ) ); if ( PQgetisnull( d->result, at(), i ) ) { QVariant v; v.cast( type ); return v; } switch ( type ) { case QVariant::Bool: { QVariant b ( (bool)(val == "t"), 0 ); return ( b ); } case QVariant::String: return QVariant( val ); case QVariant::LongLong: if ( val[0] == '-' ) return QVariant( val.toLongLong() ); else return QVariant( val.toULongLong() ); case QVariant::Int: return QVariant( val.toInt() ); case QVariant::Double: if ( ptype == NUMERICOID ) return QVariant( val ); return QVariant( val.toDouble() ); case QVariant::Date: if ( val.isEmpty() ) { return QVariant( QDate() ); } else { return QVariant( QDate::fromString( val, Qt::ISODate ) ); } case QVariant::Time: if ( val.isEmpty() ) return QVariant( QTime() ); if ( val.at( val.length() - 3 ) == '+' ) // strip the timezone return QVariant( QTime::fromString( val.left( val.length() - 3 ), Qt::ISODate ) ); return QVariant( QTime::fromString( val, Qt::ISODate ) ); case QVariant::DateTime: { if ( val.length() < 10 ) return QVariant( QDateTime() ); // remove the timezone QString dtval = val; if ( dtval.at( dtval.length() - 3 ) == '+' ) dtval.truncate( dtval.length() - 3 ); // milliseconds are sometimes returned with 2 digits only if ( dtval.at( dtval.length() - 3 ).isPunct() ) dtval += '0'; if ( dtval.isEmpty() ) return QVariant( QDateTime() ); else return QVariant( QDateTime::fromString( dtval, Qt::ISODate ) ); } case QVariant::Point: return QVariant( pointFromString( val ) ); case QVariant::Rect: // format '(x,y),(x',y')' { int pivot = val.find( "),(" ); if ( pivot != -1 ) return QVariant( QRect( pointFromString( val.mid(pivot+2,val.length()) ), pointFromString( val.mid(0,pivot+1) ) ) ); return QVariant( QRect() ); } case QVariant::PointArray: // format '((x,y),(x1,y1),...,(xn,yn))' { QRegExp pointPattern("\\([0-9-]*,[0-9-]*\\)"); int points = val.contains( pointPattern ); QPointArray parray( points ); int idx = 1; for ( int i = 0; i < points; i++ ){ int start = val.find( pointPattern, idx ); int end = -1; if ( start != -1 ) { end = val.find( ')', start+1 ); if ( end != -1 ) { parray.setPoint( i, pointFromString( val.mid(idx, end-idx+1) ) ); } else parray.setPoint( i, QPoint() ); } else { parray.setPoint( i, QPoint() ); break; } idx = end+2; } return QVariant( parray ); } case QVariant::ByteArray: { if ( ptype == BYTEAOID ) { uint i = 0; int index = 0; uint len = val.length(); static const QChar backslash( '\\' ); QByteArray ba( (int)len ); while ( i < len ) { if ( val.at( i ) == backslash ) { if ( val.at( i + 1 ).isDigit() ) { ba[ index++ ] = (char)(val.mid( i + 1, 3 ).toInt( 0, 8 )); i += 4; } else { ba[ index++ ] = val.at( i + 1 ); i += 2; } } else { ba[ index++ ] = val.at( i++ ).unicode(); } } ba.resize( index ); return QVariant( ba ); } QByteArray ba; ((QSqlDriver*)driver())->beginTransaction(); Oid oid = val.toInt(); int fd = lo_open( d->connection, oid, INV_READ );#ifdef QT_CHECK_RANGE if ( fd < 0) { qWarning( "QPSQLResult::data: unable to open large object for read" ); ((QSqlDriver*)driver())->commitTransaction(); return QVariant( ba ); }#endif int size = 0; int retval = lo_lseek( d->connection, fd, 0L, SEEK_END ); if ( retval >= 0 ) { size = lo_tell( d->connection, fd ); lo_lseek( d->connection, fd, 0L, SEEK_SET ); } if ( size == 0 ) { lo_close( d->connection, fd ); ((QSqlDriver*)driver())->commitTransaction(); return QVariant( ba ); } char * buf = new char[ size ];#ifdef Q_OS_WIN32 // ### For some reason lo_read() fails if we try to read more than // ### 32760 bytes char * p = buf; int nread = 0; while( size < nread ){ retval = lo_read( d->connection, fd, p, 32760 ); nread += retval; p += retval; }#else retval = lo_read( d->connection, fd, buf, size );#endif if (retval < 0) { qWarning( "QPSQLResult::data: unable to read large object" ); } else { ba.duplicate( buf, size ); } delete [] buf; lo_close( d->connection, fd ); ((QSqlDriver*)driver())->commitTransaction(); return QVariant( ba ); } default: case QVariant::Invalid:#ifdef QT_CHECK_RANGE qWarning("QPSQLResult::data: unknown data type");#endif ; } 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; setActive( FALSE ); setAt( QSql::BeforeFirst ); if ( d->result ) PQclear( d->result ); if ( d->isUtf8 ) { d->result = PQexec( d->connection, query.utf8().data() ); } else { d->result = PQexec( d->connection, query.local8Bit().data() ); } int status = PQresultStatus( d->result ); if ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK ) { if ( status == PGRES_TUPLES_OK ) { setSelect( TRUE ); currentSize = PQntuples( d->result ); } else { setSelect( FALSE ); currentSize = -1; } setActive( TRUE ); return TRUE; } setLastError( qMakeError( "Unable to create query", QSqlError::Statement, d ) ); return FALSE;}int QPSQLResult::size(){ return currentSize;}int QPSQLResult::numRowsAffected(){ return QString( PQcmdTuples( d->result ) ).toInt();}///////////////////////////////////////////////////////////////////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'" );#ifdef QT_CHECK_RANGE int status = PQresultStatus( result ); if ( status != PGRES_COMMAND_OK ) qWarning( "%s", PQerrorMessage( connection ) );#endif 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( PQgetvalue( result, 0, 0 ) ); PQclear( result ); QRegExp rx( "(\\d+)\\.(\\d+)" ); rx.setMinimal ( TRUE ); // enforce non-greedy RegExp if ( rx.search( val ) != -1 ) { int vMaj = rx.cap( 1 ).toInt(); int vMin = rx.cap( 2 ).toInt(); if ( vMaj < 6 ) {#ifdef QT_CHECK_RANGE qWarning( "This version of PostgreSQL is not supported and may not work." );#endif 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 {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -