mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-28 21:05:59 +00:00
insert SQL join
This commit is contained in:
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
import { compact } from 'lodash';
|
import { compact } from 'lodash';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import keycodes from '../utility/keycodes';
|
||||||
|
|
||||||
export let columns: TableControlColumn[];
|
export let columns: TableControlColumn[];
|
||||||
export let rows;
|
export let rows;
|
||||||
@@ -21,16 +22,31 @@
|
|||||||
export let selectable = false;
|
export let selectable = false;
|
||||||
export let selectedIndex = 0;
|
export let selectedIndex = 0;
|
||||||
|
|
||||||
$: columnList = _.compact(_.flatten(columns));
|
export let domTable;
|
||||||
|
|
||||||
let domTable;
|
$: columnList = _.compact(_.flatten(columns));
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (focusOnCreate) domTable.focus();
|
if (focusOnCreate) domTable.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleKeyDown = event => {
|
||||||
|
if (event.keyCode == keycodes.downArrow) {
|
||||||
|
selectedIndex = Math.min(selectedIndex + 1, rows.length - 1);
|
||||||
|
}
|
||||||
|
if (event.keyCode == keycodes.upArrow) {
|
||||||
|
selectedIndex = Math.max(0, selectedIndex - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<table bind:this={domTable} class:selectable>
|
<table
|
||||||
|
bind:this={domTable}
|
||||||
|
class:selectable
|
||||||
|
on:keydown
|
||||||
|
tabindex={selectable ? -1 : undefined}
|
||||||
|
on:keydown={handleKeyDown}
|
||||||
|
>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{#each columnList as col}
|
{#each columnList as col}
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
|
|
||||||
export let value;
|
export let value;
|
||||||
export let focused;
|
export let focused;
|
||||||
|
export let domEditor;
|
||||||
let domEditor;
|
|
||||||
|
|
||||||
if (focused) onMount(() => domEditor.focus());
|
if (focused) onMount(() => domEditor.focus());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input type="text" {...$$restProps} bind:value on:change on:input bind:this={domEditor} />
|
<input type="text" {...$$restProps} bind:value on:change on:input bind:this={domEditor} on:keydown />
|
||||||
|
|||||||
187
packages/web/src/modals/InsertJoinModal.svelte
Normal file
187
packages/web/src/modals/InsertJoinModal.svelte
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FormStyledButton from '../elements/FormStyledButton.svelte';
|
||||||
|
import TableControl from '../elements/TableControl.svelte';
|
||||||
|
import TextField from '../forms/TextField.svelte';
|
||||||
|
import analyseQuerySources from '../query/analyseQuerySources';
|
||||||
|
import SqlEditor from '../query/SqlEditor.svelte';
|
||||||
|
import keycodes from '../utility/keycodes';
|
||||||
|
|
||||||
|
import ModalBase from './ModalBase.svelte';
|
||||||
|
import { closeCurrentModal } from './modalTools';
|
||||||
|
|
||||||
|
export let sql;
|
||||||
|
export let onInsert;
|
||||||
|
export let dbinfo;
|
||||||
|
export let engine;
|
||||||
|
|
||||||
|
let sourceIndex = 0;
|
||||||
|
let targetIndex = 0;
|
||||||
|
let joinIndex = 0;
|
||||||
|
let alias = '';
|
||||||
|
|
||||||
|
let domSource = null;
|
||||||
|
let domTarget = null;
|
||||||
|
let domAlias = null;
|
||||||
|
let domJoin = null;
|
||||||
|
|
||||||
|
const JOIN_TYPES = ['INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN'];
|
||||||
|
|
||||||
|
$: sources = analyseQuerySources(sql, [...dbinfo.tables.map(x => x.pureName), ...dbinfo.views.map(x => x.pureName)]);
|
||||||
|
|
||||||
|
$: targets = computeTargets(sources, sourceIndex);
|
||||||
|
|
||||||
|
function computeTargets(sources, sourceIndex) {
|
||||||
|
const source = sources[sourceIndex];
|
||||||
|
if (!source) return [];
|
||||||
|
/** @type {import('dbgate-types').TableInfo} */
|
||||||
|
const table = dbinfo.tables.find(x => x.pureName == sources[sourceIndex].name);
|
||||||
|
if (!table) return [];
|
||||||
|
return [
|
||||||
|
...table.foreignKeys.map(fk => ({
|
||||||
|
baseColumns: fk.columns.map(x => x.columnName).join(', '),
|
||||||
|
refTable: fk.refTableName,
|
||||||
|
refColumns: fk.columns.map(x => x.refColumnName).join(', '),
|
||||||
|
constraintName: fk.constraintName,
|
||||||
|
columnMap: fk.columns,
|
||||||
|
})),
|
||||||
|
...table.dependencies.map(fk => ({
|
||||||
|
baseColumns: fk.columns.map(x => x.refColumnName).join(', '),
|
||||||
|
refTable: fk.pureName,
|
||||||
|
refColumns: fk.columns.map(x => x.columnName).join(', '),
|
||||||
|
constraintName: fk.constraintName,
|
||||||
|
columnMap: fk.columns.map(x => ({
|
||||||
|
columnName: x.refColumnName,
|
||||||
|
refColumnName: x.columnName,
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$: sqlPreview = computePreview(joinIndex, sources, targets, sourceIndex, targetIndex, alias);
|
||||||
|
|
||||||
|
function computePreview(joinIndex, sources, targets, sourceIndex, targetIndex, alias) {
|
||||||
|
const source = sources[sourceIndex];
|
||||||
|
const target = targets[targetIndex];
|
||||||
|
if (source && target) {
|
||||||
|
return `${JOIN_TYPES[joinIndex]} ${target.refTable}${alias ? ` ${alias}` : ''} ON ${target.columnMap
|
||||||
|
.map(col => `${source.name}.${col.columnName} = ${alias || target.refTable}.${col.refColumnName}`)
|
||||||
|
.join(' AND ')}`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceKeyDown = event => {
|
||||||
|
if (event.keyCode == keycodes.enter || event.keyCode == keycodes.rightArrow) {
|
||||||
|
domTarget.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const targetKeyDown = event => {
|
||||||
|
if (event.keyCode == keycodes.leftArrow) {
|
||||||
|
domSource.focus();
|
||||||
|
}
|
||||||
|
if (event.keyCode == keycodes.enter || event.keyCode == keycodes.rightArrow) {
|
||||||
|
domJoin.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const joinKeyDown = event => {
|
||||||
|
if (event.keyCode == keycodes.leftArrow) {
|
||||||
|
domTarget.focus();
|
||||||
|
}
|
||||||
|
if (event.keyCode == keycodes.enter) {
|
||||||
|
domAlias.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const aliasKeyDown = event => {
|
||||||
|
if (event.keyCode == keycodes.enter) {
|
||||||
|
event.preventDefault();
|
||||||
|
closeCurrentModal();
|
||||||
|
onInsert(sqlPreview);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ModalBase {...$$restProps}>
|
||||||
|
<svelte:fragment slot="header">Insert join</svelte:fragment>
|
||||||
|
|
||||||
|
<div class="flex mb-3">
|
||||||
|
<div class="m-1">
|
||||||
|
<div class="m-1">Existing table</div>
|
||||||
|
|
||||||
|
<TableControl
|
||||||
|
rows={sources}
|
||||||
|
focusOnCreate
|
||||||
|
bind:selectedIndex={sourceIndex}
|
||||||
|
bind:domTable={domSource}
|
||||||
|
selectable
|
||||||
|
on:keydown={sourceKeyDown}
|
||||||
|
columns={[
|
||||||
|
{ fieldName: 'alias', header: 'Alias' },
|
||||||
|
{ fieldName: 'name', header: 'Name' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-1">
|
||||||
|
<div class="m-1">New table</div>
|
||||||
|
|
||||||
|
<TableControl
|
||||||
|
rows={targets}
|
||||||
|
bind:selectedIndex={targetIndex}
|
||||||
|
bind:domTable={domTarget}
|
||||||
|
selectable
|
||||||
|
on:keydown={targetKeyDown}
|
||||||
|
columns={[
|
||||||
|
{ fieldName: 'baseColumns', header: 'Column from' },
|
||||||
|
{ fieldName: 'refTable', header: 'Table to' },
|
||||||
|
{ fieldName: 'refColumns', header: 'Column to' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-1">
|
||||||
|
<div class="m-1">Join</div>
|
||||||
|
|
||||||
|
<TableControl
|
||||||
|
rows={JOIN_TYPES.map(name => ({ name }))}
|
||||||
|
bind:selectedIndex={joinIndex}
|
||||||
|
bind:domTable={domJoin}
|
||||||
|
selectable
|
||||||
|
on:keydown={joinKeyDown}
|
||||||
|
columns={[{ fieldName: 'name', header: 'Join type' }]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="m-1">Alias</div>
|
||||||
|
<TextField
|
||||||
|
value={alias}
|
||||||
|
on:input={e => {
|
||||||
|
// @ts-ignore
|
||||||
|
alias = e.target.value;
|
||||||
|
}}
|
||||||
|
bind:domEditor={domAlias}
|
||||||
|
on:keydown={aliasKeyDown}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sql">
|
||||||
|
<SqlEditor readOnly value={sqlPreview} {engine} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<svelte:fragment slot="footer">
|
||||||
|
<FormStyledButton
|
||||||
|
value="OK"
|
||||||
|
onClick={() => {
|
||||||
|
closeCurrentModal();
|
||||||
|
onInsert(sqlPreview);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormStyledButton type="button" value="Close" onClick={closeCurrentModal} />
|
||||||
|
</svelte:fragment>
|
||||||
|
</ModalBase>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.sql {
|
||||||
|
position: relative;
|
||||||
|
height: 80px;
|
||||||
|
width: 40vw;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -10,6 +10,14 @@
|
|||||||
testEnabled: () => getCurrentEditor() != null,
|
testEnabled: () => getCurrentEditor() != null,
|
||||||
onClick: () => getCurrentEditor().formatCode(),
|
onClick: () => getCurrentEditor().formatCode(),
|
||||||
});
|
});
|
||||||
|
registerCommand({
|
||||||
|
id: 'query.insertSqlJoin',
|
||||||
|
category: 'Query',
|
||||||
|
name: 'Insert SQL Join',
|
||||||
|
keyText: 'Ctrl+J',
|
||||||
|
testEnabled: () => getCurrentEditor() != null,
|
||||||
|
onClick: () => getCurrentEditor().insertSqlJoin(),
|
||||||
|
});
|
||||||
registerFileCommands({
|
registerFileCommands({
|
||||||
idPrefix: 'query',
|
idPrefix: 'query',
|
||||||
category: 'Query',
|
category: 'Query',
|
||||||
@@ -25,7 +33,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { get_current_component } from 'svelte/internal';
|
import { get_current_component, insert } from 'svelte/internal';
|
||||||
import { getContext } from 'svelte';
|
import { getContext } from 'svelte';
|
||||||
import sqlFormatter from 'sql-formatter';
|
import sqlFormatter from 'sql-formatter';
|
||||||
|
|
||||||
@@ -39,7 +47,7 @@
|
|||||||
import applySqlTemplate from '../utility/applySqlTemplate';
|
import applySqlTemplate from '../utility/applySqlTemplate';
|
||||||
import axiosInstance from '../utility/axiosInstance';
|
import axiosInstance from '../utility/axiosInstance';
|
||||||
import { changeTab } from '../utility/common';
|
import { changeTab } from '../utility/common';
|
||||||
import { useConnectionInfo } from '../utility/metadataLoaders';
|
import { getDatabaseInfo, useConnectionInfo } from '../utility/metadataLoaders';
|
||||||
import socket from '../utility/socket';
|
import socket from '../utility/socket';
|
||||||
import SocketMessageView from '../query/SocketMessageView.svelte';
|
import SocketMessageView from '../query/SocketMessageView.svelte';
|
||||||
import memberStore from '../utility/memberStore';
|
import memberStore from '../utility/memberStore';
|
||||||
@@ -47,6 +55,8 @@
|
|||||||
import ResultTabs from '../query/ResultTabs.svelte';
|
import ResultTabs from '../query/ResultTabs.svelte';
|
||||||
import { registerFileCommands } from '../commands/stdCommands';
|
import { registerFileCommands } from '../commands/stdCommands';
|
||||||
import invalidateCommands from '../commands/invalidateCommands';
|
import invalidateCommands from '../commands/invalidateCommands';
|
||||||
|
import { showModal } from '../modals/modalTools';
|
||||||
|
import InsertJoinModal from '../modals/InsertJoinModal.svelte';
|
||||||
|
|
||||||
export let tabid;
|
export let tabid;
|
||||||
export let conid;
|
export let conid;
|
||||||
@@ -163,6 +173,19 @@
|
|||||||
editor.clearSelection();
|
editor.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function insertSqlJoin() {
|
||||||
|
const dbinfo = await getDatabaseInfo({ conid, database });
|
||||||
|
showModal(InsertJoinModal, {
|
||||||
|
sql: getData(),
|
||||||
|
engine: $connection && $connection.engine,
|
||||||
|
dbinfo,
|
||||||
|
onInsert: text => {
|
||||||
|
const editor = domEditor.getEditor();
|
||||||
|
editor.session.insert(editor.getCursorPosition(), text);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const handleMesageClick = message => {
|
const handleMesageClick = message => {
|
||||||
// console.log('EDITOR', editorRef.current.editor);
|
// console.log('EDITOR', editorRef.current.editor);
|
||||||
if (domEditor.getEditor()) {
|
if (domEditor.getEditor()) {
|
||||||
@@ -190,6 +213,7 @@
|
|||||||
{ divider: true },
|
{ divider: true },
|
||||||
{ command: 'query.toggleComment' },
|
{ command: 'query.toggleComment' },
|
||||||
{ command: 'query.formatCode' },
|
{ command: 'query.formatCode' },
|
||||||
|
{ command: 'query.insertSqlJoin' },
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
{ command: 'query.save' },
|
{ command: 'query.save' },
|
||||||
{ command: 'query.saveAs' },
|
{ command: 'query.saveAs' },
|
||||||
|
|||||||
Reference in New Issue
Block a user