1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Implements Open FileGDB OGR driver.
5  * Author:   Even Rouault, <even dot rouault at spatialys.com>
6  *
7  ******************************************************************************
8  * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include "cpl_port.h"
30 #include "ogr_openfilegdb.h"
31 
32 #include <cmath>
33 #include <cstddef>
34 #include <cstdio>
35 #include <cstdlib>
36 #include <cstring>
37 #include <algorithm>
38 #include <string>
39 
40 #include "cpl_conv.h"
41 #include "cpl_error.h"
42 #include "cpl_minixml.h"
43 #include "cpl_quad_tree.h"
44 #include "cpl_string.h"
45 #include "ogr_api.h"
46 #include "ogr_core.h"
47 #include "ogr_feature.h"
48 #include "ogr_geometry.h"
49 #include "ogr_spatialref.h"
50 #include "ogr_srs_api.h"
51 #include "ogrsf_frmts.h"
52 #include "filegdbtable.h"
53 #include "ogr_swq.h"
54 
55 CPL_CVSID("$Id: ogropenfilegdblayer.cpp 6b84484fd22d27a4611ab64da86ba221156293f8 2021-08-25 10:30:20 +0200 Even Rouault $")
56 
57 /************************************************************************/
58 /*                      OGROpenFileGDBGeomFieldDefn                     */
59 /************************************************************************/
60 class OGROpenFileGDBGeomFieldDefn: public OGRGeomFieldDefn
61 {
62         OGROpenFileGDBLayer* m_poLayer;
63 
64     public:
OGROpenFileGDBGeomFieldDefn(OGROpenFileGDBLayer * poLayer,const char * pszNameIn,OGRwkbGeometryType eGeomTypeIn)65         OGROpenFileGDBGeomFieldDefn(OGROpenFileGDBLayer* poLayer,
66                                     const char *pszNameIn,
67                                     OGRwkbGeometryType eGeomTypeIn) :
68             OGRGeomFieldDefn(pszNameIn, eGeomTypeIn),
69             m_poLayer(poLayer)
70         {}
71 
~OGROpenFileGDBGeomFieldDefn()72         ~OGROpenFileGDBGeomFieldDefn() {}
73 
UnsetLayer()74         void UnsetLayer() { m_poLayer = nullptr; }
75 
GetSpatialRef() const76         virtual OGRSpatialReference* GetSpatialRef() const override
77         {
78             if( poSRS )
79                 return poSRS;
80             if( m_poLayer != nullptr )
81                 (void) m_poLayer->BuildLayerDefinition();
82             return poSRS;
83         }
84 };
85 
86 /************************************************************************/
87 /*                      OGROpenFileGDBFeatureDefn                       */
88 /************************************************************************/
89 class OGROpenFileGDBFeatureDefn: public OGRFeatureDefn
90 {
91         OGROpenFileGDBLayer* m_poLayer;
92         mutable int m_bHasBuildFieldDefn;
93 
LazyGeomInit() const94         void LazyGeomInit() const
95         {
96             /* FileGDB v9 case */
97             if( !m_bHasBuildFieldDefn &&
98                 m_poLayer != nullptr && m_poLayer->m_eGeomType != wkbNone &&
99                 m_poLayer->m_osDefinition.empty() )
100             {
101                 m_bHasBuildFieldDefn = TRUE;
102                 (void) m_poLayer->BuildLayerDefinition();
103             }
104         }
105 
106     public:
OGROpenFileGDBFeatureDefn(OGROpenFileGDBLayer * poLayer,const char * pszName)107         OGROpenFileGDBFeatureDefn( OGROpenFileGDBLayer* poLayer,
108                                    const char * pszName ) :
109                         OGRFeatureDefn(pszName), m_poLayer(poLayer)
110         {
111             m_bHasBuildFieldDefn = FALSE;
112         }
113 
~OGROpenFileGDBFeatureDefn()114         ~OGROpenFileGDBFeatureDefn() {}
115 
UnsetLayer()116         void UnsetLayer()
117         {
118             if( nGeomFieldCount )
119                 reinterpret_cast<OGROpenFileGDBGeomFieldDefn *>(
120                     papoGeomFieldDefn[0])->UnsetLayer();
121             m_poLayer = nullptr;
122         }
123 
GetFieldCount() const124         virtual int GetFieldCount() const override
125         {
126             if( nFieldCount )
127                 return nFieldCount;
128             if( !m_bHasBuildFieldDefn && m_poLayer != nullptr )
129             {
130                 m_bHasBuildFieldDefn = TRUE;
131                 (void) m_poLayer->BuildLayerDefinition();
132             }
133             return nFieldCount;
134         }
135 
GetGeomFieldCount() const136         virtual int GetGeomFieldCount() const override
137         {
138             LazyGeomInit();
139             return nGeomFieldCount;
140         }
141 
GetGeomFieldDefn(int i)142         virtual OGRGeomFieldDefn* GetGeomFieldDefn( int i ) override
143         {
144             LazyGeomInit();
145             return OGRFeatureDefn::GetGeomFieldDefn(i);
146         }
147 
GetGeomFieldDefn(int i) const148         virtual const OGRGeomFieldDefn* GetGeomFieldDefn( int i ) const override
149         {
150             LazyGeomInit();
151             return OGRFeatureDefn::GetGeomFieldDefn(i);
152         }
153 };
154 
155 /************************************************************************/
156 /*                      OGROpenFileGDBLayer()                           */
157 /************************************************************************/
158 
OGROpenFileGDBLayer(const char * pszGDBFilename,const char * pszName,const std::string & osDefinition,const std::string & osDocumentation,const char *,OGRwkbGeometryType eGeomType)159 OGROpenFileGDBLayer::OGROpenFileGDBLayer( const char* pszGDBFilename,
160                                           const char* pszName,
161                                           const std::string& osDefinition,
162                                           const std::string& osDocumentation,
163                                           const char* /* pszGeomName */,
164                                           OGRwkbGeometryType eGeomType ) :
165     m_osGDBFilename(pszGDBFilename),
166     m_osName(pszName),
167     m_poLyrTable(nullptr),
168     m_poFeatureDefn(nullptr),
169     m_iGeomFieldIdx(-1),
170     m_iCurFeat(0),
171     m_osDefinition(osDefinition),
172     m_osDocumentation(osDocumentation),
173     m_eGeomType(wkbNone),
174     m_bValidLayerDefn(-1),
175     m_bEOF(FALSE),
176     m_poGeomConverter(nullptr),
177     m_iFieldToReadAsBinary(-1),
178     m_poAttributeIterator(nullptr),
179     m_bIteratorSufficientToEvaluateFilter(FALSE),
180     m_poIterMinMax(nullptr),
181     m_eSpatialIndexState(SPI_IN_BUILDING),
182     m_pQuadTree(nullptr),
183     m_pahFilteredFeatures(nullptr),
184     m_nFilteredFeatureCount(-1)
185 {
186     // TODO(rouault): What error on compiler versions?  r33032 does not say.
187 
188     // We cannot initialize m_poFeatureDefn in above list. MSVC doesn't like
189     // this to be used in initialization list.
190     m_poFeatureDefn = new OGROpenFileGDBFeatureDefn(this, pszName);
191     SetDescription( m_poFeatureDefn->GetName() );
192     m_poFeatureDefn->SetGeomType(wkbNone);
193     m_poFeatureDefn->Reference();
194 
195     m_eGeomType = eGeomType;
196 
197     if( !m_osDefinition.empty() )
198     {
199         BuildGeometryColumnGDBv10();
200     }
201 }
202 
203 /***********************************************************************/
204 /*                      ~OGROpenFileGDBLayer()                         */
205 /***********************************************************************/
206 
~OGROpenFileGDBLayer()207 OGROpenFileGDBLayer::~OGROpenFileGDBLayer()
208 {
209     delete m_poLyrTable;
210     if( m_poFeatureDefn )
211     {
212         m_poFeatureDefn->UnsetLayer();
213         m_poFeatureDefn->Release();
214     }
215     delete m_poAttributeIterator;
216     delete m_poIterMinMax;
217     delete m_poGeomConverter;
218     delete m_poSpatialIndexIterator;
219     delete m_poCombinedIterator;
220     if( m_pQuadTree != nullptr )
221         CPLQuadTreeDestroy(m_pQuadTree);
222     CPLFree(m_pahFilteredFeatures);
223 }
224 
225 /************************************************************************/
226 /*                           BuildSRS()                                 */
227 /************************************************************************/
228 
BuildSRS(const char * pszWKT)229 static OGRSpatialReference* BuildSRS(const char* pszWKT)
230 {
231     OGRSpatialReference* poSRS = new OGRSpatialReference();
232     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
233     if( poSRS->importFromWkt(pszWKT) != OGRERR_NONE )
234     {
235         delete poSRS;
236         poSRS = nullptr;
237     }
238     if( poSRS != nullptr )
239     {
240         if( CPLTestBool(CPLGetConfigOption("USE_OSR_FIND_MATCHES", "YES")) )
241         {
242             int nEntries = 0;
243             int* panConfidence = nullptr;
244             OGRSpatialReferenceH* pahSRS =
245                 poSRS->FindMatches(nullptr, &nEntries, &panConfidence);
246             if( nEntries == 1 && panConfidence[0] == 100 )
247             {
248                 poSRS->Release();
249                 poSRS = reinterpret_cast<OGRSpatialReference*>(pahSRS[0]);
250                 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
251                 CPLFree(pahSRS);
252             }
253             else
254             {
255                 OSRFreeSRSArray(pahSRS);
256             }
257             CPLFree(panConfidence);
258         }
259         else
260         {
261             poSRS->AutoIdentifyEPSG();
262         }
263     }
264     return poSRS;
265 }
266 
267 /************************************************************************/
268 /*                     BuildGeometryColumnGDBv10()                      */
269 /************************************************************************/
270 
BuildGeometryColumnGDBv10()271 int OGROpenFileGDBLayer::BuildGeometryColumnGDBv10()
272 {
273     CPLXMLNode* psTree = CPLParseXMLString(m_osDefinition.c_str());
274     if( psTree == nullptr )
275     {
276         m_osDefinition = "";
277         return FALSE;
278     }
279 
280     CPLStripXMLNamespace( psTree, nullptr, TRUE );
281     /* CPLSerializeXMLTreeToFile( psTree, "/dev/stderr" ); */
282     CPLXMLNode* psInfo = CPLSearchXMLNode( psTree, "=DEFeatureClassInfo" );
283     if( psInfo == nullptr )
284         psInfo = CPLSearchXMLNode( psTree, "=DETableInfo" );
285     if( psInfo == nullptr )
286     {
287         m_osDefinition = "";
288         CPLDestroyXMLNode(psTree);
289         return FALSE;
290     }
291 
292     m_bTimeInUTC = CPLTestBool(CPLGetXMLValue(psInfo, "IsTimeInUTC", "false"));
293 
294     /* We cannot trust the XML definition to build the field definitions. */
295     /* It sometimes misses a few fields ! */
296 
297     const bool bHasZ = CPLTestBool(CPLGetXMLValue( psInfo, "HasZ", "NO" ));
298     const bool bHasM = CPLTestBool(CPLGetXMLValue( psInfo, "HasM", "NO" ));
299     const char* pszShapeType = CPLGetXMLValue(psInfo, "ShapeType", nullptr);
300     const char* pszShapeFieldName =
301         CPLGetXMLValue(psInfo, "ShapeFieldName", nullptr);
302     if( pszShapeType != nullptr && pszShapeFieldName != nullptr )
303     {
304         m_eGeomType =
305             FileGDBOGRGeometryConverter::GetGeometryTypeFromESRI(pszShapeType);
306 
307         if( EQUAL(pszShapeType, "esriGeometryMultiPatch") )
308         {
309             if( m_poLyrTable == nullptr )
310             {
311                 m_poLyrTable = new FileGDBTable();
312                 if( !(m_poLyrTable->Open(m_osGDBFilename, GetDescription())) )
313                 {
314                     delete m_poLyrTable;
315                     m_poLyrTable = nullptr;
316                     m_bValidLayerDefn = FALSE;
317                 }
318             }
319             if( m_poLyrTable != nullptr )
320             {
321                 m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
322                 if( m_iGeomFieldIdx >= 0 )
323                 {
324                     FileGDBGeomField* poGDBGeomField =
325                         reinterpret_cast<FileGDBGeomField *>(
326                             m_poLyrTable->GetField(m_iGeomFieldIdx));
327                     if( m_poGeomConverter == nullptr )
328                     {
329                         m_poGeomConverter =
330                             FileGDBOGRGeometryConverter::BuildConverter(poGDBGeomField);
331                     }
332                     TryToDetectMultiPatchKind();
333                 }
334             }
335         }
336 
337         if( bHasZ )
338             m_eGeomType = wkbSetZ( m_eGeomType );
339         if( bHasM )
340             m_eGeomType = wkbSetM( m_eGeomType );
341 
342         const char* pszWKT =
343             CPLGetXMLValue( psInfo, "SpatialReference.WKT", nullptr );
344         const int nWKID =
345             atoi(CPLGetXMLValue( psInfo, "SpatialReference.WKID", "0" ));
346         // The concept of LatestWKID is explained in
347         // http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r3000000n1000000
348         int nLatestWKID = atoi(
349             CPLGetXMLValue( psInfo, "SpatialReference.LatestWKID", "0" ));
350 
351         OGROpenFileGDBGeomFieldDefn* poGeomFieldDefn =
352             new OGROpenFileGDBGeomFieldDefn(nullptr, pszShapeFieldName, m_eGeomType);
353 
354         CPLXMLNode* psGPFieldInfoExs = CPLGetXMLNode(psInfo, "GPFieldInfoExs");
355         if( psGPFieldInfoExs )
356         {
357             for(CPLXMLNode* psChild = psGPFieldInfoExs->psChild;
358                             psChild != nullptr;
359                             psChild = psChild->psNext )
360             {
361                 if( psChild->eType != CXT_Element )
362                     continue;
363                 if( EQUAL( psChild->pszValue, "GPFieldInfoEx" ) &&
364                     EQUAL( CPLGetXMLValue(psChild, "Name", "" ),
365                            pszShapeFieldName ) )
366                 {
367                     poGeomFieldDefn->SetNullable(
368                         CPLTestBool(CPLGetXMLValue(
369                             psChild, "IsNullable", "TRUE" )) );
370                     break;
371                 }
372             }
373         }
374 
375         OGRSpatialReference* poSRS = nullptr;
376         if( nWKID > 0 || nLatestWKID > 0 )
377         {
378             int bSuccess = FALSE;
379             poSRS = new OGRSpatialReference();
380             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
381             CPLPushErrorHandler(CPLQuietErrorHandler);
382             // Try first with nLatestWKID as there is a higher chance it is a
383             // EPSG code and not an ESRI one.
384             if( nLatestWKID > 0 )
385             {
386                 if( poSRS->importFromEPSG(nLatestWKID) == OGRERR_NONE )
387                 {
388                     bSuccess = TRUE;
389                 }
390                 else
391                 {
392                     CPLDebug( "OpenFileGDB", "Cannot import SRID %d",
393                               nLatestWKID);
394                 }
395             }
396             if( !bSuccess && nWKID > 0 )
397             {
398                 if( poSRS->importFromEPSG(nWKID) == OGRERR_NONE )
399                 {
400                     bSuccess = TRUE;
401                 }
402                 else
403                 {
404                     CPLDebug("OpenFileGDB", "Cannot import SRID %d", nWKID);
405                 }
406             }
407             if( !bSuccess )
408             {
409                 delete poSRS;
410                 poSRS = nullptr;
411             }
412             CPLPopErrorHandler();
413             CPLErrorReset();
414         }
415         if( poSRS == nullptr && pszWKT != nullptr && pszWKT[0] != '{' )
416         {
417             poSRS = BuildSRS(pszWKT);
418         }
419         if( poSRS != nullptr )
420         {
421             poGeomFieldDefn->SetSpatialRef(poSRS);
422             poSRS->Dereference();
423         }
424         m_poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE);
425     }
426     else
427     {
428         m_eGeomType = wkbNone;
429     }
430     CPLDestroyXMLNode(psTree);
431 
432     return TRUE;
433 }
434 
435 /************************************************************************/
436 /*                   TryToDetectMultiPatchKind()                        */
437 /************************************************************************/
438 
439 // If the first and last feature have the same geometry type, then use
440 // it for the whole layer.
TryToDetectMultiPatchKind()441 void OGROpenFileGDBLayer::TryToDetectMultiPatchKind()
442 {
443     CPLAssert( m_poLyrTable != nullptr );
444     CPLAssert( m_iGeomFieldIdx >= 0 );
445 
446     if( m_poLyrTable->GetTotalRecordCount() == 0 )
447         return;
448     int nFirstIdx = m_poLyrTable->GetAndSelectNextNonEmptyRow(0);
449     if( nFirstIdx < 0 )
450         return;
451 
452     const OGRField* psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
453     if( psField == nullptr )
454         return;
455     OGRGeometry* poGeom = m_poGeomConverter->GetAsGeometry(psField);
456     if( poGeom == nullptr )
457         return;
458     const OGRwkbGeometryType eType = poGeom->getGeometryType();
459     delete poGeom;
460 
461     int nLastIdx = m_poLyrTable->GetTotalRecordCount()-1;
462     const GUInt32 nErrorCount = CPLGetErrorCounter();
463     while( nLastIdx > nFirstIdx &&
464            m_poLyrTable->GetOffsetInTableForRow(nLastIdx) == 0 &&
465            nErrorCount == CPLGetErrorCounter() )
466     {
467         nLastIdx --;
468     }
469     if( nLastIdx > nFirstIdx && m_poLyrTable->SelectRow(nLastIdx) )
470     {
471         psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
472         if( psField == nullptr )
473         {
474             m_eGeomType = eType;
475             return;
476         }
477         poGeom = m_poGeomConverter->GetAsGeometry(psField);
478         if( poGeom == nullptr )
479         {
480             m_eGeomType = eType;
481             return;
482         }
483         if( eType == poGeom->getGeometryType() )
484             m_eGeomType = eType;
485         delete poGeom;
486     }
487 }
488 
489 /************************************************************************/
490 /*                      BuildLayerDefinition()                          */
491 /************************************************************************/
492 
BuildLayerDefinition()493 int OGROpenFileGDBLayer::BuildLayerDefinition()
494 {
495     if( m_bValidLayerDefn >= 0 )
496         return m_bValidLayerDefn;
497 
498     if( m_poLyrTable == nullptr )
499     {
500         m_poLyrTable = new FileGDBTable();
501         if( !(m_poLyrTable->Open(m_osGDBFilename, GetDescription())) )
502         {
503             delete m_poLyrTable;
504             m_poLyrTable = nullptr;
505             m_bValidLayerDefn = FALSE;
506             return FALSE;
507         }
508     }
509 
510     m_bValidLayerDefn = TRUE;
511 
512     m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
513     if( m_iGeomFieldIdx >= 0 )
514     {
515         FileGDBGeomField* poGDBGeomField =
516             reinterpret_cast<FileGDBGeomField *>(
517                 m_poLyrTable->GetField(m_iGeomFieldIdx));
518         if( m_poGeomConverter == nullptr )
519         {
520             m_poGeomConverter =
521             FileGDBOGRGeometryConverter::BuildConverter(poGDBGeomField);
522         }
523 
524         if( !(m_poLyrTable->HasSpatialIndex() &&
525               CPLTestBool(
526                 CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX", "YES"))) &&
527             CPLTestBool(
528                 CPLGetConfigOption("OPENFILEGDB_IN_MEMORY_SPI", "YES")) )
529         {
530             CPLRectObj sGlobalBounds;
531             sGlobalBounds.minx = poGDBGeomField->GetXMin();
532             sGlobalBounds.miny = poGDBGeomField->GetYMin();
533             sGlobalBounds.maxx = poGDBGeomField->GetXMax();
534             sGlobalBounds.maxy = poGDBGeomField->GetYMax();
535             m_pQuadTree = CPLQuadTreeCreate(&sGlobalBounds,
536                                             nullptr);
537             CPLQuadTreeSetMaxDepth(m_pQuadTree,
538                 CPLQuadTreeGetAdvisedMaxDepth(
539                     m_poLyrTable->GetValidRecordCount()));
540         }
541         else
542         {
543             m_eSpatialIndexState = SPI_INVALID;
544         }
545     }
546 
547     if( m_iGeomFieldIdx >= 0 &&
548         (m_osDefinition.empty() ||
549          m_poFeatureDefn->OGRFeatureDefn::GetGeomFieldCount() == 0) )
550     {
551         /* FileGDB v9 case */
552         FileGDBGeomField* poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
553             m_poLyrTable->GetField(m_iGeomFieldIdx) );
554         const char* pszName = poGDBGeomField->GetName().c_str();
555         const FileGDBTableGeometryType eGDBGeomType =
556             m_poLyrTable->GetGeometryType();
557 
558         OGRwkbGeometryType eGeomType = wkbUnknown;
559         switch( eGDBGeomType )
560         {
561             case FGTGT_NONE: /* doesn't make sense ! */ break;
562             case FGTGT_POINT: eGeomType = wkbPoint; break;
563             case FGTGT_MULTIPOINT: eGeomType = wkbMultiPoint; break;
564             case FGTGT_LINE: eGeomType = wkbMultiLineString; break;
565             case FGTGT_POLYGON: eGeomType = wkbMultiPolygon; break;
566             case FGTGT_MULTIPATCH: eGeomType = wkbUnknown; break;
567         }
568 
569         if( m_eGeomType != wkbUnknown && wkbFlatten(eGeomType) != wkbFlatten(m_eGeomType) )
570         {
571             CPLError(CE_Warning, CPLE_AppDefined,
572                      "Inconsistency for layer geometry type");
573         }
574 
575         m_eGeomType = eGeomType;
576 
577         if( eGDBGeomType == FGTGT_MULTIPATCH )
578         {
579             TryToDetectMultiPatchKind();
580         }
581 
582         if( m_poLyrTable->GetGeomTypeHasZ() )
583             m_eGeomType = wkbSetZ(m_eGeomType);
584 
585         if( m_poLyrTable->GetGeomTypeHasM() )
586             m_eGeomType = wkbSetM(m_eGeomType);
587 
588         OGROpenFileGDBGeomFieldDefn* poGeomFieldDefn =
589                 new OGROpenFileGDBGeomFieldDefn(nullptr, pszName, m_eGeomType);
590         poGeomFieldDefn->SetNullable(poGDBGeomField->IsNullable());
591 
592         m_poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE);
593 
594         OGRSpatialReference* poSRS = nullptr;
595         if( !poGDBGeomField->GetWKT().empty() &&
596             poGDBGeomField->GetWKT()[0] != '{' )
597         {
598             poSRS = BuildSRS( poGDBGeomField->GetWKT().c_str() );
599         }
600         if( poSRS != nullptr )
601         {
602             poGeomFieldDefn->SetSpatialRef(poSRS);
603             poSRS->Dereference();
604         }
605     }
606     else if( m_osDefinition.empty() && m_iGeomFieldIdx < 0 )
607     {
608         m_eGeomType = wkbNone;
609     }
610 
611     CPLXMLTreeCloser oTree(nullptr);
612     const CPLXMLNode* psGPFieldInfoExs = nullptr;
613 
614     if( !m_osDefinition.empty() )
615     {
616         oTree.reset(CPLParseXMLString(m_osDefinition.c_str()));
617         if( oTree != nullptr )
618         {
619             CPLStripXMLNamespace( oTree.get(), nullptr, TRUE );
620             CPLXMLNode* psInfo =
621                 CPLSearchXMLNode( oTree.get(), "=DEFeatureClassInfo" );
622             if( psInfo == nullptr )
623                 psInfo = CPLSearchXMLNode( oTree.get(), "=DETableInfo" );
624             if( psInfo != nullptr )
625                 psGPFieldInfoExs =
626                     CPLGetXMLNode(psInfo, "GPFieldInfoExs");
627         }
628     }
629 
630     for(int i=0;i<m_poLyrTable->GetFieldCount();i++)
631     {
632         if( i == m_iGeomFieldIdx )
633             continue;
634 
635         const FileGDBField* poGDBField = m_poLyrTable->GetField(i);
636         OGRFieldType eType = OFTString;
637         OGRFieldSubType eSubType = OFSTNone;
638         int nWidth = poGDBField->GetMaxWidth();
639         switch( poGDBField->GetType() )
640         {
641             case FGFT_INT16:
642                 eType = OFTInteger;
643                 eSubType = OFSTInt16;
644                 break;
645             case FGFT_INT32:
646                 eType = OFTInteger;
647                 break;
648             case FGFT_FLOAT32:
649                 eType = OFTReal;
650                 eSubType = OFSTFloat32;
651                 break;
652             case FGFT_FLOAT64:
653                 eType = OFTReal;
654                 break;
655             case FGFT_STRING:
656                 /* nWidth = poGDBField->GetMaxWidth(); */
657                 eType = OFTString;
658                 break;
659             case FGFT_UUID_1:
660             case FGFT_UUID_2:
661             case FGFT_XML:
662                 eType = OFTString;
663                 break;
664             case FGFT_DATETIME:
665                 eType = OFTDateTime;
666                 break;
667             case FGFT_UNDEFINED:
668             case FGFT_OBJECTID:
669             case FGFT_GEOMETRY:
670                 CPLAssert(false);
671                 break;
672             case FGFT_BINARY:
673             {
674                 /* Special case for v9 GDB_UserMetadata table */
675                 if( m_iFieldToReadAsBinary < 0 &&
676                     poGDBField->GetName() == "Xml" &&
677                     poGDBField->GetType() == FGFT_BINARY )
678                 {
679                     m_iFieldToReadAsBinary = i;
680                     eType = OFTString;
681                 }
682                 else
683                 {
684                     eType = OFTBinary;
685                 }
686                 break;
687             }
688             case FGFT_RASTER:
689             {
690                 const FileGDBRasterField* rasterField = cpl::down_cast<const FileGDBRasterField*>(poGDBField);
691                 if( rasterField->IsManaged() )
692                     eType = OFTInteger;
693                 else
694                     eType = OFTString;
695                 break;
696             }
697         }
698         OGRFieldDefn oFieldDefn(poGDBField->GetName().c_str(), eType);
699         oFieldDefn.SetAlternativeName(poGDBField->GetAlias().c_str());
700         oFieldDefn.SetSubType(eSubType);
701         // On creation in the FileGDB driver (GDBFieldTypeToWidthPrecision) if
702         // string width is 0, we pick up 65535 by default to mean unlimited
703         // string length, but we do not want to advertise such a big number.
704         if( eType == OFTString && nWidth < 65535 )
705             oFieldDefn.SetWidth(nWidth);
706         oFieldDefn.SetNullable(poGDBField->IsNullable());
707 
708         const CPLXMLNode* psFieldDef = nullptr;
709         if( psGPFieldInfoExs != nullptr )
710         {
711             for(const CPLXMLNode* psChild = psGPFieldInfoExs->psChild;
712                             psChild != nullptr;
713                             psChild = psChild->psNext )
714             {
715                 if( psChild->eType != CXT_Element )
716                     continue;
717                 if( EQUAL( psChild->pszValue, "GPFieldInfoEx") &&
718                     EQUAL( CPLGetXMLValue(psChild, "Name", ""),
719                            poGDBField->GetName().c_str()) )
720                 {
721                     psFieldDef = psChild;
722                     break;
723                 }
724             }
725         }
726 
727         const OGRField* psDefault = poGDBField->GetDefault();
728         if( !OGR_RawField_IsUnset(psDefault) && !OGR_RawField_IsNull(psDefault) )
729         {
730             if( eType == OFTString )
731             {
732                 CPLString osDefault("'");
733                 char* pszTmp = CPLEscapeString(psDefault->String, -1, CPLES_SQL);
734                 osDefault += pszTmp;
735                 CPLFree(pszTmp);
736                 osDefault += "'";
737                 oFieldDefn.SetDefault(osDefault);
738             }
739             else if( eType == OFTInteger || eType == OFTReal )
740             {
741                 // GDBs and the FileGDB SDK are not always reliable for
742                 // numeric values It often occurs that the XML definition in
743                 // a00000004.gdbtable does not match the default values (in
744                 // binary) found in the field definition section of the
745                 // .gdbtable of the layers themselves So check consistency.
746 
747                 const char* pszDefaultValue = nullptr;
748                 if( psFieldDef )
749                 {
750                     // From ArcGIS this is called DefaultValueNumeric
751                     // for integer and real.
752                     // From FileGDB API this is
753                     // called DefaultValue xsi:type=xs:int for integer
754                     // and DefaultValueNumeric for real ...
755                     pszDefaultValue =
756                         CPLGetXMLValue( psFieldDef, "DefaultValueNumeric",
757                                         nullptr );
758                     if( pszDefaultValue == nullptr )
759                         pszDefaultValue =
760                             CPLGetXMLValue( psFieldDef, "DefaultValue",
761                                             nullptr );
762                 }
763                 if( pszDefaultValue != nullptr )
764                 {
765                     if( eType == OFTInteger )
766                     {
767                         if ( atoi(pszDefaultValue) != psDefault->Integer)
768                         {
769                             CPLDebug(
770                                 "OpenFileGDB",
771                                 "For field %s, XML definition mentions %s "
772                                 "as default value whereas .gdbtable header "
773                                 "mentions %d. Using %s",
774                                 poGDBField->GetName().c_str(),
775                                 pszDefaultValue,
776                                 psDefault->Integer,
777                                 pszDefaultValue );
778                         }
779                         oFieldDefn.SetDefault(pszDefaultValue);
780                     }
781                     else if( eType == OFTReal )
782                     {
783                         if( fabs(CPLAtof(pszDefaultValue) - psDefault->Real) >
784                             1e-15 )
785                         {
786                             CPLDebug(
787                                 "OpenFileGDB", "For field %s, XML definition "
788                                 "mentions %s as default value whereas "
789                                 ".gdbtable header mentions %.18g. Using %s",
790                                 poGDBField->GetName().c_str(),
791                                 pszDefaultValue,
792                                 psDefault->Real,
793                                 pszDefaultValue );
794                         }
795                         oFieldDefn.SetDefault(pszDefaultValue);
796                     }
797                 }
798             }
799             else if( eType == OFTDateTime )
800                 oFieldDefn.SetDefault(
801                     CPLSPrintf( "'%04d/%02d/%02d %02d:%02d:%02d'",
802                                 psDefault->Date.Year,
803                                 psDefault->Date.Month,
804                                 psDefault->Date.Day,
805                                 psDefault->Date.Hour,
806                                 psDefault->Date.Minute,
807                                 static_cast<int>(psDefault->Date.Second) ));
808         }
809 
810         if( psFieldDef )
811         {
812             const char* pszDomainName = CPLGetXMLValue(psFieldDef, "DomainName", nullptr);
813             if( pszDomainName )
814                 oFieldDefn.SetDomainName(pszDomainName);
815         }
816 
817         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
818     }
819 
820     if( m_poLyrTable->HasDeletedFeaturesListed() )
821     {
822         OGRFieldDefn oFieldDefn("_deleted_", OFTInteger);
823         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
824     }
825 
826     return TRUE;
827 }
828 
829 /************************************************************************/
830 /*                           GetGeomType()                              */
831 /************************************************************************/
832 
GetGeomType()833 OGRwkbGeometryType OGROpenFileGDBLayer::GetGeomType()
834 {
835     if( m_eGeomType == wkbUnknown ||
836         m_osDefinition.empty() /* FileGDB v9 case */ )
837     {
838         (void) BuildLayerDefinition();
839     }
840 
841     return m_eGeomType;
842 }
843 
844 /***********************************************************************/
845 /*                          GetLayerDefn()                             */
846 /***********************************************************************/
847 
GetLayerDefn()848 OGRFeatureDefn* OGROpenFileGDBLayer::GetLayerDefn()
849 {
850     return m_poFeatureDefn;
851 }
852 
853 /***********************************************************************/
854 /*                          GetFIDColumn()                             */
855 /***********************************************************************/
856 
GetFIDColumn()857 const char* OGROpenFileGDBLayer::GetFIDColumn()
858 {
859     if( !BuildLayerDefinition() )
860         return "";
861     return m_poLyrTable->GetObjectIdColName().c_str();
862 }
863 
864 /***********************************************************************/
865 /*                          ResetReading()                             */
866 /***********************************************************************/
867 
ResetReading()868 void OGROpenFileGDBLayer::ResetReading()
869 {
870     if( m_iCurFeat != 0 )
871     {
872         if( m_eSpatialIndexState == SPI_IN_BUILDING )
873             m_eSpatialIndexState = SPI_INVALID;
874     }
875     m_bEOF = FALSE;
876     m_iCurFeat = 0;
877     if( m_poAttributeIterator )
878         m_poAttributeIterator->Reset();
879     if( m_poSpatialIndexIterator )
880         m_poSpatialIndexIterator->Reset();
881     if( m_poCombinedIterator )
882         m_poCombinedIterator->Reset();
883 }
884 
885 /***********************************************************************/
886 /*                         SetSpatialFilter()                          */
887 /***********************************************************************/
888 
SetSpatialFilter(OGRGeometry * poGeom)889 void OGROpenFileGDBLayer::SetSpatialFilter( OGRGeometry *poGeom )
890 {
891     if( !BuildLayerDefinition() )
892         return;
893 
894     OGRLayer::SetSpatialFilter(poGeom);
895 
896     if( m_bFilterIsEnvelope )
897     {
898         OGREnvelope sLayerEnvelope;
899         if( GetExtent(&sLayerEnvelope, FALSE) == OGRERR_NONE )
900         {
901             if( m_sFilterEnvelope.MinX <= sLayerEnvelope.MinX &&
902                 m_sFilterEnvelope.MinY <= sLayerEnvelope.MinY &&
903                 m_sFilterEnvelope.MaxX >= sLayerEnvelope.MaxX &&
904                 m_sFilterEnvelope.MaxY >= sLayerEnvelope.MaxY )
905             {
906 #ifdef DEBUG
907                 CPLDebug("OpenFileGDB", "Disabling spatial filter since it "
908                          "contains the layer spatial extent");
909 #endif
910                 poGeom = nullptr;
911                 OGRLayer::SetSpatialFilter(poGeom);
912             }
913         }
914     }
915 
916     if( poGeom != nullptr )
917     {
918         if( m_poSpatialIndexIterator == nullptr &&
919             m_poLyrTable->HasSpatialIndex() &&
920             CPLTestBool(
921                 CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX", "YES")) )
922         {
923             m_poSpatialIndexIterator = FileGDBSpatialIndexIterator::Build(
924                 m_poLyrTable, m_sFilterEnvelope);
925         }
926         else if( m_poSpatialIndexIterator != nullptr )
927         {
928             if( !m_poSpatialIndexIterator->SetEnvelope(m_sFilterEnvelope) )
929             {
930                 delete m_poSpatialIndexIterator;
931                 m_poSpatialIndexIterator = nullptr;
932             }
933         }
934         else if( m_eSpatialIndexState == SPI_COMPLETED )
935         {
936             CPLRectObj aoi;
937             aoi.minx = m_sFilterEnvelope.MinX;
938             aoi.miny = m_sFilterEnvelope.MinY;
939             aoi.maxx = m_sFilterEnvelope.MaxX;
940             aoi.maxy = m_sFilterEnvelope.MaxY;
941             CPLFree(m_pahFilteredFeatures);
942             m_nFilteredFeatureCount = -1;
943             m_pahFilteredFeatures = CPLQuadTreeSearch(m_pQuadTree,
944                                                       &aoi,
945                                                       &m_nFilteredFeatureCount);
946             if( m_nFilteredFeatureCount >= 0 )
947             {
948                 size_t* panStart = (size_t*)m_pahFilteredFeatures;
949                 std::sort(panStart, panStart + m_nFilteredFeatureCount);
950             }
951         }
952 
953         m_poLyrTable->InstallFilterEnvelope(&m_sFilterEnvelope);
954     }
955     else
956     {
957         delete m_poSpatialIndexIterator;
958         m_poSpatialIndexIterator = nullptr;
959         CPLFree(m_pahFilteredFeatures);
960         m_pahFilteredFeatures = nullptr;
961         m_nFilteredFeatureCount = -1;
962         m_poLyrTable->InstallFilterEnvelope(nullptr);
963     }
964 
965     BuildCombinedIterator();
966 }
967 
968 /***********************************************************************/
969 /*                            CompValues()                             */
970 /***********************************************************************/
971 
CompValues(OGRFieldDefn * poFieldDefn,const swq_expr_node * poValue1,const swq_expr_node * poValue2)972 static int CompValues(OGRFieldDefn* poFieldDefn,
973                       const swq_expr_node* poValue1,
974                       const swq_expr_node* poValue2)
975 {
976     int ret = 0;
977     switch( poFieldDefn->GetType() )
978     {
979         case OFTInteger:
980         {
981             int n1, n2;
982             if (poValue1->field_type == SWQ_FLOAT)
983                 n1 = (int) poValue1->float_value;
984             else
985                 n1 = (int) poValue1->int_value;
986             if (poValue2->field_type == SWQ_FLOAT)
987                 n2 = (int) poValue2->float_value;
988             else
989                 n2 = (int) poValue2->int_value;
990             if( n1 < n2 )
991                 ret = -1;
992             else if( n1 == n2 )
993                 ret = 0;
994             else
995                 ret = 1;
996             break;
997         }
998 
999         case OFTReal:
1000             if( poValue1->float_value < poValue2->float_value )
1001                 ret = -1;
1002             else if( poValue1->float_value == poValue2->float_value )
1003                 ret = 0;
1004             else
1005                 ret = 1;
1006             break;
1007 
1008         case OFTString:
1009             ret = strcmp(poValue1->string_value, poValue2->string_value);
1010             break;
1011 
1012         case OFTDate:
1013         case OFTTime:
1014         case OFTDateTime:
1015         {
1016             if ((poValue1->field_type == SWQ_TIMESTAMP ||
1017                  poValue1->field_type == SWQ_DATE ||
1018                  poValue1->field_type == SWQ_TIME) &&
1019                 (poValue2->field_type == SWQ_TIMESTAMP ||
1020                  poValue2->field_type == SWQ_DATE ||
1021                  poValue2->field_type == SWQ_TIME))
1022             {
1023                 ret = strcmp(poValue1->string_value, poValue2->string_value);
1024             }
1025             break;
1026         }
1027 
1028         default:
1029             break;
1030     }
1031     return ret;
1032 }
1033 
1034 /***********************************************************************/
1035 /*                    OGROpenFileGDBIsComparisonOp()                   */
1036 /***********************************************************************/
1037 
OGROpenFileGDBIsComparisonOp(int op)1038 int OGROpenFileGDBIsComparisonOp(int op)
1039 {
1040     return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT ||
1041             op == SWQ_LE || op == SWQ_GT || op == SWQ_GE);
1042 }
1043 
1044 /***********************************************************************/
1045 /*                        AreExprExclusive()                           */
1046 /***********************************************************************/
1047 
1048 static const struct
1049 {
1050     swq_op op1;
1051     swq_op op2;
1052     int    expected_comp_1;
1053     int    expected_comp_2;
1054 }
1055 asPairsOfComparisons[] =
1056 {
1057     { SWQ_EQ, SWQ_EQ, -1, 1 },
1058     { SWQ_LT, SWQ_GT, -1, 0 },
1059     { SWQ_GT, SWQ_LT, 0, 1 },
1060     { SWQ_LT, SWQ_GE, -1, 999 },
1061     { SWQ_LE, SWQ_GE, -1, 999 },
1062     { SWQ_LE, SWQ_GT, -1, 999 },
1063     { SWQ_GE, SWQ_LE, 1, 999 },
1064     { SWQ_GE, SWQ_LT, 1, 999 },
1065     { SWQ_GT, SWQ_LE, 1, 999 }
1066 };
1067 
AreExprExclusive(OGRFeatureDefn * poFeatureDefn,const swq_expr_node * poNode1,const swq_expr_node * poNode2)1068 static int AreExprExclusive(OGRFeatureDefn* poFeatureDefn,
1069                             const swq_expr_node* poNode1,
1070                             const swq_expr_node* poNode2)
1071 {
1072     if( poNode1->eNodeType != SNT_OPERATION )
1073         return FALSE;
1074     if( poNode2->eNodeType != SNT_OPERATION )
1075         return FALSE;
1076 
1077     const size_t nPairs = sizeof(asPairsOfComparisons) /
1078         sizeof(asPairsOfComparisons[0]);
1079     for(size_t i = 0; i < nPairs; i++ )
1080     {
1081         if( poNode1->nOperation == asPairsOfComparisons[i].op1 &&
1082             poNode2->nOperation == asPairsOfComparisons[i].op2 &&
1083             poNode1->nSubExprCount == 2 &&
1084             poNode2->nSubExprCount == 2 )
1085         {
1086             swq_expr_node *poColumn1 = poNode1->papoSubExpr[0];
1087             swq_expr_node *poValue1 = poNode1->papoSubExpr[1];
1088             swq_expr_node *poColumn2 = poNode2->papoSubExpr[0];
1089             swq_expr_node *poValue2 = poNode2->papoSubExpr[1];
1090             if( poColumn1->eNodeType == SNT_COLUMN &&
1091                 poValue1->eNodeType == SNT_CONSTANT &&
1092                 poColumn2->eNodeType == SNT_COLUMN &&
1093                 poValue2->eNodeType == SNT_CONSTANT &&
1094                 poColumn1->field_index == poColumn2->field_index &&
1095                 poColumn1->field_index < poFeatureDefn->GetFieldCount() )
1096             {
1097                 OGRFieldDefn *poFieldDefn =
1098                     poFeatureDefn->GetFieldDefn(poColumn1->field_index);
1099 
1100                 const int nComp = CompValues(poFieldDefn, poValue1, poValue2);
1101                 return nComp == asPairsOfComparisons[i].expected_comp_1 ||
1102                        nComp == asPairsOfComparisons[i].expected_comp_2;
1103             }
1104             return FALSE;
1105         }
1106     }
1107 
1108     if( (poNode2->nOperation == SWQ_ISNULL &&
1109          OGROpenFileGDBIsComparisonOp(poNode1->nOperation) &&
1110          poNode1->nSubExprCount == 2 &&
1111          poNode2->nSubExprCount == 1) ||
1112         (poNode1->nOperation == SWQ_ISNULL &&
1113          OGROpenFileGDBIsComparisonOp(poNode2->nOperation) &&
1114          poNode2->nSubExprCount == 2 &&
1115          poNode1->nSubExprCount == 1))
1116     {
1117         swq_expr_node *poColumn1 = poNode1->papoSubExpr[0];
1118         swq_expr_node *poColumn2 = poNode2->papoSubExpr[0];
1119         if( poColumn1->eNodeType == SNT_COLUMN &&
1120             poColumn2->eNodeType == SNT_COLUMN &&
1121             poColumn1->field_index == poColumn2->field_index &&
1122             poColumn1->field_index < poFeatureDefn->GetFieldCount() )
1123         {
1124             return TRUE;
1125         }
1126     }
1127 
1128     /* In doubt: return FALSE */
1129     return FALSE;
1130 }
1131 
1132 /***********************************************************************/
1133 /*                     FillTargetValueFromSrcExpr()                    */
1134 /***********************************************************************/
1135 
1136 static
FillTargetValueFromSrcExpr(OGRFieldDefn * poFieldDefn,OGRField * poTargetValue,const swq_expr_node * poSrcValue)1137 int FillTargetValueFromSrcExpr( OGRFieldDefn* poFieldDefn,
1138                                 OGRField* poTargetValue,
1139                                 const swq_expr_node* poSrcValue )
1140 {
1141     switch( poFieldDefn->GetType() )
1142     {
1143         case OFTInteger:
1144             if (poSrcValue->field_type == SWQ_FLOAT)
1145                 poTargetValue->Integer = (int) poSrcValue->float_value;
1146             else
1147                 poTargetValue->Integer = (int) poSrcValue->int_value;
1148             break;
1149 
1150         case OFTReal:
1151             poTargetValue->Real = poSrcValue->float_value;
1152             break;
1153 
1154         case OFTString:
1155             poTargetValue->String = poSrcValue->string_value;
1156             break;
1157 
1158         case OFTDate:
1159         case OFTTime:
1160         case OFTDateTime:
1161             if (poSrcValue->field_type == SWQ_TIMESTAMP ||
1162                 poSrcValue->field_type == SWQ_DATE ||
1163                 poSrcValue->field_type == SWQ_TIME)
1164             {
1165                 int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMin = 0, nSec = 0;
1166                 if( sscanf(poSrcValue->string_value, "%04d/%02d/%02d %02d:%02d:%02d",
1167                            &nYear, &nMonth, &nDay, &nHour, &nMin, &nSec) == 6 ||
1168                     sscanf(poSrcValue->string_value, "%04d/%02d/%02d",
1169                            &nYear, &nMonth, &nDay) == 3 ||
1170                     sscanf(poSrcValue->string_value, "%02d:%02d:%02d",
1171                            &nHour, &nMin, &nSec) == 3 )
1172                 {
1173                     poTargetValue->Date.Year = (GInt16)nYear;
1174                     poTargetValue->Date.Month = (GByte)nMonth;
1175                     poTargetValue->Date.Day = (GByte)nDay;
1176                     poTargetValue->Date.Hour = (GByte)nHour;
1177                     poTargetValue->Date.Minute = (GByte)nMin;
1178                     poTargetValue->Date.Second = (GByte)nSec;
1179                     poTargetValue->Date.TZFlag = 0;
1180                     poTargetValue->Date.Reserved = 0;
1181                 }
1182                 else
1183                     return FALSE;
1184             }
1185             else
1186                 return FALSE;
1187             break;
1188 
1189         default:
1190             return FALSE;
1191     }
1192     return TRUE;
1193 }
1194 
1195 /***********************************************************************/
1196 /*                        GetColumnSubNode()                           */
1197 /***********************************************************************/
1198 
GetColumnSubNode(swq_expr_node * poNode)1199 static swq_expr_node* GetColumnSubNode(swq_expr_node* poNode)
1200 {
1201     if( poNode->eNodeType == SNT_OPERATION &&
1202         poNode->nSubExprCount == 2 )
1203     {
1204         if( poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN )
1205             return poNode->papoSubExpr[0];
1206         if( poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN )
1207             return poNode->papoSubExpr[1];
1208     }
1209     return nullptr;
1210 }
1211 
1212 /***********************************************************************/
1213 /*                        GetConstantSubNode()                         */
1214 /***********************************************************************/
1215 
GetConstantSubNode(swq_expr_node * poNode)1216 static swq_expr_node* GetConstantSubNode(swq_expr_node* poNode)
1217 {
1218     if( poNode->eNodeType == SNT_OPERATION &&
1219         poNode->nSubExprCount == 2 )
1220     {
1221         if( poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT )
1222             return poNode->papoSubExpr[1];
1223         if( poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT )
1224             return poNode->papoSubExpr[0];
1225     }
1226     return nullptr;
1227 }
1228 /***********************************************************************/
1229 /*                     BuildIteratorFromExprNode()                     */
1230 /***********************************************************************/
1231 
BuildIteratorFromExprNode(swq_expr_node * poNode)1232 FileGDBIterator* OGROpenFileGDBLayer::BuildIteratorFromExprNode(swq_expr_node* poNode)
1233 {
1234     if( m_bIteratorSufficientToEvaluateFilter == FALSE )
1235         return nullptr;
1236 
1237     if( poNode->eNodeType == SNT_OPERATION &&
1238         poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2 )
1239     {
1240         // Even if there is only one branch of the 2 that results to an
1241         // iterator, it is useful. Of course, the iterator will not be
1242         // sufficient to evaluatethe filter, but it will be a super-set of the
1243         // features
1244         FileGDBIterator* poIter1 =
1245             BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1246 
1247         /* In case the first branch didn't result to an iterator, temporarily */
1248         /* restore the flag */
1249         const bool bSaveIteratorSufficientToEvaluateFilter =
1250             CPL_TO_BOOL(m_bIteratorSufficientToEvaluateFilter);
1251         m_bIteratorSufficientToEvaluateFilter = -1;
1252         FileGDBIterator* poIter2 = BuildIteratorFromExprNode(poNode->papoSubExpr[1]);
1253         m_bIteratorSufficientToEvaluateFilter = bSaveIteratorSufficientToEvaluateFilter;
1254 
1255         if( poIter1 != nullptr && poIter2 != nullptr )
1256             return FileGDBIterator::BuildAnd(poIter1, poIter2, true);
1257         m_bIteratorSufficientToEvaluateFilter = FALSE;
1258         if( poIter1 != nullptr )
1259             return poIter1;
1260         if( poIter2 != nullptr )
1261             return poIter2;
1262     }
1263 
1264     else if( poNode->eNodeType == SNT_OPERATION &&
1265         poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2 )
1266     {
1267         /* For a OR, we need an iterator for the 2 branches */
1268         FileGDBIterator* poIter1 = BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1269         if( poIter1 != nullptr )
1270         {
1271             FileGDBIterator* poIter2 = BuildIteratorFromExprNode(poNode->papoSubExpr[1]);
1272             if( poIter2 == nullptr )
1273             {
1274                 delete poIter1;
1275             }
1276             else
1277             {
1278                 return FileGDBIterator::BuildOr(poIter1, poIter2,
1279                                         AreExprExclusive(GetLayerDefn(),
1280                                                         poNode->papoSubExpr[0],
1281                                                         poNode->papoSubExpr[1]));
1282             }
1283         }
1284     }
1285 
1286     else if( poNode->eNodeType == SNT_OPERATION &&
1287              OGROpenFileGDBIsComparisonOp(poNode->nOperation) &&
1288              poNode->nSubExprCount == 2 )
1289     {
1290         swq_expr_node *poColumn = GetColumnSubNode(poNode);
1291         swq_expr_node *poValue = GetConstantSubNode(poNode);
1292         if( poColumn != nullptr && poValue != nullptr &&
1293             poColumn->field_index < GetLayerDefn()->GetFieldCount())
1294         {
1295             OGRFieldDefn *poFieldDefn =
1296                 GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1297 
1298             int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1299             if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
1300             {
1301                 OGRField sValue;
1302 
1303                 if( FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue) )
1304                 {
1305                     FileGDBSQLOp eOp = FGSO_EQ;
1306                     CPL_IGNORE_RET_VAL(eOp);
1307                     if( poColumn == poNode->papoSubExpr[0] )
1308                     {
1309                         switch( poNode->nOperation )
1310                         {
1311                             case SWQ_LE: eOp = FGSO_LE; break;
1312                             case SWQ_LT: eOp = FGSO_LT; break;
1313                             case SWQ_NE: eOp = FGSO_EQ; /* yes : EQ */ break;
1314                             case SWQ_EQ: eOp = FGSO_EQ; break;
1315                             case SWQ_GE: eOp = FGSO_GE; break;
1316                             case SWQ_GT: eOp = FGSO_GT; break;
1317                             default: CPLAssert(false); break;
1318                         }
1319                     }
1320                     else
1321                     {
1322                         /* If "constant op column", then we must reverse */
1323                         /* the operator */
1324                         switch( poNode->nOperation )
1325                         {
1326                             case SWQ_LE: eOp = FGSO_GE; break;
1327                             case SWQ_LT: eOp = FGSO_GT; break;
1328                             case SWQ_NE: eOp = FGSO_EQ; /* yes : EQ */ break;
1329                             case SWQ_EQ: eOp = FGSO_EQ; break;
1330                             case SWQ_GE: eOp = FGSO_LE; break;
1331                             case SWQ_GT: eOp = FGSO_LT; break;
1332                             default: CPLAssert(false); break;
1333                         }
1334                     }
1335 
1336                     FileGDBIterator* poIter = FileGDBIterator::Build(
1337                         m_poLyrTable, nTableColIdx, TRUE,
1338                         eOp, poFieldDefn->GetType(), &sValue);
1339                     if( poIter != nullptr )
1340                         m_bIteratorSufficientToEvaluateFilter = TRUE;
1341                     if( poIter && poNode->nOperation == SWQ_NE )
1342                         return FileGDBIterator::BuildNot(poIter);
1343                     else
1344                         return poIter;
1345                 }
1346             }
1347         }
1348     }
1349     else if( poNode->eNodeType == SNT_OPERATION &&
1350              poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1 )
1351     {
1352         swq_expr_node *poColumn = poNode->papoSubExpr[0];
1353         if( poColumn->eNodeType == SNT_COLUMN &&
1354             poColumn->field_index < GetLayerDefn()->GetFieldCount() )
1355         {
1356             OGRFieldDefn *poFieldDefn =
1357                 GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1358 
1359             int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1360             if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
1361             {
1362                 FileGDBIterator* poIter = FileGDBIterator::BuildIsNotNull(
1363                                                     m_poLyrTable, nTableColIdx, TRUE);
1364                 if( poIter )
1365                 {
1366                     m_bIteratorSufficientToEvaluateFilter = TRUE;
1367                     poIter = FileGDBIterator::BuildNot(poIter);
1368                 }
1369                 return poIter;
1370             }
1371         }
1372     }
1373     else if( poNode->eNodeType == SNT_OPERATION &&
1374                 poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 &&
1375                 poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
1376                 poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
1377                 poNode->papoSubExpr[0]->nSubExprCount == 1 )
1378     {
1379         swq_expr_node *poColumn = poNode->papoSubExpr[0]->papoSubExpr[0];
1380         if( poColumn->eNodeType == SNT_COLUMN &&
1381             poColumn->field_index < GetLayerDefn()->GetFieldCount() )
1382         {
1383             OGRFieldDefn *poFieldDefn =
1384                 GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1385 
1386             int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1387             if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
1388             {
1389                 FileGDBIterator* poIter = FileGDBIterator::BuildIsNotNull(
1390                                                 m_poLyrTable, nTableColIdx, TRUE);
1391                 if( poIter )
1392                     m_bIteratorSufficientToEvaluateFilter = TRUE;
1393                 return poIter;
1394             }
1395         }
1396     }
1397     else if( poNode->eNodeType == SNT_OPERATION &&
1398              poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 )
1399     {
1400         swq_expr_node *poColumn = poNode->papoSubExpr[0];
1401         if( poColumn->eNodeType == SNT_COLUMN &&
1402             poColumn->field_index < GetLayerDefn()->GetFieldCount() )
1403         {
1404             bool bAllConstants = true;
1405             for( int i=1; i<poNode->nSubExprCount; i++ )
1406             {
1407                 if( poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT )
1408                     bAllConstants = false;
1409             }
1410             OGRFieldDefn *poFieldDefn =
1411                 GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1412 
1413             int nTableColIdx =
1414                 m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1415             if( bAllConstants && nTableColIdx >= 0 &&
1416                 m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
1417             {
1418                 FileGDBIterator* poRet = nullptr;
1419                 for( int i=1; i<poNode->nSubExprCount; i++ )
1420                 {
1421                     OGRField sValue;
1422                     if( !FillTargetValueFromSrcExpr(poFieldDefn, &sValue,
1423                                                     poNode->papoSubExpr[i]) )
1424                     {
1425                         delete poRet;
1426                         poRet = nullptr;
1427                         break;
1428                     }
1429                     FileGDBIterator* poIter = FileGDBIterator::Build(
1430                                         m_poLyrTable, nTableColIdx, TRUE, FGSO_EQ,
1431                                         poFieldDefn->GetType(), &sValue);
1432                     if( poIter == nullptr )
1433                     {
1434                         delete poRet;
1435                         poRet = nullptr;
1436                         break;
1437                     }
1438                     if( poRet == nullptr )
1439                         poRet = poIter;
1440                     else
1441                         poRet = FileGDBIterator::BuildOr(poRet, poIter);
1442                 }
1443                 if( poRet != nullptr )
1444                 {
1445                     m_bIteratorSufficientToEvaluateFilter = TRUE;
1446                     return poRet;
1447                 }
1448             }
1449         }
1450     }
1451     else if( poNode->eNodeType == SNT_OPERATION &&
1452              poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 )
1453     {
1454         FileGDBIterator* poIter = BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1455         /* If we have an iterator that is only partial w.r.t the full clause */
1456         /* then we cannot do anything with it unfortunately */
1457         if( m_bIteratorSufficientToEvaluateFilter == FALSE )
1458         {
1459             if( poIter != nullptr )
1460                 CPLDebug("OpenFileGDB", "Disabling use of indexes");
1461             delete poIter;
1462         }
1463         else if( poIter != nullptr )
1464         {
1465             return FileGDBIterator::BuildNot(poIter);
1466         }
1467     }
1468 
1469     if( m_bIteratorSufficientToEvaluateFilter == TRUE )
1470         CPLDebug("OpenFileGDB", "Disabling use of indexes");
1471     m_bIteratorSufficientToEvaluateFilter = FALSE;
1472     return nullptr;
1473 }
1474 
1475 /***********************************************************************/
1476 /*                         SetAttributeFilter()                        */
1477 /***********************************************************************/
1478 
SetAttributeFilter(const char * pszFilter)1479 OGRErr OGROpenFileGDBLayer::SetAttributeFilter( const char* pszFilter )
1480 {
1481     if( !BuildLayerDefinition() )
1482         return OGRERR_FAILURE;
1483 
1484     delete m_poAttributeIterator;
1485     m_poAttributeIterator = nullptr;
1486     delete m_poCombinedIterator;
1487     m_poCombinedIterator = nullptr;
1488     m_bIteratorSufficientToEvaluateFilter = FALSE;
1489 
1490     OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
1491     if( eErr != OGRERR_NONE ||
1492         !CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES")) )
1493         return eErr;
1494 
1495     if( m_poAttrQuery != nullptr && m_nFilteredFeatureCount < 0 )
1496     {
1497         swq_expr_node* poNode = (swq_expr_node*) m_poAttrQuery->GetSWQExpr();
1498         poNode->ReplaceBetweenByGEAndLERecurse();
1499         m_bIteratorSufficientToEvaluateFilter = -1;
1500         m_poAttributeIterator = BuildIteratorFromExprNode(poNode);
1501         if( m_poAttributeIterator != nullptr && m_eSpatialIndexState == SPI_IN_BUILDING )
1502             m_eSpatialIndexState = SPI_INVALID;
1503         if( m_bIteratorSufficientToEvaluateFilter < 0 )
1504             m_bIteratorSufficientToEvaluateFilter = FALSE;
1505     }
1506 
1507     BuildCombinedIterator();
1508 
1509     return eErr;
1510 }
1511 
1512 /***********************************************************************/
1513 /*                       BuildCombinedIterator()                       */
1514 /***********************************************************************/
1515 
BuildCombinedIterator()1516 void OGROpenFileGDBLayer::BuildCombinedIterator()
1517 {
1518     delete m_poCombinedIterator;
1519     if( m_poAttributeIterator && m_poSpatialIndexIterator )
1520     {
1521         m_poCombinedIterator = FileGDBIterator::BuildAnd(m_poAttributeIterator,
1522                                                          m_poSpatialIndexIterator,
1523                                                          false);
1524     }
1525     else
1526     {
1527         m_poCombinedIterator = nullptr;
1528     }
1529 }
1530 
1531 /***********************************************************************/
1532 /*                         GetCurrentFeature()                         */
1533 /***********************************************************************/
1534 
GetCurrentFeature()1535 OGRFeature* OGROpenFileGDBLayer::GetCurrentFeature()
1536 {
1537     OGRFeature *poFeature = nullptr;
1538     int iOGRIdx = 0;
1539     int iRow = m_poLyrTable->GetCurRow();
1540     for(int iGDBIdx=0;iGDBIdx<m_poLyrTable->GetFieldCount();iGDBIdx++)
1541     {
1542         if( iGDBIdx == m_iGeomFieldIdx )
1543         {
1544             if( m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored() )
1545             {
1546                 if( m_eSpatialIndexState == SPI_IN_BUILDING )
1547                     m_eSpatialIndexState = SPI_INVALID;
1548                 continue;
1549             }
1550 
1551             const OGRField* psField = m_poLyrTable->GetFieldValue(iGDBIdx);
1552             if( psField != nullptr )
1553             {
1554                 if( m_eSpatialIndexState == SPI_IN_BUILDING )
1555                 {
1556                     OGREnvelope sFeatureEnvelope;
1557                     if( m_poLyrTable->GetFeatureExtent(psField,
1558                                                        &sFeatureEnvelope) )
1559                     {
1560                         CPLRectObj sBounds;
1561                         sBounds.minx = sFeatureEnvelope.MinX;
1562                         sBounds.miny = sFeatureEnvelope.MinY;
1563                         sBounds.maxx = sFeatureEnvelope.MaxX;
1564                         sBounds.maxy = sFeatureEnvelope.MaxY;
1565                         CPLQuadTreeInsertWithBounds(m_pQuadTree,
1566                                                     (void*)(size_t)iRow,
1567                                                     &sBounds);
1568                     }
1569                 }
1570 
1571                 if( m_poFilterGeom != nullptr &&
1572                     m_eSpatialIndexState != SPI_COMPLETED &&
1573                     !m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField) )
1574                 {
1575                     delete poFeature;
1576                     return nullptr;
1577                 }
1578 
1579                 OGRGeometry* poGeom = m_poGeomConverter->GetAsGeometry(psField);
1580                 if( poGeom != nullptr )
1581                 {
1582                     OGRwkbGeometryType eFlattenType = wkbFlatten(poGeom->getGeometryType());
1583                     if( eFlattenType == wkbPolygon )
1584                         poGeom = OGRGeometryFactory::forceToMultiPolygon(poGeom);
1585                     else if( eFlattenType == wkbCurvePolygon)
1586                     {
1587                         OGRMultiSurface* poMS = new OGRMultiSurface();
1588                         poMS->addGeometryDirectly( poGeom );
1589                         poGeom = poMS;
1590                     }
1591                     else if( eFlattenType == wkbLineString )
1592                         poGeom = OGRGeometryFactory::forceToMultiLineString(poGeom);
1593                     else if (eFlattenType == wkbCompoundCurve)
1594                     {
1595                         OGRMultiCurve* poMC = new OGRMultiCurve();
1596                         poMC->addGeometryDirectly( poGeom );
1597                         poGeom = poMC;
1598                     }
1599 
1600                     poGeom->assignSpatialReference(
1601                         m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef() );
1602 
1603                     if( poFeature == nullptr )
1604                         poFeature = new OGRFeature(m_poFeatureDefn);
1605                     poFeature->SetGeometryDirectly( poGeom );
1606                 }
1607             }
1608         }
1609         else
1610         {
1611             const OGRFieldDefn* poFieldDefn = m_poFeatureDefn->GetFieldDefn(iOGRIdx);
1612             if( !poFieldDefn->IsIgnored() )
1613             {
1614                 OGRField* psField = m_poLyrTable->GetFieldValue(iGDBIdx);
1615                 if( poFeature == nullptr )
1616                     poFeature = new OGRFeature(m_poFeatureDefn);
1617                 if( psField == nullptr )
1618                 {
1619                     poFeature->SetFieldNull(iOGRIdx);
1620                 }
1621                 else
1622                 {
1623 
1624                     if( iGDBIdx == m_iFieldToReadAsBinary )
1625                         poFeature->SetField(iOGRIdx, reinterpret_cast<const char*>(psField->Binary.paData));
1626                     else if( poFieldDefn->GetType() == OFTDateTime )
1627                     {
1628                         psField->Date.TZFlag = m_bTimeInUTC ? 100 : 0;
1629                         poFeature->SetField(iOGRIdx, psField);
1630                     }
1631                     else
1632                         poFeature->SetField(iOGRIdx, psField);
1633                 }
1634             }
1635             iOGRIdx ++;
1636         }
1637     }
1638 
1639     if( poFeature == nullptr )
1640         poFeature = new OGRFeature(m_poFeatureDefn);
1641 
1642     if( m_poLyrTable->HasDeletedFeaturesListed() )
1643     {
1644         poFeature->SetField(poFeature->GetFieldCount() - 1,
1645                             m_poLyrTable->IsCurRowDeleted());
1646     }
1647 
1648     poFeature->SetFID(iRow + 1);
1649     return poFeature;
1650 }
1651 
1652 /***********************************************************************/
1653 /*                         GetNextFeature()                            */
1654 /***********************************************************************/
1655 
GetNextFeature()1656 OGRFeature* OGROpenFileGDBLayer::GetNextFeature()
1657 {
1658     if( !BuildLayerDefinition() || m_bEOF )
1659         return nullptr;
1660 
1661     FileGDBIterator* poIterator =
1662         m_poCombinedIterator ? m_poCombinedIterator:
1663         m_poSpatialIndexIterator ? m_poSpatialIndexIterator:
1664         m_poAttributeIterator;
1665 
1666     while( true )
1667     {
1668         OGRFeature *poFeature = nullptr;
1669 
1670         if( m_nFilteredFeatureCount >= 0 )
1671         {
1672             while( true )
1673             {
1674                 if( m_iCurFeat >= m_nFilteredFeatureCount )
1675                 {
1676                     return nullptr;
1677                 }
1678                 int iRow = (int)(GUIntptr_t)m_pahFilteredFeatures[m_iCurFeat++];
1679                 if( m_poLyrTable->SelectRow(iRow) )
1680                 {
1681                     poFeature = GetCurrentFeature();
1682                     if( poFeature )
1683                         break;
1684                 }
1685                 else if( m_poLyrTable->HasGotError() )
1686                 {
1687                     m_bEOF = TRUE;
1688                     return nullptr;
1689                 }
1690             }
1691         }
1692         else if( poIterator != nullptr )
1693         {
1694             while( true )
1695             {
1696                 int iRow = poIterator->GetNextRowSortedByFID();
1697                 if( iRow < 0 )
1698                     return nullptr;
1699                 if( m_poLyrTable->SelectRow(iRow) )
1700                 {
1701                     poFeature = GetCurrentFeature();
1702                     if( poFeature )
1703                         break;
1704                 }
1705                 else if( m_poLyrTable->HasGotError() )
1706                 {
1707                     m_bEOF = TRUE;
1708                     return nullptr;
1709                 }
1710             }
1711         }
1712         else
1713         {
1714             while( true )
1715             {
1716                 if( m_iCurFeat == m_poLyrTable->GetTotalRecordCount() )
1717                 {
1718                     return nullptr;
1719                 }
1720                 m_iCurFeat = m_poLyrTable->GetAndSelectNextNonEmptyRow(m_iCurFeat);
1721                 if( m_iCurFeat < 0 )
1722                 {
1723                     m_bEOF = TRUE;
1724                     return nullptr;
1725                 }
1726                 else
1727                 {
1728                     m_iCurFeat ++;
1729                     poFeature = GetCurrentFeature();
1730                     if( m_eSpatialIndexState == SPI_IN_BUILDING &&
1731                         m_iCurFeat == m_poLyrTable->GetTotalRecordCount() )
1732                     {
1733                         CPLDebug("OpenFileGDB", "SPI_COMPLETED");
1734                         m_eSpatialIndexState = SPI_COMPLETED;
1735                     }
1736                     if( poFeature )
1737                         break;
1738                 }
1739             }
1740         }
1741 
1742         if( (m_poFilterGeom == nullptr
1743              || FilterGeometry( poFeature->GetGeometryRef() ) )
1744             && (m_poAttrQuery == nullptr ||
1745                 (m_poAttributeIterator != nullptr && m_bIteratorSufficientToEvaluateFilter) ||
1746                 m_poAttrQuery->Evaluate( poFeature ) ) )
1747         {
1748             return poFeature;
1749         }
1750 
1751         delete poFeature;
1752     }
1753 }
1754 
1755 /***********************************************************************/
1756 /*                          GetFeature()                               */
1757 /***********************************************************************/
1758 
GetFeature(GIntBig nFeatureId)1759 OGRFeature* OGROpenFileGDBLayer::GetFeature( GIntBig nFeatureId )
1760 {
1761     if( !BuildLayerDefinition() )
1762         return nullptr;
1763 
1764     if( nFeatureId < 1 || nFeatureId > m_poLyrTable->GetTotalRecordCount() )
1765         return nullptr;
1766     if( !m_poLyrTable->SelectRow((int)nFeatureId - 1) )
1767         return nullptr;
1768 
1769     /* Temporarily disable spatial filter */
1770     OGRGeometry* poOldSpatialFilter = m_poFilterGeom;
1771     m_poFilterGeom = nullptr;
1772     /* and also spatial index state to avoid features to be inserted */
1773     /* multiple times in spatial index */
1774     SPIState eOldState = m_eSpatialIndexState;
1775     m_eSpatialIndexState = SPI_INVALID;
1776 
1777     OGRFeature* poFeature = GetCurrentFeature();
1778 
1779     /* Set it back */
1780     m_poFilterGeom = poOldSpatialFilter;
1781     m_eSpatialIndexState = eOldState;
1782 
1783     return poFeature;
1784 }
1785 
1786 /***********************************************************************/
1787 /*                         SetNextByIndex()                            */
1788 /***********************************************************************/
1789 
SetNextByIndex(GIntBig nIndex)1790 OGRErr OGROpenFileGDBLayer::SetNextByIndex( GIntBig nIndex )
1791 {
1792     if( m_poAttributeIterator != nullptr || m_poSpatialIndexIterator != nullptr )
1793         return OGRLayer::SetNextByIndex(nIndex);
1794 
1795     if( !BuildLayerDefinition() )
1796         return OGRERR_FAILURE;
1797 
1798     if( m_eSpatialIndexState == SPI_IN_BUILDING )
1799         m_eSpatialIndexState = SPI_INVALID;
1800 
1801     if( m_nFilteredFeatureCount >= 0 )
1802     {
1803         if( nIndex < 0 || nIndex >= m_nFilteredFeatureCount )
1804             return OGRERR_FAILURE;
1805         m_iCurFeat = (int) nIndex;
1806         return OGRERR_NONE;
1807     }
1808     else if( m_poLyrTable->GetValidRecordCount() == m_poLyrTable->GetTotalRecordCount() )
1809     {
1810         if( nIndex < 0 || nIndex >= m_poLyrTable->GetValidRecordCount() )
1811             return OGRERR_FAILURE;
1812         m_iCurFeat = (int) nIndex;
1813         return OGRERR_NONE;
1814     }
1815     else
1816         return OGRLayer::SetNextByIndex(nIndex);
1817 }
1818 
1819 /***********************************************************************/
1820 /*                           GetExtent()                               */
1821 /***********************************************************************/
1822 
GetExtent(OGREnvelope * psExtent,int)1823 OGRErr OGROpenFileGDBLayer::GetExtent( OGREnvelope *psExtent, int /* bForce */)
1824 {
1825     if( !BuildLayerDefinition() )
1826         return OGRERR_FAILURE;
1827 
1828     if( m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0 )
1829     {
1830         FileGDBGeomField* poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
1831             m_poLyrTable->GetField(m_iGeomFieldIdx));
1832         psExtent->MinX = poGDBGeomField->GetXMin();
1833         psExtent->MinY = poGDBGeomField->GetYMin();
1834         psExtent->MaxX = poGDBGeomField->GetXMax();
1835         psExtent->MaxY = poGDBGeomField->GetYMax();
1836         return OGRERR_NONE;
1837     }
1838 
1839     return OGRERR_FAILURE;
1840 }
1841 
1842 /***********************************************************************/
1843 /*                         GetFeatureCount()                           */
1844 /***********************************************************************/
1845 
GetFeatureCount(int bForce)1846 GIntBig OGROpenFileGDBLayer::GetFeatureCount( int bForce )
1847 {
1848     if( !BuildLayerDefinition() )
1849         return 0;
1850 
1851     /* No filter */
1852     if( (m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0 ) &&
1853         m_poAttrQuery == nullptr )
1854     {
1855         return m_poLyrTable->GetValidRecordCount();
1856     }
1857     else if( m_nFilteredFeatureCount >= 0 && m_poAttrQuery == nullptr )
1858     {
1859         return m_nFilteredFeatureCount;
1860     }
1861 
1862     /* Only geometry filter ? */
1863     if( m_poAttrQuery == nullptr && m_bFilterIsEnvelope )
1864     {
1865         if( m_poSpatialIndexIterator )
1866         {
1867             m_poSpatialIndexIterator->Reset();
1868             int nCount = 0;
1869             while( true )
1870             {
1871                 const int nRowIdx = m_poSpatialIndexIterator->GetNextRowSortedByFID();
1872                 if( nRowIdx < 0 )
1873                     break;
1874                 if( !m_poLyrTable->SelectRow(nRowIdx) )
1875                 {
1876                     if( m_poLyrTable->HasGotError() )
1877                         break;
1878                     else
1879                         continue;
1880                 }
1881 
1882                 const OGRField* psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
1883                 if( psField != nullptr )
1884                 {
1885                     if( m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField) )
1886                     {
1887                         OGRGeometry* poGeom = m_poGeomConverter->GetAsGeometry(psField);
1888                         if( poGeom != nullptr && FilterGeometry( poGeom ))
1889                         {
1890                             nCount ++;
1891                         }
1892                         delete poGeom;
1893                     }
1894                 }
1895             }
1896             return nCount;
1897         }
1898 
1899         int nCount = 0;
1900         if( m_eSpatialIndexState == SPI_IN_BUILDING && m_iCurFeat != 0 )
1901             m_eSpatialIndexState = SPI_INVALID;
1902 
1903         int nFilteredFeatureCountAlloc = 0;
1904         if( m_eSpatialIndexState == SPI_IN_BUILDING )
1905         {
1906             CPLFree(m_pahFilteredFeatures);
1907             m_pahFilteredFeatures = nullptr;
1908             m_nFilteredFeatureCount = 0;
1909         }
1910 
1911         for(int i=0;i<m_poLyrTable->GetTotalRecordCount();i++)
1912         {
1913             if( !m_poLyrTable->SelectRow(i) )
1914             {
1915                 if( m_poLyrTable->HasGotError() )
1916                     break;
1917                 else
1918                     continue;
1919             }
1920 
1921             const OGRField* psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
1922             if( psField != nullptr )
1923             {
1924                 if( m_eSpatialIndexState == SPI_IN_BUILDING )
1925                 {
1926                     OGREnvelope sFeatureEnvelope;
1927                     if( m_poLyrTable->GetFeatureExtent(psField,
1928                                                        &sFeatureEnvelope) )
1929                     {
1930                         CPLRectObj sBounds;
1931                         sBounds.minx = sFeatureEnvelope.MinX;
1932                         sBounds.miny = sFeatureEnvelope.MinY;
1933                         sBounds.maxx = sFeatureEnvelope.MaxX;
1934                         sBounds.maxy = sFeatureEnvelope.MaxY;
1935                         CPLQuadTreeInsertWithBounds(m_pQuadTree,
1936                                                     (void*)(size_t)i,
1937                                                     &sBounds);
1938                     }
1939                 }
1940 
1941                 if( m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField) )
1942                 {
1943                     OGRGeometry* poGeom = m_poGeomConverter->GetAsGeometry(psField);
1944                     if( poGeom != nullptr && FilterGeometry( poGeom ))
1945                     {
1946                         if( m_eSpatialIndexState == SPI_IN_BUILDING )
1947                         {
1948                             if( nCount == nFilteredFeatureCountAlloc )
1949                             {
1950                                 nFilteredFeatureCountAlloc =
1951                                   4 * nFilteredFeatureCountAlloc / 3 + 1024;
1952                                 m_pahFilteredFeatures = static_cast<void**>(
1953                                     CPLRealloc(
1954                                         m_pahFilteredFeatures,
1955                                         sizeof(void*) *
1956                                         nFilteredFeatureCountAlloc));
1957                             }
1958                             m_pahFilteredFeatures[nCount] = (void*)(size_t)i;
1959                         }
1960                         nCount ++;
1961                     }
1962                     delete poGeom;
1963                 }
1964             }
1965         }
1966         if( m_eSpatialIndexState == SPI_IN_BUILDING )
1967         {
1968             m_nFilteredFeatureCount = nCount;
1969             m_eSpatialIndexState = SPI_COMPLETED;
1970         }
1971 
1972         return nCount;
1973     }
1974     /* Only simple attribute filter ? */
1975     else if( m_poFilterGeom == nullptr &&
1976              m_poAttributeIterator != nullptr && m_bIteratorSufficientToEvaluateFilter )
1977     {
1978         return m_poAttributeIterator->GetRowCount();
1979     }
1980 
1981     return OGRLayer::GetFeatureCount(bForce);
1982 }
1983 
1984 /***********************************************************************/
1985 /*                         TestCapability()                            */
1986 /***********************************************************************/
1987 
TestCapability(const char * pszCap)1988 int OGROpenFileGDBLayer::TestCapability( const char * pszCap )
1989 {
1990     if( !BuildLayerDefinition() )
1991         return FALSE;
1992 
1993     if( EQUAL(pszCap,OLCFastFeatureCount) )
1994     {
1995         return( (m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0 ) &&
1996                 m_poAttrQuery == nullptr );
1997     }
1998     else if( EQUAL(pszCap,OLCFastSetNextByIndex) )
1999     {
2000         return ( m_poLyrTable->GetValidRecordCount() ==
2001                  m_poLyrTable->GetTotalRecordCount() &&
2002                  m_poAttributeIterator == nullptr &&
2003                  m_poSpatialIndexIterator == nullptr );
2004     }
2005     else if( EQUAL(pszCap,OLCRandomRead) )
2006     {
2007         return TRUE;
2008     }
2009     else if( EQUAL(pszCap,OLCFastGetExtent) )
2010     {
2011         return TRUE;
2012     }
2013     else if( EQUAL(pszCap,OLCIgnoreFields) )
2014     {
2015         return TRUE;
2016     }
2017     else if( EQUAL(pszCap,OLCStringsAsUTF8) )
2018     {
2019         return TRUE; /* ? */
2020     }
2021     else if( EQUAL(pszCap, OLCFastSpatialFilter) )
2022     {
2023         return m_eSpatialIndexState == SPI_COMPLETED || m_poLyrTable->HasSpatialIndex();
2024     }
2025 
2026     return FALSE;
2027 }
2028 
2029 /***********************************************************************/
2030 /*                         HasIndexForField()                          */
2031 /***********************************************************************/
2032 
HasIndexForField(const char * pszFieldName)2033 int OGROpenFileGDBLayer::HasIndexForField(const char* pszFieldName)
2034 {
2035     if( !BuildLayerDefinition() )
2036         return FALSE;
2037 
2038     int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName);
2039     return ( nTableColIdx >= 0 &&
2040              m_poLyrTable->GetField(nTableColIdx)->HasIndex() );
2041 }
2042 
2043 /***********************************************************************/
2044 /*                             BuildIndex()                            */
2045 /***********************************************************************/
2046 
BuildIndex(const char * pszFieldName,int bAscending,int op,swq_expr_node * poValue)2047 FileGDBIterator* OGROpenFileGDBLayer::BuildIndex(const char* pszFieldName,
2048                                                  int bAscending,
2049                                                  int op,
2050                                                  swq_expr_node* poValue)
2051 {
2052     if( !BuildLayerDefinition() )
2053         return nullptr;
2054 
2055     int idx = GetLayerDefn()->GetFieldIndex(pszFieldName);
2056     if( idx < 0 )
2057         return nullptr;
2058     OGRFieldDefn* poFieldDefn = GetLayerDefn()->GetFieldDefn(idx);
2059 
2060     int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName);
2061     if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
2062     {
2063         if( op < 0 )
2064             return FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, bAscending);
2065 
2066         OGRField sValue;
2067         if( FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue) )
2068         {
2069             FileGDBSQLOp eOp;
2070             switch( op )
2071             {
2072                 case SWQ_LE: eOp = FGSO_LE; break;
2073                 case SWQ_LT: eOp = FGSO_LT; break;
2074                 case SWQ_EQ: eOp = FGSO_EQ; break;
2075                 case SWQ_GE: eOp = FGSO_GE; break;
2076                 case SWQ_GT: eOp = FGSO_GT; break;
2077                 default: return nullptr;
2078             }
2079 
2080             return FileGDBIterator::Build(
2081                             m_poLyrTable, nTableColIdx, bAscending,
2082                             eOp, poFieldDefn->GetType(), &sValue);
2083         }
2084     }
2085     return nullptr;
2086 }
2087 
2088 /***********************************************************************/
2089 /*                          GetMinMaxValue()                           */
2090 /***********************************************************************/
2091 
GetMinMaxValue(OGRFieldDefn * poFieldDefn,int bIsMin,int & eOutType)2092 const OGRField* OGROpenFileGDBLayer::GetMinMaxValue(OGRFieldDefn* poFieldDefn,
2093                                                     int bIsMin,
2094                                                     int& eOutType)
2095 {
2096     eOutType = -1;
2097     if( !BuildLayerDefinition() )
2098         return nullptr;
2099 
2100     const int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
2101     if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
2102     {
2103         delete m_poIterMinMax;
2104         m_poIterMinMax = FileGDBIterator::BuildIsNotNull(
2105             m_poLyrTable, nTableColIdx, TRUE);
2106         if( m_poIterMinMax != nullptr )
2107         {
2108             const OGRField* poRet = (bIsMin ) ?
2109                 m_poIterMinMax->GetMinValue(eOutType) :
2110                 m_poIterMinMax->GetMaxValue(eOutType);
2111             if( poRet == nullptr )
2112                 eOutType = poFieldDefn->GetType();
2113             return poRet;
2114         }
2115     }
2116     return nullptr;
2117 }
2118 
2119 /***********************************************************************/
2120 /*                        GetMinMaxSumCount()                          */
2121 /***********************************************************************/
2122 
GetMinMaxSumCount(OGRFieldDefn * poFieldDefn,double & dfMin,double & dfMax,double & dfSum,int & nCount)2123 int OGROpenFileGDBLayer::GetMinMaxSumCount(OGRFieldDefn* poFieldDefn,
2124                                            double& dfMin, double& dfMax,
2125                                            double& dfSum, int& nCount)
2126 {
2127     dfMin = 0.0;
2128     dfMax = 0.0;
2129     dfSum = 0.0;
2130     nCount = 0;
2131     if( !BuildLayerDefinition() )
2132         return FALSE;
2133 
2134     int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
2135     if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
2136     {
2137         FileGDBIterator* poIter = FileGDBIterator::BuildIsNotNull(
2138             m_poLyrTable, nTableColIdx, TRUE);
2139         if( poIter != nullptr )
2140         {
2141             int nRet = poIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount);
2142             delete poIter;
2143             return nRet;
2144         }
2145     }
2146     return FALSE;
2147 }
2148