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