@intentius/chant-lexicon-sql 0.124.18 → 0.124.21

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 (198) hide show
  1. package/dist/clickhouse/access.d.ts +36 -7
  2. package/dist/clickhouse/access.d.ts.map +1 -1
  3. package/dist/clickhouse/apply/statements.d.ts.map +1 -1
  4. package/dist/clickhouse/entities.d.ts +60 -0
  5. package/dist/clickhouse/entities.d.ts.map +1 -1
  6. package/dist/clickhouse/import/generator.d.ts +1 -7
  7. package/dist/clickhouse/import/generator.d.ts.map +1 -1
  8. package/dist/clickhouse/import/live-export.d.ts +8 -0
  9. package/dist/clickhouse/import/live-export.d.ts.map +1 -1
  10. package/dist/clickhouse/live/catalog.d.ts +6 -5
  11. package/dist/clickhouse/live/catalog.d.ts.map +1 -1
  12. package/dist/clickhouse/live/describe-resources.d.ts.map +1 -1
  13. package/dist/clickhouse/live/undeclarable.d.ts +32 -0
  14. package/dist/clickhouse/live/undeclarable.d.ts.map +1 -0
  15. package/dist/clickhouse/parser.d.ts +16 -0
  16. package/dist/clickhouse/parser.d.ts.map +1 -1
  17. package/dist/clickhouse/plan/deep.d.ts.map +1 -1
  18. package/dist/clickhouse/plan/diff.d.ts.map +1 -1
  19. package/dist/clickhouse/plan/normalize.d.ts +6 -3
  20. package/dist/clickhouse/plan/normalize.d.ts.map +1 -1
  21. package/dist/clickhouse/plan/rules.d.ts +3 -0
  22. package/dist/clickhouse/plan/rules.d.ts.map +1 -1
  23. package/dist/clickhouse/testing/fake-server.d.ts +6 -0
  24. package/dist/clickhouse/testing/fake-server.d.ts.map +1 -1
  25. package/dist/clickhouse/topology.d.ts.map +1 -1
  26. package/dist/clickhouse.d.ts +1 -1
  27. package/dist/clickhouse.d.ts.map +1 -1
  28. package/dist/codegen/coverage-page.d.ts +7 -0
  29. package/dist/codegen/coverage-page.d.ts.map +1 -0
  30. package/dist/codegen/docs.d.ts.map +1 -1
  31. package/dist/codegen/registry.d.ts.map +1 -1
  32. package/dist/core/undeclarable.d.ts +38 -0
  33. package/dist/core/undeclarable.d.ts.map +1 -0
  34. package/dist/coverage/clickhouse-commands.json +24 -0
  35. package/dist/coverage/clickhouse.json +25 -0
  36. package/dist/coverage/postgres-commands.json +245 -0
  37. package/dist/coverage/postgres.json +51 -0
  38. package/dist/coverage.d.ts +68 -0
  39. package/dist/coverage.d.ts.map +1 -1
  40. package/dist/files/clickhouse.d.ts +2 -1
  41. package/dist/files/clickhouse.d.ts.map +1 -1
  42. package/dist/files/common.d.ts +12 -0
  43. package/dist/files/common.d.ts.map +1 -1
  44. package/dist/files/postgres.d.ts +1 -1
  45. package/dist/files/postgres.d.ts.map +1 -1
  46. package/dist/import-parser.d.ts +7 -2
  47. package/dist/import-parser.d.ts.map +1 -1
  48. package/dist/index.d.ts +2 -2
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/integrity.json +10 -10
  51. package/dist/lint/post-synth/postgres-names.d.ts.map +1 -1
  52. package/dist/lint/post-synth/sql101.d.ts.map +1 -1
  53. package/dist/lint/rules/sqlch001.d.ts +1 -1
  54. package/dist/lint/rules/sqlch001.d.ts.map +1 -1
  55. package/dist/lint/rules/sqlpg001.d.ts.map +1 -1
  56. package/dist/lint/rules/templates.d.ts +1 -1
  57. package/dist/lint/rules/templates.d.ts.map +1 -1
  58. package/dist/lsp/postgres-template.d.ts.map +1 -1
  59. package/dist/manifest.json +1 -1
  60. package/dist/meta.json +40 -0
  61. package/dist/okf/index.md +9 -1
  62. package/dist/okf/rules/SQL101.md +1 -0
  63. package/dist/okf/rules/SQLCH001.md +2 -2
  64. package/dist/okf/rules/SQLPG113.md +2 -0
  65. package/dist/okf/types/PostgresAggregate.md +13 -0
  66. package/dist/okf/types/PostgresCast.md +9 -0
  67. package/dist/okf/types/PostgresCollation.md +9 -0
  68. package/dist/okf/types/PostgresCompositeType.md +13 -0
  69. package/dist/okf/types/PostgresEventTrigger.md +9 -0
  70. package/dist/okf/types/PostgresRangeType.md +13 -0
  71. package/dist/okf/types/Quota.md +9 -0
  72. package/dist/okf/types/SettingsProfile.md +9 -0
  73. package/dist/plugin.d.ts.map +1 -1
  74. package/dist/postgres/apply/apply.d.ts.map +1 -1
  75. package/dist/postgres/apply/statements.d.ts +4 -3
  76. package/dist/postgres/apply/statements.d.ts.map +1 -1
  77. package/dist/postgres/entities.d.ts +129 -6
  78. package/dist/postgres/entities.d.ts.map +1 -1
  79. package/dist/postgres/entity-types.d.ts +6 -0
  80. package/dist/postgres/entity-types.d.ts.map +1 -1
  81. package/dist/postgres/import/generator.d.ts +3 -2
  82. package/dist/postgres/import/generator.d.ts.map +1 -1
  83. package/dist/postgres/import/live-export.d.ts +8 -0
  84. package/dist/postgres/import/live-export.d.ts.map +1 -1
  85. package/dist/postgres/live/catalog-kinds.d.ts +28 -0
  86. package/dist/postgres/live/catalog-kinds.d.ts.map +1 -0
  87. package/dist/postgres/live/catalog.d.ts +23 -8
  88. package/dist/postgres/live/catalog.d.ts.map +1 -1
  89. package/dist/postgres/live/describe-resources.d.ts.map +1 -1
  90. package/dist/postgres/live/undeclarable.d.ts +42 -0
  91. package/dist/postgres/live/undeclarable.d.ts.map +1 -0
  92. package/dist/postgres/parser.d.ts +88 -5
  93. package/dist/postgres/parser.d.ts.map +1 -1
  94. package/dist/postgres/plan/deep.d.ts.map +1 -1
  95. package/dist/postgres/plan/diff.d.ts.map +1 -1
  96. package/dist/postgres/plan/normalize.d.ts +17 -2
  97. package/dist/postgres/plan/normalize.d.ts.map +1 -1
  98. package/dist/postgres/plan/rules.d.ts +3 -0
  99. package/dist/postgres/plan/rules.d.ts.map +1 -1
  100. package/dist/postgres/plan/schema.d.ts +3 -2
  101. package/dist/postgres/plan/schema.d.ts.map +1 -1
  102. package/dist/postgres/testing/fake-client.d.ts +6 -0
  103. package/dist/postgres/testing/fake-client.d.ts.map +1 -1
  104. package/dist/postgres/testing/roundtrip-schema.d.ts +4 -2
  105. package/dist/postgres/testing/roundtrip-schema.d.ts.map +1 -1
  106. package/dist/postgres.d.ts +1 -1
  107. package/dist/postgres.d.ts.map +1 -1
  108. package/dist/rules/postgres-names.ts +15 -2
  109. package/dist/rules/postgres-templates.ts +2 -2
  110. package/dist/rules/sql101.ts +1 -0
  111. package/dist/rules/sqlch001.ts +10 -3
  112. package/dist/rules/sqlpg001.ts +6 -21
  113. package/dist/rules/sqlpg113.ts +1 -1
  114. package/dist/rules/templates.ts +2 -2
  115. package/dist/validate.d.ts.map +1 -1
  116. package/package.json +2 -2
  117. package/src/clickhouse/access.ts +197 -22
  118. package/src/clickhouse/apply/statements.ts +17 -5
  119. package/src/clickhouse/entities.ts +78 -2
  120. package/src/clickhouse/import/generator.ts +16 -3
  121. package/src/clickhouse/import/live-export.ts +17 -2
  122. package/src/clickhouse/live/catalog.ts +57 -10
  123. package/src/clickhouse/live/describe-resources.ts +3 -1
  124. package/src/clickhouse/live/kinds.e2e.test.ts +179 -0
  125. package/src/clickhouse/live/undeclarable.e2e.test.ts +65 -0
  126. package/src/clickhouse/live/undeclarable.ts +66 -0
  127. package/src/clickhouse/parser.test.ts +45 -2
  128. package/src/clickhouse/parser.ts +111 -3
  129. package/src/clickhouse/plan/deep.ts +25 -6
  130. package/src/clickhouse/plan/diff.ts +41 -1
  131. package/src/clickhouse/plan/normalize.ts +12 -4
  132. package/src/clickhouse/plan/rules.ts +22 -1
  133. package/src/clickhouse/quota-profile.test.ts +186 -0
  134. package/src/clickhouse/table-function.test.ts +54 -0
  135. package/src/clickhouse/testdata/server-ddl-baseline.json +17 -14
  136. package/src/clickhouse/testing/fake-server.ts +5 -0
  137. package/src/clickhouse/topology.ts +4 -2
  138. package/src/clickhouse.ts +6 -0
  139. package/src/codegen/classifier-page.test.ts +6 -0
  140. package/src/codegen/coverage-page.ts +77 -0
  141. package/src/codegen/docs.ts +5 -0
  142. package/src/codegen/postgres-classifier-page.ts +1 -1
  143. package/src/codegen/registry.ts +8 -0
  144. package/src/core/undeclarable.test.ts +141 -0
  145. package/src/core/undeclarable.ts +66 -0
  146. package/src/coverage/clickhouse-commands.json +24 -0
  147. package/src/coverage/clickhouse.json +25 -0
  148. package/src/coverage/postgres-commands.json +245 -0
  149. package/src/coverage/postgres.json +51 -0
  150. package/src/coverage.test.ts +78 -0
  151. package/src/coverage.ts +142 -0
  152. package/src/files/clickhouse.ts +20 -7
  153. package/src/files/common.ts +18 -0
  154. package/src/files/postgres.ts +25 -6
  155. package/src/generated/lexicon-sql.json +40 -0
  156. package/src/import-parser.ts +12 -4
  157. package/src/index.ts +2 -2
  158. package/src/lint/post-synth/postgres-names.ts +15 -2
  159. package/src/lint/post-synth/sql101.ts +1 -0
  160. package/src/lint/post-synth/sqlpg113.ts +1 -1
  161. package/src/lint/rules/postgres-templates.ts +2 -2
  162. package/src/lint/rules/sqlch001.ts +10 -3
  163. package/src/lint/rules/sqlpg001.ts +6 -21
  164. package/src/lint/rules/templates.ts +2 -2
  165. package/src/lsp/postgres-template.ts +14 -0
  166. package/src/mcp/index.ts +5 -5
  167. package/src/mcp/postgres.ts +1 -1
  168. package/src/plugin.ts +13 -2
  169. package/src/postgres/apply/apply.ts +8 -2
  170. package/src/postgres/apply/statements.ts +29 -6
  171. package/src/postgres/entities.test.ts +2 -2
  172. package/src/postgres/entities.ts +316 -28
  173. package/src/postgres/entity-types.ts +6 -0
  174. package/src/postgres/import/generator.ts +31 -4
  175. package/src/postgres/import/live-export.ts +25 -2
  176. package/src/postgres/kinds.test.ts +140 -0
  177. package/src/postgres/live/catalog-kinds.ts +312 -0
  178. package/src/postgres/live/catalog.ts +33 -11
  179. package/src/postgres/live/describe-resources.ts +14 -5
  180. package/src/postgres/live/kinds.e2e.test.ts +182 -0
  181. package/src/postgres/live/roundtrip.e2e.test.ts +9 -3
  182. package/src/postgres/live/undeclarable.e2e.test.ts +83 -0
  183. package/src/postgres/live/undeclarable.ts +141 -0
  184. package/src/postgres/parser.test.ts +2 -1
  185. package/src/postgres/parser.ts +307 -17
  186. package/src/postgres/plan/deep.ts +33 -0
  187. package/src/postgres/plan/diff.ts +45 -0
  188. package/src/postgres/plan/normalize.ts +153 -2
  189. package/src/postgres/plan/rules.ts +21 -0
  190. package/src/postgres/plan/schema.ts +6 -4
  191. package/src/postgres/testing/fake-client.ts +5 -0
  192. package/src/postgres/testing/roundtrip-schema.ts +19 -1
  193. package/src/postgres.ts +19 -0
  194. package/src/skill-defs.ts +1 -1
  195. package/src/skills/chant-sql-postgres.md +7 -3
  196. package/src/skills/chant-sql.md +3 -1
  197. package/src/unknown-clauses.test.ts +2 -1
  198. package/src/validate.ts +8 -0
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * The Postgres entities: `schema`, `table`, `index`, `view`, `sequence`,
3
- * `type`, `domain`, `extension`, `func`, `procedure`, `trigger`, `policy`,
4
- * `role` and `grant` tagged templates that parse their DDL at fold time into
5
- * one Declarable each (chant #3278, #3279, #3680, #3681).
3
+ * `type` (enum, composite and range), `domain`, `extension`, `func`,
4
+ * `procedure`, `aggregate`, `trigger`, `eventTrigger`, `collation`, `cast`,
5
+ * `policy`, `role` and `grant` tagged templates that parse their DDL at fold
6
+ * time into one Declarable each (chant #3278, #3279, #3680, #3681, #3911).
6
7
  *
7
8
  * ```ts
8
9
  * import { schema, table, index } from "@intentius/chant-lexicon-sql/postgres";
@@ -56,7 +57,7 @@
56
57
  import type { AttrRef } from "@intentius/chant/attrref";
57
58
  import { setInterpolationFields } from "@intentius/chant/provenance";
58
59
  import { isTrivia, POSTGRES_LEXICAL, SqlSyntaxError, untokenize, type Token } from "./tokens";
59
- import { identValue, parseStatements, type ColumnNode, type CommentNode, type ConstraintNode, type GrantNode, type NameNode, type RowSecurityNode, type Span, type StatementNode } from "./parser";
60
+ import { identValue, parseStatements, type ColumnNode, type CommentNode, type ConstraintNode, type DefElem, type GrantNode, type NameNode, type RowSecurityNode, type Span, type StatementNode } from "./parser";
60
61
  import { keywordCategory, quoteIdent } from "./keywords";
61
62
  import { feed, lineage, spanText as text, splice as spliceWith, templateSyntaxError, unparsedClauses, type TemplateCtx, type TemplateDialect, type UnparsedClause } from "../core/interpolation";
62
63
  import { SqlObject, isColumnRefOf, isSqlObjectOf, makeSqlEntity } from "../core/entity";
@@ -260,6 +261,86 @@ export interface EnumProps extends CommonProps {
260
261
  labels: string[];
261
262
  }
262
263
 
264
+ /** A composite type's attribute, as written. */
265
+ export interface CompositeAttributeDef {
266
+ name: string;
267
+ type: string;
268
+ collate?: string;
269
+ }
270
+
271
+ export interface CompositeTypeProps extends CommonProps {
272
+ attributes: CompositeAttributeDef[];
273
+ }
274
+
275
+ export interface RangeTypeProps extends CommonProps {
276
+ /** `SUBTYPE`: the element type. */
277
+ subtype: string;
278
+ subtypeOpclass?: string;
279
+ collation?: string;
280
+ /** `CANONICAL`: the function that canonicalizes a value (a C function). */
281
+ canonical?: string;
282
+ subtypeDiff?: string;
283
+ /** `MULTIRANGE_TYPE_NAME`: the multirange type's name, when it is not the one Postgres derives. */
284
+ multirangeTypeName?: string;
285
+ }
286
+
287
+ /** One argument of an aggregate, as written. */
288
+ export interface AggregateArgDef {
289
+ /** `in` when not written. */
290
+ mode: "in" | "variadic";
291
+ name?: string;
292
+ type: string;
293
+ }
294
+
295
+ export interface AggregateProps extends CommonProps {
296
+ orReplace?: boolean;
297
+ /** The arguments; empty for `(*)`. An ordered-set aggregate's direct arguments. */
298
+ args: AggregateArgDef[];
299
+ /** An ordered-set aggregate's aggregated arguments (`ORDER BY ...`). */
300
+ orderBy?: AggregateArgDef[];
301
+ /**
302
+ * The definition: each option by its name, folded (`sfunc`, `stype`,
303
+ * `initcond`), to its value as written; an option given without a value
304
+ * (`HYPOTHETICAL`, `FINALFUNC_EXTRA`) to `true`.
305
+ */
306
+ options: Record<string, string>;
307
+ }
308
+
309
+ export interface CollationProps extends CommonProps {
310
+ ifNotExists?: boolean;
311
+ /** Each option by its name, folded (`provider`, `locale`, `deterministic`), to its value as written. */
312
+ options: Record<string, string>;
313
+ }
314
+
315
+ export interface CastProps extends Omit<CommonProps, "schema"> {
316
+ /** `(source AS target)`: a cast is named by its two types and lives in no schema. */
317
+ name: string;
318
+ /** The source type as written. */
319
+ sourceType: string;
320
+ /** The target type as written. */
321
+ targetType: string;
322
+ /** `WITH FUNCTION` (`function`), `WITHOUT FUNCTION` (`binary`) or `WITH INOUT` (`inout`). */
323
+ method: "function" | "binary" | "inout";
324
+ /** The cast function: the entity when interpolated, else its name. */
325
+ function?: PostgresObject | string;
326
+ /** The cast function's SQL name. */
327
+ functionName?: string;
328
+ /** The cast function's parameter types, when written. */
329
+ functionArgs?: string;
330
+ /** `explicit` (the default), `AS ASSIGNMENT` or `AS IMPLICIT`. */
331
+ context: "explicit" | "assignment" | "implicit";
332
+ }
333
+
334
+ export interface EventTriggerProps extends Omit<CommonProps, "schema"> {
335
+ /** `ddl_command_start`, `ddl_command_end`, `sql_drop`, `table_rewrite` or `login`. */
336
+ event: string;
337
+ /** `WHEN TAG IN (...)`: the command tags it fires for; every one when absent. */
338
+ tags?: string[];
339
+ /** The function it executes: the entity when interpolated, else its name. */
340
+ function: PostgresObject | string;
341
+ functionName: string;
342
+ }
343
+
263
344
  export interface DomainProps extends CommonProps {
264
345
  /** The underlying data type. */
265
346
  dataType: string;
@@ -488,11 +569,44 @@ export interface PostgresEnum extends PostgresObject {
488
569
  readonly props: EnumProps;
489
570
  }
490
571
 
572
+ export interface PostgresCompositeType extends PostgresObject {
573
+ readonly entityType: "Postgres::CompositeType";
574
+ readonly props: CompositeTypeProps;
575
+ }
576
+
577
+ export interface PostgresRangeType extends PostgresObject {
578
+ readonly entityType: "Postgres::RangeType";
579
+ readonly props: RangeTypeProps;
580
+ }
581
+
582
+ /** What the `type` tag declares: an enum, a composite or a range type. */
583
+ export type PostgresType = PostgresEnum | PostgresCompositeType | PostgresRangeType;
584
+
491
585
  export interface PostgresDomain extends PostgresObject {
492
586
  readonly entityType: "Postgres::Domain";
493
587
  readonly props: DomainProps;
494
588
  }
495
589
 
590
+ export interface PostgresAggregate extends PostgresObject {
591
+ readonly entityType: "Postgres::Aggregate";
592
+ readonly props: AggregateProps;
593
+ }
594
+
595
+ export interface PostgresCollation extends PostgresObject {
596
+ readonly entityType: "Postgres::Collation";
597
+ readonly props: CollationProps;
598
+ }
599
+
600
+ export interface PostgresCast extends PostgresObject {
601
+ readonly entityType: "Postgres::Cast";
602
+ readonly props: CastProps;
603
+ }
604
+
605
+ export interface PostgresEventTrigger extends PostgresObject {
606
+ readonly entityType: "Postgres::EventTrigger";
607
+ readonly props: EventTriggerProps;
608
+ }
609
+
496
610
  export interface PostgresExtension extends PostgresObject {
497
611
  readonly entityType: "Postgres::Extension";
498
612
  readonly props: ExtensionProps;
@@ -637,23 +751,46 @@ export const POSTGRES_TEMPLATES: TemplateDialect = {
637
751
 
638
752
  type Ctx = TemplateCtx;
639
753
 
640
- export type PostgresTag = "schema" | "table" | "index" | "view" | "sequence" | "type" | "domain" | "extension" | "func" | "procedure" | "trigger" | "policy" | "role" | "grant";
641
-
642
- const STATEMENT_OF: Record<PostgresTag, StatementNode["statement"]> = {
643
- schema: "schema",
644
- table: "table",
645
- index: "index",
646
- view: "view",
647
- sequence: "sequence",
648
- type: "enum",
649
- domain: "domain",
650
- extension: "extension",
651
- func: "function",
652
- procedure: "procedure",
653
- trigger: "trigger",
654
- policy: "policy",
655
- role: "role",
656
- grant: "grant",
754
+ export type PostgresTag =
755
+ | "schema"
756
+ | "table"
757
+ | "index"
758
+ | "view"
759
+ | "sequence"
760
+ | "type"
761
+ | "domain"
762
+ | "extension"
763
+ | "func"
764
+ | "procedure"
765
+ | "aggregate"
766
+ | "trigger"
767
+ | "eventTrigger"
768
+ | "collation"
769
+ | "cast"
770
+ | "policy"
771
+ | "role"
772
+ | "grant";
773
+
774
+ /** The statements each tag holds. */
775
+ export const STATEMENTS_OF: Record<PostgresTag, readonly StatementNode["statement"][]> = {
776
+ schema: ["schema"],
777
+ table: ["table"],
778
+ index: ["index"],
779
+ view: ["view"],
780
+ sequence: ["sequence"],
781
+ type: ["enum", "composite", "range"],
782
+ domain: ["domain"],
783
+ extension: ["extension"],
784
+ func: ["function"],
785
+ procedure: ["procedure"],
786
+ aggregate: ["aggregate"],
787
+ trigger: ["trigger"],
788
+ eventTrigger: ["eventTrigger"],
789
+ collation: ["collation"],
790
+ cast: ["cast"],
791
+ policy: ["policy"],
792
+ role: ["role"],
793
+ grant: ["grant"],
657
794
  };
658
795
 
659
796
  const TAG_OF: Record<StatementNode["statement"], PostgresTag | undefined> = {
@@ -663,11 +800,17 @@ const TAG_OF: Record<StatementNode["statement"], PostgresTag | undefined> = {
663
800
  view: "view",
664
801
  sequence: "sequence",
665
802
  enum: "type",
803
+ composite: "type",
804
+ range: "type",
666
805
  domain: "domain",
667
806
  extension: "extension",
668
807
  function: "func",
669
808
  procedure: "procedure",
809
+ aggregate: "aggregate",
670
810
  trigger: "trigger",
811
+ eventTrigger: "eventTrigger",
812
+ collation: "collation",
813
+ cast: "cast",
671
814
  policy: "policy",
672
815
  role: "role",
673
816
  grant: "grant",
@@ -683,11 +826,17 @@ export const STATEMENT_NAMES: Record<StatementNode["statement"], string> = {
683
826
  view: "CREATE VIEW",
684
827
  sequence: "CREATE SEQUENCE",
685
828
  enum: "CREATE TYPE",
829
+ composite: "CREATE TYPE",
830
+ range: "CREATE TYPE",
686
831
  domain: "CREATE DOMAIN",
687
832
  extension: "CREATE EXTENSION",
688
833
  function: "CREATE FUNCTION",
689
834
  procedure: "CREATE PROCEDURE",
835
+ aggregate: "CREATE AGGREGATE",
690
836
  trigger: "CREATE TRIGGER",
837
+ eventTrigger: "CREATE EVENT TRIGGER",
838
+ collation: "CREATE COLLATION",
839
+ cast: "CREATE CAST",
691
840
  policy: "CREATE POLICY",
692
841
  role: "CREATE ROLE",
693
842
  grant: "GRANT",
@@ -715,6 +864,13 @@ export function stringValue(s: string | undefined): string | undefined {
715
864
  return s;
716
865
  }
717
866
 
867
+ /** A definition list's values, by option name: each value as written, interpolations rendered; an option with no value is `true`. */
868
+ function definitionValues(ctx: Ctx, elems: readonly DefElem[], path: string): Record<string, string> {
869
+ const out: Record<string, string> = {};
870
+ for (const e of elems) out[e.name] = e.value ? req(text(ctx, e.value, path)) : "true";
871
+ return out;
872
+ }
873
+
718
874
  /** A qualified name's schema and name. An interpolated schema names the schema; an interpolated object gives both. */
719
875
  function qualified(ctx: Ctx, n: NameNode): { schema?: string; name: string } {
720
876
  const pieces: string[] = [];
@@ -926,7 +1082,12 @@ function applyComments(
926
1082
  if (value !== undefined) constraint!.comment = value;
927
1083
  } else {
928
1084
  if (!objectTypes.includes(c.objectType)) refuse(c, `does not comment on the ${tag} this template declares`);
929
- if (c.objectType === "TRIGGER" || c.objectType === "POLICY") {
1085
+ if (c.objectType === "CAST") {
1086
+ // `COMMENT ON CAST (source AS target)`: the two types, as the CREATE CAST writes them.
1087
+ const loose = (x: string) => x.toLowerCase().replace(/\s+/g, " ").replace(/\s*([(),.])\s*/g, "$1");
1088
+ const named = `(${req(text(ctx, c.cast!.source))} AS ${req(text(ctx, c.cast!.target))})`;
1089
+ if (loose(named) !== loose(self.name)) refuse(c, `names another cast than ${self.name}`);
1090
+ } else if (c.objectType === "TRIGGER" || c.objectType === "POLICY") {
930
1091
  // `COMMENT ON TRIGGER name ON table`: the name is the trigger's, the table its own.
931
1092
  if (qualified(ctx, c.target).name !== self.name || !c.on || !isSelf({ ...qualified(ctx, c.on), name: self.name })) refuse(c, `names another ${c.objectType.toLowerCase()} than ${self.name}`);
932
1093
  } else if (!isSelf(qualified(ctx, c.target))) refuse(c, `names another object than ${sqlNameOf(self)}`);
@@ -1152,7 +1313,7 @@ function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string
1152
1313
  throw templateSyntaxError(tag, parts, err);
1153
1314
  }
1154
1315
  const [node, ...rest] = nodes as [StatementNode, ...StatementNode[]];
1155
- if (node.statement !== STATEMENT_OF[tag]) {
1316
+ if (!STATEMENTS_OF[tag].includes(node.statement)) {
1156
1317
  const holds = STATEMENT_NAMES[node.statement];
1157
1318
  const use = TAG_OF[node.statement];
1158
1319
  throw new SqlTemplateError(
@@ -1194,9 +1355,9 @@ function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string
1194
1355
  const dependsOn = [...new Set([...values, ...(joined?.refs ?? [])].filter((v) => isPostgresObject(v) || isColumnRef(v)))];
1195
1356
  const slots: TypedSlot[] = [];
1196
1357
  const unparsed = unparsedClauses(ctx, nodes.flatMap((n) => ("unknown" in n && n.unknown ? n.unknown : [])));
1197
- const make = <T extends PostgresObject>(type: PostgresEntityType, q: { schema?: string; name: string }, props: object, columnNames?: readonly string[]): T => {
1358
+ const make = <T extends PostgresObject>(type: PostgresEntityType, q: { schema?: string; name: string }, props: object, columnNames?: readonly string[], sqlName = sqlNameOf(q)): T => {
1198
1359
  if (unparsed) (props as { unparsed?: UnparsedClause[] }).unparsed = unparsed;
1199
- const entity = makeSqlEntity(PostgresObject.prototype as PostgresObject, type, sqlNameOf(q), props, columnNames, dependsOn) as T;
1360
+ const entity = makeSqlEntity(PostgresObject.prototype as PostgresObject, type, sqlName, props, columnNames, dependsOn) as T;
1200
1361
  const fed = ctx.fed.map((paths) => [...paths].sort());
1201
1362
  if (!joined) setInterpolationFields(entity, fed);
1202
1363
  else {
@@ -1239,6 +1400,99 @@ function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string
1239
1400
  const props: EnumProps = stripUnset({ ...q, labels: node.labels.map((l) => req(stringValue(text(ctx, l, "labels")))), comment, ddl, source });
1240
1401
  return make(POSTGRES_ENTITY_TYPES.enum, q, props);
1241
1402
  }
1403
+ case "composite": {
1404
+ const q = qualified(ctx, node.name);
1405
+ const comment = applyComments(tag, ctx, comments, q, ["TYPE"]);
1406
+ const attributes = node.attributes.map((a) => {
1407
+ feed(ctx, a.nameSpan, "attributes");
1408
+ return stripUnset({ name: columnName(ctx, { name: a.name, span: a.nameSpan }), type: req(text(ctx, a.type, "attributes")), collate: text(ctx, a.collate, "attributes") }) as CompositeAttributeDef;
1409
+ });
1410
+ const props: CompositeTypeProps = strip({ ...q, attributes, comment, ddl, source }, ["attributes"]);
1411
+ return make(POSTGRES_ENTITY_TYPES.composite, q, props);
1412
+ }
1413
+ case "range": {
1414
+ const q = qualified(ctx, node.name);
1415
+ const comment = applyComments(tag, ctx, comments, q, ["TYPE"]);
1416
+ const opt = definitionValues(ctx, node.options, "options");
1417
+ const props: RangeTypeProps = stripUnset({
1418
+ ...q,
1419
+ subtype: req(opt.subtype),
1420
+ subtypeOpclass: opt.subtype_opclass,
1421
+ collation: opt.collation,
1422
+ canonical: opt.canonical,
1423
+ subtypeDiff: opt.subtype_diff,
1424
+ multirangeTypeName: opt.multirange_type_name,
1425
+ comment,
1426
+ ddl,
1427
+ source,
1428
+ });
1429
+ return make(POSTGRES_ENTITY_TYPES.range, q, props);
1430
+ }
1431
+ case "aggregate": {
1432
+ const q = qualified(ctx, node.name);
1433
+ const comment = applyComments(tag, ctx, comments, q, ["AGGREGATE"]);
1434
+ const arg = (a: (typeof node.args)[number]): AggregateArgDef => stripUnset({ mode: (a.mode?.toLowerCase() ?? "in") as AggregateArgDef["mode"], name: a.name, type: req(text(ctx, a.type, "args")) });
1435
+ const props: AggregateProps = strip(
1436
+ {
1437
+ ...q,
1438
+ orReplace: node.orReplace || undefined,
1439
+ args: node.args.map(arg),
1440
+ orderBy: node.orderBy?.map(arg),
1441
+ options: definitionValues(ctx, node.options, "options"),
1442
+ comment,
1443
+ ddl,
1444
+ source,
1445
+ },
1446
+ ["args"],
1447
+ );
1448
+ return make(POSTGRES_ENTITY_TYPES.aggregate, q, props);
1449
+ }
1450
+ case "collation": {
1451
+ const q = qualified(ctx, node.name);
1452
+ const comment = applyComments(tag, ctx, comments, q, ["COLLATION"]);
1453
+ const props: CollationProps = stripUnset({ ...q, ifNotExists: node.ifNotExists || undefined, options: definitionValues(ctx, node.options, "options"), comment, ddl, source });
1454
+ return make(POSTGRES_ENTITY_TYPES.collation, q, props);
1455
+ }
1456
+ case "cast": {
1457
+ const sourceType = req(text(ctx, node.source, "sourceType"));
1458
+ const targetType = req(text(ctx, node.target, "targetType"));
1459
+ const name = `(${sourceType} AS ${targetType})`;
1460
+ const comment = applyComments(tag, ctx, comments, { name }, ["CAST"]);
1461
+ const props: CastProps = stripUnset({
1462
+ name,
1463
+ sourceType,
1464
+ targetType,
1465
+ method: node.method,
1466
+ function: node.function ? target(ctx, node.function) : undefined,
1467
+ functionName: node.function ? targetName(ctx, node.function) : undefined,
1468
+ functionArgs: node.functionArgs ? text(ctx, node.functionArgs, "functionArgs") : undefined,
1469
+ context: node.context,
1470
+ comment,
1471
+ ddl,
1472
+ source,
1473
+ });
1474
+ return make(POSTGRES_ENTITY_TYPES.cast, { name }, props, undefined, name);
1475
+ }
1476
+ case "eventTrigger": {
1477
+ const name = node.name.pieces[0] || identValue(req(text(ctx, node.name.span)));
1478
+ feed(ctx, node.name.span, "name");
1479
+ const comment = applyComments(tag, ctx, comments, { name }, ["EVENT TRIGGER"]);
1480
+ const tags = node.filters.flatMap((f) => f.values.map((v) => req(stringValue(text(ctx, v, "tags")))));
1481
+ const props: EventTriggerProps = strip(
1482
+ {
1483
+ name,
1484
+ event: node.event.name || identValue(req(text(ctx, node.event.span, "event"))),
1485
+ tags: tags.length > 0 ? tags : undefined,
1486
+ function: target(ctx, node.function),
1487
+ functionName: targetName(ctx, node.function),
1488
+ comment,
1489
+ ddl,
1490
+ source,
1491
+ },
1492
+ [],
1493
+ );
1494
+ return make(POSTGRES_ENTITY_TYPES.eventTrigger, { name }, props);
1495
+ }
1242
1496
  case "domain": {
1243
1497
  const q = qualified(ctx, node.name);
1244
1498
  const checks = node.checks.map((c) => stripUnset({ name: c.name, expr: req(text(ctx, c.expr, "checks")), notValid: c.notValid }) as CheckDef);
@@ -1644,9 +1898,13 @@ export function sequence(strings: TemplateStringsArray, ...values: unknown[]): P
1644
1898
  return build("sequence", strings, values) as PostgresSequence;
1645
1899
  }
1646
1900
 
1647
- /** `` type`CREATE TYPE name AS ENUM (...)` ``: one enum type. */
1648
- export function type(strings: TemplateStringsArray, ...values: unknown[]): PostgresEnum {
1649
- return build("type", strings, values) as PostgresEnum;
1901
+ /**
1902
+ * `` type`CREATE TYPE name AS ENUM (...)` ``: one enum type; `AS (attribute
1903
+ * type, ...)` a composite type, `AS RANGE (SUBTYPE = ...)` a range type (and
1904
+ * its multirange type, which Postgres creates with it).
1905
+ */
1906
+ export function type(strings: TemplateStringsArray, ...values: unknown[]): PostgresType {
1907
+ return build("type", strings, values) as PostgresType;
1650
1908
  }
1651
1909
 
1652
1910
  /** `` domain`CREATE DOMAIN ...` ``: one domain. */
@@ -1673,6 +1931,36 @@ export function procedure(strings: TemplateStringsArray, ...values: unknown[]):
1673
1931
  return build("procedure", strings, values) as PostgresProcedure;
1674
1932
  }
1675
1933
 
1934
+ /**
1935
+ * `` aggregate`CREATE [OR REPLACE] AGGREGATE name(argtype, ...) (SFUNC = ..., STYPE = ...)` ``:
1936
+ * one aggregate, identified, as a function is, by its argument types.
1937
+ */
1938
+ export function aggregate(strings: TemplateStringsArray, ...values: unknown[]): PostgresAggregate {
1939
+ return build("aggregate", strings, values) as PostgresAggregate;
1940
+ }
1941
+
1942
+ /** `` collation`CREATE COLLATION name (provider = icu, locale = '...')` ``: one collation. */
1943
+ export function collation(strings: TemplateStringsArray, ...values: unknown[]): PostgresCollation {
1944
+ return build("collation", strings, values) as PostgresCollation;
1945
+ }
1946
+
1947
+ /**
1948
+ * `` cast`CREATE CAST (source AS target) WITH FUNCTION ${fn}(source) [AS ASSIGNMENT | AS IMPLICIT]` ``:
1949
+ * one cast, named by its two types. Creating one takes owning the source or
1950
+ * the target type.
1951
+ */
1952
+ export function cast(strings: TemplateStringsArray, ...values: unknown[]): PostgresCast {
1953
+ return build("cast", strings, values) as PostgresCast;
1954
+ }
1955
+
1956
+ /**
1957
+ * `` eventTrigger`CREATE EVENT TRIGGER name ON ddl_command_end WHEN TAG IN ('CREATE TABLE') EXECUTE FUNCTION ${fn}()` ``:
1958
+ * one event trigger, the database's (no schema). Creating one takes a superuser.
1959
+ */
1960
+ export function eventTrigger(strings: TemplateStringsArray, ...values: unknown[]): PostgresEventTrigger {
1961
+ return build("eventTrigger", strings, values) as PostgresEventTrigger;
1962
+ }
1963
+
1676
1964
  /** `` trigger`CREATE [OR REPLACE] [CONSTRAINT] TRIGGER name ... ON ${table} ... EXECUTE FUNCTION ${fn}()` ``: one trigger on its table. */
1677
1965
  export function trigger(strings: TemplateStringsArray, ...values: unknown[]): PostgresTrigger {
1678
1966
  return build("trigger", strings, values) as PostgresTrigger;
@@ -11,7 +11,13 @@ export const POSTGRES_ENTITY_TYPES = {
11
11
  materializedView: "Postgres::MaterializedView",
12
12
  sequence: "Postgres::Sequence",
13
13
  enum: "Postgres::Enum",
14
+ composite: "Postgres::CompositeType",
15
+ range: "Postgres::RangeType",
14
16
  domain: "Postgres::Domain",
17
+ aggregate: "Postgres::Aggregate",
18
+ collation: "Postgres::Collation",
19
+ cast: "Postgres::Cast",
20
+ eventTrigger: "Postgres::EventTrigger",
15
21
  extension: "Postgres::Extension",
16
22
  function: "Postgres::Function",
17
23
  procedure: "Postgres::Procedure",
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The import IR as TypeScript: one `schema.ts` holding every imported object
3
- * as a `schema`, `table`, `index`, `view`, `sequence`, `type`, `domain` or
4
- * `extension` declaration, in dependency order, with the references between
3
+ * as a `schema`, `table`, `index`, `view`, `sequence`, `type`, `domain`,
4
+ * `extension`, `func`, `aggregate`, `collation`, `cast`, `eventTrigger` (and
5
+ * the rest of the tags) declaration, in dependency order, with the references between
5
6
  * them interpolated, so the build orders them as the server did:
6
7
  *
7
8
  * - a qualified name of another imported object becomes `${export}`
@@ -52,7 +53,13 @@ const TAG: Record<string, string> = {
52
53
  [POSTGRES_ENTITY_TYPES.materializedView]: "view",
53
54
  [POSTGRES_ENTITY_TYPES.sequence]: "sequence",
54
55
  [POSTGRES_ENTITY_TYPES.enum]: "type",
56
+ [POSTGRES_ENTITY_TYPES.composite]: "type",
57
+ [POSTGRES_ENTITY_TYPES.range]: "type",
55
58
  [POSTGRES_ENTITY_TYPES.domain]: "domain",
59
+ [POSTGRES_ENTITY_TYPES.aggregate]: "aggregate",
60
+ [POSTGRES_ENTITY_TYPES.collation]: "collation",
61
+ [POSTGRES_ENTITY_TYPES.cast]: "cast",
62
+ [POSTGRES_ENTITY_TYPES.eventTrigger]: "eventTrigger",
56
63
  [POSTGRES_ENTITY_TYPES.extension]: "extension",
57
64
  [POSTGRES_ENTITY_TYPES.function]: "func",
58
65
  [POSTGRES_ENTITY_TYPES.procedure]: "procedure",
@@ -63,6 +70,19 @@ const TAG: Record<string, string> = {
63
70
  [POSTGRES_ENTITY_TYPES.defaultPrivileges]: "grant",
64
71
  };
65
72
 
73
+ /** The kinds no other object's DDL names. */
74
+ const UNREFERENCED: ReadonlySet<string> = new Set([
75
+ POSTGRES_ENTITY_TYPES.index,
76
+ POSTGRES_ENTITY_TYPES.trigger,
77
+ POSTGRES_ENTITY_TYPES.policy,
78
+ POSTGRES_ENTITY_TYPES.grant,
79
+ POSTGRES_ENTITY_TYPES.defaultPrivileges,
80
+ POSTGRES_ENTITY_TYPES.role,
81
+ POSTGRES_ENTITY_TYPES.extension,
82
+ POSTGRES_ENTITY_TYPES.cast,
83
+ POSTGRES_ENTITY_TYPES.eventTrigger,
84
+ ]);
85
+
66
86
  /** The tag each entity type is declared with (`table`, `func`, ...). */
67
87
  export const POSTGRES_TAG_OF: Readonly<Record<string, string>> = TAG;
68
88
 
@@ -104,6 +124,13 @@ function bareSpots(tokens: Token[]): Set<number> {
104
124
  for (const node of nodes) {
105
125
  if (node.statement === "table") for (const c of node.columns) if (c.type) first(c.type.from, c.type.to);
106
126
  if (node.statement === "domain") first(node.type.from, node.type.to);
127
+ if (node.statement === "composite") for (const a of node.attributes) first(a.type.from, a.type.to);
128
+ if (node.statement === "cast") {
129
+ first(node.source.from, node.source.to);
130
+ first(node.target.from, node.target.to);
131
+ name(node.function);
132
+ }
133
+ if (node.statement === "eventTrigger") name(node.function);
107
134
  if (node.statement === "index" || node.statement === "policy") name(node.table);
108
135
  if (node.statement === "trigger") {
109
136
  name(node.table);
@@ -202,8 +229,8 @@ export function postgresTemplates(items: readonly Item[], targets: readonly Item
202
229
  const schemaExport = new Map<string, string>();
203
230
  for (const it of targets) {
204
231
  if (it.type === POSTGRES_ENTITY_TYPES.schema) schemaExport.set(it.name, it.exportName);
205
- // An index or a trigger is never referenced by name in another object's DDL.
206
- else if (it.type !== POSTGRES_ENTITY_TYPES.index && it.type !== POSTGRES_ENTITY_TYPES.trigger && it.type !== POSTGRES_ENTITY_TYPES.policy && it.type !== POSTGRES_ENTITY_TYPES.grant && it.type !== POSTGRES_ENTITY_TYPES.defaultPrivileges && it.type !== POSTGRES_ENTITY_TYPES.role && it.type !== POSTGRES_ENTITY_TYPES.extension) {
232
+ // An index, a trigger, a cast or an event trigger is never referenced by name in another object's DDL.
233
+ else if (!UNREFERENCED.has(it.type)) {
207
234
  byQualified.set(it.schema ? `${it.schema}.${it.name}` : it.name, it);
208
235
  }
209
236
  }
@@ -17,6 +17,12 @@
17
17
  * declarations: the GRANT and REVOKE statements that take a new object's
18
18
  * privileges to what the server holds. Roles are the environment's and are
19
19
  * not imported.
20
+ * - An object of a kind no declaration holds (an aggregate, a cast, a
21
+ * composite type, an event trigger, ...; `../live/undeclarable.ts`) fails
22
+ * the import, which names every one of them (#3911). `skipUndeclarable`
23
+ * (`--skip-undeclarable`) imports the rest and lists them as warnings. A
24
+ * selector or `owned` import asks for objects chant can hold, so it does
25
+ * not look.
20
26
  */
21
27
 
22
28
  import type { ExportedTemplate, ResourceSelector } from "@intentius/chant/lexicon";
@@ -25,6 +31,9 @@ import { markProviderOwned, readLiveSchema } from "../live/catalog";
25
31
  import { objectsToIR, type ImportedPgObject } from "./ir";
26
32
  import { isChantManaged } from "../../core/ownership";
27
33
  import { importedAccess } from "../access/import";
34
+ import { readUndeclarable } from "../live/undeclarable";
35
+ import { describeUndeclarable, refuseUndeclarable, type UndeclarableObject } from "../../core/undeclarable";
36
+ import { isProviderOwned, providerData } from "../providers";
28
37
 
29
38
  export interface ExportOptions extends Omit<BindOptions, "environment"> {
30
39
  environment: string;
@@ -33,20 +42,33 @@ export interface ExportOptions extends Omit<BindOptions, "environment"> {
33
42
  selector?: ResourceSelector;
34
43
  owned?: boolean;
35
44
  verbatim?: boolean;
45
+ /** Import what can be declared, and list the objects of kinds no declaration holds as warnings instead of failing (#3911). */
46
+ skipUndeclarable?: boolean;
36
47
  }
37
48
 
38
49
  export async function exportResources(options: ExportOptions): Promise<ExportedTemplate> {
39
50
  const { target, client } = await bindPostgres(options);
40
51
  let live;
41
52
  let access: ImportedPgObject[] = [];
53
+ // A selector or an owned import asks only for what chant can hold.
54
+ const checked = !options.selector && !options.owned;
55
+ let undeclarable: { objects: UndeclarableObject[]; unread: string[] } = { objects: [], unread: [] };
42
56
  try {
57
+ if (checked) undeclarable = await readUndeclarable(client, { schemas: target.schemas });
43
58
  live = markProviderOwned(await readLiveSchema(client, { schemas: target.schemas, access: target.access === true }), target.provider);
44
59
  // Where the profile manages access, the privileges on what is imported, and the default privileges, as grant declarations.
45
60
  if (target.access === true && !options.selector) access = await importedAccess(client, live.filter((o) => !o.foreign && !o.unsupported && (!options.owned || isChantManaged(o.comment))), target.schemas);
46
61
  } finally {
47
62
  await client.end();
48
63
  }
49
- const warnings: string[] = [];
64
+ const warnings: string[] = undeclarable.unread.map((u) => `the server was not checked for objects of one kind chant cannot declare, so some may be left out: ${u}`);
65
+ // A managed provider's own (in its schemas, or owned by its roles) is the provider's, as for the objects read below.
66
+ const provider = target.provider;
67
+ const providers = (o: UndeclarableObject) =>
68
+ provider !== undefined &&
69
+ ((o.schema !== undefined && isProviderOwned(provider, { kind: "schema", name: o.schema })) || (o.owner !== undefined && isProviderOwned(provider, { kind: "role", name: o.owner })));
70
+ for (const o of undeclarable.objects.filter(providers)) warnings.push(`${describeUndeclarable(o)} is kept by ${providerData(provider!).label}; left out`);
71
+ refuseUndeclarable("postgres", undeclarable.objects.filter((o) => !providers(o)), options.skipUndeclarable, warnings);
50
72
  const objects: ImportedPgObject[] = [];
51
73
  for (const o of live) {
52
74
  if (options.owned && !isChantManaged(o.comment)) continue;
@@ -54,7 +76,8 @@ export async function exportResources(options: ExportOptions): Promise<ExportedT
54
76
  const name = options.selector?.name;
55
77
  if (name !== undefined && name !== o.name && name !== `${o.schema}.${o.name}`) continue;
56
78
  if (o.unsupported) {
57
- warnings.push(`${o.schema ? `${o.schema}.` : ""}${o.name}${o.signature ?? ""} is left out: ${o.unsupported}`);
79
+ // When checked, it is among the undeclarable objects above.
80
+ if (!checked) warnings.push(`${o.schema ? `${o.schema}.` : ""}${o.name}${o.signature ?? ""} is left out: ${o.unsupported}`);
58
81
  continue;
59
82
  }
60
83
  if (o.foreign) {