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