@coldsmirk/inkstone-sql 0.32.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,7 +31,7 @@ const runnable = runnableStatement(text, from, to, "postgres");
31
31
  if (runnable.kind === "one") {
32
32
  // Markers are read off the BLANKED form — `select '?'` binds nothing — and under the kind's
33
33
  // own binding rules: `markers.refusal` mirrors the engine scanner's own faults (mixed forms,
34
- // `??` on mysql/dm, a hand-written `$1` beside a minted marker), so the editor can say so
34
+ // `??` on mysql/dm/clickhouse, a hand-written `$1` beside a minted marker), so the editor can say so
35
35
  // before the round trip does.
36
36
  const markers = statementMarkers(runnable.blanked, "postgres");
37
37
  }
@@ -41,7 +41,7 @@ Everything the intelligence answers goes through one lexical reader taken under
41
41
 
42
42
  ## The mirror invariant
43
43
 
44
- `noise.ts` (which runs of a statement are strings / comments / quoted names, per kind) and the marker grammar mirror [**polyglot's engine scanner**](https://github.com/coldsmirk/polyglot) (`internal/scan`) flag for flag. The scanner decides what actually binds; a reader that diverged would prompt for a parameter the engine refuses, or send a statement short of one it binds. **A scanner change requires a matching release of this package**, and the per-kind cases in `reader.test.ts` are the invariant's guard.
44
+ `noise.ts` (which runs of a statement are strings / comments / quoted names, per kind) and the marker grammar mirror [**polyglot's engine scanner**](https://github.com/coldsmirk/polyglot) (`sql/Placeholders`, over its profiles in `kinds/Dialect`) flag for flag, each kind under the two lexicons the sidecar reads it by: the database's own says where a statement ends and what is prose, and the one the kind's driver finds a `?` by says where the markers are — SQL Server nests a block comment, but Microsoft's driver ends one at its first close and binds a `?` past it. The scanner decides what actually binds; a reader that diverged would prompt for a parameter the engine refuses, or send a statement short of one it binds. **A scanner change requires a matching release of this package**, and the per-kind cases in `reader.test.ts` are the invariant's guard.
45
45
 
46
46
  ## Structured refusals
47
47
 
@@ -14,8 +14,11 @@
14
14
  * Doris (`doris`, StarRocks and SelectDB with it) speaks MySQL's protocol and grammar and reads
15
15
  * here as the MySQL idiom throughout, but its own parser admits no identifier past ASCII bare —
16
16
  * `患者` must be backticked where MySQL takes it as written — so it earned a kind for its spelling.
17
+ * ClickHouse (`clickhouse`) is a lexicon of its own on the engine — backslash escapes and backtick
18
+ * names as MySQL's, but `--` a comment without its space, `?` its own bind marker — and names its
19
+ * functions by their case (`toDate`, never `TODATE`).
17
20
  */
18
- type SqlKind = "postgres" | "mysql" | "oracle" | "sqlserver" | "dm" | "kingbase" | "doris";
21
+ type SqlKind = "postgres" | "mysql" | "oracle" | "sqlserver" | "dm" | "kingbase" | "doris" | "clickhouse";
19
22
  /**
20
23
  * One table or view as the catalog reports it. Names carry the vendor's own casing — that casing
21
24
  * is information, and everything downstream preserves it.
@@ -170,9 +173,10 @@ type Runnable = {
170
173
  */
171
174
  text: string;
172
175
  /**
173
- * The same span with everything the engine's own scanner skips whole — literals, comments and
174
- * quoted names — overwritten by spaces, character for character, so offsets still line up with
175
- * `text`. Anything reading the statement's syntax rather than its content must read this: in
176
+ * The statement as the engine's own scanner reads it when it is sent — under the lexicon the
177
+ * kind's driver finds a `?` by, from the statement's own start — with everything the scanner
178
+ * skips whole (literals, comments and quoted names) overwritten by spaces, character for
179
+ * character, so offsets still line up with `text`. A marker scan must read this: in
176
180
  * `select '?'` the `?` is prose, and a marker scan of the raw text would ask the operator for
177
181
  * a value the engine then refuses as an extra argument.
178
182
  */
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as CatalogTable, S as CatalogFunction, _ as readCall, a as createSqlAssist, b as runnableStatement, c as SqlSource, d as CallRead, f as IdentifierRead, g as TableRef, h as StatementRead, i as SqlSignatureHelp, l as createSqlSchema, m as StatementContext, n as SqlCompletionCandidate, o as SqlCatalog, p as Runnable, r as SqlHoverCard, s as SqlSchema, t as SqlAssist, u as qualifiedName, v as readIdentifier, w as SqlKind, x as CatalogColumn, y as readStatement } from "./assist-BM4bNDhw.js";
1
+ import { C as CatalogTable, S as CatalogFunction, _ as readCall, a as createSqlAssist, b as runnableStatement, c as SqlSource, d as CallRead, f as IdentifierRead, g as TableRef, h as StatementRead, i as SqlSignatureHelp, l as createSqlSchema, m as StatementContext, n as SqlCompletionCandidate, o as SqlCatalog, p as Runnable, r as SqlHoverCard, s as SqlSchema, t as SqlAssist, u as qualifiedName, v as readIdentifier, w as SqlKind, x as CatalogColumn, y as readStatement } from "./assist-Cr2idunB.js";
2
2
  //#region src/functions.d.ts
3
3
  /**
4
4
  * The function vocabulary this bench speaks: the curated floor this project wrote
@@ -78,11 +78,17 @@ interface SqlFunction {
78
78
  * manual: the words an integration engineer writes at this console, weighted toward reading — a
79
79
  * keyword or function missing here costs one suggestion, so the lists stay honest rather than
80
80
  * exhaustive. Everything is lowercase, the casing the bench itself writes (the tree's
81
- * `select * from`, the editor placeholder); completion re-cases it to follow the operator's hand.
81
+ * `select * from`, the editor placeholder); completion re-cases it to follow the operator's hand —
82
+ * but for the functions of a kind that names them by their case, spelled as the vendor spells them.
82
83
  */
83
84
  interface SqlDialect {
84
85
  keywords: string[];
85
86
  functions: SqlFunction[];
87
+ /**
88
+ * The kind names a function by its case (ClickHouse's `toDate`, never `TODATE`): its curated
89
+ * functions are written as listed, whatever the operator's hand.
90
+ */
91
+ functionsByCase?: boolean;
86
92
  }
87
93
  export declare const SQL_DIALECTS: Record<SqlKind, SqlDialect>;
88
94
  /**
@@ -133,11 +139,11 @@ export declare function joinGuesses(kind: SqlKind, target: SqlSource, others: Sq
133
139
  * Placeholder detection for what a run asks the operator for.
134
140
  *
135
141
  * This is **not** validation and must never become it: the engine-side scanner (the polyglot
136
- * sidecar's `internal/scan`) is the authority on what binds, it is lexical and per-dialect, and a second
137
- * implementation here would drift from it the first time a dialect gained a quoting form. All
138
- * this decides is whether the run has values to ask for, which form the parameter dialog
139
- * offers, and — where the statement is one the scanner refuses outright — the refusal, as a
140
- * code, so the editor can say so before the round trip does.
142
+ * sidecar's `sql/Placeholders`) is the authority on what binds, it is lexical and per-dialect,
143
+ * and a second implementation here would drift from it the first time a dialect gained a quoting
144
+ * form. All this decides is whether the run has values to ask for, which form the parameter
145
+ * dialog offers, and — where the statement is one the scanner refuses outright — the refusal, as
146
+ * a code, so the editor can say so before the round trip does.
141
147
  *
142
148
  * **Callers pass the statement's blanked form** (`Runnable.blanked`), never its raw text. A
143
149
  * disagreement with the engine's scanner is not cosmetic in either direction — it answers both
@@ -146,8 +152,9 @@ export declare function joinGuesses(kind: SqlKind, target: SqlSource, others: Sq
146
152
  * has no placeholder in it. Blanking is the reader's job because the reader already owns what is
147
153
  * prose and what is SQL for this page, and it takes that decision under the data source's own
148
154
  * lexical profile (`noise.ts`), mode for mode with the scanner — MySQL's `#` comments and
149
- * backslash escapes, Postgres's `$tag$` bodies, Oracle's `q'…'`, every kind's quoted names — so
150
- * what reaches this file is exactly the text the engine would mint markers from.
155
+ * backslash escapes, Postgres's `$tag$` bodies, Oracle's `q'…'`, every kind's quoted names, and
156
+ * SQL Server's block comments as its driver ends them — so what reaches this file is exactly the
157
+ * text the engine would mint markers from.
151
158
  */
152
159
  /**
153
160
  * The binding form a statement's markers suggest.
@@ -159,8 +166,8 @@ type MarkerForm = "none" | "positional" | "named";
159
166
  *
160
167
  * - `"mixed-forms"` — the statement writes both `?` and `:name`; the scanner binds neither.
161
168
  * - `"question-escape-unsupported"` — `??` on a kind whose native form is itself `?` (MySQL,
162
- * 达梦): the escape has no rendering that survives, and neither dialect has a `?` operator to
163
- * escape in the first place.
169
+ * 达梦, ClickHouse): the escape has no rendering that survives, and none of them has a `?`
170
+ * operator to escape in the first place.
164
171
  * - `"native-marker-collision"` — a marker hand-written in the kind's own bind form (`$1`,
165
172
  * `:1`, `@p1`) beside a `?`/`:name` the scanner would mint: both would bind the same slot.
166
173
  */
@@ -191,6 +198,11 @@ interface StatementMarkers {
191
198
  export declare function statementMarkers(statement: string, kind: SqlKind): StatementMarkers;
192
199
  //#endregion
193
200
  //#region src/noise.d.ts
201
+ /**
202
+ * Which lexicon a text is read under: the database's own — where a statement ends, what is prose
203
+ * — or the one the kind's driver finds a `?` by, which the sidecar mints its markers under.
204
+ */
205
+ type NoiseLexicon = "database" | "driver";
194
206
  /**
195
207
  * A run the reader must not look inside. `sealed` says the closing delimiter was found: a caret
196
208
  * right after a sealed run is back in the statement, while a caret at the end of an unterminated
@@ -223,9 +235,10 @@ interface BlankedSql {
223
235
  /**
224
236
  * The statement with its noise blanked out, char for char, so every offset survives — in the two
225
237
  * renderings the reader needs (see [BlankedSql]). One pass, mirroring the sidecar scanner's own
226
- * loop over its mutually exclusive lexical modes.
238
+ * loop over its mutually exclusive lexical modes, under the database's own lexicon unless the
239
+ * driver's is asked for.
227
240
  */
228
- export declare function blankNoise(text: string, kind: SqlKind): BlankedSql;
241
+ export declare function blankNoise(text: string, kind: SqlKind, lexicon?: NoiseLexicon): BlankedSql;
229
242
  /**
230
243
  * Whether the caret sits inside a string or a comment, where the bench has nothing to say. A
231
244
  * quoted name is not one: it is a name the operator is writing, and the reader is about names.
@@ -373,4 +386,4 @@ export declare function spellQualifiedName(kind: SqlKind, table: {
373
386
  name: string;
374
387
  }): string;
375
388
  //#endregion
376
- export { type BenchFunction, type BindRefusal, type BlankedSql, type Bound, type BoundParams, type CallRead, type CatalogColumn, type CatalogFunction, type CatalogTable, type IdentifierRead, type JoinGuess, type JoinReason, type MarkerForm, type MarkerRefusal, type NoiseSpan, type ParamType, type ParamValue, type Runnable, type SqlAssist, type SqlCatalog, type SqlCompletionCandidate, type SqlDialect, type SqlFunction, type SqlFunctions, type SqlHoverCard, type SqlKind, type SqlScalar, type SqlSchema, type SqlSignatureHelp, type SqlSource, type StatementContext, type StatementMarkers, type StatementOrigin, type StatementRead, type TableRef, createSqlAssist, createSqlSchema, qualifiedName, readCall, readIdentifier, readStatement, runnableStatement };
389
+ export { type BenchFunction, type BindRefusal, type BlankedSql, type Bound, type BoundParams, type CallRead, type CatalogColumn, type CatalogFunction, type CatalogTable, type IdentifierRead, type JoinGuess, type JoinReason, type MarkerForm, type MarkerRefusal, type NoiseLexicon, type NoiseSpan, type ParamType, type ParamValue, type Runnable, type SqlAssist, type SqlCatalog, type SqlCompletionCandidate, type SqlDialect, type SqlFunction, type SqlFunctions, type SqlHoverCard, type SqlKind, type SqlScalar, type SqlSchema, type SqlSignatureHelp, type SqlSource, type StatementContext, type StatementMarkers, type StatementOrigin, type StatementRead, type TableRef, createSqlAssist, createSqlSchema, qualifiedName, readCall, readIdentifier, readStatement, runnableStatement };
package/dist/index.js CHANGED
@@ -181,6 +181,63 @@ const MYSQL = dialect([
181
181
  fn("group_concat", "group_concat(expr)", "Aggregate: joins each row's value into one string; takes `separator '…'`."),
182
182
  fn("json_extract", "json_extract(json, path)", "Reads a JSON path, as in `'$.id'`.")
183
183
  ]);
184
+ const CLICKHOUSE = {
185
+ ...dialect([
186
+ "final",
187
+ "prewhere",
188
+ "sample",
189
+ "array",
190
+ "global",
191
+ "any",
192
+ "asof",
193
+ "semi",
194
+ "anti",
195
+ "limit",
196
+ "offset",
197
+ "settings",
198
+ "format",
199
+ "totals",
200
+ "rollup",
201
+ "cube",
202
+ "ilike",
203
+ "interval",
204
+ "true",
205
+ "false"
206
+ ], [
207
+ fn("now", "now(timezone)", "Current date and time, in the server's time zone unless one is named."),
208
+ fn("today", "today()", "Current date."),
209
+ fn("toDate", "toDate(value)", "Converts to a Date."),
210
+ fn("toDateTime", "toDateTime(value, timezone)", "Converts to a DateTime, in a time zone where one is named."),
211
+ fn("toString", "toString(value)", "Converts to a String."),
212
+ fn("toInt64", "toInt64(value)", "Converts to a 64-bit integer."),
213
+ fn("toFloat64", "toFloat64(value)", "Converts to a 64-bit float."),
214
+ fn("toStartOfDay", "toStartOfDay(time)", "Rounds a time down to the start of its day."),
215
+ fn("toStartOfHour", "toStartOfHour(time)", "Rounds a time down to the start of its hour."),
216
+ fn("toStartOfMonth", "toStartOfMonth(date)", "Rounds a date down to the first day of its month."),
217
+ fn("formatDateTime", "formatDateTime(time, format)", "Formats a time through a format string, as in `'%Y-%m-%d'`."),
218
+ fn("dateDiff", "dateDiff(unit, start, end)", "Unit boundaries crossed between two times, as in `dateDiff('day', a, b)`."),
219
+ fn("if", "if(condition, then, else)", "Ternary choice."),
220
+ fn("multiIf", "multiIf(condition, then, …, else)", "The value of the first condition that holds, or the last argument."),
221
+ fn("ifNull", "ifNull(value, fallback)", "The second argument when the first is NULL."),
222
+ fn("concat", "concat(value, …)", "Joins the arguments as strings."),
223
+ fn("length", "length(value)", "Bytes in a string, elements in an array; `lengthUTF8` counts characters."),
224
+ fn("substring", "substring(text, offset, length)", "`length` bytes from byte `offset`, counting from 1; `substringUTF8` counts characters."),
225
+ fn("position", "position(text, search)", "Byte position of a substring, counting from 1, or 0 when it is absent."),
226
+ fn("replaceAll", "replaceAll(text, search, replacement)", "Replaces every occurrence of a substring."),
227
+ fn("splitByChar", "splitByChar(separator, text)", "Splits a string on a one-character separator into an array."),
228
+ fn("JSONExtractString", "JSONExtractString(json, key, …)", "A string read out of JSON by key or index, empty where there is none."),
229
+ fn("has", "has(array, value)", "Whether the array holds the value."),
230
+ fn("arrayJoin", "arrayJoin(array)", "One row per element of the array."),
231
+ fn("countIf", "countIf(condition)", "Aggregate: rows for which the condition holds."),
232
+ fn("sumIf", "sumIf(value, condition)", "Aggregate: the sum over the rows for which the condition holds."),
233
+ fn("uniq", "uniq(expr)", "Aggregate: the distinct values, counted approximately."),
234
+ fn("uniqExact", "uniqExact(expr)", "Aggregate: the distinct values, counted exactly."),
235
+ fn("groupArray", "groupArray(expr)", "Aggregate: collects each row's value into an array."),
236
+ fn("argMax", "argMax(value, by)", "Aggregate: `value` from the row where `by` is largest."),
237
+ fn("any", "any(expr)", "Aggregate: one value of the group, whichever is met first.")
238
+ ]),
239
+ functionsByCase: true
240
+ };
184
241
  const SQL_DIALECTS = {
185
242
  postgres: POSTGRES$1,
186
243
  kingbase: POSTGRES$1,
@@ -222,7 +279,8 @@ const SQL_DIALECTS = {
222
279
  fn("rtrim", "rtrim(text)", "Strips trailing whitespace."),
223
280
  fn("string_agg", "string_agg(expr, delimiter)", "Aggregate: joins each row's value with a delimiter.")
224
281
  ]),
225
- dm: ORACLE$1
282
+ dm: ORACLE$1,
283
+ clickhouse: CLICKHOUSE
226
284
  };
227
285
  function signatureParameters(signature) {
228
286
  const open = signature.indexOf("(");
@@ -323,6 +381,13 @@ const TSQL_RESERVED = reservedWords(`
323
381
  tran transaction trigger truncate try_convert tsequal union unique unpivot update updatetext
324
382
  use user values varying view waitfor when where while with within writetext
325
383
  `);
384
+ const CLICKHOUSE_RESERVED = reservedWords(`
385
+ all and anti any array as asc asof between by case cast collate cross cube desc distinct else
386
+ end except exists false fill final first format from full global group having ilike in inner
387
+ insert interpolate intersect interval into is join last left like limit not null nulls offset
388
+ on or order outer paste prewhere qualify right rollup sample select semi settings step table
389
+ then totals true union using values when where window with
390
+ `);
326
391
  const ORACLE = {
327
392
  open: "\"",
328
393
  close: "\"",
@@ -357,10 +422,18 @@ const SPELLINGS = {
357
422
  regular: /^[a-z_@#][\w@#$]*$/i,
358
423
  reserved: TSQL_RESERVED
359
424
  },
360
- dm: ORACLE
425
+ dm: ORACLE,
426
+ clickhouse: {
427
+ open: "`",
428
+ close: "`",
429
+ backslash: true,
430
+ regular: /^[a-z_]\w*$/i,
431
+ reserved: CLICKHOUSE_RESERVED
432
+ }
361
433
  };
362
434
  function delimited(spelling, name) {
363
- return `${spelling.open}${name.replaceAll(spelling.close, () => spelling.close + spelling.close)}${spelling.close}`;
435
+ const escaped = spelling.backslash === true ? name.replaceAll("\\", () => "\\\\") : name;
436
+ return `${spelling.open}${escaped.replaceAll(spelling.close, () => spelling.close + spelling.close)}${spelling.close}`;
364
437
  }
365
438
  function spellIdentifier(kind, name) {
366
439
  const spelling = SPELLINGS[kind];
@@ -434,6 +507,11 @@ const BASE = {
434
507
  nestedBlockComments: false,
435
508
  alternativeQuoting: false
436
509
  };
510
+ const SQLSERVER = {
511
+ ...BASE,
512
+ bracketIdentifiers: true,
513
+ nestedBlockComments: true
514
+ };
437
515
  const NOISE_PROFILES = {
438
516
  postgres: {
439
517
  ...BASE,
@@ -461,18 +539,25 @@ const NOISE_PROFILES = {
461
539
  ...BASE,
462
540
  alternativeQuoting: true
463
541
  },
464
- sqlserver: {
465
- ...BASE,
466
- bracketIdentifiers: true
467
- },
542
+ sqlserver: SQLSERVER,
468
543
  dm: BASE,
469
544
  kingbase: {
470
545
  ...BASE,
471
546
  escapeStrings: true,
472
547
  nestedBlockComments: true,
473
548
  dollarQuoting: true
549
+ },
550
+ clickhouse: {
551
+ ...BASE,
552
+ backslashEscapes: true,
553
+ hashLineComments: true,
554
+ backtickIdentifiers: true
474
555
  }
475
556
  };
557
+ const DRIVER_PROFILES = { sqlserver: {
558
+ ...SQLSERVER,
559
+ nestedBlockComments: false
560
+ } };
476
561
  const WORD_START = /[a-z_\u{0080}-\u{FFFF}]/iu;
477
562
  const WORD_BYTE = /[\w$\u{0080}-\u{FFFF}]/u;
478
563
  const NAME_BYTE = /\w/;
@@ -575,10 +660,14 @@ function opensLineComment(text, from, profile) {
575
660
  const after = text[from + 2];
576
661
  return !profile.lineCommentNeedsSpace || after === void 0 || after <= " ";
577
662
  }
578
- let lastBlanked = null;
579
- function blankNoise(text, kind) {
580
- if (lastBlanked !== null && lastBlanked.text === text && lastBlanked.kind === kind) return lastBlanked.result;
581
- const profile = NOISE_PROFILES[kind];
663
+ const lastBlanked = {
664
+ database: null,
665
+ driver: null
666
+ };
667
+ function blankNoise(text, kind, lexicon = "database") {
668
+ const last = lastBlanked[lexicon];
669
+ if (last !== null && last.text === text && last.kind === kind) return last.result;
670
+ const profile = lexicon === "driver" ? DRIVER_PROFILES[kind] ?? NOISE_PROFILES[kind] : NOISE_PROFILES[kind];
582
671
  const withNames = text.split("");
583
672
  const blanked = text.split("");
584
673
  const spans = [];
@@ -622,7 +711,7 @@ function blankNoise(text, kind) {
622
711
  blanked: blanked.join(""),
623
712
  spans
624
713
  };
625
- lastBlanked = {
714
+ lastBlanked[lexicon] = {
626
715
  text,
627
716
  kind,
628
717
  result
@@ -918,10 +1007,11 @@ function runnableStatement(text, from, to, kind) {
918
1007
  if (spans.length === 0) return { kind: "none" };
919
1008
  if (selected && spans.length > 1) return { kind: "several" };
920
1009
  const span = spans[0];
1010
+ const statement = text.slice(span.from, span.to);
921
1011
  return {
922
1012
  kind: "one",
923
- text: text.slice(span.from, span.to),
924
- blanked: blanked.slice(span.from, span.to),
1013
+ text: statement,
1014
+ blanked: blankNoise(statement, kind, "driver").blanked,
925
1015
  from: span.from,
926
1016
  to: span.to,
927
1017
  alone: statementSpans(text, blanked, 0, text.length).length <= 1
@@ -1023,7 +1113,7 @@ function expansionCandidate(kind, sources, replacesStar) {
1023
1113
  };
1024
1114
  }
1025
1115
  function createSqlCompletion(kind, schema, functions) {
1026
- const { keywords } = SQL_DIALECTS[kind];
1116
+ const { keywords, functionsByCase = false } = SQL_DIALECTS[kind];
1027
1117
  const spelled = /* @__PURE__ */ new Map();
1028
1118
  async function wordCandidates(typed) {
1029
1119
  const upper = typed !== "" && typed === typed.toUpperCase() && typed !== typed.toLowerCase();
@@ -1039,7 +1129,7 @@ function createSqlCompletion(kind, schema, functions) {
1039
1129
  sortGroup: RANK_KEYWORD
1040
1130
  };
1041
1131
  }), ...known.map((entry) => {
1042
- const name = entry.origin === "curated" ? spell(entry.name) : entry.name;
1132
+ const name = entry.origin === "curated" && !functionsByCase ? spell(entry.name) : entry.name;
1043
1133
  const written = entry.origin === "curated" ? name : spellCallable(kind, name);
1044
1134
  const call = entry.qualifier === "" ? written : `${spellIdentifier(kind, entry.qualifier)}.${written}`;
1045
1135
  return {
@@ -1161,7 +1251,8 @@ const DIALECTS = {
1161
1251
  oracle: "plsql",
1162
1252
  dm: "plsql",
1163
1253
  sqlserver: "transactsql",
1164
- kingbase: "postgresql"
1254
+ kingbase: "postgresql",
1255
+ clickhouse: "clickhouse"
1165
1256
  };
1166
1257
  const PLACEHOLDERS = String.raw`\?|:[A-Za-z_]\w*`;
1167
1258
  function sentinelFor(text) {
@@ -1427,7 +1518,11 @@ function createSqlAssist(kind, catalog) {
1427
1518
  //#endregion
1428
1519
  //#region src/markers.ts
1429
1520
  const SCAN = /[A-Z_\u0080-\uFFFF][\w$\u0080-\uFFFF]*|(?<qq>\?\?)|::|(?<q>\?)|:(?<name>[A-Z_]\w*)|(?<colonDigits>:\d+)|(?<dollarDigits>\$\d+)|(?<atDigits>@P\d+)/gi;
1430
- const QUESTION_IS_NATIVE = /* @__PURE__ */ new Set(["mysql", "dm"]);
1521
+ const QUESTION_IS_NATIVE = /* @__PURE__ */ new Set([
1522
+ "mysql",
1523
+ "dm",
1524
+ "clickhouse"
1525
+ ]);
1431
1526
  const NATIVE_GROUP = {
1432
1527
  postgres: "dollarDigits",
1433
1528
  mysql: null,
@@ -1435,7 +1530,8 @@ const NATIVE_GROUP = {
1435
1530
  oracle: "colonDigits",
1436
1531
  sqlserver: "atDigits",
1437
1532
  dm: null,
1438
- kingbase: "dollarDigits"
1533
+ kingbase: "dollarDigits",
1534
+ clickhouse: null
1439
1535
  };
1440
1536
  function statementMarkers(statement, kind) {
1441
1537
  const names = [];
package/dist/monaco.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as SqlAssist } from "./assist-BM4bNDhw.js";
1
+ import { t as SqlAssist } from "./assist-Cr2idunB.js";
2
2
  import * as monacoEditor from "monaco-editor";
3
3
  import { IDisposable } from "monaco-editor";
4
4
  //#region src/monaco.d.ts
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as SqlAssist } from "./assist-BM4bNDhw.js";
1
+ import { t as SqlAssist } from "./assist-Cr2idunB.js";
2
2
  import { ConsoleCaret, ConsoleEditorHandle, ConsoleEditorProps } from "@coldsmirk/inkstone-react/monaco";
3
3
  import { ReactNode } from "react";
4
4
  //#region src/react.d.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coldsmirk/inkstone-sql",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "description": "Schema-aware SQL editing intelligence for Monaco: lexical statement reading under per-dialect noise profiles, catalog-driven completion/hover/signature help, unified-placeholder awareness, formatting, and a drop-in React SQL editor.",
5
5
  "keywords": [
6
6
  "sql",
@@ -48,12 +48,12 @@
48
48
  "devDependencies": {
49
49
  "monaco-editor": "^0.57.0",
50
50
  "react": "^19.3.0",
51
- "@coldsmirk/inkstone-react": "^0.32.0"
51
+ "@coldsmirk/inkstone-react": "^0.33.0"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "monaco-editor": "^0.57.0",
55
55
  "react": ">=19",
56
- "@coldsmirk/inkstone-react": "^0.32.0"
56
+ "@coldsmirk/inkstone-react": "^0.33.0"
57
57
  },
58
58
  "peerDependenciesMeta": {
59
59
  "@coldsmirk/inkstone-react": {