@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.
Files changed (142) hide show
  1. package/README.md +65 -5
  2. package/dist/agent/reliable-consumer.d.ts.map +1 -1
  3. package/dist/agent/reliable-consumer.js +1 -0
  4. package/dist/agent/reliable-consumer.js.map +1 -1
  5. package/dist/agent/workflow.d.ts +2 -0
  6. package/dist/agent/workflow.d.ts.map +1 -1
  7. package/dist/agent/workflow.js +114 -13
  8. package/dist/agent/workflow.js.map +1 -1
  9. package/dist/bridges/a2a.d.ts +1 -1
  10. package/dist/bridges/a2a.js +1 -1
  11. package/dist/client/capabilities.d.ts +14 -1
  12. package/dist/client/capabilities.d.ts.map +1 -1
  13. package/dist/client/capabilities.js +57 -23
  14. package/dist/client/capabilities.js.map +1 -1
  15. package/dist/client/errors.d.ts +6 -1
  16. package/dist/client/errors.d.ts.map +1 -1
  17. package/dist/client/errors.js +5 -0
  18. package/dist/client/errors.js.map +1 -1
  19. package/dist/client/laser.d.ts +8 -0
  20. package/dist/client/laser.d.ts.map +1 -1
  21. package/dist/client/laser.js +42 -10
  22. package/dist/client/laser.js.map +1 -1
  23. package/dist/client/managed.d.ts +3 -1
  24. package/dist/client/managed.d.ts.map +1 -1
  25. package/dist/client/managed.js +2 -2
  26. package/dist/client/managed.js.map +1 -1
  27. package/dist/full.d.ts +1 -0
  28. package/dist/full.d.ts.map +1 -1
  29. package/dist/full.js +1 -0
  30. package/dist/full.js.map +1 -1
  31. package/dist/iggy/apache-iggy.d.ts +6 -2
  32. package/dist/iggy/apache-iggy.d.ts.map +1 -1
  33. package/dist/iggy/apache-iggy.js +11 -10
  34. package/dist/iggy/apache-iggy.js.map +1 -1
  35. package/dist/index.d.ts +11 -5
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +5 -3
  38. package/dist/index.js.map +1 -1
  39. package/dist/managed/batch.d.ts.map +1 -1
  40. package/dist/managed/batch.js +23 -2
  41. package/dist/managed/batch.js.map +1 -1
  42. package/dist/managed/destinations.d.ts +19 -0
  43. package/dist/managed/destinations.d.ts.map +1 -0
  44. package/dist/managed/destinations.js +90 -0
  45. package/dist/managed/destinations.js.map +1 -0
  46. package/dist/managed/kv.d.ts +33 -6
  47. package/dist/managed/kv.d.ts.map +1 -1
  48. package/dist/managed/kv.js +99 -17
  49. package/dist/managed/kv.js.map +1 -1
  50. package/dist/managed/query.d.ts +26 -19
  51. package/dist/managed/query.d.ts.map +1 -1
  52. package/dist/managed/query.js +97 -55
  53. package/dist/managed/query.js.map +1 -1
  54. package/dist/stream/publish.d.ts +3 -0
  55. package/dist/stream/publish.d.ts.map +1 -1
  56. package/dist/stream/publish.js +25 -0
  57. package/dist/stream/publish.js.map +1 -1
  58. package/dist/stream/record.d.ts +4 -1
  59. package/dist/stream/record.d.ts.map +1 -1
  60. package/dist/stream/record.js +34 -3
  61. package/dist/stream/record.js.map +1 -1
  62. package/dist/wire/arrow.d.ts +39 -0
  63. package/dist/wire/arrow.d.ts.map +1 -0
  64. package/dist/wire/arrow.js +133 -0
  65. package/dist/wire/arrow.js.map +1 -0
  66. package/dist/wire/authz.d.ts +2 -2
  67. package/dist/wire/authz.d.ts.map +1 -1
  68. package/dist/wire/authz.js +39 -20
  69. package/dist/wire/authz.js.map +1 -1
  70. package/dist/wire/cbor.d.ts +9 -1
  71. package/dist/wire/cbor.d.ts.map +1 -1
  72. package/dist/wire/cbor.js +45 -2
  73. package/dist/wire/cbor.js.map +1 -1
  74. package/dist/wire/checkpoint.d.ts +386 -0
  75. package/dist/wire/checkpoint.d.ts.map +1 -0
  76. package/dist/wire/checkpoint.js +1373 -0
  77. package/dist/wire/checkpoint.js.map +1 -0
  78. package/dist/wire/codes.d.ts +10 -0
  79. package/dist/wire/codes.d.ts.map +1 -1
  80. package/dist/wire/codes.js +14 -0
  81. package/dist/wire/codes.js.map +1 -1
  82. package/dist/wire/commands.d.ts +14 -5
  83. package/dist/wire/commands.d.ts.map +1 -1
  84. package/dist/wire/commands.js +75 -7
  85. package/dist/wire/commands.js.map +1 -1
  86. package/dist/wire/control.d.ts +3 -12
  87. package/dist/wire/control.d.ts.map +1 -1
  88. package/dist/wire/control.js +10 -43
  89. package/dist/wire/control.js.map +1 -1
  90. package/dist/wire/destination.d.ts +86 -0
  91. package/dist/wire/destination.d.ts.map +1 -0
  92. package/dist/wire/destination.js +251 -0
  93. package/dist/wire/destination.js.map +1 -0
  94. package/dist/wire/headers.d.ts +1 -0
  95. package/dist/wire/headers.d.ts.map +1 -1
  96. package/dist/wire/headers.js +1 -0
  97. package/dist/wire/headers.js.map +1 -1
  98. package/dist/wire/hello.d.ts +83 -11
  99. package/dist/wire/hello.d.ts.map +1 -1
  100. package/dist/wire/hello.js +292 -73
  101. package/dist/wire/hello.js.map +1 -1
  102. package/dist/wire/http.d.ts +153 -1
  103. package/dist/wire/http.d.ts.map +1 -1
  104. package/dist/wire/http.js +491 -10
  105. package/dist/wire/http.js.map +1 -1
  106. package/dist/wire/ids.d.ts +60 -0
  107. package/dist/wire/ids.d.ts.map +1 -1
  108. package/dist/wire/ids.js +143 -0
  109. package/dist/wire/ids.js.map +1 -1
  110. package/dist/wire/index.d.ts +5 -0
  111. package/dist/wire/index.d.ts.map +1 -1
  112. package/dist/wire/index.js +5 -0
  113. package/dist/wire/index.js.map +1 -1
  114. package/dist/wire/kv.d.ts +35 -0
  115. package/dist/wire/kv.d.ts.map +1 -1
  116. package/dist/wire/kv.js +178 -18
  117. package/dist/wire/kv.js.map +1 -1
  118. package/dist/wire/limits.d.ts +10 -0
  119. package/dist/wire/limits.d.ts.map +1 -1
  120. package/dist/wire/limits.js +10 -0
  121. package/dist/wire/limits.js.map +1 -1
  122. package/dist/wire/mutation.d.ts +10 -0
  123. package/dist/wire/mutation.d.ts.map +1 -1
  124. package/dist/wire/mutation.js +14 -0
  125. package/dist/wire/mutation.js.map +1 -1
  126. package/dist/wire/query.d.ts +192 -97
  127. package/dist/wire/query.d.ts.map +1 -1
  128. package/dist/wire/query.js +968 -443
  129. package/dist/wire/query.js.map +1 -1
  130. package/dist/wire/result.d.ts +6 -0
  131. package/dist/wire/result.d.ts.map +1 -1
  132. package/dist/wire/result.js +19 -2
  133. package/dist/wire/result.js.map +1 -1
  134. package/dist/wire/schema.d.ts +121 -0
  135. package/dist/wire/schema.d.ts.map +1 -0
  136. package/dist/wire/schema.js +778 -0
  137. package/dist/wire/schema.js.map +1 -0
  138. package/dist/wire/source.d.ts +35 -0
  139. package/dist/wire/source.d.ts.map +1 -0
  140. package/dist/wire/source.js +105 -0
  141. package/dist/wire/source.js.map +1 -0
  142. package/package.json +5 -5
