[QGIS Commit] r9183 - trunk/qgis/src/providers/postgres

svn_qgis at osgeo.org svn_qgis at osgeo.org
Wed Aug 27 04:00:57 EDT 2008


Author: jef
Date: 2008-08-27 04:00:57 -0400 (Wed, 27 Aug 2008)
New Revision: 9183

Modified:
   trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp
   trunk/qgis/src/providers/postgres/qgspostgresprovider.h
Log:
postgres provider update
- track open cursors and commit transaction whenever no open cursors are left
- close cursor after immediately after feature retrieval (releases locks earlier)
- wrap postgres calls in Conn class
- introduce Result class to handle PQclear mostly transparently (fixes minor leaks)
- remove executeDbCommand method (duplicate of PQexecNR)
- introduce support for estimate_extents (fixes #582)
- introduce support for enum types (fixes #1210)



Modified: trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp
===================================================================
--- trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp	2008-08-27 07:44:03 UTC (rev 9182)
+++ trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp	2008-08-27 08:00:57 UTC (rev 9183)
@@ -50,16 +50,15 @@
 const QString POSTGRES_KEY = "postgres";
 const QString POSTGRES_DESCRIPTION = "PostgreSQL/PostGIS data provider";
 
-QMap<QString, QgsPostgresProvider::Conn *> QgsPostgresProvider::connectionsRO;
-QMap<QString, QgsPostgresProvider::Conn *> QgsPostgresProvider::connectionsRW;
+QMap<QString, QgsPostgresProvider::Conn *> QgsPostgresProvider::Conn::connectionsRO;
+QMap<QString, QgsPostgresProvider::Conn *> QgsPostgresProvider::Conn::connectionsRW;
 int QgsPostgresProvider::providerIds = 0;
 
 QgsPostgresProvider::QgsPostgresProvider( QString const & uri )
     : QgsVectorDataProvider( uri ),
     mFetching( false ),
     geomType( QGis::WKBUnknown ),
-    mFeatureQueueSize( 200 ),
-    gotPostgisVersion( false )
+    mFeatureQueueSize( 200 )
 {
   // assume this is a valid layer until we determine otherwise
   valid = true;
@@ -89,32 +88,19 @@
   QgsDebugMsg( "Table name is: " + mTableName );
 
   connectionRW = NULL;
-  connectionRO = connectDb( mUri.connInfo() );
+  connectionRO = Conn::connectDb( mUri.connInfo(), true );
   if ( connectionRO == NULL )
   {
     valid = false;
     return;
   }
 
-  /* Check to see if we have GEOS support and if not, warn the user about
-     the problems they will see :) */
-  QgsDebugMsg( "Checking for GEOS support" );
-
-  if ( !hasGEOS( connectionRO ) )
-  {
-    showMessageBox( tr( "No GEOS Support!" ),
-                    tr( "Your PostGIS installation has no GEOS support.\n"
-                        "Feature selection and identification will not "
-                        "work properly.\nPlease install PostGIS with "
-                        "GEOS support (http://geos.refractions.net)" ) );
-  }
-
   QgsDebugMsg( "Checking for permissions on the relation" );
 
   // Check that we can read from the table (i.e., we have
   // select permission).
   QString sql = QString( "select * from %1 limit 1" ).arg( mSchemaTableName );
-  PGresult* testAccess = PQexec( connectionRO, sql.toUtf8() );
+  Result testAccess = connectionRO->PQexec( sql );
   if ( PQresultStatus( testAccess ) != PGRES_TUPLES_OK )
   {
     showMessageBox( tr( "Unable to access relation" ),
@@ -122,12 +108,10 @@
                     tr( " relation.\nThe error message from the database was:\n" ) +
                     QString::fromUtf8( PQresultErrorMessage( testAccess ) ) + ".\n" +
                     "SQL: " + sql );
-    PQclear( testAccess );
     valid = false;
     disconnectDb();
     return;
   }
-  PQclear( testAccess );
 
   sql = QString( "SELECT "
                  "has_table_privilege(%1,'DELETE'),"
@@ -136,7 +120,7 @@
                  "current_schema()" )
         .arg( quotedValue( mSchemaTableName ) );
 
-  testAccess = PQexec( connectionRO, sql.toUtf8() );
+  testAccess = connectionRO->PQexec( sql );
   if ( PQresultStatus( testAccess ) != PGRES_TUPLES_OK )
   {
     showMessageBox( tr( "Unable to access relation" ),
@@ -144,7 +128,6 @@
                     tr( " relation.\nThe error message from the database was:\n" ) +
                     QString::fromUtf8( PQresultErrorMessage( testAccess ) ) + ".\n" +
                     "SQL: " + sql );
-    PQclear( testAccess );
     valid = false;
     disconnectDb();
     return;
@@ -178,7 +161,6 @@
   }
   if ( mSchemaName == "" )
     mSchemaName = mCurrentSchema;
-  PQclear( testAccess );
 
   sql = QString( "SELECT 1 FROM pg_class,pg_namespace WHERE "
                  "pg_class.relnamespace=pg_namespace.oid AND "
@@ -186,12 +168,11 @@
                  "relname=%1 AND nspname=%2" )
         .arg( quotedValue( mTableName ) )
         .arg( quotedValue( mSchemaName ) );
-  testAccess = PQexec( connectionRO, sql.toUtf8() );
+  testAccess = connectionRO->PQexec( sql );
   if ( PQresultStatus( testAccess ) == PGRES_TUPLES_OK && PQntuples( testAccess ) == 1 )
   {
     enabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes;
   }
-  PQclear( testAccess );
 
   if ( !getGeometryDetails() ) // gets srid and geometry type
   {
@@ -217,7 +198,7 @@
   // Set the postgresql message level so that we don't get the
   // 'there is no transaction in progress' warning.
 #ifndef QGISDEBUG
-  PQexecNR( connectionRO, QString( "set client_min_messages to error" ).toUtf8() );
+  connectionRO->PQexecNR( "set client_min_messages to error" );
 #endif
 
   // Kick off the long running threads
@@ -289,15 +270,16 @@
   //pLog.flush();
 }
 
-PGconn *QgsPostgresProvider::connectDb( const QString & conninfo, bool readonly )
+QgsPostgresProvider::Conn *QgsPostgresProvider::Conn::connectDb( const QString & conninfo, bool readonly )
 {
-  QMap<QString, QgsPostgresProvider::Conn *> &connections = readonly ? connectionsRO : connectionsRW;
+  QMap<QString, QgsPostgresProvider::Conn *> &connections =
+    readonly ? QgsPostgresProvider::Conn::connectionsRO : QgsPostgresProvider::Conn::connectionsRW;
 
   if ( connections.contains( conninfo ) )
   {
     QgsDebugMsg( QString( "Using cached connection for %1" ).arg( conninfo ) );
     connections[conninfo]->ref++;
-    return connections[conninfo]->conn;
+    return connections[conninfo];
   }
 
   QgsDebugMsg( QString( "New postgres connection for " ) + conninfo );
@@ -333,59 +315,70 @@
   Conn *conn = new Conn( pd );
   connections.insert( conninfo, conn );
 
-  if ( readonly )
+  /* Check to see if we have GEOS support and if not, warn the user about
+     the problems they will see :) */
+  QgsDebugMsg( "Checking for GEOS support" );
+
+  if ( !conn->hasGEOS() )
   {
-    QgsDebugMsg( "Starting transaction for cursors" );
-    PQexecNR( pd, QString( "BEGIN" ).toUtf8() );
+    showMessageBox( tr( "No GEOS Support!" ),
+                    tr( "Your PostGIS installation has no GEOS support.\n"
+                        "Feature selection and identification will not "
+                        "work properly.\nPlease install PostGIS with "
+                        "GEOS support (http://geos.refractions.net)" ) );
   }
 
-  return pd;
+
+
+  return conn;
 }
 
 void QgsPostgresProvider::disconnectDb()
 {
   if ( mFetching )
   {
-    PQexecNR( connectionRO, QString( "CLOSE qgisf%1" ).arg( providerId ).toUtf8() );
+    connectionRO->closeCursor( QString( "qgisf%1" ).arg( providerId ) );
     mFetching = false;
   }
 
-  if ( !connectionRO )
+  if ( connectionRO )
   {
-    QMap<QString, Conn *>::iterator i;
-    for ( i = connectionsRO.begin(); i != connectionsRO.end() && i.value()->conn != connectionRO; i++ )
-      ;
+    Conn::disconnectRO( connectionRO );
+  }
 
-    assert( i.value()->conn == connectionRO );
-    assert( i.value()->ref > 0 );
+  if ( connectionRW )
+  {
+    Conn::disconnectRW( connectionRW );
+  }
+}
 
-    if ( --i.value()->ref == 0 )
-    {
-      PQfinish( connectionRO );
-      delete i.value();
-      connectionsRO.remove( i.key() );
-    }
+void QgsPostgresProvider::Conn::disconnectRW( Conn *&connection )
+{
+  disconnect( connectionsRW, connection );
+}
 
-    connectionRO = 0;
+void QgsPostgresProvider::Conn::disconnectRO( Conn *&connection )
+{
+  disconnect( connectionsRO, connection );
+}
 
-    if ( connectionRW )
-    {
-      for ( i = connectionsRW.begin(); i != connectionsRW.end() && i.value()->conn != connectionRW; i++ )
-        ;
+void QgsPostgresProvider::Conn::disconnect( QMap<QString, Conn *>& connections, Conn *&conn )
+{
+  QMap<QString, Conn *>::iterator i;
+  for ( i = connections.begin(); i != connections.end() && i.value() != conn; i++ )
+    ;
 
-      assert( i.value()->conn == connectionRW );
-      assert( i.value()->ref > 0 );
+  assert( i.value() == conn );
+  assert( i.value()->ref > 0 );
 
-      if ( --i.value()->ref == 0 )
-      {
-        PQfinish( connectionRW );
-        delete i.value();
-        connectionsRW.remove( i.key() );
-      }
-
-      connectionRW = 0;
-    }
+  if ( --i.value()->ref == 0 )
+  {
+    i.value()->PQfinish();
+    delete i.value();
+    connections.remove( i.key() );
   }
+
+  conn = NULL;
 }
 
 QString QgsPostgresProvider::storageType() const
@@ -393,21 +386,21 @@
   return "PostgreSQL database with PostGIS extension";
 }
 
-bool QgsPostgresProvider::declareCursor( const QString &cursorName,
-    const QgsAttributeList &fetchAttributes,
-    bool fetchGeometry,
-    QString whereClause )
+bool QgsPostgresProvider::declareCursor(
+  const QString &cursorName,
+  const QgsAttributeList &fetchAttributes,
+  bool fetchGeometry,
+  QString whereClause )
 {
   try
   {
-    QString declare = QString( "declare %1 binary cursor for select %2" )
-                      .arg( cursorName ).arg( quotedIdentifier( primaryKey ) );
+    QString query = QString( "select %1" ).arg( quotedIdentifier( primaryKey ) );
 
     if ( fetchGeometry )
     {
-      declare += QString( ",asbinary(%1,'%2')" )
-                 .arg( quotedIdentifier( geometryColumn ) )
-                 .arg( endianString() );
+      query += QString( ",asbinary(%1,'%2')" )
+               .arg( quotedIdentifier( geometryColumn ) )
+               .arg( endianString() );
     }
 
     for ( QgsAttributeList::const_iterator it = fetchAttributes.constBegin(); it != fetchAttributes.constEnd(); ++it )
@@ -421,30 +414,28 @@
       const QString &type = fld.typeName();
       if ( type == "money" )
       {
-        declare += QString( ",cash_out(%1)" ).arg( quotedIdentifier( fieldname ) );
+        query += QString( ",cash_out(%1)" ).arg( quotedIdentifier( fieldname ) );
       }
       else if ( type.startsWith( "_" ) )
       {
-        declare += QString( ",array_out(%1)" ).arg( quotedIdentifier( fieldname ) );
+        query += QString( ",array_out(%1)" ).arg( quotedIdentifier( fieldname ) );
       }
       else if ( type == "bool" )
       {
-        declare += QString( ",boolout(%1)" ).arg( quotedIdentifier( fieldname ) );
+        query += QString( ",boolout(%1)" ).arg( quotedIdentifier( fieldname ) );
       }
       else
       {
-        declare += "," + quotedIdentifier( fieldname ) + "::text";
+        query += "," + quotedIdentifier( fieldname ) + "::text";
       }
     }
 
-    declare += " from " + mSchemaTableName;
+    query += " from " + mSchemaTableName;
 
     if ( !whereClause.isEmpty() )
-      declare += QString( " where %1" ).arg( whereClause );
+      query += QString( " where %1" ).arg( whereClause );
 
-    QgsDebugMsg( "Binary cursor: " + declare );
-
-    return PQexecNR( connectionRO, declare.toUtf8() );
+    return connectionRO->openCursor( cursorName, query );
   }
   catch ( PGFieldNotFound )
   {
@@ -550,7 +541,7 @@
 
   if ( mFetching )
   {
-    PQexecNR( connectionRO, QString( "CLOSE %1" ).arg( cursorName ).toUtf8() );
+    connectionRO->closeCursor( cursorName );
     mFetching = false;
 
     while ( !mFeatureQueue.empty() )
@@ -610,13 +601,13 @@
   if ( mFeatureQueue.empty() )
   {
     QString fetch = QString( "fetch forward %1 from %2" ).arg( mFeatureQueueSize ).arg( cursorName );
-    if ( PQsendQuery( connectionRO, fetch.toUtf8() ) == 0 ) //fetch features in asynchronously
+    if ( connectionRO->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously
     {
       qWarning( "PQsendQuery failed (1)" );
     }
 
-    PGresult *queryResult;
-    while (( queryResult = PQgetResult( connectionRO ) ) )
+    Result queryResult;
+    while (( queryResult = connectionRO->PQgetResult() ) )
     {
       int rows = PQntuples( queryResult );
       if ( rows == 0 )
@@ -627,14 +618,14 @@
         mFeatureQueue.push( QgsFeature() );
         getFeature( queryResult, row, mFetchGeom, mFeatureQueue.back(), mAttributesToFetch );
       } // for each row in queue
-
-      PQclear( queryResult );
     }
   }
 
   if ( mFeatureQueue.empty() )
   {
     QgsDebugMsg( "End of features" );
+    connectionRO->closeCursor( cursorName );
+    mFetching = false;
     return false;
   }
 
@@ -662,7 +653,7 @@
   if ( !declareCursor( cursorName, fetchAttributes, fetchGeometry, QString( "%2=%3" ).arg( quotedIdentifier( primaryKey ) ).arg( featureId ) ) )
     return false;
 
-  PGresult *queryResult = PQexec( connectionRO, QString( "fetch forward 1 from %1" ).arg( cursorName ).toUtf8() );
+  Result queryResult = connectionRO->PQexec( QString( "fetch forward 1 from %1" ).arg( cursorName ) );
   if ( queryResult == 0 )
     return false;
 
@@ -670,8 +661,7 @@
   if ( rows == 0 )
   {
     QgsDebugMsg( "feature " + QString::number( featureId ) + " not found" );
-    PQclear( queryResult );
-    PQexecNR( connectionRO, QString( "CLOSE %1" ).arg( cursorName ).toUtf8() );
+    connectionRO->closeCursor( cursorName );
     return false;
   }
   else if ( rows != 1 )
@@ -681,10 +671,8 @@
 
   bool gotit = getFeature( queryResult, 0, fetchGeometry, feature, fetchAttributes );
 
-  PQclear( queryResult );
+  connectionRO->closeCursor( cursorName );
 
-  PQexecNR( connectionRO, QString( "CLOSE %1" ).arg( cursorName ).toUtf8() );
-
   return gotit;
 }
 
@@ -760,7 +748,7 @@
   if ( mFetching )
   {
     //move cursor to first record
-    PQexecNR( connectionRO, QString( "move 0 in qgisf%1" ).arg( providerId ).toUtf8() );
+    connectionRO->PQexecNR( QString( "move 0 in qgisf%1" ).arg( providerId ) );
   }
   mFeatureQueue.empty();
   loadFields();
@@ -788,25 +776,21 @@
 
   // Get the relation oid for use in later queries
   QString sql = QString( "SELECT regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
-  PGresult *tresult = PQexec( connectionRO, sql.toUtf8() );
+  Result tresult = connectionRO->PQexec( sql );
   QString tableoid = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
-  PQclear( tresult );
 
   // Get the table description
   sql = QString( "SELECT description FROM pg_description WHERE objoid=%1 AND objsubid=0" ).arg( tableoid );
-  tresult = PQexec( connectionRO, sql.toUtf8() );
+  tresult = connectionRO->PQexec( sql );
   if ( PQntuples( tresult ) > 0 )
     mDataComment = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
-  PQclear( tresult );
 
   // Populate the field vector for this layer. The field vector contains
   // field name, type, length, and precision (if numeric)
   sql = QString( "select * from %1 limit 0" ).arg( mSchemaTableName );
 
-  PGresult *result = PQexec( connectionRO, sql.toUtf8() );
-  //--std::cout << "Field: Name, Type, Size, Modifier:" << std::endl;
+  Result result = connectionRO->PQexec( sql );
 
-
   // The queries inside this loop could possibly be combined into one
   // single query - this would make the code run faster.
 
@@ -819,70 +803,82 @@
     int fieldModifier = PQfmod( result, i );
     QString fieldComment( "" );
 
-    sql = QString( "SELECT typname,typlen FROM pg_type WHERE oid=%1" ).arg( typOid );
+    sql = QString( "SELECT typname,typtype,typelem,typlen FROM pg_type WHERE oid=%1" ).arg( typOid );
     // just oid; needs more work to support array type
     //      "oid = (SELECT Distinct typelem FROM pg_type WHERE "  //needs DISTINCT to guard against 2 or more rows on int2
     //      "typelem = " + typOid + " AND typlen = -1)";
 
-    PGresult* oidResult = PQexec( connectionRO, sql.toUtf8() );
+    Result oidResult = connectionRO->PQexec( sql );
     QString fieldTypeName = QString::fromUtf8( PQgetvalue( oidResult, 0, 0 ) );
-    QString fieldSize = QString::fromUtf8( PQgetvalue( oidResult, 0, 1 ) );
-    PQclear( oidResult );
+    QString fieldTType = QString::fromUtf8( PQgetvalue( oidResult, 0, 1 ) );
+    QString fieldElem = QString::fromUtf8( PQgetvalue( oidResult, 0, 2 ) );
+    QString fieldSize = QString::fromUtf8( PQgetvalue( oidResult, 0, 3 ) );
 
     sql = QString( "SELECT attnum FROM pg_attribute WHERE attrelid=%1 AND attname=%2" )
           .arg( tableoid ).arg( quotedValue( fieldName ) );
 
-    PGresult *tresult = PQexec( connectionRO, sql.toUtf8() );
+    Result tresult = connectionRO->PQexec( sql );
     QString attnum = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
-    PQclear( tresult );
 
     sql = QString( "SELECT description FROM pg_description WHERE objoid=%1 AND objsubid=%2" )
           .arg( tableoid ).arg( attnum );
 
-    tresult = PQexec( connectionRO, sql.toUtf8() );
+    tresult = connectionRO->PQexec( sql );
     if ( PQntuples( tresult ) > 0 )
       fieldComment = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
-    PQclear( tresult );
 
     if ( fieldName != geometryColumn )
     {
       QVariant::Type fieldType;
-      bool isArray = fieldTypeName.startsWith( "_" );
 
-      if ( isArray )
-        fieldTypeName = fieldTypeName.mid( 1 );
+      if ( fieldTType == "b" )
+      {
+        bool isArray = fieldTypeName.startsWith( "_" );
 
-      if ( fieldTypeName == "int8" )
-        fieldType = QVariant::LongLong;
-      else if ( fieldTypeName.startsWith( "int" ) || fieldTypeName == "serial" )
-        fieldType = QVariant::Int;
-      else if ( fieldTypeName == "real" || fieldTypeName == "double precision" || fieldTypeName.startsWith( "float" ) || fieldTypeName == "numeric" )
-        fieldType = QVariant::Double;
-      else if ( fieldTypeName == "text" ||
-                fieldTypeName == "char" ||
-                fieldTypeName == "bpchar" ||
-                fieldTypeName == "varchar" ||
-                fieldTypeName == "bool" ||
-                fieldTypeName == "money" ||
-                fieldTypeName.startsWith( "time" ) ||
-                fieldTypeName.startsWith( "date" ) )
+        if ( isArray )
+          fieldTypeName = fieldTypeName.mid( 1 );
+
+        if ( fieldTypeName == "int8" )
+          fieldType = QVariant::LongLong;
+        else if ( fieldTypeName.startsWith( "int" ) || fieldTypeName == "serial" )
+          fieldType = QVariant::Int;
+        else if ( fieldTypeName == "real" || fieldTypeName == "double precision" || fieldTypeName.startsWith( "float" ) || fieldTypeName == "numeric" )
+          fieldType = QVariant::Double;
+        else if ( fieldTypeName == "text" ||
+                  fieldTypeName == "char" ||
+                  fieldTypeName == "bpchar" ||
+                  fieldTypeName == "varchar" ||
+                  fieldTypeName == "bool" ||
+                  fieldTypeName == "money" ||
+                  fieldTypeName.startsWith( "time" ) ||
+                  fieldTypeName.startsWith( "date" ) )
+          fieldType = QVariant::String;
+        else
+        {
+          QgsDebugMsg( "Field " + fieldName + " ignored, because of unsupported type " + fieldTypeName );
+          continue;
+        }
+
+        if ( isArray )
+        {
+          fieldTypeName = "_" + fieldTypeName;
+          fieldType = QVariant::String;
+        }
+      }
+      else if ( fieldTType == "e" )
+      {
+        // enum
         fieldType = QVariant::String;
+      }
       else
       {
-        QgsDebugMsg( "Field " + fieldName + " ignored, because of unsupported type " + fieldTypeName );
+        QgsDebugMsg( "Field " + fieldName + " ignored, because of unsupported type type " + fieldTType );
         continue;
       }
 
-      if ( isArray )
-      {
-        fieldTypeName = "_" + fieldTypeName;
-        fieldType = QVariant::String;
-      }
-
       attributeFields.insert( i, QgsField( fieldName, fieldType, fieldTypeName, fieldSize.toInt(), fieldModifier, fieldComment ) );
     }
   }
-  PQclear( result );
 }
 
 QString QgsPostgresProvider::getPrimaryKey()
@@ -896,7 +892,7 @@
 
   QgsDebugMsg( "Getting unique index using '" + sql + "'" );
 
-  PGresult *pk = executeDbCommand( connectionRO, sql );
+  Result pk = connectionRO->PQexec( sql );
 
   QgsDebugMsg( "Got " + QString::number( PQntuples( pk ) ) + " rows." );
 
@@ -914,9 +910,8 @@
 
     sql = QString( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" )
           .arg( quotedValue( mSchemaTableName ) );
-    PGresult* tableType = executeDbCommand( connectionRO, sql );
+    Result tableType = connectionRO->PQexec( sql );
     QString type = QString::fromUtf8( PQgetvalue( tableType, 0, 0 ) );
-    PQclear( tableType );
 
     primaryKey = "";
 
@@ -929,7 +924,7 @@
       sql = QString( "SELECT attname FROM pg_attribute WHERE attname='oid' AND attrelid=regclass(%1)" )
             .arg( quotedValue( mSchemaTableName ) );
 
-      PGresult* oidCheck = executeDbCommand( connectionRO, sql );
+      Result oidCheck = connectionRO->PQexec( sql );
 
       if ( PQntuples( oidCheck ) != 0 )
       {
@@ -947,7 +942,6 @@
                             "int4 with a unique constraint on it (which includes the\n"
                             "primary key) or has a PostgreSQL oid column.\n" ) );
       }
-      PQclear( oidCheck );
     }
     else if ( type == "v" ) // the relation is a view
     {
@@ -977,7 +971,7 @@
         // Get the column name and data type
         sql = QString( "select attname,pg_type.typname from pg_attribute,pg_type where atttypid=pg_type.oid and attnum=%1 and attrelid=regclass(%2)" )
               .arg( col ).arg( quotedValue( mSchemaTableName ) );
-        PGresult* types = executeDbCommand( connectionRO, sql );
+        Result types = connectionRO->PQexec( sql );
 
         if ( PQntuples( types ) > 0 )
         {
@@ -996,8 +990,6 @@
         {
           //QgsDebugMsg( QString("name and type of %3. column of %1.%2 not found").arg(mSchemaName).arg(mTables).arg(col) );
         }
-
-        PQclear( types );
       }
       else
       {
@@ -1005,7 +997,7 @@
               .arg( col.replace( " ", "," ) )
               .arg( quotedValue( mSchemaTableName ) );
 
-        PGresult* types = executeDbCommand( connectionRO, sql );
+        Result types = connectionRO->PQexec( sql );
         QString colNames;
         int numCols = PQntuples( types );
         for ( int j = 0; j < numCols; ++j )
@@ -1039,7 +1031,7 @@
       // If there is an oid on the table, use that instead,
       // otherwise give up
       sql = QString( "select attname from pg_attribute where attname='oid' and attrelid=regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
-      PGresult* oidCheck = executeDbCommand( connectionRO, sql );
+      Result oidCheck = connectionRO->PQexec( sql );
 
       if ( PQntuples( oidCheck ) != 0 )
       {
@@ -1052,7 +1044,6 @@
                      "as a qgis key into the table (either a column with a "
                      "unique index and type int4 or a PostgreSQL oid column.\n" );
       }
-      PQclear( oidCheck );
     }
 
     // Either primaryKey has been set by the above code, or it
@@ -1065,7 +1056,6 @@
       showMessageBox( tr( "Unable to find a key column" ), log );
     }
   }
