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

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 (336) hide show
  1. package/LICENSE +1 -1
  2. package/dist/bundled.d.ts +1 -1
  3. package/dist/bundled.d.ts.map +1 -1
  4. package/dist/bundled.js.map +1 -1
  5. package/dist/delta-format.d.ts +9 -0
  6. package/dist/delta-format.d.ts.map +1 -0
  7. package/dist/delta-format.js +12 -0
  8. package/dist/delta-format.js.map +1 -0
  9. package/dist/delta-source-loader-types.d.ts +27 -0
  10. package/dist/delta-source-loader-types.d.ts.map +1 -0
  11. package/dist/delta-source-loader-types.js +28 -0
  12. package/dist/delta-source-loader-types.js.map +1 -0
  13. package/dist/delta-source.d.ts +48 -0
  14. package/dist/delta-source.d.ts.map +1 -0
  15. package/dist/delta-source.js +222 -0
  16. package/dist/delta-source.js.map +1 -0
  17. package/dist/delta-types.d.ts +38 -0
  18. package/dist/delta-types.d.ts.map +1 -0
  19. package/dist/delta-types.js +5 -0
  20. package/dist/delta-types.js.map +1 -0
  21. package/dist/geoparquet-loader.js +1 -1
  22. package/dist/iceberg-rest-catalog.d.ts +49 -0
  23. package/dist/iceberg-rest-catalog.d.ts.map +1 -0
  24. package/dist/iceberg-rest-catalog.js +78 -0
  25. package/dist/iceberg-rest-catalog.js.map +1 -0
  26. package/dist/iceberg-table-source.d.ts +63 -0
  27. package/dist/iceberg-table-source.d.ts.map +1 -0
  28. package/dist/iceberg-table-source.js +797 -0
  29. package/dist/iceberg-table-source.js.map +1 -0
  30. package/dist/iceberg-types.d.ts +150 -0
  31. package/dist/iceberg-types.d.ts.map +1 -0
  32. package/dist/iceberg-types.js +5 -0
  33. package/dist/iceberg-types.js.map +1 -0
  34. package/dist/index.cjs +15841 -3306
  35. package/dist/index.cjs.map +3 -3
  36. package/dist/index.d.ts +8 -3
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +5 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/lib/arrow/convert-schema-from-parquet.d.ts +3 -1
  41. package/dist/lib/arrow/convert-schema-from-parquet.d.ts.map +1 -1
  42. package/dist/lib/arrow/convert-schema-from-parquet.js +94 -19
  43. package/dist/lib/arrow/convert-schema-from-parquet.js.map +1 -1
  44. package/dist/lib/arrow/create-nested-arrow-vector.js +89 -0
  45. package/dist/lib/arrow/create-nested-arrow-vector.js.map +1 -1
  46. package/dist/lib/constants.js +1 -1
  47. package/dist/lib/encoders/encode-table-to-parquet-js.d.ts.map +1 -1
  48. package/dist/lib/encoders/encode-table-to-parquet-js.js +202 -7
  49. package/dist/lib/encoders/encode-table-to-parquet-js.js.map +1 -1
  50. package/dist/lib/geo/geoparquet-covering.d.ts +19 -0
  51. package/dist/lib/geo/geoparquet-covering.d.ts.map +1 -0
  52. package/dist/lib/geo/geoparquet-covering.js +137 -0
  53. package/dist/lib/geo/geoparquet-covering.js.map +1 -0
  54. package/dist/lib/geo/geospatial-metadata.d.ts.map +1 -1
  55. package/dist/lib/geo/geospatial-metadata.js +39 -13
  56. package/dist/lib/geo/geospatial-metadata.js.map +1 -1
  57. package/dist/lib/parquet-bloom-filter-planner.d.ts +16 -0
  58. package/dist/lib/parquet-bloom-filter-planner.d.ts.map +1 -0
  59. package/dist/lib/parquet-bloom-filter-planner.js +57 -0
  60. package/dist/lib/parquet-bloom-filter-planner.js.map +1 -0
  61. package/dist/lib/parquet-bloom-filter.d.ts +30 -0
  62. package/dist/lib/parquet-bloom-filter.d.ts.map +1 -0
  63. package/dist/lib/parquet-bloom-filter.js +289 -0
  64. package/dist/lib/parquet-bloom-filter.js.map +1 -0
  65. package/dist/lib/parquet-encryption.d.ts +80 -0
  66. package/dist/lib/parquet-encryption.d.ts.map +1 -0
  67. package/dist/lib/parquet-encryption.js +190 -0
  68. package/dist/lib/parquet-encryption.js.map +1 -0
  69. package/dist/lib/parquet-page-index.d.ts +34 -5
  70. package/dist/lib/parquet-page-index.d.ts.map +1 -1
  71. package/dist/lib/parquet-page-index.js +114 -28
  72. package/dist/lib/parquet-page-index.js.map +1 -1
  73. package/dist/lib/parquet-predicate.d.ts +5 -1
  74. package/dist/lib/parquet-predicate.d.ts.map +1 -1
  75. package/dist/lib/parquet-predicate.js +42 -14
  76. package/dist/lib/parquet-predicate.js.map +1 -1
  77. package/dist/lib/parquet-source-worker-decoder.d.ts +4 -1
  78. package/dist/lib/parquet-source-worker-decoder.d.ts.map +1 -1
  79. package/dist/lib/parquet-source-worker-decoder.js +83 -17
  80. package/dist/lib/parquet-source-worker-decoder.js.map +1 -1
  81. package/dist/lib/parquet-source-worker-types.d.ts +24 -0
  82. package/dist/lib/parquet-source-worker-types.d.ts.map +1 -1
  83. package/dist/lib/parsers/get-parquet-schema.d.ts.map +1 -1
  84. package/dist/lib/parsers/get-parquet-schema.js +3 -1
  85. package/dist/lib/parsers/get-parquet-schema.js.map +1 -1
  86. package/dist/lib/parsers/parse-parquet-to-arrow-js.d.ts.map +1 -1
  87. package/dist/lib/parsers/parse-parquet-to-arrow-js.js +327 -18
  88. package/dist/lib/parsers/parse-parquet-to-arrow-js.js.map +1 -1
  89. package/dist/lib/parsers/parse-parquet-to-arrow.d.ts.map +1 -1
  90. package/dist/lib/parsers/parse-parquet-to-arrow.js +5 -5
  91. package/dist/lib/parsers/parse-parquet-to-arrow.js.map +1 -1
  92. package/dist/lib/parsers/parse-parquet-to-columns.d.ts.map +1 -1
  93. package/dist/lib/parsers/parse-parquet-to-columns.js +3 -1
  94. package/dist/lib/parsers/parse-parquet-to-columns.js.map +1 -1
  95. package/dist/lib/parsers/parse-parquet-to-json.d.ts.map +1 -1
  96. package/dist/lib/parsers/parse-parquet-to-json.js +18 -2
  97. package/dist/lib/parsers/parse-parquet-to-json.js.map +1 -1
  98. package/dist/lib/slice-parquet-batch.d.ts +9 -0
  99. package/dist/lib/slice-parquet-batch.d.ts.map +1 -0
  100. package/dist/lib/slice-parquet-batch.js +28 -0
  101. package/dist/lib/slice-parquet-batch.js.map +1 -0
  102. package/dist/lib/sources/parquet-range-file.d.ts +1 -1
  103. package/dist/lib/sources/parquet-range-file.d.ts.map +1 -1
  104. package/dist/lib/sources/parquet-range-file.js +28 -22
  105. package/dist/lib/sources/parquet-range-file.js.map +1 -1
  106. package/dist/lib/utils/normalize-parquet-options.d.ts.map +1 -1
  107. package/dist/lib/utils/normalize-parquet-options.js +15 -1
  108. package/dist/lib/utils/normalize-parquet-options.js.map +1 -1
  109. package/dist/parquet-dataset-source.cjs +16528 -4928
  110. package/dist/parquet-dataset-source.cjs.map +3 -3
  111. package/dist/parquet-dataset-source.d.ts +31 -4
  112. package/dist/parquet-dataset-source.d.ts.map +1 -1
  113. package/dist/parquet-dataset-source.js +296 -74
  114. package/dist/parquet-dataset-source.js.map +1 -1
  115. package/dist/parquet-encoded-page-types.d.ts +108 -0
  116. package/dist/parquet-encoded-page-types.d.ts.map +1 -0
  117. package/dist/parquet-encoded-page-types.js +5 -0
  118. package/dist/parquet-encoded-page-types.js.map +1 -0
  119. package/dist/parquet-js-loader-types.d.ts +2 -0
  120. package/dist/parquet-js-loader-types.d.ts.map +1 -1
  121. package/dist/parquet-js-loader-types.js +2 -1
  122. package/dist/parquet-js-loader-types.js.map +1 -1
  123. package/dist/parquet-js-loader.d.ts +1 -0
  124. package/dist/parquet-js-loader.d.ts.map +1 -1
  125. package/dist/parquet-js-loader.js +2 -1
  126. package/dist/parquet-js-loader.js.map +1 -1
  127. package/dist/parquet-js-writer.d.ts +23 -2
  128. package/dist/parquet-js-writer.d.ts.map +1 -1
  129. package/dist/parquet-js-writer.js +1 -1
  130. package/dist/parquet-js-writer.js.map +1 -1
  131. package/dist/parquet-loader-base.js +1 -1
  132. package/dist/parquet-loader-options.d.ts +11 -0
  133. package/dist/parquet-loader-options.d.ts.map +1 -1
  134. package/dist/parquet-loader-options.js.map +1 -1
  135. package/dist/parquet-source-capabilities.d.ts +8 -1
  136. package/dist/parquet-source-capabilities.d.ts.map +1 -1
  137. package/dist/parquet-source-capabilities.js +10 -0
  138. package/dist/parquet-source-capabilities.js.map +1 -1
  139. package/dist/parquet-source-loader-types.d.ts +10 -0
  140. package/dist/parquet-source-loader-types.d.ts.map +1 -1
  141. package/dist/parquet-source-loader-types.js +11 -1
  142. package/dist/parquet-source-loader-types.js.map +1 -1
  143. package/dist/parquet-source-loader.cjs +14121 -2796
  144. package/dist/parquet-source-loader.cjs.map +3 -3
  145. package/dist/parquet-source-loader.d.ts +51 -4
  146. package/dist/parquet-source-loader.d.ts.map +1 -1
  147. package/dist/parquet-source-loader.js +651 -38
  148. package/dist/parquet-source-loader.js.map +1 -1
  149. package/dist/parquet-source-types.d.ts +180 -30
  150. package/dist/parquet-source-types.d.ts.map +1 -1
  151. package/dist/parquet-source-worker.js +7378 -6989
  152. package/dist/parquet-source-worker.js.map +4 -4
  153. package/dist/parquet-worker-node.cjs +8501 -1045
  154. package/dist/parquet-worker-node.cjs.map +4 -4
  155. package/dist/parquet-worker-url.js +5 -1
  156. package/dist/parquet-worker-url.js.map +1 -1
  157. package/dist/parquet-worker.js +289 -218
  158. package/dist/parquet-worker.js.map +4 -4
  159. package/dist/parquetjs/codecs/declare.d.ts +15 -0
  160. package/dist/parquetjs/codecs/declare.d.ts.map +1 -1
  161. package/dist/parquetjs/codecs/declare.js +10 -0
  162. package/dist/parquetjs/codecs/declare.js.map +1 -1
  163. package/dist/parquetjs/codecs/delta.d.ts.map +1 -1
  164. package/dist/parquetjs/codecs/delta.js +356 -7
  165. package/dist/parquetjs/codecs/delta.js.map +1 -1
  166. package/dist/parquetjs/codecs/index.d.ts.map +1 -1
  167. package/dist/parquetjs/codecs/index.js +4 -0
  168. package/dist/parquetjs/codecs/index.js.map +1 -1
  169. package/dist/parquetjs/codecs/plain.d.ts +1 -1
  170. package/dist/parquetjs/codecs/plain.d.ts.map +1 -1
  171. package/dist/parquetjs/codecs/plain.js +153 -12
  172. package/dist/parquetjs/codecs/plain.js.map +1 -1
  173. package/dist/parquetjs/codecs/rle.d.ts +4 -0
  174. package/dist/parquetjs/codecs/rle.d.ts.map +1 -1
  175. package/dist/parquetjs/codecs/rle.js +406 -3
  176. package/dist/parquetjs/codecs/rle.js.map +1 -1
  177. package/dist/parquetjs/compression.d.ts +9 -0
  178. package/dist/parquetjs/compression.d.ts.map +1 -1
  179. package/dist/parquetjs/compression.js +86 -67
  180. package/dist/parquetjs/compression.js.map +1 -1
  181. package/dist/parquetjs/encoder/dictionary-planner.d.ts.map +1 -1
  182. package/dist/parquetjs/encoder/dictionary-planner.js +5 -2
  183. package/dist/parquetjs/encoder/dictionary-planner.js.map +1 -1
  184. package/dist/parquetjs/encoder/parquet-encoder.d.ts +43 -1
  185. package/dist/parquetjs/encoder/parquet-encoder.d.ts.map +1 -1
  186. package/dist/parquetjs/encoder/parquet-encoder.js +691 -34
  187. package/dist/parquetjs/encoder/parquet-encoder.js.map +1 -1
  188. package/dist/parquetjs/parquet-thrift/BoundingBox.d.ts +29 -0
  189. package/dist/parquetjs/parquet-thrift/BoundingBox.d.ts.map +1 -0
  190. package/dist/parquetjs/parquet-thrift/BoundingBox.js +85 -0
  191. package/dist/parquetjs/parquet-thrift/BoundingBox.js.map +1 -0
  192. package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts +5 -0
  193. package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts.map +1 -1
  194. package/dist/parquetjs/parquet-thrift/ColumnChunk.js +35 -0
  195. package/dist/parquetjs/parquet-thrift/ColumnChunk.js.map +1 -1
  196. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.d.ts +35 -0
  197. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.d.ts.map +1 -0
  198. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.js +135 -0
  199. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.js.map +1 -0
  200. package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts +10 -0
  201. package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts.map +1 -1
  202. package/dist/parquetjs/parquet-thrift/ColumnMetaData.js +73 -0
  203. package/dist/parquetjs/parquet-thrift/ColumnMetaData.js.map +1 -1
  204. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.d.ts +22 -0
  205. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.d.ts.map +1 -0
  206. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.js +111 -0
  207. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.js.map +1 -0
  208. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.d.ts +16 -0
  209. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.d.ts.map +1 -0
  210. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.js +57 -0
  211. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.js.map +1 -0
  212. package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts +5 -0
  213. package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts.map +1 -1
  214. package/dist/parquetjs/parquet-thrift/FileMetaData.js +35 -0
  215. package/dist/parquetjs/parquet-thrift/FileMetaData.js.map +1 -1
  216. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.d.ts +18 -0
  217. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.d.ts.map +1 -0
  218. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.js +62 -0
  219. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.js.map +1 -0
  220. package/dist/parquetjs/parquet-thrift/SizeStatistics.d.ts +26 -0
  221. package/dist/parquetjs/parquet-thrift/SizeStatistics.d.ts.map +1 -0
  222. package/dist/parquetjs/parquet-thrift/SizeStatistics.js +85 -0
  223. package/dist/parquetjs/parquet-thrift/SizeStatistics.js.map +1 -0
  224. package/dist/parquetjs/parquet-thrift/index.d.ts +6 -0
  225. package/dist/parquetjs/parquet-thrift/index.d.ts.map +1 -1
  226. package/dist/parquetjs/parquet-thrift/index.js +6 -0
  227. package/dist/parquetjs/parquet-thrift/index.js.map +1 -1
  228. package/dist/parquetjs/parser/decoders.d.ts +10 -1
  229. package/dist/parquetjs/parser/decoders.d.ts.map +1 -1
  230. package/dist/parquetjs/parser/decoders.js +318 -14
  231. package/dist/parquetjs/parser/decoders.js.map +1 -1
  232. package/dist/parquetjs/parser/encoded-pages.d.ts +24 -0
  233. package/dist/parquetjs/parser/encoded-pages.d.ts.map +1 -0
  234. package/dist/parquetjs/parser/encoded-pages.js +206 -0
  235. package/dist/parquetjs/parser/encoded-pages.js.map +1 -0
  236. package/dist/parquetjs/parser/parquet-reader.d.ts +88 -12
  237. package/dist/parquetjs/parser/parquet-reader.d.ts.map +1 -1
  238. package/dist/parquetjs/parser/parquet-reader.js +713 -66
  239. package/dist/parquetjs/parser/parquet-reader.js.map +1 -1
  240. package/dist/parquetjs/schema/declare.d.ts +22 -3
  241. package/dist/parquetjs/schema/declare.d.ts.map +1 -1
  242. package/dist/parquetjs/schema/declare.js.map +1 -1
  243. package/dist/parquetjs/schema/schema.d.ts.map +1 -1
  244. package/dist/parquetjs/schema/schema.js +10 -0
  245. package/dist/parquetjs/schema/schema.js.map +1 -1
  246. package/dist/parquetjs/schema/shred.d.ts.map +1 -1
  247. package/dist/parquetjs/schema/shred.js +219 -11
  248. package/dist/parquetjs/schema/shred.js.map +1 -1
  249. package/dist/parquetjs/schema/types.js +19 -21
  250. package/dist/parquetjs/schema/types.js.map +1 -1
  251. package/dist/parquetjs/schema/variant.d.ts +3 -0
  252. package/dist/parquetjs/schema/variant.d.ts.map +1 -0
  253. package/dist/parquetjs/schema/variant.js +248 -0
  254. package/dist/parquetjs/schema/variant.js.map +1 -0
  255. package/dist/parquetjs/utils/crc32.d.ts +3 -0
  256. package/dist/parquetjs/utils/crc32.d.ts.map +1 -0
  257. package/dist/parquetjs/utils/crc32.js +15 -0
  258. package/dist/parquetjs/utils/crc32.js.map +1 -0
  259. package/dist/parquetjs/utils/read-utils.d.ts +11 -1
  260. package/dist/parquetjs/utils/read-utils.d.ts.map +1 -1
  261. package/dist/parquetjs/utils/read-utils.js +21 -1
  262. package/dist/parquetjs/utils/read-utils.js.map +1 -1
  263. package/dist/unbundled.d.ts +2 -1
  264. package/dist/unbundled.d.ts.map +1 -1
  265. package/dist/unbundled.js +1 -0
  266. package/dist/unbundled.js.map +1 -1
  267. package/package.json +36 -13
  268. package/src/bundled.ts +10 -0
  269. package/src/delta-format.ts +12 -0
  270. package/src/delta-source-loader-types.ts +40 -0
  271. package/src/delta-source.ts +305 -0
  272. package/src/delta-types.ts +40 -0
  273. package/src/iceberg-rest-catalog.ts +122 -0
  274. package/src/iceberg-table-source.ts +1037 -0
  275. package/src/iceberg-types.ts +165 -0
  276. package/src/index.ts +59 -1
  277. package/src/lib/arrow/convert-schema-from-parquet.ts +113 -19
  278. package/src/lib/arrow/create-nested-arrow-vector.ts +116 -0
  279. package/src/lib/encoders/encode-table-to-parquet-js.ts +268 -7
  280. package/src/lib/geo/geoparquet-covering.ts +201 -0
  281. package/src/lib/geo/geospatial-metadata.ts +42 -15
  282. package/src/lib/parquet-bloom-filter-planner.ts +86 -0
  283. package/src/lib/parquet-bloom-filter.ts +345 -0
  284. package/src/lib/parquet-encryption.ts +344 -0
  285. package/src/lib/parquet-page-index.ts +180 -34
  286. package/src/lib/parquet-predicate.ts +52 -16
  287. package/src/lib/parquet-source-worker-decoder.ts +116 -20
  288. package/src/lib/parquet-source-worker-types.ts +25 -0
  289. package/src/lib/parsers/get-parquet-schema.ts +3 -1
  290. package/src/lib/parsers/parse-parquet-to-arrow-js.ts +397 -17
  291. package/src/lib/parsers/parse-parquet-to-arrow.ts +4 -5
  292. package/src/lib/parsers/parse-parquet-to-columns.ts +3 -1
  293. package/src/lib/parsers/parse-parquet-to-json.ts +18 -2
  294. package/src/lib/slice-parquet-batch.ts +32 -0
  295. package/src/lib/sources/parquet-range-file.ts +35 -29
  296. package/src/lib/utils/normalize-parquet-options.ts +17 -1
  297. package/src/parquet-dataset-source.ts +397 -85
  298. package/src/parquet-encoded-page-types.ts +122 -0
  299. package/src/parquet-js-loader-types.ts +1 -0
  300. package/src/parquet-js-loader.ts +2 -1
  301. package/src/parquet-js-writer.ts +30 -0
  302. package/src/parquet-loader-options.ts +11 -0
  303. package/src/parquet-source-capabilities.ts +18 -1
  304. package/src/parquet-source-loader-types.ts +10 -0
  305. package/src/parquet-source-loader.ts +1024 -52
  306. package/src/parquet-source-types.ts +197 -28
  307. package/src/parquet-worker-url.ts +6 -1
  308. package/src/parquetjs/codecs/declare.ts +27 -0
  309. package/src/parquetjs/codecs/delta.ts +466 -6
  310. package/src/parquetjs/codecs/index.ts +4 -0
  311. package/src/parquetjs/codecs/plain.ts +177 -11
  312. package/src/parquetjs/codecs/rle.ts +573 -3
  313. package/src/parquetjs/compression.ts +102 -76
  314. package/src/parquetjs/encoder/dictionary-planner.ts +7 -2
  315. package/src/parquetjs/encoder/parquet-encoder.ts +1016 -38
  316. package/src/parquetjs/parquet-thrift/BoundingBox.ts +108 -0
  317. package/src/parquetjs/parquet-thrift/ColumnChunk.ts +35 -0
  318. package/src/parquetjs/parquet-thrift/ColumnCryptoMetaData.ts +139 -0
  319. package/src/parquetjs/parquet-thrift/ColumnMetaData.ts +73 -0
  320. package/src/parquetjs/parquet-thrift/EncryptionAlgorithm.ts +127 -0
  321. package/src/parquetjs/parquet-thrift/FileCryptoMetaData.ts +60 -0
  322. package/src/parquetjs/parquet-thrift/FileMetaData.ts +35 -0
  323. package/src/parquetjs/parquet-thrift/GeospatialStatistics.ts +68 -0
  324. package/src/parquetjs/parquet-thrift/SizeStatistics.ts +98 -0
  325. package/src/parquetjs/parquet-thrift/index.ts +6 -0
  326. package/src/parquetjs/parser/decoders.ts +451 -26
  327. package/src/parquetjs/parser/encoded-pages.ts +278 -0
  328. package/src/parquetjs/parser/parquet-reader.ts +1112 -87
  329. package/src/parquetjs/schema/declare.ts +22 -2
  330. package/src/parquetjs/schema/schema.ts +10 -0
  331. package/src/parquetjs/schema/shred.ts +238 -11
  332. package/src/parquetjs/schema/types.ts +16 -25
  333. package/src/parquetjs/schema/variant.ts +313 -0
  334. package/src/parquetjs/utils/crc32.ts +15 -0
  335. package/src/parquetjs/utils/read-utils.ts +26 -1
  336. package/src/unbundled.ts +14 -0
