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