LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/pg - ogrpgdatasource.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 957 824 86.1 %
Date: 2013-03-30 Functions: 50 37 74.0 %

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

Generated by: LCOV version 1.7