@intentius/chant-lexicon-sql 0.124.48 → 0.124.50

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 +251 -3
  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 +275 -3
  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
@@ -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
- * `policy`, `role` and `grant` tagged templates that parse their DDL at fold
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BxB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC"}
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;AAiDD,wEAAwE;AACxE,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAO,CAAC;AA8JrE,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"}
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,CA0E3G"}
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;AA4C7F,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"}
@@ -33,10 +33,12 @@ function commentOn(what, comment) {
33
33
  const own = stripMarker(c);
34
34
  return own === "" ? [] : [`COMMENT ON ${what} IS ${literal(own)}`];
35
35
  }
36
- const notInternal = (catalog, oid) => `NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = 'pg_catalog.${catalog}'::pg_catalog.regclass AND d.objid = ${oid} AND d.deptype = 'i')`;
36
+ const notInternal = (catalog, oid) => `NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend dep_ WHERE dep_.classid = 'pg_catalog.${catalog}'::pg_catalog.regclass AND dep_.objid = ${oid} AND dep_.deptype = 'i')`;
37
37
  /** What depends on an object of catalog `catalog` (a normal dependency), as the server describes each. */
38
- const dependentsOf = (catalog, oid) => `ARRAY(SELECT pg_catalog.pg_describe_object(d.classid, d.objid, d.objsubid) FROM pg_catalog.pg_depend d
39
- WHERE d.refclassid = 'pg_catalog.${catalog}'::pg_catalog.regclass AND d.refobjid = ${oid} AND d.deptype = 'n' ORDER BY 1)`;
38
+ const dependentsOf = (catalog, oid) =>
39
+ // `dep_`, not `d`: a caller's own alias (the text search dictionary query's `d`) would otherwise shadow it.
40
+ `ARRAY(SELECT pg_catalog.pg_describe_object(dep_.classid, dep_.objid, dep_.objsubid) FROM pg_catalog.pg_depend dep_
41
+ WHERE dep_.refclassid = 'pg_catalog.${catalog}'::pg_catalog.regclass AND dep_.refobjid = ${oid} AND dep_.deptype = 'n' ORDER BY 1)`;
40
42
  /** A collation's name as a clause: a `pg_catalog` one bare. */
41
43
  const collationName = (oid) => `(SELECT CASE WHEN cn.nspname = 'pg_catalog' THEN pg_catalog.quote_ident(co.collname) ELSE pg_catalog.quote_ident(cn.nspname) || '.' || pg_catalog.quote_ident(co.collname) END ` +
42
44
  `FROM pg_catalog.pg_collation co JOIN pg_catalog.pg_namespace cn ON cn.oid = co.collnamespace WHERE co.oid = ${oid})`;
@@ -437,6 +439,7 @@ export async function readDatabaseKinds(client, scope) {
437
439
  });
438
440
  }
439
441
  out.push(...(await readPublications(client)));
442
+ out.push(...(await readOperatorClasses(client, scope)), ...(await readTextSearch(client, scope)), ...(await readForeignTables(client, scope)));
440
443
  return out;
441
444
  }
442
445
  /** Publications. Column lists, row filters and schema members are 15's; they are read where the catalog has them. */
@@ -505,3 +508,248 @@ async function readPublications(client) {
505
508
  }
506
509
  return out;
507
510
  }
