turbine-orm 0.77.1 → 0.78.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 +13 -9
- package/dist/cjs/cli/config.d.ts +7 -1
- package/dist/cjs/cli/config.js +11 -2
- package/dist/cjs/cli/destructive.d.ts +1 -1
- package/dist/cjs/cli/destructive.js +307 -9
- package/dist/cjs/cli/index.js +252 -42
- package/dist/cjs/cli/mcp.d.ts +23 -0
- package/dist/cjs/cli/mcp.js +190 -152
- package/dist/cjs/cli/migrate.d.ts +243 -3
- package/dist/cjs/cli/migrate.js +432 -43
- package/dist/cjs/cli/sql-statements.js +27 -0
- package/dist/cjs/cli/studio.d.ts +0 -1
- package/dist/cjs/cli/studio.js +9 -7
- package/dist/cjs/client.d.ts +8 -1
- package/dist/cjs/client.js +7 -0
- package/dist/cjs/connection-url.d.ts +82 -0
- package/dist/cjs/connection-url.js +187 -1
- package/dist/cjs/errors.d.ts +112 -12
- package/dist/cjs/errors.js +558 -114
- package/dist/cjs/generate.js +47 -15
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/introspect.d.ts +33 -0
- package/dist/cjs/introspect.js +54 -1
- package/dist/cjs/mssql.js +21 -1
- package/dist/cjs/nested-write.js +85 -14
- package/dist/cjs/pipeline-submittable.d.ts +2 -0
- package/dist/cjs/pipeline-submittable.js +88 -3
- package/dist/cjs/pipeline.js +13 -1
- package/dist/cjs/powdb-introspect.d.ts +5 -1
- package/dist/cjs/powdb-introspect.js +5 -1
- package/dist/cjs/powql.d.ts +14 -0
- package/dist/cjs/powql.js +44 -4
- package/dist/cjs/prisma-compat.js +95 -8
- package/dist/cjs/query/aggregates.js +56 -6
- package/dist/cjs/query/builder.d.ts +76 -13
- package/dist/cjs/query/builder.js +188 -58
- package/dist/cjs/query/compound-unique.d.ts +76 -0
- package/dist/cjs/query/compound-unique.js +129 -0
- package/dist/cjs/query/index.d.ts +1 -1
- package/dist/cjs/query/types.d.ts +65 -11
- package/dist/cjs/query/where.d.ts +85 -19
- package/dist/cjs/query/where.js +262 -47
- package/dist/cjs/query/writes.d.ts +11 -2
- package/dist/cjs/query/writes.js +116 -21
- package/dist/cjs/seed.d.ts +16 -0
- package/dist/cjs/seed.js +16 -0
- package/dist/cli/config.d.ts +7 -1
- package/dist/cli/config.js +11 -2
- package/dist/cli/destructive.d.ts +1 -1
- package/dist/cli/destructive.js +307 -9
- package/dist/cli/index.js +254 -44
- package/dist/cli/mcp.d.ts +23 -0
- package/dist/cli/mcp.js +187 -150
- package/dist/cli/migrate.d.ts +243 -3
- package/dist/cli/migrate.js +423 -45
- package/dist/cli/sql-statements.js +27 -0
- package/dist/cli/studio.d.ts +0 -1
- package/dist/cli/studio.js +10 -7
- package/dist/client.d.ts +8 -1
- package/dist/client.js +7 -0
- package/dist/connection-url.d.ts +82 -0
- package/dist/connection-url.js +183 -0
- package/dist/errors.d.ts +112 -12
- package/dist/errors.js +558 -114
- package/dist/generate.js +47 -15
- package/dist/index.d.ts +1 -1
- package/dist/introspect.d.ts +33 -0
- package/dist/introspect.js +53 -1
- package/dist/mssql.js +21 -1
- package/dist/nested-write.js +85 -14
- package/dist/pipeline-submittable.d.ts +2 -0
- package/dist/pipeline-submittable.js +87 -3
- package/dist/pipeline.js +14 -2
- package/dist/powdb-introspect.d.ts +5 -1
- package/dist/powdb-introspect.js +5 -1
- package/dist/powql.d.ts +14 -0
- package/dist/powql.js +45 -5
- package/dist/prisma-compat.js +96 -9
- package/dist/query/aggregates.js +56 -6
- package/dist/query/builder.d.ts +76 -13
- package/dist/query/builder.js +188 -58
- package/dist/query/compound-unique.d.ts +76 -0
- package/dist/query/compound-unique.js +126 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/types.d.ts +65 -11
- package/dist/query/where.d.ts +85 -19
- package/dist/query/where.js +260 -47
- package/dist/query/writes.d.ts +11 -2
- package/dist/query/writes.js +117 -22
- package/dist/seed.d.ts +16 -0
- package/dist/seed.js +16 -0
- package/package.json +3 -3
- package/skills/turbine-orm/SKILL.md +37 -10
package/dist/cjs/query/writes.js
CHANGED
|
@@ -403,7 +403,15 @@ function buildUpdate(qi, args) {
|
|
|
403
403
|
// The empty-`where` guard checks the USER predicate only, a global filter
|
|
404
404
|
// must never turn an unguarded mass update into an allowed one.
|
|
405
405
|
const userHasPredicate = !whereMod.userPredicateIsEmpty(qi, userWhere) || !!lock;
|
|
406
|
-
|
|
406
|
+
const allowFullTableScan = (0, types_js_1.resolveUnsafeFlag)(args.allowFullTableScan, 'allowFullTableScan');
|
|
407
|
+
whereMod.assertMutationHasPredicate(qi, 'update', userHasPredicate ? ' WHERE x' : '', allowFullTableScan);
|
|
408
|
+
// `update` returns ONE row, so its where must identify one: the 0.73
|
|
409
|
+
// `findUnique` rule (query/compound-unique.ts) applied to the write that has
|
|
410
|
+
// the same shape. On the USER's where, before the global filter is merged
|
|
411
|
+
// (a filter narrows, it never identifies), and skipped only under the
|
|
412
|
+
// explicit full-table opt-in, which already means "every row".
|
|
413
|
+
if (!allowFullTableScan)
|
|
414
|
+
(0, compound_unique_js_1.assertMutationWhereIdentifiesOneRow)(qi.tableMeta, qi.table, userWhere, 'update');
|
|
407
415
|
// The SQL is built from the global-filter-merged where (soft-delete keeps an
|
|
408
416
|
// update from touching already-deleted rows).
|
|
409
417
|
const whereObj = (whereMod.mergeGlobalFilter(qi, userWhere) ?? {});
|
|
@@ -536,7 +544,12 @@ function buildDelete(qi, args) {
|
|
|
536
544
|
// Prisma compound-unique selector → the column conjunction (before the guard).
|
|
537
545
|
const userWhere = (0, compound_unique_js_1.expandCompoundUniqueWhere)(qi.tableMeta, args.where);
|
|
538
546
|
// Guard the USER predicate (a global filter must not satisfy the guard).
|
|
539
|
-
|
|
547
|
+
const allowFullTableScan = (0, types_js_1.resolveUnsafeFlag)(args.allowFullTableScan, 'allowFullTableScan');
|
|
548
|
+
whereMod.assertMutationHasPredicate(qi, 'delete', whereMod.userPredicateIsEmpty(qi, userWhere) ? '' : ' WHERE x', allowFullTableScan);
|
|
549
|
+
// Same identity rule as `update` (see buildUpdate): one returned row means
|
|
550
|
+
// one addressed row. `deleteMany` is the many-row path.
|
|
551
|
+
if (!allowFullTableScan)
|
|
552
|
+
(0, compound_unique_js_1.assertMutationWhereIdentifiesOneRow)(qi.tableMeta, qi.table, userWhere, 'delete');
|
|
540
553
|
const whereObj = (whereMod.mergeGlobalFilter(qi, userWhere) ?? {});
|
|
541
554
|
const whereFp = whereMod.fingerprintWhere(qi, whereObj);
|
|
542
555
|
const ck = `d:${whereFp}${whereMod.globalFilterCacheSegment(qi)}`;
|
|
@@ -590,6 +603,23 @@ function buildUpsert(qi, args) {
|
|
|
590
603
|
qi.currentSkip = (0, types_js_1.resolveSkipGlobalFilters)(args.skipGlobalFilters);
|
|
591
604
|
// Prisma compound-unique selector on the conflict target → its member columns.
|
|
592
605
|
const upsertWhere = (0, compound_unique_js_1.expandCompoundUniqueWhere)(qi.tableMeta, args.where);
|
|
606
|
+
// An upsert's `where` becomes the CONFLICT TARGET, so it carries the same
|
|
607
|
+
// one-row contract `update` / `delete` do and is refused by the same rule
|
|
608
|
+
// (query/compound-unique.ts), with its own sentence because the consequence
|
|
609
|
+
// differs. Without it a non-unique `where` reached the server as
|
|
610
|
+
// `ON CONFLICT ("role")` and came back as a bare SQLSTATE 42P10 on
|
|
611
|
+
// PostgreSQL, and WORSE than an error on the engines that do not read the
|
|
612
|
+
// conflict target back: MySQL's `ON DUPLICATE KEY UPDATE` ignores it and
|
|
613
|
+
// keys off whichever unique index the row happens to violate, and SQL
|
|
614
|
+
// Server's `MERGE ... ON` matches every row the predicate matches and
|
|
615
|
+
// updates all of them.
|
|
616
|
+
//
|
|
617
|
+
// Before any SQL is assembled, and this is the only where-guard `upsert`
|
|
618
|
+
// runs: `{}` and `{ email: undefined }` are refused here too, and by the
|
|
619
|
+
// right sentence. The empty-`where` guard's own message points at
|
|
620
|
+
// `allowFullTableScan: UNSAFE`, which is not an option on `UpsertArgs` at
|
|
621
|
+
// all, so borrowing it here would name a way out that does not exist.
|
|
622
|
+
(0, compound_unique_js_1.assertMutationWhereIdentifiesOneRow)(qi.tableMeta, qi.table, upsertWhere, 'upsert');
|
|
593
623
|
// Build the INSERT part from create data
|
|
594
624
|
const createEntries = writeEntries(qi, args.create);
|
|
595
625
|
const columns = createEntries.map(([k]) => qi.toSqlColumn(k));
|
|
@@ -599,25 +629,47 @@ function buildUpsert(qi, args) {
|
|
|
599
629
|
// The conflict target comes from `where` keys, must be unique/PK columns
|
|
600
630
|
const conflictKeys = Object.keys(upsertWhere).filter((k) => upsertWhere[k] !== undefined);
|
|
601
631
|
const conflictColumns = conflictKeys.map((k) => qi.toSqlColumn(k));
|
|
602
|
-
//
|
|
632
|
+
// The conflict-UPDATE SET goes through the SAME operator-aware compiler as
|
|
633
|
+
// `update()` (buildSetClause), so `set` / `increment` / `decrement` /
|
|
634
|
+
// `multiply` / `divide` mean here what they mean there and a misspelled
|
|
635
|
+
// operator gets the same E003. This branch used to bind each value directly,
|
|
636
|
+
// which stored an operator OBJECT as the JSON text `{"set":"x"}` in a text
|
|
637
|
+
// column with no error at all. buildSetClause pushes onto `params` as it
|
|
638
|
+
// goes, so the numbering continues after the create params exactly as the
|
|
639
|
+
// hand-rolled `paramIdx` did. No SQL cache here (the statement is rebuilt per
|
|
640
|
+
// call), so the fingerprint / collect mirrors update() keeps are not needed.
|
|
603
641
|
const updateEntries = writeEntries(qi, args.update);
|
|
604
|
-
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
paramIdx++;
|
|
608
|
-
return clause;
|
|
609
|
-
});
|
|
610
|
-
const updateParams = updateEntries.map(([k, v]) => coerceWriteValue(qi, k, v));
|
|
611
|
-
const params = [...createParams, ...updateParams];
|
|
642
|
+
const params = [...createParams];
|
|
643
|
+
const refQualifier = upsertReferenceQualifier(qi);
|
|
644
|
+
const setClauses = updateEntries.map(([k, v]) => buildSetClause(qi, k, v, params, refQualifier));
|
|
612
645
|
// Global filter → restrict the conflict-UPDATE (soft-delete / tenancy) so an
|
|
613
646
|
// upsert never resurrects a soft-deleted row or writes across tenants. Only
|
|
614
|
-
// on engines whose upsert can carry a predicate (Postgres); the gf
|
|
615
|
-
// continue the placeholder numbering after create+update params.
|
|
647
|
+
// on engines whose upsert can carry a predicate (Postgres, SQLite); the gf
|
|
648
|
+
// params continue the placeholder numbering after create+update params.
|
|
649
|
+
//
|
|
650
|
+
// Compiled against the TABLE QUALIFIER, not bare: in `ON CONFLICT ... DO
|
|
651
|
+
// UPDATE ... WHERE` both the target table and `excluded` are in scope, so an
|
|
652
|
+
// unqualified column is ambiguous and PostgreSQL rejected EVERY upsert on a
|
|
653
|
+
// globally filtered table at parse time (42702), insert path included.
|
|
616
654
|
let updateWhere;
|
|
617
655
|
if (qi.dialect.supportsUpsertUpdateWhere) {
|
|
618
656
|
const gf = whereMod.resolveGlobalFilter(qi, qi.table);
|
|
619
|
-
if (gf)
|
|
620
|
-
|
|
657
|
+
if (gf) {
|
|
658
|
+
// Compiled through a scope whose FROM-item reference is ALREADY RENDERED
|
|
659
|
+
// (`"users"`), which is what the target table is inside `ON CONFLICT ...
|
|
660
|
+
// DO UPDATE`. `aliasWhereScope` is the wrong seam for that: it takes a
|
|
661
|
+
// BARE alias and quotes it itself for the nested-relation correlation
|
|
662
|
+
// parent, so handing it `q(table)` produced a triple-quoted reference
|
|
663
|
+
// inside an EXISTS body and the whole statement failed with 42P01
|
|
664
|
+
// whenever the configured global filter was a relation filter rather
|
|
665
|
+
// than a plain column. A bare table name is not the answer either: it
|
|
666
|
+
// would leave the qualifier unquoted and break every table whose name
|
|
667
|
+
// needs quoting. `buildRenderedRefWhere` is the rendered-reference seam,
|
|
668
|
+
// one parameter with one meaning, used for the qualifier and the
|
|
669
|
+
// correlation parent alike, and shared with the batched `_count`
|
|
670
|
+
// follow-up, which had the identical bug.
|
|
671
|
+
updateWhere = whereMod.buildRenderedRefWhere(qi, qi.table, qi.tableMeta, qi.q(qi.table), gf, params) ?? undefined;
|
|
672
|
+
}
|
|
621
673
|
}
|
|
622
674
|
const sql = qi.dialect.buildUpsertStatement({
|
|
623
675
|
table: qi.q(qi.table),
|
|
@@ -634,11 +686,21 @@ function buildUpsert(qi, args) {
|
|
|
634
686
|
transform: (result) => {
|
|
635
687
|
const row = result.rows[0];
|
|
636
688
|
if (!row) {
|
|
689
|
+
// With a conflict-UPDATE predicate in play there IS a way to get no
|
|
690
|
+
// row: the key conflicts with a row the global filter hides, the
|
|
691
|
+
// predicate is false for it, the engine skips the update, and the
|
|
692
|
+
// insert cannot happen either because the key is taken. Nothing was
|
|
693
|
+
// written and the hidden row is untouched; say so, rather than "this
|
|
694
|
+
// should never happen".
|
|
637
695
|
throw new errors_js_1.NotFoundError({
|
|
638
696
|
table: qi.table,
|
|
639
697
|
where: args.where,
|
|
640
698
|
operation: 'upsert',
|
|
641
|
-
message:
|
|
699
|
+
message: updateWhere
|
|
700
|
+
? `upsert on "${qi.table}" wrote nothing: the row that conflicts on the given key is excluded by the ` +
|
|
701
|
+
`configured global filter for "${qi.table}", so it was neither updated (the filter hides it) nor ` +
|
|
702
|
+
'inserted (the key is taken). Pass `skipGlobalFilters: UNSAFE` to address it anyway.'
|
|
703
|
+
: `upsert on "${qi.table}" returned no row from RETURNING *; this should never happen.`,
|
|
642
704
|
});
|
|
643
705
|
}
|
|
644
706
|
return parseWriteRow(qi, row);
|
|
@@ -654,6 +716,29 @@ function buildUpsert(qi, args) {
|
|
|
654
716
|
: undefined,
|
|
655
717
|
};
|
|
656
718
|
}
|
|
719
|
+
/**
|
|
720
|
+
* The qualifier an upsert's arithmetic SET operator reads its column through.
|
|
721
|
+
*
|
|
722
|
+
* `INSERT ... ON CONFLICT DO UPDATE SET n = n + 1` is 42702 on PostgreSQL
|
|
723
|
+
* because `n` could be the target row's or `excluded`'s, so the reference is
|
|
724
|
+
* qualified with the table name on every engine whose upsert statement names
|
|
725
|
+
* the table directly (PostgreSQL, SQLite, MySQL all accept `"t"."n"` there).
|
|
726
|
+
* SQL Server is the exception and keeps the bare reference: its `MERGE`
|
|
727
|
+
* aliases the target as `T`, so the TABLE NAME does not resolve there at all,
|
|
728
|
+
* and the alias belongs to that dialect's own `buildUpsertStatement` rather
|
|
729
|
+
* than to this module. An arithmetic operator in an mssql upsert may therefore
|
|
730
|
+
* still be refused by the server as an ambiguous column; that shape has never
|
|
731
|
+
* worked (it previously bound the operator OBJECT as the value), and the sound
|
|
732
|
+
* fix is for the dialect to publish its target alias, which is a change to
|
|
733
|
+
* `dialect.ts`. It fails loudly either way, so no wrong answer is possible.
|
|
734
|
+
* `set` and literal values reference no column and are unaffected everywhere.
|
|
735
|
+
*
|
|
736
|
+
* Gated on the dialect NAME, never on hook presence: every engine dialect
|
|
737
|
+
* spreads `postgresDialect`, so a presence test would say yes for all of them.
|
|
738
|
+
*/
|
|
739
|
+
function upsertReferenceQualifier(qi) {
|
|
740
|
+
return qi.dialect.name === 'mssql' ? '' : `${qi.q(qi.table)}.`;
|
|
741
|
+
}
|
|
657
742
|
function buildUpdateMany(qi, args) {
|
|
658
743
|
assertWritable(qi, 'updateMany');
|
|
659
744
|
qi.currentSkip = (0, types_js_1.resolveSkipGlobalFilters)(args.skipGlobalFilters);
|
|
@@ -916,9 +1001,19 @@ function assertNoGeneratedColumns(qi, data, operation) {
|
|
|
916
1001
|
* Returns the SQL fragment (e.g., `"view_count" = "view_count" + $3`) and
|
|
917
1002
|
* pushes any required params onto the shared params array so that WHERE
|
|
918
1003
|
* clause numbering continues correctly afterward.
|
|
1004
|
+
*
|
|
1005
|
+
* `refQualifier` prefixes the column REFERENCE an arithmetic operator reads
|
|
1006
|
+
* from (`"view_count" = <qualifier>"view_count" + $n`). A plain `UPDATE` has
|
|
1007
|
+
* one table in scope and passes nothing; `upsert` passes the table name,
|
|
1008
|
+
* because inside `ON CONFLICT ... DO UPDATE SET` the target table and
|
|
1009
|
+
* `excluded` are BOTH in scope and a bare reference is 42702 on PostgreSQL.
|
|
1010
|
+
* The assignment target on the left stays bare on every engine (it can only
|
|
1011
|
+
* ever be the target table's column), and `set` / literal values reference no
|
|
1012
|
+
* column at all, so their SQL is byte-identical with or without a qualifier.
|
|
919
1013
|
*/
|
|
920
|
-
function buildSetClause(qi, key, value, params) {
|
|
1014
|
+
function buildSetClause(qi, key, value, params, refQualifier = '') {
|
|
921
1015
|
const col = qi.toSqlColumn(key);
|
|
1016
|
+
const ref = `${refQualifier}${col}`;
|
|
922
1017
|
// Enum columns get an explicit `::"EnumName"` cast on their value bind
|
|
923
1018
|
// (see enumTypeForColumn); `''` everywhere else. Value-invariant, so the
|
|
924
1019
|
// SQL cache and collectSetParams are unaffected.
|
|
@@ -945,19 +1040,19 @@ function buildSetClause(qi, key, value, params) {
|
|
|
945
1040
|
}
|
|
946
1041
|
if (op === 'increment') {
|
|
947
1042
|
params.push(opValue);
|
|
948
|
-
return `${col} = ${
|
|
1043
|
+
return `${col} = ${ref} + ${qi.p(params.length)}`;
|
|
949
1044
|
}
|
|
950
1045
|
if (op === 'decrement') {
|
|
951
1046
|
params.push(opValue);
|
|
952
|
-
return `${col} = ${
|
|
1047
|
+
return `${col} = ${ref} - ${qi.p(params.length)}`;
|
|
953
1048
|
}
|
|
954
1049
|
if (op === 'multiply') {
|
|
955
1050
|
params.push(opValue);
|
|
956
|
-
return `${col} = ${
|
|
1051
|
+
return `${col} = ${ref} * ${qi.p(params.length)}`;
|
|
957
1052
|
}
|
|
958
1053
|
if (op === 'divide') {
|
|
959
1054
|
params.push(opValue);
|
|
960
|
-
return `${col} = ${
|
|
1055
|
+
return `${col} = ${ref} / ${qi.p(params.length)}`;
|
|
961
1056
|
}
|
|
962
1057
|
}
|
|
963
1058
|
// Fall through: multi-key objects or non-operator single-key objects
|
package/dist/cjs/seed.d.ts
CHANGED
|
@@ -12,4 +12,20 @@ export type DefinedSeed = () => Promise<void>;
|
|
|
12
12
|
* Exported for unit testing the frame parser in isolation.
|
|
13
13
|
*/
|
|
14
14
|
export declare function parseStackFramePath(line: string): string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Declare a seed: `fn` receives a connected `TurbineClient` and is awaited.
|
|
17
|
+
*
|
|
18
|
+
* The returned function runs the seed on demand. When the module that calls
|
|
19
|
+
* `defineSeed` is itself the process entry point (`npx tsx seed.ts`, or the
|
|
20
|
+
* child `turbine seed` spawns), the seed also runs automatically.
|
|
21
|
+
*
|
|
22
|
+
* The client connects through `DATABASE_URL`. Under `turbine seed` that value
|
|
23
|
+
* is the configured connection string with `search_path` PINNED to the
|
|
24
|
+
* configured `schema` (as the `options=-c search_path` connection parameter,
|
|
25
|
+
* so it can never leak onto a pooled backend the way a `SET` would). Every
|
|
26
|
+
* unqualified table name the seed writes therefore lands in the same schema
|
|
27
|
+
* `push`, `generate` and the generated client use; a seed that must reach
|
|
28
|
+
* another schema qualifies the name. The default `public` adds nothing, so a
|
|
29
|
+
* project that never set `schema` connects exactly as before.
|
|
30
|
+
*/
|
|
15
31
|
export declare function defineSeed(fn: SeedFunction): DefinedSeed;
|
package/dist/cjs/seed.js
CHANGED
|
@@ -143,6 +143,22 @@ async function runSeed(fn) {
|
|
|
143
143
|
await db.disconnect();
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Declare a seed: `fn` receives a connected `TurbineClient` and is awaited.
|
|
148
|
+
*
|
|
149
|
+
* The returned function runs the seed on demand. When the module that calls
|
|
150
|
+
* `defineSeed` is itself the process entry point (`npx tsx seed.ts`, or the
|
|
151
|
+
* child `turbine seed` spawns), the seed also runs automatically.
|
|
152
|
+
*
|
|
153
|
+
* The client connects through `DATABASE_URL`. Under `turbine seed` that value
|
|
154
|
+
* is the configured connection string with `search_path` PINNED to the
|
|
155
|
+
* configured `schema` (as the `options=-c search_path` connection parameter,
|
|
156
|
+
* so it can never leak onto a pooled backend the way a `SET` would). Every
|
|
157
|
+
* unqualified table name the seed writes therefore lands in the same schema
|
|
158
|
+
* `push`, `generate` and the generated client use; a seed that must reach
|
|
159
|
+
* another schema qualifies the name. The default `public` adds nothing, so a
|
|
160
|
+
* project that never set `schema` connects exactly as before.
|
|
161
|
+
*/
|
|
146
162
|
function defineSeed(fn) {
|
|
147
163
|
const run = () => runSeed(fn);
|
|
148
164
|
if (isDirectSeedModule()) {
|
package/dist/cli/config.d.ts
CHANGED
|
@@ -230,5 +230,11 @@ export declare function connectionStringHasPassword(connectionString: string): b
|
|
|
230
230
|
* `.env` that holds the real value. The refusal lives here rather than at the
|
|
231
231
|
* call site so no future caller can reintroduce the leak by passing the raw
|
|
232
232
|
* `--url` through.
|
|
233
|
+
*
|
|
234
|
+
* `schema` is the RESOLVED Postgres schema (`--schema` or the default), written
|
|
235
|
+
* as the config's `schema` value. `init --schema app` probes `app` and then
|
|
236
|
+
* used to write `schema: 'public'` regardless, so the next documented step,
|
|
237
|
+
* `push`, diffed the starter schema against the wrong namespace and proposed
|
|
238
|
+
* dropping every column it found there.
|
|
233
239
|
*/
|
|
234
|
-
export declare function configTemplate(connectionString?: string): string;
|
|
240
|
+
export declare function configTemplate(connectionString?: string, schema?: string): string;
|
package/dist/cli/config.js
CHANGED
|
@@ -255,9 +255,18 @@ export function connectionStringHasPassword(connectionString) {
|
|
|
255
255
|
* `.env` that holds the real value. The refusal lives here rather than at the
|
|
256
256
|
* call site so no future caller can reintroduce the leak by passing the raw
|
|
257
257
|
* `--url` through.
|
|
258
|
+
*
|
|
259
|
+
* `schema` is the RESOLVED Postgres schema (`--schema` or the default), written
|
|
260
|
+
* as the config's `schema` value. `init --schema app` probes `app` and then
|
|
261
|
+
* used to write `schema: 'public'` regardless, so the next documented step,
|
|
262
|
+
* `push`, diffed the starter schema against the wrong namespace and proposed
|
|
263
|
+
* dropping every column it found there.
|
|
258
264
|
*/
|
|
259
|
-
export function configTemplate(connectionString) {
|
|
265
|
+
export function configTemplate(connectionString, schema = 'public') {
|
|
260
266
|
const inlineUrl = connectionString && !connectionStringHasPassword(connectionString) ? connectionString : undefined;
|
|
267
|
+
// Same delimiter rule as the URL below: a schema name is an arbitrary
|
|
268
|
+
// identifier, and a quote in it must not end the TS string early.
|
|
269
|
+
const schemaLiteral = schema.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
261
270
|
// Single quotes are the string delimiter in the emitted TS, so a connection
|
|
262
271
|
// string containing one would otherwise produce a config file that does not
|
|
263
272
|
// parse (a password-free URL can still carry a quote in a query parameter).
|
|
@@ -278,7 +287,7 @@ ${urlLine}
|
|
|
278
287
|
out: './generated/turbine',
|
|
279
288
|
|
|
280
289
|
/** Postgres schema to introspect (default: public) */
|
|
281
|
-
schema: '
|
|
290
|
+
schema: '${schemaLiteral}',
|
|
282
291
|
|
|
283
292
|
/** Tables to exclude from generation */
|
|
284
293
|
// exclude: ['_migrations', '_sessions'],
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* migration runner. Two lexers is how this guard came to disagree with the
|
|
29
29
|
* executor about what a file contained (see that module's header).
|
|
30
30
|
*/
|
|
31
|
-
export type DestructiveKind = 'drop-table' | 'drop-schema' | 'drop-database' | 'drop-owned' | 'drop-matview' | 'drop-column' | 'drop-cascade' | 'detach-partition' | 'truncate' | 'delete' | 'update-without-where' | 'alter-column-type' | 'merge-delete' | 'rename' | 'dynamic-destructive';
|
|
31
|
+
export type DestructiveKind = 'drop-table' | 'drop-schema' | 'drop-database' | 'drop-owned' | 'drop-matview' | 'drop-column' | 'drop-cascade' | 'detach-partition' | 'truncate' | 'delete' | 'update-without-where' | 'alter-column-type' | 'merge-delete' | 'rename' | 'rewrite-rule' | 'dynamic-destructive' | 'dynamic-unclassified';
|
|
32
32
|
export interface DestructiveStatement {
|
|
33
33
|
/** The offending SQL statement (trimmed, possibly long, display truncated) */
|
|
34
34
|
statement: string;
|