This commit is contained in:
SPRINX0\prochazka
2025-06-12 16:55:58 +02:00
44 changed files with 1437 additions and 22 deletions

View File

@@ -20,6 +20,7 @@ const crypto = require('crypto');
* @param {string} options.ignoreNameRegex - regex for ignoring objects by name
* @param {string} options.targetSchema - target schema for deployment
* @param {number} options.maxMissingTablesRatio - maximum ratio of missing tables in database. Safety check, if missing ratio is highe, deploy is stopped (preventing accidental drop of all tables)
* @param {boolean} options.useTransaction - run deploy in transaction. If not provided, it will be set to true if driver supports transactions
*/
async function deployDb({
connection,
@@ -33,6 +34,7 @@ async function deployDb({
ignoreNameRegex = '',
targetSchema = null,
maxMissingTablesRatio = undefined,
useTransaction,
}) {
if (!driver) driver = requireEngineDriver(connection);
const dbhan = systemConnection || (await connectUtility(driver, connection, 'read'));
@@ -60,7 +62,14 @@ async function deployDb({
maxMissingTablesRatio,
});
// console.log('RUNNING DEPLOY SCRIPT:', sql);
await executeQuery({ connection, systemConnection: dbhan, driver, sql, logScriptItems: true });
await executeQuery({
connection,
systemConnection: dbhan,
driver,
sql,
logScriptItems: true,
useTransaction,
});
await scriptDeployer.runPost();
} finally {

View File

@@ -14,6 +14,7 @@ const logger = getLogger('execQuery');
* @param {string} [options.sql] - SQL query
* @param {string} [options.sqlFile] - SQL file
* @param {boolean} [options.logScriptItems] - whether to log script items instead of whole script
* @param {boolean} [options.useTransaction] - run query in transaction
* @param {boolean} [options.skipLogging] - whether to skip logging
*/
async function executeQuery({
@@ -24,6 +25,7 @@ async function executeQuery({
sqlFile = undefined,
logScriptItems = false,
skipLogging = false,
useTransaction,
}) {
if (!logScriptItems && !skipLogging) {
logger.info({ sql: getLimitedQuery(sql) }, `Execute query`);
@@ -42,7 +44,7 @@ async function executeQuery({
logger.debug(`Running SQL query, length: ${sql.length}`);
}
await driver.script(dbhan, sql, { logScriptItems });
await driver.script(dbhan, sql, { logScriptItems, useTransaction });
} finally {
if (!systemConnection) {
await driver.close(dbhan);

View File

@@ -41,13 +41,14 @@ program
'regex, which table data will be loaded and stored in model (in load command)'
)
.option('-e, --engine <engine>', 'engine name, eg. mysql@dbgate-plugin-mysql')
.option('--commonjs', 'Creates CommonJS module');
.option('--commonjs', 'Creates CommonJS module')
.option('--transaction', 'Run deploy query in transaction');
program
.command('deploy <modelFolder>')
.description('Deploys model to database')
.action(modelFolder => {
const { engine, server, user, password, database } = program.opts();
const { engine, server, user, password, database, transaction } = program.opts();
// const hooks = [];
// if (program.autoIndexForeignKeys) hooks.push(dbmodel.hooks.autoIndexForeignKeys);
@@ -61,6 +62,7 @@ program
database,
},
modelFolder,
useTransaction: transaction,
})
);
});

View File

@@ -12,6 +12,9 @@ export function dumpSqlSelect(dmp: SqlDumper, cmd: Select) {
if (cmd.topRecords) {
if (!dmp.dialect.rangeSelect || dmp.dialect.offsetFetchRangeSyntax) dmp.put('^top %s ', cmd.topRecords);
}
if (cmd.range && dmp.dialect.offsetFirstSkipSyntax) {
dmp.put('^first %s ^skip %s ', cmd.range.limit, cmd.range.offset);
}
if (cmd.selectAll) {
dmp.put('* ');
}
@@ -52,6 +55,8 @@ export function dumpSqlSelect(dmp: SqlDumper, cmd: Select) {
if (cmd.range) {
if (dmp.dialect.offsetFetchRangeSyntax) {
dmp.put('^offset %s ^rows ^fetch ^next %s ^rows ^only', cmd.range.offset, cmd.range.limit);
} else if (dmp.dialect.offsetFirstSkipSyntax) {
//
} else if (dmp.dialect.offsetNotSupported) {
dmp.put('^limit %s', cmd.range.limit + cmd.range.offset);
} else {

View File

@@ -266,11 +266,11 @@ export class SqlDumper implements AlterProcessor {
this.columnDefault(column);
}
if (includeNullable && !this.dialect?.specificNullabilityImplementation) {
this.put(column.notNull ? '^not ^null' : '^null');
this.put(column.notNull ? '^not ^null' : this.dialect.implicitNullDeclaration ? '' : '^null');
}
} else {
if (includeNullable && !this.dialect?.specificNullabilityImplementation) {
this.put(column.notNull ? '^not ^null' : '^null');
this.put(column.notNull ? '^not ^null' : this.dialect.implicitNullDeclaration ? '' : '^null');
}
if (includeDefault && column.defaultValue?.toString()?.trim()) {
this.columnDefault(column);

View File

@@ -8,6 +8,7 @@ export interface SqlDialect {
topRecords?: boolean;
stringEscapeChar: string;
offsetFetchRangeSyntax?: boolean;
offsetFirstSkipSyntax?: boolean;
offsetNotSupported?: boolean;
quoteIdentifier(s: string): string;
fallbackDataType?: string;
@@ -47,6 +48,7 @@ export interface SqlDialect {
namedDefaultConstraint?: boolean;
specificNullabilityImplementation?: boolean;
implicitNullDeclaration?: boolean;
omitForeignKeys?: boolean;
omitUniqueConstraints?: boolean;
omitIndexes?: boolean;
@@ -66,6 +68,7 @@ export interface SqlDialect {
requireFromDual?: boolean;
userDatabaseNamePrefix?: string; // c## in Oracle
upperCaseAllDbObjectNames?: boolean;
dbFileExtension?: string;
defaultValueBeforeNullability?: boolean;
predefinedDataTypes: string[];

View File

@@ -45,12 +45,14 @@ export type TestEngineInfo = {
skipChangeNullability?: boolean;
skipRenameColumn?: boolean;
skipDropReferences?: boolean;
skipRenameTable?: boolean;
forceSortResults?: boolean;
forceSortStructureColumns?: boolean;
alterTableAddColumnSyntax?: boolean;
dbSnapshotBySeconds?: boolean;
setNullDefaultInsteadOfDrop?: boolean;
runDeployInTransaction?: boolean;
useTextTypeForStrings?: boolean;
@@ -60,6 +62,8 @@ export type TestEngineInfo = {
defaultSchemaName?: string;
generateDbFile?: boolean;
generateDbFileOnServer?: boolean;
databaseFileLocationOnServer?: string;
dbSnapshotBySeconds?: boolean;
dumpFile?: string;
dumpChecks?: Array<{ sql: string; res: string }>;