sql generator

This commit is contained in:
Jan Prochazka
2021-04-01 08:09:22 +02:00
parent a1993214e2
commit d62177d996
10 changed files with 175 additions and 44 deletions

View File

@@ -15,13 +15,62 @@ export function addTableDependencies(db: DatabaseInfo): DatabaseInfo {
function fillTableExtendedInfo(db: DatabaseInfo) { function fillTableExtendedInfo(db: DatabaseInfo) {
return { return {
...db, ...db,
tables: db.tables.map(table => ({ tables: (db.tables || []).map(obj => ({
...table, ...obj,
columns: (table.columns || []).map(column => ({ objectTypeField: 'tables',
pureName: table.pureName, columns: (obj.columns || []).map(column => ({
schemaName: table.schemaName, pureName: obj.pureName,
schemaName: obj.schemaName,
...column, ...column,
})), })),
primaryKey: obj.primaryKey
? {
...obj.primaryKey,
pureName: obj.pureName,
schemaName: obj.schemaName,
constraintType: 'primaryKey',
}
: undefined,
foreignKeys: (obj.foreignKeys || []).map(cnt => ({
...cnt,
pureName: obj.pureName,
schemaName: obj.schemaName,
constraintType: 'foreignKey',
})),
indexes: (obj.indexes || []).map(cnt => ({
...cnt,
pureName: obj.pureName,
schemaName: obj.schemaName,
constraintType: 'index',
})),
checks: (obj.checks || []).map(cnt => ({
...cnt,
pureName: obj.pureName,
schemaName: obj.schemaName,
constraintType: 'check',
})),
uniques: (obj.uniques || []).map(cnt => ({
...cnt,
pureName: obj.pureName,
schemaName: obj.schemaName,
constraintType: 'unique',
})),
})),
views: (db.views || []).map(obj => ({
...obj,
objectTypeField: 'views',
})),
procedures: (db.procedures || []).map(obj => ({
...obj,
objectTypeField: 'procedures',
})),
functions: (db.functions || []).map(obj => ({
...obj,
objectTypeField: 'functions',
})),
triggers: (db.triggers || []).map(obj => ({
...obj,
objectTypeField: 'triggers',
})), })),
}; };
} }

View File

@@ -70,6 +70,7 @@ export interface TableInfo extends DatabaseObjectInfo {
foreignKeys: ForeignKeyInfo[]; foreignKeys: ForeignKeyInfo[];
dependencies?: ForeignKeyInfo[]; dependencies?: ForeignKeyInfo[];
indexes?: IndexInfo[]; indexes?: IndexInfo[];
uniques?: UniqueInfo[];
checks?: CheckInfo[]; checks?: CheckInfo[];
} }

View File

