@zyno-io/dk-server-foundation 26.412.2137 → 26.509.2227
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/resources/proto/generated/devconsole/devconsole.js +1 -1
- package/dist/src/app/config.d.ts +1 -0
- package/dist/src/app/config.d.ts.map +1 -1
- package/dist/src/app/config.js +1 -1
- package/dist/src/app/config.js.map +1 -1
- package/dist/src/database/common.d.ts +3 -0
- package/dist/src/database/common.d.ts.map +1 -1
- package/dist/src/database/common.js +46 -1
- package/dist/src/database/common.js.map +1 -1
- package/dist/src/database/factory.d.ts +17 -0
- package/dist/src/database/factory.d.ts.map +1 -0
- package/dist/src/database/factory.js +53 -0
- package/dist/src/database/factory.js.map +1 -0
- package/dist/src/database/index.d.ts +2 -0
- package/dist/src/database/index.d.ts.map +1 -1
- package/dist/src/database/index.js +2 -0
- package/dist/src/database/index.js.map +1 -1
- package/dist/src/database/migration/MigrationResetCommand.d.ts.map +1 -1
- package/dist/src/database/migration/MigrationResetCommand.js +6 -20
- package/dist/src/database/migration/MigrationResetCommand.js.map +1 -1
- package/dist/src/database/migration/MigrationRunCommand.d.ts.map +1 -1
- package/dist/src/database/migration/MigrationRunCommand.js +2 -0
- package/dist/src/database/migration/MigrationRunCommand.js.map +1 -1
- package/dist/src/database/migration/create/MigrationCreateCommand.d.ts +2 -0
- package/dist/src/database/migration/create/MigrationCreateCommand.d.ts.map +1 -1
- package/dist/src/database/migration/create/MigrationCreateCommand.js +33 -11
- package/dist/src/database/migration/create/MigrationCreateCommand.js.map +1 -1
- package/dist/src/database/migration/create/builder-regenerator.d.ts +12 -0
- package/dist/src/database/migration/create/builder-regenerator.d.ts.map +1 -0
- package/dist/src/database/migration/create/builder-regenerator.js +300 -0
- package/dist/src/database/migration/create/builder-regenerator.js.map +1 -0
- package/dist/src/database/migration/create/db-reader.js +47 -6
- package/dist/src/database/migration/create/db-reader.js.map +1 -1
- package/dist/src/database/migration/create/ddl-generator.d.ts.map +1 -1
- package/dist/src/database/migration/create/ddl-generator.js +36 -206
- package/dist/src/database/migration/create/ddl-generator.js.map +1 -1
- package/dist/src/database/migration/create/file-generator.d.ts +2 -0
- package/dist/src/database/migration/create/file-generator.d.ts.map +1 -1
- package/dist/src/database/migration/create/file-generator.js +7 -2
- package/dist/src/database/migration/create/file-generator.js.map +1 -1
- package/dist/src/database/migration/create/schema-model.d.ts +2 -0
- package/dist/src/database/migration/create/schema-model.d.ts.map +1 -1
- package/dist/src/database/migration/create/schema-model.js +1 -1
- package/dist/src/database/mysql.d.ts.map +1 -1
- package/dist/src/database/mysql.js +5 -11
- package/dist/src/database/mysql.js.map +1 -1
- package/dist/src/database/postgres.d.ts +1 -0
- package/dist/src/database/postgres.d.ts.map +1 -1
- package/dist/src/database/postgres.js +1 -13
- package/dist/src/database/postgres.js.map +1 -1
- package/dist/src/database/schema/AlterBlueprint.d.ts +44 -0
- package/dist/src/database/schema/AlterBlueprint.d.ts.map +1 -0
- package/dist/src/database/schema/AlterBlueprint.js +82 -0
- package/dist/src/database/schema/AlterBlueprint.js.map +1 -0
- package/dist/src/database/schema/Blueprint.d.ts +16 -0
- package/dist/src/database/schema/Blueprint.d.ts.map +1 -0
- package/dist/src/database/schema/Blueprint.js +51 -0
- package/dist/src/database/schema/Blueprint.js.map +1 -0
- package/dist/src/database/schema/BlueprintBase.d.ts +59 -0
- package/dist/src/database/schema/BlueprintBase.d.ts.map +1 -0
- package/dist/src/database/schema/BlueprintBase.js +349 -0
- package/dist/src/database/schema/BlueprintBase.js.map +1 -0
- package/dist/src/database/schema/ColumnDefinition.d.ts +43 -0
- package/dist/src/database/schema/ColumnDefinition.d.ts.map +1 -0
- package/dist/src/database/schema/ColumnDefinition.js +117 -0
- package/dist/src/database/schema/ColumnDefinition.js.map +1 -0
- package/dist/src/database/schema/Schema.d.ts +32 -0
- package/dist/src/database/schema/Schema.d.ts.map +1 -0
- package/dist/src/database/schema/Schema.js +148 -0
- package/dist/src/database/schema/Schema.js.map +1 -0
- package/dist/src/database/schema/grammar/Grammar.d.ts +34 -0
- package/dist/src/database/schema/grammar/Grammar.d.ts.map +1 -0
- package/dist/src/database/schema/grammar/Grammar.js +36 -0
- package/dist/src/database/schema/grammar/Grammar.js.map +1 -0
- package/dist/src/database/schema/grammar/MySQLGrammar.d.ts +28 -0
- package/dist/src/database/schema/grammar/MySQLGrammar.d.ts.map +1 -0
- package/dist/src/database/schema/grammar/MySQLGrammar.js +190 -0
- package/dist/src/database/schema/grammar/MySQLGrammar.js.map +1 -0
- package/dist/src/database/schema/grammar/PostgresGrammar.d.ts +28 -0
- package/dist/src/database/schema/grammar/PostgresGrammar.d.ts.map +1 -0
- package/dist/src/database/schema/grammar/PostgresGrammar.js +223 -0
- package/dist/src/database/schema/grammar/PostgresGrammar.js.map +1 -0
- package/dist/src/database/schema/index.d.ts +9 -0
- package/dist/src/database/schema/index.d.ts.map +1 -0
- package/dist/src/database/schema/index.js +12 -0
- package/dist/src/database/schema/index.js.map +1 -0
- package/docs/content/cli.md +4 -1
- package/docs/content/configuration.md +6 -0
- package/docs/content/database.md +128 -0
- package/package.json +1 -1
- package/src/app/config.ts +2 -0
- package/src/database/common.ts +51 -0
- package/src/database/factory.ts +59 -0
- package/src/database/index.ts +2 -0
- package/src/database/migration/MigrationResetCommand.ts +6 -22
- package/src/database/migration/MigrationRunCommand.ts +2 -0
- package/src/database/migration/create/MigrationCreateCommand.ts +40 -12
- package/src/database/migration/create/builder-regenerator.ts +287 -0
- package/src/database/migration/create/db-reader.ts +43 -6
- package/src/database/migration/create/ddl-generator.ts +35 -212
- package/src/database/migration/create/file-generator.ts +5 -1
- package/src/database/migration/create/schema-model.ts +2 -0
- package/src/database/mysql.ts +7 -15
- package/src/database/postgres.ts +3 -17
- package/src/database/schema/AlterBlueprint.ts +85 -0
- package/src/database/schema/Blueprint.ts +43 -0
- package/src/database/schema/BlueprintBase.ts +386 -0
- package/src/database/schema/ColumnDefinition.ts +131 -0
- package/src/database/schema/Schema.ts +164 -0
- package/src/database/schema/grammar/Grammar.ts +61 -0
- package/src/database/schema/grammar/MySQLGrammar.ts +196 -0
- package/src/database/schema/grammar/PostgresGrammar.ts +232 -0
- package/src/database/schema/index.ts +8 -0
|
@@ -1,10 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Grammar, MySQLGrammar, PostgresGrammar } from '../../schema';
|
|
2
2
|
import { ColumnModification, ColumnSchema, Dialect, ForeignKeySchema, IndexSchema, SchemaDiff, TableDiff, TableSchema } from './schema-model';
|
|
3
3
|
|
|
4
|
-
const VALID_FK_ACTIONS = new Set(['RESTRICT', 'CASCADE', 'SET NULL', 'SET DEFAULT', 'NO ACTION']);
|
|
5
|
-
|
|
6
4
|
export const COMMENT_PREFIX = '\x00comment:';
|
|
7
5
|
|
|
6
|
+
// --- Grammar delegation ---
|
|
7
|
+
//
|
|
8
|
+
// SQL emission lives in src/database/schema/grammar/. This module owns the high-level
|
|
9
|
+
// diff orchestration (statement ordering, deferred FKs, PG enum dedup, MySQL column
|
|
10
|
+
// reordering, etc.). The thin helpers below delegate to dialect-specific Grammars.
|
|
11
|
+
|
|
12
|
+
const grammarCache = new Map<string, Grammar>();
|
|
13
|
+
function getGrammar(dialect: Dialect, pgSchema?: string): Grammar {
|
|
14
|
+
const key = `${dialect}:${pgSchema ?? 'public'}`;
|
|
15
|
+
let g = grammarCache.get(key);
|
|
16
|
+
if (!g) {
|
|
17
|
+
g = dialect === 'mysql' ? new MySQLGrammar(pgSchema ?? 'public') : new PostgresGrammar(pgSchema ?? 'public');
|
|
18
|
+
grammarCache.set(key, g);
|
|
19
|
+
}
|
|
20
|
+
return g;
|
|
21
|
+
}
|
|
22
|
+
function getPgGrammar(pgSchema?: string): PostgresGrammar {
|
|
23
|
+
return getGrammar('postgres', pgSchema) as PostgresGrammar;
|
|
24
|
+
}
|
|
25
|
+
|
|
8
26
|
export function generateDDL(diff: SchemaDiff): string[] {
|
|
9
27
|
const statements: string[] = [];
|
|
10
28
|
const { dialect, pgSchema } = diff;
|
|
@@ -485,216 +503,33 @@ function generatePostgresTableDDL(diff: TableDiff, pgSchema?: string, globalEnum
|
|
|
485
503
|
// --- Shared helpers ---
|
|
486
504
|
|
|
487
505
|
function createTable(table: TableSchema, dialect: Dialect, pgSchema?: string): string {
|
|
488
|
-
|
|
489
|
-
const pkCols = table.columns.filter(c => c.isPrimaryKey).map(c => c.name);
|
|
490
|
-
|
|
491
|
-
for (const col of table.columns) {
|
|
492
|
-
if (dialect === 'mysql') {
|
|
493
|
-
lines.push(` ${mysqlColumnDef(col)}`);
|
|
494
|
-
} else {
|
|
495
|
-
lines.push(` ${pgColumnDef(col, pgSchema)}`);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
if (pkCols.length > 0) {
|
|
500
|
-
const quoted = pkCols.map(c => q(dialect, c)).join(', ');
|
|
501
|
-
lines.push(` PRIMARY KEY (${quoted})`);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
return `CREATE TABLE ${qTable(dialect, table.name, pgSchema)} (\n${lines.join(',\n')}\n)`;
|
|
506
|
+
return getGrammar(dialect, pgSchema).createTable(table);
|
|
505
507
|
}
|
|
506
508
|
|
|
507
509
|
function createIndex(tableName: string, idx: IndexSchema, dialect: Dialect, pgSchema?: string): string {
|
|
508
|
-
|
|
509
|
-
const spatial = idx.spatial && dialect === 'mysql' ? 'SPATIAL ' : '';
|
|
510
|
-
const cols = idx.columns.map(c => q(dialect, c)).join(', ');
|
|
511
|
-
return `CREATE ${spatial}${unique}INDEX ${q(dialect, idx.name)} ON ${qTable(dialect, tableName, pgSchema)} (${cols})`;
|
|
510
|
+
return getGrammar(dialect, pgSchema).createIndex(tableName, idx);
|
|
512
511
|
}
|
|
513
512
|
|
|
514
513
|
function addForeignKey(tableName: string, fk: ForeignKeySchema, dialect: Dialect, pgSchema?: string): string {
|
|
515
|
-
|
|
516
|
-
const refCols = fk.referencedColumns.map(c => q(dialect, c)).join(', ');
|
|
517
|
-
const onDelete = validateFkAction(fk.onDelete, dialect);
|
|
518
|
-
const onUpdate = validateFkAction(fk.onUpdate, dialect);
|
|
519
|
-
return (
|
|
520
|
-
`ALTER TABLE ${qTable(dialect, tableName, pgSchema)} ADD CONSTRAINT ${q(dialect, fk.name)} ` +
|
|
521
|
-
`FOREIGN KEY (${cols}) REFERENCES ${qTable(dialect, fk.referencedTable, pgSchema)} (${refCols}) ` +
|
|
522
|
-
`ON DELETE ${onDelete} ON UPDATE ${onUpdate}`
|
|
523
|
-
);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
function validateFkAction(action: string, dialect: Dialect): string {
|
|
527
|
-
const upper = action.toUpperCase();
|
|
528
|
-
if (!VALID_FK_ACTIONS.has(upper)) {
|
|
529
|
-
throw new Error(`Invalid foreign key action: '${action}'`);
|
|
530
|
-
}
|
|
531
|
-
if (upper === 'SET DEFAULT' && dialect === 'mysql') {
|
|
532
|
-
throw new Error(`Foreign key action 'SET DEFAULT' is not supported by MySQL/InnoDB`);
|
|
533
|
-
}
|
|
534
|
-
return upper;
|
|
514
|
+
return getGrammar(dialect, pgSchema).addForeignKey(tableName, fk);
|
|
535
515
|
}
|
|
536
516
|
|
|
537
517
|
function createEnumType(typeName: string, values: string[], pgSchema?: string): string[] {
|
|
538
|
-
|
|
539
|
-
const vals = values.map(v => `'${escapeStr(v)}'`).join(', ');
|
|
540
|
-
const schemaFilter =
|
|
541
|
-
pgSchema && pgSchema !== 'public' ? ` AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = '${escapeStr(pgSchema)}')` : '';
|
|
542
|
-
return [
|
|
543
|
-
[
|
|
544
|
-
`DO $$ BEGIN`,
|
|
545
|
-
`IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = '${escapeStr(typeName)}'${schemaFilter}) THEN`,
|
|
546
|
-
` CREATE TYPE ${qualifiedName} AS ENUM (${vals});`,
|
|
547
|
-
`END IF;`,
|
|
548
|
-
`END $$`
|
|
549
|
-
].join('\n'),
|
|
550
|
-
`CREATE CAST (text AS ${qualifiedName}) WITH INOUT AS IMPLICIT`
|
|
551
|
-
];
|
|
518
|
+
return getPgGrammar(pgSchema).createEnumType(typeName, values);
|
|
552
519
|
}
|
|
553
520
|
|
|
554
|
-
// ---
|
|
521
|
+
// --- Column definition (delegated to Grammar) ---
|
|
555
522
|
|
|
556
523
|
function mysqlColumnDef(col: ColumnSchema): string {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
if (col.unsigned) def += ' UNSIGNED';
|
|
560
|
-
if (!col.nullable) def += ' NOT NULL';
|
|
561
|
-
if (col.autoIncrement) def += ' AUTO_INCREMENT';
|
|
562
|
-
|
|
563
|
-
if (col.defaultExpression) {
|
|
564
|
-
def += ` DEFAULT ${col.defaultExpression}`;
|
|
565
|
-
} else if (col.defaultValue !== undefined) {
|
|
566
|
-
def += ` DEFAULT '${escapeStr(String(col.defaultValue), 'mysql')}'`;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
if (col.onUpdateExpression) {
|
|
570
|
-
def += ` ON UPDATE ${col.onUpdateExpression}`;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
return def;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
function mysqlTypeDef(col: ColumnSchema): string {
|
|
577
|
-
switch (col.type) {
|
|
578
|
-
case 'varchar':
|
|
579
|
-
return `VARCHAR(${col.size || 255})`;
|
|
580
|
-
case 'char':
|
|
581
|
-
return `CHAR(${col.size || 1})`;
|
|
582
|
-
case 'tinyint':
|
|
583
|
-
return col.size === 1 ? 'TINYINT(1)' : 'TINYINT';
|
|
584
|
-
case 'smallint':
|
|
585
|
-
return 'SMALLINT';
|
|
586
|
-
case 'int':
|
|
587
|
-
return 'INT';
|
|
588
|
-
case 'bigint':
|
|
589
|
-
return 'BIGINT';
|
|
590
|
-
case 'float':
|
|
591
|
-
return 'FLOAT';
|
|
592
|
-
case 'double':
|
|
593
|
-
return 'DOUBLE';
|
|
594
|
-
case 'decimal':
|
|
595
|
-
if (col.size === undefined) return 'DECIMAL';
|
|
596
|
-
return col.scale !== undefined ? `DECIMAL(${col.size},${col.scale})` : `DECIMAL(${col.size})`;
|
|
597
|
-
case 'boolean':
|
|
598
|
-
return 'TINYINT(1)';
|
|
599
|
-
case 'date':
|
|
600
|
-
return 'DATE';
|
|
601
|
-
case 'datetime':
|
|
602
|
-
return 'DATETIME';
|
|
603
|
-
case 'timestamp':
|
|
604
|
-
return 'TIMESTAMP';
|
|
605
|
-
case 'text':
|
|
606
|
-
return 'TEXT';
|
|
607
|
-
case 'binary':
|
|
608
|
-
return `BINARY(${col.size || 16})`;
|
|
609
|
-
case 'blob':
|
|
610
|
-
return 'BLOB';
|
|
611
|
-
case 'json':
|
|
612
|
-
return 'JSON';
|
|
613
|
-
case 'point':
|
|
614
|
-
return 'POINT';
|
|
615
|
-
case 'enum':
|
|
616
|
-
if (col.enumValues) {
|
|
617
|
-
const vals = col.enumValues.map(v => `'${escapeStr(v, 'mysql')}'`).join(',');
|
|
618
|
-
return `ENUM(${vals})`;
|
|
619
|
-
}
|
|
620
|
-
return 'VARCHAR(255)';
|
|
621
|
-
default:
|
|
622
|
-
return col.type.toUpperCase();
|
|
623
|
-
}
|
|
524
|
+
return getGrammar('mysql').columnDef(col);
|
|
624
525
|
}
|
|
625
526
|
|
|
626
|
-
// --- PostgreSQL column definition ---
|
|
627
|
-
|
|
628
527
|
function pgColumnDef(col: ColumnSchema, pgSchema?: string): string {
|
|
629
|
-
|
|
630
|
-
if (col.autoIncrement) {
|
|
631
|
-
// Use SERIAL/BIGSERIAL for auto-increment
|
|
632
|
-
typeDef = col.type === 'bigint' ? 'BIGSERIAL' : 'SERIAL';
|
|
633
|
-
} else {
|
|
634
|
-
typeDef = pgTypeDef(col, pgSchema);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
let def = `${q('postgres', col.name)} ${typeDef}`;
|
|
638
|
-
|
|
639
|
-
if (!col.nullable && !col.autoIncrement) def += ' NOT NULL';
|
|
640
|
-
|
|
641
|
-
if (!col.autoIncrement) {
|
|
642
|
-
if (col.defaultExpression) {
|
|
643
|
-
def += ` DEFAULT ${col.defaultExpression}`;
|
|
644
|
-
} else if (col.defaultValue !== undefined) {
|
|
645
|
-
def += ` DEFAULT '${escapeStr(String(col.defaultValue))}'`;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
return def;
|
|
528
|
+
return getGrammar('postgres', pgSchema).columnDef(col);
|
|
650
529
|
}
|
|
651
530
|
|
|
652
531
|
function pgTypeDef(col: ColumnSchema, pgSchema?: string): string {
|
|
653
|
-
|
|
654
|
-
case 'varchar':
|
|
655
|
-
return col.size ? `VARCHAR(${col.size})` : 'VARCHAR';
|
|
656
|
-
case 'char':
|
|
657
|
-
return `CHAR(${col.size || 1})`;
|
|
658
|
-
case 'smallint':
|
|
659
|
-
return 'SMALLINT';
|
|
660
|
-
case 'int':
|
|
661
|
-
case 'integer':
|
|
662
|
-
return 'INTEGER';
|
|
663
|
-
case 'bigint':
|
|
664
|
-
return 'BIGINT';
|
|
665
|
-
case 'real':
|
|
666
|
-
case 'float':
|
|
667
|
-
return 'REAL';
|
|
668
|
-
case 'double precision':
|
|
669
|
-
case 'double':
|
|
670
|
-
return 'DOUBLE PRECISION';
|
|
671
|
-
case 'decimal':
|
|
672
|
-
case 'numeric':
|
|
673
|
-
if (col.size === undefined) return 'NUMERIC';
|
|
674
|
-
return col.scale !== undefined ? `NUMERIC(${col.size},${col.scale})` : `NUMERIC(${col.size})`;
|
|
675
|
-
case 'boolean':
|
|
676
|
-
return 'BOOLEAN';
|
|
677
|
-
case 'date':
|
|
678
|
-
return 'DATE';
|
|
679
|
-
case 'timestamp':
|
|
680
|
-
return 'TIMESTAMP';
|
|
681
|
-
case 'timestamptz':
|
|
682
|
-
return 'TIMESTAMPTZ';
|
|
683
|
-
case 'text':
|
|
684
|
-
return 'TEXT';
|
|
685
|
-
case 'bytea':
|
|
686
|
-
return 'BYTEA';
|
|
687
|
-
case 'json':
|
|
688
|
-
return 'JSON';
|
|
689
|
-
case 'jsonb':
|
|
690
|
-
return 'JSONB';
|
|
691
|
-
case 'uuid':
|
|
692
|
-
return 'UUID';
|
|
693
|
-
case 'enum':
|
|
694
|
-
return col.enumTypeName ? qType(col.enumTypeName, pgSchema) : 'TEXT';
|
|
695
|
-
default:
|
|
696
|
-
return col.type.toUpperCase();
|
|
697
|
-
}
|
|
532
|
+
return getPgGrammar(pgSchema).columnType(col);
|
|
698
533
|
}
|
|
699
534
|
|
|
700
535
|
// --- AFTER clause helpers (MySQL) ---
|
|
@@ -737,35 +572,23 @@ function findAfterClauseForExisting(colName: string, diff: TableDiff, dialect: D
|
|
|
737
572
|
}
|
|
738
573
|
|
|
739
574
|
function q(dialect: Dialect, name: string): string {
|
|
740
|
-
return
|
|
575
|
+
return getGrammar(dialect).quote(name);
|
|
741
576
|
}
|
|
742
577
|
|
|
743
578
|
function qTable(dialect: Dialect, name: string, pgSchema?: string): string {
|
|
744
|
-
|
|
745
|
-
return `${quoteId(dialect, pgSchema)}.${quoteId(dialect, name)}`;
|
|
746
|
-
}
|
|
747
|
-
return quoteId(dialect, name);
|
|
579
|
+
return getGrammar(dialect, pgSchema).qualifiedTable(name);
|
|
748
580
|
}
|
|
749
581
|
|
|
750
582
|
function qType(typeName: string, pgSchema?: string): string {
|
|
751
|
-
|
|
752
|
-
return `${quoteId('postgres', pgSchema)}.${quoteId('postgres', typeName)}`;
|
|
753
|
-
}
|
|
754
|
-
return quoteId('postgres', typeName);
|
|
583
|
+
return getPgGrammar(pgSchema).qualifiedType(typeName);
|
|
755
584
|
}
|
|
756
585
|
|
|
757
586
|
function pgRegclass(name: string, pgSchema?: string): string {
|
|
758
|
-
//
|
|
759
|
-
|
|
760
|
-
return `${quoteId('postgres', pgSchema)}.${quoteId('postgres', name)}`;
|
|
761
|
-
}
|
|
762
|
-
return quoteId('postgres', name);
|
|
587
|
+
// Same form as qualifiedType — used in SQL string literals (quoted identifiers).
|
|
588
|
+
return getPgGrammar(pgSchema).qualifiedType(name);
|
|
763
589
|
}
|
|
764
590
|
|
|
765
591
|
function escapeStr(s: string, dialect?: Dialect): string {
|
|
766
|
-
|
|
767
|
-
if (dialect === 'mysql') {
|
|
768
|
-
s = s.replace(/\\/g, '\\\\');
|
|
769
|
-
}
|
|
592
|
+
if (dialect === 'mysql') s = s.replace(/\\/g, '\\\\');
|
|
770
593
|
return s.replace(/'/g, "''");
|
|
771
594
|
}
|
|
@@ -5,6 +5,11 @@ import { getSourceMigrationsDir } from '../helpers';
|
|
|
5
5
|
import { COMMENT_PREFIX } from './ddl-generator';
|
|
6
6
|
|
|
7
7
|
export function generateMigrationFile(statements: string[], description: string): string {
|
|
8
|
+
return writeMigrationFile(buildFileContent(statements), description);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Write pre-rendered file content (e.g. builder-based) to a timestamped migration file. */
|
|
12
|
+
export function writeMigrationFile(content: string, description: string): string {
|
|
8
13
|
const migrationsDir = getSourceMigrationsDir();
|
|
9
14
|
|
|
10
15
|
if (!existsSync(migrationsDir)) {
|
|
@@ -16,7 +21,6 @@ export function generateMigrationFile(statements: string[], description: string)
|
|
|
16
21
|
const filename = `${timestamp}_${slug}.ts`;
|
|
17
22
|
const filePath = path.join(migrationsDir, filename);
|
|
18
23
|
|
|
19
|
-
const content = buildFileContent(statements);
|
|
20
24
|
writeFileSync(filePath, content, 'utf8');
|
|
21
25
|
|
|
22
26
|
return filePath;
|
|
@@ -22,6 +22,8 @@ export interface ColumnSchema {
|
|
|
22
22
|
isIdentity?: boolean; // PG only: true if GENERATED ... AS IDENTITY (vs sequence-backed serial)
|
|
23
23
|
sequenceName?: string; // PG only: actual sequence name from pg_get_serial_sequence()
|
|
24
24
|
ordinalPosition: number; // 1-based column order
|
|
25
|
+
/** MySQL only — positioning hint for ALTER TABLE ADD COLUMN. `null` = FIRST, string = AFTER <col>, undefined = no clause. */
|
|
26
|
+
afterColumn?: string | null;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export interface IndexSchema {
|
package/src/database/mysql.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ClassType, getClassName } from '@deepkit/core';
|
|
2
|
-
import { Logger } from '@deepkit/logger';
|
|
3
2
|
import { MySQLConnection, MySQLDatabaseAdapter as BaseMySQLDatabaseAdapter } from '@deepkit/mysql';
|
|
4
3
|
import { Database, DatabaseSession } from '@deepkit/orm';
|
|
5
4
|
import { isNonUndefined } from '@deepkit/sql';
|
|
@@ -7,7 +6,7 @@ import { databaseAnnotation, ReflectionKind, Type } from '@deepkit/type';
|
|
|
7
6
|
import { PoolConfig } from 'mariadb';
|
|
8
7
|
|
|
9
8
|
import { Coordinate } from '.';
|
|
10
|
-
import { getAppConfig
|
|
9
|
+
import { getAppConfig } from '../app/resolver';
|
|
11
10
|
import { globalState } from '../app/state';
|
|
12
11
|
import { BaseDatabase } from './common';
|
|
13
12
|
|
|
@@ -120,21 +119,14 @@ export function createMySQLDatabase(
|
|
|
120
119
|
entities.push(...globalState.additionalEntities);
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
// Mark the adapter so acquireSessionLock can lazy-init the _locks table on first use.
|
|
123
|
+
// Constructor-time fire-and-forget would race with pool teardown for short-lived CLI
|
|
124
|
+
// commands like migration:reset that don't issue any DB work.
|
|
125
125
|
if (enableLocksTable) {
|
|
126
|
-
|
|
127
|
-
`CREATE TABLE IF NOT EXISTS \`_locks\` (
|
|
128
|
-
\`key\` VARCHAR(255) NOT NULL PRIMARY KEY,
|
|
129
|
-
\`createdAt\` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
130
|
-
\`lastTouched\` DATETIME
|
|
131
|
-
)`
|
|
132
|
-
)
|
|
133
|
-
.then(() => this.rawExecute(`DELETE FROM _locks WHERE lastTouched < NOW() - INTERVAL 1 HOUR`))
|
|
134
|
-
.catch(err => {
|
|
135
|
-
r(Logger).error(`Could not create _locks table: %s`, err);
|
|
136
|
-
});
|
|
126
|
+
(adapter as unknown as { _enableLocksTable: boolean })._enableLocksTable = true;
|
|
137
127
|
}
|
|
128
|
+
|
|
129
|
+
super(adapter, entities);
|
|
138
130
|
}
|
|
139
131
|
};
|
|
140
132
|
}
|
package/src/database/postgres.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { ClassType } from '@deepkit/core';
|
|
2
|
-
import { Logger } from '@deepkit/logger';
|
|
3
2
|
import { Database, DatabaseSession } from '@deepkit/orm';
|
|
4
3
|
import { PostgresConnection, PostgresDatabaseAdapter as BasePostgresDatabaseAdapter } from '@deepkit/postgres';
|
|
5
4
|
import { isNonUndefined } from '@deepkit/sql';
|
|
6
5
|
import { ReflectionKind, Type } from '@deepkit/type';
|
|
7
6
|
import { PoolConfig } from 'pg';
|
|
8
7
|
|
|
9
|
-
import { getAppConfig
|
|
8
|
+
import { getAppConfig } from '../app/resolver';
|
|
10
9
|
import { globalState } from '../app/state';
|
|
11
10
|
import { BaseDatabase } from './common';
|
|
12
11
|
|
|
@@ -46,6 +45,7 @@ export class PostgresDatabaseAdapter extends BasePostgresDatabaseAdapter {
|
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
export interface IPostgresDatabaseAdapterConfig extends PoolConfig {
|
|
48
|
+
/** Accepted for cross-dialect API parity with createMySQLDatabase. PG uses `pg_advisory_xact_lock` for session locks — no table needed. */
|
|
49
49
|
enableLocksTable?: boolean;
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -58,7 +58,7 @@ export function createPostgresDatabase(
|
|
|
58
58
|
const appConfig = getAppConfig();
|
|
59
59
|
const isProduction = appConfig.APP_ENV === 'production';
|
|
60
60
|
|
|
61
|
-
const { enableLocksTable, ...otherConfig } = config;
|
|
61
|
+
const { enableLocksTable: _enableLocksTable, ...otherConfig } = config;
|
|
62
62
|
|
|
63
63
|
const ssl = appConfig.PG_SSL ? { rejectUnauthorized: appConfig.PG_SSL_REJECT_UNAUTHORIZED ?? true } : undefined;
|
|
64
64
|
|
|
@@ -81,20 +81,6 @@ export function createPostgresDatabase(
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
super(adapter, entities);
|
|
84
|
-
|
|
85
|
-
if (enableLocksTable) {
|
|
86
|
-
this.rawExecute(
|
|
87
|
-
`CREATE TABLE IF NOT EXISTS "_locks" (
|
|
88
|
-
"key" VARCHAR(255) NOT NULL PRIMARY KEY,
|
|
89
|
-
"createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
90
|
-
"lastTouched" TIMESTAMP
|
|
91
|
-
)`
|
|
92
|
-
)
|
|
93
|
-
.then(() => this.rawExecute(`DELETE FROM "_locks" WHERE "lastTouched" < NOW() - INTERVAL '1 hour'`))
|
|
94
|
-
.catch(err => {
|
|
95
|
-
r(Logger).error(`Could not create _locks table: %s`, err);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
84
|
}
|
|
99
85
|
};
|
|
100
86
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ColumnSchema, ForeignKeySchema, IndexSchema } from '../migration/create/schema-model';
|
|
2
|
+
import { BlueprintBase } from './BlueprintBase';
|
|
3
|
+
import { ColumnDefinition } from './ColumnDefinition';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Alter-table blueprint. Records discrete operations (add/drop/rename/change column,
|
|
7
|
+
* add/drop index/FK, set/drop primary key) for an existing table.
|
|
8
|
+
*
|
|
9
|
+
* Inherits all column-type methods from BlueprintBase — by default a column added via
|
|
10
|
+
* `t.string('email')` becomes an ADD COLUMN. Call `.change()` on the returned ColumnDefinition
|
|
11
|
+
* to mark it as a modification (MODIFY COLUMN / ALTER COLUMN) instead.
|
|
12
|
+
*/
|
|
13
|
+
export class AlterBlueprint extends BlueprintBase {
|
|
14
|
+
public readonly addedColumns: ColumnSchema[] = [];
|
|
15
|
+
public readonly modifiedColumns: ColumnSchema[] = [];
|
|
16
|
+
public readonly droppedColumns: string[] = [];
|
|
17
|
+
public readonly renamedColumns: { from: string; to: string }[] = [];
|
|
18
|
+
public readonly addedIndexes: IndexSchema[] = [];
|
|
19
|
+
public readonly droppedIndexes: string[] = [];
|
|
20
|
+
public readonly addedForeignKeys: ForeignKeySchema[] = [];
|
|
21
|
+
public readonly droppedForeignKeys: string[] = [];
|
|
22
|
+
public newPrimaryKey?: string[];
|
|
23
|
+
public dropsPrimaryKey = false;
|
|
24
|
+
|
|
25
|
+
protected addColumnImpl(col: ColumnSchema): ColumnDefinition {
|
|
26
|
+
this.addedColumns.push(col);
|
|
27
|
+
return new ColumnDefinition(this, col);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected addIndexImpl(idx: IndexSchema): void {
|
|
31
|
+
this.addedIndexes.push(idx);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected addForeignKeyImpl(fk: ForeignKeySchema): void {
|
|
35
|
+
this.addedForeignKeys.push(fk);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
dropColumn(name: string): this {
|
|
39
|
+
this.droppedColumns.push(name);
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
renameColumn(from: string, to: string): this {
|
|
44
|
+
this.renamedColumns.push({ from, to });
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Drop an index by name. */
|
|
49
|
+
dropIndex(name: string): this {
|
|
50
|
+
this.droppedIndexes.push(name);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Alias of dropIndex — same op at the SQL level, kept for readability. */
|
|
55
|
+
dropUnique(name: string): this {
|
|
56
|
+
return this.dropIndex(name);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Drop a foreign key constraint by name. */
|
|
60
|
+
dropForeign(name: string): this {
|
|
61
|
+
this.droppedForeignKeys.push(name);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Set / replace the table's primary key. */
|
|
66
|
+
primary(columns: string[]): this {
|
|
67
|
+
this.newPrimaryKey = columns;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Drop the existing primary key. Combine with `.primary([...])` to replace. */
|
|
72
|
+
dropPrimary(): this {
|
|
73
|
+
this.dropsPrimaryKey = true;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Called by ColumnDefinition.change() — moves a column from added → modified. */
|
|
78
|
+
markColumnAsModified(col: ColumnSchema): void {
|
|
79
|
+
const idx = this.addedColumns.indexOf(col);
|
|
80
|
+
if (idx >= 0) {
|
|
81
|
+
this.addedColumns.splice(idx, 1);
|
|
82
|
+
this.modifiedColumns.push(col);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ColumnSchema, ForeignKeySchema, IndexSchema, TableSchema } from '../migration/create/schema-model';
|
|
2
|
+
import { BlueprintBase } from './BlueprintBase';
|
|
3
|
+
import { ColumnDefinition } from './ColumnDefinition';
|
|
4
|
+
|
|
5
|
+
/** Create-table blueprint. Records columns + indexes + FKs and renders a TableSchema. */
|
|
6
|
+
export class Blueprint extends BlueprintBase {
|
|
7
|
+
public readonly columns: ColumnSchema[] = [];
|
|
8
|
+
public readonly indexes: IndexSchema[] = [];
|
|
9
|
+
public readonly foreignKeys: ForeignKeySchema[] = [];
|
|
10
|
+
|
|
11
|
+
protected addColumnImpl(col: ColumnSchema): ColumnDefinition {
|
|
12
|
+
this.columns.push(col);
|
|
13
|
+
return new ColumnDefinition(this, col);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
protected addIndexImpl(idx: IndexSchema): void {
|
|
17
|
+
this.indexes.push(idx);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
protected addForeignKeyImpl(fk: ForeignKeySchema): void {
|
|
21
|
+
this.foreignKeys.push(fk);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Composite primary key — replaces single-column .primary() flag with a multi-column PK. */
|
|
25
|
+
primary(columns: string[]): this {
|
|
26
|
+
for (const col of this.columns) {
|
|
27
|
+
col.isPrimaryKey = columns.includes(col.name);
|
|
28
|
+
}
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
build(): TableSchema {
|
|
33
|
+
this.columns.forEach((col, i) => {
|
|
34
|
+
col.ordinalPosition = i + 1;
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
name: this.tableName,
|
|
38
|
+
columns: this.columns,
|
|
39
|
+
indexes: this.indexes,
|
|
40
|
+
foreignKeys: this.foreignKeys
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|