UNPKG

dbgate-tools

Version:

Auxiliary tools for other DbGate packages.

719 lines (718 loc) 26.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SqlDumper = void 0; const isString_1 = __importDefault(require("lodash/isString")); const isNumber_1 = __importDefault(require("lodash/isNumber")); const isDate_1 = __importDefault(require("lodash/isDate")); const isArray_1 = __importDefault(require("lodash/isArray")); const isPlainObject_1 = __importDefault(require("lodash/isPlainObject")); const keys_1 = __importDefault(require("lodash/keys")); const v1_1 = __importDefault(require("uuid/v1")); class SqlDumper { static convertKeywordCase(keyword) { var _a, _b; if (this.keywordsCase == 'lowerCase') return (_a = keyword === null || keyword === void 0 ? void 0 : keyword.toString()) === null || _a === void 0 ? void 0 : _a.toLowerCase(); return (_b = keyword === null || keyword === void 0 ? void 0 : keyword.toString()) === null || _b === void 0 ? void 0 : _b.toUpperCase(); } constructor(driver) { this.s = ''; this.indentLevel = 0; this.driver = driver; this.dialect = driver.dialect; } endCommand() { this.putRaw(';\n'); } putRaw(text) { this.s += text; } escapeString(value) { const esc = this.dialect.stringEscapeChar; let res = ''; for (let i = 0; i < value.length; i++) { const c = value[i]; if (c == esc || c == "'") { res += esc; } res += c; } return res; } putStringValue(value) { this.putRaw("'"); this.putRaw(this.escapeString(value)); this.putRaw("'"); } putByteArrayValue(value) { this.put('^null'); } putValue(value, dataType = null) { if (value === null) this.put('^null'); else if (value === true) this.putRaw('1'); else if (value === false) this.putRaw('0'); else if ((0, isString_1.default)(value)) this.putStringValue(value); else if ((0, isNumber_1.default)(value)) this.putRaw(value.toString()); else if ((0, isDate_1.default)(value)) this.putStringValue(new Date(value).toISOString()); else if ((value === null || value === void 0 ? void 0 : value.type) == 'Buffer' && (0, isArray_1.default)(value === null || value === void 0 ? void 0 : value.data)) this.putByteArrayValue(value === null || value === void 0 ? void 0 : value.data); else if (value === null || value === void 0 ? void 0 : value.$bigint) this.putRaw(value === null || value === void 0 ? void 0 : value.$bigint); else if ((0, isPlainObject_1.default)(value) || (0, isArray_1.default)(value)) this.putStringValue(JSON.stringify(value)); else this.put('^null'); } putCmd(format, ...args) { this.put(format, ...args); this.endCommand(); } putFormattedValue(c, value) { switch (c) { case 's': if (value != null) { this.putRaw(value.toString()); } break; case 'i': { this.putRaw(this.dialect.quoteIdentifier(value)); } break; case 'k': { if (value) { this.putRaw(SqlDumper.convertKeywordCase(value)); } } break; case 'f': { const { schemaName, pureName } = value; if (schemaName) { this.putRaw(this.dialect.quoteIdentifier(schemaName)); this.putRaw('.'); } this.putRaw(this.dialect.quoteIdentifier(pureName)); } break; case 'v': this.putValue(value); break; case 'V': this.putValue(value.value, value.dataType); break; case 'c': value(this); break; } } putFormattedList(c, collection) { if (!collection) return; this.putCollection(', ', collection, item => this.putFormattedValue(c, item)); } put(format, ...args) { let i = 0; let argIndex = 0; const length = format.length; while (i < length) { let c = format[i]; i++; switch (c) { case '^': if (format[i] == '^') { this.putRaw('^'); i++; break; } while (i < length && format[i].match(/[a-z0-9_]/i)) { this.putRaw(SqlDumper.convertKeywordCase(format[i])); i++; } break; case '~': if (format[i] == '~') { this.putRaw('~'); i++; break; } let ident = ''; while (i < length && format[i].match(/[a-z0-9_]/i)) { ident += format[i]; i++; } this.putRaw(this.dialect.quoteIdentifier(ident)); break; case '%': c = format[i]; i++; switch (c) { case '%': this.putRaw('%'); break; case ',': c = format[i]; i++; this.putFormattedList(c, args[argIndex]); break; default: this.putFormattedValue(c, args[argIndex]); break; } argIndex++; break; case '&': c = format[i]; i++; switch (c) { case '&': this.putRaw('&'); break; case '>': this.indentLevel++; break; case '<': this.indentLevel--; break; case 'n': this.putRaw('\n'); this.putRaw(' '.repeat(2 * this.indentLevel)); break; } break; default: this.putRaw(c); break; } } } autoIncrement() { this.put(' ^auto_increment'); } createDatabase(name) { this.putCmd('^create ^database %i', name); } dropDatabase(name) { this.putCmd('^drop ^database %i', name); } createSchema(name) { this.putCmd('^create ^schema %i', name); } dropSchema(name) { this.putCmd('^drop ^schema %i', name); } specialColumnOptions(column) { } selectScopeIdentity(table) { } columnType(dataType) { const type = dataType || this.dialect.fallbackDataType; const typeWithValues = type.match(/([^(]+)(\(.+[^)]\))/); if (typeWithValues === null || typeWithValues === void 0 ? void 0 : typeWithValues.length) { typeWithValues.shift(); this.putRaw(SqlDumper.convertKeywordCase(typeWithValues.shift())); this.putRaw(typeWithValues); } else { this.putRaw(SqlDumper.convertKeywordCase(type)); } } columnDefinition(column, { includeDefault = true, includeNullable = true, includeCollate = true } = {}) { var _a, _b, _c, _d, _e, _f, _g, _h; if (column.computedExpression) { this.put('^as %s', column.computedExpression); if (column.isPersisted) this.put(' ^persisted'); return; } this.columnType(column.dataType); if (column.autoIncrement && !((_a = this.dialect) === null || _a === void 0 ? void 0 : _a.disableAutoIncrement)) { this.autoIncrement(); } this.putRaw(' '); this.specialColumnOptions(column); if ((_b = this.dialect) === null || _b === void 0 ? void 0 : _b.defaultValueBeforeNullability) { if (includeDefault && ((_d = (_c = column.defaultValue) === null || _c === void 0 ? void 0 : _c.toString()) === null || _d === void 0 ? void 0 : _d.trim())) { this.columnDefault(column); } if (includeNullable && !((_e = this.dialect) === null || _e === void 0 ? void 0 : _e.specificNullabilityImplementation)) { this.put(column.notNull ? '^not ^null' : this.dialect.implicitNullDeclaration ? '' : '^null'); } } else { if (includeNullable && !((_f = this.dialect) === null || _f === void 0 ? void 0 : _f.specificNullabilityImplementation)) { this.put(column.notNull ? '^not ^null' : this.dialect.implicitNullDeclaration ? '' : '^null'); } if (includeDefault && ((_h = (_g = column.defaultValue) === null || _g === void 0 ? void 0 : _g.toString()) === null || _h === void 0 ? void 0 : _h.trim())) { this.columnDefault(column); } } } columnDefault(column) { var _a; if (column.defaultConstraint != null && ((_a = this.dialect) === null || _a === void 0 ? void 0 : _a.namedDefaultConstraint)) { this.put(' ^constraint %i ^default %s ', column.defaultConstraint, column.defaultValue); } else { this.put(' ^default %s ', column.defaultValue); } } putCollection(delimiter, collection, lambda) { if (!collection) return; let first = true; for (const item of collection) { if (!first) this.put(delimiter); first = false; lambda(item); } } createTable(table) { this.put('^create ^table %f ( &>&n', table); this.putCollection(',&n', table.columns, col => { this.put('%i ', col.columnName); this.columnDefinition(col); }); this.createTablePrimaryKeyCore(table); (table.foreignKeys || []).forEach(fk => { this.put(',&n'); this.createForeignKeyFore(fk); }); (table.uniques || []).forEach(uq => { this.put(',&n'); this.createUniqueCore(uq); }); (table.checks || []).forEach(chk => { this.put(',&n'); this.createCheckCore(chk); }); this.put('&<&n)'); this.tableOptions(table); this.endCommand(); (table.indexes || []).forEach(ix => { this.createIndex(ix); }); } tableOptions(table) { var _a, _b, _c; const options = ((_c = (_b = (_a = this.driver) === null || _a === void 0 ? void 0 : _a.dialect) === null || _b === void 0 ? void 0 : _b.getTableFormOptions) === null || _c === void 0 ? void 0 : _c.call(_b, 'sqlCreateTable')) || []; for (const option of options) { if (table[option.name]) { this.put('&n'); this.put(option.sqlFormatString, table[option.name]); } } } createTablePrimaryKeyCore(table) { if (table.primaryKey) { this.put(',&n'); if (table.primaryKey.constraintName && !this.dialect.anonymousPrimaryKey) { this.put('^constraint %i', table.primaryKey.constraintName); } this.put(' ^primary ^key (%,i)', table.primaryKey.columns.map(x => x.columnName)); } } createForeignKeyFore(fk) { if (fk.constraintName != null && !this.dialect.anonymousForeignKey) { this.put('^constraint %i ', fk.constraintName); } this.put('^foreign ^key (%,i) ^references %f (%,i)', fk.columns.map(x => x.columnName), { schemaName: fk.refSchemaName, pureName: fk.refTableName }, fk.columns.map(x => x.refColumnName)); if (fk.deleteAction) this.put(' ^on ^delete %k', fk.deleteAction); if (fk.updateAction) this.put(' ^on ^update %k', fk.updateAction); } transform(type, dumpExpr) { dumpExpr(); } allowIdentityInsert(table, allow) { } enableConstraints(table, enabled) { } enableAllForeignKeys(enabled) { } comment(value) { if (!value) return; for (const line of value.split('\n')) { this.put(' -- %s', line.trimRight()); } } createView(obj) { this.putRaw(obj.createSql); this.endCommand(); } dropView(obj, { testIfExists = false }) { this.putCmd('^drop ^view %f', obj); } alterView(obj) { this.putRaw(obj.createSql.replace(/create\s+view/i, 'ALTER VIEW')); this.endCommand(); } changeViewSchema(obj, newSchema) { } renameView(obj, newSchema) { } createMatview(obj) { this.putRaw(obj.createSql); this.endCommand(); } dropMatview(obj, { testIfExists = false }) { this.putCmd('^drop ^materialized ^view %f', obj); } alterMatview(obj) { this.putRaw(obj.createSql.replace(/create\s+view/i, 'ALTER VIEW')); this.endCommand(); } changeMatviewSchema(obj, newSchema) { } renameMatview(obj, newSchema) { } createProcedure(obj) { this.putRaw(obj.createSql); this.endCommand(); } dropProcedure(obj, { testIfExists = false }) { this.putCmd('^drop ^procedure %f', obj); } alterProcedure(obj) { this.putRaw(obj.createSql.replace(/create\s+procedure/i, 'ALTER PROCEDURE')); this.endCommand(); } changeProcedureSchema(obj, newSchema) { } renameProcedure(obj, newSchema) { } createFunction(obj) { this.putRaw(obj.createSql); this.endCommand(); } dropFunction(obj, { testIfExists = false }) { this.putCmd('^drop ^function %f', obj); } alterFunction(obj) { this.putRaw(obj.createSql.replace(/create\s+function/i, 'ALTER FUNCTION')); this.endCommand(); } changeFunctionSchema(obj, newSchema) { } renameFunction(obj, newSchema) { } createTrigger(obj) { this.putRaw(obj.createSql); this.endCommand(); } dropTrigger(obj, { testIfExists = false }) { this.putCmd('^drop ^trigger %f', obj); } alterTrigger(obj) { this.putRaw(obj.createSql.replace(/create\s+trigger/i, 'ALTER TRIGGER')); this.endCommand(); } changeTriggerSchema(obj, newSchema) { } renameTrigger(obj, newSchema) { } createSchedulerEvent(obj) { this.putRaw(obj.createSql); this.endCommand(); } dropSchedulerEvent(obj, { testIfExists = false }) { this.putCmd('^drop ^event %f', obj); } dropConstraintCore(cnt) { this.putCmd('^alter ^table %f ^drop ^constraint %i', cnt, cnt.constraintName); } dropConstraint(cnt) { switch (cnt.constraintType) { case 'primaryKey': this.dropPrimaryKey(cnt); break; case 'foreignKey': this.dropForeignKey(cnt); break; case 'unique': this.dropUnique(cnt); break; case 'check': this.dropCheck(cnt); break; case 'index': this.dropIndex(cnt); break; } } createConstraint(cnt) { switch (cnt.constraintType) { case 'primaryKey': this.createPrimaryKey(cnt); break; case 'foreignKey': this.createForeignKey(cnt); break; case 'unique': this.createUnique(cnt); break; case 'check': this.createCheck(cnt); break; case 'index': this.createIndex(cnt); break; } } changeConstraint(oldConstraint, newConstraint) { } dropForeignKey(fk) { if (this.dialect.explicitDropConstraint) { this.putCmd('^alter ^table %f ^drop ^foreign ^key %i', fk, fk.constraintName); } else { this.dropConstraintCore(fk); } } createForeignKey(fk) { this.put('^alter ^table %f ^add ', fk); this.createForeignKeyFore(fk); this.endCommand(); } dropPrimaryKey(pk) { if (this.dialect.explicitDropConstraint) { this.putCmd('^alter ^table %f ^drop ^primary ^key', pk); } else { this.dropConstraintCore(pk); } } createPrimaryKey(pk) { this.putCmd('^alter ^table %f ^add ^constraint %i ^primary ^key (%,i)', pk, pk.constraintName, pk.columns.map(x => x.columnName)); } dropIndex(ix) { this.put('^drop ^index %i', ix.constraintName); if (this.dialect.dropIndexContainsTableSpec) { this.put(' ^on %f', ix); } this.endCommand(); } createIndex(ix) { this.put('^create'); if (ix.isUnique) this.put(' ^unique'); this.put(' ^index %i &n^on %f (&>&n', ix.constraintName, ix); this.putCollection(',&n', ix.columns, col => { this.put('%i %k', col.columnName, col.isDescending == true ? 'DESC' : 'ASC'); }); this.put('&<&n)'); if (ix.filterDefinition && this.dialect.filteredIndexes) { this.put('&n^where %s', ix.filterDefinition); } this.endCommand(); } dropUnique(uq) { this.dropConstraintCore(uq); } createUniqueCore(uq) { this.put('^constraint %i ^unique (%,i)', uq.constraintName, uq.columns.map(x => x.columnName)); } createUnique(uq) { this.put('^alter ^table %f ^add ', uq); this.createUniqueCore(uq); this.endCommand(); } dropCheck(ch) { this.dropConstraintCore(ch); } createCheckCore(ch) { this.put('^constraint %i ^check (%s)', ch.constraintName, ch.definition); } createCheck(ch) { this.put('^alter ^table %f ^add ', ch); this.createCheckCore(ch); this.endCommand(); } renameConstraint(constraint, newName) { } createColumn(column, constraints) { this.put('^alter ^table %f ^add ', column); if (this.dialect.createColumnWithColumnKeyword) this.put('^column '); this.put(' %i ', column.columnName); this.columnDefinition(column); this.inlineConstraints(constraints); this.endCommand(); } inlineConstraints(constrains) { if (constrains == null) return; for (const cnt of constrains) { if (cnt.constraintType == 'primaryKey') { if (cnt.constraintName != null && !this.dialect.anonymousPrimaryKey) { this.put(' ^constraint %i', cnt.constraintName); } this.put(' ^primary ^key '); } } } dropColumn(column) { this.putCmd('^alter ^table %f ^drop ^column %i', column, column.columnName); } renameColumn(column, newName) { } changeColumn(oldcol, newcol, constraints) { } dropTable(obj, { testIfExists = false } = {}) { this.putCmd('^drop ^table %f', obj); } changeTableSchema(obj, schema) { } renameTable(obj, newname) { } renameSqlObject(obj, newname) { } beginTransaction() { this.putCmd('^begin ^transaction'); } commitTransaction() { this.putCmd('^commit'); } rollbackTransaction() { this.putCmd('^rollback'); } alterProlog() { } alterEpilog() { } selectTableIntoNewTable(sourceName, targetName) { this.putCmd('^select * ^into %f ^from %f', targetName, sourceName); } truncateTable(name) { this.putCmd('^truncate ^table %f', name); } dropConstraints(table, dropReferences = false) { if (dropReferences && this.dialect.dropForeignKey) { table.dependencies.forEach(cnt => this.dropConstraint(cnt)); } if (this.dialect.dropIndex) { table.indexes.forEach(cnt => this.dropIndex(cnt)); } if (this.dialect.dropForeignKey) { table.foreignKeys.forEach(cnt => this.dropForeignKey(cnt)); } if (this.dialect.dropPrimaryKey && table.primaryKey) { this.dropPrimaryKey(table.primaryKey); } } recreateTable(oldTable, newTable) { if (!oldTable.pairingId || !newTable.pairingId || oldTable.pairingId != newTable.pairingId) { throw new Error('Recreate is not possible: oldTable.paringId != newTable.paringId'); } const tmpTable = `temp_${(0, v1_1.default)()}`; const columnPairs = oldTable.columns .map(oldcol => ({ oldcol, newcol: newTable.columns.find(x => x.pairingId == oldcol.pairingId), })) .filter(x => x.newcol); if (this.driver.supportsTransactions) { this.dropConstraints(oldTable, true); this.renameTable(oldTable, tmpTable); this.createTable(newTable); const autoinc = newTable.columns.find(x => x.autoIncrement); if (autoinc) { this.allowIdentityInsert(newTable, true); } this.putCmd('^insert ^into %f (%,i) select %,i ^from %f', newTable, columnPairs.map(x => x.newcol.columnName), columnPairs.map(x => x.oldcol.columnName), { ...oldTable, pureName: tmpTable }); if (autoinc) { this.allowIdentityInsert(newTable, false); } if (this.dialect.dropForeignKey) { newTable.dependencies.forEach(cnt => this.createConstraint(cnt)); } this.dropTable({ ...oldTable, pureName: tmpTable }); } else { // we have to preserve old table as long as possible this.createTable({ ...newTable, pureName: tmpTable }); this.putCmd('^insert ^into %f (%,i) select %,s ^from %f', { ...newTable, pureName: tmpTable }, columnPairs.map(x => x.newcol.columnName), columnPairs.map(x => x.oldcol.columnName), oldTable); this.dropTable(oldTable); this.renameTable({ ...newTable, pureName: tmpTable }, newTable.pureName); } } createSqlObject(obj) { this.putRaw(obj.createSql); this.endCommand(); } getSqlObjectSqlName(ojectTypeField) { switch (ojectTypeField) { case 'procedures': return 'PROCEDURE'; case 'views': return 'VIEW'; case 'functions': return 'FUNCTION'; case 'triggers': return 'TRIGGER'; case 'matviews': return 'MATERIALIZED VIEW'; case 'schedulerEvents': return 'EVENT'; } } dropSqlObject(obj) { this.putCmd('^drop %s %f', this.getSqlObjectSqlName(obj.objectTypeField), obj); } setTableOption(table, optionName, optionValue) { var _a, _b; const options = (_b = (_a = this === null || this === void 0 ? void 0 : this.dialect) === null || _a === void 0 ? void 0 : _a.getTableFormOptions) === null || _b === void 0 ? void 0 : _b.call(_a, 'sqlAlterTable'); const option = options === null || options === void 0 ? void 0 : options.find(x => x.name == optionName && !x.disabled); if (!option) { return; } this.setTableOptionCore(table, optionName, optionValue, option.sqlFormatString); this.endCommand(); } setTableOptionCore(table, optionName, optionValue, formatString) { this.put('^alter ^table %f ', table); this.put(formatString, optionValue); } fillNewNotNullDefaults(col) { if (col.notNull && col.defaultValue != null) { this.putCmd('^update %f ^set %i = %s ^where %i ^is ^null', col, col.columnName, col.defaultValue, col.columnName); } } fillPreloadedRows(table, oldRows, newRows, key, insertOnly, autoIncrementColumn) { let was = false; for (const row of newRows) { const old = oldRows === null || oldRows === void 0 ? void 0 : oldRows.find(r => key.every(col => r[col] == row[col])); const rowKeys = (0, keys_1.default)(row); if (old) { const updated = []; for (const col of rowKeys) { if (row[col] != old[col] && !(insertOnly === null || insertOnly === void 0 ? void 0 : insertOnly.includes(col))) { updated.push(col); } } if (updated.length > 0) { if (was) this.put(';\n'); was = true; this.put('^update %f ^set ', table); this.putCollection(', ', updated, col => this.put('%i=%v', col, row[col])); this.put(' ^where '); this.putCollection(' ^and ', key, col => this.put('%i=%v', col, row[col])); } } else { if (was) this.put(';\n'); was = true; const autoinc = rowKeys.includes(autoIncrementColumn); if (autoinc) this.allowIdentityInsert(table, true); this.put('^insert ^into %f (%,i) ^values (%,v)', table, rowKeys, rowKeys.map(x => row[x])); if (autoinc) this.allowIdentityInsert(table, false); } } if (was) { this.endCommand(); } } callableTemplate(func) { this.put('^call %f(&>&n', func); this.putCollection(',&n', func.parameters || [], param => { this.putRaw(param.parameterMode == 'IN' ? ':' + param.parameterName : param.parameterName); }); this.put('&<&n)'); this.endCommand(); } } exports.SqlDumper = SqlDumper; SqlDumper.keywordsCase = 'upperCase';