@wavehouse/chtypes 0.5.2 → 1.0.2

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 (195) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +34 -38
  3. package/dist/abi1/buildinfo.d.ts +40 -0
  4. package/dist/abi1/buildinfo.d.ts.map +1 -0
  5. package/dist/abi1/buildinfo.js +83 -0
  6. package/dist/abi1/buildinfo.js.map +1 -0
  7. package/dist/abi1/calls.gen.d.ts +55 -0
  8. package/dist/abi1/calls.gen.d.ts.map +1 -0
  9. package/dist/abi1/calls.gen.js +110 -0
  10. package/dist/abi1/calls.gen.js.map +1 -0
  11. package/dist/abi1/decls.gen.d.ts +115 -0
  12. package/dist/abi1/decls.gen.d.ts.map +1 -0
  13. package/dist/abi1/decls.gen.js +1613 -0
  14. package/dist/abi1/decls.gen.js.map +1 -0
  15. package/dist/abi1/errmap.gen.d.ts +18 -0
  16. package/dist/abi1/errmap.gen.d.ts.map +1 -0
  17. package/dist/abi1/errmap.gen.js +66 -0
  18. package/dist/abi1/errmap.gen.js.map +1 -0
  19. package/dist/abi1/errors.d.ts +107 -0
  20. package/dist/abi1/errors.d.ts.map +1 -0
  21. package/dist/abi1/errors.js +134 -0
  22. package/dist/abi1/errors.js.map +1 -0
  23. package/dist/abi1/handles.d.ts +31 -0
  24. package/dist/abi1/handles.d.ts.map +1 -0
  25. package/dist/abi1/handles.js +76 -0
  26. package/dist/abi1/handles.js.map +1 -0
  27. package/dist/abi1/index.d.ts +17 -0
  28. package/dist/abi1/index.d.ts.map +1 -0
  29. package/dist/abi1/index.js +17 -0
  30. package/dist/abi1/index.js.map +1 -0
  31. package/dist/abi1/libc.d.ts +54 -0
  32. package/dist/abi1/libc.d.ts.map +1 -0
  33. package/dist/abi1/libc.gen.d.ts +12 -0
  34. package/dist/abi1/libc.gen.d.ts.map +1 -0
  35. package/dist/abi1/libc.gen.js +75 -0
  36. package/dist/abi1/libc.gen.js.map +1 -0
  37. package/dist/abi1/libc.js +109 -0
  38. package/dist/abi1/libc.js.map +1 -0
  39. package/dist/abi1/loader.d.ts +101 -0
  40. package/dist/abi1/loader.d.ts.map +1 -0
  41. package/dist/abi1/loader.js +274 -0
  42. package/dist/abi1/loader.js.map +1 -0
  43. package/dist/abi1/raw.d.ts +104 -0
  44. package/dist/abi1/raw.d.ts.map +1 -0
  45. package/dist/abi1/raw.js +296 -0
  46. package/dist/abi1/raw.js.map +1 -0
  47. package/dist/abi1/strictjson.d.ts +19 -0
  48. package/dist/abi1/strictjson.d.ts.map +1 -0
  49. package/dist/abi1/strictjson.js +78 -0
  50. package/dist/abi1/strictjson.js.map +1 -0
  51. package/dist/abi1/vocab.gen.d.ts +169 -0
  52. package/dist/abi1/vocab.gen.d.ts.map +1 -0
  53. package/dist/abi1/vocab.gen.js +306 -0
  54. package/dist/abi1/vocab.gen.js.map +1 -0
  55. package/dist/cli.d.ts +25 -26
  56. package/dist/cli.d.ts.map +1 -1
  57. package/dist/cli.js +206 -218
  58. package/dist/cli.js.map +1 -1
  59. package/dist/documents.d.ts +195 -0
  60. package/dist/documents.d.ts.map +1 -0
  61. package/dist/documents.js +389 -0
  62. package/dist/documents.js.map +1 -0
  63. package/dist/env.d.ts +16 -0
  64. package/dist/env.d.ts.map +1 -0
  65. package/dist/env.js +57 -0
  66. package/dist/env.js.map +1 -0
  67. package/dist/index.d.ts +19 -24
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +17 -23
  70. package/dist/index.js.map +1 -1
  71. package/dist/json.d.ts +11 -89
  72. package/dist/json.d.ts.map +1 -1
  73. package/dist/json.js +16 -180
  74. package/dist/json.js.map +1 -1
  75. package/dist/library.d.ts +53 -309
  76. package/dist/library.d.ts.map +1 -1
  77. package/dist/library.js +77 -315
  78. package/dist/library.js.map +1 -1
  79. package/dist/ocifetch/constants.gen.d.ts +84 -0
  80. package/dist/ocifetch/constants.gen.d.ts.map +1 -0
  81. package/dist/ocifetch/constants.gen.js +92 -0
  82. package/dist/ocifetch/constants.gen.js.map +1 -0
  83. package/dist/ocifetch/dsse.d.ts +114 -0
  84. package/dist/ocifetch/dsse.d.ts.map +1 -0
  85. package/dist/ocifetch/dsse.js +331 -0
  86. package/dist/ocifetch/dsse.js.map +1 -0
  87. package/dist/ocifetch/ensure.d.ts +65 -0
  88. package/dist/ocifetch/ensure.d.ts.map +1 -0
  89. package/dist/ocifetch/ensure.js +775 -0
  90. package/dist/ocifetch/ensure.js.map +1 -0
  91. package/dist/ocifetch/errors.d.ts +92 -0
  92. package/dist/ocifetch/errors.d.ts.map +1 -0
  93. package/dist/ocifetch/errors.js +101 -0
  94. package/dist/ocifetch/errors.js.map +1 -0
  95. package/dist/ocifetch/goldens.d.ts +44 -0
  96. package/dist/ocifetch/goldens.d.ts.map +1 -0
  97. package/dist/ocifetch/goldens.js +62 -0
  98. package/dist/ocifetch/goldens.js.map +1 -0
  99. package/dist/ocifetch/http.d.ts +124 -0
  100. package/dist/ocifetch/http.d.ts.map +1 -0
  101. package/dist/ocifetch/http.js +549 -0
  102. package/dist/ocifetch/http.js.map +1 -0
  103. package/dist/ocifetch/index.d.ts +17 -0
  104. package/dist/ocifetch/index.d.ts.map +1 -0
  105. package/dist/ocifetch/index.js +14 -0
  106. package/dist/ocifetch/index.js.map +1 -0
  107. package/dist/ocifetch/layout.d.ts +126 -0
  108. package/dist/ocifetch/layout.d.ts.map +1 -0
  109. package/dist/ocifetch/layout.js +375 -0
  110. package/dist/ocifetch/layout.js.map +1 -0
  111. package/dist/ocifetch/localverify.d.ts +43 -0
  112. package/dist/ocifetch/localverify.d.ts.map +1 -0
  113. package/dist/ocifetch/localverify.js +164 -0
  114. package/dist/ocifetch/localverify.js.map +1 -0
  115. package/dist/ocifetch/lock.d.ts +39 -0
  116. package/dist/ocifetch/lock.d.ts.map +1 -0
  117. package/dist/ocifetch/lock.js +140 -0
  118. package/dist/ocifetch/lock.js.map +1 -0
  119. package/dist/ocifetch/oci.d.ts +100 -0
  120. package/dist/ocifetch/oci.d.ts.map +1 -0
  121. package/dist/ocifetch/oci.js +323 -0
  122. package/dist/ocifetch/oci.js.map +1 -0
  123. package/dist/ocifetch/referrers.d.ts +55 -0
  124. package/dist/ocifetch/referrers.d.ts.map +1 -0
  125. package/dist/ocifetch/referrers.js +132 -0
  126. package/dist/ocifetch/referrers.js.map +1 -0
  127. package/dist/ocifetch/types.d.ts +167 -0
  128. package/dist/ocifetch/types.d.ts.map +1 -0
  129. package/dist/ocifetch/types.js +88 -0
  130. package/dist/ocifetch/types.js.map +1 -0
  131. package/dist/ocifetch/unpack.d.ts +55 -0
  132. package/dist/ocifetch/unpack.d.ts.map +1 -0
  133. package/dist/ocifetch/unpack.js +205 -0
  134. package/dist/ocifetch/unpack.js.map +1 -0
  135. package/dist/registry.d.ts +38 -387
  136. package/dist/registry.d.ts.map +1 -1
  137. package/dist/registry.js +95 -819
  138. package/dist/registry.js.map +1 -1
  139. package/dist/schema.d.ts +77 -588
  140. package/dist/schema.d.ts.map +1 -1
  141. package/dist/schema.js +87 -549
  142. package/dist/schema.js.map +1 -1
  143. package/dist/settings.d.ts +27 -36
  144. package/dist/settings.d.ts.map +1 -1
  145. package/dist/settings.js +75 -25
  146. package/dist/settings.js.map +1 -1
  147. package/dist/setup.d.ts +46 -0
  148. package/dist/setup.d.ts.map +1 -0
  149. package/dist/setup.js +79 -0
  150. package/dist/setup.js.map +1 -0
  151. package/dist/tar.d.ts +27 -11
  152. package/dist/tar.d.ts.map +1 -1
  153. package/dist/tar.js +54 -32
  154. package/dist/tar.js.map +1 -1
  155. package/package.json +2 -2
  156. package/dist/discover.d.ts +0 -142
  157. package/dist/discover.d.ts.map +0 -1
  158. package/dist/discover.js +0 -288
  159. package/dist/discover.js.map +0 -1
  160. package/dist/error-codes.d.ts +0 -80
  161. package/dist/error-codes.d.ts.map +0 -1
  162. package/dist/error-codes.js +0 -139
  163. package/dist/error-codes.js.map +0 -1
  164. package/dist/errors.d.ts +0 -284
  165. package/dist/errors.d.ts.map +0 -1
  166. package/dist/errors.js +0 -321
  167. package/dist/errors.js.map +0 -1
  168. package/dist/fetch.d.ts +0 -382
  169. package/dist/fetch.d.ts.map +0 -1
  170. package/dist/fetch.js +0 -1498
  171. package/dist/fetch.js.map +0 -1
  172. package/dist/ffi.d.ts +0 -410
  173. package/dist/ffi.d.ts.map +0 -1
  174. package/dist/ffi.js +0 -1154
  175. package/dist/ffi.js.map +0 -1
  176. package/dist/format.d.ts +0 -128
  177. package/dist/format.d.ts.map +0 -1
  178. package/dist/format.js +0 -132
  179. package/dist/format.js.map +0 -1
  180. package/dist/numeric.d.ts +0 -20
  181. package/dist/numeric.d.ts.map +0 -1
  182. package/dist/numeric.js +0 -107
  183. package/dist/numeric.js.map +0 -1
  184. package/dist/paths.d.ts +0 -55
  185. package/dist/paths.d.ts.map +0 -1
  186. package/dist/paths.js +0 -87
  187. package/dist/paths.js.map +0 -1
  188. package/dist/results.d.ts +0 -495
  189. package/dist/results.d.ts.map +0 -1
  190. package/dist/results.js +0 -406
  191. package/dist/results.js.map +0 -1
  192. package/dist/transform.d.ts +0 -73
  193. package/dist/transform.d.ts.map +0 -1
  194. package/dist/transform.js +0 -533
  195. package/dist/transform.js.map +0 -1
