@prisma/orm-family-sql 8.0.0-rc.2-dev.3 → 8.0.0-rc.20-dev.1
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/README.md +2 -6
- package/dist/{aggregate-descriptor-registry-C7hel5O7.mjs → aggregate-descriptor-registry-ZrKjxwQe.mjs} +5 -5
- package/dist/aggregate-descriptor-registry-ZrKjxwQe.mjs.map +1 -0
- package/dist/ast-D0miQ1B6.mjs +372 -0
- package/dist/ast-D0miQ1B6.mjs.map +1 -0
- package/dist/ast-YEMe_B8z.d.mts +292 -0
- package/dist/ast-YEMe_B8z.d.mts.map +1 -0
- package/dist/attribute-specs-DA_RwQFB.d.mts +114 -0
- package/dist/attribute-specs-DA_RwQFB.d.mts.map +1 -0
- package/dist/{authored-check-naming-Hf9990DV.mjs → authored-check-naming-BHGJhjqn.mjs} +5 -5
- package/dist/authored-check-naming-BHGJhjqn.mjs.map +1 -0
- package/dist/{authored-check-naming-BWBe-PVE.d.mts → authored-check-naming-D-o196ry.d.mts} +3 -3
- package/dist/authored-check-naming-D-o196ry.d.mts.map +1 -0
- package/dist/authoring-entity-types-CgKIgWq5-pEdfjam5.d.mts +9 -0
- package/dist/authoring-entity-types-CgKIgWq5-pEdfjam5.d.mts.map +1 -0
- package/dist/builder.d.mts +4 -3
- package/dist/builder__runtime.d.mts +29 -31
- package/dist/builder__runtime.d.mts.map +1 -1
- package/dist/builder__runtime.mjs +261 -191
- package/dist/builder__runtime.mjs.map +1 -1
- package/dist/builder__types.d.mts +6 -3
- package/dist/{canonicalization-hooks-dmXkOR4a.mjs → canonicalization-hooks-Ce1v_TDl.mjs} +2 -2
- package/dist/canonicalization-hooks-Ce1v_TDl.mjs.map +1 -0
- package/dist/{canonicalization-hooks-CYnW_sGn.d.mts → canonicalization-hooks-DyapQ0my.d.mts} +2 -2
- package/dist/canonicalization-hooks-DyapQ0my.d.mts.map +1 -0
- package/dist/{check-constraint-B7qGu6rc-HvQjjX82.d.mts → check-constraint-B7qGu6rc-Bk2ezWXu.d.mts} +4 -4
- package/dist/check-constraint-B7qGu6rc-Bk2ezWXu.d.mts.map +1 -0
- package/dist/{codec-descriptor-registry-N0dhtKaH.mjs → codec-descriptor-registry-CiiFzMtb.mjs} +13 -40
- package/dist/codec-descriptor-registry-CiiFzMtb.mjs.map +1 -0
- package/dist/collection-methods-BB8YH69u.d.mts +1 -0
- package/dist/collection-methods-D2UnCS0S-DOy2qHYS.d.mts +77 -0
- package/dist/collection-methods-D2UnCS0S-DOy2qHYS.d.mts.map +1 -0
- package/dist/{config-types-D9fdKG3g.d.mts → config-types-C-1mIiJO.d.mts} +5 -5
- package/dist/config-types-C-1mIiJO.d.mts.map +1 -0
- package/dist/{config-types-gAm54C_v.mjs → config-types-DkEeq_qq.mjs} +10 -6
- package/dist/config-types-DkEeq_qq.mjs.map +1 -0
- package/dist/{contract-builder-D4a5dT7j.mjs → contract-builder-50lF0kz1.mjs} +431 -226
- package/dist/contract-builder-50lF0kz1.mjs.map +1 -0
- package/dist/contract-builder-C_Zi57iD.d.mts +1967 -0
- package/dist/contract-builder-C_Zi57iD.d.mts.map +1 -0
- package/dist/contract-emitter.d.mts +4 -7
- package/dist/contract-emitter.d.mts.map +1 -1
- package/dist/contract-emitter.mjs +1 -1
- package/dist/{contract-errors-BCN6RVI5-5IHBF_lg.mjs → contract-errors-BCN6RVI5-DoH6b_KZ.mjs} +2 -2
- package/dist/contract-errors-BCN6RVI5-DoH6b_KZ.mjs.map +1 -0
- package/dist/contract-prisma7__provider.d.mts +97 -0
- package/dist/contract-prisma7__provider.d.mts.map +1 -0
- package/dist/contract-prisma7__provider.mjs +2011 -0
- package/dist/contract-prisma7__provider.mjs.map +1 -0
- package/dist/contract-psl.d.mts +18 -29
- package/dist/contract-psl.d.mts.map +1 -1
- package/dist/contract-psl.mjs +1 -1
- package/dist/contract-psl__attribute-specs.d.mts +2 -0
- package/dist/contract-psl__attribute-specs.mjs +2 -0
- package/dist/contract-psl__default-table-name.d.mts +7 -0
- package/dist/contract-psl__default-table-name.d.mts.map +1 -0
- package/dist/contract-psl__default-table-name.mjs +2 -0
- package/dist/contract-psl__diagnostics.d.mts +12 -0
- package/dist/contract-psl__diagnostics.d.mts.map +1 -0
- package/dist/contract-psl__diagnostics.mjs +2 -0
- package/dist/contract-psl__map-names.d.mts +16 -0
- package/dist/contract-psl__map-names.d.mts.map +1 -0
- package/dist/contract-psl__map-names.mjs +21 -0
- package/dist/contract-psl__map-names.mjs.map +1 -0
- package/dist/contract-psl__provider.d.mts +3 -3
- package/dist/contract-psl__provider.d.mts.map +1 -1
- package/dist/contract-psl__provider.mjs +95 -46
- package/dist/contract-psl__provider.mjs.map +1 -1
- package/dist/contract-ts.d.mts +5 -5
- package/dist/contract-ts.mjs +5 -4
- package/dist/contract-ts__config-types.d.mts +1 -1
- package/dist/contract-ts__config-types.mjs +1 -1
- package/dist/contract-ts__contract-builder.d.mts +4 -4
- package/dist/contract-ts__contract-builder.mjs +4 -3
- package/dist/{contract-view-BMR5IWk4.mjs → contract-view-DqpzeYeE.mjs} +2 -2
- package/dist/contract-view-DqpzeYeE.mjs.map +1 -0
- package/dist/{contract-view-CPHxoAEX.d.mts → contract-view-DuB35or0.d.mts} +3 -3
- package/dist/contract-view-DuB35or0.d.mts.map +1 -0
- package/dist/contract.d.mts +30 -28
- package/dist/contract.mjs +23 -18
- package/dist/contract__authored-check-naming.d.mts +1 -1
- package/dist/contract__authored-check-naming.mjs +1 -1
- package/dist/contract__canonicalization-hooks.d.mts +1 -1
- package/dist/contract__canonicalization-hooks.mjs +1 -1
- package/dist/contract__contract-view.d.mts +1 -1
- package/dist/contract__contract-view.mjs +1 -1
- package/dist/contract__data-type-support.d.mts +2 -0
- package/dist/contract__data-type-support.mjs +2 -0
- package/dist/contract__data-type.d.mts +2 -0
- package/dist/contract__data-type.mjs +3 -0
- package/dist/contract__entity-handle-lowering-hook.d.mts +2 -2
- package/dist/contract__entity-handle-lowering-hook.mjs +2 -2
- package/dist/contract__entity-kinds.d.mts +1 -1
- package/dist/contract__entity-kinds.mjs +1 -1
- package/dist/contract__factories.d.mts +1 -1
- package/dist/contract__factories.mjs +1 -1
- package/dist/contract__foreign-key-materialization.d.mts +2 -2
- package/dist/contract__foreign-key-materialization.mjs +3 -2
- package/dist/contract__index-naming.d.mts +2 -2
- package/dist/contract__index-naming.mjs +2 -2
- package/dist/contract__index-type-validation.d.mts +2 -2
- package/dist/contract__index-type-validation.mjs +1 -1
- package/dist/contract__index-types.d.mts +3 -3
- package/dist/contract__index-types.mjs +2 -2
- package/dist/contract__referential-action-sql.d.mts +1 -1
- package/dist/contract__referential-action-sql.mjs +1 -1
- package/dist/contract__resolve-storage-table.d.mts +1 -1
- package/dist/contract__resolve-storage-table.mjs +1 -1
- package/dist/contract__sql-expression.d.mts +2 -0
- package/dist/contract__sql-expression.mjs +2 -0
- package/dist/contract__types.d.mts +12 -12
- package/dist/contract__types.mjs +4 -4
- package/dist/contract__validators.d.mts +2 -2
- package/dist/contract__validators.mjs +3 -3
- package/dist/contract__value-set-derivation-hook.d.mts +1 -1
- package/dist/contract__value-set-derivation-hook.mjs +1 -1
- package/dist/control-BK50zF6c.mjs +2721 -0
- package/dist/control-BK50zF6c.mjs.map +1 -0
- package/dist/control-ChAUdOOu.d.mts +1416 -0
- package/dist/control-ChAUdOOu.d.mts.map +1 -0
- package/dist/{control-adapter-C99J7aEF-B7eQd7vD.d.mts → control-adapter-Bn0uhplA-BzP2uPqJ.d.mts} +27 -44
- package/dist/control-adapter-Bn0uhplA-BzP2uPqJ.d.mts.map +1 -0
- package/dist/control-adapter-NZ2kKc4x.d.mts +13 -0
- package/dist/control-adapter-NZ2kKc4x.d.mts.map +1 -0
- package/dist/control-adapter-SlQkTEwT.mjs +16 -0
- package/dist/control-adapter-SlQkTEwT.mjs.map +1 -0
- package/dist/data-type-BFSpaotu.mjs +52 -0
- package/dist/data-type-BFSpaotu.mjs.map +1 -0
- package/dist/data-type-DJIvKQHf.d.mts +158 -0
- package/dist/data-type-DJIvKQHf.d.mts.map +1 -0
- package/dist/data-type-support-BhSoPUFj.mjs +9 -0
- package/dist/data-type-support-BhSoPUFj.mjs.map +1 -0
- package/dist/data-type-support-CXpA0ex-.d.mts +7 -0
- package/dist/data-type-support-CXpA0ex-.d.mts.map +1 -0
- package/dist/{db-D18PrXKW-DvTv4JFn.d.mts → db-D1d75IvM-D4mRDC6w.d.mts} +123 -128
- package/dist/db-D1d75IvM-D4mRDC6w.d.mts.map +1 -0
- package/dist/{ddl-types-CXx4vPz6-BsgrnSUY.d.mts → ddl-types-By5mT2_S-CdaaFvsX.d.mts} +24 -29
- package/dist/ddl-types-By5mT2_S-CdaaFvsX.d.mts.map +1 -0
- package/dist/default-mapping-CAOONlKQ-4m07nb-O.d.mts +39 -0
- package/dist/default-mapping-CAOONlKQ-4m07nb-O.d.mts.map +1 -0
- package/dist/default-table-name-BfFY8qYR-CWZFDX6v.mjs +9 -0
- package/dist/default-table-name-BfFY8qYR-CWZFDX6v.mjs.map +1 -0
- package/dist/derived-checks-D5HVRvqN-BJx1gwPA.mjs +1508 -0
- package/dist/derived-checks-D5HVRvqN-BJx1gwPA.mjs.map +1 -0
- package/dist/{derived-checks-DPdD01O0-DGqOFjs6.d.mts → derived-checks-DpPkO6Dt-9U9UJZOo.d.mts} +4 -4
- package/dist/derived-checks-DpPkO6Dt-9U9UJZOo.d.mts.map +1 -0
- package/dist/diagnostics-RHJxoiIk.mjs +23 -0
- package/dist/diagnostics-RHJxoiIk.mjs.map +1 -0
- package/dist/{diff-DbTNVm2f.d.mts → diff-1qHOLoUB.d.mts} +23 -10
- package/dist/diff-1qHOLoUB.d.mts.map +1 -0
- package/dist/diff-C1rFOhek.mjs +21 -0
- package/dist/diff-C1rFOhek.mjs.map +1 -0
- package/dist/{dist-ZK6oeI5e.mjs → dist-C8Y-PPuE.mjs} +36 -212
- package/dist/dist-C8Y-PPuE.mjs.map +1 -0
- package/dist/{dist-C_q1_QR9.mjs → dist-CHz1hF8M.mjs} +2 -2
- package/dist/dist-CHz1hF8M.mjs.map +1 -0
- package/dist/{dist-x6jvtkKM.mjs → dist-CMDXybwm.mjs} +2 -2
- package/dist/dist-CMDXybwm.mjs.map +1 -0
- package/dist/{driver-types-BQtRkOJ0-Bv2RPSfr.d.mts → driver-types-DRsYeq3j-BimeCnUk.d.mts} +11 -2
- package/dist/driver-types-DRsYeq3j-BimeCnUk.d.mts.map +1 -0
- package/dist/entity-handle-lowering-hook-BJZSlVVF.mjs +16 -0
- package/dist/entity-handle-lowering-hook-BJZSlVVF.mjs.map +1 -0
- package/dist/{entity-handle-lowering-hook-CIvRKBfa.d.mts → entity-handle-lowering-hook-BuvLtgFi.d.mts} +8 -11
- package/dist/entity-handle-lowering-hook-BuvLtgFi.d.mts.map +1 -0
- package/dist/{entity-kinds-B5cUFU22.d.mts → entity-kinds-BPG2xCZV.d.mts} +7 -7
- package/dist/entity-kinds-BPG2xCZV.d.mts.map +1 -0
- package/dist/{entity-kinds-D7vwoN_T-CCBwFJy0.mjs → entity-kinds-JR5SAm5h-DOH6VUtL.mjs} +31 -28
- package/dist/entity-kinds-JR5SAm5h-DOH6VUtL.mjs.map +1 -0
- package/dist/{errors-B5g0xWro-mWH2C07F.mjs → errors-B5g0xWro-C8plbqga.mjs} +2 -2
- package/dist/errors-B5g0xWro-C8plbqga.mjs.map +1 -0
- package/dist/errors.d.mts +1 -1
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +1 -1
- package/dist/exports-C3wGYtID-D65DEUTC.mjs +2389 -0
- package/dist/exports-C3wGYtID-D65DEUTC.mjs.map +1 -0
- package/dist/{expression-FJK6Ar-d.d.mts → expression-6ld_ZQL6-fOuazzzm.d.mts} +37 -16
- package/dist/expression-6ld_ZQL6-fOuazzzm.d.mts.map +1 -0
- package/dist/expression-CieaNuRM-B1uVqxwd.d.mts +22 -0
- package/dist/expression-CieaNuRM-B1uVqxwd.d.mts.map +1 -0
- package/dist/expression-D8HstQiB.d.mts +2 -0
- package/dist/{expression-CX5mTA-z.mjs → expression-impl-B-XNINxr-BhN2sysZ.mjs} +41 -18
- package/dist/expression-impl-B-XNINxr-BhN2sysZ.mjs.map +1 -0
- package/dist/{factories-SVor5xIT.mjs → factories-Crobuxwn.mjs} +27 -20
- package/dist/factories-Crobuxwn.mjs.map +1 -0
- package/dist/{factories-P3qBQhO-.d.mts → factories-DbNP9qfs.d.mts} +16 -10
- package/dist/factories-DbNP9qfs.d.mts.map +1 -0
- package/dist/family.d.mts +17 -13
- package/dist/family.mjs +17 -12
- package/dist/family__control-adapter.d.mts +3 -3
- package/dist/family__control-adapter.mjs +2 -1
- package/dist/family__control.d.mts +5 -4
- package/dist/family__control.mjs +4 -4
- package/dist/family__diff.d.mts +2 -3
- package/dist/family__diff.mjs +2 -2
- package/dist/family__ir.d.mts +1 -1
- package/dist/family__ir.mjs +2 -2
- package/dist/family__migration.d.mts +1 -1
- package/dist/family__migration.mjs +1 -1
- package/dist/family__pack.d.mts +2 -249
- package/dist/family__pack.mjs +3 -18
- package/dist/family__psl-build.d.mts +3 -0
- package/dist/family__psl-build.mjs +3 -0
- package/dist/family__psl-infer.d.mts +2 -2
- package/dist/family__psl-infer.mjs +2 -2
- package/dist/family__runtime.d.mts +2 -2
- package/dist/family__runtime.mjs +2 -2
- package/dist/family__verify.d.mts +1 -1
- package/dist/family__verify.mjs +1 -1
- package/dist/foreign-key-materialization-48P2V8on.d.mts +98 -0
- package/dist/foreign-key-materialization-48P2V8on.d.mts.map +1 -0
- package/dist/foreign-key-materialization-BM59w_Xg.mjs +287 -0
- package/dist/foreign-key-materialization-BM59w_Xg.mjs.map +1 -0
- package/dist/{foreign-key-C-iTPuRT-C9ip8zFx.d.mts → foreign-key-qkDFfG8t-TaK_GYWR.d.mts} +21 -5
- package/dist/foreign-key-qkDFfG8t-TaK_GYWR.d.mts.map +1 -0
- package/dist/functions-CsOoN7I3.mjs +88 -0
- package/dist/functions-CsOoN7I3.mjs.map +1 -0
- package/dist/functions-EW-RrRn3.d.mts +73 -0
- package/dist/functions-EW-RrRn3.d.mts.map +1 -0
- package/dist/generator-functions-D_qdqBV--Bq296cED.mjs +544 -0
- package/dist/generator-functions-D_qdqBV--Bq296cED.mjs.map +1 -0
- package/dist/index-7nHHQsAj.d.mts +606 -0
- package/dist/index-7nHHQsAj.d.mts.map +1 -0
- package/dist/{index-C5I8D-MB.d.mts → index-Nqc3OHR_.d.mts} +4 -5
- package/dist/index-Nqc3OHR_.d.mts.map +1 -0
- package/dist/index-equivalence-DswCiq5l-CGsyPP9B.mjs +109 -0
- package/dist/index-equivalence-DswCiq5l-CGsyPP9B.mjs.map +1 -0
- package/dist/{index-naming-DREt6js7-CRH27yUv.mjs → index-naming-BOcJT1B3-mG8Fd6KN.mjs} +35 -9
- package/dist/index-naming-BOcJT1B3-mG8Fd6KN.mjs.map +1 -0
- package/dist/{index-naming-Ct65z5Xk.d.mts → index-naming-Cs6wwia3.d.mts} +15 -5
- package/dist/index-naming-Cs6wwia3.d.mts.map +1 -0
- package/dist/index-reference-4wsdeB7v-DCwswK-q.d.mts +67 -0
- package/dist/index-reference-4wsdeB7v-DCwswK-q.d.mts.map +1 -0
- package/dist/index-reference-BQSrkRfT.mjs +55 -0
- package/dist/index-reference-BQSrkRfT.mjs.map +1 -0
- package/dist/index-reference-D8ewQRJr.d.mts +1 -0
- package/dist/index-type-validation-DJEMmmw0.mjs +42 -0
- package/dist/index-type-validation-DJEMmmw0.mjs.map +1 -0
- package/dist/index-type-validation-fGGwAHWc.d.mts +16 -0
- package/dist/index-type-validation-fGGwAHWc.d.mts.map +1 -0
- package/dist/index-types-H1O43iYT-BD0VwPqV.d.mts +66 -0
- package/dist/index-types-H1O43iYT-BD0VwPqV.d.mts.map +1 -0
- package/dist/index-types-pC_5vADQ-BOOBi14R.mjs +95 -0
- package/dist/index-types-pC_5vADQ-BOOBi14R.mjs.map +1 -0
- package/dist/index-types-snhtt_Fo.d.mts +1 -0
- package/dist/interpreter-D5qk4B---CG0C7kdx.mjs +2280 -0
- package/dist/interpreter-D5qk4B---CG0C7kdx.mjs.map +1 -0
- package/dist/{ir-Bghjt-FG.d.mts → ir-BvptQsFW.d.mts} +11 -5
- package/dist/ir-BvptQsFW.d.mts.map +1 -0
- package/dist/{ir-I-xciGUH.mjs → ir-DzsUDO8e.mjs} +2 -2
- package/dist/ir-DzsUDO8e.mjs.map +1 -0
- package/dist/{middleware-AhTz1BOW-th-Suxuf.d.mts → middleware-DHkW6pRr-DM6hd9iZ.d.mts} +5 -5
- package/dist/middleware-DHkW6pRr-DM6hd9iZ.d.mts.map +1 -0
- package/dist/middleware-gqy3l13N.d.mts +1 -0
- package/dist/{middleware-CMr4CHNz-DHIUdxlm.mjs → middleware-yBAfiVWn-DZO0vCRQ.mjs} +3 -3
- package/dist/middleware-yBAfiVWn-DZO0vCRQ.mjs.map +1 -0
- package/dist/{migration-CK4oePIo.mjs → migration-BJQjkqFz.mjs} +4 -4
- package/dist/migration-BJQjkqFz.mjs.map +1 -0
- package/dist/{migration-CjDRZNep.d.mts → migration-Cxmpzaz0.d.mts} +6 -6
- package/dist/migration-Cxmpzaz0.d.mts.map +1 -0
- package/dist/{naming-CV9j2pXl-KARaDReS.d.mts → naming-BcD36g9T-Mfe_xGGL.d.mts} +9 -14
- package/dist/naming-BcD36g9T-Mfe_xGGL.d.mts.map +1 -0
- package/dist/{naming-DKr4TJd7-Cszhq9RV.mjs → naming-bM5qm5PY-jOg3JFMb.mjs} +20 -31
- package/dist/naming-bM5qm5PY-jOg3JFMb.mjs.map +1 -0
- package/dist/{ddl-types-BGdnAbXo-DKPHPUGx.mjs → opaque-sql-Dset3kln-CtsPDTXn.mjs} +24 -11
- package/dist/opaque-sql-Dset3kln-CtsPDTXn.mjs.map +1 -0
- package/dist/operations.d.mts +1 -1
- package/dist/operations.mjs +1 -1
- package/dist/orm-client.d.mts +817 -267
- package/dist/orm-client.d.mts.map +1 -1
- package/dist/orm-client.mjs +4719 -2942
- package/dist/orm-client.mjs.map +1 -1
- package/dist/pack-DA8HZ2mo.d.mts +535 -0
- package/dist/pack-DA8HZ2mo.d.mts.map +1 -0
- package/dist/pack-gSvGQWqs.mjs +80 -0
- package/dist/pack-gSvGQWqs.mjs.map +1 -0
- package/dist/{plan-CDxduYaN-pLIyyJaB.d.mts → plan-Cr4azjnG-CuL8FqXC.d.mts} +12 -8
- package/dist/plan-Cr4azjnG-CuL8FqXC.d.mts.map +1 -0
- package/dist/{plan-Dh0LbYIv-Crx5-ThG.mjs → plan-Dh0LbYIv-ubBSdUgl.mjs} +2 -2
- package/dist/plan-Dh0LbYIv-ubBSdUgl.mjs.map +1 -0
- package/dist/plan-Di6Gs2JD.d.mts +2 -0
- package/dist/psl-attribute-parsing-BCsFWsSp-BJ-wGlY6.mjs +42 -0
- package/dist/psl-attribute-parsing-BCsFWsSp-BJ-wGlY6.mjs.map +1 -0
- package/dist/psl-build-DFBG6vhz.mjs +426 -0
- package/dist/psl-build-DFBG6vhz.mjs.map +1 -0
- package/dist/psl-build-RelubU2E.d.mts +112 -0
- package/dist/psl-build-RelubU2E.d.mts.map +1 -0
- package/dist/psl-column-resolution-ChZqioOc-BREg4J7d.d.mts +24 -0
- package/dist/psl-column-resolution-ChZqioOc-BREg4J7d.d.mts.map +1 -0
- package/dist/psl-field-resolution-C5KQ7kzP-B0NwuRWV.mjs +1960 -0
- package/dist/psl-field-resolution-C5KQ7kzP-B0NwuRWV.mjs.map +1 -0
- package/dist/psl-infer--g4i84Go.mjs +201 -0
- package/dist/psl-infer--g4i84Go.mjs.map +1 -0
- package/dist/psl-infer-DzoxPZGd.d.mts +80 -0
- package/dist/psl-infer-DzoxPZGd.d.mts.map +1 -0
- package/dist/psl-names-C3cwe0rg-D1CTmC3a.mjs +66 -0
- package/dist/psl-names-C3cwe0rg-D1CTmC3a.mjs.map +1 -0
- package/dist/{query-lane-context-Qa-eddrE-kuZ8Z86-.d.mts → query-lane-context-BCEoKy0_-CzmyavC9.d.mts} +20 -49
- package/dist/query-lane-context-BCEoKy0_-CzmyavC9.d.mts.map +1 -0
- package/dist/query-lane-context-VqH0mS9B.d.mts +1 -0
- package/dist/{referential-action-sql-DJ9ps6-S.d.mts → referential-action-sql-B2vKoUK6.d.mts} +3 -3
- package/dist/referential-action-sql-B2vKoUK6.d.mts.map +1 -0
- package/dist/{referential-action-sql-DSqkrySN.mjs → referential-action-sql-BLzCtTi8.mjs} +2 -2
- package/dist/referential-action-sql-BLzCtTi8.mjs.map +1 -0
- package/dist/relational-core.d.mts +14 -13
- package/dist/relational-core.mjs +8 -8
- package/dist/relational-core__aggregate-descriptor-registry.d.mts +2 -2
- package/dist/relational-core__aggregate-descriptor-registry.d.mts.map +1 -1
- package/dist/relational-core__aggregate-descriptor-registry.mjs +1 -1
- package/dist/relational-core__ast.d.mts +5 -5
- package/dist/relational-core__ast.mjs +5 -5
- package/dist/relational-core__codec-descriptor-registry.d.mts +7 -8
- package/dist/relational-core__codec-descriptor-registry.d.mts.map +1 -1
- package/dist/relational-core__codec-descriptor-registry.mjs +1 -1
- package/dist/relational-core__collection-methods.d.mts +3 -0
- package/dist/relational-core__contract-free.d.mts +18 -8
- package/dist/relational-core__contract-free.d.mts.map +1 -1
- package/dist/relational-core__contract-free.mjs +26 -10
- package/dist/relational-core__contract-free.mjs.map +1 -1
- package/dist/relational-core__expression.d.mts +4 -2
- package/dist/relational-core__expression.mjs +2 -2
- package/dist/relational-core__functions.d.mts +2 -0
- package/dist/relational-core__functions.mjs +2 -0
- package/dist/relational-core__index-reference.d.mts +3 -0
- package/dist/relational-core__index-reference.mjs +2 -0
- package/dist/relational-core__middleware.d.mts +2 -2
- package/dist/relational-core__middleware.mjs +1 -1
- package/dist/relational-core__plan.d.mts +4 -4
- package/dist/relational-core__plan.mjs +1 -1
- package/dist/relational-core__query-lane-context.d.mts +3 -3
- package/dist/relational-core__types.d.mts +2 -2
- package/dist/{resolve-storage-table-DZsfnzeW.mjs → resolve-storage-table-BHLQsOza.mjs} +3 -3
- package/dist/resolve-storage-table-BHLQsOza.mjs.map +1 -0
- package/dist/{resolve-storage-table-Nyrgk4h8.d.mts → resolve-storage-table-DCqtmnSU.d.mts} +4 -4
- package/dist/resolve-storage-table-DCqtmnSU.d.mts.map +1 -0
- package/dist/{runtime-tIHpGCcG.mjs → runtime-6tH1yOxK.mjs} +6 -6
- package/dist/runtime-6tH1yOxK.mjs.map +1 -0
- package/dist/{runtime-DpKF2Yu2.d.mts → runtime-CF6rAlE5.d.mts} +7 -8
- package/dist/runtime-CF6rAlE5.d.mts.map +1 -0
- package/dist/runtime.d.mts +5 -5
- package/dist/runtime.mjs +2 -1799
- package/dist/runtime__internal__prepared-query.d.mts +2 -2
- package/dist/runtime__internal__prepared-query.mjs +2 -2
- package/dist/{schema-differ-DpG_gnur-DFAtGezx.d.mts → schema-differ-DmP74bB8-tqHKiJ88.d.mts} +6 -6
- package/dist/schema-differ-DmP74bB8-tqHKiJ88.d.mts.map +1 -0
- package/dist/schema-ir.d.mts +2 -2
- package/dist/schema-ir.mjs +2 -2
- package/dist/schema-ir__naming.d.mts +1 -1
- package/dist/schema-ir__naming.mjs +1 -1
- package/dist/schema-ir__types.d.mts +3 -3
- package/dist/schema-ir__types.mjs +2 -2
- package/dist/{schema-verify-C_PEdlAr-Dxxjm6EM.mjs → schema-verify-C3wbdviw-DMjIubOr.mjs} +50 -8
- package/dist/schema-verify-C3wbdviw-DMjIubOr.mjs.map +1 -0
- package/dist/{serialized-index-CrgG8ct1-DG3JpHc7.d.mts → serialized-index-CrgG8ct1-DKVJ7TDX.d.mts} +4 -4
- package/dist/serialized-index-CrgG8ct1-DKVJ7TDX.d.mts.map +1 -0
- package/dist/{serialized-index-nBTwanKy-_ddEK4tk.mjs → serialized-index-DjCCnXB1-DMA7BtO2.mjs} +4 -4
- package/dist/serialized-index-DjCCnXB1-DMA7BtO2.mjs.map +1 -0
- package/dist/{sql-contract-serializer-2oBWuxTe-DKZTf2n8.mjs → sql-contract-serializer-sMY3prTx-D2BfrNxs.mjs} +41 -16
- package/dist/sql-contract-serializer-sMY3prTx-D2BfrNxs.mjs.map +1 -0
- package/dist/sql-data-type-D7M-YTTY-D2vRqDxn.mjs +400 -0
- package/dist/sql-data-type-D7M-YTTY-D2vRqDxn.mjs.map +1 -0
- package/dist/{sql-execution-plan--jKyLpe5-CMGhgO0M.d.mts → sql-execution-plan-3uxYjHyu-DLknM-Pj.d.mts} +3 -3
- package/dist/sql-execution-plan-3uxYjHyu-DLknM-Pj.d.mts.map +1 -0
- package/dist/sql-expression-B4BrhGPi.d.mts +62 -0
- package/dist/sql-expression-B4BrhGPi.d.mts.map +1 -0
- package/dist/sql-expression-CKgKznXS.mjs +137 -0
- package/dist/sql-expression-CKgKznXS.mjs.map +1 -0
- package/dist/{sql-index-ByAAE7K8-DKj2V86p.d.mts → sql-index-ByAAE7K8-Dqtn_vUL.d.mts} +4 -4
- package/dist/sql-index-ByAAE7K8-Dqtn_vUL.d.mts.map +1 -0
- package/dist/{sql-node-kEz6YFZo-v2F7delx.d.mts → sql-node-kEz6YFZo-1ogT6Cxt.d.mts} +2 -2
- package/dist/sql-node-kEz6YFZo-1ogT6Cxt.d.mts.map +1 -0
- package/dist/{sql-storage-8LqxeLAY-DH4DdUXx.d.mts → sql-storage-DeCAh5Hi-CEM-m8cs.d.mts} +19 -22
- package/dist/sql-storage-DeCAh5Hi-CEM-m8cs.d.mts.map +1 -0
- package/dist/{storage-table-DSeWszOL-CI-cItEK.d.mts → storage-table-Bf1ZjnxO-pUj6pyP0.d.mts} +17 -24
- package/dist/storage-table-Bf1ZjnxO-pUj6pyP0.d.mts.map +1 -0
- package/dist/{storage-table-ZzbR-xzQ-BRoTQE_2.mjs → storage-table-DDvN83mw-DkbBFmK9.mjs} +12 -9
- package/dist/storage-table-DDvN83mw-DkbBFmK9.mjs.map +1 -0
- package/dist/{storage-value-set-BGlj5Uus-CvZD--GG.d.mts → storage-value-set-BGlj5Uus-ny2k-0PA.d.mts} +3 -3
- package/dist/storage-value-set-BGlj5Uus-ny2k-0PA.d.mts.map +1 -0
- package/dist/transaction-state-BfXhs7D7-DhmbySW9.mjs +45 -0
- package/dist/transaction-state-BfXhs7D7-DhmbySW9.mjs.map +1 -0
- package/dist/transaction-state-DR3nKTmH-D29jmVE-.d.mts +115 -0
- package/dist/transaction-state-DR3nKTmH-D29jmVE-.d.mts.map +1 -0
- package/dist/types-B9wSEUsV.d.mts +1 -0
- package/dist/{types-CUsJrvEl.mjs → types-CTdSkl0s.mjs} +13 -6
- package/dist/types-CTdSkl0s.mjs.map +1 -0
- package/dist/types-CkAd8SWm.d.mts +57 -0
- package/dist/types-CkAd8SWm.d.mts.map +1 -0
- package/dist/{types-IqZMLrFC-DcH8v_u3.d.mts → types-Cq6ImLAB-RlU4jJAX.d.mts} +27 -41
- package/dist/types-Cq6ImLAB-RlU4jJAX.d.mts.map +1 -0
- package/dist/{types-YStFFkKs-D9UAmnON.d.mts → types-CuCxAQat-Dz8P4xyt.d.mts} +141 -86
- package/dist/types-CuCxAQat-Dz8P4xyt.d.mts.map +1 -0
- package/dist/types-DfKvtJwS-CrpWTGBJ.d.mts +273 -0
- package/dist/types-DfKvtJwS-CrpWTGBJ.d.mts.map +1 -0
- package/dist/{types-4YenawkP-CwaaZSid.mjs → types-DoyiTsjg-2YMhMJDA.mjs} +181 -80
- package/dist/types-DoyiTsjg-2YMhMJDA.mjs.map +1 -0
- package/dist/{types-v3qxLLxo-DCeKv7yY.d.mts → types-DriMIMNQ-BkbsW7w3.d.mts} +68 -34
- package/dist/types-DriMIMNQ-BkbsW7w3.d.mts.map +1 -0
- package/dist/{types-Dt4e_jC--COeP8xzN.mjs → types-NTQsD2MS-xcLtvKO9.mjs} +83 -17
- package/dist/types-NTQsD2MS-xcLtvKO9.mjs.map +1 -0
- package/dist/{types-CyxGtOOh-BggSa6Fe.d.mts → types-TXO7x1BA-CYYIY8NI.d.mts} +14 -14
- package/dist/types-TXO7x1BA-CYYIY8NI.d.mts.map +1 -0
- package/dist/types-ljUNyiWe.d.mts +1 -0
- package/dist/{unique-constraint-M5TlQnJQ-BiIHJ85k.d.mts → unique-constraint-M5TlQnJQ-qX-maNUV.d.mts} +3 -3
- package/dist/unique-constraint-M5TlQnJQ-qX-maNUV.d.mts.map +1 -0
- package/dist/{util-DQQgv2j1-D6INZaRG.mjs → util-BBgcP3gI-CpL-0VuC.mjs} +12 -3
- package/dist/util-BBgcP3gI-CpL-0VuC.mjs.map +1 -0
- package/dist/{validators-CrWD6bSP.mjs → validators-BwA7ttZM.mjs} +0 -0
- package/dist/validators-BwA7ttZM.mjs.map +1 -0
- package/dist/{validators-Belo8QqM.d.mts → validators-FJs8HvFr.d.mts} +54 -16
- package/dist/validators-FJs8HvFr.d.mts.map +1 -0
- package/dist/{value-set-derivation-hook-BhgGVtw0.d.mts → value-set-derivation-hook-3E437jPU.d.mts} +3 -3
- package/dist/value-set-derivation-hook-3E437jPU.d.mts.map +1 -0
- package/dist/{value-set-derivation-hook-Di9JZ0X7.mjs → value-set-derivation-hook-HVaxhALD.mjs} +2 -2
- package/dist/value-set-derivation-hook-HVaxhALD.mjs.map +1 -0
- package/dist/{verify-u0UTdZgD-DSAaz9rW.mjs → verify-BVk5bjdH-DhKm6hB3.mjs} +8 -7
- package/dist/verify-BVk5bjdH-DhKm6hB3.mjs.map +1 -0
- package/dist/{verify-zDRnw0Rn.d.mts → verify-DHXbuZPB.d.mts} +2 -2
- package/dist/verify-DHXbuZPB.d.mts.map +1 -0
- package/package.json +33 -22
- package/dist/aggregate-descriptor-registry-C7hel5O7.mjs.map +0 -1
- package/dist/ast-HFcfQZr-.d.mts +0 -206
- package/dist/ast-HFcfQZr-.d.mts.map +0 -1
- package/dist/ast-Lbl4VwqP.mjs +0 -325
- package/dist/ast-Lbl4VwqP.mjs.map +0 -1
- package/dist/authored-check-naming-BWBe-PVE.d.mts.map +0 -1
- package/dist/authored-check-naming-Hf9990DV.mjs.map +0 -1
- package/dist/authoring-type-constructors-Blqoa2Ua-DqvNCOb2.mjs +0 -316
- package/dist/authoring-type-constructors-Blqoa2Ua-DqvNCOb2.mjs.map +0 -1
- package/dist/canonicalization-hooks-CYnW_sGn.d.mts.map +0 -1
- package/dist/canonicalization-hooks-dmXkOR4a.mjs.map +0 -1
- package/dist/check-constraint-B7qGu6rc-HvQjjX82.d.mts.map +0 -1
- package/dist/codec-descriptor-registry-N0dhtKaH.mjs.map +0 -1
- package/dist/config-types-D9fdKG3g.d.mts.map +0 -1
- package/dist/config-types-gAm54C_v.mjs.map +0 -1
- package/dist/contract-builder-CG_tGDbB.d.mts +0 -1360
- package/dist/contract-builder-CG_tGDbB.d.mts.map +0 -1
- package/dist/contract-builder-D4a5dT7j.mjs.map +0 -1
- package/dist/contract-errors-BCN6RVI5-5IHBF_lg.mjs.map +0 -1
- package/dist/contract-view-BMR5IWk4.mjs.map +0 -1
- package/dist/contract-view-CPHxoAEX.d.mts.map +0 -1
- package/dist/contract__pack-types.d.mts +0 -2
- package/dist/control-D3cB5YQp.mjs +0 -1267
- package/dist/control-D3cB5YQp.mjs.map +0 -1
- package/dist/control-DTLJ1WtZ.d.mts +0 -945
- package/dist/control-DTLJ1WtZ.d.mts.map +0 -1
- package/dist/control-adapter-C99J7aEF-B7eQd7vD.d.mts.map +0 -1
- package/dist/control-adapter-MZJQiwcE.d.mts +0 -1
- package/dist/db-D18PrXKW-DvTv4JFn.d.mts.map +0 -1
- package/dist/ddl-types-BGdnAbXo-DKPHPUGx.mjs.map +0 -1
- package/dist/ddl-types-CXx4vPz6-BsgrnSUY.d.mts.map +0 -1
- package/dist/derived-checks-DFMRmiAu-DGfB8aS4.mjs +0 -1057
- package/dist/derived-checks-DFMRmiAu-DGfB8aS4.mjs.map +0 -1
- package/dist/derived-checks-DPdD01O0-DGqOFjs6.d.mts.map +0 -1
- package/dist/diff-B16bL_IR.mjs +0 -13
- package/dist/diff-B16bL_IR.mjs.map +0 -1
- package/dist/diff-DbTNVm2f.d.mts.map +0 -1
- package/dist/dist-C_q1_QR9.mjs.map +0 -1
- package/dist/dist-ZK6oeI5e.mjs.map +0 -1
- package/dist/dist-x6jvtkKM.mjs.map +0 -1
- package/dist/driver-types-BQtRkOJ0-Bv2RPSfr.d.mts.map +0 -1
- package/dist/entity-handle-lowering-hook-CIvRKBfa.d.mts.map +0 -1
- package/dist/entity-handle-lowering-hook-DwLIKNiS.mjs +0 -11
- package/dist/entity-handle-lowering-hook-DwLIKNiS.mjs.map +0 -1
- package/dist/entity-kinds-B5cUFU22.d.mts.map +0 -1
- package/dist/entity-kinds-D7vwoN_T-CCBwFJy0.mjs.map +0 -1
- package/dist/errors-B5g0xWro-mWH2C07F.mjs.map +0 -1
- package/dist/expression-CX5mTA-z.mjs.map +0 -1
- package/dist/expression-FJK6Ar-d.d.mts.map +0 -1
- package/dist/factories-P3qBQhO-.d.mts.map +0 -1
- package/dist/factories-SVor5xIT.mjs.map +0 -1
- package/dist/family__pack.d.mts.map +0 -1
- package/dist/family__pack.mjs.map +0 -1
- package/dist/foreign-key-C-iTPuRT-C9ip8zFx.d.mts.map +0 -1
- package/dist/foreign-key-materialization-CkvOzJWy.d.mts +0 -65
- package/dist/foreign-key-materialization-CkvOzJWy.d.mts.map +0 -1
- package/dist/foreign-key-materialization-Cu7-7LHl.mjs +0 -73
- package/dist/foreign-key-materialization-Cu7-7LHl.mjs.map +0 -1
- package/dist/index-C-vQOMY_.d.mts +0 -84
- package/dist/index-C-vQOMY_.d.mts.map +0 -1
- package/dist/index-C5I8D-MB.d.mts.map +0 -1
- package/dist/index-naming-Ct65z5Xk.d.mts.map +0 -1
- package/dist/index-naming-DREt6js7-CRH27yUv.mjs.map +0 -1
- package/dist/index-type-validation-COH4d78M.mjs +0 -17
- package/dist/index-type-validation-COH4d78M.mjs.map +0 -1
- package/dist/index-type-validation-CZ5L2lli.d.mts +0 -10
- package/dist/index-type-validation-CZ5L2lli.d.mts.map +0 -1
- package/dist/index-types-CNhHgKHi.d.mts +0 -1
- package/dist/index-types-DJQ_xwaE.mjs +0 -40
- package/dist/index-types-DJQ_xwaE.mjs.map +0 -1
- package/dist/index-types-DPVhsW0v-D-BikM4h.d.mts +0 -31
- package/dist/index-types-DPVhsW0v-D-BikM4h.d.mts.map +0 -1
- package/dist/interpreter-DyiUUAWh-DYdjIpiE.mjs +0 -3680
- package/dist/interpreter-DyiUUAWh-DYdjIpiE.mjs.map +0 -1
- package/dist/ir-Bghjt-FG.d.mts.map +0 -1
- package/dist/ir-I-xciGUH.mjs.map +0 -1
- package/dist/lane-query-builder.d.mts +0 -236
- package/dist/lane-query-builder.d.mts.map +0 -1
- package/dist/lane-query-builder.mjs +0 -67
- package/dist/lane-query-builder.mjs.map +0 -1
- package/dist/middleware--TtoxUiW.d.mts +0 -1
- package/dist/middleware-AhTz1BOW-th-Suxuf.d.mts.map +0 -1
- package/dist/middleware-CMr4CHNz-DHIUdxlm.mjs.map +0 -1
- package/dist/migration-CK4oePIo.mjs.map +0 -1
- package/dist/migration-CjDRZNep.d.mts.map +0 -1
- package/dist/naming-CV9j2pXl-KARaDReS.d.mts.map +0 -1
- package/dist/naming-DKr4TJd7-Cszhq9RV.mjs.map +0 -1
- package/dist/pack-types-BcLTpwbv.d.mts +0 -14
- package/dist/pack-types-BcLTpwbv.d.mts.map +0 -1
- package/dist/plan-BHLdb0Z7.d.mts +0 -2
- package/dist/plan-CDxduYaN-pLIyyJaB.d.mts.map +0 -1
- package/dist/plan-Dh0LbYIv-Crx5-ThG.mjs.map +0 -1
- package/dist/prepared-query-BJr50wPm-ChoqfJbX.mjs +0 -22
- package/dist/prepared-query-BJr50wPm-ChoqfJbX.mjs.map +0 -1
- package/dist/prepared-statement-9QpkE6JG-C430V3MO.d.mts +0 -529
- package/dist/prepared-statement-9QpkE6JG-C430V3MO.d.mts.map +0 -1
- package/dist/psl-infer-CCAD6FWA.d.mts +0 -119
- package/dist/psl-infer-CCAD6FWA.d.mts.map +0 -1
- package/dist/psl-infer-Dk5kgKre.mjs +0 -362
- package/dist/psl-infer-Dk5kgKre.mjs.map +0 -1
- package/dist/query-lane-context-Qa-eddrE-kuZ8Z86-.d.mts.map +0 -1
- package/dist/query-lane-context-XMvk7fmY.d.mts +0 -1
- package/dist/referential-action-sql-DJ9ps6-S.d.mts.map +0 -1
- package/dist/referential-action-sql-DSqkrySN.mjs.map +0 -1
- package/dist/resolve-storage-table-DZsfnzeW.mjs.map +0 -1
- package/dist/resolve-storage-table-Nyrgk4h8.d.mts.map +0 -1
- package/dist/runtime-DpKF2Yu2.d.mts.map +0 -1
- package/dist/runtime-tIHpGCcG.mjs.map +0 -1
- package/dist/runtime.mjs.map +0 -1
- package/dist/schema-differ-DpG_gnur-DFAtGezx.d.mts.map +0 -1
- package/dist/schema-verify-C_PEdlAr-Dxxjm6EM.mjs.map +0 -1
- package/dist/serialized-index-CrgG8ct1-DG3JpHc7.d.mts.map +0 -1
- package/dist/serialized-index-nBTwanKy-_ddEK4tk.mjs.map +0 -1
- package/dist/sql-contract-serializer-2oBWuxTe-DKZTf2n8.mjs.map +0 -1
- package/dist/sql-execution-plan--jKyLpe5-CMGhgO0M.d.mts.map +0 -1
- package/dist/sql-index-ByAAE7K8-DKj2V86p.d.mts.map +0 -1
- package/dist/sql-node-kEz6YFZo-v2F7delx.d.mts.map +0 -1
- package/dist/sql-storage-8LqxeLAY-DH4DdUXx.d.mts.map +0 -1
- package/dist/storage-table-DSeWszOL-CI-cItEK.d.mts.map +0 -1
- package/dist/storage-table-ZzbR-xzQ-BRoTQE_2.mjs.map +0 -1
- package/dist/storage-value-set-BGlj5Uus-CvZD--GG.d.mts.map +0 -1
- package/dist/timestamp-now-generator-DRXygu32-DN9Qb1uI.mjs +0 -173
- package/dist/timestamp-now-generator-DRXygu32-DN9Qb1uI.mjs.map +0 -1
- package/dist/types-4YenawkP-CwaaZSid.mjs.map +0 -1
- package/dist/types-B0msOnBM.d.mts +0 -1
- package/dist/types-BDUG29Df-LE85dIXP.d.mts +0 -161
- package/dist/types-BDUG29Df-LE85dIXP.d.mts.map +0 -1
- package/dist/types-CUsJrvEl.mjs.map +0 -1
- package/dist/types-CyxGtOOh-BggSa6Fe.d.mts.map +0 -1
- package/dist/types-Dt4e_jC--COeP8xzN.mjs.map +0 -1
- package/dist/types-Dwcw0F8X.d.mts +0 -1
- package/dist/types-IqZMLrFC-DcH8v_u3.d.mts.map +0 -1
- package/dist/types-UcHa2cTe.d.mts +0 -17
- package/dist/types-UcHa2cTe.d.mts.map +0 -1
- package/dist/types-YStFFkKs-D9UAmnON.d.mts.map +0 -1
- package/dist/types-v3qxLLxo-DCeKv7yY.d.mts.map +0 -1
- package/dist/unique-constraint-M5TlQnJQ-BiIHJ85k.d.mts.map +0 -1
- package/dist/util-DQQgv2j1-D6INZaRG.mjs.map +0 -1
- package/dist/validators-Belo8QqM.d.mts.map +0 -1
- package/dist/validators-CrWD6bSP.mjs.map +0 -1
- package/dist/value-set-derivation-hook-BhgGVtw0.d.mts.map +0 -1
- package/dist/value-set-derivation-hook-Di9JZ0X7.mjs.map +0 -1
- package/dist/verify-u0UTdZgD-DSAaz9rW.mjs.map +0 -1
- package/dist/verify-zDRnw0Rn.d.mts.map +0 -1
- /package/dist/{contract__pack-types.mjs → relational-core__collection-methods.mjs} +0 -0
|
@@ -0,0 +1,2721 @@
|
|
|
1
|
+
import { u as namingOf } from "./naming-bM5qm5PY-jOg3JFMb.mjs";
|
|
2
|
+
import { t as sqlContractCanonicalizationHooks } from "./canonicalization-hooks-Ce1v_TDl.mjs";
|
|
3
|
+
import { _ as unquotedSqlBaseName, a as renderSqlTypeName, c as sameSqlColumnType, m as sqlStorageType, n as isSqlDataType, p as sqlDataTypeOfCodec, r as nameIsCatalogText, u as sqlCodecColumnLength, y as validateSqlTypeParams } from "./sql-data-type-D7M-YTTY-D2vRqDxn.mjs";
|
|
4
|
+
import { c as StorageTable } from "./storage-table-DDvN83mw-DkbBFmK9.mjs";
|
|
5
|
+
import { c as SqlSchemaIR, n as RelationalSchemaNodeKind, u as SqlTableIR } from "./types-DoyiTsjg-2YMhMJDA.mjs";
|
|
6
|
+
import { f as sqlExpressionRegistration, m as sqlTextsReadBack, o as printSqlExpressionLiteral } from "./sql-expression-CKgKznXS.mjs";
|
|
7
|
+
import { d as nonEmptyTypeParams, u as isStorageTypeInstance } from "./types-CTdSkl0s.mjs";
|
|
8
|
+
import { g as valueObjectStorageTypeMissingMessage } from "./validators-BwA7ttZM.mjs";
|
|
9
|
+
import { a as describeUnsupportedSqlAttribute, b as sqlAttributeSpecs } from "./psl-field-resolution-C5KQ7kzP-B0NwuRWV.mjs";
|
|
10
|
+
import { t as describeUnresolvedSqlType } from "./diagnostics-RHJxoiIk.mjs";
|
|
11
|
+
import { t as sqlEmission } from "./dist-C8Y-PPuE.mjs";
|
|
12
|
+
import { a as sqlFamilyGeneratorDescriptors, i as sqlFamilyEntityTypes, n as sqlFamilyAuthoringFieldPresets, o as sqlFamilyGeneratorFunctions, r as sqlFamilyAuthoringTypes, s as sqlFamilyPslBlockDescriptors } from "./generator-functions-D_qdqBV--Bq296cED.mjs";
|
|
13
|
+
import { c as verifySqlSchemaByDiff, n as classifyDiffSubjectGranularity, o as enforceSqlDataTypeInvariants, t as classifyDiffEntityKind } from "./schema-verify-C3wbdviw-DMjIubOr.mjs";
|
|
14
|
+
import { t as sqlFamilyError } from "./errors-B5g0xWro-C8plbqga.mjs";
|
|
15
|
+
import { t as SqlContractSerializer } from "./sql-contract-serializer-sMY3prTx-D2BfrNxs.mjs";
|
|
16
|
+
import { t as collectSupportedCodecTypeIds } from "./verify-BVk5bjdH-DhKm6hB3.mjs";
|
|
17
|
+
import { isStructuredError, structuredError } from "@prisma/orm-framework/utils/structured-error";
|
|
18
|
+
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
19
|
+
import { collectScalarTypeConstructors, tsTaggedTemplateSource } from "@prisma/orm-framework/components/authoring";
|
|
20
|
+
import { ContractValidationError } from "@prisma/orm-framework/contract/contract-validation-error";
|
|
21
|
+
import { UNBOUND_NAMESPACE_ID, isPlainRecord } from "@prisma/orm-framework/components/ir";
|
|
22
|
+
import { assembleDataTypes, codecForRef, dataTypeParams, objectSchemaKeys } from "@prisma/orm-framework/components/codec";
|
|
23
|
+
import { canonicalizeJson } from "@prisma/orm-framework/components/utils";
|
|
24
|
+
import { blindCast } from "@prisma/orm-framework/utils/casts";
|
|
25
|
+
import { InternalError, isInternalError } from "@prisma/orm-framework/utils/internal-error";
|
|
26
|
+
import { type } from "arktype";
|
|
27
|
+
import { asNamespaceId, governingControlPolicy } from "@prisma/orm-framework/contract/types";
|
|
28
|
+
import { notOk, ok } from "@prisma/orm-framework/utils/result";
|
|
29
|
+
import { APP_SPACE_ID, SchemaTreeNode, VERIFY_CODE_HASH_MISMATCH, VERIFY_CODE_MARKER_MISSING, VERIFY_CODE_TARGET_MISMATCH, assembleAuthoringContributions as assembleAuthoringContributions$1, describeMigrationStatement, extractCodecLookup, fieldStatementText, issueOutcome, migrationRunnerFailure, modelDisplayName, sameMarkerHashes } from "@prisma/orm-framework/components/control";
|
|
30
|
+
import { assertDescriptorSelfConsistency } from "@prisma/orm-toolchain/migration-tools/spaces";
|
|
31
|
+
import { jsonToTsSource } from "@prisma/orm-framework/ts-render";
|
|
32
|
+
//#region ../../../2-sql-family/9-family/dist/control.mjs
|
|
33
|
+
function tablesOf(storage) {
|
|
34
|
+
return Object.entries(storage.namespaces).flatMap(([namespaceId, namespace]) => Object.entries(namespace.entries["table"] ?? {}).map(([tableName, table]) => ({
|
|
35
|
+
path: `storage.namespaces.${namespaceId}.entries.table.${tableName}`,
|
|
36
|
+
namespaceId,
|
|
37
|
+
tableName,
|
|
38
|
+
table: blindCast(table)
|
|
39
|
+
})));
|
|
40
|
+
}
|
|
41
|
+
function dataTypeProblems(storage, codecLookup) {
|
|
42
|
+
return [...tablesOf(storage).flatMap(({ path, table }) => Object.entries(table.columns).map(([columnName, column]) => ({
|
|
43
|
+
path: `${path}.columns.${columnName}`,
|
|
44
|
+
codecId: column.codecId,
|
|
45
|
+
dataType: column.dataType
|
|
46
|
+
}))), ...Object.entries(storage.types ?? {}).flatMap(([typeName, entry]) => isStorageTypeInstance(entry) ? [{
|
|
47
|
+
path: `storage.types.${typeName}`,
|
|
48
|
+
codecId: entry.codecId,
|
|
49
|
+
dataType: entry.dataType
|
|
50
|
+
}] : [])].flatMap(({ path, codecId, dataType }) => {
|
|
51
|
+
const represented = codecLookup.descriptorFor(codecId)?.dataType;
|
|
52
|
+
if (represented === void 0 || represented === dataType) return [];
|
|
53
|
+
return [`${path}: codec ${codecId} represents ${represented}, not ${dataType}`];
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Whether `type` refuses `params`. The check of the column's parameters reports that, so its default is not read under them. */
|
|
57
|
+
function refusesParams(type, params) {
|
|
58
|
+
if (!isSqlDataType(type)) return false;
|
|
59
|
+
try {
|
|
60
|
+
validateSqlTypeParams(type, params);
|
|
61
|
+
return false;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (isStructuredError(error) && error.code === "CONTRACT.TYPE_PARAMS_INVALID") return true;
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Names the namespace, unless the contract has only the unbound one, as a SQLite contract does. */
|
|
68
|
+
function columnPlace(refused) {
|
|
69
|
+
return refused.namespaceId === UNBOUND_NAMESPACE_ID ? `Table "${refused.table}", column "${refused.column}"` : `Namespace "${refused.namespaceId}", table "${refused.table}", column "${refused.column}"`;
|
|
70
|
+
}
|
|
71
|
+
function notAValue(refused) {
|
|
72
|
+
return {
|
|
73
|
+
refused,
|
|
74
|
+
text: `${columnPlace(refused)}: the default ${JSON.stringify(refused.value)}${refused.elementIndex === void 0 ? "" : ` at element ${refused.elementIndex + 1}`} is not a value of ${refused.dataType}: ${refused.message}`
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function notAList(refused) {
|
|
78
|
+
return {
|
|
79
|
+
refused,
|
|
80
|
+
text: `${columnPlace(refused)}: the default ${JSON.stringify(refused.value)} is not a list, and the column holds a list of ${refused.dataType}`
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Each literal default the column's type does not hold, under the column's parameters, element by element on a list column. A `null` is SQL NULL, which every column takes. */
|
|
84
|
+
function refusedDefaults(storage, dataTypes) {
|
|
85
|
+
return tablesOf(storage).flatMap(({ namespaceId, tableName, table }) => Object.entries(table.columns).flatMap(([columnName, column]) => {
|
|
86
|
+
const columnDefault = column.default;
|
|
87
|
+
if (columnDefault?.kind !== "literal") return [];
|
|
88
|
+
const type = dataTypes.get(column.dataType);
|
|
89
|
+
if (type === void 0) return [];
|
|
90
|
+
const params = dataTypeParams(type, nonEmptyTypeParams(column.typeParams));
|
|
91
|
+
if (refusesParams(type, params)) return [];
|
|
92
|
+
const place = {
|
|
93
|
+
namespaceId,
|
|
94
|
+
table: tableName,
|
|
95
|
+
column: columnName,
|
|
96
|
+
dataType: type.id
|
|
97
|
+
};
|
|
98
|
+
const isList = column.many !== void 0 && column.many !== false;
|
|
99
|
+
if (isList && columnDefault.value !== null && !Array.isArray(columnDefault.value)) return [notAList({
|
|
100
|
+
...place,
|
|
101
|
+
value: columnDefault.value,
|
|
102
|
+
message: "The column holds a list, and the default is not one.",
|
|
103
|
+
reason: "type-refused-default"
|
|
104
|
+
})];
|
|
105
|
+
const listed = isList && Array.isArray(columnDefault.value);
|
|
106
|
+
const values = isList && Array.isArray(columnDefault.value) ? columnDefault.value : [columnDefault.value];
|
|
107
|
+
for (const [index, value] of values.entries()) {
|
|
108
|
+
if (value === null) continue;
|
|
109
|
+
try {
|
|
110
|
+
type.fromContract(value, params);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (isInternalError(error)) throw error;
|
|
113
|
+
return [notAValue({
|
|
114
|
+
...place,
|
|
115
|
+
value,
|
|
116
|
+
...listed ? { elementIndex: index } : {},
|
|
117
|
+
message: error instanceof Error ? error.message : String(error),
|
|
118
|
+
reason: "type-refused-default"
|
|
119
|
+
})];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return [];
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
function valueObjectProblems(contract, authoring) {
|
|
126
|
+
const storageTypeName = authoring.valueObjectStorageType;
|
|
127
|
+
const expectedCodecId = storageTypeName === void 0 ? void 0 : collectScalarTypeConstructors(authoring.type).get(storageTypeName)?.codecId;
|
|
128
|
+
const tables = new Map(tablesOf(contract.storage).map((entry) => [`${entry.namespaceId}\0${entry.tableName}`, entry]));
|
|
129
|
+
return Object.values(contract.domain.namespaces).flatMap((namespace) => Object.values(namespace.models).flatMap((model) => {
|
|
130
|
+
const storage = blindCast(model.storage);
|
|
131
|
+
const table = tables.get(`${storage.namespaceId}\0${storage.table}`);
|
|
132
|
+
if (table === void 0) return [];
|
|
133
|
+
return Object.entries(model.fields).flatMap(([fieldName, field]) => {
|
|
134
|
+
if (field.type.kind !== "valueObject") return [];
|
|
135
|
+
const columnName = storage.fields[fieldName]?.column;
|
|
136
|
+
const column = columnName === void 0 ? void 0 : table.table.columns[columnName];
|
|
137
|
+
if (columnName === void 0 || column === void 0) return [];
|
|
138
|
+
const path = `${table.path}.columns.${columnName}`;
|
|
139
|
+
if (expectedCodecId === void 0) return [valueObjectStorageTypeMissingMessage(path)];
|
|
140
|
+
if (column.codecId === expectedCodecId) return [];
|
|
141
|
+
return [`${path}: a value-object column uses codec ${expectedCodecId}, the codec of the stack's value-object storage type ${storageTypeName}, not ${column.codecId}`];
|
|
142
|
+
});
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Checks a deserialized contract against the stack that loads it: each column's codec represents the data type the column names, each value-object column uses the codec of the stack's value-object storage type, and each literal default is a value of its column's type under the column's parameters.
|
|
147
|
+
*/
|
|
148
|
+
function assertContractMatchesStack(contract, stack) {
|
|
149
|
+
const defaults = refusedDefaults(contract.storage, stack.dataTypes.lookup);
|
|
150
|
+
const problems = [
|
|
151
|
+
...dataTypeProblems(contract.storage, stack.codecLookup),
|
|
152
|
+
...valueObjectProblems(contract, stack.authoringContributions),
|
|
153
|
+
...defaults.map(({ text }) => text)
|
|
154
|
+
];
|
|
155
|
+
if (problems.length > 0) throw new ContractValidationError(problems.join("; "), "storage", defaults.length > 0 ? { refusedDefaults: defaults.map(({ refused }) => refused) } : void 0);
|
|
156
|
+
}
|
|
157
|
+
function isStorageDetails(details) {
|
|
158
|
+
if (typeof details !== "object" || details === null) return false;
|
|
159
|
+
const [objectType, name, schema, table] = [
|
|
160
|
+
"objectType",
|
|
161
|
+
"name",
|
|
162
|
+
"schema",
|
|
163
|
+
"table"
|
|
164
|
+
].map((key) => Reflect.get(details, key));
|
|
165
|
+
return typeof objectType === "string" && typeof name === "string" && (schema === void 0 || typeof schema === "string") && (table === void 0 || typeof table === "string");
|
|
166
|
+
}
|
|
167
|
+
function storageDetailsOf(operation) {
|
|
168
|
+
const target = Reflect.get(operation, "target");
|
|
169
|
+
if (typeof target !== "object" || target === null) return void 0;
|
|
170
|
+
const details = Reflect.get(target, "details");
|
|
171
|
+
return isStorageDetails(details) ? details : void 0;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The name the database knows an operation's object by, read from the operation's target
|
|
175
|
+
* details: `schema.table.column` for a column, `schema.name` for anything else, without the
|
|
176
|
+
* schema in the unbound namespace. An operation without details is named by its id.
|
|
177
|
+
*/
|
|
178
|
+
function storageNameOfOperation(operation) {
|
|
179
|
+
const details = storageDetailsOf(operation);
|
|
180
|
+
if (details === void 0) return operation.id;
|
|
181
|
+
const schema = details.schema === void 0 || details.schema === UNBOUND_NAMESPACE_ID ? [] : [details.schema];
|
|
182
|
+
const table = details.objectType === "column" && details.table !== void 0 ? [details.table] : [];
|
|
183
|
+
return [
|
|
184
|
+
...schema,
|
|
185
|
+
...table,
|
|
186
|
+
details.name
|
|
187
|
+
].join(".");
|
|
188
|
+
}
|
|
189
|
+
function isDdlStatement(sqlStatement) {
|
|
190
|
+
const trimmed = sqlStatement.trim().toLowerCase();
|
|
191
|
+
return trimmed.startsWith("create ") || trimmed.startsWith("alter ") || trimmed.startsWith("drop ");
|
|
192
|
+
}
|
|
193
|
+
function hasExecuteSteps(operation) {
|
|
194
|
+
const execute = Reflect.get(operation, "execute");
|
|
195
|
+
if (!Array.isArray(execute)) return false;
|
|
196
|
+
return execute.every((step) => typeof step === "object" && step !== null && "sql" in step);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Extracts a best-effort SQL DDL preview for CLI plan output.
|
|
200
|
+
* Presentation-only: never used to decide migration correctness.
|
|
201
|
+
*/
|
|
202
|
+
function extractSqlDdl(operations) {
|
|
203
|
+
const statements = [];
|
|
204
|
+
for (const operation of operations) {
|
|
205
|
+
if (!hasExecuteSteps(operation)) continue;
|
|
206
|
+
for (const step of operation.execute) if (typeof step.sql === "string" && isDdlStatement(step.sql)) statements.push(step.sql.trim());
|
|
207
|
+
}
|
|
208
|
+
return statements;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Wraps `extractSqlDdl` into the family-agnostic `OperationPreview` shape.
|
|
212
|
+
* Each statement carries `language: 'sql'`.
|
|
213
|
+
*/
|
|
214
|
+
function sqlOperationsToPreview(operations) {
|
|
215
|
+
return { statements: extractSqlDdl(operations).map((text) => ({
|
|
216
|
+
text,
|
|
217
|
+
language: "sql"
|
|
218
|
+
})) };
|
|
219
|
+
}
|
|
220
|
+
function missingDescriptorOperationError(targetId, operation) {
|
|
221
|
+
return sqlFamilyError("CONTRACT.PACK_CONTRIBUTION_INVALID", `SQL target "${targetId}" is missing the required ${operation} descriptor operation`, {
|
|
222
|
+
why: `The target descriptor does not contribute the ${operation} operation the SQL family requires for this call.`,
|
|
223
|
+
fix: `Use a target package whose control descriptor implements ${operation}.`,
|
|
224
|
+
meta: {
|
|
225
|
+
targetId,
|
|
226
|
+
operation
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
function extractCodecTypeIdsFromContract(contract) {
|
|
231
|
+
const typeIds = /* @__PURE__ */ new Set();
|
|
232
|
+
if (typeof contract === "object" && contract !== null && "storage" in contract && typeof contract.storage === "object" && contract.storage !== null && "namespaces" in contract.storage && typeof contract.storage.namespaces === "object" && contract.storage.namespaces !== null) {
|
|
233
|
+
const namespaces = blindCast(contract.storage.namespaces);
|
|
234
|
+
for (const ns of Object.values(namespaces)) {
|
|
235
|
+
const tbls = ns.entries["table"];
|
|
236
|
+
if (typeof tbls !== "object" || tbls === null) continue;
|
|
237
|
+
for (const table of Object.values(tbls)) if (typeof table === "object" && table !== null && "columns" in table && typeof table.columns === "object" && table.columns !== null) {
|
|
238
|
+
const columns = blindCast(table.columns);
|
|
239
|
+
for (const column of Object.values(columns)) if (column && typeof column === "object" && "codecId" in column && typeof column.codecId === "string") typeIds.add(column.codecId);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return Array.from(typeIds).sort();
|
|
244
|
+
}
|
|
245
|
+
function createVerifyResult(options) {
|
|
246
|
+
const contract = { storageHash: options.contractStorageHash };
|
|
247
|
+
if (options.contractProfileHash) contract.profileHash = options.contractProfileHash;
|
|
248
|
+
const target = { expected: options.expectedTargetId };
|
|
249
|
+
if (options.actualTargetId) target.actual = options.actualTargetId;
|
|
250
|
+
const meta = { contractPath: options.contractPath };
|
|
251
|
+
if (options.configPath) meta.configPath = options.configPath;
|
|
252
|
+
return {
|
|
253
|
+
ok: options.ok,
|
|
254
|
+
summary: options.summary,
|
|
255
|
+
contract,
|
|
256
|
+
target,
|
|
257
|
+
meta,
|
|
258
|
+
timings: { total: options.totalTime },
|
|
259
|
+
...ifDefined("code", options.code),
|
|
260
|
+
...options.marker ? { marker: {
|
|
261
|
+
storageHash: options.marker.storageHash,
|
|
262
|
+
profileHash: options.marker.profileHash
|
|
263
|
+
} } : {},
|
|
264
|
+
...ifDefined("missingCodecs", options.missingCodecs),
|
|
265
|
+
...ifDefined("codecCoverageSkipped", options.codecCoverageSkipped)
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Builds a map from each extension id to the set of extension ids it
|
|
270
|
+
* transitively depends on. Uses the same declared-dependency data that
|
|
271
|
+
* `buildExtensionLoadOrder` in control-stack uses.
|
|
272
|
+
*/
|
|
273
|
+
function buildTransitiveDependsOnMap(extensions) {
|
|
274
|
+
const directDeps = /* @__PURE__ */ new Map();
|
|
275
|
+
for (const ext of extensions) {
|
|
276
|
+
const packs = ext.contractSpace?.contractJson?.extensions;
|
|
277
|
+
const deps = packs !== null && typeof packs === "object" ? Object.keys(packs) : [];
|
|
278
|
+
directDeps.set(ext.id, deps);
|
|
279
|
+
}
|
|
280
|
+
const result = /* @__PURE__ */ new Map();
|
|
281
|
+
const resolve = (id, visiting) => {
|
|
282
|
+
const cached = result.get(id);
|
|
283
|
+
if (cached !== void 0) return cached;
|
|
284
|
+
const set = /* @__PURE__ */ new Set();
|
|
285
|
+
result.set(id, set);
|
|
286
|
+
for (const depId of directDeps.get(id) ?? []) {
|
|
287
|
+
set.add(depId);
|
|
288
|
+
if (!visiting.has(depId)) {
|
|
289
|
+
visiting.add(depId);
|
|
290
|
+
for (const transitive of resolve(depId, visiting)) set.add(transitive);
|
|
291
|
+
visiting.delete(depId);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return set;
|
|
295
|
+
};
|
|
296
|
+
for (const ext of extensions) resolve(ext.id, /* @__PURE__ */ new Set([ext.id]));
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Asserts that no cross-space FK in any extension points against the
|
|
301
|
+
* dependency direction.
|
|
302
|
+
*
|
|
303
|
+
* A cross-space FK (target.spaceId present) from extension A pointing at
|
|
304
|
+
* space B is a violation when B depends on A (directly or transitively),
|
|
305
|
+
* because that means A is pointing "upward" against the dependency arrows
|
|
306
|
+
* established by the extension load order.
|
|
307
|
+
*
|
|
308
|
+
* Throws with a diagnostic naming the violating extension (source), the
|
|
309
|
+
* target space, and the direction violation.
|
|
310
|
+
*/
|
|
311
|
+
function isObjectRecord(v) {
|
|
312
|
+
return typeof v === "object" && v !== null;
|
|
313
|
+
}
|
|
314
|
+
function assertNoCrossSpaceFkReverseReferences(extensions) {
|
|
315
|
+
const dependsOnMap = buildTransitiveDependsOnMap(extensions);
|
|
316
|
+
for (const ext of extensions) {
|
|
317
|
+
const namespaces = ext.contractSpace?.contractJson?.storage?.namespaces;
|
|
318
|
+
if (!isObjectRecord(namespaces)) continue;
|
|
319
|
+
for (const ns of Object.values(namespaces)) {
|
|
320
|
+
if (!isObjectRecord(ns)) continue;
|
|
321
|
+
const entries = ns["entries"];
|
|
322
|
+
if (!isObjectRecord(entries)) continue;
|
|
323
|
+
for (const slot of Object.values(entries)) {
|
|
324
|
+
if (!isObjectRecord(slot)) continue;
|
|
325
|
+
for (const table of Object.values(slot)) {
|
|
326
|
+
if (!isObjectRecord(table)) continue;
|
|
327
|
+
const foreignKeys = table["foreignKeys"];
|
|
328
|
+
if (!Array.isArray(foreignKeys)) continue;
|
|
329
|
+
for (const fk of foreignKeys) {
|
|
330
|
+
if (!isObjectRecord(fk)) continue;
|
|
331
|
+
const target = fk["target"];
|
|
332
|
+
if (!isObjectRecord(target)) continue;
|
|
333
|
+
if (target["spaceId"] === void 0) continue;
|
|
334
|
+
const targetSpaceId = target["spaceId"];
|
|
335
|
+
if (typeof targetSpaceId !== "string") continue;
|
|
336
|
+
if (dependsOnMap.get(targetSpaceId)?.has(ext.id)) throw sqlFamilyError("CONTRACT.FOREIGN_KEY_INVALID", `Cross-space FK reverse-reference detected: extension "${ext.id}" has a cross-space FK targeting space "${targetSpaceId}", but "${targetSpaceId}" depends on "${ext.id}". Cross-space FKs must follow the dependency direction (a space can only reference spaces it depends on, not spaces that depend on it).`, {
|
|
337
|
+
why: "The foreign key points against the contract-space dependency direction.",
|
|
338
|
+
fix: "Move the foreign key to the depending space, or restructure the extension dependencies so the referencing space depends on the referenced one.",
|
|
339
|
+
meta: {
|
|
340
|
+
extensionId: ext.id,
|
|
341
|
+
targetSpaceId
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function createSqlFamilyInstance(stack) {
|
|
351
|
+
enforceSqlDataTypeInvariants(stack.declaredDataTypes, stack.codecDescriptors);
|
|
352
|
+
if (!stack.adapter) throw new InternalError("SQL family requires an adapter descriptor in ControlStack");
|
|
353
|
+
const target = blindCast(stack.target);
|
|
354
|
+
const adapter = blindCast(stack.adapter);
|
|
355
|
+
const extensions = blindCast(stack.extensions);
|
|
356
|
+
for (const extension of extensions) if (extension.contractSpace) {
|
|
357
|
+
const { contractJson, headRef } = extension.contractSpace;
|
|
358
|
+
assertDescriptorSelfConsistency({
|
|
359
|
+
extensionId: extension.id,
|
|
360
|
+
target: contractJson.target,
|
|
361
|
+
targetFamily: contractJson.targetFamily,
|
|
362
|
+
storage: contractJson.storage,
|
|
363
|
+
headRefHash: headRef.hash,
|
|
364
|
+
...sqlContractCanonicalizationHooks
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
assertNoCrossSpaceFkReverseReferences(extensions);
|
|
368
|
+
const { codecTypeImports, extensionIds } = stack;
|
|
369
|
+
let controlAdapter;
|
|
370
|
+
const getControlAdapter = () => controlAdapter ??= adapter.create(stack);
|
|
371
|
+
const targetSerializer = blindCast(target).contractSerializer;
|
|
372
|
+
const targetInferPslContract = blindCast(target).inferPslContract;
|
|
373
|
+
const pslBuildContext = {
|
|
374
|
+
authoringContributions: stack.authoringContributions,
|
|
375
|
+
codecLookup: stack.codecLookup,
|
|
376
|
+
dataTypes: stack.dataTypes,
|
|
377
|
+
generatorFunctions: stack.generatorFunctions,
|
|
378
|
+
componentIdsByGenerator: stack.controlMutationDefaults.componentIdsByGenerator
|
|
379
|
+
};
|
|
380
|
+
const targetBuildPslContract = blindCast(target).buildPslContract;
|
|
381
|
+
const diffSchema = blindCast(target).diffSchema;
|
|
382
|
+
const targetGranularityOf = blindCast(target).classifySubjectGranularity;
|
|
383
|
+
const targetEntityKindOf = blindCast(target).classifyEntityKind;
|
|
384
|
+
const describedContracts = extensions.flatMap((extension) => extension.contractSpace ? [{
|
|
385
|
+
spaceId: extension.id,
|
|
386
|
+
contract: extension.contractSpace.contractJson
|
|
387
|
+
}] : []);
|
|
388
|
+
const loadContract = (contractOrJson) => {
|
|
389
|
+
const serializer = targetSerializer ?? new SqlContractSerializer();
|
|
390
|
+
const json = targetSerializer !== void 0 && !isPlainRecord(contractOrJson) ? targetSerializer.serializeContract(blindCast(contractOrJson)) : contractOrJson;
|
|
391
|
+
const contract = serializer.deserializeContract(json);
|
|
392
|
+
assertContractMatchesStack(contract, stack);
|
|
393
|
+
return contract;
|
|
394
|
+
};
|
|
395
|
+
const signSpaceMarker = async (controlAdapter, driver, { space, contract, expected }) => {
|
|
396
|
+
const storageHash = contract.storage.storageHash;
|
|
397
|
+
const profileHash = "profileHash" in contract && typeof contract.profileHash === "string" ? contract.profileHash : storageHash;
|
|
398
|
+
const signed = {
|
|
399
|
+
space,
|
|
400
|
+
contract: {
|
|
401
|
+
storageHash,
|
|
402
|
+
profileHash
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
const readHashes = async () => {
|
|
406
|
+
const marker = await controlAdapter.readMarker(driver, space);
|
|
407
|
+
return marker === null ? null : {
|
|
408
|
+
storageHash: marker.storageHash,
|
|
409
|
+
profileHash: marker.profileHash
|
|
410
|
+
};
|
|
411
|
+
};
|
|
412
|
+
const existing = await readHashes();
|
|
413
|
+
if (!sameMarkerHashes(existing, expected)) return {
|
|
414
|
+
...signed,
|
|
415
|
+
status: "conflict",
|
|
416
|
+
expected,
|
|
417
|
+
found: existing
|
|
418
|
+
};
|
|
419
|
+
if (existing === null) {
|
|
420
|
+
await controlAdapter.insertMarker(driver, space, {
|
|
421
|
+
storageHash,
|
|
422
|
+
profileHash
|
|
423
|
+
});
|
|
424
|
+
return {
|
|
425
|
+
...signed,
|
|
426
|
+
status: "created"
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
if (existing.storageHash === storageHash && existing.profileHash === profileHash) return {
|
|
430
|
+
...signed,
|
|
431
|
+
status: "unchanged"
|
|
432
|
+
};
|
|
433
|
+
if (!await controlAdapter.updateMarker(driver, space, existing.storageHash, {
|
|
434
|
+
storageHash,
|
|
435
|
+
profileHash
|
|
436
|
+
})) return {
|
|
437
|
+
...signed,
|
|
438
|
+
status: "conflict",
|
|
439
|
+
expected,
|
|
440
|
+
found: await readHashes()
|
|
441
|
+
};
|
|
442
|
+
return {
|
|
443
|
+
...signed,
|
|
444
|
+
status: "updated",
|
|
445
|
+
previous: existing
|
|
446
|
+
};
|
|
447
|
+
};
|
|
448
|
+
const signSpaces = async (driver, spaces) => {
|
|
449
|
+
const [first] = spaces;
|
|
450
|
+
if (first === void 0) return [];
|
|
451
|
+
const controlAdapter = getControlAdapter();
|
|
452
|
+
return controlAdapter.withTransaction(driver, async () => {
|
|
453
|
+
await controlAdapter.lockMarker(driver);
|
|
454
|
+
for (const query of controlAdapter.bootstrapSignMarkerQueries()) {
|
|
455
|
+
const lowered = await controlAdapter.lowerToExecuteRequest(query, { contract: first.contract });
|
|
456
|
+
await driver.query(lowered.sql, lowered.params);
|
|
457
|
+
}
|
|
458
|
+
const signatures = [];
|
|
459
|
+
for (const space of spaces) signatures.push(await signSpaceMarker(controlAdapter, driver, space));
|
|
460
|
+
return signatures;
|
|
461
|
+
});
|
|
462
|
+
};
|
|
463
|
+
return {
|
|
464
|
+
familyId: "sql",
|
|
465
|
+
codecTypeImports,
|
|
466
|
+
extensionIds,
|
|
467
|
+
deserializeContract(contractJson) {
|
|
468
|
+
return loadContract(contractJson);
|
|
469
|
+
},
|
|
470
|
+
async verify(verifyOptions) {
|
|
471
|
+
const { driver, contract: rawContract, expectedTargetId, contractPath, configPath } = verifyOptions;
|
|
472
|
+
const startTime = Date.now();
|
|
473
|
+
const contract = loadContract(rawContract);
|
|
474
|
+
const contractStorageHash = contract.storage.storageHash;
|
|
475
|
+
const contractProfileHash = contract.profileHash;
|
|
476
|
+
const contractTarget = contract.target;
|
|
477
|
+
const marker = await getControlAdapter().readMarker(driver, APP_SPACE_ID);
|
|
478
|
+
let missingCodecs;
|
|
479
|
+
let codecCoverageSkipped = false;
|
|
480
|
+
const supportedTypeIds = collectSupportedCodecTypeIds([
|
|
481
|
+
adapter,
|
|
482
|
+
target,
|
|
483
|
+
...extensions
|
|
484
|
+
]);
|
|
485
|
+
if (supportedTypeIds.length === 0) codecCoverageSkipped = true;
|
|
486
|
+
else {
|
|
487
|
+
const supportedSet = new Set(supportedTypeIds);
|
|
488
|
+
const missing = extractCodecTypeIdsFromContract(contract).filter((id) => !supportedSet.has(id));
|
|
489
|
+
if (missing.length > 0) missingCodecs = missing;
|
|
490
|
+
}
|
|
491
|
+
if (!marker) return createVerifyResult({
|
|
492
|
+
ok: false,
|
|
493
|
+
code: VERIFY_CODE_MARKER_MISSING,
|
|
494
|
+
summary: "Marker missing",
|
|
495
|
+
contractStorageHash,
|
|
496
|
+
expectedTargetId,
|
|
497
|
+
contractPath,
|
|
498
|
+
totalTime: Date.now() - startTime,
|
|
499
|
+
...contractProfileHash ? { contractProfileHash } : {},
|
|
500
|
+
...missingCodecs ? { missingCodecs } : {},
|
|
501
|
+
...codecCoverageSkipped ? { codecCoverageSkipped } : {},
|
|
502
|
+
...configPath ? { configPath } : {}
|
|
503
|
+
});
|
|
504
|
+
if (contractTarget !== expectedTargetId) return createVerifyResult({
|
|
505
|
+
ok: false,
|
|
506
|
+
code: VERIFY_CODE_TARGET_MISMATCH,
|
|
507
|
+
summary: "Target mismatch",
|
|
508
|
+
contractStorageHash,
|
|
509
|
+
marker,
|
|
510
|
+
expectedTargetId,
|
|
511
|
+
actualTargetId: contractTarget,
|
|
512
|
+
contractPath,
|
|
513
|
+
totalTime: Date.now() - startTime,
|
|
514
|
+
...contractProfileHash ? { contractProfileHash } : {},
|
|
515
|
+
...missingCodecs ? { missingCodecs } : {},
|
|
516
|
+
...codecCoverageSkipped ? { codecCoverageSkipped } : {},
|
|
517
|
+
...configPath ? { configPath } : {}
|
|
518
|
+
});
|
|
519
|
+
if (marker.storageHash !== contractStorageHash) return createVerifyResult({
|
|
520
|
+
ok: false,
|
|
521
|
+
code: VERIFY_CODE_HASH_MISMATCH,
|
|
522
|
+
summary: "Hash mismatch",
|
|
523
|
+
contractStorageHash,
|
|
524
|
+
marker,
|
|
525
|
+
expectedTargetId,
|
|
526
|
+
contractPath,
|
|
527
|
+
totalTime: Date.now() - startTime,
|
|
528
|
+
...contractProfileHash ? { contractProfileHash } : {},
|
|
529
|
+
...missingCodecs ? { missingCodecs } : {},
|
|
530
|
+
...codecCoverageSkipped ? { codecCoverageSkipped } : {},
|
|
531
|
+
...configPath ? { configPath } : {}
|
|
532
|
+
});
|
|
533
|
+
if (contractProfileHash && marker.profileHash !== contractProfileHash) return createVerifyResult({
|
|
534
|
+
ok: false,
|
|
535
|
+
code: VERIFY_CODE_HASH_MISMATCH,
|
|
536
|
+
summary: "Hash mismatch",
|
|
537
|
+
contractStorageHash,
|
|
538
|
+
contractProfileHash,
|
|
539
|
+
marker,
|
|
540
|
+
expectedTargetId,
|
|
541
|
+
contractPath,
|
|
542
|
+
totalTime: Date.now() - startTime,
|
|
543
|
+
...missingCodecs ? { missingCodecs } : {},
|
|
544
|
+
...codecCoverageSkipped ? { codecCoverageSkipped } : {},
|
|
545
|
+
...configPath ? { configPath } : {}
|
|
546
|
+
});
|
|
547
|
+
return createVerifyResult({
|
|
548
|
+
ok: true,
|
|
549
|
+
summary: "Database matches contract",
|
|
550
|
+
contractStorageHash,
|
|
551
|
+
marker,
|
|
552
|
+
expectedTargetId,
|
|
553
|
+
contractPath,
|
|
554
|
+
totalTime: Date.now() - startTime,
|
|
555
|
+
...contractProfileHash ? { contractProfileHash } : {},
|
|
556
|
+
...missingCodecs ? { missingCodecs } : {},
|
|
557
|
+
...codecCoverageSkipped ? { codecCoverageSkipped } : {},
|
|
558
|
+
...configPath ? { configPath } : {}
|
|
559
|
+
});
|
|
560
|
+
},
|
|
561
|
+
verifySchema(options) {
|
|
562
|
+
const contract = loadContract(options.contract);
|
|
563
|
+
if (!diffSchema) throw missingDescriptorOperationError(target.targetId, "diffSchema");
|
|
564
|
+
if (!targetGranularityOf) throw missingDescriptorOperationError(target.targetId, "classifySubjectGranularity");
|
|
565
|
+
return verifySqlSchemaByDiff({
|
|
566
|
+
contract,
|
|
567
|
+
schema: options.schema,
|
|
568
|
+
strict: options.strict,
|
|
569
|
+
frameworkComponents: options.frameworkComponents,
|
|
570
|
+
diffSchema,
|
|
571
|
+
granularityOf: targetGranularityOf
|
|
572
|
+
});
|
|
573
|
+
},
|
|
574
|
+
/**
|
|
575
|
+
* Classifies a diff issue's subject granularity on demand, by resolving
|
|
576
|
+
* its node's `nodeKind` through the target's classifier — the
|
|
577
|
+
* {@link import('@internal/framework-components/control').SchemaSubjectClassifierCapable}
|
|
578
|
+
* capability. Framework consumers spanning contract spaces (the
|
|
579
|
+
* migration aggregate's unclaimed-elements sweep) detect and call this
|
|
580
|
+
* instead of reaching into the concrete schema-IR node, which they
|
|
581
|
+
* cannot read; nothing is stamped on the issue or the node.
|
|
582
|
+
*/
|
|
583
|
+
classifySubjectGranularity(issue) {
|
|
584
|
+
if (!targetGranularityOf) throw missingDescriptorOperationError(target.targetId, "classifySubjectGranularity");
|
|
585
|
+
return classifyDiffSubjectGranularity(issue, targetGranularityOf);
|
|
586
|
+
},
|
|
587
|
+
/**
|
|
588
|
+
* Classifies a diff issue's subject storage `entityKind` on demand, by
|
|
589
|
+
* resolving its node's `nodeKind` through the target's classifier —
|
|
590
|
+
* the sibling of `classifySubjectGranularity` above, and part of the
|
|
591
|
+
* same {@link import('@internal/framework-components/control').SchemaSubjectClassifierCapable}
|
|
592
|
+
* capability.
|
|
593
|
+
*/
|
|
594
|
+
classifyEntityKind(issue) {
|
|
595
|
+
if (!targetEntityKindOf) throw missingDescriptorOperationError(target.targetId, "classifyEntityKind");
|
|
596
|
+
return classifyDiffEntityKind(issue, targetEntityKindOf);
|
|
597
|
+
},
|
|
598
|
+
async signSpaces(options) {
|
|
599
|
+
return signSpaces(options.driver, options.spaces);
|
|
600
|
+
},
|
|
601
|
+
async readMarker(options) {
|
|
602
|
+
return getControlAdapter().readMarker(options.driver, options.space);
|
|
603
|
+
},
|
|
604
|
+
async readAllMarkers(options) {
|
|
605
|
+
return getControlAdapter().readAllMarkers(options.driver);
|
|
606
|
+
},
|
|
607
|
+
async readLedger(options) {
|
|
608
|
+
return getControlAdapter().readLedger(options.driver, options.space);
|
|
609
|
+
},
|
|
610
|
+
async initMarker(options) {
|
|
611
|
+
return getControlAdapter().initMarker(options.driver, options.space, options.destination);
|
|
612
|
+
},
|
|
613
|
+
async updateMarker(options) {
|
|
614
|
+
return getControlAdapter().updateMarker(options.driver, options.space, options.expectedFrom, options.destination);
|
|
615
|
+
},
|
|
616
|
+
async writeLedgerEntry(options) {
|
|
617
|
+
return getControlAdapter().writeLedgerEntry(options.driver, options.space, options.entry);
|
|
618
|
+
},
|
|
619
|
+
async introspect(options) {
|
|
620
|
+
return getControlAdapter().introspect(options.driver, options.contract);
|
|
621
|
+
},
|
|
622
|
+
async inferPslContract(schemaIR) {
|
|
623
|
+
if (!targetInferPslContract) throw sqlFamilyError("CONTRACT.INFER_UNSUPPORTED", `Target "${target.targetId}" does not support contract infer (no inferPslContract on its descriptor).`, {
|
|
624
|
+
why: "The target descriptor does not provide the inferPslContract hook, so a PSL contract cannot be inferred from the database schema.",
|
|
625
|
+
fix: "Use a target package that supports contract infer, or author the contract instead of inferring it.",
|
|
626
|
+
meta: { targetId: target.targetId }
|
|
627
|
+
});
|
|
628
|
+
return targetInferPslContract(schemaIR, pslBuildContext, describedContracts);
|
|
629
|
+
},
|
|
630
|
+
buildPslContract(contract) {
|
|
631
|
+
if (!targetBuildPslContract) throw sqlFamilyError("CONTRACT.PRINT_UNSUPPORTED", `Target "${target.targetId}" does not support contract print (no buildPslContract on its descriptor).`, {
|
|
632
|
+
why: "The target descriptor does not provide the buildPslContract hook, so the contract cannot be printed as a Prisma 8 PSL file.",
|
|
633
|
+
fix: "Use a target whose descriptor provides buildPslContract, or write the Prisma 8 PSL file by hand.",
|
|
634
|
+
meta: { targetId: target.targetId }
|
|
635
|
+
});
|
|
636
|
+
return {
|
|
637
|
+
document: targetBuildPslContract(contract, pslBuildContext),
|
|
638
|
+
sourceSettings: ifDefined("defaultControlPolicy", contract.defaultControlPolicy)
|
|
639
|
+
};
|
|
640
|
+
},
|
|
641
|
+
lowerAst(ast, context) {
|
|
642
|
+
return getControlAdapter().lowerToExecuteRequest(ast, context);
|
|
643
|
+
},
|
|
644
|
+
bootstrapControlTableQueries() {
|
|
645
|
+
return getControlAdapter().bootstrapControlTableQueries();
|
|
646
|
+
},
|
|
647
|
+
toOperationPreview(operations) {
|
|
648
|
+
return sqlOperationsToPreview(operations);
|
|
649
|
+
},
|
|
650
|
+
storageNameOf(operation) {
|
|
651
|
+
return storageNameOfOperation(operation);
|
|
652
|
+
},
|
|
653
|
+
toSchemaView(schema) {
|
|
654
|
+
const root = blindCast(schema);
|
|
655
|
+
const namespaceEntries = root.namespaces !== void 0 ? Object.entries(root.namespaces).map(([namespaceKey, namespace]) => [namespaceKey, namespace.tables]) : [[void 0, root.tables ?? {}]];
|
|
656
|
+
const qualify = namespaceEntries.length > 1;
|
|
657
|
+
const tableNodes = namespaceEntries.flatMap(([namespaceKey, tables]) => Object.entries(tables).map(([tableName, table]) => [
|
|
658
|
+
qualify && namespaceKey !== void 0 ? `${namespaceKey}.${tableName}` : tableName,
|
|
659
|
+
tableName,
|
|
660
|
+
table
|
|
661
|
+
])).map(([displayName, tableName, table]) => {
|
|
662
|
+
const children = [];
|
|
663
|
+
const columnNodes = [];
|
|
664
|
+
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
665
|
+
const label = `${columnName}: ${column.typeText} (${column.nullable ? "nullable" : "not nullable"})`;
|
|
666
|
+
columnNodes.push(new SchemaTreeNode({
|
|
667
|
+
kind: "field",
|
|
668
|
+
id: `column-${displayName}-${columnName}`,
|
|
669
|
+
label,
|
|
670
|
+
meta: {
|
|
671
|
+
typeText: column.typeText,
|
|
672
|
+
nullable: column.nullable,
|
|
673
|
+
...ifDefined("default", column.default)
|
|
674
|
+
}
|
|
675
|
+
}));
|
|
676
|
+
}
|
|
677
|
+
if (columnNodes.length > 0) children.push(new SchemaTreeNode({
|
|
678
|
+
kind: "collection",
|
|
679
|
+
id: `columns-${displayName}`,
|
|
680
|
+
label: "columns",
|
|
681
|
+
children: columnNodes
|
|
682
|
+
}));
|
|
683
|
+
if (table.primaryKey) {
|
|
684
|
+
const pkColumns = table.primaryKey.columns.join(", ");
|
|
685
|
+
children.push(new SchemaTreeNode({
|
|
686
|
+
kind: "index",
|
|
687
|
+
id: `primary-key-${displayName}`,
|
|
688
|
+
label: `primary key: ${pkColumns}`,
|
|
689
|
+
meta: {
|
|
690
|
+
columns: table.primaryKey.columns,
|
|
691
|
+
...table.primaryKey.name ? { name: table.primaryKey.name } : {}
|
|
692
|
+
}
|
|
693
|
+
}));
|
|
694
|
+
}
|
|
695
|
+
for (const unique of table.uniques) {
|
|
696
|
+
const name = unique.name ?? `${tableName}_${unique.columns.join("_")}_unique`;
|
|
697
|
+
const label = `unique ${name}`;
|
|
698
|
+
children.push(new SchemaTreeNode({
|
|
699
|
+
kind: "index",
|
|
700
|
+
id: `unique-${displayName}-${name}`,
|
|
701
|
+
label,
|
|
702
|
+
meta: {
|
|
703
|
+
columns: unique.columns,
|
|
704
|
+
unique: true
|
|
705
|
+
}
|
|
706
|
+
}));
|
|
707
|
+
}
|
|
708
|
+
for (const index of table.indexes) {
|
|
709
|
+
const name = index.name;
|
|
710
|
+
const label = index.unique ? `unique index ${name}` : `index ${name}`;
|
|
711
|
+
children.push(new SchemaTreeNode({
|
|
712
|
+
kind: "index",
|
|
713
|
+
id: `index-${displayName}-${name}`,
|
|
714
|
+
label,
|
|
715
|
+
meta: {
|
|
716
|
+
columns: index.columns,
|
|
717
|
+
unique: index.unique
|
|
718
|
+
}
|
|
719
|
+
}));
|
|
720
|
+
}
|
|
721
|
+
const tableMeta = {};
|
|
722
|
+
if (table.primaryKey) {
|
|
723
|
+
tableMeta["primaryKey"] = table.primaryKey.columns;
|
|
724
|
+
if (table.primaryKey.name) tableMeta["primaryKeyName"] = table.primaryKey.name;
|
|
725
|
+
}
|
|
726
|
+
if (table.foreignKeys.length > 0) tableMeta["foreignKeys"] = table.foreignKeys.map((fk) => ({
|
|
727
|
+
columns: fk.columns,
|
|
728
|
+
referencedTable: fk.referencedTable,
|
|
729
|
+
referencedColumns: fk.referencedColumns,
|
|
730
|
+
...fk.name ? { name: fk.name } : {}
|
|
731
|
+
}));
|
|
732
|
+
return new SchemaTreeNode({
|
|
733
|
+
kind: "entity",
|
|
734
|
+
id: `table-${displayName}`,
|
|
735
|
+
label: `table ${displayName}`,
|
|
736
|
+
...Object.keys(tableMeta).length > 0 ? { meta: tableMeta } : {},
|
|
737
|
+
...children.length > 0 ? { children } : {}
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
return { root: new SchemaTreeNode({
|
|
741
|
+
kind: "root",
|
|
742
|
+
id: "sql-schema",
|
|
743
|
+
label: "database",
|
|
744
|
+
...tableNodes.length > 0 ? { children: tableNodes } : {}
|
|
745
|
+
}) };
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
const sqlFamilyDescribeUnsupportedAttribute = describeUnsupportedSqlAttribute;
|
|
750
|
+
const sqlFamilyDescribeUnresolvedType = describeUnresolvedSqlType;
|
|
751
|
+
var SqlFamilyDescriptor = class {
|
|
752
|
+
kind = "family";
|
|
753
|
+
id = "sql";
|
|
754
|
+
familyId = "sql";
|
|
755
|
+
version = "0.0.1";
|
|
756
|
+
emission = sqlEmission;
|
|
757
|
+
dataTypes = sqlExpressionRegistration.dataTypes;
|
|
758
|
+
authoring = {
|
|
759
|
+
field: sqlFamilyAuthoringFieldPresets,
|
|
760
|
+
type: sqlFamilyAuthoringTypes,
|
|
761
|
+
entityTypes: sqlFamilyEntityTypes,
|
|
762
|
+
pslBlockDescriptors: sqlFamilyPslBlockDescriptors,
|
|
763
|
+
attributeSpecs: sqlAttributeSpecs,
|
|
764
|
+
dataTypes: sqlExpressionRegistration.authoring.dataTypes
|
|
765
|
+
};
|
|
766
|
+
generatorFunctions = sqlFamilyGeneratorFunctions;
|
|
767
|
+
controlMutationDefaults = {
|
|
768
|
+
defaultFunctionRegistry: /* @__PURE__ */ new Map(),
|
|
769
|
+
generatorDescriptors: sqlFamilyGeneratorDescriptors
|
|
770
|
+
};
|
|
771
|
+
fieldLength = ({ codecId, typeParams }, dataTypes) => sqlCodecColumnLength(dataTypes, codecId, typeParams);
|
|
772
|
+
pslDiagnostics = {
|
|
773
|
+
describeUnsupportedAttribute: sqlFamilyDescribeUnsupportedAttribute,
|
|
774
|
+
describeUnresolvedType: sqlFamilyDescribeUnresolvedType
|
|
775
|
+
};
|
|
776
|
+
create(stack) {
|
|
777
|
+
return createSqlFamilyInstance(stack);
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
/**
|
|
781
|
+
* The SQL family's default functions, each a database default: `autoincrement()` and `now()`. A value the client generates is a generator function, written `@onCreate(...)`.
|
|
782
|
+
*/
|
|
783
|
+
function sqlDefaultFunctions() {
|
|
784
|
+
return {
|
|
785
|
+
autoincrement: {
|
|
786
|
+
signature: { documentation: "Generates an increasing integer value in the database." },
|
|
787
|
+
usageSignatures: ["autoincrement()"],
|
|
788
|
+
lower: () => databaseFunction("autoincrement()")
|
|
789
|
+
},
|
|
790
|
+
now: {
|
|
791
|
+
signature: { documentation: "Uses the current database timestamp as the default value." },
|
|
792
|
+
usageSignatures: ["now()"],
|
|
793
|
+
lower: () => databaseFunction("now()")
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function databaseFunction(expression) {
|
|
798
|
+
return {
|
|
799
|
+
ok: true,
|
|
800
|
+
value: {
|
|
801
|
+
kind: "storage",
|
|
802
|
+
defaultValue: {
|
|
803
|
+
kind: "function",
|
|
804
|
+
expression
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
function convertColumn(name, column, types, hooks) {
|
|
810
|
+
const dataType = sqlDataTypeOfCodec(column.codecId, types);
|
|
811
|
+
const storage = sqlStorageType(dataType, types.dataTypeLookup);
|
|
812
|
+
const typeText = schemaTypeText(storage, column.typeParams);
|
|
813
|
+
const many = column.many !== void 0 && column.many !== false;
|
|
814
|
+
const rawColumnDefault = column.default ?? void 0;
|
|
815
|
+
const resolvedColumnDefault = rawColumnDefault !== void 0 && hooks.resolveDefault ? hooks.resolveDefault(rawColumnDefault, {
|
|
816
|
+
typeText,
|
|
817
|
+
many
|
|
818
|
+
}) : rawColumnDefault;
|
|
819
|
+
return {
|
|
820
|
+
name,
|
|
821
|
+
typeText,
|
|
822
|
+
nullable: column.nullable,
|
|
823
|
+
...ifDefined("many", many ? true : void 0),
|
|
824
|
+
...ifDefined("resolvedDefault", resolvedColumnDefault),
|
|
825
|
+
...ifDefined("authoredDefault", rawColumnDefault),
|
|
826
|
+
codecRef: buildColumnCodecRef(column, many ? true : void 0),
|
|
827
|
+
storedType: storage,
|
|
828
|
+
storedTypeParams: storage.sql.normalize(dataTypeParams(storage, column.typeParams)),
|
|
829
|
+
...ifDefined("catalogText", nameIsCatalogText(storage) ? typeText : void 0),
|
|
830
|
+
...ifDefined("defaultValue", rawColumnDefault?.kind === "literal" && rawColumnDefault.value instanceof Date ? dateDefaultValue(rawColumnDefault.value, codecForRef(types.codecLookup, buildColumnCodecRef(column, void 0))) : contractDefaultValue(rawColumnDefault, dataType, dataTypeParams(dataType, column.typeParams), many)),
|
|
831
|
+
sqlExpressionLiteralFor
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
function sqlExpressionLiteralFor(text) {
|
|
835
|
+
return sqlTextsReadBack([text]) ? printSqlExpressionLiteral(text) : void 0;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* A `Date` default, which an in-memory contract can hold, as the value of the column's type the column's codec reads it as. `undefined` when the codec refuses it or no codec is registered, and the default then compares unequal to any other.
|
|
839
|
+
*/
|
|
840
|
+
function dateDefaultValue(value, codec) {
|
|
841
|
+
if (codec === void 0) return void 0;
|
|
842
|
+
try {
|
|
843
|
+
return codec.toDataTypeValue(value);
|
|
844
|
+
} catch (error) {
|
|
845
|
+
if (isInternalError(error)) throw error;
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* A literal default as a value of the column's type, read from the JSON the contract stores, element by element for a list column. A `null` is a value of a type that stores one, such as a JSON type, and SQL NULL otherwise, as DDL writes it. Loading a contract refuses a value its type does not hold, so a refusal here only means no value, and the comparison falls back to the stored JSON.
|
|
851
|
+
*/
|
|
852
|
+
function contractDefaultValue(columnDefault, type, params, many) {
|
|
853
|
+
if (columnDefault?.kind !== "literal" || columnDefault.value instanceof Date) return void 0;
|
|
854
|
+
const stored = columnDefault.value;
|
|
855
|
+
const read = (json) => {
|
|
856
|
+
try {
|
|
857
|
+
return type.fromContract(json, params);
|
|
858
|
+
} catch (error) {
|
|
859
|
+
if (isStructuredError(error) && json === null) return null;
|
|
860
|
+
throw error;
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
try {
|
|
864
|
+
if (many && Array.isArray(stored)) return stored.map(read);
|
|
865
|
+
return read(stored) ?? void 0;
|
|
866
|
+
} catch (error) {
|
|
867
|
+
if (isStructuredError(error)) return void 0;
|
|
868
|
+
throw error;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* The column's type as the database reports it, from the type it is stored as: the written name with its normal-form parameters (`character(1)` for a bare `character`), or for a
|
|
873
|
+
* type that claims a kind (an enum), its type name as stored.
|
|
874
|
+
*/
|
|
875
|
+
function schemaTypeText(type, typeParams) {
|
|
876
|
+
const params = dataTypeParams(type, typeParams);
|
|
877
|
+
return type.sql.claimsKind === void 0 ? renderSqlTypeName(type, type.sql.normalize(params)) : unquotedSqlBaseName(type, params);
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Builds the column's `CodecRef` from its codec identity — the same construction the query AST and the migration DDL renderer already use (TML-2456, TML-2918).
|
|
881
|
+
*/
|
|
882
|
+
function buildColumnCodecRef(column, many) {
|
|
883
|
+
return {
|
|
884
|
+
codecId: column.codecId,
|
|
885
|
+
...ifDefined("typeParams", column.typeParams !== void 0 ? blindCast(column.typeParams) : void 0),
|
|
886
|
+
...ifDefined("many", many)
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
function convertCheck(check, tableName, tableColumns) {
|
|
890
|
+
return {
|
|
891
|
+
naming: namingOf(check.name, check.prefix),
|
|
892
|
+
expression: check.expression,
|
|
893
|
+
dependsOn: flatColumnDependsOn(tableName, tableColumns)
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
function convertUnique(unique, tableName) {
|
|
897
|
+
return {
|
|
898
|
+
columns: unique.columns,
|
|
899
|
+
...ifDefined("name", unique.name),
|
|
900
|
+
dependsOn: flatColumnDependsOn(tableName, unique.columns)
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
function convertIndex(index, tableName, tableColumns) {
|
|
904
|
+
const base = {
|
|
905
|
+
naming: namingOf(index.name, index.prefix),
|
|
906
|
+
where: index.where,
|
|
907
|
+
unique: index.unique,
|
|
908
|
+
partial: index.where !== void 0,
|
|
909
|
+
type: index.type,
|
|
910
|
+
options: index.options,
|
|
911
|
+
annotations: void 0,
|
|
912
|
+
dependsOn: flatColumnDependsOn(tableName, index.columns ?? tableColumns)
|
|
913
|
+
};
|
|
914
|
+
return index.expression !== void 0 ? {
|
|
915
|
+
...base,
|
|
916
|
+
expression: index.expression
|
|
917
|
+
} : {
|
|
918
|
+
...base,
|
|
919
|
+
columns: index.columns ?? []
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* The referenced table's chain in the flat (single-schema) tree
|
|
924
|
+
* `contractToSchemaIR`/`contractNamespaceToSchemaIR` build: the root
|
|
925
|
+
* (`SqlSchemaIR`, fixed `'database'` id) followed by the table's own id.
|
|
926
|
+
* Postgres discards this when it re-derives the FK against its own
|
|
927
|
+
* multi-schema tree shape (`contractToPostgresDatabaseSchemaNode`); SQLite's
|
|
928
|
+
* flat tree uses it as-is.
|
|
929
|
+
*/
|
|
930
|
+
function flatSchemaDependsOn(tableName) {
|
|
931
|
+
return [{
|
|
932
|
+
nodeKind: RelationalSchemaNodeKind.schema,
|
|
933
|
+
id: "database"
|
|
934
|
+
}, {
|
|
935
|
+
nodeKind: RelationalSchemaNodeKind.table,
|
|
936
|
+
id: tableName
|
|
937
|
+
}];
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* The chains from a table-child object (foreign key, index, unique, primary
|
|
941
|
+
* key) to each of the own columns it is built on, in the flat tree. Dropping
|
|
942
|
+
* a covered column auto-drops the object, so the object's drop must precede
|
|
943
|
+
* the column's; the graph derives that direction from these edges.
|
|
944
|
+
*/
|
|
945
|
+
function flatColumnDependsOn(tableName, columns) {
|
|
946
|
+
return columns.map((column) => [
|
|
947
|
+
{
|
|
948
|
+
nodeKind: RelationalSchemaNodeKind.schema,
|
|
949
|
+
id: "database"
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
nodeKind: RelationalSchemaNodeKind.table,
|
|
953
|
+
id: tableName
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
nodeKind: RelationalSchemaNodeKind.column,
|
|
957
|
+
id: `column:${column}`
|
|
958
|
+
}
|
|
959
|
+
]);
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* The FK's referenced-namespace identity comes from the target's namespace
|
|
963
|
+
* node, not the raw namespace-id string. An unbound target namespace stamps
|
|
964
|
+
* no `referencedSchema` at all — the FK node's id renders the absence as the
|
|
965
|
+
* empty segment, which is what flat (single-schema) introspection produces,
|
|
966
|
+
* so both diff sides' FK ids meet by construction. A bound namespace (or a
|
|
967
|
+
* cross-space target whose namespace lives in another contract's storage)
|
|
968
|
+
* stamps its coordinate verbatim; namespaced targets (Postgres) resolve the
|
|
969
|
+
* real DDL schema downstream.
|
|
970
|
+
*
|
|
971
|
+
* `dependsOn` carries the referenced table (created before the FK, dropped
|
|
972
|
+
* after it) plus the FK's own columns (dropped after the FK, since dropping a
|
|
973
|
+
* column auto-drops the FK built on it).
|
|
974
|
+
*/
|
|
975
|
+
function convertForeignKey(fk, storage) {
|
|
976
|
+
const targetIsUnbound = storage.namespaces[fk.target.namespaceId]?.isUnbound === true;
|
|
977
|
+
return {
|
|
978
|
+
columns: fk.source.columns,
|
|
979
|
+
referencedTable: fk.target.tableName,
|
|
980
|
+
...targetIsUnbound ? {} : { referencedSchema: fk.target.namespaceId },
|
|
981
|
+
referencedColumns: fk.target.columns,
|
|
982
|
+
...ifDefined("name", fk.name),
|
|
983
|
+
...ifDefined("onDelete", fk.onDelete),
|
|
984
|
+
...ifDefined("onUpdate", fk.onUpdate),
|
|
985
|
+
dependsOn: [flatSchemaDependsOn(fk.target.tableName), ...flatColumnDependsOn(fk.source.tableName, fk.source.columns)]
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
const COLUMN_TYPE_ERROR_CODES = /* @__PURE__ */ new Set(["CONTRACT.CODEC_DESCRIPTOR_MISSING", "CONTRACT.DATA_TYPE_UNREGISTERED"]);
|
|
989
|
+
function withColumnInErrors(table, column, build) {
|
|
990
|
+
try {
|
|
991
|
+
return build();
|
|
992
|
+
} catch (cause) {
|
|
993
|
+
if (!isStructuredError(cause) || !COLUMN_TYPE_ERROR_CODES.has(cause.code)) throw cause;
|
|
994
|
+
throw structuredError(cause.code, cause.message, {
|
|
995
|
+
cause,
|
|
996
|
+
...ifDefined("why", cause.why),
|
|
997
|
+
...ifDefined("fix", cause.fix),
|
|
998
|
+
meta: {
|
|
999
|
+
...cause.meta,
|
|
1000
|
+
table,
|
|
1001
|
+
column
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
function convertTable(name, table, types, hooks, storage) {
|
|
1007
|
+
const columns = {};
|
|
1008
|
+
for (const [colName, colDef] of Object.entries(table.columns)) columns[colName] = withColumnInErrors(name, colName, () => convertColumn(colName, colDef, types, hooks));
|
|
1009
|
+
const checks = table.checks && table.checks.length > 0 ? table.checks.map((c) => convertCheck(c, name, Object.keys(table.columns))) : void 0;
|
|
1010
|
+
return new SqlTableIR({
|
|
1011
|
+
name,
|
|
1012
|
+
columns,
|
|
1013
|
+
...ifDefined("primaryKey", table.primaryKey !== void 0 ? {
|
|
1014
|
+
columns: table.primaryKey.columns,
|
|
1015
|
+
...ifDefined("name", table.primaryKey.name),
|
|
1016
|
+
dependsOn: flatColumnDependsOn(name, table.primaryKey.columns)
|
|
1017
|
+
} : void 0),
|
|
1018
|
+
foreignKeys: table.foreignKeys.map((fk) => convertForeignKey(fk, storage)),
|
|
1019
|
+
uniques: table.uniques.map((u) => convertUnique(u, name)),
|
|
1020
|
+
indexes: table.indexes.map((i) => convertIndex(i, name, Object.keys(table.columns))),
|
|
1021
|
+
...ifDefined("checks", checks)
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Detects destructive changes between two contract storages.
|
|
1026
|
+
*
|
|
1027
|
+
* The additive-only planner silently ignores removals (tables, columns).
|
|
1028
|
+
* This function detects those removals so callers can report them as conflicts
|
|
1029
|
+
* rather than silently producing an empty plan.
|
|
1030
|
+
*
|
|
1031
|
+
* Returns an empty array if no destructive changes are found.
|
|
1032
|
+
*/
|
|
1033
|
+
function detectDestructiveChanges(from, to) {
|
|
1034
|
+
if (!from) return [];
|
|
1035
|
+
const hasOwn = (value, key) => Object.hasOwn(value, key);
|
|
1036
|
+
const conflicts = [];
|
|
1037
|
+
const namespaceIds = [.../* @__PURE__ */ new Set([...Object.keys(from.namespaces), ...Object.keys(to.namespaces)])].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1038
|
+
for (const namespaceId of namespaceIds) {
|
|
1039
|
+
const fromNs = from.namespaces[namespaceId];
|
|
1040
|
+
const toNs = to.namespaces[namespaceId];
|
|
1041
|
+
const fromTables = fromNs?.entries.table;
|
|
1042
|
+
if (!fromTables) continue;
|
|
1043
|
+
for (const tableName of Object.keys(fromTables)) {
|
|
1044
|
+
const toTableRaw = toNs?.entries.table?.[tableName];
|
|
1045
|
+
if (!StorageTable.is(toTableRaw)) {
|
|
1046
|
+
conflicts.push({
|
|
1047
|
+
kind: "tableRemoved",
|
|
1048
|
+
summary: `Table "${tableName}" was removed`
|
|
1049
|
+
});
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
const toTable = toTableRaw;
|
|
1053
|
+
const fromTableRaw = fromTables[tableName];
|
|
1054
|
+
if (!StorageTable.is(fromTableRaw)) continue;
|
|
1055
|
+
const fromTable = fromTableRaw;
|
|
1056
|
+
for (const columnName of Object.keys(fromTable.columns)) if (!hasOwn(toTable.columns, columnName)) conflicts.push({
|
|
1057
|
+
kind: "columnRemoved",
|
|
1058
|
+
summary: `Column "${tableName}"."${columnName}" was removed`
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
return conflicts;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Converts the tables of a single namespace into a `SqlSchemaIR`, keyed by
|
|
1066
|
+
* table name within that namespace. Unlike {@link contractToSchemaIR}, which
|
|
1067
|
+
* flattens every namespace's tables into one bare-keyed record (and throws on a
|
|
1068
|
+
* cross-namespace name collision), this scopes the table iteration to one
|
|
1069
|
+
* namespace so the same table name can exist in two schemas.
|
|
1070
|
+
*
|
|
1071
|
+
* The full `storage` is still passed to `convertTable`, so value-set / enum /
|
|
1072
|
+
* type resolution that legitimately spans namespaces is unaffected. Foreign
|
|
1073
|
+
* keys are built purely from the FK descriptor (`fk.target`), so cross-namespace
|
|
1074
|
+
* FKs survive per-namespace conversion. The `annotations` block (storage-type
|
|
1075
|
+
* derived) is omitted here — the per-namespace tree consumer reads only the
|
|
1076
|
+
* per-table fields.
|
|
1077
|
+
*/
|
|
1078
|
+
function contractNamespaceToSchemaIR(storage, namespaceId, options) {
|
|
1079
|
+
if (options.annotationNamespace.length === 0) throw sqlFamilyError("CONTRACT.PACK_CONTRIBUTION_INVALID", "annotationNamespace must be a non-empty string", {
|
|
1080
|
+
why: "The calling target pack passed an empty annotationNamespace to the contract-to-schema-IR projection.",
|
|
1081
|
+
fix: "Fix the target pack to pass its non-empty annotation namespace (e.g. \"pg\").",
|
|
1082
|
+
meta: { option: "annotationNamespace" }
|
|
1083
|
+
});
|
|
1084
|
+
const namespace = storage.namespaces[namespaceId];
|
|
1085
|
+
if (!namespace) return new SqlSchemaIR({ tables: {} });
|
|
1086
|
+
const tables = {};
|
|
1087
|
+
for (const [tableName, tableDefRaw] of Object.entries(namespace.entries.table ?? {})) {
|
|
1088
|
+
StorageTable.assert(tableDefRaw, `namespaces.${namespaceId}.entries.table.${tableName}`);
|
|
1089
|
+
tables[tableName] = convertTable(tableName, tableDefRaw, options, options, storage);
|
|
1090
|
+
}
|
|
1091
|
+
return new SqlSchemaIR({ tables });
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Converts a `Contract` to `SqlSchemaIR`.
|
|
1095
|
+
*
|
|
1096
|
+
* Reads `contract.storage` for tables.
|
|
1097
|
+
*
|
|
1098
|
+
* Drops codec metadata (`codecId`) since the schema IR only represents
|
|
1099
|
+
* structural information. Each column's type is written from the data type its codec
|
|
1100
|
+
* represents, with its parameters (e.g. `character` + `{ length: 36 }` → `character(36)`),
|
|
1101
|
+
* so the resulting IR compares correctly against the "to" contract during planning.
|
|
1102
|
+
*
|
|
1103
|
+
* Returns an empty schema IR when `contract` is `null` (new project).
|
|
1104
|
+
*/
|
|
1105
|
+
function contractToSchemaIR(contract, options) {
|
|
1106
|
+
if (options.annotationNamespace.length === 0) throw sqlFamilyError("CONTRACT.PACK_CONTRIBUTION_INVALID", "annotationNamespace must be a non-empty string", {
|
|
1107
|
+
why: "The calling target pack passed an empty annotationNamespace to the contract-to-schema-IR projection.",
|
|
1108
|
+
fix: "Fix the target pack to pass its non-empty annotation namespace (e.g. \"pg\").",
|
|
1109
|
+
meta: { option: "annotationNamespace" }
|
|
1110
|
+
});
|
|
1111
|
+
if (!contract) return new SqlSchemaIR({ tables: {} });
|
|
1112
|
+
const storage = contract.storage;
|
|
1113
|
+
const tables = {};
|
|
1114
|
+
for (const ns of Object.values(storage.namespaces)) for (const [tableName, tableDefRaw] of Object.entries(ns.entries.table ?? {})) {
|
|
1115
|
+
StorageTable.assert(tableDefRaw, `namespaces.${ns.id}.entries.table.${tableName}`);
|
|
1116
|
+
const tableDef = tableDefRaw;
|
|
1117
|
+
if (tables[tableName] !== void 0) throw sqlFamilyError("CONTRACT.TABLE_AMBIGUOUS", `contractToSchemaIR: duplicate SQL table name "${tableName}" across namespaces (ambiguous for flat SqlSchemaIR.tables).`, {
|
|
1118
|
+
why: "Two namespaces declare a table with the same name, which is ambiguous for the flat schema-IR table map.",
|
|
1119
|
+
fix: "Rename one of the tables so every table name is unique across namespaces.",
|
|
1120
|
+
meta: { table: tableName }
|
|
1121
|
+
});
|
|
1122
|
+
tables[tableName] = convertTable(tableName, tableDef, options, options, storage);
|
|
1123
|
+
}
|
|
1124
|
+
return new SqlSchemaIR({ tables });
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Whether a call is allowed to emit under a given control policy.
|
|
1128
|
+
*
|
|
1129
|
+
* - `managed` — full lifecycle, every op allowed.
|
|
1130
|
+
* - `tolerated` — create-if-absent only: allowed iff the call creates a whole
|
|
1131
|
+
* new top-level object (and its subject was positively resolved). Anything
|
|
1132
|
+
* that modifies an existing object, and anything whose subject could not be
|
|
1133
|
+
* resolved, is suppressed.
|
|
1134
|
+
* - `external` / `observed` — no DDL at all.
|
|
1135
|
+
*/
|
|
1136
|
+
function callAllowedUnderControlPolicy(policy, subject) {
|
|
1137
|
+
switch (policy) {
|
|
1138
|
+
case "managed": return true;
|
|
1139
|
+
case "tolerated": return subject?.createsNewObject === true;
|
|
1140
|
+
case "external":
|
|
1141
|
+
case "observed": return false;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Partition the calls produced for a single set of subjects into those the
|
|
1146
|
+
* effective control policy permits (`kept`) and a list of
|
|
1147
|
+
* {@link SuppressionRecord}s describing the suppressed calls.
|
|
1148
|
+
*
|
|
1149
|
+
* **Prefer {@link partitionIssuesByControlPolicy}** for the schema-issue
|
|
1150
|
+
* pipeline: it filters subjects out of the planner's *input* so the planner
|
|
1151
|
+
* never has to reason about un-modeled state on `external`/`observed`
|
|
1152
|
+
* subjects. This call-level helper remains for paths that bypass the issue
|
|
1153
|
+
* pipeline — currently the codec-emitted field-event ops, which originate
|
|
1154
|
+
* from declared contract fields rather than from introspected schema state
|
|
1155
|
+
* and therefore cannot trip the diff engine.
|
|
1156
|
+
*/
|
|
1157
|
+
function partitionCallsByControlPolicy(options) {
|
|
1158
|
+
const defaultControlPolicy = options.contract.defaultControlPolicy;
|
|
1159
|
+
const kept = [];
|
|
1160
|
+
const suppressions = [];
|
|
1161
|
+
for (const call of options.calls) {
|
|
1162
|
+
const subject = options.resolveControlPolicySubject(call);
|
|
1163
|
+
const policy = governingControlPolicy(subject, defaultControlPolicy);
|
|
1164
|
+
if (callAllowedUnderControlPolicy(policy, subject)) kept.push(call);
|
|
1165
|
+
else suppressions.push({
|
|
1166
|
+
subject,
|
|
1167
|
+
policy,
|
|
1168
|
+
factoryName: options.resolveFactoryName(call),
|
|
1169
|
+
createsNewObject: subject?.createsNewObject ?? false
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
return Object.freeze({
|
|
1173
|
+
kept: Object.freeze(kept),
|
|
1174
|
+
suppressions: Object.freeze(suppressions)
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Partition a list of schema-issue-shaped inputs by the effective control
|
|
1179
|
+
* policy of each issue's subject *before* the planner is invoked.
|
|
1180
|
+
*
|
|
1181
|
+
* `plannable` is the list of issues whose subject's effective policy permits
|
|
1182
|
+
* the planner to act on them (`managed`, or `tolerated` for whole-object
|
|
1183
|
+
* creation issues only). Issues for `external`/`observed` subjects, and
|
|
1184
|
+
* non-creation issues for `tolerated` subjects, are dropped from the planner's
|
|
1185
|
+
* input entirely — they never enter introspection-driven planning, never feed
|
|
1186
|
+
* the diff engine, and never produce DDL calls that would have to be
|
|
1187
|
+
* post-filtered. This sidesteps a class of failure where the diff engine
|
|
1188
|
+
* cannot reason about the live shape of a subject the user marked as
|
|
1189
|
+
* out-of-scope (`external`).
|
|
1190
|
+
*
|
|
1191
|
+
* `suppressions` is one {@link SuppressionRecord} per suppressed subject (not
|
|
1192
|
+
* per suppressed issue). Its `factoryName` is the creation factory name when any
|
|
1193
|
+
* of the subject's issues is whole-object creation (e.g. `createTable`), else
|
|
1194
|
+
* `undefined` — the family never invents a modification verb for an op that
|
|
1195
|
+
* produced no call; the target renders the message.
|
|
1196
|
+
*
|
|
1197
|
+
* Unresolved-subject issues (`resolveControlPolicySubject` returns
|
|
1198
|
+
* `undefined`) emit one record each; they cannot be deduplicated because they
|
|
1199
|
+
* carry no subject coordinate.
|
|
1200
|
+
*/
|
|
1201
|
+
function partitionIssuesByControlPolicy(options) {
|
|
1202
|
+
const defaultControlPolicy = options.contract.defaultControlPolicy;
|
|
1203
|
+
const plannable = [];
|
|
1204
|
+
const suppressedSubjects = /* @__PURE__ */ new Map();
|
|
1205
|
+
const unresolvedSuppressions = [];
|
|
1206
|
+
for (const issue of options.issues) {
|
|
1207
|
+
const subject = options.resolveControlPolicySubject(issue);
|
|
1208
|
+
const policy = governingControlPolicy(subject, defaultControlPolicy);
|
|
1209
|
+
const creationFactoryName = options.resolveCreationFactoryName(issue);
|
|
1210
|
+
if (policy === "managed") {
|
|
1211
|
+
plannable.push(issue);
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
if (policy === "tolerated" && subject !== void 0 && creationFactoryName !== void 0 && subject.createsNewObject) {
|
|
1215
|
+
plannable.push(issue);
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
if (subject === void 0) {
|
|
1219
|
+
unresolvedSuppressions.push({
|
|
1220
|
+
subject: void 0,
|
|
1221
|
+
policy,
|
|
1222
|
+
factoryName: creationFactoryName,
|
|
1223
|
+
createsNewObject: false
|
|
1224
|
+
});
|
|
1225
|
+
continue;
|
|
1226
|
+
}
|
|
1227
|
+
const key = subjectKey(subject);
|
|
1228
|
+
const existing = suppressedSubjects.get(key);
|
|
1229
|
+
if (existing) {
|
|
1230
|
+
if (existing.creationFactoryName === void 0 && creationFactoryName !== void 0) existing.creationFactoryName = creationFactoryName;
|
|
1231
|
+
} else suppressedSubjects.set(key, {
|
|
1232
|
+
subject,
|
|
1233
|
+
policy,
|
|
1234
|
+
...ifDefined("creationFactoryName", creationFactoryName)
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
const suppressions = [...unresolvedSuppressions];
|
|
1238
|
+
for (const entry of suppressedSubjects.values()) suppressions.push({
|
|
1239
|
+
subject: entry.subject,
|
|
1240
|
+
policy: entry.policy,
|
|
1241
|
+
factoryName: entry.creationFactoryName,
|
|
1242
|
+
createsNewObject: entry.subject.createsNewObject
|
|
1243
|
+
});
|
|
1244
|
+
return Object.freeze({
|
|
1245
|
+
plannable: Object.freeze(plannable),
|
|
1246
|
+
suppressions: Object.freeze(suppressions)
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
function subjectKey(subject) {
|
|
1250
|
+
return `${subject.namespaceId}\u0000${subject.entityKind ?? ""}\u0000${subject.entityName ?? ""}\u0000${subject.rlsPolicy ?? ""}`;
|
|
1251
|
+
}
|
|
1252
|
+
function planFieldEventOperations(options) {
|
|
1253
|
+
return planFieldEventCalls(options).map(({ call }) => call);
|
|
1254
|
+
}
|
|
1255
|
+
/** The calls the codec hooks return, each with the field event's column. */
|
|
1256
|
+
function planFieldEventCalls(options) {
|
|
1257
|
+
const priorContract = options.priorContract;
|
|
1258
|
+
const newContract = options.newContract;
|
|
1259
|
+
const renamedTo = new Map(options.tableRenames.map((rename) => [renameKey(rename.namespaceId, rename.from), rename.to]));
|
|
1260
|
+
const newNameOf = (namespaceId, tableName) => renamedTo.get(renameKey(namespaceId, tableName));
|
|
1261
|
+
const columnRenamedTo = new Map(options.columnRenames.map((rename) => [renameKey(rename.namespaceId, rename.table, rename.from), rename.to]));
|
|
1262
|
+
const added = [];
|
|
1263
|
+
const dropped = [];
|
|
1264
|
+
const altered = [];
|
|
1265
|
+
const namespaceIds = unionSorted(priorContract ? Object.keys(priorContract.storage.namespaces) : [], Object.keys(newContract.storage.namespaces));
|
|
1266
|
+
for (const namespaceId of namespaceIds) {
|
|
1267
|
+
const priorNs = priorContract?.storage.namespaces[namespaceId];
|
|
1268
|
+
const newNs = newContract.storage.namespaces[namespaceId];
|
|
1269
|
+
const priorTables = underNewNames(priorNs?.entries.table, (tableName) => newNameOf(namespaceId, tableName));
|
|
1270
|
+
const newTables = newNs?.entries.table;
|
|
1271
|
+
const tableNames = unionSorted(priorTables ? Object.keys(priorTables) : [], newTables ? Object.keys(newTables) : []);
|
|
1272
|
+
for (const tableName of tableNames) {
|
|
1273
|
+
const priorTableRaw = priorTables?.[tableName];
|
|
1274
|
+
const newTableRaw = newTables?.[tableName];
|
|
1275
|
+
const priorTable = StorageTable.is(priorTableRaw) ? priorTableRaw : void 0;
|
|
1276
|
+
const newTable = StorageTable.is(newTableRaw) ? newTableRaw : void 0;
|
|
1277
|
+
const priorColumns = underNewNames(priorTable?.columns, (columnName) => columnRenamedTo.get(renameKey(namespaceId, tableName, columnName)));
|
|
1278
|
+
const fieldNames = unionSorted(priorColumns ? Object.keys(priorColumns) : [], newTable ? Object.keys(newTable.columns) : []);
|
|
1279
|
+
for (const fieldName of fieldNames) {
|
|
1280
|
+
const priorField = priorColumns?.[fieldName];
|
|
1281
|
+
const newField = newTable?.columns[fieldName];
|
|
1282
|
+
const entry = {
|
|
1283
|
+
namespaceId,
|
|
1284
|
+
tableName,
|
|
1285
|
+
fieldName,
|
|
1286
|
+
priorTable,
|
|
1287
|
+
newTable,
|
|
1288
|
+
priorField,
|
|
1289
|
+
newField
|
|
1290
|
+
};
|
|
1291
|
+
if (priorField === void 0 && newField !== void 0) added.push(entry);
|
|
1292
|
+
else if (priorField !== void 0 && newField === void 0) dropped.push(entry);
|
|
1293
|
+
else if (priorField !== void 0 && newField !== void 0) {
|
|
1294
|
+
if (isAlteration(priorField, newField, options.dataTypes)) altered.push(entry);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
const calls = [];
|
|
1300
|
+
appendCalls("added", added, options.codecHooks, calls, (e) => e.newField?.codecId);
|
|
1301
|
+
appendCalls("dropped", dropped, options.codecHooks, calls, (e) => e.priorField?.codecId);
|
|
1302
|
+
appendCalls("altered", altered, options.codecHooks, calls, (e) => e.newField?.codecId);
|
|
1303
|
+
return calls;
|
|
1304
|
+
}
|
|
1305
|
+
function renameKey(...parts) {
|
|
1306
|
+
return JSON.stringify(parts);
|
|
1307
|
+
}
|
|
1308
|
+
function underNewNames(tables, newNameOf) {
|
|
1309
|
+
if (tables === void 0) return void 0;
|
|
1310
|
+
return Object.fromEntries(Object.entries(tables).map(([tableName, table]) => [newNameOf(tableName) ?? tableName, table]));
|
|
1311
|
+
}
|
|
1312
|
+
function appendCalls(event, entries, codecHooks, calls, pickCodecId) {
|
|
1313
|
+
for (const entry of entries) {
|
|
1314
|
+
const codecId = pickCodecId(entry);
|
|
1315
|
+
if (codecId === void 0) continue;
|
|
1316
|
+
const hook = codecHooks.get(codecId);
|
|
1317
|
+
if (!hook?.onFieldEvent) continue;
|
|
1318
|
+
const ctx = buildContext(event, entry);
|
|
1319
|
+
const emitted = hook.onFieldEvent(event, ctx);
|
|
1320
|
+
for (const call of emitted) calls.push({
|
|
1321
|
+
call,
|
|
1322
|
+
namespaceId: entry.namespaceId,
|
|
1323
|
+
tableName: entry.tableName,
|
|
1324
|
+
columnName: entry.fieldName
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* The context's prior/new sides are scoped to the event:
|
|
1330
|
+
*
|
|
1331
|
+
* - `'added'` — only `newTable` / `newField` populated.
|
|
1332
|
+
* - `'dropped'` — only `priorTable` / `priorField` populated.
|
|
1333
|
+
* - `'altered'` — both sides populated.
|
|
1334
|
+
*/
|
|
1335
|
+
function buildContext(event, entry) {
|
|
1336
|
+
const base = {
|
|
1337
|
+
namespaceId: entry.namespaceId,
|
|
1338
|
+
tableName: entry.tableName,
|
|
1339
|
+
fieldName: entry.fieldName
|
|
1340
|
+
};
|
|
1341
|
+
if (event === "added") return {
|
|
1342
|
+
...base,
|
|
1343
|
+
...entry.newTable !== void 0 ? { newTable: entry.newTable } : {},
|
|
1344
|
+
...entry.newField !== void 0 ? { newField: entry.newField } : {}
|
|
1345
|
+
};
|
|
1346
|
+
if (event === "dropped") return {
|
|
1347
|
+
...base,
|
|
1348
|
+
...entry.priorTable !== void 0 ? { priorTable: entry.priorTable } : {},
|
|
1349
|
+
...entry.priorField !== void 0 ? { priorField: entry.priorField } : {}
|
|
1350
|
+
};
|
|
1351
|
+
return {
|
|
1352
|
+
...base,
|
|
1353
|
+
...entry.priorTable !== void 0 ? { priorTable: entry.priorTable } : {},
|
|
1354
|
+
...entry.newTable !== void 0 ? { newTable: entry.newTable } : {},
|
|
1355
|
+
...entry.priorField !== void 0 ? { priorField: entry.priorField } : {},
|
|
1356
|
+
...entry.newField !== void 0 ? { newField: entry.newField } : {}
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* `'altered'` predicate. Returns `false` whenever `codecId` differs —
|
|
1361
|
+
* any codec change suppresses the `altered` event entirely, including
|
|
1362
|
+
* cases where another property also differs in the same diff. Codec
|
|
1363
|
+
* rotation is a v1 non-goal; avoiding the mixed event keeps the
|
|
1364
|
+
* migration semantics for codec changes explicit rather than smuggling
|
|
1365
|
+
* them through as `altered`.
|
|
1366
|
+
*
|
|
1367
|
+
* For non-`codecId` diffs, returns `true` iff any other column property
|
|
1368
|
+
* differs.
|
|
1369
|
+
*/
|
|
1370
|
+
function isAlteration(prior, current, dataTypes) {
|
|
1371
|
+
if (prior.codecId !== current.codecId) return false;
|
|
1372
|
+
return !sameStorageColumn(prior, current, dataTypes);
|
|
1373
|
+
}
|
|
1374
|
+
function sameStorageColumn(a, b, dataTypes) {
|
|
1375
|
+
if (a === b) return true;
|
|
1376
|
+
if (!sameSqlColumnType(a, b, dataTypes)) return false;
|
|
1377
|
+
if (a.nullable !== b.nullable) return false;
|
|
1378
|
+
if (canonicalizeJson(codecOwnParams(a, dataTypes)) !== canonicalizeJson(codecOwnParams(b, dataTypes))) return false;
|
|
1379
|
+
if (!sameJson(a.default, b.default)) return false;
|
|
1380
|
+
return true;
|
|
1381
|
+
}
|
|
1382
|
+
/** The parameters a column's codec keeps for itself: every key its data type does not declare. */
|
|
1383
|
+
function codecOwnParams(column, dataTypes) {
|
|
1384
|
+
const declared = new Set(objectSchemaKeys(dataTypes.get(column.dataType)?.params) ?? []);
|
|
1385
|
+
return Object.fromEntries(Object.entries(column.typeParams ?? {}).filter(([key]) => !declared.has(key)));
|
|
1386
|
+
}
|
|
1387
|
+
function sameJson(a, b) {
|
|
1388
|
+
if (a === b) return true;
|
|
1389
|
+
if (a === void 0 || b === void 0) return false;
|
|
1390
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
1391
|
+
}
|
|
1392
|
+
function unionSorted(a, b) {
|
|
1393
|
+
const set = /* @__PURE__ */ new Set();
|
|
1394
|
+
for (const name of a) set.add(name);
|
|
1395
|
+
for (const name of b) set.add(name);
|
|
1396
|
+
return [...set].sort((x, y) => x < y ? -1 : x > y ? 1 : 0);
|
|
1397
|
+
}
|
|
1398
|
+
const COLUMN_RENAME_UNMATCHED_CODE = "MIGRATION.COLUMN_RENAME_UNMATCHED";
|
|
1399
|
+
function tableLabel$1(namespaceId, table) {
|
|
1400
|
+
return namespaceId === void 0 || namespaceId === UNBOUND_NAMESPACE_ID ? table : `${namespaceId}.${table}`;
|
|
1401
|
+
}
|
|
1402
|
+
function columnLabel(namespaceId, table, column) {
|
|
1403
|
+
return `"${tableLabel$1(namespaceId, table)}"."${column}"`;
|
|
1404
|
+
}
|
|
1405
|
+
function declaresColumn(contract, namespaceId, table, column) {
|
|
1406
|
+
const columns = contract.storage.namespaces[namespaceId]?.entries.table?.[table]?.columns;
|
|
1407
|
+
return columns !== void 0 && Object.hasOwn(columns, column);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Refuses a `renameColumn` call that does not match the migration's contracts, with
|
|
1411
|
+
* `MIGRATION.COLUMN_RENAME_UNMATCHED`.
|
|
1412
|
+
*/
|
|
1413
|
+
function unmatchedColumnRename(rename, reason) {
|
|
1414
|
+
return sqlFamilyError(COLUMN_RENAME_UNMATCHED_CODE, `renameColumn ${columnLabel(rename.namespaceId, rename.table, rename.from)} to "${rename.to}" does not match the migration's contracts: ${reason}.`, {
|
|
1415
|
+
why: "renameColumn must name a table and a column as the migration's earlier rename operations leave them, and a new column name that the end contract has on that table and that the table does not have yet. Order the rename calls in the sequence the renames happen, and check the names of the table, the column and the namespace.",
|
|
1416
|
+
meta: {
|
|
1417
|
+
table: rename.table,
|
|
1418
|
+
from: rename.from,
|
|
1419
|
+
to: rename.to
|
|
1420
|
+
}
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* Checks a column rename: the table must exist in `previous` (in exactly one namespace when the
|
|
1425
|
+
* namespace is not given) with the old column and without the new one, and the end contract must
|
|
1426
|
+
* have the new column on that table.
|
|
1427
|
+
*/
|
|
1428
|
+
function checkColumnRename(previous, endContract, rename) {
|
|
1429
|
+
const namespaceIds = rename.namespaceId === void 0 ? previous.namespacesWithTable(rename.table) : previous.hasTable(rename.namespaceId, rename.table) ? [rename.namespaceId] : [];
|
|
1430
|
+
const [namespaceId, ...others] = namespaceIds;
|
|
1431
|
+
if (namespaceId === void 0) return notOk({ kind: "tableMissing" });
|
|
1432
|
+
if (others.length > 0) return notOk({
|
|
1433
|
+
kind: "tableInManyNamespaces",
|
|
1434
|
+
namespaceIds
|
|
1435
|
+
});
|
|
1436
|
+
if (!previous.hasColumn(namespaceId, rename.table, rename.from)) return notOk({
|
|
1437
|
+
kind: "columnMissing",
|
|
1438
|
+
namespaceId
|
|
1439
|
+
});
|
|
1440
|
+
const [taken] = previous.columnsNamed(namespaceId, rename.table, rename.to).filter((column) => column !== rename.from);
|
|
1441
|
+
if (taken !== void 0) return notOk({
|
|
1442
|
+
kind: "nameTaken",
|
|
1443
|
+
namespaceId,
|
|
1444
|
+
taken
|
|
1445
|
+
});
|
|
1446
|
+
if (!declaresColumn(endContract, namespaceId, rename.table, rename.to)) return notOk({
|
|
1447
|
+
kind: "notInEndContract",
|
|
1448
|
+
namespaceId
|
|
1449
|
+
});
|
|
1450
|
+
return ok({
|
|
1451
|
+
namespaceId,
|
|
1452
|
+
table: rename.table,
|
|
1453
|
+
from: rename.from,
|
|
1454
|
+
to: rename.to
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
/** The reason `MIGRATION.COLUMN_RENAME_UNMATCHED` gives for a mismatch. */
|
|
1458
|
+
function columnRenameMismatchReason(rename, mismatch) {
|
|
1459
|
+
switch (mismatch.kind) {
|
|
1460
|
+
case "tableMissing": return `table "${tableLabel$1(rename.namespaceId, rename.table)}" does not exist at this point of the migration`;
|
|
1461
|
+
case "tableInManyNamespaces": return `table "${rename.table}" is declared in more than one namespace (${mismatch.namespaceIds.join(", ")}); name its namespace`;
|
|
1462
|
+
case "columnMissing": return `column ${columnLabel(mismatch.namespaceId, rename.table, rename.from)} does not exist at this point of the migration`;
|
|
1463
|
+
case "nameTaken": return `column ${columnLabel(mismatch.namespaceId, rename.table, rename.to)} already exists at this point of the migration${mismatch.taken === rename.to ? "" : ` as "${mismatch.taken}"`}`;
|
|
1464
|
+
case "notInEndContract": return `column ${columnLabel(mismatch.namespaceId, rename.table, rename.to)} does not exist in the end contract`;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Resolves a column a migration renames with {@link checkColumnRename}, refusing a mismatch with
|
|
1469
|
+
* `MIGRATION.COLUMN_RENAME_UNMATCHED`.
|
|
1470
|
+
*/
|
|
1471
|
+
function resolveColumnRenameAgainst(previous, endContract, rename) {
|
|
1472
|
+
const checked = checkColumnRename(previous, endContract, rename);
|
|
1473
|
+
if (!checked.ok) return notOk(unmatchedColumnRename(rename, columnRenameMismatchReason(rename, checked.failure)));
|
|
1474
|
+
return ok(checked.value);
|
|
1475
|
+
}
|
|
1476
|
+
const TABLE_RENAME_UNMATCHED_CODE = "MIGRATION.TABLE_RENAME_UNMATCHED";
|
|
1477
|
+
function tableLabel(namespaceId, tableName) {
|
|
1478
|
+
return namespaceId === void 0 || namespaceId === UNBOUND_NAMESPACE_ID ? tableName : `${namespaceId}.${tableName}`;
|
|
1479
|
+
}
|
|
1480
|
+
function declares(contract, namespaceId, tableName) {
|
|
1481
|
+
return Object.hasOwn(contract.storage.namespaces[namespaceId]?.entries.table ?? {}, tableName);
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* Refuses a `renameTable` call that does not match the migration's contracts, with
|
|
1485
|
+
* `MIGRATION.TABLE_RENAME_UNMATCHED`.
|
|
1486
|
+
*/
|
|
1487
|
+
function unmatchedTableRename(rename, reason) {
|
|
1488
|
+
return sqlFamilyError(TABLE_RENAME_UNMATCHED_CODE, `renameTable "${tableLabel(rename.namespaceId, rename.from)}" to "${rename.to}" does not match the migration's contracts: ${reason}.`, {
|
|
1489
|
+
why: "renameTable must name a table as the migration's earlier rename operations leave it, and a new name that the end contract has and that no earlier rename operation has already produced. Order the renameTable calls in the sequence the renames happen, make the rename its own schema change, and check the spelling, the table and the namespace.",
|
|
1490
|
+
meta: {
|
|
1491
|
+
from: rename.from,
|
|
1492
|
+
to: rename.to
|
|
1493
|
+
}
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Checks a table rename: the table must exist in `previous` (in exactly one namespace when the
|
|
1498
|
+
* namespace is not given), and the new name must exist in the end contract and not in `previous`.
|
|
1499
|
+
*/
|
|
1500
|
+
function checkTableRename(previous, endContract, rename) {
|
|
1501
|
+
const namespaceIds = rename.namespaceId === void 0 ? previous.namespacesWithTable(rename.from) : previous.hasTable(rename.namespaceId, rename.from) ? [rename.namespaceId] : [];
|
|
1502
|
+
const [namespaceId, ...others] = namespaceIds;
|
|
1503
|
+
if (namespaceId === void 0) return notOk({ kind: "tableMissing" });
|
|
1504
|
+
if (others.length > 0) return notOk({
|
|
1505
|
+
kind: "tableInManyNamespaces",
|
|
1506
|
+
namespaceIds
|
|
1507
|
+
});
|
|
1508
|
+
const [taken] = previous.tablesNamed(namespaceId, rename.to).filter((table) => table !== rename.from);
|
|
1509
|
+
if (taken !== void 0) return notOk({
|
|
1510
|
+
kind: "nameTaken",
|
|
1511
|
+
namespaceId,
|
|
1512
|
+
taken
|
|
1513
|
+
});
|
|
1514
|
+
if (!declares(endContract, namespaceId, rename.to)) return notOk({
|
|
1515
|
+
kind: "notInEndContract",
|
|
1516
|
+
namespaceId
|
|
1517
|
+
});
|
|
1518
|
+
return ok({
|
|
1519
|
+
namespaceId,
|
|
1520
|
+
from: rename.from,
|
|
1521
|
+
to: rename.to
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
/** The reason `MIGRATION.TABLE_RENAME_UNMATCHED` gives for a mismatch. */
|
|
1525
|
+
function tableRenameMismatchReason(rename, mismatch) {
|
|
1526
|
+
switch (mismatch.kind) {
|
|
1527
|
+
case "tableMissing": return `table "${tableLabel(rename.namespaceId, rename.from)}" does not exist at this point of the migration`;
|
|
1528
|
+
case "tableInManyNamespaces": return `table "${rename.from}" is declared in more than one namespace (${mismatch.namespaceIds.join(", ")}); name its namespace`;
|
|
1529
|
+
case "nameTaken": return `table "${tableLabel(mismatch.namespaceId, rename.to)}" already exists at this point of the migration${mismatch.taken === rename.to ? "" : ` as "${mismatch.taken}"`}`;
|
|
1530
|
+
case "notInEndContract": return `table "${tableLabel(mismatch.namespaceId, rename.to)}" does not exist in the end contract`;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Resolves a table a migration renames with {@link checkTableRename}, refusing a mismatch with
|
|
1535
|
+
* `MIGRATION.TABLE_RENAME_UNMATCHED`.
|
|
1536
|
+
*/
|
|
1537
|
+
function resolveTableRenameAgainst(previous, endContract, rename) {
|
|
1538
|
+
const checked = checkTableRename(previous, endContract, rename);
|
|
1539
|
+
if (!checked.ok) return notOk(unmatchedTableRename(rename, tableRenameMismatchReason(rename, checked.failure)));
|
|
1540
|
+
return ok(checked.value);
|
|
1541
|
+
}
|
|
1542
|
+
/** Whether a model's storage names a table, as a model a SQL contract stores in a table does. */
|
|
1543
|
+
function isSqlModelStorage(storage) {
|
|
1544
|
+
return typeof storage["table"] === "string" && typeof storage["namespaceId"] === "string" && typeof storage["fields"] === "object" && storage["fields"] !== null;
|
|
1545
|
+
}
|
|
1546
|
+
/** The storage of a model the contract stores in a table, or `undefined` for any other model. */
|
|
1547
|
+
function sqlModelStorage(contract, coordinate) {
|
|
1548
|
+
const storage = contract.domain.namespaces[coordinate.namespaceId]?.models[coordinate.model]?.storage;
|
|
1549
|
+
return storage !== void 0 && isSqlModelStorage(storage) ? storage : void 0;
|
|
1550
|
+
}
|
|
1551
|
+
function modelTable(contract, coordinate) {
|
|
1552
|
+
const storage = sqlModelStorage(contract, coordinate);
|
|
1553
|
+
return storage === void 0 ? void 0 : {
|
|
1554
|
+
namespaceId: storage.namespaceId,
|
|
1555
|
+
table: storage.table
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* The storage effect of a model rename: the origin model's table compared with the destination
|
|
1560
|
+
* model's. Equal tables mean the rename needs no storage change.
|
|
1561
|
+
*/
|
|
1562
|
+
function modelRenameStorageEffect(statement, fromContract, contract) {
|
|
1563
|
+
const from = modelTable(fromContract, statement.from);
|
|
1564
|
+
if (from === void 0) return notOk({
|
|
1565
|
+
kind: "noTable",
|
|
1566
|
+
model: statement.from
|
|
1567
|
+
});
|
|
1568
|
+
const to = modelTable(contract, statement.to);
|
|
1569
|
+
if (to === void 0) return notOk({
|
|
1570
|
+
kind: "noTable",
|
|
1571
|
+
model: statement.to
|
|
1572
|
+
});
|
|
1573
|
+
if (from.namespaceId !== to.namespaceId) return ok({
|
|
1574
|
+
kind: "moveNamespace",
|
|
1575
|
+
from,
|
|
1576
|
+
to
|
|
1577
|
+
});
|
|
1578
|
+
if (from.table === to.table) return ok({ kind: "unchanged" });
|
|
1579
|
+
return ok({
|
|
1580
|
+
kind: "renameTable",
|
|
1581
|
+
rename: {
|
|
1582
|
+
namespaceId: from.namespaceId,
|
|
1583
|
+
from: from.table,
|
|
1584
|
+
to: to.table
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
function fieldColumn(contract, coordinate) {
|
|
1589
|
+
const fields = sqlModelStorage(contract, coordinate)?.fields;
|
|
1590
|
+
return fields !== void 0 && Object.hasOwn(fields, coordinate.field) ? fields[coordinate.field]?.column : void 0;
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* The storage effect of a field rename: the origin field's column compared with the destination
|
|
1594
|
+
* field's. A relation field has no column on either side, and equal columns need no change.
|
|
1595
|
+
*/
|
|
1596
|
+
function fieldRenameStorageEffect(statement, fromContract, contract) {
|
|
1597
|
+
const table = modelTable(fromContract, statement.from);
|
|
1598
|
+
if (table === void 0) return notOk({
|
|
1599
|
+
kind: "noTable",
|
|
1600
|
+
model: statement.from
|
|
1601
|
+
});
|
|
1602
|
+
const from = fieldColumn(fromContract, statement.from);
|
|
1603
|
+
const to = fieldColumn(contract, statement.to);
|
|
1604
|
+
if (from === void 0 && to === void 0) return ok({ kind: "unchanged" });
|
|
1605
|
+
if (from === void 0 || to === void 0) return notOk({ kind: "columnOnOneSide" });
|
|
1606
|
+
if (from === to) return ok({ kind: "unchanged" });
|
|
1607
|
+
return ok({
|
|
1608
|
+
kind: "renameColumn",
|
|
1609
|
+
table,
|
|
1610
|
+
from,
|
|
1611
|
+
to
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
function operationsOf(call) {
|
|
1615
|
+
return [call, ...call.companions];
|
|
1616
|
+
}
|
|
1617
|
+
/** The origins of a plan whose statements renamed and moved nothing. */
|
|
1618
|
+
const NO_RENAMED_ORIGINS = {
|
|
1619
|
+
originTable: (current) => current,
|
|
1620
|
+
originColumn: (_origin, column) => column
|
|
1621
|
+
};
|
|
1622
|
+
const DRIFTED = "so the database has drifted from that contract. Inspect it with prisma db schema, or leave out this statement.";
|
|
1623
|
+
const UNMATCHED = "A statement plans the rename a hand-written rename call makes, and that call does not match the contracts. Leave out this statement.";
|
|
1624
|
+
function statementRefused(statement, summary, why, location, refusedOperationClass) {
|
|
1625
|
+
return {
|
|
1626
|
+
kind: "statementRefused",
|
|
1627
|
+
summary,
|
|
1628
|
+
why,
|
|
1629
|
+
refusedStatement: statement,
|
|
1630
|
+
...refusedOperationClass === void 0 ? {} : { refusedOperationClass },
|
|
1631
|
+
...location === void 0 ? {} : { location: {
|
|
1632
|
+
namespaceId: location.namespaceId,
|
|
1633
|
+
entityKind: "table",
|
|
1634
|
+
entityName: location.table,
|
|
1635
|
+
...location.column === void 0 ? {} : { column: location.column }
|
|
1636
|
+
} }
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
function tableControlPolicy(contract, table) {
|
|
1640
|
+
const node = contract.storage.namespaces[table.namespaceId]?.entries.table?.[table.table];
|
|
1641
|
+
return governingControlPolicy({ explicitNodeControlPolicy: StorageTable.is(node) ? node.control : void 0 }, contract.defaultControlPolicy);
|
|
1642
|
+
}
|
|
1643
|
+
function tableKey(table) {
|
|
1644
|
+
return JSON.stringify([table.namespaceId, table.table]);
|
|
1645
|
+
}
|
|
1646
|
+
/** A contract space other than the one planned that declares the namespace, if any. */
|
|
1647
|
+
function otherSpaceDeclaring(ownership, namespaceId) {
|
|
1648
|
+
return (ownership.oracle?.namespaceDeclaringSpaces(namespaceId) ?? []).find((spaceId) => spaceId !== ownership.spaceId);
|
|
1649
|
+
}
|
|
1650
|
+
function withoutNamespacesRefused(statement, label) {
|
|
1651
|
+
return statementRefused(statement, `${label}: the database target keeps every table in one namespace`, "This database target has no namespaces, so a statement cannot rename a namespace or move a model to another one. Leave out this statement.", void 0);
|
|
1652
|
+
}
|
|
1653
|
+
function namespaceText(namespaceId) {
|
|
1654
|
+
return namespaceId === UNBOUND_NAMESPACE_ID ? "the unbound namespace" : `namespace "${namespaceId}"`;
|
|
1655
|
+
}
|
|
1656
|
+
function qualifiedTable(table) {
|
|
1657
|
+
return table.namespaceId === UNBOUND_NAMESPACE_ID ? table.table : `${table.namespaceId}.${table.table}`;
|
|
1658
|
+
}
|
|
1659
|
+
function tableRenameRefused(statement, label, rename, mismatch) {
|
|
1660
|
+
switch (mismatch.kind) {
|
|
1661
|
+
case "tableMissing": return statementRefused(statement, `${label}: the schema being planned from has no table "${rename.from}"`, `The database has no table "${rename.from}", although the contract it was last updated to names it, ${DRIFTED}`, {
|
|
1662
|
+
namespaceId: rename.namespaceId,
|
|
1663
|
+
table: rename.from
|
|
1664
|
+
});
|
|
1665
|
+
case "nameTaken": return statementRefused(statement, `${label}: the schema being planned from already has a table "${rename.to}"${mismatch.taken === rename.to ? "" : `, as "${mismatch.taken}"`}`, `A rename cannot replace a table that already exists. Rename or drop table "${mismatch.taken}" first, or leave out this statement.`, {
|
|
1666
|
+
namespaceId: rename.namespaceId,
|
|
1667
|
+
table: rename.to
|
|
1668
|
+
});
|
|
1669
|
+
case "tableInManyNamespaces":
|
|
1670
|
+
case "notInEndContract": return statementRefused(statement, `${label}: ${tableRenameMismatchReason(rename, mismatch)}`, UNMATCHED, {
|
|
1671
|
+
namespaceId: rename.namespaceId,
|
|
1672
|
+
table: rename.from
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
function columnRenameRefused(statement, label, rename, mismatch) {
|
|
1677
|
+
const table = {
|
|
1678
|
+
namespaceId: rename.namespaceId,
|
|
1679
|
+
table: rename.table
|
|
1680
|
+
};
|
|
1681
|
+
switch (mismatch.kind) {
|
|
1682
|
+
case "tableMissing": return statementRefused(statement, `${label}: the schema being planned from has no table "${rename.table}"`, `The database has no table "${rename.table}", although the contract it was last updated to names it, ${DRIFTED}`, table);
|
|
1683
|
+
case "columnMissing": return statementRefused(statement, `${label}: the schema being planned from has no column "${rename.from}" on table "${rename.table}"`, `The database has no column "${rename.from}" on table "${rename.table}", although the contract it was last updated to names it, ${DRIFTED}`, {
|
|
1684
|
+
...table,
|
|
1685
|
+
column: rename.from
|
|
1686
|
+
});
|
|
1687
|
+
case "nameTaken": return statementRefused(statement, `${label}: the schema being planned from already has a column "${mismatch.taken}" on table "${rename.table}"`, `A rename cannot replace a column that already exists. Rename or drop column "${mismatch.taken}" of table "${rename.table}" first, or leave out this statement.`, {
|
|
1688
|
+
...table,
|
|
1689
|
+
column: rename.to
|
|
1690
|
+
});
|
|
1691
|
+
case "tableInManyNamespaces":
|
|
1692
|
+
case "notInEndContract": return statementRefused(statement, `${label}: ${columnRenameMismatchReason(rename, mismatch)}`, UNMATCHED, {
|
|
1693
|
+
...table,
|
|
1694
|
+
column: rename.from
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
var StatementPlanner = class {
|
|
1699
|
+
#fromContract;
|
|
1700
|
+
#contract;
|
|
1701
|
+
#policy;
|
|
1702
|
+
#target;
|
|
1703
|
+
/** Where each table renamed or moved so far is in the working schema, keyed by its origin place. */
|
|
1704
|
+
#renamedTables = /* @__PURE__ */ new Map();
|
|
1705
|
+
/** The origin place of each table renamed or moved so far, keyed as `#renamedTables`. */
|
|
1706
|
+
#originTables = /* @__PURE__ */ new Map();
|
|
1707
|
+
/** Each column renamed so far: its origin table, and its name before and after. */
|
|
1708
|
+
#renamedColumns = [];
|
|
1709
|
+
/** The name each namespace renamed so far has in the working schema, keyed by its origin name. */
|
|
1710
|
+
#renamedNamespaces = /* @__PURE__ */ new Map();
|
|
1711
|
+
#ownership;
|
|
1712
|
+
calls = [];
|
|
1713
|
+
/** How many operations the calls planned so far lower to. */
|
|
1714
|
+
#operationCount = 0;
|
|
1715
|
+
tableRenames = [];
|
|
1716
|
+
columnRenames = [];
|
|
1717
|
+
constructor(input) {
|
|
1718
|
+
this.#fromContract = input.fromContract;
|
|
1719
|
+
this.#contract = input.contract;
|
|
1720
|
+
this.#policy = input.policy;
|
|
1721
|
+
this.#target = input.target;
|
|
1722
|
+
this.#ownership = input.ownership;
|
|
1723
|
+
}
|
|
1724
|
+
/** Plans one statement; the result is the positions of the operations it accounts for. */
|
|
1725
|
+
plan(statement) {
|
|
1726
|
+
switch (statement.entity) {
|
|
1727
|
+
case "namespace": return this.#planNamespace(statement);
|
|
1728
|
+
case "model": return this.#planModel(statement);
|
|
1729
|
+
case "field": return this.#planField(statement);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
/** Where a table of the origin is in the working schema, after the renames planned so far. */
|
|
1733
|
+
#current(origin) {
|
|
1734
|
+
const table = this.#renamedTables.get(tableKey(origin)) ?? origin;
|
|
1735
|
+
return {
|
|
1736
|
+
namespaceId: this.#renamedNamespaces.get(table.namespaceId) ?? table.namespaceId,
|
|
1737
|
+
table: table.table
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
#planNamespace(statement) {
|
|
1741
|
+
const rename = {
|
|
1742
|
+
from: statement.from,
|
|
1743
|
+
to: statement.to
|
|
1744
|
+
};
|
|
1745
|
+
const label = `Cannot rename namespace "${rename.from}" to "${rename.to}"`;
|
|
1746
|
+
const namespaces = this.#target.namespaces;
|
|
1747
|
+
if (namespaces === void 0) return notOk(withoutNamespacesRefused(statement, label));
|
|
1748
|
+
const movePerModel = `--rename ${rename.from}.<Model>:${rename.to}.<Model>`;
|
|
1749
|
+
const refusal = namespaces.namespaceRenameRefusal(rename);
|
|
1750
|
+
if (refusal !== void 0) return notOk(statementRefused(statement, `${label}: ${refusal.reason}`, refusal.fix, void 0));
|
|
1751
|
+
const otherSpace = this.#ownership === void 0 ? void 0 : otherSpaceDeclaring(this.#ownership, rename.from);
|
|
1752
|
+
if (otherSpace !== void 0) return notOk(statementRefused(statement, `${label}: contract space "${otherSpace}" also declares it`, `Renaming the namespace would rename it under contract space "${otherSpace}" too, whose contract still names it "${rename.from}". Leave out this statement, or move each model with ${movePerModel} instead.`, void 0));
|
|
1753
|
+
const tables = Object.keys(this.#contract.storage.namespaces[rename.to]?.entries.table ?? {});
|
|
1754
|
+
for (const table of tables) {
|
|
1755
|
+
const controlPolicy = tableControlPolicy(this.#contract, {
|
|
1756
|
+
namespaceId: rename.to,
|
|
1757
|
+
table
|
|
1758
|
+
});
|
|
1759
|
+
if (controlPolicy === "managed") continue;
|
|
1760
|
+
return notOk(statementRefused(statement, `${label}: table "${table}" has control policy "${controlPolicy}"`, `Statements rename only namespaces whose tables all have control policy "managed"; table "${table}" is "${controlPolicy}". Make the change in the database yourself and leave out this statement.`, {
|
|
1761
|
+
namespaceId: rename.to,
|
|
1762
|
+
table
|
|
1763
|
+
}));
|
|
1764
|
+
}
|
|
1765
|
+
const schemaTables = this.#target.tables();
|
|
1766
|
+
if (!schemaTables.hasNamespace(rename.from)) return notOk(statementRefused(statement, `${label}: the schema being planned from has no namespace "${rename.from}"`, `The database has no namespace "${rename.from}", although the contract it was last updated to names it, ${DRIFTED}`, void 0));
|
|
1767
|
+
if (schemaTables.hasNamespace(rename.to)) return notOk(statementRefused(statement, `${label}: the database already has a namespace "${rename.to}"`, `The database already has a namespace "${rename.to}", which the contract it was last updated to does not declare. If it holds nothing, drop or rename it in the database yourself; otherwise move each model with ${movePerModel} instead of this statement.`, void 0));
|
|
1768
|
+
const planned = this.#emit(statement, label, void 0, namespaces.renameNamespaceCall(rename));
|
|
1769
|
+
if (planned.ok) this.#renamedNamespaces.set(rename.from, rename.to);
|
|
1770
|
+
return planned;
|
|
1771
|
+
}
|
|
1772
|
+
#controlPolicyRefusal(statement, label, destinationTable) {
|
|
1773
|
+
const controlPolicy = tableControlPolicy(this.#contract, destinationTable);
|
|
1774
|
+
if (controlPolicy === "managed") return void 0;
|
|
1775
|
+
return statementRefused(statement, `${label}: the table's control policy is "${controlPolicy}"`, `Statements rename only tables, and columns of tables, whose control policy is "managed"; table "${destinationTable.table}" is "${controlPolicy}". Make the change in the database yourself and leave out this statement.`, destinationTable);
|
|
1776
|
+
}
|
|
1777
|
+
#emit(statement, label, location, call) {
|
|
1778
|
+
const refused = operationsOf(call).map((operation) => operation.operationClass).find((operationClass) => !this.#policy.allowedOperationClasses.includes(operationClass));
|
|
1779
|
+
if (refused !== void 0) return notOk(statementRefused(statement, `${label}: the plan does not allow "${refused}" operations`, `The rename produces a "${refused}" operation, and this command plans only ${this.#policy.allowedOperationClasses.map((c) => `"${c}"`).join(", ")} operations. Leave out this statement, or make the change with a command that allows "${refused}" operations, such as migration plan.`, location, refused));
|
|
1780
|
+
this.#target.apply(call);
|
|
1781
|
+
this.calls.push(call);
|
|
1782
|
+
const first = this.#operationCount;
|
|
1783
|
+
this.#operationCount += operationsOf(call).length;
|
|
1784
|
+
return ok(operationsOf(call).map((_, offset) => first + offset));
|
|
1785
|
+
}
|
|
1786
|
+
#planModel(statement) {
|
|
1787
|
+
const worked = modelRenameStorageEffect(statement, this.#fromContract, this.#contract);
|
|
1788
|
+
if (!worked.ok) return notOk(statementRefused(statement, `Model "${modelDisplayName(worked.failure.model)}" has no table in its contract`, `A model rename renames the model's table, and model "${modelDisplayName(worked.failure.model)}" has none in its contract. Leave out this statement.`, void 0));
|
|
1789
|
+
const effect = worked.value;
|
|
1790
|
+
if (effect.kind === "unchanged") return ok([]);
|
|
1791
|
+
const [origin, destination] = effect.kind === "moveNamespace" ? [effect.from, effect.to] : [{
|
|
1792
|
+
namespaceId: effect.rename.namespaceId,
|
|
1793
|
+
table: effect.rename.from
|
|
1794
|
+
}, {
|
|
1795
|
+
namespaceId: effect.rename.namespaceId,
|
|
1796
|
+
table: effect.rename.to
|
|
1797
|
+
}];
|
|
1798
|
+
const current = this.#current(origin);
|
|
1799
|
+
if (current.namespaceId === destination.namespaceId) {
|
|
1800
|
+
if (current.table === destination.table) return ok([]);
|
|
1801
|
+
return this.#planTableRename(statement, origin, {
|
|
1802
|
+
namespaceId: destination.namespaceId,
|
|
1803
|
+
from: current.table,
|
|
1804
|
+
to: destination.table
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
return this.#planMove(statement, origin, current, destination);
|
|
1808
|
+
}
|
|
1809
|
+
#planTableRename(statement, origin, rename) {
|
|
1810
|
+
const destinationTable = {
|
|
1811
|
+
namespaceId: rename.namespaceId,
|
|
1812
|
+
table: rename.to
|
|
1813
|
+
};
|
|
1814
|
+
const label = `Cannot rename table "${rename.from}" to "${rename.to}"`;
|
|
1815
|
+
const policyRefusal = this.#controlPolicyRefusal(statement, label, destinationTable);
|
|
1816
|
+
if (policyRefusal !== void 0) return notOk(policyRefusal);
|
|
1817
|
+
const checked = checkTableRename(this.#target.tables(), this.#contract, rename);
|
|
1818
|
+
if (!checked.ok) return notOk(tableRenameRefused(statement, label, rename, checked.failure));
|
|
1819
|
+
const planned = this.#emit(statement, label, destinationTable, this.#target.renameTableCall(rename));
|
|
1820
|
+
if (planned.ok) {
|
|
1821
|
+
this.tableRenames.push(rename);
|
|
1822
|
+
this.#renamedTables.set(tableKey(origin), destinationTable);
|
|
1823
|
+
this.#originTables.set(tableKey(origin), origin);
|
|
1824
|
+
}
|
|
1825
|
+
return planned;
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Moves the table to the destination namespace under its own name, creating the namespace first
|
|
1829
|
+
* when the working schema lacks it, then renames it there when the destination names it
|
|
1830
|
+
* differently. The statement accounts for the move and the rename, not the namespace's creation.
|
|
1831
|
+
*/
|
|
1832
|
+
#planMove(statement, origin, from, to) {
|
|
1833
|
+
const label = `Cannot move table "${from.table}" from ${namespaceText(from.namespaceId)} to ${namespaceText(to.namespaceId)}`;
|
|
1834
|
+
const namespaces = this.#target.namespaces;
|
|
1835
|
+
if (namespaces === void 0) return notOk(withoutNamespacesRefused(statement, label));
|
|
1836
|
+
if (to.namespaceId === UNBOUND_NAMESPACE_ID) return notOk(statementRefused(statement, `Cannot move table "${from.table}" from ${namespaceText(from.namespaceId)} to the unbound namespace: the unbound namespace has no fixed place in the database`, `A table of the unbound namespace lives wherever the connection that uses it looks, so a migration cannot move a table there. Move the table yourself and leave out this statement, or keep the model in ${namespaceText(from.namespaceId)}.`, from));
|
|
1837
|
+
const moved = {
|
|
1838
|
+
namespaceId: to.namespaceId,
|
|
1839
|
+
table: from.table
|
|
1840
|
+
};
|
|
1841
|
+
const policyRefusal = this.#controlPolicyRefusal(statement, label, to);
|
|
1842
|
+
if (policyRefusal !== void 0) return notOk(policyRefusal);
|
|
1843
|
+
const tables = this.#target.tables();
|
|
1844
|
+
if (!tables.hasTable(from.namespaceId, from.table)) return notOk(statementRefused(statement, `${label}: the schema being planned from has no table "${qualifiedTable(from)}"`, `The database has no table "${qualifiedTable(from)}", although the contract it was last updated to names it, ${DRIFTED}`, from));
|
|
1845
|
+
const [taken] = [...tables.tablesNamed(to.namespaceId, from.table), ...tables.tablesNamed(to.namespaceId, to.table)];
|
|
1846
|
+
if (taken !== void 0) {
|
|
1847
|
+
const takenTable = qualifiedTable({
|
|
1848
|
+
namespaceId: to.namespaceId,
|
|
1849
|
+
table: taken
|
|
1850
|
+
});
|
|
1851
|
+
return notOk(statementRefused(statement, `${label}: the schema being planned from already has a table "${takenTable}"`, `A move cannot replace a table that already exists. Rename, move or drop table "${takenTable}" first, or leave out this statement.`, {
|
|
1852
|
+
namespaceId: to.namespaceId,
|
|
1853
|
+
table: taken
|
|
1854
|
+
}));
|
|
1855
|
+
}
|
|
1856
|
+
for (const call of namespaces.createNamespaceCalls(to.namespaceId)) {
|
|
1857
|
+
const created = this.#emit(statement, label, moved, call);
|
|
1858
|
+
if (!created.ok) return created;
|
|
1859
|
+
}
|
|
1860
|
+
const planned = this.#emit(statement, label, moved, namespaces.moveTableCall({
|
|
1861
|
+
table: from.table,
|
|
1862
|
+
from: from.namespaceId,
|
|
1863
|
+
to: to.namespaceId
|
|
1864
|
+
}));
|
|
1865
|
+
if (!planned.ok) return planned;
|
|
1866
|
+
this.#renamedTables.set(tableKey(origin), moved);
|
|
1867
|
+
this.#originTables.set(tableKey(origin), origin);
|
|
1868
|
+
if (to.table === from.table) return planned;
|
|
1869
|
+
const renamed = this.#planTableRename(statement, origin, {
|
|
1870
|
+
namespaceId: to.namespaceId,
|
|
1871
|
+
from: from.table,
|
|
1872
|
+
to: to.table
|
|
1873
|
+
});
|
|
1874
|
+
if (!renamed.ok) return renamed;
|
|
1875
|
+
return ok([...planned.value, ...renamed.value]);
|
|
1876
|
+
}
|
|
1877
|
+
#planField(statement) {
|
|
1878
|
+
const worked = fieldRenameStorageEffect(statement, this.#fromContract, this.#contract);
|
|
1879
|
+
if (!worked.ok && worked.failure.kind === "noTable") return notOk(statementRefused(statement, `Model "${modelDisplayName(worked.failure.model)}" has no table in its contract`, `A field rename renames the field's column, and model "${modelDisplayName(worked.failure.model)}" has no table in its contract. Leave out this statement.`, void 0));
|
|
1880
|
+
if (!worked.ok) return notOk(statementRefused(statement, `Field "${modelDisplayName(statement.from)}.${statement.from.field}" has a column on one side only of ${describeMigrationStatement(statement, this.#fromContract, this.#contract)}`, "A field rename renames a column or changes nothing in storage, and this field gains or loses its column. Leave out this statement and plan the change without it.", void 0));
|
|
1881
|
+
const effect = worked.value;
|
|
1882
|
+
if (effect.kind === "unchanged") return ok([]);
|
|
1883
|
+
const current = this.#current(effect.table);
|
|
1884
|
+
const table = current.table;
|
|
1885
|
+
const destinationTable = modelTable(this.#contract, statement.to);
|
|
1886
|
+
if (destinationTable !== void 0 && (destinationTable.namespaceId !== current.namespaceId || destinationTable.table !== table)) {
|
|
1887
|
+
const tableRename = {
|
|
1888
|
+
namespaceId: current.namespaceId,
|
|
1889
|
+
from: table,
|
|
1890
|
+
to: destinationTable.table
|
|
1891
|
+
};
|
|
1892
|
+
const model = modelDisplayName(statement.to);
|
|
1893
|
+
const fieldText = `${model}.${statement.from.field}:${model}.${statement.to.field}`;
|
|
1894
|
+
return notOk(statementRefused(statement, `Cannot rename column "${table}"."${effect.from}": the model's table changes from "${table}" to "${destinationTable.table}", and no statement renames the table`, `The column would be renamed on a table the plan then drops and creates under the new name, and a migration that only changes the table name is planned the same way. With migration plan, rename the table by hand in its own migration first. Change the contract so that only the table name changes, and run prisma contract emit. Run prisma migration new --name <name> --from <hash of the migration the database is at>, add ${this.#target.renderTableRename(tableRename)} to the operations of its migration.ts, and run node on that migration.ts to write its ops.json. Then change the contract to its final form, emit it, and plan the field rename with --from that migration. With db update, change the contract so that only the table name changes, and run prisma contract emit. Rename the table in the database yourself with ${this.#target.tableRenameByHand(tableRename).join("; ")}, check that prisma db update --dry-run plans no drop of a table or column, and run prisma db update to store that contract. Then emit the final contract and run prisma db update --rename ${fieldText}.`, {
|
|
1895
|
+
namespaceId: current.namespaceId,
|
|
1896
|
+
table,
|
|
1897
|
+
column: effect.from
|
|
1898
|
+
}));
|
|
1899
|
+
}
|
|
1900
|
+
const rename = {
|
|
1901
|
+
namespaceId: current.namespaceId,
|
|
1902
|
+
table,
|
|
1903
|
+
from: effect.from,
|
|
1904
|
+
to: effect.to
|
|
1905
|
+
};
|
|
1906
|
+
const tableLocation = {
|
|
1907
|
+
namespaceId: rename.namespaceId,
|
|
1908
|
+
table
|
|
1909
|
+
};
|
|
1910
|
+
const label = `Cannot rename column "${table}"."${rename.from}" to "${rename.to}"`;
|
|
1911
|
+
const policyRefusal = this.#controlPolicyRefusal(statement, label, tableLocation);
|
|
1912
|
+
if (policyRefusal !== void 0) return notOk(policyRefusal);
|
|
1913
|
+
const checked = checkColumnRename(this.#target.tables(), this.#contract, rename);
|
|
1914
|
+
if (!checked.ok) return notOk(columnRenameRefused(statement, label, rename, checked.failure));
|
|
1915
|
+
const planned = this.#emit(statement, label, {
|
|
1916
|
+
...tableLocation,
|
|
1917
|
+
column: rename.from
|
|
1918
|
+
}, this.#target.renameColumnCall(rename));
|
|
1919
|
+
if (planned.ok) {
|
|
1920
|
+
this.columnRenames.push(rename);
|
|
1921
|
+
this.#renamedColumns.push({
|
|
1922
|
+
origin: effect.table,
|
|
1923
|
+
from: effect.from,
|
|
1924
|
+
to: effect.to
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
return planned;
|
|
1928
|
+
}
|
|
1929
|
+
/** The origins of the working schema's tables and columns, as the statements left them. */
|
|
1930
|
+
origins() {
|
|
1931
|
+
const sameTable = (left, right) => left.namespaceId === right.namespaceId && left.table === right.table;
|
|
1932
|
+
return {
|
|
1933
|
+
originTable: (current) => {
|
|
1934
|
+
for (const origin of this.#originTables.values()) if (sameTable(this.#current(origin), current)) return origin;
|
|
1935
|
+
for (const [origin, renamed] of this.#renamedNamespaces) if (renamed === current.namespaceId) return {
|
|
1936
|
+
namespaceId: origin,
|
|
1937
|
+
table: current.table
|
|
1938
|
+
};
|
|
1939
|
+
return current;
|
|
1940
|
+
},
|
|
1941
|
+
originColumn: (origin, column) => this.#renamedColumns.find((renamed) => sameTable(renamed.origin, origin) && renamed.to === column)?.from ?? column
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
};
|
|
1945
|
+
/**
|
|
1946
|
+
* Plans the statements against a target's working schema, namespace renames first and the rest in
|
|
1947
|
+
* the order given: model and field statements name the origin and destination contracts, so the
|
|
1948
|
+
* namespace renames' place among them does not matter. Each model rename becomes a table rename
|
|
1949
|
+
* and each field rename a column rename, computed against the schema earlier statements left, then
|
|
1950
|
+
* applied to it. The first statement that cannot be planned fails the whole
|
|
1951
|
+
* plan with a `statementRefused` conflict that carries the statement.
|
|
1952
|
+
*/
|
|
1953
|
+
function planStatements(input) {
|
|
1954
|
+
const { fromContract } = input;
|
|
1955
|
+
const [first] = input.statements;
|
|
1956
|
+
if (first === void 0) return ok({
|
|
1957
|
+
calls: [],
|
|
1958
|
+
tableRenames: [],
|
|
1959
|
+
columnRenames: [],
|
|
1960
|
+
appliedStatements: [],
|
|
1961
|
+
origins: NO_RENAMED_ORIGINS
|
|
1962
|
+
});
|
|
1963
|
+
if (fromContract === null) return notOk(statementRefused(first, "Statements need an origin contract, and this plan has none", "A statement names models and fields of the origin contract, and this plan has none. Plan from a contract that has the old names, or leave out the statement.", void 0));
|
|
1964
|
+
const planner = new StatementPlanner({
|
|
1965
|
+
...input,
|
|
1966
|
+
fromContract,
|
|
1967
|
+
ownership: input.ownership
|
|
1968
|
+
});
|
|
1969
|
+
const appliedStatements = [];
|
|
1970
|
+
const namespacesFirst = [...input.statements.filter((statement) => statement.entity === "namespace"), ...input.statements.filter((statement) => statement.entity !== "namespace")];
|
|
1971
|
+
for (const statement of namespacesFirst) {
|
|
1972
|
+
const operationIndexes = planner.plan(statement);
|
|
1973
|
+
if (!operationIndexes.ok) return operationIndexes;
|
|
1974
|
+
appliedStatements.push({
|
|
1975
|
+
statement,
|
|
1976
|
+
operationIndexes: operationIndexes.value
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
return ok({
|
|
1980
|
+
calls: planner.calls,
|
|
1981
|
+
tableRenames: planner.tableRenames,
|
|
1982
|
+
columnRenames: planner.columnRenames,
|
|
1983
|
+
appliedStatements,
|
|
1984
|
+
origins: planner.origins()
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1987
|
+
/**
|
|
1988
|
+
* The column of each field a convert or backfill statement names, as the destination contract
|
|
1989
|
+
* stores it. These statements rename nothing, so the strategies that plan a field's change read
|
|
1990
|
+
* them instead of the working schema. A field with no column has none to list.
|
|
1991
|
+
*/
|
|
1992
|
+
function fieldStatementColumns(statements, contract) {
|
|
1993
|
+
const columnsOf = (kind) => statements.flatMap((statement) => {
|
|
1994
|
+
if (statement.kind !== kind) return [];
|
|
1995
|
+
const table = modelTable(contract, statement.field);
|
|
1996
|
+
const column = fieldColumn(contract, statement.field);
|
|
1997
|
+
return table === void 0 || column === void 0 ? [] : [{
|
|
1998
|
+
...table,
|
|
1999
|
+
column,
|
|
2000
|
+
field: statement.field,
|
|
2001
|
+
text: fieldStatementText(contract, statement.field)
|
|
2002
|
+
}];
|
|
2003
|
+
});
|
|
2004
|
+
return {
|
|
2005
|
+
convert: columnsOf("convert"),
|
|
2006
|
+
backfill: columnsOf("backfill")
|
|
2007
|
+
};
|
|
2008
|
+
}
|
|
2009
|
+
/**
|
|
2010
|
+
* A conflict for the first backfill statement that names a field the origin model does not have and
|
|
2011
|
+
* whose column has a default: the default fills the existing rows when the column is added, so the
|
|
2012
|
+
* statement has nothing to fill. A field already in the origin keeps its backfill, since a default
|
|
2013
|
+
* does not fill the rows that hold NULL when the column becomes required.
|
|
2014
|
+
*/
|
|
2015
|
+
function backfillOfDefaultedField(statements, fromContract, contract) {
|
|
2016
|
+
for (const statement of statements) {
|
|
2017
|
+
if (statement.kind !== "backfill" || fromContract === null) continue;
|
|
2018
|
+
const originModel = sqlModelStorage(fromContract, statement.field);
|
|
2019
|
+
if (originModel === void 0 || Object.hasOwn(originModel.fields, statement.field.field)) continue;
|
|
2020
|
+
const table = modelTable(contract, statement.field);
|
|
2021
|
+
const column = fieldColumn(contract, statement.field);
|
|
2022
|
+
if (table === void 0 || column === void 0) continue;
|
|
2023
|
+
if ((contract.storage.namespaces[table.namespaceId]?.entries.table?.[table.table]?.columns[column])?.default === void 0) continue;
|
|
2024
|
+
return statementRefused(statement, `${describeMigrationStatement(statement, fromContract, contract)}: the field is new and has a default`, "The default fills the existing rows when the column is added, so there is nothing to backfill. Leave out the --backfill statement.", {
|
|
2025
|
+
namespaceId: table.namespaceId,
|
|
2026
|
+
table: table.table,
|
|
2027
|
+
column
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
/**
|
|
2032
|
+
* Every statement of a plan in the order given, with the operations it accounts for: a rename's
|
|
2033
|
+
* from `renames`, and none yet for any other statement.
|
|
2034
|
+
*/
|
|
2035
|
+
function appliedStatementsInOrder(statements, renames) {
|
|
2036
|
+
return statements.map((statement) => renames.find((applied) => applied.statement === statement) ?? {
|
|
2037
|
+
statement,
|
|
2038
|
+
operationIndexes: []
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
/**
|
|
2042
|
+
* The field of `contract` that stores `column` of `table`, or undefined when no field stores it or
|
|
2043
|
+
* tables of two storage namespaces share the table's name.
|
|
2044
|
+
*/
|
|
2045
|
+
function fieldOfColumn(contract, table, column) {
|
|
2046
|
+
const fields = Object.entries(contract.domain.namespaces).flatMap(([namespaceId, namespace]) => Object.entries(namespace.models).flatMap(([model, definition]) => {
|
|
2047
|
+
const { storage } = definition;
|
|
2048
|
+
if (!isSqlModelStorage(storage) || storage.table !== table) return [];
|
|
2049
|
+
const field = Object.entries(storage.fields).find(([, stored]) => stored.column === column);
|
|
2050
|
+
if (field === void 0) return [];
|
|
2051
|
+
return [{
|
|
2052
|
+
storageNamespace: storage.namespaceId,
|
|
2053
|
+
isRoot: definition.base === void 0,
|
|
2054
|
+
subject: {
|
|
2055
|
+
kind: "field",
|
|
2056
|
+
namespaceId: asNamespaceId(namespaceId),
|
|
2057
|
+
model,
|
|
2058
|
+
field: field[0]
|
|
2059
|
+
}
|
|
2060
|
+
}];
|
|
2061
|
+
}));
|
|
2062
|
+
if (new Set(fields.map(({ storageNamespace }) => storageNamespace)).size !== 1) return void 0;
|
|
2063
|
+
return (fields.find(({ isRoot }) => isRoot) ?? fields[0])?.subject;
|
|
2064
|
+
}
|
|
2065
|
+
/** What a codec hook's call is about: the column of the field event it was returned for. */
|
|
2066
|
+
function fieldEventStorage(field) {
|
|
2067
|
+
return {
|
|
2068
|
+
storageName: [
|
|
2069
|
+
...field.namespaceId === UNBOUND_NAMESPACE_ID ? [] : [field.namespaceId],
|
|
2070
|
+
field.tableName,
|
|
2071
|
+
field.columnName
|
|
2072
|
+
].join("."),
|
|
2073
|
+
table: {
|
|
2074
|
+
namespaceId: field.namespaceId,
|
|
2075
|
+
table: field.tableName,
|
|
2076
|
+
column: field.columnName
|
|
2077
|
+
}
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
/**
|
|
2081
|
+
* Names for destructive calls a target cannot map to storage: each call's factory name, with a
|
|
2082
|
+
* `#n` ordinal when several calls of the plan share it, so each name picks out one call.
|
|
2083
|
+
*/
|
|
2084
|
+
function unknownCallNames(calls) {
|
|
2085
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2086
|
+
for (const call of calls) counts.set(call.factoryName, (counts.get(call.factoryName) ?? 0) + 1);
|
|
2087
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2088
|
+
return new Map(calls.map((call) => {
|
|
2089
|
+
const ordinal = (seen.get(call.factoryName) ?? 0) + 1;
|
|
2090
|
+
seen.set(call.factoryName, ordinal);
|
|
2091
|
+
return [call, (counts.get(call.factoryName) ?? 0) > 1 ? `${call.factoryName}#${ordinal}` : call.factoryName];
|
|
2092
|
+
}));
|
|
2093
|
+
}
|
|
2094
|
+
/** The models of `contract` that store `table`: a model and its variants share one. */
|
|
2095
|
+
function modelsStoring(contract, namespaceId, table) {
|
|
2096
|
+
return Object.entries(contract.domain.namespaces).flatMap(([modelNamespace, namespace]) => Object.entries(namespace.models).flatMap(([model, definition]) => {
|
|
2097
|
+
const { storage } = definition;
|
|
2098
|
+
if (!isSqlModelStorage(storage)) return [];
|
|
2099
|
+
if (storage.namespaceId !== namespaceId || storage.table !== table) return [];
|
|
2100
|
+
return [{
|
|
2101
|
+
coordinate: {
|
|
2102
|
+
namespaceId: asNamespaceId(modelNamespace),
|
|
2103
|
+
model
|
|
2104
|
+
},
|
|
2105
|
+
isRoot: definition.base === void 0,
|
|
2106
|
+
fields: storage.fields
|
|
2107
|
+
}];
|
|
2108
|
+
}));
|
|
2109
|
+
}
|
|
2110
|
+
function subjectOf(target, context) {
|
|
2111
|
+
const storage = {
|
|
2112
|
+
kind: "storage",
|
|
2113
|
+
name: target.storageName
|
|
2114
|
+
};
|
|
2115
|
+
const { fromContract } = context;
|
|
2116
|
+
if (target.table === void 0 || fromContract === null) return storage;
|
|
2117
|
+
const origin = context.origins.originTable({
|
|
2118
|
+
namespaceId: target.table.namespaceId,
|
|
2119
|
+
table: target.table.table
|
|
2120
|
+
});
|
|
2121
|
+
const models = modelsStoring(fromContract, origin.namespaceId, origin.table);
|
|
2122
|
+
if (target.table.column === void 0) {
|
|
2123
|
+
const model = models.find(({ isRoot }) => isRoot) ?? models[0];
|
|
2124
|
+
return model === void 0 ? storage : {
|
|
2125
|
+
kind: "model",
|
|
2126
|
+
...model.coordinate
|
|
2127
|
+
};
|
|
2128
|
+
}
|
|
2129
|
+
const column = context.origins.originColumn(origin, target.table.column);
|
|
2130
|
+
for (const model of [...models].sort((a, b) => Number(b.isRoot) - Number(a.isRoot))) {
|
|
2131
|
+
const field = Object.entries(model.fields).find(([, stored]) => stored.column === column)?.[0];
|
|
2132
|
+
if (field !== void 0) return {
|
|
2133
|
+
kind: "field",
|
|
2134
|
+
...model.coordinate,
|
|
2135
|
+
field
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
return storage;
|
|
2139
|
+
}
|
|
2140
|
+
/**
|
|
2141
|
+
* How to keep the rows of a dropped table whose model the destination keeps under the same name in
|
|
2142
|
+
* another table of the same namespace: no statement can rename that table, so the user renames it
|
|
2143
|
+
* by hand. None when the origin contract already stores a model in that table.
|
|
2144
|
+
*/
|
|
2145
|
+
function keepTableAdvice(target, subject, context) {
|
|
2146
|
+
if (subject.kind !== "model" || target.table === void 0 || context.fromContract === null) return;
|
|
2147
|
+
const storage = context.contract.domain.namespaces[subject.namespaceId]?.models[subject.model]?.storage;
|
|
2148
|
+
if (storage === void 0 || !isSqlModelStorage(storage)) return void 0;
|
|
2149
|
+
const { namespaceId, table: from } = target.table;
|
|
2150
|
+
if (storage.namespaceId !== namespaceId || storage.table === from) return void 0;
|
|
2151
|
+
if (modelsStoring(context.fromContract, namespaceId, storage.table).length > 0) return void 0;
|
|
2152
|
+
const rename = {
|
|
2153
|
+
namespaceId,
|
|
2154
|
+
from,
|
|
2155
|
+
to: storage.table
|
|
2156
|
+
};
|
|
2157
|
+
return `To keep its rows, rename table "${from}" to "${storage.table}" instead; a statement cannot rename the table of a model that keeps its name. With db update, rename the table in the database yourself with ${context.byHand.tableRenameByHand(rename).join("; ")}, then run prisma db update. With migration plan, write the rename as a migration of its own instead: run prisma migration new --name <name> --from <hash of the migration the database is at>, add ${context.byHand.renderTableRename(rename)} to the operations of its migration.ts, run node on that migration.ts so it writes ops.json, then run prisma db migrate.`;
|
|
2158
|
+
}
|
|
2159
|
+
/**
|
|
2160
|
+
* The subject of each operation that loses data and each that changes access, at its position in
|
|
2161
|
+
* the plan. A subject is a model or a field of the origin contract when one stores it, found under
|
|
2162
|
+
* its origin name when a rename statement renamed its table or column earlier in the plan.
|
|
2163
|
+
*/
|
|
2164
|
+
function subjectsOfCalls(calls, context) {
|
|
2165
|
+
const dataLoss = [];
|
|
2166
|
+
const accessChanges = [];
|
|
2167
|
+
const temporaryValues = [];
|
|
2168
|
+
let operationIndex = 0;
|
|
2169
|
+
for (const call of calls) {
|
|
2170
|
+
for (const { table, column, value } of call.temporaryValues) temporaryValues.push({
|
|
2171
|
+
operationIndex,
|
|
2172
|
+
subject: fieldOfColumn(context.contract, table, column) ?? {
|
|
2173
|
+
kind: "storage",
|
|
2174
|
+
name: `${table}.${column}`
|
|
2175
|
+
},
|
|
2176
|
+
value
|
|
2177
|
+
});
|
|
2178
|
+
for (const target of call.dataLoss) {
|
|
2179
|
+
const subject = subjectOf(target, context);
|
|
2180
|
+
dataLoss.push({
|
|
2181
|
+
operationIndex,
|
|
2182
|
+
subject,
|
|
2183
|
+
loss: target.loss,
|
|
2184
|
+
label: call.label,
|
|
2185
|
+
...ifDefined("keepDataAdvice", keepTableAdvice(target, subject, context))
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2188
|
+
for (const target of call.accessChanges) accessChanges.push({
|
|
2189
|
+
operationIndex,
|
|
2190
|
+
subject: subjectOf(target, context),
|
|
2191
|
+
widens: target.widens
|
|
2192
|
+
});
|
|
2193
|
+
operationIndex += call.operationCount;
|
|
2194
|
+
}
|
|
2195
|
+
return {
|
|
2196
|
+
dataLoss,
|
|
2197
|
+
accessChanges,
|
|
2198
|
+
temporaryValues
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
const readOnlyEmptyObject = Object.freeze({});
|
|
2202
|
+
function cloneRecord(value) {
|
|
2203
|
+
if (value === readOnlyEmptyObject) return value;
|
|
2204
|
+
return blindCast(Object.freeze({ ...value }));
|
|
2205
|
+
}
|
|
2206
|
+
function freezeSteps(steps) {
|
|
2207
|
+
if (steps.length === 0) return Object.freeze([]);
|
|
2208
|
+
return Object.freeze(steps.map((step) => Object.freeze({
|
|
2209
|
+
description: step.description,
|
|
2210
|
+
sql: step.sql,
|
|
2211
|
+
...step.params ? { params: Object.freeze([...step.params]) } : {},
|
|
2212
|
+
...step.meta ? { meta: cloneRecord(step.meta) } : {},
|
|
2213
|
+
...step.expectation ? { expectation: Object.freeze({ ...step.expectation }) } : {}
|
|
2214
|
+
})));
|
|
2215
|
+
}
|
|
2216
|
+
function freezeDetailsValue(value) {
|
|
2217
|
+
if (value === null || value === void 0) return value;
|
|
2218
|
+
if (typeof value !== "object") return value;
|
|
2219
|
+
if (Array.isArray(value)) return blindCast(Object.freeze([...value]));
|
|
2220
|
+
return blindCast(Object.freeze({ ...value }));
|
|
2221
|
+
}
|
|
2222
|
+
function freezeTargetDetails(target) {
|
|
2223
|
+
return Object.freeze({
|
|
2224
|
+
id: target.id,
|
|
2225
|
+
...target.details !== void 0 ? { details: freezeDetailsValue(target.details) } : {}
|
|
2226
|
+
});
|
|
2227
|
+
}
|
|
2228
|
+
function freezeOperation(operation) {
|
|
2229
|
+
return Object.freeze({
|
|
2230
|
+
id: operation.id,
|
|
2231
|
+
label: operation.label,
|
|
2232
|
+
...operation.summary ? { summary: operation.summary } : {},
|
|
2233
|
+
operationClass: operation.operationClass,
|
|
2234
|
+
...operation.invariantId ? { invariantId: operation.invariantId } : {},
|
|
2235
|
+
target: freezeTargetDetails(operation.target),
|
|
2236
|
+
precheck: freezeSteps(operation.precheck),
|
|
2237
|
+
execute: freezeSteps(operation.execute),
|
|
2238
|
+
postcheck: freezeSteps(operation.postcheck),
|
|
2239
|
+
...operation.meta ? { meta: cloneRecord(operation.meta) } : {}
|
|
2240
|
+
});
|
|
2241
|
+
}
|
|
2242
|
+
function freezeOperations(operations) {
|
|
2243
|
+
if (operations.length === 0) return Object.freeze([]);
|
|
2244
|
+
return Object.freeze(operations.map((operation) => freezeOperation(operation)));
|
|
2245
|
+
}
|
|
2246
|
+
function createMigrationPlan(options) {
|
|
2247
|
+
return Object.freeze({
|
|
2248
|
+
targetId: options.targetId,
|
|
2249
|
+
spaceId: options.spaceId,
|
|
2250
|
+
...options.origin !== void 0 ? { origin: options.origin ? Object.freeze({ ...options.origin }) : null } : {},
|
|
2251
|
+
destination: Object.freeze({ ...options.destination }),
|
|
2252
|
+
operations: freezeOperations(options.operations),
|
|
2253
|
+
providedInvariants: Object.freeze([...options.providedInvariants]),
|
|
2254
|
+
...options.meta ? { meta: cloneRecord(options.meta) } : {}
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
function plannerSuccess(plan, appliedStatements, subjects, warnings) {
|
|
2258
|
+
return Object.freeze({
|
|
2259
|
+
kind: "success",
|
|
2260
|
+
plan,
|
|
2261
|
+
appliedStatements: Object.freeze([...appliedStatements]),
|
|
2262
|
+
dataLoss: Object.freeze([...subjects.dataLoss]),
|
|
2263
|
+
accessChanges: Object.freeze([...subjects.accessChanges]),
|
|
2264
|
+
temporaryValues: Object.freeze([...subjects.temporaryValues]),
|
|
2265
|
+
...warnings && warnings.length > 0 ? { warnings: Object.freeze(warnings.map((conflict) => Object.freeze({
|
|
2266
|
+
kind: conflict.kind,
|
|
2267
|
+
summary: conflict.summary,
|
|
2268
|
+
...conflict.why ? { why: conflict.why } : {},
|
|
2269
|
+
...conflict.location ? { location: Object.freeze({ ...conflict.location }) } : {},
|
|
2270
|
+
...conflict.refusedStatement ? { refusedStatement: conflict.refusedStatement } : {},
|
|
2271
|
+
...conflict.refusedOperationClass ? { refusedOperationClass: conflict.refusedOperationClass } : {},
|
|
2272
|
+
...conflict.meta ? { meta: cloneRecord(conflict.meta) } : {}
|
|
2273
|
+
}))) } : {}
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
function plannerFailure(conflicts) {
|
|
2277
|
+
return Object.freeze({
|
|
2278
|
+
kind: "failure",
|
|
2279
|
+
conflicts: Object.freeze(conflicts.map((conflict) => Object.freeze({
|
|
2280
|
+
kind: conflict.kind,
|
|
2281
|
+
summary: conflict.summary,
|
|
2282
|
+
...conflict.why ? { why: conflict.why } : {},
|
|
2283
|
+
...conflict.location ? { location: Object.freeze({ ...conflict.location }) } : {},
|
|
2284
|
+
...conflict.refusedStatement ? { refusedStatement: conflict.refusedStatement } : {},
|
|
2285
|
+
...conflict.refusedOperationClass ? { refusedOperationClass: conflict.refusedOperationClass } : {},
|
|
2286
|
+
...conflict.meta ? { meta: cloneRecord(conflict.meta) } : {}
|
|
2287
|
+
})))
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Creates a successful migration runner result.
|
|
2292
|
+
*/
|
|
2293
|
+
function runnerSuccess(value) {
|
|
2294
|
+
return ok(Object.freeze({
|
|
2295
|
+
operationsPlanned: value.operationsPlanned,
|
|
2296
|
+
operationsExecuted: value.operationsExecuted
|
|
2297
|
+
}));
|
|
2298
|
+
}
|
|
2299
|
+
/**
|
|
2300
|
+
* Creates a failed migration runner result.
|
|
2301
|
+
*/
|
|
2302
|
+
function runnerFailure(code, summary, options) {
|
|
2303
|
+
return notOk(migrationRunnerFailure(code, summary, options));
|
|
2304
|
+
}
|
|
2305
|
+
/**
|
|
2306
|
+
* Policy used by `db init`: additive-only operations, no widening/destructive steps.
|
|
2307
|
+
*/
|
|
2308
|
+
const INIT_ADDITIVE_POLICY = Object.freeze({ allowedOperationClasses: Object.freeze(["additive"]) });
|
|
2309
|
+
const RequiredColumnMeta = type({ requiredColumn: {
|
|
2310
|
+
table: "string",
|
|
2311
|
+
column: "string",
|
|
2312
|
+
reason: "'nullValues' | 'newField' | 'backfillMissedRows'"
|
|
2313
|
+
} });
|
|
2314
|
+
/**
|
|
2315
|
+
* The meta of a step that fails when rows have no value for a column that is to become required:
|
|
2316
|
+
* rows hold NULL in it, or, for a new column that can be added to an empty table only, the table
|
|
2317
|
+
* has rows. When the step fails, the runner counts those rows.
|
|
2318
|
+
*/
|
|
2319
|
+
function requiredColumnMeta(table, column, reason) {
|
|
2320
|
+
return { requiredColumn: {
|
|
2321
|
+
table,
|
|
2322
|
+
column,
|
|
2323
|
+
reason
|
|
2324
|
+
} };
|
|
2325
|
+
}
|
|
2326
|
+
/** The column a step checks before it becomes required, as {@link requiredColumnMeta} marks it. */
|
|
2327
|
+
function requiredColumnOf(step) {
|
|
2328
|
+
const meta = RequiredColumnMeta(step.meta ?? {});
|
|
2329
|
+
return meta instanceof type.errors ? void 0 : meta.requiredColumn;
|
|
2330
|
+
}
|
|
2331
|
+
/** What a runner failure says of the rows a column that is to become required has no value in. */
|
|
2332
|
+
function rowsWithoutValueSummary(required, rows) {
|
|
2333
|
+
const { table, column } = required;
|
|
2334
|
+
if (required.reason === "newField") return `"${table}" has ${rows} ${rows === 1 ? "row" : "rows"}, so the new required column "${column}" cannot be added: it can be added to an empty table only.`;
|
|
2335
|
+
const counted = `${rows} ${rows === 1 ? "row" : "rows"} of "${table}"`;
|
|
2336
|
+
if (required.reason === "backfillMissedRows") return `${counted} still ${rows === 1 ? "holds" : "hold"} NULL in "${column}" after its backfill step, so it cannot become required.`;
|
|
2337
|
+
return `${counted} ${rows === 1 ? "holds" : "hold"} NULL in "${column}", so it cannot become required.`;
|
|
2338
|
+
}
|
|
2339
|
+
function migrationRange(edges, operationIndex, operationCount) {
|
|
2340
|
+
let start = 0;
|
|
2341
|
+
for (const edge of edges) {
|
|
2342
|
+
const end = start + edge.operationCount;
|
|
2343
|
+
if (operationIndex < end) return {
|
|
2344
|
+
start,
|
|
2345
|
+
end
|
|
2346
|
+
};
|
|
2347
|
+
start = end;
|
|
2348
|
+
}
|
|
2349
|
+
return {
|
|
2350
|
+
start: 0,
|
|
2351
|
+
end: operationCount
|
|
2352
|
+
};
|
|
2353
|
+
}
|
|
2354
|
+
/**
|
|
2355
|
+
* The marks of the steps of `operations` that belong to the same migration as the operation at
|
|
2356
|
+
* `operationIndex`, in plan order: `edges` say how many operations each migration of the plan has.
|
|
2357
|
+
*/
|
|
2358
|
+
function requiredColumnsOfMigration(operations, edges, operationIndex) {
|
|
2359
|
+
const { start, end } = migrationRange(edges, operationIndex, operations.length);
|
|
2360
|
+
return operations.slice(start, end).flatMap((operation, offset) => [
|
|
2361
|
+
...operation.precheck,
|
|
2362
|
+
...operation.execute,
|
|
2363
|
+
...operation.postcheck
|
|
2364
|
+
].flatMap((step) => {
|
|
2365
|
+
const required = requiredColumnOf(step);
|
|
2366
|
+
return required === void 0 ? [] : [{
|
|
2367
|
+
operation,
|
|
2368
|
+
operationIndex: start + offset,
|
|
2369
|
+
required
|
|
2370
|
+
}];
|
|
2371
|
+
}));
|
|
2372
|
+
}
|
|
2373
|
+
/**
|
|
2374
|
+
* A runner failure's summary and reports for the rows that columns becoming required have no value
|
|
2375
|
+
* in. The failed operation's marks are counted first, and the first of them with such rows is the
|
|
2376
|
+
* one the summary names; undefined when none has any. The other marks of the same migration are
|
|
2377
|
+
* counted after it, until a count fails, such as for a column an earlier operation adds, so that
|
|
2378
|
+
* the advice can name every field that needs a value.
|
|
2379
|
+
*/
|
|
2380
|
+
async function rowsWithoutValueFailure(input) {
|
|
2381
|
+
const report = (mark, rows) => ({
|
|
2382
|
+
field: fieldOfColumn(input.contract, mark.required.table, mark.required.column),
|
|
2383
|
+
reason: mark.required.reason,
|
|
2384
|
+
rows,
|
|
2385
|
+
operationIndex: mark.operationIndex
|
|
2386
|
+
});
|
|
2387
|
+
for (const mark of input.failed) {
|
|
2388
|
+
const rows = await input.count(mark);
|
|
2389
|
+
if (rows === 0) continue;
|
|
2390
|
+
const reports = [report(mark, rows)];
|
|
2391
|
+
for (const other of input.others) {
|
|
2392
|
+
if (other.required.table === mark.required.table && other.required.column === mark.required.column) continue;
|
|
2393
|
+
let otherRows;
|
|
2394
|
+
try {
|
|
2395
|
+
otherRows = await input.count(other);
|
|
2396
|
+
} catch {
|
|
2397
|
+
break;
|
|
2398
|
+
}
|
|
2399
|
+
if (otherRows > 0) reports.push(report(other, otherRows));
|
|
2400
|
+
}
|
|
2401
|
+
return {
|
|
2402
|
+
summary: rowsWithoutValueSummary(mark.required, rows),
|
|
2403
|
+
reports
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
/** Keywords that end an expression and continue the statement around it. */
|
|
2408
|
+
const STATEMENT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
2409
|
+
"FROM",
|
|
2410
|
+
"WHERE",
|
|
2411
|
+
"UNION",
|
|
2412
|
+
"INTERSECT",
|
|
2413
|
+
"EXCEPT",
|
|
2414
|
+
"GROUP",
|
|
2415
|
+
"ORDER",
|
|
2416
|
+
"HAVING",
|
|
2417
|
+
"LIMIT",
|
|
2418
|
+
"RETURNING",
|
|
2419
|
+
"WINDOW"
|
|
2420
|
+
]);
|
|
2421
|
+
const WORD = /[A-Za-z_][A-Za-z0-9_$]*/y;
|
|
2422
|
+
const DOLLAR_QUOTE = /\$[A-Za-z_]*\$/y;
|
|
2423
|
+
function quoteAt(text, index, quoting) {
|
|
2424
|
+
return quoting.find((quote) => text.slice(index, index + quote.open.length).toUpperCase() === quote.open.toUpperCase());
|
|
2425
|
+
}
|
|
2426
|
+
function endOfQuote(text, start, quote) {
|
|
2427
|
+
let index = start + quote.open.length;
|
|
2428
|
+
while (index < text.length) {
|
|
2429
|
+
if (quote.backslashEscapes && text[index] === "\\") {
|
|
2430
|
+
index += 2;
|
|
2431
|
+
continue;
|
|
2432
|
+
}
|
|
2433
|
+
if (text.startsWith(quote.close, index)) {
|
|
2434
|
+
const after = index + quote.close.length;
|
|
2435
|
+
if (!quote.doubledCloseEscapes || !text.startsWith(quote.close, after)) return after;
|
|
2436
|
+
index = after + quote.close.length;
|
|
2437
|
+
continue;
|
|
2438
|
+
}
|
|
2439
|
+
index += 1;
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
/**
|
|
2443
|
+
* Returns what in `text` could end or extend the statement a migration writes it into, such as the
|
|
2444
|
+
* conversion of an `ALTER COLUMN … USING` clause or a column of an `INSERT … SELECT` list, or
|
|
2445
|
+
* undefined when it finds nothing. Text inside the quotes of `quoting` may hold anything; outside
|
|
2446
|
+
* them the text may not hold a semicolon, a SQL comment, dollar quoting, or, outside parentheses
|
|
2447
|
+
* and brackets, a comma or a keyword that would end the expression. `FROM` directly after
|
|
2448
|
+
* `DISTINCT`, as in `IS DISTINCT FROM`, belongs to the expression. It checks for these mistakes
|
|
2449
|
+
* only; it does not parse the SQL.
|
|
2450
|
+
*/
|
|
2451
|
+
function checkSqlExpressionText(text, quoting) {
|
|
2452
|
+
let depth = 0;
|
|
2453
|
+
let index = 0;
|
|
2454
|
+
let previousWord;
|
|
2455
|
+
while (index < text.length) {
|
|
2456
|
+
const character = text[index] ?? "";
|
|
2457
|
+
const quote = quoteAt(text, index, quoting);
|
|
2458
|
+
if (quote !== void 0) {
|
|
2459
|
+
const end = endOfQuote(text, index, quote);
|
|
2460
|
+
if (end === void 0) return "a quote that is not closed";
|
|
2461
|
+
index = end;
|
|
2462
|
+
previousWord = void 0;
|
|
2463
|
+
continue;
|
|
2464
|
+
}
|
|
2465
|
+
if (character === ";") return "a semicolon outside a quoted literal";
|
|
2466
|
+
if (text.startsWith("--", index) || text.startsWith("/*", index)) return "a SQL comment";
|
|
2467
|
+
DOLLAR_QUOTE.lastIndex = index;
|
|
2468
|
+
if (character === "$" && DOLLAR_QUOTE.test(text)) return "dollar quoting";
|
|
2469
|
+
if (character === "," && depth === 0) return "a comma outside parentheses";
|
|
2470
|
+
if (character === "(" || character === "[") depth += 1;
|
|
2471
|
+
if (character === ")" || character === "]") {
|
|
2472
|
+
depth -= 1;
|
|
2473
|
+
if (depth < 0) return "parentheses that do not match";
|
|
2474
|
+
}
|
|
2475
|
+
WORD.lastIndex = index;
|
|
2476
|
+
const word = /[A-Za-z_]/.test(character) ? WORD.exec(text)?.[0].toUpperCase() : void 0;
|
|
2477
|
+
if (word !== void 0) {
|
|
2478
|
+
if (depth === 0 && STATEMENT_KEYWORDS.has(word) && !(word === "FROM" && previousWord === "DISTINCT")) return `the keyword ${word} outside parentheses`;
|
|
2479
|
+
previousWord = word;
|
|
2480
|
+
index += word.length;
|
|
2481
|
+
continue;
|
|
2482
|
+
}
|
|
2483
|
+
if (!/\s/.test(character)) previousWord = void 0;
|
|
2484
|
+
index += 1;
|
|
2485
|
+
}
|
|
2486
|
+
return depth === 0 ? void 0 : "parentheses that do not match";
|
|
2487
|
+
}
|
|
2488
|
+
/** Whether `slot` is a placeholder the user has not filled yet. */
|
|
2489
|
+
function isUnfilledSlot(slot) {
|
|
2490
|
+
return slot?.kind === "placeholder";
|
|
2491
|
+
}
|
|
2492
|
+
/** The SQL of a written slot; undefined for an unfilled one or none. */
|
|
2493
|
+
function sqlOfSlot(slot) {
|
|
2494
|
+
return slot?.kind === "sql" ? slot.sql : void 0;
|
|
2495
|
+
}
|
|
2496
|
+
/** The slot as `migration.ts` writes it: SQL text written through `sql`, or a `placeholder(...)` call. */
|
|
2497
|
+
function slotSource(slot, sql) {
|
|
2498
|
+
return slot.kind === "placeholder" ? `placeholder(${jsonToTsSource(slot.slot)})` : sql.source(slot.sql);
|
|
2499
|
+
}
|
|
2500
|
+
/** `moduleSpecifier` is the module the generated file imports `sql` from. */
|
|
2501
|
+
function createSqlTextSources(moduleSpecifier) {
|
|
2502
|
+
let usesTag = false;
|
|
2503
|
+
return {
|
|
2504
|
+
source(text) {
|
|
2505
|
+
const written = tsTaggedTemplateSource("sql", text);
|
|
2506
|
+
usesTag ||= written.usesTag;
|
|
2507
|
+
return written.source;
|
|
2508
|
+
},
|
|
2509
|
+
imports() {
|
|
2510
|
+
return usesTag ? [{
|
|
2511
|
+
moduleSpecifier,
|
|
2512
|
+
symbol: "sql"
|
|
2513
|
+
}] : [];
|
|
2514
|
+
}
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
/** The codecs and data types a set of composed components registers. */
|
|
2518
|
+
function sqlTypeLookupsOf(frameworkComponents) {
|
|
2519
|
+
return {
|
|
2520
|
+
codecLookup: extractCodecLookup(frameworkComponents),
|
|
2521
|
+
dataTypeLookup: assembleDataTypes(frameworkComponents).lookup
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2524
|
+
const TABLE_NAME_CASE_CHANGED_CODE = "MIGRATION.TABLE_NAME_CASE_CHANGED";
|
|
2525
|
+
function lowerFirst(value) {
|
|
2526
|
+
return value.charAt(0).toLowerCase() + value.slice(1);
|
|
2527
|
+
}
|
|
2528
|
+
function plannedTable(node, namespaceId) {
|
|
2529
|
+
return {
|
|
2530
|
+
namespaceId,
|
|
2531
|
+
tableName: node.name
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
function renameNeedsNamespace(drop, dropped, contract, defaultNamespaceId) {
|
|
2535
|
+
if (drop.namespaceId !== UNBOUND_NAMESPACE_ID && drop.namespaceId !== defaultNamespaceId) return true;
|
|
2536
|
+
const declaredElsewhere = (namespaceId, tableName) => namespaceId !== drop.namespaceId && tableName === drop.tableName;
|
|
2537
|
+
return dropped.some((other) => declaredElsewhere(other.namespaceId, other.tableName)) || Object.values(contract.storage.namespaces).some((namespace) => Object.keys(namespace.entries.table ?? {}).some((tableName) => declaredElsewhere(namespace.id, tableName)));
|
|
2538
|
+
}
|
|
2539
|
+
/**
|
|
2540
|
+
* Finds every (drop `X`, create `Y`) pair in the same namespace where `X` is
|
|
2541
|
+
* `Y` with its first letter lowered. That shape is the signature of a schema
|
|
2542
|
+
* upgraded across the release in which a model with no `@@map` stopped
|
|
2543
|
+
* lowering the first letter of its table name: planning it would drop the
|
|
2544
|
+
* user's table and recreate it empty. Columns are deliberately not compared,
|
|
2545
|
+
* so a user who also changed a field in the same upgrade is still protected.
|
|
2546
|
+
*
|
|
2547
|
+
* Returns one conflict per pair, carrying `MIGRATION.TABLE_NAME_CASE_CHANGED`
|
|
2548
|
+
* in `meta.code`, or an empty array when the plan has no such pair.
|
|
2549
|
+
*/
|
|
2550
|
+
function detectTableNameCaseChanges(input) {
|
|
2551
|
+
const dropped = [];
|
|
2552
|
+
const created = [];
|
|
2553
|
+
for (const issue of input.issues) {
|
|
2554
|
+
const outcome = issueOutcome(issue);
|
|
2555
|
+
if (outcome === "not-equal") continue;
|
|
2556
|
+
const table = input.tableOf(issue);
|
|
2557
|
+
if (table === void 0) continue;
|
|
2558
|
+
(outcome === "not-expected" ? dropped : created).push(plannedTable(table, input.namespaceIdOf(issue)));
|
|
2559
|
+
}
|
|
2560
|
+
const conflicts = [];
|
|
2561
|
+
for (const create of created) {
|
|
2562
|
+
const drop = dropped.find((candidate) => candidate.namespaceId === create.namespaceId && candidate.tableName === lowerFirst(create.tableName));
|
|
2563
|
+
if (drop === void 0) continue;
|
|
2564
|
+
const renameCall = input.renameTableCall({
|
|
2565
|
+
namespaceId: renameNeedsNamespace(drop, dropped, input.contract, input.defaultNamespaceId) ? drop.namespaceId : void 0,
|
|
2566
|
+
from: drop.tableName,
|
|
2567
|
+
to: create.tableName
|
|
2568
|
+
});
|
|
2569
|
+
conflicts.push({
|
|
2570
|
+
kind: "tableNameCaseChanged",
|
|
2571
|
+
summary: `${TABLE_NAME_CASE_CHANGED_CODE}: table "${create.tableName}" would be created and table "${drop.tableName}" dropped. Prisma 8 changed the default table name: a model with no @@map now names its table verbatim, so model ${create.tableName} points at "${create.tableName}" instead of "${drop.tableName}".`,
|
|
2572
|
+
why: `To keep table "${drop.tableName}" and its rows, add @@map("${drop.tableName}") to model ${create.tableName} (or run the add-model-map codemod over the schema) and plan again. To rename the table and keep its rows instead: in a project with migration history, make the rename its own schema change, create its migration with prisma migration new --from <hash of the migration the database is at>, and add ${renameCall} to the migration's operations, which renames the table and the objects named after it; in a project that uses db update, rename it by hand with ${input.renameByHandStatements({
|
|
2573
|
+
namespaceId: create.namespaceId,
|
|
2574
|
+
from: drop.tableName,
|
|
2575
|
+
to: create.tableName
|
|
2576
|
+
}).join("; ")}, then run db update again.`,
|
|
2577
|
+
location: {
|
|
2578
|
+
namespaceId: create.namespaceId,
|
|
2579
|
+
entityKind: "table",
|
|
2580
|
+
entityName: create.tableName
|
|
2581
|
+
},
|
|
2582
|
+
meta: {
|
|
2583
|
+
code: TABLE_NAME_CASE_CHANGED_CODE,
|
|
2584
|
+
droppedTable: drop.tableName,
|
|
2585
|
+
createdTable: create.tableName
|
|
2586
|
+
}
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
return conflicts;
|
|
2590
|
+
}
|
|
2591
|
+
/**
|
|
2592
|
+
* Whether a required column the plan adds to a table with rows may take one temporary value in
|
|
2593
|
+
* every row. Not when it is in the primary key, a unique constraint or a foreign key, which one
|
|
2594
|
+
* value in every row would break, and not when its values come from a value set, since no one value
|
|
2595
|
+
* stands for every member. Such a column is added for an empty table only.
|
|
2596
|
+
*/
|
|
2597
|
+
function columnTakesTemporaryValue(table, column) {
|
|
2598
|
+
return table.columns[column]?.valueSet === void 0 && !(table.primaryKey?.columns.includes(column) ?? false) && !table.uniques.some(({ columns }) => columns.includes(column)) && !table.foreignKeys.some(({ source }) => source.columns.includes(column));
|
|
2599
|
+
}
|
|
2600
|
+
const ConvertedColumnMeta = type({ convertedColumn: {
|
|
2601
|
+
table: "string",
|
|
2602
|
+
column: "string",
|
|
2603
|
+
by: "'cast' | 'conversion'"
|
|
2604
|
+
} });
|
|
2605
|
+
/**
|
|
2606
|
+
* The meta of a step that converts the values of `column` in `table` to the column's new type, or
|
|
2607
|
+
* checks that it did. When the step fails, the runner names the field that stores the column.
|
|
2608
|
+
*/
|
|
2609
|
+
function convertedColumnMeta(table, column, by) {
|
|
2610
|
+
return { convertedColumn: {
|
|
2611
|
+
table,
|
|
2612
|
+
column,
|
|
2613
|
+
by
|
|
2614
|
+
} };
|
|
2615
|
+
}
|
|
2616
|
+
/** The column a step converts to its new type, as {@link convertedColumnMeta} marks it. */
|
|
2617
|
+
function convertedColumnOf(step) {
|
|
2618
|
+
const meta = ConvertedColumnMeta(step.meta ?? {});
|
|
2619
|
+
return meta instanceof type.errors ? void 0 : meta.convertedColumn;
|
|
2620
|
+
}
|
|
2621
|
+
/**
|
|
2622
|
+
* The field of `contract` whose column a failed step converts, or undefined when the step
|
|
2623
|
+
* converts no column, no field stores the column, or tables of two storage namespaces share its name.
|
|
2624
|
+
*/
|
|
2625
|
+
function unconvertedFieldOf(step, contract) {
|
|
2626
|
+
const converted = convertedColumnOf(step);
|
|
2627
|
+
return converted === void 0 ? void 0 : fieldOfColumn(contract, converted.table, converted.column);
|
|
2628
|
+
}
|
|
2629
|
+
/** Reads a reported literal default with the column's codec. `raw` is the reported text, which a refusal quotes. */
|
|
2630
|
+
async function readReportedDefault(codec, literal, raw) {
|
|
2631
|
+
const unreadable = (error, element) => {
|
|
2632
|
+
if (isInternalError(error)) throw error;
|
|
2633
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
2634
|
+
return { defaultUnreadable: `The column codec ${codec.id} cannot read ${element}the default the database reports, ${raw}: ${reason}` };
|
|
2635
|
+
};
|
|
2636
|
+
if (literal.kind === "value") try {
|
|
2637
|
+
const value = await readBody(codec, literal.body);
|
|
2638
|
+
return {
|
|
2639
|
+
resolvedDefault: {
|
|
2640
|
+
kind: "literal",
|
|
2641
|
+
value: codec.dataType.toContract(value)
|
|
2642
|
+
},
|
|
2643
|
+
defaultValue: value
|
|
2644
|
+
};
|
|
2645
|
+
} catch (error) {
|
|
2646
|
+
return unreadable(error, "");
|
|
2647
|
+
}
|
|
2648
|
+
const values = [];
|
|
2649
|
+
for (const [index, body] of literal.elements.entries()) try {
|
|
2650
|
+
values.push(body === null ? null : await readBody(codec, body));
|
|
2651
|
+
} catch (error) {
|
|
2652
|
+
return unreadable(error, `element ${index + 1} of `);
|
|
2653
|
+
}
|
|
2654
|
+
return {
|
|
2655
|
+
resolvedDefault: {
|
|
2656
|
+
kind: "literal",
|
|
2657
|
+
value: values.map((value) => value === null ? null : codec.dataType.toContract(value))
|
|
2658
|
+
},
|
|
2659
|
+
defaultValue: values
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2662
|
+
/**
|
|
2663
|
+
* Reads a reported literal default with the column's codec, and, when the contract's default is an expression that writes a literal, that literal too, so the diff compares the two as values.
|
|
2664
|
+
*/
|
|
2665
|
+
async function readColumnDefault(codec, reported, written) {
|
|
2666
|
+
const read = await readReportedDefault(codec, reported.literal, reported.raw);
|
|
2667
|
+
if (written === void 0 || !("defaultValue" in read)) return read;
|
|
2668
|
+
const writtenRead = await readReportedDefault(codec, written.literal, written.expression);
|
|
2669
|
+
return "defaultValue" in writtenRead ? {
|
|
2670
|
+
...read,
|
|
2671
|
+
defaultExpressionValue: writtenRead.defaultValue
|
|
2672
|
+
} : read;
|
|
2673
|
+
}
|
|
2674
|
+
async function readBody(codec, body) {
|
|
2675
|
+
return codec.toDataTypeValue(await codec.fromWire(body, {}));
|
|
2676
|
+
}
|
|
2677
|
+
function isLoose(value) {
|
|
2678
|
+
return typeof value === "object" && value !== null;
|
|
2679
|
+
}
|
|
2680
|
+
function child(value, key) {
|
|
2681
|
+
if (!isLoose(value)) return void 0;
|
|
2682
|
+
const found = value[key];
|
|
2683
|
+
return isLoose(found) ? found : void 0;
|
|
2684
|
+
}
|
|
2685
|
+
/**
|
|
2686
|
+
* The columns `contract` declares. `contract` is a contract, or the namespaces of several contract spaces merged with each space's contract under `contracts`, as multi-space introspection receives them.
|
|
2687
|
+
*/
|
|
2688
|
+
function contractColumns(contract) {
|
|
2689
|
+
const contracts = isLoose(contract) && Array.isArray(contract["contracts"]) ? contract["contracts"] : [contract];
|
|
2690
|
+
return { columnOf(namespaceIds, table, column) {
|
|
2691
|
+
for (const space of contracts) {
|
|
2692
|
+
const namespaces = child(child(space, "storage"), "namespaces");
|
|
2693
|
+
if (namespaces === void 0) continue;
|
|
2694
|
+
for (const id of namespaceIds ?? Object.keys(namespaces)) {
|
|
2695
|
+
const found = child(child(child(child(child(child(namespaces, id), "entries"), "table"), table), "columns"), column);
|
|
2696
|
+
const codecId = found?.["codecId"];
|
|
2697
|
+
if (found === void 0 || typeof codecId !== "string") continue;
|
|
2698
|
+
const columnDefault = child(found, "default");
|
|
2699
|
+
const expression = columnDefault?.["expression"];
|
|
2700
|
+
return {
|
|
2701
|
+
codecRef: columnCodecRef(codecId, found),
|
|
2702
|
+
defaultExpression: columnDefault?.["kind"] === "function" && typeof expression === "string" ? expression : void 0
|
|
2703
|
+
};
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
} };
|
|
2707
|
+
}
|
|
2708
|
+
function columnCodecRef(codecId, column) {
|
|
2709
|
+
const params = nonEmptyTypeParams(child(column, "typeParams"));
|
|
2710
|
+
const many = column["many"];
|
|
2711
|
+
return {
|
|
2712
|
+
codecId,
|
|
2713
|
+
...ifDefined("typeParams", params === void 0 ? void 0 : blindCast(params)),
|
|
2714
|
+
...ifDefined("many", many !== void 0 && many !== false ? true : void 0)
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
var control_default = new SqlFamilyDescriptor();
|
|
2718
|
+
//#endregion
|
|
2719
|
+
export { unmatchedColumnRename as $, planFieldEventOperations as A, resolveTableRenameAgainst as B, fieldStatementColumns as C, partitionCallsByControlPolicy as D, otherSpaceDeclaring as E, readReportedDefault as F, slotSource as G, rowsWithoutValueSummary as H, requiredColumnMeta as I, sqlTypeLookupsOf as J, sqlDefaultFunctions as K, requiredColumnOf as L, plannerFailure as M, plannerSuccess as N, partitionIssuesByControlPolicy as O, readColumnDefault as P, unknownCallNames as Q, requiredColumnsOfMigration as R, fieldRenameStorageEffect as S, modelRenameStorageEffect as T, runnerFailure as U, rowsWithoutValueFailure as V, runnerSuccess as W, subjectsOfCalls as X, storageNameOfOperation as Y, unconvertedFieldOf as Z, createSqlTextSources as _, appliedStatementsInOrder as a, fieldEventStorage as b, checkSqlExpressionText as c, contractNamespaceToSchemaIR as d, unmatchedTableRename as et, contractToSchemaIR as f, createMigrationPlan as g, convertedColumnOf as h, TABLE_NAME_CASE_CHANGED_CODE as i, planStatements as j, planFieldEventCalls as k, columnTakesTemporaryValue as l, convertedColumnMeta as m, INIT_ADDITIVE_POLICY as n, assembleAuthoringContributions$1 as o, control_default as p, sqlOfSlot as q, NO_RENAMED_ORIGINS as r, backfillOfDefaultedField as s, COLUMN_RENAME_UNMATCHED_CODE as t, contractColumns as u, detectDestructiveChanges as v, isUnfilledSlot as w, fieldOfColumn as x, detectTableNameCaseChanges as y, resolveColumnRenameAgainst as z };
|
|
2720
|
+
|
|
2721
|
+
//# sourceMappingURL=control-BK50zF6c.mjs.map
|