Coverage Report

Created: 2026-11-12 08:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/amigocloud/ogramigoclouddatasource.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  AmigoCloud Translator
4
 * Purpose:  Implements OGRAmigoCloudDataSource class
5
 * Author:   Victor Chernetsky, <victor at amigocloud dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2015, Victor Chernetsky, <victor at amigocloud dot com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_multiproc.h"  // CPLSleep()
14
15
#include "ogr_amigocloud.h"
16
#include "ogr_pgdump.h"
17
#include "ogrlibjsonutils.h"
18
#include <sstream>
19
20
CPLString OGRAMIGOCLOUDGetOptionValue(const char *pszFilename,
21
                                      const char *pszOptionName);
22
23
/************************************************************************/
24
/*                      OGRAmigoCloudDataSource()                       */
25
/************************************************************************/
26
27
OGRAmigoCloudDataSource::OGRAmigoCloudDataSource()
28
324
    : pszProjectId(nullptr), papoLayers(nullptr), nLayers(0), bReadWrite(false),
29
324
      bUseHTTPS(true), bMustCleanPersistent(false), bHasOGRMetadataFunction(-1)
30
324
{
31
324
}
32
33
/************************************************************************/
34
/*                      ~OGRAmigoCloudDataSource()                      */
35
/************************************************************************/
36
37
OGRAmigoCloudDataSource::~OGRAmigoCloudDataSource()
38
39
324
{
40
324
    for (int i = 0; i < nLayers; i++)
41
0
        delete papoLayers[i];
42
324
    CPLFree(papoLayers);
43
44
324
    if (bMustCleanPersistent)
45
0
    {
46
0
        char **papszOptions = nullptr;
47
0
        papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT",
48
0
                                       CPLSPrintf("AMIGOCLOUD:%p", this));
49
0
        papszOptions = CSLAddString(papszOptions, GetUserAgentOption().c_str());
50
0
        CPLHTTPDestroyResult(CPLHTTPFetch(GetAPIURL(), papszOptions));
51
0
        CSLDestroy(papszOptions);
52
0
    }
53
54
324
    CPLFree(pszProjectId);
55
324
}
56
57
std::string OGRAmigoCloudDataSource::GetUserAgentOption()
58
152
{
59
152
    std::stringstream userAgent;
60
152
    userAgent << "USERAGENT=gdal/AmigoCloud build:"
61
152
              << GDALVersionInfo("RELEASE_NAME");
62
152
    return userAgent.str();
63
152
}
64
65
/************************************************************************/
66
/*                           TestCapability()                           */
67
/************************************************************************/
68
69
bool OGRAmigoCloudDataSource::TestCapability(const char *pszCap) const
70
71
0
{
72
0
    if (bReadWrite && EQUAL(pszCap, ODsCCreateLayer) && nLayers == 0)
73
0
        return TRUE;
74
0
    else if (bReadWrite && EQUAL(pszCap, ODsCDeleteLayer))
75
0
        return TRUE;
76
0
    else if (EQUAL(pszCap, ODsCRandomLayerWrite))
77
0
        return bReadWrite;
78
0
    else if (EQUAL(pszCap, ODsCZGeometries))
79
0
        return TRUE;
80
0
    else
81
0
        return FALSE;
82
0
}
83
84
/************************************************************************/
85
/*                              GetLayer()                              */
86
/************************************************************************/
87
88
const OGRLayer *OGRAmigoCloudDataSource::GetLayer(int iLayer) const
89
0
{
90
0
    if (iLayer < 0 || iLayer >= nLayers)
91
0
        return nullptr;
92
0
    else
93
0
        return papoLayers[iLayer];
94
0
}
95
96
/************************************************************************/
97
/*                           GetLayerByName()                           */
98
/************************************************************************/
99
100
OGRLayer *OGRAmigoCloudDataSource::GetLayerByName(const char *pszLayerName)
101
0
{
102
0
    if (nLayers > 1)
103
0
    {
104
0
        return GDALDataset::GetLayerByName(pszLayerName);
105
0
    }
106
0
    else if (nLayers == 1)
107
0
    {
108
0
        return papoLayers[0];
109
0
    }
110
0
    return nullptr;
111
0
}
112
113
/************************************************************************/
114
/*                    OGRAMIGOCLOUDGetOptionValue()                     */
115
/************************************************************************/
116
117
CPLString OGRAMIGOCLOUDGetOptionValue(const char *pszFilename,
118
                                      const char *pszOptionName)