511
+ // ── Operator classes and families, text search, foreign tables (#4406) ──
512
+ /** An object named by `schema`.`name` columns, as a clause: a `pg_catalog` one bare. */
513
+ 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`;
514
+ /** An operand type, `NONE` for the missing side of a prefix operator. */
515
+ const operand = (column) => `CASE WHEN ${column} = 0 THEN 'NONE' ELSE pg_catalog.format_type(${column}, NULL) END`;
516
+ /**
517
+ * Operator classes and families. A class carries its members: the operators
518
+ * and support functions that depend on it (`pg_depend`, a member made by
519
+ * CREATE OPERATOR CLASS depends on the class; one added by ALTER OPERATOR
520
+ * FAMILY ... ADD depends on the family and is "loose"). A family named as its
521
+ * class, in the same schema, is the one CREATE OPERATOR CLASS made for it and
522
+ * is the class's; every other family is read, with a warning when it holds
523
+ * loose members, which a declaration cannot carry.
524
+ */
525
+ async function readOperatorClasses(client, scope) {
526
+ const out = [];
527
+ const s = scopeSql("n.nspname", scope);
528
+ 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`;
529
+ 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,
530
+ pg_catalog.format_type(c.opcintype, NULL) AS type,
531
+ CASE WHEN c.opckeytype <> 0 THEN pg_catalog.format_type(c.opckeytype, NULL) END AS storage,
532
+ CASE WHEN f.opfname = c.opcname AND f.opfnamespace = c.opcnamespace THEN NULL ELSE ${refName("fn.nspname", "f.opfname")} END AS family,
533
+ ARRAY(SELECT 'OPERATOR ' || o.amopstrategy || ' ' || pg_catalog.quote_ident(opn.nspname) || '.' || op.oprname ||
534
+ ' (' || ${operand("o.amoplefttype")} || ', ' || ${operand("o.amoprighttype")} || ')' ||
535
+ CASE WHEN o.amoppurpose = 'o' THEN ' FOR ORDER BY ' || (SELECT pg_catalog.quote_ident(sn.nspname) || '.' || pg_catalog.quote_ident(sf.opfname)
536
+ FROM pg_catalog.pg_opfamily sf JOIN pg_catalog.pg_namespace sn ON sn.oid = sf.opfnamespace WHERE sf.oid = o.amopsortfamily) ELSE '' END
537
+ FROM pg_catalog.pg_amop o ${member("pg_catalog.pg_amop", "o")}
538
+ JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr JOIN pg_catalog.pg_namespace opn ON opn.oid = op.oprnamespace
539
+ ORDER BY o.amopstrategy, o.amoplefttype, o.amoprighttype) AS operators,
540
+ ARRAY(SELECT 'FUNCTION ' || p.amprocnum || ' (' || ${operand("p.amproclefttype")} || ', ' || ${operand("p.amprocrighttype")} || ') ' || p.amproc::pg_catalog.regprocedure::pg_catalog.text
541
+ FROM pg_catalog.pg_amproc p ${member("pg_catalog.pg_amproc", "p")}
542
+ ORDER BY p.amprocnum, p.amproclefttype, p.amprocrighttype) AS functions,
543
+ pg_catalog.obj_description(c.oid, 'pg_opclass') AS comment, ${dependentsOf("pg_opclass", "c.oid")} AS dependents
544
+ 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
545
+ JOIN pg_catalog.pg_opfamily f ON f.oid = c.opcfamily JOIN pg_catalog.pg_namespace fn ON fn.oid = f.opfnamespace
546
+ WHERE ${s.sql} AND c.oid >= 16384 AND ${notExtensionOwned("pg_opclass", "c.oid")}
547
+ ORDER BY n.nspname ${C}, c.opcname ${C}, am.amname ${C}`, s.params);
548
+ for (const r of classes) {
549
+ const q = qname(String(r.schema), String(r.name));
550
+ const method = String(r.method);
551
+ const items = [...(r.operators ?? []), ...(r.functions ?? []), ...(str(r.storage) ? [`STORAGE ${r.storage}`] : [])];
552
+ out.push({
553
+ type: POSTGRES_ENTITY_TYPES.operatorClass,
554
+ schema: String(r.schema),
555
+ name: String(r.name),
556
+ oid: String(r.oid),
557
+ signature: ` USING ${method}`,
558
+ ...withComment(r),
559
+ ...dependents(r),
560
+ ...(items.length === 0 ? { unsupported: "no operator, support function or storage type of it could be read; a CREATE OPERATOR CLASS needs at least one" } : {}),
561
+ statement: [
562
+ `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 ")}`,
563
+ ...commentOn(`OPERATOR CLASS ${q} USING ${method}`, r.comment),
564
+ ].join(";\n"),
565
+ });
566
+ }
567
+ const families = await client.query(`SELECT f.oid::text AS oid, n.nspname AS schema, f.opfname AS name, am.amname AS method,
568
+ (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
569
+ 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)
570
+ AND dep.objid = m.oid AND dep.refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass)) AS loose,
571
+ pg_catalog.obj_description(f.oid, 'pg_opfamily') AS comment, ${dependentsOf("pg_opfamily", "f.oid")} AS dependents
572
+ 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
573
+ WHERE ${s.sql} AND f.oid >= 16384 AND ${notExtensionOwned("pg_opfamily", "f.oid")}
574
+ 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)
575
+ ORDER BY n.nspname ${C}, f.opfname ${C}, am.amname ${C}`, s.params);
576
+ for (const r of families) {
577
+ const q = qname(String(r.schema), String(r.name));
578
+ const method = String(r.method);
579
+ const loose = Number(r.loose ?? 0);
580
+ out.push({
581
+ type: POSTGRES_ENTITY_TYPES.operatorFamily,
582
+ schema: String(r.schema),
583
+ name: String(r.name),
584
+ oid: String(r.oid),
585
+ signature: ` USING ${method}`,
586
+ ...withComment(r),
587
+ ...dependents(r),
588
+ ...(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`] } : {}),
589
+ statement: [`CREATE OPERATOR FAMILY ${q} USING ${method}`, ...commentOn(`OPERATOR FAMILY ${q} USING ${method}`, r.comment)].join(";\n"),
590
+ });
591
+ }
592
+ return out;
593
+ }
594
+ /**
595
+ * A dictionary's `dictinitoption`, which the server prints as
596
+ * `name = 'value', ...` (a quote and a backslash doubled in the value), as a
597
+ * list of name and value; undefined when the text is not that form.
598
+ */
599
+ export function parseDictionaryOptions(init) {
600
+ if (init === undefined || init.trim() === "")
601
+ return [];
602
+ const out = [];
603
+ const item = /\s*("(?:[^"]|"")+"|[^\s=,]+)\s*=\s*'((?:[^']|'')*)'\s*(?:,|$)/y;
604
+ let at = 0;
605
+ while (at < init.length) {
606
+ item.lastIndex = at;
607
+ const m = item.exec(init);
608
+ if (!m)
609
+ return undefined;
610
+ const name = m[1].startsWith('"') ? m[1].slice(1, -1).replace(/""/g, '"') : m[1];
611
+ out.push([name, m[2].replace(/''/g, "'").replace(/\\\\/g, "\\")]);
612
+ at = item.lastIndex;
613
+ }
614
+ return out;
615
+ }
616
+ /** Text search dictionaries and configurations; a configuration's mappings are the ALTER ... ADD MAPPING statements after its CREATE. */
617
+ async function readTextSearch(client, scope) {
618
+ const out = [];
619
+ const s = scopeSql("n.nspname", scope);
620
+ const dictionaries = await client.query(`SELECT d.oid::text AS oid, n.nspname AS schema, d.dictname AS name, d.dictinitoption AS init,
621
+ (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,
622
+ pg_catalog.obj_description(d.oid, 'pg_ts_dict') AS comment, ${dependentsOf("pg_ts_dict", "d.oid")} AS dependents
623
+ FROM pg_catalog.pg_ts_dict d JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace
624
+ WHERE ${s.sql} AND d.oid >= 16384 AND ${notExtensionOwned("pg_ts_dict", "d.oid")}
625
+ ORDER BY n.nspname ${C}, d.dictname ${C}`, s.params);
626
+ for (const r of dictionaries) {
627
+ const q = qname(String(r.schema), String(r.name));
628
+ const options = parseDictionaryOptions(str(r.init));
629
+ out.push({
630
+ type: POSTGRES_ENTITY_TYPES.textSearchDictionary,
631
+ schema: String(r.schema),
632
+ name: String(r.name),
633
+ oid: String(r.oid),
634
+ ...withComment(r),
635
+ ...dependents(r),
636
+ ...(options === undefined ? { unsupported: `its options (${str(r.init)}) are not in the form the server writes them` } : {}),
637
+ statement: [
638
+ `CREATE TEXT SEARCH DICTIONARY ${q} (${["TEMPLATE = " + r.template, ...(options ?? []).map(([k, v]) => `${quoteIdent(k)} = ${literal(v)}`)].join(", ")})`,
639
+ ...commentOn(`TEXT SEARCH DICTIONARY ${q}`, r.comment),
640
+ ].join(";\n"),
641
+ });
642
+ }
643
+ const configurations = await client.query(`SELECT c.oid::text AS oid, n.nspname AS schema, c.cfgname AS name,
644
+ (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,
645
+ 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)
646
+ 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
647
+ GROUP BY tt.tokid, tt.alias ORDER BY tt.tokid) AS mappings,
648
+ pg_catalog.obj_description(c.oid, 'pg_ts_config') AS comment, ${dependentsOf("pg_ts_config", "c.oid")} AS dependents
649
+ FROM pg_catalog.pg_ts_config c JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace
650
+ WHERE ${s.sql} AND c.oid >= 16384 AND ${notExtensionOwned("pg_ts_config", "c.oid")}
651
+ ORDER BY n.nspname ${C}, c.cfgname ${C}`, s.params);
652
+ for (const r of configurations) {
653
+ const q = qname(String(r.schema), String(r.name));
654
+ // Token types that share their dictionaries share an ADD MAPPING, in the parser's token order.
655
+ const groups = new Map();
656
+ for (const line of r.mappings ?? []) {
657
+ const [token, dictionaries] = line.split("\t");
658
+ groups.set(dictionaries, [...(groups.get(dictionaries) ?? []), quoteIdent(token)]);
659
+ }
660
+ out.push({
661
+ type: POSTGRES_ENTITY_TYPES.textSearchConfiguration,
662
+ schema: String(r.schema),
663
+ name: String(r.name),
664
+ oid: String(r.oid),
665
+ ...withComment(r),
666
+ ...dependents(r),
667
+ statement: [
668
+ `CREATE TEXT SEARCH CONFIGURATION ${q} (PARSER = ${r.parser})`,
669
+ ...[...groups].map(([dicts, tokens]) => `ALTER TEXT SEARCH CONFIGURATION ${q} ADD MAPPING FOR ${tokens.join(", ")} WITH ${dicts}`),
670
+ ...commentOn(`TEXT SEARCH CONFIGURATION ${q}`, r.comment),
671
+ ].join(";\n"),
672
+ });
673
+ }
674
+ return out;
675
+ }
676
+ /**
677
+ * Foreign tables: their columns (type, collation, default, NOT NULL and
678
+ * OPTIONS), CHECK constraints, server and OPTIONS. One that is a partition,
679
+ * inherits or has a generated column is read as unsupported, since a
680
+ * declaration does not hold those. An option that holds a credential is left
681
+ * out and said so.
682
+ */
683
+ async function readForeignTables(client, scope) {
684
+ const s = scopeSql("n.nspname", scope);
685
+ const tables = await client.query(`SELECT c.oid::text AS oid, n.nspname AS schema, c.relname AS name, c.relispartition AS ispartition,
686
+ EXISTS (SELECT 1 FROM pg_catalog.pg_inherits i WHERE i.inhrelid = c.oid) AS inherits,
687
+ fs.srvname AS server, ft.ftoptions AS options,
688
+ pg_catalog.obj_description(c.oid, 'pg_class') AS comment, ${dependentsOf("pg_class", "c.oid")} AS dependents
689
+ FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
690
+ JOIN pg_catalog.pg_foreign_table ft ON ft.ftrelid = c.oid JOIN pg_catalog.pg_foreign_server fs ON fs.oid = ft.ftserver
691
+ WHERE c.relkind = 'f' AND ${s.sql} AND ${notExtensionOwned("pg_class", "c.oid")}
692
+ ORDER BY n.nspname ${C}, c.relname ${C}`, s.params);
693
+ if (tables.length === 0)
694
+ return [];
695
+ const oids = tables.map((t) => String(t.oid));
696
+ 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,
697
+ a.attgenerated::text AS generated, pg_catalog.pg_get_expr(d.adbin, d.adrelid, true) AS dflt,
698
+ CASE WHEN a.attcollation <> 0 AND a.attcollation <> t.typcollation THEN ${collationName("a.attcollation")} END AS collation,
699
+ a.attfdwoptions AS options, pg_catalog.col_description(a.attrelid, a.attnum) AS comment
700
+ FROM pg_catalog.pg_attribute AS a JOIN pg_catalog.pg_type t ON t.oid = a.atttypid
701
+ LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
702
+ WHERE a.attrelid = ANY($1::oid[]) AND a.attnum > 0 AND NOT a.attisdropped
703
+ ORDER BY a.attrelid, a.attnum`, [oids]);
704
+ 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
705
+ FROM pg_catalog.pg_constraint k
706
+ WHERE k.conrelid = ANY($1::oid[]) AND k.contype = 'c' AND k.conislocal
707
+ ORDER BY k.conrelid, k.conname ${C}`, [oids]);
708
+ return tables.map((t) => {
709
+ const rel = String(t.oid);
710
+ const q = qname(String(t.schema), String(t.name));
711
+ const omitted = [];
712
+ const lines = [];
713
+ const commentLines = [];
714
+ let generated = false;
715
+ for (const c of columns.filter((x) => String(x.rel) === rel)) {
716
+ const parts = [quoteIdent(String(c.name)), String(c.type)];
717
+ const opts = optionsOf(c.options);
718
+ omitted.push(...opts.omitted);
719
+ if (opts.clause)
720
+ parts.push(opts.clause.trim());
721
+ if (str(c.collation))
722
+ parts.push(`COLLATE ${c.collation}`);
723
+ if (str(c.generated))
724
+ generated = true;
725
+ else if (str(c.dflt))
726
+ parts.push(`DEFAULT ${c.dflt}`);
727
+ if (c.notnull === true)
728
+ parts.push("NOT NULL");
729
+ lines.push(parts.join(" "));
730
+ commentLines.push(...commentOn(`COLUMN ${q}.${quoteIdent(String(c.name))}`, c.comment));
731
+ }
732
+ for (const k of checks.filter((x) => String(x.rel) === rel)) {
733
+ lines.push(`CONSTRAINT ${quoteIdent(String(k.name))} ${k.def}`);
734
+ commentLines.push(...commentOn(`CONSTRAINT ${quoteIdent(String(k.name))} ON ${q}`, k.comment));
735
+ }
736
+ const { clause, omitted: tableOmitted } = optionsOf(t.options);
737
+ omitted.push(...tableOmitted);
738
+ 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;
739
+ return {
740
+ type: POSTGRES_ENTITY_TYPES.foreignTable,
741
+ schema: String(t.schema),
742
+ name: String(t.name),
743
+ oid: rel,
744
+ ...withComment(t),
745
+ ...dependents(t),
746
+ ...omittedWarning(omitted),
747
+ ...(unsupported ? { unsupported } : {}),
748
+ statement: [
749
+ `CREATE FOREIGN TABLE ${q} (${lines.length > 0 ? `\n ${lines.join(",\n ")}\n` : ""}) SERVER ${quoteIdent(String(t.server))}${clause}`,
750
+ ...commentOn(`FOREIGN TABLE ${q}`, t.comment),
751
+ ...commentLines,
752
+ ].join(";\n"),
753
+ };
754
+ });
755
+ }
@@ -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,CAkBtK;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"}
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"}