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