@prisma-next/family-sql 0.14.0-dev.57 → 0.14.0-dev.59
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/{control-adapter-BVOj4gXI.d.mts → control-adapter-BeN8TDZ5.d.mts} +3 -16
- package/dist/control-adapter-BeN8TDZ5.d.mts.map +1 -0
- package/dist/control-adapter.d.mts +1 -1
- package/dist/control.d.mts +2 -2
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +14 -3
- package/dist/control.mjs.map +1 -1
- package/dist/diff.d.mts +4 -52
- package/dist/diff.d.mts.map +1 -1
- package/dist/diff.mjs +1 -175
- package/dist/diff.mjs.map +1 -1
- package/dist/migration.d.mts +1 -1
- package/dist/{types-BW7pzb2j.d.mts → types-COTsneBU.d.mts} +2 -2
- package/dist/{types-BW7pzb2j.d.mts.map → types-COTsneBU.d.mts.map} +1 -1
- package/package.json +21 -21
- package/src/core/diff/sql-schema-diff.ts +3 -39
- package/src/core/migrations/contract-to-schema-ir.ts +17 -3
- package/src/exports/diff.ts +4 -17
- package/dist/control-adapter-BVOj4gXI.d.mts.map +0 -1
- package/src/core/diff/diff-tree-normalization.ts +0 -211
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ⚠️ ACCEPTED-UNDER-PROTEST DEBT — DELETE IN SLICE 2.6 (`unify-unique-and-index-nodes`).
|
|
3
|
-
*
|
|
4
|
-
* A unique constraint IS a unique index. This slice modeled uniques and indexes
|
|
5
|
-
* as two separate node kinds (`SqlUniqueIR` vs `SqlIndexIR` — even though
|
|
6
|
-
* `SqlIndexIR` already carries `unique: boolean`), so the strict-by-kind differ
|
|
7
|
-
* cannot pair a contract unique with a live unique index. This whole file
|
|
8
|
-
* exists only to reconcile those two representations after the fact — it
|
|
9
|
-
* should not exist.
|
|
10
|
-
*
|
|
11
|
-
* The fix — delete `SqlUniqueIR`, model unique enforcement as
|
|
12
|
-
* `SqlIndexIR { unique: true }` on both derivation and introspection, and
|
|
13
|
-
* delete `diff-tree-normalization.ts` — is specified in
|
|
14
|
-
* `projects/postgres-rls/slices/unify-unique-and-index-nodes/spec.md` and
|
|
15
|
-
* scheduled as the very next slice. Do NOT extend this.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Pre-diff tree normalization: adjustments to the actual/expected trees the
|
|
20
|
-
* generic differ (`diffSchemas`) runs over, so legacy semantic-equivalence
|
|
21
|
-
* rules (a unique constraint satisfied by a unique index, an FK schema
|
|
22
|
-
* segment that differs only in spelling) surface as same-kind node pairs
|
|
23
|
-
* instead of spurious drift.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import {
|
|
27
|
-
SqlForeignKeyIR,
|
|
28
|
-
SqlIndexIR,
|
|
29
|
-
SqlSchemaIR,
|
|
30
|
-
SqlTableIR,
|
|
31
|
-
SqlUniqueIR,
|
|
32
|
-
} from '@prisma-next/sql-schema-ir/types';
|
|
33
|
-
|
|
34
|
-
// ============================================================================
|
|
35
|
-
// Semantic satisfaction — derivation-side normalization of the actual tree
|
|
36
|
-
// ============================================================================
|
|
37
|
-
|
|
38
|
-
export interface SemanticSatisfactionInput {
|
|
39
|
-
readonly expectedUniques: readonly SqlUniqueIR[];
|
|
40
|
-
readonly expectedIndexes: readonly SqlIndexIR[];
|
|
41
|
-
readonly actualUniques: readonly SqlUniqueIR[];
|
|
42
|
-
readonly actualIndexes: readonly SqlIndexIR[];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface SemanticSatisfactionResult {
|
|
46
|
-
readonly actualUniques: readonly SqlUniqueIR[];
|
|
47
|
-
readonly actualIndexes: readonly SqlIndexIR[];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function sameColumns(a: readonly string[], b: readonly string[]): boolean {
|
|
51
|
-
return a.length === b.length && a.every((c, i) => c === b[i]);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Adjusts a table pair's ACTUAL unique/index child lists so the legacy
|
|
56
|
-
* walk's cross-kind semantic satisfaction materializes as same-kind node
|
|
57
|
-
* pairs for the differ (the differ pairs strictly by id, so a `unique:`
|
|
58
|
-
* node can never pair with an `index:` node). Three legacy rules, ported
|
|
59
|
-
* from `isUniqueConstraintSatisfied` / `isIndexSatisfied` and the
|
|
60
|
-
* strict-extras loops of the retired relational walk:
|
|
61
|
-
*
|
|
62
|
-
* 1. A contract unique satisfied by a live unique INDEX: the actual index
|
|
63
|
-
* node is reclassified as a unique node (it pairs with the expected
|
|
64
|
-
* unique and stops being a candidate extra).
|
|
65
|
-
* 2. A contract index (with no type/options demands) satisfied by a live
|
|
66
|
-
* unique CONSTRAINT: a same-kind actual index node is synthesized so
|
|
67
|
-
* the expected index pairs — the unique constraint itself stays (the
|
|
68
|
-
* legacy strict-extras loop still reports it as an undeclared unique).
|
|
69
|
-
* 3. Live unique indexes are never extras in the legacy walk (its
|
|
70
|
-
* strict-extras loop skips `unique: true` rows), so any remaining
|
|
71
|
-
* actual unique-index node with no expected index counterpart is
|
|
72
|
-
* dropped rather than surfacing as `not-expected`.
|
|
73
|
-
*/
|
|
74
|
-
export function resolveSemanticSatisfaction(
|
|
75
|
-
input: SemanticSatisfactionInput,
|
|
76
|
-
): SemanticSatisfactionResult {
|
|
77
|
-
let actualIndexes = [...input.actualIndexes];
|
|
78
|
-
const actualUniques = [...input.actualUniques];
|
|
79
|
-
|
|
80
|
-
// Rule 1: reclassify a satisfying unique index as the unique constraint
|
|
81
|
-
// the contract declared.
|
|
82
|
-
for (const expectedUnique of input.expectedUniques) {
|
|
83
|
-
const alreadyPaired = actualUniques.some((u) => sameColumns(u.columns, expectedUnique.columns));
|
|
84
|
-
if (alreadyPaired) continue;
|
|
85
|
-
const satisfyingIndex = actualIndexes.find(
|
|
86
|
-
(idx) => idx.unique && sameColumns(idx.columns, expectedUnique.columns),
|
|
87
|
-
);
|
|
88
|
-
if (satisfyingIndex) {
|
|
89
|
-
actualIndexes = actualIndexes.filter((idx) => idx !== satisfyingIndex);
|
|
90
|
-
actualUniques.push(
|
|
91
|
-
new SqlUniqueIR({
|
|
92
|
-
columns: satisfyingIndex.columns,
|
|
93
|
-
...(satisfyingIndex.name !== undefined ? { name: satisfyingIndex.name } : {}),
|
|
94
|
-
}),
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Rule 2: synthesize an index node from a satisfying unique constraint.
|
|
100
|
-
for (const expectedIndex of input.expectedIndexes) {
|
|
101
|
-
if (expectedIndex.type !== undefined || expectedIndex.options !== undefined) continue;
|
|
102
|
-
const alreadyPaired = actualIndexes.some((idx) =>
|
|
103
|
-
sameColumns(idx.columns, expectedIndex.columns),
|
|
104
|
-
);
|
|
105
|
-
if (alreadyPaired) continue;
|
|
106
|
-
const satisfyingUnique = actualUniques.find((u) =>
|
|
107
|
-
sameColumns(u.columns, expectedIndex.columns),
|
|
108
|
-
);
|
|
109
|
-
if (satisfyingUnique) {
|
|
110
|
-
actualIndexes.push(new SqlIndexIR({ columns: satisfyingUnique.columns, unique: false }));
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Rule 3: remaining unique indexes with no expected counterpart are
|
|
115
|
-
// invisible to the legacy extras loop — drop them.
|
|
116
|
-
actualIndexes = actualIndexes.filter(
|
|
117
|
-
(idx) =>
|
|
118
|
-
!idx.unique || input.expectedIndexes.some((exp) => sameColumns(exp.columns, idx.columns)),
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
return { actualUniques, actualIndexes };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ============================================================================
|
|
125
|
-
// Flat-tree helpers (single-schema targets)
|
|
126
|
-
// ============================================================================
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Applies {@link resolveSemanticSatisfaction} across a flat table pair set:
|
|
130
|
-
* every actual table with an expected counterpart gets its unique/index
|
|
131
|
-
* child lists adjusted; unpaired tables pass through untouched.
|
|
132
|
-
*/
|
|
133
|
-
export function normalizeFlatActualForDiff(
|
|
134
|
-
expected: SqlSchemaIR,
|
|
135
|
-
actual: SqlSchemaIR,
|
|
136
|
-
): SqlSchemaIR {
|
|
137
|
-
const tables: Record<string, SqlTableIR> = {};
|
|
138
|
-
for (const [name, actualTable] of Object.entries(actual.tables)) {
|
|
139
|
-
const expectedTable = expected.tables[name];
|
|
140
|
-
if (expectedTable === undefined) {
|
|
141
|
-
tables[name] = actualTable;
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
const adjusted = resolveSemanticSatisfaction({
|
|
145
|
-
expectedUniques: expectedTable.uniques,
|
|
146
|
-
expectedIndexes: expectedTable.indexes,
|
|
147
|
-
actualUniques: actualTable.uniques,
|
|
148
|
-
actualIndexes: actualTable.indexes,
|
|
149
|
-
});
|
|
150
|
-
tables[name] = new SqlTableIR({
|
|
151
|
-
name: actualTable.name,
|
|
152
|
-
columns: actualTable.columns,
|
|
153
|
-
foreignKeys: actualTable.foreignKeys,
|
|
154
|
-
uniques: adjusted.actualUniques,
|
|
155
|
-
indexes: adjusted.actualIndexes,
|
|
156
|
-
...(actualTable.primaryKey !== undefined ? { primaryKey: actualTable.primaryKey } : {}),
|
|
157
|
-
...(actualTable.annotations !== undefined ? { annotations: actualTable.annotations } : {}),
|
|
158
|
-
...(actualTable.checks !== undefined ? { checks: actualTable.checks } : {}),
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
return new SqlSchemaIR({
|
|
162
|
-
tables,
|
|
163
|
-
...(actual.annotations !== undefined ? { annotations: actual.annotations } : {}),
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Neutralizes the FK schema segment on a flat expected tree so its FK diff
|
|
169
|
-
* nodes pair with introspected FKs on single-schema targets: the family
|
|
170
|
-
* converter stamps `referencedSchema` with the contract namespace id
|
|
171
|
-
* verbatim (the unbound sentinel on non-namespaced targets), while a
|
|
172
|
-
* single-schema introspection stamps none — resolving both sides to the
|
|
173
|
-
* empty segment makes the ids meet.
|
|
174
|
-
*/
|
|
175
|
-
export function neutralizeFlatExpectedFkSchemas(expected: SqlSchemaIR): SqlSchemaIR {
|
|
176
|
-
const tables: Record<string, SqlTableIR> = {};
|
|
177
|
-
for (const [name, table] of Object.entries(expected.tables)) {
|
|
178
|
-
if (table.foreignKeys.length === 0) {
|
|
179
|
-
tables[name] = table;
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
const foreignKeys = table.foreignKeys.map(
|
|
183
|
-
(fk) =>
|
|
184
|
-
new SqlForeignKeyIR({
|
|
185
|
-
columns: fk.columns,
|
|
186
|
-
referencedTable: fk.referencedTable,
|
|
187
|
-
referencedColumns: fk.referencedColumns,
|
|
188
|
-
...(fk.referencedSchema !== undefined ? { referencedSchema: fk.referencedSchema } : {}),
|
|
189
|
-
...(fk.name !== undefined ? { name: fk.name } : {}),
|
|
190
|
-
...(fk.onDelete !== undefined ? { onDelete: fk.onDelete } : {}),
|
|
191
|
-
...(fk.onUpdate !== undefined ? { onUpdate: fk.onUpdate } : {}),
|
|
192
|
-
...(fk.annotations !== undefined ? { annotations: fk.annotations } : {}),
|
|
193
|
-
resolvedReferencedNamespace: '',
|
|
194
|
-
}),
|
|
195
|
-
);
|
|
196
|
-
tables[name] = new SqlTableIR({
|
|
197
|
-
name: table.name,
|
|
198
|
-
columns: table.columns,
|
|
199
|
-
foreignKeys,
|
|
200
|
-
uniques: table.uniques,
|
|
201
|
-
indexes: table.indexes,
|
|
202
|
-
...(table.primaryKey !== undefined ? { primaryKey: table.primaryKey } : {}),
|
|
203
|
-
...(table.annotations !== undefined ? { annotations: table.annotations } : {}),
|
|
204
|
-
...(table.checks !== undefined ? { checks: table.checks } : {}),
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
return new SqlSchemaIR({
|
|
208
|
-
tables,
|
|
209
|
-
...(expected.annotations !== undefined ? { annotations: expected.annotations } : {}),
|
|
210
|
-
});
|
|
211
|
-
}
|