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

svn_qgis at osgeo.org svn_qgis at osgeo.org
Thu Mar 27 19:50:45 EDT 2008


Author: jef
Date: 2008-03-27 19:50:45 -0400 (Thu, 27 Mar 2008)
New Revision: 8290

Modified:
   trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp
   trunk/qgis/src/providers/postgres/qgspostgresprovider.h
Log:
another postgres provider update:
- make declareCursor and getFeature private
- there's no need to track column names in cursors
  (which contained a bug triggering #962 again and more)
- introduce private method field(index) that verifies that
  the index in valid or throws an exception.
- use that in public methods that get indexes from
  outside (minValue, maxValue, uniqueValues...)


Modified: trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp
===================================================================
--- trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp	2008-03-27 14:50:11 UTC (rev 8289)
+++ trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp	2008-03-27 23:50:45 UTC (rev 8290)
@@ -367,108 +367,121 @@
   return "PostgreSQL database with PostGIS extension";
 }
 
-void QgsPostgresProvider::declareCursor(const QString &cursorName,
+bool QgsPostgresProvider::declareCursor(const QString &cursorName,
                                         const QgsAttributeList &fetchAttributes,
                                         bool fetchGeometry,
-                                        QString whereClause,
-                                        QStringList &attributeNames)
+                                        QString whereClause)
 {
-  QString declare = QString("declare %1 binary cursor with hold for select %2")
-                      .arg(cursorName).arg(quotedIdentifier(primaryKey));
-
-  if(fetchGeometry)
+  try
   {
-    declare += QString(",asbinary(%1,'%2') as qgs_feature_geometry")
-                 .arg( quotedIdentifier(geometryColumn) )
-                 .arg( endianString() ); 
-  }
+    QString declare = QString("declare %1 binary cursor with hold for select %2")
+      .arg(cursorName).arg(quotedIdentifier(primaryKey));
 
-  QgsFieldMap attributeMap = fields();
-  for (QgsAttributeList::const_iterator it = fetchAttributes.constBegin(); it != fetchAttributes.constEnd(); ++it)
-  {
-    QgsFieldMap::const_iterator fieldIt = attributeMap.find(*it);
-    if(fieldIt != attributeMap.end())
+    if(fetchGeometry)
     {
-      const QString &fieldname = fieldIt.value().name();
+      declare += QString(",asbinary(%1,'%2') as qgs_feature_geometry")
+        .arg( quotedIdentifier(geometryColumn) )
+        .arg( endianString() ); 
+    }
 
-      if( fieldname != primaryKey ) 
+    for (QgsAttributeList::const_iterator it = fetchAttributes.constBegin(); it != fetchAttributes.constEnd(); ++it)
+    {
+      const QgsField &fld = field(*it);
+
+      const QString &fieldname = fld.name();
+      if( fieldname == primaryKey )
+        continue;
+
+      const QString &type = fld.typeName();
+      if( type == "money" || type.startsWith("_") )
       {
-        if( fieldIt.value().typeName() == "money" || fieldIt.value().typeName().startsWith("_") )
-        {
-          // money and arrays don't support cast to text, but return text
-          // TODO: check other types
-          declare += "," + quotedIdentifier( fieldname );
-        }
-        else if( fieldIt.value().typeName() == "bool" )
-        {
-          // bool doesn't support cast to text either and even doesn't return text.
-          // (even text() doesn't work with binary cursors)
-          declare += QString(",CASE WHEN %1 THEN 't' WHEN NOT %1 THEN 'f' ELSE NULL END AS %1")
-                       .arg( quotedIdentifier(fieldname) );
-        }
-        else
-        {
-          declare += "," + quotedIdentifier( fieldname ) + "::text";
-        }
+        // money and arrays don't support cast to text, but return text
+        // TODO: check other types
+        declare += "," + quotedIdentifier( fieldname );
       }
-
-      attributeNames << fieldname;
+      else if( type == "bool" )
+      {
+        // bool doesn't support cast to text either and even doesn't return text.
+        // (even text() doesn't work with binary cursors)
+        declare += QString(",CASE WHEN %1 THEN 't' WHEN NOT %1 THEN 'f' ELSE NULL END AS %1")
+          .arg( quotedIdentifier(fieldname) );
+      }
+      else
+      {
+        declare += "," + quotedIdentifier( fieldname ) + "::text";
+      }
     }
-  }
 
-  declare += " from " + mSchemaTableName;
-  
-  if( !whereClause.isEmpty() )
-    declare += QString(" where %1").arg(whereClause);
+    declare += " from " + mSchemaTableName;
 
-  QgsDebugMsg("Binary cursor: " + declare);
+    if( !whereClause.isEmpty() )
+      declare += QString(" where %1").arg(whereClause);
 
-  PQexecNR(connection, declare.toUtf8());
+    QgsDebugMsg("Binary cursor: " + declare);
+
+    return PQexecNR(connection, declare.toUtf8());
+  }
+  catch(PGFieldNotFound)
+  {
+    return false;
+  }
 }
 
