Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer.cpp
Line
Count
Source
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
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "ogr_openfilegdb.h"
15
16
#include <cmath>
17
#include <cstddef>
18
#include <cstdio>
19
#include <cstdlib>
20
#include <cstring>
21
#include <cwchar>
22
#include <algorithm>
23
#include <limits>
24
#include <string>
25
26
#include "cpl_conv.h"
27
#include "cpl_error.h"
28
#include "cpl_minixml.h"
29
#include "cpl_quad_tree.h"
30
#include "cpl_string.h"
31
#include "ogr_api.h"
32
#include "ogr_core.h"
33
#include "ogr_feature.h"
34
#include "ogr_geometry.h"
35
#include "ogr_spatialref.h"
36
#include "ogr_srs_api.h"
37
#include "ogrsf_frmts.h"
38
#include "filegdbtable.h"
39
#include "ogr_swq.h"
40
#include "filegdb_coordprec_read.h"
41
42
13.0k
OGROpenFileGDBGeomFieldDefn::~OGROpenFileGDBGeomFieldDefn() = default;
43
44
27.5k
OGROpenFileGDBFeatureDefn::~OGROpenFileGDBFeatureDefn() = default;
45
46
/************************************************************************/
47
/*                        OGROpenFileGDBLayer()                         */
48
/************************************************************************/
49
50
OGROpenFileGDBLayer::OGROpenFileGDBLayer(
51
    OGROpenFileGDBDataSource *poDS, const char *pszGDBFilename,
52
    const char *pszName, const std::string &osDefinition,
53
    const std::string &osDocumentation, bool bEditable,
54
    OGRwkbGeometryType eGeomType, const std::string &osParentDefinition)
55
22.4k
    : m_poDS(poDS), m_osGDBFilename(pszGDBFilename), m_osName(pszName),
56
22.4k
      m_bEditable(bEditable), m_osDefinition(osDefinition),
57
22.4k
      m_osDocumentation(osDocumentation)
58
22.4k
{
59
    // TODO(rouault): What error on compiler versions?  r33032 does not say.
60
61
    // We cannot initialize m_poFeatureDefn in above list. MSVC doesn't like
62
    // this to be used in initialization list.
63
22.4k
    m_poFeatureDefn = new OGROpenFileGDBFeatureDefn(this, pszName, false);
64
22.4k
    SetDescription(m_poFeatureDefn->GetName());
65
22.4k
    m_poFeatureDefn->SetGeomType(wkbNone);
66
22.4k
    m_poFeatureDefn->Reference();
67
68
22.4k
    m_eGeomType = eGeomType;
69
70
22.4k
    if (!m_osDefinition.empty())
71
7.32k
    {
72
7.32k
        BuildGeometryColumnGDBv10(osParentDefinition);
73
7.32k
    }
74
75
    // bSealFields = false because we do lazy resolution of fields
76
22.4k
    m_poFeatureDefn->Seal(/* bSealFields = */ false);
77
22.4k
}
78
79
/************************************************************************/
80
/*                        OGROpenFileGDBLayer()                         */
81
/************************************************************************/
82
83
OGROpenFileGDBLayer::OGROpenFileGDBLayer(OGROpenFileGDBDataSource *poDS,
84
                                         const char *pszGDBFilename,
85
                                         const char *pszName,
86
                                         OGRwkbGeometryType eType,
87
                                         CSLConstList papszOptions)
88
5.13k
    : m_poDS(poDS), m_osGDBFilename(pszGDBFilename), m_osName(pszName),
89
5.13k
      m_aosCreationOptions(papszOptions), m_eGeomType(eType),
90
      m_bArcGISPro32OrLater(
91
5.13k
          EQUAL(CSLFetchNameValueDef(papszOptions, "TARGET_ARCGIS_VERSION", ""),
92
                "ARCGIS_PRO_3_2_OR_LATER"))
93
5.13k
{
94
5.13k
}
95
96
/************************************************************************/
97
/*                        ~OGROpenFileGDBLayer()                        */
98
/************************************************************************/
99
100
OGROpenFileGDBLayer::~OGROpenFileGDBLayer()
101
27.6k
{
102
27.6k
    OGROpenFileGDBLayer::SyncToDisk();
103
104
27.6k
    if (m_poFeatureDefn)
105
27.5k
    {
106
27.5k
        m_poFeatureDefn->UnsetLayer();
107
27.5k
        m_poFeatureDefn->Release();
108
27.5k
    }
109
110
27.6k
    delete m_poLyrTable;
111
112
27.6k
    delete m_poAttributeIterator;
113
27.6k
    delete m_poIterMinMax;
114
27.6k
    delete m_poSpatialIndexIterator;
115
27.6k
    delete m_poCombinedIterator;
116
27.6k
    if (m_pQuadTree != nullptr)
117
6.26k
        CPLQuadTreeDestroy(m_pQuadTree);
118
27.6k
    CPLFree(m_pahFilteredFeatures);
119
27.6k
}
120
121
/************************************************************************/
122
/*                               Close()                                */
123
/************************************************************************/
124
125
void OGROpenFileGDBLayer::Close()
126
3.46k
{
127
3.46k
    delete m_poLyrTable;
128
3.46k
    m_poLyrTable = nullptr;
129
3.46k
    m_bValidLayerDefn = FALSE;
130
3.46k
}
131
132
/************************************************************************/
133
/*                     BuildGeometryColumnGDBv10()                      */
134
/************************************************************************/
135
136
int OGROpenFileGDBLayer::BuildGeometryColumnGDBv10(
137
    const std::string &osParentDefinition)
138
7.32k
{
139
7.32k
    CPLXMLNode *psTree = CPLParseXMLString(m_osDefinition.c_str());
140
7.32k
    if (psTree == nullptr)
141
343
    {
142
343
        m_osDefinition = "";
143
343
        return FALSE;
144
343
    }
145
146
6.98k
    CPLStripXMLNamespace(psTree, nullptr, TRUE);
147
    /* CPLSerializeXMLTreeToFile( psTree, "/dev/stderr" ); */
148
6.98k
    const CPLXMLNode *psInfo = CPLSearchXMLNode(psTree, "=DEFeatureClassInfo");
149
6.98k
    if (psInfo == nullptr)
150
2.54k
        psInfo = CPLSearchXMLNode(psTree, "=DETableInfo");
151
6.98k
    if (psInfo == nullptr)
152
9
    {
153
9
        m_osDefinition = "";
154
9
        CPLDestroyXMLNode(psTree);
155
9
        return FALSE;
156
9
    }
157
158
6.97k
    const char *pszAliasName = CPLGetXMLValue(psInfo, "AliasName", nullptr);
159
6.97k
    if (pszAliasName && strcmp(pszAliasName, GetDescription()) != 0)
160
0
    {
161
0
        SetMetadataItem("ALIAS_NAME", pszAliasName);
162
0
    }
163
164
6.97k
    m_bTimeInUTC = CPLTestBool(CPLGetXMLValue(psInfo, "IsTimeInUTC", "false"));
165
166
    /* We cannot trust the XML definition to build the field definitions. */
167
    /* It sometimes misses a few fields ! */
168
169
6.97k
    const bool bHasZ = CPLTestBool(CPLGetXMLValue(psInfo, "HasZ", "NO"));
170
6.97k
    const bool bHasM = CPLTestBool(CPLGetXMLValue(psInfo, "HasM", "NO"));
171
6.97k
    const char *pszShapeType = CPLGetXMLValue(psInfo, "ShapeType", nullptr);
172
6.97k
    const char *pszShapeFieldName =
173
6.97k
        CPLGetXMLValue(psInfo, "ShapeFieldName", nullptr);
174
6.97k
    if (pszShapeType != nullptr && pszShapeFieldName != nullptr)
175
4.41k
    {
176
4.41k
        m_eGeomType =
177
4.41k
            FileGDBOGRGeometryConverter::GetGeometryTypeFromESRI(pszShapeType);
178
179
4.41k
        if (EQUAL(pszShapeType, "esriGeometryMultiPatch"))
180
733
        {
181
733
            if (m_poLyrTable == nullptr)
182
733
            {
183
733
                m_poLyrTable = new FileGDBTable();
184
733
                if (!(m_poLyrTable->Open(m_osGDBFilename, m_bEditable,
185
733
                                         GetDescription())))
186
36
                {
187
36
                    Close();
188
36
                }
189
733
            }
190
733
            if (m_poLyrTable != nullptr)
191
697
            {
192
697
                m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
193
697
                if (m_iGeomFieldIdx >= 0)
194
697
                {
195
697
                    FileGDBGeomField *poGDBGeomField =
196
697
                        reinterpret_cast<FileGDBGeomField *>(
197
697
                            m_poLyrTable->GetField(m_iGeomFieldIdx));
198
697
                    m_poGeomConverter.reset(
199
697
                        FileGDBOGRGeometryConverter::BuildConverter(
200
697
                            poGDBGeomField));
201
697
                    TryToDetectMultiPatchKind();
202
697
                }
203
697
            }
204
733
        }
205
206
4.41k
        if (bHasZ)
207
1.99k
            m_eGeomType = wkbSetZ(m_eGeomType);
208
4.41k
        if (bHasM)
209
1.35k
            m_eGeomType = wkbSetM(m_eGeomType);
210
211
4.41k
        auto poGeomFieldDefn = std::make_unique<OGROpenFileGDBGeomFieldDefn>(
212
4.41k
            nullptr, pszShapeFieldName, m_eGeomType);
213
214
4.41k
        const CPLXMLNode *psGPFieldInfoExs =
215
4.41k
            CPLGetXMLNode(psInfo, "GPFieldInfoExs");
216
4.41k
        if (psGPFieldInfoExs)
217
4.12k
        {
218
10.0k
            for (const CPLXMLNode *psChild = psGPFieldInfoExs->psChild; psChild;
219
5.93k
                 psChild = psChild->psNext)
220
9.74k
            {
221
9.74k
                if (psChild->eType != CXT_Element)
222
4.26k
                    continue;
223
5.48k
                if (EQUAL(psChild->pszValue, "GPFieldInfoEx") &&
224
5.45k
                    EQUAL(CPLGetXMLValue(psChild, "Name", ""),
225
5.48k
                          pszShapeFieldName))
226
3.81k
                {
227
3.81k
                    poGeomFieldDefn->SetNullable(CPLTestBool(
228
3.81k
                        CPLGetXMLValue(psChild, "IsNullable", "TRUE")));
229
3.81k
                    break;
230
3.81k
                }
231
5.48k
            }
232
4.12k
        }
233
234
4.41k
        const CPLXMLNode *psSpatialReference =
235
4.41k
            CPLGetXMLNode(psInfo, "SpatialReference");
236
4.41k
        if (psSpatialReference)
237
4.41k
        {
238
4.41k
            poGeomFieldDefn->SetCoordinatePrecision(
239
4.41k
                GDBGridSettingsToOGR(psSpatialReference));
240
4.41k
        }
241
242
4.41k
        OGRSpatialReferenceRefCountedPtr poParentSRS;
243
4.41k
        if (!osParentDefinition.empty())
244
0
        {
245
0
            CPLXMLNode *psParentTree =
246
0
                CPLParseXMLString(osParentDefinition.c_str());
247
0
            if (psParentTree != nullptr)
248
0
            {
249
0
                CPLStripXMLNamespace(psParentTree, nullptr, TRUE);
250
0
                CPLXMLNode *psParentInfo =
251
0
                    CPLSearchXMLNode(psParentTree, "=DEFeatureDataset");
252
0
                if (psParentInfo != nullptr)
253
0
                {
254
0
                    poParentSRS = m_poDS->BuildSRS(psParentInfo);
255
0
                }
256
0
                CPLDestroyXMLNode(psParentTree);
257
0
            }
258
0
            if (poParentSRS == nullptr)
259
0
            {
260
0
                CPLDebug("OpenFileGDB", "Cannot get SRS from feature dataset");
261
0
            }
262
0
        }
263
264
4.41k
        auto poSRS = m_poDS->BuildSRS(psInfo);
265
4.41k
        if (poParentSRS)
266
0
        {
267
0
            if (poSRS)
268
0
            {
269
0
                if (!poSRS->IsSame(poParentSRS.get()))
270
0
                {
271
                    // Not sure this situation is really valid (seems more a
272
                    // bug of the editing software), but happens with
273
                    // https://github.com/OSGeo/gdal/issues/5747
274
                    // In the situation of
275
                    // https://github.com/OSGeo/gdal/issues/5747, the SRS inside
276
                    // the .gdbtable is consistent with the XML definition of
277
                    // the feature dataset, so it seems that the XML
278
                    // definition of the feature table lacked an update.
279
0
                    CPLDebug(
280
0
                        "OpenFileGDB",
281
0
                        "Table %s declare a CRS '%s' in its XML definition, "
282
0
                        "but its feature dataset declares '%s'. "
283
0
                        "Using the later",
284
0
                        GetDescription(), poSRS->GetName(),
285
0
                        poParentSRS->GetName());
286
0
                }
287
0
            }
288
            // Always use the SRS of the feature dataset
289
0
            poSRS = std::move(poParentSRS);
290
0
        }
291
4.41k
        if (poSRS != nullptr)
292
2.18k
        {
293
2.18k
            poGeomFieldDefn->SetSpatialRef(std::move(poSRS));
294
2.18k
        }
295
4.41k
        m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
296
4.41k
    }
297
2.55k
    else
298
2.55k
    {
299
2.55k
        m_eGeomType = wkbNone;
300
2.55k
    }
301
6.97k
    CPLDestroyXMLNode(psTree);
302
303
6.97k
    return TRUE;
304
6.98k
}
305
306
/************************************************************************/
307
/*                     TryToDetectMultiPatchKind()                      */
308
/************************************************************************/
309
310
// If the first and last feature have the same geometry type, then use
311
// it for the whole layer.
312
void OGROpenFileGDBLayer::TryToDetectMultiPatchKind()
313
1.86k
{
314
1.86k
    CPLAssert(m_poLyrTable != nullptr);
315
1.86k
    CPLAssert(m_iGeomFieldIdx >= 0);
316
317
1.86k
    if (m_poLyrTable->GetTotalRecordCount() == 0)
318
316
        return;
319
1.54k
    const int64_t nFirstIdx = m_poLyrTable->GetAndSelectNextNonEmptyRow(0);
320
1.54k
    if (nFirstIdx < 0)
321
12
        return;
322
323
1.53k
    const OGRField *psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
324
1.53k
    if (psField == nullptr)
325
395
        return;
326
1.14k
    OGRGeometry *poGeom = m_poGeomConverter->GetAsGeometry(psField);
327
1.14k
    if (poGeom == nullptr)
328
160
        return;
329
982
    const OGRwkbGeometryType eType = poGeom->getGeometryType();
330
982
    delete poGeom;
331
332
982
    int64_t nLastIdx = m_poLyrTable->GetTotalRecordCount() - 1;
333
982
    const GUInt32 nErrorCount = CPLGetErrorCounter();
334
19.8k
    while (nLastIdx > nFirstIdx &&
335
19.6k
           m_poLyrTable->GetOffsetInTableForRow(nLastIdx) == 0 &&
336
18.9k
           nErrorCount == CPLGetErrorCounter())
337
18.8k
    {
338
18.8k
        nLastIdx--;
339
18.8k
    }
340
982
    if (nLastIdx > nFirstIdx && m_poLyrTable->SelectRow(nLastIdx))
341
744
    {
342
744
        psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
343
744
        if (psField == nullptr)
344
7
        {
345
7
            m_eGeomType = eType;
346
7
            return;
347
7
        }
348
737
        poGeom = m_poGeomConverter->GetAsGeometry(psField);
349
737
        if (poGeom == nullptr)
350
128
        {
351
128
            m_eGeomType = eType;
352
128
            return;
353
128
        }
354
609
        if (eType == poGeom->getGeometryType())
355
357
            m_eGeomType = eType;
356
609
        delete poGeom;
357
609
    }
358
982
}
359
360
/************************************************************************/
361
/*                        BuildLayerDefinition()                        */
362
/************************************************************************/
363
364
int OGROpenFileGDBLayer::BuildLayerDefinition()
365
1.27M
{
366
1.27M
    if (m_bValidLayerDefn >= 0)
367
1.26M
        return m_bValidLayerDefn;
368
369
11.9k
    if (m_poLyrTable == nullptr)
370
11.9k
    {
371
11.9k
        m_poLyrTable = new FileGDBTable();
372
11.9k
        if (!(m_poLyrTable->Open(m_osGDBFilename, m_bEditable,
373
11.9k
                                 GetDescription())))
374
3.42k
        {
375
3.42k
            if (m_bEditable)
376
0
            {
377
                // Retry in read-only mode
378
0
                m_bEditable = false;
379
0
                delete m_poLyrTable;
380
0
                m_poLyrTable = new FileGDBTable();
381
0
                if (!(m_poLyrTable->Open(m_osGDBFilename, m_bEditable,
382
0
                                         GetDescription())))
383
0
                {
384
0
                    Close();
385
0
                    return FALSE;
386
0
                }
387
0
                else
388
0
                {
389
0
                    CPLError(
390
0
                        CE_Failure, CPLE_FileIO,
391
0
                        "Cannot open %s in update mode, but only in read-only",
392
0
                        GetDescription());
393
0
                }
394
0
            }
395
3.42k
            else
396
3.42k
            {
397
3.42k
                Close();
398
3.42k
                return FALSE;
399
3.42k
            }
400
3.42k
        }
401
11.9k
    }
402
403
8.54k
    m_bValidLayerDefn = TRUE;
404
8.54k
    auto oTemporaryUnsealer(m_poFeatureDefn->GetTemporaryUnsealer());
405
406
8.54k
    m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
407
8.54k
    if (m_iGeomFieldIdx >= 0)
408
6.62k
    {
409
6.62k
        FileGDBGeomField *poGDBGeomField = cpl::down_cast<FileGDBGeomField *>(
410
6.62k
            m_poLyrTable->GetField(m_iGeomFieldIdx));
411
6.62k
        m_poGeomConverter.reset(
412
6.62k
            FileGDBOGRGeometryConverter::BuildConverter(poGDBGeomField));
413
414
#ifdef DEBUG
415
        const auto poSRS = GetSpatialRef();
416
        if (poSRS != nullptr && !poGDBGeomField->GetWKT().empty() &&
417
            poGDBGeomField->GetWKT()[0] != '{')
418
        {
419
            auto poSRSFromGDBTable =
420
                m_poDS->BuildSRS(poGDBGeomField->GetWKT().c_str());
421
            if (poSRSFromGDBTable)
422
            {
423
                if (!poSRS->IsSame(poSRSFromGDBTable.get()))
424
                {
425
                    CPLDebug("OpenFileGDB",
426
                             "Table %s declare a CRS '%s' in its XML "
427
                             "definition (or in its parent's one), "
428
                             "but its .gdbtable declares '%s'. "
429
                             "Using the former",
430
                             GetDescription(), poSRS->GetName(),
431
                             poSRSFromGDBTable->GetName());
432
                }
433
            }
434
        }
435
#endif
436
437
6.62k
        if (!(m_poLyrTable->CanUseIndices() &&
438
6.62k
              m_poLyrTable->HasSpatialIndex() &&
439
361
              CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX",
440
361
                                             "YES"))) &&
441
6.26k
            CPLTestBool(CPLGetConfigOption("OPENFILEGDB_IN_MEMORY_SPI", "YES")))
