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

Generated by: LCOV version 1.14