mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
query splitter refactor
This commit is contained in:
parent
a76e742ce6
commit
0c48a5ee09
@ -1,4 +1,5 @@
|
|||||||
const engines = require('../engines');
|
const engines = require('../engines');
|
||||||
|
const { splitQuery } = require('dbgate-query-splitter');
|
||||||
const { testWrapper } = require('../tools');
|
const { testWrapper } = require('../tools');
|
||||||
|
|
||||||
const initSql = ['CREATE TABLE t1 (id int)', 'INSERT INTO t1 (id) VALUES (1)', 'INSERT INTO t1 (id) VALUES (2)'];
|
const initSql = ['CREATE TABLE t1 (id int)', 'INSERT INTO t1 (id) VALUES (1)', 'INSERT INTO t1 (id) VALUES (2)'];
|
||||||
@ -43,13 +44,22 @@ class StreamHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeStream(driver, conn, sql) {
|
function executeStreamItem(driver, conn, sql) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const handler = new StreamHandler(resolve);
|
const handler = new StreamHandler(resolve);
|
||||||
driver.stream(conn, sql, handler);
|
driver.stream(conn, sql, handler);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function executeStream(driver, conn, sql) {
|
||||||
|
const results = [];
|
||||||
|
for (const sqlItem of splitQuery(driver.getQuerySplitterOptions('stream'))) {
|
||||||
|
const item = await executeStreamItem(driver, conn, sqlItem);
|
||||||
|
results.push(...item);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
describe('Query', () => {
|
describe('Query', () => {
|
||||||
test.each(engines.map(engine => [engine.label, engine]))(
|
test.each(engines.map(engine => [engine.label, engine]))(
|
||||||
'Simple query - %s',
|
'Simple query - %s',
|
||||||
@ -129,7 +139,7 @@ describe('Query', () => {
|
|||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql);
|
for (const sql of initSql) await driver.query(conn, sql);
|
||||||
|
|
||||||
await driver.query(
|
await driver.script(
|
||||||
conn,
|
conn,
|
||||||
'INSERT INTO t1 (id) VALUES (3);INSERT INTO t1 (id) VALUES (4);UPDATE t1 SET id=10 WHERE id=1;DELETE FROM t1 WHERE id=2;'
|
'INSERT INTO t1 (id) VALUES (3);INSERT INTO t1 (id) VALUES (4);UPDATE t1 SET id=10 WHERE id=1;DELETE FROM t1 WHERE id=2;'
|
||||||
);
|
);
|
||||||
|
@ -9,7 +9,7 @@ services:
|
|||||||
- 15000:5432
|
- 15000:5432
|
||||||
|
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql
|
image: mysql:8.0.18
|
||||||
command: --default-authentication-plugin=mysql_native_password
|
command: --default-authentication-plugin=mysql_native_password
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
|
@ -112,6 +112,14 @@ module.exports = {
|
|||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
runScript_meta: 'post',
|
||||||
|
async runScript({ conid, database, sql }) {
|
||||||
|
console.log(`Processing script, conid=${conid}, database=${database}, sql=${sql}`);
|
||||||
|
const opened = await this.ensureOpened(conid, database);
|
||||||
|
const res = await this.sendRequest(opened, { msgtype: 'runScript', sql });
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
|
||||||
collectionData_meta: 'post',
|
collectionData_meta: 'post',
|
||||||
async collectionData({ conid, database, options }) {
|
async collectionData({ conid, database, options }) {
|
||||||
const opened = await this.ensureOpened(conid, database);
|
const opened = await this.ensureOpened(conid, database);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const stableStringify = require('json-stable-stringify');
|
const stableStringify = require('json-stable-stringify');
|
||||||
|
const { splitQuery } = require('dbgate-query-splitter');
|
||||||
const childProcessChecker = require('../utility/childProcessChecker');
|
const childProcessChecker = require('../utility/childProcessChecker');
|
||||||
const { extractBoolSettingsValue, extractIntSettingsValue } = require('dbgate-tools');
|
const { extractBoolSettingsValue, extractIntSettingsValue } = require('dbgate-tools');
|
||||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||||
@ -52,7 +53,7 @@ async function handleIncrementalRefresh(forceSend) {
|
|||||||
if (newStructure != null) {
|
if (newStructure != null) {
|
||||||
analysedStructure = newStructure;
|
analysedStructure = newStructure;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forceSend || newStructure != null) {
|
if (forceSend || newStructure != null) {
|
||||||
process.send({ msgtype: 'structure', structure: analysedStructure });
|
process.send({ msgtype: 'structure', structure: analysedStructure });
|
||||||
}
|
}
|
||||||
@ -120,6 +121,17 @@ function waitConnected() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleRunScript({ msgid, sql }) {
|
||||||
|
await waitConnected();
|
||||||
|
const driver = requireEngineDriver(storedConnection);
|
||||||
|
try {
|
||||||
|
await driver.script(sql);
|
||||||
|
process.send({ msgtype: 'response', msgid });
|
||||||
|
} catch (err) {
|
||||||
|
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleQueryData({ msgid, sql }) {
|
async function handleQueryData({ msgid, sql }) {
|
||||||
await waitConnected();
|
await waitConnected();
|
||||||
const driver = requireEngineDriver(storedConnection);
|
const driver = requireEngineDriver(storedConnection);
|
||||||
@ -188,6 +200,7 @@ function handlePing() {
|
|||||||
const messageHandlers = {
|
const messageHandlers = {
|
||||||
connect: handleConnect,
|
connect: handleConnect,
|
||||||
queryData: handleQueryData,
|
queryData: handleQueryData,
|
||||||
|
runScript: handleRunScript,
|
||||||
updateCollection: handleUpdateCollection,
|
updateCollection: handleUpdateCollection,
|
||||||
collectionData: handleCollectionData,
|
collectionData: handleCollectionData,
|
||||||
sqlPreview: handleSqlPreview,
|
sqlPreview: handleSqlPreview,
|
||||||
|
@ -3,7 +3,7 @@ const path = require('path');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const childProcessChecker = require('../utility/childProcessChecker');
|
const childProcessChecker = require('../utility/childProcessChecker');
|
||||||
const goSplit = require('../utility/goSplit');
|
const { splitQuery } = require('dbgate-query-splitter');
|
||||||
|
|
||||||
const { jsldir } = require('../utility/directories');
|
const { jsldir } = require('../utility/directories');
|
||||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||||
@ -166,7 +166,7 @@ async function handleExecuteQuery({ sql }) {
|
|||||||
const resultIndexHolder = {
|
const resultIndexHolder = {
|
||||||
value: 0,
|
value: 0,
|
||||||
};
|
};
|
||||||
for (const sqlItem of goSplit(sql)) {
|
for (const sqlItem of splitQuery(sql, driver.getQuerySplitterOptions('stream'))) {
|
||||||
await handleStream(driver, resultIndexHolder, sqlItem);
|
await handleStream(driver, resultIndexHolder, sqlItem);
|
||||||
// const handler = new StreamHandler(resultIndex);
|
// const handler = new StreamHandler(resultIndex);
|
||||||
// const stream = await driver.stream(systemConnection, sqlItem, handler);
|
// const stream = await driver.stream(systemConnection, sqlItem, handler);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
const goSplit = require('../utility/goSplit');
|
const { splitQuery } = require('dbgate-query-splitter');
|
||||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||||
const { decryptConnection } = require('../utility/crypting');
|
|
||||||
const connectUtility = require('../utility/connectUtility');
|
const connectUtility = require('../utility/connectUtility');
|
||||||
|
|
||||||
async function executeQuery({ connection, sql }) {
|
async function executeQuery({ connection, sql }) {
|
||||||
@ -10,9 +9,9 @@ async function executeQuery({ connection, sql }) {
|
|||||||
const pool = await connectUtility(driver, connection);
|
const pool = await connectUtility(driver, connection);
|
||||||
console.log(`Connected.`);
|
console.log(`Connected.`);
|
||||||
|
|
||||||
for (const sqlItem of goSplit(sql)) {
|
for (const sqlItem of splitQuery(sql, driver.getQuerySplitterOptions('script'))) {
|
||||||
console.log('Executing query', sqlItem);
|
console.log('Executing query', sqlItem);
|
||||||
await driver.query(pool, sqlItem);
|
await driver.query(pool, sqlItem, { discardResult: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Query finished`);
|
console.log(`Query finished`);
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
function goSplit(sql) {
|
|
||||||
if (!sql) return [];
|
|
||||||
const lines = sql.split('\n');
|
|
||||||
const res = [];
|
|
||||||
let buffer = '';
|
|
||||||
for (const line of lines) {
|
|
||||||
if (/^\s*go\s*$/i.test(line)) {
|
|
||||||
if (buffer.trim()) res.push(buffer);
|
|
||||||
buffer = '';
|
|
||||||
} else {
|
|
||||||
buffer += line + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (buffer.trim()) res.push(buffer);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = goSplit;
|
|
@ -7,6 +7,7 @@ export interface SplitterOptions {
|
|||||||
allowCustomDelimiter: boolean;
|
allowCustomDelimiter: boolean;
|
||||||
allowGoDelimiter: boolean;
|
allowGoDelimiter: boolean;
|
||||||
allowDollarDollarString: boolean;
|
allowDollarDollarString: boolean;
|
||||||
|
noSplit: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultSplitterOptions: SplitterOptions = {
|
export const defaultSplitterOptions: SplitterOptions = {
|
||||||
@ -18,6 +19,7 @@ export const defaultSplitterOptions: SplitterOptions = {
|
|||||||
allowCustomDelimiter: false,
|
allowCustomDelimiter: false,
|
||||||
allowGoDelimiter: false,
|
allowGoDelimiter: false,
|
||||||
allowDollarDollarString: false,
|
allowDollarDollarString: false,
|
||||||
|
noSplit: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mysqlSplitterOptions: SplitterOptions = {
|
export const mysqlSplitterOptions: SplitterOptions = {
|
||||||
@ -48,3 +50,17 @@ export const postgreSplitterOptions: SplitterOptions = {
|
|||||||
stringsEnds: { "'": "'", '"': '"' },
|
stringsEnds: { "'": "'", '"': '"' },
|
||||||
stringEscapes: { "'": "'", '"': '"' },
|
stringEscapes: { "'": "'", '"': '"' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const sqliteSplitterOptions: SplitterOptions = {
|
||||||
|
...defaultSplitterOptions,
|
||||||
|
|
||||||
|
stringsBegins: ["'", '"'],
|
||||||
|
stringsEnds: { "'": "'", '"': '"' },
|
||||||
|
stringEscapes: { "'": "'", '"': '"' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const noSplitSplitterOptions: SplitterOptions = {
|
||||||
|
...defaultSplitterOptions,
|
||||||
|
|
||||||
|
noSplit: true,
|
||||||
|
};
|
||||||
|
@ -162,6 +162,15 @@ function pushQuery(context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function splitQuery(sql: string, options: SplitterOptions = null): string[] {
|
export function splitQuery(sql: string, options: SplitterOptions = null): string[] {
|
||||||
|
const usedOptions = {
|
||||||
|
...defaultSplitterOptions,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usedOptions.noSplit) {
|
||||||
|
return [sql];
|
||||||
|
}
|
||||||
|
|
||||||
const context: SplitExecutionContext = {
|
const context: SplitExecutionContext = {
|
||||||
source: sql,
|
source: sql,
|
||||||
end: sql.length,
|
end: sql.length,
|
||||||
@ -170,10 +179,7 @@ export function splitQuery(sql: string, options: SplitterOptions = null): string
|
|||||||
currentCommandStart: 0,
|
currentCommandStart: 0,
|
||||||
output: [],
|
output: [],
|
||||||
wasDataOnLine: false,
|
wasDataOnLine: false,
|
||||||
options: {
|
options: usedOptions,
|
||||||
...defaultSplitterOptions,
|
|
||||||
...options,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
while (context.position < context.end) {
|
while (context.position < context.end) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { mysqlSplitterOptions, mssqlSplitterOptions, postgreSplitterOptions } from './options';
|
import { mysqlSplitterOptions, mssqlSplitterOptions, postgreSplitterOptions, noSplitSplitterOptions } from './options';
|
||||||
import { splitQuery } from './splitQuery';
|
import { splitQuery } from './splitQuery';
|
||||||
|
|
||||||
test('simple query', () => {
|
test('simple query', () => {
|
||||||
@ -71,3 +71,9 @@ test('go delimiter', () => {
|
|||||||
const output = splitQuery(input, mssqlSplitterOptions);
|
const output = splitQuery(input, mssqlSplitterOptions);
|
||||||
expect(output).toEqual(['SELECT 1', 'SELECT 2']);
|
expect(output).toEqual(['SELECT 1', 'SELECT 2']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('no split', () => {
|
||||||
|
const input = 'SELECT 1;SELECT 2';
|
||||||
|
const output = splitQuery(input, noSplitSplitterOptions);
|
||||||
|
expect(output).toEqual(['SELECT 1;SELECT 2']);
|
||||||
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { SqlDumper } from './SqlDumper';
|
import { SqlDumper } from './SqlDumper';
|
||||||
|
import { splitQuery } from 'dbgate-query-splitter';
|
||||||
|
|
||||||
const dialect = {
|
const dialect = {
|
||||||
limitSelect: true,
|
limitSelect: true,
|
||||||
@ -34,4 +35,9 @@ export const driverBase = {
|
|||||||
createDumper() {
|
createDumper() {
|
||||||
return new this.dumperClass(this);
|
return new this.dumperClass(this);
|
||||||
},
|
},
|
||||||
|
async script(pool, sql) {
|
||||||
|
for (const sqlItem of splitQuery(sql, this.getQuerySplitterOptions('script'))) {
|
||||||
|
await this.query(pool, sqlItem, { discardResult: true });
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
8
packages/types/engines.d.ts
vendored
8
packages/types/engines.d.ts
vendored
@ -12,6 +12,10 @@ export interface StreamOptions {
|
|||||||
info?: (info) => void;
|
info?: (info) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface QueryOptions {
|
||||||
|
discardResult?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface WriteTableOptions {
|
export interface WriteTableOptions {
|
||||||
dropIfExists?: boolean;
|
dropIfExists?: boolean;
|
||||||
truncate?: boolean;
|
truncate?: boolean;
|
||||||
@ -45,7 +49,7 @@ export interface EngineDriver {
|
|||||||
databaseUrlPlaceholder?: string;
|
databaseUrlPlaceholder?: string;
|
||||||
connect({ server, port, user, password, database }): Promise<any>;
|
connect({ server, port, user, password, database }): Promise<any>;
|
||||||
close(pool): Promise<any>;
|
close(pool): Promise<any>;
|
||||||
query(pool: any, sql: string): Promise<QueryResult>;
|
query(pool: any, sql: string, options?: QueryOptions): Promise<QueryResult>;
|
||||||
stream(pool: any, sql: string, options: StreamOptions);
|
stream(pool: any, sql: string, options: StreamOptions);
|
||||||
readQuery(pool: any, sql: string, structure?: TableInfo): Promise<stream.Readable>;
|
readQuery(pool: any, sql: string, structure?: TableInfo): Promise<stream.Readable>;
|
||||||
writeTable(pool: any, name: NamedObjectInfo, options: WriteTableOptions): Promise<stream.Writeable>;
|
writeTable(pool: any, name: NamedObjectInfo, options: WriteTableOptions): Promise<stream.Writeable>;
|
||||||
@ -73,6 +77,8 @@ export interface EngineDriver {
|
|||||||
updateCollection(pool: any, changeSet: any): Promise<any>;
|
updateCollection(pool: any, changeSet: any): Promise<any>;
|
||||||
getCollectionUpdateScript(changeSet: any): string;
|
getCollectionUpdateScript(changeSet: any): string;
|
||||||
createDatabase(pool: any, name: string): Promise;
|
createDatabase(pool: any, name: string): Promise;
|
||||||
|
getQuerySplitterOptions(usage: 'stream' | 'script'): any;
|
||||||
|
script(pool: any, sql: string): Promise;
|
||||||
|
|
||||||
analyserClass?: any;
|
analyserClass?: any;
|
||||||
dumperClass?: any;
|
dumperClass?: any;
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
async function handleConfirmSql(sql) {
|
async function handleConfirmSql(sql) {
|
||||||
const resp = await axiosInstance.request({
|
const resp = await axiosInstance.request({
|
||||||
url: 'database-connections/query-data',
|
url: 'database-connections/run-script',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: {
|
params: {
|
||||||
conid,
|
conid,
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dbgate-plugin-tools": "^1.0.7",
|
"dbgate-plugin-tools": "^1.0.7",
|
||||||
|
"dbgate-query-splitter": "^4.1.1",
|
||||||
"webpack": "^4.42.0",
|
"webpack": "^4.42.0",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack-cli": "^3.3.11",
|
||||||
"dbgate-tools": "^4.1.1",
|
"dbgate-tools": "^4.1.1",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const { driverBase } = global.DBGATE_TOOLS;
|
const { driverBase } = global.DBGATE_TOOLS;
|
||||||
const Dumper = require('./Dumper');
|
const Dumper = require('./Dumper');
|
||||||
|
const { noSplitSplitterOptions } = require('dbgate-query-splitter/lib/options');
|
||||||
|
|
||||||
const mongoIdRegex = /^[0-9a-f]{24}$/;
|
const mongoIdRegex = /^[0-9a-f]{24}$/;
|
||||||
|
|
||||||
@ -34,6 +35,8 @@ const driver = {
|
|||||||
supportsDatabaseUrl: true,
|
supportsDatabaseUrl: true,
|
||||||
databaseUrlPlaceholder: 'e.g. mongodb://username:password@mongodb.mydomain.net/dbname',
|
databaseUrlPlaceholder: 'e.g. mongodb://username:password@mongodb.mydomain.net/dbname',
|
||||||
|
|
||||||
|
getQuerySplitterOptions: () => noSplitSplitterOptions,
|
||||||
|
|
||||||
showConnectionField: (field, values) => {
|
showConnectionField: (field, values) => {
|
||||||
if (field == 'useDatabaseUrl') return true;
|
if (field == 'useDatabaseUrl') return true;
|
||||||
if (values.useDatabaseUrl) {
|
if (values.useDatabaseUrl) {
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dbgate-plugin-tools": "^1.0.7",
|
"dbgate-plugin-tools": "^1.0.7",
|
||||||
|
"dbgate-query-splitter": "^4.1.1",
|
||||||
"webpack": "^4.42.0",
|
"webpack": "^4.42.0",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack-cli": "^3.3.11",
|
||||||
"dbgate-tools": "^4.1.1",
|
"dbgate-tools": "^4.1.1",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const { driverBase } = global.DBGATE_TOOLS;
|
const { driverBase } = global.DBGATE_TOOLS;
|
||||||
const MsSqlDumper = require('./MsSqlDumper');
|
const MsSqlDumper = require('./MsSqlDumper');
|
||||||
|
const { mssqlSplitterOptions } = require('dbgate-query-splitter/lib/options');
|
||||||
|
|
||||||
/** @type {import('dbgate-types').SqlDialect} */
|
/** @type {import('dbgate-types').SqlDialect} */
|
||||||
const dialect = {
|
const dialect = {
|
||||||
@ -34,6 +35,7 @@ const driver = {
|
|||||||
},
|
},
|
||||||
showConnectionField: (field, values) =>
|
showConnectionField: (field, values) =>
|
||||||
['authType', 'server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase'].includes(field),
|
['authType', 'server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase'].includes(field),
|
||||||
|
getQuerySplitterOptions: () => mssqlSplitterOptions,
|
||||||
|
|
||||||
engine: 'mssql@dbgate-plugin-mssql',
|
engine: 'mssql@dbgate-plugin-mssql',
|
||||||
title: 'Microsoft SQL Server',
|
title: 'Microsoft SQL Server',
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dbgate-plugin-tools": "^1.0.7",
|
"dbgate-plugin-tools": "^1.0.7",
|
||||||
|
"dbgate-query-splitter": "^4.1.1",
|
||||||
"webpack": "^4.42.0",
|
"webpack": "^4.42.0",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack-cli": "^3.3.11",
|
||||||
"@verycrazydog/mysql-parser": "^1.2.0",
|
"@verycrazydog/mysql-parser": "^1.2.0",
|
||||||
|
@ -4,7 +4,6 @@ const driverBases = require('../frontend/drivers');
|
|||||||
const Analyser = require('./Analyser');
|
const Analyser = require('./Analyser');
|
||||||
const mysql2 = require('mysql2');
|
const mysql2 = require('mysql2');
|
||||||
const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools');
|
const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools');
|
||||||
const mysqlSplitter = require('@verycrazydog/mysql-parser');
|
|
||||||
|
|
||||||
function extractColumns(fields) {
|
function extractColumns(fields) {
|
||||||
if (fields) {
|
if (fields) {
|
||||||
@ -24,18 +23,49 @@ function zipDataRow(rowArray, columns) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runQueryItem(connection, sql) {
|
/** @type {import('dbgate-types').EngineDriver} */
|
||||||
return new Promise((resolve, reject) => {
|
const drivers = driverBases.map(driverBase => ({
|
||||||
connection.query(sql, function (error, results, fields) {
|
...driverBase,
|
||||||
if (error) reject(error);
|
analyserClass: Analyser,
|
||||||
const columns = extractColumns(fields);
|
|
||||||
resolve({ rows: results && columns && results.map && results.map(row => zipDataRow(row, columns)), columns });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runStreamItem(connection, sql, options) {
|
async connect({ server, port, user, password, database, ssl }) {
|
||||||
return new Promise((resolve, reject) => {
|
const connection = mysql2.createConnection({
|
||||||
|
host: server,
|
||||||
|
port,
|
||||||
|
user,
|
||||||
|
password,
|
||||||
|
database,
|
||||||
|
ssl,
|
||||||
|
rowsAsArray: true,
|
||||||
|
supportBigNumbers: true,
|
||||||
|
bigNumberStrings: true,
|
||||||
|
// TODO: test following options
|
||||||
|
// multipleStatements: true,
|
||||||
|
// dateStrings: true,
|
||||||
|
});
|
||||||
|
connection._database_name = database;
|
||||||
|
return connection;
|
||||||
|
},
|
||||||
|
async close(pool) {
|
||||||
|
return pool.close();
|
||||||
|
},
|
||||||
|
query(connection, sql) {
|
||||||
|
if (sql == null) {
|
||||||
|
return {
|
||||||
|
rows: [],
|
||||||
|
columns: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
connection.query(sql, function (error, results, fields) {
|
||||||
|
if (error) reject(error);
|
||||||
|
const columns = extractColumns(fields);
|
||||||
|
resolve({ rows: results && columns && results.map && results.map(row => zipDataRow(row, columns)), columns });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async stream(connection, sql, options) {
|
||||||
const query = connection.query(sql);
|
const query = connection.query(sql);
|
||||||
let columns = [];
|
let columns = [];
|
||||||
|
|
||||||
@ -51,7 +81,7 @@ async function runStreamItem(connection, sql, options) {
|
|||||||
// };
|
// };
|
||||||
|
|
||||||
const handleEnd = () => {
|
const handleEnd = () => {
|
||||||
resolve();
|
options.done();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRow = row => {
|
const handleRow = row => {
|
||||||
@ -86,64 +116,6 @@ async function runStreamItem(connection, sql, options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
query.on('error', handleError).on('fields', handleFields).on('result', handleRow).on('end', handleEnd);
|
query.on('error', handleError).on('fields', handleFields).on('result', handleRow).on('end', handleEnd);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @type {import('dbgate-types').EngineDriver} */
|
|
||||||
const drivers = driverBases.map(driverBase => ({
|
|
||||||
...driverBase,
|
|
||||||
analyserClass: Analyser,
|
|
||||||
|
|
||||||
async connect({ server, port, user, password, database, ssl }) {
|
|
||||||
const connection = mysql2.createConnection({
|
|
||||||
host: server,
|
|
||||||
port,
|
|
||||||
user,
|
|
||||||
password,
|
|
||||||
database,
|
|
||||||
ssl,
|
|
||||||
rowsAsArray: true,
|
|
||||||
supportBigNumbers: true,
|
|
||||||
bigNumberStrings: true,
|
|
||||||
// TODO: test following options
|
|
||||||
// multipleStatements: true,
|
|
||||||
// dateStrings: true,
|
|
||||||
});
|
|
||||||
connection._database_name = database;
|
|
||||||
return connection;
|
|
||||||
},
|
|
||||||
async close(pool) {
|
|
||||||
return pool.close();
|
|
||||||
},
|
|
||||||
async query(connection, sql) {
|
|
||||||
if (sql == null) {
|
|
||||||
return {
|
|
||||||
rows: [],
|
|
||||||
columns: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const sqlSplitted = mysqlSplitter.split(sql);
|
|
||||||
let res = {
|
|
||||||
rows: [],
|
|
||||||
columns: [],
|
|
||||||
};
|
|
||||||
for (const sqlItem of sqlSplitted) {
|
|
||||||
const resultItem = await runQueryItem(connection, sqlItem);
|
|
||||||
if (resultItem.rows && resultItem.columns && resultItem.columns.length > 0) {
|
|
||||||
res = resultItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
async stream(connection, sql, options) {
|
|
||||||
const sqlSplitted = mysqlSplitter.split(sql);
|
|
||||||
|
|
||||||
for (const sqlItem of sqlSplitted) {
|
|
||||||
await runStreamItem(connection, sqlItem, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
options.done();
|
|
||||||
},
|
},
|
||||||
async readQuery(connection, sql, structure) {
|
async readQuery(connection, sql, structure) {
|
||||||
const query = connection.query(sql);
|
const query = connection.query(sql);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const { driverBase } = global.DBGATE_TOOLS;
|
const { driverBase } = global.DBGATE_TOOLS;
|
||||||
|
const { mysqlSplitterOptions } = require('dbgate-query-splitter/lib/options');
|
||||||
const Dumper = require('./Dumper');
|
const Dumper = require('./Dumper');
|
||||||
|
|
||||||
/** @type {import('dbgate-types').SqlDialect} */
|
/** @type {import('dbgate-types').SqlDialect} */
|
||||||
@ -21,6 +22,7 @@ const mysqlDriverBase = {
|
|||||||
dumperClass: Dumper,
|
dumperClass: Dumper,
|
||||||
dialect,
|
dialect,
|
||||||
defaultPort: 3306,
|
defaultPort: 3306,
|
||||||
|
getQuerySplitterOptions: () => mysqlSplitterOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @type {import('dbgate-types').EngineDriver} */
|
/** @type {import('dbgate-types').EngineDriver} */
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dbgate-plugin-tools": "^1.0.7",
|
"dbgate-plugin-tools": "^1.0.7",
|
||||||
|
"dbgate-query-splitter": "^4.1.1",
|
||||||
"dbgate-tools": "^4.1.1",
|
"dbgate-tools": "^4.1.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"pg": "^7.17.0",
|
"pg": "^7.17.0",
|
||||||
|
@ -23,56 +23,6 @@ function zipDataRow(rowArray, columns) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runStreamItem(client, sql, options) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const query = new pg.Query({
|
|
||||||
text: sql,
|
|
||||||
rowMode: 'array',
|
|
||||||
});
|
|
||||||
|
|
||||||
let wasHeader = false;
|
|
||||||
|
|
||||||
query.on('row', row => {
|
|
||||||
if (!wasHeader) {
|
|
||||||
columns = extractPostgresColumns(query._result);
|
|
||||||
if (columns && columns.length > 0) {
|
|
||||||
options.recordset(columns);
|
|
||||||
}
|
|
||||||
wasHeader = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
options.row(zipDataRow(row, columns));
|
|
||||||
});
|
|
||||||
|
|
||||||
query.on('end', () => {
|
|
||||||
if (!wasHeader) {
|
|
||||||
columns = extractPostgresColumns(query._result);
|
|
||||||
if (columns && columns.length > 0) {
|
|
||||||
options.recordset(columns);
|
|
||||||
}
|
|
||||||
wasHeader = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
query.on('error', error => {
|
|
||||||
console.log('ERROR', error);
|
|
||||||
const { message, lineNumber, procName } = error;
|
|
||||||
options.info({
|
|
||||||
message,
|
|
||||||
line: lineNumber,
|
|
||||||
procedure: procName,
|
|
||||||
time: new Date(),
|
|
||||||
severity: 'error',
|
|
||||||
});
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
client.query(query);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @type {import('dbgate-types').EngineDriver} */
|
/** @type {import('dbgate-types').EngineDriver} */
|
||||||
const drivers = driverBases.map(driverBase => ({
|
const drivers = driverBases.map(driverBase => ({
|
||||||
...driverBase,
|
...driverBase,
|
||||||
@ -127,21 +77,52 @@ const drivers = driverBases.map(driverBase => ({
|
|||||||
const columns = extractPostgresColumns(res);
|
const columns = extractPostgresColumns(res);
|
||||||
return { rows: res.rows.map(row => zipDataRow(row, columns)), columns };
|
return { rows: res.rows.map(row => zipDataRow(row, columns)), columns };
|
||||||
},
|
},
|
||||||
async stream(client, sql, options) {
|
stream(client, sql, options) {
|
||||||
let sqlSplitted;
|
const query = new pg.Query({
|
||||||
try {
|
text: sql,
|
||||||
sqlSplitted = identify(sql, { dialect: 'psql', strict: false });
|
rowMode: 'array',
|
||||||
} catch (e) {
|
});
|
||||||
// workaround
|
|
||||||
sqlSplitted = [{ text: sql }];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const sqlItem of sqlSplitted) {
|
let wasHeader = false;
|
||||||
await runStreamItem(client, sqlItem.text, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
options.done();
|
query.on('row', row => {
|
||||||
// return stream;
|
if (!wasHeader) {
|
||||||
|
columns = extractPostgresColumns(query._result);
|
||||||
|
if (columns && columns.length > 0) {
|
||||||
|
options.recordset(columns);
|
||||||
|
}
|
||||||
|
wasHeader = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.row(zipDataRow(row, columns));
|
||||||
|
});
|
||||||
|
|
||||||
|
query.on('end', () => {
|
||||||
|
if (!wasHeader) {
|
||||||
|
columns = extractPostgresColumns(query._result);
|
||||||
|
if (columns && columns.length > 0) {
|
||||||
|
options.recordset(columns);
|
||||||
|
}
|
||||||
|
wasHeader = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
query.on('error', error => {
|
||||||
|
console.log('ERROR', error);
|
||||||
|
const { message, lineNumber, procName } = error;
|
||||||
|
options.info({
|
||||||
|
message,
|
||||||
|
line: lineNumber,
|
||||||
|
procedure: procName,
|
||||||
|
time: new Date(),
|
||||||
|
severity: 'error',
|
||||||
|
});
|
||||||
|
options.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
client.query(query);
|
||||||
},
|
},
|
||||||
async getVersion(client) {
|
async getVersion(client) {
|
||||||
const { rows } = await this.query(client, 'SELECT version()');
|
const { rows } = await this.query(client, 'SELECT version()');
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const { driverBase } = global.DBGATE_TOOLS;
|
const { driverBase } = global.DBGATE_TOOLS;
|
||||||
const Dumper = require('./Dumper');
|
const Dumper = require('./Dumper');
|
||||||
|
const { postgreSplitterOptions } = require('dbgate-query-splitter/lib/options');
|
||||||
|
|
||||||
/** @type {import('dbgate-types').SqlDialect} */
|
/** @type {import('dbgate-types').SqlDialect} */
|
||||||
const dialect = {
|
const dialect = {
|
||||||
@ -21,6 +22,7 @@ const postgresDriverBase = {
|
|||||||
dialect,
|
dialect,
|
||||||
showConnectionField: (field, values) =>
|
showConnectionField: (field, values) =>
|
||||||
['server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase'].includes(field),
|
['server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase'].includes(field),
|
||||||
|
getQuerySplitterOptions: () => postgreSplitterOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @type {import('dbgate-types').EngineDriver} */
|
/** @type {import('dbgate-types').EngineDriver} */
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dbgate-tools": "^4.1.1",
|
"dbgate-tools": "^4.1.1",
|
||||||
"dbgate-plugin-tools": "^1.0.4",
|
"dbgate-plugin-tools": "^1.0.4",
|
||||||
|
"dbgate-query-splitter": "^4.1.1",
|
||||||
"byline": "^5.0.0",
|
"byline": "^5.0.0",
|
||||||
"sql-query-identifier": "^2.1.0",
|
"sql-query-identifier": "^2.1.0",
|
||||||
"webpack": "^4.42.0",
|
"webpack": "^4.42.0",
|
||||||
|
@ -2,7 +2,7 @@ const _ = require('lodash');
|
|||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
const driverBase = require('../frontend/driver');
|
const driverBase = require('../frontend/driver');
|
||||||
const Analyser = require('./Analyser');
|
const Analyser = require('./Analyser');
|
||||||
const { identify } = require('sql-query-identifier');
|
const { splitQuery, sqliteSplitterOptions } = require('dbgate-query-splitter');
|
||||||
const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools');
|
const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools');
|
||||||
|
|
||||||
let Database;
|
let Database;
|
||||||
@ -82,13 +82,13 @@ const driver = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async stream(client, sql, options) {
|
async stream(client, sql, options) {
|
||||||
const sqlSplitted = identify(sql, { dialect: 'sqlite', strict: false });
|
const sqlSplitted = splitQuery(sql, sqliteSplitterOptions);
|
||||||
|
|
||||||
const rowCounter = { count: 0, date: null };
|
const rowCounter = { count: 0, date: null };
|
||||||
|
|
||||||
const inTransaction = client.transaction(() => {
|
const inTransaction = client.transaction(() => {
|
||||||
for (const sqlItem of sqlSplitted) {
|
for (const sqlItem of sqlSplitted) {
|
||||||
runStreamItem(client, sqlItem.text, options, rowCounter);
|
runStreamItem(client, sqlItem, options, rowCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rowCounter.date) {
|
if (rowCounter.date) {
|
||||||
@ -117,6 +117,16 @@ const driver = {
|
|||||||
options.done();
|
options.done();
|
||||||
// return stream;
|
// return stream;
|
||||||
},
|
},
|
||||||
|
async script(client, sql) {
|
||||||
|
const inTransaction = client.transaction(() => {
|
||||||
|
for (const sqlItem of splitQuery(sql, this.getQuerySplitterOptions('script'))) {
|
||||||
|
const stmt = client.prepare(sqlItem);
|
||||||
|
stmt.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
inTransaction();
|
||||||
|
},
|
||||||
|
|
||||||
async readQueryTask(stmt, pass) {
|
async readQueryTask(stmt, pass) {
|
||||||
// let sent = 0;
|
// let sent = 0;
|
||||||
for (const row of stmt.iterate()) {
|
for (const row of stmt.iterate()) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const { driverBase } = global.DBGATE_TOOLS;
|
const { driverBase } = global.DBGATE_TOOLS;
|
||||||
const Dumper = require('./Dumper');
|
const Dumper = require('./Dumper');
|
||||||
|
const { sqliteSplitterOptions, noSplitSplitterOptions } = require('dbgate-query-splitter/lib/options');
|
||||||
|
|
||||||
function getDatabaseFileLabel(databaseFile) {
|
function getDatabaseFileLabel(databaseFile) {
|
||||||
if (!databaseFile) return databaseFile;
|
if (!databaseFile) return databaseFile;
|
||||||
@ -34,6 +35,7 @@ const driver = {
|
|||||||
singleDatabase: true,
|
singleDatabase: true,
|
||||||
defaultDatabase: getDatabaseFileLabel(connection.databaseFile),
|
defaultDatabase: getDatabaseFileLabel(connection.databaseFile),
|
||||||
}),
|
}),
|
||||||
|
getQuerySplitterOptions: (usage) => (usage == 'stream' ? noSplitSplitterOptions : sqliteSplitterOptions),
|
||||||
// isFileDatabase: true,
|
// isFileDatabase: true,
|
||||||
isElectronOnly: true,
|
isElectronOnly: true,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user