LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/carto - ogrcartodatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 304 392 77.6 %
Date: 2024-05-03 15:49:35 Functions: 17 19 89.5 %

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

Generated by: LCOV version 1.14