diff --git a/apgdiff/src/main/java/cz/startnet/utils/pgdiff/IProgressReporter.java b/apgdiff/src/main/java/cz/startnet/utils/pgdiff/IProgressReporter.java index aa61d82ce9..8c5d95acaa 100644 --- a/apgdiff/src/main/java/cz/startnet/utils/pgdiff/IProgressReporter.java +++ b/apgdiff/src/main/java/cz/startnet/utils/pgdiff/IProgressReporter.java @@ -1,8 +1,11 @@ package cz.startnet.utils.pgdiff; +import java.util.List; + public interface IProgressReporter { void writeMessage(String message); void writeWarning(String message); void writeError(String message); void terminate(); + void showData(String query, List> Object); } diff --git a/apgdiff/src/main/java/cz/startnet/utils/pgdiff/loader/callables/QueriesBatchCallable.java b/apgdiff/src/main/java/cz/startnet/utils/pgdiff/loader/callables/QueriesBatchCallable.java index b229d764e7..06686e2f3e 100644 --- a/apgdiff/src/main/java/cz/startnet/utils/pgdiff/loader/callables/QueriesBatchCallable.java +++ b/apgdiff/src/main/java/cz/startnet/utils/pgdiff/loader/callables/QueriesBatchCallable.java @@ -1,9 +1,12 @@ package cz.startnet.utils.pgdiff.loader.callables; import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Statement; +import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.regex.Pattern; @@ -15,6 +18,7 @@ import cz.startnet.utils.pgdiff.IProgressReporter; import cz.startnet.utils.pgdiff.PgDiffUtils; +import cz.startnet.utils.pgdiff.loader.jdbc.JdbcType; import ru.taximaxim.codekeeper.apgdiff.ApgdiffConsts.JDBC_CONSTS; public class QueriesBatchCallable extends StatementCallable { @@ -44,12 +48,15 @@ public String call() throws Exception { List queries = batches.get(0); subMonitor.setWorkRemaining(queries.size()); for (String query : queries) { - currQuery = query; PgDiffUtils.checkCancelled(monitor); - st.execute(query); + currQuery = query; + if (st.execute(query) && reporter != null) { + writeResult(query); + } writeWarnings(); writeStatus(query); + subMonitor.worked(1); } } else { @@ -78,7 +85,7 @@ public String call() throws Exception { reporter.writeMessage("Script finished"); } } catch (PSQLException ex) { - if (reporter == null) { + if (reporter == null || ex.getServerErrorMessage() == null) { throw ex; } ServerErrorMessage sem = ex.getServerErrorMessage(); @@ -94,6 +101,36 @@ public String call() throws Exception { return JDBC_CONSTS.JDBC_SUCCESS; } + private void writeResult(String query) throws SQLException { + List> results = new ArrayList<>(); + try (ResultSet res = st.getResultSet()) { + + ResultSetMetaData meta = res.getMetaData(); + int count = meta.getColumnCount(); + + // add column names as first list + List names = new ArrayList<>(count); + for (int i = 1; i <= count; i++) { + String type = meta.getColumnTypeName(i); + String dealias = JdbcType.DATA_TYPE_ALIASES.get(type); + names.add(meta.getColumnLabel(i) + '\n' + + (dealias == null ? type : dealias)); + } + results.add(names); + + // add other rows + while (res.next()) { + List row = new ArrayList<>(count); + results.add(row); + for (int i = 1; i <= count; i++) { + row.add(res.getObject(i)); + } + } + } + + reporter.showData(query, results); + } + private void writeWarnings() throws SQLException { if (reporter == null) { return; diff --git a/apgdiff/src/main/java/cz/startnet/utils/pgdiff/loader/jdbc/JdbcType.java b/apgdiff/src/main/java/cz/startnet/utils/pgdiff/loader/jdbc/JdbcType.java index 8824379ea9..3810a91306 100644 --- a/apgdiff/src/main/java/cz/startnet/utils/pgdiff/loader/jdbc/JdbcType.java +++ b/apgdiff/src/main/java/cz/startnet/utils/pgdiff/loader/jdbc/JdbcType.java @@ -12,7 +12,7 @@ public class JdbcType{ - private static final Map DATA_TYPE_ALIASES; + public static final Map DATA_TYPE_ALIASES; static { Map aliases = new HashMap<>(); diff --git a/ru.taximaxim.codekeeper.ui/OSGI-INF/l10n/bundle.properties b/ru.taximaxim.codekeeper.ui/OSGI-INF/l10n/bundle.properties index a628617582..5fa0826fd6 100644 --- a/ru.taximaxim.codekeeper.ui/OSGI-INF/l10n/bundle.properties +++ b/ru.taximaxim.codekeeper.ui/OSGI-INF/l10n/bundle.properties @@ -88,6 +88,7 @@ templates.name.sqlEditor = SQL Editor Templates view.category.name = pgCodeKeeper Views view.name.DepcyView = pg Dependencies view.name.ProjectOverrideView = Object overrides +view.name.ResultSetView = Query result wizard.name.keeperImportWizard = pgCodeKeeper Project wizard.name.keeperNewMsWizard = pgCodeKeeper MS SQL Project diff --git a/ru.taximaxim.codekeeper.ui/OSGI-INF/l10n/bundle_ru_RU.properties b/ru.taximaxim.codekeeper.ui/OSGI-INF/l10n/bundle_ru_RU.properties index 7ece9f9b80..3b4fdc5377 100644 --- a/ru.taximaxim.codekeeper.ui/OSGI-INF/l10n/bundle_ru_RU.properties +++ b/ru.taximaxim.codekeeper.ui/OSGI-INF/l10n/bundle_ru_RU.properties @@ -88,6 +88,7 @@ templates.name.sqlEditor = \u0428\u0430\u0431\u043B\u043E\u043D\u044B \u0440\u04 view.category.name = \u041F\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F pgCodeKeeper view.name.DepcyView = pg \u0417\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 view.name.ProjectOverrideView = \u041F\u0435\u0440\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u043D\u044B\u0435 \u043E\u0431\u044A\u0435\u043A\u0442\u044B +view.name.ResultSetView = \u0420\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u0437\u0430\u043F\u0440\u043E\u0441\u0430 wizard.name.keeperImportWizard = \u041F\u0440\u043E\u0435\u043A\u0442 pgCodeKeeper wizard.name.keeperNewMsWizard = MS SQL \u043F\u0440\u043E\u0435\u043A\u0442 pgCodeKeeper diff --git a/ru.taximaxim.codekeeper.ui/icons/data_table_row.png b/ru.taximaxim.codekeeper.ui/icons/data_table_row.png new file mode 100644 index 0000000000..e08d872781 Binary files /dev/null and b/ru.taximaxim.codekeeper.ui/icons/data_table_row.png differ diff --git a/ru.taximaxim.codekeeper.ui/plugin.xml b/ru.taximaxim.codekeeper.ui/plugin.xml index 67bddda9f8..9b028e5864 100644 --- a/ru.taximaxim.codekeeper.ui/plugin.xml +++ b/ru.taximaxim.codekeeper.ui/plugin.xml @@ -874,6 +874,12 @@ class="ru.taximaxim.codekeeper.ui.views.ProjectOverrideView" icon="icons/lib.gif" category="ru.taximaxim.codekeeper.ui.viewsCategory" /> + - - + + + + diff --git a/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/UIConsts.java b/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/UIConsts.java index 07a6ee1650..ceea540c1b 100644 --- a/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/UIConsts.java +++ b/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/UIConsts.java @@ -37,6 +37,7 @@ interface DECORATOR { interface VIEW { String OVERRIDE_VIEW = PLUGIN_ID.THIS + ".pgoverrideview"; //$NON-NLS-1$ + String RESULT_SET_VIEW = PLUGIN_ID.THIS + ".resultsetview"; //$NON-NLS-1$ } interface WIZARD { diff --git a/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/consoles/UiProgressReporter.java b/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/consoles/UiProgressReporter.java index c1b0b39cdb..9aab70f9dd 100644 --- a/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/consoles/UiProgressReporter.java +++ b/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/consoles/UiProgressReporter.java @@ -1,12 +1,21 @@ package ru.taximaxim.codekeeper.ui.consoles; +import java.util.List; + import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.console.IConsole; import org.eclipse.ui.console.IConsoleManager; import cz.startnet.utils.pgdiff.IProgressReporter; +import ru.taximaxim.codekeeper.ui.Log; +import ru.taximaxim.codekeeper.ui.UIConsts.VIEW; +import ru.taximaxim.codekeeper.ui.UiSync; +import ru.taximaxim.codekeeper.ui.views.ResultSetView; public class UiProgressReporter implements IProgressReporter { @@ -47,4 +56,18 @@ public void writeError(String message) { public void terminate() { console.terminate(); } + + @Override + public void showData(String query, List> results) { + UiSync.exec(PlatformUI.getWorkbench().getDisplay(), () -> { + IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + try { + ResultSetView viewPart = (ResultSetView) page.showView(VIEW.RESULT_SET_VIEW); + viewPart.addData(query, results); + } catch (PartInitException e) { + Log.log(e); + } + }); + + } } diff --git a/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/views/ResultSetView.java b/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/views/ResultSetView.java new file mode 100644 index 0000000000..be5f7c8f38 --- /dev/null +++ b/ru.taximaxim.codekeeper.ui/src/ru/taximaxim/codekeeper/ui/views/ResultSetView.java @@ -0,0 +1,172 @@ +package ru.taximaxim.codekeeper.ui.views; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.ViewerCell; +import org.eclipse.jface.viewers.ViewerColumn; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.part.ViewPart; + +import cz.startnet.utils.pgdiff.PgDiffUtils; +import ru.taximaxim.codekeeper.apgdiff.fileutils.FileUtils; +import ru.taximaxim.codekeeper.ui.UIConsts; + +public class ResultSetView extends ViewPart { + + private CTabFolder tabFolder; + + @Override + public void createPartControl(Composite parent) { + tabFolder = new CTabFolder(parent, SWT.BOTTOM); + GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); + gd.widthHint = 700; + tabFolder.setLayoutData(gd); + } + + public void addData(String query, List> results) { + if (results.isEmpty()) { + return; + } + + Composite tabComposite = new Composite(tabFolder, SWT.NONE); + tabComposite.setLayout(new GridLayout()); + tabComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + CTabItem tabItem = new CTabItem(tabFolder, SWT.CLOSE); + tabItem.setText(FileUtils.getFileDate()); + tabItem.setControl(tabComposite); + + Label l = new Label(tabComposite, SWT.NONE); + + String preview = query.replaceAll("\\s+", " ").trim(); //$NON-NLS-1$ //$NON-NLS-2$ + l.setText(preview.length() > 60 ? preview.substring(0, 60) + " <...> " : preview); //$NON-NLS-1$ + l.setToolTipText(query); + + TableViewer viewer = new TableViewer(tabComposite); + + viewer.setContentProvider(ArrayContentProvider.getInstance()); + viewer.getTable().setLayoutData(new GridData(GridData.FILL_BOTH)); + viewer.getTable().setLinesVisible(true); + viewer.getTable().setHeaderVisible(true); + + viewer.getTable().addKeyListener(new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) { + IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); + if (selection.isEmpty() || (e.stateMask & SWT.CTRL) != SWT.CTRL || e.keyCode != 'c') { + return; + } + + StringBuilder sb = new StringBuilder(); + for (Object r : selection.toList()) { + List row = (List) r; + for (Object val : row) { + sb.append(valueForCsv(val)); + sb.append(','); + } + // remove trailing comma + sb.setLength(sb.length() - 1); + sb.append(UIConsts._NL); + } + + final Clipboard cb = new Clipboard(viewer.getControl().getDisplay()); + cb.setContents(new Object[] {sb.toString()}, new Transfer[] {TextTransfer.getInstance()}); + } + }); + + + List names = results.get(0); + + TableViewerColumn num = new TableViewerColumn(viewer, SWT.LEFT); + num.getColumn().setResizable(true); + num.setLabelProvider(new RowNumberLabelProvider()); + num.getColumn().setWidth(40); + + for (int i = 0; i < names.size(); i++) { + TableViewerColumn col = new TableViewerColumn(viewer, SWT.LEFT); + col.getColumn().setResizable(true); + col.getColumn().setMoveable(true); + col.getColumn().setText(names.get(i).toString()); + col.setLabelProvider(new IndexedColumnLabelProvider(i)); + col.getColumn().setWidth(150); + } + + viewer.setInput(results.subList(1, results.size())); + tabFolder.setSelection(tabItem); + } + + @Override + public void setFocus() { + tabFolder.setFocus(); + } + + private String valueForCsv(Object val) { + if (val == null) { + return ""; //$NON-NLS-1$ + } + String s = val.toString(); + if (s.isEmpty() || s.indexOf(',') != -1 || s.indexOf(';') != -1 + || s.indexOf('\n') != -1 || s.indexOf('"') != -1) { + return PgDiffUtils.quoteName(s); + } else { + return s; + } + } + + private static class IndexedColumnLabelProvider extends ColumnLabelProvider { + private final int i; + + private IndexedColumnLabelProvider(int i) { + this.i = i; + } + + @Override + public String getText(Object element) { + List l = (List) element; + Object obj = l.get(i); + return obj == null ? "" : obj.toString(); //$NON-NLS-1$ + } + } + + private static class RowNumberLabelProvider extends ColumnLabelProvider { + + private TableViewer viewer; + + @Override + protected void initialize(ColumnViewer viewer, ViewerColumn column) { + super.initialize(viewer, column); + this.viewer = null; + if (viewer instanceof TableViewer) { + this.viewer = (TableViewer) viewer; + } + } + + @Override + public void update(ViewerCell cell) { + super.update(cell); + if (viewer != null) { + int index = Arrays.asList(viewer.getTable().getItems()).indexOf(cell.getItem()); + cell.setText("" + (index + 1)); //$NON-NLS-1$ + } + } + } +}