@coldsmirk/inkstone-sql 0.33.0 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +93 -14
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -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) (`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.
|
|
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; ClickHouse's driver reads a `?` as the conditional operator `a ? b : c` where a lone `:` follows it before any other `?`, and binds nothing through that `:` (`conditional.ts`). 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
|
|
package/dist/index.d.ts
CHANGED
|
@@ -152,9 +152,10 @@ export declare function joinGuesses(kind: SqlKind, target: SqlSource, others: Sq
|
|
|
152
152
|
* has no placeholder in it. Blanking is the reader's job because the reader already owns what is
|
|
153
153
|
* prose and what is SQL for this page, and it takes that decision under the data source's own
|
|
154
154
|
* lexical profile (`noise.ts`), mode for mode with the scanner — MySQL's `#` comments and
|
|
155
|
-
* backslash escapes, Postgres's `$tag$` bodies, Oracle's `q'…'`, every kind's quoted names,
|
|
156
|
-
* SQL Server's block comments as its driver ends them
|
|
157
|
-
* text the engine would mint
|
|
155
|
+
* backslash escapes, Postgres's `$tag$` bodies, Oracle's `q'…'`, every kind's quoted names,
|
|
156
|
+
* SQL Server's block comments as its driver ends them, and ClickHouse's conditional operator as
|
|
157
|
+
* its driver crosses it — so what reaches this file is exactly the text the engine would mint
|
|
158
|
+
* markers from.
|
|
158
159
|
*/
|
|
159
160
|
/**
|
|
160
161
|
* The binding form a statement's markers suggest.
|
|
@@ -166,8 +167,8 @@ type MarkerForm = "none" | "positional" | "named";
|
|
|
166
167
|
*
|
|
167
168
|
* - `"mixed-forms"` — the statement writes both `?` and `:name`; the scanner binds neither.
|
|
168
169
|
* - `"question-escape-unsupported"` — `??` on a kind whose native form is itself `?` (MySQL,
|
|
169
|
-
* 达梦, ClickHouse): the escape has no rendering that survives, and none
|
|
170
|
-
* operator
|
|
170
|
+
* 达梦, ClickHouse): the escape has no rendering that survives, and none is needed — MySQL and
|
|
171
|
+
* 达梦 have no `?` operator, and ClickHouse's one, the conditional `a ? b : c`, is read as one.
|
|
171
172
|
* - `"native-marker-collision"` — a marker hand-written in the kind's own bind form (`$1`,
|
|
172
173
|
* `:1`, `@p1`) beside a `?`/`:name` the scanner would mint: both would bind the same slot.
|
|
173
174
|
*/
|
package/dist/index.js
CHANGED
|
@@ -181,7 +181,7 @@ 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 = {
|
|
184
|
+
const CLICKHOUSE$1 = {
|
|
185
185
|
...dialect([
|
|
186
186
|
"final",
|
|
187
187
|
"prewhere",
|
|
@@ -280,7 +280,7 @@ const SQL_DIALECTS = {
|
|
|
280
280
|
fn("string_agg", "string_agg(expr, delimiter)", "Aggregate: joins each row's value with a delimiter.")
|
|
281
281
|
]),
|
|
282
282
|
dm: ORACLE$1,
|
|
283
|
-
clickhouse: CLICKHOUSE
|
|
283
|
+
clickhouse: CLICKHOUSE$1
|
|
284
284
|
};
|
|
285
285
|
function signatureParameters(signature) {
|
|
286
286
|
const open = signature.indexOf("(");
|
|
@@ -494,6 +494,67 @@ function joinGuesses(kind, target, others) {
|
|
|
494
494
|
return [...found.values()];
|
|
495
495
|
}
|
|
496
496
|
//#endregion
|
|
497
|
+
//#region src/conditional.ts
|
|
498
|
+
function conditionalEnd(text, at) {
|
|
499
|
+
let index = at + 2;
|
|
500
|
+
while (index >= 0 && index < text.length) {
|
|
501
|
+
const ch = text[index];
|
|
502
|
+
if (ch === "?" || ch === ":") return ch === ":" && index + 1 < text.length && text[index + 1] !== ":" && text[index - 1] !== ":" ? index + 1 : -1;
|
|
503
|
+
index = skipped(text, index);
|
|
504
|
+
}
|
|
505
|
+
return -1;
|
|
506
|
+
}
|
|
507
|
+
function skipped(text, at) {
|
|
508
|
+
const ch = text[at];
|
|
509
|
+
if (ch === "'" || ch === "\"" || ch === "`") return quoted(text, at);
|
|
510
|
+
if (ch === "(" || ch === "[" || ch === "{") return group(text, at);
|
|
511
|
+
if (ch === "-" && text[at + 1] === "-") return lineEnd(text, at + 2);
|
|
512
|
+
if (ch === "/" && text[at + 1] === "*") return blockEnd(text, at + 2);
|
|
513
|
+
return at + 1;
|
|
514
|
+
}
|
|
515
|
+
function quoted(text, open) {
|
|
516
|
+
const quote = text[open];
|
|
517
|
+
for (let index = open + 1; index < text.length; index += 1) {
|
|
518
|
+
const ch = text[index];
|
|
519
|
+
if (ch === "\\") index += 1;
|
|
520
|
+
else if (ch === quote) {
|
|
521
|
+
if (text[index + 1] !== quote) return index + 1;
|
|
522
|
+
index += 1;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return -1;
|
|
526
|
+
}
|
|
527
|
+
const CLOSERS = {
|
|
528
|
+
"(": ")",
|
|
529
|
+
"[": "]",
|
|
530
|
+
"{": "}"
|
|
531
|
+
};
|
|
532
|
+
function group(text, open) {
|
|
533
|
+
const close = CLOSERS[text[open]];
|
|
534
|
+
let index = open + 1;
|
|
535
|
+
while (index >= 0 && index < text.length) {
|
|
536
|
+
if (text[index] === close) return index + 1;
|
|
537
|
+
index = skipped(text, index);
|
|
538
|
+
}
|
|
539
|
+
return -1;
|
|
540
|
+
}
|
|
541
|
+
function lineEnd(text, from) {
|
|
542
|
+
const end = text.indexOf("\n", from);
|
|
543
|
+
return end > from ? end + 1 : text.length;
|
|
544
|
+
}
|
|
545
|
+
function blockEnd(text, from) {
|
|
546
|
+
let depth = 1;
|
|
547
|
+
for (let index = from; index + 1 < text.length; index += 1) if (text[index] === "/" && text[index + 1] === "*") {
|
|
548
|
+
depth += 1;
|
|
549
|
+
index += 1;
|
|
550
|
+
} else if (text[index] === "*" && text[index + 1] === "/") {
|
|
551
|
+
depth -= 1;
|
|
552
|
+
index += 1;
|
|
553
|
+
if (depth === 0) return index + 1;
|
|
554
|
+
}
|
|
555
|
+
return -1;
|
|
556
|
+
}
|
|
557
|
+
//#endregion
|
|
497
558
|
//#region src/noise.ts
|
|
498
559
|
const BASE = {
|
|
499
560
|
backslashEscapes: false,
|
|
@@ -505,13 +566,23 @@ const BASE = {
|
|
|
505
566
|
dollarQuoting: false,
|
|
506
567
|
escapeStrings: false,
|
|
507
568
|
nestedBlockComments: false,
|
|
508
|
-
alternativeQuoting: false
|
|
569
|
+
alternativeQuoting: false,
|
|
570
|
+
backslashInBackticks: false,
|
|
571
|
+
conditionalOperator: false
|
|
509
572
|
};
|
|
510
573
|
const SQLSERVER = {
|
|
511
574
|
...BASE,
|
|
512
575
|
bracketIdentifiers: true,
|
|
513
576
|
nestedBlockComments: true
|
|
514
577
|
};
|
|
578
|
+
const CLICKHOUSE = {
|
|
579
|
+
...BASE,
|
|
580
|
+
backslashEscapes: true,
|
|
581
|
+
hashLineComments: true,
|
|
582
|
+
backtickIdentifiers: true,
|
|
583
|
+
nestedBlockComments: true,
|
|
584
|
+
backslashInBackticks: true
|
|
585
|
+
};
|
|
515
586
|
const NOISE_PROFILES = {
|
|
516
587
|
postgres: {
|
|
517
588
|
...BASE,
|
|
@@ -547,17 +618,18 @@ const NOISE_PROFILES = {
|
|
|
547
618
|
nestedBlockComments: true,
|
|
548
619
|
dollarQuoting: true
|
|
549
620
|
},
|
|
621
|
+
clickhouse: CLICKHOUSE
|
|
622
|
+
};
|
|
623
|
+
const DRIVER_PROFILES = {
|
|
624
|
+
sqlserver: {
|
|
625
|
+
...SQLSERVER,
|
|
626
|
+
nestedBlockComments: false
|
|
627
|
+
},
|
|
550
628
|
clickhouse: {
|
|
551
|
-
...
|
|
552
|
-
|
|
553
|
-
hashLineComments: true,
|
|
554
|
-
backtickIdentifiers: true
|
|
629
|
+
...CLICKHOUSE,
|
|
630
|
+
conditionalOperator: true
|
|
555
631
|
}
|
|
556
632
|
};
|
|
557
|
-
const DRIVER_PROFILES = { sqlserver: {
|
|
558
|
-
...SQLSERVER,
|
|
559
|
-
nestedBlockComments: false
|
|
560
|
-
} };
|
|
561
633
|
const WORD_START = /[a-z_\u{0080}-\u{FFFF}]/iu;
|
|
562
634
|
const WORD_BYTE = /[\w$\u{0080}-\u{FFFF}]/u;
|
|
563
635
|
const NAME_BYTE = /\w/;
|
|
@@ -689,11 +761,18 @@ function blankNoise(text, kind, lexicon = "database") {
|
|
|
689
761
|
const ch = text[at];
|
|
690
762
|
if (ch === "'") skip(closeDelimited(text, at + 1, "'", profile.backslashEscapes), false);
|
|
691
763
|
else if (ch === "\"") skip(closeDelimited(text, at + 1, "\"", profile.backslashEscapes), !profile.doubleQuotedStrings);
|
|
692
|
-
else if (ch === "`" && profile.backtickIdentifiers) skip(closeDelimited(text, at + 1, "`",
|
|
764
|
+
else if (ch === "`" && profile.backtickIdentifiers) skip(closeDelimited(text, at + 1, "`", profile.backslashInBackticks), true);
|
|
693
765
|
else if (ch === "[" && profile.bracketIdentifiers) skip(closeBracket(text, at + 1), true);
|
|
694
766
|
else if (opensLineComment(text, at, profile)) skip(closeLine(text, at), false);
|
|
695
767
|
else if (ch === "/" && text[at + 1] === "*") skip(closeBlock(text, at + 2, profile.nestedBlockComments), false);
|
|
696
|
-
else if (ch === "
|
|
768
|
+
else if (ch === "?" && profile.conditionalOperator) {
|
|
769
|
+
const end = conditionalEnd(text, at);
|
|
770
|
+
if (end < 0) at += text[at + 1] === "?" ? 2 : 1;
|
|
771
|
+
else skip({
|
|
772
|
+
end,
|
|
773
|
+
sealed: true
|
|
774
|
+
}, false);
|
|
775
|
+
} else if (ch === "$" && profile.dollarQuoting) {
|
|
697
776
|
const quoted = closeDollarQuoted(text, at);
|
|
698
777
|
if (quoted === null) at += 1;
|
|
699
778
|
else skip(quoted, false);
|
|
@@ -1517,7 +1596,7 @@ function createSqlAssist(kind, catalog) {
|
|
|
1517
1596
|
}
|
|
1518
1597
|
//#endregion
|
|
1519
1598
|
//#region src/markers.ts
|
|
1520
|
-
const SCAN = /[A-Z_\u0080-\uFFFF][\w$\u0080-\uFFFF]*|(?<qq>\?\?)|::|(?<q>\?)
|
|
1599
|
+
const SCAN = /[A-Z_\u0080-\uFFFF][\w$\u0080-\uFFFF]*|(?<qq>\?\?)|::|(?<q>\?)|(?<!\.):(?<name>[A-Z_]\w*)|(?<colonDigits>:\d+)|(?<dollarDigits>\$\d+)|(?<atDigits>@P\d+)/gi;
|
|
1521
1600
|
const QUESTION_IS_NATIVE = /* @__PURE__ */ new Set([
|
|
1522
1601
|
"mysql",
|
|
1523
1602
|
"dm",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coldsmirk/inkstone-sql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.1",
|
|
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.
|
|
51
|
+
"@coldsmirk/inkstone-react": "^0.34.1"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"monaco-editor": "^0.57.0",
|
|
55
55
|
"react": ">=19",
|
|
56
|
-
"@coldsmirk/inkstone-react": "^0.
|
|
56
|
+
"@coldsmirk/inkstone-react": "^0.34.1"
|
|
57
57
|
},
|
|
58
58
|
"peerDependenciesMeta": {
|
|
59
59
|
"@coldsmirk/inkstone-react": {
|