LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/pg - ogrpgdatasource.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 941 799 84.9 %
Date: 2011-12-18 Functions: 50 36 72.0 %

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

Generated by: LCOV version 1.7