Skip to content

Commit 54accfd

Browse files
committed
feat: add universal way of stringify the result
1 parent a249300 commit 54accfd

File tree

8 files changed

+111
-19
lines changed

8 files changed

+111
-19
lines changed

src/drivers/MySQLConnection.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ function mapHeaderType(column: ColumnDefinition): QueryResultHeader {
5050
type = { type: 'number' };
5151
} else if ([0, 246].includes(column.type)) {
5252
type = { type: 'decimal' };
53+
} else if ([255, 252, 250, 251, 252].includes(column.type)) {
54+
type = { type: 'other' };
5355
}
5456

5557
const databaseNameLength = column._buf[13];
@@ -185,9 +187,7 @@ export default class MySQLConnection extends SQLLikeConnection {
185187

186188
async close() {
187189
if (this.pool) {
188-
const conn = await this.getConnection();
189-
conn.end();
190-
conn.destroy();
190+
this.pool.end().catch();
191191
}
192192

193193
if (this.keepAliveTimerId) {

src/libs/TransformResult.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DatabaseSchemas } from 'types/SqlSchema';
1+
import { DatabaseSchemas, TableColumnSchema } from 'types/SqlSchema';
22
import { SqlStatementResult } from './SqlRunnerManager';
33
import { QueryResultHeader } from 'types/SqlResult';
44

@@ -42,3 +42,56 @@ export function transformResultHeaderUseSchema(
4242
};
4343
});
4444
}
45+
46+
/**
47+
* Sometimes the result from database cannot be easily
48+
* converted to string or number. This function looks at
49+
* the data type of column and transform it to friendly
50+
* displayable string
51+
*
52+
* @param value
53+
* @param header
54+
* @returns
55+
*/
56+
export function getDisplayableFromDatabaseValue(
57+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
58+
value: any,
59+
header?: TableColumnSchema
60+
) {
61+
if (value === null) return null;
62+
if (value === undefined) return undefined;
63+
64+
try {
65+
if (!header) return value.toString();
66+
67+
if (header.dataType === 'geometry') {
68+
return JSON.stringify(value as object);
69+
}
70+
71+
return value.toString();
72+
} catch {
73+
return '';
74+
}
75+
}
76+
77+
/**
78+
* Turn the whole result into friendly-string
79+
*
80+
* @param rows
81+
* @param headers
82+
* @returns
83+
*/
84+
export function getDisplayableFromDatabaseRows(
85+
rows: Record<string, unknown>[],
86+
headers: QueryResultHeader[]
87+
): Record<string, unknown>[] {
88+
return rows.map((row) => {
89+
return headers.reduce<Record<string, unknown>>((acc, cur) => {
90+
acc[cur.name] = getDisplayableFromDatabaseValue(
91+
row[cur.name],
92+
cur.columnDefinition
93+
);
94+
return acc;
95+
}, {});
96+
});
97+
}

