@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.
- package/dist/clickhouse/access.d.ts +36 -7
- package/dist/clickhouse/access.d.ts.map +1 -1
- package/dist/clickhouse/apply/statements.d.ts.map +1 -1
- package/dist/clickhouse/entities.d.ts +60 -0
- package/dist/clickhouse/entities.d.ts.map +1 -1
- package/dist/clickhouse/import/generator.d.ts +1 -7
- package/dist/clickhouse/import/generator.d.ts.map +1 -1
- package/dist/clickhouse/import/live-export.d.ts +8 -0
- package/dist/clickhouse/import/live-export.d.ts.map +1 -1
- package/dist/clickhouse/live/catalog.d.ts +6 -5
- package/dist/clickhouse/live/catalog.d.ts.map +1 -1
- package/dist/clickhouse/live/describe-resources.d.ts.map +1 -1
- package/dist/clickhouse/live/undeclarable.d.ts +32 -0
- package/dist/clickhouse/live/undeclarable.d.ts.map +1 -0
- package/dist/clickhouse/parser.d.ts +16 -0
- package/dist/clickhouse/parser.d.ts.map +1 -1
- package/dist/clickhouse/plan/deep.d.ts.map +1 -1
- package/dist/clickhouse/plan/diff.d.ts.map +1 -1
- package/dist/clickhouse/plan/normalize.d.ts +6 -3
- package/dist/clickhouse/plan/normalize.d.ts.map +1 -1
- package/dist/clickhouse/plan/rules.d.ts +3 -0
- package/dist/clickhouse/plan/rules.d.ts.map +1 -1
- package/dist/clickhouse/testing/fake-server.d.ts +6 -0
- package/dist/clickhouse/testing/fake-server.d.ts.map +1 -1
- package/dist/clickhouse/topology.d.ts.map +1 -1
- package/dist/clickhouse.d.ts +1 -1
- package/dist/clickhouse.d.ts.map +1 -1
- package/dist/codegen/coverage-page.d.ts +7 -0
- package/dist/codegen/coverage-page.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/registry.d.ts.map +1 -1
- package/dist/core/undeclarable.d.ts +38 -0
- package/dist/core/undeclarable.d.ts.map +1 -0
- package/dist/coverage/clickhouse-commands.json +24 -0
- package/dist/coverage/clickhouse.json +25 -0
- package/dist/coverage/postgres-commands.json +245 -0
- package/dist/coverage/postgres.json +51 -0
- package/dist/coverage.d.ts +68 -0
- package/dist/coverage.d.ts.map +1 -1
- package/dist/files/clickhouse.d.ts +2 -1
- package/dist/files/clickhouse.d.ts.map +1 -1
- package/dist/files/common.d.ts +12 -0
- package/dist/files/common.d.ts.map +1 -1
- package/dist/files/postgres.d.ts +1 -1
- package/dist/files/postgres.d.ts.map +1 -1
- package/dist/import-parser.d.ts +7 -2
- package/dist/import-parser.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +10 -10
- package/dist/lint/post-synth/postgres-names.d.ts.map +1 -1
- package/dist/lint/post-synth/sql101.d.ts.map +1 -1
- package/dist/lint/rules/sqlch001.d.ts +1 -1
- package/dist/lint/rules/sqlch001.d.ts.map +1 -1
- package/dist/lint/rules/sqlpg001.d.ts.map +1 -1
- package/dist/lint/rules/templates.d.ts +1 -1
- package/dist/lint/rules/templates.d.ts.map +1 -1
- package/dist/lsp/postgres-template.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/meta.json +40 -0
- package/dist/okf/index.md +9 -1
- package/dist/okf/rules/SQL101.md +1 -0
- package/dist/okf/rules/SQLCH001.md +2 -2
- package/dist/okf/rules/SQLPG113.md +2 -0
- package/dist/okf/types/PostgresAggregate.md +13 -0
- package/dist/okf/types/PostgresCast.md +9 -0
- package/dist/okf/types/PostgresCollation.md +9 -0
- package/dist/okf/types/PostgresCompositeType.md +13 -0
- package/dist/okf/types/PostgresEventTrigger.md +9 -0
- package/dist/okf/types/PostgresRangeType.md +13 -0
- package/dist/okf/types/Quota.md +9 -0
- package/dist/okf/types/SettingsProfile.md +9 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/postgres/apply/apply.d.ts.map +1 -1
- package/dist/postgres/apply/statements.d.ts +4 -3
- package/dist/postgres/apply/statements.d.ts.map +1 -1
- package/dist/postgres/entities.d.ts +129 -6
- package/dist/postgres/entities.d.ts.map +1 -1
- package/dist/postgres/entity-types.d.ts +6 -0
- package/dist/postgres/entity-types.d.ts.map +1 -1
- package/dist/postgres/import/generator.d.ts +3 -2
- package/dist/postgres/import/generator.d.ts.map +1 -1
- package/dist/postgres/import/live-export.d.ts +8 -0
- package/dist/postgres/import/live-export.d.ts.map +1 -1
- package/dist/postgres/live/catalog-kinds.d.ts +28 -0
- package/dist/postgres/live/catalog-kinds.d.ts.map +1 -0
- package/dist/postgres/live/catalog.d.ts +23 -8
- package/dist/postgres/live/catalog.d.ts.map +1 -1
- package/dist/postgres/live/describe-resources.d.ts.map +1 -1
- package/dist/postgres/live/undeclarable.d.ts +42 -0
- package/dist/postgres/live/undeclarable.d.ts.map +1 -0
- package/dist/postgres/parser.d.ts +88 -5
- package/dist/postgres/parser.d.ts.map +1 -1
- package/dist/postgres/plan/deep.d.ts.map +1 -1
- package/dist/postgres/plan/diff.d.ts.map +1 -1
- package/dist/postgres/plan/normalize.d.ts +17 -2
- package/dist/postgres/plan/normalize.d.ts.map +1 -1
- package/dist/postgres/plan/rules.d.ts +3 -0
- package/dist/postgres/plan/rules.d.ts.map +1 -1
- package/dist/postgres/plan/schema.d.ts +3 -2
- package/dist/postgres/plan/schema.d.ts.map +1 -1
- package/dist/postgres/testing/fake-client.d.ts +6 -0
- package/dist/postgres/testing/fake-client.d.ts.map +1 -1
- package/dist/postgres/testing/roundtrip-schema.d.ts +4 -2
- package/dist/postgres/testing/roundtrip-schema.d.ts.map +1 -1
- package/dist/postgres.d.ts +1 -1
- package/dist/postgres.d.ts.map +1 -1
- package/dist/rules/postgres-names.ts +15 -2
- package/dist/rules/postgres-templates.ts +2 -2
- package/dist/rules/sql101.ts +1 -0
- package/dist/rules/sqlch001.ts +10 -3
- package/dist/rules/sqlpg001.ts +6 -21
- package/dist/rules/sqlpg113.ts +1 -1
- package/dist/rules/templates.ts +2 -2
- package/dist/validate.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/clickhouse/access.ts +197 -22
- package/src/clickhouse/apply/statements.ts +17 -5
- package/src/clickhouse/entities.ts +78 -2
- package/src/clickhouse/import/generator.ts +16 -3
- package/src/clickhouse/import/live-export.ts +17 -2
- package/src/clickhouse/live/catalog.ts +57 -10
- package/src/clickhouse/live/describe-resources.ts +3 -1
- package/src/clickhouse/live/kinds.e2e.test.ts +179 -0
- package/src/clickhouse/live/undeclarable.e2e.test.ts +65 -0
- package/src/clickhouse/live/undeclarable.ts +66 -0
- package/src/clickhouse/parser.test.ts +45 -2
- package/src/clickhouse/parser.ts +111 -3
- package/src/clickhouse/plan/deep.ts +25 -6
- package/src/clickhouse/plan/diff.ts +41 -1
- package/src/clickhouse/plan/normalize.ts +12 -4
- package/src/clickhouse/plan/rules.ts +22 -1
- package/src/clickhouse/quota-profile.test.ts +186 -0
- package/src/clickhouse/table-function.test.ts +54 -0
- package/src/clickhouse/testdata/server-ddl-baseline.json +17 -14
- package/src/clickhouse/testing/fake-server.ts +5 -0
- package/src/clickhouse/topology.ts +4 -2
- package/src/clickhouse.ts +6 -0
- package/src/codegen/classifier-page.test.ts +6 -0
- package/src/codegen/coverage-page.ts +77 -0
- package/src/codegen/docs.ts +5 -0
- package/src/codegen/postgres-classifier-page.ts +1 -1
- package/src/codegen/registry.ts +8 -0
- package/src/core/undeclarable.test.ts +141 -0
- package/src/core/undeclarable.ts +66 -0
- package/src/coverage/clickhouse-commands.json +24 -0
- package/src/coverage/clickhouse.json +25 -0
- package/src/coverage/postgres-commands.json +245 -0
- package/src/coverage/postgres.json +51 -0
- package/src/coverage.test.ts +78 -0
- package/src/coverage.ts +142 -0
- package/src/files/clickhouse.ts +20 -7
- package/src/files/common.ts +18 -0
- package/src/files/postgres.ts +25 -6
- package/src/generated/lexicon-sql.json +40 -0
- package/src/import-parser.ts +12 -4
- package/src/index.ts +2 -2
- package/src/lint/post-synth/postgres-names.ts +15 -2
- package/src/lint/post-synth/sql101.ts +1 -0
- package/src/lint/post-synth/sqlpg113.ts +1 -1
- package/src/lint/rules/postgres-templates.ts +2 -2
- package/src/lint/rules/sqlch001.ts +10 -3
- package/src/lint/rules/sqlpg001.ts +6 -21
- package/src/lint/rules/templates.ts +2 -2
- package/src/lsp/postgres-template.ts +14 -0
- package/src/mcp/index.ts +5 -5
- package/src/mcp/postgres.ts +1 -1
- package/src/plugin.ts +13 -2
- package/src/postgres/apply/apply.ts +8 -2
- package/src/postgres/apply/statements.ts +29 -6
- package/src/postgres/entities.test.ts +2 -2
- package/src/postgres/entities.ts +316 -28
- package/src/postgres/entity-types.ts +6 -0
- package/src/postgres/import/generator.ts +31 -4
- package/src/postgres/import/live-export.ts +25 -2
- package/src/postgres/kinds.test.ts +140 -0
- package/src/postgres/live/catalog-kinds.ts +312 -0
- package/src/postgres/live/catalog.ts +33 -11
- package/src/postgres/live/describe-resources.ts +14 -5
- package/src/postgres/live/kinds.e2e.test.ts +182 -0
- package/src/postgres/live/roundtrip.e2e.test.ts +9 -3
- package/src/postgres/live/undeclarable.e2e.test.ts +83 -0
- package/src/postgres/live/undeclarable.ts +141 -0
- package/src/postgres/parser.test.ts +2 -1
- package/src/postgres/parser.ts +307 -17
- package/src/postgres/plan/deep.ts +33 -0
- package/src/postgres/plan/diff.ts +45 -0
- package/src/postgres/plan/normalize.ts +153 -2
- package/src/postgres/plan/rules.ts +21 -0
- package/src/postgres/plan/schema.ts +6 -4
- package/src/postgres/testing/fake-client.ts +5 -0
- package/src/postgres/testing/roundtrip-schema.ts +19 -1
- package/src/postgres.ts +19 -0
- package/src/skill-defs.ts +1 -1
- package/src/skills/chant-sql-postgres.md +7 -3
- package/src/skills/chant-sql.md +3 -1
- package/src/unknown-clauses.test.ts +2 -1
- package/src/validate.ts +8 -0
package/src/postgres/entities.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Postgres entities: `schema`, `table`, `index`, `view`, `sequence`,
|
|
3
|
-
* `type
|
|
4
|
-
* `
|
|
5
|
-
*
|
|
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 =
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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 === "
|
|
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
|
|
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,
|
|
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
|
-
/**
|
|
1648
|
-
|
|
1649
|
-
|
|
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
|
|
4
|
-
* `extension`
|
|
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
|
|
206
|
-
else if (
|
|
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
|
-
|
|
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) {
|