@loaders.gl/parquet 5.0.0-alpha.2 → 5.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/LICENSE +1 -1
  2. package/dist/bundled.d.ts +1 -1
  3. package/dist/bundled.d.ts.map +1 -1
  4. package/dist/bundled.js.map +1 -1
  5. package/dist/delta-format.d.ts +9 -0
  6. package/dist/delta-format.d.ts.map +1 -0
  7. package/dist/delta-format.js +12 -0
  8. package/dist/delta-format.js.map +1 -0
  9. package/dist/delta-source-loader-types.d.ts +27 -0
  10. package/dist/delta-source-loader-types.d.ts.map +1 -0
  11. package/dist/delta-source-loader-types.js +28 -0
  12. package/dist/delta-source-loader-types.js.map +1 -0
  13. package/dist/delta-source.d.ts +48 -0
  14. package/dist/delta-source.d.ts.map +1 -0
  15. package/dist/delta-source.js +222 -0
  16. package/dist/delta-source.js.map +1 -0
  17. package/dist/delta-types.d.ts +38 -0
  18. package/dist/delta-types.d.ts.map +1 -0
  19. package/dist/delta-types.js +5 -0
  20. package/dist/delta-types.js.map +1 -0
  21. package/dist/geoparquet-loader.js +1 -1
  22. package/dist/iceberg-rest-catalog.d.ts +49 -0
  23. package/dist/iceberg-rest-catalog.d.ts.map +1 -0
  24. package/dist/iceberg-rest-catalog.js +78 -0
  25. package/dist/iceberg-rest-catalog.js.map +1 -0
  26. package/dist/iceberg-table-source.d.ts +63 -0
  27. package/dist/iceberg-table-source.d.ts.map +1 -0
  28. package/dist/iceberg-table-source.js +797 -0
  29. package/dist/iceberg-table-source.js.map +1 -0
  30. package/dist/iceberg-types.d.ts +150 -0
  31. package/dist/iceberg-types.d.ts.map +1 -0
  32. package/dist/iceberg-types.js +5 -0
  33. package/dist/iceberg-types.js.map +1 -0
  34. package/dist/index.cjs +15005 -3143
  35. package/dist/index.cjs.map +3 -3
  36. package/dist/index.d.ts +7 -3
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +5 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/lib/arrow/convert-schema-from-parquet.d.ts +3 -1
  41. package/dist/lib/arrow/convert-schema-from-parquet.d.ts.map +1 -1
  42. package/dist/lib/arrow/convert-schema-from-parquet.js +94 -19
  43. package/dist/lib/arrow/convert-schema-from-parquet.js.map +1 -1
  44. package/dist/lib/arrow/create-nested-arrow-vector.js +89 -0
  45. package/dist/lib/arrow/create-nested-arrow-vector.js.map +1 -1
  46. package/dist/lib/constants.js +1 -1
  47. package/dist/lib/encoders/encode-table-to-parquet-js.d.ts.map +1 -1
  48. package/dist/lib/encoders/encode-table-to-parquet-js.js +202 -7
  49. package/dist/lib/encoders/encode-table-to-parquet-js.js.map +1 -1
  50. package/dist/lib/geo/geoparquet-covering.d.ts +19 -0
  51. package/dist/lib/geo/geoparquet-covering.d.ts.map +1 -0
  52. package/dist/lib/geo/geoparquet-covering.js +137 -0
  53. package/dist/lib/geo/geoparquet-covering.js.map +1 -0
  54. package/dist/lib/geo/geospatial-metadata.d.ts.map +1 -1
  55. package/dist/lib/geo/geospatial-metadata.js +39 -13
  56. package/dist/lib/geo/geospatial-metadata.js.map +1 -1
  57. package/dist/lib/parquet-bloom-filter-planner.d.ts +16 -0
  58. package/dist/lib/parquet-bloom-filter-planner.d.ts.map +1 -0
  59. package/dist/lib/parquet-bloom-filter-planner.js +57 -0
  60. package/dist/lib/parquet-bloom-filter-planner.js.map +1 -0
  61. package/dist/lib/parquet-bloom-filter.d.ts +30 -0
  62. package/dist/lib/parquet-bloom-filter.d.ts.map +1 -0
  63. package/dist/lib/parquet-bloom-filter.js +289 -0
  64. package/dist/lib/parquet-bloom-filter.js.map +1 -0
  65. package/dist/lib/parquet-encryption.d.ts +80 -0
  66. package/dist/lib/parquet-encryption.d.ts.map +1 -0
  67. package/dist/lib/parquet-encryption.js +190 -0
  68. package/dist/lib/parquet-encryption.js.map +1 -0
  69. package/dist/lib/parquet-page-index.d.ts +34 -5
  70. package/dist/lib/parquet-page-index.d.ts.map +1 -1
  71. package/dist/lib/parquet-page-index.js +114 -28
  72. package/dist/lib/parquet-page-index.js.map +1 -1
  73. package/dist/lib/parquet-predicate.d.ts +5 -1
  74. package/dist/lib/parquet-predicate.d.ts.map +1 -1
  75. package/dist/lib/parquet-predicate.js +42 -14
  76. package/dist/lib/parquet-predicate.js.map +1 -1
  77. package/dist/lib/parquet-source-worker-decoder.d.ts +4 -1
  78. package/dist/lib/parquet-source-worker-decoder.d.ts.map +1 -1
  79. package/dist/lib/parquet-source-worker-decoder.js +83 -17
  80. package/dist/lib/parquet-source-worker-decoder.js.map +1 -1
  81. package/dist/lib/parquet-source-worker-types.d.ts +24 -0
  82. package/dist/lib/parquet-source-worker-types.d.ts.map +1 -1
  83. package/dist/lib/parsers/get-parquet-schema.d.ts.map +1 -1
  84. package/dist/lib/parsers/get-parquet-schema.js +3 -1
  85. package/dist/lib/parsers/get-parquet-schema.js.map +1 -1
  86. package/dist/lib/parsers/parse-parquet-to-arrow-js.d.ts.map +1 -1
  87. package/dist/lib/parsers/parse-parquet-to-arrow-js.js +327 -18
  88. package/dist/lib/parsers/parse-parquet-to-arrow-js.js.map +1 -1
  89. package/dist/lib/parsers/parse-parquet-to-arrow.d.ts.map +1 -1
  90. package/dist/lib/parsers/parse-parquet-to-arrow.js +5 -5
  91. package/dist/lib/parsers/parse-parquet-to-arrow.js.map +1 -1
  92. package/dist/lib/parsers/parse-parquet-to-columns.d.ts.map +1 -1
  93. package/dist/lib/parsers/parse-parquet-to-columns.js +3 -1
  94. package/dist/lib/parsers/parse-parquet-to-columns.js.map +1 -1
  95. package/dist/lib/parsers/parse-parquet-to-json.d.ts.map +1 -1
  96. package/dist/lib/parsers/parse-parquet-to-json.js +18 -2
  97. package/dist/lib/parsers/parse-parquet-to-json.js.map +1 -1
  98. package/dist/lib/slice-parquet-batch.d.ts +9 -0
  99. package/dist/lib/slice-parquet-batch.d.ts.map +1 -0
  100. package/dist/lib/slice-parquet-batch.js +28 -0
  101. package/dist/lib/slice-parquet-batch.js.map +1 -0
  102. package/dist/lib/sources/parquet-range-file.d.ts +1 -1
  103. package/dist/lib/sources/parquet-range-file.d.ts.map +1 -1
  104. package/dist/lib/sources/parquet-range-file.js +28 -22
  105. package/dist/lib/sources/parquet-range-file.js.map +1 -1
  106. package/dist/lib/utils/normalize-parquet-options.d.ts.map +1 -1
  107. package/dist/lib/utils/normalize-parquet-options.js +15 -1
  108. package/dist/lib/utils/normalize-parquet-options.js.map +1 -1
  109. package/dist/parquet-dataset-source.cjs +15766 -4896
  110. package/dist/parquet-dataset-source.cjs.map +3 -3
  111. package/dist/parquet-dataset-source.d.ts +31 -4
  112. package/dist/parquet-dataset-source.d.ts.map +1 -1
  113. package/dist/parquet-dataset-source.js +296 -74
  114. package/dist/parquet-dataset-source.js.map +1 -1
  115. package/dist/parquet-js-loader-types.d.ts +2 -0
  116. package/dist/parquet-js-loader-types.d.ts.map +1 -1
  117. package/dist/parquet-js-loader-types.js +2 -1
  118. package/dist/parquet-js-loader-types.js.map +1 -1
  119. package/dist/parquet-js-loader.d.ts +1 -0
  120. package/dist/parquet-js-loader.d.ts.map +1 -1
  121. package/dist/parquet-js-loader.js +2 -1
  122. package/dist/parquet-js-loader.js.map +1 -1
  123. package/dist/parquet-js-writer.d.ts +23 -2
  124. package/dist/parquet-js-writer.d.ts.map +1 -1
  125. package/dist/parquet-js-writer.js +1 -1
  126. package/dist/parquet-js-writer.js.map +1 -1
  127. package/dist/parquet-loader-base.js +1 -1
  128. package/dist/parquet-loader-options.d.ts +11 -0
  129. package/dist/parquet-loader-options.d.ts.map +1 -1
  130. package/dist/parquet-loader-options.js.map +1 -1
  131. package/dist/parquet-source-capabilities.d.ts +8 -1
  132. package/dist/parquet-source-capabilities.d.ts.map +1 -1
  133. package/dist/parquet-source-capabilities.js +10 -0
  134. package/dist/parquet-source-capabilities.js.map +1 -1
  135. package/dist/parquet-source-loader-types.d.ts +10 -0
  136. package/dist/parquet-source-loader-types.d.ts.map +1 -1
  137. package/dist/parquet-source-loader-types.js +11 -1
  138. package/dist/parquet-source-loader-types.js.map +1 -1
  139. package/dist/parquet-source-loader.cjs +14911 -4316
  140. package/dist/parquet-source-loader.cjs.map +3 -3
  141. package/dist/parquet-source-loader.d.ts +42 -4
  142. package/dist/parquet-source-loader.d.ts.map +1 -1
  143. package/dist/parquet-source-loader.js +585 -38
  144. package/dist/parquet-source-loader.js.map +1 -1
  145. package/dist/parquet-source-types.d.ts +180 -30
  146. package/dist/parquet-source-types.d.ts.map +1 -1
  147. package/dist/parquet-source-worker.js +7055 -6989
  148. package/dist/parquet-source-worker.js.map +4 -4
  149. package/dist/parquet-worker-node.cjs +8505 -1041
  150. package/dist/parquet-worker-node.cjs.map +4 -4
  151. package/dist/parquet-worker-url.js +5 -1
  152. package/dist/parquet-worker-url.js.map +1 -1
  153. package/dist/parquet-worker.js +289 -218
  154. package/dist/parquet-worker.js.map +4 -4
  155. package/dist/parquetjs/codecs/declare.d.ts +15 -0
  156. package/dist/parquetjs/codecs/declare.d.ts.map +1 -1
  157. package/dist/parquetjs/codecs/declare.js +10 -0
  158. package/dist/parquetjs/codecs/declare.js.map +1 -1
  159. package/dist/parquetjs/codecs/delta.d.ts.map +1 -1
  160. package/dist/parquetjs/codecs/delta.js +356 -7
  161. package/dist/parquetjs/codecs/delta.js.map +1 -1
  162. package/dist/parquetjs/codecs/index.d.ts.map +1 -1
  163. package/dist/parquetjs/codecs/index.js +4 -0
  164. package/dist/parquetjs/codecs/index.js.map +1 -1
  165. package/dist/parquetjs/codecs/plain.d.ts +1 -1
  166. package/dist/parquetjs/codecs/plain.d.ts.map +1 -1
  167. package/dist/parquetjs/codecs/plain.js +153 -12
  168. package/dist/parquetjs/codecs/plain.js.map +1 -1
  169. package/dist/parquetjs/codecs/rle.d.ts +4 -0
  170. package/dist/parquetjs/codecs/rle.d.ts.map +1 -1
  171. package/dist/parquetjs/codecs/rle.js +406 -3
  172. package/dist/parquetjs/codecs/rle.js.map +1 -1
  173. package/dist/parquetjs/compression.d.ts +9 -0
  174. package/dist/parquetjs/compression.d.ts.map +1 -1
  175. package/dist/parquetjs/compression.js +86 -67
  176. package/dist/parquetjs/compression.js.map +1 -1
  177. package/dist/parquetjs/encoder/dictionary-planner.d.ts.map +1 -1
  178. package/dist/parquetjs/encoder/dictionary-planner.js +5 -2
  179. package/dist/parquetjs/encoder/dictionary-planner.js.map +1 -1
  180. package/dist/parquetjs/encoder/parquet-encoder.d.ts +43 -1
  181. package/dist/parquetjs/encoder/parquet-encoder.d.ts.map +1 -1
  182. package/dist/parquetjs/encoder/parquet-encoder.js +691 -34
  183. package/dist/parquetjs/encoder/parquet-encoder.js.map +1 -1
  184. package/dist/parquetjs/parquet-thrift/BoundingBox.d.ts +29 -0
  185. package/dist/parquetjs/parquet-thrift/BoundingBox.d.ts.map +1 -0
  186. package/dist/parquetjs/parquet-thrift/BoundingBox.js +85 -0
  187. package/dist/parquetjs/parquet-thrift/BoundingBox.js.map +1 -0
  188. package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts +5 -0
  189. package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts.map +1 -1
  190. package/dist/parquetjs/parquet-thrift/ColumnChunk.js +35 -0
  191. package/dist/parquetjs/parquet-thrift/ColumnChunk.js.map +1 -1
  192. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.d.ts +35 -0
  193. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.d.ts.map +1 -0
  194. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.js +135 -0
  195. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.js.map +1 -0
  196. package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts +10 -0
  197. package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts.map +1 -1
  198. package/dist/parquetjs/parquet-thrift/ColumnMetaData.js +73 -0
  199. package/dist/parquetjs/parquet-thrift/ColumnMetaData.js.map +1 -1
  200. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.d.ts +22 -0
  201. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.d.ts.map +1 -0
  202. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.js +111 -0
  203. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.js.map +1 -0
  204. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.d.ts +16 -0
  205. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.d.ts.map +1 -0
  206. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.js +57 -0
  207. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.js.map +1 -0
  208. package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts +5 -0
  209. package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts.map +1 -1
  210. package/dist/parquetjs/parquet-thrift/FileMetaData.js +35 -0
  211. package/dist/parquetjs/parquet-thrift/FileMetaData.js.map +1 -1
  212. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.d.ts +18 -0
  213. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.d.ts.map +1 -0
  214. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.js +62 -0
  215. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.js.map +1 -0
  216. package/dist/parquetjs/parquet-thrift/SizeStatistics.d.ts +26 -0
  217. package/dist/parquetjs/parquet-thrift/SizeStatistics.d.ts.map +1 -0
  218. package/dist/parquetjs/parquet-thrift/SizeStatistics.js +85 -0
  219. package/dist/parquetjs/parquet-thrift/SizeStatistics.js.map +1 -0
  220. package/dist/parquetjs/parquet-thrift/index.d.ts +6 -0
  221. package/dist/parquetjs/parquet-thrift/index.d.ts.map +1 -1
  222. package/dist/parquetjs/parquet-thrift/index.js +6 -0
  223. package/dist/parquetjs/parquet-thrift/index.js.map +1 -1
  224. package/dist/parquetjs/parser/decoders.d.ts +10 -1
  225. package/dist/parquetjs/parser/decoders.d.ts.map +1 -1
  226. package/dist/parquetjs/parser/decoders.js +318 -14
  227. package/dist/parquetjs/parser/decoders.js.map +1 -1
  228. package/dist/parquetjs/parser/parquet-reader.d.ts +78 -12
  229. package/dist/parquetjs/parser/parquet-reader.d.ts.map +1 -1
  230. package/dist/parquetjs/parser/parquet-reader.js +605 -66
  231. package/dist/parquetjs/parser/parquet-reader.js.map +1 -1
  232. package/dist/parquetjs/schema/declare.d.ts +22 -3
  233. package/dist/parquetjs/schema/declare.d.ts.map +1 -1
  234. package/dist/parquetjs/schema/declare.js.map +1 -1
  235. package/dist/parquetjs/schema/schema.d.ts.map +1 -1
  236. package/dist/parquetjs/schema/schema.js +10 -0
  237. package/dist/parquetjs/schema/schema.js.map +1 -1
  238. package/dist/parquetjs/schema/shred.d.ts.map +1 -1
  239. package/dist/parquetjs/schema/shred.js +219 -11
  240. package/dist/parquetjs/schema/shred.js.map +1 -1
  241. package/dist/parquetjs/schema/types.js +19 -21
  242. package/dist/parquetjs/schema/types.js.map +1 -1
  243. package/dist/parquetjs/schema/variant.d.ts +3 -0
  244. package/dist/parquetjs/schema/variant.d.ts.map +1 -0
  245. package/dist/parquetjs/schema/variant.js +248 -0
  246. package/dist/parquetjs/schema/variant.js.map +1 -0
  247. package/dist/parquetjs/utils/crc32.d.ts +3 -0
  248. package/dist/parquetjs/utils/crc32.d.ts.map +1 -0
  249. package/dist/parquetjs/utils/crc32.js +15 -0
  250. package/dist/parquetjs/utils/crc32.js.map +1 -0
  251. package/dist/parquetjs/utils/read-utils.d.ts +11 -1
  252. package/dist/parquetjs/utils/read-utils.d.ts.map +1 -1
  253. package/dist/parquetjs/utils/read-utils.js +21 -1
  254. package/dist/parquetjs/utils/read-utils.js.map +1 -1
  255. package/dist/unbundled.d.ts +2 -1
  256. package/dist/unbundled.d.ts.map +1 -1
  257. package/dist/unbundled.js +1 -0
  258. package/dist/unbundled.js.map +1 -1
  259. package/package.json +36 -11
  260. package/src/bundled.ts +3 -0
  261. package/src/delta-format.ts +12 -0
  262. package/src/delta-source-loader-types.ts +40 -0
  263. package/src/delta-source.ts +305 -0
  264. package/src/delta-types.ts +40 -0
  265. package/src/iceberg-rest-catalog.ts +122 -0
  266. package/src/iceberg-table-source.ts +1037 -0
  267. package/src/iceberg-types.ts +165 -0
  268. package/src/index.ts +50 -1
  269. package/src/lib/arrow/convert-schema-from-parquet.ts +113 -19
  270. package/src/lib/arrow/create-nested-arrow-vector.ts +116 -0
  271. package/src/lib/encoders/encode-table-to-parquet-js.ts +268 -7
  272. package/src/lib/geo/geoparquet-covering.ts +201 -0
  273. package/src/lib/geo/geospatial-metadata.ts +42 -15
  274. package/src/lib/parquet-bloom-filter-planner.ts +86 -0
  275. package/src/lib/parquet-bloom-filter.ts +345 -0
  276. package/src/lib/parquet-encryption.ts +344 -0
  277. package/src/lib/parquet-page-index.ts +180 -34
  278. package/src/lib/parquet-predicate.ts +52 -16
  279. package/src/lib/parquet-source-worker-decoder.ts +116 -20
  280. package/src/lib/parquet-source-worker-types.ts +25 -0
  281. package/src/lib/parsers/get-parquet-schema.ts +3 -1
  282. package/src/lib/parsers/parse-parquet-to-arrow-js.ts +397 -17
  283. package/src/lib/parsers/parse-parquet-to-arrow.ts +4 -5
  284. package/src/lib/parsers/parse-parquet-to-columns.ts +3 -1
  285. package/src/lib/parsers/parse-parquet-to-json.ts +18 -2
  286. package/src/lib/slice-parquet-batch.ts +32 -0
  287. package/src/lib/sources/parquet-range-file.ts +35 -29
  288. package/src/lib/utils/normalize-parquet-options.ts +17 -1
  289. package/src/parquet-dataset-source.ts +397 -85
  290. package/src/parquet-js-loader-types.ts +1 -0
  291. package/src/parquet-js-loader.ts +2 -1
  292. package/src/parquet-js-writer.ts +30 -0
  293. package/src/parquet-loader-options.ts +11 -0
  294. package/src/parquet-source-capabilities.ts +18 -1
  295. package/src/parquet-source-loader-types.ts +10 -0
  296. package/src/parquet-source-loader.ts +929 -52
  297. package/src/parquet-source-types.ts +197 -28
  298. package/src/parquet-worker-url.ts +6 -1
  299. package/src/parquetjs/codecs/declare.ts +27 -0
  300. package/src/parquetjs/codecs/delta.ts +466 -6
  301. package/src/parquetjs/codecs/index.ts +4 -0
  302. package/src/parquetjs/codecs/plain.ts +177 -11
  303. package/src/parquetjs/codecs/rle.ts +573 -3
  304. package/src/parquetjs/compression.ts +102 -76
  305. package/src/parquetjs/encoder/dictionary-planner.ts +7 -2
  306. package/src/parquetjs/encoder/parquet-encoder.ts +1016 -38
  307. package/src/parquetjs/parquet-thrift/BoundingBox.ts +108 -0
  308. package/src/parquetjs/parquet-thrift/ColumnChunk.ts +35 -0
  309. package/src/parquetjs/parquet-thrift/ColumnCryptoMetaData.ts +139 -0
  310. package/src/parquetjs/parquet-thrift/ColumnMetaData.ts +73 -0
  311. package/src/parquetjs/parquet-thrift/EncryptionAlgorithm.ts +127 -0
  312. package/src/parquetjs/parquet-thrift/FileCryptoMetaData.ts +60 -0
  313. package/src/parquetjs/parquet-thrift/FileMetaData.ts +35 -0
  314. package/src/parquetjs/parquet-thrift/GeospatialStatistics.ts +68 -0
  315. package/src/parquetjs/parquet-thrift/SizeStatistics.ts +98 -0
  316. package/src/parquetjs/parquet-thrift/index.ts +6 -0
  317. package/src/parquetjs/parser/decoders.ts +451 -26
  318. package/src/parquetjs/parser/parquet-reader.ts +935 -87
  319. package/src/parquetjs/schema/declare.ts +22 -2
  320. package/src/parquetjs/schema/schema.ts +10 -0
  321. package/src/parquetjs/schema/shred.ts +238 -11
  322. package/src/parquetjs/schema/types.ts +16 -25
  323. package/src/parquetjs/schema/variant.ts +313 -0
  324. package/src/parquetjs/utils/crc32.ts +15 -0
  325. package/src/parquetjs/utils/read-utils.ts +26 -1
  326. package/src/unbundled.ts +14 -0
