Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Carto Translator
4 : * Purpose: Implements OGRCARTODataSource class
5 : * Author: Even Rouault, even dot rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2013, Even Rouault <even dot rouault at spatialys.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_carto.h"
30 : #include "ogr_pgdump.h"
31 : #include "ogrgeojsonreader.h"
32 :
33 : /************************************************************************/
34 : /* OGRCARTODataSource() */
35 : /************************************************************************/
36 :
37 35 : OGRCARTODataSource::OGRCARTODataSource()
38 : : pszName(nullptr), pszAccount(nullptr), papoLayers(nullptr), nLayers(0),
39 : bReadWrite(false), bBatchInsert(true), bCopyMode(true), bUseHTTPS(false),
40 : bMustCleanPersistent(false), bHasOGRMetadataFunction(-1),
41 35 : nPostGISMajor(2), nPostGISMinor(0)
42 : {
43 35 : }
44 :
45 : /************************************************************************/
46 : /* ~OGRCARTODataSource() */
47 : /************************************************************************/
48 :
49 70 : OGRCARTODataSource::~OGRCARTODataSource()
50 :
51 : {
52 53 : for (int i = 0; i < nLayers; i++)
53 18 : delete papoLayers[i];
54 35 : CPLFree(papoLayers);
55 :
56 35 : if (bMustCleanPersistent)
57 : {
58 0 : char **papszOptions = nullptr;
59 0 : papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT",
60 : CPLSPrintf("CARTO:%p", this));
61 0 : CPLHTTPDestroyResult(CPLHTTPFetch(GetAPIURL(), papszOptions));
62 0 : CSLDestroy(papszOptions);
63 : }
64 :
65 35 : CPLFree(pszName);
66 35 : CPLFree(pszAccount);
67 70 : }
68 :
69 : /************************************************************************/
70 : /* TestCapability() */
71 : /************************************************************************/
72 :
73 0 : int OGRCARTODataSource::TestCapability(const char *pszCap)
74 :
75 : {
76 0 : if (bReadWrite && EQUAL(pszCap, ODsCCreateLayer))
77 0 : return TRUE;
78 0 : else if (bReadWrite && EQUAL(pszCap, ODsCDeleteLayer))
79 0 : return TRUE;
80 0 : else if (bReadWrite && EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
81 0 : return TRUE;
82 0 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
83 0 : return bReadWrite;
84 : else
85 0 : return FALSE;
86 : }
87 :
88 : /************************************************************************/
89 : /* GetLayer() */
90 : /************************************************************************/
91 :
92 12 : OGRLayer *OGRCARTODataSource::GetLayer(int iLayer)
93 :
94 : {
95 12 : if (iLayer < 0 || iLayer >= nLayers)
96 0 : return nullptr;
97 : else
98 12 : return papoLayers[iLayer];
99 : }
100 :
101 : /************************************************************************/
102 : /* GetLayerByName() */
103 : /************************************************************************/
104 :
105 3 : OGRLayer *OGRCARTODataSource::GetLayerByName(const char *pszLayerName)
106 : {
107 3 : OGRLayer *poLayer = OGRDataSource::GetLayerByName(pszLayerName);
108 3 : return poLayer;
109 : }
110 :
111 : /************************************************************************/
112 : /* OGRCARTOGetOptionValue() */
113 : /************************************************************************/
114 :
115 35 : static CPLString OGRCARTOGetOptionValue(const char *pszFilename,
116 : const char *pszOptionName)
117 : {
118 70 : CPLString osOptionName(pszOptionName);
119 35 : osOptionName += "=";
120 35 : const char *pszOptionValue = strstr(pszFilename, osOptionName);
121 35 : if (!pszOptionValue)
122 35 : return "";
123 :
124 0 : CPLString osOptionValue(pszOptionValue + osOptionName.size());
125 0 : const char *pszSpace = strchr(osOptionValue.c_str(), ' ');
126 0 : if (pszSpace)
127 0 : osOptionValue.resize(pszSpace - osOptionValue.c_str());
128 0 : return osOptionValue;
129 : }
130 :
131 : /************************************************************************/
132 : /* Open() */
133 : /************************************************************************/
134 :
135 35 : int OGRCARTODataSource::Open(const char *pszFilename, char **papszOpenOptionsIn,
136 : int bUpdateIn)
137 :
138 : {
139 35 : bReadWrite = CPL_TO_BOOL(bUpdateIn);
140 35 : bBatchInsert = CPLTestBool(
141 : CSLFetchNameValueDef(papszOpenOptionsIn, "BATCH_INSERT", "YES"));
142 35 : bCopyMode = CPLTestBool(
143 : CSLFetchNameValueDef(papszOpenOptionsIn, "COPY_MODE", "YES"));
144 35 : if (bCopyMode)
145 29 : bBatchInsert = TRUE;
146 :
147 35 : pszName = CPLStrdup(pszFilename);
148 35 : if (CSLFetchNameValue(papszOpenOptionsIn, "ACCOUNT"))
149 0 : pszAccount =
150 0 : CPLStrdup(CSLFetchNameValue(papszOpenOptionsIn, "ACCOUNT"));
151 : else
152 : {
153 35 : if (STARTS_WITH_CI(pszFilename, "CARTODB:"))
154 0 : pszAccount = CPLStrdup(pszFilename + strlen("CARTODB:"));
155 : else
156 35 : pszAccount = CPLStrdup(pszFilename + strlen("CARTO:"));
157 35 : char *pchSpace = strchr(pszAccount, ' ');
158 35 : if (pchSpace)
159 0 : *pchSpace = '\0';
160 35 : if (pszAccount[0] == 0)
161 : {
162 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing account name");
163 0 : return FALSE;
164 : }
165 : }
166 :
167 : osAPIKey = CSLFetchNameValueDef(
168 : papszOpenOptionsIn, "API_KEY",
169 : CPLGetConfigOption("CARTO_API_KEY",
170 35 : CPLGetConfigOption("CARTODB_API_KEY", "")));
171 :
172 70 : CPLString osTables = OGRCARTOGetOptionValue(pszFilename, "tables");
173 :
174 : /*if( osTables.empty() && osAPIKey.empty() )
175 : {
176 : CPLError(CE_Failure, CPLE_AppDefined,
177 : "When not specifying tables option, CARTO_API_KEY must be
178 : defined"); return FALSE;
179 : }*/
180 :
181 35 : bUseHTTPS = CPLTestBool(CPLGetConfigOption(
182 : "CARTO_HTTPS", CPLGetConfigOption("CARTODB_HTTPS", "YES")));
183 :
184 35 : OGRLayer *poSchemaLayer = ExecuteSQLInternal("SELECT current_schema()");
185 35 : if (poSchemaLayer)
186 : {
187 31 : OGRFeature *poFeat = poSchemaLayer->GetNextFeature();
188 31 : if (poFeat)
189 : {
190 20 : if (poFeat->GetFieldCount() == 1)
191 : {
192 19 : osCurrentSchema = poFeat->GetFieldAsString(0);
193 : }
194 20 : delete poFeat;
195 : }
196 31 : ReleaseResultSet(poSchemaLayer);
197 : }
198 35 : if (osCurrentSchema.empty())
199 16 : return FALSE;
200 :
201 : /* -------------------------------------------------------------------- */
202 : /* Find out PostGIS version */
203 : /* -------------------------------------------------------------------- */
204 19 : if (bReadWrite)
205 : {
206 : OGRLayer *poPostGISVersionLayer =
207 10 : ExecuteSQLInternal("SELECT postgis_version()");
208 10 : if (poPostGISVersionLayer)
209 : {
210 10 : OGRFeature *poFeat = poPostGISVersionLayer->GetNextFeature();
211 10 : if (poFeat)
212 : {
213 10 : if (poFeat->GetFieldCount() == 1)
214 : {
215 10 : const char *pszVersion = poFeat->GetFieldAsString(0);
216 10 : nPostGISMajor = atoi(pszVersion);
217 10 : const char *pszDot = strchr(pszVersion, '.');
218 10 : nPostGISMinor = 0;
219 10 : if (pszDot)
220 10 : nPostGISMinor = atoi(pszDot + 1);
221 : }
222 10 : delete poFeat;
223 : }
224 10 : ReleaseResultSet(poPostGISVersionLayer);
225 : }
226 : }
227 :
228 19 : if (!osAPIKey.empty() && bUpdateIn)
229 : {
230 10 : ExecuteSQLInternal(
231 : "DROP FUNCTION IF EXISTS ogr_table_metadata(TEXT,TEXT); "
232 : "CREATE OR REPLACE FUNCTION ogr_table_metadata(schema_name TEXT, "
233 : "table_name TEXT) RETURNS TABLE "
234 : "(attname TEXT, typname TEXT, attlen INT, format_type TEXT, "
235 : "attnum INT, attnotnull BOOLEAN, indisprimary BOOLEAN, "
236 : "defaultexpr TEXT, dim INT, srid INT, geomtyp TEXT, srtext TEXT) "
237 : "AS $$ "
238 : "SELECT a.attname::text, t.typname::text, a.attlen::int, "
239 : "format_type(a.atttypid,a.atttypmod)::text, "
240 : "a.attnum::int, "
241 : "a.attnotnull::boolean, "
242 : "i.indisprimary::boolean, "
243 : "pg_get_expr(def.adbin, c.oid)::text AS defaultexpr, "
244 : "(CASE WHEN t.typname = 'geometry' THEN "
245 : "postgis_typmod_dims(a.atttypmod) ELSE NULL END)::int dim, "
246 : "(CASE WHEN t.typname = 'geometry' THEN "
247 : "postgis_typmod_srid(a.atttypmod) ELSE NULL END)::int srid, "
248 : "(CASE WHEN t.typname = 'geometry' THEN "
249 : "postgis_typmod_type(a.atttypmod) ELSE NULL END)::text geomtyp, "
250 : "srtext "
251 : "FROM pg_class c "
252 : "JOIN pg_attribute a ON a.attnum > 0 AND "
253 : "a.attrelid = c.oid AND c.relname = $2 "
254 : "AND c.relname IN (SELECT CDB_UserTables())"
255 : "JOIN pg_type t ON a.atttypid = t.oid "
256 : "JOIN pg_namespace n ON c.relnamespace=n.oid AND n.nspname = $1 "
257 : "LEFT JOIN pg_index i ON c.oid = i.indrelid AND "
258 : "i.indisprimary = 't' AND a.attnum = ANY(i.indkey) "
259 : "LEFT JOIN pg_attrdef def ON def.adrelid = c.oid AND "
260 : "def.adnum = a.attnum "
261 : "LEFT JOIN spatial_ref_sys srs ON srs.srid = "
262 : "postgis_typmod_srid(a.atttypmod) "
263 : "ORDER BY a.attnum "
264 : "$$ LANGUAGE SQL");
265 : }
266 :
267 19 : if (!osTables.empty())
268 : {
269 0 : char **papszTables = CSLTokenizeString2(osTables, ",", 0);
270 0 : for (int i = 0; papszTables && papszTables[i]; i++)
271 : {
272 0 : papoLayers = (OGRCARTOTableLayer **)CPLRealloc(
273 0 : papoLayers, (nLayers + 1) * sizeof(OGRCARTOTableLayer *));
274 0 : papoLayers[nLayers++] =
275 0 : new OGRCARTOTableLayer(this, papszTables[i]);
276 : }
277 0 : CSLDestroy(papszTables);
278 0 : return TRUE;
279 : }
280 :
281 19 : OGRLayer *poTableListLayer = ExecuteSQLInternal("SELECT CDB_UserTables()");
282 19 : if (poTableListLayer)
283 : {
284 : OGRFeature *poFeat;
285 35 : while ((poFeat = poTableListLayer->GetNextFeature()) != nullptr)
286 : {
287 17 : if (poFeat->GetFieldCount() == 1)
288 : {
289 34 : papoLayers = (OGRCARTOTableLayer **)CPLRealloc(
290 17 : papoLayers, (nLayers + 1) * sizeof(OGRCARTOTableLayer *));
291 17 : papoLayers[nLayers++] =
292 17 : new OGRCARTOTableLayer(this, poFeat->GetFieldAsString(0));
293 : }
294 17 : delete poFeat;
295 : }
296 18 : ReleaseResultSet(poTableListLayer);
297 : }
298 1 : else if (osCurrentSchema == "public")
299 1 : return FALSE;
300 :
301 : /* There's currently a bug with CDB_UserTables() on multi-user accounts */
302 18 : if (nLayers == 0 && osCurrentSchema != "public")
303 : {
304 1 : CPLString osSQL;
305 : osSQL.Printf("SELECT c.relname FROM pg_class c, pg_namespace n "
306 : "WHERE c.relkind in ('r', 'v') AND c.relname !~ '^pg_' "
307 : "AND c.relnamespace=n.oid AND n.nspname = '%s'",
308 1 : OGRCARTOEscapeLiteral(osCurrentSchema).c_str());
309 1 : poTableListLayer = ExecuteSQLInternal(osSQL);
310 1 : if (poTableListLayer)
311 : {
312 : OGRFeature *poFeat;
313 2 : while ((poFeat = poTableListLayer->GetNextFeature()) != nullptr)
314 : {
315 1 : if (poFeat->GetFieldCount() == 1)
316 : {
317 2 : papoLayers = (OGRCARTOTableLayer **)CPLRealloc(
318 1 : papoLayers,
319 1 : (nLayers + 1) * sizeof(OGRCARTOTableLayer *));
320 1 : papoLayers[nLayers++] = new OGRCARTOTableLayer(
321 1 : this, poFeat->GetFieldAsString(0));
322 : }
323 1 : delete poFeat;
324 : }
325 1 : ReleaseResultSet(poTableListLayer);
326 : }
327 : else
328 0 : return FALSE;
329 : }
330 :
331 18 : return TRUE;
332 : }
333 :
334 : /************************************************************************/
335 : /* GetAPIURL() */
336 : /************************************************************************/
337 :
338 340 : const char *OGRCARTODataSource::GetAPIURL() const
339 : {
340 340 : const char *pszAPIURL = CPLGetConfigOption(
341 : "CARTO_API_URL", CPLGetConfigOption("CARTODB_API_URL", nullptr));
342 340 : if (pszAPIURL)
343 340 : return pszAPIURL;
344 0 : else if (bUseHTTPS)
345 0 : return CPLSPrintf("https://%s.carto.com/api/v2/sql", pszAccount);
346 : else
347 0 : return CPLSPrintf("http://%s.carto.com/api/v2/sql", pszAccount);
348 : }
349 :
350 : /************************************************************************/
351 : /* FetchSRSId() */
352 : /************************************************************************/
353 :
354 2 : int OGRCARTODataSource::FetchSRSId(const OGRSpatialReference *poSRS)
355 :
356 : {
357 : const char *pszAuthorityName;
358 :
359 2 : if (poSRS == nullptr)
360 0 : return 0;
361 :
362 4 : OGRSpatialReference oSRS(*poSRS);
363 : // cppcheck-suppress uselessAssignmentPtrArg
364 2 : poSRS = nullptr;
365 :
366 2 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
367 :
368 2 : if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
369 : {
370 : /* --------------------------------------------------------------------
371 : */
372 : /* Try to identify an EPSG code */
373 : /* --------------------------------------------------------------------
374 : */
375 0 : oSRS.AutoIdentifyEPSG();
376 :
377 0 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
378 0 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
379 : {
380 0 : const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
381 0 : if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
382 : {
383 : /* Import 'clean' SRS */
384 0 : oSRS.importFromEPSG(atoi(pszAuthorityCode));
385 :
386 0 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
387 : }
388 : }
389 : }
390 : /* -------------------------------------------------------------------- */
391 : /* Check whether the EPSG authority code is already mapped to a */
392 : /* SRS ID. */
393 : /* -------------------------------------------------------------------- */
394 2 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
395 : {
396 : /* For the root authority name 'EPSG', the authority code
397 : * should always be integral
398 : */
399 2 : const int nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
400 :
401 2 : return nAuthorityCode;
402 : }
403 :
404 0 : return 0;
405 : }
406 :
407 : /************************************************************************/
408 : /* ICreateLayer() */
409 : /************************************************************************/
410 :
411 : OGRLayer *
412 6 : OGRCARTODataSource::ICreateLayer(const char *pszNameIn,
413 : const OGRGeomFieldDefn *poGeomFieldDefn,
414 : CSLConstList papszOptions)
415 : {
416 6 : if (!bReadWrite)
417 : {
418 1 : CPLError(CE_Failure, CPLE_AppDefined,
419 : "Operation not available in read-only mode");
420 1 : return nullptr;
421 : }
422 :
423 5 : const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
424 : const auto poSpatialRef =
425 5 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
426 :
427 : /* -------------------------------------------------------------------- */
428 : /* Do we already have this layer? If so, set it up for overwrite */
429 : /* away? */
430 : /* -------------------------------------------------------------------- */
431 : bool bOverwriteOption =
432 7 : CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
433 2 : !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO");
434 :
435 9 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
436 : {
437 5 : if (EQUAL(pszNameIn, papoLayers[iLayer]->GetName()))
438 : {
439 3 : if (bOverwriteOption)
440 : {
441 : /* We set DropOnCreation so the remote table isn't dropped */
442 : /* As we are going to overwrite it in a single transaction */
443 2 : papoLayers[iLayer]->SetDropOnCreation(true);
444 2 : DeleteLayer(iLayer);
445 : }
446 : else
447 : {
448 1 : CPLError(CE_Failure, CPLE_AppDefined,
449 : "Layer %s already exists, CreateLayer failed.\n"
450 : "Use the layer creation option OVERWRITE=YES to "
451 : "replace it.",
452 : pszNameIn);
453 1 : return nullptr;
454 : }
455 : }
456 : }
457 :
458 4 : CPLString osName(pszNameIn);
459 4 : if (CPLFetchBool(papszOptions, "LAUNDER", true))
460 : {
461 4 : char *pszTmp = OGRPGCommonLaunderName(pszNameIn, "CARTO", false);
462 4 : osName = pszTmp;
463 4 : CPLFree(pszTmp);
464 : }
465 :
466 4 : OGRCARTOTableLayer *poLayer = new OGRCARTOTableLayer(this, osName);
467 4 : if (bOverwriteOption)
468 2 : poLayer->SetDropOnCreation(true);
469 :
470 : const bool bGeomNullable =
471 4 : CPLFetchBool(papszOptions, "GEOMETRY_NULLABLE", true);
472 4 : int nSRID = poSpatialRef ? FetchSRSId(poSpatialRef) : 0;
473 : bool bCartoify =
474 8 : CPLFetchBool(papszOptions, "CARTODBFY",
475 4 : CPLFetchBool(papszOptions, "CARTODBIFY", true));
476 4 : if (bCartoify)
477 : {
478 2 : if (nSRID != 4326)
479 : {
480 1 : CPLError(CE_Warning, CPLE_AppDefined,
481 : "Cannot register table in dashboard with "
482 : "cdb_cartodbfytable() since its SRS is not EPSG:4326."
483 : " Check the documentation for more information");
484 1 : bCartoify = false;
485 : }
486 1 : else if (eGType == wkbNone)
487 : {
488 0 : CPLError(
489 : CE_Warning, CPLE_AppDefined,
490 : "Cannot register table in dashboard with "
491 : "cdb_cartodbfytable() since its geometry type isn't defined."
492 : " Check the documentation for more information");
493 0 : bCartoify = false;
494 : }
495 : }
496 :
497 4 : poLayer->SetLaunderFlag(CPLFetchBool(papszOptions, "LAUNDER", true));
498 :
499 4 : OGRSpatialReference *poSRSClone = nullptr;
500 4 : if (poSpatialRef)
501 : {
502 1 : poSRSClone = poSpatialRef->Clone();
503 1 : poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
504 : }
505 4 : poLayer->SetDeferredCreation(eGType, poSRSClone, bGeomNullable, bCartoify);
506 4 : if (poSRSClone)
507 1 : poSRSClone->Release();
508 8 : papoLayers = (OGRCARTOTableLayer **)CPLRealloc(
509 4 : papoLayers, (nLayers + 1) * sizeof(OGRCARTOTableLayer *));
510 4 : papoLayers[nLayers++] = poLayer;
511 :
512 4 : return poLayer;
513 : }
514 :
515 : /************************************************************************/
516 : /* DeleteLayer() */
517 : /************************************************************************/
518 :
519 5 : OGRErr OGRCARTODataSource::DeleteLayer(int iLayer)
520 : {
521 5 : if (!bReadWrite)
522 : {
523 1 : CPLError(CE_Failure, CPLE_AppDefined,
524 : "Operation not available in read-only mode");
525 1 : return OGRERR_FAILURE;
526 : }
527 :
528 4 : if (iLayer < 0 || iLayer >= nLayers)
529 : {
530 0 : CPLError(CE_Failure, CPLE_AppDefined,
531 : "Layer %d not in legal range of 0 to %d.", iLayer,
532 0 : nLayers - 1);
533 0 : return OGRERR_FAILURE;
534 : }
535 :
536 : /* -------------------------------------------------------------------- */
537 : /* Blow away our OGR structures related to the layer. This is */
538 : /* pretty dangerous if anything has a reference to this layer! */
539 : /* -------------------------------------------------------------------- */
540 8 : CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName();
541 :
542 4 : CPLDebug("CARTO", "DeleteLayer(%s)", osLayerName.c_str());
543 :
544 4 : int bDeferredCreation = papoLayers[iLayer]->GetDeferredCreation();
545 4 : bool bDropOnCreation = papoLayers[iLayer]->GetDropOnCreation();
546 4 : papoLayers[iLayer]->CancelDeferredCreation();
547 4 : delete papoLayers[iLayer];
548 4 : memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
549 4 : sizeof(void *) * (nLayers - iLayer - 1));
550 4 : nLayers--;
551 :
552 4 : if (osLayerName.empty())
553 0 : return OGRERR_NONE;
554 :
555 4 : if (!bDeferredCreation && !bDropOnCreation)
556 : {
557 2 : CPLString osSQL;
558 : osSQL.Printf("DROP TABLE %s",
559 2 : OGRCARTOEscapeIdentifier(osLayerName).c_str());
560 :
561 2 : json_object *poObj = RunSQL(osSQL);
562 2 : if (poObj == nullptr)
563 1 : return OGRERR_FAILURE;
564 1 : json_object_put(poObj);
565 : }
566 :
567 3 : return OGRERR_NONE;
568 : }
569 :
570 : /************************************************************************/
571 : /* AddHTTPOptions() */
572 : /************************************************************************/
573 :
574 0 : char **OGRCARTODataSource::AddHTTPOptions()
575 : {
576 0 : bMustCleanPersistent = true;
577 :
578 0 : return CSLAddString(nullptr, CPLSPrintf("PERSISTENT=CARTO:%p", this));
579 : }
580 :
581 : /************************************************************************/
582 : /* RunCopyFrom() */
583 : /************************************************************************/
584 :
585 2 : json_object *OGRCARTODataSource::RunCopyFrom(const char *pszSQL,
586 : const char *pszCopyFile)
587 : {
588 :
589 : /* -------------------------------------------------------------------- */
590 : /* Set up our copyfrom end point URL */
591 : /* -------------------------------------------------------------------- */
592 2 : const char *pszAPIURL = GetAPIURL();
593 4 : CPLString osURL(pszAPIURL);
594 2 : osURL += "/copyfrom?q=";
595 :
596 2 : if (!(strlen(pszSQL) > 0))
597 : {
598 0 : CPLDebug("CARTO", "RunCopyFrom: pszSQL is empty");
599 0 : return nullptr;
600 : }
601 :
602 2 : if (!(strlen(pszCopyFile) > 0))
603 : {
604 0 : CPLDebug("CARTO", "RunCopyFrom: pszCopyFile is empty");
605 0 : return nullptr;
606 : }
607 :
608 : /* -------------------------------------------------------------------- */
609 : /* URL encode the COPY sql and add to URL with API key */
610 : /* -------------------------------------------------------------------- */
611 2 : CPLDebug("CARTO", "RunCopyFrom: osCopySQL = %s", pszSQL);
612 2 : char *pszEscapedSQL = CPLEscapeString(pszSQL, -1, CPLES_URL);
613 2 : osURL += pszEscapedSQL;
614 2 : CPLFree(pszEscapedSQL);
615 :
616 2 : if (!osAPIKey.empty())
617 : {
618 2 : osURL += "&api_key=";
619 2 : osURL += osAPIKey;
620 : }
621 :
622 : /* -------------------------------------------------------------------- */
623 : /* Set the POST payload */
624 : /* -------------------------------------------------------------------- */
625 4 : CPLString osSQL("POSTFIELDS=");
626 2 : osSQL += pszCopyFile;
627 :
628 : /* -------------------------------------------------------------------- */
629 : /* Make the HTTP request */
630 : /* -------------------------------------------------------------------- */
631 4 : char **papszOptions = CSLAddString(
632 2 : !STARTS_WITH(pszAPIURL, "/vsimem/") ? AddHTTPOptions() : nullptr,
633 : osSQL);
634 2 : CPLHTTPResult *psResult = CPLHTTPFetch(osURL, papszOptions);
635 2 : CSLDestroy(papszOptions);
636 2 : if (psResult == nullptr)
637 : {
638 0 : CPLDebug("CARTO", "RunCopyFrom: null return from CPLHTTPFetch");
639 0 : return nullptr;
640 : }
641 :
642 : /* -------------------------------------------------------------------- */
643 : /* Check for some error conditions and report. HTML Messages */
644 : /* are transformed info failure. */
645 : /* -------------------------------------------------------------------- */
646 2 : if (psResult->pszContentType &&
647 0 : STARTS_WITH(psResult->pszContentType, "text/html"))
648 : {
649 0 : CPLDebug("CARTO", "RunCopyFrom HTML Response:%s", psResult->pabyData);
650 0 : CPLError(CE_Failure, CPLE_AppDefined,
651 : "HTML error page returned by server");
652 0 : CPLHTTPDestroyResult(psResult);
653 0 : return nullptr;
654 : }
655 2 : if (psResult->pszErrBuf != nullptr)
656 : {
657 1 : CPLError(CE_Failure, CPLE_AppDefined, "RunCopyFrom Error Message:%s",
658 : psResult->pszErrBuf);
659 : }
660 1 : else if (psResult->nStatus != 0)
661 : {
662 0 : CPLError(CE_Failure, CPLE_AppDefined, "RunCopyFrom Error Status:%d",
663 : psResult->nStatus);
664 : }
665 :
666 2 : if (psResult->pabyData == nullptr)
667 : {
668 1 : CPLHTTPDestroyResult(psResult);
669 1 : return nullptr;
670 : }
671 :
672 1 : json_object *poObj = nullptr;
673 1 : const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
674 1 : if (!OGRJSonParse(pszText, &poObj, true))
675 : {
676 0 : CPLDebug("CARTO", "RunCopyFrom unable to parse JSON return: %s",
677 : pszText);
678 0 : CPLHTTPDestroyResult(psResult);
679 0 : return nullptr;
680 : }
681 :
682 1 : CPLHTTPDestroyResult(psResult);
683 :
684 1 : if (poObj != nullptr)
685 : {
686 1 : if (json_object_get_type(poObj) == json_type_object)
687 : {
688 1 : json_object *poError = CPL_json_object_object_get(poObj, "error");
689 1 : if (poError != nullptr &&
690 1 : json_object_get_type(poError) == json_type_array &&
691 0 : json_object_array_length(poError) > 0)
692 : {
693 0 : poError = json_object_array_get_idx(poError, 0);
694 0 : if (poError != nullptr &&
695 0 : json_object_get_type(poError) == json_type_string)
696 : {
697 0 : CPLError(CE_Failure, CPLE_AppDefined,
698 : "Error returned by server : %s",
699 : json_object_get_string(poError));
700 0 : json_object_put(poObj);
701 0 : return nullptr;
702 : }
703 : }
704 : }
705 : else
706 : {
707 0 : json_object_put(poObj);
708 0 : return nullptr;
709 : }
710 : }
711 :
712 1 : return poObj;
713 : }
714 :
715 : /************************************************************************/
716 : /* RunSQL() */
717 : /************************************************************************/
718 :
719 169 : json_object *OGRCARTODataSource::RunSQL(const char *pszUnescapedSQL)
720 : {
721 338 : CPLString osSQL("POSTFIELDS=q=");
722 : /* Do post escaping */
723 35235 : for (int i = 0; pszUnescapedSQL[i] != 0; i++)
724 : {
725 35066 : const int ch = ((unsigned char *)pszUnescapedSQL)[i];
726 35066 : if (ch != '&' && ch >= 32 && ch < 128)
727 35054 : osSQL += (char)ch;
728 : else
729 12 : osSQL += CPLSPrintf("%%%02X", ch);
730 : }
731 :
732 : /* -------------------------------------------------------------------- */
733 : /* Provide the API Key */
734 : /* -------------------------------------------------------------------- */
735 169 : if (!osAPIKey.empty())
736 : {
737 137 : osSQL += "&api_key=";
738 137 : osSQL += osAPIKey;
739 : }
740 :
741 : /* -------------------------------------------------------------------- */
742 : /* Collection the header options and execute request. */
743 : /* -------------------------------------------------------------------- */
744 169 : const char *pszAPIURL = GetAPIURL();
745 338 : char **papszOptions = CSLAddString(
746 169 : !STARTS_WITH(pszAPIURL, "/vsimem/") ? AddHTTPOptions() : nullptr,
747 : osSQL);
748 169 : CPLHTTPResult *psResult = CPLHTTPFetch(GetAPIURL(), papszOptions);
749 169 : CSLDestroy(papszOptions);
750 169 : if (psResult == nullptr)
751 0 : return nullptr;
752 :
753 : /* -------------------------------------------------------------------- */
754 : /* Check for some error conditions and report. HTML Messages */
755 : /* are transformed info failure. */
756 : /* -------------------------------------------------------------------- */
757 169 : if (psResult->pszContentType &&
758 1 : STARTS_WITH(psResult->pszContentType, "text/html"))
759 : {
760 1 : CPLDebug("CARTO", "RunSQL HTML Response:%s", psResult->pabyData);
761 1 : CPLError(CE_Failure, CPLE_AppDefined,
762 : "HTML error page returned by server");
763 1 : CPLHTTPDestroyResult(psResult);
764 1 : return nullptr;
765 : }
766 168 : if (psResult->pszErrBuf != nullptr)
767 : {
768 35 : CPLError(CE_Failure, CPLE_AppDefined, "RunSQL Error Message:%s",
769 : psResult->pszErrBuf);
770 : }
771 133 : else if (psResult->nStatus != 0)
772 : {
773 0 : CPLError(CE_Failure, CPLE_AppDefined, "RunSQL Error Status:%d",
774 : psResult->nStatus);
775 : }
776 :
777 168 : if (psResult->pabyData == nullptr)
778 : {
779 48 : CPLHTTPDestroyResult(psResult);
780 48 : return nullptr;
781 : }
782 :
783 120 : if (strlen((const char *)psResult->pabyData) < 1000)
784 111 : CPLDebug("CARTO", "RunSQL Response:%s", psResult->pabyData);
785 :
786 120 : json_object *poObj = nullptr;
787 120 : const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
788 120 : if (!OGRJSonParse(pszText, &poObj, true))
789 : {
790 1 : CPLHTTPDestroyResult(psResult);
791 1 : return nullptr;
792 : }
793 :
794 119 : CPLHTTPDestroyResult(psResult);
795 :
796 119 : if (poObj != nullptr)
797 : {
798 119 : if (json_object_get_type(poObj) == json_type_object)
799 : {
800 118 : json_object *poError = CPL_json_object_object_get(poObj, "error");
801 119 : if (poError != nullptr &&
802 119 : json_object_get_type(poError) == json_type_array &&
803 1 : json_object_array_length(poError) > 0)
804 : {
805 1 : poError = json_object_array_get_idx(poError, 0);
806 2 : if (poError != nullptr &&
807 1 : json_object_get_type(poError) == json_type_string)
808 : {
809 1 : CPLError(CE_Failure, CPLE_AppDefined,
810 : "Error returned by server : %s",
811 : json_object_get_string(poError));
812 1 : json_object_put(poObj);
813 1 : return nullptr;
814 : }
815 : }
816 : }
817 : else
818 : {
819 1 : json_object_put(poObj);
820 1 : return nullptr;
821 : }
822 : }
823 :
824 117 : return poObj;
825 : }
826 :
827 : /************************************************************************/
828 : /* OGRCARTOGetSingleRow() */
829 : /************************************************************************/
830 :
831 30 : json_object *OGRCARTOGetSingleRow(json_object *poObj)
832 : {
833 30 : if (poObj == nullptr)
834 : {
835 8 : return nullptr;
836 : }
837 :
838 22 : json_object *poRows = CPL_json_object_object_get(poObj, "rows");
839 43 : if (poRows == nullptr || json_object_get_type(poRows) != json_type_array ||
840 21 : json_object_array_length(poRows) != 1)
841 : {
842 1 : return nullptr;
843 : }
844 :
845 21 : json_object *poRowObj = json_object_array_get_idx(poRows, 0);
846 42 : if (poRowObj == nullptr ||
847 21 : json_object_get_type(poRowObj) != json_type_object)
848 : {
849 0 : return nullptr;
850 : }
851 :
852 21 : return poRowObj;
853 : }
854 :
855 : /************************************************************************/
856 : /* ExecuteSQL() */
857 : /************************************************************************/
858 :
859 1 : OGRLayer *OGRCARTODataSource::ExecuteSQL(const char *pszSQLCommand,
860 : OGRGeometry *poSpatialFilter,
861 : const char *pszDialect)
862 :
863 : {
864 1 : return ExecuteSQLInternal(pszSQLCommand, poSpatialFilter, pszDialect, true);
865 : }
866 :
867 91 : OGRLayer *OGRCARTODataSource::ExecuteSQLInternal(const char *pszSQLCommand,
868 : OGRGeometry *poSpatialFilter,
869 : const char *pszDialect,
870 : bool bRunDeferredActions)
871 :
872 : {
873 91 : if (bRunDeferredActions)
874 : {
875 2 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
876 : {
877 1 : papoLayers[iLayer]->RunDeferredCreationIfNecessary();
878 1 : CPL_IGNORE_RET_VAL(papoLayers[iLayer]->FlushDeferredBuffer());
879 1 : papoLayers[iLayer]->RunDeferredCartofy();
880 : }
881 : }
882 :
883 : /* Skip leading spaces */
884 91 : while (*pszSQLCommand == ' ')
885 0 : pszSQLCommand++;
886 :
887 : /* -------------------------------------------------------------------- */
888 : /* Use generic implementation for recognized dialects */
889 : /* -------------------------------------------------------------------- */
890 91 : if (IsGenericSQLDialect(pszDialect))
891 0 : return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter,
892 0 : pszDialect);
893 :
894 : /* -------------------------------------------------------------------- */
895 : /* Special case DELLAYER: command. */
896 : /* -------------------------------------------------------------------- */
897 91 : if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
898 : {
899 1 : const char *pszLayerName = pszSQLCommand + 9;
900 :
901 1 : while (*pszLayerName == ' ')
902 0 : pszLayerName++;
903 :
904 1 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
905 : {
906 1 : if (EQUAL(papoLayers[iLayer]->GetName(), pszLayerName))
907 : {
908 1 : DeleteLayer(iLayer);
909 1 : break;
910 : }
911 : }
912 1 : return nullptr;
913 : }
914 :
915 90 : if (!STARTS_WITH_CI(pszSQLCommand, "SELECT") &&
916 10 : !STARTS_WITH_CI(pszSQLCommand, "EXPLAIN") &&
917 10 : !STARTS_WITH_CI(pszSQLCommand, "WITH"))
918 : {
919 10 : RunSQL(pszSQLCommand);
920 10 : return nullptr;
921 : }
922 :
923 80 : OGRCARTOResultLayer *poLayer = new OGRCARTOResultLayer(this, pszSQLCommand);
924 :
925 80 : if (poSpatialFilter != nullptr)
926 0 : poLayer->SetSpatialFilter(poSpatialFilter);
927 :
928 80 : if (!poLayer->IsOK())
929 : {
930 9 : delete poLayer;
931 9 : return nullptr;
932 : }
933 :
934 71 : return poLayer;
935 : }
936 :
937 : /************************************************************************/
938 : /* ReleaseResultSet() */
939 : /************************************************************************/
940 :
941 71 : void OGRCARTODataSource::ReleaseResultSet(OGRLayer *poLayer)
942 :
943 : {
944 71 : delete poLayer;
945 71 : }
|