src/renderer/screens/DatabaseScreen/ExportModal/index.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
faEllipsis,
88
} from '@fortawesome/free-solid-svg-icons';
99
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
10+
import { getDisplayableFromDatabaseRows } from 'libs/TransformResult';
1011
import { useCallback, useState } from 'react';
1112
import Button from 'renderer/components/Button';
1213
import Modal from 'renderer/components/Modal';
@@ -192,15 +193,21 @@ export default function ExportModal({ data, onClose }: ExportModalProps) {
192193

193194
if (format === 'excel') {
194195
window.electron
195-
.saveExcelFile(fileName, data.rows)
196+
.saveExcelFile(
197+
fileName,
198+
getDisplayableFromDatabaseRows(data.rows, data.headers)
199+
)
196200
.then(() => setStage('SUCCESS'))
197201
.catch((e) => {
198202
console.error(e);
199203
setStage('ERROR');
200204
});
201205
} else if (format === 'csv') {
202206
window.electron
203-
.saveCsvFile(fileName, data.rows)
207+
.saveCsvFile(
208+
fileName,
209+
getDisplayableFromDatabaseRows(data.rows, data.headers)
210+
)
204211
.then(() => setStage('SUCCESS'))
205212
.catch((e) => {
206213
console.error(e);

src/renderer/screens/DatabaseScreen/QueryResultViewer/TableCell/TableCell.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { memo } from 'react';
2-
import TableCellString from './TableCellString';
32
import TableCellNumber from './TableCellNumber';
43
import TableCellJson from './TableCellJson';
54
import { QueryResultHeader } from 'types/SqlResult';
65
import TableCellDecimal from './TableCellDecimal';
76
import TableCellEnum from './TableCellEnum';
87
import { TableCellCustomTypeProps } from './createTableCellType';
8+
import TableCellOther from './TableCellOther';
9+
import TableCellString from './TableCellString';
910

1011
interface TableCellProps {
1112
value: unknown;
@@ -25,13 +26,15 @@ function getComponentFromHeader(
2526
return TableCellJson;
2627
} else if (header.type.type === 'decimal') {
2728
return TableCellDecimal;
29+
} else if (['string'].includes(header.type.type)) {
30+
return TableCellString;
2831
} else if (header.columnDefinition) {
2932
if (header.columnDefinition.dataType === 'enum') {
3033
return TableCellEnum;
3134
}
3235
}
3336

34-
return TableCellString;
37+
return TableCellOther;
3538
}
3639

3740
export default memo(function TableCell(props: TableCellProps) {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { getDisplayableFromDatabaseValue } from 'libs/TransformResult';
2+
import { TableEditableContentProps } from './TableEditableCell';
3+
import createTableCellType from './createTableCellType';
4+
import TableCellContent from 'renderer/components/ResizableTable/TableCellContent';
5+
6+
function TableCellStringContent({ header, value }: TableEditableContentProps) {
7+
return (
8+
<TableCellContent
9+
value={getDisplayableFromDatabaseValue(value, header.columnDefinition)}
10+
/>
11+
);
12+
}
13+
14+
const TableCellOther = createTableCellType({
15+
readOnly: true,
16+
diff: (prev: string, current: string) => prev !== current,
17+
content: TableCellStringContent,
18+
onCopy: (value: string) => {
19+
return value;
20+
},
21+
onPaste: (value: string) => {
22+
return { accept: true, value };
23+
},
24+
});
25+
26+
export default TableCellOther;

src/renderer/screens/DatabaseScreen/QueryResultViewer/TableCell/TableEditableCell.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ export interface TableEditableEditorProps {
2727
}
2828

2929
export interface TableEditableContentProps {
30+
header: QueryResultHeader;
3031
value: unknown;
3132
}
3233

3334
interface TableEditableCellProps {
3435
diff: (prev: unknown, current: unknown) => boolean;
35-
editor: React.FC<TableEditableEditorProps>;
36+
editor?: React.FC<TableEditableEditorProps>;
3637
detactEditor?: boolean;
3738
content: React.FC<TableEditableContentProps>;
3839
row: number;
@@ -75,14 +76,16 @@ const TableEditableCell = forwardRef<
7576

7677
const insertValueHandler = useCallback(
7778
(newValue: unknown) => {
79+
if (readOnly) return;
80+
7881
setAfterValue(newValue);
7982
if (diff(value, newValue)) {
8083
setChange(row, col, newValue);
8184
} else {
8285
removeChange(row, col);
8386
}
8487
},
85-
[setAfterValue, setChange, diff]
88+
[setAfterValue, setChange, diff, readOnly]
8689
);
8790

8891
const copyHandler = useCallback(() => {
@@ -108,7 +111,6 @@ const TableEditableCell = forwardRef<
108111
() => {
109112
return {
110113
discard: () => {
111-
console.log('discard', value, row, col);
112114
setAfterValue(value);
113115
removeChange(row, col);
114116
},
@@ -127,10 +129,10 @@ const TableEditableCell = forwardRef<
127129
);
128130

129131
const onEnterEditMode = useCallback(() => {
130-
if (!onEditMode) {
132+
if (!onEditMode && !readOnly) {
131133
setOnEditMode(true);
132134
}
133-
}, [onEditMode, setOnEditMode]);
135+
}, [onEditMode, setOnEditMode, readOnly]);
134136

135137
const handleFocus = useCallback(() => {
136138
if (!onFocus) {
@@ -184,7 +186,7 @@ const TableEditableCell = forwardRef<
184186
onContextMenu={handleFocus}
185187
onDoubleClick={onEnterEditMode}
186188
>
187-
{onEditMode ? (
189+
{Editor && onEditMode ? (
188190
detactEditor ? (
189191
<>
190192
<Editor
@@ -193,7 +195,7 @@ const TableEditableCell = forwardRef<
193195
onExit={onExitEditMode}
194196
readOnly={readOnly}
195197
/>
196-
<Content value={afterValue} />
198+
<Content value={afterValue} header={header} />
197199
</>
198200
) : (
199201
<Editor
@@ -204,7 +206,7 @@ const TableEditableCell = forwardRef<
204206
/>
205207
)
206208
) : (
207-
<Content value={afterValue} />
209+
<Content value={afterValue} header={header} />
208210
)}
209211
</div>
210212
);

src/renderer/screens/DatabaseScreen/QueryResultViewer/TableCell/createTableCellType.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import { QueryResultHeader } from 'types/SqlResult';
88

99
interface TableCellCustomTypeOptions<T> {
1010
diff: (prev: T, current: T) => boolean;
11-
editor: React.FC<TableEditableEditorProps>;
11+
editor?: React.FC<TableEditableEditorProps>;
1212
content: React.FC<TableEditableContentProps>;
1313
detachEditor?: boolean;
1414
onCopy?: (value: T) => string;
1515
onPaste?: (value: string) => { accept: boolean; value: T };
16+
readOnly?: boolean;
1617
}
1718

1819
export interface TableCellCustomTypeProps<T> {
@@ -52,7 +53,7 @@ export default function createTableCellType<T>(
5253
col={col}
5354
editor={options.editor}
5455
content={options.content}
55-
readOnly={readOnly}
56+
readOnly={options.readOnly || readOnly}
5657
detactEditor={options.detachEditor}
5758
onCopy={options.onCopy}
5859
onPaste={options.onPaste}

src/types/SqlResult.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { TableColumnSchema } from './SqlSchema';
22

33
export interface QueryResultHeaderType {
4-
type: 'string' | 'number' | 'json' | 'decimal';
4+
type: 'string' | 'number' | 'json' | 'decimal' | 'other';
55
}
66

77
export interface QueryResultHeader {

0 commit comments

Comments
 (0)