442
6.26k
        {
443
6.26k
            CPLRectObj sGlobalBounds;
444
6.26k
            sGlobalBounds.minx = poGDBGeomField->GetXMin();
445
6.26k
            sGlobalBounds.miny = poGDBGeomField->GetYMin();
446
6.26k
            sGlobalBounds.maxx = poGDBGeomField->GetXMax();
447
6.26k
            sGlobalBounds.maxy = poGDBGeomField->GetYMax();
448
6.26k
            m_pQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
449
6.26k
            CPLQuadTreeSetMaxDepth(
450
6.26k
                m_pQuadTree,
451
6.26k
                CPLQuadTreeGetAdvisedMaxDepth(
452
6.26k
                    static_cast<int>(std::min<int64_t>(
453
6.26k
                        INT_MAX, m_poLyrTable->GetValidRecordCount()))));
454
6.26k
        }
455
361
        else
456
361
        {
457
361
            m_eSpatialIndexState = SPI_INVALID;
458
361
        }
459
6.62k
    }
460
461
8.54k
    if (m_iGeomFieldIdx >= 0 &&
462
6.62k
        (m_osDefinition.empty() ||
463
292
         m_poFeatureDefn->OGRFeatureDefn::GetGeomFieldCount() == 0))
464
6.35k
    {
465
        /* FileGDB v9 case */
466
6.35k
        FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
467
6.35k
            m_poLyrTable->GetField(m_iGeomFieldIdx));
468
6.35k
        const char *pszName = poGDBGeomField->GetName().c_str();
469
6.35k
        const FileGDBTableGeometryType eGDBGeomType =
470
6.35k
            m_poLyrTable->GetGeometryType();
471
472
6.35k
        OGRwkbGeometryType eGeomType = wkbUnknown;
473
6.35k
        switch (eGDBGeomType)
474
6.35k
        {
475
474
            case FGTGT_NONE: /* doesn't make sense ! */
476
474
                break;
477
345
            case FGTGT_POINT:
478
345
                eGeomType = wkbPoint;
479
345
                break;
480
186
            case FGTGT_MULTIPOINT:
481
186
                eGeomType = wkbMultiPoint;
482
186
                break;
483
880
            case FGTGT_LINE:
484
880
                eGeomType = wkbMultiLineString;
485
880
                break;
486
3.30k
            case FGTGT_POLYGON:
487
3.30k
                eGeomType = wkbMultiPolygon;
488
3.30k
                break;
489
1.16k
            case FGTGT_MULTIPATCH:
490
1.16k
                eGeomType = wkbUnknown;
491
1.16k
                break;
492
6.35k
        }
493
494
6.35k
        if (m_eGeomType != wkbUnknown &&
495
79
            wkbFlatten(eGeomType) != wkbFlatten(m_eGeomType))
496
52
        {
497
52
            CPLError(CE_Warning, CPLE_AppDefined,
498
52
                     "Inconsistency for layer geometry type");
499
52
        }
500
501
6.35k
        m_eGeomType = eGeomType;
502
503
6.35k
        if (eGDBGeomType == FGTGT_MULTIPATCH)
504
1.16k
        {
505
1.16k
            TryToDetectMultiPatchKind();
506
1.16k
        }
507
508
6.35k
        if (m_poLyrTable->GetGeomTypeHasZ())
509
2.12k
            m_eGeomType = wkbSetZ(m_eGeomType);
510
511
6.35k
        if (m_poLyrTable->GetGeomTypeHasM())
512
2.09k
            m_eGeomType = wkbSetM(m_eGeomType);
513
514
6.35k
        {
515
6.35k
            auto poGeomFieldDefn =
516
6.35k
                std::make_unique<OGROpenFileGDBGeomFieldDefn>(nullptr, pszName,
517
6.35k
                                                              m_eGeomType);
518
6.35k
            poGeomFieldDefn->SetNullable(poGDBGeomField->IsNullable());
519
520
6.35k
            m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
521
6.35k
        }
522
6.35k
        auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(0);
523
524
6.35k
        OGRSpatialReferenceRefCountedPtr poSRS;
525
6.35k
        if (!poGDBGeomField->GetWKT().empty() &&
526
6.34k
            poGDBGeomField->GetWKT()[0] != '{')
527
6.31k
        {
528
6.31k
            poSRS = m_poDS->BuildSRS(poGDBGeomField->GetWKT().c_str());
529
6.31k
        }
530
6.35k
        if (poSRS != nullptr)
531
3.09k
        {
532
3.09k
            poGeomFieldDefn->SetSpatialRef(poSRS.get());
533
3.09k
        }
534
6.35k
    }
535
2.18k
    else if (m_osDefinition.empty() && m_iGeomFieldIdx < 0)
536
1.86k
    {
537
1.86k
        m_eGeomType = wkbNone;
538
1.86k
    }
539
540
8.54k
    CPLXMLTreeCloser oTree(nullptr);
541
8.54k
    const CPLXMLNode *psGPFieldInfoExs = nullptr;
542
543
8.54k
    std::string osAreaFieldName;
544
8.54k
    std::string osLengthFieldName;
545
8.54k
    if (!m_osDefinition.empty())
546
351
    {
547
351
        oTree.reset(CPLParseXMLString(m_osDefinition.c_str()));
548
351
        if (oTree != nullptr)
549
351
        {
550
351
            CPLStripXMLNamespace(oTree.get(), nullptr, TRUE);
551
351
            CPLXMLNode *psInfo =
552
351
                CPLSearchXMLNode(oTree.get(), "=DEFeatureClassInfo");
553
351
            if (psInfo == nullptr)
554
39
                psInfo = CPLSearchXMLNode(oTree.get(), "=DETableInfo");
555
351
            if (psInfo != nullptr)
556
351
            {
557
351
                psGPFieldInfoExs = CPLGetXMLNode(psInfo, "GPFieldInfoExs");
558
351
                osAreaFieldName = CPLGetXMLValue(psInfo, "AreaFieldName", "");
559
351
                osLengthFieldName =
560
351
                    CPLGetXMLValue(psInfo, "LengthFieldName", "");
561
351
                m_osPath = CPLGetXMLValue(psInfo, "CatalogPath", "");
562
351
            }
563
351
        }
564
351
    }
565
566
56.2k
    for (int i = 0; i < m_poLyrTable->GetFieldCount(); i++)