package/dist/schema.js CHANGED
@@ -1,571 +1,109 @@
1
1
  /**
2
- * A compiled schema — one tenant table's column list, compiled inside one
3
- * version's library, plus the engine and TTL declarations that make `rows()`
4
- * answer with the storage layer's verdict as well as the type layer's.
5
- */
6
- import { ChtypesError } from './errors.js';
7
- import { DOC_ALL, EXPORT_NONE } from './format.js';
8
- import { parseDocument } from './json.js';
9
- import { batchResultOf, filterResultOf, rowResultOf, } from './results.js';
10
- import { encodeSettings } from './settings.js';
11
- const schemaFinalizer = new FinalizationRegistry((n) => {
12
- if (n.handle === null)
13
- return; // closed explicitly already
14
- for (const h of n.openFilters)
15
- n.native.filterFree(h);
16
- n.openFilters.clear();
17
- for (const h of n.openBlocks)
18
- n.native.blockFree(h);
19
- n.openBlocks.clear();
20
- n.native.schemaFree(n.handle);
21
- n.handle = null;
22
- });
23
- const filterFinalizer = new FinalizationRegistry((n) => {
24
- if (n.handle === null)
25
- return;
26
- if (n.schema.openFilters.delete(n.handle))
27
- n.native.filterFree(n.handle);
28
- n.handle = null;
29
- });
30
- const blockFinalizer = new FinalizationRegistry((n) => {
31
- if (n.handle === null)
32
- return;
33
- if (n.schema.openBlocks.delete(n.handle))
34
- n.native.blockFree(n.handle);
35
- n.handle = null;
36
- });
37
- /**
38
- * A compiled schema — one tenant table's column list, compiled inside one
39
- * version's library. Obtained from `Library#compileDdl`; never constructed
40
- * directly. Release with `close()` (idempotent) or `using` / `Symbol.dispose`
41
- * — `close()` stays the primary path; a GC finalizer that calls it for a
42
- * schema nobody closed is a backstop, not a replacement, since there is no
43
- * promise about WHEN (or, in principle, whether) it runs.
2
+ * `Schema`, `Filter` and `Block`: the compiled objects of `docs/reference/bindings-v1.md` §2.
44
3
  *
45
- * Thread-safety: the C ABI forbids using one `chs_schema *` from two threads
46
- * at once; on a single JS thread every call here is synchronous, so ordinary
47
- * Node code satisfies that by construction. Do not share a `Schema` across
48
- * `worker_threads`.
49
- */
50
- export class Schema {
51
- native;
52
- ddl;
53
- /**
54
- * The declared columns as ClickHouse canonicalized them, in declaration
55
- * order — flattened under `flatten_nested=1`, DEFAULT-rewritten types
56
- * (`x Int64 DEFAULT NULL` compiles as `Nullable(Int64)`), ALIAS types
57
- * inferred. A gateway detects EPHEMERAL columns here, at compile time
58
- * (`defaultKind === 'EPHEMERAL'`), not per row.
59
- */
60
- columns;
61
- /** The handle plus the GC-finalizer coordination state; see `SchemaNative`. */
62
- n;
63
- /**
64
- * Every open `Filter` compiled from this handle, so `close()` can free them
65
- * FIRST — the C layer does not refcount, and freeing the schema under a
66
- * live filter is use-after-free (the C ABI contract §Filters, handle lifetime).
67
- */
68
- filters = new Set();
69
- /**
70
- * Every open `Block` parsed from this handle — the same non-owning rule,
71
- * the same free-before-schema order (the C ABI contract §Blocks).
72
- */
73
- blocks = new Set();
74
- /** @internal — obtained from `Library#compileDdl`. */
75
- constructor(native, handle,
76
- /** The column-declaration list this schema was compiled from, verbatim. */
77
- ddl) {
78
- this.native = native;
79
- this.ddl = ddl;
80
- this.n = { native, handle, openFilters: new Set(), openBlocks: new Set() };
81
- this.columns = native.columns(handle);
82
- schemaFinalizer.register(this, this.n, this);
83
- }
84
- live() {
85
- if (this.n.handle === null)
86
- throw new ChtypesError('chtypes: schema is closed');
87
- return this.n.handle;
88
- }
89
- /**
90
- * Declare the table engine, so `rows()` applies the engine's own insert-time
91
- * merge (`optimize_on_insert = 1`): a CollapsingMergeTree refusing an invalid
92
- * Sign with code 117 before anything is stored, a SummingMergeTree summing
93
- * equal keys and dropping all-zero rows, a ReplacingMergeTree deduplicating.
94
- *
95
- * `options.mergeTreeSettings` declares the table's MergeTree-namespace
96
- * settings — see `EngineOptions` for the error contract (unknown name ⇒ the
97
- * server's 115; non-default declared value ⇒ `unsupported`, never silently
98
- * ignored). Absent/empty is structurally the plain engine declaration:
99
- * `chs_schema_engine` takes "{}" either way.
100
- *
101
- * The two error classes here are the refusal/decline split and MUST be
102
- * handled as peers — `UnsupportedError` is deliberately NOT
103
- * `instanceof SchemaError` (docs/reference/bindings.md rule 12):
104
- *
105
- * @param engine - the engine expression, e.g. `"SummingMergeTree"`,
106
- * `"CollapsingMergeTree(sign)"`.
107
- * @param orderBy - the sorting key, e.g. `"(day, key)"`.
108
- * @param options - the MergeTree-namespace `SETTINGS` clause, if any.
109
- * @throws {SchemaError} when the SERVER refused (positive code): this DDL
110
- * can never exist and the tenant has to be told. Today that is 115, an
111
- * unknown MergeTree setting name, with the server's own message verbatim.
112
- * @throws {UnsupportedError} when this LIBRARY declined: an engine or
113
- * sorting key this build does not model, a known MergeTree setting
114
- * declared at a non-default value, or a guarded exception. A real server
115
- * might well have accepted it — validate cautiously, fall back to the
116
- * server, and never present the decline as a rejection.
117
- */
118
- setEngine(engine, orderBy, options) {
119
- this.native.schemaEngine(this.live(), engine, orderBy, encodeSettings(options?.mergeTreeSettings));
120
- }
121
- /**
122
- * Declare the table's rows TTL (`ts + INTERVAL 30 DAY`). An expired row is
123
- * reported NOT STORED through the batch's `transformed` list.
124
- *
125
- * Column-level TTLs need no call: they are part of the declaration list.
126
- *
127
- * @param ttl - the rows-TTL expression, e.g. `"ts + INTERVAL 30 DAY"`. It is
128
- * validated under the handle's declared compile profile when one exists.
129
- * @throws {UnsupportedError} for a TTL form this build refuses rather than
130
- * guesses: WHERE / GROUP BY TTLs, TO DISK/VOLUME moves, RECOMPRESS, any
131
- * clock-reading TTL expression, and guarded exceptions. Fall back to the
132
- * server; do not report a tenant error.
133
- */
134
- setTtl(ttl) {
135
- this.native.schemaTtl(this.live(), ttl);
136
- }
137
- /**
138
- * Declare the table's partition key — the `PARTITION BY` clause after the
139
- * engine, e.g. `"toYYYYMM(ts)"` or `"(toDate(ts), tenant)"`
140
- * (`chs_schema_partition_by`, revision 6). The key is built by the server's
141
- * own CREATE-path call over this schema's columns, under the handle's
142
- * compile profile. A second call REPLACES the first; `""` removes the
143
- * declaration, and the schema then answers exactly as one that never
144
- * declared a key.
145
- *
146
- * With a key declared, `row` and `rows` answer `RowResult#partitionId` for
147
- * every row that would be stored and `BatchResult#partitionCount` for the
148
- * batch, and a body that would split into more partitions than the call's
149
- * `max_partitions_per_insert_block` allows is an ordinary `'rejected'` with
150
- * `errCode` 252 (TOO_MANY_PARTS), the server's own — a verdict, never a
151
- * throw.
152
- *
153
- * @param expr - the partition key expression, or `""` to remove it.
154
- * @throws {SchemaError} when the server's own CREATE path refuses the key
155
- * (e.g. 36 BAD_ARGUMENTS for a non-deterministic key, 549
156
- * DATA_TYPE_CANNOT_BE_USED_IN_KEY) — `setEngine`'s SIGN rule, not
157
- * `setTtl`'s.
158
- * @throws {UnsupportedError} when this build declines (-1, a guarded
159
- * exception), or the artifact predates `chs_schema_partition_by`. -2 is a
160
- * key the server accepts but this build will not evaluate; a
161
- * non-deterministic key is the server's own rejection, 36 BAD_ARGUMENTS.
162
- */
163
- setPartitionBy(expr) {
164
- this.native.schemaPartitionBy(this.live(), expr);
165
- }
166
- /**
167
- * Validate and coerce ONE row body, answering exactly as this ClickHouse
168
- * version's insert path would.
169
- *
170
- * There is no exception for a bad row: rejection, poisoning and declines all
171
- * arrive as the `RowResult`'s `outcome` (see `Outcome` for the taxonomy and
172
- * the caller's obligations per arm).
173
- *
174
- * @param format - the wire format code (`Format`). Binary-format support
175
- * depends on the loaded artifact — probe, don't assume.
176
- * @param raw - the row's bytes, exactly as they would arrive in an INSERT
177
- * body. Always bytes, never a JS string: binary formats contain NUL bytes
178
- * and text rows can carry invalid UTF-8 on purpose.
179
- * @param settings - per-call settings; they win over the compile profile and
180
- * the library defaults (except a type gate the profile declared, which the
181
- * handle has already settled, as a real server's CREATE does).
182
- * @param options - `RowOptions#columns` (revision 5), the INSERT column
183
- * list. `row()` takes `RowOptions`, not `RowsOptions`: the revision-3
184
- * export/doc-flag channels are meaningless on a single row, so this
185
- * method's options type simply does not offer them.
186
- * @returns the `RowResult` — verdict, stored values, and every silent change.
187
- * @throws {ChtypesError} when the schema is closed, or a settings value is a
188
- * JS `number`.
189
- * @throws {UnsupportedError} when the artifact predates `chs_row`.
190
- */
191
- row(format, raw, settings, options) {
192
- const doc = this.native.row(this.live(), format, raw, encodeSettings(settings), options?.columns);
193
- return rowResultOf(parseDocument(doc));
194
- }
195
- /**
196
- * Validate and coerce a whole request body, which may hold many rows.
197
- *
198
- * This is not `row()` in a loop and must never be implemented as one: row
199
- * separation is format-specific (a quoted CSV field can contain a newline),
200
- * `input_format_allow_errors_num` / `_ratio` decide whether a bad row is
201
- * skipped or aborts the batch, and one batch is one clock instant for the
202
- * volatile-DEFAULT guarantee.
203
- *
204
- * @param format - the wire format code (`Format`).
205
- * @param body - the whole request body as bytes (never a JS string).
206
- * @param settings - per-call settings; same precedence as `row`.
207
- * @returns the `BatchResult`. When `engineRows` is present it — not `rows` —
208
- * is the stored truth, and batch-level storage transforms (`ttl_expired`,
209
- * `ttl_column_expired`) are folded into `transformed`.
210
- * With `options.exportFormat` the same ONE call also serializes the batch's
211
- * accepted rows through the vendored writer: `payload` carries the bytes
212
- * (zero-length = emitted-empty, an accepted batch with zero accepted rows;
213
- * `undefined` + `exportDeclined` = withheld, with the reason), and `spans`
214
- * is index-aligned with `rows` — `payload.subarray(s.off, s.off + s.len)`
215
- * IS row i's line. With `options.docFlags` the per-row documents are
216
- * thinned to the selected groups; the verdict channel is never thinned.
217
- * See `RowsOptions` for the defaults and `BatchResult` for the three
218
- * payload states.
219
- *
220
- * @param format - the wire format code (`Format`).
221
- * @param body - the whole request body as bytes (never a JS string).
222
- * @param settings - per-call settings; same precedence as `row`.
223
- * @param options - the revision-3 export/doc-flag channels, plus
224
- * `columns` (revision 5), the INSERT column list, and `rowFilter`
225
- * (revision 5, second half) — see `RowsOptions`; absent = today's full
226
- * document, byte-identical to revision 2, no list, byte-identical to
227
- * every revision before 5, and no filter, byte-identical byte for byte.
228
- * @returns the `BatchResult`. When `engineRows` is present it — not `rows` —
229
- * is the stored truth, and batch-level storage transforms (`ttl_expired`,
230
- * `ttl_column_expired`) are folded into `transformed`. With
231
- * `options.rowFilter`, `rowsPassed`/`rowsCut` join the result and every
232
- * row carries its filter `verdict` beside its own `outcome`.
233
- * @throws {ChtypesError} when the schema is closed, a settings value is a JS
234
- * `number`, the artifact predates `chs_rows` (a mandatory symbol), the
235
- * filter is closed, or the filter comes from a different loaded library.
236
- */
237
- rows(format, body, settings, options) {
238
- const exportFormat = options?.exportFormat ?? EXPORT_NONE;
239
- const docFlags = options?.docFlags ?? (options?.exportFormat === undefined ? DOC_ALL : 0);
240
- const rowFilter = options?.rowFilter;
241
- let filterHandle;
242
- if (rowFilter !== undefined) {
243
- if (rowFilter.nativeLib !== this.native) {
244
- throw new ChtypesError('chtypes: filter and schema come from different libraries');
245
- }
246
- filterHandle = rowFilter.liveHandle();
247
- }
248
- const { doc, payload } = this.native.rows(this.live(), format, body, encodeSettings(settings), exportFormat, docFlags, options?.columns, filterHandle);
249
- return batchResultOf(parseDocument(doc), payload);
250
- }
251
- /**
252
- * Compile one boolean SQL expression over this schema's PHYSICAL columns
253
- * (ordinary + MATERIALIZED; naming an ALIAS/EPHEMERAL column fails with
254
- * ClickHouse's own UNKNOWN_IDENTIFIER, exactly where a real CREATE fails) —
255
- * the same TreeRewriter + ExpressionAnalyzer pipeline the CONSTRAINT CHECK
256
- * path runs, so comparison semantics are WHERE-side by construction:
257
- * `x = 256` over UInt8 promotes (false for every row), it never wraps
258
- * (the C ABI contract §Filters).
259
- *
260
- * The expression may contain `{name:Type}` query parameters, bound with
261
- * `options.params` (revision 4) — substitution is the server's own
262
- * `ReplaceQueryParameterVisitor`, run before analysis, exactly where a
263
- * real server runs it; see `CompileFilterOptions` for the injection-safety
264
- * and cache-discipline contract. Close the filter when done (`close()` /
265
- * `Symbol.dispose`); `schema.close()` also closes every open filter FIRST,
266
- * so no caller ordering can free the schema under a live filter.
267
- *
268
- * ENFORCEMENT GATE: nothing may enforce read-side security on this surface
269
- * until the WHERE-truth rig gates green (zero over-admit, zero over-hide);
270
- * until then it is a shadow/replay surface.
271
- *
272
- * @param expr - one boolean expression, e.g. `"x = 256"` or
273
- * `"tenant = {t:String}"`.
274
- * @param options - the `{name:Type}` bindings, if any.
275
- * @returns the compiled `Filter`.
276
- * @throws {SchemaError} when ClickHouse itself refuses the expression —
277
- * unknown identifier (47), unknown function, an analyzer-raised
278
- * NO_COMMON_TYPE — and, since revision 4, the server's own parameter
279
- * refusals: an UNBOUND `{name:Type}` is **456** UNKNOWN_QUERY_PARAMETER
280
- * ("Substitution `name` is not set"), a value the declared type cannot
281
- * parse completely is **457** BAD_QUERY_PARAMETER — the server's own
282
- * code and message, verbatim. A bound name the expression never uses is
283
- * ignored, as a live server ignores an unused `param_*`.
284
- * @throws {UnsupportedError} when this build declines: a non-deterministic
285
- * expression (clock reads — `now() > ts` —, `rand()`, server-constants;
286
- * the scan runs AFTER substitution, so a value can never smuggle one
287
- * in), or an artifact that predates the filter trio.
288
- */
289
- compileFilter(expr, options) {
290
- const handle = this.native.filterCompile(this.live(), expr, encodeSettings(options?.params));
291
- this.n.openFilters.add(handle);
292
- const filter = new Filter(this.native, this, this.n, handle, expr);
293
- this.filters.add(filter);
294
- return filter;
295
- }
296
- /** @internal — `Filter#close` deregisters itself here. */
297
- forgetFilter(filter) {
298
- this.filters.delete(filter);
299
- }
300
- /**
301
- * Parse a body ONCE into a `Block` (`chs_block_parse`) — the parse half of
302
- * `Filter#rows`, exported so K filters can evaluate one event with no
303
- * re-parse (`Filter#eval`; the C ABI contract §Blocks). Same formats and
304
- * settings contract as `rows` (`settings` is the PARSE-side map: format
305
- * settings, clock keys; evaluation takes none). Volatile DEFAULTs resolve
306
- * against THIS call's clock instant, so
307
- * `filter.eval(schema.parseBlock(...))` ≡ `filter.rows(...)` exactly when
308
- * the clock is pinned (`chtypes_now_epoch_nanos`) or the schema has no
309
- * volatile DEFAULT.
310
- *
311
- * Per-row parse failures do NOT throw — they are recorded IN the block and
312
- * answer `'d'` from every filter, with the recorded error. Release
313
- * with `close()` / `Symbol.dispose`; `schema.close()` closes open blocks
314
- * FIRST, the C-required order.
315
- *
316
- * @param format - the wire format code (`Format`).
317
- * @param body - the rows as bytes (never a JS string).
318
- * @param settings - parse-side settings; same precedence as `Schema#rows`.
319
- * @param options - `RowOptions#columns` (revision 5), the INSERT column
320
- * list read exactly as `Schema#row` reads it. `parseBlock()` takes
321
- * `RowOptions`, not `RowsOptions`: the revision-3 export/doc-flag
322
- * channels are meaningless here, so this method's options type simply
323
- * does not offer them. Filters compiled against this schema still
324
- * evaluate the schema's PHYSICAL columns, so a listed `EPHEMERAL`
325
- * column stays unreferenceable in a filter.
326
- * @returns the parsed `Block`.
327
- * @throws {SchemaError} on a call-level failure — an unknown setting's
328
- * 115, an unsplittable body, a binary decode fault, the deferred
329
- * JSONEachRow framing verdict: a malformed body yields no block and no
330
- * partial answers.
331
- * @throws {UnsupportedError} when this build declines the call, or the
332
- * artifact predates the block twin.
333
- */
334
- parseBlock(format, body, settings, options) {
335
- const handle = this.native.blockParse(this.live(), format, body, encodeSettings(settings), options?.columns);
336
- this.n.openBlocks.add(handle);
337
- const block = new Block(this.native, this, this.n, handle);
338
- this.blocks.add(block);
339
- return block;
340
- }
341
- /** @internal — `Block#close` deregisters itself here. */
342
- forgetBlock(block) {
343
- this.blocks.delete(block);
344
- }
345
- /**
346
- * Release the native schema. Idempotent. After it, `row` / `rows` /
347
- * `setEngine` / `setTtl` / `setPartitionBy` throw `ChtypesError` ("schema
348
- * is closed").
349
- * Any `Filter` or `Block` still open on this schema is closed FIRST, in
350
- * the same call — the handles-before-schema free order the C layer
351
- * requires, enforced here so no dispose ordering can get it backwards.
352
- */
353
- close() {
354
- for (const filter of this.filters)
355
- filter.close();
356
- this.filters.clear();
357
- for (const block of this.blocks)
358
- block.close();
359
- this.blocks.clear();
360
- if (this.n.handle === null)
361
- return;
362
- this.native.schemaFree(this.n.handle);
363
- this.n.handle = null;
364
- schemaFinalizer.unregister(this);
365
- }
366
- /** `using schema = lib.compileDdl(...)` releases it at scope exit. */
367
- [Symbol.dispose]() {
368
- this.close();
369
- }
370
- }
371
- /**
372
- * One boolean SQL expression compiled against a `Schema`'s columns
373
- * (`chs_filter_compile`). Obtained from `Schema#compileFilter`; never
374
- * constructed directly.
375
- *
376
- * LIFETIME: a filter REFERENCES its schema handle — the C layer does not copy
377
- * and does not refcount (the C ABI contract §Filters). This binding enforces the
378
- * free order structurally, both ways: the `Filter` holds its `Schema` (so the
379
- * schema stays reachable), and `Schema#close` closes every open filter before
380
- * freeing the schema. `close()` is idempotent, and `using` / `Symbol.dispose`
381
- * work on both objects in any nesting — the schema's dispose runs the
382
- * filter's first when the caller forgot. A filter compiled from a schema
383
- * answers for THAT handle: recompile filters when the schema is recompiled.
384
- * A filter nobody closed is also backed by a GC finalizer, coordinated with
385
- * its schema's own (see `SchemaNative`) so the two can never free this
386
- * filter's handle out of order or twice, whichever one the GC happens to run
387
- * first.
4
+ * Every public call makes exactly one ABI call over the generated, typed
5
+ * layer, then decodes the document it returned (`./documents.ts`) and
6
+ * computes nothing. The per-call zone is the `session_timezone` key of the
7
+ * call's settings and nothing more (`./settings.ts`).
388
8
  *
389
- * THREADS: the header's rule, verbatim — one `chs_filter` "must not be used
390
- * from two threads at once, and a chs_filter call is ALSO a use of its schema
391
- * handle" (two filters over ONE schema must not run concurrently either). On
392
- * a single JS thread every call here is synchronous, so ordinary Node code
393
- * satisfies both by construction (docs/reference/bindings.md §Concurrency); do not
394
- * share a `Filter` — or its `Schema` — across `worker_threads`.
395
- *
396
- * ENFORCEMENT GATE: `'e'` and `'d'` verdicts are NOT answers — a
397
- * caller enforcing visibility MUST fail closed on both — and NO caller may
398
- * enforce read-side security on this surface until the WHERE-truth rig gates
399
- * green; until then it is a shadow/replay surface (the C ABI contract §Filters).
9
+ * Handles. Each object holds the generated layer's handle wrapper, whose
10
+ * `ptr` raises a `UsageError` naming the object once it is closed, before any
11
+ * C call. A filter or a block holds a counted reference to its schema inside
12
+ * the library, so a binding object neither holds its parent alive nor orders
13
+ * its frees: closing a schema while its filters are in use is legal, and
14
+ * `close` is idempotent. A `FinalizationRegistry` per handle class frees what
15
+ * the caller abandons. TypeScript runs every call synchronously on one
16
+ * thread, so the close guard of the other bindings reduces to this check: no
17
+ * call is ever in flight while `close` runs.
400
18
  */
