table grid display, SQL for browse table is generated on FE

This commit is contained in:
Jan Prochazka
2020-03-05 09:40:05 +01:00
parent bbc969fa71
commit aad9512951
14 changed files with 152 additions and 39 deletions

View File

@@ -66,4 +66,12 @@ module.exports = {
tables: _.sortBy(tables, x => `${x.schemaName}.${x.pureName}`), tables: _.sortBy(tables, x => `${x.schemaName}.${x.pureName}`),
}; // .map(fp.pick(['tableName', 'schemaName'])); }; // .map(fp.pick(['tableName', 'schemaName']));
}, },
queryData_meta: 'post',
async queryData({ conid, database, sql }) {
console.log(`Processing query, conid=${conid}, database=${database}, sql=${sql}`);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'queryData', sql });
return res;
},
}; };

View File

@@ -2,12 +2,12 @@ const _ = require('lodash');
const databaseConnections = require('./databaseConnections'); const databaseConnections = require('./databaseConnections');
module.exports = { module.exports = {
tableData_meta: 'get', // tableData_meta: 'get',
async tableData({ conid, database, schemaName, pureName }) { // async tableData({ conid, database, schemaName, pureName }) {
const opened = await databaseConnections.ensureOpened(conid, database); // const opened = await databaseConnections.ensureOpened(conid, database);
const res = await databaseConnections.sendRequest(opened, { msgtype: 'tableData', schemaName, pureName }); // const res = await databaseConnections.sendRequest(opened, { msgtype: 'tableData', schemaName, pureName });
return res; // return res;
}, // },
tableInfo_meta: 'get', tableInfo_meta: 'get',
async tableInfo({ conid, database, schemaName, pureName }) { async tableInfo({ conid, database, schemaName, pureName }) {

View File

@@ -32,24 +32,23 @@ function waitConnected() {
}); });
} }
async function handleTableData({ msgid, schemaName, pureName }) { async function handleQueryData({ msgid, sql }) {
// const select = new Select();
// if (driver.dialect.limitSelect) select.topRecords = 100;
// if (driver.dialect.rangeSelect) select.range = { offset: 0, limit: 100 };
// select.from = { schemaName, pureName };
// select.selectAll = true;
// const sql = select.toSql(driver);
await waitConnected(); await waitConnected();
const driver = engines(storedConnection); const driver = engines(storedConnection);
const select = new Select();
if (driver.dialect.limitSelect) select.topRecords = 100;
if (driver.dialect.rangeSelect) select.range = { offset: 0, limit: 100 };
select.from = { schemaName, pureName };
select.selectAll = true;
const sql = select.toSql(driver);
const res = await driver.query(systemConnection, sql); const res = await driver.query(systemConnection, sql);
process.send({ msgtype: 'response', msgid, ...res }); process.send({ msgtype: 'response', msgid, ...res });
} }
const messageHandlers = { const messageHandlers = {
connect: handleConnect, connect: handleConnect,
tableData: handleTableData, queryData: handleQueryData,
}; };
async function handleMessage({ msgtype, ...other }) { async function handleMessage({ msgtype, ...other }) {

1
packages/datalib/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
lib

View File

@@ -0,0 +1,22 @@
{
"version": "0.1.0",
"name": "@dbgate/datalib",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"scripts": {
"prepare": "yarn build",
"build": "tsc",
"start": "tsc --watch"
},
"files": [
"lib"
],
"dependencies": {
"@dbgate/sqltree": "^0.1.0"
},
"devDependencies": {
"@dbgate/types": "^0.1.0",
"@types/node": "^13.7.0",
"typescript": "^3.7.5"
}
}

View File

@@ -0,0 +1,5 @@
import {Select} from '@dbgate/sqltree'
export default abstract class GridDisplay {
abstract getPageQuery(offse: number, count: number): string;
}

View File

@@ -0,0 +1,20 @@
import GridDisplay from "./GridDisplay";
import { Select } from "@dbgate/sqltree";
import { TableInfo, EngineDriver } from "@dbgate/types";
export default class TableGridDisplay extends GridDisplay {
constructor(public table: TableInfo, public driver: EngineDriver) {
super();
}
getPageQuery(offset: number, count: number) {
const select = new Select();
if (this.driver.dialect.limitSelect) select.topRecords = count;
if (this.driver.dialect.rangeSelect)
select.range = { offset: offset, limit: count };
select.from = this.table;
select.selectAll = true;
const sql = select.toSql(this.driver);
return sql;
}
}

View File

@@ -0,0 +1,2 @@
export { default as GridDisplay } from "./GridDisplay";
export { default as TableGridDisplay } from "./TableGridDisplay";

View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "ES2015",
"module": "commonjs",
"declaration": true,
"skipLibCheck": true,
"outDir": "lib"
},
"include": [
"src/**/*"
]
}

