1 : /******************************************************************************
2 : * $Id: ogrcouchdbtablelayer.cpp 23437 2011-11-28 23:03:56Z rouault $
3 : *
4 : * Project: CouchDB Translator
5 : * Purpose: Implements OGRCouchDBTableLayer 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 "ogrgeojsonreader.h"
32 : #include "ogrgeojsonwriter.h"
33 : #include "json_object_private.h" // json_object_iter, complete type required
34 : #include "swq.h"
35 :
36 : #include <algorithm>
37 :
38 : CPL_CVSID("$Id: ogrcouchdbtablelayer.cpp 23437 2011-11-28 23:03:56Z rouault $");
39 :
40 : /************************************************************************/
41 : /* OGRCouchDBTableLayer() */
42 : /************************************************************************/
43 :
44 15 : OGRCouchDBTableLayer::OGRCouchDBTableLayer(OGRCouchDBDataSource* poDS,
45 : const char* pszName) :
46 15 : OGRCouchDBLayer(poDS)
47 :
48 : {
49 15 : osName = pszName;
50 15 : char* pszEscapedName = CPLEscapeString(pszName, -1, CPLES_URL);
51 15 : osEscapedName = pszEscapedName;
52 15 : CPLFree(pszEscapedName);
53 :
54 15 : bInTransaction = FALSE;
55 :
56 15 : eGeomType = wkbUnknown;
57 :
58 15 : nNextFIDForCreate = -1;
59 15 : bHasLoadedMetadata = FALSE;
60 15 : bMustWriteMetadata = FALSE;
61 :
62 15 : bMustRunSpatialFilter = FALSE;
63 15 : bServerSideSpatialFilteringWorks = TRUE;
64 15 : bHasOGRSpatial = -1;
65 15 : bHasGeocouchUtilsMinimalSpatialView = FALSE;
66 :
67 15 : bServerSideAttributeFilteringWorks = TRUE;
68 15 : bHasInstalledAttributeFilter = FALSE;
69 :
70 15 : nUpdateSeq = -1;
71 15 : bAlwaysValid = FALSE;
72 :
73 15 : bExtentValid = FALSE;
74 15 : bExtentSet = FALSE;
75 15 : dfMinX = 0;
76 15 : dfMinY = 0;
77 15 : dfMaxX = 0;
78 15 : dfMaxY = 0;
79 :
80 15 : nCoordPrecision = atoi(CPLGetConfigOption("OGR_COUCHDB_COORDINATE_PRECISION", "-1"));
81 15 : }
82 :
83 : /************************************************************************/
84 : /* ~OGRCouchDBTableLayer() */
85 : /************************************************************************/
86 :
87 15 : OGRCouchDBTableLayer::~OGRCouchDBTableLayer()
88 :
89 : {
90 15 : if( bMustWriteMetadata )
91 0 : WriteMetadata();
92 :
93 15 : for(int i=0;i<(int)aoTransactionFeatures.size();i++)
94 : {
95 0 : json_object_put(aoTransactionFeatures[i]);
96 : }
97 15 : }
98 :
99 : /************************************************************************/
100 : /* ResetReading() */
101 : /************************************************************************/
102 :
103 12 : void OGRCouchDBTableLayer::ResetReading()
104 :
105 : {
106 12 : OGRCouchDBLayer::ResetReading();
107 :
108 12 : json_object_put(poFeatures);
109 12 : poFeatures = NULL;
110 12 : aoFeatures.resize(0);
111 :
112 12 : bMustRunSpatialFilter = m_poFilterGeom != NULL;
113 12 : aosIdsToFetch.resize(0);
114 12 : }
115 :
116 : /************************************************************************/
117 : /* TestCapability() */
118 : /************************************************************************/
119 :
120 0 : int OGRCouchDBTableLayer::TestCapability( const char * pszCap )
121 :
122 : {
123 0 : if( EQUAL(pszCap,OLCFastFeatureCount) )
124 0 : return m_poFilterGeom == NULL && m_poAttrQuery == NULL;
125 :
126 0 : else if( EQUAL(pszCap,OLCFastGetExtent) )
127 0 : return bExtentValid;
128 :
129 0 : else if( EQUAL(pszCap,OLCRandomRead) )
130 0 : return TRUE;
131 :
132 0 : else if( EQUAL(pszCap,OLCSequentialWrite)
133 : || EQUAL(pszCap,OLCRandomWrite)
134 : || EQUAL(pszCap,OLCDeleteFeature) )
135 0 : return poDS->IsReadWrite();
136 :
137 0 : else if( EQUAL(pszCap,OLCCreateField) )
138 0 : return poDS->IsReadWrite();
139 :
140 0 : else if( EQUAL(pszCap, OLCTransactions) )
141 0 : return poDS->IsReadWrite();
142 :
143 0 : return OGRCouchDBLayer::TestCapability(pszCap);
144 : }
145 :
146 : /************************************************************************/
147 : /* RunSpatialFilterQueryIfNecessary() */
148 : /************************************************************************/
149 :
150 3 : int OGRCouchDBTableLayer::RunSpatialFilterQueryIfNecessary()
151 : {
152 3 : if (!bMustRunSpatialFilter)
153 0 : return TRUE;
154 :
155 3 : bMustRunSpatialFilter = FALSE;
156 :
157 3 : CPLAssert(nOffset == 0);
158 :
159 3 : aosIdsToFetch.resize(0);
160 :
161 3 : const char* pszSpatialFilter = NULL;
162 4 : if (bHasOGRSpatial < 0 || bHasOGRSpatial == FALSE)
163 : {
164 2 : pszSpatialFilter = CPLGetConfigOption("COUCHDB_SPATIAL_FILTER" , NULL);
165 2 : if (pszSpatialFilter)
166 0 : bHasOGRSpatial = FALSE;
167 : }
168 :
169 3 : if (bHasOGRSpatial < 0)
170 : {
171 2 : CPLString osURI("/");
172 2 : osURI += osEscapedName;
173 2 : osURI += "/_design/ogr_spatial";
174 :
175 2 : json_object* poAnswerObj = poDS->GET(osURI);
176 : bHasOGRSpatial = (poAnswerObj != NULL &&
177 : json_object_is_type(poAnswerObj, json_type_object) &&
178 2 : json_object_object_get(poAnswerObj, "spatial") != NULL);
179 2 : json_object_put(poAnswerObj);
180 :
181 2 : if (!bHasOGRSpatial)
182 : {
183 : /* Test if we have the 'minimal' spatial view provided by https://github.com/maxogden/geocouch-utils */
184 1 : osURI = "/";
185 1 : osURI += osEscapedName;
186 1 : osURI += "/_design/geo";
187 :
188 : json_object* poSpatialObj;
189 1 : poAnswerObj = poDS->GET(osURI);
190 : bHasGeocouchUtilsMinimalSpatialView = (poAnswerObj != NULL &&
191 : json_object_is_type(poAnswerObj, json_type_object) &&
192 : (poSpatialObj = json_object_object_get(poAnswerObj, "spatial")) != NULL &&
193 : json_object_is_type(poSpatialObj, json_type_object) &&
194 1 : json_object_object_get(poSpatialObj, "minimal") != NULL);
195 1 : json_object_put(poAnswerObj);
196 :
197 1 : if (!bHasGeocouchUtilsMinimalSpatialView)
198 : {
199 : CPLDebug("CouchDB",
200 1 : "Geocouch not working --> client-side spatial filtering");
201 1 : bServerSideSpatialFilteringWorks = FALSE;
202 1 : return FALSE;
203 : }
204 0 : }
205 : }
206 :
207 2 : OGREnvelope sEnvelope;
208 2 : m_poFilterGeom->getEnvelope( &sEnvelope );
209 :
210 2 : if (bHasOGRSpatial)
211 2 : pszSpatialFilter = "_design/ogr_spatial/_spatial/spatial";
212 0 : else if (bHasGeocouchUtilsMinimalSpatialView)
213 0 : pszSpatialFilter = "_design/geo/_spatial/minimal";
214 :
215 2 : CPLString osURI("/");
216 2 : osURI += osEscapedName;
217 2 : osURI += "/";
218 2 : osURI += pszSpatialFilter;
219 2 : osURI += "?bbox=";
220 : osURI += CPLSPrintf("%.9f,%.9f,%.9f,%.9f",
221 : sEnvelope.MinX, sEnvelope.MinY,
222 2 : sEnvelope.MaxX, sEnvelope.MaxY);
223 :
224 2 : json_object* poAnswerObj = poDS->GET(osURI);
225 2 : if (poAnswerObj == NULL)
226 : {
227 : CPLDebug("CouchDB",
228 0 : "Geocouch not working --> client-side spatial filtering");
229 0 : bServerSideSpatialFilteringWorks = FALSE;
230 0 : return FALSE;
231 : }
232 :
233 2 : if ( !json_object_is_type(poAnswerObj, json_type_object) )
234 : {
235 : CPLDebug("CouchDB",
236 0 : "Geocouch not working --> client-side spatial filtering");
237 0 : bServerSideSpatialFilteringWorks = FALSE;
238 : CPLError(CE_Failure, CPLE_AppDefined,
239 0 : "FetchNextRowsSpatialFilter() failed");
240 0 : json_object_put(poAnswerObj);
241 0 : return FALSE;
242 : }
243 :
244 : /* Catch error for a non geocouch database */
245 2 : json_object* poError = json_object_object_get(poAnswerObj, "error");
246 2 : json_object* poReason = json_object_object_get(poAnswerObj, "reason");
247 :
248 2 : const char* pszError = json_object_get_string(poError);
249 2 : const char* pszReason = json_object_get_string(poReason);
250 :
251 2 : if (pszError && pszReason && strcmp(pszError, "not_found") == 0 &&
252 : strcmp(pszReason, "Document is missing attachment") == 0)
253 : {
254 : CPLDebug("CouchDB",
255 0 : "Geocouch not working --> client-side spatial filtering");
256 0 : bServerSideSpatialFilteringWorks = FALSE;
257 0 : json_object_put(poAnswerObj);
258 0 : return FALSE;
259 : }
260 :
261 2 : if (poDS->IsError(poAnswerObj, "FetchNextRowsSpatialFilter() failed"))
262 : {
263 : CPLDebug("CouchDB",
264 0 : "Geocouch not working --> client-side spatial filtering");
265 0 : bServerSideSpatialFilteringWorks = FALSE;
266 0 : json_object_put(poAnswerObj);
267 0 : return FALSE;
268 : }
269 :
270 2 : json_object* poRows = json_object_object_get(poAnswerObj, "rows");
271 2 : if (poRows == NULL ||
272 : !json_object_is_type(poRows, json_type_array))
273 : {
274 : CPLDebug("CouchDB",
275 0 : "Geocouch not working --> client-side spatial filtering");
276 0 : bServerSideSpatialFilteringWorks = FALSE;
277 : CPLError(CE_Failure, CPLE_AppDefined,
278 0 : "FetchNextRowsSpatialFilter() failed");
279 0 : json_object_put(poAnswerObj);
280 0 : return FALSE;
281 : }
282 :
283 2 : int nRows = json_object_array_length(poRows);
284 14 : for(int i=0;i<nRows;i++)
285 : {
286 12 : json_object* poRow = json_object_array_get_idx(poRows, i);
287 12 : if ( poRow == NULL ||
288 : !json_object_is_type(poRow, json_type_object) )
289 : {
290 : CPLError(CE_Failure, CPLE_AppDefined,
291 0 : "FetchNextRowsSpatialFilter() failed");
292 0 : json_object_put(poAnswerObj);
293 0 : return FALSE;
294 : }
295 :
296 12 : json_object* poId = json_object_object_get(poRow, "id");
297 12 : const char* pszId = json_object_get_string(poId);
298 12 : if (pszId != NULL)
299 : {
300 12 : aosIdsToFetch.push_back(pszId);
301 : }
302 : }
303 :
304 2 : std::sort(aosIdsToFetch.begin(), aosIdsToFetch.end());
305 :
306 2 : json_object_put(poAnswerObj);
307 :
308 2 : return TRUE;
309 : }
310 :
311 : /************************************************************************/
312 : /* FetchNextRowsSpatialFilter() */
313 : /************************************************************************/
314 :
315 3 : int OGRCouchDBTableLayer::FetchNextRowsSpatialFilter()
316 : {
317 3 : if (!RunSpatialFilterQueryIfNecessary())
318 1 : return FALSE;
319 :
320 2 : CPLString osContent("{\"keys\":[");
321 2 : int nLimit = MIN(nOffset + GetFeaturesToFetch(), (int)aosIdsToFetch.size());
322 14 : for(int i=nOffset;i<nLimit;i++)
323 : {
324 12 : if (i > nOffset)
325 10 : osContent += ",";
326 12 : osContent += "\"";
327 12 : osContent += aosIdsToFetch[i];
328 12 : osContent += "\"";
329 : }
330 2 : osContent += "]}";
331 :
332 2 : CPLString osURI("/");
333 2 : osURI += osEscapedName;
334 2 : osURI += "/_all_docs?include_docs=true";
335 2 : json_object* poAnswerObj = poDS->POST(osURI, osContent);
336 2 : return FetchNextRowsAnalyseDocs(poAnswerObj);
337 : }
338 :
339 : /************************************************************************/
340 : /* HasFilterOnFieldOrCreateIfNecessary() */
341 : /************************************************************************/
342 :
343 5 : int OGRCouchDBTableLayer::HasFilterOnFieldOrCreateIfNecessary(const char* pszFieldName)
344 : {
345 5 : std::map<CPLString, int>::iterator oIter = oMapFilterFields.find(pszFieldName);
346 5 : if (oIter != oMapFilterFields.end())
347 2 : return oIter->second;
348 :
349 3 : CPLString osURI("/");
350 3 : osURI += osEscapedName;
351 3 : osURI += "/_design/ogr_filter_";
352 3 : osURI += pszFieldName;
353 :
354 3 : int bFoundFilter = FALSE;
355 :
356 3 : json_object* poAnswerObj = poDS->GET(osURI);
357 3 : if (poAnswerObj &&
358 : json_object_is_type(poAnswerObj, json_type_object) &&
359 : json_object_object_get(poAnswerObj, "views") != NULL)
360 : {
361 2 : bFoundFilter = TRUE;
362 : }
363 3 : json_object_put(poAnswerObj);
364 :
365 3 : if (!bFoundFilter)
366 : {
367 1 : json_object* poDoc = json_object_new_object();
368 1 : json_object* poViews = json_object_new_object();
369 1 : json_object* poFilter = json_object_new_object();
370 :
371 1 : CPLString osMap;
372 :
373 : OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(
374 1 : poFeatureDefn->GetFieldIndex(pszFieldName));
375 1 : CPLAssert(poFieldDefn);
376 : int bIsNumeric = poFieldDefn->GetType() == OFTInteger ||
377 1 : poFieldDefn->GetType() == OFTReal;
378 :
379 1 : if (bGeoJSONDocument)
380 : {
381 0 : osMap = "function(doc) { if (doc.properties && doc.properties.";
382 0 : osMap += pszFieldName;
383 0 : if (bIsNumeric)
384 : {
385 0 : osMap += " && typeof doc.properties.";
386 0 : osMap += pszFieldName;
387 0 : osMap += " == \"number\"";
388 : }
389 0 : osMap += ") emit(";
390 0 : osMap += "doc.properties.";
391 0 : osMap += pszFieldName;
392 0 : osMap +=", ";
393 0 : if (bIsNumeric)
394 : {
395 0 : osMap += "doc.properties.";
396 0 : osMap += pszFieldName;
397 : }
398 : else
399 0 : osMap +="null";
400 0 : osMap += "); }";
401 : }
402 : else
403 : {
404 1 : osMap = "function(doc) { if (doc.";
405 1 : osMap += pszFieldName;
406 1 : if (bIsNumeric)
407 : {
408 1 : osMap += " && typeof doc.";
409 1 : osMap += pszFieldName;
410 1 : osMap += " == \"number\"";
411 : }
412 1 : osMap += ") emit(";
413 1 : osMap += "doc.";
414 1 : osMap += pszFieldName;
415 1 : osMap +=", ";
416 1 : if (bIsNumeric)
417 : {
418 1 : osMap += "doc.";
419 1 : osMap += pszFieldName;
420 : }
421 : else
422 0 : osMap +="null";
423 1 : osMap += "); }";
424 : }
425 :
426 1 : json_object_object_add(poDoc, "views", poViews);
427 1 : json_object_object_add(poViews, "filter", poFilter);
428 1 : json_object_object_add(poFilter, "map", json_object_new_string(osMap));
429 :
430 1 : if (bIsNumeric)
431 1 : json_object_object_add(poFilter, "reduce", json_object_new_string("_stats"));
432 : else
433 0 : json_object_object_add(poFilter, "reduce", json_object_new_string("_count"));
434 :
435 : json_object* poAnswerObj = poDS->PUT(osURI,
436 1 : json_object_to_json_string(poDoc));
437 :
438 1 : json_object_put(poDoc);
439 :
440 1 : if (poDS->IsOK(poAnswerObj, "Filter creation failed"))
441 : {
442 0 : bFoundFilter = TRUE;
443 0 : if (!bAlwaysValid)
444 0 : bMustWriteMetadata = TRUE;
445 0 : nUpdateSeq++;
446 : }
447 :
448 1 : json_object_put(poAnswerObj);
449 : }
450 :
451 3 : oMapFilterFields[pszFieldName] = bFoundFilter;
452 :
453 3 : return bFoundFilter;
454 : }
455 :
456 : /************************************************************************/
457 : /* OGRCouchDBGetOpStr() */
458 : /************************************************************************/
459 :
460 2 : static const char* OGRCouchDBGetOpStr(int nOperation,
461 : int& bOutHasStrictComparisons)
462 : {
463 2 : bOutHasStrictComparisons = FALSE;
464 :
465 2 : switch(nOperation)
466 : {
467 2 : case SWQ_EQ: return "=";
468 0 : case SWQ_GE: return ">=";
469 0 : case SWQ_LE: return "<=";
470 0 : case SWQ_GT: bOutHasStrictComparisons = TRUE; return ">";
471 0 : case SWQ_LT: bOutHasStrictComparisons = TRUE; return "<";
472 0 : default: return "unknown op";
473 : }
474 : }
475 :
476 : /************************************************************************/
477 : /* OGRCouchDBGetValue() */
478 : /************************************************************************/
479 :
480 2 : static CPLString OGRCouchDBGetValue(swq_field_type eType,
481 : swq_expr_node* poNode)
482 : {
483 2 : if (eType == SWQ_STRING)
484 : {
485 0 : CPLString osVal("\"");
486 0 : osVal += poNode->string_value;
487 0 : osVal += "\"";
488 0 : return osVal;
489 : }
490 2 : else if (eType == SWQ_INTEGER)
491 : {
492 0 : return CPLSPrintf("%d", poNode->int_value);
493 : }
494 2 : else if (eType == SWQ_FLOAT)
495 : {
496 2 : return CPLSPrintf("%.9f", poNode->float_value);
497 : }
498 : else
499 : {
500 0 : CPLError(CE_Failure, CPLE_AppDefined, "Handled case! File a bug!");
501 0 : return "";
502 : }
503 : }
504 :
505 : /************************************************************************/
506 : /* OGRCouchDBGetKeyName() */
507 : /************************************************************************/
508 :
509 2 : static const char* OGRCouchDBGetKeyName(int nOperation)
510 : {
511 2 : if (nOperation == SWQ_EQ)
512 : {
513 2 : return "key";
514 : }
515 0 : else if (nOperation == SWQ_GE ||
516 : nOperation == SWQ_GT)
517 : {
518 0 : return "startkey";
519 : }
520 0 : else if (nOperation == SWQ_LE ||
521 : nOperation == SWQ_LT)
522 : {
523 0 : return "endkey";
524 : }
525 : else
526 : {
527 0 : CPLError(CE_Failure, CPLE_AppDefined, "Handled case! File a bug!");
528 0 : return "";
529 : }
530 : }
531 : /************************************************************************/
532 : /* BuildAttrQueryURI() */
533 : /************************************************************************/
534 :
535 4 : CPLString OGRCouchDBTableLayer::BuildAttrQueryURI(int& bOutHasStrictComparisons)
536 : {
537 4 : CPLString osURI = "";
538 :
539 4 : bOutHasStrictComparisons = FALSE;
540 :
541 4 : int bCanHandleFilter = FALSE;
542 :
543 4 : swq_expr_node * pNode = (swq_expr_node *) m_poAttrQuery->GetSWGExpr();
544 16 : if (pNode->eNodeType == SNT_OPERATION &&
545 : (pNode->nOperation == SWQ_EQ ||
546 : pNode->nOperation == SWQ_GE ||
547 : pNode->nOperation == SWQ_LE ||
548 : pNode->nOperation == SWQ_GT ||
549 : pNode->nOperation == SWQ_LT) &&
550 : pNode->nSubExprCount == 2 &&
551 4 : pNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
552 4 : pNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
553 : {
554 4 : int nIndex = pNode->papoSubExpr[0]->field_index;
555 4 : swq_field_type eType = pNode->papoSubExpr[1]->field_type;
556 4 : const char* pszFieldName = poFeatureDefn->GetFieldDefn(nIndex)->GetNameRef();
557 :
558 4 : if (pNode->nOperation == SWQ_EQ &&
559 : nIndex == _ID_FIELD && eType == SWQ_STRING)
560 : {
561 0 : bCanHandleFilter = TRUE;
562 :
563 0 : osURI = "/";
564 0 : osURI += osEscapedName;
565 0 : osURI += "/_all_docs?";
566 : }
567 4 : else if (nIndex >= FIRST_FIELD &&
568 : (eType == SWQ_STRING || eType == SWQ_INTEGER || eType == SWQ_FLOAT))
569 : {
570 4 : int bFoundFilter = HasFilterOnFieldOrCreateIfNecessary(pszFieldName);
571 4 : if (bFoundFilter)
572 : {
573 2 : bCanHandleFilter = TRUE;
574 :
575 2 : osURI = "/";
576 2 : osURI += osEscapedName;
577 2 : osURI += "/_design/ogr_filter_";
578 2 : osURI += pszFieldName;
579 2 : osURI += "/_view/filter?";
580 : }
581 : }
582 :
583 4 : if (bCanHandleFilter)
584 : {
585 2 : const char* pszOp = OGRCouchDBGetOpStr(pNode->nOperation, bOutHasStrictComparisons);
586 2 : CPLString osVal = OGRCouchDBGetValue(eType, pNode->papoSubExpr[1]);
587 2 : CPLDebug("CouchDB", "Evaluating %s %s %s", pszFieldName, pszOp, osVal.c_str());
588 :
589 2 : osURI += OGRCouchDBGetKeyName(pNode->nOperation);
590 2 : osURI += "=";
591 2 : osURI += osVal;
592 : }
593 : }
594 0 : else if (pNode->eNodeType == SNT_OPERATION &&
595 : pNode->nOperation == SWQ_AND &&
596 : pNode->nSubExprCount == 2 &&
597 0 : pNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
598 0 : pNode->papoSubExpr[1]->eNodeType == SNT_OPERATION &&
599 0 : (((pNode->papoSubExpr[0]->nOperation == SWQ_GE ||
600 0 : pNode->papoSubExpr[0]->nOperation == SWQ_GT) &&
601 0 : (pNode->papoSubExpr[1]->nOperation == SWQ_LE ||
602 0 : pNode->papoSubExpr[1]->nOperation == SWQ_LT)) ||
603 0 : ((pNode->papoSubExpr[0]->nOperation == SWQ_LE ||
604 0 : pNode->papoSubExpr[0]->nOperation == SWQ_LT) &&
605 0 : (pNode->papoSubExpr[1]->nOperation == SWQ_GE ||
606 0 : pNode->papoSubExpr[1]->nOperation == SWQ_GT))) &&
607 0 : pNode->papoSubExpr[0]->nSubExprCount == 2 &&
608 0 : pNode->papoSubExpr[1]->nSubExprCount == 2 &&
609 0 : pNode->papoSubExpr[0]->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
610 0 : pNode->papoSubExpr[0]->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
611 0 : pNode->papoSubExpr[1]->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
612 0 : pNode->papoSubExpr[1]->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
613 : {
614 0 : int nIndex0 = pNode->papoSubExpr[0]->papoSubExpr[0]->field_index;
615 0 : swq_field_type eType0 = pNode->papoSubExpr[0]->papoSubExpr[1]->field_type;
616 0 : int nIndex1 = pNode->papoSubExpr[1]->papoSubExpr[0]->field_index;
617 0 : swq_field_type eType1 = pNode->papoSubExpr[1]->papoSubExpr[1]->field_type;
618 0 : const char* pszFieldName = poFeatureDefn->GetFieldDefn(nIndex0)->GetNameRef();
619 :
620 0 : if (nIndex0 == nIndex1 && eType0 == eType1 &&
621 : nIndex0 == _ID_FIELD && eType0 == SWQ_STRING)
622 : {
623 0 : bCanHandleFilter = TRUE;
624 :
625 0 : osURI = "/";
626 0 : osURI += osEscapedName;
627 0 : osURI += "/_all_docs?";
628 : }
629 0 : else if (nIndex0 == nIndex1 && eType0 == eType1 &&
630 : nIndex0 >= FIRST_FIELD &&
631 : (eType0 == SWQ_STRING || eType0 == SWQ_INTEGER || eType0 == SWQ_FLOAT))
632 : {
633 0 : int bFoundFilter = HasFilterOnFieldOrCreateIfNecessary(pszFieldName);
634 0 : if (bFoundFilter)
635 : {
636 0 : bCanHandleFilter = TRUE;
637 :
638 0 : osURI = "/";
639 0 : osURI += osEscapedName;
640 0 : osURI += "/_design/ogr_filter_";
641 0 : osURI += pszFieldName;
642 0 : osURI += "/_view/filter?";
643 : }
644 : }
645 :
646 0 : if (bCanHandleFilter)
647 : {
648 0 : swq_field_type eType = eType0;
649 0 : CPLString osVal0 = OGRCouchDBGetValue(eType, pNode->papoSubExpr[0]->papoSubExpr[1]);
650 0 : CPLString osVal1 = OGRCouchDBGetValue(eType, pNode->papoSubExpr[1]->papoSubExpr[1]);
651 :
652 0 : int nOperation0 = pNode->papoSubExpr[0]->nOperation;
653 0 : int nOperation1 = pNode->papoSubExpr[1]->nOperation;
654 :
655 0 : const char* pszOp0 = OGRCouchDBGetOpStr(nOperation0, bOutHasStrictComparisons);
656 0 : const char* pszOp1 = OGRCouchDBGetOpStr(nOperation1, bOutHasStrictComparisons);
657 :
658 : CPLDebug("CouchDB", "Evaluating %s %s %s AND %s %s %s",
659 : pszFieldName, pszOp0, osVal0.c_str(),
660 0 : pszFieldName, pszOp1, osVal1.c_str());
661 :
662 0 : osURI += OGRCouchDBGetKeyName(nOperation0);
663 0 : osURI += "=";
664 0 : osURI += osVal0;
665 0 : osURI += "&";
666 0 : osURI += OGRCouchDBGetKeyName(nOperation1);
667 0 : osURI += "=";
668 0 : osURI += osVal1;
669 : }
670 : }
671 0 : else if (pNode->eNodeType == SNT_OPERATION &&
672 : pNode->nOperation == SWQ_BETWEEN &&
673 : pNode->nSubExprCount == 3 &&
674 0 : pNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
675 0 : pNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
676 0 : pNode->papoSubExpr[2]->eNodeType == SNT_CONSTANT)
677 : {
678 0 : int nIndex = pNode->papoSubExpr[0]->field_index;
679 0 : swq_field_type eType = pNode->papoSubExpr[0]->field_type;
680 0 : const char* pszFieldName = poFeatureDefn->GetFieldDefn(nIndex)->GetNameRef();
681 :
682 0 : if (nIndex == _ID_FIELD && eType == SWQ_STRING)
683 : {
684 0 : bCanHandleFilter = TRUE;
685 :
686 0 : osURI = "/";
687 0 : osURI += osEscapedName;
688 0 : osURI += "/_all_docs?";
689 : }
690 0 : else if (nIndex >= FIRST_FIELD &&
691 : (eType == SWQ_STRING || eType == SWQ_INTEGER || eType == SWQ_FLOAT))
692 : {
693 0 : int bFoundFilter = HasFilterOnFieldOrCreateIfNecessary(pszFieldName);
694 0 : if (bFoundFilter)
695 : {
696 0 : bCanHandleFilter = TRUE;
697 :
698 0 : osURI = "/";
699 0 : osURI += osEscapedName;
700 0 : osURI += "/_design/ogr_filter_";
701 0 : osURI += pszFieldName;
702 0 : osURI += "/_view/filter?";
703 : }
704 : }
705 :
706 0 : if (bCanHandleFilter)
707 : {
708 0 : CPLString osVal0 = OGRCouchDBGetValue(eType, pNode->papoSubExpr[1]);
709 0 : CPLString osVal1 = OGRCouchDBGetValue(eType, pNode->papoSubExpr[2]);
710 :
711 : CPLDebug("CouchDB", "Evaluating %s BETWEEN %s AND %s",
712 0 : pszFieldName, osVal0.c_str(), osVal1.c_str());
713 :
714 0 : osURI += OGRCouchDBGetKeyName(SWQ_GE);
715 0 : osURI += "=";
716 0 : osURI += osVal0;
717 0 : osURI += "&";
718 0 : osURI += OGRCouchDBGetKeyName(SWQ_LE);
719 0 : osURI += "=";
720 0 : osURI += osVal1;
721 : }
722 : }
723 :
724 0 : return osURI;
725 : }
726 :
727 : /************************************************************************/
728 : /* FetchNextRowsAttributeFilter() */
729 : /************************************************************************/
730 :
731 2 : int OGRCouchDBTableLayer::FetchNextRowsAttributeFilter()
732 : {
733 2 : if (bHasInstalledAttributeFilter)
734 : {
735 2 : bHasInstalledAttributeFilter = FALSE;
736 :
737 2 : CPLAssert(nOffset == 0);
738 :
739 2 : int bOutHasStrictComparisons = FALSE;
740 2 : osURIAttributeFilter = BuildAttrQueryURI(bOutHasStrictComparisons);
741 :
742 2 : if (osURIAttributeFilter.size() == 0)
743 : {
744 : CPLDebug("CouchDB",
745 1 : "Turning to client-side attribute filtering");
746 1 : bServerSideAttributeFilteringWorks = FALSE;
747 1 : return FALSE;
748 : }
749 : }
750 :
751 1 : CPLString osURI(osURIAttributeFilter);
752 : osURI += CPLSPrintf("&limit=%d&skip=%d&include_docs=true",
753 1 : GetFeaturesToFetch(), nOffset);
754 1 : if (strstr(osURI, "/_all_docs?") == NULL)
755 1 : osURI += "&reduce=false";
756 1 : json_object* poAnswerObj = poDS->GET(osURI);
757 1 : return FetchNextRowsAnalyseDocs(poAnswerObj);
758 : }
759 :
760 : /************************************************************************/
761 : /* FetchNextRows() */
762 : /************************************************************************/
763 :
764 10 : int OGRCouchDBTableLayer::FetchNextRows()
765 : {
766 10 : json_object_put(poFeatures);
767 10 : poFeatures = NULL;
768 10 : aoFeatures.resize(0);
769 :
770 10 : if( m_poFilterGeom != NULL && bServerSideSpatialFilteringWorks )
771 : {
772 3 : int bRet = FetchNextRowsSpatialFilter();
773 3 : if (bRet || bServerSideSpatialFilteringWorks)
774 2 : return bRet;
775 : }
776 :
777 8 : if( m_poAttrQuery != NULL && bServerSideAttributeFilteringWorks )
778 : {
779 2 : int bRet = FetchNextRowsAttributeFilter();
780 2 : if (bRet || bServerSideAttributeFilteringWorks)
781 1 : return bRet;
782 : }
783 :
784 7 : CPLString osURI("/");
785 7 : osURI += osEscapedName;
786 : osURI += CPLSPrintf("/_all_docs?limit=%d&skip=%d&include_docs=true",
787 7 : GetFeaturesToFetch(), nOffset);
788 7 : json_object* poAnswerObj = poDS->GET(osURI);
789 7 : return FetchNextRowsAnalyseDocs(poAnswerObj);
790 : }
791 :
792 :
793 : /************************************************************************/
794 : /* GetFeature() */
795 : /************************************************************************/
796 :
797 2 : OGRFeature * OGRCouchDBTableLayer::GetFeature( long nFID )
798 : {
799 2 : GetLayerDefn();
800 :
801 2 : return GetFeature(CPLSPrintf("%09d", (int)nFID));
802 : }
803 :
804 : /************************************************************************/
805 : /* GetFeature() */
806 : /************************************************************************/
807 :
808 2 : OGRFeature * OGRCouchDBTableLayer::GetFeature( const char* pszId )
809 : {
810 2 : GetLayerDefn();
811 :
812 2 : CPLString osURI("/");
813 2 : osURI += osEscapedName;
814 2 : osURI += "/";
815 2 : osURI += pszId;
816 2 : json_object* poAnswerObj = poDS->GET(osURI);
817 2 : if (poAnswerObj == NULL)
818 0 : return NULL;
819 :
820 2 : if ( !json_object_is_type(poAnswerObj, json_type_object) )
821 : {
822 : CPLError(CE_Failure, CPLE_AppDefined, "GetFeature(%s) failed",
823 0 : pszId);
824 0 : json_object_put(poAnswerObj);
825 0 : return NULL;
826 : }
827 :
828 2 : if ( poDS->IsError(poAnswerObj, CPLSPrintf("GetFeature(%s) failed", pszId)) )
829 : {
830 0 : json_object_put(poAnswerObj);
831 0 : return NULL;
832 : }
833 :
834 2 : OGRFeature* poFeature = TranslateFeature( poAnswerObj );
835 :
836 2 : json_object_put( poAnswerObj );
837 :
838 2 : return poFeature;
839 : }
840 :
841 : /************************************************************************/
842 : /* GetLayerDefn() */
843 : /************************************************************************/
844 :
845 145 : OGRFeatureDefn * OGRCouchDBTableLayer::GetLayerDefn()
846 : {
847 145 : if (poFeatureDefn == NULL)
848 12 : LoadMetadata();
849 :
850 145 : if (poFeatureDefn == NULL)
851 : {
852 6 : poFeatureDefn = new OGRFeatureDefn( osName );
853 6 : poFeatureDefn->Reference();
854 :
855 6 : poFeatureDefn->SetGeomType(eGeomType);
856 :
857 6 : OGRFieldDefn oFieldId("_id", OFTString);
858 6 : poFeatureDefn->AddFieldDefn(&oFieldId);
859 :
860 6 : OGRFieldDefn oFieldRev("_rev", OFTString);
861 6 : poFeatureDefn->AddFieldDefn(&oFieldRev);
862 :
863 6 : if (nNextFIDForCreate == 0)
864 : {
865 0 : return poFeatureDefn;
866 : }
867 :
868 6 : CPLString osURI("/");
869 6 : osURI += osEscapedName;
870 6 : osURI += "/_all_docs?limit=10&include_docs=true";
871 6 : json_object* poAnswerObj = poDS->GET(osURI);
872 6 : if (poAnswerObj == NULL)
873 0 : return poFeatureDefn;
874 :
875 6 : BuildFeatureDefnFromRows(poAnswerObj);
876 :
877 6 : eGeomType = poFeatureDefn->GetGeomType();
878 :
879 6 : json_object_put(poAnswerObj);
880 : }
881 :
882 145 : return poFeatureDefn;
883 : }
884 :
885 : /************************************************************************/
886 : /* GetFeatureCount() */
887 : /************************************************************************/
888 :
889 6 : int OGRCouchDBTableLayer::GetFeatureCount(int bForce)
890 : {
891 6 : GetLayerDefn();
892 :
893 6 : if (m_poFilterGeom == NULL && m_poAttrQuery != NULL)
894 : {
895 2 : int bOutHasStrictComparisons = FALSE;
896 2 : CPLString osURI = BuildAttrQueryURI(bOutHasStrictComparisons);
897 2 : if (!bOutHasStrictComparisons && osURI.size() != 0 &&
898 : strstr(osURI, "/_all_docs?") == NULL)
899 : {
900 1 : osURI += "&reduce=true";
901 1 : json_object* poAnswerObj = poDS->GET(osURI);
902 1 : json_object* poRows = NULL;
903 1 : if (poAnswerObj != NULL &&
904 : json_object_is_type(poAnswerObj, json_type_object) &&
905 : (poRows = json_object_object_get(poAnswerObj, "rows")) != NULL &&
906 : json_object_is_type(poRows, json_type_array))
907 : {
908 1 : int nLength = json_object_array_length(poRows);
909 1 : if (nLength == 0)
910 : {
911 0 : json_object_put(poAnswerObj);
912 0 : return 0;
913 : }
914 1 : else if (nLength == 1)
915 : {
916 1 : json_object* poRow = json_object_array_get_idx(poRows, 0);
917 1 : if (poRow && json_object_is_type(poRow, json_type_object))
918 : {
919 : /* for string fields */
920 1 : json_object* poValue = json_object_object_get(poRow, "value");
921 1 : if (poValue != NULL && json_object_is_type(poValue, json_type_int))
922 : {
923 0 : int nVal = json_object_get_int(poValue);
924 0 : json_object_put(poAnswerObj);
925 0 : return nVal;
926 : }
927 1 : else if (poValue != NULL && json_object_is_type(poValue, json_type_object))
928 : {
929 : /* for numeric fields */
930 1 : json_object* poCount = json_object_object_get(poValue, "count");
931 1 : if (poCount != NULL && json_object_is_type(poCount, json_type_int))
932 : {
933 1 : int nVal = json_object_get_int(poCount);
934 1 : json_object_put(poAnswerObj);
935 1 : return nVal;
936 : }
937 : }
938 : }
939 : }
940 : }
941 0 : json_object_put(poAnswerObj);
942 0 : }
943 : }
944 :
945 5 : if (m_poFilterGeom != NULL && m_poAttrQuery == NULL &&
946 : wkbFlatten(eGeomType) == wkbPoint)
947 : {
948 : /* Only optimize for wkbPoint case. Otherwise the result might be higher */
949 : /* than the real value since the intersection of the bounding box of the */
950 : /* geometry of a feature does not necessary mean the intersection of the */
951 : /* geometry itself */
952 0 : RunSpatialFilterQueryIfNecessary();
953 0 : if (bServerSideSpatialFilteringWorks)
954 : {
955 0 : return (int)aosIdsToFetch.size();
956 : }
957 : }
958 :
959 5 : if (m_poFilterGeom != NULL || m_poAttrQuery != NULL)
960 3 : return OGRCouchDBLayer::GetFeatureCount(bForce);
961 :
962 2 : return GetTotalFeatureCount();
963 : }
964 :
965 : /************************************************************************/
966 : /* GetFeatureCount() */
967 : /************************************************************************/
968 :
969 2 : int OGRCouchDBTableLayer::GetTotalFeatureCount()
970 : {
971 2 : int nTotalRows = -1;
972 :
973 2 : CPLString osURI("/");
974 2 : osURI += osEscapedName;
975 2 : osURI += "/_all_docs?startkey_docid=_&endkey_docid=_zzzzzzzzzzzzzzz";
976 2 : json_object* poAnswerObj = poDS->GET(osURI);
977 2 : if (poAnswerObj == NULL)
978 0 : return nTotalRows;
979 :
980 2 : if ( !json_object_is_type(poAnswerObj, json_type_object) )
981 : {
982 0 : json_object_put(poAnswerObj);
983 0 : return nTotalRows;
984 : }
985 :
986 : json_object* poTotalRows = json_object_object_get(poAnswerObj,
987 2 : "total_rows");
988 2 : if (poTotalRows != NULL &&
989 : json_object_is_type(poTotalRows, json_type_int))
990 : {
991 2 : nTotalRows = json_object_get_int(poTotalRows);
992 : }
993 :
994 2 : json_object* poRows = json_object_object_get(poAnswerObj, "rows");
995 2 : if (poRows == NULL ||
996 : !json_object_is_type(poRows, json_type_array))
997 : {
998 0 : json_object_put(poAnswerObj);
999 0 : return nTotalRows;
1000 : }
1001 :
1002 2 : bHasOGRSpatial = FALSE;
1003 :
1004 2 : int nSpecialRows = json_object_array_length(poRows);
1005 7 : for(int i=0;i<nSpecialRows;i++)
1006 : {
1007 5 : json_object* poRow = json_object_array_get_idx(poRows, i);
1008 5 : if ( poRow != NULL &&
1009 : json_object_is_type(poRow, json_type_object) )
1010 : {
1011 5 : json_object* poId = json_object_object_get(poRow, "id");
1012 5 : const char* pszId = json_object_get_string(poId);
1013 5 : if ( pszId && strcmp(pszId, "_design/ogr_spatial") == 0)
1014 : {
1015 1 : bHasOGRSpatial = TRUE;
1016 : }
1017 : }
1018 : }
1019 :
1020 2 : if (!bHasOGRSpatial)
1021 : {
1022 1 : bServerSideSpatialFilteringWorks = FALSE;
1023 : }
1024 :
1025 2 : if (nTotalRows >= nSpecialRows)
1026 2 : nTotalRows -= nSpecialRows;
1027 :
1028 2 : json_object_put(poAnswerObj);
1029 :
1030 2 : return nTotalRows;
1031 : }
1032 :
1033 : /************************************************************************/
1034 : /* CreateField() */
1035 : /************************************************************************/
1036 :
1037 0 : OGRErr OGRCouchDBTableLayer::CreateField( OGRFieldDefn *poField,
1038 : int bApproxOK )
1039 : {
1040 :
1041 0 : if (!poDS->IsReadWrite())
1042 : {
1043 : CPLError(CE_Failure, CPLE_AppDefined,
1044 0 : "Operation not available in read-only mode");
1045 0 : return OGRERR_FAILURE;
1046 : }
1047 :
1048 0 : GetLayerDefn();
1049 :
1050 0 : poFeatureDefn->AddFieldDefn(poField);
1051 :
1052 0 : bMustWriteMetadata = TRUE;
1053 :
1054 0 : return OGRERR_NONE;
1055 : }
1056 :
1057 : /************************************************************************/
1058 : /* OGRCouchDBWriteFeature */
1059 : /************************************************************************/
1060 :
1061 0 : static json_object* OGRCouchDBWriteFeature( OGRFeature* poFeature,
1062 : OGRwkbGeometryType eGeomType,
1063 : int bGeoJSONDocument,
1064 : int nCoordPrecision )
1065 : {
1066 0 : CPLAssert( NULL != poFeature );
1067 :
1068 0 : json_object* poObj = json_object_new_object();
1069 0 : CPLAssert( NULL != poObj );
1070 :
1071 0 : if (poFeature->IsFieldSet(_ID_FIELD))
1072 : {
1073 0 : const char* pszId = poFeature->GetFieldAsString(_ID_FIELD);
1074 : json_object_object_add( poObj, "_id",
1075 0 : json_object_new_string(pszId) );
1076 :
1077 0 : if ( poFeature->GetFID() != OGRNullFID &&
1078 : strcmp(CPLSPrintf("%09ld", poFeature->GetFID()), pszId) != 0 )
1079 : {
1080 : CPLDebug("CouchDB",
1081 : "_id field = %s, but FID = %09ld --> taking into account _id field only",
1082 : pszId,
1083 0 : poFeature->GetFID());
1084 : }
1085 : }
1086 0 : else if ( poFeature->GetFID() != OGRNullFID )
1087 : {
1088 : json_object_object_add( poObj, "_id",
1089 0 : json_object_new_string(CPLSPrintf("%09ld", poFeature->GetFID())) );
1090 : }
1091 :
1092 0 : if (poFeature->IsFieldSet(_REV_FIELD))
1093 : {
1094 0 : const char* pszRev = poFeature->GetFieldAsString(_REV_FIELD);
1095 : json_object_object_add( poObj, "_rev",
1096 0 : json_object_new_string(pszRev) );
1097 : }
1098 :
1099 0 : if (bGeoJSONDocument)
1100 : {
1101 : json_object_object_add( poObj, "type",
1102 0 : json_object_new_string("Feature") );
1103 : }
1104 :
1105 : /* -------------------------------------------------------------------- */
1106 : /* Write feature attributes to GeoJSON "properties" object. */
1107 : /* -------------------------------------------------------------------- */
1108 0 : json_object* poObjProps = NULL;
1109 :
1110 0 : poObjProps = OGRGeoJSONWriteAttributes( poFeature );
1111 0 : if (poObjProps)
1112 : {
1113 0 : json_object_object_del(poObjProps, "_id");
1114 0 : json_object_object_del(poObjProps, "_rev");
1115 : }
1116 :
1117 0 : if (bGeoJSONDocument)
1118 : {
1119 0 : json_object_object_add( poObj, "properties", poObjProps );
1120 : }
1121 : else
1122 : {
1123 : json_object_iter it;
1124 0 : it.key = NULL;
1125 0 : it.val = NULL;
1126 0 : it.entry = NULL;
1127 0 : json_object_object_foreachC( poObjProps, it )
1128 : {
1129 0 : json_object_object_add( poObj, it.key, json_object_get(it.val) );
1130 : }
1131 0 : json_object_put(poObjProps);
1132 : }
1133 :
1134 : /* -------------------------------------------------------------------- */
1135 : /* Write feature geometry to GeoJSON "geometry" object. */
1136 : /* Null geometries are allowed, according to the GeoJSON Spec. */
1137 : /* -------------------------------------------------------------------- */
1138 0 : if (eGeomType != wkbNone)
1139 : {
1140 0 : json_object* poObjGeom = NULL;
1141 :
1142 0 : OGRGeometry* poGeometry = poFeature->GetGeometryRef();
1143 0 : if ( NULL != poGeometry )
1144 : {
1145 0 : poObjGeom = OGRGeoJSONWriteGeometry( poGeometry, nCoordPrecision );
1146 0 : if ( poObjGeom != NULL &&
1147 0 : wkbFlatten(poGeometry->getGeometryType()) != wkbPoint &&
1148 0 : !poGeometry->IsEmpty() )
1149 : {
1150 0 : OGREnvelope sEnvelope;
1151 0 : poGeometry->getEnvelope(&sEnvelope);
1152 :
1153 0 : json_object* bbox = json_object_new_array();
1154 0 : json_object_array_add(bbox, json_object_new_double_with_precision(sEnvelope.MinX, nCoordPrecision));
1155 0 : json_object_array_add(bbox, json_object_new_double_with_precision(sEnvelope.MinY, nCoordPrecision));
1156 0 : json_object_array_add(bbox, json_object_new_double_with_precision(sEnvelope.MaxX, nCoordPrecision));
1157 0 : json_object_array_add(bbox, json_object_new_double_with_precision(sEnvelope.MaxY, nCoordPrecision));
1158 0 : json_object_object_add( poObjGeom, "bbox", bbox );
1159 : }
1160 : }
1161 :
1162 0 : json_object_object_add( poObj, "geometry", poObjGeom );
1163 : }
1164 :
1165 0 : return poObj;
1166 : }
1167 :
1168 : /************************************************************************/
1169 : /* GetMaximumId() */
1170 : /************************************************************************/
1171 :
1172 0 : int OGRCouchDBTableLayer::GetMaximumId()
1173 : {
1174 0 : CPLString osURI("/");
1175 0 : osURI += osEscapedName;
1176 0 : osURI += "/_all_docs?startkey_docid=999999999&endkey_docid=000000000&descending=true&limit=1";
1177 0 : json_object* poAnswerObj = poDS->GET(osURI);
1178 0 : if (poAnswerObj == NULL)
1179 0 : return -1;
1180 :
1181 0 : if ( !json_object_is_type(poAnswerObj, json_type_object) )
1182 : {
1183 0 : CPLError(CE_Failure, CPLE_AppDefined, "GetMaximumId() failed");
1184 0 : json_object_put(poAnswerObj);
1185 0 : return -1;
1186 : }
1187 :
1188 0 : if (poDS->IsError(poAnswerObj, "GetMaximumId() failed"))
1189 : {
1190 0 : json_object_put(poAnswerObj);
1191 0 : return -1;
1192 : }
1193 :
1194 0 : json_object* poRows = json_object_object_get(poAnswerObj, "rows");
1195 0 : if (poRows == NULL ||
1196 : !json_object_is_type(poRows, json_type_array))
1197 : {
1198 0 : CPLError(CE_Failure, CPLE_AppDefined, "GetMaximumId() failed");
1199 0 : json_object_put(poAnswerObj);
1200 0 : return -1;
1201 : }
1202 :
1203 0 : int nRows = json_object_array_length(poRows);
1204 0 : if (nRows != 1)
1205 : {
1206 0 : CPLError(CE_Failure, CPLE_AppDefined, "GetMaximumId() failed");
1207 0 : json_object_put(poAnswerObj);
1208 0 : return -1;
1209 : }
1210 :
1211 0 : json_object* poRow = json_object_array_get_idx(poRows, 0);
1212 0 : if ( poRow == NULL ||
1213 : !json_object_is_type(poRow, json_type_object) )
1214 : {
1215 0 : CPLError(CE_Failure, CPLE_AppDefined, "GetMaximumId() failed");
1216 0 : json_object_put(poAnswerObj);
1217 0 : return -1;
1218 : }
1219 :
1220 0 : json_object* poId = json_object_object_get(poRow, "id");
1221 0 : const char* pszId = json_object_get_string(poId);
1222 0 : if (pszId != NULL)
1223 : {
1224 0 : int nId = atoi(pszId);
1225 0 : json_object_put(poAnswerObj);
1226 0 : return nId;
1227 : }
1228 :
1229 0 : json_object_put(poAnswerObj);
1230 0 : return -1;
1231 : }
1232 :
1233 : /************************************************************************/
1234 : /* CreateFeature() */
1235 : /************************************************************************/
1236 :
1237 0 : OGRErr OGRCouchDBTableLayer::CreateFeature( OGRFeature *poFeature )
1238 :
1239 : {
1240 0 : GetLayerDefn();
1241 :
1242 0 : if (!poDS->IsReadWrite())
1243 : {
1244 : CPLError(CE_Failure, CPLE_AppDefined,
1245 0 : "Operation not available in read-only mode");
1246 0 : return OGRERR_FAILURE;
1247 : }
1248 :
1249 0 : if (poFeature->IsFieldSet(_REV_FIELD))
1250 : {
1251 : static int bOnce = FALSE;
1252 0 : if (!bOnce)
1253 : {
1254 0 : bOnce = TRUE;
1255 0 : CPLDebug("CouchDB", "CreateFeature() should be called with an unset _rev field. Ignoring it");
1256 : }
1257 0 : poFeature->UnsetField(_REV_FIELD);
1258 : }
1259 :
1260 0 : if (nNextFIDForCreate < 0)
1261 : {
1262 0 : nNextFIDForCreate = GetMaximumId();
1263 0 : if (nNextFIDForCreate >= 0)
1264 0 : nNextFIDForCreate ++;
1265 : else
1266 0 : nNextFIDForCreate = GetTotalFeatureCount();
1267 : }
1268 :
1269 0 : OGRGeometry* poGeom = poFeature->GetGeometryRef();
1270 0 : if (bExtentValid && poGeom != NULL && !poGeom->IsEmpty())
1271 : {
1272 0 : OGREnvelope sEnvelope;
1273 0 : poGeom->getEnvelope(&sEnvelope);
1274 0 : if (!bExtentSet)
1275 : {
1276 0 : dfMinX = sEnvelope.MinX;
1277 0 : dfMinY = sEnvelope.MinY;
1278 0 : dfMaxX = sEnvelope.MaxX;
1279 0 : dfMaxY = sEnvelope.MaxY;
1280 0 : bExtentSet = TRUE;
1281 : }
1282 0 : if (sEnvelope.MinX < dfMinX)
1283 0 : dfMinX = sEnvelope.MinX;
1284 0 : if (sEnvelope.MinY < dfMinY)
1285 0 : dfMinY = sEnvelope.MinY;
1286 0 : if (sEnvelope.MaxX > dfMaxX)
1287 0 : dfMaxX = sEnvelope.MaxX;
1288 0 : if (sEnvelope.MaxY > dfMaxY)
1289 0 : dfMaxY = sEnvelope.MaxY;
1290 : }
1291 :
1292 0 : if (bExtentValid && eGeomType != wkbNone)
1293 0 : bMustWriteMetadata = TRUE;
1294 :
1295 0 : int nFID = nNextFIDForCreate ++;
1296 0 : CPLString osFID;
1297 0 : if (!poFeature->IsFieldSet(_ID_FIELD) ||
1298 : !CSLTestBoolean(CPLGetConfigOption("COUCHDB_PRESERVE_ID_ON_INSERT", "FALSE")))
1299 : {
1300 0 : if (poFeature->GetFID() != OGRNullFID)
1301 : {
1302 0 : nFID = (int)poFeature->GetFID();
1303 : }
1304 0 : osFID = CPLSPrintf("%09d", nFID);
1305 :
1306 0 : poFeature->SetField(_ID_FIELD, osFID);
1307 0 : poFeature->SetFID(nFID);
1308 : }
1309 : else
1310 : {
1311 0 : const char* pszId = poFeature->GetFieldAsString(_ID_FIELD);
1312 0 : osFID = pszId;
1313 : }
1314 :
1315 : json_object* poObj = OGRCouchDBWriteFeature(poFeature, eGeomType,
1316 : bGeoJSONDocument,
1317 0 : nCoordPrecision);
1318 :
1319 0 : if (bInTransaction)
1320 : {
1321 0 : aoTransactionFeatures.push_back(poObj);
1322 :
1323 0 : return OGRERR_NONE;
1324 : }
1325 :
1326 0 : const char* pszJson = json_object_to_json_string( poObj );
1327 0 : CPLString osURI("/");
1328 0 : osURI += osEscapedName;
1329 0 : osURI += "/";
1330 0 : osURI += osFID;
1331 0 : json_object* poAnswerObj = poDS->PUT(osURI, pszJson);
1332 0 : json_object_put( poObj );
1333 :
1334 0 : if (poAnswerObj == NULL)
1335 0 : return OGRERR_FAILURE;
1336 :
1337 0 : if (!poDS->IsOK(poAnswerObj, "Feature creation failed"))
1338 : {
1339 0 : json_object_put(poAnswerObj);
1340 0 : return OGRERR_FAILURE;
1341 : }
1342 :
1343 0 : json_object* poId = json_object_object_get(poAnswerObj, "id");
1344 0 : json_object* poRev = json_object_object_get(poAnswerObj, "rev");
1345 :
1346 0 : const char* pszId = json_object_get_string(poId);
1347 0 : const char* pszRev = json_object_get_string(poRev);
1348 :
1349 0 : if (pszId)
1350 : {
1351 0 : poFeature->SetField(_ID_FIELD, pszId);
1352 :
1353 0 : int nFID = atoi(pszId);
1354 0 : const char* pszFID = CPLSPrintf("%09d", nFID);
1355 0 : if (strcmp(pszId, pszFID) == 0)
1356 0 : poFeature->SetFID(nFID);
1357 : else
1358 0 : poFeature->SetFID(-1);
1359 : }
1360 0 : if (pszRev)
1361 : {
1362 0 : poFeature->SetField(_REV_FIELD, pszRev);
1363 : }
1364 :
1365 0 : json_object_put(poAnswerObj);
1366 :
1367 0 : nUpdateSeq ++;
1368 :
1369 0 : return OGRERR_NONE;
1370 : }
1371 :
1372 : /************************************************************************/
1373 : /* SetFeature() */
1374 : /************************************************************************/
1375 :
1376 0 : OGRErr OGRCouchDBTableLayer::SetFeature( OGRFeature *poFeature )
1377 : {
1378 0 : GetLayerDefn();
1379 :
1380 0 : if (!poDS->IsReadWrite())
1381 : {
1382 : CPLError(CE_Failure, CPLE_AppDefined,
1383 0 : "Operation not available in read-only mode");
1384 0 : return OGRERR_FAILURE;
1385 : }
1386 :
1387 0 : if (!poFeature->IsFieldSet(_ID_FIELD))
1388 : {
1389 : CPLError(CE_Failure, CPLE_AppDefined,
1390 0 : "SetFeature() requires non null _id field");
1391 0 : return OGRERR_FAILURE;
1392 : }
1393 :
1394 : json_object* poObj = OGRCouchDBWriteFeature(poFeature, eGeomType,
1395 : bGeoJSONDocument,
1396 0 : nCoordPrecision);
1397 :
1398 0 : const char* pszJson = json_object_to_json_string( poObj );
1399 0 : CPLString osURI("/");
1400 0 : osURI += osEscapedName;
1401 0 : osURI += "/";
1402 0 : osURI += poFeature->GetFieldAsString(_ID_FIELD);
1403 0 : json_object* poAnswerObj = poDS->PUT(osURI, pszJson);
1404 0 : json_object_put( poObj );
1405 :
1406 0 : if (poAnswerObj == NULL)
1407 0 : return OGRERR_FAILURE;
1408 :
1409 0 : if (!poDS->IsOK(poAnswerObj, "Feature update failed"))
1410 : {
1411 0 : json_object_put(poAnswerObj);
1412 0 : return OGRERR_FAILURE;
1413 : }
1414 :
1415 0 : json_object* poRev = json_object_object_get(poAnswerObj, "rev");
1416 0 : const char* pszRev = json_object_get_string(poRev);
1417 0 : poFeature->SetField(_REV_FIELD, pszRev);
1418 :
1419 0 : json_object_put(poAnswerObj);
1420 :
1421 0 : if (bExtentValid && eGeomType != wkbNone)
1422 : {
1423 0 : bExtentValid = FALSE;
1424 0 : bMustWriteMetadata = TRUE;
1425 : }
1426 0 : nUpdateSeq ++;
1427 :
1428 0 : return OGRERR_NONE;
1429 : }
1430 :
1431 : /************************************************************************/
1432 : /* DeleteFeature() */
1433 : /************************************************************************/
1434 :
1435 0 : OGRErr OGRCouchDBTableLayer::DeleteFeature( long nFID )
1436 : {
1437 0 : GetLayerDefn();
1438 :
1439 0 : if (!poDS->IsReadWrite())
1440 : {
1441 : CPLError(CE_Failure, CPLE_AppDefined,
1442 0 : "Operation not available in read-only mode");
1443 0 : return OGRERR_FAILURE;
1444 : }
1445 :
1446 0 : OGRFeature* poFeature = GetFeature(nFID);
1447 0 : if (poFeature == NULL)
1448 0 : return OGRERR_FAILURE;
1449 :
1450 0 : return DeleteFeature(poFeature);
1451 : }
1452 :
1453 : /************************************************************************/
1454 : /* DeleteFeature() */
1455 : /************************************************************************/
1456 :
1457 0 : OGRErr OGRCouchDBTableLayer::DeleteFeature( const char* pszId )
1458 : {
1459 0 : GetLayerDefn();
1460 :
1461 0 : if (!poDS->IsReadWrite())
1462 : {
1463 : CPLError(CE_Failure, CPLE_AppDefined,
1464 0 : "Operation not available in read-only mode");
1465 0 : return OGRERR_FAILURE;
1466 : }
1467 :
1468 0 : OGRFeature* poFeature = GetFeature(pszId);
1469 0 : if (poFeature == NULL)
1470 0 : return OGRERR_FAILURE;
1471 :
1472 0 : return DeleteFeature(poFeature);
1473 : }
1474 :
1475 : /************************************************************************/
1476 : /* DeleteFeature() */
1477 : /************************************************************************/
1478 :
1479 0 : OGRErr OGRCouchDBTableLayer::DeleteFeature( OGRFeature* poFeature )
1480 : {
1481 0 : if (!poFeature->IsFieldSet(_ID_FIELD) ||
1482 : !poFeature->IsFieldSet(_REV_FIELD))
1483 : {
1484 0 : delete poFeature;
1485 0 : return OGRERR_FAILURE;
1486 : }
1487 :
1488 0 : const char* pszId = poFeature->GetFieldAsString(_ID_FIELD);
1489 0 : const char* pszRev = poFeature->GetFieldAsString(_REV_FIELD);
1490 :
1491 0 : CPLString osURI("/");
1492 0 : osURI += osEscapedName;
1493 0 : osURI += "/";
1494 0 : osURI += CPLSPrintf("%s?rev=%s", pszId, pszRev);
1495 :
1496 0 : if (bExtentValid && eGeomType != wkbNone)
1497 0 : bMustWriteMetadata = TRUE;
1498 :
1499 0 : OGRGeometry* poGeom = poFeature->GetGeometryRef();
1500 0 : if (bExtentValid && bExtentSet && poGeom != NULL && !poGeom->IsEmpty())
1501 : {
1502 0 : OGREnvelope sEnvelope;
1503 0 : poGeom->getEnvelope(&sEnvelope);
1504 0 : if (dfMinX == sEnvelope.MinX ||
1505 : dfMinY == sEnvelope.MinY ||
1506 : dfMaxX == sEnvelope.MaxX ||
1507 : dfMaxY == sEnvelope.MaxY)
1508 : {
1509 0 : bExtentValid = FALSE;
1510 : }
1511 : }
1512 :
1513 0 : delete poFeature;
1514 :
1515 0 : json_object* poAnswerObj = poDS->DELETE(osURI);
1516 :
1517 0 : if (poAnswerObj == NULL)
1518 0 : return OGRERR_FAILURE;
1519 :
1520 0 : if (!poDS->IsOK(poAnswerObj, "Feature deletion failed"))
1521 : {
1522 0 : json_object_put(poAnswerObj);
1523 0 : return OGRERR_FAILURE;
1524 : }
1525 :
1526 0 : nUpdateSeq ++;
1527 :
1528 0 : json_object_put(poAnswerObj);
1529 :
1530 0 : return OGRERR_NONE;
1531 : }
1532 :
1533 : /************************************************************************/
1534 : /* StartTransaction() */
1535 : /************************************************************************/
1536 :
1537 0 : OGRErr OGRCouchDBTableLayer::StartTransaction()
1538 : {
1539 0 : GetLayerDefn();
1540 :
1541 0 : if (bInTransaction)
1542 : {
1543 0 : CPLError(CE_Failure, CPLE_AppDefined, "Already in transaction");
1544 0 : return OGRERR_FAILURE;
1545 : }
1546 :
1547 0 : if (!poDS->IsReadWrite())
1548 : {
1549 : CPLError(CE_Failure, CPLE_AppDefined,
1550 0 : "Operation not available in read-only mode");
1551 0 : return OGRERR_FAILURE;
1552 : }
1553 :
1554 0 : bInTransaction = TRUE;
1555 :
1556 0 : return OGRERR_NONE;
1557 : }
1558 :
1559 : /************************************************************************/
1560 : /* CommitTransaction() */
1561 : /************************************************************************/
1562 :
1563 0 : OGRErr OGRCouchDBTableLayer::CommitTransaction()
1564 : {
1565 0 : GetLayerDefn();
1566 :
1567 0 : if (!bInTransaction)
1568 : {
1569 0 : CPLError(CE_Failure, CPLE_AppDefined, "Should be in transaction");
1570 0 : return OGRERR_FAILURE;
1571 : }
1572 :
1573 0 : bInTransaction = FALSE;
1574 :
1575 0 : if (aoTransactionFeatures.size() == 0)
1576 0 : return OGRERR_NONE;
1577 :
1578 0 : CPLString osPost("{ \"docs\": [");
1579 0 : for(int i=0;i<(int)aoTransactionFeatures.size();i++)
1580 : {
1581 0 : if (i>0) osPost += ",";
1582 0 : const char* pszJson = json_object_to_json_string( aoTransactionFeatures[i] );
1583 0 : osPost += pszJson;
1584 0 : json_object_put(aoTransactionFeatures[i]);
1585 : }
1586 0 : osPost += "] }";
1587 0 : aoTransactionFeatures.resize(0);
1588 :
1589 0 : CPLString osURI("/");
1590 0 : osURI += osEscapedName;
1591 0 : osURI += "/_bulk_docs";
1592 0 : json_object* poAnswerObj = poDS->POST(osURI, osPost);
1593 :
1594 0 : if (poAnswerObj == NULL)
1595 0 : return OGRERR_FAILURE;
1596 :
1597 0 : if ( json_object_is_type(poAnswerObj, json_type_object) )
1598 : {
1599 0 : poDS->IsError(poAnswerObj, "Bulk feature creation failed");
1600 :
1601 0 : json_object_put(poAnswerObj);
1602 0 : return OGRERR_FAILURE;
1603 : }
1604 :
1605 0 : if ( !json_object_is_type(poAnswerObj, json_type_array) )
1606 : {
1607 0 : CPLError(CE_Failure, CPLE_AppDefined, "Bulk feature creation failed");
1608 0 : json_object_put(poAnswerObj);
1609 :
1610 0 : return OGRERR_FAILURE;
1611 : }
1612 :
1613 0 : int nRows = json_object_array_length(poAnswerObj);
1614 0 : for(int i=0;i<nRows;i++)
1615 : {
1616 0 : json_object* poRow = json_object_array_get_idx(poAnswerObj, i);
1617 0 : if ( poRow != NULL &&
1618 : json_object_is_type(poRow, json_type_object) )
1619 : {
1620 0 : json_object* poId = json_object_object_get(poRow, "id");
1621 0 : json_object* poRev = json_object_object_get(poRow, "rev");
1622 0 : json_object* poError = json_object_object_get(poRow, "error");
1623 0 : json_object* poReason = json_object_object_get(poRow, "reason");
1624 :
1625 0 : const char* pszId = json_object_get_string(poId);
1626 :
1627 0 : if (poError != NULL)
1628 : {
1629 0 : const char* pszError = json_object_get_string(poError);
1630 0 : const char* pszReason = json_object_get_string(poReason);
1631 :
1632 : CPLError(CE_Failure, CPLE_AppDefined,
1633 : "Bulk feature creation failed : for %s: %s, %s",
1634 : pszId ? pszId : "",
1635 : pszError ? pszError : "",
1636 0 : pszReason ? pszReason : "");
1637 : }
1638 0 : else if (poRev != NULL)
1639 : {
1640 : /*const char* pszRev = json_object_get_string(poRev);
1641 : CPLDebug("CouchDB", "id = %s, rev = %s",
1642 : pszId ? pszId : "", pszRev ? pszRev : "");*/
1643 :
1644 0 : nUpdateSeq ++;
1645 : }
1646 : }
1647 : }
1648 :
1649 0 : json_object_put(poAnswerObj);
1650 :
1651 0 : return OGRERR_NONE;
1652 : }
1653 :
1654 : /************************************************************************/
1655 : /* RollbackTransaction() */
1656 : /************************************************************************/
1657 :
1658 0 : OGRErr OGRCouchDBTableLayer::RollbackTransaction()
1659 : {
1660 0 : GetLayerDefn();
1661 :
1662 0 : if (!bInTransaction)
1663 : {
1664 0 : CPLError(CE_Failure, CPLE_AppDefined, "Should be in transaction");
1665 0 : return OGRERR_FAILURE;
1666 : }
1667 0 : bInTransaction = FALSE;
1668 0 : for(int i=0;i<(int)aoTransactionFeatures.size();i++)
1669 : {
1670 0 : json_object_put(aoTransactionFeatures[i]);
1671 : }
1672 0 : aoTransactionFeatures.resize(0);
1673 0 : return OGRERR_NONE;
1674 : }
1675 :
1676 : /************************************************************************/
1677 : /* SetAttributeFilter() */
1678 : /************************************************************************/
1679 :
1680 2 : OGRErr OGRCouchDBTableLayer::SetAttributeFilter( const char *pszQuery )
1681 :
1682 : {
1683 2 : GetLayerDefn();
1684 :
1685 2 : bServerSideAttributeFilteringWorks = TRUE;
1686 :
1687 2 : OGRErr eErr = OGRCouchDBLayer::SetAttributeFilter(pszQuery);
1688 :
1689 2 : if (eErr == OGRERR_NONE)
1690 : {
1691 2 : bHasInstalledAttributeFilter = TRUE;
1692 : }
1693 :
1694 2 : return eErr;
1695 : }
1696 :
1697 :
1698 : /************************************************************************/
1699 : /* SetSpatialFilter() */
1700 : /************************************************************************/
1701 :
1702 2 : void OGRCouchDBTableLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
1703 :
1704 : {
1705 2 : GetLayerDefn();
1706 :
1707 2 : if( InstallFilter( poGeomIn ) )
1708 : {
1709 2 : bMustRunSpatialFilter = TRUE;
1710 :
1711 2 : ResetReading();
1712 : }
1713 2 : }
1714 :
1715 : /************************************************************************/
1716 : /* SetInfoAfterCreation() */
1717 : /************************************************************************/
1718 :
1719 0 : void OGRCouchDBTableLayer::SetInfoAfterCreation(OGRwkbGeometryType eGType,
1720 : OGRSpatialReference* poSRSIn,
1721 : int nUpdateSeqIn,
1722 : int bGeoJSONDocumentIn)
1723 : {
1724 0 : eGeomType = eGType;
1725 0 : nNextFIDForCreate = 0;
1726 0 : bMustWriteMetadata = TRUE;
1727 0 : bExtentValid = TRUE;
1728 0 : bHasLoadedMetadata = TRUE;
1729 0 : nUpdateSeq = nUpdateSeqIn;
1730 0 : bGeoJSONDocument = bGeoJSONDocumentIn;
1731 :
1732 0 : CPLAssert(poSRS == NULL);
1733 0 : poSRS = poSRSIn;
1734 0 : if (poSRS)
1735 0 : poSRS->Reference();
1736 0 : }
1737 :
1738 : /************************************************************************/
1739 : /* GetSpatialRef() */
1740 : /************************************************************************/
1741 :
1742 2 : OGRSpatialReference* OGRCouchDBTableLayer::GetSpatialRef()
1743 : {
1744 2 : LoadMetadata();
1745 :
1746 2 : return poSRS;
1747 : }
1748 :
1749 : /************************************************************************/
1750 : /* OGRCouchDBIsNumericObject() */
1751 : /************************************************************************/
1752 :
1753 32 : static int OGRCouchDBIsNumericObject(json_object* poObj)
1754 : {
1755 32 : int iType = json_object_get_type(poObj);
1756 32 : return iType == json_type_int || iType == json_type_double;
1757 : }
1758 :
1759 : /************************************************************************/
1760 : /* LoadMetadata() */
1761 : /************************************************************************/
1762 :
1763 16 : void OGRCouchDBTableLayer::LoadMetadata()
1764 : {
1765 16 : if (bHasLoadedMetadata)
1766 1 : return;
1767 :
1768 15 : bHasLoadedMetadata = TRUE;
1769 :
1770 15 : CPLString osURI("/");
1771 15 : osURI += osEscapedName;
1772 15 : osURI += "/_design/ogr_metadata";
1773 15 : json_object* poAnswerObj = poDS->GET(osURI);
1774 15 : if (poAnswerObj == NULL)
1775 : return;
1776 :
1777 15 : if ( !json_object_is_type(poAnswerObj, json_type_object) )
1778 : {
1779 0 : CPLError(CE_Failure, CPLE_AppDefined, "LoadMetadata() failed");
1780 0 : json_object_put(poAnswerObj);
1781 : return;
1782 : }
1783 :
1784 15 : json_object* poRev = json_object_object_get(poAnswerObj, "_rev");
1785 15 : const char* pszRev = json_object_get_string(poRev);
1786 15 : if (pszRev)
1787 8 : osMetadataRev = pszRev;
1788 :
1789 15 : json_object* poError = json_object_object_get(poAnswerObj, "error");
1790 15 : const char* pszError = json_object_get_string(poError);
1791 15 : if (pszError && strcmp(pszError, "not_found") == 0)
1792 : {
1793 7 : json_object_put(poAnswerObj);
1794 : return;
1795 : }
1796 :
1797 8 : if (poDS->IsError(poAnswerObj, "LoadMetadata() failed"))
1798 : {
1799 0 : json_object_put(poAnswerObj);
1800 : return;
1801 : }
1802 :
1803 8 : json_object* poJsonSRS = json_object_object_get(poAnswerObj, "srs");
1804 8 : const char* pszSRS = json_object_get_string(poJsonSRS);
1805 8 : if (pszSRS != NULL)
1806 : {
1807 8 : poSRS = new OGRSpatialReference();
1808 16 : if (poSRS->importFromWkt((char**)&pszSRS) != OGRERR_NONE)
1809 : {
1810 0 : delete poSRS;
1811 0 : poSRS = NULL;
1812 : }
1813 : }
1814 :
1815 8 : json_object* poGeomType = json_object_object_get(poAnswerObj, "geomtype");
1816 8 : const char* pszGeomType = json_object_get_string(poGeomType);
1817 :
1818 8 : if (pszGeomType)
1819 : {
1820 8 : if (EQUAL(pszGeomType, "NONE"))
1821 : {
1822 0 : eGeomType = wkbNone;
1823 0 : bExtentValid = TRUE;
1824 : }
1825 : else
1826 : {
1827 8 : eGeomType = OGRFromOGCGeomType(pszGeomType);
1828 :
1829 8 : json_object* poIs25D = json_object_object_get(poAnswerObj, "is_25D");
1830 8 : if (poIs25D && json_object_get_boolean(poIs25D))
1831 0 : eGeomType = (OGRwkbGeometryType) (eGeomType | wkb25DBit);
1832 :
1833 8 : json_object* poExtent = json_object_object_get(poAnswerObj, "extent");
1834 8 : if (poExtent && json_object_get_type(poExtent) == json_type_object)
1835 : {
1836 : json_object* poUpdateSeq =
1837 8 : json_object_object_get(poExtent, "validity_update_seq");
1838 8 : if (poUpdateSeq && json_object_get_type(poUpdateSeq) == json_type_int)
1839 : {
1840 8 : int nValidityUpdateSeq = json_object_get_int(poUpdateSeq);
1841 8 : if (nValidityUpdateSeq <= 0)
1842 : {
1843 8 : bAlwaysValid = TRUE;
1844 : }
1845 : else
1846 : {
1847 0 : if (nUpdateSeq < 0)
1848 0 : nUpdateSeq = FetchUpdateSeq();
1849 0 : if (nUpdateSeq != nValidityUpdateSeq)
1850 : {
1851 : CPLDebug("CouchDB",
1852 : "_design/ogr_metadata.extent.validity_update_seq "
1853 0 : "doesn't match database update_seq --> ignoring stored extent");
1854 0 : poUpdateSeq = NULL;
1855 : }
1856 : }
1857 : }
1858 : else
1859 0 : poUpdateSeq = NULL;
1860 :
1861 8 : json_object* poBbox = json_object_object_get(poExtent, "bbox");
1862 8 : if (poUpdateSeq && poBbox &&
1863 : json_object_get_type(poBbox) == json_type_array &&
1864 : json_object_array_length(poBbox) == 4 &&
1865 : OGRCouchDBIsNumericObject(json_object_array_get_idx(poBbox, 0)) &&
1866 : OGRCouchDBIsNumericObject(json_object_array_get_idx(poBbox, 1)) &&
1867 : OGRCouchDBIsNumericObject(json_object_array_get_idx(poBbox, 2)) &&
1868 : OGRCouchDBIsNumericObject(json_object_array_get_idx(poBbox, 3)))
1869 : {
1870 8 : dfMinX = json_object_get_double(json_object_array_get_idx(poBbox, 0));
1871 8 : dfMinY = json_object_get_double(json_object_array_get_idx(poBbox, 1));
1872 8 : dfMaxX = json_object_get_double(json_object_array_get_idx(poBbox, 2));
1873 8 : dfMaxY = json_object_get_double(json_object_array_get_idx(poBbox, 3));
1874 8 : bExtentValid = bExtentSet = TRUE;
1875 : }
1876 : }
1877 : }
1878 : }
1879 :
1880 8 : json_object* poGeoJSON = json_object_object_get(poAnswerObj, "geojson_documents");
1881 8 : if (poGeoJSON && json_object_is_type(poGeoJSON, json_type_boolean))
1882 0 : bGeoJSONDocument = json_object_get_boolean(poGeoJSON);
1883 :
1884 8 : json_object* poFields = json_object_object_get(poAnswerObj, "fields");
1885 8 : if (poFields && json_object_is_type(poFields, json_type_array))
1886 : {
1887 8 : poFeatureDefn = new OGRFeatureDefn( osName );
1888 8 : poFeatureDefn->Reference();
1889 :
1890 8 : poFeatureDefn->SetGeomType(eGeomType);
1891 :
1892 8 : OGRFieldDefn oFieldId("_id", OFTString);
1893 8 : poFeatureDefn->AddFieldDefn(&oFieldId);
1894 :
1895 8 : OGRFieldDefn oFieldRev("_rev", OFTString);
1896 8 : poFeatureDefn->AddFieldDefn(&oFieldRev);
1897 :
1898 8 : int nFields = json_object_array_length(poFields);
1899 32 : for(int i=0;i<nFields;i++)
1900 : {
1901 24 : json_object* poField = json_object_array_get_idx(poFields, i);
1902 24 : if (poField && json_object_is_type(poField, json_type_object))
1903 : {
1904 24 : json_object* poName = json_object_object_get(poField, "name");
1905 24 : const char* pszName = json_object_get_string(poName);
1906 24 : if (pszName)
1907 : {
1908 24 : json_object* poType = json_object_object_get(poField, "type");
1909 24 : const char* pszType = json_object_get_string(poType);
1910 24 : OGRFieldType eType = OFTString;
1911 24 : if (pszType)
1912 : {
1913 24 : if (strcmp(pszType, "integer") == 0)
1914 0 : eType = OFTInteger;
1915 24 : else if (strcmp(pszType, "integerlist") == 0)
1916 0 : eType = OFTIntegerList;
1917 24 : else if (strcmp(pszType, "real") == 0)
1918 16 : eType = OFTReal;
1919 8 : else if (strcmp(pszType, "reallist") == 0)
1920 0 : eType = OFTRealList;
1921 8 : else if (strcmp(pszType, "string") == 0)
1922 8 : eType = OFTString;
1923 0 : else if (strcmp(pszType, "stringlist") == 0)
1924 0 : eType = OFTStringList;
1925 : }
1926 :
1927 24 : OGRFieldDefn oField(pszName, eType);
1928 24 : poFeatureDefn->AddFieldDefn(&oField);
1929 : }
1930 : }
1931 8 : }
1932 : }
1933 :
1934 8 : json_object_put(poAnswerObj);
1935 :
1936 0 : return;
1937 : }
1938 :
1939 : /************************************************************************/
1940 : /* WriteMetadata() */
1941 : /************************************************************************/
1942 :
1943 0 : void OGRCouchDBTableLayer::WriteMetadata()
1944 : {
1945 0 : GetLayerDefn();
1946 :
1947 0 : CPLString osURI;
1948 0 : osURI = "/";
1949 0 : osURI += osEscapedName;
1950 0 : osURI += "/_design/ogr_metadata";
1951 :
1952 0 : json_object* poDoc = json_object_new_object();
1953 :
1954 0 : if (osMetadataRev.size() > 0)
1955 : {
1956 : json_object_object_add(poDoc, "_rev",
1957 0 : json_object_new_string(osMetadataRev));
1958 : }
1959 :
1960 0 : if (poSRS)
1961 : {
1962 0 : char* pszWKT = NULL;
1963 0 : poSRS->exportToWkt(&pszWKT);
1964 0 : if (pszWKT)
1965 : {
1966 : json_object_object_add(poDoc, "srs",
1967 0 : json_object_new_string(pszWKT));
1968 0 : CPLFree(pszWKT);
1969 : }
1970 : }
1971 :
1972 0 : if (eGeomType != wkbNone)
1973 : {
1974 : json_object_object_add(poDoc, "geomtype",
1975 0 : json_object_new_string(OGRToOGCGeomType(eGeomType)));
1976 0 : if (poFeatureDefn->GetGeomType() & wkb25DBit)
1977 : {
1978 : json_object_object_add(poDoc, "is_25D",
1979 0 : json_object_new_boolean(TRUE));
1980 : }
1981 :
1982 0 : if (bExtentValid && bExtentSet && nUpdateSeq >= 0)
1983 : {
1984 0 : json_object* poExtent = json_object_new_object();
1985 0 : json_object_object_add(poDoc, "extent", poExtent);
1986 :
1987 : json_object_object_add(poExtent, "validity_update_seq",
1988 0 : json_object_new_int((bAlwaysValid) ? -1 : nUpdateSeq + 1));
1989 :
1990 0 : json_object* poBbox = json_object_new_array();
1991 0 : json_object_object_add(poExtent, "bbox", poBbox);
1992 0 : json_object_array_add(poBbox, json_object_new_double_with_precision(dfMinX, nCoordPrecision));
1993 0 : json_object_array_add(poBbox, json_object_new_double_with_precision(dfMinY, nCoordPrecision));
1994 0 : json_object_array_add(poBbox, json_object_new_double_with_precision(dfMaxX, nCoordPrecision));
1995 0 : json_object_array_add(poBbox, json_object_new_double_with_precision(dfMaxY, nCoordPrecision));
1996 : }
1997 : }
1998 : else
1999 : {
2000 : json_object_object_add(poDoc, "geomtype",
2001 0 : json_object_new_string("NONE"));
2002 : }
2003 :
2004 : json_object_object_add(poDoc, "geojson_documents",
2005 0 : json_object_new_boolean(bGeoJSONDocument));
2006 :
2007 0 : json_object* poFields = json_object_new_array();
2008 0 : json_object_object_add(poDoc, "fields", poFields);
2009 :
2010 :
2011 0 : for(int i=FIRST_FIELD;i<poFeatureDefn->GetFieldCount();i++)
2012 : {
2013 0 : json_object* poField = json_object_new_object();
2014 0 : json_object_array_add(poFields, poField);
2015 :
2016 : json_object_object_add(poField, "name",
2017 0 : json_object_new_string(poFeatureDefn->GetFieldDefn(i)->GetNameRef()));
2018 :
2019 0 : const char* pszType = NULL;
2020 0 : switch (poFeatureDefn->GetFieldDefn(i)->GetType())
2021 : {
2022 0 : case OFTInteger: pszType = "integer"; break;
2023 0 : case OFTReal: pszType = "real"; break;
2024 0 : case OFTString: pszType = "string"; break;
2025 0 : case OFTIntegerList: pszType = "integerlist"; break;
2026 0 : case OFTRealList: pszType = "reallist"; break;
2027 0 : case OFTStringList: pszType = "stringlist"; break;
2028 0 : default: pszType = "string"; break;
2029 : }
2030 :
2031 : json_object_object_add(poField, "type",
2032 0 : json_object_new_string(pszType));
2033 : }
2034 :
2035 : json_object* poAnswerObj = poDS->PUT(osURI,
2036 0 : json_object_to_json_string(poDoc));
2037 :
2038 0 : json_object_put(poDoc);
2039 :
2040 0 : if (poDS->IsOK(poAnswerObj, "Metadata creation failed"))
2041 : {
2042 0 : nUpdateSeq++;
2043 :
2044 0 : json_object* poRev = json_object_object_get(poAnswerObj, "_rev");
2045 0 : const char* pszRev = json_object_get_string(poRev);
2046 0 : if (pszRev)
2047 0 : osMetadataRev = pszRev;
2048 : }
2049 :
2050 0 : json_object_put(poAnswerObj);
2051 0 : }
2052 :
2053 : /************************************************************************/
2054 : /* GetExtent() */
2055 : /************************************************************************/
2056 :
2057 2 : OGRErr OGRCouchDBTableLayer::GetExtent(OGREnvelope *psExtent, int bForce)
2058 : {
2059 2 : LoadMetadata();
2060 :
2061 2 : if (!bExtentValid)
2062 1 : return OGRCouchDBLayer::GetExtent(psExtent, bForce);
2063 :
2064 1 : psExtent->MinX = 0.0;
2065 1 : psExtent->MaxX = 0.0;
2066 1 : psExtent->MinY = 0.0;
2067 1 : psExtent->MaxY = 0.0;
2068 :
2069 1 : if (!bExtentSet)
2070 0 : return OGRERR_FAILURE;
2071 :
2072 1 : psExtent->MinX = dfMinX;
2073 1 : psExtent->MaxX = dfMaxX;
2074 1 : psExtent->MinY = dfMinY;
2075 1 : psExtent->MaxY = dfMaxY;
2076 :
2077 1 : return OGRERR_NONE;
2078 : }
2079 :
2080 : /************************************************************************/
2081 : /* FetchUpdateSeq() */
2082 : /************************************************************************/
2083 :
2084 0 : int OGRCouchDBTableLayer::FetchUpdateSeq()
2085 : {
2086 0 : if (nUpdateSeq >= 0)
2087 0 : return nUpdateSeq;
2088 :
2089 0 : CPLString osURI("/");
2090 0 : osURI += osEscapedName;
2091 0 : osURI += "/";
2092 :
2093 0 : json_object* poAnswerObj = poDS->GET(osURI);
2094 0 : if (poAnswerObj != NULL &&
2095 : json_object_is_type(poAnswerObj, json_type_object) &&
2096 : json_object_object_get(poAnswerObj, "update_seq") != NULL)
2097 : {
2098 : nUpdateSeq = json_object_get_int(json_object_object_get(poAnswerObj,
2099 0 : "update_seq"));
2100 : }
2101 : else
2102 : {
2103 0 : poDS->IsError(poAnswerObj, "FetchUpdateSeq() failed");
2104 : }
2105 :
2106 0 : json_object_put(poAnswerObj);
2107 :
2108 0 : return nUpdateSeq;
2109 : }
|