1 : /******************************************************************************
2 : * $Id: ogrcouchdbdatasource.cpp 25311 2012-12-15 12:48:14Z rouault $
3 : *
4 : * Project: CouchDB Translator
5 : * Purpose: Implements OGRCouchDBDataSource class
6 : * Author: Even Rouault, even dot rouault at mines dash paris dot org
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2011, Even Rouault <even dot rouault at mines dash 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_couchdb.h"
31 : #include "swq.h"
32 :
33 : CPL_CVSID("$Id: ogrcouchdbdatasource.cpp 25311 2012-12-15 12:48:14Z rouault $");
34 :
35 : /************************************************************************/
36 : /* OGRCouchDBDataSource() */
37 : /************************************************************************/
38 :
39 96 : OGRCouchDBDataSource::OGRCouchDBDataSource()
40 :
41 : {
42 96 : papoLayers = NULL;
43 96 : nLayers = 0;
44 :
45 96 : pszName = NULL;
46 :
47 96 : bReadWrite = FALSE;
48 :
49 96 : bMustCleanPersistant = FALSE;
50 96 : }
51 :
52 : /************************************************************************/
53 : /* ~OGRCouchDBDataSource() */
54 : /************************************************************************/
55 :
56 96 : OGRCouchDBDataSource::~OGRCouchDBDataSource()
57 :
58 : {
59 112 : for( int i = 0; i < nLayers; i++ )
60 16 : delete papoLayers[i];
61 96 : CPLFree( papoLayers );
62 :
63 96 : if (bMustCleanPersistant)
64 : {
65 : char** papszOptions = CSLAddString(NULL,
66 16 : CPLSPrintf("CLOSE_PERSISTENT=CouchDB:%p", this));
67 16 : CPLHTTPFetch( osURL, papszOptions);
68 16 : CSLDestroy(papszOptions);
69 : }
70 :
71 96 : CPLFree( pszName );
72 96 : }
73 :
74 : /************************************************************************/
75 : /* TestCapability() */
76 : /************************************************************************/
77 :
78 0 : int OGRCouchDBDataSource::TestCapability( const char * pszCap )
79 :
80 : {
81 0 : if( bReadWrite && EQUAL(pszCap,ODsCCreateLayer) )
82 0 : return TRUE;
83 0 : else if( bReadWrite && EQUAL(pszCap,ODsCDeleteLayer) )
84 0 : return TRUE;
85 : else
86 0 : return FALSE;
87 : }
88 :
89 : /************************************************************************/
90 : /* GetLayer() */
91 : /************************************************************************/
92 :
93 17 : OGRLayer *OGRCouchDBDataSource::GetLayer( int iLayer )
94 :
95 : {
96 17 : if( iLayer < 0 || iLayer >= nLayers )
97 0 : return NULL;
98 : else
99 17 : return papoLayers[iLayer];
100 : }
101 :
102 : /************************************************************************/
103 : /* GetLayerByName() */
104 : /************************************************************************/
105 :
106 1 : OGRLayer *OGRCouchDBDataSource::GetLayerByName(const char * pszLayerName)
107 : {
108 1 : OGRLayer* poLayer = OGRDataSource::GetLayerByName(pszLayerName);
109 1 : if (poLayer)
110 1 : return poLayer;
111 :
112 0 : return OpenDatabase(pszLayerName);
113 : }
114 :
115 : /************************************************************************/
116 : /* OpenDatabase() */
117 : /************************************************************************/
118 :
119 15 : OGRLayer* OGRCouchDBDataSource::OpenDatabase(const char* pszLayerName)
120 : {
121 15 : CPLString osTableName;
122 15 : CPLString osEscapedName;
123 15 : if (pszLayerName)
124 : {
125 0 : osTableName = pszLayerName;
126 0 : char* pszEscapedName = CPLEscapeString(pszLayerName, -1, CPLES_URL);
127 0 : osEscapedName = pszEscapedName;
128 0 : CPLFree(pszEscapedName);
129 : }
130 : else
131 : {
132 15 : char* pszURL = CPLStrdup(osURL);
133 15 : char* pszLastSlash = strrchr(pszURL, '/');
134 15 : if (pszLastSlash)
135 : {
136 15 : osEscapedName = pszLastSlash + 1;
137 15 : char* pszName = CPLUnescapeString(osEscapedName, NULL, CPLES_URL);
138 15 : osTableName = pszName;
139 15 : CPLFree(pszName);
140 15 : *pszLastSlash = 0;
141 : }
142 15 : osURL = pszURL;
143 15 : CPLFree(pszURL);
144 15 : pszURL = NULL;
145 :
146 15 : if (pszLastSlash == NULL)
147 0 : return NULL;
148 : }
149 :
150 15 : CPLString osURI("/");
151 15 : osURI += osEscapedName;
152 :
153 15 : json_object* poAnswerObj = GET(osURI);
154 15 : if (poAnswerObj == NULL)
155 0 : return NULL;
156 :
157 15 : if ( !json_object_is_type(poAnswerObj, json_type_object) ||
158 : json_object_object_get(poAnswerObj, "db_name") == NULL )
159 : {
160 0 : IsError(poAnswerObj, "Database opening failed");
161 :
162 0 : json_object_put(poAnswerObj);
163 0 : return NULL;
164 : }
165 :
166 15 : OGRCouchDBTableLayer* poLayer = new OGRCouchDBTableLayer(this, osTableName);
167 :
168 30 : if ( json_object_object_get(poAnswerObj, "update_seq") != NULL )
169 : {
170 15 : int nUpdateSeq = json_object_get_int(json_object_object_get(poAnswerObj, "update_seq"));
171 15 : poLayer->SetUpdateSeq(nUpdateSeq);
172 : }
173 :
174 15 : json_object_put(poAnswerObj);
175 :
176 15 : papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
177 15 : papoLayers[nLayers ++] = poLayer;
178 :
179 15 : return poLayer;
180 : }
181 :
182 : /************************************************************************/
183 : /* OpenView() */
184 : /************************************************************************/
185 :
186 1 : OGRLayer* OGRCouchDBDataSource::OpenView()
187 : {
188 1 : OGRCouchDBRowsLayer* poLayer = new OGRCouchDBRowsLayer(this);
189 1 : if (!poLayer->BuildFeatureDefn())
190 : {
191 0 : delete poLayer;
192 0 : return NULL;
193 : }
194 :
195 1 : papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
196 1 : papoLayers[nLayers ++] = poLayer;
197 :
198 1 : return poLayer;
199 : }
200 :
201 : /************************************************************************/
202 : /* Open() */
203 : /************************************************************************/
204 :
205 96 : int OGRCouchDBDataSource::Open( const char * pszFilename, int bUpdateIn)
206 :
207 : {
208 96 : int bHTTP = FALSE;
209 96 : if (strncmp(pszFilename, "http://", 7) == 0 ||
210 : strncmp(pszFilename, "https://", 8) == 0)
211 0 : bHTTP = TRUE;
212 96 : else if (!EQUALN(pszFilename, "CouchDB:", 8))
213 80 : return FALSE;
214 :
215 16 : bReadWrite = bUpdateIn;
216 :
217 16 : pszName = CPLStrdup( pszFilename );
218 :
219 16 : if (bHTTP)
220 0 : osURL = pszFilename;
221 : else
222 16 : osURL = pszFilename + 8;
223 16 : if (osURL.size() > 0 && osURL[osURL.size() - 1] == '/')
224 0 : osURL.resize(osURL.size() - 1);
225 :
226 16 : const char* pszUserPwd = CPLGetConfigOption("COUCHDB_USERPWD", NULL);
227 16 : if (pszUserPwd)
228 0 : osUserPwd = pszUserPwd;
229 :
230 :
231 16 : if ((strstr(osURL, "/_design/") && strstr(osURL, "/_view/")) ||
232 : strstr(osURL, "/_all_docs"))
233 : {
234 1 : return OpenView() != NULL;
235 : }
236 :
237 : /* If passed with http://useraccount.knownprovider.com/database, do not */
238 : /* try to issue /all_dbs, but directly open the database */
239 15 : const char* pszKnowProvider = strstr(osURL, ".iriscouch.com/");
240 15 : if (pszKnowProvider != NULL &&
241 : strchr(pszKnowProvider + strlen(".iriscouch.com/"), '/' ) == NULL)
242 : {
243 15 : return OpenDatabase() != NULL;
244 : }
245 0 : pszKnowProvider = strstr(osURL, ".cloudant.com/");
246 0 : if (pszKnowProvider != NULL &&
247 : strchr(pszKnowProvider + strlen(".cloudant.com/"), '/' ) == NULL)
248 : {
249 0 : return OpenDatabase() != NULL;
250 : }
251 :
252 : /* Get list of tables */
253 0 : json_object* poAnswerObj = GET("/_all_dbs");
254 :
255 0 : if (poAnswerObj == NULL)
256 0 : return FALSE;
257 :
258 0 : if ( !json_object_is_type(poAnswerObj, json_type_array) )
259 : {
260 0 : if ( json_object_is_type(poAnswerObj, json_type_object) )
261 : {
262 0 : json_object* poError = json_object_object_get(poAnswerObj, "error");
263 0 : json_object* poReason = json_object_object_get(poAnswerObj, "reason");
264 :
265 0 : const char* pszError = json_object_get_string(poError);
266 0 : const char* pszReason = json_object_get_string(poReason);
267 :
268 0 : if (pszError && pszReason && strcmp(pszError, "not_found") == 0 &&
269 : strcmp(pszReason, "missing") == 0)
270 : {
271 0 : json_object_put(poAnswerObj);
272 0 : poAnswerObj = NULL;
273 :
274 0 : CPLErrorReset();
275 :
276 0 : return OpenDatabase() != NULL;
277 : }
278 : }
279 0 : if (poAnswerObj)
280 : {
281 0 : IsError(poAnswerObj, "Database listing failed");
282 :
283 0 : json_object_put(poAnswerObj);
284 0 : return FALSE;
285 : }
286 : }
287 :
288 0 : int nTables = json_object_array_length(poAnswerObj);
289 0 : for(int i=0;i<nTables;i++)
290 : {
291 0 : json_object* poAnswerObjDBName = json_object_array_get_idx(poAnswerObj, i);
292 0 : if ( json_object_is_type(poAnswerObjDBName, json_type_string) )
293 : {
294 0 : const char* pszDBName = json_object_get_string(poAnswerObjDBName);
295 0 : if ( strcmp(pszDBName, "_users") != 0 &&
296 : strcmp(pszDBName, "_replicator") != 0 )
297 : {
298 0 : papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
299 0 : papoLayers[nLayers ++] = new OGRCouchDBTableLayer(this, pszDBName);
300 : }
301 : }
302 : }
303 :
304 0 : json_object_put(poAnswerObj);
305 :
306 0 : return TRUE;
307 : }
308 :
309 :
310 : /************************************************************************/
311 : /* CreateLayer() */
312 : /************************************************************************/
313 :
314 0 : OGRLayer *OGRCouchDBDataSource::CreateLayer( const char *pszName,
315 : OGRSpatialReference *poSpatialRef,
316 : OGRwkbGeometryType eGType,
317 : char ** papszOptions )
318 : {
319 0 : if (!bReadWrite)
320 : {
321 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode");
322 0 : return NULL;
323 : }
324 :
325 : /* -------------------------------------------------------------------- */
326 : /* Do we already have this layer? If so, should we blow it */
327 : /* away? */
328 : /* -------------------------------------------------------------------- */
329 : int iLayer;
330 :
331 0 : for( iLayer = 0; iLayer < nLayers; iLayer++ )
332 : {
333 0 : if( EQUAL(pszName,papoLayers[iLayer]->GetName()) )
334 : {
335 0 : if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL
336 : && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
337 : {
338 0 : DeleteLayer( pszName );
339 0 : break;
340 : }
341 : else
342 : {
343 : CPLError( CE_Failure, CPLE_AppDefined,
344 : "Layer %s already exists, CreateLayer failed.\n"
345 : "Use the layer creation option OVERWRITE=YES to "
346 : "replace it.",
347 0 : pszName );
348 0 : return NULL;
349 : }
350 : }
351 : }
352 :
353 0 : char* pszEscapedName = CPLEscapeString(pszName, -1, CPLES_URL);
354 0 : CPLString osEscapedName = pszEscapedName;
355 0 : CPLFree(pszEscapedName);
356 :
357 : /* -------------------------------------------------------------------- */
358 : /* Create "database" */
359 : /* -------------------------------------------------------------------- */
360 0 : CPLString osURI;
361 0 : osURI = "/";
362 0 : osURI += osEscapedName;
363 0 : json_object* poAnswerObj = PUT(osURI, NULL);
364 :
365 0 : if (poAnswerObj == NULL)
366 0 : return NULL;
367 :
368 0 : if (!IsOK(poAnswerObj, "Layer creation failed"))
369 : {
370 0 : json_object_put(poAnswerObj);
371 0 : return NULL;
372 : }
373 :
374 0 : json_object_put(poAnswerObj);
375 :
376 : /* -------------------------------------------------------------------- */
377 : /* Create "spatial index" */
378 : /* -------------------------------------------------------------------- */
379 0 : int nUpdateSeq = 0;
380 0 : if (eGType != wkbNone)
381 : {
382 0 : osURI = "/";
383 0 : osURI += osEscapedName;
384 0 : osURI += "/_design/ogr_spatial";
385 :
386 0 : CPLString osContent("{ \"spatial\": { \"spatial\" : \"function(doc) { if (doc.geometry && doc.geometry.coordinates && doc.geometry.coordinates.length != 0) { emit(doc.geometry, null); } } \" } }");
387 :
388 0 : poAnswerObj = PUT(osURI, osContent);
389 :
390 0 : if (IsOK(poAnswerObj, "Spatial index creation failed"))
391 0 : nUpdateSeq ++;
392 :
393 0 : json_object_put(poAnswerObj);
394 : }
395 :
396 :
397 : /* -------------------------------------------------------------------- */
398 : /* Create validation function */
399 : /* -------------------------------------------------------------------- */
400 0 : const char* pszUpdatePermissions = CSLFetchNameValueDef(papszOptions, "UPDATE_PERMISSIONS", "LOGGED_USER");
401 0 : CPLString osValidation;
402 0 : if (EQUAL(pszUpdatePermissions, "LOGGED_USER"))
403 : {
404 0 : osValidation = "{\"validate_doc_update\": \"function(new_doc, old_doc, userCtx) { if(!userCtx.name) { throw({forbidden: \\\"Please log in first.\\\"}); } }\" }";
405 : }
406 0 : else if (EQUAL(pszUpdatePermissions, "ALL"))
407 : {
408 0 : osValidation = "{\"validate_doc_update\": \"function(new_doc, old_doc, userCtx) { }\" }";
409 : }
410 0 : else if (EQUAL(pszUpdatePermissions, "ADMIN"))
411 : {
412 0 : osValidation = "{\"validate_doc_update\": \"function(new_doc, old_doc, userCtx) {if (userCtx.roles.indexOf('_admin') === -1) { throw({forbidden: \\\"No changes allowed except by admin.\\\"}); } }\" }";
413 : }
414 0 : else if (strncmp(pszUpdatePermissions, "function(", 9) == 0)
415 : {
416 0 : osValidation = "{\"validate_doc_update\": \"";
417 0 : osValidation += pszUpdatePermissions;
418 0 : osValidation += "\"}";
419 : }
420 :
421 0 : if (osValidation.size())
422 : {
423 0 : osURI = "/";
424 0 : osURI += osEscapedName;
425 0 : osURI += "/_design/ogr_validation";
426 :
427 0 : poAnswerObj = PUT(osURI, osValidation);
428 :
429 0 : if (IsOK(poAnswerObj, "Validation function creation failed"))
430 0 : nUpdateSeq ++;
431 :
432 0 : json_object_put(poAnswerObj);
433 : }
434 :
435 0 : int bGeoJSONDocument = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "GEOJSON", "TRUE"));
436 0 : int nCoordPrecision = atoi(CSLFetchNameValueDef(papszOptions, "COORDINATE_PRECISION", "-1"));
437 :
438 0 : OGRCouchDBTableLayer* poLayer = new OGRCouchDBTableLayer(this, pszName);
439 0 : if (nCoordPrecision != -1)
440 0 : poLayer->SetCoordinatePrecision(nCoordPrecision);
441 0 : poLayer->SetInfoAfterCreation(eGType, poSpatialRef, nUpdateSeq, bGeoJSONDocument);
442 0 : papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
443 0 : papoLayers[nLayers ++] = poLayer;
444 0 : return poLayer;
445 : }
446 :
447 : /************************************************************************/
448 : /* DeleteLayer() */
449 : /************************************************************************/
450 :
451 0 : void OGRCouchDBDataSource::DeleteLayer( const char *pszLayerName )
452 :
453 : {
454 : int iLayer;
455 :
456 : /* -------------------------------------------------------------------- */
457 : /* Try to find layer. */
458 : /* -------------------------------------------------------------------- */
459 0 : for( iLayer = 0; iLayer < nLayers; iLayer++ )
460 : {
461 0 : if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) )
462 0 : break;
463 : }
464 :
465 0 : if( iLayer == nLayers )
466 : {
467 : CPLError( CE_Failure, CPLE_AppDefined,
468 : "Attempt to delete layer '%s', but this layer is not known to OGR.",
469 0 : pszLayerName );
470 0 : return;
471 : }
472 :
473 0 : DeleteLayer(iLayer);
474 : }
475 :
476 : /************************************************************************/
477 : /* DeleteLayer() */
478 : /************************************************************************/
479 :
480 0 : OGRErr OGRCouchDBDataSource::DeleteLayer(int iLayer)
481 : {
482 0 : if (!bReadWrite)
483 : {
484 : CPLError(CE_Failure, CPLE_AppDefined,
485 0 : "Operation not available in read-only mode");
486 0 : return OGRERR_FAILURE;
487 : }
488 :
489 0 : if( iLayer < 0 || iLayer >= nLayers )
490 : {
491 : CPLError( CE_Failure, CPLE_AppDefined,
492 : "Layer %d not in legal range of 0 to %d.",
493 0 : iLayer, nLayers-1 );
494 0 : return OGRERR_FAILURE;
495 : }
496 :
497 0 : CPLString osLayerName = GetLayer(iLayer)->GetName();
498 :
499 : /* -------------------------------------------------------------------- */
500 : /* Blow away our OGR structures related to the layer. This is */
501 : /* pretty dangerous if anything has a reference to this layer! */
502 : /* -------------------------------------------------------------------- */
503 0 : CPLDebug( "CouchDB", "DeleteLayer(%s)", osLayerName.c_str() );
504 :
505 0 : delete papoLayers[iLayer];
506 : memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
507 0 : sizeof(void *) * (nLayers - iLayer - 1) );
508 0 : nLayers--;
509 :
510 : /* -------------------------------------------------------------------- */
511 : /* Remove from the database. */
512 : /* -------------------------------------------------------------------- */
513 :
514 0 : char* pszEscapedName = CPLEscapeString(osLayerName, -1, CPLES_URL);
515 0 : CPLString osEscapedName = pszEscapedName;
516 0 : CPLFree(pszEscapedName);
517 :
518 0 : CPLString osURI;
519 0 : osURI = "/";
520 0 : osURI += osEscapedName;
521 0 : json_object* poAnswerObj = DELETE(osURI);
522 :
523 0 : if (poAnswerObj == NULL)
524 0 : return OGRERR_FAILURE;
525 :
526 0 : if (!IsOK(poAnswerObj, "Layer deletion failed"))
527 : {
528 0 : json_object_put(poAnswerObj);
529 0 : return OGRERR_FAILURE;
530 : }
531 :
532 0 : json_object_put(poAnswerObj);
533 :
534 0 : return OGRERR_NONE;
535 : }
536 :
537 : /************************************************************************/
538 : /* ExecuteSQL() */
539 : /************************************************************************/
540 :
541 1 : OGRLayer * OGRCouchDBDataSource::ExecuteSQL( const char *pszSQLCommand,
542 : OGRGeometry *poSpatialFilter,
543 : const char *pszDialect )
544 :
545 : {
546 1 : if( pszDialect != NULL && EQUAL(pszDialect,"OGRSQL") )
547 : return OGRDataSource::ExecuteSQL( pszSQLCommand,
548 : poSpatialFilter,
549 0 : pszDialect );
550 :
551 : /* -------------------------------------------------------------------- */
552 : /* Special case DELLAYER: command. */
553 : /* -------------------------------------------------------------------- */
554 1 : if( EQUALN(pszSQLCommand,"DELLAYER:",9) )
555 : {
556 0 : const char *pszLayerName = pszSQLCommand + 9;
557 :
558 0 : while( *pszLayerName == ' ' )
559 0 : pszLayerName++;
560 :
561 0 : DeleteLayer( pszLayerName );
562 0 : return NULL;
563 : }
564 :
565 : /* -------------------------------------------------------------------- */
566 : /* Special case 'COMPACT ON ' command. */
567 : /* -------------------------------------------------------------------- */
568 1 : if( EQUALN(pszSQLCommand,"COMPACT ON ",11) )
569 : {
570 0 : const char *pszLayerName = pszSQLCommand + 11;
571 :
572 0 : while( *pszLayerName == ' ' )
573 0 : pszLayerName++;
574 :
575 0 : CPLString osURI("/");
576 0 : osURI += pszLayerName;
577 0 : osURI += "/_compact";
578 :
579 0 : json_object* poAnswerObj = POST(osURI, NULL);
580 0 : IsError(poAnswerObj, "Database compaction failed");
581 0 : json_object_put(poAnswerObj);
582 :
583 0 : return NULL;
584 : }
585 :
586 : /* -------------------------------------------------------------------- */
587 : /* Special case 'VIEW CLEANUP ON ' command. */
588 : /* -------------------------------------------------------------------- */
589 1 : if( EQUALN(pszSQLCommand,"VIEW CLEANUP ON ",16) )
590 : {
591 0 : const char *pszLayerName = pszSQLCommand + 16;
592 :
593 0 : while( *pszLayerName == ' ' )
594 0 : pszLayerName++;
595 :
596 0 : CPLString osURI("/");
597 0 : osURI += pszLayerName;
598 0 : osURI += "/_view_cleanup";
599 :
600 0 : json_object* poAnswerObj = POST(osURI, NULL);
601 0 : IsError(poAnswerObj, "View cleanup failed");
602 0 : json_object_put(poAnswerObj);
603 :
604 0 : return NULL;
605 : }
606 :
607 : /* -------------------------------------------------------------------- */
608 : /* Deal with "DELETE FROM layer_name WHERE expression" statement */
609 : /* -------------------------------------------------------------------- */
610 1 : if( EQUALN(pszSQLCommand, "DELETE FROM ", 12) )
611 : {
612 0 : const char* pszIter = pszSQLCommand + 12;
613 0 : while(*pszIter && *pszIter != ' ')
614 0 : pszIter ++;
615 0 : if (*pszIter == 0)
616 : {
617 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid statement");
618 0 : return NULL;
619 : }
620 :
621 0 : CPLString osName = pszSQLCommand + 12;
622 0 : osName.resize(pszIter - (pszSQLCommand + 12));
623 0 : OGRCouchDBLayer* poLayer = (OGRCouchDBLayer*)GetLayerByName(osName);
624 0 : if (poLayer == NULL)
625 : {
626 : CPLError(CE_Failure, CPLE_AppDefined,
627 0 : "Unknown layer : %s", osName.c_str());
628 0 : return NULL;
629 : }
630 0 : if (poLayer->GetLayerType() != COUCHDB_TABLE_LAYER)
631 0 : return NULL;
632 0 : OGRCouchDBTableLayer* poTableLayer = (OGRCouchDBTableLayer*)poLayer;
633 :
634 0 : while(*pszIter && *pszIter == ' ')
635 0 : pszIter ++;
636 0 : if (!EQUALN(pszIter, "WHERE ", 5))
637 : {
638 0 : CPLError(CE_Failure, CPLE_AppDefined, "WHERE clause missing");
639 0 : return NULL;
640 : }
641 0 : pszIter += 5;
642 :
643 0 : const char* pszQuery = pszIter;
644 :
645 : /* Check with the generic SQL engine that this is a valid WHERE clause */
646 0 : OGRFeatureQuery oQuery;
647 0 : OGRErr eErr = oQuery.Compile( poLayer->GetLayerDefn(), pszQuery );
648 0 : if( eErr != OGRERR_NONE )
649 : {
650 0 : return NULL;
651 : }
652 :
653 0 : swq_expr_node * pNode = (swq_expr_node *) oQuery.GetSWGExpr();
654 0 : if (pNode->eNodeType == SNT_OPERATION &&
655 : pNode->nOperation == SWQ_EQ &&
656 : pNode->nSubExprCount == 2 &&
657 0 : pNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
658 0 : pNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
659 0 : pNode->papoSubExpr[0]->field_index == _ID_FIELD &&
660 0 : pNode->papoSubExpr[1]->field_type == SWQ_STRING)
661 : {
662 0 : poTableLayer->DeleteFeature(pNode->papoSubExpr[1]->string_value);
663 : }
664 : else
665 : {
666 : CPLError(CE_Failure, CPLE_AppDefined,
667 0 : "Invalid WHERE clause. Expecting '_id' = 'a_value'");
668 0 : return NULL;
669 : }
670 :
671 :
672 0 : return NULL;
673 : }
674 :
675 : /* -------------------------------------------------------------------- */
676 : /* Try an optimized implementation when doing only stats */
677 : /* -------------------------------------------------------------------- */
678 1 : if (poSpatialFilter == NULL && EQUALN(pszSQLCommand, "SELECT", 6))
679 : {
680 1 : OGRLayer* poRet = ExecuteSQLStats(pszSQLCommand);
681 1 : if (poRet)
682 1 : return poRet;
683 : }
684 :
685 : return OGRDataSource::ExecuteSQL( pszSQLCommand,
686 : poSpatialFilter,
687 0 : pszDialect );
688 : }
689 :
690 : /************************************************************************/
691 : /* ExecuteSQLStats() */
692 : /************************************************************************/
693 :
694 : class PointerAutoFree
695 : {
696 : void ** m_pp;
697 : public:
698 4 : PointerAutoFree(void** pp) { m_pp = pp; }
699 4 : ~PointerAutoFree() { CPLFree(*m_pp); *m_pp = NULL; }
700 : };
701 :
702 : class OGRCouchDBOneLineLayer : public OGRLayer
703 : {
704 : public:
705 : OGRFeature* poFeature;
706 : OGRFeatureDefn* poFeatureDefn;
707 : int bEnd;
708 :
709 1 : OGRCouchDBOneLineLayer() { poFeature = NULL; poFeatureDefn = NULL; bEnd = FALSE; }
710 1 : ~OGRCouchDBOneLineLayer()
711 1 : {
712 1 : delete poFeature;
713 1 : if( poFeatureDefn != NULL )
714 1 : poFeatureDefn->Release();
715 1 : }
716 :
717 0 : virtual void ResetReading() { bEnd = FALSE;}
718 1 : virtual OGRFeature *GetNextFeature()
719 : {
720 1 : if (bEnd) return NULL;
721 1 : bEnd = TRUE;
722 1 : return poFeature->Clone();
723 : }
724 0 : virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; }
725 0 : virtual int TestCapability( const char * ) { return FALSE; }
726 : };
727 :
728 1 : OGRLayer * OGRCouchDBDataSource::ExecuteSQLStats( const char *pszSQLCommand )
729 : {
730 1 : swq_select sSelectInfo;
731 1 : if( sSelectInfo.preparse( pszSQLCommand ) != CPLE_None )
732 : {
733 0 : return NULL;
734 : }
735 :
736 1 : if (sSelectInfo.table_count != 1)
737 : {
738 0 : return NULL;
739 : }
740 :
741 1 : swq_table_def *psTableDef = &sSelectInfo.table_defs[0];
742 1 : if( psTableDef->data_source != NULL )
743 : {
744 0 : return NULL;
745 : }
746 :
747 : OGRCouchDBLayer* _poSrcLayer =
748 1 : (OGRCouchDBLayer* )GetLayerByName( psTableDef->table_name );
749 1 : if (_poSrcLayer == NULL)
750 : {
751 0 : return NULL;
752 : }
753 1 : if (_poSrcLayer->GetLayerType() != COUCHDB_TABLE_LAYER)
754 0 : return NULL;
755 :
756 1 : OGRCouchDBTableLayer* poSrcLayer = (OGRCouchDBTableLayer* ) _poSrcLayer;
757 :
758 1 : int nFieldCount = poSrcLayer->GetLayerDefn()->GetFieldCount();
759 :
760 : swq_field_list sFieldList;
761 1 : memset( &sFieldList, 0, sizeof(sFieldList) );
762 1 : sFieldList.table_count = sSelectInfo.table_count;
763 1 : sFieldList.table_defs = sSelectInfo.table_defs;
764 :
765 1 : sFieldList.count = 0;
766 1 : sFieldList.names = (char **) CPLMalloc( sizeof(char *) * nFieldCount );
767 : sFieldList.types = (swq_field_type *)
768 1 : CPLMalloc( sizeof(swq_field_type) * nFieldCount );
769 : sFieldList.table_ids = (int *)
770 1 : CPLMalloc( sizeof(int) * nFieldCount );
771 : sFieldList.ids = (int *)
772 1 : CPLMalloc( sizeof(int) * nFieldCount );
773 :
774 1 : PointerAutoFree oHolderNames((void**)&(sFieldList.names));
775 1 : PointerAutoFree oHolderTypes((void**)&(sFieldList.types));
776 1 : PointerAutoFree oHolderTableIds((void**)&(sFieldList.table_ids));
777 1 : PointerAutoFree oHolderIds((void**)&(sFieldList.ids));
778 :
779 : int iField;
780 12 : for( iField = 0;
781 6 : iField < poSrcLayer->GetLayerDefn()->GetFieldCount();
782 : iField++ )
783 : {
784 5 : OGRFieldDefn *poFDefn=poSrcLayer->GetLayerDefn()->GetFieldDefn(iField);
785 5 : int iOutField = sFieldList.count++;
786 5 : sFieldList.names[iOutField] = (char *) poFDefn->GetNameRef();
787 5 : if( poFDefn->GetType() == OFTInteger )
788 0 : sFieldList.types[iOutField] = SWQ_INTEGER;
789 5 : else if( poFDefn->GetType() == OFTReal )
790 2 : sFieldList.types[iOutField] = SWQ_FLOAT;
791 3 : else if( poFDefn->GetType() == OFTString )
792 3 : sFieldList.types[iOutField] = SWQ_STRING;
793 : else
794 0 : sFieldList.types[iOutField] = SWQ_OTHER;
795 :
796 5 : sFieldList.table_ids[iOutField] = 0;
797 5 : sFieldList.ids[iOutField] = iField;
798 : }
799 :
800 1 : CPLString osLastFieldName;
801 6 : for( iField = 0; iField < sSelectInfo.result_columns; iField++ )
802 : {
803 5 : swq_col_def *psColDef = sSelectInfo.column_defs + iField;
804 5 : if (psColDef->field_name == NULL)
805 0 : return NULL;
806 :
807 5 : if (strcmp(psColDef->field_name, "*") != 0)
808 : {
809 4 : if (osLastFieldName.size() == 0)
810 1 : osLastFieldName = psColDef->field_name;
811 3 : else if (strcmp(osLastFieldName, psColDef->field_name) != 0)
812 0 : return NULL;
813 :
814 4 : if (poSrcLayer->GetLayerDefn()->GetFieldIndex(psColDef->field_name) == -1)
815 0 : return NULL;
816 : }
817 :
818 5 : if (!(psColDef->col_func == SWQCF_AVG ||
819 : psColDef->col_func == SWQCF_MIN ||
820 : psColDef->col_func == SWQCF_MAX ||
821 : psColDef->col_func == SWQCF_COUNT ||
822 : psColDef->col_func == SWQCF_SUM))
823 0 : return NULL;
824 :
825 5 : if (psColDef->distinct_flag) /* TODO: could perhaps be relaxed */
826 0 : return NULL;
827 : }
828 :
829 1 : if (osLastFieldName.size() == 0)
830 0 : return NULL;
831 :
832 : /* Normalize field name */
833 1 : int nIndex = poSrcLayer->GetLayerDefn()->GetFieldIndex(osLastFieldName);
834 1 : osLastFieldName = poSrcLayer->GetLayerDefn()->GetFieldDefn(nIndex)->GetNameRef();
835 :
836 : /* -------------------------------------------------------------------- */
837 : /* Finish the parse operation. */
838 : /* -------------------------------------------------------------------- */
839 :
840 1 : if( sSelectInfo.parse( &sFieldList, 0 ) != CE_None )
841 : {
842 0 : return NULL;
843 : }
844 :
845 1 : if (sSelectInfo.join_defs != NULL ||
846 : sSelectInfo.where_expr != NULL ||
847 : sSelectInfo.order_defs != NULL ||
848 : sSelectInfo.query_mode != SWQM_SUMMARY_RECORD)
849 : {
850 0 : return NULL;
851 : }
852 :
853 6 : for( iField = 0; iField < sSelectInfo.result_columns; iField++ )
854 : {
855 5 : swq_col_def *psColDef = sSelectInfo.column_defs + iField;
856 5 : if (psColDef->field_index == -1)
857 : {
858 1 : if (psColDef->col_func == SWQCF_COUNT)
859 1 : continue;
860 :
861 0 : return NULL;
862 : }
863 4 : if (psColDef->field_type != SWQ_INTEGER &&
864 : psColDef->field_type != SWQ_FLOAT)
865 : {
866 0 : return NULL;
867 : }
868 : }
869 :
870 1 : int bFoundFilter = poSrcLayer->HasFilterOnFieldOrCreateIfNecessary(osLastFieldName);
871 1 : if (!bFoundFilter)
872 0 : return NULL;
873 :
874 1 : CPLString osURI = "/";
875 1 : osURI += poSrcLayer->GetName();
876 1 : osURI += "/_design/ogr_filter_";
877 1 : osURI += osLastFieldName;
878 1 : osURI += "/_view/filter?reduce=true";
879 :
880 1 : json_object* poAnswerObj = GET(osURI);
881 1 : json_object* poRows = NULL;
882 1 : if (!(poAnswerObj != NULL &&
883 : json_object_is_type(poAnswerObj, json_type_object) &&
884 : (poRows = json_object_object_get(poAnswerObj, "rows")) != NULL &&
885 : json_object_is_type(poRows, json_type_array)))
886 : {
887 0 : json_object_put(poAnswerObj);
888 0 : return NULL;
889 : }
890 :
891 1 : int nLength = json_object_array_length(poRows);
892 1 : if (nLength != 1)
893 : {
894 0 : json_object_put(poAnswerObj);
895 0 : return NULL;
896 : }
897 :
898 1 : json_object* poRow = json_object_array_get_idx(poRows, 0);
899 1 : if (!(poRow && json_object_is_type(poRow, json_type_object)))
900 : {
901 0 : json_object_put(poAnswerObj);
902 0 : return NULL;
903 : }
904 :
905 1 : json_object* poValue = json_object_object_get(poRow, "value");
906 1 : if (!(poValue != NULL && json_object_is_type(poValue, json_type_object)))
907 : {
908 0 : json_object_put(poAnswerObj);
909 0 : return NULL;
910 : }
911 :
912 1 : json_object* poSum = json_object_object_get(poValue, "sum");
913 1 : json_object* poCount = json_object_object_get(poValue, "count");
914 1 : json_object* poMin = json_object_object_get(poValue, "min");
915 1 : json_object* poMax = json_object_object_get(poValue, "max");
916 1 : if (poSum != NULL && (json_object_is_type(poSum, json_type_int) ||
917 : json_object_is_type(poSum, json_type_double)) &&
918 : poCount != NULL && (json_object_is_type(poCount, json_type_int) ||
919 : json_object_is_type(poCount, json_type_double)) &&
920 : poMin != NULL && (json_object_is_type(poMin, json_type_int) ||
921 : json_object_is_type(poMin, json_type_double)) &&
922 : poMax != NULL && (json_object_is_type(poMax, json_type_int) ||
923 : json_object_is_type(poMax, json_type_double)) )
924 : {
925 1 : double dfSum = json_object_get_double(poSum);
926 1 : int nCount = json_object_get_int(poCount);
927 1 : double dfMin = json_object_get_double(poMin);
928 1 : double dfMax = json_object_get_double(poMax);
929 1 : json_object_put(poAnswerObj);
930 :
931 : //CPLDebug("CouchDB", "sum=%f, count=%d, min=%f, max=%f",
932 : // dfSum, nCount, dfMin, dfMax);
933 :
934 1 : OGRFeatureDefn* poFeatureDefn = new OGRFeatureDefn(poSrcLayer->GetName());
935 1 : poFeatureDefn->Reference();
936 :
937 6 : for( iField = 0; iField < sSelectInfo.result_columns; iField++ )
938 : {
939 5 : swq_col_def *psColDef = sSelectInfo.column_defs + iField;
940 5 : OGRFieldDefn oFDefn( "", OFTInteger );
941 :
942 5 : if( psColDef->field_alias != NULL )
943 : {
944 0 : oFDefn.SetName(psColDef->field_alias);
945 : }
946 : else
947 : {
948 : const swq_operation *op = swq_op_registrar::GetOperator(
949 5 : (swq_op) psColDef->col_func );
950 : oFDefn.SetName( CPLSPrintf( "%s_%s",
951 : op->osName.c_str(),
952 5 : psColDef->field_name ) );
953 : }
954 :
955 5 : if( psColDef->col_func == SWQCF_COUNT )
956 1 : oFDefn.SetType( OFTInteger );
957 4 : else if (psColDef->field_type == SWQ_INTEGER)
958 0 : oFDefn.SetType( OFTInteger );
959 4 : else if (psColDef->field_type == SWQ_FLOAT)
960 4 : oFDefn.SetType( OFTReal );
961 :
962 5 : poFeatureDefn->AddFieldDefn(&oFDefn);
963 : }
964 :
965 1 : OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
966 :
967 6 : for( iField = 0; iField < sSelectInfo.result_columns; iField++ )
968 : {
969 5 : swq_col_def *psColDef = sSelectInfo.column_defs + iField;
970 5 : switch(psColDef->col_func)
971 : {
972 : case SWQCF_AVG:
973 1 : if (nCount)
974 1 : poFeature->SetField(iField, dfSum / nCount);
975 1 : break;
976 : case SWQCF_MIN:
977 1 : poFeature->SetField(iField, dfMin);
978 1 : break;
979 : case SWQCF_MAX:
980 1 : poFeature->SetField(iField, dfMax);
981 1 : break;
982 : case SWQCF_COUNT:
983 1 : poFeature->SetField(iField, nCount);
984 1 : break;
985 : case SWQCF_SUM:
986 1 : poFeature->SetField(iField, dfSum);
987 : break;
988 : default:
989 : break;
990 : }
991 : }
992 :
993 1 : poFeature->SetFID(0);
994 :
995 1 : OGRCouchDBOneLineLayer* poAnswerLayer = new OGRCouchDBOneLineLayer();
996 1 : poAnswerLayer->poFeatureDefn = poFeatureDefn;
997 1 : poAnswerLayer->poFeature = poFeature;
998 1 : return poAnswerLayer;
999 : }
1000 0 : json_object_put(poAnswerObj);
1001 :
1002 0 : return NULL;
1003 : }
1004 :
1005 : /************************************************************************/
1006 : /* ReleaseResultSet() */
1007 : /************************************************************************/
1008 :
1009 1 : void OGRCouchDBDataSource::ReleaseResultSet( OGRLayer * poLayer )
1010 :
1011 : {
1012 1 : delete poLayer;
1013 1 : }
1014 :
1015 : /************************************************************************/
1016 : /* REQUEST() */
1017 : /************************************************************************/
1018 :
1019 62 : json_object* OGRCouchDBDataSource::REQUEST(const char* pszVerb,
1020 : const char* pszURI,
1021 : const char* pszData)
1022 : {
1023 62 : bMustCleanPersistant = TRUE;
1024 :
1025 62 : char** papszOptions = NULL;
1026 62 : papszOptions = CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=CouchDB:%p", this));
1027 :
1028 62 : CPLString osCustomRequest("CUSTOMREQUEST=");
1029 62 : osCustomRequest += pszVerb;
1030 62 : papszOptions = CSLAddString(papszOptions, osCustomRequest);
1031 :
1032 62 : CPLString osPOSTFIELDS("POSTFIELDS=");
1033 62 : if (pszData)
1034 3 : osPOSTFIELDS += pszData;
1035 62 : papszOptions = CSLAddString(papszOptions, osPOSTFIELDS);
1036 :
1037 62 : papszOptions = CSLAddString(papszOptions, "HEADERS=Content-Type: application/json");
1038 :
1039 62 : if (osUserPwd.size())
1040 : {
1041 0 : CPLString osUserPwdOption("USERPWD=");
1042 0 : osUserPwdOption += osUserPwd;
1043 0 : papszOptions = CSLAddString(papszOptions, osUserPwdOption);
1044 : }
1045 :
1046 62 : CPLDebug("CouchDB", "%s %s", pszVerb, pszURI);
1047 62 : CPLString osFullURL(osURL);
1048 62 : osFullURL += pszURI;
1049 62 : CPLPushErrorHandler(CPLQuietErrorHandler);
1050 62 : CPLHTTPResult * psResult = CPLHTTPFetch( osFullURL, papszOptions);
1051 62 : CPLPopErrorHandler();
1052 62 : CSLDestroy(papszOptions);
1053 62 : if (psResult == NULL)
1054 0 : return NULL;
1055 :
1056 62 : const char* pszServer = CSLFetchNameValue(psResult->papszHeaders, "Server");
1057 62 : if (pszServer == NULL || !EQUALN(pszServer, "CouchDB", 7))
1058 : {
1059 0 : CPLHTTPDestroyResult(psResult);
1060 0 : return NULL;
1061 : }
1062 :
1063 62 : if (psResult->nDataLen == 0)
1064 : {
1065 0 : CPLHTTPDestroyResult(psResult);
1066 0 : return NULL;
1067 : }
1068 :
1069 62 : json_tokener* jstok = NULL;
1070 62 : json_object* jsobj = NULL;
1071 :
1072 62 : jstok = json_tokener_new();
1073 62 : jsobj = json_tokener_parse_ex(jstok, (const char*)psResult->pabyData, -1);
1074 62 : if( jstok->err != json_tokener_success)
1075 : {
1076 : CPLError( CE_Failure, CPLE_AppDefined,
1077 : "JSON parsing error: %s (at offset %d)",
1078 0 : json_tokener_errors[jstok->err], jstok->char_offset);
1079 :
1080 0 : json_tokener_free(jstok);
1081 :
1082 0 : CPLHTTPDestroyResult(psResult);
1083 0 : return NULL;
1084 : }
1085 62 : json_tokener_free(jstok);
1086 :
1087 62 : CPLHTTPDestroyResult(psResult);
1088 62 : return jsobj;
1089 : }
1090 :
1091 : /************************************************************************/
1092 : /* GET() */
1093 : /************************************************************************/
1094 :
1095 59 : json_object* OGRCouchDBDataSource::GET(const char* pszURI)
1096 : {
1097 59 : return REQUEST("GET", pszURI, NULL);
1098 : }
1099 :
1100 : /************************************************************************/
1101 : /* PUT() */
1102 : /************************************************************************/
1103 :
1104 1 : json_object* OGRCouchDBDataSource::PUT(const char* pszURI, const char* pszData)
1105 : {
1106 1 : return REQUEST("PUT", pszURI, pszData);
1107 : }
1108 :
1109 : /************************************************************************/
1110 : /* POST() */
1111 : /************************************************************************/
1112 :
1113 2 : json_object* OGRCouchDBDataSource::POST(const char* pszURI, const char* pszData)
1114 : {
1115 2 : return REQUEST("POST", pszURI, pszData);
1116 : }
1117 :
1118 : /************************************************************************/
1119 : /* DELETE() */
1120 : /************************************************************************/
1121 :
1122 0 : json_object* OGRCouchDBDataSource::DELETE(const char* pszURI)
1123 : {
1124 0 : return REQUEST("DELETE", pszURI, NULL);
1125 : }
1126 :
1127 : /************************************************************************/
1128 : /* IsError() */
1129 : /************************************************************************/
1130 :
1131 31 : int OGRCouchDBDataSource::IsError(json_object* poAnswerObj,
1132 : const char* pszErrorMsg)
1133 : {
1134 31 : if ( poAnswerObj == NULL ||
1135 : !json_object_is_type(poAnswerObj, json_type_object) )
1136 : {
1137 0 : return FALSE;
1138 : }
1139 :
1140 31 : json_object* poError = json_object_object_get(poAnswerObj, "error");
1141 31 : json_object* poReason = json_object_object_get(poAnswerObj, "reason");
1142 :
1143 31 : const char* pszError = json_object_get_string(poError);
1144 31 : const char* pszReason = json_object_get_string(poReason);
1145 31 : if (pszError != NULL)
1146 : {
1147 : CPLError(CE_Failure, CPLE_AppDefined,
1148 : "%s : %s, %s",
1149 : pszErrorMsg,
1150 : pszError ? pszError : "",
1151 1 : pszReason ? pszReason : "");
1152 :
1153 1 : return TRUE;
1154 : }
1155 :
1156 30 : return FALSE;
1157 : }
1158 :
1159 : /************************************************************************/
1160 : /* IsOK() */
1161 : /************************************************************************/
1162 :
1163 1 : int OGRCouchDBDataSource::IsOK(json_object* poAnswerObj,
1164 : const char* pszErrorMsg)
1165 : {
1166 1 : if ( poAnswerObj == NULL ||
1167 : !json_object_is_type(poAnswerObj, json_type_object) )
1168 : {
1169 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
1170 0 : pszErrorMsg);
1171 :
1172 0 : return FALSE;
1173 : }
1174 :
1175 1 : json_object* poOK = json_object_object_get(poAnswerObj, "ok");
1176 1 : if ( !poOK )
1177 : {
1178 1 : IsError(poAnswerObj, pszErrorMsg);
1179 :
1180 1 : return FALSE;
1181 : }
1182 :
1183 0 : const char* pszOK = json_object_get_string(poOK);
1184 0 : if ( !pszOK || !CSLTestBoolean(pszOK) )
1185 : {
1186 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMsg);
1187 :
1188 0 : return FALSE;
1189 : }
1190 :
1191 0 : return TRUE;
1192 : }
|