-  PQclear( pk );
 
   if ( primaryKey.length() > 0 )
   {
@@ -1109,7 +1099,7 @@
     // Get the oid from pg_class for the given schema.relation for use
     // in subsequent queries.
     sql = QString( "select regclass(%1)::oid" ).arg( quotedValue( quotedIdentifier( schemaName ) + "." + quotedIdentifier( tableName ) ) );
-    PGresult* result = PQexec( connectionRO, sql.toUtf8() );
+    Result result = connectionRO->PQexec( sql );
     QString rel_oid;
     if ( PQntuples( result ) == 1 )
     {
@@ -1122,10 +1112,8 @@
       QgsDebugMsg( "Relation " + schemaName + "." + tableName +
                    " doesn't exist in the pg_class table."
                    "This shouldn't happen and is odd." );
-      PQclear( result );
       continue;
     }
-    PQclear( result );
 
     // This sql returns one or more rows if the column 'tableCol' in
     // table 'tableName' and schema 'schemaName' has one or more
@@ -1140,7 +1128,7 @@
                    "and conrelid=%2 and (contype='p' or contype='u') "
                    "and array_dims(conkey)='[1:1]'" ).arg( quotedValue( tableCol ) ).arg( rel_oid );
 
-    result = PQexec( connectionRO, sql.toUtf8() );
+    result = connectionRO->PQexec( sql );
     if ( PQntuples( result ) == 1 && colType == "int4" )
       suitable[viewCol] = iter->second;
 
