diff --git a/integration-tests/__tests__/deploy-database.spec.js b/integration-tests/__tests__/deploy-database.spec.js index aac6aad88..480ba1e09 100644 --- a/integration-tests/__tests__/deploy-database.spec.js +++ b/integration-tests/__tests__/deploy-database.spec.js @@ -82,30 +82,34 @@ async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) { dbdiffOptionsExtra.schemaMode = 'ignore'; for (const loadedDbModel of dbModelsYaml) { - const { sql, isEmpty } = await generateDeploySql({ - systemConnection: conn.isPreparedOnly ? undefined : conn, - connection: conn.isPreparedOnly ? conn : undefined, - driver, - loadedDbModel, - dbdiffOptionsExtra, - }); - console.debug('Generated deploy script:', sql); - if (!allowDropStatements) { - expect(sql.toUpperCase().includes('DROP ')).toBeFalsy(); - } + if (_.isString(loadedDbModel)) { + await driver.script(conn, loadedDbModel); + } else { + const { sql, isEmpty } = await generateDeploySql({ + systemConnection: conn.isPreparedOnly ? undefined : conn, + connection: conn.isPreparedOnly ? conn : undefined, + driver, + loadedDbModel, + dbdiffOptionsExtra, + }); + console.debug('Generated deploy script:', sql); + if (!allowDropStatements) { + expect(sql.toUpperCase().includes('DROP ')).toBeFalsy(); + } - console.log('dbModelsYaml.length', dbModelsYaml.length, index); - if (testEmptyLastScript && index == dbModelsYaml.length - 1) { - expect(isEmpty).toBeTruthy(); - } + console.log('dbModelsYaml.length', dbModelsYaml.length, index); + if (testEmptyLastScript && index == dbModelsYaml.length - 1) { + expect(isEmpty).toBeTruthy(); + } - await deployDb({ - systemConnection: conn.isPreparedOnly ? undefined : conn, - connection: conn.isPreparedOnly ? conn : undefined, - driver, - loadedDbModel, - dbdiffOptionsExtra, - }); + await deployDb({ + systemConnection: conn.isPreparedOnly ? undefined : conn, + connection: conn.isPreparedOnly ? conn : undefined, + driver, + loadedDbModel, + dbdiffOptionsExtra, + }); + } index++; } @@ -416,6 +420,48 @@ describe('Deploy database', () => { }) ); + test.each(engines.map(engine => [engine.label, engine]))( + 'Change column to NOT NULL column with default - %s', + testWrapper(async (conn, driver, engine) => { + await testDatabaseDeploy(engine, conn, driver, [ + [ + { + name: 't1.table.yaml', + json: { + name: 't1', + columns: [ + { name: 'id', type: 'int', notNull: true }, + { name: 'val', type: 'int' }, + ], + + primaryKey: ['id'], + }, + }, + ], + 'insert into t1 (id, val) values (1, 1); insert into t1 (id) values (2)', + [ + { + name: 't1.table.yaml', + json: { + name: 't1', + columns: [ + { name: 'id', type: 'int', notNull: true }, + { name: 'val', type: 'int', notNull: true, default: '20' }, + ], + primaryKey: ['id'], + }, + }, + ], + ]); + + const res1 = await driver.query(conn, `select val from t1 where id = 1`); + expect(res1.rows[0].val).toEqual(1); + + const res2 = await driver.query(conn, `select val from t1 where id = 2`); + expect(res2.rows[0].val).toEqual(20); + }) + ); + const T1 = { name: 't1.table.yaml', json: { diff --git a/packages/tools/src/SqlDumper.ts b/packages/tools/src/SqlDumper.ts index a5f6ffca4..8fa3a83f2 100644 --- a/packages/tools/src/SqlDumper.ts +++ b/packages/tools/src/SqlDumper.ts @@ -731,6 +731,12 @@ export class SqlDumper implements AlterProcessor { this.put(formatString, optionValue); } + fillNewNotNullDefaults(col: ColumnInfo) { + if (col.notNull && col.defaultValue) { + this.putCmd('^update %f ^set %i = %s ^where %i ^is ^null', col, col.columnName, col.defaultValue, col.columnName); + } + } + fillPreloadedRows( table: NamedObjectInfo, oldRows: any[], diff --git a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js index 68f599535..b87c6b44e 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js @@ -132,6 +132,7 @@ class MsSqlDumper extends SqlDumper { } else { this.dropDefault(oldcol); if (oldcol.columnName != newcol.columnName) this.renameColumn(oldcol, newcol.columnName); + this.fillNewNotNullDefaults(newcol); this.put('^alter ^table %f ^alter ^column %i ', oldcol, oldcol.columnName, newcol.columnName); this.columnDefinition(newcol, { includeDefault: false }); this.endCommand(); diff --git a/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js b/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js index dd269f590..963fc1dc1 100644 --- a/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js +++ b/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js @@ -76,10 +76,6 @@ class Dumper extends SqlDumper { if (!testEqualTypes(oldcol, newcol)) { this.putCmd('^alter ^table %f ^alter ^column %i ^type %s', oldcol, newcol.columnName, newcol.dataType); } - if (oldcol.notNull != newcol.notNull) { - if (newcol.notNull) this.putCmd('^alter ^table %f ^alter ^column %i ^set ^not ^null', newcol, newcol.columnName); - else this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^not ^null', newcol, newcol.columnName); - } if (oldcol.defaultValue != newcol.defaultValue) { if (newcol.defaultValue == null) { this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^default', newcol, newcol.columnName); @@ -92,6 +88,11 @@ class Dumper extends SqlDumper { ); } } + if (oldcol.notNull != newcol.notNull) { + this.fillNewNotNullDefaults(newcol); + if (newcol.notNull) this.putCmd('^alter ^table %f ^alter ^column %i ^set ^not ^null', newcol, newcol.columnName); + else this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^not ^null', newcol, newcol.columnName); + } } putValue(value) {