LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pg - ogrpgdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1268 1451 87.4 %
Date: 2024-11-21 22:18:42 Functions: 56 62 90.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGRPGDataSource class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2000, Frank Warmerdam
       9             :  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include <string.h>
      15             : #include "ogr_pg.h"
      16             : #include "cpl_conv.h"
      17             : #include "cpl_string.h"
      18             : #include "cpl_hash_set.h"
      19             : #include <cctype>
      20             : #include <set>
      21             : 
      22             : #define PQexec this_is_an_error
      23             : 
      24             : static void OGRPGNoticeProcessor(void *arg, const char *pszMessage);
      25             : 
      26             : /************************************************************************/
      27             : /*                          OGRPGDataSource()                           */
      28             : /************************************************************************/
      29             : 
      30             : OGRPGDataSource::OGRPGDataSource() = default;
      31             : 
      32             : /************************************************************************/
      33             : /*                          ~OGRPGDataSource()                          */
      34             : /************************************************************************/
      35             : 
      36         838 : OGRPGDataSource::~OGRPGDataSource()
      37             : 
      38             : {
      39         419 :     OGRPGDataSource::FlushCache(true);
      40             : 
      41         419 :     CPLFree(pszForcedTables);
      42         419 :     CSLDestroy(papszSchemaList);
      43             : 
      44         885 :     for (int i = 0; i < nLayers; i++)
      45         466 :         delete papoLayers[i];
      46             : 
      47         419 :     CPLFree(papoLayers);
      48             : 
      49         419 :     if (hPGConn != nullptr)
      50             :     {
      51             :         // If there are prelude statements, don't mess with transactions.
      52         416 :         if (CSLFetchNameValue(papszOpenOptions, "PRELUDE_STATEMENTS") ==
      53             :             nullptr)
      54         408 :             FlushSoftTransaction();
      55             : 
      56             :         /* --------------------------------------------------------------------
      57             :          */
      58             :         /*      Send closing statements */
      59             :         /* --------------------------------------------------------------------
      60             :          */
      61             :         const char *pszClosingStatements =
      62         416 :             CSLFetchNameValue(papszOpenOptions, "CLOSING_STATEMENTS");
      63         416 :         if (pszClosingStatements != nullptr)
      64             :         {
      65             :             PGresult *hResult =
      66           6 :                 OGRPG_PQexec(hPGConn, pszClosingStatements, TRUE);
      67           6 :             OGRPGClearResult(hResult);
      68             :         }
      69             : 
      70             :         /* XXX - mloskot: After the connection is closed, valgrind still
      71             :          * reports 36 bytes definitely lost, somewhere in the libpq.
      72             :          */
      73         416 :         PQfinish(hPGConn);
      74         416 :         hPGConn = nullptr;
      75             :     }
      76         838 : }
      77             : 
      78             : /************************************************************************/
      79             : /*                              FlushCache()                            */
      80             : /************************************************************************/
      81             : 
      82        1750 : OGRErr OGRPGDataSource::FlushCacheWithRet(bool /* bAtClosing */)
      83             : {
      84        1750 :     OGRErr eErr = EndCopy();
      85        1750 :     if (eErr == OGRERR_NONE)
      86             :     {
      87        3153 :         for (int iLayer = 0; iLayer < nLayers; iLayer++)
      88             :         {
      89        1404 :             papoLayers[iLayer]->RunDeferredCreationIfNecessary();
      90             :         }
      91             :     }
      92        1750 :     return eErr;
      93             : }
      94             : 
      95        1694 : CPLErr OGRPGDataSource::FlushCache(bool bAtClosing)
      96             : {
      97        1694 :     return FlushCacheWithRet(bAtClosing) == OGRERR_NONE ? CE_None : CE_Failure;
      98             : }
      99             : 
     100             : /************************************************************************/
     101             : /*                         GetCurrentSchema()                           */
     102             : /************************************************************************/
     103             : 
     104         414 : CPLString OGRPGDataSource::GetCurrentSchema()
     105             : {
     106             :     /* -------------------------------------------- */
     107             :     /*          Get the current schema              */
     108             :     /* -------------------------------------------- */
     109         414 :     PGresult *hResult = OGRPG_PQexec(hPGConn, "SELECT current_schema()");
     110         414 :     if (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0))
     111             :     {
     112         414 :         osCurrentSchema = PQgetvalue(hResult, 0, 0);
     113             :     }
     114         414 :     OGRPGClearResult(hResult);
     115             : 
     116         828 :     return osCurrentSchema;
     117             : }
     118             : 
     119             : /************************************************************************/
     120             : /*                      OGRPGDecodeVersionString()                      */
     121             : /************************************************************************/
     122             : 
     123         646 : void OGRPGDataSource::OGRPGDecodeVersionString(PGver *psVersion,
     124             :                                                const char *pszVer)
     125             : {
     126             :     // Skip leading spaces
     127         646 :     while (*pszVer == ' ')
     128           0 :         pszVer++;
     129        1292 :     std::string osVer(pszVer);
     130             :     // And truncate at the first space
     131         646 :     const auto nPosSpace = osVer.find(' ');
     132         646 :     if (nPosSpace != std::string::npos)
     133         646 :         osVer.resize(nPosSpace);
     134             : 
     135         646 :     memset(psVersion, 0, sizeof(*psVersion));
     136        1292 :     const CPLStringList aosTokens(CSLTokenizeString2(osVer.c_str(), ".", 0));
     137         646 :     if (aosTokens.size() >= 1)
     138         646 :         psVersion->nMajor = atoi(aosTokens[0]);
     139         646 :     if (aosTokens.size() >= 2)
     140         646 :         psVersion->nMinor = atoi(aosTokens[1]);
     141         646 :     if (aosTokens.size() >= 3)
     142           0 :         psVersion->nRelease = atoi(aosTokens[2]);
     143         646 : }
     144             : 
     145             : /************************************************************************/
     146             : /*                     One entry for each PG table                      */
     147             : /************************************************************************/
     148             : 
     149             : struct PGTableEntry
     150             : {
     151             :     char *pszTableName = nullptr;
     152             :     char *pszSchemaName = nullptr;
     153             :     char *pszDescription = nullptr;
     154             :     int nGeomColumnCount = 0;
     155             :     PGGeomColumnDesc *pasGeomColumns = nullptr; /* list of geometry columns */
     156             :     int bDerivedInfoAdded =
     157             :         false; /* set to TRUE if it derives from another table */
     158             : };
     159             : 
     160        1418 : static unsigned long OGRPGHashTableEntry(const void *_psTableEntry)
     161             : {
     162        1418 :     const PGTableEntry *psTableEntry =
     163             :         static_cast<const PGTableEntry *>(_psTableEntry);
     164        2836 :     return CPLHashSetHashStr(CPLString().Printf(
     165        2836 :         "%s.%s", psTableEntry->pszSchemaName, psTableEntry->pszTableName));
     166             : }
     167             : 
     168         383 : static int OGRPGEqualTableEntry(const void *_psTableEntry1,
     169             :                                 const void *_psTableEntry2)
     170             : {
     171         383 :     const PGTableEntry *psTableEntry1 =
     172             :         static_cast<const PGTableEntry *>(_psTableEntry1);
     173         383 :     const PGTableEntry *psTableEntry2 =
     174             :         static_cast<const PGTableEntry *>(_psTableEntry2);
     175         383 :     return strcmp(psTableEntry1->pszTableName, psTableEntry2->pszTableName) ==
     176         760 :                0 &&
     177         377 :            strcmp(psTableEntry1->pszSchemaName, psTableEntry2->pszSchemaName) ==
     178         383 :                0;
     179             : }
     180             : 
     181         156 : static void OGRPGTableEntryAddGeomColumn(
     182             :     PGTableEntry *psTableEntry, const char *pszName,
     183             :     const char *pszGeomType = nullptr, int GeometryTypeFlags = 0,
     184             :     int nSRID = UNDETERMINED_SRID, PostgisType ePostgisType = GEOM_TYPE_UNKNOWN,
     185             :     int bNullable = TRUE)
     186             : {
     187         312 :     psTableEntry->pasGeomColumns = static_cast<PGGeomColumnDesc *>(CPLRealloc(
     188         156 :         psTableEntry->pasGeomColumns,
     189         156 :         sizeof(PGGeomColumnDesc) * (psTableEntry->nGeomColumnCount + 1)));
     190         312 :     psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].pszName =
     191         156 :         CPLStrdup(pszName);
     192         156 :     psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].pszGeomType =
     193         156 :         (pszGeomType) ? CPLStrdup(pszGeomType) : nullptr;
     194         156 :     psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount]
     195         156 :         .GeometryTypeFlags = GeometryTypeFlags;
     196             :     /* With PostGIS 2.0, querying geometry_columns can return 0, not only when
     197             :      */
     198             :     /* the SRID is truly set to 0, but also when there's no constraint */
     199         156 :     psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].nSRID =
     200         156 :         nSRID > 0 ? nSRID : UNDETERMINED_SRID;
     201         156 :     psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].ePostgisType =
     202             :         ePostgisType;
     203         156 :     psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].bNullable =
     204             :         bNullable;
     205         156 :     psTableEntry->nGeomColumnCount++;
     206         156 : }
     207             : 
     208         712 : static void OGRPGFreeTableEntry(void *_psTableEntry)
     209             : {
     210         712 :     PGTableEntry *psTableEntry = static_cast<PGTableEntry *>(_psTableEntry);
     211         712 :     CPLFree(psTableEntry->pszTableName);
     212         712 :     CPLFree(psTableEntry->pszSchemaName);
     213         712 :     CPLFree(psTableEntry->pszDescription);
     214         868 :     for (int i = 0; i < psTableEntry->nGeomColumnCount; i++)
     215             :     {
     216         156 :         CPLFree(psTableEntry->pasGeomColumns[i].pszName);
     217         156 :         CPLFree(psTableEntry->pasGeomColumns[i].pszGeomType);
     218             :     }
     219         712 :     CPLFree(psTableEntry->pasGeomColumns);
     220         712 :     CPLFree(psTableEntry);
     221         712 : }
     222             : 
     223         360 : static PGTableEntry *OGRPGFindTableEntry(CPLHashSet *hSetTables,
     224             :                                          const char *pszTableName,
     225             :                                          const char *pszSchemaName)
     226             : {
     227         360 :     PGTableEntry sEntry;
     228         360 :     sEntry.pszTableName = const_cast<char *>(pszTableName);
     229         360 :     sEntry.pszSchemaName = const_cast<char *>(pszSchemaName);
     230         720 :     return static_cast<PGTableEntry *>(CPLHashSetLookup(hSetTables, &sEntry));
     231             : }
     232             : 
     233         346 : static PGTableEntry *OGRPGAddTableEntry(CPLHashSet *hSetTables,
     234             :                                         const char *pszTableName,
     235             :                                         const char *pszSchemaName,
     236             :                                         const char *pszDescription)
     237             : {
     238             :     PGTableEntry *psEntry =
     239         346 :         static_cast<PGTableEntry *>(CPLCalloc(1, sizeof(PGTableEntry)));
     240         346 :     psEntry->pszTableName = CPLStrdup(pszTableName);
     241         346 :     psEntry->pszSchemaName = CPLStrdup(pszSchemaName);
     242         346 :     psEntry->pszDescription = CPLStrdup(pszDescription ? pszDescription : "");
     243             : 
     244         346 :     CPLHashSetInsert(hSetTables, psEntry);
     245             : 
     246         346 :     return psEntry;
     247             : }
     248             : 
     249             : /************************************************************************/
     250             : /*                                Open()                                */
     251             : /************************************************************************/
     252             : 
     253         419 : int OGRPGDataSource::Open(const char *pszNewName, int bUpdate, int bTestOpen,
     254             :                           char **papszOpenOptionsIn)
     255             : 
     256             : {
     257         419 :     CPLAssert(nLayers == 0);
     258         419 :     papszOpenOptions = CSLDuplicate(papszOpenOptionsIn);
     259             : 
     260             :     const char *pszPreludeStatements =
     261         419 :         CSLFetchNameValue(papszOpenOptions, "PRELUDE_STATEMENTS");
     262         419 :     if (pszPreludeStatements)
     263             :     {
     264             :         // If the prelude statements starts with BEGIN, then don't emit one
     265             :         // in our code.
     266           8 :         if (STARTS_WITH_CI(pszPreludeStatements, "BEGIN"))
     267           6 :             nSoftTransactionLevel = 1;
     268             :     }
     269             : 
     270             :     /* -------------------------------------------------------------------- */
     271             :     /*      Verify postgresql prefix.                                       */
     272             :     /* -------------------------------------------------------------------- */
     273         419 :     if (STARTS_WITH_CI(pszNewName, "PGB:"))
     274             :     {
     275             : #if defined(BINARY_CURSOR_ENABLED)
     276             :         bUseBinaryCursor = TRUE;
     277             :         CPLDebug("PG", "BINARY cursor is used for geometry fetching");
     278             : #endif
     279             :     }
     280         419 :     else if (!STARTS_WITH_CI(pszNewName, "PG:") &&
     281           2 :              !STARTS_WITH(pszNewName, "postgresql://"))
     282             :     {
     283           0 :         if (!bTestOpen)
     284           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     285             :                      "%s does not conform to PostgreSQL naming convention,"
     286             :                      " PG:* or postgresql://\n",
     287             :                      pszNewName);
     288           0 :         return FALSE;
     289             :     }
     290             : 
     291         414 :     const auto QuoteAndEscapeConnectionParam = [](const char *pszParam)
     292             :     {
     293         414 :         CPLString osRet("\'");
     294       10764 :         for (int i = 0; pszParam[i]; ++i)
     295             :         {
     296       10350 :             if (pszParam[i] == '\'')
     297           0 :                 osRet += "\\'";
     298       10350 :             else if (pszParam[i] == '\\')
     299           0 :                 osRet += "\\\\";
     300             :             else
     301       10350 :                 osRet += pszParam[i];
     302             :         }
     303         414 :         osRet += '\'';
     304         414 :         return osRet;
     305             :     };
     306             : 
     307         838 :     CPLString osConnectionName(pszNewName);
     308         419 :     if (osConnectionName.find("PG:postgresql://") == 0)
     309           1 :         osConnectionName = osConnectionName.substr(3);
     310         419 :     const bool bIsURI = osConnectionName.find("postgresql://") == 0;
     311             : 
     312         419 :     const char *const apszOpenOptions[] = {
     313             :         "service", "dbname", "port", "user", "password", "host",
     314             :         // Non-postgreSQL options
     315             :         "active_schema", "schemas", "tables"};
     316         838 :     std::string osSchemas;
     317         838 :     std::string osForcedTables;
     318        4190 :     for (const char *pszOpenOption : apszOpenOptions)
     319             :     {
     320        3771 :         const char *pszVal = CSLFetchNameValue(papszOpenOptions, pszOpenOption);
     321        3771 :         if (pszVal && strcmp(pszOpenOption, "active_schema") == 0)
     322             :         {
     323           1 :             osActiveSchema = pszVal;
     324             :         }
     325        3770 :         else if (pszVal && strcmp(pszOpenOption, "schemas") == 0)
     326             :         {
     327           0 :             osSchemas = pszVal;
     328             :         }
     329        3770 :         else if (pszVal && strcmp(pszOpenOption, "tables") == 0)
     330             :         {
     331           0 :             osForcedTables = pszVal;
     332             :         }
     333        3770 :         else if (pszVal)
     334             :         {
     335           2 :             if (bIsURI)
     336             :             {
     337             :                 osConnectionName +=
     338           2 :                     osConnectionName.find('?') == std::string::npos ? '?' : '&';
     339             :             }
     340             :             else
     341             :             {
     342           0 :                 if (osConnectionName.back() != ':')
     343           0 :                     osConnectionName += ' ';
     344             :             }
     345           2 :             osConnectionName += pszOpenOption;
     346           2 :             osConnectionName += "=";
     347           2 :             if (bIsURI)
     348             :             {
     349           2 :                 char *pszTmp = CPLEscapeString(pszVal, -1, CPLES_URL);
     350           2 :                 osConnectionName += pszTmp;
     351           2 :                 CPLFree(pszTmp);
     352             :             }
     353             :             else
     354             :             {
     355           0 :                 osConnectionName += QuoteAndEscapeConnectionParam(pszVal);
     356             :             }
     357             :         }
     358             :     }
     359             : 
     360             :     /* -------------------------------------------------------------------- */
     361             :     /*      Set application name if not found in connection string          */
     362             :     /* -------------------------------------------------------------------- */
     363             : 
     364         836 :     if (strstr(pszNewName, "application_name") == nullptr &&
     365         417 :         getenv("PGAPPNAME") == nullptr)
     366             :     {
     367         417 :         if (bIsURI)
     368             :         {
     369             :             osConnectionName +=
     370           3 :                 osConnectionName.find('?') == std::string::npos ? '?' : '&';
     371             :         }
     372             :         else
     373             :         {
     374         414 :             if (osConnectionName.back() != ':')
     375         414 :                 osConnectionName += ' ';
     376             :         }
     377         417 :         osConnectionName += "application_name=";
     378         834 :         std::string osVal("GDAL ");
     379         417 :         osVal += GDALVersionInfo("RELEASE_NAME");
     380         417 :         if (bIsURI)
     381             :         {
     382           3 :             char *pszTmp = CPLEscapeString(osVal.c_str(), -1, CPLES_URL);
     383           3 :             osConnectionName += pszTmp;
     384           3 :             CPLFree(pszTmp);
     385             :         }
     386             :         else
     387             :         {
     388         414 :             osConnectionName += QuoteAndEscapeConnectionParam(osVal.c_str());
     389             :         }
     390             :     }
     391             : 
     392             :     const auto ParseAndRemoveParam =
     393        1248 :         [](char *pszStr, const char *pszParamName, std::string &osValue)
     394             :     {
     395        1248 :         const int nParamNameLen = static_cast<int>(strlen(pszParamName));
     396        1248 :         bool bInSingleQuotedString = false;
     397      154001 :         for (int i = 0; pszStr[i]; i++)
     398             :         {
     399      153172 :             if (bInSingleQuotedString)
     400             :             {
     401       21722 :                 if (pszStr[i] == '\\')
     402             :                 {
     403          12 :                     if (pszStr[i + 1] == '\\' || pszStr[i + 1] == '\'')
     404             :                     {
     405          12 :                         ++i;
     406             :                     }
     407             :                 }
     408       21710 :                 else if (pszStr[i] == '\'')
     409             :                 {
     410         841 :                     bInSingleQuotedString = false;
     411             :                 }
     412             :             }
     413      131450 :             else if (pszStr[i] == '\'')
     414             :             {
     415         841 :                 bInSingleQuotedString = true;
     416             :             }
     417      130609 :             else if (EQUALN(pszStr + i, pszParamName, nParamNameLen) &&
     418         419 :                      (pszStr[i + nParamNameLen] == '=' ||
     419           3 :                       pszStr[i + nParamNameLen] == ' '))
     420             :             {
     421         419 :                 const int iStart = i;
     422         419 :                 i += nParamNameLen;
     423         423 :                 while (pszStr[i] == ' ')
     424           4 :                     ++i;
     425         419 :                 if (pszStr[i] == '=')
     426             :                 {
     427         419 :                     ++i;
     428         425 :                     while (pszStr[i] == ' ')
     429           6 :                         ++i;
     430         419 :                     if (pszStr[i] == '\'')
     431             :                     {
     432           2 :                         ++i;
     433         106 :                         for (; pszStr[i]; i++)
     434             :                         {
     435         106 :                             if (pszStr[i] == '\\')
     436             :                             {
     437           0 :                                 if (pszStr[i + 1] == '\\' ||
     438           0 :                                     pszStr[i + 1] == '\'')
     439             :                                 {
     440           0 :                                     osValue += pszStr[i + 1];
     441           0 :                                     ++i;
     442             :                                 }
     443             :                             }
     444         106 :                             else if (pszStr[i] == '\'')
     445             :                             {
     446           2 :                                 ++i;
     447           2 :                                 break;
     448             :                             }
     449             :                             else
     450             :                             {
     451         104 :                                 osValue += pszStr[i];
     452             :                             }
     453             :                         }
     454             :                     }
     455             :                     else
     456             :                     {
     457       11962 :                         for (; pszStr[i] && pszStr[i] != ' '; i++)
     458             :                         {
     459       11545 :                             osValue += pszStr[i];
     460             :                         }
     461             :                     }
     462             : 
     463             :                     // Edit pszStr to remove the parameter and its value
     464         419 :                     if (pszStr[i] == ' ')
     465             :                     {
     466         417 :                         memmove(pszStr + iStart, pszStr + i,
     467         417 :                                 strlen(pszStr + i) + 1);
     468             :                     }
     469             :                     else
     470             :                     {
     471           2 :                         pszStr[iStart] = 0;
     472             :                     }
     473             :                 }
     474         419 :                 return true;
     475             :             }
     476             :         }
     477         829 :         return false;
     478             :     };
     479             : 
     480         419 :     char *pszConnectionName = CPLStrdup(osConnectionName);
     481         419 :     char *pszConnectionNameNoPrefix =
     482         838 :         pszConnectionName + (STARTS_WITH_CI(pszConnectionName, "PGB:")  ? 4
     483         419 :                              : STARTS_WITH_CI(pszConnectionName, "PG:") ? 3
     484             :                                                                         : 0);
     485             : 
     486             :     /* -------------------------------------------------------------------- */
     487             :     /*      Determine if the connection string contains an optional         */
     488             :     /*      ACTIVE_SCHEMA portion. If so, parse it out.                     */
     489             :     /* -------------------------------------------------------------------- */
     490         835 :     if (osActiveSchema.empty() && !bIsURI &&
     491         416 :         !ParseAndRemoveParam(pszConnectionNameNoPrefix, "active_schema",
     492             :                              osActiveSchema))
     493             :     {
     494         412 :         osActiveSchema = "public";
     495             :     }
     496             : 
     497             :     /* -------------------------------------------------------------------- */
     498             :     /*      Determine if the connection string contains an optional         */
     499             :     /*      SCHEMAS portion. If so, parse it out.                           */
     500             :     /* -------------------------------------------------------------------- */
     501         838 :     if (!osSchemas.empty() ||
     502         419 :         (!bIsURI &&
     503         416 :          ParseAndRemoveParam(pszConnectionNameNoPrefix, "schemas", osSchemas)))
     504             :     {
     505         409 :         papszSchemaList = CSLTokenizeString2(osSchemas.c_str(), ",", 0);
     506             : 
     507             :         /* If there is only one schema specified, make it the active schema */
     508         409 :         if (CSLCount(papszSchemaList) == 1)
     509             :         {
     510         407 :             osActiveSchema = papszSchemaList[0];
     511             :         }
     512             :     }
     513             : 
     514             :     /* -------------------------------------------------------------------- */
     515             :     /*      Determine if the connection string contains an optional         */
     516             :     /*      TABLES portion. If so, parse it out. The expected               */
     517             :     /*      connection string in this case will be, e.g.:                   */
     518             :     /*                                                                      */
     519             :     /*        'PG:dbname=warmerda user=warmerda tables=s1.t1,[s2.t2,...]    */
     520             :     /*              - where sN is schema and tN is table name               */
     521             :     /*      We must also strip this information from the connection         */
     522             :     /*      string; PQconnectdb() does not like unknown directives          */
     523             :     /* -------------------------------------------------------------------- */
     524         838 :     if (!osForcedTables.empty() ||
     525         419 :         (!bIsURI && ParseAndRemoveParam(pszConnectionNameNoPrefix, "tables",
     526             :                                         osForcedTables)))
     527             :     {
     528           6 :         pszForcedTables = CPLStrdup(osForcedTables.c_str());
     529             :     }
     530             : 
     531             :     /* -------------------------------------------------------------------- */
     532             :     /*      Try to establish connection.                                    */
     533             :     /* -------------------------------------------------------------------- */
     534         419 :     hPGConn = PQconnectdb(pszConnectionNameNoPrefix);
     535         419 :     CPLFree(pszConnectionName);
     536         419 :     pszConnectionName = nullptr;
     537             : 
     538         419 :     if (hPGConn == nullptr || PQstatus(hPGConn) == CONNECTION_BAD)
     539             :     {
     540           3 :         CPLError(CE_Failure, CPLE_AppDefined, "PQconnectdb failed.\n%s",
     541           3 :                  PQerrorMessage(hPGConn));
     542             : 
     543           3 :         PQfinish(hPGConn);
     544           3 :         hPGConn = nullptr;
     545             : 
     546           3 :         return FALSE;
     547             :     }
     548             : 
     549         416 :     bDSUpdate = bUpdate;
     550             : 
     551             :     /* -------------------------------------------------------------------- */
     552             :     /*      Send prelude statements                                         */
     553             :     /* -------------------------------------------------------------------- */
     554         416 :     if (pszPreludeStatements != nullptr)
     555             :     {
     556           8 :         PGresult *hResult = OGRPG_PQexec(hPGConn, pszPreludeStatements, TRUE);
     557           8 :         if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
     558             :         {
     559           2 :             OGRPGClearResult(hResult);
     560           2 :             return FALSE;
     561             :         }
     562             : 
     563           6 :         OGRPGClearResult(hResult);
     564             :     }
     565             : 
     566             :     /* -------------------------------------------------------------------- */
     567             :     /*      Set the encoding to UTF8 as the driver advertises UTF8          */
     568             :     /*      unless PGCLIENTENCODING is defined                              */
     569             :     /* -------------------------------------------------------------------- */
     570         414 :     if (CPLGetConfigOption("PGCLIENTENCODING", nullptr) == nullptr)
     571             :     {
     572         412 :         const char *encoding = "UTF8";
     573         412 :         if (PQsetClientEncoding(hPGConn, encoding) == -1)
     574             :         {
     575           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     576             :                      "PQsetClientEncoding(%s) failed.\n%s", encoding,
     577           0 :                      PQerrorMessage(hPGConn));
     578             :         }
     579             :     }
     580             : 
     581             :     {
     582         414 :         PGresult *hResult = OGRPG_PQexec(hPGConn, "SHOW client_encoding");
     583         828 :         if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
     584         414 :             PQntuples(hResult) == 1)
     585             :         {
     586         414 :             const char *pszClientEncoding = PQgetvalue(hResult, 0, 0);
     587         414 :             if (pszClientEncoding)
     588             :             {
     589         414 :                 CPLDebug("PG", "Client encoding: '%s'", pszClientEncoding);
     590         414 :                 if (EQUAL(pszClientEncoding, "UTF8"))
     591             :                 {
     592         412 :                     m_bUTF8ClientEncoding = true;
     593             :                 }
     594             :             }
     595             :         }
     596         414 :         OGRPGClearResult(hResult);
     597             :     }
     598             : 
     599             :     /* -------------------------------------------------------------------- */
     600             :     /*      Install a notice processor.                                     */
     601             :     /* -------------------------------------------------------------------- */
     602         414 :     PQsetNoticeProcessor(hPGConn, OGRPGNoticeProcessor, this);
     603             : 
     604             :     /* -------------------------------------------------------------------- */
     605             :     /*      Detect PostGIS schema                                           */
     606             :     /* -------------------------------------------------------------------- */
     607         828 :     CPLString osPostgisSchema;
     608             :     {
     609         414 :         PGresult *hResult = OGRPG_PQexec(
     610             :             hPGConn,
     611             :             "SELECT n.nspname FROM pg_proc p JOIN pg_namespace n "
     612         414 :             "ON n.oid = p.pronamespace WHERE proname = 'postgis_version'");
     613         828 :         if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
     614         414 :             PQntuples(hResult) > 0)
     615             :         {
     616         414 :             const char *pszPostgisSchema = PQgetvalue(hResult, 0, 0);
     617             : 
     618         414 :             CPLDebug("PG", "PostGIS schema: '%s'", pszPostgisSchema);
     619             : 
     620         414 :             osPostgisSchema = pszPostgisSchema;
     621             :         }
     622         414 :         OGRPGClearResult(hResult);
     623             :     }
     624             : 
     625             :     /* -------------------------------------------------------------------- */
     626             :     /*      Get search_path                                                 */
     627             :     /* -------------------------------------------------------------------- */
     628         828 :     std::string osSearchPath = "public";
     629             :     {
     630         414 :         PGresult *hResult = OGRPG_PQexec(hPGConn, "SHOW search_path");
     631         828 :         if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
     632         414 :             PQntuples(hResult) > 0)
     633             :         {
     634         414 :             const char *pszVal = PQgetvalue(hResult, 0, 0);
     635         414 :             if (pszVal)
     636             :             {
     637         414 :                 osSearchPath = pszVal;
     638             :             }
     639             :         }
     640         414 :         OGRPGClearResult(hResult);
     641             :     }
     642             : 
     643             :     /* -------------------------------------------------------------------- */
     644             :     /*      Set active schema if different from 'public'. Also add          */
     645             :     /*      postgis schema if needed.                                       */
     646             :     /* -------------------------------------------------------------------- */
     647         417 :     if (osActiveSchema != "public" ||
     648           6 :         (!osPostgisSchema.empty() &&
     649           3 :          osSearchPath.find(osPostgisSchema) == std::string::npos))
     650             :     {
     651         411 :         std::string osNewSearchPath;
     652         411 :         if (osActiveSchema != "public")
     653             :         {
     654             :             osNewSearchPath +=
     655         411 :                 OGRPGEscapeString(hPGConn, osActiveSchema.c_str());
     656             :         }
     657         411 :         if (!osSearchPath.empty() && osSearchPath != "\"\"")
     658             :         {
     659         410 :             if (!osNewSearchPath.empty())
     660         410 :                 osNewSearchPath += ',';
     661         410 :             osNewSearchPath += osSearchPath;
     662             :         }
     663         822 :         if (!osPostgisSchema.empty() &&
     664         411 :             osSearchPath.find(osPostgisSchema) == std::string::npos)
     665             :         {
     666           1 :             if (!osNewSearchPath.empty())
     667           1 :                 osNewSearchPath += ',';
     668             :             osNewSearchPath +=
     669           1 :                 OGRPGEscapeString(hPGConn, osPostgisSchema.c_str());
     670             :         }
     671         411 :         CPLDebug("PG", "Modifying search_path from %s to %s",
     672             :                  osSearchPath.c_str(), osNewSearchPath.c_str());
     673             : 
     674         411 :         std::string osCommand = "SET search_path=" + osNewSearchPath;
     675         411 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     676             : 
     677         411 :         if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
     678             :         {
     679           0 :             OGRPGClearResult(hResult);
     680           0 :             CPLDebug("PG", "Command \"%s\" failed. Trying without 'public'.",
     681             :                      osCommand.c_str());
     682             :             osCommand = CPLSPrintf(
     683             :                 "SET search_path=%s",
     684           0 :                 OGRPGEscapeString(hPGConn, osActiveSchema.c_str()).c_str());
     685           0 :             PGresult *hResult2 = OGRPG_PQexec(hPGConn, osCommand.c_str());
     686             : 
     687           0 :             if (!hResult2 || PQresultStatus(hResult2) != PGRES_COMMAND_OK)
     688             :             {
     689           0 :                 OGRPGClearResult(hResult2);
     690             : 
     691           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s",
     692           0 :                          PQerrorMessage(hPGConn));
     693             : 
     694           0 :                 return FALSE;
     695             :             }
     696             :         }
     697             : 
     698         411 :         OGRPGClearResult(hResult);
     699             :     }
     700             : 
     701             :     /* -------------------------------------------------------------------- */
     702             :     /*      Find out PostgreSQL version                                     */
     703             :     /* -------------------------------------------------------------------- */
     704         414 :     sPostgreSQLVersion.nMajor = -1;
     705         414 :     sPostgreSQLVersion.nMinor = -1;
     706         414 :     sPostgreSQLVersion.nRelease = -1;
     707             : 
     708         414 :     PGresult *hResult = OGRPG_PQexec(hPGConn, "SELECT version()");
     709         828 :     if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
     710         414 :         PQntuples(hResult) > 0)
     711             :     {
     712         414 :         char *pszVer = PQgetvalue(hResult, 0, 0);
     713             : 
     714         414 :         CPLDebug("PG", "PostgreSQL version string : '%s'", pszVer);
     715             : 
     716             :         /* Should work with "PostgreSQL X.Y.Z ..." or "EnterpriseDB X.Y.Z ..."
     717             :          */
     718         414 :         const char *pszSpace = strchr(pszVer, ' ');
     719         414 :         if (pszSpace != nullptr &&
     720         414 :             isdigit(static_cast<unsigned char>(pszSpace[1])))
     721             :         {
     722         414 :             OGRPGDecodeVersionString(&sPostgreSQLVersion, pszSpace + 1);
     723             :         }
     724             :     }
     725         414 :     OGRPGClearResult(hResult);
     726         414 :     CPLAssert(nullptr ==
     727             :               hResult); /* Test if safe PQclear has not been broken */
     728             : 
     729             :     /* -------------------------------------------------------------------- */
     730             :     /*      Set standard_conforming_strings=ON                              */
     731             :     /* -------------------------------------------------------------------- */
     732             : 
     733         414 :     hResult = OGRPG_PQexec(hPGConn, "SET standard_conforming_strings = ON");
     734         414 :     if (!(hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK))
     735             :     {
     736           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
     737           0 :         OGRPGClearResult(hResult);
     738           0 :         return FALSE;
     739             :     }
     740         414 :     OGRPGClearResult(hResult);
     741             : 
     742             : /* -------------------------------------------------------------------- */
     743             : /*      Test if time binary format is int8 or float8                    */
     744             : /* -------------------------------------------------------------------- */
     745             : #if defined(BINARY_CURSOR_ENABLED)
     746             :     if (bUseBinaryCursor)
     747             :     {
     748             :         SoftStartTransaction();
     749             : 
     750             :         hResult =
     751             :             OGRPG_PQexec(hPGConn, "DECLARE gettimebinaryformat BINARY CURSOR "
     752             :                                   "FOR SELECT CAST ('00:00:01' AS time)");
     753             : 
     754             :         if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
     755             :         {
     756             :             OGRPGClearResult(hResult);
     757             : 
     758             :             hResult = OGRPG_PQexec(hPGConn, "FETCH ALL IN gettimebinaryformat");
     759             : 
     760             :             if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
     761             :                 PQntuples(hResult) == 1)
     762             :             {
     763             :                 if (PQfformat(hResult, 0) == 1)  // Binary data representation
     764             :                 {
     765             :                     CPLAssert(PQgetlength(hResult, 0, 0) == 8);
     766             :                     unsigned int nVal[2] = {0, 0};
     767             :                     memcpy(nVal, PQgetvalue(hResult, 0, 0), 8);
     768             :                     CPL_MSBPTR32(&nVal[0]);
     769             :                     CPL_MSBPTR32(&nVal[1]);
     770             :                     double dVal = 0.0;
     771             :                     memcpy(&dVal, PQgetvalue(hResult, 0, 0), 8);
     772             :                     CPL_MSBPTR64(&dVal);
     773             :                     if (nVal[0] == 0 && nVal[1] == 1000000)
     774             :                     {
     775             :                         bBinaryTimeFormatIsInt8 = TRUE;
     776             :                         CPLDebug("PG", "Time binary format is int8");
     777             :                     }
     778             :                     else if (dVal == 1.)
     779             :                     {
     780             :                         bBinaryTimeFormatIsInt8 = FALSE;
     781             :                         CPLDebug("PG", "Time binary format is float8");
     782             :                     }
     783             :                     else
     784             :                     {
     785             :                         bBinaryTimeFormatIsInt8 = FALSE;
     786             :                         CPLDebug("PG", "Time binary format is unknown");
     787             :                     }
     788             :                 }
     789             :             }
     790             :         }
     791             : 
     792             :         OGRPGClearResult(hResult);
     793             : 
     794             :         hResult = OGRPG_PQexec(hPGConn, "CLOSE gettimebinaryformat");
     795             :         OGRPGClearResult(hResult);
     796             : 
     797             :         SoftCommitTransaction();
     798             :     }
     799             : #endif
     800             : 
     801             :     /* -------------------------------------------------------------------- */
     802             :     /*      Test to see if this database instance has support for the       */
     803             :     /*      PostGIS Geometry type.  If so, disable sequential scanning      */
     804             :     /*      so we will get the value of the gist indexes.                   */
     805             :     /* -------------------------------------------------------------------- */
     806         414 :     hResult =
     807         414 :         OGRPG_PQexec(hPGConn, "SELECT oid, typname FROM pg_type WHERE typname "
     808             :                               "IN ('geometry', 'geography') AND typtype='b'");
     809             : 
     810         414 :     if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
     811        1242 :         PQntuples(hResult) > 0 &&
     812         414 :         CPLTestBool(CPLGetConfigOption("PG_USE_POSTGIS", "YES")))
     813             :     {
     814         696 :         for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
     815             :         {
     816         464 :             const char *pszOid = PQgetvalue(hResult, iRecord, 0);
     817         464 :             const char *pszTypname = PQgetvalue(hResult, iRecord, 1);
     818         464 :             if (EQUAL(pszTypname, "geometry"))
     819             :             {
     820         232 :                 bHavePostGIS = TRUE;
     821         232 :                 nGeometryOID = static_cast<Oid>(strtoul(pszOid, nullptr, 10));
     822             :             }
     823         232 :             else if (CPLTestBool(CPLGetConfigOption("PG_USE_GEOGRAPHY", "YES")))
     824             :             {
     825         232 :                 bHaveGeography = TRUE;
     826         232 :                 nGeographyOID = static_cast<Oid>(strtoul(pszOid, nullptr, 10));
     827             :             }
     828             :         }
     829             :     }
     830             : 
     831         414 :     OGRPGClearResult(hResult);
     832             : 
     833             :     /* -------------------------------------------------------------------- */
     834             :     /*      Find out PostGIS version                                        */
     835             :     /* -------------------------------------------------------------------- */
     836             : 
     837         414 :     sPostGISVersion.nMajor = -1;
     838         414 :     sPostGISVersion.nMinor = -1;
     839         414 :     sPostGISVersion.nRelease = -1;
     840             : 
     841         414 :     if (bHavePostGIS)
     842             :     {
     843         232 :         hResult = OGRPG_PQexec(hPGConn, "SELECT postgis_version()");
     844         464 :         if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
     845         232 :             PQntuples(hResult) > 0)
     846             :         {
     847         232 :             char *pszVer = PQgetvalue(hResult, 0, 0);
     848             : 
     849         232 :             CPLDebug("PG", "PostGIS version string : '%s'", pszVer);
     850             : 
     851         232 :             OGRPGDecodeVersionString(&sPostGISVersion, pszVer);
     852             :         }
     853         232 :         OGRPGClearResult(hResult);
     854             :     }
     855             : 
     856         414 :     m_bHasGeometryColumns =
     857         414 :         OGRPG_Check_Table_Exists(hPGConn, "geometry_columns");
     858         414 :     m_bHasSpatialRefSys = OGRPG_Check_Table_Exists(hPGConn, "spatial_ref_sys");
     859             : 
     860             :     /* -------------------------------------------------------------------- */
     861             :     /*      Find out "unknown SRID" value                                   */
     862             :     /* -------------------------------------------------------------------- */
     863             : 
     864         414 :     if (sPostGISVersion.nMajor >= 2)
     865             :     {
     866         232 :         hResult =
     867         232 :             OGRPG_PQexec(hPGConn, "SELECT ST_Srid('POINT EMPTY'::GEOMETRY)");
     868             : 
     869         464 :         if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
     870         232 :             PQntuples(hResult) > 0)
     871             :         {
     872         232 :             nUndefinedSRID = atoi(PQgetvalue(hResult, 0, 0));
     873             :         }
     874             : 
     875         232 :         OGRPGClearResult(hResult);
     876             :     }
     877             :     else
     878         182 :         nUndefinedSRID = -1;
     879             : 
     880         414 :     GetCurrentSchema();
     881             : 
     882         828 :     bListAllTables = CPLTestBool(
     883         414 :         CSLFetchNameValueDef(papszOpenOptions, "LIST_ALL_TABLES",
     884             :                              CPLGetConfigOption("PG_LIST_ALL_TABLES", "NO")));
     885             : 
     886         828 :     m_bSkipViews = CPLTestBool(
     887         414 :         CSLFetchNameValueDef(papszOpenOptions, "SKIP_VIEWS",
     888             :                              CPLGetConfigOption("PG_SKIP_VIEWS", "NO")));
     889             : 
     890         414 :     return TRUE;
     891             : }
     892             : 
     893             : /************************************************************************/
     894             : /*                            LoadTables()                              */
     895             : /************************************************************************/
     896             : 
     897        1243 : void OGRPGDataSource::LoadTables()
     898             : {
     899        1243 :     if (bHasLoadTables)
     900         970 :         return;
     901         273 :     bHasLoadTables = TRUE;
     902             : 
     903         273 :     PGTableEntry **papsTables = nullptr;
     904         273 :     int nTableCount = 0;
     905         273 :     CPLHashSet *hSetTables = nullptr;
     906         546 :     std::set<CPLString> osRegisteredLayers;
     907             : 
     908         307 :     for (int i = 0; i < nLayers; i++)
     909             :     {
     910          34 :         osRegisteredLayers.insert(papoLayers[i]->GetName());
     911             :     }
     912             : 
     913         273 :     if (pszForcedTables)
     914             :     {
     915           4 :         char **papszTableList = CSLTokenizeString2(pszForcedTables, ",", 0);
     916             : 
     917          10 :         for (int i = 0; i < CSLCount(papszTableList); i++)
     918             :         {
     919             :             // Get schema and table name
     920             :             char **papszQualifiedParts =
     921           6 :                 CSLTokenizeString2(papszTableList[i], ".", 0);
     922           6 :             int nParts = CSLCount(papszQualifiedParts);
     923             : 
     924           6 :             if (nParts == 1 || nParts == 2)
     925             :             {
     926             :                 /* Find the geometry column name if specified */
     927           6 :                 char *pszGeomColumnName = nullptr;
     928           6 :                 char *pos = strchr(
     929           6 :                     papszQualifiedParts[CSLCount(papszQualifiedParts) - 1],
     930             :                     '(');
     931           6 :                 if (pos != nullptr)
     932             :                 {
     933           0 :                     *pos = '\0';
     934           0 :                     pszGeomColumnName = pos + 1;
     935           0 :                     int len = static_cast<int>(strlen(pszGeomColumnName));
     936           0 :                     if (len > 0)
     937           0 :                         pszGeomColumnName[len - 1] = '\0';
     938             :                 }
     939             : 
     940          12 :                 papsTables = static_cast<PGTableEntry **>(CPLRealloc(
     941           6 :                     papsTables, sizeof(PGTableEntry *) * (nTableCount + 1)));
     942          12 :                 papsTables[nTableCount] = static_cast<PGTableEntry *>(
     943           6 :                     CPLCalloc(1, sizeof(PGTableEntry)));
     944           6 :                 if (pszGeomColumnName)
     945           0 :                     OGRPGTableEntryAddGeomColumn(papsTables[nTableCount],
     946             :                                                  pszGeomColumnName);
     947             : 
     948           6 :                 if (nParts == 2)
     949             :                 {
     950           0 :                     papsTables[nTableCount]->pszSchemaName =
     951           0 :                         CPLStrdup(papszQualifiedParts[0]);
     952           0 :                     papsTables[nTableCount]->pszTableName =
     953           0 :                         CPLStrdup(papszQualifiedParts[1]);
     954             :                 }
     955             :                 else
     956             :                 {
     957          12 :                     papsTables[nTableCount]->pszSchemaName =
     958           6 :                         CPLStrdup(osActiveSchema.c_str());
     959          12 :                     papsTables[nTableCount]->pszTableName =
     960           6 :                         CPLStrdup(papszQualifiedParts[0]);
     961             :                 }
     962           6 :                 nTableCount++;
     963             :             }
     964             : 
     965           6 :             CSLDestroy(papszQualifiedParts);
     966             :         }
     967             : 
     968           4 :         CSLDestroy(papszTableList);
     969             :     }
     970             : 
     971             :     /* -------------------------------------------------------------------- */
     972             :     /*      Get a list of available tables if they have not been            */
     973             :     /*      specified through the TABLES connection string param           */
     974             :     /* -------------------------------------------------------------------- */
     975         273 :     const char *pszAllowedRelations = m_bSkipViews ? "'r'" : "'r','v','m','f'";
     976             : 
     977         273 :     hSetTables = CPLHashSetNew(OGRPGHashTableEntry, OGRPGEqualTableEntry,
     978             :                                OGRPGFreeTableEntry);
     979             : 
     980         269 :     if (nTableCount == 0 && bHavePostGIS && sPostGISVersion.nMajor >= 2 &&
     981         693 :         !bListAllTables &&
     982             :         /* Config option mostly for comparison/debugging/etc... */
     983         151 :         CPLTestBool(CPLGetConfigOption("PG_USE_POSTGIS2_OPTIM", "YES")))
     984             :     {
     985             :         /* --------------------------------------------------------------------
     986             :          */
     987             :         /*      With PostGIS 2.0, the geometry_columns and geography_columns */
     988             :         /*      are views, based on the catalog system, that can be slow to */
     989             :         /*      query, so query directly the catalog system. */
     990             :         /*      See http://trac.osgeo.org/postgis/ticket/3092 */
     991             :         /* --------------------------------------------------------------------
     992             :          */
     993         150 :         CPLString osCommand;
     994         300 :         const char *pszConstraintDef = sPostgreSQLVersion.nMajor >= 12
     995         150 :                                            ? "pg_get_constraintdef(s.oid)"
     996             :                                            : "s.consrc";
     997             :         osCommand.Printf(
     998             :             "SELECT c.relname, n.nspname, c.relkind, a.attname, t.typname, "
     999             :             "postgis_typmod_dims(a.atttypmod) dim, "
    1000             :             "postgis_typmod_srid(a.atttypmod) srid, "
    1001             :             "postgis_typmod_type(a.atttypmod)::text geomtyp, "
    1002             :             "array_agg(%s)::text att_constraints, a.attnotnull, "
    1003             :             "d.description "
    1004             :             "FROM pg_class c JOIN pg_attribute a ON a.attrelid=c.oid "
    1005             :             "JOIN pg_namespace n ON c.relnamespace = n.oid "
    1006             :             "AND c.relkind in (%s) AND NOT ( n.nspname = 'public' AND "
    1007             :             "c.relname = 'raster_columns' ) "
    1008             :             "JOIN pg_type t ON a.atttypid = t.oid AND (t.typname = "
    1009             :             "'geometry'::name OR t.typname = 'geography'::name) "
    1010             :             "LEFT JOIN pg_constraint s ON s.connamespace = n.oid AND "
    1011             :             "s.conrelid = c.oid "
    1012             :             "AND a.attnum = ANY (s.conkey) "
    1013             :             "AND (%s LIKE '%%geometrytype(%% = %%' OR %s LIKE '%%ndims(%% = "
    1014             :             "%%' OR %s LIKE '%%srid(%% = %%') "
    1015             :             "LEFT JOIN pg_description d ON d.objoid = c.oid AND d.classoid = "
    1016             :             "'pg_class'::regclass::oid AND d.objsubid = 0 "
    1017             :             "GROUP BY c.relname, n.nspname, c.relkind, a.attname, t.typname, "
    1018             :             "dim, srid, geomtyp, a.attnotnull, c.oid, a.attnum, d.description "
    1019             :             "ORDER BY c.oid, a.attnum",
    1020             :             pszConstraintDef, pszAllowedRelations, pszConstraintDef,
    1021         150 :             pszConstraintDef, pszConstraintDef);
    1022         150 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    1023             : 
    1024         150 :         if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
    1025             :         {
    1026           0 :             OGRPGClearResult(hResult);
    1027             : 
    1028           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
    1029           0 :                      PQerrorMessage(hPGConn));
    1030           0 :             goto end;
    1031             :         }
    1032             :         /* --------------------------------------------------------------------
    1033             :          */
    1034             :         /*      Parse the returned table list */
    1035             :         /* --------------------------------------------------------------------
    1036             :          */
    1037         226 :         for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
    1038             :         {
    1039          76 :             const char *pszTable = PQgetvalue(hResult, iRecord, 0);
    1040          76 :             const char *pszSchemaName = PQgetvalue(hResult, iRecord, 1);
    1041          76 :             const char *pszGeomColumnName = PQgetvalue(hResult, iRecord, 3);
    1042          76 :             const char *pszGeomOrGeography = PQgetvalue(hResult, iRecord, 4);
    1043          76 :             const char *pszDim = PQgetvalue(hResult, iRecord, 5);
    1044          76 :             const char *pszSRID = PQgetvalue(hResult, iRecord, 6);
    1045          76 :             const char *pszGeomType = PQgetvalue(hResult, iRecord, 7);
    1046          76 :             const char *pszConstraint = PQgetvalue(hResult, iRecord, 8);
    1047          76 :             const char *pszNotNull = PQgetvalue(hResult, iRecord, 9);
    1048          76 :             const char *pszDescription = PQgetvalue(hResult, iRecord, 10);
    1049             :             /*const char *pszRelkind = PQgetvalue(hResult, iRecord, 2);
    1050             :             CPLDebug("PG", "%s %s %s %s %s %s %s %s %s %s",
    1051             :                      pszTable, pszSchemaName, pszRelkind,
    1052             :                      pszGeomColumnName, pszGeomOrGeography, pszDim,
    1053             :                      pszSRID, pszGeomType, pszConstraint, pszNotNull);*/
    1054             : 
    1055          76 :             int bNullable = EQUAL(pszNotNull, "f");
    1056             : 
    1057          76 :             PostgisType ePostgisType = GEOM_TYPE_UNKNOWN;
    1058          76 :             if (EQUAL(pszGeomOrGeography, "geometry"))
    1059          71 :                 ePostgisType = GEOM_TYPE_GEOMETRY;
    1060           5 :             else if (EQUAL(pszGeomOrGeography, "geography"))
    1061           5 :                 ePostgisType = GEOM_TYPE_GEOGRAPHY;
    1062             : 
    1063          76 :             int nGeomCoordDimension = atoi(pszDim);
    1064          76 :             bool bHasM = pszGeomType[strlen(pszGeomType) - 1] == 'M';
    1065          76 :             int nSRID = atoi(pszSRID);
    1066             : 
    1067             :             /* Analyze constraints that might override geometrytype, */
    1068             :             /* coordinate dimension and SRID */
    1069         152 :             CPLString osConstraint(pszConstraint);
    1070          76 :             osConstraint = osConstraint.tolower();
    1071          76 :             pszConstraint = osConstraint.c_str();
    1072          76 :             const char *pszNeedle = strstr(pszConstraint, "geometrytype(");
    1073         152 :             CPLString osGeometryType;
    1074          76 :             if (pszNeedle)
    1075             :             {
    1076           4 :                 pszNeedle = strchr(pszNeedle, '\'');
    1077           4 :                 if (pszNeedle)
    1078             :                 {
    1079           4 :                     pszNeedle++;
    1080           4 :                     const char *pszEnd = strchr(pszNeedle, '\'');
    1081           4 :                     if (pszEnd)
    1082             :                     {
    1083           4 :                         osGeometryType = pszNeedle;
    1084           4 :                         osGeometryType.resize(pszEnd - pszNeedle);
    1085           4 :                         pszGeomType = osGeometryType.c_str();
    1086           4 :                         bHasM = pszGeomType[strlen(pszGeomType) - 1] == 'M';
    1087             :                     }
    1088             :                 }
    1089             :             }
    1090             : 
    1091          76 :             pszNeedle = strstr(pszConstraint, "srid(");
    1092          76 :             if (pszNeedle)
    1093             :             {
    1094           4 :                 pszNeedle = strchr(pszNeedle, '=');
    1095           4 :                 if (pszNeedle)
    1096             :                 {
    1097           4 :                     pszNeedle++;
    1098           4 :                     nSRID = atoi(pszNeedle);
    1099             :                 }
    1100             :             }
    1101             : 
    1102          76 :             pszNeedle = strstr(pszConstraint, "ndims(");
    1103          76 :             if (pszNeedle)
    1104             :             {
    1105           4 :                 pszNeedle = strchr(pszNeedle, '=');
    1106           4 :                 if (pszNeedle)
    1107             :                 {
    1108           4 :                     pszNeedle++;
    1109           4 :                     nGeomCoordDimension = atoi(pszNeedle);
    1110             :                 }
    1111             :             }
    1112             : 
    1113          76 :             int GeomTypeFlags = 0;
    1114          76 :             if (nGeomCoordDimension == 3)
    1115             :             {
    1116          26 :                 if (bHasM)
    1117           0 :                     GeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
    1118             :                 else
    1119          26 :                     GeomTypeFlags |= OGRGeometry::OGR_G_3D;
    1120             :             }
    1121          50 :             else if (nGeomCoordDimension == 4)
    1122             :             {
    1123           3 :                 GeomTypeFlags |=
    1124             :                     OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
    1125             :             }
    1126             : 
    1127         152 :             papsTables = static_cast<PGTableEntry **>(CPLRealloc(
    1128          76 :                 papsTables, sizeof(PGTableEntry *) * (nTableCount + 1)));
    1129         152 :             papsTables[nTableCount] =
    1130          76 :                 static_cast<PGTableEntry *>(CPLCalloc(1, sizeof(PGTableEntry)));
    1131          76 :             papsTables[nTableCount]->pszTableName = CPLStrdup(pszTable);
    1132          76 :             papsTables[nTableCount]->pszSchemaName = CPLStrdup(pszSchemaName);
    1133         152 :             papsTables[nTableCount]->pszDescription =
    1134          76 :                 CPLStrdup(pszDescription ? pszDescription : "");
    1135             : 
    1136          76 :             OGRPGTableEntryAddGeomColumn(
    1137          76 :                 papsTables[nTableCount], pszGeomColumnName, pszGeomType,
    1138             :                 GeomTypeFlags, nSRID, ePostgisType, bNullable);
    1139          76 :             nTableCount++;
    1140             : 
    1141             :             PGTableEntry *psEntry =
    1142          76 :                 OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName);
    1143          76 :             if (psEntry == nullptr)
    1144          62 :                 psEntry = OGRPGAddTableEntry(hSetTables, pszTable,
    1145             :                                              pszSchemaName, pszDescription);
    1146          76 :             OGRPGTableEntryAddGeomColumn(psEntry, pszGeomColumnName,
    1147             :                                          pszGeomType, GeomTypeFlags, nSRID,
    1148             :                                          ePostgisType, bNullable);
    1149             :         }
    1150             : 
    1151         150 :         OGRPGClearResult(hResult);
    1152             :     }
    1153         123 :     else if (nTableCount == 0)
    1154             :     {
    1155         119 :         CPLString osCommand;
    1156             : 
    1157             :         /* Caution : in PostGIS case, the result has 11 columns, whereas in the
    1158             :          */
    1159             :         /* non-PostGIS case it has only 3 columns */
    1160         119 :         if (bHavePostGIS && !bListAllTables)
    1161             :         {
    1162             :             osCommand.Printf(
    1163             :                 "SELECT c.relname, n.nspname, g.f_geometry_column, "
    1164             :                 "g.type, g.coord_dimension, g.srid, %d, a.attnotnull, "
    1165             :                 "d.description, c.oid as oid, a.attnum as attnum "
    1166             :                 "FROM pg_class c "
    1167             :                 "JOIN pg_namespace n ON c.relnamespace=n.oid "
    1168             :                 "JOIN geometry_columns g "
    1169             :                 "ON c.relname::TEXT = g.f_table_name::TEXT AND n.nspname = "
    1170             :                 "g.f_table_schema "
    1171             :                 "JOIN pg_attribute a "
    1172             :                 "ON a.attname = g.f_geometry_column AND a.attrelid = c.oid "
    1173             :                 "LEFT JOIN pg_description d "
    1174             :                 "ON d.objoid = c.oid AND d.classoid = "
    1175             :                 "'pg_class'::regclass::oid AND d.objsubid = 0 "
    1176             :                 "WHERE c.relkind in (%s) ",
    1177           1 :                 GEOM_TYPE_GEOMETRY, pszAllowedRelations);
    1178             : 
    1179           1 :             if (bHaveGeography)
    1180           2 :                 osCommand += CPLString().Printf(
    1181             :                     "UNION SELECT c.relname, n.nspname, "
    1182             :                     "g.f_geography_column, "
    1183             :                     "g.type, g.coord_dimension, g.srid, %d, a.attnotnull, "
    1184             :                     "d.description, c.oid as oid, a.attnum as attnum "
    1185             :                     "FROM pg_class c "
    1186             :                     "JOIN pg_namespace n ON c.relnamespace=n.oid "
    1187             :                     "JOIN geography_columns g "
    1188             :                     "ON c.relname::TEXT = g.f_table_name::TEXT AND n.nspname = "
    1189             :                     "g.f_table_schema "
    1190             :                     "JOIN pg_attribute a "
    1191             :                     "ON a.attname = g.f_geography_column AND a.attrelid = "
    1192             :                     "c.oid "
    1193             :                     "LEFT JOIN pg_description d "
    1194             :                     "ON d.objoid = c.oid AND d.classoid = "
    1195             :                     "'pg_class'::regclass::oid AND d.objsubid = 0 "
    1196             :                     "WHERE c.relkind in (%s)",
    1197           1 :                     GEOM_TYPE_GEOGRAPHY, pszAllowedRelations);
    1198           1 :             osCommand += " ORDER BY oid, attnum";
    1199             :         }
    1200             :         else
    1201             :             osCommand.Printf("SELECT c.relname, n.nspname, d.description FROM "
    1202             :                              "pg_class c "
    1203             :                              "JOIN pg_namespace n ON c.relnamespace=n.oid "
    1204             :                              "LEFT JOIN pg_description d "
    1205             :                              "ON d.objoid = c.oid AND d.classoid = "
    1206             :                              "'pg_class'::regclass::oid AND d.objsubid = 0 "
    1207             :                              "WHERE (c.relkind in (%s) AND "
    1208             :                              "c.relname !~ '^pg_')",
    1209         118 :                              pszAllowedRelations);
    1210             : 
    1211         119 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    1212             : 
    1213         119 :         if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
    1214             :         {
    1215           0 :             OGRPGClearResult(hResult);
    1216             : 
    1217           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
    1218           0 :                      PQerrorMessage(hPGConn));
    1219           0 :             goto end;
    1220             :         }
    1221             : 
    1222             :         /* --------------------------------------------------------------------
    1223             :          */
    1224             :         /*      Parse the returned table list */
    1225             :         /* --------------------------------------------------------------------
    1226             :          */
    1227        8410 :         for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
    1228             :         {
    1229        8291 :             const char *pszTable = PQgetvalue(hResult, iRecord, 0);
    1230        8291 :             const char *pszSchemaName = PQgetvalue(hResult, iRecord, 1);
    1231        8291 :             const char *pszGeomColumnName = nullptr;
    1232        8291 :             const char *pszGeomType = nullptr;
    1233        8291 :             const char *pszDescription = nullptr;
    1234        8291 :             int nGeomCoordDimension = 0;
    1235        8291 :             bool bHasM = false;
    1236        8291 :             int nSRID = 0;
    1237        8291 :             int bNullable = TRUE;
    1238        8291 :             PostgisType ePostgisType = GEOM_TYPE_UNKNOWN;
    1239        8291 :             if (bHavePostGIS && !bListAllTables)
    1240             :             {
    1241           2 :                 pszGeomColumnName = PQgetvalue(hResult, iRecord, 2);
    1242           2 :                 pszGeomType = PQgetvalue(hResult, iRecord, 3);
    1243           2 :                 bHasM = pszGeomType[strlen(pszGeomType) - 1] == 'M';
    1244           2 :                 nGeomCoordDimension = atoi(PQgetvalue(hResult, iRecord, 4));
    1245           2 :                 nSRID = atoi(PQgetvalue(hResult, iRecord, 5));
    1246           2 :                 ePostgisType = static_cast<PostgisType>(
    1247           2 :                     atoi(PQgetvalue(hResult, iRecord, 6)));
    1248           2 :                 bNullable = EQUAL(PQgetvalue(hResult, iRecord, 7), "f");
    1249           2 :                 pszDescription = PQgetvalue(hResult, iRecord, 8);
    1250             :             }
    1251             :             else
    1252             :             {
    1253        8289 :                 pszDescription = PQgetvalue(hResult, iRecord, 2);
    1254             :             }
    1255             : 
    1256        8291 :             if (EQUAL(pszTable, "spatial_ref_sys") ||
    1257        8173 :                 EQUAL(pszTable, "geometry_columns") ||
    1258        8057 :                 EQUAL(pszTable, "geography_columns"))
    1259         350 :                 continue;
    1260             : 
    1261        7941 :             if (EQUAL(pszSchemaName, "information_schema") ||
    1262         393 :                 EQUAL(pszSchemaName, "ogr_system_tables"))
    1263        7657 :                 continue;
    1264             : 
    1265         284 :             int GeomTypeFlags = 0;
    1266         284 :             if (nGeomCoordDimension == 3)
    1267             :             {
    1268           1 :                 if (bHasM)
    1269           0 :                     GeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
    1270             :                 else
    1271           1 :                     GeomTypeFlags |= OGRGeometry::OGR_G_3D;
    1272             :             }
    1273         283 :             else if (nGeomCoordDimension == 4)
    1274             :             {
    1275           0 :                 GeomTypeFlags |=
    1276             :                     OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
    1277             :             }
    1278             : 
    1279         568 :             papsTables = static_cast<PGTableEntry **>(CPLRealloc(
    1280         284 :                 papsTables, sizeof(PGTableEntry *) * (nTableCount + 1)));
    1281         568 :             papsTables[nTableCount] =
    1282         284 :                 static_cast<PGTableEntry *>(CPLCalloc(1, sizeof(PGTableEntry)));
    1283         284 :             papsTables[nTableCount]->pszTableName = CPLStrdup(pszTable);
    1284         284 :             papsTables[nTableCount]->pszSchemaName = CPLStrdup(pszSchemaName);
    1285         568 :             papsTables[nTableCount]->pszDescription =
    1286         284 :                 CPLStrdup(pszDescription ? pszDescription : "");
    1287         284 :             if (pszGeomColumnName)
    1288           2 :                 OGRPGTableEntryAddGeomColumn(
    1289           2 :                     papsTables[nTableCount], pszGeomColumnName, pszGeomType,
    1290             :                     GeomTypeFlags, nSRID, ePostgisType, bNullable);
    1291         284 :             nTableCount++;
    1292             : 
    1293             :             PGTableEntry *psEntry =
    1294         284 :                 OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName);
    1295         284 :             if (psEntry == nullptr)
    1296         284 :                 psEntry = OGRPGAddTableEntry(hSetTables, pszTable,
    1297             :                                              pszSchemaName, pszDescription);
    1298         284 :             if (pszGeomColumnName)
    1299           2 :                 OGRPGTableEntryAddGeomColumn(psEntry, pszGeomColumnName,
    1300             :                                              pszGeomType, GeomTypeFlags, nSRID,
    1301             :                                              ePostgisType, bNullable);
    1302             :         }
    1303             : 
    1304             :         /* --------------------------------------------------------------------
    1305             :          */
    1306             :         /*      Cleanup */
    1307             :         /* --------------------------------------------------------------------
    1308             :          */
    1309         119 :         OGRPGClearResult(hResult);
    1310             :     }
    1311             : 
    1312             :     /* -------------------------------------------------------------------- */
    1313             :     /*      Register the available tables.                                  */
    1314             :     /* -------------------------------------------------------------------- */
    1315         639 :     for (int iRecord = 0; iRecord < nTableCount; iRecord++)
    1316             :     {
    1317             :         const PGTableEntry *psEntry = static_cast<PGTableEntry *>(
    1318         366 :             CPLHashSetLookup(hSetTables, papsTables[iRecord]));
    1319             : 
    1320             :         /* If SCHEMAS= is specified, only take into account tables inside */
    1321             :         /* one of the specified schemas */
    1322         726 :         if (papszSchemaList != nullptr &&
    1323         360 :             CSLFindString(papszSchemaList,
    1324         360 :                           papsTables[iRecord]->pszSchemaName) == -1)
    1325             :         {
    1326         278 :             continue;
    1327             :         }
    1328             : 
    1329         126 :         CPLString osDefnName;
    1330             : 
    1331         252 :         if (papsTables[iRecord]->pszSchemaName &&
    1332         126 :             osCurrentSchema != papsTables[iRecord]->pszSchemaName)
    1333             :         {
    1334           6 :             osDefnName.Printf("%s.%s", papsTables[iRecord]->pszSchemaName,
    1335           6 :                               papsTables[iRecord]->pszTableName);
    1336             :         }
    1337             :         else
    1338             :         {
    1339             :             // no prefix for current_schema in layer name, for backwards
    1340             :             // compatibility
    1341         120 :             osDefnName = papsTables[iRecord]->pszTableName;
    1342             :         }
    1343         126 :         if (osRegisteredLayers.find(osDefnName) != osRegisteredLayers.end())
    1344          38 :             continue;
    1345          88 :         osRegisteredLayers.insert(osDefnName);
    1346             : 
    1347         176 :         OGRPGTableLayer *poLayer = OpenTable(
    1348          88 :             osCurrentSchema, papsTables[iRecord]->pszTableName,
    1349          88 :             papsTables[iRecord]->pszSchemaName,
    1350          88 :             papsTables[iRecord]->pszDescription, nullptr, bDSUpdate, FALSE);
    1351          88 :         if (psEntry != nullptr)
    1352             :         {
    1353          82 :             if (psEntry->nGeomColumnCount > 0)
    1354             :             {
    1355          44 :                 poLayer->SetGeometryInformation(psEntry->pasGeomColumns,
    1356          44 :                                                 psEntry->nGeomColumnCount);
    1357             :             }
    1358             :         }
    1359             :         else
    1360             :         {
    1361           6 :             if (papsTables[iRecord]->nGeomColumnCount > 0)
    1362             :             {
    1363           0 :                 poLayer->SetGeometryInformation(
    1364           0 :                     papsTables[iRecord]->pasGeomColumns,
    1365           0 :                     papsTables[iRecord]->nGeomColumnCount);
    1366             :             }
    1367             :         }
    1368             :     }
    1369             : 
    1370         273 : end:
    1371         273 :     if (hSetTables)
    1372         273 :         CPLHashSetDestroy(hSetTables);
    1373             : 
    1374         639 :     for (int i = 0; i < nTableCount; i++)
    1375         366 :         OGRPGFreeTableEntry(papsTables[i]);
    1376         273 :     CPLFree(papsTables);
    1377             : }
    1378             : 
    1379             : /************************************************************************/
    1380             : /*                             OpenTable()                              */
    1381             : /************************************************************************/
    1382             : 
    1383         498 : OGRPGTableLayer *OGRPGDataSource::OpenTable(CPLString &osCurrentSchemaIn,
    1384             :                                             const char *pszNewName,
    1385             :                                             const char *pszSchemaName,
    1386             :                                             const char *pszDescription,
    1387             :                                             const char *pszGeomColumnForced,
    1388             :                                             int bUpdate, int bTestOpen)
    1389             : 
    1390             : {
    1391             :     /* -------------------------------------------------------------------- */
    1392             :     /*      Create the layer object.                                        */
    1393             :     /* -------------------------------------------------------------------- */
    1394             :     OGRPGTableLayer *poLayer =
    1395             :         new OGRPGTableLayer(this, osCurrentSchemaIn, pszNewName, pszSchemaName,
    1396         498 :                             pszDescription, pszGeomColumnForced, bUpdate);
    1397         498 :     if (bTestOpen && !(poLayer->ReadTableDefinition()))
    1398             :     {
    1399         227 :         delete poLayer;
    1400         227 :         return nullptr;
    1401             :     }
    1402             : 
    1403             :     /* -------------------------------------------------------------------- */
    1404             :     /*      Add layer to data source layer list.                            */
    1405             :     /* -------------------------------------------------------------------- */
    1406         271 :     papoLayers = static_cast<OGRPGTableLayer **>(
    1407         271 :         CPLRealloc(papoLayers, sizeof(OGRPGTableLayer *) * (nLayers + 1)));
    1408         271 :     papoLayers[nLayers++] = poLayer;
    1409             : 
    1410         271 :     return poLayer;
    1411             : }
    1412             : 
    1413             : /************************************************************************/
    1414             : /*                            DeleteLayer()                             */
    1415             : /************************************************************************/
    1416             : 
    1417          24 : OGRErr OGRPGDataSource::DeleteLayer(int iLayer)
    1418             : 
    1419             : {
    1420             :     /* Force loading of all registered tables */
    1421          24 :     GetLayerCount();
    1422          24 :     if (iLayer < 0 || iLayer >= nLayers)
    1423           0 :         return OGRERR_FAILURE;
    1424             : 
    1425          24 :     EndCopy();
    1426             : 
    1427             :     /* -------------------------------------------------------------------- */
    1428             :     /*      Blow away our OGR structures related to the layer.  This is     */
    1429             :     /*      pretty dangerous if anything has a reference to this layer!     */
    1430             :     /* -------------------------------------------------------------------- */
    1431          48 :     CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName();
    1432          48 :     CPLString osTableName = papoLayers[iLayer]->GetTableName();
    1433          48 :     CPLString osSchemaName = papoLayers[iLayer]->GetSchemaName();
    1434             : 
    1435          24 :     CPLDebug("PG", "DeleteLayer(%s)", osLayerName.c_str());
    1436             : 
    1437          24 :     delete papoLayers[iLayer];
    1438          24 :     memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
    1439          24 :             sizeof(void *) * (nLayers - iLayer - 1));
    1440          24 :     nLayers--;
    1441             : 
    1442          24 :     if (osLayerName.empty())
    1443           0 :         return OGRERR_NONE;
    1444             : 
    1445             :     /* -------------------------------------------------------------------- */
    1446             :     /*      Remove from the database.                                       */
    1447             :     /* -------------------------------------------------------------------- */
    1448          24 :     CPLString osCommand;
    1449             : 
    1450          24 :     SoftStartTransaction();
    1451             : 
    1452             :     osCommand.Printf("DROP TABLE %s.%s CASCADE",
    1453          48 :                      OGRPGEscapeColumnName(osSchemaName).c_str(),
    1454          72 :                      OGRPGEscapeColumnName(osTableName).c_str());
    1455          24 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    1456          24 :     OGRPGClearResult(hResult);
    1457             : 
    1458          24 :     SoftCommitTransaction();
    1459             : 
    1460          24 :     return OGRERR_NONE;
    1461             : }
    1462             : 
    1463             : /************************************************************************/
    1464             : /*                           ICreateLayer()                             */
    1465             : /************************************************************************/
    1466             : 
    1467         226 : OGRLayer *OGRPGDataSource::ICreateLayer(const char *pszLayerName,
    1468             :                                         const OGRGeomFieldDefn *poGeomFieldDefn,
    1469             :                                         CSLConstList papszOptions)
    1470             : 
    1471             : {
    1472         226 :     const char *pszGeomType = nullptr;
    1473         226 :     char *pszTableName = nullptr;
    1474         226 :     char *pszSchemaName = nullptr;
    1475         226 :     int GeometryTypeFlags = 0;
    1476             : 
    1477         226 :     auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
    1478             :     const auto poSRS =
    1479         226 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
    1480             : 
    1481         226 :     if (pszLayerName == nullptr)
    1482           0 :         return nullptr;
    1483             : 
    1484         226 :     EndCopy();
    1485             : 
    1486             :     const bool bUTF8ToASCII =
    1487         226 :         CPLFetchBool(papszOptions, "LAUNDER_ASCII", false);
    1488             :     const bool bLaunder =
    1489         226 :         bUTF8ToASCII || CPLFetchBool(papszOptions, "LAUNDER", true);
    1490             : 
    1491         226 :     const char *pszFIDColumnNameIn = CSLFetchNameValue(papszOptions, "FID");
    1492         452 :     CPLString osFIDColumnName;
    1493         226 :     if (pszFIDColumnNameIn == nullptr)
    1494         224 :         osFIDColumnName = "ogc_fid";
    1495             :     else
    1496             :     {
    1497           2 :         if (bLaunder)
    1498             :         {
    1499             :             char *pszLaunderedFid =
    1500           2 :                 OGRPGCommonLaunderName(pszFIDColumnNameIn, "PG", bUTF8ToASCII);
    1501           2 :             osFIDColumnName += pszLaunderedFid;
    1502           2 :             CPLFree(pszLaunderedFid);
    1503             :         }
    1504             :         else
    1505           0 :             osFIDColumnName += pszFIDColumnNameIn;
    1506             :     }
    1507         452 :     CPLString osFIDColumnNameEscaped = OGRPGEscapeColumnName(osFIDColumnName);
    1508             : 
    1509         226 :     if (STARTS_WITH(pszLayerName, "pg"))
    1510             :     {
    1511           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1512             :                  "The layer name should not begin by 'pg' as it is a reserved "
    1513             :                  "prefix");
    1514             :     }
    1515             : 
    1516         226 :     if (OGR_GT_HasZ(eType))
    1517           5 :         GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
    1518         226 :     if (OGR_GT_HasM(eType))
    1519           2 :         GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
    1520             : 
    1521         226 :     int ForcedGeometryTypeFlags = -1;
    1522         226 :     const char *pszDim = CSLFetchNameValue(papszOptions, "DIM");
    1523         226 :     if (pszDim != nullptr)
    1524             :     {
    1525          75 :         if (EQUAL(pszDim, "XY") || EQUAL(pszDim, "2"))
    1526             :         {
    1527           0 :             GeometryTypeFlags = 0;
    1528           0 :             ForcedGeometryTypeFlags = GeometryTypeFlags;
    1529             :         }
    1530          75 :         else if (EQUAL(pszDim, "XYZ") || EQUAL(pszDim, "3"))
    1531             :         {
    1532          69 :             GeometryTypeFlags = OGRGeometry::OGR_G_3D;
    1533          69 :             ForcedGeometryTypeFlags = GeometryTypeFlags;
    1534             :         }
    1535           6 :         else if (EQUAL(pszDim, "XYM"))
    1536             :         {
    1537           3 :             GeometryTypeFlags = OGRGeometry::OGR_G_MEASURED;
    1538           3 :             ForcedGeometryTypeFlags = GeometryTypeFlags;
    1539             :         }
    1540           3 :         else if (EQUAL(pszDim, "XYZM") || EQUAL(pszDim, "4"))
    1541             :         {
    1542           3 :             GeometryTypeFlags =
    1543             :                 OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
    1544           3 :             ForcedGeometryTypeFlags = GeometryTypeFlags;
    1545             :         }
    1546             :         else
    1547             :         {
    1548           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for DIM");
    1549             :         }
    1550             :     }
    1551             : 
    1552             :     /* Should we turn layers with None geometry type as Unknown/GEOMETRY */
    1553             :     /* so they are still recorded in geometry_columns table ? (#4012) */
    1554         226 :     int bNoneAsUnknown = CPLTestBool(
    1555         226 :         CSLFetchNameValueDef(papszOptions, "NONE_AS_UNKNOWN", "NO"));
    1556         226 :     if (bNoneAsUnknown && eType == wkbNone)
    1557           0 :         eType = wkbUnknown;
    1558             : 
    1559         226 :     int bExtractSchemaFromLayerName = CPLTestBool(CSLFetchNameValueDef(
    1560         226 :         papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
    1561             : 
    1562             :     /* Postgres Schema handling:
    1563             :        Extract schema name from input layer name or passed with -lco SCHEMA.
    1564             :        Set layer name to "schema.table" or to "table" if schema ==
    1565             :        current_schema() Usage without schema name is backwards compatible
    1566             :     */
    1567         226 :     const char *pszDotPos = strstr(pszLayerName, ".");
    1568         226 :     if (pszDotPos != nullptr && bExtractSchemaFromLayerName)
    1569             :     {
    1570          19 :         int length = static_cast<int>(pszDotPos - pszLayerName);
    1571          19 :         pszSchemaName = static_cast<char *>(CPLMalloc(length + 1));
    1572          19 :         strncpy(pszSchemaName, pszLayerName, length);
    1573          19 :         pszSchemaName[length] = '\0';
    1574             : 
    1575          19 :         if (bLaunder)
    1576          17 :             pszTableName = OGRPGCommonLaunderName(pszDotPos + 1, "PG",
    1577             :                                                   bUTF8ToASCII);  // skip "."
    1578             :         else
    1579           2 :             pszTableName = CPLStrdup(
    1580           4 :                 OGRPGCommonGenerateShortEnoughIdentifier(pszDotPos + 1)
    1581          19 :                     .c_str());  // skip "."
    1582             :     }
    1583             :     else
    1584             :     {
    1585         207 :         pszSchemaName = nullptr;
    1586         207 :         if (bLaunder)
    1587         207 :             pszTableName = OGRPGCommonLaunderName(pszLayerName, "PG",
    1588             :                                                   bUTF8ToASCII);  // skip "."
    1589             :         else
    1590             :             pszTableName =
    1591           0 :                 CPLStrdup(OGRPGCommonGenerateShortEnoughIdentifier(pszLayerName)
    1592             :                               .c_str());  // skip "."
    1593             :     }
    1594             : 
    1595             :     /* -------------------------------------------------------------------- */
    1596             :     /*      Set the default schema for the layers.                          */
    1597             :     /* -------------------------------------------------------------------- */
    1598         226 :     if (CSLFetchNameValue(papszOptions, "SCHEMA") != nullptr)
    1599             :     {
    1600           3 :         CPLFree(pszSchemaName);
    1601           3 :         pszSchemaName = CPLStrdup(CSLFetchNameValue(papszOptions, "SCHEMA"));
    1602             :     }
    1603             : 
    1604         226 :     const bool bTemporary = CPLFetchBool(papszOptions, "TEMPORARY", false);
    1605         226 :     if (bTemporary)
    1606             :     {
    1607           1 :         CPLFree(pszSchemaName);
    1608           1 :         pszSchemaName = CPLStrdup("pg_temp");
    1609             :     }
    1610         226 :     if (pszSchemaName == nullptr)
    1611             :     {
    1612         205 :         pszSchemaName = CPLStrdup(osCurrentSchema);
    1613             :     }
    1614             : 
    1615             :     // Check that the schema exist. If there is a single match in a case
    1616             :     // insensitive way, use it. Otherwise error out if the match is not exact.
    1617             :     {
    1618         226 :         const auto osNewSchemaName = FindSchema(pszSchemaName);
    1619         226 :         if (!osNewSchemaName.has_value())
    1620             :         {
    1621           4 :             CPLFree(pszTableName);
    1622           4 :             CPLFree(pszSchemaName);
    1623           4 :             return nullptr;
    1624             :         }
    1625         222 :         CPLFree(pszSchemaName);
    1626         222 :         pszSchemaName = CPLStrdup(osNewSchemaName->c_str());
    1627             :     }
    1628             : 
    1629             :     /* -------------------------------------------------------------------- */
    1630             :     /*      Do we already have this layer?  If so, should we blow it        */
    1631             :     /*      away?                                                           */
    1632             :     /* -------------------------------------------------------------------- */
    1633         444 :     CPLString osSQLLayerName;
    1634         444 :     if (pszSchemaName == nullptr ||
    1635         222 :         (!osCurrentSchema.empty() &&
    1636         222 :          EQUAL(pszSchemaName, osCurrentSchema.c_str())))
    1637         205 :         osSQLLayerName = pszTableName;
    1638             :     else
    1639             :     {
    1640          17 :         osSQLLayerName = pszSchemaName;
    1641          17 :         osSQLLayerName += ".";
    1642          17 :         osSQLLayerName += pszTableName;
    1643             :     }
    1644             : 
    1645             :     /* GetLayerByName() can instantiate layers that would have been */
    1646             :     /* 'hidden' otherwise, for example, non-spatial tables in a */
    1647             :     /* PostGIS-enabled database, so this apparently useless command is */
    1648             :     /* not useless. (#4012) */
    1649         222 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    1650         222 :     GetLayerByName(osSQLLayerName);
    1651         222 :     CPLPopErrorHandler();
    1652         222 :     CPLErrorReset();
    1653             : 
    1654             :     /* Force loading of all registered tables */
    1655         222 :     GetLayerCount();
    1656             : 
    1657         308 :     for (int iLayer = 0; iLayer < nLayers; iLayer++)
    1658             :     {
    1659          88 :         if (EQUAL(osSQLLayerName.c_str(), papoLayers[iLayer]->GetName()))
    1660             :         {
    1661          30 :             if (CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
    1662          14 :                 !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO"))
    1663             :             {
    1664          14 :                 DeleteLayer(iLayer);
    1665             :             }
    1666             :             else
    1667             :             {
    1668           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1669             :                          "Layer %s already exists, CreateLayer failed.\n"
    1670             :                          "Use the layer creation option OVERWRITE=YES to "
    1671             :                          "replace it.",
    1672             :                          osSQLLayerName.c_str());
    1673           2 :                 CPLFree(pszTableName);
    1674           2 :                 CPLFree(pszSchemaName);
    1675           2 :                 return nullptr;
    1676             :             }
    1677             :         }
    1678             :     }
    1679             : 
    1680             :     /* -------------------------------------------------------------------- */
    1681             :     /*      Handle the GEOM_TYPE option.                                    */
    1682             :     /* -------------------------------------------------------------------- */
    1683         220 :     pszGeomType = CSLFetchNameValue(papszOptions, "GEOM_TYPE");
    1684         220 :     if (pszGeomType == nullptr)
    1685             :     {
    1686         214 :         if (bHavePostGIS)
    1687         127 :             pszGeomType = "geometry";
    1688             :         else
    1689          87 :             pszGeomType = "bytea";
    1690             :     }
    1691             : 
    1692         220 :     const char *pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
    1693         220 :     if (eType != wkbNone && EQUAL(pszGeomType, "geography"))
    1694             :     {
    1695           5 :         if (!bHaveGeography)
    1696             :         {
    1697           0 :             CPLError(
    1698             :                 CE_Failure, CPLE_AppDefined,
    1699             :                 "GEOM_TYPE=geography is only supported in PostGIS >= 1.5.\n"
    1700             :                 "Creation of layer %s has failed.",
    1701             :                 pszLayerName);
    1702           0 :             CPLFree(pszTableName);
    1703           0 :             CPLFree(pszSchemaName);
    1704           0 :             return nullptr;
    1705             :         }
    1706             : 
    1707           5 :         if (pszGFldName == nullptr)
    1708           2 :             pszGFldName = "the_geog";
    1709             :     }
    1710         215 :     else if (eType != wkbNone && bHavePostGIS &&
    1711         107 :              !EQUAL(pszGeomType, "geography"))
    1712             :     {
    1713         107 :         if (pszGFldName == nullptr)
    1714         105 :             pszGFldName = "wkb_geometry";
    1715             :     }
    1716             : 
    1717         220 :     if (eType != wkbNone && bHavePostGIS && !EQUAL(pszGeomType, "geometry") &&
    1718           5 :         !EQUAL(pszGeomType, "geography"))
    1719             :     {
    1720           0 :         if (bHaveGeography)
    1721           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1722             :                      "GEOM_TYPE in PostGIS enabled databases must be "
    1723             :                      "'geometry' or 'geography'.\n"
    1724             :                      "Creation of layer %s with GEOM_TYPE %s has failed.",
    1725             :                      pszLayerName, pszGeomType);
    1726             :         else
    1727           0 :             CPLError(
    1728             :                 CE_Failure, CPLE_AppDefined,
    1729             :                 "GEOM_TYPE in PostGIS enabled databases must be 'geometry'.\n"
    1730             :                 "Creation of layer %s with GEOM_TYPE %s has failed.",
    1731             :                 pszLayerName, pszGeomType);
    1732             : 
    1733           0 :         CPLFree(pszTableName);
    1734           0 :         CPLFree(pszSchemaName);
    1735           0 :         return nullptr;
    1736             :     }
    1737             : 
    1738             :     /* -------------------------------------------------------------------- */
    1739             :     /*      Try to get the SRS Id of this spatial reference system,         */
    1740             :     /*      adding tot the srs table if needed.                             */
    1741             :     /* -------------------------------------------------------------------- */
    1742         220 :     int nSRSId = nUndefinedSRID;
    1743             : 
    1744         220 :     if (poSRS != nullptr)
    1745          12 :         nSRSId = FetchSRSId(poSRS);
    1746             : 
    1747         220 :     if (eType != wkbNone && EQUAL(pszGeomType, "geography"))
    1748             :     {
    1749           5 :         if (poSRS != nullptr && !poSRS->IsGeographic())
    1750             :         {
    1751           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    1752             :                      "geography type only supports geographic SRS");
    1753             : 
    1754           1 :             CPLFree(pszTableName);
    1755           1 :             CPLFree(pszSchemaName);
    1756           1 :             return nullptr;
    1757             :         }
    1758             : 
    1759           4 :         if (!(sPostGISVersion.nMajor >= 3 ||
    1760           0 :               (sPostGISVersion.nMajor == 2 && sPostGISVersion.nMinor >= 2)) &&
    1761           0 :             nSRSId != nUndefinedSRID && nSRSId != 4326)
    1762             :         {
    1763           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1764             :                      "geography type in PostGIS < 2.2 only supports SRS = "
    1765             :                      "EPSG:4326");
    1766             : 
    1767           0 :             CPLFree(pszTableName);
    1768           0 :             CPLFree(pszSchemaName);
    1769           0 :             return nullptr;
    1770             :         }
    1771             : 
    1772           4 :         if (nSRSId == nUndefinedSRID)
    1773             :         {
    1774           3 :             CPLError(CE_Warning, CPLE_AppDefined,
    1775             :                      "Assuming EPSG:4326 for geographic type (but no implicit "
    1776             :                      "reprojection to it will be done)");
    1777           3 :             nSRSId = 4326;
    1778             :         }
    1779             :     }
    1780             : 
    1781         219 :     const char *pszGeometryType = OGRToOGCGeomType(eType);
    1782             : 
    1783             :     int bDeferredCreation =
    1784         219 :         CPLTestBool(CPLGetConfigOption("OGR_PG_DEFERRED_CREATION", "YES"));
    1785         219 :     if (!bHavePostGIS)
    1786          87 :         bDeferredCreation =
    1787             :             FALSE; /* to avoid unnecessary implementation and testing burden */
    1788             : 
    1789             :     /* -------------------------------------------------------------------- */
    1790             :     /*      Create a basic table with the FID.  Also include the            */
    1791             :     /*      geometry if this is not a PostGIS enabled table.                */
    1792             :     /* -------------------------------------------------------------------- */
    1793         219 :     const bool bFID64 = CPLFetchBool(papszOptions, "FID64", false);
    1794         219 :     const char *pszSerialType = bFID64 ? "BIGSERIAL" : "SERIAL";
    1795             : 
    1796         438 :     CPLString osCreateTable;
    1797         219 :     if (bTemporary)
    1798             :     {
    1799             :         osCreateTable.Printf("CREATE TEMPORARY TABLE %s",
    1800           1 :                              OGRPGEscapeColumnName(pszTableName).c_str());
    1801             :     }
    1802             :     else
    1803             :     {
    1804             :         osCreateTable.Printf(
    1805             :             "CREATE%s TABLE %s.%s",
    1806         218 :             CPLFetchBool(papszOptions, "UNLOGGED", false) ? " UNLOGGED" : "",
    1807         436 :             OGRPGEscapeColumnName(pszSchemaName).c_str(),
    1808         872 :             OGRPGEscapeColumnName(pszTableName).c_str());
    1809             :     }
    1810             : 
    1811         219 :     const char *suffix = nullptr;
    1812         219 :     if ((GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
    1813          77 :         (GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
    1814           4 :         suffix = "ZM";
    1815         219 :     else if ((GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) &&
    1816           4 :              (EQUAL(pszGeomType, "geography") ||
    1817           3 :               wkbFlatten(eType) != wkbUnknown))
    1818           2 :         suffix = "M";
    1819         213 :     else if (GeometryTypeFlags & OGRGeometry::OGR_G_3D)
    1820          73 :         suffix = "Z";
    1821             :     else
    1822         140 :         suffix = "";
    1823             : 
    1824             :     {
    1825         438 :         CPLString osCommand;
    1826         219 :         if (eType != wkbNone && !bHavePostGIS)
    1827             :         {
    1828          79 :             if (pszGFldName && !EQUAL(pszGFldName, "wkb_geometry"))
    1829             :             {
    1830           3 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1831             :                          "GEOMETRY_NAME=%s ignored, and set instead to "
    1832             :                          "'wkb_geometry' as it is the only geometry column "
    1833             :                          "name recognized for non-PostGIS enabled databases.",
    1834             :                          pszGFldName);
    1835             :             }
    1836          79 :             pszGFldName = "wkb_geometry";
    1837          79 :             osCommand.Printf("%s ( "
    1838             :                              "    %s %s, "
    1839             :                              "   %s %s, "
    1840             :                              "   PRIMARY KEY (%s)",
    1841             :                              osCreateTable.c_str(),
    1842             :                              osFIDColumnNameEscaped.c_str(), pszSerialType,
    1843             :                              pszGFldName, pszGeomType,
    1844          79 :                              osFIDColumnNameEscaped.c_str());
    1845             :         }
    1846         140 :         else if (!bDeferredCreation && eType != wkbNone &&
    1847           2 :                  EQUAL(pszGeomType, "geography"))
    1848             :         {
    1849           1 :             osCommand.Printf(
    1850             :                 "%s ( %s %s, %s geography(%s%s%s), PRIMARY KEY (%s)",
    1851             :                 osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
    1852           2 :                 pszSerialType, OGRPGEscapeColumnName(pszGFldName).c_str(),
    1853             :                 pszGeometryType, suffix,
    1854           1 :                 nSRSId ? CPLSPrintf(",%d", nSRSId) : "",
    1855           2 :                 osFIDColumnNameEscaped.c_str());
    1856             :         }
    1857         139 :         else if (!bDeferredCreation && eType != wkbNone &&
    1858           1 :                  !EQUAL(pszGeomType, "geography") &&
    1859           1 :                  sPostGISVersion.nMajor >= 2)
    1860             :         {
    1861           1 :             osCommand.Printf(
    1862             :                 "%s ( %s %s, %s geometry(%s%s%s), PRIMARY KEY (%s)",
    1863             :                 osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
    1864           2 :                 pszSerialType, OGRPGEscapeColumnName(pszGFldName).c_str(),
    1865             :                 pszGeometryType, suffix,
    1866           0 :                 nSRSId ? CPLSPrintf(",%d", nSRSId) : "",
    1867           2 :                 osFIDColumnNameEscaped.c_str());
    1868             :         }
    1869             :         else
    1870             :         {
    1871             :             osCommand.Printf("%s ( %s %s, PRIMARY KEY (%s)",
    1872             :                              osCreateTable.c_str(),
    1873             :                              osFIDColumnNameEscaped.c_str(), pszSerialType,
    1874         138 :                              osFIDColumnNameEscaped.c_str());
    1875             :         }
    1876         219 :         osCreateTable = std::move(osCommand);
    1877             :     }
    1878             : 
    1879             :     const char *pszSI =
    1880         219 :         CSLFetchNameValueDef(papszOptions, "SPATIAL_INDEX", "GIST");
    1881         219 :     bool bCreateSpatialIndex =
    1882           0 :         (EQUAL(pszSI, "GIST") || EQUAL(pszSI, "SPGIST") ||
    1883         219 :          EQUAL(pszSI, "BRIN") || EQUAL(pszSI, "YES") || EQUAL(pszSI, "ON") ||
    1884           0 :          EQUAL(pszSI, "TRUE"));
    1885         219 :     if (!bCreateSpatialIndex && !EQUAL(pszSI, "NO") && !EQUAL(pszSI, "OFF") &&
    1886           0 :         !EQUAL(pszSI, "FALSE") && !EQUAL(pszSI, "NONE"))
    1887             :     {
    1888           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    1889             :                  "SPATIAL_INDEX=%s not supported", pszSI);
    1890             :     }
    1891         438 :     const char *pszSpatialIndexType = EQUAL(pszSI, "SPGIST") ? "SPGIST"
    1892         219 :                                       : EQUAL(pszSI, "BRIN") ? "BRIN"
    1893             :                                                              : "GIST";
    1894         190 :     if (eType != wkbNone && bCreateSpatialIndex &&
    1895         409 :         CPLFetchBool(papszOptions, "UNLOGGED", false) &&
    1896           0 :         !(sPostgreSQLVersion.nMajor > 9 ||
    1897           0 :           (sPostgreSQLVersion.nMajor == 9 && sPostgreSQLVersion.nMinor >= 3)))
    1898             :     {
    1899           0 :         CPLError(
    1900             :             CE_Warning, CPLE_NotSupported,
    1901             :             "GiST index only supported since Postgres 9.3 on unlogged table");
    1902           0 :         bCreateSpatialIndex = false;
    1903             :     }
    1904             : 
    1905         219 :     if (!bDeferredCreation)
    1906             :     {
    1907          89 :         SoftStartTransaction();
    1908             : 
    1909          89 :         CPLString osCommand = osCreateTable;
    1910          89 :         osCommand += " )";
    1911             : 
    1912          89 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    1913          89 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    1914             :         {
    1915           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    1916           0 :                      PQerrorMessage(hPGConn));
    1917           0 :             CPLFree(pszTableName);
    1918           0 :             CPLFree(pszSchemaName);
    1919             : 
    1920           0 :             OGRPGClearResult(hResult);
    1921             : 
    1922           0 :             SoftRollbackTransaction();
    1923           0 :             return nullptr;
    1924             :         }
    1925             : 
    1926          89 :         OGRPGClearResult(hResult);
    1927             : 
    1928          89 :         if (eType != wkbNone && bHavePostGIS && bCreateSpatialIndex)
    1929             :         {
    1930             :             // Create the spatial index.
    1931             :             // We're doing this before we add geometry and record to the
    1932             :             // table, so this may not be exactly the best way to do it.
    1933             :             const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
    1934           2 :                 pszTableName, pszGFldName, 0));
    1935             : 
    1936             :             osCommand.Printf("CREATE INDEX %s ON %s.%s USING %s (%s)",
    1937           4 :                              OGRPGEscapeColumnName(osIndexName.c_str()).c_str(),
    1938           4 :                              OGRPGEscapeColumnName(pszSchemaName).c_str(),
    1939           4 :                              OGRPGEscapeColumnName(pszTableName).c_str(),
    1940             :                              pszSpatialIndexType,
    1941          10 :                              OGRPGEscapeColumnName(pszGFldName).c_str());
    1942             : 
    1943           2 :             hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    1944             : 
    1945           2 :             if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
    1946             :             {
    1947           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1948             :                          "'%s' failed for layer %s, index creation has failed.",
    1949             :                          osCommand.c_str(), pszLayerName);
    1950             : 
    1951           0 :                 CPLFree(pszTableName);
    1952           0 :                 CPLFree(pszSchemaName);
    1953             : 
    1954           0 :                 OGRPGClearResult(hResult);
    1955             : 
    1956           0 :                 SoftRollbackTransaction();
    1957             : 
    1958           0 :                 return nullptr;
    1959             :             }
    1960           2 :             OGRPGClearResult(hResult);
    1961             :         }
    1962             : 
    1963             :         /* --------------------------------------------------------------------
    1964             :          */
    1965             :         /*      Complete, and commit the transaction. */
    1966             :         /* --------------------------------------------------------------------
    1967             :          */
    1968          89 :         SoftCommitTransaction();
    1969             :     }
    1970             : 
    1971             :     /* -------------------------------------------------------------------- */
    1972             :     /*      Create the layer object.                                        */
    1973             :     /* -------------------------------------------------------------------- */
    1974             :     OGRPGTableLayer *poLayer = new OGRPGTableLayer(
    1975         219 :         this, osCurrentSchema, pszTableName, pszSchemaName, "", nullptr, TRUE);
    1976         219 :     poLayer->SetTableDefinition(osFIDColumnName, pszGFldName, eType,
    1977             :                                 pszGeomType, nSRSId, GeometryTypeFlags);
    1978         219 :     poLayer->SetLaunderFlag(bLaunder);
    1979         219 :     poLayer->SetUTF8ToASCIIFlag(bUTF8ToASCII);
    1980         219 :     poLayer->SetPrecisionFlag(CPLFetchBool(papszOptions, "PRECISION", true));
    1981             :     // poLayer->SetForcedSRSId(nForcedSRSId);
    1982         219 :     poLayer->SetForcedGeometryTypeFlags(ForcedGeometryTypeFlags);
    1983         219 :     poLayer->SetCreateSpatialIndex(bCreateSpatialIndex, pszSpatialIndexType);
    1984         219 :     poLayer->SetDeferredCreation(bDeferredCreation, osCreateTable);
    1985             : 
    1986         219 :     const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
    1987         219 :     if (pszDescription != nullptr)
    1988           2 :         poLayer->SetForcedDescription(pszDescription);
    1989             : 
    1990             :     /* HSTORE_COLUMNS existed at a time during GDAL 1.10dev */
    1991             :     const char *pszHSTOREColumns =
    1992         219 :         CSLFetchNameValue(papszOptions, "HSTORE_COLUMNS");
    1993         219 :     if (pszHSTOREColumns != nullptr)
    1994           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1995             :                  "HSTORE_COLUMNS not recognized. Use COLUMN_TYPES instead.");
    1996             : 
    1997             :     const char *pszOverrideColumnTypes =
    1998         219 :         CSLFetchNameValue(papszOptions, "COLUMN_TYPES");
    1999         219 :     poLayer->SetOverrideColumnTypes(pszOverrideColumnTypes);
    2000             : 
    2001         219 :     poLayer->AllowAutoFIDOnCreateViaCopy();
    2002         219 :     if (CPLTestBool(CPLGetConfigOption("PG_USE_COPY", "YES")))
    2003         213 :         poLayer->SetUseCopy();
    2004             : 
    2005         219 :     if (bFID64)
    2006           2 :         poLayer->SetMetadataItem(OLMD_FID64, "YES");
    2007             : 
    2008             :     /* -------------------------------------------------------------------- */
    2009             :     /*      Add layer to data source layer list.                            */
    2010             :     /* -------------------------------------------------------------------- */
    2011         219 :     papoLayers = static_cast<OGRPGTableLayer **>(
    2012         219 :         CPLRealloc(papoLayers, sizeof(OGRPGTableLayer *) * (nLayers + 1)));
    2013             : 
    2014         219 :     papoLayers[nLayers++] = poLayer;
    2015             : 
    2016         219 :     CPLFree(pszTableName);
    2017         219 :     CPLFree(pszSchemaName);
    2018             : 
    2019         219 :     return poLayer;
    2020             : }
    2021             : 
    2022             : /************************************************************************/
    2023             : /*                           TestCapability()                           */
    2024             : /************************************************************************/
    2025             : 
    2026         115 : int OGRPGDataSource::TestCapability(const char *pszCap)
    2027             : 
    2028             : {
    2029         115 :     if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer) ||
    2030          84 :         EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
    2031          51 :         return TRUE;
    2032          64 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
    2033          12 :         return TRUE;
    2034          52 :     else if (EQUAL(pszCap, ODsCTransactions))
    2035          20 :         return TRUE;
    2036          32 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
    2037          12 :         return TRUE;
    2038          20 :     else if (EQUAL(pszCap, ODsCZGeometries))
    2039          14 :         return TRUE;
    2040           6 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
    2041           0 :         return TRUE;
    2042             :     else
    2043           6 :         return FALSE;
    2044             : }
    2045             : 
    2046             : /************************************************************************/
    2047             : /*                           GetLayerCount()                            */
    2048             : /************************************************************************/
    2049             : 
    2050        1243 : int OGRPGDataSource::GetLayerCount()
    2051             : {
    2052        1243 :     LoadTables();
    2053        1243 :     return nLayers;
    2054             : }
    2055             : 
    2056             : /************************************************************************/
    2057             : /*                              GetLayer()                              */
    2058             : /************************************************************************/
    2059             : 
    2060         311 : OGRLayer *OGRPGDataSource::GetLayer(int iLayer)
    2061             : 
    2062             : {
    2063             :     /* Force loading of all registered tables */
    2064         311 :     if (iLayer < 0 || iLayer >= GetLayerCount())
    2065          10 :         return nullptr;
    2066             :     else
    2067         301 :         return papoLayers[iLayer];
    2068             : }
    2069             : 
    2070             : /************************************************************************/
    2071             : /*                             FindSchema()                             */
    2072             : /************************************************************************/
    2073             : 
    2074             : // Check that the schema exists. If there is a single match in a case
    2075             : // insensitive way, use it. Otherwise error out if the match is not exact.
    2076             : // Return the schema name with its exact case from pg_catalog, or an empty
    2077             : // string if an error occurs.
    2078             : std::optional<std::string>
    2079         282 : OGRPGDataSource::FindSchema(const char *pszSchemaNameIn)
    2080             : {
    2081         282 :     if (strcmp(pszSchemaNameIn, "public") == 0 ||
    2082         279 :         strcmp(pszSchemaNameIn, "pg_temp") == 0)
    2083             :     {
    2084           5 :         return pszSchemaNameIn;
    2085             :     }
    2086             : 
    2087         277 :     EndCopy();
    2088             : 
    2089         554 :     std::string osSchemaName;
    2090             :     std::string osCommand(
    2091         554 :         "SELECT nspname FROM pg_catalog.pg_namespace WHERE nspname ILIKE ");
    2092         277 :     osCommand += OGRPGEscapeString(hPGConn, pszSchemaNameIn);
    2093         277 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2094         277 :     if (hResult && PQntuples(hResult) == 1)
    2095             :     {
    2096         269 :         osSchemaName = PQgetvalue(hResult, 0, 0);
    2097             :     }
    2098           8 :     else if (hResult)
    2099             :     {
    2100           8 :         const int nTuples = PQntuples(hResult);
    2101           8 :         if (nTuples == 0)
    2102             :         {
    2103           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2104             :                      "Schema \"%s\" does not exist.", pszSchemaNameIn);
    2105           2 :             return {};
    2106             :         }
    2107          12 :         for (int i = 0; i < nTuples; ++i)
    2108             :         {
    2109          10 :             if (strcmp(PQgetvalue(hResult, i, 0), pszSchemaNameIn) == 0)
    2110             :             {
    2111           4 :                 osSchemaName = pszSchemaNameIn;
    2112           4 :                 break;
    2113             :             }
    2114             :         }
    2115           6 :         if (osSchemaName.empty())
    2116             :         {
    2117           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2118             :                      "Several schemas exist whose name matches \"%s\", but "
    2119             :                      "not with that case. "
    2120             :                      "Please specify the schema name with the exact case.",
    2121             :                      pszSchemaNameIn);
    2122           2 :             return {};
    2123             :         }
    2124             :     }
    2125         273 :     OGRPGClearResult(hResult);
    2126             : 
    2127         273 :     return osSchemaName;
    2128             : }
    2129             : 
    2130             : /************************************************************************/
    2131             : /*                           GetLayerByName()                           */
    2132             : /************************************************************************/
    2133             : 
    2134         528 : OGRLayer *OGRPGDataSource::GetLayerByName(const char *pszNameIn)
    2135             : 
    2136             : {
    2137         528 :     char *pszTableName = nullptr;
    2138         528 :     char *pszGeomColumnName = nullptr;
    2139         528 :     char *pszSchemaName = nullptr;
    2140             : 
    2141         528 :     if (!pszNameIn)
    2142           0 :         return nullptr;
    2143             : 
    2144             :     /* first a case sensitive check */
    2145             :     /* do NOT force loading of all registered tables */
    2146         744 :     for (int i = 0; i < nLayers; i++)
    2147             :     {
    2148         312 :         OGRPGTableLayer *poLayer = papoLayers[i];
    2149             : 
    2150         312 :         if (strcmp(pszNameIn, poLayer->GetName()) == 0)
    2151             :         {
    2152          96 :             return poLayer;
    2153             :         }
    2154             :     }
    2155             : 
    2156             :     /* then case insensitive */
    2157         600 :     for (int i = 0; i < nLayers; i++)
    2158             :     {
    2159         168 :         OGRPGTableLayer *poLayer = papoLayers[i];
    2160             : 
    2161         168 :         if (EQUAL(pszNameIn, poLayer->GetName()))
    2162             :         {
    2163           0 :             return poLayer;
    2164             :         }
    2165             :     }
    2166             : 
    2167         432 :     char *pszNameWithoutBracket = CPLStrdup(pszNameIn);
    2168         432 :     char *pos = strchr(pszNameWithoutBracket, '(');
    2169         432 :     if (pos != nullptr)
    2170             :     {
    2171           5 :         *pos = '\0';
    2172           5 :         pszGeomColumnName = CPLStrdup(pos + 1);
    2173           5 :         int len = static_cast<int>(strlen(pszGeomColumnName));
    2174           5 :         if (len > 0)
    2175           5 :             pszGeomColumnName[len - 1] = '\0';
    2176             :     }
    2177             : 
    2178         432 :     pos = strchr(pszNameWithoutBracket, '.');
    2179         432 :     if (pos != nullptr)
    2180             :     {
    2181          56 :         *pos = '\0';
    2182          56 :         const auto osSchemaName = FindSchema(pszNameWithoutBracket);
    2183          56 :         if (!osSchemaName.has_value())
    2184             :         {
    2185           0 :             CPLFree(pszNameWithoutBracket);
    2186           0 :             CPLFree(pszGeomColumnName);
    2187           0 :             return nullptr;
    2188             :         }
    2189          56 :         pszSchemaName = CPLStrdup(osSchemaName->c_str());
    2190          56 :         pszTableName = CPLStrdup(pos + 1);
    2191             :     }
    2192             :     else
    2193             :     {
    2194         376 :         pszTableName = CPLStrdup(pszNameWithoutBracket);
    2195             :     }
    2196             : 
    2197         432 :     if (strlen(pszTableName) > OGR_PG_NAMEDATALEN - 1)
    2198           0 :         pszTableName[OGR_PG_NAMEDATALEN - 1] = 0;
    2199             : 
    2200         432 :     CPLFree(pszNameWithoutBracket);
    2201         432 :     pszNameWithoutBracket = nullptr;
    2202             : 
    2203         432 :     OGRPGTableLayer *poLayer = nullptr;
    2204             : 
    2205         432 :     if (pszSchemaName != nullptr && osCurrentSchema == pszSchemaName &&
    2206             :         pszGeomColumnName == nullptr)
    2207             :     {
    2208             :         poLayer =
    2209          26 :             cpl::down_cast<OGRPGTableLayer *>(GetLayerByName(pszTableName));
    2210             :     }
    2211             :     else
    2212             :     {
    2213         406 :         EndCopy();
    2214             : 
    2215         812 :         const CPLString osTableName(pszTableName);
    2216         812 :         const CPLString osTableNameLower = CPLString(pszTableName).tolower();
    2217         406 :         if (osTableName != osTableNameLower)
    2218           4 :             CPLPushErrorHandler(CPLQuietErrorHandler);
    2219         406 :         poLayer = OpenTable(osCurrentSchema, pszTableName, pszSchemaName,
    2220             :                             nullptr, pszGeomColumnName, bDSUpdate, TRUE);
    2221         406 :         if (osTableName != osTableNameLower)
    2222           4 :             CPLPopErrorHandler();
    2223         406 :         if (poLayer == nullptr && osTableName != osTableNameLower)
    2224             :         {
    2225             :             poLayer =
    2226           4 :                 OpenTable(osCurrentSchema, osTableNameLower, pszSchemaName,
    2227             :                           nullptr, pszGeomColumnName, bDSUpdate, TRUE);
    2228             :         }
    2229             :     }
    2230             : 
    2231         432 :     CPLFree(pszTableName);
    2232         432 :     CPLFree(pszSchemaName);
    2233         432 :     CPLFree(pszGeomColumnName);
    2234             : 
    2235         432 :     return poLayer;
    2236             : }
    2237             : 
    2238             : /************************************************************************/
    2239             : /*                        OGRPGNoticeProcessor()                        */
    2240             : /************************************************************************/
    2241             : 
    2242         421 : static void OGRPGNoticeProcessor(CPL_UNUSED void *arg, const char *pszMessage)
    2243             : {
    2244         421 :     CPLDebug("OGR_PG_NOTICE", "%s", pszMessage);
    2245         421 : }
    2246             : 
    2247             : /************************************************************************/
    2248             : /*                      InitializeMetadataTables()                      */
    2249             : /*                                                                      */
    2250             : /*      Create the metadata tables (SPATIAL_REF_SYS and                 */
    2251             : /*      GEOMETRY_COLUMNS).                                              */
    2252             : /************************************************************************/
    2253             : 
    2254           0 : OGRErr OGRPGDataSource::InitializeMetadataTables()
    2255             : 
    2256             : {
    2257             :     // implement later.
    2258           0 :     return OGRERR_FAILURE;
    2259             : }
    2260             : 
    2261             : /************************************************************************/
    2262             : /*                              FetchSRS()                              */
    2263             : /*                                                                      */
    2264             : /*      Return a SRS corresponding to a particular id.  Note that       */
    2265             : /*      reference counting should be honoured on the returned           */
    2266             : /*      OGRSpatialReference, as handles may be cached.                  */
    2267             : /************************************************************************/
    2268             : 
    2269          33 : const OGRSpatialReference *OGRPGDataSource::FetchSRS(int nId)
    2270             : 
    2271             : {
    2272          33 :     if (nId < 0 || !m_bHasSpatialRefSys)
    2273           0 :         return nullptr;
    2274             : 
    2275             :     /* -------------------------------------------------------------------- */
    2276             :     /*      First, we look through our SRID cache, is it there?             */
    2277             :     /* -------------------------------------------------------------------- */
    2278          33 :     auto oIter = m_oSRSCache.find(nId);
    2279          33 :     if (oIter != m_oSRSCache.end())
    2280             :     {
    2281           6 :         return oIter->second.get();
    2282             :     }
    2283             : 
    2284          27 :     EndCopy();
    2285             : 
    2286             :     /* -------------------------------------------------------------------- */
    2287             :     /*      Try looking up in spatial_ref_sys table.                        */
    2288             :     /* -------------------------------------------------------------------- */
    2289          54 :     CPLString osCommand;
    2290          27 :     std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSRS;
    2291             : 
    2292          27 :     osCommand.Printf("SELECT srtext, auth_name, auth_srid FROM spatial_ref_sys "
    2293             :                      "WHERE srid = %d",
    2294          27 :                      nId);
    2295          27 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2296             : 
    2297          54 :     if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
    2298          27 :         PQntuples(hResult) == 1)
    2299             :     {
    2300          27 :         const char *pszWKT = PQgetvalue(hResult, 0, 0);
    2301          27 :         const char *pszAuthName = PQgetvalue(hResult, 0, 1);
    2302          27 :         const char *pszAuthSRID = PQgetvalue(hResult, 0, 2);
    2303          27 :         poSRS.reset(new OGRSpatialReference());
    2304          27 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2305             : 
    2306             :         // Try to import first from EPSG code, and then from WKT
    2307          27 :         if (pszAuthName && pszAuthSRID && EQUAL(pszAuthName, "EPSG") &&
    2308          78 :             atoi(pszAuthSRID) == nId &&
    2309          24 :             poSRS->importFromEPSG(nId) == OGRERR_NONE)
    2310             :         {
    2311             :             // do nothing
    2312             :         }
    2313           3 :         else if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
    2314             :         {
    2315           0 :             poSRS.reset();
    2316             :         }
    2317             :     }
    2318             :     else
    2319             :     {
    2320           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Could not fetch SRS: %s",
    2321           0 :                  PQerrorMessage(hPGConn));
    2322             :     }
    2323             : 
    2324          27 :     OGRPGClearResult(hResult);
    2325             : 
    2326          27 :     if (poSRS)
    2327          27 :         poSRS->StripTOWGS84IfKnownDatumAndAllowed();
    2328             : 
    2329             :     /* -------------------------------------------------------------------- */
    2330             :     /*      Add to the cache.                                               */
    2331             :     /* -------------------------------------------------------------------- */
    2332          27 :     oIter = m_oSRSCache.emplace(nId, std::move(poSRS)).first;
    2333          27 :     return oIter->second.get();
    2334             : }
    2335             : 
    2336             : /************************************************************************/
    2337             : /*                             FetchSRSId()                             */
    2338             : /*                                                                      */
    2339             : /*      Fetch the id corresponding to an SRS, and if not found, add     */
    2340             : /*      it to the table.                                                */
    2341             : /************************************************************************/
    2342             : 
    2343          18 : int OGRPGDataSource::FetchSRSId(const OGRSpatialReference *poSRS)
    2344             : 
    2345             : {
    2346          18 :     if (poSRS == nullptr || !m_bHasSpatialRefSys)
    2347           0 :         return nUndefinedSRID;
    2348             : 
    2349          36 :     OGRSpatialReference oSRS(*poSRS);
    2350             :     // cppcheck-suppress uselessAssignmentPtrArg
    2351          18 :     poSRS = nullptr;
    2352             : 
    2353          18 :     const char *pszAuthorityName = oSRS.GetAuthorityName(nullptr);
    2354             : 
    2355          18 :     if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
    2356             :     {
    2357             :         /* --------------------------------------------------------------------
    2358             :          */
    2359             :         /*      Try to identify an EPSG code */
    2360             :         /* --------------------------------------------------------------------
    2361             :          */
    2362           2 :         oSRS.AutoIdentifyEPSG();
    2363             : 
    2364           2 :         pszAuthorityName = oSRS.GetAuthorityName(nullptr);
    2365           2 :         if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
    2366             :         {
    2367           1 :             const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
    2368           1 :             if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
    2369             :             {
    2370             :                 /* Import 'clean' SRS */
    2371           1 :                 oSRS.importFromEPSG(atoi(pszAuthorityCode));
    2372             : 
    2373           1 :                 pszAuthorityName = oSRS.GetAuthorityName(nullptr);
    2374             :             }
    2375             :         }
    2376             :     }
    2377             :     /* -------------------------------------------------------------------- */
    2378             :     /*      Check whether the authority name/code is already mapped to a    */
    2379             :     /*      SRS ID.                                                         */
    2380             :     /* -------------------------------------------------------------------- */
    2381          36 :     CPLString osCommand;
    2382          18 :     int nAuthorityCode = 0;
    2383          18 :     if (pszAuthorityName != nullptr)
    2384             :     {
    2385             :         /* Check that the authority code is integral */
    2386          17 :         nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
    2387          17 :         if (nAuthorityCode > 0)
    2388             :         {
    2389             :             osCommand.Printf("SELECT srid FROM spatial_ref_sys WHERE "
    2390             :                              "auth_name = '%s' AND auth_srid = %d",
    2391          17 :                              pszAuthorityName, nAuthorityCode);
    2392          17 :             PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2393             : 
    2394          34 :             if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
    2395          17 :                 PQntuples(hResult) > 0)
    2396             :             {
    2397          14 :                 int nSRSId = atoi(PQgetvalue(hResult, 0, 0));
    2398             : 
    2399          14 :                 OGRPGClearResult(hResult);
    2400             : 
    2401          14 :                 return nSRSId;
    2402             :             }
    2403             : 
    2404           3 :             OGRPGClearResult(hResult);
    2405             :         }
    2406             :     }
    2407             : 
    2408             :     /* -------------------------------------------------------------------- */
    2409             :     /*      Translate SRS to WKT.                                           */
    2410             :     /* -------------------------------------------------------------------- */
    2411           4 :     char *pszWKT = nullptr;
    2412           4 :     if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
    2413             :     {
    2414           0 :         CPLFree(pszWKT);
    2415           0 :         return nUndefinedSRID;
    2416             :     }
    2417             : 
    2418             :     /* -------------------------------------------------------------------- */
    2419             :     /*      Try to find in the existing table.                              */
    2420             :     /* -------------------------------------------------------------------- */
    2421             :     CPLString osWKT =
    2422           8 :         OGRPGEscapeString(hPGConn, pszWKT, -1, "spatial_ref_sys", "srtext");
    2423             :     osCommand.Printf("SELECT srid FROM spatial_ref_sys WHERE srtext = %s",
    2424           4 :                      osWKT.c_str());
    2425           4 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2426           4 :     CPLFree(pszWKT);   // CM:  Added to prevent mem leaks
    2427           4 :     pszWKT = nullptr;  // CM:  Added
    2428             : 
    2429             :     /* -------------------------------------------------------------------- */
    2430             :     /*      We got it!  Return it.                                          */
    2431             :     /* -------------------------------------------------------------------- */
    2432           8 :     if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
    2433           4 :         PQntuples(hResult) > 0)
    2434             :     {
    2435           0 :         int nSRSId = atoi(PQgetvalue(hResult, 0, 0));
    2436             : 
    2437           0 :         OGRPGClearResult(hResult);
    2438             : 
    2439           0 :         return nSRSId;
    2440             :     }
    2441             : 
    2442             :     /* -------------------------------------------------------------------- */
    2443             :     /*      If the command actually failed, then the metadata table is      */
    2444             :     /*      likely missing. Try defining it.                                */
    2445             :     /* -------------------------------------------------------------------- */
    2446             :     const bool bTableMissing =
    2447           4 :         hResult == nullptr || PQresultStatus(hResult) == PGRES_NONFATAL_ERROR;
    2448             : 
    2449           4 :     OGRPGClearResult(hResult);
    2450             : 
    2451           4 :     if (bTableMissing)
    2452             :     {
    2453           0 :         if (InitializeMetadataTables() != OGRERR_NONE)
    2454           0 :             return nUndefinedSRID;
    2455             :     }
    2456             : 
    2457             :     /* -------------------------------------------------------------------- */
    2458             :     /*      Get the current maximum srid in the srs table.                  */
    2459             :     /* -------------------------------------------------------------------- */
    2460           4 :     hResult = OGRPG_PQexec(hPGConn, "SELECT MAX(srid) FROM spatial_ref_sys");
    2461             : 
    2462           4 :     int nSRSId = 1;
    2463           4 :     if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
    2464             :     {
    2465           4 :         nSRSId = atoi(PQgetvalue(hResult, 0, 0)) + 1;
    2466           4 :         OGRPGClearResult(hResult);
    2467             :     }
    2468             : 
    2469             :     /* -------------------------------------------------------------------- */
    2470             :     /*      Try adding the SRS to the SRS table.                            */
    2471             :     /* -------------------------------------------------------------------- */
    2472           4 :     char *pszProj4 = nullptr;
    2473           4 :     if (oSRS.exportToProj4(&pszProj4) != OGRERR_NONE)
    2474             :     {
    2475           0 :         CPLFree(pszProj4);
    2476           0 :         return nUndefinedSRID;
    2477             :     }
    2478             : 
    2479           4 :     CPLString osProj4 = OGRPGEscapeString(hPGConn, pszProj4, -1,
    2480           4 :                                           "spatial_ref_sys", "proj4text");
    2481             : 
    2482           4 :     if (pszAuthorityName != nullptr && nAuthorityCode > 0)
    2483             :     {
    2484           3 :         nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
    2485             : 
    2486           3 :         osCommand.Printf("INSERT INTO spatial_ref_sys "
    2487             :                          "(srid,srtext,proj4text,auth_name,auth_srid) "
    2488             :                          "VALUES (%d, %s, %s, '%s', %d)",
    2489             :                          nSRSId, osWKT.c_str(), osProj4.c_str(),
    2490           3 :                          pszAuthorityName, nAuthorityCode);
    2491             :     }
    2492             :     else
    2493             :     {
    2494             :         osCommand.Printf("INSERT INTO spatial_ref_sys (srid,srtext,proj4text) "
    2495             :                          "VALUES (%d,%s,%s)",
    2496           1 :                          nSRSId, osWKT.c_str(), osProj4.c_str());
    2497             :     }
    2498             : 
    2499             :     // Free everything that was allocated.
    2500           4 :     CPLFree(pszProj4);
    2501           4 :     CPLFree(pszWKT);
    2502             : 
    2503           4 :     hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2504           4 :     OGRPGClearResult(hResult);
    2505             : 
    2506           4 :     return nSRSId;
    2507             : }
    2508             : 
    2509             : /************************************************************************/
    2510             : /*                         StartTransaction()                           */
    2511             : /*                                                                      */
    2512             : /* Should only be called by user code. Not driver internals.            */
    2513             : /************************************************************************/
    2514             : 
    2515          74 : OGRErr OGRPGDataSource::StartTransaction(CPL_UNUSED int bForce)
    2516             : {
    2517          74 :     if (bUserTransactionActive)
    2518             :     {
    2519           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2520             :                  "Transaction already established");
    2521           2 :         return OGRERR_FAILURE;
    2522             :     }
    2523             : 
    2524          72 :     CPLAssert(!bSavePointActive);
    2525          72 :     EndCopy();
    2526             : 
    2527          72 :     if (nSoftTransactionLevel == 0)
    2528             :     {
    2529          66 :         OGRErr eErr = DoTransactionCommand("BEGIN");
    2530          66 :         if (eErr != OGRERR_NONE)
    2531           0 :             return eErr;
    2532             :     }
    2533             :     else
    2534             :     {
    2535           6 :         OGRErr eErr = DoTransactionCommand("SAVEPOINT ogr_savepoint");
    2536           6 :         if (eErr != OGRERR_NONE)
    2537           0 :             return eErr;
    2538             : 
    2539           6 :         bSavePointActive = TRUE;
    2540             :     }
    2541             : 
    2542          72 :     nSoftTransactionLevel++;
    2543          72 :     bUserTransactionActive = true;
    2544             : 
    2545             :     /*CPLDebug("PG", "poDS=%p StartTransaction() nSoftTransactionLevel=%d",
    2546             :              this, nSoftTransactionLevel);*/
    2547             : 
    2548          72 :     return OGRERR_NONE;
    2549             : }
    2550             : 
    2551             : /************************************************************************/
    2552             : /*                         CommitTransaction()                          */
    2553             : /*                                                                      */
    2554             : /* Should only be called by user code. Not driver internals.            */
    2555             : /************************************************************************/
    2556             : 
    2557          58 : OGRErr OGRPGDataSource::CommitTransaction()
    2558             : {
    2559          58 :     if (!bUserTransactionActive)
    2560             :     {
    2561           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
    2562           2 :         return OGRERR_FAILURE;
    2563             :     }
    2564             : 
    2565             :     /*CPLDebug("PG", "poDS=%p CommitTransaction() nSoftTransactionLevel=%d",
    2566             :              this, nSoftTransactionLevel);*/
    2567             : 
    2568          56 :     OGRErr eErr = FlushCacheWithRet(false);
    2569          56 :     if (eErr != OGRERR_NONE)
    2570             :     {
    2571           1 :         RollbackTransaction();
    2572           1 :         return eErr;
    2573             :     }
    2574             : 
    2575          55 :     nSoftTransactionLevel--;
    2576          55 :     bUserTransactionActive = false;
    2577             : 
    2578          55 :     if (bSavePointActive)
    2579             :     {
    2580           4 :         CPLAssert(nSoftTransactionLevel > 0);
    2581           4 :         bSavePointActive = FALSE;
    2582             : 
    2583           4 :         eErr = DoTransactionCommand("RELEASE SAVEPOINT ogr_savepoint");
    2584             :     }
    2585             :     else
    2586             :     {
    2587          51 :         if (nSoftTransactionLevel > 0)
    2588             :         {
    2589             :             // This means we have cursors still in progress
    2590           7 :             for (int i = 0; i < nLayers; i++)
    2591           5 :                 papoLayers[i]->InvalidateCursor();
    2592           2 :             CPLAssert(nSoftTransactionLevel == 0);
    2593             :         }
    2594             : 
    2595          51 :         eErr = DoTransactionCommand("COMMIT");
    2596             :     }
    2597             : 
    2598          55 :     return eErr;
    2599             : }
    2600             : 
    2601             : /************************************************************************/
    2602             : /*                        RollbackTransaction()                         */
    2603             : /*                                                                      */
    2604             : /* Should only be called by user code. Not driver internals.            */
    2605             : /************************************************************************/
    2606             : 
    2607          19 : OGRErr OGRPGDataSource::RollbackTransaction()
    2608             : {
    2609          19 :     if (!bUserTransactionActive)
    2610             :     {
    2611           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
    2612           2 :         return OGRERR_FAILURE;
    2613             :     }
    2614             : 
    2615             :     /*CPLDebug("PG", "poDS=%p RollbackTransaction() nSoftTransactionLevel=%d",
    2616             :              this, nSoftTransactionLevel);*/
    2617             : 
    2618          17 :     FlushCache(false);
    2619             : 
    2620          17 :     nSoftTransactionLevel--;
    2621          17 :     bUserTransactionActive = false;
    2622             : 
    2623             :     OGRErr eErr;
    2624          17 :     if (bSavePointActive)
    2625             :     {
    2626           2 :         CPLAssert(nSoftTransactionLevel > 0);
    2627           2 :         bSavePointActive = FALSE;
    2628             : 
    2629           2 :         eErr = DoTransactionCommand("ROLLBACK TO SAVEPOINT ogr_savepoint");
    2630             :     }
    2631             :     else
    2632             :     {
    2633          15 :         if (nSoftTransactionLevel > 0)
    2634             :         {
    2635             :             // This means we have cursors still in progress
    2636           0 :             for (int i = 0; i < nLayers; i++)
    2637           0 :                 papoLayers[i]->InvalidateCursor();
    2638           0 :             CPLAssert(nSoftTransactionLevel == 0);
    2639             :         }
    2640             : 
    2641          15 :         eErr = DoTransactionCommand("ROLLBACK");
    2642             :     }
    2643             : 
    2644          17 :     return eErr;
    2645             : }
    2646             : 
    2647             : /************************************************************************/
    2648             : /*                        SoftStartTransaction()                        */
    2649             : /*                                                                      */
    2650             : /*      Create a transaction scope.  If we already have a               */
    2651             : /*      transaction active this isn't a real transaction, but just      */
    2652             : /*      an increment to the scope count.                                */
    2653             : /************************************************************************/
    2654             : 
    2655         908 : OGRErr OGRPGDataSource::SoftStartTransaction()
    2656             : 
    2657             : {
    2658         908 :     nSoftTransactionLevel++;
    2659             :     /*CPLDebug("PG", "poDS=%p SoftStartTransaction() nSoftTransactionLevel=%d",
    2660             :              this, nSoftTransactionLevel);*/
    2661             : 
    2662         908 :     OGRErr eErr = OGRERR_NONE;
    2663         908 :     if (nSoftTransactionLevel == 1)
    2664             :     {
    2665         823 :         eErr = DoTransactionCommand("BEGIN");
    2666             :     }
    2667             : 
    2668         908 :     return eErr;
    2669             : }
    2670             : 
    2671             : /************************************************************************/
    2672             : /*                     SoftCommitTransaction()                          */
    2673             : /*                                                                      */
    2674             : /*      Commit the current transaction if we are at the outer           */
    2675             : /*      scope.                                                          */
    2676             : /************************************************************************/
    2677             : 
    2678         906 : OGRErr OGRPGDataSource::SoftCommitTransaction()
    2679             : 
    2680             : {
    2681         906 :     EndCopy();
    2682             : 
    2683             :     /*CPLDebug("PG", "poDS=%p SoftCommitTransaction() nSoftTransactionLevel=%d",
    2684             :              this, nSoftTransactionLevel);*/
    2685             : 
    2686         906 :     if (nSoftTransactionLevel <= 0)
    2687             :     {
    2688           0 :         CPLAssert(false);
    2689             :         return OGRERR_FAILURE;
    2690             :     }
    2691             : 
    2692         906 :     OGRErr eErr = OGRERR_NONE;
    2693         906 :     nSoftTransactionLevel--;
    2694         906 :     if (nSoftTransactionLevel == 0)
    2695             :     {
    2696         823 :         CPLAssert(!bSavePointActive);
    2697             : 
    2698         823 :         eErr = DoTransactionCommand("COMMIT");
    2699             :     }
    2700             : 
    2701         906 :     return eErr;
    2702             : }
    2703             : 
    2704             : /************************************************************************/
    2705             : /*                  SoftRollbackTransaction()                           */
    2706             : /*                                                                      */
    2707             : /*      Do a rollback of the current transaction if we are at the 1st   */
    2708             : /*      level                                                           */
    2709             : /************************************************************************/
    2710             : 
    2711           2 : OGRErr OGRPGDataSource::SoftRollbackTransaction()
    2712             : 
    2713             : {
    2714           2 :     EndCopy();
    2715             : 
    2716             :     /*CPLDebug("PG", "poDS=%p SoftRollbackTransaction()
    2717             :        nSoftTransactionLevel=%d", this, nSoftTransactionLevel);*/
    2718             : 
    2719           2 :     if (nSoftTransactionLevel <= 0)
    2720             :     {
    2721           0 :         CPLAssert(false);
    2722             :         return OGRERR_FAILURE;
    2723             :     }
    2724             : 
    2725           2 :     OGRErr eErr = OGRERR_NONE;
    2726           2 :     nSoftTransactionLevel--;
    2727           2 :     if (nSoftTransactionLevel == 0)
    2728             :     {
    2729           2 :         CPLAssert(!bSavePointActive);
    2730             : 
    2731           2 :         eErr = DoTransactionCommand("ROLLBACK");
    2732             :     }
    2733             : 
    2734           2 :     return eErr;
    2735             : }
    2736             : 
    2737             : /************************************************************************/
    2738             : /*                        FlushSoftTransaction()                        */
    2739             : /*                                                                      */
    2740             : /*      Force the unwinding of any active transaction, and its          */
    2741             : /*      commit. Should only be used by datasource destructor            */
    2742             : /************************************************************************/
    2743             : 
    2744         408 : OGRErr OGRPGDataSource::FlushSoftTransaction()
    2745             : 
    2746             : {
    2747             :     /*CPLDebug("PG", "poDS=%p FlushSoftTransaction() nSoftTransactionLevel=%d",
    2748             :              this, nSoftTransactionLevel);*/
    2749             : 
    2750         408 :     if (nSoftTransactionLevel <= 0)
    2751         408 :         return OGRERR_NONE;
    2752             : 
    2753           0 :     bSavePointActive = FALSE;
    2754             : 
    2755           0 :     CPLAssert(nSoftTransactionLevel == 1);
    2756           0 :     nSoftTransactionLevel = 0;
    2757           0 :     return DoTransactionCommand("COMMIT");
    2758             : }
    2759             : 
    2760             : /************************************************************************/
    2761             : /*                          DoTransactionCommand()                      */
    2762             : /************************************************************************/
    2763             : 
    2764        1792 : OGRErr OGRPGDataSource::DoTransactionCommand(const char *pszCommand)
    2765             : 
    2766             : {
    2767        1792 :     OGRErr eErr = OGRERR_NONE;
    2768        1792 :     PGconn *l_hPGConn = GetPGConn();
    2769             : 
    2770        1792 :     PGresult *hResult = OGRPG_PQexec(l_hPGConn, pszCommand);
    2771        1792 :     osDebugLastTransactionCommand = pszCommand;
    2772             : 
    2773        1792 :     if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2774             :     {
    2775           0 :         eErr = OGRERR_FAILURE;
    2776             :     }
    2777             : 
    2778        1792 :     OGRPGClearResult(hResult);
    2779             : 
    2780        1792 :     return eErr;
    2781             : }
    2782             : 
    2783             : /************************************************************************/
    2784             : /*                     OGRPGNoResetResultLayer                          */
    2785             : /************************************************************************/
    2786             : 
    2787             : class OGRPGNoResetResultLayer final : public OGRPGLayer
    2788             : {
    2789             :   public:
    2790             :     OGRPGNoResetResultLayer(OGRPGDataSource *poDSIn, PGresult *hResultIn);
    2791             : 
    2792             :     virtual ~OGRPGNoResetResultLayer();
    2793             : 
    2794             :     virtual void ResetReading() override;
    2795             : 
    2796           0 :     virtual int TestCapability(const char *) override
    2797             :     {
    2798           0 :         return FALSE;
    2799             :     }
    2800             : 
    2801             :     virtual OGRFeature *GetNextFeature() override;
    2802             : 
    2803           0 :     virtual CPLString GetFromClauseForGetExtent() override
    2804             :     {
    2805           0 :         CPLAssert(false);
    2806             :         return "";
    2807             :     }
    2808             : 
    2809          47 :     virtual void ResolveSRID(const OGRPGGeomFieldDefn *poGFldDefn) override
    2810             :     {
    2811          47 :         poGFldDefn->nSRSId = -1;
    2812          47 :     }
    2813             : };
    2814             : 
    2815             : /************************************************************************/
    2816             : /*                     OGRPGNoResetResultLayer()                        */
    2817             : /************************************************************************/
    2818             : 
    2819          95 : OGRPGNoResetResultLayer::OGRPGNoResetResultLayer(OGRPGDataSource *poDSIn,
    2820          95 :                                                  PGresult *hResultIn)
    2821             : {
    2822          95 :     poDS = poDSIn;
    2823          95 :     ReadResultDefinition(hResultIn);
    2824          95 :     hCursorResult = hResultIn;
    2825          95 :     CreateMapFromFieldNameToIndex(hCursorResult, poFeatureDefn,
    2826          95 :                                   m_panMapFieldNameToIndex,
    2827          95 :                                   m_panMapFieldNameToGeomIndex);
    2828          95 : }
    2829             : 
    2830             : /************************************************************************/
    2831             : /*                   ~OGRPGNoResetResultLayer()                         */
    2832             : /************************************************************************/
    2833             : 
    2834         380 : OGRPGNoResetResultLayer::~OGRPGNoResetResultLayer()
    2835             : 
    2836             : {
    2837          95 :     OGRPGClearResult(hCursorResult);
    2838          95 :     hCursorResult = nullptr;
    2839         190 : }
    2840             : 
    2841             : /************************************************************************/
    2842             : /*                            ResetReading()                            */
    2843             : /************************************************************************/
    2844             : 
    2845          95 : void OGRPGNoResetResultLayer::ResetReading()
    2846             : {
    2847          95 :     iNextShapeId = 0;
    2848          95 : }
    2849             : 
    2850             : /************************************************************************/
    2851             : /*                           GetNextFeature()                           */
    2852             : /************************************************************************/
    2853             : 
    2854         190 : OGRFeature *OGRPGNoResetResultLayer::GetNextFeature()
    2855             : 
    2856             : {
    2857         190 :     if (iNextShapeId == PQntuples(hCursorResult))
    2858             :     {
    2859          95 :         return nullptr;
    2860             :     }
    2861         190 :     return RecordToFeature(hCursorResult, m_panMapFieldNameToIndex,
    2862          95 :                            m_panMapFieldNameToGeomIndex,
    2863          95 :                            static_cast<int>(iNextShapeId++));
    2864             : }
    2865             : 
    2866             : /************************************************************************/
    2867             : /*                      OGRPGMemLayerWrapper                            */
    2868             : /************************************************************************/
    2869             : 
    2870             : class OGRPGMemLayerWrapper final : public OGRLayer
    2871             : {
    2872             :   private:
    2873             :     OGRPGMemLayerWrapper(const OGRPGMemLayerWrapper &) = delete;
    2874             :     OGRPGMemLayerWrapper &operator=(const OGRPGMemLayerWrapper &) = delete;
    2875             : 
    2876             :     GDALDataset *poMemDS = nullptr;
    2877             :     OGRLayer *poMemLayer = nullptr;
    2878             : 
    2879             :   public:
    2880          95 :     explicit OGRPGMemLayerWrapper(GDALDataset *poMemDSIn)
    2881          95 :     {
    2882          95 :         poMemDS = poMemDSIn;
    2883          95 :         poMemLayer = poMemDS->GetLayer(0);
    2884          95 :     }
    2885             : 
    2886         188 :     virtual ~OGRPGMemLayerWrapper()
    2887          94 :     {
    2888          94 :         delete poMemDS;
    2889         188 :     }
    2890             : 
    2891           0 :     virtual void ResetReading() override
    2892             :     {
    2893           0 :         poMemLayer->ResetReading();
    2894           0 :     }
    2895             : 
    2896          79 :     virtual OGRFeature *GetNextFeature() override
    2897             :     {
    2898          79 :         return poMemLayer->GetNextFeature();
    2899             :     }
    2900             : 
    2901           0 :     virtual OGRFeatureDefn *GetLayerDefn() override
    2902             :     {
    2903           0 :         return poMemLayer->GetLayerDefn();
    2904             :     }
    2905             : 
    2906           0 :     virtual int TestCapability(const char *) override
    2907             :     {
    2908           0 :         return FALSE;
    2909             :     }
    2910             : };
    2911             : 
    2912             : /************************************************************************/
    2913             : /*                           GetMetadataItem()                          */
    2914             : /************************************************************************/
    2915             : 
    2916         509 : const char *OGRPGDataSource::GetMetadataItem(const char *pszKey,
    2917             :                                              const char *pszDomain)
    2918             : {
    2919             :     /* Only used by ogr_pg.py to check inner working */
    2920         509 :     if (pszDomain != nullptr && EQUAL(pszDomain, "_debug_") &&
    2921             :         pszKey != nullptr)
    2922             :     {
    2923         278 :         if (EQUAL(pszKey, "bHasLoadTables"))
    2924          10 :             return CPLSPrintf("%d", bHasLoadTables);
    2925         268 :         if (EQUAL(pszKey, "nSoftTransactionLevel"))
    2926          70 :             return CPLSPrintf("%d", nSoftTransactionLevel);
    2927         198 :         if (EQUAL(pszKey, "bSavePointActive"))
    2928          66 :             return CPLSPrintf("%d", bSavePointActive);
    2929         132 :         if (EQUAL(pszKey, "bUserTransactionActive"))
    2930          66 :             return CPLSPrintf("%d", bUserTransactionActive);
    2931          66 :         if (EQUAL(pszKey, "osDebugLastTransactionCommand"))
    2932             :         {
    2933             :             const char *pszRet =
    2934          66 :                 CPLSPrintf("%s", osDebugLastTransactionCommand.c_str());
    2935          66 :             osDebugLastTransactionCommand = "";
    2936          66 :             return pszRet;
    2937             :         }
    2938             :     }
    2939         231 :     return GDALDataset::GetMetadataItem(pszKey, pszDomain);
    2940             : }
    2941             : 
    2942             : /************************************************************************/
    2943             : /*                             ExecuteSQL()                             */
    2944             : /************************************************************************/
    2945             : 
    2946        1239 : OGRLayer *OGRPGDataSource::ExecuteSQL(const char *pszSQLCommand,
    2947             :                                       OGRGeometry *poSpatialFilter,
    2948             :                                       const char *pszDialect)
    2949             : 
    2950             : {
    2951             :     /* Skip leading whitespace characters */
    2952        1239 :     while (std::isspace(static_cast<unsigned char>(*pszSQLCommand)))
    2953           0 :         pszSQLCommand++;
    2954             : 
    2955        1239 :     FlushCache(false);
    2956             : 
    2957             :     /* -------------------------------------------------------------------- */
    2958             :     /*      Use generic implementation for recognized dialects              */
    2959             :     /* -------------------------------------------------------------------- */
    2960        1239 :     if (IsGenericSQLDialect(pszDialect))
    2961           0 :         return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
    2962           0 :                                        pszDialect);
    2963             : 
    2964             :     /* -------------------------------------------------------------------- */
    2965             :     /*      Special case DELLAYER: command.                                 */
    2966             :     /* -------------------------------------------------------------------- */
    2967        1239 :     if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
    2968             :     {
    2969           6 :         const char *pszLayerName = pszSQLCommand + 9;
    2970             : 
    2971           6 :         while (*pszLayerName == ' ')
    2972           0 :             pszLayerName++;
    2973             : 
    2974           6 :         GetLayerCount();
    2975           8 :         for (int iLayer = 0; iLayer < nLayers; iLayer++)
    2976             :         {
    2977           6 :             if (EQUAL(papoLayers[iLayer]->GetName(), pszLayerName))
    2978             :             {
    2979           4 :                 DeleteLayer(iLayer);
    2980           4 :                 break;
    2981             :             }
    2982             :         }
    2983           6 :         return nullptr;
    2984             :     }
    2985             : 
    2986             :     /* -------------------------------------------------------------------- */
    2987             :     /*      Execute the statement.                                          */
    2988             :     /* -------------------------------------------------------------------- */
    2989        1233 :     PGresult *hResult = nullptr;
    2990             : 
    2991        1233 :     if (STARTS_WITH_CI(pszSQLCommand, "SELECT") == FALSE ||
    2992         211 :         (strstr(pszSQLCommand, "from") == nullptr &&
    2993         198 :          strstr(pszSQLCommand, "FROM") == nullptr))
    2994             :     {
    2995             :         /* For something that is not a select or a select without table, do not
    2996             :          */
    2997             :         /* run under transaction (CREATE DATABASE, VACUUM don't like
    2998             :          * transactions) */
    2999             : 
    3000        1112 :         hResult =
    3001        1112 :             OGRPG_PQexec(hPGConn, pszSQLCommand, TRUE /* multiple allowed */);
    3002        1112 :         if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
    3003             :         {
    3004          95 :             CPLDebug("PG", "Command Results Tuples = %d", PQntuples(hResult));
    3005             : 
    3006             :             GDALDriver *poMemDriver =
    3007          95 :                 GetGDALDriverManager()->GetDriverByName("Memory");
    3008          95 :             if (poMemDriver)
    3009             :             {
    3010             :                 OGRPGLayer *poResultLayer =
    3011          95 :                     new OGRPGNoResetResultLayer(this, hResult);
    3012             :                 GDALDataset *poMemDS =
    3013          95 :                     poMemDriver->Create("", 0, 0, 0, GDT_Unknown, nullptr);
    3014          95 :                 poMemDS->CopyLayer(poResultLayer, "sql_statement");
    3015             :                 OGRPGMemLayerWrapper *poResLayer =
    3016          95 :                     new OGRPGMemLayerWrapper(poMemDS);
    3017          95 :                 delete poResultLayer;
    3018          95 :                 return poResLayer;
    3019             :             }
    3020             :             else
    3021           0 :                 return nullptr;
    3022        1017 :         }
    3023             :     }
    3024             :     else
    3025             :     {
    3026         121 :         SoftStartTransaction();
    3027             : 
    3028         121 :         CPLString osCommand;
    3029             :         osCommand.Printf("DECLARE %s CURSOR for %s", "executeSQLCursor",
    3030         121 :                          pszSQLCommand);
    3031             : 
    3032         121 :         hResult = OGRPG_PQexec(hPGConn, osCommand);
    3033             : 
    3034             :         /* --------------------------------------------------------------------
    3035             :          */
    3036             :         /*      Do we have a tuple result? If so, instantiate a results */
    3037             :         /*      layer for it. */
    3038             :         /* --------------------------------------------------------------------
    3039             :          */
    3040         121 :         if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
    3041             :         {
    3042         120 :             OGRPGClearResult(hResult);
    3043             : 
    3044         120 :             osCommand.Printf("FETCH 0 in %s", "executeSQLCursor");
    3045         120 :             hResult = OGRPG_PQexec(hPGConn, osCommand);
    3046             : 
    3047             :             OGRPGResultLayer *poLayer =
    3048         120 :                 new OGRPGResultLayer(this, pszSQLCommand, hResult);
    3049             : 
    3050         120 :             OGRPGClearResult(hResult);
    3051             : 
    3052         120 :             osCommand.Printf("CLOSE %s", "executeSQLCursor");
    3053         120 :             hResult = OGRPG_PQexec(hPGConn, osCommand);
    3054         120 :             OGRPGClearResult(hResult);
    3055             : 
    3056         120 :             SoftCommitTransaction();
    3057             : 
    3058         120 :             if (poSpatialFilter != nullptr)
    3059           5 :                 poLayer->SetSpatialFilter(poSpatialFilter);
    3060             : 
    3061         120 :             return poLayer;
    3062             :         }
    3063             :         else
    3064             :         {
    3065           1 :             SoftRollbackTransaction();
    3066             :         }
    3067             :     }
    3068             : 
    3069        1018 :     OGRPGClearResult(hResult);
    3070             : 
    3071        1018 :     return nullptr;
    3072             : }
    3073             : 
    3074             : /************************************************************************/
    3075             : /*                          AbortSQL()                                  */
    3076             : /************************************************************************/
    3077             : 
    3078           5 : OGRErr OGRPGDataSource::AbortSQL()
    3079             : {
    3080           5 :     auto cancel = PQgetCancel(hPGConn);
    3081             :     int result;
    3082           5 :     if (cancel)
    3083             :     {
    3084             :         char errbuf[255];
    3085           5 :         result = PQcancel(cancel, errbuf, 255);
    3086           5 :         if (!result)
    3087           0 :             CPLDebug("PG", "Error canceling the query: %s", errbuf);
    3088           5 :         PQfreeCancel(cancel);
    3089           5 :         return result ? OGRERR_NONE : OGRERR_FAILURE;
    3090             :     }
    3091           0 :     return OGRERR_FAILURE;
    3092             : }
    3093             : 
    3094             : /************************************************************************/
    3095             : /*                          ReleaseResultSet()                          */
    3096             : /************************************************************************/
    3097             : 
    3098         217 : void OGRPGDataSource::ReleaseResultSet(OGRLayer *poLayer)
    3099             : 
    3100             : {
    3101         217 :     delete poLayer;
    3102         217 : }
    3103             : 
    3104             : /************************************************************************/
    3105             : /*                             StartCopy()                              */
    3106             : /************************************************************************/
    3107             : 
    3108        3739 : void OGRPGDataSource::StartCopy(OGRPGTableLayer *poPGLayer)
    3109             : {
    3110        3739 :     if (poLayerInCopyMode == poPGLayer)
    3111        3554 :         return;
    3112         185 :     EndCopy();
    3113         185 :     poLayerInCopyMode = poPGLayer;
    3114         185 :     poLayerInCopyMode->StartCopy();
    3115             : }
    3116             : 
    3117             : /************************************************************************/
    3118             : /*                              EndCopy()                               */
    3119             : /************************************************************************/
    3120             : 
    3121        9580 : OGRErr OGRPGDataSource::EndCopy()
    3122             : {
    3123        9580 :     if (poLayerInCopyMode != nullptr)
    3124             :     {
    3125         185 :         OGRErr result = poLayerInCopyMode->EndCopy();
    3126         185 :         poLayerInCopyMode = nullptr;
    3127             : 
    3128         185 :         return result;
    3129             :     }
    3130             :     else
    3131        9395 :         return OGRERR_NONE;
    3132             : }
    3133             : 
    3134             : /************************************************************************/
    3135             : /*                     CreateMetadataTableIfNeeded()                    */
    3136             : /************************************************************************/
    3137             : 
    3138           4 : bool OGRPGDataSource::CreateMetadataTableIfNeeded()
    3139             : {
    3140           4 :     if (m_bCreateMetadataTableIfNeededRun)
    3141           0 :         return m_bCreateMetadataTableIfNeededSuccess;
    3142             : 
    3143           4 :     m_bCreateMetadataTableIfNeededRun = true;
    3144             : 
    3145           4 :     const bool bIsSuperUser = IsSuperUser();
    3146           4 :     if (!bIsSuperUser && !OGRSystemTablesEventTriggerExists())
    3147             :     {
    3148           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    3149             :                  "User lacks super user privilege to be able to create event "
    3150             :                  "trigger ogr_system_tables_event_trigger_for_metadata");
    3151           1 :         m_bCreateMetadataTableIfNeededSuccess = true;
    3152           1 :         return true;
    3153             :     }
    3154             : 
    3155             :     PGresult *hResult;
    3156             : 
    3157           3 :     hResult = OGRPG_PQexec(
    3158             :         hPGConn,
    3159             :         "SELECT c.oid FROM pg_class c "
    3160             :         "JOIN pg_namespace n ON c.relnamespace=n.oid "
    3161             :         "WHERE c.relname = 'metadata' AND n.nspname = 'ogr_system_tables'");
    3162             :     const bool bFound =
    3163           3 :         (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0));
    3164           3 :     OGRPGClearResult(hResult);
    3165             : 
    3166           3 :     hResult = OGRPG_PQexec(
    3167             :         hPGConn,
    3168             :         "SELECT has_database_privilege((select current_database()), 'CREATE')");
    3169             :     const bool bCanCreateSchema =
    3170           6 :         (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0) &&
    3171           3 :          strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
    3172           3 :     OGRPGClearResult(hResult);
    3173             : 
    3174           3 :     if (!bFound)
    3175             :     {
    3176           2 :         if (!bCanCreateSchema)
    3177             :         {
    3178           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    3179             :                      "User lacks CREATE SCHEMA privilege to be able to create "
    3180             :                      "ogr_system_tables.metadata table");
    3181           0 :             return false;
    3182             :         }
    3183             :     }
    3184             :     else
    3185             :     {
    3186           1 :         if (!HasWritePermissionsOnMetadataTable())
    3187             :         {
    3188           0 :             return false;
    3189             :         }
    3190           1 :         if (!bCanCreateSchema)
    3191             :         {
    3192           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    3193             :                      "User lacks CREATE SCHEMA privilege. Assuming "
    3194             :                      "ogr_system_tables.metadata table has correct structure");
    3195           0 :             m_bCreateMetadataTableIfNeededSuccess = true;
    3196           0 :             return true;
    3197             :         }
    3198             :     }
    3199             : 
    3200           3 :     hResult =
    3201           3 :         OGRPG_PQexec(hPGConn, "CREATE SCHEMA IF NOT EXISTS ogr_system_tables");
    3202           3 :     if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
    3203           0 :                      PQresultStatus(hResult) != PGRES_TUPLES_OK))
    3204             :     {
    3205           0 :         OGRPGClearResult(hResult);
    3206           0 :         return false;
    3207             :     }
    3208           3 :     OGRPGClearResult(hResult);
    3209             : 
    3210           3 :     hResult = OGRPG_PQexec(
    3211             :         hPGConn, "CREATE TABLE IF NOT EXISTS ogr_system_tables.metadata("
    3212             :                  "id SERIAL, "
    3213             :                  "schema_name TEXT NOT NULL, "
    3214             :                  "table_name TEXT NOT NULL, "
    3215             :                  "metadata TEXT,"
    3216             :                  "UNIQUE(schema_name, table_name))");
    3217           3 :     if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
    3218           0 :                      PQresultStatus(hResult) != PGRES_TUPLES_OK))
    3219             :     {
    3220           0 :         OGRPGClearResult(hResult);
    3221           0 :         return false;
    3222             :     }
    3223           3 :     OGRPGClearResult(hResult);
    3224             : 
    3225           3 :     hResult = OGRPG_PQexec(
    3226             :         hPGConn,
    3227             :         "DROP FUNCTION IF EXISTS "
    3228             :         "ogr_system_tables.event_trigger_function_for_metadata() CASCADE");
    3229           3 :     if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
    3230           0 :                      PQresultStatus(hResult) != PGRES_TUPLES_OK))
    3231             :     {
    3232           0 :         OGRPGClearResult(hResult);
    3233           0 :         return false;
    3234             :     }
    3235           3 :     OGRPGClearResult(hResult);
    3236             : 
    3237           3 :     hResult = OGRPG_PQexec(
    3238             :         hPGConn,
    3239             :         "CREATE FUNCTION "
    3240             :         "ogr_system_tables.event_trigger_function_for_metadata()\n"
    3241             :         "RETURNS event_trigger LANGUAGE plpgsql AS $$\n"
    3242             :         "DECLARE\n"
    3243             :         "    obj record;\n"
    3244             :         "BEGIN\n"
    3245             :         "  IF has_schema_privilege('ogr_system_tables', 'USAGE') THEN\n"
    3246             :         "   IF has_table_privilege('ogr_system_tables.metadata', 'DELETE') "
    3247             :         "THEN\n"
    3248             :         "    FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n"
    3249             :         "    LOOP\n"
    3250             :         "        IF obj.object_type = 'table' THEN\n"
    3251             :         "            DELETE FROM ogr_system_tables.metadata m WHERE "
    3252             :         "m.schema_name = obj.schema_name AND m.table_name = "
    3253             :         "obj.object_name;\n"
    3254             :         "        END IF;\n"
    3255             :         "    END LOOP;\n"
    3256             :         "   END IF;\n"
    3257             :         "  END IF;\n"
    3258             :         "END;\n"
    3259             :         "$$;");
    3260           3 :     if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
    3261           0 :                      PQresultStatus(hResult) != PGRES_TUPLES_OK))
    3262             :     {
    3263           0 :         OGRPGClearResult(hResult);
    3264           0 :         return false;
    3265             :     }
    3266           3 :     OGRPGClearResult(hResult);
    3267             : 
    3268           3 :     if (bIsSuperUser)
    3269             :     {
    3270           3 :         hResult = OGRPG_PQexec(hPGConn,
    3271             :                                "DROP EVENT TRIGGER IF EXISTS "
    3272             :                                "ogr_system_tables_event_trigger_for_metadata");
    3273           3 :         if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
    3274           0 :                          PQresultStatus(hResult) != PGRES_TUPLES_OK))
    3275             :         {
    3276           0 :             OGRPGClearResult(hResult);
    3277           0 :             return false;
    3278             :         }
    3279           3 :         OGRPGClearResult(hResult);
    3280             : 
    3281           3 :         hResult = OGRPG_PQexec(
    3282             :             hPGConn,
    3283             :             "CREATE EVENT TRIGGER ogr_system_tables_event_trigger_for_metadata "
    3284             :             "ON sql_drop "
    3285             :             "EXECUTE FUNCTION "
    3286             :             "ogr_system_tables.event_trigger_function_for_metadata()");
    3287           3 :         if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
    3288           0 :                          PQresultStatus(hResult) != PGRES_TUPLES_OK))
    3289             :         {
    3290           0 :             OGRPGClearResult(hResult);
    3291           0 :             return false;
    3292             :         }
    3293           3 :         OGRPGClearResult(hResult);
    3294             :     }
    3295             : 
    3296           3 :     m_bCreateMetadataTableIfNeededSuccess = true;
    3297           3 :     m_bOgrSystemTablesMetadataTableExistenceTested = true;
    3298           3 :     m_bOgrSystemTablesMetadataTableFound = true;
    3299           3 :     return true;
    3300             : }
    3301             : 
    3302             : /************************************************************************/
    3303             : /*                               IsSuperUser()                          */
    3304             : /************************************************************************/
    3305             : 
    3306           4 : bool OGRPGDataSource::IsSuperUser()
    3307             : {
    3308           4 :     PGresult *hResult = OGRPG_PQexec(
    3309           4 :         hPGConn, "SELECT usesuper FROM pg_user WHERE usename = CURRENT_USER");
    3310             :     const bool bRet =
    3311           7 :         (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0) &&
    3312           3 :          strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
    3313           4 :     OGRPGClearResult(hResult);
    3314           4 :     return bRet;
    3315             : }
    3316             : 
    3317             : /************************************************************************/
    3318             : /*                  OGRSystemTablesEventTriggerExists()                 */
    3319             : /************************************************************************/
    3320             : 
    3321           1 : bool OGRPGDataSource::OGRSystemTablesEventTriggerExists()
    3322             : {
    3323             :     PGresult *hResult =
    3324           1 :         OGRPG_PQexec(hPGConn, "SELECT 1 FROM pg_event_trigger WHERE evtname = "
    3325           1 :                               "'ogr_system_tables_event_trigger_for_metadata'");
    3326           1 :     const bool bRet = (hResult && PQntuples(hResult) == 1);
    3327           1 :     OGRPGClearResult(hResult);
    3328           1 :     return bRet;
    3329             : }
    3330             : 
    3331             : /************************************************************************/
    3332             : /*                    HasOgrSystemTablesMetadataTable()                 */
    3333             : /************************************************************************/
    3334             : 
    3335          60 : bool OGRPGDataSource::HasOgrSystemTablesMetadataTable()
    3336             : {
    3337          93 :     if (!m_bOgrSystemTablesMetadataTableExistenceTested &&
    3338          33 :         CPLTestBool(CPLGetConfigOption("OGR_PG_ENABLE_METADATA", "YES")))
    3339             :     {
    3340          30 :         m_bOgrSystemTablesMetadataTableExistenceTested = true;
    3341             :         // Check that the ogr_system_tables.metadata table exists (without
    3342             :         // causing errors that might abort transactions)
    3343          30 :         PGresult *hResult = OGRPG_PQexec(
    3344             :             hPGConn,
    3345             :             "SELECT c.oid FROM pg_class c "
    3346             :             "JOIN pg_namespace n ON c.relnamespace=n.oid "
    3347          30 :             "WHERE c.relname = 'metadata' AND n.nspname = 'ogr_system_tables'");
    3348             :         const bool bFound =
    3349          30 :             (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0));
    3350          30 :         OGRPGClearResult(hResult);
    3351          30 :         if (!bFound)
    3352          14 :             return false;
    3353             : 
    3354          17 :         hResult = OGRPG_PQexec(
    3355             :             hPGConn,
    3356             :             "SELECT has_schema_privilege('ogr_system_tables', 'USAGE')");
    3357             :         const bool bHasSchemaPrivilege =
    3358          17 :             (hResult && PQntuples(hResult) == 1 &&
    3359          51 :              !PQgetisnull(hResult, 0, 0) &&
    3360          17 :              strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
    3361          17 :         OGRPGClearResult(hResult);
    3362          17 :         if (!bHasSchemaPrivilege)
    3363             :         {
    3364           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    3365             :                      "Table ogr_system_tables.metadata exists but user lacks "
    3366             :                      "USAGE privilege on ogr_system_tables schema");
    3367           1 :             return false;
    3368             :         }
    3369             : 
    3370          16 :         hResult = OGRPG_PQexec(
    3371             :             hPGConn, "SELECT has_table_privilege('ogr_system_tables.metadata', "
    3372             :                      "'SELECT')");
    3373          16 :         m_bOgrSystemTablesMetadataTableFound =
    3374          16 :             (hResult && PQntuples(hResult) == 1 &&
    3375          48 :              !PQgetisnull(hResult, 0, 0) &&
    3376          16 :              strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
    3377          16 :         OGRPGClearResult(hResult);
    3378          16 :         if (!m_bOgrSystemTablesMetadataTableFound)
    3379             :         {
    3380           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    3381             :                      "Table ogr_system_tables.metadata exists but user lacks "
    3382             :                      "SELECT privilege on it");
    3383             :         }
    3384             :     }
    3385          46 :     return m_bOgrSystemTablesMetadataTableFound;
    3386             : }
    3387             : 
    3388             : /************************************************************************/
    3389             : /*                   HasWritePermissionsOnMetadataTable()               */
    3390             : /************************************************************************/
    3391             : 
    3392          12 : bool OGRPGDataSource::HasWritePermissionsOnMetadataTable()
    3393             : {
    3394          12 :     if (!m_bHasWritePermissionsOnMetadataTableRun)
    3395             :     {
    3396          11 :         m_bHasWritePermissionsOnMetadataTableRun = true;
    3397             : 
    3398          11 :         if (HasOgrSystemTablesMetadataTable())
    3399             :         {
    3400          10 :             PGresult *hResult = OGRPG_PQexec(
    3401             :                 hPGConn,
    3402             :                 "SELECT has_table_privilege('ogr_system_tables.metadata', "
    3403             :                 "'INSERT') "
    3404             :                 "AND    has_table_privilege('ogr_system_tables.metadata', "
    3405          10 :                 "'DELETE')");
    3406          10 :             m_bHasWritePermissionsOnMetadataTableSuccess =
    3407          10 :                 (hResult && PQntuples(hResult) == 1 &&
    3408          30 :                  !PQgetisnull(hResult, 0, 0) &&
    3409          10 :                  strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
    3410          10 :             OGRPGClearResult(hResult);
    3411          10 :             if (!m_bHasWritePermissionsOnMetadataTableSuccess)
    3412             :             {
    3413           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3414             :                          "User lacks INSERT and/OR DELETE privilege on "
    3415             :                          "ogr_system_tables.metadata table");
    3416             :             }
    3417             :         }
    3418             :     }
    3419          12 :     return m_bHasWritePermissionsOnMetadataTableSuccess;
    3420             : }

Generated by: LCOV version 1.14