diff

preliminary patch to add geography support - Jürgen Fischer, 2010-06-25 01:01 PM

Download (17.7 KB)

 
1
Index: src/app/postgres/qgspgsourceselect.cpp
2
===================================================================
3
--- src/app/postgres/qgspgsourceselect.cpp	(revision 13803)
4
+++ src/app/postgres/qgspgsourceselect.cpp	(working copy)
5
@@ -606,66 +606,105 @@
6
 
7
 bool QgsPgSourceSelect::getTableInfo( PGconn *pg, bool searchGeometryColumnsOnly, bool searchPublicOnly )
8
 {
9
-  int n = 0;
10
+  int nColumns = 0;
11
+  int nGTables = 0;
12
   QApplication::setOverrideCursor( Qt::WaitCursor );
13
 
14
-  // The following query returns only tables that exist and the user has SELECT privilege on.
15
-  // Can't use regclass here because table must exist, else error occurs.
16
-  QString sql = "select "
17
-                "f_table_name,"
18
-                "f_table_schema,"
19
-                "f_geometry_column,"
20
-                "type,"
21
-                "pg_class.relkind"
22
-                " from "
23
-                "geometry_columns,"
24
-                "pg_class,"
25
-                "pg_namespace"
26
-                " where "
27
-                "relname=f_table_name"
28
-                " and f_table_schema=nspname"
29
-                " and pg_namespace.oid=pg_class.relnamespace"
30
-                " and has_schema_privilege(pg_namespace.nspname,'usage')"
31
-                " and has_table_privilege('\"'||pg_namespace.nspname||'\".\"'||pg_class.relname||'\"','select')" // user has select privilege
32
-                " order by "
33
-                "f_table_schema,f_table_name,f_geometry_column";
34
+  PGresult *result = 0;
35
 
36
-  PGresult *result = PQexec( pg, sql.toUtf8() );
37
-  if ( result )
38
+  for ( int i = 0; i < 2; i++ )
39
   {
40
-    if ( PQresultStatus( result ) != PGRES_TUPLES_OK )
41
+    QString gtableName, columnName;
42
+
43
+    if ( i == 0 )
44
     {
45
-      QMessageBox::warning( this,
46
-                            tr( "Accessible tables could not be determined" ),
47
-                            tr( "Database connection was successful, but the accessible tables could not be determined.\n\n"
48
-                                "The error message from the database was:\n%1\n" )
49
-                            .arg( QString::fromUtf8( PQresultErrorMessage( result ) ) ) );
50
-      n = -1;
51
+      gtableName = "geometry_columns";
52
+      columnName = "f_geometry_column";
53
     }
54
-    else if ( PQntuples( result ) > 0 )
55
+    else if ( i == 1 )
56
     {
57
-      for ( int idx = 0; idx < PQntuples( result ); idx++ )
58
+      gtableName = "geography_columns";
59
+      columnName = "f_geography_column";
60
+    }
61
+
62
+    // The following query returns only tables that exist and the user has SELECT privilege on.
63
+    // Can't use regclass here because table must exist, else error occurs.
64
+    QString sql = QString( "select "
65
+                           "f_table_name,"
66
+                           "f_table_schema,"
67
+                           "%2,"
68
+                           "upper(type),"
69
+                           "pg_class.relkind"
70
+                           " from "
71
+                           "%1,"
72
+                           "pg_class,"
73
+                           "pg_namespace"
74
+                           " where "
75
+                           "relname=f_table_name"
76
+                           " and f_table_schema=nspname"
77
+                           " and pg_namespace.oid=pg_class.relnamespace"
78
+                           " and has_schema_privilege(pg_namespace.nspname,'usage')"
79
+                           " and has_table_privilege('\"'||pg_namespace.nspname||'\".\"'||pg_class.relname||'\"','select')" // user has select privilege
80
+                           " order by "
81
+                           "f_table_schema,f_table_name,%2" ).arg( gtableName ).arg( columnName );
82
+
83
+    QgsDebugMsg( "sql: " + sql );
84
+
85
+    result = PQexec( pg, sql.toUtf8() );
86
+    if ( result )
87
+    {
88
+      if ( PQresultStatus( result ) != PGRES_TUPLES_OK )
89
       {
90
-        QString tableName = QString::fromUtf8( PQgetvalue( result, idx, 0 ) );
91
-        QString schemaName = QString::fromUtf8( PQgetvalue( result, idx, 1 ) );
92
-        QString column = QString::fromUtf8( PQgetvalue( result, idx, 2 ) );
93
-        QString type = QString::fromUtf8( PQgetvalue( result, idx, 3 ) );
94
-        QString relkind = QString::fromUtf8( PQgetvalue( result, idx, 4 ) );
95
+        PGresult *r = PQexec( pg, "COMMIT" );
96
+        if ( r )
97
+          PQclear( r );
98
+      }
99
+      else
100
+      {
101
+        nGTables++;
102
 
103
-        QString as = "";
104
-        if ( type == "GEOMETRY" && !searchGeometryColumnsOnly )
105
+        if ( PQntuples( result ) > 0 )
106
         {
107
-          addSearchGeometryColumn( schemaName, tableName,  column );
108
-          as = type = "WAITING";
109
+
110
+          for ( int idx = 0; idx < PQntuples( result ); idx++ )
111
+          {
112
+            QString tableName = QString::fromUtf8( PQgetvalue( result, idx, 0 ) );
113
+            QString schemaName = QString::fromUtf8( PQgetvalue( result, idx, 1 ) );
114
+            QString column = QString::fromUtf8( PQgetvalue( result, idx, 2 ) );
115
+            QString type = QString::fromUtf8( PQgetvalue( result, idx, 3 ) );
116
+            QString relkind = QString::fromUtf8( PQgetvalue( result, idx, 4 ) );
117
+
118
+            QgsDebugMsg( QString( "%1 %2.%3.%4: %5 %6" )
119
+                         .arg( gtableName )
120
+                         .arg( schemaName ).arg( tableName ).arg( column )
121
+                         .arg( type )
122
+                         .arg( relkind ) );
123
+
124
+            QString as = "";
125
+            if ( type == "GEOMETRY" && !searchGeometryColumnsOnly )
126
+            {
127
+              addSearchGeometryColumn( schemaName, tableName,  column );
128
+              as = type = "WAITING";
129
+            }
130
+
131
+            mTableModel.addTableEntry( type, schemaName, tableName, column, relkind == "v" ? pkCandidates( pg, schemaName, tableName ) : QStringList(), "" );
132
+            nColumns++;
133
+          }
134
         }
135
-
136
-        mTableModel.addTableEntry( type, schemaName, tableName, column, relkind == "v" ? pkCandidates( pg, schemaName, tableName ) : QStringList(), "" );
137
-        n++;
138
       }
139
     }
140
+
141
+    PQclear( result );
142
+    result = 0;
143
   }
144
 
145
-  PQclear( result );
146
+  if ( nColumns == 0 )
147
+  {
148
+    QMessageBox::warning( this,
149
+                          tr( "Accessible tables could not be determined" ),
150
+                          tr( "Database connection was successful, but the accessible tables could not be determined." ) );
151
+    nColumns = -1;
152
+  }
153
 
154
   //search for geometry columns in tables that are not in the geometry_columns metatable
155
   QApplication::restoreOverrideCursor();
156
@@ -676,39 +715,47 @@
157
     // geometry_columns table. This code is specific to postgresql,
158
     // but an equivalent query should be possible in other
159
     // databases.
160
-    sql = "select "
161
-          "pg_class.relname,"
162
-          "pg_namespace.nspname,"
163
-          "pg_attribute.attname,"
164
-          "pg_class.relkind"
165
-          " from "
166
-          "pg_attribute,"
167
-          "pg_class,"
168
-          "pg_namespace"
169
-          " where "
170
-          "pg_namespace.oid = pg_class.relnamespace"
171
-          " and pg_attribute.attrelid = pg_class.oid "
172
-          " and ("
173
-          "pg_attribute.atttypid = regtype('geometry')"
174
-          " or pg_attribute.atttypid IN (select oid FROM pg_type WHERE typbasetype=regtype('geometry'))"
175
-          ")"
176
-          " and has_schema_privilege(pg_namespace.nspname,'usage')"
177
-          " and has_table_privilege('\"'||pg_namespace.nspname||'\".\"'||pg_class.relname||'\"','select')";
178
+    QString sql = "select "
179
+                  "pg_class.relname"
180
+                  ",pg_namespace.nspname"
181
+                  ",pg_attribute.attname"
182
+                  ",pg_class.relkind"
183
+                  " from "
184
+                  "pg_attribute"
185
+                  ",pg_class"
186
+                  ",pg_namespace"
187
+                  " where "
188
+                  "pg_namespace.oid=pg_class.relnamespace"
189
+                  " and pg_attribute.attrelid = pg_class.oid"
190
+                  " and ("
191
+                  "pg_attribute.atttypid::regtype::text IN ('geometry','geography')"
192
+                  " or pg_attribute.atttypid IN (select oid FROM pg_type WHERE typbasetype::regtype::text IN ('geometry','geography'))"
193
+                  ")"
194
+                  " and has_schema_privilege( pg_namespace.nspname, 'usage' )"
195
+                  " and has_table_privilege( '\"' || pg_namespace.nspname || '\".\"' || pg_class.relname || '\"', 'select' )";
196
+
197
     // user has select privilege
198
     if ( searchPublicOnly )
199
       sql += " and pg_namespace.nspname = 'public'";
200
 
201
-    if ( n > 0 )
202
+    if ( nColumns > 0 )
203
     {
204
       sql += " and not exists (select * from geometry_columns WHERE pg_namespace.nspname=f_table_schema AND pg_class.relname=f_table_name)";
205
+
206
+      if ( nGTables > 1 )
207
+      {
208
+        sql += " and not exists (select * from geography_columns WHERE pg_namespace.nspname=f_table_schema AND pg_class.relname=f_table_name)";
209
+      }
210
     }
211
     else
212
     {
213
-      n = 0;
214
+      nColumns = 0;
215
     }
216
 
217
-    sql += " and pg_class.relkind in ('v', 'r')"; // only from views and relations (tables)
218
+    sql += " and pg_class.relkind in( 'v', 'r' )"; // only from views and relations (tables)
219
 
220
+    QgsDebugMsg( "sql: " + sql );
221
+
222
     result = PQexec( pg, sql.toUtf8() );
223
 
224
     if ( PQresultStatus( result ) != PGRES_TUPLES_OK )
225
@@ -718,8 +765,8 @@
226
                             tr( "Database connection was successful, but the accessible tables could not be determined.\n\n"
227
                                 "The error message from the database was:\n%1\n" )
228
                             .arg( QString::fromUtf8( PQresultErrorMessage( result ) ) ) );
