mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-01-27 16:57:00 -06:00
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:
parent
c5ca394cec
commit
25b89f7624
@ -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')}
|
||||
|
@ -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',
|
||||
|
@ -211,5 +211,6 @@
|
||||
background-color: $sql-editor-disable-bg !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.sql-editor-mark {
|
||||
border-bottom: 2px dotted red;
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
)
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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',
|
||||
|
@ -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(()=>{
|
||||
|
@ -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,
|
||||
}, {
|
||||
|
@ -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...'));
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user