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