19
+ import { DocFlags, EXPORT_NONE } from './abi1/index.js';
20
+ import { decodeBatch, decodeFilterResult, decodeRow, decodeSchemaDescription, } from './documents.js';
21
+ import { bytesIn, encodeColumns, encodeParams, encodeSettings } from './settings.js';
22
+ function bodyIn(body) {
23
+ if (typeof body === 'string')
24
+ throw new TypeError('chtypes: a body is bytes (a Uint8Array); it never accepts the text type');
25
+ return body;
26
+ }
27
+ const filterHandles = new WeakMap();
28
+ /** A compiled `WHERE`-style expression bound to a schema. It holds its own zone, fixed at compile. */
401
29
  export class Filter {
402
- native;
403
- schema;
404
- expr;
405
- /** The handle plus the GC-finalizer coordination state; see `FilterNative`. */
406
- n;
407
- /** @internal — obtained from `Schema#compileFilter`. */
408
- constructor(native, schema, schemaNative, handle,
409
- /** The expression text as compiled, for logging and cache keys. */
410
- expr) {
411
- this.native = native;
412
- this.schema = schema;
413
- this.expr = expr;
414
- this.n = { native, schema: schemaNative, handle };
415
- filterFinalizer.register(this, this.n, this);
416
- }
417
- /** @internal — the loaded library this filter's handle belongs to, for
418
- * `Schema#rows(options.rowFilter)`'s cross-library check. */
419
- get nativeLib() {
420
- return this.native;
421
- }
422
- /** @internal — the live handle, for `Schema#rows(options.rowFilter)`. */
423
- liveHandle() {
424
- if (this.n.handle === null)
425
- throw new ChtypesError('chtypes: filter is closed');
426
- return this.n.handle;
427
- }
428
- /**
429
- * Evaluate the filter over a body of rows (`chs_filter_rows`) — the same
430
- * formats and settings contract as `Schema#rows`, ONE C call. Rows are
431
- * evaluated INDEPENDENTLY (there is no INSERT to abort):
432
- * `input_format_allow_errors_*` does not apply, a bad text row declines
433
- * (`'d'`) and the tail resyncs so verdict indexes keep matching input
434
- * rows, and volatile DEFAULTs resolve against one clock instant per call.
435
- *
436
- * @param format - the wire format code (`Format`).
437
- * @param body - the rows as bytes (never a JS string).
438
- * @param settings - per-call settings; same precedence as `Schema#rows`.
439
- * @returns the `FilterResult` — the call-level outcome, and one verdict per
440
- * row when it is `'ok'`.
441
- * @throws {ChtypesError} when the filter is closed, or a settings value is
442
- * a JS `number`.
443
- */
444
- rows(format, body, settings) {
445
- if (this.n.handle === null)
446
- throw new ChtypesError('chtypes: filter is closed');
447
- const doc = this.native.filterRows(this.n.handle, format, body, encodeSettings(settings));
448
- return filterResultOf(parseDocument(doc));
449
- }
450
- /**
451
- * Evaluate this filter over an already-parsed `Block` (`chs_filter_eval`)
452
- * — the SAME result document `rows` returns: same `FilterResult` fields,
453
- * same verdicts, same `errors` rule (a row the parse recorded as
454
- * unparseable answers `'d'` with the recorded error). Evaluation is
455
- * a pure function of (filter, block): no settings, and the block is
456
- * neither consumed nor mutated, so one block can be evaluated by K filters
457
- * sequentially with no re-parse — the live-SSE call shape.
458
- *
459
- * Filter and block MUST come from the SAME schema: a mismatched pair
460
- * answers a REJECTED result (code 1002) — the C layer's loud refusal,
461
- * never undefined behavior. A pair from two different libraries throws
462
- * `ChtypesError`: no handle ever crosses a dlopen'd image boundary.
463
- *
464
- * @param block - a `Block` from `Schema#parseBlock`.
465
- * @returns the `FilterResult`, exactly as `rows` would answer it.
466
- * @throws {ChtypesError} when the filter or block is closed, or they come
467
- * from two different libraries.
468
- */
30
+ #calls;
31
+ #h;
32
+ /** Not for callers: use `Schema.compileFilter`. */
33
+ constructor(calls, handle) {
34
+ this.#calls = calls;
35
+ this.#h = handle;
36
+ filterHandles.set(this, handle);
37
+ }
38
+ /** Evaluate over a body. */
39
+ rows(format, body, options = {}) {
40
+ return decodeFilterResult(this.#calls.filterEvalBody(this.#h, format, bodyIn(body), encodeSettings(options.settings, options.sessionTimezone)));
41
+ }
42
+ /** Evaluate over a parsed block. The filter brings its zone and the block brought its parse zone, so there are no settings here. */
469
43
  eval(block) {
470
- if (this.n.handle === null)
471
- throw new ChtypesError('chtypes: filter is closed');
472
- if (block.nativeLib !== this.native) {
473
- throw new ChtypesError('chtypes: filter and block come from different libraries');
474
- }
475
- const doc = this.native.filterEval(this.n.handle, block.liveHandle());
476
- return filterResultOf(parseDocument(doc));
44
+ return decodeFilterResult(this.#calls.filterEvalBlock(this.#h, blockHandles.get(block)));
477
45
  }
478
- /**
479
- * Release the native filter (`chs_filter_free`). Idempotent, and also
480
- * performed by the schema's own `close()` — filters first, then the schema,
481
- * the C-required order. Also the backstop a GC finalizer calls for a filter
482
- * nobody closed — see `SchemaNative`; the `openFilters.delete` guard is
483
- * what makes it safe to run whether `close()`, this filter's own finalizer,
484
- * or the schema's finalizer gets here first.
485
- */
46
+ /** Release this filter; idempotent. */
486
47
  close() {
487
- if (this.n.handle === null)
488
- return;
489
- if (this.n.schema.openFilters.delete(this.n.handle)) {
490
- this.native.filterFree(this.n.handle);
491
- }
492
- this.n.handle = null;
493
- this.schema.forgetFilter(this);
494
- filterFinalizer.unregister(this);
48
+ this.#h.close();
495
49
  }
496
- /** `using filter = schema.compileFilter(...)` releases it at scope exit. */
497
50
  [Symbol.dispose]() {
498
51
  this.close();
499
52
  }
500
53
  }
501
- /**
502
- * One body, parsed ONCE under one schema handle and one clock instant
503
- * (`chs_block_parse`). Obtained from `Schema#parseBlock`; evaluated by
504
- * `Filter#eval`. The parse-once/eval-many twin of `Filter#rows`
505
- * (the C ABI contract §Blocks): the live-SSE hot path is K filters × 1 event, and
506
- * the block sheds the re-parse.
507
- *
508
- * LIFETIME: a block REFERENCES its schema handle exactly as a filter does —
509
- * the C layer does not copy and does not refcount. This binding enforces the
510
- * free order structurally, both ways: the `Block` holds its `Schema` (so the
511
- * schema stays reachable), and `Schema#close` closes every open block before
512
- * freeing the schema — `using` / `Symbol.dispose` work on all three objects
513
- * in any nesting, and the schema's dispose runs the block's first when the
514
- * caller forgot. A block may be evaluated by MANY filters, sequentially;
515
- * evaluation does not consume or mutate it. Recompile blocks when the schema
516
- * is recompiled.
517
- *
518
- * THREADS: one block must not be used from two threads at once, and an eval
519
- * is a use of BOTH handles. On a single JS thread every call here is
520
- * synchronous, so ordinary Node code satisfies both by construction; do not
521
- * share a `Block` — or its `Schema` — across `worker_threads`.
522
- *
523
- * A block nobody closed is also backed by a GC finalizer, coordinated with
524
- * its schema's own (see `SchemaNative`) so the two can never free this
525
- * block's handle out of order or twice, whichever one the GC happens to run
526
- * first.
527
- */
54
+ const blockHandles = new WeakMap();
55
+ /** A body parsed once, evaluated by any number of filters. */
528
56
  export class Block {
529
- native;
530
- schema;
531
- /** The handle plus the GC-finalizer coordination state; see `BlockNative`. */
532
- n;
533
- /** @internal — obtained from `Schema#parseBlock`. */
534
- constructor(native, schema, schemaNative, handle) {
535
- this.native = native;
536
- this.schema = schema;
537
- this.n = { native, schema: schemaNative, handle };
538
- blockFinalizer.register(this, this.n, this);
57
+ #h;
58
+ /** Not for callers: use `Schema.parseBlock`. */
59
+ constructor(handle) {
60
+ this.#h = handle;
61
+ blockHandles.set(this, handle);
539
62
  }
540
- /** @internal — the loaded library this block's handle belongs to. */
541
- get nativeLib() {
542
- return this.native;
63
+ /** Release this block; idempotent. */
64
+ close() {
65
+ this.#h.close();
543
66
  }
544
- /** @internal — the live handle, for `Filter#eval`. */
545
- liveHandle() {
546
- if (this.n.handle === null)
547
- throw new ChtypesError('chtypes: block is closed');
548
- return this.n.handle;
67
+ [Symbol.dispose]() {
68
+ this.close();
549
69
  }
550
- /**
551
- * Release the native block (`chs_block_free`). Idempotent, and also
552
- * performed by the schema's own `close()` — blocks first, then the schema,
553
- * the C-required order. Also the backstop a GC finalizer calls for a block
554
- * nobody closed — see `SchemaNative`; the `openBlocks.delete` guard is what
555
- * makes it safe to run whether `close()`, this block's own finalizer, or
556
- * the schema's finalizer gets here first.
557
- */
70
+ }
71
+ /** A compiled `CREATE TABLE`: describe it, preview rows and bodies against it, compile filters and parse blocks over it. */
72
+ export class Schema {
73
+ #calls;
74
+ #h;
75
+ /** Not for callers: use `Library.compileTable`. */
76
+ constructor(calls, handle) {
77
+ this.#calls = calls;
78
+ this.#h = handle;
79
+ }
80
+ /** The columns, in declared order. */
81
+ describe() {
82
+ return decodeSchemaDescription(this.#calls.schemaDescribe(this.#h));
83
+ }
84
+ /** One row. */
85
+ row(format, body, options = {}) {
86
+ return decodeRow(this.#calls.previewRow(this.#h, format, bodyIn(body), encodeSettings(options.settings, options.sessionTimezone), encodeColumns(options.columns)));
87
+ }
88
+ /** A whole body. */
89
+ rows(format, body, options = {}) {
90
+ const filter = options.rowFilter === undefined ? null : filterHandles.get(options.rowFilter);
91
+ const exporting = options.exportFormat !== undefined;
92
+ const out = this.#calls.previewBatch(this.#h, format, bodyIn(body), encodeSettings(options.settings, options.sessionTimezone), encodeColumns(options.columns), filter, exporting ? options.exportFormat : EXPORT_NONE, options.docFlags ?? DocFlags.All);
93
+ return decodeBatch(out.out, exporting ? out.outExport : undefined);
94
+ }
95
+ /** Compile a filter. Its zone is fixed here. */
96
+ compileFilter(expr, options = {}) {
97
+ return new Filter(this.#calls, this.#calls.filterCreate(this.#h, bytesIn(expr), encodeParams(options.params), encodeSettings(options.settings, options.sessionTimezone)));
98
+ }
99
+ /** Parse a body once. */
100
+ parseBlock(format, body, options = {}) {
101
+ return new Block(this.#calls.blockCreate(this.#h, format, bodyIn(body), encodeSettings(options.settings, options.sessionTimezone), encodeColumns(options.columns)));
102
+ }
103
+ /** Release this schema; idempotent. Filters and blocks made from it keep working. */
558
104
  close() {
559
- if (this.n.handle === null)
560
- return;
561
- if (this.n.schema.openBlocks.delete(this.n.handle)) {
562
- this.native.blockFree(this.n.handle);
563
- }
564
- this.n.handle = null;
565
- this.schema.forgetBlock(this);
566
- blockFinalizer.unregister(this);
105
+ this.#h.close();
567
106
  }
568
- /** `using block = schema.parseBlock(...)` releases it at scope exit. */
569
107
  [Symbol.dispose]() {
570
108
  this.close();
571
109
  }