@laserdata/laser-sdk 0.1.1 → 0.2.1
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/README.md +65 -5
- package/dist/agent/reliable-consumer.d.ts.map +1 -1
- package/dist/agent/reliable-consumer.js +1 -0
- package/dist/agent/reliable-consumer.js.map +1 -1
- package/dist/agent/workflow.d.ts +2 -0
- package/dist/agent/workflow.d.ts.map +1 -1
- package/dist/agent/workflow.js +114 -13
- package/dist/agent/workflow.js.map +1 -1
- package/dist/bridges/a2a.d.ts +1 -1
- package/dist/bridges/a2a.js +1 -1
- package/dist/client/capabilities.d.ts +14 -1
- package/dist/client/capabilities.d.ts.map +1 -1
- package/dist/client/capabilities.js +57 -23
- package/dist/client/capabilities.js.map +1 -1
- package/dist/client/errors.d.ts +6 -1
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +5 -0
- package/dist/client/errors.js.map +1 -1
- package/dist/client/laser.d.ts +8 -0
- package/dist/client/laser.d.ts.map +1 -1
- package/dist/client/laser.js +42 -10
- package/dist/client/laser.js.map +1 -1
- package/dist/client/managed.d.ts +3 -1
- package/dist/client/managed.d.ts.map +1 -1
- package/dist/client/managed.js +2 -2
- package/dist/client/managed.js.map +1 -1
- package/dist/full.d.ts +1 -0
- package/dist/full.d.ts.map +1 -1
- package/dist/full.js +1 -0
- package/dist/full.js.map +1 -1
- package/dist/iggy/apache-iggy.d.ts +6 -2
- package/dist/iggy/apache-iggy.d.ts.map +1 -1
- package/dist/iggy/apache-iggy.js +11 -10
- package/dist/iggy/apache-iggy.js.map +1 -1
- package/dist/index.d.ts +11 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/managed/batch.d.ts.map +1 -1
- package/dist/managed/batch.js +23 -2
- package/dist/managed/batch.js.map +1 -1
- package/dist/managed/destinations.d.ts +19 -0
- package/dist/managed/destinations.d.ts.map +1 -0
- package/dist/managed/destinations.js +90 -0
- package/dist/managed/destinations.js.map +1 -0
- package/dist/managed/kv.d.ts +33 -6
- package/dist/managed/kv.d.ts.map +1 -1
- package/dist/managed/kv.js +99 -17
- package/dist/managed/kv.js.map +1 -1
- package/dist/managed/query.d.ts +26 -19
- package/dist/managed/query.d.ts.map +1 -1
- package/dist/managed/query.js +97 -55
- package/dist/managed/query.js.map +1 -1
- package/dist/stream/publish.d.ts +3 -0
- package/dist/stream/publish.d.ts.map +1 -1
- package/dist/stream/publish.js +25 -0
- package/dist/stream/publish.js.map +1 -1
- package/dist/stream/record.d.ts +4 -1
- package/dist/stream/record.d.ts.map +1 -1
- package/dist/stream/record.js +34 -3
- package/dist/stream/record.js.map +1 -1
- package/dist/wire/arrow.d.ts +39 -0
- package/dist/wire/arrow.d.ts.map +1 -0
- package/dist/wire/arrow.js +133 -0
- package/dist/wire/arrow.js.map +1 -0
- package/dist/wire/authz.d.ts +2 -2
- package/dist/wire/authz.d.ts.map +1 -1
- package/dist/wire/authz.js +39 -20
- package/dist/wire/authz.js.map +1 -1
- package/dist/wire/cbor.d.ts +9 -1
- package/dist/wire/cbor.d.ts.map +1 -1
- package/dist/wire/cbor.js +45 -2
- package/dist/wire/cbor.js.map +1 -1
- package/dist/wire/checkpoint.d.ts +386 -0
- package/dist/wire/checkpoint.d.ts.map +1 -0
- package/dist/wire/checkpoint.js +1373 -0
- package/dist/wire/checkpoint.js.map +1 -0
- package/dist/wire/codes.d.ts +10 -0
- package/dist/wire/codes.d.ts.map +1 -1
- package/dist/wire/codes.js +14 -0
- package/dist/wire/codes.js.map +1 -1
- package/dist/wire/commands.d.ts +14 -5
- package/dist/wire/commands.d.ts.map +1 -1
- package/dist/wire/commands.js +75 -7
- package/dist/wire/commands.js.map +1 -1
- package/dist/wire/control.d.ts +3 -12
- package/dist/wire/control.d.ts.map +1 -1
- package/dist/wire/control.js +10 -43
- package/dist/wire/control.js.map +1 -1
- package/dist/wire/destination.d.ts +86 -0
- package/dist/wire/destination.d.ts.map +1 -0
- package/dist/wire/destination.js +251 -0
- package/dist/wire/destination.js.map +1 -0
- package/dist/wire/headers.d.ts +1 -0
- package/dist/wire/headers.d.ts.map +1 -1
- package/dist/wire/headers.js +1 -0
- package/dist/wire/headers.js.map +1 -1
- package/dist/wire/hello.d.ts +83 -11
- package/dist/wire/hello.d.ts.map +1 -1
- package/dist/wire/hello.js +292 -73
- package/dist/wire/hello.js.map +1 -1
- package/dist/wire/http.d.ts +153 -1
- package/dist/wire/http.d.ts.map +1 -1
- package/dist/wire/http.js +491 -10
- package/dist/wire/http.js.map +1 -1
- package/dist/wire/ids.d.ts +60 -0
- package/dist/wire/ids.d.ts.map +1 -1
- package/dist/wire/ids.js +143 -0
- package/dist/wire/ids.js.map +1 -1
- package/dist/wire/index.d.ts +5 -0
- package/dist/wire/index.d.ts.map +1 -1
- package/dist/wire/index.js +5 -0
- package/dist/wire/index.js.map +1 -1
- package/dist/wire/kv.d.ts +35 -0
- package/dist/wire/kv.d.ts.map +1 -1
- package/dist/wire/kv.js +178 -18
- package/dist/wire/kv.js.map +1 -1
- package/dist/wire/limits.d.ts +10 -0
- package/dist/wire/limits.d.ts.map +1 -1
- package/dist/wire/limits.js +10 -0
- package/dist/wire/limits.js.map +1 -1
- package/dist/wire/mutation.d.ts +10 -0
- package/dist/wire/mutation.d.ts.map +1 -1
- package/dist/wire/mutation.js +14 -0
- package/dist/wire/mutation.js.map +1 -1
- package/dist/wire/query.d.ts +192 -97
- package/dist/wire/query.d.ts.map +1 -1
- package/dist/wire/query.js +968 -443
- package/dist/wire/query.js.map +1 -1
- package/dist/wire/result.d.ts +6 -0
- package/dist/wire/result.d.ts.map +1 -1
- package/dist/wire/result.js +19 -2
- package/dist/wire/result.js.map +1 -1
- package/dist/wire/schema.d.ts +121 -0
- package/dist/wire/schema.d.ts.map +1 -0
- package/dist/wire/schema.js +778 -0
- package/dist/wire/schema.js.map +1 -0
- package/dist/wire/source.d.ts +35 -0
- package/dist/wire/source.d.ts.map +1 -0
- package/dist/wire/source.js +105 -0
- package/dist/wire/source.js.map +1 -0
- package/package.json +5 -5
package/dist/wire/query.js
CHANGED
|
@@ -1,48 +1,79 @@
|
|
|
1
|
-
import { CodecError } from "../client/errors.js";
|
|
2
|
-
import { encodeNamed, expectMap, expectString, field, singleVariantTag } from "./cbor.js";
|
|
1
|
+
import { CodecError, InvalidError } from "../client/errors.js";
|
|
2
|
+
import { decodeOne, encodeNamed, expectMap, expectString, field, singleVariantTag } from "./cbor.js";
|
|
3
|
+
import { BackendResourceId, DestinationId, QueryExecutionId } from "./ids.js";
|
|
4
|
+
import { decodeLogicalField, decodeTypedValue, encodeLogicalField, encodeTypedValue, validateResultFields, validateTypedValueAgainst, validateTypedValue } from "./schema.js";
|
|
5
|
+
import { MAX_PAGE_SIZE, MAX_QUERY_CURSOR_BYTES, MAX_QUERY_FIELDS, MAX_QUERY_NAME_BYTES, MAX_QUERY_PARAMETERS, MAX_QUERY_PREDICATES, MAX_RAW_SQL_BYTES, MAX_TEXT_QUERY_BYTES, MAX_VECTOR_DIMENSIONS } from "./limits.js";
|
|
3
6
|
import { QUERY_OP_VERSION } from "./codes.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
["
|
|
8
|
-
["
|
|
7
|
+
export function encodeQueryPageEnvelope(value) {
|
|
8
|
+
return mapOf([
|
|
9
|
+
["v", value.v],
|
|
10
|
+
["execution_id", value.executionId.toBytes()],
|
|
11
|
+
["cursor", value.cursor],
|
|
12
|
+
["deadline_micros", value.deadlineMicros]
|
|
9
13
|
]);
|
|
10
14
|
}
|
|
11
|
-
export function
|
|
15
|
+
export function encodeQueryCancelEnvelope(value) {
|
|
16
|
+
return mapOf([
|
|
17
|
+
["v", value.v],
|
|
18
|
+
["execution_id", value.executionId.toBytes()]
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
export function encodeQueryStatusEnvelope(value) {
|
|
22
|
+
return mapOf([
|
|
23
|
+
["v", value.v],
|
|
24
|
+
["execution_id", value.executionId.toBytes()]
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
export function decodeQueryPageEnvelope(map, context) {
|
|
12
28
|
return {
|
|
13
|
-
|
|
14
|
-
|
|
29
|
+
v: field.requiredU32(map, "v", context),
|
|
30
|
+
executionId: QueryExecutionId.fromBytes(field.requiredBytes(map, "execution_id", context)),
|
|
31
|
+
cursor: field.requiredString(map, "cursor", context),
|
|
32
|
+
deadlineMicros: field.requiredU64(map, "deadline_micros", context)
|
|
15
33
|
};
|
|
16
34
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"gt",
|
|
23
|
-
"gte",
|
|
24
|
-
"in",
|
|
25
|
-
"contains",
|
|
26
|
-
"prefix"
|
|
27
|
-
]);
|
|
28
|
-
function parseCmpOp(word, context) {
|
|
29
|
-
if (!CMP_OPS.has(word)) {
|
|
30
|
-
throw new CodecError(`\`${word}\` is not a recognized comparison operator`, context, "op");
|
|
31
|
-
}
|
|
32
|
-
return word;
|
|
35
|
+
export function decodeQueryCancelEnvelope(map, context) {
|
|
36
|
+
return {
|
|
37
|
+
v: field.requiredU32(map, "v", context),
|
|
38
|
+
executionId: QueryExecutionId.fromBytes(field.requiredBytes(map, "execution_id", context))
|
|
39
|
+
};
|
|
33
40
|
}
|
|
34
|
-
export function
|
|
35
|
-
return
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
export function decodeQueryStatusEnvelope(map, context) {
|
|
42
|
+
return {
|
|
43
|
+
v: field.requiredU32(map, "v", context),
|
|
44
|
+
executionId: QueryExecutionId.fromBytes(field.requiredBytes(map, "execution_id", context))
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function encodeQueryPageEnvelopeFrame(value) {
|
|
48
|
+
return encodeNamed(encodeQueryPageEnvelope(value));
|
|
49
|
+
}
|
|
50
|
+
export function encodeQueryCancelEnvelopeFrame(value) {
|
|
51
|
+
return encodeNamed(encodeQueryCancelEnvelope(value));
|
|
52
|
+
}
|
|
53
|
+
export function encodeQueryStatusEnvelopeFrame(value) {
|
|
54
|
+
return encodeNamed(encodeQueryStatusEnvelope(value));
|
|
40
55
|
}
|
|
41
|
-
export function
|
|
56
|
+
export function decodeQueryPageEnvelopeFrame(bytes) {
|
|
57
|
+
const context = "QueryPageEnvelope";
|
|
58
|
+
return decodeQueryPageEnvelope(expectMap(decodeOne(bytes, context), context), context);
|
|
59
|
+
}
|
|
60
|
+
export function decodeQueryCancelEnvelopeFrame(bytes) {
|
|
61
|
+
const context = "QueryCancelEnvelope";
|
|
62
|
+
return decodeQueryCancelEnvelope(expectMap(decodeOne(bytes, context), context), context);
|
|
63
|
+
}
|
|
64
|
+
export function decodeQueryStatusEnvelopeFrame(bytes) {
|
|
65
|
+
const context = "QueryStatusEnvelope";
|
|
66
|
+
return decodeQueryStatusEnvelope(expectMap(decodeOne(bytes, context), context), context);
|
|
67
|
+
}
|
|
68
|
+
export function operationalTarget(index) {
|
|
69
|
+
return { kind: "operational", index };
|
|
70
|
+
}
|
|
71
|
+
export function lakehouseTarget(destinationId, destinationGeneration, snapshot) {
|
|
42
72
|
return {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
73
|
+
kind: "lakehouse",
|
|
74
|
+
destinationId,
|
|
75
|
+
destinationGeneration,
|
|
76
|
+
...(snapshot === undefined ? {} : { snapshot })
|
|
46
77
|
};
|
|
47
78
|
}
|
|
48
79
|
export function filterAll(filters) {
|
|
@@ -57,542 +88,1036 @@ export function filterNegate(filter) {
|
|
|
57
88
|
export function filterPred(fieldName, op, value) {
|
|
58
89
|
return { kind: "pred", predicate: { field: fieldName, op, value } };
|
|
59
90
|
}
|
|
60
|
-
export function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
91
|
+
export function newQuery(target, executionId, deadlineMicros) {
|
|
92
|
+
return {
|
|
93
|
+
executionId,
|
|
94
|
+
target,
|
|
95
|
+
deadlineMicros,
|
|
96
|
+
byKey: [],
|
|
97
|
+
order: [],
|
|
98
|
+
page: { limit: 50, offset: 0n, wantTotal: false },
|
|
99
|
+
distinct: false,
|
|
100
|
+
select: { fields: [], payload: false },
|
|
101
|
+
consistency: "eventual"
|
|
102
|
+
};
|
|
71
103
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
104
|
+
function optional(map, name, value, encode = (item) => item) {
|
|
105
|
+
if (value !== undefined)
|
|
106
|
+
map.set(name, encode(value));
|
|
107
|
+
}
|
|
108
|
+
function mapOf(entries) {
|
|
109
|
+
return new Map(entries);
|
|
110
|
+
}
|
|
111
|
+
function idBytes(id) {
|
|
112
|
+
return id.toBytes();
|
|
113
|
+
}
|
|
114
|
+
export function encodeQueryTarget(target) {
|
|
115
|
+
const map = new Map([["kind", target.kind]]);
|
|
116
|
+
if (target.kind === "operational")
|
|
117
|
+
map.set("index", target.index);
|
|
118
|
+
else {
|
|
119
|
+
map.set("destination_id", idBytes(target.destinationId));
|
|
120
|
+
map.set("destination_generation", target.destinationGeneration);
|
|
121
|
+
optional(map, "snapshot", target.snapshot, encodeSnapshotSelector);
|
|
76
122
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
123
|
+
return map;
|
|
124
|
+
}
|
|
125
|
+
export function decodeQueryTarget(map, context) {
|
|
126
|
+
const kind = field.requiredString(map, "kind", context);
|
|
127
|
+
if (kind === "operational")
|
|
128
|
+
return { kind, index: field.requiredString(map, "index", context) };
|
|
129
|
+
if (kind === "lakehouse") {
|
|
130
|
+
const snapshot = field.optionalMap(map, "snapshot", context);
|
|
131
|
+
return {
|
|
132
|
+
kind,
|
|
133
|
+
destinationId: DestinationId.fromBytes(field.requiredBytes(map, "destination_id", context)),
|
|
134
|
+
destinationGeneration: field.requiredU64(map, "destination_generation", context),
|
|
135
|
+
...(snapshot === undefined
|
|
136
|
+
? {}
|
|
137
|
+
: { snapshot: decodeSnapshotSelector(snapshot, `${context}.snapshot`) })
|
|
138
|
+
};
|
|
80
139
|
}
|
|
81
|
-
|
|
82
|
-
|
|
140
|
+
throw new CodecError(`unknown query target \`${kind}\``, context, "kind");
|
|
141
|
+
}
|
|
142
|
+
function encodeSnapshotSelector(selector) {
|
|
143
|
+
return mapOf([
|
|
144
|
+
["kind", selector.kind],
|
|
145
|
+
["value", selector.value]
|
|
146
|
+
]);
|
|
147
|
+
}
|
|
148
|
+
function decodeSnapshotSelector(map, context) {
|
|
149
|
+
const kind = field.requiredString(map, "kind", context);
|
|
150
|
+
const value = field.requiredI64(map, "value", context);
|
|
151
|
+
if (kind === "snapshot_id" || kind === "timestamp_micros")
|
|
152
|
+
return { kind, value };
|
|
153
|
+
throw new CodecError(`unknown snapshot selector \`${kind}\``, context, "kind");
|
|
154
|
+
}
|
|
155
|
+
function encodeKeyMatch(value) {
|
|
156
|
+
return mapOf([
|
|
157
|
+
["field", value.field],
|
|
158
|
+
["value", encodeTypedValue(value.value)]
|
|
159
|
+
]);
|
|
160
|
+
}
|
|
161
|
+
function decodeKeyMatch(map, context) {
|
|
162
|
+
return {
|
|
163
|
+
field: field.requiredString(map, "field", context),
|
|
164
|
+
value: decodeTypedValue(map.get("value"), `${context}.value`)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function encodePredicate(value) {
|
|
168
|
+
return mapOf([
|
|
169
|
+
["field", value.field],
|
|
170
|
+
["op", value.op],
|
|
171
|
+
["value", encodeTypedValue(value.value)]
|
|
172
|
+
]);
|
|
173
|
+
}
|
|
174
|
+
function decodePredicate(map, context) {
|
|
175
|
+
return {
|
|
176
|
+
field: field.requiredString(map, "field", context),
|
|
177
|
+
op: decodeWord(map, "op", context, [
|
|
178
|
+
"eq",
|
|
179
|
+
"ne",
|
|
180
|
+
"lt",
|
|
181
|
+
"lte",
|
|
182
|
+
"gt",
|
|
183
|
+
"gte",
|
|
184
|
+
"in",
|
|
185
|
+
"contains",
|
|
186
|
+
"prefix"
|
|
187
|
+
]),
|
|
188
|
+
value: decodeTypedValue(map.get("value"), `${context}.value`)
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
export function encodeFilter(value) {
|
|
192
|
+
switch (value.kind) {
|
|
83
193
|
case "all":
|
|
84
|
-
return
|
|
194
|
+
return mapOf([["all", value.filters.map(encodeFilter)]]);
|
|
85
195
|
case "any":
|
|
86
|
-
return
|
|
196
|
+
return mapOf([["any", value.filters.map(encodeFilter)]]);
|
|
87
197
|
case "not":
|
|
88
|
-
return
|
|
198
|
+
return mapOf([["not", encodeFilter(value.filter)]]);
|
|
89
199
|
case "pred":
|
|
90
|
-
return
|
|
91
|
-
default:
|
|
92
|
-
throw new CodecError(`\`${String(tag)}\` is not a recognized filter tag`, context, "filter");
|
|
200
|
+
return mapOf([["pred", encodePredicate(value.predicate)]]);
|
|
93
201
|
}
|
|
94
202
|
}
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
203
|
+
export function decodeFilter(value, context) {
|
|
204
|
+
const [tag, inner] = singleVariantTag(value, context);
|
|
205
|
+
if (tag === "all" || tag === "any") {
|
|
206
|
+
if (!Array.isArray(inner))
|
|
207
|
+
throw new CodecError("filter children must be an array", context, tag);
|
|
208
|
+
return {
|
|
209
|
+
kind: tag,
|
|
210
|
+
filters: inner.map((item, index) => decodeFilter(item, `${context}.${tag}[${String(index)}]`))
|
|
211
|
+
};
|
|
98
212
|
}
|
|
99
|
-
|
|
213
|
+
if (tag === "not")
|
|
214
|
+
return { kind: tag, filter: decodeFilter(inner, `${context}.not`) };
|
|
215
|
+
if (tag === "pred")
|
|
216
|
+
return {
|
|
217
|
+
kind: tag,
|
|
218
|
+
predicate: decodePredicate(expectMap(inner, `${context}.pred`), `${context}.pred`)
|
|
219
|
+
};
|
|
220
|
+
throw new CodecError(`unknown filter \`${tag}\``, context, "filter");
|
|
100
221
|
}
|
|
101
|
-
|
|
102
|
-
const map =
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
map.set("params", rawSql.params.map((value) => encodeValue(value)));
|
|
222
|
+
function encodeSort(value) {
|
|
223
|
+
const map = mapOf([["field", value.field]]);
|
|
224
|
+
if (value.dir !== "asc")
|
|
225
|
+
map.set("dir", value.dir);
|
|
106
226
|
return map;
|
|
107
227
|
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
sql: field.requiredString(map, "sql", context),
|
|
111
|
-
params: field.optionalArray(map, "params", context, (item, index) => decodeValue(item, `${context}.params[${String(index)}]`))
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
function parseDir(word, context) {
|
|
115
|
-
if (word !== "asc" && word !== "desc") {
|
|
116
|
-
throw new CodecError(`\`${word}\` is not a recognized sort direction`, context, "dir");
|
|
117
|
-
}
|
|
118
|
-
return word;
|
|
119
|
-
}
|
|
120
|
-
export function encodeSort(sort) {
|
|
121
|
-
return new Map([
|
|
122
|
-
["field", sort.field],
|
|
123
|
-
["dir", sort.dir]
|
|
124
|
-
]);
|
|
125
|
-
}
|
|
126
|
-
export function decodeSort(map, context) {
|
|
127
|
-
const dir = field.optionalString(map, "dir", context);
|
|
228
|
+
function decodeSort(map, context) {
|
|
128
229
|
return {
|
|
129
230
|
field: field.requiredString(map, "field", context),
|
|
130
|
-
dir: dir
|
|
231
|
+
dir: decodeOptionalWord(map, "dir", context, ["asc", "desc"]) ?? "asc"
|
|
131
232
|
};
|
|
132
233
|
}
|
|
133
|
-
|
|
234
|
+
function encodeText(value) {
|
|
134
235
|
const map = new Map();
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
map.set("query", text.query);
|
|
236
|
+
optional(map, "field", value.field);
|
|
237
|
+
map.set("query", value.query);
|
|
138
238
|
return map;
|
|
139
239
|
}
|
|
140
|
-
|
|
240
|
+
function decodeText(map, context) {
|
|
141
241
|
const textField = field.optionalString(map, "field", context);
|
|
142
242
|
return {
|
|
143
|
-
...(textField
|
|
243
|
+
...(textField === undefined ? {} : { field: textField }),
|
|
144
244
|
query: field.requiredString(map, "query", context)
|
|
145
245
|
};
|
|
146
246
|
}
|
|
147
|
-
|
|
148
|
-
return
|
|
149
|
-
["field",
|
|
150
|
-
["embedding", [...
|
|
151
|
-
["top_k",
|
|
247
|
+
function encodeVector(value) {
|
|
248
|
+
return mapOf([
|
|
249
|
+
["field", value.field],
|
|
250
|
+
["embedding", [...value.embedding]],
|
|
251
|
+
["top_k", value.topK]
|
|
152
252
|
]);
|
|
153
253
|
}
|
|
154
|
-
|
|
254
|
+
function decodeVector(map, context) {
|
|
155
255
|
return {
|
|
156
256
|
field: field.requiredString(map, "field", context),
|
|
157
|
-
embedding: field.requiredArray(map, "embedding", context, (item
|
|
257
|
+
embedding: field.requiredArray(map, "embedding", context, (item) => {
|
|
258
|
+
if (typeof item !== "number")
|
|
259
|
+
throw new CodecError("embedding value must be a number", context, "embedding");
|
|
260
|
+
return item;
|
|
261
|
+
}),
|
|
158
262
|
topK: field.requiredU32(map, "top_k", context)
|
|
159
263
|
};
|
|
160
264
|
}
|
|
161
|
-
function
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
const AGG_FUNC_WORDS = new Map([
|
|
168
|
-
["count", "count"],
|
|
169
|
-
["count_distinct", "countDistinct"],
|
|
170
|
-
["sum", "sum"],
|
|
171
|
-
["avg", "avg"],
|
|
172
|
-
["min", "min"],
|
|
173
|
-
["max", "max"],
|
|
174
|
-
["percentile", "percentile"],
|
|
175
|
-
["std_dev", "stdDev"]
|
|
176
|
-
]);
|
|
177
|
-
const AGG_FUNC_TO_WORD = new Map(Array.from(AGG_FUNC_WORDS.entries(), ([word, value]) => [value, word]));
|
|
178
|
-
function parseAggFunc(word, context) {
|
|
179
|
-
const value = AGG_FUNC_WORDS.get(word);
|
|
180
|
-
if (value === undefined) {
|
|
181
|
-
throw new CodecError(`\`${word}\` is not a recognized aggregate function`, context, "func");
|
|
182
|
-
}
|
|
183
|
-
return value;
|
|
184
|
-
}
|
|
185
|
-
function aggFuncToWord(value) {
|
|
186
|
-
const word = AGG_FUNC_TO_WORD.get(value);
|
|
187
|
-
if (word === undefined) {
|
|
188
|
-
throw new CodecError(`\`${value}\` is not a recognized aggregate function`, "AggFunc", "func");
|
|
189
|
-
}
|
|
190
|
-
return word;
|
|
191
|
-
}
|
|
192
|
-
export function encodeAggCall(call) {
|
|
193
|
-
const map = new Map();
|
|
194
|
-
map.set("func", aggFuncToWord(call.func));
|
|
195
|
-
if (call.field !== undefined)
|
|
196
|
-
map.set("field", call.field);
|
|
197
|
-
if (call.arg !== undefined)
|
|
198
|
-
map.set("arg", call.arg);
|
|
199
|
-
map.set("alias", call.alias);
|
|
265
|
+
function encodeAggCall(value) {
|
|
266
|
+
const map = mapOf([["func", value.func]]);
|
|
267
|
+
optional(map, "field", value.field);
|
|
268
|
+
optional(map, "arg", value.arg);
|
|
269
|
+
map.set("alias", value.alias);
|
|
200
270
|
return map;
|
|
201
271
|
}
|
|
202
|
-
|
|
272
|
+
function decodeAggCall(map, context) {
|
|
203
273
|
const callField = field.optionalString(map, "field", context);
|
|
204
274
|
const arg = field.optionalF64(map, "arg", context);
|
|
205
275
|
return {
|
|
206
|
-
func:
|
|
207
|
-
|
|
208
|
-
|
|
276
|
+
func: decodeWord(map, "func", context, [
|
|
277
|
+
"count",
|
|
278
|
+
"count_distinct",
|
|
279
|
+
"sum",
|
|
280
|
+
"avg",
|
|
281
|
+
"min",
|
|
282
|
+
"max",
|
|
283
|
+
"percentile",
|
|
284
|
+
"std_dev"
|
|
285
|
+
]),
|
|
286
|
+
...(callField === undefined ? {} : { field: callField }),
|
|
287
|
+
...(arg === undefined ? {} : { arg }),
|
|
209
288
|
alias: field.requiredString(map, "alias", context)
|
|
210
289
|
};
|
|
211
290
|
}
|
|
212
|
-
|
|
213
|
-
return new Map([
|
|
214
|
-
["field", window.field],
|
|
215
|
-
["every_micros", window.everyMicros]
|
|
216
|
-
]);
|
|
217
|
-
}
|
|
218
|
-
export function decodeWindow(map, context) {
|
|
219
|
-
return {
|
|
220
|
-
field: field.requiredString(map, "field", context),
|
|
221
|
-
everyMicros: field.requiredU64(map, "every_micros", context)
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
export function encodeAggregate(aggregate) {
|
|
291
|
+
function encodeAggregate(value) {
|
|
225
292
|
const map = new Map();
|
|
226
|
-
if (
|
|
227
|
-
map.set("group_by", [...
|
|
228
|
-
map.set("funcs",
|
|
229
|
-
|
|
230
|
-
|
|
293
|
+
if (value.groupBy.length > 0)
|
|
294
|
+
map.set("group_by", [...value.groupBy]);
|
|
295
|
+
map.set("funcs", value.funcs.map(encodeAggCall));
|
|
296
|
+
optional(map, "window", value.window, (item) => mapOf([
|
|
297
|
+
["field", item.field],
|
|
298
|
+
["every_micros", item.everyMicros]
|
|
299
|
+
]));
|
|
231
300
|
return map;
|
|
232
301
|
}
|
|
233
|
-
|
|
234
|
-
const
|
|
302
|
+
function decodeAggregate(map, context) {
|
|
303
|
+
const window = field.optionalMap(map, "window", context);
|
|
235
304
|
return {
|
|
236
305
|
groupBy: field.optionalArray(map, "group_by", context, (item, index) => expectString(item, `${context}.group_by[${String(index)}]`)),
|
|
237
306
|
funcs: field.requiredArray(map, "funcs", context, (item, index) => decodeAggCall(expectMap(item, `${context}.funcs[${String(index)}]`), `${context}.funcs[${String(index)}]`)),
|
|
238
|
-
...(
|
|
307
|
+
...(window === undefined
|
|
308
|
+
? {}
|
|
309
|
+
: {
|
|
310
|
+
window: {
|
|
311
|
+
field: field.requiredString(window, "field", context),
|
|
312
|
+
everyMicros: field.requiredU64(window, "every_micros", context)
|
|
313
|
+
}
|
|
314
|
+
})
|
|
239
315
|
};
|
|
240
316
|
}
|
|
241
|
-
|
|
317
|
+
function encodeSelect(value) {
|
|
242
318
|
const map = new Map();
|
|
243
|
-
if (
|
|
244
|
-
map.set("fields", [...
|
|
245
|
-
map.set("payload",
|
|
319
|
+
if (value.fields.length > 0)
|
|
320
|
+
map.set("fields", [...value.fields]);
|
|
321
|
+
map.set("payload", value.payload);
|
|
246
322
|
return map;
|
|
247
323
|
}
|
|
248
|
-
|
|
324
|
+
function decodeSelect(map, context) {
|
|
249
325
|
return {
|
|
250
326
|
fields: field.optionalArray(map, "fields", context, (item, index) => expectString(item, `${context}.fields[${String(index)}]`)),
|
|
251
327
|
payload: field.optionalBoolean(map, "payload", context) ?? false
|
|
252
328
|
};
|
|
253
329
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
default:
|
|
263
|
-
throw new CodecError(`\`${word}\` is not a recognized consistency level`, context, "consistency");
|
|
264
|
-
}
|
|
330
|
+
function encodeRawSql(value) {
|
|
331
|
+
const map = mapOf([
|
|
332
|
+
["dialect", value.dialect],
|
|
333
|
+
["sql", value.sql]
|
|
334
|
+
]);
|
|
335
|
+
if (value.params.length > 0)
|
|
336
|
+
map.set("params", value.params.map(encodeTypedValue));
|
|
337
|
+
return map;
|
|
265
338
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
339
|
+
function decodeRawSql(map, context) {
|
|
340
|
+
return {
|
|
341
|
+
dialect: decodeWord(map, "dialect", context, ["data_fusion", "postgres", "my_sql", "sqlite"]),
|
|
342
|
+
sql: field.requiredString(map, "sql", context),
|
|
343
|
+
params: field.optionalArray(map, "params", context, (item, index) => decodeTypedValue(item, `${context}.params[${String(index)}]`))
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function encodePageRequest(value) {
|
|
347
|
+
const map = mapOf([["limit", value.limit]]);
|
|
348
|
+
optional(map, "offset", value.offset);
|
|
349
|
+
optional(map, "cursor", value.cursor);
|
|
350
|
+
if (value.wantTotal)
|
|
351
|
+
map.set("want_total", true);
|
|
352
|
+
return map;
|
|
353
|
+
}
|
|
354
|
+
function decodePageRequest(map, context) {
|
|
355
|
+
const offset = field.optionalU64(map, "offset", context);
|
|
356
|
+
const cursor = field.optionalString(map, "cursor", context);
|
|
357
|
+
return {
|
|
358
|
+
limit: field.requiredU32(map, "limit", context),
|
|
359
|
+
...(offset === undefined ? {} : { offset }),
|
|
360
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
361
|
+
wantTotal: field.optionalBoolean(map, "want_total", context) ?? false
|
|
362
|
+
};
|
|
275
363
|
}
|
|
276
364
|
export function encodeQuery(query) {
|
|
277
|
-
const map =
|
|
278
|
-
|
|
365
|
+
const map = mapOf([
|
|
366
|
+
["execution_id", idBytes(query.executionId)],
|
|
367
|
+
["target", encodeQueryTarget(query.target)],
|
|
368
|
+
["deadline_micros", query.deadlineMicros]
|
|
369
|
+
]);
|
|
279
370
|
if (query.byKey.length > 0)
|
|
280
|
-
map.set("by_key", query.byKey.map(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
map.set("filter", encodeFilter(query.filter));
|
|
287
|
-
if (query.vector !== undefined)
|
|
288
|
-
map.set("vector", encodeVectorQuery(query.vector));
|
|
289
|
-
if (query.text !== undefined)
|
|
290
|
-
map.set("text", encodeTextQuery(query.text));
|
|
371
|
+
map.set("by_key", query.byKey.map(encodeKeyMatch));
|
|
372
|
+
optional(map, "message_type", query.messageType);
|
|
373
|
+
optional(map, "time_range", query.timeRange, (item) => [...item]);
|
|
374
|
+
optional(map, "filter", query.filter, encodeFilter);
|
|
375
|
+
optional(map, "vector", query.vector, encodeVector);
|
|
376
|
+
optional(map, "text", query.text, encodeText);
|
|
291
377
|
if (query.order.length > 0)
|
|
292
|
-
map.set("order", query.order.map(
|
|
293
|
-
map.set("
|
|
294
|
-
map
|
|
295
|
-
|
|
296
|
-
map.set("aggregate", encodeAggregate(query.aggregate));
|
|
297
|
-
if (query.having !== undefined)
|
|
298
|
-
map.set("having", encodeFilter(query.having));
|
|
378
|
+
map.set("order", query.order.map(encodeSort));
|
|
379
|
+
map.set("page", encodePageRequest(query.page));
|
|
380
|
+
optional(map, "aggregate", query.aggregate, encodeAggregate);
|
|
381
|
+
optional(map, "having", query.having, encodeFilter);
|
|
299
382
|
if (query.distinct)
|
|
300
383
|
map.set("distinct", true);
|
|
301
384
|
map.set("select", encodeSelect(query.select));
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (query.rawSql !== undefined)
|
|
305
|
-
map.set("raw_sql", encodeRawSql(query.rawSql));
|
|
385
|
+
optional(map, "fork", query.fork);
|
|
386
|
+
optional(map, "raw_sql", query.rawSql, encodeRawSql);
|
|
306
387
|
if (query.consistency !== "eventual")
|
|
307
|
-
map.set("consistency",
|
|
308
|
-
if (query.wantTotal)
|
|
309
|
-
map.set("want_total", true);
|
|
388
|
+
map.set("consistency", query.consistency);
|
|
310
389
|
return map;
|
|
311
390
|
}
|
|
312
391
|
export function decodeQuery(map, context) {
|
|
313
392
|
const messageType = field.optionalString(map, "message_type", context);
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
393
|
+
const timeRangeValue = map.get("time_range");
|
|
394
|
+
let timeRange;
|
|
395
|
+
if (timeRangeValue !== undefined) {
|
|
396
|
+
if (!Array.isArray(timeRangeValue) || timeRangeValue.length !== 2)
|
|
397
|
+
throw new CodecError("time range must contain two values", context, "time_range");
|
|
398
|
+
timeRange = [asU64(timeRangeValue[0], context), asU64(timeRangeValue[1], context)];
|
|
399
|
+
}
|
|
400
|
+
const filter = map.get("filter");
|
|
401
|
+
const vector = field.optionalMap(map, "vector", context);
|
|
402
|
+
const text = field.optionalMap(map, "text", context);
|
|
403
|
+
const aggregate = field.optionalMap(map, "aggregate", context);
|
|
404
|
+
const having = map.get("having");
|
|
320
405
|
const fork = field.optionalString(map, "fork", context);
|
|
321
|
-
const
|
|
322
|
-
const consistencyWord = field.optionalString(map, "consistency", context);
|
|
323
|
-
const selectMap = field.optionalMap(map, "select", context);
|
|
406
|
+
const rawSql = field.optionalMap(map, "raw_sql", context);
|
|
324
407
|
return {
|
|
325
|
-
|
|
408
|
+
executionId: QueryExecutionId.fromBytes(field.requiredBytes(map, "execution_id", context)),
|
|
409
|
+
target: decodeQueryTarget(field.requiredMap(map, "target", context), `${context}.target`),
|
|
410
|
+
deadlineMicros: field.requiredU64(map, "deadline_micros", context),
|
|
326
411
|
byKey: field.optionalArray(map, "by_key", context, (item, index) => decodeKeyMatch(expectMap(item, `${context}.by_key[${String(index)}]`), `${context}.by_key[${String(index)}]`)),
|
|
327
|
-
...(messageType
|
|
328
|
-
...(
|
|
329
|
-
...(
|
|
330
|
-
...(
|
|
331
|
-
|
|
332
|
-
: {}),
|
|
333
|
-
...(textMap !== undefined ? { text: decodeTextQuery(textMap, `${context}.text`) } : {}),
|
|
412
|
+
...(messageType === undefined ? {} : { messageType }),
|
|
413
|
+
...(timeRange === undefined ? {} : { timeRange }),
|
|
414
|
+
...(filter === undefined ? {} : { filter: decodeFilter(filter, `${context}.filter`) }),
|
|
415
|
+
...(vector === undefined ? {} : { vector: decodeVector(vector, `${context}.vector`) }),
|
|
416
|
+
...(text === undefined ? {} : { text: decodeText(text, `${context}.text`) }),
|
|
334
417
|
order: field.optionalArray(map, "order", context, (item, index) => decodeSort(expectMap(item, `${context}.order[${String(index)}]`), `${context}.order[${String(index)}]`)),
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
...(havingRaw !== undefined ? { having: decodeFilter(havingRaw, `${context}.having`) } : {}),
|
|
418
|
+
page: decodePageRequest(field.requiredMap(map, "page", context), `${context}.page`),
|
|
419
|
+
...(aggregate === undefined
|
|
420
|
+
? {}
|
|
421
|
+
: { aggregate: decodeAggregate(aggregate, `${context}.aggregate`) }),
|
|
422
|
+
...(having === undefined ? {} : { having: decodeFilter(having, `${context}.having`) }),
|
|
341
423
|
distinct: field.optionalBoolean(map, "distinct", context) ?? false,
|
|
342
|
-
select:
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
consistency: consistencyWord !== undefined ? parseConsistency(consistencyWord, context) : "eventual",
|
|
348
|
-
wantTotal: field.optionalBoolean(map, "want_total", context) ?? false
|
|
424
|
+
select: decodeSelect(field.requiredMap(map, "select", context), `${context}.select`),
|
|
425
|
+
...(fork === undefined ? {} : { fork }),
|
|
426
|
+
...(rawSql === undefined ? {} : { rawSql: decodeRawSql(rawSql, `${context}.raw_sql`) }),
|
|
427
|
+
consistency: decodeOptionalWord(map, "consistency", context, ["eventual", "read_your_writes", "strong"]) ??
|
|
428
|
+
"eventual"
|
|
349
429
|
};
|
|
350
430
|
}
|
|
351
|
-
function
|
|
352
|
-
if (
|
|
353
|
-
throw new
|
|
431
|
+
export function validateQuery(query) {
|
|
432
|
+
if (query.executionId.asU128() === 0n || query.deadlineMicros === 0n)
|
|
433
|
+
throw new InvalidError("query execution identity and deadline must be nonzero");
|
|
434
|
+
if (query.target.kind === "operational")
|
|
435
|
+
validateName(query.target.index);
|
|
436
|
+
else {
|
|
437
|
+
if (query.target.destinationId.asU128() === 0n || query.target.destinationGeneration === 0n)
|
|
438
|
+
throw new InvalidError("lakehouse target identity must be nonzero");
|
|
439
|
+
if (query.target.snapshot !== undefined && query.target.snapshot.value <= 0n)
|
|
440
|
+
throw new InvalidError("snapshot selector must be positive");
|
|
441
|
+
if (query.fork !== undefined)
|
|
442
|
+
throw new InvalidError("lakehouse queries cannot use an operational fork");
|
|
443
|
+
}
|
|
444
|
+
if (query.page.limit < 1 || query.page.limit > MAX_PAGE_SIZE)
|
|
445
|
+
throw new InvalidError("query page limit is invalid");
|
|
446
|
+
if (query.page.offset !== undefined && query.page.cursor !== undefined)
|
|
447
|
+
throw new InvalidError("query page cannot contain both an offset and a cursor");
|
|
448
|
+
if (query.page.cursor !== undefined)
|
|
449
|
+
validateCursor(query.page.cursor);
|
|
450
|
+
for (const [label, count] of [
|
|
451
|
+
["exact matches", query.byKey.length],
|
|
452
|
+
["sort fields", query.order.length],
|
|
453
|
+
["selected fields", query.select.fields.length]
|
|
454
|
+
]) {
|
|
455
|
+
if (count > MAX_QUERY_FIELDS)
|
|
456
|
+
throw new InvalidError(`query ${label} count exceeds cap ${String(MAX_QUERY_FIELDS)}`);
|
|
457
|
+
}
|
|
458
|
+
for (const match of query.byKey) {
|
|
459
|
+
validateName(match.field);
|
|
460
|
+
validateTypedValue(match.value);
|
|
461
|
+
}
|
|
462
|
+
if (query.messageType !== undefined)
|
|
463
|
+
validateName(query.messageType);
|
|
464
|
+
if (query.timeRange !== undefined && query.timeRange[0] >= query.timeRange[1])
|
|
465
|
+
throw new InvalidError("query time range must be a nonempty half-open interval");
|
|
466
|
+
for (const sort of query.order)
|
|
467
|
+
validateName(sort.field);
|
|
468
|
+
for (const field of query.select.fields)
|
|
469
|
+
validateName(field);
|
|
470
|
+
if (query.text !== undefined) {
|
|
471
|
+
if (query.text.query.trim().length === 0 ||
|
|
472
|
+
byteLength(query.text.query) > MAX_TEXT_QUERY_BYTES ||
|
|
473
|
+
hasControlCharacter(query.text.query))
|
|
474
|
+
throw new InvalidError("text query is invalid");
|
|
475
|
+
if (query.text.field !== undefined &&
|
|
476
|
+
(query.text.field.length === 0 ||
|
|
477
|
+
byteLength(query.text.field) > MAX_QUERY_NAME_BYTES ||
|
|
478
|
+
hasControlCharacter(query.text.field)))
|
|
479
|
+
throw new InvalidError("text query field is invalid");
|
|
480
|
+
}
|
|
481
|
+
if (query.vector !== undefined) {
|
|
482
|
+
validateName(query.vector.field);
|
|
483
|
+
if (query.vector.embedding.length === 0 ||
|
|
484
|
+
query.vector.embedding.length > MAX_VECTOR_DIMENSIONS ||
|
|
485
|
+
query.vector.embedding.some((value) => !Number.isFinite(value) || Object.is(value, -0)) ||
|
|
486
|
+
query.vector.topK < 1 ||
|
|
487
|
+
query.vector.topK > MAX_PAGE_SIZE)
|
|
488
|
+
throw new InvalidError("vector query is invalid");
|
|
489
|
+
}
|
|
490
|
+
const visit = (filter, depth = 1, count = { value: 0 }) => {
|
|
491
|
+
if (filter === undefined)
|
|
492
|
+
return;
|
|
493
|
+
count.value += 1;
|
|
494
|
+
if (count.value > MAX_QUERY_PREDICATES)
|
|
495
|
+
throw new InvalidError("query filter node count exceeds cap");
|
|
496
|
+
if (depth > 64)
|
|
497
|
+
throw new InvalidError("query filter depth exceeds cap");
|
|
498
|
+
if (filter.kind === "all" || filter.kind === "any") {
|
|
499
|
+
if (filter.filters.length === 0)
|
|
500
|
+
throw new InvalidError("filter conjunction cannot be empty");
|
|
501
|
+
for (const child of filter.filters)
|
|
502
|
+
visit(child, depth + 1, count);
|
|
503
|
+
}
|
|
504
|
+
else if (filter.kind === "not")
|
|
505
|
+
visit(filter.filter, depth + 1, count);
|
|
506
|
+
else {
|
|
507
|
+
validateName(filter.predicate.field);
|
|
508
|
+
validateTypedValue(filter.predicate.value);
|
|
509
|
+
if (filter.predicate.op === "in" &&
|
|
510
|
+
(filter.predicate.value.kind !== "list" || filter.predicate.value.value.length === 0))
|
|
511
|
+
throw new InvalidError("query IN predicate requires a nonempty typed list");
|
|
512
|
+
if (filter.predicate.op === "prefix" && filter.predicate.value.kind !== "string")
|
|
513
|
+
throw new InvalidError("query prefix predicate requires a string value");
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
visit(query.filter);
|
|
517
|
+
visit(query.having);
|
|
518
|
+
if (query.having !== undefined && query.aggregate === undefined)
|
|
519
|
+
throw new InvalidError("having requires an aggregate");
|
|
520
|
+
if (query.aggregate !== undefined) {
|
|
521
|
+
if (query.aggregate.funcs.length === 0 ||
|
|
522
|
+
query.aggregate.funcs.length > MAX_QUERY_PARAMETERS ||
|
|
523
|
+
query.aggregate.groupBy.length > MAX_QUERY_FIELDS)
|
|
524
|
+
throw new InvalidError("aggregate function or group count is invalid");
|
|
525
|
+
for (const field of query.aggregate.groupBy)
|
|
526
|
+
validateName(field);
|
|
527
|
+
const aliases = new Set();
|
|
528
|
+
for (const call of query.aggregate.funcs) {
|
|
529
|
+
validateName(call.alias);
|
|
530
|
+
if (aliases.has(call.alias))
|
|
531
|
+
throw new InvalidError("aggregate aliases must be unique");
|
|
532
|
+
aliases.add(call.alias);
|
|
533
|
+
if (call.field !== undefined)
|
|
534
|
+
validateName(call.field);
|
|
535
|
+
if (call.func === "count") {
|
|
536
|
+
if (call.arg !== undefined)
|
|
537
|
+
throw new InvalidError("count aggregate cannot carry an argument");
|
|
538
|
+
}
|
|
539
|
+
else if (call.func === "percentile") {
|
|
540
|
+
if (call.field === undefined ||
|
|
541
|
+
call.arg === undefined ||
|
|
542
|
+
!Number.isFinite(call.arg) ||
|
|
543
|
+
call.arg < 0 ||
|
|
544
|
+
call.arg > 1)
|
|
545
|
+
throw new InvalidError("percentile aggregate is invalid");
|
|
546
|
+
}
|
|
547
|
+
else if (call.field === undefined || call.arg !== undefined) {
|
|
548
|
+
throw new InvalidError("aggregate field and argument do not match its function");
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (query.aggregate.window !== undefined) {
|
|
552
|
+
validateName(query.aggregate.window.field);
|
|
553
|
+
if (query.aggregate.window.everyMicros === 0n)
|
|
554
|
+
throw new InvalidError("aggregate window duration must be nonzero");
|
|
555
|
+
}
|
|
556
|
+
if (query.select.fields.length > 0 || query.select.payload)
|
|
557
|
+
throw new InvalidError("aggregate query cannot select rows");
|
|
558
|
+
}
|
|
559
|
+
if (query.rawSql !== undefined) {
|
|
560
|
+
if (query.rawSql.sql.trim().length === 0 ||
|
|
561
|
+
byteLength(query.rawSql.sql) > MAX_RAW_SQL_BYTES ||
|
|
562
|
+
hasDisallowedSqlControl(query.rawSql.sql) ||
|
|
563
|
+
query.rawSql.params.length > MAX_QUERY_PARAMETERS)
|
|
564
|
+
throw new InvalidError("raw SQL is invalid");
|
|
565
|
+
for (const param of query.rawSql.params)
|
|
566
|
+
validateTypedValue(param);
|
|
567
|
+
if (query.byKey.length > 0 ||
|
|
568
|
+
query.messageType !== undefined ||
|
|
569
|
+
query.timeRange !== undefined ||
|
|
570
|
+
query.filter !== undefined ||
|
|
571
|
+
query.vector !== undefined ||
|
|
572
|
+
query.text !== undefined ||
|
|
573
|
+
query.order.length > 0 ||
|
|
574
|
+
query.aggregate !== undefined ||
|
|
575
|
+
query.having !== undefined ||
|
|
576
|
+
query.distinct ||
|
|
577
|
+
query.select.fields.length > 0 ||
|
|
578
|
+
query.select.payload)
|
|
579
|
+
throw new InvalidError("raw SQL cannot be combined with the structured query expression");
|
|
354
580
|
}
|
|
355
|
-
const [start, end] = value;
|
|
356
|
-
return [expectU64Value(start, context), expectU64Value(end, context)];
|
|
357
581
|
}
|
|
358
|
-
function
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
582
|
+
export function validateQueryEnvelope(envelope) {
|
|
583
|
+
validateQueryControl(envelope.v, envelope.query.executionId);
|
|
584
|
+
validateQuery(envelope.query);
|
|
585
|
+
}
|
|
586
|
+
export function validateQueryPageEnvelope(envelope) {
|
|
587
|
+
validateQueryControl(envelope.v, envelope.executionId);
|
|
588
|
+
validateCursor(envelope.cursor);
|
|
589
|
+
if (envelope.deadlineMicros === 0n)
|
|
590
|
+
throw new InvalidError("query page deadline must be nonzero");
|
|
591
|
+
}
|
|
592
|
+
export function validateQueryCancelEnvelope(envelope) {
|
|
593
|
+
validateQueryControl(envelope.v, envelope.executionId);
|
|
594
|
+
}
|
|
595
|
+
export function validateQueryStatusEnvelope(envelope) {
|
|
596
|
+
validateQueryControl(envelope.v, envelope.executionId);
|
|
597
|
+
}
|
|
598
|
+
function validateQueryControl(version, executionId) {
|
|
599
|
+
if (version !== QUERY_OP_VERSION)
|
|
600
|
+
throw new InvalidError(`query version must be ${String(QUERY_OP_VERSION)}`);
|
|
601
|
+
if (executionId.asU128() === 0n)
|
|
602
|
+
throw new InvalidError("query execution id must be nonzero");
|
|
603
|
+
}
|
|
604
|
+
function validateCursor(cursor) {
|
|
605
|
+
if (cursor.length === 0 ||
|
|
606
|
+
byteLength(cursor) > MAX_QUERY_CURSOR_BYTES ||
|
|
607
|
+
hasControlCharacter(cursor))
|
|
608
|
+
throw new InvalidError("query cursor is invalid");
|
|
364
609
|
}
|
|
365
610
|
export function encodeQueryEnvelope(envelope) {
|
|
366
|
-
return
|
|
367
|
-
["v",
|
|
611
|
+
return mapOf([
|
|
612
|
+
["v", envelope.v],
|
|
368
613
|
["query", encodeQuery(envelope.query)]
|
|
369
614
|
]);
|
|
370
615
|
}
|
|
371
616
|
export function decodeQueryEnvelope(map, context) {
|
|
372
|
-
|
|
373
|
-
|
|
617
|
+
return {
|
|
618
|
+
v: field.requiredU32(map, "v", context),
|
|
619
|
+
query: decodeQuery(field.requiredMap(map, "query", context), `${context}.query`)
|
|
620
|
+
};
|
|
374
621
|
}
|
|
375
622
|
export function encodeQueryEnvelopeFrame(envelope) {
|
|
376
623
|
return encodeNamed(encodeQueryEnvelope(envelope), { forceFloatNumbers: true });
|
|
377
624
|
}
|
|
378
|
-
export function
|
|
625
|
+
export function decodeQueryEnvelopeFrame(bytes) {
|
|
626
|
+
return decodeQueryEnvelope(expectMap(decodeOne(bytes, "QueryEnvelope"), "QueryEnvelope"), "QueryEnvelope");
|
|
627
|
+
}
|
|
628
|
+
function encodeRow(value) {
|
|
629
|
+
const map = mapOf([["values", value.values.map(encodeTypedValue)]]);
|
|
630
|
+
optional(map, "score", value.score);
|
|
631
|
+
return map;
|
|
632
|
+
}
|
|
633
|
+
function decodeRow(map, context) {
|
|
634
|
+
const score = field.optionalF64(map, "score", context);
|
|
635
|
+
return {
|
|
636
|
+
values: field.requiredArray(map, "values", context, (item, index) => decodeTypedValue(item, `${context}.values[${String(index)}]`)),
|
|
637
|
+
...(score === undefined ? {} : { score })
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function encodePage(value) {
|
|
379
641
|
const map = new Map();
|
|
380
|
-
map
|
|
381
|
-
map.set("limit",
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
map
|
|
642
|
+
optional(map, "offset", value.offset);
|
|
643
|
+
map.set("limit", value.limit);
|
|
644
|
+
optional(map, "total", value.total);
|
|
645
|
+
map.set("has_more", value.hasMore);
|
|
646
|
+
optional(map, "next_cursor", value.nextCursor);
|
|
385
647
|
return map;
|
|
386
648
|
}
|
|
387
|
-
|
|
649
|
+
function decodePage(map, context) {
|
|
650
|
+
const offset = field.optionalU64(map, "offset", context);
|
|
388
651
|
const total = field.optionalU64(map, "total", context);
|
|
652
|
+
const nextCursor = field.optionalString(map, "next_cursor", context);
|
|
389
653
|
return {
|
|
390
|
-
offset:
|
|
654
|
+
...(offset === undefined ? {} : { offset }),
|
|
391
655
|
limit: field.requiredU32(map, "limit", context),
|
|
392
|
-
...(total
|
|
393
|
-
hasMore: field.requiredBoolean(map, "has_more", context)
|
|
656
|
+
...(total === undefined ? {} : { total }),
|
|
657
|
+
hasMore: field.requiredBoolean(map, "has_more", context),
|
|
658
|
+
...(nextCursor === undefined ? {} : { nextCursor })
|
|
394
659
|
};
|
|
395
660
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
return
|
|
661
|
+
function encodeEngine(value) {
|
|
662
|
+
const map = mapOf([
|
|
663
|
+
["name", value.name],
|
|
664
|
+
["version", value.version]
|
|
665
|
+
]);
|
|
666
|
+
optional(map, "dialect", value.dialect);
|
|
667
|
+
return map;
|
|
403
668
|
}
|
|
404
|
-
function
|
|
405
|
-
|
|
669
|
+
function decodeEngine(map, context) {
|
|
670
|
+
const dialect = decodeOptionalWord(map, "dialect", context, [
|
|
671
|
+
"data_fusion",
|
|
672
|
+
"postgres",
|
|
673
|
+
"my_sql",
|
|
674
|
+
"sqlite"
|
|
675
|
+
]);
|
|
676
|
+
return {
|
|
677
|
+
name: field.requiredString(map, "name", context),
|
|
678
|
+
version: field.requiredString(map, "version", context),
|
|
679
|
+
...(dialect === undefined ? {} : { dialect })
|
|
680
|
+
};
|
|
406
681
|
}
|
|
407
|
-
function
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
682
|
+
function encodeResolvedTarget(value) {
|
|
683
|
+
const map = mapOf([["kind", value.kind]]);
|
|
684
|
+
if (value.kind === "operational")
|
|
685
|
+
map.set("index", value.index);
|
|
686
|
+
else {
|
|
687
|
+
map.set("destination_id", idBytes(value.destinationId));
|
|
688
|
+
map.set("destination_generation", value.destinationGeneration);
|
|
689
|
+
map.set("table_uuid", value.tableUuid);
|
|
690
|
+
map.set("namespace", [...value.namespace]);
|
|
691
|
+
map.set("table", value.table);
|
|
692
|
+
map.set("snapshot_id", value.snapshotId);
|
|
693
|
+
map.set("schema_id", value.schemaId);
|
|
694
|
+
map.set("partition_spec_id", value.partitionSpecId);
|
|
414
695
|
}
|
|
415
|
-
|
|
696
|
+
map.set("backend_resource_id", idBytes(value.backendResourceId));
|
|
697
|
+
map.set("backend_generation", value.backendGeneration);
|
|
698
|
+
map.set("runtime_configuration_revision", value.runtimeConfigurationRevision);
|
|
699
|
+
return map;
|
|
416
700
|
}
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
if (
|
|
425
|
-
|
|
426
|
-
if (
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
701
|
+
function decodeResolvedTarget(map, context) {
|
|
702
|
+
const kind = field.requiredString(map, "kind", context);
|
|
703
|
+
const common = {
|
|
704
|
+
backendResourceId: BackendResourceId.fromBytes(field.requiredBytes(map, "backend_resource_id", context)),
|
|
705
|
+
backendGeneration: field.requiredU64(map, "backend_generation", context),
|
|
706
|
+
runtimeConfigurationRevision: field.requiredU64(map, "runtime_configuration_revision", context)
|
|
707
|
+
};
|
|
708
|
+
if (kind === "operational")
|
|
709
|
+
return { kind, index: field.requiredString(map, "index", context), ...common };
|
|
710
|
+
if (kind === "lakehouse")
|
|
711
|
+
return {
|
|
712
|
+
kind,
|
|
713
|
+
destinationId: DestinationId.fromBytes(field.requiredBytes(map, "destination_id", context)),
|
|
714
|
+
destinationGeneration: field.requiredU64(map, "destination_generation", context),
|
|
715
|
+
...common,
|
|
716
|
+
tableUuid: fixedBytes(field.requiredBytes(map, "table_uuid", context), 16, context),
|
|
717
|
+
namespace: field.requiredArray(map, "namespace", context, (item, index) => expectString(item, `${context}.namespace[${String(index)}]`)),
|
|
718
|
+
table: field.requiredString(map, "table", context),
|
|
719
|
+
snapshotId: field.requiredI64(map, "snapshot_id", context),
|
|
720
|
+
schemaId: field.requiredI32(map, "schema_id", context),
|
|
721
|
+
partitionSpecId: field.requiredI32(map, "partition_spec_id", context)
|
|
722
|
+
};
|
|
723
|
+
throw new CodecError(`unknown resolved target \`${kind}\``, context, "kind");
|
|
724
|
+
}
|
|
725
|
+
function encodeContext(value) {
|
|
726
|
+
const map = mapOf([
|
|
727
|
+
["execution_id", idBytes(value.executionId)],
|
|
728
|
+
["engine", encodeEngine(value.engine)],
|
|
729
|
+
["resolved_target", encodeResolvedTarget(value.resolvedTarget)],
|
|
730
|
+
["requested_consistency", value.requestedConsistency],
|
|
731
|
+
["delivered_consistency", value.deliveredConsistency]
|
|
732
|
+
]);
|
|
733
|
+
optional(map, "boundary", value.boundary, (item) => mapOf([
|
|
734
|
+
["digest", item.digest],
|
|
735
|
+
["relation_to_current", item.relationToCurrent]
|
|
736
|
+
]));
|
|
737
|
+
optional(map, "checkpoint_revision", value.checkpointRevision);
|
|
738
|
+
optional(map, "global_state_revision", value.globalStateRevision);
|
|
739
|
+
map.set("truncated", value.truncated);
|
|
740
|
+
map.set("elapsed_micros", value.elapsedMicros);
|
|
741
|
+
map.set("scanned_bytes", value.scannedBytes);
|
|
742
|
+
map.set("produced_bytes", value.producedBytes);
|
|
743
|
+
map.set("row_count", value.rowCount);
|
|
434
744
|
return map;
|
|
435
745
|
}
|
|
436
|
-
|
|
437
|
-
const
|
|
438
|
-
const
|
|
439
|
-
const
|
|
440
|
-
const stream = field.optionalU32(map, "stream", context);
|
|
441
|
-
const topic = field.optionalU32(map, "topic", context);
|
|
442
|
-
const payload = field.optionalBytes(map, "payload", context);
|
|
443
|
-
const score = field.optionalF64(map, "score", context);
|
|
746
|
+
function decodeContext(map, context) {
|
|
747
|
+
const boundary = field.optionalMap(map, "boundary", context);
|
|
748
|
+
const checkpointRevision = field.optionalU64(map, "checkpoint_revision", context);
|
|
749
|
+
const globalStateRevision = field.optionalU64(map, "global_state_revision", context);
|
|
444
750
|
return {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
751
|
+
executionId: QueryExecutionId.fromBytes(field.requiredBytes(map, "execution_id", context)),
|
|
752
|
+
engine: decodeEngine(field.requiredMap(map, "engine", context), `${context}.engine`),
|
|
753
|
+
resolvedTarget: decodeResolvedTarget(field.requiredMap(map, "resolved_target", context), `${context}.resolved_target`),
|
|
754
|
+
requestedConsistency: decodeWord(map, "requested_consistency", context, [
|
|
755
|
+
"eventual",
|
|
756
|
+
"read_your_writes",
|
|
757
|
+
"strong"
|
|
758
|
+
]),
|
|
759
|
+
deliveredConsistency: decodeWord(map, "delivered_consistency", context, [
|
|
760
|
+
"eventual",
|
|
761
|
+
"read_your_writes",
|
|
762
|
+
"strong"
|
|
763
|
+
]),
|
|
764
|
+
...(boundary === undefined
|
|
765
|
+
? {}
|
|
766
|
+
: {
|
|
767
|
+
boundary: {
|
|
768
|
+
digest: fixedBytes(field.requiredBytes(boundary, "digest", context), 32, context),
|
|
769
|
+
relationToCurrent: decodeWord(boundary, "relation_to_current", context, [
|
|
770
|
+
"current",
|
|
771
|
+
"historical",
|
|
772
|
+
"ahead_of_observed_checkpoint"
|
|
773
|
+
])
|
|
774
|
+
}
|
|
775
|
+
}),
|
|
776
|
+
...(checkpointRevision === undefined ? {} : { checkpointRevision }),
|
|
777
|
+
...(globalStateRevision === undefined ? {} : { globalStateRevision }),
|
|
778
|
+
truncated: field.requiredBoolean(map, "truncated", context),
|
|
779
|
+
elapsedMicros: field.requiredU64(map, "elapsed_micros", context),
|
|
780
|
+
scannedBytes: field.requiredU64(map, "scanned_bytes", context),
|
|
781
|
+
producedBytes: field.requiredU64(map, "produced_bytes", context),
|
|
782
|
+
rowCount: field.requiredU64(map, "row_count", context)
|
|
453
783
|
};
|
|
454
784
|
}
|
|
455
|
-
export function encodeQueryResult(
|
|
456
|
-
return
|
|
457
|
-
["
|
|
458
|
-
["
|
|
785
|
+
export function encodeQueryResult(value) {
|
|
786
|
+
return mapOf([
|
|
787
|
+
["fields", value.fields.map(encodeLogicalField)],
|
|
788
|
+
["rows", value.rows.map(encodeRow)],
|
|
789
|
+
["page", encodePage(value.page)],
|
|
790
|
+
["context", encodeContext(value.context)]
|
|
459
791
|
]);
|
|
460
792
|
}
|
|
461
793
|
export function decodeQueryResult(map, context) {
|
|
462
|
-
|
|
794
|
+
const result = {
|
|
795
|
+
fields: field.requiredArray(map, "fields", context, (item, index) => decodeLogicalField(expectMap(item, `${context}.fields[${String(index)}]`), `${context}.fields[${String(index)}]`)),
|
|
463
796
|
rows: field.requiredArray(map, "rows", context, (item, index) => decodeRow(expectMap(item, `${context}.rows[${String(index)}]`), `${context}.rows[${String(index)}]`)),
|
|
464
|
-
page: decodePage(field.requiredMap(map, "page", context), `${context}.page`)
|
|
797
|
+
page: decodePage(field.requiredMap(map, "page", context), `${context}.page`),
|
|
798
|
+
context: decodeContext(field.requiredMap(map, "context", context), `${context}.context`)
|
|
465
799
|
};
|
|
800
|
+
validateQueryResult(result);
|
|
801
|
+
return result;
|
|
802
|
+
}
|
|
803
|
+
export function validateQueryResult(result) {
|
|
804
|
+
validateResultFields(result.fields);
|
|
805
|
+
for (const row of result.rows) {
|
|
806
|
+
if (row.values.length !== result.fields.length)
|
|
807
|
+
throw new InvalidError("query row value count does not match the result schema");
|
|
808
|
+
for (let index = 0; index < result.fields.length; index += 1) {
|
|
809
|
+
const field = result.fields[index];
|
|
810
|
+
const value = row.values[index];
|
|
811
|
+
if (field === undefined || value === undefined)
|
|
812
|
+
throw new InvalidError("query row value count does not match the result schema");
|
|
813
|
+
validateTypedValueAgainst(value, field.fieldType, field.required);
|
|
814
|
+
}
|
|
815
|
+
if (row.score !== undefined && !Number.isFinite(row.score))
|
|
816
|
+
throw new InvalidError("query row score must be finite");
|
|
817
|
+
}
|
|
818
|
+
if (result.context.executionId.asU128() === 0n)
|
|
819
|
+
throw new InvalidError("query result execution id must be nonzero");
|
|
820
|
+
if (result.context.rowCount !== BigInt(result.rows.length))
|
|
821
|
+
throw new InvalidError("query result row count does not match the page rows");
|
|
822
|
+
validateQueryContext(result.context);
|
|
823
|
+
if (result.page.limit < 1 || result.page.limit > MAX_PAGE_SIZE)
|
|
824
|
+
throw new InvalidError("query result page limit is invalid");
|
|
825
|
+
if (result.page.hasMore !== (result.page.nextCursor !== undefined))
|
|
826
|
+
throw new InvalidError("query result has_more and next_cursor must agree");
|
|
827
|
+
if (result.page.nextCursor !== undefined)
|
|
828
|
+
validateCursor(result.page.nextCursor);
|
|
829
|
+
}
|
|
830
|
+
function validateQueryContext(context) {
|
|
831
|
+
if (context.executionId.asU128() === 0n)
|
|
832
|
+
throw new InvalidError("query context execution id must be nonzero");
|
|
833
|
+
validateName(context.engine.name);
|
|
834
|
+
validateName(context.engine.version);
|
|
835
|
+
const consistencyRank = {
|
|
836
|
+
eventual: 0,
|
|
837
|
+
read_your_writes: 1,
|
|
838
|
+
strong: 2
|
|
839
|
+
};
|
|
840
|
+
if (consistencyRank[context.deliveredConsistency] < consistencyRank[context.requestedConsistency])
|
|
841
|
+
throw new InvalidError("delivered consistency is weaker than requested consistency");
|
|
842
|
+
const target = context.resolvedTarget;
|
|
843
|
+
if (target.kind === "operational") {
|
|
844
|
+
validateName(target.index);
|
|
845
|
+
if (target.backendResourceId.asU128() === 0n ||
|
|
846
|
+
target.backendGeneration === 0n ||
|
|
847
|
+
target.runtimeConfigurationRevision === 0n)
|
|
848
|
+
throw new InvalidError("operational query context is missing backend evidence");
|
|
849
|
+
if (context.boundary !== undefined ||
|
|
850
|
+
context.checkpointRevision !== undefined ||
|
|
851
|
+
context.globalStateRevision !== undefined)
|
|
852
|
+
throw new InvalidError("operational query context cannot carry lakehouse evidence");
|
|
853
|
+
}
|
|
854
|
+
else {
|
|
855
|
+
if (target.destinationId.asU128() === 0n ||
|
|
856
|
+
target.destinationGeneration === 0n ||
|
|
857
|
+
target.backendResourceId.asU128() === 0n ||
|
|
858
|
+
target.backendGeneration === 0n ||
|
|
859
|
+
target.runtimeConfigurationRevision === 0n ||
|
|
860
|
+
target.tableUuid.length !== 16 ||
|
|
861
|
+
target.namespace.length === 0 ||
|
|
862
|
+
target.snapshotId <= 0n ||
|
|
863
|
+
target.schemaId < 0 ||
|
|
864
|
+
target.partitionSpecId < 0 ||
|
|
865
|
+
context.boundary === undefined ||
|
|
866
|
+
context.checkpointRevision === undefined ||
|
|
867
|
+
context.globalStateRevision === undefined)
|
|
868
|
+
throw new InvalidError("lakehouse query context is missing resolved target evidence");
|
|
869
|
+
for (const part of target.namespace)
|
|
870
|
+
validateName(part);
|
|
871
|
+
validateName(target.table);
|
|
872
|
+
}
|
|
873
|
+
if (context.boundary !== undefined && context.boundary.digest.length !== 32)
|
|
874
|
+
throw new InvalidError("query materialization boundary digest must contain 32 bytes");
|
|
875
|
+
}
|
|
876
|
+
export function queryResultValue(result, row, name) {
|
|
877
|
+
const index = result.fields.findIndex((item) => item.name === name);
|
|
878
|
+
return index < 0 ? undefined : row.values[index];
|
|
879
|
+
}
|
|
880
|
+
function snakeToRustVariant(value) {
|
|
881
|
+
return value
|
|
882
|
+
.split("_")
|
|
883
|
+
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
|
|
884
|
+
.join("");
|
|
885
|
+
}
|
|
886
|
+
function rustVariantToSnake(value) {
|
|
887
|
+
return value.replaceAll(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
466
888
|
}
|
|
467
889
|
export function encodeQueryError(error) {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
case "backend":
|
|
478
|
-
return new Map([["Backend", error.message]]);
|
|
479
|
-
case "unavailable":
|
|
480
|
-
return new Map([["Unavailable", error.message]]);
|
|
481
|
-
case "tooLarge":
|
|
482
|
-
return new Map([
|
|
483
|
-
[
|
|
484
|
-
"TooLarge",
|
|
485
|
-
new Map([
|
|
486
|
-
["what", error.what],
|
|
487
|
-
["size", BigInt(error.size)],
|
|
488
|
-
["cap", BigInt(error.cap)]
|
|
489
|
-
])
|
|
490
|
-
]
|
|
491
|
-
]);
|
|
492
|
-
case "version":
|
|
493
|
-
return new Map([
|
|
494
|
-
[
|
|
495
|
-
"Version",
|
|
496
|
-
new Map([
|
|
497
|
-
["expected", BigInt(error.expected)],
|
|
498
|
-
["got", BigInt(error.got)]
|
|
499
|
-
])
|
|
500
|
-
]
|
|
501
|
-
]);
|
|
502
|
-
case "stale":
|
|
503
|
-
return new Map([
|
|
504
|
-
[
|
|
505
|
-
"Stale",
|
|
506
|
-
new Map([
|
|
507
|
-
["what", error.what],
|
|
508
|
-
["applied", error.applied],
|
|
509
|
-
["required", error.required]
|
|
510
|
-
])
|
|
511
|
-
]
|
|
512
|
-
]);
|
|
513
|
-
case "unrecognized":
|
|
514
|
-
return new Map([[error.tag, error.value]]);
|
|
890
|
+
const tag = snakeToRustVariant(error.kind);
|
|
891
|
+
if ("message" in error)
|
|
892
|
+
return mapOf([[tag, error.message]]);
|
|
893
|
+
const body = new Map();
|
|
894
|
+
for (const [key, value] of Object.entries(error)) {
|
|
895
|
+
if (key === "kind")
|
|
896
|
+
continue;
|
|
897
|
+
const wireKey = key.replaceAll(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
898
|
+
body.set(wireKey, value instanceof QueryExecutionId ? value.toBytes() : value);
|
|
515
899
|
}
|
|
900
|
+
return mapOf([[tag, body]]);
|
|
516
901
|
}
|
|
517
902
|
export function decodeQueryError(value, context) {
|
|
518
|
-
const [tag,
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
case "TooLarge": {
|
|
533
|
-
const tooLargeMap = expectMap(inner, context);
|
|
903
|
+
const [tag, body] = singleVariantTag(value, context);
|
|
904
|
+
const kind = rustVariantToSnake(tag);
|
|
905
|
+
if ([
|
|
906
|
+
"unsupported",
|
|
907
|
+
"unauthorized",
|
|
908
|
+
"index_not_found",
|
|
909
|
+
"fork_not_found",
|
|
910
|
+
"backend",
|
|
911
|
+
"unavailable"
|
|
912
|
+
].includes(kind))
|
|
913
|
+
return { kind: kind, message: expectString(body, context) };
|
|
914
|
+
const map = expectMap(body, context);
|
|
915
|
+
switch (kind) {
|
|
916
|
+
case "too_large":
|
|
534
917
|
return {
|
|
535
|
-
kind
|
|
536
|
-
what: field.requiredString(
|
|
537
|
-
size: field.
|
|
538
|
-
cap: field.
|
|
918
|
+
kind,
|
|
919
|
+
what: field.requiredString(map, "what", context),
|
|
920
|
+
size: field.requiredU64(map, "size", context),
|
|
921
|
+
cap: field.requiredU64(map, "cap", context)
|
|
539
922
|
};
|
|
540
|
-
|
|
541
|
-
case "Version": {
|
|
542
|
-
const versionMap = expectMap(inner, context);
|
|
923
|
+
case "version":
|
|
543
924
|
return {
|
|
544
|
-
kind
|
|
545
|
-
expected: field.requiredU32(
|
|
546
|
-
got: field.requiredU32(
|
|
925
|
+
kind,
|
|
926
|
+
expected: field.requiredU32(map, "expected", context),
|
|
927
|
+
got: field.requiredU32(map, "got", context)
|
|
547
928
|
};
|
|
548
|
-
|
|
549
|
-
case "Stale": {
|
|
550
|
-
const staleMap = expectMap(inner, context);
|
|
929
|
+
case "stale":
|
|
551
930
|
return {
|
|
552
|
-
kind
|
|
553
|
-
what: field.requiredString(
|
|
554
|
-
applied: field.requiredU64(
|
|
555
|
-
required: field.requiredU64(
|
|
931
|
+
kind,
|
|
932
|
+
what: field.requiredString(map, "what", context),
|
|
933
|
+
applied: field.requiredU64(map, "applied", context),
|
|
934
|
+
required: field.requiredU64(map, "required", context)
|
|
935
|
+
};
|
|
936
|
+
case "cancelled":
|
|
937
|
+
case "deadline_exceeded":
|
|
938
|
+
return {
|
|
939
|
+
kind,
|
|
940
|
+
executionId: QueryExecutionId.fromBytes(field.requiredBytes(map, "execution_id", context))
|
|
941
|
+
};
|
|
942
|
+
case "expired_snapshot":
|
|
943
|
+
return { kind, snapshotId: field.requiredI64(map, "snapshot_id", context) };
|
|
944
|
+
case "stale_generation":
|
|
945
|
+
return {
|
|
946
|
+
kind,
|
|
947
|
+
what: field.requiredString(map, "what", context),
|
|
948
|
+
requested: field.requiredU64(map, "requested", context),
|
|
949
|
+
observed: field.requiredU64(map, "observed", context)
|
|
950
|
+
};
|
|
951
|
+
case "target_unavailable":
|
|
952
|
+
return { kind, reason: field.requiredString(map, "reason", context) };
|
|
953
|
+
case "resource_limit":
|
|
954
|
+
return {
|
|
955
|
+
kind,
|
|
956
|
+
resource: field.requiredString(map, "resource", context),
|
|
957
|
+
observed: field.requiredU64(map, "observed", context),
|
|
958
|
+
limit: field.requiredU64(map, "limit", context)
|
|
556
959
|
};
|
|
557
|
-
}
|
|
558
960
|
default:
|
|
559
|
-
|
|
961
|
+
throw new CodecError(`unknown query error \`${tag}\``, context, "error");
|
|
560
962
|
}
|
|
561
963
|
}
|
|
562
|
-
export function
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
case "err":
|
|
567
|
-
return new Map([["Err", encodeQueryError(reply.error)]]);
|
|
568
|
-
case "unrecognized":
|
|
569
|
-
return new Map([[reply.tag, reply.value]]);
|
|
570
|
-
}
|
|
964
|
+
export function encodeQueryReplyFrame(reply) {
|
|
965
|
+
return encodeNamed(reply.kind === "ok"
|
|
966
|
+
? mapOf([["Ok", encodeQueryResult(reply.result)]])
|
|
967
|
+
: mapOf([["Err", encodeQueryError(reply.error)]]), { forceFloatNumbers: true });
|
|
571
968
|
}
|
|
572
969
|
export function decodeQueryReply(value, context) {
|
|
573
|
-
const [tag,
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
default:
|
|
580
|
-
return { kind: "unrecognized", tag, value: inner };
|
|
581
|
-
}
|
|
970
|
+
const [tag, body] = singleVariantTag(value, context);
|
|
971
|
+
if (tag === "Ok")
|
|
972
|
+
return { kind: "ok", result: decodeQueryResult(expectMap(body, context), context) };
|
|
973
|
+
if (tag === "Err")
|
|
974
|
+
return { kind: "err", error: decodeQueryError(body, context) };
|
|
975
|
+
throw new CodecError(`unknown query reply \`${tag}\``, context, "reply");
|
|
582
976
|
}
|
|
583
|
-
export function
|
|
584
|
-
return
|
|
977
|
+
export function decodeQueryReplyFrame(bytes) {
|
|
978
|
+
return decodeQueryReply(decodeOne(bytes, "QueryReply"), "QueryReply");
|
|
585
979
|
}
|
|
586
|
-
export function
|
|
587
|
-
|
|
980
|
+
export function encodeQueryExecutionStatus(value) {
|
|
981
|
+
const map = mapOf([
|
|
982
|
+
["execution_id", value.executionId.toBytes()],
|
|
983
|
+
["state", value.state],
|
|
984
|
+
["started_at_micros", value.startedAtMicros]
|
|
985
|
+
]);
|
|
986
|
+
optional(map, "finished_at_micros", value.finishedAtMicros);
|
|
987
|
+
map.set("scanned_bytes", value.scannedBytes);
|
|
988
|
+
map.set("produced_bytes", value.producedBytes);
|
|
989
|
+
map.set("row_count", value.rowCount);
|
|
990
|
+
optional(map, "error", value.error, encodeQueryError);
|
|
991
|
+
return map;
|
|
588
992
|
}
|
|
589
|
-
export function
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
993
|
+
export function decodeQueryExecutionStatus(map, context) {
|
|
994
|
+
const finishedAtMicros = field.optionalU64(map, "finished_at_micros", context);
|
|
995
|
+
const error = map.get("error");
|
|
996
|
+
const status = {
|
|
997
|
+
executionId: QueryExecutionId.fromBytes(field.requiredBytes(map, "execution_id", context)),
|
|
998
|
+
state: decodeWord(map, "state", context, [
|
|
999
|
+
"queued",
|
|
1000
|
+
"planning",
|
|
1001
|
+
"running",
|
|
1002
|
+
"completed",
|
|
1003
|
+
"cancelled",
|
|
1004
|
+
"failed",
|
|
1005
|
+
"expired"
|
|
1006
|
+
]),
|
|
1007
|
+
startedAtMicros: field.requiredU64(map, "started_at_micros", context),
|
|
1008
|
+
...(finishedAtMicros === undefined ? {} : { finishedAtMicros }),
|
|
1009
|
+
scannedBytes: field.requiredU64(map, "scanned_bytes", context),
|
|
1010
|
+
producedBytes: field.requiredU64(map, "produced_bytes", context),
|
|
1011
|
+
rowCount: field.requiredU64(map, "row_count", context),
|
|
1012
|
+
...(error === undefined ? {} : { error: decodeQueryError(error, `${context}.error`) })
|
|
596
1013
|
};
|
|
1014
|
+
validateQueryExecutionStatus(status);
|
|
1015
|
+
return status;
|
|
1016
|
+
}
|
|
1017
|
+
export function validateQueryExecutionStatus(status) {
|
|
1018
|
+
if (status.executionId.asU128() === 0n || status.startedAtMicros === 0n)
|
|
1019
|
+
throw new InvalidError("query execution identity and start time must be nonzero");
|
|
1020
|
+
const terminal = status.state === "completed" ||
|
|
1021
|
+
status.state === "cancelled" ||
|
|
1022
|
+
status.state === "failed" ||
|
|
1023
|
+
status.state === "expired";
|
|
1024
|
+
if ((terminal &&
|
|
1025
|
+
(status.finishedAtMicros === undefined ||
|
|
1026
|
+
status.finishedAtMicros < status.startedAtMicros)) ||
|
|
1027
|
+
(!terminal && status.finishedAtMicros !== undefined))
|
|
1028
|
+
throw new InvalidError("query execution finish time does not match its state");
|
|
1029
|
+
if ((status.state === "failed") !== (status.error !== undefined))
|
|
1030
|
+
throw new InvalidError("query execution error must be present exactly for failed state");
|
|
1031
|
+
}
|
|
1032
|
+
export function encodeQueryStatusReplyFrame(reply) {
|
|
1033
|
+
return encodeNamed(reply.kind === "ok"
|
|
1034
|
+
? mapOf([["Ok", encodeQueryExecutionStatus(reply.status)]])
|
|
1035
|
+
: mapOf([["Err", encodeQueryError(reply.error)]]));
|
|
1036
|
+
}
|
|
1037
|
+
export function decodeQueryStatusReply(value, context) {
|
|
1038
|
+
const [tag, body] = singleVariantTag(value, context);
|
|
1039
|
+
if (tag === "Ok")
|
|
1040
|
+
return { kind: "ok", status: decodeQueryExecutionStatus(expectMap(body, context), context) };
|
|
1041
|
+
if (tag === "Err")
|
|
1042
|
+
return { kind: "err", error: decodeQueryError(body, context) };
|
|
1043
|
+
throw new CodecError(`unknown query status reply \`${tag}\``, context, "reply");
|
|
1044
|
+
}
|
|
1045
|
+
export function decodeQueryStatusReplyFrame(bytes) {
|
|
1046
|
+
return decodeQueryStatusReply(decodeOne(bytes, "QueryStatusReply"), "QueryStatusReply");
|
|
1047
|
+
}
|
|
1048
|
+
function decodeWord(map, key, context, allowed) {
|
|
1049
|
+
const value = field.requiredString(map, key, context);
|
|
1050
|
+
if (!allowed.includes(value))
|
|
1051
|
+
throw new CodecError(`field \`${key}\` has unsupported value \`${value}\``, context, key);
|
|
1052
|
+
return value;
|
|
1053
|
+
}
|
|
1054
|
+
function decodeOptionalWord(map, key, context, allowed) {
|
|
1055
|
+
const value = field.optionalString(map, key, context);
|
|
1056
|
+
if (value === undefined)
|
|
1057
|
+
return undefined;
|
|
1058
|
+
if (!allowed.includes(value))
|
|
1059
|
+
throw new CodecError(`field \`${key}\` has unsupported value \`${value}\``, context, key);
|
|
1060
|
+
return value;
|
|
1061
|
+
}
|
|
1062
|
+
function asU64(value, context) {
|
|
1063
|
+
if (typeof value === "bigint" && value >= 0n)
|
|
1064
|
+
return value;
|
|
1065
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0)
|
|
1066
|
+
return BigInt(value);
|
|
1067
|
+
throw new CodecError("value must fit u64", context, "value");
|
|
1068
|
+
}
|
|
1069
|
+
function fixedBytes(value, length, context) {
|
|
1070
|
+
if (value.length !== length)
|
|
1071
|
+
throw new CodecError(`expected ${String(length)} bytes`, context, "bytes");
|
|
1072
|
+
return value;
|
|
1073
|
+
}
|
|
1074
|
+
function validateName(value) {
|
|
1075
|
+
if (value.length === 0 ||
|
|
1076
|
+
byteLength(value) > MAX_QUERY_NAME_BYTES ||
|
|
1077
|
+
value.trim() !== value ||
|
|
1078
|
+
hasControlCharacter(value))
|
|
1079
|
+
throw new InvalidError("query name is invalid");
|
|
1080
|
+
}
|
|
1081
|
+
function byteLength(value) {
|
|
1082
|
+
return new TextEncoder().encode(value).length;
|
|
1083
|
+
}
|
|
1084
|
+
function hasControlCharacter(value) {
|
|
1085
|
+
for (const character of value) {
|
|
1086
|
+
const code = character.codePointAt(0) ?? 0;
|
|
1087
|
+
if (code < 32 || code === 127)
|
|
1088
|
+
return true;
|
|
1089
|
+
}
|
|
1090
|
+
return false;
|
|
1091
|
+
}
|
|
1092
|
+
function hasDisallowedSqlControl(value) {
|
|
1093
|
+
for (const character of value) {
|
|
1094
|
+
if (hasControlCharacter(character) &&
|
|
1095
|
+
character !== "\n" &&
|
|
1096
|
+
character !== "\r" &&
|
|
1097
|
+
character !== "\t")
|
|
1098
|
+
return true;
|
|
1099
|
+
}
|
|
1100
|
+
return false;
|
|
1101
|
+
}
|
|
1102
|
+
export function parseConsistency(value, context) {
|
|
1103
|
+
if (value === "eventual" || value === "read_your_writes" || value === "strong")
|
|
1104
|
+
return value;
|
|
1105
|
+
throw new CodecError(`unknown consistency \`${value}\``, context, "consistency");
|
|
1106
|
+
}
|
|
1107
|
+
export function consistencyToWord(value) {
|
|
1108
|
+
return value;
|
|
1109
|
+
}
|
|
1110
|
+
export function pageAtLeast(page, rowsOnPage) {
|
|
1111
|
+
return page.offset === undefined ? undefined : page.offset + BigInt(rowsOnPage);
|
|
1112
|
+
}
|
|
1113
|
+
export function pageTotalPages(page) {
|
|
1114
|
+
return page.total === undefined || page.limit === 0
|
|
1115
|
+
? undefined
|
|
1116
|
+
: (page.total + BigInt(page.limit) - 1n) / BigInt(page.limit);
|
|
1117
|
+
}
|
|
1118
|
+
export function consistencyGateCheck(applied, required, consistency, what) {
|
|
1119
|
+
return consistency === "eventual" || applied >= required
|
|
1120
|
+
? undefined
|
|
1121
|
+
: { kind: "stale", what, applied, required };
|
|
597
1122
|
}
|
|
598
1123
|
//# sourceMappingURL=query.js.map
|