@@ -1163,7 +1151,6 @@
 
     log << details;
 
-    PQclear( result );
     if ( tableCol == "oid" )
       oids.push_back( iter );
   }
@@ -1199,16 +1186,15 @@
     sql = QString( "select * from pg_index where indrelid=%1 and indkey[0]=(select attnum from pg_attribute where attrelid=%1 and attname=%2)" )
           .arg( rel_oid )
           .arg( quotedValue( i->second.column ) );
-    PGresult* result = PQexec( connectionRO, sql.toUtf8() );
+    Result result = connectionRO->PQexec( sql );
 
     if ( PQntuples( result ) > 0 && uniqueData( mSchemaName, mTableName, i->first ) )
-    { // Got one. Use it.
+    {
+      // Got one. Use it.
       key = i->first;
-
       QgsDebugMsg( "Picked column '" + key + "' because it has an index." );
       break;
     }
-    PQclear( result );
   }
 
   if ( key.isEmpty() )
@@ -1285,13 +1271,11 @@
                 .arg( quotedIdentifier( schemaName ) )
                 .arg( quotedIdentifier( tableName ) );
 
-  PGresult* unique = PQexec( connectionRO, sql.toUtf8() );
+  Result unique = connectionRO->PQexec( sql );
 
   if ( PQntuples( unique ) == 1 && QString::fromUtf8( PQgetvalue( unique, 0, 0 ) ).startsWith( "t" ) )
     isUnique = true;
 