567
47.7k
    {
568
47.7k
        if (i == m_iGeomFieldIdx)
569
6.62k
            continue;
570
41.0k
        if (i == m_poLyrTable->GetObjectIdFieldIdx())
571
8.31k
            continue;
572
573
32.7k
        FileGDBField *poGDBField = m_poLyrTable->GetField(i);
574
32.7k
        OGRFieldType eType = OFTString;
575
32.7k
        OGRFieldSubType eSubType = OFSTNone;
576
32.7k
        int nWidth = poGDBField->GetMaxWidth();
577
32.7k
        switch (poGDBField->GetType())
578
32.7k
        {
579
2.48k
            case FGFT_INT16:
580
2.48k
                eType = OFTInteger;
581
2.48k
                eSubType = OFSTInt16;
582
2.48k
                break;
583
7.06k
            case FGFT_INT32:
584
7.06k
                eType = OFTInteger;
585
7.06k
                break;
586
1.53k
            case FGFT_FLOAT32:
587
1.53k
                eType = OFTReal;
588
1.53k
                eSubType = OFSTFloat32;
589
1.53k
                break;
590
6.67k
            case FGFT_FLOAT64:
591
6.67k
                eType = OFTReal;
592
6.67k
                break;
593
4.79k
            case FGFT_STRING:
594
                /* nWidth = poGDBField->GetMaxWidth(); */
595
4.79k
                eType = OFTString;
596
4.79k
                break;
597
1.72k
            case FGFT_GUID:
598
1.85k
            case FGFT_GLOBALID:
599
3.53k
            case FGFT_XML:
600
3.53k
                eType = OFTString;
601
3.53k
                break;
602
1.81k
            case FGFT_DATETIME:
603
1.81k
                eType = OFTDateTime;
604
1.81k
                break;
605
0
            case FGFT_UNDEFINED:
606
0
            case FGFT_OBJECTID:
607
0
            case FGFT_GEOMETRY:
608
0
                CPLAssert(false);
609
0
                break;
610
3.40k
            case FGFT_BINARY:
611
3.40k
            {
612
                /* Special case for v9 GDB_UserMetadata table */
613
3.40k
                if (m_iFieldToReadAsBinary < 0 &&
614
3.36k
                    poGDBField->GetName() == "Xml" &&
615
21
                    poGDBField->GetType() == FGFT_BINARY)
616
21
                {
617
21
                    m_iFieldToReadAsBinary = i;
618
21
                    eType = OFTString;
619
21
                }
620
3.38k
                else
621
3.38k
                {
622
3.38k
                    eType = OFTBinary;
623
3.38k
                }
624
3.40k
                break;
625
0
            }
626
186
            case FGFT_RASTER:
627
186
            {
628
186
                const FileGDBRasterField *rasterField =
629
186
                    cpl::down_cast<const FileGDBRasterField *>(poGDBField);
630
186
                if (rasterField->GetRasterType() ==
631
186
                    FileGDBRasterField::Type::MANAGED)
632
59
                    eType = OFTInteger;
633
127
                else if (rasterField->GetRasterType() ==
634
127
                         FileGDBRasterField::Type::EXTERNAL)
635
110
                    eType = OFTString;
636
17
                else
637
17
                    eType = OFTBinary;
638
186
                break;
639
0
            }
640
53
            case FGFT_INT64:
641
53
                m_bArcGISPro32OrLater = true;
642
53
                eType = OFTInteger64;
643
53
                break;
644
893
            case FGFT_DATE:
645
893
                m_bArcGISPro32OrLater = true;
646
893
                eType = OFTDate;
647
893
                break;
648
287
            case FGFT_TIME:
649
287
                m_bArcGISPro32OrLater = true;
650
287
                eType = OFTTime;
651
287
                break;
652
37
            case FGFT_DATETIME_WITH_OFFSET:
653
37
                m_bArcGISPro32OrLater = true;
654
37
                eType = OFTDateTime;
655
37
                break;
656
32.7k
        }
657
32.7k
        OGRFieldDefn oFieldDefn(poGDBField->GetName().c_str(), eType);
658
32.7k
        oFieldDefn.SetAlternativeName(poGDBField->GetAlias().c_str());
659
32.7k
        oFieldDefn.SetSubType(eSubType);
660
        // On creation in the FileGDB driver (GDBFieldTypeToLengthInBytes) if
661
        // string width is 0, we pick up DEFAULT_STRING_WIDTH=65536 by default
662
        // to mean unlimited string length, but we do not want to advertise
663
        // such a big number.
664
32.7k
        if (eType == OFTString &&
665
8.45k
            (nWidth < DEFAULT_STRING_WIDTH ||
666
1.65k
             CPLTestBool(CPLGetConfigOption(
667
1.65k
                 "OPENFILEGDB_REPORT_GENUINE_FIELD_WIDTH", "NO"))))
668
6.80k
        {
669
6.80k
            oFieldDefn.SetWidth(nWidth);
670
6.80k
        }
671
32.7k
        oFieldDefn.SetNullable(poGDBField->IsNullable());
672
673
32.7k
        const CPLXMLNode *psFieldDef = nullptr;
674
32.7k
        if (psGPFieldInfoExs != nullptr)
675
2.86k
        {
676
2.86k
            for (const CPLXMLNode *psChild = psGPFieldInfoExs->psChild;
677
26.3k
                 psChild != nullptr; psChild = psChild->psNext)
678
25.9k
            {
679
25.9k
                if (psChild->eType != CXT_Element)
680
2.96k
                    continue;
681
22.9k
                if (EQUAL(psChild->pszValue, "GPFieldInfoEx") &&
682
22.9k
                    EQUAL(CPLGetXMLValue(psChild, "Name", ""),
683
22.9k
                          poGDBField->GetName().c_str()))
684
2.40k
                {
685
2.40k
                    psFieldDef = psChild;
686
2.40k
                    break;
687
2.40k
                }
688
22.9k
            }
689
2.86k
        }
690
691
32.7k
        if (psFieldDef && poGDBField->GetType() == FGFT_DATETIME)
692
190
        {
693
190
            if (EQUAL(CPLGetXMLValue(psFieldDef, "HighPrecision", ""), "true"))
694
0
            {
695
0
                poGDBField->SetHighPrecision();
696
0
            }
697
190
        }
698
699
32.7k
        const OGRField *psDefault = poGDBField->GetDefault();
700
32.7k
        if (!OGR_RawField_IsUnset(psDefault) && !OGR_RawField_IsNull(psDefault))
701
555
        {
702
555
            if (eType == OFTString)
703
28
            {
704
28
                CPLString osDefault("'");
705
28
                char *pszTmp =
706
28
                    CPLEscapeString(psDefault->String, -1, CPLES_SQL);
707
28
                osDefault += pszTmp;
708
28
                CPLFree(pszTmp);
709
28
                osDefault += "'";
710
28
                oFieldDefn.SetDefault(osDefault);
711
28
            }
712
527
            else if (eType == OFTInteger || eType == OFTReal ||
713
1
                     eType == OFTInteger64)
714
526
            {
715
                // GDBs and the FileGDB SDK are not always reliable for
716
                // numeric values It often occurs that the XML definition in
717
                // a00000004.gdbtable does not match the default values (in
718
                // binary) found in the field definition section of the
719
                // .gdbtable of the layers themselves So check consistency.
720
721
526
                const char *pszDefaultValue = nullptr;
722
526
                if (psFieldDef)
723
4
                {
724
                    // From ArcGIS this is called DefaultValueNumeric
725
                    // for integer and real.
726
                    // From FileGDB API this is
727
                    // called DefaultValue xsi:type=xs:int for integer
728
                    // and DefaultValueNumeric for real ...
729
4
                    pszDefaultValue = CPLGetXMLValue(
730
4
                        psFieldDef, "DefaultValueNumeric", nullptr);
731
4
                    if (pszDefaultValue == nullptr)
732
1
                        pszDefaultValue =
733
1
                            CPLGetXMLValue(psFieldDef, "DefaultValue", nullptr);
734
                    // For ArcGIS Pro 3.2 and esriFieldTypeBigInteger, this is
735
                    // DefaultValueInteger
736
4
                    if (pszDefaultValue == nullptr)
737
1
                        pszDefaultValue = CPLGetXMLValue(
738
1
                            psFieldDef, "DefaultValueInteger", nullptr);
739
4
                }
740
526
                if (pszDefaultValue != nullptr)
741
3
                {
742
3
                    if (eType == OFTInteger)
743
3
                    {
744
3
                        if (atoi(pszDefaultValue) != psDefault->Integer)
745
2
                        {
746
2
                            CPLDebug(
747
2
                                "OpenFileGDB",
748
2
                                "For field %s, XML definition mentions %s "
749
2
                                "as default value whereas .gdbtable header "
750
2
                                "mentions %d. Using %s",
751
2
                                poGDBField->GetName().c_str(), pszDefaultValue,
752
2
                                psDefault->Integer, pszDefaultValue);
753
2
                        }
754
3
                        oFieldDefn.SetDefault(pszDefaultValue);
755
3
                    }
756
0
                    else if (eType == OFTReal)
757
0
                    {
758
0
                        if (fabs(CPLAtof(pszDefaultValue) - psDefault->Real) >
759
0
                            1e-15)
760
0
                        {
761
0
                            CPLDebug(
762
0
                                "OpenFileGDB",
763
0
                                "For field %s, XML definition "
764
0
                                "mentions %s as default value whereas "
765
0
                                ".gdbtable header mentions %.17g. Using %s",
766
0
                                poGDBField->GetName().c_str(), pszDefaultValue,
767
0
                                psDefault->Real, pszDefaultValue);
768
0
                        }
769
0
                        oFieldDefn.SetDefault(pszDefaultValue);
770
0
                    }
771
0
                    else if (eType == OFTInteger64)
772
0
                    {
773
0
                        if (CPLAtoGIntBig(pszDefaultValue) !=
774
0
                            psDefault->Integer64)
775
0
                        {
776
0
                            CPLDebug(
777
0
                                "OpenFileGDB",
778
0
                                "For field %s, XML definition mentions %s "
779
0
                                "as default value whereas .gdbtable header "
780
0
                                "mentions " CPL_FRMT_GIB ". Using %s",
781
0
                                poGDBField->GetName().c_str(), pszDefaultValue,
782
0
                                psDefault->Integer64, pszDefaultValue);
783
0
                        }
784
0
                        oFieldDefn.SetDefault(pszDefaultValue);
785
0
                    }
786
3
                }
787
526
            }
788
1
            else if (eType == OFTDateTime)
789
0
            {
790
0
                if (poGDBField->GetType() == FGFT_DATETIME_WITH_OFFSET)
791
0
                {
792
0
                    oFieldDefn.SetDefault(CPLSPrintf(
793
0
                        "'%04d/%02d/%02d %02d:%02d:%06.03f%c%02d:%02d'",
794
0
                        psDefault->Date.Year, psDefault->Date.Month,
795
0
                        psDefault->Date.Day, psDefault->Date.Hour,
796
0
                        psDefault->Date.Minute, psDefault->Date.Second,
797
0
                        psDefault->Date.TZFlag >= 100 ? '+' : '-',
798
0
                        std::abs(psDefault->Date.TZFlag - 100) / 4,
799
0
                        (std::abs(psDefault->Date.TZFlag - 100) % 4) * 15));
800
0
                }
801
0
                else
802
0
                {
803
0
                    oFieldDefn.SetDefault(CPLSPrintf(
804
0
                        "'%04d/%02d/%02d %02d:%02d:%02d'", psDefault->Date.Year,
805
0
                        psDefault->Date.Month, psDefault->Date.Day,
806
0
                        psDefault->Date.Hour, psDefault->Date.Minute,
807
0
                        static_cast<int>(psDefault->Date.Second)));
808
0
                }
809
0
            }
810
1
            else if (eType == OFTDate)
811
0
                oFieldDefn.SetDefault(
812
0
                    CPLSPrintf("'%04d/%02d/%02d'", psDefault->Date.Year,
813
0
                               psDefault->Date.Month, psDefault->Date.Day));
814
1
            else if (eType == OFTTime)
815
1
                oFieldDefn.SetDefault(
816
1
                    CPLSPrintf("'%02d:%02d:%02d'", psDefault->Date.Hour,
817
1
                               psDefault->Date.Minute,
818
1
                               static_cast<int>(psDefault->Date.Second)));
819
555
        }
820
821
32.7k
        if (psFieldDef)
822
2.40k
        {
823
2.40k
            const char *pszDomainName =
824
2.40k
                CPLGetXMLValue(psFieldDef, "DomainName", nullptr);
825
2.40k
            if (pszDomainName)
826
0
                oFieldDefn.SetDomainName(pszDomainName);
827
2.40k
        }
828
829
32.7k
        if (osAreaFieldName == poGDBField->GetName() &&
830
2.06k
            oFieldDefn.GetType() == OFTReal)
831
73
        {
832
73
            m_iAreaField = m_poFeatureDefn->GetFieldCount();
833
73
            oFieldDefn.SetDefault("FILEGEODATABASE_SHAPE_AREA");
834
73
        }
835
32.7k
        else if (osLengthFieldName == poGDBField->GetName() &&
836
1.98k
                 oFieldDefn.GetType() == OFTReal)
837
7
        {
838
7
            m_iLengthField = m_poFeatureDefn->GetFieldCount();
839
7
            oFieldDefn.SetDefault("FILEGEODATABASE_SHAPE_LENGTH");
840
7
        }
841
842
32.7k
        m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
843
32.7k
    }
844
845
8.54k
    if (m_poLyrTable->HasDeletedFeaturesListed())
846
0
    {
847
0
        OGRFieldDefn oFieldDefn("_deleted_", OFTInteger);
848
0
        m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
849
0
    }
850
851
8.54k
    return TRUE;
852
8.54k
}
853
854
/************************************************************************/
855
/*                            GetGeomType()                             */
856
/************************************************************************/
857
858
OGRwkbGeometryType OGROpenFileGDBLayer::GetGeomType() const
859
11.9k
{
860
11.9k
    if (m_eGeomType == wkbUnknown ||
861
9.19k
        m_osDefinition.empty() /* FileGDB v9 case */)
862
10.2k
    {
863
10.2k
        (void)const_cast<OGROpenFileGDBLayer *>(this)->BuildLayerDefinition();
864
10.2k
    }
865
866
11.9k
    return m_eGeomType;
867
11.9k
}
868
869
/************************************************************************/
870
/*                            GetLayerDefn()                            */
871
/************************************************************************/
872
873
const OGRFeatureDefn *OGROpenFileGDBLayer::GetLayerDefn() const
874
78.2k
{
875
78.2k
    return m_poFeatureDefn;
876
78.2k
}
877
878
/************************************************************************/
879
/*                            GetFIDColumn()                            */
880
/************************************************************************/
881
882
const char *OGROpenFileGDBLayer::GetFIDColumn() const
883
109k
{
884
109k
    if (!const_cast<OGROpenFileGDBLayer *>(this)->BuildLayerDefinition())
885
3.45k
        return "";
886
105k
    int iIdx = m_poLyrTable->GetObjectIdFieldIdx();
887
105k
    if (iIdx < 0)
888
231
        return "";
889
105k
    return m_poLyrTable->GetField(iIdx)->GetName().c_str();
890
105k
}
891
892
/************************************************************************/
893
/*                            ResetReading()                            */
894
/************************************************************************/
895
896
void OGROpenFileGDBLayer::ResetReading()
897
0
{
898
0
    if (m_iCurFeat != 0)
899
0
    {
900
0
        if (m_eSpatialIndexState == SPI_IN_BUILDING)
901
0
            m_eSpatialIndexState = SPI_INVALID;
902
0
    }
903
0
    m_bEOF = FALSE;
904
0
    m_iCurFeat = 0;
905
0
    if (m_poAttributeIterator)
906
0
        m_poAttributeIterator->Reset();
907
0
    if (m_poSpatialIndexIterator)
908
0
        m_poSpatialIndexIterator->Reset();
909
0
    if (m_poCombinedIterator)
910
0
        m_poCombinedIterator->Reset();
911
0
}
912
913
/************************************************************************/
914
/*                         ISetSpatialFilter()                          */
915
/************************************************************************/
916
917
OGRErr OGROpenFileGDBLayer::ISetSpatialFilter(int iGeomField,
918
                                              const OGRGeometry *poGeom)
