@loaders.gl/parquet 5.0.0-alpha.2 → 5.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/dist/bundled.d.ts +1 -1
- package/dist/bundled.d.ts.map +1 -1
- package/dist/bundled.js.map +1 -1
- package/dist/delta-format.d.ts +9 -0
- package/dist/delta-format.d.ts.map +1 -0
- package/dist/delta-format.js +12 -0
- package/dist/delta-format.js.map +1 -0
- package/dist/delta-source-loader-types.d.ts +27 -0
- package/dist/delta-source-loader-types.d.ts.map +1 -0
- package/dist/delta-source-loader-types.js +28 -0
- package/dist/delta-source-loader-types.js.map +1 -0
- package/dist/delta-source.d.ts +48 -0
- package/dist/delta-source.d.ts.map +1 -0
- package/dist/delta-source.js +222 -0
- package/dist/delta-source.js.map +1 -0
- package/dist/delta-types.d.ts +38 -0
- package/dist/delta-types.d.ts.map +1 -0
- package/dist/delta-types.js +5 -0
- package/dist/delta-types.js.map +1 -0
- package/dist/geoparquet-loader.js +1 -1
- package/dist/iceberg-rest-catalog.d.ts +49 -0
- package/dist/iceberg-rest-catalog.d.ts.map +1 -0
- package/dist/iceberg-rest-catalog.js +78 -0
- package/dist/iceberg-rest-catalog.js.map +1 -0
- package/dist/iceberg-table-source.d.ts +63 -0
- package/dist/iceberg-table-source.d.ts.map +1 -0
- package/dist/iceberg-table-source.js +797 -0
- package/dist/iceberg-table-source.js.map +1 -0
- package/dist/iceberg-types.d.ts +150 -0
- package/dist/iceberg-types.d.ts.map +1 -0
- package/dist/iceberg-types.js +5 -0
- package/dist/iceberg-types.js.map +1 -0
- package/dist/index.cjs +15005 -3143
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/arrow/convert-schema-from-parquet.d.ts +3 -1
- package/dist/lib/arrow/convert-schema-from-parquet.d.ts.map +1 -1
- package/dist/lib/arrow/convert-schema-from-parquet.js +94 -19
- package/dist/lib/arrow/convert-schema-from-parquet.js.map +1 -1
- package/dist/lib/arrow/create-nested-arrow-vector.js +89 -0
- package/dist/lib/arrow/create-nested-arrow-vector.js.map +1 -1
- package/dist/lib/constants.js +1 -1
- package/dist/lib/encoders/encode-table-to-parquet-js.d.ts.map +1 -1
- package/dist/lib/encoders/encode-table-to-parquet-js.js +202 -7
- package/dist/lib/encoders/encode-table-to-parquet-js.js.map +1 -1
- package/dist/lib/geo/geoparquet-covering.d.ts +19 -0
- package/dist/lib/geo/geoparquet-covering.d.ts.map +1 -0
- package/dist/lib/geo/geoparquet-covering.js +137 -0
- package/dist/lib/geo/geoparquet-covering.js.map +1 -0
- package/dist/lib/geo/geospatial-metadata.d.ts.map +1 -1
- package/dist/lib/geo/geospatial-metadata.js +39 -13
- package/dist/lib/geo/geospatial-metadata.js.map +1 -1
- package/dist/lib/parquet-bloom-filter-planner.d.ts +16 -0
- package/dist/lib/parquet-bloom-filter-planner.d.ts.map +1 -0
- package/dist/lib/parquet-bloom-filter-planner.js +57 -0
- package/dist/lib/parquet-bloom-filter-planner.js.map +1 -0
- package/dist/lib/parquet-bloom-filter.d.ts +30 -0
- package/dist/lib/parquet-bloom-filter.d.ts.map +1 -0
- package/dist/lib/parquet-bloom-filter.js +289 -0
- package/dist/lib/parquet-bloom-filter.js.map +1 -0
- package/dist/lib/parquet-encryption.d.ts +80 -0
- package/dist/lib/parquet-encryption.d.ts.map +1 -0
- package/dist/lib/parquet-encryption.js +190 -0
- package/dist/lib/parquet-encryption.js.map +1 -0
- package/dist/lib/parquet-page-index.d.ts +34 -5
- package/dist/lib/parquet-page-index.d.ts.map +1 -1
- package/dist/lib/parquet-page-index.js +114 -28
- package/dist/lib/parquet-page-index.js.map +1 -1
- package/dist/lib/parquet-predicate.d.ts +5 -1
- package/dist/lib/parquet-predicate.d.ts.map +1 -1
- package/dist/lib/parquet-predicate.js +42 -14
- package/dist/lib/parquet-predicate.js.map +1 -1
- package/dist/lib/parquet-source-worker-decoder.d.ts +4 -1
- package/dist/lib/parquet-source-worker-decoder.d.ts.map +1 -1
- package/dist/lib/parquet-source-worker-decoder.js +83 -17
- package/dist/lib/parquet-source-worker-decoder.js.map +1 -1
- package/dist/lib/parquet-source-worker-types.d.ts +24 -0
- package/dist/lib/parquet-source-worker-types.d.ts.map +1 -1
- package/dist/lib/parsers/get-parquet-schema.d.ts.map +1 -1
- package/dist/lib/parsers/get-parquet-schema.js +3 -1
- package/dist/lib/parsers/get-parquet-schema.js.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-arrow-js.d.ts.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-arrow-js.js +327 -18
- package/dist/lib/parsers/parse-parquet-to-arrow-js.js.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-arrow.d.ts.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-arrow.js +5 -5
- package/dist/lib/parsers/parse-parquet-to-arrow.js.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-columns.d.ts.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-columns.js +3 -1
- package/dist/lib/parsers/parse-parquet-to-columns.js.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-json.d.ts.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-json.js +18 -2
- package/dist/lib/parsers/parse-parquet-to-json.js.map +1 -1
- package/dist/lib/slice-parquet-batch.d.ts +9 -0
- package/dist/lib/slice-parquet-batch.d.ts.map +1 -0
- package/dist/lib/slice-parquet-batch.js +28 -0
- package/dist/lib/slice-parquet-batch.js.map +1 -0
- package/dist/lib/sources/parquet-range-file.d.ts +1 -1
- package/dist/lib/sources/parquet-range-file.d.ts.map +1 -1
- package/dist/lib/sources/parquet-range-file.js +28 -22
- package/dist/lib/sources/parquet-range-file.js.map +1 -1
- package/dist/lib/utils/normalize-parquet-options.d.ts.map +1 -1
- package/dist/lib/utils/normalize-parquet-options.js +15 -1
- package/dist/lib/utils/normalize-parquet-options.js.map +1 -1
- package/dist/parquet-dataset-source.cjs +15766 -4896
- package/dist/parquet-dataset-source.cjs.map +3 -3
- package/dist/parquet-dataset-source.d.ts +31 -4
- package/dist/parquet-dataset-source.d.ts.map +1 -1
- package/dist/parquet-dataset-source.js +296 -74
- package/dist/parquet-dataset-source.js.map +1 -1
- package/dist/parquet-js-loader-types.d.ts +2 -0
- package/dist/parquet-js-loader-types.d.ts.map +1 -1
- package/dist/parquet-js-loader-types.js +2 -1
- package/dist/parquet-js-loader-types.js.map +1 -1
- package/dist/parquet-js-loader.d.ts +1 -0
- package/dist/parquet-js-loader.d.ts.map +1 -1
- package/dist/parquet-js-loader.js +2 -1
- package/dist/parquet-js-loader.js.map +1 -1
- package/dist/parquet-js-writer.d.ts +23 -2
- package/dist/parquet-js-writer.d.ts.map +1 -1
- package/dist/parquet-js-writer.js +1 -1
- package/dist/parquet-js-writer.js.map +1 -1
- package/dist/parquet-loader-base.js +1 -1
- package/dist/parquet-loader-options.d.ts +11 -0
- package/dist/parquet-loader-options.d.ts.map +1 -1
- package/dist/parquet-loader-options.js.map +1 -1
- package/dist/parquet-source-capabilities.d.ts +8 -1
- package/dist/parquet-source-capabilities.d.ts.map +1 -1
- package/dist/parquet-source-capabilities.js +10 -0
- package/dist/parquet-source-capabilities.js.map +1 -1
- package/dist/parquet-source-loader-types.d.ts +10 -0
- package/dist/parquet-source-loader-types.d.ts.map +1 -1
- package/dist/parquet-source-loader-types.js +11 -1
- package/dist/parquet-source-loader-types.js.map +1 -1
- package/dist/parquet-source-loader.cjs +14911 -4316
- package/dist/parquet-source-loader.cjs.map +3 -3
- package/dist/parquet-source-loader.d.ts +42 -4
- package/dist/parquet-source-loader.d.ts.map +1 -1
- package/dist/parquet-source-loader.js +585 -38
- package/dist/parquet-source-loader.js.map +1 -1
- package/dist/parquet-source-types.d.ts +180 -30
- package/dist/parquet-source-types.d.ts.map +1 -1
- package/dist/parquet-source-worker.js +7055 -6989
- package/dist/parquet-source-worker.js.map +4 -4
- package/dist/parquet-worker-node.cjs +8505 -1041
- package/dist/parquet-worker-node.cjs.map +4 -4
- package/dist/parquet-worker-url.js +5 -1
- package/dist/parquet-worker-url.js.map +1 -1
- package/dist/parquet-worker.js +289 -218
- package/dist/parquet-worker.js.map +4 -4
- package/dist/parquetjs/codecs/declare.d.ts +15 -0
- package/dist/parquetjs/codecs/declare.d.ts.map +1 -1
- package/dist/parquetjs/codecs/declare.js +10 -0
- package/dist/parquetjs/codecs/declare.js.map +1 -1
- package/dist/parquetjs/codecs/delta.d.ts.map +1 -1
- package/dist/parquetjs/codecs/delta.js +356 -7
- package/dist/parquetjs/codecs/delta.js.map +1 -1
- package/dist/parquetjs/codecs/index.d.ts.map +1 -1
- package/dist/parquetjs/codecs/index.js +4 -0
- package/dist/parquetjs/codecs/index.js.map +1 -1
- package/dist/parquetjs/codecs/plain.d.ts +1 -1
- package/dist/parquetjs/codecs/plain.d.ts.map +1 -1
- package/dist/parquetjs/codecs/plain.js +153 -12
- package/dist/parquetjs/codecs/plain.js.map +1 -1
- package/dist/parquetjs/codecs/rle.d.ts +4 -0
- package/dist/parquetjs/codecs/rle.d.ts.map +1 -1
- package/dist/parquetjs/codecs/rle.js +406 -3
- package/dist/parquetjs/codecs/rle.js.map +1 -1
- package/dist/parquetjs/compression.d.ts +9 -0
- package/dist/parquetjs/compression.d.ts.map +1 -1
- package/dist/parquetjs/compression.js +86 -67
- package/dist/parquetjs/compression.js.map +1 -1
- package/dist/parquetjs/encoder/dictionary-planner.d.ts.map +1 -1
- package/dist/parquetjs/encoder/dictionary-planner.js +5 -2
- package/dist/parquetjs/encoder/dictionary-planner.js.map +1 -1
- package/dist/parquetjs/encoder/parquet-encoder.d.ts +43 -1
- package/dist/parquetjs/encoder/parquet-encoder.d.ts.map +1 -1
- package/dist/parquetjs/encoder/parquet-encoder.js +691 -34
- package/dist/parquetjs/encoder/parquet-encoder.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/BoundingBox.d.ts +29 -0
- package/dist/parquetjs/parquet-thrift/BoundingBox.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/BoundingBox.js +85 -0
- package/dist/parquetjs/parquet-thrift/BoundingBox.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts +5 -0
- package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnChunk.js +35 -0
- package/dist/parquetjs/parquet-thrift/ColumnChunk.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.d.ts +35 -0
- package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.js +135 -0
- package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts +10 -0
- package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnMetaData.js +73 -0
- package/dist/parquetjs/parquet-thrift/ColumnMetaData.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.d.ts +22 -0
- package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.js +111 -0
- package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.d.ts +16 -0
- package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.js +57 -0
- package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts +5 -0
- package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/FileMetaData.js +35 -0
- package/dist/parquetjs/parquet-thrift/FileMetaData.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/GeospatialStatistics.d.ts +18 -0
- package/dist/parquetjs/parquet-thrift/GeospatialStatistics.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/GeospatialStatistics.js +62 -0
- package/dist/parquetjs/parquet-thrift/GeospatialStatistics.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/SizeStatistics.d.ts +26 -0
- package/dist/parquetjs/parquet-thrift/SizeStatistics.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/SizeStatistics.js +85 -0
- package/dist/parquetjs/parquet-thrift/SizeStatistics.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/index.d.ts +6 -0
- package/dist/parquetjs/parquet-thrift/index.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/index.js +6 -0
- package/dist/parquetjs/parquet-thrift/index.js.map +1 -1
- package/dist/parquetjs/parser/decoders.d.ts +10 -1
- package/dist/parquetjs/parser/decoders.d.ts.map +1 -1
- package/dist/parquetjs/parser/decoders.js +318 -14
- package/dist/parquetjs/parser/decoders.js.map +1 -1
- package/dist/parquetjs/parser/parquet-reader.d.ts +78 -12
- package/dist/parquetjs/parser/parquet-reader.d.ts.map +1 -1
- package/dist/parquetjs/parser/parquet-reader.js +605 -66
- package/dist/parquetjs/parser/parquet-reader.js.map +1 -1
- package/dist/parquetjs/schema/declare.d.ts +22 -3
- package/dist/parquetjs/schema/declare.d.ts.map +1 -1
- package/dist/parquetjs/schema/declare.js.map +1 -1
- package/dist/parquetjs/schema/schema.d.ts.map +1 -1
- package/dist/parquetjs/schema/schema.js +10 -0
- package/dist/parquetjs/schema/schema.js.map +1 -1
- package/dist/parquetjs/schema/shred.d.ts.map +1 -1
- package/dist/parquetjs/schema/shred.js +219 -11
- package/dist/parquetjs/schema/shred.js.map +1 -1
- package/dist/parquetjs/schema/types.js +19 -21
- package/dist/parquetjs/schema/types.js.map +1 -1
- package/dist/parquetjs/schema/variant.d.ts +3 -0
- package/dist/parquetjs/schema/variant.d.ts.map +1 -0
- package/dist/parquetjs/schema/variant.js +248 -0
- package/dist/parquetjs/schema/variant.js.map +1 -0
- package/dist/parquetjs/utils/crc32.d.ts +3 -0
- package/dist/parquetjs/utils/crc32.d.ts.map +1 -0
- package/dist/parquetjs/utils/crc32.js +15 -0
- package/dist/parquetjs/utils/crc32.js.map +1 -0
- package/dist/parquetjs/utils/read-utils.d.ts +11 -1
- package/dist/parquetjs/utils/read-utils.d.ts.map +1 -1
- package/dist/parquetjs/utils/read-utils.js +21 -1
- package/dist/parquetjs/utils/read-utils.js.map +1 -1
- package/dist/unbundled.d.ts +2 -1
- package/dist/unbundled.d.ts.map +1 -1
- package/dist/unbundled.js +1 -0
- package/dist/unbundled.js.map +1 -1
- package/package.json +36 -11
- package/src/bundled.ts +3 -0
- package/src/delta-format.ts +12 -0
- package/src/delta-source-loader-types.ts +40 -0
- package/src/delta-source.ts +305 -0
- package/src/delta-types.ts +40 -0
- package/src/iceberg-rest-catalog.ts +122 -0
- package/src/iceberg-table-source.ts +1037 -0
- package/src/iceberg-types.ts +165 -0
- package/src/index.ts +50 -1
- package/src/lib/arrow/convert-schema-from-parquet.ts +113 -19
- package/src/lib/arrow/create-nested-arrow-vector.ts +116 -0
- package/src/lib/encoders/encode-table-to-parquet-js.ts +268 -7
- package/src/lib/geo/geoparquet-covering.ts +201 -0
- package/src/lib/geo/geospatial-metadata.ts +42 -15
- package/src/lib/parquet-bloom-filter-planner.ts +86 -0
- package/src/lib/parquet-bloom-filter.ts +345 -0
- package/src/lib/parquet-encryption.ts +344 -0
- package/src/lib/parquet-page-index.ts +180 -34
- package/src/lib/parquet-predicate.ts +52 -16
- package/src/lib/parquet-source-worker-decoder.ts +116 -20
- package/src/lib/parquet-source-worker-types.ts +25 -0
- package/src/lib/parsers/get-parquet-schema.ts +3 -1
- package/src/lib/parsers/parse-parquet-to-arrow-js.ts +397 -17
- package/src/lib/parsers/parse-parquet-to-arrow.ts +4 -5
- package/src/lib/parsers/parse-parquet-to-columns.ts +3 -1
- package/src/lib/parsers/parse-parquet-to-json.ts +18 -2
- package/src/lib/slice-parquet-batch.ts +32 -0
- package/src/lib/sources/parquet-range-file.ts +35 -29
- package/src/lib/utils/normalize-parquet-options.ts +17 -1
- package/src/parquet-dataset-source.ts +397 -85
- package/src/parquet-js-loader-types.ts +1 -0
- package/src/parquet-js-loader.ts +2 -1
- package/src/parquet-js-writer.ts +30 -0
- package/src/parquet-loader-options.ts +11 -0
- package/src/parquet-source-capabilities.ts +18 -1
- package/src/parquet-source-loader-types.ts +10 -0
- package/src/parquet-source-loader.ts +929 -52
- package/src/parquet-source-types.ts +197 -28
- package/src/parquet-worker-url.ts +6 -1
- package/src/parquetjs/codecs/declare.ts +27 -0
- package/src/parquetjs/codecs/delta.ts +466 -6
- package/src/parquetjs/codecs/index.ts +4 -0
- package/src/parquetjs/codecs/plain.ts +177 -11
- package/src/parquetjs/codecs/rle.ts +573 -3
- package/src/parquetjs/compression.ts +102 -76
- package/src/parquetjs/encoder/dictionary-planner.ts +7 -2
- package/src/parquetjs/encoder/parquet-encoder.ts +1016 -38
- package/src/parquetjs/parquet-thrift/BoundingBox.ts +108 -0
- package/src/parquetjs/parquet-thrift/ColumnChunk.ts +35 -0
- package/src/parquetjs/parquet-thrift/ColumnCryptoMetaData.ts +139 -0
- package/src/parquetjs/parquet-thrift/ColumnMetaData.ts +73 -0
- package/src/parquetjs/parquet-thrift/EncryptionAlgorithm.ts +127 -0
- package/src/parquetjs/parquet-thrift/FileCryptoMetaData.ts +60 -0
- package/src/parquetjs/parquet-thrift/FileMetaData.ts +35 -0
- package/src/parquetjs/parquet-thrift/GeospatialStatistics.ts +68 -0
- package/src/parquetjs/parquet-thrift/SizeStatistics.ts +98 -0
- package/src/parquetjs/parquet-thrift/index.ts +6 -0
- package/src/parquetjs/parser/decoders.ts +451 -26
- package/src/parquetjs/parser/parquet-reader.ts +935 -87
- package/src/parquetjs/schema/declare.ts +22 -2
- package/src/parquetjs/schema/schema.ts +10 -0
- package/src/parquetjs/schema/shred.ts +238 -11
- package/src/parquetjs/schema/types.ts +16 -25
- package/src/parquetjs/schema/variant.ts +313 -0
- package/src/parquetjs/utils/crc32.ts +15 -0
- package/src/parquetjs/utils/read-utils.ts +26 -1
- package/src/unbundled.ts +14 -0
|
@@ -2,13 +2,41 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {hydrateArrowTable} from '@loaders.gl/arrow';
|
|
5
|
+
import {dehydrateArrowTable, hydrateArrowTable, IndexedArrowTable} from '@loaders.gl/arrow';
|
|
6
6
|
import type {CoreAPI, ReadableFile, SourceLoader} from '@loaders.gl/loader-utils';
|
|
7
|
-
import {
|
|
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';
|
|
8
24
|
import type {ArrayType, ArrowTable, Schema} from '@loaders.gl/schema';
|
|
9
25
|
import {convertTable} from '@loaders.gl/schema-utils';
|
|
10
26
|
|
|
11
27
|
import {getSchemaFromParquetReader} from './lib/parsers/get-parquet-schema';
|
|
28
|
+
import {
|
|
29
|
+
canGeoParquetRowGroupMatch,
|
|
30
|
+
combineParquetPredicates,
|
|
31
|
+
createGeoParquetBoundingBoxPredicate
|
|
32
|
+
} from './lib/geo/geoparquet-covering';
|
|
33
|
+
import {
|
|
34
|
+
decodeParquetSplitBlockBloomFilter,
|
|
35
|
+
encodeParquetBloomFilterValue,
|
|
36
|
+
hashParquetBloomFilterValue,
|
|
37
|
+
checkParquetSplitBlockBloomFilter
|
|
38
|
+
} from './lib/parquet-bloom-filter';
|
|
39
|
+
import {getParquetBloomFilterProbes} from './lib/parquet-bloom-filter-planner';
|
|
12
40
|
import {
|
|
13
41
|
canParquetRowGroupMatch,
|
|
14
42
|
copyParquetPredicate,
|
|
@@ -31,8 +59,12 @@ import {
|
|
|
31
59
|
type ParquetSourceWorkerOptions,
|
|
32
60
|
type ParquetSourceWorkerResult
|
|
33
61
|
} from './lib/parquet-source-worker-types';
|
|
62
|
+
import {sliceParquetBatch} from './lib/slice-parquet-batch';
|
|
34
63
|
import {ParquetRangeFile} from './lib/sources/parquet-range-file';
|
|
64
|
+
import {toPrimitive} from './parquetjs/schema/types';
|
|
65
|
+
import type {ParquetType} from './parquetjs/schema/declare';
|
|
35
66
|
import {
|
|
67
|
+
PARQUET_TABLE_QUERY_CAPABILITIES,
|
|
36
68
|
PARQUET_SOURCE_CAPABILITIES,
|
|
37
69
|
type ParquetSourceCapabilities
|
|
38
70
|
} from './parquet-source-capabilities';
|
|
@@ -41,18 +73,24 @@ import type {
|
|
|
41
73
|
ParquetBatch,
|
|
42
74
|
ParquetColumnChunkMetadata,
|
|
43
75
|
ParquetColumnChunkStatistics,
|
|
76
|
+
ParquetColumnChunkSizeStatistics,
|
|
77
|
+
ParquetGeospatialStatistics,
|
|
44
78
|
ParquetMetadataRequestOptions,
|
|
45
79
|
ParquetObjectVersion,
|
|
80
|
+
ParquetPageScanPlan,
|
|
46
81
|
ParquetPredicate,
|
|
47
82
|
ParquetRowGroupMetadata,
|
|
83
|
+
ParquetSortingColumn,
|
|
48
84
|
ParquetSourceBatch,
|
|
49
85
|
ParquetSourceLoaderOptions,
|
|
50
86
|
ParquetSourceMetadata,
|
|
51
87
|
ParquetSourceReadOptions,
|
|
88
|
+
ParquetSourceExplain,
|
|
52
89
|
ParquetTelemetry,
|
|
53
90
|
ParquetTelemetryEvent
|
|
54
91
|
} from './parquet-source-types';
|
|
55
92
|
export {
|
|
93
|
+
PARQUET_TABLE_QUERY_CAPABILITIES,
|
|
56
94
|
PARQUET_SOURCE_CAPABILITIES,
|
|
57
95
|
type ParquetSourceCapabilities
|
|
58
96
|
} from './parquet-source-capabilities';
|
|
@@ -62,6 +100,8 @@ import {
|
|
|
62
100
|
Encoding,
|
|
63
101
|
type ColumnChunk,
|
|
64
102
|
type FileMetaData,
|
|
103
|
+
type GeospatialStatistics as ParquetThriftGeospatialStatistics,
|
|
104
|
+
type RowGroup,
|
|
65
105
|
type Statistics as ParquetThriftStatistics
|
|
66
106
|
} from './parquetjs/parquet-thrift/index';
|
|
67
107
|
import {ParquetReader} from './parquetjs/parser/parquet-reader';
|
|
@@ -84,10 +124,15 @@ export type {
|
|
|
84
124
|
ParquetBatch,
|
|
85
125
|
ParquetBatchMetadata,
|
|
86
126
|
ParquetBatchProvenance,
|
|
127
|
+
ParquetBoundingBox,
|
|
87
128
|
ParquetColumnChunkMetadata,
|
|
88
129
|
ParquetColumnChunkStatistics,
|
|
130
|
+
ParquetColumnChunkSizeStatistics,
|
|
131
|
+
ParquetGeospatialBoundingBox,
|
|
132
|
+
ParquetGeospatialStatistics,
|
|
89
133
|
ParquetMetadataRequestOptions,
|
|
90
134
|
ParquetObjectVersion,
|
|
135
|
+
ParquetPageScanPlan,
|
|
91
136
|
ParquetComparisonPredicate,
|
|
92
137
|
ParquetInPredicate,
|
|
93
138
|
ParquetLogicalPredicate,
|
|
@@ -99,10 +144,12 @@ export type {
|
|
|
99
144
|
ParquetRangeRequestOptions,
|
|
100
145
|
ParquetReadOptions,
|
|
101
146
|
ParquetRowGroupMetadata,
|
|
147
|
+
ParquetSortingColumn,
|
|
102
148
|
ParquetSourceBatch,
|
|
103
149
|
ParquetSourceLoaderOptions,
|
|
104
150
|
ParquetSourceMetadata,
|
|
105
151
|
ParquetSourceReadOptions,
|
|
152
|
+
ParquetSourceExplain,
|
|
106
153
|
ParquetTelemetry,
|
|
107
154
|
ParquetTelemetryEvent
|
|
108
155
|
} from './parquet-source-types';
|
|
@@ -149,6 +196,27 @@ type SettledParquetRowGroupRead =
|
|
|
149
196
|
result?: never;
|
|
150
197
|
};
|
|
151
198
|
|
|
199
|
+
type ParquetPhysicalRowGroupPlan = {
|
|
200
|
+
/** Predicate including any GeoParquet covering predicate derived from a bounding box. */
|
|
201
|
+
predicate?: ParquetPredicate;
|
|
202
|
+
/** Row groups retained by all physical pruning stages. */
|
|
203
|
+
rowGroupIndices: number[];
|
|
204
|
+
/** Candidate row groups explicitly requested by the caller. */
|
|
205
|
+
requested: number;
|
|
206
|
+
/** Row groups rejected by the caller metadata callback. */
|
|
207
|
+
prunedByCallback: number;
|
|
208
|
+
/** Row groups rejected by native geospatial statistics. */
|
|
209
|
+
prunedBySpatial: number;
|
|
210
|
+
/** Row groups rejected by column or covering statistics. */
|
|
211
|
+
prunedByStatistics: number;
|
|
212
|
+
/** Row groups rejected by split-block Bloom filters. */
|
|
213
|
+
prunedByBloomFilter: number;
|
|
214
|
+
/** Bloom-filter payloads read while planning. */
|
|
215
|
+
bloomFiltersRead: number;
|
|
216
|
+
/** Bloom-filter bytes read while planning. */
|
|
217
|
+
bloomFilterBytesRead: number;
|
|
218
|
+
};
|
|
219
|
+
|
|
152
220
|
const {
|
|
153
221
|
preload: _preloadParquetSourceLoader,
|
|
154
222
|
createDataSource: _createMetadataDataSource,
|
|
@@ -169,7 +237,12 @@ export const ParquetSourceLoaderWithParser = {
|
|
|
169
237
|
export {ParquetSourceLoaderWithParser as ParquetSourceLoader};
|
|
170
238
|
|
|
171
239
|
/** Reusable Parquet source that caches footer/schema state and selectively reads byte ranges. */
|
|
172
|
-
export class ParquetSource
|
|
240
|
+
export class ParquetSource
|
|
241
|
+
extends DataSource<string | Blob, ParquetSourceLoaderOptions>
|
|
242
|
+
implements TableScanSource<ParquetSourceBatch, ParquetPredicate>
|
|
243
|
+
{
|
|
244
|
+
/** Common projection, predicate, limit, streaming, and cancellation capabilities. */
|
|
245
|
+
readonly tableQueryCapabilities = PARQUET_TABLE_QUERY_CAPABILITIES;
|
|
173
246
|
/** Immutable feature support for the current range-backed source implementation. */
|
|
174
247
|
readonly capabilities: ParquetSourceCapabilities = PARQUET_SOURCE_CAPABILITIES;
|
|
175
248
|
/** Shared initialization for this source instance. */
|
|
@@ -212,6 +285,99 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
212
285
|
};
|
|
213
286
|
}
|
|
214
287
|
|
|
288
|
+
/** Discovers schema, capabilities, and footer statistics without decoding Parquet data pages. */
|
|
289
|
+
async getQueryMetadata(options: ScanQueryMetadataOptions = {}): Promise<ScanQueryMetadata> {
|
|
290
|
+
const [schema, metadata] = await Promise.all([
|
|
291
|
+
this.getSchema({signal: options.signal}),
|
|
292
|
+
this.getMetadata({signal: options.signal})
|
|
293
|
+
]);
|
|
294
|
+
return createScanQueryMetadata({
|
|
295
|
+
sourceType: 'parquet',
|
|
296
|
+
queryType: 'table',
|
|
297
|
+
execution: {status: 'supported', method: 'read'},
|
|
298
|
+
name: metadata.name,
|
|
299
|
+
schema,
|
|
300
|
+
capabilities: {table: this.tableQueryCapabilities},
|
|
301
|
+
columnRoles: getParquetColumnRoles(schema.fields.map(field => field.name)),
|
|
302
|
+
statistics: {rowCount: metadata.rowCount, byteLength: metadata.fileByteLength}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Plans a portable query using Parquet metadata without decoding data pages. */
|
|
307
|
+
async getScanPlan(options: ParquetSourceReadOptions = {}): Promise<ParquetSourceExplain> {
|
|
308
|
+
const readOptions = this.getReadOptions(options);
|
|
309
|
+
const initialization = await this.getInitialization(readOptions.signal);
|
|
310
|
+
const sourceColumnNames = initialization.schema.fields.map(field => field.name);
|
|
311
|
+
const signal = readOptions.signal ?? new AbortController().signal;
|
|
312
|
+
const physicalPlan = await this.planRowGroups(initialization, readOptions, signal);
|
|
313
|
+
const pagePlans = await this.planPages(initialization, readOptions, physicalPlan, signal);
|
|
314
|
+
const explanation = explainTableQuery(
|
|
315
|
+
sourceColumnNames,
|
|
316
|
+
{
|
|
317
|
+
columns: readOptions.columns,
|
|
318
|
+
predicate: physicalPlan.predicate,
|
|
319
|
+
limit: readOptions.limit
|
|
320
|
+
},
|
|
321
|
+
PARQUET_TABLE_QUERY_CAPABILITIES
|
|
322
|
+
);
|
|
323
|
+
return Object.freeze({
|
|
324
|
+
...explanation,
|
|
325
|
+
source: 'parquet' as const,
|
|
326
|
+
rowGroups: Object.freeze({
|
|
327
|
+
indices: Object.freeze([...physicalPlan.rowGroupIndices]),
|
|
328
|
+
requested: physicalPlan.requested,
|
|
329
|
+
selected: physicalPlan.rowGroupIndices.length,
|
|
330
|
+
prunedByCallback: physicalPlan.prunedByCallback,
|
|
331
|
+
prunedBySpatial: physicalPlan.prunedBySpatial,
|
|
332
|
+
prunedByStatistics: physicalPlan.prunedByStatistics,
|
|
333
|
+
prunedByBloomFilter: physicalPlan.prunedByBloomFilter
|
|
334
|
+
}),
|
|
335
|
+
bloomFilters: Object.freeze({
|
|
336
|
+
read: physicalPlan.bloomFiltersRead,
|
|
337
|
+
bytesRead: physicalPlan.bloomFilterBytesRead
|
|
338
|
+
}),
|
|
339
|
+
pages: Object.freeze({
|
|
340
|
+
rowGroupsPlanned: new Set(pagePlans.map(plan => plan.rowGroupIndex)).size,
|
|
341
|
+
indexesRead: pagePlans.reduce((sum, plan) => sum + plan.indexesRead, 0),
|
|
342
|
+
total: pagePlans.reduce((sum, plan) => sum + plan.totalPages, 0),
|
|
343
|
+
selected: pagePlans.reduce((sum, plan) => sum + plan.selectedPages, 0),
|
|
344
|
+
rowsPruned: pagePlans.reduce((sum, plan) => sum + plan.rowsPruned, 0),
|
|
345
|
+
plans: Object.freeze(pagePlans)
|
|
346
|
+
})
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Explains the common logical query and Parquet-specific physical scan plan. */
|
|
351
|
+
async explain(options: ParquetSourceReadOptions = {}): Promise<ParquetSourceExplain> {
|
|
352
|
+
return await this.getScanPlan(options);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/** Executes a previously computed physical plan while preserving its row-group decisions. */
|
|
356
|
+
async *executeScanPlan(
|
|
357
|
+
plan: ParquetSourceExplain,
|
|
358
|
+
options: ParquetSourceReadOptions = {}
|
|
359
|
+
): AsyncIterable<ParquetSourceBatch> {
|
|
360
|
+
if (plan.source !== 'parquet') {
|
|
361
|
+
throw new Error('ParquetSource can only execute Parquet scan plans');
|
|
362
|
+
}
|
|
363
|
+
const predicateStep = plan.plan.find(step => step.kind === 'filter');
|
|
364
|
+
const limitStep = plan.plan.find(step => step.kind === 'limit');
|
|
365
|
+
yield* this.read({
|
|
366
|
+
...options,
|
|
367
|
+
columns: options.columns ?? plan.outputColumns,
|
|
368
|
+
predicate:
|
|
369
|
+
options.predicate ??
|
|
370
|
+
(predicateStep?.kind === 'filter' ? predicateStep.predicate : undefined),
|
|
371
|
+
limit: options.limit ?? (limitStep?.kind === 'limit' ? limitStep.limit : undefined),
|
|
372
|
+
rowGroups: plan.rowGroups.indices
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** Common scan-architecture alias for selective Parquet reads. */
|
|
377
|
+
scan(options: ParquetSourceReadOptions = {}): AsyncIterable<ParquetSourceBatch> {
|
|
378
|
+
return this.read(options);
|
|
379
|
+
}
|
|
380
|
+
|
|
215
381
|
/** Returns a copy of cumulative transport, decode, conversion, and pruning telemetry. */
|
|
216
382
|
getTelemetry(): ParquetTelemetry {
|
|
217
383
|
return Object.freeze({...this.telemetry});
|
|
@@ -219,10 +385,26 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
219
385
|
|
|
220
386
|
/** Selectively fetches row groups and columns as ordered Arrow batches with source provenance. */
|
|
221
387
|
async *read(options: ParquetSourceReadOptions = {}): AsyncIterable<ParquetSourceBatch> {
|
|
388
|
+
const startedAt = Date.now();
|
|
389
|
+
const telemetryBefore = this.getTelemetry();
|
|
222
390
|
const readOptions = this.getReadOptions(options);
|
|
391
|
+
if (readOptions.limit === 0) {
|
|
392
|
+
emitScanExecutionTelemetry(
|
|
393
|
+
options.onTelemetry,
|
|
394
|
+
createParquetScanExecutionTelemetry(
|
|
395
|
+
telemetryBefore,
|
|
396
|
+
this.getTelemetry(),
|
|
397
|
+
startedAt,
|
|
398
|
+
'early-terminated',
|
|
399
|
+
'limit'
|
|
400
|
+
)
|
|
401
|
+
);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
223
404
|
const readContext = createReadAbortContext(readOptions.signal);
|
|
224
405
|
const inFlightReads = new Set<Promise<SettledParquetRowGroupRead>>();
|
|
225
406
|
let completed = false;
|
|
407
|
+
let limitReached = false;
|
|
226
408
|
let readError: unknown;
|
|
227
409
|
this.activeReadControllers.add(readContext.abortController);
|
|
228
410
|
|
|
@@ -231,33 +413,21 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
231
413
|
await this.getCompressionInitialization();
|
|
232
414
|
throwIfAborted(readContext.abortController.signal);
|
|
233
415
|
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
416
|
+
const physicalPlan = await this.planRowGroups(
|
|
417
|
+
initialization,
|
|
418
|
+
readOptions,
|
|
419
|
+
readContext.abortController.signal
|
|
237
420
|
);
|
|
238
|
-
const
|
|
239
|
-
? candidateRowGroupIndices.filter(rowGroupIndex =>
|
|
240
|
-
readOptions.rowGroupFilter!(initialization.metadata.rowGroups[rowGroupIndex])
|
|
241
|
-
)
|
|
242
|
-
: candidateRowGroupIndices;
|
|
243
|
-
const predicate = readOptions.predicate;
|
|
244
|
-
const availableColumns = new Set(initialization.schema.fields.map(field => field.name));
|
|
245
|
-
if (predicate) {
|
|
246
|
-
validateParquetPredicate(predicate, availableColumns);
|
|
247
|
-
}
|
|
248
|
-
const rowGroupIndices = predicate
|
|
249
|
-
? callbackFilteredRowGroupIndices.filter(rowGroupIndex =>
|
|
250
|
-
canParquetRowGroupMatch(predicate, initialization.metadata.rowGroups[rowGroupIndex])
|
|
251
|
-
)
|
|
252
|
-
: callbackFilteredRowGroupIndices;
|
|
253
|
-
const rowGroupsPrunedByStatistics =
|
|
254
|
-
callbackFilteredRowGroupIndices.length - rowGroupIndices.length;
|
|
421
|
+
const {predicate, rowGroupIndices} = physicalPlan;
|
|
255
422
|
this.recordTelemetry(
|
|
256
423
|
'row-group-prune',
|
|
257
424
|
{
|
|
258
|
-
rowGroupsRequested:
|
|
259
|
-
rowGroupsPruned:
|
|
260
|
-
rowGroupsPrunedByStatistics
|
|
425
|
+
rowGroupsRequested: physicalPlan.requested,
|
|
426
|
+
rowGroupsPruned: physicalPlan.requested - rowGroupIndices.length,
|
|
427
|
+
rowGroupsPrunedByStatistics: physicalPlan.prunedByStatistics,
|
|
428
|
+
rowGroupsPrunedByBloomFilter: physicalPlan.prunedByBloomFilter,
|
|
429
|
+
bloomFiltersRead: physicalPlan.bloomFiltersRead,
|
|
430
|
+
bloomFilterBytesRead: physicalPlan.bloomFilterBytesRead
|
|
261
431
|
},
|
|
262
432
|
{}
|
|
263
433
|
);
|
|
@@ -271,9 +441,12 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
271
441
|
const projectedSchema = projectSchema(initialization.schema, columns);
|
|
272
442
|
const projectedColumnNames = columns.length ? new Set(columns) : undefined;
|
|
273
443
|
const workerOptions = this.getWorkerOptions(concurrency, readContext.abortController.signal);
|
|
274
|
-
const decodeOnWorker =
|
|
444
|
+
const decodeOnWorker =
|
|
445
|
+
canDecodeParquetSourceOnWorker(workerOptions) &&
|
|
446
|
+
(!initialization.reader.encrypted || Boolean(this.options.parquet?.keyRetriever));
|
|
275
447
|
const scheduledReads = new Map<number, Promise<SettledParquetRowGroupRead>>();
|
|
276
448
|
let nextPositionToSchedule = 0;
|
|
449
|
+
let remainingRows = readOptions.limit ?? Number.POSITIVE_INFINITY;
|
|
277
450
|
|
|
278
451
|
const scheduleReads = (): void => {
|
|
279
452
|
while (
|
|
@@ -329,21 +502,30 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
329
502
|
{arrowConversionDurationMs: deserializationDurationMs},
|
|
330
503
|
{rowGroupIndex, durationMs: deserializationDurationMs}
|
|
331
504
|
);
|
|
332
|
-
const batch =
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
505
|
+
const batch = sliceParquetBatch(
|
|
506
|
+
createParquetBatchFromArrow(
|
|
507
|
+
initialization.metadata,
|
|
508
|
+
projectedSchema,
|
|
509
|
+
rowGroupIndex,
|
|
510
|
+
workerBatch.rowGroupRowOffset,
|
|
511
|
+
arrowTable,
|
|
512
|
+
workerBatch.rowCount,
|
|
513
|
+
workerBatch.rowGroupRowIndices
|
|
514
|
+
),
|
|
515
|
+
Math.min(workerBatch.rowCount, remainingRows)
|
|
340
516
|
);
|
|
341
517
|
this.recordTelemetry(
|
|
342
518
|
'batch',
|
|
343
|
-
{batchesEmitted: 1, rowsEmitted:
|
|
344
|
-
{rowGroupIndex, rowCount:
|
|
519
|
+
{batchesEmitted: 1, rowsEmitted: batch.length},
|
|
520
|
+
{rowGroupIndex, rowCount: batch.length}
|
|
345
521
|
);
|
|
346
522
|
yield batch;
|
|
523
|
+
remainingRows -= batch.length;
|
|
524
|
+
if (remainingRows === 0) {
|
|
525
|
+
limitReached = true;
|
|
526
|
+
completed = true;
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
347
529
|
}
|
|
348
530
|
continue;
|
|
349
531
|
}
|
|
@@ -356,7 +538,11 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
356
538
|
outputRowOffset += outputBatchSize
|
|
357
539
|
) {
|
|
358
540
|
throwIfAborted(readContext.abortController.signal);
|
|
359
|
-
const batchRowCount = Math.min(
|
|
541
|
+
const batchRowCount = Math.min(
|
|
542
|
+
outputBatchSize,
|
|
543
|
+
outputRowCount - outputRowOffset,
|
|
544
|
+
remainingRows
|
|
545
|
+
);
|
|
360
546
|
const batchRowIndices = rowIndices
|
|
361
547
|
? rowIndices!.slice(outputRowOffset, outputRowOffset + batchRowCount)
|
|
362
548
|
: undefined;
|
|
@@ -388,6 +574,12 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
388
574
|
{rowGroupIndex, rowCount: batchRowCount}
|
|
389
575
|
);
|
|
390
576
|
yield batch;
|
|
577
|
+
remainingRows -= batch.length;
|
|
578
|
+
if (remainingRows === 0) {
|
|
579
|
+
limitReached = true;
|
|
580
|
+
completed = true;
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
391
583
|
}
|
|
392
584
|
}
|
|
393
585
|
completed = true;
|
|
@@ -403,11 +595,201 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
403
595
|
if (!completed && readError === undefined) {
|
|
404
596
|
this.recordTelemetry('cancel', {cancellationCount: 1}, {});
|
|
405
597
|
}
|
|
598
|
+
const cancelled = readContext.abortController.signal.aborted || readOptions.signal?.aborted;
|
|
406
599
|
readContext.abortController.abort();
|
|
407
600
|
readContext.removeSignalListener();
|
|
408
601
|
this.activeReadControllers.delete(readContext.abortController);
|
|
409
602
|
await Promise.allSettled([...inFlightReads]);
|
|
603
|
+
const status: ScanExecutionTelemetry['status'] = readError
|
|
604
|
+
? cancelled
|
|
605
|
+
? 'cancelled'
|
|
606
|
+
: 'failed'
|
|
607
|
+
: limitReached || !completed
|
|
608
|
+
? 'early-terminated'
|
|
609
|
+
: 'completed';
|
|
610
|
+
emitScanExecutionTelemetry(
|
|
611
|
+
options.onTelemetry,
|
|
612
|
+
createParquetScanExecutionTelemetry(
|
|
613
|
+
telemetryBefore,
|
|
614
|
+
this.getTelemetry(),
|
|
615
|
+
startedAt,
|
|
616
|
+
status,
|
|
617
|
+
limitReached
|
|
618
|
+
? 'limit'
|
|
619
|
+
: !completed && readError === undefined
|
|
620
|
+
? 'consumer-return'
|
|
621
|
+
: undefined,
|
|
622
|
+
readError
|
|
623
|
+
)
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** Produces the physical row-group plan shared by explain and execution. */
|
|
629
|
+
private async planRowGroups(
|
|
630
|
+
initialization: ParquetSourceInitialization,
|
|
631
|
+
readOptions: ParquetSourceReadOptions,
|
|
632
|
+
signal: AbortSignal
|
|
633
|
+
): Promise<ParquetPhysicalRowGroupPlan> {
|
|
634
|
+
throwIfAborted(signal);
|
|
635
|
+
const candidateRowGroupIndices = normalizeRowGroupIndices(
|
|
636
|
+
readOptions.rowGroups,
|
|
637
|
+
initialization.fileMetadata.row_groups.length
|
|
638
|
+
);
|
|
639
|
+
const callbackFilteredRowGroupIndices = readOptions.rowGroupFilter
|
|
640
|
+
? candidateRowGroupIndices.filter(rowGroupIndex =>
|
|
641
|
+
readOptions.rowGroupFilter!(initialization.metadata.rowGroups[rowGroupIndex])
|
|
642
|
+
)
|
|
643
|
+
: candidateRowGroupIndices;
|
|
644
|
+
const spatiallyFilteredRowGroupIndices = readOptions.bbox
|
|
645
|
+
? callbackFilteredRowGroupIndices.filter(rowGroupIndex =>
|
|
646
|
+
canGeoParquetRowGroupMatch(
|
|
647
|
+
initialization.metadata,
|
|
648
|
+
initialization.metadata.rowGroups[rowGroupIndex],
|
|
649
|
+
readOptions.bbox!,
|
|
650
|
+
readOptions.geometryColumn
|
|
651
|
+
)
|
|
652
|
+
)
|
|
653
|
+
: callbackFilteredRowGroupIndices;
|
|
654
|
+
const spatialPredicate = readOptions.bbox
|
|
655
|
+
? createGeoParquetBoundingBoxPredicate(
|
|
656
|
+
initialization.metadata,
|
|
657
|
+
readOptions.bbox,
|
|
658
|
+
readOptions.geometryColumn
|
|
659
|
+
)
|
|
660
|
+
: undefined;
|
|
661
|
+
const predicate = combineParquetPredicates(readOptions.predicate, spatialPredicate);
|
|
662
|
+
if (predicate) {
|
|
663
|
+
validateParquetPredicate(
|
|
664
|
+
predicate,
|
|
665
|
+
new Set(initialization.schema.fields.map(field => field.name))
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
const statisticsRowGroupIndices = predicate
|
|
669
|
+
? spatiallyFilteredRowGroupIndices.filter(rowGroupIndex =>
|
|
670
|
+
canParquetRowGroupMatch(predicate, initialization.metadata.rowGroups[rowGroupIndex])
|
|
671
|
+
)
|
|
672
|
+
: spatiallyFilteredRowGroupIndices;
|
|
673
|
+
const bloomFilterResult = predicate
|
|
674
|
+
? await filterParquetRowGroupsWithBloomFilters(
|
|
675
|
+
initialization,
|
|
676
|
+
statisticsRowGroupIndices,
|
|
677
|
+
predicate,
|
|
678
|
+
signal
|
|
679
|
+
)
|
|
680
|
+
: {rowGroupIndices: statisticsRowGroupIndices, filtersRead: 0, bytesRead: 0};
|
|
681
|
+
return {
|
|
682
|
+
predicate,
|
|
683
|
+
rowGroupIndices: bloomFilterResult.rowGroupIndices,
|
|
684
|
+
requested: candidateRowGroupIndices.length,
|
|
685
|
+
prunedByCallback: candidateRowGroupIndices.length - callbackFilteredRowGroupIndices.length,
|
|
686
|
+
prunedBySpatial:
|
|
687
|
+
callbackFilteredRowGroupIndices.length - spatiallyFilteredRowGroupIndices.length,
|
|
688
|
+
prunedByStatistics:
|
|
689
|
+
spatiallyFilteredRowGroupIndices.length - statisticsRowGroupIndices.length,
|
|
690
|
+
prunedByBloomFilter:
|
|
691
|
+
statisticsRowGroupIndices.length - bloomFilterResult.rowGroupIndices.length,
|
|
692
|
+
bloomFiltersRead: bloomFilterResult.filtersRead,
|
|
693
|
+
bloomFilterBytesRead: bloomFilterResult.bytesRead
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/** Produces explainable page-index and byte-range plans for retained row groups. */
|
|
698
|
+
private async planPages(
|
|
699
|
+
initialization: ParquetSourceInitialization,
|
|
700
|
+
readOptions: ParquetSourceReadOptions,
|
|
701
|
+
rowGroupPlan: ParquetPhysicalRowGroupPlan,
|
|
702
|
+
signal: AbortSignal
|
|
703
|
+
): Promise<ParquetPageScanPlan[]> {
|
|
704
|
+
if (!rowGroupPlan.predicate) return [];
|
|
705
|
+
const columns = normalizeColumns(readOptions.columns, initialization.schema);
|
|
706
|
+
const predicateColumns = getParquetPredicateColumns(rowGroupPlan.predicate);
|
|
707
|
+
const decodedColumns =
|
|
708
|
+
columns.length === 0 ? [] : [...new Set([...columns, ...predicateColumns])];
|
|
709
|
+
const hasHiddenPredicateColumns =
|
|
710
|
+
columns.length > 0 && predicateColumns.some(column => !columns.includes(column));
|
|
711
|
+
const phases = hasHiddenPredicateColumns
|
|
712
|
+
? [
|
|
713
|
+
{
|
|
714
|
+
phase: 'predicate' as const,
|
|
715
|
+
columns: predicateColumns.map(column => [column]),
|
|
716
|
+
predicate: rowGroupPlan.predicate
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
phase: 'projection' as const,
|
|
720
|
+
columns: columns.map(column => [column]),
|
|
721
|
+
predicate: undefined
|
|
722
|
+
}
|
|
723
|
+
]
|
|
724
|
+
: [
|
|
725
|
+
{
|
|
726
|
+
phase: 'combined' as const,
|
|
727
|
+
columns: decodedColumns.map(column => [column]),
|
|
728
|
+
predicate: rowGroupPlan.predicate
|
|
729
|
+
}
|
|
730
|
+
];
|
|
731
|
+
const plans: ParquetPageScanPlan[] = [];
|
|
732
|
+
for (const rowGroupIndex of rowGroupPlan.rowGroupIndices) {
|
|
733
|
+
const rowGroup = initialization.fileMetadata.row_groups[rowGroupIndex];
|
|
734
|
+
let predicateProvedEmpty = false;
|
|
735
|
+
for (const phase of phases) {
|
|
736
|
+
throwIfAborted(signal);
|
|
737
|
+
if (phase.phase === 'projection' && predicateProvedEmpty) {
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
await initialization.reader.resolveColumnMetadata(rowGroup, rowGroupIndex, phase.columns);
|
|
741
|
+
const pagePlan = phase.predicate
|
|
742
|
+
? await createParquetPagePruningPlan(
|
|
743
|
+
initialization.file,
|
|
744
|
+
rowGroup,
|
|
745
|
+
initialization.parquetSchema,
|
|
746
|
+
phase.columns,
|
|
747
|
+
phase.predicate,
|
|
748
|
+
signal,
|
|
749
|
+
{
|
|
750
|
+
rowGroupOrdinal: rowGroupIndex,
|
|
751
|
+
int96AsTimestamp: Boolean(this.options.parquet?.int96AsTimestamp),
|
|
752
|
+
decryptModule: (bytes, module, rowGroupOrdinal, columnOrdinal, columnChunk) =>
|
|
753
|
+
initialization.reader.decryptIndexModule(
|
|
754
|
+
bytes,
|
|
755
|
+
module,
|
|
756
|
+
rowGroupOrdinal,
|
|
757
|
+
columnOrdinal,
|
|
758
|
+
columnChunk
|
|
759
|
+
)
|
|
760
|
+
}
|
|
761
|
+
)
|
|
762
|
+
: undefined;
|
|
763
|
+
plans.push(
|
|
764
|
+
pagePlan
|
|
765
|
+
? Object.freeze({
|
|
766
|
+
rowGroupIndex,
|
|
767
|
+
phase: phase.phase,
|
|
768
|
+
columns: Object.freeze(phase.columns.map(column => Object.freeze([...column]))),
|
|
769
|
+
rowRanges: Object.freeze(
|
|
770
|
+
pagePlan.rowRanges.map(rowRange => Object.freeze({...rowRange}))
|
|
771
|
+
),
|
|
772
|
+
indexesRead: pagePlan.indexCount,
|
|
773
|
+
totalPages: pagePlan.totalPageCount,
|
|
774
|
+
selectedPages: pagePlan.selectedPageCount,
|
|
775
|
+
rowsPruned: pagePlan.prunedRowCount,
|
|
776
|
+
ranges: Object.freeze(
|
|
777
|
+
getParquetPageReadRanges(
|
|
778
|
+
rowGroup,
|
|
779
|
+
phase.columns,
|
|
780
|
+
pagePlan,
|
|
781
|
+
initialization.parquetSchema
|
|
782
|
+
).map(range => Object.freeze({...range}))
|
|
783
|
+
)
|
|
784
|
+
})
|
|
785
|
+
: createFullColumnScanPlan(rowGroup, rowGroupIndex, phase.phase, phase.columns)
|
|
786
|
+
);
|
|
787
|
+
if (phase.phase === 'predicate' && pagePlan?.rowRanges.length === 0) {
|
|
788
|
+
predicateProvedEmpty = true;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
410
791
|
}
|
|
792
|
+
return plans;
|
|
411
793
|
}
|
|
412
794
|
|
|
413
795
|
/** Closes the underlying readable file and aborts active remote requests. */
|
|
@@ -421,6 +803,7 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
421
803
|
}
|
|
422
804
|
await this.readableFile?.close();
|
|
423
805
|
const initialization = await this.initializationPromise?.catch(() => null);
|
|
806
|
+
initialization?.reader.close();
|
|
424
807
|
if (initialization?.file !== this.readableFile) {
|
|
425
808
|
await initialization?.file.close();
|
|
426
809
|
}
|
|
@@ -458,11 +841,17 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
458
841
|
const rowGroups = options.rowGroups ?? this.options.parquet?.rowGroups;
|
|
459
842
|
const columns = options.columns ?? this.options.parquet?.columns;
|
|
460
843
|
const predicate = options.predicate ?? this.options.parquet?.predicate;
|
|
844
|
+
const bbox = options.bbox ?? this.options.parquet?.bbox;
|
|
845
|
+
const limit = options.limit ?? this.options.parquet?.limit;
|
|
846
|
+
validateTableQueryLimit(limit);
|
|
461
847
|
return {
|
|
462
848
|
rowGroups: rowGroups && [...rowGroups],
|
|
463
849
|
columns: columns && [...columns],
|
|
464
850
|
rowGroupFilter: options.rowGroupFilter ?? this.options.parquet?.rowGroupFilter,
|
|
465
851
|
predicate: predicate ? copyParquetPredicate(predicate) : undefined,
|
|
852
|
+
limit,
|
|
853
|
+
bbox: bbox ? ([...bbox] as ParquetSourceReadOptions['bbox']) : undefined,
|
|
854
|
+
geometryColumn: options.geometryColumn ?? this.options.parquet?.geometryColumn,
|
|
466
855
|
batchSize: options.batchSize ?? this.options.parquet?.batchSize,
|
|
467
856
|
concurrency: options.concurrency ?? this.options.parquet?.concurrency,
|
|
468
857
|
signal: options.signal
|
|
@@ -500,7 +889,25 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
500
889
|
workerOptions: ParquetSourceWorkerOptions | undefined,
|
|
501
890
|
signal: AbortSignal
|
|
502
891
|
): Promise<ParquetRowGroupReadResult> {
|
|
892
|
+
if (
|
|
893
|
+
predicate &&
|
|
894
|
+
projectedColumnNames &&
|
|
895
|
+
getParquetPredicateColumns(predicate).some(column => !projectedColumnNames.has(column))
|
|
896
|
+
) {
|
|
897
|
+
return await this.readRowGroupWithLateMaterialization(
|
|
898
|
+
initialization,
|
|
899
|
+
rowGroupIndex,
|
|
900
|
+
columnList,
|
|
901
|
+
projectedSchema,
|
|
902
|
+
batchSize,
|
|
903
|
+
predicate,
|
|
904
|
+
projectedColumnNames,
|
|
905
|
+
workerOptions,
|
|
906
|
+
signal
|
|
907
|
+
);
|
|
908
|
+
}
|
|
503
909
|
const rowGroup = initialization.fileMetadata.row_groups[rowGroupIndex];
|
|
910
|
+
await initialization.reader.resolveColumnMetadata(rowGroup, rowGroupIndex, columnList);
|
|
504
911
|
const pagePlan = predicate
|
|
505
912
|
? await createParquetPagePruningPlan(
|
|
506
913
|
initialization.file,
|
|
@@ -508,7 +915,19 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
508
915
|
initialization.parquetSchema,
|
|
509
916
|
columnList,
|
|
510
917
|
predicate,
|
|
511
|
-
signal
|
|
918
|
+
signal,
|
|
919
|
+
{
|
|
920
|
+
rowGroupOrdinal: rowGroupIndex,
|
|
921
|
+
int96AsTimestamp: Boolean(this.options.parquet?.int96AsTimestamp),
|
|
922
|
+
decryptModule: (bytes, module, rowGroupOrdinal, columnOrdinal, columnChunk) =>
|
|
923
|
+
initialization.reader.decryptIndexModule(
|
|
924
|
+
bytes,
|
|
925
|
+
module,
|
|
926
|
+
rowGroupOrdinal,
|
|
927
|
+
columnOrdinal,
|
|
928
|
+
columnChunk
|
|
929
|
+
)
|
|
930
|
+
}
|
|
512
931
|
)
|
|
513
932
|
: undefined;
|
|
514
933
|
if (pagePlan) {
|
|
@@ -542,7 +961,8 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
542
961
|
columnList,
|
|
543
962
|
rowRange,
|
|
544
963
|
pagePlan.pageLocations,
|
|
545
|
-
signal
|
|
964
|
+
signal,
|
|
965
|
+
rowGroupIndex
|
|
546
966
|
)
|
|
547
967
|
)
|
|
548
968
|
)
|
|
@@ -551,7 +971,8 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
551
971
|
initialization.parquetSchema,
|
|
552
972
|
rowGroup,
|
|
553
973
|
columnList,
|
|
554
|
-
signal
|
|
974
|
+
signal,
|
|
975
|
+
rowGroupIndex
|
|
555
976
|
)
|
|
556
977
|
];
|
|
557
978
|
throwIfAborted(signal);
|
|
@@ -615,7 +1036,7 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
615
1036
|
return Boolean(path && (columnList.length === 0 || fieldIndexOf(columnList, path) >= 0));
|
|
616
1037
|
});
|
|
617
1038
|
const rangeDescriptors = pagePlan
|
|
618
|
-
? getParquetPageReadRanges(rowGroup, columnList, pagePlan)
|
|
1039
|
+
? getParquetPageReadRanges(rowGroup, columnList, pagePlan, initialization.parquetSchema)
|
|
619
1040
|
: selectedColumnChunks.map(getColumnChunkRange);
|
|
620
1041
|
const ranges = await Promise.all(
|
|
621
1042
|
rangeDescriptors.map(async ({offset, length}) => {
|
|
@@ -624,6 +1045,26 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
624
1045
|
);
|
|
625
1046
|
throwIfAborted(signal);
|
|
626
1047
|
|
|
1048
|
+
const workerColumnChunks = await Promise.all(
|
|
1049
|
+
selectedColumnChunks.map(async columnChunk => {
|
|
1050
|
+
const descriptor = createParquetSourceWorkerColumnChunk(columnChunk);
|
|
1051
|
+
descriptor.columnOrdinal = rowGroup.columns.indexOf(columnChunk);
|
|
1052
|
+
if (columnChunk.crypto_metadata) {
|
|
1053
|
+
const columnOrdinal = rowGroup.columns.indexOf(columnChunk);
|
|
1054
|
+
const key = await initialization.reader.getColumnKeyForWorker(
|
|
1055
|
+
columnChunk,
|
|
1056
|
+
rowGroupIndex,
|
|
1057
|
+
columnOrdinal
|
|
1058
|
+
);
|
|
1059
|
+
descriptor.encrypted = true;
|
|
1060
|
+
descriptor.keyMetadata = key.keyMetadata?.slice().buffer;
|
|
1061
|
+
descriptor.keyMaterial = key.keyMaterial.slice().buffer;
|
|
1062
|
+
}
|
|
1063
|
+
return descriptor;
|
|
1064
|
+
})
|
|
1065
|
+
);
|
|
1066
|
+
const encryptionContext = initialization.reader.getEncryptionContextForWorker();
|
|
1067
|
+
|
|
627
1068
|
let workerResult: ParquetSourceWorkerResult;
|
|
628
1069
|
try {
|
|
629
1070
|
workerResult = await decodeParquetSourceRowGroupOnWorker(
|
|
@@ -633,12 +1074,21 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
633
1074
|
uncompressedByteLength: Number(rowGroup.total_byte_size),
|
|
634
1075
|
schemaDefinition: initialization.parquetSchema.schema,
|
|
635
1076
|
projectedSchema,
|
|
636
|
-
columnChunks:
|
|
1077
|
+
columnChunks: workerColumnChunks,
|
|
637
1078
|
ranges,
|
|
638
1079
|
batchSize: batchSize || Math.max(Number(rowGroup.num_rows), 1),
|
|
639
1080
|
predicate: predicate ? copyParquetPredicate(predicate) : undefined,
|
|
640
1081
|
pagePlan,
|
|
641
|
-
|
|
1082
|
+
encryption: encryptionContext
|
|
1083
|
+
? {
|
|
1084
|
+
algorithm: encryptionContext.algorithm,
|
|
1085
|
+
aadPrefix: encryptionContext.aadPrefix?.slice().buffer,
|
|
1086
|
+
fileUnique: encryptionContext.fileUnique.slice().buffer
|
|
1087
|
+
}
|
|
1088
|
+
: undefined,
|
|
1089
|
+
preserveBinary: Boolean(this.options.parquet?.preserveBinary),
|
|
1090
|
+
int96AsTimestamp: Boolean(this.options.parquet?.int96AsTimestamp),
|
|
1091
|
+
verifyPageChecksums: Boolean(this.options.parquet?.verifyPageChecksums)
|
|
642
1092
|
},
|
|
643
1093
|
workerOptions
|
|
644
1094
|
);
|
|
@@ -670,6 +1120,72 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
670
1120
|
return {rowGroupIndex, rowCount: workerResult.rowCount, workerResult};
|
|
671
1121
|
}
|
|
672
1122
|
|
|
1123
|
+
/** Decodes predicate columns first, avoiding projected-column decoding for empty matches. */
|
|
1124
|
+
private async readRowGroupWithLateMaterialization(
|
|
1125
|
+
initialization: ParquetSourceInitialization,
|
|
1126
|
+
rowGroupIndex: number,
|
|
1127
|
+
columnList: string[][],
|
|
1128
|
+
projectedSchema: Schema,
|
|
1129
|
+
batchSize: number | undefined,
|
|
1130
|
+
predicate: ParquetPredicate,
|
|
1131
|
+
projectedColumnNames: ReadonlySet<string>,
|
|
1132
|
+
workerOptions: ParquetSourceWorkerOptions | undefined,
|
|
1133
|
+
signal: AbortSignal
|
|
1134
|
+
): Promise<ParquetRowGroupReadResult> {
|
|
1135
|
+
const predicateColumns = getParquetPredicateColumns(predicate);
|
|
1136
|
+
const predicateColumnList = predicateColumns.map(column => [column]);
|
|
1137
|
+
const filterResult = await this.readRowGroup(
|
|
1138
|
+
initialization,
|
|
1139
|
+
rowGroupIndex,
|
|
1140
|
+
predicateColumnList,
|
|
1141
|
+
projectSchema(initialization.schema, predicateColumns),
|
|
1142
|
+
batchSize,
|
|
1143
|
+
predicate,
|
|
1144
|
+
new Set(predicateColumns),
|
|
1145
|
+
workerOptions,
|
|
1146
|
+
signal
|
|
1147
|
+
);
|
|
1148
|
+
const rowIndices = filterResult.rowIndices;
|
|
1149
|
+
const selectedRowIndices = rowIndices || getWorkerRowIndices(filterResult.workerResult);
|
|
1150
|
+
if (!selectedRowIndices || selectedRowIndices.length === 0) {
|
|
1151
|
+
return {rowGroupIndex, rowCount: 0, columns: {}, rowIndices: []};
|
|
1152
|
+
}
|
|
1153
|
+
const projectedColumnList = columnList.filter(columnPath =>
|
|
1154
|
+
projectedColumnNames.has(columnPath[0])
|
|
1155
|
+
);
|
|
1156
|
+
const projectedResult = await this.readRowGroup(
|
|
1157
|
+
initialization,
|
|
1158
|
+
rowGroupIndex,
|
|
1159
|
+
projectedColumnList,
|
|
1160
|
+
projectedSchema,
|
|
1161
|
+
batchSize,
|
|
1162
|
+
undefined,
|
|
1163
|
+
projectedColumnNames,
|
|
1164
|
+
workerOptions,
|
|
1165
|
+
signal
|
|
1166
|
+
);
|
|
1167
|
+
if (workerOptions && projectedResult.workerResult) {
|
|
1168
|
+
return {
|
|
1169
|
+
rowGroupIndex,
|
|
1170
|
+
rowCount: selectedRowIndices.length,
|
|
1171
|
+
rowIndices: selectedRowIndices,
|
|
1172
|
+
workerResult: gatherWorkerProjectedRows(
|
|
1173
|
+
projectedResult.workerResult,
|
|
1174
|
+
selectedRowIndices,
|
|
1175
|
+
batchSize
|
|
1176
|
+
)
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
return {
|
|
1180
|
+
rowGroupIndex,
|
|
1181
|
+
rowCount: selectedRowIndices.length,
|
|
1182
|
+
rowIndices: selectedRowIndices,
|
|
1183
|
+
columns: projectedResult.columns
|
|
1184
|
+
? gatherParquetColumns(projectedResult.columns, selectedRowIndices, projectedColumnNames)
|
|
1185
|
+
: {}
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
|
|
673
1189
|
/** Records the rows and pages avoided by one conservative page-index plan. */
|
|
674
1190
|
private recordPagePruningTelemetry(rowGroupIndex: number, plan: ParquetPagePruningPlan): void {
|
|
675
1191
|
this.recordTelemetry(
|
|
@@ -692,6 +1208,11 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
692
1208
|
try {
|
|
693
1209
|
const reader = new ParquetReader(file, {
|
|
694
1210
|
preserveBinary: this.options.parquet?.preserveBinary,
|
|
1211
|
+
int96AsTimestamp: this.options.parquet?.int96AsTimestamp,
|
|
1212
|
+
verifyPageChecksums: this.options.parquet?.verifyPageChecksums,
|
|
1213
|
+
verifyFooterSignature: this.options.parquet?.verifyFooterSignature,
|
|
1214
|
+
keyRetriever: this.options.parquet?.keyRetriever,
|
|
1215
|
+
aadPrefix: this.options.parquet?.aadPrefix,
|
|
695
1216
|
signal
|
|
696
1217
|
});
|
|
697
1218
|
const fileMetadata = await reader.getFileMetadata();
|
|
@@ -764,6 +1285,29 @@ export class ParquetSource extends DataSource<string | Blob, ParquetSourceLoader
|
|
|
764
1285
|
}
|
|
765
1286
|
}
|
|
766
1287
|
|
|
1288
|
+
function getParquetColumnRoles(columnNames: readonly string[]): Record<string, ScanColumnRole> {
|
|
1289
|
+
const roles: Record<string, ScanColumnRole> = {};
|
|
1290
|
+
for (const columnName of columnNames) {
|
|
1291
|
+
const normalizedName = columnName.toLowerCase();
|
|
1292
|
+
if (normalizedName === 'geometry' || normalizedName === 'geom' || normalizedName === 'shape') {
|
|
1293
|
+
roles[columnName] = 'geometry';
|
|
1294
|
+
} else if (
|
|
1295
|
+
normalizedName === 'longitude' ||
|
|
1296
|
+
normalizedName === 'lon' ||
|
|
1297
|
+
normalizedName === 'x'
|
|
1298
|
+
) {
|
|
1299
|
+
roles[columnName] = 'longitude';
|
|
1300
|
+
} else if (
|
|
1301
|
+
normalizedName === 'latitude' ||
|
|
1302
|
+
normalizedName === 'lat' ||
|
|
1303
|
+
normalizedName === 'y'
|
|
1304
|
+
) {
|
|
1305
|
+
roles[columnName] = 'latitude';
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
return roles;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
767
1311
|
/** Normalizes a decoded Parquet footer into source metadata. */
|
|
768
1312
|
function createParquetSourceMetadata(
|
|
769
1313
|
data: string | Blob,
|
|
@@ -784,7 +1328,8 @@ function createParquetSourceMetadata(
|
|
|
784
1328
|
rowOffset,
|
|
785
1329
|
Number(rowGroup.num_rows),
|
|
786
1330
|
Number(rowGroup.total_byte_size),
|
|
787
|
-
columns
|
|
1331
|
+
columns,
|
|
1332
|
+
rowGroup.sorting_columns
|
|
788
1333
|
);
|
|
789
1334
|
rowOffset += normalizedRowGroup.rowCount;
|
|
790
1335
|
return normalizedRowGroup;
|
|
@@ -821,6 +1366,8 @@ function createColumnChunkMetadata(
|
|
|
821
1366
|
columnMetadata.statistics,
|
|
822
1367
|
parquetSchema.findField(columnMetadata.path_in_schema)
|
|
823
1368
|
);
|
|
1369
|
+
const geospatialStatistics = createGeospatialStatistics(columnMetadata.geospatial_statistics);
|
|
1370
|
+
const sizeStatistics = createSizeStatistics(columnMetadata.size_statistics);
|
|
824
1371
|
const compressedByteLength = Number(columnMetadata.total_compressed_size);
|
|
825
1372
|
const uncompressedByteLength = Number(columnMetadata.total_uncompressed_size);
|
|
826
1373
|
return Object.freeze({
|
|
@@ -851,7 +1398,92 @@ function createColumnChunkMetadata(
|
|
|
851
1398
|
? undefined
|
|
852
1399
|
: Number(columnChunk.offset_index_offset),
|
|
853
1400
|
offsetIndexByteLength: columnChunk.offset_index_length,
|
|
854
|
-
|
|
1401
|
+
bloomFilterOffset:
|
|
1402
|
+
columnMetadata.bloom_filter_offset === undefined
|
|
1403
|
+
? undefined
|
|
1404
|
+
: Number(columnMetadata.bloom_filter_offset),
|
|
1405
|
+
bloomFilterByteLength: columnMetadata.bloom_filter_length,
|
|
1406
|
+
statistics,
|
|
1407
|
+
sizeStatistics,
|
|
1408
|
+
geospatialStatistics
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/** Normalizes optional Parquet size statistics without inventing missing counts. */
|
|
1413
|
+
function createSizeStatistics(
|
|
1414
|
+
statistics:
|
|
1415
|
+
| {
|
|
1416
|
+
unencoded_byte_array_data_bytes?: {toNumber?: () => number} | number;
|
|
1417
|
+
repetition_level_histogram?: Array<{toNumber?: () => number} | number>;
|
|
1418
|
+
definition_level_histogram?: Array<{toNumber?: () => number} | number>;
|
|
1419
|
+
}
|
|
1420
|
+
| undefined
|
|
1421
|
+
): ParquetColumnChunkSizeStatistics | undefined {
|
|
1422
|
+
if (!statistics) return undefined;
|
|
1423
|
+
const result: ParquetColumnChunkSizeStatistics = {
|
|
1424
|
+
unencodedByteArrayDataBytes:
|
|
1425
|
+
statistics.unencoded_byte_array_data_bytes === undefined
|
|
1426
|
+
? undefined
|
|
1427
|
+
: toSafeNumber(statistics.unencoded_byte_array_data_bytes),
|
|
1428
|
+
repetitionLevelHistogram: normalizeSizeStatisticsList(statistics.repetition_level_histogram),
|
|
1429
|
+
definitionLevelHistogram: normalizeSizeStatisticsList(statistics.definition_level_histogram)
|
|
1430
|
+
};
|
|
1431
|
+
if (
|
|
1432
|
+
result.unencodedByteArrayDataBytes === undefined &&
|
|
1433
|
+
result.repetitionLevelHistogram === undefined &&
|
|
1434
|
+
result.definitionLevelHistogram === undefined
|
|
1435
|
+
) {
|
|
1436
|
+
return undefined;
|
|
1437
|
+
}
|
|
1438
|
+
return Object.freeze({
|
|
1439
|
+
...result,
|
|
1440
|
+
repetitionLevelHistogram: result.repetitionLevelHistogram
|
|
1441
|
+
? Object.freeze(result.repetitionLevelHistogram)
|
|
1442
|
+
: undefined,
|
|
1443
|
+
definitionLevelHistogram: result.definitionLevelHistogram
|
|
1444
|
+
? Object.freeze(result.definitionLevelHistogram)
|
|
1445
|
+
: undefined
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
function toSafeNumber(value: {toNumber?: () => number} | number): number | undefined {
|
|
1450
|
+
const numberValue = typeof value === 'number' ? value : value.toNumber?.();
|
|
1451
|
+
return numberValue !== undefined && Number.isSafeInteger(numberValue) && numberValue >= 0
|
|
1452
|
+
? numberValue
|
|
1453
|
+
: undefined;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
/** Converts a Thrift int64 histogram only when every entry is a safe non-negative integer. */
|
|
1457
|
+
function normalizeSizeStatisticsList(
|
|
1458
|
+
values: Array<{toNumber?: () => number} | number> | undefined
|
|
1459
|
+
): readonly number[] | undefined {
|
|
1460
|
+
if (!values) return undefined;
|
|
1461
|
+
const normalized = values.map(toSafeNumber);
|
|
1462
|
+
return normalized.every((value): value is number => value !== undefined) ? normalized : undefined;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/** Normalizes native Parquet geospatial statistics from the decoded footer. */
|
|
1466
|
+
function createGeospatialStatistics(
|
|
1467
|
+
statistics: ParquetThriftGeospatialStatistics | undefined
|
|
1468
|
+
): ParquetGeospatialStatistics | undefined {
|
|
1469
|
+
if (!statistics) return undefined;
|
|
1470
|
+
const bbox = statistics.bbox
|
|
1471
|
+
? Object.freeze({
|
|
1472
|
+
xmin: statistics.bbox.xmin,
|
|
1473
|
+
xmax: statistics.bbox.xmax,
|
|
1474
|
+
ymin: statistics.bbox.ymin,
|
|
1475
|
+
ymax: statistics.bbox.ymax,
|
|
1476
|
+
zmin: statistics.bbox.zmin,
|
|
1477
|
+
zmax: statistics.bbox.zmax,
|
|
1478
|
+
mmin: statistics.bbox.mmin,
|
|
1479
|
+
mmax: statistics.bbox.mmax
|
|
1480
|
+
})
|
|
1481
|
+
: undefined;
|
|
1482
|
+
return Object.freeze({
|
|
1483
|
+
bbox,
|
|
1484
|
+
geometryTypes: statistics.geospatial_types
|
|
1485
|
+
? Object.freeze([...statistics.geospatial_types])
|
|
1486
|
+
: undefined
|
|
855
1487
|
});
|
|
856
1488
|
}
|
|
857
1489
|
|
|
@@ -933,7 +1565,8 @@ function createRowGroupMetadata(
|
|
|
933
1565
|
rowOffset: number,
|
|
934
1566
|
rowCount: number,
|
|
935
1567
|
uncompressedByteLength: number,
|
|
936
|
-
columns: ParquetColumnChunkMetadata[]
|
|
1568
|
+
columns: ParquetColumnChunkMetadata[],
|
|
1569
|
+
sortingColumns: RowGroup['sorting_columns']
|
|
937
1570
|
): ParquetRowGroupMetadata {
|
|
938
1571
|
const compressedByteLength = columns.reduce(
|
|
939
1572
|
(sum, column) => sum + column.compressedByteLength,
|
|
@@ -947,10 +1580,54 @@ function createRowGroupMetadata(
|
|
|
947
1580
|
uncompressedSize: uncompressedByteLength,
|
|
948
1581
|
compressedByteLength,
|
|
949
1582
|
compressedSize: compressedByteLength,
|
|
950
|
-
columns: Object.freeze(columns)
|
|
1583
|
+
columns: Object.freeze(columns),
|
|
1584
|
+
sortingColumns: Object.freeze(
|
|
1585
|
+
(sortingColumns || []).map(
|
|
1586
|
+
sortingColumn =>
|
|
1587
|
+
Object.freeze({
|
|
1588
|
+
columnIndex: sortingColumn.column_idx,
|
|
1589
|
+
descending: sortingColumn.descending,
|
|
1590
|
+
nullsFirst: sortingColumn.nulls_first
|
|
1591
|
+
}) satisfies ParquetSortingColumn
|
|
1592
|
+
)
|
|
1593
|
+
)
|
|
951
1594
|
});
|
|
952
1595
|
}
|
|
953
1596
|
|
|
1597
|
+
/** Describes a phase that reads complete column chunks because no page index is available. */
|
|
1598
|
+
function createFullColumnScanPlan(
|
|
1599
|
+
rowGroup: RowGroup,
|
|
1600
|
+
rowGroupIndex: number,
|
|
1601
|
+
phase: ParquetPageScanPlan['phase'],
|
|
1602
|
+
columns: readonly string[][]
|
|
1603
|
+
): ParquetPageScanPlan {
|
|
1604
|
+
const selectedColumnChunks = rowGroup.columns.filter(columnChunk => {
|
|
1605
|
+
const path = columnChunk.meta_data?.path_in_schema;
|
|
1606
|
+
return Boolean(
|
|
1607
|
+
path && (columns.length === 0 || columns.some(column => pathMatchesSelection(column, path)))
|
|
1608
|
+
);
|
|
1609
|
+
});
|
|
1610
|
+
const rowCount = Number(rowGroup.num_rows);
|
|
1611
|
+
return Object.freeze({
|
|
1612
|
+
rowGroupIndex,
|
|
1613
|
+
phase,
|
|
1614
|
+
columns: Object.freeze(columns.map(column => Object.freeze([...column]))),
|
|
1615
|
+
rowRanges: Object.freeze([Object.freeze({start: 0, end: rowCount})]),
|
|
1616
|
+
indexesRead: 0,
|
|
1617
|
+
totalPages: 0,
|
|
1618
|
+
selectedPages: 0,
|
|
1619
|
+
rowsPruned: 0,
|
|
1620
|
+
ranges: Object.freeze(
|
|
1621
|
+
selectedColumnChunks.map(columnChunk => Object.freeze(getColumnChunkRange(columnChunk)))
|
|
1622
|
+
)
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
/** Returns whether a requested path selects a footer leaf or one of its nested children. */
|
|
1627
|
+
function pathMatchesSelection(selection: readonly string[], path: readonly string[]): boolean {
|
|
1628
|
+
return selection.length <= path.length && selection.every((part, index) => part === path[index]);
|
|
1629
|
+
}
|
|
1630
|
+
|
|
954
1631
|
/** Returns the complete contiguous byte range occupied by one Parquet column chunk. */
|
|
955
1632
|
function getColumnChunkRange(columnChunk: ColumnChunk): {offset: number; length: number} {
|
|
956
1633
|
if (columnChunk.file_path) {
|
|
@@ -1048,11 +1725,8 @@ function createParquetBatchFromArrow(
|
|
|
1048
1725
|
: undefined
|
|
1049
1726
|
});
|
|
1050
1727
|
return {
|
|
1051
|
-
|
|
1052
|
-
shape: 'arrow-table',
|
|
1728
|
+
...makeTableScanBatch({shape: 'arrow-table', schema, data}),
|
|
1053
1729
|
schemaType: 'explicit',
|
|
1054
|
-
schema,
|
|
1055
|
-
data,
|
|
1056
1730
|
length: rowCount,
|
|
1057
1731
|
metadata: provenance,
|
|
1058
1732
|
...provenance
|
|
@@ -1173,6 +1847,9 @@ function createParquetTelemetry(): ParquetTelemetry {
|
|
|
1173
1847
|
rowGroupsRequested: 0,
|
|
1174
1848
|
rowGroupsPruned: 0,
|
|
1175
1849
|
rowGroupsPrunedByStatistics: 0,
|
|
1850
|
+
rowGroupsPrunedByBloomFilter: 0,
|
|
1851
|
+
bloomFiltersRead: 0,
|
|
1852
|
+
bloomFilterBytesRead: 0,
|
|
1176
1853
|
rowGroupsPrunedByPageIndex: 0,
|
|
1177
1854
|
pageIndexesRead: 0,
|
|
1178
1855
|
pagesRead: 0,
|
|
@@ -1188,6 +1865,45 @@ function createParquetTelemetry(): ParquetTelemetry {
|
|
|
1188
1865
|
};
|
|
1189
1866
|
}
|
|
1190
1867
|
|
|
1868
|
+
/** Converts cumulative Parquet counters into one portable read-scoped execution snapshot. */
|
|
1869
|
+
function createParquetScanExecutionTelemetry(
|
|
1870
|
+
before: ParquetTelemetry,
|
|
1871
|
+
after: ParquetTelemetry,
|
|
1872
|
+
startedAt: number,
|
|
1873
|
+
status: ScanExecutionTelemetry['status'],
|
|
1874
|
+
earlyTerminationReason?: ScanExecutionTelemetry['earlyTerminationReason'],
|
|
1875
|
+
error?: unknown
|
|
1876
|
+
): ScanExecutionTelemetry {
|
|
1877
|
+
const delta = Object.fromEntries(
|
|
1878
|
+
Object.keys(after).map(key => {
|
|
1879
|
+
const telemetryKey = key as keyof ParquetTelemetry;
|
|
1880
|
+
return [key, after[telemetryKey] - before[telemetryKey]];
|
|
1881
|
+
})
|
|
1882
|
+
) as ParquetTelemetry;
|
|
1883
|
+
const rowsRead = Math.max(delta.predicateRowsTested, delta.rowsEmitted);
|
|
1884
|
+
return Object.freeze({
|
|
1885
|
+
status,
|
|
1886
|
+
sourcesPlanned: 1,
|
|
1887
|
+
sourcesRead:
|
|
1888
|
+
status === 'early-terminated' && earlyTerminationReason === 'limit' && rowsRead === 0 ? 0 : 1,
|
|
1889
|
+
batchesRead: delta.batchesEmitted,
|
|
1890
|
+
batchesDecoded: delta.rowGroupsDecoded,
|
|
1891
|
+
rowsRead,
|
|
1892
|
+
rowsTested: delta.predicateRowsTested || undefined,
|
|
1893
|
+
rowsRetained: delta.predicateRowsMatched || undefined,
|
|
1894
|
+
rowsReturned: delta.rowsEmitted,
|
|
1895
|
+
bytesRead: delta.downloadedBytes,
|
|
1896
|
+
bytesFetched: delta.downloadedBytes,
|
|
1897
|
+
filesOpened: rowsRead || delta.downloadedBytes ? 1 : 0,
|
|
1898
|
+
tasksOpened: delta.rowGroupsDecoded,
|
|
1899
|
+
rowsPruned: delta.rowsPrunedByPageIndex,
|
|
1900
|
+
durationMilliseconds: Date.now() - startedAt,
|
|
1901
|
+
earlyTerminationReason,
|
|
1902
|
+
details: Object.freeze({...delta}),
|
|
1903
|
+
...(error === undefined ? {} : {error})
|
|
1904
|
+
});
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1191
1907
|
/** Returns a monotonic timestamp when available and falls back to wall-clock time. */
|
|
1192
1908
|
function getCurrentTime(): number {
|
|
1193
1909
|
return globalThis.performance?.now() ?? Date.now();
|
|
@@ -1259,6 +1975,167 @@ function getSourceName(data: string | Blob, url: string): string {
|
|
|
1259
1975
|
return sourceName.replace(/\.parquet$/i, '') || 'parquet';
|
|
1260
1976
|
}
|
|
1261
1977
|
|
|
1978
|
+
type ParquetBloomFilterReadResult = {
|
|
1979
|
+
rowGroupIndices: number[];
|
|
1980
|
+
filtersRead: number;
|
|
1981
|
+
bytesRead: number;
|
|
1982
|
+
};
|
|
1983
|
+
|
|
1984
|
+
/** Reconstructs exact row-group indexes from worker predicate batches. */
|
|
1985
|
+
function getWorkerRowIndices(workerResult?: ParquetSourceWorkerResult): number[] {
|
|
1986
|
+
if (!workerResult) return [];
|
|
1987
|
+
return workerResult.batches.flatMap(batch =>
|
|
1988
|
+
batch.rowGroupRowIndices
|
|
1989
|
+
? [...batch.rowGroupRowIndices]
|
|
1990
|
+
: Array.from({length: batch.rowCount}, (_, index) => batch.rowGroupRowOffset + index)
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
/** Gathers projected worker Arrow batches through the shared indexed Arrow table utility. */
|
|
1995
|
+
function gatherWorkerProjectedRows(
|
|
1996
|
+
workerResult: ParquetSourceWorkerResult,
|
|
1997
|
+
rowIndices: readonly number[],
|
|
1998
|
+
batchSize: number | undefined
|
|
1999
|
+
): ParquetSourceWorkerResult {
|
|
2000
|
+
const tables = workerResult.batches.map(batch => hydrateArrowTable(batch.arrowTable));
|
|
2001
|
+
if (tables.length === 0) {
|
|
2002
|
+
return {...workerResult, rowCount: 0, batches: []};
|
|
2003
|
+
}
|
|
2004
|
+
const table = tables.length === 1 ? tables[0] : tables[0].concat(...tables.slice(1));
|
|
2005
|
+
const indexedTable = new IndexedArrowTable(table, rowIndices);
|
|
2006
|
+
const outputBatchSize = batchSize || Math.max(rowIndices.length, 1);
|
|
2007
|
+
const batches: ParquetSourceWorkerResult['batches'] = [];
|
|
2008
|
+
for (let offset = 0; offset < rowIndices.length; offset += outputBatchSize) {
|
|
2009
|
+
const end = Math.min(offset + outputBatchSize, rowIndices.length);
|
|
2010
|
+
const batchIndexes = rowIndices.slice(offset, end);
|
|
2011
|
+
const batchTable = indexedTable.slice(offset, end).materializeArrowTable();
|
|
2012
|
+
batches.push({
|
|
2013
|
+
rowGroupRowOffset: batchIndexes[0],
|
|
2014
|
+
rowCount: batchIndexes.length,
|
|
2015
|
+
rowGroupRowIndices: [...batchIndexes],
|
|
2016
|
+
arrowTable: dehydrateArrowTable(batchTable)
|
|
2017
|
+
});
|
|
2018
|
+
}
|
|
2019
|
+
return {
|
|
2020
|
+
sourceRowCount: workerResult.sourceRowCount,
|
|
2021
|
+
rowCount: rowIndices.length,
|
|
2022
|
+
batches,
|
|
2023
|
+
decodeDurationMs: workerResult.decodeDurationMs,
|
|
2024
|
+
arrowConversionDurationMs: workerResult.arrowConversionDurationMs
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
/** Applies only proven-negative Bloom-filter checks to candidate row groups. */
|
|
2029
|
+
async function filterParquetRowGroupsWithBloomFilters(
|
|
2030
|
+
initialization: ParquetSourceInitialization,
|
|
2031
|
+
rowGroupIndices: readonly number[],
|
|
2032
|
+
predicate: ParquetPredicate,
|
|
2033
|
+
signal: AbortSignal
|
|
2034
|
+
): Promise<ParquetBloomFilterReadResult> {
|
|
2035
|
+
const rowGroups: number[] = [];
|
|
2036
|
+
let filtersRead = 0;
|
|
2037
|
+
let bytesRead = 0;
|
|
2038
|
+
for (const rowGroupIndex of rowGroupIndices) {
|
|
2039
|
+
const rowGroup = initialization.metadata.rowGroups[rowGroupIndex];
|
|
2040
|
+
const probes = getParquetBloomFilterProbes(predicate, rowGroup);
|
|
2041
|
+
let keepRowGroup = true;
|
|
2042
|
+
for (const probe of probes) {
|
|
2043
|
+
if (
|
|
2044
|
+
probe.column.bloomFilterOffset === undefined ||
|
|
2045
|
+
probe.column.bloomFilterByteLength === undefined
|
|
2046
|
+
) {
|
|
2047
|
+
continue;
|
|
2048
|
+
}
|
|
2049
|
+
let field: ParquetField;
|
|
2050
|
+
try {
|
|
2051
|
+
field = initialization.parquetSchema.findField([...probe.column.path]);
|
|
2052
|
+
} catch {
|
|
2053
|
+
throwIfAborted(signal);
|
|
2054
|
+
continue;
|
|
2055
|
+
}
|
|
2056
|
+
if (!field.primitiveType) continue;
|
|
2057
|
+
let filter: ReturnType<typeof decodeParquetSplitBlockBloomFilter>;
|
|
2058
|
+
try {
|
|
2059
|
+
const rawColumnOrdinal = initialization.fileMetadata.row_groups[
|
|
2060
|
+
rowGroupIndex
|
|
2061
|
+
].columns.findIndex(
|
|
2062
|
+
columnChunk =>
|
|
2063
|
+
JSON.stringify(columnChunk.meta_data?.path_in_schema) ===
|
|
2064
|
+
JSON.stringify(probe.column.path)
|
|
2065
|
+
);
|
|
2066
|
+
const rawColumnChunk =
|
|
2067
|
+
rawColumnOrdinal >= 0
|
|
2068
|
+
? initialization.fileMetadata.row_groups[rowGroupIndex].columns[rawColumnOrdinal]
|
|
2069
|
+
: undefined;
|
|
2070
|
+
const data = await initialization.file.read(
|
|
2071
|
+
probe.column.bloomFilterOffset,
|
|
2072
|
+
probe.column.bloomFilterByteLength,
|
|
2073
|
+
signal
|
|
2074
|
+
);
|
|
2075
|
+
const decodedBloomFilter = rawColumnChunk
|
|
2076
|
+
? await initialization.reader.decryptBloomFilter(
|
|
2077
|
+
toUint8Array(data),
|
|
2078
|
+
rowGroupIndex,
|
|
2079
|
+
rawColumnOrdinal,
|
|
2080
|
+
rawColumnChunk
|
|
2081
|
+
)
|
|
2082
|
+
: toUint8Array(data);
|
|
2083
|
+
filter = decodeParquetSplitBlockBloomFilter(decodedBloomFilter);
|
|
2084
|
+
filtersRead++;
|
|
2085
|
+
bytesRead += data.byteLength;
|
|
2086
|
+
} catch {
|
|
2087
|
+
throwIfAborted(signal);
|
|
2088
|
+
continue;
|
|
2089
|
+
}
|
|
2090
|
+
if (
|
|
2091
|
+
filter.algorithm !== 'BLOCK' ||
|
|
2092
|
+
filter.hash !== 'XXHASH' ||
|
|
2093
|
+
filter.compression !== 'UNCOMPRESSED'
|
|
2094
|
+
) {
|
|
2095
|
+
continue;
|
|
2096
|
+
}
|
|
2097
|
+
const mayContainValue = probe.values.some(value => {
|
|
2098
|
+
try {
|
|
2099
|
+
if (value instanceof Date) return true;
|
|
2100
|
+
const physicalValue = getParquetBloomFilterPhysicalValue(value, field);
|
|
2101
|
+
const encoded = encodeParquetBloomFilterValue(
|
|
2102
|
+
physicalValue,
|
|
2103
|
+
field.primitiveType as Parameters<typeof encodeParquetBloomFilterValue>[1],
|
|
2104
|
+
field.typeLength
|
|
2105
|
+
);
|
|
2106
|
+
return checkParquetSplitBlockBloomFilter(
|
|
2107
|
+
filter.bitset,
|
|
2108
|
+
hashParquetBloomFilterValue(encoded)
|
|
2109
|
+
);
|
|
2110
|
+
} catch {
|
|
2111
|
+
throwIfAborted(signal);
|
|
2112
|
+
return true;
|
|
2113
|
+
}
|
|
2114
|
+
});
|
|
2115
|
+
if (!mayContainValue) {
|
|
2116
|
+
keepRowGroup = false;
|
|
2117
|
+
break;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
if (keepRowGroup) rowGroups.push(rowGroupIndex);
|
|
2121
|
+
}
|
|
2122
|
+
return {rowGroupIndices: rowGroups, filtersRead, bytesRead};
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
/** Converts logical predicate values into the physical values stored in Bloom filters. */
|
|
2126
|
+
function getParquetBloomFilterPhysicalValue(
|
|
2127
|
+
value: unknown,
|
|
2128
|
+
field: ParquetField
|
|
2129
|
+
): boolean | number | bigint | string | Uint8Array {
|
|
2130
|
+
if (field.originalType?.startsWith('DECIMAL_')) {
|
|
2131
|
+
return toPrimitive(field.originalType as ParquetType, value, field) as
|
|
2132
|
+
| number
|
|
2133
|
+
| bigint
|
|
2134
|
+
| Uint8Array;
|
|
2135
|
+
}
|
|
2136
|
+
return value as boolean | number | bigint | string | Uint8Array;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
1262
2139
|
/** Returns true when at least one HTTP object validator was captured. */
|
|
1263
2140
|
function hasObjectVersion(version?: ParquetObjectVersion): version is ParquetObjectVersion {
|
|
1264
2141
|
return Boolean(version?.etag || version?.lastModified);
|