[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