📄 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 <qsqlrecord.h>#include <qregexp.h>#include <qdatetime.h>#include <qpointarray.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 errno#include <math.h>extern#if defined (QT_PLUGIN)Q_EXPORT#endifQPtrDict<QSqlDriverExtension> *qt_driver_extension_dict;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;}QSqlError qMakeError( const QString& err, int type, const QPSQLPrivate* p ){ return QSqlError("QPSQL: " + err, QString(PQerrorMessage( p->connection )), type);}QVariant::Type qDecodePSQLType( int t ){ QVariant::Type type = QVariant::Invalid; switch ( t ) { case BOOLOID : type = QVariant::Bool; break; case INT8OID : 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 POINTOID : type = QVariant::Point; break; case BOXOID : type = QVariant::Rect; break; case POLYGONOID : case LINEOID : case LSEGOID : case PATHOID : type = QVariant::PointArray; break; // case ZPBITOID : // 7.x // case VARBITOID : // 7.x case OIDOID : 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 BYTEAOID : 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 conversionsQPoint 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 );}/* // ### this should be obsolete?QTime qTimeFromDouble( double tm ){ int hour = ((int)tm / ( 60 * 60 ) ); int min = (((int) (tm / 60)) % 60 ); int sec = (((int) tm) % 60 ); return QTime( hour, min, sec );}*/QVariant QPSQLResult::data( int i ){ int ptype = PQftype( d->result, i ); QVariant::Type type = qDecodePSQLType( ptype ); QString val; if ( d->isUtf8 ) { val = QString::fromUtf8( PQgetvalue( d->result, at(), i ) ); } else { val = QString::fromLocal8Bit( PQgetvalue( d->result, at(), i ) ); } switch ( type ) { case QVariant::Bool: { QVariant b ( (bool)(val == "t"), 0 ); return ( b ); } case QVariant::String: if ( PQgetisnull( d->result, at(), i ) ) { return QVariant( QString() ); } else { return QVariant( val ); } case QVariant::Int: if ( ptype == INT8OID ) // keep these as strings so we don't lose precision return QVariant( val ); 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() ); // strip the timezone if ( val.at( val.length() - 3 ) == '+' ) val.truncate( val.length() - 3 ); return QVariant( QTime::fromString( val, Qt::ISODate ) ); case QVariant::DateTime: if ( val.length() < 10 ) return QVariant( QDateTime() ); // remove the timezone if ( val.at( val.length() - 3 ) == '+' ) val.truncate( val.length() - 3 ); // milliseconds are sometimes returned with 2 digits only if ( val.at( val.length() - 3 ).isPunct() ) val += '0'; if ( val.isEmpty() ) return QVariant( QDateTime() ); else return QVariant( QDateTime::fromString( val, 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: { 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 ) { setSelect( (status == PGRES_TUPLES_OK) ); currentSize = PQntuples( d->result ); setActive( TRUE ); return TRUE; } setLastError( qMakeError( "Unable to create query", QSqlError::Statement, d ) ); return FALSE;}int QPSQLResult::size(){ return currentSize;}int QPSQLResult::numRowsAffected(){ return isSelect() ? size() : QString( PQcmdTuples( d->result ) ).toInt();}///////////////////////////////////////////////////////////////////QPSQLDriver::QPSQLDriver( QObject * parent, const char * name ) : QSqlDriver(parent,name ? name : "QPSQL"), pro( QPSQLDriver::Version6 ){ init();}void QPSQLDriver::init(){ if ( !qt_driver_extension_dict ) qt_driver_extension_dict = new QPtrDict<QSqlDriverExtension>; qt_driver_extension_dict->insert( this, new QPSQLDriverExtension(this) ); d = new QPSQLPrivate();}QPSQLDriver::~QPSQLDriver(){ if ( d->connection ) PQfinish( d->connection ); delete d; if ( qt_driver_extension_dict ) { if ( !qt_driver_extension_dict->isEmpty() ) { QSqlDriverExtension *ext = qt_driver_extension_dict->take( this ); delete ext; } if ( qt_driver_extension_dict->isEmpty() ) { delete qt_driver_extension_dict; qt_driver_extension_dict = 0; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -