diff --git a/package.json b/package.json index 4658893ca..6d403f16c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "4.2.7", + "version": "4.2.8-beta.2", "name": "dbgate-all", "workspaces": [ "packages/*", diff --git a/packages/api/src/shell/index.js b/packages/api/src/shell/index.js index 905c3e6ea..2e3a42b83 100644 --- a/packages/api/src/shell/index.js +++ b/packages/api/src/shell/index.js @@ -8,6 +8,7 @@ const consoleObjectWriter = require('./consoleObjectWriter'); const jsonLinesWriter = require('./jsonLinesWriter'); const jsonArrayWriter = require('./jsonArrayWriter'); const jsonLinesReader = require('./jsonLinesReader'); +const sqlDataWriter = require('./sqlDataWriter'); const jslDataReader = require('./jslDataReader'); const archiveWriter = require('./archiveWriter'); const archiveReader = require('./archiveReader'); @@ -29,6 +30,7 @@ const dbgateApi = { jsonLinesWriter, jsonArrayWriter, jsonLinesReader, + sqlDataWriter, fakeObjectReader, consoleObjectWriter, jslDataReader, diff --git a/packages/api/src/shell/sqlDataWriter.js b/packages/api/src/shell/sqlDataWriter.js new file mode 100644 index 000000000..50324db03 --- /dev/null +++ b/packages/api/src/shell/sqlDataWriter.js @@ -0,0 +1,54 @@ +const fs = require('fs'); +const stream = require('stream'); +const path = require('path'); +const { driverBase } = require('dbgate-tools'); +const requireEngineDriver = require('../utility/requireEngineDriver'); + +class SqlizeStream extends stream.Transform { + constructor({ fileName }) { + super({ objectMode: true }); + this.wasHeader = false; + this.tableName = path.parse(fileName).name; + this.driver = driverBase; + } + _transform(chunk, encoding, done) { + let skip = false; + if (!this.wasHeader) { + if ( + chunk.__isStreamHeader || + // TODO remove isArray test + Array.isArray(chunk.columns) + ) { + skip = true; + this.tableName = chunk.pureName; + if (chunk.engine) { + // @ts-ignore + this.driver = requireEngineDriver(chunk.engine) || driverBase; + } + } + this.wasHeader = true; + } + if (!skip) { + const dmp = this.driver.createDumper(); + dmp.put( + '^insert ^into %f (%,i) ^values (%,v);\n', + { pureName: this.tableName }, + Object.keys(chunk), + Object.values(chunk) + ); + this.push(dmp.s); + } + done(); + } +} + +async function sqlDataWriter({ fileName, driver, encoding = 'utf-8' }) { + console.log(`Writing file ${fileName}`); + const stringify = new SqlizeStream({ fileName }); + const fileStream = fs.createWriteStream(fileName, encoding); + stringify.pipe(fileStream); + stringify['finisher'] = fileStream; + return stringify; +} + +module.exports = sqlDataWriter; diff --git a/packages/tools/src/DatabaseAnalyser.ts b/packages/tools/src/DatabaseAnalyser.ts index 4b5ee4eda..45d8d7baa 100644 --- a/packages/tools/src/DatabaseAnalyser.ts +++ b/packages/tools/src/DatabaseAnalyser.ts @@ -4,7 +4,10 @@ import _groupBy from 'lodash/groupBy'; import _pick from 'lodash/pick'; import _compact from 'lodash/compact'; +const STRUCTURE_FIELDS = ['tables', 'collections', 'views', 'matviews', 'functions', 'procedures', 'triggers']; + const fp_pick = arg => array => _pick(array, arg); + export class DatabaseAnalyser { structure: DatabaseInfo; modifications: DatabaseModification[]; @@ -23,8 +26,20 @@ export class DatabaseAnalyser { async _computeSingleObjectId() {} + addEngineField(db: DatabaseInfo) { + if (!this.driver?.engine) return; + for (const field of STRUCTURE_FIELDS) { + if (!db[field]) continue; + for (const item of db[field]) { + item.engine = this.driver.engine; + } + } + db.engine = this.driver.engine; + return db; + } + async fullAnalysis() { - const res = await this._runAnalysis(); + const res = this.addEngineField(await this._runAnalysis()); // console.log('FULL ANALYSIS', res); return res; } @@ -33,7 +48,7 @@ export class DatabaseAnalyser { // console.log('Analysing SINGLE OBJECT', name, typeField); this.singleObjectFilter = { ...name, typeField }; await this._computeSingleObjectId(); - const res = await this._runAnalysis(); + const res = this.addEngineField(await this._runAnalysis()); // console.log('SINGLE OBJECT RES', res); const obj = res[typeField]?.length == 1 @@ -50,11 +65,11 @@ export class DatabaseAnalyser { if (this.modifications == null) { // modifications not implemented, perform full analysis this.structure = null; - return this._runAnalysis(); + return this.addEngineField(await this._runAnalysis()); } if (this.modifications.length == 0) return null; console.log('DB modifications detected:', this.modifications); - return this.mergeAnalyseResult(await this._runAnalysis()); + return this.addEngineField(this.mergeAnalyseResult(await this._runAnalysis())); } mergeAnalyseResult(newlyAnalysed) { @@ -66,7 +81,7 @@ export class DatabaseAnalyser { } const res = {}; - for (const field of ['tables', 'collections', 'views', 'matviews', 'functions', 'procedures', 'triggers']) { + for (const field of STRUCTURE_FIELDS) { const removedIds = this.modifications .filter(x => x.action == 'remove' && x.objectTypeField == field) .map(x => x.objectId); diff --git a/packages/types/dbinfo.d.ts b/packages/types/dbinfo.d.ts index 6c4effef3..191423da5 100644 --- a/packages/types/dbinfo.d.ts +++ b/packages/types/dbinfo.d.ts @@ -106,4 +106,5 @@ export interface DatabaseInfoObjects { export interface DatabaseInfo extends DatabaseInfoObjects { schemas: SchemaInfo[]; + engine?: string; } diff --git a/packages/web/src/celldata/JsonCellView.svelte b/packages/web/src/celldata/JsonCellView.svelte index fbb9c2e62..ecad18e7e 100644 --- a/packages/web/src/celldata/JsonCellView.svelte +++ b/packages/web/src/celldata/JsonCellView.svelte @@ -51,11 +51,11 @@ } :global(.theme-type-dark) .inner { - --json-tree-string-color: #efc5c5; - --json-tree-symbol-color: #efc5c5; - --json-tree-boolean-color: #a6b3f5; - --json-tree-function-color: #a6b3f5; - --json-tree-number-color: #bfbdf2; + --json-tree-string-color: #ffc5c5; + --json-tree-symbol-color: #ffc5c5; + --json-tree-boolean-color: #b6c3ff; + --json-tree-function-color: #b6c3ff; + --json-tree-number-color: #bfbdff; --json-tree-label-color: #e9aaed; --json-tree-arrow-color: #d4d4d4; --json-tree-null-color: #dcdcdc; diff --git a/packages/web/src/datagrid/DataGridCore.svelte b/packages/web/src/datagrid/DataGridCore.svelte index e9e59ac9b..f88a2ce0f 100644 --- a/packages/web/src/datagrid/DataGridCore.svelte +++ b/packages/web/src/datagrid/DataGridCore.svelte @@ -171,7 +171,6 @@ if (_.isPlainObject(value) || _.isArray(value)) return JSON.stringify(value); return value; } - {#if !display || (!isDynamicStructure && (!columns || columns.length == 0))} @@ -1001,7 +1008,13 @@ {/each} {:else} -
+
@@ -1187,5 +1199,4 @@ right: 40px; bottom: 20px; } - diff --git a/packages/web/src/plugins/fileformats.ts b/packages/web/src/plugins/fileformats.ts index 6f91fa086..e10082891 100644 --- a/packages/web/src/plugins/fileformats.ts +++ b/packages/web/src/plugins/fileformats.ts @@ -15,6 +15,13 @@ const jsonFormat = { writerFunc: 'jsonArrayWriter', }; +const sqlFormat = { + storageType: 'sql', + extension: 'sql', + name: 'SQL', + writerFunc: 'sqlDataWriter', +}; + const jsonlQuickExport = { label: 'JSON lines', extension: 'jsonl', @@ -37,8 +44,19 @@ const jsonQuickExport = { }), }; +const sqlQuickExport = { + label: 'SQL', + extension: 'sql', + createWriter: fileName => ({ + functionName: 'sqlDataWriter', + props: { + fileName, + }, + }), +}; + export function buildFileFormats(plugins): FileFormatDefinition[] { - const res = [jsonlFormat, jsonFormat]; + const res = [jsonlFormat, jsonFormat, sqlFormat]; for (const { content } of plugins) { const { fileFormats } = content; if (fileFormats) res.push(...fileFormats); @@ -47,7 +65,7 @@ export function buildFileFormats(plugins): FileFormatDefinition[] { } export function buildQuickExports(plugins): QuickExportDefinition[] { - const res = [jsonQuickExport, jsonlQuickExport]; + const res = [jsonQuickExport, jsonlQuickExport, sqlQuickExport]; for (const { content } of plugins) { if (content.quickExports) res.push(...content.quickExports); } diff --git a/packages/web/src/query/MessageView.svelte b/packages/web/src/query/MessageView.svelte index c5cee48d3..99d47cc78 100644 --- a/packages/web/src/query/MessageView.svelte +++ b/packages/web/src/query/MessageView.svelte @@ -69,11 +69,16 @@ .main { flex: 1; display: flex; + position: relative; overflow-y: scroll; background-color: var(--theme-bg-0); } table { - flex: 1; + position: absolute; + left: 0; + right: 0; + top: 0; + width: 100%; border-spacing: 0; border-collapse: collapse; }