UNPKG

dbgate-api

Version:

Allows run DbGate data-manipulation scripts.

135 lines (120 loc) 5.38 kB
const { getAlterDatabaseScript, generateDbPairingId, matchPairedObjects, databaseInfoFromYamlModel, extendDatabaseInfo, modelCompareDbDiffOptions, enrichWithPreloadedRows, skipNamesInStructureByRegex, replaceSchemaInStructure, filterStructureBySchema, skipDbGateInternalObjects, } = require('dbgate-tools'); const importDbModel = require('../utility/importDbModel'); const requireEngineDriver = require('../utility/requireEngineDriver'); const { connectUtility } = require('../utility/connectUtility'); /** * Generates query for deploying model into database * @param {object} options * @param {connectionType} options.connection - connection object * @param {object} options.systemConnection - system connection (result of driver.connect). If not provided, new connection will be created * @param {object} options.driver - driver object. If not provided, it will be loaded from connection * @param {object} options.analysedStructure - analysed structure of the database. If not provided, it will be loaded * @param {string} options.modelFolder - folder with model files (YAML files for tables, SQL files for views, procedures, ...) * @param {import('dbgate-tools').DatabaseModelFile[] | import('dbgate-types').DatabaseInfo} options.loadedDbModel - loaded database model - collection of yaml and SQL files loaded into array * @param {function[]} options.modelTransforms - array of functions for transforming model * @param {object} options.dbdiffOptionsExtra - extra options for dbdiff * @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) */ async function generateDeploySql({ connection, systemConnection = undefined, driver = undefined, analysedStructure = undefined, modelFolder = undefined, loadedDbModel = undefined, modelTransforms = undefined, dbdiffOptionsExtra = {}, ignoreNameRegex = '', targetSchema = null, maxMissingTablesRatio = undefined, }) { if (!driver) driver = requireEngineDriver(connection); const dbhan = systemConnection || (await connectUtility(driver, connection, 'read')); if ( driver?.dialect?.multipleSchema && !targetSchema && dbdiffOptionsExtra?.['schemaMode'] !== 'ignore' && dbdiffOptionsExtra?.['schemaMode'] !== 'ignoreImplicit' ) { if (!driver?.dialect?.defaultSchemaName) { throw new Error('targetSchema is required for databases with multiple schemas'); } targetSchema = driver.dialect.defaultSchemaName; } try { if (!analysedStructure) { analysedStructure = await driver.analyseFull(dbhan); } let deployedModelSource = loadedDbModel ? databaseInfoFromYamlModel(loadedDbModel) : await importDbModel(modelFolder); if (ignoreNameRegex) { analysedStructure = skipNamesInStructureByRegex(analysedStructure, new RegExp(ignoreNameRegex, 'i')); deployedModelSource = skipNamesInStructureByRegex(deployedModelSource, new RegExp(ignoreNameRegex, 'i')); } analysedStructure = skipDbGateInternalObjects(analysedStructure); for (const transform of modelTransforms || []) { deployedModelSource = transform(deployedModelSource); } if (targetSchema) { deployedModelSource = replaceSchemaInStructure(deployedModelSource, targetSchema); analysedStructure = filterStructureBySchema(analysedStructure, targetSchema); } const deployedModel = generateDbPairingId(extendDatabaseInfo(deployedModelSource)); const currentModel = generateDbPairingId(extendDatabaseInfo(analysedStructure)); const opts = { ...modelCompareDbDiffOptions, noDropTable: true, noDropColumn: true, noDropConstraint: true, noDropSqlObject: true, noRenameTable: true, noRenameColumn: true, ...dbdiffOptionsExtra, }; const currentModelPaired = matchPairedObjects(deployedModel, currentModel, opts); const currentModelPairedPreloaded = await enrichWithPreloadedRows(deployedModel, currentModelPaired, dbhan, driver); if (maxMissingTablesRatio != null) { const missingTables = currentModelPaired.tables.filter( x => !deployedModel.tables.find(y => y.pairingId == x.pairingId) ); const missingTableCount = missingTables.length; const missingTablesRatio = missingTableCount / (currentModelPaired.tables.length || 1); if (missingTablesRatio > maxMissingTablesRatio) { throw new Error(`Too many missing tables (${missingTablesRatio * 100}%), aborting deploy`); } } // console.log('currentModelPairedPreloaded', currentModelPairedPreloaded.tables[0]); // console.log('deployedModel', deployedModel.tables[0]); // console.log('currentModel', currentModel.tables[0]); // console.log('currentModelPaired', currentModelPaired.tables[0]); const res = getAlterDatabaseScript( currentModelPairedPreloaded, deployedModel, opts, currentModelPairedPreloaded, deployedModel, driver ); return res; } finally { if (!systemConnection) { await driver.close(dbhan); } } } module.exports = generateDeploySql;