1 : /******************************************************************************
2 : * $Id: ogrpgdatasource.cpp 23581 2011-12-16 17:16:56Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRPGDataSource class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2000, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include <string.h>
31 : #include "ogr_pg.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_string.h"
34 : #include "cpl_hash_set.h"
35 :
36 : #define PQexec this_is_an_error
37 :
38 : CPL_CVSID("$Id: ogrpgdatasource.cpp 23581 2011-12-16 17:16:56Z rouault $");
39 :
40 : static void OGRPGNoticeProcessor( void *arg, const char * pszMessage );
41 :
42 : /************************************************************************/
43 : /* OGRPGDataSource() */
44 : /************************************************************************/
45 :
46 249 : OGRPGDataSource::OGRPGDataSource()
47 :
48 : {
49 249 : pszName = NULL;
50 249 : pszDBName = NULL;
51 249 : papoLayers = NULL;
52 249 : nLayers = 0;
53 249 : hPGConn = NULL;
54 249 : bHavePostGIS = FALSE;
55 249 : bHaveGeography = FALSE;
56 249 : bUseBinaryCursor = FALSE;
57 249 : nSoftTransactionLevel = 0;
58 249 : bBinaryTimeFormatIsInt8 = FALSE;
59 :
60 249 : nGeometryOID = (Oid) 0;
61 249 : nGeographyOID = (Oid) 0;
62 :
63 249 : nKnownSRID = 0;
64 249 : panSRID = NULL;
65 249 : papoSRS = NULL;
66 :
67 249 : poLayerInCopyMode = NULL;
68 249 : nUndefinedSRID = -1; /* actual value will be autotected if PostGIS >= 2.0 detected */
69 249 : }
70 :
71 : /************************************************************************/
72 : /* ~OGRPGDataSource() */
73 : /************************************************************************/
74 :
75 249 : OGRPGDataSource::~OGRPGDataSource()
76 :
77 : {
78 : int i;
79 :
80 249 : FlushSoftTransaction();
81 :
82 249 : CPLFree( pszName );
83 249 : CPLFree( pszDBName );
84 :
85 5364 : for( i = 0; i < nLayers; i++ )
86 5115 : delete papoLayers[i];
87 :
88 249 : CPLFree( papoLayers );
89 :
90 249 : if( hPGConn != NULL )
91 : {
92 : /* XXX - mloskot: After the connection is closed, valgrind still
93 : * reports 36 bytes definitely lost, somewhere in the libpq.
94 : */
95 105 : PQfinish( hPGConn );
96 105 : hPGConn = NULL;
97 : }
98 :
99 252 : for( i = 0; i < nKnownSRID; i++ )
100 : {
101 3 : if( papoSRS[i] != NULL )
102 3 : papoSRS[i]->Release();
103 : }
104 249 : CPLFree( panSRID );
105 249 : CPLFree( papoSRS );
106 249 : }
107 :
108 : /************************************************************************/
109 : /* GetCurrentSchema() */
110 : /************************************************************************/
111 :
112 246 : CPLString OGRPGDataSource::GetCurrentSchema()
113 : {
114 246 : CPLString osCurrentSchema;
115 : /* -------------------------------------------- */
116 : /* Get the current schema */
117 : /* -------------------------------------------- */
118 246 : PGresult *hResult = OGRPG_PQexec(hPGConn,"SELECT current_schema()");
119 246 : if ( hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult,0,0) )
120 : {
121 246 : osCurrentSchema = PQgetvalue(hResult,0,0);
122 : }
123 246 : OGRPGClearResult( hResult );
124 :
125 0 : return osCurrentSchema;
126 : }
127 :
128 : /************************************************************************/
129 : /* OGRPGDecodeVersionString() */
130 : /************************************************************************/
131 :
132 179 : void OGRPGDataSource::OGRPGDecodeVersionString(PGver* psVersion, const char* pszVer)
133 : {
134 : GUInt32 iLen;
135 : const char* ptr;
136 : char szNum[25];
137 : char szVer[10];
138 :
139 179 : while ( *pszVer == ' ' ) pszVer++;
140 :
141 179 : ptr = pszVer;
142 : // get Version string
143 179 : while (*ptr && *ptr != ' ') ptr++;
144 179 : iLen = ptr-pszVer;
145 179 : if ( iLen > sizeof(szVer) - 1 ) iLen = sizeof(szVer) - 1;
146 179 : strncpy(szVer,pszVer,iLen);
147 179 : szVer[iLen] = '\0';
148 :
149 179 : ptr = pszVer = szVer;
150 :
151 : // get Major number
152 179 : while (*ptr && *ptr != '.') ptr++;
153 179 : iLen = ptr-pszVer;
154 179 : if ( iLen > sizeof(szNum) - 1) iLen = sizeof(szNum) - 1;
155 179 : strncpy(szNum,pszVer,iLen);
156 179 : szNum[iLen] = '\0';
157 179 : psVersion->nMajor = atoi(szNum);
158 :
159 179 : if (*ptr == 0)
160 0 : return;
161 179 : pszVer = ++ptr;
162 :
163 : // get Minor number
164 179 : while (*ptr && *ptr != '.') ptr++;
165 179 : iLen = ptr-pszVer;
166 179 : if ( iLen > sizeof(szNum) - 1) iLen = sizeof(szNum) - 1;
167 179 : strncpy(szNum,pszVer,iLen);
168 179 : szNum[iLen] = '\0';
169 179 : psVersion->nMinor = atoi(szNum);
170 :
171 :
172 179 : if ( *ptr )
173 : {
174 105 : pszVer = ++ptr;
175 :
176 : // get Release number
177 105 : while (*ptr && *ptr != '.') ptr++;
178 105 : iLen = ptr-pszVer;
179 105 : if ( iLen > sizeof(szNum) - 1) iLen = sizeof(szNum) - 1;
180 105 : strncpy(szNum,pszVer,iLen);
181 105 : szNum[iLen] = '\0';
182 105 : psVersion->nRelease = atoi(szNum);
183 : }
184 :
185 : }
186 :
187 :
188 : /************************************************************************/
189 : /* One entry for each PG table */
190 : /************************************************************************/
191 :
192 : typedef struct
193 : {
194 : char* pszName;
195 : char* pszGeomType;
196 : int nCoordDimension;
197 : int nSRID;
198 : PostgisType ePostgisType;
199 : } PGGeomColumnDesc;
200 :
201 : typedef struct
202 : {
203 : char* pszTableName;
204 : char* pszSchemaName;
205 : int nGeomColumnCount;
206 : PGGeomColumnDesc* pasGeomColumns; /* list of geometry columns */
207 : int bDerivedInfoAdded; /* set to TRUE if it derives from another table */
208 : } PGTableEntry;
209 :
210 25318 : static unsigned long OGRPGHashTableEntry(const void * _psTableEntry)
211 : {
212 25318 : const PGTableEntry* psTableEntry = (PGTableEntry*)_psTableEntry;
213 : return CPLHashSetHashStr(CPLString().Printf("%s.%s",
214 25318 : psTableEntry->pszSchemaName, psTableEntry->pszTableName));
215 : }
216 :
217 11305 : static int OGRPGEqualTableEntry(const void* _psTableEntry1, const void* _psTableEntry2)
218 : {
219 11305 : const PGTableEntry* psTableEntry1 = (PGTableEntry*)_psTableEntry1;
220 11305 : const PGTableEntry* psTableEntry2 = (PGTableEntry*)_psTableEntry2;
221 : return strcmp(psTableEntry1->pszTableName, psTableEntry2->pszTableName) == 0 &&
222 11305 : strcmp(psTableEntry1->pszSchemaName, psTableEntry2->pszSchemaName) == 0;
223 : }
224 :
225 6957 : static void OGRPGTableEntryAddGeomColumn(PGTableEntry* psTableEntry,
226 : const char* pszName,
227 : const char* pszGeomType = NULL,
228 : int nCoordDimension = 0,
229 : int nSRID = UNDETERMINED_SRID,
230 : PostgisType ePostgisType = GEOM_TYPE_UNKNOWN)
231 : {
232 : psTableEntry->pasGeomColumns = (PGGeomColumnDesc*)
233 : CPLRealloc(psTableEntry->pasGeomColumns,
234 6957 : sizeof(PGGeomColumnDesc) * (psTableEntry->nGeomColumnCount + 1));
235 6957 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].pszName = CPLStrdup(pszName);
236 6957 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].pszGeomType = (pszGeomType) ? CPLStrdup(pszGeomType) : NULL;
237 6957 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].nCoordDimension = nCoordDimension;
238 6957 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].nSRID = nSRID;
239 6957 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].ePostgisType = ePostgisType;
240 6957 : psTableEntry->nGeomColumnCount ++;
241 6957 : }
242 :
243 288 : static void OGRPGTableEntryAddGeomColumn(PGTableEntry* psTableEntry,
244 : const PGGeomColumnDesc* psGeomColumnDesc)
245 : {
246 : OGRPGTableEntryAddGeomColumn(psTableEntry,
247 : psGeomColumnDesc->pszName,
248 : psGeomColumnDesc->pszGeomType,
249 : psGeomColumnDesc->nCoordDimension,
250 : psGeomColumnDesc->nSRID,
251 288 : psGeomColumnDesc->ePostgisType);
252 288 : }
253 :
254 10342 : static void OGRPGFreeTableEntry(void * _psTableEntry)
255 : {
256 10342 : PGTableEntry* psTableEntry = (PGTableEntry*)_psTableEntry;
257 10342 : CPLFree(psTableEntry->pszTableName);
258 10342 : CPLFree(psTableEntry->pszSchemaName);
259 : int i;
260 17299 : for(i=0;i<psTableEntry->nGeomColumnCount;i++)
261 : {
262 6957 : CPLFree(psTableEntry->pasGeomColumns[i].pszName);
263 6957 : CPLFree(psTableEntry->pasGeomColumns[i].pszGeomType);
264 : }
265 10342 : CPLFree(psTableEntry->pasGeomColumns);
266 10342 : CPLFree(psTableEntry);
267 10342 : }
268 :
269 5709 : static PGTableEntry* OGRPGFindTableEntry(CPLHashSet* hSetTables,
270 : const char* pszTableName,
271 : const char* pszSchemaName)
272 : {
273 : PGTableEntry sEntry;
274 5709 : sEntry.pszTableName = (char*) pszTableName;
275 5709 : sEntry.pszSchemaName = (char*) pszSchemaName;
276 5709 : return (PGTableEntry*) CPLHashSetLookup(hSetTables, &sEntry);
277 : }
278 :
279 5127 : static PGTableEntry* OGRPGAddTableEntry(CPLHashSet* hSetTables,
280 : const char* pszTableName,
281 : const char* pszSchemaName)
282 : {
283 5127 : PGTableEntry* psEntry = (PGTableEntry*) CPLCalloc(1, sizeof(PGTableEntry));
284 5127 : psEntry->pszTableName = CPLStrdup(pszTableName);
285 5127 : psEntry->pszSchemaName = CPLStrdup(pszSchemaName);
286 :
287 5127 : CPLHashSetInsert(hSetTables, psEntry);
288 :
289 5127 : return psEntry;
290 : }
291 :
292 : /************************************************************************/
293 : /* Open() */
294 : /************************************************************************/
295 :
296 249 : int OGRPGDataSource::Open( const char * pszNewName, int bUpdate,
297 : int bTestOpen )
298 :
299 : {
300 249 : CPLAssert( nLayers == 0 );
301 :
302 : /* -------------------------------------------------------------------- */
303 : /* Verify postgresql prefix. */
304 : /* -------------------------------------------------------------------- */
305 249 : if( EQUALN(pszNewName,"PGB:",4) )
306 : {
307 4 : bUseBinaryCursor = TRUE;
308 4 : CPLDebug("PG","BINARY cursor is used for geometry fetching");
309 : }
310 : else
311 245 : if( !EQUALN(pszNewName,"PG:",3) )
312 : {
313 144 : if( !bTestOpen )
314 : CPLError( CE_Failure, CPLE_AppDefined,
315 : "%s does not conform to PostgreSQL naming convention,"
316 0 : " PG:*\n", pszNewName );
317 144 : return FALSE;
318 : }
319 :
320 105 : pszName = CPLStrdup( pszNewName );
321 105 : char* pszConnectionName = CPLStrdup(pszName);
322 :
323 : /* -------------------------------------------------------------------- */
324 : /* Determine if the connection string contains an optional */
325 : /* ACTIVE_SCHEMA portion. If so, parse it out. */
326 : /* -------------------------------------------------------------------- */
327 : char *pszActiveSchemaStart;
328 105 : CPLString osActiveSchema;
329 105 : pszActiveSchemaStart = strstr(pszConnectionName, "active_schema=");
330 105 : if (pszActiveSchemaStart == NULL)
331 103 : pszActiveSchemaStart = strstr(pszConnectionName, "ACTIVE_SCHEMA=");
332 105 : if (pszActiveSchemaStart != NULL)
333 : {
334 : char *pszActiveSchema;
335 2 : const char *pszEnd = NULL;
336 :
337 2 : pszActiveSchema = CPLStrdup( pszActiveSchemaStart + strlen("active_schema=") );
338 :
339 2 : pszEnd = strchr(pszActiveSchemaStart, ' ');
340 2 : if( pszEnd == NULL )
341 2 : pszEnd = pszConnectionName + strlen(pszConnectionName);
342 :
343 : // Remove ACTIVE_SCHEMA=xxxxx from pszConnectionName string
344 2 : memmove( pszActiveSchemaStart, pszEnd, strlen(pszEnd) + 1 );
345 :
346 2 : pszActiveSchema[pszEnd - pszActiveSchemaStart - strlen("active_schema=")] = '\0';
347 :
348 2 : osActiveSchema = pszActiveSchema;
349 2 : CPLFree(pszActiveSchema);
350 : }
351 : else
352 : {
353 103 : osActiveSchema = "public";
354 : }
355 :
356 : /* -------------------------------------------------------------------- */
357 : /* Determine if the connection string contains an optional */
358 : /* SCHEMAS portion. If so, parse it out. */
359 : /* -------------------------------------------------------------------- */
360 : char *pszSchemasStart;
361 105 : char **papszSchemaList = NULL;
362 105 : pszSchemasStart = strstr(pszConnectionName, "schemas=");
363 105 : if (pszSchemasStart == NULL)
364 101 : pszSchemasStart = strstr(pszConnectionName, "SCHEMAS=");
365 105 : if (pszSchemasStart != NULL)
366 : {
367 : char *pszSchemas;
368 4 : const char *pszEnd = NULL;
369 :
370 4 : pszSchemas = CPLStrdup( pszSchemasStart + strlen("schemas=") );
371 :
372 4 : pszEnd = strchr(pszSchemasStart, ' ');
373 4 : if( pszEnd == NULL )
374 4 : pszEnd = pszConnectionName + strlen(pszConnectionName);
375 :
376 : // Remove SCHEMAS=xxxxx from pszConnectionName string
377 4 : memmove( pszSchemasStart, pszEnd, strlen(pszEnd) + 1 );
378 :
379 4 : pszSchemas[pszEnd - pszSchemasStart - strlen("schemas=")] = '\0';
380 :
381 4 : papszSchemaList = CSLTokenizeString2( pszSchemas, ",", 0 );
382 :
383 4 : CPLFree(pszSchemas);
384 :
385 : /* If there is only one schema specified, make it the active schema */
386 4 : if (CSLCount(papszSchemaList) == 1)
387 : {
388 2 : osActiveSchema = papszSchemaList[0];
389 : }
390 : }
391 :
392 : /* -------------------------------------------------------------------- */
393 : /* Determine if the connection string contains an optional */
394 : /* TABLES portion. If so, parse it out. The expected */
395 : /* connection string in this case will be, e.g.: */
396 : /* */
397 : /* 'PG:dbname=warmerda user=warmerda tables=s1.t1,[s2.t2,...] */
398 : /* - where sN is schema and tN is table name */
399 : /* We must also strip this information from the connection */
400 : /* string; PQconnectdb() does not like unknown directives */
401 : /* -------------------------------------------------------------------- */
402 105 : PGTableEntry **papsTables = NULL;
403 105 : int nTableCount = 0;
404 :
405 : char *pszTableStart;
406 105 : pszTableStart = strstr(pszConnectionName, "tables=");
407 105 : if (pszTableStart == NULL)
408 102 : pszTableStart = strstr(pszConnectionName, "TABLES=");
409 :
410 105 : if( pszTableStart != NULL )
411 : {
412 : char **papszTableList;
413 : char *pszTableSpec;
414 5 : const char *pszEnd = NULL;
415 : int i;
416 :
417 5 : pszTableSpec = CPLStrdup( pszTableStart + 7 );
418 :
419 5 : pszEnd = strchr(pszTableStart, ' ');
420 5 : if( pszEnd == NULL )
421 5 : pszEnd = pszConnectionName + strlen(pszConnectionName);
422 :
423 : // Remove TABLES=xxxxx from pszConnectionName string
424 5 : memmove( pszTableStart, pszEnd, strlen(pszEnd) + 1 );
425 :
426 5 : pszTableSpec[pszEnd - pszTableStart - 7] = '\0';
427 5 : papszTableList = CSLTokenizeString2( pszTableSpec, ",", 0 );
428 :
429 12 : for( i = 0; i < CSLCount(papszTableList); i++ )
430 : {
431 : char **papszQualifiedParts;
432 :
433 : // Get schema and table name
434 7 : papszQualifiedParts = CSLTokenizeString2( papszTableList[i],
435 14 : ".", 0 );
436 7 : int nParts = CSLCount( papszQualifiedParts );
437 :
438 7 : if( nParts == 1 || nParts == 2 )
439 : {
440 : /* Find the geometry column name if specified */
441 7 : char* pszGeomColumnName = NULL;
442 7 : char* pos = strchr(papszQualifiedParts[CSLCount( papszQualifiedParts ) - 1], '(');
443 7 : if (pos != NULL)
444 : {
445 1 : *pos = '\0';
446 1 : pszGeomColumnName = pos+1;
447 1 : int len = strlen(pszGeomColumnName);
448 1 : if (len > 0)
449 1 : pszGeomColumnName[len - 1] = '\0';
450 : }
451 :
452 7 : papsTables = (PGTableEntry**)CPLRealloc(papsTables, sizeof(PGTableEntry*) * (nTableCount + 1));
453 7 : papsTables[nTableCount] = (PGTableEntry*) CPLCalloc(1, sizeof(PGTableEntry));
454 7 : if (pszGeomColumnName)
455 1 : OGRPGTableEntryAddGeomColumn(papsTables[nTableCount], pszGeomColumnName);
456 :
457 7 : if( nParts == 2 )
458 : {
459 0 : papsTables[nTableCount]->pszSchemaName = CPLStrdup( papszQualifiedParts[0] );
460 0 : papsTables[nTableCount]->pszTableName = CPLStrdup( papszQualifiedParts[1] );
461 : }
462 : else
463 : {
464 7 : papsTables[nTableCount]->pszSchemaName = CPLStrdup( osActiveSchema.c_str());
465 7 : papsTables[nTableCount]->pszTableName = CPLStrdup( papszQualifiedParts[0] );
466 : }
467 7 : nTableCount ++;
468 : }
469 :
470 7 : CSLDestroy(papszQualifiedParts);
471 : }
472 :
473 5 : CSLDestroy(papszTableList);
474 5 : CPLFree(pszTableSpec);
475 : }
476 :
477 :
478 105 : CPLString osCurrentSchema;
479 105 : CPLHashSet *hSetTables = NULL;
480 105 : int bRet = FALSE;
481 105 : int bListAllTables = CSLTestBoolean(CPLGetConfigOption("PG_LIST_ALL_TABLES", "NO"));
482 105 : PGresult *hResult = NULL;
483 :
484 : /* -------------------------------------------------------------------- */
485 : /* Try to establish connection. */
486 : /* -------------------------------------------------------------------- */
487 105 : hPGConn = PQconnectdb( pszConnectionName + (bUseBinaryCursor ? 4 : 3) );
488 105 : CPLFree(pszConnectionName);
489 105 : pszConnectionName = NULL;
490 :
491 105 : if( hPGConn == NULL || PQstatus(hPGConn) == CONNECTION_BAD )
492 : {
493 : CPLError( CE_Failure, CPLE_AppDefined,
494 : "PQconnectdb failed.\n%s",
495 0 : PQerrorMessage(hPGConn) );
496 :
497 0 : PQfinish(hPGConn);
498 0 : hPGConn = NULL;
499 :
500 0 : goto end;
501 : }
502 :
503 105 : bDSUpdate = bUpdate;
504 :
505 : /* -------------------------------------------------------------------- */
506 : /* Set the encoding to UTF8 as the driver advertizes UTF8 */
507 : /* unless PGCLIENTENCODING is defined */
508 : /* -------------------------------------------------------------------- */
509 105 : if (CPLGetConfigOption("PGCLIENTENCODING", NULL) == NULL)
510 : {
511 103 : const char* encoding = "UNICODE";
512 103 : if (PQsetClientEncoding(hPGConn, encoding) == -1)
513 : {
514 : CPLError( CE_Warning, CPLE_AppDefined,
515 : "PQsetClientEncoding(%s) failed.\n%s",
516 0 : encoding, PQerrorMessage( hPGConn ) );
517 : }
518 : }
519 :
520 : /* -------------------------------------------------------------------- */
521 : /* Install a notice processor. */
522 : /* -------------------------------------------------------------------- */
523 105 : PQsetNoticeProcessor( hPGConn, OGRPGNoticeProcessor, this );
524 :
525 : /* -------------------------------------------------------------------- */
526 : /* Try to establish the database name from the connection */
527 : /* string passed. */
528 : /* -------------------------------------------------------------------- */
529 105 : if( strstr(pszNewName, "dbname=") != NULL )
530 : {
531 105 : pszDBName = CPLStrdup( strstr(pszNewName, "dbname=") + 7 );
532 :
533 945 : for( int i = 0; pszDBName[i] != '\0'; i++ )
534 : {
535 851 : if( pszDBName[i] == ' ' )
536 : {
537 11 : pszDBName[i] = '\0';
538 11 : break;
539 : }
540 : }
541 : }
542 0 : else if( getenv( "USER" ) != NULL )
543 0 : pszDBName = CPLStrdup( getenv("USER") );
544 : else
545 0 : pszDBName = CPLStrdup( "unknown_dbname" );
546 :
547 105 : CPLDebug( "PG", "DBName=\"%s\"", pszDBName );
548 :
549 : /* -------------------------------------------------------------------- */
550 : /* Set active schema if different from 'public' */
551 : /* -------------------------------------------------------------------- */
552 105 : if (strcmp(osActiveSchema, "public") != 0)
553 : {
554 4 : CPLString osCommand;
555 4 : osCommand.Printf("SET search_path='%s',public", osActiveSchema.c_str());
556 4 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand );
557 :
558 4 : if( !hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK )
559 : {
560 0 : OGRPGClearResult( hResult );
561 :
562 : CPLError( CE_Failure, CPLE_AppDefined,
563 0 : "%s", PQerrorMessage(hPGConn) );
564 :
565 : goto end;
566 : }
567 :
568 4 : OGRPGClearResult(hResult);
569 : }
570 :
571 : /* -------------------------------------------------------------------- */
572 : /* Find out PostgreSQL version */
573 : /* -------------------------------------------------------------------- */
574 105 : sPostgreSQLVersion.nMajor = -1;
575 105 : sPostgreSQLVersion.nMinor = -1;
576 105 : sPostgreSQLVersion.nRelease = -1;
577 :
578 105 : hResult = OGRPG_PQexec(hPGConn, "SELECT version()" );
579 105 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK
580 : && PQntuples(hResult) > 0 )
581 : {
582 105 : char * pszVer = PQgetvalue(hResult,0,0);
583 :
584 105 : CPLDebug("PG","PostgreSQL version string : '%s'", pszVer);
585 :
586 105 : if (EQUALN(pszVer, "PostgreSQL ", 11))
587 : {
588 105 : OGRPGDecodeVersionString(&sPostgreSQLVersion, pszVer + 11);
589 105 : if (sPostgreSQLVersion.nMajor == 7 && sPostgreSQLVersion.nMinor < 4)
590 : {
591 : /* We don't support BINARY CURSOR for PostgreSQL < 7.4. */
592 : /* The binary protocol for arrays seems to be different from later versions */
593 0 : CPLDebug("PG","BINARY cursor will finally NOT be used because version < 7.4");
594 0 : bUseBinaryCursor = FALSE;
595 : }
596 : }
597 : }
598 105 : OGRPGClearResult(hResult);
599 105 : CPLAssert(NULL == hResult); /* Test if safe PQclear has not been broken */
600 :
601 : /* -------------------------------------------------------------------- */
602 : /* Test if time binary format is int8 or float8 */
603 : /* -------------------------------------------------------------------- */
604 : #if !defined(PG_PRE74)
605 105 : if (bUseBinaryCursor)
606 : {
607 4 : SoftStartTransaction();
608 :
609 4 : hResult = OGRPG_PQexec(hPGConn, "DECLARE gettimebinaryformat BINARY CURSOR FOR SELECT CAST ('00:00:01' AS time)");
610 :
611 4 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
612 : {
613 4 : OGRPGClearResult( hResult );
614 :
615 4 : hResult = OGRPG_PQexec(hPGConn, "FETCH ALL IN gettimebinaryformat" );
616 :
617 4 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK && PQntuples(hResult) == 1 )
618 : {
619 4 : if ( PQfformat( hResult, 0 ) == 1 ) // Binary data representation
620 : {
621 0 : CPLAssert(PQgetlength(hResult, 0, 0) == 8);
622 : double dVal;
623 : unsigned int nVal[2];
624 0 : memcpy( nVal, PQgetvalue( hResult, 0, 0 ), 8 );
625 0 : CPL_MSBPTR32(&nVal[0]);
626 0 : CPL_MSBPTR32(&nVal[1]);
627 0 : memcpy( &dVal, PQgetvalue( hResult, 0, 0 ), 8 );
628 0 : CPL_MSBPTR64(&dVal);
629 0 : if (nVal[0] == 0 && nVal[1] == 1000000)
630 : {
631 0 : bBinaryTimeFormatIsInt8 = TRUE;
632 0 : CPLDebug( "PG", "Time binary format is int8");
633 : }
634 0 : else if (dVal == 1.)
635 : {
636 0 : bBinaryTimeFormatIsInt8 = FALSE;
637 0 : CPLDebug( "PG", "Time binary format is float8");
638 : }
639 : else
640 : {
641 0 : bBinaryTimeFormatIsInt8 = FALSE;
642 0 : CPLDebug( "PG", "Time binary format is unknown");
643 : }
644 : }
645 : }
646 : }
647 :
648 4 : OGRPGClearResult( hResult );
649 :
650 4 : hResult = OGRPG_PQexec(hPGConn, "CLOSE gettimebinaryformat");
651 4 : OGRPGClearResult( hResult );
652 :
653 4 : SoftCommit();
654 : }
655 : #endif
656 :
657 : #ifdef notdef
658 : /* This would be the quickest fix... instead, ogrpglayer has been updated to support */
659 : /* bytea hex format */
660 : if (sPostgreSQLVersion.nMajor >= 9)
661 : {
662 : /* Starting with PostgreSQL 9.0, the default output format for values of type bytea */
663 : /* is hex, whereas we traditionnaly expect escape */
664 : hResult = OGRPG_PQexec(hPGConn, "SET bytea_output TO escape");
665 : OGRPGClearResult( hResult );
666 : }
667 : #endif
668 :
669 : /* -------------------------------------------------------------------- */
670 : /* Test to see if this database instance has support for the */
671 : /* PostGIS Geometry type. If so, disable sequential scanning */
672 : /* so we will get the value of the gist indexes. */
673 : /* -------------------------------------------------------------------- */
674 105 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
675 :
676 105 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
677 : {
678 105 : OGRPGClearResult( hResult );
679 105 : CPLAssert(NULL == hResult);
680 :
681 : hResult = OGRPG_PQexec(hPGConn,
682 105 : "SELECT oid FROM pg_type WHERE typname = 'geometry'" );
683 : }
684 :
685 105 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK
686 : && PQntuples(hResult) > 0 && CSLTestBoolean(CPLGetConfigOption("PG_USE_POSTGIS", "YES")))
687 : {
688 74 : bHavePostGIS = TRUE;
689 74 : nGeometryOID = atoi(PQgetvalue(hResult,0,0));
690 : }
691 : else
692 : {
693 31 : nGeometryOID = (Oid) 0;
694 : }
695 :
696 105 : OGRPGClearResult( hResult );
697 :
698 : /* -------------------------------------------------------------------- */
699 : /* Find out PostGIS version */
700 : /* -------------------------------------------------------------------- */
701 :
702 105 : sPostGISVersion.nMajor = -1;
703 105 : sPostGISVersion.nMinor = -1;
704 105 : sPostGISVersion.nRelease = -1;
705 :
706 105 : if( bHavePostGIS )
707 : {
708 74 : hResult = OGRPG_PQexec(hPGConn, "SELECT postgis_version()" );
709 74 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK
710 : && PQntuples(hResult) > 0 )
711 : {
712 74 : char * pszVer = PQgetvalue(hResult,0,0);
713 :
714 74 : CPLDebug("PG","PostGIS version string : '%s'", pszVer);
715 :
716 74 : OGRPGDecodeVersionString(&sPostGISVersion, pszVer);
717 :
718 : }
719 74 : OGRPGClearResult(hResult);
720 :
721 :
722 74 : if (sPostGISVersion.nMajor == 0 && sPostGISVersion.nMinor < 8)
723 : {
724 : // Turning off sequential scans for PostGIS < 0.8
725 0 : hResult = OGRPG_PQexec(hPGConn, "SET ENABLE_SEQSCAN = OFF");
726 :
727 0 : CPLDebug( "PG", "SET ENABLE_SEQSCAN=OFF" );
728 : }
729 : else
730 : {
731 : // PostGIS >=0.8 is correctly integrated with query planner,
732 : // thus PostgreSQL will use indexes whenever appropriate.
733 74 : hResult = OGRPG_PQexec(hPGConn, "SET ENABLE_SEQSCAN = ON");
734 : }
735 74 : OGRPGClearResult( hResult );
736 : }
737 :
738 : /* -------------------------------------------------------------------- */
739 : /* Find out "unknown SRID" value */
740 : /* -------------------------------------------------------------------- */
741 :
742 105 : if (sPostGISVersion.nMajor >= 2)
743 : {
744 : hResult = OGRPG_PQexec(hPGConn,
745 0 : "SELECT ST_Srid('POINT EMPTY'::GEOMETRY)" );
746 :
747 0 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK
748 : && PQntuples(hResult) > 0)
749 : {
750 0 : nUndefinedSRID = atoi(PQgetvalue(hResult,0,0));
751 : }
752 :
753 0 : OGRPGClearResult( hResult );
754 : }
755 : else
756 105 : nUndefinedSRID = -1;
757 :
758 105 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
759 105 : OGRPGClearResult( hResult );
760 :
761 : /* -------------------------------------------------------------------- */
762 : /* Get a list of available tables if they have not been */
763 : /* specified through the TABLES connection string param */
764 : /* -------------------------------------------------------------------- */
765 :
766 105 : hSetTables = CPLHashSetNew(OGRPGHashTableEntry, OGRPGEqualTableEntry, OGRPGFreeTableEntry);
767 :
768 105 : if (nTableCount == 0)
769 : {
770 100 : CPLString osCommand;
771 : const char* pszAllowedRelations;
772 100 : if( CSLTestBoolean(CPLGetConfigOption("PG_SKIP_VIEWS", "NO")) )
773 0 : pszAllowedRelations = "'r'";
774 : else
775 100 : pszAllowedRelations = "'r','v'";
776 :
777 100 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
778 :
779 100 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
780 : {
781 100 : OGRPGClearResult( hResult );
782 :
783 : /* Caution : in PostGIS case, the result has 3 columns, whereas in the */
784 : /* non-PostGIS case it has only 2 columns */
785 170 : if ( bHavePostGIS && !bListAllTables )
786 : {
787 : /* PostGIS 1.5 brings support for 'geography' type. */
788 : /* Checks that the type exists */
789 :
790 : /* Note: the PG_USE_GEOGRAPHY config option is only used for testing */
791 : /* purpose, to test the ability of the driver to work with older PostGIS */
792 : /* versions, even when we have a newer one. It should not be used by */
793 : /* *real* OGR users */
794 70 : if ((sPostGISVersion.nMajor > 1 ||
795 : (sPostGISVersion.nMajor == 1 && sPostGISVersion.nMinor >= 5)) &&
796 : CSLTestBoolean(CPLGetConfigOption("PG_USE_GEOGRAPHY", "YES")) )
797 : {
798 : hResult = OGRPG_PQexec(hPGConn,
799 70 : "SELECT oid FROM pg_type WHERE typname = 'geography'" );
800 :
801 70 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK
802 : && PQntuples(hResult) > 0)
803 : {
804 70 : bHaveGeography = TRUE;
805 70 : nGeographyOID = atoi(PQgetvalue(hResult,0,0));
806 : }
807 : else
808 : {
809 0 : CPLDebug("PG", "PostGIS >= 1.5 detected but cannot find 'geography' type");
810 : }
811 :
812 70 : OGRPGClearResult( hResult );
813 : }
814 :
815 : osCommand.Printf("DECLARE mycursor CURSOR for "
816 : "SELECT c.relname, n.nspname, g.f_geometry_column, g.type, g.coord_dimension, g.srid, %d FROM pg_class c, pg_namespace n, geometry_columns g "
817 : "WHERE (c.relkind in (%s) AND c.relname !~ '^pg_' AND c.relnamespace=n.oid "
818 : "AND c.relname::TEXT = g.f_table_name::TEXT AND n.nspname = g.f_table_schema)",
819 70 : GEOM_TYPE_GEOMETRY, pszAllowedRelations);
820 :
821 70 : if (bHaveGeography)
822 : osCommand += CPLString().Printf(
823 : "UNION SELECT c.relname, n.nspname, g.f_geography_column, g.type, g.coord_dimension, g.srid, %d FROM pg_class c, pg_namespace n, geography_columns g "
824 : "WHERE (c.relkind in (%s) AND c.relname !~ '^pg_' AND c.relnamespace=n.oid "
825 : "AND c.relname::TEXT = g.f_table_name::TEXT AND n.nspname = g.f_table_schema)",
826 70 : GEOM_TYPE_GEOGRAPHY, pszAllowedRelations);
827 : }
828 : else
829 : osCommand.Printf("DECLARE mycursor CURSOR for "
830 : "SELECT c.relname, n.nspname FROM pg_class c, pg_namespace n "
831 : "WHERE (c.relkind in (%s) AND c.relname !~ '^pg_' AND c.relnamespace=n.oid)",
832 30 : pszAllowedRelations);
833 :
834 100 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
835 : }
836 :
837 100 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
838 : {
839 100 : OGRPGClearResult( hResult );
840 100 : hResult = OGRPG_PQexec(hPGConn, "FETCH ALL in mycursor" );
841 : }
842 :
843 100 : if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK )
844 : {
845 0 : OGRPGClearResult( hResult );
846 :
847 : CPLError( CE_Failure, CPLE_AppDefined,
848 0 : "%s", PQerrorMessage(hPGConn) );
849 : goto end;
850 : }
851 :
852 : /* -------------------------------------------------------------------- */
853 : /* Parse the returned table list */
854 : /* -------------------------------------------------------------------- */
855 6904 : for( int iRecord = 0; iRecord < PQntuples(hResult); iRecord++ )
856 : {
857 6804 : const char *pszTable = PQgetvalue(hResult, iRecord, 0);
858 6804 : const char *pszSchemaName = PQgetvalue(hResult, iRecord, 1);
859 6804 : const char *pszGeomColumnName = NULL;
860 6804 : const char *pszGeomType = NULL;
861 6804 : int nGeomCoordDimension = 0;
862 6804 : int nSRID = 0;
863 6804 : PostgisType ePostgisType = GEOM_TYPE_UNKNOWN;
864 6804 : if (bHavePostGIS && !bListAllTables)
865 : {
866 3334 : pszGeomColumnName = PQgetvalue(hResult, iRecord, 2);
867 3334 : pszGeomType = PQgetvalue(hResult, iRecord, 3);
868 3334 : nGeomCoordDimension = atoi(PQgetvalue(hResult, iRecord, 4));
869 3334 : nSRID = atoi(PQgetvalue(hResult, iRecord, 5));
870 3334 : ePostgisType = (PostgisType) atoi(PQgetvalue(hResult, iRecord, 6));
871 : }
872 :
873 6804 : if( EQUAL(pszTable,"spatial_ref_sys")
874 : || EQUAL(pszTable,"geometry_columns")
875 : || EQUAL(pszTable,"geography_columns") )
876 90 : continue;
877 :
878 6714 : if( EQUAL(pszSchemaName,"information_schema") )
879 1650 : continue;
880 :
881 5064 : papsTables = (PGTableEntry**)CPLRealloc(papsTables, sizeof(PGTableEntry*) * (nTableCount + 1));
882 5064 : papsTables[nTableCount] = (PGTableEntry*) CPLCalloc(1, sizeof(PGTableEntry));
883 5064 : papsTables[nTableCount]->pszTableName = CPLStrdup( pszTable );
884 5064 : papsTables[nTableCount]->pszSchemaName = CPLStrdup( pszSchemaName );
885 5064 : if (pszGeomColumnName)
886 : OGRPGTableEntryAddGeomColumn(papsTables[nTableCount],
887 : pszGeomColumnName,
888 : pszGeomType, nGeomCoordDimension,
889 3334 : nSRID, ePostgisType);
890 5064 : nTableCount ++;
891 :
892 5064 : PGTableEntry* psEntry = OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName);
893 5064 : if (psEntry == NULL)
894 5011 : psEntry = OGRPGAddTableEntry(hSetTables, pszTable, pszSchemaName);
895 5064 : if (pszGeomColumnName)
896 : OGRPGTableEntryAddGeomColumn(psEntry,
897 : pszGeomColumnName,
898 : pszGeomType,
899 : nGeomCoordDimension,
900 3334 : nSRID, ePostgisType);
901 : }
902 :
903 : /* -------------------------------------------------------------------- */
904 : /* Cleanup */
905 : /* -------------------------------------------------------------------- */
906 100 : OGRPGClearResult( hResult );
907 :
908 100 : hResult = OGRPG_PQexec(hPGConn, "CLOSE mycursor");
909 100 : OGRPGClearResult( hResult );
910 :
911 100 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
912 100 : OGRPGClearResult( hResult );
913 :
914 100 : if ( bHavePostGIS && !bListAllTables )
915 : {
916 70 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
917 :
918 70 : OGRPGClearResult( hResult );
919 :
920 : /* -------------------------------------------------------------------- */
921 : /* Fetch inherited tables */
922 : /* -------------------------------------------------------------------- */
923 : hResult = OGRPG_PQexec(hPGConn,
924 : "DECLARE mycursor CURSOR for "
925 : "SELECT c1.relname AS derived, c2.relname AS parent, n.nspname "
926 : "FROM pg_class c1, pg_class c2, pg_namespace n, pg_inherits i "
927 : "WHERE i.inhparent = c2.oid AND i.inhrelid = c1.oid AND c1.relnamespace=n.oid "
928 : "AND c1.relkind in ('r', 'v') AND c1.relnamespace=n.oid AND c2.relkind in ('r','v') "
929 70 : "AND c2.relname !~ '^pg_' AND c2.relnamespace=n.oid");
930 :
931 70 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
932 : {
933 70 : OGRPGClearResult( hResult );
934 70 : hResult = OGRPG_PQexec(hPGConn, "FETCH ALL in mycursor" );
935 : }
936 :
937 70 : if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK )
938 : {
939 0 : OGRPGClearResult( hResult );
940 :
941 : CPLError( CE_Failure, CPLE_AppDefined,
942 0 : "%s", PQerrorMessage(hPGConn) );
943 : goto end;
944 : }
945 :
946 : /* -------------------------------------------------------------------- */
947 : /* Parse the returned table list */
948 : /* -------------------------------------------------------------------- */
949 : int bHasDoneSomething;
950 128 : do
951 : {
952 : /* Iterate over the tuples while we have managed to resolved at least one */
953 : /* table to its table parent with a geometry */
954 : /* For example if we have C inherits B and B inherits A, where A is a base table with a geometry */
955 : /* The first pass will add B to the set of tables */
956 : /* The second pass will add C to the set of tables */
957 :
958 128 : bHasDoneSomething = FALSE;
959 :
960 580 : for( int iRecord = 0; iRecord < PQntuples(hResult); iRecord++ )
961 : {
962 452 : const char *pszTable = PQgetvalue(hResult, iRecord, 0);
963 452 : const char *pszParentTable = PQgetvalue(hResult, iRecord, 1);
964 452 : const char *pszSchemaName = PQgetvalue(hResult, iRecord, 2);
965 :
966 452 : PGTableEntry* psEntry = OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName);
967 : /* We must be careful that a derived table can have its own geometry column(s) */
968 : /* and some inherited from another table */
969 452 : if (psEntry == NULL || psEntry->bDerivedInfoAdded == FALSE)
970 : {
971 : PGTableEntry* psParentEntry =
972 193 : OGRPGFindTableEntry(hSetTables, pszParentTable, pszSchemaName);
973 193 : if (psParentEntry != NULL)
974 : {
975 : /* The parent table of this table is already in the set, so we */
976 : /* can now add the table in the set if it was not in it already */
977 :
978 144 : bHasDoneSomething = TRUE;
979 :
980 144 : if (psEntry == NULL)
981 116 : psEntry = OGRPGAddTableEntry(hSetTables, pszTable, pszSchemaName);
982 :
983 : int iGeomColumn;
984 288 : for(iGeomColumn = 0; iGeomColumn < psParentEntry->nGeomColumnCount; iGeomColumn++)
985 : {
986 144 : papsTables = (PGTableEntry**)CPLRealloc(papsTables, sizeof(PGTableEntry*) * (nTableCount + 1));
987 144 : papsTables[nTableCount] = (PGTableEntry*) CPLCalloc(1, sizeof(PGTableEntry));
988 144 : papsTables[nTableCount]->pszTableName = CPLStrdup( pszTable );
989 144 : papsTables[nTableCount]->pszSchemaName = CPLStrdup( pszSchemaName );
990 : OGRPGTableEntryAddGeomColumn(papsTables[nTableCount],
991 144 : &psParentEntry->pasGeomColumns[iGeomColumn]);
992 144 : nTableCount ++;
993 :
994 : OGRPGTableEntryAddGeomColumn(psEntry,
995 144 : &psParentEntry->pasGeomColumns[iGeomColumn]);
996 : }
997 :
998 144 : psEntry->bDerivedInfoAdded = TRUE;
999 : }
1000 : }
1001 : }
1002 : } while(bHasDoneSomething);
1003 :
1004 : /* -------------------------------------------------------------------- */
1005 : /* Cleanup */
1006 : /* -------------------------------------------------------------------- */
1007 70 : OGRPGClearResult( hResult );
1008 :
1009 70 : hResult = OGRPG_PQexec(hPGConn, "CLOSE mycursor");
1010 70 : OGRPGClearResult( hResult );
1011 :
1012 70 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
1013 70 : OGRPGClearResult( hResult );
1014 :
1015 0 : }
1016 :
1017 : }
1018 :
1019 105 : osCurrentSchema = GetCurrentSchema();
1020 :
1021 : /* -------------------------------------------------------------------- */
1022 : /* Register the available tables. */
1023 : /* -------------------------------------------------------------------- */
1024 5320 : for( int iRecord = 0; iRecord < nTableCount; iRecord++ )
1025 : {
1026 : PGTableEntry* psEntry;
1027 5215 : psEntry = (PGTableEntry* )CPLHashSetLookup(hSetTables, papsTables[iRecord]);
1028 :
1029 : /* If SCHEMAS= is specified, only take into account tables inside */
1030 : /* one of the specified schemas */
1031 5457 : if (papszSchemaList != NULL &&
1032 242 : CSLFindString(papszSchemaList, papsTables[iRecord]->pszSchemaName) == -1)
1033 : {
1034 119 : continue;
1035 : }
1036 :
1037 : /* Some heuristics to preserve backward compatibility with the way that */
1038 : /* layers were reported in GDAL <= 1.5.0 */
1039 : /* That is to say : */
1040 : /* - if we get only one geometry column from the request to geometry_columns */
1041 : /* then use it but don't report it into layer definition */
1042 : /* - if we get several geometry columns, use their names and report them */
1043 : /* except for the wkb_geometry column */
1044 : /* - if we get no geometry column, let ReadTableDefinition() parses the columns */
1045 : /* and find the likely geometry column */
1046 :
1047 : OGRPGTableLayer* poLayer;
1048 10056 : if (psEntry != NULL && psEntry->nGeomColumnCount <= 1)
1049 : {
1050 4960 : if (psEntry->nGeomColumnCount == 1)
1051 : {
1052 3289 : poLayer = OpenTable( osCurrentSchema, papsTables[iRecord]->pszTableName,
1053 3289 : papsTables[iRecord]->pszSchemaName,
1054 9867 : psEntry->pasGeomColumns[0].pszName, bUpdate, FALSE, FALSE );
1055 3289 : poLayer->SetGeometryInformation(psEntry->pasGeomColumns[0].pszGeomType,
1056 3289 : psEntry->pasGeomColumns[0].nCoordDimension,
1057 3289 : psEntry->pasGeomColumns[0].nSRID,
1058 13156 : psEntry->pasGeomColumns[0].ePostgisType);
1059 : }
1060 : else
1061 1671 : poLayer = OpenTable( osCurrentSchema, papsTables[iRecord]->pszTableName,
1062 3342 : papsTables[iRecord]->pszSchemaName, NULL, bUpdate, FALSE, FALSE );
1063 : }
1064 : else
1065 : {
1066 136 : if (papsTables[iRecord]->nGeomColumnCount == 0)
1067 6 : poLayer = OpenTable( osCurrentSchema, papsTables[iRecord]->pszTableName,
1068 12 : papsTables[iRecord]->pszSchemaName, NULL, bUpdate, FALSE, FALSE );
1069 : else
1070 : {
1071 130 : poLayer = OpenTable( osCurrentSchema, papsTables[iRecord]->pszTableName,
1072 260 : papsTables[iRecord]->pszSchemaName, papsTables[iRecord]->pasGeomColumns[0].pszName,
1073 520 : bUpdate, FALSE, !EQUAL(papsTables[iRecord]->pasGeomColumns[0].pszName, "wkb_geometry") );
1074 130 : poLayer->SetGeometryInformation(papsTables[iRecord]->pasGeomColumns[0].pszGeomType,
1075 130 : papsTables[iRecord]->pasGeomColumns[0].nCoordDimension,
1076 130 : papsTables[iRecord]->pasGeomColumns[0].nSRID,
1077 520 : papsTables[iRecord]->pasGeomColumns[0].ePostgisType);
1078 : }
1079 : }
1080 : }
1081 :
1082 105 : bRet = TRUE;
1083 :
1084 : end:
1085 105 : if (hSetTables)
1086 105 : CPLHashSetDestroy(hSetTables);
1087 105 : CSLDestroy( papszSchemaList );
1088 :
1089 5320 : for(int i=0;i<nTableCount;i++)
1090 5215 : OGRPGFreeTableEntry(papsTables[i]);
1091 105 : CPLFree(papsTables);
1092 :
1093 105 : return bRet;
1094 : }
1095 :
1096 : /************************************************************************/
1097 : /* OpenTable() */
1098 : /************************************************************************/
1099 :
1100 5182 : OGRPGTableLayer* OGRPGDataSource::OpenTable( CPLString& osCurrentSchema,
1101 : const char *pszNewName,
1102 : const char *pszSchemaName,
1103 : const char * pszGeomColumnIn,
1104 : int bUpdate,
1105 : int bTestOpen,
1106 : int bAdvertizeGeomColumn)
1107 :
1108 : {
1109 : /* -------------------------------------------------------------------- */
1110 : /* Create the layer object. */
1111 : /* -------------------------------------------------------------------- */
1112 : OGRPGTableLayer *poLayer;
1113 :
1114 : poLayer = new OGRPGTableLayer( this, osCurrentSchema,
1115 : pszNewName, pszSchemaName,
1116 : pszGeomColumnIn, bUpdate,
1117 5182 : bAdvertizeGeomColumn );
1118 5268 : if( bTestOpen && poLayer->GetLayerDefnCanReturnNULL() == NULL )
1119 : {
1120 53 : delete poLayer;
1121 53 : return NULL;
1122 : }
1123 :
1124 : /* -------------------------------------------------------------------- */
1125 : /* Add layer to data source layer list. */
1126 : /* -------------------------------------------------------------------- */
1127 : papoLayers = (OGRPGTableLayer **)
1128 5129 : CPLRealloc( papoLayers, sizeof(OGRPGTableLayer *) * (nLayers+1) );
1129 5129 : papoLayers[nLayers++] = poLayer;
1130 :
1131 5129 : return poLayer;
1132 : }
1133 :
1134 : /************************************************************************/
1135 : /* DeleteLayer() */
1136 : /************************************************************************/
1137 :
1138 66 : int OGRPGDataSource::DeleteLayer( int iLayer )
1139 :
1140 : {
1141 66 : if( iLayer < 0 || iLayer >= nLayers )
1142 0 : return OGRERR_FAILURE;
1143 :
1144 : /* -------------------------------------------------------------------- */
1145 : /* Blow away our OGR structures related to the layer. This is */
1146 : /* pretty dangerous if anything has a reference to this layer! */
1147 : /* -------------------------------------------------------------------- */
1148 66 : CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName();
1149 66 : CPLString osTableName = papoLayers[iLayer]->GetTableName();
1150 66 : CPLString osSchemaName = papoLayers[iLayer]->GetSchemaName();
1151 :
1152 66 : CPLDebug( "PG", "DeleteLayer(%s)", osLayerName.c_str() );
1153 :
1154 66 : delete papoLayers[iLayer];
1155 : memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
1156 66 : sizeof(void *) * (nLayers - iLayer - 1) );
1157 66 : nLayers--;
1158 :
1159 66 : if (osLayerName.size() == 0)
1160 0 : return OGRERR_NONE;
1161 :
1162 : /* -------------------------------------------------------------------- */
1163 : /* Remove from the database. */
1164 : /* -------------------------------------------------------------------- */
1165 : PGresult *hResult;
1166 66 : CPLString osCommand;
1167 :
1168 66 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
1169 66 : OGRPGClearResult( hResult );
1170 :
1171 66 : if( bHavePostGIS )
1172 : {
1173 : /* This is unnecessary if the layer is not a geometry table, or an inherited geometry table */
1174 : /* but it shouldn't hurt */
1175 : osCommand.Printf(
1176 : "SELECT DropGeometryColumn('%s','%s',(SELECT f_geometry_column from geometry_columns where f_table_name='%s' and f_table_schema='%s' order by f_geometry_column limit 1))",
1177 37 : osSchemaName.c_str(), osTableName.c_str(), osTableName.c_str(), osSchemaName.c_str() );
1178 :
1179 37 : hResult = OGRPG_PQexec( hPGConn, osCommand.c_str() );
1180 37 : OGRPGClearResult( hResult );
1181 : }
1182 :
1183 66 : osCommand.Printf("DROP TABLE \"%s\".\"%s\" CASCADE", osSchemaName.c_str(), osTableName.c_str() );
1184 66 : hResult = OGRPG_PQexec( hPGConn, osCommand.c_str() );
1185 66 : OGRPGClearResult( hResult );
1186 :
1187 66 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
1188 66 : OGRPGClearResult( hResult );
1189 :
1190 66 : return OGRERR_NONE;
1191 : }
1192 :
1193 : /************************************************************************/
1194 : /* CreateLayer() */
1195 : /************************************************************************/
1196 :
1197 : OGRLayer *
1198 55 : OGRPGDataSource::CreateLayer( const char * pszLayerName,
1199 : OGRSpatialReference *poSRS,
1200 : OGRwkbGeometryType eType,
1201 : char ** papszOptions )
1202 :
1203 : {
1204 55 : PGresult *hResult = NULL;
1205 55 : CPLString osCommand;
1206 55 : const char *pszGeomType = NULL;
1207 55 : char *pszTableName = NULL;
1208 55 : char *pszSchemaName = NULL;
1209 55 : int nDimension = 3;
1210 :
1211 55 : if (pszLayerName == NULL)
1212 0 : return NULL;
1213 :
1214 55 : const char* pszFIDColumnName = CSLFetchNameValue(papszOptions, "FID");
1215 55 : CPLString osFIDColumnName;
1216 55 : if (pszFIDColumnName == NULL)
1217 55 : osFIDColumnName = "OGC_FID";
1218 : else
1219 : {
1220 0 : if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) )
1221 : {
1222 0 : char* pszLaunderedFid = LaunderName(pszFIDColumnName);
1223 0 : osFIDColumnName += OGRPGEscapeColumnName(pszLaunderedFid);
1224 0 : CPLFree(pszLaunderedFid);
1225 : }
1226 : else
1227 0 : osFIDColumnName += OGRPGEscapeColumnName(pszFIDColumnName);
1228 : }
1229 55 : pszFIDColumnName = osFIDColumnName.c_str();
1230 :
1231 55 : if (strncmp(pszLayerName, "pg", 2) == 0)
1232 : {
1233 : CPLError(CE_Warning, CPLE_AppDefined,
1234 0 : "The layer name should not begin by 'pg' as it is a reserved prefix");
1235 : }
1236 :
1237 55 : if( wkbFlatten(eType) == eType )
1238 55 : nDimension = 2;
1239 :
1240 55 : if( CSLFetchNameValue( papszOptions, "DIM") != NULL )
1241 9 : nDimension = atoi(CSLFetchNameValue( papszOptions, "DIM"));
1242 :
1243 : /* Should we turn layers with None geometry type as Unknown/GEOMETRY */
1244 : /* so they are still recorded in geometry_columns table ? (#4012) */
1245 : int bNoneAsUnknown = CSLTestBoolean(CSLFetchNameValueDef(
1246 55 : papszOptions, "NONE_AS_UNKNOWN", "NO"));
1247 55 : if (bNoneAsUnknown && eType == wkbNone)
1248 0 : eType = wkbUnknown;
1249 :
1250 :
1251 : int bExtractSchemaFromLayerName = CSLTestBoolean(CSLFetchNameValueDef(
1252 55 : papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
1253 :
1254 : /* Postgres Schema handling:
1255 : Extract schema name from input layer name or passed with -lco SCHEMA.
1256 : Set layer name to "schema.table" or to "table" if schema == current_schema()
1257 : Usage without schema name is backwards compatible
1258 : */
1259 55 : const char* pszDotPos = strstr(pszLayerName,".");
1260 58 : if ( pszDotPos != NULL && bExtractSchemaFromLayerName )
1261 : {
1262 3 : int length = pszDotPos - pszLayerName;
1263 3 : pszSchemaName = (char*)CPLMalloc(length+1);
1264 3 : strncpy(pszSchemaName, pszLayerName, length);
1265 3 : pszSchemaName[length] = '\0';
1266 :
1267 3 : if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) )
1268 3 : pszTableName = LaunderName( pszDotPos + 1 ); //skip "."
1269 : else
1270 0 : pszTableName = CPLStrdup( pszDotPos + 1 ); //skip "."
1271 : }
1272 : else
1273 : {
1274 52 : pszSchemaName = NULL;
1275 52 : if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) )
1276 52 : pszTableName = LaunderName( pszLayerName ); //skip "."
1277 : else
1278 0 : pszTableName = CPLStrdup( pszLayerName ); //skip "."
1279 : }
1280 :
1281 :
1282 : /* -------------------------------------------------------------------- */
1283 : /* Set the default schema for the layers. */
1284 : /* -------------------------------------------------------------------- */
1285 55 : if( CSLFetchNameValue( papszOptions, "SCHEMA" ) != NULL )
1286 : {
1287 3 : CPLFree(pszSchemaName);
1288 3 : pszSchemaName = CPLStrdup(CSLFetchNameValue( papszOptions, "SCHEMA" ));
1289 : }
1290 :
1291 55 : CPLString osCurrentSchema = GetCurrentSchema();
1292 :
1293 55 : if ( pszSchemaName == NULL && strlen(osCurrentSchema) > 0)
1294 : {
1295 51 : pszSchemaName = CPLStrdup(osCurrentSchema);
1296 : }
1297 :
1298 : /* -------------------------------------------------------------------- */
1299 : /* Do we already have this layer? If so, should we blow it */
1300 : /* away? */
1301 : /* -------------------------------------------------------------------- */
1302 : int iLayer;
1303 :
1304 55 : FlushSoftTransaction();
1305 :
1306 55 : CPLString osSQLLayerName;
1307 55 : if (pszSchemaName == NULL || (strlen(osCurrentSchema) > 0 && EQUAL(pszSchemaName, osCurrentSchema.c_str())))
1308 51 : osSQLLayerName = pszTableName;
1309 : else
1310 : {
1311 4 : osSQLLayerName = pszSchemaName;
1312 4 : osSQLLayerName += ".";
1313 4 : osSQLLayerName += pszTableName;
1314 : }
1315 :
1316 : /* GetLayerByName() can instanciate layers that would have been */
1317 : /* 'hidden' otherwise, for example, non-spatial tables in a */
1318 : /* Postgis-enabled database, so this apparently useless command is */
1319 : /* not useless... (#4012) */
1320 55 : CPLPushErrorHandler(CPLQuietErrorHandler);
1321 55 : GetLayerByName(osSQLLayerName);
1322 55 : CPLPopErrorHandler();
1323 55 : CPLErrorReset();
1324 :
1325 2821 : for( iLayer = 0; iLayer < nLayers; iLayer++ )
1326 : {
1327 2768 : if( EQUAL(osSQLLayerName.c_str(),papoLayers[iLayer]->GetName()) )
1328 : {
1329 8 : if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL
1330 : && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
1331 : {
1332 6 : DeleteLayer( iLayer );
1333 : }
1334 : else
1335 : {
1336 : CPLError( CE_Failure, CPLE_AppDefined,
1337 : "Layer %s already exists, CreateLayer failed.\n"
1338 : "Use the layer creation option OVERWRITE=YES to "
1339 : "replace it.",
1340 2 : osSQLLayerName.c_str() );
1341 2 : CPLFree( pszTableName );
1342 2 : CPLFree( pszSchemaName );
1343 2 : return NULL;
1344 : }
1345 : }
1346 : }
1347 :
1348 : /* -------------------------------------------------------------------- */
1349 : /* Handle the GEOM_TYPE option. */
1350 : /* -------------------------------------------------------------------- */
1351 53 : pszGeomType = CSLFetchNameValue( papszOptions, "GEOM_TYPE" );
1352 53 : if( pszGeomType == NULL )
1353 : {
1354 52 : if( bHavePostGIS )
1355 33 : pszGeomType = "geometry";
1356 : else
1357 19 : pszGeomType = "bytea";
1358 : }
1359 :
1360 53 : if( eType != wkbNone && EQUAL(pszGeomType, "geography") && !bHaveGeography )
1361 : {
1362 : CPLError( CE_Failure, CPLE_AppDefined,
1363 : "GEOM_TYPE=geography is only supported in PostGIS >= 1.5.\n"
1364 : "Creation of layer %s has failed.",
1365 0 : pszLayerName );
1366 0 : CPLFree( pszTableName );
1367 0 : CPLFree( pszSchemaName );
1368 0 : return NULL;
1369 : }
1370 :
1371 53 : if( eType != wkbNone && bHavePostGIS && !EQUAL(pszGeomType,"geometry") &&
1372 : !EQUAL(pszGeomType, "geography") )
1373 : {
1374 0 : if( bHaveGeography )
1375 : CPLError( CE_Failure, CPLE_AppDefined,
1376 : "GEOM_TYPE in PostGIS enabled databases must be 'geometry' or 'geography'.\n"
1377 : "Creation of layer %s with GEOM_TYPE %s has failed.",
1378 0 : pszLayerName, pszGeomType );
1379 : else
1380 : CPLError( CE_Failure, CPLE_AppDefined,
1381 : "GEOM_TYPE in PostGIS enabled databases must be 'geometry'.\n"
1382 : "Creation of layer %s with GEOM_TYPE %s has failed.",
1383 0 : pszLayerName, pszGeomType );
1384 :
1385 0 : CPLFree( pszTableName );
1386 0 : CPLFree( pszSchemaName );
1387 0 : return NULL;
1388 : }
1389 :
1390 : /* -------------------------------------------------------------------- */
1391 : /* Try to get the SRS Id of this spatial reference system, */
1392 : /* adding tot the srs table if needed. */
1393 : /* -------------------------------------------------------------------- */
1394 53 : int nSRSId = nUndefinedSRID;
1395 :
1396 53 : if( poSRS != NULL )
1397 12 : nSRSId = FetchSRSId( poSRS );
1398 :
1399 53 : const char *pszGeometryType = OGRToOGCGeomType(eType);
1400 : /* -------------------------------------------------------------------- */
1401 : /* Create a basic table with the FID. Also include the */
1402 : /* geometry if this is not a PostGIS enabled table. */
1403 : /* -------------------------------------------------------------------- */
1404 53 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
1405 53 : OGRPGClearResult( hResult );
1406 :
1407 53 : const char *pszGFldName = NULL;
1408 :
1409 53 : CPLString osCreateTable;
1410 : int bTemporary = CSLFetchNameValue( papszOptions, "TEMPORARY" ) != NULL &&
1411 53 : CSLTestBoolean(CSLFetchNameValue( papszOptions, "TEMPORARY" ));
1412 53 : if (bTemporary)
1413 : {
1414 0 : CPLFree(pszSchemaName);
1415 0 : pszSchemaName = CPLStrdup("pg_temp_1");
1416 0 : osCreateTable.Printf("CREATE TEMPORARY TABLE \"%s\"", pszTableName);
1417 : }
1418 : else
1419 53 : osCreateTable.Printf("CREATE TABLE \"%s\".\"%s\"", pszSchemaName, pszTableName);
1420 :
1421 69 : if( eType != wkbNone && !bHavePostGIS )
1422 : {
1423 : osCommand.Printf(
1424 : "%s ( "
1425 : " %s SERIAL, "
1426 : " WKB_GEOMETRY %s, "
1427 : " CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
1428 : osCreateTable.c_str(),
1429 : pszFIDColumnName,
1430 : pszGeomType,
1431 16 : pszTableName, pszFIDColumnName);
1432 : }
1433 38 : else if ( eType != wkbNone && EQUAL(pszGeomType, "geography") )
1434 : {
1435 1 : if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL )
1436 1 : pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
1437 : else
1438 0 : pszGFldName = "the_geog";
1439 :
1440 1 : if (nSRSId)
1441 : osCommand.Printf(
1442 : "%s ( %s SERIAL, %s geography(%s%s,%d), CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
1443 : osCreateTable.c_str(), pszFIDColumnName,
1444 : OGRPGEscapeColumnName(pszGFldName).c_str(), pszGeometryType,
1445 : nDimension == 2 ? "" : "Z", nSRSId, pszTableName,
1446 1 : pszFIDColumnName);
1447 : else
1448 : osCommand.Printf(
1449 : "%s ( %s SERIAL, %s geography(%s%s), CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
1450 : osCreateTable.c_str(), pszFIDColumnName,
1451 : OGRPGEscapeColumnName(pszGFldName).c_str(), pszGeometryType,
1452 : nDimension == 2 ? "" : "Z", pszTableName,
1453 0 : pszFIDColumnName);
1454 : }
1455 : else
1456 : {
1457 : osCommand.Printf(
1458 : "%s ( %s SERIAL, CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
1459 36 : osCreateTable.c_str(), pszFIDColumnName, pszTableName, pszFIDColumnName );
1460 : }
1461 :
1462 53 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1463 53 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
1464 : {
1465 : CPLError( CE_Failure, CPLE_AppDefined,
1466 0 : "%s\n%s", osCommand.c_str(), PQerrorMessage(hPGConn) );
1467 0 : CPLFree( pszTableName );
1468 0 : CPLFree( pszSchemaName );
1469 :
1470 0 : OGRPGClearResult( hResult );
1471 0 : hResult = OGRPG_PQexec( hPGConn, "ROLLBACK" );
1472 0 : OGRPGClearResult( hResult );
1473 0 : return NULL;
1474 : }
1475 :
1476 53 : OGRPGClearResult( hResult );
1477 :
1478 53 : CPLString osEscapedTableNameSingleQuote = OGRPGEscapeString(hPGConn, pszTableName, -1, "");
1479 53 : const char* pszEscapedTableNameSingleQuote = osEscapedTableNameSingleQuote.c_str();
1480 :
1481 : /* -------------------------------------------------------------------- */
1482 : /* Eventually we should be adding this table to a table of */
1483 : /* "geometric layers", capturing the WKT projection, and */
1484 : /* perhaps some other housekeeping. */
1485 : /* -------------------------------------------------------------------- */
1486 53 : if( eType != wkbNone && bHavePostGIS && !EQUAL(pszGeomType, "geography"))
1487 : {
1488 28 : if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL )
1489 2 : pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
1490 : else
1491 26 : pszGFldName = "wkb_geometry";
1492 :
1493 28 : if (sPostGISVersion.nMajor <= 1)
1494 : {
1495 : /* Sometimes there is an old cruft entry in the geometry_columns
1496 : * table if things were not properly cleaned up before. We make
1497 : * an effort to clean out such cruft.
1498 : * Note: PostGIS 2.0 defines geometry_columns as a view (no clean up is needed)
1499 : */
1500 : osCommand.Printf(
1501 : "DELETE FROM geometry_columns WHERE f_table_name = %s AND f_table_schema = '%s'",
1502 28 : pszEscapedTableNameSingleQuote, pszSchemaName );
1503 :
1504 28 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1505 28 : OGRPGClearResult( hResult );
1506 : }
1507 :
1508 : osCommand.Printf(
1509 : "SELECT AddGeometryColumn('%s',%s,'%s',%d,'%s',%d)",
1510 : pszSchemaName, pszEscapedTableNameSingleQuote, pszGFldName,
1511 28 : nSRSId, pszGeometryType, nDimension );
1512 :
1513 28 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1514 :
1515 28 : if( !hResult
1516 : || PQresultStatus(hResult) != PGRES_TUPLES_OK )
1517 : {
1518 : CPLError( CE_Failure, CPLE_AppDefined,
1519 : "AddGeometryColumn failed for layer %s, layer creation has failed.",
1520 1 : pszLayerName );
1521 :
1522 1 : CPLFree( pszTableName );
1523 1 : CPLFree( pszSchemaName );
1524 :
1525 1 : OGRPGClearResult( hResult );
1526 :
1527 1 : hResult = OGRPG_PQexec(hPGConn, "ROLLBACK");
1528 1 : OGRPGClearResult( hResult );
1529 :
1530 1 : return NULL;
1531 : }
1532 :
1533 27 : OGRPGClearResult( hResult );
1534 : }
1535 :
1536 52 : if( eType != wkbNone && bHavePostGIS )
1537 : {
1538 : /* -------------------------------------------------------------------- */
1539 : /* Create the spatial index. */
1540 : /* */
1541 : /* We're doing this before we add geometry and record to the table */
1542 : /* so this may not be exactly the best way to do it. */
1543 : /* -------------------------------------------------------------------- */
1544 28 : const char *pszSI = CSLFetchNameValue( papszOptions, "SPATIAL_INDEX" );
1545 28 : if( pszSI == NULL || CSLTestBoolean(pszSI) )
1546 : {
1547 : osCommand.Printf("CREATE INDEX \"%s_geom_idx\" "
1548 : "ON \"%s\".\"%s\" "
1549 : "USING GIST (%s)",
1550 : pszTableName, pszSchemaName, pszTableName,
1551 28 : OGRPGEscapeColumnName(pszGFldName).c_str());
1552 :
1553 28 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1554 :
1555 28 : if( !hResult
1556 : || PQresultStatus(hResult) != PGRES_COMMAND_OK )
1557 : {
1558 : CPLError( CE_Failure, CPLE_AppDefined,
1559 : "'%s' failed for layer %s, index creation has failed.",
1560 0 : osCommand.c_str(), pszLayerName );
1561 :
1562 0 : CPLFree( pszTableName );
1563 0 : CPLFree( pszSchemaName );
1564 :
1565 0 : OGRPGClearResult( hResult );
1566 :
1567 0 : hResult = OGRPG_PQexec(hPGConn, "ROLLBACK");
1568 0 : OGRPGClearResult( hResult );
1569 :
1570 0 : return NULL;
1571 : }
1572 28 : OGRPGClearResult( hResult );
1573 : }
1574 : }
1575 :
1576 : /* -------------------------------------------------------------------- */
1577 : /* Complete, and commit the transaction. */
1578 : /* -------------------------------------------------------------------- */
1579 52 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
1580 52 : OGRPGClearResult( hResult );
1581 :
1582 : /* -------------------------------------------------------------------- */
1583 : /* Create the layer object. */
1584 : /* -------------------------------------------------------------------- */
1585 : OGRPGTableLayer *poLayer;
1586 :
1587 52 : poLayer = new OGRPGTableLayer( this, osCurrentSchema, pszTableName, pszSchemaName, NULL, TRUE, FALSE, nSRSId);
1588 104 : if( poLayer->GetLayerDefnCanReturnNULL() == NULL )
1589 : {
1590 0 : CPLFree( pszTableName );
1591 0 : CPLFree( pszSchemaName );
1592 0 : delete poLayer;
1593 0 : return NULL;
1594 : }
1595 :
1596 52 : poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) );
1597 52 : poLayer->SetPrecisionFlag( CSLFetchBoolean(papszOptions,"PRECISION",TRUE));
1598 :
1599 : /* -------------------------------------------------------------------- */
1600 : /* Add layer to data source layer list. */
1601 : /* -------------------------------------------------------------------- */
1602 : papoLayers = (OGRPGTableLayer **)
1603 52 : CPLRealloc( papoLayers, sizeof(OGRPGTableLayer *) * (nLayers+1) );
1604 :
1605 52 : papoLayers[nLayers++] = poLayer;
1606 :
1607 52 : CPLFree( pszTableName );
1608 52 : CPLFree( pszSchemaName );
1609 :
1610 52 : return poLayer;
1611 : }
1612 :
1613 : /************************************************************************/
1614 : /* TestCapability() */
1615 : /************************************************************************/
1616 :
1617 8 : int OGRPGDataSource::TestCapability( const char * pszCap )
1618 :
1619 : {
1620 8 : if( EQUAL(pszCap,ODsCCreateLayer)
1621 : || EQUAL(pszCap,ODsCDeleteLayer) )
1622 8 : return TRUE;
1623 : else
1624 0 : return FALSE;
1625 : }
1626 :
1627 : /************************************************************************/
1628 : /* GetLayer() */
1629 : /************************************************************************/
1630 :
1631 1743 : OGRLayer *OGRPGDataSource::GetLayer( int iLayer )
1632 :
1633 : {
1634 1743 : if( iLayer < 0 || iLayer >= nLayers )
1635 0 : return NULL;
1636 : else
1637 1743 : return papoLayers[iLayer];
1638 : }
1639 :
1640 : /************************************************************************/
1641 : /* GetLayerByName() */
1642 : /************************************************************************/
1643 :
1644 175 : OGRLayer *OGRPGDataSource::GetLayerByName( const char *pszName )
1645 :
1646 : {
1647 175 : char* pszTableName = NULL;
1648 175 : char *pszGeomColumnName = NULL;
1649 175 : char *pszSchemaName = NULL;
1650 :
1651 175 : if ( ! pszName )
1652 0 : return NULL;
1653 :
1654 : int i;
1655 :
1656 175 : int count = GetLayerCount();
1657 : /* first a case sensitive check */
1658 7247 : for( i = 0; i < count; i++ )
1659 : {
1660 7161 : OGRPGTableLayer *poLayer = papoLayers[i];
1661 :
1662 7161 : if( strcmp( pszName, poLayer->GetName() ) == 0 )
1663 : {
1664 89 : return poLayer;
1665 : }
1666 : }
1667 :
1668 : /* then case insensitive */
1669 4081 : for( i = 0; i < count; i++ )
1670 : {
1671 3995 : OGRPGTableLayer *poLayer = papoLayers[i];
1672 :
1673 3995 : if( EQUAL( pszName, poLayer->GetName() ) )
1674 : {
1675 0 : return poLayer;
1676 : }
1677 : }
1678 :
1679 86 : char* pszNameWithoutBracket = CPLStrdup(pszName);
1680 86 : char *pos = strchr(pszNameWithoutBracket, '(');
1681 86 : if (pos != NULL)
1682 : {
1683 1 : *pos = '\0';
1684 1 : pszGeomColumnName = CPLStrdup(pos+1);
1685 1 : int len = strlen(pszGeomColumnName);
1686 1 : if (len > 0)
1687 1 : pszGeomColumnName[len - 1] = '\0';
1688 : }
1689 :
1690 86 : pos = strchr(pszNameWithoutBracket, '.');
1691 86 : if (pos != NULL)
1692 : {
1693 16 : *pos = '\0';
1694 16 : pszSchemaName = CPLStrdup(pszNameWithoutBracket);
1695 16 : pszTableName = CPLStrdup(pos + 1);
1696 : }
1697 : else
1698 : {
1699 70 : pszTableName = CPLStrdup(pszNameWithoutBracket);
1700 : }
1701 86 : CPLFree(pszNameWithoutBracket);
1702 86 : pszNameWithoutBracket = NULL;
1703 :
1704 86 : CPLString osCurrentSchema = GetCurrentSchema();
1705 : OGRPGTableLayer* poLayer = OpenTable( osCurrentSchema, pszTableName,
1706 : pszSchemaName,
1707 86 : pszGeomColumnName, TRUE, TRUE, TRUE );
1708 86 : CPLFree(pszTableName);
1709 86 : CPLFree(pszSchemaName);
1710 86 : CPLFree(pszGeomColumnName);
1711 :
1712 86 : return poLayer;
1713 : }
1714 :
1715 :
1716 : /************************************************************************/
1717 : /* OGRPGNoticeProcessor() */
1718 : /************************************************************************/
1719 :
1720 155 : static void OGRPGNoticeProcessor( void *arg, const char * pszMessage )
1721 :
1722 : {
1723 155 : CPLDebug( "OGR_PG_NOTICE", "%s", pszMessage );
1724 155 : }
1725 :
1726 : /************************************************************************/
1727 : /* InitializeMetadataTables() */
1728 : /* */
1729 : /* Create the metadata tables (SPATIAL_REF_SYS and */
1730 : /* GEOMETRY_COLUMNS). */
1731 : /************************************************************************/
1732 :
1733 0 : OGRErr OGRPGDataSource::InitializeMetadataTables()
1734 :
1735 : {
1736 : // implement later.
1737 0 : return OGRERR_FAILURE;
1738 : }
1739 :
1740 : /************************************************************************/
1741 : /* FetchSRS() */
1742 : /* */
1743 : /* Return a SRS corresponding to a particular id. Note that */
1744 : /* reference counting should be honoured on the returned */
1745 : /* OGRSpatialReference, as handles may be cached. */
1746 : /************************************************************************/
1747 :
1748 3 : OGRSpatialReference *OGRPGDataSource::FetchSRS( int nId )
1749 :
1750 : {
1751 3 : if( nId < 0 )
1752 0 : return NULL;
1753 :
1754 : /* -------------------------------------------------------------------- */
1755 : /* First, we look through our SRID cache, is it there? */
1756 : /* -------------------------------------------------------------------- */
1757 : int i;
1758 :
1759 3 : for( i = 0; i < nKnownSRID; i++ )
1760 : {
1761 0 : if( panSRID[i] == nId )
1762 0 : return papoSRS[i];
1763 : }
1764 :
1765 : /* -------------------------------------------------------------------- */
1766 : /* Try looking up in spatial_ref_sys table. */
1767 : /* -------------------------------------------------------------------- */
1768 3 : PGresult *hResult = NULL;
1769 3 : CPLString osCommand;
1770 3 : OGRSpatialReference *poSRS = NULL;
1771 :
1772 3 : SoftStartTransaction();
1773 :
1774 : osCommand.Printf(
1775 : "SELECT srtext FROM spatial_ref_sys "
1776 : "WHERE srid = %d",
1777 3 : nId );
1778 3 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() );
1779 :
1780 3 : if( hResult
1781 : && PQresultStatus(hResult) == PGRES_TUPLES_OK
1782 : && PQntuples(hResult) == 1 )
1783 : {
1784 : char *pszWKT;
1785 :
1786 3 : pszWKT = PQgetvalue(hResult,0,0);
1787 3 : poSRS = new OGRSpatialReference();
1788 6 : if( poSRS->importFromWkt( &pszWKT ) != OGRERR_NONE )
1789 : {
1790 0 : delete poSRS;
1791 0 : poSRS = NULL;
1792 : }
1793 : }
1794 :
1795 3 : OGRPGClearResult( hResult );
1796 3 : SoftCommit();
1797 :
1798 : /* -------------------------------------------------------------------- */
1799 : /* Add to the cache. */
1800 : /* -------------------------------------------------------------------- */
1801 3 : panSRID = (int *) CPLRealloc(panSRID,sizeof(int) * (nKnownSRID+1) );
1802 : papoSRS = (OGRSpatialReference **)
1803 3 : CPLRealloc(papoSRS, sizeof(void*) * (nKnownSRID + 1) );
1804 3 : panSRID[nKnownSRID] = nId;
1805 3 : papoSRS[nKnownSRID] = poSRS;
1806 3 : nKnownSRID++;
1807 :
1808 3 : return poSRS;
1809 : }
1810 :
1811 : /************************************************************************/
1812 : /* FetchSRSId() */
1813 : /* */
1814 : /* Fetch the id corresponding to an SRS, and if not found, add */
1815 : /* it to the table. */
1816 : /************************************************************************/
1817 :
1818 12 : int OGRPGDataSource::FetchSRSId( OGRSpatialReference * poSRS )
1819 :
1820 : {
1821 12 : PGresult *hResult = NULL;
1822 12 : CPLString osCommand;
1823 12 : char *pszWKT = NULL;
1824 12 : int nSRSId = nUndefinedSRID;
1825 : const char* pszAuthorityName;
1826 :
1827 12 : if( poSRS == NULL )
1828 0 : return nUndefinedSRID;
1829 :
1830 12 : OGRSpatialReference oSRS(*poSRS);
1831 12 : poSRS = NULL;
1832 :
1833 12 : pszAuthorityName = oSRS.GetAuthorityName(NULL);
1834 :
1835 12 : if( pszAuthorityName == NULL || strlen(pszAuthorityName) == 0 )
1836 : {
1837 : /* -------------------------------------------------------------------- */
1838 : /* Try to identify an EPSG code */
1839 : /* -------------------------------------------------------------------- */
1840 8 : oSRS.AutoIdentifyEPSG();
1841 :
1842 8 : pszAuthorityName = oSRS.GetAuthorityName(NULL);
1843 8 : if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG"))
1844 : {
1845 1 : const char* pszAuthorityCode = oSRS.GetAuthorityCode(NULL);
1846 1 : if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 )
1847 : {
1848 : /* Import 'clean' SRS */
1849 1 : oSRS.importFromEPSG( atoi(pszAuthorityCode) );
1850 :
1851 1 : pszAuthorityName = oSRS.GetAuthorityName(NULL);
1852 : }
1853 : }
1854 : }
1855 : /* -------------------------------------------------------------------- */
1856 : /* Check whether the EPSG authority code is already mapped to a */
1857 : /* SRS ID. */
1858 : /* -------------------------------------------------------------------- */
1859 12 : if( pszAuthorityName != NULL && EQUAL( pszAuthorityName, "EPSG" ) )
1860 : {
1861 : int nAuthorityCode;
1862 :
1863 : /* For the root authority name 'EPSG', the authority code
1864 : * should always be integral
1865 : */
1866 5 : nAuthorityCode = atoi( oSRS.GetAuthorityCode(NULL) );
1867 :
1868 : osCommand.Printf("SELECT srid FROM spatial_ref_sys WHERE "
1869 : "auth_name = '%s' AND auth_srid = %d",
1870 : pszAuthorityName,
1871 5 : nAuthorityCode );
1872 5 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1873 :
1874 5 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK
1875 : && PQntuples(hResult) > 0 )
1876 : {
1877 3 : nSRSId = atoi(PQgetvalue( hResult, 0, 0 ));
1878 :
1879 3 : OGRPGClearResult( hResult );
1880 :
1881 3 : return nSRSId;
1882 : }
1883 :
1884 2 : OGRPGClearResult( hResult );
1885 : }
1886 :
1887 : /* -------------------------------------------------------------------- */
1888 : /* Translate SRS to WKT. */
1889 : /* -------------------------------------------------------------------- */
1890 9 : if( oSRS.exportToWkt( &pszWKT ) != OGRERR_NONE )
1891 : {
1892 0 : CPLFree(pszWKT);
1893 0 : return nUndefinedSRID;
1894 : }
1895 :
1896 : /* -------------------------------------------------------------------- */
1897 : /* Try to find in the existing table. */
1898 : /* -------------------------------------------------------------------- */
1899 9 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
1900 9 : OGRPGClearResult( hResult );
1901 :
1902 9 : CPLString osWKT = OGRPGEscapeString(hPGConn, pszWKT, -1, "srtext");
1903 : osCommand.Printf(
1904 : "SELECT srid FROM spatial_ref_sys WHERE srtext = %s",
1905 9 : osWKT.c_str() );
1906 9 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() );
1907 9 : CPLFree( pszWKT ); // CM: Added to prevent mem leaks
1908 9 : pszWKT = NULL; // CM: Added
1909 :
1910 : /* -------------------------------------------------------------------- */
1911 : /* We got it! Return it. */
1912 : /* -------------------------------------------------------------------- */
1913 9 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK
1914 : && PQntuples(hResult) > 0 )
1915 : {
1916 4 : nSRSId = atoi(PQgetvalue( hResult, 0, 0 ));
1917 :
1918 4 : OGRPGClearResult( hResult );
1919 :
1920 4 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
1921 4 : OGRPGClearResult( hResult );
1922 :
1923 4 : return nSRSId;
1924 : }
1925 :
1926 : /* -------------------------------------------------------------------- */
1927 : /* If the command actually failed, then the metadata table is */
1928 : /* likely missing. Try defining it. */
1929 : /* -------------------------------------------------------------------- */
1930 : int bTableMissing;
1931 :
1932 : bTableMissing =
1933 5 : hResult == NULL || PQresultStatus(hResult) == PGRES_NONFATAL_ERROR;
1934 :
1935 5 : OGRPGClearResult( hResult );
1936 :
1937 5 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
1938 5 : OGRPGClearResult( hResult );
1939 :
1940 5 : if( bTableMissing )
1941 : {
1942 0 : if( InitializeMetadataTables() != OGRERR_NONE )
1943 0 : return nUndefinedSRID;
1944 : }
1945 :
1946 : /* -------------------------------------------------------------------- */
1947 : /* Get the current maximum srid in the srs table. */
1948 : /* -------------------------------------------------------------------- */
1949 5 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
1950 5 : OGRPGClearResult( hResult );
1951 :
1952 5 : hResult = OGRPG_PQexec(hPGConn, "SELECT MAX(srid) FROM spatial_ref_sys" );
1953 :
1954 5 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK )
1955 : {
1956 5 : nSRSId = atoi(PQgetvalue(hResult,0,0)) + 1;
1957 5 : OGRPGClearResult( hResult );
1958 : }
1959 : else
1960 : {
1961 0 : nSRSId = 1;
1962 : }
1963 :
1964 : /* -------------------------------------------------------------------- */
1965 : /* Try adding the SRS to the SRS table. */
1966 : /* -------------------------------------------------------------------- */
1967 5 : char *pszProj4 = NULL;
1968 5 : if( oSRS.exportToProj4( &pszProj4 ) != OGRERR_NONE )
1969 : {
1970 0 : CPLFree( pszProj4 );
1971 0 : return nUndefinedSRID;
1972 : }
1973 :
1974 5 : CPLString osProj4 = OGRPGEscapeString(hPGConn, pszProj4, -1, "proj4text");
1975 :
1976 7 : if( pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG") )
1977 : {
1978 : int nAuthorityCode;
1979 :
1980 2 : nAuthorityCode = atoi( oSRS.GetAuthorityCode(NULL) );
1981 :
1982 : osCommand.Printf(
1983 : "INSERT INTO spatial_ref_sys (srid,srtext,proj4text,auth_name,auth_srid) "
1984 : "VALUES (%d, %s, %s, '%s', %d)",
1985 : nSRSId, osWKT.c_str(), osProj4.c_str(),
1986 2 : pszAuthorityName, nAuthorityCode );
1987 : }
1988 : else
1989 : {
1990 : osCommand.Printf(
1991 : "INSERT INTO spatial_ref_sys (srid,srtext,proj4text) VALUES (%d,%s,%s)",
1992 3 : nSRSId, osWKT.c_str(), osProj4.c_str() );
1993 : }
1994 :
1995 : // Free everything that was allocated.
1996 5 : CPLFree( pszProj4 );
1997 5 : CPLFree( pszWKT);
1998 :
1999 5 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() );
2000 5 : OGRPGClearResult( hResult );
2001 :
2002 5 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
2003 5 : OGRPGClearResult( hResult );
2004 :
2005 5 : return nSRSId;
2006 : }
2007 :
2008 : /************************************************************************/
2009 : /* SoftStartTransaction() */
2010 : /* */
2011 : /* Create a transaction scope. If we already have a */
2012 : /* transaction active this isn't a real transaction, but just */
2013 : /* an increment to the scope count. */
2014 : /************************************************************************/
2015 :
2016 4616 : OGRErr OGRPGDataSource::SoftStartTransaction()
2017 :
2018 : {
2019 4616 : nSoftTransactionLevel++;
2020 :
2021 4616 : if( nSoftTransactionLevel == 1 )
2022 : {
2023 513 : PGresult *hResult = NULL;
2024 513 : PGconn *hPGConn = GetPGConn();
2025 :
2026 : //CPLDebug( "PG", "BEGIN Transaction" );
2027 513 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
2028 :
2029 513 : if( !hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK )
2030 : {
2031 0 : OGRPGClearResult( hResult );
2032 :
2033 : CPLDebug( "PG", "BEGIN Transaction failed:\n%s",
2034 0 : PQerrorMessage( hPGConn ) );
2035 0 : return OGRERR_FAILURE;
2036 : }
2037 :
2038 513 : OGRPGClearResult( hResult );
2039 : }
2040 :
2041 4616 : return OGRERR_NONE;
2042 : }
2043 :
2044 : /************************************************************************/
2045 : /* SoftCommit() */
2046 : /* */
2047 : /* Commit the current transaction if we are at the outer */
2048 : /* scope. */
2049 : /************************************************************************/
2050 :
2051 4616 : OGRErr OGRPGDataSource::SoftCommit()
2052 :
2053 : {
2054 4616 : EndCopy();
2055 :
2056 4616 : if( nSoftTransactionLevel <= 0 )
2057 : {
2058 0 : CPLDebug( "PG", "SoftCommit() with no transaction active." );
2059 0 : return OGRERR_FAILURE;
2060 : }
2061 :
2062 4616 : nSoftTransactionLevel--;
2063 :
2064 4616 : if( nSoftTransactionLevel == 0 )
2065 : {
2066 513 : PGresult *hResult = NULL;
2067 513 : PGconn *hPGConn = GetPGConn();
2068 :
2069 : //CPLDebug( "PG", "COMMIT Transaction" );
2070 513 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
2071 :
2072 513 : if( !hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK )
2073 : {
2074 0 : OGRPGClearResult( hResult );
2075 :
2076 : CPLDebug( "PG", "COMMIT Transaction failed:\n%s",
2077 0 : PQerrorMessage( hPGConn ) );
2078 0 : return OGRERR_FAILURE;
2079 : }
2080 :
2081 513 : OGRPGClearResult( hResult );
2082 : }
2083 :
2084 4616 : return OGRERR_NONE;
2085 : }
2086 :
2087 : /************************************************************************/
2088 : /* SoftRollback() */
2089 : /* */
2090 : /* Force a rollback of the current transaction if there is one, */
2091 : /* even if we are nested several levels deep. */
2092 : /************************************************************************/
2093 :
2094 0 : OGRErr OGRPGDataSource::SoftRollback()
2095 :
2096 : {
2097 0 : if( nSoftTransactionLevel <= 0 )
2098 : {
2099 0 : CPLDebug( "PG", "SoftRollback() with no transaction active." );
2100 0 : return OGRERR_FAILURE;
2101 : }
2102 :
2103 0 : nSoftTransactionLevel = 0;
2104 :
2105 0 : PGresult *hResult = NULL;
2106 0 : PGconn *hPGConn = GetPGConn();
2107 :
2108 0 : hResult = OGRPG_PQexec(hPGConn, "ROLLBACK");
2109 :
2110 0 : if( !hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK )
2111 : {
2112 0 : OGRPGClearResult( hResult );
2113 :
2114 0 : return OGRERR_FAILURE;
2115 : }
2116 :
2117 0 : OGRPGClearResult( hResult );
2118 :
2119 0 : return OGRERR_NONE;
2120 : }
2121 :
2122 : /************************************************************************/
2123 : /* FlushSoftTransaction() */
2124 : /* */
2125 : /* Force the unwinding of any active transaction, and it's */
2126 : /* commit. */
2127 : /************************************************************************/
2128 :
2129 1526 : OGRErr OGRPGDataSource::FlushSoftTransaction()
2130 :
2131 : {
2132 : /* This must come first because of ogr2ogr. If you want
2133 : to use ogr2ogr with COPY support, then you must specify
2134 : that ogr2ogr does not use transactions. Thus,
2135 : nSoftTransactionLevel will always be zero, so this has
2136 : to come first. */
2137 1526 : EndCopy();
2138 :
2139 1526 : if( nSoftTransactionLevel <= 0 )
2140 1116 : return OGRERR_NONE;
2141 :
2142 410 : nSoftTransactionLevel = 1;
2143 :
2144 410 : return SoftCommit();
2145 : }
2146 :
2147 :
2148 : /************************************************************************/
2149 : /* OGRPGNoResetResultLayer */
2150 : /************************************************************************/
2151 :
2152 : class OGRPGNoResetResultLayer : public OGRPGLayer
2153 : {
2154 : public:
2155 : OGRPGNoResetResultLayer(OGRPGDataSource *poDSIn,
2156 : PGresult *hResultIn);
2157 :
2158 : virtual ~OGRPGNoResetResultLayer();
2159 :
2160 : virtual void ResetReading();
2161 :
2162 0 : virtual int TestCapability( const char * ) { return FALSE; }
2163 :
2164 : virtual OGRFeature *GetNextFeature();
2165 : };
2166 :
2167 :
2168 : /************************************************************************/
2169 : /* OGRPGNoResetResultLayer() */
2170 : /************************************************************************/
2171 :
2172 21 : OGRPGNoResetResultLayer::OGRPGNoResetResultLayer( OGRPGDataSource *poDSIn,
2173 21 : PGresult *hResultIn )
2174 : {
2175 21 : poDS = poDSIn;
2176 21 : poFeatureDefn = ReadResultDefinition(hResultIn);
2177 21 : hCursorResult = hResultIn;
2178 21 : CreateMapFromFieldNameToIndex();
2179 21 : }
2180 :
2181 : /************************************************************************/
2182 : /* ~OGRPGNoResetResultLayer() */
2183 : /************************************************************************/
2184 :
2185 21 : OGRPGNoResetResultLayer::~OGRPGNoResetResultLayer()
2186 :
2187 : {
2188 21 : OGRPGClearResult( hCursorResult );
2189 21 : hCursorResult = NULL;
2190 21 : }
2191 :
2192 : /************************************************************************/
2193 : /* ResetReading() */
2194 : /************************************************************************/
2195 :
2196 21 : void OGRPGNoResetResultLayer::ResetReading()
2197 : {
2198 21 : iNextShapeId = 0;
2199 21 : }
2200 :
2201 : /************************************************************************/
2202 : /* GetNextFeature() */
2203 : /************************************************************************/
2204 :
2205 42 : OGRFeature *OGRPGNoResetResultLayer::GetNextFeature()
2206 :
2207 : {
2208 42 : if (iNextShapeId == PQntuples(hCursorResult))
2209 : {
2210 21 : return NULL;
2211 : }
2212 21 : return RecordToFeature(iNextShapeId ++);
2213 : }
2214 :
2215 : /************************************************************************/
2216 : /* OGRPGMemLayerWrapper */
2217 : /************************************************************************/
2218 :
2219 : class OGRPGMemLayerWrapper : public OGRLayer
2220 : {
2221 : private:
2222 : OGRDataSource *poMemDS;
2223 : OGRLayer *poMemLayer;
2224 :
2225 : public:
2226 21 : OGRPGMemLayerWrapper( OGRDataSource *poMemDSIn )
2227 21 : {
2228 21 : poMemDS = poMemDSIn;
2229 21 : poMemLayer = poMemDS->GetLayer(0);
2230 21 : }
2231 :
2232 21 : ~OGRPGMemLayerWrapper() { delete poMemDS; }
2233 :
2234 0 : virtual void ResetReading() { poMemLayer->ResetReading(); }
2235 17 : virtual OGRFeature *GetNextFeature() { return poMemLayer->GetNextFeature(); }
2236 0 : virtual OGRFeatureDefn *GetLayerDefn() { return poMemLayer->GetLayerDefn(); }
2237 0 : virtual int TestCapability( const char * ) { return FALSE; }
2238 : };
2239 :
2240 : /************************************************************************/
2241 : /* ExecuteSQL() */
2242 : /************************************************************************/
2243 :
2244 334 : OGRLayer * OGRPGDataSource::ExecuteSQL( const char *pszSQLCommand,
2245 : OGRGeometry *poSpatialFilter,
2246 : const char *pszDialect )
2247 :
2248 : {
2249 : /* Skip leading spaces */
2250 668 : while(*pszSQLCommand == ' ')
2251 0 : pszSQLCommand ++;
2252 :
2253 : /* -------------------------------------------------------------------- */
2254 : /* Use generic implementation for OGRSQL dialect. */
2255 : /* -------------------------------------------------------------------- */
2256 334 : if( pszDialect != NULL && EQUAL(pszDialect,"OGRSQL") )
2257 : return OGRDataSource::ExecuteSQL( pszSQLCommand,
2258 : poSpatialFilter,
2259 0 : pszDialect );
2260 :
2261 : /* -------------------------------------------------------------------- */
2262 : /* Special case DELLAYER: command. */
2263 : /* -------------------------------------------------------------------- */
2264 334 : if( EQUALN(pszSQLCommand,"DELLAYER:",9) )
2265 : {
2266 142 : const char *pszLayerName = pszSQLCommand + 9;
2267 :
2268 284 : while( *pszLayerName == ' ' )
2269 0 : pszLayerName++;
2270 :
2271 5631 : for( int iLayer = 0; iLayer < nLayers; iLayer++ )
2272 : {
2273 5545 : if( EQUAL(papoLayers[iLayer]->GetName(),
2274 : pszLayerName ))
2275 : {
2276 56 : DeleteLayer( iLayer );
2277 56 : break;
2278 : }
2279 : }
2280 142 : return NULL;
2281 : }
2282 :
2283 : /* -------------------------------------------------------------------- */
2284 : /* Execute the statement. */
2285 : /* -------------------------------------------------------------------- */
2286 192 : PGresult *hResult = NULL;
2287 :
2288 192 : FlushSoftTransaction();
2289 :
2290 192 : if( EQUALN(pszSQLCommand,"VACUUM",6)
2291 : || SoftStartTransaction() == OGRERR_NONE )
2292 : {
2293 329 : if (EQUALN(pszSQLCommand, "SELECT", 6) == FALSE ||
2294 : (strstr(pszSQLCommand, "from") == NULL && strstr(pszSQLCommand, "FROM") == NULL))
2295 : {
2296 158 : hResult = OGRPG_PQexec(hPGConn, pszSQLCommand, TRUE /* multiple allowed */ );
2297 158 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
2298 : {
2299 21 : CPLDebug( "PG", "Command Results Tuples = %d", PQntuples(hResult) );
2300 21 : FlushSoftTransaction();
2301 :
2302 : OGRSFDriver* poMemDriver = OGRSFDriverRegistrar::GetRegistrar()->
2303 21 : GetDriverByName("Memory");
2304 21 : if (poMemDriver)
2305 : {
2306 21 : OGRPGLayer* poResultLayer = new OGRPGNoResetResultLayer( this, hResult );
2307 21 : OGRDataSource* poMemDS = poMemDriver->CreateDataSource("");
2308 21 : poMemDS->CopyLayer(poResultLayer, "sql_statement");
2309 42 : OGRPGMemLayerWrapper* poResLayer = new OGRPGMemLayerWrapper(poMemDS);
2310 42 : delete poResultLayer;
2311 21 : return poResLayer;
2312 : }
2313 : else
2314 0 : return NULL;
2315 : }
2316 : }
2317 : else
2318 : {
2319 34 : CPLString osCommand;
2320 : osCommand.Printf( "DECLARE %s CURSOR for %s",
2321 34 : "executeSQLCursor", pszSQLCommand );
2322 :
2323 34 : hResult = OGRPG_PQexec(hPGConn, osCommand );
2324 :
2325 : /* -------------------------------------------------------------------- */
2326 : /* Do we have a tuple result? If so, instantiate a results */
2327 : /* layer for it. */
2328 : /* -------------------------------------------------------------------- */
2329 34 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
2330 : {
2331 34 : OGRPGResultLayer *poLayer = NULL;
2332 :
2333 34 : OGRPGClearResult( hResult );
2334 :
2335 34 : osCommand.Printf( "FETCH 0 in %s", "executeSQLCursor" );
2336 34 : hResult = OGRPG_PQexec(hPGConn, osCommand );
2337 :
2338 34 : poLayer = new OGRPGResultLayer( this, pszSQLCommand, hResult );
2339 :
2340 34 : OGRPGClearResult( hResult );
2341 :
2342 34 : if( poSpatialFilter != NULL )
2343 0 : poLayer->SetSpatialFilter( poSpatialFilter );
2344 :
2345 34 : return poLayer;
2346 0 : }
2347 : }
2348 : }
2349 :
2350 : /* -------------------------------------------------------------------- */
2351 : /* Generate an error report if an error occured. */
2352 : /* -------------------------------------------------------------------- */
2353 137 : if( !hResult ||
2354 : (PQresultStatus(hResult) == PGRES_NONFATAL_ERROR
2355 : || PQresultStatus(hResult) == PGRES_FATAL_ERROR ) )
2356 : {
2357 : CPLError( CE_Failure, CPLE_AppDefined,
2358 9 : "%s", PQerrorMessage( hPGConn ) );
2359 : }
2360 :
2361 137 : OGRPGClearResult( hResult );
2362 :
2363 137 : FlushSoftTransaction();
2364 :
2365 137 : return NULL;
2366 : }
2367 :
2368 : /************************************************************************/
2369 : /* ReleaseResultSet() */
2370 : /************************************************************************/
2371 :
2372 57 : void OGRPGDataSource::ReleaseResultSet( OGRLayer * poLayer )
2373 :
2374 : {
2375 57 : delete poLayer;
2376 57 : }
2377 :
2378 : /************************************************************************/
2379 : /* LaunderName() */
2380 : /************************************************************************/
2381 :
2382 228 : char *OGRPGDataSource::LaunderName( const char *pszSrcName )
2383 :
2384 : {
2385 228 : char *pszSafeName = CPLStrdup( pszSrcName );
2386 :
2387 22241 : for( int i = 0; pszSafeName[i] != '\0'; i++ )
2388 : {
2389 22013 : pszSafeName[i] = (char) tolower( pszSafeName[i] );
2390 22013 : if( pszSafeName[i] == '\'' || pszSafeName[i] == '-' || pszSafeName[i] == '#' )
2391 0 : pszSafeName[i] = '_';
2392 : }
2393 :
2394 228 : if( strcmp(pszSrcName,pszSafeName) != 0 )
2395 : CPLDebug("PG","LaunderName('%s') -> '%s'",
2396 44 : pszSrcName, pszSafeName);
2397 :
2398 228 : return pszSafeName;
2399 : }
2400 :
2401 : /************************************************************************/
2402 : /* StartCopy() */
2403 : /************************************************************************/
2404 8 : void OGRPGDataSource::StartCopy( OGRPGTableLayer *poPGLayer )
2405 : {
2406 8 : EndCopy();
2407 8 : poLayerInCopyMode = poPGLayer;
2408 8 : }
2409 :
2410 : /************************************************************************/
2411 : /* EndCopy() */
2412 : /************************************************************************/
2413 6150 : OGRErr OGRPGDataSource::EndCopy( )
2414 : {
2415 6150 : if( poLayerInCopyMode != NULL )
2416 : {
2417 8 : OGRErr result = poLayerInCopyMode->EndCopy();
2418 8 : poLayerInCopyMode = NULL;
2419 :
2420 8 : return result;
2421 : }
2422 : else
2423 6142 : return OGRERR_NONE;
2424 : }
2425 :
2426 : /************************************************************************/
2427 : /* CopyInProgress() */
2428 : /************************************************************************/
2429 0 : int OGRPGDataSource::CopyInProgress( )
2430 : {
2431 0 : return ( poLayerInCopyMode != NULL );
2432 : }
|