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

Generated by: LCOV version 1.14