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