browse view data

This commit is contained in:
Jan Prochazka
2020-04-12 18:44:59 +02:00
parent ea6c31187b
commit 4bc252fdd2
20 changed files with 497 additions and 322 deletions

View File

@@ -1,15 +1,9 @@
const _ = require('lodash');
const fp = require('lodash/fp');
const uuidv1 = require('uuid/v1');
const connections = require('./connections');
const socket = require('../utility/socket');
const { fork } = require('child_process');
const DatabaseAnalyser = require('@dbgate/engines/default/DatabaseAnalyser');
function pickObjectNames(array) {
return _.sortBy(array, (x) => `${x.schemaName}.${x.pureName}`).map(fp.pick(['pureName', 'schemaName']));
}
module.exports = {
/** @type {import('@dbgate/types').OpenedDatabaseConnection[]} */
opened: [],
@@ -62,19 +56,6 @@ module.exports = {
return promise;
},
listObjects_meta: 'get',
async listObjects({ conid, database }) {
const opened = await this.ensureOpened(conid, database);
const types = ['tables', 'views', 'procedures', 'functions', 'triggers'];
return types.reduce(
(res, type) => ({
...res,
[type]: pickObjectNames(opened.structure[type]),
}),
{}
);
},
queryData_meta: 'post',
async queryData({ conid, database, sql }) {
console.log(`Processing query, conid=${conid}, database=${database}, sql=${sql}`);

View File

@@ -0,0 +1,47 @@
const _ = require('lodash');
const fp = require('lodash/fp');
const databaseConnections = require('./databaseConnections');
function pickObjectNames(array) {
return _.sortBy(array, (x) => `${x.schemaName}.${x.pureName}`).map(fp.pick(['pureName', 'schemaName']));
}
module.exports = {
// tableData_meta: 'get',
// async tableData({ conid, database, schemaName, pureName }) {
// const opened = await databaseConnections.ensureOpened(conid, database);
// const res = await databaseConnections.sendRequest(opened, { msgtype: 'tableData', schemaName, pureName });
// return res;
// },
listObjects_meta: 'get',
async listObjects({ conid, database }) {
const opened = await databaseConnections.ensureOpened(conid, database);
const types = ['tables', 'views', 'procedures', 'functions', 'triggers'];
return types.reduce(
(res, type) => ({
...res,
[type]: pickObjectNames(opened.structure[type]),
}),
{}
);
},
tableInfo_meta: 'get',
async tableInfo({ conid, database, schemaName, pureName }) {
const opened = await databaseConnections.ensureOpened(conid, database);
const table = opened.structure.tables.find((x) => x.pureName == pureName && x.schemaName == schemaName);
const allForeignKeys = _.flatten(opened.structure.tables.map((x) => x.foreignKeys));
return {
...table,
dependencies: allForeignKeys.filter((x) => x.refSchemaName == schemaName && x.refTableName == pureName),
};
},
viewInfo_meta: 'get',
async viewInfo({ conid, database, schemaName, pureName }) {
const opened = await databaseConnections.ensureOpened(conid, database);
const view = opened.structure.views.find((x) => x.pureName == pureName && x.schemaName == schemaName);
return view;
},
};

View File

@@ -1,22 +0,0 @@
const _ = require('lodash');
const databaseConnections = require('./databaseConnections');
module.exports = {
// tableData_meta: 'get',
// async tableData({ conid, database, schemaName, pureName }) {
// const opened = await databaseConnections.ensureOpened(conid, database);
// const res = await databaseConnections.sendRequest(opened, { msgtype: 'tableData', schemaName, pureName });
// return res;
// },
tableInfo_meta: 'get',
async tableInfo({ conid, database, schemaName, pureName }) {
const opened = await databaseConnections.ensureOpened(conid, database);
const table = opened.structure.tables.find(x => x.pureName == pureName && x.schemaName == schemaName);
const allForeignKeys = _.flatten(opened.structure.tables.map(x => x.foreignKeys));
return {
...table,
dependencies: allForeignKeys.filter(x => x.refSchemaName == schemaName && x.refTableName == pureName),
};
},
};

View File

@@ -12,7 +12,7 @@ const socket = require('./utility/socket');
const connections = require('./controllers/connections');
const serverConnections = require('./controllers/serverConnections');
const databaseConnections = require('./controllers/databaseConnections');
const tables = require('./controllers/tables');
const metadata = require('./controllers/metadata');
const sessions = require('./controllers/sessions');
const jsldata = require('./controllers/jsldata');
@@ -30,7 +30,7 @@ function start(argument = null) {
useController(app, '/connections', connections);
useController(app, '/server-connections', serverConnections);
useController(app, '/database-connections', databaseConnections);
useController(app, '/tables', tables);
useController(app, '/metadata', metadata);
useController(app, '/sessions', sessions);
useController(app, '/jsldata', jsldata);

View File

@@ -40,7 +40,7 @@ export function findExistingChangeSetItem(
changeSet: ChangeSet,
definition: ChangeSetRowDefinition
): [keyof ChangeSet, ChangeSetItem] {
if (!changeSet) return ['updates', null];
if (!changeSet || !definition) return ['updates', null];
if (definition.insertedRowIndex != null) {
return [
'inserts',

View File

@@ -1,10 +1,10 @@
import _ from 'lodash';
import { GridConfig, GridCache, GridConfigColumns } from './GridConfig';
import { ForeignKeyInfo, TableInfo, ColumnInfo, DbType, EngineDriver } from '@dbgate/types';
import { ForeignKeyInfo, TableInfo, ColumnInfo, DbType, EngineDriver, NamedObjectInfo } from '@dbgate/types';
import { parseFilter, getFilterType } from '@dbgate/filterparser';
import { filterName } from './filterName';
import { Select, Expression } from '@dbgate/sqltree';
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
import { Expression, Select, treeToSql, dumpSqlSelect } from '@dbgate/sqltree';
export interface DisplayColumn {
schemaName: string;
@@ -46,12 +46,8 @@ export abstract class GridDisplay {
protected setConfig: (config: GridConfig) => void,
public cache: GridCache,
protected setCache: ChangeCacheFunc,
protected getTableInfo: ({ schemaName, pureName }) => Promise<TableInfo>,
public driver?: EngineDriver
) {}
getPageQuery(offset: number, count: number): string {
return null;
}
columns: DisplayColumn[];
baseTable?: TableInfo;
changeSetKeyFields: string[] = null;
@@ -113,39 +109,6 @@ export abstract class GridDisplay {
return (this.config.hiddenColumns || []).map((x) => _.findIndex(this.columns, (y) => y.uniqueName == x));
}
enrichExpandedColumns(list: DisplayColumn[]): DisplayColumn[] {
const res = [];
for (const item of list) {
res.push(item);
if (this.isExpandedColumn(item.uniqueName)) res.push(...this.getExpandedColumns(item));
}
return res;
}
getExpandedColumns(column: DisplayColumn) {
const table = this.cache.tables[column.uniqueName];
if (table) {
return this.enrichExpandedColumns(this.getDisplayColumns(table, column.uniquePath));
} else {
// load expanded columns
this.requireFkTarget(column);
}
return [];
}
requireFkTarget(column: DisplayColumn) {
const { uniqueName, foreignKey } = column;
this.getTableInfo({ schemaName: foreignKey.refSchemaName, pureName: foreignKey.refTableName }).then((table) => {
this.setCache((cache) => ({
...cache,
tables: {
...cache.tables,
[uniqueName]: table,
},
}));
});
}
isColumnChecked(column: DisplayColumn) {
// console.log('isColumnChecked', column, this.config.hiddenColumns);
return column.uniquePath.length == 1
@@ -153,142 +116,6 @@ export abstract class GridDisplay {
: this.config.addedColumns.includes(column.uniqueName);
}
getDisplayColumn(table: TableInfo, col: ColumnInfo, parentPath: string[]) {
const uniquePath = [...parentPath, col.columnName];
const uniqueName = uniquePath.join('.');
// console.log('this.config.addedColumns', this.config.addedColumns, uniquePath);
return {
...col,
pureName: table.pureName,
schemaName: table.schemaName,
headerText: uniquePath.length == 1 ? col.columnName : `${table.pureName}.${col.columnName}`,
uniqueName,
uniquePath,
isPrimaryKey: table.primaryKey && !!table.primaryKey.columns.find((x) => x.columnName == col.columnName),
foreignKey:
table.foreignKeys &&
table.foreignKeys.find((fk) => fk.columns.length == 1 && fk.columns[0].columnName == col.columnName),
};
}
addAddedColumnsToSelect(
select: Select,
columns: DisplayColumn[],
parentAlias: string,
displayedColumnInfo: DisplayedColumnInfo
): ReferenceActionResult {
let res: ReferenceActionResult = 'noAction';
for (const column of columns) {
if (this.config.addedColumns.includes(column.uniqueName)) {
select.columns.push({
exprType: 'column',
columnName: column.columnName,
alias: column.uniqueName,
source: { name: column, alias: parentAlias },
});
displayedColumnInfo[column.uniqueName] = {
...column,
sourceAlias: parentAlias,
};
res = 'refAdded';
}
}
return res;
}
addJoinsFromExpandedColumns(
select: Select,
columns: DisplayColumn[],
parentAlias: string,
columnSources
): ReferenceActionResult {
let res: ReferenceActionResult = 'noAction';
for (const column of columns) {
if (this.isExpandedColumn(column.uniqueName)) {
const table = this.cache.tables[column.uniqueName];
if (table) {
const childAlias = `${column.uniqueName}_ref`;
const subcolumns = this.getDisplayColumns(table, column.uniquePath);
const tableAction = combineReferenceActions(
this.addJoinsFromExpandedColumns(select, subcolumns, childAlias, columnSources),
this.addAddedColumnsToSelect(select, subcolumns, childAlias, columnSources)
);
if (tableAction == 'refAdded') {
this.addReferenceToSelect(select, parentAlias, column);
res = 'refAdded';
}
if (tableAction == 'loadRequired') {
return 'loadRequired';
}
} else {
this.requireFkTarget(column);
res = 'loadRequired';
}
}
}
return res;
// const addedColumns = this.getGridColumns().filter(x=>x.)
}
addReferenceToSelect(select: Select, parentAlias: string, column: DisplayColumn) {
const childAlias = `${column.uniqueName}_ref`;
if ((select.from.relations || []).find((x) => x.alias == childAlias)) return;
const table = this.cache.tables[column.uniqueName];
select.from.relations = [
...(select.from.relations || []),
{
joinType: 'LEFT JOIN',
name: table,
alias: childAlias,
conditions: [
{
conditionType: 'binary',
operator: '=',
left: {
exprType: 'column',
columnName: column.columnName,
source: { name: column, alias: parentAlias },
},
right: {
exprType: 'column',
columnName: table.primaryKey.columns[0].columnName,
source: { name: table, alias: childAlias },
},
},
],
},
];
}
addHintsToSelect(select: Select): ReferenceActionResult {
let res: ReferenceActionResult = 'noAction';
for (const column of this.getGridColumns()) {
if (column.foreignKey) {
const table = this.cache.tables[column.uniqueName];
if (table) {
const hintColumn = table.columns.find((x) => x?.dataType?.toLowerCase()?.includes('char'));
if (hintColumn) {
const parentUniqueName = column.uniquePath.slice(0, -1).join('.');
this.addReferenceToSelect(select, parentUniqueName ? `${parentUniqueName}_ref` : 'basetbl', column);
const childAlias = `${column.uniqueName}_ref`;
select.columns.push({
exprType: 'column',
columnName: hintColumn.columnName,
alias: `hint_${column.uniqueName}`,
source: { alias: childAlias },
});
res = 'refAdded';
}
} else {
this.requireFkTarget(column);
res = 'loadRequired';
}
}
}
return res;
}
applyFilterOnSelect(select: Select, displayedColumnInfo: DisplayedColumnInfo) {
for (const uniqueName in this.config.filters) {
const filter = this.config.filters[uniqueName];
@@ -328,20 +155,8 @@ export abstract class GridDisplay {
}
}
getDisplayColumns(table: TableInfo, parentPath: string[]) {
return (
table?.columns
?.map((col) => this.getDisplayColumn(table, col, parentPath))
?.map((col) => ({
...col,
isChecked: this.isColumnChecked(col),
hintColumnName: col.foreignKey ? `hint_${col.uniqueName}` : null,
})) || []
);
}
getColumns(columnFilter) {
return this.enrichExpandedColumns(this.columns.filter((col) => filterName(columnFilter, col.columnName)));
return this.columns.filter((col) => filterName(columnFilter, col.columnName));
}
getGridColumns() {
@@ -421,4 +236,54 @@ export abstract class GridDisplay {
condition: insertedRowIndex == null ? this.getChangeSetCondition(row) : null,
};
}
createSelect(): Select {
return null;
}
processReferences(select: Select, displayedColumnInfo: DisplayedColumnInfo): ReferenceActionResult {
return 'noAction';
}
createSelectBase(name: NamedObjectInfo, columns: ColumnInfo[]) {
if (!columns) return null;
const orderColumnName = columns[0].columnName;
const select: Select = {
commandType: 'select',
from: { name, alias: 'basetbl' },
columns: columns.map((col) => ({
exprType: 'column',
alias: col.columnName,
source: { alias: 'basetbl' },
...col,
})),
orderBy: [
{
exprType: 'column',
columnName: orderColumnName,
direction: 'ASC',
},
],
};
const displayedColumnInfo = _.keyBy(
this.columns.map((col) => ({ ...col, sourceAlias: 'basetbl' })),
'uniqueName'
);
const action = this.processReferences(select, displayedColumnInfo)
this.applyFilterOnSelect(select, displayedColumnInfo);
this.applySortOnSelect(select, displayedColumnInfo);
if (action == 'loadRequired') {
return null;
}
return select;
}
getPageQuery(offset: number, count: number) {
const select = this.createSelect();
if (!select) return null;
if (this.driver.dialect.rangeSelect) select.range = { offset: offset, limit: count };
else if (this.driver.dialect.limitSelect) select.topRecords = count;
const sql = treeToSql(this.driver, select, dumpSqlSelect);
return sql;
}
}

View File

@@ -1,8 +1,16 @@
import _ from 'lodash';
import { GridDisplay, combineReferenceActions, ChangeCacheFunc } from './GridDisplay';
import { Select, treeToSql, dumpSqlSelect } from '@dbgate/sqltree';
import { TableInfo, EngineDriver } from '@dbgate/types';
import {
GridDisplay,
combineReferenceActions,
ChangeCacheFunc,
DisplayColumn,
ReferenceActionResult,
DisplayedColumnInfo,
} from './GridDisplay';
import { TableInfo, EngineDriver, ViewInfo, ColumnInfo } from '@dbgate/types';
import { GridConfig, GridCache } from './GridConfig';
import { Expression, Select, treeToSql, dumpSqlSelect } from '@dbgate/sqltree';
import { filterName } from './filterName';
export class TableGridDisplay extends GridDisplay {
constructor(
@@ -12,9 +20,9 @@ export class TableGridDisplay extends GridDisplay {
setConfig: (config: GridConfig) => void,
cache: GridCache,
setCache: ChangeCacheFunc,
getTableInfo: ({ schemaName, pureName }) => Promise<TableInfo>
protected getTableInfo: ({ schemaName, pureName }) => Promise<TableInfo>
) {
super(config, setConfig, cache, setCache, getTableInfo, driver);
super(config, setConfig, cache, setCache, driver);
this.columns = this.getDisplayColumns(table, []);
this.filterable = true;
this.sortable = true;
@@ -27,48 +35,202 @@ export class TableGridDisplay extends GridDisplay {
}
}
createSelect() {
if (!this.table.columns) return null;
const orderColumnName = this.table.columns[0].columnName;
const select: Select = {
commandType: 'select',
from: { name: this.table, alias: 'basetbl' },
columns: this.table.columns.map((col) => ({
exprType: 'column',
alias: col.columnName,
source: { alias: 'basetbl' },
...col,
})),
orderBy: [
{
exprType: 'column',
columnName: orderColumnName,
direction: 'ASC',
},
],
};
const displayedColumnInfo = _.keyBy(
this.columns.map((col) => ({ ...col, sourceAlias: 'basetbl' })),
'uniqueName'
getDisplayColumns(table: TableInfo, parentPath: string[]) {
return (
table?.columns
?.map((col) => this.getDisplayColumn(table, col, parentPath))
?.map((col) => ({
...col,
isChecked: this.isColumnChecked(col),
hintColumnName: col.foreignKey ? `hint_${col.uniqueName}` : null,
})) || []
);
}
addJoinsFromExpandedColumns(
select: Select,
columns: DisplayColumn[],
parentAlias: string,
columnSources
): ReferenceActionResult {
let res: ReferenceActionResult = 'noAction';
for (const column of columns) {
if (this.isExpandedColumn(column.uniqueName)) {
const table = this.cache.tables[column.uniqueName];
if (table) {
const childAlias = `${column.uniqueName}_ref`;
const subcolumns = this.getDisplayColumns(table, column.uniquePath);
const tableAction = combineReferenceActions(
this.addJoinsFromExpandedColumns(select, subcolumns, childAlias, columnSources),
this.addAddedColumnsToSelect(select, subcolumns, childAlias, columnSources)
);
if (tableAction == 'refAdded') {
this.addReferenceToSelect(select, parentAlias, column);
res = 'refAdded';
}
if (tableAction == 'loadRequired') {
return 'loadRequired';
}
} else {
this.requireFkTarget(column);
res = 'loadRequired';
}
}
}
return res;
// const addedColumns = this.getGridColumns().filter(x=>x.)
}
addReferenceToSelect(select: Select, parentAlias: string, column: DisplayColumn) {
const childAlias = `${column.uniqueName}_ref`;
if ((select.from.relations || []).find((x) => x.alias == childAlias)) return;
const table = this.cache.tables[column.uniqueName];
select.from.relations = [
...(select.from.relations || []),
{
joinType: 'LEFT JOIN',
name: table,
alias: childAlias,
conditions: [
{
conditionType: 'binary',
operator: '=',
left: {
exprType: 'column',
columnName: column.columnName,
source: { name: column, alias: parentAlias },
},
right: {
exprType: 'column',
columnName: table.primaryKey.columns[0].columnName,
source: { name: table, alias: childAlias },
},
},
],
},
];
}
addHintsToSelect(select: Select): ReferenceActionResult {
let res: ReferenceActionResult = 'noAction';
for (const column of this.getGridColumns()) {
if (column.foreignKey) {
const table = this.cache.tables[column.uniqueName];
if (table) {
const hintColumn = table.columns.find((x) => x?.dataType?.toLowerCase()?.includes('char'));
if (hintColumn) {
const parentUniqueName = column.uniquePath.slice(0, -1).join('.');
this.addReferenceToSelect(select, parentUniqueName ? `${parentUniqueName}_ref` : 'basetbl', column);
const childAlias = `${column.uniqueName}_ref`;
select.columns.push({
exprType: 'column',
columnName: hintColumn.columnName,
alias: `hint_${column.uniqueName}`,
source: { alias: childAlias },
});
res = 'refAdded';
}
} else {
this.requireFkTarget(column);
res = 'loadRequired';
}
}
}
return res;
}
enrichExpandedColumns(list: DisplayColumn[]): DisplayColumn[] {
const res = [];
for (const item of list) {
res.push(item);
if (this.isExpandedColumn(item.uniqueName)) res.push(...this.getExpandedColumns(item));
}
return res;
}
getExpandedColumns(column: DisplayColumn) {
const table = this.cache.tables[column.uniqueName];
if (table) {
return this.enrichExpandedColumns(this.getDisplayColumns(table, column.uniquePath));
} else {
// load expanded columns
this.requireFkTarget(column);
}
return [];
}
requireFkTarget(column: DisplayColumn) {
const { uniqueName, foreignKey } = column;
this.getTableInfo({ schemaName: foreignKey.refSchemaName, pureName: foreignKey.refTableName }).then((table) => {
this.setCache((cache) => ({
...cache,
tables: {
...cache.tables,
[uniqueName]: table,
},
}));
});
}
processReferences(select: Select, displayedColumnInfo: DisplayedColumnInfo): ReferenceActionResult {
const action = combineReferenceActions(
this.addJoinsFromExpandedColumns(select, this.columns, 'basetbl', displayedColumnInfo),
this.addHintsToSelect(select)
);
this.applyFilterOnSelect(select, displayedColumnInfo);
this.applySortOnSelect(select, displayedColumnInfo);
if (action == 'loadRequired') {
return null;
}
return action;
}
createSelect() {
const select = this.createSelectBase(this.table, this.table.columns);
return select;
}
getPageQuery(offset: number, count: number) {
const select = this.createSelect();
if (!select) return null;
if (this.driver.dialect.rangeSelect) select.range = { offset: offset, limit: count };
else if (this.driver.dialect.limitSelect) select.topRecords = count;
const sql = treeToSql(this.driver, select, dumpSqlSelect);
return sql;
getColumns(columnFilter) {
return this.enrichExpandedColumns(this.columns.filter((col) => filterName(columnFilter, col.columnName)));
}
getDisplayColumn(table: TableInfo, col: ColumnInfo, parentPath: string[]) {
const uniquePath = [...parentPath, col.columnName];
const uniqueName = uniquePath.join('.');
// console.log('this.config.addedColumns', this.config.addedColumns, uniquePath);
return {
...col,
pureName: table.pureName,
schemaName: table.schemaName,
headerText: uniquePath.length == 1 ? col.columnName : `${table.pureName}.${col.columnName}`,
uniqueName,
uniquePath,
isPrimaryKey: table.primaryKey && !!table.primaryKey.columns.find((x) => x.columnName == col.columnName),
foreignKey:
table.foreignKeys &&
table.foreignKeys.find((fk) => fk.columns.length == 1 && fk.columns[0].columnName == col.columnName),
};
}
addAddedColumnsToSelect(
select: Select,
columns: DisplayColumn[],
parentAlias: string,
displayedColumnInfo: DisplayedColumnInfo
): ReferenceActionResult {
let res: ReferenceActionResult = 'noAction';
for (const column of columns) {
if (this.config.addedColumns.includes(column.uniqueName)) {
select.columns.push({
exprType: 'column',
columnName: column.columnName,
alias: column.uniqueName,
source: { name: column, alias: parentAlias },
});
displayedColumnInfo[column.uniqueName] = {
...column,
sourceAlias: parentAlias,
};
res = 'refAdded';
}
}
return res;
}
}

View File

@@ -0,0 +1,50 @@
import _ from 'lodash';
import { GridDisplay, ChangeCacheFunc } from './GridDisplay';
import { EngineDriver, ViewInfo, ColumnInfo } from '@dbgate/types';
import { GridConfig, GridCache } from './GridConfig';
export class ViewGridDisplay extends GridDisplay {
constructor(
public view: ViewInfo,
driver: EngineDriver,
config: GridConfig,
setConfig: (config: GridConfig) => void,
cache: GridCache,
setCache: ChangeCacheFunc
) {
super(config, setConfig, cache, setCache, driver);
this.columns = this.getDisplayColumns(view);
this.filterable = true;
this.sortable = true;
this.editable = true;
}
getDisplayColumns(view: ViewInfo) {
return (
view?.columns
?.map((col) => this.getDisplayColumn(view, col))
?.map((col) => ({
...col,
isChecked: this.isColumnChecked(col),
})) || []
);
}
getDisplayColumn(view: ViewInfo, col: ColumnInfo) {
const uniquePath = [col.columnName];
const uniqueName = uniquePath.join('.');
return {
...col,
pureName: view.pureName,
schemaName: view.schemaName,
headerText: col.columnName,
uniqueName,
uniquePath,
};
}
createSelect() {
const select = this.createSelectBase(this.view, this.view.columns);
return select;
}
}

View File

@@ -1,6 +1,7 @@
export * from "./GridDisplay";
export * from "./GridConfig";
export * from "./TableGridDisplay";
export * from "./ViewGridDisplay";
export * from "./JslGridDisplay";
export * from "./ChangeSet";
export * from "./filterName";

View File

@@ -227,6 +227,7 @@ class MsSqlAnalyser extends DatabaseAnalayser {
this.pool,
this.createQuery('programmables', ['procedures', 'functions'])
);
const viewColumnRows = await this.driver.query(this.pool, this.createQuery('viewColumns', ['views']));
const tables = tablesRows.rows.map((row) => ({
...row,
@@ -245,6 +246,14 @@ class MsSqlAnalyser extends DatabaseAnalayser {
const views = viewsRows.rows.map((row) => ({
...row,
createSql: getCreateSql(row),
columns: viewColumnRows.rows
.filter((col) => (col.objectId = row.objectId))
.map(({ isNullable, isIdentity, ...col }) => ({
...col,
notNull: !isNullable,
autoIncrement: !!isIdentity,
commonType: detectType(col),
})),
}));
const procedures = programmableRows.rows

View File

@@ -6,6 +6,7 @@ const modifications = require('./modifications');
const loadSqlCode = require('./loadSqlCode');
const views = require('./views');
const programmables = require('./programmables');
const viewColumns = require('./viewColumns');
module.exports = {
columns,
@@ -16,4 +17,5 @@ module.exports = {
loadSqlCode,
views,
programmables,
viewColumns,
};

View File

@@ -0,0 +1,18 @@
module.exports = `
select
o.object_id AS objectId,
col.TABLE_SCHEMA as schemaName,
col.TABLE_NAME as pureName,
col.COLUMN_NAME as columnName,
col.IS_NULLABLE as isNullable,
col.DATA_TYPE as dataType,
col.CHARACTER_MAXIMUM_LENGTH,
col.NUMERIC_PRECISION as precision,
col.NUMERIC_SCALE as scale,
col.COLUMN_DEFAULT
FROM sys.objects o
INNER JOIN sys.schemas u ON u.schema_id=o.schema_id
INNER JOIN INFORMATION_SCHEMA.COLUMNS col ON col.TABLE_NAME = o.name AND col.TABLE_SCHEMA = u.name
WHERE o.type in ('V') and o.object_id =[OBJECT_ID_CONDITION]
order by col.ORDINAL_POSITION
`;

View File

@@ -61,7 +61,9 @@ export interface TableInfo extends DatabaseObjectInfo {
dependencies?: ForeignKeyInfo[];
}
export interface ViewInfo extends SqlObjectInfo {}
export interface ViewInfo extends SqlObjectInfo {
columns: ColumnInfo[];
}
export interface ProcedureInfo extends SqlObjectInfo {}

View File

@@ -6,58 +6,54 @@ import getConnectionInfo from '../utility/getConnectionInfo';
import fullDisplayName from '../utility/fullDisplayName';
import { filterName } from '@dbgate/datalib';
// async function openTableDetail(setOpenedTabs, tabComponent, { schemaName, pureName, conid, database }) {
// const connection = await getConnectionInfo(conid);
// const tooltip = `${connection.displayName || connection.server}\n${database}\n${fullDisplayName({
// schemaName,
// pureName,
// })}`;
async function openViewDetail(setOpenedTabs, tabComponent, { schemaName, pureName, conid, database }) {
const connection = await getConnectionInfo(conid);
const tooltip = `${connection.displayName || connection.server}\n${database}\n${fullDisplayName({
schemaName,
pureName,
})}`;
// openNewTab(setOpenedTabs, {
// title: pureName,
// tooltip,
// icon: 'table2.svg',
// tabComponent,
// props: {
// schemaName,
// pureName,
// conid,
// database,
// },
// });
// }
openNewTab(setOpenedTabs, {
title: pureName,
tooltip,
icon: 'view2.svg',
tabComponent,
props: {
schemaName,
pureName,
conid,
database,
},
});
}
// function Menu({ data, makeAppObj, setOpenedTabs }) {
// const handleOpenData = () => {
// openTableDetail(setOpenedTabs, 'TableDataTab', data);
// };
// const handleOpenStructure = () => {
// openTableDetail(setOpenedTabs, 'TableStructureTab', data);
// };
// const handleOpenCreateScript = () => {
// openTableDetail(setOpenedTabs, 'TableCreateScriptTab', data);
// };
// return (
// <>
// <DropDownMenuItem onClick={handleOpenData}>Open data</DropDownMenuItem>
// <DropDownMenuItem onClick={handleOpenStructure}>Open structure</DropDownMenuItem>
// <DropDownMenuItem onClick={handleOpenCreateScript}>Create SQL</DropDownMenuItem>
// </>
// );
// }
function Menu({ data, makeAppObj, setOpenedTabs }) {
const handleOpenData = () => {
openViewDetail(setOpenedTabs, 'TableDataTab', data);
};
const handleOpenCreateScript = () => {
openViewDetail(setOpenedTabs, 'TableCreateScriptTab', data);
};
return (
<>
<DropDownMenuItem onClick={handleOpenData}>Open data</DropDownMenuItem>
<DropDownMenuItem onClick={handleOpenCreateScript}>Create SQL</DropDownMenuItem>
</>
);
}
const viewAppObject = () => ({ conid, database, pureName, schemaName }, { setOpenedTabs }) => {
const title = schemaName ? `${schemaName}.${pureName}` : pureName;
const key = title;
const Icon = ViewIcon;
// const onClick = ({ schemaName, pureName }) => {
// openTableDetail(setOpenedTabs, 'TableDataTab', {
// schemaName,
// pureName,
// conid,
// database,
// });
// };
const onClick = ({ schemaName, pureName }) => {
openViewDetail(setOpenedTabs, 'ViewDataTab', {
schemaName,
pureName,
conid,
database,
});
};
const matcher = (filter) => filterName(filter, pureName);
const groupTitle = 'Views';
@@ -65,7 +61,8 @@ const viewAppObject = () => ({ conid, database, pureName, schemaName }, { setOpe
title,
key,
Icon,
// Menu, onClick,
Menu,
onClick,
matcher,
groupTitle,
};

View File

@@ -0,0 +1,50 @@
import React from 'react';
import useFetch from '../utility/useFetch';
import styled from 'styled-components';
import theme from '../theme';
import DataGrid from '../datagrid/DataGrid';
import { ViewGridDisplay, createGridConfig, createGridCache, createChangeSet } from '@dbgate/datalib';
import useTableInfo from '../utility/useTableInfo';
import useConnectionInfo from '../utility/useConnectionInfo';
import engines from '@dbgate/engines';
import getTableInfo from '../utility/getTableInfo';
import useUndoReducer from '../utility/useUndoReducer';
import usePropsCompare from '../utility/usePropsCompare';
import { useUpdateDatabaseForTab } from '../utility/globalState';
import useViewInfo from '../utility/useViewInfo';
export default function ViewDataTab({ conid, database, schemaName, pureName, tabVisible, toolbarPortalRef }) {
const viewInfo = useViewInfo({ conid, database, schemaName, pureName });
const [config, setConfig] = React.useState(createGridConfig());
const [cache, setCache] = React.useState(createGridCache());
const [changeSetState, dispatchChangeSet] = useUndoReducer(createChangeSet());
useUpdateDatabaseForTab(tabVisible, conid, database);
const connection = useConnectionInfo(conid);
// usePropsCompare({ tableInfo, connection, config, cache });
const display = React.useMemo(
() =>
viewInfo && connection
? new ViewGridDisplay(viewInfo, engines(connection), config, setConfig, cache, setCache,
)
: null,
[viewInfo, connection, config, cache]
);
if (!display) return null;
return (
<DataGrid
// key={`${conid}, ${database}, ${schemaName}, ${pureName}`}
conid={conid}
database={database}
display={display}
tabVisible={tabVisible}
changeSetState={changeSetState}
dispatchChangeSet={dispatchChangeSet}
toolbarPortalRef={toolbarPortalRef}
/>
);
}

View File

@@ -1,10 +1,12 @@
import TableDataTab from './TableDataTab';
import ViewDataTab from './ViewDataTab';
import TableStructureTab from './TableStructureTab';
import TableCreateScriptTab from './TableCreateScriptTab';
import QueryTab from './QueryTab';
export default {
TableDataTab,
ViewDataTab,
TableStructureTab,
TableCreateScriptTab,
QueryTab,

View File

@@ -3,7 +3,7 @@ import axios from './axios';
export default async function getTableInfo({ conid, database, schemaName, pureName }) {
const resp = await axios.request({
method: 'get',
url: 'tables/table-info',
url: 'metadata/table-info',
params: { conid, database, schemaName, pureName },
});
/** @type {import('@dbgate/types').TableInfo} */

View File

@@ -3,7 +3,7 @@ import useFetch from './useFetch';
export default function useTableInfo({ conid, database, schemaName, pureName }) {
/** @type {import('@dbgate/types').TableInfo} */
const tableInfo = useFetch({
url: 'tables/table-info',
url: 'metadata/table-info',
params: { conid, database, schemaName, pureName },
reloadTrigger: `database-structure-changed-${conid}-${database}`,
});

View File

@@ -0,0 +1,11 @@
import useFetch from './useFetch';
export default function useViewInfo({ conid, database, schemaName, pureName }) {
/** @type {import('@dbgate/types').ViewInfo} */
const viewInfo = useFetch({
url: 'metadata/view-info',
params: { conid, database, schemaName, pureName },
reloadTrigger: `database-structure-changed-${conid}-${database}`,
});
return viewInfo;
}

View File

@@ -95,7 +95,7 @@ function ConnectionList() {
function SqlObjectList({ conid, database }) {
const objects = useFetch({
url: `database-connections/list-objects?conid=${conid}&database=${database}`,
url: `metadata/list-objects?conid=${conid}&database=${database}`,
reloadTrigger: `database-structure-changed-${conid}-${database}`,
});