119
324
{
120
324
    CPLString osOptionName(pszOptionName);
121
324
    osOptionName += "=";
122
324
    const char *pszOptionValue = strstr(pszFilename, osOptionName);
123
324
    if (!pszOptionValue)
124
172
        return "";
125
126
152
    CPLString osOptionValue(pszOptionValue + osOptionName.size());
127
152
    const char *pszSpace = strchr(osOptionValue.c_str(), ' ');
128
152
    if (pszSpace)
129
57
        osOptionValue.resize(pszSpace - osOptionValue.c_str());
130
152
    return osOptionValue;
131
324
}
132
133
bool OGRAmigoCloudDataSource::ListDatasets()
134
0
{
135
0
    std::stringstream url;
136
0
    url << std::string(GetAPIURL()) << "/users/0/projects/"
137
0
        << std::string(GetProjectId()) << "/datasets/?summary";
138
0
    json_object *result = RunGET(url.str().c_str());
139
0
    if (result == nullptr)
140
0
    {
141
0
        CPLError(CE_Failure, CPLE_AppDefined, "AmigoCloud:get failed.");
142
0
        return false;
143
0
    }
144
145
0
    {
146
0
        auto type = json_object_get_type(result);
147
0
        if (type == json_type_object)
148
0
        {
149
0
            json_object *poResults =
150
0
                CPL_json_object_object_get(result, "results");
151
0
            if (poResults != nullptr &&
152
0
                json_object_get_type(poResults) == json_type_array)
153
0
            {
154
0
                CPLprintf("List of available datasets for project id: %s\n",
155
0
                          GetProjectId());
156
0
                CPLprintf("| id \t | name\n");
157
0
                CPLprintf("|--------|-------------------\n");
158
0
                const auto nSize = json_object_array_length(poResults);
159
0
                for (auto i = decltype(nSize){0}; i < nSize; ++i)
160
0
                {
161
0
                    json_object *ds = json_object_array_get_idx(poResults, i);
162
0
                    if (ds != nullptr)
163
0
                    {
164
0
                        const char *name = nullptr;
165
0
                        int64_t dataset_id = 0;
166
0
                        json_object *poName =
167
0
                            CPL_json_object_object_get(ds, "name");
168
0
                        if (poName != nullptr)
169
0
                        {
170
0
                            name = json_object_get_string(poName);
171
0
                        }
172
0
                        json_object *poId =
173
0
                            CPL_json_object_object_get(ds, "id");
174
0
                        if (poId != nullptr)
175
0
                        {
176
0
                            dataset_id = json_object_get_int64(poId);
177
0
                        }
178
0
                        if (name != nullptr)
179
0
                        {
180
0
                            std::stringstream str;
181
0
                            str << "| " << dataset_id << "\t | " << name;
182
0
                            CPLprintf("%s\n", str.str().c_str());
183
0
                        }
184
0
                    }
185
0
                }
186
0
            }
187
0
        }
188
0
        json_object_put(result);
189
0
    }
190
0
    return true;
191
0
}
192
193
/************************************************************************/
194
/*                                Open()                                */
195
/************************************************************************/
196
197
int OGRAmigoCloudDataSource::Open(const char *pszFilename,
198
                                  char **papszOpenOptionsIn, int bUpdateIn)
