/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * Copyright (C) 2011-2012 Eugene Fradkin (eugene.fradkin@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jkiss.dbeaver.ui.controls.querylog; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jface.action.*; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.text.source.ISharedTextColors; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.*; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.IWorkbenchPartSite; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.core.CoreCommands; import org.jkiss.dbeaver.core.CoreMessages; import org.jkiss.dbeaver.core.DBeaverCore; import org.jkiss.dbeaver.core.DBeaverUI; import org.jkiss.dbeaver.model.DBConstants; import org.jkiss.dbeaver.model.DBPDataSourceContainer; import org.jkiss.dbeaver.model.exec.DBCExecutionContext; import org.jkiss.dbeaver.model.preferences.DBPPreferenceListener; import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore; import org.jkiss.dbeaver.model.qm.*; import org.jkiss.dbeaver.model.qm.meta.*; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.sql.SQLConstants; import org.jkiss.dbeaver.model.sql.SQLDialect; import org.jkiss.dbeaver.registry.DataSourceRegistry; import org.jkiss.dbeaver.runtime.qm.DefaultEventFilter; import org.jkiss.dbeaver.ui.*; import org.jkiss.dbeaver.ui.controls.TableColumnSortListener; import org.jkiss.dbeaver.ui.dialogs.sql.BaseSQLDialog; import org.jkiss.dbeaver.ui.editors.sql.handlers.OpenHandler; import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.LongKeyMap; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.Locale; /** * QueryLogViewer */ public class QueryLogViewer extends Viewer implements QMMetaListener, DBPPreferenceListener { private static final Log log = Log.getLog(QueryLogViewer.class); private static final String QUERY_LOG_CONTROL_ID = "org.jkiss.dbeaver.ui.qm.log"; //$NON-NLS-1$ private static final String VIEWER_ID = "DBeaver.QM.LoigViewer"; private static final int MIN_ENTRIES_PER_PAGE = 1; public static final RGB COLOR_LIGHT_GREEN = new RGB(0xE4, 0xFF, 0xB5); public static final RGB COLOR_LIGHT_RED = new RGB(0xFF, 0x63, 0x47); public static final RGB COLOR_LIGHT_YELLOW = new RGB(0xFF, 0xE4, 0xB5); public static final RGB COLOR_BLACK = new RGB(0x00, 0x00, 0x00); private static abstract class LogColumn { private final String id; private final String title; private final String toolTip; private final int widthHint; private LogColumn(String id, String title, String toolTip, int widthHint) { this.id = id; this.title = title; this.toolTip = toolTip; this.widthHint = widthHint; } abstract String getText(QMMetaEvent event); String getToolTipText(QMMetaEvent event) { return getText(event); } } private static class ColumnDescriptor { LogColumn logColumn; TableColumn tableColumn; ColumnDescriptor(LogColumn logColumn, TableColumn tableColumn) { this.logColumn = logColumn; this.tableColumn = tableColumn; } } private LogColumn COLUMN_TIME = new LogColumn("time", CoreMessages.controls_querylog_column_time_name, CoreMessages.controls_querylog_column_time_tooltip, 80) { private final DateFormat timeFormat = new SimpleDateFormat(DBConstants.DEFAULT_TIME_FORMAT, Locale.getDefault()); //$NON-NLS-1$ private final DateFormat timestampFormat = new SimpleDateFormat(DBConstants.DEFAULT_TIMESTAMP_FORMAT, Locale.getDefault()); //$NON-NLS-1$ @Override String getText(QMMetaEvent event) { return timeFormat.format(event.getObject().getOpenTime()); } String getToolTipText(QMMetaEvent event) { return timestampFormat.format(event.getObject().getOpenTime()); } }; private static LogColumn COLUMN_TYPE = new LogColumn("type", CoreMessages.controls_querylog_column_type_name, CoreMessages.controls_querylog_column_type_tooltip, 100) { @Override String getText(QMMetaEvent event) { return getObjectType(event.getObject()); } }; private static LogColumn COLUMN_TEXT = new LogColumn("text", CoreMessages.controls_querylog_column_text_name, CoreMessages.controls_querylog_column_text_tooltip, 400) { @Override String getText(QMMetaEvent event) { QMMObject object = event.getObject(); if (object instanceof QMMStatementExecuteInfo) { QMMStatementExecuteInfo statement = (QMMStatementExecuteInfo) object; //return SQLUtils.stripTransformations(statement.getQueryString()); return CommonUtils.truncateString(statement.getQueryString(), 4000); } else if (object instanceof QMMTransactionInfo) { if (((QMMTransactionInfo) object).isCommitted()) { return CoreMessages.controls_querylog_commit; } else { return CoreMessages.controls_querylog_rollback; } } else if (object instanceof QMMTransactionSavepointInfo) { if (((QMMTransactionSavepointInfo) object).isCommitted()) { return CoreMessages.controls_querylog_commit; } else { return CoreMessages.controls_querylog_rollback; } } else if (object instanceof QMMSessionInfo) { String containerName = ((QMMSessionInfo) object).getContainerName(); switch (event.getAction()) { case BEGIN: return CoreMessages.controls_querylog_connected_to + containerName + "\""; case END: return CoreMessages.controls_querylog_disconnected_from + containerName + "\""; default: return "?"; } } return ""; //$NON-NLS-1$ } }; private static LogColumn COLUMN_DURATION = new LogColumn("duration", CoreMessages.controls_querylog_column_duration_name, CoreMessages.controls_querylog_column_duration_tooltip, 100) { @Override String getText(QMMetaEvent event) { QMMObject object = event.getObject(); if (object instanceof QMMStatementExecuteInfo) { QMMStatementExecuteInfo exec = (QMMStatementExecuteInfo) object; if (exec.isClosed()) { final long execTime = exec.getCloseTime() - exec.getOpenTime(); final long fetchTime = exec.isFetching() ? 0 : exec.getFetchEndTime() - exec.getFetchBeginTime(); return String.valueOf(execTime + fetchTime) + CoreMessages.controls_querylog__ms; } else { return ""; //$NON-NLS-1$ } } else if (object instanceof QMMTransactionInfo) { QMMTransactionInfo txn = (QMMTransactionInfo) object; if (txn.isClosed()) { return formatMinutes(txn.getCloseTime() - txn.getOpenTime()); } else { return ""; //$NON-NLS-1$ } } else if (object instanceof QMMTransactionSavepointInfo) { QMMTransactionSavepointInfo sp = (QMMTransactionSavepointInfo) object; if (sp.isClosed()) { return formatMinutes(sp.getCloseTime() - sp.getOpenTime()); } else { return ""; //$NON-NLS-1$ } } else if (object instanceof QMMSessionInfo) { QMMSessionInfo session = (QMMSessionInfo) object; if (session.isClosed()) { return formatMinutes(session.getCloseTime() - session.getOpenTime()); } else { return ""; //$NON-NLS-1$ } } return ""; //$NON-NLS-1$ } }; private static LogColumn COLUMN_ROWS = new LogColumn("rows", CoreMessages.controls_querylog_column_rows_name, CoreMessages.controls_querylog_column_rows_tooltip, 120) { @Override String getText(QMMetaEvent event) { QMMObject object = event.getObject(); if (object instanceof QMMStatementExecuteInfo) { QMMStatementExecuteInfo exec = (QMMStatementExecuteInfo) object; if (exec.isClosed() && !exec.isFetching()) { long rowCount = exec.getRowCount(); if (rowCount < 0) { return ""; //$NON-NLS-1$ } else { return String.valueOf(rowCount); } } } return ""; //$NON-NLS-1$ } }; private static LogColumn COLUMN_RESULT = new LogColumn("result", CoreMessages.controls_querylog_column_result_name, CoreMessages.controls_querylog_column_result_tooltip, 120) { @Override String getText(QMMetaEvent event) { if (event.getObject() instanceof QMMStatementExecuteInfo) { QMMStatementExecuteInfo exec = (QMMStatementExecuteInfo) event.getObject(); if (exec.isClosed()) { if (exec.hasError()) { if (exec.getErrorCode() == 0) { return exec.getErrorMessage(); } else if (exec.getErrorMessage() == null) { return CoreMessages.controls_querylog_error + exec.getErrorCode() + "]"; } else { return "[" + exec.getErrorCode() + "] " + exec.getErrorMessage(); } } else { return CoreMessages.controls_querylog_success; } } } return ""; //$NON-NLS-1$ } }; private static LogColumn COLUMN_DATA_SOURCE = new LogColumn("datasource", CoreMessages.controls_querylog_column_connection_name, CoreMessages.controls_querylog_column_connection_tooltip, 150) { @Override String getText(QMMetaEvent event) { QMMObject object = event.getObject(); String containerName = null; if (object instanceof QMMSessionInfo) { containerName = ((QMMSessionInfo) object).getContainerName(); } else if (object instanceof QMMTransactionInfo) { containerName = ((QMMTransactionInfo) object).getSession().getContainerName(); } else if (object instanceof QMMTransactionSavepointInfo) { containerName = ((QMMTransactionSavepointInfo) object).getTransaction().getSession().getContainerName(); } else if (object instanceof QMMStatementInfo) { containerName = ((QMMStatementInfo) object).getSession().getContainerName(); } else if (object instanceof QMMStatementExecuteInfo) { containerName = ((QMMStatementExecuteInfo) object).getStatement().getSession().getContainerName(); } return containerName == null ? "?" : containerName; } }; private static LogColumn COLUMN_CONTEXT = new LogColumn("context", CoreMessages.controls_querylog_column_context_name, CoreMessages.controls_querylog_column_context_tooltip, 150) { @Override String getText(QMMetaEvent event) { QMMObject object = event.getObject(); String contextName = null; if (object instanceof QMMSessionInfo) { contextName = ((QMMSessionInfo) object).getContextName(); } else if (object instanceof QMMTransactionInfo) { contextName = ((QMMTransactionInfo) object).getSession().getContextName(); } else if (object instanceof QMMTransactionSavepointInfo) { contextName = ((QMMTransactionSavepointInfo) object).getTransaction().getSession().getContextName(); } else if (object instanceof QMMStatementInfo) { contextName = ((QMMStatementInfo) object).getSession().getContextName(); } else if (object instanceof QMMStatementExecuteInfo) { contextName = ((QMMStatementExecuteInfo) object).getStatement().getSession().getContextName(); } if (contextName == null) { return "?"; } return contextName; } }; private LogColumn[] ALL_COLUMNS = new LogColumn[] { COLUMN_TIME, COLUMN_TYPE, COLUMN_TEXT, COLUMN_DURATION, COLUMN_ROWS, COLUMN_RESULT, COLUMN_DATA_SOURCE, COLUMN_CONTEXT, }; private final IWorkbenchPartSite site; private Table logTable; private java.util.List<ColumnDescriptor> columns = new ArrayList<>(); private LongKeyMap<TableItem> objectToItemMap = new LongKeyMap<>(); private QMEventFilter defaultFilter = new DefaultEventFilter(); private QMEventFilter filter; private boolean useDefaultFilter = true; private final Color colorLightGreen; private final Color colorLightRed; private final Color colorLightYellow; private final Color colorGray; private final Font boldFont; private DragSource dndSource; private int entriesPerPage = MIN_ENTRIES_PER_PAGE; public QueryLogViewer(Composite parent, IWorkbenchPartSite site, QMEventFilter filter, boolean showConnection) { super(); this.site = site; // Prepare colors ISharedTextColors sharedColors = DBeaverUI.getSharedTextColors(); colorLightGreen = sharedColors.getColor(COLOR_LIGHT_GREEN); colorLightRed = sharedColors.getColor(COLOR_LIGHT_RED); colorLightYellow = sharedColors.getColor(COLOR_LIGHT_YELLOW); colorGray = sharedColors.getColor(COLOR_BLACK); boldFont = UIUtils.makeBoldFont(parent.getFont()); boolean inDialog = UIUtils.isInDialog(parent); // Create log table logTable = new Table( parent, SWT.MULTI | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL | (inDialog ? SWT.BORDER : SWT.NONE)); logTable.setData(this); logTable.setLinesVisible(true); logTable.setHeaderVisible(true); GridData gd = new GridData(GridData.FILL_BOTH); logTable.setLayoutData(gd); new TableToolTip(logTable) { @Override public String getItemToolTip(TableItem item, int selectedColumn) { LogColumn column = (LogColumn) logTable.getColumn(selectedColumn).getData(); return column.getToolTipText((QMMetaEvent) item.getData()); } }; createColumns(showConnection); { // Register control in focus service (to provide handlers binding) UIUtils.addFocusTracker(site, QUERY_LOG_CONTROL_ID, logTable); logTable.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { // Unregister from focus service UIUtils.removeFocusTracker(QueryLogViewer.this.site, logTable); dispose(); } }); } createContextMenu(); addDragAndDropSupport(); logTable.addSelectionListener(new SelectionAdapter() { @Override public void widgetDefaultSelected(SelectionEvent e) { //TableItem item = (TableItem)e.item; showEventDetails((QMMetaEvent) e.item.getData()); } }); this.filter = filter; reloadEvents(); QMUtils.registerMetaListener(this); DBeaverCore.getGlobalPreferenceStore().addPropertyChangeListener(this); } public void setFilter(QMEventFilter filter) { this.filter = filter; } public void setUseDefaultFilter(boolean useDefaultFilter) { this.useDefaultFilter = useDefaultFilter; } private void showEventDetails(QMMetaEvent event) { EventViewDialog dialog = new EventViewDialog(event); dialog.open(); } private void createColumns(boolean showConnection) { for (TableColumn tableColumn : logTable.getColumns()) { tableColumn.dispose(); } columns.clear(); final IDialogSettings dialogSettings = UIUtils.getDialogSettings(VIEWER_ID); int colIndex = 0; for (final LogColumn logColumn : ALL_COLUMNS) { if (!showConnection && (logColumn == COLUMN_DATA_SOURCE || logColumn == COLUMN_CONTEXT)) { continue; } final TableColumn tableColumn = UIUtils.createTableColumn(logTable, SWT.NONE, logColumn.title); tableColumn.setData(logColumn); final String colWidth = dialogSettings.get("column-" + logColumn.id); if (colWidth != null) { tableColumn.setWidth(Integer.parseInt(colWidth)); } else { tableColumn.setWidth(logColumn.widthHint); } tableColumn.setToolTipText(logColumn.toolTip); final ColumnDescriptor cd = new ColumnDescriptor(logColumn, tableColumn); columns.add(cd); tableColumn.addListener(SWT.Selection, new TableColumnSortListener(logTable, colIndex)); tableColumn.addListener(SWT.Resize, new Listener() { @Override public void handleEvent(Event event) { final int width = tableColumn.getWidth(); dialogSettings.put("column-" + logColumn.id, String.valueOf(width)); } }); colIndex++; } } private void dispose() { DBeaverCore.getGlobalPreferenceStore().removePropertyChangeListener(this); QMUtils.unregisterMetaListener(this); UIUtils.dispose(dndSource); UIUtils.dispose(logTable); UIUtils.dispose(boldFont); } @Override public Table getControl() { return logTable; } @Override public Object getInput() { return null; } @Override public void setInput(Object input) { } @Override public IStructuredSelection getSelection() { TableItem[] items = logTable.getSelection(); QMMetaEvent[] data = new QMMetaEvent[items.length]; for (int i = 0, itemsLength = items.length; i < itemsLength; i++) { data[i] = (QMMetaEvent)items[i].getData(); } return new StructuredSelection(data); } @Override public void setSelection(ISelection selection, boolean reveal) { } @Override public void refresh() { reloadEvents(); } private static String getObjectType(QMMObject object) { if (object instanceof QMMSessionInfo) { return CoreMessages.model_navigator_Connection; } else if (object instanceof QMMStatementInfo || object instanceof QMMStatementExecuteInfo) { QMMStatementInfo statement; if (object instanceof QMMStatementInfo) { statement = (QMMStatementInfo) object; } else { statement = ((QMMStatementExecuteInfo)object).getStatement(); } return "SQL" + (statement == null ? "" : " / " + statement.getPurpose().name()); //$NON-NLS-1$ // } else if (object instanceof QMMStatementScripInfo) { // return CoreMessages.controls_querylog_script; } else if (object instanceof QMMTransactionInfo) { return CoreMessages.controls_querylog_transaction; } else if (object instanceof QMMTransactionSavepointInfo) { return CoreMessages.controls_querylog_savepoint; } return ""; //$NON-NLS-1$ } private Font getObjectFont(QMMetaEvent event) { if (event.getObject() instanceof QMMStatementExecuteInfo) { QMMStatementExecuteInfo exec = (QMMStatementExecuteInfo) event.getObject(); if (!exec.isClosed() || exec.isFetching()) { return boldFont; } } return null; } private Color getObjectForeground(QMMetaEvent event) { if (getObjectBackground(event) != null) { return colorGray; } /* if (event.getObject() instanceof QMMStatementExecuteInfo) { QMMStatementExecuteInfo exec = (QMMStatementExecuteInfo) event.getObject(); if (exec.getStatement().getPurpose().isUser()) { return null; } else { return colorGray; } } */ return null; } private Color getObjectBackground(QMMetaEvent event) { if (event.getObject() instanceof QMMStatementExecuteInfo) { QMMStatementExecuteInfo exec = (QMMStatementExecuteInfo) event.getObject(); if (exec.hasError()) { return colorLightRed; } QMMTransactionSavepointInfo savepoint = exec.getSavepoint(); if (savepoint == null) { return colorLightGreen; } else if (savepoint.isClosed()) { return savepoint.isCommitted() ? colorLightGreen : colorLightYellow; } else { return null; } } else if (event.getObject() instanceof QMMTransactionInfo || event.getObject() instanceof QMMTransactionSavepointInfo) { QMMTransactionSavepointInfo savepoint; if (event.getObject() instanceof QMMTransactionInfo) { savepoint = ((QMMTransactionInfo) event.getObject()).getCurrentSavepoint(); } else { savepoint = (QMMTransactionSavepointInfo) event.getObject(); } return savepoint.isCommitted() ? colorLightGreen : colorLightYellow; } return null; } private void reloadEvents() { DBPPreferenceStore store = DBeaverCore.getGlobalPreferenceStore(); this.entriesPerPage = Math.max(MIN_ENTRIES_PER_PAGE, store.getInt(QMConstants.PROP_ENTRIES_PER_PAGE)); this.defaultFilter = new DefaultEventFilter(); clearLog(); updateMetaInfo(QMUtils.getPastMetaEvents()); } @Override public void metaInfoChanged(@NotNull final java.util.List<QMMetaEvent> events) { if (DBeaverCore.isClosing()) { return; } // Run in UI thread DBeaverUI.syncExec(new Runnable() { @Override public void run() { updateMetaInfo(events); } }); } private synchronized void updateMetaInfo(final java.util.List<QMMetaEvent> events) { if (logTable.isDisposed()) { return; } logTable.setRedraw(false); try { // Add events in reverse order int itemIndex = 0; for (int i = events.size(); i > 0; i--) { if (useDefaultFilter && itemIndex >= entriesPerPage) { // Do not add remaining (older) events - they don't fit page anyway break; } QMMetaEvent event = events.get(i - 1); if ((filter != null && !filter.accept(event)) || (useDefaultFilter && !defaultFilter.accept(event))) { continue; } QMMObject object = event.getObject(); if (object instanceof QMMStatementExecuteInfo) { itemIndex = createOrUpdateItem(event, itemIndex); } else if (object instanceof QMMTransactionInfo || object instanceof QMMTransactionSavepointInfo) { itemIndex = createOrUpdateItem(event, itemIndex); // Update all dependent statements if (object instanceof QMMTransactionInfo) { for (QMMTransactionSavepointInfo savepoint = ((QMMTransactionInfo) object).getCurrentSavepoint(); savepoint != null; savepoint = savepoint.getPrevious()) { updateExecutions(event, savepoint); } } else { updateExecutions(event, (QMMTransactionSavepointInfo) object); } } else if (object instanceof QMMSessionInfo) { QMMetaEvent.Action action = event.getAction(); if (action == QMMetaEvent.Action.BEGIN || action == QMMetaEvent.Action.END) { TableItem item = new TableItem(logTable, SWT.NONE, itemIndex++); updateItem(event, item); } } } int itemCount = logTable.getItemCount(); if (itemCount > entriesPerPage) { int[] indexes = new int[itemCount - entriesPerPage]; for (int i = 0; i < itemCount - entriesPerPage; i++) { indexes[i] = entriesPerPage + i; TableItem tableItem = logTable.getItem(entriesPerPage + i); if (tableItem != null && tableItem.getData() instanceof QMMObject) { objectToItemMap.remove(((QMMObject) tableItem.getData()).getObjectId()); } } logTable.remove(indexes); } } catch (Exception e) { log.error("Error updating Query Log", e); } finally { if (!logTable.isDisposed()) { logTable.setRedraw(true); } } } private void updateExecutions(QMMetaEvent event, QMMTransactionSavepointInfo savepoint) { for (Iterator<QMMStatementExecuteInfo> i = savepoint.getExecutions(); i.hasNext(); ) { QMMStatementExecuteInfo exec = i.next(); if (exec.hasError()) { // Do not update color of failed executions (it has to be red) continue; } TableItem item = objectToItemMap.get(exec.getObjectId()); if (item != null && !item.isDisposed()) { item.setFont(getObjectFont(event)); item.setForeground(getObjectForeground(event)); item.setBackground(getObjectBackground(event)); } } } private int createOrUpdateItem(QMMetaEvent event, int itemIndex) { TableItem item = objectToItemMap.get(event.getObject().getObjectId()); if (item == null) { item = new TableItem(logTable, SWT.NONE, itemIndex++); objectToItemMap.put(event.getObject().getObjectId(), item); } updateItem(event, item); return itemIndex; } private void updateItem(QMMetaEvent event, TableItem item) { item.setData(event); for (int i = 0, columnsSize = columns.size(); i < columnsSize; i++) { ColumnDescriptor cd = columns.get(i); item.setText(i, TextUtils.getSingleLineString(cd.logColumn.getText(event))); } item.setFont(getObjectFont(event)); item.setForeground(getObjectForeground(event)); item.setBackground(getObjectBackground(event)); } private void createContextMenu() { MenuManager menuMgr = new MenuManager(); Menu menu = menuMgr.createContextMenu(logTable); menuMgr.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(IMenuManager manager) { IAction editorAction = new Action("Open in SQL console", DBeaverIcons.getImageDescriptor(UIIcon.SQL_CONSOLE)) { @Override public void run() { openSelectionInEditor(); } }; IAction copyAction = new Action(CoreMessages.controls_querylog_action_copy) { @Override public void run() { copySelectionToClipboard(false); } }; copyAction.setEnabled(logTable.getSelectionCount() > 0); copyAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY); IAction copyAllAction = new Action(CoreMessages.controls_querylog_action_copy_all_fields) { @Override public void run() { copySelectionToClipboard(true); } }; copyAllAction.setEnabled(logTable.getSelectionCount() > 0); copyAllAction.setActionDefinitionId(CoreCommands.CMD_COPY_SPECIAL); IAction selectAllAction = new Action(CoreMessages.controls_querylog_action_select_all) { @Override public void run() { selectAll(); } }; selectAllAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL); IAction clearLogAction = new Action(CoreMessages.controls_querylog_action_clear_log) { @Override public void run() { clearLog(); } }; boolean hasStatements = false; for (TableItem item : logTable.getSelection()) { if (((QMMetaEvent)item.getData()).getObject() instanceof QMMStatementExecuteInfo) { hasStatements = true; break; } } if (hasStatements) { manager.add(editorAction); manager.add(new Separator()); } manager.add(copyAction); manager.add(copyAllAction); manager.add(selectAllAction); manager.add(clearLogAction); //manager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); } }); menuMgr.setRemoveAllWhenShown(true); logTable.setMenu(menu); site.registerContextMenu(menuMgr, this); } private void openSelectionInEditor() { DBPDataSourceContainer dsContainer = null; StringBuilder sql = new StringBuilder(); TableItem[] items = logTable.getSelection(); for (TableItem item : items) { QMMetaEvent event = (QMMetaEvent) item.getData(); QMMObject object = event.getObject(); if (object instanceof QMMStatementExecuteInfo) { QMMStatementExecuteInfo stmtExec = (QMMStatementExecuteInfo) object; if (dsContainer == null) { String containerId = stmtExec.getStatement().getSession().getContainerId(); dsContainer = DataSourceRegistry.findDataSource(containerId); } String queryString = stmtExec.getQueryString(); if (!CommonUtils.isEmptyTrimmed(queryString)) { if (sql.length() > 0) { sql.append("\n"); } queryString = queryString.trim(); sql.append(queryString); if (!queryString.endsWith(SQLConstants.DEFAULT_STATEMENT_DELIMITER)) { sql.append(SQLConstants.DEFAULT_STATEMENT_DELIMITER).append("\n"); } } } } if (sql.length() > 0) { OpenHandler.openSQLConsole( DBeaverUI.getActiveWorkbenchWindow(), dsContainer, "QueryManager", sql.toString() ); } } private void addDragAndDropSupport() { Transfer[] types = new Transfer[] {TextTransfer.getInstance()}; int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK; dndSource = new DragSource(logTable, operations); dndSource.setTransfer(types); dndSource.addDragListener (new DragSourceListener() { @Override public void dragStart(DragSourceEvent event) { } @Override public void dragSetData (DragSourceEvent event) { String tdt = getSelectedText(false); if (!CommonUtils.isEmpty(tdt)) { event.data = tdt; } else { event.data = ""; //$NON-NLS-1$ } } @Override public void dragFinished(DragSourceEvent event) { } }); } public synchronized void clearLog() { logTable.removeAll(); objectToItemMap.clear(); } public void selectAll() { if (!logTable.isDisposed()) { logTable.selectAll(); } } public void copySelectionToClipboard(boolean extraInfo) { String tdt = getSelectedText(extraInfo); if (CommonUtils.isEmpty(tdt)) { return; } if (tdt.length() > 0) { UIUtils.setClipboardContents(logTable.getDisplay(), TextTransfer.getInstance(), tdt); } } private String getSelectedText(boolean extraInfo) { IStructuredSelection selection = getSelection(); if (selection.isEmpty()) { return null; } StringBuilder tdt = new StringBuilder(); for (Iterator<?> i = selection.iterator(); i.hasNext(); ) { QMMetaEvent item = (QMMetaEvent)i.next(); if (tdt.length() > 0) { tdt.append(GeneralUtils.getDefaultLineSeparator()); } if (extraInfo) { for (int i1 = 0, columnsSize = columns.size(); i1 < columnsSize; i1++) { ColumnDescriptor cd = columns.get(i1); String text = cd.logColumn.getText(item); if (i1 > 0) { tdt.append('\t'); } tdt.append(text); } } else { String text = COLUMN_TEXT.getText(item); tdt.append(text); } } return tdt.toString(); } private static String formatMinutes(long ms) { long min = ms / 1000 / 60; long sec = (ms - min * 1000 * 60) / 1000; return NLS.bind(CoreMessages.controls_querylog_format_minutes, String.valueOf(min), String.valueOf(sec)); } private ConfigRefreshJob configRefreshJob = null; @Override public synchronized void preferenceChange(PreferenceChangeEvent event) { if (event.getProperty().startsWith(QMConstants.PROP_PREFIX)) { // Many properties could be changed at once // So here we just schedule single refresh job if (configRefreshJob == null) { configRefreshJob = new ConfigRefreshJob(); configRefreshJob.schedule(250); configRefreshJob.addJobChangeListener(new JobChangeAdapter() { @Override public void done(IJobChangeEvent event) { configRefreshJob = null; } }); } } } private class ConfigRefreshJob extends AbstractUIJob { ConfigRefreshJob() { super(CoreMessages.controls_querylog_job_refresh); } @Override protected IStatus runInUIThread(DBRProgressMonitor monitor) { reloadEvents(); return Status.OK_STATUS; } } private class EventViewDialog extends BaseSQLDialog { private static final String DIALOG_ID = "DBeaver.QM.EventViewDialog";//$NON-NLS-1$ private final QMMetaEvent object; EventViewDialog(QMMetaEvent object) { super(QueryLogViewer.this.getControl().getShell(), QueryLogViewer.this.site, "Event", null); setShellStyle(SWT.SHELL_TRIM); this.object = object; } @Override protected IDialogSettings getDialogBoundsSettings() { return UIUtils.getDialogSettings(DIALOG_ID); } @Override protected void configureShell(Shell shell) { super.configureShell(shell); shell.setText(CoreMessages.controls_querylog_shell_text + COLUMN_TYPE.getText(object)); } @Override protected Composite createDialogArea(Composite parent) { final Composite composite = new Composite(parent, SWT.NONE); composite.setLayoutData(new GridData(GridData.FILL_BOTH)); composite.setLayout(new GridLayout(1, false)); final Composite topFrame = UIUtils.createPlaceholder(composite, 2, 5); topFrame.setLayoutData(new GridData(GridData.FILL_BOTH)); UIUtils.createLabelText(topFrame, CoreMessages.controls_querylog_label_time, COLUMN_TIME.getText(object), SWT.READ_ONLY); UIUtils.createLabelText(topFrame, CoreMessages.controls_querylog_label_type, COLUMN_TYPE.getText(object), SWT.BORDER | SWT.READ_ONLY); final Label messageLabel = UIUtils.createControlLabel(topFrame, CoreMessages.controls_querylog_label_text); messageLabel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); Control msg; if (object.getObject() instanceof QMMStatementExecuteInfo) { msg = createSQLPanel(topFrame); } else { final Text messageText = new Text(topFrame, SWT.BORDER | SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL); messageText.setText(COLUMN_TEXT.getText(object)); msg = messageText; } GridData gd = new GridData(GridData.FILL_BOTH); //gd.heightHint = 40; gd.widthHint = 500; msg.setLayoutData(gd); final Composite bottomFrame = UIUtils.createPlaceholder(composite, 1, 5); bottomFrame.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); final Label resultLabel = UIUtils.createControlLabel(bottomFrame, CoreMessages.controls_querylog_label_result); resultLabel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); final Text resultText = new Text(bottomFrame, SWT.BORDER | SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL | SWT.H_SCROLL); resultText.setText(COLUMN_RESULT.getText(object)); gd = new GridData(GridData.FILL_HORIZONTAL); gd.heightHint = 60; gd.widthHint = 300; resultText.setLayoutData(gd); return composite; } @Override protected void createButtonsForButtonBar(Composite parent) { createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); } @Override protected SQLDialect getSQLDialect() { if (object.getObject() instanceof QMMStatementExecuteInfo) { return ((QMMStatementExecuteInfo) object.getObject()).getStatement().getSession().getSQLDialect(); } return super.getSQLDialect(); } @Override protected DBCExecutionContext getExecutionContext() { return null; } @Override protected String getSQLText() { return COLUMN_TEXT.getText(object); } @Override protected boolean isLabelVisible() { return false; } } }