-void QgsPostgresProvider::getFeature(PGresult *queryResult, int row, bool fetchGeometry,
+bool QgsPostgresProvider::getFeature(PGresult *queryResult, int row, bool fetchGeometry,
                                      QgsFeature &feature,
-                                     const QStringList &attributeNames,
                                      const QgsAttributeList &fetchAttributes)
 {  
-  int oid = *(int *)PQgetvalue(queryResult, row, PQfnumber(queryResult,quotedIdentifier(primaryKey).toUtf8()));
-  if (swapEndian)
-    oid = ntohl(oid); // convert oid to opposite endian
+  try
+  {
+    int oid = *(int *)PQgetvalue(queryResult, row, 0);
+    if (swapEndian)
+      oid = ntohl(oid); // convert oid to opposite endian
 
-  feature.setFeatureId(oid);
+    feature.setFeatureId(oid);
 
-  // fetch attributes
-  QgsAttributeList::const_iterator it = fetchAttributes.constBegin();
-  for(QStringList::const_iterator namesIt = attributeNames.begin(); namesIt != attributeNames.end(); ++namesIt, ++it)
-  {
-    QString val;
+    int col;  // first attribute column
 
-    if( (*namesIt) == primaryKey)
+    if (fetchGeometry)
     {
-      val = QString::number(oid);
-    }
-    else
-    {
-      int fn = PQfnumber(queryResult,quotedIdentifier(*namesIt).toUtf8());
-
-      if( !PQgetisnull(queryResult, row, fn) )
+      int returnedLength = PQgetlength(queryResult, row, 1); 
+      if(returnedLength > 0)
       {
-        val = QString::fromUtf8(PQgetvalue(queryResult, row, fn));
+        unsigned char *featureGeom = new unsigned char[returnedLength + 1];
+        memset(featureGeom, '\0', returnedLength + 1);
+        memcpy(featureGeom, PQgetvalue(queryResult, row, PQfnumber(queryResult,QString("qgs_feature_geometry").toUtf8())), returnedLength); 
+        feature.setGeometryAndOwnership(featureGeom, returnedLength + 1);
       }
       else
       {
-        val = QString::null;
+        feature.setGeometryAndOwnership(0, 0);
+        QgsDebugMsg("Couldn't get the feature geometry in binary form");
       }
-    }
 
-    if( val.isNull() )
-    {
-      feature.addAttribute(*it, val);
+      col = 2;
     }
     else
     {
-      switch (attributeFields[*it].type())
+      col = 1;
+    }
+
+    // iterate attributes
+    for(QgsAttributeList::const_iterator it=fetchAttributes.constBegin(); it!=fetchAttributes.constEnd(); it++)
+    {
+      const QgsField &fld = field(*it);
+
+      if( fld.name() == primaryKey )
       {
+        // primary key was already processed
+        feature.addAttribute(*it, QString::number(oid) );
+        continue;
+      }
+
+      if( !PQgetisnull(queryResult, row, col) )
+      {
+        QString val = QString::fromUtf8(PQgetvalue(queryResult, row, col));
+
+        switch (fld.type())
+        {
         case QVariant::LongLong:
           feature.addAttribute(*it, val.toLongLong());
           break;
@@ -483,26 +496,22 @@
           break;
         default:
           assert(0 && "unsupported field type");
+        }
       }
+      else
+      {
+        feature.addAttribute(*it, QVariant(QString::null));
+      }
+
+      col++;
     }
+
+    return true;
   }
-
-  if (fetchGeometry)
+  catch(PGFieldNotFound)
   {
-    int returnedLength = PQgetlength(queryResult, row, PQfnumber(queryResult,"qgs_feature_geometry")); 
-    if(returnedLength > 0)
-    {
-      unsigned char *featureGeom = new unsigned char[returnedLength + 1];
-      memset(featureGeom, '\0', returnedLength + 1);
-      memcpy(featureGeom, PQgetvalue(queryResult, row, PQfnumber(queryResult,QString("qgs_feature_geometry").toUtf8())), returnedLength); 
-      feature.setGeometryAndOwnership(featureGeom, returnedLength + 1);
-    }
-    else
-    {
-      feature.setGeometryAndOwnership(0, 0);
-      QgsDebugMsg("Couldn't get the feature geometry in binary form");
-    }
-  } 
+    return false;
+  }
 }
 
 void QgsPostgresProvider::select(QgsAttributeList fetchAttributes, QgsRect rect, bool fetchGeometry, bool useIntersect)
@@ -552,8 +561,9 @@
 
   mFetchGeom = fetchGeometry;
   mAttributesToFetch = fetchAttributes;
-  declareCursor( cursorName, fetchAttributes, fetchGeometry, whereClause, mFetchAttributeNames );
-
+  if( !declareCursor( cursorName, fetchAttributes, fetchGeometry, whereClause ) )
+    return;
+  
   mFetching = true;
   mFirstFetch = true;
 }
