@loaders.gl/parquet 4.5.3 → 5.0.0-alpha.10

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 (790) hide show
  1. package/LICENSE +14 -3
  2. package/dist/bundled.d.ts +9 -0
  3. package/dist/bundled.d.ts.map +1 -0
  4. package/dist/bundled.js +10 -0
  5. package/dist/bundled.js.map +1 -0
  6. package/dist/delta-format.d.ts +9 -0
  7. package/dist/delta-format.d.ts.map +1 -0
  8. package/dist/delta-format.js +12 -0
  9. package/dist/delta-format.js.map +1 -0
  10. package/dist/delta-source-loader-types.d.ts +27 -0
  11. package/dist/delta-source-loader-types.d.ts.map +1 -0
  12. package/dist/delta-source-loader-types.js +28 -0
  13. package/dist/delta-source-loader-types.js.map +1 -0
  14. package/dist/delta-source.d.ts +48 -0
  15. package/dist/delta-source.d.ts.map +1 -0
  16. package/dist/delta-source.js +222 -0
  17. package/dist/delta-source.js.map +1 -0
  18. package/dist/delta-types.d.ts +38 -0
  19. package/dist/delta-types.d.ts.map +1 -0
  20. package/dist/delta-types.js +5 -0
  21. package/dist/delta-types.js.map +1 -0
  22. package/dist/geoparquet-loader-with-parser.d.ts +33 -0
  23. package/dist/geoparquet-loader-with-parser.d.ts.map +1 -0
  24. package/dist/geoparquet-loader-with-parser.js +52 -0
  25. package/dist/geoparquet-loader-with-parser.js.map +1 -0
  26. package/dist/geoparquet-loader.d.ts +59 -0
  27. package/dist/geoparquet-loader.d.ts.map +1 -0
  28. package/dist/geoparquet-loader.js +30 -0
  29. package/dist/geoparquet-loader.js.map +1 -0
  30. package/dist/iceberg-rest-catalog.d.ts +49 -0
  31. package/dist/iceberg-rest-catalog.d.ts.map +1 -0
  32. package/dist/iceberg-rest-catalog.js +78 -0
  33. package/dist/iceberg-rest-catalog.js.map +1 -0
  34. package/dist/iceberg-table-source.d.ts +63 -0
  35. package/dist/iceberg-table-source.d.ts.map +1 -0
  36. package/dist/iceberg-table-source.js +797 -0
  37. package/dist/iceberg-table-source.js.map +1 -0
  38. package/dist/iceberg-types.d.ts +150 -0
  39. package/dist/iceberg-types.d.ts.map +1 -0
  40. package/dist/iceberg-types.js +5 -0
  41. package/dist/iceberg-types.js.map +1 -0
  42. package/dist/index.cjs +41568 -6781
  43. package/dist/index.cjs.map +3 -4
  44. package/dist/index.d.ts +15 -9
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +11 -12
  47. package/dist/index.js.map +1 -1
  48. package/dist/lib/arrow/convert-columns-to-row-group.js +3 -0
  49. package/dist/lib/arrow/convert-columns-to-row-group.js.map +1 -1
  50. package/dist/lib/arrow/convert-schema-from-parquet.d.ts +3 -1
  51. package/dist/lib/arrow/convert-schema-from-parquet.d.ts.map +1 -1
  52. package/dist/lib/arrow/convert-schema-from-parquet.js +164 -29
  53. package/dist/lib/arrow/convert-schema-from-parquet.js.map +1 -1
  54. package/dist/lib/arrow/convert-schema-to-parquet.d.ts.map +1 -1
  55. package/dist/lib/arrow/convert-schema-to-parquet.js +16 -7
  56. package/dist/lib/arrow/convert-schema-to-parquet.js.map +1 -1
  57. package/dist/lib/arrow/create-nested-arrow-vector.d.ts +8 -0
  58. package/dist/lib/arrow/create-nested-arrow-vector.d.ts.map +1 -0
  59. package/dist/lib/arrow/create-nested-arrow-vector.js +485 -0
  60. package/dist/lib/arrow/create-nested-arrow-vector.js.map +1 -0
  61. package/dist/lib/constants.d.ts +0 -1
  62. package/dist/lib/constants.d.ts.map +1 -1
  63. package/dist/lib/constants.js +1 -2
  64. package/dist/lib/constants.js.map +1 -1
  65. package/dist/lib/encoders/encode-arrow-to-parquet.d.ts +2 -2
  66. package/dist/lib/encoders/encode-arrow-to-parquet.d.ts.map +1 -1
  67. package/dist/lib/encoders/encode-arrow-to-parquet.js +3 -10
  68. package/dist/lib/encoders/encode-arrow-to-parquet.js.map +1 -1
  69. package/dist/lib/encoders/encode-table-to-parquet-js.d.ts +5 -0
  70. package/dist/lib/encoders/encode-table-to-parquet-js.d.ts.map +1 -0
  71. package/dist/lib/encoders/encode-table-to-parquet-js.js +394 -0
  72. package/dist/lib/encoders/encode-table-to-parquet-js.js.map +1 -0
  73. package/dist/lib/geo/geoparquet-covering.d.ts +19 -0
  74. package/dist/lib/geo/geoparquet-covering.d.ts.map +1 -0
  75. package/dist/lib/geo/geoparquet-covering.js +137 -0
  76. package/dist/lib/geo/geoparquet-covering.js.map +1 -0
  77. package/dist/lib/geo/geospatial-metadata.d.ts +67 -0
  78. package/dist/lib/geo/geospatial-metadata.d.ts.map +1 -0
  79. package/dist/lib/geo/geospatial-metadata.js +412 -0
  80. package/dist/lib/geo/geospatial-metadata.js.map +1 -0
  81. package/dist/lib/parquet-bloom-filter-planner.d.ts +16 -0
  82. package/dist/lib/parquet-bloom-filter-planner.d.ts.map +1 -0
  83. package/dist/lib/parquet-bloom-filter-planner.js +57 -0
  84. package/dist/lib/parquet-bloom-filter-planner.js.map +1 -0
  85. package/dist/lib/parquet-bloom-filter.d.ts +30 -0
  86. package/dist/lib/parquet-bloom-filter.d.ts.map +1 -0
  87. package/dist/lib/parquet-bloom-filter.js +289 -0
  88. package/dist/lib/parquet-bloom-filter.js.map +1 -0
  89. package/dist/lib/parquet-encryption.d.ts +80 -0
  90. package/dist/lib/parquet-encryption.d.ts.map +1 -0
  91. package/dist/lib/parquet-encryption.js +190 -0
  92. package/dist/lib/parquet-encryption.js.map +1 -0
  93. package/dist/lib/parquet-page-index.d.ts +91 -0
  94. package/dist/lib/parquet-page-index.d.ts.map +1 -0
  95. package/dist/lib/parquet-page-index.js +463 -0
  96. package/dist/lib/parquet-page-index.js.map +1 -0
  97. package/dist/lib/parquet-predicate.d.ts +21 -0
  98. package/dist/lib/parquet-predicate.d.ts.map +1 -0
  99. package/dist/lib/parquet-predicate.js +328 -0
  100. package/dist/lib/parquet-predicate.js.map +1 -0
  101. package/dist/lib/parquet-source-worker-client.d.ts +6 -0
  102. package/dist/lib/parquet-source-worker-client.d.ts.map +1 -0
  103. package/dist/lib/parquet-source-worker-client.js +24 -0
  104. package/dist/lib/parquet-source-worker-client.js.map +1 -0
  105. package/dist/lib/parquet-source-worker-decoder.d.ts +7 -0
  106. package/dist/lib/parquet-source-worker-decoder.d.ts.map +1 -0
  107. package/dist/lib/parquet-source-worker-decoder.js +181 -0
  108. package/dist/lib/parquet-source-worker-decoder.js.map +1 -0
  109. package/dist/lib/parquet-source-worker-file.d.ts +24 -0
  110. package/dist/lib/parquet-source-worker-file.d.ts.map +1 -0
  111. package/dist/lib/parquet-source-worker-file.js +40 -0
  112. package/dist/lib/parquet-source-worker-file.js.map +1 -0
  113. package/dist/lib/parquet-source-worker-types.d.ts +121 -0
  114. package/dist/lib/parquet-source-worker-types.d.ts.map +1 -0
  115. package/dist/lib/parquet-source-worker-types.js +5 -0
  116. package/dist/lib/parquet-source-worker-types.js.map +1 -0
  117. package/dist/lib/parquet-worker-transport.d.ts +8 -0
  118. package/dist/lib/parquet-worker-transport.d.ts.map +1 -0
  119. package/dist/lib/parquet-worker-transport.js +40 -0
  120. package/dist/lib/parquet-worker-transport.js.map +1 -0
  121. package/dist/lib/parsers/convert-parquet-tables.d.ts +16 -0
  122. package/dist/lib/parsers/convert-parquet-tables.d.ts.map +1 -0
  123. package/dist/lib/parsers/convert-parquet-tables.js +30 -0
  124. package/dist/lib/parsers/convert-parquet-tables.js.map +1 -0
  125. package/dist/lib/parsers/get-parquet-schema.d.ts.map +1 -1
  126. package/dist/lib/parsers/get-parquet-schema.js +6 -2
  127. package/dist/lib/parsers/get-parquet-schema.js.map +1 -1
  128. package/dist/lib/parsers/parse-geoparquet-to-geojson.d.ts +4 -4
  129. package/dist/lib/parsers/parse-geoparquet-to-geojson.d.ts.map +1 -1
  130. package/dist/lib/parsers/parse-geoparquet-to-geojson.js +19 -6
  131. package/dist/lib/parsers/parse-geoparquet-to-geojson.js.map +1 -1
  132. package/dist/lib/parsers/parse-parquet-tables.d.ts +37 -0
  133. package/dist/lib/parsers/parse-parquet-tables.d.ts.map +1 -0
  134. package/dist/lib/parsers/parse-parquet-tables.js +50 -0
  135. package/dist/lib/parsers/parse-parquet-tables.js.map +1 -0
  136. package/dist/lib/parsers/parse-parquet-to-arrow-js.d.ts +19 -0
  137. package/dist/lib/parsers/parse-parquet-to-arrow-js.d.ts.map +1 -0
  138. package/dist/lib/parsers/parse-parquet-to-arrow-js.js +921 -0
  139. package/dist/lib/parsers/parse-parquet-to-arrow-js.js.map +1 -0
  140. package/dist/lib/parsers/parse-parquet-to-arrow.d.ts +7 -7
  141. package/dist/lib/parsers/parse-parquet-to-arrow.d.ts.map +1 -1
  142. package/dist/lib/parsers/parse-parquet-to-arrow.js +45 -32
  143. package/dist/lib/parsers/parse-parquet-to-arrow.js.map +1 -1
  144. package/dist/lib/parsers/parse-parquet-to-columns.d.ts +3 -3
  145. package/dist/lib/parsers/parse-parquet-to-columns.d.ts.map +1 -1
  146. package/dist/lib/parsers/parse-parquet-to-columns.js +4 -5
  147. package/dist/lib/parsers/parse-parquet-to-columns.js.map +1 -1
  148. package/dist/lib/parsers/parse-parquet-to-json.d.ts +3 -3
  149. package/dist/lib/parsers/parse-parquet-to-json.d.ts.map +1 -1
  150. package/dist/lib/parsers/parse-parquet-to-json.js +95 -28
  151. package/dist/lib/parsers/parse-parquet-to-json.js.map +1 -1
  152. package/dist/lib/slice-parquet-batch.d.ts +9 -0
  153. package/dist/lib/slice-parquet-batch.d.ts.map +1 -0
  154. package/dist/lib/slice-parquet-batch.js +28 -0
  155. package/dist/lib/slice-parquet-batch.js.map +1 -0
  156. package/dist/lib/sources/parquet-range-file.d.ts +80 -0
  157. package/dist/lib/sources/parquet-range-file.d.ts.map +1 -0
  158. package/dist/lib/sources/parquet-range-file.js +267 -0
  159. package/dist/lib/sources/parquet-range-file.js.map +1 -0
  160. package/dist/lib/sources/single-batch-queue.d.ts +24 -0
  161. package/dist/lib/sources/single-batch-queue.d.ts.map +1 -0
  162. package/dist/lib/sources/single-batch-queue.js +91 -0
  163. package/dist/lib/sources/single-batch-queue.js.map +1 -0
  164. package/dist/lib/utils/load-wasm-node.d.ts +3 -0
  165. package/dist/lib/utils/load-wasm-node.d.ts.map +1 -0
  166. package/dist/lib/utils/load-wasm-node.js +23 -0
  167. package/dist/lib/utils/load-wasm-node.js.map +1 -0
  168. package/dist/lib/utils/load-wasm.d.ts +2 -2
  169. package/dist/lib/utils/load-wasm.d.ts.map +1 -1
  170. package/dist/lib/utils/load-wasm.js +28 -12
  171. package/dist/lib/utils/load-wasm.js.map +1 -1
  172. package/dist/lib/utils/make-stream-iterator.d.ts +2 -0
  173. package/dist/lib/utils/make-stream-iterator.d.ts.map +1 -1
  174. package/dist/lib/utils/make-stream-iterator.js +123 -14
  175. package/dist/lib/utils/make-stream-iterator.js.map +1 -1
  176. package/dist/lib/utils/normalize-parquet-options.d.ts +5 -0
  177. package/dist/lib/utils/normalize-parquet-options.d.ts.map +1 -0
  178. package/dist/lib/utils/normalize-parquet-options.js +25 -0
  179. package/dist/lib/utils/normalize-parquet-options.js.map +1 -0
  180. package/dist/parquet-dataset-source.cjs +38974 -0
  181. package/dist/parquet-dataset-source.cjs.map +6 -0
  182. package/dist/parquet-dataset-source.d.ts +81 -0
  183. package/dist/parquet-dataset-source.d.ts.map +1 -0
  184. package/dist/parquet-dataset-source.js +602 -0
  185. package/dist/parquet-dataset-source.js.map +1 -0
  186. package/dist/parquet-encoded-page-types.d.ts +108 -0
  187. package/dist/parquet-encoded-page-types.d.ts.map +1 -0
  188. package/dist/parquet-encoded-page-types.js +5 -0
  189. package/dist/parquet-encoded-page-types.js.map +1 -0
  190. package/dist/parquet-format.d.ts +2 -0
  191. package/dist/parquet-format.d.ts.map +1 -1
  192. package/dist/parquet-format.js +2 -0
  193. package/dist/parquet-format.js.map +1 -1
  194. package/dist/parquet-js-loader-types.d.ts +59 -0
  195. package/dist/parquet-js-loader-types.d.ts.map +1 -0
  196. package/dist/parquet-js-loader-types.js +34 -0
  197. package/dist/parquet-js-loader-types.js.map +1 -0
  198. package/dist/parquet-js-loader.d.ts +18 -22
  199. package/dist/parquet-js-loader.d.ts.map +1 -1
  200. package/dist/parquet-js-loader.js +53 -14
  201. package/dist/parquet-js-loader.js.map +1 -1
  202. package/dist/parquet-js-writer.d.ts +65 -0
  203. package/dist/parquet-js-writer.d.ts.map +1 -0
  204. package/dist/parquet-js-writer.js +28 -0
  205. package/dist/parquet-js-writer.js.map +1 -0
  206. package/dist/parquet-loader-base.d.ts +25 -0
  207. package/dist/parquet-loader-base.d.ts.map +1 -0
  208. package/dist/parquet-loader-base.js +23 -0
  209. package/dist/parquet-loader-base.js.map +1 -0
  210. package/dist/parquet-loader-options.d.ts +55 -0
  211. package/dist/parquet-loader-options.d.ts.map +1 -0
  212. package/dist/parquet-loader-options.js +10 -0
  213. package/dist/parquet-loader-options.js.map +1 -0
  214. package/dist/parquet-loader-types.d.ts +61 -0
  215. package/dist/parquet-loader-types.d.ts.map +1 -0
  216. package/dist/parquet-loader-types.js +17 -0
  217. package/dist/parquet-loader-types.js.map +1 -0
  218. package/dist/parquet-loader.d.ts +42 -0
  219. package/dist/parquet-loader.d.ts.map +1 -0
  220. package/dist/parquet-loader.js +68 -0
  221. package/dist/parquet-loader.js.map +1 -0
  222. package/dist/parquet-source-capabilities.d.ts +43 -0
  223. package/dist/parquet-source-capabilities.d.ts.map +1 -0
  224. package/dist/parquet-source-capabilities.js +32 -0
  225. package/dist/parquet-source-capabilities.js.map +1 -0
  226. package/dist/parquet-source-loader-types.d.ts +82 -0
  227. package/dist/parquet-source-loader-types.d.ts.map +1 -0
  228. package/dist/parquet-source-loader-types.js +84 -0
  229. package/dist/parquet-source-loader-types.js.map +1 -0
  230. package/dist/parquet-source-loader.cjs +38261 -0
  231. package/dist/parquet-source-loader.cjs.map +6 -0
  232. package/dist/parquet-source-loader.d.ts +167 -0
  233. package/dist/parquet-source-loader.d.ts.map +1 -0
  234. package/dist/parquet-source-loader.js +1438 -0
  235. package/dist/parquet-source-loader.js.map +1 -0
  236. package/dist/parquet-source-types.d.ts +534 -0
  237. package/dist/parquet-source-types.d.ts.map +1 -0
  238. package/dist/parquet-source-types.js +5 -0
  239. package/dist/parquet-source-types.js.map +1 -0
  240. package/dist/parquet-source-worker-factory.d.ts +4 -0
  241. package/dist/parquet-source-worker-factory.d.ts.map +1 -0
  242. package/dist/parquet-source-worker-factory.js +6 -0
  243. package/dist/parquet-source-worker-factory.js.map +1 -0
  244. package/dist/parquet-source-worker-url.d.ts +3 -0
  245. package/dist/parquet-source-worker-url.d.ts.map +1 -0
  246. package/dist/parquet-source-worker-url.js +22 -0
  247. package/dist/parquet-source-worker-url.js.map +1 -0
  248. package/dist/parquet-source-worker.js +38871 -0
  249. package/dist/parquet-source-worker.js.map +7 -0
  250. package/dist/parquet-worker-node.cjs +78507 -0
  251. package/dist/parquet-worker-node.cjs.map +7 -0
  252. package/dist/parquet-worker-url.d.ts +3 -0
  253. package/dist/parquet-worker-url.d.ts.map +1 -0
  254. package/dist/parquet-worker-url.js +19 -0
  255. package/dist/parquet-worker-url.js.map +1 -0
  256. package/dist/parquet-worker.js +20094 -0
  257. package/dist/parquet-worker.js.map +7 -0
  258. package/dist/parquet-writer.d.ts +10 -0
  259. package/dist/parquet-writer.d.ts.map +1 -0
  260. package/dist/parquet-writer.js +27 -0
  261. package/dist/parquet-writer.js.map +1 -0
  262. package/dist/parquet_wasm_bg.wasm +0 -0
  263. package/dist/parquetjs/codecs/byte-stream-split.d.ts +9 -0
  264. package/dist/parquetjs/codecs/byte-stream-split.d.ts.map +1 -0
  265. package/dist/parquetjs/codecs/byte-stream-split.js +159 -0
  266. package/dist/parquetjs/codecs/byte-stream-split.js.map +1 -0
  267. package/dist/parquetjs/codecs/declare.d.ts +35 -3
  268. package/dist/parquetjs/codecs/declare.d.ts.map +1 -1
  269. package/dist/parquetjs/codecs/declare.js +17 -1
  270. package/dist/parquetjs/codecs/declare.js.map +1 -1
  271. package/dist/parquetjs/codecs/delta.d.ts +17 -0
  272. package/dist/parquetjs/codecs/delta.d.ts.map +1 -0
  273. package/dist/parquetjs/codecs/delta.js +788 -0
  274. package/dist/parquetjs/codecs/delta.js.map +1 -0
  275. package/dist/parquetjs/codecs/dictionary.d.ts +5 -2
  276. package/dist/parquetjs/codecs/dictionary.d.ts.map +1 -1
  277. package/dist/parquetjs/codecs/dictionary.js +16 -4
  278. package/dist/parquetjs/codecs/dictionary.js.map +1 -1
  279. package/dist/parquetjs/codecs/index.d.ts.map +1 -1
  280. package/dist/parquetjs/codecs/index.js +22 -0
  281. package/dist/parquetjs/codecs/index.js.map +1 -1
  282. package/dist/parquetjs/codecs/plain.d.ts +3 -3
  283. package/dist/parquetjs/codecs/plain.d.ts.map +1 -1
  284. package/dist/parquetjs/codecs/plain.js +231 -78
  285. package/dist/parquetjs/codecs/plain.js.map +1 -1
  286. package/dist/parquetjs/codecs/rle.d.ts +7 -3
  287. package/dist/parquetjs/codecs/rle.d.ts.map +1 -1
  288. package/dist/parquetjs/codecs/rle.js +613 -69
  289. package/dist/parquetjs/codecs/rle.js.map +1 -1
  290. package/dist/parquetjs/compression.d.ts +16 -8
  291. package/dist/parquetjs/compression.d.ts.map +1 -1
  292. package/dist/parquetjs/compression.js +114 -141
  293. package/dist/parquetjs/compression.js.map +1 -1
  294. package/dist/parquetjs/encoder/dictionary-planner.d.ts +17 -0
  295. package/dist/parquetjs/encoder/dictionary-planner.d.ts.map +1 -0
  296. package/dist/parquetjs/encoder/dictionary-planner.js +67 -0
  297. package/dist/parquetjs/encoder/dictionary-planner.js.map +1 -0
  298. package/dist/parquetjs/encoder/page-planner.d.ts +11 -0
  299. package/dist/parquetjs/encoder/page-planner.d.ts.map +1 -0
  300. package/dist/parquetjs/encoder/page-planner.js +60 -0
  301. package/dist/parquetjs/encoder/page-planner.js.map +1 -0
  302. package/dist/parquetjs/encoder/parquet-encoder.d.ts +54 -5
  303. package/dist/parquetjs/encoder/parquet-encoder.d.ts.map +1 -1
  304. package/dist/parquetjs/encoder/parquet-encoder.js +930 -66
  305. package/dist/parquetjs/encoder/parquet-encoder.js.map +1 -1
  306. package/dist/parquetjs/parquet-thrift/BoundingBox.d.ts +29 -0
  307. package/dist/parquetjs/parquet-thrift/BoundingBox.d.ts.map +1 -0
  308. package/dist/parquetjs/parquet-thrift/BoundingBox.js +85 -0
  309. package/dist/parquetjs/parquet-thrift/BoundingBox.js.map +1 -0
  310. package/dist/parquetjs/parquet-thrift/BsonType.d.ts +1 -1
  311. package/dist/parquetjs/parquet-thrift/BsonType.d.ts.map +1 -1
  312. package/dist/parquetjs/parquet-thrift/BsonType.js +1 -1
  313. package/dist/parquetjs/parquet-thrift/BsonType.js.map +1 -1
  314. package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts +7 -2
  315. package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts.map +1 -1
  316. package/dist/parquetjs/parquet-thrift/ColumnChunk.js +37 -2
  317. package/dist/parquetjs/parquet-thrift/ColumnChunk.js.map +1 -1
  318. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.d.ts +35 -0
  319. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.d.ts.map +1 -0
  320. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.js +135 -0
  321. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.js.map +1 -0
  322. package/dist/parquetjs/parquet-thrift/ColumnIndex.d.ts +6 -6
  323. package/dist/parquetjs/parquet-thrift/ColumnIndex.d.ts.map +1 -1
  324. package/dist/parquetjs/parquet-thrift/ColumnIndex.js +3 -3
  325. package/dist/parquetjs/parquet-thrift/ColumnIndex.js.map +1 -1
  326. package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts +12 -2
  327. package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts.map +1 -1
  328. package/dist/parquetjs/parquet-thrift/ColumnMetaData.js +75 -2
  329. package/dist/parquetjs/parquet-thrift/ColumnMetaData.js.map +1 -1
  330. package/dist/parquetjs/parquet-thrift/ColumnOrder.d.ts +1 -1
  331. package/dist/parquetjs/parquet-thrift/ColumnOrder.d.ts.map +1 -1
  332. package/dist/parquetjs/parquet-thrift/ColumnOrder.js +1 -1
  333. package/dist/parquetjs/parquet-thrift/ColumnOrder.js.map +1 -1
  334. package/dist/parquetjs/parquet-thrift/DataPageHeader.d.ts +1 -1
  335. package/dist/parquetjs/parquet-thrift/DataPageHeader.d.ts.map +1 -1
  336. package/dist/parquetjs/parquet-thrift/DataPageHeader.js +1 -1
  337. package/dist/parquetjs/parquet-thrift/DataPageHeader.js.map +1 -1
  338. package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.d.ts +1 -1
  339. package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.d.ts.map +1 -1
  340. package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.js +1 -1
  341. package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.js.map +1 -1
  342. package/dist/parquetjs/parquet-thrift/DateType.d.ts +1 -1
  343. package/dist/parquetjs/parquet-thrift/DateType.d.ts.map +1 -1
  344. package/dist/parquetjs/parquet-thrift/DateType.js +1 -1
  345. package/dist/parquetjs/parquet-thrift/DateType.js.map +1 -1
  346. package/dist/parquetjs/parquet-thrift/DecimalType.d.ts +1 -1
  347. package/dist/parquetjs/parquet-thrift/DecimalType.d.ts.map +1 -1
  348. package/dist/parquetjs/parquet-thrift/DecimalType.js +1 -1
  349. package/dist/parquetjs/parquet-thrift/DecimalType.js.map +1 -1
  350. package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.d.ts +1 -1
  351. package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.d.ts.map +1 -1
  352. package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.js +1 -1
  353. package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.js.map +1 -1
  354. package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.d.ts +8 -0
  355. package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.d.ts.map +1 -0
  356. package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.js +14 -0
  357. package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.js.map +1 -0
  358. package/dist/parquetjs/parquet-thrift/Encoding.d.ts +2 -1
  359. package/dist/parquetjs/parquet-thrift/Encoding.d.ts.map +1 -1
  360. package/dist/parquetjs/parquet-thrift/Encoding.js +1 -0
  361. package/dist/parquetjs/parquet-thrift/Encoding.js.map +1 -1
  362. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.d.ts +22 -0
  363. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.d.ts.map +1 -0
  364. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.js +111 -0
  365. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.js.map +1 -0
  366. package/dist/parquetjs/parquet-thrift/EnumType.d.ts +1 -1
  367. package/dist/parquetjs/parquet-thrift/EnumType.d.ts.map +1 -1
  368. package/dist/parquetjs/parquet-thrift/EnumType.js +1 -1
  369. package/dist/parquetjs/parquet-thrift/EnumType.js.map +1 -1
  370. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.d.ts +16 -0
  371. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.d.ts.map +1 -0
  372. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.js +57 -0
  373. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.js.map +1 -0
  374. package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts +7 -2
  375. package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts.map +1 -1
  376. package/dist/parquetjs/parquet-thrift/FileMetaData.js +37 -2
  377. package/dist/parquetjs/parquet-thrift/FileMetaData.js.map +1 -1
  378. package/dist/parquetjs/parquet-thrift/Float16Type.d.ts +9 -0
  379. package/dist/parquetjs/parquet-thrift/Float16Type.d.ts.map +1 -0
  380. package/dist/parquetjs/parquet-thrift/Float16Type.js +27 -0
  381. package/dist/parquetjs/parquet-thrift/Float16Type.js.map +1 -0
  382. package/dist/parquetjs/parquet-thrift/GeographyType.d.ts +14 -0
  383. package/dist/parquetjs/parquet-thrift/GeographyType.d.ts.map +1 -0
  384. package/dist/parquetjs/parquet-thrift/GeographyType.js +51 -0
  385. package/dist/parquetjs/parquet-thrift/GeographyType.js.map +1 -0
  386. package/dist/parquetjs/parquet-thrift/GeometryType.d.ts +11 -0
  387. package/dist/parquetjs/parquet-thrift/GeometryType.d.ts.map +1 -0
  388. package/dist/parquetjs/parquet-thrift/GeometryType.js +41 -0
  389. package/dist/parquetjs/parquet-thrift/GeometryType.js.map +1 -0
  390. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.d.ts +18 -0
  391. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.d.ts.map +1 -0
  392. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.js +62 -0
  393. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.js.map +1 -0
  394. package/dist/parquetjs/parquet-thrift/IndexPageHeader.d.ts +1 -1
  395. package/dist/parquetjs/parquet-thrift/IndexPageHeader.d.ts.map +1 -1
  396. package/dist/parquetjs/parquet-thrift/IndexPageHeader.js +1 -1
  397. package/dist/parquetjs/parquet-thrift/IndexPageHeader.js.map +1 -1
  398. package/dist/parquetjs/parquet-thrift/IntType.d.ts +1 -1
  399. package/dist/parquetjs/parquet-thrift/IntType.d.ts.map +1 -1
  400. package/dist/parquetjs/parquet-thrift/IntType.js +1 -1
  401. package/dist/parquetjs/parquet-thrift/IntType.js.map +1 -1
  402. package/dist/parquetjs/parquet-thrift/JsonType.d.ts +1 -1
  403. package/dist/parquetjs/parquet-thrift/JsonType.d.ts.map +1 -1
  404. package/dist/parquetjs/parquet-thrift/JsonType.js +1 -1
  405. package/dist/parquetjs/parquet-thrift/JsonType.js.map +1 -1
  406. package/dist/parquetjs/parquet-thrift/KeyValue.d.ts +1 -1
  407. package/dist/parquetjs/parquet-thrift/KeyValue.d.ts.map +1 -1
  408. package/dist/parquetjs/parquet-thrift/KeyValue.js +1 -1
  409. package/dist/parquetjs/parquet-thrift/KeyValue.js.map +1 -1
  410. package/dist/parquetjs/parquet-thrift/ListType.d.ts +1 -1
  411. package/dist/parquetjs/parquet-thrift/ListType.d.ts.map +1 -1
  412. package/dist/parquetjs/parquet-thrift/ListType.js +1 -1
  413. package/dist/parquetjs/parquet-thrift/ListType.js.map +1 -1
  414. package/dist/parquetjs/parquet-thrift/LogicalType.d.ts +17 -1
  415. package/dist/parquetjs/parquet-thrift/LogicalType.d.ts.map +1 -1
  416. package/dist/parquetjs/parquet-thrift/LogicalType.js +97 -1
  417. package/dist/parquetjs/parquet-thrift/LogicalType.js.map +1 -1
  418. package/dist/parquetjs/parquet-thrift/MapType.d.ts +1 -1
  419. package/dist/parquetjs/parquet-thrift/MapType.d.ts.map +1 -1
  420. package/dist/parquetjs/parquet-thrift/MapType.js +1 -1
  421. package/dist/parquetjs/parquet-thrift/MapType.js.map +1 -1
  422. package/dist/parquetjs/parquet-thrift/MicroSeconds.d.ts +1 -1
  423. package/dist/parquetjs/parquet-thrift/MicroSeconds.d.ts.map +1 -1
  424. package/dist/parquetjs/parquet-thrift/MicroSeconds.js +1 -1
  425. package/dist/parquetjs/parquet-thrift/MicroSeconds.js.map +1 -1
  426. package/dist/parquetjs/parquet-thrift/MilliSeconds.d.ts +1 -1
  427. package/dist/parquetjs/parquet-thrift/MilliSeconds.d.ts.map +1 -1
  428. package/dist/parquetjs/parquet-thrift/MilliSeconds.js +1 -1
  429. package/dist/parquetjs/parquet-thrift/MilliSeconds.js.map +1 -1
  430. package/dist/parquetjs/parquet-thrift/NanoSeconds.d.ts +9 -0
  431. package/dist/parquetjs/parquet-thrift/NanoSeconds.d.ts.map +1 -0
  432. package/dist/parquetjs/parquet-thrift/NanoSeconds.js +27 -0
  433. package/dist/parquetjs/parquet-thrift/NanoSeconds.js.map +1 -0
  434. package/dist/parquetjs/parquet-thrift/NullType.d.ts +1 -1
  435. package/dist/parquetjs/parquet-thrift/NullType.d.ts.map +1 -1
  436. package/dist/parquetjs/parquet-thrift/NullType.js +1 -1
  437. package/dist/parquetjs/parquet-thrift/NullType.js.map +1 -1
  438. package/dist/parquetjs/parquet-thrift/OffsetIndex.d.ts +1 -1
  439. package/dist/parquetjs/parquet-thrift/OffsetIndex.d.ts.map +1 -1
  440. package/dist/parquetjs/parquet-thrift/OffsetIndex.js +1 -1
  441. package/dist/parquetjs/parquet-thrift/OffsetIndex.js.map +1 -1
  442. package/dist/parquetjs/parquet-thrift/PageEncodingStats.d.ts +1 -1
  443. package/dist/parquetjs/parquet-thrift/PageEncodingStats.d.ts.map +1 -1
  444. package/dist/parquetjs/parquet-thrift/PageEncodingStats.js +1 -1
  445. package/dist/parquetjs/parquet-thrift/PageEncodingStats.js.map +1 -1
  446. package/dist/parquetjs/parquet-thrift/PageHeader.d.ts +1 -1
  447. package/dist/parquetjs/parquet-thrift/PageHeader.d.ts.map +1 -1
  448. package/dist/parquetjs/parquet-thrift/PageHeader.js +1 -1
  449. package/dist/parquetjs/parquet-thrift/PageHeader.js.map +1 -1
  450. package/dist/parquetjs/parquet-thrift/PageLocation.d.ts +2 -2
  451. package/dist/parquetjs/parquet-thrift/PageLocation.d.ts.map +1 -1
  452. package/dist/parquetjs/parquet-thrift/PageLocation.js +2 -2
  453. package/dist/parquetjs/parquet-thrift/PageLocation.js.map +1 -1
  454. package/dist/parquetjs/parquet-thrift/RowGroup.d.ts +2 -2
  455. package/dist/parquetjs/parquet-thrift/RowGroup.d.ts.map +1 -1
  456. package/dist/parquetjs/parquet-thrift/RowGroup.js +2 -2
  457. package/dist/parquetjs/parquet-thrift/RowGroup.js.map +1 -1
  458. package/dist/parquetjs/parquet-thrift/SchemaElement.d.ts +1 -1
  459. package/dist/parquetjs/parquet-thrift/SchemaElement.d.ts.map +1 -1
  460. package/dist/parquetjs/parquet-thrift/SchemaElement.js +1 -1
  461. package/dist/parquetjs/parquet-thrift/SchemaElement.js.map +1 -1
  462. package/dist/parquetjs/parquet-thrift/SizeStatistics.d.ts +26 -0
  463. package/dist/parquetjs/parquet-thrift/SizeStatistics.d.ts.map +1 -0
  464. package/dist/parquetjs/parquet-thrift/SizeStatistics.js +85 -0
  465. package/dist/parquetjs/parquet-thrift/SizeStatistics.js.map +1 -0
  466. package/dist/parquetjs/parquet-thrift/SortingColumn.d.ts +1 -1
  467. package/dist/parquetjs/parquet-thrift/SortingColumn.d.ts.map +1 -1
  468. package/dist/parquetjs/parquet-thrift/SortingColumn.js +1 -1
  469. package/dist/parquetjs/parquet-thrift/SortingColumn.js.map +1 -1
  470. package/dist/parquetjs/parquet-thrift/Statistics.d.ts +14 -10
  471. package/dist/parquetjs/parquet-thrift/Statistics.d.ts.map +1 -1
  472. package/dist/parquetjs/parquet-thrift/Statistics.js +38 -2
  473. package/dist/parquetjs/parquet-thrift/Statistics.js.map +1 -1
  474. package/dist/parquetjs/parquet-thrift/StringType.d.ts +1 -1
  475. package/dist/parquetjs/parquet-thrift/StringType.d.ts.map +1 -1
  476. package/dist/parquetjs/parquet-thrift/StringType.js +1 -1
  477. package/dist/parquetjs/parquet-thrift/StringType.js.map +1 -1
  478. package/dist/parquetjs/parquet-thrift/TimeType.d.ts +1 -1
  479. package/dist/parquetjs/parquet-thrift/TimeType.d.ts.map +1 -1
  480. package/dist/parquetjs/parquet-thrift/TimeType.js +1 -1
  481. package/dist/parquetjs/parquet-thrift/TimeType.js.map +1 -1
  482. package/dist/parquetjs/parquet-thrift/TimeUnit.d.ts +5 -1
  483. package/dist/parquetjs/parquet-thrift/TimeUnit.d.ts.map +1 -1
  484. package/dist/parquetjs/parquet-thrift/TimeUnit.js +25 -1
  485. package/dist/parquetjs/parquet-thrift/TimeUnit.js.map +1 -1
  486. package/dist/parquetjs/parquet-thrift/TimestampType.d.ts +1 -1
  487. package/dist/parquetjs/parquet-thrift/TimestampType.d.ts.map +1 -1
  488. package/dist/parquetjs/parquet-thrift/TimestampType.js +1 -1
  489. package/dist/parquetjs/parquet-thrift/TimestampType.js.map +1 -1
  490. package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.d.ts +1 -1
  491. package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.d.ts.map +1 -1
  492. package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.js +1 -1
  493. package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.js.map +1 -1
  494. package/dist/parquetjs/parquet-thrift/UUIDType.d.ts +1 -1
  495. package/dist/parquetjs/parquet-thrift/UUIDType.d.ts.map +1 -1
  496. package/dist/parquetjs/parquet-thrift/UUIDType.js +1 -1
  497. package/dist/parquetjs/parquet-thrift/UUIDType.js.map +1 -1
  498. package/dist/parquetjs/parquet-thrift/VariantType.d.ts +11 -0
  499. package/dist/parquetjs/parquet-thrift/VariantType.d.ts.map +1 -0
  500. package/dist/parquetjs/parquet-thrift/VariantType.js +41 -0
  501. package/dist/parquetjs/parquet-thrift/VariantType.js.map +1 -0
  502. package/dist/parquetjs/parquet-thrift/index.d.ts +12 -2
  503. package/dist/parquetjs/parquet-thrift/index.d.ts.map +1 -1
  504. package/dist/parquetjs/parquet-thrift/index.js +12 -2
  505. package/dist/parquetjs/parquet-thrift/index.js.map +1 -1
  506. package/dist/parquetjs/parser/decoders.d.ts +23 -3
  507. package/dist/parquetjs/parser/decoders.d.ts.map +1 -1
  508. package/dist/parquetjs/parser/decoders.js +687 -119
  509. package/dist/parquetjs/parser/decoders.js.map +1 -1
  510. package/dist/parquetjs/parser/encoded-pages.d.ts +24 -0
  511. package/dist/parquetjs/parser/encoded-pages.d.ts.map +1 -0
  512. package/dist/parquetjs/parser/encoded-pages.js +206 -0
  513. package/dist/parquetjs/parser/encoded-pages.js.map +1 -0
  514. package/dist/parquetjs/parser/parquet-reader.d.ts +111 -10
  515. package/dist/parquetjs/parser/parquet-reader.d.ts.map +1 -1
  516. package/dist/parquetjs/parser/parquet-reader.js +836 -69
  517. package/dist/parquetjs/parser/parquet-reader.js.map +1 -1
  518. package/dist/parquetjs/schema/declare.d.ts +83 -10
  519. package/dist/parquetjs/schema/declare.d.ts.map +1 -1
  520. package/dist/parquetjs/schema/declare.js.map +1 -1
  521. package/dist/parquetjs/schema/schema.d.ts +3 -0
  522. package/dist/parquetjs/schema/schema.d.ts.map +1 -1
  523. package/dist/parquetjs/schema/schema.js +31 -3
  524. package/dist/parquetjs/schema/schema.js.map +1 -1
  525. package/dist/parquetjs/schema/shred.d.ts +2 -0
  526. package/dist/parquetjs/schema/shred.d.ts.map +1 -1
  527. package/dist/parquetjs/schema/shred.js +285 -29
  528. package/dist/parquetjs/schema/shred.js.map +1 -1
  529. package/dist/parquetjs/schema/types.d.ts.map +1 -1
  530. package/dist/parquetjs/schema/types.js +290 -71
  531. package/dist/parquetjs/schema/types.js.map +1 -1
  532. package/dist/parquetjs/schema/variant.d.ts +3 -0
  533. package/dist/parquetjs/schema/variant.d.ts.map +1 -0
  534. package/dist/parquetjs/schema/variant.js +248 -0
  535. package/dist/parquetjs/schema/variant.js.map +1 -0
  536. package/dist/parquetjs/utils/binary-utils.d.ts +41 -0
  537. package/dist/parquetjs/utils/binary-utils.d.ts.map +1 -0
  538. package/dist/parquetjs/utils/binary-utils.js +106 -0
  539. package/dist/parquetjs/utils/binary-utils.js.map +1 -0
  540. package/dist/parquetjs/utils/crc32.d.ts +3 -0
  541. package/dist/parquetjs/utils/crc32.d.ts.map +1 -0
  542. package/dist/parquetjs/utils/crc32.js +15 -0
  543. package/dist/parquetjs/utils/crc32.js.map +1 -0
  544. package/dist/parquetjs/utils/file-utils.d.ts +1 -1
  545. package/dist/parquetjs/utils/file-utils.d.ts.map +1 -1
  546. package/dist/parquetjs/utils/file-utils.js +3 -3
  547. package/dist/parquetjs/utils/file-utils.js.map +1 -1
  548. package/dist/parquetjs/utils/read-utils.d.ts +25 -7
  549. package/dist/parquetjs/utils/read-utils.d.ts.map +1 -1
  550. package/dist/parquetjs/utils/read-utils.js +66 -39
  551. package/dist/parquetjs/utils/read-utils.js.map +1 -1
  552. package/dist/parquetjs/utils/thrift-runtime.d.ts +106 -0
  553. package/dist/parquetjs/utils/thrift-runtime.d.ts.map +1 -0
  554. package/dist/parquetjs/utils/thrift-runtime.js +50 -0
  555. package/dist/parquetjs/utils/thrift-runtime.js.map +1 -0
  556. package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.d.ts +78 -0
  557. package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.d.ts.map +1 -0
  558. package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.js +213 -0
  559. package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.js.map +1 -0
  560. package/dist/parquetjs/utils/uint8-array-compact-protocol.d.ts +80 -0
  561. package/dist/parquetjs/utils/uint8-array-compact-protocol.d.ts.map +1 -0
  562. package/dist/parquetjs/utils/uint8-array-compact-protocol.js +276 -0
  563. package/dist/parquetjs/utils/uint8-array-compact-protocol.js.map +1 -0
  564. package/dist/parquetjs/utils/uint8-array-transport.d.ts +25 -0
  565. package/dist/parquetjs/utils/uint8-array-transport.d.ts.map +1 -0
  566. package/dist/parquetjs/utils/uint8-array-transport.js +62 -0
  567. package/dist/parquetjs/utils/uint8-array-transport.js.map +1 -0
  568. package/dist/polyfills/process.d.ts +12 -0
  569. package/dist/polyfills/process.d.ts.map +1 -0
  570. package/dist/polyfills/process.js +17 -0
  571. package/dist/polyfills/process.js.map +1 -0
  572. package/dist/unbundled.d.ts +10 -0
  573. package/dist/unbundled.d.ts.map +1 -0
  574. package/dist/unbundled.js +11 -0
  575. package/dist/unbundled.js.map +1 -0
  576. package/dist/wasm.d.ts +3 -0
  577. package/dist/wasm.d.ts.map +1 -0
  578. package/dist/wasm.js +6 -0
  579. package/dist/wasm.js.map +1 -0
  580. package/dist/workers/parquet-source-worker.d.ts +2 -0
  581. package/dist/workers/parquet-source-worker.d.ts.map +1 -0
  582. package/dist/workers/parquet-source-worker.js +17 -0
  583. package/dist/workers/parquet-source-worker.js.map +1 -0
  584. package/dist/workers/parquet-worker-node.d.ts +2 -0
  585. package/dist/workers/parquet-worker-node.d.ts.map +1 -0
  586. package/dist/workers/parquet-worker-node.js +5 -0
  587. package/dist/workers/parquet-worker-node.js.map +1 -0
  588. package/dist/workers/parquet-worker.d.ts +2 -0
  589. package/dist/workers/parquet-worker.d.ts.map +1 -0
  590. package/dist/workers/parquet-worker.js +27 -0
  591. package/dist/workers/parquet-worker.js.map +1 -0
  592. package/package.json +84 -40
  593. package/src/bundled.ts +43 -0
  594. package/src/delta-format.ts +12 -0
  595. package/src/delta-source-loader-types.ts +40 -0
  596. package/src/delta-source.ts +305 -0
  597. package/src/delta-types.ts +40 -0
  598. package/src/geoparquet-loader-with-parser.ts +95 -0
  599. package/src/geoparquet-loader.ts +54 -0
  600. package/src/iceberg-rest-catalog.ts +122 -0
  601. package/src/iceberg-table-source.ts +1037 -0
  602. package/src/iceberg-types.ts +165 -0
  603. package/src/index.ts +105 -26
  604. package/src/lib/arrow/convert-columns-to-row-group.ts +3 -0
  605. package/src/lib/arrow/convert-schema-from-parquet.ts +193 -32
  606. package/src/lib/arrow/convert-schema-to-parquet.ts +16 -7
  607. package/src/lib/arrow/create-nested-arrow-vector.ts +705 -0
  608. package/src/lib/constants.ts +0 -2
  609. package/src/lib/encoders/encode-arrow-to-parquet.ts +10 -16
  610. package/src/lib/encoders/encode-table-to-parquet-js.ts +526 -0
  611. package/src/lib/geo/geoparquet-covering.ts +201 -0
  612. package/src/lib/geo/geospatial-metadata.ts +547 -0
  613. package/src/lib/parquet-bloom-filter-planner.ts +86 -0
  614. package/src/lib/parquet-bloom-filter.ts +345 -0
  615. package/src/lib/parquet-encryption.ts +344 -0
  616. package/src/lib/parquet-page-index.ts +727 -0
  617. package/src/lib/parquet-predicate.ts +410 -0
  618. package/src/lib/parquet-source-worker-client.ts +41 -0
  619. package/src/lib/parquet-source-worker-decoder.ts +252 -0
  620. package/src/lib/parquet-source-worker-file.ts +56 -0
  621. package/src/lib/parquet-source-worker-types.ts +132 -0
  622. package/src/lib/parquet-worker-transport.ts +57 -0
  623. package/src/lib/parsers/convert-parquet-tables.ts +39 -0
  624. package/src/lib/parsers/get-parquet-schema.ts +6 -2
  625. package/src/lib/parsers/parse-geoparquet-to-geojson.ts +27 -14
  626. package/src/lib/parsers/parse-parquet-tables.ts +82 -0
  627. package/src/lib/parsers/parse-parquet-to-arrow-js.ts +1213 -0
  628. package/src/lib/parsers/parse-parquet-to-arrow.ts +62 -36
  629. package/src/lib/parsers/parse-parquet-to-columns.ts +9 -8
  630. package/src/lib/parsers/parse-parquet-to-json.ts +119 -35
  631. package/src/lib/slice-parquet-batch.ts +32 -0
  632. package/src/lib/sources/parquet-range-file.ts +365 -0
  633. package/src/lib/sources/single-batch-queue.ts +95 -0
  634. package/src/lib/utils/load-wasm-node.ts +25 -0
  635. package/src/lib/utils/load-wasm.ts +36 -13
  636. package/src/lib/utils/make-stream-iterator.ts +126 -14
  637. package/src/lib/utils/normalize-parquet-options.ts +30 -0
  638. package/src/parquet-dataset-source.ts +800 -0
  639. package/src/parquet-encoded-page-types.ts +122 -0
  640. package/src/parquet-format.ts +2 -0
  641. package/src/parquet-js-loader-types.ts +53 -0
  642. package/src/parquet-js-loader.ts +76 -19
  643. package/src/parquet-js-writer.ts +98 -0
  644. package/src/parquet-loader-base.ts +34 -0
  645. package/src/parquet-loader-options.ts +62 -0
  646. package/src/parquet-loader-types.ts +35 -0
  647. package/src/parquet-loader.ts +115 -0
  648. package/src/parquet-source-capabilities.ts +73 -0
  649. package/src/parquet-source-loader-types.ts +97 -0
  650. package/src/parquet-source-loader.ts +2254 -0
  651. package/src/parquet-source-types.ts +627 -0
  652. package/src/parquet-source-worker-factory.ts +8 -0
  653. package/src/parquet-source-worker-url.ts +23 -0
  654. package/src/parquet-worker-url.ts +23 -0
  655. package/src/parquet-writer.ts +42 -0
  656. package/src/parquetjs/LICENSE +23 -1
  657. package/src/parquetjs/codecs/byte-stream-split.ts +221 -0
  658. package/src/parquetjs/codecs/declare.ts +62 -3
  659. package/src/parquetjs/codecs/delta.ts +1047 -0
  660. package/src/parquetjs/codecs/dictionary.ts +22 -4
  661. package/src/parquetjs/codecs/index.ts +22 -0
  662. package/src/parquetjs/codecs/plain.ts +317 -89
  663. package/src/parquetjs/codecs/rle.ts +852 -82
  664. package/src/parquetjs/compression.ts +137 -156
  665. package/src/parquetjs/encoder/dictionary-planner.ts +104 -0
  666. package/src/parquetjs/encoder/page-planner.ts +83 -0
  667. package/src/parquetjs/encoder/parquet-encoder.ts +1374 -95
  668. package/src/parquetjs/parquet-thrift/BoundingBox.ts +108 -0
  669. package/src/parquetjs/parquet-thrift/BsonType.ts +1 -1
  670. package/src/parquetjs/parquet-thrift/ColumnChunk.ts +37 -2
  671. package/src/parquetjs/parquet-thrift/ColumnCryptoMetaData.ts +139 -0
  672. package/src/parquetjs/parquet-thrift/ColumnIndex.ts +13 -13
  673. package/src/parquetjs/parquet-thrift/ColumnMetaData.ts +75 -2
  674. package/src/parquetjs/parquet-thrift/ColumnOrder.ts +1 -1
  675. package/src/parquetjs/parquet-thrift/DataPageHeader.ts +1 -1
  676. package/src/parquetjs/parquet-thrift/DataPageHeaderV2.ts +1 -1
  677. package/src/parquetjs/parquet-thrift/DateType.ts +1 -1
  678. package/src/parquetjs/parquet-thrift/DecimalType.ts +1 -1
  679. package/src/parquetjs/parquet-thrift/DictionaryPageHeader.ts +1 -1
  680. package/src/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.ts +13 -0
  681. package/src/parquetjs/parquet-thrift/Encoding.ts +2 -1
  682. package/src/parquetjs/parquet-thrift/EncryptionAlgorithm.ts +127 -0
  683. package/src/parquetjs/parquet-thrift/EnumType.ts +1 -1
  684. package/src/parquetjs/parquet-thrift/FileCryptoMetaData.ts +60 -0
  685. package/src/parquetjs/parquet-thrift/FileMetaData.ts +37 -2
  686. package/src/parquetjs/parquet-thrift/Float16Type.ts +28 -0
  687. package/src/parquetjs/parquet-thrift/GeographyType.ts +54 -0
  688. package/src/parquetjs/parquet-thrift/GeometryType.ts +43 -0
  689. package/src/parquetjs/parquet-thrift/GeospatialStatistics.ts +68 -0
  690. package/src/parquetjs/parquet-thrift/IndexPageHeader.ts +1 -1
  691. package/src/parquetjs/parquet-thrift/IntType.ts +1 -1
  692. package/src/parquetjs/parquet-thrift/JsonType.ts +1 -1
  693. package/src/parquetjs/parquet-thrift/KeyValue.ts +1 -1
  694. package/src/parquetjs/parquet-thrift/ListType.ts +1 -1
  695. package/src/parquetjs/parquet-thrift/LogicalType.ts +97 -1
  696. package/src/parquetjs/parquet-thrift/MapType.ts +1 -1
  697. package/src/parquetjs/parquet-thrift/MicroSeconds.ts +1 -1
  698. package/src/parquetjs/parquet-thrift/MilliSeconds.ts +1 -1
  699. package/src/parquetjs/parquet-thrift/NanoSeconds.ts +28 -0
  700. package/src/parquetjs/parquet-thrift/NullType.ts +1 -1
  701. package/src/parquetjs/parquet-thrift/OffsetIndex.ts +1 -1
  702. package/src/parquetjs/parquet-thrift/PageEncodingStats.ts +1 -1
  703. package/src/parquetjs/parquet-thrift/PageHeader.ts +1 -1
  704. package/src/parquetjs/parquet-thrift/PageLocation.ts +2 -2
  705. package/src/parquetjs/parquet-thrift/RowGroup.ts +2 -2
  706. package/src/parquetjs/parquet-thrift/SchemaElement.ts +1 -1
  707. package/src/parquetjs/parquet-thrift/SizeStatistics.ts +98 -0
  708. package/src/parquetjs/parquet-thrift/SortingColumn.ts +1 -1
  709. package/src/parquetjs/parquet-thrift/Statistics.ts +50 -14
  710. package/src/parquetjs/parquet-thrift/StringType.ts +1 -1
  711. package/src/parquetjs/parquet-thrift/TimeType.ts +1 -1
  712. package/src/parquetjs/parquet-thrift/TimeUnit.ts +25 -1
  713. package/src/parquetjs/parquet-thrift/TimestampType.ts +1 -1
  714. package/src/parquetjs/parquet-thrift/TypeDefinedOrder.ts +1 -1
  715. package/src/parquetjs/parquet-thrift/UUIDType.ts +1 -1
  716. package/src/parquetjs/parquet-thrift/VariantType.ts +43 -0
  717. package/src/parquetjs/parquet-thrift/index.ts +12 -4
  718. package/src/parquetjs/parser/decoders.ts +922 -143
  719. package/src/parquetjs/parser/encoded-pages.ts +278 -0
  720. package/src/parquetjs/parser/parquet-reader.ts +1334 -83
  721. package/src/parquetjs/schema/declare.ts +121 -9
  722. package/src/parquetjs/schema/schema.ts +33 -3
  723. package/src/parquetjs/schema/shred.ts +345 -41
  724. package/src/parquetjs/schema/types.ts +308 -84
  725. package/src/parquetjs/schema/variant.ts +313 -0
  726. package/src/parquetjs/utils/binary-utils.ts +132 -0
  727. package/src/parquetjs/utils/crc32.ts +15 -0
  728. package/src/parquetjs/utils/file-utils.ts +4 -4
  729. package/src/parquetjs/utils/read-utils.ts +77 -51
  730. package/src/parquetjs/utils/thrift-runtime.ts +109 -0
  731. package/src/parquetjs/utils/uint8-array-compact-protocol-writer.ts +254 -0
  732. package/src/parquetjs/utils/uint8-array-compact-protocol.ts +322 -0
  733. package/src/parquetjs/utils/uint8-array-transport.ts +79 -0
  734. package/src/polyfills/process.ts +17 -0
  735. package/src/unbundled.ts +47 -0
  736. package/src/wasm.ts +6 -0
  737. package/src/workers/parquet-source-worker.ts +21 -0
  738. package/src/workers/parquet-worker-node.ts +5 -0
  739. package/src/workers/parquet-worker.ts +31 -0
  740. package/dist/parquet-arrow-loader.d.ts +0 -43
  741. package/dist/parquet-arrow-loader.d.ts.map +0 -1
  742. package/dist/parquet-arrow-loader.js +0 -49
  743. package/dist/parquet-arrow-loader.js.map +0 -1
  744. package/dist/parquet-arrow-writer.d.ts +0 -9
  745. package/dist/parquet-arrow-writer.d.ts.map +0 -1
  746. package/dist/parquet-arrow-writer.js +0 -23
  747. package/dist/parquet-arrow-writer.js.map +0 -1
  748. package/dist/parquet-json-loader.d.ts +0 -148
  749. package/dist/parquet-json-loader.d.ts.map +0 -1
  750. package/dist/parquet-json-loader.js +0 -106
  751. package/dist/parquet-json-loader.js.map +0 -1
  752. package/dist/parquet-json-writer.d.ts +0 -19
  753. package/dist/parquet-json-writer.d.ts.map +0 -1
  754. package/dist/parquet-json-writer.js +0 -20
  755. package/dist/parquet-json-writer.js.map +0 -1
  756. package/dist/polyfills/buffer/buffer-polyfill.browser.d.ts +0 -3
  757. package/dist/polyfills/buffer/buffer-polyfill.browser.d.ts.map +0 -1
  758. package/dist/polyfills/buffer/buffer-polyfill.browser.js +0 -12
  759. package/dist/polyfills/buffer/buffer-polyfill.browser.js.map +0 -1
  760. package/dist/polyfills/buffer/buffer-polyfill.node.d.ts +0 -3
  761. package/dist/polyfills/buffer/buffer-polyfill.node.d.ts.map +0 -1
  762. package/dist/polyfills/buffer/buffer-polyfill.node.js +0 -17
  763. package/dist/polyfills/buffer/buffer-polyfill.node.js.map +0 -1
  764. package/dist/polyfills/buffer/buffer.d.ts +0 -222
  765. package/dist/polyfills/buffer/buffer.d.ts.map +0 -1
  766. package/dist/polyfills/buffer/buffer.js +0 -1868
  767. package/dist/polyfills/buffer/buffer.js.map +0 -1
  768. package/dist/polyfills/buffer/index.d.ts +0 -4
  769. package/dist/polyfills/buffer/index.d.ts.map +0 -1
  770. package/dist/polyfills/buffer/index.js +0 -9
  771. package/dist/polyfills/buffer/index.js.map +0 -1
  772. package/dist/polyfills/buffer/install-buffer-polyfill.d.ts +0 -2
  773. package/dist/polyfills/buffer/install-buffer-polyfill.d.ts.map +0 -1
  774. package/dist/polyfills/buffer/install-buffer-polyfill.js +0 -10
  775. package/dist/polyfills/buffer/install-buffer-polyfill.js.map +0 -1
  776. package/dist/workers/parquet-json-worker.d.ts +0 -2
  777. package/dist/workers/parquet-json-worker.d.ts.map +0 -1
  778. package/dist/workers/parquet-json-worker.js +0 -7
  779. package/dist/workers/parquet-json-worker.js.map +0 -1
  780. package/src/parquet-arrow-loader.ts +0 -74
  781. package/src/parquet-arrow-writer.ts +0 -33
  782. package/src/parquet-json-loader.ts +0 -160
  783. package/src/parquet-json-writer.ts +0 -27
  784. package/src/parquetjs/modules.d.ts +0 -25
  785. package/src/polyfills/buffer/buffer-polyfill.browser.ts +0 -14
  786. package/src/polyfills/buffer/buffer-polyfill.node.ts +0 -19
  787. package/src/polyfills/buffer/buffer.ts +0 -2205
  788. package/src/polyfills/buffer/index.ts +0 -11
  789. package/src/polyfills/buffer/install-buffer-polyfill.ts +0 -12
  790. package/src/workers/parquet-json-worker.ts +0 -8
