query splitter refactor

This commit is contained in:
Jan Prochazka 2021-06-03 11:27:49 +02:00
parent a76e742ce6
commit 0c48a5ee09
26 changed files with 203 additions and 172 deletions

View File

@ -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;'
); );

View File

@ -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:

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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`);

View File

@ -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;

View File

@ -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,
};

View File

@ -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) {

View File

@ -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']);
});

View File

@ -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 });
}
},
}; };

View File

@ -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;

View File

@ -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,

View File

@ -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",

View File

@ -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) {

View File

@ -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",

View File

@ -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',

View File

@ -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",

View File

@ -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);

View File

@ -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} */

View File

@ -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",

View File

@ -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()');

View File

@ -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} */

View File

@ -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",

View File

@ -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()) {

View File

@ -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,
}; };