199
200
324
{
201
202
324
    bReadWrite = CPL_TO_BOOL(bUpdateIn);
203
204
324
    pszProjectId = CPLStrdup(pszFilename + strlen("AMIGOCLOUD:"));
205
324
    char *pchSpace = strchr(pszProjectId, ' ');
206
324
    if (pchSpace)
207
168
        *pchSpace = '\0';
208
324
    if (pszProjectId[0] == 0)
209
0
    {
210
0
        CPLError(CE_Failure, CPLE_AppDefined, "Missing project id");
211
0
        return FALSE;
212
0
    }
213
214
324
    osAPIKey =
215
324
        CSLFetchNameValueDef(papszOpenOptionsIn, "AMIGOCLOUD_API_KEY",
216
324
                             CPLGetConfigOption("AMIGOCLOUD_API_KEY", ""));
217
218
324
    if (osAPIKey.empty())
219
324
    {
220
324
        osAPIKey =
221
324
            OGRAMIGOCLOUDGetOptionValue(pszFilename, "AMIGOCLOUD_API_KEY");
222
324
    }
223
324
    if (osAPIKey.empty())
224
172
    {
225
172
        CPLError(CE_Failure, CPLE_AppDefined,
226
172
                 "AMIGOCLOUD_API_KEY is not defined.");
227
172
        return FALSE;
228
172
    }
229
230
152
    OGRLayer *poSchemaLayer = ExecuteSQLInternal("SELECT current_schema()");
231
152
    if (poSchemaLayer)
232
0
    {
233
0
        OGRFeature *poFeat = poSchemaLayer->GetNextFeature();
234
0
        if (poFeat)
235
0
        {
236
0
            if (poFeat->GetFieldCount() == 1)
237
0
            {
238
0
                osCurrentSchema = poFeat->GetFieldAsString(0);
239
0
            }
240
0
            delete poFeat;
241
0
        }
242
0
        ReleaseResultSet(poSchemaLayer);
243
0
    }
244
152
    if (osCurrentSchema.empty())
245
152
        return FALSE;
246
247
0
    CPLString osDatasets = OGRAMIGOCLOUDGetOptionValue(pszFilename, "datasets");
248
0
    if (!osDatasets.empty())
249
0
    {
250
0
        char **papszTables = CSLTokenizeString2(osDatasets, ",", 0);
251
0
        for (int i = 0; papszTables && papszTables[i]; i++)
252
0
        {
253
254
0
            papoLayers = (OGRAmigoCloudTableLayer **)CPLRealloc(
255
0
                papoLayers, (nLayers + 1) * sizeof(OGRAmigoCloudTableLayer *));
256
257
0
            papoLayers[nLayers++] =
258
0
                new OGRAmigoCloudTableLayer(this, papszTables[i]);
259
0
        }
260
0
        CSLDestroy(papszTables);
261
262
        // If OVERWRITE: YES, truncate the layer.
263
0
        if (nLayers == 1 &&
264
0
            CPLFetchBool(papszOpenOptionsIn, "OVERWRITE", false))
265
0
        {
266
0
            TruncateDataset(papoLayers[0]->GetTableName());
267
0
        }
268
0
        return TRUE;
269
0
    }
270
0
    else
271
0
    {
272
        // If 'datasets' word is in the filename, but no dataset id specified,
273
        // print the list of available datasets
274
0
        if (std::string(pszFilename).find("datasets") != std::string::npos)
275
0
            ListDatasets();
276
0
    }
277
278
0
    return TRUE;
279
0
}
280
281
/************************************************************************/
282
/*                             GetAPIURL()                              */
283
/************************************************************************/
284
285
const char *OGRAmigoCloudDataSource::GetAPIURL() const
286
152
{
287
152
    const char *pszAPIURL = CPLGetConfigOption("AMIGOCLOUD_API_URL", nullptr);
288
152
    if (pszAPIURL)
289
0
        return pszAPIURL;
290
291
152
    else if (bUseHTTPS)
292
152
        return CPLSPrintf("https://app.amigocloud.com/api/v1");
293
0
    else
294
0
        return CPLSPrintf("http://app.amigocloud.com/api/v1");
295
152
}
296
297
/************************************************************************/
298
/*                             FetchSRSId()                             */
299
/************************************************************************/
300
301
int OGRAmigoCloudDataSource::FetchSRSId(OGRSpatialReference *poSRS)
302
303
0
{
304
0
    if (poSRS == nullptr)
305
0
        return 0;
306
307
0
    OGRSpatialReference oSRS(*poSRS);
308
    // cppcheck-suppress uselessAssignmentPtrArg
309
0
    poSRS = nullptr;
310
311
0
    const char *pszAuthorityName = oSRS.GetAuthorityName();
312
313
0
    if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
314
0
    {
315
        /* --------------------------------------------------------------------
316
         */
317
        /*      Try to identify an EPSG code */
318
        /* --------------------------------------------------------------------
319
         */
320
0
        oSRS.AutoIdentifyEPSG();
321
322
0
        pszAuthorityName = oSRS.GetAuthorityName();
323
0
        if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
324
0
        {
325
0
            const char *pszAuthorityCode = oSRS.GetAuthorityCode();
326
0
            if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
327
0
            {
328
                /* Import 'clean' SRS */
329
0
                oSRS.importFromEPSG(atoi(pszAuthorityCode));
330
331
0
                pszAuthorityName = oSRS.GetAuthorityName();
332
0
            }
333
0
        }
334
0
    }
335
    /* -------------------------------------------------------------------- */
336
    /*      Check whether the EPSG authority code is already mapped to a    */
337
    /*      SRS ID.                                                         */
338
    /* -------------------------------------------------------------------- */
339
0
    if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
340
0
    {
341
        /* For the root authority name 'EPSG', the authority code
342
         * should always be integral
343
         */
344
0
        const int nAuthorityCode = atoi(oSRS.GetAuthorityCode());
345
346
0
        return nAuthorityCode;
347
0
    }
348
349
0
    return 0;
350
0
}
351
352
/************************************************************************/
353
/*                            ICreateLayer()                            */
354
/************************************************************************/
355
356
OGRLayer *
357
OGRAmigoCloudDataSource::ICreateLayer(const char *pszNameIn,
358
                                      const OGRGeomFieldDefn *poGeomFieldDefn,
359
                                      CSLConstList papszOptions)
