///////////////////////////////////////////////////////////// // // pgAdmin 4 - PostgreSQL Tools // // Copyright (C) 2013 - 2022, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // ////////////////////////////////////////////////////////////// import React, {useCallback, useRef, useMemo, useState, useEffect} from 'react'; import _ from 'lodash'; import Layout, { LayoutHelper } from '../../../../../static/js/helpers/Layout'; import EventBus from '../../../../../static/js/helpers/EventBus'; import Query from './sections/Query'; import { ConnectionBar } from './sections/ConnectionBar'; import { ResultSet } from './sections/ResultSet'; import { StatusBar } from './sections/StatusBar'; import { MainToolBar } from './sections/MainToolBar'; import { Messages } from './sections/Messages'; import getApiInstance, {parseApiError} from '../../../../../static/js/api_instance'; import url_for from 'sources/url_for'; import { PANELS, QUERY_TOOL_EVENTS, CONNECTION_STATUS } from './QueryToolConstants'; import { useInterval } from '../../../../../static/js/custom_hooks'; import { Box } from '@material-ui/core'; import { getDatabaseLabel, getTitle, setQueryToolDockerTitle } from '../sqleditor_title'; import gettext from 'sources/gettext'; import NewConnectionDialog from './dialogs/NewConnectionDialog'; import { evalFunc } from '../../../../../static/js/utils'; import { Notifications } from './sections/Notifications'; import MacrosDialog from './dialogs/MacrosDialog'; import Notifier from '../../../../../static/js/helpers/Notifier'; import FilterDialog from './dialogs/FilterDialog'; import { QueryHistory } from './sections/QueryHistory'; import * as showQueryTool from '../show_query_tool'; import * as commonUtils from 'sources/utils'; import * as Kerberos from 'pgadmin.authenticate.kerberos'; import PropTypes from 'prop-types'; import { retrieveNodeName } from '../show_view_data'; import 'wcdocker'; import { useModal } from '../../../../../static/js/helpers/ModalProvider'; import ConnectServerContent from '../../../../../static/js/Dialogs/ConnectServerContent'; export const QueryToolContext = React.createContext(); export const QueryToolConnectionContext = React.createContext(); export const QueryToolEventsContext = React.createContext(); function fetchConnectionStatus(api, transId) { return api.get(url_for('sqleditor.connection_status', {trans_id: transId})); } function initConnection(api, params, passdata) { return api.post(url_for('NODE-server.connect_id', params), passdata); } function setPanelTitle(panel, title, qtState, dirty=false) { if(qtState.current_file) { title = qtState.current_file.split('\\').pop().split('/').pop(); } else if (!qtState.is_new_tab) { if(!title) { title = panel.$titleText?.[0].textContent; if(panel.is_dirty_editor) { // remove asterisk title = title.slice(0, -1); } } } else { title = qtState.params.title; } title = title + (dirty ? '*': ''); if (qtState.is_new_tab) { window.document.title = title; } else { panel.is_dirty_editor = dirty; setQueryToolDockerTitle(panel, true, title, qtState.current_file ? true : false); } } function onBeforeUnload(e) { e.preventDefault(); e.returnValue = 'prevent'; } export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedNodeInfo, panel, eventBusObj}) { const containerRef = React.useRef(null); const [qtState, _setQtState] = useState({ preferences: { browser: {}, sqleditor: {}, graphs: {}, misc: {}, }, is_new_tab: window.location == window.parent?.location, current_file: null, obtaining_conn: true, connected: false, connection_status: null, connection_status_msg: '', params: { ...params, is_query_tool: params.is_query_tool == 'true' ? true : false, node_name: retrieveNodeName(selectedNodeInfo), }, connection_list: [{ sgid: params.sgid, sid: params.sid, did: params.did, user: _.unescape(params.user), role: _.unescape(params.role), title: _.unescape(params.title), fgcolor: params.fgcolor, bgcolor: params.bgcolor, conn_title: getTitle( pgAdmin, null, selectedNodeInfo, true, _.unescape(params.server_name), _.unescape(params.database_name) || getDatabaseLabel(selectedNodeInfo), _.unescape(params.role) || _.unescape(params.user), params.is_query_tool == 'true' ? true : false), server_name: _.unescape(params.server_name), database_name: _.unescape(params.database_name) || getDatabaseLabel(selectedNodeInfo), is_selected: true, }], }); const setQtState = (state)=>{ _setQtState((prev)=>({...prev,...evalFunc(null, state, prev)})); }; const isDirtyRef = useRef(false); // usefull when conn change. const eventBus = useRef(eventBusObj || (new EventBus())); const docker = useRef(null); const api = useMemo(()=>getApiInstance(), []); const modal = useModal(); /* Connection status poller */ let pollTime = qtState.preferences.sqleditor.connection_status_fetch_time > 0 && !qtState.obtaining_conn && qtState.preferences?.sqleditor?.connection_status ? 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) { pollTime = -1; } useInterval(async ()=>{ try { let {data: respData} = await fetchConnectionStatus(api, qtState.params.trans_id); if(respData.data) { setQtState({ connected: true, connection_status: respData.data.status, }); } else { setQtState({ connected: false, connection_status: null, connection_status_msg: gettext('An unexpected error occurred - ensure you are logged into the application.') }); } if(respData.data.notifies) { eventBus.current.fireEvent(QUERY_TOOL_EVENTS.PUSH_NOTICE, respData.data.notifies); } } catch (error) { console.error(error); setQtState({ connected: false, connection_status: null, connection_status_msg: parseApiError(error), }); } }, pollTime); let defaultLayout = { dockbox: { mode: 'vertical', children: [ { mode: 'horizontal', children: [ { tabs: [ LayoutHelper.getPanel({id: PANELS.QUERY, title: gettext('Query'), content: }), LayoutHelper.getPanel({id: PANELS.HISTORY, title: gettext('Query History'), content: , cached: undefined}), ], }, { size: 75, tabs: [ LayoutHelper.getPanel({ id: PANELS.SCRATCH, title: gettext('Scratch Pad'), closable: true, content: