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