229
-      if ( n == 0 )
230
-        n = -1;
231
+      if ( nColumns == 0 )
232
+        nColumns = -1;
233
     }
234
     else if ( PQntuples( result ) > 0 )
235
     {
236
@@ -737,17 +784,20 @@
237
         QString column = QString::fromUtf8( PQgetvalue( result, i, 2 ) ); // attname
238
         QString relkind = QString::fromUtf8( PQgetvalue( result, i, 3 ) ); // relation kind
239
 
240
+        QgsDebugMsg( QString( "%1.%2.%3: %4" ).arg( schema ).arg( table ).arg( column ).arg( relkind ) );
241
+
242
         addSearchGeometryColumn( schema, table, column );
243
         //details.push_back(geomPair(fullDescription(schema, table, column, "WAITING"), "WAITING"));
244
-        mTableModel.addTableEntry( "Waiting", schema, table, column, relkind == "v" ? pkCandidates( pg, schema, table ) : QStringList(), "" );
245
-        n++;
246
+        mTableModel.addTableEntry( tr( "Waiting" ), schema, table, column, relkind == "v" ? pkCandidates( pg, schema, table ) : QStringList(), "" );
247
+        nColumns++;
248
       }
249
     }
250
 
251
     PQclear( result );
252
+    result = 0;
253
   }
