文件最后提交记录最后更新时间
9 小时前
9 小时前
2 天前
1 个月前
1 个月前
2 天前
1 个月前
1 个月前
5 天前
5 个月前
README

@internal/family-sql

SQL family descriptor for Prisma 8.

Purpose

Provides the SQL family descriptor (ControlFamilyDescriptor) that includes:

  • The SQL target family hook (sqlEmission)
  • Factory method (create()) to create family instances

Responsibilities

  • Family Descriptor Export: Exports the SQL ControlFamilyDescriptor for use in CLI configuration files
  • Family Instance Creation: Creates SqlFamilyInstance objects that implement control-plane domain actions (verify, schemaVerify, introspect, emitContract, deserializeContract)
  • Planner & Runner SPI: Owns the MigrationPlanner / MigrationRunner interfaces plus the SqlControlTargetDescriptor helper so targets can expose planners and runners (e.g., Postgres init planner/runner)
  • Family Hook Integration: Integrates the SQL target family hook (sqlEmission) from @internal/sql-contract-emitter
  • Control Plane Entry Point: Serves as the control plane entry point for the SQL family, enabling the CLI to select the family hook and create family instances
  • Contract-to-SchemaIR Conversion: Converts SqlStorage from a contract into SqlSchemaIR for offline migration planning, enabling migration plan to work without a database connection
  • Destructive Change Detection: Compares two SqlStorage values and identifies destructive changes (dropped tables/columns) for migration policy enforcement
  • Storage Type Control Hooks: Extracts codec-owned control hooks for planning/verification/introspection of storage.types without adding enum-specific fields to shared IR
  • Codec Ownership: Enforces a single owner per codecId for parameterized renderers and control-plane hooks to prevent ambiguous conflicts during assembly
  • Authoring Contribution Assembly: Assembles authoring contributions (type constructors, field presets) from composed components for PSL interpretation
  • Parameterized Type Verification: Expands contract typeParams into expected native type strings during schema verification and flags missing parameters as type mismatches
  • Schema Defaults Policy: Ignores execution mutation defaults during schema verification since they are applied before DB writes
  • Foreign Key Config Awareness: Schema verification respects the contract's foreignKeys configuration — when foreignKeys.constraints is false, FK constraint checks are skipped during verification (see ADR 161)
  • Referential Action Verification: When a contract FK specifies onDelete or onUpdate, the verifier compares them against the introspected schema and reports foreign_key_mismatch on mismatch (see ADR 166)

Usage

import sql from '@internal/family-sql/control';
import { createControlStack } from '@internal/framework-components/control';

// sql is a ControlFamilyDescriptor with:
// - kind: 'family'
// - id: 'sql'
// - familyId: 'sql'
// - hook: TargetFamilyHook
// - create: (stack) => SqlFamilyInstance

// Build a control stack (assembles all contributions from components)
const stack = createControlStack({
  family: sql,
  target: postgresTargetDescriptor,
  adapter: postgresAdapterDescriptor,
  driver: postgresDriverDescriptor,
  extensions: [pgVectorExtensionDescriptor],
});

// Create a family instance for control-plane operations
const familyInstance = sql.create(stack);

// Use instance methods for domain actions
const contract = familyInstance.deserializeContract(contractJson);
const verifyResult = await familyInstance.verify({ driver, contract, ... });

// Targets that implement SqlControlTargetDescriptor can build planners
const planner = postgresTargetDescriptor.migrations.createPlanner(familyInstance);
const planResult = planner.plan({
  contract: sqlContract,
  schema,
  policy,
  frameworkComponents: [postgresTargetDescriptor, postgresAdapterDescriptor, pgVectorExtensionDescriptor],
});

// Targets also provide runners for executing plans
const runner = postgresTargetDescriptor.migrations.createRunner(familyInstance);
const executeResult = await runner.execute({
  plan: planResult.plan,
  driver,
  destinationContract: sqlContract,
  frameworkComponents: [postgresTargetDescriptor, postgresAdapterDescriptor, pgVectorExtensionDescriptor],
});

// PSL contribution assembly (scalar type descriptors, mutation defaults, authoring
// contributions, codec lookup) is handled at the framework level by createControlStack.
// The CLI passes assembled contributions via ContractSourceContext when calling
// contract source providers — no manual assembly needed in user configs.

// executeResult is a Result<MigrationRunnerSuccessValue, MigrationRunnerFailure>
if (executeResult.ok) {
  console.log(`Executed ${executeResult.value.operationsExecuted} operations`);
} else {
  console.error(`Migration failed: ${executeResult.failure.code} - ${executeResult.failure.summary}`);
}

Architecture

This package is the control plane entry point for the SQL family. It composes:

  • @internal/sql-contract-emitter - Provides the SQL family hook
  • @internal/sql-operations - SQL operation signature types
  • @internal/sql-contract - SQL contract types and validation

The framework CLI uses this descriptor to:

  1. Create family instances for control-plane operations (via create())

