@intentius/chant-lexicon-sql 0.124.46 → 0.124.48
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/change-set.d.ts +1 -1
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +2 -1
- package/dist/clickhouse/apply/statements.d.ts +9 -0
- package/dist/clickhouse/apply/statements.d.ts.map +1 -1
- package/dist/clickhouse/apply/statements.js +24 -0
- package/dist/clickhouse/import/live-export.d.ts +7 -3
- package/dist/clickhouse/import/live-export.d.ts.map +1 -1
- package/dist/clickhouse/import/live-export.js +21 -5
- package/dist/clickhouse/live/named-collections.d.ts +27 -0
- package/dist/clickhouse/live/named-collections.d.ts.map +1 -0
- package/dist/clickhouse/live/named-collections.js +53 -0
- package/dist/clickhouse/live/undeclarable.d.ts +15 -3
- package/dist/clickhouse/live/undeclarable.d.ts.map +1 -1
- package/dist/clickhouse/live/undeclarable.js +18 -4
- package/dist/clickhouse/named-collections.d.ts +83 -0
- package/dist/clickhouse/named-collections.d.ts.map +1 -0
- package/dist/clickhouse/named-collections.js +203 -0
- package/dist/clickhouse/parser.d.ts.map +1 -1
- package/dist/clickhouse/parser.js +1 -1
- package/dist/clickhouse/plan/commands.d.ts +2 -1
- package/dist/clickhouse/plan/commands.d.ts.map +1 -1
- package/dist/clickhouse/plan/commands.js +17 -2
- package/dist/clickhouse/plan/diff.d.ts +15 -0
- package/dist/clickhouse/plan/diff.d.ts.map +1 -1
- package/dist/clickhouse/plan/diff.js +43 -1
- package/dist/clickhouse/plan/disruption.d.ts.map +1 -1
- package/dist/clickhouse/plan/disruption.js +5 -0
- package/dist/clickhouse/plan/rebuild-handoff.d.ts.map +1 -1
- package/dist/clickhouse/plan/rebuild-handoff.js +4 -0
- package/dist/clickhouse/plan/report.d.ts.map +1 -1
- package/dist/clickhouse/plan/report.js +6 -1
- package/dist/clickhouse/plan/rules.d.ts +5 -1
- package/dist/clickhouse/plan/rules.d.ts.map +1 -1
- package/dist/clickhouse/plan/rules.js +7 -3
- package/dist/clickhouse/plan/stream.d.ts +40 -0
- package/dist/clickhouse/plan/stream.d.ts.map +1 -0
- package/dist/clickhouse/plan/stream.js +107 -0
- package/dist/clickhouse/rebuild/observe.d.ts +3 -1
- package/dist/clickhouse/rebuild/observe.d.ts.map +1 -1
- package/dist/clickhouse/rebuild/observe.js +14 -1
- package/dist/clickhouse/review-catalog.d.ts +213 -0
- package/dist/clickhouse/review-catalog.d.ts.map +1 -0
- package/dist/clickhouse/review-catalog.js +330 -0
- package/dist/codegen/classifier-page.d.ts.map +1 -1
- package/dist/codegen/classifier-page.js +21 -4
- package/dist/codegen/clickhouse-module.d.ts.map +1 -1
- package/dist/codegen/clickhouse-module.js +3 -0
- package/dist/codegen/coverage-page.d.ts.map +1 -1
- package/dist/codegen/coverage-page.js +5 -3
- package/dist/codegen/docs.js +1 -1
- package/dist/codegen/postgres-classifier-page.js +1 -1
- package/dist/codegen/registry.d.ts.map +1 -1
- package/dist/codegen/registry.js +5 -0
- package/dist/composites/catalog.d.ts.map +1 -1
- package/dist/composites/catalog.js +100 -0
- package/dist/composites/clickhouse.d.ts +2 -0
- package/dist/composites/clickhouse.d.ts.map +1 -1
- package/dist/composites/clickhouse.js +2 -0
- package/dist/composites/otel-tables.d.ts +100 -0
- package/dist/composites/otel-tables.d.ts.map +1 -0
- package/dist/composites/otel-tables.js +282 -0
- package/dist/composites/stream-ingest.d.ts +63 -0
- package/dist/composites/stream-ingest.d.ts.map +1 -0
- package/dist/composites/stream-ingest.js +39 -0
- package/dist/coverage/clickhouse.json +1 -1
- package/dist/coverage/postgres.json +7 -7
- package/dist/coverage.d.ts +16 -6
- package/dist/coverage.d.ts.map +1 -1
- package/dist/coverage.js +10 -5
- package/dist/drizzle-command.d.ts +17 -0
- package/dist/drizzle-command.d.ts.map +1 -0
- package/dist/drizzle-command.js +91 -0
- package/dist/drizzle.d.ts +70 -0
- package/dist/drizzle.d.ts.map +1 -0
- package/dist/drizzle.js +684 -0
- package/dist/files/clickhouse.d.ts.map +1 -1
- package/dist/files/clickhouse.js +8 -1
- package/dist/files/common.d.ts +13 -0
- package/dist/files/common.d.ts.map +1 -1
- package/dist/files/common.js +5 -0
- package/dist/files/postgres.js +12 -2
- package/dist/generated/clickhouse.d.ts +2 -0
- package/dist/generated/clickhouse.d.ts.map +1 -1
- package/dist/generated/clickhouse.js +2 -0
- package/dist/import-parser.d.ts +21 -1
- package/dist/import-parser.d.ts.map +1 -1
- package/dist/import-parser.js +79 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/integrity.json +6 -5
- package/dist/lint/post-synth/audit-entries.d.ts.map +1 -1
- package/dist/lint/post-synth/audit-entries.js +1 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/index.js +2 -0
- package/dist/lint/post-synth/sqlch128.d.ts +18 -0
- package/dist/lint/post-synth/sqlch128.d.ts.map +1 -0
- package/dist/lint/post-synth/sqlch128.js +222 -0
- package/dist/lint/rules/postgres-templates.js +2 -2
- package/dist/lsp/postgres-template.d.ts.map +1 -1
- package/dist/lsp/postgres-template.js +11 -0
- package/dist/manifest.json +1 -1
- package/dist/mcp/postgres.js +1 -1
- package/dist/meta.json +25 -0
- package/dist/migrate/from-postgres/detect.d.ts +4 -0
- package/dist/migrate/from-postgres/detect.d.ts.map +1 -0
- package/dist/migrate/from-postgres/detect.js +7 -0
- package/dist/migrate/from-postgres/emit.d.ts +26 -0
- package/dist/migrate/from-postgres/emit.d.ts.map +1 -0
- package/dist/migrate/from-postgres/emit.js +86 -0
- package/dist/migrate/from-postgres/index.d.ts +51 -0
- package/dist/migrate/from-postgres/index.d.ts.map +1 -0
- package/dist/migrate/from-postgres/index.js +115 -0
- package/dist/migrate/from-postgres/translate.d.ts +73 -0
- package/dist/migrate/from-postgres/translate.d.ts.map +1 -0
- package/dist/migrate/from-postgres/translate.js +261 -0
- package/dist/migrate/from-postgres/types.d.ts +51 -0
- package/dist/migrate/from-postgres/types.d.ts.map +1 -0
- package/dist/migrate/from-postgres/types.js +211 -0
- package/dist/migration-replay.d.ts +73 -0
- package/dist/migration-replay.d.ts.map +1 -0
- package/dist/migration-replay.js +294 -0
- package/dist/okf/index.md +6 -0
- package/dist/okf/rules/SQLCH128.md +11 -0
- package/dist/okf/types/PostgresForeignDataWrapper.md +9 -0
- package/dist/okf/types/PostgresForeignServer.md +9 -0
- package/dist/okf/types/PostgresOperator.md +9 -0
- package/dist/okf/types/PostgresPublication.md +9 -0
- package/dist/okf/types/PostgresStatistics.md +9 -0
- package/dist/op/activities/postgres-migration.d.ts.map +1 -1
- package/dist/op/activities/postgres-migration.js +4 -0
- package/dist/op/activity-contracts.d.ts +20 -0
- package/dist/op/activity-contracts.d.ts.map +1 -1
- package/dist/op/activity-contracts.js +2 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +36 -2
- package/dist/postgres/apply/apply.js +8 -1
- package/dist/postgres/apply/statements.d.ts.map +1 -1
- package/dist/postgres/apply/statements.js +87 -2
- package/dist/postgres/constraint-names.d.ts +30 -0
- package/dist/postgres/constraint-names.d.ts.map +1 -0
- package/dist/postgres/constraint-names.js +72 -0
- package/dist/postgres/entities.d.ts +110 -1
- package/dist/postgres/entities.d.ts.map +1 -1
- package/dist/postgres/entities.js +146 -0
- package/dist/postgres/entity-types.d.ts +5 -0
- package/dist/postgres/entity-types.d.ts.map +1 -1
- package/dist/postgres/entity-types.js +5 -0
- package/dist/postgres/import/generator.d.ts.map +1 -1
- package/dist/postgres/import/generator.js +50 -2
- package/dist/postgres/import/live-export.d.ts.map +1 -1
- package/dist/postgres/import/live-export.js +2 -0
- package/dist/postgres/live/catalog-kinds.d.ts +14 -0
- package/dist/postgres/live/catalog-kinds.d.ts.map +1 -1
- package/dist/postgres/live/catalog-kinds.js +206 -0
- package/dist/postgres/live/catalog.d.ts +2 -0
- package/dist/postgres/live/catalog.d.ts.map +1 -1
- package/dist/postgres/live/catalog.js +3 -1
- package/dist/postgres/live/describe-resources.d.ts.map +1 -1
- package/dist/postgres/live/describe-resources.js +10 -2
- package/dist/postgres/migrate/backfill.d.ts +2 -0
- package/dist/postgres/migrate/backfill.d.ts.map +1 -1
- package/dist/postgres/migrate/backfill.js +7 -1
- package/dist/postgres/migrate/carry.d.ts +45 -1
- package/dist/postgres/migrate/carry.d.ts.map +1 -1
- package/dist/postgres/migrate/carry.js +72 -8
- package/dist/postgres/migrate/observe.d.ts +28 -4
- package/dist/postgres/migrate/observe.d.ts.map +1 -1
- package/dist/postgres/migrate/observe.js +41 -12
- package/dist/postgres/migrate/op.d.ts +26 -1
- package/dist/postgres/migrate/op.d.ts.map +1 -1
- package/dist/postgres/migrate/op.js +3 -1
- package/dist/postgres/migrate/steps.d.ts +35 -2
- package/dist/postgres/migrate/steps.d.ts.map +1 -1
- package/dist/postgres/migrate/steps.js +158 -12
- package/dist/postgres/parser.d.ts +72 -1
- package/dist/postgres/parser.d.ts.map +1 -1
- package/dist/postgres/parser.js +194 -2
- package/dist/postgres/plan/deep.d.ts.map +1 -1
- package/dist/postgres/plan/deep.js +29 -1
- package/dist/postgres/plan/diff.d.ts.map +1 -1
- package/dist/postgres/plan/diff.js +39 -1
- package/dist/postgres/plan/normalize.d.ts +17 -2
- package/dist/postgres/plan/normalize.d.ts.map +1 -1
- package/dist/postgres/plan/normalize.js +109 -1
- package/dist/postgres/plan/rules.d.ts +1 -0
- package/dist/postgres/plan/rules.d.ts.map +1 -1
- package/dist/postgres/plan/rules.js +3 -2
- package/dist/postgres/plan/schema.js +1 -1
- package/dist/postgres/secret-options.d.ts +10 -0
- package/dist/postgres/secret-options.d.ts.map +1 -0
- package/dist/postgres/secret-options.js +9 -0
- package/dist/postgres/testing/roundtrip-schema.d.ts +3 -2
- package/dist/postgres/testing/roundtrip-schema.d.ts.map +1 -1
- package/dist/postgres/testing/roundtrip-schema.js +13 -1
- package/dist/postgres.d.ts +1 -1
- package/dist/postgres.d.ts.map +1 -1
- package/dist/postgres.js +1 -1
- package/dist/rules/audit-entries.ts +1 -0
- package/dist/rules/postgres-templates.ts +2 -2
- package/dist/rules/sqlch128.ts +230 -0
- package/dist/serializer.d.ts +5 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/serializer.js +13 -2
- package/dist/sql-types-command.d.ts +13 -0
- package/dist/sql-types-command.d.ts.map +1 -1
- package/dist/sql-types-command.js +31 -16
- package/dist/sql-types.d.ts +22 -0
- package/dist/sql-types.d.ts.map +1 -1
- package/dist/sql-types.js +10 -6
- package/dist/sqlserver/lexer.d.ts +37 -0
- package/dist/sqlserver/lexer.d.ts.map +1 -0
- package/dist/sqlserver/lexer.js +254 -0
- package/dist/sqlserver/parse.d.ts +185 -0
- package/dist/sqlserver/parse.d.ts.map +1 -0
- package/dist/sqlserver/parse.js +767 -0
- package/dist/sqlserver/translate.d.ts +34 -0
- package/dist/sqlserver/translate.d.ts.map +1 -0
- package/dist/sqlserver/translate.js +495 -0
- package/dist/sqlserver/types.d.ts +44 -0
- package/dist/sqlserver/types.d.ts.map +1 -0
- package/dist/sqlserver/types.js +133 -0
- package/dist/sqlserver/validate.d.ts +31 -0
- package/dist/sqlserver/validate.d.ts.map +1 -0
- package/dist/sqlserver/validate.js +67 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +5 -0
- package/package.json +2 -2
- package/src/change-set.ts +2 -1
- package/src/clickhouse/apply/statements.ts +25 -0
- package/src/clickhouse/import/live-export.ts +20 -5
- package/src/clickhouse/live/named-collections.e2e.test.ts +88 -0
- package/src/clickhouse/live/named-collections.ts +61 -0
- package/src/clickhouse/live/undeclarable.e2e.test.ts +5 -3
- package/src/clickhouse/live/undeclarable.ts +20 -4
- package/src/clickhouse/named-collections.test.ts +187 -0
- package/src/clickhouse/named-collections.ts +224 -0
- package/src/clickhouse/parser.ts +2 -1
- package/src/clickhouse/plan/commands.ts +17 -2
- package/src/clickhouse/plan/diff.ts +57 -1
- package/src/clickhouse/plan/disruption.ts +5 -0
- package/src/clickhouse/plan/rebuild-handoff.ts +3 -0
- package/src/clickhouse/plan/report.ts +6 -1
- package/src/clickhouse/plan/rules.ts +14 -4
- package/src/clickhouse/plan/stream-s3queue.e2e.test.ts +148 -0
- package/src/clickhouse/plan/stream.test.ts +237 -0
- package/src/clickhouse/plan/stream.ts +111 -0
- package/src/clickhouse/rebuild/observe.ts +15 -1
- package/src/clickhouse/review-catalog.test.ts +181 -0
- package/src/clickhouse/review-catalog.ts +489 -0
- package/src/codegen/classifier-page.ts +21 -4
- package/src/codegen/clickhouse-module.ts +3 -0
- package/src/codegen/coverage-page.ts +5 -3
- package/src/codegen/docs.ts +1 -1
- package/src/codegen/postgres-classifier-page.ts +1 -1
- package/src/codegen/registry.ts +5 -0
- package/src/composites/catalog.test.ts +6 -1
- package/src/composites/catalog.ts +100 -0
- package/src/composites/clickhouse.ts +2 -0
- package/src/composites/composites.test.ts +1 -1
- package/src/composites/otel-tables.test.ts +118 -0
- package/src/composites/otel-tables.ts +339 -0
- package/src/composites/stream-ingest.test.ts +114 -0
- package/src/composites/stream-ingest.ts +82 -0
- package/src/core/undeclarable.test.ts +6 -5
- package/src/coverage/clickhouse.json +1 -1
- package/src/coverage/postgres.json +7 -7
- package/src/coverage.test.ts +27 -2
- package/src/coverage.ts +18 -7
- package/src/drizzle-command.ts +94 -0
- package/src/drizzle-kit.e2e.test.ts +89 -0
- package/src/drizzle-types.e2e.test.ts +67 -0
- package/src/drizzle.test.ts +358 -0
- package/src/drizzle.ts +737 -0
- package/src/files/clickhouse.ts +8 -1
- package/src/files/common.ts +14 -1
- package/src/files/postgres.ts +12 -2
- package/src/generated/clickhouse.ts +3 -0
- package/src/generated/lexicon-sql.json +25 -0
- package/src/import-parser.sqlserver.test.ts +92 -0
- package/src/import-parser.ts +79 -5
- package/src/index.ts +1 -1
- package/src/lint/post-synth/audit-entries.ts +1 -0
- package/src/lint/post-synth/index.ts +2 -0
- package/src/lint/post-synth/sqlch128.test.ts +107 -0
- package/src/lint/post-synth/sqlch128.ts +230 -0
- package/src/lint/rules/postgres-templates.ts +2 -2
- package/src/lsp/postgres-template.ts +11 -0
- package/src/mcp/postgres.ts +1 -1
- package/src/migrate/from-postgres/cli.test.ts +165 -0
- package/src/migrate/from-postgres/detect.ts +7 -0
- package/src/migrate/from-postgres/emit.ts +96 -0
- package/src/migrate/from-postgres/example.test.ts +205 -0
- package/src/migrate/from-postgres/index.ts +122 -0
- package/src/migrate/from-postgres/translate.test.ts +242 -0
- package/src/migrate/from-postgres/translate.ts +359 -0
- package/src/migrate/from-postgres/types.test.ts +116 -0
- package/src/migrate/from-postgres/types.ts +236 -0
- package/src/migrate/from-postgres/validate.e2e.test.ts +44 -0
- package/src/migrate/from-postgres/workspace.e2e.test.ts +114 -0
- package/src/migration-replay.e2e.test.ts +155 -0
- package/src/migration-replay.test.ts +173 -0
- package/src/migration-replay.ts +289 -0
- package/src/op/__snapshots__/watch-pipeline.test.ts.snap +2 -2
- package/src/op/activities/postgres-migration.ts +4 -0
- package/src/op/activity-contracts.ts +2 -0
- package/src/plugin.ts +37 -2
- package/src/postgres/apply/apply.ts +9 -1
- package/src/postgres/apply/statements.ts +74 -1
- package/src/postgres/constraint-names.ts +71 -0
- package/src/postgres/database-kinds.test.ts +384 -0
- package/src/postgres/entities.ts +266 -1
- package/src/postgres/entity-types.ts +5 -0
- package/src/postgres/import/generator.ts +42 -2
- package/src/postgres/import/live-export.ts +1 -0
- package/src/postgres/live/catalog-kinds.ts +223 -0
- package/src/postgres/live/catalog.ts +6 -1
- package/src/postgres/live/database-kinds.e2e.test.ts +164 -0
- package/src/postgres/live/describe-resources.ts +11 -2
- package/src/postgres/live/undeclarable.e2e.test.ts +13 -6
- package/src/postgres/migrate/backfill.ts +13 -1
- package/src/postgres/migrate/carry.ts +103 -8
- package/src/postgres/migrate/followups.e2e.test.ts +218 -0
- package/src/postgres/migrate/followups.test.ts +199 -0
- package/src/postgres/migrate/kinds.e2e.test.ts +5 -5
- package/src/postgres/migrate/observe.ts +53 -14
- package/src/postgres/migrate/op.ts +28 -1
- package/src/postgres/migrate/partition.e2e.test.ts +129 -0
- package/src/postgres/migrate/partition.test.ts +206 -0
- package/src/postgres/migrate/steps.ts +157 -12
- package/src/postgres/parser.ts +234 -2
- package/src/postgres/plan/deep.ts +28 -1
- package/src/postgres/plan/diff.ts +36 -1
- package/src/postgres/plan/normalize.ts +122 -1
- package/src/postgres/plan/rules.ts +10 -3
- package/src/postgres/plan/schema.ts +1 -1
- package/src/postgres/secret-options.ts +10 -0
- package/src/postgres/testing/roundtrip-schema.ts +13 -1
- package/src/postgres.ts +16 -0
- package/src/serializer.ts +13 -2
- package/src/skills/chant-sql-postgres-migration.md +3 -3
- package/src/skills/chant-sql-postgres.md +6 -1
- package/src/skills/chant-sql.md +2 -1
- package/src/sql-types-command.ts +38 -16
- package/src/sql-types.ts +11 -6
- package/src/sqlserver/fixture.test.ts +186 -0
- package/src/sqlserver/import-cli.test.ts +61 -0
- package/src/sqlserver/lexer.ts +260 -0
- package/src/sqlserver/parse.ts +865 -0
- package/src/sqlserver/translate.test.ts +396 -0
- package/src/sqlserver/translate.ts +558 -0
- package/src/sqlserver/types.test.ts +102 -0
- package/src/sqlserver/types.ts +154 -0
- package/src/sqlserver/validate.e2e.test.ts +32 -0
- package/src/sqlserver/validate.test.ts +49 -0
- package/src/sqlserver/validate.ts +76 -0
- package/src/validate.ts +5 -0
- package/src/workspace-kinds.test.ts +11 -0
- package/workspace-kinds.json +100 -2
|
@@ -215,7 +215,7 @@ function diffUnparsed(key: string, before: PgDiffObject, after: PgDiffObject, ou
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
/** The kinds (#3911) whose definition has no ALTER: a change is a drop and a create, or (an aggregate's) CREATE OR REPLACE. */
|
|
218
|
-
const REPLACED_KINDS: ReadonlySet<string> = new Set(["composite", "range", "aggregate", "collation", "cast", "eventTrigger"]);
|
|
218
|
+
const REPLACED_KINDS: ReadonlySet<string> = new Set(["composite", "range", "aggregate", "collation", "cast", "eventTrigger", "operator", "statistics"]);
|
|
219
219
|
|
|
220
220
|
/** What each of {@link REPLACED_KINDS} compares besides its name and comment. */
|
|
221
221
|
const DEFINITION: Readonly<Record<string, readonly string[]>> = {
|
|
@@ -225,8 +225,39 @@ const DEFINITION: Readonly<Record<string, readonly string[]>> = {
|
|
|
225
225
|
collation: ["provider", "lcCollate", "lcCtype", "locale", "nondeterministic", "rules"],
|
|
226
226
|
cast: ["castMethod", "function", "context"],
|
|
227
227
|
eventTrigger: ["event", "tags", "function"],
|
|
228
|
+
operator: ["definition"],
|
|
229
|
+
statistics: ["table", "kinds", "targets"],
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/** The kinds (#3911) the server alters in place, with the fields that cannot be altered (the object is dropped and created) and those that can. */
|
|
233
|
+
const ALTERED_KINDS: Readonly<Record<string, { recreate: readonly string[]; inPlace: readonly string[] }>> = {
|
|
234
|
+
foreignDataWrapper: { recreate: [], inPlace: ["handler", "validator", "options"] },
|
|
235
|
+
foreignServer: { recreate: ["serverType", "wrapper"], inPlace: ["version", "options"] },
|
|
236
|
+
publication: { recreate: ["allTables"], inPlace: ["tables", "schemas", "publish", "viaRoot", "generatedColumns"] },
|
|
228
237
|
};
|
|
229
238
|
|
|
239
|
+
/**
|
|
240
|
+
* A foreign data wrapper's, foreign server's or publication's change: the
|
|
241
|
+
* comment and each field ALTER reaches in place (SQLPG303); a field it does
|
|
242
|
+
* not reach (a server's type and wrapper, a publication's FOR ALL TABLES) by
|
|
243
|
+
* dropping and creating the object (SQLPG300), refused while something
|
|
244
|
+
* depends on it (SQLPG301).
|
|
245
|
+
*/
|
|
246
|
+
function diffAltered(key: string, before: PgDiffObject, after: PgDiffObject, out: PgChange[]): void {
|
|
247
|
+
if (!same(before.fields.comment, after.fields.comment)) out.push(change(key, "comment", "SQLPG216", show(before.fields.comment), show(after.fields.comment)));
|
|
248
|
+
const { recreate, inPlace } = ALTERED_KINDS[after.kind]!;
|
|
249
|
+
const replaced = recreate.filter((k) => !same(before.fields[k], after.fields[k]));
|
|
250
|
+
if (replaced.length > 0) {
|
|
251
|
+
const was = replaced.map((k) => `${k} ${show(before.fields[k]) ?? "-"}`).join("; ");
|
|
252
|
+
const now = replaced.map((k) => `${k} ${show(after.fields[k]) ?? "-"}`).join("; ");
|
|
253
|
+
if (before.dependents && before.dependents.length > 0) {
|
|
254
|
+
out.push(change(key, replaced.join(", "), "SQLPG301", was, now, { note: `${before.dependents.join(", ")} depend${before.dependents.length === 1 ? "s" : ""} on it` }));
|
|
255
|
+
} else out.push(change(key, replaced.join(", "), "SQLPG300", was, now));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
for (const k of inPlace) if (!same(before.fields[k], after.fields[k])) out.push(change(key, k, "SQLPG303", show(before.fields[k]), show(after.fields[k])));
|
|
259
|
+
}
|
|
260
|
+
|
|
230
261
|
/**
|
|
231
262
|
* A type's, aggregate's, collation's, cast's or event trigger's change: its
|
|
232
263
|
* comment in place; its definition by CREATE OR REPLACE AGGREGATE where that
|
|
@@ -270,6 +301,10 @@ function diffObject(key: string, before: PgDiffObject, after: PgDiffObject, out:
|
|
|
270
301
|
diffReplaced(key, before, after, out);
|
|
271
302
|
return;
|
|
272
303
|
}
|
|
304
|
+
if (after.kind in ALTERED_KINDS) {
|
|
305
|
+
diffAltered(key, before, after, out);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
273
308
|
if (before.schema !== after.schema || before.name !== after.name) {
|
|
274
309
|
out.push(change(key, "name", after.kind === "index" ? "SQLPG229" : "SQLPG228", qualified(before), qualified(after)));
|
|
275
310
|
}
|
|
@@ -51,6 +51,7 @@ import { stripMarker } from "../../core/ownership";
|
|
|
51
51
|
import { sequenceDefaults } from "../live/catalog";
|
|
52
52
|
import type {
|
|
53
53
|
AggregateArgDef,
|
|
54
|
+
OperatorProps,
|
|
54
55
|
AggregateProps,
|
|
55
56
|
CastProps,
|
|
56
57
|
CheckDef,
|
|
@@ -59,6 +60,10 @@ import type {
|
|
|
59
60
|
CompositeTypeProps,
|
|
60
61
|
DomainProps,
|
|
61
62
|
EventTriggerProps,
|
|
63
|
+
StatisticsProps,
|
|
64
|
+
ForeignDataWrapperProps,
|
|
65
|
+
ForeignServerProps,
|
|
66
|
+
PublicationProps,
|
|
62
67
|
RangeTypeProps,
|
|
63
68
|
EnumProps,
|
|
64
69
|
ExclusionDef,
|
|
@@ -348,6 +353,11 @@ export type CanonicalKind =
|
|
|
348
353
|
| "collation"
|
|
349
354
|
| "cast"
|
|
350
355
|
| "eventTrigger"
|
|
356
|
+
| "operator"
|
|
357
|
+
| "statistics"
|
|
358
|
+
| "foreignDataWrapper"
|
|
359
|
+
| "foreignServer"
|
|
360
|
+
| "publication"
|
|
351
361
|
| "function"
|
|
352
362
|
| "procedure"
|
|
353
363
|
| "trigger"
|
|
@@ -393,6 +403,11 @@ const kindOf: Record<string, CanonicalKind> = {
|
|
|
393
403
|
"Postgres::Collation": "collation",
|
|
394
404
|
"Postgres::Cast": "cast",
|
|
395
405
|
"Postgres::EventTrigger": "eventTrigger",
|
|
406
|
+
"Postgres::Operator": "operator",
|
|
407
|
+
"Postgres::Statistics": "statistics",
|
|
408
|
+
"Postgres::ForeignDataWrapper": "foreignDataWrapper",
|
|
409
|
+
"Postgres::ForeignServer": "foreignServer",
|
|
410
|
+
"Postgres::Publication": "publication",
|
|
396
411
|
"Postgres::Extension": "extension",
|
|
397
412
|
"Postgres::Function": "function",
|
|
398
413
|
"Postgres::Procedure": "procedure",
|
|
@@ -617,7 +632,7 @@ function triggerFields(p: TriggerProps, defaultSchema: string, fields: Record<st
|
|
|
617
632
|
// ── Types, aggregates, collations, casts, event triggers (#3911) ──────
|
|
618
633
|
|
|
619
634
|
/** The kinds that live in no schema. */
|
|
620
|
-
const UNPLACED: ReadonlySet<CanonicalKind> = new Set(["schema", "extension", "role", "cast", "eventTrigger"]);
|
|
635
|
+
const UNPLACED: ReadonlySet<CanonicalKind> = new Set(["schema", "extension", "role", "cast", "eventTrigger", "foreignDataWrapper", "foreignServer", "publication"]);
|
|
621
636
|
|
|
622
637
|
/**
|
|
623
638
|
* A function named where an object calls it (an aggregate's SFUNC, a cast's
|
|
@@ -700,6 +715,80 @@ function collationFields(p: CollationProps, fields: Record<string, unknown>): vo
|
|
|
700
715
|
fields.rules = o.rules;
|
|
701
716
|
}
|
|
702
717
|
|
|
718
|
+
/** An operator's identity among its overloads: its operand types, `(integer,integer)`, `NONE` for a missing side. */
|
|
719
|
+
export function operatorSignature(p: Pick<OperatorProps, "leftArg" | "rightArg">, defaultSchema: string): string {
|
|
720
|
+
const side = (t: string | undefined) => (t === undefined ? "NONE" : canonicalArgType(t, defaultSchema));
|
|
721
|
+
return `(${side(p.leftArg)},${side(p.rightArg)})`;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* An operator named where another object names it (a COMMUTATOR or NEGATOR):
|
|
726
|
+
* `OPERATOR(app.===)`, `app.===` or a bare symbol, which the server looks for
|
|
727
|
+
* in the new operator's own schema, as `schema.symbol`.
|
|
728
|
+
*/
|
|
729
|
+
export function canonicalOperatorRef(text: string, ownSchema: string): string {
|
|
730
|
+
const inner = text.trim().replace(/^OPERATOR\s*\(\s*(.*?)\s*\)$/is, "$1");
|
|
731
|
+
const m = /^("(?:[^"]|"")+"|[A-Za-z_][A-Za-z0-9_$]*)\s*\.\s*(\S.*)$/s.exec(inner);
|
|
732
|
+
return `${quoteIdent(m ? identValue(m[1]!) : ownSchema)}.${(m ? m[2]! : inner).trim()}`;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/** An operator's definition as compared: each function and operator named canonically, a flag only when set. */
|
|
736
|
+
function operatorOptions(p: OperatorProps, ownSchema: string, defaultSchema: string): Record<string, string> {
|
|
737
|
+
const out: Record<string, string> = {};
|
|
738
|
+
for (const [k, v] of Object.entries(p.options)) {
|
|
739
|
+
if (k === "function" || k === "restrict" || k === "join") out[k] = canonicalFunctionName(v, defaultSchema);
|
|
740
|
+
else if (k === "commutator" || k === "negator") out[k] = canonicalOperatorRef(v, ownSchema);
|
|
741
|
+
else if (k === "hashes" || k === "merges") {
|
|
742
|
+
if (isTrue(v)) out[k] = "true";
|
|
743
|
+
} else out[k] = defValue(v);
|
|
744
|
+
}
|
|
745
|
+
return Object.fromEntries(Object.entries(out).sort(([a], [b]) => (a < b ? -1 : 1)));
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/** An `OPTIONS (...)` list as compared: by name, none as nothing. */
|
|
749
|
+
function sortedOptions(options: Record<string, string> | undefined): Record<string, string> | undefined {
|
|
750
|
+
const entries = Object.entries(options ?? {}).sort(([a], [b]) => (a < b ? -1 : 1));
|
|
751
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const STATISTICS_ALL = ["dependencies", "mcv", "ndistinct"];
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* The kinds of statistics gathered, sorted. Left out, the server builds all
|
|
758
|
+
* three, except for a lone expression, which gets none of them (only the
|
|
759
|
+
* expression's own statistics); `pg_get_statisticsobjdef` prints the list only
|
|
760
|
+
* when it is not that default.
|
|
761
|
+
*/
|
|
762
|
+
export function statisticsKinds(kinds: readonly string[] | undefined, targets: readonly string[]): string[] | undefined {
|
|
763
|
+
if (kinds && kinds.length > 0) {
|
|
764
|
+
const sorted = [...new Set(kinds.map((k) => k.toLowerCase()))].sort();
|
|
765
|
+
return sorted.join() === STATISTICS_ALL.join() ? undefined : sorted;
|
|
766
|
+
}
|
|
767
|
+
const lone = targets.length === 1 && !/^"?[A-Za-z_][A-Za-z0-9_$]*"?$/.test(targets[0]!.trim());
|
|
768
|
+
return lone ? ["none"] : undefined;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/** Every action a publication publishes when `publish` is not given. */
|
|
772
|
+
const PUBLISH_ALL = ["delete", "insert", "truncate", "update"];
|
|
773
|
+
|
|
774
|
+
/** A publication as compared: its tables with their column lists and row filters, its schemas, and each option at its default left out. */
|
|
775
|
+
function publicationFields(p: PublicationProps, defaultSchema: string, fields: Record<string, unknown>): void {
|
|
776
|
+
fields.allTables = p.allTables === true;
|
|
777
|
+
const tables = p.tables.map((t) => ({
|
|
778
|
+
table: canonicalName(t.tableName, defaultSchema),
|
|
779
|
+
...(t.columns && t.columns.length > 0 ? { columns: [...t.columns].sort() } : {}),
|
|
780
|
+
...(t.where ? { where: canonicalExpr(t.where) } : {}),
|
|
781
|
+
}));
|
|
782
|
+
fields.tables = tables.length > 0 ? tables.sort((a, b) => (a.table < b.table ? -1 : 1)) : undefined;
|
|
783
|
+
fields.schemas = p.schemas.length > 0 ? p.schemas.map((s) => quoteIdent(identValue(s))).sort() : undefined;
|
|
784
|
+
const o = p.options;
|
|
785
|
+
const publish = o.publish === undefined ? PUBLISH_ALL : [...new Set(defValue(o.publish).split(",").map((x) => x.trim().toLowerCase()).filter(Boolean))].sort();
|
|
786
|
+
fields.publish = publish.join() === PUBLISH_ALL.join() ? undefined : publish;
|
|
787
|
+
fields.viaRoot = isTrue(o.publish_via_partition_root);
|
|
788
|
+
const generated = o.publish_generated_columns === undefined ? "none" : defValue(o.publish_generated_columns);
|
|
789
|
+
fields.generatedColumns = generated === "none" ? undefined : generated;
|
|
790
|
+
}
|
|
791
|
+
|
|
703
792
|
/** A cast's name, `(source AS target)`, its types canonical. */
|
|
704
793
|
export const castName = (p: Pick<CastProps, "sourceType" | "targetType">, defaultSchema: string): string =>
|
|
705
794
|
`(${canonicalType(p.sourceType, defaultSchema)} AS ${canonicalType(p.targetType, defaultSchema)})`;
|
|
@@ -774,6 +863,38 @@ export function canonicalPgObject(entityType: string, props: Record<string, unkn
|
|
|
774
863
|
fields.function = canonicalFunctionName(p.functionName, defaultSchema);
|
|
775
864
|
break;
|
|
776
865
|
}
|
|
866
|
+
case "operator": {
|
|
867
|
+
const p = props as unknown as OperatorProps;
|
|
868
|
+
base.signature = operatorSignature(p, defaultSchema);
|
|
869
|
+
fields.definition = operatorOptions(p, base.schema!, defaultSchema);
|
|
870
|
+
break;
|
|
871
|
+
}
|
|
872
|
+
case "statistics": {
|
|
873
|
+
const p = props as unknown as StatisticsProps;
|
|
874
|
+
fields.table = canonicalName(p.tableName, defaultSchema);
|
|
875
|
+
const targets = p.targets.map((t) => canonicalExpr(t)!).sort();
|
|
876
|
+
fields.targets = targets;
|
|
877
|
+
fields.kinds = statisticsKinds(p.kinds, p.targets);
|
|
878
|
+
break;
|
|
879
|
+
}
|
|
880
|
+
case "foreignDataWrapper": {
|
|
881
|
+
const p = props as unknown as ForeignDataWrapperProps;
|
|
882
|
+
fields.handler = p.handlerName && !p.noHandler ? canonicalFunctionName(p.handlerName, defaultSchema) : undefined;
|
|
883
|
+
fields.validator = p.validatorName && !p.noValidator ? canonicalFunctionName(p.validatorName, defaultSchema) : undefined;
|
|
884
|
+
fields.options = sortedOptions(p.options);
|
|
885
|
+
break;
|
|
886
|
+
}
|
|
887
|
+
case "foreignServer": {
|
|
888
|
+
const p = props as unknown as ForeignServerProps;
|
|
889
|
+
fields.serverType = p.serverType;
|
|
890
|
+
fields.version = p.version;
|
|
891
|
+
fields.wrapper = quoteIdent(identValue(p.wrapperName));
|
|
892
|
+
fields.options = sortedOptions(p.options);
|
|
893
|
+
break;
|
|
894
|
+
}
|
|
895
|
+
case "publication":
|
|
896
|
+
publicationFields(props as unknown as PublicationProps, defaultSchema, fields);
|
|
897
|
+
break;
|
|
777
898
|
case "function":
|
|
778
899
|
case "procedure": {
|
|
779
900
|
const p = props as unknown as RoutineProps;
|
|
@@ -360,14 +360,14 @@ export const PG_CLASSIFIER_RULES = {
|
|
|
360
360
|
SQLPG300: rule(
|
|
361
361
|
"SQLPG300",
|
|
362
362
|
"metadata",
|
|
363
|
-
"Drop and create a type, aggregate, collation, cast
|
|
364
|
-
"A composite or range type's definition, a collation's, a cast's
|
|
363
|
+
"Drop and create a type, aggregate, collation, cast, event trigger, operator, statistics object, foreign server or publication",
|
|
364
|
+
"A composite or range type's definition, a collation's, a cast's, an event trigger's, an operator's function and operands and extended statistics' have no ALTER, an aggregate's argument types, state type and final function cannot be replaced, and neither can a foreign server's type or wrapper or a publication's FOR ALL TABLES: the object is dropped and created in one transaction. Nothing on the server depends on it (a column, a view, a function or a cast would make the DROP fail); the locks are on its catalog rows only. Dropped statistics are empty until the next ANALYZE, and a publication that is dropped stops its subscribers until it is created again.",
|
|
365
365
|
`${DOCS}/sql-createtype.html`,
|
|
366
366
|
),
|
|
367
367
|
SQLPG301: rule(
|
|
368
368
|
"SQLPG301",
|
|
369
369
|
"expand",
|
|
370
|
-
"Change a type, aggregate, collation or
|
|
370
|
+
"Change a type, aggregate, collation, cast, foreign server or publication that other objects depend on",
|
|
371
371
|
"No ALTER makes the change, and DROP refuses while a column, a view, a function or another object depends on the object. Create the new definition under another name, move what depends on it, then drop the old.",
|
|
372
372
|
`${DOCS}/sql-droptype.html`,
|
|
373
373
|
),
|
|
@@ -378,6 +378,13 @@ export const PG_CLASSIFIER_RULES = {
|
|
|
378
378
|
"CREATE OR REPLACE AGGREGATE (14 and later) replaces the aggregate's functions and options in the catalog; no table is locked. Its argument types, its result and its kind (plain, ordered-set, hypothetical-set) have to stay the same, so a change of the state type or the final function is dropped and created instead (SQLPG300).",
|
|
379
379
|
`${DOCS}/sql-createaggregate.html`,
|
|
380
380
|
),
|
|
381
|
+
SQLPG303: rule(
|
|
382
|
+
"SQLPG303",
|
|
383
|
+
"metadata",
|
|
384
|
+
"Alter a foreign data wrapper, foreign server or publication in place",
|
|
385
|
+
"ALTER FOREIGN DATA WRAPPER (handler, validator, options), ALTER SERVER (version, options) and ALTER PUBLICATION (SET options, ADD and DROP TABLE and TABLES IN SCHEMA) change the object's catalog rows under a brief lock; no table is rewritten. A publication's change reaches its subscribers as the walsender next reads it: a table added is not copied to a subscription until the subscriber runs REFRESH PUBLICATION.",
|
|
386
|
+
`${DOCS}/sql-alterpublication.html`,
|
|
387
|
+
),
|
|
381
388
|
SQLPG270: rule("SQLPG270", "drop", "Drop an object", "The object and, for a table, a materialized view or a sequence, its data are gone.", `${DOCS}/sql-droptable.html`),
|
|
382
389
|
} as const satisfies Record<string, PgClassifierRule>;
|
|
383
390
|
|
|
@@ -106,7 +106,7 @@ export function pgSchemaFromBuildFile(path: string, defaultSchema = "public"): P
|
|
|
106
106
|
*/
|
|
107
107
|
export function pgNamespace(kind: string): string {
|
|
108
108
|
if (kind === "schema" || kind === "extension" || kind === "trigger" || kind === "policy" || kind === "role") return kind;
|
|
109
|
-
if (kind === "collation" || kind === "cast" || kind === "eventTrigger") return kind;
|
|
109
|
+
if (kind === "collation" || kind === "cast" || kind === "eventTrigger" || kind === "operator" || kind === "statistics" || kind === "foreignDataWrapper" || kind === "foreignServer" || kind === "publication") return kind;
|
|
110
110
|
if (kind === "grant" || kind === "defaultPrivileges") return "grant";
|
|
111
111
|
if (kind === "enum" || kind === "domain" || kind === "composite" || kind === "range") return "type";
|
|
112
112
|
if (kind === "function" || kind === "procedure" || kind === "aggregate") return "routine";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options that hold a credential. A foreign data wrapper's or server's
|
|
3
|
+
* `OPTIONS (...)` can carry any name, and a password in one would be written
|
|
4
|
+
* into a schema file by import and sent to every reader of the plan. The
|
|
5
|
+
* environment supplies credentials, in a user mapping (which is not declared)
|
|
6
|
+
* or its own secret store, as for a role's password (`./entities.ts`).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Whether an option name reads as a credential: `password`, `sslpassword`, `secret_key`, `api_token`. */
|
|
10
|
+
export const isSecretOption = (name: string): boolean => /pass(word|wd)?|secret|token|api[_-]?key|credential/i.test(name);
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* sequence options, storage parameters, comments), an extension and the
|
|
6
6
|
* domain over its type, a collation, composite and range types (and a
|
|
7
7
|
* multirange column), plain and ordered-set aggregates, a cast and an event
|
|
8
|
-
* trigger
|
|
8
|
+
* trigger, an operator, extended statistics, a foreign data wrapper and server, and a
|
|
9
|
+
* publication (#3911), a partitioned table and a partition, an ORM's
|
|
9
10
|
* revision table that import leaves out, and an object carrying chant's
|
|
10
11
|
* ownership trailer.
|
|
11
12
|
*/
|
|
@@ -60,5 +61,16 @@ CREATE CAST (app.pair AS text) WITH FUNCTION app.pair_text(app.pair) AS ASSIGNME
|
|
|
60
61
|
COMMENT ON CAST (app.pair AS text) IS 'a:b';
|
|
61
62
|
CREATE FUNCTION app.audit() RETURNS event_trigger LANGUAGE plpgsql AS $$ BEGIN END $$;
|
|
62
63
|
CREATE EVENT TRIGGER app_audit ON ddl_command_end WHEN TAG IN ('CREATE TABLE') EXECUTE FUNCTION app.audit();
|
|
64
|
+
CREATE FUNCTION app.int_same(a integer, b integer) RETURNS boolean LANGUAGE sql IMMUTABLE AS $$ SELECT a = b $$;
|
|
65
|
+
CREATE OPERATOR app.=== (FUNCTION = app.int_same, LEFTARG = integer, RIGHTARG = integer, COMMUTATOR = OPERATOR(app.===), RESTRICT = eqsel, JOIN = eqjoinsel, HASHES, MERGES);
|
|
66
|
+
COMMENT ON OPERATOR app.===(integer, integer) IS 'Same value';
|
|
67
|
+
CREATE STATISTICS app.orders_stats (dependencies) ON user_id, status FROM app.orders;
|
|
68
|
+
COMMENT ON STATISTICS app.orders_stats IS 'Users and their order states';
|
|
69
|
+
CREATE FOREIGN DATA WRAPPER app_fdw OPTIONS (debug 'true');
|
|
70
|
+
COMMENT ON FOREIGN DATA WRAPPER app_fdw IS 'Remote tables';
|
|
71
|
+
CREATE SERVER app_replica TYPE 'pg' VERSION '16' FOREIGN DATA WRAPPER app_fdw OPTIONS (host 'db.internal', port '5432');
|
|
72
|
+
COMMENT ON SERVER app_replica IS 'The read replica';
|
|
73
|
+
CREATE PUBLICATION app_pub FOR TABLE app.orders, app.users WITH (publish = 'insert, update');
|
|
74
|
+
COMMENT ON PUBLICATION app_pub IS 'Order changes';
|
|
63
75
|
CREATE TABLE public._prisma_migrations (id varchar(36) PRIMARY KEY, checksum varchar(64) NOT NULL);
|
|
64
76
|
`;
|
package/src/postgres.ts
CHANGED
|
@@ -20,6 +20,11 @@ export {
|
|
|
20
20
|
aggregate,
|
|
21
21
|
trigger,
|
|
22
22
|
eventTrigger,
|
|
23
|
+
operator,
|
|
24
|
+
statistics,
|
|
25
|
+
foreignDataWrapper,
|
|
26
|
+
foreignServer,
|
|
27
|
+
publication,
|
|
23
28
|
collation,
|
|
24
29
|
cast,
|
|
25
30
|
policy,
|
|
@@ -49,6 +54,11 @@ export {
|
|
|
49
54
|
type PostgresCollation,
|
|
50
55
|
type PostgresCast,
|
|
51
56
|
type PostgresEventTrigger,
|
|
57
|
+
type PostgresOperator,
|
|
58
|
+
type PostgresStatistics,
|
|
59
|
+
type PostgresForeignDataWrapper,
|
|
60
|
+
type PostgresForeignServer,
|
|
61
|
+
type PostgresPublication,
|
|
52
62
|
type PostgresExtension,
|
|
53
63
|
type PostgresFunction,
|
|
54
64
|
type PostgresProcedure,
|
|
@@ -75,6 +85,12 @@ export {
|
|
|
75
85
|
type CollationProps,
|
|
76
86
|
type CastProps,
|
|
77
87
|
type EventTriggerProps,
|
|
88
|
+
type OperatorProps,
|
|
89
|
+
type StatisticsProps,
|
|
90
|
+
type ForeignDataWrapperProps,
|
|
91
|
+
type ForeignServerProps,
|
|
92
|
+
type PublicationProps,
|
|
93
|
+
type PublicationTableDef,
|
|
78
94
|
type DomainProps,
|
|
79
95
|
type ExtensionProps,
|
|
80
96
|
type RoutineProps,
|
package/src/serializer.ts
CHANGED
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
* of each. Two databases of different dialects are two projects (two
|
|
22
22
|
* workspace members, #3047 question 1).
|
|
23
23
|
*
|
|
24
|
+
* A ClickHouse object that names a named collection (an engine's first
|
|
25
|
+
* argument, a table function's, a dictionary source's `NAME`) carries
|
|
26
|
+
* `namedCollections`, and the document lists every one the build names, so
|
|
27
|
+
* a plan can check each exists on the server (#4395).
|
|
28
|
+
*
|
|
24
29
|
* A Postgres document also carries `postgresMajor`, the major the project
|
|
25
30
|
* targets (`sql.postgresMajor`, else the newest pinned major). Post-synth
|
|
26
31
|
* checks and the lock classifier read it; ClickHouse output has no such field.
|
|
@@ -43,6 +48,7 @@ import { sqlFileLineOf, sqlFileOf, sqlFileReferences } from "./sql-files";
|
|
|
43
48
|
import { relative } from "node:path";
|
|
44
49
|
import { getProvenance } from "@intentius/chant/provenance";
|
|
45
50
|
import type { UnparsedClause } from "./core/interpolation";
|
|
51
|
+
import { namedCollectionReferences, type CollectionReferrer } from "./clickhouse/named-collections";
|
|
46
52
|
|
|
47
53
|
export { applyOrder } from "./core/references";
|
|
48
54
|
|
|
@@ -89,8 +95,9 @@ function objectJson(name: string, obj: ClickHouseObject, names: Map<Declarable,
|
|
|
89
95
|
props.lineage = lineageJson(props.lineage as LineageEdge[], names);
|
|
90
96
|
}
|
|
91
97
|
const dependsOn = [...new Set([...obj.dependsOn.map((r) => referenceName(r, names)).filter((n) => n !== undefined), ...extra])];
|
|
98
|
+
const collections = namedCollectionReferences(props as CollectionReferrer);
|
|
92
99
|
return JSON.parse(
|
|
93
|
-
JSON.stringify({ export: name, type: obj.entityType, sqlName: obj.sqlName, ...props, dependsOn, ddl }),
|
|
100
|
+
JSON.stringify({ export: name, type: obj.entityType, sqlName: obj.sqlName, ...props, dependsOn, ...(collections.length > 0 ? { namedCollections: collections } : {}), ddl }),
|
|
94
101
|
) as Record<string, unknown>;
|
|
95
102
|
}
|
|
96
103
|
|
|
@@ -170,10 +177,14 @@ export const sqlSerializer: Serializer = {
|
|
|
170
177
|
|
|
171
178
|
const extra = sqlFileReferences("clickhouse", objects);
|
|
172
179
|
const order = applyOrder(objects, names, extra);
|
|
180
|
+
const json = order.map((n) => objectJson(n, objects.get(n)!, names, extra.get(n)));
|
|
181
|
+
// The named collections the build references (#4395), each once, by name: a plan checks each exists.
|
|
182
|
+
const collections = [...new Set(json.flatMap((o) => (o.namedCollections as string[] | undefined) ?? []))].sort();
|
|
173
183
|
const doc = {
|
|
174
184
|
dialect: "clickhouse",
|
|
175
185
|
applyOrder: order,
|
|
176
|
-
|
|
186
|
+
...(collections.length > 0 ? { namedCollections: collections } : {}),
|
|
187
|
+
objects: json,
|
|
177
188
|
};
|
|
178
189
|
const ddl = order
|
|
179
190
|
.map((n) => `${(objects.get(n)!.props as { ddl: string }).ddl};`)
|
|
@@ -114,12 +114,12 @@ or set `replicationLag: false` to run without the check.
|
|
|
114
114
|
The Plan phase refuses, naming each object and what to do, a table or column with:
|
|
115
115
|
|
|
116
116
|
- something on the column it does not carry over: an exclusion constraint, a constraint trigger, a materialized view, a rule, a trigger, a policy, a statistics object, a generated column computed from it, an INVALID index, or a view the build does not declare;
|
|
117
|
-
- on a partitioned table,
|
|
117
|
+
- on a partitioned table, a key or foreign key on the column, or the column in the partition key;
|
|
118
118
|
- a partition (run the Op on its partitioned table) or an inheritance tree (chant #3333);
|
|
119
119
|
- a generated column itself, which the applier changes in place;
|
|
120
120
|
- no primary key;
|
|
121
|
-
- a rename combined with a type change
|
|
122
|
-
- a rename of a column with a default, an identity or a serial sequence:
|
|
121
|
+
- a rename combined with a type change, unless the Op gives `reverse` (the old column's value from the new one); with a default, a serial or an identity as well, run the Op twice, the rename first;
|
|
122
|
+
- a rename of a column with a default, an identity or a serial sequence, unless the Op gives `defaultAtContract: true` (the default lands on the new column at the contract; an explicit NULL into the new name gets the default until then);
|
|
123
123
|
- a column a logical replication publication sends; a column list that leaves it out does not stop the Op (chant #3332);
|
|
124
124
|
- a column that needs no migration, where the message says to use the applier.
|
|
125
125
|
|
|
@@ -42,6 +42,11 @@ The export name (`users`) is the object's identity in chant. The name in the SQL
|
|
|
42
42
|
| `aggregate` | `CREATE AGGREGATE` | `Postgres::Aggregate` |
|
|
43
43
|
| `trigger` | `CREATE TRIGGER` | `Postgres::Trigger` |
|
|
44
44
|
| `eventTrigger` | `CREATE EVENT TRIGGER` | `Postgres::EventTrigger` |
|
|
45
|
+
| `operator` | `CREATE OPERATOR` | `Postgres::Operator` |
|
|
46
|
+
| `statistics` | `CREATE STATISTICS` | `Postgres::Statistics` |
|
|
47
|
+
| `foreignDataWrapper` | `CREATE FOREIGN DATA WRAPPER` | `Postgres::ForeignDataWrapper` |
|
|
48
|
+
| `foreignServer` | `CREATE SERVER` | `Postgres::ForeignServer` |
|
|
49
|
+
| `publication` | `CREATE PUBLICATION` | `Postgres::Publication` |
|
|
45
50
|
| `collation` | `CREATE COLLATION` | `Postgres::Collation` |
|
|
46
51
|
| `cast` | `CREATE CAST` | `Postgres::Cast` |
|
|
47
52
|
| `policy` | `CREATE POLICY` | `Postgres::Policy` |
|
|
@@ -189,4 +194,4 @@ chant build src --lexicon sql -o dist/schema.json
|
|
|
189
194
|
|
|
190
195
|
## Not covered
|
|
191
196
|
|
|
192
|
-
`ALTER` (other than a table's row-level security), role memberships, rules,
|
|
197
|
+
`ALTER` (other than a table's row-level security), role memberships, rules, operator classes and families, foreign tables, user mappings, subscriptions, text search objects and base types are not declared with the tags. Import names an object of such a kind and stops; `docs/pages/object-coverage.mdx` lists every `CREATE` command and why. An aggregate is identified by its argument types; a cast is named `(source AS target)` and an event trigger lives in no schema. A definition change to a composite or range type, collation, cast or event trigger is a drop and a create, refused while something depends on it. Planning a schema change (`chant sql diff`, `chant sql plan`, the lock classes) is the `chant-sql-postgres-plan` skill. Apply and the expand-and-contract migration Op have their own skills, which arrive with the code they describe.
|
package/src/skills/chant-sql.md
CHANGED
|
@@ -114,6 +114,7 @@ When chant interprets a composite's factory at build time, each field records th
|
|
|
114
114
|
| SQLCH002 | lint | a `Nullable` column in the sort key or primary key, which ClickHouse refuses without `allow_nullable_key` |
|
|
115
115
|
| SQLCH003 | lint | `${events.kind}` where `${events.columns.kind}` was meant |
|
|
116
116
|
| SQLCH101 | build | an engine the pinned ClickHouse server does not have |
|
|
117
|
+
| SQLCH128 | build | a literal credential in an integration engine's or table function's arguments; name a named collection instead |
|
|
117
118
|
|
|
118
119
|
## Commands
|
|
119
120
|
|
|
@@ -126,6 +127,6 @@ chant build src --lexicon sql -o dist/schema.json
|
|
|
126
127
|
|
|
127
128
|
## Not covered
|
|
128
129
|
|
|
129
|
-
`CREATE TABLE ... AS` and `CREATE TABLE ... AS SELECT` are not declared with the tags.
|
|
130
|
+
`CREATE TABLE ... AS` and `CREATE TABLE ... AS SELECT` are not declared with the tags. A named collection is never declared: name one in an integration engine, a table function or a dictionary `SOURCE` (`ENGINE = S3(lake_creds, format = 'Parquet')`), and `chant sql plan` checks that the server has it. A second dialect is not supported.
|
|
130
131
|
|
|
131
132
|
Note that `chant migrate` does not run schema migrations. Schema changes go through `chant sql plan`, apply and the rebuild Op, described in the `chant-sql-plan` and `chant-sql-rebuild` skills.
|
package/src/sql-types-command.ts
CHANGED
|
@@ -18,6 +18,12 @@ import { basename, dirname, join, relative, resolve } from "node:path";
|
|
|
18
18
|
import type { CommandGroupContext } from "@intentius/chant/cli/command-group";
|
|
19
19
|
import { renderSqlTypes, SQL_TYPES_FILE } from "./sql-types";
|
|
20
20
|
|
|
21
|
+
export interface ProjectLocation {
|
|
22
|
+
config: Awaited<ReturnType<typeof import("@intentius/chant/config").loadChantConfigUpward>>["config"];
|
|
23
|
+
projectDir: string;
|
|
24
|
+
sourceDir: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
const USAGE = "usage: chant sql types [--output <path>] [--check]";
|
|
22
28
|
|
|
23
29
|
/** The `--output` value, or an error message. */
|
|
@@ -43,37 +49,53 @@ export function shadowingSource(path: string): string | undefined {
|
|
|
43
49
|
return existsSync(source) ? source : undefined;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if ("error" in args) {
|
|
49
|
-
console.error(`${args.error}\n${USAGE}`);
|
|
50
|
-
return 1;
|
|
51
|
-
}
|
|
52
|
-
const cwd = process.cwd();
|
|
52
|
+
/** The project the working directory belongs to: its config, its root and its source directory. */
|
|
53
|
+
export async function loadProject(cwd: string): Promise<ProjectLocation> {
|
|
53
54
|
const { loadChantConfigUpward } = await import("@intentius/chant/config");
|
|
54
55
|
const { config, configPath } = await loadChantConfigUpward(cwd);
|
|
55
56
|
const projectDir = configPath ? dirname(configPath) : cwd;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const shadow = shadowingSource(output);
|
|
59
|
-
if (shadow) {
|
|
60
|
-
console.error(`chant sql types: ${relative(cwd, shadow) || basename(shadow)} sits beside ${relative(cwd, output) || basename(output)}, and tsc reads it instead of the declarations; rename one`);
|
|
61
|
-
return 1;
|
|
62
|
-
}
|
|
57
|
+
return { config, projectDir, sourceDir: resolve(projectDir, config.sourceDir ?? ".") };
|
|
58
|
+
}
|
|
63
59
|
|
|
60
|
+
/**
|
|
61
|
+
* The fold build's entities: what `chant build` declares, `.sql` files and
|
|
62
|
+
* composite members included. Prints the build's errors and returns
|
|
63
|
+
* undefined when it fails.
|
|
64
|
+
*/
|
|
65
|
+
export async function buildFolded(project: ProjectLocation, command: string, consequence: string) {
|
|
66
|
+
const { config, projectDir, sourceDir } = project;
|
|
64
67
|
const { build } = await import("@intentius/chant/build");
|
|
65
68
|
const { resolveBuildModes, resolveProjectBuildOptions } = await import("@intentius/chant/cli/build-options");
|
|
66
69
|
const { loadPlugins, resolveProjectLexicons } = await import("@intentius/chant/cli/plugins");
|
|
67
70
|
const { sqlSerializer } = await import("./serializer");
|
|
68
71
|
const plugins = await loadPlugins(await resolveProjectLexicons(projectDir, { lexicon: "sql" }));
|
|
69
|
-
// The fold build's entities: what `chant build` declares, `.sql` files and composite members included.
|
|
70
72
|
const options = resolveProjectBuildOptions({ config, configDir: projectDir, plugins, modes: resolveBuildModes(config, { fold: true }) });
|
|
71
73
|
const result = await build(sourceDir, [sqlSerializer], undefined, options);
|
|
72
74
|
if (result.errors.length > 0) {
|
|
73
75
|
for (const e of result.errors) console.error(e instanceof Error ? e.message : String(e));
|
|
74
|
-
console.error(
|
|
76
|
+
console.error(`${command}: the build failed, so ${consequence}`);
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function runTypes(ctx: CommandGroupContext): Promise<number> {
|
|
83
|
+
const args = parseArgs(ctx.rawArgs);
|
|
84
|
+
if ("error" in args) {
|
|
85
|
+
console.error(`${args.error}\n${USAGE}`);
|
|
75
86
|
return 1;
|
|
76
87
|
}
|
|
88
|
+
const cwd = process.cwd();
|
|
89
|
+
const { config, projectDir, sourceDir } = await loadProject(cwd);
|
|
90
|
+
const output = args.output !== undefined ? resolve(cwd, args.output) : join(sourceDir, SQL_TYPES_FILE);
|
|
91
|
+
const shadow = shadowingSource(output);
|
|
92
|
+
if (shadow) {
|
|
93
|
+
console.error(`chant sql types: ${relative(cwd, shadow) || basename(shadow)} sits beside ${relative(cwd, output) || basename(output)}, and tsc reads it instead of the declarations; rename one`);
|
|
94
|
+
return 1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const result = await buildFolded({ config, projectDir, sourceDir }, "chant sql types", "nothing was written");
|
|
98
|
+
if (!result) return 1;
|
|
77
99
|
const text = renderSqlTypes(result.entities, { projectDir, config: config as unknown as Record<string, unknown> });
|
|
78
100
|
const shown = relative(cwd, output);
|
|
79
101
|
const named = shown && !shown.startsWith("..") ? shown : output;
|
package/src/sql-types.ts
CHANGED
|
@@ -133,7 +133,7 @@ function originOf(entity: Declarable, exportName: string, projectDir: string): s
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/** The names a relation's `.columns` record holds at run time, in order. */
|
|
136
|
-
const runtimeColumns = (entity: Declarable | undefined): string[] => Object.keys((entity as { columns?: object } | undefined)?.columns ?? {});
|
|
136
|
+
export const runtimeColumns = (entity: Declarable | undefined): string[] => Object.keys((entity as { columns?: object } | undefined)?.columns ?? {});
|
|
137
137
|
|
|
138
138
|
/** Whether `expr` is the column `column` alone, bare or qualified (`o.user_id`). */
|
|
139
139
|
function isBareColumn(expr: string, quoted: string): boolean {
|
|
@@ -148,7 +148,7 @@ interface LineageJson {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/** A view's column that passes one column through: that column's export and name. */
|
|
151
|
-
function passthrough(o: OutputObject, name: string, quote: (c: string) => string): { export: string; column: string } | undefined {
|
|
151
|
+
export function passthrough(o: OutputObject, name: string, quote: (c: string) => string): { export: string; column: string } | undefined {
|
|
152
152
|
const edge = ((o.lineage as LineageJson[] | undefined) ?? []).find((e) => e.output === name);
|
|
153
153
|
const ref = edge?.from.length === 1 ? edge.from[0] : undefined;
|
|
154
154
|
if (!edge || !ref) return undefined;
|
|
@@ -177,7 +177,7 @@ function spell(canonical: string, t: ParsedType): string {
|
|
|
177
177
|
const arrayOf = (ts: string): string => `${/[|&]/.test(ts) && !/^\{.*\}$/s.test(ts) ? `(${ts})` : ts}[]`;
|
|
178
178
|
|
|
179
179
|
/** A Postgres type's spelling, row type, and whether the type itself forbids NULL (a serial, a NOT NULL domain). */
|
|
180
|
-
function pgType(p: PgProject, text: string | undefined, depth = 0): { sqlType: string; ts: string; notNull: boolean } {
|
|
180
|
+
export function pgType(p: PgProject, text: string | undefined, depth = 0): { sqlType: string; ts: string; notNull: boolean } {
|
|
181
181
|
const t = parseType(text);
|
|
182
182
|
if (!t || depth > 5) return { sqlType: text?.trim() || "unknown", ts: "unknown", notNull: false };
|
|
183
183
|
const r = resolveType(p, t, { serial: true });
|
|
@@ -320,7 +320,7 @@ function renderEntry(e: RelationEntry, lines: string[]): void {
|
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
/** The dialect a build's entities declare, else the config's, else ClickHouse. */
|
|
323
|
-
function dialectOf(entities: ReadonlyMap<string, Declarable>, config: Record<string, unknown>): SqlDialect {
|
|
323
|
+
export function dialectOf(entities: ReadonlyMap<string, Declarable>, config: Record<string, unknown>): SqlDialect {
|
|
324
324
|
try {
|
|
325
325
|
return filesDialect(entities, config);
|
|
326
326
|
} catch {
|
|
@@ -328,6 +328,12 @@ function dialectOf(entities: ReadonlyMap<string, Declarable>, config: Record<str
|
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
/** The build's sql output as `chant build` writes it: the objects, and the Postgres major they were built for. */
|
|
332
|
+
export function serializedObjects(entities: ReadonlyMap<string, Declarable>, config: Record<string, unknown>): { objects: OutputObject[]; postgresMajor?: number } {
|
|
333
|
+
const out = sqlSerializer.serialize(new Map(entities), undefined, { config }) as SerializerResult | string;
|
|
334
|
+
return JSON.parse(typeof out === "string" ? out : out.primary) as { objects: OutputObject[]; postgresMajor?: number };
|
|
335
|
+
}
|
|
336
|
+
|
|
331
337
|
/**
|
|
332
338
|
* The `sql-types.d.ts` for a build's entities: one module that augments the
|
|
333
339
|
* dialect's `SqlSchema` with each table, view and dictionary, in key order.
|
|
@@ -340,8 +346,7 @@ export function renderSqlTypes(entities: ReadonlyMap<string, Declarable>, opts:
|
|
|
340
346
|
const sqlEntities = new Map([...entities].filter(([, e]) => isPostgresObject(e) || isClickHouseObject(e)));
|
|
341
347
|
let entries: RelationEntry[] = [];
|
|
342
348
|
if (sqlEntities.size > 0) {
|
|
343
|
-
const
|
|
344
|
-
const doc = JSON.parse(typeof out === "string" ? out : out.primary) as { objects: OutputObject[]; postgresMajor?: number };
|
|
349
|
+
const doc = serializedObjects(entities, config);
|
|
345
350
|
entries = dialect === "postgres" ? postgresEntries(doc.objects, doc.postgresMajor ?? PINNED_MAJOR, entities, projectDir) : clickhouseEntries(doc.objects, entities, projectDir);
|
|
346
351
|
}
|
|
347
352
|
const byKey = new Map<string, RelationEntry>();
|