919
0
{
920
0
    if (!BuildLayerDefinition())
921
0
        return OGRERR_FAILURE;
922
923
0
    OGRLayer::ISetSpatialFilter(iGeomField, poGeom);
924
925
0
    if (m_bFilterIsEnvelope)
926
0
    {
927
0
        OGREnvelope sLayerEnvelope;
928
0
        if (GetExtent(&sLayerEnvelope, FALSE) == OGRERR_NONE)
929
0
        {
930
0
            if (m_sFilterEnvelope.MinX <= sLayerEnvelope.MinX &&
931
0
                m_sFilterEnvelope.MinY <= sLayerEnvelope.MinY &&
932
0
                m_sFilterEnvelope.MaxX >= sLayerEnvelope.MaxX &&
933
0
                m_sFilterEnvelope.MaxY >= sLayerEnvelope.MaxY)
934
0
            {
935
#ifdef DEBUG
936
                CPLDebug("OpenFileGDB", "Disabling spatial filter since it "
937
                                        "contains the layer spatial extent");
938
#endif
939
0
                poGeom = nullptr;
940
0
                OGRLayer::ISetSpatialFilter(iGeomField, poGeom);
941
0
            }
942
0
        }
943
0
    }
944
945
0
    if (poGeom != nullptr)
946
0
    {
947
0
        if (m_poSpatialIndexIterator == nullptr &&
948
0
            m_poLyrTable->CanUseIndices() && m_poLyrTable->HasSpatialIndex() &&
949
0
            CPLTestBool(
950
0
                CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX", "YES")))
951
0
        {
952
0
            m_poSpatialIndexIterator = FileGDBSpatialIndexIterator::Build(
953
0
                m_poLyrTable, m_sFilterEnvelope);
954
0
        }
955
0
        else if (m_poSpatialIndexIterator != nullptr)
956
0
        {
957
0
            if (!m_poSpatialIndexIterator->SetEnvelope(m_sFilterEnvelope))
958
0
            {
959
0
                delete m_poSpatialIndexIterator;
960
0
                m_poSpatialIndexIterator = nullptr;
961
0
            }
962
0
        }
963
0
        else if (m_eSpatialIndexState == SPI_COMPLETED)
964
0
        {
965
0
            CPLRectObj aoi;
966
0
            aoi.minx = m_sFilterEnvelope.MinX;
967
0
            aoi.miny = m_sFilterEnvelope.MinY;
968
0
            aoi.maxx = m_sFilterEnvelope.MaxX;
969
0
            aoi.maxy = m_sFilterEnvelope.MaxY;
970
0
            CPLFree(m_pahFilteredFeatures);
971
0
            m_nFilteredFeatureCount = -1;
972
0
            m_pahFilteredFeatures =
973
0
                CPLQuadTreeSearch(m_pQuadTree, &aoi, &m_nFilteredFeatureCount);
974
0
            if (m_nFilteredFeatureCount >= 0)
975
0
            {
976
0
                size_t *panStart =
977
0
                    reinterpret_cast<size_t *>(m_pahFilteredFeatures);
978
0
                std::sort(panStart, panStart + m_nFilteredFeatureCount);
979
0
            }
980
0
        }
981
982
0
        m_poLyrTable->InstallFilterEnvelope(&m_sFilterEnvelope);
983
0
    }
984
0
    else
985
0
    {
986
0
        delete m_poSpatialIndexIterator;
987
0
        m_poSpatialIndexIterator = nullptr;
988
0
        CPLFree(m_pahFilteredFeatures);
989
0
        m_pahFilteredFeatures = nullptr;
990
0
        m_nFilteredFeatureCount = -1;
991
0
        m_poLyrTable->InstallFilterEnvelope(nullptr);
992
0
    }
993
994
0
    BuildCombinedIterator();
995
996
0
    return OGRERR_NONE;
997
0
}
998
999
/************************************************************************/
1000
/*                             CompValues()                             */
1001
/************************************************************************/
1002
1003
static int CompValues(const OGRFieldDefn *poFieldDefn,
1004
                      const swq_expr_node *poValue1,
1005
                      const swq_expr_node *poValue2)
1006
0
{
1007
0
    int ret = 0;
1008
0
    switch (poFieldDefn->GetType())
1009
0
    {
1010
0
        case OFTInteger:
1011
0
        {
1012
0
            int n1, n2;
1013
0
            if (poValue1->field_type == SWQ_FLOAT)
1014
0
                n1 = static_cast<int>(poValue1->float_value);
1015
0
            else
1016
0
                n1 = static_cast<int>(poValue1->int_value);
1017
0
            if (poValue2->field_type == SWQ_FLOAT)
1018
0
                n2 = static_cast<int>(poValue2->float_value);
1019
0
            else
1020
0
                n2 = static_cast<int>(poValue2->int_value);
1021
0
            if (n1 < n2)
1022
0
                ret = -1;
1023
0
            else if (n1 == n2)
1024
0
                ret = 0;
1025
0
            else
1026
0
                ret = 1;
1027
0
            break;
1028
0
        }
1029
1030
0
        case OFTReal:
1031
0
            if (poValue1->float_value < poValue2->float_value)
1032
0
                ret = -1;
1033
0
            else if (poValue1->float_value == poValue2->float_value)
1034
0
                ret = 0;
1035
0
            else
1036
0
                ret = 1;
1037
0
            break;
1038
1039
0
        case OFTString:
1040
0
            ret = strcmp(poValue1->string_value, poValue2->string_value);
1041
0
            break;
1042
1043
0
        case OFTDate:
1044
0
        case OFTTime:
1045
0
        case OFTDateTime:
1046
0
        {
1047
0
            if ((poValue1->field_type == SWQ_TIMESTAMP ||
1048
0
                 poValue1->field_type == SWQ_DATE ||
1049
0
                 poValue1->field_type == SWQ_TIME) &&
1050
0
                (poValue2->field_type == SWQ_TIMESTAMP ||
1051
0
                 poValue2->field_type == SWQ_DATE ||
1052
0
                 poValue2->field_type == SWQ_TIME))
1053
0
            {
1054
0
                ret = strcmp(poValue1->string_value, poValue2->string_value);
1055
0
            }
1056
0
            break;
1057
0
        }
1058
1059
0
        default:
1060
0
            break;
1061
0
    }
1062
0
    return ret;
1063
0
}
1064
1065
/************************************************************************/
1066
/*                    OGROpenFileGDBIsComparisonOp()                    */
1067
/************************************************************************/
1068
1069
int OGROpenFileGDBIsComparisonOp(int op)
1070
0
{
1071
0
    return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT || op == SWQ_LE ||
1072
0
            op == SWQ_GT || op == SWQ_GE);
1073
0
}
1074
1075
/************************************************************************/
1076
/*                          AreExprExclusive()                          */
1077
/************************************************************************/
1078
1079
static const struct
1080
{
1081
    swq_op op1;
1082
    swq_op op2;
1083
    int expected_comp_1;
1084
    int expected_comp_2;
1085
} asPairsOfComparisons[] = {
1086
    {SWQ_EQ, SWQ_EQ, -1, 1},   {SWQ_LT, SWQ_GT, -1, 0},
1087
    {SWQ_GT, SWQ_LT, 0, 1},    {SWQ_LT, SWQ_GE, -1, 999},
1088
    {SWQ_LE, SWQ_GE, -1, 999}, {SWQ_LE, SWQ_GT, -1, 999},
1089
    {SWQ_GE, SWQ_LE, 1, 999},  {SWQ_GE, SWQ_LT, 1, 999},
1090
    {SWQ_GT, SWQ_LE, 1, 999}};
1091
1092
static int AreExprExclusive(const OGRFeatureDefn *poFeatureDefn,
1093
                            const swq_expr_node *poNode1,
1094
                            const swq_expr_node *poNode2)
1095
0
{
1096
0
    if (poNode1->eNodeType != SNT_OPERATION)
1097
0
        return FALSE;
1098
0
    if (poNode2->eNodeType != SNT_OPERATION)
1099
0
        return FALSE;
1100
1101
0
    const size_t nPairs =
1102
0
        sizeof(asPairsOfComparisons) / sizeof(asPairsOfComparisons[0]);
1103
0
    for (size_t i = 0; i < nPairs; i++)
1104
0
    {
1105
0
        if (poNode1->nOperation == asPairsOfComparisons[i].op1 &&
1106
0
            poNode2->nOperation == asPairsOfComparisons[i].op2 &&
1107
0
            poNode1->nSubExprCount == 2 && poNode2->nSubExprCount == 2)
1108
0
        {
1109
0
            swq_expr_node *poColumn1 = poNode1->papoSubExpr[0];
1110
0
            swq_expr_node *poValue1 = poNode1->papoSubExpr[1];
1111
0
            swq_expr_node *poColumn2 = poNode2->papoSubExpr[0];
1112
0
            swq_expr_node *poValue2 = poNode2->papoSubExpr[1];
1113
0
            if (poColumn1->eNodeType == SNT_COLUMN &&
1114
0
                poValue1->eNodeType == SNT_CONSTANT &&
1115
0
                poColumn2->eNodeType == SNT_COLUMN &&
1116
0
                poValue2->eNodeType == SNT_CONSTANT &&
1117
0
                poColumn1->field_index == poColumn2->field_index &&
1118
0
                poColumn1->field_index < poFeatureDefn->GetFieldCount())
1119
0
            {
1120
0
                const OGRFieldDefn *poFieldDefn =
1121
0
                    poFeatureDefn->GetFieldDefn(poColumn1->field_index);
1122
1123
0
                const int nComp = CompValues(poFieldDefn, poValue1, poValue2);
1124
0
                return nComp == asPairsOfComparisons[i].expected_comp_1 ||
1125
0
                       nComp == asPairsOfComparisons[i].expected_comp_2;
1126
0
            }
1127
0
            return FALSE;
1128
0
        }
1129
0
    }
1130
1131
0
    if ((poNode2->nOperation == SWQ_ISNULL &&
1132
0
         OGROpenFileGDBIsComparisonOp(poNode1->nOperation) &&
1133
0
         poNode1->nSubExprCount == 2 && poNode2->nSubExprCount == 1) ||
1134
0
        (poNode1->nOperation == SWQ_ISNULL &&
1135
0
         OGROpenFileGDBIsComparisonOp(poNode2->nOperation) &&
1136
0
         poNode2->nSubExprCount == 2 && poNode1->nSubExprCount == 1))
1137
0
    {
1138
0
        swq_expr_node *poColumn1 = poNode1->papoSubExpr[0];
1139
0
        swq_expr_node *poColumn2 = poNode2->papoSubExpr[0];
1140
0
        if (poColumn1->eNodeType == SNT_COLUMN &&
1141
0
            poColumn2->eNodeType == SNT_COLUMN &&
1142
0
            poColumn1->field_index == poColumn2->field_index &&
1143
0
            poColumn1->field_index < poFeatureDefn->GetFieldCount())
1144
0
        {
1145
0
            return TRUE;
1146
0
        }
1147
0
    }
1148
1149
    /* In doubt: return FALSE */
1150
0
    return FALSE;
1151
0
}
1152
1153
/************************************************************************/
1154
/*                     FillTargetValueFromSrcExpr()                     */
1155
/************************************************************************/
1156
1157
static int FillTargetValueFromSrcExpr(const OGRFieldDefn *poFieldDefn,
1158
                                      OGRField *poTargetValue,
1159
                                      const swq_expr_node *poSrcValue)
1160
0
{
1161
0
    switch (poFieldDefn->GetType())
1162
0
    {
1163
0
        case OFTInteger:
1164
0
            if (poSrcValue->field_type == SWQ_FLOAT)
1165
0
                poTargetValue->Integer =
1166
0
                    static_cast<int>(poSrcValue->float_value);
1167
0
            else
1168
0
                poTargetValue->Integer =
1169
0
                    static_cast<int>(poSrcValue->int_value);
1170
0
            break;
1171
1172
0
        case OFTInteger64:
1173
0
            if (poSrcValue->field_type == SWQ_FLOAT)
1174
0
                poTargetValue->Integer64 =
1175
0
                    static_cast<GIntBig>(poSrcValue->float_value);
1176
0
            else
1177
0
                poTargetValue->Integer64 = poSrcValue->int_value;
1178
0
            break;
1179
1180
0
        case OFTReal:
1181
0
            poTargetValue->Real = poSrcValue->float_value;
1182
0
            break;
1183
1184
0
        case OFTString:
1185
0
            poTargetValue->String = poSrcValue->string_value;
1186
0
            break;
1187
1188
0
        case OFTDate:
1189
0
        case OFTTime:
1190
0
        case OFTDateTime:
1191
0
            if (poSrcValue->field_type == SWQ_TIMESTAMP ||
1192
0
                poSrcValue->field_type == SWQ_DATE ||
1193
0
                poSrcValue->field_type == SWQ_TIME)
1194
0
            {
1195
0
                int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMin = 0,
1196
0
                    nSec = 0;
1197
0
                if (sscanf(poSrcValue->string_value,
1198
0
                           "%04d/%02d/%02d %02d:%02d:%02d", &nYear, &nMonth,
1199
0
                           &nDay, &nHour, &nMin, &nSec) == 6 ||
1200
0
                    sscanf(poSrcValue->string_value, "%04d/%02d/%02d", &nYear,
1201
0
                           &nMonth, &nDay) == 3 ||
1202
0
                    sscanf(poSrcValue->string_value, "%02d:%02d:%02d", &nHour,
1203
0
                           &nMin, &nSec) == 3)
1204
0
                {
1205
0
                    poTargetValue->Date.Year = static_cast<GInt16>(nYear);
1206
0
                    poTargetValue->Date.Month = static_cast<GByte>(nMonth);
1207
0
                    poTargetValue->Date.Day = static_cast<GByte>(nDay);
1208
0
                    poTargetValue->Date.Hour = static_cast<GByte>(nHour);
1209
0
                    poTargetValue->Date.Minute = static_cast<GByte>(nMin);
1210
0
                    poTargetValue->Date.Second = static_cast<GByte>(nSec);
1211
0
                    poTargetValue->Date.TZFlag = 0;
1212
0
                    poTargetValue->Date.Reserved = 0;
1213
0
                }
1214
0
                else
1215
0
                    return FALSE;
1216
0
            }
1217
0
            else
1218
0
                return FALSE;
1219
0
            break;
1220
1221
0
        default:
1222
0
            return FALSE;
1223
0
    }
1224
0
    return TRUE;
1225
0
}
1226
1227
/************************************************************************/
1228
/*                          GetColumnSubNode()                          */
1229
/************************************************************************/
1230
1231
static swq_expr_node *GetColumnSubNode(swq_expr_node *poNode)
1232
0
{
1233
0
    if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
1234
0
    {
1235
0
        if (poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
1236
0
            return poNode->papoSubExpr[0];
1237
0
        if (poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN)
1238
0
            return poNode->papoSubExpr[1];
1239
0
    }
1240
0
    return nullptr;
1241
0
}
1242
1243
/************************************************************************/
1244
/*                         GetConstantSubNode()                         */
1245
/************************************************************************/
1246
1247
static swq_expr_node *GetConstantSubNode(swq_expr_node *poNode)
1248
0
{
1249
0
    if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
1250
0
    {
1251
0
        if (poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
1252
0
            return poNode->papoSubExpr[1];
1253
0
        if (poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT)
1254
0
            return poNode->papoSubExpr[0];
1255
0
    }
1256
0
    return nullptr;
1257
0
}
1258
1259
/************************************************************************/
1260
/*                     BuildIteratorFromExprNode()                      */
1261
/************************************************************************/
1262
1263
FileGDBIterator *
1264
OGROpenFileGDBLayer::BuildIteratorFromExprNode(swq_expr_node *poNode)
1265
0
{
1266
0
    if (m_bIteratorSufficientToEvaluateFilter == FALSE)
1267
0
        return nullptr;
1268
1269
0
    if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
1270
0
        poNode->nSubExprCount == 2)
1271
0
    {
1272
        // Even if there is only one branch of the 2 that results to an
1273
        // iterator, it is useful. Of course, the iterator will not be
1274
        // sufficient to evaluatethe filter, but it will be a super-set of the
1275
        // features
1276
0
        FileGDBIterator *poIter1 =
1277
0
            BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1278
1279
        /* In case the first branch didn't result to an iterator, temporarily */
1280
        /* restore the flag */
1281
0
        const bool bSaveIteratorSufficientToEvaluateFilter =
1282
0
            CPL_TO_BOOL(m_bIteratorSufficientToEvaluateFilter);
1283
0
        m_bIteratorSufficientToEvaluateFilter = -1;
1284
0
        FileGDBIterator *poIter2 =
1285
0
            BuildIteratorFromExprNode(poNode->papoSubExpr[1]);
1286
0
        m_bIteratorSufficientToEvaluateFilter =
1287
0
            bSaveIteratorSufficientToEvaluateFilter;
1288
1289
0
        if (poIter1 != nullptr && poIter2 != nullptr)
1290
0
            return FileGDBIterator::BuildAnd(poIter1, poIter2, true);
1291
0
        m_bIteratorSufficientToEvaluateFilter = FALSE;
1292
0
        if (poIter1 != nullptr)
1293
0
            return poIter1;
1294
0
        if (poIter2 != nullptr)
1295
0
            return poIter2;
1296
0
    }
1297
1298
0
    else if (poNode->eNodeType == SNT_OPERATION &&
1299
0
             poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
1300
0
    {
1301
        /* For a OR, we need an iterator for the 2 branches */
1302
0
        FileGDBIterator *poIter1 =
1303
0
            BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1304
0
        if (poIter1 != nullptr)
1305
0
        {
1306
0
            FileGDBIterator *poIter2 =
1307
0
                BuildIteratorFromExprNode(poNode->papoSubExpr[1]);
1308
0
            if (poIter2 == nullptr)
1309
0
            {
1310
0
                delete poIter1;
1311
0
            }
1312
0
            else
1313
0
            {
1314
0
                return FileGDBIterator::BuildOr(
1315
0
                    poIter1, poIter2,
1316
0
                    AreExprExclusive(GetLayerDefn(), poNode->papoSubExpr[0],
1317
0
                                     poNode->papoSubExpr[1]));
1318
0
            }
1319
0
        }
1320
0
    }
1321
1322
0
    else if (poNode->eNodeType == SNT_OPERATION &&
1323
0
             (OGROpenFileGDBIsComparisonOp(poNode->nOperation) ||
1324
0
              poNode->nOperation == SWQ_ILIKE) &&
1325
0
             poNode->nSubExprCount == 2)
1326
0
    {
1327
0
        swq_expr_node *poColumn = GetColumnSubNode(poNode);
1328
0
        swq_expr_node *poValue = GetConstantSubNode(poNode);
1329
0
        if (poColumn != nullptr && poValue != nullptr &&
1330
0
            poColumn->field_index < GetLayerDefn()->GetFieldCount())
1331
0
        {
1332
0
            const OGRFieldDefn *poFieldDefn =
1333
0
                GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1334
1335
0
            int nTableColIdx =
1336
0
                m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1337
0
            if (nTableColIdx >= 0 &&
1338
0
                m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1339
0
            {
1340
0
                OGRField sValue;
1341
1342
0
                if (FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue))
1343
0
                {
1344
0
                    FileGDBSQLOp eOp = FGSO_EQ;
1345
0
                    CPL_IGNORE_RET_VAL(eOp);
1346
0
                    if (poColumn == poNode->papoSubExpr[0])
1347
0
                    {
1348
0
                        switch (poNode->nOperation)
1349
0
                        {
1350
0
                            case SWQ_LE:
1351
0
                                eOp = FGSO_LE;
1352
0
                                break;
1353
0
                            case SWQ_LT:
1354
0
                                eOp = FGSO_LT;
1355
0
                                break;
1356
0
                            case SWQ_NE:
1357
0
                                eOp = FGSO_EQ; /* yes : EQ */
1358
0
                                break;
1359
0
                            case SWQ_EQ:
1360
0
                                eOp = FGSO_EQ;
1361
0
                                break;
1362
0
                            case SWQ_GE:
1363
0
                                eOp = FGSO_GE;
1364
0
                                break;
1365
0
                            case SWQ_GT:
1366
0
                                eOp = FGSO_GT;
1367
0
                                break;
1368
0
                            case SWQ_ILIKE:
1369
0
                                eOp = FGSO_ILIKE;
1370
0
                                break;
1371
0
                            default:
1372
0
                                CPLAssert(false);
1373
0
                                break;
1374
0
                        }
1375
0
                    }
1376
0
                    else
1377
0
                    {
1378
                        /* If "constant op column", then we must reverse */
1379
                        /* the operator */
1380
0
                        switch (poNode->nOperation)
1381
0
                        {
1382
0
                            case SWQ_LE:
1383
0
                                eOp = FGSO_GE;
1384
0
                                break;
1385
0
                            case SWQ_LT:
1386
0
                                eOp = FGSO_GT;
1387
0
                                break;
1388
0
                            case SWQ_NE:
1389
0
                                eOp = FGSO_EQ; /* yes : EQ */
1390
0
                                break;
1391
0
                            case SWQ_EQ:
1392
0
                                eOp = FGSO_EQ;
1393
0
                                break;
1394
0
                            case SWQ_GE:
1395
0
                                eOp = FGSO_LE;
1396
0
                                break;
1397
0
                            case SWQ_GT:
1398
0
                                eOp = FGSO_LT;
1399
0
                                break;
1400
0
                            case SWQ_ILIKE:
1401
0
                                eOp = FGSO_ILIKE;
1402
0
                                break;
1403
0
                            default:
1404
0
                                CPLAssert(false);
1405
0
                                break;
1406
0
                        }
1407
0
                    }
1408
1409
0
                    bool bIteratorSufficient = true;
1410
0
                    auto poField = m_poLyrTable->GetField(nTableColIdx);
1411
0
                    std::string osTruncatedStr;  // keep it in this scope !
1412
0
                    if (poField->GetType() == FGFT_STRING &&
1413
0
                        poFieldDefn->GetType() == OFTString)
1414
0
                    {
1415
                        // If we have an equality comparison, but the index
1416
                        // uses LOWER(), transform it to a ILIKE comparison
1417
0
                        if (eOp == FGSO_EQ && poField->HasIndex() &&
1418
0
                            STARTS_WITH_CI(
1419
0
                                poField->GetIndex()->GetExpression().c_str(),
1420
0
                                "LOWER("))
1421
0
                        {
1422
                            // Note: FileGDBIndexIterator::SetConstraint()
1423
                            // checks that the string to compare with has no
1424
                            // wildcard
1425
0
                            eOp = FGSO_ILIKE;
1426
1427
                            // In theory, a ILIKE is not sufficient as it is
1428
                            // case insensitive, whereas one could expect
1429
                            // equality testing to be case sensitive... but
1430
                            // it is not in OGR SQL...
1431
                            // So we can comment the below line
1432
                            // bIteratorSufficient = false;
1433
0
                        }
1434
1435
                        // As the index use ' ' as padding value, we cannot
1436
                        // fully trust the index.
1437
0
                        else if ((eOp == FGSO_EQ &&
1438
0
                                  poNode->nOperation != SWQ_NE) ||
1439
0
                                 eOp == FGSO_GE)
1440
0
                            bIteratorSufficient = false;
1441
0
                        else
1442
0
                            return nullptr;
1443
1444
0
                        const int nMaxWidthIndexedStr =
1445
0
                            poField->GetIndex()->GetMaxWidthInBytes(
1446
0
                                m_poLyrTable);
1447
0
                        if (nMaxWidthIndexedStr > 0)
1448
0
                        {
1449
0
                            wchar_t *pWide = CPLRecodeToWChar(
1450
0
                                sValue.String, CPL_ENC_UTF8, CPL_ENC_UCS2);
1451
0
                            if (pWide)
1452
0
                            {
1453
0
                                const size_t nUCS2Len = wcslen(pWide);
1454
0
                                if (nUCS2Len * sizeof(uint16_t) >
1455
0
                                    static_cast<size_t>(nMaxWidthIndexedStr))
1456
0
                                {
1457
0
                                    pWide[nMaxWidthIndexedStr /
1458
0
                                          sizeof(uint16_t)] = 0;
1459
0
                                    char *pszTruncated = CPLRecodeFromWChar(
1460
0
                                        pWide, CPL_ENC_UCS2, CPL_ENC_UTF8);
1461
0
                                    if (pszTruncated)
1462
0
                                    {
1463
0
                                        osTruncatedStr = pszTruncated;
1464
0
                                        sValue.String = &osTruncatedStr[0];
1465
0
                                        CPLFree(pszTruncated);
1466
0
                                    }
1467
0
                                }
1468
0
                                CPLFree(pWide);
1469
0
                            }
1470
0
                        }
1471
0
                    }
1472
0
                    else if (eOp == FGSO_ILIKE)
1473
0
                        return nullptr;
1474
1475
0
                    FileGDBIterator *poIter = FileGDBIterator::Build(
1476
0
                        m_poLyrTable, nTableColIdx, TRUE, eOp,
1477
0
                        poFieldDefn->GetType(), &sValue);
1478
0
                    if (poIter != nullptr)
1479
0
                        m_bIteratorSufficientToEvaluateFilter =
1480
0
                            bIteratorSufficient;
1481
0
                    if (poIter && poNode->nOperation == SWQ_NE)
1482
0
                        return FileGDBIterator::BuildNot(poIter);
1483
0
                    else
1484
0
                        return poIter;
1485
0
                }
1486
0
            }
1487
0
        }
1488
0
    }
1489
0
    else if (poNode->eNodeType == SNT_OPERATION &&
1490
0
             poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1)
1491
0
    {
1492
0
        swq_expr_node *poColumn = poNode->papoSubExpr[0];
1493
0
        if (poColumn->eNodeType == SNT_COLUMN &&
1494
0
            poColumn->field_index < GetLayerDefn()->GetFieldCount())
1495
0
        {
1496
0
            const OGRFieldDefn *poFieldDefn =
1497
0
                GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1498
1499
0
            int nTableColIdx =
1500
0
                m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1501
0
            if (nTableColIdx >= 0 &&
1502
0
                m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1503
0
            {
1504
0
                FileGDBIterator *poIter = FileGDBIterator::BuildIsNotNull(
1505
0
                    m_poLyrTable, nTableColIdx, TRUE);
1506
0
                if (poIter)
1507
0
                {
1508
0
                    m_bIteratorSufficientToEvaluateFilter = TRUE;
1509
0
                    poIter = FileGDBIterator::BuildNot(poIter);
1510
0
                }
1511
0
                return poIter;
1512
0
            }
1513
0
        }
1514
0
    }
1515
0
    else if (poNode->eNodeType == SNT_OPERATION &&
1516
0
             poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 &&
1517
0
             poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
1518
0
             poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
1519
0
             poNode->papoSubExpr[0]->nSubExprCount == 1)
1520
0
    {
1521
0
        swq_expr_node *poColumn = poNode->papoSubExpr[0]->papoSubExpr[0];
1522
0
        if (poColumn->eNodeType == SNT_COLUMN &&
1523
0
            poColumn->field_index < GetLayerDefn()->GetFieldCount())
1524
0
        {
1525
0
            const OGRFieldDefn *poFieldDefn =
1526
0
                GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1527
1528
0
            int nTableColIdx =
1529
0
                m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1530
0
            if (nTableColIdx >= 0 &&
1531
0
                m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1532
0
            {
1533
0
                FileGDBIterator *poIter = FileGDBIterator::BuildIsNotNull(
1534
0
                    m_poLyrTable, nTableColIdx, TRUE);
1535
0
                if (poIter)
1536
0
                    m_bIteratorSufficientToEvaluateFilter = TRUE;
1537
0
                return poIter;
1538
0
            }
1539
0
        }
1540
0
    }
1541
0
    else if (poNode->eNodeType == SNT_OPERATION &&
1542
0
             poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2)
1543
0
    {
1544
0
        swq_expr_node *poColumn = poNode->papoSubExpr[0];
1545
0
        if (poColumn->eNodeType == SNT_COLUMN &&
1546
0
            poColumn->field_index < GetLayerDefn()->GetFieldCount())
1547
0
        {
1548
0
            bool bAllConstants = true;
1549
0
            for (int i = 1; i < poNode->nSubExprCount; i++)
1550
0
            {
1551
0
                if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT)
1552
0
                    bAllConstants = false;
1553
0
            }
1554
0
            const OGRFieldDefn *poFieldDefn =
1555
0
                GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1556
1557
0
            int nTableColIdx =
1558
0
                m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1559
0
            if (bAllConstants && nTableColIdx >= 0 &&
1560
0
                m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1561
0
            {
1562
0
                FileGDBIterator *poRet = nullptr;
1563
1564
0
                bool bIteratorSufficient = true;
1565
0
                auto poField = m_poLyrTable->GetField(nTableColIdx);
1566
1567
0
                for (int i = 1; i < poNode->nSubExprCount; i++)
1568
0
                {
1569
0
                    OGRField sValue;
1570
0
                    if (!FillTargetValueFromSrcExpr(poFieldDefn, &sValue,
1571
0
                                                    poNode->papoSubExpr[i]))
1572
0
                    {
1573
0
                        delete poRet;
1574
0
                        poRet = nullptr;
1575
0
                        break;
1576
0
                    }
1577
1578
0
                    std::string osTruncatedStr;  // keep it in this scope !
1579
0
                    if (poField->GetType() == FGFT_STRING &&
1580
0
                        poFieldDefn->GetType() == OFTString)
1581
0
                    {
1582
0
                        const int nMaxWidthIndexedStr =
1583
0
                            poField->GetIndex()->GetMaxWidthInBytes(
1584
0
                                m_poLyrTable);
1585
0
                        if (nMaxWidthIndexedStr > 0)
1586
0
                        {
1587
0
                            wchar_t *pWide = CPLRecodeToWChar(
1588
0
                                sValue.String, CPL_ENC_UTF8, CPL_ENC_UCS2);
1589
0
                            if (pWide)
1590
0
                            {
1591
0
                                const size_t nUCS2Len = wcslen(pWide);
1592
0
                                if (nUCS2Len * sizeof(uint16_t) >
1593
0
                                    static_cast<size_t>(nMaxWidthIndexedStr))
1594
0
                                {
1595
0
                                    pWide[nMaxWidthIndexedStr /
1596
0
                                          sizeof(uint16_t)] = 0;
1597
0
                                    char *pszTruncated = CPLRecodeFromWChar(
1598
0
                                        pWide, CPL_ENC_UCS2, CPL_ENC_UTF8);
1599
0
                                    if (pszTruncated)
1600
0
                                    {
1601
0
                                        osTruncatedStr = pszTruncated;
1602
0
                                        sValue.String = &osTruncatedStr[0];
1603
0
                                        CPLFree(pszTruncated);
1604
0
                                    }
1605
0
                                }
1606
0
                                CPLFree(pWide);
1607
0
                            }
1608
0
                        }
1609
1610
                        // As the index use ' ' as padding value, we cannot
1611
                        // fully trust the index.
1612
0
                        bIteratorSufficient = false;
1613
0
                    }
1614
1615
0
                    FileGDBIterator *poIter = FileGDBIterator::Build(
1616
0
                        m_poLyrTable, nTableColIdx, TRUE, FGSO_EQ,
1617
0
                        poFieldDefn->GetType(), &sValue);
1618
0
                    if (poIter == nullptr)
1619
0
                    {
1620
0
                        delete poRet;
1621
0
                        poRet = nullptr;
1622
0
                        break;
1623
0
                    }
1624
0
                    if (poRet == nullptr)
1625
0
                        poRet = poIter;
1626
0
                    else
1627
0
                        poRet = FileGDBIterator::BuildOr(poRet, poIter);
1628
0
                }
1629
0
                if (poRet != nullptr)
1630
0
                {
1631
0
                    m_bIteratorSufficientToEvaluateFilter = bIteratorSufficient;
1632
0
                    return poRet;
1633
0
                }
1634
0
            }
1635
0
        }
1636
0
    }
1637
0
    else if (poNode->eNodeType == SNT_OPERATION &&
1638
0
             poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
1639
0
    {
1640
0
        FileGDBIterator *poIter =
1641
0
            BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1642
        /* If we have an iterator that is only partial w.r.t the full clause */
1643
        /* then we cannot do anything with it unfortunately */
1644
0
        if (m_bIteratorSufficientToEvaluateFilter == FALSE)
1645
0
        {
1646
0
            if (poIter != nullptr)
1647
0
                CPLDebug("OpenFileGDB", "Disabling use of indexes");
1648
0
            delete poIter;
1649
0
        }
1650
0
        else if (poIter != nullptr)
1651
0
        {
1652
0
            return FileGDBIterator::BuildNot(poIter);
1653
0
        }
1654
0
    }
1655
1656
0
    if (m_bIteratorSufficientToEvaluateFilter == TRUE)
1657
0
        CPLDebug("OpenFileGDB", "Disabling use of indexes");
1658
0
    m_bIteratorSufficientToEvaluateFilter = FALSE;
1659
0
    return nullptr;
1660
0
}
1661
1662
/************************************************************************/
1663
/*                         SetAttributeFilter()                         */
1664
/************************************************************************/
1665
1666
OGRErr OGROpenFileGDBLayer::SetAttributeFilter(const char *pszFilter)
1667
0
{
1668
0
    if (!BuildLayerDefinition())
1669
0
        return OGRERR_FAILURE;
1670
1671
0
    delete m_poAttributeIterator;
1672
0
    m_poAttributeIterator = nullptr;
1673
0
    delete m_poCombinedIterator;
1674
0
    m_poCombinedIterator = nullptr;
1675
0
    m_bIteratorSufficientToEvaluateFilter = FALSE;
1676
1677
0
    OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
1678
0
    if (eErr != OGRERR_NONE ||
1679
0
        !CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES")))
1680
0
        return eErr;
1681
1682
0
    if (m_poAttrQuery != nullptr && m_nFilteredFeatureCount < 0)
1683
0
    {
1684
0
        swq_expr_node *poNode =
1685
0
            static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
1686
0
        poNode->ReplaceBetweenByGEAndLERecurse();
1687
0
        m_bIteratorSufficientToEvaluateFilter = -1;
1688
0
        m_poAttributeIterator = BuildIteratorFromExprNode(poNode);
1689
0
        if (m_poAttributeIterator != nullptr &&
1690
0
            m_eSpatialIndexState == SPI_IN_BUILDING)
1691
0
            m_eSpatialIndexState = SPI_INVALID;
1692
0
        if (m_bIteratorSufficientToEvaluateFilter < 0)
1693
0
            m_bIteratorSufficientToEvaluateFilter = FALSE;
1694
0
    }
1695
1696
0
    BuildCombinedIterator();
1697
1698
0
    return eErr;
1699
0
}
1700
1701
/************************************************************************/
1702
/*                       BuildCombinedIterator()                        */
1703
/************************************************************************/
1704
1705
void OGROpenFileGDBLayer::BuildCombinedIterator()
1706
0
{
1707
0
    delete m_poCombinedIterator;
1708
0
    if (m_poAttributeIterator && m_poSpatialIndexIterator)
1709
0
    {
1710
0
        m_poCombinedIterator = FileGDBIterator::BuildAnd(
1711
0
            m_poAttributeIterator, m_poSpatialIndexIterator, false);
1712
0
    }
1713
0
    else
1714
0
    {
1715
0
        m_poCombinedIterator = nullptr;
1716
0
    }
1717
0
}
1718
1719
/************************************************************************/
1720
/*                         GetCurrentFeature()                          */
1721
/************************************************************************/
1722
1723
OGRFeature *OGROpenFileGDBLayer::GetCurrentFeature()
1724
189k
{
1725
189k
    OGRFeature *poFeature = nullptr;
1726
189k
    int iOGRIdx = 0;
1727
189k
    int64_t iRow = m_poLyrTable->GetCurRow();
1728
1.10M
    for (int iGDBIdx = 0; iGDBIdx < m_poLyrTable->GetFieldCount(); iGDBIdx++)
1729
918k
    {
1730
918k
        if (iOGRIdx == m_iFIDAsRegularColumnIndex)
1731
0
            iOGRIdx++;
1732
1733
918k
        if (iGDBIdx == m_iGeomFieldIdx)
1734
152k
        {
1735
152k
            if (m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
1736
0
            {
1737
0
                if (m_eSpatialIndexState == SPI_IN_BUILDING)
1738
0
                    m_eSpatialIndexState = SPI_INVALID;
1739
0
                continue;
1740
0
            }
1741
1742
152k
            const OGRField *psField = m_poLyrTable->GetFieldValue(iGDBIdx);
1743
152k
            if (psField != nullptr)
1744
146k
            {
1745
146k
                if (m_eSpatialIndexState == SPI_IN_BUILDING)
1746
145k
                {
1747
145k
                    OGREnvelope sFeatureEnvelope;
1748
145k
                    if (m_poLyrTable->GetFeatureExtent(psField,
1749
145k
                                                       &sFeatureEnvelope))
1750
132k
                    {
1751
#if SIZEOF_VOIDP < 8
1752
                        if (iRow > INT32_MAX)
1753
                        {
1754
                            // m_pQuadTree stores iRow values as void*
1755
                            // This would overflow here.
1756
                            m_eSpatialIndexState = SPI_INVALID;
1757
                        }
1758
                        else
1759
#endif
1760
132k
                        {
1761
132k
                            CPLRectObj sBounds;
1762
132k
                            sBounds.minx = sFeatureEnvelope.MinX;
1763
132k
                            sBounds.miny = sFeatureEnvelope.MinY;
1764
132k
                            sBounds.maxx = sFeatureEnvelope.MaxX;
1765
132k
                            sBounds.maxy = sFeatureEnvelope.MaxY;
1766
132k
                            CPLQuadTreeInsertWithBounds(
1767
132k
                                m_pQuadTree,
1768
132k
                                reinterpret_cast<void *>(
1769
132k
                                    static_cast<uintptr_t>(iRow)),
1770
132k
                                &sBounds);
1771
132k
                        }
1772
132k
                    }
1773
145k
                }
1774
1775
146k
                if (m_poFilterGeom != nullptr &&
1776
0
                    m_eSpatialIndexState != SPI_COMPLETED &&
1777
0
                    !m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(
1778
0
                        psField))
1779
0
                {
1780
0
                    delete poFeature;
1781
0
                    return nullptr;
1782
0
                }
1783
1784
146k
                OGRGeometry *poGeom = m_poGeomConverter->GetAsGeometry(psField);
1785
146k
                if (poGeom != nullptr)
1786
113k
                {
1787
113k
                    OGRwkbGeometryType eFlattenType =
1788
113k
                        wkbFlatten(poGeom->getGeometryType());
1789
113k
                    if (eFlattenType == wkbPolygon)
1790
24.2k
                        poGeom =
1791
24.2k
                            OGRGeometryFactory::forceToMultiPolygon(poGeom);
1792
88.7k
                    else if (eFlattenType == wkbCurvePolygon)
1793
10.6k
                    {
1794
10.6k
                        OGRMultiSurface *poMS = new OGRMultiSurface();
1795
10.6k
                        poMS->addGeometryDirectly(poGeom);
1796
10.6k
                        poGeom = poMS;
1797
10.6k
                    }
1798
78.0k
                    else if (eFlattenType == wkbLineString)
1799
12.6k
                        poGeom =
1800
12.6k
                            OGRGeometryFactory::forceToMultiLineString(poGeom);
1801
65.4k
                    else if (eFlattenType == wkbCompoundCurve)
1802
658
                    {
1803
658
                        OGRMultiCurve *poMC = new OGRMultiCurve();
1804
658
                        poMC->addGeometryDirectly(poGeom);
1805
658
                        poGeom = poMC;
1806
658
                    }
1807
1808
113k
                    poGeom->assignSpatialReference(
1809
113k
                        m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef());
1810
1811
113k
                    if (poFeature == nullptr)
1812
112k
                        poFeature = new OGRFeature(m_poFeatureDefn);
1813
113k
                    poFeature->SetGeometryDirectly(poGeom);
1814
113k
                }
1815
146k
            }
1816
152k
        }
1817
765k
        else if (iGDBIdx != m_poLyrTable->GetObjectIdFieldIdx())
1818
579k
        {
1819
579k
            const OGRFieldDefn *poFieldDefn =
1820
579k
                m_poFeatureDefn->GetFieldDefn(iOGRIdx);
1821
579k
            if (!poFieldDefn->IsIgnored())
1822
579k
            {
1823
579k
                const OGRField *psField = m_poLyrTable->GetFieldValue(iGDBIdx);
1824
579k
                if (poFeature == nullptr)
1825
64.2k
                    poFeature = new OGRFeature(m_poFeatureDefn);
1826
579k
                if (psField == nullptr)
1827
176k
                {
1828
176k
                    poFeature->SetFieldNull(iOGRIdx);
1829
176k
                }
1830
402k
                else
1831
402k
                {
1832
1833
402k
                    if (iGDBIdx == m_iFieldToReadAsBinary)
1834
436
                        poFeature->SetField(iOGRIdx,
1835
436
                                            reinterpret_cast<const char *>(
1836
436
                                                psField->Binary.paData));
1837
402k
                    else if (poFieldDefn->GetType() == OFTDateTime)
1838
21.8k
                    {
1839
21.8k
                        OGRField sField = *psField;
1840
21.8k
                        if (m_poLyrTable->GetField(iGDBIdx)->GetType() ==
1841
21.8k
                            FGFT_DATETIME)
1842
18.5k
                        {
1843
18.5k
                            sField.Date.TZFlag = m_bTimeInUTC ? 100 : 0;
1844
18.5k
                        }
1845
21.8k
                        poFeature->SetField(iOGRIdx, &sField);
1846
21.8k
                    }
1847
380k
                    else
1848
380k
                        poFeature->SetField(iOGRIdx, psField);
1849
402k
                }
1850
579k
            }
1851
579k
            iOGRIdx++;
1852
579k
        }
1853
918k
    }
1854
1855
189k
    if (poFeature == nullptr)
1856
12.7k
        poFeature = new OGRFeature(m_poFeatureDefn);
1857
1858
189k
    if (m_poLyrTable->HasDeletedFeaturesListed())
1859
0
    {
1860
0
        poFeature->SetField(poFeature->GetFieldCount() - 1,
1861
0
                            m_poLyrTable->IsCurRowDeleted());
1862
0
    }
1863
1864
189k
    poFeature->SetFID(iRow + 1);
1865
1866
189k
    if (m_iFIDAsRegularColumnIndex >= 0)
1867
0
        poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID());
1868
1869
189k
    return poFeature;
1870
189k
}
1871
1872
/************************************************************************/
1873
/*                           GetNextFeature()                           */
1874
/************************************************************************/
1875
1876
OGRFeature *OGROpenFileGDBLayer::GetNextFeature()
1877
201k
{
1878
201k
    if (!BuildLayerDefinition() || m_bEOF)
1879
3.45k
        return nullptr;
1880
1881
198k
    FileGDBIterator *poIterator = m_poCombinedIterator ? m_poCombinedIterator
1882
198k
                                  : m_poSpatialIndexIterator
1883
198k
                                      ? m_poSpatialIndexIterator
1884
198k
                                      : m_poAttributeIterator;
1885
1886
198k
    while (true)
1887
198k
    {
1888
198k
        OGRFeature *poFeature = nullptr;
1889
1890
198k
        if (m_nFilteredFeatureCount >= 0)
1891
0
        {
1892
0
            while (true)
1893
0
            {
1894
0
                if (m_iCurFeat >= m_nFilteredFeatureCount)
1895
0
                {
1896
0
                    return nullptr;
1897
0
                }
1898
0
                const auto iRow =
1899
0
                    static_cast<int64_t>(reinterpret_cast<GUIntptr_t>(
1900
0
                        m_pahFilteredFeatures[m_iCurFeat++]));
1901
0
                if (m_poLyrTable->SelectRow(iRow))
1902
0
                {
1903
0
                    poFeature = GetCurrentFeature();
1904
0
                    if (poFeature)
1905
0
                        break;
1906
0
                }
1907
0
                else if (m_poLyrTable->HasGotError())
1908
0
                {
1909
0
                    m_bEOF = TRUE;
1910
0
                    return nullptr;
1911
0
                }
1912
0
            }
1913
0
        }
1914
198k
        else if (poIterator != nullptr)
1915
0
        {
1916
0
            while (true)
1917
0
            {
1918
0
                const auto iRow = poIterator->GetNextRowSortedByFID();
1919
0
                if (iRow < 0)
1920
0
                    return nullptr;
1921
0
                if (m_poLyrTable->SelectRow(iRow))
1922
0
                {
1923
0
                    poFeature = GetCurrentFeature();
1924
0
                    if (poFeature)
1925
0
                        break;
1926
0
                }
1927
0
                else if (m_poLyrTable->HasGotError())
1928
0
                {
1929
0
                    m_bEOF = TRUE;
1930
0
                    return nullptr;
1931
0
                }
1932
0
            }
1933
0
        }
1934
198k
        else
1935
198k
        {
1936
198k
            while (true)
1937
198k
            {
1938
198k
                if (m_iCurFeat == m_poLyrTable->GetTotalRecordCount())
1939
7.54k
                {
1940
7.54k
                    return nullptr;
1941
7.54k
                }
1942
190k
                m_iCurFeat =
1943
190k
                    m_poLyrTable->GetAndSelectNextNonEmptyRow(m_iCurFeat);
1944
190k
                if (m_iCurFeat < 0)
1945
981
                {
1946
981
                    m_bEOF = TRUE;
1947
981
                    return nullptr;
1948
981
                }
1949
189k
                else
1950
189k
                {
1951
189k
                    m_iCurFeat++;
1952
189k
                    poFeature = GetCurrentFeature();
1953
189k
                    if (m_eSpatialIndexState == SPI_IN_BUILDING &&
1954
187k
                        m_iCurFeat == m_poLyrTable->GetTotalRecordCount())
1955
7.19k
                    {
1956
7.19k
                        CPLDebug("OpenFileGDB", "SPI_COMPLETED");
1957
7.19k
                        m_eSpatialIndexState = SPI_COMPLETED;
1958
7.19k
                    }
1959
189k
                    if (poFeature)
1960
189k
                        break;
1961
189k
                }
1962
190k
            }
1963
198k
        }
1964
1965
189k
        if ((m_poFilterGeom == nullptr ||
1966
0
             FilterGeometry(poFeature->GetGeometryRef())) &&
1967
189k
            (m_poAttrQuery == nullptr ||
1968
0
             (m_poAttributeIterator != nullptr &&
1969
0
              m_bIteratorSufficientToEvaluateFilter) ||
1970
0
             m_poAttrQuery->Evaluate(poFeature)))
1971
189k
        {
1972
189k
            return poFeature;
1973
189k
        }
1974
1975
0
        delete poFeature;
1976
0
    }
1977
198k
}
1978
1979
/************************************************************************/
1980
/*                             GetFeature()                             */
1981
/************************************************************************/
1982
1983
OGRFeature *OGROpenFileGDBLayer::GetFeature(GIntBig nFeatureId)
1984
0
{
1985
0
    if (!BuildLayerDefinition())
1986
0
        return nullptr;
1987
1988
0
    if (nFeatureId < 1 || nFeatureId > m_poLyrTable->GetTotalRecordCount())
1989
0
        return nullptr;
1990
0
    if (!m_poLyrTable->SelectRow(nFeatureId - 1))
1991
0
        return nullptr;
1992
1993
    /* Temporarily disable spatial filter */
1994
0
    OGRGeometry *poOldSpatialFilter = m_poFilterGeom;
1995
0
    m_poFilterGeom = nullptr;
1996
    /* and also spatial index state to avoid features to be inserted */
1997
    /* multiple times in spatial index */
1998
0
    SPIState eOldState = m_eSpatialIndexState;
1999
0
    m_eSpatialIndexState = SPI_INVALID;
2000
2001
0
    OGRFeature *poFeature = GetCurrentFeature();
2002
2003
    /* Set it back */
2004
0
    m_poFilterGeom = poOldSpatialFilter;
2005
0
    m_eSpatialIndexState = eOldState;
2006
2007
0
    return poFeature;
2008
0
}
2009
2010
/************************************************************************/
2011
/*                           SetNextByIndex()                           */
2012
/************************************************************************/
2013
2014
OGRErr OGROpenFileGDBLayer::SetNextByIndex(GIntBig nIndex)
2015
0
{
2016
0
    if (m_poAttributeIterator != nullptr || m_poSpatialIndexIterator != nullptr)
2017
0
        return OGRLayer::SetNextByIndex(nIndex);
2018
2019
0
    if (!BuildLayerDefinition())
2020
0
        return OGRERR_FAILURE;
2021
2022
0
    m_bEOF = false;
2023
2024
0
    if (m_eSpatialIndexState == SPI_IN_BUILDING)
2025
0
        m_eSpatialIndexState = SPI_INVALID;
2026
2027
0
    if (m_nFilteredFeatureCount >= 0)
2028
0
    {
2029
0
        if (nIndex < 0 || nIndex >= m_nFilteredFeatureCount)
2030
0
        {
2031
0
            m_bEOF = true;
2032
0
            return OGRERR_NON_EXISTING_FEATURE;
2033
0
        }
2034
0
        m_iCurFeat = nIndex;
2035
0
        return OGRERR_NONE;
2036
0
    }
2037
0
    else if (m_poLyrTable->GetValidRecordCount() ==
2038
0
             m_poLyrTable->GetTotalRecordCount())
2039
0
    {
2040
0
        if (nIndex < 0 || nIndex >= m_poLyrTable->GetValidRecordCount())
2041
0
        {
2042
0
            m_bEOF = true;
2043
0
            return OGRERR_NON_EXISTING_FEATURE;
2044
0
        }
2045
0
        m_iCurFeat = nIndex;
2046
0
        return OGRERR_NONE;
2047
0
    }
2048
0
    else
2049
0
        return OGRLayer::SetNextByIndex(nIndex);
2050
0
}
2051
2052
/************************************************************************/
2053
/*                             IGetExtent()                             */
2054
/************************************************************************/
2055
2056
OGRErr OGROpenFileGDBLayer::IGetExtent(int /* iGeomField */,
2057
                                       OGREnvelope *psExtent, bool /* bForce */)
2058
0
{
2059
0
    if (!BuildLayerDefinition())
2060
0
        return OGRERR_FAILURE;
2061
2062
0
    if (m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0)
2063
0
    {
2064
0
        FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
2065
0
            m_poLyrTable->GetField(m_iGeomFieldIdx));
2066
0
        if (!std::isnan(poGDBGeomField->GetXMin()))
2067
0
        {
2068
0
            psExtent->MinX = poGDBGeomField->GetXMin();
2069
0
            psExtent->MinY = poGDBGeomField->GetYMin();
2070
0
            psExtent->MaxX = poGDBGeomField->GetXMax();
2071
0
            psExtent->MaxY = poGDBGeomField->GetYMax();
2072
0
            return OGRERR_NONE;
2073
0
        }
2074
0
    }
2075
2076
0
    return OGRERR_FAILURE;
2077
0
}
2078
2079
/************************************************************************/
2080
/*                            IGetExtent3D()                            */
2081
/************************************************************************/
2082
2083
OGRErr OGROpenFileGDBLayer::IGetExtent3D(int iGeomField,
2084
                                         OGREnvelope3D *psExtent, bool bForce)
2085
0
{
2086
0
    if (!BuildLayerDefinition())
2087
0
        return OGRERR_FAILURE;
2088
2089
0
    if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
2090
0
        m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0)
2091
0
    {
2092
0
        FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
2093
0
            m_poLyrTable->GetField(m_iGeomFieldIdx));
2094
0
        if (!std::isnan(poGDBGeomField->GetXMin()))
2095
0
        {
2096
0
            psExtent->MinX = poGDBGeomField->GetXMin();
2097
0
            psExtent->MinY = poGDBGeomField->GetYMin();
2098
0
            psExtent->MaxX = poGDBGeomField->GetXMax();
2099
0
            psExtent->MaxY = poGDBGeomField->GetYMax();
2100
0
            if (!std::isnan(poGDBGeomField->GetZMin()))
2101
0
            {
2102
0
                psExtent->MinZ = poGDBGeomField->GetZMin();
2103
0
                psExtent->MaxZ = poGDBGeomField->GetZMax();
2104
0
            }
2105
0
            else
2106
0
            {
2107
0
                if (OGR_GT_HasZ(m_eGeomType))
2108
0
                {
2109
0
                    return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce);
2110
0
                }
2111
0
                psExtent->MinZ = std::numeric_limits<double>::infinity();
2112
0
                psExtent->MaxZ = -std::numeric_limits<double>::infinity();
2113
0
            }
2114
0
            return OGRERR_NONE;
2115
0
        }
2116
0
    }
2117
2118
0
    return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce);
2119
0
}
2120
2121
/************************************************************************/
2122
/*                          GetFeatureCount()                           */
2123
/************************************************************************/
2124
2125
GIntBig OGROpenFileGDBLayer::GetFeatureCount(int bForce)
2126
0
{
2127
0
    if (!BuildLayerDefinition())
2128
0
        return 0;
2129
2130
    /* No filter */
2131
0
    if ((m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0) &&
2132
0
        m_poAttrQuery == nullptr)
2133
0
    {
2134
0
        return m_poLyrTable->GetValidRecordCount();
2135
0
    }
2136
0
    else if (m_nFilteredFeatureCount >= 0 && m_poAttrQuery == nullptr)
2137
0
    {
2138
0
        return m_nFilteredFeatureCount;
2139
0
    }
2140
2141
    /* Only geometry filter ? */
2142
0
    if (m_poAttrQuery == nullptr && m_bFilterIsEnvelope)
2143
0
    {
2144
0
        if (m_poSpatialIndexIterator)
2145
0
        {
2146
0
            m_poSpatialIndexIterator->Reset();
2147
0
            int nCount = 0;
2148
0
            while (true)
2149
0
            {
2150
0
                const auto nRowIdx =
2151
0
                    m_poSpatialIndexIterator->GetNextRowSortedByFID();
2152
0
                if (nRowIdx < 0)
2153
0
                    break;
2154
0
                if (!m_poLyrTable->SelectRow(nRowIdx))
2155
0
                {
2156
0
                    if (m_poLyrTable->HasGotError())
2157
0
                        break;
2158
0
                    else
2159
0
                        continue;
2160
0
                }
2161
2162
0
                const OGRField *psField =
2163
0
                    m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
2164
0
                if (psField != nullptr)
2165
0
                {
2166
0
                    if (m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(
2167
0
                            psField))
2168
0
                    {
2169
0
                        OGRGeometry *poGeom =
2170
0
                            m_poGeomConverter->GetAsGeometry(psField);
2171
0
                        if (poGeom != nullptr && FilterGeometry(poGeom))
2172
0
                        {
2173
0
                            nCount++;
2174
0
                        }
2175
0
                        delete poGeom;
2176
0
                    }
2177
0
                }
2178
0
            }
2179
0
            return nCount;
2180
0
        }
2181
2182
0
        int nCount = 0;
2183
0
        if (m_eSpatialIndexState == SPI_IN_BUILDING && m_iCurFeat != 0)
2184
0
            m_eSpatialIndexState = SPI_INVALID;
2185
2186
0
        int nFilteredFeatureCountAlloc = 0;
2187
0
        if (m_eSpatialIndexState == SPI_IN_BUILDING)
2188
0
        {
2189
0
            CPLFree(m_pahFilteredFeatures);
2190
0
            m_pahFilteredFeatures = nullptr;
2191
0
            m_nFilteredFeatureCount = 0;
2192
0
        }
2193
2194
0
        for (int64_t i = 0; i < m_poLyrTable->GetTotalRecordCount(); i++)
2195
0
        {
2196
0
            if (!m_poLyrTable->SelectRow(i))
2197
0
            {
2198
0
                if (m_poLyrTable->HasGotError())
2199
0
                    break;
2200
0
                else
2201
0
                    continue;
2202
0
            }
2203
#if SIZEOF_VOIDP < 8
2204
            if (i > INT32_MAX)
2205
            {
2206
                // CPLQuadTreeInsertWithBounds stores row index values as void*
2207
                // This would overflow here.
2208
                m_eSpatialIndexState = SPI_INVALID;
2209
                break;
2210
            }
2211
#endif
2212
2213
0
            const OGRField *psField =
2214
0
                m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
2215
0
            if (psField != nullptr)
2216
0
            {
2217
0
                if (m_eSpatialIndexState == SPI_IN_BUILDING)
2218
0
                {
2219
0
                    OGREnvelope sFeatureEnvelope;
2220
0
                    if (m_poLyrTable->GetFeatureExtent(psField,
2221
0
                                                       &sFeatureEnvelope))
2222
0
                    {
2223
0
                        CPLRectObj sBounds;
2224
0
                        sBounds.minx = sFeatureEnvelope.MinX;
2225
0
                        sBounds.miny = sFeatureEnvelope.MinY;
2226
0
                        sBounds.maxx = sFeatureEnvelope.MaxX;
2227
0
                        sBounds.maxy = sFeatureEnvelope.MaxY;
2228
0
                        CPLQuadTreeInsertWithBounds(
2229
0
                            m_pQuadTree,
2230
0
                            reinterpret_cast<void *>(static_cast<uintptr_t>(i)),
2231
0
                            &sBounds);
2232
0
                    }
2233
0
                }
2234
2235
0
                if (m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField))
2236
0
                {
2237
0
                    OGRGeometry *poGeom =
2238
0
                        m_poGeomConverter->GetAsGeometry(psField);
2239
0
                    if (poGeom != nullptr && FilterGeometry(poGeom))
2240
0
                    {
2241
0
                        if (m_eSpatialIndexState == SPI_IN_BUILDING)
2242
0
                        {
2243
0
                            if (nCount == nFilteredFeatureCountAlloc)
2244
0
                            {
2245
0
                                nFilteredFeatureCountAlloc =
2246
0
                                    4 * nFilteredFeatureCountAlloc / 3 + 1024;
2247
0
                                m_pahFilteredFeatures = static_cast<void **>(
2248
0
                                    CPLRealloc(m_pahFilteredFeatures,
2249
0
                                               sizeof(void *) *
2250
0
                                                   nFilteredFeatureCountAlloc));
2251
0
                            }
2252
0
                            m_pahFilteredFeatures[nCount] =
2253
0
                                reinterpret_cast<void *>(
2254
0
                                    static_cast<uintptr_t>(i));
2255
0
                        }
2256
0
                        nCount++;
2257
0
                    }
2258
0
                    delete poGeom;
2259
0
                }
2260
0
            }
2261
0
        }
2262
0
        if (m_eSpatialIndexState == SPI_IN_BUILDING)
2263
0
        {
2264
0
            m_nFilteredFeatureCount = nCount;
2265
0
            m_eSpatialIndexState = SPI_COMPLETED;
2266
0
        }
2267
2268
0
        return nCount;
2269
0
    }
2270
    /* Only simple attribute filter ? */
2271
0
    else if (m_poFilterGeom == nullptr && m_poAttributeIterator != nullptr &&
2272
0
             m_bIteratorSufficientToEvaluateFilter)
2273
0
    {
2274
0
        return m_poAttributeIterator->GetRowCount();
2275
0
    }
2276
2277
0
    return OGRLayer::GetFeatureCount(bForce);
2278
0
}
2279
2280
/************************************************************************/
2281
/*                           TestCapability()                           */
2282
/************************************************************************/
2283
2284
int OGROpenFileGDBLayer::TestCapability(const char *pszCap) const
2285
24.5k
{
2286
24.5k
    if (!const_cast<OGROpenFileGDBLayer *>(this)->BuildLayerDefinition())
2287
0
        return FALSE;
2288
2289
24.5k
    if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteField) ||
2290
24.5k
        EQUAL(pszCap, OLCAlterFieldDefn) ||
2291
24.5k
        EQUAL(pszCap, OLCAlterGeomFieldDefn) ||
2292
24.5k
        EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite) ||
