LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/carto - ogrcartotablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 833 992 84.0 %
Date: 2024-05-03 15:49:35 Functions: 31 33 93.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Carto Translator
       4             :  * Purpose:  Implements OGRCARTOTableLayer class.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "ogr_carto.h"
      30             : #include "ogr_p.h"
      31             : #include "ogr_pgdump.h"
      32             : #include "ogrgeojsonreader.h"
      33             : 
      34             : /************************************************************************/
      35             : /*                    OGRCARTOEscapeIdentifier( )                     */
      36             : /************************************************************************/
      37             : 
      38         246 : CPLString OGRCARTOEscapeIdentifier(const char *pszStr)
      39             : {
      40         246 :     CPLString osStr;
      41             : 
      42         246 :     osStr += "\"";
      43             : 
      44         246 :     char ch = '\0';
      45        2352 :     for (int i = 0; (ch = pszStr[i]) != '\0'; i++)
      46             :     {
      47        2106 :         if (ch == '"')
      48           0 :             osStr.append(1, ch);
      49        2106 :         osStr.append(1, ch);
      50             :     }
      51             : 
      52         246 :     osStr += "\"";
      53             : 
      54         246 :     return osStr;
      55             : }
      56             : 
      57             : /************************************************************************/
      58             : /*                    OGRCARTOEscapeLiteralCopy( )                      */
      59             : /************************************************************************/
      60             : 
      61           1 : CPLString OGRCARTOEscapeLiteralCopy(const char *pszStr)
      62             : {
      63           1 :     CPLString osStr;
      64             : 
      65             :     /* convert special characters in COPY text format */
      66             :     /* into their escaped forms, and double up the escape */
      67             :     /* character */
      68           1 :     char ch = '\0';
      69           9 :     for (int i = 0; (ch = pszStr[i]) != '\0'; i++)
      70             :     {
      71           8 :         if (ch == '\t')  // tab
      72           0 :             osStr += "\\t";
      73           8 :         else if (ch == '\n')  // new line
      74           0 :             osStr += "\\n";
      75           8 :         else if (ch == '\r')  // carriage return
      76           0 :             osStr += "\\r";
      77           8 :         else if (ch == '\\')  // escape character
      78           0 :             osStr += "\\\\";
      79             :         else
      80           8 :             osStr.append(1, ch);
      81             :     }
      82             : 
      83           1 :     return osStr;
      84             : }
      85             : 
      86             : /************************************************************************/
      87             : /*                    OGRCARTOEscapeLiteral( )                        */
      88             : /************************************************************************/
      89             : 
      90          52 : CPLString OGRCARTOEscapeLiteral(const char *pszStr)
      91             : {
      92          52 :     CPLString osStr;
      93             : 
      94          52 :     char ch = '\0';
      95         425 :     for (int i = 0; (ch = pszStr[i]) != '\0'; i++)
      96             :     {
      97         373 :         if (ch == '\'')
      98           0 :             osStr.append(1, ch);
      99         373 :         osStr.append(1, ch);
     100             :     }
     101             : 
     102          52 :     return osStr;
     103             : }
     104             : 
     105             : /************************************************************************/
     106             : /*                    OGRCARTOEscapeLiteral( )                          */
     107             : /************************************************************************/
     108             : 
     109           4 : char *OGRCARTOTableLayer::OGRCARTOGetHexGeometry(OGRGeometry *poGeom, int i)
     110             : {
     111             :     OGRCartoGeomFieldDefn *poGeomFieldDefn =
     112           4 :         (OGRCartoGeomFieldDefn *)(poFeatureDefn->GetGeomFieldDefn(i));
     113           4 :     int nSRID = poGeomFieldDefn->nSRID;
     114           4 :     if (nSRID == 0)
     115           1 :         nSRID = 4326;
     116             :     char *pszEWKB;
     117           5 :     if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon &&
     118           1 :         wkbFlatten(GetGeomType()) == wkbMultiPolygon)
     119             :     {
     120           1 :         OGRMultiPolygon *poNewGeom = new OGRMultiPolygon();
     121           1 :         poNewGeom->addGeometry(poGeom);
     122           1 :         pszEWKB = OGRGeometryToHexEWKB(
     123           1 :             poNewGeom, nSRID, poDS->GetPostGISMajor(), poDS->GetPostGISMinor());
     124           1 :         delete poNewGeom;
     125             :     }
     126             :     else
     127           3 :         pszEWKB = OGRGeometryToHexEWKB(poGeom, nSRID, poDS->GetPostGISMajor(),
     128           3 :                                        poDS->GetPostGISMinor());
     129           4 :     return pszEWKB;
     130             : }
     131             : 
     132             : /************************************************************************/
     133             : /*                        OGRCARTOTableLayer()                          */
     134             : /************************************************************************/
     135             : 
     136          22 : OGRCARTOTableLayer::OGRCARTOTableLayer(OGRCARTODataSource *poDSIn,
     137          22 :                                        const char *pszName)
     138          22 :     : OGRCARTOLayer(poDSIn), osName(pszName)
     139             : {
     140          22 :     SetDescription(osName);
     141          22 :     bLaunderColumnNames = true;
     142          22 :     bInDeferredInsert = poDS->DoBatchInsert();
     143          22 :     bCopyMode = poDS->DoCopyMode();
     144          22 :     eDeferredInsertState = INSERT_UNINIT;
     145          22 :     m_nNextFIDWrite = -1;
     146          22 :     bDeferredCreation = false;
     147          22 :     bCartodbfy = false;
     148          22 :     nMaxChunkSize = atoi(CPLGetConfigOption(
     149             :                         "CARTO_MAX_CHUNK_SIZE",
     150             :                         CPLGetConfigOption("CARTODB_MAX_CHUNK_SIZE", "15"))) *
     151          22 :                     1024 * 1024;
     152          22 :     bDropOnCreation = false;
     153          22 : }
     154             : 
     155             : /************************************************************************/
     156             : /*                    ~OGRCARTOTableLayer()                           */
     157             : /************************************************************************/
     158             : 
     159          44 : OGRCARTOTableLayer::~OGRCARTOTableLayer()
     160             : 
     161             : {
     162          22 :     if (bDeferredCreation)
     163           0 :         RunDeferredCreationIfNecessary();
     164          22 :     CPL_IGNORE_RET_VAL(FlushDeferredBuffer());
     165          22 :     RunDeferredCartofy();
     166          44 : }
     167             : 
     168             : /************************************************************************/
     169             : /*                          GetLayerDefnInternal()                      */
     170             : /************************************************************************/
     171             : 
     172             : OGRFeatureDefn *
     173         146 : OGRCARTOTableLayer::GetLayerDefnInternal(CPL_UNUSED json_object *poObjIn)
     174             : {
     175         146 :     if (poFeatureDefn != nullptr)
     176         131 :         return poFeatureDefn;
     177             : 
     178          15 :     CPLString osCommand;
     179          15 :     if (poDS->IsAuthenticatedConnection())
     180             :     {
     181             :         // Get everything !
     182             :         osCommand.Printf(
     183             :             "SELECT a.attname, t.typname, a.attlen, "
     184             :             "format_type(a.atttypid,a.atttypmod), "
     185             :             "a.attnum, "
     186             :             "a.attnotnull, "
     187             :             "i.indisprimary, "
     188             :             "pg_get_expr(def.adbin, c.oid) AS defaultexpr, "
     189             :             "postgis_typmod_dims(a.atttypmod) dim, "
     190             :             "postgis_typmod_srid(a.atttypmod) srid, "
     191             :             "postgis_typmod_type(a.atttypmod)::text geomtyp, "
     192             :             "srtext "
     193             :             "FROM pg_class c "
     194             :             "JOIN pg_attribute a ON a.attnum > 0 AND "
     195             :             "a.attrelid = c.oid AND c.relname = '%s' "
     196             :             "JOIN pg_type t ON a.atttypid = t.oid "
     197             :             "JOIN pg_namespace n ON c.relnamespace=n.oid AND n.nspname= '%s' "
     198             :             "LEFT JOIN pg_index i ON c.oid = i.indrelid AND "
     199             :             "i.indisprimary = 't' AND a.attnum = ANY(i.indkey) "
     200             :             "LEFT JOIN pg_attrdef def ON def.adrelid = c.oid AND "
     201             :             "def.adnum = a.attnum "
     202             :             "LEFT JOIN spatial_ref_sys srs ON srs.srid = "
     203             :             "postgis_typmod_srid(a.atttypmod) "
     204             :             "ORDER BY a.attnum",
     205          24 :             OGRCARTOEscapeLiteral(osName).c_str(),
     206          36 :             OGRCARTOEscapeLiteral(poDS->GetCurrentSchema()).c_str());
     207             :     }
     208           3 :     else if (poDS->HasOGRMetadataFunction() != FALSE)
     209             :     {
     210             :         osCommand.Printf(
     211             :             "SELECT * FROM ogr_table_metadata('%s', '%s')",
     212           6 :             OGRCARTOEscapeLiteral(poDS->GetCurrentSchema()).c_str(),
     213           9 :             OGRCARTOEscapeLiteral(osName).c_str());
     214             :     }
     215             : 
     216          15 :     if (!osCommand.empty())
     217             :     {
     218          18 :         if (!poDS->IsAuthenticatedConnection() &&
     219           3 :             poDS->HasOGRMetadataFunction() < 0)
     220           3 :             CPLPushErrorHandler(CPLQuietErrorHandler);
     221          15 :         OGRLayer *poLyr = poDS->ExecuteSQLInternal(osCommand);
     222          18 :         if (!poDS->IsAuthenticatedConnection() &&
     223           3 :             poDS->HasOGRMetadataFunction() < 0)
     224             :         {
     225           3 :             CPLPopErrorHandler();
     226           3 :             if (poLyr == nullptr)
     227             :             {
     228           3 :                 CPLDebug("CARTO",
     229             :                          "ogr_table_metadata(text, text) not available");
     230           3 :                 CPLErrorReset();
     231             :             }
     232           0 :             else if (poLyr->GetLayerDefn()->GetFieldCount() != 12)
     233             :             {
     234           0 :                 CPLDebug("CARTO", "ogr_table_metadata(text, text) has "
     235             :                                   "unexpected column count");
     236           0 :                 poDS->ReleaseResultSet(poLyr);
     237           0 :                 poLyr = nullptr;
     238             :             }
     239           3 :             poDS->SetOGRMetadataFunction(poLyr != nullptr);
     240             :         }
     241          15 :         if (poLyr)
     242             :         {
     243             :             OGRFeature *poFeat;
     244         102 :             while ((poFeat = poLyr->GetNextFeature()) != nullptr)
     245             :             {
     246          91 :                 if (poFeatureDefn == nullptr)
     247             :                 {
     248             :                     // We could do that outside of the while() loop, but
     249             :                     // by doing that here, we are somewhat robust to
     250             :                     // ogr_table_metadata() returning suddenly an empty result
     251             :                     // set for example if CDB_UserTables() no longer works
     252          10 :                     poFeatureDefn = new OGRFeatureDefn(osName);
     253          10 :                     poFeatureDefn->Reference();
     254          10 :                     poFeatureDefn->SetGeomType(wkbNone);
     255             :                 }
     256             : 
     257          91 :                 const char *pszAttname = poFeat->GetFieldAsString("attname");
     258          91 :                 const char *pszType = poFeat->GetFieldAsString("typname");
     259          91 :                 int nWidth = poFeat->GetFieldAsInteger("attlen");
     260             :                 const char *pszFormatType =
     261          91 :                     poFeat->GetFieldAsString("format_type");
     262          91 :                 int bNotNull = poFeat->GetFieldAsInteger("attnotnull");
     263          91 :                 int bIsPrimary = poFeat->GetFieldAsInteger("indisprimary");
     264             :                 int iDefaultExpr =
     265          91 :                     poLyr->GetLayerDefn()->GetFieldIndex("defaultexpr");
     266             :                 const char *pszDefault =
     267          90 :                     (iDefaultExpr >= 0 &&
     268          90 :                      poFeat->IsFieldSetAndNotNull(iDefaultExpr))
     269         181 :                         ? poFeat->GetFieldAsString(iDefaultExpr)
     270          91 :                         : nullptr;
     271             : 
     272          91 :                 if (bIsPrimary &&
     273           9 :                     (EQUAL(pszType, "int2") || EQUAL(pszType, "int4") ||
     274           0 :                      EQUAL(pszType, "int8") || EQUAL(pszType, "serial") ||
     275           0 :                      EQUAL(pszType, "bigserial")))
     276             :                 {
     277           9 :                     osFIDColName = pszAttname;
     278             :                 }
     279          82 :                 else if (strcmp(pszAttname, "created_at") == 0 ||
     280          73 :                          strcmp(pszAttname, "updated_at") == 0 ||
     281          64 :                          strcmp(pszAttname, "the_geom_webmercator") == 0)
     282             :                 {
     283             :                     /* ignored */
     284             :                 }
     285             :                 else
     286             :                 {
     287          55 :                     if (EQUAL(pszType, "geometry"))
     288             :                     {
     289           9 :                         int nDim = poFeat->GetFieldAsInteger("dim");
     290           9 :                         int nSRID = poFeat->GetFieldAsInteger("srid");
     291             :                         const char *pszGeomType =
     292           9 :                             poFeat->GetFieldAsString("geomtyp");
     293             :                         const char *pszSRText =
     294           9 :                             (poFeat->IsFieldSetAndNotNull(
     295           9 :                                 poLyr->GetLayerDefn()->GetFieldIndex("srtext")))
     296           9 :                                 ? poFeat->GetFieldAsString("srtext")
     297           9 :                                 : nullptr;
     298             :                         OGRwkbGeometryType eType =
     299           9 :                             OGRFromOGCGeomType(pszGeomType);
     300           9 :                         if (nDim == 3)
     301           9 :                             eType = wkbSetZ(eType);
     302             :                         auto poFieldDefn =
     303             :                             std::make_unique<OGRCartoGeomFieldDefn>(pszAttname,
     304           9 :                                                                     eType);
     305           9 :                         if (bNotNull)
     306           0 :                             poFieldDefn->SetNullable(FALSE);
     307           9 :                         if (pszSRText != nullptr)
     308             :                         {
     309           9 :                             auto l_poSRS = new OGRSpatialReference();
     310           9 :                             l_poSRS->SetAxisMappingStrategy(
     311             :                                 OAMS_TRADITIONAL_GIS_ORDER);
     312             : 
     313           9 :                             if (l_poSRS->importFromWkt(pszSRText) !=
     314             :                                 OGRERR_NONE)
     315             :                             {
     316           0 :                                 delete l_poSRS;
     317           0 :                                 l_poSRS = nullptr;
     318             :                             }
     319           9 :                             if (l_poSRS != nullptr)
     320             :                             {
     321           9 :                                 poFieldDefn->SetSpatialRef(l_poSRS);
     322           9 :                                 l_poSRS->Release();
     323             :                             }
     324             :                         }
     325           9 :                         poFieldDefn->nSRID = nSRID;
     326           9 :                         poFeatureDefn->AddGeomFieldDefn(std::move(poFieldDefn));
     327             :                     }
     328             :                     else
     329             :                     {
     330          92 :                         OGRFieldDefn oField(pszAttname, OFTString);
     331          46 :                         if (bNotNull)
     332           3 :                             oField.SetNullable(FALSE);
     333          46 :                         OGRPGCommonLayerSetType(oField, pszType, pszFormatType,
     334             :                                                 nWidth);
     335          46 :                         if (pszDefault)
     336           3 :                             OGRPGCommonLayerNormalizeDefault(&oField,
     337             :                                                              pszDefault);
     338             : 
     339          46 :                         poFeatureDefn->AddFieldDefn(&oField);
     340             :                     }
     341             :                 }
     342          91 :                 delete poFeat;
     343             :             }
     344             : 
     345          11 :             poDS->ReleaseResultSet(poLyr);
     346             :         }
     347             :     }
     348             : 
     349          15 :     if (poFeatureDefn == nullptr)
     350             :     {
     351             :         osBaseSQL.Printf("SELECT * FROM %s",
     352           5 :                          OGRCARTOEscapeIdentifier(osName).c_str());
     353           5 :         EstablishLayerDefn(osName, nullptr);
     354           5 :         osBaseSQL = "";
     355             :     }
     356             : 
     357          15 :     if (!osFIDColName.empty())
     358             :     {
     359           9 :         osBaseSQL = "SELECT ";
     360           9 :         osBaseSQL += OGRCARTOEscapeIdentifier(osFIDColName);
     361             :     }
     362          24 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
     363             :     {
     364           9 :         if (osBaseSQL.empty())
     365           0 :             osBaseSQL = "SELECT ";
     366             :         else
     367           9 :             osBaseSQL += ", ";
     368          18 :         osBaseSQL += OGRCARTOEscapeIdentifier(
     369          18 :             poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef());
     370             :     }
     371          65 :     for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
     372             :     {
     373          50 :         if (osBaseSQL.empty())
     374           2 :             osBaseSQL = "SELECT ";
     375             :         else
     376          48 :             osBaseSQL += ", ";
     377         100 :         osBaseSQL += OGRCARTOEscapeIdentifier(
     378         100 :             poFeatureDefn->GetFieldDefn(i)->GetNameRef());
     379             :     }
     380          15 :     if (osBaseSQL.empty())
     381           4 :         osBaseSQL = "SELECT *";
     382          15 :     osBaseSQL += " FROM ";
     383          15 :     osBaseSQL += OGRCARTOEscapeIdentifier(osName);
     384             : 
     385          15 :     osSELECTWithoutWHERE = osBaseSQL;
     386             : 
     387          15 :     return poFeatureDefn;
     388             : }
     389             : 
     390             : /************************************************************************/
     391             : /*                        FetchNewFeatures()                            */
     392             : /************************************************************************/
     393             : 
     394          19 : json_object *OGRCARTOTableLayer::FetchNewFeatures()
     395             : {
     396          19 :     if (!osFIDColName.empty())
     397             :     {
     398          32 :         CPLString osSQL;
     399             :         osSQL.Printf(
     400             :             "%s WHERE %s%s >= " CPL_FRMT_GIB " ORDER BY %s ASC LIMIT %d",
     401             :             osSELECTWithoutWHERE.c_str(),
     402          21 :             (osWHERE.size()) ? CPLSPrintf("%s AND ", osWHERE.c_str()) : "",
     403          32 :             OGRCARTOEscapeIdentifier(osFIDColName).c_str(), m_nNextFID,
     404          32 :             OGRCARTOEscapeIdentifier(osFIDColName).c_str(),
     405          69 :             GetFeaturesToFetch());
     406          16 :         return poDS->RunSQL(osSQL);
     407             :     }
     408             :     else
     409           3 :         return OGRCARTOLayer::FetchNewFeatures();
     410             : }
     411             : 
     412             : /************************************************************************/
     413             : /*                           GetNextRawFeature()                        */
     414             : /************************************************************************/
     415             : 
     416          25 : OGRFeature *OGRCARTOTableLayer::GetNextRawFeature()
     417             : {
     418          25 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
     419           1 :         return nullptr;
     420          24 :     if (FlushDeferredBuffer() != OGRERR_NONE)
     421           0 :         return nullptr;
     422          24 :     return OGRCARTOLayer::GetNextRawFeature();
     423             : }
     424             : 
     425             : /************************************************************************/
     426             : /*                         SetAttributeFilter()                         */
     427             : /************************************************************************/
     428             : 
     429           7 : OGRErr OGRCARTOTableLayer::SetAttributeFilter(const char *pszQuery)
     430             : 
     431             : {
     432           7 :     GetLayerDefn();
     433             : 
     434           7 :     if (pszQuery == nullptr)
     435           6 :         osQuery = "";
     436             :     else
     437             :     {
     438           1 :         osQuery = "(";
     439           1 :         osQuery += pszQuery;
     440           1 :         osQuery += ")";
     441             :     }
     442             : 
     443           7 :     BuildWhere();
     444             : 
     445           7 :     ResetReading();
     446             : 
     447           7 :     return OGRERR_NONE;
     448             : }
     449             : 
     450             : /************************************************************************/
     451             : /*                          SetSpatialFilter()                          */
     452             : /************************************************************************/
     453             : 
     454           7 : void OGRCARTOTableLayer::SetSpatialFilter(int iGeomField, OGRGeometry *poGeomIn)
     455             : 
     456             : {
     457          12 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
     458           5 :         GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
     459             :     {
     460           2 :         if (iGeomField != 0)
     461             :         {
     462           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     463             :                      "Invalid geometry field index : %d", iGeomField);
     464             :         }
     465           2 :         return;
     466             :     }
     467           5 :     m_iGeomFieldFilter = iGeomField;
     468             : 
     469           5 :     if (InstallFilter(poGeomIn))
     470             :     {
     471           1 :         BuildWhere();
     472             : 
     473           1 :         ResetReading();
     474             :     }
     475             : }
     476             : 
     477             : /************************************************************************/
     478             : /*                         RunDeferredCartofy()                         */
     479             : /************************************************************************/
     480             : 
     481          23 : void OGRCARTOTableLayer::RunDeferredCartofy()
     482             : 
     483             : {
     484          23 :     if (bCartodbfy)
     485             :     {
     486           1 :         bCartodbfy = false;
     487             : 
     488           2 :         CPLString osSQL;
     489           1 :         if (poDS->GetCurrentSchema() == "public")
     490             :             osSQL.Printf("SELECT cdb_cartodbfytable('%s')",
     491           1 :                          OGRCARTOEscapeLiteral(osName).c_str());
     492             :         else
     493             :             osSQL.Printf(
     494             :                 "SELECT cdb_cartodbfytable('%s', '%s')",
     495           0 :                 OGRCARTOEscapeLiteral(poDS->GetCurrentSchema()).c_str(),
     496           0 :                 OGRCARTOEscapeLiteral(osName).c_str());
     497             : 
     498           1 :         json_object *poObj = poDS->RunSQL(osSQL);
     499           1 :         if (poObj != nullptr)
     500           1 :             json_object_put(poObj);
     501             :     }
     502          23 : }
     503             : 
     504             : /************************************************************************/
     505             : /*                        CARTOGeometryType()                           */
     506             : /************************************************************************/
     507             : 
     508           4 : static CPLString OGRCARTOGeometryType(OGRCartoGeomFieldDefn *poGeomField)
     509             : {
     510           4 :     OGRwkbGeometryType eType = poGeomField->GetType();
     511           4 :     const char *pszGeometryType = OGRToOGCGeomType(eType);
     512           4 :     const char *suffix = "";
     513             : 
     514           4 :     if (OGR_GT_HasM(eType) && OGR_GT_HasZ(eType))
     515             :     {
     516           0 :         suffix = "ZM";
     517             :     }
     518           4 :     else if (OGR_GT_HasM(eType))
     519             :     {
     520           0 :         suffix = "M";
     521             :     }
     522           4 :     else if (OGR_GT_HasZ(eType))
     523             :     {
     524           0 :         suffix = "Z";
     525             :     }
     526             : 
     527           4 :     CPLString osSQL;
     528             :     osSQL.Printf("Geometry(%s%s,%d)", pszGeometryType, suffix,
     529           4 :                  poGeomField->nSRID);
     530             : 
     531           4 :     return osSQL;
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                         FlushDeferredBuffer()                        */
     536             : /************************************************************************/
     537             : 
     538          78 : OGRErr OGRCARTOTableLayer::FlushDeferredBuffer(bool bReset)
     539             : {
     540          78 :     if (bCopyMode)
     541          61 :         return FlushDeferredCopy(bReset);
     542             :     else
     543          17 :         return FlushDeferredInsert(bReset);
     544             : }
     545             : 
     546             : /************************************************************************/
     547             : /*                         FlushDeferredInsert()                        */
     548             : /************************************************************************/
     549             : 
     550          17 : OGRErr OGRCARTOTableLayer::FlushDeferredInsert(bool bReset)
     551             : {
     552          17 :     OGRErr eErr = OGRERR_NONE;
     553          17 :     if (bInDeferredInsert && !osDeferredBuffer.empty())
     554             :     {
     555           6 :         osDeferredBuffer = "BEGIN;" + osDeferredBuffer;
     556           6 :         if (eDeferredInsertState == INSERT_MULTIPLE_FEATURE)
     557             :         {
     558           2 :             osDeferredBuffer += ";";
     559           2 :             eDeferredInsertState = INSERT_UNINIT;
     560             :         }
     561           6 :         osDeferredBuffer += "COMMIT;";
     562             : 
     563           6 :         json_object *poObj = poDS->RunSQL(osDeferredBuffer);
     564           6 :         if (poObj != nullptr)
     565             :         {
     566           5 :             json_object_put(poObj);
     567             :         }
     568             :         else
     569             :         {
     570           1 :             bInDeferredInsert = false;
     571           1 :             eErr = OGRERR_FAILURE;
     572             :         }
     573             :     }
     574             : 
     575          17 :     osDeferredBuffer = "";
     576          17 :     if (bReset)
     577             :     {
     578          16 :         bInDeferredInsert = false;
     579          16 :         m_nNextFIDWrite = -1;
     580             :     }
     581          17 :     return eErr;
     582             : }
     583             : 
     584             : /************************************************************************/
     585             : /*                         FlushDeferredCopy()                          */
     586             : /************************************************************************/
     587             : 
     588          61 : OGRErr OGRCARTOTableLayer::FlushDeferredCopy(bool bReset)
     589             : {
     590          61 :     OGRErr eErr = OGRERR_NONE;
     591          61 :     if (!osDeferredBuffer.empty())
     592             :     {
     593             :         /* And end-of-file marker to data buffer */
     594           2 :         osDeferredBuffer += "\\.\n";
     595             : 
     596           2 :         json_object *poObj = poDS->RunCopyFrom(osCopySQL, osDeferredBuffer);
     597           2 :         if (poObj != nullptr)
     598             :         {
     599           1 :             json_object_put(poObj);
     600             :         }
     601             :         else
     602             :         {
     603           1 :             bInDeferredInsert = false;
     604           1 :             eErr = OGRERR_FAILURE;
     605             :         }
     606             :     }
     607             : 
     608          61 :     osDeferredBuffer.clear();
     609          61 :     if (bReset)
     610             :     {
     611          60 :         bInDeferredInsert = false;
     612          60 :         m_nNextFIDWrite = -1;
     613             :     }
     614          61 :     return eErr;
     615             : }
     616             : 
     617             : /************************************************************************/
     618             : /*                          CreateGeomField()                           */
     619             : /************************************************************************/
     620             : 
     621             : OGRErr
     622           0 : OGRCARTOTableLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
     623             :                                     CPL_UNUSED int bApproxOK)
     624             : {
     625           0 :     if (!poDS->IsReadWrite())
     626             :     {
     627           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     628             :                  "Operation not available in read-only mode");
     629           0 :         return OGRERR_FAILURE;
     630             :     }
     631             : 
     632           0 :     OGRwkbGeometryType eType = poGeomFieldIn->GetType();
     633           0 :     if (eType == wkbNone)
     634             :     {
     635           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     636             :                  "Cannot create geometry field of type wkbNone");
     637           0 :         return OGRERR_FAILURE;
     638             :     }
     639             : 
     640           0 :     const char *pszNameIn = poGeomFieldIn->GetNameRef();
     641           0 :     if (pszNameIn == nullptr || EQUAL(pszNameIn, ""))
     642             :     {
     643           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     644             :                  "Cannot add un-named geometry field");
     645           0 :         return OGRERR_FAILURE;
     646             :     }
     647             : 
     648             :     /* Flush the write buffer before trying this. */
     649           0 :     if (eDeferredInsertState == INSERT_MULTIPLE_FEATURE)
     650             :     {
     651           0 :         if (FlushDeferredBuffer() != OGRERR_NONE)
     652           0 :             return OGRERR_FAILURE;
     653             :     }
     654             : 
     655             :     auto poGeomField =
     656           0 :         std::make_unique<OGRCartoGeomFieldDefn>(pszNameIn, eType);
     657           0 :     if (EQUAL(poGeomField->GetNameRef(), ""))
     658             :     {
     659           0 :         if (poFeatureDefn->GetGeomFieldCount() == 0)
     660           0 :             poGeomField->SetName("the_geom");
     661             :     }
     662           0 :     const auto poSRSIn = poGeomFieldIn->GetSpatialRef();
     663           0 :     if (poSRSIn)
     664             :     {
     665           0 :         auto l_poSRS = poSRSIn->Clone();
     666           0 :         l_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     667           0 :         poGeomField->SetSpatialRef(l_poSRS);
     668           0 :         l_poSRS->Release();
     669             :     }
     670             : 
     671           0 :     if (bLaunderColumnNames)
     672             :     {
     673             :         char *pszSafeName =
     674           0 :             OGRPGCommonLaunderName(poGeomField->GetNameRef(), "CARTO", false);
     675           0 :         poGeomField->SetName(pszSafeName);
     676           0 :         CPLFree(pszSafeName);
     677             :     }
     678             : 
     679           0 :     const OGRSpatialReference *poSRS = poGeomField->GetSpatialRef();
     680           0 :     int nSRID = 0;
     681           0 :     if (poSRS != nullptr)
     682           0 :         nSRID = poDS->FetchSRSId(poSRS);
     683             : 
     684           0 :     poGeomField->SetType(eType);
     685           0 :     poGeomField->SetNullable(poGeomFieldIn->IsNullable());
     686           0 :     poGeomField->nSRID = nSRID;
     687             : 
     688             :     /* -------------------------------------------------------------------- */
     689             :     /*      Create the new field.                                           */
     690             :     /* -------------------------------------------------------------------- */
     691             : 
     692           0 :     if (!bDeferredCreation)
     693             :     {
     694           0 :         CPLString osSQL;
     695             :         osSQL.Printf(
     696             :             "ALTER TABLE %s ADD COLUMN %s %s",
     697           0 :             OGRCARTOEscapeIdentifier(osName).c_str(),
     698           0 :             OGRCARTOEscapeIdentifier(poGeomField->GetNameRef()).c_str(),
     699           0 :             OGRCARTOGeometryType(poGeomField.get()).c_str());
     700           0 :         if (!poGeomField->IsNullable())
     701           0 :             osSQL += " NOT NULL";
     702             : 
     703           0 :         json_object *poObj = poDS->RunSQL(osSQL);
     704           0 :         if (poObj == nullptr)
     705           0 :             return OGRERR_FAILURE;
     706           0 :         json_object_put(poObj);
     707             :     }
     708             : 
     709           0 :     poFeatureDefn->AddGeomFieldDefn(std::move(poGeomField));
     710           0 :     return OGRERR_NONE;
     711             : }
     712             : 
     713             : /************************************************************************/
     714             : /*                            CreateField()                             */
     715             : /************************************************************************/
     716             : 
     717           6 : OGRErr OGRCARTOTableLayer::CreateField(const OGRFieldDefn *poFieldIn,
     718             :                                        CPL_UNUSED int bApproxOK)
     719             : {
     720           6 :     GetLayerDefn();
     721             : 
     722           6 :     if (!poDS->IsReadWrite())
     723             :     {
     724           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     725             :                  "Operation not available in read-only mode");
     726           1 :         return OGRERR_FAILURE;
     727             :     }
     728             : 
     729           5 :     if (eDeferredInsertState == INSERT_MULTIPLE_FEATURE)
     730             :     {
     731           0 :         if (FlushDeferredBuffer() != OGRERR_NONE)
     732           0 :             return OGRERR_FAILURE;
     733             :     }
     734             : 
     735          10 :     OGRFieldDefn oField(poFieldIn);
     736           5 :     if (bLaunderColumnNames)
     737             :     {
     738             :         char *pszName =
     739           5 :             OGRPGCommonLaunderName(oField.GetNameRef(), "CARTO", false);
     740           5 :         oField.SetName(pszName);
     741           5 :         CPLFree(pszName);
     742             :     }
     743             : 
     744             :     /* -------------------------------------------------------------------- */
     745             :     /*      Create the new field.                                           */
     746             :     /* -------------------------------------------------------------------- */
     747             : 
     748           5 :     if (!bDeferredCreation)
     749             :     {
     750           4 :         CPLString osSQL;
     751             :         osSQL.Printf("ALTER TABLE %s ADD COLUMN %s %s",
     752           8 :                      OGRCARTOEscapeIdentifier(osName).c_str(),
     753           8 :                      OGRCARTOEscapeIdentifier(oField.GetNameRef()).c_str(),
     754          16 :                      OGRPGCommonLayerGetType(oField, false, true).c_str());
     755           4 :         if (!oField.IsNullable())
     756           0 :             osSQL += " NOT NULL";
     757           4 :         if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
     758             :         {
     759           0 :             osSQL += " DEFAULT ";
     760           0 :             osSQL += OGRPGCommonLayerGetPGDefault(&oField);
     761             :         }
     762             : 
     763           4 :         json_object *poObj = poDS->RunSQL(osSQL);
     764           4 :         if (poObj == nullptr)
     765           1 :             return OGRERR_FAILURE;
     766           3 :         json_object_put(poObj);
     767             :     }
     768             : 
     769           4 :     poFeatureDefn->AddFieldDefn(&oField);
     770             : 
     771           4 :     return OGRERR_NONE;
     772             : }
     773             : 
     774             : /************************************************************************/
     775             : /*                            DeleteField()                             */
     776             : /************************************************************************/
     777             : 
     778           4 : OGRErr OGRCARTOTableLayer::DeleteField(int iField)
     779             : {
     780           8 :     CPLString osSQL;
     781             : 
     782           4 :     if (!poDS->IsReadWrite())
     783             :     {
     784           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     785             :                  "Operation not available in read-only mode");
     786           1 :         return OGRERR_FAILURE;
     787             :     }
     788             : 
     789           3 :     if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
     790             :     {
     791           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
     792           1 :         return OGRERR_FAILURE;
     793             :     }
     794             : 
     795           2 :     if (eDeferredInsertState == INSERT_MULTIPLE_FEATURE)
     796             :     {
     797           0 :         if (FlushDeferredBuffer() != OGRERR_NONE)
     798           0 :             return OGRERR_FAILURE;
     799             :     }
     800             : 
     801             :     /* -------------------------------------------------------------------- */
     802             :     /*      Drop the field.                                                 */
     803             :     /* -------------------------------------------------------------------- */
     804             : 
     805             :     osSQL.Printf("ALTER TABLE %s DROP COLUMN %s",
     806           4 :                  OGRCARTOEscapeIdentifier(osName).c_str(),
     807           4 :                  OGRCARTOEscapeIdentifier(
     808           2 :                      poFeatureDefn->GetFieldDefn(iField)->GetNameRef())
     809           4 :                      .c_str());
     810             : 
     811           2 :     json_object *poObj = poDS->RunSQL(osSQL);
     812           2 :     if (poObj == nullptr)
     813           1 :         return OGRERR_FAILURE;
     814           1 :     json_object_put(poObj);
     815             : 
     816           1 :     return poFeatureDefn->DeleteFieldDefn(iField);
     817             : }
     818             : 
     819             : /************************************************************************/
     820             : /*                           ICreateFeature()                            */
     821             : /************************************************************************/
     822             : 
     823          16 : OGRErr OGRCARTOTableLayer::ICreateFeature(OGRFeature *poFeature)
     824             : 
     825             : {
     826          16 :     if (bDeferredCreation)
     827             :     {
     828           3 :         if (RunDeferredCreationIfNecessary() != OGRERR_NONE)
     829           1 :             return OGRERR_FAILURE;
     830             :     }
     831             : 
     832          15 :     GetLayerDefn();
     833          15 :     bool bHasUserFieldMatchingFID = false;
     834          15 :     if (!osFIDColName.empty())
     835          15 :         bHasUserFieldMatchingFID =
     836          15 :             poFeatureDefn->GetFieldIndex(osFIDColName) >= 0;
     837             : 
     838          15 :     if (!poDS->IsReadWrite())
     839             :     {
     840           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     841             :                  "Operation not available in read-only mode");
     842           1 :         return OGRERR_FAILURE;
     843             :     }
     844             : 
     845          28 :     CPLString osSQL;
     846             : 
     847          14 :     bool bHasJustGotNextFID = false;
     848          18 :     if (!bHasUserFieldMatchingFID && bInDeferredInsert && m_nNextFIDWrite < 0 &&
     849           4 :         !osFIDColName.empty())
     850             :     {
     851           8 :         CPLString osSeqName;
     852             :         osSQL.Printf(
     853             :             "SELECT pg_catalog.pg_get_serial_sequence('%s', '%s') AS seq_name",
     854           8 :             OGRCARTOEscapeLiteral(osName).c_str(),
     855          12 :             OGRCARTOEscapeLiteral(osFIDColName).c_str());
     856           4 :         json_object *poObj = poDS->RunSQL(osSQL);
     857           4 :         json_object *poRowObj = OGRCARTOGetSingleRow(poObj);
     858           4 :         if (poRowObj != nullptr)
     859             :         {
     860             :             json_object *poSeqName =
     861           4 :                 CPL_json_object_object_get(poRowObj, "seq_name");
     862           8 :             if (poSeqName != nullptr &&
     863           4 :                 json_object_get_type(poSeqName) == json_type_string)
     864             :             {
     865           4 :                 osSeqName = json_object_get_string(poSeqName);
     866             :             }
     867             :         }
     868             : 
     869           4 :         if (poObj != nullptr)
     870           4 :             json_object_put(poObj);
     871             : 
     872           4 :         if (!osSeqName.empty())
     873             :         {
     874             :             osSQL.Printf("SELECT nextval('%s') AS nextid",
     875           4 :                          OGRCARTOEscapeLiteral(osSeqName).c_str());
     876             : 
     877           4 :             poObj = poDS->RunSQL(osSQL);
     878           4 :             poRowObj = OGRCARTOGetSingleRow(poObj);
     879           4 :             if (poRowObj != nullptr)
     880             :             {
     881             :                 json_object *poID =
     882           4 :                     CPL_json_object_object_get(poRowObj, "nextid");
     883           8 :                 if (poID != nullptr &&
     884           4 :                     json_object_get_type(poID) == json_type_int)
     885             :                 {
     886           4 :                     m_nNextFIDWrite = json_object_get_int64(poID);
     887           4 :                     bHasJustGotNextFID = true;
     888             :                 }
     889             :             }
     890             : 
     891           4 :             if (poObj != nullptr)
     892           4 :                 json_object_put(poObj);
     893             :         }
     894             :     }
     895             : 
     896          14 :     if (bCopyMode)
     897           2 :         return ICreateFeatureCopy(poFeature, bHasUserFieldMatchingFID,
     898           2 :                                   bHasJustGotNextFID);
     899             :     else
     900          12 :         return ICreateFeatureInsert(poFeature, bHasUserFieldMatchingFID,
     901          12 :                                     bHasJustGotNextFID);
     902             : }
     903             : 
     904             : /************************************************************************/
     905             : /*                           ICreateFeatureCopy()                       */
     906             : /************************************************************************/
     907             : 
     908           2 : OGRErr OGRCARTOTableLayer::ICreateFeatureCopy(OGRFeature *poFeature,
     909             :                                               bool bHasUserFieldMatchingFID,
     910             :                                               bool bHasJustGotNextFID)
     911             : {
     912           4 :     CPLString osCopyFile;
     913           2 :     GetLayerDefn();
     914             : 
     915           2 :     if (eDeferredInsertState == INSERT_MULTIPLE_FEATURE)
     916             :     {
     917           1 :         bool bReset = false;
     918           1 :         if (m_abFieldSetForInsert.size() !=
     919           1 :             static_cast<size_t>(poFeatureDefn->GetFieldCount()))
     920             :         {
     921           0 :             bReset = true;
     922             :         }
     923             :         else
     924             :         {
     925           1 :             for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
     926             :             {
     927           2 :                 if (m_abFieldSetForInsert[i] !=
     928           1 :                     CPL_TO_BOOL(poFeature->IsFieldSet(i)))
     929             :                 {
     930           1 :                     bReset = true;
     931           1 :                     break;
     932             :                 }
     933             :             }
     934             :         }
     935           1 :         if (bReset)
     936             :         {
     937           1 :             if (FlushDeferredBuffer(false) != OGRERR_NONE)
     938             :             {
     939           0 :                 return OGRERR_FAILURE;
     940             :             }
     941           1 :             eDeferredInsertState = INSERT_UNINIT;
     942             :         }
     943             :     }
     944             : 
     945             :     /* We are doing a new COPY for each full buffer, so we will */
     946             :     /* construct a new COPY statement here, even though we could */
     947             :     /* reuse the same one over and over if we cached it (hmm) */
     948           2 :     if (eDeferredInsertState == INSERT_UNINIT)
     949             :     {
     950           2 :         osCopySQL.clear();
     951           2 :         osCopySQL.Printf("COPY %s ", OGRCARTOEscapeIdentifier(osName).c_str());
     952           2 :         bool bMustComma = false;
     953             :         /* Non-spatial column names */
     954           2 :         m_abFieldSetForInsert.resize(poFeatureDefn->GetFieldCount());
     955          12 :         for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
     956             :         {
     957             :             /* We base the columns we attempt to COPY based on */
     958             :             /* the set pattern of the first feature we see. */
     959          10 :             m_abFieldSetForInsert[i] = CPL_TO_BOOL(poFeature->IsFieldSet(i));
     960          10 :             if (!poFeature->IsFieldSet(i))
     961           8 :                 continue;
     962             : 
     963           2 :             if (bMustComma)
     964           0 :                 osCopySQL += ",";
     965             :             else
     966             :             {
     967           2 :                 osCopySQL += "(";
     968           2 :                 bMustComma = true;
     969             :             }
     970             : 
     971           4 :             osCopySQL += OGRCARTOEscapeIdentifier(
     972           4 :                 poFeatureDefn->GetFieldDefn(i)->GetNameRef());
     973             :         }
     974             :         /* Geometry column names */
     975           4 :         for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
     976             :         {
     977           2 :             if (bMustComma)
     978           2 :                 osCopySQL += ",";
     979             :             else
     980           0 :                 bMustComma = true;
     981             : 
     982           4 :             osCopySQL += OGRCARTOEscapeIdentifier(
     983           4 :                 poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef());
     984             :         }
     985             :         /* FID column */
     986           6 :         if (!bHasUserFieldMatchingFID && !osFIDColName.empty() &&
     987           2 :             (poFeature->GetFID() != OGRNullFID ||
     988           2 :              (m_nNextFIDWrite >= 0 && bHasJustGotNextFID)))
     989             :         {
     990           1 :             if (bMustComma)
     991           1 :                 osCopySQL += ",";
     992             :             else
     993             :             {
     994           0 :                 osCopySQL += "(";
     995           0 :                 bMustComma = true;
     996             :             }
     997             : 
     998           1 :             osCopySQL += OGRCARTOEscapeIdentifier(osFIDColName);
     999             :         }
    1000             :         /* No columns at all? Return an error! */
    1001           2 :         if (!bMustComma)
    1002           0 :             return OGRERR_FAILURE;
    1003             :         else
    1004           2 :             osCopySQL += ")";
    1005             : 
    1006           2 :         osCopySQL += " FROM STDIN WITH (FORMAT text, ENCODING UTF8)";
    1007           2 :         CPLDebug("CARTO", "ICreateFeatureCopy(%s)", osCopySQL.c_str());
    1008             : 
    1009           2 :         eDeferredInsertState = INSERT_MULTIPLE_FEATURE;
    1010             :     }
    1011             : 
    1012             :     /* Now write the data line into the copy file */
    1013           2 :     bool bMustTab = false;
    1014          12 :     for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    1015             :     {
    1016             :         /* Unset fields get skipped (assuming same field set
    1017             :            pattern as first input feature) */
    1018          10 :         if (!poFeature->IsFieldSet(i))
    1019           8 :             continue;
    1020             : 
    1021             :         /* Tab separator for 'text' format as necessary */
    1022           2 :         if (bMustTab)
    1023           0 :             osCopyFile += "\t";
    1024             :         else
    1025           2 :             bMustTab = true;
    1026             : 
    1027           2 :         OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType();
    1028             :         /* Null fields get a NULL marker */
    1029           2 :         if (poFeature->IsFieldNull(i))
    1030             :         {
    1031           0 :             osCopyFile += "\\N";
    1032             :         }
    1033           2 :         else if (eType == OFTString || eType == OFTDateTime ||
    1034           1 :                  eType == OFTDate || eType == OFTTime)
    1035             :         {
    1036             :             /* Strip out tab and newline characters */
    1037           1 :             osCopyFile +=
    1038           1 :                 OGRCARTOEscapeLiteralCopy(poFeature->GetFieldAsString(i));
    1039             :         }
    1040           2 :         else if ((eType == OFTInteger || eType == OFTInteger64) &&
    1041           1 :                  poFeatureDefn->GetFieldDefn(i)->GetSubType() == OFSTBoolean)
    1042             :         {
    1043           0 :             osCopyFile += poFeature->GetFieldAsInteger(i) ? "t" : "f";
    1044             :         }
    1045             :         else
    1046           1 :             osCopyFile += poFeature->GetFieldAsString(i);
    1047             :     }
    1048             : 
    1049           4 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1050             :     {
    1051           2 :         if (bMustTab)
    1052           2 :             osCopyFile += "\t";
    1053             :         else
    1054           0 :             bMustTab = true;
    1055             : 
    1056           2 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    1057           2 :         if (poGeom == nullptr)
    1058             :         {
    1059           0 :             osCopyFile += "\\N";
    1060           0 :             continue;
    1061             :         }
    1062             : 
    1063           2 :         char *pszEWKB = OGRCARTOGetHexGeometry(poGeom, i);
    1064           2 :         osCopyFile += pszEWKB;
    1065           2 :         CPLFree(pszEWKB);
    1066             :     }
    1067             : 
    1068           2 :     if (!bHasUserFieldMatchingFID && !osFIDColName.empty())
    1069             :     {
    1070           2 :         if (poFeature->GetFID() != OGRNullFID)
    1071             :         {
    1072           0 :             if (bMustTab)
    1073           0 :                 osCopyFile += "\t";
    1074             : 
    1075           0 :             osCopyFile += CPLSPrintf(CPL_FRMT_GIB, poFeature->GetFID());
    1076             :         }
    1077           2 :         else if (m_nNextFIDWrite >= 0 && bHasJustGotNextFID)
    1078             :         {
    1079           1 :             if (bMustTab)
    1080           1 :                 osCopyFile += "\t";
    1081             : 
    1082           1 :             osCopyFile += CPLSPrintf(CPL_FRMT_GIB, m_nNextFIDWrite);
    1083             :         }
    1084             :     }
    1085             : 
    1086             :     /* If we do have access to the FID (because we're incrementing it */
    1087             :     /* ourselves) set it onto the incoming feature so it knows what */
    1088             :     /* FID was supplied by the back-end. */
    1089           2 :     if (!bHasUserFieldMatchingFID && !osFIDColName.empty() &&
    1090           4 :         m_nNextFIDWrite >= 0 && poFeature->GetFID() == OGRNullFID)
    1091             :     {
    1092           2 :         poFeature->SetFID(m_nNextFIDWrite);
    1093           2 :         m_nNextFIDWrite++;
    1094             :     }
    1095             : 
    1096           2 :     OGRErr eRet = OGRERR_NONE;
    1097             :     /* Add current record to buffer */
    1098           2 :     osDeferredBuffer += osCopyFile;
    1099           2 :     osDeferredBuffer += "\n";
    1100           2 :     if ((int)osDeferredBuffer.size() > nMaxChunkSize)
    1101             :     {
    1102           0 :         eRet = FlushDeferredBuffer(false);
    1103           0 :         eDeferredInsertState = INSERT_UNINIT;
    1104             :     }
    1105             : 
    1106           2 :     return eRet;
    1107             : }
    1108             : 
    1109             : /************************************************************************/
    1110             : /*                           ICreateFeatureInsert()                      */
    1111             : /************************************************************************/
    1112             : 
    1113          12 : OGRErr OGRCARTOTableLayer::ICreateFeatureInsert(OGRFeature *poFeature,
    1114             :                                                 bool bHasUserFieldMatchingFID,
    1115             :                                                 bool bHasJustGotNextFID)
    1116             : {
    1117          24 :     CPLString osSQL;
    1118          12 :     GetLayerDefn();
    1119             : 
    1120             :     // Check if we can go on with multiple insertion mode
    1121          12 :     if (eDeferredInsertState == INSERT_MULTIPLE_FEATURE)
    1122             :     {
    1123           3 :         if (!bHasUserFieldMatchingFID && !osFIDColName.empty() &&
    1124           1 :             (poFeature->GetFID() != OGRNullFID ||
    1125           1 :              (m_nNextFIDWrite >= 0 && bHasJustGotNextFID)))
    1126             :         {
    1127           0 :             if (FlushDeferredBuffer(false) != OGRERR_NONE)
    1128           0 :                 return OGRERR_FAILURE;
    1129             :         }
    1130             :     }
    1131             : 
    1132          12 :     bool bWriteInsertInto = (eDeferredInsertState != INSERT_MULTIPLE_FEATURE);
    1133          12 :     bool bResetToUninitInsertStateAfterwards = false;
    1134          12 :     if (eDeferredInsertState == INSERT_UNINIT)
    1135             :     {
    1136           8 :         if (!bInDeferredInsert)
    1137             :         {
    1138           0 :             eDeferredInsertState = INSERT_SINGLE_FEATURE;
    1139             :         }
    1140          23 :         else if (!bHasUserFieldMatchingFID && !osFIDColName.empty() &&
    1141           8 :                  (poFeature->GetFID() != OGRNullFID ||
    1142           7 :                   (m_nNextFIDWrite >= 0 && bHasJustGotNextFID)))
    1143             :         {
    1144           4 :             eDeferredInsertState = INSERT_SINGLE_FEATURE;
    1145           4 :             bResetToUninitInsertStateAfterwards = true;
    1146             :         }
    1147             :         else
    1148             :         {
    1149           4 :             eDeferredInsertState = INSERT_MULTIPLE_FEATURE;
    1150          17 :             for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    1151             :             {
    1152          13 :                 if (poFeatureDefn->GetFieldDefn(i)->GetDefault() != nullptr)
    1153           2 :                     eDeferredInsertState = INSERT_SINGLE_FEATURE;
    1154             :             }
    1155             :         }
    1156             :     }
    1157             : 
    1158          12 :     bool bMustComma = false;
    1159          12 :     if (bWriteInsertInto)
    1160             :     {
    1161             :         osSQL.Printf("INSERT INTO %s ",
    1162          11 :                      OGRCARTOEscapeIdentifier(osName).c_str());
    1163          48 :         for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    1164             :         {
    1165          69 :             if (eDeferredInsertState != INSERT_MULTIPLE_FEATURE &&
    1166          32 :                 !poFeature->IsFieldSet(i))
    1167          26 :                 continue;
    1168             : 
    1169          11 :             if (bMustComma)
    1170           6 :                 osSQL += ", ";
    1171             :             else
    1172             :             {
    1173           5 :                 osSQL += "(";
    1174           5 :                 bMustComma = true;
    1175             :             }
    1176             : 
    1177          22 :             osSQL += OGRCARTOEscapeIdentifier(
    1178          22 :                 poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    1179             :         }
    1180             : 
    1181          22 :         for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1182             :         {
    1183          20 :             if (eDeferredInsertState != INSERT_MULTIPLE_FEATURE &&
    1184           9 :                 poFeature->GetGeomFieldRef(i) == nullptr)
    1185           8 :                 continue;
    1186             : 
    1187           3 :             if (bMustComma)
    1188           2 :                 osSQL += ", ";
    1189             :             else
    1190             :             {
    1191           1 :                 osSQL += "(";
    1192           1 :                 bMustComma = true;
    1193             :             }
    1194             : 
    1195           6 :             osSQL += OGRCARTOEscapeIdentifier(
    1196           6 :                 poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef());
    1197             :         }
    1198             : 
    1199          32 :         if (!bHasUserFieldMatchingFID && !osFIDColName.empty() &&
    1200          11 :             (poFeature->GetFID() != OGRNullFID ||
    1201          10 :              (m_nNextFIDWrite >= 0 && bHasJustGotNextFID)))
    1202             :         {
    1203           4 :             if (bMustComma)
    1204           3 :                 osSQL += ", ";
    1205             :             else
    1206             :             {
    1207           1 :                 osSQL += "(";
    1208           1 :                 bMustComma = true;
    1209             :             }
    1210             : 
    1211           4 :             osSQL += OGRCARTOEscapeIdentifier(osFIDColName);
    1212             :         }
    1213             : 
    1214          11 :         if (!bMustComma && eDeferredInsertState == INSERT_MULTIPLE_FEATURE)
    1215           0 :             eDeferredInsertState = INSERT_SINGLE_FEATURE;
    1216             :     }
    1217             : 
    1218          12 :     if (!bMustComma && eDeferredInsertState == INSERT_SINGLE_FEATURE)
    1219           4 :         osSQL += "DEFAULT VALUES";
    1220             :     else
    1221             :     {
    1222           8 :         if (!bWriteInsertInto &&
    1223           1 :             eDeferredInsertState == INSERT_MULTIPLE_FEATURE)
    1224           1 :             osSQL += ", (";
    1225             :         else
    1226           7 :             osSQL += ") VALUES (";
    1227             : 
    1228           8 :         bMustComma = false;
    1229          36 :         for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    1230             :         {
    1231          28 :             if (!poFeature->IsFieldSet(i))
    1232             :             {
    1233          20 :                 if (eDeferredInsertState == INSERT_MULTIPLE_FEATURE)
    1234             :                 {
    1235           8 :                     if (bMustComma)
    1236           8 :                         osSQL += ", ";
    1237             :                     else
    1238           0 :                         bMustComma = true;
    1239           8 :                     osSQL += "NULL";
    1240             :                 }
    1241          20 :                 continue;
    1242             :             }
    1243             : 
    1244           8 :             if (bMustComma)
    1245           2 :                 osSQL += ", ";
    1246             :             else
    1247           6 :                 bMustComma = true;
    1248             : 
    1249           8 :             OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType();
    1250           8 :             if (poFeature->IsFieldNull(i))
    1251             :             {
    1252           1 :                 osSQL += "NULL";
    1253             :             }
    1254           7 :             else if (eType == OFTString || eType == OFTDateTime ||
    1255           2 :                      eType == OFTDate || eType == OFTTime)
    1256             :             {
    1257           5 :                 osSQL += "'";
    1258           5 :                 osSQL += OGRCARTOEscapeLiteral(poFeature->GetFieldAsString(i));
    1259           5 :                 osSQL += "'";
    1260             :             }
    1261           4 :             else if ((eType == OFTInteger || eType == OFTInteger64) &&
    1262           2 :                      poFeatureDefn->GetFieldDefn(i)->GetSubType() ==
    1263             :                          OFSTBoolean)
    1264             :             {
    1265           1 :                 osSQL += poFeature->GetFieldAsInteger(i) ? "'t'" : "'f'";
    1266             :             }
    1267             :             else
    1268           1 :                 osSQL += poFeature->GetFieldAsString(i);
    1269             :         }
    1270             : 
    1271          16 :         for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1272             :         {
    1273           8 :             OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    1274           8 :             if (poGeom == nullptr)
    1275             :             {
    1276           6 :                 if (eDeferredInsertState == INSERT_MULTIPLE_FEATURE)
    1277             :                 {
    1278           2 :                     if (bMustComma)
    1279           2 :                         osSQL += ", ";
    1280             :                     else
    1281           0 :                         bMustComma = true;
    1282           2 :                     osSQL += "NULL";
    1283             :                 }
    1284           6 :                 continue;
    1285             :             }
    1286             : 
    1287           2 :             if (bMustComma)
    1288           1 :                 osSQL += ", ";
    1289             :             else
    1290           1 :                 bMustComma = true;
    1291             : 
    1292           2 :             char *pszEWKB = OGRCARTOGetHexGeometry(poGeom, i);
    1293             : 
    1294           2 :             osSQL += "'";
    1295           2 :             osSQL += pszEWKB;
    1296           2 :             osSQL += "'";
    1297           2 :             CPLFree(pszEWKB);
    1298             :         }
    1299             : 
    1300          15 :         if (bWriteInsertInto && !bHasUserFieldMatchingFID &&
    1301           7 :             !osFIDColName.empty())
    1302             :         {
    1303           7 :             if (poFeature->GetFID() != OGRNullFID)
    1304             :             {
    1305           1 :                 if (bMustComma)
    1306           0 :                     osSQL += ", ";
    1307             :                 // No need to set bMustComma to true in else case
    1308             :                 // Not in a loop.
    1309             : 
    1310           1 :                 osSQL += CPLSPrintf(CPL_FRMT_GIB, poFeature->GetFID());
    1311             :             }
    1312           6 :             else if (m_nNextFIDWrite >= 0 && bHasJustGotNextFID)
    1313             :             {
    1314           3 :                 if (bMustComma)
    1315           3 :                     osSQL += ", ";
    1316             :                 // No need to set bMustComma to true in else case.
    1317             :                 // Not in a loop.
    1318           3 :                 osSQL += CPLSPrintf(CPL_FRMT_GIB, m_nNextFIDWrite);
    1319             :             }
    1320             :         }
    1321             : 
    1322           8 :         osSQL += ")";
    1323             :     }
    1324             : 
    1325          12 :     if (!bHasUserFieldMatchingFID && !osFIDColName.empty() &&
    1326          24 :         m_nNextFIDWrite >= 0 && poFeature->GetFID() == OGRNullFID)
    1327             :     {
    1328           9 :         poFeature->SetFID(m_nNextFIDWrite);
    1329           9 :         m_nNextFIDWrite++;
    1330             :     }
    1331             : 
    1332          12 :     if (bInDeferredInsert)
    1333             :     {
    1334           9 :         OGRErr eRet = OGRERR_NONE;
    1335             :         // In multiple mode, this would require rebuilding the osSQL
    1336             :         // buffer. Annoying.
    1337          24 :         if (eDeferredInsertState == INSERT_SINGLE_FEATURE &&
    1338          10 :             !osDeferredBuffer.empty() &&
    1339           1 :             (int)osDeferredBuffer.size() + (int)osSQL.size() > nMaxChunkSize)
    1340             :         {
    1341           0 :             eRet = FlushDeferredBuffer(false);
    1342             :         }
    1343             : 
    1344           9 :         osDeferredBuffer += osSQL;
    1345           9 :         if (eDeferredInsertState == INSERT_SINGLE_FEATURE)
    1346           6 :             osDeferredBuffer += ";";
    1347             : 
    1348           9 :         if ((int)osDeferredBuffer.size() > nMaxChunkSize)
    1349             :         {
    1350           1 :             eRet = FlushDeferredBuffer(false);
    1351             :         }
    1352             : 
    1353           9 :         if (bResetToUninitInsertStateAfterwards)
    1354           4 :             eDeferredInsertState = INSERT_UNINIT;
    1355             : 
    1356           9 :         return eRet;
    1357             :     }
    1358             : 
    1359           3 :     if (!osFIDColName.empty())
    1360             :     {
    1361           3 :         osSQL += " RETURNING ";
    1362           3 :         osSQL += OGRCARTOEscapeIdentifier(osFIDColName);
    1363             : 
    1364           3 :         json_object *poObj = poDS->RunSQL(osSQL);
    1365           3 :         json_object *poRowObj = OGRCARTOGetSingleRow(poObj);
    1366           3 :         if (poRowObj == nullptr)
    1367             :         {
    1368           1 :             if (poObj != nullptr)
    1369           0 :                 json_object_put(poObj);
    1370           1 :             return OGRERR_FAILURE;
    1371             :         }
    1372             : 
    1373           2 :         json_object *poID = CPL_json_object_object_get(poRowObj, osFIDColName);
    1374           2 :         if (poID != nullptr && json_object_get_type(poID) == json_type_int)
    1375             :         {
    1376           2 :             poFeature->SetFID(json_object_get_int64(poID));
    1377             :         }
    1378             : 
    1379           2 :         if (poObj != nullptr)
    1380           2 :             json_object_put(poObj);
    1381             : 
    1382           2 :         return OGRERR_NONE;
    1383             :     }
    1384             :     else
    1385             :     {
    1386           0 :         OGRErr eRet = OGRERR_FAILURE;
    1387           0 :         json_object *poObj = poDS->RunSQL(osSQL);
    1388           0 :         if (poObj != nullptr)
    1389             :         {
    1390             :             json_object *poTotalRows =
    1391           0 :                 CPL_json_object_object_get(poObj, "total_rows");
    1392           0 :             if (poTotalRows != nullptr &&
    1393           0 :                 json_object_get_type(poTotalRows) == json_type_int)
    1394             :             {
    1395           0 :                 int nTotalRows = json_object_get_int(poTotalRows);
    1396           0 :                 if (nTotalRows == 1)
    1397             :                 {
    1398           0 :                     eRet = OGRERR_NONE;
    1399             :                 }
    1400             :             }
    1401           0 :             json_object_put(poObj);
    1402             :         }
    1403             : 
    1404           0 :         return eRet;
    1405             :     }
    1406             : }
    1407             : 
    1408             : /************************************************************************/
    1409             : /*                            ISetFeature()                              */
    1410             : /************************************************************************/
    1411             : 
    1412           5 : OGRErr OGRCARTOTableLayer::ISetFeature(OGRFeature *poFeature)
    1413             : 
    1414             : {
    1415           5 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    1416           0 :         return OGRERR_FAILURE;
    1417           5 :     if (FlushDeferredBuffer() != OGRERR_NONE)
    1418           0 :         return OGRERR_FAILURE;
    1419             : 
    1420           5 :     GetLayerDefn();
    1421             : 
    1422           5 :     if (!poDS->IsReadWrite())
    1423             :     {
    1424           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1425             :                  "Operation not available in read-only mode");
    1426           1 :         return OGRERR_FAILURE;
    1427             :     }
    1428             : 
    1429           4 :     if (poFeature->GetFID() == OGRNullFID)
    1430             :     {
    1431           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1432             :                  "FID required on features given to SetFeature().");
    1433           1 :         return OGRERR_FAILURE;
    1434             :     }
    1435             : 
    1436           6 :     CPLString osSQL;
    1437           3 :     osSQL.Printf("UPDATE %s SET ", OGRCARTOEscapeIdentifier(osName).c_str());
    1438           3 :     bool bMustComma = false;
    1439          12 :     for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    1440             :     {
    1441           9 :         if (!poFeature->IsFieldSet(i))
    1442           0 :             continue;
    1443             : 
    1444           9 :         if (bMustComma)
    1445           6 :             osSQL += ", ";
    1446             :         else
    1447           3 :             bMustComma = true;
    1448             : 
    1449          18 :         osSQL += OGRCARTOEscapeIdentifier(
    1450          18 :             poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    1451           9 :         osSQL += " = ";
    1452             : 
    1453           9 :         if (poFeature->IsFieldNull(i))
    1454             :         {
    1455           0 :             osSQL += "NULL";
    1456             :         }
    1457             :         else
    1458             :         {
    1459           9 :             OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType();
    1460           9 :             if (eType == OFTString || eType == OFTDateTime ||
    1461           6 :                 eType == OFTDate || eType == OFTTime)
    1462             :             {
    1463           3 :                 osSQL += "'";
    1464           3 :                 osSQL += OGRCARTOEscapeLiteral(poFeature->GetFieldAsString(i));
    1465           3 :                 osSQL += "'";
    1466             :             }
    1467          12 :             else if ((eType == OFTInteger || eType == OFTInteger64) &&
    1468           6 :                      poFeatureDefn->GetFieldDefn(i)->GetSubType() ==
    1469             :                          OFSTBoolean)
    1470             :             {
    1471           3 :                 osSQL += poFeature->GetFieldAsInteger(i) ? "'t'" : "'f'";
    1472             :             }
    1473             :             else
    1474           3 :                 osSQL += poFeature->GetFieldAsString(i);
    1475             :         }
    1476             :     }
    1477             : 
    1478           6 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1479             :     {
    1480           3 :         if (bMustComma)
    1481           3 :             osSQL += ", ";
    1482             :         else
    1483           0 :             bMustComma = true;
    1484             : 
    1485           6 :         osSQL += OGRCARTOEscapeIdentifier(
    1486           6 :             poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef());
    1487           3 :         osSQL += " = ";
    1488             : 
    1489           3 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    1490           3 :         if (poGeom == nullptr)
    1491             :         {
    1492           0 :             osSQL += "NULL";
    1493             :         }
    1494             :         else
    1495             :         {
    1496             :             OGRCartoGeomFieldDefn *poGeomFieldDefn =
    1497           3 :                 (OGRCartoGeomFieldDefn *)(poFeatureDefn->GetGeomFieldDefn(i));
    1498           3 :             int nSRID = poGeomFieldDefn->nSRID;
    1499           3 :             if (nSRID == 0)
    1500           0 :                 nSRID = 4326;
    1501             :             char *pszEWKB =
    1502           3 :                 OGRGeometryToHexEWKB(poGeom, nSRID, poDS->GetPostGISMajor(),
    1503           3 :                                      poDS->GetPostGISMinor());
    1504           3 :             osSQL += "'";
    1505           3 :             osSQL += pszEWKB;
    1506           3 :             osSQL += "'";
    1507           3 :             CPLFree(pszEWKB);
    1508             :         }
    1509             :     }
    1510             : 
    1511           3 :     if (!bMustComma)  // nothing to do
    1512           0 :         return OGRERR_NONE;
    1513             : 
    1514             :     osSQL += CPLSPrintf(" WHERE %s = " CPL_FRMT_GIB,
    1515           6 :                         OGRCARTOEscapeIdentifier(osFIDColName).c_str(),
    1516           6 :                         poFeature->GetFID());
    1517             : 
    1518           3 :     OGRErr eRet = OGRERR_FAILURE;
    1519           3 :     json_object *poObj = poDS->RunSQL(osSQL);
    1520           3 :     if (poObj != nullptr)
    1521             :     {
    1522             :         json_object *poTotalRows =
    1523           2 :             CPL_json_object_object_get(poObj, "total_rows");
    1524           4 :         if (poTotalRows != nullptr &&
    1525           2 :             json_object_get_type(poTotalRows) == json_type_int)
    1526             :         {
    1527           2 :             int nTotalRows = json_object_get_int(poTotalRows);
    1528           2 :             if (nTotalRows > 0)
    1529             :             {
    1530           1 :                 eRet = OGRERR_NONE;
    1531             :             }
    1532             :             else
    1533           1 :                 eRet = OGRERR_NON_EXISTING_FEATURE;
    1534             :         }
    1535           2 :         json_object_put(poObj);
    1536             :     }
    1537             : 
    1538           3 :     return eRet;
    1539             : }
    1540             : 
    1541             : /************************************************************************/
    1542             : /*                          DeleteFeature()                             */
    1543             : /************************************************************************/
    1544             : 
    1545           4 : OGRErr OGRCARTOTableLayer::DeleteFeature(GIntBig nFID)
    1546             : 
    1547             : {
    1548             : 
    1549           4 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    1550           0 :         return OGRERR_FAILURE;
    1551           4 :     if (FlushDeferredBuffer() != OGRERR_NONE)
    1552           0 :         return OGRERR_FAILURE;
    1553             : 
    1554           4 :     GetLayerDefn();
    1555             : 
    1556           4 :     if (!poDS->IsReadWrite())
    1557             :     {
    1558           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1559             :                  "Operation not available in read-only mode");
    1560           1 :         return OGRERR_FAILURE;
    1561             :     }
    1562             : 
    1563           3 :     if (osFIDColName.empty())
    1564           0 :         return OGRERR_FAILURE;
    1565             : 
    1566           3 :     CPLString osSQL;
    1567             :     osSQL.Printf("DELETE FROM %s WHERE %s = " CPL_FRMT_GIB,
    1568           6 :                  OGRCARTOEscapeIdentifier(osName).c_str(),
    1569           9 :                  OGRCARTOEscapeIdentifier(osFIDColName).c_str(), nFID);
    1570             : 
    1571           3 :     OGRErr eRet = OGRERR_FAILURE;
    1572           3 :     json_object *poObj = poDS->RunSQL(osSQL);
    1573           3 :     if (poObj != nullptr)
    1574             :     {
    1575             :         json_object *poTotalRows =
    1576           2 :             CPL_json_object_object_get(poObj, "total_rows");
    1577           4 :         if (poTotalRows != nullptr &&
    1578           2 :             json_object_get_type(poTotalRows) == json_type_int)
    1579             :         {
    1580           2 :             int nTotalRows = json_object_get_int(poTotalRows);
    1581           2 :             if (nTotalRows > 0)
    1582             :             {
    1583           1 :                 eRet = OGRERR_NONE;
    1584             :             }
    1585             :             else
    1586           1 :                 eRet = OGRERR_NON_EXISTING_FEATURE;
    1587             :         }
    1588           2 :         json_object_put(poObj);
    1589             :     }
    1590             : 
    1591           3 :     return eRet;
    1592             : }
    1593             : 
    1594             : /************************************************************************/
    1595             : /*                             GetSRS_SQL()                             */
    1596             : /************************************************************************/
    1597             : 
    1598           0 : CPLString OGRCARTOTableLayer::GetSRS_SQL(const char *pszGeomCol)
    1599             : {
    1600           0 :     CPLString osSQL;
    1601             : 
    1602             :     osSQL.Printf("SELECT srid, srtext FROM spatial_ref_sys WHERE srid IN "
    1603             :                  "(SELECT Find_SRID('%s', '%s', '%s'))",
    1604           0 :                  OGRCARTOEscapeLiteral(poDS->GetCurrentSchema()).c_str(),
    1605           0 :                  OGRCARTOEscapeLiteral(osName).c_str(),
    1606           0 :                  OGRCARTOEscapeLiteral(pszGeomCol).c_str());
    1607             : 
    1608           0 :     return osSQL;
    1609             : }
    1610             : 
    1611             : /************************************************************************/
    1612             : /*                             BuildWhere()                             */
    1613             : /*                                                                      */
    1614             : /*      Build the WHERE statement appropriate to the current set of     */
    1615             : /*      criteria (spatial and attribute queries).                       */
    1616             : /************************************************************************/
    1617             : 
    1618           8 : void OGRCARTOTableLayer::BuildWhere()
    1619             : 
    1620             : {
    1621           8 :     osWHERE = "";
    1622             : 
    1623           9 :     if (m_poFilterGeom != nullptr && m_iGeomFieldFilter >= 0 &&
    1624           1 :         m_iGeomFieldFilter < poFeatureDefn->GetGeomFieldCount())
    1625             :     {
    1626           1 :         OGREnvelope sEnvelope;
    1627             : 
    1628           1 :         m_poFilterGeom->getEnvelope(&sEnvelope);
    1629             : 
    1630             :         CPLString osGeomColumn(
    1631           1 :             poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)->GetNameRef());
    1632             : 
    1633             :         char szBox3D_1[128];
    1634             :         char szBox3D_2[128];
    1635             :         char *pszComma;
    1636             : 
    1637           1 :         CPLsnprintf(szBox3D_1, sizeof(szBox3D_1), "%.18g %.18g", sEnvelope.MinX,
    1638             :                     sEnvelope.MinY);
    1639           1 :         while ((pszComma = strchr(szBox3D_1, ',')) != nullptr)
    1640           0 :             *pszComma = '.';
    1641           1 :         CPLsnprintf(szBox3D_2, sizeof(szBox3D_2), "%.18g %.18g", sEnvelope.MaxX,
    1642             :                     sEnvelope.MaxY);
    1643           1 :         while ((pszComma = strchr(szBox3D_2, ',')) != nullptr)
    1644           0 :             *pszComma = '.';
    1645             :         osWHERE.Printf("(%s && 'BOX3D(%s, %s)'::box3d)",
    1646           2 :                        OGRCARTOEscapeIdentifier(osGeomColumn).c_str(),
    1647           2 :                        szBox3D_1, szBox3D_2);
    1648             :     }
    1649             : 
    1650           8 :     if (!osQuery.empty())
    1651             :     {
    1652           2 :         if (!osWHERE.empty())
    1653           1 :             osWHERE += " AND ";
    1654           2 :         osWHERE += osQuery;
    1655             :     }
    1656             : 
    1657           8 :     if (osFIDColName.empty())
    1658             :     {
    1659           2 :         osBaseSQL = osSELECTWithoutWHERE;
    1660           2 :         if (!osWHERE.empty())
    1661             :         {
    1662           0 :             osBaseSQL += " WHERE ";
    1663           0 :             osBaseSQL += osWHERE;
    1664             :         }
    1665             :     }
    1666           8 : }
    1667             : 
    1668             : /************************************************************************/
    1669             : /*                              GetFeature()                            */
    1670             : /************************************************************************/
    1671             : 
    1672           5 : OGRFeature *OGRCARTOTableLayer::GetFeature(GIntBig nFeatureId)
    1673             : {
    1674             : 
    1675           5 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    1676           0 :         return nullptr;
    1677           5 :     if (FlushDeferredBuffer() != OGRERR_NONE)
    1678           0 :         return nullptr;
    1679             : 
    1680           5 :     GetLayerDefn();
    1681             : 
    1682           5 :     if (osFIDColName.empty())
    1683           1 :         return OGRCARTOLayer::GetFeature(nFeatureId);
    1684             : 
    1685           8 :     CPLString osSQL = osSELECTWithoutWHERE;
    1686           4 :     osSQL += " WHERE ";
    1687           4 :     osSQL += OGRCARTOEscapeIdentifier(osFIDColName).c_str();
    1688           4 :     osSQL += " = ";
    1689           4 :     osSQL += CPLSPrintf(CPL_FRMT_GIB, nFeatureId);
    1690             : 
    1691           4 :     json_object *poObj = poDS->RunSQL(osSQL);
    1692           4 :     json_object *poRowObj = OGRCARTOGetSingleRow(poObj);
    1693           4 :     if (poRowObj == nullptr)
    1694             :     {
    1695           2 :         if (poObj != nullptr)
    1696           0 :             json_object_put(poObj);
    1697           2 :         return OGRCARTOLayer::GetFeature(nFeatureId);
    1698             :     }
    1699             : 
    1700           2 :     OGRFeature *poFeature = BuildFeature(poRowObj);
    1701           2 :     json_object_put(poObj);
    1702             : 
    1703           2 :     return poFeature;
    1704             : }
    1705             : 
    1706             : /************************************************************************/
    1707             : /*                          GetFeatureCount()                           */
    1708             : /************************************************************************/
    1709             : 
    1710           9 : GIntBig OGRCARTOTableLayer::GetFeatureCount(int bForce)
    1711             : {
    1712             : 
    1713           9 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    1714           0 :         return 0;
    1715           9 :     if (FlushDeferredBuffer() != OGRERR_NONE)
    1716           0 :         return 0;
    1717             : 
    1718           9 :     GetLayerDefn();
    1719             : 
    1720             :     CPLString osSQL(CPLSPrintf("SELECT COUNT(*) FROM %s",
    1721          18 :                                OGRCARTOEscapeIdentifier(osName).c_str()));
    1722           9 :     if (!osWHERE.empty())
    1723             :     {
    1724           5 :         osSQL += " WHERE ";
    1725           5 :         osSQL += osWHERE;
    1726             :     }
    1727             : 
    1728           9 :     json_object *poObj = poDS->RunSQL(osSQL);
    1729           9 :     json_object *poRowObj = OGRCARTOGetSingleRow(poObj);
    1730           9 :     if (poRowObj == nullptr)
    1731             :     {
    1732           5 :         if (poObj != nullptr)
    1733           1 :             json_object_put(poObj);
    1734           5 :         return OGRCARTOLayer::GetFeatureCount(bForce);
    1735             :     }
    1736             : 
    1737           4 :     json_object *poCount = CPL_json_object_object_get(poRowObj, "count");
    1738           4 :     if (poCount == nullptr || json_object_get_type(poCount) != json_type_int)
    1739             :     {
    1740           1 :         json_object_put(poObj);
    1741           1 :         return OGRCARTOLayer::GetFeatureCount(bForce);
    1742             :     }
    1743             : 
    1744           3 :     GIntBig nRet = (GIntBig)json_object_get_int64(poCount);
    1745             : 
    1746           3 :     json_object_put(poObj);
    1747             : 
    1748           3 :     return nRet;
    1749             : }
    1750             : 
    1751             : /************************************************************************/
    1752             : /*                             GetExtent()                              */
    1753             : /*                                                                      */
    1754             : /*      For PostGIS use internal Extend(geometry) function              */
    1755             : /*      in other cases we use standard OGRLayer::GetExtent()            */
    1756             : /************************************************************************/
    1757             : 
    1758           6 : OGRErr OGRCARTOTableLayer::GetExtent(int iGeomField, OGREnvelope *psExtent,
    1759             :                                      int bForce)
    1760             : {
    1761          12 :     CPLString osSQL;
    1762             : 
    1763           6 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    1764           0 :         return OGRERR_FAILURE;
    1765           6 :     if (FlushDeferredBuffer() != OGRERR_NONE)
    1766           0 :         return OGRERR_FAILURE;
    1767             : 
    1768          12 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
    1769           6 :         GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
    1770             :     {
    1771           0 :         if (iGeomField != 0)
    1772             :         {
    1773           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1774             :                      "Invalid geometry field index : %d", iGeomField);
    1775             :         }
    1776           0 :         return OGRERR_FAILURE;
    1777             :     }
    1778             : 
    1779             :     OGRGeomFieldDefn *poGeomFieldDefn =
    1780           6 :         poFeatureDefn->GetGeomFieldDefn(iGeomField);
    1781             : 
    1782             :     /* Do not take the spatial filter into account */
    1783             :     osSQL.Printf(
    1784             :         "SELECT ST_Extent(%s) FROM %s",
    1785          12 :         OGRCARTOEscapeIdentifier(poGeomFieldDefn->GetNameRef()).c_str(),
    1786          18 :         OGRCARTOEscapeIdentifier(osName).c_str());
    1787             : 
    1788           6 :     json_object *poObj = poDS->RunSQL(osSQL);
    1789           6 :     json_object *poRowObj = OGRCARTOGetSingleRow(poObj);
    1790           6 :     if (poRowObj != nullptr)
    1791             :     {
    1792             :         json_object *poExtent =
    1793           5 :             CPL_json_object_object_get(poRowObj, "st_extent");
    1794           9 :         if (poExtent != nullptr &&
    1795           4 :             json_object_get_type(poExtent) == json_type_string)
    1796             :         {
    1797           4 :             const char *pszBox = json_object_get_string(poExtent);
    1798             :             const char *ptr, *ptrEndParenthesis;
    1799             :             char szVals[64 * 6 + 6];
    1800             : 
    1801           4 :             ptr = strchr(pszBox, '(');
    1802           4 :             if (ptr)
    1803           3 :                 ptr++;
    1804           7 :             if (ptr == nullptr ||
    1805           6 :                 (ptrEndParenthesis = strchr(ptr, ')')) == nullptr ||
    1806           2 :                 ptrEndParenthesis - ptr > (int)(sizeof(szVals) - 1))
    1807             :             {
    1808           2 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1809             :                          "Bad extent representation: '%s'", pszBox);
    1810             : 
    1811           2 :                 json_object_put(poObj);
    1812           2 :                 return OGRERR_FAILURE;
    1813             :             }
    1814             : 
    1815           2 :             strncpy(szVals, ptr, ptrEndParenthesis - ptr);
    1816           2 :             szVals[ptrEndParenthesis - ptr] = '\0';
    1817             : 
    1818             :             char **papszTokens =
    1819           2 :                 CSLTokenizeString2(szVals, " ,", CSLT_HONOURSTRINGS);
    1820           2 :             int nTokenCnt = 4;
    1821             : 
    1822           2 :             if (CSLCount(papszTokens) != nTokenCnt)
    1823             :             {
    1824           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1825             :                          "Bad extent representation: '%s'", pszBox);
    1826           1 :                 CSLDestroy(papszTokens);
    1827             : 
    1828           1 :                 json_object_put(poObj);
    1829           1 :                 return OGRERR_FAILURE;
    1830             :             }
    1831             : 
    1832             :             // Take X,Y coords
    1833             :             // For PostGIS ver >= 1.0.0 -> Tokens: X1 Y1 X2 Y2 (nTokenCnt = 4)
    1834             :             // For PostGIS ver < 1.0.0 -> Tokens: X1 Y1 Z1 X2 Y2 Z2 (nTokenCnt =
    1835             :             // 6)
    1836             :             // =>   X2 index calculated as nTokenCnt/2
    1837             :             //      Y2 index calculated as nTokenCnt/2+1
    1838             : 
    1839           1 :             psExtent->MinX = CPLAtof(papszTokens[0]);
    1840           1 :             psExtent->MinY = CPLAtof(papszTokens[1]);
    1841           1 :             psExtent->MaxX = CPLAtof(papszTokens[nTokenCnt / 2]);
    1842           1 :             psExtent->MaxY = CPLAtof(papszTokens[nTokenCnt / 2 + 1]);
    1843             : 
    1844           1 :             CSLDestroy(papszTokens);
    1845             : 
    1846           1 :             json_object_put(poObj);
    1847           1 :             return OGRERR_NONE;
    1848             :         }
    1849             :     }
    1850             : 
    1851           2 :     if (poObj != nullptr)
    1852           1 :         json_object_put(poObj);
    1853             : 
    1854           2 :     if (iGeomField == 0)
    1855           2 :         return OGRLayer::GetExtent(psExtent, bForce);
    1856             :     else
    1857           0 :         return OGRLayer::GetExtent(iGeomField, psExtent, bForce);
    1858             : }
    1859             : 
    1860             : /************************************************************************/
    1861             : /*                           TestCapability()                           */
    1862             : /************************************************************************/
    1863             : 
    1864          16 : int OGRCARTOTableLayer::TestCapability(const char *pszCap)
    1865             : 
    1866             : {
    1867          16 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    1868           0 :         return TRUE;
    1869          16 :     if (EQUAL(pszCap, OLCFastGetExtent))
    1870           0 :         return TRUE;
    1871          16 :     if (EQUAL(pszCap, OLCRandomRead))
    1872             :     {
    1873           0 :         GetLayerDefn();
    1874           0 :         return !osFIDColName.empty();
    1875             :     }
    1876             : 
    1877          16 :     if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite) ||
    1878          16 :         EQUAL(pszCap, OLCDeleteFeature) || EQUAL(pszCap, OLCCreateField) ||
    1879          16 :         EQUAL(pszCap, OLCDeleteField) || EQUAL(pszCap, OLCCreateGeomField))
    1880             :     {
    1881           0 :         return poDS->IsReadWrite();
    1882             :     }
    1883             : 
    1884          16 :     return OGRCARTOLayer::TestCapability(pszCap);
    1885             : }
    1886             : 
    1887             : /************************************************************************/
    1888             : /*                        SetDeferredCreation()                          */
    1889             : /************************************************************************/
    1890             : 
    1891           4 : void OGRCARTOTableLayer::SetDeferredCreation(OGRwkbGeometryType eGType,
    1892             :                                              OGRSpatialReference *poSRSIn,
    1893             :                                              bool bGeomNullable,
    1894             :                                              bool bCartodbfyIn)
    1895             : {
    1896           4 :     bDeferredCreation = true;
    1897           4 :     m_nNextFIDWrite = 1;
    1898           4 :     CPLAssert(poFeatureDefn == nullptr);
    1899           4 :     bCartodbfy = bCartodbfyIn;
    1900           4 :     poFeatureDefn = new OGRFeatureDefn(osName);
    1901           4 :     poFeatureDefn->Reference();
    1902           4 :     poFeatureDefn->SetGeomType(wkbNone);
    1903           4 :     if (eGType == wkbPolygon)
    1904           2 :         eGType = wkbMultiPolygon;
    1905           2 :     else if (eGType == wkbPolygon25D)
    1906           0 :         eGType = wkbMultiPolygon25D;
    1907           4 :     if (eGType != wkbNone)
    1908             :     {
    1909             :         auto poFieldDefn =
    1910           4 :             std::make_unique<OGRCartoGeomFieldDefn>("the_geom", eGType);
    1911           4 :         poFieldDefn->SetNullable(bGeomNullable);
    1912           4 :         if (poSRSIn != nullptr)
    1913             :         {
    1914           1 :             poFieldDefn->nSRID = poDS->FetchSRSId(poSRSIn);
    1915           1 :             poFieldDefn->SetSpatialRef(poSRSIn);
    1916             :         }
    1917           4 :         poFeatureDefn->AddGeomFieldDefn(std::move(poFieldDefn));
    1918             :     }
    1919           4 :     osFIDColName = "cartodb_id";
    1920             :     osBaseSQL.Printf("SELECT * FROM %s",
    1921           4 :                      OGRCARTOEscapeIdentifier(osName).c_str());
    1922           4 :     osSELECTWithoutWHERE = osBaseSQL;
    1923           4 : }
    1924             : 
    1925             : /************************************************************************/
    1926             : /*                      RunDeferredCreationIfNecessary()                 */
    1927             : /************************************************************************/
    1928             : 
    1929           5 : OGRErr OGRCARTOTableLayer::RunDeferredCreationIfNecessary()
    1930             : {
    1931           5 :     if (!bDeferredCreation)
    1932           1 :         return OGRERR_NONE;
    1933           4 :     bDeferredCreation = false;
    1934             : 
    1935           8 :     CPLString osSQL;
    1936           4 :     CPLDebug("CARTO", "Overwrite on creation (%d)", bDropOnCreation);
    1937           4 :     if (bDropOnCreation)
    1938             :         osSQL.Printf("BEGIN; DROP TABLE IF EXISTS %s;",
    1939           2 :                      OGRCARTOEscapeIdentifier(osName).c_str());
    1940             : 
    1941             :     osSQL += CPLSPrintf("CREATE TABLE %s ( %s SERIAL,",
    1942           8 :                         OGRCARTOEscapeIdentifier(osName).c_str(),
    1943           8 :                         osFIDColName.c_str());
    1944             : 
    1945           8 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1946             :     {
    1947             :         OGRCartoGeomFieldDefn *poFieldDefn =
    1948           4 :             (OGRCartoGeomFieldDefn *)(poFeatureDefn->GetGeomFieldDefn(i));
    1949           4 :         OGRwkbGeometryType eGType = poFieldDefn->GetType();
    1950           4 :         if (eGType == wkbNone)
    1951           0 :             continue;
    1952             : 
    1953           4 :         const char *pszFieldName = "the_geom";
    1954             : 
    1955           4 :         if (i > 0)
    1956           0 :             pszFieldName = poFieldDefn->GetNameRef();
    1957             : 
    1958           4 :         if (pszFieldName == nullptr || strlen(pszFieldName) == 0)
    1959           0 :             return OGRERR_FAILURE;
    1960             : 
    1961             :         osSQL += CPLSPrintf("%s %s%s,", pszFieldName,
    1962           8 :                             OGRCARTOGeometryType(poFieldDefn).c_str(),
    1963           8 :                             (!poFieldDefn->IsNullable()) ? " NOT NULL" : "");
    1964             :     }
    1965             : 
    1966           5 :     for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    1967             :     {
    1968           1 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(i);
    1969           1 :         if (strcmp(poFieldDefn->GetNameRef(), osFIDColName) != 0)
    1970             :         {
    1971           1 :             osSQL += OGRCARTOEscapeIdentifier(poFieldDefn->GetNameRef());
    1972           1 :             osSQL += " ";
    1973           1 :             osSQL += OGRPGCommonLayerGetType(*poFieldDefn, false, true);
    1974           1 :             if (!poFieldDefn->IsNullable())
    1975           1 :                 osSQL += " NOT NULL";
    1976           2 :             if (poFieldDefn->GetDefault() != nullptr &&
    1977           1 :                 !poFieldDefn->IsDefaultDriverSpecific())
    1978             :             {
    1979           1 :                 osSQL += " DEFAULT ";
    1980           1 :                 osSQL += poFieldDefn->GetDefault();
    1981             :             }
    1982           1 :             osSQL += ",";
    1983             :         }
    1984             :     }
    1985             : 
    1986           4 :     osSQL += CPLSPrintf("PRIMARY KEY (%s) )", osFIDColName.c_str());
    1987             : 
    1988             :     CPLString osSeqName(OGRCARTOEscapeIdentifier(
    1989           8 :         CPLSPrintf("%s_%s_seq", osName.c_str(), osFIDColName.c_str())));
    1990             : 
    1991           4 :     osSQL += ";";
    1992             :     osSQL +=
    1993           4 :         CPLSPrintf("DROP SEQUENCE IF EXISTS %s CASCADE", osSeqName.c_str());
    1994           4 :     osSQL += ";";
    1995           4 :     osSQL += CPLSPrintf("CREATE SEQUENCE %s START 1", osSeqName.c_str());
    1996           4 :     osSQL += ";";
    1997             :     osSQL += CPLSPrintf("ALTER SEQUENCE %s OWNED BY %s.%s", osSeqName.c_str(),
    1998           8 :                         OGRCARTOEscapeIdentifier(osName).c_str(),
    1999           8 :                         osFIDColName.c_str());
    2000           4 :     osSQL += ";";
    2001             :     osSQL +=
    2002             :         CPLSPrintf("ALTER TABLE %s ALTER COLUMN %s SET DEFAULT nextval('%s')",
    2003           8 :                    OGRCARTOEscapeIdentifier(osName).c_str(),
    2004           8 :                    osFIDColName.c_str(), osSeqName.c_str());
    2005             : 
    2006           4 :     if (bDropOnCreation)
    2007           2 :         osSQL += "; COMMIT;";
    2008             : 
    2009           4 :     bDropOnCreation = false;
    2010             : 
    2011           4 :     json_object *poObj = poDS->RunSQL(osSQL);
    2012           4 :     if (poObj == nullptr)
    2013           2 :         return OGRERR_FAILURE;
    2014           2 :     json_object_put(poObj);
    2015             : 
    2016           2 :     return OGRERR_NONE;
    2017             : }

Generated by: LCOV version 1.14