@loaders.gl/parquet 4.5.3 → 5.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +14 -3
- package/dist/bundled.d.ts +9 -0
- package/dist/bundled.d.ts.map +1 -0
- package/dist/bundled.js +10 -0
- package/dist/bundled.js.map +1 -0
- 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-with-parser.d.ts +33 -0
- package/dist/geoparquet-loader-with-parser.d.ts.map +1 -0
- package/dist/geoparquet-loader-with-parser.js +52 -0
- package/dist/geoparquet-loader-with-parser.js.map +1 -0
- package/dist/geoparquet-loader.d.ts +59 -0
- package/dist/geoparquet-loader.d.ts.map +1 -0
- package/dist/geoparquet-loader.js +30 -0
- package/dist/geoparquet-loader.js.map +1 -0
- 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 +41568 -6781
- package/dist/index.cjs.map +3 -4
- package/dist/index.d.ts +15 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -12
- package/dist/index.js.map +1 -1
- package/dist/lib/arrow/convert-columns-to-row-group.js +3 -0
- package/dist/lib/arrow/convert-columns-to-row-group.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 +164 -29
- package/dist/lib/arrow/convert-schema-from-parquet.js.map +1 -1
- package/dist/lib/arrow/convert-schema-to-parquet.d.ts.map +1 -1
- package/dist/lib/arrow/convert-schema-to-parquet.js +16 -7
- package/dist/lib/arrow/convert-schema-to-parquet.js.map +1 -1
- package/dist/lib/arrow/create-nested-arrow-vector.d.ts +8 -0
- package/dist/lib/arrow/create-nested-arrow-vector.d.ts.map +1 -0
- package/dist/lib/arrow/create-nested-arrow-vector.js +485 -0
- package/dist/lib/arrow/create-nested-arrow-vector.js.map +1 -0
- package/dist/lib/constants.d.ts +0 -1
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +1 -2
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/encoders/encode-arrow-to-parquet.d.ts +2 -2
- package/dist/lib/encoders/encode-arrow-to-parquet.d.ts.map +1 -1
- package/dist/lib/encoders/encode-arrow-to-parquet.js +3 -10
- package/dist/lib/encoders/encode-arrow-to-parquet.js.map +1 -1
- package/dist/lib/encoders/encode-table-to-parquet-js.d.ts +5 -0
- package/dist/lib/encoders/encode-table-to-parquet-js.d.ts.map +1 -0
- package/dist/lib/encoders/encode-table-to-parquet-js.js +394 -0
- package/dist/lib/encoders/encode-table-to-parquet-js.js.map +1 -0
- 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 +67 -0
- package/dist/lib/geo/geospatial-metadata.d.ts.map +1 -0
- package/dist/lib/geo/geospatial-metadata.js +412 -0
- package/dist/lib/geo/geospatial-metadata.js.map +1 -0
- 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 +91 -0
- package/dist/lib/parquet-page-index.d.ts.map +1 -0
- package/dist/lib/parquet-page-index.js +463 -0
- package/dist/lib/parquet-page-index.js.map +1 -0
- package/dist/lib/parquet-predicate.d.ts +21 -0
- package/dist/lib/parquet-predicate.d.ts.map +1 -0
- package/dist/lib/parquet-predicate.js +328 -0
- package/dist/lib/parquet-predicate.js.map +1 -0
- package/dist/lib/parquet-source-worker-client.d.ts +6 -0
- package/dist/lib/parquet-source-worker-client.d.ts.map +1 -0
- package/dist/lib/parquet-source-worker-client.js +24 -0
- package/dist/lib/parquet-source-worker-client.js.map +1 -0
- package/dist/lib/parquet-source-worker-decoder.d.ts +7 -0
- package/dist/lib/parquet-source-worker-decoder.d.ts.map +1 -0
- package/dist/lib/parquet-source-worker-decoder.js +181 -0
- package/dist/lib/parquet-source-worker-decoder.js.map +1 -0
- package/dist/lib/parquet-source-worker-file.d.ts +24 -0
- package/dist/lib/parquet-source-worker-file.d.ts.map +1 -0
- package/dist/lib/parquet-source-worker-file.js +40 -0
- package/dist/lib/parquet-source-worker-file.js.map +1 -0
- package/dist/lib/parquet-source-worker-types.d.ts +121 -0
- package/dist/lib/parquet-source-worker-types.d.ts.map +1 -0
- package/dist/lib/parquet-source-worker-types.js +5 -0
- package/dist/lib/parquet-source-worker-types.js.map +1 -0
- package/dist/lib/parquet-worker-transport.d.ts +8 -0
- package/dist/lib/parquet-worker-transport.d.ts.map +1 -0
- package/dist/lib/parquet-worker-transport.js +40 -0
- package/dist/lib/parquet-worker-transport.js.map +1 -0
- package/dist/lib/parsers/convert-parquet-tables.d.ts +16 -0
- package/dist/lib/parsers/convert-parquet-tables.d.ts.map +1 -0
- package/dist/lib/parsers/convert-parquet-tables.js +30 -0
- package/dist/lib/parsers/convert-parquet-tables.js.map +1 -0
- package/dist/lib/parsers/get-parquet-schema.d.ts.map +1 -1
- package/dist/lib/parsers/get-parquet-schema.js +6 -2
- package/dist/lib/parsers/get-parquet-schema.js.map +1 -1
- package/dist/lib/parsers/parse-geoparquet-to-geojson.d.ts +4 -4
- package/dist/lib/parsers/parse-geoparquet-to-geojson.d.ts.map +1 -1
- package/dist/lib/parsers/parse-geoparquet-to-geojson.js +19 -6
- package/dist/lib/parsers/parse-geoparquet-to-geojson.js.map +1 -1
- package/dist/lib/parsers/parse-parquet-tables.d.ts +37 -0
- package/dist/lib/parsers/parse-parquet-tables.d.ts.map +1 -0
- package/dist/lib/parsers/parse-parquet-tables.js +50 -0
- package/dist/lib/parsers/parse-parquet-tables.js.map +1 -0
- package/dist/lib/parsers/parse-parquet-to-arrow-js.d.ts +19 -0
- package/dist/lib/parsers/parse-parquet-to-arrow-js.d.ts.map +1 -0
- package/dist/lib/parsers/parse-parquet-to-arrow-js.js +921 -0
- package/dist/lib/parsers/parse-parquet-to-arrow-js.js.map +1 -0
- package/dist/lib/parsers/parse-parquet-to-arrow.d.ts +7 -7
- package/dist/lib/parsers/parse-parquet-to-arrow.d.ts.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-arrow.js +45 -32
- package/dist/lib/parsers/parse-parquet-to-arrow.js.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-columns.d.ts +3 -3
- package/dist/lib/parsers/parse-parquet-to-columns.d.ts.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-columns.js +4 -5
- package/dist/lib/parsers/parse-parquet-to-columns.js.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-json.d.ts +3 -3
- package/dist/lib/parsers/parse-parquet-to-json.d.ts.map +1 -1
- package/dist/lib/parsers/parse-parquet-to-json.js +95 -28
- 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 +80 -0
- package/dist/lib/sources/parquet-range-file.d.ts.map +1 -0
- package/dist/lib/sources/parquet-range-file.js +267 -0
- package/dist/lib/sources/parquet-range-file.js.map +1 -0
- package/dist/lib/sources/single-batch-queue.d.ts +24 -0
- package/dist/lib/sources/single-batch-queue.d.ts.map +1 -0
- package/dist/lib/sources/single-batch-queue.js +91 -0
- package/dist/lib/sources/single-batch-queue.js.map +1 -0
- package/dist/lib/utils/load-wasm-node.d.ts +3 -0
- package/dist/lib/utils/load-wasm-node.d.ts.map +1 -0
- package/dist/lib/utils/load-wasm-node.js +23 -0
- package/dist/lib/utils/load-wasm-node.js.map +1 -0
- package/dist/lib/utils/load-wasm.d.ts +2 -2
- package/dist/lib/utils/load-wasm.d.ts.map +1 -1
- package/dist/lib/utils/load-wasm.js +28 -12
- package/dist/lib/utils/load-wasm.js.map +1 -1
- package/dist/lib/utils/make-stream-iterator.d.ts +2 -0
- package/dist/lib/utils/make-stream-iterator.d.ts.map +1 -1
- package/dist/lib/utils/make-stream-iterator.js +123 -14
- package/dist/lib/utils/make-stream-iterator.js.map +1 -1
- package/dist/lib/utils/normalize-parquet-options.d.ts +5 -0
- package/dist/lib/utils/normalize-parquet-options.d.ts.map +1 -0
- package/dist/lib/utils/normalize-parquet-options.js +25 -0
- package/dist/lib/utils/normalize-parquet-options.js.map +1 -0
- package/dist/parquet-dataset-source.cjs +38974 -0
- package/dist/parquet-dataset-source.cjs.map +6 -0
- package/dist/parquet-dataset-source.d.ts +81 -0
- package/dist/parquet-dataset-source.d.ts.map +1 -0
- package/dist/parquet-dataset-source.js +602 -0
- package/dist/parquet-dataset-source.js.map +1 -0
- package/dist/parquet-encoded-page-types.d.ts +108 -0
- package/dist/parquet-encoded-page-types.d.ts.map +1 -0
- package/dist/parquet-encoded-page-types.js +5 -0
- package/dist/parquet-encoded-page-types.js.map +1 -0
- package/dist/parquet-format.d.ts +2 -0
- package/dist/parquet-format.d.ts.map +1 -1
- package/dist/parquet-format.js +2 -0
- package/dist/parquet-format.js.map +1 -1
- package/dist/parquet-js-loader-types.d.ts +59 -0
- package/dist/parquet-js-loader-types.d.ts.map +1 -0
- package/dist/parquet-js-loader-types.js +34 -0
- package/dist/parquet-js-loader-types.js.map +1 -0
- package/dist/parquet-js-loader.d.ts +18 -22
- package/dist/parquet-js-loader.d.ts.map +1 -1
- package/dist/parquet-js-loader.js +53 -14
- package/dist/parquet-js-loader.js.map +1 -1
- package/dist/parquet-js-writer.d.ts +65 -0
- package/dist/parquet-js-writer.d.ts.map +1 -0
- package/dist/parquet-js-writer.js +28 -0
- package/dist/parquet-js-writer.js.map +1 -0
- package/dist/parquet-loader-base.d.ts +25 -0
- package/dist/parquet-loader-base.d.ts.map +1 -0
- package/dist/parquet-loader-base.js +23 -0
- package/dist/parquet-loader-base.js.map +1 -0
- package/dist/parquet-loader-options.d.ts +55 -0
- package/dist/parquet-loader-options.d.ts.map +1 -0
- package/dist/parquet-loader-options.js +10 -0
- package/dist/parquet-loader-options.js.map +1 -0
- package/dist/parquet-loader-types.d.ts +61 -0
- package/dist/parquet-loader-types.d.ts.map +1 -0
- package/dist/parquet-loader-types.js +17 -0
- package/dist/parquet-loader-types.js.map +1 -0
- package/dist/parquet-loader.d.ts +42 -0
- package/dist/parquet-loader.d.ts.map +1 -0
- package/dist/parquet-loader.js +68 -0
- package/dist/parquet-loader.js.map +1 -0
- package/dist/parquet-source-capabilities.d.ts +43 -0
- package/dist/parquet-source-capabilities.d.ts.map +1 -0
- package/dist/parquet-source-capabilities.js +32 -0
- package/dist/parquet-source-capabilities.js.map +1 -0
- package/dist/parquet-source-loader-types.d.ts +82 -0
- package/dist/parquet-source-loader-types.d.ts.map +1 -0
- package/dist/parquet-source-loader-types.js +84 -0
- package/dist/parquet-source-loader-types.js.map +1 -0
- package/dist/parquet-source-loader.cjs +38261 -0
- package/dist/parquet-source-loader.cjs.map +6 -0
- package/dist/parquet-source-loader.d.ts +167 -0
- package/dist/parquet-source-loader.d.ts.map +1 -0
- package/dist/parquet-source-loader.js +1438 -0
- package/dist/parquet-source-loader.js.map +1 -0
- package/dist/parquet-source-types.d.ts +534 -0
- package/dist/parquet-source-types.d.ts.map +1 -0
- package/dist/parquet-source-types.js +5 -0
- package/dist/parquet-source-types.js.map +1 -0
- package/dist/parquet-source-worker-factory.d.ts +4 -0
- package/dist/parquet-source-worker-factory.d.ts.map +1 -0
- package/dist/parquet-source-worker-factory.js +6 -0
- package/dist/parquet-source-worker-factory.js.map +1 -0
- package/dist/parquet-source-worker-url.d.ts +3 -0
- package/dist/parquet-source-worker-url.d.ts.map +1 -0
- package/dist/parquet-source-worker-url.js +22 -0
- package/dist/parquet-source-worker-url.js.map +1 -0
- package/dist/parquet-source-worker.js +38871 -0
- package/dist/parquet-source-worker.js.map +7 -0
- package/dist/parquet-worker-node.cjs +78507 -0
- package/dist/parquet-worker-node.cjs.map +7 -0
- package/dist/parquet-worker-url.d.ts +3 -0
- package/dist/parquet-worker-url.d.ts.map +1 -0
- package/dist/parquet-worker-url.js +19 -0
- package/dist/parquet-worker-url.js.map +1 -0
- package/dist/parquet-worker.js +20094 -0
- package/dist/parquet-worker.js.map +7 -0
- package/dist/parquet-writer.d.ts +10 -0
- package/dist/parquet-writer.d.ts.map +1 -0
- package/dist/parquet-writer.js +27 -0
- package/dist/parquet-writer.js.map +1 -0
- package/dist/parquet_wasm_bg.wasm +0 -0
- package/dist/parquetjs/codecs/byte-stream-split.d.ts +9 -0
- package/dist/parquetjs/codecs/byte-stream-split.d.ts.map +1 -0
- package/dist/parquetjs/codecs/byte-stream-split.js +159 -0
- package/dist/parquetjs/codecs/byte-stream-split.js.map +1 -0
- package/dist/parquetjs/codecs/declare.d.ts +35 -3
- package/dist/parquetjs/codecs/declare.d.ts.map +1 -1
- package/dist/parquetjs/codecs/declare.js +17 -1
- package/dist/parquetjs/codecs/declare.js.map +1 -1
- package/dist/parquetjs/codecs/delta.d.ts +17 -0
- package/dist/parquetjs/codecs/delta.d.ts.map +1 -0
- package/dist/parquetjs/codecs/delta.js +788 -0
- package/dist/parquetjs/codecs/delta.js.map +1 -0
- package/dist/parquetjs/codecs/dictionary.d.ts +5 -2
- package/dist/parquetjs/codecs/dictionary.d.ts.map +1 -1
- package/dist/parquetjs/codecs/dictionary.js +16 -4
- package/dist/parquetjs/codecs/dictionary.js.map +1 -1
- package/dist/parquetjs/codecs/index.d.ts.map +1 -1
- package/dist/parquetjs/codecs/index.js +22 -0
- package/dist/parquetjs/codecs/index.js.map +1 -1
- package/dist/parquetjs/codecs/plain.d.ts +3 -3
- package/dist/parquetjs/codecs/plain.d.ts.map +1 -1
- package/dist/parquetjs/codecs/plain.js +231 -78
- package/dist/parquetjs/codecs/plain.js.map +1 -1
- package/dist/parquetjs/codecs/rle.d.ts +7 -3
- package/dist/parquetjs/codecs/rle.d.ts.map +1 -1
- package/dist/parquetjs/codecs/rle.js +613 -69
- package/dist/parquetjs/codecs/rle.js.map +1 -1
- package/dist/parquetjs/compression.d.ts +16 -8
- package/dist/parquetjs/compression.d.ts.map +1 -1
- package/dist/parquetjs/compression.js +114 -141
- package/dist/parquetjs/compression.js.map +1 -1
- package/dist/parquetjs/encoder/dictionary-planner.d.ts +17 -0
- package/dist/parquetjs/encoder/dictionary-planner.d.ts.map +1 -0
- package/dist/parquetjs/encoder/dictionary-planner.js +67 -0
- package/dist/parquetjs/encoder/dictionary-planner.js.map +1 -0
- package/dist/parquetjs/encoder/page-planner.d.ts +11 -0
- package/dist/parquetjs/encoder/page-planner.d.ts.map +1 -0
- package/dist/parquetjs/encoder/page-planner.js +60 -0
- package/dist/parquetjs/encoder/page-planner.js.map +1 -0
- package/dist/parquetjs/encoder/parquet-encoder.d.ts +54 -5
- package/dist/parquetjs/encoder/parquet-encoder.d.ts.map +1 -1
- package/dist/parquetjs/encoder/parquet-encoder.js +930 -66
- 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/BsonType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/BsonType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/BsonType.js +1 -1
- package/dist/parquetjs/parquet-thrift/BsonType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts +7 -2
- package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnChunk.js +37 -2
- 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/ColumnIndex.d.ts +6 -6
- package/dist/parquetjs/parquet-thrift/ColumnIndex.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnIndex.js +3 -3
- package/dist/parquetjs/parquet-thrift/ColumnIndex.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts +12 -2
- package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnMetaData.js +75 -2
- package/dist/parquetjs/parquet-thrift/ColumnMetaData.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnOrder.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnOrder.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnOrder.js +1 -1
- package/dist/parquetjs/parquet-thrift/ColumnOrder.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/DataPageHeader.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/DataPageHeader.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/DataPageHeader.js +1 -1
- package/dist/parquetjs/parquet-thrift/DataPageHeader.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.js +1 -1
- package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/DateType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/DateType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/DateType.js +1 -1
- package/dist/parquetjs/parquet-thrift/DateType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/DecimalType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/DecimalType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/DecimalType.js +1 -1
- package/dist/parquetjs/parquet-thrift/DecimalType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.js +1 -1
- package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.d.ts +8 -0
- package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.js +14 -0
- package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/Encoding.d.ts +2 -1
- package/dist/parquetjs/parquet-thrift/Encoding.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/Encoding.js +1 -0
- package/dist/parquetjs/parquet-thrift/Encoding.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/EnumType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/EnumType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/EnumType.js +1 -1
- package/dist/parquetjs/parquet-thrift/EnumType.js.map +1 -1
- 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 +7 -2
- package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/FileMetaData.js +37 -2
- package/dist/parquetjs/parquet-thrift/FileMetaData.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/Float16Type.d.ts +9 -0
- package/dist/parquetjs/parquet-thrift/Float16Type.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/Float16Type.js +27 -0
- package/dist/parquetjs/parquet-thrift/Float16Type.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/GeographyType.d.ts +14 -0
- package/dist/parquetjs/parquet-thrift/GeographyType.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/GeographyType.js +51 -0
- package/dist/parquetjs/parquet-thrift/GeographyType.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/GeometryType.d.ts +11 -0
- package/dist/parquetjs/parquet-thrift/GeometryType.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/GeometryType.js +41 -0
- package/dist/parquetjs/parquet-thrift/GeometryType.js.map +1 -0
- 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/IndexPageHeader.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/IndexPageHeader.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/IndexPageHeader.js +1 -1
- package/dist/parquetjs/parquet-thrift/IndexPageHeader.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/IntType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/IntType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/IntType.js +1 -1
- package/dist/parquetjs/parquet-thrift/IntType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/JsonType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/JsonType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/JsonType.js +1 -1
- package/dist/parquetjs/parquet-thrift/JsonType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/KeyValue.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/KeyValue.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/KeyValue.js +1 -1
- package/dist/parquetjs/parquet-thrift/KeyValue.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/ListType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/ListType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/ListType.js +1 -1
- package/dist/parquetjs/parquet-thrift/ListType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/LogicalType.d.ts +17 -1
- package/dist/parquetjs/parquet-thrift/LogicalType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/LogicalType.js +97 -1
- package/dist/parquetjs/parquet-thrift/LogicalType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/MapType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/MapType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/MapType.js +1 -1
- package/dist/parquetjs/parquet-thrift/MapType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/MicroSeconds.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/MicroSeconds.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/MicroSeconds.js +1 -1
- package/dist/parquetjs/parquet-thrift/MicroSeconds.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/MilliSeconds.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/MilliSeconds.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/MilliSeconds.js +1 -1
- package/dist/parquetjs/parquet-thrift/MilliSeconds.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/NanoSeconds.d.ts +9 -0
- package/dist/parquetjs/parquet-thrift/NanoSeconds.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/NanoSeconds.js +27 -0
- package/dist/parquetjs/parquet-thrift/NanoSeconds.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/NullType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/NullType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/NullType.js +1 -1
- package/dist/parquetjs/parquet-thrift/NullType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/OffsetIndex.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/OffsetIndex.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/OffsetIndex.js +1 -1
- package/dist/parquetjs/parquet-thrift/OffsetIndex.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/PageEncodingStats.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/PageEncodingStats.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/PageEncodingStats.js +1 -1
- package/dist/parquetjs/parquet-thrift/PageEncodingStats.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/PageHeader.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/PageHeader.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/PageHeader.js +1 -1
- package/dist/parquetjs/parquet-thrift/PageHeader.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/PageLocation.d.ts +2 -2
- package/dist/parquetjs/parquet-thrift/PageLocation.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/PageLocation.js +2 -2
- package/dist/parquetjs/parquet-thrift/PageLocation.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/RowGroup.d.ts +2 -2
- package/dist/parquetjs/parquet-thrift/RowGroup.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/RowGroup.js +2 -2
- package/dist/parquetjs/parquet-thrift/RowGroup.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/SchemaElement.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/SchemaElement.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/SchemaElement.js +1 -1
- package/dist/parquetjs/parquet-thrift/SchemaElement.js.map +1 -1
- 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/SortingColumn.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/SortingColumn.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/SortingColumn.js +1 -1
- package/dist/parquetjs/parquet-thrift/SortingColumn.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/Statistics.d.ts +14 -10
- package/dist/parquetjs/parquet-thrift/Statistics.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/Statistics.js +38 -2
- package/dist/parquetjs/parquet-thrift/Statistics.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/StringType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/StringType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/StringType.js +1 -1
- package/dist/parquetjs/parquet-thrift/StringType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/TimeType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/TimeType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/TimeType.js +1 -1
- package/dist/parquetjs/parquet-thrift/TimeType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/TimeUnit.d.ts +5 -1
- package/dist/parquetjs/parquet-thrift/TimeUnit.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/TimeUnit.js +25 -1
- package/dist/parquetjs/parquet-thrift/TimeUnit.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/TimestampType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/TimestampType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/TimestampType.js +1 -1
- package/dist/parquetjs/parquet-thrift/TimestampType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.js +1 -1
- package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/UUIDType.d.ts +1 -1
- package/dist/parquetjs/parquet-thrift/UUIDType.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/UUIDType.js +1 -1
- package/dist/parquetjs/parquet-thrift/UUIDType.js.map +1 -1
- package/dist/parquetjs/parquet-thrift/VariantType.d.ts +11 -0
- package/dist/parquetjs/parquet-thrift/VariantType.d.ts.map +1 -0
- package/dist/parquetjs/parquet-thrift/VariantType.js +41 -0
- package/dist/parquetjs/parquet-thrift/VariantType.js.map +1 -0
- package/dist/parquetjs/parquet-thrift/index.d.ts +12 -2
- package/dist/parquetjs/parquet-thrift/index.d.ts.map +1 -1
- package/dist/parquetjs/parquet-thrift/index.js +12 -2
- package/dist/parquetjs/parquet-thrift/index.js.map +1 -1
- package/dist/parquetjs/parser/decoders.d.ts +23 -3
- package/dist/parquetjs/parser/decoders.d.ts.map +1 -1
- package/dist/parquetjs/parser/decoders.js +687 -119
- package/dist/parquetjs/parser/decoders.js.map +1 -1
- package/dist/parquetjs/parser/encoded-pages.d.ts +24 -0
- package/dist/parquetjs/parser/encoded-pages.d.ts.map +1 -0
- package/dist/parquetjs/parser/encoded-pages.js +206 -0
- package/dist/parquetjs/parser/encoded-pages.js.map +1 -0
- package/dist/parquetjs/parser/parquet-reader.d.ts +111 -10
- package/dist/parquetjs/parser/parquet-reader.d.ts.map +1 -1
- package/dist/parquetjs/parser/parquet-reader.js +836 -69
- package/dist/parquetjs/parser/parquet-reader.js.map +1 -1
- package/dist/parquetjs/schema/declare.d.ts +83 -10
- 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 +3 -0
- package/dist/parquetjs/schema/schema.d.ts.map +1 -1
- package/dist/parquetjs/schema/schema.js +31 -3
- package/dist/parquetjs/schema/schema.js.map +1 -1
- package/dist/parquetjs/schema/shred.d.ts +2 -0
- package/dist/parquetjs/schema/shred.d.ts.map +1 -1
- package/dist/parquetjs/schema/shred.js +285 -29
- package/dist/parquetjs/schema/shred.js.map +1 -1
- package/dist/parquetjs/schema/types.d.ts.map +1 -1
- package/dist/parquetjs/schema/types.js +290 -71
- 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/binary-utils.d.ts +41 -0
- package/dist/parquetjs/utils/binary-utils.d.ts.map +1 -0
- package/dist/parquetjs/utils/binary-utils.js +106 -0
- package/dist/parquetjs/utils/binary-utils.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/file-utils.d.ts +1 -1
- package/dist/parquetjs/utils/file-utils.d.ts.map +1 -1
- package/dist/parquetjs/utils/file-utils.js +3 -3
- package/dist/parquetjs/utils/file-utils.js.map +1 -1
- package/dist/parquetjs/utils/read-utils.d.ts +25 -7
- package/dist/parquetjs/utils/read-utils.d.ts.map +1 -1
- package/dist/parquetjs/utils/read-utils.js +66 -39
- package/dist/parquetjs/utils/read-utils.js.map +1 -1
- package/dist/parquetjs/utils/thrift-runtime.d.ts +106 -0
- package/dist/parquetjs/utils/thrift-runtime.d.ts.map +1 -0
- package/dist/parquetjs/utils/thrift-runtime.js +50 -0
- package/dist/parquetjs/utils/thrift-runtime.js.map +1 -0
- package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.d.ts +78 -0
- package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.d.ts.map +1 -0
- package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.js +213 -0
- package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.js.map +1 -0
- package/dist/parquetjs/utils/uint8-array-compact-protocol.d.ts +80 -0
- package/dist/parquetjs/utils/uint8-array-compact-protocol.d.ts.map +1 -0
- package/dist/parquetjs/utils/uint8-array-compact-protocol.js +276 -0
- package/dist/parquetjs/utils/uint8-array-compact-protocol.js.map +1 -0
- package/dist/parquetjs/utils/uint8-array-transport.d.ts +25 -0
- package/dist/parquetjs/utils/uint8-array-transport.d.ts.map +1 -0
- package/dist/parquetjs/utils/uint8-array-transport.js +62 -0
- package/dist/parquetjs/utils/uint8-array-transport.js.map +1 -0
- package/dist/polyfills/process.d.ts +12 -0
- package/dist/polyfills/process.d.ts.map +1 -0
- package/dist/polyfills/process.js +17 -0
- package/dist/polyfills/process.js.map +1 -0
- package/dist/unbundled.d.ts +10 -0
- package/dist/unbundled.d.ts.map +1 -0
- package/dist/unbundled.js +11 -0
- package/dist/unbundled.js.map +1 -0
- package/dist/wasm.d.ts +3 -0
- package/dist/wasm.d.ts.map +1 -0
- package/dist/wasm.js +6 -0
- package/dist/wasm.js.map +1 -0
- package/dist/workers/parquet-source-worker.d.ts +2 -0
- package/dist/workers/parquet-source-worker.d.ts.map +1 -0
- package/dist/workers/parquet-source-worker.js +17 -0
- package/dist/workers/parquet-source-worker.js.map +1 -0
- package/dist/workers/parquet-worker-node.d.ts +2 -0
- package/dist/workers/parquet-worker-node.d.ts.map +1 -0
- package/dist/workers/parquet-worker-node.js +5 -0
- package/dist/workers/parquet-worker-node.js.map +1 -0
- package/dist/workers/parquet-worker.d.ts +2 -0
- package/dist/workers/parquet-worker.d.ts.map +1 -0
- package/dist/workers/parquet-worker.js +27 -0
- package/dist/workers/parquet-worker.js.map +1 -0
- package/package.json +84 -40
- package/src/bundled.ts +43 -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/geoparquet-loader-with-parser.ts +95 -0
- package/src/geoparquet-loader.ts +54 -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 +105 -26
- package/src/lib/arrow/convert-columns-to-row-group.ts +3 -0
- package/src/lib/arrow/convert-schema-from-parquet.ts +193 -32
- package/src/lib/arrow/convert-schema-to-parquet.ts +16 -7
- package/src/lib/arrow/create-nested-arrow-vector.ts +705 -0
- package/src/lib/constants.ts +0 -2
- package/src/lib/encoders/encode-arrow-to-parquet.ts +10 -16
- package/src/lib/encoders/encode-table-to-parquet-js.ts +526 -0
- package/src/lib/geo/geoparquet-covering.ts +201 -0
- package/src/lib/geo/geospatial-metadata.ts +547 -0
- 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 +727 -0
- package/src/lib/parquet-predicate.ts +410 -0
- package/src/lib/parquet-source-worker-client.ts +41 -0
- package/src/lib/parquet-source-worker-decoder.ts +252 -0
- package/src/lib/parquet-source-worker-file.ts +56 -0
- package/src/lib/parquet-source-worker-types.ts +132 -0
- package/src/lib/parquet-worker-transport.ts +57 -0
- package/src/lib/parsers/convert-parquet-tables.ts +39 -0
- package/src/lib/parsers/get-parquet-schema.ts +6 -2
- package/src/lib/parsers/parse-geoparquet-to-geojson.ts +27 -14
- package/src/lib/parsers/parse-parquet-tables.ts +82 -0
- package/src/lib/parsers/parse-parquet-to-arrow-js.ts +1213 -0
- package/src/lib/parsers/parse-parquet-to-arrow.ts +62 -36
- package/src/lib/parsers/parse-parquet-to-columns.ts +9 -8
- package/src/lib/parsers/parse-parquet-to-json.ts +119 -35
- package/src/lib/slice-parquet-batch.ts +32 -0
- package/src/lib/sources/parquet-range-file.ts +365 -0
- package/src/lib/sources/single-batch-queue.ts +95 -0
- package/src/lib/utils/load-wasm-node.ts +25 -0
- package/src/lib/utils/load-wasm.ts +36 -13
- package/src/lib/utils/make-stream-iterator.ts +126 -14
- package/src/lib/utils/normalize-parquet-options.ts +30 -0
- package/src/parquet-dataset-source.ts +800 -0
- package/src/parquet-encoded-page-types.ts +122 -0
- package/src/parquet-format.ts +2 -0
- package/src/parquet-js-loader-types.ts +53 -0
- package/src/parquet-js-loader.ts +76 -19
- package/src/parquet-js-writer.ts +98 -0
- package/src/parquet-loader-base.ts +34 -0
- package/src/parquet-loader-options.ts +62 -0
- package/src/parquet-loader-types.ts +35 -0
- package/src/parquet-loader.ts +115 -0
- package/src/parquet-source-capabilities.ts +73 -0
- package/src/parquet-source-loader-types.ts +97 -0
- package/src/parquet-source-loader.ts +2254 -0
- package/src/parquet-source-types.ts +627 -0
- package/src/parquet-source-worker-factory.ts +8 -0
- package/src/parquet-source-worker-url.ts +23 -0
- package/src/parquet-worker-url.ts +23 -0
- package/src/parquet-writer.ts +42 -0
- package/src/parquetjs/LICENSE +23 -1
- package/src/parquetjs/codecs/byte-stream-split.ts +221 -0
- package/src/parquetjs/codecs/declare.ts +62 -3
- package/src/parquetjs/codecs/delta.ts +1047 -0
- package/src/parquetjs/codecs/dictionary.ts +22 -4
- package/src/parquetjs/codecs/index.ts +22 -0
- package/src/parquetjs/codecs/plain.ts +317 -89
- package/src/parquetjs/codecs/rle.ts +852 -82
- package/src/parquetjs/compression.ts +137 -156
- package/src/parquetjs/encoder/dictionary-planner.ts +104 -0
- package/src/parquetjs/encoder/page-planner.ts +83 -0
- package/src/parquetjs/encoder/parquet-encoder.ts +1374 -95
- package/src/parquetjs/parquet-thrift/BoundingBox.ts +108 -0
- package/src/parquetjs/parquet-thrift/BsonType.ts +1 -1
- package/src/parquetjs/parquet-thrift/ColumnChunk.ts +37 -2
- package/src/parquetjs/parquet-thrift/ColumnCryptoMetaData.ts +139 -0
- package/src/parquetjs/parquet-thrift/ColumnIndex.ts +13 -13
- package/src/parquetjs/parquet-thrift/ColumnMetaData.ts +75 -2
- package/src/parquetjs/parquet-thrift/ColumnOrder.ts +1 -1
- package/src/parquetjs/parquet-thrift/DataPageHeader.ts +1 -1
- package/src/parquetjs/parquet-thrift/DataPageHeaderV2.ts +1 -1
- package/src/parquetjs/parquet-thrift/DateType.ts +1 -1
- package/src/parquetjs/parquet-thrift/DecimalType.ts +1 -1
- package/src/parquetjs/parquet-thrift/DictionaryPageHeader.ts +1 -1
- package/src/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.ts +13 -0
- package/src/parquetjs/parquet-thrift/Encoding.ts +2 -1
- package/src/parquetjs/parquet-thrift/EncryptionAlgorithm.ts +127 -0
- package/src/parquetjs/parquet-thrift/EnumType.ts +1 -1
- package/src/parquetjs/parquet-thrift/FileCryptoMetaData.ts +60 -0
- package/src/parquetjs/parquet-thrift/FileMetaData.ts +37 -2
- package/src/parquetjs/parquet-thrift/Float16Type.ts +28 -0
- package/src/parquetjs/parquet-thrift/GeographyType.ts +54 -0
- package/src/parquetjs/parquet-thrift/GeometryType.ts +43 -0
- package/src/parquetjs/parquet-thrift/GeospatialStatistics.ts +68 -0
- package/src/parquetjs/parquet-thrift/IndexPageHeader.ts +1 -1
- package/src/parquetjs/parquet-thrift/IntType.ts +1 -1
- package/src/parquetjs/parquet-thrift/JsonType.ts +1 -1
- package/src/parquetjs/parquet-thrift/KeyValue.ts +1 -1
- package/src/parquetjs/parquet-thrift/ListType.ts +1 -1
- package/src/parquetjs/parquet-thrift/LogicalType.ts +97 -1
- package/src/parquetjs/parquet-thrift/MapType.ts +1 -1
- package/src/parquetjs/parquet-thrift/MicroSeconds.ts +1 -1
- package/src/parquetjs/parquet-thrift/MilliSeconds.ts +1 -1
- package/src/parquetjs/parquet-thrift/NanoSeconds.ts +28 -0
- package/src/parquetjs/parquet-thrift/NullType.ts +1 -1
- package/src/parquetjs/parquet-thrift/OffsetIndex.ts +1 -1
- package/src/parquetjs/parquet-thrift/PageEncodingStats.ts +1 -1
- package/src/parquetjs/parquet-thrift/PageHeader.ts +1 -1
- package/src/parquetjs/parquet-thrift/PageLocation.ts +2 -2
- package/src/parquetjs/parquet-thrift/RowGroup.ts +2 -2
- package/src/parquetjs/parquet-thrift/SchemaElement.ts +1 -1
- package/src/parquetjs/parquet-thrift/SizeStatistics.ts +98 -0
- package/src/parquetjs/parquet-thrift/SortingColumn.ts +1 -1
- package/src/parquetjs/parquet-thrift/Statistics.ts +50 -14
- package/src/parquetjs/parquet-thrift/StringType.ts +1 -1
- package/src/parquetjs/parquet-thrift/TimeType.ts +1 -1
- package/src/parquetjs/parquet-thrift/TimeUnit.ts +25 -1
- package/src/parquetjs/parquet-thrift/TimestampType.ts +1 -1
- package/src/parquetjs/parquet-thrift/TypeDefinedOrder.ts +1 -1
- package/src/parquetjs/parquet-thrift/UUIDType.ts +1 -1
- package/src/parquetjs/parquet-thrift/VariantType.ts +43 -0
- package/src/parquetjs/parquet-thrift/index.ts +12 -4
- package/src/parquetjs/parser/decoders.ts +922 -143
- package/src/parquetjs/parser/encoded-pages.ts +278 -0
- package/src/parquetjs/parser/parquet-reader.ts +1334 -83
- package/src/parquetjs/schema/declare.ts +121 -9
- package/src/parquetjs/schema/schema.ts +33 -3
- package/src/parquetjs/schema/shred.ts +345 -41
- package/src/parquetjs/schema/types.ts +308 -84
- package/src/parquetjs/schema/variant.ts +313 -0
- package/src/parquetjs/utils/binary-utils.ts +132 -0
- package/src/parquetjs/utils/crc32.ts +15 -0
- package/src/parquetjs/utils/file-utils.ts +4 -4
- package/src/parquetjs/utils/read-utils.ts +77 -51
- package/src/parquetjs/utils/thrift-runtime.ts +109 -0
- package/src/parquetjs/utils/uint8-array-compact-protocol-writer.ts +254 -0
- package/src/parquetjs/utils/uint8-array-compact-protocol.ts +322 -0
- package/src/parquetjs/utils/uint8-array-transport.ts +79 -0
- package/src/polyfills/process.ts +17 -0
- package/src/unbundled.ts +47 -0
- package/src/wasm.ts +6 -0
- package/src/workers/parquet-source-worker.ts +21 -0
- package/src/workers/parquet-worker-node.ts +5 -0
- package/src/workers/parquet-worker.ts +31 -0
- package/dist/parquet-arrow-loader.d.ts +0 -43
- package/dist/parquet-arrow-loader.d.ts.map +0 -1
- package/dist/parquet-arrow-loader.js +0 -49
- package/dist/parquet-arrow-loader.js.map +0 -1
- package/dist/parquet-arrow-writer.d.ts +0 -9
- package/dist/parquet-arrow-writer.d.ts.map +0 -1
- package/dist/parquet-arrow-writer.js +0 -23
- package/dist/parquet-arrow-writer.js.map +0 -1
- package/dist/parquet-json-loader.d.ts +0 -148
- package/dist/parquet-json-loader.d.ts.map +0 -1
- package/dist/parquet-json-loader.js +0 -106
- package/dist/parquet-json-loader.js.map +0 -1
- package/dist/parquet-json-writer.d.ts +0 -19
- package/dist/parquet-json-writer.d.ts.map +0 -1
- package/dist/parquet-json-writer.js +0 -20
- package/dist/parquet-json-writer.js.map +0 -1
- package/dist/polyfills/buffer/buffer-polyfill.browser.d.ts +0 -3
- package/dist/polyfills/buffer/buffer-polyfill.browser.d.ts.map +0 -1
- package/dist/polyfills/buffer/buffer-polyfill.browser.js +0 -12
- package/dist/polyfills/buffer/buffer-polyfill.browser.js.map +0 -1
- package/dist/polyfills/buffer/buffer-polyfill.node.d.ts +0 -3
- package/dist/polyfills/buffer/buffer-polyfill.node.d.ts.map +0 -1
- package/dist/polyfills/buffer/buffer-polyfill.node.js +0 -17
- package/dist/polyfills/buffer/buffer-polyfill.node.js.map +0 -1
- package/dist/polyfills/buffer/buffer.d.ts +0 -222
- package/dist/polyfills/buffer/buffer.d.ts.map +0 -1
- package/dist/polyfills/buffer/buffer.js +0 -1868
- package/dist/polyfills/buffer/buffer.js.map +0 -1
- package/dist/polyfills/buffer/index.d.ts +0 -4
- package/dist/polyfills/buffer/index.d.ts.map +0 -1
- package/dist/polyfills/buffer/index.js +0 -9
- package/dist/polyfills/buffer/index.js.map +0 -1
- package/dist/polyfills/buffer/install-buffer-polyfill.d.ts +0 -2
- package/dist/polyfills/buffer/install-buffer-polyfill.d.ts.map +0 -1
- package/dist/polyfills/buffer/install-buffer-polyfill.js +0 -10
- package/dist/polyfills/buffer/install-buffer-polyfill.js.map +0 -1
- package/dist/workers/parquet-json-worker.d.ts +0 -2
- package/dist/workers/parquet-json-worker.d.ts.map +0 -1
- package/dist/workers/parquet-json-worker.js +0 -7
- package/dist/workers/parquet-json-worker.js.map +0 -1
- package/src/parquet-arrow-loader.ts +0 -74
- package/src/parquet-arrow-writer.ts +0 -33
- package/src/parquet-json-loader.ts +0 -160
- package/src/parquet-json-writer.ts +0 -27
- package/src/parquetjs/modules.d.ts +0 -25
- package/src/polyfills/buffer/buffer-polyfill.browser.ts +0 -14
- package/src/polyfills/buffer/buffer-polyfill.node.ts +0 -19
- package/src/polyfills/buffer/buffer.ts +0 -2205
- package/src/polyfills/buffer/index.ts +0 -11
- package/src/polyfills/buffer/install-buffer-polyfill.ts +0 -12
- package/src/workers/parquet-json-worker.ts +0 -8
|
@@ -6,43 +6,105 @@
|
|
|
6
6
|
|
|
7
7
|
/* eslint-disable camelcase */
|
|
8
8
|
import {stream} from '@loaders.gl/loader-utils';
|
|
9
|
+
import {getWKBGeometryStatistics, type WKBGeometryBoundingBox} from '@loaders.gl/gis';
|
|
9
10
|
import {ParquetCodecOptions, PARQUET_CODECS} from '../codecs/index';
|
|
10
11
|
import * as Compression from '../compression';
|
|
11
|
-
import {
|
|
12
|
+
import type {
|
|
12
13
|
ParquetRowGroup,
|
|
13
14
|
ParquetCodec,
|
|
14
15
|
ParquetColumnChunk,
|
|
15
16
|
ParquetField,
|
|
17
|
+
ParquetLogicalType,
|
|
16
18
|
PrimitiveType,
|
|
17
19
|
ParquetRow
|
|
18
20
|
} from '../schema/declare';
|
|
19
21
|
import {ParquetSchema} from '../schema/schema';
|
|
20
22
|
import * as Shred from '../schema/shred';
|
|
21
23
|
import {
|
|
24
|
+
BsonType,
|
|
25
|
+
BoundingBox,
|
|
22
26
|
ColumnChunk,
|
|
27
|
+
ColumnCryptoMetaData,
|
|
28
|
+
ColumnIndex,
|
|
23
29
|
ColumnMetaData,
|
|
30
|
+
SizeStatistics,
|
|
31
|
+
Statistics,
|
|
32
|
+
SortingColumn,
|
|
24
33
|
CompressionCodec,
|
|
25
34
|
ConvertedType,
|
|
26
35
|
DataPageHeader,
|
|
27
36
|
DataPageHeaderV2,
|
|
37
|
+
DateType,
|
|
38
|
+
DecimalType,
|
|
39
|
+
DictionaryPageHeader,
|
|
40
|
+
EdgeInterpolationAlgorithm,
|
|
28
41
|
Encoding,
|
|
42
|
+
EnumType,
|
|
29
43
|
FieldRepetitionType,
|
|
30
44
|
FileMetaData,
|
|
45
|
+
Float16Type,
|
|
46
|
+
GeographyType,
|
|
47
|
+
GeospatialStatistics,
|
|
48
|
+
GeometryType,
|
|
49
|
+
IntType,
|
|
50
|
+
JsonType,
|
|
31
51
|
KeyValue,
|
|
52
|
+
ListType,
|
|
53
|
+
LogicalType,
|
|
54
|
+
MapType,
|
|
55
|
+
MicroSeconds,
|
|
56
|
+
MilliSeconds,
|
|
57
|
+
NanoSeconds,
|
|
58
|
+
NullType,
|
|
32
59
|
PageHeader,
|
|
60
|
+
PageEncodingStats,
|
|
33
61
|
PageType,
|
|
62
|
+
PageLocation,
|
|
34
63
|
RowGroup,
|
|
35
64
|
SchemaElement,
|
|
36
|
-
|
|
65
|
+
StringType,
|
|
66
|
+
TimeType,
|
|
67
|
+
TimeUnit,
|
|
68
|
+
TimestampType,
|
|
69
|
+
Type,
|
|
70
|
+
UUIDType,
|
|
71
|
+
VariantType,
|
|
72
|
+
OffsetIndex
|
|
37
73
|
} from '../parquet-thrift/index';
|
|
38
74
|
import {osopen, oswrite, osclose} from '../utils/file-utils';
|
|
39
75
|
import {getBitWidth, serializeThrift} from '../utils/read-utils';
|
|
40
|
-
import
|
|
76
|
+
import {concatUint8Arrays, encodeUtf8, writeUInt32LE} from '../utils/binary-utils';
|
|
77
|
+
import {crc32} from '../utils/crc32';
|
|
78
|
+
import {CompactInt64} from '../utils/uint8-array-compact-protocol';
|
|
79
|
+
import {planColumnPages} from './page-planner';
|
|
80
|
+
import {planDictionary, type ParquetDictionaryPolicy} from './dictionary-planner';
|
|
81
|
+
import {
|
|
82
|
+
decodeParquetSplitBlockBloomFilter,
|
|
83
|
+
encodeParquetBloomFilterValue,
|
|
84
|
+
encodeParquetSplitBlockBloomFilter
|
|
85
|
+
} from '../../lib/parquet-bloom-filter';
|
|
86
|
+
import {BoundaryOrder} from '../parquet-thrift/BoundaryOrder';
|
|
87
|
+
import {
|
|
88
|
+
EncryptionWithColumnKey,
|
|
89
|
+
EncryptionWithFooterKey
|
|
90
|
+
} from '../parquet-thrift/ColumnCryptoMetaData';
|
|
91
|
+
import {EncryptionAlgorithm} from '../parquet-thrift/EncryptionAlgorithm';
|
|
92
|
+
import {FileCryptoMetaData} from '../parquet-thrift/FileCryptoMetaData';
|
|
93
|
+
import {PARQUET_MAGIC_ENCRYPTED} from '../../lib/constants';
|
|
94
|
+
import {
|
|
95
|
+
createParquetModuleAad,
|
|
96
|
+
createParquetFooterSignature,
|
|
97
|
+
encryptParquetModule,
|
|
98
|
+
type ParquetWriterEncryptionOptions,
|
|
99
|
+
type ParquetWriterFooterSignatureOptions
|
|
100
|
+
} from '../../lib/parquet-encryption';
|
|
41
101
|
|
|
42
102
|
/**
|
|
43
103
|
* Parquet File Magic String
|
|
44
104
|
*/
|
|
45
105
|
const PARQUET_MAGIC = 'PAR1';
|
|
106
|
+
const PARQUET_MAGIC_BYTES = encodeUtf8(PARQUET_MAGIC);
|
|
107
|
+
const PARQUET_MAGIC_ENCRYPTED_BYTES = encodeUtf8(PARQUET_MAGIC_ENCRYPTED);
|
|
46
108
|
|
|
47
109
|
/**
|
|
48
110
|
* Parquet File Format Version
|
|
@@ -66,6 +128,29 @@ export interface ParquetEncoderOptions {
|
|
|
66
128
|
rowGroupSize?: number;
|
|
67
129
|
pageSize?: number;
|
|
68
130
|
useDataPageV2?: boolean;
|
|
131
|
+
dictionary?: ParquetDictionaryPolicy;
|
|
132
|
+
columnDictionaries?: Record<string, ParquetDictionaryPolicy>;
|
|
133
|
+
dictionaryPageSizeLimit?: number;
|
|
134
|
+
bloomFilter?: boolean | Record<string, boolean>;
|
|
135
|
+
pageIndex?: boolean | Record<string, boolean>;
|
|
136
|
+
/** Emit CRC-32 checksums for page bodies. */
|
|
137
|
+
writePageChecksums?: boolean;
|
|
138
|
+
/** Emit optional SizeStatistics metadata for each column chunk. */
|
|
139
|
+
writeSizeStatistics?: boolean;
|
|
140
|
+
/** Emit optional min/max/null-count statistics for each column chunk. */
|
|
141
|
+
writeStatistics?: boolean | Record<string, boolean>;
|
|
142
|
+
/** Declare the row-group sort order using top-level or dotted leaf column names. */
|
|
143
|
+
sortingColumns?: readonly ParquetSortingColumnOption[];
|
|
144
|
+
/** Encode INT96 input values as epoch nanoseconds using the canonical Julian-day layout. */
|
|
145
|
+
int96AsTimestamp?: boolean;
|
|
146
|
+
/** Encrypt the footer using Parquet modular encryption. */
|
|
147
|
+
encryption?: ParquetWriterEncryptionOptions;
|
|
148
|
+
/** Authenticate a plaintext footer with a Parquet modular-encryption signature. */
|
|
149
|
+
footerSignature?: ParquetWriterFooterSignatureOptions;
|
|
150
|
+
/** Stable file identifier shared by encrypted column modules and the footer. */
|
|
151
|
+
encryptionFileUnique?: Uint8Array;
|
|
152
|
+
/** Zero-based row-group ordinal used by encrypted module AAD. */
|
|
153
|
+
rowGroupOrdinal?: number;
|
|
69
154
|
|
|
70
155
|
// Write Stream Options
|
|
71
156
|
flags?: string;
|
|
@@ -76,13 +161,23 @@ export interface ParquetEncoderOptions {
|
|
|
76
161
|
start?: number;
|
|
77
162
|
}
|
|
78
163
|
|
|
164
|
+
/** Writer-facing declaration for one row-group sort key. */
|
|
165
|
+
export type ParquetSortingColumnOption = {
|
|
166
|
+
/** Top-level field name or dotted nested leaf path. */
|
|
167
|
+
column: string;
|
|
168
|
+
/** Sort direction; defaults to ascending. */
|
|
169
|
+
descending?: boolean;
|
|
170
|
+
/** Whether null values sort before non-null values; defaults to false. */
|
|
171
|
+
nullsFirst?: boolean;
|
|
172
|
+
};
|
|
173
|
+
|
|
79
174
|
/**
|
|
80
175
|
* Write a parquet file to an output stream. The ParquetEncoder will perform
|
|
81
176
|
* buffering/batching for performance, so close() must be called after all rows
|
|
82
177
|
* are written.
|
|
83
178
|
*/
|
|
84
179
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
85
|
-
export class ParquetEncoder<
|
|
180
|
+
export class ParquetEncoder<_T> {
|
|
86
181
|
/**
|
|
87
182
|
* Convenience method to create a new buffered parquet writer that writes to
|
|
88
183
|
* the specified file
|
|
@@ -126,8 +221,7 @@ export class ParquetEncoder<T> {
|
|
|
126
221
|
) {
|
|
127
222
|
this.schema = schema;
|
|
128
223
|
this.envelopeWriter = envelopeWriter;
|
|
129
|
-
|
|
130
|
-
this.rowBuffer = {};
|
|
224
|
+
this.rowBuffer = schema.rowGroup();
|
|
131
225
|
this.rowGroupSize = opts.rowGroupSize || PARQUET_DEFAULT_ROW_GROUP_SIZE;
|
|
132
226
|
this.closed = false;
|
|
133
227
|
this.userMetadata = {};
|
|
@@ -156,8 +250,8 @@ export class ParquetEncoder<T> {
|
|
|
156
250
|
}
|
|
157
251
|
Shred.shredRecord(this.schema, row, this.rowBuffer);
|
|
158
252
|
if (this.rowBuffer.rowCount >= this.rowGroupSize) {
|
|
159
|
-
|
|
160
|
-
this.rowBuffer =
|
|
253
|
+
await this.envelopeWriter.writeRowGroup(this.rowBuffer);
|
|
254
|
+
this.rowBuffer = this.schema.rowGroup();
|
|
161
255
|
}
|
|
162
256
|
}
|
|
163
257
|
|
|
@@ -175,8 +269,8 @@ export class ParquetEncoder<T> {
|
|
|
175
269
|
this.closed = true;
|
|
176
270
|
|
|
177
271
|
if (this.rowBuffer.rowCount > 0 || this.rowBuffer.rowCount >= this.rowGroupSize) {
|
|
178
|
-
|
|
179
|
-
this.rowBuffer =
|
|
272
|
+
await this.envelopeWriter.writeRowGroup(this.rowBuffer);
|
|
273
|
+
this.rowBuffer = this.schema.rowGroup();
|
|
180
274
|
}
|
|
181
275
|
|
|
182
276
|
await this.envelopeWriter.writeFooter(this.userMetadata);
|
|
@@ -236,17 +330,32 @@ export class ParquetEnvelopeWriter {
|
|
|
236
330
|
}
|
|
237
331
|
|
|
238
332
|
public schema: ParquetSchema;
|
|
239
|
-
public write: (buf:
|
|
333
|
+
public write: (buf: Uint8Array) => Promise<void>;
|
|
240
334
|
public close: () => Promise<void>;
|
|
241
335
|
public offset: number;
|
|
242
336
|
public rowCount: number;
|
|
243
337
|
public rowGroups: RowGroup[];
|
|
244
338
|
public pageSize: number;
|
|
245
339
|
public useDataPageV2: boolean;
|
|
340
|
+
public dictionary: ParquetDictionaryPolicy;
|
|
341
|
+
public columnDictionaries: Record<string, ParquetDictionaryPolicy>;
|
|
342
|
+
public dictionaryPageSizeLimit: number;
|
|
343
|
+
public bloomFilter?: boolean | Record<string, boolean>;
|
|
344
|
+
public pageIndex?: boolean | Record<string, boolean>;
|
|
345
|
+
public writePageChecksums: boolean;
|
|
346
|
+
public writeSizeStatistics: boolean;
|
|
347
|
+
public writeStatistics?: boolean | Record<string, boolean>;
|
|
348
|
+
public sortingColumns?: readonly ParquetSortingColumnOption[];
|
|
349
|
+
/** Optional modular-encryption configuration for the footer. */
|
|
350
|
+
public encryption?: ParquetWriterEncryptionOptions;
|
|
351
|
+
/** Optional plaintext-footer signature configuration. */
|
|
352
|
+
public footerSignature?: ParquetWriterFooterSignatureOptions;
|
|
353
|
+
/** File identifier used for encrypted column-module AAD. */
|
|
354
|
+
public encryptionFileUnique?: Uint8Array;
|
|
246
355
|
|
|
247
356
|
constructor(
|
|
248
357
|
schema: ParquetSchema,
|
|
249
|
-
writeFn: (buf:
|
|
358
|
+
writeFn: (buf: Uint8Array) => Promise<void>,
|
|
250
359
|
closeFn: () => Promise<void>,
|
|
251
360
|
fileOffset: number,
|
|
252
361
|
opts: ParquetEncoderOptions
|
|
@@ -259,9 +368,49 @@ export class ParquetEnvelopeWriter {
|
|
|
259
368
|
this.rowGroups = [];
|
|
260
369
|
this.pageSize = opts.pageSize || PARQUET_DEFAULT_PAGE_SIZE;
|
|
261
370
|
this.useDataPageV2 = 'useDataPageV2' in opts ? Boolean(opts.useDataPageV2) : false;
|
|
371
|
+
this.dictionary = opts.dictionary ?? 'auto';
|
|
372
|
+
this.columnDictionaries = opts.columnDictionaries || {};
|
|
373
|
+
this.dictionaryPageSizeLimit = opts.dictionaryPageSizeLimit ?? 1024 * 1024;
|
|
374
|
+
this.bloomFilter = opts.bloomFilter;
|
|
375
|
+
this.pageIndex = opts.pageIndex;
|
|
376
|
+
this.writePageChecksums = Boolean(opts.writePageChecksums);
|
|
377
|
+
this.writeSizeStatistics = Boolean(opts.writeSizeStatistics);
|
|
378
|
+
this.writeStatistics = opts.writeStatistics;
|
|
379
|
+
this.sortingColumns = opts.sortingColumns;
|
|
380
|
+
const encryptionFileUnique = opts.encryption?.fileUnique
|
|
381
|
+
? new Uint8Array(opts.encryption.fileUnique)
|
|
382
|
+
: undefined;
|
|
383
|
+
const footerSignatureFileUnique = opts.footerSignature?.fileUnique
|
|
384
|
+
? new Uint8Array(opts.footerSignature.fileUnique)
|
|
385
|
+
: undefined;
|
|
386
|
+
if (
|
|
387
|
+
encryptionFileUnique &&
|
|
388
|
+
footerSignatureFileUnique &&
|
|
389
|
+
!encryptionFileUnique.every((value, index) => value === footerSignatureFileUnique[index])
|
|
390
|
+
) {
|
|
391
|
+
throw new Error('Parquet encryption and footer signature file_unique values must match');
|
|
392
|
+
}
|
|
393
|
+
const sharedFileUnique =
|
|
394
|
+
encryptionFileUnique ??
|
|
395
|
+
footerSignatureFileUnique ??
|
|
396
|
+
(opts.encryption || opts.footerSignature ? createFileUnique() : undefined);
|
|
397
|
+
this.encryption = opts.encryption
|
|
398
|
+
? {...opts.encryption, fileUnique: new Uint8Array(sharedFileUnique!)}
|
|
399
|
+
: undefined;
|
|
400
|
+
this.footerSignature = opts.footerSignature
|
|
401
|
+
? {
|
|
402
|
+
...opts.footerSignature,
|
|
403
|
+
keyMetadata: new Uint8Array(opts.footerSignature.keyMetadata),
|
|
404
|
+
aadPrefix: opts.footerSignature.aadPrefix
|
|
405
|
+
? new Uint8Array(opts.footerSignature.aadPrefix)
|
|
406
|
+
: undefined,
|
|
407
|
+
fileUnique: new Uint8Array(sharedFileUnique!)
|
|
408
|
+
}
|
|
409
|
+
: undefined;
|
|
410
|
+
this.encryptionFileUnique = sharedFileUnique;
|
|
262
411
|
}
|
|
263
412
|
|
|
264
|
-
writeSection(buf:
|
|
413
|
+
writeSection(buf: Uint8Array): Promise<void> {
|
|
265
414
|
this.offset += buf.length;
|
|
266
415
|
return this.write(buf);
|
|
267
416
|
}
|
|
@@ -270,7 +419,9 @@ export class ParquetEnvelopeWriter {
|
|
|
270
419
|
* Encode the parquet file header
|
|
271
420
|
*/
|
|
272
421
|
writeHeader(): Promise<void> {
|
|
273
|
-
return this.writeSection(
|
|
422
|
+
return this.writeSection(
|
|
423
|
+
this.encryption && !this.footerSignature ? PARQUET_MAGIC_ENCRYPTED_BYTES : PARQUET_MAGIC_BYTES
|
|
424
|
+
);
|
|
274
425
|
}
|
|
275
426
|
|
|
276
427
|
/**
|
|
@@ -281,7 +432,19 @@ export class ParquetEnvelopeWriter {
|
|
|
281
432
|
const rgroup = await encodeRowGroup(this.schema, records, {
|
|
282
433
|
baseOffset: this.offset,
|
|
283
434
|
pageSize: this.pageSize,
|
|
284
|
-
useDataPageV2: this.useDataPageV2
|
|
435
|
+
useDataPageV2: this.useDataPageV2,
|
|
436
|
+
dictionary: this.dictionary,
|
|
437
|
+
columnDictionaries: this.columnDictionaries,
|
|
438
|
+
dictionaryPageSizeLimit: this.dictionaryPageSizeLimit,
|
|
439
|
+
bloomFilter: this.bloomFilter,
|
|
440
|
+
pageIndex: this.pageIndex,
|
|
441
|
+
writePageChecksums: this.writePageChecksums,
|
|
442
|
+
writeSizeStatistics: this.writeSizeStatistics,
|
|
443
|
+
writeStatistics: this.writeStatistics,
|
|
444
|
+
sortingColumns: this.sortingColumns,
|
|
445
|
+
encryption: this.encryption,
|
|
446
|
+
encryptionFileUnique: this.encryptionFileUnique,
|
|
447
|
+
rowGroupOrdinal: this.rowGroups.length
|
|
285
448
|
});
|
|
286
449
|
|
|
287
450
|
this.rowCount += records.rowCount;
|
|
@@ -292,15 +455,30 @@ export class ParquetEnvelopeWriter {
|
|
|
292
455
|
/**
|
|
293
456
|
* Write the parquet file footer
|
|
294
457
|
*/
|
|
295
|
-
writeFooter(userMetadata: Record<string, string>): Promise<void> {
|
|
458
|
+
async writeFooter(userMetadata: Record<string, string>): Promise<void> {
|
|
296
459
|
if (!userMetadata) {
|
|
297
460
|
// tslint:disable-next-line:no-parameter-reassignment
|
|
298
461
|
userMetadata = {};
|
|
299
462
|
}
|
|
300
463
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
464
|
+
if (this.footerSignature) {
|
|
465
|
+
await this.writeSection(
|
|
466
|
+
await encodeSignedFooter(
|
|
467
|
+
this.schema,
|
|
468
|
+
this.rowCount,
|
|
469
|
+
this.rowGroups,
|
|
470
|
+
userMetadata,
|
|
471
|
+
this.footerSignature
|
|
472
|
+
)
|
|
473
|
+
);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const footer = encodeFooter(this.schema, this.rowCount, this.rowGroups, userMetadata);
|
|
477
|
+
if (!this.encryption) {
|
|
478
|
+
await this.writeSection(footer);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
await this.writeSection(await encodeEncryptedFooter(footer, this.encryption));
|
|
304
482
|
}
|
|
305
483
|
|
|
306
484
|
/**
|
|
@@ -369,36 +547,52 @@ function encodeValues(
|
|
|
369
547
|
*/
|
|
370
548
|
async function encodeDataPage(
|
|
371
549
|
column: ParquetField,
|
|
372
|
-
data: ParquetColumnChunk
|
|
550
|
+
data: ParquetColumnChunk,
|
|
551
|
+
valueEncoding: ParquetCodec = column.encoding!,
|
|
552
|
+
valueBitWidth?: number,
|
|
553
|
+
writePageChecksums = false,
|
|
554
|
+
statistics?: Statistics,
|
|
555
|
+
int96AsTimestamp = false
|
|
373
556
|
): Promise<{
|
|
374
557
|
header: PageHeader;
|
|
375
558
|
headerSize: number;
|
|
376
|
-
page:
|
|
559
|
+
page: Uint8Array;
|
|
377
560
|
}> {
|
|
378
561
|
/* encode repetition and definition levels */
|
|
379
|
-
let rLevelsBuf:
|
|
562
|
+
let rLevelsBuf: Uint8Array = new Uint8Array(0);
|
|
380
563
|
if (column.rLevelMax > 0) {
|
|
381
|
-
rLevelsBuf = encodeValues(
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
564
|
+
rLevelsBuf = encodeValues(
|
|
565
|
+
PARQUET_RDLVL_TYPE,
|
|
566
|
+
PARQUET_RDLVL_ENCODING,
|
|
567
|
+
data.rlevels as number[],
|
|
568
|
+
{
|
|
569
|
+
bitWidth: getBitWidth(column.rLevelMax)
|
|
570
|
+
// disableEnvelope: false
|
|
571
|
+
}
|
|
572
|
+
);
|
|
385
573
|
}
|
|
386
574
|
|
|
387
|
-
let dLevelsBuf:
|
|
575
|
+
let dLevelsBuf: Uint8Array = new Uint8Array(0);
|
|
388
576
|
if (column.dLevelMax > 0) {
|
|
389
|
-
dLevelsBuf = encodeValues(
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
577
|
+
dLevelsBuf = encodeValues(
|
|
578
|
+
PARQUET_RDLVL_TYPE,
|
|
579
|
+
PARQUET_RDLVL_ENCODING,
|
|
580
|
+
data.dlevels as number[],
|
|
581
|
+
{
|
|
582
|
+
bitWidth: getBitWidth(column.dLevelMax)
|
|
583
|
+
// disableEnvelope: false
|
|
584
|
+
}
|
|
585
|
+
);
|
|
393
586
|
}
|
|
394
587
|
|
|
395
588
|
/* encode values */
|
|
396
|
-
const valuesBuf = encodeValues(column.primitiveType!,
|
|
589
|
+
const valuesBuf = encodeValues(column.primitiveType!, valueEncoding, data.values as any[], {
|
|
397
590
|
typeLength: column.typeLength,
|
|
398
|
-
bitWidth: column.typeLength
|
|
591
|
+
bitWidth: valueBitWidth ?? column.typeLength,
|
|
592
|
+
int96AsTimestamp
|
|
399
593
|
});
|
|
400
594
|
|
|
401
|
-
const dataBuf =
|
|
595
|
+
const dataBuf = concatUint8Arrays([rLevelsBuf, dLevelsBuf, valuesBuf]);
|
|
402
596
|
|
|
403
597
|
// compression = column.compression === 'UNCOMPRESSED' ? (compression || 'UNCOMPRESSED') : column.compression;
|
|
404
598
|
const compressedBuf = await Compression.deflate(column.compression!, dataBuf);
|
|
@@ -408,17 +602,19 @@ async function encodeDataPage(
|
|
|
408
602
|
type: PageType.DATA_PAGE,
|
|
409
603
|
data_page_header: new DataPageHeader({
|
|
410
604
|
num_values: data.count,
|
|
411
|
-
encoding: Encoding[
|
|
605
|
+
encoding: Encoding[valueEncoding] as any,
|
|
412
606
|
definition_level_encoding: Encoding[PARQUET_RDLVL_ENCODING], // [PARQUET_RDLVL_ENCODING],
|
|
413
|
-
repetition_level_encoding: Encoding[PARQUET_RDLVL_ENCODING] // [PARQUET_RDLVL_ENCODING]
|
|
607
|
+
repetition_level_encoding: Encoding[PARQUET_RDLVL_ENCODING], // [PARQUET_RDLVL_ENCODING]
|
|
608
|
+
statistics
|
|
414
609
|
}),
|
|
415
610
|
uncompressed_page_size: dataBuf.length,
|
|
416
|
-
compressed_page_size: compressedBuf.length
|
|
611
|
+
compressed_page_size: compressedBuf.length,
|
|
612
|
+
crc: writePageChecksums ? crc32(compressedBuf) : undefined
|
|
417
613
|
});
|
|
418
614
|
|
|
419
615
|
/* concat page header, repetition and definition levels and values */
|
|
420
616
|
const headerBuf = serializeThrift(header);
|
|
421
|
-
const page =
|
|
617
|
+
const page = concatUint8Arrays([headerBuf, compressedBuf]);
|
|
422
618
|
|
|
423
619
|
return {header, headerSize: headerBuf.length, page};
|
|
424
620
|
}
|
|
@@ -429,36 +625,52 @@ async function encodeDataPage(
|
|
|
429
625
|
async function encodeDataPageV2(
|
|
430
626
|
column: ParquetField,
|
|
431
627
|
data: ParquetColumnChunk,
|
|
432
|
-
rowCount: number
|
|
628
|
+
rowCount: number,
|
|
629
|
+
valueEncoding: ParquetCodec = column.encoding!,
|
|
630
|
+
valueBitWidth?: number,
|
|
631
|
+
writePageChecksums = false,
|
|
632
|
+
statistics?: Statistics,
|
|
633
|
+
int96AsTimestamp = false
|
|
433
634
|
): Promise<{
|
|
434
635
|
header: PageHeader;
|
|
435
636
|
headerSize: number;
|
|
436
|
-
page:
|
|
637
|
+
page: Uint8Array;
|
|
437
638
|
}> {
|
|
438
639
|
/* encode values */
|
|
439
|
-
const valuesBuf = encodeValues(column.primitiveType!,
|
|
640
|
+
const valuesBuf = encodeValues(column.primitiveType!, valueEncoding, data.values as any[], {
|
|
440
641
|
typeLength: column.typeLength,
|
|
441
|
-
bitWidth: column.typeLength
|
|
642
|
+
bitWidth: valueBitWidth ?? column.typeLength,
|
|
643
|
+
int96AsTimestamp
|
|
442
644
|
});
|
|
443
645
|
|
|
444
646
|
// compression = column.compression === 'UNCOMPRESSED' ? (compression || 'UNCOMPRESSED') : column.compression;
|
|
445
647
|
const compressedBuf = await Compression.deflate(column.compression!, valuesBuf);
|
|
446
648
|
|
|
447
649
|
/* encode repetition and definition levels */
|
|
448
|
-
let rLevelsBuf:
|
|
650
|
+
let rLevelsBuf: Uint8Array = new Uint8Array(0);
|
|
449
651
|
if (column.rLevelMax > 0) {
|
|
450
|
-
rLevelsBuf = encodeValues(
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
652
|
+
rLevelsBuf = encodeValues(
|
|
653
|
+
PARQUET_RDLVL_TYPE,
|
|
654
|
+
PARQUET_RDLVL_ENCODING,
|
|
655
|
+
data.rlevels as number[],
|
|
656
|
+
{
|
|
657
|
+
bitWidth: getBitWidth(column.rLevelMax),
|
|
658
|
+
disableEnvelope: true
|
|
659
|
+
}
|
|
660
|
+
);
|
|
454
661
|
}
|
|
455
662
|
|
|
456
|
-
let dLevelsBuf:
|
|
663
|
+
let dLevelsBuf: Uint8Array = new Uint8Array(0);
|
|
457
664
|
if (column.dLevelMax > 0) {
|
|
458
|
-
dLevelsBuf = encodeValues(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
665
|
+
dLevelsBuf = encodeValues(
|
|
666
|
+
PARQUET_RDLVL_TYPE,
|
|
667
|
+
PARQUET_RDLVL_ENCODING,
|
|
668
|
+
data.dlevels as number[],
|
|
669
|
+
{
|
|
670
|
+
bitWidth: getBitWidth(column.dLevelMax),
|
|
671
|
+
disableEnvelope: true
|
|
672
|
+
}
|
|
673
|
+
);
|
|
462
674
|
}
|
|
463
675
|
|
|
464
676
|
/* build page header */
|
|
@@ -468,21 +680,114 @@ async function encodeDataPageV2(
|
|
|
468
680
|
num_values: data.count,
|
|
469
681
|
num_nulls: data.count - data.values.length,
|
|
470
682
|
num_rows: rowCount,
|
|
471
|
-
encoding: Encoding[
|
|
683
|
+
encoding: Encoding[valueEncoding] as any,
|
|
472
684
|
definition_levels_byte_length: dLevelsBuf.length,
|
|
473
685
|
repetition_levels_byte_length: rLevelsBuf.length,
|
|
474
|
-
is_compressed: column.compression !== 'UNCOMPRESSED'
|
|
686
|
+
is_compressed: column.compression !== 'UNCOMPRESSED',
|
|
687
|
+
statistics
|
|
475
688
|
}),
|
|
476
689
|
uncompressed_page_size: rLevelsBuf.length + dLevelsBuf.length + valuesBuf.length,
|
|
477
|
-
compressed_page_size: rLevelsBuf.length + dLevelsBuf.length + compressedBuf.length
|
|
690
|
+
compressed_page_size: rLevelsBuf.length + dLevelsBuf.length + compressedBuf.length,
|
|
691
|
+
crc: writePageChecksums
|
|
692
|
+
? crc32(concatUint8Arrays([rLevelsBuf, dLevelsBuf, compressedBuf]))
|
|
693
|
+
: undefined
|
|
478
694
|
});
|
|
479
695
|
|
|
480
696
|
/* concat page header, repetition and definition levels and values */
|
|
481
697
|
const headerBuf = serializeThrift(header);
|
|
482
|
-
const page =
|
|
698
|
+
const page = concatUint8Arrays([headerBuf, rLevelsBuf, dLevelsBuf, compressedBuf]);
|
|
483
699
|
return {header, headerSize: headerBuf.length, page};
|
|
484
700
|
}
|
|
485
701
|
|
|
702
|
+
/** Encodes one chunk-wide PLAIN dictionary page. */
|
|
703
|
+
async function encodeDictionaryPage(
|
|
704
|
+
column: ParquetField,
|
|
705
|
+
dictionaryValues: unknown[],
|
|
706
|
+
writePageChecksums = false,
|
|
707
|
+
int96AsTimestamp = false
|
|
708
|
+
): Promise<{header: PageHeader; headerSize: number; page: Uint8Array}> {
|
|
709
|
+
const valuesBuffer = encodeValues(column.primitiveType!, 'PLAIN', dictionaryValues as any[], {
|
|
710
|
+
typeLength: column.typeLength,
|
|
711
|
+
int96AsTimestamp
|
|
712
|
+
});
|
|
713
|
+
const compressedBuffer = await Compression.deflate(column.compression!, valuesBuffer);
|
|
714
|
+
const header = new PageHeader({
|
|
715
|
+
type: PageType.DICTIONARY_PAGE,
|
|
716
|
+
dictionary_page_header: new DictionaryPageHeader({
|
|
717
|
+
num_values: dictionaryValues.length,
|
|
718
|
+
encoding: Encoding.PLAIN
|
|
719
|
+
}),
|
|
720
|
+
uncompressed_page_size: valuesBuffer.length,
|
|
721
|
+
compressed_page_size: compressedBuffer.length,
|
|
722
|
+
crc: writePageChecksums ? crc32(compressedBuffer) : undefined
|
|
723
|
+
});
|
|
724
|
+
const headerBuffer = serializeThrift(header);
|
|
725
|
+
return {
|
|
726
|
+
header,
|
|
727
|
+
headerSize: headerBuffer.length,
|
|
728
|
+
page: concatUint8Arrays([headerBuffer, compressedBuffer])
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/** Computes native Parquet geospatial statistics for a GEOMETRY or GEOGRAPHY column chunk. */
|
|
733
|
+
function createGeospatialStatistics(
|
|
734
|
+
column: ParquetField,
|
|
735
|
+
values: ParquetColumnChunk['values']
|
|
736
|
+
): GeospatialStatistics | undefined {
|
|
737
|
+
const logicalType = column.logicalType?.type || column.originalType;
|
|
738
|
+
if (logicalType !== 'GEOMETRY' && logicalType !== 'GEOGRAPHY') return undefined;
|
|
739
|
+
const geometryTypes = new Set<number>();
|
|
740
|
+
let bbox: WKBGeometryBoundingBox | undefined;
|
|
741
|
+
// Vertex bounds are not conservative for GEOGRAPHY: non-linear edge algorithms can reach
|
|
742
|
+
// extrema between vertices. Until edge-aware geodesic bounds are available, omit the bbox while
|
|
743
|
+
// retaining the useful geometry-type statistics.
|
|
744
|
+
const canWriteBoundingBox = logicalType === 'GEOMETRY';
|
|
745
|
+
for (const value of values) {
|
|
746
|
+
if (!(value instanceof ArrayBuffer) && !ArrayBuffer.isView(value)) {
|
|
747
|
+
throw new Error(`${logicalType} columns must contain WKB binary values`);
|
|
748
|
+
}
|
|
749
|
+
const statistics = getWKBGeometryStatistics(value);
|
|
750
|
+
geometryTypes.add(statistics.geometryType);
|
|
751
|
+
if (canWriteBoundingBox) {
|
|
752
|
+
bbox = mergeGeometryBoundingBoxes(bbox, statistics.bbox);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return new GeospatialStatistics({
|
|
756
|
+
bbox: bbox ? new BoundingBox(bbox) : undefined,
|
|
757
|
+
geospatial_types: [...geometryTypes].sort((left, right) => left - right)
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/** Merges finite per-dimension bounds without inventing absent Z or M dimensions. */
|
|
762
|
+
function mergeGeometryBoundingBoxes(
|
|
763
|
+
target: WKBGeometryBoundingBox | undefined,
|
|
764
|
+
source: WKBGeometryBoundingBox | undefined
|
|
765
|
+
): WKBGeometryBoundingBox | undefined {
|
|
766
|
+
if (!source) return target;
|
|
767
|
+
if (!target) return {...source};
|
|
768
|
+
target.xmin = Math.min(target.xmin, source.xmin);
|
|
769
|
+
target.xmax = Math.max(target.xmax, source.xmax);
|
|
770
|
+
target.ymin = Math.min(target.ymin, source.ymin);
|
|
771
|
+
target.ymax = Math.max(target.ymax, source.ymax);
|
|
772
|
+
mergeOptionalBounds(target, source, 'zmin', Math.min);
|
|
773
|
+
mergeOptionalBounds(target, source, 'zmax', Math.max);
|
|
774
|
+
mergeOptionalBounds(target, source, 'mmin', Math.min);
|
|
775
|
+
mergeOptionalBounds(target, source, 'mmax', Math.max);
|
|
776
|
+
return target;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/** Merges one optional bound while preserving a dimension absent from every value. */
|
|
780
|
+
function mergeOptionalBounds(
|
|
781
|
+
target: WKBGeometryBoundingBox,
|
|
782
|
+
source: WKBGeometryBoundingBox,
|
|
783
|
+
key: 'zmin' | 'zmax' | 'mmin' | 'mmax',
|
|
784
|
+
merge: (left: number, right: number) => number
|
|
785
|
+
): void {
|
|
786
|
+
const sourceValue = source[key];
|
|
787
|
+
if (sourceValue === undefined) return;
|
|
788
|
+
target[key] = target[key] === undefined ? sourceValue : merge(target[key]!, sourceValue);
|
|
789
|
+
}
|
|
790
|
+
|
|
486
791
|
/**
|
|
487
792
|
* Encode an array of values into a parquet column chunk
|
|
488
793
|
*/
|
|
@@ -490,54 +795,746 @@ async function encodeColumnChunk(
|
|
|
490
795
|
column: ParquetField,
|
|
491
796
|
buffer: ParquetRowGroup,
|
|
492
797
|
offset: number,
|
|
493
|
-
opts: ParquetEncoderOptions
|
|
798
|
+
opts: ParquetEncoderOptions,
|
|
799
|
+
rowGroupOrdinal: number,
|
|
800
|
+
columnOrdinal: number
|
|
494
801
|
): Promise<{
|
|
495
|
-
body:
|
|
802
|
+
body: Uint8Array;
|
|
496
803
|
metadata: ColumnMetaData;
|
|
804
|
+
encryptedColumnMetadata?: Uint8Array;
|
|
805
|
+
cryptoMetadata?: ColumnCryptoMetaData;
|
|
497
806
|
metadataOffset: number;
|
|
807
|
+
offsetIndexOffset?: number;
|
|
808
|
+
offsetIndexLength?: number;
|
|
809
|
+
columnIndexOffset?: number;
|
|
810
|
+
columnIndexLength?: number;
|
|
498
811
|
}> {
|
|
499
812
|
const data = buffer.columnData[column.path.join()];
|
|
500
813
|
const baseOffset = (opts.baseOffset || 0) + offset;
|
|
501
814
|
/* encode data page(s) */
|
|
502
|
-
|
|
503
|
-
|
|
815
|
+
const pageRecords: Array<{
|
|
816
|
+
page: Uint8Array;
|
|
817
|
+
headerSize: number;
|
|
818
|
+
dictionary: boolean;
|
|
819
|
+
}> = [];
|
|
820
|
+
const pageLocations: PageLocation[] = [];
|
|
504
821
|
// tslint:disable-next-line:variable-name
|
|
505
822
|
let total_uncompressed_size = 0;
|
|
506
823
|
// tslint:disable-next-line:variable-name
|
|
507
824
|
let total_compressed_size = 0;
|
|
508
|
-
|
|
825
|
+
const plannedPages = planColumnPages(
|
|
826
|
+
column,
|
|
827
|
+
data,
|
|
828
|
+
buffer.rowCount,
|
|
829
|
+
opts.pageSize || PARQUET_DEFAULT_PAGE_SIZE
|
|
830
|
+
);
|
|
831
|
+
const dictionaryPolicy =
|
|
832
|
+
opts.columnDictionaries?.[column.path[0]] ?? opts.dictionary ?? ('auto' as const);
|
|
833
|
+
const dictionaryPlan = planDictionary(
|
|
834
|
+
column,
|
|
835
|
+
Array.from(data.values),
|
|
836
|
+
dictionaryPolicy,
|
|
837
|
+
opts.dictionaryPageSizeLimit ?? 1024 * 1024
|
|
838
|
+
);
|
|
839
|
+
if (dictionaryPlan) {
|
|
840
|
+
const result = await encodeDictionaryPage(
|
|
841
|
+
column,
|
|
842
|
+
dictionaryPlan.values,
|
|
843
|
+
opts.writePageChecksums,
|
|
844
|
+
opts.int96AsTimestamp
|
|
845
|
+
);
|
|
846
|
+
pageRecords.push({page: result.page, headerSize: result.headerSize, dictionary: true});
|
|
847
|
+
total_uncompressed_size += result.header.uncompressed_page_size + result.headerSize;
|
|
848
|
+
total_compressed_size += result.header.compressed_page_size + result.headerSize;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const valueEncoding: ParquetCodec = dictionaryPlan
|
|
852
|
+
? column.encoding === 'PLAIN_DICTIONARY'
|
|
853
|
+
? 'PLAIN_DICTIONARY'
|
|
854
|
+
: 'RLE_DICTIONARY'
|
|
855
|
+
: column.encoding === 'PLAIN_DICTIONARY' || column.encoding === 'RLE_DICTIONARY'
|
|
856
|
+
? 'PLAIN'
|
|
857
|
+
: column.encoding!;
|
|
858
|
+
let dictionaryIndexOffset = 0;
|
|
859
|
+
for (const plannedPage of plannedPages) {
|
|
860
|
+
const pageData = dictionaryPlan
|
|
861
|
+
? {
|
|
862
|
+
...plannedPage.data,
|
|
863
|
+
values: dictionaryPlan.indices.slice(
|
|
864
|
+
dictionaryIndexOffset,
|
|
865
|
+
dictionaryIndexOffset + plannedPage.data.values.length
|
|
866
|
+
)
|
|
867
|
+
}
|
|
868
|
+
: plannedPage.data;
|
|
869
|
+
dictionaryIndexOffset += plannedPage.data.values.length;
|
|
509
870
|
const result = opts.useDataPageV2
|
|
510
|
-
? await encodeDataPageV2(
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
871
|
+
? await encodeDataPageV2(
|
|
872
|
+
column,
|
|
873
|
+
pageData,
|
|
874
|
+
plannedPage.rowCount,
|
|
875
|
+
valueEncoding,
|
|
876
|
+
dictionaryPlan?.bitWidth,
|
|
877
|
+
opts.writePageChecksums,
|
|
878
|
+
isStatisticsEnabled(opts.writeStatistics, column)
|
|
879
|
+
? createColumnStatistics(column, plannedPage.data)
|
|
880
|
+
: undefined,
|
|
881
|
+
opts.int96AsTimestamp
|
|
882
|
+
)
|
|
883
|
+
: await encodeDataPage(
|
|
884
|
+
column,
|
|
885
|
+
pageData,
|
|
886
|
+
valueEncoding,
|
|
887
|
+
dictionaryPlan?.bitWidth,
|
|
888
|
+
opts.writePageChecksums,
|
|
889
|
+
isStatisticsEnabled(opts.writeStatistics, column)
|
|
890
|
+
? createColumnStatistics(column, plannedPage.data)
|
|
891
|
+
: undefined,
|
|
892
|
+
opts.int96AsTimestamp
|
|
893
|
+
);
|
|
894
|
+
pageRecords.push({page: result.page, headerSize: result.headerSize, dictionary: false});
|
|
514
895
|
total_uncompressed_size += result.header.uncompressed_page_size + result.headerSize;
|
|
515
896
|
total_compressed_size += result.header.compressed_page_size + result.headerSize;
|
|
516
897
|
}
|
|
517
898
|
|
|
518
|
-
|
|
899
|
+
const encryption = opts.encryption;
|
|
900
|
+
const encryptedColumn = Boolean(encryption && shouldEncryptColumn(encryption, column));
|
|
901
|
+
const columnKeyMetadata = encryption ? getColumnKeyMetadata(encryption, column) : undefined;
|
|
902
|
+
const pages = encryptedColumn
|
|
903
|
+
? await encryptParquetColumnPages(
|
|
904
|
+
pageRecords,
|
|
905
|
+
encryption!,
|
|
906
|
+
opts.encryptionFileUnique!,
|
|
907
|
+
opts.rowGroupOrdinal ?? 0,
|
|
908
|
+
columnOrdinal,
|
|
909
|
+
columnKeyMetadata
|
|
910
|
+
)
|
|
911
|
+
: pageRecords.map(record => record.page);
|
|
912
|
+
let pageOffset = 0;
|
|
913
|
+
let dataPageIndex = 0;
|
|
914
|
+
let firstRowIndex = 0;
|
|
915
|
+
for (const [pageIndex, page] of pages.entries()) {
|
|
916
|
+
if (!pageRecords[pageIndex].dictionary) {
|
|
917
|
+
pageLocations.push(
|
|
918
|
+
new PageLocation({
|
|
919
|
+
offset: baseOffset + pageOffset,
|
|
920
|
+
compressed_page_size: page.length,
|
|
921
|
+
first_row_index: firstRowIndex
|
|
922
|
+
})
|
|
923
|
+
);
|
|
924
|
+
firstRowIndex += plannedPages[dataPageIndex++].rowCount;
|
|
925
|
+
}
|
|
926
|
+
pageOffset += page.length;
|
|
927
|
+
}
|
|
928
|
+
total_compressed_size = pages.reduce((total, page) => total + page.length, 0);
|
|
929
|
+
const pagesBuf = concatUint8Arrays(pages);
|
|
519
930
|
// const compression = column.compression === 'UNCOMPRESSED' ? (opts.compression || 'UNCOMPRESSED') : column.compression;
|
|
931
|
+
const bloomFilterEnabled =
|
|
932
|
+
opts.bloomFilter === true || opts.bloomFilter?.[column.path[0]] === true;
|
|
933
|
+
const bloomFilter = bloomFilterEnabled ? createColumnBloomFilter(column, data.values) : undefined;
|
|
934
|
+
const encodedBloomFilter =
|
|
935
|
+
bloomFilter && encryptedColumn
|
|
936
|
+
? await encryptParquetBloomFilter(
|
|
937
|
+
bloomFilter,
|
|
938
|
+
encryption!,
|
|
939
|
+
opts.encryptionFileUnique!,
|
|
940
|
+
opts.rowGroupOrdinal ?? 0,
|
|
941
|
+
columnOrdinal,
|
|
942
|
+
columnKeyMetadata
|
|
943
|
+
)
|
|
944
|
+
: bloomFilter;
|
|
945
|
+
const pageIndexEnabled = opts.pageIndex === true || opts.pageIndex?.[column.path[0]] === true;
|
|
946
|
+
const pageIndexes = pageIndexEnabled
|
|
947
|
+
? createColumnPageIndexes(column, plannedPages, pageLocations, opts.int96AsTimestamp)
|
|
948
|
+
: undefined;
|
|
949
|
+
const encodedPageIndexes =
|
|
950
|
+
pageIndexes && encryptedColumn
|
|
951
|
+
? await encryptParquetPageIndexes(
|
|
952
|
+
pageIndexes,
|
|
953
|
+
encryption!,
|
|
954
|
+
opts.encryptionFileUnique!,
|
|
955
|
+
opts.rowGroupOrdinal ?? 0,
|
|
956
|
+
columnOrdinal,
|
|
957
|
+
columnKeyMetadata
|
|
958
|
+
)
|
|
959
|
+
: pageIndexes;
|
|
520
960
|
|
|
521
961
|
/* prepare metadata header */
|
|
522
962
|
const metadata = new ColumnMetaData({
|
|
523
963
|
path_in_schema: column.path,
|
|
524
|
-
num_values: data.count,
|
|
525
|
-
data_page_offset: baseOffset,
|
|
964
|
+
num_values: int64(data.count),
|
|
965
|
+
data_page_offset: int64(baseOffset + (dictionaryPlan ? pages[0].length : 0)),
|
|
966
|
+
dictionary_page_offset: dictionaryPlan ? int64(baseOffset) : undefined,
|
|
526
967
|
encodings: [],
|
|
527
|
-
|
|
528
|
-
|
|
968
|
+
encoding_stats: [
|
|
969
|
+
...(dictionaryPlan
|
|
970
|
+
? [
|
|
971
|
+
new PageEncodingStats({
|
|
972
|
+
page_type: PageType.DICTIONARY_PAGE,
|
|
973
|
+
encoding: Encoding.PLAIN,
|
|
974
|
+
count: 1
|
|
975
|
+
})
|
|
976
|
+
]
|
|
977
|
+
: []),
|
|
978
|
+
new PageEncodingStats({
|
|
979
|
+
page_type: opts.useDataPageV2 ? PageType.DATA_PAGE_V2 : PageType.DATA_PAGE,
|
|
980
|
+
encoding: Encoding[valueEncoding],
|
|
981
|
+
count: plannedPages.length
|
|
982
|
+
})
|
|
983
|
+
],
|
|
984
|
+
total_uncompressed_size: int64(total_uncompressed_size), // : pagesBuf.length,
|
|
985
|
+
total_compressed_size: int64(total_compressed_size),
|
|
529
986
|
type: Type[column.primitiveType!],
|
|
530
|
-
codec: CompressionCodec[column.compression!]
|
|
987
|
+
codec: CompressionCodec[column.compression!],
|
|
988
|
+
bloom_filter_offset: encodedBloomFilter ? int64(baseOffset + pagesBuf.length) : undefined,
|
|
989
|
+
bloom_filter_length: encodedBloomFilter?.length,
|
|
990
|
+
geospatial_statistics: createGeospatialStatistics(column, data.values),
|
|
991
|
+
size_statistics: opts.writeSizeStatistics ? createSizeStatistics(column, data) : undefined,
|
|
992
|
+
statistics: isStatisticsEnabled(opts.writeStatistics, column)
|
|
993
|
+
? createColumnStatistics(column, data)
|
|
994
|
+
: undefined
|
|
531
995
|
});
|
|
532
996
|
|
|
533
997
|
/* list encodings */
|
|
534
998
|
metadata.encodings.push(Encoding[PARQUET_RDLVL_ENCODING]);
|
|
535
|
-
metadata.encodings.push(Encoding
|
|
999
|
+
if (dictionaryPlan) metadata.encodings.push(Encoding.PLAIN);
|
|
1000
|
+
metadata.encodings.push(Encoding[valueEncoding]);
|
|
536
1001
|
|
|
537
1002
|
/* concat metadata header and data pages */
|
|
538
|
-
const
|
|
539
|
-
const
|
|
540
|
-
|
|
1003
|
+
const metadataEncoded = serializeThrift(metadata);
|
|
1004
|
+
const metadataSection = encryptedColumn ? undefined : metadataEncoded;
|
|
1005
|
+
const metadataOffset = baseOffset + pagesBuf.length + (encodedBloomFilter?.length || 0);
|
|
1006
|
+
const offsetIndexOffset = encodedPageIndexes
|
|
1007
|
+
? metadataOffset + (metadataSection?.length || 0)
|
|
1008
|
+
: undefined;
|
|
1009
|
+
const columnIndexOffset = encodedPageIndexes?.columnIndex
|
|
1010
|
+
? metadataOffset + (metadataSection?.length || 0) + encodedPageIndexes.offsetIndex.length
|
|
1011
|
+
: undefined;
|
|
1012
|
+
const body = concatUint8Arrays([
|
|
1013
|
+
pagesBuf,
|
|
1014
|
+
...(encodedBloomFilter ? [encodedBloomFilter] : []),
|
|
1015
|
+
...(metadataSection ? [metadataSection] : []),
|
|
1016
|
+
...(encodedPageIndexes
|
|
1017
|
+
? [
|
|
1018
|
+
encodedPageIndexes.offsetIndex,
|
|
1019
|
+
...(encodedPageIndexes.columnIndex ? [encodedPageIndexes.columnIndex] : [])
|
|
1020
|
+
]
|
|
1021
|
+
: [])
|
|
1022
|
+
]);
|
|
1023
|
+
return {
|
|
1024
|
+
body,
|
|
1025
|
+
metadata,
|
|
1026
|
+
metadataOffset,
|
|
1027
|
+
offsetIndexOffset,
|
|
1028
|
+
offsetIndexLength: encodedPageIndexes?.offsetIndex.length,
|
|
1029
|
+
columnIndexOffset,
|
|
1030
|
+
columnIndexLength: encodedPageIndexes?.columnIndex?.length,
|
|
1031
|
+
...(encryptedColumn
|
|
1032
|
+
? {
|
|
1033
|
+
encryptedColumnMetadata: await encryptParquetModule(metadataEncoded, {
|
|
1034
|
+
algorithm: encryption!.algorithm ?? 'AES_GCM_V1',
|
|
1035
|
+
aad: createParquetModuleAad(
|
|
1036
|
+
encryption!.aadPrefix,
|
|
1037
|
+
opts.encryptionFileUnique!,
|
|
1038
|
+
'column-metadata',
|
|
1039
|
+
opts.rowGroupOrdinal ?? 0,
|
|
1040
|
+
columnOrdinal
|
|
1041
|
+
),
|
|
1042
|
+
keyMetadata: columnKeyMetadata ?? encryption!.keyMetadata,
|
|
1043
|
+
keyRetriever: encryption!.keyRetriever
|
|
1044
|
+
}),
|
|
1045
|
+
cryptoMetadata: new ColumnCryptoMetaData({
|
|
1046
|
+
...(columnKeyMetadata
|
|
1047
|
+
? {
|
|
1048
|
+
ENCRYPTION_WITH_COLUMN_KEY: new EncryptionWithColumnKey({
|
|
1049
|
+
path_in_schema: [...column.path],
|
|
1050
|
+
key_metadata: new Uint8Array(columnKeyMetadata)
|
|
1051
|
+
})
|
|
1052
|
+
}
|
|
1053
|
+
: {ENCRYPTION_WITH_FOOTER_KEY: new EncryptionWithFooterKey()})
|
|
1054
|
+
})
|
|
1055
|
+
}
|
|
1056
|
+
: {})
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/** Returns whether a writer column should use modular encryption. */
|
|
1061
|
+
function shouldEncryptColumn(
|
|
1062
|
+
options: ParquetWriterEncryptionOptions,
|
|
1063
|
+
column: ParquetField
|
|
1064
|
+
): boolean {
|
|
1065
|
+
return Boolean(
|
|
1066
|
+
options.columnKeyMetadata?.[column.path[0]] ||
|
|
1067
|
+
options.encryptColumns === true ||
|
|
1068
|
+
options.encryptColumns?.[column.path[0]] === true
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/** Returns a defensive copy of the key metadata assigned to one top-level column. */
|
|
1073
|
+
function getColumnKeyMetadata(
|
|
1074
|
+
options: ParquetWriterEncryptionOptions,
|
|
1075
|
+
column: ParquetField
|
|
1076
|
+
): Uint8Array | undefined {
|
|
1077
|
+
const keyMetadata = options.columnKeyMetadata?.[column.path[0]];
|
|
1078
|
+
return keyMetadata ? new Uint8Array(keyMetadata) : undefined;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/** Adds the four-byte module length required by encrypted page and Bloom-filter modules. */
|
|
1082
|
+
function prefixEncryptedModule(module: Uint8Array): Uint8Array {
|
|
1083
|
+
const prefixed = new Uint8Array(module.byteLength + 4);
|
|
1084
|
+
writeUInt32LE(prefixed, module.byteLength, 0);
|
|
1085
|
+
prefixed.set(module, 4);
|
|
1086
|
+
return prefixed;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/** Encrypts one column's dictionary and data pages using the Parquet module AAD rules. */
|
|
1090
|
+
async function encryptParquetColumnPages(
|
|
1091
|
+
pageRecords: readonly {page: Uint8Array; headerSize: number; dictionary: boolean}[],
|
|
1092
|
+
options: ParquetWriterEncryptionOptions,
|
|
1093
|
+
fileUnique: Uint8Array,
|
|
1094
|
+
rowGroupOrdinal: number,
|
|
1095
|
+
columnOrdinal: number,
|
|
1096
|
+
keyMetadata?: Uint8Array
|
|
1097
|
+
): Promise<Uint8Array[]> {
|
|
1098
|
+
const encryptedPages: Uint8Array[] = [];
|
|
1099
|
+
let dataPageOrdinal = 0;
|
|
1100
|
+
const algorithm = options.algorithm ?? 'AES_GCM_V1';
|
|
1101
|
+
for (const pageRecord of pageRecords) {
|
|
1102
|
+
const moduleType = pageRecord.dictionary ? 'dictionary-page' : 'data-page';
|
|
1103
|
+
const headerModuleType = pageRecord.dictionary ? 'dictionary-page-header' : 'data-page-header';
|
|
1104
|
+
const header = await encryptParquetModule(pageRecord.page.subarray(0, pageRecord.headerSize), {
|
|
1105
|
+
algorithm,
|
|
1106
|
+
aad: createParquetModuleAad(
|
|
1107
|
+
options.aadPrefix,
|
|
1108
|
+
fileUnique,
|
|
1109
|
+
headerModuleType,
|
|
1110
|
+
rowGroupOrdinal,
|
|
1111
|
+
columnOrdinal,
|
|
1112
|
+
pageRecord.dictionary ? undefined : dataPageOrdinal
|
|
1113
|
+
),
|
|
1114
|
+
keyMetadata: keyMetadata ?? options.keyMetadata,
|
|
1115
|
+
keyRetriever: options.keyRetriever
|
|
1116
|
+
});
|
|
1117
|
+
const body = await encryptParquetModule(pageRecord.page.subarray(pageRecord.headerSize), {
|
|
1118
|
+
algorithm,
|
|
1119
|
+
aad: createParquetModuleAad(
|
|
1120
|
+
options.aadPrefix,
|
|
1121
|
+
fileUnique,
|
|
1122
|
+
moduleType,
|
|
1123
|
+
rowGroupOrdinal,
|
|
1124
|
+
columnOrdinal,
|
|
1125
|
+
pageRecord.dictionary ? undefined : dataPageOrdinal
|
|
1126
|
+
),
|
|
1127
|
+
keyMetadata: keyMetadata ?? options.keyMetadata,
|
|
1128
|
+
keyRetriever: options.keyRetriever,
|
|
1129
|
+
page: true
|
|
1130
|
+
});
|
|
1131
|
+
encryptedPages.push(
|
|
1132
|
+
concatUint8Arrays([prefixEncryptedModule(header), prefixEncryptedModule(body)])
|
|
1133
|
+
);
|
|
1134
|
+
if (!pageRecord.dictionary) dataPageOrdinal++;
|
|
1135
|
+
}
|
|
1136
|
+
return encryptedPages;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/** Encrypts a split-block Bloom filter header and bitset as independent Parquet modules. */
|
|
1140
|
+
async function encryptParquetBloomFilter(
|
|
1141
|
+
bloomFilter: Uint8Array,
|
|
1142
|
+
options: ParquetWriterEncryptionOptions,
|
|
1143
|
+
fileUnique: Uint8Array,
|
|
1144
|
+
rowGroupOrdinal: number,
|
|
1145
|
+
columnOrdinal: number,
|
|
1146
|
+
keyMetadata?: Uint8Array
|
|
1147
|
+
): Promise<Uint8Array> {
|
|
1148
|
+
const parsed = decodeParquetSplitBlockBloomFilter(bloomFilter);
|
|
1149
|
+
const algorithm = options.algorithm ?? 'AES_GCM_V1';
|
|
1150
|
+
const keyOptions = {
|
|
1151
|
+
algorithm,
|
|
1152
|
+
keyMetadata: keyMetadata ?? options.keyMetadata,
|
|
1153
|
+
keyRetriever: options.keyRetriever
|
|
1154
|
+
};
|
|
1155
|
+
const header = await encryptParquetModule(bloomFilter.subarray(0, parsed.headerByteLength), {
|
|
1156
|
+
...keyOptions,
|
|
1157
|
+
aad: createParquetModuleAad(
|
|
1158
|
+
options.aadPrefix,
|
|
1159
|
+
fileUnique,
|
|
1160
|
+
'bloom-filter-header',
|
|
1161
|
+
rowGroupOrdinal,
|
|
1162
|
+
columnOrdinal
|
|
1163
|
+
)
|
|
1164
|
+
});
|
|
1165
|
+
const bitset = await encryptParquetModule(parsed.bitset, {
|
|
1166
|
+
...keyOptions,
|
|
1167
|
+
aad: createParquetModuleAad(
|
|
1168
|
+
options.aadPrefix,
|
|
1169
|
+
fileUnique,
|
|
1170
|
+
'bloom-filter-bitset',
|
|
1171
|
+
rowGroupOrdinal,
|
|
1172
|
+
columnOrdinal
|
|
1173
|
+
)
|
|
1174
|
+
});
|
|
1175
|
+
return concatUint8Arrays([prefixEncryptedModule(header), prefixEncryptedModule(bitset)]);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/** Encrypts column and offset indexes independently while preserving their footer offsets. */
|
|
1179
|
+
async function encryptParquetPageIndexes(
|
|
1180
|
+
pageIndexes: {offsetIndex: Uint8Array; columnIndex?: Uint8Array},
|
|
1181
|
+
options: ParquetWriterEncryptionOptions,
|
|
1182
|
+
fileUnique: Uint8Array,
|
|
1183
|
+
rowGroupOrdinal: number,
|
|
1184
|
+
columnOrdinal: number,
|
|
1185
|
+
keyMetadata?: Uint8Array
|
|
1186
|
+
): Promise<{offsetIndex: Uint8Array; columnIndex?: Uint8Array}> {
|
|
1187
|
+
const algorithm = options.algorithm ?? 'AES_GCM_V1';
|
|
1188
|
+
const encryptIndex = (bytes: Uint8Array, module: 'offset-index' | 'column-index') =>
|
|
1189
|
+
encryptParquetModule(bytes, {
|
|
1190
|
+
algorithm,
|
|
1191
|
+
aad: createParquetModuleAad(
|
|
1192
|
+
options.aadPrefix,
|
|
1193
|
+
fileUnique,
|
|
1194
|
+
module,
|
|
1195
|
+
rowGroupOrdinal,
|
|
1196
|
+
columnOrdinal
|
|
1197
|
+
),
|
|
1198
|
+
keyMetadata: keyMetadata ?? options.keyMetadata,
|
|
1199
|
+
keyRetriever: options.keyRetriever
|
|
1200
|
+
});
|
|
1201
|
+
return {
|
|
1202
|
+
offsetIndex: await encryptIndex(pageIndexes.offsetIndex, 'offset-index'),
|
|
1203
|
+
columnIndex: pageIndexes.columnIndex
|
|
1204
|
+
? await encryptIndex(pageIndexes.columnIndex, 'column-index')
|
|
1205
|
+
: undefined
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/** Builds page indexes for primitive leaves, including nested and repeated leaves. */
|
|
1210
|
+
function createColumnPageIndexes(
|
|
1211
|
+
column: ParquetField,
|
|
1212
|
+
plannedPages: ReturnType<typeof planColumnPages>,
|
|
1213
|
+
pageLocations: PageLocation[],
|
|
1214
|
+
int96AsTimestamp = false
|
|
1215
|
+
): {offsetIndex: Uint8Array; columnIndex?: Uint8Array} | undefined {
|
|
1216
|
+
if (
|
|
1217
|
+
!column.primitiveType ||
|
|
1218
|
+
!isPageIndexPhysicalType(column.primitiveType) ||
|
|
1219
|
+
plannedPages.length !== pageLocations.length ||
|
|
1220
|
+
plannedPages.length === 0
|
|
1221
|
+
) {
|
|
1222
|
+
return undefined;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
const offsetIndex = serializeThrift(new OffsetIndex({page_locations: pageLocations}));
|
|
1226
|
+
if (!supportsStatisticsSortOrder(column)) {
|
|
1227
|
+
return {offsetIndex};
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
const nullPages: boolean[] = [];
|
|
1231
|
+
const minValues: Uint8Array[] = [];
|
|
1232
|
+
const maxValues: Uint8Array[] = [];
|
|
1233
|
+
// A repeated leaf's definition-level count is not a row-level null count.
|
|
1234
|
+
// Omit null_counts for those leaves so readers do not interpret entry counts
|
|
1235
|
+
// as null counts while still benefiting from min/max page pruning.
|
|
1236
|
+
const nullCounts: number[] | undefined = column.rLevelMax === 0 ? [] : undefined;
|
|
1237
|
+
for (const plannedPage of plannedPages) {
|
|
1238
|
+
const values = plannedPage.data.values;
|
|
1239
|
+
const nullCount = plannedPage.data.count - values.length;
|
|
1240
|
+
nullCounts?.push(nullCount);
|
|
1241
|
+
if (values.length === 0) {
|
|
1242
|
+
nullPages.push(true);
|
|
1243
|
+
minValues.push(new Uint8Array(0));
|
|
1244
|
+
maxValues.push(new Uint8Array(0));
|
|
1245
|
+
continue;
|
|
1246
|
+
}
|
|
1247
|
+
const bounds = getPageBounds(
|
|
1248
|
+
values,
|
|
1249
|
+
column.primitiveType!,
|
|
1250
|
+
column.typeLength,
|
|
1251
|
+
int96AsTimestamp
|
|
1252
|
+
);
|
|
1253
|
+
if (!bounds) return undefined;
|
|
1254
|
+
nullPages.push(false);
|
|
1255
|
+
minValues.push(bounds.min);
|
|
1256
|
+
maxValues.push(bounds.max);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
return {
|
|
1260
|
+
offsetIndex,
|
|
1261
|
+
columnIndex: serializeThrift(
|
|
1262
|
+
new ColumnIndex({
|
|
1263
|
+
null_pages: nullPages,
|
|
1264
|
+
min_values: minValues,
|
|
1265
|
+
max_values: maxValues,
|
|
1266
|
+
boundary_order: BoundaryOrder.UNORDERED,
|
|
1267
|
+
null_counts: nullCounts
|
|
1268
|
+
})
|
|
1269
|
+
)
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
function isPageIndexPhysicalType(
|
|
1274
|
+
physicalType: ParquetField['primitiveType']
|
|
1275
|
+
): physicalType is PageIndexPhysicalType {
|
|
1276
|
+
return (
|
|
1277
|
+
physicalType === 'BOOLEAN' ||
|
|
1278
|
+
physicalType === 'INT32' ||
|
|
1279
|
+
physicalType === 'INT64' ||
|
|
1280
|
+
physicalType === 'INT96' ||
|
|
1281
|
+
physicalType === 'FLOAT' ||
|
|
1282
|
+
physicalType === 'DOUBLE' ||
|
|
1283
|
+
physicalType === 'BYTE_ARRAY' ||
|
|
1284
|
+
physicalType === 'FIXED_LEN_BYTE_ARRAY'
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
type PageIndexPhysicalType =
|
|
1289
|
+
| 'BOOLEAN'
|
|
1290
|
+
| 'INT32'
|
|
1291
|
+
| 'INT64'
|
|
1292
|
+
| 'INT96'
|
|
1293
|
+
| 'FLOAT'
|
|
1294
|
+
| 'DOUBLE'
|
|
1295
|
+
| 'BYTE_ARRAY'
|
|
1296
|
+
| 'FIXED_LEN_BYTE_ARRAY';
|
|
1297
|
+
|
|
1298
|
+
function getPageBounds(
|
|
1299
|
+
values: ParquetColumnChunk['values'],
|
|
1300
|
+
physicalType: PageIndexPhysicalType,
|
|
1301
|
+
typeLength?: number,
|
|
1302
|
+
int96AsTimestamp = false
|
|
1303
|
+
): {min: Uint8Array; max: Uint8Array} | undefined {
|
|
1304
|
+
if (physicalType === 'INT96' && !int96AsTimestamp) return undefined;
|
|
1305
|
+
const normalizedValues = Array.from(values as Iterable<unknown>, value =>
|
|
1306
|
+
normalizePageIndexValue(value, physicalType)
|
|
1307
|
+
);
|
|
1308
|
+
if (normalizedValues.some(value => value === undefined)) return undefined;
|
|
1309
|
+
const comparableValues = normalizedValues as Array<
|
|
1310
|
+
boolean | number | bigint | string | Uint8Array
|
|
1311
|
+
>;
|
|
1312
|
+
let min = comparableValues[0];
|
|
1313
|
+
let max = comparableValues[0];
|
|
1314
|
+
for (const value of comparableValues.slice(1)) {
|
|
1315
|
+
if (comparePageIndexValues(value, min, physicalType) < 0) min = value;
|
|
1316
|
+
if (comparePageIndexValues(value, max, physicalType) > 0) max = value;
|
|
1317
|
+
}
|
|
1318
|
+
try {
|
|
1319
|
+
return {
|
|
1320
|
+
min: encodePageIndexValue(min, physicalType, typeLength, int96AsTimestamp),
|
|
1321
|
+
max: encodePageIndexValue(max, physicalType, typeLength, int96AsTimestamp)
|
|
1322
|
+
};
|
|
1323
|
+
} catch {
|
|
1324
|
+
return undefined;
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
function encodePageIndexValue(
|
|
1329
|
+
value: boolean | number | bigint | string | Uint8Array,
|
|
1330
|
+
physicalType: PageIndexPhysicalType,
|
|
1331
|
+
typeLength: number | undefined,
|
|
1332
|
+
int96AsTimestamp: boolean
|
|
1333
|
+
): Uint8Array {
|
|
1334
|
+
if (physicalType === 'INT96') {
|
|
1335
|
+
if (!int96AsTimestamp) {
|
|
1336
|
+
throw new Error('INT96 page indexes require timestamp mode');
|
|
1337
|
+
}
|
|
1338
|
+
return PARQUET_CODECS.PLAIN.encodeValues('INT96', [value], {int96AsTimestamp});
|
|
1339
|
+
}
|
|
1340
|
+
return encodeParquetBloomFilterValue(value, physicalType, typeLength);
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
function normalizePageIndexValue(
|
|
1344
|
+
value: unknown,
|
|
1345
|
+
physicalType: PageIndexPhysicalType
|
|
1346
|
+
): boolean | number | bigint | string | Uint8Array | undefined {
|
|
1347
|
+
if (physicalType === 'BYTE_ARRAY' || physicalType === 'FIXED_LEN_BYTE_ARRAY') {
|
|
1348
|
+
if (typeof value === 'string') return new TextEncoder().encode(value);
|
|
1349
|
+
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
1350
|
+
if (ArrayBuffer.isView(value)) {
|
|
1351
|
+
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength).slice();
|
|
1352
|
+
}
|
|
1353
|
+
return undefined;
|
|
1354
|
+
}
|
|
1355
|
+
if (
|
|
1356
|
+
typeof value === 'boolean' ||
|
|
1357
|
+
typeof value === 'number' ||
|
|
1358
|
+
typeof value === 'bigint' ||
|
|
1359
|
+
typeof value === 'string'
|
|
1360
|
+
) {
|
|
1361
|
+
return value;
|
|
1362
|
+
}
|
|
1363
|
+
return undefined;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
function comparePageIndexValues(
|
|
1367
|
+
left: boolean | number | bigint | string | Uint8Array,
|
|
1368
|
+
right: boolean | number | bigint | string | Uint8Array,
|
|
1369
|
+
physicalType: PageIndexPhysicalType
|
|
1370
|
+
): number {
|
|
1371
|
+
if (physicalType === 'BYTE_ARRAY' || physicalType === 'FIXED_LEN_BYTE_ARRAY') {
|
|
1372
|
+
const leftBytes = typeof left === 'string' ? new TextEncoder().encode(left) : left;
|
|
1373
|
+
const rightBytes = typeof right === 'string' ? new TextEncoder().encode(right) : right;
|
|
1374
|
+
if (!(leftBytes instanceof Uint8Array) || !(rightBytes instanceof Uint8Array)) return 0;
|
|
1375
|
+
const length = Math.min(leftBytes.length, rightBytes.length);
|
|
1376
|
+
for (let index = 0; index < length; index++) {
|
|
1377
|
+
if (leftBytes[index] !== rightBytes[index]) {
|
|
1378
|
+
return leftBytes[index] < rightBytes[index] ? -1 : 1;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
return leftBytes.length - rightBytes.length;
|
|
1382
|
+
}
|
|
1383
|
+
if (typeof left === 'boolean' && typeof right === 'boolean') return Number(left) - Number(right);
|
|
1384
|
+
if (physicalType !== 'INT64' && physicalType !== 'INT96') {
|
|
1385
|
+
const leftNumber = Number(left);
|
|
1386
|
+
const rightNumber = Number(right);
|
|
1387
|
+
return leftNumber < rightNumber ? -1 : leftNumber > rightNumber ? 1 : 0;
|
|
1388
|
+
}
|
|
1389
|
+
try {
|
|
1390
|
+
const leftNumber = BigInt(left as number | bigint | string);
|
|
1391
|
+
const rightNumber = BigInt(right as number | bigint | string);
|
|
1392
|
+
return leftNumber < rightNumber ? -1 : leftNumber > rightNumber ? 1 : 0;
|
|
1393
|
+
} catch {
|
|
1394
|
+
const leftNumber = Number(left);
|
|
1395
|
+
const rightNumber = Number(right);
|
|
1396
|
+
return leftNumber < rightNumber ? -1 : leftNumber > rightNumber ? 1 : 0;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
/** Builds a Bloom filter for writer-supported physical scalar columns. */
|
|
1401
|
+
function createColumnBloomFilter(
|
|
1402
|
+
column: ParquetField,
|
|
1403
|
+
values: ParquetColumnChunk['values']
|
|
1404
|
+
): Uint8Array | undefined {
|
|
1405
|
+
const physicalType = column.primitiveType;
|
|
1406
|
+
if (
|
|
1407
|
+
physicalType !== 'BOOLEAN' &&
|
|
1408
|
+
physicalType !== 'INT32' &&
|
|
1409
|
+
physicalType !== 'INT64' &&
|
|
1410
|
+
physicalType !== 'FLOAT' &&
|
|
1411
|
+
physicalType !== 'DOUBLE' &&
|
|
1412
|
+
physicalType !== 'BYTE_ARRAY' &&
|
|
1413
|
+
physicalType !== 'FIXED_LEN_BYTE_ARRAY'
|
|
1414
|
+
) {
|
|
1415
|
+
return undefined;
|
|
1416
|
+
}
|
|
1417
|
+
const bloomValues = values.map(value => {
|
|
1418
|
+
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
1419
|
+
if (ArrayBuffer.isView(value)) {
|
|
1420
|
+
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength).slice();
|
|
1421
|
+
}
|
|
1422
|
+
return value;
|
|
1423
|
+
});
|
|
1424
|
+
return encodeParquetSplitBlockBloomFilter(
|
|
1425
|
+
bloomValues as Parameters<typeof encodeParquetSplitBlockBloomFilter>[0],
|
|
1426
|
+
physicalType,
|
|
1427
|
+
column.typeLength
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
/** Builds optional size statistics from one shredded column chunk. */
|
|
1432
|
+
function createSizeStatistics(column: ParquetField, data: ParquetColumnChunk): SizeStatistics {
|
|
1433
|
+
const repetitionLevelHistogram = createLevelHistogram(data.rlevels, column.rLevelMax);
|
|
1434
|
+
const definitionLevelHistogram = createLevelHistogram(data.dlevels, column.dLevelMax);
|
|
1435
|
+
let unencodedByteArrayDataBytes = 0;
|
|
1436
|
+
if (column.primitiveType === 'BYTE_ARRAY') {
|
|
1437
|
+
for (const value of data.values) {
|
|
1438
|
+
if (typeof value === 'string') {
|
|
1439
|
+
unencodedByteArrayDataBytes += encodeUtf8(value).byteLength;
|
|
1440
|
+
} else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
|
|
1441
|
+
unencodedByteArrayDataBytes += value.byteLength;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
return new SizeStatistics({
|
|
1446
|
+
unencoded_byte_array_data_bytes:
|
|
1447
|
+
column.primitiveType === 'BYTE_ARRAY' ? unencodedByteArrayDataBytes : undefined,
|
|
1448
|
+
repetition_level_histogram: repetitionLevelHistogram,
|
|
1449
|
+
definition_level_histogram: definitionLevelHistogram
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
/** Returns whether standard column statistics are enabled for one top-level field. */
|
|
1454
|
+
function isStatisticsEnabled(
|
|
1455
|
+
option: boolean | Record<string, boolean> | undefined,
|
|
1456
|
+
column: ParquetField
|
|
1457
|
+
): boolean {
|
|
1458
|
+
return option === true || option?.[column.path[0]] === true;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
/** Builds conservative standard min/max/null-count statistics for one column chunk. */
|
|
1462
|
+
function createColumnStatistics(
|
|
1463
|
+
column: ParquetField,
|
|
1464
|
+
data: ParquetColumnChunk
|
|
1465
|
+
): Statistics | undefined {
|
|
1466
|
+
if (
|
|
1467
|
+
!column.primitiveType ||
|
|
1468
|
+
!isPageIndexPhysicalType(column.primitiveType) ||
|
|
1469
|
+
!supportsStatisticsSortOrder(column)
|
|
1470
|
+
) {
|
|
1471
|
+
return undefined;
|
|
1472
|
+
}
|
|
1473
|
+
const statistics = new Statistics({
|
|
1474
|
+
null_count: column.rLevelMax === 0 ? data.count - data.values.length : undefined,
|
|
1475
|
+
is_min_value_exact: true,
|
|
1476
|
+
is_max_value_exact: true
|
|
1477
|
+
});
|
|
1478
|
+
if (data.values.length === 0) return statistics;
|
|
1479
|
+
const bounds = getPageBounds(data.values, column.primitiveType, column.typeLength);
|
|
1480
|
+
if (!bounds) return undefined;
|
|
1481
|
+
statistics.min_value = bounds.min;
|
|
1482
|
+
statistics.max_value = bounds.max;
|
|
1483
|
+
return statistics;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
/** Returns whether the physical representation preserves the logical sort order. */
|
|
1487
|
+
function supportsStatisticsSortOrder(column: ParquetField): boolean {
|
|
1488
|
+
const logicalType = column.logicalType?.type || column.originalType;
|
|
1489
|
+
if (logicalType === 'FLOAT16') return false;
|
|
1490
|
+
if (logicalType === 'DECIMAL' || logicalType?.startsWith('DECIMAL_')) {
|
|
1491
|
+
return column.primitiveType !== 'BYTE_ARRAY' && column.primitiveType !== 'FIXED_LEN_BYTE_ARRAY';
|
|
1492
|
+
}
|
|
1493
|
+
if (logicalType === 'INTEGER' || logicalType?.startsWith('UINT_')) {
|
|
1494
|
+
return column.logicalType?.isSigned !== false && !logicalType.startsWith('UINT_');
|
|
1495
|
+
}
|
|
1496
|
+
return true;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
/** Counts the occurrences of each definition or repetition level. */
|
|
1500
|
+
function createLevelHistogram(
|
|
1501
|
+
levels: ParquetColumnChunk['rlevels'],
|
|
1502
|
+
maximumLevel: number
|
|
1503
|
+
): number[] {
|
|
1504
|
+
const histogram = new Array<number>(maximumLevel + 1).fill(0);
|
|
1505
|
+
for (const level of levels) {
|
|
1506
|
+
if (level >= 0 && level <= maximumLevel) histogram[level]++;
|
|
1507
|
+
}
|
|
1508
|
+
return histogram;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/** Converts writer-facing sort keys into Parquet leaf-column indexes. */
|
|
1512
|
+
function createSortingColumns(
|
|
1513
|
+
schema: ParquetSchema,
|
|
1514
|
+
sortingColumns: readonly ParquetSortingColumnOption[] | undefined
|
|
1515
|
+
): SortingColumn[] | undefined {
|
|
1516
|
+
if (!sortingColumns?.length) return undefined;
|
|
1517
|
+
const leafFields = schema.fieldList.filter(field => !field.isNested);
|
|
1518
|
+
const usedColumnIndexes = new Set<number>();
|
|
1519
|
+
return sortingColumns.map(sortKey => {
|
|
1520
|
+
const columnIndex = leafFields.findIndex(
|
|
1521
|
+
field =>
|
|
1522
|
+
(field.path.length === 1 && field.name === sortKey.column) ||
|
|
1523
|
+
(field.path.length > 1 && field.path.join('.') === sortKey.column)
|
|
1524
|
+
);
|
|
1525
|
+
if (columnIndex < 0) {
|
|
1526
|
+
throw new Error(`Unknown Parquet sorting column ${sortKey.column}`);
|
|
1527
|
+
}
|
|
1528
|
+
if (usedColumnIndexes.has(columnIndex)) {
|
|
1529
|
+
throw new Error(`Duplicate Parquet sorting column ${sortKey.column}`);
|
|
1530
|
+
}
|
|
1531
|
+
usedColumnIndexes.add(columnIndex);
|
|
1532
|
+
return new SortingColumn({
|
|
1533
|
+
column_idx: columnIndex,
|
|
1534
|
+
descending: Boolean(sortKey.descending),
|
|
1535
|
+
nulls_first: Boolean(sortKey.nullsFirst)
|
|
1536
|
+
});
|
|
1537
|
+
});
|
|
541
1538
|
}
|
|
542
1539
|
|
|
543
1540
|
/**
|
|
@@ -548,32 +1545,52 @@ async function encodeRowGroup(
|
|
|
548
1545
|
data: ParquetRowGroup,
|
|
549
1546
|
opts: ParquetEncoderOptions
|
|
550
1547
|
): Promise<{
|
|
551
|
-
body:
|
|
1548
|
+
body: Uint8Array;
|
|
552
1549
|
metadata: RowGroup;
|
|
553
1550
|
}> {
|
|
554
1551
|
const metadata = new RowGroup({
|
|
555
|
-
num_rows: data.rowCount,
|
|
1552
|
+
num_rows: int64(data.rowCount),
|
|
556
1553
|
columns: [],
|
|
557
|
-
total_byte_size: 0
|
|
1554
|
+
total_byte_size: int64(0),
|
|
1555
|
+
sorting_columns: createSortingColumns(schema, opts.sortingColumns)
|
|
558
1556
|
});
|
|
559
1557
|
|
|
560
|
-
let body =
|
|
1558
|
+
let body: Uint8Array = new Uint8Array(0);
|
|
561
1559
|
for (const field of schema.fieldList) {
|
|
562
1560
|
if (field.isNested) {
|
|
563
1561
|
continue; // eslint-disable-line no-continue
|
|
564
1562
|
}
|
|
565
1563
|
|
|
566
|
-
const cchunkData = await encodeColumnChunk(
|
|
1564
|
+
const cchunkData = await encodeColumnChunk(
|
|
1565
|
+
field,
|
|
1566
|
+
data,
|
|
1567
|
+
body.length,
|
|
1568
|
+
opts,
|
|
1569
|
+
opts.rowGroupOrdinal ?? 0,
|
|
1570
|
+
metadata.columns.length
|
|
1571
|
+
);
|
|
567
1572
|
|
|
568
1573
|
const cchunk = new ColumnChunk({
|
|
569
|
-
file_offset: cchunkData.metadataOffset,
|
|
570
|
-
meta_data: cchunkData.metadata
|
|
1574
|
+
file_offset: int64(cchunkData.metadataOffset),
|
|
1575
|
+
meta_data: cchunkData.encryptedColumnMetadata ? undefined : cchunkData.metadata,
|
|
1576
|
+
offset_index_offset:
|
|
1577
|
+
cchunkData.offsetIndexOffset === undefined
|
|
1578
|
+
? undefined
|
|
1579
|
+
: int64(cchunkData.offsetIndexOffset),
|
|
1580
|
+
offset_index_length: cchunkData.offsetIndexLength,
|
|
1581
|
+
column_index_offset:
|
|
1582
|
+
cchunkData.columnIndexOffset === undefined
|
|
1583
|
+
? undefined
|
|
1584
|
+
: int64(cchunkData.columnIndexOffset),
|
|
1585
|
+
column_index_length: cchunkData.columnIndexLength,
|
|
1586
|
+
crypto_metadata: cchunkData.cryptoMetadata,
|
|
1587
|
+
encrypted_column_metadata: cchunkData.encryptedColumnMetadata
|
|
571
1588
|
});
|
|
572
1589
|
|
|
573
1590
|
metadata.columns.push(cchunk);
|
|
574
|
-
metadata.total_byte_size =
|
|
1591
|
+
metadata.total_byte_size = int64(Number(metadata.total_byte_size) + cchunkData.body.length);
|
|
575
1592
|
|
|
576
|
-
body =
|
|
1593
|
+
body = concatUint8Arrays([body, cchunkData.body]);
|
|
577
1594
|
}
|
|
578
1595
|
|
|
579
1596
|
return {body, metadata};
|
|
@@ -582,19 +1599,44 @@ async function encodeRowGroup(
|
|
|
582
1599
|
/**
|
|
583
1600
|
* Encode a parquet file metadata footer
|
|
584
1601
|
*/
|
|
585
|
-
function
|
|
1602
|
+
function encodeFooterMetadata(
|
|
586
1603
|
schema: ParquetSchema,
|
|
587
1604
|
rowCount: number,
|
|
588
1605
|
rowGroups: RowGroup[],
|
|
589
|
-
userMetadata: Record<string, string
|
|
590
|
-
|
|
1606
|
+
userMetadata: Record<string, string>,
|
|
1607
|
+
footerSignature?: ParquetWriterFooterSignatureOptions
|
|
1608
|
+
): Uint8Array {
|
|
1609
|
+
const signatureAlgorithm = footerSignature?.algorithm ?? 'AES_GCM_V1';
|
|
1610
|
+
const signatureFileUnique = footerSignature?.fileUnique
|
|
1611
|
+
? new Uint8Array(footerSignature.fileUnique)
|
|
1612
|
+
: undefined;
|
|
591
1613
|
const metadata = new FileMetaData({
|
|
592
1614
|
version: PARQUET_VERSION,
|
|
593
1615
|
created_by: 'parquets',
|
|
594
|
-
num_rows: rowCount,
|
|
1616
|
+
num_rows: int64(rowCount),
|
|
595
1617
|
row_groups: rowGroups,
|
|
596
1618
|
schema: [],
|
|
597
|
-
key_value_metadata: []
|
|
1619
|
+
key_value_metadata: [],
|
|
1620
|
+
encryption_algorithm: footerSignature
|
|
1621
|
+
? new EncryptionAlgorithm({
|
|
1622
|
+
...(signatureAlgorithm === 'AES_GCM_CTR_V1'
|
|
1623
|
+
? {
|
|
1624
|
+
AES_GCM_CTR_V1: {
|
|
1625
|
+
aad_prefix: footerSignature.aadPrefix,
|
|
1626
|
+
aad_file_unique: signatureFileUnique
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
: {
|
|
1630
|
+
AES_GCM_V1: {
|
|
1631
|
+
aad_prefix: footerSignature.aadPrefix,
|
|
1632
|
+
aad_file_unique: signatureFileUnique
|
|
1633
|
+
}
|
|
1634
|
+
})
|
|
1635
|
+
})
|
|
1636
|
+
: undefined,
|
|
1637
|
+
footer_signing_key_metadata: footerSignature
|
|
1638
|
+
? new Uint8Array(footerSignature.keyMetadata)
|
|
1639
|
+
: undefined
|
|
598
1640
|
});
|
|
599
1641
|
|
|
600
1642
|
for (const key in userMetadata) {
|
|
@@ -627,19 +1669,256 @@ function encodeFooter(
|
|
|
627
1669
|
}
|
|
628
1670
|
|
|
629
1671
|
if (field.originalType) {
|
|
630
|
-
|
|
1672
|
+
const convertedType = ConvertedType[field.originalType as keyof typeof ConvertedType];
|
|
1673
|
+
if (typeof convertedType === 'number') {
|
|
1674
|
+
schemaElem.converted_type = convertedType;
|
|
1675
|
+
}
|
|
631
1676
|
}
|
|
632
1677
|
|
|
633
1678
|
schemaElem.type_length = field.typeLength;
|
|
1679
|
+
schemaElem.precision = field.precision ?? field.presision;
|
|
1680
|
+
schemaElem.scale = field.scale;
|
|
1681
|
+
schemaElem.field_id = field.fieldId;
|
|
1682
|
+
const logicalType = getFieldLogicalType(field);
|
|
1683
|
+
if (logicalType) {
|
|
1684
|
+
schemaElem.logicalType = encodeParquetLogicalType(logicalType);
|
|
1685
|
+
}
|
|
634
1686
|
|
|
635
1687
|
metadata.schema.push(schemaElem);
|
|
636
1688
|
}
|
|
637
1689
|
|
|
638
|
-
|
|
639
|
-
|
|
1690
|
+
return serializeThrift(metadata);
|
|
1691
|
+
}
|
|
640
1692
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
1693
|
+
/** Encodes an unsigned plaintext footer with its standard Parquet trailer. */
|
|
1694
|
+
function encodeFooter(
|
|
1695
|
+
schema: ParquetSchema,
|
|
1696
|
+
rowCount: number,
|
|
1697
|
+
rowGroups: RowGroup[],
|
|
1698
|
+
userMetadata: Record<string, string>
|
|
1699
|
+
): Uint8Array {
|
|
1700
|
+
const metadataEncoded = encodeFooterMetadata(schema, rowCount, rowGroups, userMetadata);
|
|
1701
|
+
const footerEncoded = new Uint8Array(metadataEncoded.length + 8);
|
|
1702
|
+
|
|
1703
|
+
footerEncoded.set(metadataEncoded);
|
|
1704
|
+
writeUInt32LE(footerEncoded, metadataEncoded.length, metadataEncoded.length);
|
|
1705
|
+
footerEncoded.set(PARQUET_MAGIC_BYTES, metadataEncoded.length + 4);
|
|
644
1706
|
return footerEncoded;
|
|
645
1707
|
}
|
|
1708
|
+
|
|
1709
|
+
/** Encodes a plaintext footer with its trailing 28-byte authenticated signature. */
|
|
1710
|
+
async function encodeSignedFooter(
|
|
1711
|
+
schema: ParquetSchema,
|
|
1712
|
+
rowCount: number,
|
|
1713
|
+
rowGroups: RowGroup[],
|
|
1714
|
+
userMetadata: Record<string, string>,
|
|
1715
|
+
options: ParquetWriterFooterSignatureOptions
|
|
1716
|
+
): Promise<Uint8Array> {
|
|
1717
|
+
const metadata = encodeFooterMetadata(schema, rowCount, rowGroups, userMetadata, options);
|
|
1718
|
+
const signature = await createParquetFooterSignature(metadata, options);
|
|
1719
|
+
const footerEncoded = new Uint8Array(metadata.length + signature.length + 8);
|
|
1720
|
+
footerEncoded.set(metadata);
|
|
1721
|
+
footerEncoded.set(signature, metadata.length);
|
|
1722
|
+
writeUInt32LE(
|
|
1723
|
+
footerEncoded,
|
|
1724
|
+
metadata.length + signature.length,
|
|
1725
|
+
metadata.length + signature.length
|
|
1726
|
+
);
|
|
1727
|
+
footerEncoded.set(PARQUET_MAGIC_BYTES, metadata.length + signature.length + 4);
|
|
1728
|
+
return footerEncoded;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
/** Wraps a serialized footer in Parquet modular-encryption metadata and AES-GCM. */
|
|
1732
|
+
async function encodeEncryptedFooter(
|
|
1733
|
+
footer: Uint8Array,
|
|
1734
|
+
options: ParquetWriterEncryptionOptions
|
|
1735
|
+
): Promise<Uint8Array> {
|
|
1736
|
+
const algorithm = options.algorithm ?? 'AES_GCM_V1';
|
|
1737
|
+
const fileUnique = options.fileUnique ? new Uint8Array(options.fileUnique) : createFileUnique();
|
|
1738
|
+
if (fileUnique.byteLength !== 8) {
|
|
1739
|
+
throw new Error(`Parquet encrypted footer file_unique must be 8 bytes`);
|
|
1740
|
+
}
|
|
1741
|
+
const algorithmParameters = {
|
|
1742
|
+
aad_prefix: options.aadPrefix && new Uint8Array(options.aadPrefix),
|
|
1743
|
+
aad_file_unique: fileUnique,
|
|
1744
|
+
supply_aad_prefix: undefined
|
|
1745
|
+
};
|
|
1746
|
+
const encryptionAlgorithm =
|
|
1747
|
+
algorithm === 'AES_GCM_CTR_V1'
|
|
1748
|
+
? new EncryptionAlgorithm({AES_GCM_CTR_V1: algorithmParameters})
|
|
1749
|
+
: new EncryptionAlgorithm({AES_GCM_V1: algorithmParameters});
|
|
1750
|
+
const cryptoMetadata = serializeThrift(
|
|
1751
|
+
new FileCryptoMetaData({
|
|
1752
|
+
encryption_algorithm: encryptionAlgorithm,
|
|
1753
|
+
key_metadata: options.keyMetadata && new Uint8Array(options.keyMetadata)
|
|
1754
|
+
})
|
|
1755
|
+
);
|
|
1756
|
+
const footerMetadata = footer.subarray(0, footer.byteLength - 8);
|
|
1757
|
+
const encryptedFooter = await encryptParquetModule(footerMetadata, {
|
|
1758
|
+
algorithm,
|
|
1759
|
+
aad: createParquetModuleAad(options.aadPrefix, fileUnique, 'footer'),
|
|
1760
|
+
keyMetadata: options.keyMetadata,
|
|
1761
|
+
keyRetriever: options.keyRetriever
|
|
1762
|
+
});
|
|
1763
|
+
const metadataSize = cryptoMetadata.byteLength + encryptedFooter.byteLength;
|
|
1764
|
+
const trailer = new Uint8Array(8);
|
|
1765
|
+
writeUInt32LE(trailer, metadataSize, 0);
|
|
1766
|
+
trailer.set(encodeUtf8(PARQUET_MAGIC_ENCRYPTED), 4);
|
|
1767
|
+
return concatUint8Arrays([cryptoMetadata, encryptedFooter, trailer]);
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
/** Generates the eight-byte file identifier required by Parquet module AAD. */
|
|
1771
|
+
function createFileUnique(): Uint8Array {
|
|
1772
|
+
const fileUnique = new Uint8Array(8);
|
|
1773
|
+
const cryptoProvider = globalThis.crypto;
|
|
1774
|
+
if (!cryptoProvider?.getRandomValues) {
|
|
1775
|
+
throw new Error('Parquet encrypted footer requires Web Crypto random values');
|
|
1776
|
+
}
|
|
1777
|
+
cryptoProvider.getRandomValues(fileUnique);
|
|
1778
|
+
return fileUnique;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
/** Returns an explicit or inferred modern logical annotation for a writer field. */
|
|
1782
|
+
function getFieldLogicalType(field: ParquetField): ParquetLogicalType | undefined {
|
|
1783
|
+
if (field.logicalType) return field.logicalType;
|
|
1784
|
+
const originalType = field.originalType;
|
|
1785
|
+
if (!originalType) return undefined;
|
|
1786
|
+
if (originalType === 'UTF8') return {type: 'STRING'};
|
|
1787
|
+
if (originalType === 'ENUM') return {type: 'ENUM'};
|
|
1788
|
+
if (originalType.startsWith('DECIMAL_')) {
|
|
1789
|
+
return {
|
|
1790
|
+
type: 'DECIMAL',
|
|
1791
|
+
precision: field.precision ?? field.presision,
|
|
1792
|
+
scale: field.scale
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
if (originalType === 'DATE') return {type: 'DATE'};
|
|
1796
|
+
if (originalType.startsWith('TIME_')) {
|
|
1797
|
+
return {
|
|
1798
|
+
type: 'TIME',
|
|
1799
|
+
unit: originalType.slice(5) as ParquetLogicalType['unit'],
|
|
1800
|
+
isAdjustedToUTC: true
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
if (originalType.startsWith('TIMESTAMP_')) {
|
|
1804
|
+
return {
|
|
1805
|
+
type: 'TIMESTAMP',
|
|
1806
|
+
unit: originalType.slice(10) as ParquetLogicalType['unit'],
|
|
1807
|
+
isAdjustedToUTC: true
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
if (originalType.startsWith('UINT_') || originalType.startsWith('INT_')) {
|
|
1811
|
+
return {
|
|
1812
|
+
type: 'INTEGER',
|
|
1813
|
+
bitWidth: Number(originalType.slice(originalType.indexOf('_') + 1)) as 8 | 16 | 32 | 64,
|
|
1814
|
+
isSigned: originalType.startsWith('INT_')
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
if (originalType === 'JSON' || originalType === 'BSON') return {type: originalType};
|
|
1818
|
+
if (
|
|
1819
|
+
originalType === 'UUID' ||
|
|
1820
|
+
originalType === 'FLOAT16' ||
|
|
1821
|
+
originalType === 'UNKNOWN' ||
|
|
1822
|
+
originalType === 'VARIANT' ||
|
|
1823
|
+
originalType === 'GEOMETRY' ||
|
|
1824
|
+
originalType === 'GEOGRAPHY'
|
|
1825
|
+
) {
|
|
1826
|
+
return {type: originalType};
|
|
1827
|
+
}
|
|
1828
|
+
return undefined;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
/** Converts the internal logical annotation into the Parquet 2.13 Thrift union. */
|
|
1832
|
+
function encodeParquetLogicalType(logicalType: ParquetLogicalType): LogicalType {
|
|
1833
|
+
switch (logicalType.type) {
|
|
1834
|
+
case 'STRING':
|
|
1835
|
+
return LogicalType.fromSTRING(new StringType());
|
|
1836
|
+
case 'MAP':
|
|
1837
|
+
return LogicalType.fromMAP(new MapType());
|
|
1838
|
+
case 'LIST':
|
|
1839
|
+
return LogicalType.fromLIST(new ListType());
|
|
1840
|
+
case 'ENUM':
|
|
1841
|
+
return LogicalType.fromENUM(new EnumType());
|
|
1842
|
+
case 'DECIMAL':
|
|
1843
|
+
if (logicalType.precision === undefined || logicalType.scale === undefined) {
|
|
1844
|
+
throw new Error('Parquet DECIMAL requires precision and scale');
|
|
1845
|
+
}
|
|
1846
|
+
return LogicalType.fromDECIMAL(
|
|
1847
|
+
new DecimalType({precision: logicalType.precision, scale: logicalType.scale})
|
|
1848
|
+
);
|
|
1849
|
+
case 'DATE':
|
|
1850
|
+
return LogicalType.fromDATE(new DateType());
|
|
1851
|
+
case 'TIME':
|
|
1852
|
+
return LogicalType.fromTIME(
|
|
1853
|
+
new TimeType({
|
|
1854
|
+
isAdjustedToUTC: logicalType.isAdjustedToUTC ?? false,
|
|
1855
|
+
unit: encodeParquetTimeUnit(logicalType.unit)
|
|
1856
|
+
})
|
|
1857
|
+
);
|
|
1858
|
+
case 'TIMESTAMP':
|
|
1859
|
+
return LogicalType.fromTIMESTAMP(
|
|
1860
|
+
new TimestampType({
|
|
1861
|
+
isAdjustedToUTC: logicalType.isAdjustedToUTC ?? false,
|
|
1862
|
+
unit: encodeParquetTimeUnit(logicalType.unit)
|
|
1863
|
+
})
|
|
1864
|
+
);
|
|
1865
|
+
case 'INTEGER':
|
|
1866
|
+
if (logicalType.bitWidth === undefined || logicalType.isSigned === undefined) {
|
|
1867
|
+
throw new Error('Parquet INTEGER requires bitWidth and isSigned');
|
|
1868
|
+
}
|
|
1869
|
+
return LogicalType.fromINTEGER(
|
|
1870
|
+
new IntType({bitWidth: logicalType.bitWidth, isSigned: logicalType.isSigned})
|
|
1871
|
+
);
|
|
1872
|
+
case 'UNKNOWN':
|
|
1873
|
+
return LogicalType.fromUNKNOWN(new NullType());
|
|
1874
|
+
case 'JSON':
|
|
1875
|
+
return LogicalType.fromJSON(new JsonType());
|
|
1876
|
+
case 'BSON':
|
|
1877
|
+
return LogicalType.fromBSON(new BsonType());
|
|
1878
|
+
case 'UUID':
|
|
1879
|
+
return LogicalType.fromUUID(new UUIDType());
|
|
1880
|
+
case 'FLOAT16':
|
|
1881
|
+
return LogicalType.fromFLOAT16(new Float16Type());
|
|
1882
|
+
case 'VARIANT':
|
|
1883
|
+
return LogicalType.fromVARIANT(
|
|
1884
|
+
new VariantType({specification_version: logicalType.specificationVersion})
|
|
1885
|
+
);
|
|
1886
|
+
case 'GEOMETRY':
|
|
1887
|
+
return LogicalType.fromGEOMETRY(new GeometryType({crs: logicalType.crs}));
|
|
1888
|
+
case 'GEOGRAPHY':
|
|
1889
|
+
return LogicalType.fromGEOGRAPHY(
|
|
1890
|
+
new GeographyType({
|
|
1891
|
+
crs: logicalType.crs,
|
|
1892
|
+
algorithm:
|
|
1893
|
+
logicalType.algorithm === undefined
|
|
1894
|
+
? undefined
|
|
1895
|
+
: EdgeInterpolationAlgorithm[
|
|
1896
|
+
logicalType.algorithm as keyof typeof EdgeInterpolationAlgorithm
|
|
1897
|
+
]
|
|
1898
|
+
})
|
|
1899
|
+
);
|
|
1900
|
+
default:
|
|
1901
|
+
throw new Error(
|
|
1902
|
+
`Unsupported Parquet logical type ${(logicalType as ParquetLogicalType).type}`
|
|
1903
|
+
);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
/** Encodes one Parquet logical time unit. */
|
|
1908
|
+
function encodeParquetTimeUnit(unit: ParquetLogicalType['unit']): TimeUnit {
|
|
1909
|
+
switch (unit) {
|
|
1910
|
+
case 'MILLIS':
|
|
1911
|
+
return TimeUnit.fromMILLIS(new MilliSeconds());
|
|
1912
|
+
case 'MICROS':
|
|
1913
|
+
return TimeUnit.fromMICROS(new MicroSeconds());
|
|
1914
|
+
case 'NANOS':
|
|
1915
|
+
return TimeUnit.fromNANOS(new NanoSeconds());
|
|
1916
|
+
default:
|
|
1917
|
+
throw new Error('Parquet TIME and TIMESTAMP require a unit');
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
/** Wrap a writer metadata number in the thrift int64 compatibility object. */
|
|
1922
|
+
function int64(value: number): any {
|
|
1923
|
+
return new CompactInt64(BigInt(value));
|
|
1924
|
+
}
|