Merge branch 'master' into tableeditor2

This commit is contained in:
Jan Prochazka
2021-08-19 13:39:45 +02:00
9 changed files with 158 additions and 52 deletions

View File

@@ -1,6 +1,6 @@
{ {
"private": true, "private": true,
"version": "4.2.7", "version": "4.2.8-beta.2",
"name": "dbgate-all", "name": "dbgate-all",
"workspaces": [ "workspaces": [
"packages/*", "packages/*",

View File

@@ -8,6 +8,7 @@ const consoleObjectWriter = require('./consoleObjectWriter');
const jsonLinesWriter = require('./jsonLinesWriter'); const jsonLinesWriter = require('./jsonLinesWriter');
const jsonArrayWriter = require('./jsonArrayWriter'); const jsonArrayWriter = require('./jsonArrayWriter');
const jsonLinesReader = require('./jsonLinesReader'); const jsonLinesReader = require('./jsonLinesReader');
const sqlDataWriter = require('./sqlDataWriter');
const jslDataReader = require('./jslDataReader'); const jslDataReader = require('./jslDataReader');
const archiveWriter = require('./archiveWriter'); const archiveWriter = require('./archiveWriter');
const archiveReader = require('./archiveReader'); const archiveReader = require('./archiveReader');
@@ -29,6 +30,7 @@ const dbgateApi = {
jsonLinesWriter, jsonLinesWriter,
jsonArrayWriter, jsonArrayWriter,
jsonLinesReader, jsonLinesReader,
sqlDataWriter,
fakeObjectReader, fakeObjectReader,
consoleObjectWriter, consoleObjectWriter,
jslDataReader, jslDataReader,

View File

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

View File

@@ -4,7 +4,10 @@ import _groupBy from 'lodash/groupBy';
import _pick from 'lodash/pick'; import _pick from 'lodash/pick';
import _compact from 'lodash/compact'; import _compact from 'lodash/compact';
const STRUCTURE_FIELDS = ['tables', 'collections', 'views', 'matviews', 'functions', 'procedures', 'triggers'];
const fp_pick = arg => array => _pick(array, arg); const fp_pick = arg => array => _pick(array, arg);
export class DatabaseAnalyser { export class DatabaseAnalyser {
structure: DatabaseInfo; structure: DatabaseInfo;
modifications: DatabaseModification[]; modifications: DatabaseModification[];
@@ -23,8 +26,20 @@ export class DatabaseAnalyser {
async _computeSingleObjectId() {} 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() { async fullAnalysis() {
const res = await this._runAnalysis(); const res = this.addEngineField(await this._runAnalysis());
// console.log('FULL ANALYSIS', res); // console.log('FULL ANALYSIS', res);
return res; return res;
} }
@@ -33,7 +48,7 @@ export class DatabaseAnalyser {
// console.log('Analysing SINGLE OBJECT', name, typeField); // console.log('Analysing SINGLE OBJECT', name, typeField);
this.singleObjectFilter = { ...name, typeField }; this.singleObjectFilter = { ...name, typeField };
await this._computeSingleObjectId(); await this._computeSingleObjectId();
const res = await this._runAnalysis(); const res = this.addEngineField(await this._runAnalysis());
// console.log('SINGLE OBJECT RES', res); // console.log('SINGLE OBJECT RES', res);
const obj = const obj =
res[typeField]?.length == 1 res[typeField]?.length == 1
@@ -50,11 +65,11 @@ export class DatabaseAnalyser {
if (this.modifications == null) { if (this.modifications == null) {
// modifications not implemented, perform full analysis // modifications not implemented, perform full analysis
this.structure = null; this.structure = null;
return this._runAnalysis(); return this.addEngineField(await this._runAnalysis());
} }
if (this.modifications.length == 0) return null; if (this.modifications.length == 0) return null;
console.log('DB modifications detected:', this.modifications); console.log('DB modifications detected:', this.modifications);
return this.mergeAnalyseResult(await this._runAnalysis()); return this.addEngineField(this.mergeAnalyseResult(await this._runAnalysis()));
} }
mergeAnalyseResult(newlyAnalysed) { mergeAnalyseResult(newlyAnalysed) {
@@ -66,7 +81,7 @@ export class DatabaseAnalyser {
} }
const res = {}; const res = {};
for (const field of ['tables', 'collections', 'views', 'matviews', 'functions', 'procedures', 'triggers']) { for (const field of STRUCTURE_FIELDS) {
const removedIds = this.modifications const removedIds = this.modifications
.filter(x => x.action == 'remove' && x.objectTypeField == field) .filter(x => x.action == 'remove' && x.objectTypeField == field)
.map(x => x.objectId); .map(x => x.objectId);

View File

@@ -106,4 +106,5 @@ export interface DatabaseInfoObjects {
export interface DatabaseInfo extends DatabaseInfoObjects { export interface DatabaseInfo extends DatabaseInfoObjects {
schemas: SchemaInfo[]; schemas: SchemaInfo[];
engine?: string;
} }

View File

@@ -51,11 +51,11 @@
} }
:global(.theme-type-dark) .inner { :global(.theme-type-dark) .inner {
--json-tree-string-color: #efc5c5; --json-tree-string-color: #ffc5c5;
--json-tree-symbol-color: #efc5c5; --json-tree-symbol-color: #ffc5c5;
--json-tree-boolean-color: #a6b3f5; --json-tree-boolean-color: #b6c3ff;
--json-tree-function-color: #a6b3f5; --json-tree-function-color: #b6c3ff;
--json-tree-number-color: #bfbdf2; --json-tree-number-color: #bfbdff;
--json-tree-label-color: #e9aaed; --json-tree-label-color: #e9aaed;
--json-tree-arrow-color: #d4d4d4; --json-tree-arrow-color: #d4d4d4;
--json-tree-null-color: #dcdcdc; --json-tree-null-color: #dcdcdc;

View File

@@ -171,7 +171,6 @@
if (_.isPlainObject(value) || _.isArray(value)) return JSON.stringify(value); if (_.isPlainObject(value) || _.isArray(value)) return JSON.stringify(value);
return value; return value;
} }
</script> </script>
<script lang="ts"> <script lang="ts">
@@ -666,43 +665,52 @@
function handleGridWheel(event) { function handleGridWheel(event) {
if (event.shiftKey) { if (event.shiftKey) {
let newFirstVisibleColumnScrollIndex = firstVisibleColumnScrollIndex; scrollHorizontal(event.deltaY, event.deltaX);
if (event.deltaY > 0) {
newFirstVisibleColumnScrollIndex++;
}
if (event.deltaY < 0) {
newFirstVisibleColumnScrollIndex--;
}
if (newFirstVisibleColumnScrollIndex > maxScrollColumn) {
newFirstVisibleColumnScrollIndex = maxScrollColumn;
}
if (newFirstVisibleColumnScrollIndex < 0) {
newFirstVisibleColumnScrollIndex = 0;
}
firstVisibleColumnScrollIndex = newFirstVisibleColumnScrollIndex;
domHorizontalScroll.scroll(newFirstVisibleColumnScrollIndex);
} else { } else {
let newFirstVisibleRowScrollIndex = firstVisibleRowScrollIndex; scrollHorizontal(event.deltaX, event.deltaY);
if (event.deltaY > 0) { scrollVertical(event.deltaX, event.deltaY);
newFirstVisibleRowScrollIndex += wheelRowCount;
}
if (event.deltaY < 0) {
newFirstVisibleRowScrollIndex -= wheelRowCount;
}
let rowCount = grider.rowCount;
if (newFirstVisibleRowScrollIndex + visibleRowCountLowerBound > rowCount) {
newFirstVisibleRowScrollIndex = rowCount - visibleRowCountLowerBound + 1;
}
if (newFirstVisibleRowScrollIndex < 0) {
newFirstVisibleRowScrollIndex = 0;
}
firstVisibleRowScrollIndex = newFirstVisibleRowScrollIndex;
domVerticalScroll.scroll(newFirstVisibleRowScrollIndex);
} }
} }
function scrollVertical(deltaX, deltaY) {
let newFirstVisibleRowScrollIndex = firstVisibleRowScrollIndex;
if (deltaY > 0 && deltaX === -0) {
newFirstVisibleRowScrollIndex += wheelRowCount;
} else if (deltaY < 0 && deltaX === -0) {
newFirstVisibleRowScrollIndex -= wheelRowCount;
}
let rowCount = grider.rowCount;
if (newFirstVisibleRowScrollIndex + visibleRowCountLowerBound > rowCount) {
newFirstVisibleRowScrollIndex = rowCount - visibleRowCountLowerBound + 1;
}
if (newFirstVisibleRowScrollIndex < 0) {
newFirstVisibleRowScrollIndex = 0;
}
firstVisibleRowScrollIndex = newFirstVisibleRowScrollIndex;
domVerticalScroll.scroll(newFirstVisibleRowScrollIndex);
}
function scrollHorizontal(deltaX, deltaY) {
let newFirstVisibleColumnScrollIndex = firstVisibleColumnScrollIndex;
if (deltaX > 0 && deltaY === -0) {
newFirstVisibleColumnScrollIndex++;
} else if (deltaX < 0 && deltaY === -0) {
newFirstVisibleColumnScrollIndex--;
}
if (newFirstVisibleColumnScrollIndex > maxScrollColumn) {
newFirstVisibleColumnScrollIndex = maxScrollColumn;
}
if (newFirstVisibleColumnScrollIndex < 0) {
newFirstVisibleColumnScrollIndex = 0;
}
firstVisibleColumnScrollIndex = newFirstVisibleColumnScrollIndex;
domHorizontalScroll.scroll(newFirstVisibleColumnScrollIndex);
}
function getSelectedRowIndexes() { function getSelectedRowIndexes() {
if (selectedCells.find(x => x[0] == 'header')) return _.range(0, grider.rowCount); if (selectedCells.find(x => x[0] == 'header')) return _.range(0, grider.rowCount);
return _.uniq((selectedCells || []).map(x => x[0])).filter(x => _.isNumber(x)); return _.uniq((selectedCells || []).map(x => x[0])).filter(x => _.isNumber(x));
@@ -980,7 +988,6 @@
); );
const menu = getContextMenu(); const menu = getContextMenu();
</script> </script>
{#if !display || (!isDynamicStructure && (!columns || columns.length == 0))} {#if !display || (!isDynamicStructure && (!columns || columns.length == 0))}
@@ -1001,7 +1008,13 @@
{/each} {/each}
</div> </div>
{:else} {:else}
<div class="container" bind:clientWidth={containerWidth} bind:clientHeight={containerHeight} use:contextMenu={menu}> <div
class="container"
bind:clientWidth={containerWidth}
bind:clientHeight={containerHeight}
use:contextMenu={menu}
on:wheel={handleGridWheel}
>
<input <input
type="text" type="text"
class="focus-field" class="focus-field"
@@ -1019,7 +1032,6 @@
on:mousedown={handleGridMouseDown} on:mousedown={handleGridMouseDown}
on:mousemove={handleGridMouseMove} on:mousemove={handleGridMouseMove}
on:mouseup={handleGridMouseUp} on:mouseup={handleGridMouseUp}
on:wheel={handleGridWheel}
> >
<thead> <thead>
<tr> <tr>
@@ -1187,5 +1199,4 @@
right: 40px; right: 40px;
bottom: 20px; bottom: 20px;
} }
</style> </style>

View File

@@ -15,6 +15,13 @@ const jsonFormat = {
writerFunc: 'jsonArrayWriter', writerFunc: 'jsonArrayWriter',
}; };
const sqlFormat = {
storageType: 'sql',
extension: 'sql',
name: 'SQL',
writerFunc: 'sqlDataWriter',
};
const jsonlQuickExport = { const jsonlQuickExport = {
label: 'JSON lines', label: 'JSON lines',
extension: 'jsonl', 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[] { export function buildFileFormats(plugins): FileFormatDefinition[] {
const res = [jsonlFormat, jsonFormat]; const res = [jsonlFormat, jsonFormat, sqlFormat];
for (const { content } of plugins) { for (const { content } of plugins) {
const { fileFormats } = content; const { fileFormats } = content;
if (fileFormats) res.push(...fileFormats); if (fileFormats) res.push(...fileFormats);
@@ -47,7 +65,7 @@ export function buildFileFormats(plugins): FileFormatDefinition[] {
} }
export function buildQuickExports(plugins): QuickExportDefinition[] { export function buildQuickExports(plugins): QuickExportDefinition[] {
const res = [jsonQuickExport, jsonlQuickExport]; const res = [jsonQuickExport, jsonlQuickExport, sqlQuickExport];
for (const { content } of plugins) { for (const { content } of plugins) {
if (content.quickExports) res.push(...content.quickExports); if (content.quickExports) res.push(...content.quickExports);
} }

View File

@@ -69,11 +69,16 @@
.main { .main {
flex: 1; flex: 1;
display: flex; display: flex;
position: relative;
overflow-y: scroll; overflow-y: scroll;
background-color: var(--theme-bg-0); background-color: var(--theme-bg-0);
} }
table { table {
flex: 1; position: absolute;
left: 0;
right: 0;
top: 0;
width: 100%;
border-spacing: 0; border-spacing: 0;
border-collapse: collapse; border-collapse: collapse;
} }