@@ -2,24 +2,39 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import type {CoreAPI} from '@loaders.gl/loader-utils';
5
+ import {
6
+ executeScanTasks,
7
+ emitScanExecutionTelemetry,
8
+ explainTableQuery,
9
+ validateTableQueryLimit,
10
+ type CoreAPI,
11
+ type ScanFragment,
12
+ type ScanFragmentProvider,
13
+ type ScanTask,
14
+ type ScanExecutionTelemetry
15
+ } from '@loaders.gl/loader-utils';
6
16
  import type {Schema} from '@loaders.gl/schema';
7
17
 
8
- import {SingleBatchQueue} from './lib/sources/single-batch-queue';
9
18
  import {ParquetSource} from './parquet-source-loader';
19
+ import {sliceParquetBatch} from './lib/slice-parquet-batch';
20
+ import {PARQUET_TABLE_QUERY_CAPABILITIES} from './parquet-source-capabilities';
10
21
  import type {
11
22
  ParquetDatasetBatch,
12
23
  ParquetDatasetBatchProvenance,
13
24
  ParquetDatasetBoundingBox,
14
25
  ParquetDatasetFile,
26
+ ParquetDatasetFileScanPlan,
15
27
  ParquetDatasetFileCollection,
16
28
  ParquetDatasetFileQuery,
17
29
  ParquetDatasetFiles,
18
30
  ParquetDatasetPartitionValue,
19
31
  ParquetDatasetReadOptions,
32
+ ParquetDatasetExplain,
20
33
  ParquetDatasetSourceOptions,
21
34
  ParquetDatasetTelemetry,
22
35
  ParquetSourceBatch,
36
+ ParquetPredicate,
37
+ ParquetSourceExplain,
23
38
  ParquetTelemetry
24
39
  } from './parquet-source-types';