-  PQclear( unique );
-
   return isUnique;
 }
 
@@ -1299,7 +1283,7 @@
 {
   QString newViewDefSql = QString( "SELECT definition FROM pg_views WHERE schemaname=%1 AND viewname=%2" )
                           .arg( quotedValue( ns ) ).arg( quotedValue( relname ) );
-  PGresult* newViewDefResult = PQexec( connectionRO, newViewDefSql.toUtf8() );
+  Result newViewDefResult = connectionRO->PQexec( newViewDefSql );
   int numEntries = PQntuples( newViewDefResult );
 
   if ( numEntries > 0 ) //relation is a view
@@ -1375,25 +1359,21 @@
       .arg( quotedValue( relname ) )
       .arg( quotedValue( newAttNameTable ) );
 
-    PGresult* viewColumnResult = PQexec( connectionRO, viewColumnSql.toUtf8() );
+    Result viewColumnResult = connectionRO->PQexec( viewColumnSql );
     if ( PQntuples( viewColumnResult ) > 0 )
     {
       QString newTableSchema = QString::fromUtf8( PQgetvalue( viewColumnResult, 0, 0 ) );
       QString newTableName = QString::fromUtf8( PQgetvalue( viewColumnResult, 0, 1 ) );
       int retvalue = SRCFromViewColumn( newTableSchema, newTableName, newAttNameTable, newAttNameView, newViewDefinition, result );
-      PQclear( viewColumnResult );
       return retvalue;
     }
     else
     {
-      PQclear( viewColumnResult );
       return 1;
     }
 
   }
 
-  PQclear( newViewDefResult );
-
   //relation is table, we just have to add the type
   QString typeSql = QString( "SELECT "
                              "pg_type.typname"
@@ -1413,13 +1393,12 @@
                     .arg( quotedValue( ns ) )
                     .arg( quotedValue( attname_table ) );
 
-  PGresult* typeSqlResult = PQexec( connectionRO, typeSql.toUtf8() );
+  Result typeSqlResult = connectionRO->PQexec( typeSql );
   if ( PQntuples( typeSqlResult ) < 1 )
   {
     return 1;
   }
   QString type = QString::fromUtf8( PQgetvalue( typeSqlResult, 0, 0 ) );
-  PQclear( typeSqlResult );
 
   result.schema = ns;
   result.relation = relname;
@@ -1486,20 +1465,19 @@
              "view_schema=%1 AND view_name=%2" )
     .arg( quotedValue( mSchemaName ) )
     .arg( quotedValue( mTableName ) );
-  PGresult* viewColumnResult = PQexec( connectionRO, viewColumnSql.toUtf8() );
+  Result viewColumnResult = connectionRO->PQexec( viewColumnSql );
 
   //find out view definition
   QString viewDefSql = QString( "SELECT definition FROM pg_views WHERE schemaname=%1 AND viewname=%2" )
                        .arg( quotedValue( mSchemaName ) )
                        .arg( quotedValue( mTableName ) );
-  PGresult* viewDefResult = PQexec( connectionRO, viewDefSql.toUtf8() );
+  Result viewDefResult = connectionRO->PQexec( viewDefSql );
   if ( PQntuples( viewDefResult ) < 1 )
   {
-    PQclear( viewDefResult );
     return;
   }
+
   QString viewDefinition( QString::fromUtf8( PQgetvalue( viewDefResult, 0, 0 ) ) );
-  PQclear( viewDefResult );
 
   QString ns, relname, attname_table, attname_view;
   SRC columnInformation;
@@ -1530,7 +1508,7 @@
       if ( s.indexIn( viewDefinition ) != -1 )
       {
         attname_view = s.cap( 1 );
-        qWarning( "original view column name was: " + attname_view.toUtf8() );
+        QgsDebugMsg( QString( "original view column name was: %1" ).arg( attname_view ) );
       }
     }
 
@@ -1538,8 +1516,6 @@
     cols.insert( std::make_pair( attname_view, columnInformation ) );
     QgsDebugMsg( "Inserting into cols (for key " + attname_view + " ): " + columnInformation.schema + "." + columnInformation.relation + "." + columnInformation.column + "." + columnInformation.type );
   }
-  PQclear( viewColumnResult );
-
 }
 
 // Returns the minimum value of an attribute
@@ -1563,9 +1539,8 @@
             .arg( mSchemaTableName )
             .arg( sqlWhereClause );
     }
-    PGresult *rmin = PQexec( connectionRO, sql.toUtf8() );
+    Result rmin = connectionRO->PQexec( sql );
     QString minimumValue = QString::fromUtf8( PQgetvalue( rmin, 0, 0 ) );
-    PQclear( rmin );
     return minimumValue.toDouble();
   }
   catch ( PGFieldNotFound )
@@ -1598,13 +1573,12 @@
             .arg( sqlWhereClause );
     }
 
-    PGresult *res = PQexec( connectionRO, sql.toUtf8() );
+    Result res = connectionRO->PQexec( sql );
     if ( PQresultStatus( res ) == PGRES_TUPLES_OK )
     {
       for ( int i = 0; i < PQntuples( res ); i++ )
         uniqueValues.append( QString::fromUtf8( PQgetvalue( res, i, 0 ) ) );
     }
-    PQclear( res );
   }
   catch ( PGFieldNotFound )
   {
@@ -1633,9 +1607,8 @@
             .arg( mSchemaTableName )
             .arg( sqlWhereClause );
     }
-    PGresult *rmax = PQexec( connectionRO, sql.toUtf8() );
+    Result rmax = connectionRO->PQexec( sql );
     QString maxValue = QString::fromUtf8( PQgetvalue( rmax, 0, 0 ) );
-    PQclear( rmax );
     return maxValue.toDouble();
   }
   catch ( PGFieldNotFound )
@@ -1653,9 +1626,8 @@
         .arg( quotedIdentifier( primaryKey ) )
         .arg( mSchemaTableName );
 
-  PGresult *rmax = PQexec( connectionRO, sql.toUtf8() );
+  Result rmax = connectionRO->PQexec( sql );
   QString maxValue = QString::fromUtf8( PQgetvalue( rmax, 0, 0 ) );
-  PQclear( rmax );
 
   return maxValue.toInt();
 }
@@ -1686,15 +1658,13 @@
 
     QVariant defaultValue( QString::null );
 
