diff --git a/integration-tests/__tests__/query.spec.js b/integration-tests/__tests__/query.spec.js index df1c10cd..daa47779 100644 --- a/integration-tests/__tests__/query.spec.js +++ b/integration-tests/__tests__/query.spec.js @@ -1,4 +1,5 @@ const engines = require('../engines'); +const { splitQuery } = require('dbgate-query-splitter'); const { testWrapper } = require('../tools'); 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 => { const handler = new StreamHandler(resolve); 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', () => { test.each(engines.map(engine => [engine.label, engine]))( 'Simple query - %s', @@ -129,7 +139,7 @@ describe('Query', () => { testWrapper(async (conn, driver, engine) => { for (const sql of initSql) await driver.query(conn, sql); - await driver.query( + await driver.script( 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;' ); diff --git a/integration-tests/docker-compose.yaml b/integration-tests/docker-compose.yaml index 556b46e8..8a1f0b69 100644 --- a/integration-tests/docker-compose.yaml +++ b/integration-tests/docker-compose.yaml @@ -9,7 +9,7 @@ services: - 15000:5432 mysql: - image: mysql + image: mysql:8.0.18 command: --default-authentication-plugin=mysql_native_password restart: always ports: diff --git a/packages/api/src/controllers/databaseConnections.js b/packages/api/src/controllers/databaseConnections.js index 59ca5c76..de8a7bcf 100644 --- a/packages/api/src/controllers/databaseConnections.js +++ b/packages/api/src/controllers/databaseConnections.js @@ -112,6 +112,14 @@ module.exports = { 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', async collectionData({ conid, database, options }) { const opened = await this.ensureOpened(conid, database); diff --git a/packages/api/src/proc/databaseConnectionProcess.js b/packages/api/src/proc/databaseConnectionProcess.js index 3c6f0b1f..e8c3d099 100644 --- a/packages/api/src/proc/databaseConnectionProcess.js +++ b/packages/api/src/proc/databaseConnectionProcess.js @@ -1,4 +1,5 @@ const stableStringify = require('json-stable-stringify'); +const { splitQuery } = require('dbgate-query-splitter'); const childProcessChecker = require('../utility/childProcessChecker'); const { extractBoolSettingsValue, extractIntSettingsValue } = require('dbgate-tools'); const requireEngineDriver = require('../utility/requireEngineDriver'); @@ -52,7 +53,7 @@ async function handleIncrementalRefresh(forceSend) { if (newStructure != null) { analysedStructure = newStructure; } - + if (forceSend || newStructure != null) { 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 }) { await waitConnected(); const driver = requireEngineDriver(storedConnection); @@ -188,6 +200,7 @@ function handlePing() { const messageHandlers = { connect: handleConnect, queryData: handleQueryData, + runScript: handleRunScript, updateCollection: handleUpdateCollection, collectionData: handleCollectionData, sqlPreview: handleSqlPreview, diff --git a/packages/api/src/proc/sessionProcess.js b/packages/api/src/proc/sessionProcess.js index 46b4c7fe..e11323f2 100644 --- a/packages/api/src/proc/sessionProcess.js +++ b/packages/api/src/proc/sessionProcess.js @@ -3,7 +3,7 @@ const path = require('path'); const fs = require('fs'); const _ = require('lodash'); const childProcessChecker = require('../utility/childProcessChecker'); -const goSplit = require('../utility/goSplit'); +const { splitQuery } = require('dbgate-query-splitter'); const { jsldir } = require('../utility/directories'); const requireEngineDriver = require('../utility/requireEngineDriver'); @@ -166,7 +166,7 @@ async function handleExecuteQuery({ sql }) { const resultIndexHolder = { value: 0, }; - for (const sqlItem of goSplit(sql)) { + for (const sqlItem of splitQuery(sql, driver.getQuerySplitterOptions('stream'))) { await handleStream(driver, resultIndexHolder, sqlItem); // const handler = new StreamHandler(resultIndex); // const stream = await driver.stream(systemConnection, sqlItem, handler); diff --git a/packages/api/src/shell/executeQuery.js b/packages/api/src/shell/executeQuery.js index e38fd54a..536617be 100644 --- a/packages/api/src/shell/executeQuery.js +++ b/packages/api/src/shell/executeQuery.js @@ -1,6 +1,5 @@ -const goSplit = require('../utility/goSplit'); +const { splitQuery } = require('dbgate-query-splitter'); const requireEngineDriver = require('../utility/requireEngineDriver'); -const { decryptConnection } = require('../utility/crypting'); const connectUtility = require('../utility/connectUtility'); async function executeQuery({ connection, sql }) { @@ -10,9 +9,9 @@ async function executeQuery({ connection, sql }) { const pool = await connectUtility(driver, connection); console.log(`Connected.`); - for (const sqlItem of goSplit(sql)) { + for (const sqlItem of splitQuery(sql, driver.getQuerySplitterOptions('script'))) { console.log('Executing query', sqlItem); - await driver.query(pool, sqlItem); + await driver.query(pool, sqlItem, { discardResult: true }); } console.log(`Query finished`); diff --git a/packages/api/src/utility/goSplit.js b/packages/api/src/utility/goSplit.js deleted file mode 100644 index 9555ca7e..00000000 --- a/packages/api/src/utility/goSplit.js +++ /dev/null @@ -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; diff --git a/packages/query-splitter/src/options.ts b/packages/query-splitter/src/options.ts index 6f646164..eef5ec88 100644 --- a/packages/query-splitter/src/options.ts +++ b/packages/query-splitter/src/options.ts @@ -7,6 +7,7 @@ export interface SplitterOptions { allowCustomDelimiter: boolean; allowGoDelimiter: boolean; allowDollarDollarString: boolean; + noSplit: boolean; } export const defaultSplitterOptions: SplitterOptions = { @@ -18,6 +19,7 @@ export const defaultSplitterOptions: SplitterOptions = { allowCustomDelimiter: false, allowGoDelimiter: false, allowDollarDollarString: false, + noSplit: false, }; export const mysqlSplitterOptions: SplitterOptions = { @@ -48,3 +50,17 @@ export const postgreSplitterOptions: SplitterOptions = { stringsEnds: { "'": "'", '"': '"' }, stringEscapes: { "'": "'", '"': '"' }, }; + +export const sqliteSplitterOptions: SplitterOptions = { + ...defaultSplitterOptions, + + stringsBegins: ["'", '"'], + stringsEnds: { "'": "'", '"': '"' }, + stringEscapes: { "'": "'", '"': '"' }, +}; + +export const noSplitSplitterOptions: SplitterOptions = { + ...defaultSplitterOptions, + + noSplit: true, +}; diff --git a/packages/query-splitter/src/splitQuery.ts b/packages/query-splitter/src/splitQuery.ts index cf593e00..b4c08830 100644 --- a/packages/query-splitter/src/splitQuery.ts +++ b/packages/query-splitter/src/splitQuery.ts @@ -162,6 +162,15 @@ function pushQuery(context) { } export function splitQuery(sql: string, options: SplitterOptions = null): string[] { + const usedOptions = { + ...defaultSplitterOptions, + ...options, + }; + + if (usedOptions.noSplit) { + return [sql]; + } + const context: SplitExecutionContext = { source: sql, end: sql.length, @@ -170,10 +179,7 @@ export function splitQuery(sql: string, options: SplitterOptions = null): string currentCommandStart: 0, output: [], wasDataOnLine: false, - options: { - ...defaultSplitterOptions, - ...options, - }, + options: usedOptions, }; while (context.position < context.end) { diff --git a/packages/query-splitter/src/splitter.test.ts b/packages/query-splitter/src/splitter.test.ts index 422d8de5..f2b2b237 100644 --- a/packages/query-splitter/src/splitter.test.ts +++ b/packages/query-splitter/src/splitter.test.ts @@ -1,4 +1,4 @@ -import { mysqlSplitterOptions, mssqlSplitterOptions, postgreSplitterOptions } from './options'; +import { mysqlSplitterOptions, mssqlSplitterOptions, postgreSplitterOptions, noSplitSplitterOptions } from './options'; import { splitQuery } from './splitQuery'; test('simple query', () => { @@ -71,3 +71,9 @@ test('go delimiter', () => { const output = splitQuery(input, mssqlSplitterOptions); 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']); +}); diff --git a/packages/tools/src/driverBase.ts b/packages/tools/src/driverBase.ts index a9a9ca3f..7adeb8cf 100644 --- a/packages/tools/src/driverBase.ts +++ b/packages/tools/src/driverBase.ts @@ -1,4 +1,5 @@ import { SqlDumper } from './SqlDumper'; +import { splitQuery } from 'dbgate-query-splitter'; const dialect = { limitSelect: true, @@ -34,4 +35,9 @@ export const driverBase = { createDumper() { 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 }); + } + }, }; diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts index 52342716..4f751080 100644 --- a/packages/types/engines.d.ts +++ b/packages/types/engines.d.ts @@ -12,6 +12,10 @@ export interface StreamOptions { info?: (info) => void; } +export interface QueryOptions { + discardResult?: boolean; +} + export interface WriteTableOptions { dropIfExists?: boolean; truncate?: boolean; @@ -45,7 +49,7 @@ export interface EngineDriver { databaseUrlPlaceholder?: string; connect({ server, port, user, password, database }): Promise; close(pool): Promise; - query(pool: any, sql: string): Promise; + query(pool: any, sql: string, options?: QueryOptions): Promise; stream(pool: any, sql: string, options: StreamOptions); readQuery(pool: any, sql: string, structure?: TableInfo): Promise; writeTable(pool: any, name: NamedObjectInfo, options: WriteTableOptions): Promise; @@ -73,6 +77,8 @@ export interface EngineDriver { updateCollection(pool: any, changeSet: any): Promise; getCollectionUpdateScript(changeSet: any): string; createDatabase(pool: any, name: string): Promise; + getQuerySplitterOptions(usage: 'stream' | 'script'): any; + script(pool: any, sql: string): Promise; analyserClass?: any; dumperClass?: any; diff --git a/packages/web/src/tabs/TableDataTab.svelte b/packages/web/src/tabs/TableDataTab.svelte index 8b964b96..41efdb6e 100644 --- a/packages/web/src/tabs/TableDataTab.svelte +++ b/packages/web/src/tabs/TableDataTab.svelte @@ -64,7 +64,7 @@ async function handleConfirmSql(sql) { const resp = await axiosInstance.request({ - url: 'database-connections/query-data', + url: 'database-connections/run-script', method: 'post', params: { conid, diff --git a/plugins/dbgate-plugin-mongo/package.json b/plugins/dbgate-plugin-mongo/package.json index 850f1859..6c808c0a 100644 --- a/plugins/dbgate-plugin-mongo/package.json +++ b/plugins/dbgate-plugin-mongo/package.json @@ -32,6 +32,7 @@ }, "devDependencies": { "dbgate-plugin-tools": "^1.0.7", + "dbgate-query-splitter": "^4.1.1", "webpack": "^4.42.0", "webpack-cli": "^3.3.11", "dbgate-tools": "^4.1.1", diff --git a/plugins/dbgate-plugin-mongo/src/frontend/driver.js b/plugins/dbgate-plugin-mongo/src/frontend/driver.js index 83ca9800..c349b34a 100644 --- a/plugins/dbgate-plugin-mongo/src/frontend/driver.js +++ b/plugins/dbgate-plugin-mongo/src/frontend/driver.js @@ -1,5 +1,6 @@ const { driverBase } = global.DBGATE_TOOLS; const Dumper = require('./Dumper'); +const { noSplitSplitterOptions } = require('dbgate-query-splitter/lib/options'); const mongoIdRegex = /^[0-9a-f]{24}$/; @@ -34,6 +35,8 @@ const driver = { supportsDatabaseUrl: true, databaseUrlPlaceholder: 'e.g. mongodb://username:password@mongodb.mydomain.net/dbname', + getQuerySplitterOptions: () => noSplitSplitterOptions, + showConnectionField: (field, values) => { if (field == 'useDatabaseUrl') return true; if (values.useDatabaseUrl) { diff --git a/plugins/dbgate-plugin-mssql/package.json b/plugins/dbgate-plugin-mssql/package.json index 3bb6d7ce..18410cd0 100644 --- a/plugins/dbgate-plugin-mssql/package.json +++ b/plugins/dbgate-plugin-mssql/package.json @@ -32,6 +32,7 @@ }, "devDependencies": { "dbgate-plugin-tools": "^1.0.7", + "dbgate-query-splitter": "^4.1.1", "webpack": "^4.42.0", "webpack-cli": "^3.3.11", "dbgate-tools": "^4.1.1", diff --git a/plugins/dbgate-plugin-mssql/src/frontend/driver.js b/plugins/dbgate-plugin-mssql/src/frontend/driver.js index 1b8a0415..3acb51d0 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/driver.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/driver.js @@ -1,5 +1,6 @@ const { driverBase } = global.DBGATE_TOOLS; const MsSqlDumper = require('./MsSqlDumper'); +const { mssqlSplitterOptions } = require('dbgate-query-splitter/lib/options'); /** @type {import('dbgate-types').SqlDialect} */ const dialect = { @@ -34,6 +35,7 @@ const driver = { }, showConnectionField: (field, values) => ['authType', 'server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase'].includes(field), + getQuerySplitterOptions: () => mssqlSplitterOptions, engine: 'mssql@dbgate-plugin-mssql', title: 'Microsoft SQL Server', diff --git a/plugins/dbgate-plugin-mysql/package.json b/plugins/dbgate-plugin-mysql/package.json index bfef27ac..23ababd1 100644 --- a/plugins/dbgate-plugin-mysql/package.json +++ b/plugins/dbgate-plugin-mysql/package.json @@ -32,6 +32,7 @@ }, "devDependencies": { "dbgate-plugin-tools": "^1.0.7", + "dbgate-query-splitter": "^4.1.1", "webpack": "^4.42.0", "webpack-cli": "^3.3.11", "@verycrazydog/mysql-parser": "^1.2.0", diff --git a/plugins/dbgate-plugin-mysql/src/backend/drivers.js b/plugins/dbgate-plugin-mysql/src/backend/drivers.js index bddf482f..0d05a567 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/drivers.js +++ b/plugins/dbgate-plugin-mysql/src/backend/drivers.js @@ -4,7 +4,6 @@ const driverBases = require('../frontend/drivers'); const Analyser = require('./Analyser'); const mysql2 = require('mysql2'); const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools'); -const mysqlSplitter = require('@verycrazydog/mysql-parser'); function extractColumns(fields) { if (fields) { @@ -24,18 +23,49 @@ function zipDataRow(rowArray, columns) { ); } -async function runQueryItem(connection, sql) { - 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 }); - }); - }); -} +/** @type {import('dbgate-types').EngineDriver} */ +const drivers = driverBases.map(driverBase => ({ + ...driverBase, + analyserClass: Analyser, -async function runStreamItem(connection, sql, options) { - return new Promise((resolve, reject) => { + 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(); + }, + 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); let columns = []; @@ -51,7 +81,7 @@ async function runStreamItem(connection, sql, options) { // }; const handleEnd = () => { - resolve(); + options.done(); }; 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); - }); -} - -/** @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) { const query = connection.query(sql); diff --git a/plugins/dbgate-plugin-mysql/src/frontend/drivers.js b/plugins/dbgate-plugin-mysql/src/frontend/drivers.js index 78448d20..1e4c337e 100644 --- a/plugins/dbgate-plugin-mysql/src/frontend/drivers.js +++ b/plugins/dbgate-plugin-mysql/src/frontend/drivers.js @@ -1,4 +1,5 @@ const { driverBase } = global.DBGATE_TOOLS; +const { mysqlSplitterOptions } = require('dbgate-query-splitter/lib/options'); const Dumper = require('./Dumper'); /** @type {import('dbgate-types').SqlDialect} */ @@ -21,6 +22,7 @@ const mysqlDriverBase = { dumperClass: Dumper, dialect, defaultPort: 3306, + getQuerySplitterOptions: () => mysqlSplitterOptions, }; /** @type {import('dbgate-types').EngineDriver} */ diff --git a/plugins/dbgate-plugin-postgres/package.json b/plugins/dbgate-plugin-postgres/package.json index 9d0bb351..c7a4ad02 100644 --- a/plugins/dbgate-plugin-postgres/package.json +++ b/plugins/dbgate-plugin-postgres/package.json @@ -31,6 +31,7 @@ }, "devDependencies": { "dbgate-plugin-tools": "^1.0.7", + "dbgate-query-splitter": "^4.1.1", "dbgate-tools": "^4.1.1", "lodash": "^4.17.21", "pg": "^7.17.0", diff --git a/plugins/dbgate-plugin-postgres/src/backend/drivers.js b/plugins/dbgate-plugin-postgres/src/backend/drivers.js index 7f7cae48..b1bdd1df 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/drivers.js +++ b/plugins/dbgate-plugin-postgres/src/backend/drivers.js @@ -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} */ const drivers = driverBases.map(driverBase => ({ ...driverBase, @@ -127,21 +77,52 @@ const drivers = driverBases.map(driverBase => ({ const columns = extractPostgresColumns(res); return { rows: res.rows.map(row => zipDataRow(row, columns)), columns }; }, - async stream(client, sql, options) { - let sqlSplitted; - try { - sqlSplitted = identify(sql, { dialect: 'psql', strict: false }); - } catch (e) { - // workaround - sqlSplitted = [{ text: sql }]; - } + stream(client, sql, options) { + const query = new pg.Query({ + text: sql, + rowMode: 'array', + }); - for (const sqlItem of sqlSplitted) { - await runStreamItem(client, sqlItem.text, options); - } + let wasHeader = false; - options.done(); - // return stream; + 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; + } + + 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) { const { rows } = await this.query(client, 'SELECT version()'); diff --git a/plugins/dbgate-plugin-postgres/src/frontend/drivers.js b/plugins/dbgate-plugin-postgres/src/frontend/drivers.js index 4b9b73d7..fc0b987d 100644 --- a/plugins/dbgate-plugin-postgres/src/frontend/drivers.js +++ b/plugins/dbgate-plugin-postgres/src/frontend/drivers.js @@ -1,5 +1,6 @@ const { driverBase } = global.DBGATE_TOOLS; const Dumper = require('./Dumper'); +const { postgreSplitterOptions } = require('dbgate-query-splitter/lib/options'); /** @type {import('dbgate-types').SqlDialect} */ const dialect = { @@ -21,6 +22,7 @@ const postgresDriverBase = { dialect, showConnectionField: (field, values) => ['server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase'].includes(field), + getQuerySplitterOptions: () => postgreSplitterOptions, }; /** @type {import('dbgate-types').EngineDriver} */ diff --git a/plugins/dbgate-plugin-sqlite/package.json b/plugins/dbgate-plugin-sqlite/package.json index 0d09dcf9..7c3bb7ba 100644 --- a/plugins/dbgate-plugin-sqlite/package.json +++ b/plugins/dbgate-plugin-sqlite/package.json @@ -32,6 +32,7 @@ "devDependencies": { "dbgate-tools": "^4.1.1", "dbgate-plugin-tools": "^1.0.4", + "dbgate-query-splitter": "^4.1.1", "byline": "^5.0.0", "sql-query-identifier": "^2.1.0", "webpack": "^4.42.0", diff --git a/plugins/dbgate-plugin-sqlite/src/backend/driver.js b/plugins/dbgate-plugin-sqlite/src/backend/driver.js index 28526e00..e6a8cf21 100644 --- a/plugins/dbgate-plugin-sqlite/src/backend/driver.js +++ b/plugins/dbgate-plugin-sqlite/src/backend/driver.js @@ -2,7 +2,7 @@ const _ = require('lodash'); const stream = require('stream'); const driverBase = require('../frontend/driver'); const Analyser = require('./Analyser'); -const { identify } = require('sql-query-identifier'); +const { splitQuery, sqliteSplitterOptions } = require('dbgate-query-splitter'); const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools'); let Database; @@ -82,13 +82,13 @@ const driver = { } }, 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 inTransaction = client.transaction(() => { for (const sqlItem of sqlSplitted) { - runStreamItem(client, sqlItem.text, options, rowCounter); + runStreamItem(client, sqlItem, options, rowCounter); } if (rowCounter.date) { @@ -117,6 +117,16 @@ const driver = { options.done(); // 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) { // let sent = 0; for (const row of stmt.iterate()) { diff --git a/plugins/dbgate-plugin-sqlite/src/frontend/driver.js b/plugins/dbgate-plugin-sqlite/src/frontend/driver.js index 98b0520b..99c7c1fe 100644 --- a/plugins/dbgate-plugin-sqlite/src/frontend/driver.js +++ b/plugins/dbgate-plugin-sqlite/src/frontend/driver.js @@ -1,5 +1,6 @@ const { driverBase } = global.DBGATE_TOOLS; const Dumper = require('./Dumper'); +const { sqliteSplitterOptions, noSplitSplitterOptions } = require('dbgate-query-splitter/lib/options'); function getDatabaseFileLabel(databaseFile) { if (!databaseFile) return databaseFile; @@ -34,6 +35,7 @@ const driver = { singleDatabase: true, defaultDatabase: getDatabaseFileLabel(connection.databaseFile), }), + getQuerySplitterOptions: (usage) => (usage == 'stream' ? noSplitSplitterOptions : sqliteSplitterOptions), // isFileDatabase: true, isElectronOnly: true, };