@@ -594,7 +604,7 @@
     for (int row = 0; row < rows; row++)
     {
       mFeatureQueue.push(QgsFeature());
-      getFeature(queryResult, row, mFetchGeom, mFeatureQueue.back(), mFetchAttributeNames, mAttributesToFetch);
+      getFeature(queryResult, row, mFetchGeom, mFeatureQueue.back(), mAttributesToFetch);
     } // for each row in queue
 
     PQclear(queryResult);
@@ -625,9 +635,9 @@
 
 bool QgsPostgresProvider::getFeatureAtId(int featureId, QgsFeature& feature, bool fetchGeometry, QgsAttributeList fetchAttributes)
 {
-  QStringList attributeNames;
   QString cursorName = QString("qgisfid%1").arg(providerId);
-  declareCursor( cursorName, fetchAttributes, fetchGeometry, QString("%2=%3").arg(quotedIdentifier(primaryKey)).arg(featureId), attributeNames );
+  if( !declareCursor( cursorName, fetchAttributes, fetchGeometry, QString("%2=%3").arg(quotedIdentifier(primaryKey)).arg(featureId) ) )
+    return false;
 
   PGresult *queryResult = PQexec(connection, QString("fetch forward 1 from %1").arg(cursorName).toUtf8());
   if(queryResult==0)
@@ -646,12 +656,13 @@
     QgsDebugMsg( QString("found %1 features instead of just one.").arg(rows) );
   }
 
-  getFeature(queryResult, 0, fetchGeometry, feature, attributeNames, fetchAttributes);
+  bool gotit = getFeature(queryResult, 0, fetchGeometry, feature, fetchAttributes);
 
   PQclear(queryResult);
 
   PQexecNR(connection, QString("CLOSE %1").arg(cursorName).toUtf8());
-  return true;
+
+  return gotit;
 }
 
 
@@ -690,6 +701,18 @@
   return numberFeatures;
 }
 
+const QgsField &QgsPostgresProvider::field(int index) const
+{
+  QgsFieldMap::const_iterator it = attributeFields.find(index);
+
+  if( it==attributeFields.constEnd() ) {
+    QgsDebugMsg("Field " + QString::number(index) + " not found.");
+    throw PGFieldNotFound();
+  }
+
+  return it.value();
+}
+
 /**
  * Return the number of fields
  */
