2011-04-27 10:05:43 +00:00
/****************************************************************************
* *
2013-01-02 11:13:29 +00:00
* * Copyright ( C ) 2013 Digia Plc and / or its subsidiary ( - ies ) .
2012-09-19 12:28:29 +00:00
* * Contact : http : //www.qt-project.org/legal
2011-04-27 10:05:43 +00:00
* *
* * This file is part of the QtSql module of the Qt Toolkit .
* *
* * $ QT_BEGIN_LICENSE : LGPL $
2012-09-19 12:28:29 +00:00
* * Commercial License Usage
* * Licensees holding valid commercial Qt licenses may use this file in
* * accordance with the commercial license agreement provided with the
* * Software or , alternatively , in accordance with the terms contained in
* * a written agreement between you and Digia . For licensing terms and
* * conditions see http : //qt.digia.com/licensing. For further information
* * use the contact form at http : //qt.digia.com/contact-us.
* *
2011-04-27 10:05:43 +00:00
* * GNU Lesser General Public License Usage
2012-09-19 12:28:29 +00:00
* * Alternatively , this file may be used under the terms of the GNU Lesser
* * General Public License version 2.1 as published by the Free Software
* * Foundation and appearing in the file LICENSE . LGPL included in the
* * packaging of this file . Please review the following information to
* * ensure the GNU Lesser General Public License version 2.1 requirements
* * will be met : http : //www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
* *
* * In addition , as a special exception , Digia gives you certain additional
* * rights . These rights are described in the Digia Qt LGPL Exception
2011-04-27 10:05:43 +00:00
* * version 1.1 , included in the file LGPL_EXCEPTION . txt in this package .
* *
2011-05-24 09:34:08 +00:00
* * GNU General Public License Usage
2012-09-19 12:28:29 +00:00
* * Alternatively , this file may be used under the terms of the GNU
* * General Public License version 3.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 the GNU General Public License version 3.0 requirements will be
* * met : http : //www.gnu.org/copyleft/gpl.html.
2011-04-27 10:05:43 +00:00
* *
2012-01-24 06:17:24 +00:00
* *
2011-04-27 10:05:43 +00:00
* * $ QT_END_LICENSE $
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-02-22 00:54:24 +00:00
# include "qsql_psql_p.h"
2011-04-27 10:05:43 +00:00
# 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 <qsocketnotifier.h>
# include <qstringlist.h>
# include <qmutex.h>
2013-02-10 22:53:31 +00:00
# include <QtSql/private/qsqlresult_p.h>
2011-04-27 10:05:43 +00:00
# include <libpq-fe.h>
# include <pg_config.h>
# include <stdlib.h>
# include <math.h>
// below code taken from an example at http://www.gnu.org/software/hello/manual/autoconf/Function-Portability.html
# ifndef isnan
# define isnan(x) \
( sizeof ( x ) = = sizeof ( long double ) ? isnan_ld ( x ) \
: sizeof ( x ) = = sizeof ( double ) ? isnan_d ( x ) \
: isnan_f ( x ) )
static inline int isnan_f ( float x ) { return x ! = x ; }
static inline int isnan_d ( double x ) { return x ! = x ; }
static inline int isnan_ld ( long double x ) { return x ! = x ; }
# endif
# ifndef isinf
# define isinf(x) \
( sizeof ( x ) = = sizeof ( long double ) ? isinf_ld ( x ) \
: sizeof ( x ) = = sizeof ( double ) ? isinf_d ( x ) \
: isinf_f ( x ) )
static inline int isinf_f ( float x ) { return isnan ( x - x ) ; }
static inline int isinf_d ( double x ) { return isnan ( x - x ) ; }
static inline int isinf_ld ( long double x ) { return isnan ( x - x ) ; }
# endif
// 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 29
/* 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 ) ; }
2012-01-31 16:35:00 +00:00
Q_DECLARE_OPAQUE_POINTER ( PGconn * )
2011-04-27 10:05:43 +00:00
Q_DECLARE_METATYPE ( PGconn * )
2011-12-30 11:00:09 +00:00
2012-01-31 16:35:00 +00:00
Q_DECLARE_OPAQUE_POINTER ( PGresult * )
2011-04-27 10:05:43 +00:00
Q_DECLARE_METATYPE ( PGresult * )
QT_BEGIN_NAMESPACE
inline void qPQfreemem ( void * buffer )
{
PQfreemem ( buffer ) ;
}
class QPSQLDriverPrivate
{
public :
2013-03-07 10:52:52 +00:00
QPSQLDriverPrivate ( QPSQLDriver * qq )
: q ( qq ) ,
connection ( 0 ) ,
isUtf8 ( false ) ,
pro ( QPSQLDriver : : Version6 ) ,
sn ( 0 ) ,
pendingNotifyCheck ( false ) ,
hasBackslashEscape ( false )
{ }
2012-03-22 17:42:56 +00:00
QPSQLDriver * q ;
2011-04-27 10:05:43 +00:00
PGconn * connection ;
bool isUtf8 ;
QPSQLDriver : : Protocol pro ;
QSocketNotifier * sn ;
QStringList seid ;
2012-03-22 17:42:56 +00:00
mutable bool pendingNotifyCheck ;
2013-03-07 10:52:52 +00:00
bool hasBackslashEscape ;
2011-04-27 10:05:43 +00:00
void appendTables ( QStringList & tl , QSqlQuery & t , QChar type ) ;
2012-03-22 17:42:56 +00:00
PGresult * exec ( const char * stmt ) const ;
PGresult * exec ( const QString & stmt ) const ;
QPSQLDriver : : Protocol getPSQLVersion ( ) ;
bool setEncodingUtf8 ( ) ;
void setDatestyle ( ) ;
2013-03-07 10:52:52 +00:00
void detectBackslashEscape ( ) ;
2011-04-27 10:05:43 +00:00
} ;
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 = '%1') "
" and (relname !~ '^Inv') "
" and (relname !~ '^pg_') " ) . arg ( type ) ;
}
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 ) ) ;
}
}
2012-03-22 17:42:56 +00:00
PGresult * QPSQLDriverPrivate : : exec ( const char * stmt ) const
{
PGresult * result = PQexec ( connection , stmt ) ;
if ( seid . size ( ) & & ! pendingNotifyCheck ) {
pendingNotifyCheck = true ;
QMetaObject : : invokeMethod ( q , " _q_handleNotification " , Qt : : QueuedConnection , Q_ARG ( int , 0 ) ) ;
}
return result ;
}
PGresult * QPSQLDriverPrivate : : exec ( const QString & stmt ) const
{
return exec ( isUtf8 ? stmt . toUtf8 ( ) . constData ( ) : stmt . toLocal8Bit ( ) . constData ( ) ) ;
}
2011-04-27 10:05:43 +00:00
class QPSQLResultPrivate
{
public :
2013-02-26 22:58:20 +00:00
QPSQLResultPrivate ( QPSQLResult * qq ) : q ( qq ) , result ( 0 ) , currentSize ( - 1 ) , preparedQueriesEnabled ( false ) { }
2013-02-10 22:53:31 +00:00
static QString fieldSerial ( int i ) { return QLatin1Char ( ' $ ' ) + QString : : number ( i + 1 ) ; }
2013-02-10 21:20:24 +00:00
void deallocatePreparedStmt ( ) ;
2013-02-26 22:58:20 +00:00
const QPSQLDriverPrivate * privDriver ( ) const { return reinterpret_cast < const QPSQLDriver * > ( q - > driver ( ) ) - > d ; }
2011-04-27 10:05:43 +00:00
QPSQLResult * q ;
PGresult * result ;
int currentSize ;
bool preparedQueriesEnabled ;
QString preparedStmtId ;
bool processResults ( ) ;
} ;
static QSqlError qMakeError ( const QString & err , QSqlError : : ErrorType type ,
2012-07-01 14:11:00 +00:00
const QPSQLDriverPrivate * p , PGresult * result = 0 )
2011-04-27 10:05:43 +00:00
{
const char * s = PQerrorMessage ( p - > connection ) ;
QString msg = p - > isUtf8 ? QString : : fromUtf8 ( s ) : QString : : fromLocal8Bit ( s ) ;
2012-07-01 14:11:00 +00:00
if ( result ) {
const char * sCode = PQresultErrorField ( result , PG_DIAG_SQLSTATE ) ;
msg + = QString : : fromLatin1 ( " (%1) " ) . arg ( QString : : fromLatin1 ( sCode ) ) ;
}
2011-04-27 10:05:43 +00:00
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 " ,
2013-02-26 22:58:20 +00:00
" Unable to create query " ) , QSqlError : : StatementError , privDriver ( ) , result ) ) ;
2011-04-27 10:05:43 +00:00
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 ;
}
2013-02-10 21:20:24 +00:00
void QPSQLResultPrivate : : deallocatePreparedStmt ( )
2011-04-27 10:05:43 +00:00
{
2013-02-10 21:20:24 +00:00
const QString stmt = QLatin1String ( " DEALLOCATE " ) + preparedStmtId ;
2013-02-26 22:58:20 +00:00
PGresult * result = privDriver ( ) - > exec ( stmt ) ;
2011-04-27 10:05:43 +00:00
if ( PQresultStatus ( result ) ! = PGRES_COMMAND_OK )
2013-02-26 22:58:20 +00:00
qWarning ( " Unable to free statement: %s " , PQerrorMessage ( privDriver ( ) - > connection ) ) ;
2011-04-27 10:05:43 +00:00
PQclear ( result ) ;
2013-02-10 21:20:24 +00:00
preparedStmtId . clear ( ) ;
2011-04-27 10:05:43 +00:00
}
2013-02-26 22:58:20 +00:00
QPSQLResult : : QPSQLResult ( const QPSQLDriver * db )
2011-04-27 10:05:43 +00:00
: QSqlResult ( db )
{
d = new QPSQLResultPrivate ( this ) ;
d - > preparedQueriesEnabled = db - > hasFeature ( QSqlDriver : : PreparedQueries ) ;
}
QPSQLResult : : ~ QPSQLResult ( )
{
cleanup ( ) ;
if ( d - > preparedQueriesEnabled & & ! d - > preparedStmtId . isNull ( ) )
2013-02-10 21:20:24 +00:00
d - > deallocatePreparedStmt ( ) ;
2011-04-27 10:05:43 +00:00
delete d ;
}
QVariant QPSQLResult : : handle ( ) const
{
return QVariant : : fromValue ( 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 :
2013-02-26 22:58:20 +00:00
return d - > privDriver ( ) - > isUtf8 ? QString : : fromUtf8 ( val ) : QString : : fromLatin1 ( val ) ;
2011-04-27 10:05:43 +00:00
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 ) {
if ( numericalPrecisionPolicy ( ) ! = QSql : : HighPrecision ) {
QVariant retval ;
bool convert ;
2012-05-02 14:32:26 +00:00
double dbl = QString : : fromLatin1 ( val ) . toDouble ( & convert ) ;
2011-04-27 10:05:43 +00:00
if ( numericalPrecisionPolicy ( ) = = QSql : : LowPrecisionInt64 )
retval = ( qlonglong ) dbl ;
else if ( numericalPrecisionPolicy ( ) = = QSql : : LowPrecisionInt32 )
retval = ( int ) dbl ;
else if ( numericalPrecisionPolicy ( ) = = QSql : : LowPrecisionDouble )
retval = dbl ;
if ( ! convert )
return QVariant ( ) ;
return retval ;
}
2012-05-02 14:32:26 +00:00
return QString : : fromLatin1 ( val ) ;
2011-04-27 10:05:43 +00:00
}
2012-05-02 14:32:26 +00:00
return QString : : fromLatin1 ( val ) . toDouble ( ) ;
2011-04-27 10:05:43 +00:00
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 ( ' + ' ) | | str . at ( str . length ( ) - 3 ) = = QLatin1Char ( ' - ' ) )
// strip the timezone
// TODO: fix this when timestamp support comes into QDateTime
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
// TODO: fix this when timestamp support comes into QDateTime
if ( dtval . at ( dtval . length ( ) - 3 ) = = QLatin1Char ( ' + ' ) | | 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 ;
2013-02-26 22:58:20 +00:00
d - > result = d - > privDriver ( ) - > exec ( query ) ;
2011-04-27 10:05:43 +00:00
return d - > processResults ( ) ;
}
int QPSQLResult : : size ( )
{
return d - > currentSize ;
}
int QPSQLResult : : numRowsAffected ( )
{
return QString : : fromLatin1 ( PQcmdTuples ( d - > result ) ) . toInt ( ) ;
}
QVariant QPSQLResult : : lastInsertId ( ) const
{
2013-02-26 22:58:20 +00:00
if ( d - > privDriver ( ) - > pro > = QPSQLDriver : : Version81 ) {
2013-02-26 17:23:43 +00:00
QSqlQuery qry ( driver ( ) - > createResult ( ) ) ;
// Most recent sequence value obtained from nextval
if ( qry . exec ( QLatin1String ( " SELECT lastval(); " ) ) & & qry . next ( ) )
return qry . value ( 0 ) ;
} else if ( isActive ( ) ) {
2011-04-27 10:05:43 +00:00
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 ;
2013-02-26 22:58:20 +00:00
if ( d - > privDriver ( ) - > isUtf8 )
2011-04-27 10:05:43 +00:00
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 ;
}
void QPSQLResult : : virtual_hook ( int id , void * data )
{
Q_ASSERT ( data ) ;
2012-11-14 02:40:22 +00:00
QSqlResult : : virtual_hook ( id , data ) ;
2011-04-27 10:05:43 +00:00
}
static QString qCreateParamString ( const QVector < QVariant > boundValues , const QSqlDriver * driver )
{
if ( boundValues . isEmpty ( ) )
return QString ( ) ;
QString params ;
QSqlField f ;
for ( int i = 0 ; i < boundValues . count ( ) ; + + i ) {
const QVariant & val = boundValues . at ( i ) ;
f . setType ( val . type ( ) ) ;
if ( val . isNull ( ) )
f . clear ( ) ;
else
f . setValue ( val ) ;
if ( ! params . isNull ( ) )
params . append ( QLatin1String ( " , " ) ) ;
params . append ( driver - > formatValue ( f ) ) ;
}
return params ;
}
Q_GLOBAL_STATIC ( QMutex , qMutex )
QString qMakePreparedStmtId ( )
{
qMutex ( ) - > lock ( ) ;
static unsigned int qPreparedStmtCount = 0 ;
QString id = QLatin1String ( " qpsqlpstmt_ " ) + QString : : number ( + + qPreparedStmtCount , 16 ) ;
qMutex ( ) - > unlock ( ) ;
return id ;
}
bool QPSQLResult : : prepare ( const QString & query )
{
if ( ! d - > preparedQueriesEnabled )
return QSqlResult : : prepare ( query ) ;
cleanup ( ) ;
if ( ! d - > preparedStmtId . isEmpty ( ) )
2013-02-10 21:20:24 +00:00
d - > deallocatePreparedStmt ( ) ;
2011-04-27 10:05:43 +00:00
const QString stmtId = qMakePreparedStmtId ( ) ;
2013-02-10 22:53:31 +00:00
const QString stmt = QString : : fromLatin1 ( " PREPARE %1 AS " ) . arg ( stmtId ) . append ( QSqlResultPrivate : : positionalToNamedBinding ( query , QPSQLResultPrivate : : fieldSerial ) ) ;
2011-04-27 10:05:43 +00:00
2013-02-26 22:58:20 +00:00
PGresult * result = d - > privDriver ( ) - > exec ( stmt ) ;
2011-04-27 10:05:43 +00:00
if ( PQresultStatus ( result ) ! = PGRES_COMMAND_OK ) {
setLastError ( qMakeError ( QCoreApplication : : translate ( " QPSQLResult " ,
2013-02-26 22:58:20 +00:00
" Unable to prepare statement " ) , QSqlError : : StatementError , d - > privDriver ( ) , result ) ) ;
2011-04-27 10:05:43 +00:00
PQclear ( result ) ;
d - > preparedStmtId . clear ( ) ;
return false ;
}
PQclear ( result ) ;
d - > preparedStmtId = stmtId ;
return true ;
}
bool QPSQLResult : : exec ( )
{
if ( ! d - > preparedQueriesEnabled )
return QSqlResult : : exec ( ) ;
cleanup ( ) ;
QString stmt ;
2013-02-10 20:46:25 +00:00
const QString params = qCreateParamString ( boundValues ( ) , driver ( ) ) ;
2011-04-27 10:05:43 +00:00
if ( params . isEmpty ( ) )
stmt = QString : : fromLatin1 ( " EXECUTE %1 " ) . arg ( d - > preparedStmtId ) ;
else
stmt = QString : : fromLatin1 ( " EXECUTE %1 (%2) " ) . arg ( d - > preparedStmtId ) . arg ( params ) ;
2013-02-26 22:58:20 +00:00
d - > result = d - > privDriver ( ) - > exec ( stmt ) ;
2011-04-27 10:05:43 +00:00
return d - > processResults ( ) ;
}
///////////////////////////////////////////////////////////////////
2012-03-22 17:42:56 +00:00
bool QPSQLDriverPrivate : : setEncodingUtf8 ( )
2011-04-27 10:05:43 +00:00
{
2012-03-22 17:42:56 +00:00
PGresult * result = exec ( " SET CLIENT_ENCODING TO 'UNICODE' " ) ;
2011-04-27 10:05:43 +00:00
int status = PQresultStatus ( result ) ;
PQclear ( result ) ;
return status = = PGRES_COMMAND_OK ;
}
2012-03-22 17:42:56 +00:00
void QPSQLDriverPrivate : : setDatestyle ( )
2011-04-27 10:05:43 +00:00
{
2012-03-22 17:42:56 +00:00
PGresult * result = exec ( " SET DATESTYLE TO 'ISO' " ) ;
2011-04-27 10:05:43 +00:00
int status = PQresultStatus ( result ) ;
if ( status ! = PGRES_COMMAND_OK )
qWarning ( " %s " , PQerrorMessage ( connection ) ) ;
PQclear ( result ) ;
}
2013-03-07 10:52:52 +00:00
void QPSQLDriverPrivate : : detectBackslashEscape ( )
{
// standard_conforming_strings option introduced in 8.2
// http://www.postgresql.org/docs/8.2/static/runtime-config-compatible.html
if ( pro < QPSQLDriver : : Version82 ) {
hasBackslashEscape = true ;
} else {
hasBackslashEscape = false ;
PGresult * result = exec ( QLatin1Literal ( " SELECT ' \\ \\ ' x " ) ) ;
int status = PQresultStatus ( result ) ;
if ( status = = PGRES_COMMAND_OK | | status = = PGRES_TUPLES_OK )
if ( QString : : fromLatin1 ( PQgetvalue ( result , 0 , 0 ) ) = = QLatin1Literal ( " \\ " ) )
hasBackslashEscape = true ;
PQclear ( result ) ;
}
}
2011-04-27 10:05:43 +00:00
static QPSQLDriver : : Protocol qMakePSQLVersion ( int vMaj , int vMin )
{
switch ( vMaj ) {
case 6 :
return QPSQLDriver : : Version6 ;
case 7 :
{
switch ( vMin ) {
case 1 :
return QPSQLDriver : : Version71 ;
case 3 :
return QPSQLDriver : : Version73 ;
case 4 :
return QPSQLDriver : : Version74 ;
default :
return QPSQLDriver : : Version7 ;
}
break ;
}
case 8 :
{
switch ( vMin ) {
case 1 :
return QPSQLDriver : : Version81 ;
case 2 :
return QPSQLDriver : : Version82 ;
case 3 :
return QPSQLDriver : : Version83 ;
case 4 :
return QPSQLDriver : : Version84 ;
default :
return QPSQLDriver : : Version8 ;
}
break ;
}
case 9 :
return QPSQLDriver : : Version9 ;
break ;
default :
break ;
}
return QPSQLDriver : : VersionUnknown ;
}
2012-03-22 17:42:56 +00:00
QPSQLDriver : : Protocol QPSQLDriverPrivate : : getPSQLVersion ( )
2011-04-27 10:05:43 +00:00
{
QPSQLDriver : : Protocol serverVersion = QPSQLDriver : : Version6 ;
2012-03-22 17:42:56 +00:00
PGresult * result = exec ( " select version() " ) ;
2011-04-27 10:05:43 +00:00
int status = PQresultStatus ( result ) ;
if ( status = = PGRES_COMMAND_OK | | status = = PGRES_TUPLES_OK ) {
2012-05-02 14:32:26 +00:00
QString val = QString : : fromLatin1 ( PQgetvalue ( result , 0 , 0 ) ) ;
2011-04-27 10:05:43 +00:00
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 ( ) ;
serverVersion = qMakePSQLVersion ( vMaj , vMin ) ;
2012-12-20 04:26:50 +00:00
# if defined(PG_MAJORVERSION)
if ( rx . indexIn ( QLatin1String ( PG_MAJORVERSION ) ) ! = - 1 )
# elif defined(PG_VERSION)
if ( rx . indexIn ( QLatin1String ( PG_VERSION ) ) ! = - 1 )
# else
if ( 0 )
# endif
{
2011-04-27 10:05:43 +00:00
vMaj = rx . cap ( 1 ) . toInt ( ) ;
vMin = rx . cap ( 2 ) . toInt ( ) ;
2012-12-20 04:26:50 +00:00
QPSQLDriver : : Protocol clientVersion = qMakePSQLVersion ( vMaj , vMin ) ;
if ( serverVersion > = QPSQLDriver : : Version9 & & clientVersion < QPSQLDriver : : Version9 ) {
//Client version before QPSQLDriver::Version9 only supports escape mode for bytea type,
//but bytea format is set to hex by default in PSQL 9 and above. So need to force the
//server use the old escape mode when connects to the new server with old client library.
result = exec ( " SET bytea_output=escape; " ) ;
status = PQresultStatus ( result ) ;
} else if ( serverVersion = = QPSQLDriver : : VersionUnknown ) {
serverVersion = clientVersion ;
if ( serverVersion ! = QPSQLDriver : : VersionUnknown )
qWarning ( " The server version of this PostgreSQL is unknown, falling back to the client version. " ) ;
}
2011-04-27 10:05:43 +00:00
}
}
}
PQclear ( result ) ;
//keep the old behavior unchanged
if ( serverVersion = = QPSQLDriver : : VersionUnknown )
serverVersion = QPSQLDriver : : Version6 ;
if ( serverVersion < QPSQLDriver : : Version71 ) {
qWarning ( " This version of PostgreSQL is not supported and may not work. " ) ;
}
return serverVersion ;
}
QPSQLDriver : : QPSQLDriver ( QObject * parent )
: QSqlDriver ( parent )
{
init ( ) ;
}
QPSQLDriver : : QPSQLDriver ( PGconn * conn , QObject * parent )
: QSqlDriver ( parent )
{
init ( ) ;
d - > connection = conn ;
if ( conn ) {
2012-03-22 17:42:56 +00:00
d - > pro = d - > getPSQLVersion ( ) ;
2013-03-07 10:52:52 +00:00
d - > detectBackslashEscape ( ) ;
2011-04-27 10:05:43 +00:00
setOpen ( true ) ;
setOpenError ( false ) ;
}
}
void QPSQLDriver : : init ( )
{
2012-03-22 17:42:56 +00:00
d = new QPSQLDriverPrivate ( this ) ;
2011-04-27 10:05:43 +00:00
}
QPSQLDriver : : ~ QPSQLDriver ( )
{
if ( d - > connection )
PQfinish ( d - > connection ) ;
delete d ;
}
QVariant QPSQLDriver : : handle ( ) const
{
return QVariant : : fromValue ( d - > connection ) ;
}
bool QPSQLDriver : : hasFeature ( DriverFeature f ) const
{
switch ( f ) {
case Transactions :
case QuerySize :
case LastInsertId :
case LowPrecisionNumbers :
case EventNotifications :
return true ;
case PreparedQueries :
case PositionalPlaceholders :
return d - > pro > = QPSQLDriver : : Version82 ;
case BatchOperations :
case NamedPlaceholders :
case SimpleLocking :
case FinishQuery :
case MultipleResultSets :
2012-12-22 03:51:03 +00:00
case CancelQuery :
2011-04-27 10:05:43 +00:00
return false ;
case BLOB :
return d - > pro > = QPSQLDriver : : Version71 ;
case Unicode :
return d - > isUtf8 ;
}
return false ;
}
/*
Quote a string for inclusion into the connection string
\ - > \ \
' - > \ '
surround string by single quotes
*/
static QString qQuote ( QString s )
{
s . replace ( QLatin1Char ( ' \\ ' ) , QLatin1String ( " \\ \\ " ) ) ;
s . replace ( QLatin1Char ( ' \' ' ) , QLatin1String ( " \\ ' " ) ) ;
s . append ( QLatin1Char ( ' \' ' ) ) . prepend ( QLatin1Char ( ' \' ' ) ) ;
return s ;
}
bool QPSQLDriver : : open ( const QString & db ,
const QString & user ,
const QString & password ,
const QString & host ,
int port ,
const QString & connOpts )
{
if ( isOpen ( ) )
close ( ) ;
QString connectString ;
if ( ! host . isEmpty ( ) )
connectString . append ( QLatin1String ( " host= " ) ) . append ( qQuote ( host ) ) ;
if ( ! db . isEmpty ( ) )
connectString . append ( QLatin1String ( " dbname= " ) ) . append ( qQuote ( db ) ) ;
if ( ! user . isEmpty ( ) )
connectString . append ( QLatin1String ( " user= " ) ) . append ( qQuote ( user ) ) ;
if ( ! password . isEmpty ( ) )
connectString . append ( QLatin1String ( " password= " ) ) . append ( qQuote ( password ) ) ;
if ( port ! = - 1 )
connectString . append ( QLatin1String ( " port= " ) ) . append ( qQuote ( QString : : number ( port ) ) ) ;
// add any connect options - the server will handle error detection
if ( ! connOpts . isEmpty ( ) ) {
QString opt = connOpts ;
opt . replace ( QLatin1Char ( ' ; ' ) , QLatin1Char ( ' ' ) , Qt : : CaseInsensitive ) ;
connectString . append ( QLatin1Char ( ' ' ) ) . append ( opt ) ;
}
d - > connection = PQconnectdb ( connectString . toLocal8Bit ( ) . constData ( ) ) ;
if ( PQstatus ( d - > connection ) = = CONNECTION_BAD ) {
setLastError ( qMakeError ( tr ( " Unable to connect " ) , QSqlError : : ConnectionError , d ) ) ;
setOpenError ( true ) ;
PQfinish ( d - > connection ) ;
d - > connection = 0 ;
return false ;
}
2012-03-22 17:42:56 +00:00
d - > pro = d - > getPSQLVersion ( ) ;
2013-03-07 10:52:52 +00:00
d - > detectBackslashEscape ( ) ;
2012-03-22 17:42:56 +00:00
d - > isUtf8 = d - > setEncodingUtf8 ( ) ;
d - > setDatestyle ( ) ;
2011-04-27 10:05:43 +00:00
setOpen ( true ) ;
setOpenError ( false ) ;
return true ;
}
void QPSQLDriver : : close ( )
{
if ( isOpen ( ) ) {
d - > seid . clear ( ) ;
if ( d - > sn ) {
disconnect ( d - > sn , SIGNAL ( activated ( int ) ) , this , SLOT ( _q_handleNotification ( int ) ) ) ;
delete d - > sn ;
d - > sn = 0 ;
}
if ( d - > connection )
PQfinish ( d - > connection ) ;
d - > connection = 0 ;
setOpen ( false ) ;
setOpenError ( false ) ;
}
}
QSqlResult * QPSQLDriver : : createResult ( ) const
{
2013-02-26 22:58:20 +00:00
return new QPSQLResult ( this ) ;
2011-04-27 10:05:43 +00:00
}
bool QPSQLDriver : : beginTransaction ( )
{
if ( ! isOpen ( ) ) {
qWarning ( " QPSQLDriver::beginTransaction: Database not open " ) ;
return false ;
}
2012-03-22 17:42:56 +00:00
PGresult * res = d - > exec ( " BEGIN " ) ;
2011-04-27 10:05:43 +00:00
if ( ! res | | PQresultStatus ( res ) ! = PGRES_COMMAND_OK ) {
setLastError ( qMakeError ( tr ( " Could not begin transaction " ) ,
2012-07-01 14:11:00 +00:00
QSqlError : : TransactionError , d , res ) ) ;
PQclear ( res ) ;
2011-04-27 10:05:43 +00:00
return false ;
}
PQclear ( res ) ;
return true ;
}
bool QPSQLDriver : : commitTransaction ( )
{
if ( ! isOpen ( ) ) {
qWarning ( " QPSQLDriver::commitTransaction: Database not open " ) ;
return false ;
}
2012-03-22 17:42:56 +00:00
PGresult * res = d - > exec ( " COMMIT " ) ;
2011-04-27 10:05:43 +00:00
bool transaction_failed = false ;
// XXX
// This hack is used to tell if the transaction has succeeded for the protocol versions of
// PostgreSQL below. For 7.x and other protocol versions we are left in the dark.
// This hack can dissapear once there is an API to query this sort of information.
if ( d - > pro = = QPSQLDriver : : Version8 | |
d - > pro = = QPSQLDriver : : Version81 | |
d - > pro = = QPSQLDriver : : Version82 | |
d - > pro = = QPSQLDriver : : Version83 | |
d - > pro = = QPSQLDriver : : Version84 | |
d - > pro = = QPSQLDriver : : Version9 ) {
transaction_failed = qstrcmp ( PQcmdStatus ( res ) , " ROLLBACK " ) = = 0 ;
}
if ( ! res | | PQresultStatus ( res ) ! = PGRES_COMMAND_OK | | transaction_failed ) {
setLastError ( qMakeError ( tr ( " Could not commit transaction " ) ,
2012-07-01 14:11:00 +00:00
QSqlError : : TransactionError , d , res ) ) ;
PQclear ( res ) ;
2011-04-27 10:05:43 +00:00
return false ;
}
PQclear ( res ) ;
return true ;
}
bool QPSQLDriver : : rollbackTransaction ( )
{
if ( ! isOpen ( ) ) {
qWarning ( " QPSQLDriver::rollbackTransaction: Database not open " ) ;
return false ;
}
2012-03-22 17:42:56 +00:00
PGresult * res = d - > exec ( " ROLLBACK " ) ;
2011-04-27 10:05:43 +00:00
if ( ! res | | PQresultStatus ( res ) ! = PGRES_COMMAND_OK ) {
setLastError ( qMakeError ( tr ( " Could not rollback transaction " ) ,
2012-07-01 14:11:00 +00:00
QSqlError : : TransactionError , d , res ) ) ;
2011-04-27 10:05:43 +00:00
PQclear ( res ) ;
return false ;
}
PQclear ( res ) ;
return true ;
}
QStringList QPSQLDriver : : tables ( QSql : : TableType type ) const
{
QStringList tl ;
if ( ! isOpen ( ) )
return tl ;
QSqlQuery t ( createResult ( ) ) ;
t . setForwardOnly ( true ) ;
if ( type & QSql : : Tables )
d - > appendTables ( tl , t , QLatin1Char ( ' r ' ) ) ;
if ( type & QSql : : Views )
d - > appendTables ( tl , t , QLatin1Char ( ' v ' ) ) ;
if ( type & QSql : : SystemTables ) {
t . exec ( QLatin1String ( " select relname from pg_class where (relkind = 'r') "
" and (relname like 'pg_%') " ) ) ;
while ( t . next ( ) )
tl . append ( t . value ( 0 ) . toString ( ) ) ;
}
return tl ;
}
static void qSplitTableName ( QString & tablename , QString & schema )
{
int dot = tablename . indexOf ( QLatin1Char ( ' . ' ) ) ;
if ( dot = = - 1 )
return ;
schema = tablename . left ( dot ) ;
tablename = tablename . mid ( dot + 1 ) ;
}
QSqlIndex QPSQLDriver : : primaryIndex ( const QString & tablename ) const
{
QSqlIndex idx ( tablename ) ;
if ( ! isOpen ( ) )
return idx ;
QSqlQuery i ( createResult ( ) ) ;
QString stmt ;
QString tbl = tablename ;
QString schema ;
qSplitTableName ( tbl , schema ) ;
if ( isIdentifierEscaped ( tbl , QSqlDriver : : TableName ) )
tbl = stripDelimiters ( tbl , QSqlDriver : : TableName ) ;
else
tbl = tbl . toLower ( ) ;
if ( isIdentifierEscaped ( schema , QSqlDriver : : TableName ) )
schema = stripDelimiters ( schema , QSqlDriver : : TableName ) ;
else
schema = schema . toLower ( ) ;
switch ( d - > pro ) {
case 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 " ) ;
break ;
case QPSQLDriver : : Version7 :
case QPSQLDriver : : Version71 :
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 " ) ;
break ;
case QPSQLDriver : : Version73 :
case QPSQLDriver : : Version74 :
case QPSQLDriver : : Version8 :
case QPSQLDriver : : Version81 :
case QPSQLDriver : : Version82 :
case QPSQLDriver : : Version83 :
case QPSQLDriver : : Version84 :
case QPSQLDriver : : Version9 :
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 ) ) ;
break ;
case QPSQLDriver : : VersionUnknown :
qFatal ( " PSQL version is unknown " ) ;
break ;
}
i . exec ( stmt . arg ( tbl ) ) ;
while ( i . isActive ( ) & & i . next ( ) ) {
QSqlField f ( i . value ( 0 ) . toString ( ) , qDecodePSQLType ( i . value ( 1 ) . toInt ( ) ) ) ;
idx . append ( f ) ;
idx . setName ( i . value ( 2 ) . toString ( ) ) ;
}
return idx ;
}
QSqlRecord QPSQLDriver : : record ( const QString & tablename ) const
{
QSqlRecord info ;
if ( ! isOpen ( ) )
return info ;
QString tbl = tablename ;
QString schema ;
qSplitTableName ( tbl , schema ) ;
if ( isIdentifierEscaped ( tbl , QSqlDriver : : TableName ) )
tbl = stripDelimiters ( tbl , QSqlDriver : : TableName ) ;
else
tbl = tbl . toLower ( ) ;
if ( isIdentifierEscaped ( schema , QSqlDriver : : TableName ) )
schema = stripDelimiters ( schema , QSqlDriver : : TableName ) ;
else
schema = schema . toLower ( ) ;
QString stmt ;
switch ( d - > pro ) {
case 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 " ) ;
break ;
case 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 " ) ;
break ;
case QPSQLDriver : : Version71 :
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 " ) ;
break ;
case QPSQLDriver : : Version73 :
case QPSQLDriver : : Version74 :
case QPSQLDriver : : Version8 :
case QPSQLDriver : : Version81 :
case QPSQLDriver : : Version82 :
case QPSQLDriver : : Version83 :
case QPSQLDriver : : Version84 :
case QPSQLDriver : : Version9 :
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 ) ) ;
break ;
case QPSQLDriver : : VersionUnknown :
qFatal ( " PSQL version is unknown " ) ;
break ;
}
QSqlQuery query ( createResult ( ) ) ;
query . exec ( stmt . arg ( tbl ) ) ;
if ( d - > pro > = QPSQLDriver : : Version71 ) {
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 ) ;
}
}
return info ;
}
QString QPSQLDriver : : formatValue ( const QSqlField & field , bool trimStrings ) const
{
QString r ;
if ( field . isNull ( ) ) {
r = QLatin1String ( " NULL " ) ;
} else {
switch ( field . type ( ) ) {
case QVariant : : DateTime :
# ifndef QT_NO_DATESTRING
if ( field . value ( ) . toDateTime ( ) . isValid ( ) ) {
QDate dt = field . value ( ) . toDateTime ( ) . date ( ) ;
QTime tm = field . value ( ) . toDateTime ( ) . time ( ) ;
2013-02-11 19:46:54 +00:00
// msecs need to be right aligned otherwise psql interprets them wrong
2011-04-27 10:05:43 +00:00
r = QLatin1Char ( ' \' ' ) + QString : : number ( dt . year ( ) ) + QLatin1Char ( ' - ' )
+ QString : : number ( dt . month ( ) ) + QLatin1Char ( ' - ' )
+ QString : : number ( dt . day ( ) ) + QLatin1Char ( ' ' )
+ tm . toString ( ) + QLatin1Char ( ' . ' )
+ QString : : number ( tm . msec ( ) ) . rightJustified ( 3 , QLatin1Char ( ' 0 ' ) )
+ QLatin1Char ( ' \' ' ) ;
} else {
r = QLatin1String ( " NULL " ) ;
}
# else
r = QLatin1String ( " NULL " ) ;
# endif // QT_NO_DATESTRING
break ;
case QVariant : : Time :
# ifndef QT_NO_DATESTRING
if ( field . value ( ) . toTime ( ) . isValid ( ) ) {
r = QLatin1Char ( ' \' ' ) + field . value ( ) . toTime ( ) . toString ( QLatin1String ( " hh:mm:ss.zzz " ) ) + QLatin1Char ( ' \' ' ) ;
} else
# endif
{
r = QLatin1String ( " NULL " ) ;
}
break ;
case QVariant : : String :
r = QSqlDriver : : formatValue ( field , trimStrings ) ;
2013-03-07 10:52:52 +00:00
if ( d - > hasBackslashEscape )
r . replace ( QLatin1String ( " \\ " ) , QLatin1String ( " \\ \\ " ) ) ;
2011-04-27 10:05:43 +00:00
break ;
case QVariant : : Bool :
if ( field . value ( ) . toBool ( ) )
r = QLatin1String ( " TRUE " ) ;
else
r = QLatin1String ( " FALSE " ) ;
break ;
case QVariant : : ByteArray : {
QByteArray ba ( field . value ( ) . toByteArray ( ) ) ;
size_t len ;
# if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 80200
unsigned char * data = PQescapeByteaConn ( d - > connection , ( unsigned char * ) ba . constData ( ) , ba . size ( ) , & len ) ;
# else
unsigned char * data = PQescapeBytea ( ( unsigned char * ) ba . constData ( ) , ba . size ( ) , & len ) ;
# endif
r + = QLatin1Char ( ' \' ' ) ;
r + = QLatin1String ( ( const char * ) data ) ;
r + = QLatin1Char ( ' \' ' ) ;
qPQfreemem ( data ) ;
break ;
}
case QVariant : : Double : {
double val = field . value ( ) . toDouble ( ) ;
if ( isnan ( val ) )
r = QLatin1String ( " 'NaN' " ) ;
else {
int res = isinf ( val ) ;
if ( res = = 1 )
r = QLatin1String ( " 'Infinity' " ) ;
else if ( res = = - 1 )
r = QLatin1String ( " '-Infinity' " ) ;
else
r = QSqlDriver : : formatValue ( field , trimStrings ) ;
}
break ;
}
default :
r = QSqlDriver : : formatValue ( field , trimStrings ) ;
break ;
}
}
return r ;
}
QString QPSQLDriver : : escapeIdentifier ( const QString & identifier , IdentifierType ) const
{
QString res = identifier ;
if ( ! identifier . isEmpty ( ) & & ! identifier . startsWith ( QLatin1Char ( ' " ' ) ) & & ! identifier . endsWith ( QLatin1Char ( ' " ' ) ) ) {
res . replace ( QLatin1Char ( ' " ' ) , QLatin1String ( " \" \" " ) ) ;
res . prepend ( QLatin1Char ( ' " ' ) ) . append ( QLatin1Char ( ' " ' ) ) ;
res . replace ( QLatin1Char ( ' . ' ) , QLatin1String ( " \" . \" " ) ) ;
}
return res ;
}
bool QPSQLDriver : : isOpen ( ) const
{
return PQstatus ( d - > connection ) = = CONNECTION_OK ;
}
QPSQLDriver : : Protocol QPSQLDriver : : protocol ( ) const
{
return d - > pro ;
}
2012-03-22 20:27:26 +00:00
bool QPSQLDriver : : subscribeToNotification ( const QString & name )
2011-04-27 10:05:43 +00:00
{
if ( ! isOpen ( ) ) {
qWarning ( " QPSQLDriver::subscribeToNotificationImplementation: database not open. " ) ;
return false ;
}
if ( d - > seid . contains ( name ) ) {
qWarning ( " QPSQLDriver::subscribeToNotificationImplementation: already subscribing to '%s'. " ,
qPrintable ( name ) ) ;
return false ;
}
int socket = PQsocket ( d - > connection ) ;
if ( socket ) {
2012-03-22 17:42:56 +00:00
// Add the name to the list of subscriptions here so that QSQLDriverPrivate::exec knows
// to check for notifications immediately after executing the LISTEN
d - > seid < < name ;
2011-04-27 10:05:43 +00:00
QString query = QLatin1String ( " LISTEN " ) + escapeIdentifier ( name , QSqlDriver : : TableName ) ;
2012-07-01 14:11:00 +00:00
PGresult * result = d - > exec ( query ) ;
if ( PQresultStatus ( result ) ! = PGRES_COMMAND_OK ) {
setLastError ( qMakeError ( tr ( " Unable to subscribe " ) , QSqlError : : StatementError , d , result ) ) ;
2011-04-27 10:05:43 +00:00
return false ;
}
if ( ! d - > sn ) {
d - > sn = new QSocketNotifier ( socket , QSocketNotifier : : Read ) ;
connect ( d - > sn , SIGNAL ( activated ( int ) ) , this , SLOT ( _q_handleNotification ( int ) ) ) ;
}
2012-03-22 17:42:56 +00:00
} else {
qWarning ( " QPSQLDriver::subscribeToNotificationImplementation: PQsocket didn't return a valid socket to listen on " ) ;
return false ;
2011-04-27 10:05:43 +00:00
}
return true ;
}
2012-03-22 20:27:26 +00:00
bool QPSQLDriver : : unsubscribeFromNotification ( const QString & name )
2011-04-27 10:05:43 +00:00
{
if ( ! isOpen ( ) ) {
qWarning ( " QPSQLDriver::unsubscribeFromNotificationImplementation: database not open. " ) ;
return false ;
}
if ( ! d - > seid . contains ( name ) ) {
qWarning ( " QPSQLDriver::unsubscribeFromNotificationImplementation: not subscribed to '%s'. " ,
qPrintable ( name ) ) ;
return false ;
}
QString query = QLatin1String ( " UNLISTEN " ) + escapeIdentifier ( name , QSqlDriver : : TableName ) ;
2012-07-01 14:11:00 +00:00
PGresult * result = d - > exec ( query ) ;
if ( PQresultStatus ( result ) ! = PGRES_COMMAND_OK ) {
setLastError ( qMakeError ( tr ( " Unable to unsubscribe " ) , QSqlError : : StatementError , d , result ) ) ;
2011-04-27 10:05:43 +00:00
return false ;
}
d - > seid . removeAll ( name ) ;
if ( d - > seid . isEmpty ( ) ) {
disconnect ( d - > sn , SIGNAL ( activated ( int ) ) , this , SLOT ( _q_handleNotification ( int ) ) ) ;
delete d - > sn ;
d - > sn = 0 ;
}
return true ;
}
2012-03-22 20:27:26 +00:00
QStringList QPSQLDriver : : subscribedToNotifications ( ) const
2011-04-27 10:05:43 +00:00
{
return d - > seid ;
}
void QPSQLDriver : : _q_handleNotification ( int )
{
2012-03-22 17:42:56 +00:00
d - > pendingNotifyCheck = false ;
2011-04-27 10:05:43 +00:00
PQconsumeInput ( d - > connection ) ;
PGnotify * notify = 0 ;
while ( ( notify = PQnotifies ( d - > connection ) ) ! = 0 ) {
QString name ( QLatin1String ( notify - > relname ) ) ;
2012-01-24 20:27:36 +00:00
if ( d - > seid . contains ( name ) ) {
2012-03-22 17:42:56 +00:00
QString payload ;
# if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 70400
if ( notify - > extra )
2012-05-02 14:32:26 +00:00
payload = d - > isUtf8 ? QString : : fromUtf8 ( notify - > extra ) : QString : : fromLatin1 ( notify - > extra ) ;
2012-03-22 17:42:56 +00:00
# endif
2011-04-27 10:05:43 +00:00
emit notification ( name ) ;
2012-03-22 17:42:56 +00:00
QSqlDriver : : NotificationSource source = ( notify - > be_pid = = PQbackendPID ( d - > connection ) ) ? QSqlDriver : : SelfSource : QSqlDriver : : OtherSource ;
2012-03-22 17:42:56 +00:00
emit notification ( name , source , payload ) ;
2012-01-24 20:27:36 +00:00
}
2011-04-27 10:05:43 +00:00
else
qWarning ( " QPSQLDriver: received notification for '%s' which isn't subscribed to. " ,
qPrintable ( name ) ) ;
qPQfreemem ( notify ) ;
}
}
QT_END_NAMESPACE