Index: src/app/postgres/qgspgsourceselect.cpp =================================================================== --- src/app/postgres/qgspgsourceselect.cpp (revision 13803) +++ src/app/postgres/qgspgsourceselect.cpp (working copy) @@ -606,66 +606,105 @@ bool QgsPgSourceSelect::getTableInfo( PGconn *pg, bool searchGeometryColumnsOnly, bool searchPublicOnly ) { - int n = 0; + int nColumns = 0; + int nGTables = 0; QApplication::setOverrideCursor( Qt::WaitCursor ); - // The following query returns only tables that exist and the user has SELECT privilege on. - // Can't use regclass here because table must exist, else error occurs. - QString sql = "select " - "f_table_name," - "f_table_schema," - "f_geometry_column," - "type," - "pg_class.relkind" - " from " - "geometry_columns," - "pg_class," - "pg_namespace" - " where " - "relname=f_table_name" - " and f_table_schema=nspname" - " and pg_namespace.oid=pg_class.relnamespace" - " and has_schema_privilege(pg_namespace.nspname,'usage')" - " and has_table_privilege('\"'||pg_namespace.nspname||'\".\"'||pg_class.relname||'\"','select')" // user has select privilege - " order by " - "f_table_schema,f_table_name,f_geometry_column"; + PGresult *result = 0; - PGresult *result = PQexec( pg, sql.toUtf8() ); - if ( result ) + for ( int i = 0; i < 2; i++ ) { - if ( PQresultStatus( result ) != PGRES_TUPLES_OK ) + QString gtableName, columnName; + + if ( i == 0 ) { - QMessageBox::warning( this, - tr( "Accessible tables could not be determined" ), - tr( "Database connection was successful, but the accessible tables could not be determined.\n\n" - "The error message from the database was:\n%1\n" ) - .arg( QString::fromUtf8( PQresultErrorMessage( result ) ) ) ); - n = -1; + gtableName = "geometry_columns"; + columnName = "f_geometry_column"; } - else if ( PQntuples( result ) > 0 ) + else if ( i == 1 ) { - for ( int idx = 0; idx < PQntuples( result ); idx++ ) + gtableName = "geography_columns"; + columnName = "f_geography_column"; + } + + // The following query returns only tables that exist and the user has SELECT privilege on. + // Can't use regclass here because table must exist, else error occurs. + QString sql = QString( "select " + "f_table_name," + "f_table_schema," + "%2," + "upper(type)," + "pg_class.relkind" + " from " + "%1," + "pg_class," + "pg_namespace" + " where " + "relname=f_table_name" + " and f_table_schema=nspname" + " and pg_namespace.oid=pg_class.relnamespace" + " and has_schema_privilege(pg_namespace.nspname,'usage')" + " and has_table_privilege('\"'||pg_namespace.nspname||'\".\"'||pg_class.relname||'\"','select')" // user has select privilege + " order by " + "f_table_schema,f_table_name,%2" ).arg( gtableName ).arg( columnName ); + + QgsDebugMsg( "sql: " + sql ); + + result = PQexec( pg, sql.toUtf8() ); + if ( result ) + { + if ( PQresultStatus( result ) != PGRES_TUPLES_OK ) { - QString tableName = QString::fromUtf8( PQgetvalue( result, idx, 0 ) ); - QString schemaName = QString::fromUtf8( PQgetvalue( result, idx, 1 ) ); - QString column = QString::fromUtf8( PQgetvalue( result, idx, 2 ) ); - QString type = QString::fromUtf8( PQgetvalue( result, idx, 3 ) ); - QString relkind = QString::fromUtf8( PQgetvalue( result, idx, 4 ) ); + PGresult *r = PQexec( pg, "COMMIT" ); + if ( r ) + PQclear( r ); + } + else + { + nGTables++; - QString as = ""; - if ( type == "GEOMETRY" && !searchGeometryColumnsOnly ) + if ( PQntuples( result ) > 0 ) { - addSearchGeometryColumn( schemaName, tableName, column ); - as = type = "WAITING"; + + for ( int idx = 0; idx < PQntuples( result ); idx++ ) + { + QString tableName = QString::fromUtf8( PQgetvalue( result, idx, 0 ) ); + QString schemaName = QString::fromUtf8( PQgetvalue( result, idx, 1 ) ); + QString column = QString::fromUtf8( PQgetvalue( result, idx, 2 ) ); + QString type = QString::fromUtf8( PQgetvalue( result, idx, 3 ) ); + QString relkind = QString::fromUtf8( PQgetvalue( result, idx, 4 ) ); + + QgsDebugMsg( QString( "%1 %2.%3.%4: %5 %6" ) + .arg( gtableName ) + .arg( schemaName ).arg( tableName ).arg( column ) + .arg( type ) + .arg( relkind ) ); + + QString as = ""; + if ( type == "GEOMETRY" && !searchGeometryColumnsOnly ) + { + addSearchGeometryColumn( schemaName, tableName, column ); + as = type = "WAITING"; + } + + mTableModel.addTableEntry( type, schemaName, tableName, column, relkind == "v" ? pkCandidates( pg, schemaName, tableName ) : QStringList(), "" ); + nColumns++; + } } - - mTableModel.addTableEntry( type, schemaName, tableName, column, relkind == "v" ? pkCandidates( pg, schemaName, tableName ) : QStringList(), "" ); - n++; } } + + PQclear( result ); + result = 0; } - PQclear( result ); + if ( nColumns == 0 ) + { + QMessageBox::warning( this, + tr( "Accessible tables could not be determined" ), + tr( "Database connection was successful, but the accessible tables could not be determined." ) ); + nColumns = -1; + } //search for geometry columns in tables that are not in the geometry_columns metatable QApplication::restoreOverrideCursor(); @@ -676,39 +715,47 @@ // geometry_columns table. This code is specific to postgresql, // but an equivalent query should be possible in other // databases. - sql = "select " - "pg_class.relname," - "pg_namespace.nspname," - "pg_attribute.attname," - "pg_class.relkind" - " from " - "pg_attribute," - "pg_class," - "pg_namespace" - " where " - "pg_namespace.oid = pg_class.relnamespace" - " and pg_attribute.attrelid = pg_class.oid " - " and (" - "pg_attribute.atttypid = regtype('geometry')" - " or pg_attribute.atttypid IN (select oid FROM pg_type WHERE typbasetype=regtype('geometry'))" - ")" - " and has_schema_privilege(pg_namespace.nspname,'usage')" - " and has_table_privilege('\"'||pg_namespace.nspname||'\".\"'||pg_class.relname||'\"','select')"; + QString sql = "select " + "pg_class.relname" + ",pg_namespace.nspname" + ",pg_attribute.attname" + ",pg_class.relkind" + " from " + "pg_attribute" + ",pg_class" + ",pg_namespace" + " where " + "pg_namespace.oid=pg_class.relnamespace" + " and pg_attribute.attrelid = pg_class.oid" + " and (" + "pg_attribute.atttypid::regtype::text IN ('geometry','geography')" + " or pg_attribute.atttypid IN (select oid FROM pg_type WHERE typbasetype::regtype::text IN ('geometry','geography'))" + ")" + " and has_schema_privilege( pg_namespace.nspname, 'usage' )" + " and has_table_privilege( '\"' || pg_namespace.nspname || '\".\"' || pg_class.relname || '\"', 'select' )"; + // user has select privilege if ( searchPublicOnly ) sql += " and pg_namespace.nspname = 'public'"; - if ( n > 0 ) + if ( nColumns > 0 ) { sql += " and not exists (select * from geometry_columns WHERE pg_namespace.nspname=f_table_schema AND pg_class.relname=f_table_name)"; + + if ( nGTables > 1 ) + { + sql += " and not exists (select * from geography_columns WHERE pg_namespace.nspname=f_table_schema AND pg_class.relname=f_table_name)"; + } } else { - n = 0; + nColumns = 0; } - sql += " and pg_class.relkind in ('v', 'r')"; // only from views and relations (tables) + sql += " and pg_class.relkind in( 'v', 'r' )"; // only from views and relations (tables) + QgsDebugMsg( "sql: " + sql ); + result = PQexec( pg, sql.toUtf8() ); if ( PQresultStatus( result ) != PGRES_TUPLES_OK ) @@ -718,8 +765,8 @@ tr( "Database connection was successful, but the accessible tables could not be determined.\n\n" "The error message from the database was:\n%1\n" ) .arg( QString::fromUtf8( PQresultErrorMessage( result ) ) ) ); - if ( n == 0 ) - n = -1; + if ( nColumns == 0 ) + nColumns = -1; } else if ( PQntuples( result ) > 0 ) { @@ -737,17 +784,20 @@ QString column = QString::fromUtf8( PQgetvalue( result, i, 2 ) ); // attname QString relkind = QString::fromUtf8( PQgetvalue( result, i, 3 ) ); // relation kind + QgsDebugMsg( QString( "%1.%2.%3: %4" ).arg( schema ).arg( table ).arg( column ).arg( relkind ) ); + addSearchGeometryColumn( schema, table, column ); //details.push_back(geomPair(fullDescription(schema, table, column, "WAITING"), "WAITING")); - mTableModel.addTableEntry( "Waiting", schema, table, column, relkind == "v" ? pkCandidates( pg, schema, table ) : QStringList(), "" ); - n++; + mTableModel.addTableEntry( tr( "Waiting" ), schema, table, column, relkind == "v" ? pkCandidates( pg, schema, table ) : QStringList(), "" ); + nColumns++; } } PQclear( result ); + result = 0; } - if ( n == 0 ) + if ( nColumns == 0 ) { QMessageBox::warning( this, tr( "No accessible tables found" ), @@ -756,7 +806,7 @@ "geometry." ) ); } - return n > 0; + return nColumns > 0; } QString QgsPgSourceSelect::fullDescription( QString schema, QString table, @@ -852,7 +902,9 @@ query += "\"" + schemas[i] + "\".\"" + tables[i] + "\""; } - PGresult* gresult = PQexec( pd, query.toUtf8() ); + QgsDebugMsg( "sql: " + query ); + + PGresult *gresult = PQexec( pd, query.toUtf8() ); QString type; if ( PQresultStatus( gresult ) == PGRES_TUPLES_OK ) { Index: src/providers/postgres/qgspostgresprovider.h =================================================================== --- src/providers/postgres/qgspostgresprovider.h (revision 13803) +++ src/providers/postgres/qgspostgresprovider.h (working copy) @@ -383,6 +383,11 @@ bool isQuery; /** + * geometry is geography + */ + bool isGeography; + + /** * Name of the table with no schema */ QString mTableName; Index: src/providers/postgres/qgspostgresprovider.cpp =================================================================== --- src/providers/postgres/qgspostgresprovider.cpp (revision 13803) +++ src/providers/postgres/qgspostgresprovider.cpp (working copy) @@ -68,7 +68,6 @@ providerId = providerIds++; - QgsDebugMsg( "Postgresql Layer Creation" ); QgsDebugMsg( "URI: " + uri ); mUri = QgsDataSourceURI( uri ); @@ -78,6 +77,7 @@ mTableName = mUri.table(); geometryColumn = mUri.geometryColumn(); sqlWhereClause = mUri.sql(); + isGeography = false; if ( mSchemaName.isEmpty() && mTableName.startsWith( "(select", Qt::CaseInsensitive ) && @@ -374,6 +374,10 @@ { return QString( "asewkt(%1)" ).arg( quotedIdentifier( fld.name() ) ); } + else if ( type == "geography" ) + { + return QString( "st_astext(%1)" ).arg( quotedIdentifier( fld.name() ) ); + } else { return quotedIdentifier( fld.name() ) + "::text"; @@ -392,9 +396,16 @@ if ( fetchGeometry ) { - query += QString( ",asbinary(%1,'%2')" ) - .arg( quotedIdentifier( geometryColumn ) ) - .arg( endianString() ); + if ( isGeography ) + { + query += QString( ",st_asbinary(%1)" ).arg( quotedIdentifier( geometryColumn ) ); + } + else + { + query += QString( ",asbinary(%1,'%2')" ) + .arg( quotedIdentifier( geometryColumn ) ) + .arg( endianString() ); + } } for ( QgsAttributeList::const_iterator it = fetchAttributes.constBegin(); it != fetchAttributes.constEnd(); ++it ) @@ -539,21 +550,32 @@ if ( !rect.isEmpty() ) { - if ( useIntersect ) + if ( isGeography ) { - // Contributed by #qgis irc "creeping" - // This version actually invokes PostGIS's use of spatial indexes - whereClause = QString( "%1 && setsrid('BOX3D(%2)'::box3d,%3) and intersects(%1,setsrid('BOX3D(%2)'::box3d,%3))" ) - .arg( quotedIdentifier( geometryColumn ) ) - .arg( rect.asWktCoordinates() ) - .arg( srid ); + rect = QgsRectangle( -180.0, -90.0, 180.0, 90.0 ).intersect( &rect ); + if ( !rect.isFinite() ) + whereClause = "false"; } - else + + if ( whereClause.isEmpty() ) { - whereClause = QString( "%1 && setsrid('BOX3D(%2)'::box3d,%3)" ) - .arg( quotedIdentifier( geometryColumn ) ) - .arg( rect.asWktCoordinates() ) - .arg( srid ); + + if ( useIntersect ) + { + // Contributed by #qgis irc "creeping" + // This version actually invokes PostGIS's use of spatial indexes + whereClause = QString( "%1 && setsrid('BOX3D(%2)'::box3d,%3) and intersects(%1,setsrid('BOX3D(%2)'::box3d,%3))" ) + .arg( quotedIdentifier( geometryColumn ) ) + .arg( rect.asWktCoordinates() ) + .arg( srid ); + } + else + { + whereClause = QString( "%1 && setsrid('BOX3D(%2)'::box3d,%3)" ) + .arg( quotedIdentifier( geometryColumn ) ) + .arg( rect.asWktCoordinates() ) + .arg( srid ); + } } } @@ -2305,6 +2327,10 @@ { values += QString( ",geomfromewkt(%1)" ).arg( quotedValue( it->toString() ) ); } + else if ( fit->typeName() == "geography" ) + { + values += QString( ",st_geographyfromewkt(%1)" ).arg( quotedValue( it->toString() ) ); + } else { values += "," + quotedValue( it->toString() ); @@ -2317,6 +2343,10 @@ { values += QString( ",geomfromewkt($%1)" ).arg( defaultValues.size() + offset ); } + else if ( fit->typeName() == "geography" ) + { + values += QString( ",st_geographyfromewkt($%1)" ).arg( defaultValues.size() + offset ); + } else { values += QString( ",$%1" ).arg( defaultValues.size() + offset ); @@ -2576,7 +2606,9 @@ else first = false; - sql += QString( fld.typeName() != "geometry" ? "%1=%2" : "%1=geomfromewkt(%2)" ) + sql += QString( fld.typeName() == "geometry" ? "%1=geomfromewkt(%2)" : + fld.typeName() == "geography" ? "%1=st_geographyfromewkt(%2)" : + "%1=%2" ) .arg( quotedIdentifier( fld.name() ) ) .arg( quotedValue( siter->toString() ) ); } @@ -2779,6 +2811,9 @@ QgsRectangle QgsPostgresProvider::extent() { + if ( isGeography ) + return QgsRectangle( -180.0, -90.0, 180.0, 90.0 ); + if ( layerExtent.isEmpty() ) { QString sql; @@ -2986,7 +3021,6 @@ .arg( quotedValue( schemaName ) ); QgsDebugMsg( "Getting geometry column: " + sql ); - result = connectionRO->PQexec( sql ); QgsDebugMsg( "geometry column query returned " + QString::number( PQntuples( result ) ) ); @@ -2999,6 +3033,28 @@ if ( srid.isEmpty() || fType.isEmpty() ) { + sql = QString( "select upper(type),srid from geography_columns" + " where f_table_name=%1 and f_geography_column=%2 and f_table_schema=%3" ) + .arg( quotedValue( tableName ) ) + .arg( quotedValue( geomCol ) ) + .arg( quotedValue( schemaName ) ); + + QgsDebugMsg( "Getting geography column: " + sql ); + result = connectionRO->PQexec( sql ); + + QgsDebugMsg( "geography column query returned " + QString::number( PQntuples( result ) ) ); + + if ( PQntuples( result ) > 0 ) + { + fType = QString::fromUtf8( PQgetvalue( result, 0, 0 ) ); + srid = QString::fromUtf8( PQgetvalue( result, 0, 1 ) ); + + isGeography = true; + } + } + + if ( srid.isEmpty() || fType.isEmpty() ) + { // 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. @@ -3116,6 +3172,7 @@ QgsDebugMsg( "type is " + fType ); QgsDebugMsg( "Feature type is " + QString::number( geomType ) ); QgsDebugMsg( "Feature type name is " + QString( QGis::qgisFeatureTypes[geomType] ) ); + QgsDebugMsg( "Geometry is geography " + isGeography ); } else {