@chkit/clickhouse 0.1.2-beta.7 → 0.2.0-beta.12
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/dist/alter-column-statement.d.ts +32 -0
- package/dist/alter-column-statement.d.ts.map +1 -0
- package/dist/alter-column-statement.js +137 -0
- package/dist/alter-column-statement.js.map +1 -0
- package/dist/async-insert-guard.d.ts +31 -0
- package/dist/async-insert-guard.d.ts.map +1 -0
- package/dist/async-insert-guard.js +55 -0
- package/dist/async-insert-guard.js.map +1 -0
- package/dist/canonicalize.d.ts +21 -0
- package/dist/canonicalize.d.ts.map +1 -0
- package/dist/canonicalize.js +70 -0
- package/dist/canonicalize.js.map +1 -0
- package/dist/create-table-parser.d.ts +3 -0
- package/dist/create-table-parser.d.ts.map +1 -1
- package/dist/create-table-parser.js +54 -43
- package/dist/create-table-parser.js.map +1 -1
- package/dist/ddl-propagation.d.ts +62 -6
- package/dist/ddl-propagation.d.ts.map +1 -1
- package/dist/ddl-propagation.js +459 -49
- package/dist/ddl-propagation.js.map +1 -1
- package/dist/e2e-testkit.d.ts +26 -4
- package/dist/e2e-testkit.d.ts.map +1 -1
- package/dist/e2e-testkit.js +56 -14
- package/dist/e2e-testkit.js.map +1 -1
- package/dist/exact-json.d.ts +27 -0
- package/dist/exact-json.d.ts.map +1 -0
- package/dist/exact-json.js +128 -0
- package/dist/exact-json.js.map +1 -0
- package/dist/index.d.ts +52 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +220 -64
- package/dist/index.js.map +1 -1
- package/dist/insert-barrier.d.ts +32 -0
- package/dist/insert-barrier.d.ts.map +1 -0
- package/dist/insert-barrier.js +57 -0
- package/dist/insert-barrier.js.map +1 -0
- package/dist/replicas.d.ts +21 -0
- package/dist/replicas.d.ts.map +1 -0
- package/dist/replicas.js +39 -0
- package/dist/replicas.js.map +1 -0
- package/dist/view-details.d.ts +30 -0
- package/dist/view-details.d.ts.map +1 -0
- package/dist/view-details.js +73 -0
- package/dist/view-details.js.map +1 -0
- package/package.json +5 -2
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a `MODIFY COLUMN` statement sets, in `system.columns` terms. A field is
|
|
3
|
+
* set only when the statement sets it: ClickHouse keeps a column's default,
|
|
4
|
+
* comment and codec when MODIFY COLUMN leaves them out.
|
|
5
|
+
*/
|
|
6
|
+
export interface ColumnIntent {
|
|
7
|
+
/** `''` after `REMOVE DEFAULT` (or MATERIALIZED, ALIAS, EPHEMERAL). */
|
|
8
|
+
defaultKind?: string;
|
|
9
|
+
/** The expression as written; the server formats it before it is compared. */
|
|
10
|
+
defaultExpression?: string;
|
|
11
|
+
/** `EPHEMERAL` without an expression stores `defaultValueOfTypeName(<type>)`. */
|
|
12
|
+
typeDefault?: boolean;
|
|
13
|
+
comment?: string;
|
|
14
|
+
/** `REMOVE CODEC`. A codec the statement sets is compared between replicas only. */
|
|
15
|
+
codecRemoved?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Reads what `ALTER TABLE ... MODIFY COLUMN <column> ...` sets. Returns an
|
|
19
|
+
* empty intent when the statement is not a single MODIFY COLUMN of `column`
|
|
20
|
+
* (hand-written or plugin-rewritten SQL), so the wait falls back to checking
|
|
21
|
+
* that every replica agrees.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseModifyColumnIntent(statement: string, column: string): ColumnIntent;
|
|
24
|
+
/**
|
|
25
|
+
* Reads `ALTER TABLE ... RENAME COLUMN [IF EXISTS] <from> TO <to>`. Names may
|
|
26
|
+
* contain ':', so an operation key cannot tell where `from` ends.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseRenameColumn(statement: string): {
|
|
29
|
+
from: string;
|
|
30
|
+
to: string;
|
|
31
|
+
} | undefined;
|
|
32
|
+
//# sourceMappingURL=alter-column-statement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alter-column-statement.d.ts","sourceRoot":"","sources":["../src/alter-column-statement.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,oFAAoF;IACpF,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAMD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CA+CvF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAS7F"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { identifierName, isTrivia, stringLiteralValue, tokenizeSQL } from '@chkit/core';
|
|
2
|
+
const DEFAULT_KINDS = new Set(['DEFAULT', 'MATERIALIZED', 'ALIAS', 'EPHEMERAL']);
|
|
3
|
+
// Clauses that may follow a column's type or default expression.
|
|
4
|
+
const TRAILING_CLAUSES = new Set(['TTL', 'SETTINGS', 'FIRST', 'AFTER', 'STATISTICS', 'PRIMARY']);
|
|
5
|
+
/**
|
|
6
|
+
* Reads what `ALTER TABLE ... MODIFY COLUMN <column> ...` sets. Returns an
|
|
7
|
+
* empty intent when the statement is not a single MODIFY COLUMN of `column`
|
|
8
|
+
* (hand-written or plugin-rewritten SQL), so the wait falls back to checking
|
|
9
|
+
* that every replica agrees.
|
|
10
|
+
*/
|
|
11
|
+
export function parseModifyColumnIntent(statement, column) {
|
|
12
|
+
const tokens = significantTokens(statement);
|
|
13
|
+
let i = findTopLevel(tokens, ['MODIFY', 'COLUMN']);
|
|
14
|
+
if (i < 0)
|
|
15
|
+
return {};
|
|
16
|
+
if (word(tokens[i]) === 'IF' && word(tokens[i + 1]) === 'EXISTS')
|
|
17
|
+
i += 2;
|
|
18
|
+
const name = tokens[i];
|
|
19
|
+
if (!name || identifierName(name) !== column)
|
|
20
|
+
return {};
|
|
21
|
+
i += 1;
|
|
22
|
+
if (word(tokens[i]) === 'REMOVE') {
|
|
23
|
+
const removed = word(tokens[i + 1]);
|
|
24
|
+
if (DEFAULT_KINDS.has(removed))
|
|
25
|
+
return { defaultKind: '' };
|
|
26
|
+
if (removed === 'CODEC')
|
|
27
|
+
return { codecRemoved: true };
|
|
28
|
+
if (removed === 'COMMENT')
|
|
29
|
+
return { comment: '' };
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
const intent = {};
|
|
33
|
+
i = skipToClause(tokens, i);
|
|
34
|
+
const kind = word(tokens[i]);
|
|
35
|
+
if (DEFAULT_KINDS.has(kind)) {
|
|
36
|
+
const start = i + 1;
|
|
37
|
+
i = skipToClause(tokens, start);
|
|
38
|
+
const expression = tokens.slice(start, i);
|
|
39
|
+
intent.defaultKind = kind;
|
|
40
|
+
if (expression.length > 0) {
|
|
41
|
+
// biome-ignore lint/style/noNonNullAssertion: slice is non-empty
|
|
42
|
+
intent.defaultExpression = statement.slice(expression[0].start, expression[expression.length - 1].end);
|
|
43
|
+
}
|
|
44
|
+
else if (kind === 'EPHEMERAL') {
|
|
45
|
+
intent.typeDefault = true;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (;;) {
|
|
52
|
+
const clause = word(tokens[i]);
|
|
53
|
+
if (clause === 'COMMENT') {
|
|
54
|
+
const value = tokens[i + 1] ? stringLiteralValue(tokens[i + 1]) : undefined;
|
|
55
|
+
if (value === undefined)
|
|
56
|
+
return intent;
|
|
57
|
+
intent.comment = value;
|
|
58
|
+
i += 2;
|
|
59
|
+
}
|
|
60
|
+
else if (clause === 'CODEC') {
|
|
61
|
+
i = skipToClause(tokens, i + 1);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
return intent;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Reads `ALTER TABLE ... RENAME COLUMN [IF EXISTS] <from> TO <to>`. Names may
|
|
70
|
+
* contain ':', so an operation key cannot tell where `from` ends.
|
|
71
|
+
*/
|
|
72
|
+
export function parseRenameColumn(statement) {
|
|
73
|
+
const tokens = significantTokens(statement);
|
|
74
|
+
let i = findTopLevel(tokens, ['RENAME', 'COLUMN']);
|
|
75
|
+
if (i < 0)
|
|
76
|
+
return undefined;
|
|
77
|
+
if (word(tokens[i]) === 'IF' && word(tokens[i + 1]) === 'EXISTS')
|
|
78
|
+
i += 2;
|
|
79
|
+
const from = tokens[i] ? identifierName(tokens[i]) : undefined;
|
|
80
|
+
const to = tokens[i + 2] ? identifierName(tokens[i + 2]) : undefined;
|
|
81
|
+
if (from === undefined || to === undefined || word(tokens[i + 1]) !== 'TO')
|
|
82
|
+
return undefined;
|
|
83
|
+
return { from, to };
|
|
84
|
+
}
|
|
85
|
+
function significantTokens(statement) {
|
|
86
|
+
return tokenizeSQL(statement).filter((token) => !isTrivia(token));
|
|
87
|
+
}
|
|
88
|
+
/** Index just past the first top-level occurrence of the keyword sequence. */
|
|
89
|
+
function findTopLevel(tokens, keywords) {
|
|
90
|
+
let depth = 0;
|
|
91
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
92
|
+
depth += nesting(tokens[i]);
|
|
93
|
+
if (depth === 0 && keywords.every((keyword, offset) => word(tokens[i + offset]) === keyword)) {
|
|
94
|
+
return i + keywords.length;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return -1;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Index of the next top-level clause keyword at or after `from`: a default
|
|
101
|
+
* kind, `COMMENT '<text>'`, `CODEC(...)`, a trailing clause, `,` or `;`.
|
|
102
|
+
*/
|
|
103
|
+
function skipToClause(tokens, from) {
|
|
104
|
+
let depth = 0;
|
|
105
|
+
for (let i = from; i < tokens.length; i++) {
|
|
106
|
+
if (depth === 0 && isClauseStart(tokens, i))
|
|
107
|
+
return i;
|
|
108
|
+
depth += nesting(tokens[i]);
|
|
109
|
+
}
|
|
110
|
+
return tokens.length;
|
|
111
|
+
}
|
|
112
|
+
function isClauseStart(tokens, i) {
|
|
113
|
+
const token = tokens[i];
|
|
114
|
+
if (token.text === ',' || token.text === ';')
|
|
115
|
+
return true;
|
|
116
|
+
const keyword = word(token);
|
|
117
|
+
if (DEFAULT_KINDS.has(keyword) || TRAILING_CLAUSES.has(keyword))
|
|
118
|
+
return true;
|
|
119
|
+
if (keyword === 'COMMENT')
|
|
120
|
+
return tokens[i + 1]?.kind === 'string';
|
|
121
|
+
if (keyword === 'CODEC')
|
|
122
|
+
return tokens[i + 1]?.text === '(';
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
function nesting(token) {
|
|
126
|
+
if (token?.kind !== 'punctuation')
|
|
127
|
+
return 0;
|
|
128
|
+
if (token.text === '(' || token.text === '[')
|
|
129
|
+
return 1;
|
|
130
|
+
if (token.text === ')' || token.text === ']')
|
|
131
|
+
return -1;
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
function word(token) {
|
|
135
|
+
return token?.kind === 'identifier' ? token.text.toUpperCase() : '';
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=alter-column-statement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alter-column-statement.js","sourceRoot":"","sources":["../src/alter-column-statement.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ClickHouseExecutor } from './index.js';
|
|
2
|
+
export interface QualifiedTable {
|
|
3
|
+
database: string;
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
export interface AsyncInsertGuardOptions {
|
|
7
|
+
/** `clickhouse.cluster`; without it the `default` cluster is probed (ObsessionDB). */
|
|
8
|
+
cluster?: string;
|
|
9
|
+
log?: (line: string) => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Refuses to go on when an asynchronous INSERT with `wait_for_async_insert = 0`
|
|
13
|
+
* wrote to one of `tables` in the last 10 minutes.
|
|
14
|
+
*
|
|
15
|
+
* ClickHouse binds the async insert buffer of a table to its uuid. When the
|
|
16
|
+
* table is renamed or exchanged before a buffer is flushed, the flush fails
|
|
17
|
+
* with TABLE_UUID_MISMATCH and the buffered rows are dropped. A writer that
|
|
18
|
+
* waits for the flush (`wait_for_async_insert = 1`) receives that error and
|
|
19
|
+
* can retry; a fire-and-forget writer already got its acknowledgement, so its
|
|
20
|
+
* rows are lost without a trace. A cutover swaps the table while writers keep
|
|
21
|
+
* inserting, so it must not run while such writers are active.
|
|
22
|
+
*
|
|
23
|
+
* `system.query_log` is read on every replica, because an INSERT is logged by
|
|
24
|
+
* the replica that received it. Only queries that went through the async
|
|
25
|
+
* queue count (`ProfileEvents['AsyncInsertQuery']`), and a setting absent from
|
|
26
|
+
* `Settings` has its built-in default. A user who cannot read `query_log` gets
|
|
27
|
+
* a warning instead of a refusal.
|
|
28
|
+
*/
|
|
29
|
+
export declare function assertNoFireAndForgetAsyncInserts(executor: ClickHouseExecutor, tables: QualifiedTable[], options?: AsyncInsertGuardOptions): Promise<void>;
|
|
30
|
+
export declare function fireAndForgetInsertsSql(source: string, names: string[]): string;
|
|
31
|
+
//# sourceMappingURL=async-insert-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-insert-guard.d.ts","sourceRoot":"","sources":["../src/async-insert-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAMpD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,uBAAuB;IACtC,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,kBAAkB,EAC5B,MAAM,EAAE,cAAc,EAAE,EACxB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAY/E"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { allReplicas, resolveReplicaFanout, stringLiteral } from './replicas.js';
|
|
2
|
+
/** How far back `system.query_log` is searched for fire-and-forget async inserts. */
|
|
3
|
+
const ASYNC_INSERT_WINDOW_MINUTES = 10;
|
|
4
|
+
/**
|
|
5
|
+
* Refuses to go on when an asynchronous INSERT with `wait_for_async_insert = 0`
|
|
6
|
+
* wrote to one of `tables` in the last 10 minutes.
|
|
7
|
+
*
|
|
8
|
+
* ClickHouse binds the async insert buffer of a table to its uuid. When the
|
|
9
|
+
* table is renamed or exchanged before a buffer is flushed, the flush fails
|
|
10
|
+
* with TABLE_UUID_MISMATCH and the buffered rows are dropped. A writer that
|
|
11
|
+
* waits for the flush (`wait_for_async_insert = 1`) receives that error and
|
|
12
|
+
* can retry; a fire-and-forget writer already got its acknowledgement, so its
|
|
13
|
+
* rows are lost without a trace. A cutover swaps the table while writers keep
|
|
14
|
+
* inserting, so it must not run while such writers are active.
|
|
15
|
+
*
|
|
16
|
+
* `system.query_log` is read on every replica, because an INSERT is logged by
|
|
17
|
+
* the replica that received it. Only queries that went through the async
|
|
18
|
+
* queue count (`ProfileEvents['AsyncInsertQuery']`), and a setting absent from
|
|
19
|
+
* `Settings` has its built-in default. A user who cannot read `query_log` gets
|
|
20
|
+
* a warning instead of a refusal.
|
|
21
|
+
*/
|
|
22
|
+
export async function assertNoFireAndForgetAsyncInserts(executor, tables, options = {}) {
|
|
23
|
+
const names = tables.map((table) => `${table.database}.${table.name}`);
|
|
24
|
+
const fanout = await resolveReplicaFanout(executor, options.cluster);
|
|
25
|
+
const source = fanout ? allReplicas(fanout, 'system.query_log') : 'system.query_log';
|
|
26
|
+
let rows;
|
|
27
|
+
try {
|
|
28
|
+
rows = await executor.query(fireAndForgetInsertsSql(source, names));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
options.log?.(` warning: could not read system.query_log to check for fire-and-forget async inserts into ${names.join(', ')} (${error instanceof Error ? error.message.split('\n')[0] : String(error)}). Make sure no writer uses async_insert = 1 with wait_for_async_insert = 0 on these tables.`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const found = rows.filter((row) => Number(row.inserts) > 0);
|
|
35
|
+
if (found.length === 0)
|
|
36
|
+
return;
|
|
37
|
+
const counts = found.map((row) => `${row.inserts} into ${row.table}`).join(', ');
|
|
38
|
+
throw new Error(`Fire-and-forget async inserts (async_insert = 1, wait_for_async_insert = 0) ran in the last ${ASYNC_INSERT_WINDOW_MINUTES} minutes: ${counts}. ` +
|
|
39
|
+
'ClickHouse drops the buffered rows of such an insert when its table is exchanged or renamed (TABLE_UUID_MISMATCH at flush), and the writer is never told. ' +
|
|
40
|
+
'Switch these writers to wait_for_async_insert = 1 (an insert in flight at the swap then fails with a retryable error) or pause them, ' +
|
|
41
|
+
`wait ${ASYNC_INSERT_WINDOW_MINUTES} minutes, and run chkit migrate --apply again.`);
|
|
42
|
+
}
|
|
43
|
+
export function fireAndForgetInsertsSql(source, names) {
|
|
44
|
+
const effective = (setting) => `if(mapContains(Settings, ${stringLiteral(setting)}), Settings[${stringLiteral(setting)}], (SELECT default FROM system.settings WHERE name = ${stringLiteral(setting)}))`;
|
|
45
|
+
const list = `[${names.map(stringLiteral).join(', ')}]`;
|
|
46
|
+
return [
|
|
47
|
+
`SELECT arrayJoin(arrayIntersect(tables, ${list})) AS table, count() AS inserts FROM ${source}`,
|
|
48
|
+
`WHERE event_date >= yesterday() AND event_time >= now() - INTERVAL ${ASYNC_INSERT_WINDOW_MINUTES} MINUTE`,
|
|
49
|
+
"AND type = 'QueryFinish' AND query_kind = 'Insert' AND ProfileEvents['AsyncInsertQuery'] > 0",
|
|
50
|
+
`AND hasAny(tables, ${list})`,
|
|
51
|
+
`AND ${effective('wait_for_async_insert')} IN ('0', 'false')`,
|
|
52
|
+
'GROUP BY table ORDER BY table',
|
|
53
|
+
].join(' ');
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=async-insert-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-insert-guard.js","sourceRoot":"","sources":["../src/async-insert-guard.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAEhF,qFAAqF;AACrF,MAAM,2BAA2B,GAAG,EAAE,CAAA;AAatC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,QAA4B,EAC5B,MAAwB,EACxB,UAAmC,EAAE;IAErC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IACtE,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;IACpE,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAA;IACpF,IAAI,IAAwD,CAAA;IAC5D,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,uBAAuB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;IACrE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,EAAE,CACX,8FAA8F,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,8FAA8F,CACvR,CAAA;QACD,OAAM;IACR,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IAC3D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,SAAS,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChF,MAAM,IAAI,KAAK,CACb,+FAA+F,2BAA2B,aAAa,MAAM,IAAI;QAC/I,4JAA4J;QAC5J,uIAAuI;QACvI,QAAQ,2BAA2B,gDAAgD,CACtF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,MAAc,EAAE,KAAe;IACrE,MAAM,SAAS,GAAG,CAAC,OAAe,EAAE,EAAE,CACpC,4BAA4B,aAAa,CAAC,OAAO,CAAC,eAAe,aAAa,CAAC,OAAO,CAAC,wDAAwD,aAAa,CAAC,OAAO,CAAC,IAAI,CAAA;IAC3K,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAA;IACvD,OAAO;QACL,2CAA2C,IAAI,wCAAwC,MAAM,EAAE;QAC/F,sEAAsE,2BAA2B,SAAS;QAC1G,8FAA8F;QAC9F,sBAAsB,IAAI,GAAG;QAC7B,OAAO,SAAS,CAAC,uBAAuB,CAAC,oBAAoB;QAC7D,+BAA+B;KAChC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACb,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ClickHouseExecutor } from './index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Canonicalize SQL fragments through ClickHouse's own formatter, so a schema
|
|
4
|
+
* fragment can be compared against what ClickHouse actually stores. The server
|
|
5
|
+
* rewrites expressions on the way in — spacing, operator precedence parens,
|
|
6
|
+
* `INTERVAL n UNIT` → `toIntervalUnit(n)` — which no local string normalizer can
|
|
7
|
+
* reproduce (#195).
|
|
8
|
+
*
|
|
9
|
+
* `wrap` distinguishes a scalar expression (wrapped as `SELECT <expr>`, prefix
|
|
10
|
+
* stripped back off) from a full `SELECT` query (formatted as-is). Formatting is
|
|
11
|
+
* batched into a single query using `formatQuerySingleLineOrNull`, which yields
|
|
12
|
+
* NULL for a fragment it can't parse rather than failing the whole batch — those
|
|
13
|
+
* are simply left out of the map so the caller falls back to string comparison.
|
|
14
|
+
*
|
|
15
|
+
* Returns a map from the input fragment to its ClickHouse-canonical form. A
|
|
16
|
+
* fragment missing from the map (unparseable, or an empty input) has no entry.
|
|
17
|
+
*/
|
|
18
|
+
export declare function canonicalizeSqlFragments(executor: ClickHouseExecutor, fragments: readonly string[], options: {
|
|
19
|
+
wrap: boolean;
|
|
20
|
+
}): Promise<Map<string, string>>;
|
|
21
|
+
//# sourceMappingURL=canonicalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonicalize.d.ts","sourceRoot":"","sources":["../src/canonicalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAkCpD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,kBAAkB,EAC5B,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,OAAO,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACzB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAwB9B"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
function quoteLiteral(value) {
|
|
2
|
+
return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "''")}'`;
|
|
3
|
+
}
|
|
4
|
+
function stripSelectPrefix(formatted) {
|
|
5
|
+
const match = formatted.match(/^SELECT\s+([\s\S]+)$/i);
|
|
6
|
+
return match?.[1]?.trim() ?? null;
|
|
7
|
+
}
|
|
8
|
+
// Keep each batched query well under ClickHouse's default max_query_size
|
|
9
|
+
// (262144 bytes) so a large schema doesn't overflow a single query — which would
|
|
10
|
+
// throw and silently drop the whole run back to string comparison.
|
|
11
|
+
const MAX_BATCH_LITERAL_BYTES = 128_000;
|
|
12
|
+
function batchFragments(fragments) {
|
|
13
|
+
const batches = [];
|
|
14
|
+
let current = [];
|
|
15
|
+
let size = 0;
|
|
16
|
+
for (const fragment of fragments) {
|
|
17
|
+
const cost = quoteLiteral(fragment).length + 2; // literal + ", " separator
|
|
18
|
+
if (current.length > 0 && size + cost > MAX_BATCH_LITERAL_BYTES) {
|
|
19
|
+
batches.push(current);
|
|
20
|
+
current = [];
|
|
21
|
+
size = 0;
|
|
22
|
+
}
|
|
23
|
+
current.push(fragment);
|
|
24
|
+
size += cost;
|
|
25
|
+
}
|
|
26
|
+
if (current.length > 0)
|
|
27
|
+
batches.push(current);
|
|
28
|
+
return batches;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Canonicalize SQL fragments through ClickHouse's own formatter, so a schema
|
|
32
|
+
* fragment can be compared against what ClickHouse actually stores. The server
|
|
33
|
+
* rewrites expressions on the way in — spacing, operator precedence parens,
|
|
34
|
+
* `INTERVAL n UNIT` → `toIntervalUnit(n)` — which no local string normalizer can
|
|
35
|
+
* reproduce (#195).
|
|
36
|
+
*
|
|
37
|
+
* `wrap` distinguishes a scalar expression (wrapped as `SELECT <expr>`, prefix
|
|
38
|
+
* stripped back off) from a full `SELECT` query (formatted as-is). Formatting is
|
|
39
|
+
* batched into a single query using `formatQuerySingleLineOrNull`, which yields
|
|
40
|
+
* NULL for a fragment it can't parse rather than failing the whole batch — those
|
|
41
|
+
* are simply left out of the map so the caller falls back to string comparison.
|
|
42
|
+
*
|
|
43
|
+
* Returns a map from the input fragment to its ClickHouse-canonical form. A
|
|
44
|
+
* fragment missing from the map (unparseable, or an empty input) has no entry.
|
|
45
|
+
*/
|
|
46
|
+
export async function canonicalizeSqlFragments(executor, fragments, options) {
|
|
47
|
+
const result = new Map();
|
|
48
|
+
const unique = [...new Set(fragments.map((fragment) => fragment.trim()).filter((f) => f.length > 0))];
|
|
49
|
+
if (unique.length === 0)
|
|
50
|
+
return result;
|
|
51
|
+
const formatCall = options.wrap
|
|
52
|
+
? `formatQuerySingleLineOrNull('SELECT ' || fragment)`
|
|
53
|
+
: `formatQuerySingleLineOrNull(fragment)`;
|
|
54
|
+
for (const batch of batchFragments(unique)) {
|
|
55
|
+
const arrayLiteral = batch.map(quoteLiteral).join(', ');
|
|
56
|
+
const rows = await executor.query(`SELECT arrayMap(fragment -> ${formatCall}, [${arrayLiteral}]) AS formatted`);
|
|
57
|
+
const formatted = rows[0]?.formatted ?? [];
|
|
58
|
+
for (let i = 0; i < batch.length; i += 1) {
|
|
59
|
+
const raw = batch[i];
|
|
60
|
+
const value = formatted[i];
|
|
61
|
+
if (raw === undefined || value == null)
|
|
62
|
+
continue;
|
|
63
|
+
const canonical = options.wrap ? stripSelectPrefix(value) : value.trim();
|
|
64
|
+
if (canonical)
|
|
65
|
+
result.set(raw, canonical);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=canonicalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonicalize.js","sourceRoot":"","sources":["../src/canonicalize.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type TableConstraintDefinition } from '@chkit/core';
|
|
1
2
|
type ProjectionDefinitionShape = {
|
|
2
3
|
name: string;
|
|
3
4
|
query: string;
|
|
@@ -14,5 +15,7 @@ export declare function parseOrderByFromCreateTableQuery(createTableQuery: strin
|
|
|
14
15
|
export declare function parsePartitionByFromCreateTableQuery(createTableQuery: string | undefined): string | undefined;
|
|
15
16
|
export declare function parseUniqueKeyFromCreateTableQuery(createTableQuery: string | undefined): string | undefined;
|
|
16
17
|
export declare function parseProjectionsFromCreateTableQuery(createTableQuery: string | undefined): ProjectionDefinitionShape[];
|
|
18
|
+
/** `CONSTRAINT name CHECK|ASSUME expr` entries of the column list. */
|
|
19
|
+
export declare function parseConstraintsFromCreateTableQuery(createTableQuery: string | undefined): TableConstraintDefinition[];
|
|
17
20
|
export {};
|
|
18
21
|
//# sourceMappingURL=create-table-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-table-parser.d.ts","sourceRoot":"","sources":["../src/create-table-parser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create-table-parser.d.ts","sourceRoot":"","sources":["../src/create-table-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,yBAAyB,EAE/B,MAAM,aAAa,CAAA;AAEpB,KAAK,yBAAyB,GAC1B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AA6EjD,wBAAgB,iCAAiC,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAoB9G;AAED,wBAAgB,4BAA4B,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAMrG;AAED,wBAAgB,+BAA+B,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAOxG;AAED,wBAAgB,mCAAmC,CACjD,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,MAAM,GAAG,SAAS,CAMpB;AAED,wBAAgB,gCAAgC,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAMzG;AAED,wBAAgB,oCAAoC,CAClD,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,MAAM,GAAG,SAAS,CAMpB;AAED,wBAAgB,kCAAkC,CAChD,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,MAAM,GAAG,SAAS,CAMpB;AAED,wBAAgB,oCAAoC,CAClD,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,yBAAyB,EAAE,CA8B7B;AAED,sEAAsE;AACtE,wBAAgB,oCAAoC,CAClD,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,yBAAyB,EAAE,CAiB7B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { normalizeProjectionIndex, normalizeSQLFragment, splitTopLevelComma } from '@chkit/core';
|
|
2
|
-
function parseClauseFromCreateTableQuery(createTableQuery, clausePattern, stopPattern) {
|
|
1
|
+
import { findQuoteEnd, findTopLevelSQLPattern, isQuoteChar, normalizeProjectionIndex, normalizeSQLFragment, splitTopLevelComma, unquoteIdentifiers, } from '@chkit/core';
|
|
2
|
+
function parseClauseFromCreateTableQuery(createTableQuery, clausePattern, stopPattern, preserveWhitespace = false) {
|
|
3
3
|
if (!createTableQuery)
|
|
4
4
|
return undefined;
|
|
5
5
|
// Table-level clauses (ENGINE, ORDER BY, PRIMARY KEY, ...) only appear after
|
|
@@ -7,55 +7,45 @@ function parseClauseFromCreateTableQuery(createTableQuery, clausePattern, stopPa
|
|
|
7
7
|
// body — e.g. the `ORDER BY` of a projection's SELECT — and swallow the real
|
|
8
8
|
// clause plus everything up to the next stop keyword (issue #190).
|
|
9
9
|
const options = extractTableOptions(createTableQuery);
|
|
10
|
-
const start = options
|
|
11
|
-
if (!start
|
|
10
|
+
const start = findTopLevelSQLPattern(options, clausePattern);
|
|
11
|
+
if (!start)
|
|
12
12
|
return undefined;
|
|
13
|
-
const afterClause = options.slice(start.index + start
|
|
14
|
-
const stop = afterClause
|
|
13
|
+
const afterClause = options.slice(start.index + start.length);
|
|
14
|
+
const stop = findTopLevelSQLPattern(afterClause, stopPattern);
|
|
15
15
|
const raw = (stop ? afterClause.slice(0, stop.index) : afterClause).trim();
|
|
16
16
|
if (!raw)
|
|
17
17
|
return undefined;
|
|
18
|
-
return normalizeSQLFragment(raw);
|
|
18
|
+
return preserveWhitespace ? raw : normalizeSQLFragment(raw);
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Positions of the parens that open and close the column list — the close being
|
|
22
22
|
* the one right before the table-level `ENGINE =`. Returns undefined when there
|
|
23
|
-
* is no balanced column list (e.g. a view, or a query we can't parse).
|
|
23
|
+
* is no balanced column list (e.g. a view, or a query we can't parse). Quoted
|
|
24
|
+
* strings and identifiers are skipped, so parens or commas inside a quoted
|
|
25
|
+
* table or column name never count.
|
|
24
26
|
*/
|
|
25
27
|
function findColumnListBounds(createTableQuery) {
|
|
26
|
-
const engineMatch = /\)\s*ENGINE\s*=/i.exec(createTableQuery);
|
|
27
|
-
if (!engineMatch || engineMatch.index === undefined)
|
|
28
|
-
return undefined;
|
|
29
|
-
const left = createTableQuery.slice(0, engineMatch.index + 1);
|
|
30
|
-
const openIndex = left.indexOf('(');
|
|
31
|
-
if (openIndex === -1)
|
|
32
|
-
return undefined;
|
|
33
28
|
let depth = 0;
|
|
34
|
-
let
|
|
35
|
-
let
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
continue;
|
|
40
|
-
if (inString) {
|
|
41
|
-
if (char === stringQuote && left[i - 1] !== '\\') {
|
|
42
|
-
inString = false;
|
|
43
|
-
}
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
if (char === "'" || char === '"') {
|
|
47
|
-
inString = true;
|
|
48
|
-
stringQuote = char;
|
|
29
|
+
let openIndex = -1;
|
|
30
|
+
for (let i = 0; i < createTableQuery.length; i += 1) {
|
|
31
|
+
const char = createTableQuery[i];
|
|
32
|
+
if (isQuoteChar(char)) {
|
|
33
|
+
i = findQuoteEnd(createTableQuery, i, char);
|
|
49
34
|
continue;
|
|
50
35
|
}
|
|
51
36
|
if (char === '(') {
|
|
37
|
+
if (depth === 0)
|
|
38
|
+
openIndex = i;
|
|
52
39
|
depth += 1;
|
|
53
40
|
continue;
|
|
54
41
|
}
|
|
55
42
|
if (char === ')') {
|
|
56
43
|
depth -= 1;
|
|
57
|
-
if (depth === 0)
|
|
58
|
-
return
|
|
44
|
+
if (depth === 0) {
|
|
45
|
+
return /^\s*ENGINE\s*=/i.test(createTableQuery.slice(i + 1))
|
|
46
|
+
? { open: openIndex, close: i }
|
|
47
|
+
: undefined;
|
|
48
|
+
}
|
|
59
49
|
}
|
|
60
50
|
}
|
|
61
51
|
return undefined;
|
|
@@ -81,10 +71,13 @@ function extractTableOptions(createTableQuery) {
|
|
|
81
71
|
export function parseSettingsFromCreateTableQuery(createTableQuery) {
|
|
82
72
|
if (!createTableQuery)
|
|
83
73
|
return {};
|
|
84
|
-
const
|
|
85
|
-
|
|
74
|
+
const options = extractTableOptions(createTableQuery);
|
|
75
|
+
const start = findTopLevelSQLPattern(options, /\bSETTINGS\b/i);
|
|
76
|
+
if (!start)
|
|
86
77
|
return {};
|
|
87
|
-
const
|
|
78
|
+
const tail = options.slice(start.index + start.length);
|
|
79
|
+
const stop = findTopLevelSQLPattern(tail, /\bCOMMENT\b|;/i);
|
|
80
|
+
const rawSettings = (stop ? tail.slice(0, stop.index) : tail).trim();
|
|
88
81
|
if (!rawSettings)
|
|
89
82
|
return {};
|
|
90
83
|
const items = splitTopLevelComma(rawSettings);
|
|
@@ -111,7 +104,7 @@ export function parseTTLFromCreateTableQuery(createTableQuery) {
|
|
|
111
104
|
return normalizeSQLFragment(raw);
|
|
112
105
|
}
|
|
113
106
|
export function parseEngineFromCreateTableQuery(createTableQuery) {
|
|
114
|
-
return parseClauseFromCreateTableQuery(createTableQuery, /\bENGINE\s*=\s*/i, /\bPRIMARY\s+KEY\b|\bORDER\s+BY\b|\bPARTITION\s+BY\b|\bUNIQUE\s+KEY\b|\bSAMPLE\s+BY\b|\bTTL\b|\bSETTINGS\b|;|$/i);
|
|
107
|
+
return parseClauseFromCreateTableQuery(createTableQuery, /\bENGINE\s*=\s*/i, /\bPRIMARY\s+KEY\b|\bORDER\s+BY\b|\bPARTITION\s+BY\b|\bUNIQUE\s+KEY\b|\bSAMPLE\s+BY\b|\bTTL\b|\bSETTINGS\b|\bCOMMENT\b|;|$/i, true);
|
|
115
108
|
}
|
|
116
109
|
export function parsePrimaryKeyFromCreateTableQuery(createTableQuery) {
|
|
117
110
|
return parseClauseFromCreateTableQuery(createTableQuery, /\bPRIMARY\s+KEY\b/i, /\bORDER\s+BY\b|\bPARTITION\s+BY\b|\bUNIQUE\s+KEY\b|\bSAMPLE\s+BY\b|\bTTL\b|\bSETTINGS\b|;|$/i);
|
|
@@ -134,25 +127,43 @@ export function parseProjectionsFromCreateTableQuery(createTableQuery) {
|
|
|
134
127
|
for (const part of parts) {
|
|
135
128
|
// Index-only projections have no SELECT body, so they must be matched
|
|
136
129
|
// before the parenthesized SELECT form.
|
|
137
|
-
const indexMatch = part.match(/^\s*PROJECTION\s+(
|
|
130
|
+
const indexMatch = part.match(/^\s*PROJECTION\s+(`(?:[^`\\]|\\.|``)+`|[A-Za-z_][A-Za-z0-9_]*)\s+INDEX\s+([\s\S]+?)\s+TYPE\s+([A-Za-z_][A-Za-z0-9_]*)\s*$/i);
|
|
138
131
|
if (indexMatch) {
|
|
139
|
-
const name = (indexMatch[1] ??
|
|
140
|
-
const index = normalizeProjectionIndex(indexMatch[
|
|
141
|
-
const type = (indexMatch[
|
|
132
|
+
const name = unquoteIdentifiers(indexMatch[1] ?? '').trim();
|
|
133
|
+
const index = normalizeProjectionIndex(indexMatch[2] ?? '');
|
|
134
|
+
const type = (indexMatch[3] ?? '').trim();
|
|
142
135
|
if (!name || !index || !type)
|
|
143
136
|
continue;
|
|
144
137
|
projections.push({ name, index, type });
|
|
145
138
|
continue;
|
|
146
139
|
}
|
|
147
|
-
const match = part.match(/^\s*PROJECTION\s+(
|
|
140
|
+
const match = part.match(/^\s*PROJECTION\s+(`(?:[^`\\]|\\.|``)+`|[A-Za-z_][A-Za-z0-9_]*)\s*\(([\s\S]*)\)\s*$/i);
|
|
148
141
|
if (!match)
|
|
149
142
|
continue;
|
|
150
|
-
const name = (match[1] ??
|
|
151
|
-
const query = normalizeSQLFragment((match[
|
|
143
|
+
const name = unquoteIdentifiers(match[1] ?? '').trim();
|
|
144
|
+
const query = normalizeSQLFragment((match[2] ?? '').trim());
|
|
152
145
|
if (!name || !query)
|
|
153
146
|
continue;
|
|
154
147
|
projections.push({ name, query });
|
|
155
148
|
}
|
|
156
149
|
return projections;
|
|
157
150
|
}
|
|
151
|
+
/** `CONSTRAINT name CHECK|ASSUME expr` entries of the column list. */
|
|
152
|
+
export function parseConstraintsFromCreateTableQuery(createTableQuery) {
|
|
153
|
+
const body = extractCreateTableBody(createTableQuery);
|
|
154
|
+
if (!body)
|
|
155
|
+
return [];
|
|
156
|
+
const constraints = [];
|
|
157
|
+
for (const part of splitTopLevelComma(body)) {
|
|
158
|
+
const match = part.match(/^\s*CONSTRAINT\s+(`(?:[^`\\]|\\.|``)+`|[A-Za-z_][A-Za-z0-9_]*)\s+(CHECK|ASSUME)\s+([\s\S]+?)\s*$/i);
|
|
159
|
+
if (!match)
|
|
160
|
+
continue;
|
|
161
|
+
const name = unquoteIdentifiers(match[1] ?? '').trim();
|
|
162
|
+
const expression = normalizeSQLFragment(match[3] ?? '');
|
|
163
|
+
if (!name || !expression)
|
|
164
|
+
continue;
|
|
165
|
+
constraints.push((match[2] ?? '').toUpperCase() === 'CHECK' ? { name, check: expression } : { name, assume: expression });
|
|
166
|
+
}
|
|
167
|
+
return constraints;
|
|
168
|
+
}
|
|
158
169
|
//# sourceMappingURL=create-table-parser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-table-parser.js","sourceRoot":"","sources":["../src/create-table-parser.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"create-table-parser.js","sourceRoot":"","sources":["../src/create-table-parser.ts"],"names":[],"mappings":"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"}
|