Fixed the following issues for the new query tool:

1) Failed to fetch query history error sometimes.
 2) In copy paste row, if a copied row has [null], then those are pasted as an empty string.
 3) When Data output is empty, show an empty grid.
 4) Schema diff generates a script button resulting in an empty window. Fixes #7306.
 5) Detach the DataOutput panel > Try editing text cell > Text editor is hidden behind the data output panel

refs #6131
This commit is contained in:
Aditya Toshniwal 2022-04-25 18:11:39 +05:30 committed by Akshay Joshi
parent c5ca394cec
commit 25b89f7624
14 changed files with 52 additions and 28 deletions

View File

@ -734,12 +734,12 @@ function SchemaDialogView({
onClose={onErrClose} />
</Box>
{showFooter && <Box className={classes.footer}>
{useMemo(()=><Box>
{useMemo(()=>((!props.disableSqlHelp || !props.disableDialogHelp) && <Box>
<PgIconButton data-test="sql-help" onClick={()=>props.onHelp(true, isNew)} icon={<InfoIcon />}
disabled={props.disableSqlHelp} className={classes.buttonMargin} title="SQL help for this object type."/>
<PgIconButton data-test="dialog-help" onClick={()=>props.onHelp(false, isNew)} icon={<HelpIcon />} title="Help for this dialog."
disabled={props.disableDialogHelp}/>
</Box>, [])}
</Box>), [])}
<Box marginLeft="auto">
<DefaultButton data-test="Close" onClick={props.onClose} startIcon={<CloseIcon />} className={classes.buttonMargin}>
{gettext('Close')}

View File

@ -40,7 +40,6 @@ const useStyles = makeStyles((theme)=>({
border: 'none',
'&.dragging': {
opacity: 0.6,
pointerEvents: 'visible',
},
'& .dock': {
borderRadius: 'inherit',
@ -49,6 +48,7 @@ const useStyles = makeStyles((theme)=>({
borderRadius: theme.shape.borderRadius,
'&.dock-panel.dragging': {
opacity: 1,
pointerEvents: 'visible',
},
'& .dock-ink-bar': {
height: '0px',

View File

@ -211,5 +211,6 @@
background-color: $sql-editor-disable-bg !important;
}
.sql-editor-mark {
border-bottom: 2px dotted red;
}

View File

@ -479,6 +479,7 @@ def initialize_erd(trans_id, sgid, sid, did):
return make_json_response(
data={
'connId': str(trans_id),
'database': conn.db,
'serverVersion': conn.manager.version,
}
)

View File

@ -81,6 +81,7 @@ export default class BodyWidget extends React.Component {
table_dialog_open: true,
oto_dialog_open: true,
otm_dialog_open: true,
database: null,
};
this.diagram = new ERDCore();
/* Flag for checking if user has opted for save before close */
@ -575,6 +576,7 @@ export default class BodyWidget extends React.Component {
sid: this.props.params.sid,
did: this.props.params.did,
stype: this.props.params.server_type,
database: this.state.database,
};
let sqlId = `erd${this.props.params.trans_id}`;
@ -764,6 +766,7 @@ export default class BodyWidget extends React.Component {
this.setState({
conn_status: CONNECT_STATUS.CONNECTED,
server_version: response.data.data.serverVersion,
database: response.data.data.database,
});
return true;
} catch (error) {

View File

@ -111,7 +111,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
const modal = useModal();
/* Connection status poller */
let pollTime = qtState.preferences.sqleditor.connection_status_fetch_time > 0 ?
let pollTime = qtState.preferences.sqleditor.connection_status_fetch_time > 0 && !qtState.obtaining_conn ?
qtState.preferences.sqleditor.connection_status_fetch_time*1000 : -1;
/* No need to poll when the query is executing. Query poller will the txn status */
if(qtState.connection_status === CONNECTION_STATUS.TRANSACTION_STATUS_ACTIVE && qtState.connected) {

View File

@ -47,6 +47,8 @@ export default class CopyData {
}
clipboard.copyToClipboard(csvRows.join('\n'));
localStorage.setItem('copied-with-headers', withHeaders);
/* Push actual row to storage, can be used to identify null columns */
localStorage.setItem('copied-rows', JSON.stringify(rows));
}
escape(iStr) {

View File

@ -22,8 +22,7 @@ import Notifier from '../../../../../../static/js/helpers/Notifier';
const useStyles = makeStyles((theme)=>({
textEditor: {
position: 'absolute',
zIndex: 1050,
zIndex: 1080,
backgroundColor: theme.palette.background.default,
padding: '0.25rem',
fontSize: '12px',
@ -41,7 +40,7 @@ const useStyles = makeStyles((theme)=>({
},
jsonEditor: {
position: 'absolute',
zIndex: 1050,
zIndex: 1080,
backgroundColor: theme.palette.background.default,
...theme.mixins.panelBorder,
padding: '0.25rem',

View File

@ -335,12 +335,8 @@ export default function QueryToolDataGrid({columns, rows, totalRowCount, dataCha
};
useEffect(()=>{
if(columns.length > 0 || rows.length > 0) {
let initCols = initialiseColumns(columns, rows, totalRowCount, columnWidthBy);
setColumns(formatColumns(initCols, dataChangeStore, selectedColumns, onSelectedColumnsChangeWrapped, props.rowKeyGetter, classes));
} else {
setColumns([], [], 0);
}
let initCols = initialiseColumns(columns, rows, totalRowCount, columnWidthBy);
setColumns(formatColumns(initCols, dataChangeStore, selectedColumns, onSelectedColumnsChangeWrapped, props.rowKeyGetter, classes));
}, [columns, rowsResetKey]);
useEffect(()=>{

View File

@ -166,9 +166,11 @@ export default function Query() {
const layoutEvenBus = useContext(LayoutEventsContext);
const lastSavedText = React.useRef('');
const markedLine = React.useRef(0);
const marker = React.useRef();
const removeHighlightError = (cmObj)=>{
// Remove already existing marker
marker.current?.clear();
cmObj.removeLineClass(markedLine.current, 'wrap', 'CodeMirror-activeline-background');
markedLine.current = 0;
};
@ -218,7 +220,7 @@ export default function Query() {
endMarker = errorLine.length;
// Mark the error text
cmObj.markText({
marker.current = cmObj.markText({
line: errorLineNo,
ch: startMarker,
}, {

View File

@ -14,7 +14,7 @@ import _ from 'lodash';
import clsx from 'clsx';
import { Box, Grid, List, ListItem, ListSubheader } from '@material-ui/core';
import url_for from 'sources/url_for';
import { QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
import { QueryToolConnectionContext, QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
import moment from 'moment';
import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
import AssessmentRoundedIcon from '@material-ui/icons/AssessmentRounded';
@ -86,6 +86,9 @@ const useStyles = makeStyles((theme)=>({
},
removeBtnMargin: {
marginLeft: '0.25rem',
},
queryMargin: {
marginTop: '12px',
}
}));
@ -337,12 +340,13 @@ function QueryHistoryDetails({entry}) {
<DefaultButton size="xs" className={classes.copyBtn} onClick={onCopyToEditor}>{gettext('Copy to Query Editor')}</DefaultButton>
<CodeMirror
value={entry.query}
readonly={true}
options={{
foldGutter: false,
lineNumbers: false,
gutters: [],
readOnly: true,
}}
className={classes.queryMargin}
/>
</Box>
<Box marginTop="0.5rem">
@ -361,6 +365,7 @@ QueryHistoryDetails.propTypes = {
export function QueryHistory() {
const qhu = React.useRef(new QueryHistoryUtils());
const queryToolCtx = React.useContext(QueryToolContext);
const queryToolConnCtx = React.useContext(QueryToolConnectionContext);
const classes = useStyles();
const eventBus = React.useContext(QueryToolEventsContext);
const [selectedItemKey, setSelectedItemKey] = React.useState(1);
@ -371,11 +376,16 @@ export function QueryHistory() {
const layoutEvenBus = React.useContext(LayoutEventsContext);
const listRef = React.useRef();
React.useEffect(async ()=>{
React.useEffect(()=>{
layoutEvenBus.registerListener(LAYOUT_EVENTS.ACTIVE, (currentTabId)=>{
currentTabId == PANELS.HISTORY && listRef.current?.focus();
});
}, []);
React.useEffect(async ()=>{
if(!queryToolConnCtx.connected) {
return;
}
setLoaderText(gettext('Fetching history...'));
try {
let {data: respData} = await queryToolCtx.api.get(url_for('sqleditor.get_query_history', {
@ -402,7 +412,7 @@ export function QueryHistory() {
listRef.current?.focus();
eventBus.registerListener(QUERY_TOOL_EVENTS.PUSH_HISTORY, pushHistory);
return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.PUSH_HISTORY, pushHistory);
}, []);
}, [queryToolConnCtx.connected]);
const onRemove = async ()=>{
setLoaderText(gettext('Removing history entry...'));

View File

@ -547,10 +547,12 @@ export class ResultSetUtils {
return columns;
}
processClipboardVal(columnVal, col) {
processClipboardVal(columnVal, col, rawCopiedVal) {
if(columnVal === '') {
if(col.has_default_val) {
columnVal = undefined;
} else if(rawCopiedVal === null) {
columnVal = null;
}
}
if(col.cell === 'boolean') {
@ -570,14 +572,19 @@ export class ResultSetUtils {
if(!_.isArray(result) || !_.size(result)) {
return retVal;
}
for(const rec of result) {
let copiedRowsObjects = [];
try {
/* If the raw row objects are available, use to them identify null values */
copiedRowsObjects = JSON.parse(localStorage.getItem('copied-rows'));
} catch {/* Suppress the error */}
for(const [recIdx, rec] of result?.entries()) {
// Convert 2darray to dict.
let rowObj = {};
for(const col of columns) {
let columnVal = rec[col.pos];
/* If the source is clipboard, then it needs some extra handling */
if(fromClipboard) {
columnVal = this.processClipboardVal(columnVal, col);
columnVal = this.processClipboardVal(columnVal, col, copiedRowsObjects[recIdx]?.[col.key]);
}
rowObj[col.key] = columnVal;
}
@ -605,15 +612,16 @@ export class ResultSetUtils {
}
queryFinished(httpMessage, onResultsAvailable, onExplain) {
let msg;
let endTime = new Date();
this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_END, true);
this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_CONNECTION_STATUS, httpMessage.data.data.transaction_status);
this.eventBus.fireEvent(QUERY_TOOL_EVENTS.TASK_END, gettext('Query complete'), endTime);
this.setEndTime(endTime);
msg = gettext('Query returned successfully in %s.', this.queryRunTime());
let msg = gettext('Query returned successfully in %s.', this.queryRunTime());
if(this.hasResultsToDisplay(httpMessage.data.data)) {
msg = gettext('Successfully run. Total query runtime: %s.', this.queryRunTime())
+ '\n' + gettext('%s rows affected.', httpMessage.data.data?.rows_affected);
if(!_.isNull(httpMessage.data.data.additional_messages)){
msg = httpMessage.data.data.additional_messages + '\n' + msg;
}
@ -1194,10 +1202,10 @@ export function ResultSet() {
<Box className={classes.root} ref={containerRef} tabIndex="0">
<Loader message={loaderText} />
<Loader message={isLoadingMore ? gettext('Loading more rows...') : null} style={{top: 'unset', right: 'unset', padding: '0.5rem 1rem'}}/>
{(columns.length == 0 && rows.length == 0) &&
{!queryData &&
<EmptyPanelMessage text={gettext('No data output. Execute a query to get output.')}/>
}
{(columns.length != 0 || rows.length != 0) && <>
{queryData && <>
<ResultSetToolbar containerRef={containerRef} canEdit={queryData.can_edit}/>
<Box flexGrow="1" minHeight="0">
<QueryToolDataGrid

View File

@ -85,6 +85,7 @@ export function generateScript(parentData, queryToolMod) {
+`&sid=${parentData.sid}`
+`&server_type=${parentData.stype}`
+`&did=${parentData.did}`
+`&database_name=${parentData.database}`
+`&sql_id=${parentData.sql_id}`;
launchQueryTool(queryToolMod, transId, url_endpoint, queryToolTitle, '');
@ -102,6 +103,7 @@ export function showERDSqlTool(parentData, erdSqlId, queryToolTitle, queryToolMo
},
database: {
_id: parentData.did,
label: parentData.database,
},
};

View File

@ -16,7 +16,7 @@ const pgAdmin = pgWindow.pgAdmin;
export function getDatabaseLabel(parentData) {
return parentData.database ? parentData.database.label
: parentData.server.db;
: parentData.server?.db;
}
function isServerInformationAvailable(parentData) {