mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-28 19:56:00 +00:00
Merge branch 'master' into develop
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
# ChangeLog
|
# ChangeLog
|
||||||
|
|
||||||
|
### 4.3.2
|
||||||
|
- FIXED: Sorted database list in PostgreSQL (#178)
|
||||||
|
- FIXED: Loading stricture of PostgreSQL database, when it contains indexes on expressions (#175)
|
||||||
|
- ADDED: Hotkey Shift+Alt+F for formatting SQL code
|
||||||
|
|
||||||
### 4.3.1
|
### 4.3.1
|
||||||
- FIXED: #173 Using key phrase for SSH key file connection
|
- FIXED: #173 Using key phrase for SSH key file connection
|
||||||
- ADDED: #172 Abiloity to quick search within database names
|
- ADDED: #172 Abiloity to quick search within database names
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
[](https://snapcraft.io/dbgate)
|
[](https://snapcraft.io/dbgate)
|
||||||
[](https://github.com/prettier/prettier)
|
[](https://github.com/prettier/prettier)
|
||||||
|
|
||||||
# DbGate - database administration tool
|
# DbGate - database manager
|
||||||
|
|
||||||
DbGate modern, fast and easy to use database manager
|
DbGate is modern, fast and easy to use (no)SQL database client
|
||||||
|
|
||||||
* Try it online - [demo.dbgate.org](https://demo.dbgate.org) - online demo application
|
* Try it online - [demo.dbgate.org](https://demo.dbgate.org) - online demo application
|
||||||
* Download application for Windows, Linux or Mac from [dbgate.org](https://dbgate.org/download/)
|
* Download application for Windows, Linux or Mac from [dbgate.org](https://dbgate.org/download/)
|
||||||
@@ -26,6 +26,7 @@ Supported databases:
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
* Table data editing, with SQL change script preview
|
* Table data editing, with SQL change script preview
|
||||||
|
* Edit table schema, indexes, primary and foreign keys
|
||||||
* Light and dark theme
|
* Light and dark theme
|
||||||
* Master/detail views
|
* Master/detail views
|
||||||
* Query designer
|
* Query designer
|
||||||
|
|||||||
@@ -154,6 +154,9 @@ function createWindow() {
|
|||||||
enableRemoteModule: true,
|
enableRemoteModule: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
if (store.get('winIsMaximized')) {
|
||||||
|
mainWindow.maximize();
|
||||||
|
}
|
||||||
|
|
||||||
mainMenu = buildMenu();
|
mainMenu = buildMenu();
|
||||||
mainWindow.setMenu(mainMenu);
|
mainWindow.setMenu(mainMenu);
|
||||||
@@ -171,6 +174,7 @@ function createWindow() {
|
|||||||
});
|
});
|
||||||
mainWindow.on('close', () => {
|
mainWindow.on('close', () => {
|
||||||
store.set('winBounds', mainWindow.getBounds());
|
store.set('winBounds', mainWindow.getBounds());
|
||||||
|
store.set('winIsMaximized', mainWindow.isMaximized());
|
||||||
});
|
});
|
||||||
mainWindow.loadURL(startUrl);
|
mainWindow.loadURL(startUrl);
|
||||||
if (os.platform() == 'linux') {
|
if (os.platform() == 'linux') {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "4.3.2-beta.1",
|
"version": "4.3.3-beta.3",
|
||||||
"name": "dbgate-all",
|
"name": "dbgate-all",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
|
|||||||
@@ -115,6 +115,10 @@ export abstract class GridDisplay {
|
|||||||
return this.getColumns(null).filter(col => col.isChecked || col.uniquePath.length == 1);
|
return this.getColumns(null).filter(col => col.isChecked || col.uniquePath.length == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFkTarget(column: DisplayColumn): TableInfo {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
reload() {
|
reload() {
|
||||||
this.setCache(reloadDataCacheFunc);
|
this.setCache(reloadDataCacheFunc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { filterName } from 'dbgate-tools';
|
import { filterName } from 'dbgate-tools';
|
||||||
|
import _ from 'lodash';
|
||||||
import { useDatabaseList } from '../utility/metadataLoaders';
|
import { useDatabaseList } from '../utility/metadataLoaders';
|
||||||
import AppObjectList from './AppObjectList.svelte';
|
import AppObjectList from './AppObjectList.svelte';
|
||||||
import * as databaseAppObject from './DatabaseAppObject.svelte';
|
import * as databaseAppObject from './DatabaseAppObject.svelte';
|
||||||
@@ -11,6 +12,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AppObjectList
|
<AppObjectList
|
||||||
list={($databases || []).filter(x => filterName(filter, x.name)).map(db => ({ ...db, connection: data }))}
|
list={_.sortBy(
|
||||||
|
($databases || []).filter(x => filterName(filter, x.name)),
|
||||||
|
'name'
|
||||||
|
).map(db => ({ ...db, connection: data }))}
|
||||||
module={databaseAppObject}
|
module={databaseAppObject}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -111,11 +111,29 @@
|
|||||||
id: 'dataGrid.filterSelected',
|
id: 'dataGrid.filterSelected',
|
||||||
category: 'Data grid',
|
category: 'Data grid',
|
||||||
name: 'Filter selected value',
|
name: 'Filter selected value',
|
||||||
keyText: 'Ctrl+F',
|
keyText: 'Ctrl+Shift+F',
|
||||||
testEnabled: () => getCurrentDataGrid()?.getDisplay().filterable,
|
testEnabled: () => getCurrentDataGrid()?.getDisplay().filterable,
|
||||||
onClick: () => getCurrentDataGrid().filterSelectedValue(),
|
onClick: () => getCurrentDataGrid().filterSelectedValue(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerCommand({
|
||||||
|
id: 'dataGrid.findColumn',
|
||||||
|
category: 'Data grid',
|
||||||
|
name: 'Find colunn',
|
||||||
|
keyText: 'Ctrl+F',
|
||||||
|
testEnabled: () => getCurrentDataGrid() != null,
|
||||||
|
getSubCommands: () => getCurrentDataGrid().buildFindMenu(),
|
||||||
|
});
|
||||||
|
|
||||||
|
registerCommand({
|
||||||
|
id: 'dataGrid.hideColumn',
|
||||||
|
category: 'Data grid',
|
||||||
|
name: 'Hide colunn',
|
||||||
|
keyText: 'Ctrl+H',
|
||||||
|
testEnabled: () => getCurrentDataGrid() != null,
|
||||||
|
onClick: () => getCurrentDataGrid().hideColumn(),
|
||||||
|
});
|
||||||
|
|
||||||
registerCommand({
|
registerCommand({
|
||||||
id: 'dataGrid.clearFilter',
|
id: 'dataGrid.clearFilter',
|
||||||
category: 'Data grid',
|
category: 'Data grid',
|
||||||
@@ -125,6 +143,15 @@
|
|||||||
onClick: () => getCurrentDataGrid().clearFilter(),
|
onClick: () => getCurrentDataGrid().clearFilter(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerCommand({
|
||||||
|
id: 'dataGrid.generateSqlFromData',
|
||||||
|
category: 'Data grid',
|
||||||
|
name: 'Generate SQL',
|
||||||
|
keyText: 'Ctrl+G',
|
||||||
|
testEnabled: () => getCurrentDataGrid()?.generateSqlFromDataEnabled(),
|
||||||
|
onClick: () => getCurrentDataGrid().generateSqlFromData(),
|
||||||
|
});
|
||||||
|
|
||||||
registerCommand({
|
registerCommand({
|
||||||
id: 'dataGrid.openFreeTable',
|
id: 'dataGrid.openFreeTable',
|
||||||
category: 'Data grid',
|
category: 'Data grid',
|
||||||
@@ -215,6 +242,8 @@
|
|||||||
import { editJsonRowDocument } from '../jsonview/CollectionJsonRow.svelte';
|
import { editJsonRowDocument } from '../jsonview/CollectionJsonRow.svelte';
|
||||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||||
import CollapseButton from './CollapseButton.svelte';
|
import CollapseButton from './CollapseButton.svelte';
|
||||||
|
import GenerateSqlFromDataModal from '../modals/GenerateSqlFromDataModal.svelte';
|
||||||
|
import { showModal } from '../modals/modalTools';
|
||||||
|
|
||||||
export let onLoadNextData = undefined;
|
export let onLoadNextData = undefined;
|
||||||
export let grider = undefined;
|
export let grider = undefined;
|
||||||
@@ -347,6 +376,7 @@
|
|||||||
});
|
});
|
||||||
const text = lines.join('\r\n');
|
const text = lines.join('\r\n');
|
||||||
copyTextToClipboard(text);
|
copyTextToClipboard(text);
|
||||||
|
// if (domFocusField) domFocusField.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadNextDataIfNeeded() {
|
export function loadNextDataIfNeeded() {
|
||||||
@@ -416,6 +446,119 @@
|
|||||||
editJsonRowDocument(grider, rowIndex);
|
editJsonRowDocument(grider, rowIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildFindMenu() {
|
||||||
|
const res = [];
|
||||||
|
|
||||||
|
async function clickColumn(uniquePath) {
|
||||||
|
display.setColumnVisibility(uniquePath, true);
|
||||||
|
await tick();
|
||||||
|
const invMap = _.invert(realColumnUniqueNames);
|
||||||
|
const colIndex = invMap[uniquePath.join('.')];
|
||||||
|
scrollIntoView([null, colIndex]);
|
||||||
|
|
||||||
|
currentCell = [currentCell[0], parseInt(colIndex)];
|
||||||
|
selectedCells = [currentCell];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const column of display.columns) {
|
||||||
|
if (column.uniquePath.length > 1) continue;
|
||||||
|
|
||||||
|
res.push({
|
||||||
|
text: column.columnName,
|
||||||
|
icon: 'img column',
|
||||||
|
onClick: async () => {
|
||||||
|
clickColumn(column.uniquePath);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (const column of display.columns) {
|
||||||
|
if (column.uniquePath.length > 1) continue;
|
||||||
|
if (column.isExpandable) {
|
||||||
|
const table = display.getFkTarget(column);
|
||||||
|
if (!table) continue;
|
||||||
|
|
||||||
|
for (const childColumn of table.columns) {
|
||||||
|
res.push({
|
||||||
|
text: `${column.columnName}.${childColumn.columnName}`,
|
||||||
|
icon: 'img column',
|
||||||
|
onClick: async () => {
|
||||||
|
display.toggleExpandedColumn(column.uniqueName, true);
|
||||||
|
clickColumn([...column.uniquePath, childColumn.columnName]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const fk of display?.baseTable?.foreignKeys || []) {
|
||||||
|
res.push({
|
||||||
|
text: `${fk.refTableName} (${fk.columns.map(x => x.columnName).join(', ')})`,
|
||||||
|
icon: 'img link',
|
||||||
|
onClick: () => {
|
||||||
|
onReferenceClick({
|
||||||
|
schemaName: fk.refSchemaName,
|
||||||
|
pureName: fk.refTableName,
|
||||||
|
columns: fk.columns.map(col => ({
|
||||||
|
baseName: col.columnName,
|
||||||
|
refName: col.refColumnName,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const fk of display?.baseTable?.dependencies || []) {
|
||||||
|
res.push({
|
||||||
|
text: `${fk.pureName} (${fk.columns.map(x => x.columnName).join(', ')})`,
|
||||||
|
icon: 'img reference',
|
||||||
|
onClick: () => {
|
||||||
|
onReferenceClick({
|
||||||
|
schemaName: fk.schemaName,
|
||||||
|
pureName: fk.pureName,
|
||||||
|
columns: fk.columns.map(col => ({
|
||||||
|
baseName: col.refColumnName,
|
||||||
|
refName: col.columnName,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hideColumn() {
|
||||||
|
const columnIndexes = _.uniq(selectedCells.map(x => x[1]));
|
||||||
|
for (const index of columnIndexes) {
|
||||||
|
const name = realColumnUniqueNames[index];
|
||||||
|
const column = display.allColumns.find(x => x.uniqueName == name);
|
||||||
|
if (column) {
|
||||||
|
display.setColumnVisibility(column.uniquePath, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// selectedCells = [currentCell];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateSqlFromDataEnabled() {
|
||||||
|
return !!display?.baseTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateSqlFromData() {
|
||||||
|
const columnIndexes = _.uniq(selectedCells.map(x => x[1]));
|
||||||
|
columnIndexes.sort();
|
||||||
|
|
||||||
|
showModal(GenerateSqlFromDataModal, {
|
||||||
|
rows: getSelectedRowData(),
|
||||||
|
allColumns: display.baseTable.columns.map(x => x.columnName),
|
||||||
|
selectedColumns: columnIndexes.map(x => realColumnUniqueNames[x]),
|
||||||
|
keyColumns: display?.baseTable?.primaryKey?.columns?.map(x => x.columnName) || [
|
||||||
|
display.baseTable.columns[0].columnName,
|
||||||
|
],
|
||||||
|
engineDriver: display?.driver,
|
||||||
|
tableInfo: display.baseTable,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$: autofillMarkerCell =
|
$: autofillMarkerCell =
|
||||||
selectedCells && selectedCells.length > 0 && _.uniq(selectedCells.map(x => x[0])).length == 1
|
selectedCells && selectedCells.length > 0 && _.uniq(selectedCells.map(x => x[0])).length == 1
|
||||||
? [_.max(selectedCells.map(x => x[0])), _.max(selectedCells.map(x => x[1]))]
|
? [_.max(selectedCells.map(x => x[0])), _.max(selectedCells.map(x => x[1]))]
|
||||||
@@ -611,6 +754,8 @@
|
|||||||
selectedCells = [...selectedCells, cell];
|
selectedCells = [...selectedCells, cell];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (event.shiftKey) {
|
||||||
|
selectedCells = getCellRange(oldCurrentCell, cell);
|
||||||
} else {
|
} else {
|
||||||
selectedCells = getCellRange(cell, cell);
|
selectedCells = getCellRange(cell, cell);
|
||||||
dragStartCell = cell;
|
dragStartCell = cell;
|
||||||
@@ -927,7 +1072,7 @@
|
|||||||
currentCell = cell;
|
currentCell = cell;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
selectedCells = [cell];
|
selectedCells = [cell];
|
||||||
domFocusField.focus();
|
if (domFocusField) domFocusField.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
const [inplaceEditorState, dispatchInsplaceEditor] = createReducer((state, action) => {
|
const [inplaceEditorState, dispatchInsplaceEditor] = createReducer((state, action) => {
|
||||||
@@ -977,6 +1122,8 @@
|
|||||||
{ command: 'dataGrid.setNull' },
|
{ command: 'dataGrid.setNull' },
|
||||||
{ placeTag: 'edit' },
|
{ placeTag: 'edit' },
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
|
{ command: 'dataGrid.findColumn' },
|
||||||
|
{ command: 'dataGrid.hideColumn' },
|
||||||
{ command: 'dataGrid.filterSelected' },
|
{ command: 'dataGrid.filterSelected' },
|
||||||
{ command: 'dataGrid.clearFilter' },
|
{ command: 'dataGrid.clearFilter' },
|
||||||
{ command: 'dataGrid.undo' },
|
{ command: 'dataGrid.undo' },
|
||||||
@@ -984,6 +1131,7 @@
|
|||||||
{ command: 'dataGrid.editJsonDocument' },
|
{ command: 'dataGrid.editJsonDocument' },
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
{ placeTag: 'export' },
|
{ placeTag: 'export' },
|
||||||
|
{ command: 'dataGrid.generateSqlFromData' },
|
||||||
{ command: 'dataGrid.openFreeTable' },
|
{ command: 'dataGrid.openFreeTable' },
|
||||||
{ command: 'dataGrid.openChartFromSelection' },
|
{ command: 'dataGrid.openChartFromSelection' },
|
||||||
{ placeTag: 'chart' }
|
{ placeTag: 'chart' }
|
||||||
|
|||||||
44
packages/web/src/elements/CheckableColumnList.svelte
Normal file
44
packages/web/src/elements/CheckableColumnList.svelte
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FormStyledButton from './FormStyledButton.svelte';
|
||||||
|
|
||||||
|
export let selectedColumns;
|
||||||
|
export let allColumns;
|
||||||
|
export let disabled = false;
|
||||||
|
|
||||||
|
function toggleColumn(column) {
|
||||||
|
if (selectedColumns.includes(column)) selectedColumns = selectedColumns.filter(x => x != column);
|
||||||
|
else selectedColumns = [...selectedColumns, column];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<FormStyledButton value="All" on:click={() => (selectedColumns = allColumns)} {disabled} />
|
||||||
|
<FormStyledButton value="None" on:click={() => (selectedColumns = [])} {disabled} />
|
||||||
|
</div>
|
||||||
|
<div class="list">
|
||||||
|
{#each allColumns as column}
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
{disabled}
|
||||||
|
checked={selectedColumns.includes(column)}
|
||||||
|
on:change={() => toggleColumn(column)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span on:click={() => toggleColumn(column)} class="label">
|
||||||
|
{column}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.list {
|
||||||
|
max-height: 25vh;
|
||||||
|
overflow: scroll;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
import { getFormContext } from './FormProviderCore.svelte';
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
export let disabled;
|
export let disabled = false;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
id: 'dataForm.filterSelected',
|
id: 'dataForm.filterSelected',
|
||||||
category: 'Data form',
|
category: 'Data form',
|
||||||
name: 'Filter this value',
|
name: 'Filter this value',
|
||||||
keyText: 'Ctrl+F',
|
keyText: 'Ctrl+Shift+F',
|
||||||
testEnabled: () => getCurrentDataForm() != null,
|
testEnabled: () => getCurrentDataForm() != null,
|
||||||
onClick: () => getCurrentDataForm().filterSelectedValue(),
|
onClick: () => getCurrentDataForm().filterSelectedValue(),
|
||||||
});
|
});
|
||||||
|
|||||||
140
packages/web/src/modals/GenerateSqlFromDataModal.svelte
Normal file
140
packages/web/src/modals/GenerateSqlFromDataModal.svelte
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import CheckableColumnList from '../elements/CheckableColumnList.svelte';
|
||||||
|
import FormStyledButton from '../elements/FormStyledButton.svelte';
|
||||||
|
import TableControl from '../elements/TableControl.svelte';
|
||||||
|
import FormProvider from '../forms/FormProvider.svelte';
|
||||||
|
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||||
|
import TextField from '../forms/TextField.svelte';
|
||||||
|
import analyseQuerySources from '../query/analyseQuerySources';
|
||||||
|
import newQuery from '../query/newQuery';
|
||||||
|
import SqlEditor from '../query/SqlEditor.svelte';
|
||||||
|
import keycodes from '../utility/keycodes';
|
||||||
|
|
||||||
|
import ModalBase from './ModalBase.svelte';
|
||||||
|
import { closeCurrentModal } from './modalTools';
|
||||||
|
|
||||||
|
export let rows;
|
||||||
|
export let allColumns = [];
|
||||||
|
export let selectedColumns = [];
|
||||||
|
export let keyColumns = [];
|
||||||
|
export let tableInfo;
|
||||||
|
export let engineDriver;
|
||||||
|
|
||||||
|
let queryTypeIndex = 0;
|
||||||
|
let domQueryType = null;
|
||||||
|
|
||||||
|
let valueColumns = selectedColumns.filter(x => allColumns.includes(x));
|
||||||
|
let whereColumns = keyColumns.filter(x => allColumns.includes(x));
|
||||||
|
|
||||||
|
const QUERY_TYPES = ['INSERT', 'UPDATE', 'DELETE'];
|
||||||
|
const VALUE_QUERIES = ['INSERT', 'UPDATE'];
|
||||||
|
const WHERE_QUERIES = ['UPDATE', 'DELETE'];
|
||||||
|
|
||||||
|
$: sqlPreview = computePreview(rows, valueColumns, whereColumns, queryTypeIndex);
|
||||||
|
|
||||||
|
function computePreview(rows, valueColumns, whereColumns, queryTypeIndex) {
|
||||||
|
const queryType = QUERY_TYPES[queryTypeIndex];
|
||||||
|
|
||||||
|
const dmp = engineDriver.createDumper();
|
||||||
|
|
||||||
|
function putCondition(row) {
|
||||||
|
dmp.putCollection(' ^and ', whereColumns, col =>
|
||||||
|
row[col] == null ? dmp.put('%i ^is ^null', col) : dmp.put('%i=%v', col, row[col])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (queryType) {
|
||||||
|
case 'INSERT':
|
||||||
|
for (const row of rows) {
|
||||||
|
dmp.putCmd(
|
||||||
|
'^insert ^into %f (%,i) ^values (%,v)',
|
||||||
|
tableInfo,
|
||||||
|
valueColumns,
|
||||||
|
valueColumns.map(col => row[col])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'UPDATE':
|
||||||
|
for (const row of rows) {
|
||||||
|
dmp.put('^update %f ^set', tableInfo);
|
||||||
|
dmp.putCollection(', ', valueColumns, col => dmp.put('%i=%v', col, row[col]));
|
||||||
|
dmp.put(' ^where ');
|
||||||
|
putCondition(row);
|
||||||
|
dmp.endCommand();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'DELETE':
|
||||||
|
for (const row of rows) {
|
||||||
|
dmp.put('^delete ^from %f ^where ', tableInfo);
|
||||||
|
putCondition(row);
|
||||||
|
dmp.endCommand();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return dmp.s;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormProvider>
|
||||||
|
<ModalBase {...$$restProps}>
|
||||||
|
<svelte:fragment slot="header">Generate SQL from data</svelte:fragment>
|
||||||
|
|
||||||
|
<div class="flex mb-3">
|
||||||
|
<div class="m-1 col-4">
|
||||||
|
<div class="m-1">Choose query type</div>
|
||||||
|
|
||||||
|
<TableControl
|
||||||
|
rows={QUERY_TYPES.map(name => ({ name }))}
|
||||||
|
bind:selectedIndex={queryTypeIndex}
|
||||||
|
bind:domTable={domQueryType}
|
||||||
|
focusOnCreate
|
||||||
|
selectable
|
||||||
|
columns={[{ fieldName: 'name', header: 'Query type' }]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-1 col-4">
|
||||||
|
<div class="m-1">Value columns</div>
|
||||||
|
|
||||||
|
<CheckableColumnList
|
||||||
|
{allColumns}
|
||||||
|
bind:selectedColumns={valueColumns}
|
||||||
|
disabled={!VALUE_QUERIES.includes(QUERY_TYPES[queryTypeIndex])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-1 col-4">
|
||||||
|
<div class="m-1">WHERE columns</div>
|
||||||
|
|
||||||
|
<CheckableColumnList
|
||||||
|
{allColumns}
|
||||||
|
bind:selectedColumns={whereColumns}
|
||||||
|
disabled={!WHERE_QUERIES.includes(QUERY_TYPES[queryTypeIndex])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sql">
|
||||||
|
<SqlEditor readOnly value={sqlPreview} engine={engineDriver?.engine} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<svelte:fragment slot="footer">
|
||||||
|
<FormSubmit
|
||||||
|
value="OK"
|
||||||
|
on:click={() => {
|
||||||
|
newQuery({ initialData: sqlPreview });
|
||||||
|
closeCurrentModal();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||||
|
</svelte:fragment>
|
||||||
|
</ModalBase>
|
||||||
|
</FormProvider>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.sql {
|
||||||
|
position: relative;
|
||||||
|
height: 25vh;
|
||||||
|
width: 40vw;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
<svelte:fragment slot="header">Insert join</svelte:fragment>
|
<svelte:fragment slot="header">Insert join</svelte:fragment>
|
||||||
|
|
||||||
<div class="flex mb-3">
|
<div class="flex mb-3">
|
||||||
<div class="m-1">
|
<div class="m-1 col-3">
|
||||||
<div class="m-1">Existing table</div>
|
<div class="m-1">Existing table</div>
|
||||||
|
|
||||||
<TableControl
|
<TableControl
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="m-1">
|
<div class="m-1 col-6">
|
||||||
<div class="m-1">New table</div>
|
<div class="m-1">New table</div>
|
||||||
|
|
||||||
<TableControl
|
<TableControl
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="m-1">
|
<div class="m-1 col-3">
|
||||||
<div class="m-1">Join</div>
|
<div class="m-1">Join</div>
|
||||||
|
|
||||||
<TableControl
|
<TableControl
|
||||||
|
|||||||
@@ -12,10 +12,10 @@
|
|||||||
import useEffect from '../utility/useEffect';
|
import useEffect from '../utility/useEffect';
|
||||||
import { getContext } from 'svelte';
|
import { getContext } from 'svelte';
|
||||||
import { mountCodeCompletion } from './codeCompletion';
|
import { mountCodeCompletion } from './codeCompletion';
|
||||||
export let engine;
|
export let engine = null;
|
||||||
export let conid;
|
export let conid = null;
|
||||||
export let database;
|
export let database = null;
|
||||||
export let readOnly;
|
export let readOnly = false;
|
||||||
|
|
||||||
let domEditor;
|
let domEditor;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
id: 'query.formatCode',
|
id: 'query.formatCode',
|
||||||
category: 'Query',
|
category: 'Query',
|
||||||
name: 'Format code',
|
name: 'Format code',
|
||||||
|
keyText: 'Shift+Alt+F',
|
||||||
testEnabled: () => getCurrentEditor()?.isSqlEditor(),
|
testEnabled: () => getCurrentEditor()?.isSqlEditor(),
|
||||||
onClick: () => getCurrentEditor().formatCode(),
|
onClick: () => getCurrentEditor().formatCode(),
|
||||||
});
|
});
|
||||||
@@ -203,6 +204,7 @@
|
|||||||
onInsert: text => {
|
onInsert: text => {
|
||||||
const editor = domEditor.getEditor();
|
const editor = domEditor.getEditor();
|
||||||
editor.session.insert(editor.getCursorPosition(), text);
|
editor.session.insert(editor.getCursorPosition(), text);
|
||||||
|
domEditor?.getEditor()?.focus();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
export function copyTextToClipboard(text) {
|
export function copyTextToClipboard(text) {
|
||||||
|
const oldFocus = document.activeElement;
|
||||||
|
|
||||||
const textArea = document.createElement('textarea');
|
const textArea = document.createElement('textarea');
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -53,4 +55,6 @@ export function copyTextToClipboard(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.body.removeChild(textArea);
|
document.body.removeChild(textArea);
|
||||||
|
|
||||||
|
if (oldFocus) oldFocus.focus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SearchBoxWrapper>
|
<SearchBoxWrapper>
|
||||||
<SearchInput placeholder="Search connection" bind:value={filter} />
|
<SearchInput placeholder="Search connection or database" bind:value={filter} />
|
||||||
<InlineButton on:click={handleRefreshConnections}>Refresh</InlineButton>
|
<InlineButton on:click={handleRefreshConnections}>Refresh</InlineButton>
|
||||||
</SearchBoxWrapper>
|
</SearchBoxWrapper>
|
||||||
<WidgetsInnerContainer>
|
<WidgetsInnerContainer>
|
||||||
|
|||||||
Reference in New Issue
Block a user