dbgate/packages/engines/default/SqlDumper.js

256 lines
6.4 KiB
JavaScript
Raw Normal View History

2020-03-12 11:46:07 +00:00
const _ = require('lodash');
const moment = require('moment');
2020-02-02 18:27:25 +00:00
class SqlDumper {
2020-02-03 18:52:02 +00:00
/** @param driver {import('@dbgate/types').EngineDriver} */
2020-02-02 18:27:25 +00:00
constructor(driver) {
2020-03-12 11:46:07 +00:00
this.s = '';
2020-02-02 18:27:25 +00:00
this.driver = driver;
this.dialect = driver.dialect;
2020-03-02 21:08:06 +00:00
this.indentLevel = 0;
}
endCommand() {
2020-03-12 11:46:07 +00:00
this.putRaw(';\n');
2020-02-02 18:27:25 +00:00
}
putRaw(text) {
this.s += text;
}
2020-03-12 11:46:07 +00:00
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("'");
}
putValue(value) {
if (_.isString(value)) this.putStringValue(value);
if (_.isNumber(value)) this.putRaw(value.toString());
if (_.isDate(value)) this.putStringValue(moment(value).toISOString());
}
2020-02-03 19:34:38 +00:00
putCmd(format, ...args) {
this.put(format, ...args);
2020-03-02 21:08:06 +00:00
this.endCommand();
2020-02-02 18:27:25 +00:00
}
putFormattedValue(c, value) {
switch (c) {
2020-03-12 11:46:07 +00:00
case 's':
2020-02-02 18:27:25 +00:00
if (value != null) {
this.putRaw(value.toString());
}
break;
2020-03-12 11:46:07 +00:00
case 'i':
2020-03-02 21:08:06 +00:00
{
this.putRaw(this.dialect.quoteIdentifier(value));
}
break;
2020-03-12 11:46:07 +00:00
case 'k':
2020-03-03 06:14:12 +00:00
{
if (value) {
this.putRaw(value.toUpperCase());
}
2020-03-03 06:14:12 +00:00
}
break;
2020-03-12 11:46:07 +00:00
case 'f':
2020-02-02 18:27:25 +00:00
{
const { schemaName, pureName } = value;
if (schemaName) {
this.putRaw(this.dialect.quoteIdentifier(schemaName));
2020-03-12 11:46:07 +00:00
this.putRaw('.');
2020-02-02 18:27:25 +00:00
}
this.putRaw(this.dialect.quoteIdentifier(pureName));
}
break;
2020-03-12 11:46:07 +00:00
case 'v':
this.putValue(value);
break;
2020-02-02 18:27:25 +00:00
}
}
2020-03-02 21:22:44 +00:00
putFormattedList(c, collection) {
if (!collection) return;
2020-03-12 11:46:07 +00:00
this.putCollection(', ', collection, item => this.putFormattedValue(c, item));
2020-03-02 21:22:44 +00:00
}
2020-02-02 18:27:25 +00:00
/** @param format {string} */
put(format, ...args) {
let i = 0;
let argIndex = 0;
const length = format.length;
while (i < length) {
let c = format[i];
i++;
switch (c) {
2020-03-12 11:46:07 +00:00
case '^':
2020-03-02 21:08:06 +00:00
while (i < length && format[i].match(/[a-z0-9_]/i)) {
2020-02-02 18:27:25 +00:00
this.putRaw(format[i].toUpperCase());
i++;
}
break;
2020-03-12 11:46:07 +00:00
case '%':
2020-02-02 18:27:25 +00:00
c = format[i];
i++;
2020-03-02 21:22:44 +00:00
switch (c) {
2020-03-12 11:46:07 +00:00
case '%':
this.putRaw('%');
2020-03-02 21:22:44 +00:00
break;
2020-03-12 11:46:07 +00:00
case ',':
2020-03-02 21:22:44 +00:00
c = format[i];
i++;
this.putFormattedList(c, args[argIndex]);
break;
default:
this.putFormattedValue(c, args[argIndex]);
break;
}
2020-02-02 18:27:25 +00:00
argIndex++;
break;
2020-03-12 11:46:07 +00:00
case '&':
2020-03-02 21:08:06 +00:00
c = format[i];
i++;
switch (c) {
2020-03-12 11:46:07 +00:00
case '&':
this.putRaw('&');
2020-03-02 21:08:06 +00:00
break;
2020-03-12 11:46:07 +00:00
case '>':
2020-03-02 21:08:06 +00:00
this.indentLevel++;
break;
2020-03-12 11:46:07 +00:00
case '<':
2020-03-02 21:08:06 +00:00
this.indentLevel--;
break;
2020-03-12 11:46:07 +00:00
case 'n':
this.putRaw('\n');
this.putRaw(' '.repeat(2 * this.indentLevel));
2020-03-02 21:08:06 +00:00
break;
}
break;
2020-02-02 18:27:25 +00:00
default:
this.putRaw(c);
break;
}
}
}
2020-03-02 21:08:06 +00:00
autoIncrement() {
2020-03-12 11:46:07 +00:00
this.put(' ^auto_increment');
2020-03-02 21:08:06 +00:00
}
/**
* @param column {import('@dbgate/types').ColumnInfo}
*/
2020-03-12 11:46:07 +00:00
columnDefinition(column, { includeDefault = true, includeNullable = true, includeCollate = true } = {}) {
2020-03-02 21:08:06 +00:00
if (column.computedExpression) {
2020-03-12 11:46:07 +00:00
this.put('^as %s', column.computedExpression);
if (column.isPersisted) this.put(' ^persisted');
2020-03-02 21:08:06 +00:00
return;
}
2020-03-12 11:46:07 +00:00
this.put('%k', column.dataType);
2020-03-02 21:08:06 +00:00
if (column.autoIncrement) {
this.autoIncrement();
}
2020-03-12 11:46:07 +00:00
this.putRaw(' ');
2020-03-02 21:08:06 +00:00
if (column.isSparse) {
2020-03-12 11:46:07 +00:00
this.put(' ^sparse ');
2020-03-02 21:08:06 +00:00
}
if (includeNullable) {
2020-03-12 11:46:07 +00:00
this.put(column.notNull ? '^not ^null' : '^null');
2020-03-02 21:08:06 +00:00
}
if (includeDefault && column.defaultValue != null) {
this.columnDefault(column);
}
}
/**
* @param column {import('@dbgate/types').ColumnInfo}
*/
columnDefault(column) {
if (column.defaultConstraint != null) {
2020-03-12 11:46:07 +00:00
this.put(' ^constraint %i ^default %s ', column.defaultConstraint, column.defaultValue);
2020-03-02 21:08:06 +00:00
} else {
2020-03-12 11:46:07 +00:00
this.put(' ^default %s ', column.defaultValue);
2020-03-02 21:08:06 +00:00
}
}
/**
* @template T
* @param {string} delimiter
* @param {T[]} collection
* @param {(col: T) => void} lambda
*/
putCollection(delimiter, collection, lambda) {
2020-03-05 08:44:38 +00:00
if (!collection) return;
2020-03-02 21:08:06 +00:00
let first = true;
for (const item of collection) {
if (!first) this.put(delimiter);
first = false;
lambda(item);
}
}
/** @param table {import('@dbgate/types').TableInfo} */
createTable(table) {
2020-03-12 11:46:07 +00:00
this.put('^create ^table %f ( &>&n', table);
this.putCollection(',&n', table.columns, col => {
this.put('%i ', col.columnName);
2020-03-02 21:08:06 +00:00
this.columnDefinition(col);
});
2020-03-02 21:22:44 +00:00
if (table.primaryKey) {
2020-03-12 11:46:07 +00:00
this.put(',&n');
2020-03-02 21:22:44 +00:00
if (table.primaryKey.constraintName) {
2020-03-12 11:46:07 +00:00
this.put('^constraint %i', table.primaryKey.constraintName);
2020-03-02 21:22:44 +00:00
}
this.put(
2020-03-12 11:46:07 +00:00
' ^primary ^key (%,i)',
2020-03-02 21:22:44 +00:00
table.primaryKey.columns.map(x => x.columnName)
);
}
2020-03-05 08:44:38 +00:00
if (table.foreignKeys) {
table.foreignKeys.forEach(fk => {
2020-03-12 11:46:07 +00:00
this.put(',&n');
2020-03-05 08:44:38 +00:00
this.createForeignKeyFore(fk);
});
}
2020-03-02 21:08:06 +00:00
// foreach (var cnt in table.Uniques)
// {
2020-03-03 06:14:12 +00:00
// if (!first) this.put(", &n");
2020-03-02 21:08:06 +00:00
// first = false;
// CreateUniqueCore(cnt);
// }
// foreach (var cnt in table.Checks)
// {
2020-03-03 06:14:12 +00:00
// if (!first) this.put(", &n");
2020-03-02 21:08:06 +00:00
// first = false;
// CreateCheckCore(cnt);
// }
2020-03-12 11:46:07 +00:00
this.put('&<&n)');
2020-03-02 21:08:06 +00:00
this.endCommand();
// foreach (var ix in table.Indexes)
// {
// CreateIndex(ix);
// }
}
2020-03-03 06:14:12 +00:00
/** @param fk {import('@dbgate/types').ForeignKeyInfo} */
createForeignKeyFore(fk) {
2020-03-12 11:46:07 +00:00
if (fk.constraintName != null) this.put('^constraint %i ', fk.constraintName);
2020-03-03 06:14:12 +00:00
this.put(
2020-03-12 11:46:07 +00:00
'^foreign ^key (%,i) ^references %f (%,i)',
2020-03-03 06:14:12 +00:00
fk.columns.map(x => x.columnName),
{ schemaName: fk.refSchemaName, pureName: fk.refTableName },
fk.columns.map(x => x.refColumnName)
);
2020-03-12 11:46:07 +00:00
if (fk.deleteAction) this.put(' ^on ^delete %k', fk.deleteAction);
if (fk.updateAction) this.put(' ^on ^update %k', fk.updateAction);
2020-03-03 06:14:12 +00:00
}
2020-02-02 18:27:25 +00:00
}
module.exports = SqlDumper;