@@ -32,7 +32,11 @@ import {
32
32
  } from '../parquetjs/utils/binary-utils';
33
33
  import {Uint8ArrayCompactProtocol} from '../parquetjs/utils/uint8-array-compact-protocol';
34
34
  import {Uint8ArrayTransport} from '../parquetjs/utils/uint8-array-transport';
35
- import {canParquetStatisticsMatch, getParquetPredicateColumns} from './parquet-predicate';
35
+ import {
36
+ canParquetStatisticsMatch,
37
+ getParquetPredicatePath,
38
+ getParquetPredicatePaths
39
+ } from './parquet-predicate';
36
40
 
37
41
  /** Half-open logical row range relative to one Parquet row group. */
38
42
  export type ParquetRowRange = {
@@ -73,7 +77,27 @@ export type ParquetPagePruningPlan = {
73
77
  prunedRowCount: number;
74
78
  };
75
79
 
76
- type ParquetPageStatistics = {
80
+ /** Decrypts an encrypted page-index module before Thrift decoding. */
81
+ export type ParquetPageIndexDecryptor = (
82
+ bytes: Uint8Array,
83
+ module: 'column-index' | 'offset-index',
84
+ rowGroupOrdinal: number,
85
+ columnOrdinal: number,
86
+ columnChunk: ColumnChunk
87
+ ) => Promise<Uint8Array>;
88
+
89
+ /** Optional controls for reading page indexes from a Parquet source. */
90
+ export type ParquetPagePruningOptions = {
91
+ /** Row-group ordinal used when constructing encrypted-module AAD. */
92
+ rowGroupOrdinal?: number;
93
+ /** Decode legacy INT96 page statistics as epoch nanoseconds. */
94
+ int96AsTimestamp?: boolean;
95
+ /** Decrypts encrypted index modules; omitted for plaintext files. */
96
+ decryptModule?: ParquetPageIndexDecryptor;
97
+ };
98
+
99
+ /** Logical statistics attached to one data page. */
100
+ export type ParquetPageStatistics = {
77
101
  min?: unknown;
78
102
  max?: unknown;
79
103
  nullCount?: number;
@@ -89,8 +113,10 @@ type ParquetColumnPageStatistics = {
89
113
  /**
90
114
  * Builds a conservative selective-page plan from Parquet column and offset indexes.
91
115
  *
92
- * Returns `undefined` when indexes or the current flat-column decoder cannot safely avoid full
93
- * column-chunk reads. An empty `rowRanges` array means the indexes prove the predicate cannot match.
116
+ * Returns `undefined` when indexes or the selected decoder cannot safely avoid full column-chunk
117
+ * reads. Repeated leaves are selected only when every decoded column uses the same page row
118
+ * boundaries, so ranges begin and end on complete logical rows. An empty `rowRanges` array means
119
+ * the indexes prove the predicate cannot match.
94
120
  */
95
121
  export async function createParquetPagePruningPlan(
96
122
  file: ReadableFile,
@@ -98,23 +124,27 @@ export async function createParquetPagePruningPlan(
98
124
  schema: ParquetSchema,
99
125
  selectedColumnPaths: readonly string[][],
100
126
  predicate: ParquetPredicate,
101
- signal?: AbortSignal
127
+ signal?: AbortSignal,
128
+ options?: ParquetPagePruningOptions
102
129
  ): Promise<ParquetPagePruningPlan | undefined> {
103
130
  const rowCount = Number(rowGroup.num_rows);
104
131
  const selectedColumnChunks = getSelectedColumnChunks(rowGroup, selectedColumnPaths);
105
- if (!isSafeFlatPageSelection(schema, selectedColumnChunks) || rowCount <= 0) {
132
+ if (!isSafePageSelection(schema, selectedColumnChunks) || rowCount <= 0) {
106
133
  return undefined;
107
134
  }
108
135
 
109
136
  const pageLocations: ParquetPageLocations = {};
110
137
  const pageStatistics: Record<string, ParquetColumnPageStatistics> = {};
111
- const predicateColumns = new Set(getParquetPredicateColumns(predicate));
138
+ const predicatePaths = new Set(
139
+ getParquetPredicatePaths(predicate).map(path => JSON.stringify(path))
140
+ );
112
141
  let indexCount = 0;
113
142
 
114
143
  await Promise.all(
115
144
  selectedColumnChunks.map(async columnChunk => {
145
+ const columnOrdinal = rowGroup.columns.indexOf(columnChunk);
116
146
  const path = columnChunk.meta_data!.path_in_schema;
117
- const pathKey = path.join('.');
147
+ const pathKey = JSON.stringify(path);
118
148
  const offsetIndexRange = getParquetIndexRange(
119
149
  columnChunk.offset_index_offset,
120
150
  columnChunk.offset_index_length,
@@ -130,14 +160,23 @@ export async function createParquetPagePruningPlan(
130
160
  );
131
161
  let pages: ParquetDataPageLocation[];
132
162
  try {
133
- pages = decodeOffsetIndex(toUint8Array(offsetIndexBytes), rowCount);
163
+ const decryptedBytes = options?.decryptModule
164
+ ? await options.decryptModule(
165
+ toUint8Array(offsetIndexBytes),
166
+ 'offset-index',
167
+ options.rowGroupOrdinal ?? 0,
168
+ columnOrdinal,
169
+ columnChunk
170
+ )
171
+ : toUint8Array(offsetIndexBytes);
172
+ pages = decodeOffsetIndex(decryptedBytes, rowCount);
134
173
  } catch {
135
174
  return;
136
175
  }
137
176
  pageLocations[pathKey] = pages;
138
177
  indexCount++;
139
178
 
140
- if (path.length !== 1 || !predicateColumns.has(path[0])) {
179
+ if (!predicatePaths.has(pathKey)) {
141
180
  return;
142
181
  }
143
182
  const columnIndexRange = getParquetIndexRange(
@@ -155,9 +194,18 @@ export async function createParquetPagePruningPlan(
155
194
  );
156
195
  const field = schema.findField(path);
157
196
  try {
158
- pageStatistics[path[0]] = {
197
+ const decryptedBytes = options?.decryptModule
198
+ ? await options.decryptModule(
199
+ toUint8Array(columnIndexBytes),
200
+ 'column-index',
201
+ options.rowGroupOrdinal ?? 0,
202
+ columnOrdinal,
203
+ columnChunk
204
+ )
205
+ : toUint8Array(columnIndexBytes);
206
+ pageStatistics[pathKey] = {
159
207
  pages,
160
- statistics: decodeColumnIndex(toUint8Array(columnIndexBytes), pages, field)
208
+ statistics: decodeParquetColumnIndex(decryptedBytes, pages, field, options)
161
209
  };
162
210
  } catch {
163
211
  return;
@@ -168,12 +216,16 @@ export async function createParquetPagePruningPlan(
168
216
 
169
217
  if (
170
218
  selectedColumnChunks.some(
171
- columnChunk => !pageLocations[columnChunk.meta_data!.path_in_schema.join('.')]
219
+ columnChunk => !pageLocations[JSON.stringify(columnChunk.meta_data!.path_in_schema)]
172
220
  )
173
221
  ) {
174
222
  return undefined;
175
223
  }
176
224
 
225
+ if (!hasCompatiblePageBoundaries(schema, selectedColumnChunks, pageLocations)) {
226
+ return undefined;
227
+ }
228
+
177
229
  const candidateRanges = getPredicateRowRanges(predicate, pageStatistics, rowCount);
178
230
  if (candidateRanges === undefined) {
179
231
  return undefined;
@@ -206,18 +258,21 @@ export async function createParquetPagePruningPlan(
206
258
  export function getParquetPageReadRanges(
207
259
  rowGroup: RowGroup,
208
260
  selectedColumnPaths: readonly string[][],
209
- plan: ParquetPagePruningPlan
261
+ plan: ParquetPagePruningPlan,
262
+ schema: ParquetSchema
210
263
  ): Array<{offset: number; length: number}> {
211
264
  const ranges: Array<{offset: number; length: number}> = [];
212
265
  for (const columnChunk of getSelectedColumnChunks(rowGroup, selectedColumnPaths)) {
213
266
  const columnMetadata = columnChunk.meta_data!;
214
- const pages = plan.pageLocations[columnMetadata.path_in_schema.join('.')];
267
+ const pages = plan.pageLocations[JSON.stringify(columnMetadata.path_in_schema)];
215
268
  const selectedPages = pages.filter(page =>
216
269
  plan.rowRanges.some(range => page.endRowIndex > range.start && page.firstRowIndex < range.end)
217
270
  );
218
271
  if (!selectedPages.length) {
219
272
  continue;
220
273
  }
274
+ const field = schema.findField(columnMetadata.path_in_schema);
275
+ const repeated = field.rLevelMax > 0 || field.repetitionType === 'REPEATED';
221
276
  const dictionaryPageOffset = Number(columnMetadata.dictionary_page_offset);
222
277
  if (Number.isSafeInteger(dictionaryPageOffset) && dictionaryPageOffset > 0) {
223
278
  ranges.push({
@@ -232,7 +287,9 @@ export function getParquetPageReadRanges(
232
287
  if (!overlappingPages.length) {
233
288
  continue;
234
289
  }
235
- const firstPage = overlappingPages[0];
290
+ // A repeated page may begin with a continuation level. Transfer the complete prefix so
291
+ // worker-backed readers can probe predecessor pages before decoding the selected range.
292
+ const firstPage = repeated ? pages[0] : overlappingPages[0];
236
293
  const lastPage = overlappingPages[overlappingPages.length - 1];
237
294
  ranges.push({
238
295
  offset: firstPage.offset,
@@ -253,10 +310,27 @@ export function decodeOffsetIndex(bytes: Uint8Array, rowCount: number): ParquetD
253
310
  ) {
254
311
  throw new Error('Invalid Parquet offset index page locations');
255
312
  }
313
+ let previousFirstRowIndex = -1;
314
+ for (const location of offsetIndex.page_locations) {
315
+ const firstRowIndex = Number(location.first_row_index);
316
+ if (!Number.isSafeInteger(firstRowIndex) || firstRowIndex < previousFirstRowIndex) {
317
+ throw new Error('Invalid Parquet offset index page locations');
318
+ }
319
+ previousFirstRowIndex = firstRowIndex;
320
+ }
256
321
  return offsetIndex.page_locations.map((location, index, locations) => {
257
322
  const firstRowIndex = Number(location.first_row_index);
258
- const endRowIndex =
259
- index + 1 < locations.length ? Number(locations[index + 1].first_row_index) : rowCount;
323
+ // A repeated row may continue on one or more pages. In that case the offset index
324
+ // legitimately repeats first_row_index. Extend each continuation page to the next
325
+ // strictly larger row start so selecting the row includes the complete continuation.
326
+ let endRowIndex = rowCount;
327
+ for (let nextIndex = index + 1; nextIndex < locations.length; nextIndex++) {
328
+ const nextFirstRowIndex = Number(locations[nextIndex].first_row_index);
329
+ if (nextFirstRowIndex > firstRowIndex) {
330
+ endRowIndex = nextFirstRowIndex;
331
+ break;
332
+ }
333
+ }
260
334
  const offset = Number(location.offset);
261
335
  const compressedByteLength = location.compressed_page_size;
262
336
  if (
@@ -308,7 +382,7 @@ function getPredicateRowRanges(
308
382
  | ParquetComparisonPredicate
309
383
  | ParquetInPredicate
310
384
  | ParquetNullPredicate;
311
- const column = columns[leafPredicate.args[0].property];
385
+ const column = columns[JSON.stringify(getParquetPredicatePath(leafPredicate.args[0]))];
312
386
  if (!column) {
313
387
  return undefined;
314
388
  }
@@ -328,11 +402,12 @@ function getPredicateRowRanges(
328
402
  return mergeRowRanges(ranges);
329
403
  }
330
404
 
331
- /** Decodes column-index values into the logical representation used by exact predicates. */
332
- function decodeColumnIndex(
405
+ /** Decodes column-index values into logical per-page statistics. */
406
+ export function decodeParquetColumnIndex(
333
407
  bytes: Uint8Array,
334
408
  pages: readonly ParquetDataPageLocation[],
335
- field: ParquetField
409
+ field: ParquetField,
410
+ options?: Pick<ParquetPagePruningOptions, 'int96AsTimestamp'>
336
411
  ): ParquetPageStatistics[] {
337
412
  const protocol = new Uint8ArrayCompactProtocol(new Uint8ArrayTransport(bytes));
338
413
  const columnIndex = ColumnIndex.read(protocol as any);
@@ -347,10 +422,10 @@ function decodeColumnIndex(
347
422
  return pages.map((_page, pageIndex) => ({
348
423
  min: columnIndex.null_pages[pageIndex]
349
424
  ? undefined
350
- : decodeParquetPageStatisticsValue(columnIndex.min_values[pageIndex], field),
425
+ : decodeParquetPageStatisticsValue(columnIndex.min_values[pageIndex], field, options),
351
426
  max: columnIndex.null_pages[pageIndex]
352
427
  ? undefined
353
- : decodeParquetPageStatisticsValue(columnIndex.max_values[pageIndex], field),
428
+ : decodeParquetPageStatisticsValue(columnIndex.max_values[pageIndex], field, options),
354
429
  nullCount:
355
430
  columnIndex.null_counts?.[pageIndex] !== undefined
356
431
  ? Number(columnIndex.null_counts[pageIndex])
@@ -363,7 +438,11 @@ function decodeColumnIndex(
363
438
  }
364
439
 
365
440
  /** Decodes one physical page-index statistic and applies its Parquet logical annotation. */
366
- export function decodeParquetPageStatisticsValue(bytes: Uint8Array, field: ParquetField): unknown {
441
+ export function decodeParquetPageStatisticsValue(
442
+ bytes: Uint8Array,
443
+ field: ParquetField,
444
+ options?: Pick<ParquetPagePruningOptions, 'int96AsTimestamp'>
445
+ ): unknown {
367
446
  let primitiveValue: unknown;
368
447
  switch (field.primitiveType) {
369
448
  case 'BOOLEAN':
@@ -384,6 +463,18 @@ export function decodeParquetPageStatisticsValue(bytes: Uint8Array, field: Parqu
384
463
  primitiveValue = readDoubleLE(bytes, 0);
385
464
  break;
386
465
  case 'INT96':
466
+ if (options?.int96AsTimestamp && bytes.byteLength >= 12) {
467
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
468
+ const nanosecondsOfDay = dataView.getBigUint64(0, true);
469
+ if (nanosecondsOfDay < 86_400_000_000_000n) {
470
+ primitiveValue =
471
+ (BigInt(dataView.getInt32(8, true)) - 2440588n) * 86_400_000_000_000n +
472
+ nanosecondsOfDay;
473
+ break;
474
+ }
475
+ }
476
+ primitiveValue = copyUint8Array(bytes);
477
+ break;
387
478
  case 'BYTE_ARRAY':
388
479
  case 'FIXED_LEN_BYTE_ARRAY':
389
480
  primitiveValue = copyUint8Array(bytes);
@@ -409,16 +500,13 @@ function getSelectedColumnChunks(
409
500
  });
410
501
  }
411
502
 
412
- /** Restricts selective page reads to independently materializable flat primitive columns. */
413
- function isSafeFlatPageSelection(
414
- schema: ParquetSchema,
415
- columnChunks: readonly ColumnChunk[]
416
- ): boolean {
503
+ /** Restricts selective page reads to primitive leaves with safe page boundaries. */
504
+ function isSafePageSelection(schema: ParquetSchema, columnChunks: readonly ColumnChunk[]): boolean {
417
505
  return (
418
506
  columnChunks.length > 0 &&
419
507
  columnChunks.every(columnChunk => {
420
508
  const path = columnChunk.meta_data?.path_in_schema;
421
- if (!path || path.length !== 1) {
509
+ if (!path) {
422
510
  return false;
423
511
  }
424
512
  const field = schema.findField(path);
@@ -427,9 +515,7 @@ function isSafeFlatPageSelection(
427
515
  );
428
516
  const dictionaryPageOffset = Number(columnChunk.meta_data!.dictionary_page_offset);
429
517
  return (
430
- field.path.length === 1 &&
431
- field.repetitionType !== 'REPEATED' &&
432
- field.rLevelMax === 0 &&
518
+ field.primitiveType !== undefined &&
433
519
  (!dictionaryEncoded ||
434
520
  (Number.isSafeInteger(dictionaryPageOffset) && dictionaryPageOffset > 0))
435
521
  );
@@ -437,6 +523,66 @@ function isSafeFlatPageSelection(
437
523
  );
438
524
  }
439
525
 
526
+ /**
527
+ * Returns whether a column can be decoded from selected page ranges without changing row shape.
528
+ *
529
+ * Repeated leaves are safe only when the selected columns share complete logical-row boundaries.
530
+ */
531
+ export function canUseParquetPageIndexForColumn(
532
+ schema: ParquetSchema,
533
+ columnChunk: ColumnChunk
534
+ ): boolean {
535
+ const path = columnChunk.meta_data?.path_in_schema;
536
+ if (!path) {
537
+ return false;
538
+ }
539
+ const field = schema.findField(path);
540
+ const dictionaryEncoded = columnChunk.meta_data!.encodings.some(
541
+ encoding => encoding === Encoding.PLAIN_DICTIONARY || encoding === Encoding.RLE_DICTIONARY
542
+ );
543
+ const dictionaryPageOffset = Number(columnChunk.meta_data!.dictionary_page_offset);
544
+ return (
545
+ field.primitiveType !== undefined &&
546
+ field.repetitionType !== 'REPEATED' &&
547
+ field.rLevelMax === 0 &&
548
+ (!dictionaryEncoded || (Number.isSafeInteger(dictionaryPageOffset) && dictionaryPageOffset > 0))
549
+ );
550
+ }
551
+
552
+ /** Ensures repeated selective reads keep every selected column on identical row boundaries. */
553
+ function hasCompatiblePageBoundaries(
554
+ schema: ParquetSchema,
555
+ columnChunks: readonly ColumnChunk[],
556
+ pageLocations: ParquetPageLocations
557
+ ): boolean {
558
+ const hasRepeatedLeaf = columnChunks.some(columnChunk => {
559
+ const field = schema.findField(columnChunk.meta_data!.path_in_schema);
560
+ return field.rLevelMax > 0 || field.repetitionType === 'REPEATED';
561
+ });
562
+ if (!hasRepeatedLeaf) {
563
+ return true;
564
+ }
565
+ // Offset indexes identify the first logical row represented by each page, but do not
566
+ // expose repetition levels. Equal starts therefore indicate a continuation page whose
567
+ // boundary cannot be proven safe for a selective read; retain the full-column fallback.
568
+ if (
569
+ columnChunks.some(columnChunk => {
570
+ const pages = pageLocations[JSON.stringify(columnChunk.meta_data!.path_in_schema)];
571
+ return pages.some(
572
+ (page, pageIndex) =>
573
+ pageIndex > 0 && page.firstRowIndex === pages[pageIndex - 1].firstRowIndex
574
+ );
575
+ })
576
+ ) {
577
+ return false;
578
+ }
579
+ const signatures = columnChunks.map(columnChunk => {
580
+ const pages = pageLocations[JSON.stringify(columnChunk.meta_data!.path_in_schema)];
581
+ return pages.map(page => `${page.firstRowIndex}:${page.endRowIndex}`).join('|');
582
+ });
583
+ return signatures.every(signature => signature === signatures[0]);
584
+ }
585
+
440
586
  /** Validates one optional footer index byte range against the containing file. */
441
587
  export function getParquetIndexRange(
442
588
  offsetValue: ColumnChunk['offset_index_offset'],
@@ -12,6 +12,7 @@ import type {
12
12
  ParquetNotPredicate,
13
13
  ParquetNullPredicate,
14
14
  ParquetPredicate,
15
+ ParquetPredicateProperty,
15
16
  ParquetPredicateValue,
16
17
  ParquetRowGroupMetadata
17
18
  } from '../parquet-source-types';
@@ -19,14 +20,20 @@ import {encodeUtf8} from '../parquetjs/utils/binary-utils';
19
20
 
20
21
  /** Returns the unique top-level columns referenced by a predicate. */
21
22
  export function getParquetPredicateColumns(predicate: ParquetPredicate): string[] {
22
- const columns = new Set<string>();
23
+ return [...new Set(getParquetPredicatePaths(predicate).map(path => path[0]))];
24
+ }
25
+
26
+ /** Returns the unique physical schema paths referenced by a predicate. */
27
+ export function getParquetPredicatePaths(predicate: ParquetPredicate): string[][] {
28
+ const paths = new Map<string, string[]>();
23
29
  visitParquetPredicate(predicate, child => {
24
30
  const property = getPredicateProperty(child);
25
31
  if (property) {
26
- columns.add(property.property);
32
+ const path = [...getParquetPredicatePath(property)];
33
+ paths.set(path.join('\0'), path);
27
34
  }
28
35
  });
29
- return [...columns];
36
+ return [...paths.values()];
30
37
  }
31
38
 
32
39
  /** Copies a predicate tree and mutable scalar values for read-scoped option snapshots. */
@@ -75,8 +82,12 @@ export function validateParquetPredicate(
75
82
  return;
76
83
  }
77
84
  const property = child.args[0];
78
- if (!availableColumns.has(property.property)) {
79
- throw new Error(`Parquet predicate column not found: ${property.property}`);
85
+ const path = getParquetPredicatePath(property);
86
+ if (!path.length || path.some(pathComponent => !pathComponent)) {
87
+ throw new Error('Parquet predicate property path must contain non-empty strings');
88
+ }
89
+ if (!availableColumns.has(path[0])) {
90
+ throw new Error(`Parquet predicate column not found: ${path.join('.')}`);
80
91
  }
81
92
  if (child.op === 'in' && child.args[1].length === 0) {
82
93
  throw new Error('Parquet predicate in requires at least one value');
@@ -99,11 +110,11 @@ export function canParquetRowGroupMatch(
99
110
  if (child.op !== 'isNull') {
100
111
  return true;
101
112
  }
102
- const column = getRowGroupColumn(rowGroup, child.args[0].property);
113
+ const column = getRowGroupColumn(rowGroup, getParquetPredicatePath(child.args[0]));
103
114
  return !column?.statistics || column.statistics.nullCount !== rowGroup.rowCount;
104
115
  }
105
116
 
106
- const column = getRowGroupColumn(rowGroup, predicate.args[0].property);
117
+ const column = getRowGroupColumn(rowGroup, getParquetPredicatePath(predicate.args[0]));
107
118
  const statistics = column?.statistics;
108
119
  if (!statistics) {
109
120
  return true;
@@ -160,7 +171,7 @@ export function gatherParquetColumns(
160
171
  if (selectedColumns && !selectedColumns.has(name)) {
161
172
  continue;
162
173
  }
163
- gatheredColumns[name] = rowIndices.map(rowIndex => getColumnValue(column, rowIndex));
174
+ gatheredColumns[name] = rowIndices.map(rowIndex => (column as ArrayLike<unknown>)[rowIndex]);
164
175
  }
165
176
  return gatheredColumns;
166
177
  }
@@ -182,7 +193,7 @@ function evaluateParquetPredicate(
182
193
  const result = evaluateParquetPredicate(predicate.args[0], columns, rowIndex);
183
194
  return result === null ? null : !result;
184
195
  }
185
- const value = getColumnValue(columns[predicate.args[0].property], rowIndex);
196
+ const value = getColumnValue(columns, getParquetPredicatePath(predicate.args[0]), rowIndex);
186
197
  if (predicate.op === 'isNull') {
187
198
  return value === null || value === undefined;
188
199
  }
@@ -311,11 +322,27 @@ function compareUint8Arrays(left: Uint8Array, right: Uint8Array): number {
311
322
  }
312
323
 
313
324
  /** Reads one value from an array-like materialized Parquet column. */
314
- function getColumnValue(column: ArrayType | undefined, rowIndex: number): unknown {
325
+ function getColumnValue(
326
+ columns: Record<string, ArrayType>,
327
+ path: readonly string[],
328
+ rowIndex: number
329
+ ): unknown {
330
+ const column = columns[path[0]];
315
331
  if (!column) {
316
332
  throw new Error('Parquet predicate column was not decoded');
317
333
  }
318
- return (column as ArrayLike<unknown>)[rowIndex];
334
+ let value = (column as ArrayLike<unknown>)[rowIndex];
335
+ for (let pathIndex = 1; pathIndex < path.length; pathIndex++) {
336
+ if (value === null || value === undefined || typeof value !== 'object') {
337
+ return undefined;
338
+ }
339
+ const property = path[pathIndex];
340
+ value =
341
+ typeof (value as {get?: unknown}).get === 'function'
342
+ ? (value as {get: (name: string) => unknown}).get(property)
343
+ : (value as Record<string, unknown>)[property];
344
+ }
345
+ return value;
319
346
  }
320
347
 
321
348
  /** Copies mutable binary and date predicate values. */
@@ -330,21 +357,30 @@ function copyPredicateValue(value: ParquetPredicateValue): ParquetPredicateValue
330
357
  }
331
358
 
332
359
  /** Copies a predicate property reference. */
333
- function copyPredicateProperty(property: {property: string}): {property: string} {
334
- return {property: property.property};
360
+ function copyPredicateProperty(property: ParquetPredicateProperty): ParquetPredicateProperty {
361
+ return {
362
+ property: Array.isArray(property.property) ? [...property.property] : property.property
363
+ };
364
+ }
365
+
366
+ /** Returns one predicate property as an explicit Parquet schema path. */
367
+ export function getParquetPredicatePath(property: ParquetPredicateProperty): readonly string[] {
368
+ return Array.isArray(property.property) ? property.property : [property.property as string];
335
369
  }
336
370
 
337
371
  /** Returns the property reference from a leaf predicate. */
338
- function getPredicateProperty(predicate: ParquetPredicate): {property: string} | undefined {
372
+ function getPredicateProperty(predicate: ParquetPredicate): ParquetPredicateProperty | undefined {
339
373
  return isParquetLogicalPredicate(predicate) || isParquetNotPredicate(predicate)
340
374
  ? undefined
341
375
  : predicate.args[0];
342
376
  }
343
377
 
344
378
  /** Returns one top-level column chunk from normalized row-group metadata. */
345
- function getRowGroupColumn(rowGroup: ParquetRowGroupMetadata, property: string) {
379
+ function getRowGroupColumn(rowGroup: ParquetRowGroupMetadata, path: readonly string[]) {
346
380
  return rowGroup.columns.find(
347
- columnChunk => columnChunk.path.length === 1 && columnChunk.path[0] === property
381
+ columnChunk =>
382
+ columnChunk.path.length === path.length &&
383
+ columnChunk.path.every((pathComponent, index) => pathComponent === path[index])
348
384
  );
349
385
  }
350
386