@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.
- package/dist/clickhouse/plan/statements.d.ts +29 -0
- package/dist/clickhouse/plan/statements.d.ts.map +1 -0
- package/dist/clickhouse/plan/statements.js +334 -0
- package/dist/codegen/classifier-page.d.ts.map +1 -1
- package/dist/codegen/classifier-page.js +23 -0
- package/dist/codegen/postgres-classifier-page.js +2 -2
- package/dist/codegen/registry.d.ts.map +1 -1
- package/dist/codegen/registry.js +5 -0
- package/dist/core/statement-class.d.ts +55 -0
- package/dist/core/statement-class.d.ts.map +1 -0
- package/dist/core/statement-class.js +43 -0
- package/dist/coverage/postgres.json +6 -6
- package/dist/drizzle.d.ts.map +1 -1
- package/dist/drizzle.js +5 -0
- package/dist/files/postgres.d.ts.map +1 -1
- package/dist/files/postgres.js +23 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/integrity.json +5 -5
- package/dist/lint/post-synth/postgres-names.d.ts.map +1 -1
- package/dist/lint/post-synth/postgres-names.js +3 -0
- package/dist/lint/rules/postgres-templates.js +2 -2
- package/dist/lsp/postgres-template.d.ts.map +1 -1
- package/dist/lsp/postgres-template.js +10 -0
- package/dist/manifest.json +1 -1
- package/dist/mcp/postgres.js +1 -1
- package/dist/meta.json +25 -0
- package/dist/migration-classify.d.ts +23 -0
- package/dist/migration-classify.d.ts.map +1 -0
- package/dist/migration-classify.js +44 -0
- package/dist/okf/index.md +5 -0
- package/dist/okf/types/PostgresForeignTable.md +9 -0
- package/dist/okf/types/PostgresOperatorClass.md +9 -0
- package/dist/okf/types/PostgresOperatorFamily.md +9 -0
- package/dist/okf/types/PostgresTextSearchConfiguration.md +9 -0
- package/dist/okf/types/PostgresTextSearchDictionary.md +9 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +13 -0
- package/dist/postgres/access/acl.js +1 -1
- package/dist/postgres/access/import.js +1 -1
- package/dist/postgres/apply/apply.js +5 -0
- package/dist/postgres/apply/statements.d.ts.map +1 -1
- package/dist/postgres/apply/statements.js +156 -2
- package/dist/postgres/entities.d.ts +120 -2
- package/dist/postgres/entities.d.ts.map +1 -1
- package/dist/postgres/entities.js +180 -5
- package/dist/postgres/entity-types.d.ts +5 -0
- package/dist/postgres/entity-types.d.ts.map +1 -1
- package/dist/postgres/entity-types.js +5 -0
- package/dist/postgres/import/generator.d.ts.map +1 -1
- package/dist/postgres/import/generator.js +16 -1
- package/dist/postgres/live/catalog-kinds.d.ts +6 -0
- package/dist/postgres/live/catalog-kinds.d.ts.map +1 -1
- package/dist/postgres/live/catalog-kinds.js +246 -0
- package/dist/postgres/live/describe-resources.d.ts.map +1 -1
- package/dist/postgres/live/describe-resources.js +2 -0
- package/dist/postgres/migrate/observe.d.ts.map +1 -1
- package/dist/postgres/migrate/observe.js +4 -1
- package/dist/postgres/migrate/replication.d.ts +42 -0
- package/dist/postgres/migrate/replication.d.ts.map +1 -1
- package/dist/postgres/migrate/replication.js +53 -0
- package/dist/postgres/parser.d.ts +73 -1
- package/dist/postgres/parser.d.ts.map +1 -1
- package/dist/postgres/parser.js +181 -7
- package/dist/postgres/plan/deep.d.ts.map +1 -1
- package/dist/postgres/plan/deep.js +15 -4
- package/dist/postgres/plan/diff.d.ts.map +1 -1
- package/dist/postgres/plan/diff.js +120 -5
- package/dist/postgres/plan/normalize.d.ts +12 -1
- package/dist/postgres/plan/normalize.d.ts.map +1 -1
- package/dist/postgres/plan/normalize.js +151 -0
- package/dist/postgres/plan/rules.d.ts +3 -0
- package/dist/postgres/plan/rules.d.ts.map +1 -1
- package/dist/postgres/plan/rules.js +5 -2
- package/dist/postgres/plan/schema.js +1 -1
- package/dist/postgres/plan/statements.d.ts +26 -0
- package/dist/postgres/plan/statements.d.ts.map +1 -0
- package/dist/postgres/plan/statements.js +364 -0
- package/dist/postgres/testing/roundtrip-schema.d.ts +3 -2
- package/dist/postgres/testing/roundtrip-schema.d.ts.map +1 -1
- package/dist/postgres/testing/roundtrip-schema.js +12 -1
- package/dist/postgres.d.ts +1 -1
- package/dist/postgres.d.ts.map +1 -1
- package/dist/postgres.js +1 -1
- package/dist/rules/postgres-names.ts +2 -0
- package/dist/rules/postgres-templates.ts +2 -2
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +5 -0
- package/package.json +2 -2
- package/src/clickhouse/plan/statements.test.ts +61 -0
- package/src/clickhouse/plan/statements.ts +306 -0
- package/src/codegen/classifier-page.ts +23 -0
- package/src/codegen/postgres-classifier-page.ts +2 -2
- package/src/codegen/registry.ts +5 -0
- package/src/core/statement-class.ts +88 -0
- package/src/coverage/postgres.json +6 -6
- package/src/drizzle.ts +5 -0
- package/src/files/postgres.ts +22 -1
- package/src/generated/lexicon-sql.json +25 -0
- package/src/index.ts +1 -1
- package/src/lint/post-synth/postgres-checks.test.ts +8 -1
- package/src/lint/post-synth/postgres-names.ts +2 -0
- package/src/lint/rules/postgres-templates.ts +2 -2
- package/src/lsp/postgres-template.ts +9 -0
- package/src/mcp/postgres.ts +1 -1
- package/src/migration-classify.test.ts +150 -0
- package/src/migration-classify.ts +45 -0
- package/src/op/watch-pipeline.test.ts +8 -1
- package/src/plugin.ts +14 -0
- package/src/postgres/access/acl.ts +1 -1
- package/src/postgres/access/import.ts +1 -1
- package/src/postgres/apply/apply.ts +5 -0
- package/src/postgres/apply/statements.ts +151 -3
- package/src/postgres/database-kinds.test.ts +498 -17
- package/src/postgres/entities.ts +276 -5
- package/src/postgres/entity-types.ts +5 -0
- package/src/postgres/import/generator.ts +13 -1
- package/src/postgres/live/catalog-kinds.ts +271 -0
- package/src/postgres/live/describe-resources.ts +2 -0
- package/src/postgres/migrate/observe.ts +3 -1
- package/src/postgres/migrate/replication.ts +66 -0
- package/src/postgres/migrate/subscriber.e2e.test.ts +141 -0
- package/src/postgres/migrate/subscriber.test.ts +59 -0
- package/src/postgres/parser.ts +225 -6
- package/src/postgres/plan/deep.ts +15 -4
- package/src/postgres/plan/diff.ts +107 -6
- package/src/postgres/plan/normalize.ts +155 -0
- package/src/postgres/plan/rules.ts +25 -4
- package/src/postgres/plan/schema.ts +1 -1
- package/src/postgres/plan/statements.test.ts +78 -0
- package/src/postgres/plan/statements.ts +299 -0
- package/src/postgres/testing/roundtrip-schema.ts +12 -1
- package/src/postgres.ts +17 -0
- package/src/skills/chant-sql-postgres-migration.md +1 -0
- package/src/skills/chant-sql-postgres.md +5 -0
- package/src/sql-files.test.ts +33 -0
- package/src/validate.ts +5 -0
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* The Postgres entities: `schema`, `table`, `index`, `view`, `sequence`,
|
|
3
3
|
* `type` (enum, composite and range), `domain`, `extension`, `func`,
|
|
4
4
|
* `procedure`, `aggregate`, `trigger`, `eventTrigger`, `collation`, `cast`,
|
|
5
|
-
* `
|
|
5
|
+
* `operatorClass`, `operatorFamily`, `foreignTable`, `textSearchConfiguration`,
|
|
6
|
+
* `textSearchDictionary`, `policy`, `role` and `grant` tagged templates that parse their DDL at fold
|
|
6
7
|
* time into one Declarable each (chant #3278, #3279, #3680, #3681, #3911).
|
|
7
8
|
*
|
|
8
9
|
* ```ts
|
|
@@ -195,6 +196,11 @@ export const STATEMENTS_OF = {
|
|
|
195
196
|
trigger: ["trigger"],
|
|
196
197
|
eventTrigger: ["eventTrigger"],
|
|
197
198
|
operator: ["operator"],
|
|
199
|
+
operatorClass: ["operatorClass"],
|
|
200
|
+
operatorFamily: ["operatorFamily"],
|
|
201
|
+
foreignTable: ["foreignTable"],
|
|
202
|
+
textSearchConfiguration: ["textSearchConfiguration"],
|
|
203
|
+
textSearchDictionary: ["textSearchDictionary"],
|
|
198
204
|
statistics: ["statistics"],
|
|
199
205
|
foreignDataWrapper: ["foreignDataWrapper"],
|
|
200
206
|
foreignServer: ["foreignServer"],
|
|
@@ -222,6 +228,12 @@ const TAG_OF = {
|
|
|
222
228
|
trigger: "trigger",
|
|
223
229
|
eventTrigger: "eventTrigger",
|
|
224
230
|
operator: "operator",
|
|
231
|
+
operatorClass: "operatorClass",
|
|
232
|
+
operatorFamily: "operatorFamily",
|
|
233
|
+
foreignTable: "foreignTable",
|
|
234
|
+
textSearchConfiguration: "textSearchConfiguration",
|
|
235
|
+
textSearchDictionary: "textSearchDictionary",
|
|
236
|
+
textSearchMapping: undefined,
|
|
225
237
|
statistics: "statistics",
|
|
226
238
|
foreignDataWrapper: "foreignDataWrapper",
|
|
227
239
|
foreignServer: "foreignServer",
|
|
@@ -252,6 +264,12 @@ export const STATEMENT_NAMES = {
|
|
|
252
264
|
trigger: "CREATE TRIGGER",
|
|
253
265
|
eventTrigger: "CREATE EVENT TRIGGER",
|
|
254
266
|
operator: "CREATE OPERATOR",
|
|
267
|
+
operatorClass: "CREATE OPERATOR CLASS",
|
|
268
|
+
operatorFamily: "CREATE OPERATOR FAMILY",
|
|
269
|
+
foreignTable: "CREATE FOREIGN TABLE",
|
|
270
|
+
textSearchConfiguration: "CREATE TEXT SEARCH CONFIGURATION",
|
|
271
|
+
textSearchDictionary: "CREATE TEXT SEARCH DICTIONARY",
|
|
272
|
+
textSearchMapping: "ALTER TEXT SEARCH CONFIGURATION",
|
|
255
273
|
statistics: "CREATE STATISTICS",
|
|
256
274
|
foreignDataWrapper: "CREATE FOREIGN DATA WRAPPER",
|
|
257
275
|
foreignServer: "CREATE SERVER",
|
|
@@ -364,7 +382,7 @@ function columnName(ctx, c) {
|
|
|
364
382
|
const v = t?.kind === "ref" ? ctx.values[t.part] : undefined;
|
|
365
383
|
return isColumnRef(v) ? v.attribute : req(text(ctx, c.span));
|
|
366
384
|
}
|
|
367
|
-
function columnDef(ctx, c) {
|
|
385
|
+
function columnDef(ctx, c, tag = "table") {
|
|
368
386
|
for (const span of [c.nameSpan, c.type, c.default, c.collate, c.compression, c.storage, c.generated?.expr, c.generated?.options])
|
|
369
387
|
feed(ctx, span, "columns");
|
|
370
388
|
const def = { name: columnName(ctx, { name: c.name, span: c.nameSpan }) };
|
|
@@ -389,6 +407,8 @@ function columnDef(ctx, c) {
|
|
|
389
407
|
set("collate", text(ctx, c.collate));
|
|
390
408
|
set("compression", text(ctx, c.compression));
|
|
391
409
|
set("storage", text(ctx, c.storage));
|
|
410
|
+
if (c.options && c.options.length > 0)
|
|
411
|
+
def.options = genericOptionValues(tag, ctx, c.options);
|
|
392
412
|
return def;
|
|
393
413
|
}
|
|
394
414
|
function deferral(c) {
|
|
@@ -466,7 +486,7 @@ function exclusionDef(ctx, c) {
|
|
|
466
486
|
* the object itself, one of its columns, or one of its named constraints.
|
|
467
487
|
* Anything else is refused, since a template declares one object.
|
|
468
488
|
*/
|
|
469
|
-
function applyComments(tag, ctx, comments, self, objectTypes, parts = {}) {
|
|
489
|
+
function applyComments(tag, ctx, comments, self, objectTypes, parts = {}, using) {
|
|
470
490
|
let comment;
|
|
471
491
|
const isSelf = (q) => q.name === self.name && (q.schema === undefined || self.schema === undefined || q.schema === self.schema);
|
|
472
492
|
const refuse = (c, why) => {
|
|
@@ -524,6 +544,9 @@ function applyComments(tag, ctx, comments, self, objectTypes, parts = {}) {
|
|
|
524
544
|
}
|
|
525
545
|
else if (!isSelf(qualified(ctx, c.target)))
|
|
526
546
|
refuse(c, `names another object than ${sqlNameOf(self)}`);
|
|
547
|
+
// An operator class or family is named by its index method too.
|
|
548
|
+
if (using !== undefined && c.using !== using)
|
|
549
|
+
refuse(c, `names another index method than ${using}`);
|
|
527
550
|
comment = value;
|
|
528
551
|
}
|
|
529
552
|
}
|
|
@@ -547,7 +570,7 @@ function lastDot(ctx, span) {
|
|
|
547
570
|
export function followOnStray(tag, rest) {
|
|
548
571
|
if (tag === "grant")
|
|
549
572
|
return rest[0];
|
|
550
|
-
return rest.find((n) => n.statement !== "comment" && !(tag === "table" && n.statement === "rowSecurity"));
|
|
573
|
+
return rest.find((n) => n.statement !== "comment" && !(tag === "table" && n.statement === "rowSecurity") && !(tag === "textSearchConfiguration" && n.statement === "textSearchMapping"));
|
|
551
574
|
}
|
|
552
575
|
/**
|
|
553
576
|
* The interpolations that sit inside a string constant (a routine's
|
|
@@ -780,7 +803,7 @@ function build(tag, strings, rawValues) {
|
|
|
780
803
|
}
|
|
781
804
|
const stray = followOnStray(tag, rest);
|
|
782
805
|
if (stray) {
|
|
783
|
-
throw new SqlTemplateError(tag, `holds a second statement (${STATEMENT_NAMES[stray.statement]}); a template declares one object, followed only by COMMENT ON statements for it${tag === "table" ? " and ALTER TABLE ... ROW LEVEL SECURITY" : ""}`, 0, 0);
|
|
806
|
+
throw new SqlTemplateError(tag, `holds a second statement (${STATEMENT_NAMES[stray.statement]}); a template declares one object, followed only by COMMENT ON statements for it${tag === "table" ? " and ALTER TABLE ... ROW LEVEL SECURITY" : tag === "textSearchConfiguration" ? " and ALTER TEXT SEARCH CONFIGURATION ... ADD MAPPING" : ""}`, 0, 0);
|
|
784
807
|
}
|
|
785
808
|
const comments = rest.filter((n) => n.statement === "comment");
|
|
786
809
|
const rowSecurity = rest.filter((n) => n.statement === "rowSecurity");
|
|
@@ -947,6 +970,119 @@ function build(tag, strings, rawValues) {
|
|
|
947
970
|
// The symbol is unquoted wherever the operator is named: `app.===`.
|
|
948
971
|
return make(POSTGRES_ENTITY_TYPES.operator, q, props, undefined, q.schema ? `${quoteIdent(q.schema)}.${q.name}` : q.name);
|
|
949
972
|
}
|
|
973
|
+
case "operatorClass": {
|
|
974
|
+
const q = qualified(ctx, node.name);
|
|
975
|
+
if (node.method === "")
|
|
976
|
+
throw new SqlTemplateError(tag, "the index method is written out, not interpolated", 0, 0);
|
|
977
|
+
const comment = applyComments(tag, ctx, comments, q, ["OPERATOR CLASS"], {}, node.method);
|
|
978
|
+
const items = node.items.map((i) => {
|
|
979
|
+
if (i.kind === "storage")
|
|
980
|
+
return { kind: "storage", type: req(text(ctx, i.type, "items")) };
|
|
981
|
+
if (i.kind === "function") {
|
|
982
|
+
feed(ctx, i.function.span, "items");
|
|
983
|
+
return stripUnset({ kind: "function", number: req(text(ctx, i.number, "items")), types: text(ctx, i.types, "items"), function: targetName(ctx, i.function), args: text(ctx, i.args, "items") ?? "" });
|
|
984
|
+
}
|
|
985
|
+
return stripUnset({
|
|
986
|
+
kind: "operator",
|
|
987
|
+
number: req(text(ctx, i.number, "items")),
|
|
988
|
+
operator: req(text(ctx, i.operator.span, "items")),
|
|
989
|
+
types: text(ctx, i.types, "items"),
|
|
990
|
+
orderBy: i.orderBy ? targetName(ctx, i.orderBy) : undefined,
|
|
991
|
+
});
|
|
992
|
+
});
|
|
993
|
+
const props = stripUnset({
|
|
994
|
+
...q,
|
|
995
|
+
default: node.default || undefined,
|
|
996
|
+
dataType: req(text(ctx, node.type, "dataType")),
|
|
997
|
+
method: node.method,
|
|
998
|
+
family: node.family ? targetName(ctx, node.family) : undefined,
|
|
999
|
+
items,
|
|
1000
|
+
comment,
|
|
1001
|
+
ddl,
|
|
1002
|
+
source,
|
|
1003
|
+
});
|
|
1004
|
+
return make(POSTGRES_ENTITY_TYPES.operatorClass, q, props);
|
|
1005
|
+
}
|
|
1006
|
+
case "operatorFamily": {
|
|
1007
|
+
const q = qualified(ctx, node.name);
|
|
1008
|
+
if (node.method === "")
|
|
1009
|
+
throw new SqlTemplateError(tag, "the index method is written out, not interpolated", 0, 0);
|
|
1010
|
+
const comment = applyComments(tag, ctx, comments, q, ["OPERATOR FAMILY"], {}, node.method);
|
|
1011
|
+
const props = stripUnset({ ...q, method: node.method, comment, ddl, source });
|
|
1012
|
+
return make(POSTGRES_ENTITY_TYPES.operatorFamily, q, props);
|
|
1013
|
+
}
|
|
1014
|
+
case "foreignTable": {
|
|
1015
|
+
const q = qualified(ctx, node.name);
|
|
1016
|
+
const columns = node.columns.map((c) => columnDef(ctx, c, tag));
|
|
1017
|
+
const checks = [];
|
|
1018
|
+
const file = (c, cols) => {
|
|
1019
|
+
if (c.kind === "CHECK")
|
|
1020
|
+
checks.push(checkDef(ctx, c));
|
|
1021
|
+
else if (c.kind === "NOT NULL") {
|
|
1022
|
+
const col = columns.find((x) => x.name === cols[0]);
|
|
1023
|
+
if (!col)
|
|
1024
|
+
throw new SqlTemplateError(tag, `NOT NULL ${cols[0]} names no column of ${sqlNameOf(q)}`, 0, 0);
|
|
1025
|
+
col.notNull = true;
|
|
1026
|
+
if (c.name)
|
|
1027
|
+
col.notNullName = c.name;
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
node.columns.forEach((c, i) => c.constraints.forEach((k) => file(k, [columns[i].name])));
|
|
1031
|
+
node.constraints.forEach((k) => file(k, k.columns.map((x) => columnName(ctx, x))));
|
|
1032
|
+
const named = new Map();
|
|
1033
|
+
for (const c of checks)
|
|
1034
|
+
if (c.name)
|
|
1035
|
+
named.set(c.name, c);
|
|
1036
|
+
const comment = applyComments(tag, ctx, comments, q, ["FOREIGN TABLE"], { columns: new Map(columns.map((c) => [c.name, c])), constraints: named });
|
|
1037
|
+
const props = strip({
|
|
1038
|
+
...q,
|
|
1039
|
+
ifNotExists: node.ifNotExists || undefined,
|
|
1040
|
+
columns,
|
|
1041
|
+
checks,
|
|
1042
|
+
serverName: databaseObjectName(ctx, node.server),
|
|
1043
|
+
options: genericOptionValues(tag, ctx, node.options),
|
|
1044
|
+
comment,
|
|
1045
|
+
ddl,
|
|
1046
|
+
source,
|
|
1047
|
+
}, ["columns", "checks", "options"]);
|
|
1048
|
+
return make(POSTGRES_ENTITY_TYPES.foreignTable, q, props, columns.map((c) => c.name));
|
|
1049
|
+
}
|
|
1050
|
+
case "textSearchDictionary": {
|
|
1051
|
+
const q = qualified(ctx, node.name);
|
|
1052
|
+
const comment = applyComments(tag, ctx, comments, q, ["TEXT SEARCH DICTIONARY"]);
|
|
1053
|
+
const { template, ...options } = definitionValues(ctx, node.options, "options");
|
|
1054
|
+
const props = strip({ ...q, template: req(template), options, comment, ddl, source }, ["options"]);
|
|
1055
|
+
return make(POSTGRES_ENTITY_TYPES.textSearchDictionary, q, props);
|
|
1056
|
+
}
|
|
1057
|
+
case "textSearchConfiguration": {
|
|
1058
|
+
const q = qualified(ctx, node.name);
|
|
1059
|
+
const comment = applyComments(tag, ctx, comments, q, ["TEXT SEARCH CONFIGURATION"]);
|
|
1060
|
+
const opt = definitionValues(ctx, node.options, "options");
|
|
1061
|
+
const seen = new Set();
|
|
1062
|
+
const mappings = rest
|
|
1063
|
+
.filter((n) => n.statement === "textSearchMapping")
|
|
1064
|
+
.map((m) => {
|
|
1065
|
+
feed(ctx, m.name.span, "mappings");
|
|
1066
|
+
const t = qualified(ctx, m.name);
|
|
1067
|
+
if (t.name !== q.name || (t.schema !== undefined && q.schema !== undefined && t.schema !== q.schema)) {
|
|
1068
|
+
throw new SqlTemplateError(tag, `ADD MAPPING names another configuration (${sqlNameOf(t)}) than ${sqlNameOf(q)}`, 0, 0);
|
|
1069
|
+
}
|
|
1070
|
+
const tokens = m.tokens.map((x) => x.name || identValue(req(text(ctx, x.span, "mappings"))));
|
|
1071
|
+
for (const tok of tokens) {
|
|
1072
|
+
if (seen.has(tok))
|
|
1073
|
+
throw new SqlTemplateError(tag, `the token type ${tok} is mapped twice in ${sqlNameOf(q)}`, 0, 0);
|
|
1074
|
+
seen.add(tok);
|
|
1075
|
+
}
|
|
1076
|
+
for (const d of m.dictionaries)
|
|
1077
|
+
feed(ctx, d.span, "mappings");
|
|
1078
|
+
return { tokens, dictionaries: m.dictionaries.map((d) => sqlNameOf(qualified(ctx, d))) };
|
|
1079
|
+
});
|
|
1080
|
+
const props = strip({ ...q, parser: req(opt.parser), mappings, comment, ddl, source }, ["mappings"]);
|
|
1081
|
+
return make(POSTGRES_ENTITY_TYPES.textSearchConfiguration, q, props);
|
|
1082
|
+
}
|
|
1083
|
+
case "textSearchMapping":
|
|
1084
|
+
// Only a configuration's follow-on statement (`followOnStray`); first in a template it is refused above.
|
|
1085
|
+
throw new SqlTemplateError(tag, "holds an ALTER TEXT SEARCH CONFIGURATION before any CREATE", 0, 0);
|
|
950
1086
|
case "statistics": {
|
|
951
1087
|
const q = qualified(ctx, node.name);
|
|
952
1088
|
const comment = applyComments(tag, ctx, comments, q, ["STATISTICS"]);
|
|
@@ -1479,6 +1615,45 @@ export function eventTrigger(strings, ...values) {
|
|
|
1479
1615
|
export function operator(strings, ...values) {
|
|
1480
1616
|
return build("operator", strings, values);
|
|
1481
1617
|
}
|
|
1618
|
+
/**
|
|
1619
|
+
* `` operatorClass`CREATE OPERATOR CLASS app.ops DEFAULT FOR TYPE int USING btree AS OPERATOR 1 <, FUNCTION 1 ${cmp}(int, int)` ``:
|
|
1620
|
+
* one operator class, identified by its name and index method. Creating one takes a superuser.
|
|
1621
|
+
*/
|
|
1622
|
+
export function operatorClass(strings, ...values) {
|
|
1623
|
+
return build("operatorClass", strings, values);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* `` operatorFamily`CREATE OPERATOR FAMILY app.ops USING btree` ``: one operator
|
|
1627
|
+
* family, identified by its name and index method. Its members come with the
|
|
1628
|
+
* operator classes that name it. Creating one takes a superuser.
|
|
1629
|
+
*/
|
|
1630
|
+
export function operatorFamily(strings, ...values) {
|
|
1631
|
+
return build("operatorFamily", strings, values);
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* `` foreignTable`CREATE FOREIGN TABLE app.remote_orders (id int, total numeric) SERVER ${remote} OPTIONS (table_name 'orders')` ``:
|
|
1635
|
+
* one foreign table. It has its own tag, not `table`, because it is another
|
|
1636
|
+
* kind of relation: another CREATE and ALTER, no key, index, partitioning or
|
|
1637
|
+
* storage clauses, and a server it cannot move off.
|
|
1638
|
+
*/
|
|
1639
|
+
export function foreignTable(strings, ...values) {
|
|
1640
|
+
return build("foreignTable", strings, values);
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* `` textSearchDictionary`CREATE TEXT SEARCH DICTIONARY app.english_stop (TEMPLATE = snowball, Language = english, StopWords = english)` ``:
|
|
1644
|
+
* one text search dictionary: a template and its options.
|
|
1645
|
+
*/
|
|
1646
|
+
export function textSearchDictionary(strings, ...values) {
|
|
1647
|
+
return build("textSearchDictionary", strings, values);
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* `` textSearchConfiguration`CREATE TEXT SEARCH CONFIGURATION app.english2 (PARSER = default); ALTER TEXT SEARCH CONFIGURATION app.english2 ADD MAPPING FOR asciiword WITH ${dict}` ``:
|
|
1651
|
+
* one text search configuration: a parser, and the dictionaries each token
|
|
1652
|
+
* type it finds is looked up in.
|
|
1653
|
+
*/
|
|
1654
|
+
export function textSearchConfiguration(strings, ...values) {
|
|
1655
|
+
return build("textSearchConfiguration", strings, values);
|
|
1656
|
+
}
|
|
1482
1657
|
/**
|
|
1483
1658
|
* `` statistics`CREATE STATISTICS name (ndistinct, dependencies) ON a, b FROM ${table}` ``:
|
|
1484
1659
|
* extended statistics on columns or expressions of one table.
|
|
@@ -18,6 +18,11 @@ export declare const POSTGRES_ENTITY_TYPES: {
|
|
|
18
18
|
readonly cast: "Postgres::Cast";
|
|
19
19
|
readonly eventTrigger: "Postgres::EventTrigger";
|
|
20
20
|
readonly operator: "Postgres::Operator";
|
|
21
|
+
readonly operatorClass: "Postgres::OperatorClass";
|
|
22
|
+
readonly operatorFamily: "Postgres::OperatorFamily";
|
|
23
|
+
readonly foreignTable: "Postgres::ForeignTable";
|
|
24
|
+
readonly textSearchConfiguration: "Postgres::TextSearchConfiguration";
|
|
25
|
+
readonly textSearchDictionary: "Postgres::TextSearchDictionary";
|
|
21
26
|
readonly statistics: "Postgres::Statistics";
|
|
22
27
|
readonly foreignDataWrapper: "Postgres::ForeignDataWrapper";
|
|
23
28
|
readonly foreignServer: "Postgres::ForeignServer";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-types.d.ts","sourceRoot":"","sources":["../../src/postgres/entity-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,qBAAqB
|
|
1
|
+
{"version":3,"file":"entity-types.d.ts","sourceRoot":"","sources":["../../src/postgres/entity-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCxB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC"}
|
|
@@ -18,6 +18,11 @@ export const POSTGRES_ENTITY_TYPES = {
|
|
|
18
18
|
cast: "Postgres::Cast",
|
|
19
19
|
eventTrigger: "Postgres::EventTrigger",
|
|
20
20
|
operator: "Postgres::Operator",
|
|
21
|
+
operatorClass: "Postgres::OperatorClass",
|
|
22
|
+
operatorFamily: "Postgres::OperatorFamily",
|
|
23
|
+
foreignTable: "Postgres::ForeignTable",
|
|
24
|
+
textSearchConfiguration: "Postgres::TextSearchConfiguration",
|
|
25
|
+
textSearchDictionary: "Postgres::TextSearchDictionary",
|
|
21
26
|
statistics: "Postgres::Statistics",
|
|
22
27
|
foreignDataWrapper: "Postgres::ForeignDataWrapper",
|
|
23
28
|
foreignServer: "Postgres::ForeignServer",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../../src/postgres/import/generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAC5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAKjE,kGAAkG;AAClG,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AACD,KAAK,IAAI,GAAG,cAAc,CAAC;AAE3B,uHAAuH;AACvH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../../src/postgres/import/generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAC5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAKjE,kGAAkG;AAClG,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AACD,KAAK,IAAI,GAAG,cAAc,CAAC;AAE3B,uHAAuH;AACvH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAwDD,wEAAwE;AACxE,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAO,CAAC;AAmKrE,0DAA0D;AAC1D,eAAO,MAAM,QAAQ,GAAI,MAAM,YAAY,KAAG,MAA0F,CAAC;AAEzI;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,OAAO,GAAE,SAAS,IAAI,EAAU,GAAG;IAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;CAAE,CA6BhJ;AAaD,qBAAa,iBAAkB,YAAW,mBAAmB;IAG/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAFpC,QAAQ,CAAC,UAAU,QAAQ;gBAEE,MAAM,CAAC,EAAE,MAAM,YAAA;IAE5C,QAAQ,CAAC,EAAE,EAAE,UAAU,GAAG,aAAa,EAAE;CAqB1C"}
|
|
@@ -41,6 +41,11 @@ const TAG = {
|
|
|
41
41
|
[POSTGRES_ENTITY_TYPES.cast]: "cast",
|
|
42
42
|
[POSTGRES_ENTITY_TYPES.eventTrigger]: "eventTrigger",
|
|
43
43
|
[POSTGRES_ENTITY_TYPES.operator]: "operator",
|
|
44
|
+
[POSTGRES_ENTITY_TYPES.operatorClass]: "operatorClass",
|
|
45
|
+
[POSTGRES_ENTITY_TYPES.operatorFamily]: "operatorFamily",
|
|
46
|
+
[POSTGRES_ENTITY_TYPES.foreignTable]: "foreignTable",
|
|
47
|
+
[POSTGRES_ENTITY_TYPES.textSearchConfiguration]: "textSearchConfiguration",
|
|
48
|
+
[POSTGRES_ENTITY_TYPES.textSearchDictionary]: "textSearchDictionary",
|
|
44
49
|
[POSTGRES_ENTITY_TYPES.statistics]: "statistics",
|
|
45
50
|
[POSTGRES_ENTITY_TYPES.foreignDataWrapper]: "foreignDataWrapper",
|
|
46
51
|
[POSTGRES_ENTITY_TYPES.foreignServer]: "foreignServer",
|
|
@@ -66,8 +71,10 @@ const UNREFERENCED = new Set([
|
|
|
66
71
|
POSTGRES_ENTITY_TYPES.cast,
|
|
67
72
|
POSTGRES_ENTITY_TYPES.eventTrigger,
|
|
68
73
|
POSTGRES_ENTITY_TYPES.operator,
|
|
74
|
+
// An index names a class, but a class and its own family share a name; a configuration shares one with a dictionary, which a mapping names.
|
|
75
|
+
POSTGRES_ENTITY_TYPES.operatorClass,
|
|
76
|
+
POSTGRES_ENTITY_TYPES.textSearchConfiguration,
|
|
69
77
|
POSTGRES_ENTITY_TYPES.statistics,
|
|
70
|
-
POSTGRES_ENTITY_TYPES.foreignServer,
|
|
71
78
|
POSTGRES_ENTITY_TYPES.publication,
|
|
72
79
|
]);
|
|
73
80
|
/** The tag each entity type is declared with (`table`, `func`, ...). */
|
|
@@ -126,6 +133,14 @@ function bareSpots(tokens) {
|
|
|
126
133
|
name(node.function);
|
|
127
134
|
if (node.statement === "foreignServer")
|
|
128
135
|
name(node.wrapper);
|
|
136
|
+
if (node.statement === "foreignTable") {
|
|
137
|
+
for (const c of node.columns)
|
|
138
|
+
if (c.type)
|
|
139
|
+
first(c.type.from, c.type.to);
|
|
140
|
+
name(node.server);
|
|
141
|
+
}
|
|
142
|
+
if (node.statement === "operatorClass")
|
|
143
|
+
first(node.type.from, node.type.to);
|
|
129
144
|
if (node.statement === "foreignDataWrapper") {
|
|
130
145
|
name(node.handler ?? undefined);
|
|
131
146
|
name(node.validator ?? undefined);
|
|
@@ -39,4 +39,10 @@ export declare function readOperators(client: PostgresClient, scope: SchemaScope
|
|
|
39
39
|
* not narrow it. A credential in an option is left out and said so.
|
|
40
40
|
*/
|
|
41
41
|
export declare function readDatabaseKinds(client: PostgresClient, scope: SchemaScope): Promise<LivePgObject[]>;
|
|
42
|
+
/**
|
|
43
|
+
* A dictionary's `dictinitoption`, which the server prints as
|
|
44
|
+
* `name = 'value', ...` (a quote and a backslash doubled in the value), as a
|
|
45
|
+
* list of name and value; undefined when the text is not that form.
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseDictionaryOptions(init: string | undefined): Array<[string, string]> | undefined;
|
|
42
48
|
//# sourceMappingURL=catalog-kinds.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog-kinds.d.ts","sourceRoot":"","sources":["../../../src/postgres/live/catalog-kinds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAA+B,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AA2C7F,4GAA4G;AAC5G,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA0EvG;AAED,kFAAkF;AAClF,wBAAsB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAuCxG;AAID,gFAAgF;AAChF,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA8H1G;AAMD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAwCvG;AAkBD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"catalog-kinds.d.ts","sourceRoot":"","sources":["../../../src/postgres/live/catalog-kinds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAA+B,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AA2C7F,4GAA4G;AAC5G,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA0EvG;AAED,kFAAkF;AAClF,wBAAsB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAuCxG;AAID,gFAAgF;AAChF,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA8H1G;AAMD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAwCvG;AAkBD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA2E3G;AA0KD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAcpG"}
|
|
@@ -437,6 +437,7 @@ export async function readDatabaseKinds(client, scope) {
|
|
|
437
437
|
});
|
|
438
438
|
}
|
|
439
439
|
out.push(...(await readPublications(client)));
|
|
440
|
+
out.push(...(await readOperatorClasses(client, scope)), ...(await readTextSearch(client, scope)), ...(await readForeignTables(client, scope)));
|
|
440
441
|
return out;
|
|
441
442
|
}
|
|
442
443
|
/** Publications. Column lists, row filters and schema members are 15's; they are read where the catalog has them. */
|
|
@@ -505,3 +506,248 @@ async function readPublications(client) {
|
|
|
505
506
|
}
|
|
506
507
|
return out;
|
|
507
508
|
}
|
|
509
|
+
// ── Operator classes and families, text search, foreign tables (#4406) ──
|
|
510
|
+
/** An object named by `schema`.`name` columns, as a clause: a `pg_catalog` one bare. */
|
|
511
|
+
const refName = (schema, name) => `CASE WHEN ${schema} = 'pg_catalog' THEN pg_catalog.quote_ident(${name}) ELSE pg_catalog.quote_ident(${schema}) || '.' || pg_catalog.quote_ident(${name}) END`;
|
|
512
|
+
/** An operand type, `NONE` for the missing side of a prefix operator. */
|
|
513
|
+
const operand = (column) => `CASE WHEN ${column} = 0 THEN 'NONE' ELSE pg_catalog.format_type(${column}, NULL) END`;
|
|
514
|
+
/**
|
|
515
|
+
* Operator classes and families. A class carries its members: the operators
|
|
516
|
+
* and support functions that depend on it (`pg_depend`, a member made by
|
|
517
|
+
* CREATE OPERATOR CLASS depends on the class; one added by ALTER OPERATOR
|
|
518
|
+
* FAMILY ... ADD depends on the family and is "loose"). A family named as its
|
|
519
|
+
* class, in the same schema, is the one CREATE OPERATOR CLASS made for it and
|
|
520
|
+
* is the class's; every other family is read, with a warning when it holds
|
|
521
|
+
* loose members, which a declaration cannot carry.
|
|
522
|
+
*/
|
|
523
|
+
async function readOperatorClasses(client, scope) {
|
|
524
|
+
const out = [];
|
|
525
|
+
const s = scopeSql("n.nspname", scope);
|
|
526
|
+
const member = (catalog, alias) => `JOIN pg_catalog.pg_depend dep ON dep.classid = '${catalog}'::pg_catalog.regclass AND dep.objid = ${alias}.oid AND dep.refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass AND dep.refobjid = c.oid`;
|
|
527
|
+
const classes = await client.query(`SELECT c.oid::text AS oid, n.nspname AS schema, c.opcname AS name, am.amname AS method, c.opcdefault AS isdefault,
|
|
528
|
+
pg_catalog.format_type(c.opcintype, NULL) AS type,
|
|
529
|
+
CASE WHEN c.opckeytype <> 0 THEN pg_catalog.format_type(c.opckeytype, NULL) END AS storage,
|
|
530
|
+
CASE WHEN f.opfname = c.opcname AND f.opfnamespace = c.opcnamespace THEN NULL ELSE ${refName("fn.nspname", "f.opfname")} END AS family,
|
|
531
|
+
ARRAY(SELECT 'OPERATOR ' || o.amopstrategy || ' ' || pg_catalog.quote_ident(opn.nspname) || '.' || op.oprname ||
|
|
532
|
+
' (' || ${operand("o.amoplefttype")} || ', ' || ${operand("o.amoprighttype")} || ')' ||
|
|
533
|
+
CASE WHEN o.amoppurpose = 'o' THEN ' FOR ORDER BY ' || (SELECT pg_catalog.quote_ident(sn.nspname) || '.' || pg_catalog.quote_ident(sf.opfname)
|
|
534
|
+
FROM pg_catalog.pg_opfamily sf JOIN pg_catalog.pg_namespace sn ON sn.oid = sf.opfnamespace WHERE sf.oid = o.amopsortfamily) ELSE '' END
|
|
535
|
+
FROM pg_catalog.pg_amop o ${member("pg_catalog.pg_amop", "o")}
|
|
536
|
+
JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr JOIN pg_catalog.pg_namespace opn ON opn.oid = op.oprnamespace
|
|
537
|
+
ORDER BY o.amopstrategy, o.amoplefttype, o.amoprighttype) AS operators,
|
|
538
|
+
ARRAY(SELECT 'FUNCTION ' || p.amprocnum || ' (' || ${operand("p.amproclefttype")} || ', ' || ${operand("p.amprocrighttype")} || ') ' || p.amproc::pg_catalog.regprocedure::pg_catalog.text
|
|
539
|
+
FROM pg_catalog.pg_amproc p ${member("pg_catalog.pg_amproc", "p")}
|
|
540
|
+
ORDER BY p.amprocnum, p.amproclefttype, p.amprocrighttype) AS functions,
|
|
541
|
+
pg_catalog.obj_description(c.oid, 'pg_opclass') AS comment, ${dependentsOf("pg_opclass", "c.oid")} AS dependents
|
|
542
|
+
FROM pg_catalog.pg_opclass c JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace JOIN pg_catalog.pg_am am ON am.oid = c.opcmethod
|
|
543
|
+
JOIN pg_catalog.pg_opfamily f ON f.oid = c.opcfamily JOIN pg_catalog.pg_namespace fn ON fn.oid = f.opfnamespace
|
|
544
|
+
WHERE ${s.sql} AND c.oid >= 16384 AND ${notExtensionOwned("pg_opclass", "c.oid")}
|
|
545
|
+
ORDER BY n.nspname ${C}, c.opcname ${C}, am.amname ${C}`, s.params);
|
|
546
|
+
for (const r of classes) {
|
|
547
|
+
const q = qname(String(r.schema), String(r.name));
|
|
548
|
+
const method = String(r.method);
|
|
549
|
+
const items = [...(r.operators ?? []), ...(r.functions ?? []), ...(str(r.storage) ? [`STORAGE ${r.storage}`] : [])];
|
|
550
|
+
out.push({
|
|
551
|
+
type: POSTGRES_ENTITY_TYPES.operatorClass,
|
|
552
|
+
schema: String(r.schema),
|
|
553
|
+
name: String(r.name),
|
|
554
|
+
oid: String(r.oid),
|
|
555
|
+
signature: ` USING ${method}`,
|
|
556
|
+
...withComment(r),
|
|
557
|
+
...dependents(r),
|
|
558
|
+
...(items.length === 0 ? { unsupported: "no operator, support function or storage type of it could be read; a CREATE OPERATOR CLASS needs at least one" } : {}),
|
|
559
|
+
statement: [
|
|
560
|
+
`CREATE OPERATOR CLASS ${q}${r.isdefault === true ? " DEFAULT" : ""} FOR TYPE ${r.type} USING ${method}${str(r.family) ? ` FAMILY ${r.family}` : ""} AS\n ${items.join(",\n ")}`,
|
|
561
|
+
...commentOn(`OPERATOR CLASS ${q} USING ${method}`, r.comment),
|
|
562
|
+
].join(";\n"),
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
const families = await client.query(`SELECT f.oid::text AS oid, n.nspname AS schema, f.opfname AS name, am.amname AS method,
|
|
566
|
+
(SELECT count(*) FROM (SELECT o.oid FROM pg_catalog.pg_amop o WHERE o.amopfamily = f.oid UNION ALL SELECT p.oid FROM pg_catalog.pg_amproc p WHERE p.amprocfamily = f.oid) m
|
|
567
|
+
WHERE NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend dep WHERE dep.classid IN ('pg_catalog.pg_amop'::pg_catalog.regclass, 'pg_catalog.pg_amproc'::pg_catalog.regclass)
|
|
568
|
+
AND dep.objid = m.oid AND dep.refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass)) AS loose,
|
|
569
|
+
pg_catalog.obj_description(f.oid, 'pg_opfamily') AS comment, ${dependentsOf("pg_opfamily", "f.oid")} AS dependents
|
|
570
|
+
FROM pg_catalog.pg_opfamily f JOIN pg_catalog.pg_namespace n ON n.oid = f.opfnamespace JOIN pg_catalog.pg_am am ON am.oid = f.opfmethod
|
|
571
|
+
WHERE ${s.sql} AND f.oid >= 16384 AND ${notExtensionOwned("pg_opfamily", "f.oid")}
|
|
572
|
+
AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_opclass c WHERE c.opcfamily = f.oid AND c.opcname = f.opfname AND c.opcnamespace = f.opfnamespace)
|
|
573
|
+
ORDER BY n.nspname ${C}, f.opfname ${C}, am.amname ${C}`, s.params);
|
|
574
|
+
for (const r of families) {
|
|
575
|
+
const q = qname(String(r.schema), String(r.name));
|
|
576
|
+
const method = String(r.method);
|
|
577
|
+
const loose = Number(r.loose ?? 0);
|
|
578
|
+
out.push({
|
|
579
|
+
type: POSTGRES_ENTITY_TYPES.operatorFamily,
|
|
580
|
+
schema: String(r.schema),
|
|
581
|
+
name: String(r.name),
|
|
582
|
+
oid: String(r.oid),
|
|
583
|
+
signature: ` USING ${method}`,
|
|
584
|
+
...withComment(r),
|
|
585
|
+
...dependents(r),
|
|
586
|
+
...(loose > 0 ? { warnings: [`${loose} loose member${loose === 1 ? "" : "s"} added with ALTER OPERATOR FAMILY ... ADD ${loose === 1 ? "is" : "are"} left out; a declaration holds the family and its classes' members`] } : {}),
|
|
587
|
+
statement: [`CREATE OPERATOR FAMILY ${q} USING ${method}`, ...commentOn(`OPERATOR FAMILY ${q} USING ${method}`, r.comment)].join(";\n"),
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
return out;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* A dictionary's `dictinitoption`, which the server prints as
|
|
594
|
+
* `name = 'value', ...` (a quote and a backslash doubled in the value), as a
|
|
595
|
+
* list of name and value; undefined when the text is not that form.
|
|
596
|
+
*/
|
|
597
|
+
export function parseDictionaryOptions(init) {
|
|
598
|
+
if (init === undefined || init.trim() === "")
|
|
599
|
+
return [];
|
|
600
|
+
const out = [];
|
|
601
|
+
const item = /\s*("(?:[^"]|"")+"|[^\s=,]+)\s*=\s*'((?:[^']|'')*)'\s*(?:,|$)/y;
|
|
602
|
+
let at = 0;
|
|
603
|
+
while (at < init.length) {
|
|
604
|
+
item.lastIndex = at;
|
|
605
|
+
const m = item.exec(init);
|
|
606
|
+
if (!m)
|
|
607
|
+
return undefined;
|
|
608
|
+
const name = m[1].startsWith('"') ? m[1].slice(1, -1).replace(/""/g, '"') : m[1];
|
|
609
|
+
out.push([name, m[2].replace(/''/g, "'").replace(/\\\\/g, "\\")]);
|
|
610
|
+
at = item.lastIndex;
|
|
611
|
+
}
|
|
612
|
+
return out;
|
|
613
|
+
}
|
|
614
|
+
/** Text search dictionaries and configurations; a configuration's mappings are the ALTER ... ADD MAPPING statements after its CREATE. */
|
|
615
|
+
async function readTextSearch(client, scope) {
|
|
616
|
+
const out = [];
|
|
617
|
+
const s = scopeSql("n.nspname", scope);
|
|
618
|
+
const dictionaries = await client.query(`SELECT d.oid::text AS oid, n.nspname AS schema, d.dictname AS name, d.dictinitoption AS init,
|
|
619
|
+
(SELECT ${refName("tn.nspname", "t.tmplname")} FROM pg_catalog.pg_ts_template t JOIN pg_catalog.pg_namespace tn ON tn.oid = t.tmplnamespace WHERE t.oid = d.dicttemplate) AS template,
|
|
620
|
+
pg_catalog.obj_description(d.oid, 'pg_ts_dict') AS comment, ${dependentsOf("pg_ts_dict", "d.oid")} AS dependents
|
|
621
|
+
FROM pg_catalog.pg_ts_dict d JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace
|
|
622
|
+
WHERE ${s.sql} AND d.oid >= 16384 AND ${notExtensionOwned("pg_ts_dict", "d.oid")}
|
|
623
|
+
ORDER BY n.nspname ${C}, d.dictname ${C}`, s.params);
|
|
624
|
+
for (const r of dictionaries) {
|
|
625
|
+
const q = qname(String(r.schema), String(r.name));
|
|
626
|
+
const options = parseDictionaryOptions(str(r.init));
|
|
627
|
+
out.push({
|
|
628
|
+
type: POSTGRES_ENTITY_TYPES.textSearchDictionary,
|
|
629
|
+
schema: String(r.schema),
|
|
630
|
+
name: String(r.name),
|
|
631
|
+
oid: String(r.oid),
|
|
632
|
+
...withComment(r),
|
|
633
|
+
...dependents(r),
|
|
634
|
+
...(options === undefined ? { unsupported: `its options (${str(r.init)}) are not in the form the server writes them` } : {}),
|
|
635
|
+
statement: [
|
|
636
|
+
`CREATE TEXT SEARCH DICTIONARY ${q} (${["TEMPLATE = " + r.template, ...(options ?? []).map(([k, v]) => `${quoteIdent(k)} = ${literal(v)}`)].join(", ")})`,
|
|
637
|
+
...commentOn(`TEXT SEARCH DICTIONARY ${q}`, r.comment),
|
|
638
|
+
].join(";\n"),
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
const configurations = await client.query(`SELECT c.oid::text AS oid, n.nspname AS schema, c.cfgname AS name,
|
|
642
|
+
(SELECT ${refName("pn.nspname", "p.prsname")} FROM pg_catalog.pg_ts_parser p JOIN pg_catalog.pg_namespace pn ON pn.oid = p.prsnamespace WHERE p.oid = c.cfgparser) AS parser,
|
|
643
|
+
ARRAY(SELECT tt.alias || E'\\t' || pg_catalog.string_agg((SELECT ${refName("dn.nspname", "d.dictname")} FROM pg_catalog.pg_ts_dict d JOIN pg_catalog.pg_namespace dn ON dn.oid = d.dictnamespace WHERE d.oid = m.mapdict), ', ' ORDER BY m.mapseqno)
|
|
644
|
+
FROM pg_catalog.pg_ts_config_map m, pg_catalog.ts_token_type(c.cfgparser) tt WHERE m.mapcfg = c.oid AND tt.tokid = m.maptokentype
|
|
645
|
+
GROUP BY tt.tokid, tt.alias ORDER BY tt.tokid) AS mappings,
|
|
646
|
+
pg_catalog.obj_description(c.oid, 'pg_ts_config') AS comment, ${dependentsOf("pg_ts_config", "c.oid")} AS dependents
|
|
647
|
+
FROM pg_catalog.pg_ts_config c JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace
|
|
648
|
+
WHERE ${s.sql} AND c.oid >= 16384 AND ${notExtensionOwned("pg_ts_config", "c.oid")}
|
|
649
|
+
ORDER BY n.nspname ${C}, c.cfgname ${C}`, s.params);
|
|
650
|
+
for (const r of configurations) {
|
|
651
|
+
const q = qname(String(r.schema), String(r.name));
|
|
652
|
+
// Token types that share their dictionaries share an ADD MAPPING, in the parser's token order.
|
|
653
|
+
const groups = new Map();
|
|
654
|
+
for (const line of r.mappings ?? []) {
|
|
655
|
+
const [token, dictionaries] = line.split("\t");
|
|
656
|
+
groups.set(dictionaries, [...(groups.get(dictionaries) ?? []), quoteIdent(token)]);
|
|
657
|
+
}
|
|
658
|
+
out.push({
|
|
659
|
+
type: POSTGRES_ENTITY_TYPES.textSearchConfiguration,
|
|
660
|
+
schema: String(r.schema),
|
|
661
|
+
name: String(r.name),
|
|
662
|
+
oid: String(r.oid),
|
|
663
|
+
...withComment(r),
|
|
664
|
+
...dependents(r),
|
|
665
|
+
statement: [
|
|
666
|
+
`CREATE TEXT SEARCH CONFIGURATION ${q} (PARSER = ${r.parser})`,
|
|
667
|
+
...[...groups].map(([dicts, tokens]) => `ALTER TEXT SEARCH CONFIGURATION ${q} ADD MAPPING FOR ${tokens.join(", ")} WITH ${dicts}`),
|
|
668
|
+
...commentOn(`TEXT SEARCH CONFIGURATION ${q}`, r.comment),
|
|
669
|
+
].join(";\n"),
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
return out;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Foreign tables: their columns (type, collation, default, NOT NULL and
|
|
676
|
+
* OPTIONS), CHECK constraints, server and OPTIONS. One that is a partition,
|
|
677
|
+
* inherits or has a generated column is read as unsupported, since a
|
|
678
|
+
* declaration does not hold those. An option that holds a credential is left
|
|
679
|
+
* out and said so.
|
|
680
|
+
*/
|
|
681
|
+
async function readForeignTables(client, scope) {
|
|
682
|
+
const s = scopeSql("n.nspname", scope);
|
|
683
|
+
const tables = await client.query(`SELECT c.oid::text AS oid, n.nspname AS schema, c.relname AS name, c.relispartition AS ispartition,
|
|
684
|
+
EXISTS (SELECT 1 FROM pg_catalog.pg_inherits i WHERE i.inhrelid = c.oid) AS inherits,
|
|
685
|
+
fs.srvname AS server, ft.ftoptions AS options,
|
|
686
|
+
pg_catalog.obj_description(c.oid, 'pg_class') AS comment, ${dependentsOf("pg_class", "c.oid")} AS dependents
|
|
687
|
+
FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
688
|
+
JOIN pg_catalog.pg_foreign_table ft ON ft.ftrelid = c.oid JOIN pg_catalog.pg_foreign_server fs ON fs.oid = ft.ftserver
|
|
689
|
+
WHERE c.relkind = 'f' AND ${s.sql} AND ${notExtensionOwned("pg_class", "c.oid")}
|
|
690
|
+
ORDER BY n.nspname ${C}, c.relname ${C}`, s.params);
|
|
691
|
+
if (tables.length === 0)
|
|
692
|
+
return [];
|
|
693
|
+
const oids = tables.map((t) => String(t.oid));
|
|
694
|
+
const columns = await client.query(`SELECT a.attrelid::text AS rel, a.attname AS name, pg_catalog.format_type(a.atttypid, a.atttypmod) AS type, a.attnotnull AS notnull,
|
|
695
|
+
a.attgenerated::text AS generated, pg_catalog.pg_get_expr(d.adbin, d.adrelid, true) AS dflt,
|
|
696
|
+
CASE WHEN a.attcollation <> 0 AND a.attcollation <> t.typcollation THEN ${collationName("a.attcollation")} END AS collation,
|
|
697
|
+
a.attfdwoptions AS options, pg_catalog.col_description(a.attrelid, a.attnum) AS comment
|
|
698
|
+
FROM pg_catalog.pg_attribute AS a JOIN pg_catalog.pg_type t ON t.oid = a.atttypid
|
|
699
|
+
LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
|
|
700
|
+
WHERE a.attrelid = ANY($1::oid[]) AND a.attnum > 0 AND NOT a.attisdropped
|
|
701
|
+
ORDER BY a.attrelid, a.attnum`, [oids]);
|
|
702
|
+
const checks = await client.query(`SELECT k.conrelid::text AS rel, k.conname AS name, pg_catalog.pg_get_constraintdef(k.oid, true) AS def, pg_catalog.obj_description(k.oid, 'pg_constraint') AS comment
|
|
703
|
+
FROM pg_catalog.pg_constraint k
|
|
704
|
+
WHERE k.conrelid = ANY($1::oid[]) AND k.contype = 'c' AND k.conislocal
|
|
705
|
+
ORDER BY k.conrelid, k.conname ${C}`, [oids]);
|
|
706
|
+
return tables.map((t) => {
|
|
707
|
+
const rel = String(t.oid);
|
|
708
|
+
const q = qname(String(t.schema), String(t.name));
|
|
709
|
+
const omitted = [];
|
|
710
|
+
const lines = [];
|
|
711
|
+
const commentLines = [];
|
|
712
|
+
let generated = false;
|
|
713
|
+
for (const c of columns.filter((x) => String(x.rel) === rel)) {
|
|
714
|
+
const parts = [quoteIdent(String(c.name)), String(c.type)];
|
|
715
|
+
const opts = optionsOf(c.options);
|
|
716
|
+
omitted.push(...opts.omitted);
|
|
717
|
+
if (opts.clause)
|
|
718
|
+
parts.push(opts.clause.trim());
|
|
719
|
+
if (str(c.collation))
|
|
720
|
+
parts.push(`COLLATE ${c.collation}`);
|
|
721
|
+
if (str(c.generated))
|
|
722
|
+
generated = true;
|
|
723
|
+
else if (str(c.dflt))
|
|
724
|
+
parts.push(`DEFAULT ${c.dflt}`);
|
|
725
|
+
if (c.notnull === true)
|
|
726
|
+
parts.push("NOT NULL");
|
|
727
|
+
lines.push(parts.join(" "));
|
|
728
|
+
commentLines.push(...commentOn(`COLUMN ${q}.${quoteIdent(String(c.name))}`, c.comment));
|
|
729
|
+
}
|
|
730
|
+
for (const k of checks.filter((x) => String(x.rel) === rel)) {
|
|
731
|
+
lines.push(`CONSTRAINT ${quoteIdent(String(k.name))} ${k.def}`);
|
|
732
|
+
commentLines.push(...commentOn(`CONSTRAINT ${quoteIdent(String(k.name))} ON ${q}`, k.comment));
|
|
733
|
+
}
|
|
734
|
+
const { clause, omitted: tableOmitted } = optionsOf(t.options);
|
|
735
|
+
omitted.push(...tableOmitted);
|
|
736
|
+
const unsupported = t.ispartition === true ? "it is a partition of another table (CREATE FOREIGN TABLE ... PARTITION OF is not declared)" : t.inherits === true ? "it inherits from another table (INHERITS is not declared on a foreign table)" : generated ? "it has a generated column (not declared on a foreign table)" : undefined;
|
|
737
|
+
return {
|
|
738
|
+
type: POSTGRES_ENTITY_TYPES.foreignTable,
|
|
739
|
+
schema: String(t.schema),
|
|
740
|
+
name: String(t.name),
|
|
741
|
+
oid: rel,
|
|
742
|
+
...withComment(t),
|
|
743
|
+
...dependents(t),
|
|
744
|
+
...omittedWarning(omitted),
|
|
745
|
+
...(unsupported ? { unsupported } : {}),
|
|
746
|
+
statement: [
|
|
747
|
+
`CREATE FOREIGN TABLE ${q} (${lines.length > 0 ? `\n ${lines.join(",\n ")}\n` : ""}) SERVER ${quoteIdent(String(t.server))}${clause}`,
|
|
748
|
+
...commentOn(`FOREIGN TABLE ${q}`, t.comment),
|
|
749
|
+
...commentLines,
|
|
750
|
+
].join(";\n"),
|
|
751
|
+
};
|
|
752
|
+
});
|
|
753
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-resources.d.ts","sourceRoot":"","sources":["../../../src/postgres/live/describe-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAGL,KAAK,uBAAuB,EAG7B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAoD,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAC;AAoBjH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAAE,aAAa,EAAE,MAAM,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"describe-resources.d.ts","sourceRoot":"","sources":["../../../src/postgres/live/describe-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAGL,KAAK,uBAAuB,EAG7B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAoD,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAC;AAoBjH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAAE,aAAa,EAAE,MAAM,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAoBtK;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG;IAAE,MAAM,EAAE,kBAAkB,GAAG,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAQxI;AAED,+HAA+H;AAC/H,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GAAG,MAAM,EAAE,GAAG,SAAS,CAWhJ;AAqDD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE;IACP,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,GACnC,OAAO,CAAC,uBAAuB,CAAC,CAMlC"}
|
|
@@ -45,6 +45,8 @@ export function declaredAddress(entity, defaultSchema) {
|
|
|
45
45
|
entity.type === POSTGRES_ENTITY_TYPES.procedure ||
|
|
46
46
|
entity.type === POSTGRES_ENTITY_TYPES.aggregate ||
|
|
47
47
|
entity.type === POSTGRES_ENTITY_TYPES.operator ||
|
|
48
|
+
entity.type === POSTGRES_ENTITY_TYPES.operatorClass ||
|
|
49
|
+
entity.type === POSTGRES_ENTITY_TYPES.operatorFamily ||
|
|
48
50
|
entity.type === POSTGRES_ENTITY_TYPES.trigger ||
|
|
49
51
|
entity.type === POSTGRES_ENTITY_TYPES.policy) {
|
|
50
52
|
const signature = canonicalPgObject(entity.type, entity.props, defaultSchema).signature;
|