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

Generated by: LCOV version 1.14