@prisma-next/target-postgres 0.14.0-dev.5 → 0.14.0-dev.51
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/authoring-DzHOdWTl.mjs +364 -0
- package/dist/authoring-DzHOdWTl.mjs.map +1 -0
- package/dist/canonicalize-Bn3BM1Cn.mjs +46 -0
- package/dist/canonicalize-Bn3BM1Cn.mjs.map +1 -0
- package/dist/canonicalize-CAbXtVtB.d.mts +37 -0
- package/dist/canonicalize-CAbXtVtB.d.mts.map +1 -0
- package/dist/{codec-ids-CnXu9Qy3.d.mts → codec-ids-BzrFF-I4.d.mts} +3 -2
- package/dist/codec-ids-BzrFF-I4.d.mts.map +1 -0
- package/dist/{codec-ids-BvytN2P8.mjs → codec-ids-C_-Hj6bL.mjs} +3 -2
- package/dist/codec-ids-C_-Hj6bL.mjs.map +1 -0
- package/dist/codec-ids.d.mts +2 -2
- package/dist/codec-ids.mjs +2 -2
- package/dist/{codec-types-DHCkwPKE.d.mts → codec-types-BDVmvKAW.d.mts} +3 -2
- package/dist/codec-types-BDVmvKAW.d.mts.map +1 -0
- package/dist/codec-types.d.mts +1 -1
- package/dist/{codecs--0A5_4Bq.d.mts → codecs-Dk2sxGLv.d.mts} +77 -4
- package/dist/codecs-Dk2sxGLv.d.mts.map +1 -0
- package/dist/codecs.d.mts +1 -1
- package/dist/codecs.mjs +117 -2
- package/dist/codecs.mjs.map +1 -1
- package/dist/contract-free.d.mts +8 -3
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +4 -4
- package/dist/control-policy-DUFgy9jO.mjs +1103 -0
- package/dist/control-policy-DUFgy9jO.mjs.map +1 -0
- package/dist/control.d.mts +2 -2
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +757 -35
- package/dist/control.mjs.map +1 -1
- package/dist/data-transform-BOWpliq8.mjs.map +1 -1
- package/dist/{data-transform-DDgWdB5o.d.mts → data-transform-DPzwBMhW.d.mts} +3 -3
- package/dist/{data-transform-DDgWdB5o.d.mts.map → data-transform-DPzwBMhW.d.mts.map} +1 -1
- package/dist/data-transform.d.mts +1 -1
- package/dist/{ddl-QDyOSeLc.mjs → ddl-ujxH2A7r.mjs} +63 -6
- package/dist/ddl-ujxH2A7r.mjs.map +1 -0
- package/dist/ddl.d.mts +3 -2
- package/dist/ddl.mjs +2 -2
- package/dist/default-normalizer.d.mts +1 -1
- package/dist/default-normalizer.d.mts.map +1 -1
- package/dist/default-normalizer.mjs +247 -1
- package/dist/default-normalizer.mjs.map +1 -0
- package/dist/descriptor-meta-CuGDsrG4.mjs +19 -0
- package/dist/descriptor-meta-CuGDsrG4.mjs.map +1 -0
- package/dist/descriptor-meta-runtime-UwYdV8fD.mjs +12 -0
- package/dist/descriptor-meta-runtime-UwYdV8fD.mjs.map +1 -0
- package/dist/diff-database-schema-BmSmWcaY.mjs +313 -0
- package/dist/diff-database-schema-BmSmWcaY.mjs.map +1 -0
- package/dist/diff-database-schema.d.mts +46 -0
- package/dist/diff-database-schema.d.mts.map +1 -0
- package/dist/diff-database-schema.mjs +2 -0
- package/dist/issue-planner.d.mts +35 -11
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +2 -2
- package/dist/migration.d.mts +5 -5
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +4 -4
- package/dist/namespace-ids-xtp_ZY86.mjs +7 -0
- package/dist/namespace-ids-xtp_ZY86.mjs.map +1 -0
- package/dist/native-type-normalizer.mjs +37 -1
- package/dist/native-type-normalizer.mjs.map +1 -0
- package/dist/nodes-CSkPX3rI.d.mts +120 -0
- package/dist/nodes-CSkPX3rI.d.mts.map +1 -0
- package/dist/{nodes-Bbhs2rwj.mjs → nodes-Dh__NS58.mjs} +56 -2
- package/dist/nodes-Dh__NS58.mjs.map +1 -0
- package/dist/{op-factory-call-D_p5vxwt.mjs → op-factory-call-BKRnMgrB.mjs} +205 -31
- package/dist/op-factory-call-BKRnMgrB.mjs.map +1 -0
- package/dist/{op-factory-call-DmQEc3XV.d.mts → op-factory-call-Dt-Jx5JS.d.mts} +46 -15
- package/dist/op-factory-call-Dt-Jx5JS.d.mts.map +1 -0
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.mjs +2 -2
- package/dist/pack.d.mts +115 -3
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/planner-BMBRPzsv.mjs +367 -0
- package/dist/planner-BMBRPzsv.mjs.map +1 -0
- package/dist/{planner-ddl-builders-B2wOwLqI.mjs → planner-ddl-builders-B8Nn6nHN.mjs} +12 -27
- package/dist/planner-ddl-builders-B8Nn6nHN.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts +4 -6
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +2 -2
- package/dist/planner-identity-values-CJPha2Sz.mjs.map +1 -1
- package/dist/planner-identity-values.d.mts +8 -2
- package/dist/planner-identity-values.d.mts.map +1 -1
- package/dist/planner-identity-values.mjs +2 -2
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts → planner-produced-postgres-migration-C0a7I1-e.d.mts} +4 -4
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts.map → planner-produced-postgres-migration-C0a7I1-e.d.mts.map} +1 -1
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs → planner-produced-postgres-migration-Dp6IO_gj.mjs} +3 -3
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs.map → planner-produced-postgres-migration-Dp6IO_gj.mjs.map} +1 -1
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/{planner-sql-checks-jqUUGyQR.mjs → planner-sql-checks-Cze3vMfe.mjs} +2 -2
- package/dist/{planner-sql-checks-jqUUGyQR.mjs.map → planner-sql-checks-Cze3vMfe.mjs.map} +1 -1
- package/dist/planner-sql-checks.d.mts +2 -2
- package/dist/planner-sql-checks.d.mts.map +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/{planner-target-details-CIY6tLeo.d.mts → planner-target-details-HkP4RrRO.d.mts} +2 -2
- package/dist/planner-target-details-HkP4RrRO.d.mts.map +1 -0
- package/dist/planner-target-details.d.mts +1 -1
- package/dist/planner.d.mts +55 -17
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +3 -2
- package/dist/{postgres-contract-serializer-k3TAcPMY.mjs → postgres-contract-view-3xfUgjnC.mjs} +44 -41
- package/dist/postgres-contract-view-3xfUgjnC.mjs.map +1 -0
- package/dist/postgres-contract-view-C9j5cqP0.d.mts +34 -0
- package/dist/postgres-contract-view-C9j5cqP0.d.mts.map +1 -0
- package/dist/postgres-database-schema-node-C9bTuMrd.d.mts +190 -0
- package/dist/postgres-database-schema-node-C9bTuMrd.d.mts.map +1 -0
- package/dist/{postgres-migration-B5jKrXv3.mjs → postgres-migration-D0jwzYDT.mjs} +21 -2
- package/dist/postgres-migration-D0jwzYDT.mjs.map +1 -0
- package/dist/{postgres-migration-Y4YBJqkS.d.mts → postgres-migration-DFaxuRyI.d.mts} +20 -7
- package/dist/postgres-migration-DFaxuRyI.d.mts.map +1 -0
- package/dist/postgres-native-enum-xdiqmBf_.mjs +41 -0
- package/dist/postgres-native-enum-xdiqmBf_.mjs.map +1 -0
- package/dist/postgres-rls-policy--CSddvMR.d.mts +52 -0
- package/dist/postgres-rls-policy--CSddvMR.d.mts.map +1 -0
- package/dist/{postgres-schema-COGZ1ark.mjs → postgres-schema-CajYAwny.mjs} +135 -7
- package/dist/postgres-schema-CajYAwny.mjs.map +1 -0
- package/dist/postgres-schema-DqnhQRvR.d.mts +226 -0
- package/dist/postgres-schema-DqnhQRvR.d.mts.map +1 -0
- package/dist/postgres-table-schema-node-BgaSrxYN.mjs +249 -0
- package/dist/postgres-table-schema-node-BgaSrxYN.mjs.map +1 -0
- package/dist/render-ops.d.mts +1 -1
- package/dist/{render-typescript-KMgosran.mjs → render-typescript-7yqILcwr.mjs} +45 -16
- package/dist/render-typescript-7yqILcwr.mjs.map +1 -0
- package/dist/render-typescript.mjs +1 -1
- package/dist/resolve-ddl-schema-Bv1ZvjX8.mjs +22 -0
- package/dist/resolve-ddl-schema-Bv1ZvjX8.mjs.map +1 -0
- package/dist/rls-canonicalize.d.mts +2 -0
- package/dist/rls-canonicalize.mjs +2 -0
- package/dist/runtime.d.mts +6 -8
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +3 -3
- package/dist/runtime.mjs.map +1 -1
- package/dist/schema-ir-annotations.d.mts +15 -0
- package/dist/schema-ir-annotations.d.mts.map +1 -0
- package/dist/schema-ir-annotations.mjs +2 -0
- package/dist/schema-node-kinds-ClScchhi.mjs +76 -0
- package/dist/schema-node-kinds-ClScchhi.mjs.map +1 -0
- package/dist/types.d.mts +4 -140
- package/dist/types.mjs +3 -2
- package/package.json +23 -17
- package/src/contract-free/checks.ts +73 -0
- package/src/contract-free/ddl.ts +45 -1
- package/src/core/authoring.ts +287 -2
- package/src/core/codec-ids.ts +1 -0
- package/src/core/codec-type-map.ts +2 -0
- package/src/core/codecs.ts +141 -1
- package/src/core/ddl/nodes.ts +94 -2
- package/src/core/default-normalizer.ts +122 -2
- package/src/core/descriptor-meta.ts +6 -1
- package/src/core/entity-kinds.ts +30 -0
- package/src/core/migrations/bound-schema.ts +10 -0
- package/src/core/migrations/column-ddl-rendering.ts +117 -0
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +149 -0
- package/src/core/migrations/control-policy.ts +70 -47
- package/src/core/migrations/diff-database-schema.ts +280 -0
- package/src/core/migrations/issue-planner.ts +647 -652
- package/src/core/migrations/op-factory-call.ts +109 -21
- package/src/core/migrations/operations/columns.ts +24 -12
- package/src/core/migrations/operations/constraints.ts +1 -1
- package/src/core/migrations/operations/data-transform.ts +6 -0
- package/src/core/migrations/operations/dependencies.ts +2 -1
- package/src/core/migrations/operations/rls.ts +106 -0
- package/src/core/migrations/operations/shared.ts +0 -30
- package/src/core/migrations/planner-ddl-builders.ts +20 -58
- package/src/core/migrations/planner-identity-values.ts +1 -1
- package/src/core/migrations/planner-recipes.ts +31 -13
- package/src/core/migrations/planner-sql-checks.ts +1 -1
- package/src/core/migrations/planner-strategies.ts +248 -216
- package/src/core/migrations/planner-target-details.ts +3 -1
- package/src/core/migrations/planner.ts +297 -59
- package/src/core/migrations/postgres-migration.ts +34 -4
- package/src/core/migrations/render-typescript.ts +42 -15
- package/src/core/migrations/resolve-ddl-schema.ts +26 -0
- package/src/core/migrations/runner.ts +10 -16
- package/src/core/migrations/verify-postgres-namespaces.ts +47 -30
- package/src/core/namespace-ids.ts +3 -0
- package/src/core/postgres-contract-serializer.ts +41 -71
- package/src/core/postgres-contract-view.ts +48 -0
- package/src/core/postgres-native-enum.ts +51 -0
- package/src/core/postgres-rls-policy.ts +64 -0
- package/src/core/postgres-role.ts +37 -0
- package/src/core/postgres-schema-verifier.ts +10 -7
- package/src/core/postgres-schema.ts +62 -14
- package/src/core/postgres-validators.ts +27 -0
- package/src/core/psl-infer/infer-psl-contract.ts +1068 -0
- package/src/core/psl-infer/postgres-default-mapping.ts +16 -0
- package/src/core/psl-infer/postgres-type-map.ts +157 -0
- package/src/core/rls/canonicalize.ts +48 -0
- package/src/core/schema-ir/postgres-database-schema-node.ts +60 -0
- package/src/core/schema-ir/postgres-namespace-schema-node.ts +60 -0
- package/src/core/schema-ir/postgres-policy-schema-node.ts +90 -0
- package/src/core/schema-ir/postgres-role-schema-node.ts +63 -0
- package/src/core/schema-ir/postgres-table-schema-node.ts +118 -0
- package/src/core/schema-ir/schema-node-kinds.ts +106 -0
- package/src/exports/contract-free.ts +7 -1
- package/src/exports/control.ts +27 -33
- package/src/exports/ddl.ts +4 -0
- package/src/exports/diff-database-schema.ts +7 -0
- package/src/exports/issue-planner.ts +1 -1
- package/src/exports/migration.ts +1 -0
- package/src/exports/op-factory-call.ts +1 -0
- package/src/exports/planner-ddl-builders.ts +0 -2
- package/src/exports/planner-identity-values.ts +4 -1
- package/src/exports/planner.ts +1 -0
- package/src/exports/rls-canonicalize.ts +6 -0
- package/src/exports/runtime.ts +1 -0
- package/src/exports/schema-ir-annotations.ts +1 -0
- package/src/exports/types.ts +28 -0
- package/dist/codec-ids-BvytN2P8.mjs.map +0 -1
- package/dist/codec-ids-CnXu9Qy3.d.mts.map +0 -1
- package/dist/codec-types-DHCkwPKE.d.mts.map +0 -1
- package/dist/codecs--0A5_4Bq.d.mts.map +0 -1
- package/dist/ddl-QDyOSeLc.mjs.map +0 -1
- package/dist/default-normalizer-DyyCHQWs.mjs +0 -131
- package/dist/default-normalizer-DyyCHQWs.mjs.map +0 -1
- package/dist/descriptor-meta-CpGygXpI.mjs +0 -140
- package/dist/descriptor-meta-CpGygXpI.mjs.map +0 -1
- package/dist/issue-planner-DL6g3CmE.mjs +0 -824
- package/dist/issue-planner-DL6g3CmE.mjs.map +0 -1
- package/dist/native-type-normalizer-Bc9XJzWC.mjs +0 -38
- package/dist/native-type-normalizer-Bc9XJzWC.mjs.map +0 -1
- package/dist/nodes-Bbhs2rwj.mjs.map +0 -1
- package/dist/nodes-pLeLgdis.d.mts +0 -67
- package/dist/nodes-pLeLgdis.d.mts.map +0 -1
- package/dist/op-factory-call-D_p5vxwt.mjs.map +0 -1
- package/dist/op-factory-call-DmQEc3XV.d.mts.map +0 -1
- package/dist/planner-Bs_baQax.mjs +0 -324
- package/dist/planner-Bs_baQax.mjs.map +0 -1
- package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +0 -1
- package/dist/planner-target-details-CIY6tLeo.d.mts.map +0 -1
- package/dist/postgres-contract-serializer-k3TAcPMY.mjs.map +0 -1
- package/dist/postgres-migration-B5jKrXv3.mjs.map +0 -1
- package/dist/postgres-migration-Y4YBJqkS.d.mts.map +0 -1
- package/dist/postgres-schema-COGZ1ark.mjs.map +0 -1
- package/dist/render-typescript-KMgosran.mjs.map +0 -1
- package/dist/types.d.mts.map +0 -1
- package/src/core/postgres-enum-type-schema.ts +0 -17
package/dist/control.mjs
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import { t as postgresTargetDescriptorMeta } from "./descriptor-meta-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { t as postgresTargetDescriptorMeta } from "./descriptor-meta-CuGDsrG4.mjs";
|
|
2
|
+
import { n as postgresDiffSubjectEntityKind, r as postgresDiffSubjectGranularity } from "./schema-node-kinds-ClScchhi.mjs";
|
|
3
|
+
import { a as PostgresDatabaseSchemaNode } from "./postgres-table-schema-node-BgaSrxYN.mjs";
|
|
4
|
+
import { n as diffPostgresSchema, r as contractToPostgresDatabaseSchemaNode } from "./diff-database-schema-BmSmWcaY.mjs";
|
|
5
|
+
import { r as renderDefaultLiteral } from "./planner-ddl-builders-B8Nn6nHN.mjs";
|
|
6
|
+
import { n as PostgresContractSerializer } from "./postgres-contract-view-3xfUgjnC.mjs";
|
|
7
|
+
import { t as createPostgresMigrationPlanner } from "./planner-BMBRPzsv.mjs";
|
|
8
|
+
import { UNBOUND_NAMESPACE_ID, coordinateKey, elementCoordinates } from "@prisma-next/framework-components/ir";
|
|
7
9
|
import { blindCast } from "@prisma-next/utils/casts";
|
|
8
|
-
import {
|
|
10
|
+
import { buildNativeTypeExpander, runnerFailure, runnerSuccess } from "@prisma-next/family-sql/control";
|
|
9
11
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
10
|
-
import {
|
|
11
|
-
import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
|
|
12
|
-
import { notOk, ok, okVoid } from "@prisma-next/utils/result";
|
|
12
|
+
import { SqlSchemaIR, SqlTableIR } from "@prisma-next/sql-schema-ir/types";
|
|
13
13
|
import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
|
|
14
|
-
import {
|
|
14
|
+
import { notOk, ok, okVoid } from "@prisma-next/utils/result";
|
|
15
15
|
import { SqlSchemaVerifierBase } from "@prisma-next/family-sql/ir";
|
|
16
|
+
import { SqlQueryError } from "@prisma-next/sql-errors";
|
|
17
|
+
import { buildChildRelationField, deriveRelationFieldName, inferRelations, mapDefault, parseRawDefault, toFieldName, toModelName, toNamedTypeName } from "@prisma-next/family-sql/psl-infer";
|
|
18
|
+
import { UNSPECIFIED_PSL_NAMESPACE_ID, makePslNamespace, makePslNamespaceEntries } from "@prisma-next/framework-components/psl-ast";
|
|
16
19
|
//#region src/core/migrations/runner.ts
|
|
17
20
|
const LOCK_DOMAIN = "prisma_next.contract.marker";
|
|
18
21
|
/**
|
|
@@ -74,19 +77,15 @@ var PostgresMigrationRunner = class {
|
|
|
74
77
|
applyValue = applyResult.value;
|
|
75
78
|
}
|
|
76
79
|
if (space === APP_SPACE_ID) {
|
|
77
|
-
const
|
|
80
|
+
const schemaNode = await this.family.introspect({
|
|
78
81
|
driver,
|
|
79
82
|
contract: options.destinationContract
|
|
80
83
|
});
|
|
81
|
-
const schemaVerifyResult =
|
|
84
|
+
const schemaVerifyResult = this.family.verifySchema({
|
|
82
85
|
contract: options.destinationContract,
|
|
83
|
-
schema:
|
|
86
|
+
schema: schemaNode,
|
|
84
87
|
strict: options.strictVerification ?? true,
|
|
85
|
-
|
|
86
|
-
typeMetadataRegistry: this.family.typeMetadataRegistry,
|
|
87
|
-
frameworkComponents: options.frameworkComponents,
|
|
88
|
-
normalizeDefault: parsePostgresDefault,
|
|
89
|
-
normalizeNativeType: normalizeSchemaNativeType
|
|
88
|
+
frameworkComponents: options.frameworkComponents
|
|
90
89
|
});
|
|
91
90
|
if (!schemaVerifyResult.ok) return runnerFailure("SCHEMA_VERIFY_FAILED", schemaVerifyResult.summary, {
|
|
92
91
|
why: "The resulting database schema does not satisfy the destination contract.",
|
|
@@ -387,10 +386,10 @@ var PostgresMigrationRunner = class {
|
|
|
387
386
|
/**
|
|
388
387
|
* Postgres target `SchemaVerifier` concretion. Plugs into the
|
|
389
388
|
* SQL-shared verification surface; production verification today still
|
|
390
|
-
* routes through the
|
|
391
|
-
* carries options (codec hooks,
|
|
392
|
-
* that the framework-level
|
|
393
|
-
* surface.
|
|
389
|
+
* routes through the family verify verdict (`verifySqlSchemaByDiff` over
|
|
390
|
+
* `diffPostgresSchema`), which carries options (codec hooks,
|
|
391
|
+
* normalizers, framework components) that the framework-level
|
|
392
|
+
* `SchemaVerifyOptions` shape does not yet surface.
|
|
394
393
|
*
|
|
395
394
|
* The hooks return the empty list pending the call-site migration that
|
|
396
395
|
* routes the existing verifier behaviour through the SPI — at that
|
|
@@ -408,18 +407,731 @@ var PostgresSchemaVerifier = class extends SqlSchemaVerifierBase {
|
|
|
408
407
|
}
|
|
409
408
|
};
|
|
410
409
|
//#endregion
|
|
411
|
-
//#region src/
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return hooks.expandNativeType(input);
|
|
410
|
+
//#region src/core/psl-infer/postgres-default-mapping.ts
|
|
411
|
+
const POSTGRES_FUNCTION_ATTRIBUTES = { "gen_random_uuid()": "@default(dbgenerated(\"gen_random_uuid()\"))" };
|
|
412
|
+
function formatDbGeneratedAttribute(expression) {
|
|
413
|
+
return `@default(dbgenerated(${JSON.stringify(expression)}))`;
|
|
414
|
+
}
|
|
415
|
+
function createPostgresDefaultMapping() {
|
|
416
|
+
return {
|
|
417
|
+
functionAttributes: POSTGRES_FUNCTION_ATTRIBUTES,
|
|
418
|
+
fallbackFunctionAttribute: formatDbGeneratedAttribute
|
|
421
419
|
};
|
|
422
420
|
}
|
|
421
|
+
//#endregion
|
|
422
|
+
//#region src/core/psl-infer/postgres-type-map.ts
|
|
423
|
+
const POSTGRES_TO_PSL = {
|
|
424
|
+
text: "String",
|
|
425
|
+
bool: "Boolean",
|
|
426
|
+
boolean: "Boolean",
|
|
427
|
+
int4: "Int",
|
|
428
|
+
integer: "Int",
|
|
429
|
+
int8: "BigInt",
|
|
430
|
+
bigint: "BigInt",
|
|
431
|
+
float8: "Float",
|
|
432
|
+
"double precision": "Float",
|
|
433
|
+
numeric: "Decimal",
|
|
434
|
+
decimal: "Decimal",
|
|
435
|
+
timestamptz: "DateTime",
|
|
436
|
+
"timestamp with time zone": "DateTime",
|
|
437
|
+
jsonb: "Json",
|
|
438
|
+
bytea: "Bytes"
|
|
439
|
+
};
|
|
440
|
+
const PRESERVED_NATIVE_TYPES = {
|
|
441
|
+
"character varying": {
|
|
442
|
+
pslType: "String",
|
|
443
|
+
attributeName: "db.VarChar"
|
|
444
|
+
},
|
|
445
|
+
character: {
|
|
446
|
+
pslType: "String",
|
|
447
|
+
attributeName: "db.Char"
|
|
448
|
+
},
|
|
449
|
+
char: {
|
|
450
|
+
pslType: "String",
|
|
451
|
+
attributeName: "db.Char"
|
|
452
|
+
},
|
|
453
|
+
varchar: {
|
|
454
|
+
pslType: "String",
|
|
455
|
+
attributeName: "db.VarChar"
|
|
456
|
+
},
|
|
457
|
+
uuid: {
|
|
458
|
+
pslType: "String",
|
|
459
|
+
attributeName: "db.Uuid"
|
|
460
|
+
},
|
|
461
|
+
int2: {
|
|
462
|
+
pslType: "Int",
|
|
463
|
+
attributeName: "db.SmallInt"
|
|
464
|
+
},
|
|
465
|
+
smallint: {
|
|
466
|
+
pslType: "Int",
|
|
467
|
+
attributeName: "db.SmallInt"
|
|
468
|
+
},
|
|
469
|
+
float4: {
|
|
470
|
+
pslType: "Float",
|
|
471
|
+
attributeName: "db.Real"
|
|
472
|
+
},
|
|
473
|
+
real: {
|
|
474
|
+
pslType: "Float",
|
|
475
|
+
attributeName: "db.Real"
|
|
476
|
+
},
|
|
477
|
+
timestamp: {
|
|
478
|
+
pslType: "DateTime",
|
|
479
|
+
attributeName: "db.Timestamp"
|
|
480
|
+
},
|
|
481
|
+
"timestamp without time zone": {
|
|
482
|
+
pslType: "DateTime",
|
|
483
|
+
attributeName: "db.Timestamp"
|
|
484
|
+
},
|
|
485
|
+
date: {
|
|
486
|
+
pslType: "DateTime",
|
|
487
|
+
attributeName: "db.Date"
|
|
488
|
+
},
|
|
489
|
+
time: {
|
|
490
|
+
pslType: "DateTime",
|
|
491
|
+
attributeName: "db.Time"
|
|
492
|
+
},
|
|
493
|
+
"time without time zone": {
|
|
494
|
+
pslType: "DateTime",
|
|
495
|
+
attributeName: "db.Time"
|
|
496
|
+
},
|
|
497
|
+
timetz: {
|
|
498
|
+
pslType: "DateTime",
|
|
499
|
+
attributeName: "db.Timetz"
|
|
500
|
+
},
|
|
501
|
+
"time with time zone": {
|
|
502
|
+
pslType: "DateTime",
|
|
503
|
+
attributeName: "db.Timetz"
|
|
504
|
+
},
|
|
505
|
+
json: {
|
|
506
|
+
pslType: "Json",
|
|
507
|
+
attributeName: "db.Json"
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
const PARAMETERIZED_NATIVE_TYPES = {
|
|
511
|
+
"character varying": {
|
|
512
|
+
pslType: "String",
|
|
513
|
+
attributeName: "db.VarChar"
|
|
514
|
+
},
|
|
515
|
+
character: {
|
|
516
|
+
pslType: "String",
|
|
517
|
+
attributeName: "db.Char"
|
|
518
|
+
},
|
|
519
|
+
char: {
|
|
520
|
+
pslType: "String",
|
|
521
|
+
attributeName: "db.Char"
|
|
522
|
+
},
|
|
523
|
+
varchar: {
|
|
524
|
+
pslType: "String",
|
|
525
|
+
attributeName: "db.VarChar"
|
|
526
|
+
},
|
|
527
|
+
numeric: {
|
|
528
|
+
pslType: "Decimal",
|
|
529
|
+
attributeName: "db.Numeric"
|
|
530
|
+
},
|
|
531
|
+
timestamp: {
|
|
532
|
+
pslType: "DateTime",
|
|
533
|
+
attributeName: "db.Timestamp"
|
|
534
|
+
},
|
|
535
|
+
timestamptz: {
|
|
536
|
+
pslType: "DateTime",
|
|
537
|
+
attributeName: "db.Timestamptz"
|
|
538
|
+
},
|
|
539
|
+
time: {
|
|
540
|
+
pslType: "DateTime",
|
|
541
|
+
attributeName: "db.Time"
|
|
542
|
+
},
|
|
543
|
+
timetz: {
|
|
544
|
+
pslType: "DateTime",
|
|
545
|
+
attributeName: "db.Timetz"
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
const PARAMETERIZED_TYPE_PATTERN = /^(.+?)\((.+)\)$/;
|
|
549
|
+
function getOwnMappingValue(map, key) {
|
|
550
|
+
return Object.hasOwn(map, key) ? map[key] : void 0;
|
|
551
|
+
}
|
|
552
|
+
function getOwnRecordValue(map, key) {
|
|
553
|
+
return Object.hasOwn(map, key) ? map[key] : void 0;
|
|
554
|
+
}
|
|
555
|
+
function createNativeTypeAttribute(name, args) {
|
|
556
|
+
return args && args.length > 0 ? {
|
|
557
|
+
name,
|
|
558
|
+
args
|
|
559
|
+
} : { name };
|
|
560
|
+
}
|
|
561
|
+
function splitTypeParameterList(params) {
|
|
562
|
+
return params.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
563
|
+
}
|
|
564
|
+
function createPostgresTypeMap(enumTypeNames) {
|
|
565
|
+
return { resolve(nativeType) {
|
|
566
|
+
if (enumTypeNames?.has(nativeType)) return {
|
|
567
|
+
pslType: nativeType,
|
|
568
|
+
nativeType
|
|
569
|
+
};
|
|
570
|
+
const paramMatch = nativeType.match(PARAMETERIZED_TYPE_PATTERN);
|
|
571
|
+
if (paramMatch) {
|
|
572
|
+
const [, baseType = nativeType, params = ""] = paramMatch;
|
|
573
|
+
const template = getOwnRecordValue(PARAMETERIZED_NATIVE_TYPES, baseType);
|
|
574
|
+
if (template) return {
|
|
575
|
+
pslType: template.pslType,
|
|
576
|
+
nativeType,
|
|
577
|
+
typeParams: {
|
|
578
|
+
baseType,
|
|
579
|
+
params
|
|
580
|
+
},
|
|
581
|
+
nativeTypeAttribute: createNativeTypeAttribute(template.attributeName, splitTypeParameterList(params))
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
const preservedType = getOwnRecordValue(PRESERVED_NATIVE_TYPES, nativeType);
|
|
585
|
+
if (preservedType) return {
|
|
586
|
+
pslType: preservedType.pslType,
|
|
587
|
+
nativeType,
|
|
588
|
+
nativeTypeAttribute: createNativeTypeAttribute(preservedType.attributeName)
|
|
589
|
+
};
|
|
590
|
+
const pslType = getOwnMappingValue(POSTGRES_TO_PSL, nativeType);
|
|
591
|
+
if (pslType) return {
|
|
592
|
+
pslType,
|
|
593
|
+
nativeType
|
|
594
|
+
};
|
|
595
|
+
return {
|
|
596
|
+
unsupported: true,
|
|
597
|
+
nativeType
|
|
598
|
+
};
|
|
599
|
+
} };
|
|
600
|
+
}
|
|
601
|
+
//#endregion
|
|
602
|
+
//#region src/core/psl-infer/infer-psl-contract.ts
|
|
603
|
+
const SYNTHETIC_SPAN = {
|
|
604
|
+
start: {
|
|
605
|
+
offset: 0,
|
|
606
|
+
line: 1,
|
|
607
|
+
column: 1
|
|
608
|
+
},
|
|
609
|
+
end: {
|
|
610
|
+
offset: 0,
|
|
611
|
+
line: 1,
|
|
612
|
+
column: 1
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
const PSL_SCALAR_TYPE_NAMES = new Set([
|
|
616
|
+
"String",
|
|
617
|
+
"Boolean",
|
|
618
|
+
"Int",
|
|
619
|
+
"BigInt",
|
|
620
|
+
"Float",
|
|
621
|
+
"Decimal",
|
|
622
|
+
"DateTime",
|
|
623
|
+
"Json",
|
|
624
|
+
"Bytes"
|
|
625
|
+
]);
|
|
626
|
+
/**
|
|
627
|
+
* Coordinates every element a set of already-assembled contracts declare,
|
|
628
|
+
* mapped to the {@link SqlDescribedContractSpace} that owns it and keyed by
|
|
629
|
+
* the shared {@link coordinateKey} helper. `contract infer` uses this to omit
|
|
630
|
+
* database elements a stack extension pack's contract space already
|
|
631
|
+
* describes — reusing the same coordinate walk the contract-space aggregate
|
|
632
|
+
* and cross-space collision check use (`elementCoordinates`), rather than a
|
|
633
|
+
* bespoke per-entity-kind membership test — and, for a foreign key whose
|
|
634
|
+
* referenced table an entry owns, to resolve the qualified cross-space
|
|
635
|
+
* relation it describes.
|
|
636
|
+
*/
|
|
637
|
+
function describedContractOwners(describedContracts) {
|
|
638
|
+
const owners = /* @__PURE__ */ new Map();
|
|
639
|
+
for (const entry of describedContracts) for (const coordinate of elementCoordinates(entry.contract.storage)) owners.set(coordinateKey(coordinate), entry);
|
|
640
|
+
return owners;
|
|
641
|
+
}
|
|
642
|
+
function resolveCrossSpaceTarget(owner, namespaceId, tableName) {
|
|
643
|
+
const domainNamespace = owner.contract.domain.namespaces[namespaceId];
|
|
644
|
+
if (domainNamespace === void 0) return;
|
|
645
|
+
for (const [modelName, model] of Object.entries(domainNamespace.models)) {
|
|
646
|
+
const storage = blindCast(model.storage);
|
|
647
|
+
if (storage.namespaceId !== namespaceId || storage.table !== tableName) continue;
|
|
648
|
+
const fieldNamesByColumn = /* @__PURE__ */ new Map();
|
|
649
|
+
for (const [fieldName, fieldStorage] of Object.entries(storage.fields)) fieldNamesByColumn.set(fieldStorage.column, { fieldName });
|
|
650
|
+
return {
|
|
651
|
+
spaceId: owner.spaceId,
|
|
652
|
+
namespaceId,
|
|
653
|
+
modelName,
|
|
654
|
+
fieldNamesByColumn
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Classifies every foreign key on a surviving table into one of three cases.
|
|
660
|
+
* A foreign key that carries a `referencedSchema` is checked against the
|
|
661
|
+
* pack-owned coordinates first, so a pack-owned target wins even when a local
|
|
662
|
+
* table happens to share its bare name; only foreign keys with no owned
|
|
663
|
+
* coordinate fall through to the local/dangling distinction.
|
|
664
|
+
*
|
|
665
|
+
* - **Cross-space**: `referencedSchema` is set and a described contract owns
|
|
666
|
+
* the coordinate `(referencedSchema, 'table', referencedTable)`. The
|
|
667
|
+
* referenced table is absent from the tree (omitted because the pack
|
|
668
|
+
* describes it, or never introspected — `contract infer` walks a single
|
|
669
|
+
* namespace). Removed from `foreignKeys` (so `inferRelations` never falls
|
|
670
|
+
* back to a bare, unqualified table name for it) and replaced with a
|
|
671
|
+
* `RelationField` qualified with the owning pack's space id and namespace
|
|
672
|
+
* id. `owners` holds only pack-declared coordinates, so an app's own table
|
|
673
|
+
* (e.g. `public.users`) is never owned and cannot be captured here.
|
|
674
|
+
* - **Local**: not a pack-owned coordinate, and the referenced table survived
|
|
675
|
+
* introspection — left untouched, `inferRelations` handles it as before.
|
|
676
|
+
* - **Dangling**: not a pack-owned coordinate, and the referenced table is
|
|
677
|
+
* neither in the tree nor owned by any described contract. Removed from
|
|
678
|
+
* `foreignKeys`, keeping the scalar column, rather than emitting a relation
|
|
679
|
+
* to a model that was never defined.
|
|
680
|
+
*
|
|
681
|
+
* A pack that owns the referenced coordinate but declares no domain model
|
|
682
|
+
* mapped to it is malformed; that case throws rather than degrading to a
|
|
683
|
+
* silent drop, which would contradict the dangling definition above.
|
|
684
|
+
*/
|
|
685
|
+
function resolveForeignKeys(tables, owners) {
|
|
686
|
+
const resultTables = {};
|
|
687
|
+
const extraRelationsByTable = /* @__PURE__ */ new Map();
|
|
688
|
+
const crossSpaceFieldNamesByTable = /* @__PURE__ */ new Map();
|
|
689
|
+
for (const [tableName, table] of Object.entries(tables)) {
|
|
690
|
+
const keptForeignKeys = [];
|
|
691
|
+
for (const fk of table.foreignKeys) {
|
|
692
|
+
if (fk.referencedSchema !== void 0) {
|
|
693
|
+
const owner = owners.get(coordinateKey({
|
|
694
|
+
namespaceId: fk.referencedSchema,
|
|
695
|
+
entityKind: "table",
|
|
696
|
+
entityName: fk.referencedTable
|
|
697
|
+
}));
|
|
698
|
+
if (owner !== void 0) {
|
|
699
|
+
const target = resolveCrossSpaceTarget(owner, fk.referencedSchema, fk.referencedTable);
|
|
700
|
+
if (target === void 0) throw new Error(`contract infer: described contract space "${owner.spaceId}" owns storage coordinate "${fk.referencedSchema}.${fk.referencedTable}" but declares no domain model mapped to it. A pack that describes a table must also declare the domain model it maps to; this pack is malformed.`);
|
|
701
|
+
if (!crossSpaceFieldNamesByTable.has(fk.referencedTable)) crossSpaceFieldNamesByTable.set(fk.referencedTable, target.fieldNamesByColumn);
|
|
702
|
+
const fieldName = deriveRelationFieldName(fk.columns, fk.referencedTable);
|
|
703
|
+
const optional = fk.columns.some((columnName) => table.columns[columnName]?.nullable ?? false);
|
|
704
|
+
const relationField = {
|
|
705
|
+
...buildChildRelationField(fieldName, target.modelName, fk, optional),
|
|
706
|
+
typeNamespaceId: target.namespaceId,
|
|
707
|
+
typeContractSpaceId: target.spaceId
|
|
708
|
+
};
|
|
709
|
+
const existingRelations = extraRelationsByTable.get(tableName);
|
|
710
|
+
if (existingRelations) existingRelations.push(relationField);
|
|
711
|
+
else extraRelationsByTable.set(tableName, [relationField]);
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (tables[fk.referencedTable] !== void 0) keptForeignKeys.push(fk);
|
|
716
|
+
}
|
|
717
|
+
resultTables[tableName] = keptForeignKeys.length === table.foreignKeys.length ? table : new SqlTableIR({
|
|
718
|
+
...table,
|
|
719
|
+
foreignKeys: keptForeignKeys
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
return {
|
|
723
|
+
tables: resultTables,
|
|
724
|
+
extraRelationsByTable,
|
|
725
|
+
crossSpaceFieldNamesByTable
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Infers a PSL AST (for `printPsl`) from an introspected Postgres schema tree.
|
|
730
|
+
*
|
|
731
|
+
* Target-owned inference: it walks the `PostgresDatabaseSchemaNode` tree and
|
|
732
|
+
* owns the Postgres dialect knowledge — the native type map and default map.
|
|
733
|
+
* Relation inference, name transforms, generic default mapping, and raw-default
|
|
734
|
+
* parsing are shape-neutral utilities imported from the SQL family.
|
|
735
|
+
*
|
|
736
|
+
* This slice emits relational-only PSL, byte-identical to the prior flat
|
|
737
|
+
* inference: the tree's tables (across its namespaces — `contract infer`
|
|
738
|
+
* introspects a single live namespace) are gathered into the model set and
|
|
739
|
+
* emitted as one `UNSPECIFIED_PSL_NAMESPACE_ID` bucket. Top-level entities
|
|
740
|
+
* (policies/roles → PSL extension blocks) are a later slice.
|
|
741
|
+
*
|
|
742
|
+
* `describedContracts` — the stack's extension packs' already-assembled
|
|
743
|
+
* contracts, each paired with its space id — is consulted while gathering
|
|
744
|
+
* tables: a table whose coordinate `(schemaName, 'table', tableName)` one of
|
|
745
|
+
* those contracts already declares is omitted, before the duplicate-name
|
|
746
|
+
* check below and before relation inference, so it cannot spuriously
|
|
747
|
+
* collide with an app table and never contributes a bare relation field. A
|
|
748
|
+
* surviving table's foreign key into an omitted, pack-owned table is not
|
|
749
|
+
* dropped: {@link resolveForeignKeys} rewrites it into a relation qualified
|
|
750
|
+
* with the pack's space id (`<spaceId>:<namespaceId>.<Model>`) instead.
|
|
751
|
+
*/
|
|
752
|
+
function inferPostgresPslContract(tree, describedContracts) {
|
|
753
|
+
const namespaces = Object.values(tree.namespaces);
|
|
754
|
+
const owners = describedContractOwners(describedContracts ?? []);
|
|
755
|
+
const enumTypeNames = [...new Set(namespaces.flatMap((ns) => ns.nativeEnumTypeNames))];
|
|
756
|
+
if (enumTypeNames.length > 0) {
|
|
757
|
+
const names = enumTypeNames.join(", ");
|
|
758
|
+
throw new Error(`contract infer: the database contains native Postgres enum type(s): ${names}. Native Postgres enums (CREATE TYPE … AS ENUM) are not adoptable by contract infer. Drop the native type and replace each column with a text column carrying a CHECK constraint, then re-run contract infer. The domain enum (enum Name { @@type("pg/text@1") … }) authoring surface generates the required check automatically.`);
|
|
759
|
+
}
|
|
760
|
+
const tables = {};
|
|
761
|
+
for (const namespace of namespaces) for (const [tableName, table] of Object.entries(namespace.tables)) {
|
|
762
|
+
if (owners.has(coordinateKey({
|
|
763
|
+
namespaceId: namespace.schemaName,
|
|
764
|
+
entityKind: "table",
|
|
765
|
+
entityName: tableName
|
|
766
|
+
}))) continue;
|
|
767
|
+
if (tables[tableName] !== void 0) throw new Error(`contract infer: duplicate table name "${tableName}" across schemas is not yet supported (single-namespace PSL inference emits one flat bucket; multi-namespace \`namespace { … }\` output is a later slice).`);
|
|
768
|
+
tables[tableName] = new SqlTableIR(table);
|
|
769
|
+
}
|
|
770
|
+
const { tables: resolvedTables, extraRelationsByTable, crossSpaceFieldNamesByTable } = resolveForeignKeys(tables, owners);
|
|
771
|
+
return buildPslDocumentAst(new SqlSchemaIR({ tables: resolvedTables }), {
|
|
772
|
+
typeMap: createPostgresTypeMap(/* @__PURE__ */ new Set()),
|
|
773
|
+
defaultMapping: createPostgresDefaultMapping(),
|
|
774
|
+
parseRawDefault
|
|
775
|
+
}, {
|
|
776
|
+
extraRelationsByTable,
|
|
777
|
+
crossSpaceFieldNamesByTable
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
function buildPslDocumentAst(schemaIR, options, foreignKeyExtras) {
|
|
781
|
+
const { typeMap, defaultMapping, parseRawDefault: rawDefaultParser } = options;
|
|
782
|
+
const { extraRelationsByTable, crossSpaceFieldNamesByTable } = foreignKeyExtras;
|
|
783
|
+
const modelNames = buildTopLevelNameMap(Object.keys(schemaIR.tables), toModelName, "model", "table");
|
|
784
|
+
const modelNameMap = new Map([...modelNames].map(([tableName, result]) => [tableName, result.name]));
|
|
785
|
+
const reservedNamedTypeNames = createReservedNamedTypeNames(modelNames);
|
|
786
|
+
const fieldNamesByTable = new Map([...crossSpaceFieldNamesByTable, ...buildFieldNamesByTable(schemaIR.tables)]);
|
|
787
|
+
const { relationsByTable } = inferRelations(schemaIR.tables, modelNameMap);
|
|
788
|
+
const namedTypes = seedNamedTypeRegistry(schemaIR, typeMap, /* @__PURE__ */ new Map(), reservedNamedTypeNames);
|
|
789
|
+
const models = [];
|
|
790
|
+
for (const table of Object.values(schemaIR.tables)) models.push(buildModel(table, typeMap, /* @__PURE__ */ new Map(), fieldNamesByTable, namedTypes, defaultMapping, rawDefaultParser, [...relationsByTable.get(table.name) ?? [], ...extraRelationsByTable.get(table.name) ?? []]));
|
|
791
|
+
const sortedModels = topologicalSort(models, schemaIR.tables, modelNameMap);
|
|
792
|
+
const namedTypeEntries = [...namedTypes.entriesByKey.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
793
|
+
const types = namedTypeEntries.length > 0 ? {
|
|
794
|
+
kind: "types",
|
|
795
|
+
declarations: namedTypeEntries.map(buildNamedTypeDeclaration),
|
|
796
|
+
span: SYNTHETIC_SPAN
|
|
797
|
+
} : void 0;
|
|
798
|
+
return {
|
|
799
|
+
kind: "document",
|
|
800
|
+
sourceId: "<sql-schema-ir>",
|
|
801
|
+
namespaces: [makePslNamespace({
|
|
802
|
+
kind: "namespace",
|
|
803
|
+
name: UNSPECIFIED_PSL_NAMESPACE_ID,
|
|
804
|
+
entries: makePslNamespaceEntries(sortedModels, [], []),
|
|
805
|
+
span: SYNTHETIC_SPAN
|
|
806
|
+
})],
|
|
807
|
+
...types ? { types } : {},
|
|
808
|
+
span: SYNTHETIC_SPAN
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
function buildModel(table, typeMap, enumNameMap, fieldNamesByTable, namedTypes, defaultMapping, rawDefaultParser, relationFields) {
|
|
812
|
+
const { name: modelName, map: mapName } = toModelName(table.name);
|
|
813
|
+
const fieldNameMap = fieldNamesByTable.get(table.name);
|
|
814
|
+
const pkColumns = new Set(table.primaryKey?.columns ?? []);
|
|
815
|
+
const isSinglePk = pkColumns.size === 1;
|
|
816
|
+
const singlePkConstraintName = isSinglePk ? table.primaryKey?.name : void 0;
|
|
817
|
+
const uniqueColumns = /* @__PURE__ */ new Map();
|
|
818
|
+
for (const unique of table.uniques) if (unique.columns.length === 1) {
|
|
819
|
+
const [columnName = ""] = unique.columns;
|
|
820
|
+
const existingConstraintName = uniqueColumns.get(columnName);
|
|
821
|
+
if (!uniqueColumns.has(columnName) || existingConstraintName === void 0 && unique.name) uniqueColumns.set(columnName, unique.name);
|
|
822
|
+
}
|
|
823
|
+
const fields = [];
|
|
824
|
+
for (const column of Object.values(table.columns)) fields.push(buildScalarField(column, table, typeMap, enumNameMap, fieldNameMap, namedTypes, defaultMapping, rawDefaultParser, pkColumns, isSinglePk, singlePkConstraintName, uniqueColumns));
|
|
825
|
+
const usedFieldNames = new Set(fields.map((field) => field.name));
|
|
826
|
+
for (const rel of relationFields) fields.push(buildRelationField(rel, table.name, fieldNamesByTable, usedFieldNames));
|
|
827
|
+
const modelAttributes = [];
|
|
828
|
+
if (table.primaryKey && table.primaryKey.columns.length > 1) {
|
|
829
|
+
const pkFieldNames = table.primaryKey.columns.map((columnName) => resolveColumnFieldName(fieldNamesByTable, table.name, columnName));
|
|
830
|
+
modelAttributes.push(buildModelConstraintAttribute("id", pkFieldNames, table.primaryKey.name));
|
|
831
|
+
}
|
|
832
|
+
for (const unique of table.uniques) if (unique.columns.length > 1) {
|
|
833
|
+
const uniqueFieldNames = unique.columns.map((columnName) => resolveColumnFieldName(fieldNamesByTable, table.name, columnName));
|
|
834
|
+
modelAttributes.push(buildModelConstraintAttribute("unique", uniqueFieldNames, unique.name));
|
|
835
|
+
}
|
|
836
|
+
for (const index of table.indexes) if (!index.unique) {
|
|
837
|
+
const indexFieldNames = index.columns.map((columnName) => resolveColumnFieldName(fieldNamesByTable, table.name, columnName));
|
|
838
|
+
modelAttributes.push(buildModelConstraintAttribute("index", indexFieldNames, index.name));
|
|
839
|
+
}
|
|
840
|
+
if (mapName) modelAttributes.push(buildMapAttribute("model", mapName));
|
|
841
|
+
const comment = table.primaryKey ? void 0 : "// WARNING: This table has no primary key in the database";
|
|
842
|
+
return {
|
|
843
|
+
kind: "model",
|
|
844
|
+
name: modelName,
|
|
845
|
+
fields,
|
|
846
|
+
attributes: modelAttributes,
|
|
847
|
+
span: SYNTHETIC_SPAN,
|
|
848
|
+
...comment !== void 0 ? { comment } : {}
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
function buildScalarField(column, table, typeMap, enumNameMap, fieldNameMap, namedTypes, defaultMapping, rawDefaultParser, pkColumns, isSinglePk, singlePkConstraintName, uniqueColumns) {
|
|
852
|
+
const resolvedField = fieldNameMap?.get(column.name);
|
|
853
|
+
const fieldName = resolvedField?.fieldName ?? toFieldName(column.name).name;
|
|
854
|
+
const fieldMap = resolvedField?.fieldMap;
|
|
855
|
+
const resolution = typeMap.resolve(column.nativeType, table.annotations);
|
|
856
|
+
if ("unsupported" in resolution) {
|
|
857
|
+
const attrs = [];
|
|
858
|
+
if (fieldMap !== void 0) attrs.push(buildMapAttribute("field", fieldMap));
|
|
859
|
+
return {
|
|
860
|
+
kind: "field",
|
|
861
|
+
name: fieldName,
|
|
862
|
+
typeName: `Unsupported("${escapePslString(resolution.nativeType)}")`,
|
|
863
|
+
optional: column.nullable,
|
|
864
|
+
list: column.many === true,
|
|
865
|
+
attributes: attrs,
|
|
866
|
+
span: SYNTHETIC_SPAN
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
let typeName = resolution.pslType;
|
|
870
|
+
const enumPslName = enumNameMap.get(column.nativeType);
|
|
871
|
+
if (enumPslName) typeName = enumPslName;
|
|
872
|
+
if (resolution.nativeTypeAttribute && !enumPslName) typeName = resolveNamedTypeName(namedTypes, resolution);
|
|
873
|
+
const attributes = [];
|
|
874
|
+
const isId = isSinglePk && pkColumns.has(column.name);
|
|
875
|
+
if (isId) attributes.push(buildSimpleConstraintFieldAttribute("id", singlePkConstraintName));
|
|
876
|
+
if (column.default !== void 0) {
|
|
877
|
+
const parsed = parseColumnDefault(column.default, column.nativeType, rawDefaultParser);
|
|
878
|
+
if (parsed) {
|
|
879
|
+
const result = mapDefault(parsed, defaultMapping);
|
|
880
|
+
if ("attribute" in result) attributes.push(parseDefaultAttributeString(result.attribute));
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
if (uniqueColumns.has(column.name) && !isId) {
|
|
884
|
+
const uniqueConstraintName = uniqueColumns.get(column.name);
|
|
885
|
+
attributes.push(buildSimpleConstraintFieldAttribute("unique", uniqueConstraintName));
|
|
886
|
+
}
|
|
887
|
+
if (fieldMap !== void 0) attributes.push(buildMapAttribute("field", fieldMap));
|
|
888
|
+
return {
|
|
889
|
+
kind: "field",
|
|
890
|
+
name: fieldName,
|
|
891
|
+
typeName,
|
|
892
|
+
optional: column.nullable,
|
|
893
|
+
list: column.many === true,
|
|
894
|
+
attributes,
|
|
895
|
+
span: SYNTHETIC_SPAN
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function buildRelationField(rel, hostTableName, fieldNamesByTable, usedFieldNames) {
|
|
899
|
+
const fieldName = createUniqueFieldName(rel.fieldName, usedFieldNames);
|
|
900
|
+
usedFieldNames.add(fieldName);
|
|
901
|
+
const args = [];
|
|
902
|
+
if (rel.fields && rel.references) {
|
|
903
|
+
if (rel.relationName) args.push(namedArg("name", `"${escapePslString(rel.relationName)}"`));
|
|
904
|
+
args.push(namedArg("fields", `[${rel.fields.map((columnName) => resolveColumnFieldName(fieldNamesByTable, hostTableName, columnName)).join(", ")}]`));
|
|
905
|
+
args.push(namedArg("references", `[${rel.references.map((columnName) => resolveColumnFieldName(fieldNamesByTable, rel.referencedTableName ?? "", columnName)).join(", ")}]`));
|
|
906
|
+
if (rel.onDelete) args.push(namedArg("onDelete", rel.onDelete));
|
|
907
|
+
if (rel.onUpdate) args.push(namedArg("onUpdate", rel.onUpdate));
|
|
908
|
+
if (rel.fkName) args.push(namedArg("map", `"${escapePslString(rel.fkName)}"`));
|
|
909
|
+
} else if (rel.relationName) args.push(namedArg("name", `"${escapePslString(rel.relationName)}"`));
|
|
910
|
+
const attrs = args.length > 0 ? [buildAttribute("field", "relation", args)] : [];
|
|
911
|
+
return {
|
|
912
|
+
kind: "field",
|
|
913
|
+
name: fieldName,
|
|
914
|
+
typeName: rel.typeName,
|
|
915
|
+
...ifDefined("typeNamespaceId", rel.typeNamespaceId),
|
|
916
|
+
...ifDefined("typeContractSpaceId", rel.typeContractSpaceId),
|
|
917
|
+
optional: rel.optional,
|
|
918
|
+
list: rel.list,
|
|
919
|
+
attributes: attrs,
|
|
920
|
+
span: SYNTHETIC_SPAN
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
function buildModelConstraintAttribute(name, fields, constraintName) {
|
|
924
|
+
const args = [positionalArg(`[${fields.join(", ")}]`)];
|
|
925
|
+
if (constraintName !== void 0) args.push(namedArg("map", `"${escapePslString(constraintName)}"`));
|
|
926
|
+
return buildAttribute("model", name, args);
|
|
927
|
+
}
|
|
928
|
+
function buildSimpleConstraintFieldAttribute(name, constraintName) {
|
|
929
|
+
if (constraintName === void 0) return buildAttribute("field", name, []);
|
|
930
|
+
return buildAttribute("field", name, [namedArg("map", `"${escapePslString(constraintName)}"`)]);
|
|
931
|
+
}
|
|
932
|
+
function parseDefaultAttributeString(attributeText) {
|
|
933
|
+
return buildAttribute("field", "default", [positionalArg(attributeText.replace(/^@default\(/, "").replace(/\)$/, ""))]);
|
|
934
|
+
}
|
|
935
|
+
function buildMapAttribute(target, mapName) {
|
|
936
|
+
return buildAttribute(target, "map", [positionalArg(`"${escapePslString(mapName)}"`)]);
|
|
937
|
+
}
|
|
938
|
+
function buildAttribute(target, name, args) {
|
|
939
|
+
return {
|
|
940
|
+
kind: "attribute",
|
|
941
|
+
target,
|
|
942
|
+
name,
|
|
943
|
+
args,
|
|
944
|
+
span: SYNTHETIC_SPAN
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function positionalArg(value) {
|
|
948
|
+
return {
|
|
949
|
+
kind: "positional",
|
|
950
|
+
value,
|
|
951
|
+
span: SYNTHETIC_SPAN
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
function namedArg(name, value) {
|
|
955
|
+
return {
|
|
956
|
+
kind: "named",
|
|
957
|
+
name,
|
|
958
|
+
value,
|
|
959
|
+
span: SYNTHETIC_SPAN
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
function buildNamedTypeDeclaration(entry) {
|
|
963
|
+
const attribute = buildAttribute("namedType", entry.nativeTypeAttribute.name, (entry.nativeTypeAttribute.args ?? []).map(positionalArg));
|
|
964
|
+
return {
|
|
965
|
+
kind: "namedType",
|
|
966
|
+
name: entry.name,
|
|
967
|
+
baseType: entry.baseType,
|
|
968
|
+
attributes: [attribute],
|
|
969
|
+
span: SYNTHETIC_SPAN
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function escapePslString(value) {
|
|
973
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Resolves a `SqlColumnIR.default` value into a normalized {@link ColumnDefault}.
|
|
977
|
+
*
|
|
978
|
+
* `SqlSchemaIR` types the column default as `string` (a raw database default
|
|
979
|
+
* expression). Some legacy fixtures and tests still pass already-normalized
|
|
980
|
+
* `ColumnDefault` objects in the same slot, so we accept either shape
|
|
981
|
+
* defensively at runtime.
|
|
982
|
+
*/
|
|
983
|
+
function parseColumnDefault(value, nativeType, rawDefaultParser) {
|
|
984
|
+
if (typeof value === "string") return rawDefaultParser ? rawDefaultParser(value, nativeType) : void 0;
|
|
985
|
+
if (value !== null && typeof value === "object" && "kind" in value) return value;
|
|
986
|
+
}
|
|
987
|
+
function buildFieldNamesByTable(tables) {
|
|
988
|
+
const fieldNamesByTable = /* @__PURE__ */ new Map();
|
|
989
|
+
for (const table of Object.values(tables)) {
|
|
990
|
+
const assignmentOrder = [...Object.values(table.columns).map((column, index) => {
|
|
991
|
+
const { name, map } = toFieldName(column.name);
|
|
992
|
+
return {
|
|
993
|
+
columnName: column.name,
|
|
994
|
+
desiredFieldName: name,
|
|
995
|
+
fieldMap: map,
|
|
996
|
+
index
|
|
997
|
+
};
|
|
998
|
+
})].sort((left, right) => {
|
|
999
|
+
const mapComparison = Number(left.fieldMap !== void 0) - Number(right.fieldMap !== void 0);
|
|
1000
|
+
if (mapComparison !== 0) return mapComparison;
|
|
1001
|
+
return left.index - right.index;
|
|
1002
|
+
});
|
|
1003
|
+
const usedFieldNames = /* @__PURE__ */ new Set();
|
|
1004
|
+
const tableFieldNames = /* @__PURE__ */ new Map();
|
|
1005
|
+
for (const column of assignmentOrder) {
|
|
1006
|
+
const fieldName = createUniqueFieldName(column.desiredFieldName, usedFieldNames);
|
|
1007
|
+
usedFieldNames.add(fieldName);
|
|
1008
|
+
tableFieldNames.set(column.columnName, {
|
|
1009
|
+
fieldName,
|
|
1010
|
+
fieldMap: column.fieldMap
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
fieldNamesByTable.set(table.name, tableFieldNames);
|
|
1014
|
+
}
|
|
1015
|
+
return fieldNamesByTable;
|
|
1016
|
+
}
|
|
1017
|
+
function resolveColumnFieldName(fieldNamesByTable, tableName, columnName) {
|
|
1018
|
+
return fieldNamesByTable.get(tableName)?.get(columnName)?.fieldName ?? toFieldName(columnName).name;
|
|
1019
|
+
}
|
|
1020
|
+
function createUniqueFieldName(desiredName, usedFieldNames) {
|
|
1021
|
+
if (!usedFieldNames.has(desiredName)) return desiredName;
|
|
1022
|
+
let counter = 2;
|
|
1023
|
+
while (usedFieldNames.has(`${desiredName}${counter}`)) counter++;
|
|
1024
|
+
return `${desiredName}${counter}`;
|
|
1025
|
+
}
|
|
1026
|
+
function buildTopLevelNameMap(sources, normalize, kind, sourceKind) {
|
|
1027
|
+
const results = /* @__PURE__ */ new Map();
|
|
1028
|
+
const normalizedToSources = /* @__PURE__ */ new Map();
|
|
1029
|
+
for (const source of sources) {
|
|
1030
|
+
const normalized = normalize(source);
|
|
1031
|
+
results.set(source, normalized);
|
|
1032
|
+
normalizedToSources.set(normalized.name, [...normalizedToSources.get(normalized.name) ?? [], source]);
|
|
1033
|
+
}
|
|
1034
|
+
const duplicates = [...normalizedToSources.entries()].filter(([, conflictingSources]) => conflictingSources.length > 1);
|
|
1035
|
+
if (duplicates.length > 0) {
|
|
1036
|
+
const details = duplicates.map(([normalizedName, conflictingSources]) => `- ${kind} "${normalizedName}" from ${sourceKind}s ${conflictingSources.map((source) => `"${source}"`).join(", ")}`);
|
|
1037
|
+
throw new Error(`PSL ${kind} name collisions detected:\n${details.join("\n")}`);
|
|
1038
|
+
}
|
|
1039
|
+
return results;
|
|
1040
|
+
}
|
|
1041
|
+
function createReservedNamedTypeNames(modelNames) {
|
|
1042
|
+
const reservedNames = new Set(PSL_SCALAR_TYPE_NAMES);
|
|
1043
|
+
for (const result of modelNames.values()) reservedNames.add(result.name);
|
|
1044
|
+
return reservedNames;
|
|
1045
|
+
}
|
|
1046
|
+
function seedNamedTypeRegistry(schemaIR, typeMap, enumNameMap, reservedNames) {
|
|
1047
|
+
const seeds = /* @__PURE__ */ new Map();
|
|
1048
|
+
for (const tableName of Object.keys(schemaIR.tables).sort()) {
|
|
1049
|
+
const table = schemaIR.tables[tableName];
|
|
1050
|
+
if (!table) continue;
|
|
1051
|
+
for (const columnName of Object.keys(table.columns).sort()) {
|
|
1052
|
+
const column = table.columns[columnName];
|
|
1053
|
+
if (!column) continue;
|
|
1054
|
+
const resolution = typeMap.resolve(column.nativeType, table.annotations);
|
|
1055
|
+
if ("unsupported" in resolution || enumNameMap.has(column.nativeType) || !resolution.nativeTypeAttribute) continue;
|
|
1056
|
+
const signatureKey = createNamedTypeSignatureKey(resolution);
|
|
1057
|
+
if (!seeds.has(signatureKey)) seeds.set(signatureKey, {
|
|
1058
|
+
baseType: resolution.pslType,
|
|
1059
|
+
desiredName: toNamedTypeName(column.name),
|
|
1060
|
+
nativeTypeAttribute: resolution.nativeTypeAttribute
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
const registry = {
|
|
1065
|
+
entriesByKey: /* @__PURE__ */ new Map(),
|
|
1066
|
+
usedNames: new Set(reservedNames)
|
|
1067
|
+
};
|
|
1068
|
+
const sortedSeeds = [...seeds.entries()].sort((left, right) => {
|
|
1069
|
+
const desiredNameComparison = left[1].desiredName.localeCompare(right[1].desiredName);
|
|
1070
|
+
if (desiredNameComparison !== 0) return desiredNameComparison;
|
|
1071
|
+
return left[0].localeCompare(right[0]);
|
|
1072
|
+
});
|
|
1073
|
+
for (const [signatureKey, seed] of sortedSeeds) {
|
|
1074
|
+
const name = createUniqueFieldName(seed.desiredName, registry.usedNames);
|
|
1075
|
+
registry.entriesByKey.set(signatureKey, {
|
|
1076
|
+
name,
|
|
1077
|
+
baseType: seed.baseType,
|
|
1078
|
+
nativeTypeAttribute: seed.nativeTypeAttribute
|
|
1079
|
+
});
|
|
1080
|
+
registry.usedNames.add(name);
|
|
1081
|
+
}
|
|
1082
|
+
return registry;
|
|
1083
|
+
}
|
|
1084
|
+
function resolveNamedTypeName(registry, resolution) {
|
|
1085
|
+
const key = createNamedTypeSignatureKey(resolution);
|
|
1086
|
+
const existing = registry.entriesByKey.get(key);
|
|
1087
|
+
if (existing) return existing.name;
|
|
1088
|
+
throw new Error(`Named type registry was not seeded for native type "${resolution.nativeType}"`);
|
|
1089
|
+
}
|
|
1090
|
+
function createNamedTypeSignatureKey(resolution) {
|
|
1091
|
+
return JSON.stringify({
|
|
1092
|
+
baseType: resolution.pslType,
|
|
1093
|
+
nativeTypeAttribute: resolution.nativeTypeAttribute ? {
|
|
1094
|
+
name: resolution.nativeTypeAttribute.name,
|
|
1095
|
+
args: resolution.nativeTypeAttribute.args ?? null
|
|
1096
|
+
} : null
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
function topologicalSort(models, tables, modelNameMap) {
|
|
1100
|
+
const modelByName = /* @__PURE__ */ new Map();
|
|
1101
|
+
for (const model of models) modelByName.set(model.name, model);
|
|
1102
|
+
const deps = /* @__PURE__ */ new Map();
|
|
1103
|
+
const tableToModel = /* @__PURE__ */ new Map();
|
|
1104
|
+
for (const tableName of Object.keys(tables)) {
|
|
1105
|
+
const modelName = modelNameMap.get(tableName);
|
|
1106
|
+
tableToModel.set(tableName, modelName);
|
|
1107
|
+
deps.set(modelName, /* @__PURE__ */ new Set());
|
|
1108
|
+
}
|
|
1109
|
+
for (const [tableName, table] of Object.entries(tables)) {
|
|
1110
|
+
const modelName = tableToModel.get(tableName);
|
|
1111
|
+
for (const fk of table.foreignKeys) {
|
|
1112
|
+
const refModelName = tableToModel.get(fk.referencedTable);
|
|
1113
|
+
if (refModelName && refModelName !== modelName) deps.get(modelName).add(refModelName);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
const result = [];
|
|
1117
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1118
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
1119
|
+
const sortedNames = [...deps.keys()].sort();
|
|
1120
|
+
function visit(name) {
|
|
1121
|
+
if (visited.has(name)) return;
|
|
1122
|
+
if (visiting.has(name)) return;
|
|
1123
|
+
visiting.add(name);
|
|
1124
|
+
const sortedDeps = [...deps.get(name)].sort();
|
|
1125
|
+
for (const dep of sortedDeps) visit(dep);
|
|
1126
|
+
visiting.delete(name);
|
|
1127
|
+
visited.add(name);
|
|
1128
|
+
result.push(modelByName.get(name));
|
|
1129
|
+
}
|
|
1130
|
+
for (const name of sortedNames) visit(name);
|
|
1131
|
+
return result;
|
|
1132
|
+
}
|
|
1133
|
+
//#endregion
|
|
1134
|
+
//#region src/exports/control.ts
|
|
423
1135
|
function postgresRenderDefault(def, column) {
|
|
424
1136
|
if (def.kind === "function") return def.expression;
|
|
425
1137
|
return renderDefaultLiteral(def.value, column);
|
|
@@ -428,6 +1140,15 @@ const postgresTargetDescriptor = {
|
|
|
428
1140
|
...postgresTargetDescriptorMeta,
|
|
429
1141
|
contractSerializer: new PostgresContractSerializer(),
|
|
430
1142
|
schemaVerifier: new PostgresSchemaVerifier(),
|
|
1143
|
+
inferPslContract(schema, describedContracts) {
|
|
1144
|
+
PostgresDatabaseSchemaNode.assert(schema);
|
|
1145
|
+
return inferPostgresPslContract(schema, describedContracts);
|
|
1146
|
+
},
|
|
1147
|
+
diffSchema(input) {
|
|
1148
|
+
return diffPostgresSchema(input);
|
|
1149
|
+
},
|
|
1150
|
+
classifySubjectGranularity: postgresDiffSubjectGranularity,
|
|
1151
|
+
classifyEntityKind: postgresDiffSubjectEntityKind,
|
|
431
1152
|
migrations: {
|
|
432
1153
|
createPlanner(adapter) {
|
|
433
1154
|
return createPostgresMigrationPlanner(adapter);
|
|
@@ -436,9 +1157,10 @@ const postgresTargetDescriptor = {
|
|
|
436
1157
|
return createPostgresMigrationRunner(family);
|
|
437
1158
|
},
|
|
438
1159
|
contractToSchema(contract, frameworkComponents) {
|
|
439
|
-
|
|
1160
|
+
const expander = buildNativeTypeExpander(frameworkComponents);
|
|
1161
|
+
return contractToPostgresDatabaseSchemaNode(blindCast(contract), {
|
|
440
1162
|
annotationNamespace: "pg",
|
|
441
|
-
...ifDefined("expandNativeType",
|
|
1163
|
+
...ifDefined("expandNativeType", expander),
|
|
442
1164
|
renderDefault: postgresRenderDefault
|
|
443
1165
|
});
|
|
444
1166
|
}
|