@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
@@ -1,17 +1,22 @@
1
1
  // loaders.gl
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
- import { hydrateArrowTable } from '@loaders.gl/arrow';
5
- import { BlobFile, DataSource, isBrowser } from '@loaders.gl/loader-utils';
4
+ import { dehydrateArrowTable, hydrateArrowTable, IndexedArrowTable } from '@loaders.gl/arrow';
5
+ import { BlobFile, DataSource, createScanQueryMetadata, emitScanExecutionTelemetry, explainTableQuery, isBrowser, makeTableScanBatch, validateTableQueryLimit } from '@loaders.gl/loader-utils';
6
6
  import { convertTable } from '@loaders.gl/schema-utils';
7
7
  import { getSchemaFromParquetReader } from "./lib/parsers/get-parquet-schema.js";
8
+ import { canGeoParquetRowGroupMatch, combineParquetPredicates, createGeoParquetBoundingBoxPredicate } from "./lib/geo/geoparquet-covering.js";
9
+ import { decodeParquetSplitBlockBloomFilter, encodeParquetBloomFilterValue, hashParquetBloomFilterValue, checkParquetSplitBlockBloomFilter } from "./lib/parquet-bloom-filter.js";
10
+ import { getParquetBloomFilterProbes } from "./lib/parquet-bloom-filter-planner.js";
8
11
  import { canParquetRowGroupMatch, copyParquetPredicate, filterParquetRowIndices, gatherParquetColumns, getParquetPredicateColumns, validateParquetPredicate } from "./lib/parquet-predicate.js";
9
12
  import { createParquetPagePruningPlan, getParquetPageReadRanges } from "./lib/parquet-page-index.js";
10
13
  import { canDecodeParquetSourceOnWorker, decodeParquetSourceRowGroupOnWorker } from "./lib/parquet-source-worker-client.js";
14
+ import { sliceParquetBatch } from "./lib/slice-parquet-batch.js";
11
15
  import { ParquetRangeFile } from "./lib/sources/parquet-range-file.js";
12
- import { PARQUET_SOURCE_CAPABILITIES } from "./parquet-source-capabilities.js";
16
+ import { toPrimitive } from "./parquetjs/schema/types.js";
17
+ import { PARQUET_TABLE_QUERY_CAPABILITIES, PARQUET_SOURCE_CAPABILITIES } from "./parquet-source-capabilities.js";
13
18
  import { ParquetSourceLoader as ParquetSourceLoaderMetadata } from "./parquet-source-loader-types.js";
14
- export { PARQUET_SOURCE_CAPABILITIES } from "./parquet-source-capabilities.js";
19
+ export { PARQUET_TABLE_QUERY_CAPABILITIES, PARQUET_SOURCE_CAPABILITIES } from "./parquet-source-capabilities.js";
15
20
  import { preloadCompressions } from "./parquetjs/compression.js";
16
21
  import { CompressionCodec, Encoding } from "./parquetjs/parquet-thrift/index.js";
17
22
  import { ParquetReader } from "./parquetjs/parser/parquet-reader.js";
