LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/carto - ogrcartodatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 299 387 77.3 %
Date: 2024-11-21 22:18:42 Functions: 16 18 88.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Carto Translator
       4             :  * Purpose:  Implements OGRCARTODataSource 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_pgdump.h"
      15             : #include "ogrlibjsonutils.h"
      16             : 
      17             : /************************************************************************/
      18             : /*                        OGRCARTODataSource()                        */
      19             : /************************************************************************/
      20             : 
      21          35 : OGRCARTODataSource::OGRCARTODataSource()
      22             :     : pszAccount(nullptr), papoLayers(nullptr), nLayers(0), bReadWrite(false),
      23             :       bBatchInsert(true), bCopyMode(true), bUseHTTPS(false),
      24             :       bMustCleanPersistent(false), bHasOGRMetadataFunction(-1),
      25          35 :       nPostGISMajor(2), nPostGISMinor(0)
      26             : {
      27          35 : }
      28             : 
      29             : /************************************************************************/
      30             : /*                       ~OGRCARTODataSource()                        */
      31             : /************************************************************************/
      32             : 
      33          70 : OGRCARTODataSource::~OGRCARTODataSource()
      34             : 
      35             : {
      36          53 :     for (int i = 0; i < nLayers; i++)
      37          18 :         delete papoLayers[i];
      38          35 :     CPLFree(papoLayers);
      39             : 
      40          35 :     if (bMustCleanPersistent)
      41             :     {
      42           0 :         char **papszOptions = nullptr;
      43           0 :         papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT",
      44             :                                        CPLSPrintf("CARTO:%p", this));
      45           0 :         CPLHTTPDestroyResult(CPLHTTPFetch(GetAPIURL(), papszOptions));
      46           0 :         CSLDestroy(papszOptions);
      47             :     }
      48             : 
      49          35 :     CPLFree(pszAccount);
      50          70 : }
      51             : 
      52             : /************************************************************************/
      53             : /*                           TestCapability()                           */
      54             : /************************************************************************/
      55             : 
      56           0 : int OGRCARTODataSource::TestCapability(const char *pszCap)
      57             : 
      58             : {
      59           0 :     if (bReadWrite && EQUAL(pszCap, ODsCCreateLayer))
      60           0 :         return TRUE;
      61           0 :     else if (bReadWrite && EQUAL(pszCap, ODsCDeleteLayer))
      62           0 :         return TRUE;
      63           0 :     else if (bReadWrite && EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
      64           0 :         return TRUE;
      65           0 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
      66           0 :         return bReadWrite;
      67             :     else
      68           0 :         return FALSE;
      69             : }
      70             : 
      71             : /************************************************************************/
      72             : /*                              GetLayer()                              */
      73             : /************************************************************************/
      74             : 
      75          12 : OGRLayer *OGRCARTODataSource::GetLayer(int iLayer)
      76             : 
      77             : {
      78          12 :     if (iLayer < 0 || iLayer >= nLayers)
      79           0 :         return nullptr;
      80             :     else
      81          12 :         return papoLayers[iLayer];
      82             : }
      83             : 
      84             : /************************************************************************/
      85             : /*                     OGRCARTOGetOptionValue()                       */
      86             : /************************************************************************/
      87             : 
      88          35 : static CPLString OGRCARTOGetOptionValue(const char *pszFilename,
      89             :                                         const char *pszOptionName)
      90             : {
      91          70 :     CPLString osOptionName(pszOptionName);
      92          35 :     osOptionName += "=";
      93          35 :     const char *pszOptionValue = strstr(pszFilename, osOptionName);
      94          35 :     if (!pszOptionValue)
      95          35 :         return "";
      96             : 
      97           0 :     CPLString osOptionValue(pszOptionValue + osOptionName.size());
      98           0 :     const char *pszSpace = strchr(osOptionValue.c_str(), ' ');
      99           0 :     if (pszSpace)
     100           0 :         osOptionValue.resize(pszSpace - osOptionValue.c_str());
     101           0 :     return osOptionValue;
     102             : }
     103             : 
     104             : /************************************************************************/
     105             : /*                                Open()                                */
     106             : /************************************************************************/
     107             : 
     108          35 : int OGRCARTODataSource::Open(const char *pszFilename, char **papszOpenOptionsIn,
     109             :                              int bUpdateIn)
     110             : 
     111             : {
     112          35 :     bReadWrite = CPL_TO_BOOL(bUpdateIn);
     113          35 :     bBatchInsert = CPLTestBool(
     114             :         CSLFetchNameValueDef(papszOpenOptionsIn, "BATCH_INSERT", "YES"));
     115          35 :     bCopyMode = CPLTestBool(
     116             :         CSLFetchNameValueDef(papszOpenOptionsIn, "COPY_MODE", "YES"));
     117          35 :     if (bCopyMode)
     118          29 :         bBatchInsert = TRUE;
     119             : 
     120          35 :     if (CSLFetchNameValue(papszOpenOptionsIn, "ACCOUNT"))
     121           0 :         pszAccount =
     122           0 :             CPLStrdup(CSLFetchNameValue(papszOpenOptionsIn, "ACCOUNT"));
     123             :     else
     124             :     {
     125          35 :         if (STARTS_WITH_CI(pszFilename, "CARTODB:"))
     126           0 :             pszAccount = CPLStrdup(pszFilename + strlen("CARTODB:"));
     127             :         else
     128          35 :             pszAccount = CPLStrdup(pszFilename + strlen("CARTO:"));
     129          35 :         char *pchSpace = strchr(pszAccount, ' ');
     130          35 :         if (pchSpace)
     131           0 :             *pchSpace = '\0';
     132          35 :         if (pszAccount[0] == 0)
     133             :         {
     134           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing account name");
     135           0 :             return FALSE;
     136             :         }
     137             :     }
     138             : 
     139             :     osAPIKey = CSLFetchNameValueDef(
     140             :         papszOpenOptionsIn, "API_KEY",
     141             :         CPLGetConfigOption("CARTO_API_KEY",
     142          35 :                            CPLGetConfigOption("CARTODB_API_KEY", "")));
     143             : 
     144          70 :     CPLString osTables = OGRCARTOGetOptionValue(pszFilename, "tables");
     145             : 
     146             :     /*if( osTables.empty() && osAPIKey.empty() )
     147             :     {
     148             :         CPLError(CE_Failure, CPLE_AppDefined,
     149             :                  "When not specifying tables option, CARTO_API_KEY must be
     150             :     defined"); return FALSE;
     151             :     }*/
     152             : 
     153          35 :     bUseHTTPS = CPLTestBool(CPLGetConfigOption(
     154             :         "CARTO_HTTPS", CPLGetConfigOption("CARTODB_HTTPS", "YES")));
     155             : 
     156          35 :     OGRLayer *poSchemaLayer = ExecuteSQLInternal("SELECT current_schema()");
     157          35 :     if (poSchemaLayer)
     158             :     {
     159          31 :         OGRFeature *poFeat = poSchemaLayer->GetNextFeature();
     160          31 :         if (poFeat)
     161             :         {
     162          20 :             if (poFeat->GetFieldCount() == 1)
     163             :             {
     164          19 :                 osCurrentSchema = poFeat->GetFieldAsString(0);
     165             :             }
     166          20 :             delete poFeat;
     167             :         }
     168          31 :         ReleaseResultSet(poSchemaLayer);
     169             :     }
     170          35 :     if (osCurrentSchema.empty())
     171          16 :         return FALSE;
     172             : 
     173             :     /* -------------------------------------------------------------------- */
     174             :     /*      Find out PostGIS version                                        */
     175             :     /* -------------------------------------------------------------------- */
     176          19 :     if (bReadWrite)
     177             :     {
     178             :         OGRLayer *poPostGISVersionLayer =
     179          10 :             ExecuteSQLInternal("SELECT postgis_version()");
     180          10 :         if (poPostGISVersionLayer)
     181             :         {
     182          10 :             OGRFeature *poFeat = poPostGISVersionLayer->GetNextFeature();
     183          10 :             if (poFeat)
     184             :             {
     185          10 :                 if (poFeat->GetFieldCount() == 1)
     186             :                 {
     187          10 :                     const char *pszVersion = poFeat->GetFieldAsString(0);
     188          10 :                     nPostGISMajor = atoi(pszVersion);
     189          10 :                     const char *pszDot = strchr(pszVersion, '.');
     190          10 :                     nPostGISMinor = 0;
     191          10 :                     if (pszDot)
     192          10 :                         nPostGISMinor = atoi(pszDot + 1);
     193             :                 }
     194          10 :                 delete poFeat;
     195             :             }
     196          10 :             ReleaseResultSet(poPostGISVersionLayer);
     197             :         }
     198             :     }
     199             : 
     200          19 :     if (!osAPIKey.empty() && bUpdateIn)
     201             :     {
     202          10 :         ExecuteSQLInternal(
     203             :             "DROP FUNCTION IF EXISTS ogr_table_metadata(TEXT,TEXT); "
     204             :             "CREATE OR REPLACE FUNCTION ogr_table_metadata(schema_name TEXT, "
     205             :             "table_name TEXT) RETURNS TABLE "
     206             :             "(attname TEXT, typname TEXT, attlen INT, format_type TEXT, "
     207             :             "attnum INT, attnotnull BOOLEAN, indisprimary BOOLEAN, "
     208             :             "defaultexpr TEXT, dim INT, srid INT, geomtyp TEXT, srtext TEXT) "
     209             :             "AS $$ "
     210             :             "SELECT a.attname::text, t.typname::text, a.attlen::int, "
     211             :             "format_type(a.atttypid,a.atttypmod)::text, "
     212             :             "a.attnum::int, "
     213             :             "a.attnotnull::boolean, "
     214             :             "i.indisprimary::boolean, "
     215             :             "pg_get_expr(def.adbin, c.oid)::text AS defaultexpr, "
     216             :             "(CASE WHEN t.typname = 'geometry' THEN "
     217             :             "postgis_typmod_dims(a.atttypmod) ELSE NULL END)::int dim, "
     218             :             "(CASE WHEN t.typname = 'geometry' THEN "
     219             :             "postgis_typmod_srid(a.atttypmod) ELSE NULL END)::int srid, "
     220             :             "(CASE WHEN t.typname = 'geometry' THEN "
     221             :             "postgis_typmod_type(a.atttypmod) ELSE NULL END)::text geomtyp, "
     222             :             "srtext "
     223             :             "FROM pg_class c "
     224             :             "JOIN pg_attribute a ON a.attnum > 0 AND "
     225             :             "a.attrelid = c.oid AND c.relname = $2 "
     226             :             "AND c.relname IN (SELECT CDB_UserTables())"
     227             :             "JOIN pg_type t ON a.atttypid = t.oid "
     228             :             "JOIN pg_namespace n ON c.relnamespace=n.oid AND n.nspname = $1 "
     229             :             "LEFT JOIN pg_index i ON c.oid = i.indrelid AND "
     230             :             "i.indisprimary = 't' AND a.attnum = ANY(i.indkey) "
     231             :             "LEFT JOIN pg_attrdef def ON def.adrelid = c.oid AND "
     232             :             "def.adnum = a.attnum "
     233             :             "LEFT JOIN spatial_ref_sys srs ON srs.srid = "
     234             :             "postgis_typmod_srid(a.atttypmod) "
     235             :             "ORDER BY a.attnum "
     236             :             "$$ LANGUAGE SQL");
     237             :     }
     238             : 
     239          19 :     if (!osTables.empty())
     240             :     {
     241           0 :         char **papszTables = CSLTokenizeString2(osTables, ",", 0);
     242           0 :         for (int i = 0; papszTables && papszTables[i]; i++)
     243             :         {
     244           0 :             papoLayers = (OGRCARTOTableLayer **)CPLRealloc(
     245           0 :                 papoLayers, (nLayers + 1) * sizeof(OGRCARTOTableLayer *));
     246           0 :             papoLayers[nLayers++] =
     247           0 :                 new OGRCARTOTableLayer(this, papszTables[i]);
     248             :         }
     249           0 :         CSLDestroy(papszTables);
     250           0 :         return TRUE;
     251             :     }
     252             : 
     253          19 :     OGRLayer *poTableListLayer = ExecuteSQLInternal("SELECT CDB_UserTables()");
     254          19 :     if (poTableListLayer)
     255             :     {
     256             :         OGRFeature *poFeat;
     257          35 :         while ((poFeat = poTableListLayer->GetNextFeature()) != nullptr)
     258             :         {
     259          17 :             if (poFeat->GetFieldCount() == 1)
     260             :             {
     261          34 :                 papoLayers = (OGRCARTOTableLayer **)CPLRealloc(
     262          17 :                     papoLayers, (nLayers + 1) * sizeof(OGRCARTOTableLayer *));
     263          17 :                 papoLayers[nLayers++] =
     264          17 :                     new OGRCARTOTableLayer(this, poFeat->GetFieldAsString(0));
     265             :             }
     266          17 :             delete poFeat;
     267             :         }
     268          18 :         ReleaseResultSet(poTableListLayer);
     269             :     }
     270           1 :     else if (osCurrentSchema == "public")
     271           1 :         return FALSE;
     272             : 
     273             :     /* There's currently a bug with CDB_UserTables() on multi-user accounts */
     274          18 :     if (nLayers == 0 && osCurrentSchema != "public")
     275             :     {
     276           1 :         CPLString osSQL;
     277             :         osSQL.Printf("SELECT c.relname FROM pg_class c, pg_namespace n "
     278             :                      "WHERE c.relkind in ('r', 'v') AND c.relname !~ '^pg_' "
     279             :                      "AND c.relnamespace=n.oid AND n.nspname = '%s'",
     280           1 :                      OGRCARTOEscapeLiteral(osCurrentSchema).c_str());
     281           1 :         poTableListLayer = ExecuteSQLInternal(osSQL);
     282           1 :         if (poTableListLayer)
     283             :         {
     284             :             OGRFeature *poFeat;
     285           2 :             while ((poFeat = poTableListLayer->GetNextFeature()) != nullptr)
     286             :             {
     287           1 :                 if (poFeat->GetFieldCount() == 1)
     288             :                 {
     289           2 :                     papoLayers = (OGRCARTOTableLayer **)CPLRealloc(
     290           1 :                         papoLayers,
     291           1 :                         (nLayers + 1) * sizeof(OGRCARTOTableLayer *));
     292           1 :                     papoLayers[nLayers++] = new OGRCARTOTableLayer(
     293           1 :                         this, poFeat->GetFieldAsString(0));
     294             :                 }
     295           1 :                 delete poFeat;
     296             :             }
     297           1 :             ReleaseResultSet(poTableListLayer);
     298             :         }
     299             :         else
     300           0 :             return FALSE;
     301             :     }
     302             : 
     303          18 :     return TRUE;
     304             : }
     305             : 
     306             : /************************************************************************/
     307             : /*                            GetAPIURL()                               */
     308             : /************************************************************************/
     309             : 
     310         340 : const char *OGRCARTODataSource::GetAPIURL() const
     311             : {
     312         340 :     const char *pszAPIURL = CPLGetConfigOption(
     313             :         "CARTO_API_URL", CPLGetConfigOption("CARTODB_API_URL", nullptr));
     314         340 :     if (pszAPIURL)
     315         340 :         return pszAPIURL;
     316           0 :     else if (bUseHTTPS)
     317           0 :         return CPLSPrintf("https://%s.carto.com/api/v2/sql", pszAccount);
     318             :     else
     319           0 :         return CPLSPrintf("http://%s.carto.com/api/v2/sql", pszAccount);
     320             : }
     321             : 
     322             : /************************************************************************/
     323             : /*                             FetchSRSId()                             */
     324             : /************************************************************************/
     325             : 
     326           2 : int OGRCARTODataSource::FetchSRSId(const OGRSpatialReference *poSRS)
     327             : 
     328             : {
     329             :     const char *pszAuthorityName;
     330             : 
     331           2 :     if (poSRS == nullptr)
     332           0 :         return 0;
     333             : 
     334           4 :     OGRSpatialReference oSRS(*poSRS);
     335             :     // cppcheck-suppress uselessAssignmentPtrArg
     336           2 :     poSRS = nullptr;
     337             : 
     338           2 :     pszAuthorityName = oSRS.GetAuthorityName(nullptr);
     339             : 
     340           2 :     if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
     341             :     {
     342             :         /* --------------------------------------------------------------------
     343             :          */
     344             :         /*      Try to identify an EPSG code */
     345             :         /* --------------------------------------------------------------------
     346             :          */
     347           0 :         oSRS.AutoIdentifyEPSG();
     348             : 
     349           0 :         pszAuthorityName = oSRS.GetAuthorityName(nullptr);
     350           0 :         if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
     351             :         {
     352           0 :             const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
     353           0 :             if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
     354             :             {
     355             :                 /* Import 'clean' SRS */
     356           0 :                 oSRS.importFromEPSG(atoi(pszAuthorityCode));
     357             : 
     358           0 :                 pszAuthorityName = oSRS.GetAuthorityName(nullptr);
     359             :             }
     360             :         }
     361             :     }
     362             :     /* -------------------------------------------------------------------- */
     363             :     /*      Check whether the EPSG authority code is already mapped to a    */
     364             :     /*      SRS ID.                                                         */
     365             :     /* -------------------------------------------------------------------- */
     366           2 :     if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
     367             :     {
     368             :         /* For the root authority name 'EPSG', the authority code
     369             :          * should always be integral
     370             :          */
     371           2 :         const int nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
     372             : 
     373           2 :         return nAuthorityCode;
     374             :     }
     375             : 
     376           0 :     return 0;
     377             : }
     378             : 
     379             : /************************************************************************/
     380             : /*                          ICreateLayer()                              */
     381             : /************************************************************************/
     382             : 
     383             : OGRLayer *
     384           6 : OGRCARTODataSource::ICreateLayer(const char *pszNameIn,
     385             :                                  const OGRGeomFieldDefn *poGeomFieldDefn,
     386             :                                  CSLConstList papszOptions)
     387             : {
     388           6 :     if (!bReadWrite)
     389             :     {
     390           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     391             :                  "Operation not available in read-only mode");
     392           1 :         return nullptr;
     393             :     }
     394             : 
     395           5 :     const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     396             :     const auto poSpatialRef =
     397           5 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     398             : 
     399             :     /* -------------------------------------------------------------------- */
     400             :     /*      Do we already have this layer?  If so, set it up for overwrite  */
     401             :     /*      away?                                                           */
     402             :     /* -------------------------------------------------------------------- */
     403             :     bool bOverwriteOption =
     404           7 :         CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
     405           2 :         !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO");
     406             : 
     407           9 :     for (int iLayer = 0; iLayer < nLayers; iLayer++)
     408             :     {
     409           5 :         if (EQUAL(pszNameIn, papoLayers[iLayer]->GetName()))
     410             :         {
     411           3 :             if (bOverwriteOption)
     412             :             {
     413             :                 /* We set DropOnCreation so the remote table isn't dropped */
     414             :                 /* As we are going to overwrite it in a single transaction */
     415           2 :                 papoLayers[iLayer]->SetDropOnCreation(true);
     416           2 :                 DeleteLayer(iLayer);
     417             :             }
     418             :             else
     419             :             {
     420           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     421             :                          "Layer %s already exists, CreateLayer failed.\n"
     422             :                          "Use the layer creation option OVERWRITE=YES to "
     423             :                          "replace it.",
     424             :                          pszNameIn);
     425           1 :                 return nullptr;
     426             :             }
     427             :         }
     428             :     }
     429             : 
     430           4 :     CPLString osName(pszNameIn);
     431           4 :     if (CPLFetchBool(papszOptions, "LAUNDER", true))
     432             :     {
     433           4 :         char *pszTmp = OGRPGCommonLaunderName(pszNameIn, "CARTO", false);
     434           4 :         osName = pszTmp;
     435           4 :         CPLFree(pszTmp);
     436             :     }
     437             : 
     438           4 :     OGRCARTOTableLayer *poLayer = new OGRCARTOTableLayer(this, osName);
     439           4 :     if (bOverwriteOption)
     440           2 :         poLayer->SetDropOnCreation(true);
     441             : 
     442             :     const bool bGeomNullable =
     443           4 :         CPLFetchBool(papszOptions, "GEOMETRY_NULLABLE", true);
     444           4 :     int nSRID = poSpatialRef ? FetchSRSId(poSpatialRef) : 0;
     445             :     bool bCartoify =
     446           8 :         CPLFetchBool(papszOptions, "CARTODBFY",
     447           4 :                      CPLFetchBool(papszOptions, "CARTODBIFY", true));
     448           4 :     if (bCartoify)
     449             :     {
     450           2 :         if (nSRID != 4326)
     451             :         {
     452           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     453             :                      "Cannot register table in dashboard with "
     454             :                      "cdb_cartodbfytable() since its SRS is not EPSG:4326."
     455             :                      " Check the documentation for more information");
     456           1 :             bCartoify = false;
     457             :         }
     458           1 :         else if (eGType == wkbNone)
     459             :         {
     460           0 :             CPLError(
     461             :                 CE_Warning, CPLE_AppDefined,
     462             :                 "Cannot register table in dashboard with "
     463             :                 "cdb_cartodbfytable() since its geometry type isn't defined."
     464             :                 " Check the documentation for more information");
     465           0 :             bCartoify = false;
     466             :         }
     467             :     }
     468             : 
     469           4 :     poLayer->SetLaunderFlag(CPLFetchBool(papszOptions, "LAUNDER", true));
     470             : 
     471           4 :     OGRSpatialReference *poSRSClone = nullptr;
     472           4 :     if (poSpatialRef)
     473             :     {
     474           1 :         poSRSClone = poSpatialRef->Clone();
     475           1 :         poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     476             :     }
     477           4 :     poLayer->SetDeferredCreation(eGType, poSRSClone, bGeomNullable, bCartoify);
     478           4 :     if (poSRSClone)
     479           1 :         poSRSClone->Release();
     480           8 :     papoLayers = (OGRCARTOTableLayer **)CPLRealloc(
     481           4 :         papoLayers, (nLayers + 1) * sizeof(OGRCARTOTableLayer *));
     482           4 :     papoLayers[nLayers++] = poLayer;
     483             : 
     484           4 :     return poLayer;
     485             : }
     486             : 
     487             : /************************************************************************/
     488             : /*                            DeleteLayer()                             */
     489             : /************************************************************************/
     490             : 
     491           5 : OGRErr OGRCARTODataSource::DeleteLayer(int iLayer)
     492             : {
     493           5 :     if (!bReadWrite)
     494             :     {
     495           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     496             :                  "Operation not available in read-only mode");
     497           1 :         return OGRERR_FAILURE;
     498             :     }
     499             : 
     500           4 :     if (iLayer < 0 || iLayer >= nLayers)
     501             :     {
     502           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     503             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
     504           0 :                  nLayers - 1);
     505           0 :         return OGRERR_FAILURE;
     506             :     }
     507             : 
     508             :     /* -------------------------------------------------------------------- */
     509             :     /*      Blow away our OGR structures related to the layer.  This is     */
     510             :     /*      pretty dangerous if anything has a reference to this layer!     */
     511             :     /* -------------------------------------------------------------------- */
     512           8 :     CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName();
     513             : 
     514           4 :     CPLDebug("CARTO", "DeleteLayer(%s)", osLayerName.c_str());
     515             : 
     516           4 :     int bDeferredCreation = papoLayers[iLayer]->GetDeferredCreation();
     517           4 :     bool bDropOnCreation = papoLayers[iLayer]->GetDropOnCreation();
     518           4 :     papoLayers[iLayer]->CancelDeferredCreation();
     519           4 :     delete papoLayers[iLayer];
     520           4 :     memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
     521           4 :             sizeof(void *) * (nLayers - iLayer - 1));
     522           4 :     nLayers--;
     523             : 
     524           4 :     if (osLayerName.empty())
     525           0 :         return OGRERR_NONE;
     526             : 
     527           4 :     if (!bDeferredCreation && !bDropOnCreation)
     528             :     {
     529           2 :         CPLString osSQL;
     530             :         osSQL.Printf("DROP TABLE %s",
     531           2 :                      OGRCARTOEscapeIdentifier(osLayerName).c_str());
     532             : 
     533           2 :         json_object *poObj = RunSQL(osSQL);
     534           2 :         if (poObj == nullptr)
     535           1 :             return OGRERR_FAILURE;
     536           1 :         json_object_put(poObj);
     537             :     }
     538             : 
     539           3 :     return OGRERR_NONE;
     540             : }
     541             : 
     542             : /************************************************************************/
     543             : /*                          AddHTTPOptions()                            */
     544             : /************************************************************************/
     545             : 
     546           0 : char **OGRCARTODataSource::AddHTTPOptions()
     547             : {
     548           0 :     bMustCleanPersistent = true;
     549             : 
     550           0 :     return CSLAddString(nullptr, CPLSPrintf("PERSISTENT=CARTO:%p", this));
     551             : }
     552             : 
     553             : /************************************************************************/
     554             : /*                               RunCopyFrom()                               */
     555             : /************************************************************************/
     556             : 
     557           2 : json_object *OGRCARTODataSource::RunCopyFrom(const char *pszSQL,
     558             :                                              const char *pszCopyFile)
     559             : {
     560             : 
     561             :     /* -------------------------------------------------------------------- */
     562             :     /*  Set up our copyfrom end point URL                                   */
     563             :     /* -------------------------------------------------------------------- */
     564           2 :     const char *pszAPIURL = GetAPIURL();
     565           4 :     CPLString osURL(pszAPIURL);
     566           2 :     osURL += "/copyfrom?q=";
     567             : 
     568           2 :     if (!(strlen(pszSQL) > 0))
     569             :     {
     570           0 :         CPLDebug("CARTO", "RunCopyFrom: pszSQL is empty");
     571           0 :         return nullptr;
     572             :     }
     573             : 
     574           2 :     if (!(strlen(pszCopyFile) > 0))
     575             :     {
     576           0 :         CPLDebug("CARTO", "RunCopyFrom: pszCopyFile is empty");
     577           0 :         return nullptr;
     578             :     }
     579             : 
     580             :     /* -------------------------------------------------------------------- */
     581             :     /*  URL encode the COPY sql and add to URL with API key                 */
     582             :     /* -------------------------------------------------------------------- */
     583           2 :     CPLDebug("CARTO", "RunCopyFrom: osCopySQL = %s", pszSQL);
     584           2 :     char *pszEscapedSQL = CPLEscapeString(pszSQL, -1, CPLES_URL);
     585           2 :     osURL += pszEscapedSQL;
     586           2 :     CPLFree(pszEscapedSQL);
     587             : 
     588           2 :     if (!osAPIKey.empty())
     589             :     {
     590           2 :         osURL += "&api_key=";
     591           2 :         osURL += osAPIKey;
     592             :     }
     593             : 
     594             :     /* -------------------------------------------------------------------- */
     595             :     /*  Set the POST payload                                                */
     596             :     /* -------------------------------------------------------------------- */
     597           4 :     CPLString osSQL("POSTFIELDS=");
     598           2 :     osSQL += pszCopyFile;
     599             : 
     600             :     /* -------------------------------------------------------------------- */
     601             :     /*  Make the HTTP request                                               */
     602             :     /* -------------------------------------------------------------------- */
     603           4 :     char **papszOptions = CSLAddString(
     604           2 :         !STARTS_WITH(pszAPIURL, "/vsimem/") ? AddHTTPOptions() : nullptr,
     605             :         osSQL);
     606           2 :     CPLHTTPResult *psResult = CPLHTTPFetch(osURL, papszOptions);
     607           2 :     CSLDestroy(papszOptions);
     608           2 :     if (psResult == nullptr)
     609             :     {
     610           0 :         CPLDebug("CARTO", "RunCopyFrom: null return from CPLHTTPFetch");
     611           0 :         return nullptr;
     612             :     }
     613             : 
     614             :     /* -------------------------------------------------------------------- */
     615             :     /*      Check for some error conditions and report.  HTML Messages      */
     616             :     /*      are transformed info failure.                                   */
     617             :     /* -------------------------------------------------------------------- */
     618           2 :     if (psResult->pszContentType &&
     619           0 :         STARTS_WITH(psResult->pszContentType, "text/html"))
     620             :     {
     621           0 :         CPLDebug("CARTO", "RunCopyFrom HTML Response:%s", psResult->pabyData);
     622           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     623             :                  "HTML error page returned by server");
     624           0 :         CPLHTTPDestroyResult(psResult);
     625           0 :         return nullptr;
     626             :     }
     627           2 :     if (psResult->pszErrBuf != nullptr)
     628             :     {
     629           1 :         CPLError(CE_Failure, CPLE_AppDefined, "RunCopyFrom Error Message:%s",
     630             :                  psResult->pszErrBuf);
     631             :     }
     632           1 :     else if (psResult->nStatus != 0)
     633             :     {
     634           0 :         CPLError(CE_Failure, CPLE_AppDefined, "RunCopyFrom Error Status:%d",
     635             :                  psResult->nStatus);
     636             :     }
     637             : 
     638           2 :     if (psResult->pabyData == nullptr)
     639             :     {
     640           1 :         CPLHTTPDestroyResult(psResult);
     641           1 :         return nullptr;
     642             :     }
     643             : 
     644           1 :     json_object *poObj = nullptr;
     645           1 :     const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
     646           1 :     if (!OGRJSonParse(pszText, &poObj, true))
     647             :     {
     648           0 :         CPLDebug("CARTO", "RunCopyFrom unable to parse JSON return: %s",
     649             :                  pszText);
     650           0 :         CPLHTTPDestroyResult(psResult);
     651           0 :         return nullptr;
     652             :     }
     653             : 
     654           1 :     CPLHTTPDestroyResult(psResult);
     655             : 
     656           1 :     if (poObj != nullptr)
     657             :     {
     658           1 :         if (json_object_get_type(poObj) == json_type_object)
     659             :         {
     660           1 :             json_object *poError = CPL_json_object_object_get(poObj, "error");
     661           1 :             if (poError != nullptr &&
     662           1 :                 json_object_get_type(poError) == json_type_array &&
     663           0 :                 json_object_array_length(poError) > 0)
     664             :             {
     665           0 :                 poError = json_object_array_get_idx(poError, 0);
     666           0 :                 if (poError != nullptr &&
     667           0 :                     json_object_get_type(poError) == json_type_string)
     668             :                 {
     669           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     670             :                              "Error returned by server : %s",
     671             :                              json_object_get_string(poError));
     672           0 :                     json_object_put(poObj);
     673           0 :                     return nullptr;
     674             :                 }
     675             :             }
     676             :         }
     677             :         else
     678             :         {
     679           0 :             json_object_put(poObj);
     680           0 :             return nullptr;
     681             :         }
     682             :     }
     683             : 
     684           1 :     return poObj;
     685             : }
     686             : 
     687             : /************************************************************************/
     688             : /*                               RunSQL()                               */
     689             : /************************************************************************/
     690             : 
     691         169 : json_object *OGRCARTODataSource::RunSQL(const char *pszUnescapedSQL)
     692             : {
     693         338 :     CPLString osSQL("POSTFIELDS=q=");
     694             :     /* Do post escaping */
     695       35235 :     for (int i = 0; pszUnescapedSQL[i] != 0; i++)
     696             :     {
     697       35066 :         const int ch = ((unsigned char *)pszUnescapedSQL)[i];
     698       35066 :         if (ch != '&' && ch >= 32 && ch < 128)
     699       35054 :             osSQL += (char)ch;
     700             :         else
     701          12 :             osSQL += CPLSPrintf("%%%02X", ch);
     702             :     }
     703             : 
     704             :     /* -------------------------------------------------------------------- */
     705             :     /*      Provide the API Key                                             */
     706             :     /* -------------------------------------------------------------------- */
     707         169 :     if (!osAPIKey.empty())
     708             :     {
     709         137 :         osSQL += "&api_key=";
     710         137 :         osSQL += osAPIKey;
     711             :     }
     712             : 
     713             :     /* -------------------------------------------------------------------- */
     714             :     /*      Collection the header options and execute request.              */
     715             :     /* -------------------------------------------------------------------- */
     716         169 :     const char *pszAPIURL = GetAPIURL();
     717         338 :     char **papszOptions = CSLAddString(
     718         169 :         !STARTS_WITH(pszAPIURL, "/vsimem/") ? AddHTTPOptions() : nullptr,
     719             :         osSQL);
     720         169 :     CPLHTTPResult *psResult = CPLHTTPFetch(GetAPIURL(), papszOptions);
     721         169 :     CSLDestroy(papszOptions);
     722         169 :     if (psResult == nullptr)
     723           0 :         return nullptr;
     724             : 
     725             :     /* -------------------------------------------------------------------- */
     726             :     /*      Check for some error conditions and report.  HTML Messages      */
     727             :     /*      are transformed info failure.                                   */
     728             :     /* -------------------------------------------------------------------- */
     729         169 :     if (psResult->pszContentType &&
     730           1 :         STARTS_WITH(psResult->pszContentType, "text/html"))
     731             :     {
     732           1 :         CPLDebug("CARTO", "RunSQL HTML Response:%s", psResult->pabyData);
     733           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     734             :                  "HTML error page returned by server");
     735           1 :         CPLHTTPDestroyResult(psResult);
     736           1 :         return nullptr;
     737             :     }
     738         168 :     if (psResult->pszErrBuf != nullptr)
     739             :     {
     740          35 :         CPLError(CE_Failure, CPLE_AppDefined, "RunSQL Error Message:%s",
     741             :                  psResult->pszErrBuf);
     742             :     }
     743         133 :     else if (psResult->nStatus != 0)
     744             :     {
     745           0 :         CPLError(CE_Failure, CPLE_AppDefined, "RunSQL Error Status:%d",
     746             :                  psResult->nStatus);
     747             :     }
     748             : 
     749         168 :     if (psResult->pabyData == nullptr)
     750             :     {
     751          48 :         CPLHTTPDestroyResult(psResult);
     752          48 :         return nullptr;
     753             :     }
     754             : 
     755         120 :     if (strlen((const char *)psResult->pabyData) < 1000)
     756         111 :         CPLDebug("CARTO", "RunSQL Response:%s", psResult->pabyData);
     757             : 
     758         120 :     json_object *poObj = nullptr;
     759         120 :     const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
     760         120 :     if (!OGRJSonParse(pszText, &poObj, true))
     761             :     {
     762           1 :         CPLHTTPDestroyResult(psResult);
     763           1 :         return nullptr;
     764             :     }
     765             : 
     766         119 :     CPLHTTPDestroyResult(psResult);
     767             : 
     768         119 :     if (poObj != nullptr)
     769             :     {
     770         119 :         if (json_object_get_type(poObj) == json_type_object)
     771             :         {
     772         118 :             json_object *poError = CPL_json_object_object_get(poObj, "error");
     773         119 :             if (poError != nullptr &&
     774         119 :                 json_object_get_type(poError) == json_type_array &&
     775           1 :                 json_object_array_length(poError) > 0)
     776             :             {
     777           1 :                 poError = json_object_array_get_idx(poError, 0);
     778           2 :                 if (poError != nullptr &&
     779           1 :                     json_object_get_type(poError) == json_type_string)
     780             :                 {
     781           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     782             :                              "Error returned by server : %s",
     783             :                              json_object_get_string(poError));
     784           1 :                     json_object_put(poObj);
     785           1 :                     return nullptr;
     786             :                 }
     787             :             }
     788             :         }
     789             :         else
     790             :         {
     791           1 :             json_object_put(poObj);
     792           1 :             return nullptr;
     793             :         }
     794             :     }
     795             : 
     796         117 :     return poObj;
     797             : }
     798             : 
     799             : /************************************************************************/
     800             : /*                        OGRCARTOGetSingleRow()                      */
     801             : /************************************************************************/
     802             : 
     803          30 : json_object *OGRCARTOGetSingleRow(json_object *poObj)
     804             : {
     805          30 :     if (poObj == nullptr)
     806             :     {
     807           8 :         return nullptr;
     808             :     }
     809             : 
     810          22 :     json_object *poRows = CPL_json_object_object_get(poObj, "rows");
     811          43 :     if (poRows == nullptr || json_object_get_type(poRows) != json_type_array ||
     812          21 :         json_object_array_length(poRows) != 1)
     813             :     {
     814           1 :         return nullptr;
     815             :     }
     816             : 
     817          21 :     json_object *poRowObj = json_object_array_get_idx(poRows, 0);
     818          42 :     if (poRowObj == nullptr ||
     819          21 :         json_object_get_type(poRowObj) != json_type_object)
     820             :     {
     821           0 :         return nullptr;
     822             :     }
     823             : 
     824          21 :     return poRowObj;
     825             : }
     826             : 
     827             : /************************************************************************/
     828             : /*                             ExecuteSQL()                             */
     829             : /************************************************************************/
     830             : 
     831           1 : OGRLayer *OGRCARTODataSource::ExecuteSQL(const char *pszSQLCommand,
     832             :                                          OGRGeometry *poSpatialFilter,
     833             :                                          const char *pszDialect)
     834             : 
     835             : {
     836           1 :     return ExecuteSQLInternal(pszSQLCommand, poSpatialFilter, pszDialect, true);
     837             : }
     838             : 
     839          91 : OGRLayer *OGRCARTODataSource::ExecuteSQLInternal(const char *pszSQLCommand,
     840             :                                                  OGRGeometry *poSpatialFilter,
     841             :                                                  const char *pszDialect,
     842             :                                                  bool bRunDeferredActions)
     843             : 
     844             : {
     845          91 :     if (bRunDeferredActions)
     846             :     {
     847           2 :         for (int iLayer = 0; iLayer < nLayers; iLayer++)
     848             :         {
     849           1 :             papoLayers[iLayer]->RunDeferredCreationIfNecessary();
     850           1 :             CPL_IGNORE_RET_VAL(papoLayers[iLayer]->FlushDeferredBuffer());
     851           1 :             papoLayers[iLayer]->RunDeferredCartofy();
     852             :         }
     853             :     }
     854             : 
     855             :     /* Skip leading spaces */
     856          91 :     while (*pszSQLCommand == ' ')
     857           0 :         pszSQLCommand++;
     858             : 
     859             :     /* -------------------------------------------------------------------- */
     860             :     /*      Use generic implementation for recognized dialects              */
     861             :     /* -------------------------------------------------------------------- */
     862          91 :     if (IsGenericSQLDialect(pszDialect))
     863           0 :         return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
     864           0 :                                        pszDialect);
     865             : 
     866             :     /* -------------------------------------------------------------------- */
     867             :     /*      Special case DELLAYER: command.                                 */
     868             :     /* -------------------------------------------------------------------- */
     869          91 :     if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
     870             :     {
     871           1 :         const char *pszLayerName = pszSQLCommand + 9;
     872             : 
     873           1 :         while (*pszLayerName == ' ')
     874           0 :             pszLayerName++;
     875             : 
     876           1 :         for (int iLayer = 0; iLayer < nLayers; iLayer++)
     877             :         {
     878           1 :             if (EQUAL(papoLayers[iLayer]->GetName(), pszLayerName))
     879             :             {
     880           1 :                 DeleteLayer(iLayer);
     881           1 :                 break;
     882             :             }
     883             :         }
     884           1 :         return nullptr;
     885             :     }
     886             : 
     887          90 :     if (!STARTS_WITH_CI(pszSQLCommand, "SELECT") &&
     888          10 :         !STARTS_WITH_CI(pszSQLCommand, "EXPLAIN") &&
     889          10 :         !STARTS_WITH_CI(pszSQLCommand, "WITH"))
     890             :     {
     891          10 :         RunSQL(pszSQLCommand);
     892          10 :         return nullptr;
     893             :     }
     894             : 
     895          80 :     OGRCARTOResultLayer *poLayer = new OGRCARTOResultLayer(this, pszSQLCommand);
     896             : 
     897          80 :     if (poSpatialFilter != nullptr)
     898           0 :         poLayer->SetSpatialFilter(poSpatialFilter);
     899             : 
     900          80 :     if (!poLayer->IsOK())
     901             :     {
     902           9 :         delete poLayer;
     903           9 :         return nullptr;
     904             :     }
     905             : 
     906          71 :     return poLayer;
     907             : }
     908             : 
     909             : /************************************************************************/
     910             : /*                          ReleaseResultSet()                          */
     911             : /************************************************************************/
     912             : 
     913          71 : void OGRCARTODataSource::ReleaseResultSet(OGRLayer *poLayer)
     914             : 
     915             : {
     916          71 :     delete poLayer;
     917          71 : }

Generated by: LCOV version 1.14