Family instances implement domain actions:

  • deserializeContract(contractJson): Validates and normalizes contract, returns Contract without mappings

  • verify(): Verifies database marker against contract (compares target, storageHash, profileHash)

  • schemaVerify(): Verifies database schema against contract (compares contract requirements vs live schema)

  • introspect(): Introspects database schema and returns SqlSchemaIR

  • toSchemaView(schema): Projects SqlSchemaIR into CoreSchemaView for human-readable display. Always displays native database types (e.g., int4, text) rather than mapped codec IDs (e.g., pg/int4@1) to reflect actual database state.

  • emitContract({ contract }): Emits contract JSON and DTS as strings. Handles stripping mappings and validation internally. Uses preassembled state (operation registry, type imports, extension IDs).

  • inferPslContract(schemaIR): Infers a PSL contract AST from an introspected schema, for contract infer. Delegates to the target descriptor's optional inferPslContract hook; throws CONTRACT.INFER_UNSUPPORTED when the target has none.

  • buildPslContract(contract): Builds the PSL document AST that reads back as the same contract, for contract print. Delegates to the target descriptor's optional buildPslContract hook, and passes it the stack's authoring contributions, codecs and data types (SqlPslBuildContext), the parts the PSL source reads the file back with. Returns the document with sourceSettings: the settings the config must set on the new PSL source because a PSL file cannot carry them. Today that is only the contract's defaultControlPolicy, present when the contract has one. Throws CONTRACT.PRINT_UNSUPPORTED when the target has no hook, or when the contract holds something PSL cannot express. contract must be one the target's contract serializer accepted.

The descriptor is "pure data + factory" - it only provides the hook and factory method. All family-specific logic lives on the instance.

Package Structure

  • src/core/control-descriptor.ts: SqlFamilyDescriptor class implementing ControlFamilyDescriptor interface (pure data + factory)
  • src/core/control-instance.ts: createSqlFamilyInstance function that creates SqlFamilyInstance with domain action methods (deserializeContract, verify, schemaVerify, introspect, toSchemaView, emitContract). Contains convertOperationManifest function used internally by instance creation and test utilities in the same package.
  • src/core/assembly.ts: Assembly helpers for extracting type imports, collecting codec-owned storage type control hooks, and composing mutation-default registries with duplicate detection.
  • src/core/verify.ts: Verification helpers (parseContractMarkerRow, collectSupportedCodecTypeIds)
  • src/core/control-adapter.ts: SQL control adapter interface (SqlControlAdapter) for control-plane operations
  • src/core/migrations/: Migration IR helpers plus planner and runner SPI types (MigrationPlanner, MigrationRunner, SqlControlTargetDescriptor). Runners return MigrationRunnerResult which is a union of success/failure.
  • src/core/migrations/contract-to-schema-ir.ts: contractToSchemaIR(contract, { annotationNamespace, ... }) converts a contract to SqlSchemaIR for offline migration planning (used by migration plan to synthesize the "from" schema without a database connection). A target may pass dataTypeOf, built by buildDataTypeResolver(frameworkComponents) in data-type-resolver.ts, which returns the data type a codec's descriptor names among the data types every component registers. Each column carries its data type, and a default of a type that declares a canonical form compares through it, so two texts of one date or time value are equal. Also exports detectDestructiveChanges(from, to) which compares two SqlStorage values and returns a list of destructive changes (dropped tables, dropped columns) for migration policy enforcement.

Migration Runner Error Codes

The runner returns structured errors with the following codes:

  • DESTINATION_CONTRACT_MISMATCH: Plan destination hash doesn't match provided contract hash
  • MARKER_ORIGIN_MISMATCH: Existing marker doesn't match plan's expected origin
  • POLICY_VIOLATION: Operation class is not allowed by the plan's policy
  • PRECHECK_FAILED: Operation precheck returned false
  • POSTCHECK_FAILED: Operation postcheck returned false after execution
  • SCHEMA_VERIFY_FAILED: Resulting schema doesn't satisfy the destination contract
  • EXECUTION_FAILED: SQL execution error during operation execution
  • src/exports/control.ts: Control plane entry point (exports SqlFamilyDescriptor instance)
  • src/exports/runtime.ts: Runtime plane entry point

Entrypoints

  • ./control: Control plane entry point for CLI/config usage (exports SqlFamilyDescriptor)
  • ./control-adapter: SQL control adapter interface (SqlControlAdapter, SqlControlAdapterDescriptor) for target-specific adapters
  • ./psl-build: PSL building blocks both contract infer and contract print use, with no dialect knowledge: mapDefault (a stored default as the PSL attribute that reads back as it), the PslTypeMap types, and toEnumMemberName
  • ./psl-infer: Database-to-PSL inference utilities for contract infer: name transforms, relation inference, and the printer-config types
  • ./runtime: Runtime plane identity exports only (family ID, types, descriptor identity). Does not export runtime creation helpers—use instantiateExecutionStack from @internal/framework-components/execution and createExecutionContext, createRuntime, createSqlExecutionStack from @internal/sql-runtime. See ADR 152.
  • ./verify: Marker row parsing helper (parseContractMarkerRow). Marker reads are owned by each SqlControlAdapter (e.g. PostgresControlAdapter.readMarker) so dialect-specific SQL stays target-local.

How to debug db init

  • CLI orchestration: packages/1-framework/3-tooling/cli/src/commands/db-init.ts
  • Planner/runner SPI types: packages/2-sql/3-tooling/family/src/core/migrations/types.ts
  • Pure schema verifier (used by planner + runner): @internal/family-sql/schema-verify (source: packages/2-sql/3-tooling/family/src/core/schema-verify/)
  • Postgres implementation:
    • Planner: packages/3-targets/3-targets/postgres/src/core/migrations/planner.ts
    • Runner: packages/3-targets/3-targets/postgres/src/core/migrations/runner.ts
  • Tests:
    • CLI integration: test/integration/test/cli.db-init.e2e.test.ts
    • Target unit/integration: packages/3-targets/3-targets/postgres/test/migrations/*