360
0
{
361
0
    if (!bReadWrite)
362
0
    {
363
0
        CPLError(CE_Failure, CPLE_AppDefined,
364
0
                 "Operation not available in read-only mode");
365
0
        return nullptr;
366
0
    }
367
368
0
    const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
369
0
    const auto poSpatialRef =
370
0
        poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
371
372
0
    CPLString osName(pszNameIn);
373
0
    OGRAmigoCloudTableLayer *poLayer =
374
0
        new OGRAmigoCloudTableLayer(this, osName);
375
0
    const bool bGeomNullable =
376
0
        CPLFetchBool(papszOptions, "GEOMETRY_NULLABLE", true);
377
0
    OGRSpatialReference *poSRSClone = nullptr;
378
0
    if (poSpatialRef)
379
0
    {
380
0
        poSRSClone = poSpatialRef->Clone();
381
0
        poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
382
0
    }
383
0
    poLayer->SetDeferredCreation(eGType, poSRSClone, bGeomNullable);
384
0
    if (poSRSClone)
385
0
        poSRSClone->Release();
386
0
    papoLayers = (OGRAmigoCloudTableLayer **)CPLRealloc(
387
0
        papoLayers, (nLayers + 1) * sizeof(OGRAmigoCloudTableLayer *));
388
0
    papoLayers[nLayers++] = poLayer;
389
390
0
    return poLayer;
391
0
}
392
393
/************************************************************************/
394
/*                            DeleteLayer()                             */
395
/************************************************************************/
396
397
OGRErr OGRAmigoCloudDataSource::DeleteLayer(int iLayer)
398
0
{
399
0
    if (!bReadWrite)
400
0
    {
401
0
        CPLError(CE_Failure, CPLE_AppDefined,
402
0
                 "Operation not available in read-only mode");
403
0
        return OGRERR_FAILURE;
404
0
    }
405
406
0
    if (iLayer < 0 || iLayer >= nLayers)
407
0
    {
408
0
        CPLError(CE_Failure, CPLE_AppDefined,
409
0
                 "Layer %d not in legal range of 0 to %d.", iLayer,
410
0
                 nLayers - 1);
411
0
        return OGRERR_FAILURE;
412
0
    }
413
414
    /* -------------------------------------------------------------------- */
415
    /*      Blow away our OGR structures related to the layer.  This is     */
416
    /*      pretty dangerous if anything has a reference to this layer!     */
417
    /* -------------------------------------------------------------------- */
418
0
    CPLString osDatasetId = papoLayers[iLayer]->GetDatasetId();
419
420
0
    CPLDebug("AMIGOCLOUD", "DeleteLayer(%s)", osDatasetId.c_str());
421
422
0
    int bDeferredCreation = papoLayers[iLayer]->GetDeferredCreation();
423
0
    papoLayers[iLayer]->CancelDeferredCreation();
424
0
    delete papoLayers[iLayer];
425
0
    memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
426
0
            sizeof(void *) * (nLayers - iLayer - 1));
427
0
    nLayers--;
428
429
0
    if (osDatasetId.empty())
430
0
        return OGRERR_NONE;
431
432
0
    if (!bDeferredCreation)
433
0
    {
434
0
        std::stringstream url;
435
0
        url << std::string(GetAPIURL())
436
0
            << "/users/0/projects/" + std::string(GetProjectId()) +
437
0
                   "/datasets/" + osDatasetId.c_str();
438
0
        if (!RunDELETE(url.str().c_str()))
439
0
        {
440
0
            return OGRERR_FAILURE;
441
0
        }
442
0
    }
443
444
0
    return OGRERR_NONE;
445
0
}
446
447
/************************************************************************/
448
/*                           AddHTTPOptions()                           */
449
/************************************************************************/
450
451
char **OGRAmigoCloudDataSource::AddHTTPOptions()
452
0
{
453
0
    bMustCleanPersistent = true;
454
455
0
    return CSLAddString(nullptr, CPLSPrintf("PERSISTENT=AMIGOCLOUD:%p", this));
456
0
}
457
458
/************************************************************************/
459
/*                              RunPOST()                               */
460
/************************************************************************/
461
462
json_object *OGRAmigoCloudDataSource::RunPOST(const char *pszURL,
463
                                              const char *pszPostData,
464
                                              const char *pszHeaders)
