Coverage Report

Created: 2025-12-03 08:24

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