Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: AmigoCloud Translator
4 : * Purpose: Implements OGRAmigoCloudTableLayer class.
5 : * Author: Even Rouault, <even dot rouault at spatialys.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_p.h"
15 : #include "ogr_pgdump.h"
16 : #include "ogrlibjsonutils.h"
17 : #include <sstream>
18 : #include <iomanip>
19 :
20 : /************************************************************************/
21 : /* OGRAMIGOCLOUDEscapeIdentifier( ) */
22 : /************************************************************************/
23 :
24 0 : CPLString OGRAMIGOCLOUDEscapeIdentifier(const char *pszStr)
25 : {
26 0 : CPLString osStr;
27 :
28 0 : osStr += "\"";
29 :
30 0 : char ch = '\0';
31 0 : for (int i = 0; (ch = pszStr[i]) != '\0'; i++)
32 : {
33 0 : if (ch == '"')
34 0 : osStr.append(1, ch);
35 0 : osStr.append(1, ch);
36 : }
37 :
38 0 : osStr += "\"";
39 :
40 0 : return osStr;
41 : }
42 :
43 0 : std::string OGRAMIGOCLOUDJsonEncode(const std::string &s)
44 : {
45 0 : std::ostringstream o;
46 0 : for (auto c = s.cbegin(); c != s.cend(); c++)
47 : {
48 0 : switch (*c)
49 : {
50 0 : case '"':
51 0 : o << "\\\"";
52 0 : break;
53 0 : case '\\':
54 0 : o << "\\\\";
55 0 : break;
56 0 : case '\b':
57 0 : o << "\\b";
58 0 : break;
59 0 : case '\f':
60 0 : o << "\\f";
61 0 : break;
62 0 : case '\n':
63 0 : o << "\\n";
64 0 : break;
65 0 : case '\r':
66 0 : o << "\\r";
67 0 : break;
68 0 : case '\t':
69 0 : o << "\\t";
70 0 : break;
71 0 : default:
72 0 : if (*c <= '\x1f')
73 : {
74 0 : o << "\\u" << std::hex << std::setw(4) << std::setfill('0')
75 0 : << (int)*c;
76 : }
77 : else
78 : {
79 0 : o << *c;
80 : }
81 : }
82 : }
83 0 : return o.str();
84 : }
85 :
86 : /************************************************************************/
87 : /* OGRAmigoCloudTableLayer() */
88 : /************************************************************************/
89 :
90 0 : OGRAmigoCloudTableLayer::OGRAmigoCloudTableLayer(
91 0 : OGRAmigoCloudDataSource *poDSIn, const char *pszName)
92 : : OGRAmigoCloudLayer(poDSIn), osDatasetId(CPLString(pszName)), nNextFID(-1),
93 0 : bDeferredCreation(FALSE)
94 : {
95 0 : osTableName = CPLString("dataset_") + osDatasetId;
96 0 : SetDescription(osDatasetId);
97 0 : osName = osDatasetId;
98 0 : nMaxChunkSize =
99 0 : atoi(CPLGetConfigOption("AMIGOCLOUD_MAX_CHUNK_SIZE", "15")) * 1024 *
100 : 1024;
101 0 : }
102 :
103 : /************************************************************************/
104 : /* ~OGRAmigoCloudTableLayer() */
105 : /************************************************************************/
106 :
107 0 : OGRAmigoCloudTableLayer::~OGRAmigoCloudTableLayer()
108 :
109 : {
110 0 : if (bDeferredCreation)
111 0 : RunDeferredCreationIfNecessary();
112 0 : FlushDeferredInsert();
113 0 : }
114 :
115 : /************************************************************************/
116 : /* GetLayerDefnInternal() */
117 : /************************************************************************/
118 :
119 : OGRFeatureDefn *
120 0 : OGRAmigoCloudTableLayer::GetLayerDefnInternal(CPL_UNUSED json_object *poObjIn)
121 : {
122 0 : if (poFeatureDefn != nullptr)
123 : {
124 0 : return poFeatureDefn;
125 : }
126 :
127 : osBaseSQL.Printf("SELECT * FROM %s",
128 0 : OGRAMIGOCLOUDEscapeIdentifier(osTableName).c_str());
129 0 : EstablishLayerDefn(osTableName, nullptr);
130 0 : osBaseSQL = "";
131 :
132 0 : if (!osFIDColName.empty())
133 : {
134 0 : CPLString sql;
135 : sql.Printf("SELECT %s FROM %s",
136 0 : OGRAMIGOCLOUDEscapeIdentifier(osFIDColName).c_str(),
137 0 : OGRAMIGOCLOUDEscapeIdentifier(osTableName).c_str());
138 0 : json_object *poObj = poDS->RunSQL(sql);
139 0 : if (poObj != nullptr && json_object_get_type(poObj) == json_type_object)
140 : {
141 0 : json_object *poRows = CPL_json_object_object_get(poObj, "data");
142 :
143 0 : if (poRows != nullptr &&
144 0 : json_object_get_type(poRows) == json_type_array)
145 : {
146 0 : mFIDs.clear();
147 0 : const auto nLength = json_object_array_length(poRows);
148 0 : for (auto i = decltype(nLength){0}; i < nLength; i++)
149 : {
150 0 : json_object *obj = json_object_array_get_idx(poRows, i);
151 :
152 : json_object_iter it;
153 0 : it.key = nullptr;
154 0 : it.val = nullptr;
155 0 : it.entry = nullptr;
156 0 : json_object_object_foreachC(obj, it)
157 : {
158 0 : const char *pszColName = it.key;
159 0 : if (it.val != nullptr)
160 : {
161 0 : if (EQUAL(pszColName, osFIDColName.c_str()))
162 : {
163 : std::string amigo_id =
164 0 : json_object_get_string(it.val);
165 0 : OGRAmigoCloudFID aFID(amigo_id, iNext);
166 0 : mFIDs[aFID.iFID] = aFID;
167 : }
168 : }
169 : }
170 : }
171 : }
172 0 : json_object_put(poObj);
173 : }
174 : }
175 :
176 0 : if (!osFIDColName.empty())
177 : {
178 0 : osBaseSQL = "SELECT ";
179 0 : osBaseSQL += OGRAMIGOCLOUDEscapeIdentifier(osFIDColName);
180 : }
181 0 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
182 : {
183 0 : if (osBaseSQL.empty())
184 0 : osBaseSQL = "SELECT ";
185 : else
186 0 : osBaseSQL += ", ";
187 0 : osBaseSQL += OGRAMIGOCLOUDEscapeIdentifier(
188 0 : poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef());
189 : }
190 0 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
191 : {
192 0 : if (osBaseSQL.empty())
193 0 : osBaseSQL = "SELECT ";
194 : else
195 0 : osBaseSQL += ", ";
196 0 : osBaseSQL += OGRAMIGOCLOUDEscapeIdentifier(
197 0 : poFeatureDefn->GetFieldDefn(i)->GetNameRef());
198 : }
199 0 : if (osBaseSQL.empty())
200 0 : osBaseSQL = "SELECT *";
201 0 : osBaseSQL += " FROM ";
202 0 : osBaseSQL += OGRAMIGOCLOUDEscapeIdentifier(osTableName);
203 :
204 0 : osSELECTWithoutWHERE = osBaseSQL;
205 :
206 0 : return poFeatureDefn;
207 : }
208 :
209 : /************************************************************************/
210 : /* FetchNewFeatures() */
211 : /************************************************************************/
212 :
213 0 : json_object *OGRAmigoCloudTableLayer::FetchNewFeatures(GIntBig iNextIn)
214 : {
215 0 : if (!osFIDColName.empty())
216 : {
217 0 : CPLString osSQL;
218 :
219 0 : if (!osWHERE.empty())
220 : {
221 : osSQL.Printf("%s WHERE %s ", osSELECTWithoutWHERE.c_str(),
222 0 : (!osWHERE.empty()) ? CPLSPrintf("%s", osWHERE.c_str())
223 0 : : "");
224 : }
225 : else
226 : {
227 0 : osSQL.Printf("%s", osSELECTWithoutWHERE.c_str());
228 : }
229 :
230 0 : if (osSQL.ifind("SELECT") != std::string::npos &&
231 0 : osSQL.ifind(" LIMIT ") == std::string::npos)
232 : {
233 0 : osSQL += " LIMIT ";
234 0 : osSQL += CPLSPrintf("%d", GetFeaturesToFetch());
235 0 : osSQL += " OFFSET ";
236 0 : osSQL += CPLSPrintf(CPL_FRMT_GIB, iNextIn);
237 : }
238 0 : return poDS->RunSQL(osSQL);
239 : }
240 : else
241 0 : return OGRAmigoCloudLayer::FetchNewFeatures(iNextIn);
242 : }
243 :
244 : /************************************************************************/
245 : /* GetNextRawFeature() */
246 : /************************************************************************/
247 :
248 0 : OGRFeature *OGRAmigoCloudTableLayer::GetNextRawFeature()
249 : {
250 0 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
251 0 : return nullptr;
252 0 : FlushDeferredInsert();
253 0 : return OGRAmigoCloudLayer::GetNextRawFeature();
254 : }
255 :
256 : /************************************************************************/
257 : /* SetAttributeFilter() */
258 : /************************************************************************/
259 :
260 0 : OGRErr OGRAmigoCloudTableLayer::SetAttributeFilter(const char *pszQuery)
261 :
262 : {
263 0 : GetLayerDefn();
264 :
265 0 : if (pszQuery == nullptr)
266 0 : osQuery = "";
267 : else
268 : {
269 0 : osQuery = "(";
270 0 : osQuery += pszQuery;
271 0 : osQuery += ")";
272 : }
273 :
274 0 : BuildWhere();
275 :
276 0 : ResetReading();
277 :
278 0 : return OGRERR_NONE;
279 : }
280 :
281 : /************************************************************************/
282 : /* SetSpatialFilter() */
283 : /************************************************************************/
284 :
285 0 : void OGRAmigoCloudTableLayer::SetSpatialFilter(int iGeomField,
286 : OGRGeometry *poGeomIn)
287 :
288 : {
289 0 : if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
290 0 : GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
291 : {
292 0 : if (iGeomField != 0)
293 : {
294 0 : CPLError(CE_Failure, CPLE_AppDefined,
295 : "Invalid geometry field index : %d", iGeomField);
296 : }
297 0 : return;
298 : }
299 0 : m_iGeomFieldFilter = iGeomField;
300 :
301 0 : if (InstallFilter(poGeomIn))
302 : {
303 0 : BuildWhere();
304 :
305 0 : ResetReading();
306 : }
307 : }
308 :
309 : /************************************************************************/
310 : /* FlushDeferredInsert() */
311 : /************************************************************************/
312 :
313 0 : void OGRAmigoCloudTableLayer::FlushDeferredInsert()
314 :
315 : {
316 0 : if (vsDeferredInsertChangesets.empty())
317 0 : return;
318 :
319 0 : std::stringstream url;
320 0 : url << std::string(poDS->GetAPIURL())
321 0 : << "/users/0/projects/" + std::string(poDS->GetProjectId()) +
322 0 : "/datasets/" + osDatasetId + "/submit_change";
323 :
324 0 : std::stringstream query;
325 :
326 0 : query << "{\"type\":\"DML\",\"entity\":\"" << osTableName << "\",";
327 0 : query << "\"parent\":null,\"action\":\"INSERT\",\"data\":[";
328 :
329 0 : int counter = 0;
330 0 : for (size_t i = 0; i < vsDeferredInsertChangesets.size(); i++)
331 : {
332 0 : if (counter > 0)
333 0 : query << ",";
334 0 : query << vsDeferredInsertChangesets[i].c_str();
335 0 : counter++;
336 : }
337 0 : query << "]}";
338 :
339 0 : std::stringstream changeset;
340 0 : changeset << "{\"change\": \"" << OGRAMIGOCLOUDJsonEncode(query.str())
341 0 : << "\"}";
342 :
343 : json_object *poObj =
344 0 : poDS->RunPOST(url.str().c_str(), changeset.str().c_str());
345 0 : if (poObj != nullptr)
346 0 : json_object_put(poObj);
347 :
348 0 : vsDeferredInsertChangesets.clear();
349 0 : nNextFID = -1;
350 : }
351 :
352 : /************************************************************************/
353 : /* CreateField() */
354 : /************************************************************************/
355 :
356 0 : OGRErr OGRAmigoCloudTableLayer::CreateField(const OGRFieldDefn *poFieldIn,
357 : CPL_UNUSED int bApproxOK)
358 : {
359 0 : GetLayerDefn();
360 :
361 0 : if (!poDS->IsReadWrite())
362 : {
363 0 : CPLError(CE_Failure, CPLE_AppDefined,
364 : "Operation not available in read-only mode");
365 0 : return OGRERR_FAILURE;
366 : }
367 :
368 0 : OGRFieldDefn oField(poFieldIn);
369 : /* -------------------------------------------------------------------- */
370 : /* Create the new field. */
371 : /* -------------------------------------------------------------------- */
372 :
373 0 : if (!bDeferredCreation)
374 : {
375 0 : CPLString osSQL;
376 : osSQL.Printf("ALTER TABLE %s ADD COLUMN %s %s",
377 0 : OGRAMIGOCLOUDEscapeIdentifier(osTableName).c_str(),
378 0 : OGRAMIGOCLOUDEscapeIdentifier(oField.GetNameRef()).c_str(),
379 0 : OGRPGCommonLayerGetType(oField, false, true).c_str());
380 0 : if (!oField.IsNullable())
381 0 : osSQL += " NOT NULL";
382 0 : if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
383 : {
384 0 : osSQL += " DEFAULT ";
385 0 : osSQL += OGRPGCommonLayerGetPGDefault(&oField);
386 : }
387 :
388 0 : json_object *poObj = poDS->RunSQL(osSQL);
389 0 : if (poObj == nullptr)
390 0 : return OGRERR_FAILURE;
391 0 : json_object_put(poObj);
392 : }
393 :
394 0 : poFeatureDefn->AddFieldDefn(&oField);
395 :
396 0 : return OGRERR_NONE;
397 : }
398 :
399 : /************************************************************************/
400 : /* ICreateFeature() */
401 : /************************************************************************/
402 :
403 0 : OGRErr OGRAmigoCloudTableLayer::ICreateFeature(OGRFeature *poFeature)
404 :
405 : {
406 0 : if (bDeferredCreation)
407 : {
408 0 : if (RunDeferredCreationIfNecessary() != OGRERR_NONE)
409 0 : return OGRERR_FAILURE;
410 : }
411 :
412 0 : GetLayerDefn();
413 :
414 0 : if (!poDS->IsReadWrite())
415 : {
416 0 : CPLError(CE_Failure, CPLE_AppDefined,
417 : "Operation not available in read-only mode");
418 0 : return OGRERR_FAILURE;
419 : }
420 :
421 0 : std::stringstream record;
422 :
423 0 : record << "{\"new\":{";
424 :
425 0 : int counter = 0;
426 :
427 : // Add geometry field
428 0 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
429 : {
430 0 : if (poFeature->GetGeomFieldRef(i) == nullptr)
431 0 : continue;
432 :
433 : record << "\""
434 0 : << OGRAMIGOCLOUDJsonEncode(
435 0 : poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef())
436 0 : << "\":";
437 :
438 0 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
439 0 : if (poGeom == nullptr)
440 0 : continue;
441 :
442 : OGRAmigoCloudGeomFieldDefn *poGeomFieldDefn =
443 0 : (OGRAmigoCloudGeomFieldDefn *)poFeatureDefn->GetGeomFieldDefn(i);
444 0 : int nSRID = poGeomFieldDefn->nSRID;
445 0 : if (nSRID == 0)
446 0 : nSRID = 4326;
447 0 : char *pszEWKB = nullptr;
448 0 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon &&
449 0 : wkbFlatten(GetGeomType()) == wkbMultiPolygon)
450 : {
451 0 : OGRMultiPolygon *poNewGeom = new OGRMultiPolygon();
452 0 : poNewGeom->addGeometry(poGeom);
453 0 : pszEWKB = OGRGeometryToHexEWKB(poNewGeom, nSRID, 2, 1);
454 0 : delete poNewGeom;
455 : }
456 : else
457 :
458 0 : pszEWKB = OGRGeometryToHexEWKB(poGeom, nSRID, 2, 1);
459 0 : record << "\"" << pszEWKB << "\"";
460 0 : CPLFree(pszEWKB);
461 :
462 0 : counter++;
463 : }
464 :
465 0 : std::string amigo_id_value;
466 :
467 : // Add non-geometry field
468 0 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
469 : {
470 0 : std::string name = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
471 0 : std::string value = poFeature->GetFieldAsString(i);
472 :
473 0 : if (name == "amigo_id")
474 : {
475 0 : amigo_id_value = value;
476 0 : continue;
477 : }
478 0 : if (!poFeature->IsFieldSet(i))
479 0 : continue;
480 :
481 0 : if (counter > 0)
482 0 : record << ",";
483 :
484 0 : record << OGRAMIGOCLOUDEscapeIdentifier(name.c_str()) << ":";
485 :
486 0 : if (!poFeature->IsFieldNull(i))
487 : {
488 0 : OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType();
489 0 : if (eType == OFTString || eType == OFTDateTime ||
490 0 : eType == OFTDate || eType == OFTTime)
491 : {
492 0 : record << "\"" << OGRAMIGOCLOUDJsonEncode(value.c_str())
493 0 : << "\"";
494 : }
495 : else
496 0 : record << OGRAMIGOCLOUDJsonEncode(value.c_str());
497 : }
498 : else
499 0 : record << "null";
500 :
501 0 : counter++;
502 : }
503 :
504 0 : record << "},";
505 :
506 0 : if (!amigo_id_value.empty())
507 : {
508 0 : record << "\"amigo_id\":\"" << amigo_id_value << "\"";
509 : }
510 : else
511 : {
512 0 : record << "\"amigo_id\":null";
513 : }
514 :
515 0 : record << "}";
516 :
517 0 : vsDeferredInsertChangesets.push_back(record.str());
518 :
519 0 : return OGRERR_NONE;
520 : }
521 :
522 : /************************************************************************/
523 : /* ISetFeature() */
524 : /************************************************************************/
525 :
526 0 : OGRErr OGRAmigoCloudTableLayer::ISetFeature(OGRFeature *poFeature)
527 :
528 : {
529 0 : OGRErr eRet = OGRERR_FAILURE;
530 :
531 0 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
532 0 : return OGRERR_FAILURE;
533 0 : FlushDeferredInsert();
534 :
535 0 : GetLayerDefn();
536 :
537 0 : if (!poDS->IsReadWrite())
538 : {
539 0 : CPLError(CE_Failure, CPLE_AppDefined,
540 : "Operation not available in read-only mode");
541 0 : return OGRERR_FAILURE;
542 : }
543 :
544 0 : if (poFeature->GetFID() == OGRNullFID)
545 : {
546 0 : CPLError(CE_Failure, CPLE_AppDefined,
547 : "FID required on features given to SetFeature().");
548 0 : return OGRERR_FAILURE;
549 : }
550 :
551 0 : const auto it = mFIDs.find(poFeature->GetFID());
552 0 : if (it != mFIDs.end())
553 : {
554 0 : const OGRAmigoCloudFID &aFID = it->second;
555 :
556 0 : CPLString osSQL;
557 : osSQL.Printf("UPDATE %s SET ",
558 0 : OGRAMIGOCLOUDEscapeIdentifier(osTableName).c_str());
559 0 : bool bMustComma = false;
560 0 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
561 : {
562 0 : if (!poFeature->IsFieldSet(i))
563 0 : continue;
564 :
565 0 : if (bMustComma)
566 0 : osSQL += ", ";
567 : else
568 0 : bMustComma = true;
569 :
570 0 : osSQL += OGRAMIGOCLOUDEscapeIdentifier(
571 0 : poFeatureDefn->GetFieldDefn(i)->GetNameRef());
572 0 : osSQL += " = ";
573 :
574 0 : if (poFeature->IsFieldNull(i))
575 : {
576 0 : osSQL += "NULL";
577 : }
578 : else
579 : {
580 0 : OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType();
581 0 : if (eType == OFTString || eType == OFTDateTime ||
582 0 : eType == OFTDate || eType == OFTTime)
583 : {
584 0 : osSQL += "'";
585 : osSQL +=
586 0 : OGRAMIGOCLOUDJsonEncode(poFeature->GetFieldAsString(i));
587 0 : osSQL += "'";
588 : }
589 0 : else if ((eType == OFTInteger || eType == OFTInteger64) &&
590 0 : poFeatureDefn->GetFieldDefn(i)->GetSubType() ==
591 : OFSTBoolean)
592 : {
593 0 : osSQL += poFeature->GetFieldAsInteger(i) ? "'t'" : "'f'";
594 : }
595 : else
596 0 : osSQL += poFeature->GetFieldAsString(i);
597 : }
598 : }
599 :
600 0 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
601 : {
602 0 : if (bMustComma)
603 0 : osSQL += ", ";
604 : else
605 0 : bMustComma = true;
606 :
607 0 : osSQL += OGRAMIGOCLOUDEscapeIdentifier(
608 0 : poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef());
609 0 : osSQL += " = ";
610 :
611 0 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
612 0 : if (poGeom == nullptr)
613 : {
614 0 : osSQL += "NULL";
615 : }
616 : else
617 : {
618 : OGRAmigoCloudGeomFieldDefn *poGeomFieldDefn =
619 : (OGRAmigoCloudGeomFieldDefn *)
620 0 : poFeatureDefn->GetGeomFieldDefn(i);
621 0 : int nSRID = poGeomFieldDefn->nSRID;
622 0 : if (nSRID == 0)
623 0 : nSRID = 4326;
624 0 : char *pszEWKB = OGRGeometryToHexEWKB(poGeom, nSRID, 2, 1);
625 0 : osSQL += "'";
626 0 : osSQL += pszEWKB;
627 0 : osSQL += "'";
628 0 : CPLFree(pszEWKB);
629 : }
630 : }
631 :
632 0 : if (!bMustComma) // nothing to do
633 0 : return OGRERR_NONE;
634 :
635 : osSQL += CPLSPrintf(" WHERE %s = '%s'",
636 0 : OGRAMIGOCLOUDEscapeIdentifier(osFIDColName).c_str(),
637 0 : aFID.osAmigoId.c_str());
638 :
639 0 : std::stringstream changeset;
640 0 : changeset << "{\"query\": \"" << OGRAMIGOCLOUDJsonEncode(osSQL)
641 0 : << "\"}";
642 0 : std::stringstream url;
643 0 : url << std::string(poDS->GetAPIURL())
644 0 : << "/users/0/projects/" + std::string(poDS->GetProjectId()) +
645 0 : "/sql";
646 : json_object *poObj =
647 0 : poDS->RunPOST(url.str().c_str(), changeset.str().c_str());
648 :
649 0 : if (poObj != nullptr)
650 : {
651 : json_object *poTotalRows =
652 0 : CPL_json_object_object_get(poObj, "total_rows");
653 0 : if (poTotalRows != nullptr &&
654 0 : json_object_get_type(poTotalRows) == json_type_int)
655 : {
656 0 : int nTotalRows = json_object_get_int(poTotalRows);
657 0 : if (nTotalRows > 0)
658 : {
659 0 : eRet = OGRERR_NONE;
660 : }
661 : else
662 0 : eRet = OGRERR_NON_EXISTING_FEATURE;
663 : }
664 0 : json_object_put(poObj);
665 : }
666 : }
667 0 : return eRet;
668 : }
669 :
670 : /************************************************************************/
671 : /* DeleteFeature() */
672 : /************************************************************************/
673 :
674 0 : OGRErr OGRAmigoCloudTableLayer::DeleteFeature(GIntBig nFID)
675 :
676 : {
677 0 : OGRErr eRet = OGRERR_FAILURE;
678 :
679 0 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
680 0 : return OGRERR_FAILURE;
681 0 : FlushDeferredInsert();
682 :
683 0 : GetLayerDefn();
684 :
685 0 : if (!poDS->IsReadWrite())
686 : {
687 0 : CPLError(CE_Failure, CPLE_AppDefined,
688 : "Operation not available in read-only mode");
689 0 : return OGRERR_FAILURE;
690 : }
691 :
692 0 : if (osFIDColName.empty())
693 0 : return OGRERR_FAILURE;
694 :
695 0 : const auto it = mFIDs.find(nFID);
696 0 : if (it != mFIDs.end())
697 : {
698 0 : const OGRAmigoCloudFID &aFID = it->second;
699 :
700 0 : CPLString osSQL;
701 : osSQL.Printf("DELETE FROM %s WHERE %s = '%s'",
702 0 : OGRAMIGOCLOUDEscapeIdentifier(osTableName).c_str(),
703 0 : OGRAMIGOCLOUDEscapeIdentifier(osFIDColName).c_str(),
704 0 : aFID.osAmigoId.c_str());
705 :
706 0 : std::stringstream changeset;
707 0 : changeset << "{\"query\": \"" << OGRAMIGOCLOUDJsonEncode(osSQL)
708 0 : << "\"}";
709 0 : std::stringstream url;
710 0 : url << std::string(poDS->GetAPIURL())
711 0 : << "/users/0/projects/" + std::string(poDS->GetProjectId()) +
712 0 : "/sql";
713 : json_object *poObj =
714 0 : poDS->RunPOST(url.str().c_str(), changeset.str().c_str());
715 0 : if (poObj != nullptr)
716 : {
717 0 : json_object_put(poObj);
718 0 : eRet = OGRERR_NONE;
719 : }
720 : }
721 0 : return eRet;
722 : }
723 :
724 : /************************************************************************/
725 : /* GetSRS_SQL() */
726 : /************************************************************************/
727 :
728 0 : CPLString OGRAmigoCloudTableLayer::GetSRS_SQL(const char *pszGeomCol)
729 : {
730 0 : CPLString osSQL;
731 :
732 : osSQL.Printf("SELECT srid, srtext FROM spatial_ref_sys WHERE srid IN "
733 : "(SELECT Find_SRID('%s', '%s', '%s'))",
734 0 : OGRAMIGOCLOUDJsonEncode(poDS->GetCurrentSchema()).c_str(),
735 0 : OGRAMIGOCLOUDJsonEncode(osTableName).c_str(),
736 0 : OGRAMIGOCLOUDJsonEncode(pszGeomCol).c_str());
737 :
738 0 : return osSQL;
739 : }
740 :
741 : /************************************************************************/
742 : /* BuildWhere() */
743 : /* */
744 : /* Build the WHERE statement appropriate to the current set of */
745 : /* criteria (spatial and attribute queries). */
746 : /************************************************************************/
747 :
748 0 : void OGRAmigoCloudTableLayer::BuildWhere()
749 :
750 : {
751 0 : osWHERE = "";
752 :
753 0 : if (m_poFilterGeom != nullptr && m_iGeomFieldFilter >= 0 &&
754 0 : m_iGeomFieldFilter < poFeatureDefn->GetGeomFieldCount())
755 : {
756 0 : OGREnvelope sEnvelope;
757 :
758 0 : m_poFilterGeom->getEnvelope(&sEnvelope);
759 :
760 : CPLString osGeomColumn(
761 0 : poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)->GetNameRef());
762 :
763 : char szBox3D_1[128];
764 : char szBox3D_2[128];
765 0 : char *pszComma = nullptr;
766 :
767 0 : CPLsnprintf(szBox3D_1, sizeof(szBox3D_1), "%.17g %.17g", sEnvelope.MinX,
768 : sEnvelope.MinY);
769 0 : while ((pszComma = strchr(szBox3D_1, ',')) != nullptr)
770 0 : *pszComma = '.';
771 0 : CPLsnprintf(szBox3D_2, sizeof(szBox3D_2), "%.17g %.17g", sEnvelope.MaxX,
772 : sEnvelope.MaxY);
773 0 : while ((pszComma = strchr(szBox3D_2, ',')) != nullptr)
774 0 : *pszComma = '.';
775 : osWHERE.Printf("(%s && 'BOX3D(%s, %s)'::box3d)",
776 0 : OGRAMIGOCLOUDEscapeIdentifier(osGeomColumn).c_str(),
777 0 : szBox3D_1, szBox3D_2);
778 : }
779 :
780 0 : if (!osQuery.empty())
781 : {
782 0 : if (!osWHERE.empty())
783 0 : osWHERE += " AND ";
784 0 : osWHERE += osQuery;
785 : }
786 :
787 0 : if (osFIDColName.empty())
788 : {
789 0 : osBaseSQL = osSELECTWithoutWHERE;
790 0 : if (!osWHERE.empty())
791 : {
792 0 : osBaseSQL += " WHERE ";
793 0 : osBaseSQL += osWHERE;
794 : }
795 : }
796 0 : }
797 :
798 : /************************************************************************/
799 : /* GetFeature() */
800 : /************************************************************************/
801 :
802 0 : OGRFeature *OGRAmigoCloudTableLayer::GetFeature(GIntBig nFeatureId)
803 : {
804 :
805 0 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
806 0 : return nullptr;
807 0 : FlushDeferredInsert();
808 :
809 0 : GetLayerDefn();
810 :
811 0 : if (osFIDColName.empty())
812 0 : return OGRAmigoCloudLayer::GetFeature(nFeatureId);
813 :
814 0 : const auto it = mFIDs.find(nFeatureId);
815 0 : if (it != mFIDs.end())
816 : {
817 0 : const OGRAmigoCloudFID &aFID = it->second;
818 :
819 0 : CPLString osSQL = osSELECTWithoutWHERE;
820 0 : osSQL += " WHERE ";
821 0 : osSQL += OGRAMIGOCLOUDEscapeIdentifier(osFIDColName).c_str();
822 0 : osSQL += " = ";
823 0 : osSQL += CPLSPrintf("'%s'", aFID.osAmigoId.c_str());
824 :
825 0 : json_object *poObj = poDS->RunSQL(osSQL);
826 0 : json_object *poRowObj = OGRAMIGOCLOUDGetSingleRow(poObj);
827 0 : if (poRowObj == nullptr)
828 : {
829 0 : if (poObj != nullptr)
830 0 : json_object_put(poObj);
831 0 : return OGRAmigoCloudLayer::GetFeature(nFeatureId);
832 : }
833 :
834 0 : OGRFeature *poFeature = BuildFeature(poRowObj);
835 0 : json_object_put(poObj);
836 :
837 0 : return poFeature;
838 : }
839 0 : return nullptr;
840 : }
841 :
842 : /************************************************************************/
843 : /* GetFeatureCount() */
844 : /************************************************************************/
845 :
846 0 : GIntBig OGRAmigoCloudTableLayer::GetFeatureCount(int bForce)
847 : {
848 :
849 0 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
850 0 : return 0;
851 0 : FlushDeferredInsert();
852 :
853 0 : GetLayerDefn();
854 :
855 : CPLString osSQL(
856 : CPLSPrintf("SELECT COUNT(*) FROM %s",
857 0 : OGRAMIGOCLOUDEscapeIdentifier(osTableName).c_str()));
858 0 : if (!osWHERE.empty())
859 : {
860 0 : osSQL += " WHERE ";
861 0 : osSQL += osWHERE;
862 : }
863 :
864 0 : json_object *poObj = poDS->RunSQL(osSQL);
865 0 : json_object *poRowObj = OGRAMIGOCLOUDGetSingleRow(poObj);
866 0 : if (poRowObj == nullptr)
867 : {
868 0 : if (poObj != nullptr)
869 0 : json_object_put(poObj);
870 0 : return OGRAmigoCloudLayer::GetFeatureCount(bForce);
871 : }
872 :
873 0 : json_object *poCount = CPL_json_object_object_get(poRowObj, "count");
874 0 : if (poCount == nullptr || json_object_get_type(poCount) != json_type_int)
875 : {
876 0 : json_object_put(poObj);
877 0 : return OGRAmigoCloudLayer::GetFeatureCount(bForce);
878 : }
879 :
880 0 : GIntBig nRet = (GIntBig)json_object_get_int64(poCount);
881 :
882 0 : json_object_put(poObj);
883 :
884 0 : return nRet;
885 : }
886 :
887 : /************************************************************************/
888 : /* GetExtent() */
889 : /* */
890 : /* For PostGIS use internal Extend(geometry) function */
891 : /* in other cases we use standard OGRLayer::GetExtent() */
892 : /************************************************************************/
893 :
894 0 : OGRErr OGRAmigoCloudTableLayer::GetExtent(int iGeomField, OGREnvelope *psExtent,
895 : int bForce)
896 : {
897 0 : CPLString osSQL;
898 :
899 0 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
900 0 : return OGRERR_FAILURE;
901 0 : FlushDeferredInsert();
902 :
903 0 : if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
904 0 : GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
905 : {
906 0 : if (iGeomField != 0)
907 : {
908 0 : CPLError(CE_Failure, CPLE_AppDefined,
909 : "Invalid geometry field index : %d", iGeomField);
910 : }
911 0 : return OGRERR_FAILURE;
912 : }
913 :
914 : OGRGeomFieldDefn *poGeomFieldDefn =
915 0 : poFeatureDefn->GetGeomFieldDefn(iGeomField);
916 :
917 : /* Do not take the spatial filter into account */
918 : osSQL.Printf(
919 : "SELECT ST_Extent(%s) FROM %s",
920 0 : OGRAMIGOCLOUDEscapeIdentifier(poGeomFieldDefn->GetNameRef()).c_str(),
921 0 : OGRAMIGOCLOUDEscapeIdentifier(osTableName).c_str());
922 :
923 0 : json_object *poObj = poDS->RunSQL(osSQL);
924 0 : json_object *poRowObj = OGRAMIGOCLOUDGetSingleRow(poObj);
925 0 : if (poRowObj != nullptr)
926 : {
927 : json_object *poExtent =
928 0 : CPL_json_object_object_get(poRowObj, "st_extent");
929 0 : if (poExtent != nullptr &&
930 0 : json_object_get_type(poExtent) == json_type_string)
931 : {
932 0 : const char *pszBox = json_object_get_string(poExtent);
933 : const char *ptr, *ptrEndParenthesis;
934 : char szVals[64 * 6 + 6];
935 :
936 0 : ptr = strchr(pszBox, '(');
937 0 : if (ptr)
938 0 : ptr++;
939 0 : if (ptr == nullptr ||
940 0 : (ptrEndParenthesis = strchr(ptr, ')')) == nullptr ||
941 0 : ptrEndParenthesis - ptr > (int)(sizeof(szVals) - 1))
942 : {
943 0 : CPLError(CE_Failure, CPLE_IllegalArg,
944 : "Bad extent representation: '%s'", pszBox);
945 :
946 0 : json_object_put(poObj);
947 0 : return OGRERR_FAILURE;
948 : }
949 :
950 0 : strncpy(szVals, ptr, ptrEndParenthesis - ptr);
951 0 : szVals[ptrEndParenthesis - ptr] = '\0';
952 :
953 : char **papszTokens =
954 0 : CSLTokenizeString2(szVals, " ,", CSLT_HONOURSTRINGS);
955 0 : int nTokenCnt = 4;
956 :
957 0 : if (CSLCount(papszTokens) != nTokenCnt)
958 : {
959 0 : CPLError(CE_Failure, CPLE_IllegalArg,
960 : "Bad extent representation: '%s'", pszBox);
961 0 : CSLDestroy(papszTokens);
962 :
963 0 : json_object_put(poObj);
964 0 : return OGRERR_FAILURE;
965 : }
966 :
967 : // Take X,Y coords
968 : // For PostGIS ver >= 1.0.0 -> Tokens: X1 Y1 X2 Y2 (nTokenCnt = 4)
969 : // For PostGIS ver < 1.0.0 -> Tokens: X1 Y1 Z1 X2 Y2 Z2 (nTokenCnt =
970 : // 6)
971 : // => X2 index calculated as nTokenCnt/2
972 : // Y2 index calculated as nTokenCnt/2+1
973 :
974 0 : psExtent->MinX = CPLAtof(papszTokens[0]);
975 0 : psExtent->MinY = CPLAtof(papszTokens[1]);
976 0 : psExtent->MaxX = CPLAtof(papszTokens[nTokenCnt / 2]);
977 0 : psExtent->MaxY = CPLAtof(papszTokens[nTokenCnt / 2 + 1]);
978 :
979 0 : CSLDestroy(papszTokens);
980 :
981 0 : json_object_put(poObj);
982 0 : return OGRERR_NONE;
983 : }
984 : }
985 :
986 0 : if (poObj != nullptr)
987 0 : json_object_put(poObj);
988 :
989 0 : if (iGeomField == 0)
990 0 : return OGRLayer::GetExtent(psExtent, bForce);
991 : else
992 0 : return OGRLayer::GetExtent(iGeomField, psExtent, bForce);
993 : }
994 :
995 : /************************************************************************/
996 : /* TestCapability() */
997 : /************************************************************************/
998 :
999 0 : int OGRAmigoCloudTableLayer::TestCapability(const char *pszCap)
1000 :
1001 : {
1002 0 : if (EQUAL(pszCap, OLCFastFeatureCount))
1003 0 : return TRUE;
1004 0 : if (EQUAL(pszCap, OLCFastGetExtent))
1005 0 : return TRUE;
1006 0 : if (EQUAL(pszCap, OLCRandomRead))
1007 : {
1008 0 : GetLayerDefn();
1009 0 : return !osFIDColName.empty();
1010 : }
1011 :
1012 0 : if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite) ||
1013 0 : EQUAL(pszCap, OLCDeleteFeature) || EQUAL(pszCap, ODsCCreateLayer) ||
1014 0 : EQUAL(pszCap, ODsCDeleteLayer))
1015 : {
1016 0 : return poDS->IsReadWrite();
1017 : }
1018 :
1019 0 : return OGRAmigoCloudLayer::TestCapability(pszCap);
1020 : }
1021 :
1022 : /************************************************************************/
1023 : /* SetDeferredCreation() */
1024 : /************************************************************************/
1025 :
1026 0 : void OGRAmigoCloudTableLayer::SetDeferredCreation(OGRwkbGeometryType eGType,
1027 : OGRSpatialReference *poSRS,
1028 : int bGeomNullable)
1029 : {
1030 0 : bDeferredCreation = TRUE;
1031 0 : nNextFID = 1;
1032 0 : CPLAssert(poFeatureDefn == nullptr);
1033 0 : poFeatureDefn = new OGRFeatureDefn(osTableName);
1034 0 : poFeatureDefn->Reference();
1035 0 : poFeatureDefn->SetGeomType(wkbNone);
1036 0 : if (eGType == wkbPolygon)
1037 0 : eGType = wkbMultiPolygon;
1038 0 : else if (eGType == wkbPolygon25D)
1039 0 : eGType = wkbMultiPolygon25D;
1040 0 : if (eGType != wkbNone)
1041 : {
1042 : auto poFieldDefn = std::make_unique<OGRAmigoCloudGeomFieldDefn>(
1043 0 : "wkb_geometry", eGType);
1044 0 : poFieldDefn->SetNullable(bGeomNullable);
1045 0 : if (poSRS != nullptr)
1046 : {
1047 0 : poFieldDefn->nSRID = poDS->FetchSRSId(poSRS);
1048 0 : poFieldDefn->SetSpatialRef(poSRS);
1049 : }
1050 0 : poFeatureDefn->AddGeomFieldDefn(std::move(poFieldDefn));
1051 : }
1052 :
1053 : osBaseSQL.Printf("SELECT * FROM %s",
1054 0 : OGRAMIGOCLOUDEscapeIdentifier(osTableName).c_str());
1055 0 : }
1056 :
1057 0 : CPLString OGRAmigoCloudTableLayer::GetAmigoCloudType(const OGRFieldDefn &oField)
1058 : {
1059 : char szFieldType[256];
1060 :
1061 : /* -------------------------------------------------------------------- */
1062 : /* AmigoCloud supported types. */
1063 : /* -------------------------------------------------------------------- */
1064 0 : if (oField.GetType() == OFTInteger)
1065 : {
1066 0 : strcpy(szFieldType, "integer");
1067 : }
1068 0 : else if (oField.GetType() == OFTInteger64)
1069 : {
1070 0 : strcpy(szFieldType, "bigint");
1071 : }
1072 0 : else if (oField.GetType() == OFTReal)
1073 : {
1074 0 : strcpy(szFieldType, "float");
1075 : }
1076 0 : else if (oField.GetType() == OFTString)
1077 : {
1078 0 : strcpy(szFieldType, "string");
1079 : }
1080 0 : else if (oField.GetType() == OFTDate)
1081 : {
1082 0 : strcpy(szFieldType, "date");
1083 : }
1084 0 : else if (oField.GetType() == OFTTime)
1085 : {
1086 0 : strcpy(szFieldType, "time");
1087 : }
1088 0 : else if (oField.GetType() == OFTDateTime)
1089 : {
1090 0 : strcpy(szFieldType, "datetime");
1091 : }
1092 : else
1093 : {
1094 0 : CPLError(CE_Failure, CPLE_NotSupported,
1095 : "Can't create field %s with type %s on PostgreSQL layers.",
1096 : oField.GetNameRef(),
1097 : OGRFieldDefn::GetFieldTypeName(oField.GetType()));
1098 0 : strcpy(szFieldType, "");
1099 : }
1100 :
1101 0 : return szFieldType;
1102 : }
1103 :
1104 0 : bool OGRAmigoCloudTableLayer::IsDatasetExists()
1105 : {
1106 0 : std::stringstream url;
1107 0 : url << std::string(poDS->GetAPIURL())
1108 0 : << "/users/0/projects/" + std::string(poDS->GetProjectId()) +
1109 0 : "/datasets/" + osDatasetId;
1110 0 : json_object *result = poDS->RunGET(url.str().c_str());
1111 0 : if (result == nullptr)
1112 0 : return false;
1113 :
1114 : {
1115 0 : int type = json_object_get_type(result);
1116 0 : if (type == json_type_object)
1117 : {
1118 0 : json_object *poId = CPL_json_object_object_get(result, "id");
1119 0 : if (poId != nullptr)
1120 : {
1121 0 : json_object_put(result);
1122 0 : return true;
1123 : }
1124 : }
1125 0 : json_object_put(result);
1126 : }
1127 :
1128 : // Sleep 3 sec
1129 0 : CPLSleep(3);
1130 :
1131 0 : return false;
1132 : }
1133 :
1134 : /************************************************************************/
1135 : /* RunDeferredCreationIfNecessary() */
1136 : /************************************************************************/
1137 :
1138 0 : OGRErr OGRAmigoCloudTableLayer::RunDeferredCreationIfNecessary()
1139 : {
1140 0 : if (!bDeferredCreation)
1141 0 : return OGRERR_NONE;
1142 0 : bDeferredCreation = FALSE;
1143 0 : std::stringstream json;
1144 0 : json << "{ \"name\":\"" << osDatasetId << "\",";
1145 0 : json << "\"schema\": \"[";
1146 0 : int counter = 0;
1147 0 : OGRwkbGeometryType eGType = GetGeomType();
1148 0 : if (eGType != wkbNone)
1149 : {
1150 0 : CPLString osGeomType = OGRToOGCGeomType(eGType);
1151 0 : if (wkbHasZ(eGType))
1152 0 : osGeomType += "Z";
1153 :
1154 : OGRAmigoCloudGeomFieldDefn *poFieldDefn =
1155 0 : (OGRAmigoCloudGeomFieldDefn *)poFeatureDefn->GetGeomFieldDefn(0);
1156 :
1157 0 : json << "{\\\"name\\\":\\\"" << poFieldDefn->GetNameRef() << "\\\",";
1158 0 : json << "\\\"type\\\":\\\"geometry\\\",";
1159 0 : json << "\\\"geometry_type\\\":\\\"" << osGeomType << "\\\",";
1160 :
1161 0 : if (!poFieldDefn->IsNullable())
1162 0 : json << "\\\"nullable\\\":false,";
1163 : else
1164 0 : json << "\\\"nullable\\\":true,";
1165 :
1166 0 : json << "\\\"visible\\\": true}";
1167 :
1168 0 : counter++;
1169 : }
1170 :
1171 0 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
1172 : {
1173 0 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(i);
1174 0 : if (strcmp(poFieldDefn->GetNameRef(), osFIDColName) != 0)
1175 : {
1176 0 : if (counter > 0)
1177 0 : json << ",";
1178 :
1179 : json << "{\\\"name\\\":\\\"" << poFieldDefn->GetNameRef()
1180 0 : << "\\\",";
1181 0 : json << "\\\"type\\\":\\\"" << GetAmigoCloudType(*poFieldDefn)
1182 0 : << "\\\",";
1183 0 : if (!poFieldDefn->IsNullable())
1184 0 : json << "\\\"nullable\\\":false,";
1185 : else
1186 0 : json << "\\\"nullable\\\":true,";
1187 :
1188 0 : if (poFieldDefn->GetDefault() != nullptr &&
1189 0 : !poFieldDefn->IsDefaultDriverSpecific())
1190 : {
1191 : json << "\\\"default\\\":\\\"" << poFieldDefn->GetDefault()
1192 0 : << "\\\",";
1193 : }
1194 0 : json << "\\\"visible\\\": true}";
1195 0 : counter++;
1196 : }
1197 : }
1198 :
1199 0 : json << " ] \" }";
1200 :
1201 0 : std::stringstream url;
1202 0 : url << std::string(poDS->GetAPIURL())
1203 0 : << "/users/0/projects/" + std::string(poDS->GetProjectId()) +
1204 0 : "/datasets/create";
1205 :
1206 0 : json_object *result = poDS->RunPOST(url.str().c_str(), json.str().c_str());
1207 0 : if (result != nullptr)
1208 : {
1209 0 : if (json_object_get_type(result) == json_type_object)
1210 : {
1211 0 : json_object *poName = CPL_json_object_object_get(result, "name");
1212 0 : if (poName != nullptr)
1213 : {
1214 0 : osName = json_object_to_json_string(poName);
1215 : }
1216 :
1217 0 : json_object *poId = CPL_json_object_object_get(result, "id");
1218 0 : if (poId != nullptr)
1219 : {
1220 : osTableName =
1221 0 : CPLString("dataset_") + json_object_to_json_string(poId);
1222 0 : osDatasetId = json_object_to_json_string(poId);
1223 0 : int retry = 10;
1224 0 : while (!IsDatasetExists() && retry >= 0)
1225 : {
1226 0 : retry--;
1227 : }
1228 0 : json_object_put(result);
1229 0 : return OGRERR_NONE;
1230 : }
1231 : }
1232 : }
1233 0 : return OGRERR_FAILURE;
1234 : }
|