Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/core/src/modules/sql-dialect.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ export interface SqlDialect {
*/
wrapCreateSequence?(qualifiedName: string, createSql: string): string;

/**
* Wrap the standard `ALTER SEQUENCE name ...;` into a dialect-safe form.
* Called with the qualified name and the full `ALTER SEQUENCE name ...;` string.
* MariaDB needs the CYCLE/CACHE negatives collapsed to the single tokens
* `NOCYCLE`/`NOCACHE` (the generic renderer emits `NO CYCLE`/`NO CACHE`, which
* MariaDB rejects with a syntax error) — the same normalization wrapCreateSequence
* applies on the CREATE path. Default: returns the statement unchanged.
*/
wrapAlterSequence?(qualifiedName: string, alterSql: string): string;

// ── Version-aware DROP hooks ────────────────────────────────────────────────
// Each hook receives the server version string detected at connect time.
// When the version is undefined the hook should fall back to the modern syntax.
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/modules/sql-generator.module.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,20 @@ describe('SqlGeneratorModule.generateMigrationPlan', () => {
expect(ora.some((s) => /RESTART/i.test(s))).toBe(false);
});

it('renders a MODIFIED MariaDB sequence ALTER with single-token NOCYCLE/NOCACHE (MariaDB rejects "NO CYCLE"/"NO CACHE")', () => {
const diff: TableDiff = {
tableName: 'ORDER_SEQ', objectType: 'SEQUENCE', status: 'MODIFIED',
columnDiffs: [], indexDiffs: [], foreignKeyDiffs: [],
sourceTable: tableSchema({ name: 'ORDER_SEQ', objectType: 'SEQUENCE', sequence: { start: '1000', increment: '1', minValue: '1', maxValue: '9223372036854775806', cycle: false, cache: 0 } }),
targetTable: tableSchema({ name: 'ORDER_SEQ', objectType: 'SEQUENCE', sequence: { start: '1', increment: '1', cycle: true, cache: 1000 } }),
};
const stmts = gen.generateMigrationPlan([diff], 'mariadb', { sourceSchema: 'demo_a', targetSchema: 'demo_b' }).flatMap((s) => s.statements);
const alter = stmts.find((s) => /ALTER SEQUENCE/.test(s))!;
expect(alter).toContain('NOCYCLE');
expect(alter).toContain('NOCACHE');
expect(alter).not.toMatch(/NO CYCLE|NO CACHE/);
});

it('SQL Server changes a column default by dropping the named DF constraint then re-adding', () => {
const diff: TableDiff = {
tableName: 'ORDER_ITEMS', objectType: 'TABLE', status: 'MODIFIED',
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/modules/sql-generator.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,8 @@ export class SqlGeneratorModule {
if (s.maxValue !== undefined) alter += ` MAXVALUE ${s.maxValue}`;
alter += s.cycle ? ` CYCLE` : ` NO CYCLE`;
if (s.cache !== undefined) alter += s.cache > 0 ? ` CACHE ${s.cache}` : ` NO CACHE`;
statements.push(alter + `;`);
const alterSql = alter + `;`;
statements.push(dialect.wrapAlterSequence?.(tableName, alterSql) ?? alterSql);
} else if (obj.objectType === 'TYPE' && obj.sourceTable) {
statements.push(`DROP TYPE ${tableName};`);
statements.push(this.renderCreateType({ ...obj.sourceTable, name: tableName }, dialect));
Expand Down
21 changes: 17 additions & 4 deletions packages/core/src/providers/mysql/mysql.sql-dialect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ const types = makeDialectTypeFns({
},
});

/**
* MariaDB's sequence grammar wants NOCYCLE/NOCACHE as single tokens — the generic
* renderer's `NO CYCLE`/`NO CACHE` (with a space) is a syntax error. Shared by the
* CREATE and ALTER SEQUENCE wrappers so both paths stay in sync.
*/
function normalizeSequenceTokens(sql: string): string {
return sql.replace(/\bNO CYCLE\b/g, 'NOCYCLE').replace(/\bNO CACHE\b/g, 'NOCACHE');
}

const mysqlDialect: SqlDialect = {
identityClause(c: ColumnSpec): string {
return c.identity ? ` AUTO_INCREMENT` : '';
Expand Down Expand Up @@ -131,10 +140,14 @@ const mysqlDialect: SqlDialect = {
// generic renderer's default spacing) is a syntax error — and supports
// IF NOT EXISTS directly (unlike SQL Server's OBJECT_ID-guard workaround).
wrapCreateSequence(_qualifiedName: string, createSql: string): string {
return createSql
.replace(/\bNO CYCLE\b/g, 'NOCYCLE')
.replace(/\bNO CACHE\b/g, 'NOCACHE')
.replace(/^CREATE SEQUENCE /, 'CREATE SEQUENCE IF NOT EXISTS ');
return normalizeSequenceTokens(createSql).replace(/^CREATE SEQUENCE /, 'CREATE SEQUENCE IF NOT EXISTS ');
},

// The ALTER SEQUENCE path in the generator builds its statement directly (it
// doesn't route through wrapCreateSequence), so it needs the same NOCYCLE/NOCACHE
// normalization or MariaDB rejects `NO CYCLE`/`NO CACHE` with a syntax error.
wrapAlterSequence(_qualifiedName: string, alterSql: string): string {
return normalizeSequenceTokens(alterSql);
},

...types,
Expand Down
Loading