Coverage Report

Created: 2025-08-11 09:23

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