@chkit/core 0.1.2-beta.7 → 0.2.0-beta.11
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 +26 -6
- package/dist/canonical.d.ts.map +1 -1
- package/dist/canonical.js +26 -7
- package/dist/canonical.js.map +1 -1
- package/dist/column-default.d.ts +62 -0
- package/dist/column-default.d.ts.map +1 -0
- package/dist/column-default.js +212 -0
- package/dist/column-default.js.map +1 -0
- package/dist/conflict-markers.d.ts +3 -0
- package/dist/conflict-markers.d.ts.map +1 -0
- package/dist/conflict-markers.js +9 -0
- package/dist/conflict-markers.js.map +1 -0
- package/dist/identifier.d.ts +22 -0
- package/dist/identifier.d.ts.map +1 -0
- package/dist/identifier.js +69 -0
- package/dist/identifier.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -3
- package/dist/index.js.map +1 -1
- package/dist/insert-columns.d.ts +13 -0
- package/dist/insert-columns.d.ts.map +1 -0
- package/dist/insert-columns.js +36 -0
- package/dist/insert-columns.js.map +1 -0
- package/dist/kafka.d.ts +9 -0
- package/dist/kafka.d.ts.map +1 -0
- package/dist/kafka.js +64 -0
- package/dist/kafka.js.map +1 -0
- package/dist/key-clause.d.ts +6 -1
- package/dist/key-clause.d.ts.map +1 -1
- package/dist/key-clause.js +12 -13
- package/dist/key-clause.js.map +1 -1
- package/dist/model-types.d.ts +84 -6
- package/dist/model-types.d.ts.map +1 -1
- package/dist/model.d.ts +3 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +9 -2
- package/dist/model.js.map +1 -1
- package/dist/object-dependencies.d.ts +18 -0
- package/dist/object-dependencies.d.ts.map +1 -0
- package/dist/object-dependencies.js +351 -0
- package/dist/object-dependencies.js.map +1 -0
- package/dist/on-cluster.d.ts.map +1 -1
- package/dist/on-cluster.js +7 -3
- package/dist/on-cluster.js.map +1 -1
- package/dist/planner.d.ts.map +1 -1
- package/dist/planner.js +93 -82
- package/dist/planner.js.map +1 -1
- package/dist/projection.d.ts.map +1 -1
- package/dist/projection.js +5 -40
- package/dist/projection.js.map +1 -1
- package/dist/schema-loader.d.ts +2 -0
- package/dist/schema-loader.d.ts.map +1 -1
- package/dist/schema-loader.js +72 -5
- package/dist/schema-loader.js.map +1 -1
- package/dist/sql-lexer.d.ts +29 -0
- package/dist/sql-lexer.d.ts.map +1 -0
- package/dist/sql-lexer.js +220 -0
- package/dist/sql-lexer.js.map +1 -0
- package/dist/sql-normalizer.d.ts +48 -0
- package/dist/sql-normalizer.d.ts.map +1 -1
- package/dist/sql-normalizer.js +118 -1
- package/dist/sql-normalizer.js.map +1 -1
- package/dist/sql-scan.d.ts +20 -0
- package/dist/sql-scan.d.ts.map +1 -0
- package/dist/sql-scan.js +82 -0
- package/dist/sql-scan.js.map +1 -0
- package/dist/sql-splitter.d.ts.map +1 -1
- package/dist/sql-splitter.js +52 -5
- package/dist/sql-splitter.js.map +1 -1
- package/dist/sql.d.ts +7 -0
- package/dist/sql.d.ts.map +1 -1
- package/dist/sql.js +51 -39
- package/dist/sql.js.map +1 -1
- package/dist/text-index-sql.d.ts +6 -0
- package/dist/text-index-sql.d.ts.map +1 -0
- package/dist/text-index-sql.js +200 -0
- package/dist/text-index-sql.js.map +1 -0
- package/dist/text-index.d.ts +10 -0
- package/dist/text-index.d.ts.map +1 -0
- package/dist/text-index.js +121 -0
- package/dist/text-index.js.map +1 -0
- package/dist/ts-import.d.ts.map +1 -1
- package/dist/ts-import.js +23 -3
- package/dist/ts-import.js.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +303 -3
- package/dist/validate.js.map +1 -1
- package/package.json +1 -1
- package/dist/utils.d.ts +0 -2
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -2
- package/dist/utils.js.map +0 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
const WHITESPACE = /[ \t\n\r\f\v]/;
|
|
2
|
+
const IDENTIFIER_START = /[A-Za-z_]/;
|
|
3
|
+
const IDENTIFIER_PART = /[A-Za-z0-9_$]/;
|
|
4
|
+
const WORD_CHAR = /[A-Za-z0-9_]/;
|
|
5
|
+
const WORD = /^[A-Za-z0-9_]+$/;
|
|
6
|
+
const DIGIT = /[0-9]/;
|
|
7
|
+
const HEX_DIGIT = /[0-9A-Fa-f]/;
|
|
8
|
+
const HEREDOC_TAG = /[A-Za-z0-9_]/;
|
|
9
|
+
const EXPONENT = /^[eE][+-]?[0-9]/;
|
|
10
|
+
const HEX_EXPONENT = /^[pP][+-]?[0-9]/;
|
|
11
|
+
export function tokenizeSQL(sql) {
|
|
12
|
+
const tokens = [];
|
|
13
|
+
let previous;
|
|
14
|
+
let i = 0;
|
|
15
|
+
while (i < sql.length) {
|
|
16
|
+
const { kind, end, terminated } = readToken(sql, i, previous);
|
|
17
|
+
const token = { kind, text: sql.slice(i, end), start: i, end, terminated };
|
|
18
|
+
tokens.push(token);
|
|
19
|
+
if (!isTrivia(token))
|
|
20
|
+
previous = token;
|
|
21
|
+
i = end;
|
|
22
|
+
}
|
|
23
|
+
return tokens;
|
|
24
|
+
}
|
|
25
|
+
/** Whitespace and comments: tokens that never change what a statement means. */
|
|
26
|
+
export function isTrivia(token) {
|
|
27
|
+
return token.kind === 'whitespace' || token.kind === 'line_comment' || token.kind === 'block_comment';
|
|
28
|
+
}
|
|
29
|
+
/** The name an identifier token denotes: bare text, or the unquoted value. */
|
|
30
|
+
export function identifierName(token) {
|
|
31
|
+
if (token.kind === 'identifier')
|
|
32
|
+
return token.text;
|
|
33
|
+
if (token.kind !== 'quoted_identifier' || !token.terminated)
|
|
34
|
+
return undefined;
|
|
35
|
+
return unescapeQuoted(token.text);
|
|
36
|
+
}
|
|
37
|
+
/** The value of a terminated string literal (`'…'` or a `$tag$…$tag$` heredoc). */
|
|
38
|
+
export function stringLiteralValue(token) {
|
|
39
|
+
if (token.kind !== 'string' || !token.terminated)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (token.text.startsWith('$')) {
|
|
42
|
+
const tagLength = token.text.indexOf('$', 1) + 1;
|
|
43
|
+
return token.text.slice(tagLength, token.text.length - tagLength);
|
|
44
|
+
}
|
|
45
|
+
return unescapeQuoted(token.text);
|
|
46
|
+
}
|
|
47
|
+
// `previous` is the last token that is not trivia; ClickHouse uses it to tell
|
|
48
|
+
// a qualifier dot from the start of a number.
|
|
49
|
+
function readToken(sql, i, previous) {
|
|
50
|
+
const char = sql[i] ?? '';
|
|
51
|
+
const next = sql[i + 1] ?? '';
|
|
52
|
+
if (WHITESPACE.test(char))
|
|
53
|
+
return complete('whitespace', skipWhile(sql, i, WHITESPACE));
|
|
54
|
+
// `--x`, `//x`, `# x` and `#!x` are line comments; `#x` is not (ClickHouse rejects it).
|
|
55
|
+
if ((char === '-' && next === '-') ||
|
|
56
|
+
(char === '/' && next === '/') ||
|
|
57
|
+
(char === '#' && (next === ' ' || next === '!'))) {
|
|
58
|
+
return complete('line_comment', lineEnd(sql, i));
|
|
59
|
+
}
|
|
60
|
+
if (char === '/' && next === '*')
|
|
61
|
+
return readBlockComment(sql, i);
|
|
62
|
+
if (char === "'")
|
|
63
|
+
return { kind: 'string', ...readQuoted(sql, i, "'") };
|
|
64
|
+
if (char === '`' || char === '"')
|
|
65
|
+
return { kind: 'quoted_identifier', ...readQuoted(sql, i, char) };
|
|
66
|
+
if (char === '$') {
|
|
67
|
+
const heredocEnd = readHeredoc(sql, i);
|
|
68
|
+
if (heredocEnd !== undefined)
|
|
69
|
+
return complete('string', heredocEnd);
|
|
70
|
+
if (WORD_CHAR.test(next))
|
|
71
|
+
return complete('identifier', skipWhile(sql, i, IDENTIFIER_PART));
|
|
72
|
+
}
|
|
73
|
+
if (DIGIT.test(char))
|
|
74
|
+
return readNumberOrWord(sql, i, previous?.text === '.');
|
|
75
|
+
if (char === '.' && DIGIT.test(next) && !endsOperand(previous))
|
|
76
|
+
return complete('number', fractionEnd(sql, i));
|
|
77
|
+
if (IDENTIFIER_START.test(char))
|
|
78
|
+
return complete('identifier', skipWhile(sql, i, IDENTIFIER_PART));
|
|
79
|
+
const codePoint = sql.codePointAt(i) ?? 0;
|
|
80
|
+
return complete('punctuation', i + String.fromCodePoint(codePoint).length);
|
|
81
|
+
}
|
|
82
|
+
// ClickHouse reads a digit-led run as a numeric literal and, when word
|
|
83
|
+
// characters continue it, as a bare word: `1m_rollup` and `2024db` are names,
|
|
84
|
+
// while `1_000`, `1.5e+3` and `0x1F` are numbers. Right after a `.` only
|
|
85
|
+
// integer digits form the literal, so `t.1.2` is tuple access and `db.1e5` is a
|
|
86
|
+
// name. Word characters exclude `$` here: `1a$b` is `1a` followed by `$b`.
|
|
87
|
+
function readNumberOrWord(sql, i, afterDot) {
|
|
88
|
+
const literalEnd = afterDot ? digitsEnd(sql, i, DIGIT) : numericLiteralEnd(sql, i);
|
|
89
|
+
const end = skipWhile(sql, literalEnd, WORD_CHAR);
|
|
90
|
+
const isName = end > literalEnd && WORD.test(sql.slice(i, end));
|
|
91
|
+
return complete(isName ? 'identifier' : 'number', end);
|
|
92
|
+
}
|
|
93
|
+
// An optional 0x/0b prefix, digits, an optional fraction and an optional
|
|
94
|
+
// exponent. A `.` joins the literal only before a digit, so `1.x` keeps its dot.
|
|
95
|
+
function numericLiteralEnd(sql, i) {
|
|
96
|
+
const prefix = sql.slice(i, i + 3);
|
|
97
|
+
const hex = /^0[xX][0-9A-Fa-f]/.test(prefix);
|
|
98
|
+
const digit = hex ? HEX_DIGIT : DIGIT;
|
|
99
|
+
let j = digitsEnd(sql, hex || /^0[bB][01]/.test(prefix) ? i + 2 : i, digit);
|
|
100
|
+
if (sql[j] === '.' && DIGIT.test(sql[j + 1] ?? ''))
|
|
101
|
+
j = digitsEnd(sql, j + 1, digit);
|
|
102
|
+
const exponent = (hex ? HEX_EXPONENT : EXPONENT).exec(sql.slice(j, j + 3));
|
|
103
|
+
return exponent ? digitsEnd(sql, j + exponent[0].length - 1, DIGIT) : j;
|
|
104
|
+
}
|
|
105
|
+
// One block of digits; `_` may separate two digits (`1_000`).
|
|
106
|
+
function digitsEnd(sql, i, digit) {
|
|
107
|
+
let j = i;
|
|
108
|
+
while (j < sql.length) {
|
|
109
|
+
const char = sql[j] ?? '';
|
|
110
|
+
const separator = char === '_' && j > i && digit.test(sql[j + 1] ?? '');
|
|
111
|
+
if (!digit.test(char) && !separator)
|
|
112
|
+
break;
|
|
113
|
+
j += 1;
|
|
114
|
+
}
|
|
115
|
+
return j;
|
|
116
|
+
}
|
|
117
|
+
// `.5` or `.5e3` where a value starts.
|
|
118
|
+
function fractionEnd(sql, i) {
|
|
119
|
+
const j = skipWhile(sql, i + 1, DIGIT);
|
|
120
|
+
const exponent = EXPONENT.exec(sql.slice(j, j + 3));
|
|
121
|
+
return exponent ? skipWhile(sql, j + exponent[0].length - 1, DIGIT) : j;
|
|
122
|
+
}
|
|
123
|
+
// After a name, a number, `)` or `]`, a `.` is a qualifier or tuple access.
|
|
124
|
+
function endsOperand(token) {
|
|
125
|
+
if (token === undefined)
|
|
126
|
+
return false;
|
|
127
|
+
return (token.kind === 'identifier' ||
|
|
128
|
+
token.kind === 'quoted_identifier' ||
|
|
129
|
+
token.kind === 'number' ||
|
|
130
|
+
token.text === ')' ||
|
|
131
|
+
token.text === ']');
|
|
132
|
+
}
|
|
133
|
+
// ClickHouse block comments nest: `/* a /* b */ c */` is one comment.
|
|
134
|
+
function readBlockComment(sql, i) {
|
|
135
|
+
let depth = 0;
|
|
136
|
+
let j = i;
|
|
137
|
+
while (j < sql.length) {
|
|
138
|
+
if (sql.startsWith('/*', j)) {
|
|
139
|
+
depth += 1;
|
|
140
|
+
j += 2;
|
|
141
|
+
}
|
|
142
|
+
else if (sql.startsWith('*/', j)) {
|
|
143
|
+
depth -= 1;
|
|
144
|
+
j += 2;
|
|
145
|
+
if (depth === 0)
|
|
146
|
+
return complete('block_comment', j);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
j += 1;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { kind: 'block_comment', end: sql.length, terminated: false };
|
|
153
|
+
}
|
|
154
|
+
// A doubled delimiter or a backslash escapes the delimiter inside quotes.
|
|
155
|
+
function readQuoted(sql, i, quote) {
|
|
156
|
+
let j = i + 1;
|
|
157
|
+
while (j < sql.length) {
|
|
158
|
+
const char = sql[j];
|
|
159
|
+
if (char === '\\') {
|
|
160
|
+
j += 2;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (char === quote) {
|
|
164
|
+
if (sql[j + 1] === quote) {
|
|
165
|
+
j += 2;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
return { end: j + 1, terminated: true };
|
|
169
|
+
}
|
|
170
|
+
j += 1;
|
|
171
|
+
}
|
|
172
|
+
return { end: sql.length, terminated: false };
|
|
173
|
+
}
|
|
174
|
+
// `$tag$ … $tag$` (including `$$ … $$`), where the tag is `[A-Za-z0-9_]*`. It
|
|
175
|
+
// is only a literal when the same tag closes it later; otherwise ClickHouse
|
|
176
|
+
// reads the `$` as part of an identifier or as punctuation.
|
|
177
|
+
function readHeredoc(sql, i) {
|
|
178
|
+
const tagEnd = skipWhile(sql, i + 1, HEREDOC_TAG);
|
|
179
|
+
if (sql[tagEnd] !== '$')
|
|
180
|
+
return undefined;
|
|
181
|
+
const tag = sql.slice(i, tagEnd + 1);
|
|
182
|
+
const close = sql.indexOf(tag, tagEnd + 1);
|
|
183
|
+
return close === -1 ? undefined : close + tag.length;
|
|
184
|
+
}
|
|
185
|
+
// Resolves a doubled delimiter and backslash escapes to the escaped character,
|
|
186
|
+
// which is exact for the quotes and backslashes names and literals contain.
|
|
187
|
+
function unescapeQuoted(text) {
|
|
188
|
+
const quote = text[0] ?? '';
|
|
189
|
+
const body = text.slice(1, -1);
|
|
190
|
+
let out = '';
|
|
191
|
+
for (let i = 0; i < body.length; i += 1) {
|
|
192
|
+
const char = body[i] ?? '';
|
|
193
|
+
if (char === '\\' && i + 1 < body.length) {
|
|
194
|
+
out += body[i + 1];
|
|
195
|
+
i += 1;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (char === quote && body[i + 1] === quote) {
|
|
199
|
+
out += quote;
|
|
200
|
+
i += 1;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
out += char;
|
|
204
|
+
}
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
function complete(kind, end) {
|
|
208
|
+
return { kind, end, terminated: true };
|
|
209
|
+
}
|
|
210
|
+
function skipWhile(sql, i, pattern) {
|
|
211
|
+
let j = i;
|
|
212
|
+
while (j < sql.length && pattern.test(sql[j] ?? ''))
|
|
213
|
+
j += 1;
|
|
214
|
+
return j;
|
|
215
|
+
}
|
|
216
|
+
function lineEnd(sql, i) {
|
|
217
|
+
const newline = sql.indexOf('\n', i);
|
|
218
|
+
return newline === -1 ? sql.length : newline;
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=sql-lexer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-lexer.js","sourceRoot":"","sources":["../src/sql-lexer.ts"],"names":[],"mappings":"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"}
|
package/dist/sql-normalizer.d.ts
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare expression tokens while preserving quoted values and identifier case.
|
|
3
|
+
* SQL the lexer cannot read (e.g. an unterminated quote) falls back to its
|
|
4
|
+
* normalizeSQLFragment form, encoded as a JSON string so it never equals a token
|
|
5
|
+
* list.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sqlExpressionFingerprint(value: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Whether `expression` is the default ClickHouse stores for a bare EPHEMERAL
|
|
10
|
+
* column. ClickHouse synthesizes defaultValueOfTypeName('<type as written>');
|
|
11
|
+
* the literal keeps alias spellings (BIGINT, TEXT) that system.columns.type
|
|
12
|
+
* canonicalizes, so any single string literal counts.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isSyntheticEphemeralDefault(expression: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Canonical one-line form of a SQL fragment: a view or materialized view `as`,
|
|
17
|
+
* `partitionBy`, `ttl`, a skip index expression, a projection, a dictionary
|
|
18
|
+
* `source`/`layout`/`lifetime`, or the same clauses read back from ClickHouse.
|
|
19
|
+
*
|
|
20
|
+
* Comments are dropped and every run of whitespace becomes one space, inside
|
|
21
|
+
* string literals too. Comments must go before the newlines do: a `--`, `//`,
|
|
22
|
+
* `# ` or `#!` comment ends at its newline, so on a single line it would swallow
|
|
23
|
+
* the rest of the fragment and the `;` the renderer appends after it (#232).
|
|
24
|
+
* Comment syntax is ClickHouse's, from the shared lexer: markers inside string
|
|
25
|
+
* literals, quoted identifiers and heredocs are text, and an unterminated block
|
|
26
|
+
* comment or string is kept as written for ClickHouse to report.
|
|
27
|
+
*
|
|
28
|
+
* A dropped comment leaves one space, so the tokens around it never fuse: a
|
|
29
|
+
* minus, an empty block comment and a minus become `- -`, not a `--` comment.
|
|
30
|
+
* Text without comments or a stray `#` normalizes exactly as a plain
|
|
31
|
+
* whitespace collapse would, and the result is stable: normalizing it again
|
|
32
|
+
* changes nothing.
|
|
33
|
+
*/
|
|
1
34
|
export declare function normalizeSQLFragment(value: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* SQL text without its comments, trimmed. Unlike normalizeSQLFragment, all
|
|
37
|
+
* other text stays as written, including whitespace inside string literals.
|
|
38
|
+
* chkit renders a column default expression on one line of a CREATE or ALTER
|
|
39
|
+
* statement, where a `--` comment would swallow the `,`, `COMMENT`, `CODEC` or
|
|
40
|
+
* `;` after it (#234).
|
|
41
|
+
*
|
|
42
|
+
* A run of whitespace and comments that holds a comment becomes one space, so
|
|
43
|
+
* the tokens around it never fuse, or a newline after a stray `#`, for the
|
|
44
|
+
* reason normalizeSQLFragment gives. A stray `#` at the end is followed by a
|
|
45
|
+
* newline too: the ` COMMENT` or ` CODEC` rendered after it would otherwise
|
|
46
|
+
* turn it into a `# ` comment that hides them. An unterminated block comment
|
|
47
|
+
* or string is kept as written for ClickHouse to report.
|
|
48
|
+
*/
|
|
49
|
+
export declare function stripSQLComments(value: string): string;
|
|
2
50
|
export declare function normalizeEngine(engine: string): string;
|
|
3
51
|
//# sourceMappingURL=sql-normalizer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-normalizer.d.ts","sourceRoot":"","sources":["../src/sql-normalizer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sql-normalizer.d.ts","sourceRoot":"","sources":["../src/sql-normalizer.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM9D;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEvE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAmB1D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAsBtD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOtD"}
|
package/dist/sql-normalizer.js
CHANGED
|
@@ -1,11 +1,128 @@
|
|
|
1
|
+
import { isKafkaEngine, normalizeKafkaEngine } from './kafka.js';
|
|
2
|
+
import { tokenizeSQL } from './sql-lexer.js';
|
|
3
|
+
import { textExpressionFingerprint, textSQLFingerprint } from './text-index-sql.js';
|
|
4
|
+
const SYNTHETIC_EPHEMERAL_DEFAULT = /^defaultValueOfTypeName\s*\(\s*'(?:[^'\\]|\\.|'')*'\s*\)$/s;
|
|
5
|
+
/**
|
|
6
|
+
* Compare expression tokens while preserving quoted values and identifier case.
|
|
7
|
+
* SQL the lexer cannot read (e.g. an unterminated quote) falls back to its
|
|
8
|
+
* normalizeSQLFragment form, encoded as a JSON string so it never equals a token
|
|
9
|
+
* list.
|
|
10
|
+
*/
|
|
11
|
+
export function sqlExpressionFingerprint(value) {
|
|
12
|
+
try {
|
|
13
|
+
return textSQLFingerprint(textExpressionFingerprint(value));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return JSON.stringify(normalizeSQLFragment(value));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Whether `expression` is the default ClickHouse stores for a bare EPHEMERAL
|
|
21
|
+
* column. ClickHouse synthesizes defaultValueOfTypeName('<type as written>');
|
|
22
|
+
* the literal keeps alias spellings (BIGINT, TEXT) that system.columns.type
|
|
23
|
+
* canonicalizes, so any single string literal counts.
|
|
24
|
+
*/
|
|
25
|
+
export function isSyntheticEphemeralDefault(expression) {
|
|
26
|
+
return SYNTHETIC_EPHEMERAL_DEFAULT.test(expression.trim());
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Canonical one-line form of a SQL fragment: a view or materialized view `as`,
|
|
30
|
+
* `partitionBy`, `ttl`, a skip index expression, a projection, a dictionary
|
|
31
|
+
* `source`/`layout`/`lifetime`, or the same clauses read back from ClickHouse.
|
|
32
|
+
*
|
|
33
|
+
* Comments are dropped and every run of whitespace becomes one space, inside
|
|
34
|
+
* string literals too. Comments must go before the newlines do: a `--`, `//`,
|
|
35
|
+
* `# ` or `#!` comment ends at its newline, so on a single line it would swallow
|
|
36
|
+
* the rest of the fragment and the `;` the renderer appends after it (#232).
|
|
37
|
+
* Comment syntax is ClickHouse's, from the shared lexer: markers inside string
|
|
38
|
+
* literals, quoted identifiers and heredocs are text, and an unterminated block
|
|
39
|
+
* comment or string is kept as written for ClickHouse to report.
|
|
40
|
+
*
|
|
41
|
+
* A dropped comment leaves one space, so the tokens around it never fuse: a
|
|
42
|
+
* minus, an empty block comment and a minus become `- -`, not a `--` comment.
|
|
43
|
+
* Text without comments or a stray `#` normalizes exactly as a plain
|
|
44
|
+
* whitespace collapse would, and the result is stable: normalizing it again
|
|
45
|
+
* changes nothing.
|
|
46
|
+
*/
|
|
1
47
|
export function normalizeSQLFragment(value) {
|
|
2
|
-
|
|
48
|
+
let out = '';
|
|
49
|
+
let separator = '';
|
|
50
|
+
let afterBareHash = false;
|
|
51
|
+
for (const token of tokenizeSQL(value)) {
|
|
52
|
+
if (separatesTokens(token)) {
|
|
53
|
+
// `#` not followed by a space or `!` is a syntax error in ClickHouse.
|
|
54
|
+
// A space after it would turn it into a `# ` comment that hides the rest
|
|
55
|
+
// of the fragment, so keep the error visible with a newline instead.
|
|
56
|
+
separator = afterBareHash ? '\n' : ' ';
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (out !== '')
|
|
60
|
+
out += separator;
|
|
61
|
+
out += token.text.replace(/\s+/g, ' ');
|
|
62
|
+
separator = '';
|
|
63
|
+
afterBareHash = token.kind === 'punctuation' && token.text === '#';
|
|
64
|
+
}
|
|
65
|
+
// An unterminated string or block comment at the end can still end in a space.
|
|
66
|
+
return out.trim();
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* SQL text without its comments, trimmed. Unlike normalizeSQLFragment, all
|
|
70
|
+
* other text stays as written, including whitespace inside string literals.
|
|
71
|
+
* chkit renders a column default expression on one line of a CREATE or ALTER
|
|
72
|
+
* statement, where a `--` comment would swallow the `,`, `COMMENT`, `CODEC` or
|
|
73
|
+
* `;` after it (#234).
|
|
74
|
+
*
|
|
75
|
+
* A run of whitespace and comments that holds a comment becomes one space, so
|
|
76
|
+
* the tokens around it never fuse, or a newline after a stray `#`, for the
|
|
77
|
+
* reason normalizeSQLFragment gives. A stray `#` at the end is followed by a
|
|
78
|
+
* newline too: the ` COMMENT` or ` CODEC` rendered after it would otherwise
|
|
79
|
+
* turn it into a `# ` comment that hides them. An unterminated block comment
|
|
80
|
+
* or string is kept as written for ClickHouse to report.
|
|
81
|
+
*/
|
|
82
|
+
export function stripSQLComments(value) {
|
|
83
|
+
let out = '';
|
|
84
|
+
let gap = '';
|
|
85
|
+
let gapHasComment = false;
|
|
86
|
+
let afterBareHash = false;
|
|
87
|
+
let endsWithBareHash = false;
|
|
88
|
+
for (const token of tokenizeSQL(value)) {
|
|
89
|
+
const comment = isClosedComment(token);
|
|
90
|
+
if (comment || token.kind === 'whitespace') {
|
|
91
|
+
gap += token.text;
|
|
92
|
+
gapHasComment = gapHasComment || comment;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
out += gapHasComment ? (afterBareHash ? '\n' : ' ') : gap;
|
|
96
|
+
out += token.text;
|
|
97
|
+
gap = '';
|
|
98
|
+
gapHasComment = false;
|
|
99
|
+
afterBareHash = token.kind === 'punctuation' && token.text === '#';
|
|
100
|
+
// trim() also drops Unicode whitespace, which the lexer reads as punctuation.
|
|
101
|
+
if (!/^\s$/.test(token.text))
|
|
102
|
+
endsWithBareHash = afterBareHash;
|
|
103
|
+
}
|
|
104
|
+
return endsWithBareHash ? `${out.trim()}\n` : out.trim();
|
|
3
105
|
}
|
|
4
106
|
export function normalizeEngine(engine) {
|
|
107
|
+
if (isKafkaEngine(engine))
|
|
108
|
+
return normalizeKafkaEngine(engine);
|
|
5
109
|
let normalized = engine.trim().replace(/^Shared/, '');
|
|
6
110
|
if (!normalized.includes('(')) {
|
|
7
111
|
normalized += '()';
|
|
8
112
|
}
|
|
9
113
|
return normalized;
|
|
10
114
|
}
|
|
115
|
+
// Whitespace and closed comments. The lexer reads Unicode whitespace such as a
|
|
116
|
+
// no-break space as punctuation; ClickHouse skips it like a space, and chkit
|
|
117
|
+
// has always collapsed it like one.
|
|
118
|
+
function separatesTokens(token) {
|
|
119
|
+
if (token.kind === 'whitespace')
|
|
120
|
+
return true;
|
|
121
|
+
if (token.kind === 'line_comment' || token.kind === 'block_comment')
|
|
122
|
+
return token.terminated;
|
|
123
|
+
return token.kind === 'punctuation' && /^\s$/.test(token.text);
|
|
124
|
+
}
|
|
125
|
+
function isClosedComment(token) {
|
|
126
|
+
return (token.kind === 'line_comment' || token.kind === 'block_comment') && token.terminated;
|
|
127
|
+
}
|
|
11
128
|
//# sourceMappingURL=sql-normalizer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-normalizer.js","sourceRoot":"","sources":["../src/sql-normalizer.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"sql-normalizer.js","sourceRoot":"","sources":["../src/sql-normalizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type QuoteChar = "'" | '"' | '`';
|
|
2
|
+
export declare function isQuoteChar(char: string | undefined): char is QuoteChar;
|
|
3
|
+
export declare function findQuoteEnd(sql: string, start: number, quote: string): number;
|
|
4
|
+
/**
|
|
5
|
+
* Peel one layer of wrapping parentheses, but only when the leading `(` closes
|
|
6
|
+
* at the very end — so `(a, b)` becomes `a, b` while `(a), (b)` is left intact.
|
|
7
|
+
*/
|
|
8
|
+
export declare function stripWrappingParens(input: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Index of the `)` that matches the `(` at `openIndex`, or undefined when the
|
|
11
|
+
* parens are unbalanced.
|
|
12
|
+
*/
|
|
13
|
+
export declare function findMatchingParen(input: string, openIndex: number): number | undefined;
|
|
14
|
+
/** Find a clause/delimiter outside quoted strings, identifiers and parentheses. */
|
|
15
|
+
export declare function findTopLevelSQLPattern(sql: string, pattern: RegExp): {
|
|
16
|
+
index: number;
|
|
17
|
+
length: number;
|
|
18
|
+
} | undefined;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=sql-scan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-scan.d.ts","sourceRoot":"","sources":["../src/sql-scan.ts"],"names":[],"mappings":"AAQA,KAAK,SAAS,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAEhC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,IAAI,SAAS,CAEvE;AAID,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAS9E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYtF;AAED,mFAAmF;AACnF,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GACd;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAyB/C"}
|
package/dist/sql-scan.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Quote-aware SQL scanning primitives. Characters inside a single-quoted
|
|
2
|
+
// string, a double-quoted identifier, or a backtick-quoted identifier are
|
|
3
|
+
// literal text, not structure — parens and commas there must be ignored.
|
|
4
|
+
//
|
|
5
|
+
// Every scanner skips quoted regions through `findQuoteEnd`, so the escaping
|
|
6
|
+
// rule lives in exactly one place and the copies cannot drift apart again
|
|
7
|
+
// (#197).
|
|
8
|
+
export function isQuoteChar(char) {
|
|
9
|
+
return char === "'" || char === '"' || char === '`';
|
|
10
|
+
}
|
|
11
|
+
// Index of the quote closing the one at `start`; honours backslash escapes and
|
|
12
|
+
// doubled quotes. Unterminated input runs to the end of the string.
|
|
13
|
+
export function findQuoteEnd(sql, start, quote) {
|
|
14
|
+
for (let i = start + 1; i < sql.length; i += 1) {
|
|
15
|
+
if (sql[i] === '\\')
|
|
16
|
+
i += 1;
|
|
17
|
+
else if (sql[i] === quote) {
|
|
18
|
+
if (sql[i + 1] === quote)
|
|
19
|
+
i += 1;
|
|
20
|
+
else
|
|
21
|
+
return i;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return sql.length;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Peel one layer of wrapping parentheses, but only when the leading `(` closes
|
|
28
|
+
* at the very end — so `(a, b)` becomes `a, b` while `(a), (b)` is left intact.
|
|
29
|
+
*/
|
|
30
|
+
export function stripWrappingParens(input) {
|
|
31
|
+
if (!input.startsWith('(') || !input.endsWith(')'))
|
|
32
|
+
return input;
|
|
33
|
+
const close = findMatchingParen(input, 0);
|
|
34
|
+
return close === input.length - 1 ? input.slice(1, -1).trim() : input;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Index of the `)` that matches the `(` at `openIndex`, or undefined when the
|
|
38
|
+
* parens are unbalanced.
|
|
39
|
+
*/
|
|
40
|
+
export function findMatchingParen(input, openIndex) {
|
|
41
|
+
let depth = 0;
|
|
42
|
+
for (let i = openIndex; i < input.length; i += 1) {
|
|
43
|
+
const char = input[i];
|
|
44
|
+
if (isQuoteChar(char))
|
|
45
|
+
i = findQuoteEnd(input, i, char);
|
|
46
|
+
else if (char === '(')
|
|
47
|
+
depth += 1;
|
|
48
|
+
else if (char === ')') {
|
|
49
|
+
depth -= 1;
|
|
50
|
+
if (depth === 0)
|
|
51
|
+
return i;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
/** Find a clause/delimiter outside quoted strings, identifiers and parentheses. */
|
|
57
|
+
export function findTopLevelSQLPattern(sql, pattern) {
|
|
58
|
+
let depth = 0;
|
|
59
|
+
for (let i = 0; i < sql.length; i += 1) {
|
|
60
|
+
const char = sql[i];
|
|
61
|
+
if (isQuoteChar(char)) {
|
|
62
|
+
i = findQuoteEnd(sql, i, char);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (char === '(') {
|
|
66
|
+
depth += 1;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (char === ')') {
|
|
70
|
+
depth -= 1;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (depth !== 0 ||
|
|
74
|
+
(/[A-Za-z_]/.test(char ?? '') && i > 0 && /[A-Za-z0-9_]/.test(sql[i - 1] ?? '')))
|
|
75
|
+
continue;
|
|
76
|
+
const match = sql.slice(i).match(pattern);
|
|
77
|
+
if (match?.index === 0)
|
|
78
|
+
return { index: i, length: match[0].length };
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=sql-scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-scan.js","sourceRoot":"","sources":["../src/sql-scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-splitter.d.ts","sourceRoot":"","sources":["../src/sql-splitter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sql-splitter.d.ts","sourceRoot":"","sources":["../src/sql-splitter.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAoGxD;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAsHjE"}
|
package/dist/sql-splitter.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isTrivia, tokenizeSQL } from './sql-lexer.js';
|
|
1
2
|
export function splitSqlStatements(sql) {
|
|
2
3
|
const statements = [];
|
|
3
4
|
let current = '';
|
|
@@ -19,19 +20,29 @@ export function splitSqlStatements(sql) {
|
|
|
19
20
|
}
|
|
20
21
|
if (inSingleQuote) {
|
|
21
22
|
current += ch;
|
|
23
|
+
if (ch === '\\' && next) {
|
|
24
|
+
current += next;
|
|
25
|
+
i += 1;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
22
28
|
if (ch === "'" && next === "'") {
|
|
23
29
|
current += next;
|
|
24
30
|
i += 1;
|
|
25
31
|
continue;
|
|
26
32
|
}
|
|
27
|
-
if (ch === "'"
|
|
33
|
+
if (ch === "'") {
|
|
28
34
|
inSingleQuote = false;
|
|
29
35
|
}
|
|
30
36
|
continue;
|
|
31
37
|
}
|
|
32
38
|
if (inDoubleQuote) {
|
|
33
39
|
current += ch;
|
|
34
|
-
if (ch === '
|
|
40
|
+
if (ch === '\\' && next) {
|
|
41
|
+
current += next;
|
|
42
|
+
i += 1;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (ch === '"') {
|
|
35
46
|
inDoubleQuote = false;
|
|
36
47
|
}
|
|
37
48
|
continue;
|
|
@@ -110,19 +121,29 @@ export function extractExecutableStatements(sql) {
|
|
|
110
121
|
}
|
|
111
122
|
if (inSingleQuote) {
|
|
112
123
|
stripped += ch;
|
|
124
|
+
if (ch === '\\' && next) {
|
|
125
|
+
stripped += next;
|
|
126
|
+
i += 1;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
113
129
|
if (ch === "'" && next === "'") {
|
|
114
130
|
stripped += next;
|
|
115
131
|
i += 1;
|
|
116
132
|
continue;
|
|
117
133
|
}
|
|
118
|
-
if (ch === "'"
|
|
134
|
+
if (ch === "'") {
|
|
119
135
|
inSingleQuote = false;
|
|
120
136
|
}
|
|
121
137
|
continue;
|
|
122
138
|
}
|
|
123
139
|
if (inDoubleQuote) {
|
|
124
140
|
stripped += ch;
|
|
125
|
-
if (ch === '
|
|
141
|
+
if (ch === '\\' && next) {
|
|
142
|
+
stripped += next;
|
|
143
|
+
i += 1;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (ch === '"') {
|
|
126
147
|
inDoubleQuote = false;
|
|
127
148
|
}
|
|
128
149
|
continue;
|
|
@@ -163,6 +184,32 @@ export function extractExecutableStatements(sql) {
|
|
|
163
184
|
}
|
|
164
185
|
stripped += ch;
|
|
165
186
|
}
|
|
166
|
-
|
|
187
|
+
const statements = splitSqlStatements(stripped);
|
|
188
|
+
// A statement made only of comments is not executable: ClickHouse rejects it
|
|
189
|
+
// as "Empty query" (for example a block comment after the last statement).
|
|
190
|
+
const executable = statements.filter(hasExecutableContent);
|
|
191
|
+
if (executable.length === statements.length)
|
|
192
|
+
return statements;
|
|
193
|
+
// The splitter does not know `#` and `//` line comments (#197). When one of
|
|
194
|
+
// them holds a `;`, the splitter cut that comment line in two; keeping the
|
|
195
|
+
// comment-only piece makes ClickHouse reject the file instead of running the
|
|
196
|
+
// commented-out text after the `;`.
|
|
197
|
+
if (tokenizeSQL(stripped).some(isLineCommentWithSemicolon))
|
|
198
|
+
return statements;
|
|
199
|
+
return executable;
|
|
200
|
+
}
|
|
201
|
+
// True unless every token is whitespace or a terminated comment (`/* */`,
|
|
202
|
+
// nested or not, `# `, `#!`, `//`; `--` comments are already stripped). An
|
|
203
|
+
// unterminated comment counts as content, so ClickHouse reports it.
|
|
204
|
+
function hasExecutableContent(statement) {
|
|
205
|
+
// Only a statement that starts like a comment can be made only of comments.
|
|
206
|
+
if (!/^[-/#]/.test(statement))
|
|
207
|
+
return true;
|
|
208
|
+
// The splitter appended the `;`; a trailing line comment would swallow it.
|
|
209
|
+
const body = statement.endsWith(';') ? statement.slice(0, -1) : statement;
|
|
210
|
+
return tokenizeSQL(body).some((token) => !isTrivia(token) || !token.terminated);
|
|
211
|
+
}
|
|
212
|
+
function isLineCommentWithSemicolon(token) {
|
|
213
|
+
return token.kind === 'line_comment' && token.text.includes(';');
|
|
167
214
|
}
|
|
168
215
|
//# sourceMappingURL=sql-splitter.js.map
|