@@ -1577,83 +1600,103 @@
 // Returns the minimum value of an attribute
 QVariant QgsPostgresProvider::minValue(int index)
 {
-  // get the field name 
-  QgsField fld = attributeFields[index];
-  QString sql;
-  if(sqlWhereClause.isEmpty())
+  try
   {
-    sql = QString("select min(%1) from %2")
-            .arg(quotedIdentifier(fld.name()))
-            .arg(mSchemaTableName);
+    // get the field name 
+    const QgsField &fld = field(index);
+    QString sql;
+    if(sqlWhereClause.isEmpty())
+    {
+      sql = QString("select min(%1) from %2")
+        .arg(quotedIdentifier(fld.name()))
+        .arg(mSchemaTableName);
+    }
+    else
+    {
+      sql = QString("select min(%1) from %2 where %3")
+        .arg(quotedIdentifier(fld.name()))
+        .arg(mSchemaTableName)
+        .arg(sqlWhereClause);
+    }
+    PGresult *rmin = PQexec(connection, sql.toUtf8());
+    QString minValue = QString::fromUtf8(PQgetvalue(rmin,0,0));
+    PQclear(rmin);
+    return minValue.toDouble();
   }
-  else
+  catch(PGFieldNotFound)
   {
-    sql = QString("select min(%1) from %2 where %3")
-            .arg(quotedIdentifier(fld.name()))
-            .arg(mSchemaTableName)
-            .arg(sqlWhereClause);
+    return QVariant(QString::null);
   }
-  PGresult *rmin = PQexec(connection, sql.toUtf8());
-  QString minValue = QString::fromUtf8(PQgetvalue(rmin,0,0));
-  PQclear(rmin);
-  return minValue.toDouble();
 }
 
 // Returns the list of unique values of an attribute
 void QgsPostgresProvider::getUniqueValues(int index, QStringList &uniqueValues)
 {
-  // get the field name 
-  QgsField fld = attributeFields[index];
-  QString sql;
-  if(sqlWhereClause.isEmpty())
-  {
-    sql = QString("select distinct %1 from %2 order by %1")
-            .arg(quotedIdentifier(fld.name()))
-            .arg(mSchemaTableName);
-  }
-  else
-  {
-    sql = QString("select distinct %1 from %2 where %3 order by %1")
-            .arg(quotedIdentifier(fld.name()))
-            .arg(mSchemaTableName)
-            .arg(sqlWhereClause);
-  }
-
   uniqueValues.clear();
 
-  PGresult *res= PQexec(connection, sql.toUtf8());
-  if (PQresultStatus(res) == PGRES_TUPLES_OK)
+  try
   {
-    for(int i=0; i<PQntuples(res); i++)
-      uniqueValues.append( QString::fromUtf8(PQgetvalue(res,i,0)) );
+    // get the field name 
+    const QgsField &fld = field(index);
+    QString sql;
+    if(sqlWhereClause.isEmpty())
+    {
+      sql = QString("select distinct %1 from %2 order by %1")
+        .arg(quotedIdentifier(fld.name()))
+        .arg(mSchemaTableName);
+    }
+    else
+    {
+      sql = QString("select distinct %1 from %2 where %3 order by %1")
+        .arg(quotedIdentifier(fld.name()))
+        .arg(mSchemaTableName)
+        .arg(sqlWhereClause);
+    }
+
+    PGresult *res= PQexec(connection, sql.toUtf8());
+    if (PQresultStatus(res) == PGRES_TUPLES_OK)
+    {
+      for(int i=0; i<PQntuples(res); i++)
+        uniqueValues.append( QString::fromUtf8(PQgetvalue(res,i,0)) );
+    }
+    PQclear(res);
   }
-  PQclear(res);
+  catch(PGFieldNotFound)
+  { 
+  }
 }
 
 // Returns the maximum value of an attribute
 
 QVariant QgsPostgresProvider::maxValue(int index)
 {
-  // get the field name 
-  QgsField fld = attributeFields[index];
-  QString sql;
-  if(sqlWhereClause.isEmpty())
+  try
   {
-    sql = QString("select max(%1) from %2")
-            .arg(quotedIdentifier(fld.name()))
-            .arg(mSchemaTableName);
+    // get the field name 
+    const QgsField &fld = field(index);
+    QString sql;
+    if(sqlWhereClause.isEmpty())
+    {
+      sql = QString("select max(%1) from %2")
+        .arg(quotedIdentifier(fld.name()))
+        .arg(mSchemaTableName);
+    }
+    else
+    {
+      sql = QString("select max(%1) from %2 where %3")
+        .arg(quotedIdentifier(fld.name()))
+        .arg(mSchemaTableName)
+        .arg(sqlWhereClause);
+    } 
+    PGresult *rmax = PQexec(connection, sql.toUtf8());
+    QString maxValue = QString::fromUtf8(PQgetvalue(rmax,0,0));
+    PQclear(rmax);
+    return maxValue.toDouble();
   }
-  else
+  catch(PGFieldNotFound)
   {
-    sql = QString("select max(%1) from %2 where %3")
-            .arg(quotedIdentifier(fld.name()))
-            .arg(mSchemaTableName)
-            .arg(sqlWhereClause);
-  } 
-  PGresult *rmax = PQexec(connection, sql.toUtf8());
-  QString maxValue = QString::fromUtf8(PQgetvalue(rmax,0,0));
-  PQclear(rmax);
-  return maxValue.toDouble();
+    return QVariant(QString::null);
+  }
 }
 
 
