From 2f820d8daceba1cf5f346c7c7b88abb14554555e Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 25 Sep 2022 14:43:10 +0200 Subject: [PATCH 01/79] sql editor - play icon to execute sql fragment --- misc/play-dark-mode.svg | 14 ++++ misc/play-light-mode.svg | 14 ++++ packages/web/public/global.css | 24 +++++++ packages/web/src/query/AceEditor.svelte | 85 ++++++++++++++++++++++++- packages/web/src/tabs/QueryTab.svelte | 1 + 5 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 misc/play-dark-mode.svg create mode 100644 misc/play-light-mode.svg diff --git a/misc/play-dark-mode.svg b/misc/play-dark-mode.svg new file mode 100644 index 000000000..093288891 --- /dev/null +++ b/misc/play-dark-mode.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/misc/play-light-mode.svg b/misc/play-light-mode.svg new file mode 100644 index 000000000..51bdf59a7 --- /dev/null +++ b/misc/play-light-mode.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/packages/web/public/global.css b/packages/web/public/global.css index df96b6ae1..0108d0988 100644 --- a/packages/web/public/global.css +++ b/packages/web/public/global.css @@ -167,3 +167,27 @@ textarea { color: var(--theme-font-1); border: 1px solid var(--theme-border); } + +.ace_gutter-cell.ace-gutter-sql-run { + background-repeat: no-repeat; + background-position: 2px center; + + /* content: '▶'; + margin-right: 3px; */ + + /* border-radius: 20px 0px 0px 20px; */ + /* Change the color of the breakpoint if you want + box-shadow: 0px 0px 1px 1px #248c46 inset; */ +} + +.theme-type-light .ace_gutter-cell.ace-gutter-sql-run { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTguMS4xLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIKCSB2aWV3Qm94PSIwIDAgMTcuODA0IDE3LjgwNCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTcuODA0IDE3LjgwNDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8Zz4KCTxnIGlkPSJjOThfcGxheSI+CgkJPHBhdGggZmlsbD0nIzQ0NCcgZD0iTTIuMDY3LDAuMDQzQzIuMjEtMC4wMjgsMi4zNzItMC4wMDgsMi40OTMsMC4wODVsMTMuMzEyLDguNTAzYzAuMDk0LDAuMDc4LDAuMTU0LDAuMTkxLDAuMTU0LDAuMzEzCgkJCWMwLDAuMTItMC4wNjEsMC4yMzctMC4xNTQsMC4zMTRMMi40OTIsMTcuNzE3Yy0wLjA3LDAuMDU3LTAuMTYyLDAuMDg3LTAuMjUsMC4wODdsLTAuMTc2LTAuMDQKCQkJYy0wLjEzNi0wLjA2NS0wLjIyMi0wLjIwNy0wLjIyMi0wLjM2MVYwLjQwMkMxLjg0NCwwLjI1LDEuOTMsMC4xMDcsMi4wNjcsMC4wNDN6Ii8+Cgk8L2c+Cgk8ZyBpZD0iQ2FwYV8xXzc4XyI+Cgk8L2c+CjwvZz4KPC9zdmc+Cg==); +} + +.theme-type-dark .ace_gutter-cell.ace-gutter-sql-run { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTguMS4xLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIKCSB2aWV3Qm94PSIwIDAgMTcuODA0IDE3LjgwNCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTcuODA0IDE3LjgwNDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8Zz4KCTxnIGlkPSJjOThfcGxheSI+CgkJPHBhdGggZmlsbD0nI2RkZCcgZD0iTTIuMDY3LDAuMDQzQzIuMjEtMC4wMjgsMi4zNzItMC4wMDgsMi40OTMsMC4wODVsMTMuMzEyLDguNTAzYzAuMDk0LDAuMDc4LDAuMTU0LDAuMTkxLDAuMTU0LDAuMzEzCgkJCWMwLDAuMTItMC4wNjEsMC4yMzctMC4xNTQsMC4zMTRMMi40OTIsMTcuNzE3Yy0wLjA3LDAuMDU3LTAuMTYyLDAuMDg3LTAuMjUsMC4wODdsLTAuMTc2LTAuMDQKCQkJYy0wLjEzNi0wLjA2NS0wLjIyMi0wLjIwNy0wLjIyMi0wLjM2MVYwLjQwMkMxLjg0NCwwLjI1LDEuOTMsMC4xMDcsMi4wNjcsMC4wNDN6Ii8+Cgk8L2c+Cgk8ZyBpZD0iQ2FwYV8xXzc4XyI+Cgk8L2c+CjwvZz4KPC9zdmc+Cg==); +} + +.ace_gutter-cell.ace-gutter-sql-run:hover { + background-color: var(--theme-bg-2); +} \ No newline at end of file diff --git a/packages/web/src/query/AceEditor.svelte b/packages/web/src/query/AceEditor.svelte index 8544f3b33..58a2ff404 100644 --- a/packages/web/src/query/AceEditor.svelte +++ b/packages/web/src/query/AceEditor.svelte @@ -155,6 +155,7 @@ export let readOnly = false; export let splitterOptions = null; export let onKeyDown = null; + export let onExecuteFragment = null; const tabVisible: any = getContext('tabVisible'); @@ -287,6 +288,17 @@ queryParts = data; editor.setHighlightActiveLine(queryParts.length <= 1); changedCurrentQueryPart(); + updateAnnotations(); + } + + function updateAnnotations() { + editor.session.setAnnotations( + (queryParts || []).map(part => ({ + row: part.trimStart.line, + text: part.text, + className: 'ace-gutter-sql-run', + })) + ); } const handleContextMenu = e => { @@ -304,8 +316,6 @@ function changedQueryParts() { const editor = getEditor(); if (splitterOptions && editor && queryParserWorker) { - const editor = getEditor(); - const message = { text: editor.getValue(), options: { @@ -392,6 +402,77 @@ editor.container.addEventListener('contextmenu', handleContextMenu); editor.keyBinding.addKeyboardHandler(handleKeyDown); changedQueryParts(); + + // editor.session.addGutterDecoration(0, 'ace-gutter-sql-run'); + + // editor.session.setAnnotations([ + // { + // row: 1, + // text: 'SQL SCRIPT 0', + // type: 'gutter', + // }, + // ]); + + // editor.getSession().setAnnotations([ + // { + // row: 0, + // // column: 0, + // text: 'Error Message', // Or the Json reply from the parser + // type: 'error', // also "warning" and "information" + // }, + // { + // row: 1, + // // column: 0, + // text: 'SELECT * FROM \n22222', // Or the Json reply from the parser + // // type: 'info', // also "warning" and "information" + // className: 'ace-gutter-sql-run', + // }, + // ]); + + // editor.on('guttermousemove', e => console.log('MOVE', e.target), true); + // editor.on('guttermouseout', e => console.log('OUT', e.target), true); + // editor.on('guttermouseleave', e => console.log('LEAVE', e.target), true); + // editor.session.setBreakpoint(0); + + // editor.on( + // 'gutterclick', + // e => { + // const row = e.getDocumentPosition().row; + + // const part = (queryParts || []).find(part => part.trimStart.line == row); + // if (part && onExecuteFragment) { + // onExecuteFragment(part.text); + // e.stop(); + // } + // }, + // true + // ); + + editor.on( + 'guttermousedown', + e => { + const row = e.getDocumentPosition().row; + + const part = (queryParts || []).find(part => part.trimStart.line == row); + if (part && onExecuteFragment) { + onExecuteFragment(part.text); + e.stop(); + editor.moveCursorTo(part.trimStart.line, 0); + editor.selection.clearSelection() + } + }, + true + ); + + // editor.session.gutterRenderer = { + // getWidth: function (session, lastLineNumber, config) { + // return lastLineNumber.toString().length * config.characterWidth; + // }, + // getText: function (session, row) { + // return (row + 1).toString(); + // // return String.fromCharCode(row + 65); + // }, + // }; }); onDestroy(() => { diff --git a/packages/web/src/tabs/QueryTab.svelte b/packages/web/src/tabs/QueryTab.svelte index 9166fcbaa..5b079eef3 100644 --- a/packages/web/src/tabs/QueryTab.svelte +++ b/packages/web/src/tabs/QueryTab.svelte @@ -295,6 +295,7 @@ invalidateCommands(); }} bind:this={domEditor} + onExecuteFragment={sql => executeCore(sql)} /> {:else} Date: Sun, 25 Sep 2022 19:45:47 +0200 Subject: [PATCH 02/79] query error markers --- packages/api/src/proc/sessionProcess.js | 20 +++++-- .../web/src/datagrid/JslDataGridCore.svelte | 2 +- packages/web/src/query/AceEditor.svelte | 55 ++++++++++++++----- packages/web/src/query/MessageView.svelte | 3 +- .../web/src/query/SocketMessageView.svelte | 20 ++++++- packages/web/src/query/SqlEditor.svelte | 2 +- packages/web/src/tabs/QueryTab.svelte | 27 ++++++--- .../src/backend/tediousDriver.js | 4 +- .../src/backend/drivers.js | 5 +- 9 files changed, 102 insertions(+), 36 deletions(-) diff --git a/packages/api/src/proc/sessionProcess.js b/packages/api/src/proc/sessionProcess.js index 2c179c413..91800d7c8 100644 --- a/packages/api/src/proc/sessionProcess.js +++ b/packages/api/src/proc/sessionProcess.js @@ -101,8 +101,9 @@ class TableWriter { } class StreamHandler { - constructor(resultIndexHolder, resolve) { + constructor(resultIndexHolder, resolve, startLine) { this.recordset = this.recordset.bind(this); + this.startLine = startLine; this.row = this.row.bind(this); // this.error = this.error.bind(this); this.done = this.done.bind(this); @@ -155,14 +156,20 @@ class StreamHandler { this.resolve(); } info(info) { + if (info.line != null) { + info = { + ...info, + line: this.startLine + info.line, + }; + } process.send({ msgtype: 'info', info }); } } -function handleStream(driver, resultIndexHolder, sql) { +function handleStream(driver, resultIndexHolder, sqlItem) { return new Promise((resolve, reject) => { - const handler = new StreamHandler(resultIndexHolder, resolve); - driver.stream(systemConnection, sql, handler); + const handler = new StreamHandler(resultIndexHolder, resolve, sqlItem.trimStart.line); + driver.stream(systemConnection, sqlItem.text, handler); }); } @@ -221,7 +228,10 @@ async function handleExecuteQuery({ sql }) { const resultIndexHolder = { value: 0, }; - for (const sqlItem of splitQuery(sql, driver.getQuerySplitterOptions('stream'))) { + for (const sqlItem of splitQuery(sql, { + ...driver.getQuerySplitterOptions('stream'), + returnRichInfo: true, + })) { await handleStream(driver, resultIndexHolder, sqlItem); // const handler = new StreamHandler(resultIndex); // const stream = await driver.stream(systemConnection, sqlItem, handler); diff --git a/packages/web/src/datagrid/JslDataGridCore.svelte b/packages/web/src/datagrid/JslDataGridCore.svelte index 94ea6df22..cb2afde3e 100644 --- a/packages/web/src/datagrid/JslDataGridCore.svelte +++ b/packages/web/src/datagrid/JslDataGridCore.svelte @@ -65,7 +65,7 @@ let changeIndex = 0; let rowCountLoaded = null; - const throttleLoadNext = _.throttle(() => domGrid.resetLoadedAll(), 500); + const throttleLoadNext = _.throttle(() => domGrid?.resetLoadedAll(), 500); const handleJslDataStats = stats => { if (stats.changeIndex < changeIndex) return; diff --git a/packages/web/src/query/AceEditor.svelte b/packages/web/src/query/AceEditor.svelte index 58a2ff404..f3e02e833 100644 --- a/packages/web/src/query/AceEditor.svelte +++ b/packages/web/src/query/AceEditor.svelte @@ -156,6 +156,7 @@ export let splitterOptions = null; export let onKeyDown = null; export let onExecuteFragment = null; + export let errorMessages = null; const tabVisible: any = getContext('tabVisible'); @@ -184,16 +185,28 @@ return editor; } - export function getCurrentCommandText(): string { - if (currentPart != null) return currentPart.text; - if (!editor) return ''; + export function getCurrentCommandText(): { text: string; line?: number } { + if (currentPart != null) { + return { + text: currentPart.text, + line: currentPart.trimStart.line, + }; + } + if (!editor) return { text: '' }; const selectedText = editor.getSelectedText(); - if (selectedText) return selectedText; + if (selectedText) + return { + text: selectedText, + line: editor.getSelectionRange().start.row, + }; if (editor.getHighlightActiveLine()) { const line = editor.getSelectionRange().start.row; - return editor.session.getLine(line); + return { + text: editor.session.getLine(line), + line, + }; } - return ''; + return { text: '' }; } export function getCodeCompletionCommandText() { @@ -292,13 +305,25 @@ } function updateAnnotations() { - editor.session.setAnnotations( - (queryParts || []).map(part => ({ - row: part.trimStart.line, - text: part.text, - className: 'ace-gutter-sql-run', - })) - ); + editor?.session?.setAnnotations([ + ...(queryParts || []) + .filter(part => !(errorMessages || []).find(err => err.line == part.trimStart.line)) + .map(part => ({ + row: part.trimStart.line, + text: part.text, + className: 'ace-gutter-sql-run', + })), + ...(errorMessages || []).map(error => ({ + row: error.line, + text: error.message, + type: 'error', + })), + ]); + } + + $: { + errorMessages; + updateAnnotations(); } const handleContextMenu = e => { @@ -455,10 +480,10 @@ const part = (queryParts || []).find(part => part.trimStart.line == row); if (part && onExecuteFragment) { - onExecuteFragment(part.text); + onExecuteFragment(part.text, part.trimStart.line); e.stop(); editor.moveCursorTo(part.trimStart.line, 0); - editor.selection.clearSelection() + editor.selection.clearSelection(); } }, true diff --git a/packages/web/src/query/MessageView.svelte b/packages/web/src/query/MessageView.svelte index 99d47cc78..20d0a6e08 100644 --- a/packages/web/src/query/MessageView.svelte +++ b/packages/web/src/query/MessageView.svelte @@ -18,6 +18,7 @@ export let items: any[]; export let showProcedure = false; export let showLine = false; + export let startLine = 0; $: time0 = items[0] && new Date(items[0].time).getTime(); @@ -58,7 +59,7 @@ {row.procedure || ''} {/if} {#if showLine} - {row.line || ''} + {row.line == null ? '' : row.line + 1 + startLine} {/if} {/each} diff --git a/packages/web/src/query/SocketMessageView.svelte b/packages/web/src/query/SocketMessageView.svelte index cc48ffcfa..746863755 100644 --- a/packages/web/src/query/SocketMessageView.svelte +++ b/packages/web/src/query/SocketMessageView.svelte @@ -13,8 +13,11 @@ export let eventName; export let executeNumber; export let showNoMessagesAlert = false; + export let startLine = 0; + export let onChangeErrors = null; const cachedMessagesRef = createRef([]); + const lastErrorMessageCountRef = createRef(0); let displayedMessages = []; @@ -44,11 +47,26 @@ } } + $: { + if (onChangeErrors) { + const errors = displayedMessages.filter(x => x.severity == 'error'); + if (lastErrorMessageCountRef.get() != errors.length) { + onChangeErrors( + errors.map(err => ({ + ...err, + line: err.line == null ? null : err.line + startLine, + })) + ); + lastErrorMessageCountRef.set(errors.length); + } + } + } + $: $effect; {#if showNoMessagesAlert && (!displayedMessages || displayedMessages.length == 0)} {:else} - + {/if} diff --git a/packages/web/src/query/SqlEditor.svelte b/packages/web/src/query/SqlEditor.svelte index 68fc206f3..b7870756f 100644 --- a/packages/web/src/query/SqlEditor.svelte +++ b/packages/web/src/query/SqlEditor.svelte @@ -32,7 +32,7 @@ return domEditor.getEditor(); } - export function getCurrentCommandText(): string { + export function getCurrentCommandText(): { text: string; line?: number } { return domEditor.getCurrentCommandText(); } diff --git a/packages/web/src/tabs/QueryTab.svelte b/packages/web/src/tabs/QueryTab.svelte index 5b079eef3..e925dc091 100644 --- a/packages/web/src/tabs/QueryTab.svelte +++ b/packages/web/src/tabs/QueryTab.svelte @@ -86,10 +86,11 @@ let busy = false; let executeNumber = 0; + let executeStartLine = 0; let visibleResultTabs = false; let sessionId = null; let resultCount; - + let errorMessages; let domEditor; $: connection = useConnectionInfo({ conid }); @@ -143,13 +144,14 @@ return !!conid && (!$connection?.isReadOnly || driver?.readOnlySessions); } - async function executeCore(sql) { + async function executeCore(sql, startLine = 0) { if (busy) return; if (!sql || !sql.trim()) { showSnackbarError('Skipped executing empty query'); return; } + executeStartLine = startLine; executeNumber++; visibleResultTabs = true; @@ -179,13 +181,14 @@ } export async function executeCurrent() { - const sql = domEditor.getCurrentCommandText(); - await executeCore(sql); + const cmd = domEditor.getCurrentCommandText(); + await executeCore(cmd.text, cmd.line); } export async function execute() { const selectedText = domEditor.getEditor().getSelectedText(); - await executeCore(selectedText || $editorValue); + const startLine = domEditor.getEditor().getSelectionRange().start.row; + await executeCore(selectedText || $editorValue, selectedText ? startLine : 0); } export async function kill() { @@ -257,6 +260,10 @@ : null, }); + function handleChangeErrors(errors) { + errorMessages = errors; + } + function createMenu() { return [ { command: 'query.execute' }, @@ -289,13 +296,17 @@ splitterOptions={driver?.getQuerySplitterOptions('script')} value={$editorState.value || ''} menu={createMenu()} - on:input={e => setEditorData(e.detail)} + on:input={e => { + setEditorData(e.detail); + errorMessages = []; + }} on:focus={() => { activator.activate(); invalidateCommands(); }} bind:this={domEditor} - onExecuteFragment={sql => executeCore(sql)} + onExecuteFragment={(sql, startLine) => executeCore(sql, startLine)} + {errorMessages} /> {:else} diff --git a/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js b/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js index 4389667d0..45e62d126 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js +++ b/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js @@ -134,7 +134,7 @@ async function tediousStream(pool, sql, options) { const { message, lineNumber, procName } = info; options.info({ message, - line: lineNumber, + line: lineNumber != null && lineNumber > 0 ? lineNumber - 1 : lineNumber, procedure: procName, time: new Date(), severity: 'info', @@ -144,7 +144,7 @@ async function tediousStream(pool, sql, options) { const { message, lineNumber, procName } = error; options.info({ message, - line: lineNumber, + line: lineNumber != null && lineNumber > 0 ? lineNumber - 1 : lineNumber, procedure: procName, time: new Date(), severity: 'error', diff --git a/plugins/dbgate-plugin-mysql/src/backend/drivers.js b/plugins/dbgate-plugin-mysql/src/backend/drivers.js index 5c5bbbbd5..587db33f9 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/drivers.js +++ b/plugins/dbgate-plugin-mysql/src/backend/drivers.js @@ -112,11 +112,10 @@ const drivers = driverBases.map(driverBase => ({ const handleError = error => { console.log('ERROR', error); - const { message, lineNumber, procName } = error; + const { message } = error; options.info({ message, - line: lineNumber, - procedure: procName, + line: 0, time: new Date(), severity: 'error', }); From 7781ad69cf18aae49712f251d3e7d7d573c33224 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 25 Sep 2022 19:50:31 +0200 Subject: [PATCH 03/79] sql error line number - postgres --- plugins/dbgate-plugin-postgres/src/backend/drivers.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/dbgate-plugin-postgres/src/backend/drivers.js b/plugins/dbgate-plugin-postgres/src/backend/drivers.js index 46d11f02d..4faf653a5 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/drivers.js +++ b/plugins/dbgate-plugin-postgres/src/backend/drivers.js @@ -147,10 +147,14 @@ const drivers = driverBases.map(driverBase => ({ query.on('error', error => { console.log('ERROR', error); - const { message, lineNumber, procName } = error; + const { message, position, procName } = error; + let line = null; + if (position) { + line = sql.substring(0, parseInt(position)).replace(/[^\n]/g, '').length; + } options.info({ message, - line: lineNumber, + line, procedure: procName, time: new Date(), severity: 'error', From 2b26779ea8553629fb21fd1f239fd1d397c92e71 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 25 Sep 2022 20:06:34 +0200 Subject: [PATCH 04/79] fixes + sqlite error line number --- packages/api/src/proc/sessionProcess.js | 5 +++-- packages/web/src/query/SocketMessageView.svelte | 2 +- plugins/dbgate-plugin-sqlite/src/backend/driver.js | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/api/src/proc/sessionProcess.js b/packages/api/src/proc/sessionProcess.js index 91800d7c8..459e934f5 100644 --- a/packages/api/src/proc/sessionProcess.js +++ b/packages/api/src/proc/sessionProcess.js @@ -156,7 +156,7 @@ class StreamHandler { this.resolve(); } info(info) { - if (info.line != null) { + if (info && info.line != null) { info = { ...info, line: this.startLine + info.line, @@ -168,7 +168,8 @@ class StreamHandler { function handleStream(driver, resultIndexHolder, sqlItem) { return new Promise((resolve, reject) => { - const handler = new StreamHandler(resultIndexHolder, resolve, sqlItem.trimStart.line); + const start = sqlItem.trimStart || sqlItem.start; + const handler = new StreamHandler(resultIndexHolder, resolve, start && start.line); driver.stream(systemConnection, sqlItem.text, handler); }); } diff --git a/packages/web/src/query/SocketMessageView.svelte b/packages/web/src/query/SocketMessageView.svelte index 746863755..5e1bddf41 100644 --- a/packages/web/src/query/SocketMessageView.svelte +++ b/packages/web/src/query/SocketMessageView.svelte @@ -49,7 +49,7 @@ $: { if (onChangeErrors) { - const errors = displayedMessages.filter(x => x.severity == 'error'); + const errors = displayedMessages.filter(x => x.severity == 'error' && x.line != null); if (lastErrorMessageCountRef.get() != errors.length) { onChangeErrors( errors.map(err => ({ diff --git a/plugins/dbgate-plugin-sqlite/src/backend/driver.js b/plugins/dbgate-plugin-sqlite/src/backend/driver.js index 7258d18b2..1e4ee9e38 100644 --- a/plugins/dbgate-plugin-sqlite/src/backend/driver.js +++ b/plugins/dbgate-plugin-sqlite/src/backend/driver.js @@ -104,10 +104,10 @@ const driver = { inTransaction(); } catch (error) { console.log('ERROR', error); - const { message, lineNumber, procName } = error; + const { message, procName } = error; options.info({ message, - line: lineNumber, + line: 0, procedure: procName, time: new Date(), severity: 'error', From 1454ddacb85742ff146a518e339e521a25c7d533 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 25 Sep 2022 20:10:56 +0200 Subject: [PATCH 05/79] fix --- packages/web/src/query/AceEditor.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/web/src/query/AceEditor.svelte b/packages/web/src/query/AceEditor.svelte index f3e02e833..ace8344b0 100644 --- a/packages/web/src/query/AceEditor.svelte +++ b/packages/web/src/query/AceEditor.svelte @@ -305,6 +305,8 @@ } function updateAnnotations() { + if (!mode?.includes('sql')) return; + editor?.session?.setAnnotations([ ...(queryParts || []) .filter(part => !(errorMessages || []).find(err => err.line == part.trimStart.line)) From 6c9c4be31115ae758be93710c6b378332d7c0392 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 25 Sep 2022 20:11:22 +0200 Subject: [PATCH 06/79] v5.1.3-beta.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8c00752fa..68a67fcec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.3-beta.1", + "version": "5.1.3-beta.2", "name": "dbgate-all", "workspaces": [ "packages/*", From 5913788035d90249210daac284fe05a3f889de5f Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Mon, 26 Sep 2022 19:53:21 +0200 Subject: [PATCH 07/79] v5.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 68a67fcec..80ffdd1e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.3-beta.2", + "version": "5.1.3", "name": "dbgate-all", "workspaces": [ "packages/*", From e9256fe20e927d0a63d17d154a8bf99f837746fb Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Mon, 26 Sep 2022 20:19:07 +0200 Subject: [PATCH 08/79] changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3f3e2de9..cb745f730 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ Builds: - linux - application for linux - win - application for Windows +### 5.1.3 +- ADDED: Editing multiline cell values #378 #371 #359 +- ADDED: Truncate table #333 +- ADDED: Perspectives - show row count +- ADDED: Query - error markers in gutter area +- ADDED: Query - ability to execute query elements from gutter +- FIXED: Coorect error line numbers returned from queries + ### 5.1.2 - FIXED: MongoDb any export function does not work. #373 - ADDED: Query Designer short order more flexibility #372 From 8ce5e68c0de6b80700266f02741cdf1dbaf1fcbb Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 09:19:53 +0200 Subject: [PATCH 09/79] typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb745f730..844ee97ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ Builds: - ADDED: Perspectives - show row count - ADDED: Query - error markers in gutter area - ADDED: Query - ability to execute query elements from gutter -- FIXED: Coorect error line numbers returned from queries +- FIXED: Correct error line numbers returned from queries ### 5.1.2 - FIXED: MongoDb any export function does not work. #373 From c5a7f458ba4cfaa67288f876febb1750c1a7d469 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 12:24:06 +0200 Subject: [PATCH 10/79] better editor SQL splitting --- packages/api/package.json | 2 +- packages/api/src/shell/importDatabase.js | 2 +- packages/tools/package.json | 2 +- packages/types/engines.d.ts | 6 ++---- packages/web/package.json | 2 +- packages/web/src/tabs/QueryTab.svelte | 4 ++-- plugins/dbgate-plugin-mongo/package.json | 2 +- plugins/dbgate-plugin-mssql/package.json | 2 +- plugins/dbgate-plugin-mssql/src/frontend/driver.js | 5 ++++- plugins/dbgate-plugin-mysql/package.json | 2 +- plugins/dbgate-plugin-mysql/src/frontend/drivers.js | 6 +++++- plugins/dbgate-plugin-postgres/package.json | 2 +- plugins/dbgate-plugin-postgres/src/frontend/drivers.js | 5 ++++- plugins/dbgate-plugin-redis/package.json | 2 +- plugins/dbgate-plugin-sqlite/package.json | 2 +- plugins/dbgate-plugin-sqlite/src/frontend/driver.js | 9 ++++++++- yarn.lock | 8 ++++---- 17 files changed, 39 insertions(+), 24 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index e3f0c90dc..8190e5805 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -25,7 +25,7 @@ "compare-versions": "^3.6.0", "cors": "^2.8.5", "cross-env": "^6.0.3", - "dbgate-query-splitter": "^4.9.0", + "dbgate-query-splitter": "^4.9.2", "dbgate-sqltree": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1", "debug": "^4.3.4", diff --git a/packages/api/src/shell/importDatabase.js b/packages/api/src/shell/importDatabase.js index 7ad1fbfc1..6cb52961d 100644 --- a/packages/api/src/shell/importDatabase.js +++ b/packages/api/src/shell/importDatabase.js @@ -47,7 +47,7 @@ async function importDatabase({ connection = undefined, systemConnection = undef const downloadedFile = await download(inputFile); const fileStream = fs.createReadStream(downloadedFile, 'utf-8'); - const splittedStream = splitQueryStream(fileStream, driver.getQuerySplitterOptions()); + const splittedStream = splitQueryStream(fileStream, driver.getQuerySplitterOptions('script')); const importStream = new ImportStream(pool, driver); // @ts-ignore splittedStream.pipe(importStream); diff --git a/packages/tools/package.json b/packages/tools/package.json index 17001d95a..52e8d17ac 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -31,7 +31,7 @@ "typescript": "^4.4.3" }, "dependencies": { - "dbgate-query-splitter": "^4.9.0", + "dbgate-query-splitter": "^4.9.2", "dbgate-sqltree": "^5.0.0-alpha.1", "debug": "^4.3.4", "json-stable-stringify": "^1.0.1", diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts index cc15d56d2..39ac92043 100644 --- a/packages/types/engines.d.ts +++ b/packages/types/engines.d.ts @@ -89,9 +89,7 @@ export interface EngineDriver { ): Promise; analyseSingleTable(pool: any, name: NamedObjectInfo): Promise; getVersion(pool: any): Promise<{ version: string }>; - listDatabases( - pool: any - ): Promise< + listDatabases(pool: any): Promise< { name: string; }[] @@ -112,7 +110,7 @@ export interface EngineDriver { updateCollection(pool: any, changeSet: any): Promise; getCollectionUpdateScript(changeSet: any): string; createDatabase(pool: any, name: string): Promise; - getQuerySplitterOptions(usage: 'stream' | 'script'): any; + getQuerySplitterOptions(usage: 'stream' | 'script' | 'editor'): any; script(pool: any, sql: string): Promise; getNewObjectTemplates(): NewObjectTemplate[]; // direct call of pool method, only some methods could be supported, on only some drivers diff --git a/packages/web/package.json b/packages/web/package.json index 0be497b59..ce096543a 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -24,7 +24,7 @@ "chartjs-adapter-moment": "^1.0.0", "cross-env": "^7.0.3", "dbgate-datalib": "^5.0.0-alpha.1", - "dbgate-query-splitter": "^4.9.0", + "dbgate-query-splitter": "^4.9.2", "dbgate-sqltree": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1", "dbgate-types": "^5.0.0-alpha.1", diff --git a/packages/web/src/tabs/QueryTab.svelte b/packages/web/src/tabs/QueryTab.svelte index e925dc091..fefb5adf3 100644 --- a/packages/web/src/tabs/QueryTab.svelte +++ b/packages/web/src/tabs/QueryTab.svelte @@ -293,7 +293,7 @@ engine={$connection && $connection.engine} {conid} {database} - splitterOptions={driver?.getQuerySplitterOptions('script')} + splitterOptions={driver?.getQuerySplitterOptions('editor')} value={$editorState.value || ''} menu={createMenu()} on:input={e => { @@ -312,7 +312,7 @@ setEditorData(e.detail)} on:focus={() => { diff --git a/plugins/dbgate-plugin-mongo/package.json b/plugins/dbgate-plugin-mongo/package.json index 85e4c0a5e..a0329c95b 100644 --- a/plugins/dbgate-plugin-mongo/package.json +++ b/plugins/dbgate-plugin-mongo/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "dbgate-plugin-tools": "^1.0.7", - "dbgate-query-splitter": "^4.9.0", + "dbgate-query-splitter": "^4.9.2", "webpack": "^4.42.0", "webpack-cli": "^3.3.11", "dbgate-tools": "^5.0.0-alpha.1", diff --git a/plugins/dbgate-plugin-mssql/package.json b/plugins/dbgate-plugin-mssql/package.json index d3f06e9d0..6e93fde51 100644 --- a/plugins/dbgate-plugin-mssql/package.json +++ b/plugins/dbgate-plugin-mssql/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "dbgate-plugin-tools": "^1.0.7", - "dbgate-query-splitter": "^4.9.0", + "dbgate-query-splitter": "^4.9.2", "webpack": "^4.42.0", "webpack-cli": "^3.3.11", "dbgate-tools": "^5.0.0-alpha.1", diff --git a/plugins/dbgate-plugin-mssql/src/frontend/driver.js b/plugins/dbgate-plugin-mssql/src/frontend/driver.js index 14711e1fe..ae1b15c7d 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/driver.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/driver.js @@ -130,7 +130,10 @@ const driver = { (field == 'trustServerCertificate' && values.authType != 'sql' && values.authType != 'sspi') || (field == 'windowsDomain' && values.authType != 'sql' && values.authType != 'sspi'), // (field == 'useDatabaseUrl' && values.authType != 'sql' && values.authType != 'sspi') - getQuerySplitterOptions: () => mssqlSplitterOptions, + getQuerySplitterOptions: usage => + usage == 'editor' + ? { ...mssqlSplitterOptions, adaptiveGoSplit: true, ignoreComments: true, preventSingleLineSplit: true } + : 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 ca737a52f..c9c9dfafb 100644 --- a/plugins/dbgate-plugin-mysql/package.json +++ b/plugins/dbgate-plugin-mysql/package.json @@ -33,7 +33,7 @@ "devDependencies": { "antares-mysql-dumper": "^0.0.1", "dbgate-plugin-tools": "^1.0.7", - "dbgate-query-splitter": "^4.9.0", + "dbgate-query-splitter": "^4.9.2", "dbgate-tools": "^5.0.0-alpha.1", "mysql2": "^2.3.3", "webpack": "^4.42.0", diff --git a/plugins/dbgate-plugin-mysql/src/frontend/drivers.js b/plugins/dbgate-plugin-mysql/src/frontend/drivers.js index b9a840fef..3762fdf98 100644 --- a/plugins/dbgate-plugin-mysql/src/frontend/drivers.js +++ b/plugins/dbgate-plugin-mysql/src/frontend/drivers.js @@ -108,7 +108,11 @@ const mysqlDriverBase = { dumperClass: Dumper, dialect, defaultPort: 3306, - getQuerySplitterOptions: () => mysqlSplitterOptions, + getQuerySplitterOptions: usage => + usage == 'editor' + ? { ...mysqlSplitterOptions, ignoreComments: true, preventSingleLineSplit: true } + : mysqlSplitterOptions, + readOnlySessions: true, supportsDatabaseDump: true, authTypeLabel: 'Connection mode', diff --git a/plugins/dbgate-plugin-postgres/package.json b/plugins/dbgate-plugin-postgres/package.json index 74e9fb6a7..373fe2d57 100644 --- a/plugins/dbgate-plugin-postgres/package.json +++ b/plugins/dbgate-plugin-postgres/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "dbgate-plugin-tools": "^1.0.7", - "dbgate-query-splitter": "^4.9.0", + "dbgate-query-splitter": "^4.9.2", "dbgate-tools": "^5.0.0-alpha.1", "lodash": "^4.17.21", "pg": "^8.7.1", diff --git a/plugins/dbgate-plugin-postgres/src/frontend/drivers.js b/plugins/dbgate-plugin-postgres/src/frontend/drivers.js index 601a67aaa..fe1c88879 100644 --- a/plugins/dbgate-plugin-postgres/src/frontend/drivers.js +++ b/plugins/dbgate-plugin-postgres/src/frontend/drivers.js @@ -105,7 +105,10 @@ const postgresDriverBase = { dialect, // showConnectionField: (field, values) => // ['server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase'].includes(field), - getQuerySplitterOptions: () => postgreSplitterOptions, + getQuerySplitterOptions: usage => + usage == 'editor' + ? { ...postgreSplitterOptions, ignoreComments: true, preventSingleLineSplit: true } + : postgreSplitterOptions, readOnlySessions: true, databaseUrlPlaceholder: 'e.g. postgresql://user:password@localhost:5432/default_database', diff --git a/plugins/dbgate-plugin-redis/package.json b/plugins/dbgate-plugin-redis/package.json index c60df74c6..97a5c0e9b 100644 --- a/plugins/dbgate-plugin-redis/package.json +++ b/plugins/dbgate-plugin-redis/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "dbgate-plugin-tools": "^1.0.7", - "dbgate-query-splitter": "^4.9.0", + "dbgate-query-splitter": "^4.9.2", "dbgate-tools": "^5.0.0-alpha.1", "lodash": "^4.17.21", "webpack": "^4.42.0", diff --git a/plugins/dbgate-plugin-sqlite/package.json b/plugins/dbgate-plugin-sqlite/package.json index c82f057c5..ded9e4549 100644 --- a/plugins/dbgate-plugin-sqlite/package.json +++ b/plugins/dbgate-plugin-sqlite/package.json @@ -32,7 +32,7 @@ "devDependencies": { "dbgate-tools": "^5.0.0-alpha.1", "dbgate-plugin-tools": "^1.0.4", - "dbgate-query-splitter": "^4.9.0", + "dbgate-query-splitter": "^4.9.2", "byline": "^5.0.0", "webpack": "^4.42.0", "webpack-cli": "^3.3.11" diff --git a/plugins/dbgate-plugin-sqlite/src/frontend/driver.js b/plugins/dbgate-plugin-sqlite/src/frontend/driver.js index b1b99b2f6..8632d13c0 100644 --- a/plugins/dbgate-plugin-sqlite/src/frontend/driver.js +++ b/plugins/dbgate-plugin-sqlite/src/frontend/driver.js @@ -50,7 +50,14 @@ const driver = { singleDatabase: true, defaultDatabase: getDatabaseFileLabel(connection.databaseFile), }), - getQuerySplitterOptions: (usage) => (usage == 'stream' ? noSplitSplitterOptions : sqliteSplitterOptions), + + getQuerySplitterOptions: (usage) => + usage == 'editor' + ? { ...sqliteSplitterOptions, ignoreComments: true, preventSingleLineSplit: true } + : usage == 'stream' + ? noSplitSplitterOptions + : sqliteSplitterOptions, + // isFileDatabase: true, isElectronOnly: true, diff --git a/yarn.lock b/yarn.lock index ab0384f0c..f5a01b77e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3309,10 +3309,10 @@ dbgate-plugin-xml@^5.0.0-alpha.1: resolved "https://registry.yarnpkg.com/dbgate-plugin-xml/-/dbgate-plugin-xml-5.0.9.tgz#c3abf6ed8cd1450c45058d35c9326458833ed27e" integrity sha512-P8Em1A6HhF0BfxEDDEUyzdgFeJHEC5vbg12frANpWHjO3V1HGdygsT2z1ukLK8FS5BLW/vcCdOFldXZGh+wWvg== -dbgate-query-splitter@^4.9.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/dbgate-query-splitter/-/dbgate-query-splitter-4.9.0.tgz#37475929b76ebe60436fcc44f223d4d47d6483af" - integrity sha512-POifNiMDkeksA9YXaC82u5O6krYC21xyROoNjDh3ouKI4xeB37DG+cP/D4IdICWHYZudlgKiziQ4v3W+5+O1DA== +dbgate-query-splitter@^4.9.2: + version "4.9.2" + resolved "https://registry.yarnpkg.com/dbgate-query-splitter/-/dbgate-query-splitter-4.9.2.tgz#ab1a60e60887ca750dd263a59db66e82c6461a46" + integrity sha512-MwZzNNLILdUv8rg6mFysLizIEdZsLLHEOL4lAHrvLPtHaLOAb275ogtgieLqjcnsXkPlV03i2t1b697aQYdfLQ== debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" From e7ac7558ca090e84519954768dedd4600b448054 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 13:04:50 +0200 Subject: [PATCH 11/79] better UX of code editor --- packages/web/public/global.css | 6 ++ packages/web/src/query/AceEditor.svelte | 81 ++++++++++++++----------- 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/packages/web/public/global.css b/packages/web/public/global.css index 0108d0988..e211eef0b 100644 --- a/packages/web/public/global.css +++ b/packages/web/public/global.css @@ -190,4 +190,10 @@ textarea { .ace_gutter-cell.ace-gutter-sql-run:hover { background-color: var(--theme-bg-2); +} + +.ace_gutter-cell.ace-gutter-current-part { + /* background-color: var(--theme-bg-2); */ + font-weight: bold; + color: var(--theme-font-hover); } \ No newline at end of file diff --git a/packages/web/src/query/AceEditor.svelte b/packages/web/src/query/AceEditor.svelte index ace8344b0..c3e8af8a3 100644 --- a/packages/web/src/query/AceEditor.svelte +++ b/packages/web/src/query/AceEditor.svelte @@ -168,7 +168,8 @@ let queryParts = []; let currentPart = null; - let currentPartMarker = null; + let currentPartLines = []; + // let currentPartMarker = null; let queryParserWorker; @@ -194,19 +195,17 @@ } if (!editor) return { text: '' }; const selectedText = editor.getSelectedText(); - if (selectedText) + if (selectedText) { return { text: selectedText, line: editor.getSelectionRange().start.row, }; - if (editor.getHighlightActiveLine()) { - const line = editor.getSelectionRange().start.row; - return { - text: editor.session.getLine(line), - line, - }; } - return { text: '' }; + const line = editor.getSelectionRange().start.row; + return { + text: editor.session.getLine(line), + line, + }; } export function getCodeCompletionCommandText() { @@ -299,21 +298,27 @@ function processParserResult(data) { queryParts = data; - editor.setHighlightActiveLine(queryParts.length <= 1); + // editor.setHighlightActiveLine(queryParts.length <= 1); changedCurrentQueryPart(); updateAnnotations(); } function updateAnnotations() { if (!mode?.includes('sql')) return; - + editor?.session?.setAnnotations([ + ...currentPartLines.map(row => ({ + row, + className: 'ace-gutter-current-part', + })), ...(queryParts || []) - .filter(part => !(errorMessages || []).find(err => err.line == part.trimStart.line)) + .filter(part => !(errorMessages || []).find(err => err.line == part.trimStart?.line)) .map(part => ({ row: part.trimStart.line, text: part.text, - className: 'ace-gutter-sql-run', + className: currentPartLines.includes(part.trimStart.line) + ? 'ace-gutter-sql-run ace-gutter-current-part' + : 'ace-gutter-sql-run', })), ...(errorMessages || []).map(error => ({ row: error.line, @@ -364,21 +369,21 @@ } function changedCurrentQueryPart() { - if (queryParts.length <= 1) { - removeCurrentPartMarker(); - return; - } + // if (queryParts.length <= 1) { + // removeCurrentPartMarker(); + // return; + // } const selectionRange = editor.getSelectionRange(); - if ( - selectionRange.start.row != selectionRange.end.row || - selectionRange.start.column != selectionRange.end.column - ) { - removeCurrentPartMarker(); - currentPart = null; - return; - } + // if ( + // selectionRange.start.row != selectionRange.end.row || + // selectionRange.start.column != selectionRange.end.column + // ) { + // removeCurrentPartMarker(); + // currentPart = null; + // return; + // } const cursor = selectionRange.start; const part = queryParts.find( @@ -388,25 +393,30 @@ ); if (part?.text != currentPart?.text || part?.start?.position != currentPart?.start?.position) { - removeCurrentPartMarker(); + // removeCurrentPartMarker(); currentPart = part; + currentPartLines = []; if (currentPart) { const start = currentPart.trimStart || currentPart.start; const end = currentPart.trimEnd || currentPart.end; - currentPartMarker = editor - .getSession() - .addMarker(new ace.Range(start.line, start.column, end.line, end.column), 'ace_active-line', 'text'); + if (start && end) { + currentPartLines = _.range(start.line, end.line + 1); + } + // currentPartMarker = editor + // .getSession() + // .addMarker(new ace.Range(start.line, start.column, end.line, end.column), 'ace_active-line', 'text'); } + updateAnnotations(); } } - function removeCurrentPartMarker() { - if (currentPartMarker != null) { - editor.getSession().removeMarker(currentPartMarker); - currentPartMarker = null; - } - } + // function removeCurrentPartMarker() { + // if (currentPartMarker != null) { + // editor.getSession().removeMarker(currentPartMarker); + // currentPartMarker = null; + // } + // } onMount(() => { editor = ace.edit(EDITOR_ID); @@ -417,6 +427,7 @@ editor.getSession().setMode('ace/mode/' + mode); editor.setTheme('ace/theme/' + theme); editor.setValue(value, 1); + editor.setHighlightActiveLine(false); contentBackup = value; setEventCallBacks(); if (options) { From b2204e1d77743914360463fbcafb57585544d60b Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 13:21:21 +0200 Subject: [PATCH 12/79] using gutte3r decorations for active query part --- packages/web/src/query/AceEditor.svelte | 37 +++++++++++++------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/web/src/query/AceEditor.svelte b/packages/web/src/query/AceEditor.svelte index c3e8af8a3..b094ab8ad 100644 --- a/packages/web/src/query/AceEditor.svelte +++ b/packages/web/src/query/AceEditor.svelte @@ -307,18 +307,14 @@ if (!mode?.includes('sql')) return; editor?.session?.setAnnotations([ - ...currentPartLines.map(row => ({ - row, - className: 'ace-gutter-current-part', - })), ...(queryParts || []) - .filter(part => !(errorMessages || []).find(err => err.line == part.trimStart?.line)) + .filter(part => !(errorMessages || []).find(err => err.line == part.trimStart.line)) .map(part => ({ row: part.trimStart.line, text: part.text, className: currentPartLines.includes(part.trimStart.line) ? 'ace-gutter-sql-run ace-gutter-current-part' - : 'ace-gutter-sql-run', + : 'ace-gutter-sql-run', // className: 'ace-gutter-sql-run', })), ...(errorMessages || []).map(error => ({ row: error.line, @@ -369,10 +365,10 @@ } function changedCurrentQueryPart() { - // if (queryParts.length <= 1) { - // removeCurrentPartMarker(); - // return; - // } + if (queryParts.length <= 1) { + removeCurrentPartMarker(); + return; + } const selectionRange = editor.getSelectionRange(); @@ -393,15 +389,20 @@ ); if (part?.text != currentPart?.text || part?.start?.position != currentPart?.start?.position) { - // removeCurrentPartMarker(); + removeCurrentPartMarker(); currentPart = part; - currentPartLines = []; if (currentPart) { const start = currentPart.trimStart || currentPart.start; const end = currentPart.trimEnd || currentPart.end; if (start && end) { currentPartLines = _.range(start.line, end.line + 1); + for (const row of currentPartLines) { + if ((queryParts || []).find(part => part.trimStart.line == row)) { + continue; + } + editor.getSession().addGutterDecoration(row, 'ace-gutter-current-part'); + } } // currentPartMarker = editor // .getSession() @@ -411,12 +412,12 @@ } } - // function removeCurrentPartMarker() { - // if (currentPartMarker != null) { - // editor.getSession().removeMarker(currentPartMarker); - // currentPartMarker = null; - // } - // } + function removeCurrentPartMarker() { + for (const row of currentPartLines) { + editor.getSession().removeGutterDecoration(row, 'ace-gutter-current-part'); + } + currentPartLines = []; + } onMount(() => { editor = ace.edit(EDITOR_ID); From 53b6b71a29a7e0fdea2f8f5769d6de25b863f300 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 16:56:47 +0200 Subject: [PATCH 13/79] non default schema name in tab title --- packages/tools/src/driverBase.ts | 1 + packages/types/dialect.d.ts | 1 + .../web/src/appobj/DatabaseObjectAppObject.svelte | 14 ++++++++++++-- plugins/dbgate-plugin-mssql/src/frontend/driver.js | 1 + .../dbgate-plugin-postgres/src/frontend/drivers.js | 1 + 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/tools/src/driverBase.ts b/packages/tools/src/driverBase.ts index 9037df9b2..76d2dc202 100644 --- a/packages/tools/src/driverBase.ts +++ b/packages/tools/src/driverBase.ts @@ -16,6 +16,7 @@ const dialect = { isSparse: false, isPersisted: false, }, + defaultSchemaName: null, }; export const driverBase = { diff --git a/packages/types/dialect.d.ts b/packages/types/dialect.d.ts index 70b241f1f..a8a1b2378 100644 --- a/packages/types/dialect.d.ts +++ b/packages/types/dialect.d.ts @@ -9,6 +9,7 @@ export interface SqlDialect { fallbackDataType?: string; explicitDropConstraint?: boolean; anonymousPrimaryKey?: boolean; + defaultSchemaName?: string; enableConstraintsPerTable?: boolean; dropColumnDependencies?: string[]; diff --git a/packages/web/src/appobj/DatabaseObjectAppObject.svelte b/packages/web/src/appobj/DatabaseObjectAppObject.svelte index b13cabf6e..a6cceab66 100644 --- a/packages/web/src/appobj/DatabaseObjectAppObject.svelte +++ b/packages/web/src/appobj/DatabaseObjectAppObject.svelte @@ -9,7 +9,7 @@ schemaName, ...(columns?.map(({ columnName }) => ({ childName: columnName })) || []) ); - export const createTitle = ({ pureName }) => pureName; + export const createTitle = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName); export const databaseObjectIcons = { tables: 'img table', @@ -586,6 +586,16 @@ } } + function getObjectTitle(connection, schemaName, pureName) { + const driver = findEngineDriver(connection, getExtensions()); + + const defaultSchema = driver?.dialect?.defaultSchemaName; + if (schemaName && defaultSchema && schemaName != defaultSchema) { + return `${schemaName}.${pureName}`; + } + return pureName; + } + export async function openDatabaseObjectDetail( tabComponent, scriptTemplate, @@ -603,7 +613,7 @@ openNewTab( { - title: scriptTemplate ? 'Query #' : pureName, + title: scriptTemplate ? 'Query #' : getObjectTitle(connection, schemaName, pureName), tooltip, icon: icon || (scriptTemplate ? 'img sql-file' : databaseObjectIcons[objectTypeField]), tabComponent: scriptTemplate ? 'QueryTab' : tabComponent, diff --git a/plugins/dbgate-plugin-mssql/src/frontend/driver.js b/plugins/dbgate-plugin-mssql/src/frontend/driver.js index ae1b15c7d..7e13dfa4a 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/driver.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/driver.js @@ -10,6 +10,7 @@ const dialect = { rangeSelect: true, offsetFetchRangeSyntax: true, rowNumberOverPaging: true, + defaultSchemaName: 'dbo', stringEscapeChar: "'", fallbackDataType: 'nvarchar(max)', explicitDropConstraint: false, diff --git a/plugins/dbgate-plugin-postgres/src/frontend/drivers.js b/plugins/dbgate-plugin-postgres/src/frontend/drivers.js index fe1c88879..3c1ac0627 100644 --- a/plugins/dbgate-plugin-postgres/src/frontend/drivers.js +++ b/plugins/dbgate-plugin-postgres/src/frontend/drivers.js @@ -8,6 +8,7 @@ const spatialTypes = ['GEOGRAPHY']; const dialect = { rangeSelect: true, ilike: true, + defaultSchemaName: 'public', // stringEscapeChar: '\\', stringEscapeChar: "'", fallbackDataType: 'varchar', From 18b7792370bad6ef74dc3e6d011af212e50ebb75 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 18:11:46 +0200 Subject: [PATCH 14/79] customizable redis key separator #379 --- .../src/settings/ConnectionDriverFields.svelte | 4 ++++ .../dbgate-plugin-redis/src/backend/driver.js | 17 +++++++++-------- .../dbgate-plugin-redis/src/frontend/driver.js | 4 ++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/web/src/settings/ConnectionDriverFields.svelte b/packages/web/src/settings/ConnectionDriverFields.svelte index 9f9310916..ebe5e5186 100644 --- a/packages/web/src/settings/ConnectionDriverFields.svelte +++ b/packages/web/src/settings/ConnectionDriverFields.svelte @@ -173,6 +173,10 @@ /> {/if} +{#if driver?.showConnectionField('treeKeySeparator', $values)} + +{/if} + {#if driver?.showConnectionField('windowsDomain', $values)} {/if} diff --git a/plugins/dbgate-plugin-redis/src/backend/driver.js b/plugins/dbgate-plugin-redis/src/backend/driver.js index 19728f1e6..ab03f55fa 100644 --- a/plugins/dbgate-plugin-redis/src/backend/driver.js +++ b/plugins/dbgate-plugin-redis/src/backend/driver.js @@ -81,7 +81,7 @@ function splitCommandLine(str) { const driver = { ...driverBase, analyserClass: Analyser, - async connect({ server, port, password, database, useDatabaseUrl, databaseUrl }) { + async connect({ server, port, password, database, useDatabaseUrl, databaseUrl, treeKeySeparator }) { let db = 0; let pool; if (useDatabaseUrl) { @@ -95,6 +95,7 @@ const driver = { password, db, }); + pool.__treeKeySeparator = treeKeySeparator || ':'; } return pool; @@ -146,7 +147,7 @@ const driver = { info .split('\n') .filter((x) => x.trim() && !x.trim().startsWith('#')) - .map((x) => x.split(':')) + .map((x) => x.split(pool.__treeKeySeparator)) ); }, async getVersion(pool) { @@ -164,7 +165,7 @@ const driver = { }, async loadKeys(pool, root = '', filter = null) { - const keys = await this.getKeys(pool, root ? `${root}:*` : '*'); + const keys = await this.getKeys(pool, root ? `${root}${pool.__treeKeySeparator}*` : '*'); const keysFiltered = keys.filter((x) => filterName(filter, x)); const res = this.extractKeysFromLevel(root, keysFiltered); await this.enrichKeyInfo(pool, res); @@ -197,12 +198,12 @@ const driver = { }, extractKeysFromLevel(root, keys) { - const prefix = root ? `${root}:` : ''; - const rootSplit = _.compact(root.split(':')); + const prefix = root ? `${root}${pool.__treeKeySeparator}` : ''; + const rootSplit = _.compact(root.split(pool.__treeKeySeparator)); const res = {}; for (const key of keys) { if (!key.startsWith(prefix)) continue; - const keySplit = key.split(':'); + const keySplit = key.split(pool.__treeKeySeparator); if (keySplit.length > rootSplit.length) { const text = keySplit[rootSplit.length]; if (keySplit.length == rootSplit.length + 1) { @@ -216,9 +217,9 @@ const driver = { res[dctKey].count++; } else { res[dctKey] = { - text: text + ':*', + text: text + pool.__treeKeySeparator + '*', type: 'dir', - root: keySplit.slice(0, rootSplit.length + 1).join(':'), + root: keySplit.slice(0, rootSplit.length + 1).join(pool.__treeKeySeparator), count: 1, }; } diff --git a/plugins/dbgate-plugin-redis/src/frontend/driver.js b/plugins/dbgate-plugin-redis/src/frontend/driver.js index f05e14e7f..95df6fd82 100644 --- a/plugins/dbgate-plugin-redis/src/frontend/driver.js +++ b/plugins/dbgate-plugin-redis/src/frontend/driver.js @@ -78,9 +78,9 @@ const driver = { showConnectionField: (field, values) => { if (field == 'useDatabaseUrl') return true; if (values.useDatabaseUrl) { - return ['databaseUrl', 'isReadOnly'].includes(field); + return ['databaseUrl', 'isReadOnly', 'treeKeySeparator'].includes(field); } - return ['server', 'port', 'password', 'isReadOnly'].includes(field); + return ['server', 'port', 'password', 'isReadOnly', 'treeKeySeparator'].includes(field); }, showConnectionTab: (field) => field == 'sshTunnel', From 9ed1cdf4b78577175c933c983f2f5ddcbd3f3c60 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 18:18:03 +0200 Subject: [PATCH 15/79] redis key separator fix #379 --- plugins/dbgate-plugin-redis/src/backend/driver.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/dbgate-plugin-redis/src/backend/driver.js b/plugins/dbgate-plugin-redis/src/backend/driver.js index ab03f55fa..eec7d4ca7 100644 --- a/plugins/dbgate-plugin-redis/src/backend/driver.js +++ b/plugins/dbgate-plugin-redis/src/backend/driver.js @@ -147,7 +147,7 @@ const driver = { info .split('\n') .filter((x) => x.trim() && !x.trim().startsWith('#')) - .map((x) => x.split(pool.__treeKeySeparator)) + .map((x) => x.split(':')) ); }, async getVersion(pool) { @@ -167,7 +167,7 @@ const driver = { async loadKeys(pool, root = '', filter = null) { const keys = await this.getKeys(pool, root ? `${root}${pool.__treeKeySeparator}*` : '*'); const keysFiltered = keys.filter((x) => filterName(filter, x)); - const res = this.extractKeysFromLevel(root, keysFiltered); + const res = this.extractKeysFromLevel(pool, root, keysFiltered); await this.enrichKeyInfo(pool, res); return res; }, @@ -197,7 +197,7 @@ const driver = { return res; }, - extractKeysFromLevel(root, keys) { + extractKeysFromLevel(pool, root, keys) { const prefix = root ? `${root}${pool.__treeKeySeparator}` : ''; const rootSplit = _.compact(root.split(pool.__treeKeySeparator)); const res = {}; From da00e1c22801763a001152f60189b6abaa245a3e Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:02:13 +0200 Subject: [PATCH 16/79] docker build with tags --- .github/workflows/build-docker-beta.yaml | 74 +++++++++++++++++++----- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml index ea0e52263..fd43789e9 100644 --- a/.github/workflows/build-docker-beta.yaml +++ b/.github/workflows/build-docker-beta.yaml @@ -40,19 +40,61 @@ jobs: - name: Prepare docker image run: | yarn run prepare:docker - - name: Build docker image - run: | - docker build ./docker -t dbgate - - name: Push docker image - run: | - docker tag dbgate dbgate/dbgate:beta - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker push dbgate/dbgate:beta - - name: Build alpine docker image - run: | - docker build ./docker -t dbgate -f docker/Dockerfile-alpine - - name: Push alpine docker image - run: | - docker tag dbgate dbgate/dbgate:beta-alpine - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker push dbgate/dbgate:beta-alpine + + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + dbgate/dbgate + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + + - name: Build docker + uses: mr-smithers-excellent/docker-build-push@v3 + with: + image: dbgate/dbgate + registry: docker.io + dockerfile: docker/Dockerfile + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + tags: ${{ steps.meta.outputs.tags }},beta + + - name: Docker alpine meta + id: alpmeta + uses: docker/metadata-action@v4 + with: + images: | + dbgate/dbgate + tags: | + type=semver,pattern={{version}},suffix=-alpine + type=semver,pattern={{major}}.{{minor}},suffix=-alpine + + # - name: Build docker image + # run: | + # docker build ./docker -t dbgate + # - name: Push docker image + # run: | + # docker tag dbgate dbgate/dbgate:beta + # docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + # docker push dbgate/dbgate:beta + + # - name: Build alpine docker image + # run: | + # docker build ./docker -t dbgate -f docker/Dockerfile-alpine + # - name: Push alpine docker image + # run: | + # docker tag dbgate dbgate/dbgate:beta-alpine + # docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + # docker push dbgate/dbgate:beta-alpine + + - name: Build docker + uses: mr-smithers-excellent/docker-build-push@v3 + with: + image: dbgate/dbgate + registry: docker.io + dockerfile: docker/Dockerfile-alpine + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + tags: ${{ steps.alpmeta.outputs.tags }},beta From dac11104048efeb232ec4c4bd51d31547f70576c Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:02:43 +0200 Subject: [PATCH 17/79] v5.1.4-docker.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80ffdd1e0..d6469dcb5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.3", + "version": "5.1.4-docker.1", "name": "dbgate-all", "workspaces": [ "packages/*", From f157fc77d4a874a81c0dfb6a97e776b5dfbbb191 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:18:30 +0200 Subject: [PATCH 18/79] docker fix --- .github/workflows/build-docker-beta.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml index fd43789e9..5cd72b18a 100644 --- a/.github/workflows/build-docker-beta.yaml +++ b/.github/workflows/build-docker-beta.yaml @@ -48,8 +48,8 @@ jobs: images: | dbgate/dbgate tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{version}},suffix=-beta + type=semver,pattern={{major}}.{{minor}},suffix=-beta - name: Build docker uses: mr-smithers-excellent/docker-build-push@v3 @@ -68,8 +68,8 @@ jobs: images: | dbgate/dbgate tags: | - type=semver,pattern={{version}},suffix=-alpine - type=semver,pattern={{major}}.{{minor}},suffix=-alpine + type=semver,pattern={{version}},suffix=-beta-alpine + type=semver,pattern={{major}}.{{minor}},suffix=-beta-alpine # - name: Build docker image # run: | @@ -97,4 +97,4 @@ jobs: dockerfile: docker/Dockerfile-alpine username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - tags: ${{ steps.alpmeta.outputs.tags }},beta + tags: ${{ steps.alpmeta.outputs.tags }},beta-alpine From 45e54475d0cb67a4ede21f47428af78f24a2a103 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:22:41 +0200 Subject: [PATCH 19/79] docker build --- .github/workflows/build-docker-beta.yaml | 78 +++++++++++++----------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml index 5cd72b18a..e21baf7ff 100644 --- a/.github/workflows/build-docker-beta.yaml +++ b/.github/workflows/build-docker-beta.yaml @@ -49,17 +49,7 @@ jobs: dbgate/dbgate tags: | type=semver,pattern={{version}},suffix=-beta - type=semver,pattern={{major}}.{{minor}},suffix=-beta - - - name: Build docker - uses: mr-smithers-excellent/docker-build-push@v3 - with: - image: dbgate/dbgate - registry: docker.io - dockerfile: docker/Dockerfile - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - tags: ${{ steps.meta.outputs.tags }},beta +# type=semver,pattern={{major}}.{{minor}},suffix=-beta - name: Docker alpine meta id: alpmeta @@ -69,32 +59,46 @@ jobs: dbgate/dbgate tags: | type=semver,pattern={{version}},suffix=-beta-alpine - type=semver,pattern={{major}}.{{minor}},suffix=-beta-alpine +# type=semver,pattern={{major}}.{{minor}},suffix=-beta-alpine - # - name: Build docker image - # run: | - # docker build ./docker -t dbgate - # - name: Push docker image - # run: | - # docker tag dbgate dbgate/dbgate:beta - # docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - # docker push dbgate/dbgate:beta + - name: Build docker image + run: | + docker build ./docker -t dbgate + - name: Push docker image + run: | + docker tag dbgate dbgate/dbgate:beta + docker tag dbgate ${{ steps.meta.outputs.tags }} + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker push dbgate/dbgate:beta + docker push ${{ steps.meta.outputs.tags }} - # - name: Build alpine docker image - # run: | - # docker build ./docker -t dbgate -f docker/Dockerfile-alpine - # - name: Push alpine docker image - # run: | - # docker tag dbgate dbgate/dbgate:beta-alpine - # docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - # docker push dbgate/dbgate:beta-alpine + - name: Build alpine docker image + run: | + docker build ./docker -t dbgate -f docker/Dockerfile-alpine + - name: Push alpine docker image + run: | + docker tag dbgate dbgate/dbgate:beta-alpine + docker tag dbgate ${{ steps.alpmeta.outputs.tags }} + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker push dbgate/dbgate:beta-alpine + docker push ${{ steps.alpmeta.outputs.tags }} - - name: Build docker - uses: mr-smithers-excellent/docker-build-push@v3 - with: - image: dbgate/dbgate - registry: docker.io - dockerfile: docker/Dockerfile-alpine - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - tags: ${{ steps.alpmeta.outputs.tags }},beta-alpine + # - name: Build docker + # uses: mr-smithers-excellent/docker-build-push@v3 + # with: + # image: dbgate/dbgate + # registry: docker.io + # dockerfile: docker/Dockerfile + # username: ${{ secrets.DOCKER_USERNAME }} + # password: ${{ secrets.DOCKER_PASSWORD }} + # tags: ${{ steps.meta.outputs.tags }},beta + + # - name: Build docker alpine + # uses: mr-smithers-excellent/docker-build-push@v3 + # with: + # image: dbgate/dbgate + # registry: docker.io + # dockerfile: docker/Dockerfile-alpine + # username: ${{ secrets.DOCKER_USERNAME }} + # password: ${{ secrets.DOCKER_PASSWORD }} + # tags: ${{ steps.alpmeta.outputs.tags }},beta-alpine From 074390ac119982bf9031db034ee8868e4f8250d4 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:22:56 +0200 Subject: [PATCH 20/79] v5.1.4-docker.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6469dcb5..a66d507d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.4-docker.1", + "version": "5.1.4-docker.2", "name": "dbgate-all", "workspaces": [ "packages/*", From c9bf949d025aa67c2f0d77701584494422ac8881 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:37:57 +0200 Subject: [PATCH 21/79] version fix --- .github/workflows/build-docker-beta.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml index e21baf7ff..8ce9ea0a8 100644 --- a/.github/workflows/build-docker-beta.yaml +++ b/.github/workflows/build-docker-beta.yaml @@ -48,8 +48,7 @@ jobs: images: | dbgate/dbgate tags: | - type=semver,pattern={{version}},suffix=-beta -# type=semver,pattern={{major}}.{{minor}},suffix=-beta + type=match,pattern=\d+.\d+.\d+,suffix=-beta - name: Docker alpine meta id: alpmeta @@ -58,8 +57,7 @@ jobs: images: | dbgate/dbgate tags: | - type=semver,pattern={{version}},suffix=-beta-alpine -# type=semver,pattern={{major}}.{{minor}},suffix=-beta-alpine + type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine - name: Build docker image run: | From b4d0ccbd8cd93bf4efb05117ed3343df5b7e151f Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:40:06 +0200 Subject: [PATCH 22/79] v5.1.4-docker.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a66d507d6..9a76b893f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.4-docker.2", + "version": "5.1.4-docker.3", "name": "dbgate-all", "workspaces": [ "packages/*", From 643b792069d0b579407a31fcacd543c6148e46ff Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:43:54 +0200 Subject: [PATCH 23/79] docker for arm platform #383 --- .github/workflows/build-docker-beta.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml index 8ce9ea0a8..bbc69cd63 100644 --- a/.github/workflows/build-docker-beta.yaml +++ b/.github/workflows/build-docker-beta.yaml @@ -61,7 +61,7 @@ jobs: - name: Build docker image run: | - docker build ./docker -t dbgate + docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 ./docker -t dbgate - name: Push docker image run: | docker tag dbgate dbgate/dbgate:beta @@ -72,7 +72,7 @@ jobs: - name: Build alpine docker image run: | - docker build ./docker -t dbgate -f docker/Dockerfile-alpine + docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 ./docker -t dbgate -f docker/Dockerfile-alpine - name: Push alpine docker image run: | docker tag dbgate dbgate/dbgate:beta-alpine From bf9a933fb1dcb154106fb1d06ca7ae74f4a26fde Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:44:09 +0200 Subject: [PATCH 24/79] v5.1.4-docker.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9a76b893f..8d162fecb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.4-docker.3", + "version": "5.1.4-docker.4", "name": "dbgate-all", "workspaces": [ "packages/*", From 975643fb24643b41ea1bb06b33ef4ef9d0d94761 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:58:14 +0200 Subject: [PATCH 25/79] fix --- .github/workflows/build-docker-beta.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml index bbc69cd63..1d5feeab2 100644 --- a/.github/workflows/build-docker-beta.yaml +++ b/.github/workflows/build-docker-beta.yaml @@ -49,6 +49,7 @@ jobs: dbgate/dbgate tags: | type=match,pattern=\d+.\d+.\d+,suffix=-beta + type=raw,value=latest,enable=false - name: Docker alpine meta id: alpmeta @@ -58,10 +59,11 @@ jobs: dbgate/dbgate tags: | type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine + type=raw,value=latest,enable=false - name: Build docker image run: | - docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 ./docker -t dbgate + docker build ./docker -t dbgate - name: Push docker image run: | docker tag dbgate dbgate/dbgate:beta @@ -72,7 +74,7 @@ jobs: - name: Build alpine docker image run: | - docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 ./docker -t dbgate -f docker/Dockerfile-alpine + docker build ./docker -t dbgate -f docker/Dockerfile-alpine - name: Push alpine docker image run: | docker tag dbgate dbgate/dbgate:beta-alpine From 140291696b42f37c580264a86bdf15116e131568 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 19:58:29 +0200 Subject: [PATCH 26/79] v5.1.4-docker.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8d162fecb..3c9d672e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.4-docker.4", + "version": "5.1.4-docker.5", "name": "dbgate-all", "workspaces": [ "packages/*", From a2906cca9d9895fed257053b864bd45e5c2e4263 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 20:09:05 +0200 Subject: [PATCH 27/79] docker build fix --- .github/workflows/build-docker-beta.yaml | 100 +++++++++++++++-------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml index 1d5feeab2..a3867f051 100644 --- a/.github/workflows/build-docker-beta.yaml +++ b/.github/workflows/build-docker-beta.yaml @@ -25,6 +25,31 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 1 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + dbgate/dbgate + flavor: | + latest=false + tags: | + type=match,pattern=\d+.\d+.\d+,suffix=-beta + type=raw,value=beta + + - name: Docker alpine meta + id: alpmeta + uses: docker/metadata-action@v4 + with: + images: | + dbgate/dbgate + flavor: | + latest=false + tags: | + type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine + type=raw,value=beta-alpine + - name: Use Node.js 14.x uses: actions/setup-node@v1 with: @@ -41,47 +66,50 @@ jobs: run: | yarn run prepare:docker - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to DockerHub + uses: docker/login-action@v2 with: - images: | - dbgate/dbgate - tags: | - type=match,pattern=\d+.\d+.\d+,suffix=-beta - type=raw,value=latest,enable=false + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} - - name: Docker alpine meta - id: alpmeta - uses: docker/metadata-action@v4 + - name: Build and push + uses: docker/build-push-action@v3 with: - images: | - dbgate/dbgate - tags: | - type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine - type=raw,value=latest,enable=false + push: true + context: ./docker + tags: ${{ steps.meta.outputs.tags }} - - name: Build docker image - run: | - docker build ./docker -t dbgate - - name: Push docker image - run: | - docker tag dbgate dbgate/dbgate:beta - docker tag dbgate ${{ steps.meta.outputs.tags }} - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker push dbgate/dbgate:beta - docker push ${{ steps.meta.outputs.tags }} + - name: Build and push alpine + uses: docker/build-push-action@v3 + with: + push: true + context: ./docker + file: ./docker/Dockerfile-alpine + tags: ${{ steps.meta.outputs.tags }} - - name: Build alpine docker image - run: | - docker build ./docker -t dbgate -f docker/Dockerfile-alpine - - name: Push alpine docker image - run: | - docker tag dbgate dbgate/dbgate:beta-alpine - docker tag dbgate ${{ steps.alpmeta.outputs.tags }} - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker push dbgate/dbgate:beta-alpine - docker push ${{ steps.alpmeta.outputs.tags }} + # - name: Build docker image + # run: | + # docker build ./docker -t dbgate + # - name: Push docker image + # run: | + # docker tag dbgate dbgate/dbgate:beta + # docker tag dbgate ${{ steps.meta.outputs.tags }} + # docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + # docker push dbgate/dbgate:beta + # docker push ${{ steps.meta.outputs.tags }} + + # - name: Build alpine docker image + # run: | + # docker build ./docker -t dbgate -f docker/Dockerfile-alpine + # - name: Push alpine docker image + # run: | + # docker tag dbgate dbgate/dbgate:beta-alpine + # docker tag dbgate ${{ steps.alpmeta.outputs.tags }} + # docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + # docker push dbgate/dbgate:beta-alpine + # docker push ${{ steps.alpmeta.outputs.tags }} # - name: Build docker # uses: mr-smithers-excellent/docker-build-push@v3 From dba3183c94c6b88ac0913eca543711b25762089c Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 20:09:19 +0200 Subject: [PATCH 28/79] v5.1.4-docker.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3c9d672e2..6d7b3ec73 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.4-docker.5", + "version": "5.1.4-docker.6", "name": "dbgate-all", "workspaces": [ "packages/*", From bbd00ac94d07ff7a345e4224f18e600323dd4ad1 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 20:12:25 +0200 Subject: [PATCH 29/79] multi platform --- .github/workflows/build-docker-beta.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml index a3867f051..1eb3f2d35 100644 --- a/.github/workflows/build-docker-beta.yaml +++ b/.github/workflows/build-docker-beta.yaml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 1 - + - name: Docker meta id: meta uses: docker/metadata-action@v4 @@ -80,6 +80,7 @@ jobs: push: true context: ./docker tags: ${{ steps.meta.outputs.tags }} + platforms: linux/amd64,linux/arm64,linux/arm/v7 - name: Build and push alpine uses: docker/build-push-action@v3 @@ -88,6 +89,7 @@ jobs: context: ./docker file: ./docker/Dockerfile-alpine tags: ${{ steps.meta.outputs.tags }} + platforms: linux/amd64,linux/arm64,linux/arm/v7 # - name: Build docker image # run: | From cdd43822661ebc6e2e5b3e7bab027de3a2fb6b15 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 20:12:37 +0200 Subject: [PATCH 30/79] v5.1.4-docker.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d7b3ec73..6ea441c1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.4-docker.6", + "version": "5.1.4-docker.7", "name": "dbgate-all", "workspaces": [ "packages/*", From 5aff68d313b0435683b7735131dc99aff42c88e8 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 20:37:32 +0200 Subject: [PATCH 31/79] single docker build file --- .github/workflows/build-docker-beta.yaml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml index 1eb3f2d35..8ef4c4604 100644 --- a/.github/workflows/build-docker-beta.yaml +++ b/.github/workflows/build-docker-beta.yaml @@ -35,8 +35,11 @@ jobs: flavor: | latest=false tags: | - type=match,pattern=\d+.\d+.\d+,suffix=-beta - type=raw,value=beta + type=match,pattern=\d+.\d+.\d+,suffix=-beta,enable=${{ includes(tag, '-docker.') || includes(tag, '-beta.') }} + type=raw,value=beta,enable=${{ includes(tag, '-docker.') || includes(tag, '-beta.') }} + + type=match,pattern=\d+.\d+.\d+,enable=${{ !includes(tag, '-docker.') && !includes(tag, '-beta.') }} + type=raw,value=latest,enable=${{ !includes(tag, '-docker.') && !includes(tag, '-beta.') }} - name: Docker alpine meta id: alpmeta @@ -47,8 +50,11 @@ jobs: flavor: | latest=false tags: | - type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine - type=raw,value=beta-alpine + type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine,enable=${{ includes(tag, '-docker.') || includes(tag, '-beta.') }} + type=raw,value=beta-alpine,enable=${{ includes(tag, '-docker.') || includes(tag, '-beta.') }} + + type=match,pattern=\d+.\d+.\d+,suffix=-alpine,enable=${{ !includes(tag, '-docker.') && !includes(tag, '-beta.') }} + type=raw,value=alpine,enable=${{ !includes(tag, '-docker.') && !includes(tag, '-beta.') }} - name: Use Node.js 14.x uses: actions/setup-node@v1 @@ -88,7 +94,7 @@ jobs: push: true context: ./docker file: ./docker/Dockerfile-alpine - tags: ${{ steps.meta.outputs.tags }} + tags: ${{ steps.alpmeta.outputs.tags }} platforms: linux/amd64,linux/arm64,linux/arm/v7 # - name: Build docker image From 4892e4679587abee88320b6390aa801aa849437b Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 20:37:42 +0200 Subject: [PATCH 32/79] v5.1.4-docker.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6ea441c1a..224a8f443 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.4-docker.7", + "version": "5.1.4-docker.8", "name": "dbgate-all", "workspaces": [ "packages/*", From 1dfdeed0181a1f2887fc94ffa6ff99bcc609f524 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 20:42:59 +0200 Subject: [PATCH 33/79] actions fix --- .github/workflows/build-docker-beta.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml index 8ef4c4604..88e707feb 100644 --- a/.github/workflows/build-docker-beta.yaml +++ b/.github/workflows/build-docker-beta.yaml @@ -35,11 +35,11 @@ jobs: flavor: | latest=false tags: | - type=match,pattern=\d+.\d+.\d+,suffix=-beta,enable=${{ includes(tag, '-docker.') || includes(tag, '-beta.') }} - type=raw,value=beta,enable=${{ includes(tag, '-docker.') || includes(tag, '-beta.') }} + type=match,pattern=\d+.\d+.\d+,suffix=-beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} + type=raw,value=beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} - type=match,pattern=\d+.\d+.\d+,enable=${{ !includes(tag, '-docker.') && !includes(tag, '-beta.') }} - type=raw,value=latest,enable=${{ !includes(tag, '-docker.') && !includes(tag, '-beta.') }} + type=match,pattern=\d+.\d+.\d+,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} + type=raw,value=latest,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} - name: Docker alpine meta id: alpmeta @@ -50,11 +50,11 @@ jobs: flavor: | latest=false tags: | - type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine,enable=${{ includes(tag, '-docker.') || includes(tag, '-beta.') }} - type=raw,value=beta-alpine,enable=${{ includes(tag, '-docker.') || includes(tag, '-beta.') }} + type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} + type=raw,value=beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} - type=match,pattern=\d+.\d+.\d+,suffix=-alpine,enable=${{ !includes(tag, '-docker.') && !includes(tag, '-beta.') }} - type=raw,value=alpine,enable=${{ !includes(tag, '-docker.') && !includes(tag, '-beta.') }} + type=match,pattern=\d+.\d+.\d+,suffix=-alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} + type=raw,value=alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} - name: Use Node.js 14.x uses: actions/setup-node@v1 From 4de8a5b0386dbd7d2f0e811458d5691d438f64a3 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 20:44:32 +0200 Subject: [PATCH 34/79] v5.1.4-docker.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 224a8f443..7c457074a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.4-docker.8", + "version": "5.1.4-docker.9", "name": "dbgate-all", "workspaces": [ "packages/*", From 04d39f664628165f0b8fae7cd8b60a05188f540c Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 28 Sep 2022 20:47:32 +0200 Subject: [PATCH 35/79] single docker builder --- .github/workflows/build-docker-beta.yaml | 140 ----------------------- .github/workflows/build-docker.yaml | 84 ++++++++++---- 2 files changed, 60 insertions(+), 164 deletions(-) delete mode 100644 .github/workflows/build-docker-beta.yaml diff --git a/.github/workflows/build-docker-beta.yaml b/.github/workflows/build-docker-beta.yaml deleted file mode 100644 index 88e707feb..000000000 --- a/.github/workflows/build-docker-beta.yaml +++ /dev/null @@ -1,140 +0,0 @@ -name: Docker image BETA - -# on: [push] - -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+' - - 'v[0-9]+.[0-9]+.[0-9]+-docker.[0-9]+' - -jobs: - build: - - runs-on: ${{ matrix.os }} - - strategy: - matrix: - os: [ubuntu-18.04] - - steps: - - name: Context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: | - dbgate/dbgate - flavor: | - latest=false - tags: | - type=match,pattern=\d+.\d+.\d+,suffix=-beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} - type=raw,value=beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} - - type=match,pattern=\d+.\d+.\d+,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} - type=raw,value=latest,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} - - - name: Docker alpine meta - id: alpmeta - uses: docker/metadata-action@v4 - with: - images: | - dbgate/dbgate - flavor: | - latest=false - tags: | - type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} - type=raw,value=beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} - - type=match,pattern=\d+.\d+.\d+,suffix=-alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} - type=raw,value=alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} - - - name: Use Node.js 14.x - uses: actions/setup-node@v1 - with: - node-version: 14.x - - name: yarn install - run: | - # yarn --version - # yarn config set network-timeout 300000 - yarn install - - name: setCurrentVersion - run: | - yarn setCurrentVersion - - name: Prepare docker image - run: | - yarn run prepare:docker - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build and push - uses: docker/build-push-action@v3 - with: - push: true - context: ./docker - tags: ${{ steps.meta.outputs.tags }} - platforms: linux/amd64,linux/arm64,linux/arm/v7 - - - name: Build and push alpine - uses: docker/build-push-action@v3 - with: - push: true - context: ./docker - file: ./docker/Dockerfile-alpine - tags: ${{ steps.alpmeta.outputs.tags }} - platforms: linux/amd64,linux/arm64,linux/arm/v7 - - # - name: Build docker image - # run: | - # docker build ./docker -t dbgate - # - name: Push docker image - # run: | - # docker tag dbgate dbgate/dbgate:beta - # docker tag dbgate ${{ steps.meta.outputs.tags }} - # docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - # docker push dbgate/dbgate:beta - # docker push ${{ steps.meta.outputs.tags }} - - # - name: Build alpine docker image - # run: | - # docker build ./docker -t dbgate -f docker/Dockerfile-alpine - # - name: Push alpine docker image - # run: | - # docker tag dbgate dbgate/dbgate:beta-alpine - # docker tag dbgate ${{ steps.alpmeta.outputs.tags }} - # docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - # docker push dbgate/dbgate:beta-alpine - # docker push ${{ steps.alpmeta.outputs.tags }} - - # - name: Build docker - # uses: mr-smithers-excellent/docker-build-push@v3 - # with: - # image: dbgate/dbgate - # registry: docker.io - # dockerfile: docker/Dockerfile - # username: ${{ secrets.DOCKER_USERNAME }} - # password: ${{ secrets.DOCKER_PASSWORD }} - # tags: ${{ steps.meta.outputs.tags }},beta - - # - name: Build docker alpine - # uses: mr-smithers-excellent/docker-build-push@v3 - # with: - # image: dbgate/dbgate - # registry: docker.io - # dockerfile: docker/Dockerfile-alpine - # username: ${{ secrets.DOCKER_USERNAME }} - # password: ${{ secrets.DOCKER_PASSWORD }} - # tags: ${{ steps.alpmeta.outputs.tags }},beta-alpine diff --git a/.github/workflows/build-docker.yaml b/.github/workflows/build-docker.yaml index df9729ae7..cfd88b948 100644 --- a/.github/workflows/build-docker.yaml +++ b/.github/workflows/build-docker.yaml @@ -1,17 +1,11 @@ name: Docker image -# on: [push] - on: push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' - # - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 - -# on: -# push: -# branches: -# - production + - 'v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+-docker.[0-9]+' jobs: build: @@ -30,12 +24,45 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 1 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + dbgate/dbgate + flavor: | + latest=false + tags: | + type=match,pattern=\d+.\d+.\d+,suffix=-beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} + type=raw,value=beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} + + type=match,pattern=\d+.\d+.\d+,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} + type=raw,value=latest,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} + + - name: Docker alpine meta + id: alpmeta + uses: docker/metadata-action@v4 + with: + images: | + dbgate/dbgate + flavor: | + latest=false + tags: | + type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} + type=raw,value=beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} + + type=match,pattern=\d+.\d+.\d+,suffix=-alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} + type=raw,value=alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} + - name: Use Node.js 14.x uses: actions/setup-node@v1 with: node-version: 14.x - name: yarn install run: | + # yarn --version + # yarn config set network-timeout 300000 yarn install - name: setCurrentVersion run: | @@ -43,19 +70,28 @@ jobs: - name: Prepare docker image run: | yarn run prepare:docker - - name: Build docker image - run: | - docker build ./docker -t dbgate - - name: Push docker image - run: | - docker tag dbgate dbgate/dbgate - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker push dbgate/dbgate - - name: Build alpine docker image - run: | - docker build ./docker -t dbgate -f docker/Dockerfile-alpine - - name: Push alpine docker image - run: | - docker tag dbgate dbgate/dbgate:alpine - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker push dbgate/dbgate:alpine + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and push + uses: docker/build-push-action@v3 + with: + push: true + context: ./docker + tags: ${{ steps.meta.outputs.tags }} + platforms: linux/amd64,linux/arm64,linux/arm/v7 + + - name: Build and push alpine + uses: docker/build-push-action@v3 + with: + push: true + context: ./docker + file: ./docker/Dockerfile-alpine + tags: ${{ steps.alpmeta.outputs.tags }} + platforms: linux/amd64,linux/arm64,linux/arm/v7 From 833f029ab55ba4f765f437c3494468a680dde212 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 29 Sep 2022 09:58:35 +0200 Subject: [PATCH 36/79] drop database #384 --- .../api/src/controllers/serverConnections.js | 9 +++++++++ packages/api/src/proc/serverConnectionProcess.js | 16 +++++++++------- packages/tools/src/SqlDumper.ts | 8 ++++++++ packages/types/dumper.d.ts | 2 ++ packages/types/engines.d.ts | 1 + packages/web/src/appobj/DatabaseAppObject.svelte | 14 ++++++++++++++ .../dbgate-plugin-mongo/src/backend/driver.js | 5 +++++ .../src/frontend/MsSqlDumper.js | 10 ++++++++++ .../src/frontend/Dumper.js | 4 ++++ 9 files changed, 62 insertions(+), 7 deletions(-) diff --git a/packages/api/src/controllers/serverConnections.js b/packages/api/src/controllers/serverConnections.js index 7f6ad2b6d..5b06d3078 100644 --- a/packages/api/src/controllers/serverConnections.js +++ b/packages/api/src/controllers/serverConnections.js @@ -152,4 +152,13 @@ module.exports = { opened.subprocess.send({ msgtype: 'createDatabase', name }); return { status: 'ok' }; }, + + dropDatabase_meta: true, + async dropDatabase({ conid, name }, req) { + testConnectionPermission(conid, req); + const opened = await this.ensureOpened(conid); + if (opened.connection.isReadOnly) return false; + opened.subprocess.send({ msgtype: 'dropDatabase', name }); + return { status: 'ok' }; + }, }; diff --git a/packages/api/src/proc/serverConnectionProcess.js b/packages/api/src/proc/serverConnectionProcess.js index 42e5cb8d9..592071040 100644 --- a/packages/api/src/proc/serverConnectionProcess.js +++ b/packages/api/src/proc/serverConnectionProcess.js @@ -2,7 +2,6 @@ const stableStringify = require('json-stable-stringify'); const { extractBoolSettingsValue, extractIntSettingsValue } = require('dbgate-tools'); const childProcessChecker = require('../utility/childProcessChecker'); const requireEngineDriver = require('../utility/requireEngineDriver'); -const { decryptConnection } = require('../utility/crypting'); const connectUtility = require('../utility/connectUtility'); const { handleProcessCommunication } = require('../utility/processComm'); @@ -81,14 +80,16 @@ function handlePing() { lastPing = new Date().getTime(); } -async function handleCreateDatabase({ name }) { +async function handleDatabaseOp(op, { name }) { const driver = requireEngineDriver(storedConnection); systemConnection = await connectUtility(driver, storedConnection, 'app'); - console.log(`RUNNING SCRIPT: CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`); - if (driver.createDatabase) { - await driver.createDatabase(systemConnection, name); + if (driver[op]) { + await driver[op](systemConnection, name); } else { - await driver.query(systemConnection, `CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`); + const dmp = driver.createDumper(); + dmp[op](name); + console.log(`RUNNING SCRIPT: ${dmp.s}`); + await driver.query(systemConnection, dmp.s); } await handleRefresh(); } @@ -96,7 +97,8 @@ async function handleCreateDatabase({ name }) { const messageHandlers = { connect: handleConnect, ping: handlePing, - createDatabase: handleCreateDatabase, + createDatabase: props => handleDatabaseOp('createDatabase', props), + dropDatabase: props => handleDatabaseOp('dropDatabase', props), }; async function handleMessage({ msgtype, ...other }) { diff --git a/packages/tools/src/SqlDumper.ts b/packages/tools/src/SqlDumper.ts index 063989a3f..3a6de60c7 100644 --- a/packages/tools/src/SqlDumper.ts +++ b/packages/tools/src/SqlDumper.ts @@ -181,6 +181,14 @@ export class SqlDumper implements AlterProcessor { this.put(' ^auto_increment'); } + createDatabase(name: string) { + this.putCmd('^create ^database %i', name); + } + + dropDatabase(name: string) { + this.putCmd('^drop ^database %i', name); + } + specialColumnOptions(column) {} columnDefinition(column: ColumnInfo, { includeDefault = true, includeNullable = true, includeCollate = true } = {}) { diff --git a/packages/types/dumper.d.ts b/packages/types/dumper.d.ts index 9d35fbdee..8c3f0147b 100644 --- a/packages/types/dumper.d.ts +++ b/packages/types/dumper.d.ts @@ -14,6 +14,8 @@ export interface SqlDumper extends AlterProcessor { putValue(value: string | number | Date); putCollection(delimiter: string, collection: T[], lambda: (item: T) => void); transform(type: TransformType, dumpExpr: () => void); + createDatabase(name: string); + dropDatabase(name: string); endCommand(); allowIdentityInsert(table: NamedObjectInfo, allow: boolean); diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts index 39ac92043..6673b64a9 100644 --- a/packages/types/engines.d.ts +++ b/packages/types/engines.d.ts @@ -110,6 +110,7 @@ export interface EngineDriver { updateCollection(pool: any, changeSet: any): Promise; getCollectionUpdateScript(changeSet: any): string; createDatabase(pool: any, name: string): Promise; + dropDatabase(pool: any, name: string): Promise; getQuerySplitterOptions(usage: 'stream' | 'script' | 'editor'): any; script(pool: any, sql: string): Promise; getNewObjectTemplates(): NewObjectTemplate[]; diff --git a/packages/web/src/appobj/DatabaseAppObject.svelte b/packages/web/src/appobj/DatabaseAppObject.svelte index 643751937..fba48176c 100644 --- a/packages/web/src/appobj/DatabaseAppObject.svelte +++ b/packages/web/src/appobj/DatabaseAppObject.svelte @@ -77,6 +77,17 @@ ); }; + const handleDropDatabase = () => { + showModal(ConfirmModal, { + message: `Really drop database ${name}? All opened sessions with this database will be forcefully closed.`, + onConfirm: () => + apiCall('server-connections/drop-database', { + conid: connection._id, + name, + }), + }); + }; + const handleNewCollection = () => { showModal(InputTextModal, { value: '', @@ -233,6 +244,9 @@ { onClick: handleNewQuery, text: 'New query', isNewQuery: true }, driver?.databaseEngineTypes?.includes('sql') && { onClick: handleNewTable, text: 'New table' }, driver?.databaseEngineTypes?.includes('document') && { onClick: handleNewCollection, text: 'New collection' }, + isSqlOrDoc && + !connection.isReadOnly && + !connection.singleDatabase && { onClick: handleDropDatabase, text: 'Drop database' }, { divider: true }, isSqlOrDoc && !connection.isReadOnly && { onClick: handleImport, text: 'Import wizard' }, isSqlOrDoc && { onClick: handleExport, text: 'Export wizard' }, diff --git a/plugins/dbgate-plugin-mongo/src/backend/driver.js b/plugins/dbgate-plugin-mongo/src/backend/driver.js index ece65e41a..756c6a5a1 100644 --- a/plugins/dbgate-plugin-mongo/src/backend/driver.js +++ b/plugins/dbgate-plugin-mongo/src/backend/driver.js @@ -276,6 +276,11 @@ const driver = { await db.createCollection('collection1'); }, + async dropDatabase(pool, name) { + const db = pool.db(name); + await db.dropDatabase(); + }, + async loadFieldValues(pool, name, field, search) { try { const collection = pool.__getDatabase().collection(name.pureName); diff --git a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js index f83cba1e8..d308b2eed 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js @@ -16,6 +16,16 @@ class MsSqlDumper extends SqlDumper { } } + dropDatabase(name) { + this.putCmd( + `USE master; + ALTER DATABASE %i SET SINGLE_USER WITH ROLLBACK IMMEDIATE; + DROP DATABASE %i`, + name, + name + ); + } + autoIncrement() { this.put(' ^identity'); } diff --git a/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js b/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js index d47aa78d2..363193ef7 100644 --- a/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js +++ b/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js @@ -26,6 +26,10 @@ class Dumper extends SqlDumper { } } + dropDatabase(name) { + this.putCmd('^drop ^database %i ^with(^force)', name); + } + dropRecreatedTempTable(tmptable) { this.putCmd('^drop ^table %i ^cascade', tmptable); } From 1382461bdc41d84ae0a6d49f803b12e20529758b Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 29 Sep 2022 11:01:17 +0200 Subject: [PATCH 37/79] current query part fix --- packages/web/src/query/AceEditor.svelte | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/web/src/query/AceEditor.svelte b/packages/web/src/query/AceEditor.svelte index b094ab8ad..efdf02d16 100644 --- a/packages/web/src/query/AceEditor.svelte +++ b/packages/web/src/query/AceEditor.svelte @@ -306,6 +306,8 @@ function updateAnnotations() { if (!mode?.includes('sql')) return; + // console.log('UPDATING ANNOTATIONS'); + editor?.session?.setAnnotations([ ...(queryParts || []) .filter(part => !(errorMessages || []).find(err => err.line == part.trimStart.line)) @@ -367,6 +369,7 @@ function changedCurrentQueryPart() { if (queryParts.length <= 1) { removeCurrentPartMarker(); + updateAnnotations(); return; } @@ -388,7 +391,11 @@ ((cursor.row == x.end.line && cursor.column <= x.end.column) || cursor.row < x.end.line) ); - if (part?.text != currentPart?.text || part?.start?.position != currentPart?.start?.position) { + if ( + part?.text != currentPart?.text || + part?.start?.position != currentPart?.start?.position || + part?.end?.position != currentPart?.end?.position + ) { removeCurrentPartMarker(); currentPart = part; From 7604889b720cd9edf1f89cbc74ccd088bf48d7d7 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 29 Sep 2022 13:58:09 +0200 Subject: [PATCH 38/79] unsaved file marker --- .../web/src/elements/TabCloseButton.svelte | 31 +++++++++++++++++++ packages/web/src/icons/FontIcon.svelte | 1 + packages/web/src/query/AceEditor.svelte | 1 + packages/web/src/tabs/QueryTab.svelte | 10 +++++- packages/web/src/utility/common.ts | 12 ++++++- packages/web/src/utility/saveTabFile.ts | 4 ++- packages/web/src/widgets/TabsPanel.svelte | 14 ++------- 7 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 packages/web/src/elements/TabCloseButton.svelte diff --git a/packages/web/src/elements/TabCloseButton.svelte b/packages/web/src/elements/TabCloseButton.svelte new file mode 100644 index 000000000..93355ef61 --- /dev/null +++ b/packages/web/src/elements/TabCloseButton.svelte @@ -0,0 +1,31 @@ + + + { + mousein = true; + }} + on:mouseleave={() => { + mousein = false; + }} +> + + + + diff --git a/packages/web/src/icons/FontIcon.svelte b/packages/web/src/icons/FontIcon.svelte index 57302b6e8..21f5063f1 100644 --- a/packages/web/src/icons/FontIcon.svelte +++ b/packages/web/src/icons/FontIcon.svelte @@ -46,6 +46,7 @@ 'icon file': 'mdi mdi-file', 'icon loading': 'mdi mdi-loading mdi-spin', 'icon close': 'mdi mdi-close', + 'icon unsaved': 'mdi mdi-record', 'icon stop': 'mdi mdi-close-octagon', 'icon filter': 'mdi mdi-filter', 'icon filter-off': 'mdi mdi-filter-off', diff --git a/packages/web/src/query/AceEditor.svelte b/packages/web/src/query/AceEditor.svelte index efdf02d16..2968ec365 100644 --- a/packages/web/src/query/AceEditor.svelte +++ b/packages/web/src/query/AceEditor.svelte @@ -554,6 +554,7 @@ editor.on('focus', () => dispatch('focus')); editor.setReadOnly(readOnly); + editor.on('change', () => { const content = editor.getValue(); value = content; diff --git a/packages/web/src/tabs/QueryTab.svelte b/packages/web/src/tabs/QueryTab.svelte index fefb5adf3..c987fd851 100644 --- a/packages/web/src/tabs/QueryTab.svelte +++ b/packages/web/src/tabs/QueryTab.svelte @@ -52,7 +52,7 @@ import useEditorData from '../query/useEditorData'; import { extensions } from '../stores'; import applyScriptTemplate from '../utility/applyScriptTemplate'; - import { changeTab } from '../utility/common'; + import { changeTab, markTabUnsaved } from '../utility/common'; import { getDatabaseInfo, useConnectionInfo } from '../utility/metadataLoaders'; import SocketMessageView from '../query/SocketMessageView.svelte'; import useEffect from '../utility/useEffect'; @@ -283,6 +283,8 @@ } const quickExportHandlerRef = createQuickExportHandlerRef(); + + let isInitialized = false; @@ -298,11 +300,17 @@ menu={createMenu()} on:input={e => { setEditorData(e.detail); + if (isInitialized) { + markTabUnsaved(tabid); + } errorMessages = []; }} on:focus={() => { activator.activate(); invalidateCommands(); + setTimeout(() => { + isInitialized = true; + }, 100); }} bind:this={domEditor} onExecuteFragment={(sql, startLine) => executeCore(sql, startLine)} diff --git a/packages/web/src/utility/common.ts b/packages/web/src/utility/common.ts index 5fb98c0ff..e5c083cb2 100644 --- a/packages/web/src/utility/common.ts +++ b/packages/web/src/utility/common.ts @@ -1,4 +1,4 @@ -import { openedTabs } from '../stores'; +import { getOpenedTabs, openedTabs } from '../stores'; import _ from 'lodash'; import getElectron from './getElectron'; @@ -18,6 +18,16 @@ export function changeTab(tabid, changeFunc) { openedTabs.update(files => files.map(tab => (tab.tabid == tabid ? changeFunc(tab) : tab))); } +export function markTabUnsaved(tabid) { + const tab = getOpenedTabs().find(x => x.tabid == tabid); + if (tab.unsaved) return; + openedTabs.update(files => files.map(tab => (tab.tabid == tabid ? { ...tab, unsaved: true } : tab))); +} + +export function markTabSaved(tabid) { + openedTabs.update(files => files.map(tab => (tab.tabid == tabid ? { ...tab, unsaved: false } : tab))); +} + export function setSelectedTabFunc(files, tabid) { return [ ...(files || []).filter(x => x.tabid != tabid).map(x => ({ ...x, selected: false })), diff --git a/packages/web/src/utility/saveTabFile.ts b/packages/web/src/utility/saveTabFile.ts index 778807fcf..60351ecdb 100644 --- a/packages/web/src/utility/saveTabFile.ts +++ b/packages/web/src/utility/saveTabFile.ts @@ -1,7 +1,7 @@ import { derived, get } from 'svelte/store'; import { showModal } from '../modals/modalTools'; import { openedTabs } from '../stores'; -import { changeTab } from './common'; +import { changeTab, markTabSaved } from './common'; import SaveFileModal from '../modals/SaveFileModal.svelte'; import registerCommand from '../commands/registerCommand'; import { apiCall } from './api'; @@ -24,12 +24,14 @@ export default async function saveTabFile(editor, saveMode, folder, format, file if (savedFilePath) { await apiCall('files/save-as', { filePath: savedFilePath, data, format }); } + markTabSaved(tabid); }; const onSave = (title, newProps) => { changeTab(tabid, tab => ({ ...tab, title, + unsaved: false, props: { ...tab.props, savedFormat: format, diff --git a/packages/web/src/widgets/TabsPanel.svelte b/packages/web/src/widgets/TabsPanel.svelte index 7244a70c9..9eb5edcaf 100644 --- a/packages/web/src/widgets/TabsPanel.svelte +++ b/packages/web/src/widgets/TabsPanel.svelte @@ -212,6 +212,7 @@ import { getConnectionInfo, useConnectionList } from '../utility/metadataLoaders'; import { duplicateTab, getTabDbKey, sortTabs, groupTabs } from '../utility/openNewTab'; import { useConnectionColorFactory } from '../utility/useConnectionColor'; + import TabCloseButton from '../elements/TabCloseButton.svelte'; $: connectionList = useConnectionList(); @@ -434,7 +435,7 @@ {/if} - + 22
closeMultipleTabs(tab => tabGroup.tabs.find(x => x.tabid == tab.tabid))} @@ -479,9 +480,7 @@ {tab.title} - closeTab(tab.tabid)}> - - + closeTab(tab.tabid)} />
{/each} @@ -582,19 +581,12 @@ white-space: nowrap; flex-grow: 1; } - .close-button { - margin-left: 5px; - color: var(--theme-font-3); - } .close-button-right { margin-left: 5px; margin-right: 5px; color: var(--theme-font-3); } - .close-button:hover { - color: var(--theme-font-1); - } .close-button-right:hover { color: var(--theme-font-1); } From dd33d96ef68776baf0f202075415500ac8a73f7c Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 29 Sep 2022 19:35:58 +0200 Subject: [PATCH 39/79] close tabs question --- packages/web/src/modals/CloseTabModal.svelte | 47 ++++++++++++++++++++ packages/web/src/widgets/TabsPanel.svelte | 35 +++++++++++++-- 2 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 packages/web/src/modals/CloseTabModal.svelte diff --git a/packages/web/src/modals/CloseTabModal.svelte b/packages/web/src/modals/CloseTabModal.svelte new file mode 100644 index 000000000..ef8de7380 --- /dev/null +++ b/packages/web/src/modals/CloseTabModal.svelte @@ -0,0 +1,47 @@ + + + + + Confirm close tabs + +
+ Following files are modified, really close tabs? After closing, you could reopen them in history + + widget +
+ + {#each tabs as tab} +
{tab.title}
+ {/each} + + + { + closeCurrentModal(); + onConfirm(); + }} + /> + { + closeCurrentModal(); + onCancel(); + }} + /> + +
+
diff --git a/packages/web/src/widgets/TabsPanel.svelte b/packages/web/src/widgets/TabsPanel.svelte index 9eb5edcaf..a50048976 100644 --- a/packages/web/src/widgets/TabsPanel.svelte +++ b/packages/web/src/widgets/TabsPanel.svelte @@ -1,5 +1,23 @@ @@ -205,6 +223,7 @@ {database} {setConfig} dbInfos={$dbInfos} + dataPatterns={$dataPatterns} {root} onClickTableHeader={designerId => { sleep(100).then(() => { diff --git a/packages/web/src/utility/usePerspectiveDataPatterns.ts b/packages/web/src/utility/usePerspectiveDataPatterns.ts new file mode 100644 index 000000000..fdabd9023 --- /dev/null +++ b/packages/web/src/utility/usePerspectiveDataPatterns.ts @@ -0,0 +1,77 @@ +import { + analyseDataPattern, + MultipleDatabaseInfo, + PerspectiveCache, + PerspectiveConfig, + PerspectiveDatabaseConfig, + PerspectiveDataLoadProps, + PerspectiveDataPattern, + PerspectiveDataPatternDict, +} from 'dbgate-datalib'; +import { PerspectiveDataLoader } from 'dbgate-datalib/lib/PerspectiveDataLoader'; +import { writable, Readable } from 'svelte/store'; + +export async function getPerspectiveDataPatterns( + databaseConfig: PerspectiveDatabaseConfig, + config: PerspectiveConfig, + cache: PerspectiveCache, + dbInfos: MultipleDatabaseInfo, + dataLoader: PerspectiveDataLoader +): Promise { + const res = {}; + + for (const node of config.nodes) { + const conid = node.conid || databaseConfig.conid; + const database = node.database || databaseConfig.database; + const { schemaName, pureName } = node; + + const cached = cache.dataPatterns.find( + x => x.conid == conid && x.database == database && x.schemaName == schemaName && x.pureName == pureName + ); + if (cached) { + res[node.designerId] = cached; + continue; + } + + const db = dbInfos?.[conid]?.[database]; + + if (!db) continue; + + const collection = db.collections?.find(x => x.pureName == pureName && x.schemaName == schemaName); + if (!collection) continue; + + const props: PerspectiveDataLoadProps = { + databaseConfig: { conid, database }, + engineType: 'docdb', + pureName, + orderBy: [], + }; + const rows = await dataLoader.loadData(props); + const pattern = analyseDataPattern( + { + conid, + database, + pureName, + schemaName, + }, + rows + ); + + cache.dataPatterns.push(pattern); + res[node.designerId] = pattern; + } + + return res; +} + +export function usePerspectiveDataPatterns( + databaseConfig: PerspectiveDatabaseConfig, + config: PerspectiveConfig, + cache: PerspectiveCache, + dbInfos: MultipleDatabaseInfo, + dataLoader: PerspectiveDataLoader +): Readable { + const res = writable({}); + getPerspectiveDataPatterns(databaseConfig, config, cache, dbInfos, dataLoader).then(value => res.set(value)); + return res; +} From efe15bf0bb76778e45794b478d6975a445d103cb Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 1 Oct 2022 16:44:34 +0200 Subject: [PATCH 44/79] mongo perspective fixes --- .../datalib/src/PerspectiveDataProvider.ts | 4 +- .../src/processPerspectiveDefaultColunns.ts | 9 +++- packages/web/src/designer/Designer.svelte | 3 +- .../web/src/designer/DesignerTable.svelte | 2 + .../perspectives/PerspectiveDesigner.svelte | 24 +++++------ .../src/perspectives/PerspectiveView.svelte | 2 +- .../src/utility/usePerspectiveDataPatterns.ts | 42 ++++++++++++++++++- 7 files changed, 65 insertions(+), 21 deletions(-) diff --git a/packages/datalib/src/PerspectiveDataProvider.ts b/packages/datalib/src/PerspectiveDataProvider.ts index e06b223e4..6ab36bd1a 100644 --- a/packages/datalib/src/PerspectiveDataProvider.ts +++ b/packages/datalib/src/PerspectiveDataProvider.ts @@ -17,9 +17,9 @@ export interface PerspectiveDatabaseConfig { export interface PerspectiveDataLoadProps { databaseConfig: PerspectiveDatabaseConfig; - schemaName: string; + schemaName?: string; pureName: string; - dataColumns: string[]; + dataColumns?: string[]; orderBy: { columnName: string; order: 'ASC' | 'DESC'; diff --git a/packages/datalib/src/processPerspectiveDefaultColunns.ts b/packages/datalib/src/processPerspectiveDefaultColunns.ts index 060c2890b..cd8e97479 100644 --- a/packages/datalib/src/processPerspectiveDefaultColunns.ts +++ b/packages/datalib/src/processPerspectiveDefaultColunns.ts @@ -1,6 +1,7 @@ import { findForeignKeyForColumn } from 'dbgate-tools'; import { DatabaseInfo, TableInfo, ViewInfo } from 'dbgate-types'; import { createPerspectiveNodeConfig, MultipleDatabaseInfo, PerspectiveConfig } from './PerspectiveConfig'; +import { PerspectiveDataPatternDict } from './PerspectiveDataPattern'; import { PerspectiveTableNode } from './PerspectiveTreeNode'; function getPerspectiveDefaultColumns( @@ -47,6 +48,7 @@ function getPerspectiveDefaultColumns( export function perspectiveNodesHaveStructure( config: PerspectiveConfig, dbInfos: MultipleDatabaseInfo, + dataPatterns: PerspectiveDataPatternDict, conid: string, database: string ) { @@ -56,8 +58,10 @@ export function perspectiveNodesHaveStructure( const table = db.tables.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); const view = db.views.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); + const collection = db.collections.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); - if (!table && !view) return false; + if (!table && !view && !collection) return false; + if (collection && !dataPatterns?.[node.designerId]) return false; } return true; @@ -66,13 +70,14 @@ export function perspectiveNodesHaveStructure( export function shouldProcessPerspectiveDefaultColunns( config: PerspectiveConfig, dbInfos: MultipleDatabaseInfo, + dataPatterns: PerspectiveDataPatternDict, conid: string, database: string ) { const nodesNotProcessed = config.nodes.filter(x => !x.defaultColumnsProcessed); if (nodesNotProcessed.length == 0) return false; - return perspectiveNodesHaveStructure(config, dbInfos, conid, database); + return perspectiveNodesHaveStructure(config, dbInfos, dataPatterns, conid, database); } function processPerspectiveDefaultColunnsStep( diff --git a/packages/web/src/designer/Designer.svelte b/packages/web/src/designer/Designer.svelte index cc4dad1da..2dfabbda4 100644 --- a/packages/web/src/designer/Designer.svelte +++ b/packages/web/src/designer/Designer.svelte @@ -479,7 +479,7 @@ const rect = e.target.getBoundingClientRect(); var json = JSON.parse(data); const { objectTypeField } = json; - if (objectTypeField != 'tables' && objectTypeField != 'views') return; + if (objectTypeField != 'tables' && objectTypeField != 'views' && objectTypeField != 'collections') return; json.designerId = `${json.pureName}-${uuidv1()}`; json.left = e.clientX - rect.left; json.top = e.clientY - rect.top; @@ -941,6 +941,7 @@ .empty { margin: 50px; font-size: 20px; + position: absolute; } .canvas { position: relative; diff --git a/packages/web/src/designer/DesignerTable.svelte b/packages/web/src/designer/DesignerTable.svelte index ad8fa924e..ca3162057 100644 --- a/packages/web/src/designer/DesignerTable.svelte +++ b/packages/web/src/designer/DesignerTable.svelte @@ -213,6 +213,8 @@ !isMultipleTableSelection && [{ divider: true }, createDatabaseObjectMenu({ ...table, conid, database })], ]; } + + // $: console.log('COLUMNS', columns);
{ - const table = dbInfos?.[node.conid || conid]?.[node.database || database]?.tables?.find( + const db = dbInfos?.[node.conid || conid]?.[node.database || database]; + const table = db?.tables?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); + const view = db?.views?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); + let collection: CollectionInfo & { columns?: any[] } = db?.collections?.find( x => x.pureName == node.pureName && x.schemaName == node.schemaName ); - const view = dbInfos?.[node.conid || conid]?.[node.database || database]?.views?.find( - x => x.pureName == node.pureName && x.schemaName == node.schemaName - ); - let collection: CollectionInfo & { columns?: any[] } = dbInfos?.[node.conid || conid]?.[ - node.database || database - ]?.collections?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); if (collection) { const pattern = dataPatterns?.[node.designerId]; if (!pattern) return null; collection = { ...collection, - columns: pattern.columns.map(x => ({ - columnName: x.name, - })), + columns: + pattern?.columns.map(x => ({ + columnName: x.name, + })) || [], }; } @@ -144,11 +142,11 @@ }); } - async function detectAutoArrange(config: PerspectiveConfig, dbInfos, root) { + async function detectAutoArrange(config: PerspectiveConfig, dbInfos, dataPatterns, root) { if ( root && config.nodes.find(x => !x.position) && - perspectiveNodesHaveStructure(config, dbInfos, conid, database) && + perspectiveNodesHaveStructure(config, dbInfos, dataPatterns, conid, database) && config.nodes.every(x => root?.findNodeByDesignerId(x.designerId)) ) { await tick(); @@ -156,7 +154,7 @@ } } - $: detectAutoArrange(config, dbInfos, root); + $: detectAutoArrange(config, dbInfos, dataPatterns, root); // $: console.log('DESIGNER ROOT', root); diff --git a/packages/web/src/perspectives/PerspectiveView.svelte b/packages/web/src/perspectives/PerspectiveView.svelte index 877c08ac7..2caa237ed 100644 --- a/packages/web/src/perspectives/PerspectiveView.svelte +++ b/packages/web/src/perspectives/PerspectiveView.svelte @@ -168,7 +168,7 @@ $: tempRoot = root?.findNodeByDesignerId(tempRootDesignerId); $: { - if (shouldProcessPerspectiveDefaultColunns(config, $dbInfos, conid, database)) { + if (shouldProcessPerspectiveDefaultColunns(config, $dbInfos, $dataPatterns, conid, database)) { setConfig(cfg => processPerspectiveDefaultColunns(cfg, $dbInfos, conid, database)); } } diff --git a/packages/web/src/utility/usePerspectiveDataPatterns.ts b/packages/web/src/utility/usePerspectiveDataPatterns.ts index fdabd9023..525a5dd67 100644 --- a/packages/web/src/utility/usePerspectiveDataPatterns.ts +++ b/packages/web/src/utility/usePerspectiveDataPatterns.ts @@ -11,6 +11,38 @@ import { import { PerspectiveDataLoader } from 'dbgate-datalib/lib/PerspectiveDataLoader'; import { writable, Readable } from 'svelte/store'; +export function getPerspectiveDataPatternsFromCache( + databaseConfig: PerspectiveDatabaseConfig, + config: PerspectiveConfig, + cache: PerspectiveCache, + dbInfos: MultipleDatabaseInfo +): PerspectiveDataPatternDict { + const res = {}; + + for (const node of config.nodes) { + const conid = node.conid || databaseConfig.conid; + const database = node.database || databaseConfig.database; + const { schemaName, pureName } = node; + + const cached = cache.dataPatterns.find( + x => x.conid == conid && x.database == database && x.schemaName == schemaName && x.pureName == pureName + ); + if (cached) { + res[node.designerId] = cached; + continue; + } + + const db = dbInfos?.[conid]?.[database]; + + if (!db) continue; + + const collection = db.collections?.find(x => x.pureName == pureName && x.schemaName == schemaName); + if (!collection) continue; + } + + return res; +} + export async function getPerspectiveDataPatterns( databaseConfig: PerspectiveDatabaseConfig, config: PerspectiveConfig, @@ -45,6 +77,10 @@ export async function getPerspectiveDataPatterns( engineType: 'docdb', pureName, orderBy: [], + range: { + offset: 0, + limit: 10, + }, }; const rows = await dataLoader.loadData(props); const pattern = analyseDataPattern( @@ -71,7 +107,9 @@ export function usePerspectiveDataPatterns( dbInfos: MultipleDatabaseInfo, dataLoader: PerspectiveDataLoader ): Readable { - const res = writable({}); - getPerspectiveDataPatterns(databaseConfig, config, cache, dbInfos, dataLoader).then(value => res.set(value)); + const cached = getPerspectiveDataPatternsFromCache(databaseConfig, config, cache, dbInfos); + const promise = getPerspectiveDataPatterns(databaseConfig, config, cache, dbInfos, dataLoader); + const res = writable(cached); + promise.then(value => res.set(value)); return res; } From b3839def321e158726fef809438abd0286d46ee4 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 1 Oct 2022 17:48:47 +0200 Subject: [PATCH 45/79] mongo perspective stuff - basic skeleton works --- packages/datalib/src/PerspectiveTreeNode.ts | 225 +++++++++++++++++- .../src/processPerspectiveDefaultColunns.ts | 49 +++- .../src/tests/PerspectiveDisplay.test.ts | 5 +- .../src/perspectives/PerspectiveView.svelte | 2 +- 4 files changed, 259 insertions(+), 22 deletions(-) diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index d571b8a0f..c3f4ae79e 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -39,6 +39,7 @@ import { FilterType } from 'dbgate-filterparser/lib/types'; import { Condition, Expression, Select } from 'dbgate-sqltree'; // import { getPerspectiveDefaultColumns } from './getPerspectiveDefaultColumns'; import uuidv1 from 'uuid/v1'; +import { PerspectiveDataPatternColumn } from './PerspectiveDataPattern'; export interface PerspectiveDataLoadPropsWithNode { props: PerspectiveDataLoadProps; @@ -699,6 +700,173 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode { } } +export class PerspectivePatternColumnNode extends PerspectiveTreeNode { + foreignKey: ForeignKeyInfo; + refTable: TableInfo; + + constructor( + public column: PerspectiveDataPatternColumn, + dbs: MultipleDatabaseInfo, + config: PerspectiveConfig, + setConfig: ChangePerspectiveConfigFunc, + dataProvider: PerspectiveDataProvider, + databaseConfig: PerspectiveDatabaseConfig, + parentNode: PerspectiveTreeNode, + designerId: string + ) { + super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig, designerId); + } + + // matchChildRow(parentRow: any, childRow: any): boolean { + // if (!this.foreignKey) return false; + // return parentRow[this.foreignKey.columns[0].columnName] == childRow[this.foreignKey.columns[0].refColumnName]; + // } + + // getChildMatchColumns() { + // if (!this.foreignKey) return []; + // return [this.foreignKey.columns[0].columnName]; + // } + + // getParentMatchColumns() { + // if (!this.foreignKey) return []; + // return [this.foreignKey.columns[0].refColumnName]; + // } + + // getParentJoinCondition(alias: string, parentAlias: string): Condition[] { + // if (!this.foreignKey) return []; + // return this.foreignKey.columns.map(column => { + // const res: Condition = { + // conditionType: 'binary', + // operator: '=', + // left: { + // exprType: 'column', + // columnName: column.columnName, + // source: { alias: parentAlias }, + // }, + // right: { + // exprType: 'column', + // columnName: column.refColumnName, + // source: { alias }, + // }, + // }; + // return res; + // }); + // } + + // createReferenceConfigColumns(): PerspectiveReferenceConfig['columns'] { + // return this.foreignKey?.columns?.map(col => ({ + // source: col.columnName, + // target: col.refColumnName, + // })); + // } + + getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { + return null; + } + + get icon() { + return 'img column'; + } + + get codeName() { + return this.column.name; + } + + get columnName() { + return this.column.name; + } + + get fieldName() { + return this.codeName + 'Ref'; + } + + get title() { + return this.column.name; + } + + get isExpandable() { + return !!this.foreignKey; + } + + get isSortable() { + return true; + } + + get filterType(): FilterType { + return 'mongo'; + } + + generateChildNodes(): PerspectiveTreeNode[] { + return []; + // if (!this.foreignKey) return []; + // const tbl = this?.db?.tables?.find( + // x => x.pureName == this.foreignKey?.refTableName && x.schemaName == this.foreignKey?.refSchemaName + // ); + + // return getTableChildPerspectiveNodes( + // tbl, + // this.dbs, + // this.config, + // this.setConfig, + // this.dataProvider, + // this.databaseConfig, + // this + // ); + } + + // get filterInfo(): PerspectiveFilterColumnInfo { + // return { + // columnName: this.columnName, + // filterType: this.filterType, + // pureName: this.column.pureName, + // schemaName: this.column.schemaName, + // foreignKey: this.foreignKey, + // }; + // } + + // parseFilterCondition(source = null): Condition { + // const filter = this.getFilter(); + // if (!filter) return null; + // const condition = parseFilter(filter, this.filterType); + // if (!condition) return null; + // return _cloneDeepWith(condition, (expr: Expression) => { + // if (expr.exprType == 'placeholder') { + // return { + // exprType: 'column', + // columnName: this.column.columnName, + // source, + // }; + // } + // }); + // } + + // get headerTableAttributes() { + // if (this.foreignKey) { + // return { + // schemaName: this.foreignKey.refSchemaName, + // pureName: this.foreignKey.refTableName, + // conid: this.databaseConfig.conid, + // database: this.databaseConfig.database, + // }; + // } + // return null; + // } + + // get tableCode() { + // return `${this.collection.schemaName}|${this.table.pureName}`; + // } + + // get namedObject(): NamedObjectInfo { + // if (this.foreignKey) { + // return { + // schemaName: this.foreignKey.refSchemaName, + // pureName: this.foreignKey.refTableName, + // }; + // } + // return null; + // } +} + export class PerspectiveTableNode extends PerspectiveTreeNode { constructor( public table: TableInfo | ViewInfo, @@ -819,16 +987,16 @@ export class PerspectiveCollectionNode extends PerspectiveTreeNode { } generateChildNodes(): PerspectiveTreeNode[] { - return []; - // return getTableChildPerspectiveNodes( - // this.table, - // this.dbs, - // this.config, - // this.setConfig, - // this.dataProvider, - // this.databaseConfig, - // this - // ); + return getCollectionChildPerspectiveNodes( + this.designerId, + this.collection, + this.dbs, + this.config, + this.setConfig, + this.dataProvider, + this.databaseConfig, + this + ); } get icon() { @@ -1172,6 +1340,43 @@ function findDesignerIdForNode( return node; } +export function getCollectionChildPerspectiveNodes( + designerId: string, + collection: CollectionInfo, + dbs: MultipleDatabaseInfo, + config: PerspectiveConfig, + setConfig: ChangePerspectiveConfigFunc, + dataProvider: PerspectiveDataProvider, + databaseConfig: PerspectiveDatabaseConfig, + parentNode: PerspectiveTreeNode +) { + if (!collection) return []; + const db = parentNode.db; + + const pattern = dataProvider.dataPatterns[designerId]; + if (!pattern) return []; + + const columnNodes = pattern.columns.map(col => + findDesignerIdForNode( + config, + parentNode, + designerId => + new PerspectivePatternColumnNode( + col, + dbs, + config, + setConfig, + dataProvider, + databaseConfig, + parentNode, + designerId + ) + ) + ); + + return columnNodes; +} + export function getTableChildPerspectiveNodes( table: TableInfo | ViewInfo, dbs: MultipleDatabaseInfo, diff --git a/packages/datalib/src/processPerspectiveDefaultColunns.ts b/packages/datalib/src/processPerspectiveDefaultColunns.ts index cd8e97479..2e106acf7 100644 --- a/packages/datalib/src/processPerspectiveDefaultColunns.ts +++ b/packages/datalib/src/processPerspectiveDefaultColunns.ts @@ -1,9 +1,17 @@ import { findForeignKeyForColumn } from 'dbgate-tools'; import { DatabaseInfo, TableInfo, ViewInfo } from 'dbgate-types'; import { createPerspectiveNodeConfig, MultipleDatabaseInfo, PerspectiveConfig } from './PerspectiveConfig'; -import { PerspectiveDataPatternDict } from './PerspectiveDataPattern'; +import { PerspectiveDataPattern, PerspectiveDataPatternDict } from './PerspectiveDataPattern'; import { PerspectiveTableNode } from './PerspectiveTreeNode'; +const namePredicates = [ + x => x.toLowerCase() == 'name', + x => x.toLowerCase() == 'title', + x => x.toLowerCase().includes('name'), + x => x.toLowerCase().includes('title'), + x => x.toLowerCase().includes('subject'), +]; + function getPerspectiveDefaultColumns( table: TableInfo | ViewInfo, db: DatabaseInfo, @@ -11,13 +19,7 @@ function getPerspectiveDefaultColumns( ): [string[], string[]] { const columns = table.columns.map(x => x.columnName); const predicates = [ - x => x.toLowerCase() == 'name', - x => x.toLowerCase() == 'title', - x => x.toLowerCase().includes('name'), - x => x.toLowerCase().includes('title'), - x => x.toLowerCase().includes('subject'), - // x => x.toLowerCase().includes('text'), - // x => x.toLowerCase().includes('desc'), + ...namePredicates, x => table.columns .find(y => y.columnName == x) @@ -45,6 +47,16 @@ function getPerspectiveDefaultColumns( return [[columns[0]], null]; } +function getPerspectiveDefaultCollectionColumns(pattern: PerspectiveDataPattern): string[] { + const columns = pattern.columns.map(x => x.name); + const predicates = [...namePredicates, x => pattern.columns.find(y => y.name == x)?.types?.includes('string')]; + + for (const predicate of predicates) { + const col = columns.find(predicate); + if (col) return [col]; + } +} + export function perspectiveNodesHaveStructure( config: PerspectiveConfig, dbInfos: MultipleDatabaseInfo, @@ -83,6 +95,7 @@ export function shouldProcessPerspectiveDefaultColunns( function processPerspectiveDefaultColunnsStep( config: PerspectiveConfig, dbInfos: MultipleDatabaseInfo, + dataPatterns: PerspectiveDataPatternDict, conid: string, database: string ) { @@ -112,6 +125,7 @@ function processPerspectiveDefaultColunnsStep( const table = db.tables.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); const view = db.views.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); + const collection = db.collections.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); if (table || view) { const treeNode = root.findNodeByDesignerId(node.designerId); @@ -186,6 +200,22 @@ function processPerspectiveDefaultColunnsStep( }; } } + + if (collection) { + const defaultColumns = getPerspectiveDefaultCollectionColumns(dataPatterns?.[node.designerId]); + return { + ...config, + nodes: config.nodes.map(n => + n.designerId == node.designerId + ? { + ...n, + defaultColumnsProcessed: true, + checkedColumns: defaultColumns, + } + : n + ), + }; + } } return null; @@ -204,11 +234,12 @@ function markAllProcessed(config: PerspectiveConfig): PerspectiveConfig { export function processPerspectiveDefaultColunns( config: PerspectiveConfig, dbInfos: MultipleDatabaseInfo, + dataPatterns: PerspectiveDataPatternDict, conid: string, database: string ): PerspectiveConfig { while (config.nodes.filter(x => !x.defaultColumnsProcessed).length > 0) { - const newConfig = processPerspectiveDefaultColunnsStep(config, dbInfos, conid, database); + const newConfig = processPerspectiveDefaultColunnsStep(config, dbInfos, dataPatterns, conid, database); if (!newConfig) { return markAllProcessed(config); } diff --git a/packages/datalib/src/tests/PerspectiveDisplay.test.ts b/packages/datalib/src/tests/PerspectiveDisplay.test.ts index d2c74f757..f86cc6b89 100644 --- a/packages/datalib/src/tests/PerspectiveDisplay.test.ts +++ b/packages/datalib/src/tests/PerspectiveDisplay.test.ts @@ -13,6 +13,7 @@ test('test flat view', () => { const configColumns = processPerspectiveDefaultColunns( createPerspectiveConfig({ pureName: 'Artist' }), { conid: { db: chinookDbInfo } }, + null, 'conid', 'db' ); @@ -47,7 +48,7 @@ test('test one level nesting', () => { columns: [{ source: 'ArtistId', target: 'ArtistId' }], }); - const configColumns = processPerspectiveDefaultColunns(config, { conid: { db: chinookDbInfo } }, 'conid', 'db'); + const configColumns = processPerspectiveDefaultColunns(config, { conid: { db: chinookDbInfo } }, null, 'conid', 'db'); // const config = createPerspectiveConfig({ pureName: 'Artist' }); // config.nodes[0].checkedColumns = ['Album']; @@ -107,7 +108,7 @@ test('test two level nesting', () => { designerId: '2', columns: [{ source: 'AlbumId', target: 'AlbumId' }], }); - const configColumns = processPerspectiveDefaultColunns(config, { conid: { db: chinookDbInfo } }, 'conid', 'db'); + const configColumns = processPerspectiveDefaultColunns(config, { conid: { db: chinookDbInfo } }, null, 'conid', 'db'); const root = new PerspectiveTableNode( artistTable, diff --git a/packages/web/src/perspectives/PerspectiveView.svelte b/packages/web/src/perspectives/PerspectiveView.svelte index 2caa237ed..db5e47184 100644 --- a/packages/web/src/perspectives/PerspectiveView.svelte +++ b/packages/web/src/perspectives/PerspectiveView.svelte @@ -169,7 +169,7 @@ $: { if (shouldProcessPerspectiveDefaultColunns(config, $dbInfos, $dataPatterns, conid, database)) { - setConfig(cfg => processPerspectiveDefaultColunns(cfg, $dbInfos, conid, database)); + setConfig(cfg => processPerspectiveDefaultColunns(cfg, $dbInfos, $dataPatterns, conid, database)); } } From 08abec7c3e9186fd021dc2b772eff6494e600dd7 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 1 Oct 2022 18:50:54 +0200 Subject: [PATCH 46/79] perspective mongo condition --- packages/datalib/src/PerspectiveCache.ts | 10 ++- packages/datalib/src/PerspectiveDataLoader.ts | 53 ++++++++++++-- .../datalib/src/PerspectiveDataProvider.ts | 3 +- packages/datalib/src/PerspectiveTreeNode.ts | 73 +++++++++++-------- 4 files changed, 101 insertions(+), 38 deletions(-) diff --git a/packages/datalib/src/PerspectiveCache.ts b/packages/datalib/src/PerspectiveCache.ts index a2d8846f6..e794e9c4f 100644 --- a/packages/datalib/src/PerspectiveCache.ts +++ b/packages/datalib/src/PerspectiveCache.ts @@ -91,7 +91,15 @@ export class PerspectiveCache { getTableCache(props: PerspectiveDataLoadProps) { const tableKey = stableStringify( - _pick(props, ['schemaName', 'pureName', 'bindingColumns', 'databaseConfig', 'orderBy', 'condition']) + _pick(props, [ + 'schemaName', + 'pureName', + 'bindingColumns', + 'databaseConfig', + 'orderBy', + 'sqlCondition', + 'mongoCondition', + ]) ); let res = this.tables[tableKey]; diff --git a/packages/datalib/src/PerspectiveDataLoader.ts b/packages/datalib/src/PerspectiveDataLoader.ts index 580549d56..a58e7ea62 100644 --- a/packages/datalib/src/PerspectiveDataLoader.ts +++ b/packages/datalib/src/PerspectiveDataLoader.ts @@ -8,7 +8,15 @@ export class PerspectiveDataLoader { constructor(public apiCall) {} buildCondition(props: PerspectiveDataLoadProps): Condition { - const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, condition } = props; + const { + schemaName, + pureName, + bindingColumns, + bindingValues, + dataColumns, + orderBy, + sqlCondition: condition, + } = props; const conditions = []; @@ -94,7 +102,16 @@ export class PerspectiveDataLoader { } async loadDataSqlDb(props: PerspectiveDataLoadProps) { - const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, condition, engineType } = props; + const { + schemaName, + pureName, + bindingColumns, + bindingValues, + dataColumns, + orderBy, + sqlCondition: condition, + engineType, + } = props; if (dataColumns?.length == 0) { return []; @@ -147,13 +164,23 @@ export class PerspectiveDataLoader { const { pureName } = props; return { pureName, + condition: props.mongoCondition, skip: props.range?.offset, limit: props.range?.limit, }; } async loadDataDocDb(props: PerspectiveDataLoadProps) { - const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, condition, engineType } = props; + const { + schemaName, + pureName, + bindingColumns, + bindingValues, + dataColumns, + orderBy, + sqlCondition: condition, + engineType, + } = props; if (dataColumns?.length == 0) { return []; @@ -190,7 +217,15 @@ export class PerspectiveDataLoader { } async loadRowCountSqlDb(props: PerspectiveDataLoadProps) { - const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, condition } = props; + const { + schemaName, + pureName, + bindingColumns, + bindingValues, + dataColumns, + orderBy, + sqlCondition: condition, + } = props; const select: Select = { commandType: 'select', @@ -218,7 +253,15 @@ export class PerspectiveDataLoader { } async loadRowCountDocDb(props: PerspectiveDataLoadProps) { - const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, condition } = props; + const { + schemaName, + pureName, + bindingColumns, + bindingValues, + dataColumns, + orderBy, + sqlCondition: condition, + } = props; const options = { ...this.getDocDbLoadOptions(props), diff --git a/packages/datalib/src/PerspectiveDataProvider.ts b/packages/datalib/src/PerspectiveDataProvider.ts index 6ab36bd1a..b2a6ee115 100644 --- a/packages/datalib/src/PerspectiveDataProvider.ts +++ b/packages/datalib/src/PerspectiveDataProvider.ts @@ -28,7 +28,8 @@ export interface PerspectiveDataLoadProps { bindingValues?: any[][]; range?: RangeDefinition; topCount?: number; - condition?: Condition; + sqlCondition?: Condition; + mongoCondition?: any; engineType: 'sqldb' | 'docdb'; } diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index c3f4ae79e..f86a3d3d2 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -298,7 +298,7 @@ export abstract class PerspectiveTreeNode { ); } - getChildrenCondition(source = null): Condition { + getChildrenSqlCondition(source = null): Condition { const conditions = _compact([ ...this.childNodes.map(x => x.parseFilterCondition(source)), ...this.buildParentFilterConditions(), @@ -315,6 +315,17 @@ export abstract class PerspectiveTreeNode { }; } + getChildrenMongoCondition(source = null): {} { + const conditions = _compact([...this.childNodes.map(x => x.parseFilterCondition(source))]); + if (conditions.length == 0) { + return null; + } + if (conditions.length == 1) { + return conditions[0]; + } + return { $and: conditions }; + } + getOrderBy(table: TableInfo | ViewInfo | CollectionInfo): PerspectiveDataLoadProps['orderBy'] { const res = _compact( this.childNodes.map(node => { @@ -444,7 +455,7 @@ export abstract class PerspectiveTreeNode { conditionType: 'and', conditions: _compact([ ...lastNode.getParentJoinCondition(lastAlias, this.namedObject.pureName), - leafNode.getChildrenCondition({ alias: 'pert_0' }), + leafNode.getChildrenSqlCondition({ alias: 'pert_0' }), ]), }; @@ -558,7 +569,7 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode { dataColumns: this.getDataLoadColumns(), databaseConfig: this.databaseConfig, orderBy: this.getOrderBy(this.refTable), - condition: this.getChildrenCondition(), + sqlCondition: this.getChildrenSqlCondition(), engineType: 'sqldb', }; } @@ -705,6 +716,7 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { refTable: TableInfo; constructor( + public owner: NamedObjectInfo, public column: PerspectiveDataPatternColumn, dbs: MultipleDatabaseInfo, config: PerspectiveConfig, @@ -814,31 +826,29 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { // ); } - // get filterInfo(): PerspectiveFilterColumnInfo { - // return { - // columnName: this.columnName, - // filterType: this.filterType, - // pureName: this.column.pureName, - // schemaName: this.column.schemaName, - // foreignKey: this.foreignKey, - // }; - // } + get filterInfo(): PerspectiveFilterColumnInfo { + return { + columnName: this.columnName, + filterType: this.filterType, + pureName: this.owner.pureName, + schemaName: this.owner.schemaName, + foreignKey: this.foreignKey, + }; + } - // parseFilterCondition(source = null): Condition { - // const filter = this.getFilter(); - // if (!filter) return null; - // const condition = parseFilter(filter, this.filterType); - // if (!condition) return null; - // return _cloneDeepWith(condition, (expr: Expression) => { - // if (expr.exprType == 'placeholder') { - // return { - // exprType: 'column', - // columnName: this.column.columnName, - // source, - // }; - // } - // }); - // } + parseFilterCondition(source = null): {} { + const filter = this.getFilter(); + if (!filter) return null; + const condition = parseFilter(filter, 'mongo'); + if (!condition) return null; + return _cloneDeepWith(condition, expr => { + if (expr.__placeholder__) { + return { + [this.columnName]: expr.__placeholder__, + }; + } + }); + } // get headerTableAttributes() { // if (this.foreignKey) { @@ -888,7 +898,7 @@ export class PerspectiveTableNode extends PerspectiveTreeNode { dataColumns: this.getDataLoadColumns(), databaseConfig: this.databaseConfig, orderBy: this.getOrderBy(this.table), - condition: this.getChildrenCondition(), + sqlCondition: this.getChildrenSqlCondition(), engineType: 'sqldb', }; } @@ -967,7 +977,7 @@ export class PerspectiveCollectionNode extends PerspectiveTreeNode { dataColumns: this.getDataLoadColumns(), databaseConfig: this.databaseConfig, orderBy: this.getOrderBy(this.collection), - condition: this.getChildrenCondition(), + mongoCondition: this.getChildrenMongoCondition(), engineType: 'docdb', }; } @@ -1129,7 +1139,7 @@ export class PerspectiveTableReferenceNode extends PerspectiveTableNode { dataColumns: this.getDataLoadColumns(), databaseConfig: this.databaseConfig, orderBy: this.getOrderBy(this.table), - condition: this.getChildrenCondition(), + sqlCondition: this.getChildrenSqlCondition(), engineType: 'sqldb', }; } @@ -1235,7 +1245,7 @@ export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode { dataColumns: this.getDataLoadColumns(), databaseConfig: this.databaseConfig, orderBy: this.getOrderBy(this.table), - condition: this.getChildrenCondition(), + sqlCondition: this.getChildrenSqlCondition(), engineType: 'sqldb', }; } @@ -1362,6 +1372,7 @@ export function getCollectionChildPerspectiveNodes( parentNode, designerId => new PerspectivePatternColumnNode( + collection, col, dbs, config, From da5dd7ac62d9b4926e10d3798e154cf464bb068e Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 1 Oct 2022 19:18:18 +0200 Subject: [PATCH 47/79] perspective mongo sort --- packages/datalib/src/PerspectiveDataLoader.ts | 59 ++++++++++++------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/packages/datalib/src/PerspectiveDataLoader.ts b/packages/datalib/src/PerspectiveDataLoader.ts index a58e7ea62..d8875ed89 100644 --- a/packages/datalib/src/PerspectiveDataLoader.ts +++ b/packages/datalib/src/PerspectiveDataLoader.ts @@ -1,27 +1,20 @@ import { Condition, Expression, Select } from 'dbgate-sqltree'; import { PerspectiveDataLoadProps } from './PerspectiveDataProvider'; import debug from 'debug'; +import _zipObject from 'lodash/zipObject'; const dbg = debug('dbgate:PerspectiveDataLoader'); export class PerspectiveDataLoader { constructor(public apiCall) {} - buildCondition(props: PerspectiveDataLoadProps): Condition { - const { - schemaName, - pureName, - bindingColumns, - bindingValues, - dataColumns, - orderBy, - sqlCondition: condition, - } = props; + buildSqlCondition(props: PerspectiveDataLoadProps): Condition { + const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, sqlCondition } = props; const conditions = []; - if (condition) { - conditions.push(condition); + if (sqlCondition) { + conditions.push(sqlCondition); } if (bindingColumns?.length == 1) { @@ -46,6 +39,24 @@ export class PerspectiveDataLoader { : null; } + buildMongoCondition(props: PerspectiveDataLoadProps): {} { + const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, mongoCondition } = props; + + const conditions = []; + + if (mongoCondition) { + conditions.push(mongoCondition); + } + + if (bindingColumns?.length == 1) { + conditions.push({ + [bindingColumns[0]]: { $in: bindingValues.map(x => x[0]) }, + }); + } + + return conditions.length == 1 ? conditions[0] : conditions.length > 0 ? { $and: conditions } : null; + } + async loadGrouping(props: PerspectiveDataLoadProps) { const { schemaName, pureName, bindingColumns, bindingValues, dataColumns } = props; @@ -79,7 +90,7 @@ export class PerspectiveDataLoader { }, ...bindingColumnExpressions, ], - where: this.buildCondition(props), + where: this.buildSqlCondition(props), }; select.groupBy = bindingColumnExpressions; @@ -139,7 +150,7 @@ export class PerspectiveDataLoader { }, })), range: props.range, - where: this.buildCondition(props), + where: this.buildSqlCondition(props), }; if (dbg?.enabled) { @@ -160,14 +171,22 @@ export class PerspectiveDataLoader { return response.rows; } - getDocDbLoadOptions(props: PerspectiveDataLoadProps) { + getDocDbLoadOptions(props: PerspectiveDataLoadProps, useSort: boolean) { const { pureName } = props; - return { + const res: any = { pureName, - condition: props.mongoCondition, + condition: this.buildMongoCondition(props), skip: props.range?.offset, limit: props.range?.limit, }; + if (useSort && props.orderBy?.length > 0) { + res.sort = _zipObject( + props.orderBy.map(col => col.columnName), + props.orderBy.map(col => (col.order == 'DESC' ? -1 : 1)) + ); + } + + return res; } async loadDataDocDb(props: PerspectiveDataLoadProps) { @@ -194,7 +213,7 @@ export class PerspectiveDataLoader { ); } - const options = this.getDocDbLoadOptions(props); + const options = this.getDocDbLoadOptions(props, true); const response = await this.apiCall('database-connections/collection-data', { conid: props.databaseConfig.conid, @@ -239,7 +258,7 @@ export class PerspectiveDataLoader { alias: 'count', }, ], - where: this.buildCondition(props), + where: this.buildSqlCondition(props), }; const response = await this.apiCall('database-connections/sql-select', { @@ -264,7 +283,7 @@ export class PerspectiveDataLoader { } = props; const options = { - ...this.getDocDbLoadOptions(props), + ...this.getDocDbLoadOptions(props, false), countDocuments: true, }; From f60e1190c8a4a9cc03d3c04cc7df550a4efa3843 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 09:44:52 +0200 Subject: [PATCH 48/79] perspectives: mongo join works --- packages/datalib/src/PerspectiveDataLoader.ts | 52 +++- packages/datalib/src/PerspectiveTreeNode.ts | 227 ++++++++++-------- packages/tools/src/structureTools.ts | 14 +- .../src/perspectives/PerspectiveView.svelte | 16 +- .../dbgate-plugin-mongo/src/backend/driver.js | 4 + 5 files changed, 196 insertions(+), 117 deletions(-) diff --git a/packages/datalib/src/PerspectiveDataLoader.ts b/packages/datalib/src/PerspectiveDataLoader.ts index d8875ed89..e1e82f984 100644 --- a/packages/datalib/src/PerspectiveDataLoader.ts +++ b/packages/datalib/src/PerspectiveDataLoader.ts @@ -57,8 +57,8 @@ export class PerspectiveDataLoader { return conditions.length == 1 ? conditions[0] : conditions.length > 0 ? { $and: conditions } : null; } - async loadGrouping(props: PerspectiveDataLoadProps) { - const { schemaName, pureName, bindingColumns, bindingValues, dataColumns } = props; + async loadGroupingSqlDb(props: PerspectiveDataLoadProps) { + const { schemaName, pureName, bindingColumns } = props; const bindingColumnExpressions = bindingColumns.map( columnName => @@ -96,7 +96,7 @@ export class PerspectiveDataLoader { select.groupBy = bindingColumnExpressions; if (dbg?.enabled) { - dbg(`LOAD COUNTS, table=${props.pureName}, columns=${props.dataColumns?.join(',')}`); + dbg(`LOAD COUNTS, table=${props.pureName}, columns=${bindingColumns?.join(',')}`); } const response = await this.apiCall('database-connections/sql-select', { @@ -112,6 +112,52 @@ export class PerspectiveDataLoader { })); } + async loadGroupingDocDb(props: PerspectiveDataLoadProps) { + const { schemaName, pureName, bindingColumns } = props; + + const aggregate = [ + { $match: this.buildMongoCondition(props) }, + { + $group: { + _id: _zipObject( + bindingColumns, + bindingColumns.map(col => '$' + col) + ), + count: { $sum: 1 }, + }, + }, + ]; + + if (dbg?.enabled) { + dbg(`LOAD COUNTS, table=${props.pureName}, columns=${bindingColumns?.join(',')}`); + } + + const response = await this.apiCall('database-connections/collection-data', { + conid: props.databaseConfig.conid, + database: props.databaseConfig.database, + options: { + pureName, + aggregate, + }, + }); + + if (response.errorMessage) return response; + return response.rows.map(row => ({ + ...row._id, + _perspective_group_size_: parseInt(row.count), + })); + } + + async loadGrouping(props: PerspectiveDataLoadProps) { + const { engineType } = props; + switch (engineType) { + case 'sqldb': + return this.loadGroupingSqlDb(props); + case 'docdb': + return this.loadGroupingDocDb(props); + } + } + async loadDataSqlDb(props: PerspectiveDataLoadProps) { const { schemaName, diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index f86a3d3d2..746f54b93 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -8,7 +8,7 @@ import { TableInfo, ViewInfo, } from 'dbgate-types'; -import { equalFullName } from 'dbgate-tools'; +import { equalFullName, isCollectionInfo, isTableInfo, isViewInfo } from 'dbgate-tools'; import { ChangePerspectiveConfigFunc, createPerspectiveNodeConfig, @@ -879,7 +879,7 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { export class PerspectiveTableNode extends PerspectiveTreeNode { constructor( - public table: TableInfo | ViewInfo, + public table: TableInfo | ViewInfo | CollectionInfo, dbs: MultipleDatabaseInfo, config: PerspectiveConfig, setConfig: ChangePerspectiveConfigFunc, @@ -892,14 +892,16 @@ export class PerspectiveTableNode extends PerspectiveTreeNode { } getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { + const isMongo = isCollectionInfo(this.table); return { schemaName: this.table.schemaName, pureName: this.table.pureName, dataColumns: this.getDataLoadColumns(), databaseConfig: this.databaseConfig, orderBy: this.getOrderBy(this.table), - sqlCondition: this.getChildrenSqlCondition(), - engineType: 'sqldb', + sqlCondition: isMongo ? null : this.getChildrenSqlCondition(), + mongoCondition: isMongo ? this.getChildrenMongoCondition() : null, + engineType: isMongo ? 'docdb' : 'sqldb', }; } @@ -956,87 +958,87 @@ export class PerspectiveTableNode extends PerspectiveTreeNode { } } -export class PerspectiveCollectionNode extends PerspectiveTreeNode { - constructor( - public collection: CollectionInfo, - dbs: MultipleDatabaseInfo, - config: PerspectiveConfig, - setConfig: ChangePerspectiveConfigFunc, - public dataProvider: PerspectiveDataProvider, - databaseConfig: PerspectiveDatabaseConfig, - parentNode: PerspectiveTreeNode, - designerId: string - ) { - super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig, designerId); - } +// export class PerspectiveCollectionNode extends PerspectiveTreeNode { +// constructor( +// public collection: CollectionInfo, +// dbs: MultipleDatabaseInfo, +// config: PerspectiveConfig, +// setConfig: ChangePerspectiveConfigFunc, +// public dataProvider: PerspectiveDataProvider, +// databaseConfig: PerspectiveDatabaseConfig, +// parentNode: PerspectiveTreeNode, +// designerId: string +// ) { +// super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig, designerId); +// } - getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { - return { - schemaName: this.collection.schemaName, - pureName: this.collection.pureName, - dataColumns: this.getDataLoadColumns(), - databaseConfig: this.databaseConfig, - orderBy: this.getOrderBy(this.collection), - mongoCondition: this.getChildrenMongoCondition(), - engineType: 'docdb', - }; - } +// getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { +// return { +// schemaName: this.collection.schemaName, +// pureName: this.collection.pureName, +// dataColumns: this.getDataLoadColumns(), +// databaseConfig: this.databaseConfig, +// orderBy: this.getOrderBy(this.collection), +// mongoCondition: this.getChildrenMongoCondition(), +// engineType: 'docdb', +// }; +// } - get codeName() { - return this.collection.schemaName - ? `${this.collection.schemaName}:${this.collection.pureName}` - : this.collection.pureName; - } +// get codeName() { +// return this.collection.schemaName +// ? `${this.collection.schemaName}:${this.collection.pureName}` +// : this.collection.pureName; +// } - get title() { - return this.nodeConfig?.alias || this.collection.pureName; - } +// get title() { +// return this.nodeConfig?.alias || this.collection.pureName; +// } - get isExpandable() { - return true; - } +// get isExpandable() { +// return true; +// } - generateChildNodes(): PerspectiveTreeNode[] { - return getCollectionChildPerspectiveNodes( - this.designerId, - this.collection, - this.dbs, - this.config, - this.setConfig, - this.dataProvider, - this.databaseConfig, - this - ); - } +// generateChildNodes(): PerspectiveTreeNode[] { +// return getCollectionChildPerspectiveNodes( +// this.designerId, +// this.collection, +// this.dbs, +// this.config, +// this.setConfig, +// this.dataProvider, +// this.databaseConfig, +// this +// ); +// } - get icon() { - return 'img collection'; - } +// get icon() { +// return 'img collection'; +// } - getBaseTableFromThis() { - return this.collection; - } +// getBaseTableFromThis() { +// return this.collection; +// } - get headerTableAttributes() { - return { - schemaName: this.collection.schemaName, - pureName: this.collection.pureName, - conid: this.databaseConfig.conid, - database: this.databaseConfig.database, - }; - } +// get headerTableAttributes() { +// return { +// schemaName: this.collection.schemaName, +// pureName: this.collection.pureName, +// conid: this.databaseConfig.conid, +// database: this.databaseConfig.database, +// }; +// } - get tableCode() { - return `${this.collection.schemaName}|${this.collection.pureName}`; - } +// get tableCode() { +// return `${this.collection.schemaName}|${this.collection.pureName}`; +// } - get namedObject(): NamedObjectInfo { - return { - schemaName: this.collection.schemaName, - pureName: this.collection.pureName, - }; - } -} +// get namedObject(): NamedObjectInfo { +// return { +// schemaName: this.collection.schemaName, +// pureName: this.collection.pureName, +// }; +// } +// } // export class PerspectiveViewNode extends PerspectiveTreeNode { // constructor( @@ -1202,7 +1204,7 @@ export class PerspectiveTableReferenceNode extends PerspectiveTableNode { export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode { constructor( public customJoin: PerspectiveCustomJoinConfig, - table: TableInfo | ViewInfo, + table: TableInfo | ViewInfo | CollectionInfo, dbs: MultipleDatabaseInfo, config: PerspectiveConfig, setConfig: ChangePerspectiveConfigFunc, @@ -1234,6 +1236,8 @@ export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode { getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { // console.log('CUSTOM JOIN', this.customJoin); // console.log('this.getDataLoadColumns()', this.getDataLoadColumns()); + const isMongo = isCollectionInfo(this.table); + return { schemaName: this.table.schemaName, pureName: this.table.pureName, @@ -1245,8 +1249,9 @@ export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode { dataColumns: this.getDataLoadColumns(), databaseConfig: this.databaseConfig, orderBy: this.getOrderBy(this.table), - sqlCondition: this.getChildrenSqlCondition(), - engineType: 'sqldb', + sqlCondition: isMongo ? null : this.getChildrenSqlCondition(), + mongoCondition: isMongo ? this.getChildrenMongoCondition() : null, + engineType: isMongo ? 'docdb' : 'sqldb', }; } @@ -1389,7 +1394,7 @@ export function getCollectionChildPerspectiveNodes( } export function getTableChildPerspectiveNodes( - table: TableInfo | ViewInfo, + table: TableInfo | ViewInfo | CollectionInfo, dbs: MultipleDatabaseInfo, config: PerspectiveConfig, setConfig: ChangePerspectiveConfigFunc, @@ -1400,25 +1405,48 @@ export function getTableChildPerspectiveNodes( if (!table) return []; const db = parentNode.db; - const columnNodes = table.columns.map(col => - findDesignerIdForNode( - config, - parentNode, - designerId => - new PerspectiveTableColumnNode( - col, - table, - dbs, - config, - setConfig, - dataProvider, - databaseConfig, - parentNode, - designerId - ) - ) - ); + const pattern = dataProvider.dataPatterns[parentNode.designerId]; + const tableOrView = isTableInfo(table) || isViewInfo(table) ? table : null; + + const columnNodes = + tableOrView?.columns?.map(col => + findDesignerIdForNode( + config, + parentNode, + designerId => + new PerspectiveTableColumnNode( + col, + tableOrView, + dbs, + config, + setConfig, + dataProvider, + databaseConfig, + parentNode, + designerId + ) + ) + ) || + pattern?.columns?.map(col => + findDesignerIdForNode( + config, + parentNode, + designerId => + new PerspectivePatternColumnNode( + table, + col, + dbs, + config, + setConfig, + dataProvider, + databaseConfig, + parentNode, + designerId + ) + ) + ) || + []; // if (!columnNodes.find(x => x.isChecked)) { // const circularColumns = columnNodes.filter(x => x.isCircular).map(x => x.columnName); // const defaultColumns = getPerspectiveDefaultColumns(table, db, circularColumns); @@ -1480,6 +1508,7 @@ export function getTableChildPerspectiveNodes( const db = dbs?.[newConfig.conid]?.[newConfig.database]; const table = db?.tables?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); const view = db?.views?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); + const collection = db?.collections?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName); const join: PerspectiveCustomJoinConfig = { refNodeDesignerId: node.designerId, @@ -1496,11 +1525,11 @@ export function getTableChildPerspectiveNodes( : ref.columns.map(col => ({ baseColumnName: col.target, refColumnName: col.source })), }; - if (table || view) { + if (table || view || collection) { customs.push( new PerspectiveCustomJoinTreeNode( join, - table || view, + table || view || collection, dbs, config, setConfig, diff --git a/packages/tools/src/structureTools.ts b/packages/tools/src/structureTools.ts index f3d083514..660c5779c 100644 --- a/packages/tools/src/structureTools.ts +++ b/packages/tools/src/structureTools.ts @@ -1,4 +1,4 @@ -import { DatabaseInfo, TableInfo, ApplicationDefinition } from 'dbgate-types'; +import { DatabaseInfo, TableInfo, ApplicationDefinition, ViewInfo, CollectionInfo } from 'dbgate-types'; import _flatten from 'lodash/flatten'; export function addTableDependencies(db: DatabaseInfo): DatabaseInfo { @@ -118,3 +118,15 @@ export function isTableColumnUnique(table: TableInfo, column: string) { } return false; } + +export function isTableInfo(obj: { objectTypeField?: string }): obj is TableInfo { + return obj.objectTypeField == 'tables'; +} + +export function isViewInfo(obj: { objectTypeField?: string }): obj is ViewInfo { + return obj.objectTypeField == 'views'; +} + +export function isCollectionInfo(obj: { objectTypeField?: string }): obj is CollectionInfo { + return obj.objectTypeField == 'collections'; +} diff --git a/packages/web/src/perspectives/PerspectiveView.svelte b/packages/web/src/perspectives/PerspectiveView.svelte index db5e47184..a260f296b 100644 --- a/packages/web/src/perspectives/PerspectiveView.svelte +++ b/packages/web/src/perspectives/PerspectiveView.svelte @@ -28,7 +28,6 @@ import { ChangePerspectiveConfigFunc, extractPerspectiveDatabases, - PerspectiveCollectionNode, PerspectiveConfig, PerspectiveDataProvider, PerspectiveTableNode, @@ -142,20 +141,9 @@ $: dataProvider = new PerspectiveDataProvider(cache, loader, $dataPatterns); $: root = - tableInfo || viewInfo + tableInfo || viewInfo || collectionInfo ? new PerspectiveTableNode( - tableInfo || viewInfo, - $dbInfos, - config, - setConfig, - dataProvider, - { conid, database }, - null, - config.rootDesignerId - ) - : collectionInfo - ? new PerspectiveCollectionNode( - collectionInfo, + tableInfo || viewInfo || collectionInfo, $dbInfos, config, setConfig, diff --git a/plugins/dbgate-plugin-mongo/src/backend/driver.js b/plugins/dbgate-plugin-mongo/src/backend/driver.js index 756c6a5a1..a120145de 100644 --- a/plugins/dbgate-plugin-mongo/src/backend/driver.js +++ b/plugins/dbgate-plugin-mongo/src/backend/driver.js @@ -207,6 +207,10 @@ const driver = { if (options.countDocuments) { const count = await collection.countDocuments(convertObjectId(options.condition) || {}); return { count }; + } else if (options.aggregate) { + let cursor = await collection.aggregate(options.aggregate); + const rows = await cursor.toArray(); + return { rows: rows.map(transformMongoData) }; } else { // console.log('options.condition', JSON.stringify(options.condition, undefined, 2)); let cursor = await collection.find(convertObjectId(options.condition) || {}); From ccb52e9b58648e7aa6bf1c3e5cb6cba6b5066ef8 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 10:19:01 +0200 Subject: [PATCH 49/79] fix --- .../datalib/src/PerspectiveDataPattern.ts | 15 ++++- packages/datalib/src/PerspectiveTreeNode.ts | 62 +++++++------------ 2 files changed, 34 insertions(+), 43 deletions(-) diff --git a/packages/datalib/src/PerspectiveDataPattern.ts b/packages/datalib/src/PerspectiveDataPattern.ts index 9540307fc..e61b0f6f8 100644 --- a/packages/datalib/src/PerspectiveDataPattern.ts +++ b/packages/datalib/src/PerspectiveDataPattern.ts @@ -4,8 +4,9 @@ import _isString from 'lodash/isString'; import _isPlainObject from 'lodash/isPlainObject'; import _isNumber from 'lodash/isNumber'; import _isBoolean from 'lodash/isBoolean'; +import _isArray from 'lodash/isArray'; -export type PerspectiveDataPatternColumnType = 'null' | 'string' | 'number' | 'boolean' | 'object'; +export type PerspectiveDataPatternColumnType = 'null' | 'string' | 'number' | 'boolean' | 'json'; export interface PerspectiveDataPatternColumn { name: string; @@ -27,6 +28,7 @@ function detectValueType(value): PerspectiveDataPatternColumnType { if (_isString(value)) return 'string'; if (_isNumber(value)) return 'number'; if (_isBoolean(value)) return 'boolean'; + if (_isPlainObject(value) || _isArray(value)) return 'json'; if (value == null) return 'null'; } @@ -42,10 +44,19 @@ function addObjectToColumns(columns: PerspectiveDataPatternColumn[], row) { }; columns.push(column); } - const type = detectValueType(row[key]); + const value = row[key]; + const type = detectValueType(value); if (!column.types.includes(type)) { column.types.push(type); } + if (_isPlainObject(value)) { + addObjectToColumns(column.columns, value); + } + if (_isArray(value)) { + for (const item of value) { + addObjectToColumns(column.columns, item); + } + } } } } diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index 746f54b93..692d2921f 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -729,6 +729,10 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig, designerId); } + get isChildColumn() { + return this.parentNode instanceof PerspectivePatternColumnNode; + } + // matchChildRow(parentRow: any, childRow: any): boolean { // if (!this.foreignKey) return false; // return parentRow[this.foreignKey.columns[0].columnName] == childRow[this.foreignKey.columns[0].refColumnName]; @@ -797,7 +801,7 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { } get isExpandable() { - return !!this.foreignKey; + return this.column.columns.length > 0; } get isSortable() { @@ -809,6 +813,20 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { } generateChildNodes(): PerspectiveTreeNode[] { + return this.column.columns.map( + column => + new PerspectivePatternColumnNode( + this.owner, + column, + this.dbs, + this.config, + this.setConfig, + this.dataProvider, + this.databaseConfig, + this, + null + ) + ); return []; // if (!this.foreignKey) return []; // const tbl = this?.db?.tables?.find( @@ -1237,7 +1255,7 @@ export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode { // console.log('CUSTOM JOIN', this.customJoin); // console.log('this.getDataLoadColumns()', this.getDataLoadColumns()); const isMongo = isCollectionInfo(this.table); - + return { schemaName: this.table.schemaName, pureName: this.table.pureName, @@ -1355,44 +1373,6 @@ function findDesignerIdForNode( return node; } -export function getCollectionChildPerspectiveNodes( - designerId: string, - collection: CollectionInfo, - dbs: MultipleDatabaseInfo, - config: PerspectiveConfig, - setConfig: ChangePerspectiveConfigFunc, - dataProvider: PerspectiveDataProvider, - databaseConfig: PerspectiveDatabaseConfig, - parentNode: PerspectiveTreeNode -) { - if (!collection) return []; - const db = parentNode.db; - - const pattern = dataProvider.dataPatterns[designerId]; - if (!pattern) return []; - - const columnNodes = pattern.columns.map(col => - findDesignerIdForNode( - config, - parentNode, - designerId => - new PerspectivePatternColumnNode( - collection, - col, - dbs, - config, - setConfig, - dataProvider, - databaseConfig, - parentNode, - designerId - ) - ) - ); - - return columnNodes; -} - export function getTableChildPerspectiveNodes( table: TableInfo | ViewInfo | CollectionInfo, dbs: MultipleDatabaseInfo, @@ -1405,7 +1385,7 @@ export function getTableChildPerspectiveNodes( if (!table) return []; const db = parentNode.db; - const pattern = dataProvider.dataPatterns[parentNode.designerId]; + const pattern = dataProvider?.dataPatterns?.[parentNode.designerId]; const tableOrView = isTableInfo(table) || isViewInfo(table) ? table : null; From 8b511a053260b0532db58ad856c24a0a7a4681ea Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 10:19:53 +0200 Subject: [PATCH 50/79] removed commented code --- packages/datalib/src/PerspectiveTreeNode.ts | 169 -------------------- 1 file changed, 169 deletions(-) diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index 692d2921f..00e8ec422 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -975,146 +975,6 @@ export class PerspectiveTableNode extends PerspectiveTreeNode { }; } } - -// export class PerspectiveCollectionNode extends PerspectiveTreeNode { -// constructor( -// public collection: CollectionInfo, -// dbs: MultipleDatabaseInfo, -// config: PerspectiveConfig, -// setConfig: ChangePerspectiveConfigFunc, -// public dataProvider: PerspectiveDataProvider, -// databaseConfig: PerspectiveDatabaseConfig, -// parentNode: PerspectiveTreeNode, -// designerId: string -// ) { -// super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig, designerId); -// } - -// getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { -// return { -// schemaName: this.collection.schemaName, -// pureName: this.collection.pureName, -// dataColumns: this.getDataLoadColumns(), -// databaseConfig: this.databaseConfig, -// orderBy: this.getOrderBy(this.collection), -// mongoCondition: this.getChildrenMongoCondition(), -// engineType: 'docdb', -// }; -// } - -// get codeName() { -// return this.collection.schemaName -// ? `${this.collection.schemaName}:${this.collection.pureName}` -// : this.collection.pureName; -// } - -// get title() { -// return this.nodeConfig?.alias || this.collection.pureName; -// } - -// get isExpandable() { -// return true; -// } - -// generateChildNodes(): PerspectiveTreeNode[] { -// return getCollectionChildPerspectiveNodes( -// this.designerId, -// this.collection, -// this.dbs, -// this.config, -// this.setConfig, -// this.dataProvider, -// this.databaseConfig, -// this -// ); -// } - -// get icon() { -// return 'img collection'; -// } - -// getBaseTableFromThis() { -// return this.collection; -// } - -// get headerTableAttributes() { -// return { -// schemaName: this.collection.schemaName, -// pureName: this.collection.pureName, -// conid: this.databaseConfig.conid, -// database: this.databaseConfig.database, -// }; -// } - -// get tableCode() { -// return `${this.collection.schemaName}|${this.collection.pureName}`; -// } - -// get namedObject(): NamedObjectInfo { -// return { -// schemaName: this.collection.schemaName, -// pureName: this.collection.pureName, -// }; -// } -// } - -// export class PerspectiveViewNode extends PerspectiveTreeNode { -// constructor( -// public view: ViewInfo, -// dbs: MultipleDatabaseInfo, -// config: PerspectiveConfig, -// setConfig: ChangePerspectiveConfigFunc, -// public dataProvider: PerspectiveDataProvider, -// databaseConfig: PerspectiveDatabaseConfig, -// parentNode: PerspectiveTreeNode -// ) { -// super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig); -// } - -// getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { -// return { -// schemaName: this.view.schemaName, -// pureName: this.view.pureName, -// dataColumns: this.getDataLoadColumns(), -// databaseConfig: this.databaseConfig, -// orderBy: this.getOrderBy(this.view), -// condition: this.getChildrenCondition(), -// }; -// } - -// get codeName() { -// return this.view.schemaName ? `${this.view.schemaName}:${this.view.pureName}` : this.view.pureName; -// } - -// get title() { -// return this.view.pureName; -// } - -// get isExpandable() { -// return true; -// } - -// get childNodes(): PerspectiveTreeNode[] { -// return getTableChildPerspectiveNodes( -// this.view, -// this.dbs, -// this.config, -// this.setConfig, -// this.dataProvider, -// this.databaseConfig, -// this -// ); -// } - -// get icon() { -// return 'img table'; -// } - -// getBaseTableFromThis() { -// return this.view; -// } -// } - export class PerspectiveTableReferenceNode extends PerspectiveTableNode { constructor( public foreignKey: ForeignKeyInfo, @@ -1526,34 +1386,5 @@ export function getTableChildPerspectiveNodes( res.push(..._sortBy(customs, 'title')); - // const customs = []; - // for (const join of config.customJoins || []) { - // if (join.baseUniqueName == parentColumn.uniqueName) { - // const newConfig = { ...databaseConfig }; - // if (join.conid) newConfig.conid = join.conid; - // if (join.database) newConfig.database = join.database; - // const db = dbs?.[newConfig.conid]?.[newConfig.database]; - // const table = db?.tables?.find(x => x.pureName == join.refTableName && x.schemaName == join.refSchemaName); - // const view = db?.views?.find(x => x.pureName == join.refTableName && x.schemaName == join.refSchemaName); - - // if (table || view) { - // customs.push( - // new PerspectiveCustomJoinTreeNode( - // join, - // table || view, - // dbs, - // config, - // setConfig, - // dataProvider, - // newConfig, - // parentColumn, - // null - // ) - // ); - // } - // } - // } - // res.push(..._sortBy(customs, 'title')); - return res; } From d647d30258b8818871da11b320181408d6ffa10f Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 11:30:05 +0200 Subject: [PATCH 51/79] perspective nosql test --- .../datalib/src/PerspectiveDataPattern.ts | 2 +- packages/datalib/src/PerspectiveDisplay.ts | 7 +-- packages/datalib/src/PerspectiveTreeNode.ts | 35 +++++++++++-- .../src/tests/PerspectiveDisplay.test.ts | 52 +++++++++++++++++++ 4 files changed, 88 insertions(+), 8 deletions(-) diff --git a/packages/datalib/src/PerspectiveDataPattern.ts b/packages/datalib/src/PerspectiveDataPattern.ts index e61b0f6f8..6974ba5c8 100644 --- a/packages/datalib/src/PerspectiveDataPattern.ts +++ b/packages/datalib/src/PerspectiveDataPattern.ts @@ -17,7 +17,7 @@ export interface PerspectiveDataPatternColumn { export interface PerspectiveDataPattern { conid: string; database: string; - schemaName: string; + schemaName?: string; pureName: string; columns: PerspectiveDataPatternColumn[]; } diff --git a/packages/datalib/src/PerspectiveDisplay.ts b/packages/datalib/src/PerspectiveDisplay.ts index 7bf8d3e14..75d7bfa46 100644 --- a/packages/datalib/src/PerspectiveDisplay.ts +++ b/packages/datalib/src/PerspectiveDisplay.ts @@ -126,14 +126,14 @@ export class PerspectiveDisplay { fillColumns(children: PerspectiveTreeNode[], parentNodes: PerspectiveTreeNode[]) { for (const child of children) { - if (child.isCheckedColumn || child.isCheckedNode) { + if (child.generatesHiearchicGridColumn || child.generatesDataGridColumn) { this.processColumn(child, parentNodes); } } } processColumn(node: PerspectiveTreeNode, parentNodes: PerspectiveTreeNode[]) { - if (node.isCheckedColumn) { + if (node.generatesDataGridColumn) { const column = new PerspectiveDisplayColumn(this); column.title = node.columnTitle; column.dataField = node.dataField; @@ -145,7 +145,7 @@ export class PerspectiveDisplay { this.columns.push(column); } - if (node.isExpandable && node.isCheckedNode) { + if (node.generatesHiearchicGridColumn) { const countBefore = this.columns.length; this.fillColumns(node.childNodes, [...parentNodes, node]); @@ -185,6 +185,7 @@ export class PerspectiveDisplay { const subRowCollections = []; for (const node of treeNodes) { + // console.log('sourceRow[node.fieldName]', node.fieldName, sourceRow[node.fieldName]); if (sourceRow[node.fieldName]) { const subrows = { rows: this.collectRows(sourceRow[node.fieldName], node.childNodes), diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index 00e8ec422..b0c8a5e99 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -81,7 +81,7 @@ export abstract class PerspectiveTreeNode { this.parentNodeConfig = parentNode?.nodeConfig; } readonly nodeConfig: PerspectiveNodeConfig; - readonly parentNodeConfig: PerspectiveNodeConfig; + parentNodeConfig: PerspectiveNodeConfig; // defaultChecked: boolean; abstract get title(); abstract get codeName(); @@ -110,6 +110,15 @@ export abstract class PerspectiveTreeNode { get namedObject(): NamedObjectInfo { return null; } + get parentTableNode(): PerspectiveTableNode { + if (this instanceof PerspectiveTableNode) { + return this; + } + if (this.parentNode == null) { + return null; + } + return this.parentNode.parentTableNode; + } abstract getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps; get isRoot() { return this.parentNode == null; @@ -121,6 +130,12 @@ export abstract class PerspectiveTreeNode { get isSortable() { return false; } + get generatesHiearchicGridColumn() { + return this.isExpandable && this.isCheckedNode; + } + get generatesDataGridColumn() { + return this.isCheckedColumn; + } matchChildRow(parentRow: any, childRow: any): boolean { return true; } @@ -273,12 +288,12 @@ export abstract class PerspectiveTreeNode { [field]: isIncluded ? [...(n[field] || []), this.codeName] : (n[field] || []).filter(x => x != this.codeName), }); - const [cfgChanged, nodeCfg] = this.parentNode?.ensureNodeConfig(cfg); + const [cfgChanged, nodeCfg] = this.parentTableNode?.ensureNodeConfig(cfg); return { ...cfgChanged, nodes: cfgChanged.nodes.map(n => - n.designerId == (this.parentNode?.designerId || nodeCfg?.designerId) ? changedFields(n) : n + n.designerId == (this.parentTableNode?.designerId || nodeCfg?.designerId) ? changedFields(n) : n ), }; }); @@ -727,6 +742,7 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { designerId: string ) { super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig, designerId); + this.parentNodeConfig = this.parentTableNode?.nodeConfig; } get isChildColumn() { @@ -780,11 +796,22 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { return null; } + get generatesHiearchicGridColumn() { + return !!this.parentTableNode?.nodeConfig?.checkedColumns?.find(x => x.startsWith(this.codeName + '::')); + } + + // get generatesHiearchicGridColumn() { + // // return this.config &&; + // } + get icon() { return 'img column'; } get codeName() { + if (this.parentNode instanceof PerspectivePatternColumnNode) { + return `${this.parentNode.codeName}::${this.column.name}`; + } return this.column.name; } @@ -793,7 +820,7 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { } get fieldName() { - return this.codeName + 'Ref'; + return this.column.name; } get title() { diff --git a/packages/datalib/src/tests/PerspectiveDisplay.test.ts b/packages/datalib/src/tests/PerspectiveDisplay.test.ts index f86cc6b89..dfba7a3aa 100644 --- a/packages/datalib/src/tests/PerspectiveDisplay.test.ts +++ b/packages/datalib/src/tests/PerspectiveDisplay.test.ts @@ -7,6 +7,9 @@ import artistDataFlat from './artistDataFlat'; import artistDataAlbum from './artistDataAlbum'; import artistDataAlbumTrack from './artistDataAlbumTrack'; import { processPerspectiveDefaultColunns } from '../processPerspectiveDefaultColunns'; +import { DatabaseAnalyser, isCollectionInfo } from 'dbgate-tools'; +import { analyseDataPattern } from '../PerspectiveDataPattern'; +import { PerspectiveDataProvider } from '../PerspectiveDataProvider'; test('test flat view', () => { const artistTable = chinookDbInfo.tables.find(x => x.pureName == 'Artist'); @@ -141,3 +144,52 @@ test('test two level nesting', () => { expect(display.rows[2].rowSpans).toEqual([1, 2, 1]); expect(display.rows[2].rowCellSkips).toEqual([true, false, false]); }); + +test('test nosql display', () => { + const collectionInfo = { + objectTypeField: 'collections', + pureName: 'Account', + }; + const dbInfo = { + ...DatabaseAnalyser.createEmptyStructure(), + collections: [collectionInfo], + }; + const accountData = [ + { name: 'jan', email: 'jan@foo.co', follows: [{ name: 'lucie' }, { name: 'petr' }] }, + { name: 'romeo', email: 'romeo@foo.co', follows: [{ name: 'julie' }, { name: 'wiliam' }] }, + ]; + const config = createPerspectiveConfig({ pureName: 'Account' }); + const dataPatterns = { + [config.rootDesignerId]: analyseDataPattern( + { + conid: 'conid', + database: 'db', + pureName: 'Account', + }, + accountData + ), + }; + + const configColumns = processPerspectiveDefaultColunns( + config, + { conid: { db: dbInfo } }, + dataPatterns, + 'conid', + 'db' + ); + const root = new PerspectiveTableNode( + collectionInfo, + { conid: { db: dbInfo } }, + configColumns, + null, + new PerspectiveDataProvider(null, null, dataPatterns), + { conid: 'conid', database: 'db' }, + null, + configColumns.rootDesignerId + ); + const display = new PerspectiveDisplay(root, accountData); + + expect(display.rows.length).toEqual(2); + expect(display.rows[0].rowData).toEqual(['jan']); + expect(display.rows[1].rowData).toEqual(['romeo']); +}); From 7a3c46b691ff02f0f5f1a7ad90409c0fe4a35de6 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 12:01:07 +0200 Subject: [PATCH 52/79] perspective display - mongo nested objects --- packages/datalib/src/PerspectiveDisplay.ts | 31 ++++-- packages/datalib/src/PerspectiveTreeNode.ts | 1 + .../src/tests/PerspectiveDisplay.test.ts | 53 ---------- .../src/tests/PerspectiveDisplayNoSql.test.ts | 98 +++++++++++++++++++ 4 files changed, 124 insertions(+), 59 deletions(-) create mode 100644 packages/datalib/src/tests/PerspectiveDisplayNoSql.test.ts diff --git a/packages/datalib/src/PerspectiveDisplay.ts b/packages/datalib/src/PerspectiveDisplay.ts index 75d7bfa46..e3d16455c 100644 --- a/packages/datalib/src/PerspectiveDisplay.ts +++ b/packages/datalib/src/PerspectiveDisplay.ts @@ -3,6 +3,8 @@ import _max from 'lodash/max'; import _range from 'lodash/max'; import _fill from 'lodash/fill'; import _findIndex from 'lodash/findIndex'; +import _isPlainObject from 'lodash/isPlainObject'; +import _isArray from 'lodash/isArray'; import debug from 'debug'; const dbg = debug('dbgate:PerspectiveDisplay'); @@ -167,13 +169,30 @@ export class PerspectiveDisplay { // return _findIndex(this.columns, x => x.dataNode.designerId == node.designerId); // } + extractArray(value) { + if (_isArray(value)) return value; + if (_isPlainObject(value)) return [value]; + return []; + } + collectRows(sourceRows: any[], nodes: PerspectiveTreeNode[]): CollectedPerspectiveDisplayRow[] { // console.log('********** COLLECT ROWS', sourceRows); - const columnNodes = nodes.filter(x => x.isCheckedColumn); - const treeNodes = nodes.filter(x => x.isCheckedNode); + const columnNodes = nodes.filter(x => x.generatesDataGridColumn); + const treeNodes = nodes.filter(x => x.generatesHiearchicGridColumn); - // console.log('columnNodes', columnNodes); - // console.log('treeNodes', treeNodes); + // console.log( + // 'columnNodes', + // columnNodes.map(x => x.title) + // ); + // console.log( + // 'treeNodes', + // treeNodes.map(x => x.title) + // ); + + // console.log( + // 'nodes', + // nodes.map(x => x.title) + // ); const columnIndexes = columnNodes.map(node => this.findColumnIndexFromNode(node)); @@ -181,14 +200,14 @@ export class PerspectiveDisplay { for (const sourceRow of sourceRows) { // console.log('PROCESS SOURCE', sourceRow); // row.startIndex = startIndex; - const rowData = columnNodes.map(node => sourceRow[node.codeName]); + const rowData = columnNodes.map(node => sourceRow[node.fieldName]); const subRowCollections = []; for (const node of treeNodes) { // console.log('sourceRow[node.fieldName]', node.fieldName, sourceRow[node.fieldName]); if (sourceRow[node.fieldName]) { const subrows = { - rows: this.collectRows(sourceRow[node.fieldName], node.childNodes), + rows: this.collectRows(this.extractArray(sourceRow[node.fieldName]), node.childNodes), }; subRowCollections.push(subrows); } diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index b0c8a5e99..8b4bce444 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -797,6 +797,7 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { } get generatesHiearchicGridColumn() { + // console.log('generatesHiearchicGridColumn', this.parentTableNode?.nodeConfig?.checkedColumns, this.codeName + '::'); return !!this.parentTableNode?.nodeConfig?.checkedColumns?.find(x => x.startsWith(this.codeName + '::')); } diff --git a/packages/datalib/src/tests/PerspectiveDisplay.test.ts b/packages/datalib/src/tests/PerspectiveDisplay.test.ts index dfba7a3aa..82bbedc82 100644 --- a/packages/datalib/src/tests/PerspectiveDisplay.test.ts +++ b/packages/datalib/src/tests/PerspectiveDisplay.test.ts @@ -1,4 +1,3 @@ -import { TableInfo } from 'dbgate-types'; import { PerspectiveDisplay } from '../PerspectiveDisplay'; import { PerspectiveTableNode } from '../PerspectiveTreeNode'; import { chinookDbInfo } from './chinookDbInfo'; @@ -7,9 +6,6 @@ import artistDataFlat from './artistDataFlat'; import artistDataAlbum from './artistDataAlbum'; import artistDataAlbumTrack from './artistDataAlbumTrack'; import { processPerspectiveDefaultColunns } from '../processPerspectiveDefaultColunns'; -import { DatabaseAnalyser, isCollectionInfo } from 'dbgate-tools'; -import { analyseDataPattern } from '../PerspectiveDataPattern'; -import { PerspectiveDataProvider } from '../PerspectiveDataProvider'; test('test flat view', () => { const artistTable = chinookDbInfo.tables.find(x => x.pureName == 'Artist'); @@ -144,52 +140,3 @@ test('test two level nesting', () => { expect(display.rows[2].rowSpans).toEqual([1, 2, 1]); expect(display.rows[2].rowCellSkips).toEqual([true, false, false]); }); - -test('test nosql display', () => { - const collectionInfo = { - objectTypeField: 'collections', - pureName: 'Account', - }; - const dbInfo = { - ...DatabaseAnalyser.createEmptyStructure(), - collections: [collectionInfo], - }; - const accountData = [ - { name: 'jan', email: 'jan@foo.co', follows: [{ name: 'lucie' }, { name: 'petr' }] }, - { name: 'romeo', email: 'romeo@foo.co', follows: [{ name: 'julie' }, { name: 'wiliam' }] }, - ]; - const config = createPerspectiveConfig({ pureName: 'Account' }); - const dataPatterns = { - [config.rootDesignerId]: analyseDataPattern( - { - conid: 'conid', - database: 'db', - pureName: 'Account', - }, - accountData - ), - }; - - const configColumns = processPerspectiveDefaultColunns( - config, - { conid: { db: dbInfo } }, - dataPatterns, - 'conid', - 'db' - ); - const root = new PerspectiveTableNode( - collectionInfo, - { conid: { db: dbInfo } }, - configColumns, - null, - new PerspectiveDataProvider(null, null, dataPatterns), - { conid: 'conid', database: 'db' }, - null, - configColumns.rootDesignerId - ); - const display = new PerspectiveDisplay(root, accountData); - - expect(display.rows.length).toEqual(2); - expect(display.rows[0].rowData).toEqual(['jan']); - expect(display.rows[1].rowData).toEqual(['romeo']); -}); diff --git a/packages/datalib/src/tests/PerspectiveDisplayNoSql.test.ts b/packages/datalib/src/tests/PerspectiveDisplayNoSql.test.ts new file mode 100644 index 000000000..aa08e0f83 --- /dev/null +++ b/packages/datalib/src/tests/PerspectiveDisplayNoSql.test.ts @@ -0,0 +1,98 @@ +import { PerspectiveDisplay } from '../PerspectiveDisplay'; +import { PerspectiveTableNode } from '../PerspectiveTreeNode'; +import { createPerspectiveConfig, PerspectiveNodeConfig } from '../PerspectiveConfig'; +import { processPerspectiveDefaultColunns } from '../processPerspectiveDefaultColunns'; +import { DatabaseAnalyser } from 'dbgate-tools'; +import { analyseDataPattern } from '../PerspectiveDataPattern'; +import { PerspectiveDataProvider } from '../PerspectiveDataProvider'; + +const accountData = [ + { + name: 'jan', + email: 'jan@foo.co', + follows: [{ name: 'lucie' }, { name: 'petr' }], + nested: { email: 'jan@nest.cz' }, + }, + { + name: 'romeo', + email: 'romeo@foo.co', + follows: [{ name: 'julie' }, { name: 'wiliam' }], + nested: { email: 'romeo@nest.cz' }, + }, +]; + +function createDisplay(cfgFunc?: (cfg: PerspectiveNodeConfig) => void) { + const collectionInfo = { + objectTypeField: 'collections', + pureName: 'Account', + }; + const dbInfo = { + ...DatabaseAnalyser.createEmptyStructure(), + collections: [collectionInfo], + }; + const config = createPerspectiveConfig({ pureName: 'Account' }); + const dataPatterns = { + [config.rootDesignerId]: analyseDataPattern( + { + conid: 'conid', + database: 'db', + pureName: 'Account', + }, + accountData + ), + }; + const configColumns = processPerspectiveDefaultColunns( + config, + { conid: { db: dbInfo } }, + dataPatterns, + 'conid', + 'db' + ); + if (cfgFunc) { + cfgFunc(configColumns.nodes[0]); + } + const root = new PerspectiveTableNode( + collectionInfo, + { conid: { db: dbInfo } }, + configColumns, + null, + new PerspectiveDataProvider(null, null, dataPatterns), + { conid: 'conid', database: 'db' }, + null, + configColumns.rootDesignerId + ); + + const display = new PerspectiveDisplay(root, accountData); + + return display; +} + +test('test nosql display', () => { + const display = createDisplay(); + + expect(display.rows.length).toEqual(2); + expect(display.rows[0].rowData).toEqual(['jan']); + expect(display.rows[1].rowData).toEqual(['romeo']); +}); + +test('test nosql nested array display', () => { + const display = createDisplay(cfg => { + cfg.checkedColumns = ['name', 'follows::name']; + }); + + expect(display.rows.length).toEqual(4); + expect(display.rows[0].rowData).toEqual(['jan', 'lucie']); + expect(display.rows[1].rowData).toEqual([undefined, 'petr']); + expect(display.rows[2].rowData).toEqual(['romeo', 'julie']); + expect(display.rows[3].rowData).toEqual([undefined, 'wiliam']); +}); + +test('test nosql nested object', () => { + const display = createDisplay(cfg => { + cfg.checkedColumns = ['name', 'nested::email']; + }); + + expect(display.rows.length).toEqual(2); + expect(display.rows[0].rowData).toEqual(['jan', 'jan@nest.cz']); + expect(display.rows[1].rowData).toEqual(['romeo', 'romeo@nest.cz']); +}); From 1d85a1753335d72e55ba9f4a191adf2382e94746 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 15:43:00 +0200 Subject: [PATCH 53/79] fix --- packages/datalib/src/PerspectiveDisplay.ts | 2 +- packages/datalib/src/PerspectiveTreeNode.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/datalib/src/PerspectiveDisplay.ts b/packages/datalib/src/PerspectiveDisplay.ts index e3d16455c..f7f5521e7 100644 --- a/packages/datalib/src/PerspectiveDisplay.ts +++ b/packages/datalib/src/PerspectiveDisplay.ts @@ -200,7 +200,7 @@ export class PerspectiveDisplay { for (const sourceRow of sourceRows) { // console.log('PROCESS SOURCE', sourceRow); // row.startIndex = startIndex; - const rowData = columnNodes.map(node => sourceRow[node.fieldName]); + const rowData = columnNodes.map(node => sourceRow[node.columnName]); const subRowCollections = []; for (const node of treeNodes) { diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index 8b4bce444..c94cd79c3 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -606,6 +606,7 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode { get fieldName() { return this.codeName + 'Ref'; + // return this.codeName ; } get title() { From 23b345c898783503d9120b003e9a3ffe2a3bf4e5 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 17:48:03 +0200 Subject: [PATCH 54/79] perspective mongo fixes --- packages/datalib/src/PerspectiveCache.ts | 3 ++- packages/datalib/src/PerspectiveDataLoader.ts | 4 ---- packages/datalib/src/PerspectiveDataProvider.ts | 2 ++ packages/datalib/src/PerspectiveTreeNode.ts | 2 ++ 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/datalib/src/PerspectiveCache.ts b/packages/datalib/src/PerspectiveCache.ts index e794e9c4f..e706b8ae4 100644 --- a/packages/datalib/src/PerspectiveCache.ts +++ b/packages/datalib/src/PerspectiveCache.ts @@ -35,6 +35,7 @@ export class PerspectiveCacheTable { pureName: string; bindingColumns?: string[]; dataColumns: string[]; + allColumns?: boolean; loadedAll: boolean; loadedRows: any[] = []; bindingGroups: { [bindingKey: string]: PerspectiveBindingGroup } = {}; @@ -103,7 +104,7 @@ export class PerspectiveCache { ); let res = this.tables[tableKey]; - if (res && _difference(props.dataColumns, res.dataColumns).length > 0) { + if (res && _difference(props.dataColumns, res.dataColumns).length > 0 && !res.allColumns) { dbg('Delete cache because incomplete columns', props.pureName, res.dataColumns); // we have incomplete cache diff --git a/packages/datalib/src/PerspectiveDataLoader.ts b/packages/datalib/src/PerspectiveDataLoader.ts index e1e82f984..0e1a9ee13 100644 --- a/packages/datalib/src/PerspectiveDataLoader.ts +++ b/packages/datalib/src/PerspectiveDataLoader.ts @@ -247,10 +247,6 @@ export class PerspectiveDataLoader { engineType, } = props; - if (dataColumns?.length == 0) { - return []; - } - if (dbg?.enabled) { dbg( `LOAD DATA, collection=${props.pureName}, columns=${props.dataColumns?.join(',')}, range=${ diff --git a/packages/datalib/src/PerspectiveDataProvider.ts b/packages/datalib/src/PerspectiveDataProvider.ts index b2a6ee115..b719d5c25 100644 --- a/packages/datalib/src/PerspectiveDataProvider.ts +++ b/packages/datalib/src/PerspectiveDataProvider.ts @@ -20,6 +20,7 @@ export interface PerspectiveDataLoadProps { schemaName?: string; pureName: string; dataColumns?: string[]; + allColumns?: boolean; orderBy: { columnName: string; order: 'ASC' | 'DESC'; @@ -189,6 +190,7 @@ export class PerspectiveDataProvider { // load missing rows tableCache.dataColumns = props.dataColumns; + tableCache.allColumns = props.allColumns; const nextRows = await this.loader.loadData({ ...props, diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index c94cd79c3..6074d01ac 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -944,6 +944,7 @@ export class PerspectiveTableNode extends PerspectiveTreeNode { schemaName: this.table.schemaName, pureName: this.table.pureName, dataColumns: this.getDataLoadColumns(), + allColumns: isMongo, databaseConfig: this.databaseConfig, orderBy: this.getOrderBy(this.table), sqlCondition: isMongo ? null : this.getChildrenSqlCondition(), @@ -1154,6 +1155,7 @@ export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode { stableStringify ), dataColumns: this.getDataLoadColumns(), + allColumns: isMongo, databaseConfig: this.databaseConfig, orderBy: this.getOrderBy(this.table), sqlCondition: isMongo ? null : this.getChildrenSqlCondition(), From be0aeeb2c8390b084606eb71419e9e33138ae6ef Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 20:52:22 +0200 Subject: [PATCH 55/79] perspective - pattern for SQL sources --- .../api/src/proc/databaseConnectionProcess.js | 2 +- packages/datalib/src/PerspectiveDataLoader.ts | 43 ++++++++++++++----- .../datalib/src/PerspectiveDataPattern.ts | 18 ++++++++ .../datalib/src/PerspectiveDataProvider.ts | 2 +- packages/datalib/src/PerspectiveTreeNode.ts | 43 ++++++++++++------- .../src/utility/usePerspectiveDataPatterns.ts | 32 +++++++++----- 6 files changed, 102 insertions(+), 38 deletions(-) diff --git a/packages/api/src/proc/databaseConnectionProcess.js b/packages/api/src/proc/databaseConnectionProcess.js index 3cd88c580..88f5ef8d9 100644 --- a/packages/api/src/proc/databaseConnectionProcess.js +++ b/packages/api/src/proc/databaseConnectionProcess.js @@ -177,7 +177,7 @@ async function handleQueryData({ msgid, sql }, skipReadonlyCheck = false) { const res = await driver.query(systemConnection, sql); process.send({ msgtype: 'response', msgid, ...res }); } catch (err) { - process.send({ msgtype: 'response', msgid, errorMessage: err.message }); + process.send({ msgtype: 'response', msgid, errorMessage: err.message || 'Error executing SQL script' }); } } diff --git a/packages/datalib/src/PerspectiveDataLoader.ts b/packages/datalib/src/PerspectiveDataLoader.ts index 0e1a9ee13..1c9df1439 100644 --- a/packages/datalib/src/PerspectiveDataLoader.ts +++ b/packages/datalib/src/PerspectiveDataLoader.ts @@ -2,6 +2,26 @@ import { Condition, Expression, Select } from 'dbgate-sqltree'; import { PerspectiveDataLoadProps } from './PerspectiveDataProvider'; import debug from 'debug'; import _zipObject from 'lodash/zipObject'; +import _mapValues from 'lodash/mapValues'; +import _isArray from 'lodash/isArray'; +import { safeJsonParse } from 'dbgate-tools'; + +function normalizeLoadedRow(row) { + return _mapValues(row, v => safeJsonParse(v) || v); +} + +function normalizeResult(result) { + if (_isArray(result)) { + return result.map(normalizeLoadedRow); + } + if (result.errorMessage) { + return result; + } + return { + ...result, + errorMessage: 'Unspecified error', + }; +} const dbg = debug('dbgate:PerspectiveDataLoader'); @@ -187,14 +207,17 @@ export class PerspectiveDataLoader { }, })), selectAll: !dataColumns, - orderBy: orderBy?.map(({ columnName, order }) => ({ - exprType: 'column', - columnName, - direction: order, - source: { - name: { schemaName, pureName }, - }, - })), + orderBy: + orderBy?.length > 0 + ? orderBy?.map(({ columnName, order }) => ({ + exprType: 'column', + columnName, + direction: order, + source: { + name: { schemaName, pureName }, + }, + })) + : null, range: props.range, where: this.buildSqlCondition(props), }; @@ -271,9 +294,9 @@ export class PerspectiveDataLoader { const { engineType } = props; switch (engineType) { case 'sqldb': - return this.loadDataSqlDb(props); + return normalizeResult(await this.loadDataSqlDb(props)); case 'docdb': - return this.loadDataDocDb(props); + return normalizeResult(await this.loadDataDocDb(props)); } } diff --git a/packages/datalib/src/PerspectiveDataPattern.ts b/packages/datalib/src/PerspectiveDataPattern.ts index 6974ba5c8..d51e20349 100644 --- a/packages/datalib/src/PerspectiveDataPattern.ts +++ b/packages/datalib/src/PerspectiveDataPattern.ts @@ -5,6 +5,7 @@ import _isPlainObject from 'lodash/isPlainObject'; import _isNumber from 'lodash/isNumber'; import _isBoolean from 'lodash/isBoolean'; import _isArray from 'lodash/isArray'; +import { safeJsonParse } from 'dbgate-tools'; export type PerspectiveDataPatternColumnType = 'null' | 'string' | 'number' | 'boolean' | 'json'; @@ -57,6 +58,22 @@ function addObjectToColumns(columns: PerspectiveDataPatternColumn[], row) { addObjectToColumns(column.columns, item); } } + if (_isString(value)) { + const json = safeJsonParse(value); + if (json && (_isPlainObject(json) || _isArray(json))) { + if (!column.types.includes('json')) { + column.types.push('json'); + } + if (_isPlainObject(json)) { + addObjectToColumns(column.columns, json); + } + if (_isArray(json)) { + for (const item of json) { + addObjectToColumns(column.columns, item); + } + } + } + } } } } @@ -69,6 +86,7 @@ export function analyseDataPattern( ...patternBase, columns: [], }; + // console.log('ROWS', rows); for (const row of rows) { addObjectToColumns(res.columns, row); } diff --git a/packages/datalib/src/PerspectiveDataProvider.ts b/packages/datalib/src/PerspectiveDataProvider.ts index b719d5c25..18166bb97 100644 --- a/packages/datalib/src/PerspectiveDataProvider.ts +++ b/packages/datalib/src/PerspectiveDataProvider.ts @@ -1,7 +1,6 @@ import debug from 'debug'; import { Condition } from 'dbgate-sqltree'; import { RangeDefinition } from 'dbgate-types'; -import { format } from 'path'; import { PerspectiveBindingGroup, PerspectiveCache } from './PerspectiveCache'; import { PerspectiveDataLoader } from './PerspectiveDataLoader'; import { PerspectiveDataPatternDict } from './PerspectiveDataPattern'; @@ -10,6 +9,7 @@ export const PERSPECTIVE_PAGE_SIZE = 100; const dbg = debug('dbgate:PerspectiveDataProvider'); + export interface PerspectiveDatabaseConfig { conid: string; database: string; diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index 6074d01ac..dd6bf3910 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -309,6 +309,10 @@ export abstract class PerspectiveTreeNode { ...this.childNodes.map(x => x.childDataColumn), ..._flatten(this.childNodes.filter(x => x.isExpandable && x.isChecked).map(x => x.getChildMatchColumns())), ...this.getParentMatchColumns(), + ...this.childNodes + .filter(x => x instanceof PerspectivePatternColumnNode) + .filter(x => this.nodeConfig?.checkedColumns?.find(y => y.startsWith(x.codeName + '::'))) + .map(x => x.columnName), ]) ); } @@ -1282,21 +1286,30 @@ export function getTableChildPerspectiveNodes( const columnNodes = tableOrView?.columns?.map(col => - findDesignerIdForNode( - config, - parentNode, - designerId => - new PerspectiveTableColumnNode( - col, - tableOrView, - dbs, - config, - setConfig, - dataProvider, - databaseConfig, - parentNode, - designerId - ) + findDesignerIdForNode(config, parentNode, designerId => + pattern?.columns?.find(x => x.name == col.columnName)?.types.includes('json') + ? new PerspectivePatternColumnNode( + table, + pattern?.columns?.find(x => x.name == col.columnName), + dbs, + config, + setConfig, + dataProvider, + databaseConfig, + parentNode, + designerId + ) + : new PerspectiveTableColumnNode( + col, + tableOrView, + dbs, + config, + setConfig, + dataProvider, + databaseConfig, + parentNode, + designerId + ) ) ) || pattern?.columns?.map(col => diff --git a/packages/web/src/utility/usePerspectiveDataPatterns.ts b/packages/web/src/utility/usePerspectiveDataPatterns.ts index 525a5dd67..f29c44cea 100644 --- a/packages/web/src/utility/usePerspectiveDataPatterns.ts +++ b/packages/web/src/utility/usePerspectiveDataPatterns.ts @@ -29,15 +29,7 @@ export function getPerspectiveDataPatternsFromCache( ); if (cached) { res[node.designerId] = cached; - continue; } - - const db = dbInfos?.[conid]?.[database]; - - if (!db) continue; - - const collection = db.collections?.find(x => x.pureName == pureName && x.schemaName == schemaName); - if (!collection) continue; } return res; @@ -69,20 +61,38 @@ export async function getPerspectiveDataPatterns( if (!db) continue; + const table = db.tables?.find(x => x.pureName == pureName && x.schemaName == schemaName); + const view = db.views?.find(x => x.pureName == pureName && x.schemaName == schemaName); const collection = db.collections?.find(x => x.pureName == pureName && x.schemaName == schemaName); - if (!collection) continue; + if (!table && !view && !collection) continue; + + // console.log('LOAD PATTERN FOR', pureName); const props: PerspectiveDataLoadProps = { databaseConfig: { conid, database }, - engineType: 'docdb', + engineType: collection ? 'docdb' : 'sqldb', + schemaName, pureName, - orderBy: [], + orderBy: table?.primaryKey + ? table?.primaryKey.columns.map(x => ({ columnName: x.columnName, order: 'ASC' })) + : table || view + ? [{ columnName: (table || view).columns[0].columnName, order: 'ASC' }] + : null, range: { offset: 0, limit: 10, }, }; + // console.log('LOAD PROPS', props); const rows = await dataLoader.loadData(props); + + if (rows.errorMessage) { + console.error('Error loading pattern for', pureName, ':', rows.errorMessage); + continue; + } + + // console.log('PATTERN ROWS', rows); + const pattern = analyseDataPattern( { conid, From d765591e8c5c1ce6bce80aec77ed9bcdb5289dd6 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 20:52:53 +0200 Subject: [PATCH 56/79] v5.1.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 22b44c5ea..a9b0fe531 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.4-beta.11", + "version": "5.1.4", "name": "dbgate-all", "workspaces": [ "packages/*", From 17b73a58c86719dba33da20be4aa82f823495163 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 20:56:05 +0200 Subject: [PATCH 57/79] changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 844ee97ee..605c8deae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ Builds: - linux - application for linux - win - application for Windows +### 5.1.4 +- ADDED: Drop database commands #384 +- ADDED: Customizable Redis key separator #379 +- ADDED: ARM support for docker images +- ADDED: Version tags for docker images +- ADDED: Better SQL command splitting and highlighting +- ADDED: Unsaved marker for SQL files + ### 5.1.3 - ADDED: Editing multiline cell values #378 #371 #359 - ADDED: Truncate table #333 From d0c7d591c8ae30d3f20d86a642f2163dc9658fcc Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 20:58:52 +0200 Subject: [PATCH 58/79] don't tag BETA docker images with version --- .github/workflows/build-docker.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-docker.yaml b/.github/workflows/build-docker.yaml index cfd88b948..b30de953d 100644 --- a/.github/workflows/build-docker.yaml +++ b/.github/workflows/build-docker.yaml @@ -34,7 +34,7 @@ jobs: flavor: | latest=false tags: | - type=match,pattern=\d+.\d+.\d+,suffix=-beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} +# type=match,pattern=\d+.\d+.\d+,suffix=-beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} type=raw,value=beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} type=match,pattern=\d+.\d+.\d+,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} @@ -49,7 +49,7 @@ jobs: flavor: | latest=false tags: | - type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} +# type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} type=raw,value=beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} type=match,pattern=\d+.\d+.\d+,suffix=-alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} From c712005e33ff633c3f31e67f6edac3da1c255693 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 21:00:29 +0200 Subject: [PATCH 59/79] v5.1.5-beta.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a9b0fe531..aaad4c9e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.4", + "version": "5.1.5-beta.1", "name": "dbgate-all", "workspaces": [ "packages/*", From d86a5c0cb456580bb5741791feeb104bd55182ac Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 21:02:23 +0200 Subject: [PATCH 60/79] fix --- .github/workflows/build-docker.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build-docker.yaml b/.github/workflows/build-docker.yaml index b30de953d..1764abab3 100644 --- a/.github/workflows/build-docker.yaml +++ b/.github/workflows/build-docker.yaml @@ -34,7 +34,6 @@ jobs: flavor: | latest=false tags: | -# type=match,pattern=\d+.\d+.\d+,suffix=-beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} type=raw,value=beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} type=match,pattern=\d+.\d+.\d+,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} @@ -49,7 +48,6 @@ jobs: flavor: | latest=false tags: | -# type=match,pattern=\d+.\d+.\d+,suffix=-beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} type=raw,value=beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }} type=match,pattern=\d+.\d+.\d+,suffix=-alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }} From 585806134982b3cf098851f2a9fafc4720cfcb89 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 21:03:22 +0200 Subject: [PATCH 61/79] v5.1.5-beta.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aaad4c9e4..d1ef2fff7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.5-beta.1", + "version": "5.1.5-beta.2", "name": "dbgate-all", "workspaces": [ "packages/*", From 18519b55194adf16569995fd71441700cd2ca746 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 2 Oct 2022 21:04:41 +0200 Subject: [PATCH 62/79] v5.1.5-beta.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d1ef2fff7..2ad363109 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.5-beta.2", + "version": "5.1.5-beta.3", "name": "dbgate-all", "workspaces": [ "packages/*", From 2adca6415941329f33832683e9538bb75caea65b Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Tue, 4 Oct 2022 20:58:10 +0200 Subject: [PATCH 63/79] socketPath configurable with env variables #358 --- packages/api/src/controllers/connections.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/api/src/controllers/connections.js b/packages/api/src/controllers/connections.js index 7aad6ebb1..63b3c872c 100644 --- a/packages/api/src/controllers/connections.js +++ b/packages/api/src/controllers/connections.js @@ -53,6 +53,8 @@ function getPortalCollections() { databaseUrl: process.env[`URL_${id}`], useDatabaseUrl: !!process.env[`URL_${id}`], databaseFile: process.env[`FILE_${id}`], + socketPath: process.env[`SOCKET_PATH_${id}`], + authType: process.env[`AUTH_TYPE_${id}`] || (process.env[`SOCKET_PATH_${id}`] ? 'socket' : undefined), defaultDatabase: process.env[`DATABASE_${id}`] || (process.env[`FILE_${id}`] ? getDatabaseFileLabel(process.env[`FILE_${id}`]) : null), From 13d4d3445345b2e900f5ae27843208b7c519cdd2 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 8 Oct 2022 08:09:45 +0200 Subject: [PATCH 64/79] cond-disable ER diagram and query design #386 --- packages/web/src/commands/stdCommands.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/web/src/commands/stdCommands.ts b/packages/web/src/commands/stdCommands.ts index 951a67597..d5d7834f8 100644 --- a/packages/web/src/commands/stdCommands.ts +++ b/packages/web/src/commands/stdCommands.ts @@ -127,6 +127,9 @@ registerCommand({ name: 'Query design', menuName: 'New query design', onClick: () => newQueryDesign(), + testEnabled: () => + getCurrentDatabase() && + findEngineDriver(getCurrentDatabase()?.connection, getExtensions())?.databaseEngineTypes?.includes('sql'), }); registerCommand({ @@ -144,6 +147,9 @@ registerCommand({ icon: 'img diagram', name: 'ER Diagram', menuName: 'New ER diagram', + testEnabled: () => + getCurrentDatabase() && + findEngineDriver(getCurrentDatabase()?.connection, getExtensions())?.databaseEngineTypes?.includes('sql'), onClick: () => newDiagram(), }); From 23cb3a4b12231af0658f85eb571346c5f3788a5f Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 8 Oct 2022 08:34:31 +0200 Subject: [PATCH 65/79] table&database ctx menu improvement --- .../web/src/appobj/DatabaseAppObject.svelte | 37 +++++++++++++++++-- .../src/appobj/DatabaseObjectAppObject.svelte | 22 +++++------ 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/packages/web/src/appobj/DatabaseAppObject.svelte b/packages/web/src/appobj/DatabaseAppObject.svelte index fba48176c..4f9cec01b 100644 --- a/packages/web/src/appobj/DatabaseAppObject.svelte +++ b/packages/web/src/appobj/DatabaseAppObject.svelte @@ -229,6 +229,30 @@ }); }; + const handleQueryDesigner = () => { + openNewTab({ + title: 'Query #', + icon: 'img query-design', + tabComponent: 'QueryDesignTab', + props: { + conid: connection._id, + database: name, + }, + }); + }; + + const handleNewPerspective = () => { + openNewTab({ + title: 'Perspective #', + icon: 'img perspective', + tabComponent: 'PerspectiveTab', + props: { + conid: connection._id, + database: name, + }, + }); + }; + async function handleConfirmSql(sql) { saveScriptToDatabase({ conid: connection._id, database: name }, sql, false); } @@ -244,16 +268,21 @@ { onClick: handleNewQuery, text: 'New query', isNewQuery: true }, driver?.databaseEngineTypes?.includes('sql') && { onClick: handleNewTable, text: 'New table' }, driver?.databaseEngineTypes?.includes('document') && { onClick: handleNewCollection, text: 'New collection' }, - isSqlOrDoc && - !connection.isReadOnly && - !connection.singleDatabase && { onClick: handleDropDatabase, text: 'Drop database' }, + driver?.databaseEngineTypes?.includes('sql') && { onClick: handleQueryDesigner, text: 'Design query' }, + driver?.databaseEngineTypes?.includes('sql') && { + onClick: handleNewPerspective, + text: 'Design perspective query', + }, { divider: true }, isSqlOrDoc && !connection.isReadOnly && { onClick: handleImport, text: 'Import wizard' }, isSqlOrDoc && { onClick: handleExport, text: 'Export wizard' }, driver?.databaseEngineTypes?.includes('sql') && { onClick: handleSqlRestore, text: 'Restore/import SQL dump' }, driver?.supportsDatabaseDump && { onClick: handleSqlDump, text: 'Backup/export SQL dump' }, + isSqlOrDoc && + !connection.isReadOnly && + !connection.singleDatabase && { onClick: handleDropDatabase, text: 'Drop database' }, { divider: true }, - isSqlOrDoc && { onClick: handleShowDiagram, text: 'Show diagram' }, + driver?.databaseEngineTypes?.includes('sql') && { onClick: handleShowDiagram, text: 'Show diagram' }, isSqlOrDoc && { onClick: handleSqlGenerator, text: 'SQL Generator' }, isSqlOrDoc && { onClick: handleOpenJsonModel, text: 'Open model as JSON' }, isSqlOrDoc && { onClick: handleExportModel, text: 'Export DB model - experimental' }, diff --git a/packages/web/src/appobj/DatabaseObjectAppObject.svelte b/packages/web/src/appobj/DatabaseObjectAppObject.svelte index a6cceab66..d5d0fac83 100644 --- a/packages/web/src/appobj/DatabaseObjectAppObject.svelte +++ b/packages/web/src/appobj/DatabaseObjectAppObject.svelte @@ -52,7 +52,12 @@ icon: 'img table-structure', }, { - label: 'Open perspective', + label: 'Design query', + isQueryDesigner: true, + requiresWriteAccess: true, + }, + { + label: 'Design perspective query', tab: 'PerspectiveTab', forceNewTab: true, icon: 'img perspective', @@ -80,11 +85,6 @@ isDuplicateTable: true, requiresWriteAccess: true, }, - { - label: 'Query designer', - isQueryDesigner: true, - requiresWriteAccess: true, - }, { label: 'Show diagram', isDiagram: true, @@ -155,7 +155,11 @@ icon: 'img view-structure', }, { - label: 'Open perspective', + label: 'Design query', + isQueryDesigner: true, + }, + { + label: 'Design perspective query', tab: 'PerspectiveTab', forceNewTab: true, icon: 'img perspective', @@ -164,10 +168,6 @@ label: 'Drop view', isDrop: true, }, - { - label: 'Query designer', - isQueryDesigner: true, - }, { divider: true, }, From 2a2debbb882d016183469a7deb69313d847d0925 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 8 Oct 2022 09:00:19 +0200 Subject: [PATCH 66/79] fix nested mongo id as $oid #387 --- plugins/dbgate-plugin-mongo/src/backend/driver.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/dbgate-plugin-mongo/src/backend/driver.js b/plugins/dbgate-plugin-mongo/src/backend/driver.js index 756c6a5a1..3d6c5a015 100644 --- a/plugins/dbgate-plugin-mongo/src/backend/driver.js +++ b/plugins/dbgate-plugin-mongo/src/backend/driver.js @@ -9,7 +9,9 @@ const AbstractCursor = require('mongodb').AbstractCursor; const createBulkInsertStream = require('./createBulkInsertStream'); function transformMongoData(row) { - return _.mapValues(row, (v) => (v && v.constructor == ObjectId ? { $oid: v.toString() } : v)); + return _.cloneDeepWith(row, (x) => { + if (x && x.constructor == ObjectId) return { $oid: x.toString() }; + }); } async function readCursor(cursor, options) { From f19835203fea16af9bb264f05bdebafa519a1699 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 8 Oct 2022 09:08:42 +0200 Subject: [PATCH 67/79] fixed pager component #388 --- packages/web/src/elements/Pager.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/src/elements/Pager.svelte b/packages/web/src/elements/Pager.svelte index 0f3af1384..d2db8556b 100644 --- a/packages/web/src/elements/Pager.svelte +++ b/packages/web/src/elements/Pager.svelte @@ -22,7 +22,7 @@
{ - skip -= limit; + skip = parseInt(skip) - parseInt(limit); if (skip < 0) skip = 0; dispatch('load'); }} @@ -35,7 +35,7 @@ dispatch('load')} on:keydown={handleKeyDown} /> { - skip += limit; + skip = parseInt(skip) + parseInt(limit); dispatch('load'); }} > From dc0001a8cd78129d22208755c560ff583c05de77 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 8 Oct 2022 21:14:18 +0200 Subject: [PATCH 68/79] sql case configuration #389 --- packages/tools/src/SqlDumper.ts | 16 +++++++++---- packages/web/src/App.svelte | 2 ++ .../web/src/modals/InsertJoinModal.svelte | 7 ++++-- packages/web/src/query/codeCompletion.ts | 23 +++++++++++++------ .../web/src/settings/SettingsModal.svelte | 13 +++++++++++ packages/web/src/settings/settingsTools.ts | 7 ++++++ .../web/src/utility/SettingsListener.svelte | 8 +++++++ 7 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 packages/web/src/utility/SettingsListener.svelte diff --git a/packages/tools/src/SqlDumper.ts b/packages/tools/src/SqlDumper.ts index 3a6de60c7..c8bb5c6af 100644 --- a/packages/tools/src/SqlDumper.ts +++ b/packages/tools/src/SqlDumper.ts @@ -32,6 +32,12 @@ export class SqlDumper implements AlterProcessor { dialect: SqlDialect; indentLevel = 0; + static keywordsCase = 'upperCase'; + static convertKeywordCase(keyword: any): string { + if (this.keywordsCase == 'lowerCase') return keyword?.toString()?.toLowerCase(); + return keyword?.toString()?.toUpperCase(); + } + constructor(driver: EngineDriver) { this.driver = driver; this.dialect = driver.dialect; @@ -60,10 +66,10 @@ export class SqlDumper implements AlterProcessor { this.putRaw("'"); } putByteArrayValue(value) { - this.putRaw('NULL'); + this.put('^null'); } putValue(value) { - if (value === null) this.putRaw('NULL'); + if (value === null) this.put('^null'); else if (value === true) this.putRaw('1'); else if (value === false) this.putRaw('0'); else if (_isString(value)) this.putStringValue(value); @@ -71,7 +77,7 @@ export class SqlDumper implements AlterProcessor { else if (_isDate(value)) this.putStringValue(new Date(value).toISOString()); else if (value?.type == 'Buffer' && _isArray(value?.data)) this.putByteArrayValue(value?.data); else if (_isPlainObject(value) || _isArray(value)) this.putStringValue(JSON.stringify(value)); - else this.putRaw('NULL'); + else this.put('^null'); } putCmd(format, ...args) { this.put(format, ...args); @@ -92,7 +98,7 @@ export class SqlDumper implements AlterProcessor { case 'k': { if (value) { - this.putRaw(value.toUpperCase()); + this.putRaw(SqlDumper.convertKeywordCase(value)); } } break; @@ -128,7 +134,7 @@ export class SqlDumper implements AlterProcessor { switch (c) { case '^': while (i < length && format[i].match(/[a-z0-9_]/i)) { - this.putRaw(format[i].toUpperCase()); + this.putRaw(SqlDumper.convertKeywordCase(format[i])); i++; } break; diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 67c6b22cb..6938737e8 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -19,6 +19,7 @@ import AppTitleProvider from './utility/AppTitleProvider.svelte'; import getElectron from './utility/getElectron'; import AppStartInfo from './widgets/AppStartInfo.svelte'; + import SettingsListener from './utility/SettingsListener.svelte'; let loadedApi = false; let loadedPlugins = false; @@ -79,6 +80,7 @@ {#if loadedPlugins} + {:else} + import { SqlDumper } from 'dbgate-tools'; import FormStyledButton from '../buttons/FormStyledButton.svelte'; import TableControl from '../elements/TableControl.svelte'; import TextField from '../forms/TextField.svelte'; @@ -63,9 +64,11 @@ const source = sources[sourceIndex]; const target = targets[targetIndex]; if (source && target) { - return `${JOIN_TYPES[joinIndex]} ${target.refTable}${alias ? ` ${alias}` : ''} ON ${target.columnMap + return `${SqlDumper.convertKeywordCase(JOIN_TYPES[joinIndex])} ${target.refTable}${ + alias ? ` ${alias}` : '' + } ${SqlDumper.convertKeywordCase('ON')} ${target.columnMap .map(col => `${source.name}.${col.columnName} = ${alias || target.refTable}.${col.refColumnName}`) - .join(' AND ')}`; + .join(SqlDumper.convertKeywordCase(' AND '))}`; } return ''; } diff --git a/packages/web/src/query/codeCompletion.ts b/packages/web/src/query/codeCompletion.ts index e59503b59..ddb662098 100644 --- a/packages/web/src/query/codeCompletion.ts +++ b/packages/web/src/query/codeCompletion.ts @@ -2,6 +2,7 @@ import _ from 'lodash'; import { addCompleter, setCompleters } from 'ace-builds/src-noconflict/ext-language_tools'; import { getDatabaseInfo } from '../utility/metadataLoaders'; import analyseQuerySources from './analyseQuerySources'; +import { getStringSettingsValue } from '../settings/settingsTools'; const COMMON_KEYWORDS = [ 'select', @@ -78,13 +79,21 @@ export function mountCodeCompletion({ conid, database, editor, getText }) { const line = session.getLine(cursor.row).slice(0, cursor.column); const dbinfo = await getDatabaseInfo({ conid, database }); - let list = COMMON_KEYWORDS.map(word => ({ - name: word, - value: word, - caption: word, - meta: 'keyword', - score: 800, - })); + const convertUpper = getStringSettingsValue('sqlEditor.sqlCommandsCase', 'upperCase') == 'upperCase'; + + let list = COMMON_KEYWORDS.map(word => { + if (convertUpper) { + word = word.toUpperCase(); + } + + return { + name: word, + value: word, + caption: word, + meta: 'keyword', + score: 800, + }; + }); if (dbinfo) { const colMatch = line.match(/([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]*)?$/); diff --git a/packages/web/src/settings/SettingsModal.svelte b/packages/web/src/settings/SettingsModal.svelte index 99e0ca357..ebf2d9a14 100644 --- a/packages/web/src/settings/SettingsModal.svelte +++ b/packages/web/src/settings/SettingsModal.svelte @@ -111,6 +111,19 @@ ORDER BY defaultValue="30" disabled={values['connection.autoRefresh'] === false} /> + +
SQL editor
+ +
Application theme
diff --git a/packages/web/src/settings/settingsTools.ts b/packages/web/src/settings/settingsTools.ts index 7c27ad203..dbf739eee 100644 --- a/packages/web/src/settings/settingsTools.ts +++ b/packages/web/src/settings/settingsTools.ts @@ -21,3 +21,10 @@ export function getBoolSettingsValue(name, defaultValue) { if (res == null) return defaultValue; return !!res; } + +export function getStringSettingsValue(name, defaultValue) { + const settings = getCurrentSettings(); + const res = settings[name]; + if (res == null) return defaultValue; + return res; +} diff --git a/packages/web/src/utility/SettingsListener.svelte b/packages/web/src/utility/SettingsListener.svelte new file mode 100644 index 000000000..7dedd6a87 --- /dev/null +++ b/packages/web/src/utility/SettingsListener.svelte @@ -0,0 +1,8 @@ + From f5906587db0568cdf91211c311a2bc2acfaef43c Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 13 Oct 2022 10:52:10 +0200 Subject: [PATCH 69/79] perspectives: prevent multi-load --- packages/web/src/perspectives/PerspectiveTable.svelte | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/web/src/perspectives/PerspectiveTable.svelte b/packages/web/src/perspectives/PerspectiveTable.svelte index 4f7990c4a..32a9b97d0 100644 --- a/packages/web/src/perspectives/PerspectiveTable.svelte +++ b/packages/web/src/perspectives/PerspectiveTable.svelte @@ -57,6 +57,7 @@ let errorMessage; let rowCount; let isLoading = false; + let isLoadQueued = false; const lastVisibleRowIndexRef = createRef(0); const disableLoadNextRef = createRef(false); @@ -121,6 +122,12 @@ } async function loadData(node: PerspectiveTreeNode, counts) { + if (isLoading) { + isLoadQueued = true; + return; + } else { + isLoadQueued = false; + } // console.log('LOADING', node); if (!node) return; const rows = []; @@ -147,6 +154,10 @@ // loadProps.push(child.getNodeLoadProps()); // } // } + + if (isLoadQueued) { + loadData(root, $loadedCounts); + } } export function openJson() { From a240681d6d468cd1eae55e7e168fc2184ad81568 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 13 Oct 2022 11:02:25 +0200 Subject: [PATCH 70/79] auto view json #395 --- packages/web/src/perspectives/PerspectiveCell.svelte | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/web/src/perspectives/PerspectiveCell.svelte b/packages/web/src/perspectives/PerspectiveCell.svelte index 2a4708903..e4750dfd6 100644 --- a/packages/web/src/perspectives/PerspectiveCell.svelte +++ b/packages/web/src/perspectives/PerspectiveCell.svelte @@ -1,6 +1,6 @@ - + {#if value !== undefined} {#if displayType == 'json'} @@ -23,6 +23,8 @@ {:else} (no image) {/if} + {:else if _.isArray(value) || _.isPlainObject(value)} + {:else} {/if} From 44be1bdd11af3cf00f8bca6c52897f4ec21879e1 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 13 Oct 2022 12:36:48 +0200 Subject: [PATCH 71/79] menu label --- packages/web/src/appobj/DatabaseObjectAppObject.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/appobj/DatabaseObjectAppObject.svelte b/packages/web/src/appobj/DatabaseObjectAppObject.svelte index 2794baf57..375d8e86a 100644 --- a/packages/web/src/appobj/DatabaseObjectAppObject.svelte +++ b/packages/web/src/appobj/DatabaseObjectAppObject.svelte @@ -346,7 +346,7 @@ }, }, { - label: 'Open perspective', + label: 'Design perspective query', tab: 'PerspectiveTab', forceNewTab: true, icon: 'img perspective', From 0028240552be3b418f9c2a62970daa2b6fc7dd5e Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 13 Oct 2022 13:49:48 +0200 Subject: [PATCH 72/79] perspective fix --- packages/datalib/src/PerspectiveConfig.ts | 7 ++++++ .../datalib/src/PerspectiveDataProvider.ts | 9 ++------ packages/datalib/src/PerspectiveTreeNode.ts | 23 ++++++++++++++----- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/datalib/src/PerspectiveConfig.ts b/packages/datalib/src/PerspectiveConfig.ts index 1f8ec5a89..d7af928f6 100644 --- a/packages/datalib/src/PerspectiveConfig.ts +++ b/packages/datalib/src/PerspectiveConfig.ts @@ -7,6 +7,13 @@ import uuidv1 from 'uuid/v1'; // uncheckedColumns: string[]; // } +export type PerspectiveDatabaseEngineType = 'sqldb' | 'docdb'; + +export interface PerspectiveDatabaseConfig { + conid: string; + database: string; +} + export interface PerspectiveCustomJoinConfig { refNodeDesignerId: string; referenceDesignerId: string; diff --git a/packages/datalib/src/PerspectiveDataProvider.ts b/packages/datalib/src/PerspectiveDataProvider.ts index 18166bb97..5c471c55e 100644 --- a/packages/datalib/src/PerspectiveDataProvider.ts +++ b/packages/datalib/src/PerspectiveDataProvider.ts @@ -4,17 +4,12 @@ import { RangeDefinition } from 'dbgate-types'; import { PerspectiveBindingGroup, PerspectiveCache } from './PerspectiveCache'; import { PerspectiveDataLoader } from './PerspectiveDataLoader'; import { PerspectiveDataPatternDict } from './PerspectiveDataPattern'; +import { PerspectiveDatabaseConfig, PerspectiveDatabaseEngineType } from './PerspectiveConfig'; export const PERSPECTIVE_PAGE_SIZE = 100; const dbg = debug('dbgate:PerspectiveDataProvider'); - -export interface PerspectiveDatabaseConfig { - conid: string; - database: string; -} - export interface PerspectiveDataLoadProps { databaseConfig: PerspectiveDatabaseConfig; schemaName?: string; @@ -31,7 +26,7 @@ export interface PerspectiveDataLoadProps { topCount?: number; sqlCondition?: Condition; mongoCondition?: any; - engineType: 'sqldb' | 'docdb'; + engineType: PerspectiveDatabaseEngineType; } export class PerspectiveDataProvider { diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index dd6bf3910..13ca89456 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -15,6 +15,8 @@ import { MultipleDatabaseInfo, PerspectiveConfig, PerspectiveCustomJoinConfig, + PerspectiveDatabaseConfig, + PerspectiveDatabaseEngineType, PerspectiveFilterColumnInfo, PerspectiveNodeConfig, PerspectiveReferenceConfig, @@ -28,11 +30,7 @@ import _uniqBy from 'lodash/uniqBy'; import _sortBy from 'lodash/sortBy'; import _cloneDeepWith from 'lodash/cloneDeepWith'; import _findIndex from 'lodash/findIndex'; -import { - PerspectiveDatabaseConfig, - PerspectiveDataLoadProps, - PerspectiveDataProvider, -} from './PerspectiveDataProvider'; +import { PerspectiveDataLoadProps, PerspectiveDataProvider } from './PerspectiveDataProvider'; import stableStringify from 'json-stable-stringify'; import { getFilterType, parseFilter } from 'dbgate-filterparser'; import { FilterType } from 'dbgate-filterparser/lib/types'; @@ -119,6 +117,9 @@ export abstract class PerspectiveTreeNode { } return this.parentNode.parentTableNode; } + get engineType(): PerspectiveDatabaseEngineType { + return null; + } abstract getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps; get isRoot() { return this.parentNode == null; @@ -426,7 +427,9 @@ export abstract class PerspectiveTreeNode { return ( (this.parentNode?.isRoot || this.parentNode?.supportsParentFilter) && this.parentNode?.databaseConfig?.conid == this.databaseConfig?.conid && - this.parentNode?.databaseConfig?.database == this.databaseConfig?.database + this.parentNode?.databaseConfig?.database == this.databaseConfig?.database && + this.engineType == 'sqldb' && + this.parentNode?.engineType == 'sqldb' ); } @@ -532,6 +535,10 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode { ); } + get engineType() { + return this.parentNode.engineType; + } + matchChildRow(parentRow: any, childRow: any): boolean { if (!this.foreignKey) return false; return parentRow[this.foreignKey.columns[0].columnName] == childRow[this.foreignKey.columns[0].refColumnName]; @@ -942,6 +949,10 @@ export class PerspectiveTableNode extends PerspectiveTreeNode { super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig, designerId); } + get engineType(): PerspectiveDatabaseEngineType { + return isCollectionInfo(this.table) ? 'docdb' : 'sqldb'; + } + getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { const isMongo = isCollectionInfo(this.table); return { From 225518df3ea5f71bfe7f5db20aa1c6331e099a22 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 13 Oct 2022 14:09:08 +0200 Subject: [PATCH 73/79] show json icon in perspectives --- packages/datalib/src/PerspectiveDataPattern.ts | 3 ++- packages/datalib/src/PerspectiveTreeNode.ts | 7 +++++++ packages/web/src/designer/ColumnLine.svelte | 5 ++++- packages/web/src/elements/ColumnLabel.svelte | 3 ++- packages/web/src/perspectives/PerspectiveCell.svelte | 2 +- packages/web/src/perspectives/PerspectiveDesigner.svelte | 8 ++++++++ 6 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/datalib/src/PerspectiveDataPattern.ts b/packages/datalib/src/PerspectiveDataPattern.ts index d51e20349..876ffc00a 100644 --- a/packages/datalib/src/PerspectiveDataPattern.ts +++ b/packages/datalib/src/PerspectiveDataPattern.ts @@ -7,7 +7,7 @@ import _isBoolean from 'lodash/isBoolean'; import _isArray from 'lodash/isArray'; import { safeJsonParse } from 'dbgate-tools'; -export type PerspectiveDataPatternColumnType = 'null' | 'string' | 'number' | 'boolean' | 'json'; +export type PerspectiveDataPatternColumnType = 'null' | 'oid' | 'string' | 'number' | 'boolean' | 'json'; export interface PerspectiveDataPatternColumn { name: string; @@ -29,6 +29,7 @@ function detectValueType(value): PerspectiveDataPatternColumnType { if (_isString(value)) return 'string'; if (_isNumber(value)) return 'number'; if (_isBoolean(value)) return 'boolean'; + if (value?.$oid) return 'oid'; if (_isPlainObject(value) || _isArray(value)) return 'json'; if (value == null) return 'null'; } diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index 13ca89456..ae9d8cd9c 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -818,6 +818,9 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { // } get icon() { + if (this.column.types.includes('json')) { + return 'img json'; + } return 'img column'; } @@ -885,6 +888,10 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { } get filterInfo(): PerspectiveFilterColumnInfo { + if (this.isChildColumn) { + return null; + } + return { columnName: this.columnName, filterType: this.filterType, diff --git a/packages/web/src/designer/ColumnLine.svelte b/packages/web/src/designer/ColumnLine.svelte index 096d542d1..4a3284e8a 100644 --- a/packages/web/src/designer/ColumnLine.svelte +++ b/packages/web/src/designer/ColumnLine.svelte @@ -61,6 +61,9 @@ } $: sortOrderProps = settings?.getSortOrderProps ? settings?.getSortOrderProps(designerId, column.columnName) : null; + $: iconOverride = settings?.getColumnIconOverride + ? settings?.getColumnIconOverride(designerId, column.columnName) + : null;
{/if} - + {#if designerColumn?.filter} {/if} diff --git a/packages/web/src/elements/ColumnLabel.svelte b/packages/web/src/elements/ColumnLabel.svelte index abd6b11b7..ffb77ba86 100644 --- a/packages/web/src/elements/ColumnLabel.svelte +++ b/packages/web/src/elements/ColumnLabel.svelte @@ -23,8 +23,9 @@ export let foreignKey; export let conid = undefined; export let database = undefined; + export let iconOverride = undefined; - $: icon = getColumnIcon($$props, forceIcon); + $: icon = iconOverride || getColumnIcon($$props, forceIcon); diff --git a/packages/web/src/perspectives/PerspectiveCell.svelte b/packages/web/src/perspectives/PerspectiveCell.svelte index e4750dfd6..b95168b74 100644 --- a/packages/web/src/perspectives/PerspectiveCell.svelte +++ b/packages/web/src/perspectives/PerspectiveCell.svelte @@ -23,7 +23,7 @@ {:else} (no image) {/if} - {:else if _.isArray(value) || _.isPlainObject(value)} + {:else if !value.$oid && (_.isArray(value) || _.isPlainObject(value))} {:else} diff --git a/packages/web/src/perspectives/PerspectiveDesigner.svelte b/packages/web/src/perspectives/PerspectiveDesigner.svelte index 44639b35e..7fb2fd625 100644 --- a/packages/web/src/perspectives/PerspectiveDesigner.svelte +++ b/packages/web/src/perspectives/PerspectiveDesigner.svelte @@ -241,6 +241,14 @@ const orderIndex = sort.length > 1 ? _.findIndex(sort, x => x.columnName == columnName) : -1; return { order, orderIndex }; }, + getColumnIconOverride: (designerId, columnName) => { + const pattern = dataPatterns?.[designerId]; + const column = pattern?.columns.find(x => x.name == columnName); + if (column?.types?.includes('json')) { + return 'img json'; + } + return null; + }, isColumnFiltered: (designerId, columnName) => { return !!config.nodes.find(x => x.designerId == designerId)?.filters?.[columnName]; }, From bf4eb19ef5d01ac591812728b17592f90e11512e Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 13 Oct 2022 15:01:45 +0200 Subject: [PATCH 74/79] perspective fixes --- packages/datalib/src/PerspectiveTreeNode.ts | 17 +++- .../src/perspectives/PerspectiveTable.svelte | 98 +++++++++++-------- 2 files changed, 71 insertions(+), 44 deletions(-) diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index ae9d8cd9c..f3fdd0704 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -715,6 +715,7 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode { pureName: this.foreignKey.refTableName, conid: this.databaseConfig.conid, database: this.databaseConfig.database, + objectTypeField: this.table.objectTypeField, }; } return null; @@ -743,8 +744,9 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { refTable: TableInfo; constructor( - public owner: NamedObjectInfo, + public table: TableInfo | ViewInfo | CollectionInfo, public column: PerspectiveDataPatternColumn, + public tableColumn: ColumnInfo, dbs: MultipleDatabaseInfo, config: PerspectiveConfig, setConfig: ChangePerspectiveConfigFunc, @@ -848,10 +850,11 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { } get isSortable() { - return true; + return !this.isChildColumn; } get filterType(): FilterType { + if (this.tableColumn) return getFilterType(this.tableColumn.dataType); return 'mongo'; } @@ -859,8 +862,9 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { return this.column.columns.map( column => new PerspectivePatternColumnNode( - this.owner, + this.table, column, + this.tableColumn, this.dbs, this.config, this.setConfig, @@ -895,8 +899,8 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { return { columnName: this.columnName, filterType: this.filterType, - pureName: this.owner.pureName, - schemaName: this.owner.schemaName, + pureName: this.table.pureName, + schemaName: this.table.schemaName, foreignKey: this.foreignKey, }; } @@ -1013,6 +1017,7 @@ export class PerspectiveTableNode extends PerspectiveTreeNode { pureName: this.table.pureName, conid: this.databaseConfig.conid, database: this.databaseConfig.database, + objectTypeField: this.table.objectTypeField, }; } @@ -1309,6 +1314,7 @@ export function getTableChildPerspectiveNodes( ? new PerspectivePatternColumnNode( table, pattern?.columns?.find(x => x.name == col.columnName), + col, dbs, config, setConfig, @@ -1338,6 +1344,7 @@ export function getTableChildPerspectiveNodes( new PerspectivePatternColumnNode( table, col, + null, dbs, config, setConfig, diff --git a/packages/web/src/perspectives/PerspectiveTable.svelte b/packages/web/src/perspectives/PerspectiveTable.svelte index 32a9b97d0..09277e76d 100644 --- a/packages/web/src/perspectives/PerspectiveTable.svelte +++ b/packages/web/src/perspectives/PerspectiveTable.svelte @@ -16,6 +16,7 @@ ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveDisplay, + PerspectivePatternColumnNode, PerspectiveTableColumnNode, PerspectiveTreeNode, PERSPECTIVE_PAGE_SIZE, @@ -41,6 +42,24 @@ import { getFilterValueExpression } from 'dbgate-filterparser'; import StatusBarTabItem from '../widgets/StatusBarTabItem.svelte'; + const TABS_BY_FIELD = { + tables: { + text: 'table', + tabComponent: 'TableDataTab', + icon: 'img table', + }, + views: { + text: 'view', + tabComponent: 'ViewDataTab', + icon: 'img view', + }, + collections: { + text: 'collection', + tabComponent: 'CollectionDataTab', + icon: 'img collection', + }, + }; + const dbg = debug('dbgate:PerspectiveTable'); export const activator = createActivator('PerspectiveTable', true, ['Designer']); @@ -210,24 +229,28 @@ const tableNode = root?.findNodeByDesignerId(tableNodeDesignerId); if (tableNode?.headerTableAttributes) { - const { pureName, schemaName, conid, database } = tableNode?.headerTableAttributes; - res.push({ - text: `Open table ${pureName}`, - onClick: () => { - openNewTab({ - title: pureName, - icon: 'img table', - tabComponent: 'TableDataTab', - props: { - schemaName, - pureName, - conid: conid, - database: database, - objectTypeField: 'tables', - }, - }); - }, - }); + const { pureName, schemaName, conid, database, objectTypeField } = tableNode?.headerTableAttributes; + console.log('objectTypeField', objectTypeField); + const tab = TABS_BY_FIELD[objectTypeField]; + if (tab) { + res.push({ + text: `Open ${tab.text} ${pureName}`, + onClick: () => { + openNewTab({ + title: pureName, + icon: tab.icon, + tabComponent: tab.tabComponent, + props: { + schemaName, + pureName, + conid: conid, + database: database, + objectTypeField, + }, + }); + }, + }); + } } const setColumnDisplay = type => { @@ -291,42 +314,39 @@ const value = display.rows[rowIndex].rowData[columnIndex]; const { dataNode } = column; - if (dataNode instanceof PerspectiveTableColumnNode) { + if ( + dataNode.filterInfo && + (dataNode instanceof PerspectiveTableColumnNode || dataNode instanceof PerspectivePatternColumnNode) + ) { const { table } = dataNode; - let tabComponent = null; - let icon = null; - let objectTypeField = null; - if (dataNode.isTable) { - tabComponent = 'TableDataTab'; - icon = 'img table'; - objectTypeField = 'tables'; - } - if (dataNode.isView) { - tabComponent = 'ViewDataTab'; - icon = 'img view'; - objectTypeField = 'views'; - } - if (tabComponent) { + + const tab = TABS_BY_FIELD[table.objectTypeField]; + const filterExpression = getFilterValueExpression( + value, + dataNode instanceof PerspectiveTableColumnNode ? dataNode.column.dataType : null + ); + + if (tab) { res.push({ - text: 'Open filtered table', + text: 'Open filtered grid', onClick: () => { openNewTab( { title: table.pureName, - icon, - tabComponent, + icon: tab.icon, + tabComponent: tab.tabComponent, props: { schemaName: table.schemaName, pureName: table.pureName, conid, database, - objectTypeField, + objectTypeField: table.objectTypeField, }, }, { grid: { filters: { - [dataNode.columnName]: getFilterValueExpression(value, dataNode.column.dataType), + [dataNode.columnName]: filterExpression, }, // isFormView: true, }, @@ -350,7 +370,7 @@ ...n, filters: { ...n.filters, - [dataNode.columnName]: getFilterValueExpression(value, dataNode.column.dataType), + [dataNode.columnName]: filterExpression, }, } : n From 69a87bc076179dcc744ec70c7f9a30b874e6880a Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 13 Oct 2022 15:22:23 +0200 Subject: [PATCH 75/79] perspective expand fix --- packages/datalib/src/PerspectiveTreeNode.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/datalib/src/PerspectiveTreeNode.ts b/packages/datalib/src/PerspectiveTreeNode.ts index f3fdd0704..4518c149f 100644 --- a/packages/datalib/src/PerspectiveTreeNode.ts +++ b/packages/datalib/src/PerspectiveTreeNode.ts @@ -108,14 +108,14 @@ export abstract class PerspectiveTreeNode { get namedObject(): NamedObjectInfo { return null; } - get parentTableNode(): PerspectiveTableNode { + get tableNodeOrParent(): PerspectiveTableNode { if (this instanceof PerspectiveTableNode) { return this; } if (this.parentNode == null) { return null; } - return this.parentNode.parentTableNode; + return this.parentNode.tableNodeOrParent; } get engineType(): PerspectiveDatabaseEngineType { return null; @@ -289,14 +289,15 @@ export abstract class PerspectiveTreeNode { [field]: isIncluded ? [...(n[field] || []), this.codeName] : (n[field] || []).filter(x => x != this.codeName), }); - const [cfgChanged, nodeCfg] = this.parentTableNode?.ensureNodeConfig(cfg); + const [cfgChanged, nodeCfg] = this.parentNode?.tableNodeOrParent?.ensureNodeConfig(cfg); - return { + const res = { ...cfgChanged, nodes: cfgChanged.nodes.map(n => - n.designerId == (this.parentTableNode?.designerId || nodeCfg?.designerId) ? changedFields(n) : n + n.designerId == (this.parentNode?.tableNodeOrParent?.designerId || nodeCfg?.designerId) ? changedFields(n) : n ), }; + return res; }); } @@ -756,7 +757,7 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { designerId: string ) { super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig, designerId); - this.parentNodeConfig = this.parentTableNode?.nodeConfig; + this.parentNodeConfig = this.tableNodeOrParent?.nodeConfig; } get isChildColumn() { @@ -812,7 +813,7 @@ export class PerspectivePatternColumnNode extends PerspectiveTreeNode { get generatesHiearchicGridColumn() { // console.log('generatesHiearchicGridColumn', this.parentTableNode?.nodeConfig?.checkedColumns, this.codeName + '::'); - return !!this.parentTableNode?.nodeConfig?.checkedColumns?.find(x => x.startsWith(this.codeName + '::')); + return !!this.tableNodeOrParent?.nodeConfig?.checkedColumns?.find(x => x.startsWith(this.codeName + '::')); } // get generatesHiearchicGridColumn() { From 494c3c8e4a3d9969e129d8a983aa74d8cb105a23 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 13 Oct 2022 15:28:50 +0200 Subject: [PATCH 76/79] remap Command+H on mac #390 --- packages/web/src/commands/stdCommands.ts | 2 +- packages/web/src/datagrid/DataGridCore.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/src/commands/stdCommands.ts b/packages/web/src/commands/stdCommands.ts index d5d7834f8..14961db3c 100644 --- a/packages/web/src/commands/stdCommands.ts +++ b/packages/web/src/commands/stdCommands.ts @@ -605,7 +605,7 @@ export function registerFileCommands({ registerCommand({ id: idPrefix + '.replace', category, - keyText: 'CtrlOrCommand+H', + keyText: isMac() ? 'Alt+Command+F' : 'CtrlOrCommand+H', name: 'Replace', testEnabled: () => getCurrentEditor() != null, onClick: () => getCurrentEditor().replace(), diff --git a/packages/web/src/datagrid/DataGridCore.svelte b/packages/web/src/datagrid/DataGridCore.svelte index 83b7bc7f1..15e4f998b 100644 --- a/packages/web/src/datagrid/DataGridCore.svelte +++ b/packages/web/src/datagrid/DataGridCore.svelte @@ -200,7 +200,7 @@ id: 'dataGrid.hideColumn', category: 'Data grid', name: 'Hide column', - keyText: 'CtrlOrCommand+H', + keyText: isMac() ? 'Alt+Command+F' : 'CtrlOrCommand+H', testEnabled: () => getCurrentDataGrid() != null, onClick: () => getCurrentDataGrid().hideColumn(), }); From 42333a97b8d4eb8b717a133f6ed0ad663000038b Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Thu, 13 Oct 2022 15:36:24 +0200 Subject: [PATCH 77/79] v5.1.5-beta.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ad363109..eddfd78ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.5-beta.3", + "version": "5.1.5-beta.4", "name": "dbgate-all", "workspaces": [ "packages/*", From 0497f541cb1505087161a4b805c55780f9b71585 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 15 Oct 2022 22:08:57 +0200 Subject: [PATCH 78/79] changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 605c8deae..da78e8708 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,16 @@ Builds: - linux - application for linux - win - application for Windows +### 5.1.5 +- ADDED: Support perspectives for MongoDB - MongoDB query designer +- ADDED: Show JSON content directly in the overview #395 +- CHANGED: OSX Command H shortcut for hiding window #390 +- ADDED: Uppercase Autocomplete Suggestions #389 +- FIXED: Record view left/right arrows cause start record number to be treated as string #388 +- FIXED: MongoDb ObjectId behaviour not consistent in nested objects #387 +- FIXED: demo.dbgate.org - beta version crash 5.1.5-beta.3 #386 +- ADDED: connect via socket - configurable via environment variables #358 + ### 5.1.4 - ADDED: Drop database commands #384 - ADDED: Customizable Redis key separator #379 From 310774db3b2becab4c71469cb14659625c8768ac Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 15 Oct 2022 22:10:11 +0200 Subject: [PATCH 79/79] v5.1.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eddfd78ed..eeff09400 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "5.1.5-beta.4", + "version": "5.1.5", "name": "dbgate-all", "workspaces": [ "packages/*",