2293
24.5k
        EQUAL(pszCap, OLCDeleteFeature) || EQUAL(pszCap, OLCRename))
2294
0
    {
2295
0
        return m_bEditable;
2296
0
    }
2297
2298
24.5k
    if (EQUAL(pszCap, OLCFastFeatureCount))
2299
0
    {
2300
0
        return ((m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0) &&
2301
0
                m_poAttrQuery == nullptr);
2302
0
    }
2303
24.5k
    else if (EQUAL(pszCap, OLCFastSetNextByIndex))
2304
0
    {
2305
0
        return (m_poLyrTable->GetValidRecordCount() ==
2306
0
                    m_poLyrTable->GetTotalRecordCount() &&
2307
0
                m_poAttributeIterator == nullptr &&
2308
0
                m_poSpatialIndexIterator == nullptr);
2309
0
    }
2310
24.5k
    else if (EQUAL(pszCap, OLCRandomRead))
2311
0
    {
2312
0
        return TRUE;
2313
0
    }
2314
24.5k
    else if (EQUAL(pszCap, OLCFastGetExtent))
2315
0
    {
2316
0
        return TRUE;
2317
0
    }
2318
24.5k
    else if (EQUAL(pszCap, OLCFastGetExtent3D))
2319
0
    {
2320
0
        if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
2321
0
            m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0)