465
0
{
466
0
    CPLString osURL(pszURL);
467
468
    /* -------------------------------------------------------------------- */
469
    /*      Provide the API Key                                             */
470
    /* -------------------------------------------------------------------- */
471
0
    if (!osAPIKey.empty())
472
0
    {
473
0
        if (osURL.find("?") == std::string::npos)
474
0
            osURL += "?token=";
475
0
        else
476
0
            osURL += "&token=";
477
0
        osURL += osAPIKey;
478
0
    }
479
480
0
    char **papszOptions = nullptr;
481
0
    CPLString osPOSTFIELDS("POSTFIELDS=");
482
0
    if (pszPostData)
483
0
        osPOSTFIELDS += pszPostData;
484
0
    papszOptions = CSLAddString(papszOptions, osPOSTFIELDS);
485
0
    papszOptions = CSLAddString(papszOptions, pszHeaders);
486
0
    papszOptions = CSLAddString(papszOptions, GetUserAgentOption().c_str());
487
488
0
    CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), papszOptions);
489
0
    CSLDestroy(papszOptions);
490
0
    if (psResult == nullptr)
491
0
        return nullptr;
492
493
0
    if (psResult->pszContentType &&
494
0
        strncmp(psResult->pszContentType, "text/html", 9) == 0)
495
0
    {
496
0
        CPLDebug("AMIGOCLOUD", "RunPOST HTML Response: %s", psResult->pabyData);
497
0
        CPLError(CE_Failure, CPLE_AppDefined,
498
0
                 "HTML error page returned by server: %s", psResult->pabyData);
499
0
        CPLHTTPDestroyResult(psResult);
500
0
        return nullptr;
501
0
    }
502
0
    if (psResult->pszErrBuf != nullptr && psResult->pabyData != nullptr)
503
0
    {
504
0
        CPLError(CE_Failure, CPLE_AppDefined, "POST Response: %s",
505
0
                 psResult->pabyData);
506
0
    }
507
0
    else if (psResult->nStatus != 0)
508
0
    {
509
0
        CPLDebug("AMIGOCLOUD", "RunPOST Error Status:%d", psResult->nStatus);
510
0
    }
511
512
0
    if (psResult->pabyData == nullptr)
513
0
    {
514
0
        CPLHTTPDestroyResult(psResult);
515
0
        return nullptr;
516
0
    }
517
518
0
    json_object *poObj = nullptr;
519
0
    const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
520
0
    if (!OGRJSonParse(pszText, &poObj, true))
521
0
    {
522
0
        CPLHTTPDestroyResult(psResult);
523
0
        return nullptr;
524
0
    }
525
526
0
    CPLHTTPDestroyResult(psResult);
527
528
0
    if (poObj != nullptr)
529
0
    {
530
0
        if (json_object_get_type(poObj) == json_type_object)
531
0
        {
532
0
            json_object *poError = CPL_json_object_object_get(poObj, "error");
533
0
            if (poError != nullptr &&
534
0
                json_object_get_type(poError) == json_type_array &&
535
0
                json_object_array_length(poError) > 0)
536
0
            {
537
0
                poError = json_object_array_get_idx(poError, 0);
538
0
                if (poError != nullptr &&
539
0
                    json_object_get_type(poError) == json_type_string)
540
0
                {
541
0
                    CPLError(CE_Failure, CPLE_AppDefined,
542
0
                             "Error returned by server : %s",
543
0
                             json_object_get_string(poError));
544
0
                    json_object_put(poObj);
545
0
                    return nullptr;
546
0
                }
547
0
            }
548
0
            json_object *poJob = CPL_json_object_object_get(poObj, "job");
549
0
            if (poJob != nullptr)
550
0
            {
551
0
                const char *job = json_object_get_string(poJob);
552
0
                if (job != nullptr)
553
0
                {
554
0
                    waitForJobToFinish(job);
555
0
                }
556
0
            }
557
0
        }
558
0
        else
559
0
        {
560
0
            json_object_put(poObj);
561
0
            return nullptr;
562
0
        }
563
0
    }
564
565
0
    return poObj;
