@query-farm/vgi 0.6.0
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 +134 -0
- package/README.md +494 -0
- package/dist/__dbg.test-d.d.ts +1 -0
- package/dist/arguments/argument-spec.d.ts +51 -0
- package/dist/arguments/arguments.d.ts +13 -0
- package/dist/arrow/codec/branded.d.ts +83 -0
- package/dist/arrow/codec/registry.d.ts +29 -0
- package/dist/arrow/codec/repr.d.ts +23 -0
- package/dist/arrow/codec/type-descriptors.d.ts +109 -0
- package/dist/arrow/impl-arrowjs/build.d.ts +19 -0
- package/dist/arrow/impl-arrowjs/canonical.d.ts +15 -0
- package/dist/arrow/impl-arrowjs/empty.d.ts +7 -0
- package/dist/arrow/impl-arrowjs/filter.d.ts +11 -0
- package/dist/arrow/impl-arrowjs/index.d.ts +11 -0
- package/dist/arrow/impl-arrowjs/ipc.d.ts +24 -0
- package/dist/arrow/impl-arrowjs/iterate.d.ts +39 -0
- package/dist/arrow/impl-arrowjs/project.d.ts +10 -0
- package/dist/arrow/impl-arrowjs/schema.d.ts +62 -0
- package/dist/arrow/impl-arrowjs/statistics.d.ts +13 -0
- package/dist/arrow/impl-flechette/build.d.ts +14 -0
- package/dist/arrow/impl-flechette/canonical.d.ts +18 -0
- package/dist/arrow/impl-flechette/empty.d.ts +9 -0
- package/dist/arrow/impl-flechette/filter.d.ts +10 -0
- package/dist/arrow/impl-flechette/index.d.ts +11 -0
- package/dist/arrow/impl-flechette/ipc.d.ts +33 -0
- package/dist/arrow/impl-flechette/iterate.d.ts +29 -0
- package/dist/arrow/impl-flechette/message-meta.d.ts +10 -0
- package/dist/arrow/impl-flechette/normalize-type.d.ts +11 -0
- package/dist/arrow/impl-flechette/project.d.ts +3 -0
- package/dist/arrow/impl-flechette/schema.d.ts +47 -0
- package/dist/arrow/impl-flechette/statistics.d.ts +13 -0
- package/dist/arrow/index.d.ts +11 -0
- package/dist/arrow/predicates.d.ts +47 -0
- package/dist/arrow/repr.test-d.d.ts +52 -0
- package/dist/arrow/schema-types.d.ts +71 -0
- package/dist/arrow/types.d.ts +57 -0
- package/dist/catalog/attach-option.d.ts +33 -0
- package/dist/catalog/attach-options.d.ts +30 -0
- package/dist/catalog/composite.d.ts +33 -0
- package/dist/catalog/descriptors.d.ts +154 -0
- package/dist/catalog/interface.d.ts +239 -0
- package/dist/catalog/read-only.d.ts +33 -0
- package/dist/client/client.d.ts +189 -0
- package/dist/client/errors.d.ts +26 -0
- package/dist/client/helpers.d.ts +12 -0
- package/dist/client/protocol.d.ts +16 -0
- package/dist/client/types.d.ts +125 -0
- package/dist/client-entry.d.ts +11 -0
- package/dist/client-entry.js +48 -0
- package/dist/client-entry.js.map +9 -0
- package/dist/codec/asd.d.ts +25 -0
- package/dist/crypto.d.ts +36 -0
- package/dist/errors.d.ts +24 -0
- package/dist/filter-pushdown/collector.d.ts +32 -0
- package/dist/filter-pushdown/deserialize.d.ts +16 -0
- package/dist/filter-pushdown/evaluate.d.ts +51 -0
- package/dist/filter-pushdown/index.d.ts +4 -0
- package/dist/filter-pushdown/types.d.ts +84 -0
- package/dist/functions/aggregate.d.ts +129 -0
- package/dist/functions/copy-from.d.ts +81 -0
- package/dist/functions/copy-to.d.ts +121 -0
- package/dist/functions/registry.d.ts +16 -0
- package/dist/functions/scalar.d.ts +98 -0
- package/dist/functions/storage-cf-do.d.ts +47 -0
- package/dist/functions/storage.d.ts +86 -0
- package/dist/functions/table-buffering.d.ts +99 -0
- package/dist/functions/table-in-out.d.ts +64 -0
- package/dist/functions/table.d.ts +118 -0
- package/dist/functions/types.d.ts +157 -0
- package/dist/generated/vgi-client.d.ts +466 -0
- package/dist/generated/vgi-protocol-schemas.d.ts +115 -0
- package/dist/index.core.d.ts +42 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +10888 -0
- package/dist/index.js.map +85 -0
- package/dist/metadata/resolve.d.ts +3 -0
- package/dist/metadata/serialize.d.ts +12 -0
- package/dist/metadata/types.d.ts +47 -0
- package/dist/protocol/dispatch.d.ts +23 -0
- package/dist/protocol/handlers/aggregate.d.ts +3 -0
- package/dist/protocol/handlers/catalog/admin.d.ts +3 -0
- package/dist/protocol/handlers/catalog/index.d.ts +3 -0
- package/dist/protocol/handlers/catalog/index_methods.d.ts +3 -0
- package/dist/protocol/handlers/catalog/macro.d.ts +3 -0
- package/dist/protocol/handlers/catalog/shared.d.ts +70 -0
- package/dist/protocol/handlers/catalog/table.d.ts +3 -0
- package/dist/protocol/handlers/catalog/view.d.ts +3 -0
- package/dist/protocol/handlers/function.d.ts +8 -0
- package/dist/protocol/handlers/shared.d.ts +31 -0
- package/dist/protocol/handlers/table-buffering.d.ts +3 -0
- package/dist/protocol/serialize.d.ts +4 -0
- package/dist/protocol/serializers/arguments.d.ts +3 -0
- package/dist/protocol/serializers/bind.d.ts +9 -0
- package/dist/protocol/serializers/cardinality.d.ts +7 -0
- package/dist/protocol/serializers/init.d.ts +9 -0
- package/dist/protocol/serializers/shared.d.ts +12 -0
- package/dist/protocol/state-serializer.d.ts +23 -0
- package/dist/protocol/types.d.ts +129 -0
- package/dist/secrets/helpers.d.ts +15 -0
- package/dist/types.d.ts +52 -0
- package/dist/util/arrow/index.d.ts +1 -0
- package/dist/util/bytes.d.ts +2 -0
- package/dist/util/statistics.d.ts +7 -0
- package/dist/worker-cf-entry.d.ts +30 -0
- package/dist/worker-cf-entry.js +8767 -0
- package/dist/worker-cf-entry.js.map +74 -0
- package/dist/worker.d.ts +36 -0
- package/package.json +96 -0
- package/src/arguments/argument-spec.ts +200 -0
- package/src/arguments/arguments.ts +81 -0
- package/src/arrow/codec/branded.ts +206 -0
- package/src/arrow/codec/registry.ts +674 -0
- package/src/arrow/codec/repr.ts +181 -0
- package/src/arrow/codec/type-descriptors.ts +137 -0
- package/src/arrow/impl-arrowjs/build.ts +88 -0
- package/src/arrow/impl-arrowjs/canonical.ts +509 -0
- package/src/arrow/impl-arrowjs/empty.ts +72 -0
- package/src/arrow/impl-arrowjs/filter.ts +53 -0
- package/src/arrow/impl-arrowjs/index.ts +54 -0
- package/src/arrow/impl-arrowjs/ipc.ts +67 -0
- package/src/arrow/impl-arrowjs/iterate.ts +159 -0
- package/src/arrow/impl-arrowjs/project.ts +73 -0
- package/src/arrow/impl-arrowjs/schema.ts +256 -0
- package/src/arrow/impl-arrowjs/statistics.ts +185 -0
- package/src/arrow/impl-flechette/build.ts +65 -0
- package/src/arrow/impl-flechette/canonical.ts +289 -0
- package/src/arrow/impl-flechette/empty.ts +20 -0
- package/src/arrow/impl-flechette/filter.ts +42 -0
- package/src/arrow/impl-flechette/index.ts +54 -0
- package/src/arrow/impl-flechette/ipc.ts +110 -0
- package/src/arrow/impl-flechette/iterate.ts +121 -0
- package/src/arrow/impl-flechette/message-meta.ts +175 -0
- package/src/arrow/impl-flechette/normalize-type.ts +99 -0
- package/src/arrow/impl-flechette/project.ts +64 -0
- package/src/arrow/impl-flechette/schema.ts +202 -0
- package/src/arrow/impl-flechette/statistics.ts +198 -0
- package/src/arrow/index.ts +129 -0
- package/src/arrow/predicates.ts +66 -0
- package/src/arrow/repr.test-d.ts +189 -0
- package/src/arrow/schema-types.ts +213 -0
- package/src/arrow/types.ts +74 -0
- package/src/catalog/attach-option.ts +107 -0
- package/src/catalog/attach-options.ts +79 -0
- package/src/catalog/composite.ts +180 -0
- package/src/catalog/descriptors.ts +164 -0
- package/src/catalog/interface.ts +721 -0
- package/src/catalog/read-only.ts +909 -0
- package/src/client/client.ts +1348 -0
- package/src/client/errors.ts +69 -0
- package/src/client/helpers.ts +56 -0
- package/src/client/protocol.ts +33 -0
- package/src/client/types.ts +140 -0
- package/src/client-entry.ts +112 -0
- package/src/codec/asd.ts +147 -0
- package/src/crypto.ts +143 -0
- package/src/errors.ts +61 -0
- package/src/filter-pushdown/collector.ts +147 -0
- package/src/filter-pushdown/deserialize.ts +253 -0
- package/src/filter-pushdown/evaluate.ts +591 -0
- package/src/filter-pushdown/index.ts +28 -0
- package/src/filter-pushdown/types.ts +91 -0
- package/src/functions/aggregate.ts +384 -0
- package/src/functions/copy-from.ts +282 -0
- package/src/functions/copy-to.ts +319 -0
- package/src/functions/registry.ts +258 -0
- package/src/functions/scalar.ts +356 -0
- package/src/functions/storage-cf-do.ts +303 -0
- package/src/functions/storage.ts +521 -0
- package/src/functions/table-buffering.ts +562 -0
- package/src/functions/table-in-out.ts +373 -0
- package/src/functions/table.ts +521 -0
- package/src/functions/types.ts +192 -0
- package/src/generated/vgi-client.ts +560 -0
- package/src/generated/vgi-protocol-schemas.ts +864 -0
- package/src/index.core.ts +267 -0
- package/src/index.ts +11 -0
- package/src/metadata/resolve.ts +109 -0
- package/src/metadata/serialize.ts +242 -0
- package/src/metadata/types.ts +60 -0
- package/src/protocol/dispatch.ts +52 -0
- package/src/protocol/handlers/aggregate.ts +355 -0
- package/src/protocol/handlers/catalog/admin.ts +368 -0
- package/src/protocol/handlers/catalog/index.ts +25 -0
- package/src/protocol/handlers/catalog/index_methods.ts +65 -0
- package/src/protocol/handlers/catalog/macro.ts +114 -0
- package/src/protocol/handlers/catalog/shared.ts +291 -0
- package/src/protocol/handlers/catalog/table.ts +401 -0
- package/src/protocol/handlers/catalog/view.ts +119 -0
- package/src/protocol/handlers/function.ts +302 -0
- package/src/protocol/handlers/shared.ts +80 -0
- package/src/protocol/handlers/table-buffering.ts +207 -0
- package/src/protocol/serialize.ts +23 -0
- package/src/protocol/serializers/arguments.ts +160 -0
- package/src/protocol/serializers/bind.ts +212 -0
- package/src/protocol/serializers/cardinality.ts +45 -0
- package/src/protocol/serializers/init.ts +247 -0
- package/src/protocol/serializers/shared.ts +46 -0
- package/src/protocol/state-serializer.ts +181 -0
- package/src/protocol/types.ts +153 -0
- package/src/secrets/helpers.ts +70 -0
- package/src/types.ts +71 -0
- package/src/util/arrow/index.ts +23 -0
- package/src/util/bytes.ts +14 -0
- package/src/util/statistics.ts +40 -0
- package/src/worker-cf-entry.ts +172 -0
- package/src/worker.ts +212 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
//
|
|
3
|
+
// Canonical Arrow I/O for the arrow-js backend.
|
|
4
|
+
//
|
|
5
|
+
// This is the ONLY place in the arrow-js backend that knows arrow-js-specific
|
|
6
|
+
// build/read details. Everything else routes values through the codec registry
|
|
7
|
+
// (rich <-> canonical) and then through here (canonical <-> arrow-js column
|
|
8
|
+
// data). The canonical representation is defined in ../codec/registry.ts and is
|
|
9
|
+
// byte-for-byte identical across the arrow-js and flechette backends, which is
|
|
10
|
+
// what keeps the two backends in agreement.
|
|
11
|
+
//
|
|
12
|
+
// Canonical units consumed/produced here:
|
|
13
|
+
// date32 -> number (days since epoch)
|
|
14
|
+
// date64 -> bigint (ms since epoch)
|
|
15
|
+
// time32 -> number (raw s/ms)
|
|
16
|
+
// time64 -> bigint (raw us/ns)
|
|
17
|
+
// timestamp -> bigint (raw unit)
|
|
18
|
+
// duration -> bigint (raw unit)
|
|
19
|
+
// decimal -> bigint (unscaled integer)
|
|
20
|
+
// int64/u64 -> bigint
|
|
21
|
+
// bool/int*/float* -> boolean/number
|
|
22
|
+
// utf8 -> string
|
|
23
|
+
// binary/fsb -> Uint8Array
|
|
24
|
+
// struct/list/map -> object / array / Array<[k,v]>
|
|
25
|
+
// dictionary -> decoded value's canonical
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
Struct,
|
|
29
|
+
List,
|
|
30
|
+
DataType,
|
|
31
|
+
DateUnit,
|
|
32
|
+
makeData,
|
|
33
|
+
vectorFromArray,
|
|
34
|
+
Map_,
|
|
35
|
+
type Field,
|
|
36
|
+
} from "@query-farm/apache-arrow";
|
|
37
|
+
import type { VgiDataType, VgiColumnData, TaggedUnion } from "../types.js";
|
|
38
|
+
|
|
39
|
+
// ===========================================================================
|
|
40
|
+
// WRITE: canonical[] -> arrow-js column Data
|
|
41
|
+
// ===========================================================================
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Build an arrow-js column `Data` node from an array of CANONICAL values for
|
|
45
|
+
* `type`. Folds together what used to be scattered across build.ts
|
|
46
|
+
* (`buildColumnData`, `dateTimeBitWidth`, decimal/bigint/date special-casing).
|
|
47
|
+
*/
|
|
48
|
+
export function writeCanonicalColumn(
|
|
49
|
+
type: VgiDataType,
|
|
50
|
+
canonical: unknown[],
|
|
51
|
+
): VgiColumnData {
|
|
52
|
+
return buildColumnData(canonical, type as DataType) as VgiColumnData;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function buildColumnData(values: unknown[], type: DataType): any {
|
|
56
|
+
if (DataType.isList(type)) return buildListData(values, type as List);
|
|
57
|
+
if (DataType.isMap(type)) return buildMapData(values, type as Map_);
|
|
58
|
+
if (DataType.isStruct(type)) return buildStructData(values, type as Struct);
|
|
59
|
+
if (DataType.isDecimal(type)) return buildDecimalData(values, type);
|
|
60
|
+
|
|
61
|
+
// date32: canonical = day-number (number/bigint). date64: canonical = ms
|
|
62
|
+
// (bigint). time32: number; time64/timestamp/duration: bigint. All of these
|
|
63
|
+
// are "raw unit" integers that arrow-js's vectorFromArray mishandles
|
|
64
|
+
// (silently zeros 32-bit Date/Time fed raw units; throws on nulls). Build
|
|
65
|
+
// every Date/Time/Timestamp/Duration column manually so it round-trips.
|
|
66
|
+
if (DataType.isDate(type) || DataType.isTime(type) ||
|
|
67
|
+
DataType.isTimestamp(type) || DataType.isDuration(type)) {
|
|
68
|
+
const bitWidth = dateTimeBitWidth(type);
|
|
69
|
+
if (bitWidth === 64) return buildBigIntData(values, type);
|
|
70
|
+
return buildInt32Data(values, type);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Int64 / Uint64: ensure bigint storage.
|
|
74
|
+
if (DataType.isInt(type) && (type as any).bitWidth === 64) {
|
|
75
|
+
return buildBigIntData(values, type);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Everything else (bool, int8..32, float, utf8, binary, fsb, dictionary):
|
|
79
|
+
// vectorFromArray handles canonical values directly.
|
|
80
|
+
return vectorFromArray(values as any[], type).data[0];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Storage bit width for a temporal type. */
|
|
84
|
+
function dateTimeBitWidth(type: any): number {
|
|
85
|
+
if (DataType.isDate(type)) return type.unit === DateUnit.DAY ? 32 : 64;
|
|
86
|
+
if (DataType.isTime(type)) return type.bitWidth ?? 32;
|
|
87
|
+
return 64; // Timestamp / Duration
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function buildInt32Data(values: unknown[], type: DataType): any {
|
|
91
|
+
const length = values.length;
|
|
92
|
+
const buf = new Int32Array(length);
|
|
93
|
+
const nullBitmap = new Uint8Array(Math.ceil(length / 8));
|
|
94
|
+
let nullCount = 0;
|
|
95
|
+
for (let i = 0; i < length; i++) {
|
|
96
|
+
const v = values[i];
|
|
97
|
+
if (v === null || v === undefined) { nullCount++; continue; }
|
|
98
|
+
nullBitmap[i >> 3] |= 1 << (i & 7);
|
|
99
|
+
buf[i] = typeof v === "bigint" ? Number(v) : (v as number);
|
|
100
|
+
}
|
|
101
|
+
return makeData({
|
|
102
|
+
type, length,
|
|
103
|
+
nullBitmap: nullCount > 0 ? nullBitmap : undefined,
|
|
104
|
+
data: buf, nullCount,
|
|
105
|
+
} as any);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildBigIntData(values: unknown[], type: DataType): any {
|
|
109
|
+
const length = values.length;
|
|
110
|
+
const bigBuf = new BigInt64Array(length);
|
|
111
|
+
const nullBitmap = new Uint8Array(Math.ceil(length / 8));
|
|
112
|
+
let nullCount = 0;
|
|
113
|
+
for (let i = 0; i < length; i++) {
|
|
114
|
+
const v = values[i];
|
|
115
|
+
if (v === null || v === undefined) { nullCount++; continue; }
|
|
116
|
+
nullBitmap[i >> 3] |= 1 << (i & 7);
|
|
117
|
+
bigBuf[i] = typeof v === "bigint" ? v : BigInt(v as number);
|
|
118
|
+
}
|
|
119
|
+
const data = new Int32Array(bigBuf.buffer);
|
|
120
|
+
return makeData({
|
|
121
|
+
type, length,
|
|
122
|
+
nullBitmap: nullCount > 0 ? nullBitmap : undefined,
|
|
123
|
+
data, nullCount,
|
|
124
|
+
} as any);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function buildDecimalData(values: unknown[], type: DataType): any {
|
|
128
|
+
const byteWidth: number = (type as any).byteWidth ?? ((type as any).bitWidth ? (type as any).bitWidth / 8 : 16);
|
|
129
|
+
const length = values.length;
|
|
130
|
+
const buf = new Uint8Array(length * byteWidth);
|
|
131
|
+
const nullBitmap = new Uint8Array(Math.ceil(length / 8));
|
|
132
|
+
let nullCount = 0;
|
|
133
|
+
for (let i = 0; i < length; i++) {
|
|
134
|
+
const val = values[i];
|
|
135
|
+
if (val === null || val === undefined) { nullCount++; continue; }
|
|
136
|
+
nullBitmap[i >> 3] |= 1 << (i & 7);
|
|
137
|
+
let bytes: Uint8Array;
|
|
138
|
+
if (val instanceof Uint8Array) {
|
|
139
|
+
bytes = val;
|
|
140
|
+
} else if (ArrayBuffer.isView(val)) {
|
|
141
|
+
const view = val as ArrayBufferView;
|
|
142
|
+
bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
143
|
+
} else {
|
|
144
|
+
bytes = bigIntToDecimalBytes(toBig(val), byteWidth);
|
|
145
|
+
}
|
|
146
|
+
buf.set(bytes.subarray(0, byteWidth), i * byteWidth);
|
|
147
|
+
}
|
|
148
|
+
return makeData({
|
|
149
|
+
type, length,
|
|
150
|
+
nullBitmap: nullCount > 0 ? nullBitmap : undefined,
|
|
151
|
+
data: new Uint32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4),
|
|
152
|
+
nullCount,
|
|
153
|
+
} as any);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function toBig(v: unknown): bigint {
|
|
157
|
+
return typeof v === "bigint" ? v : BigInt(Math.trunc(Number(v)));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Little-endian two's-complement bytes of an unscaled decimal bigint. */
|
|
161
|
+
function bigIntToDecimalBytes(value: bigint, byteWidth: number): Uint8Array {
|
|
162
|
+
const bytes = new Uint8Array(byteWidth);
|
|
163
|
+
let v = value;
|
|
164
|
+
if (v < 0n) v = (1n << BigInt(byteWidth * 8)) + v;
|
|
165
|
+
for (let i = 0; i < byteWidth; i++) {
|
|
166
|
+
bytes[i] = Number(v & 0xffn);
|
|
167
|
+
v >>= 8n;
|
|
168
|
+
}
|
|
169
|
+
return bytes;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function buildListData(values: unknown[], listType: List): any {
|
|
173
|
+
const childField = listType.children[0];
|
|
174
|
+
const numRows = values.length;
|
|
175
|
+
const offsets = new Int32Array(numRows + 1);
|
|
176
|
+
const allItems: unknown[] = [];
|
|
177
|
+
const nullBitmap = new Uint8Array(Math.ceil(numRows / 8));
|
|
178
|
+
let nullCount = 0;
|
|
179
|
+
for (let i = 0; i < numRows; i++) {
|
|
180
|
+
offsets[i] = allItems.length;
|
|
181
|
+
const row = values[i];
|
|
182
|
+
if (row == null) {
|
|
183
|
+
nullCount++;
|
|
184
|
+
} else {
|
|
185
|
+
nullBitmap[i >> 3] |= 1 << (i & 7);
|
|
186
|
+
const items = Array.isArray(row) ? row
|
|
187
|
+
: (typeof (row as any)[Symbol.iterator] === "function") ? Array.from(row as Iterable<unknown>)
|
|
188
|
+
: null;
|
|
189
|
+
if (items) for (const item of items) allItems.push(item);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
offsets[numRows] = allItems.length;
|
|
193
|
+
const childData = allItems.length > 0
|
|
194
|
+
? buildColumnData(allItems, childField.type)
|
|
195
|
+
: emptyDataForType(childField.type);
|
|
196
|
+
return makeData({
|
|
197
|
+
type: listType, length: numRows, nullCount,
|
|
198
|
+
nullBitmap: nullCount > 0 ? nullBitmap : undefined,
|
|
199
|
+
child: childData, offset: 0, valueOffsets: offsets,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function buildMapData(values: unknown[], mapType: Map_): any {
|
|
204
|
+
const numRows = values.length;
|
|
205
|
+
const entriesField = mapType.children[0];
|
|
206
|
+
const keyField = (entriesField.type as any).children[0] as Field;
|
|
207
|
+
const valueField = (entriesField.type as any).children[1] as Field;
|
|
208
|
+
const offsets = new Int32Array(numRows + 1);
|
|
209
|
+
const allKeys: unknown[] = [];
|
|
210
|
+
const allValues: unknown[] = [];
|
|
211
|
+
const nullBitmap = new Uint8Array(Math.ceil(numRows / 8));
|
|
212
|
+
let nullCount = 0;
|
|
213
|
+
for (let i = 0; i < numRows; i++) {
|
|
214
|
+
offsets[i] = allKeys.length;
|
|
215
|
+
const row = values[i];
|
|
216
|
+
if (row == null) {
|
|
217
|
+
nullCount++;
|
|
218
|
+
} else {
|
|
219
|
+
nullBitmap[i >> 3] |= 1 << (i & 7);
|
|
220
|
+
// Canonical map is Array<[k, v]>, but tolerate Map / iterable / object.
|
|
221
|
+
const pairs: Array<[unknown, unknown]> = Array.isArray(row) ? row as any
|
|
222
|
+
: row instanceof Map ? Array.from(row.entries())
|
|
223
|
+
: (typeof (row as any)[Symbol.iterator] === "function") ? Array.from(row as any)
|
|
224
|
+
: (typeof row === "object") ? Object.entries(row as any)
|
|
225
|
+
: [];
|
|
226
|
+
for (const [k, v] of pairs) { allKeys.push(k); allValues.push(v); }
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
offsets[numRows] = allKeys.length;
|
|
230
|
+
const keyData = allKeys.length > 0
|
|
231
|
+
? buildColumnData(allKeys, keyField.type)
|
|
232
|
+
: emptyDataForType(keyField.type);
|
|
233
|
+
const valueData = allValues.length > 0
|
|
234
|
+
? buildColumnData(allValues, valueField.type)
|
|
235
|
+
: emptyDataForType(valueField.type);
|
|
236
|
+
const entriesData = makeData({
|
|
237
|
+
type: entriesField.type, length: allKeys.length,
|
|
238
|
+
children: [keyData, valueData], nullCount: 0,
|
|
239
|
+
});
|
|
240
|
+
return makeData({
|
|
241
|
+
type: mapType, length: numRows, nullCount,
|
|
242
|
+
nullBitmap: nullCount > 0 ? nullBitmap : undefined,
|
|
243
|
+
child: entriesData, offset: 0, valueOffsets: offsets,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function buildStructData(values: unknown[], structType: Struct): any {
|
|
248
|
+
const childFields = structType.children;
|
|
249
|
+
const numRows = values.length;
|
|
250
|
+
const nullBitmap = new Uint8Array(Math.ceil(numRows / 8));
|
|
251
|
+
let nullCount = 0;
|
|
252
|
+
const childArrays: unknown[][] = childFields.map(() => []);
|
|
253
|
+
for (let i = 0; i < numRows; i++) {
|
|
254
|
+
const row = values[i] as any;
|
|
255
|
+
if (row == null) {
|
|
256
|
+
nullCount++;
|
|
257
|
+
for (let c = 0; c < childFields.length; c++) childArrays[c].push(null);
|
|
258
|
+
} else {
|
|
259
|
+
nullBitmap[i >> 3] |= 1 << (i & 7);
|
|
260
|
+
for (let c = 0; c < childFields.length; c++) {
|
|
261
|
+
const name = childFields[c].name;
|
|
262
|
+
childArrays[c].push(row[name] ?? row[c] ?? null);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const children = childFields.map((cf, idx) => buildColumnData(childArrays[idx], cf.type));
|
|
267
|
+
return makeData({
|
|
268
|
+
type: structType, length: numRows, nullCount,
|
|
269
|
+
nullBitmap: nullCount > 0 ? nullBitmap : undefined, children,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function emptyDataForType(type: DataType): any {
|
|
274
|
+
if (DataType.isList(type)) {
|
|
275
|
+
return makeData({
|
|
276
|
+
type, length: 0, nullCount: 0,
|
|
277
|
+
child: emptyDataForType((type as List).children[0].type),
|
|
278
|
+
valueOffsets: new Int32Array([0]),
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
if (DataType.isMap(type)) {
|
|
282
|
+
return makeData({
|
|
283
|
+
type, length: 0, nullCount: 0,
|
|
284
|
+
child: emptyDataForType((type as Map_).children[0].type),
|
|
285
|
+
valueOffsets: new Int32Array([0]),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
if (DataType.isStruct(type)) {
|
|
289
|
+
const children = (type as any).children.map((cf: Field) => emptyDataForType(cf.type));
|
|
290
|
+
return makeData({ type, length: 0, nullCount: 0, children });
|
|
291
|
+
}
|
|
292
|
+
return makeData({ type, length: 0, nullCount: 0 });
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ===========================================================================
|
|
296
|
+
// READ: arrow-js column + index -> canonical value
|
|
297
|
+
// ===========================================================================
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Read a single CANONICAL value at `index` from an arrow-js column (Vector).
|
|
301
|
+
* Reads the underlying typed-array storage for scalars (so it is lossless and
|
|
302
|
+
* never depends on Vector.get()'s lossy/divergent coercions — e.g. arrow-js
|
|
303
|
+
* Vector.get() returns a JS number for timestamp[us], losing precision), and
|
|
304
|
+
* recurses for composites.
|
|
305
|
+
*/
|
|
306
|
+
export function readCanonicalValue(
|
|
307
|
+
type: VgiDataType,
|
|
308
|
+
column: unknown,
|
|
309
|
+
index: number,
|
|
310
|
+
): unknown {
|
|
311
|
+
const col = column as any;
|
|
312
|
+
const data = col?.data?.[0];
|
|
313
|
+
return readFromData(type as DataType, col, data, index);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Validity check against a column/Data null bitmap. */
|
|
317
|
+
function isValid(col: any, data: any, index: number): boolean {
|
|
318
|
+
if (typeof col?.isValid === "function") return col.isValid(index);
|
|
319
|
+
const bitmap = data?.nullBitmap;
|
|
320
|
+
if (bitmap && bitmap.length > 0) {
|
|
321
|
+
return ((bitmap[index >> 3] >> (index & 7)) & 1) === 1;
|
|
322
|
+
}
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function readFromData(type: DataType, col: any, data: any, index: number): unknown {
|
|
327
|
+
if (!isValid(col, data, index)) return null;
|
|
328
|
+
const tid = type.typeId;
|
|
329
|
+
|
|
330
|
+
switch (tid) {
|
|
331
|
+
case 1: // Null
|
|
332
|
+
return null;
|
|
333
|
+
case 6: // Bool
|
|
334
|
+
return col.get(index) as boolean;
|
|
335
|
+
case 2: { // Int
|
|
336
|
+
const bw = (type as any).bitWidth ?? 32;
|
|
337
|
+
const v = data?.values?.[index];
|
|
338
|
+
if (bw === 64) return typeof v === "bigint" ? v : BigInt(v);
|
|
339
|
+
return Number(v);
|
|
340
|
+
}
|
|
341
|
+
case 3: // Float
|
|
342
|
+
return Number(data?.values?.[index]);
|
|
343
|
+
case 5: // Utf8
|
|
344
|
+
case 20: // LargeUtf8
|
|
345
|
+
return String(col.get(index));
|
|
346
|
+
case 4: // Binary
|
|
347
|
+
case 19: // LargeBinary
|
|
348
|
+
case 15: // FixedSizeBinary
|
|
349
|
+
return toUint8(col.get(index));
|
|
350
|
+
case 7: // Decimal -> unscaled bigint
|
|
351
|
+
return decimalToBigInt(type, data, index);
|
|
352
|
+
case 8: { // Date
|
|
353
|
+
const v = data?.values?.[index];
|
|
354
|
+
// DAY=0 -> date32 storage = Int32 day-number (canonical number)
|
|
355
|
+
// MILLISECOND=1 -> date64 storage = BigInt64 ms (canonical bigint)
|
|
356
|
+
if ((type as any).unit === DateUnit.DAY) return Number(v);
|
|
357
|
+
return typeof v === "bigint" ? v : BigInt(v);
|
|
358
|
+
}
|
|
359
|
+
case 9: { // Time
|
|
360
|
+
const v = data?.values?.[index];
|
|
361
|
+
const bw = (type as any).bitWidth ?? 32;
|
|
362
|
+
if (bw === 64) return typeof v === "bigint" ? v : BigInt(v);
|
|
363
|
+
return Number(v);
|
|
364
|
+
}
|
|
365
|
+
case 10: // Timestamp
|
|
366
|
+
case 18: { // Duration
|
|
367
|
+
const v = data?.values?.[index];
|
|
368
|
+
return typeof v === "bigint" ? v : BigInt(v);
|
|
369
|
+
}
|
|
370
|
+
case 13: // Struct
|
|
371
|
+
return readStruct(type, col, index);
|
|
372
|
+
case 14: // Union -> TaggedUnion { tag, value }
|
|
373
|
+
return readUnion(type, col, data, index);
|
|
374
|
+
case 12: // List
|
|
375
|
+
case 16: // FixedSizeList
|
|
376
|
+
return readList(type, col, data, index);
|
|
377
|
+
case 17: // Map
|
|
378
|
+
return readMap(type, col, data, index);
|
|
379
|
+
case -1: // Dictionary
|
|
380
|
+
return readDictionary(type, col, data, index);
|
|
381
|
+
default:
|
|
382
|
+
return col.get(index);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function toUint8(v: any): Uint8Array | null {
|
|
387
|
+
if (v == null) return null;
|
|
388
|
+
if (v instanceof Uint8Array) return v;
|
|
389
|
+
if (ArrayBuffer.isView(v)) {
|
|
390
|
+
const view = v as ArrayBufferView;
|
|
391
|
+
return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
392
|
+
}
|
|
393
|
+
return v;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function decimalToBigInt(type: DataType, data: any, index: number): bigint {
|
|
397
|
+
const byteWidth: number = (type as any).byteWidth ?? ((type as any).bitWidth ? (type as any).bitWidth / 8 : 16);
|
|
398
|
+
// arrow-js stores decimals as Uint32Array; slice the element's bytes.
|
|
399
|
+
const u32: Uint32Array = data.values;
|
|
400
|
+
const wordsPerElem = byteWidth / 4;
|
|
401
|
+
const start = index * wordsPerElem;
|
|
402
|
+
const u8 = new Uint8Array(byteWidth);
|
|
403
|
+
for (let w = 0; w < wordsPerElem; w++) {
|
|
404
|
+
const word = u32[start + w] >>> 0;
|
|
405
|
+
u8[w * 4] = word & 0xff;
|
|
406
|
+
u8[w * 4 + 1] = (word >>> 8) & 0xff;
|
|
407
|
+
u8[w * 4 + 2] = (word >>> 16) & 0xff;
|
|
408
|
+
u8[w * 4 + 3] = (word >>> 24) & 0xff;
|
|
409
|
+
}
|
|
410
|
+
return decimalBytesToBigInt(u8);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** Little-endian two's-complement bytes -> signed bigint. */
|
|
414
|
+
function decimalBytesToBigInt(u8: Uint8Array): bigint {
|
|
415
|
+
let bi = 0n;
|
|
416
|
+
for (let i = u8.length - 1; i >= 0; i--) bi = (bi << 8n) | BigInt(u8[i]);
|
|
417
|
+
if (u8[u8.length - 1] & 0x80) bi -= 1n << BigInt(u8.length * 8);
|
|
418
|
+
return bi;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function readStruct(type: DataType, col: any, index: number): Record<string, unknown> | null {
|
|
422
|
+
const children = (type as any).children as Field[];
|
|
423
|
+
const out: Record<string, unknown> = {};
|
|
424
|
+
for (let c = 0; c < children.length; c++) {
|
|
425
|
+
const cf = children[c];
|
|
426
|
+
const childVec = typeof col.getChildAt === "function"
|
|
427
|
+
? col.getChildAt(c)
|
|
428
|
+
: (col.children?.[c]);
|
|
429
|
+
if (!childVec) { out[cf.name] = null; continue; }
|
|
430
|
+
out[cf.name] = readCanonicalValue(cf.type as VgiDataType, childVec, index);
|
|
431
|
+
}
|
|
432
|
+
return out;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function readUnion(type: DataType, col: any, data: any, index: number): TaggedUnion {
|
|
436
|
+
const children = (type as any).children as Field[];
|
|
437
|
+
// Per-row type code -> child index. DuckDB emits SPARSE unions, where every
|
|
438
|
+
// child is full-length and aligned with the parent row.
|
|
439
|
+
const typeIds: ArrayLike<number> | undefined = data?.typeIds;
|
|
440
|
+
const tcode = typeIds ? typeIds[index] : children.length > 0 ? 0 : -1;
|
|
441
|
+
const map = (type as any).typeIdToChildIndex as Record<number, number> | undefined;
|
|
442
|
+
const childIdx = map && map[tcode] !== undefined ? map[tcode] : tcode;
|
|
443
|
+
const childField = children[childIdx];
|
|
444
|
+
if (!childField) return { tag: null, value: null };
|
|
445
|
+
// Dense unions carry an offsets buffer remapping the parent row into the
|
|
446
|
+
// child; sparse unions read the child at the same row.
|
|
447
|
+
const offsets: ArrayLike<number> | undefined = data?.valueOffsets;
|
|
448
|
+
const childRow = offsets ? offsets[index] : index;
|
|
449
|
+
const childVec = typeof col.getChildAt === "function"
|
|
450
|
+
? col.getChildAt(childIdx)
|
|
451
|
+
: col.children?.[childIdx];
|
|
452
|
+
const value = childVec
|
|
453
|
+
? readCanonicalValue(childField.type as VgiDataType, childVec, childRow)
|
|
454
|
+
: null;
|
|
455
|
+
return { tag: childField.name, value };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function readList(type: DataType, col: any, data: any, index: number): unknown[] {
|
|
459
|
+
const childField = (type as any).children[0] as Field;
|
|
460
|
+
const childVec = typeof col.getChildAt === "function" ? col.getChildAt(0) : col.children?.[0];
|
|
461
|
+
const offsets = data?.valueOffsets;
|
|
462
|
+
let start: number, end: number;
|
|
463
|
+
if (offsets) {
|
|
464
|
+
start = offsets[index];
|
|
465
|
+
end = offsets[index + 1];
|
|
466
|
+
} else {
|
|
467
|
+
// FixedSizeList
|
|
468
|
+
const listSize = (type as any).listSize ?? 0;
|
|
469
|
+
start = index * listSize;
|
|
470
|
+
end = start + listSize;
|
|
471
|
+
}
|
|
472
|
+
const out: unknown[] = [];
|
|
473
|
+
for (let i = start; i < end; i++) {
|
|
474
|
+
out.push(readCanonicalValue(childField.type as VgiDataType, childVec, i));
|
|
475
|
+
}
|
|
476
|
+
return out;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function readMap(type: DataType, col: any, data: any, index: number): Array<[unknown, unknown]> {
|
|
480
|
+
const entriesField = (type as any).children[0] as Field;
|
|
481
|
+
const keyField = (entriesField.type as any).children[0] as Field;
|
|
482
|
+
const valueField = (entriesField.type as any).children[1] as Field;
|
|
483
|
+
const offsets = data?.valueOffsets;
|
|
484
|
+
const entriesVec = typeof col.getChildAt === "function" ? col.getChildAt(0) : col.children?.[0];
|
|
485
|
+
const keyVec = entriesVec && (typeof entriesVec.getChildAt === "function" ? entriesVec.getChildAt(0) : entriesVec.children?.[0]);
|
|
486
|
+
const valueVec = entriesVec && (typeof entriesVec.getChildAt === "function" ? entriesVec.getChildAt(1) : entriesVec.children?.[1]);
|
|
487
|
+
const start = offsets ? offsets[index] : 0;
|
|
488
|
+
const end = offsets ? offsets[index + 1] : 0;
|
|
489
|
+
const out: Array<[unknown, unknown]> = [];
|
|
490
|
+
for (let i = start; i < end; i++) {
|
|
491
|
+
out.push([
|
|
492
|
+
readCanonicalValue(keyField.type as VgiDataType, keyVec, i),
|
|
493
|
+
readCanonicalValue(valueField.type as VgiDataType, valueVec, i),
|
|
494
|
+
]);
|
|
495
|
+
}
|
|
496
|
+
return out;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function readDictionary(type: DataType, col: any, data: any, index: number): unknown {
|
|
500
|
+
const valueType = (type as any).dictionary as VgiDataType;
|
|
501
|
+
if (data && data.dictionary && data.values) {
|
|
502
|
+
const idx = Number(data.values[index]);
|
|
503
|
+
const dictVec = data.dictionary;
|
|
504
|
+
// Recurse so the decoded value is itself canonicalized.
|
|
505
|
+
return readCanonicalValue(valueType, dictVec, idx);
|
|
506
|
+
}
|
|
507
|
+
// Fallback: plain get.
|
|
508
|
+
return col.get(index);
|
|
509
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// Empty (0-row) Arrow batch construction. Handles complex types (List, Map,
|
|
3
|
+
// Struct) that need child data to round-trip through Arrow IPC serialization.
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
RecordBatch,
|
|
7
|
+
Schema,
|
|
8
|
+
Field,
|
|
9
|
+
Struct,
|
|
10
|
+
List,
|
|
11
|
+
DataType,
|
|
12
|
+
makeData,
|
|
13
|
+
Map_,
|
|
14
|
+
} from "@query-farm/apache-arrow";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create empty (0-length) data for any Arrow type, handling complex types
|
|
18
|
+
* that need child data (List, Map, Struct) to be serializable.
|
|
19
|
+
*/
|
|
20
|
+
function emptyData(field: Field): any {
|
|
21
|
+
const type = field.type;
|
|
22
|
+
|
|
23
|
+
if (DataType.isList(type)) {
|
|
24
|
+
const childField = (type as List).children[0];
|
|
25
|
+
return makeData({
|
|
26
|
+
type,
|
|
27
|
+
length: 0,
|
|
28
|
+
nullCount: 0,
|
|
29
|
+
child: emptyData(childField),
|
|
30
|
+
valueOffsets: new Int32Array([0]),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (DataType.isMap(type)) {
|
|
35
|
+
const entriesField = (type as Map_).children[0];
|
|
36
|
+
return makeData({
|
|
37
|
+
type,
|
|
38
|
+
length: 0,
|
|
39
|
+
nullCount: 0,
|
|
40
|
+
child: emptyData(entriesField),
|
|
41
|
+
valueOffsets: new Int32Array([0]),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (DataType.isStruct(type)) {
|
|
46
|
+
const children = type.children.map((child: Field) => emptyData(child));
|
|
47
|
+
return makeData({ type, length: 0, nullCount: 0, children });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return makeData({ type, length: 0, nullCount: 0 });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
import type { VgiSchema } from "../types.js";
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create an empty (0-row) batch with the given schema. Accepts arrow-js
|
|
57
|
+
* `Schema` or facade `VgiSchema` (cast at the boundary).
|
|
58
|
+
*/
|
|
59
|
+
export function emptyBatch(schema: Schema | VgiSchema): RecordBatch {
|
|
60
|
+
const a = schema as Schema;
|
|
61
|
+
const children = a.fields.map((f: Field) => emptyData(f));
|
|
62
|
+
|
|
63
|
+
const structType = new Struct(a.fields);
|
|
64
|
+
const data = makeData({
|
|
65
|
+
type: structType,
|
|
66
|
+
length: 0,
|
|
67
|
+
children,
|
|
68
|
+
nullCount: 0,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return new RecordBatch(a, data);
|
|
72
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// Mask-based row filtering for Arrow RecordBatches.
|
|
3
|
+
|
|
4
|
+
import { RecordBatch } from "@query-farm/apache-arrow";
|
|
5
|
+
import type { VgiBatch, VgiDataType } from "../types.js";
|
|
6
|
+
import { emptyBatch } from "./empty.js";
|
|
7
|
+
import { batchFromColumns } from "./build.js";
|
|
8
|
+
import { codecFor } from "../codec/registry.js";
|
|
9
|
+
import { readCanonicalValue } from "./canonical.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Filter a RecordBatch using a Uint8Array mask (0=exclude, nonzero=include).
|
|
13
|
+
* Returns a new batch containing only the rows where mask[i] is nonzero.
|
|
14
|
+
*
|
|
15
|
+
* Rows are read in canonical form then mapped back to RICH so the rebuild goes
|
|
16
|
+
* through the same codec/canonical path as every other column build — lossless
|
|
17
|
+
* and identical across backends (never the lossy/raw `Vector.get`).
|
|
18
|
+
*/
|
|
19
|
+
export function filterBatch(
|
|
20
|
+
batch: RecordBatch | VgiBatch,
|
|
21
|
+
mask: Uint8Array,
|
|
22
|
+
): RecordBatch {
|
|
23
|
+
const a = batch as RecordBatch;
|
|
24
|
+
const n = a.numRows;
|
|
25
|
+
|
|
26
|
+
// Count passing rows
|
|
27
|
+
let passCount = 0;
|
|
28
|
+
for (let i = 0; i < n; i++) {
|
|
29
|
+
if (mask[i]) passCount++;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Fast paths
|
|
33
|
+
if (passCount === n) return a;
|
|
34
|
+
if (passCount === 0) return emptyBatch(a.schema);
|
|
35
|
+
|
|
36
|
+
// Collect passing indices
|
|
37
|
+
const indices: number[] = [];
|
|
38
|
+
for (let i = 0; i < n; i++) {
|
|
39
|
+
if (mask[i]) indices.push(i);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Rebuild batch with only passing rows (canonical read -> rich -> rebuild).
|
|
43
|
+
const columns: Record<string, any[]> = {};
|
|
44
|
+
for (const field of a.schema.fields) {
|
|
45
|
+
const col = a.getChild(field.name)!;
|
|
46
|
+
const type = field.type as unknown as VgiDataType;
|
|
47
|
+
const codec = codecFor(type);
|
|
48
|
+
columns[field.name] = indices.map((i) =>
|
|
49
|
+
codec.canonicalToRich(readCanonicalValue(type, col, i)),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return batchFromColumns(columns, a.schema);
|
|
53
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// arrow-js backend for the vgi-typescript Arrow facade.
|
|
3
|
+
// Default backend (Bun worker, Node) — selected via `#arrow-impl` resolution
|
|
4
|
+
// when no `worker`/`workerd`/`browser` condition is active.
|
|
5
|
+
|
|
6
|
+
import type { VgiBackendInfo } from "../types.js";
|
|
7
|
+
|
|
8
|
+
export const backend: VgiBackendInfo = { name: "arrow-js" };
|
|
9
|
+
|
|
10
|
+
// Schema / Field / DataType factories
|
|
11
|
+
export {
|
|
12
|
+
schema,
|
|
13
|
+
field,
|
|
14
|
+
nullType,
|
|
15
|
+
bool,
|
|
16
|
+
int,
|
|
17
|
+
int8, int16, int32, int64,
|
|
18
|
+
uint8, uint16, uint32, uint64,
|
|
19
|
+
float16, float32, float64,
|
|
20
|
+
utf8, binary, fixedSizeBinary,
|
|
21
|
+
decimal, decimal128, decimal256,
|
|
22
|
+
date, dateDay, dateMillisecond,
|
|
23
|
+
time, timeSecond, timeMillisecond, timeMicrosecond, timeNanosecond,
|
|
24
|
+
timestamp, duration, interval,
|
|
25
|
+
list, fixedSizeList, struct, map,
|
|
26
|
+
dictionary,
|
|
27
|
+
union, sparseUnion, denseUnion,
|
|
28
|
+
TimeUnit, DateUnit, IntervalUnit, UnionMode,
|
|
29
|
+
} from "./schema.js";
|
|
30
|
+
|
|
31
|
+
// Batch construction / iteration / utilities
|
|
32
|
+
export { emptyBatch } from "./empty.js";
|
|
33
|
+
export { batchFromRows, batchFromColumns, columnFromArray } from "./build.js";
|
|
34
|
+
export {
|
|
35
|
+
iterRows,
|
|
36
|
+
batchToScalarDict,
|
|
37
|
+
batchToSecretDict,
|
|
38
|
+
safeNumber,
|
|
39
|
+
decodeDictValue,
|
|
40
|
+
} from "./iterate.js";
|
|
41
|
+
export { filterBatch } from "./filter.js";
|
|
42
|
+
export { projectSchema, projectBatch } from "./project.js";
|
|
43
|
+
export { readCanonicalValue } from "./canonical.js";
|
|
44
|
+
|
|
45
|
+
// IPC
|
|
46
|
+
export {
|
|
47
|
+
serializeSchema,
|
|
48
|
+
deserializeSchema,
|
|
49
|
+
serializeBatch,
|
|
50
|
+
deserializeBatch,
|
|
51
|
+
} from "./ipc.js";
|
|
52
|
+
|
|
53
|
+
// Column statistics (sparse-union heavy — backend-specific).
|
|
54
|
+
export { buildStatisticsBatch, type ColumnStatistics } from "./statistics.js";
|