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