-    PGresult* result = PQexec( connectionRO, sql.toUtf8() );
+    Result result = connectionRO->PQexec( sql );
 
     if ( PQntuples( result ) == 1 && !PQgetisnull( result, 0, 0 ) )
       defaultValue = QString::fromUtf8( PQgetvalue( result, 0, 0 ) );
 
     // QgsDebugMsg( QString("defaultValue for %1 is NULL: %2").arg(fieldId).arg( defaultValue.isNull() ) );
 
-    PQclear( result );
-
     return defaultValue;
   }
   catch ( PGFieldNotFound )
@@ -1706,24 +1676,22 @@
 /**
  * Check to see if GEOS is available
  */
-bool QgsPostgresProvider::hasGEOS( PGconn *connection )
+bool QgsPostgresProvider::Conn::hasGEOS()
 {
   // make sure info is up to date for the current connection
-  postgisVersion( connection );
+  postgisVersion();
   // get geos capability
   return geosAvailable;
 }
 
 /* Functions for determining available features in postGIS */
-QString QgsPostgresProvider::postgisVersion( PGconn *connection )
+QString QgsPostgresProvider::Conn::postgisVersion()
 {
-  PGresult *result = PQexec( connection, QString( "select postgis_version()" ).toUtf8() );
+  Result result = PQexec( "select postgis_version()" );
   postgisVersionInfo = QString::fromUtf8( PQgetvalue( result, 0, 0 ) );
 
   QgsDebugMsg( "PostGIS version info: " + postgisVersionInfo );
 
-  PQclear( result );
-
   QStringList postgisParts = postgisVersionInfo.split( " ", QString::SkipEmptyParts );
 
   // Get major and minor version
@@ -1754,7 +1722,7 @@
     projAvailable = ( proj[0].indexOf( "=1" ) > -1 );
   }
 
-  useWkbHex = postgisVersionMajor < 1;
+  mUseWkbHex = postgisVersionMajor < 1;
 
   gotPostgisVersion = true;
 
@@ -1768,16 +1736,14 @@
 
   if ( fieldValue == defaultValue && !defaultValue.isNull() )
   {
-    PGresult *result = PQexec( connectionRO, QString( "select %1" ).arg( defaultValue ).toUtf8() );
+    Result result = connectionRO->PQexec( QString( "select %1" ).arg( defaultValue ) );
     if ( PQgetisnull( result, 0, 0 ) )
     {
-      PQclear( result );
       return QByteArray( 0 );  // QByteArray(0).isNull() is true
     }
     else
     {
       QString val = QString::fromUtf8( PQgetvalue( result, 0, 0 ) );
-      PQclear( result );
       return val.toUtf8();
     }
   }
@@ -1797,7 +1763,7 @@
 
   try
   {
-    PQexecNR( connectionRW, QString( "BEGIN" ).toUtf8() );
+    connectionRW->PQexecNR( "BEGIN" );
 
     // Prepare the INSERT statement
     QString insert = QString( "INSERT INTO %1(%2,%3" )
@@ -1805,7 +1771,7 @@
                      .arg( quotedIdentifier( geometryColumn ) )
                      .arg( quotedIdentifier( primaryKey ) ),
                      values = QString( ") VALUES (GeomFromWKB($1%1,%2),$2" )
-                              .arg( useWkbHex ? "" : "::bytea" )
+                              .arg( connectionRW->useWkbHex() ? "" : "::bytea" )
                               .arg( srid );
 
     const QgsAttributeMap &attributevec = flist[0].attributeMap();
@@ -1875,14 +1841,12 @@
     insert += values + ")";
 
     QgsDebugMsg( QString( "prepare addfeatures: %1" ).arg( insert ) );
-    PGresult *stmt = PQprepare( connectionRW, "addfeatures", insert.toUtf8(), fieldId.size() + 2, NULL );
+    PGresult *stmt = connectionRW->PQprepare( "addfeatures", insert, fieldId.size() + 2, NULL );
     if ( stmt == 0 || PQresultStatus( stmt ) == PGRES_FATAL_ERROR )
       throw PGException( stmt );
-
     PQclear( stmt );
 
     int primaryKeyHighWater = maxPrimaryKeyValue();
-    const char **param = new const char *[ fieldId.size()+2 ];
 
     for ( QgsFeatureList::iterator features = flist.begin(); features != flist.end(); features++ )
     {
@@ -1891,41 +1855,28 @@
       QString geomParam;
       appendGeomString( features->geometry(), geomParam );
 
-      QList<QByteArray> qparam;
-      qparam.append( geomParam.toUtf8() );
-      qparam.append( QString( "%1" ).arg( ++primaryKeyHighWater ).toUtf8() );
+      QStringList params;
+      params << geomParam;
+      params << QString( "%1" ).arg( ++primaryKeyHighWater );
       features->setFeatureId( primaryKeyHighWater );
-      param[0] = qparam[0];
-      param[1] = qparam[1];
 
       for ( int i = 0; i < fieldId.size(); i++ )
-      {
-        qparam.append( paramValue( attributevec[ fieldId[i] ].toString(), defaultValue[i] ) );
-        if ( qparam[i+2].isNull() )
-          param[i+2] = 0;
-        else
-          param[i+2] = qparam[i+2];
-      }
+        params << paramValue( attributevec[ fieldId[i] ].toString(), defaultValue[i] );
 
-      PGresult *result = PQexecPrepared( connectionRW, "addfeatures", fieldId.size() + 2, param, NULL, NULL, 0 );
+      PGresult *result = connectionRW->PQexecPrepared( "addfeatures", params );
       if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
-      {
-        delete param;
         throw PGException( result );
-      }
-
       PQclear( result );
     }
 
-    PQexecNR( connectionRW, QString( "DEALLOCATE addfeatures" ).toUtf8() );
-    PQexecNR( connectionRW, QString( "COMMIT" ).toUtf8() );
-    delete param;
+    connectionRW->PQexecNR( "DEALLOCATE addfeatures" );
+    connectionRW->PQexecNR( "COMMIT" );
   }
   catch ( PGException &e )
   {
     e.showErrorMessage( tr( "Error while adding features" ) );
-    PQexecNR( connectionRW, QString( "DEALLOCATE addfeatures" ).toUtf8() );
-    PQexecNR( connectionRW, QString( "ROLLBACK" ).toUtf8() );
+    connectionRW->PQexecNR( "DEALLOCATE addfeatures" );
+    connectionRW->PQexecNR( "ROLLBACK" );
     returnvalue = false;
   }
 
@@ -1942,7 +1893,7 @@
 
   try
   {
-    PQexecNR( connectionRW, QString( "BEGIN" ).toUtf8() );
+    connectionRW->PQexecNR( "BEGIN" );
 
     for ( QgsFeatureIds::const_iterator it = id.begin();it != id.end();++it )
     {
@@ -1953,18 +1904,18 @@
       QgsDebugMsg( "delete sql: " + sql );
 
       //send DELETE statement and do error handling
-      PGresult* result = PQexec( connectionRW, sql.toUtf8() );
+      PGresult *result = connectionRW->PQexec( sql );
       if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
         throw PGException( result );
       PQclear( result );
     }
 
-    PQexecNR( connectionRW, QString( "COMMIT" ).toUtf8() );
+    connectionRW->PQexecNR( "COMMIT" );
   }
   catch ( PGException &e )
   {
     e.showErrorMessage( tr( "Error while deleting features" ) );
-    PQexecNR( connectionRW, QString( "ROLLBACK" ).toUtf8() );
+    connectionRW->PQexecNR( "ROLLBACK" );
     returnvalue = false;
   }
   reset();
@@ -1980,7 +1931,7 @@
 
   try
   {
-    PQexecNR( connectionRW, QString( "BEGIN" ).toUtf8() );
+    connectionRW->PQexecNR( "BEGIN" );
 
     for ( QgsNewAttributesMap::const_iterator iter = name.begin();iter != name.end();++iter )
     {
@@ -1991,18 +1942,18 @@
       QgsDebugMsg( sql );
 
       //send sql statement and do error handling
-      PGresult* result = PQexec( connectionRW, sql.toUtf8() );
+      PGresult *result = connectionRW->PQexec( sql );
       if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
         throw PGException( result );
       PQclear( result );
     }
 
-    PQexecNR( connectionRW, QString( "COMMIT" ).toUtf8() );
+    connectionRW->PQexecNR( "COMMIT" );
   }
   catch ( PGException &e )
   {
     e.showErrorMessage( tr( "Error while adding attributes" ) );
-    PQexecNR( connectionRW, QString( "ROLLBACK" ).toUtf8() );
+    connectionRW->PQexecNR( "ROLLBACK" );
     returnvalue = false;
   }
 