@@ -1679,33 +1722,39 @@
 
 QVariant QgsPostgresProvider::getDefaultValue(int fieldId)
 {
-  // Get the default column value from the Postgres information
-  // schema. If there is no default we return an empty string.
+  try
+  {
+    // Get the default column value from the Postgres information
+    // schema. If there is no default we return an empty string.
 
-  // Maintaining a cache of the results of this query would be quite
-  // simple and if this query is called lots, could save some time.
+    // Maintaining a cache of the results of this query would be quite
+    // simple and if this query is called lots, could save some time.
+    QString fieldName = field(fieldId).name();
 
-  QString fieldName = attributeFields[fieldId].name();
-
-  QString sql("SELECT column_default FROM"
+    QString sql("SELECT column_default FROM"
       " information_schema.columns WHERE"
       " column_default IS NOT NULL"
       " AND table_schema = " + quotedValue(mSchemaName) +
       " AND table_name = " + quotedValue(mTableName) +
       " AND column_name = " + quotedValue(fieldName) );
 
-  QVariant defaultValue(QString::null);
+    QVariant defaultValue(QString::null);
 
-  PGresult* result = PQexec(connection, sql.toUtf8());
+    PGresult* result = PQexec(connection, sql.toUtf8());
 
-  if (PQntuples(result)==1 && !PQgetisnull(result, 0, 0) )
-    defaultValue = QString::fromUtf8(PQgetvalue(result, 0, 0));
+    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() ) );
+    // QgsDebugMsg( QString("defaultValue for %1 is NULL: %2").arg(fieldId).arg( defaultValue.isNull() ) );
 
-  PQclear(result);
+    PQclear(result);
 
-  return defaultValue;
+    return defaultValue;
+  }
+  catch(PGFieldNotFound)
+  {
+    return QVariant(QString::null);
+  }
 }
 
 /**
@@ -2035,20 +2084,27 @@
       // cycle through the changed attributes of the feature
       for(QgsAttributeMap::const_iterator siter = attrs.begin(); siter != attrs.end(); ++siter)
       {
-        QString fieldName = attributeFields[siter.key()].name();
+        try {
+          QString fieldName = field(siter.key()).name();
 
-        QString sql = QString("UPDATE %1 SET %2=%3 WHERE %4=%5")
-                        .arg( mSchemaTableName )
-                        .arg( quotedIdentifier(fieldName) )
-                        .arg( quotedValue( siter->toString() ) )
-                        .arg( quotedIdentifier(primaryKey) )
-                        .arg( fid );
-        QgsDebugMsg(sql);
+          QString sql = QString("UPDATE %1 SET %2=%3 WHERE %4=%5")
+                          .arg( mSchemaTableName )
+                          .arg( quotedIdentifier(fieldName) )
+                          .arg( quotedValue( siter->toString() ) )
+                          .arg( quotedIdentifier(primaryKey) )
+                          .arg( fid );
+          QgsDebugMsg(sql);
 
-        PGresult* result=PQexec(connection, sql.toUtf8());
-        if( result==0 || PQresultStatus(result)==PGRES_FATAL_ERROR )
-          throw PGException(result);
-        PQclear(result);
+          PGresult* result=PQexec(connection, sql.toUtf8());
+          if( result==0 || PQresultStatus(result)==PGRES_FATAL_ERROR )
+            throw PGException(result);
+         
+          PQclear(result);
+        }
+        catch(PGFieldNotFound)
+        {
+          // Field was missing - shouldn't happen
+        }
       }
     }
 
@@ -2149,7 +2205,7 @@
 QgsAttributeList QgsPostgresProvider::allAttributesList()
 {
   QgsAttributeList attributes;
-  for(QgsFieldMap::iterator it = attributeFields.begin(); it != attributeFields.end(); ++it)
+  for(QgsFieldMap::const_iterator it = attributeFields.constBegin(); it != attributeFields.constEnd(); ++it)
   {
     attributes.push_back(it.key());
   }
@@ -2607,21 +2663,25 @@
   return value.prepend("'").append("'");
 }
 
-void QgsPostgresProvider::PQexecNR(PGconn *conn, const char *query)
+bool QgsPostgresProvider::PQexecNR(PGconn *conn, const char *query)
 {
   PGresult *res = PQexec(conn, query);
   if(res)
   {
+    int errorStatus = PQresultStatus(res);
     QgsDebugMsgLevel( QString("Query: %1 returned %2 [%3]")
                         .arg(query)
-                        .arg(PQresStatus(PQresultStatus(res)))
+                        .arg(errorStatus)
                         .arg(PQresultErrorMessage(res)), 3 );
     PQclear(res);
+
+    return errorStatus==PGRES_COMMAND_OK;
   }
   else
   {
     QgsDebugMsgLevel( QString("Query: %1 returned no result buffer").arg(query), 3 );
   }
+  return false;
 }
 
 void QgsPostgresProvider::showMessageBox(const QString& title, const QString& text)

Modified: trunk/qgis/src/providers/postgres/qgspostgresprovider.h
===================================================================
--- trunk/qgis/src/providers/postgres/qgspostgresprovider.h	2008-03-27 14:50:11 UTC (rev 8289)
+++ trunk/qgis/src/providers/postgres/qgspostgresprovider.h	2008-03-27 23:50:45 UTC (rev 8290)
@@ -110,17 +110,6 @@
                                 bool fetchGeometry = true,
                                 QgsAttributeList fetchAttributes = QgsAttributeList());
 
-    void declareCursor(const QString &cursorName,
-                       const QgsAttributeList &fetchAttributes,
-                       bool fetchGeometry,
-                       QString whereClause,
-                       QStringList &attributeNames);
-
-    void getFeature(PGresult *queryResult, int row, bool fetchGeometry,
-                    QgsFeature &feature,
-                    const QStringList &attributeNames,
-                    const QgsAttributeList &fetchAttributes);
-    
     /** Get the feature type. This corresponds to
      * WKBPoint,
      * WKBLineString,
@@ -336,6 +325,17 @@
   private:
     int providerId; // id to append to provider specific identified (like cursors)
 
+    bool declareCursor(const QString &cursorName,
+                       const QgsAttributeList &fetchAttributes,
+                       bool fetchGeometry,
+                       QString whereClause);
+
+    bool getFeature(PGresult *queryResult, int row, bool fetchGeometry,
+                    QgsFeature &feature,
+                    const QgsAttributeList &fetchAttributes);
+
+    const QgsField &field(int index) const;
+
     /** Double quote a PostgreSQL identifier for placement in a SQL string.
      */
     QString quotedIdentifier( QString ident ) const;
@@ -453,9 +453,6 @@
      */
     bool swapEndian;
 
-    /**Stores the names of the attributes to fetch*/
-    QStringList mFetchAttributeNames;
-
     bool deduceEndian();
     bool getGeometryDetails();
 
@@ -483,6 +480,9 @@
       QString column_type;
     };
 
+    struct PGFieldNotFound {
+    };
+
     struct PGException {
       PGException(PGresult *r) : result(r)
       {
@@ -603,7 +603,7 @@
     QByteArray paramValue(QString fieldvalue, const QString &defaultValue) const;
 
     // run a query and free result buffer
-    static void PQexecNR(PGconn *conn, const char *query);
+    static bool PQexecNR(PGconn *conn, const char *query);
 
     struct Conn
     {



More information about the QGIS-commit mailing list