2322
0
        {
2323
0
            FileGDBGeomField *poGDBGeomField =
2324
0
                reinterpret_cast<FileGDBGeomField *>(
2325
0
                    m_poLyrTable->GetField(m_iGeomFieldIdx));
2326
0
            if (!std::isnan(poGDBGeomField->GetXMin()))
2327
0
            {
2328
0
                if (!std::isnan(poGDBGeomField->GetZMin()))
2329
0
                {
2330
0
                    return TRUE;
2331
0
                }
2332
0
                else
2333
0
                {
2334
0
                    return !OGR_GT_HasZ(m_eGeomType);
2335
0
                }
2336
0
            }
2337
0
        }
2338
0
        return FALSE;
2339
0
    }
2340
24.5k
    else if (EQUAL(pszCap, OLCIgnoreFields))
2341
0
    {
2342
0
        return TRUE;
2343
0
    }
2344
24.5k
    else if (EQUAL(pszCap, OLCStringsAsUTF8))
2345
0
    {
2346
0
        return TRUE; /* ? */
2347
0
    }
2348
2349
24.5k
    else if (EQUAL(pszCap, OLCMeasuredGeometries))
2350
9.48k
        return TRUE;
2351
2352
15.0k
    else if (EQUAL(pszCap, OLCCurveGeometries))
