@intentius/chant-lexicon-sql 0.124.48 → 0.124.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/clickhouse/plan/statements.d.ts +29 -0
  2. package/dist/clickhouse/plan/statements.d.ts.map +1 -0
  3. package/dist/clickhouse/plan/statements.js +334 -0
  4. package/dist/codegen/classifier-page.d.ts.map +1 -1
  5. package/dist/codegen/classifier-page.js +23 -0
  6. package/dist/codegen/postgres-classifier-page.js +2 -2
  7. package/dist/codegen/registry.d.ts.map +1 -1
  8. package/dist/codegen/registry.js +5 -0
  9. package/dist/core/statement-class.d.ts +55 -0
  10. package/dist/core/statement-class.d.ts.map +1 -0
  11. package/dist/core/statement-class.js +43 -0
  12. package/dist/coverage/postgres.json +6 -6
  13. package/dist/drizzle.d.ts.map +1 -1
  14. package/dist/drizzle.js +5 -0
  15. package/dist/files/postgres.d.ts.map +1 -1
  16. package/dist/files/postgres.js +23 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/integrity.json +5 -5
  21. package/dist/lint/post-synth/postgres-names.d.ts.map +1 -1
  22. package/dist/lint/post-synth/postgres-names.js +3 -0
  23. package/dist/lint/rules/postgres-templates.js +2 -2
  24. package/dist/lsp/postgres-template.d.ts.map +1 -1
  25. package/dist/lsp/postgres-template.js +10 -0
  26. package/dist/manifest.json +1 -1
  27. package/dist/mcp/postgres.js +1 -1
  28. package/dist/meta.json +25 -0
  29. package/dist/migration-classify.d.ts +23 -0
  30. package/dist/migration-classify.d.ts.map +1 -0
  31. package/dist/migration-classify.js +44 -0
  32. package/dist/okf/index.md +5 -0
  33. package/dist/okf/types/PostgresForeignTable.md +9 -0
  34. package/dist/okf/types/PostgresOperatorClass.md +9 -0
  35. package/dist/okf/types/PostgresOperatorFamily.md +9 -0
  36. package/dist/okf/types/PostgresTextSearchConfiguration.md +9 -0
  37. package/dist/okf/types/PostgresTextSearchDictionary.md +9 -0
  38. package/dist/plugin.d.ts.map +1 -1
  39. package/dist/plugin.js +13 -0
  40. package/dist/postgres/access/acl.js +1 -1
  41. package/dist/postgres/access/import.js +1 -1
  42. package/dist/postgres/apply/apply.js +5 -0
  43. package/dist/postgres/apply/statements.d.ts.map +1 -1
  44. package/dist/postgres/apply/statements.js +156 -2
  45. package/dist/postgres/entities.d.ts +120 -2
  46. package/dist/postgres/entities.d.ts.map +1 -1
  47. package/dist/postgres/entities.js +180 -5
  48. package/dist/postgres/entity-types.d.ts +5 -0
  49. package/dist/postgres/entity-types.d.ts.map +1 -1
  50. package/dist/postgres/entity-types.js +5 -0
  51. package/dist/postgres/import/generator.d.ts.map +1 -1
  52. package/dist/postgres/import/generator.js +16 -1
  53. package/dist/postgres/live/catalog-kinds.d.ts +6 -0
  54. package/dist/postgres/live/catalog-kinds.d.ts.map +1 -1
  55. package/dist/postgres/live/catalog-kinds.js +246 -0
  56. package/dist/postgres/live/describe-resources.d.ts.map +1 -1
  57. package/dist/postgres/live/describe-resources.js +2 -0
  58. package/dist/postgres/migrate/observe.d.ts.map +1 -1
  59. package/dist/postgres/migrate/observe.js +4 -1
  60. package/dist/postgres/migrate/replication.d.ts +42 -0
  61. package/dist/postgres/migrate/replication.d.ts.map +1 -1
  62. package/dist/postgres/migrate/replication.js +53 -0
  63. package/dist/postgres/parser.d.ts +73 -1
  64. package/dist/postgres/parser.d.ts.map +1 -1
  65. package/dist/postgres/parser.js +181 -7
  66. package/dist/postgres/plan/deep.d.ts.map +1 -1
  67. package/dist/postgres/plan/deep.js +15 -4
  68. package/dist/postgres/plan/diff.d.ts.map +1 -1
  69. package/dist/postgres/plan/diff.js +120 -5
  70. package/dist/postgres/plan/normalize.d.ts +12 -1
  71. package/dist/postgres/plan/normalize.d.ts.map +1 -1
  72. package/dist/postgres/plan/normalize.js +151 -0
  73. package/dist/postgres/plan/rules.d.ts +3 -0
  74. package/dist/postgres/plan/rules.d.ts.map +1 -1
  75. package/dist/postgres/plan/rules.js +5 -2
  76. package/dist/postgres/plan/schema.js +1 -1
  77. package/dist/postgres/plan/statements.d.ts +26 -0
  78. package/dist/postgres/plan/statements.d.ts.map +1 -0
  79. package/dist/postgres/plan/statements.js +364 -0
  80. package/dist/postgres/testing/roundtrip-schema.d.ts +3 -2
  81. package/dist/postgres/testing/roundtrip-schema.d.ts.map +1 -1
  82. package/dist/postgres/testing/roundtrip-schema.js +12 -1
  83. package/dist/postgres.d.ts +1 -1
  84. package/dist/postgres.d.ts.map +1 -1
  85. package/dist/postgres.js +1 -1
  86. package/dist/rules/postgres-names.ts +2 -0
  87. package/dist/rules/postgres-templates.ts +2 -2
  88. package/dist/validate.d.ts.map +1 -1
  89. package/dist/validate.js +5 -0
  90. package/package.json +2 -2
  91. package/src/clickhouse/plan/statements.test.ts +61 -0
  92. package/src/clickhouse/plan/statements.ts +306 -0
  93. package/src/codegen/classifier-page.ts +23 -0
  94. package/src/codegen/postgres-classifier-page.ts +2 -2
  95. package/src/codegen/registry.ts +5 -0
  96. package/src/core/statement-class.ts +88 -0
  97. package/src/coverage/postgres.json +6 -6
  98. package/src/drizzle.ts +5 -0
  99. package/src/files/postgres.ts +22 -1
  100. package/src/generated/lexicon-sql.json +25 -0
  101. package/src/index.ts +1 -1
  102. package/src/lint/post-synth/postgres-checks.test.ts +8 -1
  103. package/src/lint/post-synth/postgres-names.ts +2 -0
  104. package/src/lint/rules/postgres-templates.ts +2 -2
  105. package/src/lsp/postgres-template.ts +9 -0
  106. package/src/mcp/postgres.ts +1 -1
  107. package/src/migration-classify.test.ts +150 -0
  108. package/src/migration-classify.ts +45 -0
  109. package/src/op/watch-pipeline.test.ts +8 -1
  110. package/src/plugin.ts +14 -0
  111. package/src/postgres/access/acl.ts +1 -1
  112. package/src/postgres/access/import.ts +1 -1
  113. package/src/postgres/apply/apply.ts +5 -0
  114. package/src/postgres/apply/statements.ts +151 -3
  115. package/src/postgres/database-kinds.test.ts +498 -17
  116. package/src/postgres/entities.ts +276 -5
  117. package/src/postgres/entity-types.ts +5 -0
  118. package/src/postgres/import/generator.ts +13 -1
  119. package/src/postgres/live/catalog-kinds.ts +271 -0
  120. package/src/postgres/live/describe-resources.ts +2 -0
  121. package/src/postgres/migrate/observe.ts +3 -1
  122. package/src/postgres/migrate/replication.ts +66 -0
  123. package/src/postgres/migrate/subscriber.e2e.test.ts +141 -0
  124. package/src/postgres/migrate/subscriber.test.ts +59 -0
  125. package/src/postgres/parser.ts +225 -6
  126. package/src/postgres/plan/deep.ts +15 -4
  127. package/src/postgres/plan/diff.ts +107 -6
  128. package/src/postgres/plan/normalize.ts +155 -0
  129. package/src/postgres/plan/rules.ts +25 -4
  130. package/src/postgres/plan/schema.ts +1 -1
  131. package/src/postgres/plan/statements.test.ts +78 -0
  132. package/src/postgres/plan/statements.ts +299 -0
  133. package/src/postgres/testing/roundtrip-schema.ts +12 -1
  134. package/src/postgres.ts +17 -0
  135. package/src/skills/chant-sql-postgres-migration.md +1 -0
  136. package/src/skills/chant-sql-postgres.md +5 -0
  137. package/src/sql-files.test.ts +33 -0
  138. package/src/validate.ts +5 -0
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The ClickHouse statement classifier (#4407): a migration file's statements,
3
+ * each given the class and rule the schema-diff classifier (`./rules.ts`)
4
+ * gives the same change. It reads text and contacts no server.
5
+ *
6
+ * `CREATE` goes through the declaration parser (`../parser.ts`), which also
7
+ * tells the classifier a table's keys when the file creates the table. The
8
+ * parser has no `ALTER` grammar, so `ALTER`, `DROP` and `RENAME` are read word
9
+ * by word from the same tokens.
10
+ *
11
+ * A statement alone cannot decide some changes, and those answer `undecided`
12
+ * naming both rules, the way `./disruption.ts` answers `unknown`:
13
+ *
14
+ * - `MODIFY COLUMN c <type>` is SQLCH210 (a rewrite), or SQLCH211 (a rebuild)
15
+ * when `c` is in a key. The file states the keys only when it creates the
16
+ * table.
17
+ * - `DROP COLUMN` is SQLCH202 or SQLCH213, and `RENAME COLUMN` SQLCH212 or
18
+ * SQLCH213, on the same condition.
19
+ * - `MODIFY ORDER BY` that the same `ALTER` pairs with `ADD COLUMN` is SQLCH216
20
+ * when it only appends those columns and SQLCH220 (a rebuild) otherwise.
21
+ * Without such a column it is SQLCH220.
22
+ */
23
+ import { type StatementVerdict } from "../../core/statement-class.js";
24
+ /**
25
+ * Classify a migration's up statements, in order. The statements are the
26
+ * file's own text, one per entry.
27
+ */
28
+ export declare function classifyClickHouseStatements(statements: readonly string[]): StatementVerdict[];
29
+ //# sourceMappingURL=statements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statements.d.ts","sourceRoot":"","sources":["../../../src/clickhouse/plan/statements.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH,OAAO,EAAkE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AA0PnI;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAwB9F"}
@@ -0,0 +1,334 @@
1
+ /**
2
+ * The ClickHouse statement classifier (#4407): a migration file's statements,
3
+ * each given the class and rule the schema-diff classifier (`./rules.ts`)
4
+ * gives the same change. It reads text and contacts no server.
5
+ *
6
+ * `CREATE` goes through the declaration parser (`../parser.ts`), which also
7
+ * tells the classifier a table's keys when the file creates the table. The
8
+ * parser has no `ALTER` grammar, so `ALTER`, `DROP` and `RENAME` are read word
9
+ * by word from the same tokens.
10
+ *
11
+ * A statement alone cannot decide some changes, and those answer `undecided`
12
+ * naming both rules, the way `./disruption.ts` answers `unknown`:
13
+ *
14
+ * - `MODIFY COLUMN c <type>` is SQLCH210 (a rewrite), or SQLCH211 (a rebuild)
15
+ * when `c` is in a key. The file states the keys only when it creates the
16
+ * table.
17
+ * - `DROP COLUMN` is SQLCH202 or SQLCH213, and `RENAME COLUMN` SQLCH212 or
18
+ * SQLCH213, on the same condition.
19
+ * - `MODIFY ORDER BY` that the same `ALTER` pairs with `ADD COLUMN` is SQLCH216
20
+ * when it only appends those columns and SQLCH220 (a rebuild) otherwise.
21
+ * Without such a column it is SQLCH220.
22
+ */
23
+ import { MERGE_TREE_SETTINGS } from "../../generated/clickhouse.js";
24
+ import { isTrivia, tokenizeText } from "../tokens.js";
25
+ import { parseCreate } from "../parser.js";
26
+ import { classifiedOf, splitTopLevel, sqlHead } from "../../core/statement-class.js";
27
+ import { CHANGE_CLASSES, CLASSIFIER_RULES } from "./rules.js";
28
+ const classified = (id, extra = {}) => classifiedOf(CLASSIFIER_RULES, CHANGE_CLASSES.label, id, extra);
29
+ const rule = (id, extra) => ({ rules: [{ id, ...(extra ? { extra } : {}) }] });
30
+ /** Settings only a stream engine takes (the same list `./disruption.ts` reads). */
31
+ const STREAM_SETTING = /^(?:kafka_|rabbitmq_|nats_|s3queue_|azure_queue_|keeper_path$)/;
32
+ const bare = (name) => (name.split(".").pop() ?? name).replace(/^[`"]|[`"]$/g, "").toLowerCase();
33
+ const text = (tokens, s) => (s ? tokens.slice(s.from, s.to).filter((t) => !isTrivia(t)).map((t) => t.text).join(" ") : "");
34
+ /** The identifier tokens of a key expression that are not function names. */
35
+ function columnsOf(tokens, s) {
36
+ if (!s)
37
+ return [];
38
+ const toks = tokens.slice(s.from, s.to).filter((t) => !isTrivia(t));
39
+ return toks.flatMap((t, i) => ((t.kind === "ident" || t.kind === "qident") && toks[i + 1]?.text !== "(" ? [bare(t.text)] : []));
40
+ }
41
+ function nameAt(words, i) {
42
+ let name = words[i] ?? "";
43
+ let j = i + 1;
44
+ while (words[j] === "." && words[j + 1] !== undefined) {
45
+ name += `.${words[j + 1]}`;
46
+ j += 2;
47
+ }
48
+ return { name, next: j };
49
+ }
50
+ const KNOWN_VERBS = new Set(["ADD", "DROP", "RENAME", "MODIFY", "COMMENT", "CLEAR", "MATERIALIZE", "UPDATE", "DELETE", "ATTACH", "DETACH", "FREEZE", "FETCH", "MOVE", "REPLACE", "RESET", "REMOVE", "APPLY", "UNFREEZE", "EXECUTE"]);
51
+ /** Normalise a key expression for comparing an old and a new one: no spaces, no outer parentheses. */
52
+ const keyText = (words) => {
53
+ let t = words.join("").toLowerCase();
54
+ while (t.startsWith("(") && t.endsWith(")"))
55
+ t = t.slice(1, -1);
56
+ return t;
57
+ };
58
+ function columnVerdict(kind, col, ctx) {
59
+ const plain = kind === "drop" ? "SQLCH202" : "SQLCH212";
60
+ const extra = kind === "drop" ? { destructive: true } : undefined;
61
+ if (ctx.created) {
62
+ return ctx.created.keys.has(bare(col)) ? rule("SQLCH213", extra) : rule(plain, extra);
63
+ }
64
+ return { undecided: [plain, "SQLCH213"], note: `${col} is a column change or, in a key, a rebuild; the file does not create the table, so its keys are not known offline` };
65
+ }
66
+ function tableAction(a, ctx) {
67
+ const u = a.map((w) => w.toUpperCase());
68
+ const first = u[0];
69
+ const second = u[1];
70
+ if (first === "ADD") {
71
+ if (second === "COLUMN")
72
+ return rule("SQLCH201");
73
+ if (second === "INDEX")
74
+ return rule("SQLCH204");
75
+ if (second === "PROJECTION")
76
+ return rule("SQLCH214");
77
+ if (second === "CONSTRAINT")
78
+ return rule("SQLCH215");
79
+ return { none: `ALTER TABLE ${u.slice(0, 2).join(" ")}` };
80
+ }
81
+ if (first === "DROP") {
82
+ if (second === "COLUMN") {
83
+ const k = u[2] === "IF" ? 4 : 2;
84
+ return columnVerdict("drop", a[k] ?? "column", ctx);
85
+ }
86
+ if (second === "INDEX")
87
+ return rule("SQLCH204");
88
+ if (second === "PROJECTION")
89
+ return rule("SQLCH214");
90
+ if (second === "CONSTRAINT")
91
+ return rule("SQLCH215");
92
+ return { none: `ALTER TABLE ${u.slice(0, 2).join(" ")} (removes data parts, no rule covers it)` };
93
+ }
94
+ if (first === "CLEAR") {
95
+ if (second === "INDEX")
96
+ return rule("SQLCH204");
97
+ if (second === "PROJECTION")
98
+ return rule("SQLCH214");
99
+ return { none: `ALTER TABLE CLEAR ${second ?? ""}`.trim() };
100
+ }
101
+ if (first === "RENAME" && second === "COLUMN") {
102
+ const k = u[2] === "IF" ? 4 : 2;
103
+ return columnVerdict("rename", a[k] ?? "column", ctx);
104
+ }
105
+ if (first === "COMMENT" && second === "COLUMN")
106
+ return rule("SQLCH203");
107
+ if (first === "MODIFY" || first === "RESET") {
108
+ if (second === "COLUMN") {
109
+ let k = 2;
110
+ if (u[k] === "IF" && u[k + 1] === "EXISTS")
111
+ k += 2;
112
+ const col = a[k] ?? "column";
113
+ const after = u[k + 1];
114
+ if (after === "REMOVE") {
115
+ const what = u[k + 2];
116
+ return what === "CODEC" ? rule("SQLCH208") : what === "COMMENT" ? rule("SQLCH203") : what === "TTL" ? rule("SQLCH205") : rule("SQLCH207");
117
+ }
118
+ if (after === "DEFAULT" || after === "MATERIALIZED" || after === "ALIAS" || after === "EPHEMERAL")
119
+ return rule("SQLCH207");
120
+ if (after === "CODEC")
121
+ return rule("SQLCH208");
122
+ if (after === "COMMENT")
123
+ return rule("SQLCH203");
124
+ if (after === "TTL")
125
+ return rule("SQLCH205");
126
+ if (after === "MODIFY" || after === "RESET")
127
+ return { none: "MODIFY COLUMN ... SETTING" };
128
+ if (after === undefined)
129
+ return { none: "MODIFY COLUMN with no change" };
130
+ if (ctx.created)
131
+ return ctx.created.keys.has(bare(col)) ? rule("SQLCH211") : rule("SQLCH210");
132
+ return { undecided: ["SQLCH210", "SQLCH211"], note: `a type change of ${col} is a background rewrite or, when ${col} is in a key, a rebuild; the file does not create the table, so its keys are not known offline` };
133
+ }
134
+ if (second === "ORDER" && u[2] === "BY") {
135
+ const next = a.slice(3);
136
+ if (ctx.created?.orderBy !== undefined) {
137
+ const was = ctx.created.orderBy;
138
+ const now = keyText(next);
139
+ const tail = now.startsWith(was) ? now.slice(was.length) : undefined;
140
+ const extra = tail === undefined ? [] : tail.split(/[^a-z0-9_]+/).filter(Boolean);
141
+ if (tail !== undefined && extra.length > 0 && extra.every((c) => ctx.added.has(c)))
142
+ return rule("SQLCH216");
143
+ return rule("SQLCH220");
144
+ }
145
+ const mentions = next.some((w) => ctx.added.has(bare(w)));
146
+ if (mentions)
147
+ return { undecided: ["SQLCH216", "SQLCH220"], note: "the new key appends the columns this ALTER adds only if its other elements are the old key's, which the file does not state" };
148
+ return rule("SQLCH220");
149
+ }
150
+ if (second === "SAMPLE" && u[2] === "BY")
151
+ return rule("SQLCH217");
152
+ if (second === "TTL")
153
+ return rule("SQLCH205");
154
+ if (second === "COMMENT")
155
+ return rule("SQLCH203");
156
+ if (second === "QUERY")
157
+ return rule("SQLCH241");
158
+ if (second === "REFRESH")
159
+ return rule("SQLCH244");
160
+ if (second === "SETTING" || second === "SETTINGS") {
161
+ const key = (a[2] ?? "").toLowerCase();
162
+ return settingRule(key);
163
+ }
164
+ return { none: `ALTER TABLE ${u.slice(0, 2).join(" ")}` };
165
+ }
166
+ if (first === "REMOVE") {
167
+ if (second === "TTL")
168
+ return rule("SQLCH205");
169
+ if (second === "SAMPLE" && u[2] === "BY")
170
+ return rule("SQLCH217");
171
+ return { none: `ALTER TABLE REMOVE ${second ?? ""}`.trim() };
172
+ }
173
+ if (first === "UPDATE" || first === "DELETE")
174
+ return { data: "a mutation: it rewrites the parts it touches in the background (system.mutations) and has no rollback" };
175
+ return { none: `ALTER TABLE ${u.slice(0, 2).join(" ")}` };
176
+ }
177
+ function settingRule(key) {
178
+ if (STREAM_SETTING.test(key))
179
+ return rule("SQLCH225", { note: `${key} belongs to a stream engine, which has no MODIFY SETTING` });
180
+ const spec = MERGE_TREE_SETTINGS[key];
181
+ return spec?.readonly ? rule("SQLCH218", { note: `${key} is fixed at creation` }) : rule("SQLCH206");
182
+ }
183
+ /** The `ALTER TABLE` actions of one statement, in order, a `MODIFY SETTING` list kept together. */
184
+ function alterTable(words, u, created) {
185
+ let i = 2;
186
+ if (u[i] === "IF" && u[i + 1] === "EXISTS")
187
+ i += 2;
188
+ const { name, next } = nameAt(words, i);
189
+ i = next;
190
+ if (u[i] === "ON" && u[i + 1] === "CLUSTER")
191
+ i += 3;
192
+ const actions = splitTopLevel(words.slice(i));
193
+ const added = new Set();
194
+ for (const a of actions) {
195
+ const au = a.map((w) => w.toUpperCase());
196
+ if (au[0] === "ADD" && au[1] === "COLUMN") {
197
+ const k = au[2] === "IF" ? 5 : 2;
198
+ if (a[k])
199
+ added.add(bare(a[k]));
200
+ }
201
+ }
202
+ const ctx = { added, ...(created.has(bare(name)) ? { created: created.get(bare(name)) } : {}) };
203
+ const out = [];
204
+ let inSettings = false;
205
+ for (const a of actions) {
206
+ const au = a.map((w) => w.toUpperCase());
207
+ if (inSettings && au[0] !== undefined && !KNOWN_VERBS.has(au[0])) {
208
+ out.push(settingRule((a[0] ?? "").toLowerCase()));
209
+ continue;
210
+ }
211
+ inSettings = (au[0] === "MODIFY" || au[0] === "RESET") && (au[1] === "SETTING" || au[1] === "SETTINGS");
212
+ out.push(tableAction(a, ctx));
213
+ }
214
+ return out;
215
+ }
216
+ const OBJECTS = { USER: "SQLCH271", ROLE: "SQLCH270", QUOTA: "SQLCH275" };
217
+ function alterOther(u) {
218
+ const obj = u[1];
219
+ if (obj && OBJECTS[obj])
220
+ return rule(OBJECTS[obj]);
221
+ if (obj === "ROW" && u[2] === "POLICY")
222
+ return rule("SQLCH272");
223
+ if (obj === "POLICY")
224
+ return rule("SQLCH272");
225
+ if (obj === "SETTINGS" && u[2] === "PROFILE")
226
+ return rule("SQLCH276");
227
+ if (obj === "DATABASE" && u.includes("COMMENT"))
228
+ return rule("SQLCH203");
229
+ return { none: `ALTER ${obj ?? ""}`.trim() };
230
+ }
231
+ const DATA_WORDS = new Set(["INSERT", "SELECT", "WITH", "SYSTEM", "OPTIMIZE", "TRUNCATE", "SET", "USE", "EXPLAIN", "EXISTS", "DESCRIBE", "DESC", "SHOW", "CHECK", "KILL", "WATCH"]);
232
+ /** A `CREATE` or `ATTACH`: the parser reads tables, views, dictionaries, databases and functions; the rest go by key words. */
233
+ function create(tokens, u, created) {
234
+ let i = 1;
235
+ const orReplace = u[i] === "OR" && u[i + 1] === "REPLACE";
236
+ if (orReplace)
237
+ i += 2;
238
+ let node;
239
+ try {
240
+ node = parseCreate(tokens);
241
+ }
242
+ catch {
243
+ node = undefined;
244
+ }
245
+ if (node?.statement === "table") {
246
+ const name = text(tokens, node.name).replace(/\s*\.\s*/g, ".");
247
+ const keys = new Set([...columnsOf(tokens, node.orderBy), ...columnsOf(tokens, node.primaryKey), ...columnsOf(tokens, node.partitionBy), ...columnsOf(tokens, node.sampleBy)]);
248
+ for (const c of node.columns)
249
+ if (c.primaryKey)
250
+ keys.add(bare(c.name));
251
+ created.set(bare(name), { keys, ...(node.orderBy ? { orderBy: keyText(text(tokens, node.orderBy).split(" ")) } : {}) });
252
+ }
253
+ if (orReplace && u[i] === "FUNCTION")
254
+ return rule("SQLCH260");
255
+ if (orReplace && (u[i] === "TABLE" || node?.statement === "table"))
256
+ return rule("SQLCH200", { note: "OR REPLACE drops an existing table of that name, and its rows, before it creates this one" });
257
+ return rule("SQLCH200");
258
+ }
259
+ function read(sql, created) {
260
+ let tokens;
261
+ try {
262
+ tokens = tokenizeText(sql, 0);
263
+ }
264
+ catch {
265
+ return [{ none: "text the tokenizer could not read" }];
266
+ }
267
+ const words = tokens.filter((t) => !isTrivia(t) && t.text !== ";").map((t) => t.text);
268
+ const u = words.map((w) => w.toUpperCase());
269
+ const head = u[0];
270
+ if (head === undefined)
271
+ return [{ data: "an empty statement" }];
272
+ if (head === "CREATE")
273
+ return [create(tokens, u, created)];
274
+ if (head === "ALTER" && u[1] === "TABLE")
275
+ return alterTable(words, u, created);
276
+ if (head === "ALTER")
277
+ return [alterOther(u)];
278
+ if (head === "DROP")
279
+ return [rule("SQLCH250", { destructive: true })];
280
+ if (head === "RENAME") {
281
+ if (u[1] === "DATABASE")
282
+ return [rule("SQLCH231")];
283
+ if (u[1] === "TABLE" || u[1] === "DICTIONARY")
284
+ return [rule("SQLCH230")];
285
+ return [{ none: `RENAME ${u[1] ?? ""}`.trim() }];
286
+ }
287
+ if (head === "GRANT")
288
+ return [rule("SQLCH273")];
289
+ if (head === "REVOKE")
290
+ return [rule("SQLCH274")];
291
+ if (head === "EXCHANGE")
292
+ return [{ none: "EXCHANGE TABLES swaps two tables; no rule covers it" }];
293
+ if (head === "TRUNCATE")
294
+ return [{ data: "TRUNCATE removes every row of the table" }];
295
+ if (DATA_WORDS.has(head))
296
+ return [{ data: `${head} reads or writes rows or the session, not the schema` }];
297
+ return [{ none: `${head} is not a statement the classifier reads` }];
298
+ }
299
+ /**
300
+ * Classify a migration's up statements, in order. The statements are the
301
+ * file's own text, one per entry.
302
+ */
303
+ export function classifyClickHouseStatements(statements) {
304
+ const created = new Map();
305
+ return statements.map((sql, k) => {
306
+ const readings = read(sql, created);
307
+ const out = { index: k + 1, sql: sqlHead(sql), outcome: "classified", changes: [] };
308
+ const none = [];
309
+ const notes = [];
310
+ for (const r of readings) {
311
+ if ("rules" in r)
312
+ out.changes.push(...r.rules.map((x) => classified(x.id, x.extra)));
313
+ else if ("undecided" in r) {
314
+ out.candidates = [...(out.candidates ?? []), ...r.undecided.map((id) => classified(id))];
315
+ notes.push(r.note);
316
+ }
317
+ else if ("none" in r)
318
+ none.push(r.none);
319
+ else
320
+ notes.push(r.data);
321
+ }
322
+ if (out.candidates)
323
+ out.outcome = "undecided";
324
+ else if (out.changes.length === 0 && none.length === 0)
325
+ out.outcome = "data";
326
+ else if (none.length > 0) {
327
+ out.outcome = out.changes.length > 0 ? "classified" : "unclassified";
328
+ notes.push(`no rule covers ${none.join("; ")}`);
329
+ }
330
+ if (notes.length > 0)
331
+ out.note = notes.join("; ");
332
+ return out;
333
+ });
334
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"classifier-page.d.ts","sourceRoot":"","sources":["../../src/codegen/classifier-page.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH,wBAAgB,oBAAoB,IAAI,MAAM,CAiG7C"}
1
+ {"version":3,"file":"classifier-page.d.ts","sourceRoot":"","sources":["../../src/codegen/classifier-page.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH,wBAAgB,oBAAoB,IAAI,MAAM,CAwH7C"}
@@ -87,6 +87,29 @@ A rebuild is never DDL: it is a step naming \`ClickHouseRebuildOp\`, or for a vi
87
87
 
88
88
  \`--topology\` renders the statements for where they will run: \`single\` (the default), \`cluster:<name>\`, \`replicated\` or \`cloud\`. On a cluster every statement carries \`ON CLUSTER\` and a MergeTree-family table becomes \`Replicated*MergeTree\` with its Keeper path; in a \`Replicated\` database the tables are \`Replicated*MergeTree\` with no path and no \`ON CLUSTER\`. \`diffStatements(before, after, { topology })\` takes the same choice; see [Applying to a Server](../applying/#topology).
89
89
 
90
+ ## Migration files on a pull request
91
+
92
+ A workspace member that is a goose, golang-migrate, clickhouse-migrations or Flyway directory (see [Workspace kinds](/chant/reference/workspace-kinds/)) has no declaration to diff. On a pull request, \`chant components pr-plan\` lists each migration file the pull request adds to such a member, and the plan note gives every statement in it the class the rules below give the same change. This is the "check" level of adopting chant beside an existing migration tool: it reads files, applies nothing and contacts no server, so it needs no \`--env\`.
93
+
94
+ The up statements are the ones \`chant sql replay\` would run: goose's \`-- +goose Up\` section, the whole \`.up.sql\` file for golang-migrate, the whole file for clickhouse-migrations, and a Flyway file after placeholder substitution with the values in \`flyway.toml\` or \`flyway.conf\`. A file the base commit already has is not new, whatever the pull request does to it. Undo files are left out.
95
+
96
+ \`\`\`text
97
+ \`db/00002_orders.sql\` (member \`ledger\`, postgres)
98
+ - 1. \`CREATE INDEX orders_status_idx ON orders (status)\`: needs CONCURRENTLY (SQLPG241, SHARE)
99
+ - 2. \`ALTER TABLE orders ALTER COLUMN a TYPE bigint\`: undecided between SQLPG206 metadata only and SQLPG207 ACCESS EXCLUSIVE rewrite or scan
100
+ \`\`\`
101
+
102
+ Each statement is read with the dialect's parser (\`CREATE\`, \`GRANT\`, \`COMMENT ON\`) or word by word (\`ALTER\`, \`DROP\`, \`RENAME\`), and the lock on a Postgres statement is the one its rule's text names. The code is \`lexicons/sql/src/postgres/plan/statements.ts\` and \`lexicons/sql/src/clickhouse/plan/statements.ts\`.
103
+
104
+ A statement alone cannot always decide a rule, and then the note names the candidates and does not guess:
105
+
106
+ - \`ALTER COLUMN ... TYPE\` is SQLPG206 or SQLPG207 by the column's old type, and SQLPG208 as well with a \`USING\` expression.
107
+ - \`MODIFY COLUMN <type>\` is SQLCH210 or SQLCH211, \`DROP COLUMN\` SQLCH202 or SQLCH213, and \`RENAME COLUMN\` SQLCH212 or SQLCH213, by whether the column is in a key. When the same file creates the table, its keys are known and the statement is classified.
108
+ - \`MODIFY ORDER BY\` is SQLCH220, a rebuild. It is SQLCH216 or SQLCH220 when the same \`ALTER\` adds the columns the new key ends with, unless the file created the table and shows the old key is kept.
109
+ - \`CREATE OR REPLACE VIEW\` is SQLPG250 or SQLPG251 by whether the view keeps its columns.
110
+
111
+ A statement that reads or writes rows (\`INSERT\`, \`UPDATE\`, \`TRUNCATE\`) is listed as data. A schema change no rule covers, such as \`ALTER TABLE ... OWNER TO\` or a ClickHouse \`DROP PARTITION\`, is listed as having no class.
112
+
90
113
  ## Identity and renames
91
114
 
92
115
  An object is identified by its export name between two builds, so a changed name in the SQL under the same export is a rename. Against a server, which has no export names, an object is identified by \`database.name\`; a declaration renamed since the server last saw it says so with \`-- previously: <old name>\` before its CREATE.
@@ -60,14 +60,14 @@ A column rename or a type change across kinds is never DDL: it is a step naming
60
60
 
61
61
  ## Which major
62
62
 
63
- Two rules change class across the supported majors: a STORED generated column's expression (SQLPG212) is a rewrite from 17 and expand and contract before it, and a table's access method (SQLPG226) is a rewrite from 15 and expand and contract before it.
63
+ Three rules change class across the supported majors: a STORED generated column's expression (SQLPG212) is a rewrite from 17 and expand and contract before it, a table's access method (SQLPG226) is a rewrite from 15 and expand and contract before it, and an operator's commutator, negator, HASHES and MERGES (SQLPG304) are altered in place from 17 and dropped and created before it.
64
64
 
65
65
  - \`chant sql diff\` classifies for the major the builds recorded (\`postgresMajor\` in the build output): the newer build's, else the older build's, else \`sql.postgresMajor\`, else 18. A diff between two revisions does not depend on today's config.
66
66
  - \`chant sql plan\` classifies for the major the server runs (\`server_version_num\`), since the server is what takes the locks. When the build targets another major, the plan says so in a hint.
67
67
 
68
68
  ## Identity and renames
69
69
 
70
- An object is identified by its export name between two builds, and by its schema-qualified name against a server, which has no export names. Tables, views, materialized views, sequences and indexes share one namespace per schema, types and domains another, and schemas and extensions are matched by name. Functions, procedures and aggregates share a namespace of their own and are told apart by their input parameter types, so an overload is another object; composite and range types share the types' namespace, collations have one of their own, extended statistics another, an operator by its symbol and operand types, a cast by its two types and an event trigger, a foreign data wrapper, a foreign server and a publication by their names in the database; a trigger is matched by its name on its table.
70
+ An object is identified by its export name between two builds, and by its schema-qualified name against a server, which has no export names. Tables, foreign tables, views, materialized views, sequences and indexes share one namespace per schema, types and domains another, and schemas and extensions are matched by name. Functions, procedures and aggregates share a namespace of their own and are told apart by their input parameter types, so an overload is another object; composite and range types share the types' namespace, collations have one of their own, extended statistics another, an operator by its symbol and operand types, an operator class and an operator family each by its name and index method, text search configurations one namespace and dictionaries another, a cast by its two types and an event trigger, a foreign data wrapper, a foreign server and a publication by their names in the database; a trigger is matched by its name on its table.
71
71
 
72
72
  A rename is declared where it happens. Before a CREATE, \`-- previously: <old name>\` says the object had another name; on a column's line, it says the column did:
73
73
 
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/codegen/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA+CH,wBAAgB,aAAa,IAAI,MAAM,CAKtC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/codegen/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoDH,wBAAgB,aAAa,IAAI,MAAM,CAKtC"}
@@ -38,6 +38,11 @@ const KINDS = {
38
38
  PostgresCast: POSTGRES_ENTITY_TYPES.cast,
39
39
  PostgresEventTrigger: POSTGRES_ENTITY_TYPES.eventTrigger,
40
40
  PostgresOperator: POSTGRES_ENTITY_TYPES.operator,
41
+ PostgresOperatorClass: POSTGRES_ENTITY_TYPES.operatorClass,
42
+ PostgresOperatorFamily: POSTGRES_ENTITY_TYPES.operatorFamily,
43
+ PostgresForeignTable: POSTGRES_ENTITY_TYPES.foreignTable,
44
+ PostgresTextSearchConfiguration: POSTGRES_ENTITY_TYPES.textSearchConfiguration,
45
+ PostgresTextSearchDictionary: POSTGRES_ENTITY_TYPES.textSearchDictionary,
41
46
  PostgresStatistics: POSTGRES_ENTITY_TYPES.statistics,
42
47
  PostgresForeignDataWrapper: POSTGRES_ENTITY_TYPES.foreignDataWrapper,
43
48
  PostgresForeignServer: POSTGRES_ENTITY_TYPES.foreignServer,
@@ -0,0 +1,55 @@
1
+ /**
2
+ * What the statement classifiers (`../clickhouse/plan/statements.ts`,
3
+ * `../postgres/plan/statements.ts`) answer, in one shape for both dialects
4
+ * (#4407). They read a migration file's statements offline and give each the
5
+ * class the schema-diff classifier gives the same change, citing its rule.
6
+ * Nothing runs against a server.
7
+ */
8
+ import type { ClassifierRule } from "./classifier.js";
9
+ /** One change a statement makes, under one rule. */
10
+ export interface StatementClassified {
11
+ rule: string;
12
+ /** The rule's class, e.g. `rebuild`, `concurrently`. */
13
+ class: string;
14
+ /** The class as a report names it, e.g. `REBUILD`. */
15
+ label: string;
16
+ title: string;
17
+ /** Postgres: the lock the rule's restriction names, e.g. `SHARE`. */
18
+ lock?: string;
19
+ /** Data is removed and not recoverable. */
20
+ destructive?: boolean;
21
+ note?: string;
22
+ }
23
+ /**
24
+ * - `classified`: every change has a rule.
25
+ * - `undecided`: the statement alone cannot say which of `candidates` applies
26
+ * (a column type change is a rewrite, or a rebuild when the column is in a
27
+ * key); the answer `classifyDisruption()` calls `unknown`.
28
+ * - `unclassified`: a schema change no rule covers; `note` says what.
29
+ * - `data`: the statement reads or writes rows or the session, not the schema.
30
+ */
31
+ export type StatementOutcome = "classified" | "undecided" | "unclassified" | "data";
32
+ export interface StatementVerdict {
33
+ /** 1-based position among the file's up statements. */
34
+ index: number;
35
+ /** The statement's first line, cut to 120 characters. */
36
+ sql: string;
37
+ outcome: StatementOutcome;
38
+ /** `classified`: one entry per change in the statement (an `ALTER TABLE` can hold several). */
39
+ changes: StatementClassified[];
40
+ /** `undecided`: the rules that could apply, in the order a reader should weigh them. */
41
+ candidates?: StatementClassified[];
42
+ note?: string;
43
+ }
44
+ /** A statement's own text cut for a note: the first line, at most 120 characters. */
45
+ export declare function sqlHead(sql: string): string;
46
+ /** The pieces of a classified change from a dialect's rule table. */
47
+ export declare function classifiedOf<C extends string>(rules: Readonly<Record<string, ClassifierRule<C>>>, labels: Readonly<Record<C, string>>, id: string, extra?: Partial<StatementClassified>): StatementClassified;
48
+ /** The first lock a rule's restriction names, or undefined when it names none. */
49
+ export declare const lockOf: (restriction: string) => string | undefined;
50
+ /**
51
+ * Split a statement's tokens' text into the pieces between top-level commas
52
+ * (an `ALTER TABLE`'s actions). Parentheses and brackets nest.
53
+ */
54
+ export declare function splitTopLevel(words: readonly string[]): string[][];
55
+ //# sourceMappingURL=statement-class.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statement-class.d.ts","sourceRoot":"","sources":["../../src/core/statement-class.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,MAAM,CAAC;AAEpF,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,gBAAgB,CAAC;IAC1B,+FAA+F;IAC/F,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,wFAAwF;IACxF,UAAU,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qFAAqF;AACrF,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI3C;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,EAClD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EACnC,EAAE,EAAE,MAAM,EACV,KAAK,GAAE,OAAO,CAAC,mBAAmB,CAAM,GACvC,mBAAmB,CAIrB;AAKD,kFAAkF;AAClF,eAAO,MAAM,MAAM,GAAI,aAAa,MAAM,KAAG,MAAM,GAAG,SAAwC,CAAC;AAE/F;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,EAAE,CAUlE"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * What the statement classifiers (`../clickhouse/plan/statements.ts`,
3
+ * `../postgres/plan/statements.ts`) answer, in one shape for both dialects
4
+ * (#4407). They read a migration file's statements offline and give each the
5
+ * class the schema-diff classifier gives the same change, citing its rule.
6
+ * Nothing runs against a server.
7
+ */
8
+ /** A statement's own text cut for a note: the first line, at most 120 characters. */
9
+ export function sqlHead(sql) {
10
+ const line = sql.split(/\r?\n/).find((l) => l.trim() !== "" && !l.trim().startsWith("--")) ?? sql;
11
+ const t = line.trim().replace(/\s+/g, " ");
12
+ return t.length > 120 ? `${t.slice(0, 117)}...` : t;
13
+ }
14
+ /** The pieces of a classified change from a dialect's rule table. */
15
+ export function classifiedOf(rules, labels, id, extra = {}) {
16
+ const r = rules[id];
17
+ if (!r)
18
+ throw new Error(`no classifier rule ${id}`);
19
+ return { rule: id, class: r.class, label: labels[r.class], title: r.title, ...extra };
20
+ }
21
+ /** Lock names, longest first so `SHARE UPDATE EXCLUSIVE` is not read as `SHARE`. */
22
+ const LOCK = /\b(ACCESS EXCLUSIVE|SHARE UPDATE EXCLUSIVE|SHARE ROW EXCLUSIVE|ROW EXCLUSIVE|ROW SHARE|EXCLUSIVE|SHARE)\b/;
23
+ /** The first lock a rule's restriction names, or undefined when it names none. */
24
+ export const lockOf = (restriction) => LOCK.exec(restriction)?.[1];
25
+ /**
26
+ * Split a statement's tokens' text into the pieces between top-level commas
27
+ * (an `ALTER TABLE`'s actions). Parentheses and brackets nest.
28
+ */
29
+ export function splitTopLevel(words) {
30
+ const out = [[]];
31
+ let depth = 0;
32
+ for (const w of words) {
33
+ if (w === "(" || w === "[")
34
+ depth++;
35
+ else if (w === ")" || w === "]")
36
+ depth--;
37
+ if (w === "," && depth === 0)
38
+ out.push([]);
39
+ else
40
+ out[out.length - 1].push(w);
41
+ }
42
+ return out.filter((a) => a.length > 0);
43
+ }
@@ -11,16 +11,16 @@
11
11
  { "command": "CREATE EVENT TRIGGER", "kind": "event trigger", "status": "declared", "entity": "Postgres::EventTrigger", "note": "the database's, not a schema's; creating one takes a superuser" },
12
12
  { "command": "CREATE EXTENSION", "kind": "extension", "status": "declared", "entity": "Postgres::Extension" },
13
13
  { "command": "CREATE FOREIGN DATA WRAPPER", "kind": "foreign data wrapper", "status": "declared", "entity": "Postgres::ForeignDataWrapper", "note": "the database's, not a schema's; creating one takes a superuser; one an extension installs comes with the extension; an option that holds a credential is refused" },
14
- { "command": "CREATE FOREIGN TABLE", "kind": "foreign table", "status": "refused", "reason": "no tag declares a foreign table yet" },
14
+ { "command": "CREATE FOREIGN TABLE", "kind": "foreign table", "status": "declared", "entity": "Postgres::ForeignTable", "note": "its own tag, not table: another CREATE and ALTER, no key, index or storage clauses; its server cannot be altered; one that is a partition, inherits or has a generated column is not declared; an option that holds a credential is refused" },
15
15
  { "command": "CREATE FUNCTION", "kind": "function", "status": "declared", "entity": "Postgres::Function" },
16
16
  { "command": "CREATE FUNCTION", "form": "RETURN or BEGIN ATOMIC body", "match": "\\b(?:RETURN|BEGIN\\s+ATOMIC)\\b", "kind": "routine with a SQL-standard body", "status": "refused", "reason": "the server stores a SQL-standard body parsed and prints it back rewritten, so a declaration cannot be compared with it; declare the routine with a string body (AS $$ ... $$)" },
17
17
  { "command": "CREATE GROUP", "kind": "group", "status": "declared", "entity": "Postgres::Role", "note": "an alias of CREATE ROLE" },
18
18
  { "command": "CREATE INDEX", "kind": "index", "status": "declared", "entity": "Postgres::Index" },
19
19
  { "command": "CREATE LANGUAGE", "kind": "procedural language", "status": "refused", "reason": "no tag declares a procedural language; an extension installs one, and the extension is declared instead" },
20
20
  { "command": "CREATE MATERIALIZED VIEW", "kind": "materialized view", "status": "declared", "entity": "Postgres::MaterializedView" },
21
- { "command": "CREATE OPERATOR", "kind": "operator", "status": "declared", "entity": "Postgres::Operator", "note": "identified by its name and operand types; a change is a drop and a create" },
22
- { "command": "CREATE OPERATOR CLASS", "kind": "operator class", "status": "refused", "reason": "no tag declares an operator class yet" },
23
- { "command": "CREATE OPERATOR FAMILY", "kind": "operator family", "status": "refused", "reason": "no tag declares an operator family yet" },
21
+ { "command": "CREATE OPERATOR", "kind": "operator", "status": "declared", "entity": "Postgres::Operator", "note": "identified by its name and operand types; RESTRICT and JOIN change in place, and from 17 so does setting a commutator or negator or turning on HASHES or MERGES; anything else is a drop and a create" },
22
+ { "command": "CREATE OPERATOR CLASS", "kind": "operator class", "status": "declared", "entity": "Postgres::OperatorClass", "note": "identified by its name and index method; a change is a drop and a create, since ALTER OPERATOR CLASS only renames or moves it; its own family, named as the class, comes with it" },
23
+ { "command": "CREATE OPERATOR FAMILY", "kind": "operator family", "status": "declared", "entity": "Postgres::OperatorFamily", "note": "identified by its name and index method; its members come with the classes that name it, and loose members added with ALTER OPERATOR FAMILY are not declared" },
24
24
  { "command": "CREATE POLICY", "kind": "policy", "status": "declared", "entity": "Postgres::Policy", "note": "read where the profile manages access (sql.profiles.<env>.access)" },
25
25
  { "command": "CREATE PROCEDURE", "kind": "procedure", "status": "declared", "entity": "Postgres::Procedure" },
26
26
  { "command": "CREATE PUBLICATION", "kind": "publication", "status": "declared", "entity": "Postgres::Publication", "note": "the database's, not a schema's; a table of it may carry a column list and row filter from 15" },
@@ -34,8 +34,8 @@
34
34
  { "command": "CREATE TABLE", "kind": "table", "status": "declared", "entity": "Postgres::Table" },
35
35
  { "command": "CREATE TABLE AS", "kind": "table from a query", "status": "declared", "entity": "Postgres::Table", "note": "the server keeps no query, so the table it made reads as a CREATE TABLE" },
36
36
  { "command": "CREATE TABLESPACE", "kind": "tablespace", "status": "out-of-scope", "reason": "a tablespace is the cluster's, a directory on the server; a table's TABLESPACE clause is kept" },
37
- { "command": "CREATE TEXT SEARCH CONFIGURATION", "kind": "text search configuration", "status": "refused", "reason": "no tag declares a text search configuration yet" },
38
- { "command": "CREATE TEXT SEARCH DICTIONARY", "kind": "text search dictionary", "status": "refused", "reason": "no tag declares a text search dictionary yet" },
37
+ { "command": "CREATE TEXT SEARCH CONFIGURATION", "kind": "text search configuration", "status": "declared", "entity": "Postgres::TextSearchConfiguration", "note": "PARSER and its ADD MAPPING statements; COPY is refused, since it copies mappings chant does not read; mappings change in place, the parser by drop and create" },
38
+ { "command": "CREATE TEXT SEARCH DICTIONARY", "kind": "text search dictionary", "status": "declared", "entity": "Postgres::TextSearchDictionary", "note": "a template and its options; options change in place, the template by drop and create" },
39
39
  { "command": "CREATE TEXT SEARCH PARSER", "kind": "text search parser", "status": "refused", "reason": "no tag declares a text search parser; one is C code an extension installs" },
40
40
  { "command": "CREATE TEXT SEARCH TEMPLATE", "kind": "text search template", "status": "refused", "reason": "no tag declares a text search template; one is C code an extension installs" },
41
41
  { "command": "CREATE TRANSFORM", "kind": "transform", "status": "refused", "reason": "no tag declares a transform; an extension installs one" },
@@ -1 +1 @@
1
- {"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAU9D,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAE5C,MAAM,WAAW,oBAAoB;IACnC,yGAAyG;IACzG,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,kHAAkH;IAClH,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClE,2EAA2E;IAC3E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E;AAED,+CAA+C;AAC/C,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AAsSjD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,IAAI,GAAE,oBAAyB,GAAG,aAAa,CAyUvH"}
1
+ {"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAU9D,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAE5C,MAAM,WAAW,oBAAoB;IACnC,yGAAyG;IACzG,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,kHAAkH;IAClH,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClE,2EAA2E;IAC3E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E;AAED,+CAA+C;AAC/C,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AA2SjD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,IAAI,GAAE,oBAAyB,GAAG,aAAa,CAyUvH"}
package/dist/drizzle.js CHANGED
@@ -59,6 +59,11 @@ const SKIP_REASONS = {
59
59
  "Postgres::Collation": "Drizzle has no collation",
60
60
  "Postgres::Cast": "Drizzle has no cast",
61
61
  "Postgres::Operator": "Drizzle has no operator",
62
+ "Postgres::OperatorClass": "Drizzle has no operator class",
63
+ "Postgres::OperatorFamily": "Drizzle has no operator family",
64
+ "Postgres::ForeignTable": "Drizzle has no foreign table",
65
+ "Postgres::TextSearchConfiguration": "Drizzle has no text search configuration",
66
+ "Postgres::TextSearchDictionary": "Drizzle has no text search dictionary",
62
67
  "Postgres::Statistics": "Drizzle has no extended statistics",
63
68
  "Postgres::ForeignDataWrapper": "Drizzle has no foreign data wrapper",
64
69
  "Postgres::ForeignServer": "Drizzle has no foreign server",
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/files/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAM9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAA8F,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvJ,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC;AAE/E,4CAA4C;AAC5C,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAGzD;AAED,0GAA0G;AAC1G,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,oBAAoB,CAE9E;AAKD,8EAA8E;AAC9E,8EAA8E;AAC9E,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,WAAqF,CAAC;AAsB5H;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,gBAAgB,GAAG,QAAQ,CAAC,CAgJtH"}
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/files/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAM9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAA8F,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvJ,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC;AAE/E,4CAA4C;AAC5C,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAGzD;AAED,0GAA0G;AAC1G,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,oBAAoB,CAE9E;AAKD,8EAA8E;AAC9E,8EAA8E;AAC9E,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,WAAqF,CAAC;AAsB5H;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,gBAAgB,GAAG,QAAQ,CAAC,CAoJtH"}