@@ -0,0 +1,2254 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {dehydrateArrowTable, hydrateArrowTable, IndexedArrowTable} from '@loaders.gl/arrow';
6
+ import type {CoreAPI, ReadableFile, SourceLoader} from '@loaders.gl/loader-utils';
7
+ import {
8
+ BlobFile,
9
+ DataSource,
10
+ createScanQueryMetadata,
11
+ emitScanExecutionTelemetry,
12
+ explainTableQuery,
13
+ isBrowser,
14
+ makeTableScanBatch,
15
+ validateTableQueryLimit
16
+ } from '@loaders.gl/loader-utils';
17
+ import type {
18
+ ScanColumnRole,
19
+ ScanExecutionTelemetry,
20
+ ScanQueryMetadata,
21
+ ScanQueryMetadataOptions,
22
+ TableScanSource
23
+ } from '@loaders.gl/loader-utils';
24
+ import type {ArrayType, ArrowTable, Schema} from '@loaders.gl/schema';
25
+ import {convertTable} from '@loaders.gl/schema-utils';
26
+ import {getGeoMetadata} from '@loaders.gl/gis';
27
+
28
+ import {getSchemaFromParquetReader} from './lib/parsers/get-parquet-schema';
29
+ import {
30
+ canGeoParquetRowGroupMatch,
31
+ combineParquetPredicates,
32
+ createGeoParquetBoundingBoxPredicate
33
+ } from './lib/geo/geoparquet-covering';
34
+ import {
35
+ decodeParquetSplitBlockBloomFilter,
36
+ encodeParquetBloomFilterValue,
37
+ hashParquetBloomFilterValue,
38
+ checkParquetSplitBlockBloomFilter
39
+ } from './lib/parquet-bloom-filter';
40
+ import {getParquetBloomFilterProbes} from './lib/parquet-bloom-filter-planner';
41
+ import {
42
+ canParquetRowGroupMatch,
43
+ copyParquetPredicate,
44
+ filterParquetRowIndices,
45
+ gatherParquetColumns,
46
+ getParquetPredicateColumns,
47
+ validateParquetPredicate
48
+ } from './lib/parquet-predicate';
49
+ import {
50
+ createParquetPagePruningPlan,
51
+ getParquetPageReadRanges,
52
+ type ParquetPagePruningPlan
53
+ } from './lib/parquet-page-index';
54
+ import {
55
+ canDecodeParquetSourceOnWorker,
56
+ decodeParquetSourceRowGroupOnWorker
57
+ } from './lib/parquet-source-worker-client';
58
+ import {
59
+ type ParquetSourceWorkerColumnChunk,
60
+ type ParquetSourceWorkerOptions,
61
+ type ParquetSourceWorkerResult
62
+ } from './lib/parquet-source-worker-types';
63
+ import {sliceParquetBatch} from './lib/slice-parquet-batch';
64
+ import {ParquetRangeFile} from './lib/sources/parquet-range-file';
65
+ import {toPrimitive} from './parquetjs/schema/types';
66
+ import type {ParquetType} from './parquetjs/schema/declare';
67
+ import {
68
+ PARQUET_TABLE_QUERY_CAPABILITIES,
69
+ PARQUET_SOURCE_CAPABILITIES,
70
+ type ParquetSourceCapabilities
71
+ } from './parquet-source-capabilities';
72
+ import {ParquetSourceLoader as ParquetSourceLoaderMetadata} from './parquet-source-loader-types';
73
+ import type {
74
+ ParquetEncodedPageBatch,
75
+ ParquetEncodedPageReadOptions,
76
+ ParquetDeferredPageFilter
77
+ } from './parquet-encoded-page-types';
78
+ import type {
79
+ ParquetBatch,
80
+ ParquetColumnChunkMetadata,
81
+ ParquetColumnChunkStatistics,
82
+ ParquetColumnChunkSizeStatistics,
83
+ ParquetGeospatialStatistics,
84
+ ParquetMetadataRequestOptions,
85
+ ParquetObjectVersion,
86
+ ParquetPageScanPlan,
87
+ ParquetPredicate,
88
+ ParquetRowGroupMetadata,
89
+ ParquetSortingColumn,
90
+ ParquetSourceBatch,
91
+ ParquetSourceLoaderOptions,
92
+ ParquetSourceMetadata,
93
+ ParquetSourceReadOptions,
94
+ ParquetSourceExplain,
95
+ ParquetTelemetry,
96
+ ParquetTelemetryEvent
97
+ } from './parquet-source-types';
98
+ export {
99
+ PARQUET_TABLE_QUERY_CAPABILITIES,
100
+ PARQUET_SOURCE_CAPABILITIES,
101
+ type ParquetSourceCapabilities
102
+ } from './parquet-source-capabilities';
103
+ import {preloadCompressions} from './parquetjs/compression';
104
+ import {
105
+ CompressionCodec,
106
+ Encoding,
107
+ type ColumnChunk,
108
+ type FileMetaData,
109
+ type GeospatialStatistics as ParquetThriftGeospatialStatistics,
110
+ type RowGroup,
111
+ type Statistics as ParquetThriftStatistics
112
+ } from './parquetjs/parquet-thrift/index';
113
+ import {ParquetReader} from './parquetjs/parser/parquet-reader';
114
+ import type {ParquetField} from './parquetjs/schema/declare';
115
+ import type {ParquetSchema} from './parquetjs/schema/schema';
116
+ import * as Types from './parquetjs/schema/types';
117
+ import {
118
+ copyUint8Array,
119
+ readDoubleLE,
120
+ readFloatLE,
121
+ readInt32LE,
122
+ readInt64LE,
123
+ readUInt32LE,
124
+ readUInt64LE,
125
+ toUint8Array
126
+ } from './parquetjs/utils/binary-utils';
127
+ import {fieldIndexOf} from './parquetjs/utils/read-utils';
128
+
129
+ export type {
130
+ ParquetEncodedColumnChunk,
131
+ ParquetEncodedPage,
132
+ ParquetEncodedPageBatch,
133
+ ParquetEncodedPageReadOptions,
134
+ ParquetEncodedPageSection,
135
+ ParquetDeferredPageFilter,
136
+ ParquetPageCompressionState
137
+ } from './parquet-encoded-page-types';
138
+ export type {
139
+ ParquetBatch,
140
+ ParquetBatchMetadata,
141
+ ParquetBatchProvenance,
142
+ ParquetBoundingBox,
143
+ ParquetColumnChunkMetadata,
144
+ ParquetColumnChunkStatistics,
145
+ ParquetColumnChunkSizeStatistics,
146
+ ParquetGeospatialBoundingBox,
147
+ ParquetGeospatialStatistics,
148
+ ParquetMetadataRequestOptions,
149
+ ParquetObjectVersion,
150
+ ParquetPageScanPlan,
151
+ ParquetComparisonPredicate,
152
+ ParquetInPredicate,
153
+ ParquetLogicalPredicate,
154
+ ParquetNotPredicate,
155
+ ParquetNullPredicate,
156
+ ParquetPredicate,
157
+ ParquetPredicateProperty,
158
+ ParquetPredicateValue,
159
+ ParquetRangeRequestOptions,
160
+ ParquetReadOptions,
161
+ ParquetRowGroupMetadata,
162
+ ParquetSortingColumn,
163
+ ParquetSourceBatch,
164
+ ParquetSourceLoaderOptions,
165
+ ParquetSourceMetadata,
166
+ ParquetSourceReadOptions,
167
+ ParquetSourceExplain,
168
+ ParquetTelemetry,
169
+ ParquetTelemetryEvent
170
+ } from './parquet-source-types';
171
+
172
+ type ParquetSourceInitialization = {
173
+ /** Random-access file shared by all reads. */
174
+ file: ReadableFile;
175
+ /** TypeScript Parquet reader with a cached footer. */
176
+ reader: ParquetReader;
177
+ /** loaders.gl logical schema. */
178
+ schema: Schema;
179
+ /** TypeScript Parquet decoder schema. */
180
+ parquetSchema: ParquetSchema;
181
+ /** Decoded Thrift footer. */
182
+ fileMetadata: FileMetaData;
183
+ /** Normalized public source metadata. */
184
+ metadata: ParquetSourceMetadata;
185
+ };
186
+
187
+ type ParquetRowGroupReadResult = {
188
+ /** Zero-based source row-group index. */
189
+ rowGroupIndex: number;
190
+ /** Number of logical rows retained for output. */
191
+ rowCount: number;
192
+ /** Exact source row indexes retained by the predicate. */
193
+ rowIndices?: number[];
194
+ /** Caller-thread columns when worker decoding is unavailable or disabled. */
195
+ columns?: Record<string, ArrayType>;
196
+ /** Directly transferable Arrow batches when worker decoding is enabled. */
197
+ workerResult?: ParquetSourceWorkerResult;
198
+ };
199
+
200
+ type SettledParquetRowGroupRead =
201
+ | {
202
+ /** Successfully decoded row group. */
203
+ result: ParquetRowGroupReadResult;
204
+ /** No error occurred. */
205
+ error?: never;
206
+ }
207
+ | {
208
+ /** Row-group read failed. */
209
+ error: unknown;
210
+ /** No result was decoded. */
211
+ result?: never;
212
+ };
213
+
214
+ type ParquetPhysicalRowGroupPlan = {
215
+ /** Predicate including any GeoParquet covering predicate derived from a bounding box. */
216
+ predicate?: ParquetPredicate;
217
+ /** Row groups retained by all physical pruning stages. */
218
+ rowGroupIndices: number[];
219
+ /** Candidate row groups explicitly requested by the caller. */
220
+ requested: number;
221
+ /** Row groups rejected by the caller metadata callback. */
222
+ prunedByCallback: number;
223
+ /** Row groups rejected by native geospatial statistics. */
224
+ prunedBySpatial: number;
225
+ /** Row groups rejected by column or covering statistics. */
226
+ prunedByStatistics: number;
227
+ /** Row groups rejected by split-block Bloom filters. */
228
+ prunedByBloomFilter: number;
229
+ /** Bloom-filter payloads read while planning. */
230
+ bloomFiltersRead: number;
231
+ /** Bloom-filter bytes read while planning. */
232
+ bloomFilterBytesRead: number;
233
+ };
234
+
235
+ const {
236
+ preload: _preloadParquetSourceLoader,
237
+ createDataSource: _createMetadataDataSource,
238
+ ...ParquetSourceLoaderBase
239
+ } = ParquetSourceLoaderMetadata;
240
+
241
+ /** Runtime source factory for reusable, range-backed Parquet access. */
242
+ export const ParquetSourceLoaderWithParser = {
243
+ ...ParquetSourceLoaderBase,
244
+ createDataSource: (
245
+ data: string | Blob,
246
+ options: ParquetSourceLoaderOptions,
247
+ coreApi?: CoreAPI
248
+ ): ParquetSource => new ParquetSource(data, options, coreApi)
249
+ } as const satisfies SourceLoader<ParquetSource>;
250
+
251
+ /** Runtime Parquet source loader exposed by the explicit package subpath. */
252
+ export {ParquetSourceLoaderWithParser as ParquetSourceLoader};
253
+
254
+ /** Reusable Parquet source that caches footer/schema state and selectively reads byte ranges. */
255
+ export class ParquetSource
256
+ extends DataSource<string | Blob, ParquetSourceLoaderOptions>
257
+ implements TableScanSource<ParquetSourceBatch, ParquetPredicate>
258
+ {
259
+ /** Common projection, predicate, limit, streaming, and cancellation capabilities. */
260
+ readonly tableQueryCapabilities = PARQUET_TABLE_QUERY_CAPABILITIES;
261
+ /** Immutable feature support for the current range-backed source implementation. */
262
+ readonly capabilities: ParquetSourceCapabilities = PARQUET_SOURCE_CAPABILITIES;
263
+ /** Shared initialization for this source instance. */
264
+ private initializationPromise: Promise<ParquetSourceInitialization> | null = null;
265
+ /** File allocated during source initialization, including while it is opening. */
266
+ private readableFile: ReadableFile | null = null;
267
+ /** Whether this source has been permanently closed. */
268
+ private closed = false;
269
+ /** Compression module preload shared by all selective reads. */
270
+ private compressionPromise: Promise<void> | null = null;
271
+ /** Read abort controllers used to cancel active iterators when the source closes. */
272
+ private activeReadControllers = new Set<AbortController>();
273
+ /** Cumulative source telemetry counters. */
274
+ private telemetry = createParquetTelemetry();
275
+
276
+ /** Creates a Parquet source backed by strict URL ranges or Blob slices. */
277
+ constructor(data: string | Blob, options: ParquetSourceLoaderOptions, coreApi?: CoreAPI) {
278
+ const wasmUrl = options.parquet?.wasmUrl;
279
+ super(data, options, ParquetSourceLoaderWithParser.defaultOptions, coreApi);
280
+ if (wasmUrl !== undefined) {
281
+ this.options.parquet.wasmUrl = wasmUrl;
282
+ }
283
+ }
284
+
285
+ /** Returns the cached logical schema decoded from the Parquet footer. */
286
+ async getSchema(options: {signal?: AbortSignal} = {}): Promise<Schema> {
287
+ const initialization = await this.getInitialization(options.signal);
288
+ return initialization.schema;
289
+ }
290
+
291
+ /** Returns normalized dataset, row-group, and column-chunk metadata. */
292
+ async getMetadata(options: ParquetMetadataRequestOptions = {}): Promise<ParquetSourceMetadata> {
293
+ const initialization = await this.getInitialization(options.signal);
294
+ if (!options.formatSpecificMetadata) {
295
+ return initialization.metadata;
296
+ }
297
+ return {
298
+ ...initialization.metadata,
299
+ formatSpecificMetadata: initialization.fileMetadata
300
+ };
301
+ }
302
+
303
+ /** Discovers schema, capabilities, and footer statistics without decoding Parquet data pages. */
304
+ async getQueryMetadata(options: ScanQueryMetadataOptions = {}): Promise<ScanQueryMetadata> {
305
+ const [schema, metadata] = await Promise.all([
306
+ this.getSchema({signal: options.signal}),
307
+ this.getMetadata({signal: options.signal})
308
+ ]);
309
+ return createScanQueryMetadata({
310
+ sourceType: 'parquet',
311
+ queryType: 'table',
312
+ execution: {status: 'supported', method: 'read'},
313
+ name: metadata.name,
314
+ schema,
315
+ capabilities: {table: this.tableQueryCapabilities},
316
+ columnRoles: getParquetColumnRoles(schema.fields.map(field => field.name)),
317
+ statistics: {rowCount: metadata.rowCount, byteLength: metadata.fileByteLength}
318
+ });
319
+ }
320
+
321
+ /** Plans a portable query using Parquet metadata without decoding data pages. */
322
+ async getScanPlan(options: ParquetSourceReadOptions = {}): Promise<ParquetSourceExplain> {
323
+ const readOptions = this.getReadOptions(options);
324
+ const initialization = await this.getInitialization(readOptions.signal);
325
+ const sourceColumnNames = initialization.schema.fields.map(field => field.name);
326
+ const signal = readOptions.signal ?? new AbortController().signal;
327
+ const physicalPlan = await this.planRowGroups(initialization, readOptions, signal);
328
+ const pagePlans = await this.planPages(initialization, readOptions, physicalPlan, signal);
329
+ const explanation = explainTableQuery(
330
+ sourceColumnNames,
331
+ {
332
+ columns: readOptions.columns,
333
+ predicate: physicalPlan.predicate,
334
+ limit: readOptions.limit
335
+ },
336
+ PARQUET_TABLE_QUERY_CAPABILITIES
337
+ );
338
+ return Object.freeze({
339
+ ...explanation,
340
+ source: 'parquet' as const,
341
+ rowGroups: Object.freeze({
342
+ indices: Object.freeze([...physicalPlan.rowGroupIndices]),
343
+ requested: physicalPlan.requested,
344
+ selected: physicalPlan.rowGroupIndices.length,
345
+ prunedByCallback: physicalPlan.prunedByCallback,
346
+ prunedBySpatial: physicalPlan.prunedBySpatial,
347
+ prunedByStatistics: physicalPlan.prunedByStatistics,
348
+ prunedByBloomFilter: physicalPlan.prunedByBloomFilter
349
+ }),
350
+ bloomFilters: Object.freeze({
351
+ read: physicalPlan.bloomFiltersRead,
352
+ bytesRead: physicalPlan.bloomFilterBytesRead
353
+ }),
354
+ pages: Object.freeze({
355
+ rowGroupsPlanned: new Set(pagePlans.map(plan => plan.rowGroupIndex)).size,
356
+ indexesRead: pagePlans.reduce((sum, plan) => sum + plan.indexesRead, 0),
357
+ total: pagePlans.reduce((sum, plan) => sum + plan.totalPages, 0),
358
+ selected: pagePlans.reduce((sum, plan) => sum + plan.selectedPages, 0),
359
+ rowsPruned: pagePlans.reduce((sum, plan) => sum + plan.rowsPruned, 0),
360
+ plans: Object.freeze(pagePlans)
361
+ })
362
+ });
363
+ }
364
+
365
+ /** Explains the common logical query and Parquet-specific physical scan plan. */
366
+ async explain(options: ParquetSourceReadOptions = {}): Promise<ParquetSourceExplain> {
367
+ return await this.getScanPlan(options);
368
+ }
369
+
370
+ /** Executes a previously computed physical plan while preserving its row-group decisions. */
371
+ async *executeScanPlan(
372
+ plan: ParquetSourceExplain,
373
+ options: ParquetSourceReadOptions = {}
374
+ ): AsyncIterable<ParquetSourceBatch> {
375
+ if (plan.source !== 'parquet') {
376
+ throw new Error('ParquetSource can only execute Parquet scan plans');
377
+ }
378
+ const predicateStep = plan.plan.find(step => step.kind === 'filter');
379
+ const limitStep = plan.plan.find(step => step.kind === 'limit');
380
+ yield* this.read({
381
+ ...options,
382
+ columns: options.columns ?? plan.outputColumns,
383
+ predicate:
384
+ options.predicate ??
385
+ (predicateStep?.kind === 'filter' ? predicateStep.predicate : undefined),
386
+ limit: options.limit ?? (limitStep?.kind === 'limit' ? limitStep.limit : undefined),
387
+ rowGroups: plan.rowGroups.indices
388
+ });
389
+ }
390
+
391
+ /** Common scan-architecture alias for selective Parquet reads. */
392
+ scan(options: ParquetSourceReadOptions = {}): AsyncIterable<ParquetSourceBatch> {
393
+ return this.read(options);
394
+ }
395
+
396
+ /**
397
+ * Reads candidate encoded pages without decoding values or constructing Arrow arrays.
398
+ *
399
+ * Footer statistics and Bloom filters prune complete row groups. Any exact predicate or spatial
400
+ * filter is returned as residual work, and its hidden columns are included in the page batch.
401
+ */
402
+ async *readPages(
403
+ options: ParquetEncodedPageReadOptions = {}
404
+ ): AsyncIterable<ParquetEncodedPageBatch> {
405
+ const readOptions = this.getReadOptions({
406
+ rowGroups: options.rowGroups,
407
+ columns: options.columns,
408
+ rowGroupFilter: options.rowGroupFilter,
409
+ predicate: options.predicate,
410
+ bbox: options.bbox,
411
+ geometryColumn: options.geometryColumn,
412
+ signal: options.signal
413
+ });
414
+ const readContext = createReadAbortContext(readOptions.signal);
415
+ this.activeReadControllers.add(readContext.abortController);
416
+ try {
417
+ const initialization = await this.getInitialization(readContext.abortController.signal);
418
+ await this.getCompressionInitialization();
419
+ throwIfAborted(readContext.abortController.signal);
420
+ const physicalPlan = await this.planRowGroups(
421
+ initialization,
422
+ readOptions,
423
+ readContext.abortController.signal
424
+ );
425
+ const projectedColumns = normalizeColumns(readOptions.columns, initialization.schema);
426
+ const predicateColumns = physicalPlan.predicate
427
+ ? getParquetPredicateColumns(physicalPlan.predicate)
428
+ : [];
429
+ const geoMetadata = readOptions.bbox
430
+ ? getGeoMetadata(initialization.schema.metadata)
431
+ : undefined;
432
+ const geometryColumn = readOptions.bbox
433
+ ? readOptions.geometryColumn || geoMetadata?.primary_column
434
+ : undefined;
435
+ const filterColumns = [
436
+ ...new Set([...predicateColumns, ...(geometryColumn ? [geometryColumn] : [])])
437
+ ];
438
+ const decodedColumns =
439
+ projectedColumns.length === 0 ? [] : [...new Set([...projectedColumns, ...filterColumns])];
440
+ const columnList = decodedColumns.map(column => [column]);
441
+ const preserveCompression = new Set(options.preserveCompression ?? []);
442
+ const residualFilter: ParquetDeferredPageFilter | undefined =
443
+ readOptions.predicate || readOptions.bbox
444
+ ? Object.freeze({
445
+ predicate: readOptions.predicate,
446
+ bbox: readOptions.bbox,
447
+ geometryColumn
448
+ })
449
+ : undefined;
450
+ for (const rowGroupIndex of physicalPlan.rowGroupIndices) {
451
+ throwIfAborted(readContext.abortController.signal);
452
+ const columns = await initialization.reader.readRowGroupEncodedPages(
453
+ initialization.parquetSchema,
454
+ initialization.fileMetadata.row_groups[rowGroupIndex],
455
+ columnList,
456
+ preserveCompression,
457
+ readContext.abortController.signal,
458
+ rowGroupIndex
459
+ );
460
+ yield Object.freeze({
461
+ shape: 'parquet-encoded-pages' as const,
462
+ rowGroup: initialization.metadata.rowGroups[rowGroupIndex],
463
+ projectedColumns: Object.freeze([...projectedColumns]),
464
+ filterColumns: Object.freeze(filterColumns),
465
+ columns,
466
+ residualFilter
467
+ });
468
+ }
469
+ } finally {
470
+ readContext.abortController.abort();
471
+ readContext.removeSignalListener();
472
+ this.activeReadControllers.delete(readContext.abortController);
473
+ }
474
+ }
475
+
476
+ /** Returns a copy of cumulative transport, decode, conversion, and pruning telemetry. */
477
+ getTelemetry(): ParquetTelemetry {
478
+ return Object.freeze({...this.telemetry});
479
+ }
480
+
481
+ /** Selectively fetches row groups and columns as ordered Arrow batches with source provenance. */
482
+ async *read(options: ParquetSourceReadOptions = {}): AsyncIterable<ParquetSourceBatch> {
483
+ const startedAt = Date.now();
484
+ const telemetryBefore = this.getTelemetry();
485
+ const readOptions = this.getReadOptions(options);
486
+ if (readOptions.limit === 0) {
487
+ emitScanExecutionTelemetry(
488
+ options.onTelemetry,
489
+ createParquetScanExecutionTelemetry(
490
+ telemetryBefore,
491
+ this.getTelemetry(),
492
+ startedAt,
493
+ 'early-terminated',
494
+ 'limit'
495
+ )
496
+ );
497
+ return;
498
+ }
499
+ const readContext = createReadAbortContext(readOptions.signal);
500
+ const inFlightReads = new Set<Promise<SettledParquetRowGroupRead>>();
501
+ let completed = false;
502
+ let limitReached = false;
503
+ let readError: unknown;
504
+ this.activeReadControllers.add(readContext.abortController);
505
+
506
+ try {
507
+ const initialization = await this.getInitialization(readContext.abortController.signal);
508
+ await this.getCompressionInitialization();
509
+ throwIfAborted(readContext.abortController.signal);
510
+
511
+ const physicalPlan = await this.planRowGroups(
512
+ initialization,
513
+ readOptions,
514
+ readContext.abortController.signal
515
+ );
516
+ const {predicate, rowGroupIndices} = physicalPlan;
517
+ this.recordTelemetry(
518
+ 'row-group-prune',
519
+ {
520
+ rowGroupsRequested: physicalPlan.requested,
521
+ rowGroupsPruned: physicalPlan.requested - rowGroupIndices.length,
522
+ rowGroupsPrunedByStatistics: physicalPlan.prunedByStatistics,
523
+ rowGroupsPrunedByBloomFilter: physicalPlan.prunedByBloomFilter,
524
+ bloomFiltersRead: physicalPlan.bloomFiltersRead,
525
+ bloomFilterBytesRead: physicalPlan.bloomFilterBytesRead
526
+ },
527
+ {}
528
+ );
529
+ const columns = normalizeColumns(readOptions.columns, initialization.schema);
530
+ const predicateColumns = predicate ? getParquetPredicateColumns(predicate) : [];
531
+ const decodedColumns =
532
+ columns.length === 0 ? [] : [...new Set([...columns, ...predicateColumns])];
533
+ const columnList = decodedColumns.map(column => [column]);
534
+ const batchSize = normalizeBatchSize(readOptions.batchSize);
535
+ const concurrency = normalizeConcurrency(readOptions.concurrency);
536
+ const projectedSchema = projectSchema(initialization.schema, columns);
537
+ const projectedColumnNames = columns.length ? new Set(columns) : undefined;
538
+ const workerOptions = this.getWorkerOptions(concurrency, readContext.abortController.signal);
539
+ const decodeOnWorker =
540
+ canDecodeParquetSourceOnWorker(workerOptions) &&
541
+ (!initialization.reader.encrypted || Boolean(this.options.parquet?.keyRetriever));
542
+ const scheduledReads = new Map<number, Promise<SettledParquetRowGroupRead>>();
543
+ let nextPositionToSchedule = 0;
544
+ let remainingRows = readOptions.limit ?? Number.POSITIVE_INFINITY;
545
+
546
+ const scheduleReads = (): void => {
547
+ while (
548
+ nextPositionToSchedule < rowGroupIndices.length &&
549
+ scheduledReads.size < concurrency
550
+ ) {
551
+ const position = nextPositionToSchedule++;
552
+ const rowGroupIndex = rowGroupIndices[position];
553
+ const read: Promise<SettledParquetRowGroupRead> = this.readRowGroup(
554
+ initialization,
555
+ rowGroupIndex,
556
+ columnList,
557
+ projectedSchema,
558
+ batchSize,
559
+ predicate,
560
+ projectedColumnNames,
561
+ decodeOnWorker ? workerOptions : undefined,
562
+ readContext.abortController.signal
563
+ ).then(
564
+ result => ({result}) as SettledParquetRowGroupRead,
565
+ error => ({error}) as SettledParquetRowGroupRead
566
+ );
567
+ scheduledReads.set(position, read);
568
+ inFlightReads.add(read);
569
+ void read.then(() => inFlightReads.delete(read));
570
+ }
571
+ };
572
+
573
+ scheduleReads();
574
+ for (let position = 0; position < rowGroupIndices.length; position++) {
575
+ const settledRead = await scheduledReads.get(position)!;
576
+ scheduledReads.delete(position);
577
+ scheduleReads();
578
+ if ('error' in settledRead) {
579
+ throw settledRead.error;
580
+ }
581
+
582
+ const {
583
+ rowGroupIndex,
584
+ columns: materializedColumns,
585
+ rowCount,
586
+ rowIndices,
587
+ workerResult
588
+ } = settledRead.result;
589
+ if (workerResult) {
590
+ for (const workerBatch of workerResult.batches) {
591
+ throwIfAborted(readContext.abortController.signal);
592
+ const deserializationStartTime = getCurrentTime();
593
+ const arrowTable = hydrateArrowTable(workerBatch.arrowTable);
594
+ const deserializationDurationMs = getCurrentTime() - deserializationStartTime;
595
+ this.recordTelemetry(
596
+ 'arrow-conversion',
597
+ {arrowConversionDurationMs: deserializationDurationMs},
598
+ {rowGroupIndex, durationMs: deserializationDurationMs}
599
+ );
600
+ const batch = sliceParquetBatch(
601
+ createParquetBatchFromArrow(
602
+ initialization.metadata,
603
+ projectedSchema,
604
+ rowGroupIndex,
605
+ workerBatch.rowGroupRowOffset,
606
+ arrowTable,
607
+ workerBatch.rowCount,
608
+ workerBatch.rowGroupRowIndices
609
+ ),
610
+ Math.min(workerBatch.rowCount, remainingRows)
611
+ );
612
+ this.recordTelemetry(
613
+ 'batch',
614
+ {batchesEmitted: 1, rowsEmitted: batch.length},
615
+ {rowGroupIndex, rowCount: batch.length}
616
+ );
617
+ yield batch;
618
+ remainingRows -= batch.length;
619
+ if (remainingRows === 0) {
620
+ limitReached = true;
621
+ completed = true;
622
+ return;
623
+ }
624
+ }
625
+ continue;
626
+ }
627
+
628
+ const outputRowCount = rowCount;
629
+ const outputBatchSize = batchSize || Math.max(outputRowCount, 1);
630
+ for (
631
+ let outputRowOffset = 0;
632
+ outputRowOffset < outputRowCount;
633
+ outputRowOffset += outputBatchSize
634
+ ) {
635
+ throwIfAborted(readContext.abortController.signal);
636
+ const batchRowCount = Math.min(
637
+ outputBatchSize,
638
+ outputRowCount - outputRowOffset,
639
+ remainingRows
640
+ );
641
+ const batchRowIndices = rowIndices
642
+ ? rowIndices!.slice(outputRowOffset, outputRowOffset + batchRowCount)
643
+ : undefined;
644
+ const rowGroupRowOffset = batchRowIndices?.[0] ?? outputRowOffset;
645
+ const batchColumns = sliceColumns(
646
+ materializedColumns!,
647
+ outputRowOffset,
648
+ outputRowOffset + batchRowCount
649
+ );
650
+ const conversionStartTime = getCurrentTime();
651
+ const batch = createParquetBatch(
652
+ initialization.metadata,
653
+ projectedSchema,
654
+ rowGroupIndex,
655
+ rowGroupRowOffset,
656
+ batchColumns,
657
+ batchRowCount,
658
+ batchRowIndices
659
+ );
660
+ const conversionDurationMs = getCurrentTime() - conversionStartTime;
661
+ this.recordTelemetry(
662
+ 'arrow-conversion',
663
+ {arrowConversionDurationMs: conversionDurationMs},
664
+ {rowGroupIndex, durationMs: conversionDurationMs}
665
+ );
666
+ this.recordTelemetry(
667
+ 'batch',
668
+ {batchesEmitted: 1, rowsEmitted: batchRowCount},
669
+ {rowGroupIndex, rowCount: batchRowCount}
670
+ );
671
+ yield batch;
672
+ remainingRows -= batch.length;
673
+ if (remainingRows === 0) {
674
+ limitReached = true;
675
+ completed = true;
676
+ return;
677
+ }
678
+ }
679
+ }
680
+ completed = true;
681
+ } catch (error) {
682
+ readError = error;
683
+ if (readContext.abortController.signal.aborted) {
684
+ this.recordTelemetry('cancel', {cancellationCount: 1}, {error});
685
+ } else {
686
+ this.recordTelemetry('read-error', {failedReadCount: 1}, {error});
687
+ }
688
+ throw error;
689
+ } finally {
690
+ if (!completed && readError === undefined) {
691
+ this.recordTelemetry('cancel', {cancellationCount: 1}, {});
692
+ }
693
+ const cancelled = readContext.abortController.signal.aborted || readOptions.signal?.aborted;
694
+ readContext.abortController.abort();
695
+ readContext.removeSignalListener();
696
+ this.activeReadControllers.delete(readContext.abortController);
697
+ await Promise.allSettled([...inFlightReads]);
698
+ const status: ScanExecutionTelemetry['status'] = readError
699
+ ? cancelled
700
+ ? 'cancelled'
701
+ : 'failed'
702
+ : limitReached || !completed
703
+ ? 'early-terminated'
704
+ : 'completed';
705
+ emitScanExecutionTelemetry(
706
+ options.onTelemetry,
707
+ createParquetScanExecutionTelemetry(
708
+ telemetryBefore,
709
+ this.getTelemetry(),
710
+ startedAt,
711
+ status,
712
+ limitReached
713
+ ? 'limit'
714
+ : !completed && readError === undefined
715
+ ? 'consumer-return'
716
+ : undefined,
717
+ readError
718
+ )
719
+ );
720
+ }
721
+ }
722
+
723
+ /** Produces the physical row-group plan shared by explain and execution. */
724
+ private async planRowGroups(
725
+ initialization: ParquetSourceInitialization,
726
+ readOptions: ParquetSourceReadOptions,
727
+ signal: AbortSignal
728
+ ): Promise<ParquetPhysicalRowGroupPlan> {
729
+ throwIfAborted(signal);
730
+ const candidateRowGroupIndices = normalizeRowGroupIndices(
731
+ readOptions.rowGroups,
732
+ initialization.fileMetadata.row_groups.length
733
+ );
734
+ const callbackFilteredRowGroupIndices = readOptions.rowGroupFilter
735
+ ? candidateRowGroupIndices.filter(rowGroupIndex =>
736
+ readOptions.rowGroupFilter!(initialization.metadata.rowGroups[rowGroupIndex])
737
+ )
738
+ : candidateRowGroupIndices;
739
+ const spatiallyFilteredRowGroupIndices = readOptions.bbox
740
+ ? callbackFilteredRowGroupIndices.filter(rowGroupIndex =>
741
+ canGeoParquetRowGroupMatch(
742
+ initialization.metadata,
743
+ initialization.metadata.rowGroups[rowGroupIndex],
744
+ readOptions.bbox!,
745
+ readOptions.geometryColumn
746
+ )
747
+ )
748
+ : callbackFilteredRowGroupIndices;
749
+ const spatialPredicate = readOptions.bbox
750
+ ? createGeoParquetBoundingBoxPredicate(
751
+ initialization.metadata,
752
+ readOptions.bbox,
753
+ readOptions.geometryColumn
754
+ )
755
+ : undefined;
756
+ const predicate = combineParquetPredicates(readOptions.predicate, spatialPredicate);
757
+ if (predicate) {
758
+ validateParquetPredicate(
759
+ predicate,
760
+ new Set(initialization.schema.fields.map(field => field.name))
761
+ );
762
+ }
763
+ const statisticsRowGroupIndices = predicate
764
+ ? spatiallyFilteredRowGroupIndices.filter(rowGroupIndex =>
765
+ canParquetRowGroupMatch(predicate, initialization.metadata.rowGroups[rowGroupIndex])
766
+ )
767
+ : spatiallyFilteredRowGroupIndices;
768
+ const bloomFilterResult = predicate
769
+ ? await filterParquetRowGroupsWithBloomFilters(
770
+ initialization,
771
+ statisticsRowGroupIndices,
772
+ predicate,
773
+ signal
774
+ )
775
+ : {rowGroupIndices: statisticsRowGroupIndices, filtersRead: 0, bytesRead: 0};
776
+ return {
777
+ predicate,
778
+ rowGroupIndices: bloomFilterResult.rowGroupIndices,
779
+ requested: candidateRowGroupIndices.length,
780
+ prunedByCallback: candidateRowGroupIndices.length - callbackFilteredRowGroupIndices.length,
781
+ prunedBySpatial:
782
+ callbackFilteredRowGroupIndices.length - spatiallyFilteredRowGroupIndices.length,
783
+ prunedByStatistics:
784
+ spatiallyFilteredRowGroupIndices.length - statisticsRowGroupIndices.length,
785
+ prunedByBloomFilter:
786
+ statisticsRowGroupIndices.length - bloomFilterResult.rowGroupIndices.length,
787
+ bloomFiltersRead: bloomFilterResult.filtersRead,
788
+ bloomFilterBytesRead: bloomFilterResult.bytesRead
789
+ };
790
+ }
791
+
792
+ /** Produces explainable page-index and byte-range plans for retained row groups. */
793
+ private async planPages(
794
+ initialization: ParquetSourceInitialization,
795
+ readOptions: ParquetSourceReadOptions,
796
+ rowGroupPlan: ParquetPhysicalRowGroupPlan,
797
+ signal: AbortSignal
798
+ ): Promise<ParquetPageScanPlan[]> {
799
+ if (!rowGroupPlan.predicate) return [];
800
+ const columns = normalizeColumns(readOptions.columns, initialization.schema);
801
+ const predicateColumns = getParquetPredicateColumns(rowGroupPlan.predicate);
802
+ const decodedColumns =
803
+ columns.length === 0 ? [] : [...new Set([...columns, ...predicateColumns])];
804
+ const hasHiddenPredicateColumns =
805
+ columns.length > 0 && predicateColumns.some(column => !columns.includes(column));
806
+ const phases = hasHiddenPredicateColumns
807
+ ? [
808
+ {
809
+ phase: 'predicate' as const,
810
+ columns: predicateColumns.map(column => [column]),
811
+ predicate: rowGroupPlan.predicate
812
+ },
813
+ {
814
+ phase: 'projection' as const,
815
+ columns: columns.map(column => [column]),
816
+ predicate: undefined
817
+ }
818
+ ]
819
+ : [
820
+ {
821
+ phase: 'combined' as const,
822
+ columns: decodedColumns.map(column => [column]),
823
+ predicate: rowGroupPlan.predicate
824
+ }
825
+ ];
826
+ const plans: ParquetPageScanPlan[] = [];
827
+ for (const rowGroupIndex of rowGroupPlan.rowGroupIndices) {
828
+ const rowGroup = initialization.fileMetadata.row_groups[rowGroupIndex];
829
+ let predicateProvedEmpty = false;
830
+ for (const phase of phases) {
831
+ throwIfAborted(signal);
832
+ if (phase.phase === 'projection' && predicateProvedEmpty) {
833
+ continue;
834
+ }
835
+ await initialization.reader.resolveColumnMetadata(rowGroup, rowGroupIndex, phase.columns);
836
+ const pagePlan = phase.predicate
837
+ ? await createParquetPagePruningPlan(
838
+ initialization.file,
839
+ rowGroup,
840
+ initialization.parquetSchema,
841
+ phase.columns,
842
+ phase.predicate,
843
+ signal,
844
+ {
845
+ rowGroupOrdinal: rowGroupIndex,
846
+ int96AsTimestamp: Boolean(this.options.parquet?.int96AsTimestamp),
847
+ decryptModule: (bytes, module, rowGroupOrdinal, columnOrdinal, columnChunk) =>
848
+ initialization.reader.decryptIndexModule(
849
+ bytes,
850
+ module,
851
+ rowGroupOrdinal,
852
+ columnOrdinal,
853
+ columnChunk
854
+ )
855
+ }
856
+ )
857
+ : undefined;
858
+ plans.push(
859
+ pagePlan
860
+ ? Object.freeze({
861
+ rowGroupIndex,
862
+ phase: phase.phase,
863
+ columns: Object.freeze(phase.columns.map(column => Object.freeze([...column]))),
864
+ rowRanges: Object.freeze(
865
+ pagePlan.rowRanges.map(rowRange => Object.freeze({...rowRange}))
866
+ ),
867
+ indexesRead: pagePlan.indexCount,
868
+ totalPages: pagePlan.totalPageCount,
869
+ selectedPages: pagePlan.selectedPageCount,
870
+ rowsPruned: pagePlan.prunedRowCount,
871
+ ranges: Object.freeze(
872
+ getParquetPageReadRanges(
873
+ rowGroup,
874
+ phase.columns,
875
+ pagePlan,
876
+ initialization.parquetSchema
877
+ ).map(range => Object.freeze({...range}))
878
+ )
879
+ })
880
+ : createFullColumnScanPlan(rowGroup, rowGroupIndex, phase.phase, phase.columns)
881
+ );
882
+ if (phase.phase === 'predicate' && pagePlan?.rowRanges.length === 0) {
883
+ predicateProvedEmpty = true;
884
+ }
885
+ }
886
+ }
887
+ return plans;
888
+ }
889
+
890
+ /** Closes the underlying readable file and aborts active remote requests. */
891
+ async close(): Promise<void> {
892
+ if (this.closed) {
893
+ return;
894
+ }
895
+ this.closed = true;
896
+ for (const abortController of this.activeReadControllers) {
897
+ abortController.abort();
898
+ }
899
+ await this.readableFile?.close();
900
+ const initialization = await this.initializationPromise?.catch(() => null);
901
+ initialization?.reader.close();
902
+ if (initialization?.file !== this.readableFile) {
903
+ await initialization?.file.close();
904
+ }
905
+ }
906
+
907
+ /** Returns cached initialization, resetting the cache when initialization fails. */
908
+ private getInitialization(signal?: AbortSignal): Promise<ParquetSourceInitialization> {
909
+ if (this.closed) {
910
+ return Promise.reject(new Error('ParquetSource is closed'));
911
+ }
912
+ if (!this.initializationPromise) {
913
+ this.initializationPromise = this.initialize(signal).catch(error => {
914
+ this.initializationPromise = null;
915
+ throw error;
916
+ });
917
+ }
918
+ return this.initializationPromise;
919
+ }
920
+
921
+ /** Preloads TypeScript decoder compression modules once and permits retry after failure. */
922
+ private getCompressionInitialization(): Promise<void> {
923
+ if (!this.compressionPromise) {
924
+ this.compressionPromise = preloadCompressions()
925
+ .then(() => undefined)
926
+ .catch(error => {
927
+ this.compressionPromise = null;
928
+ throw error;
929
+ });
930
+ }
931
+ return this.compressionPromise;
932
+ }
933
+
934
+ /** Snapshots source defaults and per-read overrides before asynchronous iteration begins. */
935
+ private getReadOptions(options: ParquetSourceReadOptions): ParquetSourceReadOptions {
936
+ const rowGroups = options.rowGroups ?? this.options.parquet?.rowGroups;
937
+ const columns = options.columns ?? this.options.parquet?.columns;
938
+ const predicate = options.predicate ?? this.options.parquet?.predicate;
939
+ const bbox = options.bbox ?? this.options.parquet?.bbox;
940
+ const limit = options.limit ?? this.options.parquet?.limit;
941
+ validateTableQueryLimit(limit);
942
+ return {
943
+ rowGroups: rowGroups && [...rowGroups],
944
+ columns: columns && [...columns],
945
+ rowGroupFilter: options.rowGroupFilter ?? this.options.parquet?.rowGroupFilter,
946
+ predicate: predicate ? copyParquetPredicate(predicate) : undefined,
947
+ limit,
948
+ bbox: bbox ? ([...bbox] as ParquetSourceReadOptions['bbox']) : undefined,
949
+ geometryColumn: options.geometryColumn ?? this.options.parquet?.geometryColumn,
950
+ batchSize: options.batchSize ?? this.options.parquet?.batchSize,
951
+ concurrency: options.concurrency ?? this.options.parquet?.concurrency,
952
+ signal: options.signal
953
+ };
954
+ }
955
+
956
+ /** Creates loader-utils worker options for one selective source read. */
957
+ private getWorkerOptions(concurrency: number, signal: AbortSignal): ParquetSourceWorkerOptions {
958
+ return {
959
+ core: {
960
+ worker: this.options.core?.worker ?? true,
961
+ maxConcurrency: concurrency,
962
+ maxMobileConcurrency: this.options.core?.maxMobileConcurrency ?? concurrency,
963
+ reuseWorkers: this.options.core?.reuseWorkers ?? isBrowser,
964
+ _workerType: this.options.core?._workerType
965
+ },
966
+ 'parquet-source': {
967
+ workerUrl: this.options.parquet?.workerUrl
968
+ },
969
+ parquet: {
970
+ signal
971
+ }
972
+ };
973
+ }
974
+
975
+ /** Fetches and decodes one selected row group using the cached footer and schema. */
976
+ private async readRowGroup(
977
+ initialization: ParquetSourceInitialization,
978
+ rowGroupIndex: number,
979
+ columnList: string[][],
980
+ projectedSchema: Schema,
981
+ batchSize: number | undefined,
982
+ predicate: ParquetPredicate | undefined,
983
+ projectedColumnNames: ReadonlySet<string> | undefined,
984
+ workerOptions: ParquetSourceWorkerOptions | undefined,
985
+ signal: AbortSignal
986
+ ): Promise<ParquetRowGroupReadResult> {
987
+ if (
988
+ predicate &&
989
+ projectedColumnNames &&
990
+ getParquetPredicateColumns(predicate).some(column => !projectedColumnNames.has(column))
991
+ ) {
992
+ return await this.readRowGroupWithLateMaterialization(
993
+ initialization,
994
+ rowGroupIndex,
995
+ columnList,
996
+ projectedSchema,
997
+ batchSize,
998
+ predicate,
999
+ projectedColumnNames,
1000
+ workerOptions,
1001
+ signal
1002
+ );
1003
+ }
1004
+ const rowGroup = initialization.fileMetadata.row_groups[rowGroupIndex];
1005
+ await initialization.reader.resolveColumnMetadata(rowGroup, rowGroupIndex, columnList);
1006
+ const pagePlan = predicate
1007
+ ? await createParquetPagePruningPlan(
1008
+ initialization.file,
1009
+ rowGroup,
1010
+ initialization.parquetSchema,
1011
+ columnList,
1012
+ predicate,
1013
+ signal,
1014
+ {
1015
+ rowGroupOrdinal: rowGroupIndex,
1016
+ int96AsTimestamp: Boolean(this.options.parquet?.int96AsTimestamp),
1017
+ decryptModule: (bytes, module, rowGroupOrdinal, columnOrdinal, columnChunk) =>
1018
+ initialization.reader.decryptIndexModule(
1019
+ bytes,
1020
+ module,
1021
+ rowGroupOrdinal,
1022
+ columnOrdinal,
1023
+ columnChunk
1024
+ )
1025
+ }
1026
+ )
1027
+ : undefined;
1028
+ if (pagePlan) {
1029
+ this.recordPagePruningTelemetry(rowGroupIndex, pagePlan);
1030
+ if (pagePlan.rowRanges.length === 0) {
1031
+ return {rowGroupIndex, columns: {}, rowCount: 0, rowIndices: []};
1032
+ }
1033
+ }
1034
+
1035
+ if (workerOptions) {
1036
+ return await this.readRowGroupOnWorker(
1037
+ initialization,
1038
+ rowGroupIndex,
1039
+ columnList,
1040
+ projectedSchema,
1041
+ batchSize,
1042
+ predicate,
1043
+ pagePlan,
1044
+ workerOptions,
1045
+ signal
1046
+ );
1047
+ }
1048
+
1049
+ const decodeStartTime = getCurrentTime();
1050
+ const decodedRowGroups = pagePlan
1051
+ ? await Promise.all(
1052
+ pagePlan.rowRanges.map(rowRange =>
1053
+ initialization.reader.readRowGroupRange(
1054
+ initialization.parquetSchema,
1055
+ rowGroup,
1056
+ columnList,
1057
+ rowRange,
1058
+ pagePlan.pageLocations,
1059
+ signal,
1060
+ rowGroupIndex
1061
+ )
1062
+ )
1063
+ )
1064
+ : [
1065
+ await initialization.reader.readRowGroup(
1066
+ initialization.parquetSchema,
1067
+ rowGroup,
1068
+ columnList,
1069
+ signal,
1070
+ rowGroupIndex
1071
+ )
1072
+ ];
1073
+ throwIfAborted(signal);
1074
+ const columns = concatenateMaterializedColumns(
1075
+ decodedRowGroups.map(decodedRowGroup =>
1076
+ initialization.parquetSchema.materializeColumns(decodedRowGroup)
1077
+ )
1078
+ );
1079
+ const sourceRowIndices = pagePlan
1080
+ ? pagePlan.rowRanges.flatMap(rowRange =>
1081
+ Array.from({length: rowRange.end - rowRange.start}, (_, index) => rowRange.start + index)
1082
+ )
1083
+ : undefined;
1084
+ const localRowCount = sourceRowIndices?.length ?? Number(rowGroup.num_rows);
1085
+ const localRowIndices = predicate
1086
+ ? filterParquetRowIndices(predicate, columns, localRowCount)
1087
+ : undefined;
1088
+ const rowIndices = localRowIndices?.map(rowIndex => sourceRowIndices?.[rowIndex] ?? rowIndex);
1089
+ const outputColumns = localRowIndices
1090
+ ? gatherParquetColumns(columns, localRowIndices, projectedColumnNames)
1091
+ : columns;
1092
+ const decodeDurationMs = getCurrentTime() - decodeStartTime;
1093
+ this.recordTelemetry(
1094
+ 'decode',
1095
+ {decodeDurationMs, rowGroupsDecoded: 1},
1096
+ {rowGroupIndex, durationMs: decodeDurationMs}
1097
+ );
1098
+ if (predicate) {
1099
+ this.recordTelemetry(
1100
+ 'predicate-filter',
1101
+ {
1102
+ predicateRowsTested: localRowCount,
1103
+ predicateRowsMatched: rowIndices!.length
1104
+ },
1105
+ {rowGroupIndex, rowCount: rowIndices!.length}
1106
+ );
1107
+ }
1108
+ return {
1109
+ rowGroupIndex,
1110
+ columns: outputColumns,
1111
+ rowCount: rowIndices?.length ?? localRowCount,
1112
+ rowIndices
1113
+ };
1114
+ }
1115
+
1116
+ /** Fetches selected chunks and transfers their decompression and Arrow conversion to a worker. */
1117
+ private async readRowGroupOnWorker(
1118
+ initialization: ParquetSourceInitialization,
1119
+ rowGroupIndex: number,
1120
+ columnList: string[][],
1121
+ projectedSchema: Schema,
1122
+ batchSize: number | undefined,
1123
+ predicate: ParquetPredicate | undefined,
1124
+ pagePlan: ParquetPagePruningPlan | undefined,
1125
+ workerOptions: ParquetSourceWorkerOptions,
1126
+ signal: AbortSignal
1127
+ ): Promise<ParquetRowGroupReadResult> {
1128
+ const rowGroup = initialization.fileMetadata.row_groups[rowGroupIndex];
1129
+ const selectedColumnChunks = rowGroup.columns.filter(columnChunk => {
1130
+ const path = columnChunk.meta_data?.path_in_schema;
1131
+ return Boolean(path && (columnList.length === 0 || fieldIndexOf(columnList, path) >= 0));
1132
+ });
1133
+ const rangeDescriptors = pagePlan
1134
+ ? getParquetPageReadRanges(rowGroup, columnList, pagePlan, initialization.parquetSchema)
1135
+ : selectedColumnChunks.map(getColumnChunkRange);
1136
+ const ranges = await Promise.all(
1137
+ rangeDescriptors.map(async ({offset, length}) => {
1138
+ return {offset, data: await initialization.file.read(offset, length, signal)};
1139
+ })
1140
+ );
1141
+ throwIfAborted(signal);
1142
+
1143
+ const workerColumnChunks = await Promise.all(
1144
+ selectedColumnChunks.map(async columnChunk => {
1145
+ const descriptor = createParquetSourceWorkerColumnChunk(columnChunk);
1146
+ descriptor.columnOrdinal = rowGroup.columns.indexOf(columnChunk);
1147
+ if (columnChunk.crypto_metadata) {
1148
+ const columnOrdinal = rowGroup.columns.indexOf(columnChunk);
1149
+ const key = await initialization.reader.getColumnKeyForWorker(
1150
+ columnChunk,
1151
+ rowGroupIndex,
1152
+ columnOrdinal
1153
+ );
1154
+ descriptor.encrypted = true;
1155
+ descriptor.keyMetadata = key.keyMetadata?.slice().buffer;
1156
+ descriptor.keyMaterial = key.keyMaterial.slice().buffer;
1157
+ }
1158
+ return descriptor;
1159
+ })
1160
+ );
1161
+ const encryptionContext = initialization.reader.getEncryptionContextForWorker();
1162
+
1163
+ let workerResult: ParquetSourceWorkerResult;
1164
+ try {
1165
+ workerResult = await decodeParquetSourceRowGroupOnWorker(
1166
+ {
1167
+ fileByteLength: initialization.file.size,
1168
+ rowCount: Number(rowGroup.num_rows),
1169
+ uncompressedByteLength: Number(rowGroup.total_byte_size),
1170
+ schemaDefinition: initialization.parquetSchema.schema,
1171
+ projectedSchema,
1172
+ columnChunks: workerColumnChunks,
1173
+ ranges,
1174
+ batchSize: batchSize || Math.max(Number(rowGroup.num_rows), 1),
1175
+ predicate: predicate ? copyParquetPredicate(predicate) : undefined,
1176
+ pagePlan,
1177
+ encryption: encryptionContext
1178
+ ? {
1179
+ algorithm: encryptionContext.algorithm,
1180
+ aadPrefix: encryptionContext.aadPrefix?.slice().buffer,
1181
+ fileUnique: encryptionContext.fileUnique.slice().buffer
1182
+ }
1183
+ : undefined,
1184
+ preserveBinary: Boolean(this.options.parquet?.preserveBinary),
1185
+ int96AsTimestamp: Boolean(this.options.parquet?.int96AsTimestamp),
1186
+ verifyPageChecksums: Boolean(this.options.parquet?.verifyPageChecksums)
1187
+ },
1188
+ workerOptions
1189
+ );
1190
+ } catch (error) {
1191
+ throwIfAborted(signal);
1192
+ throw error;
1193
+ }
1194
+ throwIfAborted(signal);
1195
+ this.recordTelemetry(
1196
+ 'decode',
1197
+ {decodeDurationMs: workerResult.decodeDurationMs, rowGroupsDecoded: 1},
1198
+ {rowGroupIndex, durationMs: workerResult.decodeDurationMs}
1199
+ );
1200
+ this.recordTelemetry(
1201
+ 'arrow-conversion',
1202
+ {arrowConversionDurationMs: workerResult.arrowConversionDurationMs},
1203
+ {rowGroupIndex, durationMs: workerResult.arrowConversionDurationMs}
1204
+ );
1205
+ if (predicate) {
1206
+ this.recordTelemetry(
1207
+ 'predicate-filter',
1208
+ {
1209
+ predicateRowsTested: workerResult.sourceRowCount,
1210
+ predicateRowsMatched: workerResult.rowCount
1211
+ },
1212
+ {rowGroupIndex, rowCount: workerResult.rowCount}
1213
+ );
1214
+ }
1215
+ return {rowGroupIndex, rowCount: workerResult.rowCount, workerResult};
1216
+ }
1217
+
1218
+ /** Decodes predicate columns first, avoiding projected-column decoding for empty matches. */
1219
+ private async readRowGroupWithLateMaterialization(
1220
+ initialization: ParquetSourceInitialization,
1221
+ rowGroupIndex: number,
1222
+ columnList: string[][],
1223
+ projectedSchema: Schema,
1224
+ batchSize: number | undefined,
1225
+ predicate: ParquetPredicate,
1226
+ projectedColumnNames: ReadonlySet<string>,
1227
+ workerOptions: ParquetSourceWorkerOptions | undefined,
1228
+ signal: AbortSignal
1229
+ ): Promise<ParquetRowGroupReadResult> {
1230
+ const predicateColumns = getParquetPredicateColumns(predicate);
1231
+ const predicateColumnList = predicateColumns.map(column => [column]);
1232
+ const filterResult = await this.readRowGroup(
1233
+ initialization,
1234
+ rowGroupIndex,
1235
+ predicateColumnList,
1236
+ projectSchema(initialization.schema, predicateColumns),
1237
+ batchSize,
1238
+ predicate,
1239
+ new Set(predicateColumns),
1240
+ workerOptions,
1241
+ signal
1242
+ );
1243
+ const rowIndices = filterResult.rowIndices;
1244
+ const selectedRowIndices = rowIndices || getWorkerRowIndices(filterResult.workerResult);
1245
+ if (!selectedRowIndices || selectedRowIndices.length === 0) {
1246
+ return {rowGroupIndex, rowCount: 0, columns: {}, rowIndices: []};
1247
+ }
1248
+ const projectedColumnList = columnList.filter(columnPath =>
1249
+ projectedColumnNames.has(columnPath[0])
1250
+ );
1251
+ const projectedResult = await this.readRowGroup(
1252
+ initialization,
1253
+ rowGroupIndex,
1254
+ projectedColumnList,
1255
+ projectedSchema,
1256
+ batchSize,
1257
+ undefined,
1258
+ projectedColumnNames,
1259
+ workerOptions,
1260
+ signal
1261
+ );
1262
+ if (workerOptions && projectedResult.workerResult) {
1263
+ return {
1264
+ rowGroupIndex,
1265
+ rowCount: selectedRowIndices.length,
1266
+ rowIndices: selectedRowIndices,
1267
+ workerResult: gatherWorkerProjectedRows(
1268
+ projectedResult.workerResult,
1269
+ selectedRowIndices,
1270
+ batchSize
1271
+ )
1272
+ };
1273
+ }
1274
+ return {
1275
+ rowGroupIndex,
1276
+ rowCount: selectedRowIndices.length,
1277
+ rowIndices: selectedRowIndices,
1278
+ columns: projectedResult.columns
1279
+ ? gatherParquetColumns(projectedResult.columns, selectedRowIndices, projectedColumnNames)
1280
+ : {}
1281
+ };
1282
+ }
1283
+
1284
+ /** Records the rows and pages avoided by one conservative page-index plan. */
1285
+ private recordPagePruningTelemetry(rowGroupIndex: number, plan: ParquetPagePruningPlan): void {
1286
+ this.recordTelemetry(
1287
+ 'page-index-prune',
1288
+ {
1289
+ pageIndexesRead: plan.indexCount,
1290
+ pagesRead: plan.selectedPageCount,
1291
+ pagesPruned: plan.totalPageCount - plan.selectedPageCount,
1292
+ rowsPrunedByPageIndex: plan.prunedRowCount,
1293
+ rowGroupsPrunedByPageIndex: plan.rowRanges.length === 0 ? 1 : 0,
1294
+ rowGroupsPruned: plan.rowRanges.length === 0 ? 1 : 0
1295
+ },
1296
+ {rowGroupIndex, rowCount: plan.prunedRowCount}
1297
+ );
1298
+ }
1299
+
1300
+ /** Opens the readable file and decodes its footer and schema once. */
1301
+ private async initialize(signal?: AbortSignal): Promise<ParquetSourceInitialization> {
1302
+ const file = await this.openFile(signal);
1303
+ try {
1304
+ const reader = new ParquetReader(file, {
1305
+ preserveBinary: this.options.parquet?.preserveBinary,
1306
+ int96AsTimestamp: this.options.parquet?.int96AsTimestamp,
1307
+ verifyPageChecksums: this.options.parquet?.verifyPageChecksums,
1308
+ verifyFooterSignature: this.options.parquet?.verifyFooterSignature,
1309
+ keyRetriever: this.options.parquet?.keyRetriever,
1310
+ aadPrefix: this.options.parquet?.aadPrefix,
1311
+ signal
1312
+ });
1313
+ const fileMetadata = await reader.getFileMetadata();
1314
+ const parquetSchema = await reader.getSchema();
1315
+ const schema = deepFreeze(await getSchemaFromParquetReader(reader));
1316
+ const metadata = createParquetSourceMetadata(
1317
+ this.data,
1318
+ this.url,
1319
+ file,
1320
+ fileMetadata,
1321
+ schema,
1322
+ parquetSchema,
1323
+ file instanceof ParquetRangeFile ? file.objectVersion : undefined
1324
+ );
1325
+ reader.props.signal = undefined;
1326
+ return {file, reader, schema, parquetSchema, fileMetadata, metadata};
1327
+ } catch (error) {
1328
+ await file.close();
1329
+ throw error;
1330
+ }
1331
+ }
1332
+
1333
+ /** Opens a Blob file or probes a remote object with one bounded range request. */
1334
+ private async openFile(signal?: AbortSignal): Promise<ReadableFile> {
1335
+ if (typeof this.data !== 'string') {
1336
+ this.readableFile = new BlobFile(this.data);
1337
+ return this.readableFile;
1338
+ }
1339
+ const file = new ParquetRangeFile(this.url, {
1340
+ fetch: this.fetch,
1341
+ headers: this.options.parquet?.headers,
1342
+ rangeRequests: this.options.rangeRequests,
1343
+ onTelemetry: event =>
1344
+ this.recordTelemetry(
1345
+ event.type,
1346
+ {
1347
+ rangeRequestCount: event.rangeRequestCount,
1348
+ requestedBytes: event.requestedBytes,
1349
+ downloadedBytes: event.downloadedBytes,
1350
+ cacheHits: event.cacheHits,
1351
+ networkDurationMs: event.networkDurationMs,
1352
+ failedRangeRequestCount: event.failedRangeRequestCount,
1353
+ abortedRangeRequestCount: event.abortedRangeRequestCount
1354
+ },
1355
+ {durationMs: event.networkDurationMs, error: event.error}
1356
+ )
1357
+ });
1358
+ this.readableFile = file;
1359
+ return await file.open(signal);
1360
+ }
1361
+
1362
+ /** Applies telemetry deltas and emits a callback with the resulting cumulative snapshot. */
1363
+ private recordTelemetry(
1364
+ type: ParquetTelemetryEvent['type'],
1365
+ delta: Partial<ParquetTelemetry>,
1366
+ details: Omit<ParquetTelemetryEvent, 'type' | 'telemetry'>
1367
+ ): void {
1368
+ for (const [key, value] of Object.entries(delta) as Array<
1369
+ [keyof ParquetTelemetry, number | undefined]
1370
+ >) {
1371
+ if (value !== undefined) {
1372
+ this.telemetry[key] += value;
1373
+ }
1374
+ }
1375
+ try {
1376
+ this.options.parquet?.onTelemetry?.({type, telemetry: this.getTelemetry(), ...details});
1377
+ } catch {
1378
+ // Telemetry must never change source read behavior.
1379
+ }
1380
+ }
1381
+ }
1382
+
1383
+ function getParquetColumnRoles(columnNames: readonly string[]): Record<string, ScanColumnRole> {
1384
+ const roles: Record<string, ScanColumnRole> = {};
1385
+ for (const columnName of columnNames) {
1386
+ const normalizedName = columnName.toLowerCase();
1387
+ if (normalizedName === 'geometry' || normalizedName === 'geom' || normalizedName === 'shape') {
1388
+ roles[columnName] = 'geometry';
1389
+ } else if (
1390
+ normalizedName === 'longitude' ||
1391
+ normalizedName === 'lon' ||
1392
+ normalizedName === 'x'
1393
+ ) {
1394
+ roles[columnName] = 'longitude';
1395
+ } else if (
1396
+ normalizedName === 'latitude' ||
1397
+ normalizedName === 'lat' ||
1398
+ normalizedName === 'y'
1399
+ ) {
1400
+ roles[columnName] = 'latitude';
1401
+ }
1402
+ }
1403
+ return roles;
1404
+ }
1405
+
1406
+ /** Normalizes a decoded Parquet footer into source metadata. */
1407
+ function createParquetSourceMetadata(
1408
+ data: string | Blob,
1409
+ url: string,
1410
+ file: ReadableFile,
1411
+ fileMetadata: FileMetaData,
1412
+ schema: Schema,
1413
+ parquetSchema: ParquetSchema,
1414
+ objectVersion?: ParquetObjectVersion
1415
+ ): ParquetSourceMetadata {
1416
+ let rowOffset = 0;
1417
+ const rowGroups = fileMetadata.row_groups.map((rowGroup, index) => {
1418
+ const columns = rowGroup.columns
1419
+ .filter(columnChunk => Boolean(columnChunk.meta_data))
1420
+ .map(columnChunk => createColumnChunkMetadata(columnChunk, parquetSchema));
1421
+ const normalizedRowGroup = createRowGroupMetadata(
1422
+ index,
1423
+ rowOffset,
1424
+ Number(rowGroup.num_rows),
1425
+ Number(rowGroup.total_byte_size),
1426
+ columns,
1427
+ rowGroup.sorting_columns
1428
+ );
1429
+ rowOffset += normalizedRowGroup.rowCount;
1430
+ return normalizedRowGroup;
1431
+ });
1432
+
1433
+ return Object.freeze({
1434
+ schema,
1435
+ name: getSourceName(data, url),
1436
+ url: url || undefined,
1437
+ fileByteLength: file.size,
1438
+ version: fileMetadata.version,
1439
+ formatVersion: fileMetadata.version,
1440
+ createdBy: fileMetadata.created_by,
1441
+ rowCount: Number(fileMetadata.num_rows),
1442
+ rowGroupCount: rowGroups.length,
1443
+ keyValueMetadata: Object.freeze(getKeyValueMetadata(fileMetadata)),
1444
+ rowGroups: Object.freeze(rowGroups),
1445
+ objectVersion: hasObjectVersion(objectVersion) ? Object.freeze({...objectVersion}) : undefined
1446
+ });
1447
+ }
1448
+
1449
+ /** Normalizes a decoded Parquet column chunk. */
1450
+ function createColumnChunkMetadata(
1451
+ columnChunk: FileMetaData['row_groups'][number]['columns'][number],
1452
+ parquetSchema: ParquetSchema
1453
+ ): ParquetColumnChunkMetadata {
1454
+ const columnMetadata = columnChunk.meta_data!;
1455
+ const dataPageOffset = Number(columnMetadata.data_page_offset);
1456
+ const dictionaryPageOffset =
1457
+ columnMetadata.dictionary_page_offset === undefined
1458
+ ? undefined
1459
+ : Number(columnMetadata.dictionary_page_offset);
1460
+ const statistics = createColumnChunkStatistics(
1461
+ columnMetadata.statistics,
1462
+ parquetSchema.findField(columnMetadata.path_in_schema)
1463
+ );
1464
+ const geospatialStatistics = createGeospatialStatistics(columnMetadata.geospatial_statistics);
1465
+ const sizeStatistics = createSizeStatistics(columnMetadata.size_statistics);
1466
+ const compressedByteLength = Number(columnMetadata.total_compressed_size);
1467
+ const uncompressedByteLength = Number(columnMetadata.total_uncompressed_size);
1468
+ return Object.freeze({
1469
+ path: Object.freeze([...columnMetadata.path_in_schema]),
1470
+ filePath: columnChunk.file_path || undefined,
1471
+ compression: CompressionCodec[columnMetadata.codec] || String(columnMetadata.codec),
1472
+ encodings: Object.freeze(
1473
+ columnMetadata.encodings.map(encoding => Encoding[encoding] || String(encoding))
1474
+ ),
1475
+ valueCount: Number(columnMetadata.num_values),
1476
+ fileOffset: Math.min(
1477
+ dataPageOffset,
1478
+ dictionaryPageOffset && dictionaryPageOffset > 0 ? dictionaryPageOffset : dataPageOffset
1479
+ ),
1480
+ compressedByteLength,
1481
+ compressedSize: compressedByteLength,
1482
+ uncompressedByteLength,
1483
+ uncompressedSize: uncompressedByteLength,
1484
+ dataPageOffset,
1485
+ dictionaryPageOffset,
1486
+ columnIndexOffset:
1487
+ columnChunk.column_index_offset === undefined
1488
+ ? undefined
1489
+ : Number(columnChunk.column_index_offset),
1490
+ columnIndexByteLength: columnChunk.column_index_length,
1491
+ offsetIndexOffset:
1492
+ columnChunk.offset_index_offset === undefined
1493
+ ? undefined
1494
+ : Number(columnChunk.offset_index_offset),
1495
+ offsetIndexByteLength: columnChunk.offset_index_length,
1496
+ bloomFilterOffset:
1497
+ columnMetadata.bloom_filter_offset === undefined
1498
+ ? undefined
1499
+ : Number(columnMetadata.bloom_filter_offset),
1500
+ bloomFilterByteLength: columnMetadata.bloom_filter_length,
1501
+ statistics,
1502
+ sizeStatistics,
1503
+ geospatialStatistics
1504
+ });
1505
+ }
1506
+
1507
+ /** Normalizes optional Parquet size statistics without inventing missing counts. */
1508
+ function createSizeStatistics(
1509
+ statistics:
1510
+ | {
1511
+ unencoded_byte_array_data_bytes?: {toNumber?: () => number} | number;
1512
+ repetition_level_histogram?: Array<{toNumber?: () => number} | number>;
1513
+ definition_level_histogram?: Array<{toNumber?: () => number} | number>;
1514
+ }
1515
+ | undefined
1516
+ ): ParquetColumnChunkSizeStatistics | undefined {
1517
+ if (!statistics) return undefined;
1518
+ const result: ParquetColumnChunkSizeStatistics = {
1519
+ unencodedByteArrayDataBytes:
1520
+ statistics.unencoded_byte_array_data_bytes === undefined
1521
+ ? undefined
1522
+ : toSafeNumber(statistics.unencoded_byte_array_data_bytes),
1523
+ repetitionLevelHistogram: normalizeSizeStatisticsList(statistics.repetition_level_histogram),
1524
+ definitionLevelHistogram: normalizeSizeStatisticsList(statistics.definition_level_histogram)
1525
+ };
1526
+ if (
1527
+ result.unencodedByteArrayDataBytes === undefined &&
1528
+ result.repetitionLevelHistogram === undefined &&
1529
+ result.definitionLevelHistogram === undefined
1530
+ ) {
1531
+ return undefined;
1532
+ }
1533
+ return Object.freeze({
1534
+ ...result,
1535
+ repetitionLevelHistogram: result.repetitionLevelHistogram
1536
+ ? Object.freeze(result.repetitionLevelHistogram)
1537
+ : undefined,
1538
+ definitionLevelHistogram: result.definitionLevelHistogram
1539
+ ? Object.freeze(result.definitionLevelHistogram)
1540
+ : undefined
1541
+ });
1542
+ }
1543
+
1544
+ function toSafeNumber(value: {toNumber?: () => number} | number): number | undefined {
1545
+ const numberValue = typeof value === 'number' ? value : value.toNumber?.();
1546
+ return numberValue !== undefined && Number.isSafeInteger(numberValue) && numberValue >= 0
1547
+ ? numberValue
1548
+ : undefined;
1549
+ }
1550
+
1551
+ /** Converts a Thrift int64 histogram only when every entry is a safe non-negative integer. */
1552
+ function normalizeSizeStatisticsList(
1553
+ values: Array<{toNumber?: () => number} | number> | undefined
1554
+ ): readonly number[] | undefined {
1555
+ if (!values) return undefined;
1556
+ const normalized = values.map(toSafeNumber);
1557
+ return normalized.every((value): value is number => value !== undefined) ? normalized : undefined;
1558
+ }
1559
+
1560
+ /** Normalizes native Parquet geospatial statistics from the decoded footer. */
1561
+ function createGeospatialStatistics(
1562
+ statistics: ParquetThriftGeospatialStatistics | undefined
1563
+ ): ParquetGeospatialStatistics | undefined {
1564
+ if (!statistics) return undefined;
1565
+ const bbox = statistics.bbox
1566
+ ? Object.freeze({
1567
+ xmin: statistics.bbox.xmin,
1568
+ xmax: statistics.bbox.xmax,
1569
+ ymin: statistics.bbox.ymin,
1570
+ ymax: statistics.bbox.ymax,
1571
+ zmin: statistics.bbox.zmin,
1572
+ zmax: statistics.bbox.zmax,
1573
+ mmin: statistics.bbox.mmin,
1574
+ mmax: statistics.bbox.mmax
1575
+ })
1576
+ : undefined;
1577
+ return Object.freeze({
1578
+ bbox,
1579
+ geometryTypes: statistics.geospatial_types
1580
+ ? Object.freeze([...statistics.geospatial_types])
1581
+ : undefined
1582
+ });
1583
+ }
1584
+
1585
+ /** Decodes optional footer statistics into the column's logical value representation. */
1586
+ function createColumnChunkStatistics(
1587
+ statistics: ParquetThriftStatistics | undefined,
1588
+ field: ParquetField
1589
+ ): ParquetColumnChunkStatistics | undefined {
1590
+ if (!statistics) {
1591
+ return undefined;
1592
+ }
1593
+ const minBytes = statistics.min_value ?? statistics.min;
1594
+ const maxBytes = statistics.max_value ?? statistics.max;
1595
+ const result: ParquetColumnChunkStatistics = {
1596
+ nullCount: statistics.null_count === undefined ? undefined : Number(statistics.null_count),
1597
+ distinctCount:
1598
+ statistics.distinct_count === undefined ? undefined : Number(statistics.distinct_count),
1599
+ minIsExact: statistics.is_min_value_exact,
1600
+ maxIsExact: statistics.is_max_value_exact
1601
+ };
1602
+ if (minBytes) {
1603
+ result.min = decodeStatisticsValueSafely(toUint8Array(minBytes), field);
1604
+ }
1605
+ if (maxBytes) {
1606
+ result.max = decodeStatisticsValueSafely(toUint8Array(maxBytes), field);
1607
+ }
1608
+ if (
1609
+ result.min === undefined &&
1610
+ result.max === undefined &&
1611
+ result.nullCount === undefined &&
1612
+ result.distinctCount === undefined &&
1613
+ result.minIsExact === undefined &&
1614
+ result.maxIsExact === undefined
1615
+ ) {
1616
+ return undefined;
1617
+ }
1618
+ return Object.freeze(result);
1619
+ }
1620
+
1621
+ /** Decodes one footer statistic and returns undefined for malformed or unsupported values. */
1622
+ function decodeStatisticsValueSafely(bytes: Uint8Array, field: ParquetField): unknown {
1623
+ try {
1624
+ let primitiveValue: unknown;
1625
+ switch (field.primitiveType) {
1626
+ case 'BOOLEAN':
1627
+ primitiveValue = Boolean(bytes[0]);
1628
+ break;
1629
+ case 'INT32':
1630
+ primitiveValue =
1631
+ field.originalType === 'UINT_32' ? readUInt32LE(bytes, 0) : readInt32LE(bytes, 0);
1632
+ break;
1633
+ case 'INT64':
1634
+ primitiveValue =
1635
+ field.originalType === 'UINT_64' ? readUInt64LE(bytes, 0) : readInt64LE(bytes, 0);
1636
+ break;
1637
+ case 'FLOAT':
1638
+ primitiveValue = readFloatLE(bytes, 0);
1639
+ break;
1640
+ case 'DOUBLE':
1641
+ primitiveValue = readDoubleLE(bytes, 0);
1642
+ break;
1643
+ case 'INT96':
1644
+ case 'BYTE_ARRAY':
1645
+ case 'FIXED_LEN_BYTE_ARRAY':
1646
+ primitiveValue = copyUint8Array(bytes);
1647
+ break;
1648
+ default:
1649
+ return undefined;
1650
+ }
1651
+ return Types.fromPrimitive(field.originalType || field.primitiveType, primitiveValue, field);
1652
+ } catch {
1653
+ return undefined;
1654
+ }
1655
+ }
1656
+
1657
+ /** Normalizes one decoded row group and derives its compressed byte length. */
1658
+ function createRowGroupMetadata(
1659
+ index: number,
1660
+ rowOffset: number,
1661
+ rowCount: number,
1662
+ uncompressedByteLength: number,
1663
+ columns: ParquetColumnChunkMetadata[],
1664
+ sortingColumns: RowGroup['sorting_columns']
1665
+ ): ParquetRowGroupMetadata {
1666
+ const compressedByteLength = columns.reduce(
1667
+ (sum, column) => sum + column.compressedByteLength,
1668
+ 0
1669
+ );
1670
+ return Object.freeze({
1671
+ index,
1672
+ rowOffset,
1673
+ rowCount,
1674
+ uncompressedByteLength,
1675
+ uncompressedSize: uncompressedByteLength,
1676
+ compressedByteLength,
1677
+ compressedSize: compressedByteLength,
1678
+ columns: Object.freeze(columns),
1679
+ sortingColumns: Object.freeze(
1680
+ (sortingColumns || []).map(
1681
+ sortingColumn =>
1682
+ Object.freeze({
1683
+ columnIndex: sortingColumn.column_idx,
1684
+ descending: sortingColumn.descending,
1685
+ nullsFirst: sortingColumn.nulls_first
1686
+ }) satisfies ParquetSortingColumn
1687
+ )
1688
+ )
1689
+ });
1690
+ }
1691
+
1692
+ /** Describes a phase that reads complete column chunks because no page index is available. */
1693
+ function createFullColumnScanPlan(
1694
+ rowGroup: RowGroup,
1695
+ rowGroupIndex: number,
1696
+ phase: ParquetPageScanPlan['phase'],
1697
+ columns: readonly string[][]
1698
+ ): ParquetPageScanPlan {
1699
+ const selectedColumnChunks = rowGroup.columns.filter(columnChunk => {
1700
+ const path = columnChunk.meta_data?.path_in_schema;
1701
+ return Boolean(
1702
+ path && (columns.length === 0 || columns.some(column => pathMatchesSelection(column, path)))
1703
+ );
1704
+ });
1705
+ const rowCount = Number(rowGroup.num_rows);
1706
+ return Object.freeze({
1707
+ rowGroupIndex,
1708
+ phase,
1709
+ columns: Object.freeze(columns.map(column => Object.freeze([...column]))),
1710
+ rowRanges: Object.freeze([Object.freeze({start: 0, end: rowCount})]),
1711
+ indexesRead: 0,
1712
+ totalPages: 0,
1713
+ selectedPages: 0,
1714
+ rowsPruned: 0,
1715
+ ranges: Object.freeze(
1716
+ selectedColumnChunks.map(columnChunk => Object.freeze(getColumnChunkRange(columnChunk)))
1717
+ )
1718
+ });
1719
+ }
1720
+
1721
+ /** Returns whether a requested path selects a footer leaf or one of its nested children. */
1722
+ function pathMatchesSelection(selection: readonly string[], path: readonly string[]): boolean {
1723
+ return selection.length <= path.length && selection.every((part, index) => part === path[index]);
1724
+ }
1725
+
1726
+ /** Returns the complete contiguous byte range occupied by one Parquet column chunk. */
1727
+ function getColumnChunkRange(columnChunk: ColumnChunk): {offset: number; length: number} {
1728
+ if (columnChunk.file_path) {
1729
+ throw new Error('Parquet worker decoding does not support external column chunk files');
1730
+ }
1731
+ const columnMetadata = columnChunk.meta_data;
1732
+ if (!columnMetadata) {
1733
+ throw new Error('Parquet column chunk is missing metadata');
1734
+ }
1735
+ const dataPageOffset = Number(columnMetadata.data_page_offset);
1736
+ const dictionaryPageOffset =
1737
+ columnMetadata.dictionary_page_offset === undefined
1738
+ ? undefined
1739
+ : Number(columnMetadata.dictionary_page_offset);
1740
+ const offset = Math.min(
1741
+ dataPageOffset,
1742
+ dictionaryPageOffset && dictionaryPageOffset > 0 ? dictionaryPageOffset : dataPageOffset
1743
+ );
1744
+ const length = Number(columnMetadata.total_compressed_size);
1745
+ if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 0) {
1746
+ throw new Error('Parquet column chunk range must use non-negative safe integers');
1747
+ }
1748
+ return {offset, length};
1749
+ }
1750
+
1751
+ /** Copies one selected Thrift column chunk into a structured-cloneable worker descriptor. */
1752
+ function createParquetSourceWorkerColumnChunk(
1753
+ columnChunk: ColumnChunk
1754
+ ): ParquetSourceWorkerColumnChunk {
1755
+ const columnMetadata = columnChunk.meta_data;
1756
+ if (!columnMetadata) {
1757
+ throw new Error('Parquet column chunk is missing metadata');
1758
+ }
1759
+ return {
1760
+ filePath: columnChunk.file_path || undefined,
1761
+ physicalType: columnMetadata.type,
1762
+ compressionCodec: columnMetadata.codec,
1763
+ path: [...columnMetadata.path_in_schema],
1764
+ valueCount: Number(columnMetadata.num_values),
1765
+ compressedByteLength: Number(columnMetadata.total_compressed_size),
1766
+ uncompressedByteLength: Number(columnMetadata.total_uncompressed_size),
1767
+ dataPageOffset: Number(columnMetadata.data_page_offset),
1768
+ dictionaryPageOffset:
1769
+ columnMetadata.dictionary_page_offset === undefined
1770
+ ? undefined
1771
+ : Number(columnMetadata.dictionary_page_offset)
1772
+ };
1773
+ }
1774
+
1775
+ /** Converts decoded columns into one Arrow batch and attaches stable source provenance. */
1776
+ function createParquetBatch(
1777
+ metadata: ParquetSourceMetadata,
1778
+ schema: Schema,
1779
+ rowGroupIndex: number,
1780
+ rowGroupRowOffset: number,
1781
+ columns: Record<string, ArrayType>,
1782
+ rowCount: number,
1783
+ rowGroupRowIndices?: readonly number[]
1784
+ ): ParquetBatch {
1785
+ const arrowTable = convertTable({shape: 'columnar-table', schema, data: columns}, 'arrow-table');
1786
+ return createParquetBatchFromArrow(
1787
+ metadata,
1788
+ schema,
1789
+ rowGroupIndex,
1790
+ rowGroupRowOffset,
1791
+ arrowTable.data,
1792
+ rowCount,
1793
+ rowGroupRowIndices
1794
+ );
1795
+ }
1796
+
1797
+ /** Wraps a worker-transferred or locally converted Arrow table with source provenance. */
1798
+ function createParquetBatchFromArrow(
1799
+ metadata: ParquetSourceMetadata,
1800
+ schema: Schema,
1801
+ rowGroupIndex: number,
1802
+ rowGroupRowOffset: number,
1803
+ data: ArrowTable['data'],
1804
+ rowCount: number,
1805
+ rowGroupRowIndices?: readonly number[]
1806
+ ): ParquetBatch {
1807
+ const rowGroup = metadata.rowGroups[rowGroupIndex];
1808
+ const sourceId = metadata.url || metadata.name;
1809
+ const provenance = Object.freeze({
1810
+ sourceId,
1811
+ sourceUrl: metadata.url,
1812
+ source: sourceId,
1813
+ rowGroupIndex,
1814
+ rowOffset: rowGroup.rowOffset + rowGroupRowOffset,
1815
+ rowGroupRowOffset,
1816
+ rowCount,
1817
+ rowGroupRowIndices: rowGroupRowIndices ? Object.freeze([...rowGroupRowIndices]) : undefined,
1818
+ rowIndices: rowGroupRowIndices
1819
+ ? Object.freeze(rowGroupRowIndices.map(rowIndex => rowGroup.rowOffset + rowIndex))
1820
+ : undefined
1821
+ });
1822
+ return {
1823
+ ...makeTableScanBatch({shape: 'arrow-table', schema, data}),
1824
+ schemaType: 'explicit',
1825
+ length: rowCount,
1826
+ metadata: provenance,
1827
+ ...provenance
1828
+ };
1829
+ }
1830
+
1831
+ /** Returns a contiguous row slice of every decoded column without constructing row objects. */
1832
+ function sliceColumns(
1833
+ columns: Record<string, ArrayType>,
1834
+ start: number,
1835
+ end: number
1836
+ ): Record<string, ArrayType> {
1837
+ const slicedColumns: Record<string, ArrayType> = {};
1838
+ for (const [name, column] of Object.entries(columns)) {
1839
+ const slice = (column as ArrayType & {slice?: (start: number, end: number) => ArrayType}).slice;
1840
+ slicedColumns[name] = slice
1841
+ ? slice.call(column, start, end)
1842
+ : Array.prototype.slice.call(column, start, end);
1843
+ }
1844
+ return slicedColumns;
1845
+ }
1846
+
1847
+ /** Concatenates materialized page-range fragments without constructing row objects. */
1848
+ function concatenateMaterializedColumns(
1849
+ fragments: readonly Record<string, ArrayType>[]
1850
+ ): Record<string, ArrayType> {
1851
+ const columns: Record<string, unknown[]> = {};
1852
+ for (const fragment of fragments) {
1853
+ for (const [name, values] of Object.entries(fragment)) {
1854
+ columns[name] ||= [];
1855
+ const destination = columns[name];
1856
+ for (let index = 0; index < values.length; index++) {
1857
+ destination.push(values[index]);
1858
+ }
1859
+ }
1860
+ }
1861
+ return columns as Record<string, ArrayType>;
1862
+ }
1863
+
1864
+ /** Validates and normalizes requested row-group indexes. */
1865
+ function normalizeRowGroupIndices(
1866
+ rowGroups: readonly number[] | undefined,
1867
+ rowGroupCount: number
1868
+ ): number[] {
1869
+ const rowGroupIndices = rowGroups || Array.from({length: rowGroupCount}, (_, index) => index);
1870
+ const seenIndices = new Set<number>();
1871
+ for (const rowGroupIndex of rowGroupIndices) {
1872
+ if (!Number.isInteger(rowGroupIndex) || rowGroupIndex < 0 || rowGroupIndex >= rowGroupCount) {
1873
+ throw new Error(`Invalid Parquet row-group index ${rowGroupIndex}`);
1874
+ }
1875
+ if (seenIndices.has(rowGroupIndex)) {
1876
+ throw new Error(`Duplicate Parquet row-group index ${rowGroupIndex}`);
1877
+ }
1878
+ seenIndices.add(rowGroupIndex);
1879
+ }
1880
+ return [...rowGroupIndices];
1881
+ }
1882
+
1883
+ /** Validates selected top-level columns against the source schema. */
1884
+ function normalizeColumns(columns: readonly string[] | undefined, schema: Schema): string[] {
1885
+ if (!columns?.length) {
1886
+ return [];
1887
+ }
1888
+ const availableColumns = new Set(schema.fields.map(field => field.name));
1889
+ const normalizedColumns: string[] = [];
1890
+ for (const column of columns) {
1891
+ if (!availableColumns.has(column)) {
1892
+ throw new Error(`Parquet column not found: ${column}`);
1893
+ }
1894
+ if (!normalizedColumns.includes(column)) {
1895
+ normalizedColumns.push(column);
1896
+ }
1897
+ }
1898
+ return normalizedColumns;
1899
+ }
1900
+
1901
+ /** Returns a schema containing only explicitly selected top-level fields. */
1902
+ function projectSchema(schema: Schema, columns: string[]): Schema {
1903
+ if (columns.length === 0) {
1904
+ return schema;
1905
+ }
1906
+ const selectedColumns = new Set(columns);
1907
+ return {...schema, fields: schema.fields.filter(field => selectedColumns.has(field.name))};
1908
+ }
1909
+
1910
+ /** Validates an optional output batch size. */
1911
+ function normalizeBatchSize(batchSize?: number): number | undefined {
1912
+ if (batchSize === undefined) {
1913
+ return undefined;
1914
+ }
1915
+ if (!Number.isInteger(batchSize) || batchSize <= 0) {
1916
+ throw new Error(`Invalid Parquet batch size ${batchSize}`);
1917
+ }
1918
+ return batchSize;
1919
+ }
1920
+
1921
+ /** Validates the row-group decode concurrency limit. */
1922
+ function normalizeConcurrency(concurrency = 1): number {
1923
+ if (!Number.isInteger(concurrency) || concurrency <= 0) {
1924
+ throw new Error(`Invalid Parquet concurrency ${concurrency}`);
1925
+ }
1926
+ return concurrency;
1927
+ }
1928
+
1929
+ /** Creates a zero-filled telemetry snapshot for one Parquet source. */
1930
+ function createParquetTelemetry(): ParquetTelemetry {
1931
+ return {
1932
+ rangeRequestCount: 0,
1933
+ requestedBytes: 0,
1934
+ downloadedBytes: 0,
1935
+ cacheHits: 0,
1936
+ networkDurationMs: 0,
1937
+ failedRangeRequestCount: 0,
1938
+ abortedRangeRequestCount: 0,
1939
+ retryCount: 0,
1940
+ decodeDurationMs: 0,
1941
+ arrowConversionDurationMs: 0,
1942
+ rowGroupsRequested: 0,
1943
+ rowGroupsPruned: 0,
1944
+ rowGroupsPrunedByStatistics: 0,
1945
+ rowGroupsPrunedByBloomFilter: 0,
1946
+ bloomFiltersRead: 0,
1947
+ bloomFilterBytesRead: 0,
1948
+ rowGroupsPrunedByPageIndex: 0,
1949
+ pageIndexesRead: 0,
1950
+ pagesRead: 0,
1951
+ pagesPruned: 0,
1952
+ rowsPrunedByPageIndex: 0,
1953
+ rowGroupsDecoded: 0,
1954
+ batchesEmitted: 0,
1955
+ rowsEmitted: 0,
1956
+ predicateRowsTested: 0,
1957
+ predicateRowsMatched: 0,
1958
+ cancellationCount: 0,
1959
+ failedReadCount: 0
1960
+ };
1961
+ }
1962
+
1963
+ /** Converts cumulative Parquet counters into one portable read-scoped execution snapshot. */
1964
+ function createParquetScanExecutionTelemetry(
1965
+ before: ParquetTelemetry,
1966
+ after: ParquetTelemetry,
1967
+ startedAt: number,
1968
+ status: ScanExecutionTelemetry['status'],
1969
+ earlyTerminationReason?: ScanExecutionTelemetry['earlyTerminationReason'],
1970
+ error?: unknown
1971
+ ): ScanExecutionTelemetry {
1972
+ const delta = Object.fromEntries(
1973
+ Object.keys(after).map(key => {
1974
+ const telemetryKey = key as keyof ParquetTelemetry;
1975
+ return [key, after[telemetryKey] - before[telemetryKey]];
1976
+ })
1977
+ ) as ParquetTelemetry;
1978
+ const rowsRead = Math.max(delta.predicateRowsTested, delta.rowsEmitted);
1979
+ return Object.freeze({
1980
+ status,
1981
+ sourcesPlanned: 1,
1982
+ sourcesRead:
1983
+ status === 'early-terminated' && earlyTerminationReason === 'limit' && rowsRead === 0 ? 0 : 1,
1984
+ batchesRead: delta.batchesEmitted,
1985
+ batchesDecoded: delta.rowGroupsDecoded,
1986
+ rowsRead,
1987
+ rowsTested: delta.predicateRowsTested || undefined,
1988
+ rowsRetained: delta.predicateRowsMatched || undefined,
1989
+ rowsReturned: delta.rowsEmitted,
1990
+ bytesRead: delta.downloadedBytes,
1991
+ bytesFetched: delta.downloadedBytes,
1992
+ filesOpened: rowsRead || delta.downloadedBytes ? 1 : 0,
1993
+ tasksOpened: delta.rowGroupsDecoded,
1994
+ rowsPruned: delta.rowsPrunedByPageIndex,
1995
+ durationMilliseconds: Date.now() - startedAt,
1996
+ earlyTerminationReason,
1997
+ details: Object.freeze({...delta}),
1998
+ ...(error === undefined ? {} : {error})
1999
+ });
2000
+ }
2001
+
2002
+ /** Returns a monotonic timestamp when available and falls back to wall-clock time. */
2003
+ function getCurrentTime(): number {
2004
+ return globalThis.performance?.now() ?? Date.now();
2005
+ }
2006
+
2007
+ /** Creates a read-scoped abort controller linked to the caller signal. */
2008
+ function createReadAbortContext(signal?: AbortSignal): {
2009
+ /** Controller owned by the source read operation. */
2010
+ abortController: AbortController;
2011
+ /** Removes the caller-signal listener after the read settles. */
2012
+ removeSignalListener: () => void;
2013
+ } {
2014
+ const abortController = new AbortController();
2015
+ const abortRead = (): void => abortController.abort(signal?.reason);
2016
+ if (signal?.aborted) {
2017
+ abortRead();
2018
+ } else {
2019
+ signal?.addEventListener('abort', abortRead, {once: true});
2020
+ }
2021
+ return {
2022
+ abortController,
2023
+ removeSignalListener: () => signal?.removeEventListener('abort', abortRead)
2024
+ };
2025
+ }
2026
+
2027
+ /** Throws a cross-runtime AbortError when a signal has been aborted. */
2028
+ function throwIfAborted(signal: AbortSignal): void {
2029
+ if (!signal.aborted) {
2030
+ return;
2031
+ }
2032
+ if (signal.reason !== undefined) {
2033
+ throw signal.reason;
2034
+ }
2035
+ const error = new Error('Parquet read aborted');
2036
+ error.name = 'AbortError';
2037
+ throw error;
2038
+ }
2039
+
2040
+ /** Recursively freezes the plain schema tree cached by a source. */
2041
+ function deepFreeze<T>(value: T): T {
2042
+ if (!value || typeof value !== 'object' || Object.isFrozen(value)) {
2043
+ return value;
2044
+ }
2045
+ for (const nestedValue of Object.values(value)) {
2046
+ deepFreeze(nestedValue);
2047
+ }
2048
+ return Object.freeze(value);
2049
+ }
2050
+
2051
+ /** Converts footer key/value pairs into an object. */
2052
+ function getKeyValueMetadata(fileMetadata: FileMetaData): Record<string, string> {
2053
+ const result: Record<string, string> = {};
2054
+ for (const entry of fileMetadata.key_value_metadata || []) {
2055
+ if (entry.value !== undefined) {
2056
+ result[entry.key] = entry.value;
2057
+ }
2058
+ }
2059
+ return result;
2060
+ }
2061
+
2062
+ /** Infers a stable display name from URL or File input. */
2063
+ function getSourceName(data: string | Blob, url: string): string {
2064
+ const sourceName =
2065
+ typeof data !== 'string' && 'name' in data && typeof data.name === 'string'
2066
+ ? data.name
2067
+ : typeof data === 'string'
2068
+ ? url.split(/[?#]/)[0].split('/').pop() || 'parquet'
2069
+ : 'parquet';
2070
+ return sourceName.replace(/\.parquet$/i, '') || 'parquet';
2071
+ }
2072
+
2073
+ type ParquetBloomFilterReadResult = {
2074
+ rowGroupIndices: number[];
2075
+ filtersRead: number;
2076
+ bytesRead: number;
2077
+ };
2078
+
2079
+ /** Reconstructs exact row-group indexes from worker predicate batches. */
2080
+ function getWorkerRowIndices(workerResult?: ParquetSourceWorkerResult): number[] {
2081
+ if (!workerResult) return [];
2082
+ return workerResult.batches.flatMap(batch =>
2083
+ batch.rowGroupRowIndices
2084
+ ? [...batch.rowGroupRowIndices]
2085
+ : Array.from({length: batch.rowCount}, (_, index) => batch.rowGroupRowOffset + index)
2086
+ );
2087
+ }
2088
+
2089
+ /** Gathers projected worker Arrow batches through the shared indexed Arrow table utility. */
2090
+ function gatherWorkerProjectedRows(
2091
+ workerResult: ParquetSourceWorkerResult,
2092
+ rowIndices: readonly number[],
2093
+ batchSize: number | undefined
2094
+ ): ParquetSourceWorkerResult {
2095
+ const tables = workerResult.batches.map(batch => hydrateArrowTable(batch.arrowTable));
2096
+ if (tables.length === 0) {
2097
+ return {...workerResult, rowCount: 0, batches: []};
2098
+ }
2099
+ const table = tables.length === 1 ? tables[0] : tables[0].concat(...tables.slice(1));
2100
+ const indexedTable = new IndexedArrowTable(table, rowIndices);
2101
+ const outputBatchSize = batchSize || Math.max(rowIndices.length, 1);
2102
+ const batches: ParquetSourceWorkerResult['batches'] = [];
2103
+ for (let offset = 0; offset < rowIndices.length; offset += outputBatchSize) {
2104
+ const end = Math.min(offset + outputBatchSize, rowIndices.length);
2105
+ const batchIndexes = rowIndices.slice(offset, end);
2106
+ const batchTable = indexedTable.slice(offset, end).materializeArrowTable();
2107
+ batches.push({
2108
+ rowGroupRowOffset: batchIndexes[0],
2109
+ rowCount: batchIndexes.length,
2110
+ rowGroupRowIndices: [...batchIndexes],
2111
+ arrowTable: dehydrateArrowTable(batchTable)
2112
+ });
2113
+ }
2114
+ return {
2115
+ sourceRowCount: workerResult.sourceRowCount,
2116
+ rowCount: rowIndices.length,
2117
+ batches,
2118
+ decodeDurationMs: workerResult.decodeDurationMs,
2119
+ arrowConversionDurationMs: workerResult.arrowConversionDurationMs
2120
+ };
2121
+ }
2122
+
2123
+ /** Applies only proven-negative Bloom-filter checks to candidate row groups. */
2124
+ async function filterParquetRowGroupsWithBloomFilters(
2125
+ initialization: ParquetSourceInitialization,
2126
+ rowGroupIndices: readonly number[],
2127
+ predicate: ParquetPredicate,
2128
+ signal: AbortSignal
2129
+ ): Promise<ParquetBloomFilterReadResult> {
2130
+ const rowGroups: number[] = [];
2131
+ let filtersRead = 0;
2132
+ let bytesRead = 0;
2133
+ for (const rowGroupIndex of rowGroupIndices) {
2134
+ let rowGroup = initialization.metadata.rowGroups[rowGroupIndex];
2135
+ const rawRowGroup = initialization.fileMetadata.row_groups[rowGroupIndex];
2136
+ if (rawRowGroup.columns.some(columnChunk => columnChunk.encrypted_column_metadata)) {
2137
+ // The immutable source metadata predates selective column-metadata decryption. Resolve
2138
+ // predicate columns before probing, without changing the plaintext planning path.
2139
+ await initialization.reader.resolveColumnMetadata(
2140
+ rawRowGroup,
2141
+ rowGroupIndex,
2142
+ getParquetPredicateColumns(predicate).map(column => [column])
2143
+ );
2144
+ throwIfAborted(signal);
2145
+ rowGroup = {
2146
+ ...rowGroup,
2147
+ columns: rawRowGroup.columns
2148
+ .filter(columnChunk => Boolean(columnChunk.meta_data))
2149
+ .map(columnChunk => createColumnChunkMetadata(columnChunk, initialization.parquetSchema))
2150
+ };
2151
+ }
2152
+ const probes = getParquetBloomFilterProbes(predicate, rowGroup);
2153
+ let keepRowGroup = true;
2154
+ for (const probe of probes) {
2155
+ if (
2156
+ probe.column.bloomFilterOffset === undefined ||
2157
+ probe.column.bloomFilterByteLength === undefined
2158
+ ) {
2159
+ continue;
2160
+ }
2161
+ let field: ParquetField;
2162
+ try {
2163
+ field = initialization.parquetSchema.findField([...probe.column.path]);
2164
+ } catch {
2165
+ throwIfAborted(signal);
2166
+ continue;
2167
+ }
2168
+ if (!field.primitiveType) continue;
2169
+ let filter: ReturnType<typeof decodeParquetSplitBlockBloomFilter>;
2170
+ try {
2171
+ const rawColumnOrdinal = initialization.fileMetadata.row_groups[
2172
+ rowGroupIndex
2173
+ ].columns.findIndex(
2174
+ columnChunk =>
2175
+ JSON.stringify(columnChunk.meta_data?.path_in_schema) ===
2176
+ JSON.stringify(probe.column.path)
2177
+ );
2178
+ const rawColumnChunk =
2179
+ rawColumnOrdinal >= 0
2180
+ ? initialization.fileMetadata.row_groups[rowGroupIndex].columns[rawColumnOrdinal]
2181
+ : undefined;
2182
+ const data = await initialization.file.read(
2183
+ probe.column.bloomFilterOffset,
2184
+ probe.column.bloomFilterByteLength,
2185
+ signal
2186
+ );
2187
+ const decodedBloomFilter = rawColumnChunk
2188
+ ? await initialization.reader.decryptBloomFilter(
2189
+ toUint8Array(data),
2190
+ rowGroupIndex,
2191
+ rawColumnOrdinal,
2192
+ rawColumnChunk
2193
+ )
2194
+ : toUint8Array(data);
2195
+ filter = decodeParquetSplitBlockBloomFilter(decodedBloomFilter);
2196
+ filtersRead++;
2197
+ bytesRead += data.byteLength;
2198
+ } catch {
2199
+ throwIfAborted(signal);
2200
+ continue;
2201
+ }
2202
+ if (
2203
+ filter.algorithm !== 'BLOCK' ||
2204
+ filter.hash !== 'XXHASH' ||
2205
+ filter.compression !== 'UNCOMPRESSED'
2206
+ ) {
2207
+ continue;
2208
+ }
2209
+ const mayContainValue = probe.values.some(value => {
2210
+ try {
2211
+ if (value instanceof Date) return true;
2212
+ const physicalValue = getParquetBloomFilterPhysicalValue(value, field);
2213
+ const encoded = encodeParquetBloomFilterValue(
2214
+ physicalValue,
2215
+ field.primitiveType as Parameters<typeof encodeParquetBloomFilterValue>[1],
2216
+ field.typeLength
2217
+ );
2218
+ return checkParquetSplitBlockBloomFilter(
2219
+ filter.bitset,
2220
+ hashParquetBloomFilterValue(encoded)
2221
+ );
2222
+ } catch {
2223
+ throwIfAborted(signal);
2224
+ return true;
2225
+ }
2226
+ });
2227
+ if (!mayContainValue) {
2228
+ keepRowGroup = false;
2229
+ break;
2230
+ }
2231
+ }
2232
+ if (keepRowGroup) rowGroups.push(rowGroupIndex);
2233
+ }
2234
+ return {rowGroupIndices: rowGroups, filtersRead, bytesRead};
2235
+ }
2236
+
2237
+ /** Converts logical predicate values into the physical values stored in Bloom filters. */
2238
+ function getParquetBloomFilterPhysicalValue(
2239
+ value: unknown,
2240
+ field: ParquetField
2241
+ ): boolean | number | bigint | string | Uint8Array {
2242
+ if (field.originalType?.startsWith('DECIMAL_')) {
2243
+ return toPrimitive(field.originalType as ParquetType, value, field) as
2244
+ | number
2245
+ | bigint
2246
+ | Uint8Array;
2247
+ }
2248
+ return value as boolean | number | bigint | string | Uint8Array;
2249
+ }
2250
+
2251
+ /** Returns true when at least one HTTP object validator was captured. */
2252
+ function hasObjectVersion(version?: ParquetObjectVersion): version is ParquetObjectVersion {
2253
+ return Boolean(version?.etag || version?.lastModified);
2254
+ }