2353
9.48k
        return TRUE;
2354
2355
5.53k
    else if (EQUAL(pszCap, OLCZGeometries))
2356
5.53k
        return TRUE;
2357
2358
0
    else if (EQUAL(pszCap, OLCFastSpatialFilter))
2359
0
    {
2360
0
        return m_eSpatialIndexState == SPI_COMPLETED ||
2361
0
               (m_poLyrTable->CanUseIndices() &&
2362
0
                m_poLyrTable->HasSpatialIndex());
2363
0
    }
2364
2365
0
    return FALSE;
2366
24.5k
}
2367
2368
/************************************************************************/
2369
/*                          HasIndexForField()                          */
2370
/************************************************************************/
2371
2372
bool OGROpenFileGDBLayer::HasIndexForField(const char *pszFieldName)
2373
0
{
2374
0
    if (!BuildLayerDefinition())
2375
0
        return false;
2376
0
    if (!m_poLyrTable->CanUseIndices())
2377
0
        return false;
2378
0
    int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName);
2379
0
    return (nTableColIdx >= 0 &&
2380
0
            m_poLyrTable->GetField(nTableColIdx)->HasIndex());
2381
0
}
2382
2383
/************************************************************************/
2384
/*                             BuildIndex()                             */
2385
/************************************************************************/
2386
2387
FileGDBIterator *OGROpenFileGDBLayer::BuildIndex(const char *pszFieldName,
2388
                                                 int bAscending, int op,
2389
                                                 swq_expr_node *poValue)
