LTP GCOV extension - code coverage report
Current view: directory - ogr/ogrsf_frmts/pg - ogrpgdatasource.cpp
Test: gdal_filtered.info
Date: 2010-07-12 Instrumented lines: 857
Code covered: 79.8 % Executed lines: 684

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

Generated by: LTP GCOV extension version 1.5