mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
query result shown
This commit is contained in:
parent
3bc6f60f75
commit
8be7c0aa6b
@ -34,7 +34,7 @@ module.exports = {
|
||||
openReader(jslid) {
|
||||
const file = path.join(jsldir(), `${jslid}.jsonl`);
|
||||
return new Promise((resolve, reject) =>
|
||||
lineReader.open(file, function (err, reader) {
|
||||
lineReader.open(file, (err, reader) => {
|
||||
if (err) reject(err);
|
||||
resolve();
|
||||
this.openedReaders[jslid] = {
|
||||
@ -50,7 +50,7 @@ module.exports = {
|
||||
await this.closeReader();
|
||||
}
|
||||
if (!this.openedReaders[jslid]) {
|
||||
await this.openReader();
|
||||
await this.openReader(jslid);
|
||||
}
|
||||
while (this.openedReaders[jslid].readedCount < offset) {
|
||||
await this.readLine(jslid);
|
||||
|
@ -40,11 +40,12 @@ export function findExistingChangeSetItem(
|
||||
changeSet: ChangeSet,
|
||||
definition: ChangeSetRowDefinition
|
||||
): [keyof ChangeSet, ChangeSetItem] {
|
||||
if (!changeSet) return ['updates', null];
|
||||
if (definition.insertedRowIndex != null) {
|
||||
return [
|
||||
'inserts',
|
||||
changeSet.inserts.find(
|
||||
x =>
|
||||
(x) =>
|
||||
x.pureName == definition.pureName &&
|
||||
x.schemaName == definition.schemaName &&
|
||||
x.insertedRowIndex == definition.insertedRowIndex
|
||||
@ -52,7 +53,7 @@ export function findExistingChangeSetItem(
|
||||
];
|
||||
} else {
|
||||
const inUpdates = changeSet.updates.find(
|
||||
x =>
|
||||
(x) =>
|
||||
x.pureName == definition.pureName &&
|
||||
x.schemaName == definition.schemaName &&
|
||||
_.isEqual(x.condition, definition.condition)
|
||||
@ -60,7 +61,7 @@ export function findExistingChangeSetItem(
|
||||
if (inUpdates) return ['updates', inUpdates];
|
||||
|
||||
const inDeletes = changeSet.deletes.find(
|
||||
x =>
|
||||
(x) =>
|
||||
x.pureName == definition.pureName &&
|
||||
x.schemaName == definition.schemaName &&
|
||||
_.isEqual(x.condition, definition.condition)
|
||||
@ -84,7 +85,7 @@ export function setChangeSetValue(
|
||||
if (existingItem) {
|
||||
return {
|
||||
...changeSet,
|
||||
[fieldName]: changeSet[fieldName].map(item =>
|
||||
[fieldName]: changeSet[fieldName].map((item) =>
|
||||
item == existingItem
|
||||
? {
|
||||
...item,
|
||||
@ -164,7 +165,7 @@ export function batchUpdateChangeSet(
|
||||
}
|
||||
|
||||
function extractFields(item: ChangeSetItem): UpdateField[] {
|
||||
return _.keys(item.fields).map(targetColumn => ({
|
||||
return _.keys(item.fields).map((targetColumn) => ({
|
||||
targetColumn,
|
||||
exprType: 'value',
|
||||
value: item.fields[targetColumn],
|
||||
@ -185,7 +186,7 @@ function insertToSql(item: ChangeSetItem): Insert {
|
||||
function extractCondition(item: ChangeSetItem): Condition {
|
||||
return {
|
||||
conditionType: 'and',
|
||||
conditions: _.keys(item.condition).map(columnName => ({
|
||||
conditions: _.keys(item.condition).map((columnName) => ({
|
||||
conditionType: 'binary',
|
||||
operator: '=',
|
||||
left: {
|
||||
@ -248,7 +249,7 @@ export function revertChangeSetRowChanges(changeSet: ChangeSet, definition: Chan
|
||||
if (item)
|
||||
return {
|
||||
...changeSet,
|
||||
[field]: changeSet[field].filter(x => x != item),
|
||||
[field]: changeSet[field].filter((x) => x != item),
|
||||
};
|
||||
return changeSet;
|
||||
}
|
||||
@ -280,8 +281,8 @@ export function deleteChangeSetRows(changeSet: ChangeSet, definition: ChangeSetR
|
||||
export function getChangeSetInsertedRows(changeSet: ChangeSet, name?: NamedObjectInfo) {
|
||||
if (!name) return [];
|
||||
if (!changeSet) return [];
|
||||
const rows = changeSet.inserts.filter(x => x.pureName == name.pureName && x.schemaName == name.schemaName);
|
||||
const maxIndex = _.maxBy(rows, x => x.insertedRowIndex)?.insertedRowIndex;
|
||||
const rows = changeSet.inserts.filter((x) => x.pureName == name.pureName && x.schemaName == name.schemaName);
|
||||
const maxIndex = _.maxBy(rows, (x) => x.insertedRowIndex)?.insertedRowIndex;
|
||||
if (maxIndex == null) return [];
|
||||
const res = Array(maxIndex + 1).fill({});
|
||||
for (const row of rows) {
|
||||
|
@ -55,6 +55,9 @@ export abstract class GridDisplay {
|
||||
columns: DisplayColumn[];
|
||||
baseTable?: TableInfo;
|
||||
changeSetKeyFields: string[] = null;
|
||||
sortable = false;
|
||||
filterable = false;
|
||||
editable = false;
|
||||
|
||||
setColumnVisibility(uniquePath: string[], isVisible: boolean) {
|
||||
const uniqueName = uniquePath.join('.');
|
||||
|
@ -16,11 +16,14 @@ export class TableGridDisplay extends GridDisplay {
|
||||
) {
|
||||
super(config, setConfig, cache, setCache, getTableInfo, driver);
|
||||
this.columns = this.getDisplayColumns(table, []);
|
||||
this.filterable = true;
|
||||
this.sortable = true;
|
||||
this.editable = true;
|
||||
this.baseTable = table;
|
||||
if (table && table.columns) {
|
||||
this.changeSetKeyFields = table.primaryKey
|
||||
? table.primaryKey.columns.map(x => x.columnName)
|
||||
: table.columns.map(x => x.columnName);
|
||||
? table.primaryKey.columns.map((x) => x.columnName)
|
||||
: table.columns.map((x) => x.columnName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +33,7 @@ export class TableGridDisplay extends GridDisplay {
|
||||
const select: Select = {
|
||||
commandType: 'select',
|
||||
from: { name: this.table, alias: 'basetbl' },
|
||||
columns: this.table.columns.map(col => ({
|
||||
columns: this.table.columns.map((col) => ({
|
||||
exprType: 'column',
|
||||
alias: col.columnName,
|
||||
source: { alias: 'basetbl' },
|
||||
@ -45,7 +48,7 @@ export class TableGridDisplay extends GridDisplay {
|
||||
],
|
||||
};
|
||||
const displayedColumnInfo = _.keyBy(
|
||||
this.columns.map(col => ({ ...col, sourceAlias: 'basetbl' })),
|
||||
this.columns.map((col) => ({ ...col, sourceAlias: 'basetbl' })),
|
||||
'uniqueName'
|
||||
);
|
||||
const action = combineReferenceActions(
|
||||
|
@ -10,6 +10,7 @@ const TabContainer = styled.div`
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
visibility: ${props =>
|
||||
// @ts-ignore
|
||||
props.tabVisible ? 'visible' : 'hidden'};
|
||||
|
@ -12,7 +12,8 @@ const HeaderDiv = styled.div`
|
||||
const LabelDiv = styled.div`
|
||||
flex: 1;
|
||||
min-width: 10px;
|
||||
padding-left: 2px;
|
||||
// padding-left: 2px;
|
||||
padding: 2px;
|
||||
margin: auto;
|
||||
`;
|
||||
|
||||
@ -36,10 +37,12 @@ export default function ColumnHeaderControl({ column, setSort, order }) {
|
||||
</IconWrapper>
|
||||
)}
|
||||
</LabelDiv>
|
||||
<DropDownButton>
|
||||
<DropDownMenuItem onClick={() => setSort('ASC')}>Sort ascending</DropDownMenuItem>
|
||||
<DropDownMenuItem onClick={() => setSort('DESC')}>Sort descending</DropDownMenuItem>
|
||||
</DropDownButton>
|
||||
{setSort && (
|
||||
<DropDownButton>
|
||||
<DropDownMenuItem onClick={() => setSort('ASC')}>Sort ascending</DropDownMenuItem>
|
||||
<DropDownMenuItem onClick={() => setSort('DESC')}>Sort descending</DropDownMenuItem>
|
||||
</DropDownButton>
|
||||
)}
|
||||
</HeaderDiv>
|
||||
);
|
||||
}
|
||||
|
@ -90,6 +90,7 @@ const TableHeaderCell = styled.td`
|
||||
// border-collapse: collapse;
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
// padding: 2px;
|
||||
margin: 0;
|
||||
background-color: #f6f7f9;
|
||||
overflow: hidden;
|
||||
@ -112,12 +113,10 @@ const FocusField = styled.input`
|
||||
async function loadDataPage(props, offset, limit) {
|
||||
const { display, conid, database, jslid } = props;
|
||||
|
||||
console.log('LOAD PAGE', jslid);
|
||||
|
||||
if (jslid) {
|
||||
const response = await axios.request({
|
||||
url: 'jsldata/get-rows',
|
||||
method: 'post',
|
||||
method: 'get',
|
||||
params: {
|
||||
jslid,
|
||||
offset,
|
||||
@ -256,6 +255,7 @@ export default function DataGridCore(props) {
|
||||
const [inplaceEditorState, dispatchInsplaceEditor] = React.useReducer((state, action) => {
|
||||
switch (action.type) {
|
||||
case 'show':
|
||||
if (!display.editable) return {};
|
||||
return {
|
||||
cell: action.cell,
|
||||
text: action.text,
|
||||
@ -914,39 +914,41 @@ export default function DataGridCore(props) {
|
||||
>
|
||||
<ColumnHeaderControl
|
||||
column={col}
|
||||
setSort={(order) => display.setSort(col.uniqueName, order)}
|
||||
setSort={display.sortable ? (order) => display.setSort(col.uniqueName, order) : null}
|
||||
order={display.getSortOrder(col.uniqueName)}
|
||||
/>
|
||||
</TableHeaderCell>
|
||||
))}
|
||||
</TableHeaderRow>
|
||||
<TableHeaderRow>
|
||||
<TableHeaderCell
|
||||
style={{ width: hederColwidthPx, minWidth: hederColwidthPx, maxWidth: hederColwidthPx }}
|
||||
data-row="filter"
|
||||
data-col="header"
|
||||
>
|
||||
{filterCount > 0 && (
|
||||
<InlineButton onClick={handleClearFilters} square>
|
||||
<i className="fas fa-times" />
|
||||
</InlineButton>
|
||||
)}
|
||||
</TableHeaderCell>
|
||||
{visibleRealColumns.map((col) => (
|
||||
<TableFilterCell
|
||||
key={col.uniqueName}
|
||||
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
|
||||
{display.filterable && (
|
||||
<TableHeaderRow>
|
||||
<TableHeaderCell
|
||||
style={{ width: hederColwidthPx, minWidth: hederColwidthPx, maxWidth: hederColwidthPx }}
|
||||
data-row="filter"
|
||||
data-col={col.colIndex}
|
||||
data-col="header"
|
||||
>
|
||||
<DataFilterControl
|
||||
filterType={getFilterType(col.commonType ? col.commonType.typeCode : null)}
|
||||
filter={display.getFilter(col.uniqueName)}
|
||||
setFilter={(value) => display.setFilter(col.uniqueName, value)}
|
||||
/>
|
||||
</TableFilterCell>
|
||||
))}
|
||||
</TableHeaderRow>
|
||||
{filterCount > 0 && (
|
||||
<InlineButton onClick={handleClearFilters} square>
|
||||
<i className="fas fa-times" />
|
||||
</InlineButton>
|
||||
)}
|
||||
</TableHeaderCell>
|
||||
{visibleRealColumns.map((col) => (
|
||||
<TableFilterCell
|
||||
key={col.uniqueName}
|
||||
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
|
||||
data-row="filter"
|
||||
data-col={col.colIndex}
|
||||
>
|
||||
<DataFilterControl
|
||||
filterType={getFilterType(col.commonType ? col.commonType.typeCode : null)}
|
||||
filter={display.getFilter(col.uniqueName)}
|
||||
setFilter={(value) => display.setFilter(col.uniqueName, value)}
|
||||
/>
|
||||
</TableFilterCell>
|
||||
))}
|
||||
</TableHeaderRow>
|
||||
)}
|
||||
</TableHead>
|
||||
<TableBody ref={tableBodyRef}>
|
||||
{loadedAndInsertedRows
|
||||
|
@ -1,8 +1,13 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledTable = styled.table`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
export default function MessagesView({ items }) {
|
||||
return (
|
||||
<table>
|
||||
<StyledTable>
|
||||
<tr>
|
||||
<th>Number</th>
|
||||
<th>Message</th>
|
||||
@ -19,6 +24,6 @@ export default function MessagesView({ items }) {
|
||||
<td>{row.line}</td>
|
||||
</tr>
|
||||
))}
|
||||
</table>
|
||||
</StyledTable>
|
||||
);
|
||||
}
|
||||
|
@ -13,17 +13,18 @@ import SessionMessagesView from '../query/SessionMessagesView';
|
||||
import { TabPage, TabControl } from '../widgets/TabControl';
|
||||
import getResultTabs from '../sqleditor/ResultTabs';
|
||||
import ResultTabs from '../sqleditor/ResultTabs';
|
||||
import { VerticalSplitter } from '../widgets/Splitter';
|
||||
|
||||
const MainContainer = styled.div``;
|
||||
// const MainContainer = styled.div``;
|
||||
|
||||
const EditorContainer = styled.div`
|
||||
height: 600px;
|
||||
position: relative;
|
||||
`;
|
||||
// const EditorContainer = styled.div`
|
||||
// height: 600px;
|
||||
// position: relative;
|
||||
// `;
|
||||
|
||||
const MessagesContainer = styled.div`
|
||||
height: 200px;
|
||||
`;
|
||||
// const MessagesContainer = styled.div`
|
||||
// height: 200px;
|
||||
// `;
|
||||
|
||||
export default function QueryTab({ tabid, conid, database, tabVisible, toolbarPortalRef }) {
|
||||
const localStorageKey = `sql_${tabid}`;
|
||||
@ -73,8 +74,8 @@ export default function QueryTab({ tabid, conid, database, tabVisible, toolbarPo
|
||||
const handleKeyDown = (e) => {};
|
||||
|
||||
return (
|
||||
<MainContainer>
|
||||
<EditorContainer>
|
||||
<>
|
||||
<VerticalSplitter>
|
||||
<SqlEditor
|
||||
value={queryText}
|
||||
onChange={handleChange}
|
||||
@ -82,22 +83,19 @@ export default function QueryTab({ tabid, conid, database, tabVisible, toolbarPo
|
||||
engine={connection && connection.engine}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
|
||||
{toolbarPortalRef &&
|
||||
toolbarPortalRef.current &&
|
||||
tabVisible &&
|
||||
ReactDOM.createPortal(
|
||||
<QueryToolbar isDatabaseDefined={conid && database} execute={handleExecute} />,
|
||||
toolbarPortalRef.current
|
||||
)}
|
||||
</EditorContainer>
|
||||
<MessagesContainer>
|
||||
<ResultTabs sessionId={sessionId}>
|
||||
<TabPage label="Messages">
|
||||
<SessionMessagesView sessionId={sessionId} />
|
||||
</TabPage>
|
||||
</ResultTabs>
|
||||
</MessagesContainer>
|
||||
</MainContainer>
|
||||
</VerticalSplitter>
|
||||
{toolbarPortalRef &&
|
||||
toolbarPortalRef.current &&
|
||||
tabVisible &&
|
||||
ReactDOM.createPortal(
|
||||
<QueryToolbar isDatabaseDefined={conid && database} execute={handleExecute} />,
|
||||
toolbarPortalRef.current
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
30
packages/web/src/widgets/Splitter.js
Normal file
30
packages/web/src/widgets/Splitter.js
Normal file
@ -0,0 +1,30 @@
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const MainContainer = styled.div`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const ChildContainer = styled.div`
|
||||
flex: 1;
|
||||
// flex: 0 0 50%;
|
||||
// flex-basis: 100px;
|
||||
// flex-grow: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
export function VerticalSplitter({ children }) {
|
||||
if (!_.isArray(children) || children.length !== 2) {
|
||||
throw new Error('Splitter must have exactly 2 children');
|
||||
}
|
||||
return (
|
||||
<MainContainer>
|
||||
<ChildContainer>{children[0]}</ChildContainer>
|
||||
<ChildContainer>{children[1]}</ChildContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
@ -24,6 +24,7 @@ const TabNameWrapper = styled.span`
|
||||
|
||||
const TabContainer = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
@ -36,6 +37,7 @@ const TabsContainer = styled.div`
|
||||
|
||||
const MainContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user