Line data Source code
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 0 : OGRAmigoCloudDataSource::OGRAmigoCloudDataSource()
28 : : pszProjectId(nullptr), papoLayers(nullptr), nLayers(0), bReadWrite(false),
29 0 : bUseHTTPS(true), bMustCleanPersistent(false), bHasOGRMetadataFunction(-1)
30 : {
31 0 : }
32 :
33 : /************************************************************************/
34 : /* ~OGRAmigoCloudDataSource() */
35 : /************************************************************************/
36 :
37 0 : OGRAmigoCloudDataSource::~OGRAmigoCloudDataSource()
38 :
39 : {
40 0 : for (int i = 0; i < nLayers; i++)
41 0 : delete papoLayers[i];
42 0 : CPLFree(papoLayers);
43 :
44 0 : if (bMustCleanPersistent)
45 : {
46 0 : char **papszOptions = nullptr;
47 0 : papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT",
48 : CPLSPrintf("AMIGOCLOUD:%p", this));
49 0 : papszOptions = CSLAddString(papszOptions, GetUserAgentOption().c_str());
50 0 : CPLHTTPDestroyResult(CPLHTTPFetch(GetAPIURL(), papszOptions));
51 0 : CSLDestroy(papszOptions);
52 : }
53 :
54 0 : CPLFree(pszProjectId);
55 0 : }
56 :
57 0 : std::string OGRAmigoCloudDataSource::GetUserAgentOption()
58 : {
59 0 : std::stringstream userAgent;
60 : userAgent << "USERAGENT=gdal/AmigoCloud build:"
61 0 : << GDALVersionInfo("RELEASE_NAME");
62 0 : return userAgent.str();
63 : }
64 :
65 : /************************************************************************/
66 : /* TestCapability() */
67 : /************************************************************************/
68 :
69 0 : int OGRAmigoCloudDataSource::TestCapability(const char *pszCap) const
70 :
71 : {
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 : else
81 0 : return FALSE;
82 : }
83 :
84 : /************************************************************************/
85 : /* GetLayer() */
86 : /************************************************************************/
87 :
88 0 : const OGRLayer *OGRAmigoCloudDataSource::GetLayer(int iLayer) const
89 : {
90 0 : if (iLayer < 0 || iLayer >= nLayers)
91 0 : return nullptr;
92 : else
93 0 : return papoLayers[iLayer];
94 : }
95 :
96 : /************************************************************************/
97 : /* GetLayerByName() */
98 : /************************************************************************/
99 :
100 0 : OGRLayer *OGRAmigoCloudDataSource::GetLayerByName(const char *pszLayerName)
101 : {
102 0 : if (nLayers > 1)
103 : {
104 0 : return GDALDataset::GetLayerByName(pszLayerName);
105 : }
106 0 : else if (nLayers == 1)
107 : {
108 0 : return papoLayers[0];
109 : }
110 0 : return nullptr;
111 : }
112 :
113 : /************************************************************************/
114 : /* OGRAMIGOCLOUDGetOptionValue() */
115 : /************************************************************************/
116 :
117 0 : CPLString OGRAMIGOCLOUDGetOptionValue(const char *pszFilename,
118 : const char *pszOptionName)
119 : {
120 0 : CPLString osOptionName(pszOptionName);
121 0 : osOptionName += "=";
122 0 : const char *pszOptionValue = strstr(pszFilename, osOptionName);
123 0 : if (!pszOptionValue)
124 0 : return "";
125 :
126 0 : CPLString osOptionValue(pszOptionValue + osOptionName.size());
127 0 : const char *pszSpace = strchr(osOptionValue.c_str(), ' ');
128 0 : if (pszSpace)
129 0 : osOptionValue.resize(pszSpace - osOptionValue.c_str());
130 0 : return osOptionValue;
131 : }
132 :
133 0 : bool OGRAmigoCloudDataSource::ListDatasets()
134 : {
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 : {
141 0 : CPLError(CE_Failure, CPLE_AppDefined, "AmigoCloud:get failed.");
142 0 : return false;
143 : }
144 :
145 : {
146 0 : auto type = json_object_get_type(result);
147 0 : if (type == json_type_object)
148 : {
149 : 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 : {
154 0 : CPLprintf("List of available datasets for project id: %s\n",
155 : 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 : {
161 0 : json_object *ds = json_object_array_get_idx(poResults, i);
162 0 : if (ds != nullptr)
163 : {
164 0 : const char *name = nullptr;
165 0 : int64_t dataset_id = 0;
166 : json_object *poName =
167 0 : CPL_json_object_object_get(ds, "name");
168 0 : if (poName != nullptr)
169 : {
170 0 : name = json_object_get_string(poName);
171 : }
172 : json_object *poId =
173 0 : CPL_json_object_object_get(ds, "id");
174 0 : if (poId != nullptr)
175 : {
176 0 : dataset_id = json_object_get_int64(poId);
177 : }
178 0 : if (name != nullptr)
179 : {
180 0 : std::stringstream str;
181 0 : str << "| " << dataset_id << "\t | " << name;
182 0 : CPLprintf("%s\n", str.str().c_str());
183 : }
184 : }
185 : }
186 : }
187 : }
188 0 : json_object_put(result);
189 : }
190 0 : return true;
191 : }
192 :
193 : /************************************************************************/
194 : /* Open() */
195 : /************************************************************************/
196 :
197 0 : int OGRAmigoCloudDataSource::Open(const char *pszFilename,
198 : char **papszOpenOptionsIn, int bUpdateIn)
199 :
200 : {
201 :
202 0 : bReadWrite = CPL_TO_BOOL(bUpdateIn);
203 :
204 0 : pszProjectId = CPLStrdup(pszFilename + strlen("AMIGOCLOUD:"));
205 0 : char *pchSpace = strchr(pszProjectId, ' ');
206 0 : if (pchSpace)
207 0 : *pchSpace = '\0';
208 0 : if (pszProjectId[0] == 0)
209 : {
210 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing project id");
211 0 : return FALSE;
212 : }
213 :
214 : osAPIKey =
215 : CSLFetchNameValueDef(papszOpenOptionsIn, "AMIGOCLOUD_API_KEY",
216 0 : CPLGetConfigOption("AMIGOCLOUD_API_KEY", ""));
217 :
218 0 : if (osAPIKey.empty())
219 : {
220 : osAPIKey =
221 0 : OGRAMIGOCLOUDGetOptionValue(pszFilename, "AMIGOCLOUD_API_KEY");
222 : }
223 0 : if (osAPIKey.empty())
224 : {
225 0 : CPLError(CE_Failure, CPLE_AppDefined,
226 : "AMIGOCLOUD_API_KEY is not defined.\n");
227 0 : return FALSE;
228 : }
229 :
230 0 : OGRLayer *poSchemaLayer = ExecuteSQLInternal("SELECT current_schema()");
231 0 : if (poSchemaLayer)
232 : {
233 0 : OGRFeature *poFeat = poSchemaLayer->GetNextFeature();
234 0 : if (poFeat)
235 : {
236 0 : if (poFeat->GetFieldCount() == 1)
237 : {
238 0 : osCurrentSchema = poFeat->GetFieldAsString(0);
239 : }
240 0 : delete poFeat;
241 : }
242 0 : ReleaseResultSet(poSchemaLayer);
243 : }
244 0 : if (osCurrentSchema.empty())
245 0 : return FALSE;
246 :
247 0 : CPLString osDatasets = OGRAMIGOCLOUDGetOptionValue(pszFilename, "datasets");
248 0 : if (!osDatasets.empty())
249 : {
250 0 : char **papszTables = CSLTokenizeString2(osDatasets, ",", 0);
251 0 : for (int i = 0; papszTables && papszTables[i]; i++)
252 : {
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 : }
260 0 : CSLDestroy(papszTables);
261 :
262 : // If OVERWRITE: YES, truncate the layer.
263 0 : if (nLayers == 1 &&
264 0 : CPLFetchBool(papszOpenOptionsIn, "OVERWRITE", false))
265 : {
266 0 : TruncateDataset(papoLayers[0]->GetTableName());
267 : }
268 0 : return TRUE;
269 : }
270 : else
271 : {
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 : }
277 :
278 0 : return TRUE;
279 : }
280 :
281 : /************************************************************************/
282 : /* GetAPIURL() */
283 : /************************************************************************/
284 :
285 0 : const char *OGRAmigoCloudDataSource::GetAPIURL() const
286 : {
287 0 : const char *pszAPIURL = CPLGetConfigOption("AMIGOCLOUD_API_URL", nullptr);
288 0 : if (pszAPIURL)
289 0 : return pszAPIURL;
290 :
291 0 : else if (bUseHTTPS)
292 0 : return CPLSPrintf("https://app.amigocloud.com/api/v1");
293 : else
294 0 : return CPLSPrintf("http://app.amigocloud.com/api/v1");
295 : }
296 :
297 : /************************************************************************/
298 : /* FetchSRSId() */
299 : /************************************************************************/
300 :
301 0 : int OGRAmigoCloudDataSource::FetchSRSId(OGRSpatialReference *poSRS)
302 :
303 : {
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(nullptr);
312 :
313 0 : if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
314 : {
315 : /* --------------------------------------------------------------------
316 : */
317 : /* Try to identify an EPSG code */
318 : /* --------------------------------------------------------------------
319 : */
320 0 : oSRS.AutoIdentifyEPSG();
321 :
322 0 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
323 0 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
324 : {
325 0 : const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
326 0 : if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
327 : {
328 : /* Import 'clean' SRS */
329 0 : oSRS.importFromEPSG(atoi(pszAuthorityCode));
330 :
331 0 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
332 : }
333 : }
334 : }
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 : {
341 : /* For the root authority name 'EPSG', the authority code
342 : * should always be integral
343 : */
344 0 : const int nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
345 :
346 0 : return nAuthorityCode;
347 : }
348 :
349 0 : return 0;
350 : }
351 :
352 : /************************************************************************/
353 : /* ICreateLayer() */
354 : /************************************************************************/
355 :
356 : OGRLayer *
357 0 : OGRAmigoCloudDataSource::ICreateLayer(const char *pszNameIn,
358 : const OGRGeomFieldDefn *poGeomFieldDefn,
359 : CSLConstList papszOptions)
360 : {
361 0 : if (!bReadWrite)
362 : {
363 0 : CPLError(CE_Failure, CPLE_AppDefined,
364 : "Operation not available in read-only mode");
365 0 : return nullptr;
366 : }
367 :
368 0 : const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
369 : const auto poSpatialRef =
370 0 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
371 :
372 0 : CPLString osName(pszNameIn);
373 : OGRAmigoCloudTableLayer *poLayer =
374 0 : new OGRAmigoCloudTableLayer(this, osName);
375 : const bool bGeomNullable =
376 0 : CPLFetchBool(papszOptions, "GEOMETRY_NULLABLE", true);
377 0 : OGRSpatialReference *poSRSClone = nullptr;
378 0 : if (poSpatialRef)
379 : {
380 0 : poSRSClone = poSpatialRef->Clone();
381 0 : poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
382 : }
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 : }
392 :
393 : /************************************************************************/
394 : /* DeleteLayer() */
395 : /************************************************************************/
396 :
397 0 : OGRErr OGRAmigoCloudDataSource::DeleteLayer(int iLayer)
398 : {
399 0 : if (!bReadWrite)
400 : {
401 0 : CPLError(CE_Failure, CPLE_AppDefined,
402 : "Operation not available in read-only mode");
403 0 : return OGRERR_FAILURE;
404 : }
405 :
406 0 : if (iLayer < 0 || iLayer >= nLayers)
407 : {
408 0 : CPLError(CE_Failure, CPLE_AppDefined,
409 : "Layer %d not in legal range of 0 to %d.", iLayer,
410 0 : nLayers - 1);
411 0 : return OGRERR_FAILURE;
412 : }
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 : {
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 : {
440 0 : return OGRERR_FAILURE;
441 : }
442 : }
443 :
444 0 : return OGRERR_NONE;
445 : }
446 :
447 : /************************************************************************/
448 : /* AddHTTPOptions() */
449 : /************************************************************************/
450 :
451 0 : char **OGRAmigoCloudDataSource::AddHTTPOptions()
452 : {
453 0 : bMustCleanPersistent = true;
454 :
455 0 : return CSLAddString(nullptr, CPLSPrintf("PERSISTENT=AMIGOCLOUD:%p", this));
456 : }
457 :
458 : /************************************************************************/
459 : /* RunPOST() */
460 : /************************************************************************/
461 :
462 0 : json_object *OGRAmigoCloudDataSource::RunPOST(const char *pszURL,
463 : const char *pszPostData,
464 : const char *pszHeaders)
465 : {
466 0 : CPLString osURL(pszURL);
467 :
468 : /* -------------------------------------------------------------------- */
469 : /* Provide the API Key */
470 : /* -------------------------------------------------------------------- */
471 0 : if (!osAPIKey.empty())
472 : {
473 0 : if (osURL.find("?") == std::string::npos)
474 0 : osURL += "?token=";
475 : else
476 0 : osURL += "&token=";
477 0 : osURL += osAPIKey;
478 : }
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 : {
496 0 : CPLDebug("AMIGOCLOUD", "RunPOST HTML Response: %s", psResult->pabyData);
497 0 : CPLError(CE_Failure, CPLE_AppDefined,
498 : "HTML error page returned by server: %s", psResult->pabyData);
499 0 : CPLHTTPDestroyResult(psResult);
500 0 : return nullptr;
501 : }
502 0 : if (psResult->pszErrBuf != nullptr && psResult->pabyData != nullptr)
503 : {
504 0 : CPLError(CE_Failure, CPLE_AppDefined, "POST Response: %s",
505 : psResult->pabyData);
506 : }
507 0 : else if (psResult->nStatus != 0)
508 : {
509 0 : CPLDebug("AMIGOCLOUD", "RunPOST Error Status:%d", psResult->nStatus);
510 : }
511 :
512 0 : if (psResult->pabyData == nullptr)
513 : {
514 0 : CPLHTTPDestroyResult(psResult);
515 0 : return nullptr;
516 : }
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 : {
522 0 : CPLHTTPDestroyResult(psResult);
523 0 : return nullptr;
524 : }
525 :
526 0 : CPLHTTPDestroyResult(psResult);
527 :
528 0 : if (poObj != nullptr)
529 : {
530 0 : if (json_object_get_type(poObj) == json_type_object)
531 : {
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 : {
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 : {
541 0 : CPLError(CE_Failure, CPLE_AppDefined,
542 : "Error returned by server : %s",
543 : json_object_get_string(poError));
544 0 : json_object_put(poObj);
545 0 : return nullptr;
546 : }
547 : }
548 0 : json_object *poJob = CPL_json_object_object_get(poObj, "job");
549 0 : if (poJob != nullptr)
550 : {
551 0 : const char *job = json_object_get_string(poJob);
552 0 : if (job != nullptr)
553 : {
554 0 : waitForJobToFinish(job);
555 : }
556 : }
557 : }
558 : else
559 : {
560 0 : json_object_put(poObj);
561 0 : return nullptr;
562 : }
563 : }
564 :
565 0 : return poObj;
566 : }
567 :
568 0 : bool OGRAmigoCloudDataSource::waitForJobToFinish(const char *jobId)
569 : {
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 : {
575 0 : count++;
576 0 : json_object *result = RunGET(url.str().c_str());
577 0 : if (result == nullptr)
578 : {
579 0 : CPLError(CE_Failure, CPLE_AppDefined, "waitForJobToFinish failed.");
580 0 : return false;
581 : }
582 :
583 : {
584 0 : int type = json_object_get_type(result);
585 0 : if (type == json_type_object)
586 : {
587 : 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 : {
592 0 : if (std::string(status) == "SUCCESS")
593 : {
594 0 : return true;
595 : }
596 0 : else if (std::string(status) == "FAILURE")
597 : {
598 0 : CPLError(CE_Failure, CPLE_AppDefined, "Job failed : %s",
599 : json_object_get_string(result));
600 0 : return false;
601 : }
602 : }
603 : }
604 : }
605 0 : CPLSleep(1.0); // Sleep 1 sec.
606 : }
607 0 : return false;
608 : }
609 :
610 0 : bool OGRAmigoCloudDataSource::TruncateDataset(const CPLString &tableName)
611 : {
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 : }
618 :
619 0 : void OGRAmigoCloudDataSource::SubmitChangeset(const CPLString &json)
620 : {
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 0 : bool OGRAmigoCloudDataSource::RunDELETE(const char *pszURL)
637 : {
638 0 : CPLString osURL(pszURL);
639 :
640 : /* -------------------------------------------------------------------- */
641 : /* Provide the API Key */
642 : /* -------------------------------------------------------------------- */
643 0 : if (!osAPIKey.empty())
644 : {
645 0 : if (osURL.find("?") == std::string::npos)
646 0 : osURL += "?token=";
647 : else
648 0 : osURL += "&token=";
649 0 : osURL += osAPIKey;
650 : }
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 : {
665 0 : CPLDebug("AMIGOCLOUD", "RunDELETE HTML Response:%s",
666 : psResult->pabyData);
667 0 : CPLError(CE_Failure, CPLE_AppDefined,
668 : "HTML error page returned by server:%s", psResult->pabyData);
669 0 : CPLHTTPDestroyResult(psResult);
670 0 : return false;
671 : }
672 0 : if (psResult->pszErrBuf != nullptr && psResult->pabyData != nullptr)
673 : {
674 0 : CPLError(CE_Failure, CPLE_AppDefined, "DELETE Response: %s",
675 : psResult->pabyData);
676 : }
677 0 : else if (psResult->nStatus != 0)
678 : {
679 0 : CPLDebug("AMIGOCLOUD", "DELETE Error Status:%d", psResult->nStatus);
680 : }
681 0 : CPLHTTPDestroyResult(psResult);
682 :
683 0 : return true;
684 : }
685 :
686 : /************************************************************************/
687 : /* RunGET() */
688 : /************************************************************************/
689 :
690 0 : json_object *OGRAmigoCloudDataSource::RunGET(const char *pszURL)
691 : {
692 0 : CPLString osURL(pszURL);
693 :
694 : /* -------------------------------------------------------------------- */
695 : /* Provide the API Key */
696 : /* -------------------------------------------------------------------- */
697 0 : if (!osAPIKey.empty())
698 : {
699 0 : if (osURL.find("?") == std::string::npos)
700 0 : osURL += "?token=";
701 : else
702 0 : osURL += "&token=";
703 0 : osURL += osAPIKey;
704 : }
705 0 : char **papszOptions = nullptr;
706 0 : papszOptions = CSLAddString(papszOptions, GetUserAgentOption().c_str());
707 :
708 0 : CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), papszOptions);
709 0 : CSLDestroy(papszOptions);
710 0 : if (psResult == nullptr)
711 : {
712 0 : return nullptr;
713 : }
714 :
715 0 : if (psResult->pszContentType &&
716 0 : strncmp(psResult->pszContentType, "text/html", 9) == 0)
717 : {
718 0 : CPLError(CE_Failure, CPLE_AppDefined,
719 : "HTML error page returned by server:%s", psResult->pabyData);
720 0 : CPLHTTPDestroyResult(psResult);
721 0 : return nullptr;
722 : }
723 0 : if (psResult->pszErrBuf != nullptr && psResult->pabyData != nullptr)
724 : {
725 0 : CPLError(CE_Failure, CPLE_AppDefined, "GET Response: %s",
726 : psResult->pabyData);
727 : }
728 0 : else if (psResult->nStatus != 0)
729 : {
730 0 : CPLDebug("AMIGOCLOUD", "RunGET Error Status:%d", psResult->nStatus);
731 : }
732 :
733 0 : if (psResult->pabyData == nullptr)
734 : {
735 0 : CPLHTTPDestroyResult(psResult);
736 0 : return nullptr;
737 : }
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 : {
745 0 : CPLHTTPDestroyResult(psResult);
746 0 : return nullptr;
747 : }
748 :
749 0 : CPLHTTPDestroyResult(psResult);
750 :
751 0 : if (poObj != nullptr)
752 : {
753 0 : if (json_object_get_type(poObj) == json_type_object)
754 : {
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 : {
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 : {
764 0 : CPLError(CE_Failure, CPLE_AppDefined,
765 : "Error returned by server : %s",
766 : json_object_get_string(poError));
767 0 : json_object_put(poObj);
768 0 : return nullptr;
769 : }
770 : }
771 : }
772 : else
773 : {
774 0 : json_object_put(poObj);
775 0 : return nullptr;
776 : }
777 : }
778 :
779 0 : return poObj;
780 : }
781 :
782 : /************************************************************************/
783 : /* RunSQL() */
784 : /************************************************************************/
785 :
786 0 : json_object *OGRAmigoCloudDataSource::RunSQL(const char *pszUnescapedSQL)
787 : {
788 0 : CPLString osSQL;
789 0 : std::string pszAPIURL = GetAPIURL();
790 0 : osSQL = pszAPIURL + "/users/0/projects/" + CPLString(pszProjectId) + "/sql";
791 0 : std::string sql = pszUnescapedSQL;
792 0 : if (sql.find("DELETE") != std::string::npos ||
793 0 : sql.find("delete") != std::string::npos ||
794 0 : sql.find("INSERT") != std::string::npos ||
795 0 : sql.find("insert") != std::string::npos ||
796 0 : sql.find("UPDATE") != std::string::npos ||
797 0 : sql.find("update") != std::string::npos)
798 : {
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 : }
804 : else
805 : {
806 0 : osSQL += "?query=";
807 0 : char *pszEscaped = CPLEscapeString(pszUnescapedSQL, -1, CPLES_URL);
808 0 : osSQL += pszEscaped;
809 0 : CPLFree(pszEscaped);
810 0 : return RunGET(osSQL.c_str());
811 : }
812 : }
813 :
814 : /************************************************************************/
815 : /* OGRAMIGOCLOUDGetSingleRow() */
816 : /************************************************************************/
817 :
818 0 : json_object *OGRAMIGOCLOUDGetSingleRow(json_object *poObj)
819 : {
820 0 : if (poObj == nullptr)
821 : {
822 0 : return nullptr;
823 : }
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 : {
829 0 : return nullptr;
830 : }
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 : {
836 0 : return nullptr;
837 : }
838 :
839 0 : return poRowObj;
840 : }
841 :
842 : /************************************************************************/
843 : /* ExecuteSQL() */
844 : /************************************************************************/
845 :
846 0 : OGRLayer *OGRAmigoCloudDataSource::ExecuteSQL(const char *pszSQLCommand,
847 : OGRGeometry *poSpatialFilter,
848 : const char *pszDialect)
849 :
850 : {
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 : }
860 :
861 0 : OGRLayer *OGRAmigoCloudDataSource::ExecuteSQLInternal(
862 : const char *pszSQLCommand, OGRGeometry *poSpatialFilter, const char *,
863 : bool bRunDeferredActions)
864 :
865 : {
866 0 : if (bRunDeferredActions)
867 : {
868 0 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
869 : {
870 0 : papoLayers[iLayer]->RunDeferredCreationIfNecessary();
871 0 : papoLayers[iLayer]->FlushDeferredInsert();
872 : }
873 : }
874 :
875 : /* Skip leading spaces */
876 0 : while (*pszSQLCommand == ' ')
877 0 : pszSQLCommand++;
878 :
879 0 : if (!EQUALN(pszSQLCommand, "SELECT", strlen("SELECT")) &&
880 0 : !EQUALN(pszSQLCommand, "EXPLAIN", strlen("EXPLAIN")) &&
881 0 : !EQUALN(pszSQLCommand, "WITH", strlen("WITH")))
882 : {
883 0 : RunSQL(pszSQLCommand);
884 0 : return nullptr;
885 : }
886 :
887 : OGRAmigoCloudResultLayer *poLayer =
888 0 : new OGRAmigoCloudResultLayer(this, pszSQLCommand);
889 :
890 0 : if (poSpatialFilter != nullptr)
891 0 : poLayer->SetSpatialFilter(poSpatialFilter);
892 :
893 0 : if (!poLayer->IsOK())
894 : {
895 0 : delete poLayer;
896 0 : return nullptr;
897 : }
898 :
899 0 : return poLayer;
900 : }
901 :
902 : /************************************************************************/
903 : /* ReleaseResultSet() */
904 : /************************************************************************/
905 :
906 0 : void OGRAmigoCloudDataSource::ReleaseResultSet(OGRLayer *poLayer)
907 :
908 : {
909 0 : delete poLayer;
910 0 : }
|