LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pgdump - ogrpgdumpdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 307 340 90.3 %
Date: 2025-07-03 09:54:33 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGRPGDumpDataSource class.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include <algorithm>
      14             : #include <cstring>
      15             : #include "ogr_pgdump.h"
      16             : #include "cpl_conv.h"
      17             : #include "cpl_md5.h"
      18             : #include "cpl_string.h"
      19             : 
      20             : /************************************************************************/
      21             : /*                      OGRPGDumpDataSource()                           */
      22             : /************************************************************************/
      23             : 
      24         103 : OGRPGDumpDataSource::OGRPGDumpDataSource(const char *pszNameIn,
      25         103 :                                          char **papszOptions)
      26             : {
      27         103 :     SetDescription(pszNameIn);
      28             : 
      29         103 :     const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
      30             : 
      31         103 :     bool bUseCRLF = false;
      32         103 :     if (pszCRLFFormat == nullptr)
      33             :     {
      34             : #ifdef _WIN32
      35             :         bUseCRLF = true;
      36             : #endif
      37             :     }
      38          58 :     else if (EQUAL(pszCRLFFormat, "CRLF"))
      39             :     {
      40           1 :         bUseCRLF = true;
      41             :     }
      42          57 :     else if (EQUAL(pszCRLFFormat, "LF"))
      43             :     {
      44          57 :         bUseCRLF = false;
      45             :     }
      46             :     else
      47             :     {
      48           0 :         CPLError(CE_Warning, CPLE_AppDefined,
      49             :                  "LINEFORMAT=%s not understood, use one of CRLF or LF.",
      50             :                  pszCRLFFormat);
      51             : #ifdef _WIN32
      52             :         bUseCRLF = true;
      53             : #endif
      54             :     }
      55             : 
      56         103 :     if (bUseCRLF)
      57           1 :         m_pszEOL = "\r\n";
      58             : 
      59         103 :     m_fp = VSIFOpenL(pszNameIn, "wb");
      60         103 :     if (m_fp == nullptr)
      61             :     {
      62           1 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszNameIn);
      63           1 :         return;
      64             :     }
      65             : }
      66             : 
      67             : /************************************************************************/
      68             : /*                          ~OGRPGDumpDataSource()                          */
      69             : /************************************************************************/
      70             : 
      71         206 : OGRPGDumpDataSource::~OGRPGDumpDataSource()
      72             : 
      73             : {
      74         103 :     EndCopy();
      75         103 :     m_apoLayers.clear();
      76             : 
      77         103 :     if (m_fp)
      78             :     {
      79         102 :         LogCommit();
      80         102 :         VSIFCloseL(m_fp);
      81         102 :         m_fp = nullptr;
      82             :     }
      83         206 : }
      84             : 
      85             : /************************************************************************/
      86             : /*                         LogStartTransaction()                        */
      87             : /************************************************************************/
      88             : 
      89         118 : void OGRPGDumpDataSource::LogStartTransaction()
      90             : {
      91         118 :     if (m_bInTransaction)
      92           0 :         return;
      93         118 :     m_bInTransaction = true;
      94         118 :     Log("BEGIN");
      95             : }
      96             : 
      97             : /************************************************************************/
      98             : /*                             LogCommit()                              */
      99             : /************************************************************************/
     100             : 
     101         220 : void OGRPGDumpDataSource::LogCommit()
     102             : {
     103         220 :     EndCopy();
     104             : 
     105         220 :     if (!m_bInTransaction)
     106         102 :         return;
     107         118 :     m_bInTransaction = false;
     108         118 :     Log("COMMIT");
     109             : }
     110             : 
     111             : /************************************************************************/
     112             : /*                         OGRPGCommonLaunderName()                     */
     113             : /************************************************************************/
     114             : 
     115      212462 : char *OGRPGCommonLaunderName(const char *pszSrcName, const char *pszDebugPrefix,
     116             :                              bool bUTF8ToASCII)
     117             : 
     118             : {
     119      212462 :     char *pszSafeName = bUTF8ToASCII ? CPLUTF8ForceToASCII(pszSrcName, '_')
     120      212456 :                                      : CPLStrdup(pszSrcName);
     121             : 
     122      212462 :     size_t i = 0;  // needed after loop
     123      212462 :     int iUTF8Char = 0;
     124     8917960 :     for (; pszSafeName[i] != '\0'; i++)
     125             :     {
     126     8761120 :         if (static_cast<unsigned char>(pszSafeName[i]) <= 127)
     127             :         {
     128     8761000 :             pszSafeName[i] = static_cast<char>(
     129     8761000 :                 CPLTolower(static_cast<unsigned char>(pszSafeName[i])));
     130     8761000 :             if (pszSafeName[i] == '\'' || pszSafeName[i] == '-' ||
     131     8761000 :                 pszSafeName[i] == '#')
     132             :             {
     133           6 :                 pszSafeName[i] = '_';
     134             :             }
     135             :         }
     136             : 
     137             :         // Truncate string by making sure we don't cut in the
     138             :         // middle of a UTF-8 multibyte character
     139             :         // Continuation bytes of such characters are of the form
     140             :         // 10xxxxxx (0x80), whereas single-byte are 0xxxxxxx
     141             :         // and the start of a multi-byte is 11xxxxxx
     142     8761120 :         if ((pszSafeName[i] & 0xc0) != 0x80)
     143             :         {
     144     8761060 :             ++iUTF8Char;
     145     8761060 :             if (iUTF8Char == OGR_PG_NAMEDATALEN)
     146       55626 :                 break;
     147             :         }
     148             :     }
     149             : 
     150      212462 :     if (iUTF8Char == OGR_PG_NAMEDATALEN && pszSafeName[i] != '\0')
     151             :     {
     152       55626 :         constexpr int FIRST_8_CHARS_OF_MD5 = 8;
     153       55626 :         iUTF8Char = 0;
     154     3059480 :         for (i = 0; pszSafeName[i]; ++i)
     155             :         {
     156     3059480 :             if ((pszSafeName[i] & 0xc0) != 0x80)
     157             :             {
     158     3059430 :                 ++iUTF8Char;
     159     3059430 :                 if (iUTF8Char == OGR_PG_NAMEDATALEN - FIRST_8_CHARS_OF_MD5 - 1)
     160       55626 :                     break;
     161             :             }
     162             :         }
     163       55626 :         pszSafeName[i] = '_';
     164       55626 :         memcpy(pszSafeName + i + 1, CPLMD5String(pszSrcName),
     165             :                FIRST_8_CHARS_OF_MD5);
     166       55626 :         i += FIRST_8_CHARS_OF_MD5 + 1;
     167             :     }
     168             : 
     169      212462 :     pszSafeName[i] = '\0';
     170             : 
     171      212462 :     if (strcmp(pszSrcName, pszSafeName) != 0)
     172             :     {
     173      188668 :         if (CPLStrlenUTF8Ex(pszSafeName) < CPLStrlenUTF8Ex(pszSrcName))
     174             :         {
     175       55626 :             CPLError(CE_Warning, CPLE_AppDefined,
     176             :                      "%s identifier truncated to %s", pszSrcName, pszSafeName);
     177             :         }
     178             :         else
     179             :         {
     180      133042 :             CPLDebug(pszDebugPrefix, "LaunderName('%s') -> '%s'", pszSrcName,
     181             :                      pszSafeName);
     182             :         }
     183             :     }
     184             : 
     185      212462 :     return pszSafeName;
     186             : }
     187             : 
     188             : /************************************************************************/
     189             : /*                           ICreateLayer()                             */
     190             : /************************************************************************/
     191             : 
     192             : OGRLayer *
     193         118 : OGRPGDumpDataSource::ICreateLayer(const char *pszLayerName,
     194             :                                   const OGRGeomFieldDefn *poGeomFieldDefn,
     195             :                                   CSLConstList papszOptions)
     196             : 
     197             : {
     198         118 :     if (STARTS_WITH(pszLayerName, "pg"))
     199             :     {
     200           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     201             :                  "The layer name should not begin by 'pg' as it is a reserved "
     202             :                  "prefix");
     203             :     }
     204             : 
     205         118 :     auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     206             :     const auto poSRS =
     207         118 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     208             : 
     209         118 :     const bool bCreateTable = CPLFetchBool(papszOptions, "CREATE_TABLE", true);
     210             :     const bool bCreateSchema =
     211         118 :         CPLFetchBool(papszOptions, "CREATE_SCHEMA", true);
     212             :     const char *pszDropTable =
     213         118 :         CSLFetchNameValueDef(papszOptions, "DROP_TABLE", "IF_EXISTS");
     214             :     const bool bSkipConflicts =
     215         118 :         CPLFetchBool(papszOptions, "SKIP_CONFLICTS", false);
     216         118 :     int nGeometryTypeFlags = 0;
     217             : 
     218         118 :     if (OGR_GT_HasZ(eType))
     219          23 :         nGeometryTypeFlags |= OGRGeometry::OGR_G_3D;
     220         118 :     if (OGR_GT_HasM(eType))
     221           2 :         nGeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
     222             : 
     223         118 :     int nForcedGeometryTypeFlags = -1;
     224         118 :     const char *pszDim = CSLFetchNameValue(papszOptions, "DIM");
     225         118 :     if (pszDim != nullptr)
     226             :     {
     227          19 :         if (EQUAL(pszDim, "XY") || EQUAL(pszDim, "2"))
     228             :         {
     229           0 :             nGeometryTypeFlags = 0;
     230           0 :             nForcedGeometryTypeFlags = nGeometryTypeFlags;
     231             :         }
     232          19 :         else if (EQUAL(pszDim, "XYZ") || EQUAL(pszDim, "3"))
     233             :         {
     234           7 :             nGeometryTypeFlags = OGRGeometry::OGR_G_3D;
     235           7 :             nForcedGeometryTypeFlags = nGeometryTypeFlags;
     236             :         }
     237          12 :         else if (EQUAL(pszDim, "XYM"))
     238             :         {
     239           6 :             nGeometryTypeFlags = OGRGeometry::OGR_G_MEASURED;
     240           6 :             nForcedGeometryTypeFlags = nGeometryTypeFlags;
     241             :         }
     242           6 :         else if (EQUAL(pszDim, "XYZM") || EQUAL(pszDim, "4"))
     243             :         {
     244           6 :             nGeometryTypeFlags =
     245             :                 OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
     246           6 :             nForcedGeometryTypeFlags = nGeometryTypeFlags;
     247             :         }
     248             :         else
     249             :         {
     250           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for DIM");
     251             :         }
     252             :     }
     253             : 
     254         118 :     const int nDimension =
     255         118 :         2 + ((nGeometryTypeFlags & OGRGeometry::OGR_G_3D) ? 1 : 0) +
     256         118 :         ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) ? 1 : 0);
     257             : 
     258             :     /* Should we turn layers with None geometry type as Unknown/GEOMETRY */
     259             :     /* so they are still recorded in geometry_columns table ? (#4012) */
     260         118 :     const bool bNoneAsUnknown = CPLTestBool(
     261             :         CSLFetchNameValueDef(papszOptions, "NONE_AS_UNKNOWN", "NO"));
     262             : 
     263         118 :     if (bNoneAsUnknown && eType == wkbNone)
     264           0 :         eType = wkbUnknown;
     265             : 
     266         118 :     const bool bExtractSchemaFromLayerName = CPLTestBool(CSLFetchNameValueDef(
     267             :         papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
     268             : 
     269             :     // Postgres Schema handling:
     270             : 
     271             :     // Extract schema name from input layer name or passed with -lco SCHEMA.
     272             :     // Set layer name to "schema.table" or to "table" if schema ==
     273             :     // current_schema() Usage without schema name is backwards compatible
     274             : 
     275         118 :     const char *pszDotPos = strstr(pszLayerName, ".");
     276         236 :     std::string osTable;
     277         236 :     std::string osSchema;
     278             :     const bool bUTF8ToASCII =
     279         118 :         CPLFetchBool(papszOptions, "LAUNDER_ASCII", false);
     280             :     const bool bLaunder =
     281         118 :         bUTF8ToASCII || CPLFetchBool(papszOptions, "LAUNDER", true);
     282             : 
     283         118 :     if (pszDotPos != nullptr && bExtractSchemaFromLayerName)
     284             :     {
     285          48 :         const size_t length = static_cast<size_t>(pszDotPos - pszLayerName);
     286          48 :         osSchema = pszLayerName;
     287          48 :         osSchema.resize(length);
     288             : 
     289          48 :         if (bLaunder)
     290             :         {
     291          48 :             char *pszTmp = OGRPGCommonLaunderName(pszDotPos + 1, "PGDump",
     292             :                                                   bUTF8ToASCII);  // skip "."
     293          48 :             osTable = pszTmp;
     294          48 :             CPLFree(pszTmp);
     295             :         }
     296             :         else
     297           0 :             osTable = OGRPGCommonGenerateShortEnoughIdentifier(pszDotPos +
     298          48 :                                                                1);  // skip "."
     299             :     }
     300             :     else
     301             :     {
     302          70 :         if (bLaunder)
     303             :         {
     304             :             char *pszTmp =
     305          67 :                 OGRPGCommonLaunderName(pszLayerName, "PGDump", bUTF8ToASCII);
     306          67 :             osTable = pszTmp;
     307          67 :             CPLFree(pszTmp);
     308             :         }
     309             :         else
     310           3 :             osTable = OGRPGCommonGenerateShortEnoughIdentifier(pszLayerName);
     311             :     }
     312             : 
     313             :     const std::string osTableEscaped =
     314         236 :         OGRPGDumpEscapeColumnName(osTable.c_str());
     315         118 :     const char *pszTableEscaped = osTableEscaped.c_str();
     316             : 
     317         118 :     LogCommit();
     318             : 
     319             :     /* -------------------------------------------------------------------- */
     320             :     /*      Set the default schema for the layers.                          */
     321             :     /* -------------------------------------------------------------------- */
     322         236 :     CPLString osCommand;
     323             : 
     324         118 :     const char *pszSchemaOption = CSLFetchNameValue(papszOptions, "SCHEMA");
     325         118 :     if (pszSchemaOption)
     326             :     {
     327           2 :         osSchema = pszSchemaOption;
     328           2 :         if (bCreateSchema)
     329             :         {
     330             :             osCommand.Printf(
     331             :                 "CREATE SCHEMA %s",
     332           2 :                 OGRPGDumpEscapeColumnName(osSchema.c_str()).c_str());
     333           2 :             Log(osCommand);
     334             :         }
     335             :     }
     336             : 
     337         118 :     const bool bTemporary = CPLFetchBool(papszOptions, "TEMPORARY", false);
     338         118 :     if (bTemporary)
     339             :     {
     340           1 :         osSchema = "pg_temp";
     341             :     }
     342             : 
     343         118 :     if (osSchema.empty())
     344             :     {
     345          67 :         osSchema = "public";
     346             :     }
     347             :     const std::string osSchemaEscaped =
     348         236 :         OGRPGDumpEscapeColumnName(osSchema.c_str());
     349         118 :     const char *pszSchemaEscaped = osSchemaEscaped.c_str();
     350             : 
     351             :     /* -------------------------------------------------------------------- */
     352             :     /*      Do we already have this layer?                                  */
     353             :     /* -------------------------------------------------------------------- */
     354         134 :     for (const auto &poLayer : m_apoLayers)
     355             :     {
     356          16 :         if (EQUAL(pszLayerName, poLayer->GetDescription()))
     357             :         {
     358           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     359             :                      "Layer %s already exists, CreateLayer failed.\n",
     360             :                      pszLayerName);
     361           0 :             return nullptr;
     362             :         }
     363             :     }
     364             : 
     365         118 :     if (bCreateTable &&
     366         117 :         (EQUAL(pszDropTable, "YES") || EQUAL(pszDropTable, "ON") ||
     367         117 :          EQUAL(pszDropTable, "TRUE") || EQUAL(pszDropTable, "IF_EXISTS")))
     368             :     {
     369         117 :         if (EQUAL(pszDropTable, "IF_EXISTS"))
     370             :             osCommand.Printf("DROP TABLE IF EXISTS %s.%s CASCADE",
     371         117 :                              pszSchemaEscaped, pszTableEscaped);
     372             :         else
     373             :             osCommand.Printf("DROP TABLE %s.%s CASCADE", pszSchemaEscaped,
     374           0 :                              pszTableEscaped);
     375         117 :         Log(osCommand);
     376             :     }
     377             : 
     378             :     /* -------------------------------------------------------------------- */
     379             :     /*      Handle the GEOM_TYPE option.                                    */
     380             :     /* -------------------------------------------------------------------- */
     381         118 :     const char *pszGeomType = CSLFetchNameValue(papszOptions, "GEOM_TYPE");
     382         118 :     if (pszGeomType == nullptr)
     383             :     {
     384         111 :         pszGeomType = "geometry";
     385             :     }
     386             : 
     387         118 :     if (!EQUAL(pszGeomType, "geometry") && !EQUAL(pszGeomType, "geography"))
     388             :     {
     389           0 :         CPLError(
     390             :             CE_Failure, CPLE_AppDefined,
     391             :             "GEOM_TYPE in PostGIS enabled databases must be 'geometry' or "
     392             :             "'geography'.  Creation of layer %s with GEOM_TYPE %s has failed.",
     393             :             pszLayerName, pszGeomType);
     394           0 :         return nullptr;
     395             :     }
     396             : 
     397             :     /* -------------------------------------------------------------------- */
     398             :     /*      Try to get the SRS Id of this spatial reference system,         */
     399             :     /*      adding tot the srs table if needed.                             */
     400             :     /* -------------------------------------------------------------------- */
     401             :     const char *pszPostgisVersion =
     402         118 :         CSLFetchNameValueDef(papszOptions, "POSTGIS_VERSION", "2.2");
     403         118 :     const int nPostGISMajor = atoi(pszPostgisVersion);
     404         118 :     const char *pszPostgisVersionDot = strchr(pszPostgisVersion, '.');
     405         118 :     const int nPostGISMinor =
     406         118 :         pszPostgisVersionDot ? atoi(pszPostgisVersionDot + 1) : 0;
     407         118 :     const int nUnknownSRSId = nPostGISMajor >= 2 ? 0 : -1;
     408             : 
     409         118 :     int nSRSId = nUnknownSRSId;
     410         118 :     int nForcedSRSId = -2;
     411         118 :     const char *pszSRID = CSLFetchNameValue(papszOptions, "SRID");
     412         118 :     if (pszSRID)
     413             :     {
     414           1 :         nSRSId = atoi(pszSRID);
     415           1 :         nForcedSRSId = nSRSId;
     416             :     }
     417             :     else
     418             :     {
     419         117 :         if (poSRS)
     420             :         {
     421           0 :             const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr);
     422           0 :             if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
     423             :             {
     424             :                 /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */
     425           0 :                 nSRSId = atoi(poSRS->GetAuthorityCode(nullptr));
     426             :             }
     427             :             else
     428             :             {
     429           0 :                 const char *pszGeogCSName = poSRS->GetAttrValue("GEOGCS");
     430           0 :                 if (pszGeogCSName != nullptr &&
     431           0 :                     EQUAL(pszGeogCSName, "GCS_WGS_1984"))
     432             :                 {
     433           0 :                     nSRSId = 4326;
     434             :                 }
     435             :             }
     436             :         }
     437             :     }
     438             : 
     439             :     const std::string osEscapedTableNameSingleQuote =
     440         236 :         OGRPGDumpEscapeString(osTable.c_str());
     441             :     const char *pszEscapedTableNameSingleQuote =
     442         118 :         osEscapedTableNameSingleQuote.c_str();
     443             : 
     444         118 :     const char *pszGeometryType = OGRToOGCGeomType(eType);
     445             : 
     446         118 :     const char *pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
     447         118 :     if (eType != wkbNone && !EQUAL(pszGeomType, "geography"))
     448             :     {
     449          81 :         if (pszGFldName == nullptr)
     450          73 :             pszGFldName = "wkb_geometry";
     451             : 
     452          81 :         if (nPostGISMajor < 2)
     453             :         {
     454             :             // Sometimes there is an old cruft entry in the geometry_columns
     455             :             // table if things were not properly cleaned up before.  We make
     456             :             // an effort to clean out such cruft.
     457             :             //
     458             :             // Note: PostGIS 2.0 defines geometry_columns as a view (no clean up
     459             :             // is needed).
     460             : 
     461             :             osCommand.Printf("DELETE FROM geometry_columns "
     462             :                              "WHERE f_table_name = %s AND f_table_schema = %s",
     463             :                              pszEscapedTableNameSingleQuote,
     464           1 :                              OGRPGDumpEscapeString(osSchema.c_str()).c_str());
     465           1 :             if (bCreateTable)
     466           1 :                 Log(osCommand);
     467             :         }
     468             :     }
     469             : 
     470         118 :     LogStartTransaction();
     471             : 
     472             :     /* -------------------------------------------------------------------- */
     473             :     /*      Create an empty table first.                                    */
     474             :     /* -------------------------------------------------------------------- */
     475         118 :     if (bCreateTable)
     476             :     {
     477         117 :         if (bTemporary)
     478             :         {
     479           1 :             osCommand.Printf("CREATE TEMPORARY TABLE %s()", pszTableEscaped);
     480             :         }
     481             :         else
     482             :         {
     483             :             osCommand.Printf("CREATE%s TABLE %s.%s()",
     484         116 :                              CPLFetchBool(papszOptions, "UNLOGGED", false)
     485             :                                  ? " UNLOGGED"
     486             :                                  : "",
     487         116 :                              pszSchemaEscaped, pszTableEscaped);
     488             :         }
     489         117 :         Log(osCommand);
     490             :     }
     491             : 
     492             :     /* -------------------------------------------------------------------- */
     493             :     /*      Add FID if needed.                                              */
     494             :     /* -------------------------------------------------------------------- */
     495         118 :     const char *pszFIDColumnNameIn = CSLFetchNameValue(papszOptions, "FID");
     496         236 :     CPLString osFIDColumnName;
     497         118 :     if (pszFIDColumnNameIn == nullptr)
     498         112 :         osFIDColumnName = "ogc_fid";
     499             :     else
     500             :     {
     501           6 :         if (bLaunder)
     502             :         {
     503           6 :             char *pszLaunderedFid = OGRPGCommonLaunderName(
     504             :                 pszFIDColumnNameIn, "PGDump", bUTF8ToASCII);
     505           6 :             osFIDColumnName = pszLaunderedFid;
     506           6 :             CPLFree(pszLaunderedFid);
     507             :         }
     508             :         else
     509             :         {
     510           0 :             osFIDColumnName = pszFIDColumnNameIn;
     511             :         }
     512             :     }
     513             :     const CPLString osFIDColumnNameEscaped =
     514         236 :         OGRPGDumpEscapeColumnName(osFIDColumnName);
     515             : 
     516         118 :     const bool bFID64 = CPLFetchBool(papszOptions, "FID64", false);
     517         118 :     const char *pszSerialType = bFID64 ? "BIGSERIAL" : "SERIAL";
     518             : 
     519         118 :     if (bCreateTable && !osFIDColumnName.empty())
     520             :     {
     521         230 :         std::string osConstraintName(osTable);
     522         115 :         if (CPLStrlenUTF8Ex(osTable.c_str()) + strlen("_pk") >
     523             :             static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
     524             :         {
     525           6 :             osConstraintName.clear();
     526           6 :             size_t iUTF8Char = 0;
     527         464 :             for (size_t i = 0; i < osTable.size(); ++i)
     528             :             {
     529         464 :                 if ((osTable[i] & 0xc0) != 0x80)
     530             :                 {
     531         366 :                     ++iUTF8Char;
     532         366 :                     if (iUTF8Char == OGR_PG_NAMEDATALEN - strlen("_pk"))
     533           6 :                         break;
     534             :                 }
     535         458 :                 osConstraintName += osTable[i];
     536             :             }
     537             :         }
     538         115 :         osConstraintName += "_pk";
     539             :         osCommand.Printf(
     540             :             "ALTER TABLE %s.%s ADD COLUMN %s %s "
     541             :             "CONSTRAINT %s PRIMARY KEY",
     542             :             pszSchemaEscaped, pszTableEscaped, osFIDColumnNameEscaped.c_str(),
     543             :             pszSerialType,
     544         115 :             OGRPGDumpEscapeColumnName(osConstraintName.c_str()).c_str());
     545         115 :         Log(osCommand);
     546             :     }
     547             : 
     548             :     /* -------------------------------------------------------------------- */
     549             :     /*      Create geometry/geography column (actual creation possibly      */
     550             :     /*      deferred).                                                      */
     551             :     /* -------------------------------------------------------------------- */
     552         236 :     std::vector<std::string> aosGeomCommands;
     553         118 :     if (bCreateTable && eType != wkbNone && EQUAL(pszGeomType, "geography"))
     554             :     {
     555           7 :         if (CSLFetchNameValue(papszOptions, "GEOMETRY_NAME") != nullptr)
     556           0 :             pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
     557             :         else
     558           7 :             pszGFldName = "the_geog";
     559             : 
     560           7 :         const char *suffix = "";
     561           7 :         if ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) &&
     562           4 :             (nGeometryTypeFlags & OGRGeometry::OGR_G_3D))
     563             :         {
     564           2 :             suffix = "ZM";
     565             :         }
     566           5 :         else if ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
     567             :         {
     568           2 :             suffix = "M";
     569             :         }
     570           3 :         else if ((nGeometryTypeFlags & OGRGeometry::OGR_G_3D))
     571             :         {
     572           2 :             suffix = "Z";
     573             :         }
     574             : 
     575           7 :         if (nSRSId)
     576             :             osCommand.Printf("ALTER TABLE %s.%s "
     577             :                              "ADD COLUMN %s geography(%s%s,%d)",
     578             :                              pszSchemaEscaped, pszTableEscaped,
     579           0 :                              OGRPGDumpEscapeColumnName(pszGFldName).c_str(),
     580           0 :                              pszGeometryType, suffix, nSRSId);
     581             :         else
     582             :             osCommand.Printf("ALTER TABLE %s.%s "
     583             :                              "ADD COLUMN %s geography(%s%s)",
     584             :                              pszSchemaEscaped, pszTableEscaped,
     585          14 :                              OGRPGDumpEscapeColumnName(pszGFldName).c_str(),
     586           7 :                              pszGeometryType, suffix);
     587           7 :         aosGeomCommands.push_back(osCommand);
     588             :     }
     589         111 :     else if (bCreateTable && eType != wkbNone)
     590             :     {
     591          80 :         const char *suffix = "";
     592          80 :         if (nGeometryTypeFlags ==
     593          83 :                 static_cast<int>(OGRGeometry::OGR_G_MEASURED) &&
     594           3 :             wkbFlatten(eType) != wkbUnknown)
     595             :         {
     596           2 :             suffix = "M";
     597             :         }
     598             : 
     599             :         osCommand.Printf(
     600             :             "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
     601         160 :             OGRPGDumpEscapeString(bTemporary ? "" : osSchema.c_str()).c_str(),
     602             :             pszEscapedTableNameSingleQuote,
     603         160 :             OGRPGDumpEscapeString(pszGFldName).c_str(), nSRSId, pszGeometryType,
     604         160 :             suffix, nDimension);
     605          80 :         aosGeomCommands.push_back(osCommand);
     606             :     }
     607             : 
     608             :     const char *pszSI =
     609         118 :         CSLFetchNameValueDef(papszOptions, "SPATIAL_INDEX", "GIST");
     610         118 :     const bool bCreateSpatialIndex =
     611           0 :         (EQUAL(pszSI, "GIST") || EQUAL(pszSI, "SPGIST") ||
     612         118 :          EQUAL(pszSI, "BRIN") || EQUAL(pszSI, "YES") || EQUAL(pszSI, "ON") ||
     613           0 :          EQUAL(pszSI, "TRUE"));
     614         118 :     if (!bCreateSpatialIndex && !EQUAL(pszSI, "NO") && !EQUAL(pszSI, "OFF") &&
     615           0 :         !EQUAL(pszSI, "FALSE") && !EQUAL(pszSI, "NONE"))
     616             :     {
     617           0 :         CPLError(CE_Warning, CPLE_NotSupported,
     618             :                  "SPATIAL_INDEX=%s not supported", pszSI);
     619             :     }
     620         236 :     const char *pszSpatialIndexType = EQUAL(pszSI, "SPGIST") ? "SPGIST"
     621         118 :                                       : EQUAL(pszSI, "BRIN") ? "BRIN"
     622             :                                                              : "GIST";
     623             : 
     624         236 :     std::vector<std::string> aosSpatialIndexCreationCommands;
     625         118 :     if (bCreateTable && bCreateSpatialIndex && pszGFldName && eType != wkbNone)
     626             :     {
     627             :         const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
     628         174 :             osTable.c_str(), pszGFldName, 0));
     629             : 
     630             :         /* --------------------------------------------------------------- */
     631             :         /*      Create the spatial index.                                  */
     632             :         /* --------------------------------------------------------------- */
     633             :         osCommand.Printf("CREATE INDEX %s "
     634             :                          "ON %s.%s "
     635             :                          "USING %s (%s)",
     636         174 :                          OGRPGDumpEscapeColumnName(osIndexName.c_str()).c_str(),
     637             :                          pszSchemaEscaped, pszTableEscaped, pszSpatialIndexType,
     638         261 :                          OGRPGDumpEscapeColumnName(pszGFldName).c_str());
     639          87 :         aosSpatialIndexCreationCommands.push_back(std::move(osCommand));
     640             :     }
     641             : 
     642             :     /* -------------------------------------------------------------------- */
     643             :     /*      Create the layer object.                                        */
     644             :     /* -------------------------------------------------------------------- */
     645             :     const bool bWriteAsHex =
     646         118 :         !CPLFetchBool(papszOptions, "WRITE_EWKT_GEOM", false);
     647             : 
     648             :     auto poLayer = std::make_unique<OGRPGDumpLayer>(
     649         118 :         this, osSchema.c_str(), osTable.c_str(),
     650         118 :         !osFIDColumnName.empty() ? osFIDColumnName.c_str() : nullptr,
     651         236 :         bWriteAsHex, bCreateTable, bSkipConflicts);
     652         118 :     poLayer->SetLaunderFlag(bLaunder);
     653         118 :     poLayer->SetUTF8ToASCIIFlag(bUTF8ToASCII);
     654         118 :     poLayer->SetPrecisionFlag(CPLFetchBool(papszOptions, "PRECISION", true));
     655             : 
     656             :     const char *pszOverrideColumnTypes =
     657         118 :         CSLFetchNameValue(papszOptions, "COLUMN_TYPES");
     658         118 :     poLayer->SetOverrideColumnTypes(pszOverrideColumnTypes);
     659         118 :     poLayer->SetUnknownSRSId(nUnknownSRSId);
     660         118 :     poLayer->SetForcedSRSId(nForcedSRSId);
     661         118 :     poLayer->SetCreateSpatialIndex(bCreateSpatialIndex, pszSpatialIndexType);
     662         118 :     poLayer->SetPostGISVersion(nPostGISMajor, nPostGISMinor);
     663         118 :     poLayer->SetForcedGeometryTypeFlags(nForcedGeometryTypeFlags);
     664             : 
     665             :     // Log geometry field creation immediately or defer it, according to
     666             :     // GEOM_COLUMN_POSITION
     667         118 :     const bool bGeomColumnPositionImmediate = EQUAL(
     668             :         CSLFetchNameValueDef(papszOptions, "GEOM_COLUMN_POSITION", "IMMEDIATE"),
     669             :         "IMMEDIATE");
     670         118 :     poLayer->SetGeomColumnPositionImmediate(bGeomColumnPositionImmediate);
     671         118 :     if (bGeomColumnPositionImmediate)
     672             :     {
     673         194 :         for (const auto &osSQL : aosGeomCommands)
     674          82 :             Log(osSQL.c_str());
     675             :     }
     676             :     else
     677             :     {
     678           6 :         poLayer->SetDeferredGeomFieldCreationCommands(aosGeomCommands);
     679             :     }
     680         118 :     poLayer->SetSpatialIndexCreationCommands(aosSpatialIndexCreationCommands);
     681             : 
     682         118 :     const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
     683         118 :     if (pszDescription != nullptr)
     684           1 :         poLayer->SetForcedDescription(pszDescription);
     685             : 
     686         118 :     if (eType != wkbNone)
     687             :     {
     688         176 :         OGRGeomFieldDefn oTmp(pszGFldName, eType);
     689          88 :         auto poGeomField = std::make_unique<OGRPGDumpGeomFieldDefn>(&oTmp);
     690          88 :         poGeomField->m_nSRSId = nSRSId;
     691          88 :         poGeomField->m_nGeometryTypeFlags = nGeometryTypeFlags;
     692          88 :         poLayer->GetLayerDefn()->AddGeomFieldDefn(std::move(poGeomField));
     693             :     }
     694          30 :     else if (pszGFldName)
     695           6 :         poLayer->SetGeometryFieldName(pszGFldName);
     696             : 
     697             :     /* -------------------------------------------------------------------- */
     698             :     /*      Add layer to data source layer list.                            */
     699             :     /* -------------------------------------------------------------------- */
     700         118 :     m_apoLayers.emplace_back(std::move(poLayer));
     701             : 
     702         118 :     return m_apoLayers.back().get();
     703             : }
     704             : 
     705             : /************************************************************************/
     706             : /*                           TestCapability()                           */
     707             : /************************************************************************/
     708             : 
     709          75 : int OGRPGDumpDataSource::TestCapability(const char *pszCap)
     710             : 
     711             : {
     712          75 :     if (EQUAL(pszCap, ODsCCreateLayer))
     713          39 :         return TRUE;
     714          36 :     else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
     715          13 :         return TRUE;
     716          23 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
     717           0 :         return TRUE;
     718          23 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     719           0 :         return TRUE;
     720          23 :     else if (EQUAL(pszCap, ODsCZGeometries))
     721           0 :         return TRUE;
     722          23 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     723           0 :         return TRUE;
     724             :     else
     725          23 :         return FALSE;
     726             : }
     727             : 
     728             : /************************************************************************/
     729             : /*                              GetLayer()                              */
     730             : /************************************************************************/
     731             : 
     732           7 : OGRLayer *OGRPGDumpDataSource::GetLayer(int iLayer)
     733             : 
     734             : {
     735           7 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
     736           0 :         return nullptr;
     737             :     else
     738           7 :         return m_apoLayers[iLayer].get();
     739             : }
     740             : 
     741             : /************************************************************************/
     742             : /*                                  Log()                               */
     743             : /************************************************************************/
     744             : 
     745        1274 : bool OGRPGDumpDataSource::Log(const char *pszStr, bool bAddSemiColumn)
     746             : {
     747        1274 :     if (m_fp == nullptr)
     748             :     {
     749           1 :         return false;
     750             :     }
     751             : 
     752        1273 :     VSIFWriteL(pszStr, strlen(pszStr), 1, m_fp);
     753        1273 :     if (bAddSemiColumn)
     754             :     {
     755        1232 :         const char chSemiColumn = ';';
     756        1232 :         VSIFWriteL(&chSemiColumn, 1, 1, m_fp);
     757             :     }
     758        1273 :     VSIFWriteL(m_pszEOL, strlen(m_pszEOL), 1, m_fp);
     759        1273 :     return true;
     760             : }
     761             : 
     762             : /************************************************************************/
     763             : /*                             StartCopy()                              */
     764             : /************************************************************************/
     765          10 : void OGRPGDumpDataSource::StartCopy(OGRPGDumpLayer *poPGLayer)
     766             : {
     767          10 :     EndCopy();
     768          10 :     m_poLayerInCopyMode = poPGLayer;
     769          10 : }
     770             : 
     771             : /************************************************************************/
     772             : /*                              EndCopy()                               */
     773             : /************************************************************************/
     774         333 : OGRErr OGRPGDumpDataSource::EndCopy()
     775             : {
     776         333 :     if (m_poLayerInCopyMode != nullptr)
     777             :     {
     778          10 :         OGRErr result = m_poLayerInCopyMode->EndCopy();
     779          10 :         m_poLayerInCopyMode = nullptr;
     780             : 
     781          10 :         return result;
     782             :     }
     783             : 
     784         323 :     return OGRERR_NONE;
     785             : }

Generated by: LCOV version 1.14