@metaobjectsdev/migrate-ts 0.11.3 → 0.11.5-rc.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/dist/diff/index.js +43 -1
- package/dist/diff/index.js.map +1 -1
- package/dist/drift/drift.d.ts +6 -0
- package/dist/drift/drift.d.ts.map +1 -1
- package/dist/drift/drift.js +1 -0
- package/dist/drift/drift.js.map +1 -1
- package/dist/emit/index.d.ts.map +1 -1
- package/dist/emit/index.js +0 -9
- package/dist/emit/index.js.map +1 -1
- package/dist/emit/sqlite.d.ts.map +1 -1
- package/dist/emit/sqlite.js +21 -9
- package/dist/emit/sqlite.js.map +1 -1
- package/dist/expected-schema.d.ts +9 -1
- package/dist/expected-schema.d.ts.map +1 -1
- package/dist/expected-schema.js +4 -5
- package/dist/expected-schema.js.map +1 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -6
- package/dist/index.js.map +1 -1
- package/dist/introspect/d1.js +10 -2
- package/dist/introspect/d1.js.map +1 -1
- package/dist/snapshot/plan.d.ts +4 -2
- package/dist/snapshot/plan.d.ts.map +1 -1
- package/dist/snapshot/plan.js +3 -1
- package/dist/snapshot/plan.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/view-sql-compare.d.ts.map +1 -1
- package/dist/view-sql-compare.js +5 -7
- package/dist/view-sql-compare.js.map +1 -1
- package/package.json +2 -2
- package/src/diff/index.ts +47 -1
- package/src/drift/drift.ts +7 -0
- package/src/emit/index.ts +3 -13
- package/src/emit/sqlite.ts +23 -10
- package/src/expected-schema.ts +13 -6
- package/src/index.ts +4 -13
- package/src/introspect/d1.ts +9 -2
- package/src/snapshot/plan.ts +6 -1
- package/src/types.ts +8 -0
- package/src/view-sql-compare.ts +5 -7
- package/dist/expected-views.d.ts +0 -5
- package/dist/expected-views.d.ts.map +0 -1
- package/dist/expected-views.js +0 -169
- package/dist/expected-views.js.map +0 -1
- package/dist/source-aware-diff.d.ts +0 -20
- package/dist/source-aware-diff.d.ts.map +0 -1
- package/dist/source-aware-diff.js +0 -24
- package/dist/source-aware-diff.js.map +0 -1
- package/dist/view-ddl-postgres.d.ts +0 -4
- package/dist/view-ddl-postgres.d.ts.map +0 -1
- package/dist/view-ddl-postgres.js +0 -13
- package/dist/view-ddl-postgres.js.map +0 -1
- package/dist/view-ddl-sqlite.d.ts +0 -3
- package/dist/view-ddl-sqlite.d.ts.map +0 -1
- package/dist/view-ddl-sqlite.js +0 -7
- package/dist/view-ddl-sqlite.js.map +0 -1
- package/dist/view-diff.d.ts +0 -13
- package/dist/view-diff.d.ts.map +0 -1
- package/dist/view-diff.js +0 -42
- package/dist/view-diff.js.map +0 -1
- package/src/expected-views.ts +0 -195
- package/src/source-aware-diff.ts +0 -49
- package/src/view-ddl-postgres.ts +0 -15
- package/src/view-ddl-sqlite.ts +0 -7
- package/src/view-diff.ts +0 -55
package/dist/expected-views.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
// expected-views.ts — derive ViewDescriptor[] from projection metadata.
|
|
2
|
-
//
|
|
3
|
-
// Ports the shape of csharp/MetaObjects.Codegen/Schema/PostgresSchema.cs's
|
|
4
|
-
// CreateView. v1 supports:
|
|
5
|
-
//
|
|
6
|
-
// * passthrough origin (no @via) → plain column from the single base entity
|
|
7
|
-
// * aggregate origin (@agg/@of/@via) → correlated subquery over a to-many
|
|
8
|
-
//
|
|
9
|
-
// Deferred to v2 (returns a "-- TODO" view that the runner skips applying):
|
|
10
|
-
//
|
|
11
|
-
// * passthrough origin WITH @via → to-one correlated subquery
|
|
12
|
-
// * collection origin → json_agg over a to-many
|
|
13
|
-
// * multi-base projections → blocked
|
|
14
|
-
//
|
|
15
|
-
// Identifiers are quoted throughout so mixed-case column names (e.g.
|
|
16
|
-
// "programId") survive PG's case-folding pass.
|
|
17
|
-
import { MetaPassthroughOrigin, MetaAggregateOrigin, MetaCollectionOrigin, MetaSource, SOURCE_ROLE_PRIMARY, TYPE_OBJECT, TYPE_ORIGIN, resolveColumnName, resolveTableName, stripPackage, } from "@metaobjectsdev/metadata";
|
|
18
|
-
export function buildExpectedViews(root, strategy) {
|
|
19
|
-
const out = [];
|
|
20
|
-
for (const child of root.ownChildren()) {
|
|
21
|
-
if (child.type !== TYPE_OBJECT)
|
|
22
|
-
continue;
|
|
23
|
-
const proj = child;
|
|
24
|
-
if (proj.isAbstract)
|
|
25
|
-
continue;
|
|
26
|
-
if (!isReadOnlyProjection(proj))
|
|
27
|
-
continue;
|
|
28
|
-
const view = buildView(proj, root, strategy);
|
|
29
|
-
if (view !== null)
|
|
30
|
-
out.push(view);
|
|
31
|
-
}
|
|
32
|
-
return out;
|
|
33
|
-
}
|
|
34
|
-
function isReadOnlyProjection(entity) {
|
|
35
|
-
const sources = entity.ownChildren().filter((c) => c instanceof MetaSource);
|
|
36
|
-
if (sources.length === 0)
|
|
37
|
-
return false;
|
|
38
|
-
const hasReadOnly = sources.some((s) => s.isReadOnly());
|
|
39
|
-
const hasWritable = sources.some((s) => !s.isReadOnly());
|
|
40
|
-
return hasReadOnly && !hasWritable;
|
|
41
|
-
}
|
|
42
|
-
function buildView(projection, root, strategy) {
|
|
43
|
-
const primarySource = projection.ownChildren().find((c) => c instanceof MetaSource && c.role === SOURCE_ROLE_PRIMARY);
|
|
44
|
-
// Use physicalName (FR-016 kind-aware), NOT tableName — a view-kind source names
|
|
45
|
-
// itself via @view, and tableName reads ONLY the @table slot (undefined for views),
|
|
46
|
-
// which previously bailed every @view projection here before any column was built.
|
|
47
|
-
if (primarySource === undefined)
|
|
48
|
-
return null;
|
|
49
|
-
const viewName = primarySource.physicalName;
|
|
50
|
-
const cols = [];
|
|
51
|
-
let baseEntity;
|
|
52
|
-
let blocked;
|
|
53
|
-
const T = "t"; // target alias in correlated subqueries
|
|
54
|
-
const baseTable = () => {
|
|
55
|
-
if (baseEntity === undefined)
|
|
56
|
-
return undefined;
|
|
57
|
-
const found = root.findObject(baseEntity);
|
|
58
|
-
return found ? resolveTableName(found) : baseEntity;
|
|
59
|
-
};
|
|
60
|
-
for (const f of projection.fields()) {
|
|
61
|
-
const origin = f.ownChildren().find((c) => c.type === TYPE_ORIGIN);
|
|
62
|
-
const fieldCol = resolveColumnName(f, strategy);
|
|
63
|
-
// FR-024 base-link: a projection field that uses `extends: Base.field` (instead of
|
|
64
|
-
// an explicit origin) is an implicit passthrough — its source IS the extended field.
|
|
65
|
-
// That linkage lives in EXTENDED metadata (superRef / resolveSuper()), which
|
|
66
|
-
// f.ownChildren() cannot see, so without this an extends-based field (typically the
|
|
67
|
-
// PK that declares the projection's base) falls through to "no resolvable origin" and
|
|
68
|
-
// bails the whole view — taking the sibling origin.passthrough renames down with it.
|
|
69
|
-
if (origin === undefined && f.superRef !== undefined && f.superRef.includes(".")) {
|
|
70
|
-
const [ent, field] = splitDot(f.superRef);
|
|
71
|
-
const bare = stripPackage(ent);
|
|
72
|
-
baseEntity ??= bare;
|
|
73
|
-
if (bare !== baseEntity) {
|
|
74
|
-
blocked = "passthrough from multiple base entities";
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
const srcEntity = root.findObject(baseEntity);
|
|
78
|
-
const srcCol = resolveColumnByName(srcEntity, field, strategy);
|
|
79
|
-
cols.push(` "${srcCol}" AS "${fieldCol}"`);
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (origin instanceof MetaPassthroughOrigin && origin.via === undefined &&
|
|
83
|
-
origin.from !== undefined && origin.from.includes(".")) {
|
|
84
|
-
const [ent, field] = splitDot(origin.from);
|
|
85
|
-
const bare = stripPackage(ent);
|
|
86
|
-
baseEntity ??= bare;
|
|
87
|
-
if (bare !== baseEntity) {
|
|
88
|
-
blocked = "passthrough from multiple base entities";
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
const srcEntity = root.findObject(baseEntity);
|
|
92
|
-
const srcCol = resolveColumnByName(srcEntity, field, strategy);
|
|
93
|
-
cols.push(` "${srcCol}" AS "${fieldCol}"`);
|
|
94
|
-
}
|
|
95
|
-
else if (origin instanceof MetaAggregateOrigin && origin.agg !== undefined &&
|
|
96
|
-
origin.of !== undefined && origin.via !== undefined &&
|
|
97
|
-
origin.of.includes(".") && origin.via.includes(".")) {
|
|
98
|
-
const [baseEnt, relName] = splitDot(origin.via);
|
|
99
|
-
const bareBase = stripPackage(baseEnt);
|
|
100
|
-
baseEntity ??= bareBase;
|
|
101
|
-
if (bareBase !== baseEntity) {
|
|
102
|
-
blocked = "aggregate over a different base entity";
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
const fk = resolveToManyFk(root, baseEntity, relName, strategy);
|
|
106
|
-
if (fk === null) {
|
|
107
|
-
blocked = `unresolved to-many FK for @via "${origin.via}" (target needs an identity.reference back to ${baseEntity})`;
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
const ofCol = resolveColumnByName(fk.target, splitDot(origin.of)[1], strategy);
|
|
111
|
-
cols.push(` (SELECT ${origin.agg}(${T}."${ofCol}") ` +
|
|
112
|
-
`FROM "${fk.targetTable}" ${T} ` +
|
|
113
|
-
`WHERE ${T}."${fk.fkCol}" = "${baseTable()}"."${fk.parentCol}") AS "${fieldCol}"`);
|
|
114
|
-
}
|
|
115
|
-
else if (origin instanceof MetaPassthroughOrigin || origin instanceof MetaCollectionOrigin) {
|
|
116
|
-
blocked = `field "${f.name}" uses an origin shape not yet supported by TS migrate-ts (passthrough-via / collection — deferred)`;
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
blocked = `field "${f.name}" has no resolvable origin`;
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (blocked !== undefined || baseEntity === undefined || cols.length === 0) {
|
|
125
|
-
// Caller decides what to do with a null result. For v1 the runner just
|
|
126
|
-
// omits it from expected.views — diff sees no expected view, no
|
|
127
|
-
// create-view change, and an actual leftover view (if any) gets dropped.
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
const sql = `SELECT\n${cols.join(",\n")}\nFROM "${baseTable()}"`;
|
|
131
|
-
const view = { name: viewName, sql };
|
|
132
|
-
return view;
|
|
133
|
-
}
|
|
134
|
-
function splitDot(s) {
|
|
135
|
-
const i = s.indexOf(".");
|
|
136
|
-
return [s.slice(0, i), s.slice(i + 1)];
|
|
137
|
-
}
|
|
138
|
-
function resolveColumnByName(owner, fieldName, strategy) {
|
|
139
|
-
if (owner === undefined)
|
|
140
|
-
return fieldName;
|
|
141
|
-
const field = owner.fields().find((f) => f.name === fieldName);
|
|
142
|
-
return field ? resolveColumnName(field, strategy) : fieldName;
|
|
143
|
-
}
|
|
144
|
-
function resolveToManyFk(root, baseEntityName, relName, strategy) {
|
|
145
|
-
const baseObj = root.findObject(baseEntityName);
|
|
146
|
-
if (baseObj === undefined)
|
|
147
|
-
return null;
|
|
148
|
-
const rel = baseObj.relationships().find((r) => r.name === relName);
|
|
149
|
-
if (rel === undefined || rel.objectRef === undefined)
|
|
150
|
-
return null;
|
|
151
|
-
const target = root.findObject(stripPackage(rel.objectRef));
|
|
152
|
-
if (target === undefined)
|
|
153
|
-
return null;
|
|
154
|
-
const fkRef = target.referenceIdentities().find((r) => r.targetEntity !== undefined && stripPackage(r.targetEntity) === baseEntityName);
|
|
155
|
-
if (fkRef === undefined)
|
|
156
|
-
return null;
|
|
157
|
-
const fkFields = fkRef.fields;
|
|
158
|
-
if (fkFields.length === 0)
|
|
159
|
-
return null;
|
|
160
|
-
const fkCol = resolveColumnByName(target, fkFields[0], strategy);
|
|
161
|
-
const parentFieldName = fkRef.targetFields.length > 0
|
|
162
|
-
? fkRef.targetFields[0]
|
|
163
|
-
: baseObj.primaryIdentity()?.fields[0];
|
|
164
|
-
if (parentFieldName === undefined)
|
|
165
|
-
return null;
|
|
166
|
-
const parentCol = resolveColumnByName(baseObj, parentFieldName, strategy);
|
|
167
|
-
return { target, targetTable: resolveTableName(target), fkCol, parentCol };
|
|
168
|
-
}
|
|
169
|
-
//# sourceMappingURL=expected-views.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"expected-views.js","sourceRoot":"","sources":["../src/expected-views.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { type ViewShape } from "./view-diff.js";
|
|
2
|
-
export interface ViewMigrationInput {
|
|
3
|
-
readonly viewName: string;
|
|
4
|
-
/** Previous view shape (from migration log / prior generation). undefined = new view. */
|
|
5
|
-
readonly prevShape?: ViewShape;
|
|
6
|
-
readonly nextShape: ViewShape;
|
|
7
|
-
/** Full `CREATE VIEW ... AS ...;` SQL for nextShape. */
|
|
8
|
-
readonly createSql: string;
|
|
9
|
-
}
|
|
10
|
-
export interface ViewMigrationsOpts {
|
|
11
|
-
readonly dialect: "postgres" | "sqlite";
|
|
12
|
-
readonly allowBreaking: boolean;
|
|
13
|
-
readonly views: readonly ViewMigrationInput[];
|
|
14
|
-
}
|
|
15
|
-
export interface ViewMigrationsResult {
|
|
16
|
-
readonly migrations: readonly string[];
|
|
17
|
-
readonly errors: readonly string[];
|
|
18
|
-
}
|
|
19
|
-
export declare function computeViewMigrations(opts: ViewMigrationsOpts): ViewMigrationsResult;
|
|
20
|
-
//# sourceMappingURL=source-aware-diff.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"source-aware-diff.d.ts","sourceRoot":"","sources":["../src/source-aware-diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIlE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yFAAyF;IACzF,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,wDAAwD;IACxD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAC/C;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,GAAG,oBAAoB,CAwBpF"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { classifyViewDiff } from "./view-diff.js";
|
|
2
|
-
import { emitPostgresViewMigration } from "./view-ddl-postgres.js";
|
|
3
|
-
import { emitSqliteViewMigration } from "./view-ddl-sqlite.js";
|
|
4
|
-
export function computeViewMigrations(opts) {
|
|
5
|
-
const migrations = [];
|
|
6
|
-
const errors = [];
|
|
7
|
-
for (const view of opts.views) {
|
|
8
|
-
const diffClass = view.prevShape
|
|
9
|
-
? classifyViewDiff(view.prevShape, view.nextShape)
|
|
10
|
-
: "safe-append"; // new view = treated like safe-append
|
|
11
|
-
if (diffClass === "breaking" && !opts.allowBreaking) {
|
|
12
|
-
errors.push(`View ${view.viewName} has a breaking change. Pass --allow-breaking to allow drop+recreate.`);
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
const emit = opts.dialect === "postgres"
|
|
16
|
-
? emitPostgresViewMigration
|
|
17
|
-
: emitSqliteViewMigration;
|
|
18
|
-
const sql = emit({ diffClass, viewName: view.viewName, createSql: view.createSql });
|
|
19
|
-
if (sql)
|
|
20
|
-
migrations.push(sql);
|
|
21
|
-
}
|
|
22
|
-
return { migrations, errors };
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=source-aware-diff.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"source-aware-diff.js","sourceRoot":"","sources":["../src/source-aware-diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAkB,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAsB/D,MAAM,UAAU,qBAAqB,CAAC,IAAwB;IAC5D,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS;YAC9B,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC;YAClD,CAAC,CAAC,aAAa,CAAC,CAAE,sCAAsC;QAE1D,IAAI,SAAS,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACpD,MAAM,CAAC,IAAI,CACT,QAAQ,IAAI,CAAC,QAAQ,uEAAuE,CAC7F,CAAC;YACF,SAAS;QACX,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,KAAK,UAAU;YACtC,CAAC,CAAC,yBAAyB;YAC3B,CAAC,CAAC,uBAAuB,CAAC;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QACpF,IAAI,GAAG;YAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"view-ddl-postgres.d.ts","sourceRoot":"","sources":["../src/view-ddl-postgres.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAUzE"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export {} from "./view-diff.js";
|
|
2
|
-
export function emitPostgresViewMigration(opts) {
|
|
3
|
-
switch (opts.diffClass) {
|
|
4
|
-
case "no-change": return "";
|
|
5
|
-
case "safe-append":
|
|
6
|
-
case "safe-replace":
|
|
7
|
-
// Replace CREATE VIEW with CREATE OR REPLACE VIEW.
|
|
8
|
-
return opts.createSql.replace(/^CREATE VIEW\b/i, "CREATE OR REPLACE VIEW");
|
|
9
|
-
case "breaking":
|
|
10
|
-
return `DROP VIEW IF EXISTS ${opts.viewName} CASCADE;\n${opts.createSql}`;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=view-ddl-postgres.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"view-ddl-postgres.js","sourceRoot":"","sources":["../src/view-ddl-postgres.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,MAAM,gBAAgB,CAAC;AAExD,MAAM,UAAU,yBAAyB,CAAC,IAAuB;IAC/D,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;QACvB,KAAK,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;QAC5B,KAAK,aAAa,CAAC;QACnB,KAAK,cAAc;YACjB,mDAAmD;YACnD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,wBAAwB,CAAC,CAAC;QAC7E,KAAK,UAAU;YACb,OAAO,uBAAuB,IAAI,CAAC,QAAQ,cAAc,IAAI,CAAC,SAAS,EAAE,CAAC;IAC9E,CAAC;AACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"view-ddl-sqlite.d.ts","sourceRoot":"","sources":["../src/view-ddl-sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAIvE"}
|
package/dist/view-ddl-sqlite.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export function emitSqliteViewMigration(opts) {
|
|
2
|
-
if (opts.diffClass === "no-change")
|
|
3
|
-
return "";
|
|
4
|
-
// SQLite has no CREATE OR REPLACE VIEW; must drop+create. Wrap in txn.
|
|
5
|
-
return `BEGIN;\nDROP VIEW IF EXISTS ${opts.viewName};\n${opts.createSql}\nCOMMIT;`;
|
|
6
|
-
}
|
|
7
|
-
//# sourceMappingURL=view-ddl-sqlite.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"view-ddl-sqlite.js","sourceRoot":"","sources":["../src/view-ddl-sqlite.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CAAC,IAAuB;IAC7D,IAAI,IAAI,CAAC,SAAS,KAAK,WAAW;QAAE,OAAO,EAAE,CAAC;IAC9C,uEAAuE;IACvE,OAAO,+BAA+B,IAAI,CAAC,QAAQ,MAAM,IAAI,CAAC,SAAS,WAAW,CAAC;AACrF,CAAC"}
|
package/dist/view-diff.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export interface ViewShape {
|
|
2
|
-
readonly columns: readonly string[];
|
|
3
|
-
readonly columnTypes?: Readonly<Record<string, string>>;
|
|
4
|
-
}
|
|
5
|
-
export type ViewDiffClass = "no-change" | "safe-append" | "safe-replace" | "breaking";
|
|
6
|
-
export interface ViewMigrationOpts {
|
|
7
|
-
readonly diffClass: ViewDiffClass;
|
|
8
|
-
readonly viewName: string;
|
|
9
|
-
/** Full `CREATE VIEW <name> AS ...;` statement (semicolon included). */
|
|
10
|
-
readonly createSql: string;
|
|
11
|
-
}
|
|
12
|
-
export declare function classifyViewDiff(prev: ViewShape, next: ViewShape): ViewDiffClass;
|
|
13
|
-
//# sourceMappingURL=view-diff.d.ts.map
|
package/dist/view-diff.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"view-diff.d.ts","sourceRoot":"","sources":["../src/view-diff.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACzD;AAED,MAAM,MAAM,aAAa,GACrB,WAAW,GACX,aAAa,GACb,cAAc,GACd,UAAU,CAAC;AAEf,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,aAAa,CAoChF"}
|
package/dist/view-diff.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export function classifyViewDiff(prev, next) {
|
|
2
|
-
const prevCols = prev.columns;
|
|
3
|
-
const nextCols = next.columns;
|
|
4
|
-
// Dropped column = breaking.
|
|
5
|
-
for (const c of prevCols) {
|
|
6
|
-
if (!nextCols.includes(c))
|
|
7
|
-
return "breaking";
|
|
8
|
-
}
|
|
9
|
-
// Type change on existing column = breaking.
|
|
10
|
-
if (prev.columnTypes && next.columnTypes) {
|
|
11
|
-
for (const c of prevCols) {
|
|
12
|
-
const pt = prev.columnTypes[c];
|
|
13
|
-
const nt = next.columnTypes[c];
|
|
14
|
-
if (pt && nt && pt !== nt)
|
|
15
|
-
return "breaking";
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
if (prevCols.length === nextCols.length) {
|
|
19
|
-
// Same length, same set → either identical order or reordered.
|
|
20
|
-
let identical = true;
|
|
21
|
-
for (let i = 0; i < prevCols.length; i++) {
|
|
22
|
-
if (prevCols[i] !== nextCols[i]) {
|
|
23
|
-
identical = false;
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return identical ? "no-change" : "safe-replace";
|
|
28
|
-
}
|
|
29
|
-
// Longer next, same prefix? → safe-append.
|
|
30
|
-
let prefixMatches = true;
|
|
31
|
-
for (let i = 0; i < prevCols.length; i++) {
|
|
32
|
-
if (prevCols[i] !== nextCols[i]) {
|
|
33
|
-
prefixMatches = false;
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
if (prefixMatches)
|
|
38
|
-
return "safe-append";
|
|
39
|
-
// Mixed: reordered + appended → safe-replace (data shape unchanged at column level).
|
|
40
|
-
return "safe-replace";
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=view-diff.js.map
|
package/dist/view-diff.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"view-diff.js","sourceRoot":"","sources":["../src/view-diff.ts"],"names":[],"mappings":"AAkBA,MAAM,UAAU,gBAAgB,CAAC,IAAe,EAAE,IAAe;IAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;IAE9B,6BAA6B;IAC7B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,UAAU,CAAC;IAC/C,CAAC;IAED,6CAA6C;IAC7C,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAC/B,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE;gBAAE,OAAO,UAAU,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;QACxC,+DAA+D;QAC/D,IAAI,SAAS,GAAG,IAAI,CAAC;QACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;gBAAC,SAAS,GAAG,KAAK,CAAC;gBAAC,MAAM;YAAC,CAAC;QAChE,CAAC;QACD,OAAO,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC;IAClD,CAAC;IAED,2CAA2C;IAC3C,IAAI,aAAa,GAAG,IAAI,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;YAAC,aAAa,GAAG,KAAK,CAAC;YAAC,MAAM;QAAC,CAAC;IACpE,CAAC;IACD,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IAExC,qFAAqF;IACrF,OAAO,cAAc,CAAC;AACxB,CAAC"}
|
package/src/expected-views.ts
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
// expected-views.ts — derive ViewDescriptor[] from projection metadata.
|
|
2
|
-
//
|
|
3
|
-
// Ports the shape of csharp/MetaObjects.Codegen/Schema/PostgresSchema.cs's
|
|
4
|
-
// CreateView. v1 supports:
|
|
5
|
-
//
|
|
6
|
-
// * passthrough origin (no @via) → plain column from the single base entity
|
|
7
|
-
// * aggregate origin (@agg/@of/@via) → correlated subquery over a to-many
|
|
8
|
-
//
|
|
9
|
-
// Deferred to v2 (returns a "-- TODO" view that the runner skips applying):
|
|
10
|
-
//
|
|
11
|
-
// * passthrough origin WITH @via → to-one correlated subquery
|
|
12
|
-
// * collection origin → json_agg over a to-many
|
|
13
|
-
// * multi-base projections → blocked
|
|
14
|
-
//
|
|
15
|
-
// Identifiers are quoted throughout so mixed-case column names (e.g.
|
|
16
|
-
// "programId") survive PG's case-folding pass.
|
|
17
|
-
|
|
18
|
-
import type { MetaObject, MetaRoot } from "@metaobjectsdev/metadata";
|
|
19
|
-
import {
|
|
20
|
-
type ColumnNamingStrategy,
|
|
21
|
-
MetaPassthroughOrigin,
|
|
22
|
-
MetaAggregateOrigin,
|
|
23
|
-
MetaCollectionOrigin,
|
|
24
|
-
MetaSource,
|
|
25
|
-
SOURCE_ROLE_PRIMARY,
|
|
26
|
-
TYPE_OBJECT,
|
|
27
|
-
TYPE_ORIGIN,
|
|
28
|
-
resolveColumnName,
|
|
29
|
-
resolveTableName,
|
|
30
|
-
stripPackage,
|
|
31
|
-
} from "@metaobjectsdev/metadata";
|
|
32
|
-
import type { ViewDescriptor } from "./types.js";
|
|
33
|
-
|
|
34
|
-
export function buildExpectedViews(root: MetaRoot, strategy: ColumnNamingStrategy): ViewDescriptor[] {
|
|
35
|
-
const out: ViewDescriptor[] = [];
|
|
36
|
-
for (const child of root.ownChildren()) {
|
|
37
|
-
if (child.type !== TYPE_OBJECT) continue;
|
|
38
|
-
const proj = child as MetaObject;
|
|
39
|
-
if (proj.isAbstract) continue;
|
|
40
|
-
if (!isReadOnlyProjection(proj)) continue;
|
|
41
|
-
const view = buildView(proj, root, strategy);
|
|
42
|
-
if (view !== null) out.push(view);
|
|
43
|
-
}
|
|
44
|
-
return out;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function isReadOnlyProjection(entity: MetaObject): boolean {
|
|
48
|
-
const sources = entity.ownChildren().filter((c): c is MetaSource => c instanceof MetaSource);
|
|
49
|
-
if (sources.length === 0) return false;
|
|
50
|
-
const hasReadOnly = sources.some((s) => s.isReadOnly());
|
|
51
|
-
const hasWritable = sources.some((s) => !s.isReadOnly());
|
|
52
|
-
return hasReadOnly && !hasWritable;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function buildView(
|
|
56
|
-
projection: MetaObject, root: MetaRoot, strategy: ColumnNamingStrategy,
|
|
57
|
-
): ViewDescriptor | null {
|
|
58
|
-
const primarySource = projection.ownChildren().find(
|
|
59
|
-
(c): c is MetaSource => c instanceof MetaSource && c.role === SOURCE_ROLE_PRIMARY,
|
|
60
|
-
);
|
|
61
|
-
// Use physicalName (FR-016 kind-aware), NOT tableName — a view-kind source names
|
|
62
|
-
// itself via @view, and tableName reads ONLY the @table slot (undefined for views),
|
|
63
|
-
// which previously bailed every @view projection here before any column was built.
|
|
64
|
-
if (primarySource === undefined) return null;
|
|
65
|
-
const viewName = primarySource.physicalName;
|
|
66
|
-
|
|
67
|
-
const cols: string[] = [];
|
|
68
|
-
let baseEntity: string | undefined;
|
|
69
|
-
let blocked: string | undefined;
|
|
70
|
-
const T = "t"; // target alias in correlated subqueries
|
|
71
|
-
|
|
72
|
-
const baseTable = () => {
|
|
73
|
-
if (baseEntity === undefined) return undefined;
|
|
74
|
-
const found = root.findObject(baseEntity);
|
|
75
|
-
return found ? resolveTableName(found) : baseEntity;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
for (const f of projection.fields()) {
|
|
79
|
-
const origin = f.ownChildren().find((c) => c.type === TYPE_ORIGIN);
|
|
80
|
-
const fieldCol = resolveColumnName(f, strategy);
|
|
81
|
-
|
|
82
|
-
// FR-024 base-link: a projection field that uses `extends: Base.field` (instead of
|
|
83
|
-
// an explicit origin) is an implicit passthrough — its source IS the extended field.
|
|
84
|
-
// That linkage lives in EXTENDED metadata (superRef / resolveSuper()), which
|
|
85
|
-
// f.ownChildren() cannot see, so without this an extends-based field (typically the
|
|
86
|
-
// PK that declares the projection's base) falls through to "no resolvable origin" and
|
|
87
|
-
// bails the whole view — taking the sibling origin.passthrough renames down with it.
|
|
88
|
-
if (origin === undefined && f.superRef !== undefined && f.superRef.includes(".")) {
|
|
89
|
-
const [ent, field] = splitDot(f.superRef);
|
|
90
|
-
const bare = stripPackage(ent);
|
|
91
|
-
baseEntity ??= bare;
|
|
92
|
-
if (bare !== baseEntity) { blocked = "passthrough from multiple base entities"; break; }
|
|
93
|
-
const srcEntity = root.findObject(baseEntity);
|
|
94
|
-
const srcCol = resolveColumnByName(srcEntity, field, strategy);
|
|
95
|
-
cols.push(` "${srcCol}" AS "${fieldCol}"`);
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (origin instanceof MetaPassthroughOrigin && origin.via === undefined &&
|
|
100
|
-
origin.from !== undefined && origin.from.includes(".")) {
|
|
101
|
-
const [ent, field] = splitDot(origin.from);
|
|
102
|
-
const bare = stripPackage(ent);
|
|
103
|
-
baseEntity ??= bare;
|
|
104
|
-
if (bare !== baseEntity) { blocked = "passthrough from multiple base entities"; break; }
|
|
105
|
-
const srcEntity = root.findObject(baseEntity);
|
|
106
|
-
const srcCol = resolveColumnByName(srcEntity, field, strategy);
|
|
107
|
-
cols.push(` "${srcCol}" AS "${fieldCol}"`);
|
|
108
|
-
} else if (origin instanceof MetaAggregateOrigin && origin.agg !== undefined &&
|
|
109
|
-
origin.of !== undefined && origin.via !== undefined &&
|
|
110
|
-
origin.of.includes(".") && origin.via.includes(".")) {
|
|
111
|
-
const [baseEnt, relName] = splitDot(origin.via);
|
|
112
|
-
const bareBase = stripPackage(baseEnt);
|
|
113
|
-
baseEntity ??= bareBase;
|
|
114
|
-
if (bareBase !== baseEntity) { blocked = "aggregate over a different base entity"; break; }
|
|
115
|
-
const fk = resolveToManyFk(root, baseEntity, relName, strategy);
|
|
116
|
-
if (fk === null) {
|
|
117
|
-
blocked = `unresolved to-many FK for @via "${origin.via}" (target needs an identity.reference back to ${baseEntity})`;
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
const ofCol = resolveColumnByName(fk.target, splitDot(origin.of)[1], strategy);
|
|
121
|
-
cols.push(
|
|
122
|
-
` (SELECT ${origin.agg}(${T}."${ofCol}") ` +
|
|
123
|
-
`FROM "${fk.targetTable}" ${T} ` +
|
|
124
|
-
`WHERE ${T}."${fk.fkCol}" = "${baseTable()}"."${fk.parentCol}") AS "${fieldCol}"`,
|
|
125
|
-
);
|
|
126
|
-
} else if (origin instanceof MetaPassthroughOrigin || origin instanceof MetaCollectionOrigin) {
|
|
127
|
-
blocked = `field "${f.name}" uses an origin shape not yet supported by TS migrate-ts (passthrough-via / collection — deferred)`;
|
|
128
|
-
break;
|
|
129
|
-
} else {
|
|
130
|
-
blocked = `field "${f.name}" has no resolvable origin`;
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (blocked !== undefined || baseEntity === undefined || cols.length === 0) {
|
|
136
|
-
// Caller decides what to do with a null result. For v1 the runner just
|
|
137
|
-
// omits it from expected.views — diff sees no expected view, no
|
|
138
|
-
// create-view change, and an actual leftover view (if any) gets dropped.
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const sql = `SELECT\n${cols.join(",\n")}\nFROM "${baseTable()}"`;
|
|
143
|
-
const view: ViewDescriptor = { name: viewName, sql };
|
|
144
|
-
return view;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function splitDot(s: string): [string, string] {
|
|
148
|
-
const i = s.indexOf(".");
|
|
149
|
-
return [s.slice(0, i), s.slice(i + 1)];
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function resolveColumnByName(
|
|
153
|
-
owner: MetaObject | undefined, fieldName: string, strategy: ColumnNamingStrategy,
|
|
154
|
-
): string {
|
|
155
|
-
if (owner === undefined) return fieldName;
|
|
156
|
-
const field = owner.fields().find((f) => f.name === fieldName);
|
|
157
|
-
return field ? resolveColumnName(field, strategy) : fieldName;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
interface ToManyFk {
|
|
161
|
-
target: MetaObject;
|
|
162
|
-
targetTable: string;
|
|
163
|
-
/** Column on the target entity holding the FK back to the base. */
|
|
164
|
-
fkCol: string;
|
|
165
|
-
/** Column on the base entity the FK references (usually the base's PK). */
|
|
166
|
-
parentCol: string;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function resolveToManyFk(
|
|
170
|
-
root: MetaRoot, baseEntityName: string, relName: string, strategy: ColumnNamingStrategy,
|
|
171
|
-
): ToManyFk | null {
|
|
172
|
-
const baseObj = root.findObject(baseEntityName);
|
|
173
|
-
if (baseObj === undefined) return null;
|
|
174
|
-
const rel = baseObj.relationships().find((r) => r.name === relName);
|
|
175
|
-
if (rel === undefined || rel.objectRef === undefined) return null;
|
|
176
|
-
const target = root.findObject(stripPackage(rel.objectRef));
|
|
177
|
-
if (target === undefined) return null;
|
|
178
|
-
|
|
179
|
-
const fkRef = target.referenceIdentities().find(
|
|
180
|
-
(r) => r.targetEntity !== undefined && stripPackage(r.targetEntity) === baseEntityName,
|
|
181
|
-
);
|
|
182
|
-
if (fkRef === undefined) return null;
|
|
183
|
-
const fkFields = fkRef.fields;
|
|
184
|
-
if (fkFields.length === 0) return null;
|
|
185
|
-
const fkCol = resolveColumnByName(target, fkFields[0]!, strategy);
|
|
186
|
-
|
|
187
|
-
const parentFieldName = fkRef.targetFields.length > 0
|
|
188
|
-
? fkRef.targetFields[0]!
|
|
189
|
-
: baseObj.primaryIdentity()?.fields[0];
|
|
190
|
-
if (parentFieldName === undefined) return null;
|
|
191
|
-
const parentCol = resolveColumnByName(baseObj, parentFieldName, strategy);
|
|
192
|
-
|
|
193
|
-
return { target, targetTable: resolveTableName(target), fkCol, parentCol };
|
|
194
|
-
}
|
|
195
|
-
|
package/src/source-aware-diff.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { classifyViewDiff, type ViewShape } from "./view-diff.js";
|
|
2
|
-
import { emitPostgresViewMigration } from "./view-ddl-postgres.js";
|
|
3
|
-
import { emitSqliteViewMigration } from "./view-ddl-sqlite.js";
|
|
4
|
-
|
|
5
|
-
export interface ViewMigrationInput {
|
|
6
|
-
readonly viewName: string;
|
|
7
|
-
/** Previous view shape (from migration log / prior generation). undefined = new view. */
|
|
8
|
-
readonly prevShape?: ViewShape;
|
|
9
|
-
readonly nextShape: ViewShape;
|
|
10
|
-
/** Full `CREATE VIEW ... AS ...;` SQL for nextShape. */
|
|
11
|
-
readonly createSql: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface ViewMigrationsOpts {
|
|
15
|
-
readonly dialect: "postgres" | "sqlite";
|
|
16
|
-
readonly allowBreaking: boolean;
|
|
17
|
-
readonly views: readonly ViewMigrationInput[];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ViewMigrationsResult {
|
|
21
|
-
readonly migrations: readonly string[];
|
|
22
|
-
readonly errors: readonly string[];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function computeViewMigrations(opts: ViewMigrationsOpts): ViewMigrationsResult {
|
|
26
|
-
const migrations: string[] = [];
|
|
27
|
-
const errors: string[] = [];
|
|
28
|
-
|
|
29
|
-
for (const view of opts.views) {
|
|
30
|
-
const diffClass = view.prevShape
|
|
31
|
-
? classifyViewDiff(view.prevShape, view.nextShape)
|
|
32
|
-
: "safe-append"; // new view = treated like safe-append
|
|
33
|
-
|
|
34
|
-
if (diffClass === "breaking" && !opts.allowBreaking) {
|
|
35
|
-
errors.push(
|
|
36
|
-
`View ${view.viewName} has a breaking change. Pass --allow-breaking to allow drop+recreate.`
|
|
37
|
-
);
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const emit = opts.dialect === "postgres"
|
|
42
|
-
? emitPostgresViewMigration
|
|
43
|
-
: emitSqliteViewMigration;
|
|
44
|
-
const sql = emit({ diffClass, viewName: view.viewName, createSql: view.createSql });
|
|
45
|
-
if (sql) migrations.push(sql);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return { migrations, errors };
|
|
49
|
-
}
|
package/src/view-ddl-postgres.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { ViewMigrationOpts } from "./view-diff.js";
|
|
2
|
-
|
|
3
|
-
export { type ViewMigrationOpts } from "./view-diff.js";
|
|
4
|
-
|
|
5
|
-
export function emitPostgresViewMigration(opts: ViewMigrationOpts): string {
|
|
6
|
-
switch (opts.diffClass) {
|
|
7
|
-
case "no-change": return "";
|
|
8
|
-
case "safe-append":
|
|
9
|
-
case "safe-replace":
|
|
10
|
-
// Replace CREATE VIEW with CREATE OR REPLACE VIEW.
|
|
11
|
-
return opts.createSql.replace(/^CREATE VIEW\b/i, "CREATE OR REPLACE VIEW");
|
|
12
|
-
case "breaking":
|
|
13
|
-
return `DROP VIEW IF EXISTS ${opts.viewName} CASCADE;\n${opts.createSql}`;
|
|
14
|
-
}
|
|
15
|
-
}
|
package/src/view-ddl-sqlite.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { ViewMigrationOpts } from "./view-diff.js";
|
|
2
|
-
|
|
3
|
-
export function emitSqliteViewMigration(opts: ViewMigrationOpts): string {
|
|
4
|
-
if (opts.diffClass === "no-change") return "";
|
|
5
|
-
// SQLite has no CREATE OR REPLACE VIEW; must drop+create. Wrap in txn.
|
|
6
|
-
return `BEGIN;\nDROP VIEW IF EXISTS ${opts.viewName};\n${opts.createSql}\nCOMMIT;`;
|
|
7
|
-
}
|