Coverage Report

Created: 2026-03-30 09:00

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