@@ -2019,7 +1970,7 @@
 
   try
   {
-    PQexecNR( connectionRW, QString( "BEGIN" ).toUtf8() );
+    connectionRW->PQexecNR( "BEGIN" );
 
     for ( QgsAttributeIds::const_iterator iter = ids.begin();iter != ids.end();++iter )
     {
@@ -2033,7 +1984,7 @@
                     .arg( quotedIdentifier( column ) );
 
       //send sql statement and do error handling
-      PGresult* result = PQexec( connectionRW, sql.toUtf8() );
+      PGresult *result = connectionRW->PQexec( sql );
       if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
         throw PGException( result );
       PQclear( result );
@@ -2042,12 +1993,12 @@
       attributeFields.remove( *iter );
     }
 
-    PQexecNR( connectionRW, QString( "COMMIT" ).toUtf8() );
+    connectionRW->PQexecNR( "COMMIT" );
   }
   catch ( PGException &e )
   {
     e.showErrorMessage( tr( "Error while deleting attributes" ) );
-    PQexecNR( connectionRW, QString( "ROLLBACK" ).toUtf8() );
+    connectionRW->PQexecNR( "ROLLBACK" );
     returnvalue = false;
   }
 
@@ -2064,7 +2015,7 @@
 
   try
   {
-    PQexecNR( connectionRW, QString( "BEGIN" ).toUtf8() );
+    connectionRW->PQexecNR( "BEGIN" );
 
     // cycle through the features
     for ( QgsChangedAttributesMap::const_iterator iter = attr_map.begin();iter != attr_map.end();++iter )
@@ -2106,19 +2057,18 @@
              .arg( quotedIdentifier( primaryKey ) )
              .arg( fid );
 
-      PGresult* result = PQexec( connectionRW, sql.toUtf8() );
+      PGresult *result = connectionRW->PQexec( sql );
       if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
         throw PGException( result );
-
       PQclear( result );
     }
 
-    PQexecNR( connectionRW, QString( "COMMIT" ).toUtf8() );
+    connectionRW->PQexecNR( "COMMIT" );
   }
   catch ( PGException &e )
   {
     e.showErrorMessage( tr( "Error while changing attributes" ) );
-    PQexecNR( connectionRW, QString( "ROLLBACK" ).toUtf8() );
+    connectionRW->PQexecNR( "ROLLBACK" );
     returnvalue = false;
   }
 
@@ -2132,7 +2082,7 @@
   unsigned char *buf = geom->wkbBuffer();
   for ( uint i = 0; i < geom->wkbSize(); ++i )
   {
-    if ( useWkbHex )
+    if ( connectionRW->useWkbHex() )
       geomString += QString( "%1" ).arg(( int ) buf[i], 2, 16, QChar( '0' ) );
     else
       geomString += QString( "\\%1" ).arg(( int ) buf[i], 3, 8, QChar( '0' ) );
@@ -2151,19 +2101,18 @@
   try
   {
     // Start the PostGIS transaction
-    PQexecNR( connectionRW, QString( "BEGIN" ).toUtf8() );
+    connectionRW->PQexecNR( "BEGIN" );
 
     QString update = QString( "UPDATE %1 SET %2=GeomFromWKB($1%3,%4) WHERE %5=$2" )
                      .arg( mSchemaTableName )
                      .arg( quotedIdentifier( geometryColumn ) )
-                     .arg( useWkbHex ? "" : "::bytea" )
+                     .arg( connectionRW->useWkbHex() ? "" : "::bytea" )
                      .arg( srid )
                      .arg( quotedIdentifier( primaryKey ) );
 
-    PGresult *stmt = PQprepare( connectionRW, "updatefeatures", update.toUtf8(), 2, NULL );
+    PGresult *stmt = connectionRW->PQprepare( "updatefeatures", update, 2, NULL );
     if ( stmt == 0 || PQresultStatus( stmt ) == PGRES_FATAL_ERROR )
       throw PGException( stmt );
-
     PQclear( stmt );
 
     for ( QgsGeometryMap::iterator iter  = geometry_map.begin();
@@ -2180,31 +2129,26 @@
         QString geomParam;
         appendGeomString( &*iter, geomParam );
 
-        QList<QByteArray> qparam;
-        qparam.append( geomParam.toUtf8() );
-        qparam.append( QString( "%1" ).arg( iter.key() ).toUtf8() );
+        QStringList params;
+        params << geomParam;
+        params << QString( "%1" ).arg( iter.key() );
 
-        const char *param[2];
-        param[0] = qparam[0];
-        param[1] = qparam[1];
-
-        PGresult *result = PQexecPrepared( connectionRW, "updatefeatures", 2, param, NULL, NULL, 0 );
+        PGresult *result = connectionRW->PQexecPrepared( "updatefeatures", params );
         if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
           throw PGException( result );
-
         PQclear( result );
       } // if (*iter)
 
     } // for each feature
 
-    PQexecNR( connectionRW, QString( "DEALLOCATE updatefeatures" ).toUtf8() );
-    PQexecNR( connectionRW, QString( "COMMIT" ).toUtf8() );
+    connectionRW->PQexecNR( "DEALLOCATE updatefeatures" );
+    connectionRW->PQexecNR( "COMMIT" );
   }
   catch ( PGException &e )
   {
     e.showErrorMessage( tr( "Error while changing geometry values" ) );
-    PQexecNR( connectionRW, QString( "DEALLOCATE updatefeatures" ).toUtf8() );
-    PQexecNR( connectionRW, QString( "ROLLBACK" ).toUtf8() );
+    connectionRW->PQexecNR( "DEALLOCATE updatefeatures" );
+    connectionRW->PQexecNR( "ROLLBACK" );
     returnvalue = false;
   }
 
@@ -2264,13 +2208,12 @@
   }
 #endif
 
-  PGresult *result = PQexec( connectionRO, sql.toUtf8() );
+  Result result = connectionRO->PQexec( sql );
 
   QgsDebugMsg( "Approximate Number of features as text: " +
                QString::fromUtf8( PQgetvalue( result, 0, 0 ) ) );
 
   numberFeatures = QString::fromUtf8( PQgetvalue( result, 0, 0 ) ).toLong();
-  PQclear( result );
 
   QgsDebugMsg( "Approximate Number of features: " + QString::number( numberFeatures ) );
 
@@ -2307,13 +2250,12 @@
 
                                    QgsDebugMsg( "Getting approximate extent using: '" + sql + "'" );
 
-                                   PGresult *result = PQexec( connectionRO, sql.toUtf8() );
+                                   Result result = connectionRO->PQexec( sql );
 
                                    // TODO: Guard against the result having no rows