25
40
 
@@ -27,8 +42,10 @@ export type {
27
42
  ParquetDatasetBatch,
28
43
  ParquetDatasetBatchProvenance,
29
44
  ParquetDatasetBoundingBox,
45
+ ParquetDatasetExplain,
30
46
  ParquetDatasetFile,
31
47
  ParquetDatasetFileCollection,
48
+ ParquetDatasetFileScanPlan,
32
49
  ParquetDatasetFileProvider,
33
50
  ParquetDatasetFileQuery,
34
51
  ParquetDatasetFiles,
@@ -47,11 +64,15 @@ type IndexedParquetDatasetFile = {
47
64
  index: number;
48
65
  };
49
66
 
50
- type ScheduledFileRead = {
51
- /** Bounded batch queue for this file. */
52
- queue: SingleBatchQueue<ParquetDatasetBatch>;
53
- /** File task used for deterministic cleanup. */
54
- task: Promise<void>;
67
+ type ParquetDatasetDiscoverySummary = {
68
+ /** Descriptors returned by the provider. */
69
+ discovered: number;
70
+ /** Descriptors retained after local pruning. */
71
+ selected: number;
72
+ /** Descriptors rejected by bounding-box intersection. */
73
+ prunedByBoundingBox: number;
74
+ /** Descriptors rejected by partition constraints. */
75
+ prunedByPartitions: number;
55
76
  };
56
77
 
57
78
  /**
@@ -60,7 +81,9 @@ type ScheduledFileRead = {
60
81
  * The source accepts static descriptors or a lazy provider, allowing STAC and other catalogs to be
61
82
  * injected without coupling `@loaders.gl/parquet` to a catalog protocol.
62
83
  */
63
- export class ParquetDatasetSource {
84
+ export class ParquetDatasetSource implements ScanFragmentProvider<ParquetPredicate> {
85
+ /** Common projection, predicate, limit, streaming, and cancellation capabilities. */
86
+ readonly tableQueryCapabilities = PARQUET_TABLE_QUERY_CAPABILITIES;
64
87
  /** Static descriptors or lazy catalog-backed file provider. */
65
88
  readonly files: ParquetDatasetFiles;
66
89
  /** Child-source and dataset orchestration options. */
@@ -121,6 +144,137 @@ export class ParquetDatasetSource {
121
144
  });
122
145
  }
123
146
 
147
+ /** Discovers catalog-independent fragments after applying descriptor-level pruning. */
148
+ async getScanFragments(
149
+ options: ParquetDatasetReadOptions = {}
150
+ ): Promise<readonly ScanFragment[]> {
151
+ this.assertOpen();
152
+ const fragments: ScanFragment[] = [];
153
+ const selectedFiles = this.getSelectedFiles(options);
154
+ for await (const indexedFile of selectedFiles) {
155
+ const file = indexedFile.file;
156
+ if (typeof file.data !== 'string' && !file.id) {
157
+ throw new Error(
158
+ 'Parquet dataset Blob fragments require an explicit stable id for fragment discovery'
159
+ );
160
+ }
161
+ fragments.push(
162
+ Object.freeze({
163
+ id: getDatasetFileId(file, indexedFile.index),
164
+ uri: typeof file.data === 'string' ? file.data : undefined,
165
+ partitionValues: file.partitions,
166
+ metadata: file.metadata
167
+ })
168
+ );
169
+ }
170
+ return Object.freeze(fragments);
171
+ }
172
+
173
+ /** Plans descriptor discovery and every retained file's Parquet physical scan. */
174
+ async getScanPlan(options: ParquetDatasetReadOptions = {}): Promise<ParquetDatasetExplain> {
175
+ this.assertOpen();
176
+ validateTableQueryLimit(options.limit);
177
+ const readContext = createDatasetAbortContext(options.signal);
178
+ this.activeReadControllers.add(readContext.abortController);
179
+ const discovery = createParquetDatasetDiscoverySummary();
180
+ const filePlans: ParquetDatasetFileScanPlan[] = [];
181
+ const tasks = this.getPlanTasks(options, readContext.abortController.signal, discovery);
182
+ const fileConcurrency = normalizeFileConcurrency(
183
+ options.fileConcurrency ?? this.options.parquetDataset?.fileConcurrency
184
+ );
185
+ try {
186
+ for await (const filePlan of executeScanTasks(tasks, {
187
+ concurrency: fileConcurrency,
188
+ signal: readContext.abortController.signal
189
+ })) {
190
+ filePlans.push(filePlan);
191
+ }
192
+ const firstFilePlan = filePlans[0];
193
+ if (!firstFilePlan) {
194
+ throw new Error('ParquetDatasetSource query selected no files');
195
+ }
196
+ const effectivePredicate = firstFilePlan.parquet.plan.find(step => step.kind === 'filter');
197
+ const effectiveLimit = options.limit ?? this.options.parquet?.limit;
198
+ const explanation = explainTableQuery(
199
+ firstFilePlan.parquet.sourceColumns,
200
+ {
201
+ columns: firstFilePlan.parquet.outputColumns,
202
+ predicate:
203
+ effectivePredicate?.kind === 'filter' ? effectivePredicate.predicate : undefined,
204
+ limit: effectiveLimit
205
+ },
206
+ PARQUET_TABLE_QUERY_CAPABILITIES
207
+ );
208
+ return Object.freeze({
209
+ ...explanation,
210
+ source: 'parquet-dataset' as const,
211
+ files: Object.freeze({...discovery}),
212
+ filePlans: Object.freeze(filePlans)
213
+ });
214
+ } finally {
215
+ readContext.abortController.abort();
216
+ readContext.removeSignalListener();
217
+ this.activeReadControllers.delete(readContext.abortController);
218
+ }
219
+ }
220
+
221
+ /** Explains the common logical query and physical multi-file Parquet plan. */
222
+ async explain(options: ParquetDatasetReadOptions = {}): Promise<ParquetDatasetExplain> {
223
+ return await this.getScanPlan(options);
224
+ }
225
+
226
+ /** Executes a previously computed dataset plan while retaining each file's row-group choices. */
227
+ async *executeScanPlan(
228
+ plan: ParquetDatasetExplain,
229
+ options: ParquetDatasetReadOptions = {}
230
+ ): AsyncIterable<ParquetDatasetBatch> {
231
+ if (plan.source !== 'parquet-dataset') {
232
+ throw new Error('ParquetDatasetSource can only execute dataset scan plans');
233
+ }
234
+ validateTableQueryLimit(options.limit);
235
+ let remainingRows = options.limit ?? this.options.parquet?.limit ?? Number.POSITIVE_INFINITY;
236
+ if (remainingRows === 0) return;
237
+ const limitStep = plan.plan.find(step => step.kind === 'limit');
238
+ const readOptions: ParquetDatasetReadOptions = {
239
+ ...options,
240
+ columns: options.columns ?? plan.outputColumns,
241
+ predicate: options.predicate,
242
+ limit: options.limit ?? (limitStep?.kind === 'limit' ? limitStep.limit : undefined)
243
+ };
244
+ remainingRows = readOptions.limit ?? Number.POSITIVE_INFINITY;
245
+ const readContext = createDatasetAbortContext(readOptions.signal);
246
+ this.activeReadControllers.add(readContext.abortController);
247
+ const filePlans = new Map(
248
+ plan.filePlans.map(filePlan => [filePlan.fileIndex, filePlan.parquet])
249
+ );
250
+ const tasks = this.getReadTasks(readOptions, readContext.abortController.signal, filePlans);
251
+ const fileConcurrency = normalizeFileConcurrency(
252
+ readOptions.fileConcurrency ?? this.options.parquetDataset?.fileConcurrency
253
+ );
254
+ try {
255
+ for await (const batch of executeScanTasks(tasks, {
256
+ concurrency: fileConcurrency,
257
+ signal: readContext.abortController.signal
258
+ })) {
259
+ const outputBatch = sliceParquetBatch(batch, Math.min(batch.length, remainingRows));
260
+ this.telemetry.batchesEmitted++;
261
+ this.telemetry.rowsEmitted += outputBatch.length;
262
+ yield outputBatch;
263
+ remainingRows -= outputBatch.length;
264
+ if (remainingRows === 0) return;
265
+ }
266
+ } finally {
267
+ readContext.abortController.abort();
268
+ readContext.removeSignalListener();
269
+ this.activeReadControllers.delete(readContext.abortController);
270
+ }
271
+ }
272
+
273
+ /** Common scan-architecture alias for ordered multi-file reads. */
274
+ scan(options: ParquetDatasetReadOptions = {}): AsyncIterable<ParquetDatasetBatch> {
275
+ return this.read(options);
276
+ }
277
+
124
278
  /**
125
279
  * Reads selected files as deterministic, file-ordered Arrow batches.
126
280
  *
@@ -129,81 +283,79 @@ export class ParquetDatasetSource {
129
283
  */
130
284
  async *read(options: ParquetDatasetReadOptions = {}): AsyncIterable<ParquetDatasetBatch> {
131
285
  this.assertOpen();
286
+ validateTableQueryLimit(options.limit);
287
+ const startedAt = Date.now();
288
+ const telemetryBefore = this.getTelemetry();
289
+ let remainingRows = options.limit ?? this.options.parquet?.limit ?? Number.POSITIVE_INFINITY;
290
+ if (remainingRows === 0) {
291
+ emitScanExecutionTelemetry(
292
+ options.onTelemetry,
293
+ createParquetDatasetScanExecutionTelemetry(
294
+ telemetryBefore,
295
+ this.getTelemetry(),
296
+ startedAt,
297
+ 'early-terminated',
298
+ 'limit'
299
+ )
300
+ );
301
+ return;
302
+ }
132
303
  const readContext = createDatasetAbortContext(options.signal);
133
304
  this.activeReadControllers.add(readContext.abortController);
134
- const selectedFiles = this.getSelectedFiles({
135
- ...options,
136
- signal: readContext.abortController.signal
137
- });
138
- const iterator = selectedFiles[Symbol.asyncIterator]();
139
- const scheduledReads = new Map<number, ScheduledFileRead>();
140
- let nextSchedulePosition = 0;
141
- let nextYieldPosition = 0;
142
- let providerComplete = false;
143
- let discoveryError: unknown;
305
+ const tasks = this.getReadTasks(options, readContext.abortController.signal);
144
306
  const fileConcurrency = normalizeFileConcurrency(
145
307
  options.fileConcurrency ?? this.options.parquetDataset?.fileConcurrency
146
308
  );
147
-
148
- const scheduleNext = async (): Promise<void> => {
149
- if (providerComplete) {
150
- return;
151
- }
152
- const next = await iterator.next();
153
- if (next.done) {
154
- providerComplete = true;
155
- return;
156
- }
157
- const position = nextSchedulePosition++;
158
- const queue = new SingleBatchQueue<ParquetDatasetBatch>();
159
- const task = this.readFile(next.value, options, queue, readContext.abortController.signal);
160
- scheduledReads.set(position, {queue, task});
161
- };
162
-
163
- const fillAvailableSlots = async (): Promise<void> => {
164
- try {
165
- while (scheduledReads.size < fileConcurrency && !providerComplete) {
166
- await scheduleNext();
167
- }
168
- } catch (error) {
169
- discoveryError = error;
170
- providerComplete = true;
171
- }
172
- };
309
+ let completed = false;
310
+ let limitReached = false;
311
+ let readError: unknown;
173
312
 
174
313
  try {
175
- await scheduleNext();
176
- let discoveryPromise = fillAvailableSlots();
177
-
178
- while (scheduledReads.size > 0) {
179
- const scheduledRead = scheduledReads.get(nextYieldPosition);
180
- if (!scheduledRead) {
181
- throw new Error('ParquetDatasetSource internal file ordering error');
182
- }
183
- for await (const batch of scheduledRead.queue) {
184
- throwIfAborted(readContext.abortController.signal);
185
- this.telemetry.batchesEmitted++;
186
- this.telemetry.rowsEmitted += batch.length;
187
- yield batch;
188
- }
189
- await scheduledRead.task;
190
- scheduledReads.delete(nextYieldPosition++);
191
- await discoveryPromise;
192
- if (discoveryError !== undefined) {
193
- throw discoveryError;
314
+ for await (const batch of executeScanTasks(tasks, {
315
+ concurrency: fileConcurrency,
316
+ signal: readContext.abortController.signal
317
+ })) {
318
+ const outputBatch = sliceParquetBatch(batch, Math.min(batch.length, remainingRows));
319
+ this.telemetry.batchesEmitted++;
320
+ this.telemetry.rowsEmitted += outputBatch.length;
321
+ yield outputBatch;
322
+ remainingRows -= outputBatch.length;
323
+ if (remainingRows === 0) {
324
+ limitReached = true;
325
+ return;
194
326
  }
195
- discoveryPromise = fillAvailableSlots();
196
- }
197
- await discoveryPromise;
198
- if (discoveryError !== undefined) {
199
- throw discoveryError;
200
327
  }
328
+ completed = true;
329
+ } catch (error) {
330
+ readError = error;
331
+ throw error;
201
332
  } finally {
333
+ const cancelled = readContext.abortController.signal.aborted || options.signal?.aborted;
202
334
  readContext.abortController.abort();
203
335
  readContext.removeSignalListener();
204
336
  this.activeReadControllers.delete(readContext.abortController);
205
- await iterator.return?.();
206
- await Promise.allSettled([...scheduledReads.values()].map(read => read.task));
337
+ const status: ScanExecutionTelemetry['status'] = readError
338
+ ? cancelled
339
+ ? 'cancelled'
340
+ : 'failed'
341
+ : limitReached || !completed
342
+ ? 'early-terminated'
343
+ : 'completed';
344
+ emitScanExecutionTelemetry(
345
+ options.onTelemetry,
346
+ createParquetDatasetScanExecutionTelemetry(
347
+ telemetryBefore,
348
+ this.getTelemetry(),
349
+ startedAt,
350
+ status,
351
+ limitReached
352
+ ? 'limit'
353
+ : !completed && readError === undefined
354
+ ? 'consumer-return'
355
+ : undefined,
356
+ readError
357
+ )
358
+ );
207
359
  }
208
360
  }
209
361
 
@@ -219,12 +371,75 @@ export class ParquetDatasetSource {
219
371
  }
220
372
 
221
373
  /** Reads one child source into its bounded file-order queue. */
222
- private async readFile(
374
+ private async *getReadTasks(
375
+ options: ParquetDatasetReadOptions,
376
+ signal: AbortSignal,
377
+ filePlans?: ReadonlyMap<number, ParquetSourceExplain>
378
+ ): AsyncIterable<ScanTask<ParquetDatasetBatch>> {
379
+ const selectedFiles = this.getSelectedFiles({...options, signal});
380
+ for await (const indexedFile of selectedFiles) {
381
+ if (filePlans && !filePlans.has(indexedFile.index)) {
382
+ continue;
383
+ }
384
+ yield {
385
+ run: taskSignal =>
386
+ this.readFile(indexedFile, options, taskSignal, filePlans?.get(indexedFile.index))
387
+ };
388
+ }
389
+ }
390
+
391
+ /** Plans retained files through bounded, ordered common scan tasks. */
392
+ private async *getPlanTasks(
393
+ options: ParquetDatasetReadOptions,
394
+ signal: AbortSignal,
395
+ discovery: ParquetDatasetDiscoverySummary
396
+ ): AsyncIterable<ScanTask<ParquetDatasetFileScanPlan>> {
397
+ const selectedFiles = this.getSelectedFiles({...options, signal}, discovery);
398
+ for await (const indexedFile of selectedFiles) {
399
+ yield {run: taskSignal => this.planFile(indexedFile, options, taskSignal)};
400
+ }
401
+ }
402
+
403
+ /** Produces one child physical plan and closes its temporary source. */
404
+ private async *planFile(
223
405
  indexedFile: IndexedParquetDatasetFile,
224
406
  options: ParquetDatasetReadOptions,
225
- queue: SingleBatchQueue<ParquetDatasetBatch>,
226
407
  signal: AbortSignal
227
- ): Promise<void> {
408
+ ): AsyncIterable<ParquetDatasetFileScanPlan> {
409
+ const source = this.createSource(indexedFile.file);
410
+ this.telemetry.filesOpened++;
411
+ try {
412
+ if (this.options.parquetDataset?.validateSchema !== false) {
413
+ await this.validateFileSchema(source, indexedFile, signal);
414
+ }
415
+ const parquet = await source.getScanPlan({
416
+ columns: options.columns,
417
+ predicate: options.predicate,
418
+ bbox: options.bbox,
419
+ geometryColumn: options.geometryColumn,
420
+ rowGroupFilter: options.rowGroupFilter,
421
+ limit: undefined,
422
+ signal
423
+ });
424
+ yield Object.freeze({
425
+ fileIndex: indexedFile.index,
426
+ fileId: getDatasetFileId(indexedFile.file, indexedFile.index),
427
+ partitions: indexedFile.file.partitions,
428
+ parquet
429
+ });
430
+ } finally {
431
+ await source.close();
432
+ this.addParquetTelemetry(source.getTelemetry());
433
+ }
434
+ }
435
+
436
+ /** Reads one child source as an asynchronous batch iterable. */
437
+ private async *readFile(
438
+ indexedFile: IndexedParquetDatasetFile,
439
+ options: ParquetDatasetReadOptions,
440
+ signal: AbortSignal,
441
+ scanPlan?: ParquetSourceExplain
442
+ ): AsyncIterable<ParquetDatasetBatch> {
228
443
  const source = this.createSource(indexedFile.file);
229
444
  this.telemetry.filesOpened++;
230
445
  try {
@@ -235,15 +450,18 @@ export class ParquetDatasetSource {
235
450
  columns: options.columns,
236
451
  batchSize: options.batchSize,
237
452
  concurrency: options.concurrency,
453
+ rowGroups: scanPlan?.rowGroups.indices,
238
454
  rowGroupFilter: options.rowGroupFilter,
239
- predicate: options.predicate,
455
+ predicate: options.predicate ?? (!options.bbox ? getPlanPredicate(scanPlan) : undefined),
456
+ bbox: options.bbox,
457
+ geometryColumn: options.geometryColumn,
458
+ limit: undefined,
240
459
  signal
241
460
  })) {
242
- await queue.push(createDatasetBatch(batch, indexedFile), signal);
461
+ yield createDatasetBatch(batch, indexedFile);
243
462
  }
244
- queue.finish();
245
463
  } catch (error) {
246
- queue.fail(error);
464
+ throw error;
247
465
  } finally {
248
466
  await source.close();
249
467
  this.addParquetTelemetry(source.getTelemetry());
@@ -275,12 +493,16 @@ export class ParquetDatasetSource {
275
493
 
276
494
  /** Creates a child source while preserving caller fetch, worker, range, and decode options. */
277
495
  private createSource(file: ParquetDatasetFile): ParquetSource {
278
- return new ParquetSource(file.data, this.options, this.coreApi);
496
+ const parquetOptions = this.options.parquet
497
+ ? {...this.options.parquet, limit: undefined}
498
+ : undefined;
499
+ return new ParquetSource(file.data, {...this.options, parquet: parquetOptions}, this.coreApi);
279
500
  }
280
501
 
281
502
  /** Lazily discovers descriptors and applies conservative local pruning. */
282
503
  private async *getSelectedFiles(
283
- query: ParquetDatasetFileQuery
504
+ query: ParquetDatasetFileQuery,
505
+ discovery?: ParquetDatasetDiscoverySummary
284
506
  ): AsyncIterable<IndexedParquetDatasetFile> {
285
507
  throwIfAborted(query.signal);
286
508
  const collection = await getFileCollection(this.files, query);
@@ -289,17 +511,21 @@ export class ParquetDatasetSource {
289
511
  throwIfAborted(query.signal);
290
512
  validateDatasetFile(file, index);
291
513
  this.telemetry.filesDiscovered++;
514
+ if (discovery) discovery.discovered++;
292
515
  if (query.bbox && file.bbox && !doBoundingBoxesIntersect(query.bbox, file.bbox)) {
293
516
  this.telemetry.filesPrunedByBoundingBox++;
517
+ if (discovery) discovery.prunedByBoundingBox++;
294
518
  index++;
295
519
  continue;
296
520
  }
297
521
  if (query.partitions && !matchesPartitions(file.partitions, query.partitions)) {
298
522
  this.telemetry.filesPrunedByPartitions++;
523
+ if (discovery) discovery.prunedByPartitions++;
299
524
  index++;
300
525
  continue;
301
526
  }
302
527
  this.telemetry.filesSelected++;
528
+ if (discovery) discovery.selected++;
303
529
  yield {file, index: index++};
304
530
  }
305
531
  }
@@ -319,6 +545,11 @@ export class ParquetDatasetSource {
319
545
  }
320
546
  }
321
547
 
548
+ /** Creates operation-local descriptor discovery counters. */
549
+ function createParquetDatasetDiscoverySummary(): ParquetDatasetDiscoverySummary {
550
+ return {discovered: 0, selected: 0, prunedByBoundingBox: 0, prunedByPartitions: 0};
551
+ }
552
+
322
553
  /** Normalizes static descriptors and lazy providers into one file collection. */
323
554
  async function getFileCollection(
324
555
  files: ParquetDatasetFiles,
@@ -353,6 +584,12 @@ function createDatasetBatch(
353
584
  return {...batch, metadata: provenance, ...datasetProvenance};
354
585
  }
355
586
 
587
+ /** Returns the filter predicate recorded in one child source plan. */
588
+ function getPlanPredicate(plan: ParquetSourceExplain | undefined): ParquetPredicate | undefined {
589
+ const filter = plan?.plan.find(step => step.kind === 'filter');
590
+ return filter?.kind === 'filter' ? filter.predicate : undefined;
591
+ }
592
+
356
593
  /** Returns a stable descriptor identity for batch provenance and diagnostics. */
357
594
  function getDatasetFileId(file: ParquetDatasetFile, index: number): string {
358
595
  if (file.id) {
@@ -378,18 +615,43 @@ function doBoundingBoxesIntersect(
378
615
  left: ParquetDatasetBoundingBox,
379
616
  right: ParquetDatasetBoundingBox
380
617
  ): boolean {
381
- const leftMaximumX = left.length === 6 ? left[3] : left[2];
382
- const leftMaximumY = left.length === 6 ? left[4] : left[3];
383
- const rightMaximumX = right.length === 6 ? right[3] : right[2];
384
- const rightMaximumY = right.length === 6 ? right[4] : right[3];
618
+ const leftMaximumX = left.length === 8 ? left[4] : left.length === 6 ? left[3] : left[2];
619
+ const leftMaximumY = left.length === 8 ? left[5] : left.length === 6 ? left[4] : left[3];
620
+ const rightMaximumX = right.length === 8 ? right[4] : right.length === 6 ? right[3] : right[2];
621
+ const rightMaximumY = right.length === 8 ? right[5] : right.length === 6 ? right[4] : right[3];
385
622
  return (
386
- left[0] <= rightMaximumX &&
387
- leftMaximumX >= right[0] &&
623
+ doLongitudeIntervalsIntersect(left[0], leftMaximumX, right[0], rightMaximumX) &&
388
624
  left[1] <= rightMaximumY &&
389
625
  leftMaximumY >= right[1]
390
626
  );
391
627
  }
392
628
 
629
+ /** Tests longitude intervals that may use RFC 7946 antimeridian wrapping. */
630
+ function doLongitudeIntervalsIntersect(
631
+ firstWest: number,
632
+ firstEast: number,
633
+ secondWest: number,
634
+ secondEast: number
635
+ ): boolean {
636
+ const firstIntervals =
637
+ firstWest <= firstEast
638
+ ? [[firstWest, firstEast]]
639
+ : [
640
+ [firstWest, 180],
641
+ [-180, firstEast]
642
+ ];
643
+ const secondIntervals =
644
+ secondWest <= secondEast
645
+ ? [[secondWest, secondEast]]
646
+ : [
647
+ [secondWest, 180],
648
+ [-180, secondEast]
649
+ ];
650
+ return firstIntervals.some(first =>
651
+ secondIntervals.some(second => first[0] <= second[1] && first[1] >= second[0])
652
+ );
653
+ }
654
+
393
655
  /** Tests exact partition constraints without rejecting files that omit a requested key. */
394
656
  function matchesPartitions(
395
657
  partitions: Readonly<Record<string, ParquetDatasetPartitionValue>> | undefined,
@@ -471,6 +733,9 @@ function createParquetDatasetTelemetry(): ParquetDatasetTelemetry {
471
733
  rowGroupsRequested: 0,
472
734
  rowGroupsPruned: 0,
473
735
  rowGroupsPrunedByStatistics: 0,
736
+ rowGroupsPrunedByBloomFilter: 0,
737
+ bloomFiltersRead: 0,
738
+ bloomFilterBytesRead: 0,
474
739
  rowGroupsPrunedByPageIndex: 0,
475
740
  pageIndexesRead: 0,
476
741
  pagesRead: 0,
@@ -486,3 +751,50 @@ function createParquetDatasetTelemetry(): ParquetDatasetTelemetry {
486
751
  }
487
752
  };
488
753
  }
754
+
755
+ /** Converts cumulative dataset and child counters into one portable read-scoped snapshot. */
756
+ function createParquetDatasetScanExecutionTelemetry(
757
+ before: ParquetDatasetTelemetry,
758
+ after: ParquetDatasetTelemetry,
759
+ startedAt: number,
760
+ status: ScanExecutionTelemetry['status'],
761
+ earlyTerminationReason?: ScanExecutionTelemetry['earlyTerminationReason'],
762
+ error?: unknown
763
+ ): ScanExecutionTelemetry {
764
+ const parquet = Object.fromEntries(
765
+ Object.keys(after.parquet).map(key => {
766
+ const telemetryKey = key as keyof ParquetTelemetry;
767
+ return [key, after.parquet[telemetryKey] - before.parquet[telemetryKey]];
768
+ })
769
+ ) as ParquetTelemetry;
770
+ const dataset = Object.freeze({
771
+ filesDiscovered: after.filesDiscovered - before.filesDiscovered,
772
+ filesSelected: after.filesSelected - before.filesSelected,
773
+ filesPrunedByBoundingBox: after.filesPrunedByBoundingBox - before.filesPrunedByBoundingBox,
774
+ filesPrunedByPartitions: after.filesPrunedByPartitions - before.filesPrunedByPartitions,
775
+ filesOpened: after.filesOpened - before.filesOpened,
776
+ batchesEmitted: after.batchesEmitted - before.batchesEmitted,
777
+ rowsEmitted: after.rowsEmitted - before.rowsEmitted
778
+ });
779
+ const rowsRead = Math.max(parquet.predicateRowsTested, parquet.rowsEmitted, dataset.rowsEmitted);
780
+ return Object.freeze({
781
+ status,
782
+ sourcesPlanned: Math.max(dataset.filesSelected, dataset.filesOpened),
783
+ sourcesRead: dataset.filesOpened,
784
+ batchesRead: dataset.batchesEmitted,
785
+ batchesDecoded: parquet.rowGroupsDecoded,
786
+ rowsRead,
787
+ rowsTested: parquet.predicateRowsTested || undefined,
788
+ rowsRetained: parquet.predicateRowsMatched || undefined,
789
+ rowsReturned: dataset.rowsEmitted,
790
+ bytesRead: parquet.downloadedBytes,
791
+ bytesFetched: parquet.downloadedBytes,
792
+ filesOpened: dataset.filesOpened,
793
+ tasksOpened: parquet.rowGroupsDecoded,
794
+ rowsPruned: parquet.rowsPrunedByPageIndex,
795
+ durationMilliseconds: Date.now() - startedAt,
796
+ earlyTerminationReason,
797
+ details: Object.freeze({dataset, parquet: Object.freeze({...parquet})}),
798
+ ...(error === undefined ? {} : {error})
799
+ });
800
+ }