/src/gdal/ogr/ogrsf_frmts/openfilegdb/filegdbindex.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: Implements reading of FileGDB indexes |
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 "filegdbtable_priv.h" |
15 | | |
16 | | #include <cmath> |
17 | | #include <cstddef> |
18 | | #include <cstdio> |
19 | | #include <cstring> |
20 | | #include <ctime> |
21 | | #include <algorithm> |
22 | | #include <array> |
23 | | #include <memory> |
24 | | #include <string> |
25 | | #include <vector> |
26 | | |
27 | | #include "cpl_conv.h" |
28 | | #include "cpl_error.h" |
29 | | #include "cpl_mem_cache.h" |
30 | | #include "cpl_noncopyablevector.h" |
31 | | #include "cpl_string.h" |
32 | | #include "cpl_time.h" |
33 | | #include "cpl_vsi.h" |
34 | | #include "ogr_core.h" |
35 | | #include "filegdbtable.h" |
36 | | |
37 | | namespace OpenFileGDB |
38 | | { |
39 | | |
40 | 2.06k | FileGDBIndex::~FileGDBIndex() = default; |
41 | | |
42 | | /************************************************************************/ |
43 | | /* GetFieldNameFromExpression() */ |
44 | | /************************************************************************/ |
45 | | |
46 | | std::string |
47 | | FileGDBIndex::GetFieldNameFromExpression(const std::string &osExpression) |
48 | 4.91k | { |
49 | 4.91k | if (STARTS_WITH_CI(osExpression.c_str(), "LOWER(") && |
50 | 4.91k | osExpression.back() == ')') |
51 | 0 | return osExpression.substr(strlen("LOWER("), |
52 | 0 | osExpression.size() - strlen("LOWER()")); |
53 | 4.91k | return osExpression; |
54 | 4.91k | } |
55 | | |
56 | | /************************************************************************/ |
57 | | /* GetFieldName() */ |
58 | | /************************************************************************/ |
59 | | |
60 | | std::string FileGDBIndex::GetFieldName() const |
61 | 2.85k | { |
62 | 2.85k | return GetFieldNameFromExpression(m_osExpression); |
63 | 2.85k | } |
64 | | |
65 | | /************************************************************************/ |
66 | | /* FileGDBTrivialIterator */ |
67 | | /************************************************************************/ |
68 | | |
69 | | class FileGDBTrivialIterator final : public FileGDBIterator |
70 | | { |
71 | | FileGDBIterator *poParentIter = nullptr; |
72 | | FileGDBTable *poTable = nullptr; |
73 | | int64_t iRow = 0; |
74 | | |
75 | | FileGDBTrivialIterator(const FileGDBTrivialIterator &) = delete; |
76 | | FileGDBTrivialIterator &operator=(const FileGDBTrivialIterator &) = delete; |
77 | | |
78 | | public: |
79 | | explicit FileGDBTrivialIterator(FileGDBIterator *poParentIter); |
80 | | |
81 | | virtual ~FileGDBTrivialIterator() |
82 | 0 | { |
83 | 0 | delete poParentIter; |
84 | 0 | } |
85 | | |
86 | | virtual FileGDBTable *GetTable() override |
87 | 0 | { |
88 | 0 | return poTable; |
89 | 0 | } |
90 | | |
91 | | virtual void Reset() override |
92 | 0 | { |
93 | 0 | iRow = 0; |
94 | 0 | poParentIter->Reset(); |
95 | 0 | } |
96 | | |
97 | | virtual int64_t GetNextRowSortedByFID() override; |
98 | | |
99 | | virtual int64_t GetRowCount() override |
100 | 0 | { |
101 | 0 | return poTable->GetTotalRecordCount(); |
102 | 0 | } |
103 | | |
104 | | virtual int64_t GetNextRowSortedByValue() override |
105 | 0 | { |
106 | 0 | return poParentIter->GetNextRowSortedByValue(); |
107 | 0 | } |
108 | | |
109 | | virtual const OGRField *GetMinValue(int &eOutType) override |
110 | 0 | { |
111 | 0 | return poParentIter->GetMinValue(eOutType); |
112 | 0 | } |
113 | | |
114 | | virtual const OGRField *GetMaxValue(int &eOutType) override |
115 | 0 | { |
116 | 0 | return poParentIter->GetMaxValue(eOutType); |
117 | 0 | } |
118 | | |
119 | | virtual bool GetMinMaxSumCount(double &dfMin, double &dfMax, double &dfSum, |
120 | | int &nCount) override |
121 | 0 | { |
122 | 0 | return poParentIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); |
123 | 0 | } |
124 | | }; |
125 | | |
126 | | /************************************************************************/ |
127 | | /* FileGDBNotIterator */ |
128 | | /************************************************************************/ |
129 | | |
130 | | class FileGDBNotIterator final : public FileGDBIterator |
131 | | { |
132 | | FileGDBIterator *poIterBase = nullptr; |
133 | | FileGDBTable *poTable = nullptr; |
134 | | int64_t iRow = 0; |
135 | | int64_t iNextRowBase = -1; |
136 | | int bNoHoles = 0; |
137 | | |
138 | | FileGDBNotIterator(const FileGDBNotIterator &) = delete; |
139 | | FileGDBNotIterator &operator=(const FileGDBNotIterator &) = delete; |
140 | | |
141 | | public: |
142 | | explicit FileGDBNotIterator(FileGDBIterator *poIterBase); |
143 | | virtual ~FileGDBNotIterator(); |
144 | | |
145 | | virtual FileGDBTable *GetTable() override |
146 | 0 | { |
147 | 0 | return poTable; |
148 | 0 | } |
149 | | |
150 | | virtual void Reset() override; |
151 | | virtual int64_t GetNextRowSortedByFID() override; |
152 | | virtual int64_t GetRowCount() override; |
153 | | }; |
154 | | |
155 | | /************************************************************************/ |
156 | | /* FileGDBAndIterator */ |
157 | | /************************************************************************/ |
158 | | |
159 | | class FileGDBAndIterator final : public FileGDBIterator |
160 | | { |
161 | | FileGDBIterator *poIter1 = nullptr; |
162 | | FileGDBIterator *poIter2 = nullptr; |
163 | | int64_t iNextRow1 = -1; |
164 | | int64_t iNextRow2 = -1; |
165 | | bool m_bTakeOwnershipOfIterators = false; |
166 | | |
167 | | FileGDBAndIterator(const FileGDBAndIterator &) = delete; |
168 | | FileGDBAndIterator &operator=(const FileGDBAndIterator &) = delete; |
169 | | |
170 | | public: |
171 | | FileGDBAndIterator(FileGDBIterator *poIter1, FileGDBIterator *poIter2, |
172 | | bool bTakeOwnershipOfIterators); |
173 | | virtual ~FileGDBAndIterator(); |
174 | | |
175 | | virtual FileGDBTable *GetTable() override |
176 | 0 | { |
177 | 0 | return poIter1->GetTable(); |
178 | 0 | } |
179 | | |
180 | | virtual void Reset() override; |
181 | | virtual int64_t GetNextRowSortedByFID() override; |
182 | | }; |
183 | | |
184 | | /************************************************************************/ |
185 | | /* FileGDBOrIterator */ |
186 | | /************************************************************************/ |
187 | | |
188 | | class FileGDBOrIterator final : public FileGDBIterator |
189 | | { |
190 | | FileGDBIterator *poIter1 = nullptr; |
191 | | FileGDBIterator *poIter2 = nullptr; |
192 | | int bIteratorAreExclusive = false; |
193 | | int64_t iNextRow1 = -1; |
194 | | int64_t iNextRow2 = -1; |
195 | | bool bHasJustReset = true; |
196 | | |
197 | | FileGDBOrIterator(const FileGDBOrIterator &) = delete; |
198 | | FileGDBOrIterator &operator=(const FileGDBOrIterator &) = delete; |
199 | | |
200 | | public: |
201 | | FileGDBOrIterator(FileGDBIterator *poIter1, FileGDBIterator *poIter2, |
202 | | int bIteratorAreExclusive = FALSE); |
203 | | virtual ~FileGDBOrIterator(); |
204 | | |
205 | | virtual FileGDBTable *GetTable() override |
206 | 0 | { |
207 | 0 | return poIter1->GetTable(); |
208 | 0 | } |
209 | | |
210 | | virtual void Reset() override; |
211 | | virtual int64_t GetNextRowSortedByFID() override; |
212 | | virtual int64_t GetRowCount() override; |
213 | | }; |
214 | | |
215 | | /************************************************************************/ |
216 | | /* FileGDBIndexIteratorBase */ |
217 | | /************************************************************************/ |
218 | | |
219 | | constexpr int MAX_DEPTH = 3; |
220 | | constexpr int FGDB_PAGE_SIZE_V1 = 4096; |
221 | | constexpr int FGDB_PAGE_SIZE_V2 = 65536; |
222 | | constexpr int MAX_FGDB_PAGE_SIZE = FGDB_PAGE_SIZE_V2; |
223 | | |
224 | | class FileGDBIndexIteratorBase : virtual public FileGDBIterator |
225 | | { |
226 | | protected: |
227 | | FileGDBTable *poParent = nullptr; |
228 | | bool bAscending = false; |
229 | | VSILFILE *fpCurIdx = nullptr; |
230 | | |
231 | | //! Version of .atx/.spx: 1 or 2 |
232 | | GUInt32 m_nVersion = 0; |
233 | | |
234 | | // Number of pages of size m_nPageSize |
235 | | GUInt32 m_nPageCount = 0; |
236 | | |
237 | | //! Page size in bytes: 4096 for v1 format, 65536 for v2 |
238 | | int m_nPageSize = 0; |
239 | | |
240 | | //! Maximum number of features or sub-pages referenced by a page. |
241 | | GUInt32 nMaxPerPages = 0; |
242 | | |
243 | | //! Size of ObjectID referenced in pages, in bytes. |
244 | | // sizeof(uint32_t) for V1, sizeof(uint64_t) for V2 |
245 | | GUInt32 m_nObjectIDSize = 0; |
246 | | |
247 | | //! Size of the indexed value, in bytes. |
248 | | GUInt32 m_nValueSize = 0; |
249 | | |
250 | | //! Non-leaf page header size in bytes. 8 for V1, 12 for V2 |
251 | | GUInt32 m_nNonLeafPageHeaderSize = 0; |
252 | | |
253 | | //! Leaf page header size in bytes. 12 for V1, 20 for V2 |
254 | | GUInt32 m_nLeafPageHeaderSize = 0; |
255 | | |
256 | | //! Offset within a page at which the first indexed value is found. |
257 | | GUInt32 m_nOffsetFirstValInPage = 0; |
258 | | |
259 | | //! Number of values referenced in the index. |
260 | | GUInt64 m_nValueCountInIdx = 0; |
261 | | |
262 | | GUInt32 nIndexDepth = 0; |
263 | | #ifdef DEBUG |
264 | | uint64_t iLoadedPage[MAX_DEPTH]; |
265 | | #endif |
266 | | int iFirstPageIdx[MAX_DEPTH]; |
267 | | int iLastPageIdx[MAX_DEPTH]; |
268 | | int iCurPageIdx[MAX_DEPTH]; |
269 | | GUInt32 nSubPagesCount[MAX_DEPTH]; |
270 | | uint64_t nLastPageAccessed[MAX_DEPTH]; |
271 | | |
272 | | int iCurFeatureInPage = -1; |
273 | | int nFeaturesInPage = 0; |
274 | | |
275 | | bool bEOF = false; |
276 | | |
277 | | GByte abyPage[MAX_DEPTH][MAX_FGDB_PAGE_SIZE]; |
278 | | GByte abyPageFeature[MAX_FGDB_PAGE_SIZE]; |
279 | | |
280 | | typedef lru11::Cache<uint64_t, cpl::NonCopyableVector<GByte>> CacheType; |
281 | | std::array<CacheType, MAX_DEPTH> m_oCachePage{ |
282 | | {CacheType{2, 0}, CacheType{2, 0}, CacheType{2, 0}}}; |
283 | | CacheType m_oCacheFeaturePage{2, 0}; |
284 | | |
285 | | bool ReadTrailer(const std::string &osFilename); |
286 | | |
287 | | uint64_t ReadPageNumber(int iLevel); |
288 | | bool LoadNextPage(int iLevel); |
289 | | virtual bool FindPages(int iLevel, uint64_t nPage) = 0; |
290 | | bool LoadNextFeaturePage(); |
291 | | |
292 | | FileGDBIndexIteratorBase(FileGDBTable *poParent, int bAscending); |
293 | | |
294 | | FileGDBIndexIteratorBase(const FileGDBIndexIteratorBase &) = delete; |
295 | | FileGDBIndexIteratorBase & |
296 | | operator=(const FileGDBIndexIteratorBase &) = delete; |
297 | | |
298 | | public: |
299 | | virtual ~FileGDBIndexIteratorBase(); |
300 | | |
301 | | virtual FileGDBTable *GetTable() override |
302 | 0 | { |
303 | 0 | return poParent; |
304 | 0 | } |
305 | | |
306 | | virtual void Reset() override; |
307 | | }; |
308 | | |
309 | | /************************************************************************/ |
310 | | /* FileGDBIndexIterator */ |
311 | | /************************************************************************/ |
312 | | |
313 | | constexpr int UUID_LEN_AS_STRING = 38; |
314 | | constexpr int MAX_UTF8_LEN_STR = 4 * MAX_CAR_COUNT_INDEXED_STR; |
315 | | |
316 | | class FileGDBIndexIterator final : public FileGDBIndexIteratorBase |
317 | | { |
318 | | FileGDBFieldType eFieldType = FGFT_UNDEFINED; |
319 | | FileGDBSQLOp eOp = FGSO_ISNOTNULL; |
320 | | OGRField sValue{}; |
321 | | |
322 | | bool bEvaluateToFALSE = false; |
323 | | |
324 | | int iSorted = 0; |
325 | | int nSortedCount = -1; |
326 | | int64_t *panSortedRows = nullptr; |
327 | | int SortRows(); |
328 | | |
329 | | GUInt16 asUTF16Str[MAX_CAR_COUNT_INDEXED_STR]; |
330 | | int nStrLen = 0; |
331 | | char szUUID[UUID_LEN_AS_STRING + 1]; |
332 | | |
333 | | OGRField sMin{}; |
334 | | OGRField sMax{}; |
335 | | char szMin[MAX_UTF8_LEN_STR + 1]; |
336 | | char szMax[MAX_UTF8_LEN_STR + 1]; |
337 | | const OGRField *GetMinMaxValue(OGRField *psField, int &eOutType, |
338 | | int bIsMin); |
339 | | |
340 | | virtual bool FindPages(int iLevel, uint64_t nPage) override; |
341 | | int64_t GetNextRow(); |
342 | | |
343 | | FileGDBIndexIterator(FileGDBTable *poParent, int bAscending); |
344 | | int SetConstraint(int nFieldIdx, FileGDBSQLOp op, |
345 | | OGRFieldType eOGRFieldType, const OGRField *psValue); |
346 | | |
347 | | template <class Getter> |
348 | | void GetMinMaxSumCount(double &dfMin, double &dfMax, double &dfSum, |
349 | | int &nCount); |
350 | | |
351 | | FileGDBIndexIterator(const FileGDBIndexIterator &) = delete; |
352 | | FileGDBIndexIterator &operator=(const FileGDBIndexIterator &) = delete; |
353 | | |
354 | | public: |
355 | | virtual ~FileGDBIndexIterator(); |
356 | | |
357 | | static FileGDBIterator *Build(FileGDBTable *poParentIn, int nFieldIdx, |
358 | | int bAscendingIn, FileGDBSQLOp op, |
359 | | OGRFieldType eOGRFieldType, |
360 | | const OGRField *psValue); |
361 | | |
362 | | virtual int64_t GetNextRowSortedByFID() override; |
363 | | virtual int64_t GetRowCount() override; |
364 | | virtual void Reset() override; |
365 | | |
366 | | virtual int64_t GetNextRowSortedByValue() override |
367 | 0 | { |
368 | 0 | return GetNextRow(); |
369 | 0 | } |
370 | | |
371 | | virtual const OGRField *GetMinValue(int &eOutType) override; |
372 | | virtual const OGRField *GetMaxValue(int &eOutType) override; |
373 | | virtual bool GetMinMaxSumCount(double &dfMin, double &dfMax, double &dfSum, |
374 | | int &nCount) override; |
375 | | }; |
376 | | |
377 | | /************************************************************************/ |
378 | | /* GetMinValue() */ |
379 | | /************************************************************************/ |
380 | | |
381 | | const OGRField *FileGDBIterator::GetMinValue(int &eOutType) |
382 | 0 | { |
383 | 0 | PrintError(); |
384 | 0 | eOutType = -1; |
385 | 0 | return nullptr; |
386 | 0 | } |
387 | | |
388 | | /************************************************************************/ |
389 | | /* GetMaxValue() */ |
390 | | /************************************************************************/ |
391 | | |
392 | | const OGRField *FileGDBIterator::GetMaxValue(int &eOutType) |
393 | 0 | { |
394 | 0 | PrintError(); |
395 | 0 | eOutType = -1; |
396 | 0 | return nullptr; |
397 | 0 | } |
398 | | |
399 | | /************************************************************************/ |
400 | | /* GetNextRowSortedByValue() */ |
401 | | /************************************************************************/ |
402 | | |
403 | | int64_t FileGDBIterator::GetNextRowSortedByValue() |
404 | 0 | { |
405 | 0 | PrintError(); |
406 | 0 | return -1; |
407 | 0 | } |
408 | | |
409 | | /************************************************************************/ |
410 | | /* GetMinMaxSumCount() */ |
411 | | /************************************************************************/ |
412 | | |
413 | | bool FileGDBIterator::GetMinMaxSumCount(double &dfMin, double &dfMax, |
414 | | double &dfSum, int &nCount) |
415 | 0 | { |
416 | 0 | PrintError(); |
417 | 0 | dfMin = 0.0; |
418 | 0 | dfMax = 0.0; |
419 | 0 | dfSum = 0.0; |
420 | 0 | nCount = 0; |
421 | 0 | return false; |
422 | 0 | } |
423 | | |
424 | | /************************************************************************/ |
425 | | /* Build() */ |
426 | | /************************************************************************/ |
427 | | |
428 | | FileGDBIterator *FileGDBIterator::Build(FileGDBTable *poParent, int nFieldIdx, |
429 | | int bAscending, FileGDBSQLOp op, |
430 | | OGRFieldType eOGRFieldType, |
431 | | const OGRField *psValue) |
432 | 0 | { |
433 | 0 | return FileGDBIndexIterator::Build(poParent, nFieldIdx, bAscending, op, |
434 | 0 | eOGRFieldType, psValue); |
435 | 0 | } |
436 | | |
437 | | /************************************************************************/ |
438 | | /* BuildIsNotNull() */ |
439 | | /************************************************************************/ |
440 | | |
441 | | FileGDBIterator *FileGDBIterator::BuildIsNotNull(FileGDBTable *poParent, |
442 | | int nFieldIdx, int bAscending) |
443 | 0 | { |
444 | 0 | FileGDBIterator *poIter = Build(poParent, nFieldIdx, bAscending, |
445 | 0 | FGSO_ISNOTNULL, OFTMaxType, nullptr); |
446 | 0 | if (poIter != nullptr) |
447 | 0 | { |
448 | | /* Optimization */ |
449 | 0 | if (poIter->GetRowCount() == poParent->GetTotalRecordCount()) |
450 | 0 | { |
451 | 0 | CPLAssert(poParent->GetValidRecordCount() == |
452 | 0 | poParent->GetTotalRecordCount()); |
453 | 0 | poIter = new FileGDBTrivialIterator(poIter); |
454 | 0 | } |
455 | 0 | } |
456 | 0 | return poIter; |
457 | 0 | } |
458 | | |
459 | | /************************************************************************/ |
460 | | /* BuildNot() */ |
461 | | /************************************************************************/ |
462 | | |
463 | | FileGDBIterator *FileGDBIterator::BuildNot(FileGDBIterator *poIterBase) |
464 | 0 | { |
465 | 0 | return new FileGDBNotIterator(poIterBase); |
466 | 0 | } |
467 | | |
468 | | /************************************************************************/ |
469 | | /* BuildAnd() */ |
470 | | /************************************************************************/ |
471 | | |
472 | | FileGDBIterator *FileGDBIterator::BuildAnd(FileGDBIterator *poIter1, |
473 | | FileGDBIterator *poIter2, |
474 | | bool bTakeOwnershipOfIterators) |
475 | 0 | { |
476 | 0 | return new FileGDBAndIterator(poIter1, poIter2, bTakeOwnershipOfIterators); |
477 | 0 | } |
478 | | |
479 | | /************************************************************************/ |
480 | | /* BuildOr() */ |
481 | | /************************************************************************/ |
482 | | |
483 | | FileGDBIterator *FileGDBIterator::BuildOr(FileGDBIterator *poIter1, |
484 | | FileGDBIterator *poIter2, |
485 | | int bIteratorAreExclusive) |
486 | 0 | { |
487 | 0 | return new FileGDBOrIterator(poIter1, poIter2, bIteratorAreExclusive); |
488 | 0 | } |
489 | | |
490 | | /************************************************************************/ |
491 | | /* GetRowCount() */ |
492 | | /************************************************************************/ |
493 | | |
494 | | int64_t FileGDBIterator::GetRowCount() |
495 | 0 | { |
496 | 0 | Reset(); |
497 | 0 | int64_t nCount = 0; |
498 | 0 | while (GetNextRowSortedByFID() >= 0) |
499 | 0 | nCount++; |
500 | 0 | Reset(); |
501 | 0 | return nCount; |
502 | 0 | } |
503 | | |
504 | | /************************************************************************/ |
505 | | /* FileGDBTrivialIterator() */ |
506 | | /************************************************************************/ |
507 | | |
508 | | FileGDBTrivialIterator::FileGDBTrivialIterator(FileGDBIterator *poParentIterIn) |
509 | 0 | : poParentIter(poParentIterIn), poTable(poParentIterIn->GetTable()) |
510 | 0 | { |
511 | 0 | } |
512 | | |
513 | | /************************************************************************/ |
514 | | /* GetNextRowSortedByFID() */ |
515 | | /************************************************************************/ |
516 | | |
517 | | int64_t FileGDBTrivialIterator::GetNextRowSortedByFID() |
518 | 0 | { |
519 | 0 | if (iRow < poTable->GetTotalRecordCount()) |
520 | 0 | return iRow++; |
521 | 0 | else |
522 | 0 | return -1; |
523 | 0 | } |
524 | | |
525 | | /************************************************************************/ |
526 | | /* FileGDBNotIterator() */ |
527 | | /************************************************************************/ |
528 | | |
529 | | FileGDBNotIterator::FileGDBNotIterator(FileGDBIterator *poIterBaseIn) |
530 | 0 | : poIterBase(poIterBaseIn), poTable(poIterBaseIn->GetTable()) |
531 | 0 | { |
532 | 0 | bNoHoles = |
533 | 0 | (poTable->GetValidRecordCount() == poTable->GetTotalRecordCount()); |
534 | 0 | } |
535 | | |
536 | | /************************************************************************/ |
537 | | /* ~FileGDBNotIterator() */ |
538 | | /************************************************************************/ |
539 | | |
540 | | FileGDBNotIterator::~FileGDBNotIterator() |
541 | 0 | { |
542 | 0 | delete poIterBase; |
543 | 0 | } |
544 | | |
545 | | /************************************************************************/ |
546 | | /* Reset() */ |
547 | | /************************************************************************/ |
548 | | |
549 | | void FileGDBNotIterator::Reset() |
550 | 0 | { |
551 | 0 | poIterBase->Reset(); |
552 | 0 | iRow = 0; |
553 | 0 | iNextRowBase = -1; |
554 | 0 | } |
555 | | |
556 | | /************************************************************************/ |
557 | | /* GetNextRowSortedByFID() */ |
558 | | /************************************************************************/ |
559 | | |
560 | | int64_t FileGDBNotIterator::GetNextRowSortedByFID() |
561 | 0 | { |
562 | 0 | if (iNextRowBase < 0) |
563 | 0 | { |
564 | 0 | iNextRowBase = poIterBase->GetNextRowSortedByFID(); |
565 | 0 | if (iNextRowBase < 0) |
566 | 0 | iNextRowBase = poTable->GetTotalRecordCount(); |
567 | 0 | } |
568 | |
|
569 | 0 | while (true) |
570 | 0 | { |
571 | 0 | if (iRow < iNextRowBase) |
572 | 0 | { |
573 | 0 | if (bNoHoles) |
574 | 0 | return iRow++; |
575 | 0 | else if (poTable->GetOffsetInTableForRow(iRow)) |
576 | 0 | return iRow++; |
577 | 0 | else if (!poTable->HasGotError()) |
578 | 0 | iRow++; |
579 | 0 | else |
580 | 0 | return -1; |
581 | 0 | } |
582 | 0 | else if (iRow == poTable->GetTotalRecordCount()) |
583 | 0 | return -1; |
584 | 0 | else |
585 | 0 | { |
586 | 0 | iRow = iNextRowBase + 1; |
587 | 0 | iNextRowBase = poIterBase->GetNextRowSortedByFID(); |
588 | 0 | if (iNextRowBase < 0) |
589 | 0 | iNextRowBase = poTable->GetTotalRecordCount(); |
590 | 0 | } |
591 | 0 | } |
592 | 0 | } |
593 | | |
594 | | /************************************************************************/ |
595 | | /* GetRowCount() */ |
596 | | /************************************************************************/ |
597 | | |
598 | | int64_t FileGDBNotIterator::GetRowCount() |
599 | 0 | { |
600 | 0 | return poTable->GetValidRecordCount() - poIterBase->GetRowCount(); |
601 | 0 | } |
602 | | |
603 | | /************************************************************************/ |
604 | | /* FileGDBAndIterator() */ |
605 | | /************************************************************************/ |
606 | | |
607 | | FileGDBAndIterator::FileGDBAndIterator(FileGDBIterator *poIter1In, |
608 | | FileGDBIterator *poIter2In, |
609 | | bool bTakeOwnershipOfIterators) |
610 | 0 | : poIter1(poIter1In), poIter2(poIter2In), iNextRow1(-1), iNextRow2(-1), |
611 | 0 | m_bTakeOwnershipOfIterators(bTakeOwnershipOfIterators) |
612 | 0 | { |
613 | 0 | CPLAssert(poIter1->GetTable() == poIter2->GetTable()); |
614 | 0 | } |
615 | | |
616 | | /************************************************************************/ |
617 | | /* ~FileGDBAndIterator() */ |
618 | | /************************************************************************/ |
619 | | |
620 | | FileGDBAndIterator::~FileGDBAndIterator() |
621 | 0 | { |
622 | 0 | if (m_bTakeOwnershipOfIterators) |
623 | 0 | { |
624 | 0 | delete poIter1; |
625 | 0 | delete poIter2; |
626 | 0 | } |
627 | 0 | } |
628 | | |
629 | | /************************************************************************/ |
630 | | /* Reset() */ |
631 | | /************************************************************************/ |
632 | | |
633 | | void FileGDBAndIterator::Reset() |
634 | 0 | { |
635 | 0 | poIter1->Reset(); |
636 | 0 | poIter2->Reset(); |
637 | 0 | iNextRow1 = -1; |
638 | 0 | iNextRow2 = -1; |
639 | 0 | } |
640 | | |
641 | | /************************************************************************/ |
642 | | /* GetNextRowSortedByFID() */ |
643 | | /************************************************************************/ |
644 | | |
645 | | int64_t FileGDBAndIterator::GetNextRowSortedByFID() |
646 | 0 | { |
647 | 0 | if (iNextRow1 == iNextRow2) |
648 | 0 | { |
649 | 0 | iNextRow1 = poIter1->GetNextRowSortedByFID(); |
650 | 0 | iNextRow2 = poIter2->GetNextRowSortedByFID(); |
651 | 0 | if (iNextRow1 < 0 || iNextRow2 < 0) |
652 | 0 | { |
653 | 0 | return -1; |
654 | 0 | } |
655 | 0 | } |
656 | | |
657 | 0 | while (true) |
658 | 0 | { |
659 | 0 | if (iNextRow1 < iNextRow2) |
660 | 0 | { |
661 | 0 | iNextRow1 = poIter1->GetNextRowSortedByFID(); |
662 | 0 | if (iNextRow1 < 0) |
663 | 0 | return -1; |
664 | 0 | } |
665 | 0 | else if (iNextRow2 < iNextRow1) |
666 | 0 | { |
667 | 0 | iNextRow2 = poIter2->GetNextRowSortedByFID(); |
668 | 0 | if (iNextRow2 < 0) |
669 | 0 | return -1; |
670 | 0 | } |
671 | 0 | else |
672 | 0 | return iNextRow1; |
673 | 0 | } |
674 | 0 | } |
675 | | |
676 | | /************************************************************************/ |
677 | | /* FileGDBOrIterator() */ |
678 | | /************************************************************************/ |
679 | | |
680 | | FileGDBOrIterator::FileGDBOrIterator(FileGDBIterator *poIter1In, |
681 | | FileGDBIterator *poIter2In, |
682 | | int bIteratorAreExclusiveIn) |
683 | 0 | : poIter1(poIter1In), poIter2(poIter2In), |
684 | 0 | bIteratorAreExclusive(bIteratorAreExclusiveIn) |
685 | 0 | { |
686 | 0 | CPLAssert(poIter1->GetTable() == poIter2->GetTable()); |
687 | 0 | } |
688 | | |
689 | | /************************************************************************/ |
690 | | /* ~FileGDBOrIterator() */ |
691 | | /************************************************************************/ |
692 | | |
693 | | FileGDBOrIterator::~FileGDBOrIterator() |
694 | 0 | { |
695 | 0 | delete poIter1; |
696 | 0 | delete poIter2; |
697 | 0 | } |
698 | | |
699 | | /************************************************************************/ |
700 | | /* Reset() */ |
701 | | /************************************************************************/ |
702 | | |
703 | | void FileGDBOrIterator::Reset() |
704 | 0 | { |
705 | 0 | poIter1->Reset(); |
706 | 0 | poIter2->Reset(); |
707 | 0 | iNextRow1 = -1; |
708 | 0 | iNextRow2 = -1; |
709 | 0 | bHasJustReset = true; |
710 | 0 | } |
711 | | |
712 | | /************************************************************************/ |
713 | | /* GetNextRowSortedByFID() */ |
714 | | /************************************************************************/ |
715 | | |
716 | | int64_t FileGDBOrIterator::GetNextRowSortedByFID() |
717 | 0 | { |
718 | 0 | if (bHasJustReset) |
719 | 0 | { |
720 | 0 | bHasJustReset = false; |
721 | 0 | iNextRow1 = poIter1->GetNextRowSortedByFID(); |
722 | 0 | iNextRow2 = poIter2->GetNextRowSortedByFID(); |
723 | 0 | } |
724 | |
|
725 | 0 | if (iNextRow1 < 0) |
726 | 0 | { |
727 | 0 | auto iVal = iNextRow2; |
728 | 0 | iNextRow2 = poIter2->GetNextRowSortedByFID(); |
729 | 0 | return iVal; |
730 | 0 | } |
731 | 0 | if (iNextRow2 < 0 || iNextRow1 < iNextRow2) |
732 | 0 | { |
733 | 0 | auto iVal = iNextRow1; |
734 | 0 | iNextRow1 = poIter1->GetNextRowSortedByFID(); |
735 | 0 | return iVal; |
736 | 0 | } |
737 | 0 | if (iNextRow2 < iNextRow1) |
738 | 0 | { |
739 | 0 | auto iVal = iNextRow2; |
740 | 0 | iNextRow2 = poIter2->GetNextRowSortedByFID(); |
741 | 0 | return iVal; |
742 | 0 | } |
743 | | |
744 | 0 | if (bIteratorAreExclusive) |
745 | 0 | PrintError(); |
746 | |
|
747 | 0 | auto iVal = iNextRow1; |
748 | 0 | iNextRow1 = poIter1->GetNextRowSortedByFID(); |
749 | 0 | iNextRow2 = poIter2->GetNextRowSortedByFID(); |
750 | 0 | return iVal; |
751 | 0 | } |
752 | | |
753 | | /************************************************************************/ |
754 | | /* GetRowCount() */ |
755 | | /************************************************************************/ |
756 | | |
757 | | int64_t FileGDBOrIterator::GetRowCount() |
758 | 0 | { |
759 | 0 | if (bIteratorAreExclusive) |
760 | 0 | return poIter1->GetRowCount() + poIter2->GetRowCount(); |
761 | 0 | else |
762 | 0 | return FileGDBIterator::GetRowCount(); |
763 | 0 | } |
764 | | |
765 | | /************************************************************************/ |
766 | | /* FileGDBIndexIteratorBase() */ |
767 | | /************************************************************************/ |
768 | | |
769 | | FileGDBIndexIteratorBase::FileGDBIndexIteratorBase(FileGDBTable *poParentIn, |
770 | | int bAscendingIn) |
771 | 0 | : poParent(poParentIn), bAscending(CPL_TO_BOOL(bAscendingIn)) |
772 | 0 | { |
773 | | #ifdef DEBUG |
774 | | memset(&iLoadedPage, 0, sizeof(iLoadedPage)); |
775 | | #endif |
776 | 0 | memset(&iFirstPageIdx, 0xFF, sizeof(iFirstPageIdx)); |
777 | 0 | memset(&iLastPageIdx, 0xFF, sizeof(iFirstPageIdx)); |
778 | 0 | memset(&iCurPageIdx, 0xFF, sizeof(iCurPageIdx)); |
779 | 0 | memset(&nSubPagesCount, 0, sizeof(nSubPagesCount)); |
780 | 0 | memset(&nLastPageAccessed, 0, sizeof(nLastPageAccessed)); |
781 | 0 | memset(&abyPage, 0, sizeof(abyPage)); |
782 | 0 | memset(&abyPageFeature, 0, sizeof(abyPageFeature)); |
783 | 0 | } |
784 | | |
785 | | /************************************************************************/ |
786 | | /* ~FileGDBIndexIteratorBase() */ |
787 | | /************************************************************************/ |
788 | | |
789 | | FileGDBIndexIteratorBase::~FileGDBIndexIteratorBase() |
790 | 0 | { |
791 | 0 | if (fpCurIdx) |
792 | 0 | VSIFCloseL(fpCurIdx); |
793 | 0 | fpCurIdx = nullptr; |
794 | 0 | } |
795 | | |
796 | | /************************************************************************/ |
797 | | /* ReadTrailer() */ |
798 | | /************************************************************************/ |
799 | | |
800 | | bool FileGDBIndexIteratorBase::ReadTrailer(const std::string &osFilename) |
801 | 0 | { |
802 | 0 | const bool errorRetValue = false; |
803 | |
|
804 | 0 | fpCurIdx = VSIFOpenL(osFilename.c_str(), "rb"); |
805 | 0 | returnErrorIf(fpCurIdx == nullptr); |
806 | | |
807 | 0 | VSIFSeekL(fpCurIdx, 0, SEEK_END); |
808 | 0 | vsi_l_offset nFileSize = VSIFTellL(fpCurIdx); |
809 | 0 | constexpr int V1_TRAILER_SIZE = 22; |
810 | 0 | constexpr int V2_TRAILER_SIZE = 30; |
811 | 0 | returnErrorIf(nFileSize < V1_TRAILER_SIZE); |
812 | | |
813 | 0 | GByte abyTrailer[V2_TRAILER_SIZE]; |
814 | 0 | VSIFSeekL(fpCurIdx, nFileSize - sizeof(uint32_t), SEEK_SET); |
815 | 0 | returnErrorIf(VSIFReadL(abyTrailer, sizeof(uint32_t), 1, fpCurIdx) != 1); |
816 | 0 | m_nVersion = GetUInt32(abyTrailer, 0); |
817 | 0 | returnErrorIf(m_nVersion != 1 && m_nVersion != 2); |
818 | | |
819 | 0 | if (m_nVersion == 1) |
820 | 0 | { |
821 | 0 | m_nPageSize = FGDB_PAGE_SIZE_V1; |
822 | 0 | VSIFSeekL(fpCurIdx, nFileSize - V1_TRAILER_SIZE, SEEK_SET); |
823 | 0 | returnErrorIf(VSIFReadL(abyTrailer, V1_TRAILER_SIZE, 1, fpCurIdx) != 1); |
824 | | |
825 | 0 | m_nPageCount = |
826 | 0 | static_cast<GUInt32>((nFileSize - V1_TRAILER_SIZE) / m_nPageSize); |
827 | |
|
828 | 0 | m_nValueSize = abyTrailer[0]; |
829 | 0 | m_nObjectIDSize = static_cast<uint32_t>(sizeof(uint32_t)); |
830 | 0 | m_nNonLeafPageHeaderSize = 8; |
831 | 0 | m_nLeafPageHeaderSize = 12; |
832 | |
|
833 | 0 | nMaxPerPages = (m_nPageSize - m_nLeafPageHeaderSize) / |
834 | 0 | (m_nObjectIDSize + m_nValueSize); |
835 | 0 | m_nOffsetFirstValInPage = |
836 | 0 | m_nLeafPageHeaderSize + nMaxPerPages * m_nObjectIDSize; |
837 | |
|
838 | 0 | GUInt32 nMagic1 = GetUInt32(abyTrailer + 2, 0); |
839 | 0 | returnErrorIf(nMagic1 != 1); |
840 | | |
841 | 0 | nIndexDepth = GetUInt32(abyTrailer + 6, 0); |
842 | | /* CPLDebug("OpenFileGDB", "nIndexDepth = %u", nIndexDepth); */ |
843 | 0 | returnErrorIf(!(nIndexDepth >= 1 && nIndexDepth <= MAX_DEPTH + 1)); |
844 | | |
845 | 0 | m_nValueCountInIdx = GetUInt32(abyTrailer + 10, 0); |
846 | | /* CPLDebug("OpenFileGDB", "m_nValueCountInIdx = %u", m_nValueCountInIdx); */ |
847 | | /* negative like in sample_clcV15_esri_v10.gdb/a00000005.FDO_UUID.atx */ |
848 | 0 | if ((m_nValueCountInIdx >> (8 * sizeof(m_nValueCountInIdx) - 1)) != 0) |
849 | 0 | { |
850 | 0 | CPLDebugOnly("OpenFileGDB", "m_nValueCountInIdx=%u", |
851 | 0 | static_cast<uint32_t>(m_nValueCountInIdx)); |
852 | 0 | return false; |
853 | 0 | } |
854 | | |
855 | | /* QGIS_TEST_101.gdb/a00000006.FDO_UUID.atx */ |
856 | | /* or .spx file from test dataset https://github.com/OSGeo/gdal/issues/5888 |
857 | | */ |
858 | 0 | if (m_nValueCountInIdx == 0 && nIndexDepth == 1) |
859 | 0 | { |
860 | 0 | VSIFSeekL(fpCurIdx, 4, SEEK_SET); |
861 | 0 | GByte abyBuffer[4]; |
862 | 0 | returnErrorIf(VSIFReadL(abyBuffer, 4, 1, fpCurIdx) != 1); |
863 | 0 | m_nValueCountInIdx = GetUInt32(abyBuffer, 0); |
864 | 0 | } |
865 | | /* PreNIS.gdb/a00000006.FDO_UUID.atx has depth 2 and the value of */ |
866 | | /* m_nValueCountInIdx is 11 which is not the number of non-null values */ |
867 | 0 | else if (m_nValueCountInIdx < nMaxPerPages && nIndexDepth > 1) |
868 | 0 | { |
869 | 0 | if (m_nValueCountInIdx > 0 && poParent->IsFileGDBV9() && |
870 | 0 | strstr(osFilename.c_str(), "blk_key_index.atx")) |
871 | 0 | { |
872 | | // m_nValueCountInIdx not reliable in FileGDB v9 .blk_key_index.atx |
873 | | // but index seems to be OK |
874 | 0 | return true; |
875 | 0 | } |
876 | | |
877 | 0 | CPLDebugOnly( |
878 | 0 | "OpenFileGDB", |
879 | 0 | "m_nValueCountInIdx=%u < nMaxPerPages=%u, nIndexDepth=%u", |
880 | 0 | static_cast<uint32_t>(m_nValueCountInIdx), nMaxPerPages, |
881 | 0 | nIndexDepth); |
882 | 0 | return false; |
883 | 0 | } |
884 | 0 | } |
885 | 0 | else |
886 | 0 | { |
887 | 0 | m_nPageSize = FGDB_PAGE_SIZE_V2; |
888 | 0 | VSIFSeekL(fpCurIdx, nFileSize - V2_TRAILER_SIZE, SEEK_SET); |
889 | 0 | returnErrorIf(VSIFReadL(abyTrailer, V2_TRAILER_SIZE, 1, fpCurIdx) != 1); |
890 | | |
891 | 0 | m_nPageCount = |
892 | 0 | static_cast<GUInt32>((nFileSize - V2_TRAILER_SIZE) / m_nPageSize); |
893 | |
|
894 | 0 | m_nValueSize = abyTrailer[0]; |
895 | 0 | m_nObjectIDSize = static_cast<uint32_t>(sizeof(uint64_t)); |
896 | 0 | m_nNonLeafPageHeaderSize = 12; |
897 | 0 | m_nLeafPageHeaderSize = 20; |
898 | |
|
899 | 0 | nMaxPerPages = (m_nPageSize - m_nLeafPageHeaderSize) / |
900 | 0 | (m_nObjectIDSize + m_nValueSize); |
901 | 0 | m_nOffsetFirstValInPage = |
902 | 0 | m_nLeafPageHeaderSize + nMaxPerPages * m_nObjectIDSize; |
903 | |
|
904 | 0 | GUInt32 nMagic1 = GetUInt32(abyTrailer + 2, 0); |
905 | 0 | returnErrorIf(nMagic1 != 1); |
906 | | |
907 | 0 | nIndexDepth = GetUInt32(abyTrailer + 6, 0); |
908 | | /* CPLDebug("OpenFileGDB", "nIndexDepth = %u", nIndexDepth); */ |
909 | 0 | returnErrorIf(!(nIndexDepth >= 1 && nIndexDepth <= MAX_DEPTH + 1)); |
910 | | |
911 | 0 | m_nValueCountInIdx = GetUInt64(abyTrailer + 10, 0); |
912 | 0 | } |
913 | | |
914 | 0 | return true; |
915 | 0 | } |
916 | | |
917 | | /************************************************************************/ |
918 | | /* FileGDBIndexIterator() */ |
919 | | /************************************************************************/ |
920 | | |
921 | | FileGDBIndexIterator::FileGDBIndexIterator(FileGDBTable *poParentIn, |
922 | | int bAscendingIn) |
923 | 0 | : FileGDBIndexIteratorBase(poParentIn, bAscendingIn), nStrLen(0) |
924 | 0 | { |
925 | 0 | memset(&sValue, 0, sizeof(sValue)); |
926 | 0 | memset(&asUTF16Str, 0, sizeof(asUTF16Str)); |
927 | 0 | memset(&szUUID, 0, sizeof(szUUID)); |
928 | 0 | memset(&sMin, 0, sizeof(sMin)); |
929 | 0 | memset(&sMax, 0, sizeof(sMax)); |
930 | 0 | memset(&szMin, 0, sizeof(szMin)); |
931 | 0 | memset(&szMax, 0, sizeof(szMax)); |
932 | 0 | } Unexecuted instantiation: OpenFileGDB::FileGDBIndexIterator::FileGDBIndexIterator(OpenFileGDB::FileGDBTable*, int) Unexecuted instantiation: OpenFileGDB::FileGDBIndexIterator::FileGDBIndexIterator(OpenFileGDB::FileGDBTable*, int) |
933 | | |
934 | | /************************************************************************/ |
935 | | /* ~FileGDBIndexIterator() */ |
936 | | /************************************************************************/ |
937 | | |
938 | | FileGDBIndexIterator::~FileGDBIndexIterator() |
939 | 0 | { |
940 | 0 | VSIFree(panSortedRows); |
941 | 0 | } |
942 | | |
943 | | /************************************************************************/ |
944 | | /* Build() */ |
945 | | /************************************************************************/ |
946 | | |
947 | | FileGDBIterator *FileGDBIndexIterator::Build(FileGDBTable *poParentIn, |
948 | | int nFieldIdx, int bAscendingIn, |
949 | | FileGDBSQLOp op, |
950 | | OGRFieldType eOGRFieldType, |
951 | | const OGRField *psValue) |
952 | 0 | { |
953 | 0 | FileGDBIndexIterator *poIndexIterator = |
954 | 0 | new FileGDBIndexIterator(poParentIn, bAscendingIn); |
955 | 0 | if (poIndexIterator->SetConstraint(nFieldIdx, op, eOGRFieldType, psValue)) |
956 | 0 | { |
957 | 0 | return poIndexIterator; |
958 | 0 | } |
959 | 0 | delete poIndexIterator; |
960 | 0 | return nullptr; |
961 | 0 | } |
962 | | |
963 | | /************************************************************************/ |
964 | | /* FileGDBSQLOpToStr() */ |
965 | | /************************************************************************/ |
966 | | |
967 | | static const char *FileGDBSQLOpToStr(FileGDBSQLOp op) |
968 | 0 | { |
969 | 0 | switch (op) |
970 | 0 | { |
971 | 0 | case FGSO_ISNOTNULL: |
972 | 0 | return "IS NOT NULL"; |
973 | 0 | case FGSO_LT: |
974 | 0 | return "<"; |
975 | 0 | case FGSO_LE: |
976 | 0 | return "<="; |
977 | 0 | case FGSO_EQ: |
978 | 0 | return "="; |
979 | 0 | case FGSO_GE: |
980 | 0 | return ">="; |
981 | 0 | case FGSO_GT: |
982 | 0 | return ">"; |
983 | 0 | case FGSO_ILIKE: |
984 | 0 | return "ILIKE"; |
985 | 0 | } |
986 | 0 | return "unknown_op"; |
987 | 0 | } |
988 | | |
989 | | /************************************************************************/ |
990 | | /* FileGDBValueToStr() */ |
991 | | /************************************************************************/ |
992 | | |
993 | | static const char *FileGDBValueToStr(OGRFieldType eOGRFieldType, |
994 | | const OGRField *psValue) |
995 | 0 | { |
996 | 0 | if (psValue == nullptr) |
997 | 0 | return ""; |
998 | | |
999 | 0 | switch (eOGRFieldType) |
1000 | 0 | { |
1001 | 0 | case OFTInteger: |
1002 | 0 | return CPLSPrintf("%d", psValue->Integer); |
1003 | 0 | case OFTReal: |
1004 | 0 | return CPLSPrintf("%.17g", psValue->Real); |
1005 | 0 | case OFTString: |
1006 | 0 | return psValue->String; |
1007 | 0 | case OFTDateTime: |
1008 | 0 | return CPLSPrintf( |
1009 | 0 | "%04d/%02d/%02d %02d:%02d:%02d", psValue->Date.Year, |
1010 | 0 | psValue->Date.Month, psValue->Date.Day, psValue->Date.Hour, |
1011 | 0 | psValue->Date.Minute, static_cast<int>(psValue->Date.Second)); |
1012 | 0 | case OFTDate: |
1013 | 0 | return CPLSPrintf("%04d/%02d/%02d", psValue->Date.Year, |
1014 | 0 | psValue->Date.Month, psValue->Date.Day); |
1015 | 0 | case OFTTime: |
1016 | 0 | return CPLSPrintf("%02d:%02d:%02d", psValue->Date.Hour, |
1017 | 0 | psValue->Date.Minute, |
1018 | 0 | static_cast<int>(psValue->Date.Second)); |
1019 | 0 | default: |
1020 | 0 | break; |
1021 | 0 | } |
1022 | 0 | return ""; |
1023 | 0 | } |
1024 | | |
1025 | | /************************************************************************/ |
1026 | | /* GetMaxWidthInBytes() */ |
1027 | | /************************************************************************/ |
1028 | | |
1029 | | int FileGDBIndex::GetMaxWidthInBytes(const FileGDBTable *poTable) const |
1030 | 0 | { |
1031 | 0 | const std::string osAtxName = CPLResetExtensionSafe( |
1032 | 0 | poTable->GetFilename().c_str(), (GetIndexName() + ".atx").c_str()); |
1033 | 0 | VSILFILE *fpCurIdx = VSIFOpenL(osAtxName.c_str(), "rb"); |
1034 | 0 | if (fpCurIdx == nullptr) |
1035 | 0 | return 0; |
1036 | | |
1037 | 0 | VSIFSeekL(fpCurIdx, 0, SEEK_END); |
1038 | 0 | vsi_l_offset nFileSize = VSIFTellL(fpCurIdx); |
1039 | |
|
1040 | 0 | constexpr int V1_TRAILER_SIZE = 22; |
1041 | 0 | constexpr int V2_TRAILER_SIZE = 30; |
1042 | |
|
1043 | 0 | if (nFileSize < FGDB_PAGE_SIZE_V1 + V1_TRAILER_SIZE) |
1044 | 0 | { |
1045 | 0 | VSIFCloseL(fpCurIdx); |
1046 | 0 | return 0; |
1047 | 0 | } |
1048 | | |
1049 | 0 | GByte abyTrailer[V2_TRAILER_SIZE]; |
1050 | 0 | VSIFSeekL(fpCurIdx, nFileSize - sizeof(uint32_t), SEEK_SET); |
1051 | 0 | if (VSIFReadL(abyTrailer, sizeof(uint32_t), 1, fpCurIdx) != 1) |
1052 | 0 | { |
1053 | 0 | VSIFCloseL(fpCurIdx); |
1054 | 0 | return 0; |
1055 | 0 | } |
1056 | 0 | const auto nVersion = GetUInt32(abyTrailer, 0); |
1057 | 0 | if (nVersion != 1 && nVersion != 2) |
1058 | 0 | { |
1059 | 0 | VSIFCloseL(fpCurIdx); |
1060 | 0 | return 0; |
1061 | 0 | } |
1062 | | |
1063 | 0 | const int nTrailerSize = nVersion == 1 ? V1_TRAILER_SIZE : V2_TRAILER_SIZE; |
1064 | |
|
1065 | 0 | if (nVersion == 2 && nFileSize < FGDB_PAGE_SIZE_V2 + V2_TRAILER_SIZE) |
1066 | 0 | { |
1067 | 0 | VSIFCloseL(fpCurIdx); |
1068 | 0 | return 0; |
1069 | 0 | } |
1070 | | |
1071 | 0 | VSIFSeekL(fpCurIdx, nFileSize - nTrailerSize, SEEK_SET); |
1072 | 0 | if (VSIFReadL(abyTrailer, nTrailerSize, 1, fpCurIdx) != 1) |
1073 | 0 | { |
1074 | 0 | VSIFCloseL(fpCurIdx); |
1075 | 0 | return 0; |
1076 | 0 | } |
1077 | | |
1078 | 0 | const int nRet = abyTrailer[0]; |
1079 | 0 | VSIFCloseL(fpCurIdx); |
1080 | 0 | return nRet; |
1081 | 0 | } |
1082 | | |
1083 | | /************************************************************************/ |
1084 | | /* SetConstraint() */ |
1085 | | /************************************************************************/ |
1086 | | |
1087 | | int FileGDBIndexIterator::SetConstraint(int nFieldIdx, FileGDBSQLOp op, |
1088 | | OGRFieldType eOGRFieldType, |
1089 | | const OGRField *psValue) |
1090 | 0 | { |
1091 | 0 | const int errorRetValue = FALSE; |
1092 | 0 | CPLAssert(fpCurIdx == nullptr); |
1093 | |
|
1094 | 0 | returnErrorIf(nFieldIdx < 0 || nFieldIdx >= poParent->GetFieldCount()); |
1095 | 0 | FileGDBField *poField = poParent->GetField(nFieldIdx); |
1096 | 0 | returnErrorIf(!(poField->HasIndex())); |
1097 | | |
1098 | 0 | eFieldType = poField->GetType(); |
1099 | 0 | eOp = op; |
1100 | |
|
1101 | 0 | returnErrorIf(eFieldType != FGFT_INT16 && eFieldType != FGFT_INT32 && |
1102 | 0 | eFieldType != FGFT_FLOAT32 && eFieldType != FGFT_FLOAT64 && |
1103 | 0 | eFieldType != FGFT_STRING && eFieldType != FGFT_DATETIME && |
1104 | 0 | eFieldType != FGFT_GUID && eFieldType != FGFT_GLOBALID && |
1105 | 0 | eFieldType != FGFT_INT64 && eFieldType != FGFT_DATE && |
1106 | 0 | eFieldType != FGFT_TIME && |
1107 | 0 | eFieldType != FGFT_DATETIME_WITH_OFFSET); |
1108 | | |
1109 | 0 | const auto poIndex = poField->GetIndex(); |
1110 | | |
1111 | | // Only supports ILIKE on a field string if the index expression starts |
1112 | | // with LOWER() and the string to compare with is only ASCII without |
1113 | | // wildcards |
1114 | 0 | if (eOGRFieldType == OFTString && |
1115 | 0 | STARTS_WITH_CI(poIndex->GetExpression().c_str(), "LOWER(")) |
1116 | 0 | { |
1117 | 0 | if (eOp == FGSO_ILIKE) |
1118 | 0 | { |
1119 | 0 | if (!CPLIsASCII(psValue->String, strlen(psValue->String)) || |
1120 | 0 | strchr(psValue->String, '%') || strchr(psValue->String, '_')) |
1121 | 0 | { |
1122 | 0 | return FALSE; |
1123 | 0 | } |
1124 | 0 | } |
1125 | 0 | else if (eOp != FGSO_ISNOTNULL) |
1126 | 0 | { |
1127 | 0 | return FALSE; |
1128 | 0 | } |
1129 | 0 | } |
1130 | 0 | else if (eOp == FGSO_ILIKE) |
1131 | 0 | { |
1132 | 0 | return FALSE; |
1133 | 0 | } |
1134 | | |
1135 | 0 | const std::string osAtxName = |
1136 | 0 | CPLFormFilenameSafe( |
1137 | 0 | CPLGetPathSafe(poParent->GetFilename().c_str()).c_str(), |
1138 | 0 | CPLGetBasenameSafe(poParent->GetFilename().c_str()).c_str(), |
1139 | 0 | poIndex->GetIndexName().c_str()) |
1140 | 0 | .append(".atx"); |
1141 | |
|
1142 | 0 | if (!ReadTrailer(osAtxName.c_str())) |
1143 | 0 | return FALSE; |
1144 | 0 | returnErrorIf(m_nValueCountInIdx > |
1145 | 0 | static_cast<GUInt64>(poParent->GetValidRecordCount())); |
1146 | | |
1147 | 0 | switch (eFieldType) |
1148 | 0 | { |
1149 | 0 | case FGFT_INT16: |
1150 | 0 | returnErrorIf(m_nValueSize != sizeof(GUInt16)); |
1151 | 0 | if (eOp != FGSO_ISNOTNULL) |
1152 | 0 | { |
1153 | 0 | returnErrorIf(eOGRFieldType != OFTInteger); |
1154 | 0 | sValue.Integer = psValue->Integer; |
1155 | 0 | } |
1156 | 0 | break; |
1157 | 0 | case FGFT_INT32: |
1158 | 0 | returnErrorIf(m_nValueSize != sizeof(GUInt32)); |
1159 | 0 | if (eOp != FGSO_ISNOTNULL) |
1160 | 0 | { |
1161 | 0 | returnErrorIf(eOGRFieldType != OFTInteger); |
1162 | 0 | sValue.Integer = psValue->Integer; |
1163 | 0 | } |
1164 | 0 | break; |
1165 | 0 | case FGFT_FLOAT32: |
1166 | 0 | returnErrorIf(m_nValueSize != sizeof(float)); |
1167 | 0 | if (eOp != FGSO_ISNOTNULL) |
1168 | 0 | { |
1169 | 0 | returnErrorIf(eOGRFieldType != OFTReal); |
1170 | 0 | sValue.Real = psValue->Real; |
1171 | 0 | } |
1172 | 0 | break; |
1173 | 0 | case FGFT_FLOAT64: |
1174 | 0 | returnErrorIf(m_nValueSize != sizeof(double)); |
1175 | 0 | if (eOp != FGSO_ISNOTNULL) |
1176 | 0 | { |
1177 | 0 | returnErrorIf(eOGRFieldType != OFTReal); |
1178 | 0 | sValue.Real = psValue->Real; |
1179 | 0 | } |
1180 | 0 | break; |
1181 | 0 | case FGFT_STRING: |
1182 | 0 | { |
1183 | 0 | returnErrorIf((m_nValueSize % 2) != 0); |
1184 | 0 | returnErrorIf(m_nValueSize == 0); |
1185 | 0 | returnErrorIf(m_nValueSize > 2 * MAX_CAR_COUNT_INDEXED_STR); |
1186 | 0 | nStrLen = m_nValueSize / 2; |
1187 | 0 | if (eOp != FGSO_ISNOTNULL) |
1188 | 0 | { |
1189 | 0 | returnErrorIf(eOGRFieldType != OFTString); |
1190 | 0 | wchar_t *pWide = CPLRecodeToWChar(psValue->String, CPL_ENC_UTF8, |
1191 | 0 | CPL_ENC_UCS2); |
1192 | 0 | returnErrorIf(pWide == nullptr); |
1193 | 0 | int nCount = 0; |
1194 | 0 | while (pWide[nCount] != 0) |
1195 | 0 | { |
1196 | 0 | returnErrorAndCleanupIf(nCount == nStrLen, CPLFree(pWide)); |
1197 | 0 | asUTF16Str[nCount] = pWide[nCount]; |
1198 | 0 | nCount++; |
1199 | 0 | } |
1200 | 0 | while (nCount < nStrLen) |
1201 | 0 | { |
1202 | 0 | asUTF16Str[nCount] = 32; /* space character */ |
1203 | 0 | nCount++; |
1204 | 0 | } |
1205 | 0 | CPLFree(pWide); |
1206 | 0 | } |
1207 | 0 | break; |
1208 | 0 | } |
1209 | | |
1210 | 0 | case FGFT_DATETIME: |
1211 | 0 | case FGFT_DATE: |
1212 | 0 | case FGFT_DATETIME_WITH_OFFSET: |
1213 | 0 | { |
1214 | 0 | returnErrorIf(m_nValueSize != sizeof(double)); |
1215 | 0 | if (eOp != FGSO_ISNOTNULL) |
1216 | 0 | { |
1217 | 0 | returnErrorIf( |
1218 | 0 | eOGRFieldType != OFTReal && eOGRFieldType != OFTDateTime && |
1219 | 0 | eOGRFieldType != OFTDate && eOGRFieldType != OFTTime); |
1220 | 0 | if (eOGRFieldType == OFTReal) |
1221 | 0 | sValue.Real = psValue->Real; |
1222 | 0 | else |
1223 | 0 | sValue.Real = FileGDBOGRDateToDoubleDate( |
1224 | 0 | psValue, true, |
1225 | 0 | /* bHighPrecision= */ eFieldType == |
1226 | 0 | FGFT_DATETIME_WITH_OFFSET || |
1227 | 0 | poField->IsHighPrecision()); |
1228 | 0 | } |
1229 | 0 | break; |
1230 | 0 | } |
1231 | | |
1232 | 0 | case FGFT_GUID: |
1233 | 0 | case FGFT_GLOBALID: |
1234 | 0 | { |
1235 | 0 | returnErrorIf(m_nValueSize != UUID_LEN_AS_STRING); |
1236 | 0 | if (eOp != FGSO_ISNOTNULL) |
1237 | 0 | { |
1238 | 0 | returnErrorIf(eOGRFieldType != OFTString); |
1239 | 0 | memset(szUUID, 0, UUID_LEN_AS_STRING + 1); |
1240 | | // cppcheck-suppress redundantCopy |
1241 | 0 | strncpy(szUUID, psValue->String, UUID_LEN_AS_STRING); |
1242 | 0 | bEvaluateToFALSE = eOp == FGSO_EQ && |
1243 | 0 | strlen(psValue->String) != |
1244 | 0 | static_cast<size_t>(UUID_LEN_AS_STRING); |
1245 | 0 | } |
1246 | 0 | break; |
1247 | 0 | } |
1248 | | |
1249 | 0 | case FGFT_INT64: |
1250 | 0 | returnErrorIf(m_nValueSize != sizeof(int64_t)); |
1251 | 0 | if (eOp != FGSO_ISNOTNULL) |
1252 | 0 | { |
1253 | 0 | returnErrorIf(eOGRFieldType != OFTInteger64); |
1254 | 0 | sValue.Integer64 = psValue->Integer64; |
1255 | 0 | } |
1256 | 0 | break; |
1257 | | |
1258 | 0 | case FGFT_TIME: |
1259 | 0 | { |
1260 | 0 | returnErrorIf(m_nValueSize != sizeof(double)); |
1261 | 0 | if (eOp != FGSO_ISNOTNULL) |
1262 | 0 | { |
1263 | 0 | returnErrorIf(eOGRFieldType != OFTReal && |
1264 | 0 | eOGRFieldType != OFTTime); |
1265 | 0 | if (eOGRFieldType == OFTReal) |
1266 | 0 | sValue.Real = psValue->Real; |
1267 | 0 | else |
1268 | 0 | sValue.Real = FileGDBOGRTimeToDoubleTime(psValue); |
1269 | 0 | } |
1270 | 0 | break; |
1271 | 0 | } |
1272 | | |
1273 | 0 | default: |
1274 | 0 | CPLAssert(false); |
1275 | 0 | break; |
1276 | 0 | } |
1277 | | |
1278 | 0 | if (m_nValueCountInIdx > 0) |
1279 | 0 | { |
1280 | 0 | if (nIndexDepth == 1) |
1281 | 0 | { |
1282 | 0 | iFirstPageIdx[0] = iLastPageIdx[0] = 0; |
1283 | 0 | } |
1284 | 0 | else |
1285 | 0 | { |
1286 | 0 | returnErrorIf(!FindPages(0, 1)); |
1287 | 0 | } |
1288 | 0 | } |
1289 | | |
1290 | | // To avoid 'spamming' on huge raster files |
1291 | 0 | if (poField->GetName() != "block_key") |
1292 | 0 | { |
1293 | 0 | CPLDebug("OpenFileGDB", "Using index on field %s (%s %s)", |
1294 | 0 | poField->GetName().c_str(), FileGDBSQLOpToStr(eOp), |
1295 | 0 | FileGDBValueToStr(eOGRFieldType, psValue)); |
1296 | 0 | } |
1297 | |
|
1298 | 0 | Reset(); |
1299 | |
|
1300 | 0 | return TRUE; |
1301 | 0 | } |
1302 | | |
1303 | | /************************************************************************/ |
1304 | | /* FileGDBUTF16StrCompare() */ |
1305 | | /************************************************************************/ |
1306 | | |
1307 | | static int FileGDBUTF16StrCompare(const GUInt16 *pasFirst, |
1308 | | const GUInt16 *pasSecond, int nStrLen, |
1309 | | bool bCaseInsensitive) |
1310 | 0 | { |
1311 | 0 | for (int i = 0; i < nStrLen; i++) |
1312 | 0 | { |
1313 | 0 | GUInt16 chA = pasFirst[i]; |
1314 | 0 | GUInt16 chB = pasSecond[i]; |
1315 | 0 | if (bCaseInsensitive) |
1316 | 0 | { |
1317 | 0 | if (chA >= 'a' && chA <= 'z') |
1318 | 0 | chA -= 'a' - 'A'; |
1319 | 0 | if (chB >= 'a' && chB <= 'z') |
1320 | 0 | chB -= 'a' - 'A'; |
1321 | 0 | } |
1322 | 0 | if (chA < chB) |
1323 | 0 | return -1; |
1324 | 0 | if (chA > chB) |
1325 | 0 | return 1; |
1326 | 0 | } |
1327 | 0 | return 0; |
1328 | 0 | } |
1329 | | |
1330 | | /************************************************************************/ |
1331 | | /* COMPARE() */ |
1332 | | /************************************************************************/ |
1333 | | |
1334 | 0 | #define COMPARE(a, b) (((a) < (b)) ? -1 : ((a) == (b)) ? 0 : 1) |
1335 | | |
1336 | | /************************************************************************/ |
1337 | | /* FindPages() */ |
1338 | | /************************************************************************/ |
1339 | | |
1340 | | bool FileGDBIndexIterator::FindPages(int iLevel, uint64_t nPage) |
1341 | 0 | { |
1342 | 0 | const bool errorRetValue = false; |
1343 | 0 | VSIFSeekL(fpCurIdx, static_cast<vsi_l_offset>(nPage - 1) * m_nPageSize, |
1344 | 0 | SEEK_SET); |
1345 | | #ifdef DEBUG |
1346 | | iLoadedPage[iLevel] = nPage; |
1347 | | #endif |
1348 | 0 | returnErrorIf(VSIFReadL(abyPage[iLevel], m_nPageSize, 1, fpCurIdx) != 1); |
1349 | | |
1350 | 0 | nSubPagesCount[iLevel] = GetUInt32(abyPage[iLevel] + m_nObjectIDSize, 0); |
1351 | 0 | returnErrorIf(nSubPagesCount[iLevel] == 0 || |
1352 | 0 | nSubPagesCount[iLevel] > nMaxPerPages); |
1353 | 0 | if (nIndexDepth == 2) |
1354 | 0 | returnErrorIf(m_nValueCountInIdx > static_cast<uint64_t>(nMaxPerPages) * |
1355 | 0 | (nSubPagesCount[0] + 1)); |
1356 | | |
1357 | 0 | if (eOp == FGSO_ISNOTNULL) |
1358 | 0 | { |
1359 | 0 | iFirstPageIdx[iLevel] = 0; |
1360 | 0 | iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; |
1361 | 0 | return true; |
1362 | 0 | } |
1363 | | |
1364 | 0 | GUInt32 i; |
1365 | | #ifdef DEBUG_INDEX_CONSISTENCY |
1366 | | double dfLastMax = 0.0; |
1367 | | int nLastMax = 0; |
1368 | | GUInt16 asLastMax[MAX_CAR_COUNT_INDEXED_STR] = {0}; |
1369 | | char szLastMaxUUID[UUID_LEN_AS_STRING + 1] = {0}; |
1370 | | #endif |
1371 | 0 | iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = -1; |
1372 | |
|
1373 | 0 | for (i = 0; i < nSubPagesCount[iLevel]; i++) |
1374 | 0 | { |
1375 | 0 | int nComp; |
1376 | |
|
1377 | 0 | switch (eFieldType) |
1378 | 0 | { |
1379 | 0 | case FGFT_INT16: |
1380 | 0 | { |
1381 | 0 | GInt16 nVal = |
1382 | 0 | GetInt16(abyPage[iLevel] + m_nOffsetFirstValInPage, i); |
1383 | | #ifdef DEBUG_INDEX_CONSISTENCY |
1384 | | returnErrorIf(i > 0 && nVal < nLastMax); |
1385 | | nLastMax = nVal; |
1386 | | #endif |
1387 | 0 | nComp = COMPARE(sValue.Integer, nVal); |
1388 | 0 | break; |
1389 | 0 | } |
1390 | | |
1391 | 0 | case FGFT_INT32: |
1392 | 0 | { |
1393 | 0 | GInt32 nVal = |
1394 | 0 | GetInt32(abyPage[iLevel] + m_nOffsetFirstValInPage, i); |
1395 | | #ifdef DEBUG_INDEX_CONSISTENCY |
1396 | | returnErrorIf(i > 0 && nVal < nLastMax); |
1397 | | nLastMax = nVal; |
1398 | | #endif |
1399 | 0 | nComp = COMPARE(sValue.Integer, nVal); |
1400 | 0 | break; |
1401 | 0 | } |
1402 | | |
1403 | 0 | case FGFT_INT64: |
1404 | 0 | { |
1405 | 0 | int64_t nVal = |
1406 | 0 | GetInt64(abyPage[iLevel] + m_nOffsetFirstValInPage, i); |
1407 | | #ifdef DEBUG_INDEX_CONSISTENCY |
1408 | | returnErrorIf(i > 0 && nVal < nLastMax); |
1409 | | nLastMax = nVal; |
1410 | | #endif |
1411 | 0 | nComp = COMPARE(sValue.Integer64, nVal); |
1412 | 0 | break; |
1413 | 0 | } |
1414 | | |
1415 | 0 | case FGFT_FLOAT32: |
1416 | 0 | { |
1417 | 0 | float fVal = |
1418 | 0 | GetFloat32(abyPage[iLevel] + m_nOffsetFirstValInPage, i); |
1419 | | #ifdef DEBUG_INDEX_CONSISTENCY |
1420 | | returnErrorIf(i > 0 && fVal < dfLastMax); |
1421 | | dfLastMax = fVal; |
1422 | | #endif |
1423 | 0 | nComp = COMPARE(sValue.Real, fVal); |
1424 | 0 | break; |
1425 | 0 | } |
1426 | | |
1427 | 0 | case FGFT_FLOAT64: |
1428 | 0 | { |
1429 | 0 | const double dfVal = |
1430 | 0 | GetFloat64(abyPage[iLevel] + m_nOffsetFirstValInPage, i); |
1431 | | #ifdef DEBUG_INDEX_CONSISTENCY |
1432 | | returnErrorIf(i > 0 && dfVal < dfLastMax); |
1433 | | dfLastMax = dfVal; |
1434 | | #endif |
1435 | 0 | nComp = COMPARE(sValue.Real, dfVal); |
1436 | 0 | break; |
1437 | 0 | } |
1438 | | |
1439 | 0 | case FGFT_DATETIME: |
1440 | 0 | case FGFT_DATE: |
1441 | 0 | case FGFT_TIME: |
1442 | 0 | { |
1443 | 0 | const double dfVal = |
1444 | 0 | GetFloat64(abyPage[iLevel] + m_nOffsetFirstValInPage, i); |
1445 | | #ifdef DEBUG_INDEX_CONSISTENCY |
1446 | | returnErrorIf(i > 0 && dfVal < dfLastMax); |
1447 | | dfLastMax = dfVal; |
1448 | | #endif |
1449 | 0 | if (sValue.Real + 1e-10 < dfVal) |
1450 | 0 | nComp = -1; |
1451 | 0 | else if (sValue.Real - 1e-10 > dfVal) |
1452 | 0 | nComp = 1; |
1453 | 0 | else |
1454 | 0 | nComp = 0; |
1455 | 0 | break; |
1456 | 0 | } |
1457 | | |
1458 | 0 | case FGFT_STRING: |
1459 | 0 | { |
1460 | 0 | GUInt16 *pasMax; |
1461 | 0 | GUInt16 asMax[MAX_CAR_COUNT_INDEXED_STR]; |
1462 | 0 | pasMax = asMax; |
1463 | 0 | memcpy(asMax, |
1464 | 0 | abyPage[iLevel] + m_nOffsetFirstValInPage + |
1465 | 0 | nStrLen * sizeof(GUInt16) * i, |
1466 | 0 | nStrLen * sizeof(GUInt16)); |
1467 | 0 | for (int j = 0; j < nStrLen; j++) |
1468 | 0 | CPL_LSBPTR16(&asMax[j]); |
1469 | | // Note: we have an inconsistency. OGR SQL equality operator |
1470 | | // is advertized to be case insensitive, but we have always |
1471 | | // implemented FGSO_EQ as case sensitive. |
1472 | | #ifdef DEBUG_INDEX_CONSISTENCY |
1473 | | returnErrorIf(i > 0 && |
1474 | | FileGDBUTF16StrCompare(pasMax, asLastMax, nStrLen, |
1475 | | eOp == FGSO_ILIKE) < 0); |
1476 | | memcpy(asLastMax, pasMax, nStrLen * 2); |
1477 | | #endif |
1478 | 0 | nComp = FileGDBUTF16StrCompare(asUTF16Str, pasMax, nStrLen, |
1479 | 0 | eOp == FGSO_ILIKE); |
1480 | 0 | break; |
1481 | 0 | } |
1482 | | |
1483 | 0 | case FGFT_GUID: |
1484 | 0 | case FGFT_GLOBALID: |
1485 | 0 | { |
1486 | 0 | const char *psNonzMaxUUID = reinterpret_cast<char *>( |
1487 | 0 | abyPage[iLevel] + m_nOffsetFirstValInPage + |
1488 | 0 | UUID_LEN_AS_STRING * i); |
1489 | | #ifdef DEBUG_INDEX_CONSISTENCY |
1490 | | returnErrorIf(i > 0 && memcmp(psNonzMaxUUID, szLastMaxUUID, |
1491 | | UUID_LEN_AS_STRING) < 0); |
1492 | | memcpy(szLastMaxUUID, psNonzMaxUUID, UUID_LEN_AS_STRING); |
1493 | | #endif |
1494 | 0 | nComp = memcmp(szUUID, psNonzMaxUUID, UUID_LEN_AS_STRING); |
1495 | 0 | break; |
1496 | 0 | } |
1497 | | |
1498 | 0 | default: |
1499 | 0 | CPLAssert(false); |
1500 | 0 | nComp = 0; |
1501 | 0 | break; |
1502 | 0 | } |
1503 | | |
1504 | 0 | int bStop = FALSE; |
1505 | 0 | switch (eOp) |
1506 | 0 | { |
1507 | | /* dfVal = 1 2 2 3 3 4 */ |
1508 | | /* sValue.Real = 3 */ |
1509 | | /* nComp = (sValue.Real < dfVal) ? -1 : (sValue.Real == dfVal) ? 0 : |
1510 | | * 1; */ |
1511 | 0 | case FGSO_LT: |
1512 | 0 | case FGSO_LE: |
1513 | 0 | if (iFirstPageIdx[iLevel] < 0) |
1514 | 0 | { |
1515 | 0 | iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = |
1516 | 0 | static_cast<int>(i); |
1517 | 0 | } |
1518 | 0 | else |
1519 | 0 | { |
1520 | 0 | iLastPageIdx[iLevel] = static_cast<int>(i); |
1521 | 0 | if (nComp < 0) |
1522 | 0 | { |
1523 | 0 | bStop = TRUE; |
1524 | 0 | } |
1525 | 0 | } |
1526 | 0 | break; |
1527 | | |
1528 | 0 | case FGSO_EQ: |
1529 | 0 | case FGSO_ILIKE: |
1530 | 0 | if (iFirstPageIdx[iLevel] < 0) |
1531 | 0 | { |
1532 | 0 | if (nComp <= 0) |
1533 | 0 | iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = |
1534 | 0 | static_cast<int>(i); |
1535 | 0 | } |
1536 | 0 | else |
1537 | 0 | { |
1538 | 0 | if (nComp == 0) |
1539 | 0 | iLastPageIdx[iLevel] = static_cast<int>(i); |
1540 | 0 | else |
1541 | 0 | bStop = TRUE; |
1542 | 0 | } |
1543 | 0 | break; |
1544 | | |
1545 | 0 | case FGSO_GE: |
1546 | 0 | if (iFirstPageIdx[iLevel] < 0) |
1547 | 0 | { |
1548 | 0 | if (nComp <= 0) |
1549 | 0 | { |
1550 | 0 | iFirstPageIdx[iLevel] = static_cast<int>(i); |
1551 | 0 | iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; |
1552 | 0 | bStop = TRUE; |
1553 | 0 | } |
1554 | 0 | } |
1555 | 0 | break; |
1556 | | |
1557 | 0 | case FGSO_GT: |
1558 | 0 | if (iFirstPageIdx[iLevel] < 0) |
1559 | 0 | { |
1560 | 0 | if (nComp < 0) |
1561 | 0 | { |
1562 | 0 | iFirstPageIdx[iLevel] = static_cast<int>(i); |
1563 | 0 | iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; |
1564 | 0 | bStop = TRUE; |
1565 | 0 | } |
1566 | 0 | } |
1567 | 0 | break; |
1568 | | |
1569 | 0 | case FGSO_ISNOTNULL: |
1570 | 0 | CPLAssert(false); |
1571 | 0 | break; |
1572 | 0 | } |
1573 | 0 | if (bStop) |
1574 | 0 | break; |
1575 | 0 | } |
1576 | | |
1577 | 0 | if (iFirstPageIdx[iLevel] < 0) |
1578 | 0 | { |
1579 | 0 | iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; |
1580 | 0 | } |
1581 | 0 | else if (iLastPageIdx[iLevel] < static_cast<int>(nSubPagesCount[iLevel])) |
1582 | 0 | { |
1583 | 0 | iLastPageIdx[iLevel]++; |
1584 | 0 | } |
1585 | |
|
1586 | 0 | return true; |
1587 | 0 | } |
1588 | | |
1589 | | /************************************************************************/ |
1590 | | /* Reset() */ |
1591 | | /************************************************************************/ |
1592 | | |
1593 | | void FileGDBIndexIteratorBase::Reset() |
1594 | 0 | { |
1595 | 0 | iCurPageIdx[0] = (bAscending) ? iFirstPageIdx[0] - 1 : iLastPageIdx[0] + 1; |
1596 | 0 | memset(iFirstPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(iFirstPageIdx[0])); |
1597 | 0 | memset(iLastPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(iLastPageIdx[0])); |
1598 | 0 | memset(iCurPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(iCurPageIdx[0])); |
1599 | 0 | memset(nLastPageAccessed, 0, MAX_DEPTH * sizeof(nLastPageAccessed[0])); |
1600 | 0 | iCurFeatureInPage = 0; |
1601 | 0 | nFeaturesInPage = 0; |
1602 | |
|
1603 | 0 | bEOF = (m_nValueCountInIdx == 0); |
1604 | 0 | } |
1605 | | |
1606 | | /************************************************************************/ |
1607 | | /* Reset() */ |
1608 | | /************************************************************************/ |
1609 | | |
1610 | | void FileGDBIndexIterator::Reset() |
1611 | 0 | { |
1612 | 0 | FileGDBIndexIteratorBase::Reset(); |
1613 | 0 | iSorted = 0; |
1614 | 0 | bEOF = bEOF || bEvaluateToFALSE; |
1615 | 0 | } |
1616 | | |
1617 | | /************************************************************************/ |
1618 | | /* ReadPageNumber() */ |
1619 | | /************************************************************************/ |
1620 | | |
1621 | | uint64_t FileGDBIndexIteratorBase::ReadPageNumber(int iLevel) |
1622 | 0 | { |
1623 | 0 | const int errorRetValue = 0; |
1624 | 0 | uint64_t nPage; |
1625 | 0 | if (m_nVersion == 1) |
1626 | 0 | { |
1627 | 0 | nPage = GetUInt32(abyPage[iLevel] + m_nNonLeafPageHeaderSize, |
1628 | 0 | iCurPageIdx[iLevel]); |
1629 | 0 | if (nPage == nLastPageAccessed[iLevel]) |
1630 | 0 | { |
1631 | 0 | if (!LoadNextPage(iLevel)) |
1632 | 0 | return 0; |
1633 | 0 | nPage = GetUInt32(abyPage[iLevel] + m_nNonLeafPageHeaderSize, |
1634 | 0 | iCurPageIdx[iLevel]); |
1635 | 0 | } |
1636 | 0 | } |
1637 | 0 | else |
1638 | 0 | { |
1639 | 0 | nPage = GetUInt64(abyPage[iLevel] + m_nNonLeafPageHeaderSize, |
1640 | 0 | iCurPageIdx[iLevel]); |
1641 | 0 | if (nPage == nLastPageAccessed[iLevel]) |
1642 | 0 | { |
1643 | 0 | if (!LoadNextPage(iLevel)) |
1644 | 0 | return 0; |
1645 | 0 | nPage = GetUInt64(abyPage[iLevel] + m_nNonLeafPageHeaderSize, |
1646 | 0 | iCurPageIdx[iLevel]); |
1647 | 0 | } |
1648 | 0 | } |
1649 | 0 | nLastPageAccessed[iLevel] = nPage; |
1650 | 0 | returnErrorIf(nPage < 2); |
1651 | 0 | return nPage; |
1652 | 0 | } |
1653 | | |
1654 | | /************************************************************************/ |
1655 | | /* LoadNextPage() */ |
1656 | | /************************************************************************/ |
1657 | | |
1658 | | bool FileGDBIndexIteratorBase::LoadNextPage(int iLevel) |
1659 | 0 | { |
1660 | 0 | const bool errorRetValue = false; |
1661 | 0 | if ((bAscending && iCurPageIdx[iLevel] == iLastPageIdx[iLevel]) || |
1662 | 0 | (!bAscending && iCurPageIdx[iLevel] == iFirstPageIdx[iLevel])) |
1663 | 0 | { |
1664 | 0 | if (iLevel == 0 || !LoadNextPage(iLevel - 1)) |
1665 | 0 | return false; |
1666 | | |
1667 | 0 | const auto nPage = ReadPageNumber(iLevel - 1); |
1668 | 0 | returnErrorIf(!FindPages(iLevel, nPage)); |
1669 | | |
1670 | 0 | iCurPageIdx[iLevel] = |
1671 | 0 | (bAscending) ? iFirstPageIdx[iLevel] : iLastPageIdx[iLevel]; |
1672 | 0 | } |
1673 | 0 | else |
1674 | 0 | { |
1675 | 0 | if (bAscending) |
1676 | 0 | iCurPageIdx[iLevel]++; |
1677 | 0 | else |
1678 | 0 | iCurPageIdx[iLevel]--; |
1679 | 0 | } |
1680 | | |
1681 | 0 | return true; |
1682 | 0 | } |
1683 | | |
1684 | | /************************************************************************/ |
1685 | | /* LoadNextFeaturePage() */ |
1686 | | /************************************************************************/ |
1687 | | |
1688 | | bool FileGDBIndexIteratorBase::LoadNextFeaturePage() |
1689 | 0 | { |
1690 | 0 | const bool errorRetValue = false; |
1691 | 0 | GUInt64 nPage; |
1692 | |
|
1693 | 0 | if (nIndexDepth == 1) |
1694 | 0 | { |
1695 | 0 | if (iCurPageIdx[0] == iLastPageIdx[0]) |
1696 | 0 | { |
1697 | 0 | return false; |
1698 | 0 | } |
1699 | 0 | if (bAscending) |
1700 | 0 | iCurPageIdx[0]++; |
1701 | 0 | else |
1702 | 0 | iCurPageIdx[0]--; |
1703 | 0 | nPage = 1; |
1704 | 0 | } |
1705 | 0 | else |
1706 | 0 | { |
1707 | 0 | if (!LoadNextPage(nIndexDepth - 2)) |
1708 | 0 | { |
1709 | 0 | return false; |
1710 | 0 | } |
1711 | 0 | nPage = ReadPageNumber(nIndexDepth - 2); |
1712 | 0 | returnErrorIf(nPage < 2); |
1713 | 0 | } |
1714 | | |
1715 | 0 | const cpl::NonCopyableVector<GByte> *cachedPagePtr = |
1716 | 0 | m_oCacheFeaturePage.getPtr(nPage); |
1717 | 0 | if (cachedPagePtr) |
1718 | 0 | { |
1719 | 0 | memcpy(abyPageFeature, cachedPagePtr->data(), m_nPageSize); |
1720 | 0 | } |
1721 | 0 | else |
1722 | 0 | { |
1723 | 0 | cpl::NonCopyableVector<GByte> cachedPage; |
1724 | 0 | if (m_oCacheFeaturePage.size() == m_oCacheFeaturePage.getMaxSize()) |
1725 | 0 | { |
1726 | 0 | m_oCacheFeaturePage.removeAndRecycleOldestEntry(cachedPage); |
1727 | 0 | cachedPage.clear(); |
1728 | 0 | } |
1729 | |
|
1730 | 0 | VSIFSeekL(fpCurIdx, static_cast<vsi_l_offset>(nPage - 1) * m_nPageSize, |
1731 | 0 | SEEK_SET); |
1732 | | #ifdef DEBUG |
1733 | | iLoadedPage[nIndexDepth - 1] = nPage; |
1734 | | #endif |
1735 | 0 | returnErrorIf(VSIFReadL(abyPageFeature, m_nPageSize, 1, fpCurIdx) != 1); |
1736 | 0 | cachedPage.insert(cachedPage.end(), abyPageFeature, |
1737 | 0 | abyPageFeature + m_nPageSize); |
1738 | 0 | m_oCacheFeaturePage.insert(nPage, std::move(cachedPage)); |
1739 | 0 | } |
1740 | | |
1741 | 0 | const GUInt32 nFeatures = GetUInt32(abyPageFeature + m_nObjectIDSize, 0); |
1742 | 0 | returnErrorIf(nFeatures > nMaxPerPages); |
1743 | | |
1744 | 0 | nFeaturesInPage = static_cast<int>(nFeatures); |
1745 | 0 | iCurFeatureInPage = (bAscending) ? 0 : nFeaturesInPage - 1; |
1746 | 0 | return nFeatures != 0; |
1747 | 0 | } |
1748 | | |
1749 | | /************************************************************************/ |
1750 | | /* GetNextRow() */ |
1751 | | /************************************************************************/ |
1752 | | |
1753 | | int64_t FileGDBIndexIterator::GetNextRow() |
1754 | 0 | { |
1755 | 0 | const int64_t errorRetValue = -1; |
1756 | 0 | if (bEOF) |
1757 | 0 | return -1; |
1758 | | |
1759 | 0 | while (true) |
1760 | 0 | { |
1761 | 0 | if (iCurFeatureInPage >= nFeaturesInPage || iCurFeatureInPage < 0) |
1762 | 0 | { |
1763 | 0 | if (!LoadNextFeaturePage()) |
1764 | 0 | { |
1765 | 0 | bEOF = true; |
1766 | 0 | return -1; |
1767 | 0 | } |
1768 | 0 | } |
1769 | | |
1770 | 0 | bool bMatch = false; |
1771 | 0 | if (eOp == FGSO_ISNOTNULL) |
1772 | 0 | { |
1773 | 0 | bMatch = true; |
1774 | 0 | } |
1775 | 0 | else |
1776 | 0 | { |
1777 | 0 | int nComp = 0; |
1778 | 0 | switch (eFieldType) |
1779 | 0 | { |
1780 | 0 | case FGFT_INT16: |
1781 | 0 | { |
1782 | 0 | const GInt16 nVal = |
1783 | 0 | GetInt16(abyPageFeature + m_nOffsetFirstValInPage, |
1784 | 0 | iCurFeatureInPage); |
1785 | 0 | nComp = COMPARE(sValue.Integer, nVal); |
1786 | 0 | break; |
1787 | 0 | } |
1788 | | |
1789 | 0 | case FGFT_INT32: |
1790 | 0 | { |
1791 | 0 | const GInt32 nVal = |
1792 | 0 | GetInt32(abyPageFeature + m_nOffsetFirstValInPage, |
1793 | 0 | iCurFeatureInPage); |
1794 | 0 | nComp = COMPARE(sValue.Integer, nVal); |
1795 | 0 | break; |
1796 | 0 | } |
1797 | | |
1798 | 0 | case FGFT_FLOAT32: |
1799 | 0 | { |
1800 | 0 | const float fVal = |
1801 | 0 | GetFloat32(abyPageFeature + m_nOffsetFirstValInPage, |
1802 | 0 | iCurFeatureInPage); |
1803 | 0 | nComp = COMPARE(sValue.Real, fVal); |
1804 | 0 | break; |
1805 | 0 | } |
1806 | | |
1807 | 0 | case FGFT_FLOAT64: |
1808 | 0 | { |
1809 | 0 | const double dfVal = |
1810 | 0 | GetFloat64(abyPageFeature + m_nOffsetFirstValInPage, |
1811 | 0 | iCurFeatureInPage); |
1812 | 0 | nComp = COMPARE(sValue.Real, dfVal); |
1813 | 0 | break; |
1814 | 0 | } |
1815 | | |
1816 | 0 | case FGFT_DATETIME: |
1817 | 0 | case FGFT_DATE: |
1818 | 0 | case FGFT_TIME: |
1819 | 0 | case FGFT_DATETIME_WITH_OFFSET: |
1820 | 0 | { |
1821 | 0 | const double dfVal = |
1822 | 0 | GetFloat64(abyPageFeature + m_nOffsetFirstValInPage, |
1823 | 0 | iCurFeatureInPage); |
1824 | 0 | if (sValue.Real + 1e-10 < dfVal) |
1825 | 0 | nComp = -1; |
1826 | 0 | else if (sValue.Real - 1e-10 > dfVal) |
1827 | 0 | nComp = 1; |
1828 | 0 | else |
1829 | 0 | nComp = 0; |
1830 | 0 | break; |
1831 | 0 | } |
1832 | | |
1833 | 0 | case FGFT_STRING: |
1834 | 0 | { |
1835 | 0 | GUInt16 asVal[MAX_CAR_COUNT_INDEXED_STR]; |
1836 | 0 | memcpy(asVal, |
1837 | 0 | abyPageFeature + m_nOffsetFirstValInPage + |
1838 | 0 | nStrLen * 2 * iCurFeatureInPage, |
1839 | 0 | nStrLen * 2); |
1840 | 0 | for (int j = 0; j < nStrLen; j++) |
1841 | 0 | CPL_LSBPTR16(&asVal[j]); |
1842 | | // Note: we have an inconsistency. OGR SQL equality operator |
1843 | | // is advertized to be case insensitive, but we have always |
1844 | | // implemented FGSO_EQ as case sensitive. |
1845 | 0 | nComp = FileGDBUTF16StrCompare(asUTF16Str, asVal, nStrLen, |
1846 | 0 | eOp == FGSO_ILIKE); |
1847 | 0 | break; |
1848 | 0 | } |
1849 | | |
1850 | 0 | case FGFT_GUID: |
1851 | 0 | case FGFT_GLOBALID: |
1852 | 0 | { |
1853 | 0 | nComp = memcmp(szUUID, |
1854 | 0 | abyPageFeature + m_nOffsetFirstValInPage + |
1855 | 0 | UUID_LEN_AS_STRING * iCurFeatureInPage, |
1856 | 0 | UUID_LEN_AS_STRING); |
1857 | 0 | break; |
1858 | 0 | } |
1859 | | |
1860 | 0 | case FGFT_INT64: |
1861 | 0 | { |
1862 | 0 | const int64_t nVal = |
1863 | 0 | GetInt64(abyPageFeature + m_nOffsetFirstValInPage, |
1864 | 0 | iCurFeatureInPage); |
1865 | 0 | nComp = COMPARE(sValue.Integer64, nVal); |
1866 | 0 | break; |
1867 | 0 | } |
1868 | | |
1869 | 0 | default: |
1870 | 0 | CPLAssert(false); |
1871 | 0 | nComp = 0; |
1872 | 0 | break; |
1873 | 0 | } |
1874 | | |
1875 | 0 | bMatch = false; |
1876 | 0 | CPL_IGNORE_RET_VAL(bMatch); |
1877 | 0 | switch (eOp) |
1878 | 0 | { |
1879 | 0 | case FGSO_LT: |
1880 | 0 | if (nComp <= 0 && bAscending) |
1881 | 0 | { |
1882 | 0 | bEOF = true; |
1883 | 0 | return -1; |
1884 | 0 | } |
1885 | 0 | bMatch = true; |
1886 | 0 | break; |
1887 | | |
1888 | 0 | case FGSO_LE: |
1889 | 0 | if (nComp < 0 && bAscending) |
1890 | 0 | { |
1891 | 0 | bEOF = true; |
1892 | 0 | return -1; |
1893 | 0 | } |
1894 | 0 | bMatch = true; |
1895 | 0 | break; |
1896 | | |
1897 | 0 | case FGSO_EQ: |
1898 | 0 | case FGSO_ILIKE: |
1899 | 0 | if (nComp < 0 && bAscending) |
1900 | 0 | { |
1901 | 0 | bEOF = true; |
1902 | 0 | return -1; |
1903 | 0 | } |
1904 | 0 | bMatch = nComp == 0; |
1905 | 0 | break; |
1906 | | |
1907 | 0 | case FGSO_GE: |
1908 | 0 | bMatch = nComp <= 0; |
1909 | 0 | break; |
1910 | | |
1911 | 0 | case FGSO_GT: |
1912 | 0 | bMatch = nComp < 0; |
1913 | 0 | break; |
1914 | | |
1915 | 0 | case FGSO_ISNOTNULL: |
1916 | 0 | CPLAssert(false); |
1917 | 0 | break; |
1918 | 0 | } |
1919 | 0 | } |
1920 | | |
1921 | 0 | if (bMatch) |
1922 | 0 | { |
1923 | 0 | const GUInt64 nFID = |
1924 | 0 | m_nVersion == 1 |
1925 | 0 | ? GetUInt32(abyPageFeature + m_nLeafPageHeaderSize, |
1926 | 0 | iCurFeatureInPage) |
1927 | 0 | : GetUInt64(abyPageFeature + m_nLeafPageHeaderSize, |
1928 | 0 | iCurFeatureInPage); |
1929 | 0 | if (bAscending) |
1930 | 0 | iCurFeatureInPage++; |
1931 | 0 | else |
1932 | 0 | iCurFeatureInPage--; |
1933 | 0 | returnErrorAndCleanupIf( |
1934 | 0 | nFID < 1 || nFID > static_cast<GUInt64>( |
1935 | 0 | poParent->GetTotalRecordCount()), |
1936 | 0 | bEOF = true); |
1937 | 0 | return static_cast<int64_t>(nFID - 1); |
1938 | 0 | } |
1939 | 0 | else |
1940 | 0 | { |
1941 | 0 | if (bAscending) |
1942 | 0 | iCurFeatureInPage++; |
1943 | 0 | else |
1944 | 0 | iCurFeatureInPage--; |
1945 | 0 | } |
1946 | 0 | } |
1947 | 0 | } |
1948 | | |
1949 | | /************************************************************************/ |
1950 | | /* SortRows() */ |
1951 | | /************************************************************************/ |
1952 | | |
1953 | | int FileGDBIndexIterator::SortRows() |
1954 | 0 | { |
1955 | 0 | nSortedCount = 0; |
1956 | 0 | iSorted = 0; |
1957 | 0 | int nSortedAlloc = 0; |
1958 | 0 | Reset(); |
1959 | 0 | while (true) |
1960 | 0 | { |
1961 | 0 | int64_t nRow = GetNextRow(); |
1962 | 0 | if (nRow < 0) |
1963 | 0 | break; |
1964 | 0 | if (nSortedCount == nSortedAlloc) |
1965 | 0 | { |
1966 | 0 | int nNewSortedAlloc = 4 * nSortedAlloc / 3 + 16; |
1967 | 0 | int64_t *panNewSortedRows = |
1968 | 0 | static_cast<int64_t *>(VSI_REALLOC_VERBOSE( |
1969 | 0 | panSortedRows, sizeof(int64_t) * nNewSortedAlloc)); |
1970 | 0 | if (panNewSortedRows == nullptr) |
1971 | 0 | { |
1972 | 0 | nSortedCount = 0; |
1973 | 0 | return FALSE; |
1974 | 0 | } |
1975 | 0 | nSortedAlloc = nNewSortedAlloc; |
1976 | 0 | panSortedRows = panNewSortedRows; |
1977 | 0 | } |
1978 | 0 | panSortedRows[nSortedCount++] = nRow; |
1979 | 0 | } |
1980 | 0 | if (nSortedCount == 0) |
1981 | 0 | return FALSE; |
1982 | 0 | std::sort(panSortedRows, panSortedRows + nSortedCount); |
1983 | | #ifdef m_nValueCountInIdx_reliable |
1984 | | if (eOp == FGSO_ISNOTNULL && (int64_t)m_nValueCountInIdx != nSortedCount) |
1985 | | PrintError(); |
1986 | | #endif |
1987 | 0 | return TRUE; |
1988 | 0 | } |
1989 | | |
1990 | | /************************************************************************/ |
1991 | | /* GetNextRowSortedByFID() */ |
1992 | | /************************************************************************/ |
1993 | | |
1994 | | int64_t FileGDBIndexIterator::GetNextRowSortedByFID() |
1995 | 0 | { |
1996 | 0 | if (eOp == FGSO_EQ) |
1997 | 0 | return GetNextRow(); |
1998 | | |
1999 | 0 | if (iSorted < nSortedCount) |
2000 | 0 | return panSortedRows[iSorted++]; |
2001 | | |
2002 | 0 | if (nSortedCount < 0) |
2003 | 0 | { |
2004 | 0 | if (!SortRows()) |
2005 | 0 | return -1; |
2006 | 0 | return panSortedRows[iSorted++]; |
2007 | 0 | } |
2008 | 0 | else |
2009 | 0 | { |
2010 | 0 | return -1; |
2011 | 0 | } |
2012 | 0 | } |
2013 | | |
2014 | | /************************************************************************/ |
2015 | | /* GetRowCount() */ |
2016 | | /************************************************************************/ |
2017 | | |
2018 | | int64_t FileGDBIndexIterator::GetRowCount() |
2019 | 0 | { |
2020 | | // The m_nValueCountInIdx value has been found to be unreliable when the index |
2021 | | // is built as features are inserted (and when they are not in increasing |
2022 | | // order) (with FileGDB SDK 1.3) So disable this optimization as there's no |
2023 | | // fast way to know if the value is reliable or not. |
2024 | | #ifdef m_nValueCountInIdx_reliable |
2025 | | if (eOp == FGSO_ISNOTNULL) |
2026 | | return (int64_t)m_nValueCountInIdx; |
2027 | | #endif |
2028 | |
|
2029 | 0 | if (nSortedCount >= 0) |
2030 | 0 | return nSortedCount; |
2031 | | |
2032 | 0 | int64_t nRowCount = 0; |
2033 | 0 | bool bSaveAscending = bAscending; |
2034 | 0 | bAscending = true; /* for a tiny bit of more efficiency */ |
2035 | 0 | Reset(); |
2036 | 0 | while (GetNextRow() >= 0) |
2037 | 0 | nRowCount++; |
2038 | 0 | bAscending = bSaveAscending; |
2039 | 0 | Reset(); |
2040 | 0 | return nRowCount; |
2041 | 0 | } |
2042 | | |
2043 | | /************************************************************************/ |
2044 | | /* GetMinMaxValue() */ |
2045 | | /************************************************************************/ |
2046 | | |
2047 | | const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, |
2048 | | int &eOutType, int bIsMin) |
2049 | 0 | { |
2050 | 0 | const OGRField *errorRetValue = nullptr; |
2051 | 0 | eOutType = -1; |
2052 | 0 | if (m_nValueCountInIdx == 0) |
2053 | 0 | return nullptr; |
2054 | | |
2055 | 0 | std::vector<GByte> l_abyPageV; |
2056 | 0 | try |
2057 | 0 | { |
2058 | 0 | l_abyPageV.resize(m_nPageSize); |
2059 | 0 | } |
2060 | 0 | catch (const std::exception &) |
2061 | 0 | { |
2062 | 0 | return nullptr; |
2063 | 0 | } |
2064 | 0 | GByte *l_abyPage = l_abyPageV.data(); |
2065 | 0 | uint64_t nPage = 1; |
2066 | 0 | for (GUInt32 iLevel = 0; iLevel < nIndexDepth - 1; iLevel++) |
2067 | 0 | { |
2068 | 0 | VSIFSeekL(fpCurIdx, static_cast<vsi_l_offset>(nPage - 1) * m_nPageSize, |
2069 | 0 | SEEK_SET); |
2070 | | #ifdef DEBUG |
2071 | | iLoadedPage[iLevel] = nPage; |
2072 | | #endif |
2073 | 0 | returnErrorIf(VSIFReadL(l_abyPage, m_nPageSize, 1, fpCurIdx) != 1); |
2074 | 0 | GUInt32 l_nSubPagesCount = GetUInt32(l_abyPage + m_nObjectIDSize, 0); |
2075 | 0 | returnErrorIf(l_nSubPagesCount == 0 || l_nSubPagesCount > nMaxPerPages); |
2076 | | |
2077 | 0 | if (m_nVersion == 1) |
2078 | 0 | { |
2079 | 0 | nPage = GetUInt32(l_abyPage + m_nNonLeafPageHeaderSize, |
2080 | 0 | bIsMin ? 0 : l_nSubPagesCount); |
2081 | 0 | } |
2082 | 0 | else |
2083 | 0 | { |
2084 | 0 | nPage = GetUInt64(l_abyPage + m_nNonLeafPageHeaderSize, |
2085 | 0 | bIsMin ? 0 : l_nSubPagesCount); |
2086 | 0 | } |
2087 | 0 | returnErrorIf(nPage < 2); |
2088 | 0 | } |
2089 | | |
2090 | 0 | VSIFSeekL(fpCurIdx, static_cast<vsi_l_offset>(nPage - 1) * m_nPageSize, |
2091 | 0 | SEEK_SET); |
2092 | | #ifdef DEBUG |
2093 | | iLoadedPage[nIndexDepth - 1] = nPage; |
2094 | | #endif |
2095 | 0 | returnErrorIf(VSIFReadL(l_abyPage, m_nPageSize, 1, fpCurIdx) != 1); |
2096 | | |
2097 | 0 | GUInt32 nFeatures = GetUInt32(l_abyPage + m_nObjectIDSize, 0); |
2098 | 0 | returnErrorIf(nFeatures < 1 || nFeatures > nMaxPerPages); |
2099 | | |
2100 | 0 | int iFeature = (bIsMin) ? 0 : nFeatures - 1; |
2101 | |
|
2102 | 0 | switch (eFieldType) |
2103 | 0 | { |
2104 | 0 | case FGFT_INT16: |
2105 | 0 | { |
2106 | 0 | const GInt16 nVal = |
2107 | 0 | GetInt16(l_abyPage + m_nOffsetFirstValInPage, iFeature); |
2108 | 0 | psField->Integer = nVal; |
2109 | 0 | eOutType = OFTInteger; |
2110 | 0 | return psField; |
2111 | 0 | } |
2112 | | |
2113 | 0 | case FGFT_INT32: |
2114 | 0 | { |
2115 | 0 | const GInt32 nVal = |
2116 | 0 | GetInt32(l_abyPage + m_nOffsetFirstValInPage, iFeature); |
2117 | 0 | psField->Integer = nVal; |
2118 | 0 | eOutType = OFTInteger; |
2119 | 0 | return psField; |
2120 | 0 | } |
2121 | | |
2122 | 0 | case FGFT_FLOAT32: |
2123 | 0 | { |
2124 | 0 | const float fVal = |
2125 | 0 | GetFloat32(l_abyPage + m_nOffsetFirstValInPage, iFeature); |
2126 | 0 | psField->Real = fVal; |
2127 | 0 | eOutType = OFTReal; |
2128 | 0 | return psField; |
2129 | 0 | } |
2130 | | |
2131 | 0 | case FGFT_FLOAT64: |
2132 | 0 | { |
2133 | 0 | const double dfVal = |
2134 | 0 | GetFloat64(l_abyPage + m_nOffsetFirstValInPage, iFeature); |
2135 | 0 | psField->Real = dfVal; |
2136 | 0 | eOutType = OFTReal; |
2137 | 0 | return psField; |
2138 | 0 | } |
2139 | | |
2140 | 0 | case FGFT_DATETIME: |
2141 | 0 | case FGFT_DATETIME_WITH_OFFSET: |
2142 | 0 | { |
2143 | 0 | const double dfVal = |
2144 | 0 | GetFloat64(l_abyPage + m_nOffsetFirstValInPage, iFeature); |
2145 | 0 | FileGDBDoubleDateToOGRDate(dfVal, false, psField); |
2146 | 0 | eOutType = OFTDateTime; |
2147 | 0 | return psField; |
2148 | 0 | } |
2149 | | |
2150 | 0 | case FGFT_DATE: |
2151 | 0 | { |
2152 | 0 | const double dfVal = |
2153 | 0 | GetFloat64(l_abyPage + m_nOffsetFirstValInPage, iFeature); |
2154 | 0 | FileGDBDoubleDateToOGRDate(dfVal, false, psField); |
2155 | 0 | eOutType = OFTDate; |
2156 | 0 | return psField; |
2157 | 0 | } |
2158 | | |
2159 | 0 | case FGFT_TIME: |
2160 | 0 | { |
2161 | 0 | const double dfVal = |
2162 | 0 | GetFloat64(l_abyPage + m_nOffsetFirstValInPage, iFeature); |
2163 | 0 | FileGDBDoubleTimeToOGRTime(dfVal, psField); |
2164 | 0 | eOutType = OFTTime; |
2165 | 0 | return psField; |
2166 | 0 | } |
2167 | | |
2168 | 0 | case FGFT_STRING: |
2169 | 0 | { |
2170 | 0 | wchar_t awsVal[MAX_CAR_COUNT_INDEXED_STR + 1] = {0}; |
2171 | 0 | for (int j = 0; j < nStrLen; j++) |
2172 | 0 | { |
2173 | 0 | GUInt16 nCh = |
2174 | 0 | GetUInt16(l_abyPage + m_nOffsetFirstValInPage + |
2175 | 0 | nStrLen * sizeof(GUInt16) * iFeature, |
2176 | 0 | j); |
2177 | 0 | awsVal[j] = nCh; |
2178 | 0 | } |
2179 | 0 | awsVal[nStrLen] = 0; |
2180 | 0 | char *pszOut = |
2181 | 0 | CPLRecodeFromWChar(awsVal, CPL_ENC_UCS2, CPL_ENC_UTF8); |
2182 | 0 | returnErrorIf(pszOut == nullptr); |
2183 | 0 | returnErrorAndCleanupIf(strlen(pszOut) > |
2184 | 0 | static_cast<size_t>(MAX_UTF8_LEN_STR), |
2185 | 0 | VSIFree(pszOut)); |
2186 | 0 | strcpy(psField->String, pszOut); |
2187 | 0 | CPLFree(pszOut); |
2188 | 0 | eOutType = OFTString; |
2189 | 0 | return psField; |
2190 | 0 | } |
2191 | | |
2192 | 0 | case FGFT_GUID: |
2193 | 0 | case FGFT_GLOBALID: |
2194 | 0 | { |
2195 | 0 | memcpy(psField->String, |
2196 | 0 | l_abyPage + m_nOffsetFirstValInPage + |
2197 | 0 | UUID_LEN_AS_STRING * iFeature, |
2198 | 0 | UUID_LEN_AS_STRING); |
2199 | 0 | psField->String[UUID_LEN_AS_STRING] = 0; |
2200 | 0 | eOutType = OFTString; |
2201 | 0 | return psField; |
2202 | 0 | } |
2203 | | |
2204 | 0 | case FGFT_INT64: |
2205 | 0 | { |
2206 | 0 | const int64_t nVal = |
2207 | 0 | GetInt64(l_abyPage + m_nOffsetFirstValInPage, iFeature); |
2208 | 0 | psField->Integer64 = nVal; |
2209 | 0 | eOutType = OFTInteger64; |
2210 | 0 | return psField; |
2211 | 0 | } |
2212 | | |
2213 | 0 | default: |
2214 | 0 | CPLAssert(false); |
2215 | 0 | break; |
2216 | 0 | } |
2217 | 0 | return nullptr; |
2218 | 0 | } |
2219 | | |
2220 | | /************************************************************************/ |
2221 | | /* GetMinValue() */ |
2222 | | /************************************************************************/ |
2223 | | |
2224 | | const OGRField *FileGDBIndexIterator::GetMinValue(int &eOutType) |
2225 | 0 | { |
2226 | 0 | if (eOp != FGSO_ISNOTNULL) |
2227 | 0 | return FileGDBIterator::GetMinValue(eOutType); |
2228 | 0 | if (eFieldType == FGFT_STRING || eFieldType == FGFT_GUID || |
2229 | 0 | eFieldType == FGFT_GLOBALID) |
2230 | 0 | sMin.String = szMin; |
2231 | 0 | return GetMinMaxValue(&sMin, eOutType, TRUE); |
2232 | 0 | } |
2233 | | |
2234 | | /************************************************************************/ |
2235 | | /* GetMaxValue() */ |
2236 | | /************************************************************************/ |
2237 | | |
2238 | | const OGRField *FileGDBIndexIterator::GetMaxValue(int &eOutType) |
2239 | 0 | { |
2240 | 0 | if (eOp != FGSO_ISNOTNULL) |
2241 | 0 | return FileGDBIterator::GetMinValue(eOutType); |
2242 | 0 | if (eFieldType == FGFT_STRING || eFieldType == FGFT_GUID || |
2243 | 0 | eFieldType == FGFT_GLOBALID) |
2244 | 0 | sMax.String = szMax; |
2245 | 0 | return GetMinMaxValue(&sMax, eOutType, FALSE); |
2246 | 0 | } |
2247 | | |
2248 | | /************************************************************************/ |
2249 | | /* GetMinMaxSumCount() */ |
2250 | | /************************************************************************/ |
2251 | | |
2252 | | struct Int16Getter |
2253 | | { |
2254 | | public: |
2255 | | static double GetAsDouble(const GByte *pBaseAddr, int iOffset) |
2256 | 0 | { |
2257 | 0 | return GetInt16(pBaseAddr, iOffset); |
2258 | 0 | } |
2259 | | }; |
2260 | | |
2261 | | struct Int32Getter |
2262 | | { |
2263 | | public: |
2264 | | static double GetAsDouble(const GByte *pBaseAddr, int iOffset) |
2265 | 0 | { |
2266 | 0 | return GetInt32(pBaseAddr, iOffset); |
2267 | 0 | } |
2268 | | }; |
2269 | | |
2270 | | struct Int64Getter |
2271 | | { |
2272 | | public: |
2273 | | static double GetAsDouble(const GByte *pBaseAddr, int iOffset) |
2274 | 0 | { |
2275 | 0 | return static_cast<double>(GetInt64(pBaseAddr, iOffset)); |
2276 | 0 | } |
2277 | | }; |
2278 | | |
2279 | | struct Float32Getter |
2280 | | { |
2281 | | public: |
2282 | | static double GetAsDouble(const GByte *pBaseAddr, int iOffset) |
2283 | 0 | { |
2284 | 0 | return GetFloat32(pBaseAddr, iOffset); |
2285 | 0 | } |
2286 | | }; |
2287 | | |
2288 | | struct Float64Getter |
2289 | | { |
2290 | | public: |
2291 | | static double GetAsDouble(const GByte *pBaseAddr, int iOffset) |
2292 | 0 | { |
2293 | 0 | return GetFloat64(pBaseAddr, iOffset); |
2294 | 0 | } |
2295 | | }; |
2296 | | |
2297 | | template <class Getter> |
2298 | | void FileGDBIndexIterator::GetMinMaxSumCount(double &dfMin, double &dfMax, |
2299 | | double &dfSum, int &nCount) |
2300 | 0 | { |
2301 | 0 | int nLocalCount = 0; |
2302 | 0 | double dfLocalSum = 0.0; |
2303 | 0 | double dfVal = 0.0; |
2304 | |
|
2305 | 0 | while (true) |
2306 | 0 | { |
2307 | 0 | if (iCurFeatureInPage >= nFeaturesInPage) |
2308 | 0 | { |
2309 | 0 | if (!LoadNextFeaturePage()) |
2310 | 0 | { |
2311 | 0 | break; |
2312 | 0 | } |
2313 | 0 | } |
2314 | | |
2315 | 0 | dfVal = Getter::GetAsDouble(abyPageFeature + m_nOffsetFirstValInPage, |
2316 | 0 | iCurFeatureInPage); |
2317 | |
|
2318 | 0 | dfLocalSum += dfVal; |
2319 | 0 | if (nLocalCount == 0) |
2320 | 0 | dfMin = dfVal; |
2321 | 0 | nLocalCount++; |
2322 | 0 | iCurFeatureInPage++; |
2323 | 0 | } |
2324 | |
|
2325 | 0 | dfSum = dfLocalSum; |
2326 | 0 | nCount = nLocalCount; |
2327 | 0 | dfMax = dfVal; |
2328 | 0 | } Unexecuted instantiation: void OpenFileGDB::FileGDBIndexIterator::GetMinMaxSumCount<OpenFileGDB::Int16Getter>(double&, double&, double&, int&) Unexecuted instantiation: void OpenFileGDB::FileGDBIndexIterator::GetMinMaxSumCount<OpenFileGDB::Int32Getter>(double&, double&, double&, int&) Unexecuted instantiation: void OpenFileGDB::FileGDBIndexIterator::GetMinMaxSumCount<OpenFileGDB::Int64Getter>(double&, double&, double&, int&) Unexecuted instantiation: void OpenFileGDB::FileGDBIndexIterator::GetMinMaxSumCount<OpenFileGDB::Float32Getter>(double&, double&, double&, int&) Unexecuted instantiation: void OpenFileGDB::FileGDBIndexIterator::GetMinMaxSumCount<OpenFileGDB::Float64Getter>(double&, double&, double&, int&) |
2329 | | |
2330 | | bool FileGDBIndexIterator::GetMinMaxSumCount(double &dfMin, double &dfMax, |
2331 | | double &dfSum, int &nCount) |
2332 | 0 | { |
2333 | 0 | const bool errorRetValue = false; |
2334 | 0 | dfMin = 0.0; |
2335 | 0 | dfMax = 0.0; |
2336 | 0 | dfSum = 0.0; |
2337 | 0 | nCount = 0; |
2338 | 0 | returnErrorIf(eOp != FGSO_ISNOTNULL); |
2339 | 0 | returnErrorIf(eFieldType != FGFT_INT16 && eFieldType != FGFT_INT32 && |
2340 | 0 | eFieldType != FGFT_FLOAT32 && eFieldType != FGFT_FLOAT64 && |
2341 | 0 | eFieldType != FGFT_DATETIME && eFieldType != FGFT_INT64 && |
2342 | 0 | eFieldType != FGFT_DATE && eFieldType != FGFT_TIME && |
2343 | 0 | eFieldType != FGFT_DATETIME_WITH_OFFSET); |
2344 | | |
2345 | 0 | bool bSaveAscending = bAscending; |
2346 | 0 | bAscending = true; |
2347 | 0 | Reset(); |
2348 | |
|
2349 | 0 | switch (eFieldType) |
2350 | 0 | { |
2351 | 0 | case FGFT_INT16: |
2352 | 0 | { |
2353 | 0 | GetMinMaxSumCount<Int16Getter>(dfMin, dfMax, dfSum, nCount); |
2354 | 0 | break; |
2355 | 0 | } |
2356 | 0 | case FGFT_INT32: |
2357 | 0 | { |
2358 | 0 | GetMinMaxSumCount<Int32Getter>(dfMin, dfMax, dfSum, nCount); |
2359 | 0 | break; |
2360 | 0 | } |
2361 | 0 | case FGFT_INT64: |
2362 | 0 | { |
2363 | 0 | GetMinMaxSumCount<Int64Getter>(dfMin, dfMax, dfSum, nCount); |
2364 | 0 | break; |
2365 | 0 | } |
2366 | 0 | case FGFT_FLOAT32: |
2367 | 0 | { |
2368 | 0 | GetMinMaxSumCount<Float32Getter>(dfMin, dfMax, dfSum, nCount); |
2369 | 0 | break; |
2370 | 0 | } |
2371 | 0 | case FGFT_FLOAT64: |
2372 | 0 | case FGFT_DATETIME: |
2373 | 0 | case FGFT_DATE: |
2374 | 0 | case FGFT_TIME: |
2375 | 0 | case FGFT_DATETIME_WITH_OFFSET: |
2376 | 0 | { |
2377 | 0 | GetMinMaxSumCount<Float64Getter>(dfMin, dfMax, dfSum, nCount); |
2378 | 0 | break; |
2379 | 0 | } |
2380 | 0 | default: |
2381 | 0 | CPLAssert(false); |
2382 | 0 | break; |
2383 | 0 | } |
2384 | | |
2385 | 0 | bAscending = bSaveAscending; |
2386 | 0 | Reset(); |
2387 | |
|
2388 | 0 | return true; |
2389 | 0 | } |
2390 | | |
2391 | 0 | FileGDBSpatialIndexIterator::~FileGDBSpatialIndexIterator() = default; |
2392 | | |
2393 | | /************************************************************************/ |
2394 | | /* FileGDBSpatialIndexIteratorImpl */ |
2395 | | /************************************************************************/ |
2396 | | |
2397 | | class FileGDBSpatialIndexIteratorImpl final : public FileGDBIndexIteratorBase, |
2398 | | public FileGDBSpatialIndexIterator |
2399 | | { |
2400 | | OGREnvelope m_sFilterEnvelope; |
2401 | | bool m_bHasBuiltSetFID = false; |
2402 | | std::vector<int64_t> m_oFIDVector{}; |
2403 | | size_t m_nVectorIdx = 0; |
2404 | | int m_nGridNo = 0; |
2405 | | GInt64 m_nMinVal = 0; |
2406 | | GInt64 m_nMaxVal = 0; |
2407 | | GInt32 m_nCurX = 0; |
2408 | | GInt32 m_nMaxX = 0; |
2409 | | |
2410 | | virtual bool FindPages(int iLevel, uint64_t nPage) override; |
2411 | | int GetNextRow(); |
2412 | | bool ReadNewXRange(); |
2413 | | bool ResetInternal(); |
2414 | | double GetScaledCoord(double coord) const; |
2415 | | |
2416 | | protected: |
2417 | | friend class FileGDBSpatialIndexIterator; |
2418 | | |
2419 | | FileGDBSpatialIndexIteratorImpl(FileGDBTable *poParent, |
2420 | | const OGREnvelope &sFilterEnvelope); |
2421 | | bool Init(); |
2422 | | |
2423 | | public: |
2424 | | virtual FileGDBTable *GetTable() override |
2425 | 0 | { |
2426 | 0 | return poParent; |
2427 | 0 | } // avoid MSVC C4250 inherits via dominance warning |
2428 | | |
2429 | | virtual int64_t GetNextRowSortedByFID() override; |
2430 | | virtual void Reset() override; |
2431 | | |
2432 | | virtual bool SetEnvelope(const OGREnvelope &sFilterEnvelope) override; |
2433 | | }; |
2434 | | |
2435 | | /************************************************************************/ |
2436 | | /* FileGDBSpatialIndexIteratorImpl() */ |
2437 | | /************************************************************************/ |
2438 | | |
2439 | | FileGDBSpatialIndexIteratorImpl::FileGDBSpatialIndexIteratorImpl( |
2440 | | FileGDBTable *poParentIn, const OGREnvelope &sFilterEnvelope) |
2441 | 0 | : FileGDBIndexIteratorBase(poParentIn, true), |
2442 | 0 | m_sFilterEnvelope(sFilterEnvelope) |
2443 | 0 | { |
2444 | 0 | double dfYMinClamped; |
2445 | 0 | double dfYMaxClamped; |
2446 | 0 | poParentIn->GetMinMaxProjYForSpatialIndex(dfYMinClamped, dfYMaxClamped); |
2447 | 0 | m_sFilterEnvelope.MinY = std::min( |
2448 | 0 | std::max(m_sFilterEnvelope.MinY, dfYMinClamped), dfYMaxClamped); |
2449 | 0 | m_sFilterEnvelope.MaxY = std::min( |
2450 | 0 | std::max(m_sFilterEnvelope.MaxY, dfYMinClamped), dfYMaxClamped); |
2451 | 0 | } Unexecuted instantiation: OpenFileGDB::FileGDBSpatialIndexIteratorImpl::FileGDBSpatialIndexIteratorImpl(OpenFileGDB::FileGDBTable*, OGREnvelope const&) Unexecuted instantiation: OpenFileGDB::FileGDBSpatialIndexIteratorImpl::FileGDBSpatialIndexIteratorImpl(OpenFileGDB::FileGDBTable*, OGREnvelope const&) |
2452 | | |
2453 | | /************************************************************************/ |
2454 | | /* Build() */ |
2455 | | /************************************************************************/ |
2456 | | |
2457 | | FileGDBSpatialIndexIterator * |
2458 | | FileGDBSpatialIndexIterator::Build(FileGDBTable *poParent, |
2459 | | const OGREnvelope &sFilterEnvelope) |
2460 | 0 | { |
2461 | 0 | FileGDBSpatialIndexIteratorImpl *poIterator = |
2462 | 0 | new FileGDBSpatialIndexIteratorImpl(poParent, sFilterEnvelope); |
2463 | 0 | if (!poIterator->Init()) |
2464 | 0 | { |
2465 | 0 | delete poIterator; |
2466 | 0 | return nullptr; |
2467 | 0 | } |
2468 | 0 | return poIterator; |
2469 | 0 | } |
2470 | | |
2471 | | /************************************************************************/ |
2472 | | /* SetEnvelope() */ |
2473 | | /************************************************************************/ |
2474 | | |
2475 | | bool FileGDBSpatialIndexIteratorImpl::SetEnvelope( |
2476 | | const OGREnvelope &sFilterEnvelope) |
2477 | 0 | { |
2478 | 0 | m_sFilterEnvelope = sFilterEnvelope; |
2479 | 0 | m_bHasBuiltSetFID = false; |
2480 | 0 | m_oFIDVector.clear(); |
2481 | 0 | return ResetInternal(); |
2482 | 0 | } |
2483 | | |
2484 | | /************************************************************************/ |
2485 | | /* Init() */ |
2486 | | /************************************************************************/ |
2487 | | |
2488 | | bool FileGDBSpatialIndexIteratorImpl::Init() |
2489 | 0 | { |
2490 | 0 | const bool errorRetValue = false; |
2491 | |
|
2492 | 0 | const std::string osSpxName = CPLFormFilenameSafe( |
2493 | 0 | CPLGetPathSafe(poParent->GetFilename().c_str()).c_str(), |
2494 | 0 | CPLGetBasenameSafe(poParent->GetFilename().c_str()).c_str(), "spx"); |
2495 | |
|
2496 | 0 | if (!ReadTrailer(osSpxName.c_str())) |
2497 | 0 | return false; |
2498 | | |
2499 | 0 | returnErrorIf(m_nValueSize != sizeof(uint64_t)); |
2500 | | |
2501 | 0 | const auto IsPositiveInt = [](double x) { return x >= 0 && x <= INT_MAX; }; |
2502 | |
|
2503 | 0 | const auto &gridRes = poParent->GetSpatialIndexGridResolution(); |
2504 | 0 | const FileGDBGeomField *poGDBGeomField = poParent->GetGeomField(); |
2505 | 0 | if (gridRes.empty() || !(gridRes[0] > 0) || |
2506 | | // Check if the center of the layer extent results in valid scaled |
2507 | | // coords |
2508 | 0 | !(!std::isnan(poGDBGeomField->GetXMin()) && |
2509 | 0 | IsPositiveInt(GetScaledCoord( |
2510 | 0 | 0.5 * (poGDBGeomField->GetXMin() + poGDBGeomField->GetXMax()))) && |
2511 | 0 | IsPositiveInt(GetScaledCoord( |
2512 | 0 | 0.5 * (poGDBGeomField->GetYMin() + poGDBGeomField->GetYMax()))))) |
2513 | 0 | { |
2514 | | // gridRes[0] == 1.61271680278378622e-312 happens on layer |
2515 | | // Zone18_2014_01_Broadcast of |
2516 | | // https://coast.noaa.gov/htdata/CMSP/AISDataHandler/2014/01/Zone18_2014_01.zip |
2517 | | // The FileGDB driver does not use the .spx file in that situation, |
2518 | | // so do we. |
2519 | 0 | CPLDebug("OpenFileGDB", |
2520 | 0 | "Cannot use %s as the grid resolution is invalid", |
2521 | 0 | osSpxName.c_str()); |
2522 | 0 | return false; |
2523 | 0 | } |
2524 | | |
2525 | | // Detect broken .spx file such as SWISSTLM3D_2022_LV95_LN02.gdb/a00000019.spx |
2526 | | // from https://data.geo.admin.ch/ch.swisstopo.swisstlm3d/swisstlm3d_2022-03/swisstlm3d_2022-03_2056_5728.gdb.zip |
2527 | | // which advertises nIndexDepth == 1 whereas it seems to be it should be 2. |
2528 | 0 | if (nIndexDepth == 1) |
2529 | 0 | { |
2530 | 0 | iLastPageIdx[0] = 0; |
2531 | 0 | LoadNextFeaturePage(); |
2532 | 0 | iFirstPageIdx[0] = iLastPageIdx[0] = -1; |
2533 | 0 | if (nFeaturesInPage >= 2 && |
2534 | 0 | nFeaturesInPage < poParent->GetTotalRecordCount() / 10 && |
2535 | 0 | m_nPageCount > static_cast<GUInt32>(nFeaturesInPage)) |
2536 | 0 | { |
2537 | | // Check if it looks like a non-feature page, that is that the |
2538 | | // IDs pointed by it are index page IDs and not feature IDs. |
2539 | 0 | bool bReferenceOtherPages = true; |
2540 | 0 | for (int i = 0; i < nFeaturesInPage; ++i) |
2541 | 0 | { |
2542 | 0 | const GUInt32 nID = GetUInt32(abyPageFeature + 8, i); |
2543 | 0 | if (!(nID >= 2 && nID <= m_nPageCount)) |
2544 | 0 | { |
2545 | 0 | bReferenceOtherPages = false; |
2546 | 0 | break; |
2547 | 0 | } |
2548 | 0 | } |
2549 | 0 | if (bReferenceOtherPages) |
2550 | 0 | { |
2551 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
2552 | 0 | "Cannot use %s as the index depth(=1) is suspicious " |
2553 | 0 | "(it should rather be 2)", |
2554 | 0 | osSpxName.c_str()); |
2555 | 0 | return false; |
2556 | 0 | } |
2557 | 0 | } |
2558 | 0 | } |
2559 | | |
2560 | 0 | return ResetInternal(); |
2561 | 0 | } |
2562 | | |
2563 | | /************************************************************************/ |
2564 | | /* GetScaledCoord() */ |
2565 | | /************************************************************************/ |
2566 | | |
2567 | | double FileGDBSpatialIndexIteratorImpl::GetScaledCoord(double coord) const |
2568 | 0 | { |
2569 | 0 | const auto &gridRes = poParent->GetSpatialIndexGridResolution(); |
2570 | 0 | return (coord / gridRes[0] + (1 << 29)) / (gridRes[m_nGridNo] / gridRes[0]); |
2571 | 0 | } |
2572 | | |
2573 | | /************************************************************************/ |
2574 | | /* ReadNewXRange() */ |
2575 | | /************************************************************************/ |
2576 | | |
2577 | | bool FileGDBSpatialIndexIteratorImpl::ReadNewXRange() |
2578 | 0 | { |
2579 | 0 | const GUInt64 v1 = |
2580 | 0 | (static_cast<GUInt64>(m_nGridNo) << 62) | |
2581 | 0 | (static_cast<GUInt64>(m_nCurX) << 31) | |
2582 | 0 | (static_cast<GUInt64>( |
2583 | 0 | std::min(std::max(0.0, GetScaledCoord(m_sFilterEnvelope.MinY)), |
2584 | 0 | static_cast<double>(INT_MAX)))); |
2585 | 0 | const GUInt64 v2 = |
2586 | 0 | (static_cast<GUInt64>(m_nGridNo) << 62) | |
2587 | 0 | (static_cast<GUInt64>(m_nCurX) << 31) | |
2588 | 0 | (static_cast<GUInt64>( |
2589 | 0 | std::min(std::max(0.0, GetScaledCoord(m_sFilterEnvelope.MaxY)), |
2590 | 0 | static_cast<double>(INT_MAX)))); |
2591 | 0 | if (m_nGridNo < 2) |
2592 | 0 | { |
2593 | 0 | m_nMinVal = v1; |
2594 | 0 | m_nMaxVal = v2; |
2595 | 0 | } |
2596 | 0 | else |
2597 | 0 | { |
2598 | | // Reverse order due to negative sign |
2599 | 0 | memcpy(&m_nMinVal, &v2, sizeof(GInt64)); |
2600 | 0 | memcpy(&m_nMaxVal, &v1, sizeof(GInt64)); |
2601 | 0 | } |
2602 | |
|
2603 | 0 | const bool errorRetValue = false; |
2604 | 0 | if (m_nValueCountInIdx > 0) |
2605 | 0 | { |
2606 | 0 | if (nIndexDepth == 1) |
2607 | 0 | { |
2608 | 0 | iFirstPageIdx[0] = iLastPageIdx[0] = 0; |
2609 | 0 | } |
2610 | 0 | else |
2611 | 0 | { |
2612 | 0 | returnErrorIf(!FindPages(0, 1)); |
2613 | 0 | } |
2614 | 0 | } |
2615 | | |
2616 | 0 | FileGDBIndexIteratorBase::Reset(); |
2617 | |
|
2618 | 0 | return true; |
2619 | 0 | } |
2620 | | |
2621 | | /************************************************************************/ |
2622 | | /* FindMinMaxIdx() */ |
2623 | | /************************************************************************/ |
2624 | | |
2625 | | static bool FindMinMaxIdx(const GByte *pBaseAddr, const int nVals, |
2626 | | const GInt64 nMinVal, const GInt64 nMaxVal, |
2627 | | int &minIdxOut, int &maxIdxOut) |
2628 | 0 | { |
2629 | | // Find maximum index that is <= nMaxVal |
2630 | 0 | int nMinIdx = 0; |
2631 | 0 | int nMaxIdx = nVals - 1; |
2632 | 0 | while (nMaxIdx - nMinIdx >= 2) |
2633 | 0 | { |
2634 | 0 | int nIdx = (nMinIdx + nMaxIdx) / 2; |
2635 | 0 | const GInt64 nVal = GetInt64(pBaseAddr, nIdx); |
2636 | 0 | if (nVal <= nMaxVal) |
2637 | 0 | nMinIdx = nIdx; |
2638 | 0 | else |
2639 | 0 | nMaxIdx = nIdx; |
2640 | 0 | } |
2641 | 0 | while (GetInt64(pBaseAddr, nMaxIdx) > nMaxVal) |
2642 | 0 | { |
2643 | 0 | nMaxIdx--; |
2644 | 0 | if (nMaxIdx < 0) |
2645 | 0 | { |
2646 | 0 | return false; |
2647 | 0 | } |
2648 | 0 | } |
2649 | 0 | maxIdxOut = nMaxIdx; |
2650 | | |
2651 | | // Find minimum index that is >= nMinVal |
2652 | 0 | nMinIdx = 0; |
2653 | 0 | while (nMaxIdx - nMinIdx >= 2) |
2654 | 0 | { |
2655 | 0 | int nIdx = (nMinIdx + nMaxIdx) / 2; |
2656 | 0 | const GInt64 nVal = GetInt64(pBaseAddr, nIdx); |
2657 | 0 | if (nVal >= nMinVal) |
2658 | 0 | nMaxIdx = nIdx; |
2659 | 0 | else |
2660 | 0 | nMinIdx = nIdx; |
2661 | 0 | } |
2662 | 0 | while (GetInt64(pBaseAddr, nMinIdx) < nMinVal) |
2663 | 0 | { |
2664 | 0 | nMinIdx++; |
2665 | 0 | if (nMinIdx == nVals) |
2666 | 0 | { |
2667 | 0 | return false; |
2668 | 0 | } |
2669 | 0 | } |
2670 | 0 | minIdxOut = nMinIdx; |
2671 | 0 | return true; |
2672 | 0 | } |
2673 | | |
2674 | | /************************************************************************/ |
2675 | | /* FindPages() */ |
2676 | | /************************************************************************/ |
2677 | | |
2678 | | bool FileGDBSpatialIndexIteratorImpl::FindPages(int iLevel, uint64_t nPage) |
2679 | 0 | { |
2680 | 0 | const bool errorRetValue = false; |
2681 | |
|
2682 | 0 | iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = -1; |
2683 | |
|
2684 | 0 | const cpl::NonCopyableVector<GByte> *cachedPagePtr = |
2685 | 0 | m_oCachePage[iLevel].getPtr(nPage); |
2686 | 0 | if (cachedPagePtr) |
2687 | 0 | { |
2688 | 0 | memcpy(abyPage[iLevel], cachedPagePtr->data(), m_nPageSize); |
2689 | 0 | } |
2690 | 0 | else |
2691 | 0 | { |
2692 | 0 | cpl::NonCopyableVector<GByte> cachedPage; |
2693 | 0 | if (m_oCachePage[iLevel].size() == m_oCachePage[iLevel].getMaxSize()) |
2694 | 0 | { |
2695 | 0 | m_oCachePage[iLevel].removeAndRecycleOldestEntry(cachedPage); |
2696 | 0 | cachedPage.clear(); |
2697 | 0 | } |
2698 | |
|
2699 | 0 | VSIFSeekL(fpCurIdx, static_cast<vsi_l_offset>(nPage - 1) * m_nPageSize, |
2700 | 0 | SEEK_SET); |
2701 | | #ifdef DEBUG |
2702 | | iLoadedPage[iLevel] = nPage; |
2703 | | #endif |
2704 | 0 | returnErrorIf(VSIFReadL(abyPage[iLevel], m_nPageSize, 1, fpCurIdx) != |
2705 | 0 | 1); |
2706 | 0 | cachedPage.insert(cachedPage.end(), abyPage[iLevel], |
2707 | 0 | abyPage[iLevel] + m_nPageSize); |
2708 | 0 | m_oCachePage[iLevel].insert(nPage, std::move(cachedPage)); |
2709 | 0 | } |
2710 | | |
2711 | 0 | nSubPagesCount[iLevel] = GetUInt32(abyPage[iLevel] + m_nObjectIDSize, 0); |
2712 | 0 | returnErrorIf(nSubPagesCount[iLevel] == 0 || |
2713 | 0 | nSubPagesCount[iLevel] > nMaxPerPages); |
2714 | | |
2715 | 0 | if (GetInt64(abyPage[iLevel] + m_nOffsetFirstValInPage, 0) > m_nMaxVal) |
2716 | 0 | { |
2717 | 0 | iFirstPageIdx[iLevel] = 0; |
2718 | | // nSubPagesCount[iLevel] == 1 && GetUInt32(abyPage[iLevel] + m_nLeafPageHeaderSize, 0) == |
2719 | | // 0 should only happen on non-nominal cases where one forces the depth |
2720 | | // of the index to be greater than needed. |
2721 | 0 | if (m_nVersion == 1) |
2722 | 0 | { |
2723 | 0 | iLastPageIdx[iLevel] = |
2724 | 0 | (nSubPagesCount[iLevel] == 1 && |
2725 | 0 | GetUInt32(abyPage[iLevel] + m_nLeafPageHeaderSize, 0) == 0) |
2726 | 0 | ? 0 |
2727 | 0 | : 1; |
2728 | 0 | } |
2729 | 0 | else |
2730 | 0 | { |
2731 | 0 | iLastPageIdx[iLevel] = |
2732 | 0 | (nSubPagesCount[iLevel] == 1 && |
2733 | 0 | GetUInt64(abyPage[iLevel] + m_nLeafPageHeaderSize, 0) == 0) |
2734 | 0 | ? 0 |
2735 | 0 | : 1; |
2736 | 0 | } |
2737 | 0 | } |
2738 | 0 | else if (!FindMinMaxIdx(abyPage[iLevel] + m_nOffsetFirstValInPage, |
2739 | 0 | static_cast<int>(nSubPagesCount[iLevel]), m_nMinVal, |
2740 | 0 | m_nMaxVal, iFirstPageIdx[iLevel], |
2741 | 0 | iLastPageIdx[iLevel])) |
2742 | 0 | { |
2743 | 0 | iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; |
2744 | 0 | } |
2745 | 0 | else if (iLastPageIdx[iLevel] < static_cast<int>(nSubPagesCount[iLevel])) |
2746 | 0 | { |
2747 | | // Candidate values might extend to the following sub-page |
2748 | 0 | iLastPageIdx[iLevel]++; |
2749 | 0 | } |
2750 | |
|
2751 | 0 | return true; |
2752 | 0 | } |
2753 | | |
2754 | | /************************************************************************/ |
2755 | | /* GetNextRow() */ |
2756 | | /************************************************************************/ |
2757 | | |
2758 | | int FileGDBSpatialIndexIteratorImpl::GetNextRow() |
2759 | 0 | { |
2760 | 0 | const int errorRetValue = -1; |
2761 | 0 | if (bEOF) |
2762 | 0 | return -1; |
2763 | | |
2764 | 0 | while (true) |
2765 | 0 | { |
2766 | 0 | if (iCurFeatureInPage >= nFeaturesInPage) |
2767 | 0 | { |
2768 | 0 | int nMinIdx = 0; |
2769 | 0 | int nMaxIdx = 0; |
2770 | 0 | if (!LoadNextFeaturePage() || |
2771 | 0 | !FindMinMaxIdx(abyPageFeature + m_nOffsetFirstValInPage, |
2772 | 0 | nFeaturesInPage, m_nMinVal, m_nMaxVal, nMinIdx, |
2773 | 0 | nMaxIdx) || |
2774 | 0 | nMinIdx > nMaxIdx) |
2775 | 0 | { |
2776 | 0 | if (m_nCurX < m_nMaxX) |
2777 | 0 | { |
2778 | 0 | m_nCurX++; |
2779 | 0 | if (ReadNewXRange()) |
2780 | 0 | continue; |
2781 | 0 | } |
2782 | 0 | else |
2783 | 0 | { |
2784 | 0 | const auto &gridRes = |
2785 | 0 | poParent->GetSpatialIndexGridResolution(); |
2786 | 0 | if (m_nGridNo + 1 < static_cast<int>(gridRes.size()) && |
2787 | 0 | gridRes[m_nGridNo + 1] > 0) |
2788 | 0 | { |
2789 | 0 | m_nGridNo++; |
2790 | 0 | m_nCurX = static_cast<GInt32>(std::min( |
2791 | 0 | std::max(0.0, |
2792 | 0 | GetScaledCoord(m_sFilterEnvelope.MinX)), |
2793 | 0 | static_cast<double>(INT_MAX))); |
2794 | 0 | m_nMaxX = static_cast<GInt32>(std::min( |
2795 | 0 | std::max(0.0, |
2796 | 0 | GetScaledCoord(m_sFilterEnvelope.MaxX)), |
2797 | 0 | static_cast<double>(INT_MAX))); |
2798 | 0 | if (ReadNewXRange()) |
2799 | 0 | continue; |
2800 | 0 | } |
2801 | 0 | } |
2802 | | |
2803 | 0 | bEOF = true; |
2804 | 0 | return -1; |
2805 | 0 | } |
2806 | | |
2807 | 0 | iCurFeatureInPage = nMinIdx; |
2808 | 0 | nFeaturesInPage = nMaxIdx + 1; |
2809 | 0 | } |
2810 | | |
2811 | | #ifdef DEBUG |
2812 | | const GInt64 nVal = GetInt64(abyPageFeature + m_nOffsetFirstValInPage, |
2813 | | iCurFeatureInPage); |
2814 | | CPL_IGNORE_RET_VAL(nVal); |
2815 | | CPLAssert(nVal >= m_nMinVal && nVal <= m_nMaxVal); |
2816 | | #endif |
2817 | | |
2818 | 0 | const GUInt64 nFID = |
2819 | 0 | m_nVersion == 1 ? GetUInt32(abyPageFeature + m_nLeafPageHeaderSize, |
2820 | 0 | iCurFeatureInPage) |
2821 | 0 | : GetUInt64(abyPageFeature + m_nLeafPageHeaderSize, |
2822 | 0 | iCurFeatureInPage); |
2823 | 0 | iCurFeatureInPage++; |
2824 | 0 | returnErrorAndCleanupIf( |
2825 | 0 | nFID < 1 || |
2826 | 0 | nFID > static_cast<GUInt64>(poParent->GetTotalRecordCount()), |
2827 | 0 | bEOF = true); |
2828 | 0 | return static_cast<int>(nFID - 1); |
2829 | 0 | } |
2830 | 0 | } |
2831 | | |
2832 | | /************************************************************************/ |
2833 | | /* Reset() */ |
2834 | | /************************************************************************/ |
2835 | | |
2836 | | bool FileGDBSpatialIndexIteratorImpl::ResetInternal() |
2837 | 0 | { |
2838 | 0 | m_nGridNo = 0; |
2839 | |
|
2840 | 0 | const auto &gridRes = poParent->GetSpatialIndexGridResolution(); |
2841 | 0 | if (gridRes.empty() || // shouldn't happen |
2842 | 0 | !(gridRes[0] > 0)) |
2843 | 0 | { |
2844 | 0 | return false; |
2845 | 0 | } |
2846 | | |
2847 | 0 | m_nCurX = static_cast<GInt32>( |
2848 | 0 | std::min(std::max(0.0, GetScaledCoord(m_sFilterEnvelope.MinX)), |
2849 | 0 | static_cast<double>(INT_MAX))); |
2850 | 0 | m_nMaxX = static_cast<GInt32>( |
2851 | 0 | std::min(std::max(0.0, GetScaledCoord(m_sFilterEnvelope.MaxX)), |
2852 | 0 | static_cast<double>(INT_MAX))); |
2853 | 0 | m_nVectorIdx = 0; |
2854 | 0 | return ReadNewXRange(); |
2855 | 0 | } |
2856 | | |
2857 | | void FileGDBSpatialIndexIteratorImpl::Reset() |
2858 | 0 | { |
2859 | 0 | ResetInternal(); |
2860 | 0 | } |
2861 | | |
2862 | | /************************************************************************/ |
2863 | | /* GetNextRowSortedByFID() */ |
2864 | | /************************************************************************/ |
2865 | | |
2866 | | int64_t FileGDBSpatialIndexIteratorImpl::GetNextRowSortedByFID() |
2867 | 0 | { |
2868 | 0 | if (m_nVectorIdx == 0) |
2869 | 0 | { |
2870 | 0 | if (!m_bHasBuiltSetFID) |
2871 | 0 | { |
2872 | 0 | m_bHasBuiltSetFID = true; |
2873 | | // Accumulating in a vector and sorting is measurably faster |
2874 | | // than using a unordered_set (or set) |
2875 | 0 | while (true) |
2876 | 0 | { |
2877 | 0 | const auto nFID = GetNextRow(); |
2878 | 0 | if (nFID < 0) |
2879 | 0 | break; |
2880 | 0 | m_oFIDVector.push_back(nFID); |
2881 | 0 | } |
2882 | 0 | std::sort(m_oFIDVector.begin(), m_oFIDVector.end()); |
2883 | 0 | } |
2884 | |
|
2885 | 0 | if (m_oFIDVector.empty()) |
2886 | 0 | return -1; |
2887 | 0 | const auto nFID = m_oFIDVector[m_nVectorIdx]; |
2888 | 0 | ++m_nVectorIdx; |
2889 | 0 | return nFID; |
2890 | 0 | } |
2891 | | |
2892 | 0 | const auto nLastFID = m_oFIDVector[m_nVectorIdx - 1]; |
2893 | 0 | while (m_nVectorIdx < m_oFIDVector.size()) |
2894 | 0 | { |
2895 | | // Do not return consecutive identical FID |
2896 | 0 | const auto nFID = m_oFIDVector[m_nVectorIdx]; |
2897 | 0 | ++m_nVectorIdx; |
2898 | 0 | if (nFID == nLastFID) |
2899 | 0 | { |
2900 | 0 | continue; |
2901 | 0 | } |
2902 | 0 | return nFID; |
2903 | 0 | } |
2904 | 0 | return -1; |
2905 | 0 | } |
2906 | | |
2907 | | } /* namespace OpenFileGDB */ |