@@ -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
- import { decodeValue, encodeValue } from "./value.js";
5
- export function encodeKeyMatch(match) {
6
- return new Map([
7
- ["field", match.field],
8
- ["value", match.value]
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 decodeKeyMatch(map, context) {
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
- field: field.requiredString(map, "field", context),
14
- value: field.requiredString(map, "value", context)
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
- const CMP_OPS = new Set([
18
- "eq",
19
- "ne",
20
- "lt",
21
- "lte",
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 encodePredicate(predicate) {
35
- return new Map([
36
- ["field", predicate.field],
37
- ["op", predicate.op],
38
- ["value", encodeValue(predicate.value)]
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 decodePredicate(map, context) {
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
- field: field.requiredString(map, "field", context),
44
- op: parseCmpOp(field.requiredString(map, "op", context), context),
45
- value: decodeValue(map.get("value"), `${context}.value`)
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 encodeFilter(filter) {
61
- switch (filter.kind) {
62
- case "all":
63
- return new Map([["all", filter.filters.map((child) => encodeFilter(child))]]);
64
- case "any":
65
- return new Map([["any", filter.filters.map((child) => encodeFilter(child))]]);
66
- case "not":
67
- return new Map([["not", encodeFilter(filter.filter)]]);
68
- case "pred":
69
- return new Map([["pred", encodePredicate(filter.predicate)]]);
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
- export function decodeFilter(value, context) {
73
- const map = expectMap(value, context);
74
- if (map.size !== 1) {
75
- throw new CodecError(`expected exactly one filter tag in ${context}`, context, "filter");
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
- const [first] = Array.from(map.entries());
78
- if (first === undefined) {
79
- throw new CodecError(`expected exactly one filter tag in ${context}`, context, "filter");
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
- const [tag, inner] = first;
82
- switch (tag) {
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 { kind: "all", filters: expectFilterArray(inner, context) };
194
+ return mapOf([["all", value.filters.map(encodeFilter)]]);
85
195
  case "any":
86
- return { kind: "any", filters: expectFilterArray(inner, context) };
196
+ return mapOf([["any", value.filters.map(encodeFilter)]]);
87
197
  case "not":
88
- return { kind: "not", filter: decodeFilter(inner, context) };
198
+ return mapOf([["not", encodeFilter(value.filter)]]);
89
199
  case "pred":
90
- return { kind: "pred", predicate: decodePredicate(expectMap(inner, context), context) };
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 expectFilterArray(value, context) {
96
- if (!Array.isArray(value)) {
97
- throw new CodecError(`expected an array in ${context}`, context, "filter");
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
- return value.map((item, index) => decodeFilter(item, `${context}[${String(index)}]`));
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
- export function encodeRawSql(rawSql) {
102
- const map = new Map();
103
- map.set("sql", rawSql.sql);
104
- if (rawSql.params.length > 0)
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
- export function decodeRawSql(map, context) {
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 !== undefined ? parseDir(dir, context) : "asc"
231
+ dir: decodeOptionalWord(map, "dir", context, ["asc", "desc"]) ?? "asc"
131
232
  };
132
233
  }
133
- export function encodeTextQuery(text) {
234
+ function encodeText(value) {
134
235
  const map = new Map();
135
- if (text.field !== undefined)
136
- map.set("field", text.field);
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
- export function decodeTextQuery(map, context) {
240
+ function decodeText(map, context) {
141
241
  const textField = field.optionalString(map, "field", context);
142
242
  return {
143
- ...(textField !== undefined ? { field: textField } : {}),
243
+ ...(textField === undefined ? {} : { field: textField }),
144
244
  query: field.requiredString(map, "query", context)
145
245
  };
146
246
  }
147
- export function encodeVectorQuery(vector) {
148
- return new Map([
149
- ["field", vector.field],
150
- ["embedding", [...vector.embedding]],
151
- ["top_k", BigInt(vector.topK)]
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
- export function decodeVectorQuery(map, context) {
254
+ function decodeVector(map, context) {
155
255
  return {
156
256
  field: field.requiredString(map, "field", context),
157
- embedding: field.requiredArray(map, "embedding", context, (item, index) => expectNumber(item, `${context}.embedding[${String(index)}]`)),
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 expectNumber(value, context) {
162
- if (typeof value !== "number") {
163
- throw new CodecError(`expected a number in ${context}`, context, "value");
164
- }
165
- return value;
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
- export function decodeAggCall(map, context) {
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: parseAggFunc(field.requiredString(map, "func", context), context),
207
- ...(callField !== undefined ? { field: callField } : {}),
208
- ...(arg !== undefined ? { arg } : {}),
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
- export function encodeWindow(window) {
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 (aggregate.groupBy.length > 0)
227
- map.set("group_by", [...aggregate.groupBy]);
228
- map.set("funcs", aggregate.funcs.map((call) => encodeAggCall(call)));
229
- if (aggregate.window !== undefined)
230
- map.set("window", encodeWindow(aggregate.window));
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
- export function decodeAggregate(map, context) {
234
- const windowMap = field.optionalMap(map, "window", context);
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
- ...(windowMap !== undefined ? { window: decodeWindow(windowMap, `${context}.window`) } : {})
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
- export function encodeSelect(select) {
317
+ function encodeSelect(value) {
242
318
  const map = new Map();
243
- if (select.fields.length > 0)
244
- map.set("fields", [...select.fields]);
245
- map.set("payload", select.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
- export function decodeSelect(map, context) {
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
- export function parseConsistency(word, context) {
255
- switch (word) {
256
- case "eventual":
257
- return "eventual";
258
- case "read_your_writes":
259
- return "readYourWrites";
260
- case "strong":
261
- return "strong";
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
- export function consistencyToWord(value) {
267
- switch (value) {
268
- case "eventual":
269
- return "eventual";
270
- case "readYourWrites":
271
- return "read_your_writes";
272
- case "strong":
273
- return "strong";
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 = new Map();
278
- map.set("index", query.index);
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((match) => encodeKeyMatch(match)));
281
- if (query.messageType !== undefined)
282
- map.set("message_type", query.messageType);
283
- if (query.timeRange !== undefined)
284
- map.set("time_range", [...query.timeRange]);
285
- if (query.filter !== undefined)
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((sort) => encodeSort(sort)));
293
- map.set("limit", BigInt(query.limit));
294
- map.set("offset", BigInt(query.offset));
295
- if (query.aggregate !== undefined)
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
- if (query.fork !== undefined)
303
- map.set("fork", query.fork);
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", consistencyToWord(query.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 timeRangeRaw = map.get("time_range");
315
- const filterRaw = map.get("filter");
316
- const vectorMap = field.optionalMap(map, "vector", context);
317
- const textMap = field.optionalMap(map, "text", context);
318
- const aggregateMap = field.optionalMap(map, "aggregate", context);
319
- const havingRaw = map.get("having");
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 rawSqlMap = field.optionalMap(map, "raw_sql", context);
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
- index: field.requiredString(map, "index", context),
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 !== undefined ? { messageType } : {}),
328
- ...(timeRangeRaw !== undefined ? { timeRange: expectU64Pair(timeRangeRaw, context) } : {}),
329
- ...(filterRaw !== undefined ? { filter: decodeFilter(filterRaw, `${context}.filter`) } : {}),
330
- ...(vectorMap !== undefined
331
- ? { vector: decodeVectorQuery(vectorMap, `${context}.vector`) }
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
- limit: field.requiredU32(map, "limit", context),
336
- offset: field.optionalU32(map, "offset", context) ?? 0,
337
- ...(aggregateMap !== undefined
338
- ? { aggregate: decodeAggregate(aggregateMap, `${context}.aggregate`) }
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: selectMap !== undefined
343
- ? decodeSelect(selectMap, `${context}.select`)
344
- : { fields: [], payload: false },
345
- ...(fork !== undefined ? { fork } : {}),
346
- ...(rawSqlMap !== undefined ? { rawSql: decodeRawSql(rawSqlMap, `${context}.raw_sql`) } : {}),
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 expectU64Pair(value, context) {
352
- if (!Array.isArray(value) || value.length !== 2) {
353
- throw new CodecError(`expected a 2-element array in ${context}.time_range`, context, "time_range");
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 expectU64Value(value, context) {
359
- if (typeof value === "bigint")
360
- return value;
361
- if (typeof value === "number" && Number.isInteger(value) && value >= 0)
362
- return BigInt(value);
363
- throw new CodecError(`expected an unsigned integer in ${context}.time_range`, context, "time_range");
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 new Map([
367
- ["v", BigInt(QUERY_OP_VERSION)],
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
- field.requiredU32(map, "v", context);
373
- return { query: decodeQuery(field.requiredMap(map, "query", context), `${context}.query`) };
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 encodePage(page) {
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.set("offset", BigInt(page.offset));
381
- map.set("limit", BigInt(page.limit));
382
- if (page.total !== undefined)
383
- map.set("total", page.total);
384
- map.set("has_more", page.hasMore);
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
- export function decodePage(map, context) {
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: field.requiredU32(map, "offset", context),
654
+ ...(offset === undefined ? {} : { offset }),
391
655
  limit: field.requiredU32(map, "limit", context),
392
- ...(total !== undefined ? { 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
- export function pageAtLeast(page, rowsOnPage) {
397
- return page.offset + rowsOnPage;
398
- }
399
- export function pageTotalPages(page) {
400
- if (page.total === undefined || page.limit === 0)
401
- return undefined;
402
- return (page.total + BigInt(page.limit) - 1n) / BigInt(page.limit);
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 encodeStringMap(entries) {
405
- return new Map(entries);
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 decodeStringMap(map, context) {
408
- const result = new Map();
409
- for (const [key, value] of map) {
410
- if (typeof key !== "string" || typeof value !== "string") {
411
- throw new CodecError(`${context} must map strings to strings`, context, "value");
412
- }
413
- result.set(key, value);
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
- return result;
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
- export function encodeRow(row) {
418
- const map = new Map();
419
- map.set("headers", encodeStringMap(row.headers));
420
- if (row.metadata.size > 0)
421
- map.set("metadata", encodeStringMap(row.metadata));
422
- if (row.partition !== undefined)
423
- map.set("partition", BigInt(row.partition));
424
- if (row.offset !== undefined)
425
- map.set("offset", row.offset);
426
- if (row.stream !== undefined)
427
- map.set("stream", BigInt(row.stream));
428
- if (row.topic !== undefined)
429
- map.set("topic", BigInt(row.topic));
430
- if (row.payload !== undefined)
431
- map.set("payload", row.payload);
432
- if (row.score !== undefined)
433
- map.set("score", row.score);
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
- export function decodeRow(map, context) {
437
- const metadataMap = field.optionalMap(map, "metadata", context);
438
- const partition = field.optionalU32(map, "partition", context);
439
- const offset = field.optionalU64(map, "offset", context);
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
- headers: decodeStringMap(field.requiredMap(map, "headers", context), `${context}.headers`),
446
- metadata: metadataMap !== undefined ? decodeStringMap(metadataMap, `${context}.metadata`) : new Map(),
447
- ...(partition !== undefined ? { partition } : {}),
448
- ...(offset !== undefined ? { offset } : {}),
449
- ...(stream !== undefined ? { stream } : {}),
450
- ...(topic !== undefined ? { topic } : {}),
451
- ...(payload !== undefined ? { payload } : {}),
452
- ...(score !== undefined ? { score } : {})
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(result) {
456
- return new Map([
457
- ["rows", result.rows.map((row) => encodeRow(row))],
458
- ["page", encodePage(result.page)]
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
- return {
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
- switch (error.kind) {
469
- case "unsupported":
470
- return new Map([["Unsupported", error.message]]);
471
- case "unauthorized":
472
- return new Map([["Unauthorized", error.message]]);
473
- case "indexNotFound":
474
- return new Map([["IndexNotFound", error.message]]);
475
- case "forkNotFound":
476
- return new Map([["ForkNotFound", error.message]]);
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, inner] = singleVariantTag(value, context);
519
- switch (tag) {
520
- case "Unsupported":
521
- return { kind: "unsupported", message: expectString(inner, context) };
522
- case "Unauthorized":
523
- return { kind: "unauthorized", message: expectString(inner, context) };
524
- case "IndexNotFound":
525
- return { kind: "indexNotFound", message: expectString(inner, context) };
526
- case "ForkNotFound":
527
- return { kind: "forkNotFound", message: expectString(inner, context) };
528
- case "Backend":
529
- return { kind: "backend", message: expectString(inner, context) };
530
- case "Unavailable":
531
- return { kind: "unavailable", message: expectString(inner, context) };
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: "tooLarge",
536
- what: field.requiredString(tooLargeMap, "what", context),
537
- size: field.requiredU32(tooLargeMap, "size", context),
538
- cap: field.requiredU32(tooLargeMap, "cap", context)
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: "version",
545
- expected: field.requiredU32(versionMap, "expected", context),
546
- got: field.requiredU32(versionMap, "got", context)
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: "stale",
553
- what: field.requiredString(staleMap, "what", context),
554
- applied: field.requiredU64(staleMap, "applied", context),
555
- required: field.requiredU64(staleMap, "required", context)
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
- return { kind: "unrecognized", tag, value: inner };
961
+ throw new CodecError(`unknown query error \`${tag}\``, context, "error");
560
962
  }
561
963
  }
562
- export function encodeQueryReply(reply) {
563
- switch (reply.kind) {
564
- case "ok":
565
- return new Map([["Ok", encodeQueryResult(reply.result)]]);
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, inner] = singleVariantTag(value, context);
574
- switch (tag) {
575
- case "Ok":
576
- return { kind: "ok", result: decodeQueryResult(expectMap(inner, context), context) };
577
- case "Err":
578
- return { kind: "err", error: decodeQueryError(inner, context) };
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 encodeQueryReplyFrame(reply) {
584
- return encodeNamed(encodeQueryReply(reply), { forceFloatNumbers: true });
977
+ export function decodeQueryReplyFrame(bytes) {
978
+ return decodeQueryReply(decodeOne(bytes, "QueryReply"), "QueryReply");
585
979
  }
586
- export function consistencyGateIsCaughtUp(gate) {
587
- return gate.applied >= gate.required;
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 consistencyGateCheck(gate, level, what) {
590
- if (level === "eventual" || consistencyGateIsCaughtUp(gate)) {
591
- return { ok: true };
592
- }
593
- return {
594
- ok: false,
595
- error: { kind: "stale", what, applied: gate.applied, required: gate.required }
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