@@ -28,6 +33,8 @@ export const ParquetSourceLoaderWithParser = {
28
33
  export { ParquetSourceLoaderWithParser as ParquetSourceLoader };
29
34
  /** Reusable Parquet source that caches footer/schema state and selectively reads byte ranges. */
30
35
  export class ParquetSource extends DataSource {
36
+ /** Common projection, predicate, limit, streaming, and cancellation capabilities. */
37
+ tableQueryCapabilities = PARQUET_TABLE_QUERY_CAPABILITIES;
31
38
  /** Immutable feature support for the current range-backed source implementation. */
32
39
  capabilities = PARQUET_SOURCE_CAPABILITIES;
33
40
  /** Shared initialization for this source instance. */
@@ -66,39 +73,118 @@ export class ParquetSource extends DataSource {
66
73
  formatSpecificMetadata: initialization.fileMetadata
67
74
  };
68
75
  }
76
+ /** Discovers schema, capabilities, and footer statistics without decoding Parquet data pages. */
77
+ async getQueryMetadata(options = {}) {
78
+ const [schema, metadata] = await Promise.all([
79
+ this.getSchema({ signal: options.signal }),
80
+ this.getMetadata({ signal: options.signal })
81
+ ]);
82
+ return createScanQueryMetadata({
83
+ sourceType: 'parquet',
84
+ queryType: 'table',
85
+ execution: { status: 'supported', method: 'read' },
86
+ name: metadata.name,
87
+ schema,
88
+ capabilities: { table: this.tableQueryCapabilities },
89
+ columnRoles: getParquetColumnRoles(schema.fields.map(field => field.name)),
90
+ statistics: { rowCount: metadata.rowCount, byteLength: metadata.fileByteLength }
91
+ });
92
+ }
93
+ /** Plans a portable query using Parquet metadata without decoding data pages. */
94
+ async getScanPlan(options = {}) {
95
+ const readOptions = this.getReadOptions(options);
96
+ const initialization = await this.getInitialization(readOptions.signal);
97
+ const sourceColumnNames = initialization.schema.fields.map(field => field.name);
98
+ const signal = readOptions.signal ?? new AbortController().signal;
99
+ const physicalPlan = await this.planRowGroups(initialization, readOptions, signal);
100
+ const pagePlans = await this.planPages(initialization, readOptions, physicalPlan, signal);
101
+ const explanation = explainTableQuery(sourceColumnNames, {
102
+ columns: readOptions.columns,
103
+ predicate: physicalPlan.predicate,
104
+ limit: readOptions.limit
105
+ }, PARQUET_TABLE_QUERY_CAPABILITIES);
106
+ return Object.freeze({
107
+ ...explanation,
108
+ source: 'parquet',
109
+ rowGroups: Object.freeze({
110
+ indices: Object.freeze([...physicalPlan.rowGroupIndices]),
111
+ requested: physicalPlan.requested,
112
+ selected: physicalPlan.rowGroupIndices.length,
113
+ prunedByCallback: physicalPlan.prunedByCallback,
114
+ prunedBySpatial: physicalPlan.prunedBySpatial,
115
+ prunedByStatistics: physicalPlan.prunedByStatistics,
116
+ prunedByBloomFilter: physicalPlan.prunedByBloomFilter
117
+ }),
118
+ bloomFilters: Object.freeze({
119
+ read: physicalPlan.bloomFiltersRead,
120
+ bytesRead: physicalPlan.bloomFilterBytesRead
121
+ }),
122
+ pages: Object.freeze({
123
+ rowGroupsPlanned: new Set(pagePlans.map(plan => plan.rowGroupIndex)).size,
124
+ indexesRead: pagePlans.reduce((sum, plan) => sum + plan.indexesRead, 0),
125
+ total: pagePlans.reduce((sum, plan) => sum + plan.totalPages, 0),
126
+ selected: pagePlans.reduce((sum, plan) => sum + plan.selectedPages, 0),
127
+ rowsPruned: pagePlans.reduce((sum, plan) => sum + plan.rowsPruned, 0),
128
+ plans: Object.freeze(pagePlans)
129
+ })
130
+ });
131
+ }
132
+ /** Explains the common logical query and Parquet-specific physical scan plan. */
133
+ async explain(options = {}) {
134
+ return await this.getScanPlan(options);
135
+ }
136
+ /** Executes a previously computed physical plan while preserving its row-group decisions. */
137
+ async *executeScanPlan(plan, options = {}) {
138
+ if (plan.source !== 'parquet') {
139
+ throw new Error('ParquetSource can only execute Parquet scan plans');
140
+ }
141
+ const predicateStep = plan.plan.find(step => step.kind === 'filter');
142
+ const limitStep = plan.plan.find(step => step.kind === 'limit');
143
+ yield* this.read({
144
+ ...options,
145
+ columns: options.columns ?? plan.outputColumns,
146
+ predicate: options.predicate ??
147
+ (predicateStep?.kind === 'filter' ? predicateStep.predicate : undefined),
148
+ limit: options.limit ?? (limitStep?.kind === 'limit' ? limitStep.limit : undefined),
149
+ rowGroups: plan.rowGroups.indices
150
+ });
151
+ }
152
+ /** Common scan-architecture alias for selective Parquet reads. */
153
+ scan(options = {}) {
154
+ return this.read(options);
155
+ }
69
156
  /** Returns a copy of cumulative transport, decode, conversion, and pruning telemetry. */
70
157
  getTelemetry() {
71
158
  return Object.freeze({ ...this.telemetry });
72
159
  }
73
160
  /** Selectively fetches row groups and columns as ordered Arrow batches with source provenance. */
74
161
  async *read(options = {}) {
162
+ const startedAt = Date.now();
163
+ const telemetryBefore = this.getTelemetry();
75
164
  const readOptions = this.getReadOptions(options);
165
+ if (readOptions.limit === 0) {
166
+ emitScanExecutionTelemetry(options.onTelemetry, createParquetScanExecutionTelemetry(telemetryBefore, this.getTelemetry(), startedAt, 'early-terminated', 'limit'));
167
+ return;
168
+ }
76
169
  const readContext = createReadAbortContext(readOptions.signal);
77
170
  const inFlightReads = new Set();
78
171
  let completed = false;
172
+ let limitReached = false;
79
173
  let readError;
80
174
  this.activeReadControllers.add(readContext.abortController);
81
175
  try {
82
176
  const initialization = await this.getInitialization(readContext.abortController.signal);
83
177
  await this.getCompressionInitialization();
84
178
  throwIfAborted(readContext.abortController.signal);
85
- const candidateRowGroupIndices = normalizeRowGroupIndices(readOptions.rowGroups, initialization.fileMetadata.row_groups.length);
86
- const callbackFilteredRowGroupIndices = readOptions.rowGroupFilter
87
- ? candidateRowGroupIndices.filter(rowGroupIndex => readOptions.rowGroupFilter(initialization.metadata.rowGroups[rowGroupIndex]))
88
- : candidateRowGroupIndices;
89
- const predicate = readOptions.predicate;
90
- const availableColumns = new Set(initialization.schema.fields.map(field => field.name));
91
- if (predicate) {
92
- validateParquetPredicate(predicate, availableColumns);
93
- }
94
- const rowGroupIndices = predicate
95
- ? callbackFilteredRowGroupIndices.filter(rowGroupIndex => canParquetRowGroupMatch(predicate, initialization.metadata.rowGroups[rowGroupIndex]))
96
- : callbackFilteredRowGroupIndices;
97
- const rowGroupsPrunedByStatistics = callbackFilteredRowGroupIndices.length - rowGroupIndices.length;
179
+ const physicalPlan = await this.planRowGroups(initialization, readOptions, readContext.abortController.signal);
180
+ const { predicate, rowGroupIndices } = physicalPlan;
98
181
  this.recordTelemetry('row-group-prune', {
99
- rowGroupsRequested: candidateRowGroupIndices.length,
100
- rowGroupsPruned: candidateRowGroupIndices.length - rowGroupIndices.length,
101
- rowGroupsPrunedByStatistics
182
+ rowGroupsRequested: physicalPlan.requested,
183
+ rowGroupsPruned: physicalPlan.requested - rowGroupIndices.length,
184
+ rowGroupsPrunedByStatistics: physicalPlan.prunedByStatistics,
185
+ rowGroupsPrunedByBloomFilter: physicalPlan.prunedByBloomFilter,
186
+ bloomFiltersRead: physicalPlan.bloomFiltersRead,
187
+ bloomFilterBytesRead: physicalPlan.bloomFilterBytesRead
102
188
  }, {});
103
189
  const columns = normalizeColumns(readOptions.columns, initialization.schema);
104
190
  const predicateColumns = predicate ? getParquetPredicateColumns(predicate) : [];
@@ -109,9 +195,11 @@ export class ParquetSource extends DataSource {
109
195
  const projectedSchema = projectSchema(initialization.schema, columns);
110
196
  const projectedColumnNames = columns.length ? new Set(columns) : undefined;
111
197
  const workerOptions = this.getWorkerOptions(concurrency, readContext.abortController.signal);
112
- const decodeOnWorker = canDecodeParquetSourceOnWorker(workerOptions);
198
+ const decodeOnWorker = canDecodeParquetSourceOnWorker(workerOptions) &&
199
+ (!initialization.reader.encrypted || Boolean(this.options.parquet?.keyRetriever));
113
200
  const scheduledReads = new Map();
114
201
  let nextPositionToSchedule = 0;
202
+ let remainingRows = readOptions.limit ?? Number.POSITIVE_INFINITY;
115
203
  const scheduleReads = () => {
116
204
  while (nextPositionToSchedule < rowGroupIndices.length &&
117
205
  scheduledReads.size < concurrency) {
@@ -139,9 +227,15 @@ export class ParquetSource extends DataSource {
139
227
  const arrowTable = hydrateArrowTable(workerBatch.arrowTable);
140
228
  const deserializationDurationMs = getCurrentTime() - deserializationStartTime;
141
229
  this.recordTelemetry('arrow-conversion', { arrowConversionDurationMs: deserializationDurationMs }, { rowGroupIndex, durationMs: deserializationDurationMs });
142
- const batch = createParquetBatchFromArrow(initialization.metadata, projectedSchema, rowGroupIndex, workerBatch.rowGroupRowOffset, arrowTable, workerBatch.rowCount, workerBatch.rowGroupRowIndices);
143
- this.recordTelemetry('batch', { batchesEmitted: 1, rowsEmitted: workerBatch.rowCount }, { rowGroupIndex, rowCount: workerBatch.rowCount });
230
+ const batch = sliceParquetBatch(createParquetBatchFromArrow(initialization.metadata, projectedSchema, rowGroupIndex, workerBatch.rowGroupRowOffset, arrowTable, workerBatch.rowCount, workerBatch.rowGroupRowIndices), Math.min(workerBatch.rowCount, remainingRows));
231
+ this.recordTelemetry('batch', { batchesEmitted: 1, rowsEmitted: batch.length }, { rowGroupIndex, rowCount: batch.length });
144
232
  yield batch;
233
+ remainingRows -= batch.length;
234
+ if (remainingRows === 0) {
235
+ limitReached = true;
236
+ completed = true;
237
+ return;
238
+ }
145
239
  }
146
240
  continue;
147
241
  }
@@ -149,7 +243,7 @@ export class ParquetSource extends DataSource {
149
243
  const outputBatchSize = batchSize || Math.max(outputRowCount, 1);
150
244
  for (let outputRowOffset = 0; outputRowOffset < outputRowCount; outputRowOffset += outputBatchSize) {
151
245
  throwIfAborted(readContext.abortController.signal);
152
- const batchRowCount = Math.min(outputBatchSize, outputRowCount - outputRowOffset);
246
+ const batchRowCount = Math.min(outputBatchSize, outputRowCount - outputRowOffset, remainingRows);
153
247
  const batchRowIndices = rowIndices
154
248
  ? rowIndices.slice(outputRowOffset, outputRowOffset + batchRowCount)
155
249
  : undefined;
@@ -161,6 +255,12 @@ export class ParquetSource extends DataSource {
161
255
  this.recordTelemetry('arrow-conversion', { arrowConversionDurationMs: conversionDurationMs }, { rowGroupIndex, durationMs: conversionDurationMs });
162
256
  this.recordTelemetry('batch', { batchesEmitted: 1, rowsEmitted: batchRowCount }, { rowGroupIndex, rowCount: batchRowCount });
163
257
  yield batch;
258
+ remainingRows -= batch.length;
259
+ if (remainingRows === 0) {
260
+ limitReached = true;
261
+ completed = true;
262
+ return;
263
+ }
164
264
  }
165
265
  }
166
266
  completed = true;
@@ -179,12 +279,125 @@ export class ParquetSource extends DataSource {
179
279
  if (!completed && readError === undefined) {
180
280
  this.recordTelemetry('cancel', { cancellationCount: 1 }, {});
181
281
  }
282
+ const cancelled = readContext.abortController.signal.aborted || readOptions.signal?.aborted;
182
283
  readContext.abortController.abort();
183
284
  readContext.removeSignalListener();
184
285
  this.activeReadControllers.delete(readContext.abortController);
185
286
  await Promise.allSettled([...inFlightReads]);
287
+ const status = readError
288
+ ? cancelled
289
+ ? 'cancelled'
290
+ : 'failed'
291
+ : limitReached || !completed
292
+ ? 'early-terminated'
293
+ : 'completed';
294
+ emitScanExecutionTelemetry(options.onTelemetry, createParquetScanExecutionTelemetry(telemetryBefore, this.getTelemetry(), startedAt, status, limitReached
295
+ ? 'limit'
296
+ : !completed && readError === undefined
297
+ ? 'consumer-return'
298
+ : undefined, readError));
186
299
  }
187
300
  }
301
+ /** Produces the physical row-group plan shared by explain and execution. */
302
+ async planRowGroups(initialization, readOptions, signal) {
303
+ throwIfAborted(signal);
304
+ const candidateRowGroupIndices = normalizeRowGroupIndices(readOptions.rowGroups, initialization.fileMetadata.row_groups.length);
305
+ const callbackFilteredRowGroupIndices = readOptions.rowGroupFilter
306
+ ? candidateRowGroupIndices.filter(rowGroupIndex => readOptions.rowGroupFilter(initialization.metadata.rowGroups[rowGroupIndex]))
307
+ : candidateRowGroupIndices;
308
+ const spatiallyFilteredRowGroupIndices = readOptions.bbox
309
+ ? callbackFilteredRowGroupIndices.filter(rowGroupIndex => canGeoParquetRowGroupMatch(initialization.metadata, initialization.metadata.rowGroups[rowGroupIndex], readOptions.bbox, readOptions.geometryColumn))
310
+ : callbackFilteredRowGroupIndices;
311
+ const spatialPredicate = readOptions.bbox
312
+ ? createGeoParquetBoundingBoxPredicate(initialization.metadata, readOptions.bbox, readOptions.geometryColumn)
313
+ : undefined;
314
+ const predicate = combineParquetPredicates(readOptions.predicate, spatialPredicate);
315
+ if (predicate) {
316
+ validateParquetPredicate(predicate, new Set(initialization.schema.fields.map(field => field.name)));
317
+ }
318
+ const statisticsRowGroupIndices = predicate
319
+ ? spatiallyFilteredRowGroupIndices.filter(rowGroupIndex => canParquetRowGroupMatch(predicate, initialization.metadata.rowGroups[rowGroupIndex]))
320
+ : spatiallyFilteredRowGroupIndices;
321
+ const bloomFilterResult = predicate
322
+ ? await filterParquetRowGroupsWithBloomFilters(initialization, statisticsRowGroupIndices, predicate, signal)
323
+ : { rowGroupIndices: statisticsRowGroupIndices, filtersRead: 0, bytesRead: 0 };
324
+ return {
325
+ predicate,
326
+ rowGroupIndices: bloomFilterResult.rowGroupIndices,
327
+ requested: candidateRowGroupIndices.length,
328
+ prunedByCallback: candidateRowGroupIndices.length - callbackFilteredRowGroupIndices.length,
329
+ prunedBySpatial: callbackFilteredRowGroupIndices.length - spatiallyFilteredRowGroupIndices.length,
330
+ prunedByStatistics: spatiallyFilteredRowGroupIndices.length - statisticsRowGroupIndices.length,
331
+ prunedByBloomFilter: statisticsRowGroupIndices.length - bloomFilterResult.rowGroupIndices.length,
332
+ bloomFiltersRead: bloomFilterResult.filtersRead,
333
+ bloomFilterBytesRead: bloomFilterResult.bytesRead
334
+ };
335
+ }
336
+ /** Produces explainable page-index and byte-range plans for retained row groups. */
337
+ async planPages(initialization, readOptions, rowGroupPlan, signal) {
338
+ if (!rowGroupPlan.predicate)
339
+ return [];
340
+ const columns = normalizeColumns(readOptions.columns, initialization.schema);
341
+ const predicateColumns = getParquetPredicateColumns(rowGroupPlan.predicate);
342
+ const decodedColumns = columns.length === 0 ? [] : [...new Set([...columns, ...predicateColumns])];
343
+ const hasHiddenPredicateColumns = columns.length > 0 && predicateColumns.some(column => !columns.includes(column));
344
+ const phases = hasHiddenPredicateColumns
345
+ ? [
346
+ {
347
+ phase: 'predicate',
348
+ columns: predicateColumns.map(column => [column]),
349
+ predicate: rowGroupPlan.predicate
350
+ },
351
+ {
352
+ phase: 'projection',
353
+ columns: columns.map(column => [column]),
354
+ predicate: undefined
355
+ }
356
+ ]
357
+ : [
358
+ {
359
+ phase: 'combined',
360
+ columns: decodedColumns.map(column => [column]),
361
+ predicate: rowGroupPlan.predicate
362
+ }
363
+ ];
364
+ const plans = [];
365
+ for (const rowGroupIndex of rowGroupPlan.rowGroupIndices) {
366
+ const rowGroup = initialization.fileMetadata.row_groups[rowGroupIndex];
367
+ let predicateProvedEmpty = false;
368
+ for (const phase of phases) {
369
+ throwIfAborted(signal);
370
+ if (phase.phase === 'projection' && predicateProvedEmpty) {
371
+ continue;
372
+ }
373
+ await initialization.reader.resolveColumnMetadata(rowGroup, rowGroupIndex, phase.columns);
374
+ const pagePlan = phase.predicate
375
+ ? await createParquetPagePruningPlan(initialization.file, rowGroup, initialization.parquetSchema, phase.columns, phase.predicate, signal, {
376
+ rowGroupOrdinal: rowGroupIndex,
377
+ int96AsTimestamp: Boolean(this.options.parquet?.int96AsTimestamp),
378
+ decryptModule: (bytes, module, rowGroupOrdinal, columnOrdinal, columnChunk) => initialization.reader.decryptIndexModule(bytes, module, rowGroupOrdinal, columnOrdinal, columnChunk)
379
+ })
380
+ : undefined;
381
+ plans.push(pagePlan
382
+ ? Object.freeze({
383
+ rowGroupIndex,
384
+ phase: phase.phase,
385
+ columns: Object.freeze(phase.columns.map(column => Object.freeze([...column]))),
386
+ rowRanges: Object.freeze(pagePlan.rowRanges.map(rowRange => Object.freeze({ ...rowRange }))),
387
+ indexesRead: pagePlan.indexCount,
388
+ totalPages: pagePlan.totalPageCount,
389
+ selectedPages: pagePlan.selectedPageCount,
390
+ rowsPruned: pagePlan.prunedRowCount,
391
+ ranges: Object.freeze(getParquetPageReadRanges(rowGroup, phase.columns, pagePlan, initialization.parquetSchema).map(range => Object.freeze({ ...range })))
392
+ })
393
+ : createFullColumnScanPlan(rowGroup, rowGroupIndex, phase.phase, phase.columns));
394
+ if (phase.phase === 'predicate' && pagePlan?.rowRanges.length === 0) {
395
+ predicateProvedEmpty = true;
396
+ }
397
+ }
398
+ }
399
+ return plans;
400
+ }
188
401
  /** Closes the underlying readable file and aborts active remote requests. */
189
402
  async close() {
190
403
  if (this.closed) {
@@ -196,6 +409,7 @@ export class ParquetSource extends DataSource {
196
409
  }
197
410
  await this.readableFile?.close();
198
411
  const initialization = await this.initializationPromise?.catch(() => null);
412
+ initialization?.reader.close();
199
413
  if (initialization?.file !== this.readableFile) {
200
414
  await initialization?.file.close();
201
415
  }
@@ -230,11 +444,17 @@ export class ParquetSource extends DataSource {
230
444
  const rowGroups = options.rowGroups ?? this.options.parquet?.rowGroups;
231
445
  const columns = options.columns ?? this.options.parquet?.columns;
232
446
  const predicate = options.predicate ?? this.options.parquet?.predicate;
447
+ const bbox = options.bbox ?? this.options.parquet?.bbox;
448
+ const limit = options.limit ?? this.options.parquet?.limit;
449
+ validateTableQueryLimit(limit);
233
450
  return {
234
451
  rowGroups: rowGroups && [...rowGroups],
235
452
  columns: columns && [...columns],
236
453
  rowGroupFilter: options.rowGroupFilter ?? this.options.parquet?.rowGroupFilter,
237
454
  predicate: predicate ? copyParquetPredicate(predicate) : undefined,
455
+ limit,
456
+ bbox: bbox ? [...bbox] : undefined,
457
+ geometryColumn: options.geometryColumn ?? this.options.parquet?.geometryColumn,
238
458
  batchSize: options.batchSize ?? this.options.parquet?.batchSize,
239
459
  concurrency: options.concurrency ?? this.options.parquet?.concurrency,
240
460
  signal: options.signal
@@ -260,9 +480,19 @@ export class ParquetSource extends DataSource {
260
480
  }
261
481
  /** Fetches and decodes one selected row group using the cached footer and schema. */
262
482
  async readRowGroup(initialization, rowGroupIndex, columnList, projectedSchema, batchSize, predicate, projectedColumnNames, workerOptions, signal) {
483
+ if (predicate &&
484
+ projectedColumnNames &&
485
+ getParquetPredicateColumns(predicate).some(column => !projectedColumnNames.has(column))) {
486
+ return await this.readRowGroupWithLateMaterialization(initialization, rowGroupIndex, columnList, projectedSchema, batchSize, predicate, projectedColumnNames, workerOptions, signal);
487
+ }
263
488
  const rowGroup = initialization.fileMetadata.row_groups[rowGroupIndex];
489
+ await initialization.reader.resolveColumnMetadata(rowGroup, rowGroupIndex, columnList);
264
490
  const pagePlan = predicate
265
- ? await createParquetPagePruningPlan(initialization.file, rowGroup, initialization.parquetSchema, columnList, predicate, signal)
491
+ ? await createParquetPagePruningPlan(initialization.file, rowGroup, initialization.parquetSchema, columnList, predicate, signal, {
492
+ rowGroupOrdinal: rowGroupIndex,
493
+ int96AsTimestamp: Boolean(this.options.parquet?.int96AsTimestamp),
494
+ decryptModule: (bytes, module, rowGroupOrdinal, columnOrdinal, columnChunk) => initialization.reader.decryptIndexModule(bytes, module, rowGroupOrdinal, columnOrdinal, columnChunk)
495
+ })
266
496
  : undefined;
267
497
  if (pagePlan) {
268
498
  this.recordPagePruningTelemetry(rowGroupIndex, pagePlan);
@@ -275,9 +505,9 @@ export class ParquetSource extends DataSource {
275
505
  }
276
506
  const decodeStartTime = getCurrentTime();
277
507
  const decodedRowGroups = pagePlan
278
- ? await Promise.all(pagePlan.rowRanges.map(rowRange => initialization.reader.readRowGroupRange(initialization.parquetSchema, rowGroup, columnList, rowRange, pagePlan.pageLocations, signal)))
508
+ ? await Promise.all(pagePlan.rowRanges.map(rowRange => initialization.reader.readRowGroupRange(initialization.parquetSchema, rowGroup, columnList, rowRange, pagePlan.pageLocations, signal, rowGroupIndex)))
279
509
  : [
280
- await initialization.reader.readRowGroup(initialization.parquetSchema, rowGroup, columnList, signal)
510
+ await initialization.reader.readRowGroup(initialization.parquetSchema, rowGroup, columnList, signal, rowGroupIndex)
281
511
  ];
282
512
  throwIfAborted(signal);
283
513
  const columns = concatenateMaterializedColumns(decodedRowGroups.map(decodedRowGroup => initialization.parquetSchema.materializeColumns(decodedRowGroup)));
@@ -315,12 +545,25 @@ export class ParquetSource extends DataSource {
315
545
  return Boolean(path && (columnList.length === 0 || fieldIndexOf(columnList, path) >= 0));
316
546
  });
317
547
  const rangeDescriptors = pagePlan
318
- ? getParquetPageReadRanges(rowGroup, columnList, pagePlan)
548
+ ? getParquetPageReadRanges(rowGroup, columnList, pagePlan, initialization.parquetSchema)
319
549
  : selectedColumnChunks.map(getColumnChunkRange);
320
550
  const ranges = await Promise.all(rangeDescriptors.map(async ({ offset, length }) => {
321
551
  return { offset, data: await initialization.file.read(offset, length, signal) };
322
552
  }));
323
553
  throwIfAborted(signal);
554
+ const workerColumnChunks = await Promise.all(selectedColumnChunks.map(async (columnChunk) => {
555
+ const descriptor = createParquetSourceWorkerColumnChunk(columnChunk);
556
+ descriptor.columnOrdinal = rowGroup.columns.indexOf(columnChunk);
557
+ if (columnChunk.crypto_metadata) {
558
+ const columnOrdinal = rowGroup.columns.indexOf(columnChunk);
559
+ const key = await initialization.reader.getColumnKeyForWorker(columnChunk, rowGroupIndex, columnOrdinal);
560
+ descriptor.encrypted = true;
561
+ descriptor.keyMetadata = key.keyMetadata?.slice().buffer;
562
+ descriptor.keyMaterial = key.keyMaterial.slice().buffer;
563
+ }
564
+ return descriptor;
565
+ }));
566
+ const encryptionContext = initialization.reader.getEncryptionContextForWorker();
324
567
  let workerResult;
325
568
  try {
326
569
  workerResult = await decodeParquetSourceRowGroupOnWorker({
@@ -329,12 +572,21 @@ export class ParquetSource extends DataSource {
329
572
  uncompressedByteLength: Number(rowGroup.total_byte_size),
330
573
  schemaDefinition: initialization.parquetSchema.schema,
331
574
  projectedSchema,
332
- columnChunks: selectedColumnChunks.map(createParquetSourceWorkerColumnChunk),
575
+ columnChunks: workerColumnChunks,
333
576
  ranges,
334
577
  batchSize: batchSize || Math.max(Number(rowGroup.num_rows), 1),
335
578
  predicate: predicate ? copyParquetPredicate(predicate) : undefined,
336
579
  pagePlan,
337
- preserveBinary: Boolean(this.options.parquet?.preserveBinary)
580
+ encryption: encryptionContext
581
+ ? {
582
+ algorithm: encryptionContext.algorithm,
583
+ aadPrefix: encryptionContext.aadPrefix?.slice().buffer,
584
+ fileUnique: encryptionContext.fileUnique.slice().buffer
585
+ }
586
+ : undefined,
587
+ preserveBinary: Boolean(this.options.parquet?.preserveBinary),
588
+ int96AsTimestamp: Boolean(this.options.parquet?.int96AsTimestamp),
589
+ verifyPageChecksums: Boolean(this.options.parquet?.verifyPageChecksums)
338
590
  }, workerOptions);
339
591
  }
340
592
  catch (error) {
@@ -352,6 +604,35 @@ export class ParquetSource extends DataSource {
352
604
  }
353
605
  return { rowGroupIndex, rowCount: workerResult.rowCount, workerResult };
354
606
  }
607
+ /** Decodes predicate columns first, avoiding projected-column decoding for empty matches. */
608
+ async readRowGroupWithLateMaterialization(initialization, rowGroupIndex, columnList, projectedSchema, batchSize, predicate, projectedColumnNames, workerOptions, signal) {
609
+ const predicateColumns = getParquetPredicateColumns(predicate);
610
+ const predicateColumnList = predicateColumns.map(column => [column]);
611
+ const filterResult = await this.readRowGroup(initialization, rowGroupIndex, predicateColumnList, projectSchema(initialization.schema, predicateColumns), batchSize, predicate, new Set(predicateColumns), workerOptions, signal);
612
+ const rowIndices = filterResult.rowIndices;
613
+ const selectedRowIndices = rowIndices || getWorkerRowIndices(filterResult.workerResult);
614
+ if (!selectedRowIndices || selectedRowIndices.length === 0) {
615
+ return { rowGroupIndex, rowCount: 0, columns: {}, rowIndices: [] };
616
+ }
617
+ const projectedColumnList = columnList.filter(columnPath => projectedColumnNames.has(columnPath[0]));
618
+ const projectedResult = await this.readRowGroup(initialization, rowGroupIndex, projectedColumnList, projectedSchema, batchSize, undefined, projectedColumnNames, workerOptions, signal);
619
+ if (workerOptions && projectedResult.workerResult) {
620
+ return {
621
+ rowGroupIndex,
622
+ rowCount: selectedRowIndices.length,
623
+ rowIndices: selectedRowIndices,
624
+ workerResult: gatherWorkerProjectedRows(projectedResult.workerResult, selectedRowIndices, batchSize)
625
+ };
626
+ }
627
+ return {
628
+ rowGroupIndex,
629
+ rowCount: selectedRowIndices.length,
630
+ rowIndices: selectedRowIndices,
631
+ columns: projectedResult.columns
632
+ ? gatherParquetColumns(projectedResult.columns, selectedRowIndices, projectedColumnNames)
633
+ : {}
634
+ };
635
+ }
355
636
  /** Records the rows and pages avoided by one conservative page-index plan. */
356
637
  recordPagePruningTelemetry(rowGroupIndex, plan) {
357
638
  this.recordTelemetry('page-index-prune', {
@@ -369,6 +650,11 @@ export class ParquetSource extends DataSource {
369
650
  try {
370
651
  const reader = new ParquetReader(file, {
371
652
  preserveBinary: this.options.parquet?.preserveBinary,
653
+ int96AsTimestamp: this.options.parquet?.int96AsTimestamp,
654
+ verifyPageChecksums: this.options.parquet?.verifyPageChecksums,
655
+ verifyFooterSignature: this.options.parquet?.verifyFooterSignature,
656
+ keyRetriever: this.options.parquet?.keyRetriever,
657
+ aadPrefix: this.options.parquet?.aadPrefix,
372
658
  signal
373
659
  });
374
660
  const fileMetadata = await reader.getFileMetadata();
@@ -421,6 +707,26 @@ export class ParquetSource extends DataSource {
421
707
  }
422
708
  }
423
709
  }
710
+ function getParquetColumnRoles(columnNames) {
711
+ const roles = {};
712
+ for (const columnName of columnNames) {
713
+ const normalizedName = columnName.toLowerCase();
714
+ if (normalizedName === 'geometry' || normalizedName === 'geom' || normalizedName === 'shape') {
715
+ roles[columnName] = 'geometry';
716
+ }
717
+ else if (normalizedName === 'longitude' ||
718
+ normalizedName === 'lon' ||
719
+ normalizedName === 'x') {
720
+ roles[columnName] = 'longitude';
721
+ }
722
+ else if (normalizedName === 'latitude' ||
723
+ normalizedName === 'lat' ||
724
+ normalizedName === 'y') {
725
+ roles[columnName] = 'latitude';
726
+ }
727
+ }
728
+ return roles;
729
+ }
424
730
  /** Normalizes a decoded Parquet footer into source metadata. */
425
731
  function createParquetSourceMetadata(data, url, file, fileMetadata, schema, parquetSchema, objectVersion) {
426
732
  let rowOffset = 0;
@@ -428,7 +734,7 @@ function createParquetSourceMetadata(data, url, file, fileMetadata, schema, parq
428
734
  const columns = rowGroup.columns
429
735
  .filter(columnChunk => Boolean(columnChunk.meta_data))
430
736
  .map(columnChunk => createColumnChunkMetadata(columnChunk, parquetSchema));
431
- const normalizedRowGroup = createRowGroupMetadata(index, rowOffset, Number(rowGroup.num_rows), Number(rowGroup.total_byte_size), columns);
737
+ const normalizedRowGroup = createRowGroupMetadata(index, rowOffset, Number(rowGroup.num_rows), Number(rowGroup.total_byte_size), columns, rowGroup.sorting_columns);
432
738
  rowOffset += normalizedRowGroup.rowCount;
433
739
  return normalizedRowGroup;
434
740
  });
@@ -455,6 +761,8 @@ function createColumnChunkMetadata(columnChunk, parquetSchema) {
455
761
  ? undefined
456
762
  : Number(columnMetadata.dictionary_page_offset);
457
763
  const statistics = createColumnChunkStatistics(columnMetadata.statistics, parquetSchema.findField(columnMetadata.path_in_schema));
764
+ const geospatialStatistics = createGeospatialStatistics(columnMetadata.geospatial_statistics);
765
+ const sizeStatistics = createSizeStatistics(columnMetadata.size_statistics);
458
766
  const compressedByteLength = Number(columnMetadata.total_compressed_size);
459
767
  const uncompressedByteLength = Number(columnMetadata.total_uncompressed_size);
460
768
  return Object.freeze({
@@ -478,7 +786,75 @@ function createColumnChunkMetadata(columnChunk, parquetSchema) {
478
786
  ? undefined
479
787
  : Number(columnChunk.offset_index_offset),
480
788
  offsetIndexByteLength: columnChunk.offset_index_length,
481
- statistics
789
+ bloomFilterOffset: columnMetadata.bloom_filter_offset === undefined
790
+ ? undefined
791
+ : Number(columnMetadata.bloom_filter_offset),
792
+ bloomFilterByteLength: columnMetadata.bloom_filter_length,
793
+ statistics,
794
+ sizeStatistics,
795
+ geospatialStatistics
796
+ });
797
+ }
798
+ /** Normalizes optional Parquet size statistics without inventing missing counts. */
799
+ function createSizeStatistics(statistics) {
800
+ if (!statistics)
801
+ return undefined;
802
+ const result = {
803
+ unencodedByteArrayDataBytes: statistics.unencoded_byte_array_data_bytes === undefined
804
+ ? undefined
805
+ : toSafeNumber(statistics.unencoded_byte_array_data_bytes),
806
+ repetitionLevelHistogram: normalizeSizeStatisticsList(statistics.repetition_level_histogram),
807
+ definitionLevelHistogram: normalizeSizeStatisticsList(statistics.definition_level_histogram)
808
+ };
809
+ if (result.unencodedByteArrayDataBytes === undefined &&
810
+ result.repetitionLevelHistogram === undefined &&
811
+ result.definitionLevelHistogram === undefined) {
812
+ return undefined;
813
+ }
814
+ return Object.freeze({
815
+ ...result,
816
+ repetitionLevelHistogram: result.repetitionLevelHistogram
817
+ ? Object.freeze(result.repetitionLevelHistogram)
818
+ : undefined,
819
+ definitionLevelHistogram: result.definitionLevelHistogram
820
+ ? Object.freeze(result.definitionLevelHistogram)
821
+ : undefined
822
+ });
823
+ }
824
+ function toSafeNumber(value) {
825
+ const numberValue = typeof value === 'number' ? value : value.toNumber?.();
826
+ return numberValue !== undefined && Number.isSafeInteger(numberValue) && numberValue >= 0
827
+ ? numberValue
828
+ : undefined;
829
+ }
830
+ /** Converts a Thrift int64 histogram only when every entry is a safe non-negative integer. */
831
+ function normalizeSizeStatisticsList(values) {
832
+ if (!values)
833
+ return undefined;
834
+ const normalized = values.map(toSafeNumber);
835
+ return normalized.every((value) => value !== undefined) ? normalized : undefined;
836
+ }
837
+ /** Normalizes native Parquet geospatial statistics from the decoded footer. */
838
+ function createGeospatialStatistics(statistics) {
839
+ if (!statistics)
840
+ return undefined;
841
+ const bbox = statistics.bbox
842
+ ? Object.freeze({
843
+ xmin: statistics.bbox.xmin,
844
+ xmax: statistics.bbox.xmax,
845
+ ymin: statistics.bbox.ymin,
846
+ ymax: statistics.bbox.ymax,
847
+ zmin: statistics.bbox.zmin,
848
+ zmax: statistics.bbox.zmax,
849
+ mmin: statistics.bbox.mmin,
850
+ mmax: statistics.bbox.mmax
851
+ })
852
+ : undefined;
853
+ return Object.freeze({
854
+ bbox,
855
+ geometryTypes: statistics.geospatial_types
856
+ ? Object.freeze([...statistics.geospatial_types])
857
+ : undefined
482
858
  });
483
859
  }
484
860
  /** Decodes optional footer statistics into the column's logical value representation. */
@@ -547,7 +923,7 @@ function decodeStatisticsValueSafely(bytes, field) {
547
923
  }
548
924
  }
549
925
  /** Normalizes one decoded row group and derives its compressed byte length. */
550
- function createRowGroupMetadata(index, rowOffset, rowCount, uncompressedByteLength, columns) {
926
+ function createRowGroupMetadata(index, rowOffset, rowCount, uncompressedByteLength, columns, sortingColumns) {
551
927
  const compressedByteLength = columns.reduce((sum, column) => sum + column.compressedByteLength, 0);
552
928
  return Object.freeze({
553
929
  index,
@@ -557,9 +933,37 @@ function createRowGroupMetadata(index, rowOffset, rowCount, uncompressedByteLeng
557
933
  uncompressedSize: uncompressedByteLength,
558
934
  compressedByteLength,
559
935
  compressedSize: compressedByteLength,
560
- columns: Object.freeze(columns)
936
+ columns: Object.freeze(columns),
937
+ sortingColumns: Object.freeze((sortingColumns || []).map(sortingColumn => Object.freeze({
938
+ columnIndex: sortingColumn.column_idx,
939
+ descending: sortingColumn.descending,
940
+ nullsFirst: sortingColumn.nulls_first
941
+ })))
942
+ });
943
+ }
944
+ /** Describes a phase that reads complete column chunks because no page index is available. */
945
+ function createFullColumnScanPlan(rowGroup, rowGroupIndex, phase, columns) {
946
+ const selectedColumnChunks = rowGroup.columns.filter(columnChunk => {
947
+ const path = columnChunk.meta_data?.path_in_schema;
948
+ return Boolean(path && (columns.length === 0 || columns.some(column => pathMatchesSelection(column, path))));
949
+ });
950
+ const rowCount = Number(rowGroup.num_rows);
951
+ return Object.freeze({
952
+ rowGroupIndex,
953
+ phase,
954
+ columns: Object.freeze(columns.map(column => Object.freeze([...column]))),
955
+ rowRanges: Object.freeze([Object.freeze({ start: 0, end: rowCount })]),
956
+ indexesRead: 0,
957
+ totalPages: 0,
958
+ selectedPages: 0,
959
+ rowsPruned: 0,
960
+ ranges: Object.freeze(selectedColumnChunks.map(columnChunk => Object.freeze(getColumnChunkRange(columnChunk))))
561
961
  });
562
962
  }
963
+ /** Returns whether a requested path selects a footer leaf or one of its nested children. */
964
+ function pathMatchesSelection(selection, path) {
965
+ return selection.length <= path.length && selection.every((part, index) => part === path[index]);
966
+ }
563
967
  /** Returns the complete contiguous byte range occupied by one Parquet column chunk. */
564
968
  function getColumnChunkRange(columnChunk) {
565
969
  if (columnChunk.file_path) {
@@ -623,11 +1027,8 @@ function createParquetBatchFromArrow(metadata, schema, rowGroupIndex, rowGroupRo
623
1027
  : undefined
624
1028
  });
625
1029
  return {
626
- batchType: 'data',
627
- shape: 'arrow-table',
1030
+ ...makeTableScanBatch({ shape: 'arrow-table', schema, data }),
628
1031
  schemaType: 'explicit',
629
- schema,
630
- data,
631
1032
  length: rowCount,
632
1033
  metadata: provenance,
633
1034
  ...provenance
@@ -731,6 +1132,9 @@ function createParquetTelemetry() {
731
1132
  rowGroupsRequested: 0,
732
1133
  rowGroupsPruned: 0,
733
1134
  rowGroupsPrunedByStatistics: 0,
1135
+ rowGroupsPrunedByBloomFilter: 0,
1136
+ bloomFiltersRead: 0,
1137
+ bloomFilterBytesRead: 0,
734
1138
  rowGroupsPrunedByPageIndex: 0,
735
1139
  pageIndexesRead: 0,
736
1140
  pagesRead: 0,
@@ -745,6 +1149,34 @@ function createParquetTelemetry() {
745
1149
  failedReadCount: 0
746
1150
  };
747
1151
  }
1152
+ /** Converts cumulative Parquet counters into one portable read-scoped execution snapshot. */
1153
+ function createParquetScanExecutionTelemetry(before, after, startedAt, status, earlyTerminationReason, error) {
1154
+ const delta = Object.fromEntries(Object.keys(after).map(key => {
1155
+ const telemetryKey = key;
1156
+ return [key, after[telemetryKey] - before[telemetryKey]];
1157
+ }));
1158
+ const rowsRead = Math.max(delta.predicateRowsTested, delta.rowsEmitted);
1159
+ return Object.freeze({
1160
+ status,
1161
+ sourcesPlanned: 1,
1162
+ sourcesRead: status === 'early-terminated' && earlyTerminationReason === 'limit' && rowsRead === 0 ? 0 : 1,
1163
+ batchesRead: delta.batchesEmitted,
1164
+ batchesDecoded: delta.rowGroupsDecoded,
1165
+ rowsRead,
1166
+ rowsTested: delta.predicateRowsTested || undefined,
1167
+ rowsRetained: delta.predicateRowsMatched || undefined,
1168
+ rowsReturned: delta.rowsEmitted,
1169
+ bytesRead: delta.downloadedBytes,
1170
+ bytesFetched: delta.downloadedBytes,
1171
+ filesOpened: rowsRead || delta.downloadedBytes ? 1 : 0,
1172
+ tasksOpened: delta.rowGroupsDecoded,
1173
+ rowsPruned: delta.rowsPrunedByPageIndex,
1174
+ durationMilliseconds: Date.now() - startedAt,
1175
+ earlyTerminationReason,
1176
+ details: Object.freeze({ ...delta }),
1177
+ ...(error === undefined ? {} : { error })
1178
+ });
1179
+ }
748
1180
  /** Returns a monotonic timestamp when available and falls back to wall-clock time. */
749
1181
  function getCurrentTime() {
750
1182
  return globalThis.performance?.now() ?? Date.now();
@@ -805,6 +1237,121 @@ function getSourceName(data, url) {
805
1237
  : 'parquet';
806
1238
  return sourceName.replace(/\.parquet$/i, '') || 'parquet';
807
1239
  }
1240
+ /** Reconstructs exact row-group indexes from worker predicate batches. */
1241
+ function getWorkerRowIndices(workerResult) {
1242
+ if (!workerResult)
1243
+ return [];
1244
+ return workerResult.batches.flatMap(batch => batch.rowGroupRowIndices
1245
+ ? [...batch.rowGroupRowIndices]
1246
+ : Array.from({ length: batch.rowCount }, (_, index) => batch.rowGroupRowOffset + index));
1247
+ }
1248
+ /** Gathers projected worker Arrow batches through the shared indexed Arrow table utility. */
1249
+ function gatherWorkerProjectedRows(workerResult, rowIndices, batchSize) {
1250
+ const tables = workerResult.batches.map(batch => hydrateArrowTable(batch.arrowTable));
1251
+ if (tables.length === 0) {
1252
+ return { ...workerResult, rowCount: 0, batches: [] };
1253
+ }
1254
+ const table = tables.length === 1 ? tables[0] : tables[0].concat(...tables.slice(1));
1255
+ const indexedTable = new IndexedArrowTable(table, rowIndices);
1256
+ const outputBatchSize = batchSize || Math.max(rowIndices.length, 1);
1257
+ const batches = [];
1258
+ for (let offset = 0; offset < rowIndices.length; offset += outputBatchSize) {
1259
+ const end = Math.min(offset + outputBatchSize, rowIndices.length);
1260
+ const batchIndexes = rowIndices.slice(offset, end);
1261
+ const batchTable = indexedTable.slice(offset, end).materializeArrowTable();
1262
+ batches.push({
1263
+ rowGroupRowOffset: batchIndexes[0],
1264
+ rowCount: batchIndexes.length,
1265
+ rowGroupRowIndices: [...batchIndexes],
1266
+ arrowTable: dehydrateArrowTable(batchTable)
1267
+ });
1268
+ }
1269
+ return {
1270
+ sourceRowCount: workerResult.sourceRowCount,
1271
+ rowCount: rowIndices.length,
1272
+ batches,
1273
+ decodeDurationMs: workerResult.decodeDurationMs,
1274
+ arrowConversionDurationMs: workerResult.arrowConversionDurationMs
1275
+ };
1276
+ }
1277
+ /** Applies only proven-negative Bloom-filter checks to candidate row groups. */
1278
+ async function filterParquetRowGroupsWithBloomFilters(initialization, rowGroupIndices, predicate, signal) {
1279
+ const rowGroups = [];
1280
+ let filtersRead = 0;
1281
+ let bytesRead = 0;
1282
+ for (const rowGroupIndex of rowGroupIndices) {
1283
+ const rowGroup = initialization.metadata.rowGroups[rowGroupIndex];
1284
+ const probes = getParquetBloomFilterProbes(predicate, rowGroup);
1285
+ let keepRowGroup = true;
1286
+ for (const probe of probes) {
1287
+ if (probe.column.bloomFilterOffset === undefined ||
1288
+ probe.column.bloomFilterByteLength === undefined) {
1289
+ continue;
1290
+ }
1291
+ let field;
1292
+ try {
1293
+ field = initialization.parquetSchema.findField([...probe.column.path]);
1294
+ }
1295
+ catch {
1296
+ throwIfAborted(signal);
1297
+ continue;
1298
+ }
1299
+ if (!field.primitiveType)
1300
+ continue;
1301
+ let filter;
1302
+ try {
1303
+ const rawColumnOrdinal = initialization.fileMetadata.row_groups[rowGroupIndex].columns.findIndex(columnChunk => JSON.stringify(columnChunk.meta_data?.path_in_schema) ===
1304
+ JSON.stringify(probe.column.path));
1305
+ const rawColumnChunk = rawColumnOrdinal >= 0
1306
+ ? initialization.fileMetadata.row_groups[rowGroupIndex].columns[rawColumnOrdinal]
1307
+ : undefined;
1308
+ const data = await initialization.file.read(probe.column.bloomFilterOffset, probe.column.bloomFilterByteLength, signal);
1309
+ const decodedBloomFilter = rawColumnChunk
1310
+ ? await initialization.reader.decryptBloomFilter(toUint8Array(data), rowGroupIndex, rawColumnOrdinal, rawColumnChunk)
1311
+ : toUint8Array(data);
1312
+ filter = decodeParquetSplitBlockBloomFilter(decodedBloomFilter);
1313
+ filtersRead++;
1314
+ bytesRead += data.byteLength;
1315
+ }
1316
+ catch {
1317
+ throwIfAborted(signal);
1318
+ continue;
1319
+ }
1320
+ if (filter.algorithm !== 'BLOCK' ||
1321
+ filter.hash !== 'XXHASH' ||
1322
+ filter.compression !== 'UNCOMPRESSED') {
1323
+ continue;
1324
+ }
1325
+ const mayContainValue = probe.values.some(value => {
1326
+ try {
1327
+ if (value instanceof Date)
1328
+ return true;
1329
+ const physicalValue = getParquetBloomFilterPhysicalValue(value, field);
1330
+ const encoded = encodeParquetBloomFilterValue(physicalValue, field.primitiveType, field.typeLength);
1331
+ return checkParquetSplitBlockBloomFilter(filter.bitset, hashParquetBloomFilterValue(encoded));
1332
+ }
1333
+ catch {
1334
+ throwIfAborted(signal);
1335
+ return true;
1336
+ }
1337
+ });
1338
+ if (!mayContainValue) {
1339
+ keepRowGroup = false;
1340
+ break;
1341
+ }
1342
+ }
1343
+ if (keepRowGroup)
1344
+ rowGroups.push(rowGroupIndex);
1345
+ }
1346
+ return { rowGroupIndices: rowGroups, filtersRead, bytesRead };
1347
+ }
1348
+ /** Converts logical predicate values into the physical values stored in Bloom filters. */
1349
+ function getParquetBloomFilterPhysicalValue(value, field) {
1350
+ if (field.originalType?.startsWith('DECIMAL_')) {
1351
+ return toPrimitive(field.originalType, value, field);
1352
+ }
1353
+ return value;
1354
+ }
808
1355
  /** Returns true when at least one HTTP object validator was captured. */
809
1356
  function hasObjectVersion(version) {
810
1357
  return Boolean(version?.etag || version?.lastModified);