-
                                    for ( int i = 0; i < PQntuples( result ); i++ )
 {
-  std::string box3d = PQgetvalue( result, i, 0 );
+  QString box3d = PQgetvalue( result, i, 0 );
 
     if ( 0 == i )
     {
@@ -2324,28 +2266,40 @@
     {
       // extend the initial extent
       QgsPostGisBox3d b = QgsPostGisBox3d( box3d );
-
       layerExtent.combineExtentWith( &b );
     }
 
-    QgsDebugMsg( "After row " + QString::number( i ) +
-                 ", extent is: " + layerExtent.toString() );
+    QgsDebugMsg( QString( "After row %1, extent is %2" ).arg( i ).arg( layerExtent.toString() ) );
   }
 
-  // clear query result
-  PQclear( result );
-
 #else // non-postgresql threads version
+  QString sql;
+  Result result;
+  QString ext;
 
   // get the extents
+  if ( sqlWhereClause.isEmpty() )
+  {
+    result = connectionRO->PQexec( QString( "select estimated_extent(%1,%2,%3)" )
+                                   .arg( quotedValue( mSchemaName ) )
+                                   .arg( quotedValue( mTableName ) )
+                                   .arg( quotedValue( geometryColumn ) ) );
+    if ( PQntuples( result ) == 1 )
+      ext = PQgetvalue( result, 0, 0 );
+  }
 
-  QString sql = QString( "select extent(%1) from %2" )
-                .arg( quotedIdentifier( geometryColumn ) )
-                .arg( mSchemaTableName );
-
-  if ( sqlWhereClause.length() > 0 )
+  if ( ext.isEmpty() )
   {
-    sql += " where " + sqlWhereClause;
+    sql = QString( "select extent(%1) from %2" )
+          .arg( quotedIdentifier( geometryColumn ) )
+          .arg( mSchemaTableName );
+
+    if ( !sqlWhereClause.isEmpty() )
+      sql += QString( "where %1" ).arg( sqlWhereClause );
+
+    result = connectionRO->PQexec( sql );
+    if ( PQntuples( result ) == 1 )
+      ext = PQgetvalue( result, 0, 0 );
   }
 
 #if WASTE_TIME
@@ -2359,47 +2313,20 @@
 
   QgsDebugMsg( "Getting extents using schema.table: " + sql );
 
-  PGresult *result = PQexec( connectionRO, sql.toUtf8() );
-  if ( PQntuples( result ) > 0 )
+  QRegExp rx( "\\((.+) (.+),(.+) (.+)\\)" );
+  if ( ext.contains( rx ) )
   {
-    std::string box3d = PQgetvalue( result, 0, 0 );
+    QStringList ex = rx.capturedTexts();
 
-    if ( box3d != "" )
-    {
-      std::string s;
-
-      box3d = box3d.substr( box3d.find_first_of( "(" ) + 1 );
-      box3d = box3d.substr( box3d.find_first_not_of( " " ) );
-      s = box3d.substr( 0, box3d.find_first_of( " " ) );
-      double minx = strtod( s.c_str(), NULL );
-
-      box3d = box3d.substr( box3d.find_first_of( " " ) + 1 );
-      s = box3d.substr( 0, box3d.find_first_of( " " ) );
-      double miny = strtod( s.c_str(), NULL );
-
-      box3d = box3d.substr( box3d.find_first_of( "," ) + 1 );
-      box3d = box3d.substr( box3d.find_first_not_of( " " ) );
-      s = box3d.substr( 0, box3d.find_first_of( " " ) );
-      double maxx = strtod( s.c_str(), NULL );
-
-      box3d = box3d.substr( box3d.find_first_of( " " ) + 1 );
-      s = box3d.substr( 0, box3d.find_first_of( " " ) );
-      double maxy = strtod( s.c_str(), NULL );
-
-      layerExtent.setXMaximum( maxx );
-      layerExtent.setXMinimum( minx );
-      layerExtent.setYMaximum( maxy );
-      layerExtent.setYMinimum( miny );
-    }
+    layerExtent.setXMinimum( ex[1].toDouble() );
+    layerExtent.setYMinimum( ex[2].toDouble() );
+    layerExtent.setXMaximum( ex[3].toDouble() );
+    layerExtent.setYMaximum( ex[4].toDouble() );
   }
   else
   {
     QgsDebugMsg( "extents query failed" );
   }
-
-  // clear query result
-  PQclear( result );
-
 #endif
 
 #if 0
@@ -2425,9 +2352,9 @@
 {
   QgsDebugMsg( "received a custom event " + QString::number( e->type() ) );
 
-  switch ( e->type() )
+  switch (( int ) e->type() )
   {
-    case( QEvent::Type ) QGis::ProviderExtentCalcEvent:
+    case QGis::ProviderExtentCalcEvent:
 
       QgsDebugMsg( "extent has been calculated" );
 
@@ -2455,7 +2382,7 @@
 
       break;
 
-    case( QEvent::Type ) QGis::ProviderCountCalcEvent:
+    case QGis::ProviderCountCalcEvent:
 
       QgsDebugMsg( "count has been calculated" );
 
@@ -2483,23 +2410,18 @@
   // return data in the endian of the server
 
   QString firstOid = QString( "select regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
-  PGresult * oidResult = PQexec( connectionRO, firstOid.toUtf8() );
+  Result oidResult = connectionRO->PQexec( firstOid );
   // get the int value from a "normal" select
   QString oidValue = QString::fromUtf8( PQgetvalue( oidResult, 0, 0 ) );
-  PQclear( oidResult );
 
   QgsDebugMsg( "Creating binary cursor" );
 
   // get the same value using a binary cursor
+  connectionRO->openCursor( "oidcursor", QString( "select regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) ) );
 
-  QString oidDeclare = QString( "declare oidcursor binary cursor for select regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
-  // set up the cursor
-  PQexecNR( connectionRO, oidDeclare.toUtf8() );
-  QString fetch = "fetch forward 1 from oidcursor";
-
   QgsDebugMsg( "Fetching a record and attempting to get check endian-ness" );
 
-  PGresult *fResult = PQexec( connectionRO, fetch.toUtf8() );
+  Result fResult = connectionRO->PQexec( "fetch forward 1 from oidcursor" );
   swapEndian = true;
   if ( PQntuples( fResult ) > 0 )
   {
@@ -2513,10 +2435,8 @@
     // compare the two oid values to determine if we need to do an endian swap
     if ( oid == oidValue.toInt() )
       swapEndian = false;
-
-    PQclear( fResult );
   }
-  PQexecNR( connectionRO, QString( "close oidcursor" ).toUtf8() );
+  connectionRO->closeCursor( "oidcursor" );
   return swapEndian;
 }
 
@@ -2535,7 +2455,7 @@
 
   QgsDebugMsg( "Getting geometry column: " + sql );
 
-  PGresult *result = executeDbCommand( connectionRO, sql );
+  Result result = connectionRO->PQexec( sql );
 
   QgsDebugMsg( "geometry column query returned " + QString::number( PQntuples( result ) ) );
 
@@ -2543,14 +2463,12 @@
   {
     fType = QString::fromUtf8( PQgetvalue( result, 0, 0 ) );
     srid = QString::fromUtf8( PQgetvalue( result, 0, 1 ) );
-    PQclear( result );
   }
   else
   {
     // Didn't find what we need in the geometry_columns table, so
     // get stuff from the relevant column instead. This may (will?)
     // fail if there is no data in the relevant table.
-    PQclear( result ); // for the query just before the if() statement
     sql = QString( "select srid(%1),geometrytype(%1) from %2" )
           .arg( quotedIdentifier( geometryColumn ) )
           .arg( mSchemaTableName );
@@ -2563,14 +2481,13 @@
 
     sql += " limit 1";
 
-    result = executeDbCommand( connectionRO, sql );
+    result = connectionRO->PQexec( sql );
 
     if ( PQntuples( result ) > 0 )
     {
       srid = QString::fromUtf8( PQgetvalue( result, 0, 0 ) );
       fType = QString::fromUtf8( PQgetvalue( result, 0, 1 ) );
     }
-    PQclear( result );
   }
 
   if ( !srid.isEmpty() && !fType.isEmpty() )
@@ -2589,13 +2506,12 @@
       if ( mUri.sql() != "" )
         sql += " where " + mUri.sql();
 
-      result = executeDbCommand( connectionRO, sql );
+      result = connectionRO->PQexec( sql );
 
       if ( PQntuples( result ) == 1 )
       {
         fType = QString::fromUtf8( PQgetvalue( result, 0, 0 ) );
       }
-      PQclear( result );
     }
     if ( fType == "POINT" )
     {
@@ -2654,24 +2570,6 @@
   return valid;
 }
 
-PGresult* QgsPostgresProvider::executeDbCommand( PGconn* connection,
-    const QString& sql )
-{
-  PGresult *result = PQexec( connection, sql.toUtf8() );
-
-  QgsDebugMsg( "Executed SQL: " + sql );
-  if ( PQresultStatus( result ) == PGRES_TUPLES_OK )
-  {
-    QgsDebugMsg( "Command was successful." );
-  }
-  else
-  {
-    QgsDebugMsg( "Command was unsuccessful. The error message was: "
-                 + QString::fromUtf8( PQresultErrorMessage( result ) ) );
-  }
-  return result;
-}
-
 QString QgsPostgresProvider::quotedIdentifier( QString ident ) const
 {
   ident.replace( '"', "\"\"" );
@@ -2688,9 +2586,10 @@
   return value.prepend( "'" ).append( "'" );
 }
 
-PGresult *QgsPostgresProvider::PQexec( PGconn *conn, const char *query )
+PGresult *QgsPostgresProvider::Conn::PQexec( QString query )
 {
-  PGresult *res = ::PQexec( conn, query );
+  QgsDebugMsgLevel( QString( "Executing SQL: %1" ).arg( query ), 3 );
+  PGresult *res = ::PQexec( conn, query.toUtf8() );
 
 #ifdef QGISDEBUG
   if ( res )
@@ -2710,9 +2609,33 @@
   return res;
 }
 
-bool QgsPostgresProvider::PQexecNR( PGconn *conn, const char *query )
+bool QgsPostgresProvider::Conn::openCursor( QString cursorName, QString sql )
 {
-  PGresult *res = ::PQexec( conn, query );
+  if ( openCursors++ == 0 )
+  {
+    QgsDebugMsg( "Starting read-only transaction" );
+    PQexecNR( "BEGIN READ ONLY" );
+  }
+  QgsDebugMsgLevel( QString( "Binary cursor %1 for %2" ).arg( cursorName ).arg( sql ), 3 );
+  return PQexecNR( QString( "declare %1 binary cursor for %2" ).arg( cursorName ).arg( sql ) );
+}
+
+bool QgsPostgresProvider::Conn::closeCursor( QString cursorName )
+{
+  bool res = PQexecNR( QString( "CLOSE %1" ).arg( cursorName ) );
+
+  if ( --openCursors == 0 )
+  {
+    QgsDebugMsg( "Commiting read-only transaction" );
+    PQexecNR( "COMMIT" );
+  }
+
+  return res;
+}
+
+bool QgsPostgresProvider::Conn::PQexecNR( QString query )
+{
+  Result res = ::PQexec( conn, query.toUtf8() );
   if ( res )
   {
     int errorStatus = PQresultStatus( res );
@@ -2726,7 +2649,6 @@
       QgsDebugMsgLevel( err, 3 );
     }
 #endif
-    PQclear( res );
     return errorStatus == PGRES_COMMAND_OK;
   }
   else
@@ -2736,6 +2658,48 @@
   return false;
 }
 
+PGresult *QgsPostgresProvider::Conn::PQgetResult()
+{
+  return ::PQgetResult( conn );
+}
+
+PGresult *QgsPostgresProvider::Conn::PQprepare( QString stmtName, QString query, int nParams, const Oid *paramTypes )
+{
+  return ::PQprepare( conn, stmtName.toUtf8(), query.toUtf8(), nParams, paramTypes );
+}
+
+PGresult *QgsPostgresProvider::Conn::PQexecPrepared( QString stmtName, const QStringList &params )
+{
+  const char **param = new const char *[ params.size()];
+  QList<QByteArray> qparam;
+
+  for ( int i = 0; i < params.size(); i++ )
+  {
+    qparam << params[i].toUtf8();
+
+    if ( params[i].isNull() )
+      param[i] = 0;
+    else
+      param[i] = qparam[i];
+  }
+
+  PGresult *res = ::PQexecPrepared( conn, stmtName.toUtf8(), params.size(), param, NULL, NULL, 0 );
+
+  delete [] param;
+
+  return res;
+}
+
+void QgsPostgresProvider::Conn::PQfinish()
+{
+  ::PQfinish( conn );
+}
+
+int QgsPostgresProvider::Conn::PQsendQuery( QString query )
+{
+  return ::PQsendQuery( conn, query.toUtf8() );
+}
+
 void QgsPostgresProvider::showMessageBox( const QString& title, const QString& text )
 {
   QgsMessageOutput* message = QgsMessageOutput::createMessageOutput();
@@ -2766,7 +2730,7 @@
 PGconn * QgsPostgresProvider::pgConnection()
 {
   connectRW();
-  return connectionRW;
+  return connectionRW->pgConnection();
 }
 
 QString QgsPostgresProvider::getTableName()

Modified: trunk/qgis/src/providers/postgres/qgspostgresprovider.h
===================================================================
--- trunk/qgis/src/providers/postgres/qgspostgresprovider.h	2008-08-27 07:44:03 UTC (rev 9182)
+++ trunk/qgis/src/providers/postgres/qgspostgresprovider.h	2008-08-27 08:00:57 UTC (rev 9183)
@@ -197,18 +197,6 @@
 
     QgsAttributeList allAttributesList();
 
-    //! get postgis version string
-    QString postgisVersion( PGconn * );
-
-    //! get status of GEOS capability
-    bool hasGEOS( PGconn * );
-
-    //! get status of GIST capability
-    bool hasGIST( PGconn * );
-
-    //! get status of PROJ4 capability
-    bool hasPROJ( PGconn * );
-
     /**Returns the default value for field specified by @c fieldId */
     QVariant getDefaultValue( int fieldId );
 
@@ -401,22 +389,7 @@
      * Geometry type
      */
     QGis::WKBTYPE geomType;
-    /**
-     * Connection pointer
-     */
-    PGconn *connectionRO;
-    PGconn *connectionRW;
 
-    bool connectRW()
-    {
-      if ( connectionRW )
-        return connectionRW;
-
-      connectionRW = connectDb( mUri.connInfo(), false );
-
-      return connectionRW;
-    }
-
     /**
      * Spatial reference id of the layer
      */
@@ -457,8 +430,6 @@
     bool deduceEndian();
     bool getGeometryDetails();
 
-    PGresult* executeDbCommand( PGconn* connection, const QString& sql );
-
     // Produces a QMessageBox with the given title and text. Doesn't
     // return until the user has dismissed the dialog box.
     static void showMessageBox( const QString& title, const QString& text );
@@ -556,27 +527,6 @@
     int SRCFromViewColumn( const QString& ns, const QString& relname, const QString& attname_table,
                            const QString& attname_view, const QString& viewDefinition, SRC& result ) const;
 
-    //! PostGIS version string
-    QString postgisVersionInfo;
-
-    //! Are postgisVersionMajor, postgisVersionMinor, geosAvailable, gistAvailable, projAvailable valid?
-    bool gotPostgisVersion;
-
-    //! PostGIS major version
-    int postgisVersionMajor;
-
-    //! PostGIS minor version
-    int postgisVersionMinor;
-
-    //! GEOS capability
-    bool geosAvailable;
-
-    //! GIST capability
-    bool gistAvailable;
-
-    //! PROJ4 capability
-    bool projAvailable;
-
     int enabledCapabilities;
 
     /**Returns the maximum value of the primary key attribute
@@ -597,29 +547,129 @@
      */
     void customEvent( QEvent *e );
 
-    PGconn *connectDb( const QString &conninfo, bool readonly = true );
-    void disconnectDb();
-
-    bool useWkbHex;
-
     void appendGeomString( QgsGeometry *geom, QString &geomParam ) const;
     QByteArray paramValue( QString fieldvalue, const QString &defaultValue ) const;
 
-    // run a query and check for errors
-    static PGresult *PQexec( PGconn *conn, const char *query );
+    class Conn
+    {
+      public:
+        Conn( PGconn *connection ) :
+            ref( 1 ),
+            openCursors( 0 ),
+            conn( connection ),
+            gotPostgisVersion( false )
+        {
+        }
 
-    // run a query and free result buffer
-    static bool PQexecNR( PGconn *conn, const char *query );
+        //! get postgis version string
+        QString postgisVersion();
 
-    struct Conn
+        //! get status of GEOS capability
+        bool hasGEOS();
+
+        //! get status of GIST capability
+        bool hasGIST();
+
+        //! get status of PROJ4 capability
+        bool hasPROJ();
+
+        //! encode wkb in hex
+        bool useWkbHex() { return mUseWkbHex; }
+
+        //! major PostgreSQL version
+        int majorVersion() { return postgisVersionMajor; }
+
+        // run a query and free result buffer
+        bool PQexecNR( QString query );
+
+        // cursor handling
+        bool openCursor( QString cursorName, QString declare );
+        bool closeCursor( QString cursorName );
+
+        PGconn *pgConnection() { return conn; }
+
+        //
+        // libpq wrapper
+        //
+
+        // run a query and check for errors
+        PGresult *PQexec( QString query );
+        void PQfinish();
+        int PQsendQuery( QString query );
+        PGresult *PQgetResult();
+        PGresult *PQprepare( QString stmtName, QString query, int nParams, const Oid *paramTypes );
+        PGresult *PQexecPrepared( QString stmtName, const QStringList &params );
+
+        static Conn *connectDb( const QString &conninfo, bool readonly );
+        static void disconnectRW( Conn *&conn );
+        static void disconnectRO( Conn *&conn );
+        static void disconnect( QMap<QString, Conn *> &connections, Conn *&conn );
+
+      private:
+        int ref;
+        int openCursors;
+        PGconn *conn;
+
+        //! GEOS capability
+        bool geosAvailable;
+
+        //! PostGIS version string
+        QString postgisVersionInfo;
+
+        //! Are postgisVersionMajor, postgisVersionMinor, geosAvailable, gistAvailable, projAvailable valid?
+        bool gotPostgisVersion;
+
+        //! PostGIS major version
+        int postgisVersionMajor;
+
+        //! PostGIS minor version
+        int postgisVersionMinor;
+
+        //! GIST capability
+        bool gistAvailable;
+
+        //! PROJ4 capability
+        bool projAvailable;
+
+        //! encode wkb in hex
+        bool mUseWkbHex;
+
+        static QMap<QString, Conn *> connectionsRW;
+        static QMap<QString, Conn *> connectionsRO;
+    };
+
+    class Result
     {
-      Conn( PGconn *connection ) : ref( 1 ), conn( connection ) {}
+      public:
+        Result( PGresult *theRes = 0 ) : res( theRes ) {}
+        ~Result() { if ( res ) PQclear( res ); }
 
-      int ref;
-      PGconn *conn;
+        operator PGresult *() { return res; }
+
+        Result &operator=( PGresult *theRes ) { if ( res ) PQclear( res ); res = theRes;  return *this; }
+
+      private:
+        PGresult *res;
     };
-    static QMap<QString, Conn *> connectionsRW;
-    static QMap<QString, Conn *> connectionsRO;
+
+    /**
+     * Connection pointers
+     */
+    Conn *connectionRO;
+    Conn *connectionRW;
+
+    bool connectRW()
+    {
+      if ( connectionRW )
+        return connectionRW;
+
+      connectionRW = Conn::connectDb( mUri.connInfo(), false );
+
+      return connectionRW;
+    }
+
+    void disconnectDb();
+
     static int providerIds;
 };
 



More information about the QGIS-commit mailing list