turbine-orm 0.65.0 → 0.66.0
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 +34 -32
- package/dist/adapters/cockroachdb.js +21 -3
- package/dist/adapters/index.d.ts +15 -0
- package/dist/adapters/yugabytedb.js +20 -3
- package/dist/cjs/adapters/cockroachdb.js +21 -3
- package/dist/cjs/adapters/index.d.ts +15 -0
- package/dist/cjs/adapters/yugabytedb.js +20 -3
- package/dist/cjs/cli/destructive.d.ts +18 -4
- package/dist/cjs/cli/destructive.js +230 -122
- package/dist/cjs/cli/index.d.ts +21 -4
- package/dist/cjs/cli/index.js +119 -22
- package/dist/cjs/cli/mcp.d.ts +28 -8
- package/dist/cjs/cli/mcp.js +170 -127
- package/dist/cjs/cli/migrate.d.ts +134 -13
- package/dist/cjs/cli/migrate.js +349 -241
- package/dist/cjs/cli/pii-predicate-guard.d.ts +112 -0
- package/dist/cjs/cli/pii-predicate-guard.js +390 -0
- package/dist/cjs/cli/prisma-resolve.js +75 -4
- package/dist/cjs/cli/prisma-schema.d.ts +17 -1
- package/dist/cjs/cli/prisma-schema.js +83 -17
- package/dist/cjs/cli/sql-statements.d.ts +125 -0
- package/dist/cjs/cli/sql-statements.js +378 -0
- package/dist/cjs/cli/studio.js +49 -118
- package/dist/cjs/cli/ui.d.ts +1 -1
- package/dist/cjs/client.d.ts +43 -0
- package/dist/cjs/client.js +125 -6
- package/dist/cjs/dialect.d.ts +123 -0
- package/dist/cjs/dialect.js +33 -0
- package/dist/cjs/errors.d.ts +74 -1
- package/dist/cjs/errors.js +239 -25
- package/dist/cjs/index-advisor.d.ts +33 -1
- package/dist/cjs/index-advisor.js +32 -1
- package/dist/cjs/introspect.d.ts +48 -0
- package/dist/cjs/introspect.js +222 -91
- package/dist/cjs/mssql.js +43 -1
- package/dist/cjs/mysql.d.ts +5 -2
- package/dist/cjs/mysql.js +202 -17
- package/dist/cjs/nested-write.js +6 -1
- package/dist/cjs/pipeline-submittable.js +17 -3
- package/dist/cjs/pipeline.js +75 -9
- package/dist/cjs/powdb.d.ts +23 -0
- package/dist/cjs/powdb.js +33 -1
- package/dist/cjs/powql.d.ts +61 -9
- package/dist/cjs/powql.js +186 -49
- package/dist/cjs/prisma-compat.js +160 -41
- package/dist/cjs/query/aggregates.d.ts +1 -1
- package/dist/cjs/query/aggregates.js +80 -18
- package/dist/cjs/query/batched-loader.d.ts +10 -0
- package/dist/cjs/query/batched-loader.js +268 -7
- package/dist/cjs/query/builder.d.ts +73 -0
- package/dist/cjs/query/builder.js +225 -28
- package/dist/cjs/query/filters.d.ts +162 -0
- package/dist/cjs/query/filters.js +250 -1
- package/dist/cjs/query/relations.d.ts +10 -10
- package/dist/cjs/query/relations.js +93 -12
- package/dist/cjs/query/types.d.ts +14 -1
- package/dist/cjs/query/utils.d.ts +146 -2
- package/dist/cjs/query/utils.js +210 -4
- package/dist/cjs/query/warn-registry.d.ts +10 -0
- package/dist/cjs/query/warn-registry.js +10 -0
- package/dist/cjs/query/where-compile.d.ts +30 -0
- package/dist/cjs/query/where-compile.js +41 -0
- package/dist/cjs/query/where.d.ts +128 -13
- package/dist/cjs/query/where.js +215 -77
- package/dist/cjs/query/writes.d.ts +1 -1
- package/dist/cjs/query/writes.js +39 -15
- package/dist/cjs/schema-builder.d.ts +2 -1
- package/dist/cjs/schema-sql.d.ts +94 -4
- package/dist/cjs/schema-sql.js +506 -30
- package/dist/cjs/schema.d.ts +3 -1
- package/dist/cjs/sqlite.d.ts +6 -0
- package/dist/cjs/sqlite.js +151 -10
- package/dist/cjs/typed-sql.d.ts +29 -1
- package/dist/cjs/typed-sql.js +30 -12
- package/dist/cli/destructive.d.ts +18 -4
- package/dist/cli/destructive.js +229 -121
- package/dist/cli/index.d.ts +21 -4
- package/dist/cli/index.js +120 -24
- package/dist/cli/mcp.d.ts +28 -8
- package/dist/cli/mcp.js +172 -129
- package/dist/cli/migrate.d.ts +134 -13
- package/dist/cli/migrate.js +347 -238
- package/dist/cli/pii-predicate-guard.d.ts +112 -0
- package/dist/cli/pii-predicate-guard.js +386 -0
- package/dist/cli/prisma-resolve.js +75 -4
- package/dist/cli/prisma-schema.d.ts +17 -1
- package/dist/cli/prisma-schema.js +83 -17
- package/dist/cli/sql-statements.d.ts +125 -0
- package/dist/cli/sql-statements.js +373 -0
- package/dist/cli/studio.js +49 -118
- package/dist/cli/ui.d.ts +1 -1
- package/dist/client.d.ts +43 -0
- package/dist/client.js +126 -7
- package/dist/dialect.d.ts +123 -0
- package/dist/dialect.js +33 -0
- package/dist/errors.d.ts +74 -1
- package/dist/errors.js +228 -19
- package/dist/index-advisor.d.ts +33 -1
- package/dist/index-advisor.js +31 -1
- package/dist/introspect.d.ts +48 -0
- package/dist/introspect.js +221 -91
- package/dist/mssql.js +44 -2
- package/dist/mysql.d.ts +5 -2
- package/dist/mysql.js +203 -18
- package/dist/nested-write.js +7 -2
- package/dist/pipeline-submittable.js +18 -4
- package/dist/pipeline.js +76 -10
- package/dist/powdb.d.ts +23 -0
- package/dist/powdb.js +33 -2
- package/dist/powql.d.ts +61 -9
- package/dist/powql.js +187 -50
- package/dist/prisma-compat.js +160 -41
- package/dist/query/aggregates.d.ts +1 -1
- package/dist/query/aggregates.js +82 -20
- package/dist/query/batched-loader.d.ts +10 -0
- package/dist/query/batched-loader.js +270 -9
- package/dist/query/builder.d.ts +73 -0
- package/dist/query/builder.js +226 -30
- package/dist/query/filters.d.ts +162 -0
- package/dist/query/filters.js +246 -1
- package/dist/query/relations.d.ts +10 -10
- package/dist/query/relations.js +94 -14
- package/dist/query/types.d.ts +14 -1
- package/dist/query/utils.d.ts +146 -2
- package/dist/query/utils.js +204 -3
- package/dist/query/warn-registry.d.ts +10 -0
- package/dist/query/warn-registry.js +10 -0
- package/dist/query/where-compile.d.ts +30 -0
- package/dist/query/where-compile.js +40 -1
- package/dist/query/where.d.ts +128 -13
- package/dist/query/where.js +216 -80
- package/dist/query/writes.d.ts +1 -1
- package/dist/query/writes.js +40 -16
- package/dist/schema-builder.d.ts +2 -1
- package/dist/schema-sql.d.ts +94 -4
- package/dist/schema-sql.js +505 -30
- package/dist/schema.d.ts +3 -1
- package/dist/sqlite.d.ts +6 -0
- package/dist/sqlite.js +151 -10
- package/dist/typed-sql.d.ts +29 -1
- package/dist/typed-sql.js +30 -12
- package/package.json +6 -4
package/dist/cjs/cli/index.js
CHANGED
|
@@ -74,6 +74,7 @@ exports.planEnvScaffold = planEnvScaffold;
|
|
|
74
74
|
exports.scaffoldEnvForUrl = scaffoldEnvForUrl;
|
|
75
75
|
exports.initEnvNotice = initEnvNotice;
|
|
76
76
|
exports.resolveMigrateFromPrismaUrl = resolveMigrateFromPrismaUrl;
|
|
77
|
+
exports.readPrismaSchemaSource = readPrismaSchemaSource;
|
|
77
78
|
exports.buildMigrateDeployOptions = buildMigrateDeployOptions;
|
|
78
79
|
exports.getSeedExecutionPlan = getSeedExecutionPlan;
|
|
79
80
|
exports.isLoopbackHost = isLoopbackHost;
|
|
@@ -965,15 +966,21 @@ async function runInitPush(config, url) {
|
|
|
965
966
|
}
|
|
966
967
|
const schemaDef = await loadSchemaFile(config.schemaFile);
|
|
967
968
|
const spinner = new ui_js_1.Spinner('Computing schema diff').start();
|
|
968
|
-
const diff = await (0, schema_sql_js_1.schemaDiff)(schemaDef, url);
|
|
969
|
+
const diff = await (0, schema_sql_js_1.schemaDiff)(schemaDef, url, { schema: config.schema });
|
|
969
970
|
if (diff.statements.length === 0) {
|
|
970
971
|
spinner.succeed('Database already in sync: nothing to push');
|
|
971
972
|
return;
|
|
972
973
|
}
|
|
973
974
|
spinner.succeed(`Found ${(0, ui_js_1.bold)(String(diff.statements.length))} change(s) to apply`);
|
|
975
|
+
// Same as `push`: a type change carries a warning naming what it costs the
|
|
976
|
+
// existing rows, and it is worth no less here than on the standalone command.
|
|
977
|
+
if (diff.warnings && diff.warnings.length > 0) {
|
|
978
|
+
for (const w of diff.warnings)
|
|
979
|
+
(0, ui_js_1.warn)(w);
|
|
980
|
+
}
|
|
974
981
|
const pushSpinner = new ui_js_1.Spinner('Applying schema').start();
|
|
975
982
|
try {
|
|
976
|
-
const result = await (0, schema_sql_js_1.schemaPush)(schemaDef, url, { precomputedDiff: diff });
|
|
983
|
+
const result = await (0, schema_sql_js_1.schemaPush)(schemaDef, url, { precomputedDiff: diff, schema: config.schema });
|
|
977
984
|
pushSpinner.succeed(`Applied ${(0, ui_js_1.bold)(String(result.statementsExecuted))} statement(s)`);
|
|
978
985
|
}
|
|
979
986
|
catch (err) {
|
|
@@ -984,7 +991,11 @@ async function runInitPush(config, url) {
|
|
|
984
991
|
(0, ui_js_1.warn)('Push aborted: no schema changes were applied.');
|
|
985
992
|
return;
|
|
986
993
|
}
|
|
987
|
-
const result = await (0, schema_sql_js_1.schemaPush)(schemaDef, url, {
|
|
994
|
+
const result = await (0, schema_sql_js_1.schemaPush)(schemaDef, url, {
|
|
995
|
+
allowDestructive: true,
|
|
996
|
+
precomputedDiff: diff,
|
|
997
|
+
schema: config.schema,
|
|
998
|
+
});
|
|
988
999
|
(0, ui_js_1.success)(`Applied ${(0, ui_js_1.bold)(String(result.statementsExecuted))} statement(s)`);
|
|
989
1000
|
}
|
|
990
1001
|
}
|
|
@@ -1459,19 +1470,57 @@ function resolveMigrateFromPrismaUrl(configUrl, ast, env) {
|
|
|
1459
1470
|
}
|
|
1460
1471
|
return { source: 'none', missingVariables: lookup.missingVariables };
|
|
1461
1472
|
}
|
|
1473
|
+
/**
|
|
1474
|
+
* Read a Prisma schema from a FILE or from a multi-file schema DIRECTORY
|
|
1475
|
+
* (Prisma >= 5.15, GA). A directory used to reach `readFileSync` unchanged and
|
|
1476
|
+
* die with a raw `EISDIR`, which is a stack trace rather than an answer.
|
|
1477
|
+
*
|
|
1478
|
+
* Prisma treats every `.prisma` file under the directory as one logical schema,
|
|
1479
|
+
* so they are concatenated in sorted order (stable across machines) and parsed
|
|
1480
|
+
* as a single source. Line numbers in any parse error then refer to that
|
|
1481
|
+
* concatenation, so each file is introduced by a comment naming it.
|
|
1482
|
+
*/
|
|
1483
|
+
function readPrismaSchemaSource(path) {
|
|
1484
|
+
if (!(0, node_fs_1.statSync)(path).isDirectory())
|
|
1485
|
+
return (0, node_fs_1.readFileSync)(path, 'utf-8');
|
|
1486
|
+
const files = (0, node_fs_1.readdirSync)(path)
|
|
1487
|
+
.filter((f) => f.endsWith('.prisma'))
|
|
1488
|
+
.sort();
|
|
1489
|
+
if (files.length === 0) {
|
|
1490
|
+
throw new Error(`No .prisma files found in directory ${path}.`);
|
|
1491
|
+
}
|
|
1492
|
+
return files.map((f) => `// file: ${f}\n${(0, node_fs_1.readFileSync)((0, node_path_1.join)(path, f), 'utf-8')}`).join('\n');
|
|
1493
|
+
}
|
|
1462
1494
|
async function cmdMigrateFromPrisma(args, config) {
|
|
1463
1495
|
(0, ui_js_1.banner)();
|
|
1464
|
-
// `--schema` is the Prisma schema file path in this command.
|
|
1465
|
-
|
|
1496
|
+
// `--schema` is the Prisma schema file path in this command. It may also name
|
|
1497
|
+
// a DIRECTORY (Prisma's multi-file schema, GA since 5.15), and the default
|
|
1498
|
+
// `prisma/schema.prisma` falls back to `prisma/schema/` for the same reason.
|
|
1499
|
+
const explicit = args.schema !== undefined;
|
|
1500
|
+
let prismaPath = (0, node_path_1.resolve)(args.schema ?? 'prisma/schema.prisma');
|
|
1501
|
+
if (!explicit && !(0, node_fs_1.existsSync)(prismaPath) && (0, node_fs_1.existsSync)((0, node_path_1.resolve)('prisma/schema'))) {
|
|
1502
|
+
prismaPath = (0, node_path_1.resolve)('prisma/schema');
|
|
1503
|
+
}
|
|
1466
1504
|
if (!(0, node_fs_1.existsSync)(prismaPath)) {
|
|
1467
|
-
(0, ui_js_1.error)(`Prisma schema
|
|
1505
|
+
(0, ui_js_1.error)(`Prisma schema not found: ${(0, ui_js_1.cyan)(prismaPath)}`);
|
|
1468
1506
|
(0, ui_js_1.newline)();
|
|
1469
|
-
console.log(` ${(0, ui_js_1.dim)('Point at it with')} ${(0, ui_js_1.cyan)('--schema <path
|
|
1507
|
+
console.log(` ${(0, ui_js_1.dim)('Point at it with')} ${(0, ui_js_1.cyan)('--schema <path>')} ${(0, ui_js_1.dim)('(a .prisma file, or a directory of them;')}`);
|
|
1508
|
+
console.log(` ${(0, ui_js_1.dim)('default: prisma/schema.prisma, then prisma/schema/).')}`);
|
|
1470
1509
|
(0, ui_js_1.newline)();
|
|
1471
1510
|
process.exit(1);
|
|
1472
1511
|
}
|
|
1473
1512
|
// Parse (fatal only on a construct we must understand).
|
|
1474
|
-
|
|
1513
|
+
let source;
|
|
1514
|
+
try {
|
|
1515
|
+
source = readPrismaSchemaSource(prismaPath);
|
|
1516
|
+
}
|
|
1517
|
+
catch (err) {
|
|
1518
|
+
(0, ui_js_1.newline)();
|
|
1519
|
+
(0, ui_js_1.error)(`Could not read ${(0, ui_js_1.cyan)(prismaPath)}`);
|
|
1520
|
+
console.log(` ${(0, ui_js_1.red)(err instanceof Error ? err.message : String(err))}`);
|
|
1521
|
+
(0, ui_js_1.newline)();
|
|
1522
|
+
process.exit(1);
|
|
1523
|
+
}
|
|
1475
1524
|
let ast;
|
|
1476
1525
|
try {
|
|
1477
1526
|
ast = (0, prisma_schema_js_1.parsePrismaSchema)(source);
|
|
@@ -1543,6 +1592,18 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1543
1592
|
console.log(` ${marker} ${line}`);
|
|
1544
1593
|
}
|
|
1545
1594
|
(0, ui_js_1.newline)();
|
|
1595
|
+
// Parser notes on the CONSOLE, not only in the Markdown report.
|
|
1596
|
+
//
|
|
1597
|
+
// `--no-db` is the first command anyone runs and the one place a parse
|
|
1598
|
+
// problem should surface, and it was the one place that could not: resolution
|
|
1599
|
+
// is skipped, `hasUnresolved` is false by construction, so the run printed a
|
|
1600
|
+
// clean summary and exited 0 no matter what the parser had thrown away.
|
|
1601
|
+
if (result.parseWarnings.length > 0) {
|
|
1602
|
+
(0, ui_js_1.header)('Parser notes');
|
|
1603
|
+
for (const w of result.parseWarnings)
|
|
1604
|
+
console.log(` ${(0, ui_js_1.yellow)(ui_js_1.symbols.warning)} ${w}`);
|
|
1605
|
+
(0, ui_js_1.newline)();
|
|
1606
|
+
}
|
|
1546
1607
|
// Write outputs into the generate outDir.
|
|
1547
1608
|
const outDir = (0, node_path_1.resolve)(config.out);
|
|
1548
1609
|
const rel = (0, node_path_1.relative)(process.cwd(), outDir);
|
|
@@ -1590,6 +1651,15 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1590
1651
|
(0, ui_js_1.newline)();
|
|
1591
1652
|
process.exit(1);
|
|
1592
1653
|
}
|
|
1654
|
+
// `--no-db` has no resolution to fail on, so parser notes are the ONLY signal
|
|
1655
|
+
// it can carry. Treated like an unresolved item, and cleared by the same
|
|
1656
|
+
// `--allow-partial` opt-in, so "exit 0" means the same thing in both modes.
|
|
1657
|
+
if (args.noDb && result.parseWarnings.length > 0 && !args.allowPartial) {
|
|
1658
|
+
(0, ui_js_1.warn)(`${result.parseWarnings.length} parser note(s) above (also in the report). ` +
|
|
1659
|
+
'Re-run with --allow-partial to accept them.');
|
|
1660
|
+
(0, ui_js_1.newline)();
|
|
1661
|
+
process.exit(1);
|
|
1662
|
+
}
|
|
1593
1663
|
if (args.noDb) {
|
|
1594
1664
|
(0, ui_js_1.info)(`Parse-only report written. Re-run without ${(0, ui_js_1.cyan)('--no-db')} against your database to resolve names.`);
|
|
1595
1665
|
}
|
|
@@ -1612,7 +1682,7 @@ async function cmdPush(args, config) {
|
|
|
1612
1682
|
(0, ui_js_1.info)(`Schema defines ${(0, ui_js_1.bold)(String(tableCount))} tables`);
|
|
1613
1683
|
// Compute diff
|
|
1614
1684
|
const diffSpinner = new ui_js_1.Spinner('Computing schema diff').start();
|
|
1615
|
-
const diff = await (0, schema_sql_js_1.schemaDiff)(schemaDef, url);
|
|
1685
|
+
const diff = await (0, schema_sql_js_1.schemaDiff)(schemaDef, url, { schema: config.schema });
|
|
1616
1686
|
if (diff.statements.length === 0 && diff.drop.length === 0) {
|
|
1617
1687
|
diffSpinner.succeed('Database is already in sync');
|
|
1618
1688
|
(0, ui_js_1.newline)();
|
|
@@ -1687,7 +1757,11 @@ async function cmdPush(args, config) {
|
|
|
1687
1757
|
const pushSpinner = new ui_js_1.Spinner('Applying changes').start();
|
|
1688
1758
|
let result;
|
|
1689
1759
|
try {
|
|
1690
|
-
result = await (0, schema_sql_js_1.schemaPush)(schemaDef, url, {
|
|
1760
|
+
result = await (0, schema_sql_js_1.schemaPush)(schemaDef, url, {
|
|
1761
|
+
allowDestructive: args.allowDestructive,
|
|
1762
|
+
precomputedDiff: diff,
|
|
1763
|
+
schema: config.schema,
|
|
1764
|
+
});
|
|
1691
1765
|
}
|
|
1692
1766
|
catch (err) {
|
|
1693
1767
|
if (!(err instanceof schema_sql_js_1.DestructivePushRefusal))
|
|
@@ -1699,7 +1773,11 @@ async function cmdPush(args, config) {
|
|
|
1699
1773
|
process.exit(1);
|
|
1700
1774
|
}
|
|
1701
1775
|
pushSpinner.start();
|
|
1702
|
-
result = await (0, schema_sql_js_1.schemaPush)(schemaDef, url, {
|
|
1776
|
+
result = await (0, schema_sql_js_1.schemaPush)(schemaDef, url, {
|
|
1777
|
+
allowDestructive: true,
|
|
1778
|
+
precomputedDiff: diff,
|
|
1779
|
+
schema: config.schema,
|
|
1780
|
+
});
|
|
1703
1781
|
}
|
|
1704
1782
|
pushSpinner.succeed(`Applied ${(0, ui_js_1.bold)(String(result.statementsExecuted))} statement(s)`);
|
|
1705
1783
|
if (result.tablesCreated.length > 0) {
|
|
@@ -1813,7 +1891,7 @@ async function cmdMigrateCreate(args, config) {
|
|
|
1813
1891
|
(0, ui_js_1.newline)();
|
|
1814
1892
|
const schemaDef = await loadSchemaFile(config.schemaFile);
|
|
1815
1893
|
const diffSpinner = new ui_js_1.Spinner('Computing schema diff').start();
|
|
1816
|
-
const diff = await (0, schema_sql_js_1.schemaDiff)(schemaDef, url);
|
|
1894
|
+
const diff = await (0, schema_sql_js_1.schemaDiff)(schemaDef, url, { schema: config.schema });
|
|
1817
1895
|
if (diff.statements.length === 0) {
|
|
1818
1896
|
diffSpinner.succeed('Database is already in sync: nothing to migrate');
|
|
1819
1897
|
(0, ui_js_1.newline)();
|
|
@@ -1890,7 +1968,7 @@ async function cmdMigrateCreate(args, config) {
|
|
|
1890
1968
|
(0, ui_js_1.newline)();
|
|
1891
1969
|
const schemaDef = await loadSchemaFile(config.schemaFile);
|
|
1892
1970
|
const diffSpinner = new ui_js_1.Spinner('Computing schema diff').start();
|
|
1893
|
-
const diff = await (0, schema_sql_js_1.schemaDiff)(schemaDef, url);
|
|
1971
|
+
const diff = await (0, schema_sql_js_1.schemaDiff)(schemaDef, url, { schema: config.schema });
|
|
1894
1972
|
if (diff.statements.length === 0) {
|
|
1895
1973
|
diffSpinner.succeed('Database is already in sync: nothing to migrate');
|
|
1896
1974
|
(0, ui_js_1.newline)();
|
|
@@ -2550,9 +2628,24 @@ const TIER_LABEL = {
|
|
|
2550
2628
|
'take-deliberately': 'TAKE DELIBERATELY',
|
|
2551
2629
|
scrutinize: 'SCRUTINIZE',
|
|
2552
2630
|
};
|
|
2553
|
-
/**
|
|
2631
|
+
/**
|
|
2632
|
+
* The `CREATE INDEX` for a finding (display / JSON only), honoring the
|
|
2633
|
+
* partial-null suggestion. The migration writer uses
|
|
2634
|
+
* {@link buildCreateIndexStatements} instead, which adds the preceding
|
|
2635
|
+
* `DROP INDEX CONCURRENTLY IF EXISTS` that makes a rerun converge.
|
|
2636
|
+
*/
|
|
2554
2637
|
function doctorCreateSql(f, opts) {
|
|
2555
2638
|
return (0, index_advisor_js_1.buildCreateIndexSql)(f.missing.table, f.missing.columns, f.missing.indexName, {
|
|
2639
|
+
concurrently: opts.concurrently,
|
|
2640
|
+
// The report line is advice a human may copy into psql, where the
|
|
2641
|
+
// guard is genuinely useful and there is no corpse to skip.
|
|
2642
|
+
ifNotExists: true,
|
|
2643
|
+
partialNotNull: f.score.partialNotNull,
|
|
2644
|
+
});
|
|
2645
|
+
}
|
|
2646
|
+
/** The statements the fix migration's UP body runs for one finding. */
|
|
2647
|
+
function doctorCreateStatements(f, opts) {
|
|
2648
|
+
return (0, index_advisor_js_1.buildCreateIndexStatements)(f.missing.table, f.missing.columns, f.missing.indexName, {
|
|
2556
2649
|
concurrently: opts.concurrently,
|
|
2557
2650
|
partialNotNull: f.score.partialNotNull,
|
|
2558
2651
|
});
|
|
@@ -2565,13 +2658,17 @@ const CONCURRENTLY_RECIPE_COMMENT = [
|
|
|
2565
2658
|
'--',
|
|
2566
2659
|
'-- 1. Idempotency is required. This migration is recorded only after ALL',
|
|
2567
2660
|
'-- statements succeed; a mid-file failure leaves earlier indexes built and',
|
|
2568
|
-
'-- the migration unrecorded, so a rerun must be safe.
|
|
2569
|
-
'--
|
|
2570
|
-
'--
|
|
2571
|
-
'--
|
|
2572
|
-
'--
|
|
2573
|
-
'--
|
|
2574
|
-
'--
|
|
2661
|
+
'-- the migration unrecorded, so a rerun must be safe.',
|
|
2662
|
+
'-- 2. The INVALID-index trap, and why each CREATE is preceded by a DROP.',
|
|
2663
|
+
'-- A CREATE INDEX CONCURRENTLY that fails partway leaves an INVALID index',
|
|
2664
|
+
'-- behind, under the name it was asked for. IF NOT EXISTS matches on the',
|
|
2665
|
+
'-- NAME and not on validity, so with it a rerun SKIPS the corpse and the',
|
|
2666
|
+
'-- index is never actually built: the migration records as applied while',
|
|
2667
|
+
'-- the index doctor reported is still missing. The DROP INDEX',
|
|
2668
|
+
'-- CONCURRENTLY IF EXISTS line ahead of each CREATE is a no-op on the',
|
|
2669
|
+
'-- first run (the index does not exist yet, which is why it was',
|
|
2670
|
+
'-- proposed) and clears the corpse on a rerun, so rerunning converges on',
|
|
2671
|
+
'-- a VALID index. Run "turbine doctor" to list invalid indexes.',
|
|
2575
2672
|
'-- 3. Locking. CREATE INDEX CONCURRENTLY waits for every transaction that can',
|
|
2576
2673
|
'-- see the table to finish. A long-running transaction makes it wait and',
|
|
2577
2674
|
'-- makes "migrate up" look hung. For bounded waits, SET lock_timeout /',
|
|
@@ -3285,7 +3382,7 @@ function renderPlanDivergence(divergence, attached) {
|
|
|
3285
3382
|
/** Write the --fix migration (CONCURRENTLY + directive by default; plain with --no-concurrently). */
|
|
3286
3383
|
function renderFixMigration(findings, config, args) {
|
|
3287
3384
|
const concurrently = args.noConcurrently !== true;
|
|
3288
|
-
const up = findings.
|
|
3385
|
+
const up = findings.flatMap((f) => doctorCreateStatements(f, { concurrently })).join('\n');
|
|
3289
3386
|
const down = findings.map((f) => (0, index_advisor_js_1.buildDropIndexSql)(f.missing.indexName, { concurrently })).join('\n');
|
|
3290
3387
|
let file;
|
|
3291
3388
|
if (concurrently) {
|
package/dist/cjs/cli/mcp.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Readable, Writable } from 'node:stream';
|
|
2
2
|
import pg from 'pg';
|
|
3
|
-
import { type ColumnMetadata, type RelationDef } from '../schema.js';
|
|
3
|
+
import { type ColumnMetadata, type IndexMetadata, type RelationDef } from '../schema.js';
|
|
4
4
|
export interface McpServerOptions {
|
|
5
5
|
url: string;
|
|
6
6
|
schema: string;
|
|
@@ -31,6 +31,14 @@ export interface McpServerHandle {
|
|
|
31
31
|
}
|
|
32
32
|
export declare function startMcpServer(options: McpServerOptions, transport?: McpTransport): McpServerHandle;
|
|
33
33
|
interface ForeignKeyRow {
|
|
34
|
+
/**
|
|
35
|
+
* `pg_constraint.oid` as text. THE grouping key: a constraint NAME is unique
|
|
36
|
+
* only per table (conrelid, conname), so two tables in one schema may both
|
|
37
|
+
* have a `shared_fk` and grouping on the name alone merges them, which loses
|
|
38
|
+
* one table's relation entirely and points the other at a column its target
|
|
39
|
+
* does not have. The OID is unique catalog-wide.
|
|
40
|
+
*/
|
|
41
|
+
constraint_oid: string;
|
|
34
42
|
source_table: string;
|
|
35
43
|
source_column: string;
|
|
36
44
|
target_table: string;
|
|
@@ -38,13 +46,25 @@ interface ForeignKeyRow {
|
|
|
38
46
|
constraint_name: string;
|
|
39
47
|
}
|
|
40
48
|
/**
|
|
41
|
-
* Group raw FK rows into constraint-level entries and delegate relation
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
49
|
+
* Group raw FK rows into constraint-level entries and delegate relation naming
|
|
50
|
+
* to `deriveCatalogRelations` in ../introspect.ts, the SAME entry point
|
|
51
|
+
* `turbine generate` goes through. MCP introspects for itself (it cannot assume
|
|
52
|
+
* generated metadata exists), so any divergence here is a divergence a live MCP
|
|
53
|
+
* client trips over: it reads a relation name out of the MCP schema tool, passes
|
|
54
|
+
* it back in a query, and the core builder rejects it.
|
|
55
|
+
*
|
|
56
|
+
* MCP used to assemble the pipeline by hand and drifted twice over, both times
|
|
57
|
+
* by OMITTING an optional argument, which is silently type-correct and changes
|
|
58
|
+
* the answer: without `uniqueSetsByTable` a UNIQUE foreign key came back as
|
|
59
|
+
* `users.profiles` (hasMany) where generate said `users.profile` (hasOne), and
|
|
60
|
+
* without `uniqueIndexColsByTable` every Prisma-style PK-less junction lost its
|
|
61
|
+
* auto-m2m relations. Passing the whole input set to one shared function is what
|
|
62
|
+
* makes those two failures impossible rather than merely fixed.
|
|
63
|
+
*
|
|
64
|
+
* `uniqueByTable` and `indexesByTable` are therefore REQUIRED parameters, not
|
|
65
|
+
* optional ones: an optional catalog input is exactly how the hasOne flip went
|
|
66
|
+
* missing here in the first place. Exported for the parity unit test.
|
|
47
67
|
*/
|
|
48
|
-
export declare function buildRelations(tableNames: string[], columnsByTable: Map<string, ColumnMetadata[]>, pkByTable: Map<string, string[]>, rows: ForeignKeyRow[], enums?: Record<string, string[]>): Map<string, Record<string, RelationDef>>;
|
|
68
|
+
export declare function buildRelations(tableNames: string[], columnsByTable: Map<string, ColumnMetadata[]>, pkByTable: Map<string, string[]>, rows: ForeignKeyRow[], uniqueByTable: Map<string, string[][]>, indexesByTable: Map<string, IndexMetadata[]>, enums?: Record<string, string[]>): Map<string, Record<string, RelationDef>>;
|
|
49
69
|
export declare function runMcpServer(options: McpServerOptions): Promise<void>;
|
|
50
70
|
export {};
|