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