566
0
}
567
568
bool OGRAmigoCloudDataSource::waitForJobToFinish(const char *jobId)
569
0
{
570
0
    std::stringstream url;
571
0
    url << std::string(GetAPIURL()) << "/me/jobs/" << std::string(jobId);
572
0
    int count = 0;
573
0
    while (count < 5)
574
0
    {
575
0
        count++;
576
0
        json_object *result = RunGET(url.str().c_str());
577
0
        if (result == nullptr)
578
0
        {
579
0
            CPLError(CE_Failure, CPLE_AppDefined, "waitForJobToFinish failed.");
580
0
            return false;
581
0
        }
582
583
0
        {
584
0
            int type = json_object_get_type(result);
585
0
            if (type == json_type_object)
586
0
            {
587
0
                json_object *poStatus =
588
0
                    CPL_json_object_object_get(result, "status");
589
0
                const char *status = json_object_get_string(poStatus);
590
0
                if (status != nullptr)
591
0
                {
592
0
                    if (std::string(status) == "SUCCESS")
593
0
                    {
594
0
                        return true;
595
0
                    }
596
0
                    else if (std::string(status) == "FAILURE")
597
0
                    {
598
0
                        CPLError(CE_Failure, CPLE_AppDefined, "Job failed : %s",
599
0
                                 json_object_get_string(result));
600
0
                        return false;
601
0
                    }
602
0
                }
603
0
            }
604
0
        }
605
0
        CPLSleep(1.0);  // Sleep 1 sec.
606
0
    }
607
0
    return false;
608
0
}
609
610
bool OGRAmigoCloudDataSource::TruncateDataset(const CPLString &tableName)
611
0
{
612
0
    std::stringstream changeset;
613
0
    changeset << "[{\"type\":\"DML\",\"entity\":\"" << tableName << "\",";
614
0
    changeset << "\"parent\":null,\"action\":\"TRUNCATE\",\"data\":null}]";
615
0
    SubmitChangeset(changeset.str());
616
0
    return true;
617
0
}
618
619
void OGRAmigoCloudDataSource::SubmitChangeset(const CPLString &json)
620
0
{
621
0
    std::stringstream url;
622
0
    url << std::string(GetAPIURL())
623
0
        << "/users/0/projects/" + std::string(GetProjectId()) +
624
0
               "/submit_changeset";
625
0
    std::stringstream changeset;
626
0
    changeset << "{\"changeset\":\"" << OGRAMIGOCLOUDJsonEncode(json) << "\"}";
627
0
    json_object *poObj = RunPOST(url.str().c_str(), changeset.str().c_str());
628
0
    if (poObj != nullptr)
629
0
        json_object_put(poObj);
630
0
}
631
632
/************************************************************************/
633
/*                             RunDELETE()                              */
634
/************************************************************************/
635
636
bool OGRAmigoCloudDataSource::RunDELETE(const char *pszURL)
637
0
{
638
0
    CPLString osURL(pszURL);
639
640
    /* -------------------------------------------------------------------- */
641
    /*      Provide the API Key                                             */
642
    /* -------------------------------------------------------------------- */
643
0
    if (!osAPIKey.empty())
644
0
    {
645
0
        if (osURL.find("?") == std::string::npos)
646
0
            osURL += "?token=";
647
0
        else
648
0
            osURL += "&token=";
649
0
        osURL += osAPIKey;
650
0
    }
651
652
0
    char **papszOptions = nullptr;
653
0
    CPLString osPOSTFIELDS("CUSTOMREQUEST=DELETE");
654
0
    papszOptions = CSLAddString(papszOptions, osPOSTFIELDS);
655
0
    papszOptions = CSLAddString(papszOptions, GetUserAgentOption().c_str());
656
657
0
    CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), papszOptions);
658
0
    CSLDestroy(papszOptions);
659
0
    if (psResult == nullptr)
660
0
        return false;
661
662
0
    if (psResult->pszContentType &&
663
0
        strncmp(psResult->pszContentType, "text/html", 9) == 0)
664
0
    {
665
0
        CPLDebug("AMIGOCLOUD", "RunDELETE HTML Response:%s",
666
0
                 psResult->pabyData);
667
0
        CPLError(CE_Failure, CPLE_AppDefined,
668
0
                 "HTML error page returned by server:%s", psResult->pabyData);
669
0
        CPLHTTPDestroyResult(psResult);
670
0
        return false;
671
0
    }
672
0
    if (psResult->pszErrBuf != nullptr && psResult->pabyData != nullptr)
673
0
    {
674
0
        CPLError(CE_Failure, CPLE_AppDefined, "DELETE Response: %s",
675
0
                 psResult->pabyData);
676
0
    }
677
0
    else if (psResult->nStatus != 0)
