LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pg - ogrpgdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1172 1332 88.0 %
Date: 2024-04-27 17:22:41 Functions: 51 57 89.5 %

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

Generated by: LCOV version 1.14