@@ -38,6 +38,10 @@ body {
.flex { .flex {
display: flex; display: flex;
} }
.flexcol {
display: flex;
flex-direction: column;
}
.nowrap { .nowrap {
white-space: nowrap; white-space: nowrap;
} }

View File

@@ -1,4 +1,6 @@
<script> <script>
import Link from '../elements/Link.svelte';
import { plusExpandIcon } from '../icons/expandIcons'; import { plusExpandIcon } from '../icons/expandIcons';
import FontIcon from '../icons/FontIcon.svelte'; import FontIcon from '../icons/FontIcon.svelte';
@@ -6,6 +8,7 @@
import AppObjectListItem from './AppObjectListItem.svelte'; import AppObjectListItem from './AppObjectListItem.svelte';
export let group; export let group;
export let groupFunc;
export let items; export let items;
export let module; export let module;
export let checkedObjectsStore = null; export let checkedObjectsStore = null;
@@ -14,6 +17,20 @@
$: filtered = items.filter(x => x.isMatched); $: filtered = items.filter(x => x.isMatched);
$: countText = filtered.length < items.length ? `${filtered.length}/${items.length}` : `${items.length}`; $: countText = filtered.length < items.length ? `${filtered.length}/${items.length}` : `${items.length}`;
function handleCheckAll(isChecked) {
checkedObjectsStore.update(checkList => {
const res = isChecked
? [
...checkList,
...filtered
.filter(x => !checkList.find(y => module.extractKey(x.data) == module.extractKey(y)))
.map(x => x.data),
]
: checkList.filter(x => groupFunc(x) != group);
return res;
});
}
</script> </script>
<div class="group" on:click={() => (isExpanded = !isExpanded)}> <div class="group" on:click={() => (isExpanded = !isExpanded)}>
@@ -26,6 +43,14 @@
</div> </div>
{#if isExpanded} {#if isExpanded}
{#if checkedObjectsStore}
<div class="ml-2">
<Link onClick={() => handleCheckAll(true)}>Check all</Link>
|
<Link onClick={() => handleCheckAll(false)}>Uncheck all</Link>
</div>
{/if}
{#each filtered as item (module.extractKey(item.data))} {#each filtered as item (module.extractKey(item.data))}
<AppObjectListItem {...$$restProps} {module} data={item.data} {checkedObjectsStore} on:objectClick /> <AppObjectListItem {...$$restProps} {module} data={item.data} {checkedObjectsStore} on:objectClick />
{/each} {/each}

View File

@@ -45,6 +45,7 @@
{isExpandable} {isExpandable}
{subItemsComponent} {subItemsComponent}
{checkedObjectsStore} {checkedObjectsStore}
{groupFunc}
/> />
{/each} {/each}
{:else} {:else}

View File

@@ -1,12 +1,17 @@
<script lang="ts"> <script lang="ts">
import getElectron from '../utility/getElectron'; import getElectron from '../utility/getElectron';
export let href; export let href = undefined;
export let onClick = undefined;
const electron = getElectron(); const electron = getElectron();
</script> </script>
{#if electron} {#if onClick}
<a on:click={onClick}>
<slot />
</a>
{:else if electron}
<a on:click={() => electron.shell.openExternal(href)}> <a on:click={() => electron.shell.openExternal(href)}>
<slot /> <slot />
</a> </a>

View File

@@ -9,7 +9,7 @@
<div class="largeFormMarker" class:noMargin> <div class="largeFormMarker" class:noMargin>
{#if type == 'checkbox'} {#if type == 'checkbox'}
<slot /> <slot />
<span {...labelProps} on:click={labelProps.onClick} class:disabled>{label}</span> <span {...labelProps} on:click={labelProps.onClick} class:disabled class='checkLabel'>{label}</span>
{:else} {:else}
<div class="label" {...labelProps} on:click={labelProps.onClick}> <div class="label" {...labelProps} on:click={labelProps.onClick}>
<span {...labelProps} on:click={labelProps.onClick} class:disabled>{label}</span> <span {...labelProps} on:click={labelProps.onClick} class:disabled>{label}</span>
@@ -23,6 +23,10 @@
margin-bottom: 3px; margin-bottom: 3px;
color: var(--theme-font-3); color: var(--theme-font-3);
} }
.checkLabel {
cursor: default;
user-select: none;
}
.largeFormMarker:not(.noMargin) { .largeFormMarker:not(.noMargin) {
margin: var(--dim-large-form-margin); margin: var(--dim-large-form-margin);
} }

View File

@@ -9,7 +9,7 @@
<div class="row"> <div class="row">
{#if type == 'checkbox'} {#if type == 'checkbox'}
<slot /> <slot />
<span {...labelProps} on:click={labelProps.onClick} class:disabled>{label}</span> <span {...labelProps} on:click={labelProps.onClick} class:disabled class="checkLabel">{label}</span>
{:else} {:else}
<div class="label" {...labelProps} on:click={labelProps.onClick}> <div class="label" {...labelProps} on:click={labelProps.onClick}>
<div class="label"> <div class="label">
@@ -30,6 +30,10 @@
margin-bottom: 3px; margin-bottom: 3px;
color: var(--theme-font-3); color: var(--theme-font-3);
} }
.checkLabel {
cursor: default;
user-select: none;
}
.disabled { .disabled {
color: var(--theme-font-3); color: var(--theme-font-3);
} }

View File

@@ -15,7 +15,7 @@
<ModalBase {...$$restProps}> <ModalBase {...$$restProps}>
<svelte:fragment slot="header">About DbGate</svelte:fragment> <svelte:fragment slot="header">About DbGate</svelte:fragment>
<div class="flex"> <div class="flex">
<img src="/logo192.png" /> <img src="logo192.png" />
<div> <div>
<div class="m-1"> <div class="m-1">
Version: <span>{version}</span> Version: <span>{version}</span>

View File

@@ -32,28 +32,44 @@
export let conid; export let conid;
export let database; export let database;
export let initialConfig = {
checkIfTableExists: true,
disableConstraints: true,
createTables: true,
createForeignKeys: true,
createViews: true,
createProcedures: true,
createFunctions: true,
createTriggers: true,
};
export let initialObjects = null;
let busy = false; let busy = false;
let managerSize; let managerSize;
let objectsFilter = ''; let objectsFilter = '';
let sqlPreview = ''; let sqlPreview = '';
let initialized = false;
const checkedObjectsStore = writable([]); $: dbinfo = useDatabaseInfo({ conid, database });
const valuesStore = writable({
checkIfTableExists: true, const checkedObjectsStore = writable(initialObjects || ($dbinfo && $dbinfo.tables) || []);
disableConstraints: true, const valuesStore = writable(initialConfig);
});
const loadRef = createRef(null); const loadRef = createRef(null);
$: console.log('checkedObjectsStore', $checkedObjectsStore); // $: console.log('checkedObjectsStore', $checkedObjectsStore);
$: objects = useDatabaseInfo({ conid, database }); $: if ($dbinfo && !initialized && !initialObjects) {
initialized = true;
$checkedObjectsStore = $dbinfo.tables;
}
$: generatePreview($valuesStore, $checkedObjectsStore); $: generatePreview($valuesStore, $checkedObjectsStore);
$: objectList = _.flatten( $: objectList = _.flatten(
['tables', 'views', 'procedures', 'functions'].map(objectTypeField => ['tables', 'views', 'procedures', 'functions'].map(objectTypeField =>
_.sortBy( _.sortBy(
(($objects || {})[objectTypeField] || []).map(obj => ({ ...obj, objectTypeField })), (($dbinfo || {})[objectTypeField] || []).map(obj => ({ ...obj, objectTypeField })),
['schemaName', 'pureName'] ['schemaName', 'pureName']
) )
) )
@@ -91,7 +107,7 @@
<HorizontalSplitter initialValue="300px" bind:size={managerSize}> <HorizontalSplitter initialValue="300px" bind:size={managerSize}>
<svelte:fragment slot="1"> <svelte:fragment slot="1">
<div> <div class="flexcol flex1">
<WidgetTitle>Choose objects</WidgetTitle> <WidgetTitle>Choose objects</WidgetTitle>
<SearchBoxWrapper> <SearchBoxWrapper>
<SearchInput placeholder="Search tables or objects" bind:value={objectsFilter} /> <SearchInput placeholder="Search tables or objects" bind:value={objectsFilter} />
@@ -116,40 +132,54 @@
<SqlEditor readOnly value={sqlPreview} /> <SqlEditor readOnly value={sqlPreview} />
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="2"> <svelte:fragment slot="2">
<WidgetsInnerContainer> <div class="flexcol flex1">
<FormValues let:values> <WidgetTitle>Generator settings</WidgetTitle>
<FormCheckboxField label="Drop tables" name="dropTables" /> <WidgetsInnerContainer>
{#if values.dropTables} <FormValues let:values>
<div class="ml-2"> <div class="obj-heading">Tables</div>
<FormCheckboxField label="Test if exists" name="checkIfTableExists" /> <FormCheckboxField label="Drop tables" name="dropTables" />
</div> {#if values.dropTables}
{/if} <div class="ml-2">
<FormCheckboxField label="Drop references" name="dropReferences" /> <FormCheckboxField label="Test if exists" name="checkIfTableExists" />
</div>
{/if}
<FormCheckboxField label="Drop references" name="dropReferences" />
<FormCheckboxField label="Create tables" name="createTables" /> <FormCheckboxField label="Create tables" name="createTables" />
<FormCheckboxField label="Create references" name="createReferences" /> <FormCheckboxField label="Create references" name="createReferences" />
<FormCheckboxField label="Create foreign keys" name="createForeignKeys" /> <FormCheckboxField label="Create foreign keys" name="createForeignKeys" />
<FormCheckboxField label="Create indexes" name="createIndexes" /> <FormCheckboxField label="Create indexes" name="createIndexes" />
<FormCheckboxField label="Insert" name="insert" /> <FormCheckboxField label="Insert" name="insert" />
{#if values.insert} {#if values.insert}
<div class="ml-2"> <div class="ml-2">
<FormCheckboxField label="Skip autoincrement column" name="skipAutoincrementColumn" /> <FormCheckboxField label="Skip autoincrement column" name="skipAutoincrementColumn" />
<FormCheckboxField label="Disable constraints" name="disableConstraints" /> <FormCheckboxField label="Disable constraints" name="disableConstraints" />
<FormCheckboxField label="Omit NULL values" name="omitNulls" /> <FormCheckboxField label="Omit NULL values" name="omitNulls" />
</div> </div>
{/if} {/if}
<FormCheckboxField label="Truncate tables (delete all rows)" name="truncate" /> <FormCheckboxField label="Truncate tables (delete all rows)" name="truncate" />
<!-- <HashCheckBox label='Drop' hashName={`gensql.drop${objTypePascal}`} onChange={onChange} /> {#each ['View', 'Procedure', 'Function', 'Trigger'] as objtype}
<div class="obj-heading">{objtype}s</div>
<FormCheckboxField label="Create" name={`create${objtype}s`} />
<FormCheckboxField label="Drop" name={`drop${objtype}s`} />
{#if values[`drop${objtype}s`]}
<div class="ml-2">
<FormCheckboxField label="Check if exists" name={`checkIf${objtype}Exists`} />
</div>
{/if}
{/each}
<!-- <HashCheckBox label='Drop' hashName={`gensql.drop${objTypePascal}`} onChange={onChange} />
{ {
getHashValue(`gensql.drop${objTypePascal}`) == '1' && getHashValue(`gensql.drop${objTypePascal}`) == '1' &&
<HashCheckBox label='Test if exists' hashName={`gensql.checkIf${objTypePascal}Exists`} indent={1} onChange={onChange} defaultChecked /> <HashCheckBox label='Test if exists' hashName={`gensql.checkIf${objTypePascal}Exists`} indent={1} onChange={onChange} defaultChecked />
} }
<HashCheckBox label='Create' hashName={`gensql.create${objTypePascal}`} onChange={onChange} /> --> <HashCheckBox label='Create' hashName={`gensql.create${objTypePascal}`} onChange={onChange} /> -->
</FormValues> </FormValues>
</WidgetsInnerContainer> </WidgetsInnerContainer>
</div>
</svelte:fragment> </svelte:fragment>
</HorizontalSplitter> </HorizontalSplitter>
</svelte:fragment> </svelte:fragment>
@@ -162,3 +192,11 @@
</svelte:fragment> </svelte:fragment>
</ModalBase> </ModalBase>
</FormProviderCore> </FormProviderCore>
<style>
.obj-heading {
font-size: 20px;
margin: 5px;
margin-top: 20px;
}
</style>