678
0
    {
679
0
        CPLDebug("AMIGOCLOUD", "DELETE Error Status:%d", psResult->nStatus);
680
0
    }
681
0
    CPLHTTPDestroyResult(psResult);
682
683
0
    return true;
684
0
}
685
686
/************************************************************************/
687
/*                               RunGET()                               */
688
/************************************************************************/
689
690
json_object *OGRAmigoCloudDataSource::RunGET(const char *pszURL)
691
152
{
692
152
    CPLString osURL(pszURL);
693
694
    /* -------------------------------------------------------------------- */
695
    /*      Provide the API Key                                             */
696
    /* -------------------------------------------------------------------- */
697
152
    if (!osAPIKey.empty())
698
152
    {
699
152
        if (osURL.find("?") == std::string::npos)
700
0
            osURL += "?token=";
701
152
        else
702
152
            osURL += "&token=";
703
152
        osURL += osAPIKey;
704
152
    }
705
152
    char **papszOptions = nullptr;
706
152
    papszOptions = CSLAddString(papszOptions, GetUserAgentOption().c_str());
707
708
152
    CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), papszOptions);
709
152
    CSLDestroy(papszOptions);
710
152
    if (psResult == nullptr)
711
0
    {
712
0
        return nullptr;
713
0
    }
714
715
152
    if (psResult->pszContentType &&
716
5
        strncmp(psResult->pszContentType, "text/html", 9) == 0)
717
5
    {
718
5
        CPLError(CE_Failure, CPLE_AppDefined,
719
5
                 "HTML error page returned by server:%s", psResult->pabyData);
720
5
        CPLHTTPDestroyResult(psResult);
721
5
        return nullptr;
722
5
    }
723
147
    if (psResult->pszErrBuf != nullptr && psResult->pabyData != nullptr)
724
0
    {
725
0
        CPLError(CE_Failure, CPLE_AppDefined, "GET Response: %s",
726
0
                 psResult->pabyData);
727
0
    }
728
147
    else if (psResult->nStatus != 0)
729
84
    {
730
84
        CPLDebug("AMIGOCLOUD", "RunGET Error Status:%d", psResult->nStatus);
731
84
    }
732
733
147
    if (psResult->pabyData == nullptr)
734
147
    {
735
147
        CPLHTTPDestroyResult(psResult);
736
147
        return nullptr;
737
147
    }
738
739
0
    CPLDebug("AMIGOCLOUD", "RunGET Response:%s", psResult->pabyData);
740
741
0
    json_object *poObj = nullptr;
742
0
    const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
743
0
    if (!OGRJSonParse(pszText, &poObj, true))
744
0
    {
745
0
        CPLHTTPDestroyResult(psResult);
746
0
        return nullptr;
747
0
    }
748
749
0
    CPLHTTPDestroyResult(psResult);
750
751
0
    if (poObj != nullptr)
752
0
    {
753
0
        if (json_object_get_type(poObj) == json_type_object)
754
0
        {
755
0
            json_object *poError = CPL_json_object_object_get(poObj, "error");
756
0
            if (poError != nullptr &&
757
0
                json_object_get_type(poError) == json_type_array &&
758
0
                json_object_array_length(poError) > 0)
759
0
            {
760
0
                poError = json_object_array_get_idx(poError, 0);
761
0
                if (poError != nullptr &&
762
0
                    json_object_get_type(poError) == json_type_string)
763
0
                {
764
0
                    CPLError(CE_Failure, CPLE_AppDefined,
765
0
                             "Error returned by server : %s",
766
0
                             json_object_get_string(poError));
767
0
                    json_object_put(poObj);
768
0
                    return nullptr;
769
0
                }
770
0
            }
771
0
        }
772
0
        else
773
0
        {
774
0
            json_object_put(poObj);
775
0
            return nullptr;
776
0
        }
777
0
    }
778
779
0
    return poObj;
