1 /******************************************************************************
2  * $Id: ogrcartodbdatasource.cpp 29019 2015-04-25 20:34:19Z rouault $
3  *
4  * Project:  CartoDB Translator
5  * Purpose:  Implements OGRCARTODBDataSource class
6  * Author:   Even Rouault, even dot rouault at mines dash paris dot org
7  *
8  ******************************************************************************
9  * Copyright (c) 2013, Even Rouault <even dot rouault at mines-paris dot org>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "ogr_cartodb.h"
31 #include "ogr_pgdump.h"
32 
33 CPL_CVSID("$Id: ogrcartodbdatasource.cpp 29019 2015-04-25 20:34:19Z rouault $");
34 
35 /************************************************************************/
36 /*                        OGRCARTODBDataSource()                        */
37 /************************************************************************/
38 
OGRCARTODBDataSource()39 OGRCARTODBDataSource::OGRCARTODBDataSource()
40 
41 {
42     papoLayers = NULL;
43     nLayers = 0;
44 
45     pszName = NULL;
46     pszAccount = NULL;
47 
48     bReadWrite = FALSE;
49     bBatchInsert = TRUE;
50     bUseHTTPS = FALSE;
51 
52     bMustCleanPersistant = FALSE;
53     bHasOGRMetadataFunction = -1;
54 }
55 
56 /************************************************************************/
57 /*                       ~OGRCARTODBDataSource()                        */
58 /************************************************************************/
59 
~OGRCARTODBDataSource()60 OGRCARTODBDataSource::~OGRCARTODBDataSource()
61 
62 {
63     for( int i = 0; i < nLayers; i++ )
64         delete papoLayers[i];
65     CPLFree( papoLayers );
66 
67     if (bMustCleanPersistant)
68     {
69         char** papszOptions = NULL;
70         papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT", CPLSPrintf("CARTODB:%p", this));
71         CPLHTTPFetch( GetAPIURL(), papszOptions);
72         CSLDestroy(papszOptions);
73     }
74 
75     CPLFree( pszName );
76     CPLFree( pszAccount );
77 }
78 
79 /************************************************************************/
80 /*                           TestCapability()                           */
81 /************************************************************************/
82 
TestCapability(const char * pszCap)83 int OGRCARTODBDataSource::TestCapability( const char * pszCap )
84 
85 {
86     if( bReadWrite && EQUAL(pszCap,ODsCCreateLayer) )
87         return TRUE;
88     else if( bReadWrite && EQUAL(pszCap,ODsCDeleteLayer) )
89         return TRUE;
90     else
91         return FALSE;
92 }
93 
94 /************************************************************************/
95 /*                              GetLayer()                              */
96 /************************************************************************/
97 
GetLayer(int iLayer)98 OGRLayer *OGRCARTODBDataSource::GetLayer( int iLayer )
99 
100 {
101     if( iLayer < 0 || iLayer >= nLayers )
102         return NULL;
103     else
104         return papoLayers[iLayer];
105 }
106 
107 /************************************************************************/
108 /*                          GetLayerByName()                            */
109 /************************************************************************/
110 
GetLayerByName(const char * pszLayerName)111 OGRLayer *OGRCARTODBDataSource::GetLayerByName(const char * pszLayerName)
112 {
113     OGRLayer* poLayer = OGRDataSource::GetLayerByName(pszLayerName);
114     return poLayer;
115 }
116 
117 /************************************************************************/
118 /*                     OGRCARTODBGetOptionValue()                       */
119 /************************************************************************/
120 
OGRCARTODBGetOptionValue(const char * pszFilename,const char * pszOptionName)121 CPLString OGRCARTODBGetOptionValue(const char* pszFilename,
122                                const char* pszOptionName)
123 {
124     CPLString osOptionName(pszOptionName);
125     osOptionName += "=";
126     const char* pszOptionValue = strstr(pszFilename, osOptionName);
127     if (!pszOptionValue)
128         return "";
129 
130     CPLString osOptionValue(pszOptionValue + strlen(osOptionName));
131     const char* pszSpace = strchr(osOptionValue.c_str(), ' ');
132     if (pszSpace)
133         osOptionValue.resize(pszSpace - osOptionValue.c_str());
134     return osOptionValue;
135 }
136 
137 /************************************************************************/
138 /*                                Open()                                */
139 /************************************************************************/
140 
Open(const char * pszFilename,char ** papszOpenOptions,int bUpdateIn)141 int OGRCARTODBDataSource::Open( const char * pszFilename,
142                                 char** papszOpenOptions,
143                                 int bUpdateIn )
144 
145 {
146     bReadWrite = bUpdateIn;
147     bBatchInsert = CSLTestBoolean(CSLFetchNameValueDef(papszOpenOptions, "BATCH_INSERT", "YES"));
148 
149     pszName = CPLStrdup( pszFilename );
150     if( CSLFetchNameValue(papszOpenOptions, "ACCOUNT") )
151         pszAccount = CPLStrdup(CSLFetchNameValue(papszOpenOptions, "ACCOUNT"));
152     else
153     {
154         pszAccount = CPLStrdup(pszFilename + strlen("CARTODB:"));
155         char* pchSpace = strchr(pszAccount, ' ');
156         if( pchSpace )
157             *pchSpace = '\0';
158         if( pszAccount[0] == 0 )
159         {
160             CPLError(CE_Failure, CPLE_AppDefined, "Missing account name");
161             return FALSE;
162         }
163     }
164 
165     osAPIKey = CSLFetchNameValueDef(papszOpenOptions, "API_KEY",
166                                     CPLGetConfigOption("CARTODB_API_KEY", ""));
167 
168     CPLString osTables = OGRCARTODBGetOptionValue(pszFilename, "tables");
169 
170     /*if( osTables.size() == 0 && osAPIKey.size() == 0 )
171     {
172         CPLError(CE_Failure, CPLE_AppDefined,
173                  "When not specifying tables option, CARTODB_API_KEY must be defined");
174         return FALSE;
175     }*/
176 
177     bUseHTTPS = CSLTestBoolean(CPLGetConfigOption("CARTODB_HTTPS", "YES"));
178 
179     OGRLayer* poSchemaLayer = ExecuteSQLInternal("SELECT current_schema()");
180     if( poSchemaLayer )
181     {
182         OGRFeature* poFeat = poSchemaLayer->GetNextFeature();
183         if( poFeat )
184         {
185             if( poFeat->GetFieldCount() == 1 )
186             {
187                 osCurrentSchema = poFeat->GetFieldAsString(0);
188             }
189             delete poFeat;
190         }
191         ReleaseResultSet(poSchemaLayer);
192     }
193     if( osCurrentSchema.size() == 0 )
194         return FALSE;
195 
196     if( osAPIKey.size() && bUpdateIn )
197     {
198         ExecuteSQLInternal(
199                 "DROP FUNCTION IF EXISTS ogr_table_metadata(TEXT,TEXT); "
200                 "CREATE OR REPLACE FUNCTION ogr_table_metadata(schema_name TEXT, table_name TEXT) RETURNS TABLE "
201                 "(attname TEXT, typname TEXT, attlen INT, format_type TEXT, "
202                 "attnum INT, attnotnull BOOLEAN, indisprimary BOOLEAN, "
203                 "defaultexpr TEXT, dim INT, srid INT, geomtyp TEXT, srtext TEXT) AS $$ "
204                 "SELECT a.attname::text, t.typname::text, a.attlen::int, "
205                         "format_type(a.atttypid,a.atttypmod)::text, "
206                         "a.attnum::int, "
207                         "a.attnotnull::boolean, "
208                         "i.indisprimary::boolean, "
209                         "pg_get_expr(def.adbin, c.oid)::text AS defaultexpr, "
210                         "(CASE WHEN t.typname = 'geometry' THEN postgis_typmod_dims(a.atttypmod) ELSE NULL END)::int dim, "
211                         "(CASE WHEN t.typname = 'geometry' THEN postgis_typmod_srid(a.atttypmod) ELSE NULL END)::int srid, "
212                         "(CASE WHEN t.typname = 'geometry' THEN postgis_typmod_type(a.atttypmod) ELSE NULL END)::text geomtyp, "
213                         "srtext "
214                 "FROM pg_class c "
215                 "JOIN pg_attribute a ON a.attnum > 0 AND "
216                                         "a.attrelid = c.oid AND c.relname = $2 "
217                                         "AND c.relname IN (SELECT CDB_UserTables())"
218                 "JOIN pg_type t ON a.atttypid = t.oid "
219                 "JOIN pg_namespace n ON c.relnamespace=n.oid AND n.nspname = $1 "
220                 "LEFT JOIN pg_index i ON c.oid = i.indrelid AND "
221                                         "i.indisprimary = 't' AND a.attnum = ANY(i.indkey) "
222                 "LEFT JOIN pg_attrdef def ON def.adrelid = c.oid AND "
223                                             "def.adnum = a.attnum "
224                 "LEFT JOIN spatial_ref_sys srs ON srs.srid = postgis_typmod_srid(a.atttypmod) "
225                 "ORDER BY a.attnum "
226                 "$$ LANGUAGE SQL");
227     }
228 
229     if (osTables.size() != 0)
230     {
231         char** papszTables = CSLTokenizeString2(osTables, ",", 0);
232         for(int i=0;papszTables && papszTables[i];i++)
233         {
234             papoLayers = (OGRCARTODBTableLayer**) CPLRealloc(
235                 papoLayers, (nLayers + 1) * sizeof(OGRCARTODBTableLayer*));
236             papoLayers[nLayers ++] = new OGRCARTODBTableLayer(this, papszTables[i]);
237         }
238         CSLDestroy(papszTables);
239         return TRUE;
240     }
241 
242     OGRLayer* poTableListLayer = ExecuteSQLInternal("SELECT CDB_UserTables()");
243     if( poTableListLayer )
244     {
245         OGRFeature* poFeat;
246         while( (poFeat = poTableListLayer->GetNextFeature()) != NULL )
247         {
248             if( poFeat->GetFieldCount() == 1 )
249             {
250                 papoLayers = (OGRCARTODBTableLayer**) CPLRealloc(
251                     papoLayers, (nLayers + 1) * sizeof(OGRCARTODBTableLayer*));
252                 papoLayers[nLayers ++] = new OGRCARTODBTableLayer(
253                             this, poFeat->GetFieldAsString(0));
254             }
255             delete poFeat;
256         }
257         ReleaseResultSet(poTableListLayer);
258     }
259     else if( osCurrentSchema == "public" )
260         return FALSE;
261 
262     /* There's currently a bug with CDB_UserTables() on multi-user accounts */
263     if( nLayers == 0 && osCurrentSchema != "public" )
264     {
265         CPLString osSQL;
266         osSQL.Printf("SELECT c.relname FROM pg_class c, pg_namespace n "
267                      "WHERE c.relkind in ('r', 'v') AND c.relname !~ '^pg_' AND c.relnamespace=n.oid AND n.nspname = '%s'",
268                      OGRCARTODBEscapeLiteral(osCurrentSchema).c_str());
269         poTableListLayer = ExecuteSQLInternal(osSQL);
270         if( poTableListLayer )
271         {
272             OGRFeature* poFeat;
273             while( (poFeat = poTableListLayer->GetNextFeature()) != NULL )
274             {
275                 if( poFeat->GetFieldCount() == 1 )
276                 {
277                     papoLayers = (OGRCARTODBTableLayer**) CPLRealloc(
278                         papoLayers, (nLayers + 1) * sizeof(OGRCARTODBTableLayer*));
279                     papoLayers[nLayers ++] = new OGRCARTODBTableLayer(
280                                 this, poFeat->GetFieldAsString(0));
281                 }
282                 delete poFeat;
283             }
284             ReleaseResultSet(poTableListLayer);
285         }
286         else
287             return FALSE;
288     }
289 
290     return TRUE;
291 }
292 
293 /************************************************************************/
294 /*                            GetAPIURL()                               */
295 /************************************************************************/
296 
GetAPIURL() const297 const char* OGRCARTODBDataSource::GetAPIURL() const
298 {
299     const char* pszAPIURL = CPLGetConfigOption("CARTODB_API_URL", NULL);
300     if (pszAPIURL)
301         return pszAPIURL;
302     else if (bUseHTTPS)
303         return CPLSPrintf("https://%s.cartodb.com/api/v2/sql", pszAccount);
304     else
305         return CPLSPrintf("http://%s.cartodb.com/api/v2/sql", pszAccount);
306 }
307 
308 /************************************************************************/
309 /*                             FetchSRSId()                             */
310 /************************************************************************/
311 
FetchSRSId(OGRSpatialReference * poSRS)312 int OGRCARTODBDataSource::FetchSRSId( OGRSpatialReference * poSRS )
313 
314 {
315     const char*         pszAuthorityName;
316 
317     if( poSRS == NULL )
318         return 0;
319 
320     OGRSpatialReference oSRS(*poSRS);
321     poSRS = NULL;
322 
323     pszAuthorityName = oSRS.GetAuthorityName(NULL);
324 
325     if( pszAuthorityName == NULL || strlen(pszAuthorityName) == 0 )
326     {
327 /* -------------------------------------------------------------------- */
328 /*      Try to identify an EPSG code                                    */
329 /* -------------------------------------------------------------------- */
330         oSRS.AutoIdentifyEPSG();
331 
332         pszAuthorityName = oSRS.GetAuthorityName(NULL);
333         if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG"))
334         {
335             const char* pszAuthorityCode = oSRS.GetAuthorityCode(NULL);
336             if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 )
337             {
338                 /* Import 'clean' SRS */
339                 oSRS.importFromEPSG( atoi(pszAuthorityCode) );
340 
341                 pszAuthorityName = oSRS.GetAuthorityName(NULL);
342             }
343         }
344     }
345 /* -------------------------------------------------------------------- */
346 /*      Check whether the EPSG authority code is already mapped to a    */
347 /*      SRS ID.                                                         */
348 /* -------------------------------------------------------------------- */
349     if( pszAuthorityName != NULL && EQUAL( pszAuthorityName, "EPSG" ) )
350     {
351         int             nAuthorityCode;
352 
353         /* For the root authority name 'EPSG', the authority code
354          * should always be integral
355          */
356         nAuthorityCode = atoi( oSRS.GetAuthorityCode(NULL) );
357 
358         return nAuthorityCode;
359     }
360 
361     return 0;
362 }
363 
364 /************************************************************************/
365 /*                          ICreateLayer()                              */
366 /************************************************************************/
367 
ICreateLayer(const char * pszName,OGRSpatialReference * poSpatialRef,OGRwkbGeometryType eGType,char ** papszOptions)368 OGRLayer   *OGRCARTODBDataSource::ICreateLayer( const char *pszName,
369                                            OGRSpatialReference *poSpatialRef,
370                                            OGRwkbGeometryType eGType,
371                                            char ** papszOptions )
372 {
373     if (!bReadWrite)
374     {
375         CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode");
376         return NULL;
377     }
378 
379 /* -------------------------------------------------------------------- */
380 /*      Do we already have this layer?  If so, should we blow it        */
381 /*      away?                                                           */
382 /* -------------------------------------------------------------------- */
383     int iLayer;
384 
385     for( iLayer = 0; iLayer < nLayers; iLayer++ )
386     {
387         if( EQUAL(pszName,papoLayers[iLayer]->GetName()) )
388         {
389             if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL
390                 && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
391             {
392                 DeleteLayer( iLayer );
393             }
394             else
395             {
396                 CPLError( CE_Failure, CPLE_AppDefined,
397                           "Layer %s already exists, CreateLayer failed.\n"
398                           "Use the layer creation option OVERWRITE=YES to "
399                           "replace it.",
400                           pszName );
401                 return NULL;
402             }
403         }
404     }
405 
406     CPLString osName(pszName);
407     if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) )
408     {
409         char* pszTmp = OGRPGCommonLaunderName(pszName);
410         osName = pszTmp;
411         CPLFree(pszTmp);
412     }
413 
414     OGRCARTODBTableLayer* poLayer = new OGRCARTODBTableLayer(this, osName);
415     int bGeomNullable = CSLFetchBoolean(papszOptions, "GEOMETRY_NULLABLE", TRUE);
416     int bCartoDBify = CSLFetchBoolean(papszOptions, "CARTODBFY",
417                                       CSLFetchBoolean(papszOptions, "CARTODBIFY", TRUE));
418     poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) );
419     poLayer->SetDeferedCreation(eGType, poSpatialRef, bGeomNullable, bCartoDBify);
420     papoLayers = (OGRCARTODBTableLayer**) CPLRealloc(
421                     papoLayers, (nLayers + 1) * sizeof(OGRCARTODBTableLayer*));
422     papoLayers[nLayers ++] = poLayer;
423 
424     return poLayer;
425 }
426 
427 /************************************************************************/
428 /*                            DeleteLayer()                             */
429 /************************************************************************/
430 
DeleteLayer(int iLayer)431 OGRErr OGRCARTODBDataSource::DeleteLayer(int iLayer)
432 {
433     if (!bReadWrite)
434     {
435         CPLError(CE_Failure, CPLE_AppDefined,
436                  "Operation not available in read-only mode");
437         return OGRERR_FAILURE;
438     }
439 
440     if( iLayer < 0 || iLayer >= nLayers )
441     {
442         CPLError( CE_Failure, CPLE_AppDefined,
443                   "Layer %d not in legal range of 0 to %d.",
444                   iLayer, nLayers-1 );
445         return OGRERR_FAILURE;
446     }
447 
448 /* -------------------------------------------------------------------- */
449 /*      Blow away our OGR structures related to the layer.  This is     */
450 /*      pretty dangerous if anything has a reference to this layer!     */
451 /* -------------------------------------------------------------------- */
452     CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName();
453 
454     CPLDebug( "CARTODB", "DeleteLayer(%s)", osLayerName.c_str() );
455 
456     int bDeferedCreation = papoLayers[iLayer]->GetDeferedCreation();
457     papoLayers[iLayer]->CancelDeferedCreation();
458     delete papoLayers[iLayer];
459     memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
460              sizeof(void *) * (nLayers - iLayer - 1) );
461     nLayers--;
462 
463     if (osLayerName.size() == 0)
464         return OGRERR_NONE;
465 
466     if( !bDeferedCreation )
467     {
468         CPLString osSQL;
469         osSQL.Printf("DROP TABLE %s",
470                     OGRCARTODBEscapeIdentifier(osLayerName).c_str());
471 
472         json_object* poObj = RunSQL(osSQL);
473         if( poObj == NULL )
474             return OGRERR_FAILURE;
475         json_object_put(poObj);
476     }
477 
478     return OGRERR_NONE;
479 }
480 
481 /************************************************************************/
482 /*                          AddHTTPOptions()                            */
483 /************************************************************************/
484 
AddHTTPOptions()485 char** OGRCARTODBDataSource::AddHTTPOptions()
486 {
487     bMustCleanPersistant = TRUE;
488 
489     return CSLAddString(NULL, CPLSPrintf("PERSISTENT=CARTODB:%p", this));
490 }
491 
492 /************************************************************************/
493 /*                               RunSQL()                               */
494 /************************************************************************/
495 
RunSQL(const char * pszUnescapedSQL)496 json_object* OGRCARTODBDataSource::RunSQL(const char* pszUnescapedSQL)
497 {
498     CPLString osSQL("POSTFIELDS=q=");
499     /* Do post escaping */
500     for(int i=0;pszUnescapedSQL[i] != 0;i++)
501     {
502         const int ch = ((unsigned char*)pszUnescapedSQL)[i];
503         if (ch != '&' && ch >= 32 && ch < 128)
504             osSQL += (char)ch;
505         else
506             osSQL += CPLSPrintf("%%%02X", ch);
507     }
508 
509 /* -------------------------------------------------------------------- */
510 /*      Provide the API Key                                             */
511 /* -------------------------------------------------------------------- */
512     if( osAPIKey.size() )
513     {
514         osSQL += "&api_key=";
515         osSQL += osAPIKey;
516     }
517 
518 /* -------------------------------------------------------------------- */
519 /*      Collection the header options and execute request.              */
520 /* -------------------------------------------------------------------- */
521     const char* pszAPIURL = GetAPIURL();
522     char** papszOptions = CSLAddString(
523         strncmp(pszAPIURL, "/vsimem/", strlen("/vsimem/")) != 0 ? AddHTTPOptions(): NULL, osSQL);
524     CPLHTTPResult * psResult = CPLHTTPFetch( GetAPIURL(), papszOptions);
525     CSLDestroy(papszOptions);
526 
527 /* -------------------------------------------------------------------- */
528 /*      Check for some error conditions and report.  HTML Messages      */
529 /*      are transformed info failure.                                   */
530 /* -------------------------------------------------------------------- */
531     if (psResult && psResult->pszContentType &&
532         strncmp(psResult->pszContentType, "text/html", 9) == 0)
533     {
534         CPLDebug( "CARTODB", "RunSQL HTML Response:%s", psResult->pabyData );
535         CPLError(CE_Failure, CPLE_AppDefined,
536                  "HTML error page returned by server");
537         CPLHTTPDestroyResult(psResult);
538         return NULL;
539     }
540     if (psResult && psResult->pszErrBuf != NULL)
541     {
542         CPLDebug( "CARTODB", "RunSQL Error Message:%s", psResult->pszErrBuf );
543     }
544     else if (psResult && psResult->nStatus != 0)
545     {
546         CPLDebug( "CARTODB", "RunSQL Error Status:%d", psResult->nStatus );
547     }
548 
549     if( psResult->pabyData == NULL )
550     {
551         CPLHTTPDestroyResult(psResult);
552         return NULL;
553     }
554 
555     if( strlen((const char*)psResult->pabyData) < 1000 )
556         CPLDebug( "CARTODB", "RunSQL Response:%s", psResult->pabyData );
557 
558     json_tokener* jstok = NULL;
559     json_object* poObj = NULL;
560 
561     jstok = json_tokener_new();
562     poObj = json_tokener_parse_ex(jstok, (const char*) psResult->pabyData, -1);
563     if( jstok->err != json_tokener_success)
564     {
565         CPLError( CE_Failure, CPLE_AppDefined,
566                     "JSON parsing error: %s (at offset %d)",
567                     json_tokener_error_desc(jstok->err), jstok->char_offset);
568         json_tokener_free(jstok);
569         CPLHTTPDestroyResult(psResult);
570         return NULL;
571     }
572     json_tokener_free(jstok);
573 
574     CPLHTTPDestroyResult(psResult);
575 
576     if( poObj != NULL )
577     {
578         if( json_object_get_type(poObj) == json_type_object )
579         {
580             json_object* poError = json_object_object_get(poObj, "error");
581             if( poError != NULL && json_object_get_type(poError) == json_type_array &&
582                 json_object_array_length(poError) > 0 )
583             {
584                 poError = json_object_array_get_idx(poError, 0);
585                 if( poError != NULL && json_object_get_type(poError) == json_type_string )
586                 {
587                     CPLError(CE_Failure, CPLE_AppDefined,
588                             "Error returned by server : %s", json_object_get_string(poError));
589                     json_object_put(poObj);
590                     return NULL;
591                 }
592             }
593         }
594         else
595         {
596             json_object_put(poObj);
597             return NULL;
598         }
599     }
600 
601     return poObj;
602 }
603 
604 /************************************************************************/
605 /*                        OGRCARTODBGetSingleRow()                      */
606 /************************************************************************/
607 
OGRCARTODBGetSingleRow(json_object * poObj)608 json_object* OGRCARTODBGetSingleRow(json_object* poObj)
609 {
610     if( poObj == NULL )
611     {
612         return NULL;
613     }
614 
615     json_object* poRows = json_object_object_get(poObj, "rows");
616     if( poRows == NULL ||
617         json_object_get_type(poRows) != json_type_array ||
618         json_object_array_length(poRows) != 1 )
619     {
620         return NULL;
621     }
622 
623     json_object* poRowObj = json_object_array_get_idx(poRows, 0);
624     if( poRowObj == NULL || json_object_get_type(poRowObj) != json_type_object )
625     {
626         return NULL;
627     }
628 
629     return poRowObj;
630 }
631 
632 /************************************************************************/
633 /*                             ExecuteSQL()                             */
634 /************************************************************************/
635 
ExecuteSQL(const char * pszSQLCommand,OGRGeometry * poSpatialFilter,const char * pszDialect)636 OGRLayer * OGRCARTODBDataSource::ExecuteSQL( const char *pszSQLCommand,
637                                         OGRGeometry *poSpatialFilter,
638                                         const char *pszDialect )
639 
640 {
641     return ExecuteSQLInternal(pszSQLCommand, poSpatialFilter, pszDialect,
642                               TRUE);
643 }
644 
ExecuteSQLInternal(const char * pszSQLCommand,OGRGeometry * poSpatialFilter,const char * pszDialect,int bRunDeferedActions)645 OGRLayer * OGRCARTODBDataSource::ExecuteSQLInternal( const char *pszSQLCommand,
646                                                      OGRGeometry *poSpatialFilter,
647                                                      const char *pszDialect,
648                                                      int bRunDeferedActions )
649 
650 {
651     if( bRunDeferedActions )
652     {
653         for( int iLayer = 0; iLayer < nLayers; iLayer++ )
654         {
655             papoLayers[iLayer]->RunDeferedCreationIfNecessary();
656             papoLayers[iLayer]->FlushDeferedInsert();
657         }
658     }
659 
660     /* Skip leading spaces */
661     while(*pszSQLCommand == ' ')
662         pszSQLCommand ++;
663 
664 /* -------------------------------------------------------------------- */
665 /*      Use generic implementation for recognized dialects              */
666 /* -------------------------------------------------------------------- */
667     if( IsGenericSQLDialect(pszDialect) )
668         return OGRDataSource::ExecuteSQL( pszSQLCommand,
669                                           poSpatialFilter,
670                                           pszDialect );
671 
672 /* -------------------------------------------------------------------- */
673 /*      Special case DELLAYER: command.                                 */
674 /* -------------------------------------------------------------------- */
675     if( EQUALN(pszSQLCommand,"DELLAYER:",9) )
676     {
677         const char *pszLayerName = pszSQLCommand + 9;
678 
679         while( *pszLayerName == ' ' )
680             pszLayerName++;
681 
682         for( int iLayer = 0; iLayer < nLayers; iLayer++ )
683         {
684             if( EQUAL(papoLayers[iLayer]->GetName(),
685                       pszLayerName ))
686             {
687                 DeleteLayer( iLayer );
688                 break;
689             }
690         }
691         return NULL;
692     }
693 
694     if( !EQUALN(pszSQLCommand, "SELECT", strlen("SELECT")) &&
695         !EQUALN(pszSQLCommand, "EXPLAIN", strlen("EXPLAIN")) &&
696         !EQUALN(pszSQLCommand, "WITH", strlen("WITH")) )
697     {
698         RunSQL(pszSQLCommand);
699         return NULL;
700     }
701 
702     OGRCARTODBResultLayer* poLayer = new OGRCARTODBResultLayer( this, pszSQLCommand );
703 
704     if( poSpatialFilter != NULL )
705         poLayer->SetSpatialFilter( poSpatialFilter );
706 
707     if( !poLayer->IsOK() )
708     {
709         delete poLayer;
710         return NULL;
711     }
712 
713     return poLayer;
714 }
715 
716 /************************************************************************/
717 /*                          ReleaseResultSet()                          */
718 /************************************************************************/
719 
ReleaseResultSet(OGRLayer * poLayer)720 void OGRCARTODBDataSource::ReleaseResultSet( OGRLayer * poLayer )
721 
722 {
723     delete poLayer;
724 }
725