2390
0
{
2391
0
    if (!BuildLayerDefinition())
2392
0
        return nullptr;
2393
2394
0
    int idx = GetLayerDefn()->GetFieldIndex(pszFieldName);
2395
0
    if (idx < 0)
2396
0
        return nullptr;
2397
0
    const OGRFieldDefn *poFieldDefn = GetLayerDefn()->GetFieldDefn(idx);
2398
2399
0
    int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName);
2400
0
    if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex())
2401
0
    {
2402
0
        if (op < 0)
2403
0
            return FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx,
2404
0
                                                   bAscending);
2405
2406
0
        OGRField sValue;
2407
0
        if (FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue))
2408
0
        {
2409
0
            FileGDBSQLOp eOp;
2410
0
            switch (op)
2411
0
            {
2412
0
                case SWQ_LE:
2413
0
                    eOp = FGSO_LE;
2414
0
                    break;
2415
0
                case SWQ_LT:
2416
0
                    eOp = FGSO_LT;
2417
0
                    break;
2418
0
                case SWQ_EQ:
2419
0
                    eOp = FGSO_EQ;
2420
0
                    break;
2421
0
                case SWQ_GE:
2422
0
                    eOp = FGSO_GE;
2423
0
                    break;
2424
0
                case SWQ_GT:
2425
0
                    eOp = FGSO_GT;
2426
0
                    break;
2427
0
                default:
2428
0
                    return nullptr;
2429
0
            }
2430
2431
0
            return FileGDBIterator::Build(m_poLyrTable, nTableColIdx,
2432
0
                                          bAscending, eOp,
2433
0
                                          poFieldDefn->GetType(), &sValue);
2434
0
        }
2435
0
    }
2436
0
    return nullptr;
2437
0
}
2438
2439
/************************************************************************/
2440
/*                           GetMinMaxValue()                           */
2441
/************************************************************************/
2442
2443
const OGRField *
2444
OGROpenFileGDBLayer::GetMinMaxValue(const OGRFieldDefn *poFieldDefn, int bIsMin,
2445
                                    int &eOutType)
2446
0
{
2447
0
    eOutType = -1;
2448
0
    if (!BuildLayerDefinition())
2449
0
        return nullptr;
2450
0
    if (!m_poLyrTable->CanUseIndices())
2451
0
        return nullptr;
2452
2453
0
    const int nTableColIdx =
2454
0
        m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
2455
0
    if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex())
2456
0
    {
2457
0
        delete m_poIterMinMax;
2458
0
        m_poIterMinMax =
2459
0
            FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, TRUE);
2460
0
        if (m_poIterMinMax != nullptr)
2461
0
        {
2462
0
            const OGRField *poRet = (bIsMin)
2463
0
                                        ? m_poIterMinMax->GetMinValue(eOutType)
2464
0
                                        : m_poIterMinMax->GetMaxValue(eOutType);
2465
0
            if (poRet == nullptr)
2466
0
                eOutType = poFieldDefn->GetType();
2467
0
            return poRet;
2468
0
        }
2469
0
    }
2470
0
    return nullptr;
2471
0
}
2472
2473
/************************************************************************/
2474
/*                         GetMinMaxSumCount()                          */
2475
/************************************************************************/
2476
2477
int OGROpenFileGDBLayer::GetMinMaxSumCount(const OGRFieldDefn *poFieldDefn,
2478
                                           double &dfMin, double &dfMax,
2479
                                           double &dfSum, int &nCount)
2480
0
{
2481
0
    dfMin = 0.0;
2482
0
    dfMax = 0.0;
2483
0
    dfSum = 0.0;
2484
0
    nCount = 0;
2485
0
    if (!BuildLayerDefinition())
2486
0
        return false;
2487
0
    if (!m_poLyrTable->CanUseIndices())
2488
0
        return false;
2489
2490
0
    int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
2491
0
    if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex())
2492
0
    {
2493
0
        auto poIter = std::unique_ptr<FileGDBIterator>(
2494
0
            FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, TRUE));
2495
0
        if (poIter)
2496
0
        {
2497
0
            return poIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount);
2498
0
        }
2499
0
    }
2500
0
    return false;
2501
0
}
2502
2503
/************************************************************************/
2504
/*                             GetDataset()                             */
2505
/************************************************************************/
2506
2507
GDALDataset *OGROpenFileGDBLayer::GetDataset()
2508
0
{
2509
0
    return m_poDS;
2510
0
}