View File

@@ -30,7 +30,9 @@ class SqlDumper {
break; break;
case "k": case "k":
{ {
this.putRaw(value.toUpperCase()); if (value) {
this.putRaw(value.toUpperCase());
}
} }
break; break;
case "f": case "f":
@@ -46,6 +48,7 @@ class SqlDumper {
} }
} }
putFormattedList(c, collection) { putFormattedList(c, collection) {
if (!collection) return;
this.putCollection(", ", collection, item => this.putCollection(", ", collection, item =>
this.putFormattedValue(c, item) this.putFormattedValue(c, item)
); );
@@ -130,7 +133,7 @@ class SqlDumper {
if (column.isPersisted) this.put(" ^persisted"); if (column.isPersisted) this.put(" ^persisted");
return; return;
} }
if (column.dataType) this.put("%k", column.dataType); this.put("%k", column.dataType);
if (column.autoIncrement) { if (column.autoIncrement) {
this.autoIncrement(); this.autoIncrement();
} }

View File

@@ -22,6 +22,8 @@
"socket.io-client": "^2.3.0", "socket.io-client": "^2.3.0",
"styled-components": "^4.4.1", "styled-components": "^4.4.1",
"uuid": "^3.4.0", "uuid": "^3.4.0",
"@dbgate/sqltree": "^0.1.0",
"@dbgate/datalib": "^0.1.0",
"@dbgate/engines": "^0.1.0" "@dbgate/engines": "^0.1.0"
}, },
"scripts": { "scripts": {

View File

@@ -63,10 +63,26 @@ const TableBodyCell = styled.td`
overflow: hidden; overflow: hidden;
`; `;
export default function DataGrid({ params }) { /**
* @param {object} props
* @param {number} props.conid
* @param {string} props.database
* @param {import('@dbgate/datalib').GridDisplay} props.display
*/
export default function DataGrid(props) {
const { conid, database, display } = props;
const sql = display.getPageQuery(0, 100);
console.log(`GRID, conid=${conid}, database=${database}, sql=${sql}`);
const data = useFetch({ const data = useFetch({
url: 'tables/table-data', url: 'database-connections/query-data',
params, method: 'post',
params: {
conid,
database,
},
data: { sql },
}); });
const { rows, columns } = data || {}; const { rows, columns } = data || {};
const [firstVisibleRowScrollIndex, setFirstVisibleRowScrollIndex] = React.useState(0); const [firstVisibleRowScrollIndex, setFirstVisibleRowScrollIndex] = React.useState(0);
@@ -116,21 +132,21 @@ export default function DataGrid({ params }) {
columnSizes.setExtraordinaryIndexes([], []); columnSizes.setExtraordinaryIndexes([], []);
for (let colIndex = 0; colIndex < columns.length; colIndex++) { for (let colIndex = 0; colIndex < columns.length; colIndex++) {
//this.columnSizes.PutSizeOverride(col, this.columns[col].Name.length * 8); //this.columnSizes.PutSizeOverride(col, this.columns[col].Name.length * 8);
let column = columns[colIndex]; let column = columns[colIndex];
// if (column.columnClientObject != null && column.columnClientObject.notNull) context.font = "bold 14px Helvetica"; // if (column.columnClientObject != null && column.columnClientObject.notNull) context.font = "bold 14px Helvetica";
// else context.font = "14px Helvetica"; // else context.font = "14px Helvetica";
context.font = "bold 14px Helvetica"; context.font = 'bold 14px Helvetica';
let text = column.name; let text = column.name;
let headerWidth = context.measureText(text).width + 32; let headerWidth = context.measureText(text).width + 32;
// if (column.columnClientObject != null && column.columnClientObject.icon != null) headerWidth += 16; // if (column.columnClientObject != null && column.columnClientObject.icon != null) headerWidth += 16;
// if (this.getFilterOnColumn(column.uniquePath)) headerWidth += 16; // if (this.getFilterOnColumn(column.uniquePath)) headerWidth += 16;
// if (this.getSortOrder(column.uniquePath)) headerWidth += 16; // if (this.getSortOrder(column.uniquePath)) headerWidth += 16;
columnSizes.putSizeOverride(colIndex, headerWidth); columnSizes.putSizeOverride(colIndex, headerWidth);
} }
// let headerWidth = this.rowHeaderWidthDefault; // let headerWidth = this.rowHeaderWidthDefault;

View File

@@ -3,7 +3,22 @@ import useFetch from '../utility/useFetch';
import styled from 'styled-components'; import styled from 'styled-components';
import theme from '../theme'; import theme from '../theme';
import DataGrid from '../datagrid/DataGrid'; import DataGrid from '../datagrid/DataGrid';
import { TableGridDisplay } from '@dbgate/datalib';
import useTableInfo from '../utility/useTableInfo';
import useConnectionInfo from '../utility/useConnectionInfo';
import engines from '@dbgate/engines';
export default function TableDataTab({ conid, database, schemaName, pureName }) { export default function TableDataTab({ conid, database, schemaName, pureName }) {
return <DataGrid params={{ conid, database, schemaName, pureName }} />; const tableInfo = useTableInfo({ conid, database, schemaName, pureName });
const connection = useConnectionInfo(conid);
if (!tableInfo || !connection) return null;
const display = new TableGridDisplay(tableInfo, engines(connection));
return (
<DataGrid
// key={`${conid}, ${database}, ${schemaName}, ${pureName}`}
conid={conid}
database={database}
display={display}
/>
);
} }

View File

@@ -1,16 +1,18 @@
import React from 'react'; import React from 'react';
import _ from 'lodash';
import axios from './axios'; import axios from './axios';
import useSocket from './SocketProvider'; import useSocket from './SocketProvider';
import stableStringify from 'json-stable-stringify'; import stableStringify from 'json-stable-stringify';
export default function useFetch({ export default function useFetch({
url, url,
data = undefined,
params = undefined, params = undefined,
defaultValue = undefined, defaultValue = undefined,
reloadTrigger = undefined, reloadTrigger = undefined,
...config ...config
}) { }) {
const [value, setValue] = React.useState(defaultValue); const [value, setValue] = React.useState([defaultValue, []]);
const [loadCounter, setLoadCounter] = React.useState(0); const [loadCounter, setLoadCounter] = React.useState(0);
const socket = useSocket(); const socket = useSocket();
@@ -18,24 +20,30 @@ export default function useFetch({
setLoadCounter(loadCounter + 1); setLoadCounter(loadCounter + 1);
}; };
async function loadValue() { const indicators = [url, stableStringify(data), stableStringify(params), loadCounter];
async function loadValue(loadedIndicators) {
const resp = await axios.request({ const resp = await axios.request({
method: 'get', method: 'get',
params, params,
url, url,
data,
...config, ...config,
}); });
setValue(resp.data); setValue([resp.data, loadedIndicators]);
} }
React.useEffect(() => { React.useEffect(() => {
loadValue(); loadValue(indicators);
if (reloadTrigger && socket) { if (reloadTrigger && socket) {
socket.on(reloadTrigger, handleReload); socket.on(reloadTrigger, handleReload);
return () => { return () => {
socket.off(reloadTrigger, handleReload); socket.off(reloadTrigger, handleReload);
}; };
} }
}, [url, stableStringify(params), socket, loadCounter]); }, [...indicators, socket]);
return value; const [returnValue, loadedIndicators] = value;
if (_.isEqual(indicators, loadedIndicators)) return returnValue;
return defaultValue;
} }