780
0
}
781
782
/************************************************************************/
783
/*                               RunSQL()                               */
784
/************************************************************************/
785
786
json_object *OGRAmigoCloudDataSource::RunSQL(const char *pszUnescapedSQL)
787
152
{
788
152
    CPLString osSQL;
789
152
    std::string pszAPIURL = GetAPIURL();
790
152
    osSQL = pszAPIURL + "/users/0/projects/" + CPLString(pszProjectId) + "/sql";
791
152
    const std::string sql(pszUnescapedSQL);
792
152
    if (sql.find("DELETE") != std::string::npos ||
793
152
        sql.find("delete") != std::string::npos ||
794
152
        sql.find("INSERT") != std::string::npos ||
795
152
        sql.find("insert") != std::string::npos ||
796
152
        sql.find("UPDATE") != std::string::npos ||
797
152
        sql.find("update") != std::string::npos)
798
0
    {
799
0
        std::stringstream query;
800
0
        query << "{\"query\": \"" << OGRAMIGOCLOUDJsonEncode(pszUnescapedSQL)
801
0
              << "\"}";
802
0
        return RunPOST(osSQL.c_str(), query.str().c_str());
803
0
    }
804
152
    else
805
152
    {
806
152
        osSQL += "?query=";
807
152
        char *pszEscaped = CPLEscapeString(pszUnescapedSQL, -1, CPLES_URL);
808
152
        osSQL += pszEscaped;
809
152
        CPLFree(pszEscaped);
810
152
        return RunGET(osSQL.c_str());
811
152
    }
812
152
}
813
814
/************************************************************************/
815
/*                     OGRAMIGOCLOUDGetSingleRow()                      */
816
/************************************************************************/
817
818
json_object *OGRAMIGOCLOUDGetSingleRow(json_object *poObj)
819
0
{
820
0
    if (poObj == nullptr)
821
0
    {
822
0
        return nullptr;
823
0
    }
824
825
0
    json_object *poRows = CPL_json_object_object_get(poObj, "data");
826
0
    if (poRows == nullptr || json_object_get_type(poRows) != json_type_array ||
827
0
        json_object_array_length(poRows) != 1)
828
0
    {
829
0
        return nullptr;
830
0
    }
831
832
0
    json_object *poRowObj = json_object_array_get_idx(poRows, 0);
833
0
    if (poRowObj == nullptr ||
834
0
        json_object_get_type(poRowObj) != json_type_object)
835
0
    {
836
0
        return nullptr;
837
0
    }
838
839
0
    return poRowObj;
840
0
}
841
842
/************************************************************************/
843
/*                             ExecuteSQL()                             */
844
/************************************************************************/
845
846
OGRLayer *OGRAmigoCloudDataSource::ExecuteSQL(const char *pszSQLCommand,
847
                                              OGRGeometry *poSpatialFilter,
848
                                              const char *pszDialect)
849
850
0
{
851
    /* -------------------------------------------------------------------- */
852
    /*      Use generic implementation for recognized dialects              */
853
    /* -------------------------------------------------------------------- */
854
0
    if (IsGenericSQLDialect(pszDialect))
855
0
        return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
856
0
                                       pszDialect);
857
858
0
    return ExecuteSQLInternal(pszSQLCommand, poSpatialFilter, pszDialect, true);
859
0
}
860
861
OGRLayer *OGRAmigoCloudDataSource::ExecuteSQLInternal(
862
    const char *pszSQLCommand, OGRGeometry *poSpatialFilter, const char *,
863
    bool bRunDeferredActions)
864
865
152
{
866
152
    if (bRunDeferredActions)
867
0
    {
868
0
        for (int iLayer = 0; iLayer < nLayers; iLayer++)
869
0
        {
870
0
            papoLayers[iLayer]->RunDeferredCreationIfNecessary();
871
0
            papoLayers[iLayer]->FlushDeferredInsert();
872
0
        }
873
0
    }
874
875
    /* Skip leading spaces */
876
152
    while (*pszSQLCommand == ' ')
877
0
        pszSQLCommand++;
878
879
152
    if (!EQUALN(pszSQLCommand, "SELECT", strlen("SELECT")) &&
880
0
        !EQUALN(pszSQLCommand, "EXPLAIN", strlen("EXPLAIN")) &&
881
0
        !EQUALN(pszSQLCommand, "WITH", strlen("WITH")))
882
0
    {
883
0
        RunSQL(pszSQLCommand);
884
0
        return nullptr;
885
0
    }
886
887
152
    OGRAmigoCloudResultLayer *poLayer =
888
152
        new OGRAmigoCloudResultLayer(this, pszSQLCommand);
889
890
152
    if (poSpatialFilter != nullptr)
891
0
        poLayer->SetSpatialFilter(poSpatialFilter);
892
893
152
    if (!poLayer->IsOK())
894
152
    {
895
152
        delete poLayer;
896
152
        return nullptr;
897
152
    }
898
899
0
    return poLayer;
900
152
}
901
902
/************************************************************************/
903
/*                          ReleaseResultSet()                          */
904
/************************************************************************/
905
906
void OGRAmigoCloudDataSource::ReleaseResultSet(OGRLayer *poLayer)
907
908
0
{
909
0
    delete poLayer;
910
0
}