dbgate/packages/engines/mssql/index.js

230 lines
6.0 KiB
JavaScript
Raw Normal View History

2020-03-24 18:47:53 +00:00
const _ = require('lodash');
const MsSqlAnalyser = require('./MsSqlAnalyser');
const MsSqlDumper = require('./MsSqlDumper');
2020-06-11 11:58:34 +00:00
const createBulkInsertStream = require('./createBulkInsertStream');
const { analyseSingleObject } = require('../mysql');
2020-01-04 20:59:53 +00:00
2020-02-03 18:52:02 +00:00
/** @type {import('@dbgate/types').SqlDialect} */
2020-02-02 18:27:25 +00:00
const dialect = {
limitSelect: true,
rangeSelect: true,
offsetFetchRangeSyntax: true,
2020-03-12 11:46:07 +00:00
stringEscapeChar: "'",
2020-06-11 11:58:34 +00:00
fallbackDataType: 'nvarchar(max)',
2020-02-02 18:27:25 +00:00
quoteIdentifier(s) {
return `[${s}]`;
2020-03-24 18:47:53 +00:00
},
2020-02-02 18:27:25 +00:00
};
2020-04-07 19:37:00 +00:00
function extractColumns(columns) {
2020-04-13 12:26:14 +00:00
const mapper = {};
const res = _.sortBy(_.values(columns), 'index').map((col) => ({
2020-04-10 07:50:20 +00:00
...col,
columnName: col.name,
notNull: !col.nullable,
autoIncrement: !!col.identity,
}));
2020-04-13 12:26:14 +00:00
const generateName = () => {
let index = 1;
while (res.find((x) => x.columnName == `col${index}`)) index += 1;
return `col${index}`;
};
// const groups = _.groupBy(res, 'columnName');
// for (const colname of _.keys(groups)) {
// if (groups[colname].length == 1) continue;
// mapper[colname] = [];
// for (const col of groups[colname]) {
// col.columnName = generateName();
// mapper[colname].push(colname);
// }
// }
for (const col of res) {
if (!col.columnName) {
const newName = generateName();
mapper[col.columnName] = newName;
col.columnName = newName;
}
}
return [res, mapper];
2020-04-07 19:37:00 +00:00
}
2020-02-03 18:52:02 +00:00
/** @type {import('@dbgate/types').EngineDriver} */
2020-02-02 18:27:25 +00:00
const driver = {
2020-02-03 19:34:38 +00:00
async connect(nativeModules, { server, port, user, password, database }) {
2020-06-18 15:11:08 +00:00
const pool = new nativeModules.mssql.ConnectionPool({
2020-02-02 21:27:49 +00:00
server,
port,
user,
password,
2020-03-05 10:42:16 +00:00
database,
2020-04-13 13:20:37 +00:00
requestTimeout: 1000 * 3600,
2020-03-05 10:42:16 +00:00
options: {
2020-03-24 18:47:53 +00:00
enableArithAbort: true,
},
2020-02-02 21:27:49 +00:00
});
2020-06-18 15:11:08 +00:00
await pool.connect();
2020-02-03 19:34:38 +00:00
pool._nativeModules = nativeModules;
2020-01-04 20:59:53 +00:00
return pool;
},
2020-04-13 12:26:14 +00:00
// @ts-ignore
2020-01-04 20:59:53 +00:00
async query(pool, sql) {
2020-06-11 11:58:34 +00:00
if (sql == null) {
return {
rows: [],
columns: [],
};
}
2020-01-04 20:59:53 +00:00
const resp = await pool.request().query(sql);
2020-01-25 16:26:51 +00:00
// console.log(Object.keys(resp.recordset));
2020-03-24 18:47:53 +00:00
// console.log(resp);
const res = {};
if (resp.recordset) {
2020-04-13 12:26:14 +00:00
const [columns] = extractColumns(resp.recordset.columns);
res.columns = columns;
2020-03-24 18:47:53 +00:00
res.rows = resp.recordset;
}
if (resp.rowsAffected) {
res.rowsAffected = _.sum(resp.rowsAffected);
}
return res;
2020-01-04 20:59:53 +00:00
},
async stream(pool, sql, options) {
const request = await pool.request();
2020-04-13 12:26:14 +00:00
let currentMapper = null;
const handleInfo = (info) => {
const { message, lineNumber, procName } = info;
options.info({
message,
line: lineNumber,
procedure: procName,
time: new Date(),
2020-04-10 11:49:41 +00:00
severity: 'info',
});
};
const handleDone = (result) => {
2020-04-07 19:37:00 +00:00
// console.log('RESULT', result);
options.done(result);
};
const handleRow = (row) => {
2020-04-13 12:26:14 +00:00
// if (currentMapper) {
// for (const colname of _.keys(currentMapper)) {
// let index = 0;
// for (const newcolname of currentMapper[colname]) {
// row[newcolname] = row[colname][index];
// index += 1;
// }
// delete row[colname];
// }
// }
if (currentMapper) {
row = { ...row };
for (const colname of _.keys(currentMapper)) {
const newcolname = currentMapper[colname];
row[newcolname] = row[colname];
if (_.isArray(row[newcolname])) row[newcolname] = row[newcolname].join(',');
delete row[colname];
}
}
2020-04-07 19:37:00 +00:00
options.row(row);
};
const handleRecordset = (columns) => {
2020-04-13 12:26:14 +00:00
const [extractedColumns, mapper] = extractColumns(columns);
currentMapper = mapper;
options.recordset(extractedColumns);
};
2020-04-10 11:49:41 +00:00
const handleError = (error) => {
const { message, lineNumber, procName } = error;
options.info({
message,
line: lineNumber,
procedure: procName,
time: new Date(),
severity: 'error',
});
};
request.stream = true;
2020-04-07 19:37:00 +00:00
request.on('recordset', handleRecordset);
request.on('row', handleRow);
2020-04-10 11:49:41 +00:00
request.on('error', handleError);
request.on('done', handleDone);
request.on('info', handleInfo);
request.query(sql);
2020-04-13 13:20:37 +00:00
return request;
},
2020-06-11 08:23:37 +00:00
async readQuery(pool, sql) {
const request = await pool.request();
const { stream } = pool._nativeModules;
const pass = new stream.PassThrough({
objectMode: true,
highWaterMark: 100,
});
request.stream = true;
2020-06-11 08:09:04 +00:00
request.on('recordset', (driverColumns) => {
const [columns, mapper] = extractColumns(driverColumns);
pass.write({ columns });
});
request.on('row', (row) => pass.write(row));
request.on('error', (err) => {
console.error(err);
pass.end();
});
request.on('done', () => pass.end());
request.query(sql);
return pass;
},
2020-06-11 11:58:34 +00:00
async writeTable(pool, name, options) {
const { stream, mssql } = pool._nativeModules;
return createBulkInsertStream(this, mssql, stream, pool, name, options);
},
2020-01-04 20:59:53 +00:00
async getVersion(pool) {
2020-03-24 18:47:53 +00:00
const { version } = (await this.query(pool, 'SELECT @@VERSION AS version')).rows[0];
2020-01-04 20:59:53 +00:00
return { version };
},
async listDatabases(pool) {
2020-03-24 18:47:53 +00:00
const { rows } = await this.query(pool, 'SELECT name FROM sys.databases order by name');
2020-01-25 16:26:51 +00:00
return rows;
2020-01-04 20:59:53 +00:00
},
async analyseFull(pool) {
2020-01-19 20:01:48 +00:00
const analyser = new MsSqlAnalyser(pool, this);
2020-04-12 08:16:33 +00:00
return analyser.fullAnalysis();
2020-04-11 18:24:30 +00:00
},
2020-06-11 11:58:34 +00:00
async analyseSingleObject(pool, name, typeField = 'tables') {
const analyser = new MsSqlAnalyser(pool, this);
analyser.singleObjectFilter = { name, typeField };
const res = await analyser.fullAnalysis();
return res.tables[0];
},
// @ts-ignore
analyseSingleTable(pool, name) {
return this.analyseSingleObject(pool, name, 'tables');
},
2020-04-11 18:24:30 +00:00
async analyseIncremental(pool, structure) {
const analyser = new MsSqlAnalyser(pool, this);
2020-04-12 08:16:33 +00:00
return analyser.incrementalAnalysis(structure);
},
2020-02-02 18:27:25 +00:00
createDumper() {
return new MsSqlDumper(this);
},
2020-03-23 19:41:40 +00:00
dialect,
engine: 'mssql',
2020-01-04 20:59:53 +00:00
};
2020-02-02 18:27:25 +00:00
module.exports = driver;