254
 
255
-  if ( n == 0 )
256
+  if ( nColumns == 0 )
257
   {
258
     QMessageBox::warning( this,
259
                           tr( "No accessible tables found" ),
260
@@ -756,7 +806,7 @@
261
                               "geometry." ) );
262
   }
263
 
264
-  return n > 0;
265
+  return nColumns > 0;
266
 }
267
 
268
 QString QgsPgSourceSelect::fullDescription( QString schema, QString table,
269
@@ -852,7 +902,9 @@
270
         query += "\"" + schemas[i] + "\".\"" + tables[i] + "\"";
271
       }
272
 
273
-      PGresult* gresult = PQexec( pd, query.toUtf8() );
274
+      QgsDebugMsg( "sql: " + query );
275
+
276
+      PGresult *gresult = PQexec( pd, query.toUtf8() );
277
       QString type;
278
       if ( PQresultStatus( gresult ) == PGRES_TUPLES_OK )
279
       {
280
Index: src/providers/postgres/qgspostgresprovider.h
281
===================================================================
282
--- src/providers/postgres/qgspostgresprovider.h	(revision 13803)
283
+++ src/providers/postgres/qgspostgresprovider.h	(working copy)
284
@@ -383,6 +383,11 @@
285
     bool isQuery;
286
 
287
     /**
288
+     * geometry is geography
289
+     */
290
+    bool isGeography;
291
+
292
+    /**
293
      * Name of the table with no schema
294
      */
295
     QString mTableName;
296
Index: src/providers/postgres/qgspostgresprovider.cpp
297
===================================================================
298
--- src/providers/postgres/qgspostgresprovider.cpp	(revision 13803)
299
+++ src/providers/postgres/qgspostgresprovider.cpp	(working copy)
300
@@ -68,7 +68,6 @@
301
 
302
   providerId = providerIds++;
303
 
304
-  QgsDebugMsg( "Postgresql Layer Creation" );
305
   QgsDebugMsg( "URI: " + uri );
306
 
307
   mUri = QgsDataSourceURI( uri );
308
@@ -78,6 +77,7 @@
309
   mTableName = mUri.table();
310
   geometryColumn = mUri.geometryColumn();
311
   sqlWhereClause = mUri.sql();
312
+  isGeography = false;
313
 
314
   if ( mSchemaName.isEmpty() &&
315
        mTableName.startsWith( "(select", Qt::CaseInsensitive ) &&
316
@@ -374,6 +374,10 @@
317
   {
318
     return QString( "asewkt(%1)" ).arg( quotedIdentifier( fld.name() ) );
319
   }
320
+  else if ( type == "geography" )
321
+  {
322
+    return QString( "st_astext(%1)" ).arg( quotedIdentifier( fld.name() ) );
323
+  }
324
   else
325
   {
326
     return quotedIdentifier( fld.name() ) + "::text";
327
@@ -392,9 +396,16 @@
328
 
329
     if ( fetchGeometry )
330
     {
331
-      query += QString( ",asbinary(%1,'%2')" )
332
-               .arg( quotedIdentifier( geometryColumn ) )
333
-               .arg( endianString() );
334
+      if ( isGeography )
335
+      {
336
+        query += QString( ",st_asbinary(%1)" ).arg( quotedIdentifier( geometryColumn ) );
337
+      }
338
+      else
339
+      {
340
+        query += QString( ",asbinary(%1,'%2')" )
341
+                 .arg( quotedIdentifier( geometryColumn ) )
342
+                 .arg( endianString() );
343
+      }
344
     }
345
 
346
     for ( QgsAttributeList::const_iterator it = fetchAttributes.constBegin(); it != fetchAttributes.constEnd(); ++it )
347
@@ -539,21 +550,32 @@
348
 
349
   if ( !rect.isEmpty() )
350
   {
351
-    if ( useIntersect )
352
+    if ( isGeography )
353
     {
354
-      // Contributed by #qgis irc "creeping"
355
-      // This version actually invokes PostGIS's use of spatial indexes
356
-      whereClause = QString( "%1 && setsrid('BOX3D(%2)'::box3d,%3) and intersects(%1,setsrid('BOX3D(%2)'::box3d,%3))" )
357
-                    .arg( quotedIdentifier( geometryColumn ) )
358
-                    .arg( rect.asWktCoordinates() )
359
-                    .arg( srid );
360
+      rect = QgsRectangle( -180.0, -90.0, 180.0, 90.0 ).intersect( &rect );
361
+      if ( !rect.isFinite() )
362
+        whereClause = "false";
363
     }
364
-    else
365
+
366
+    if ( whereClause.isEmpty() )
367
     {
368
-      whereClause = QString( "%1 && setsrid('BOX3D(%2)'::box3d,%3)" )
369
-                    .arg( quotedIdentifier( geometryColumn ) )
370
-                    .arg( rect.asWktCoordinates() )
371
-                    .arg( srid );
372
+
373
+      if ( useIntersect )
374
+      {
375
+        // Contributed by #qgis irc "creeping"
376
+        // This version actually invokes PostGIS's use of spatial indexes
377
+        whereClause = QString( "%1 && setsrid('BOX3D(%2)'::box3d,%3) and intersects(%1,setsrid('BOX3D(%2)'::box3d,%3))" )
378
+                      .arg( quotedIdentifier( geometryColumn ) )
379
+                      .arg( rect.asWktCoordinates() )
380
+                      .arg( srid );
381
+      }
382
+      else
383
+      {
384
+        whereClause = QString( "%1 && setsrid('BOX3D(%2)'::box3d,%3)" )
385
+                      .arg( quotedIdentifier( geometryColumn ) )
386
+                      .arg( rect.asWktCoordinates() )
387
+                      .arg( srid );
388
+      }
389
     }
390
   }
391
 
392
@@ -2305,6 +2327,10 @@
393
         {
394
           values += QString( ",geomfromewkt(%1)" ).arg( quotedValue( it->toString() ) );
395
         }
396
+        else if ( fit->typeName() == "geography" )
397
+        {
398
+          values += QString( ",st_geographyfromewkt(%1)" ).arg( quotedValue( it->toString() ) );
399
+        }
400
         else
401
         {
402
           values += "," + quotedValue( it->toString() );
403
@@ -2317,6 +2343,10 @@
404
         {
405
           values += QString( ",geomfromewkt($%1)" ).arg( defaultValues.size() + offset );
406
         }
407
+        else if ( fit->typeName() == "geography" )
408
+        {
409
+          values += QString( ",st_geographyfromewkt($%1)" ).arg( defaultValues.size() + offset );
410
+        }
411
         else
412
         {
413
           values += QString( ",$%1" ).arg( defaultValues.size() + offset );
414
@@ -2576,7 +2606,9 @@
415
           else
416
             first = false;
417
 
418
-          sql += QString( fld.typeName() != "geometry" ? "%1=%2" : "%1=geomfromewkt(%2)" )
419
+          sql += QString( fld.typeName() == "geometry" ? "%1=geomfromewkt(%2)" :
420
+                          fld.typeName() == "geography" ? "%1=st_geographyfromewkt(%2)" :
421
+                          "%1=%2" )
422
                  .arg( quotedIdentifier( fld.name() ) )
423
                  .arg( quotedValue( siter->toString() ) );
424
         }
425
@@ -2779,6 +2811,9 @@
426
 
427
 QgsRectangle QgsPostgresProvider::extent()
428
 {
429
+  if ( isGeography )
430
+    return QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
431
+
432
   if ( layerExtent.isEmpty() )
433
   {
434
     QString sql;
435
@@ -2986,7 +3021,6 @@
436
         .arg( quotedValue( schemaName ) );
437
 
438
   QgsDebugMsg( "Getting geometry column: " + sql );
439
-
440
   result = connectionRO->PQexec( sql );
441
 
442
   QgsDebugMsg( "geometry column query returned " + QString::number( PQntuples( result ) ) );
443
@@ -2999,6 +3033,28 @@
444
 
445
   if ( srid.isEmpty() || fType.isEmpty() )
446
   {
447
+    sql = QString( "select upper(type),srid from geography_columns"
448
+                   " where f_table_name=%1 and f_geography_column=%2 and f_table_schema=%3" )
449
+          .arg( quotedValue( tableName ) )
450
+          .arg( quotedValue( geomCol ) )
451
+          .arg( quotedValue( schemaName ) );
452
+
453
+    QgsDebugMsg( "Getting geography column: " + sql );
454
+    result = connectionRO->PQexec( sql );
455
+
456
+    QgsDebugMsg( "geography column query returned " + QString::number( PQntuples( result ) ) );
457
+
458
+    if ( PQntuples( result ) > 0 )
459
+    {
460
+      fType = QString::fromUtf8( PQgetvalue( result, 0, 0 ) );
461
+      srid = QString::fromUtf8( PQgetvalue( result, 0, 1 ) );
462
+
463
+      isGeography = true;
464
+    }
465
+  }
466
+
467
+  if ( srid.isEmpty() || fType.isEmpty() )
468
+  {
469
     // Didn't find what we need in the geometry_columns table, so
470
     // get stuff from the relevant column instead. This may (will?)
471
     // fail if there is no data in the relevant table.
472
@@ -3116,6 +3172,7 @@
473
     QgsDebugMsg( "type is " + fType );
474
     QgsDebugMsg( "Feature type is " + QString::number( geomType ) );
475
     QgsDebugMsg( "Feature type name is " + QString( QGis::qgisFeatureTypes[geomType] ) );
476
+    QgsDebugMsg( "Geometry is geography " + isGeography );
477
   }
478
   else
479
   {