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

Generated by: LCOV version 1.14