From 78c7886935b017b70318e91a8f12bb6ea5727b3e Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 20 May 2024 01:46:37 +0200 Subject: [PATCH] OpenFileGDB: add partial read-only support for tables with 64-bit ObjectIDs Tables with non-sparse pages in .gdbtablx are fully supported. A subset of tables with sparse pages are properly supported (cf https://github.com/rouault/dump_gdbtable/wiki/FGDB-Spec#trailing-section-16-bytes--variable-number-) When we detect that we can't properly decode ObjectID in the unsupported subset of sparse tables, we emit a warning, and turn off use of attribute/spatial indices. Read-only support for now. --- .../3features.gdb/a00000001.TablesByName.atx | Bin 0 -> 4118 bytes .../3features.gdb/a00000001.gdbindexes | Bin 0 -> 110 bytes .../3features.gdb/a00000001.gdbtable | Bin 0 -> 322 bytes .../3features.gdb/a00000001.gdbtablx | Bin 0 -> 5152 bytes .../3features.gdb/a00000002.gdbtable | Bin 0 -> 2055 bytes .../3features.gdb/a00000002.gdbtablx | Bin 0 -> 5152 bytes .../3features.gdb/a00000003.gdbindexes | Bin 0 -> 42 bytes .../3features.gdb/a00000003.gdbtable | Bin 0 -> 1016 bytes .../3features.gdb/a00000003.gdbtablx | Bin 0 -> 5152 bytes .../a00000004.CatItemsByPhysicalName.atx | Bin 0 -> 4118 bytes .../a00000004.CatItemsByType.atx | Bin 0 -> 4118 bytes .../3features.gdb/a00000004.FDO_UUID.atx | Bin 0 -> 4118 bytes .../3features.gdb/a00000004.gdbindexes | Bin 0 -> 310 bytes .../3features.gdb/a00000004.gdbtable | Bin 0 -> 10304 bytes .../3features.gdb/a00000004.gdbtablx | Bin 0 -> 5152 bytes .../3features.gdb/a00000004.horizon | Bin 0 -> 32 bytes .../objectid64/3features.gdb/a00000004.spx | Bin 0 -> 4118 bytes .../a00000005.CatItemTypesByName.atx | Bin 0 -> 12310 bytes .../a00000005.CatItemTypesByParentTypeID.atx | Bin 0 -> 4118 bytes .../a00000005.CatItemTypesByUUID.atx | Bin 0 -> 4118 bytes .../3features.gdb/a00000005.gdbindexes | Bin 0 -> 296 bytes .../3features.gdb/a00000005.gdbtable | Bin 0 -> 2071 bytes .../3features.gdb/a00000005.gdbtablx | Bin 0 -> 5152 bytes .../a00000006.CatRelsByDestinationID.atx | Bin 0 -> 4118 bytes .../a00000006.CatRelsByOriginID.atx | Bin 0 -> 4118 bytes .../3features.gdb/a00000006.CatRelsByType.atx | Bin 0 -> 4118 bytes .../3features.gdb/a00000006.FDO_UUID.atx | Bin 0 -> 4118 bytes .../3features.gdb/a00000006.gdbindexes | Bin 0 -> 318 bytes .../3features.gdb/a00000006.gdbtable | Bin 0 -> 263 bytes .../3features.gdb/a00000006.gdbtablx | Bin 0 -> 5152 bytes .../a00000007.CatRelTypesByBackwardLabel.atx | Bin 0 -> 12310 bytes .../a00000007.CatRelTypesByDestItemTypeID.atx | Bin 0 -> 4118 bytes .../a00000007.CatRelTypesByForwardLabel.atx | Bin 0 -> 12310 bytes .../a00000007.CatRelTypesByName.atx | Bin 0 -> 12310 bytes ...00000007.CatRelTypesByOriginItemTypeID.atx | Bin 0 -> 4118 bytes .../a00000007.CatRelTypesByUUID.atx | Bin 0 -> 4118 bytes .../3features.gdb/a00000007.gdbindexes | Bin 0 -> 602 bytes .../3features.gdb/a00000007.gdbtable | Bin 0 -> 3626 bytes .../3features.gdb/a00000007.gdbtablx | Bin 0 -> 5152 bytes .../3features.gdb/a00000009.gdbindexes | Bin 0 -> 116 bytes .../3features.gdb/a00000009.gdbtable | Bin 0 -> 1384 bytes .../3features.gdb/a00000009.gdbtablx | Bin 0 -> 5156 bytes .../3features.gdb/a00000009.horizon | 1 + .../objectid64/3features.gdb/a00000009.spx | Bin 0 -> 65566 bytes .../data/filegdb/objectid64/3features.gdb/gdb | Bin 0 -> 4 bytes .../objectid64/3features.gdb/timestamps | Bin 0 -> 400 bytes .../with_holes_8.gdb/a00000001.gdbtable | Bin 0 -> 505 bytes .../with_holes_8.gdb/a00000001.gdbtablx | Bin 0 -> 4128 bytes .../with_holes_8.gdb/a00000002.gdbtable | Bin 0 -> 2094 bytes .../with_holes_8.gdb/a00000002.gdbtablx | Bin 0 -> 4128 bytes .../with_holes_8.gdb/a00000003.gdbtable | Bin 0 -> 797 bytes .../with_holes_8.gdb/a00000003.gdbtablx | Bin 0 -> 4128 bytes .../with_holes_8.gdb/a00000004.freelist | Bin 0 -> 4440 bytes .../with_holes_8.gdb/a00000004.gdbtable | Bin 0 -> 47714 bytes .../with_holes_8.gdb/a00000004.gdbtablx | Bin 0 -> 4128 bytes .../with_holes_8.gdb/a00000005.gdbtable | Bin 0 -> 2060 bytes .../with_holes_8.gdb/a00000005.gdbtablx | Bin 0 -> 4128 bytes .../a00000006.FDO_OriginID.atx | Bin 0 -> 4118 bytes .../with_holes_8.gdb/a00000006.gdbindexes | Bin 0 -> 66 bytes .../with_holes_8.gdb/a00000006.gdbtable | Bin 0 -> 740 bytes .../with_holes_8.gdb/a00000006.gdbtablx | Bin 0 -> 4128 bytes .../with_holes_8.gdb/a00000007.gdbtable | Bin 0 -> 3665 bytes .../with_holes_8.gdb/a00000007.gdbtablx | Bin 0 -> 4128 bytes .../with_holes_8.gdb/a00000009.gdbindexes | Bin 0 -> 116 bytes .../with_holes_8.gdb/a00000009.gdbtable | Bin 0 -> 566 bytes .../with_holes_8.gdb/a00000009.gdbtablx | Bin 0 -> 37990 bytes .../with_holes_8.gdb/a00000009.horizon | Bin 0 -> 32 bytes .../objectid64/with_holes_8.gdb/a00000009.spx | Bin 0 -> 65566 bytes .../with_holes_8.gdb/a0000000a.gdbindexes | Bin 0 -> 116 bytes .../with_holes_8.gdb/a0000000a.gdbtable | Bin 0 -> 566 bytes .../with_holes_8.gdb/a0000000a.gdbtablx | Bin 0 -> 37990 bytes .../with_holes_8.gdb/a0000000a.horizon | Bin 0 -> 32 bytes .../objectid64/with_holes_8.gdb/a0000000a.spx | Bin 0 -> 65566 bytes .../with_holes_8.gdb/a0000000b.gdbindexes | Bin 0 -> 116 bytes .../with_holes_8.gdb/a0000000b.gdbtable | Bin 0 -> 566 bytes .../with_holes_8.gdb/a0000000b.gdbtablx | Bin 0 -> 37990 bytes .../with_holes_8.gdb/a0000000b.horizon | Bin 0 -> 32 bytes .../objectid64/with_holes_8.gdb/a0000000b.spx | Bin 0 -> 65566 bytes .../with_holes_8.gdb/a0000000c.gdbindexes | Bin 0 -> 116 bytes .../with_holes_8.gdb/a0000000c.gdbtable | Bin 0 -> 566 bytes .../with_holes_8.gdb/a0000000c.gdbtablx | Bin 0 -> 37990 bytes .../with_holes_8.gdb/a0000000c.horizon | Bin 0 -> 32 bytes .../objectid64/with_holes_8.gdb/a0000000c.spx | Bin 0 -> 65566 bytes .../with_holes_8.gdb/a0000000d.gdbindexes | Bin 0 -> 116 bytes .../with_holes_8.gdb/a0000000d.gdbtable | Bin 0 -> 566 bytes .../with_holes_8.gdb/a0000000d.gdbtablx | Bin 0 -> 37990 bytes .../with_holes_8.gdb/a0000000d.horizon | Bin 0 -> 32 bytes .../objectid64/with_holes_8.gdb/a0000000d.spx | Bin 0 -> 65566 bytes .../with_holes_8.gdb/a0000000e.gdbindexes | Bin 0 -> 116 bytes .../with_holes_8.gdb/a0000000e.gdbtable | Bin 0 -> 566 bytes .../with_holes_8.gdb/a0000000e.gdbtablx | Bin 0 -> 155936 bytes .../with_holes_8.gdb/a0000000e.horizon | Bin 0 -> 32 bytes .../objectid64/with_holes_8.gdb/a0000000e.spx | Bin 0 -> 65566 bytes .../with_holes_8.gdb/a0000000f.gdbindexes | Bin 0 -> 116 bytes .../with_holes_8.gdb/a0000000f.gdbtable | Bin 0 -> 642 bytes .../with_holes_8.gdb/a0000000f.gdbtablx | Bin 0 -> 176416 bytes .../with_holes_8.gdb/a0000000f.horizon | Bin 0 -> 32 bytes .../objectid64/with_holes_8.gdb/a0000000f.spx | Bin 0 -> 65566 bytes .../filegdb/objectid64/with_holes_8.gdb/gdb | Bin 0 -> 8 bytes .../objectid64/with_holes_8.gdb/timestamps | Bin 0 -> 400 bytes autotest/ogr/ogr_openfilegdb.py | 157 ++++- ogr/ogrsf_frmts/openfilegdb/filegdbindex.cpp | 615 +++++++++++------- .../openfilegdb/filegdbindex_write.cpp | 42 +- ogr/ogrsf_frmts/openfilegdb/filegdbtable.cpp | 285 ++++++-- ogr/ogrsf_frmts/openfilegdb/filegdbtable.h | 60 +- .../openfilegdb/filegdbtable_write.cpp | 67 +- .../openfilegdb/filegdbtable_write_fields.cpp | 2 +- .../openfilegdb/gdalopenfilegdbrasterband.cpp | 8 +- ogr/ogrsf_frmts/openfilegdb/ogr_openfilegdb.h | 4 +- .../openfilegdb/ogropenfilegdbdatasource.cpp | 2 +- .../ogropenfilegdbdatasource_write.cpp | 51 +- .../openfilegdb/ogropenfilegdblayer.cpp | 104 +-- .../openfilegdb/ogropenfilegdblayer_write.cpp | 13 +- 113 files changed, 959 insertions(+), 452 deletions(-) create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000001.TablesByName.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000001.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000001.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000001.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000002.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000002.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000003.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000003.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000003.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.CatItemsByPhysicalName.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.CatItemsByType.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.FDO_UUID.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.horizon create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.spx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.CatItemTypesByName.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.CatItemTypesByParentTypeID.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.CatItemTypesByUUID.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.CatRelsByDestinationID.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.CatRelsByOriginID.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.CatRelsByType.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.FDO_UUID.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByBackwardLabel.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByDestItemTypeID.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByForwardLabel.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByName.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByOriginItemTypeID.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByUUID.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000009.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000009.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000009.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000009.horizon create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000009.spx create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/gdb create mode 100644 autotest/ogr/data/filegdb/objectid64/3features.gdb/timestamps create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000001.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000001.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000002.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000002.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000003.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000003.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000004.freelist create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000004.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000004.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000005.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000005.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000006.FDO_OriginID.atx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000006.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000006.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000006.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000007.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000007.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.horizon create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.spx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.horizon create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.spx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.horizon create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.spx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.horizon create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.spx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.horizon create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.spx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.horizon create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.spx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.gdbindexes create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.gdbtable create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.gdbtablx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.horizon create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.spx create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/gdb create mode 100644 autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/timestamps diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000001.TablesByName.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000001.TablesByName.atx new file mode 100644 index 0000000000000000000000000000000000000000..f4eaf02531704846ef18b407c3976fa7ac1ec45f GIT binary patch literal 4118 zcmeH~?Fxc06oy~>?k>Eu=)=}v)yVb3yXjJUk1|3j3V%3*GkNC}g~w-S0|8*oNSiPV zX35N$IkRG>Ovc3LIPBF74%lH+_W^fYP#_=C5&0|v$~#53dHTjDb>~@m7JF8oW9^wl71t(>{z}p{t3v36_BeR0waJ>R z7hgr5W(|wMFcOPGBwU^|>bOQty{o+Yk~H^vZR+}x8c+jjKn6orp9BUJH!M$m<8pTb2giXbkk`ymXm7Ge`@X3@8qN1c;sT^TrV^L^*u zHh`E}GapRzlGbBdJ%RVa&*W`Hd!#@|MB)gE8x-YVXmqzIpb54xxKl|uE(w7d{dkuq z->15A>b8+iYLgRU$aK9)lk;!MTfnEsp=_lqq|Vf(@_fL=p6_`gkO@h@A?MnvKo_K6 u4b7D15mQ#z!UnsfoxiiM{V5v*-^i5J#g9CgJR%w~J*O&XjjnT3@{51d`8)go literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000001.gdbtablx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000001.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..3d6f920bbf82725a7a359819071aa1ea475639f6 GIT binary patch literal 5152 zcmeI$F%5uF5JbTvgaQS)Ko~mkfDAOC0wwg^7Kk93pX{rCHRX*}HT34*)Z2QsCr5H7 pS8^v$vgH4ocmxO#AV7cs0RjXF5FkK+009C72>ebUr@imI_W~9`1swnY literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000002.gdbtable b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000002.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..a0af90eaae1fb1738ddead12a36441ef8292c88d GIT binary patch literal 2055 zcma)7-*3|}5H{*c$JBM>=L-l0#>7CQsAyD!Hz!THh%}iJM;g3DrcFkqCP7ImHvV{o z?KWwh#Dz$f9Q*G3zB`}KEdam~89o_pGK#OWyPTb@7U=#Co!TX$U4{6>AOIn_1`aVw$t)&wf=(#gU`UYu?2c3ox(0GO zuI?fA3LT*zzBKa#yqM?G4Cgoq=e{&e;vl?Uq&N?gxKA~#Jr9)&XhQ|5wx?P41fm@m zY3IkB&B0i;CMvSOu1xlnB>I{9S?14Pa)yVcp@S?Nj@7$*Y=p7@oU`i6h9!Hv#WT)g ze*&*OO*d4}(Z8v!0g@$dzh^^-D&{dhQJX!Q29Lxr7#2x+MU%o-q};#Br65TooL90! z4|mhKmX|g|xS(eDIeFgMST3keDHZvbB774@i)eiYS@^qp5G=`ctV-|%&7hXcT4c>w62nN@a;TA-8V{K>>}pstJ{f{uzx zwLPMCxx@M@yVvWi+c+c2B|**yRJ90~?_BEwV8tnL66iN6&m&&HZqj>EQP1vYlw>%Z zvWArTxfI6is_^+f6(ZK!A$ulRq>T!y*LmZ~=!;3p`sW+_TcHP+nbl)MpW^1U@a>I0 KE8>8gpP0Wjd{iF* literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000002.gdbtablx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000002.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..7c12c568195050346285f43f1164067d9ce86e7c GIT binary patch literal 5152 zcmeI$uMNUL97pkkmOm2^Jhm2##9$eqEP*Rw1rkOeL9hS=;0Ue>8dF=-gapO!jZptC z`F_laOP;(er8I~wVx!n8mY&eUoVyj2aD)qdU^)!8P{9j+u+D-5Jm3qX*gyq0c!S3R7O;Q?EMNf(Sik}nuz&?DU;ztQzycQNS%9xS#`yy^ CN)wa- literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000003.gdbindexes b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000003.gdbindexes new file mode 100644 index 0000000000000000000000000000000000000000..58df68d525b47a895d5876a8bf5f437a90afc3f0 GIT binary patch literal 42 mcmZQ%U|?VaVmAgC27iWl22UW(z#ss`|Nk=rNhTnMhyegseFSR& literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000003.gdbtable b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000003.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..f8006ccdaad9fa9e214675facdf868ce17284898 GIT binary patch literal 1016 zcmbtSy^hmB5S~l!!U<@2g{>&8$UkwCZGb3oj88a@YzOWVMb_fm%Na$EodA+*5 z0hSnd#Tt1xp=3b^pt4)4c;JE$1{~sygncnpy1-xxbKnR=d4DgD9IcUShj0V~h#`U$ znMKxcgAD({u{RvM$hjeE`!K^ha7gejF@(6vdR!k~nX#|Rd=>9rz1lGi$838ef>ZFw z^a6!8Yeed_{DD0pogC!#?;EaTbw=cZR@WL$MQc=LNu|xIsy9?s)9c!JEcIQ>Hn4)3 za2`L8!c!v2Re3B8dX|rM`2_I{QW`Z~Q>m&el-4vnD((e~lXDSZ@i2>v7u8m_f3nQ> zaQ5L|asTrByEi{Sp5Ix${qf0u+`qex;wOOB>vG?94#YzZ-_2f0gMn`c!&%G&P3;jW zFT`(7PKg|LA%(fFB@^iqDO5#P{!0o}(K#^MzUA~r#7|f}f5wwJ4{V-IfKp9D4q~Wm&Fj8|HbLelJqnq74vN#rwN;i4$*PMVj|H(1utY%9&}kK z`lF|m()u4Q9#6(EadQ7LeooleDqR)!OB-><$dp$1}hPJ zS7r*8%)!RZV&@(x^JRHXR?CSe z;LLFzz8#57L@76BYEg~+4BVwoj>TW6c0VHbLY)1Y8a0tlHVQ}5p{bdqH8r&jGjAkO zGUcvJwUrnmoN4hP&=SoH)4+8~(OVoEK6eWDtb^RSWo63ES7toeSo8PJF)92>_q`Xu-zKjB literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.gdbtable b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..0cbf02d63282a4455bcb2f2593fd2becef0b3594 GIT binary patch literal 10304 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 CpD$kk literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.gdbtablx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..93fec31b61e3d8dce2c753b4624c1f43c4d16239 GIT binary patch literal 5152 zcmeI$!3_W*3vQKmY&$ literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.horizon b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.horizon new file mode 100644 index 0000000000000000000000000000000000000000..b64b92356a70378da21d5ffdecaaca5124025076 GIT binary patch literal 32 YcmZQ#0D;N_OdzTu3_>$hIzagj07ZNRrvLx| literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.spx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000004.spx new file mode 100644 index 0000000000000000000000000000000000000000..15bba5bf1c63e26f4de207a8e38a22b89fc95ae0 GIT binary patch literal 4118 zcmeI$u?@f=5JOQT8cIfB1vbd2lw`}CK`ek$ek5ar#1*eZl5)h$eX=gt5I_I{1g->n m)tE^&y1l!H`)U}E5I_I{1Q0*~0R#~EL7+S-ra0#pz~>L^aRjRX literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.CatItemTypesByName.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.CatItemTypesByName.atx new file mode 100644 index 0000000000000000000000000000000000000000..5f5004620ba9c73658f069eeace1557d8df8cebe GIT binary patch literal 12310 zcmeI2S#Q%o6oqft_kCZ@{yR`Y0Er^h26#@=6e`lBvRx?uhM&rJ#tBh|JXAcy zvI1FwtUy*EE09tFt@&@w2;AX*44egzz!mTmJOBl_4W5IW;2d}XZUDW1`8?mDoOTB4KiNe)23tWnWt}0< zCkbcfHTYHVlWqM~2Y<(cxBL>vYCIpO_R0?-Qiu4P;?|pbg)Q^z!gesrG^v$qK7SoX zicihG9%~u#Q+{qTaa`@E*KnDiKmSuURz)?>&y{^87w%$hAC*?#UuKd04~N$4Ixc`S z;0d?}?tzEkG&lk71N927gJ(d$8Mp+lf`c5feY|;i!>`24wc?-P=L=u=;N#*~QG;lG zqgg6_##&~u5tx04*-w}A=LWn~MS7gurR0KN94X5>ry6r+^kb;p^!vLysOOF2 zXY8s@NrjcQL*Q+OTg7ixPc)US4^o5W|H%qu1+oHJfvkWO_*JyHY9+7Td>tD925`t0 An*aa+ literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.CatItemTypesByParentTypeID.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.CatItemTypesByParentTypeID.atx new file mode 100644 index 0000000000000000000000000000000000000000..269f1f31a465213f69d5cad44ad6d2744b796761 GIT binary patch literal 4118 zcmeI$$%@1v5C-6$ec$(e?>QA1HCi5xS}zsy?(=7uzRamFGtjgJUks!O$&VP!WHOn2 z!dTXLyAdSeEM5PtiF3I?S#Oz2+Q4;Xop+$Yz&`gLY&~(Ou&j=Y6hSHHpQU6W(h) ztTGgg60*L{6pIz=ICv#iiwSnn<^au1d^ab=O^a}a_`WyNU`VRU5dLQ}*`W1Qsy$fBr{(?S+K7u}neuCbD zUP9kOA3{GuKSQ5E-$S23UqN3(??JDjcc5ROKcP3}xiw!gwhubT3(3+&KAMv3-DcIQ zX6t@ytIV_eBGp7xXC?0}G)MMgRwK6DZ``MrdJF-%N#Y!eHI2+lTkJe$ZHO|pHdn)k z50iLSoH#pq3Qs(`y4`Owq(`Zpmexihz9_agYLa{U=*43rYA=cIo3zzKQiN&rjTU-2 zGo+uKdB62JRJ$6T=_qckO@n>Xl*uNII}h}TMwtib|T&0Ij$Fn7%N5V%`8(hd(y+c5W&hW9R(a*)*<5-Mm` z+aoGhM7hl!jKafl;<(aGaq6XDDpmUI;N?2PQAvP z=wJLIhdEKr>CJs|DkCac9ZsXD4K7T>w3VH5q{`N@hoAc`xZr`S3I>AeHE67ZR~pCL z2wn$XMQa5>2f;~jA@b0SU8wRtC(BsNa3Wlaczg&B1 zXV9>Y3H4M&H6~rFVo!@rfpgYyX?0Ip9lI)qghL!u~O;Sw1ds&t%tN< z?0Xe*#zmEz2kMH&6S5LL}D5F!FlknsCrsfDqUmDbI7PK|7OI ztyu7j>N%^xNls@QJh2fviY2ZM-yVG@Qr_Q`zgUkx9|Rr*9t0i)9t0i){%-_cKYug7 MzMcQ~Ux4fNzfc=rO#lD@ literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.gdbindexes b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.gdbindexes new file mode 100644 index 0000000000000000000000000000000000000000..bc887093f340449a4e4f40224ff3a487814c09a8 GIT binary patch literal 296 zcmZQ!U|`?@VmAgC27iWlAWmY)Vn}63W+-9s1d1^*2mtZ_|BOJA1Bfx@g@B^Y42fX% zB|x3I3?U4a3dhbTnfm;XF>qb^dg{nc|bd0W}}-3 X@&j&lgxu%{v>_L00|Nu98xi6F*S08% literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.gdbtable b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000005.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..bf93ec49a465f6bf1205fb164e92bee5b3303828 GIT binary patch literal 2071 zcmZXV4@?tR9LGja+h|ws-Oi@JHbdkjWI+UoZW*Rpq%G@ARR|)Jr+GE+X$+g#e-_P&;-tXVJ z5(F_1UZ22gGQ5P(40p#}kn;gr7oqz}=+`}!&=KniKYXQqTPhL8bUsT)Dms%dEx6XL7ZwaoII8Q9t zM=rG(`qvz6+OH@JzLPZ+DeBVAuD|IQPA6Df=Rfa0OGSBWNR9`rfXE=S?6lhzt7TR4 zA$zN#enB zzO_s17Vq1b-0=Q|UN!Y(JDzl3IgjEKG3*ax8aQ6 zgjZ6fg$FbTQ;JFq$U=V0i?u6y!qootdHW{eoOgNwTL_AUt%4!ar^zqO9SoFj=HtBL z4QertxN8|R1DmTj8j7`-(=T64J|S=1lVkh%!nbGe_|8gZ^p5({fLy==T2IjvZHdYz zwe#ZfZPhjVlm{{?zMD@1U`&9A$q{A-Dx1%#w}0+jwm$Bg{q*D3-u6D65^EX8 zsADWB+^T$zKW@v6s}^;!>d>|jTq}tOBwxV7T6PQ=R5o~9;fzY4`! z+&Z)DP4bE+x+&nx^b(x#)d1soAh>mu+0krh!v5m|Zm-_mtF+EF-DsXxl7M5L>BaQK z=nH^Af9bu~fk`J~8fQNH@y3jRHvm^HMWeXliNFo}Oq7XmZsd*uG-PdleLrPK?XmCp zx#?D%a=7E{GZZKf3uj(f>6)~6&cbH{S$phw;Jk$3j*l0VI^1MX)Acd)XrTJ54ISHR z)|?*}JJW!w_vNI{h^xx6q$4MaT>3Wc@x`qR1JXI}QT5{HZXKCi@oMY5xb25Oym((6 zR^_e%98&-%J$l39qYd}X&GBYK2fY?E@R>pOP@F B2><{9 literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.CatRelsByOriginID.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.CatRelsByOriginID.atx new file mode 100644 index 0000000000000000000000000000000000000000..1664d21885e8448309fe4bc31b7f2754ba2770b8 GIT binary patch literal 4118 zcmeI#OA3H63`Nnn8D{~jwVz=ux+!t*TBU0!!Y!mBql7biA|k_6^|k&@{5=HDBW=6c zWE$t_)t9ttE+J@%mRz>QLULD+%h-tk0tg_000IagfB*srAn<2_@lE|T`M306`wgwf B2_gUh literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.CatRelsByType.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.CatRelsByType.atx new file mode 100644 index 0000000000000000000000000000000000000000..0413692ccd84d77f05a97571224962a84f049876 GIT binary patch literal 4118 zcmeI#Q3`-C2nJxgOm9MCg1tItmx}IQ7CXZT{(%#pl<(~)B3hoxH~Kf>pCNE|*ZnBg zxJn31HRoocTwKCh-$Grl8JA%v1Rwwb2tWV=5P$##AOHaf{8?aqQ-3D^k^Zya@mvX9 literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.FDO_UUID.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.FDO_UUID.atx new file mode 100644 index 0000000000000000000000000000000000000000..282a6a2783509cad39c2bb11f6f1e7b6c72073d0 GIT binary patch literal 4118 zcmeI#O$vY@5QgFDGVKNw%%7#1UMjkKS$GBsUSS+&HN&&}L`0US`WyY5_-6>5-q@PA z;`K;%R~JguSWVVaQVKRUo1434T*giW5I_I{1Q0*~0R#|00D(UXtZ(Y?$$zB(-fy!1 B2}S?_ literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.gdbindexes b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.gdbindexes new file mode 100644 index 0000000000000000000000000000000000000000..c608a88be082bd48fb5821f6bacf48df11c2495d GIT binary patch literal 318 zcmZvXu?oUK6a+_6v526JAEZG@C5Wj-MI<6B#>TIYbHordSM0&Zv3dAtL4iW?c4G0C6X92N+;_M6@{tQVBSq!NR$qXe7o(wJw3~Ve+ zETIgc5DvE*6HrAFLncEy5a&Szxzt$LT!31NVOoF!EFlb)3bd^TFzhfWny&2}TxH23Mei{vKN@m+<=L3HLs(GcybOrM_^j+V`WR zsMTjq+VK^ViW>w7o|vq0-sLXixB9~K;~<{`0C68T Axc~qF literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.gdbtablx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000006.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..431307d13c60381f56be9a7c0c765046b36cb27f GIT binary patch literal 5152 zcmeI$!3h8$3-MA9039Z2oNAZfB*pk1PBlyK!5-N L0{;rUe_$^>!R-Lc literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByBackwardLabel.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByBackwardLabel.atx new file mode 100644 index 0000000000000000000000000000000000000000..8797338e7a9c750c8dff7448fb1f9ba3da067822 GIT binary patch literal 12310 zcmeI1=}y8x6h<%Z`@a9`yFf4|Mvcn@Ac7K+62KVW#z*zs)_I{>NWyKzz7%tBVYuKfDtePM!*Od0V9wn0bTQ-{20*hJ#>ur(E(aT+vpUnqXIfY zJLnK?pk1_vHqkO#L}zFVC3%Gfj*Ifb+P*1ItmMnA>h-9`+CGqLse51DyR!#HJhjL; zRB+kCnfi<~6J<)8t$4?6O-j*mh0KNKC*1X>b;T%ue`%)mf0NcT%%74BawFD%2h;9v zuwTVdaYrt!MaC?Dii7^bC~9wsf;O?NS-m);r*SiQZGTJnDV1>2B_g%Ysgp(&&(kux z1mennp5s51w`f%9L_D+2fx7%%;x2CEgsirG9qFj3eN@Rmdu#n!_cxf&&N@b1{HEPs z?7vE1YkKQrqedndzuEFvW{i9!a&_Dub=T>gn~dM|`RjkurN?F5nq2&TXA1c^b@{uG zo)jJWT|6rs_rBA*&h0N`CZjXykf)fwfy{Py`%`v0{$)EaNGQy@KYPE-B7DfEYjoX~ z&`NaV=fl;1@@}7a)YQ4GuIx1v5a$5;SN{Qdj3#l|Wltg_n{Pj@Vzn9pBVYuKfDteP dM!*Od0V7}pjKF^&@D&ueP0ax48++Zf{0+VJ$xQ$N literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByDestItemTypeID.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByDestItemTypeID.atx new file mode 100644 index 0000000000000000000000000000000000000000..47d2132ee8b7ee9a518ea5306182b61d0b93f04a GIT binary patch literal 4118 zcmeI$xsKg1429vFWU@{6WhTo5q$8k(qNtJ*rI#}N?m0I=$|=$W2_OLjY?Bb*?3Gtk%M~PCapa1Ms4K-H{jkKgeY#~#dDN!18Oe?+hW#_S{{;Y&@ zDCS;BeJOEDm2}ylyoUKi@~mDd0c7a`B7uW^*0!N_*mv1pEVm2FJ@jCOJ#lYKm8GZL$riEF0g>s(Kgyg2k032 zeT!%rZK4c$dvj<7t)exwfwoY?N__nx{(OhrJ^Iti*LVNE;CyWVep36t(Z13i$2=#! zUHtlw{Lbms0t@3mdzvSF7F)W)&VChh^07D?r5>sB5j#!W-*L_RSlBf^dqe!o<6tRl zvhQoXAbx+FUz7QxLnPb3-EwQ%eG;GA!2FW=#G~pyyLqI&WNx|Rc#w#-qdB+>zuRhE zP+(n3YXsRB#!{V9>c9WUzB;`c?h(JhqE1Qt>QD>!q|Of)ILYKbzNZs3UzOtQQtzJ@ z|48Vu_DE^X5k_|&r&H9b2#A0Ph=2%)fCz|y2#A0Ph`_%f@S115r$+$X`}gF>Z!;8~ Ax&QzG literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByName.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByName.atx new file mode 100644 index 0000000000000000000000000000000000000000..70ed36c3fea8bbfec79d541e8f5931e81151e214 GIT binary patch literal 12310 zcmeI1`%c0z6vhwU@Avy(dlw0Y#Hi8v0E&P_WM<45-o{7u`#KCOfd&({F7b?fvTo9S z`?cL^&qPEj&;qmw<++9OB+t^7rqrb;cPPcuK&dM=b0(G!%7M2}p7*YGD*+{-1eAah zPy$Lo2`B+2pahhF67WfYYyPvLfdhOWLc7p9bPgRsn-FsZd(alN0bM{l5P!b}v5r${ z6@6!X;$Zu` zm6qJY*#{glZ}L@<&eWA?vrG?uqQc)I*u!w1Jm0=f~{ za^$oiK6ltd$-JF2ahE@vmYeV`N}4{{Dr2k#lz7(ZG`1} z-0d$yBw@6RFb)QVjMwqIb^9YbzAdmv%6J`lmRNt4;6D)f4l1}!uKf8%pBu|R D1zNXT literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByOriginItemTypeID.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByOriginItemTypeID.atx new file mode 100644 index 0000000000000000000000000000000000000000..139b478cc0a4b689389507d63d6ff6ec57496bfa GIT binary patch literal 4118 zcmeI0$*LPM5Jg|+d7kqD*%e5tNm^-NxLZfXasWanRL#a;Tfn`hmdbuqnv7~Da zvXVDR5~iksa+>z&qI-^cz36CWYEFApNBb4jxDFaJ*rl*?LwifnCCyF65ETtHTzivm z!(Nr^j(Tg#le5>U-HiGkjbzkHT{Y9(yQdzxSV>69 zk&{Mi!jcqq_(}qOUL?45a zX$iJ1k>K9l^Paw_AC_bzUkjoJPuZjO%=y+7E|E~F5oS?HnlX(Yi89CDGqtsxub10W zb1DPWowK9;aB95`GFemGY^@E<@wU{erm5^k&9!3M)2OY&WNYP~a`GWCv09aqW6;uy z_MS0EsVKmlWTl$Zw(BKE;69kRJG$dU4;;XLuP?%vVHC4sLbs(h&JBlcGClUFS*UVW zn!&B=>Wc^5|HIVZ@A~tb)4*xqG;kU?4V(r}1E+!0!2dDu>;8%TcxwOi7U1~&8=s}> AV*mgE literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByUUID.atx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.CatRelTypesByUUID.atx new file mode 100644 index 0000000000000000000000000000000000000000..dea48d3ebdbbb51845e4bdaaec08af989846aba8 GIT binary patch literal 4118 zcmeH~Nv`EK3`Kt>skD{8sb&G05fCLxqK0gwO+CE5*YyW7tZ7CzZ~@uACaFVe;Fp(| zmru}jeSrRg-ayy0AEEcq&(J^6N9Y~&1N1xe9rQQ!HS`Vi2lN~C7Wx+Y75WAG3HlTI z9{LKpuP+mLl;q;gCe<^9r0+pKEpQuk`l;MnJRdxFQA?-ljY%?+^kh<=Ue{JVD((5G z2zEvv#NtLG;k0ZN>Y6o0&NPLq_pApYD4ZwBZk0>}tJxQCtT;DZ+N`@7wce4DZIhZQ zF>9Z^PE%cQWtLz&h}DwhY$FZ$aqFfzXQab*R?)r-O;k$Kx{&P_)FhB)ZksC4$JjeF zW7CkcKtdKFf6^)4+K5{E&gWxRRF&rVxL1@Zp~0~s%>#)Vphh?f|Pu}5$)8M6Q*gQ~_Ltq9Fc(9U-D;0c#R%E7x<<_52VJ*qb9 zc&~AI*;crE)M_V@rwzC`$r&Vg&SkZa7+#+b4zpKv5~6CFAf&P=!kmzfR7^NozP{ZZe5OHdS6!@ literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.gdbindexes b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.gdbindexes new file mode 100644 index 0000000000000000000000000000000000000000..2a98c93adab6374c865fccc1045125683eb4a05b GIT binary patch literal 602 zcmaixVGF@f7=<5Fd_a6e{>-eEl9l)@8wnelmHhfSvslCKT4%fW-MP=X_q|P^Ejie@ ztR;^lJA7iMszFamlA7Kv>GywM)d?H<7g`sv6;HpM-C2SERT9zjtKJvR}JS&t08k_L_S#SGPiCL9r)!ePG?#u zD|Q4ysahx?6s@(Qww0my02RfzfQlWpSlXhFMbxo-$p(^zHa0Uicf0%l`~8pa|98_L z05A$Z58z`5AJcD=lkv+NO)%@W%(o2y_P|~YV!;YP02!cx0Pp}9VB60&8blihgD2QJ ziGdc7KnW*kC>IYTAQ>dUFM~8Az=H#wLA`GQTM>{#Jp$tHGk`nrff{bQ8cu709E8Jn zEL>JWfwxe=4mbwGDH&M#Um^mC$%Xq+5LpThqzn+u2!MWe;2ZerT=e2Hr;1g_%U!3&$sD1Sk}3%u2dQ!lx|2VWYH3nNLMgZ; zpg5+jIA(is&}IO70*%$LaC^1qXV0L$MRmGBY9jAaVB2!#gw~K#Z$FLO5w-pCoRb4C zv{D%%BIwm>?Mg?JHHNj(xD^Ib4w3~x&%oA-FYH`5 zbqKe3&97R7Uu^mA2<;V>|FR!(bIF&rA^BeMm-o4>4IL4cOvzAo1r#%gBob7lOzOaV zoAT~~gBuo+qz&XgL_T-_di|^&R|C`L|8o1yT%Q&^8aOG2BT`s~!IBp5=yb=ZBU@AM z`tnO$0%{gIkgRP7uZqUTaszYP)19kAUY~s8nRaW#HP_IcY22wodzTk%WO=vWXgmzP zX+m!DoQ-?l?eu2G=?A>LM}D2d7G@XD6xB4>%)?W@6@dM14lBg@UHpm-xpSX-Ky@vl zBPiNXu408bB~1J<7ACAebJ3CALui~u1*4)otEo@Ebu1_-oAtf_Jg*fe@JweG-1c7o zHeQ%HLDX6)Ri?6#_hN+^1^S8rlH-UK9CJ>fqAZD-y(D5rc~DmI&~(k-S(&8Y`u)|* zWP+-Du7AEB)U3lJf)ynUuZ&RfrLkI4W~~Hz6{S&`W7Gy`XcG8P_;xYRZH{sazg&lvLz2&hm(9Z%5En0G8oPn;j!8}tIAh)txUESgAsTPOo{-cFlP9*jB7X*5+0oKQ;hq{ zoVAU3kHE`?-CZuI1E6sq6*C$wanuT?h?#C`)=>a393HZCpCh}5_zHK_*vq}jcgLo_ z)~w#LzfrtlyeyynTj4Z345sFQ4tzvUgk&ukz4|CpH+4Ax5}BBgYbK@MEG=44W?vw@ zv&p*v=P1qy7HZ)}Mj%@(DPfh0NeuAP*|Neaei(1_fSaYQhrj(mpwZDsn-&LW+8)=6 zd=F0jw`2$?L=J@Q!d1i0N>VzLWA11I_Dp!Su@ts}S22iP+L6@s>+~{iRj7xkwBgYi z^}M8K=LuHG#^Dz~OuyY&iDNq>EQqaE$q8*QOjZvjNtq0>0dWl2oveH8X+9T(+neu3 z7G!jC@y**i{5RR^6=4S!c>LhKB|zrPQ^!Mp1QY^w?s3qt&?go!9>@v&tn8sfYr{5> zuHxqT57{`w@NAk6WJ_vUrqeo+4CKDp+)lle3qq<6{q^z8YP)v)Hj6`hA|jdYo@Q6i zK3VlYFRigm=qpY)d@{GLO{|Zon^Sxvc8=T_mAmnvo<tL4H9cf zB_yd>;lv|!G)Wn5C2$D9XN)&Z&#y$Y>wHDUXF`{b(Q8`#6KAZF`&EP;_%SIU#rtR- Z-lT?Oy_(Q!A$zTLr(Sm~iBS)Le*nHMfzbc} literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.gdbtablx b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000007.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..bf096e13d28fc61c7dc794d7b965ba6083f1f807 GIT binary patch literal 5152 zcmeI$tqlS}00rTN;~y?~+Q3u685C$bpaX)#(*j-KNC*~%q6ZYoIo=e3n@!%AY_e+P zR;83qw#fR~oXZuK-Ef0V6`s)a!X4H;e!zARK5!g{77n9O#@QL_NjSk5cGK{N{Vcp- nRfh|BEMNf(Sik}nuz&?DU;ztQzycPqfCVgIfqxd@_m6RY0`v~Q literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000009.gdbindexes b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000009.gdbindexes new file mode 100644 index 0000000000000000000000000000000000000000..c9d0caa2233c4a1fcbaf3847dab2f14df879a2b9 GIT binary patch literal 116 zcmZQ#U|`?@VmAgC27iWlAa-K#VsK?}W(Z;M1d1^*2mtZ_|BOJA1Bfx@If0@u-N6hQ X42cW{45>gd79eI|0O?}|Vq_Tr4EhZd literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000009.gdbtable b/autotest/ogr/data/filegdb/objectid64/3features.gdb/a00000009.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..451b4e263cc6b611ab030b0bcb1985aa6e4e0b3a GIT binary patch literal 1384 zcmaJ=Ye-XJ7=DlEoH@;Yg#8Rl1BnoEURL`N?s!d8C(aRNvdl~^saUJ1KM4a{&Lk^; zX!WNHxuSw@ts>3raWT4IXS;>OET*RGphz_FCN(mHCdB~2TOjMZg_SLI1>oPbjJB<`z zS%AiRr2*;Rek{*S`Lq>hj2olQvNQkU700{l!DJIy>UfN$IMl{--m!>DIE&{)hZ7wY zW5fWt0GPQzS6O6|4VucJ>^N0YDg0p!;)tQKd<&FjfwjuGjLH}D16exsNklj1ih=iK z-Wjl$fD!1*eD+<338)OQ5NIXneT0sIgAI}So6F~=II+WX8}d}<3b-qrdm(p+A!Hz{ zYOyXI^CPIK1Y$YYkpvaK;Zu|kjQ`{0@ve4w5kLw!vV46i_t4JuRQWYj(q3RNh0HI5 z-__+Y%hV>jED|}pjXdt3ea7Q5>pA!IK<=LW za&47MuqfjvSKk%oD5ZL2iyWb`C;|tZJ>`p}PO$r-(HIu^h^>?Zy6m#qWs#^ADfG`% z1yagj2|w9|^4aWgS!j6L@OnsSum3#ekv@0a)I4bz6YqB2YH^DXx@ulHLY$8?f86g8 zzP|dkqGxJG_TDj_`Ep*k(tSGYUYlD}-7?(D4cR_ho*vUTKI(VxI^7<> zZN_4)b5r3OJEApm#^e+2xsLlpjF!Y9Zmpv}Bta5jNQ%~w7-@uDmpqhat>1KsKqEmydIb~Wf8Sr^#cmuO<_!t$I9D^JbWSXbk(o4{@ zf(BfoM6I$!q*lFxdQqkR literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000001.gdbtablx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000001.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..52fde329b77d5952c4cc4ba8ed62037af103eb28 GIT binary patch literal 4128 zcmeI#F%5t~5Cze_a6Jalf(u-rgB}4WpryvZ^$4Is#pQ+q5hSz8>$aNicM*}$BV)ym u6Biy-3_OjPFk``n0|hrc3s}Gc7O;Q?EMNf(Sik}nuz&^rQ{bKQ-<)sz6$y9% literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000002.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000002.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..0b29eb53e07d77c606e25ebcbd77f7e494d1f585 GIT binary patch literal 2094 zcma)7?QYXB6gAq)#`H7hfha^j_g-ky`G&;7oO4oeiv!*s8&Cy z9k}G{{_&`F*sLEOk;cC3$LSS=SZ3z809XPmr+|Pf@C`&D1}@!!e0$>zICQ}Q0o_jN zh=D%+%1qWf)F%WU)pP+YY82C1Naqxts;YorKYsphQnWv-U8RSH8n#x3s*RO%yp2B- z=n~JFj8o`3spI)$0!LBk`4^MeN&P6SF{tuE7{$qo=95k_p&h0*px-d1G2_GWNF;l!dCjL!k_h74CcHQswo!K>A!HZI7c* zg>YY$cnifpBf`w|aULPkyLLG%KkAZDozR6P5?cSly&-shS~qEu#L{D&Tl9XKNY(AE zMQ!pHb-k9is+7dBPi7>3~w5w)?kAu%Bgzy!o0Xl*TRY#9KTpo6f2i6fX;xPSxL5jqp%i#yPL zmpsM&#a;4tccG*JDVDgQ$By9vCsgS0z-A$3DA8a*5=v*Z_+ZaLM^tztj-&!Lp7>7t wm2|}&+qIPAf(9>)@NxkcZ~+%^0T*xq7jOX=Z~+%^0T-CB0MGpYpTKm(563+c?f?J) literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000003.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000003.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..5300ca64943170bf47c6e0c42c14ffa4c47a1995 GIT binary patch literal 797 zcmZQ(U|?VZ;y*yl3dArV%M4+8Lj~l4w7ZL=kAktDp@E)3N@|&|WwM2lnW2$!s)=q& zW>HBc$OsmYaz+LQ9tI{)1{VefHWntfV1^)u5C&I<2p|bmzyTuI+!zuWau|vkQh~w@ z%p9!9B9Rag_6UYZhERq)hD?SMph|RAQBaX6JZgNQYJAbvaD&VaVaNx%A(f#B=$1T& zWFQM{2UiqM8DCs7_tx%v{}kl+KNA_SZO-%=igM|#uO-zlAER9SJEiFxqfm%WCVrWP^0HPQg!oYMx zi=HExpY;RGXXrTWfX?rIyLG|$V+ZX1to(S%Hz~j#Mh|X}GE_o51f^5Yo+aqf&wtob Ky1bk*JPH7ebh7#Y literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000003.gdbtablx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000003.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..730418420e468d11c7758cf599bd0f363c50b722 GIT binary patch literal 4128 zcmeI$xeWjy3*ltdTw-w)?D(44e^(+Rz*YNVNVx;aiCMt}eT0t5&UAV7cs0RjXF Ld@a!bfUfrd*zEw1 literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000004.freelist b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000004.freelist new file mode 100644 index 0000000000000000000000000000000000000000..040b0de5c092ac282111f17f566bc3fa2dde68f5 GIT binary patch literal 4440 zcmZQ)U|{$U1eZA(80@5g91!D)E|k&b2xV+HhcX0Jpo~s7Amb5Gaf|?vF$zXQU^E0q zLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1O{;kFaq5M>i>fP$eGlpL1wbkz+7s( IliKD10Ap{p$p8QV literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000004.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000004.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..56afc1dd9910f94ae71d40779de43326c9e39027 GIT binary patch literal 47714 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 literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000004.gdbtablx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000004.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..0a43ae9a20008fb30e3e2b4c91943ddc39eb1dba GIT binary patch literal 4128 zcmeI$F%5t~5Czd8M}rGoAOZ;l1vo$fK>|Gu1!_=3#XlJ&v&l4@tzNkWXi}HjbQXBh sJi;(Rzred@x5ItFcIC)XKmi35P(T3%6i`3`1r$&~f!_<%e?YCy2M=oqqyPW_ literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000005.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000005.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..9a0fe221dc4aa99430abf335eefce0eb0d7f5564 GIT binary patch literal 2060 zcmZ{k4@?tR9LEnRe{?|Qk8yEkR)GpCtWXh|qChDHgd%O3E==6@`ltu(UAa4;3~{KS z^2a|6ryEeBf-x>CF;f%~7F|XH{vS%zRWivo2FlnZQ&)-X9kjZ=WtY6%yLaEu-TVE1 z-}iIDFiZ?bHXN>SI4)0*>DgNQ!hCpFB*dkPq+#KbFo_NnhDH-BB9@0oNWrpD9m#OT z{3isw+=;(?q+?nv7Xui9aab~zfMIR|XF(>GIsGtGEbv^1;TQuI6jW%iV$1~RQ%d+! zpoA}tZ~+q>p8D>q}ACJpt!%7I1-3+h6+dWl+lb*!sCozP&vsP=T^88j=cwI_-kmJATV3 zg{5WjSjS~D8r6BlX<3fJ2~LA+4L}NsN5|dTvTTjA@`%ubVL(>K=ZC(tu!dGl}8IvAc5g{trcyGcD)y`NCLuE}k0hU2^ z(X%FN^W5X|s_T;XRnE7psM_zfS|m4ffMQ{7`0TofLyClT8x^P1FB}raj~_repVw+f z9K5+l|A^WtsL4rcNtt&cxOx?VQf>*f5hp1=F)6)+h{ z0_W}VctQF7?RWhY2g+rVG>=)&P_59eb!>$X;HNg7RcP&$o&ED^K!x@9OqB6SB3X`x z?!XPAG@Qu;4Bu~7UUIu`F=wQHWoyybE~y0Q32>cqI)651m|4@(3YV0og-f4|WHs1O zrI*99Qzqx#)U5B%KGAqIp)BNn*3&3KJ4$$~aFzp%BM2UO?$j1}Lq3^u>U@!N-;M^~ z|3hT(Y(vxXxhkE=FRE_tOWz(uSmq2L2}cVlv>yh?Z}?h76+{;5q^(IUUFKC=g3nz= z8Fvj}7+5zxZc7dAWFowFu9SC*aJf!@yt!P*b-Z} z*T+0X34d5U2C#tQraHr$t`Ip=V&0jOp4^eN?yZCVSsPGLXb;;F3ZJp7F>x=F7Dgt2 zGaSAtdTYikl<`iZS^Ul63x&w;6P7EhYo5!iKYIJ;cZa&pq9*oK;glY>o+<0{P0qJ^ zc}>`nt*HlB^mHr9;e9CJE2DKlC;9+46gV*cKYDO5YuC$xw7TlEUvmL!3+hNubumSV z>{jN1znsCn4TrkrmH^}J!?Q~M)262ZdrRbN|NRnu)Oh>lq{?$OvW^bpdNg=pCPx}b zuGrBK`Fh@r9r;)<9+Y=Ze!BTH^}b(G#!CYmJa7Dk#NI>E`0)_zysjjW*5CjYxR8e+lf~l%xfiA&MXF#5p zeCCQPes^D#6tG5u1_J`a4Nj=hV#I1D9Z}oYyVL-7s-56uchUyv6h$b|n1+A#v zW8~>9aJx{7YII_gjpF^4CBKfLM;&F?8d0SZun0u-PC1t>rP3j9o9 N|F-_R`L`{<&M#+pK3@O; literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000006.gdbindexes b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000006.gdbindexes new file mode 100644 index 0000000000000000000000000000000000000000..9e15d35d613629194e6fd897de84c6d110e2bfa8 GIT binary patch literal 66 vcmZQ%U|`?@VmAgC27iWlATDCaWJm|%JO)po7y|n=;#MK literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000006.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000006.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..1da41b719ca6c0ab6209542a3d8617aeaf4cbc11 GIT binary patch literal 740 zcmZQ(U|?Ve;%Pw43dAt*gbBj(h6>07X?GV#9|dDQLjyg7l+-d^%VY~9GeaZeR1@8l z%%YM?kP$2(<%}RBIs6%t7_t~r8Il=F7(5wV7#P@Cm{>v?LLnS(H5Q z$Gz|ON{ze@M=5lJWPA6W{_POuus^iz!=#8%cbo=Iy~HoX`Ip6QwwCDKO>XO-q8qek z5tqoiPjU`z_uBp+JNZrxr$K>-85KGf*7!Ate7JY9yki!+K~FYhtlp9z<7>3Mim}0tzUgfC38qN1*-zRqX?%$^%;f literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000007.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000007.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..231ae0568629460f3704635485ad82e2e607d757 GIT binary patch literal 3665 zcmb7G3s4hR6n!WtDhLXK3QEw?qWIyXh>8j%1R^9UKR;zUx+I&h7&fq3lqg!uAB;k+ z*bxM^YN3i!q}GaJD?{-EDvEyr(RRdQso$}RIQDI_0rFrPo0-h+eS7ac=bm@p-5m@7 z7!UvL@NWkH`d{cs-CZ!d-~_LMJiic*=PY-(S#DB%?acWSZx1hb4^Mo~OesOpY9@s# z<8A_gIWQN4Xb=lqK2XA>>)I857jfHw)G?Rm zr{8U=K<$xyr9z5R0#bLi{uHBdlIG|vj#y^JjUg1$UfTq6hcM-58$VxE-yzn7)-No* zm3$9Pd*g^%+MBJP_~(`_x!5|?Gs8ka<8gWrORkcnF@jWam{l9r!lfJm$pV>xBnD;W z0?EEAeh@En$nC=qkNq%9kf5TEH!t_kHa$s+JPx{L7vFW+{2@-5JyrCu0+VmF=V3Ia z!f6PEV{{^gv$k~)AiLmTh-u(#0@a1sho2`k|1qbGTNUUiI^5WP4y_Qyw(7;!D%uaz zwilBiIjpw{h+)9)<+RzmyIvCJw%iXZ&U(W|lXpI!mtm@r1sz!G__NCvABh!D83zRv zkZc;O-U77`2E+oe$C$#7L{VXL90Vo#kuPV;C+$YDP}`2hP=(S}t7wA6XhO-D0HiW7))iTeBgRehul^_deT(hYj;64LwX)7B+)Ucvk}nqx z9hRhupOHRzY4M~Pe|1C^cGV3N=TFk(eA;aac!= z)^jXuSelR!&$dD~5~oa&6xsi|&mHCEXvm9A#%_%!il&fDr$zU!ok=C}oTu4BtMy*vgZ=T^gKt$->e>e*0Ftf6*q#cSdEtYZZRI<;O~Ta(L0(LHO+z z-_!*ZO^&;|*Lp+X*l;x|=_#8r*whxxy}oEiwC%JHdn(s<-T8zD^_>%betEV(&09P< z_7sYQR&L9pie-H$3{bXSA^3{m^yS13#P`!{^1TjC;8x_@tG``VckG(0eBR5GXM?hj zYq+OQ#Vtp>P~TYW`y!Gi6a=kiN0G7P*KaDZ;k`pzI}k5CxY^*n{km`3lHcyWU*y_~ zazYQFejpl>F+76Cp|!K9PJC&{CbtpX($rOtG|vkjU-9i&EuZ=@p!&lXG26p)AER<0 zvnYbXB=|>)(QhbdgL|dWc7-O^?%nQRo&ENd)T?~SX*`qOz~3{#%U)mQRN2<^3RbVU zc3r$aHKHNB;bO=MZb6~6dF6#&)udDoslJ@iFa!V)qRw@XnI`Zr@uNt#Xlq{-}Gpmw5b)U=wO7=fF{R_fzjWYlM literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000007.gdbtablx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000007.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..5ed1ca9acb224f26cbb87a4b3dbafe1271471552 GIT binary patch literal 4128 zcmeI$s|^BC5Jus{-d)}#=q-d2sDV0GfdPTXq9}#X2^B!-fN*XVn17N_Ofu@oDZog* zT5VizDh-_U(yqWmTMwQZI(TasLo}f%*@q>iht#xDERN literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.gdbindexes b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.gdbindexes new file mode 100644 index 0000000000000000000000000000000000000000..cc24e2a06b9b9cd5f6fe51035fb99fb73263853b GIT binary patch literal 116 zcmZY0I|_h600Yrc6cjAHl3&(sR?y!2pJE*gL9lo!5>f)4R%Q~HMj{InCz~Dg%DFHb V{rA@PeIEn{cK=Wbo?or+as&A53=;qV literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..c99336adb5db16106db7d62144923ba71a1b38b3 GIT binary patch literal 566 zcmaJ;yGjE=6g?Zw14R4;C3UjwBd$rKu8C}n#)NEwg-F2cqOq_DqNKDF3;)ByUyy)x zK!UZOpoon{P|wWlHWHi*bMDMNbI#nEd8qNC0ZFC=Ih=&T-Dtf`^eXP&imjW5ZUo_F zzSJvPj%nFpAs_Sy7sF?sO_8R}0SP31RN za!sqZ9*$!C;RwcKSFX>#)c(!W=HK<_G!@*|%Inknk4gQwxe=q}{WrzGEjXD5woqfk cWws%n6RAw%_2Kb-iN)_l?RxZfH(5~gFLv8qJ^%m! literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.gdbtablx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..a3af82aa62a81940b5bef5675e1bb499990740a4 GIT binary patch literal 37990 zcmeI$%L#-q5CG6oRy$hIzagj07ZNRrvLx| literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.spx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a00000009.spx new file mode 100644 index 0000000000000000000000000000000000000000..5fa796d466b1859b760d8a500afd3d9fc8111303 GIT binary patch literal 65566 zcmeIup%DNu3f)4R%Q~HMj{InCz~Dg%DFHb V{rA@PeIEn{cK=Wbo?or+as&A53=;qV literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..c99336adb5db16106db7d62144923ba71a1b38b3 GIT binary patch literal 566 zcmaJ;yGjE=6g?Zw14R4;C3UjwBd$rKu8C}n#)NEwg-F2cqOq_DqNKDF3;)ByUyy)x zK!UZOpoon{P|wWlHWHi*bMDMNbI#nEd8qNC0ZFC=Ih=&T-Dtf`^eXP&imjW5ZUo_F zzSJvPj%nFpAs_Sy7sF?sO_8R}0SP31RN za!sqZ9*$!C;RwcKSFX>#)c(!W=HK<_G!@*|%Inknk4gQwxe=q}{WrzGEjXD5woqfk cWws%n6RAw%_2Kb-iN)_l?RxZfH(5~gFLv8qJ^%m! literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.gdbtablx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..d4842ffc23d1d41f9374b4e50a0e78b172693d57 GIT binary patch literal 37990 zcmeI$yA6Xd5CG5vg^n$<2z91l6Bvbp$ub3UlW+ngBnsLndMDXFe+v6m);%JkT3bf5 z4SE6u2oNAZfB*pk1nvc%_5B$q2oNAZfB*pkKPS+=w`$;P%(`Ia)7+PK{n}){43`TZ z#ly>n1p)*J5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAn?xu)#-85+?TeF MIIrdQG5_rG1&m)1>i_@% literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.horizon b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.horizon new file mode 100644 index 0000000000000000000000000000000000000000..b64b92356a70378da21d5ffdecaaca5124025076 GIT binary patch literal 32 YcmZQ#0D;N_OdzTu3_>$hIzagj07ZNRrvLx| literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.spx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000a.spx new file mode 100644 index 0000000000000000000000000000000000000000..5fa796d466b1859b760d8a500afd3d9fc8111303 GIT binary patch literal 65566 zcmeIup%DNu3f)4R%Q~HMj{InCz~Dg%DFHb V{rA@PeIEn{cK=Wbo?or+as&A53=;qV literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..c99336adb5db16106db7d62144923ba71a1b38b3 GIT binary patch literal 566 zcmaJ;yGjE=6g?Zw14R4;C3UjwBd$rKu8C}n#)NEwg-F2cqOq_DqNKDF3;)ByUyy)x zK!UZOpoon{P|wWlHWHi*bMDMNbI#nEd8qNC0ZFC=Ih=&T-Dtf`^eXP&imjW5ZUo_F zzSJvPj%nFpAs_Sy7sF?sO_8R}0SP31RN za!sqZ9*$!C;RwcKSFX>#)c(!W=HK<_G!@*|%Inknk4gQwxe=q}{WrzGEjXD5woqfk cWws%n6RAw%_2Kb-iN)_l?RxZfH(5~gFLv8qJ^%m! literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.gdbtablx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..eee05bd71076531d442af3f15f43fb65db34b54f GIT binary patch literal 37990 zcmeI$!3l&g5CFhc&Yv$U2wXE_RVN5`Xgivy2bK_I7Lt(7XLmk<{X|5QSBdUA>;wo9 zAV7cs0RjXF5U4=l+g4D75(p3=K!CvO1lDahdzyY_E?6dmX`Gin|GW2;Fz0KGRAW(U zB0zuu0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5;&7Yj6x-qJWPeH-!2mFNF` GzvBQTPYwM5 literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.horizon b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.horizon new file mode 100644 index 0000000000000000000000000000000000000000..b64b92356a70378da21d5ffdecaaca5124025076 GIT binary patch literal 32 YcmZQ#0D;N_OdzTu3_>$hIzagj07ZNRrvLx| literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.spx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000b.spx new file mode 100644 index 0000000000000000000000000000000000000000..5fa796d466b1859b760d8a500afd3d9fc8111303 GIT binary patch literal 65566 zcmeIup%DNu3f)4R%Q~HMj{InCz~Dg%DFHb V{rA@PeIEn{cK=Wbo?or+as&A53=;qV literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..c99336adb5db16106db7d62144923ba71a1b38b3 GIT binary patch literal 566 zcmaJ;yGjE=6g?Zw14R4;C3UjwBd$rKu8C}n#)NEwg-F2cqOq_DqNKDF3;)ByUyy)x zK!UZOpoon{P|wWlHWHi*bMDMNbI#nEd8qNC0ZFC=Ih=&T-Dtf`^eXP&imjW5ZUo_F zzSJvPj%nFpAs_Sy7sF?sO_8R}0SP31RN za!sqZ9*$!C;RwcKSFX>#)c(!W=HK<_G!@*|%Inknk4gQwxe=q}{WrzGEjXD5woqfk cWws%n6RAw%_2Kb-iN)_l?RxZfH(5~gFLv8qJ^%m! literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.gdbtablx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..b83ce08a274d3517fcfd1c066e081bf9228a80b6 GIT binary patch literal 37990 zcmeI*u?>VE6aY}pc%`+iiQ8DXgBv)4gEi%Qd=THo OxoxU*UQRCiza#z|+YS)` literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.horizon b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.horizon new file mode 100644 index 0000000000000000000000000000000000000000..b64b92356a70378da21d5ffdecaaca5124025076 GIT binary patch literal 32 YcmZQ#0D;N_OdzTu3_>$hIzagj07ZNRrvLx| literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.spx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000c.spx new file mode 100644 index 0000000000000000000000000000000000000000..5fa796d466b1859b760d8a500afd3d9fc8111303 GIT binary patch literal 65566 zcmeIup%DNu3f)4R%Q~HMj{InCz~Dg%DFHb V{rA@PeIEn{cK=Wbo?or+as&A53=;qV literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..c99336adb5db16106db7d62144923ba71a1b38b3 GIT binary patch literal 566 zcmaJ;yGjE=6g?Zw14R4;C3UjwBd$rKu8C}n#)NEwg-F2cqOq_DqNKDF3;)ByUyy)x zK!UZOpoon{P|wWlHWHi*bMDMNbI#nEd8qNC0ZFC=Ih=&T-Dtf`^eXP&imjW5ZUo_F zzSJvPj%nFpAs_Sy7sF?sO_8R}0SP31RN za!sqZ9*$!C;RwcKSFX>#)c(!W=HK<_G!@*|%Inknk4gQwxe=q}{WrzGEjXD5woqfk cWws%n6RAw%_2Kb-iN)_l?RxZfH(5~gFLv8qJ^%m! literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.gdbtablx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..f343e6d9eb39e33e7edb1a3c4dae26d688aa3d6f GIT binary patch literal 37990 zcmeI$I|{-;5CG6gh-j0LMsk3?ND4vl3SPzwdM4e$2$nY6gg3BXmVtTQ&JYn@9T|tu zVkSU<0D(pVrEip2S^@+J5FkK+009C72oNCfr@;AnKjzchBm08c8JyR$jP=Kw`)ge4 zEC}pX;I!Am)J1>*0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!894fv)D9w!HIN Pma&h>yWjYpUt7EZx&#al literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.horizon b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.horizon new file mode 100644 index 0000000000000000000000000000000000000000..b64b92356a70378da21d5ffdecaaca5124025076 GIT binary patch literal 32 YcmZQ#0D;N_OdzTu3_>$hIzagj07ZNRrvLx| literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.spx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000d.spx new file mode 100644 index 0000000000000000000000000000000000000000..5fa796d466b1859b760d8a500afd3d9fc8111303 GIT binary patch literal 65566 zcmeIup%DNu3f)4R%Q~HMj{InCz~Dg%DFHb V{rA@PeIEn{cK=Wbo?or+as&A53=;qV literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..c99336adb5db16106db7d62144923ba71a1b38b3 GIT binary patch literal 566 zcmaJ;yGjE=6g?Zw14R4;C3UjwBd$rKu8C}n#)NEwg-F2cqOq_DqNKDF3;)ByUyy)x zK!UZOpoon{P|wWlHWHi*bMDMNbI#nEd8qNC0ZFC=Ih=&T-Dtf`^eXP&imjW5ZUo_F zzSJvPj%nFpAs_Sy7sF?sO_8R}0SP31RN za!sqZ9*$!C;RwcKSFX>#)c(!W=HK<_G!@*|%Inknk4gQwxe=q}{WrzGEjXD5woqfk cWws%n6RAw%_2Kb-iN)_l?RxZfH(5~gFLv8qJ^%m! literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.gdbtablx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.gdbtablx new file mode 100644 index 0000000000000000000000000000000000000000..3a77f69dfcb1325bbc278c475b3bd6f26a01ef60 GIT binary patch literal 155936 zcmeI$%L&3j5CG8GMZrr>DZxg(Y5}Ro5(0u%q#7I2U6Mcw@L=3Gu>Z@me^^<9prciULc?$hIzagj07ZNRrvLx| literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.spx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000e.spx new file mode 100644 index 0000000000000000000000000000000000000000..5fa796d466b1859b760d8a500afd3d9fc8111303 GIT binary patch literal 65566 zcmeIup%DNu3f)4R%Q~HMj{InCz~Dg%DFHb V{rA@PeIEn{cK=Wbo?or+as&A53=;qV literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.gdbtable b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.gdbtable new file mode 100644 index 0000000000000000000000000000000000000000..e035332fb4d83d3d25f7581e1cf0deff6deab17e GIT binary patch literal 642 zcmb_ZyG{Z@6g`XbNR06lG}N%!2O=~oE@lx$2n(?=hD3IwSXdCEq14J)_#YPjf+4Xc zF~r(W&=?yFqMn&qEDW*o-t3$^GiT13J39*%PZ|(qDLxUG5rx|dut;=^g`Fi^Hx1qJ zgR^wLowKs0We1tG-|6=HkCcd!rp*8mL_L&HKng12`N@Y`zj|2zJ%1m^{Ohu_I=+1yRSs)w6O_Ea2L7?1NK~vVJN326Uas&tvAV7cs0RjXF5U5FD=@zf1k4vot2oNAZfB*pk1PBly zK!5-N0_OrNkGRfJ93en}009C72oNAZfB*pk1PBlyK!5-N0t5&UAVAZcuM>4@s_r|=!T3So<~rF5(p3=P`$uNzSFCB z^acnJs7m0ms%jN`S-{2YvXK!42oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0@o9mo}8-P?ho@O zA}zfr|GC-jj``9r?IMqEzxw%dp3jp1>E|Ev@mX&Fa^$Xt009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly mK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNA}2Z70z=llo3t{Lh8 literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.horizon b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.horizon new file mode 100644 index 0000000000000000000000000000000000000000..b64b92356a70378da21d5ffdecaaca5124025076 GIT binary patch literal 32 YcmZQ#0D;N_OdzTu3_>$hIzagj07ZNRrvLx| literal 0 HcmV?d00001 diff --git a/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.spx b/autotest/ogr/data/filegdb/objectid64/with_holes_8.gdb/a0000000f.spx new file mode 100644 index 0000000000000000000000000000000000000000..5fa796d466b1859b760d8a500afd3d9fc8111303 GIT binary patch literal 65566 zcmeIup%DNu3Reset(); } - virtual int GetNextRowSortedByFID() override; + virtual int64_t GetNextRowSortedByFID() override; - virtual int GetRowCount() override + virtual int64_t GetRowCount() override { return poTable->GetTotalRecordCount(); } - virtual int GetNextRowSortedByValue() override + virtual int64_t GetNextRowSortedByValue() override { return poParentIter->GetNextRowSortedByValue(); } @@ -129,8 +129,8 @@ class FileGDBTrivialIterator final : public FileGDBIterator return poParentIter->GetMaxValue(eOutType); } - virtual int GetMinMaxSumCount(double &dfMin, double &dfMax, double &dfSum, - int &nCount) override + virtual bool GetMinMaxSumCount(double &dfMin, double &dfMax, double &dfSum, + int &nCount) override { return poParentIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); } @@ -144,8 +144,8 @@ class FileGDBNotIterator final : public FileGDBIterator { FileGDBIterator *poIterBase = nullptr; FileGDBTable *poTable = nullptr; - int iRow = 0; - int iNextRowBase = -1; + int64_t iRow = 0; + int64_t iNextRowBase = -1; int bNoHoles = 0; FileGDBNotIterator(const FileGDBNotIterator &) = delete; @@ -161,8 +161,8 @@ class FileGDBNotIterator final : public FileGDBIterator } virtual void Reset() override; - virtual int GetNextRowSortedByFID() override; - virtual int GetRowCount() override; + virtual int64_t GetNextRowSortedByFID() override; + virtual int64_t GetRowCount() override; }; /************************************************************************/ @@ -173,8 +173,8 @@ class FileGDBAndIterator final : public FileGDBIterator { FileGDBIterator *poIter1 = nullptr; FileGDBIterator *poIter2 = nullptr; - int iNextRow1 = -1; - int iNextRow2 = -1; + int64_t iNextRow1 = -1; + int64_t iNextRow2 = -1; bool m_bTakeOwnershipOfIterators = false; FileGDBAndIterator(const FileGDBAndIterator &) = delete; @@ -191,7 +191,7 @@ class FileGDBAndIterator final : public FileGDBIterator } virtual void Reset() override; - virtual int GetNextRowSortedByFID() override; + virtual int64_t GetNextRowSortedByFID() override; }; /************************************************************************/ @@ -203,8 +203,8 @@ class FileGDBOrIterator final : public FileGDBIterator FileGDBIterator *poIter1 = nullptr; FileGDBIterator *poIter2 = nullptr; int bIteratorAreExclusive = false; - int iNextRow1 = -1; - int iNextRow2 = -1; + int64_t iNextRow1 = -1; + int64_t iNextRow2 = -1; bool bHasJustReset = true; FileGDBOrIterator(const FileGDBOrIterator &) = delete; @@ -221,8 +221,8 @@ class FileGDBOrIterator final : public FileGDBIterator } virtual void Reset() override; - virtual int GetNextRowSortedByFID() override; - virtual int GetRowCount() override; + virtual int64_t GetNextRowSortedByFID() override; + virtual int64_t GetRowCount() override; }; /************************************************************************/ @@ -230,7 +230,9 @@ class FileGDBOrIterator final : public FileGDBIterator /************************************************************************/ constexpr int MAX_DEPTH = 3; -constexpr int FGDB_PAGE_SIZE = 4096; +constexpr int FGDB_PAGE_SIZE_V1 = 4096; +constexpr int FGDB_PAGE_SIZE_V2 = 65536; +constexpr int MAX_FGDB_PAGE_SIZE = FGDB_PAGE_SIZE_V2; class FileGDBIndexIteratorBase : virtual public FileGDBIterator { @@ -238,40 +240,67 @@ class FileGDBIndexIteratorBase : virtual public FileGDBIterator FileGDBTable *poParent = nullptr; bool bAscending = false; VSILFILE *fpCurIdx = nullptr; + + //! Version of .atx/.spx: 1 or 2 + GUInt32 m_nVersion = 0; + + // Number of pages of size m_nPageSize GUInt32 m_nPageCount = 0; + + //! Page size in bytes: 4096 for v1 format, 65536 for v2 + int m_nPageSize = 0; + + //! Maximum number of features or sub-pages referenced by a page. GUInt32 nMaxPerPages = 0; + + //! Size of ObjectID referenced in pages, in bytes. + // sizeof(uint32_t) for V1, sizeof(uint64_t) for V2 + GUInt32 m_nObjectIDSize = 0; + + //! Size of the indexed value, in bytes. GUInt32 m_nValueSize = 0; - GUInt32 nOffsetFirstValInPage = 0; - GUInt32 nValueCountInIdx = 0; + + //! Non-leaf page header size in bytes. 8 for V1, 12 for V2 + GUInt32 m_nNonLeafPageHeaderSize = 0; + + //! Leaf page header size in bytes. 12 for V1, 20 for V2 + GUInt32 m_nLeafPageHeaderSize = 0; + + //! Offset within a page at which the first indexed value is found. + GUInt32 m_nOffsetFirstValInPage = 0; + + //! Number of values referenced in the index. + GUInt64 m_nValueCountInIdx = 0; + GUInt32 nIndexDepth = 0; #ifdef DEBUG - int iLoadedPage[MAX_DEPTH]; + uint64_t iLoadedPage[MAX_DEPTH]; #endif int iFirstPageIdx[MAX_DEPTH]; int iLastPageIdx[MAX_DEPTH]; int iCurPageIdx[MAX_DEPTH]; GUInt32 nSubPagesCount[MAX_DEPTH]; - GUInt32 nLastPageAccessed[MAX_DEPTH]; + uint64_t nLastPageAccessed[MAX_DEPTH]; int iCurFeatureInPage = -1; int nFeaturesInPage = 0; bool bEOF = false; - GByte abyPage[MAX_DEPTH][FGDB_PAGE_SIZE]; - GByte abyPageFeature[FGDB_PAGE_SIZE]; + GByte abyPage[MAX_DEPTH][MAX_FGDB_PAGE_SIZE]; + GByte abyPageFeature[MAX_FGDB_PAGE_SIZE]; - typedef lru11::Cache> CacheType; + typedef lru11::Cache> CacheType; std::array m_oCachePage{ {CacheType{2, 0}, CacheType{2, 0}, CacheType{2, 0}}}; CacheType m_oCacheFeaturePage{2, 0}; bool ReadTrailer(const std::string &osFilename); - int ReadPageNumber(int iLevel); - int LoadNextPage(int iLevel); - virtual bool FindPages(int iLevel, int nPage) = 0; - int LoadNextFeaturePage(); + uint64_t ReadPageNumber(int iLevel); + bool LoadNextPage(int iLevel); + virtual bool FindPages(int iLevel, uint64_t nPage) = 0; + bool LoadNextFeaturePage(); FileGDBIndexIteratorBase(FileGDBTable *poParent, int bAscending); @@ -307,7 +336,7 @@ class FileGDBIndexIterator final : public FileGDBIndexIteratorBase int iSorted = 0; int nSortedCount = -1; - int *panSortedRows = nullptr; + int64_t *panSortedRows = nullptr; int SortRows(); GUInt16 asUTF16Str[MAX_CAR_COUNT_INDEXED_STR]; @@ -321,8 +350,8 @@ class FileGDBIndexIterator final : public FileGDBIndexIteratorBase const OGRField *GetMinMaxValue(OGRField *psField, int &eOutType, int bIsMin); - virtual bool FindPages(int iLevel, int nPage) override; - int GetNextRow(); + virtual bool FindPages(int iLevel, uint64_t nPage) override; + int64_t GetNextRow(); FileGDBIndexIterator(FileGDBTable *poParent, int bAscending); int SetConstraint(int nFieldIdx, FileGDBSQLOp op, @@ -343,19 +372,19 @@ class FileGDBIndexIterator final : public FileGDBIndexIteratorBase OGRFieldType eOGRFieldType, const OGRField *psValue); - virtual int GetNextRowSortedByFID() override; - virtual int GetRowCount() override; + virtual int64_t GetNextRowSortedByFID() override; + virtual int64_t GetRowCount() override; virtual void Reset() override; - virtual int GetNextRowSortedByValue() override + virtual int64_t GetNextRowSortedByValue() override { return GetNextRow(); } virtual const OGRField *GetMinValue(int &eOutType) override; virtual const OGRField *GetMaxValue(int &eOutType) override; - virtual int GetMinMaxSumCount(double &dfMin, double &dfMax, double &dfSum, - int &nCount) override; + virtual bool GetMinMaxSumCount(double &dfMin, double &dfMax, double &dfSum, + int &nCount) override; }; /************************************************************************/ @@ -384,7 +413,7 @@ const OGRField *FileGDBIterator::GetMaxValue(int &eOutType) /* GetNextRowSortedByValue() */ /************************************************************************/ -int FileGDBIterator::GetNextRowSortedByValue() +int64_t FileGDBIterator::GetNextRowSortedByValue() { PrintError(); return -1; @@ -394,15 +423,15 @@ int FileGDBIterator::GetNextRowSortedByValue() /* GetMinMaxSumCount() */ /************************************************************************/ -int FileGDBIterator::GetMinMaxSumCount(double &dfMin, double &dfMax, - double &dfSum, int &nCount) +bool FileGDBIterator::GetMinMaxSumCount(double &dfMin, double &dfMax, + double &dfSum, int &nCount) { PrintError(); dfMin = 0.0; dfMax = 0.0; dfSum = 0.0; nCount = 0; - return FALSE; + return false; } /************************************************************************/ @@ -475,10 +504,10 @@ FileGDBIterator *FileGDBIterator::BuildOr(FileGDBIterator *poIter1, /* GetRowCount() */ /************************************************************************/ -int FileGDBIterator::GetRowCount() +int64_t FileGDBIterator::GetRowCount() { Reset(); - int nCount = 0; + int64_t nCount = 0; while (GetNextRowSortedByFID() >= 0) nCount++; Reset(); @@ -498,7 +527,7 @@ FileGDBTrivialIterator::FileGDBTrivialIterator(FileGDBIterator *poParentIterIn) /* GetNextRowSortedByFID() */ /************************************************************************/ -int FileGDBTrivialIterator::GetNextRowSortedByFID() +int64_t FileGDBTrivialIterator::GetNextRowSortedByFID() { if (iRow < poTable->GetTotalRecordCount()) return iRow++; @@ -541,7 +570,7 @@ void FileGDBNotIterator::Reset() /* GetNextRowSortedByFID() */ /************************************************************************/ -int FileGDBNotIterator::GetNextRowSortedByFID() +int64_t FileGDBNotIterator::GetNextRowSortedByFID() { if (iNextRowBase < 0) { @@ -579,7 +608,7 @@ int FileGDBNotIterator::GetNextRowSortedByFID() /* GetRowCount() */ /************************************************************************/ -int FileGDBNotIterator::GetRowCount() +int64_t FileGDBNotIterator::GetRowCount() { return poTable->GetValidRecordCount() - poIterBase->GetRowCount(); } @@ -626,7 +655,7 @@ void FileGDBAndIterator::Reset() /* GetNextRowSortedByFID() */ /************************************************************************/ -int FileGDBAndIterator::GetNextRowSortedByFID() +int64_t FileGDBAndIterator::GetNextRowSortedByFID() { if (iNextRow1 == iNextRow2) { @@ -697,7 +726,7 @@ void FileGDBOrIterator::Reset() /* GetNextRowSortedByFID() */ /************************************************************************/ -int FileGDBOrIterator::GetNextRowSortedByFID() +int64_t FileGDBOrIterator::GetNextRowSortedByFID() { if (bHasJustReset) { @@ -708,19 +737,19 @@ int FileGDBOrIterator::GetNextRowSortedByFID() if (iNextRow1 < 0) { - int iVal = iNextRow2; + auto iVal = iNextRow2; iNextRow2 = poIter2->GetNextRowSortedByFID(); return iVal; } if (iNextRow2 < 0 || iNextRow1 < iNextRow2) { - int iVal = iNextRow1; + auto iVal = iNextRow1; iNextRow1 = poIter1->GetNextRowSortedByFID(); return iVal; } if (iNextRow2 < iNextRow1) { - int iVal = iNextRow2; + auto iVal = iNextRow2; iNextRow2 = poIter2->GetNextRowSortedByFID(); return iVal; } @@ -728,7 +757,7 @@ int FileGDBOrIterator::GetNextRowSortedByFID() if (bIteratorAreExclusive) PrintError(); - int iVal = iNextRow1; + auto iVal = iNextRow1; iNextRow1 = poIter1->GetNextRowSortedByFID(); iNextRow2 = poIter2->GetNextRowSortedByFID(); return iVal; @@ -738,7 +767,7 @@ int FileGDBOrIterator::GetNextRowSortedByFID() /* GetRowCount() */ /************************************************************************/ -int FileGDBOrIterator::GetRowCount() +int64_t FileGDBOrIterator::GetRowCount() { if (bIteratorAreExclusive) return poIter1->GetRowCount() + poIter2->GetRowCount(); @@ -790,61 +819,109 @@ bool FileGDBIndexIteratorBase::ReadTrailer(const std::string &osFilename) VSIFSeekL(fpCurIdx, 0, SEEK_END); vsi_l_offset nFileSize = VSIFTellL(fpCurIdx); - returnErrorIf(nFileSize < FGDB_PAGE_SIZE + 22); - - VSIFSeekL(fpCurIdx, nFileSize - 22, SEEK_SET); - GByte abyTrailer[22]; - returnErrorIf(VSIFReadL(abyTrailer, 22, 1, fpCurIdx) != 1); + constexpr int V1_TRAILER_SIZE = 22; + constexpr int V2_TRAILER_SIZE = 30; + returnErrorIf(nFileSize < V1_TRAILER_SIZE); + + GByte abyTrailer[V2_TRAILER_SIZE]; + VSIFSeekL(fpCurIdx, nFileSize - sizeof(uint32_t), SEEK_SET); + returnErrorIf(VSIFReadL(abyTrailer, sizeof(uint32_t), 1, fpCurIdx) != 1); + m_nVersion = GetUInt32(abyTrailer, 0); + returnErrorIf(m_nVersion != 1 && m_nVersion != 2); + + if (m_nVersion == 1) + { + m_nPageSize = FGDB_PAGE_SIZE_V1; + VSIFSeekL(fpCurIdx, nFileSize - V1_TRAILER_SIZE, SEEK_SET); + returnErrorIf(VSIFReadL(abyTrailer, V1_TRAILER_SIZE, 1, fpCurIdx) != 1); + + m_nPageCount = + static_cast((nFileSize - V1_TRAILER_SIZE) / m_nPageSize); + + m_nValueSize = abyTrailer[0]; + m_nObjectIDSize = static_cast(sizeof(uint32_t)); + m_nNonLeafPageHeaderSize = 8; + m_nLeafPageHeaderSize = 12; + + nMaxPerPages = (m_nPageSize - m_nLeafPageHeaderSize) / + (m_nObjectIDSize + m_nValueSize); + m_nOffsetFirstValInPage = + m_nLeafPageHeaderSize + nMaxPerPages * m_nObjectIDSize; + + GUInt32 nMagic1 = GetUInt32(abyTrailer + 2, 0); + returnErrorIf(nMagic1 != 1); + + nIndexDepth = GetUInt32(abyTrailer + 6, 0); + /* CPLDebug("OpenFileGDB", "nIndexDepth = %u", nIndexDepth); */ + returnErrorIf(!(nIndexDepth >= 1 && nIndexDepth <= MAX_DEPTH + 1)); + + m_nValueCountInIdx = GetUInt32(abyTrailer + 10, 0); + /* CPLDebug("OpenFileGDB", "m_nValueCountInIdx = %u", m_nValueCountInIdx); */ + /* negative like in sample_clcV15_esri_v10.gdb/a00000005.FDO_UUID.atx */ + if ((m_nValueCountInIdx >> (8 * sizeof(m_nValueCountInIdx) - 1)) != 0) + { + CPLDebugOnly("OpenFileGDB", "m_nValueCountInIdx=%u", + static_cast(m_nValueCountInIdx)); + return false; + } - m_nPageCount = static_cast((nFileSize - 22) / FGDB_PAGE_SIZE); + /* QGIS_TEST_101.gdb/a00000006.FDO_UUID.atx */ + /* or .spx file from test dataset https://github.com/OSGeo/gdal/issues/5888 + */ + if (m_nValueCountInIdx == 0 && nIndexDepth == 1) + { + VSIFSeekL(fpCurIdx, 4, SEEK_SET); + GByte abyBuffer[4]; + returnErrorIf(VSIFReadL(abyBuffer, 4, 1, fpCurIdx) != 1); + m_nValueCountInIdx = GetUInt32(abyBuffer, 0); + } + /* PreNIS.gdb/a00000006.FDO_UUID.atx has depth 2 and the value of */ + /* m_nValueCountInIdx is 11 which is not the number of non-null values */ + else if (m_nValueCountInIdx < nMaxPerPages && nIndexDepth > 1) + { + if (m_nValueCountInIdx > 0 && poParent->IsFileGDBV9() && + strstr(osFilename.c_str(), "blk_key_index.atx")) + { + // m_nValueCountInIdx not reliable in FileGDB v9 .blk_key_index.atx + // but index seems to be OK + return true; + } - m_nValueSize = abyTrailer[0]; + CPLDebugOnly( + "OpenFileGDB", + "m_nValueCountInIdx=%u < nMaxPerPages=%u, nIndexDepth=%u", + static_cast(m_nValueCountInIdx), nMaxPerPages, + nIndexDepth); + return false; + } + } + else + { + m_nPageSize = FGDB_PAGE_SIZE_V2; + VSIFSeekL(fpCurIdx, nFileSize - V2_TRAILER_SIZE, SEEK_SET); + returnErrorIf(VSIFReadL(abyTrailer, V2_TRAILER_SIZE, 1, fpCurIdx) != 1); - nMaxPerPages = (FGDB_PAGE_SIZE - 12) / (4 + m_nValueSize); - nOffsetFirstValInPage = 12 + nMaxPerPages * 4; + m_nPageCount = + static_cast((nFileSize - V2_TRAILER_SIZE) / m_nPageSize); - GUInt32 nMagic1 = GetUInt32(abyTrailer + 2, 0); - returnErrorIf(nMagic1 != 1); + m_nValueSize = abyTrailer[0]; + m_nObjectIDSize = static_cast(sizeof(uint64_t)); + m_nNonLeafPageHeaderSize = 12; + m_nLeafPageHeaderSize = 20; - nIndexDepth = GetUInt32(abyTrailer + 6, 0); - /* CPLDebug("OpenFileGDB", "nIndexDepth = %u", nIndexDepth); */ - returnErrorIf(!(nIndexDepth >= 1 && nIndexDepth <= MAX_DEPTH + 1)); + nMaxPerPages = (m_nPageSize - m_nLeafPageHeaderSize) / + (m_nObjectIDSize + m_nValueSize); + m_nOffsetFirstValInPage = + m_nLeafPageHeaderSize + nMaxPerPages * m_nObjectIDSize; - nValueCountInIdx = GetUInt32(abyTrailer + 10, 0); - /* CPLDebug("OpenFileGDB", "nValueCountInIdx = %u", nValueCountInIdx); */ - /* negative like in sample_clcV15_esri_v10.gdb/a00000005.FDO_UUID.atx */ - if ((nValueCountInIdx >> (8 * sizeof(nValueCountInIdx) - 1)) != 0) - { - CPLDebugOnly("OpenFileGDB", "nValueCountInIdx=%u", nValueCountInIdx); - return false; - } + GUInt32 nMagic1 = GetUInt32(abyTrailer + 2, 0); + returnErrorIf(nMagic1 != 1); - /* QGIS_TEST_101.gdb/a00000006.FDO_UUID.atx */ - /* or .spx file from test dataset https://github.com/OSGeo/gdal/issues/5888 - */ - if (nValueCountInIdx == 0 && nIndexDepth == 1) - { - VSIFSeekL(fpCurIdx, 4, SEEK_SET); - GByte abyBuffer[4]; - returnErrorIf(VSIFReadL(abyBuffer, 4, 1, fpCurIdx) != 1); - nValueCountInIdx = GetUInt32(abyBuffer, 0); - } - /* PreNIS.gdb/a00000006.FDO_UUID.atx has depth 2 and the value of */ - /* nValueCountInIdx is 11 which is not the number of non-null values */ - else if (nValueCountInIdx < nMaxPerPages && nIndexDepth > 1) - { - if (nValueCountInIdx > 0 && poParent->IsFileGDBV9() && - strstr(osFilename.c_str(), "blk_key_index.atx")) - { - // nValueCountInIdx not reliable in FileGDB v9 .blk_key_index.atx - // but index seems to be OK - return true; - } + nIndexDepth = GetUInt32(abyTrailer + 6, 0); + /* CPLDebug("OpenFileGDB", "nIndexDepth = %u", nIndexDepth); */ + returnErrorIf(!(nIndexDepth >= 1 && nIndexDepth <= MAX_DEPTH + 1)); - CPLDebugOnly("OpenFileGDB", - "nValueCountInIdx=%u < nMaxPerPages=%u, nIndexDepth=%u", - nValueCountInIdx, nMaxPerPages, nIndexDepth); - return false; + m_nValueCountInIdx = GetUInt64(abyTrailer + 10, 0); } return true; @@ -970,15 +1047,40 @@ int FileGDBIndex::GetMaxWidthInBytes(const FileGDBTable *poTable) const VSIFSeekL(fpCurIdx, 0, SEEK_END); vsi_l_offset nFileSize = VSIFTellL(fpCurIdx); - if (nFileSize < FGDB_PAGE_SIZE + 22) + + constexpr int V1_TRAILER_SIZE = 22; + constexpr int V2_TRAILER_SIZE = 30; + + if (nFileSize < FGDB_PAGE_SIZE_V1 + V1_TRAILER_SIZE) + { + VSIFCloseL(fpCurIdx); + return 0; + } + + GByte abyTrailer[V2_TRAILER_SIZE]; + VSIFSeekL(fpCurIdx, nFileSize - sizeof(uint32_t), SEEK_SET); + if (VSIFReadL(abyTrailer, sizeof(uint32_t), 1, fpCurIdx) != 1) + { + VSIFCloseL(fpCurIdx); + return 0; + } + const auto nVersion = GetUInt32(abyTrailer, 0); + if (nVersion != 1 && nVersion != 2) { VSIFCloseL(fpCurIdx); return 0; } - VSIFSeekL(fpCurIdx, nFileSize - 22, SEEK_SET); - GByte abyTrailer[22]; - if (VSIFReadL(abyTrailer, 22, 1, fpCurIdx) != 1) + const int nTrailerSize = nVersion == 1 ? V1_TRAILER_SIZE : V2_TRAILER_SIZE; + + if (nVersion == 2 && nFileSize < FGDB_PAGE_SIZE_V2 + V2_TRAILER_SIZE) + { + VSIFCloseL(fpCurIdx); + return 0; + } + + VSIFSeekL(fpCurIdx, nFileSize - nTrailerSize, SEEK_SET); + if (VSIFReadL(abyTrailer, nTrailerSize, 1, fpCurIdx) != 1) { VSIFCloseL(fpCurIdx); return 0; @@ -1022,8 +1124,8 @@ int FileGDBIndexIterator::SetConstraint(int nFieldIdx, FileGDBSQLOp op, if (!ReadTrailer(pszAtxName)) return FALSE; - returnErrorIf(nValueCountInIdx > - static_cast(poParent->GetValidRecordCount())); + returnErrorIf(m_nValueCountInIdx > + static_cast(poParent->GetValidRecordCount())); switch (eFieldType) { @@ -1156,7 +1258,7 @@ int FileGDBIndexIterator::SetConstraint(int nFieldIdx, FileGDBSQLOp op, break; } - if (nValueCountInIdx > 0) + if (m_nValueCountInIdx > 0) { if (nIndexDepth == 1) { @@ -1208,21 +1310,21 @@ static int FileGDBUTF16StrCompare(const GUInt16 *pasFirst, /* FindPages() */ /************************************************************************/ -bool FileGDBIndexIterator::FindPages(int iLevel, int nPage) +bool FileGDBIndexIterator::FindPages(int iLevel, uint64_t nPage) { const bool errorRetValue = false; - VSIFSeekL(fpCurIdx, static_cast(nPage - 1) * FGDB_PAGE_SIZE, + VSIFSeekL(fpCurIdx, static_cast(nPage - 1) * m_nPageSize, SEEK_SET); #ifdef DEBUG iLoadedPage[iLevel] = nPage; #endif - returnErrorIf(VSIFReadL(abyPage[iLevel], FGDB_PAGE_SIZE, 1, fpCurIdx) != 1); + returnErrorIf(VSIFReadL(abyPage[iLevel], m_nPageSize, 1, fpCurIdx) != 1); - nSubPagesCount[iLevel] = GetUInt32(abyPage[iLevel] + 4, 0); + nSubPagesCount[iLevel] = GetUInt32(abyPage[iLevel] + m_nObjectIDSize, 0); returnErrorIf(nSubPagesCount[iLevel] == 0 || nSubPagesCount[iLevel] > nMaxPerPages); if (nIndexDepth == 2) - returnErrorIf(nValueCountInIdx > + returnErrorIf(m_nValueCountInIdx > nMaxPerPages * (nSubPagesCount[0] + 1)); if (eOp == FGSO_ISNOTNULL) @@ -1250,7 +1352,7 @@ bool FileGDBIndexIterator::FindPages(int iLevel, int nPage) case FGFT_INT16: { GInt16 nVal = - GetInt16(abyPage[iLevel] + nOffsetFirstValInPage, i); + GetInt16(abyPage[iLevel] + m_nOffsetFirstValInPage, i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && nVal < nLastMax); nLastMax = nVal; @@ -1262,7 +1364,7 @@ bool FileGDBIndexIterator::FindPages(int iLevel, int nPage) case FGFT_INT32: { GInt32 nVal = - GetInt32(abyPage[iLevel] + nOffsetFirstValInPage, i); + GetInt32(abyPage[iLevel] + m_nOffsetFirstValInPage, i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && nVal < nLastMax); nLastMax = nVal; @@ -1274,7 +1376,7 @@ bool FileGDBIndexIterator::FindPages(int iLevel, int nPage) case FGFT_INT64: { int64_t nVal = - GetInt64(abyPage[iLevel] + nOffsetFirstValInPage, i); + GetInt64(abyPage[iLevel] + m_nOffsetFirstValInPage, i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && nVal < nLastMax); nLastMax = nVal; @@ -1286,7 +1388,7 @@ bool FileGDBIndexIterator::FindPages(int iLevel, int nPage) case FGFT_FLOAT32: { float fVal = - GetFloat32(abyPage[iLevel] + nOffsetFirstValInPage, i); + GetFloat32(abyPage[iLevel] + m_nOffsetFirstValInPage, i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && fVal < dfLastMax); dfLastMax = fVal; @@ -1298,7 +1400,7 @@ bool FileGDBIndexIterator::FindPages(int iLevel, int nPage) case FGFT_FLOAT64: { const double dfVal = - GetFloat64(abyPage[iLevel] + nOffsetFirstValInPage, i); + GetFloat64(abyPage[iLevel] + m_nOffsetFirstValInPage, i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && dfVal < dfLastMax); dfLastMax = dfVal; @@ -1312,7 +1414,7 @@ bool FileGDBIndexIterator::FindPages(int iLevel, int nPage) case FGFT_TIME: { const double dfVal = - GetFloat64(abyPage[iLevel] + nOffsetFirstValInPage, i); + GetFloat64(abyPage[iLevel] + m_nOffsetFirstValInPage, i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && dfVal < dfLastMax); dfLastMax = dfVal; @@ -1332,7 +1434,7 @@ bool FileGDBIndexIterator::FindPages(int iLevel, int nPage) GUInt16 asMax[MAX_CAR_COUNT_INDEXED_STR]; pasMax = asMax; memcpy(asMax, - abyPage[iLevel] + nOffsetFirstValInPage + + abyPage[iLevel] + m_nOffsetFirstValInPage + nStrLen * sizeof(GUInt16) * i, nStrLen * sizeof(GUInt16)); for (int j = 0; j < nStrLen; j++) @@ -1350,7 +1452,7 @@ bool FileGDBIndexIterator::FindPages(int iLevel, int nPage) case FGFT_GLOBALID: { const char *psNonzMaxUUID = reinterpret_cast( - abyPage[iLevel] + nOffsetFirstValInPage + + abyPage[iLevel] + m_nOffsetFirstValInPage + UUID_LEN_AS_STRING * i); #ifdef DEBUG_INDEX_CONSISTENCY returnErrorIf(i > 0 && memcmp(psNonzMaxUUID, szLastMaxUUID, @@ -1458,14 +1560,14 @@ bool FileGDBIndexIterator::FindPages(int iLevel, int nPage) void FileGDBIndexIteratorBase::Reset() { iCurPageIdx[0] = (bAscending) ? iFirstPageIdx[0] - 1 : iLastPageIdx[0] + 1; - memset(iFirstPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(int)); - memset(iLastPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(int)); - memset(iCurPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(int)); - memset(nLastPageAccessed, 0, MAX_DEPTH * sizeof(int)); + memset(iFirstPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(iFirstPageIdx[0])); + memset(iLastPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(iLastPageIdx[0])); + memset(iCurPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(iCurPageIdx[0])); + memset(nLastPageAccessed, 0, MAX_DEPTH * sizeof(nLastPageAccessed[0])); iCurFeatureInPage = 0; nFeaturesInPage = 0; - bEOF = (nValueCountInIdx == 0); + bEOF = (m_nValueCountInIdx == 0); } /************************************************************************/ @@ -1483,15 +1585,33 @@ void FileGDBIndexIterator::Reset() /* ReadPageNumber() */ /************************************************************************/ -int FileGDBIndexIteratorBase::ReadPageNumber(int iLevel) +uint64_t FileGDBIndexIteratorBase::ReadPageNumber(int iLevel) { const int errorRetValue = 0; - GUInt32 nPage = GetUInt32(abyPage[iLevel] + 8, iCurPageIdx[iLevel]); - if (nPage == nLastPageAccessed[iLevel]) + uint64_t nPage; + if (m_nVersion == 1) { - if (!LoadNextPage(iLevel)) - return 0; - nPage = GetUInt32(abyPage[iLevel] + 8, iCurPageIdx[iLevel]); + nPage = GetUInt32(abyPage[iLevel] + m_nNonLeafPageHeaderSize, + iCurPageIdx[iLevel]); + if (nPage == nLastPageAccessed[iLevel]) + { + if (!LoadNextPage(iLevel)) + return 0; + nPage = GetUInt32(abyPage[iLevel] + m_nNonLeafPageHeaderSize, + iCurPageIdx[iLevel]); + } + } + else + { + nPage = GetUInt64(abyPage[iLevel] + m_nNonLeafPageHeaderSize, + iCurPageIdx[iLevel]); + if (nPage == nLastPageAccessed[iLevel]) + { + if (!LoadNextPage(iLevel)) + return 0; + nPage = GetUInt64(abyPage[iLevel] + m_nNonLeafPageHeaderSize, + iCurPageIdx[iLevel]); + } } nLastPageAccessed[iLevel] = nPage; returnErrorIf(nPage < 2); @@ -1502,16 +1622,16 @@ int FileGDBIndexIteratorBase::ReadPageNumber(int iLevel) /* LoadNextPage() */ /************************************************************************/ -int FileGDBIndexIteratorBase::LoadNextPage(int iLevel) +bool FileGDBIndexIteratorBase::LoadNextPage(int iLevel) { - const int errorRetValue = FALSE; + const bool errorRetValue = false; if ((bAscending && iCurPageIdx[iLevel] == iLastPageIdx[iLevel]) || (!bAscending && iCurPageIdx[iLevel] == iFirstPageIdx[iLevel])) { if (iLevel == 0 || !LoadNextPage(iLevel - 1)) - return FALSE; + return false; - GUInt32 nPage = ReadPageNumber(iLevel - 1); + const auto nPage = ReadPageNumber(iLevel - 1); returnErrorIf(!FindPages(iLevel, nPage)); iCurPageIdx[iLevel] = @@ -1525,23 +1645,23 @@ int FileGDBIndexIteratorBase::LoadNextPage(int iLevel) iCurPageIdx[iLevel]--; } - return TRUE; + return true; } /************************************************************************/ /* LoadNextFeaturePage() */ /************************************************************************/ -int FileGDBIndexIteratorBase::LoadNextFeaturePage() +bool FileGDBIndexIteratorBase::LoadNextFeaturePage() { - const int errorRetValue = FALSE; - GUInt32 nPage; + const bool errorRetValue = false; + GUInt64 nPage; if (nIndexDepth == 1) { if (iCurPageIdx[0] == iLastPageIdx[0]) { - return FALSE; + return false; } if (bAscending) iCurPageIdx[0]++; @@ -1553,7 +1673,7 @@ int FileGDBIndexIteratorBase::LoadNextFeaturePage() { if (!LoadNextPage(nIndexDepth - 2)) { - return FALSE; + return false; } nPage = ReadPageNumber(nIndexDepth - 2); returnErrorIf(nPage < 2); @@ -1563,7 +1683,7 @@ int FileGDBIndexIteratorBase::LoadNextFeaturePage() m_oCacheFeaturePage.getPtr(nPage); if (cachedPagePtr) { - memcpy(abyPageFeature, cachedPagePtr->data(), FGDB_PAGE_SIZE); + memcpy(abyPageFeature, cachedPagePtr->data(), m_nPageSize); } else { @@ -1574,37 +1694,32 @@ int FileGDBIndexIteratorBase::LoadNextFeaturePage() cachedPage.clear(); } - VSIFSeekL(fpCurIdx, - static_cast(nPage - 1) * FGDB_PAGE_SIZE, + VSIFSeekL(fpCurIdx, static_cast(nPage - 1) * m_nPageSize, SEEK_SET); #ifdef DEBUG iLoadedPage[nIndexDepth - 1] = nPage; #endif - returnErrorIf(VSIFReadL(abyPageFeature, FGDB_PAGE_SIZE, 1, fpCurIdx) != - 1); + returnErrorIf(VSIFReadL(abyPageFeature, m_nPageSize, 1, fpCurIdx) != 1); cachedPage.insert(cachedPage.end(), abyPageFeature, - abyPageFeature + FGDB_PAGE_SIZE); + abyPageFeature + m_nPageSize); m_oCacheFeaturePage.insert(nPage, std::move(cachedPage)); } - GUInt32 nFeatures = GetUInt32(abyPageFeature + 4, 0); + const GUInt32 nFeatures = GetUInt32(abyPageFeature + m_nObjectIDSize, 0); returnErrorIf(nFeatures > nMaxPerPages); nFeaturesInPage = static_cast(nFeatures); iCurFeatureInPage = (bAscending) ? 0 : nFeaturesInPage - 1; - if (nFeatures == 0) - return FALSE; - - return TRUE; + return nFeatures != 0; } /************************************************************************/ /* GetNextRow() */ /************************************************************************/ -int FileGDBIndexIterator::GetNextRow() +int64_t FileGDBIndexIterator::GetNextRow() { - const int errorRetValue = -1; + const int64_t errorRetValue = -1; if (bEOF) return -1; @@ -1632,7 +1747,7 @@ int FileGDBIndexIterator::GetNextRow() case FGFT_INT16: { const GInt16 nVal = - GetInt16(abyPageFeature + nOffsetFirstValInPage, + GetInt16(abyPageFeature + m_nOffsetFirstValInPage, iCurFeatureInPage); nComp = COMPARE(sValue.Integer, nVal); break; @@ -1641,7 +1756,7 @@ int FileGDBIndexIterator::GetNextRow() case FGFT_INT32: { const GInt32 nVal = - GetInt32(abyPageFeature + nOffsetFirstValInPage, + GetInt32(abyPageFeature + m_nOffsetFirstValInPage, iCurFeatureInPage); nComp = COMPARE(sValue.Integer, nVal); break; @@ -1650,7 +1765,7 @@ int FileGDBIndexIterator::GetNextRow() case FGFT_FLOAT32: { const float fVal = - GetFloat32(abyPageFeature + nOffsetFirstValInPage, + GetFloat32(abyPageFeature + m_nOffsetFirstValInPage, iCurFeatureInPage); nComp = COMPARE(sValue.Real, fVal); break; @@ -1659,7 +1774,7 @@ int FileGDBIndexIterator::GetNextRow() case FGFT_FLOAT64: { const double dfVal = - GetFloat64(abyPageFeature + nOffsetFirstValInPage, + GetFloat64(abyPageFeature + m_nOffsetFirstValInPage, iCurFeatureInPage); nComp = COMPARE(sValue.Real, dfVal); break; @@ -1671,7 +1786,7 @@ int FileGDBIndexIterator::GetNextRow() case FGFT_DATETIME_WITH_OFFSET: { const double dfVal = - GetFloat64(abyPageFeature + nOffsetFirstValInPage, + GetFloat64(abyPageFeature + m_nOffsetFirstValInPage, iCurFeatureInPage); if (sValue.Real + 1e-10 < dfVal) nComp = -1; @@ -1686,7 +1801,7 @@ int FileGDBIndexIterator::GetNextRow() { GUInt16 asVal[MAX_CAR_COUNT_INDEXED_STR]; memcpy(asVal, - abyPageFeature + nOffsetFirstValInPage + + abyPageFeature + m_nOffsetFirstValInPage + nStrLen * 2 * iCurFeatureInPage, nStrLen * 2); for (int j = 0; j < nStrLen; j++) @@ -1699,7 +1814,7 @@ int FileGDBIndexIterator::GetNextRow() case FGFT_GLOBALID: { nComp = memcmp(szUUID, - abyPageFeature + nOffsetFirstValInPage + + abyPageFeature + m_nOffsetFirstValInPage + UUID_LEN_AS_STRING * iCurFeatureInPage, UUID_LEN_AS_STRING); break; @@ -1708,7 +1823,7 @@ int FileGDBIndexIterator::GetNextRow() case FGFT_INT64: { const int64_t nVal = - GetInt64(abyPageFeature + nOffsetFirstValInPage, + GetInt64(abyPageFeature + m_nOffsetFirstValInPage, iCurFeatureInPage); nComp = COMPARE(sValue.Integer64, nVal); break; @@ -1767,17 +1882,21 @@ int FileGDBIndexIterator::GetNextRow() if (bMatch) { - const GUInt32 nFID = - GetUInt32(abyPageFeature + 12, iCurFeatureInPage); + const GUInt64 nFID = + m_nVersion == 1 + ? GetUInt32(abyPageFeature + m_nLeafPageHeaderSize, + iCurFeatureInPage) + : GetUInt64(abyPageFeature + m_nLeafPageHeaderSize, + iCurFeatureInPage); if (bAscending) iCurFeatureInPage++; else iCurFeatureInPage--; returnErrorAndCleanupIf( - nFID < 1 || nFID > static_cast( + nFID < 1 || nFID > static_cast( poParent->GetTotalRecordCount()), bEOF = true); - return static_cast(nFID - 1); + return static_cast(nFID - 1); } else { @@ -1801,14 +1920,15 @@ int FileGDBIndexIterator::SortRows() Reset(); while (true) { - int nRow = GetNextRow(); + int64_t nRow = GetNextRow(); if (nRow < 0) break; if (nSortedCount == nSortedAlloc) { int nNewSortedAlloc = 4 * nSortedAlloc / 3 + 16; - int *panNewSortedRows = static_cast(VSI_REALLOC_VERBOSE( - panSortedRows, sizeof(int) * nNewSortedAlloc)); + int64_t *panNewSortedRows = + static_cast(VSI_REALLOC_VERBOSE( + panSortedRows, sizeof(int64_t) * nNewSortedAlloc)); if (panNewSortedRows == nullptr) { nSortedCount = 0; @@ -1822,8 +1942,8 @@ int FileGDBIndexIterator::SortRows() if (nSortedCount == 0) return FALSE; std::sort(panSortedRows, panSortedRows + nSortedCount); -#ifdef nValueCountInIdx_reliable - if (eOp == FGSO_ISNOTNULL && (int)nValueCountInIdx != nSortedCount) +#ifdef m_nValueCountInIdx_reliable + if (eOp == FGSO_ISNOTNULL && (int64_t)m_nValueCountInIdx != nSortedCount) PrintError(); #endif return TRUE; @@ -1833,7 +1953,7 @@ int FileGDBIndexIterator::SortRows() /* GetNextRowSortedByFID() */ /************************************************************************/ -int FileGDBIndexIterator::GetNextRowSortedByFID() +int64_t FileGDBIndexIterator::GetNextRowSortedByFID() { if (eOp == FGSO_EQ) return GetNextRow(); @@ -1857,21 +1977,21 @@ int FileGDBIndexIterator::GetNextRowSortedByFID() /* GetRowCount() */ /************************************************************************/ -int FileGDBIndexIterator::GetRowCount() +int64_t FileGDBIndexIterator::GetRowCount() { - // The nValueCountInIdx value has been found to be unreliable when the index + // The m_nValueCountInIdx value has been found to be unreliable when the index // is built as features are inserted (and when they are not in increasing // order) (with FileGDB SDK 1.3) So disable this optimization as there's no // fast way to know if the value is reliable or not. -#ifdef nValueCountInIdx_reliable +#ifdef m_nValueCountInIdx_reliable if (eOp == FGSO_ISNOTNULL) - return (int)nValueCountInIdx; + return (int64_t)m_nValueCountInIdx; #endif if (nSortedCount >= 0) return nSortedCount; - int nRowCount = 0; + int64_t nRowCount = 0; bool bSaveAscending = bAscending; bAscending = true; /* for a tiny bit of more efficiency */ Reset(); @@ -1891,38 +2011,52 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, { const OGRField *errorRetValue = nullptr; eOutType = -1; - if (nValueCountInIdx == 0) + if (m_nValueCountInIdx == 0) return nullptr; - GByte l_abyPage[FGDB_PAGE_SIZE]; - GUInt32 nPage = 1; + std::vector l_abyPageV; + try + { + l_abyPageV.resize(m_nPageSize); + } + catch (const std::exception &) + { + return nullptr; + } + GByte *l_abyPage = l_abyPageV.data(); + uint64_t nPage = 1; for (GUInt32 iLevel = 0; iLevel < nIndexDepth - 1; iLevel++) { - VSIFSeekL(fpCurIdx, - static_cast(nPage - 1) * FGDB_PAGE_SIZE, + VSIFSeekL(fpCurIdx, static_cast(nPage - 1) * m_nPageSize, SEEK_SET); #ifdef DEBUG iLoadedPage[iLevel] = nPage; #endif - returnErrorIf(VSIFReadL(l_abyPage, FGDB_PAGE_SIZE, 1, fpCurIdx) != 1); - GUInt32 l_nSubPagesCount = GetUInt32(l_abyPage + 4, 0); + returnErrorIf(VSIFReadL(l_abyPage, m_nPageSize, 1, fpCurIdx) != 1); + GUInt32 l_nSubPagesCount = GetUInt32(l_abyPage + m_nObjectIDSize, 0); returnErrorIf(l_nSubPagesCount == 0 || l_nSubPagesCount > nMaxPerPages); - if (bIsMin) - nPage = GetUInt32(l_abyPage + 8, 0); + if (m_nVersion == 1) + { + nPage = GetUInt32(l_abyPage + m_nNonLeafPageHeaderSize, + bIsMin ? 0 : l_nSubPagesCount); + } else - nPage = GetUInt32(l_abyPage + 8, l_nSubPagesCount); + { + nPage = GetUInt64(l_abyPage + m_nNonLeafPageHeaderSize, + bIsMin ? 0 : l_nSubPagesCount); + } returnErrorIf(nPage < 2); } - VSIFSeekL(fpCurIdx, static_cast(nPage - 1) * FGDB_PAGE_SIZE, + VSIFSeekL(fpCurIdx, static_cast(nPage - 1) * m_nPageSize, SEEK_SET); #ifdef DEBUG iLoadedPage[nIndexDepth - 1] = nPage; #endif - returnErrorIf(VSIFReadL(l_abyPage, FGDB_PAGE_SIZE, 1, fpCurIdx) != 1); + returnErrorIf(VSIFReadL(l_abyPage, m_nPageSize, 1, fpCurIdx) != 1); - GUInt32 nFeatures = GetUInt32(l_abyPage + 4, 0); + GUInt32 nFeatures = GetUInt32(l_abyPage + m_nObjectIDSize, 0); returnErrorIf(nFeatures < 1 || nFeatures > nMaxPerPages); int iFeature = (bIsMin) ? 0 : nFeatures - 1; @@ -1932,7 +2066,7 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, case FGFT_INT16: { const GInt16 nVal = - GetInt16(l_abyPage + nOffsetFirstValInPage, iFeature); + GetInt16(l_abyPage + m_nOffsetFirstValInPage, iFeature); psField->Integer = nVal; eOutType = OFTInteger; return psField; @@ -1941,7 +2075,7 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, case FGFT_INT32: { const GInt32 nVal = - GetInt32(l_abyPage + nOffsetFirstValInPage, iFeature); + GetInt32(l_abyPage + m_nOffsetFirstValInPage, iFeature); psField->Integer = nVal; eOutType = OFTInteger; return psField; @@ -1950,7 +2084,7 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, case FGFT_FLOAT32: { const float fVal = - GetFloat32(l_abyPage + nOffsetFirstValInPage, iFeature); + GetFloat32(l_abyPage + m_nOffsetFirstValInPage, iFeature); psField->Real = fVal; eOutType = OFTReal; return psField; @@ -1959,7 +2093,7 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, case FGFT_FLOAT64: { const double dfVal = - GetFloat64(l_abyPage + nOffsetFirstValInPage, iFeature); + GetFloat64(l_abyPage + m_nOffsetFirstValInPage, iFeature); psField->Real = dfVal; eOutType = OFTReal; return psField; @@ -1969,7 +2103,7 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, case FGFT_DATETIME_WITH_OFFSET: { const double dfVal = - GetFloat64(l_abyPage + nOffsetFirstValInPage, iFeature); + GetFloat64(l_abyPage + m_nOffsetFirstValInPage, iFeature); FileGDBDoubleDateToOGRDate(dfVal, false, psField); eOutType = OFTDateTime; return psField; @@ -1978,7 +2112,7 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, case FGFT_DATE: { const double dfVal = - GetFloat64(l_abyPage + nOffsetFirstValInPage, iFeature); + GetFloat64(l_abyPage + m_nOffsetFirstValInPage, iFeature); FileGDBDoubleDateToOGRDate(dfVal, false, psField); eOutType = OFTDate; return psField; @@ -1987,7 +2121,7 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, case FGFT_TIME: { const double dfVal = - GetFloat64(l_abyPage + nOffsetFirstValInPage, iFeature); + GetFloat64(l_abyPage + m_nOffsetFirstValInPage, iFeature); FileGDBDoubleTimeToOGRTime(dfVal, psField); eOutType = OFTTime; return psField; @@ -1999,7 +2133,7 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, for (int j = 0; j < nStrLen; j++) { GUInt16 nCh = - GetUInt16(l_abyPage + nOffsetFirstValInPage + + GetUInt16(l_abyPage + m_nOffsetFirstValInPage + nStrLen * sizeof(GUInt16) * iFeature, j); awsVal[j] = nCh; @@ -2021,7 +2155,7 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, case FGFT_GLOBALID: { memcpy(psField->String, - l_abyPage + nOffsetFirstValInPage + + l_abyPage + m_nOffsetFirstValInPage + UUID_LEN_AS_STRING * iFeature, UUID_LEN_AS_STRING); psField->String[UUID_LEN_AS_STRING] = 0; @@ -2032,7 +2166,7 @@ const OGRField *FileGDBIndexIterator::GetMinMaxValue(OGRField *psField, case FGFT_INT64: { const int64_t nVal = - GetInt64(l_abyPage + nOffsetFirstValInPage, iFeature); + GetInt64(l_abyPage + m_nOffsetFirstValInPage, iFeature); psField->Integer64 = nVal; eOutType = OFTInteger64; return psField; @@ -2140,7 +2274,7 @@ void FileGDBIndexIterator::GetMinMaxSumCount(double &dfMin, double &dfMax, } } - dfVal = Getter::GetAsDouble(abyPageFeature + nOffsetFirstValInPage, + dfVal = Getter::GetAsDouble(abyPageFeature + m_nOffsetFirstValInPage, iCurFeatureInPage); dfLocalSum += dfVal; @@ -2155,10 +2289,10 @@ void FileGDBIndexIterator::GetMinMaxSumCount(double &dfMin, double &dfMax, dfMax = dfVal; } -int FileGDBIndexIterator::GetMinMaxSumCount(double &dfMin, double &dfMax, - double &dfSum, int &nCount) +bool FileGDBIndexIterator::GetMinMaxSumCount(double &dfMin, double &dfMax, + double &dfSum, int &nCount) { - const int errorRetValue = FALSE; + const bool errorRetValue = false; dfMin = 0.0; dfMax = 0.0; dfSum = 0.0; @@ -2213,7 +2347,7 @@ int FileGDBIndexIterator::GetMinMaxSumCount(double &dfMin, double &dfMax, bAscending = bSaveAscending; Reset(); - return TRUE; + return true; } /************************************************************************/ @@ -2225,7 +2359,7 @@ class FileGDBSpatialIndexIteratorImpl final : public FileGDBIndexIteratorBase, { OGREnvelope m_sFilterEnvelope; bool m_bHasBuiltSetFID = false; - std::vector m_oFIDVector{}; + std::vector m_oFIDVector{}; size_t m_nVectorIdx = 0; int m_nGridNo = 0; GInt64 m_nMinVal = 0; @@ -2233,7 +2367,7 @@ class FileGDBSpatialIndexIteratorImpl final : public FileGDBIndexIteratorBase, GInt32 m_nCurX = 0; GInt32 m_nMaxX = 0; - virtual bool FindPages(int iLevel, int nPage) override; + virtual bool FindPages(int iLevel, uint64_t nPage) override; int GetNextRow(); bool ReadNewXRange(); bool ResetInternal(); @@ -2252,7 +2386,7 @@ class FileGDBSpatialIndexIteratorImpl final : public FileGDBIndexIteratorBase, return poParent; } // avoid MSVC C4250 inherits via dominance warning - virtual int GetNextRowSortedByFID() override; + virtual int64_t GetNextRowSortedByFID() override; virtual void Reset() override; virtual bool SetEnvelope(const OGREnvelope &sFilterEnvelope) override; @@ -2426,7 +2560,7 @@ bool FileGDBSpatialIndexIteratorImpl::ReadNewXRange() } const bool errorRetValue = false; - if (nValueCountInIdx > 0) + if (m_nValueCountInIdx > 0) { if (nIndexDepth == 1) { @@ -2500,7 +2634,7 @@ static bool FindMinMaxIdx(const GByte *pBaseAddr, const int nVals, /* FindPages() */ /************************************************************************/ -bool FileGDBSpatialIndexIteratorImpl::FindPages(int iLevel, int nPage) +bool FileGDBSpatialIndexIteratorImpl::FindPages(int iLevel, uint64_t nPage) { const bool errorRetValue = false; @@ -2510,7 +2644,7 @@ bool FileGDBSpatialIndexIteratorImpl::FindPages(int iLevel, int nPage) m_oCachePage[iLevel].getPtr(nPage); if (cachedPagePtr) { - memcpy(abyPage[iLevel], cachedPagePtr->data(), FGDB_PAGE_SIZE); + memcpy(abyPage[iLevel], cachedPagePtr->data(), m_nPageSize); } else { @@ -2521,35 +2655,46 @@ bool FileGDBSpatialIndexIteratorImpl::FindPages(int iLevel, int nPage) cachedPage.clear(); } - VSIFSeekL(fpCurIdx, - static_cast(nPage - 1) * FGDB_PAGE_SIZE, + VSIFSeekL(fpCurIdx, static_cast(nPage - 1) * m_nPageSize, SEEK_SET); #ifdef DEBUG iLoadedPage[iLevel] = nPage; #endif - returnErrorIf(VSIFReadL(abyPage[iLevel], FGDB_PAGE_SIZE, 1, fpCurIdx) != + returnErrorIf(VSIFReadL(abyPage[iLevel], m_nPageSize, 1, fpCurIdx) != 1); cachedPage.insert(cachedPage.end(), abyPage[iLevel], - abyPage[iLevel] + FGDB_PAGE_SIZE); + abyPage[iLevel] + m_nPageSize); m_oCachePage[iLevel].insert(nPage, std::move(cachedPage)); } - nSubPagesCount[iLevel] = GetUInt32(abyPage[iLevel] + 4, 0); + nSubPagesCount[iLevel] = GetUInt32(abyPage[iLevel] + m_nObjectIDSize, 0); returnErrorIf(nSubPagesCount[iLevel] == 0 || nSubPagesCount[iLevel] > nMaxPerPages); - if (GetInt64(abyPage[iLevel] + nOffsetFirstValInPage, 0) > m_nMaxVal) + if (GetInt64(abyPage[iLevel] + m_nOffsetFirstValInPage, 0) > m_nMaxVal) { iFirstPageIdx[iLevel] = 0; - // nSubPagesCount[iLevel] == 1 && GetUInt32(abyPage[iLevel] + 12, 0) == + // nSubPagesCount[iLevel] == 1 && GetUInt32(abyPage[iLevel] + m_nLeafPageHeaderSize, 0) == // 0 should only happen on non-nominal cases where one forces the depth // of the index to be greater than needed. - iLastPageIdx[iLevel] = (nSubPagesCount[iLevel] == 1 && - GetUInt32(abyPage[iLevel] + 12, 0) == 0) - ? 0 - : 1; + if (m_nVersion == 1) + { + iLastPageIdx[iLevel] = + (nSubPagesCount[iLevel] == 1 && + GetUInt32(abyPage[iLevel] + m_nLeafPageHeaderSize, 0) == 0) + ? 0 + : 1; + } + else + { + iLastPageIdx[iLevel] = + (nSubPagesCount[iLevel] == 1 && + GetUInt64(abyPage[iLevel] + m_nLeafPageHeaderSize, 0) == 0) + ? 0 + : 1; + } } - else if (!FindMinMaxIdx(abyPage[iLevel] + nOffsetFirstValInPage, + else if (!FindMinMaxIdx(abyPage[iLevel] + m_nOffsetFirstValInPage, static_cast(nSubPagesCount[iLevel]), m_nMinVal, m_nMaxVal, iFirstPageIdx[iLevel], iLastPageIdx[iLevel])) @@ -2582,7 +2727,7 @@ int FileGDBSpatialIndexIteratorImpl::GetNextRow() int nMinIdx = 0; int nMaxIdx = 0; if (!LoadNextFeaturePage() || - !FindMinMaxIdx(abyPageFeature + nOffsetFirstValInPage, + !FindMinMaxIdx(abyPageFeature + m_nOffsetFirstValInPage, nFeaturesInPage, m_nMinVal, m_nMaxVal, nMinIdx, nMaxIdx) || nMinIdx > nMaxIdx) @@ -2623,17 +2768,21 @@ int FileGDBSpatialIndexIteratorImpl::GetNextRow() } #ifdef DEBUG - const GInt64 nVal = - GetInt64(abyPageFeature + nOffsetFirstValInPage, iCurFeatureInPage); + const GInt64 nVal = GetInt64(abyPageFeature + m_nOffsetFirstValInPage, + iCurFeatureInPage); CPL_IGNORE_RET_VAL(nVal); CPLAssert(nVal >= m_nMinVal && nVal <= m_nMaxVal); #endif - const GUInt32 nFID = GetUInt32(abyPageFeature + 12, iCurFeatureInPage); + const GUInt64 nFID = + m_nVersion == 1 ? GetUInt32(abyPageFeature + m_nLeafPageHeaderSize, + iCurFeatureInPage) + : GetUInt64(abyPageFeature + m_nLeafPageHeaderSize, + iCurFeatureInPage); iCurFeatureInPage++; returnErrorAndCleanupIf( nFID < 1 || - nFID > static_cast(poParent->GetTotalRecordCount()), + nFID > static_cast(poParent->GetTotalRecordCount()), bEOF = true); return static_cast(nFID - 1); } @@ -2673,7 +2822,7 @@ void FileGDBSpatialIndexIteratorImpl::Reset() /* GetNextRowSortedByFID() */ /************************************************************************/ -int FileGDBSpatialIndexIteratorImpl::GetNextRowSortedByFID() +int64_t FileGDBSpatialIndexIteratorImpl::GetNextRowSortedByFID() { if (m_nVectorIdx == 0) { @@ -2684,7 +2833,7 @@ int FileGDBSpatialIndexIteratorImpl::GetNextRowSortedByFID() // than using a unordered_set (or set) while (true) { - const int nFID = GetNextRow(); + const auto nFID = GetNextRow(); if (nFID < 0) break; m_oFIDVector.push_back(nFID); @@ -2694,16 +2843,16 @@ int FileGDBSpatialIndexIteratorImpl::GetNextRowSortedByFID() if (m_oFIDVector.empty()) return -1; - const int nFID = m_oFIDVector[m_nVectorIdx]; + const auto nFID = m_oFIDVector[m_nVectorIdx]; ++m_nVectorIdx; return nFID; } - const int nLastFID = m_oFIDVector[m_nVectorIdx - 1]; + const auto nLastFID = m_oFIDVector[m_nVectorIdx - 1]; while (m_nVectorIdx < m_oFIDVector.size()) { // Do not return consecutive identical FID - const int nFID = m_oFIDVector[m_nVectorIdx]; + const auto nFID = m_oFIDVector[m_nVectorIdx]; ++m_nVectorIdx; if (nFID == nLastFID) { diff --git a/ogr/ogrsf_frmts/openfilegdb/filegdbindex_write.cpp b/ogr/ogrsf_frmts/openfilegdb/filegdbindex_write.cpp index fb477682c525..b2561ed88b1d 100644 --- a/ogr/ogrsf_frmts/openfilegdb/filegdbindex_write.cpp +++ b/ogr/ogrsf_frmts/openfilegdb/filegdbindex_write.cpp @@ -305,7 +305,7 @@ void FileGDBTable::ComputeOptimalSpatialIndexGridResolution() { // For point, use the density as the grid resolution int nValid = 0; - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -349,7 +349,7 @@ void FileGDBTable::ComputeOptimalSpatialIndexGridResolution() int64_t nValid = 0; auto poGeomConverter = std::unique_ptr( FileGDBOGRGeometryConverter::BuildConverter(poGeomField)); - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -403,7 +403,7 @@ void FileGDBTable::ComputeOptimalSpatialIndexGridResolution() // of all geometries double dfMaxSize = 0; OGREnvelope sEnvelope; - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -847,7 +847,7 @@ bool FileGDBTable::CreateSpatialIndex() } auto poGeomConverter = std::unique_ptr( FileGDBOGRGeometryConverter::BuildConverter(poGeomField)); - typedef std::pair ValueOIDPair; + typedef std::pair ValueOIDPair; std::vector asValues; const double dfGridStep = m_adfSpatialIndexGridResolution.back(); @@ -1159,11 +1159,11 @@ bool FileGDBTable::CreateSpatialIndex() }; std::vector aSetValues; - int iLastReported = 0; + int64_t iLastReported = 0; const auto nReportIncrement = m_nTotalRecordCount / 20; try { - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) { if (m_nTotalRecordCount > 10000 && (iCurFeat + 1 == m_nTotalRecordCount || @@ -1343,9 +1343,10 @@ bool FileGDBTable::CreateAttributeIndex(const FileGDBIndex *poIndex) const auto eFieldType = m_apoFields[iField]->GetType(); if (eFieldType == FGFT_INT16) { - typedef std::pair ValueOIDPair; + typedef std::pair ValueOIDPair; std::vector asValues; - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; + ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -1368,9 +1369,10 @@ bool FileGDBTable::CreateAttributeIndex(const FileGDBIndex *poIndex) } else if (eFieldType == FGFT_INT32) { - typedef std::pair ValueOIDPair; + typedef std::pair ValueOIDPair; std::vector asValues; - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; + ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -1393,9 +1395,10 @@ bool FileGDBTable::CreateAttributeIndex(const FileGDBIndex *poIndex) } else if (eFieldType == FGFT_INT64) { - typedef std::pair ValueOIDPair; + typedef std::pair ValueOIDPair; std::vector asValues; - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; + ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -1418,9 +1421,10 @@ bool FileGDBTable::CreateAttributeIndex(const FileGDBIndex *poIndex) } else if (eFieldType == FGFT_FLOAT32) { - typedef std::pair ValueOIDPair; + typedef std::pair ValueOIDPair; std::vector asValues; - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; + ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -1444,11 +1448,12 @@ bool FileGDBTable::CreateAttributeIndex(const FileGDBIndex *poIndex) eFieldType == FGFT_DATE || eFieldType == FGFT_TIME || eFieldType == FGFT_DATETIME_WITH_OFFSET) { - typedef std::pair ValueOIDPair; + typedef std::pair ValueOIDPair; std::vector asValues; // Hack to force reading DateTime as double m_apoFields[iField]->m_bReadAsDouble = true; - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; + ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -1471,13 +1476,14 @@ bool FileGDBTable::CreateAttributeIndex(const FileGDBIndex *poIndex) } else if (eFieldType == FGFT_STRING) { - typedef std::pair, int> ValueOIDPair; + typedef std::pair, int64_t> ValueOIDPair; std::vector asValues; bRet = true; const bool bIsLower = STARTS_WITH_CI(poIndex->GetExpression().c_str(), "LOWER("); int maxStrSize = 0; - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; + ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) diff --git a/ogr/ogrsf_frmts/openfilegdb/filegdbtable.cpp b/ogr/ogrsf_frmts/openfilegdb/filegdbtable.cpp index a4a7a235891c..89f07f9cb451 100644 --- a/ogr/ogrsf_frmts/openfilegdb/filegdbtable.cpp +++ b/ogr/ogrsf_frmts/openfilegdb/filegdbtable.cpp @@ -30,6 +30,7 @@ #include "filegdbtable.h" #include +#include #include #include #include @@ -569,45 +570,55 @@ bool FileGDBTable::GuessFeatureLocations() } } - int nInvalidRecords = 0; - while (nOffset < m_nFileSize) + int64_t nInvalidRecords = 0; + try { - GUInt32 nSize; - int bDeletedRecord; - if (!IsLikelyFeatureAtOffset(nOffset, &nSize, &bDeletedRecord)) - { - nOffset++; - } - else + while (nOffset < m_nFileSize) { - /*CPLDebug("OpenFileGDB", "Feature found at offset %d (size = %d)", - nOffset, nSize);*/ - if (bDeletedRecord) + GUInt32 nSize; + int bDeletedRecord; + if (!IsLikelyFeatureAtOffset(nOffset, &nSize, &bDeletedRecord)) + { + nOffset++; + } + else { - if (bReportDeletedFeatures) + /*CPLDebug("OpenFileGDB", "Feature found at offset %d (size = %d)", + nOffset, nSize);*/ + if (bDeletedRecord) { - m_bHasDeletedFeaturesListed = TRUE; - m_anFeatureOffsets.push_back(MARK_DELETED(nOffset)); + if (bReportDeletedFeatures) + { + m_bHasDeletedFeaturesListed = TRUE; + m_anFeatureOffsets.push_back(MARK_DELETED(nOffset)); + } + else + { + nInvalidRecords++; + m_anFeatureOffsets.push_back(0); + } } else - { - nInvalidRecords++; - m_anFeatureOffsets.push_back(0); - } + m_anFeatureOffsets.push_back(nOffset); + nOffset += nSize; } - else - m_anFeatureOffsets.push_back(nOffset); - nOffset += nSize; } } - m_nTotalRecordCount = static_cast(m_anFeatureOffsets.size()); + catch (const std::bad_alloc &) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out of memory in FileGDBTable::GuessFeatureLocations()"); + return false; + } + m_nTotalRecordCount = static_cast(m_anFeatureOffsets.size()); if (m_nTotalRecordCount - nInvalidRecords > m_nValidRecordCount) { if (!m_bHasDeletedFeaturesListed) { CPLError(CE_Warning, CPLE_AppDefined, - "More features found (%d) than declared number of valid " - "features (%d). " + "More features found (%" PRId64 + ") than declared number of valid " + "features ((%" PRId64 "). " "So deleted features will likely be reported.", m_nTotalRecordCount - nInvalidRecords, m_nValidRecordCount); @@ -619,16 +630,26 @@ bool FileGDBTable::GuessFeatureLocations() } /************************************************************************/ -/* ReadTableXHeader() */ +/* ReadTableXHeaderV3() */ /************************************************************************/ -int FileGDBTable::ReadTableXHeader() +bool FileGDBTable::ReadTableXHeaderV3() { - const int errorRetValue = FALSE; + const bool errorRetValue = false; GByte abyHeader[16]; // Read .gdbtablx file header returnErrorIf(VSIFReadL(abyHeader, 16, 1, m_fpTableX) != 1); + + const int nGDBTablxVersion = GetUInt32(abyHeader, 0); + if (nGDBTablxVersion != static_cast(m_eGDBTableVersion)) + { + CPLError(CE_Failure, CPLE_AppDefined, + ".gdbtablx version is %d whereas it should be %d", + nGDBTablxVersion, static_cast(m_eGDBTableVersion)); + return false; + } + m_n1024BlocksPresent = GetUInt32(abyHeader + 4, 0); m_nTotalRecordCount = GetInt32(abyHeader + 8, 0); @@ -697,7 +718,100 @@ int FileGDBTable::ReadTableXHeader() returnErrorIf(nCountBlocks != m_n1024BlocksPresent); } } - return TRUE; + return true; +} + +/************************************************************************/ +/* ReadTableXHeaderV4() */ +/************************************************************************/ + +bool FileGDBTable::ReadTableXHeaderV4() +{ + const bool errorRetValue = false; + GByte abyHeader[16]; + + // Read .gdbtablx file header + returnErrorIf(VSIFReadL(abyHeader, 16, 1, m_fpTableX) != 1); + + const int nGDBTablxVersion = GetUInt32(abyHeader, 0); + if (nGDBTablxVersion != static_cast(m_eGDBTableVersion)) + { + CPLError(CE_Failure, CPLE_AppDefined, + ".gdbtablx version is %d whereas it should be %d", + nGDBTablxVersion, static_cast(m_eGDBTableVersion)); + return false; + } + + m_n1024BlocksPresent = GetUInt64(abyHeader + 4, 0); + + m_nTablxOffsetSize = GetUInt32(abyHeader + 12, 0); + returnErrorIf(m_nTablxOffsetSize < 4 || m_nTablxOffsetSize > 6); + + m_nOffsetTableXTrailer = + 16 + m_nTablxOffsetSize * 1024 * + static_cast(m_n1024BlocksPresent); + if (m_n1024BlocksPresent != 0) + { + GByte abyTrailer[12]; + + VSIFSeekL(m_fpTableX, m_nOffsetTableXTrailer, SEEK_SET); + returnErrorIf(VSIFReadL(abyTrailer, 12, 1, m_fpTableX) != 1); + + m_nTotalRecordCount = GetUInt64(abyTrailer, 0); + + // Cf https://github.com/rouault/dump_gdbtable/wiki/FGDB-Spec#trailing-section-16-bytes--variable-number- + // for all below magic numbers and byte sequences + GUInt32 nSizeBitmapSection = GetUInt32(abyTrailer + 8, 0); + if (nSizeBitmapSection == 0) + { + // no bitmap. Fine + } + else if (nSizeBitmapSection == 22 + 32768 + 52 && + m_nTotalRecordCount <= 32768 * 1024 * 8) + { + try + { + std::vector abyBitmapSection(nSizeBitmapSection); + returnErrorIf(VSIFReadL(abyBitmapSection.data(), + abyBitmapSection.size(), 1, + m_fpTableX) != 1); + if (memcmp(abyBitmapSection.data(), "\x01\x00\x01\x00\x00\x00", + 6) == 0 && + memcmp(abyBitmapSection.data() + 22 + 32768, + "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + 12) == 0) + { + m_abyTablXBlockMap.insert( + m_abyTablXBlockMap.end(), abyBitmapSection.data() + 22, + abyBitmapSection.data() + 22 + 32768); + } + else + { + m_bReliableObjectID = false; + } + } + catch (const std::exception &e) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate m_abyTablXBlockMap: %s", e.what()); + return false; + } + } + else + { + m_bReliableObjectID = false; + } + if (!m_bReliableObjectID) + { + m_nTotalRecordCount = 1024 * m_n1024BlocksPresent; + CPLError(CE_Warning, CPLE_AppDefined, + "Due to partial reverse engineering of the format, " + "ObjectIDs will not be accurate and attribute and spatial " + "indices cannot be used on %s", + m_osFilenameWithLayerName.c_str()); + } + } + return true; } /************************************************************************/ @@ -713,7 +827,7 @@ bool FileGDBTable::Open(const char *pszFilename, bool bUpdate, m_bUpdate = bUpdate; m_osFilename = pszFilename; - CPLString m_osFilenameWithLayerName(m_osFilename); + m_osFilenameWithLayerName = m_osFilename; if (pszLayerName) m_osFilenameWithLayerName += CPLSPrintf(" (layer %s)", pszLayerName); @@ -725,11 +839,44 @@ bool FileGDBTable::Open(const char *pszFilename, bool bUpdate, return false; } - // Read .gdtable file header + // Read .gdbtable file header GByte abyHeader[40]; returnErrorIf(VSIFReadL(abyHeader, 40, 1, m_fpTable) != 1); - m_nValidRecordCount = GetInt32(abyHeader + 4, 0); - returnErrorIf(m_nValidRecordCount < 0); + + int nGDBTableVersion = GetInt32(abyHeader, 0); + if (nGDBTableVersion == 3) + { + m_eGDBTableVersion = GDBTableVersion::V3; + } + else if (nGDBTableVersion == 4) + { + m_eGDBTableVersion = GDBTableVersion::V4; + if (m_bUpdate) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Version 4 of the FileGeodatabase format is not supported " + "for update."); + return false; + } + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Version %u of the FileGeodatabase format is not supported.", + nGDBTableVersion); + return false; + } + + if (m_eGDBTableVersion == GDBTableVersion::V3) + { + m_nValidRecordCount = GetInt32(abyHeader + 4, 0); + returnErrorIf(m_nValidRecordCount < 0); + } + else + { + m_nValidRecordCount = GetInt64(abyHeader + 16, 0); + returnErrorIf(m_nValidRecordCount < 0); + } m_nHeaderBufferMaxSize = GetInt32(abyHeader + 8, 0); @@ -765,7 +912,11 @@ bool FileGDBTable::Open(const char *pszFilename, bool bUpdate, returnErrorIf(m_fpTableX == nullptr); } } - else if (!ReadTableXHeader()) + else if (m_eGDBTableVersion == GDBTableVersion::V3 && + !ReadTableXHeaderV3()) + return false; + else if (m_eGDBTableVersion == GDBTableVersion::V4 && + !ReadTableXHeaderV4()) return false; } @@ -778,8 +929,8 @@ bool FileGDBTable::Open(const char *pszFilename, bool bUpdate, { /* Potentially unsafe. See #5842 */ CPLDebug("OpenFileGDB", - "%s: nTotalRecordCount (was %d) forced to " - "nValidRecordCount=%d", + "%s: nTotalRecordCount (was %" PRId64 ") forced to " + "nValidRecordCount=%" PRId64, m_osFilenameWithLayerName.c_str(), m_nTotalRecordCount, m_nValidRecordCount); m_nTotalRecordCount = m_nValidRecordCount; @@ -789,8 +940,10 @@ bool FileGDBTable::Open(const char *pszFilename, bool bUpdate, /* By default err on the safe side */ CPLError( CE_Warning, CPLE_AppDefined, - "File %s declares %d valid records, but %s declares " - "only %d total records. Using that later value for safety " + "File %s declares %" PRId64 + " valid records, but %s declares " + "only %" PRId64 + " total records. Using that later value for safety " "(this possibly ignoring features). " "You can also try setting OPENFILEGDB_IGNORE_GDBTABLX=YES " "to " @@ -809,7 +962,8 @@ bool FileGDBTable::Open(const char *pszFilename, bool bUpdate, else if (m_nTotalRecordCount != m_nValidRecordCount) { CPLDebug("OpenFileGDB", - "%s: nTotalRecordCount=%d nValidRecordCount=%d", + "%s: nTotalRecordCount=%" PRId64 + " nValidRecordCount=%" PRId64, pszFilename, m_nTotalRecordCount, m_nValidRecordCount); } #endif @@ -836,18 +990,19 @@ bool FileGDBTable::Open(const char *pszFilename, bool bUpdate, returnErrorIf(VSIFReadL(abyHeader, 14, 1, m_fpTable) != 1); m_nFieldDescLength = GetUInt32(abyHeader, 0); - const auto nVersion = GetUInt32(abyHeader + 4, 0); - // nVersion == 6 is used in table arcgis_pro_32_types.gdb/a0000000b.gdbtable (big_int) + const auto nSecondaryHeaderVersion = GetUInt32(abyHeader + 4, 0); + // nSecondaryHeaderVersion == 6 is used in table arcgis_pro_32_types.gdb/a0000000b.gdbtable (big_int) // Not sure why... - if (m_bUpdate && nVersion != 4 && nVersion != 6) // FileGDB v10 + if (m_bUpdate && nSecondaryHeaderVersion != 4 && + nSecondaryHeaderVersion != 6) // FileGDB v10 { CPLError(CE_Failure, CPLE_NotSupported, - "Version %u of the FileGeodatabase format is not supported " - "for update.", - nVersion); + "Version %u of the secondary header of the FileGeodatabase " + "format is not supported for update.", + nSecondaryHeaderVersion); return false; } - m_bIsV9 = (nVersion == 3); + m_bIsV9 = (nSecondaryHeaderVersion == 3); returnErrorIf(m_nOffsetFieldDesc > std::numeric_limits::max() - m_nFieldDescLength); @@ -1364,25 +1519,27 @@ static void ReadVarIntAndAddNoCheck(GByte *&pabyIter, GIntBig &nOutVal) /************************************************************************/ vsi_l_offset -FileGDBTable::GetOffsetInTableForRow(int iRow, vsi_l_offset *pnOffsetInTableX) +FileGDBTable::GetOffsetInTableForRow(int64_t iRow, + vsi_l_offset *pnOffsetInTableX) { const int errorRetValue = 0; if (pnOffsetInTableX) *pnOffsetInTableX = 0; returnErrorIf(iRow < 0 || iRow >= m_nTotalRecordCount); - m_bIsDeleted = FALSE; + m_bIsDeleted = false; if (m_fpTableX == nullptr) { - m_bIsDeleted = IS_DELETED(m_anFeatureOffsets[iRow]); - return GET_OFFSET(m_anFeatureOffsets[iRow]); + m_bIsDeleted = + IS_DELETED(m_anFeatureOffsets[static_cast(iRow)]); + return GET_OFFSET(m_anFeatureOffsets[static_cast(iRow)]); } vsi_l_offset nOffsetInTableX; if (!m_abyTablXBlockMap.empty()) { GUInt32 nCountBlocksBefore = 0; - int iBlock = iRow / 1024; + const int iBlock = static_cast(iRow / 1024); // Check if the block is not empty if (TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0) @@ -1406,7 +1563,8 @@ FileGDBTable::GetOffsetInTableForRow(int iRow, vsi_l_offset *pnOffsetInTableX) } m_nCountBlocksBeforeIBlockIdx = iBlock; m_nCountBlocksBeforeIBlockValue = nCountBlocksBefore; - const int iCorrectedRow = nCountBlocksBefore * 1024 + (iRow % 1024); + const int64_t iCorrectedRow = + static_cast(nCountBlocksBefore) * 1024 + (iRow % 1024); nOffsetInTableX = 16 + static_cast(m_nTablxOffsetSize) * iCorrectedRow; } @@ -1455,9 +1613,9 @@ uint64_t FileGDBTable::ReadFeatureOffset(const GByte *pabyBuffer) /* GetAndSelectNextNonEmptyRow() */ /************************************************************************/ -int FileGDBTable::GetAndSelectNextNonEmptyRow(int iRow) +int64_t FileGDBTable::GetAndSelectNextNonEmptyRow(int64_t iRow) { - const int errorRetValue = -1; + const int64_t errorRetValue = -1; returnErrorAndCleanupIf(iRow < 0 || iRow >= m_nTotalRecordCount, m_nCurRow = -1); @@ -1465,17 +1623,18 @@ int FileGDBTable::GetAndSelectNextNonEmptyRow(int iRow) { if (!m_abyTablXBlockMap.empty() && (iRow % 1024) == 0) { - int iBlock = iRow / 1024; + int iBlock = static_cast(iRow / 1024); if (TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0) { - int nBlocks = DIV_ROUND_UP(m_nTotalRecordCount, 1024); + int nBlocks = + static_cast(DIV_ROUND_UP(m_nTotalRecordCount, 1024)); do { iBlock++; } while (iBlock < nBlocks && TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0); - iRow = iBlock * 1024; + iRow = static_cast(iBlock) * 1024; if (iRow >= m_nTotalRecordCount) return -1; } @@ -1495,7 +1654,7 @@ int FileGDBTable::GetAndSelectNextNonEmptyRow(int iRow) /* SelectRow() */ /************************************************************************/ -int FileGDBTable::SelectRow(int iRow) +bool FileGDBTable::SelectRow(int64_t iRow) { const int errorRetValue = FALSE; returnErrorAndCleanupIf(iRow < 0 || iRow >= m_nTotalRecordCount, @@ -1539,7 +1698,8 @@ int FileGDBTable::SelectRow(int iRow) "NO"))) { CPLError(CE_Failure, CPLE_AppDefined, - "Invalid row length (%u) on feature %u compared " + "Invalid row length (%u) on feature %" PRId64 + " compared " "to the maximum size in the header (%u)", m_nRowBlobLength, iRow + 1, m_nHeaderBufferMaxSize); @@ -1549,7 +1709,8 @@ int FileGDBTable::SelectRow(int iRow) else { CPLDebug("OpenFileGDB", - "Invalid row length (%u) on feature %u compared " + "Invalid row length (%u) on feature %" PRId64 + " compared " "to the maximum size in the header (%u)", m_nRowBlobLength, iRow + 1, m_nHeaderBufferMaxSize); @@ -1571,7 +1732,7 @@ int FileGDBTable::SelectRow(int iRow) if (nOffsetTable + 4 + m_nRowBlobLength > m_nFileSize) { CPLError(CE_Failure, CPLE_AppDefined, - "Invalid row length (%u) on feature %u", + "Invalid row length (%u) on feature %" PRId64, m_nRowBlobLength, iRow + 1); m_nCurRow = -1; return errorRetValue; @@ -2253,7 +2414,7 @@ const OGRField *FileGDBTable::GetFieldValue(int iCol) if (iCol == static_cast(m_apoFields.size()) - 1 && m_pabyIterVals < pabyEnd) { - CPLDebug("OpenFileGDB", "%d bytes remaining at end of record %d", + CPLDebug("OpenFileGDB", "%d bytes remaining at end of record %" PRId64, static_cast(pabyEnd - m_pabyIterVals), m_nCurRow); } diff --git a/ogr/ogrsf_frmts/openfilegdb/filegdbtable.h b/ogr/ogrsf_frmts/openfilegdb/filegdbtable.h index a5a4fc67f126..d057f3f5935e 100644 --- a/ogr/ogrsf_frmts/openfilegdb/filegdbtable.h +++ b/ogr/ogrsf_frmts/openfilegdb/filegdbtable.h @@ -428,10 +428,19 @@ class FileGDBTable { VSILFILE *m_fpTable = nullptr; VSILFILE *m_fpTableX = nullptr; + + enum class GDBTableVersion + { + V3 = 3, // 32-bit object id + V4 = 4, // 64-bit object id (ince ArcGIS Pro 3.2) + }; + GDBTableVersion m_eGDBTableVersion = GDBTableVersion::V3; vsi_l_offset m_nFileSize = 0; /* only read when needed */ bool m_bUpdate = false; + bool m_bReliableObjectID = true; // can be set to false on some V4 files std::string m_osFilename{}; + std::string m_osFilenameWithLayerName{}; bool m_bIsV9 = false; std::vector> m_apoFields{}; int m_iObjectIdField = -1; @@ -465,7 +474,7 @@ class FileGDBTable no .gdbtablx file */ uint64_t m_nOffsetTableXTrailer = 0; - uint32_t m_n1024BlocksPresent = 0; + uint64_t m_n1024BlocksPresent = 0; std::vector m_abyTablXBlockMap{}; int m_nCountBlocksBeforeIBlockIdx = 0; /* optimization */ int m_nCountBlocksBeforeIBlockValue = 0; /* optimization */ @@ -479,9 +488,9 @@ class FileGDBTable int m_nChSaved = -1; int m_bError = FALSE; - int m_nCurRow = -1; + int64_t m_nCurRow = -1; int m_bHasDeletedFeaturesListed = FALSE; - int m_bIsDeleted = FALSE; + bool m_bIsDeleted = false; int m_nLastCol = -1; GByte *m_pabyIterVals = nullptr; int m_iAccNullable = 0; @@ -494,8 +503,8 @@ class FileGDBTable bool m_bStringsAreUTF8 = true; // if false, UTF16 std::string m_osTempString{}; // used as a temporary to store strings // recoded from UTF16 to UTF8 - int m_nValidRecordCount = 0; - int m_nTotalRecordCount = 0; + int64_t m_nValidRecordCount = 0; + int64_t m_nTotalRecordCount = 0; int m_iGeomField = -1; int m_nCountNullableFields = 0; int m_nNullableFieldsSizeInBytes = 0; @@ -556,7 +565,8 @@ class FileGDBTable bool WriteHeader(VSILFILE *fpTable); bool WriteHeaderX(VSILFILE *fpTableX); - int ReadTableXHeader(); + bool ReadTableXHeaderV3(); + bool ReadTableXHeaderV4(); int IsLikelyFeatureAtOffset(vsi_l_offset nOffset, GUInt32 *pnSize, int *pbDeletedRecord); bool GuessFeatureLocations(); @@ -624,12 +634,12 @@ class FileGDBTable return m_bGeomTypeHasM; } - int GetValidRecordCount() const + int64_t GetValidRecordCount() const { return m_nValidRecordCount; } - int GetTotalRecordCount() const + int64_t GetTotalRecordCount() const { return m_nTotalRecordCount; } @@ -670,6 +680,15 @@ class FileGDBTable return m_apoIndexes[i].get(); } + /** Return if we can use attribute or spatial indices. + * This can be false for some sparse tables with 64-bit ObjectID since + * the format of the sparse bitmap isn't fully understood yet. + */ + bool CanUseIndices() const + { + return m_bReliableObjectID; + } + bool HasSpatialIndex(); bool CreateIndex(const std::string &osIndexName, const std::string &osExpression); @@ -677,7 +696,8 @@ class FileGDBTable bool CreateSpatialIndex(); vsi_l_offset - GetOffsetInTableForRow(int iRow, vsi_l_offset *pnOffsetInTableX = nullptr); + GetOffsetInTableForRow(int64_t iRow, + vsi_l_offset *pnOffsetInTableX = nullptr); int HasDeletedFeaturesListed() const { @@ -686,20 +706,20 @@ class FileGDBTable /* Next call to SelectRow() or GetFieldValue() invalidates previously * returned values */ - int SelectRow(int iRow); - int GetAndSelectNextNonEmptyRow(int iRow); + bool SelectRow(int64_t iRow); + int64_t GetAndSelectNextNonEmptyRow(int64_t iRow); int HasGotError() const { return m_bError; } - int GetCurRow() const + int64_t GetCurRow() const { return m_nCurRow; } - int IsCurRowDeleted() const + bool IsCurRowDeleted() const { return m_bIsDeleted; } @@ -731,9 +751,9 @@ class FileGDBTable bool CreateFeature(const std::vector &asRawFields, const OGRGeometry *poGeom, int *pnFID = nullptr); - bool UpdateFeature(int nFID, const std::vector &asRawFields, + bool UpdateFeature(int64_t nFID, const std::vector &asRawFields, const OGRGeometry *poGeom); - bool DeleteFeature(int nFID); + bool DeleteFeature(int64_t nFID); bool CheckFreeListConsistency(); void DeleteFreeList(); @@ -766,18 +786,18 @@ class FileGDBIterator virtual FileGDBTable *GetTable() = 0; virtual void Reset() = 0; - virtual int GetNextRowSortedByFID() = 0; - virtual int GetRowCount(); + virtual int64_t GetNextRowSortedByFID() = 0; + virtual int64_t GetRowCount(); /* Only available on a BuildIsNotNull() iterator */ virtual const OGRField *GetMinValue(int &eOutOGRFieldType); virtual const OGRField *GetMaxValue(int &eOutOGRFieldType); /* will reset the iterator */ - virtual int GetMinMaxSumCount(double &dfMin, double &dfMax, double &dfSum, - int &nCount); + virtual bool GetMinMaxSumCount(double &dfMin, double &dfMax, double &dfSum, + int &nCount); /* Only available on a BuildIsNotNull() or Build() iterator */ - virtual int GetNextRowSortedByValue(); + virtual int64_t GetNextRowSortedByValue(); static FileGDBIterator *Build(FileGDBTable *poParent, int nFieldIdx, int bAscending, FileGDBSQLOp op, diff --git a/ogr/ogrsf_frmts/openfilegdb/filegdbtable_write.cpp b/ogr/ogrsf_frmts/openfilegdb/filegdbtable_write.cpp index aed5fe2a9b3b..befd9bc90993 100644 --- a/ogr/ogrsf_frmts/openfilegdb/filegdbtable_write.cpp +++ b/ogr/ogrsf_frmts/openfilegdb/filegdbtable_write.cpp @@ -33,6 +33,7 @@ #include "filegdbtable.h" #include +#include #include #include #include @@ -73,6 +74,7 @@ bool FileGDBTable::Create(const char *pszFilename, int nTablxOffsetSize, { CPLAssert(m_fpTable == nullptr); + m_eGDBTableVersion = GDBTableVersion::V3; m_bUpdate = true; m_eTableGeomType = eTableGeomType; m_nTablxOffsetSize = nTablxOffsetSize; @@ -88,6 +90,7 @@ bool FileGDBTable::Create(const char *pszFilename, int nTablxOffsetSize, } m_osFilename = pszFilename; + m_osFilenameWithLayerName = m_osFilename; m_fpTable = VSIFOpenL(pszFilename, "wb+"); if (m_fpTable == nullptr) { @@ -152,8 +155,9 @@ bool FileGDBTable::WriteHeader(VSILFILE *fpTable) VSIFSeekL(fpTable, 0, SEEK_SET); bool bRet = - WriteUInt32(fpTable, 3) && // version number - WriteUInt32(fpTable, m_nValidRecordCount) && // number of valid rows + WriteUInt32(fpTable, 3) && // version number + // number of valid rows + WriteUInt32(fpTable, static_cast(m_nValidRecordCount)) && WriteUInt32(fpTable, m_nHeaderBufferMaxSize) && // largest size of a feature // record / field description @@ -190,8 +194,8 @@ bool FileGDBTable::WriteHeaderX(VSILFILE *fpTableX) { VSIFSeekL(fpTableX, 0, SEEK_SET); if (!WriteUInt32(fpTableX, 3) || // version number - !WriteUInt32(fpTableX, m_n1024BlocksPresent) || - !WriteUInt32(fpTableX, m_nTotalRecordCount) || + !WriteUInt32(fpTableX, static_cast(m_n1024BlocksPresent)) || + !WriteUInt32(fpTableX, static_cast(m_nTotalRecordCount)) || !WriteUInt32(fpTableX, m_nTablxOffsetSize)) { CPLError(CE_Failure, CPLE_FileIO, "Cannot write .gdbtablx header"); @@ -267,7 +271,8 @@ bool FileGDBTable::Sync(VSILFILE *fpTable, VSILFILE *fpTableX) if (m_bDirtyHeader && fpTable) { VSIFSeekL(fpTable, 4, SEEK_SET); - bRet &= WriteUInt32(fpTable, m_nValidRecordCount); + bRet &= + WriteUInt32(fpTable, static_cast(m_nValidRecordCount)); m_nHeaderBufferMaxSize = std::max(m_nHeaderBufferMaxSize, std::max(m_nRowBufferMaxSize, m_nFieldDescLength)); @@ -285,8 +290,10 @@ bool FileGDBTable::Sync(VSILFILE *fpTable, VSILFILE *fpTableX) if (m_bDirtyTableXHeader && fpTableX) { VSIFSeekL(fpTableX, 4, SEEK_SET); - bRet &= WriteUInt32(fpTableX, m_n1024BlocksPresent); - bRet &= WriteUInt32(fpTableX, m_nTotalRecordCount); + bRet &= + WriteUInt32(fpTableX, static_cast(m_n1024BlocksPresent)); + bRet &= + WriteUInt32(fpTableX, static_cast(m_nTotalRecordCount)); m_bDirtyTableXHeader = false; } @@ -297,8 +304,8 @@ bool FileGDBTable::Sync(VSILFILE *fpTable, VSILFILE *fpTableX) m_nTablxOffsetSize * TABLX_FEATURES_PER_PAGE * static_cast(m_n1024BlocksPresent); VSIFSeekL(fpTableX, m_nOffsetTableXTrailer, SEEK_SET); - const uint32_t n1024BlocksTotal = - DIV_ROUND_UP(m_nTotalRecordCount, TABLX_FEATURES_PER_PAGE); + const uint32_t n1024BlocksTotal = static_cast( + DIV_ROUND_UP(m_nTotalRecordCount, TABLX_FEATURES_PER_PAGE)); if (!m_abyTablXBlockMap.empty()) { CPLAssert(m_abyTablXBlockMap.size() >= (n1024BlocksTotal + 7) / 8); @@ -314,7 +321,8 @@ bool FileGDBTable::Sync(VSILFILE *fpTable, VSILFILE *fpTableX) m_abyTablXBlockMap.resize(nBitmapInt32Words * 4); bRet &= WriteUInt32(fpTableX, nBitmapInt32Words); bRet &= WriteUInt32(fpTableX, n1024BlocksTotal); - bRet &= WriteUInt32(fpTableX, m_n1024BlocksPresent); + bRet &= + WriteUInt32(fpTableX, static_cast(m_n1024BlocksPresent)); uint32_t nTrailingZero32BitWords = 0; for (int i = static_cast(m_abyTablXBlockMap.size() / 4) - 1; i >= 0; --i) @@ -339,10 +347,10 @@ bool FileGDBTable::Sync(VSILFILE *fpTable, VSILFILE *fpTableX) nCountBlocks += TEST_BIT(m_abyTablXBlockMap.data(), i) != 0; if (nCountBlocks != m_n1024BlocksPresent) { - CPLError( - CE_Failure, CPLE_AppDefined, - "Sync(): nCountBlocks(=%u) != m_n1024BlocksPresent(=%u)", - nCountBlocks, m_n1024BlocksPresent); + CPLError(CE_Failure, CPLE_AppDefined, + "Sync(): nCountBlocks(=%u) != " + "m_n1024BlocksPresent(=%" PRIu64 ")", + nCountBlocks, m_n1024BlocksPresent); } #endif bRet &= VSIFWriteL(m_abyTablXBlockMap.data(), 1, @@ -1602,20 +1610,21 @@ bool FileGDBTable::SeekIntoTableXForNewFeature(int nObjectID) int iCorrectedRow; bool bWriteEmptyPageAtEnd = false; const uint32_t nPageSize = TABLX_FEATURES_PER_PAGE * m_nTablxOffsetSize; + const int nTotalRecordCount = static_cast(m_nTotalRecordCount); if (m_abyTablXBlockMap.empty()) { // Is the OID to write in the current allocated pages, or in the next // page ? if ((nObjectID - 1) / TABLX_FEATURES_PER_PAGE <= - ((m_nTotalRecordCount == 0) + ((nTotalRecordCount == 0) ? 0 - : (1 + (m_nTotalRecordCount - 1) / TABLX_FEATURES_PER_PAGE))) + : (1 + (nTotalRecordCount - 1) / TABLX_FEATURES_PER_PAGE))) { iCorrectedRow = nObjectID - 1; const auto n1024BlocksPresentBefore = m_n1024BlocksPresent; m_n1024BlocksPresent = - DIV_ROUND_UP(std::max(m_nTotalRecordCount, nObjectID), + DIV_ROUND_UP(std::max(nTotalRecordCount, nObjectID), TABLX_FEATURES_PER_PAGE); bWriteEmptyPageAtEnd = m_n1024BlocksPresent > n1024BlocksPresentBefore; @@ -1626,13 +1635,13 @@ bool FileGDBTable::SeekIntoTableXForNewFeature(int nObjectID) m_abyTablXBlockMap.resize( (DIV_ROUND_UP(nObjectID, TABLX_FEATURES_PER_PAGE) + 7) / 8); for (int i = 0; - i < DIV_ROUND_UP(m_nTotalRecordCount, TABLX_FEATURES_PER_PAGE); + i < DIV_ROUND_UP(nTotalRecordCount, TABLX_FEATURES_PER_PAGE); ++i) m_abyTablXBlockMap[i / 8] |= (1 << (i % 8)); const int iBlock = (nObjectID - 1) / TABLX_FEATURES_PER_PAGE; m_abyTablXBlockMap[iBlock / 8] |= (1 << (iBlock % 8)); iCorrectedRow = - DIV_ROUND_UP(m_nTotalRecordCount, TABLX_FEATURES_PER_PAGE) * + DIV_ROUND_UP(nTotalRecordCount, TABLX_FEATURES_PER_PAGE) * TABLX_FEATURES_PER_PAGE + ((nObjectID - 1) % TABLX_FEATURES_PER_PAGE); m_n1024BlocksPresent++; @@ -1643,7 +1652,7 @@ bool FileGDBTable::SeekIntoTableXForNewFeature(int nObjectID) { const int iBlock = (nObjectID - 1) / TABLX_FEATURES_PER_PAGE; - if (nObjectID <= m_nTotalRecordCount) + if (nObjectID <= nTotalRecordCount) { CPLAssert(iBlock / 8 < static_cast(m_abyTablXBlockMap.size())); if (TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0) @@ -1657,9 +1666,8 @@ bool FileGDBTable::SeekIntoTableXForNewFeature(int nObjectID) std::vector abyTmp(nPageSize); uint64_t nOffset = - TABLX_HEADER_SIZE + - static_cast(m_n1024BlocksPresent) * nPageSize; - for (int i = m_n1024BlocksPresent - 1; + TABLX_HEADER_SIZE + m_n1024BlocksPresent * nPageSize; + for (int i = static_cast(m_n1024BlocksPresent - 1); i >= static_cast(nCountBlocksBefore); --i) { nOffset -= nPageSize; @@ -1702,7 +1710,7 @@ bool FileGDBTable::SeekIntoTableXForNewFeature(int nObjectID) } } else if (DIV_ROUND_UP(nObjectID, TABLX_FEATURES_PER_PAGE) > - DIV_ROUND_UP(m_nTotalRecordCount, TABLX_FEATURES_PER_PAGE)) + DIV_ROUND_UP(nTotalRecordCount, TABLX_FEATURES_PER_PAGE)) { m_abyTablXBlockMap.resize( (DIV_ROUND_UP(nObjectID, TABLX_FEATURES_PER_PAGE) + 7) / 8); @@ -1817,7 +1825,7 @@ bool FileGDBTable::CreateFeature(const std::vector &asRawFields, "Maximum number of records per table reached"); return false; } - nObjectID = m_nTotalRecordCount + 1; + nObjectID = static_cast(m_nTotalRecordCount + 1); } try @@ -1882,7 +1890,8 @@ bool FileGDBTable::CreateFeature(const std::vector &asRawFields, m_nFileSize += sizeof(uint32_t) + m_nRowBlobLength; } - m_nTotalRecordCount = std::max(m_nTotalRecordCount, nObjectID); + m_nTotalRecordCount = + std::max(m_nTotalRecordCount, static_cast(nObjectID)); m_nValidRecordCount++; m_bDirtyHeader = true; @@ -1897,7 +1906,7 @@ bool FileGDBTable::CreateFeature(const std::vector &asRawFields, /* UpdateFeature() */ /************************************************************************/ -bool FileGDBTable::UpdateFeature(int nFID, +bool FileGDBTable::UpdateFeature(int64_t nFID, const std::vector &asRawFields, const OGRGeometry *poGeom) { @@ -2050,7 +2059,7 @@ bool FileGDBTable::UpdateFeature(int nFID, /* DeleteFeature() */ /************************************************************************/ -bool FileGDBTable::DeleteFeature(int nFID) +bool FileGDBTable::DeleteFeature(int64_t nFID) { if (!m_bUpdate) return false; @@ -2627,7 +2636,7 @@ void FileGDBTable::RecomputeExtent() // Scan all features OGREnvelope sLayerEnvelope; OGREnvelope sFeatureEnvelope; - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) diff --git a/ogr/ogrsf_frmts/openfilegdb/filegdbtable_write_fields.cpp b/ogr/ogrsf_frmts/openfilegdb/filegdbtable_write_fields.cpp index c8e61c7e6f3c..894e9ff23a3d 100644 --- a/ogr/ogrsf_frmts/openfilegdb/filegdbtable_write_fields.cpp +++ b/ogr/ogrsf_frmts/openfilegdb/filegdbtable_write_fields.cpp @@ -837,7 +837,7 @@ bool FileGDBTable::DeleteField(int iField) m_apoFields[m_iGeomField]->m_eType = FGFT_BINARY; m_iGeomField = -1; - for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat) { iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) diff --git a/ogr/ogrsf_frmts/openfilegdb/gdalopenfilegdbrasterband.cpp b/ogr/ogrsf_frmts/openfilegdb/gdalopenfilegdbrasterband.cpp index 6a0119005011..7e337ebb0910 100644 --- a/ogr/ogrsf_frmts/openfilegdb/gdalopenfilegdbrasterband.cpp +++ b/ogr/ogrsf_frmts/openfilegdb/gdalopenfilegdbrasterband.cpp @@ -117,10 +117,14 @@ bool OGROpenFileGDBDataSource::OpenRaster(const GDALOpenInfo *poOpenInfo, return false; } - int iRow = 0; + int64_t iRow = 0; while (iRow < oTable.GetTotalRecordCount() && (iRow = oTable.GetAndSelectNextNonEmptyRow(iRow)) >= 0) { + if (iRow >= INT32_MAX) + { + return false; + } auto psField = oTable.GetFieldValue(i_raster_id); if (!psField) { @@ -137,7 +141,7 @@ bool OGROpenFileGDBDataSource::OpenRaster(const GDALOpenInfo *poOpenInfo, continue; } - const int nGDBRasterBandId = iRow + 1; + const int nGDBRasterBandId = static_cast(iRow) + 1; psField = oTable.GetFieldValue(i_sequence_nbr); if (!psField) diff --git a/ogr/ogrsf_frmts/openfilegdb/ogr_openfilegdb.h b/ogr/ogrsf_frmts/openfilegdb/ogr_openfilegdb.h index 2dd3a8a2a654..edc16d750672 100644 --- a/ogr/ogrsf_frmts/openfilegdb/ogr_openfilegdb.h +++ b/ogr/ogrsf_frmts/openfilegdb/ogr_openfilegdb.h @@ -138,7 +138,7 @@ class OGROpenFileGDBLayer final : public OGRLayer int m_iGeomFieldIdx = -1; int m_iAreaField = -1; // index of Shape_Area field int m_iLengthField = -1; // index of Shape_Length field - int m_iCurFeat = 0; + int64_t m_iCurFeat = 0; int m_iFIDAsRegularColumnIndex = -1; std::string m_osDefinition{}; std::string m_osDocumentation{}; @@ -239,7 +239,7 @@ class OGROpenFileGDBLayer final : public OGRLayer int &eOutType); int GetMinMaxSumCount(OGRFieldDefn *poFieldDefn, double &dfMin, double &dfMax, double &dfSum, int &nCount); - int HasIndexForField(const char *pszFieldName); + bool HasIndexForField(const char *pszFieldName); FileGDBIterator *BuildIndex(const char *pszFieldName, int bAscending, int op, swq_expr_node *poValue); diff --git a/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource.cpp b/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource.cpp index 77dae116b5b7..16d0c20092eb 100644 --- a/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource.cpp +++ b/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource.cpp @@ -1504,7 +1504,7 @@ OGRFeature *OGROpenFileGDBSimpleSQLLayer::GetNextFeature() if (m_nLimit >= 0 && m_nIterated == m_nLimit) return nullptr; - int nRow = poIter->GetNextRowSortedByValue(); + const int64_t nRow = poIter->GetNextRowSortedByValue(); if (nRow < 0) return nullptr; OGRFeature *poFeature = GetFeature(nRow + 1); diff --git a/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource_write.cpp b/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource_write.cpp index 6cf35031911b..411bbbb368c3 100644 --- a/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource_write.cpp +++ b/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource_write.cpp @@ -77,7 +77,7 @@ bool OGROpenFileGDBDataSource::GetExistingSpatialRef( FETCH_FIELD_IDX(iZTolerance, "ZTolerance", FGFT_FLOAT64); FETCH_FIELD_IDX(iMTolerance, "MTolerance", FGFT_FLOAT64); - int iCurFeat = 0; + int64_t iCurFeat = 0; while (iCurFeat < oTable.GetTotalRecordCount()) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); @@ -246,7 +246,8 @@ bool OGROpenFileGDBDataSource::RemoveRelationshipFromItemRelationships( FETCH_FIELD_IDX_WITH_RET(iOriginID, "OriginID", FGFT_GUID, false); FETCH_FIELD_IDX_WITH_RET(iDestID, "DestID", FGFT_GUID, false); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -290,7 +291,7 @@ bool OGROpenFileGDBDataSource::LinkDomainToTable( FETCH_FIELD_IDX(iOriginID, "OriginID", FGFT_GUID); FETCH_FIELD_IDX(iDestID, "DestID", FGFT_GUID); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); @@ -331,7 +332,8 @@ bool OGROpenFileGDBDataSource::UnlinkDomainToTable( FETCH_FIELD_IDX(iOriginID, "OriginID", FGFT_GUID); FETCH_FIELD_IDX(iDestID, "DestID", FGFT_GUID); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -365,7 +367,8 @@ bool OGROpenFileGDBDataSource::UpdateXMLDefinition( FETCH_FIELD_IDX(iName, "Name", FGFT_STRING); FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -406,7 +409,8 @@ bool OGROpenFileGDBDataSource::FindUUIDFromName(const std::string &osName, FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID); FETCH_FIELD_IDX(iName, "Name", FGFT_STRING); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -1262,9 +1266,10 @@ OGROpenFileGDBDataSource::ICreateLayer(const char *pszLayerName, auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone; FileGDBTable oTable; - if (!oTable.Open(m_osGDBSystemCatalogFilename.c_str(), false)) + if (!oTable.Open(m_osGDBSystemCatalogFilename.c_str(), false) || + oTable.GetTotalRecordCount() >= INT32_MAX) return nullptr; - const int nTableNum = 1 + oTable.GetTotalRecordCount(); + const int nTableNum = static_cast(1 + oTable.GetTotalRecordCount()); oTable.Close(); const std::string osFilename(CPLFormFilename( @@ -1317,7 +1322,7 @@ OGRErr OGROpenFileGDBDataSource::DeleteLayer(int iLayer) FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, OGRERR_FAILURE); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); @@ -1342,7 +1347,7 @@ OGRErr OGROpenFileGDBDataSource::DeleteLayer(int iLayer) FETCH_FIELD_IDX_WITH_RET(iUUID, "UUID", FGFT_GLOBALID, OGRERR_FAILURE); FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, OGRERR_FAILURE); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); @@ -1374,7 +1379,7 @@ OGRErr OGROpenFileGDBDataSource::DeleteLayer(int iLayer) OGRERR_FAILURE); FETCH_FIELD_IDX_WITH_RET(iDestID, "DestID", FGFT_GUID, OGRERR_FAILURE); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); @@ -1561,7 +1566,7 @@ bool OGROpenFileGDBDataSource::DeleteFieldDomain( FETCH_FIELD_IDX(iType, "Type", FGFT_GUID); FETCH_FIELD_IDX(iName, "Name", FGFT_STRING); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); @@ -1601,7 +1606,7 @@ bool OGROpenFileGDBDataSource::DeleteFieldDomain( FETCH_FIELD_IDX(iDestID, "DestID", FGFT_GUID); FETCH_FIELD_IDX(iType, "Type", FGFT_GUID); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); @@ -1673,7 +1678,8 @@ bool OGROpenFileGDBDataSource::UpdateFieldDomain( FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML); bool bMatchFound = false; - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) @@ -1818,12 +1824,13 @@ bool OGROpenFileGDBDataSource::AddRelationship( const std::string osThisGUID = OFGDBGenerateUUID(); FileGDBTable oTable; - if (!oTable.Open(m_osGDBItemsFilename.c_str(), true)) + if (!oTable.Open(m_osGDBItemsFilename.c_str(), true) || + oTable.GetTotalRecordCount() >= INT32_MAX) return false; // hopefully this just needs to be a unique value. Seems to autoincrement // when created from ArcMap at least! - const int iDsId = oTable.GetTotalRecordCount() + 1; + const int iDsId = static_cast(oTable.GetTotalRecordCount() + 1); std::string osMappingTableOidName; if (relationship->GetCardinality() == @@ -2002,7 +2009,7 @@ bool OGROpenFileGDBDataSource::DeleteRelationship(const std::string &name, FETCH_FIELD_IDX_WITH_RET(iType, "Type", FGFT_GUID, false); FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, false); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); @@ -2102,12 +2109,15 @@ bool OGROpenFileGDBDataSource::UpdateRelationship( } FileGDBTable oTable; - if (!oTable.Open(m_osGDBItemsFilename.c_str(), true)) + if (!oTable.Open(m_osGDBItemsFilename.c_str(), true) || + oTable.GetTotalRecordCount() >= INT32_MAX) + { return false; + } // hopefully this just needs to be a unique value. Seems to autoincrement // when created from ArcMap at least! - const int iDsId = oTable.GetTotalRecordCount() + 1; + const int iDsId = static_cast(oTable.GetTotalRecordCount()) + 1; std::string osMappingTableOidName; if (relationship->GetCardinality() == @@ -2145,7 +2155,8 @@ bool OGROpenFileGDBDataSource::UpdateRelationship( bool bMatchFound = false; std::string osUUID; - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); if (iCurFeat < 0) diff --git a/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer.cpp b/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer.cpp index c98a978a49fa..1c33b099cc7e 100644 --- a/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer.cpp +++ b/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer.cpp @@ -330,7 +330,7 @@ void OGROpenFileGDBLayer::TryToDetectMultiPatchKind() if (m_poLyrTable->GetTotalRecordCount() == 0) return; - int nFirstIdx = m_poLyrTable->GetAndSelectNextNonEmptyRow(0); + const int64_t nFirstIdx = m_poLyrTable->GetAndSelectNextNonEmptyRow(0); if (nFirstIdx < 0) return; @@ -343,7 +343,7 @@ void OGROpenFileGDBLayer::TryToDetectMultiPatchKind() const OGRwkbGeometryType eType = poGeom->getGeometryType(); delete poGeom; - int nLastIdx = m_poLyrTable->GetTotalRecordCount() - 1; + int64_t nLastIdx = m_poLyrTable->GetTotalRecordCount() - 1; const GUInt32 nErrorCount = CPLGetErrorCounter(); while (nLastIdx > nFirstIdx && m_poLyrTable->GetOffsetInTableForRow(nLastIdx) == 0 && @@ -449,7 +449,8 @@ int OGROpenFileGDBLayer::BuildLayerDefinition() } #endif - if (!(m_poLyrTable->HasSpatialIndex() && + if (!(m_poLyrTable->CanUseIndices() && + m_poLyrTable->HasSpatialIndex() && CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX", "YES"))) && CPLTestBool(CPLGetConfigOption("OPENFILEGDB_IN_MEMORY_SPI", "YES"))) @@ -460,9 +461,11 @@ int OGROpenFileGDBLayer::BuildLayerDefinition() sGlobalBounds.maxx = poGDBGeomField->GetXMax(); sGlobalBounds.maxy = poGDBGeomField->GetYMax(); m_pQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr); - CPLQuadTreeSetMaxDepth(m_pQuadTree, - CPLQuadTreeGetAdvisedMaxDepth( - m_poLyrTable->GetValidRecordCount())); + CPLQuadTreeSetMaxDepth( + m_pQuadTree, + CPLQuadTreeGetAdvisedMaxDepth( + static_cast(std::min( + INT_MAX, m_poLyrTable->GetValidRecordCount())))); } else { @@ -957,7 +960,7 @@ void OGROpenFileGDBLayer::SetSpatialFilter(OGRGeometry *poGeom) if (poGeom != nullptr) { if (m_poSpatialIndexIterator == nullptr && - m_poLyrTable->HasSpatialIndex() && + m_poLyrTable->CanUseIndices() && m_poLyrTable->HasSpatialIndex() && CPLTestBool( CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX", "YES"))) { @@ -1703,7 +1706,7 @@ OGRFeature *OGROpenFileGDBLayer::GetCurrentFeature() { OGRFeature *poFeature = nullptr; int iOGRIdx = 0; - int iRow = m_poLyrTable->GetCurRow(); + int64_t iRow = m_poLyrTable->GetCurRow(); for (int iGDBIdx = 0; iGDBIdx < m_poLyrTable->GetFieldCount(); iGDBIdx++) { if (iOGRIdx == m_iFIDAsRegularColumnIndex) @@ -1727,16 +1730,27 @@ OGRFeature *OGROpenFileGDBLayer::GetCurrentFeature() if (m_poLyrTable->GetFeatureExtent(psField, &sFeatureEnvelope)) { - CPLRectObj sBounds; - sBounds.minx = sFeatureEnvelope.MinX; - sBounds.miny = sFeatureEnvelope.MinY; - sBounds.maxx = sFeatureEnvelope.MaxX; - sBounds.maxy = sFeatureEnvelope.MaxY; - CPLQuadTreeInsertWithBounds( - m_pQuadTree, - reinterpret_cast( - static_cast(iRow)), - &sBounds); +#if SIZEOF_VOIDP < 8 + if (iRow > INT32_MAX) + { + // m_pQuadTree stores iRow values as void* + // This would overflow here. + m_eSpatialIndexState = SPI_INVALID; + } + else +#endif + { + CPLRectObj sBounds; + sBounds.minx = sFeatureEnvelope.MinX; + sBounds.miny = sFeatureEnvelope.MinY; + sBounds.maxx = sFeatureEnvelope.MaxX; + sBounds.maxy = sFeatureEnvelope.MaxY; + CPLQuadTreeInsertWithBounds( + m_pQuadTree, + reinterpret_cast( + static_cast(iRow)), + &sBounds); + } } } @@ -1863,8 +1877,9 @@ OGRFeature *OGROpenFileGDBLayer::GetNextFeature() { return nullptr; } - int iRow = static_cast(reinterpret_cast( - m_pahFilteredFeatures[m_iCurFeat++])); + const auto iRow = + static_cast(reinterpret_cast( + m_pahFilteredFeatures[m_iCurFeat++])); if (m_poLyrTable->SelectRow(iRow)) { poFeature = GetCurrentFeature(); @@ -1882,7 +1897,7 @@ OGRFeature *OGROpenFileGDBLayer::GetNextFeature() { while (true) { - int iRow = poIterator->GetNextRowSortedByFID(); + const auto iRow = poIterator->GetNextRowSortedByFID(); if (iRow < 0) return nullptr; if (m_poLyrTable->SelectRow(iRow)) @@ -1954,7 +1969,7 @@ OGRFeature *OGROpenFileGDBLayer::GetFeature(GIntBig nFeatureId) if (nFeatureId < 1 || nFeatureId > m_poLyrTable->GetTotalRecordCount()) return nullptr; - if (!m_poLyrTable->SelectRow(static_cast(nFeatureId) - 1)) + if (!m_poLyrTable->SelectRow(nFeatureId - 1)) return nullptr; /* Temporarily disable spatial filter */ @@ -1993,7 +2008,7 @@ OGRErr OGROpenFileGDBLayer::SetNextByIndex(GIntBig nIndex) { if (nIndex < 0 || nIndex >= m_nFilteredFeatureCount) return OGRERR_FAILURE; - m_iCurFeat = static_cast(nIndex); + m_iCurFeat = nIndex; return OGRERR_NONE; } else if (m_poLyrTable->GetValidRecordCount() == @@ -2001,7 +2016,7 @@ OGRErr OGROpenFileGDBLayer::SetNextByIndex(GIntBig nIndex) { if (nIndex < 0 || nIndex >= m_poLyrTable->GetValidRecordCount()) return OGRERR_FAILURE; - m_iCurFeat = static_cast(nIndex); + m_iCurFeat = nIndex; return OGRERR_NONE; } else @@ -2105,7 +2120,7 @@ GIntBig OGROpenFileGDBLayer::GetFeatureCount(int bForce) int nCount = 0; while (true) { - const int nRowIdx = + const auto nRowIdx = m_poSpatialIndexIterator->GetNextRowSortedByFID(); if (nRowIdx < 0) break; @@ -2149,7 +2164,7 @@ GIntBig OGROpenFileGDBLayer::GetFeatureCount(int bForce) m_nFilteredFeatureCount = 0; } - for (int i = 0; i < m_poLyrTable->GetTotalRecordCount(); i++) + for (int64_t i = 0; i < m_poLyrTable->GetTotalRecordCount(); i++) { if (!m_poLyrTable->SelectRow(i)) { @@ -2158,6 +2173,15 @@ GIntBig OGROpenFileGDBLayer::GetFeatureCount(int bForce) else continue; } +#if SIZEOF_VOIDP < 8 + if (i > INT32_MAX) + { + // CPLQuadTreeInsertWithBounds stores row index values as void* + // This would overflow here. + m_eSpatialIndexState = SPI_INVALID; + break; + } +#endif const OGRField *psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx); @@ -2307,7 +2331,8 @@ int OGROpenFileGDBLayer::TestCapability(const char *pszCap) else if (EQUAL(pszCap, OLCFastSpatialFilter)) { return m_eSpatialIndexState == SPI_COMPLETED || - m_poLyrTable->HasSpatialIndex(); + (m_poLyrTable->CanUseIndices() && + m_poLyrTable->HasSpatialIndex()); } return FALSE; @@ -2317,11 +2342,12 @@ int OGROpenFileGDBLayer::TestCapability(const char *pszCap) /* HasIndexForField() */ /***********************************************************************/ -int OGROpenFileGDBLayer::HasIndexForField(const char *pszFieldName) +bool OGROpenFileGDBLayer::HasIndexForField(const char *pszFieldName) { if (!BuildLayerDefinition()) - return FALSE; - + return false; + if (!m_poLyrTable->CanUseIndices()) + return false; int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName); return (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex()); @@ -2393,6 +2419,8 @@ const OGRField *OGROpenFileGDBLayer::GetMinMaxValue(OGRFieldDefn *poFieldDefn, eOutType = -1; if (!BuildLayerDefinition()) return nullptr; + if (!m_poLyrTable->CanUseIndices()) + return nullptr; const int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); @@ -2427,21 +2455,21 @@ int OGROpenFileGDBLayer::GetMinMaxSumCount(OGRFieldDefn *poFieldDefn, dfSum = 0.0; nCount = 0; if (!BuildLayerDefinition()) - return FALSE; + return false; + if (!m_poLyrTable->CanUseIndices()) + return false; int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex()) { - FileGDBIterator *poIter = - FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, TRUE); - if (poIter != nullptr) + auto poIter = std::unique_ptr( + FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, TRUE)); + if (poIter) { - int nRet = poIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); - delete poIter; - return nRet; + return poIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); } } - return FALSE; + return false; } /************************************************************************/ diff --git a/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer_write.cpp b/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer_write.cpp index 819b24554db3..a12daa4c93cd 100644 --- a/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer_write.cpp +++ b/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer_write.cpp @@ -30,6 +30,7 @@ #include "ogr_openfilegdb.h" #include "filegdb_gdbtoogrfieldtype.h" +#include #include #include #include @@ -314,7 +315,8 @@ bool OGROpenFileGDBLayer::CreateFeatureDataset(const char *pszFeatureDataset) if (!oTable.Open(m_poDS->m_osGDBItemsFilename.c_str(), false)) return false; CPLCreateXMLElementAndValue( - psRoot, "DSID", CPLSPrintf("%d", 1 + oTable.GetTotalRecordCount())); + psRoot, "DSID", + CPLSPrintf("%" PRId64, 1 + oTable.GetTotalRecordCount())); } CPLCreateXMLElementAndValue(psRoot, "Versioned", "false"); @@ -458,7 +460,7 @@ bool OGROpenFileGDBLayer::Create(const OGRGeomFieldDefn *poSrcGeomFieldDefn) FETCH_FIELD_IDX(iName, "Name", FGFT_STRING); FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); @@ -2635,7 +2637,8 @@ void OGROpenFileGDBLayer::RefreshXMLDefinitionInMemory() if (!oTable.Open(m_poDS->m_osGDBItemsFilename.c_str(), false)) return; CPLCreateXMLElementAndValue( - psRoot, "DSID", CPLSPrintf("%d", 1 + oTable.GetTotalRecordCount())); + psRoot, "DSID", + CPLSPrintf("%" PRId64, 1 + oTable.GetTotalRecordCount())); } CPLCreateXMLElementAndValue(psRoot, "Versioned", "false"); @@ -3186,7 +3189,7 @@ OGRErr OGROpenFileGDBLayer::Rename(const char *pszDstTableName) FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, OGRERR_FAILURE); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat); @@ -3224,7 +3227,7 @@ OGRErr OGROpenFileGDBLayer::Rename(const char *pszDstTableName) FETCH_FIELD_IDX_WITH_RET(iDefinition, "Definition", FGFT_XML, OGRERR_FAILURE); - for (int iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); + for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount(); ++iCurFeat) { iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);