LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/couchdb - ogrcouchdbtablelayer.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 1050 438 41.7 %
Date: 2012-12-26 Functions: 41 23 56.1 %

       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                 : }

Generated by: LCOV version 1.7