This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new 37844d7c15 improvements to database explorer and preview, fixes #6998
(#7004)
37844d7c15 is described below
commit 37844d7c158006f61f34c039a7d4f95b83c5ba26
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Wed Apr 15 12:53:33 2026 +0200
improvements to database explorer and preview, fixes #6998 (#7004)
---
core/src/main/java/org/apache/hop/core/Const.java | 13 +
.../org/apache/hop/core/database/Database.java | 31 ++
.../pages/pipeline/transforms/gettablenames.adoc | 6 +
.../ROOT/pages/pipeline/transforms/tableinput.adoc | 7 +
.../modules/ROOT/pages/variables.adoc | 1 +
.../transforms/gettablenames/GetTableNames.java | 11 +
.../gettablenames/GetTableNamesDialog.java | 67 +--
.../pipeline/transforms/tableinput/TableInput.java | 13 +
.../transforms/tableinput/TableInputDialog.java | 98 ++--
.../hop/ui/core/database/DatabaseMetaEditor.java | 4 +-
.../database/dialog/DatabaseExplorerDialog.java | 553 ++++++++++++---------
.../dialog/GetDatabaseInfoProgressDialog.java | 8 +-
.../dialog/GetPreviewTableProgressDialog.java | 194 +++++---
.../dialog/PreviewTableSettingsDialog.java | 234 +++++++++
.../hop/ui/core/database/dialog/SqlEditor.java | 331 ++++++++----
.../org/apache/hop/ui/core/dialog/BaseDialog.java | 35 +-
.../hop/ui/core/dialog/EnterNumberDialog.java | 35 +-
.../hop/ui/core/dialog/PreviewRowsDialog.java | 38 +-
.../hop/ui/core/dialog/ProgressMonitorDialog.java | 208 +++++---
.../hop/ui/core/metadata/MetadataEditorDialog.java | 1 +
.../dialog/messages/messages_en_US.properties | 8 +
21 files changed, 1260 insertions(+), 636 deletions(-)
diff --git a/core/src/main/java/org/apache/hop/core/Const.java
b/core/src/main/java/org/apache/hop/core/Const.java
index af2bf805d4..32c2ba8e38 100644
--- a/core/src/main/java/org/apache/hop/core/Const.java
+++ b/core/src/main/java/org/apache/hop/core/Const.java
@@ -1005,6 +1005,19 @@ public class Const {
public static final String HOP_TABLE_VIEW_EXTRA_COLUMN_MARGIN =
"HOP_TABLE_VIEW_EXTRA_COLUMN_MARGIN";
+ /**
+ * Default JDBC {@link java.sql.Statement#setQueryTimeout(int)} in whole
seconds for database
+ * <strong>query preview</strong> UIs (for example the initial value in the
preview settings
+ * dialog). Normal pipeline execution does not use this variable for Table
Input statement
+ * timeouts. Set in Hop configuration, project, environment, or pipeline
variables.
+ */
+ @Variable(
+ scope = VariableScope.APPLICATION,
+ value = "20",
+ description =
+ "Default JDBC statement query timeout in seconds for database query
preview (0 = unset).")
+ public static final String HOP_QUERY_PREVIEW_TIMEOUT =
"HOP_QUERY_PREVIEW_TIMEOUT";
+
/**
* rounds double f to any number of places after decimal point Does
arithmetic using BigDecimal
* class to avoid integer overflow while rounding
diff --git a/core/src/main/java/org/apache/hop/core/database/Database.java
b/core/src/main/java/org/apache/hop/core/database/Database.java
index 2332bd5415..4a190dcac3 100644
--- a/core/src/main/java/org/apache/hop/core/database/Database.java
+++ b/core/src/main/java/org/apache/hop/core/database/Database.java
@@ -121,6 +121,14 @@ public class Database implements IVariables,
ILoggingObject, AutoCloseable {
private static final String CONST_ERROR_UPDATING_BATCH = "Error updating
batch";
private int rowlimit;
+
+ /**
+ * When positive, applied to statements created in {@link #openQuery(String,
IRowMeta, Object[],
+ * int, boolean)} via {@link Statement#setQueryTimeout(int)} (whole
seconds). Zero leaves the JDBC
+ * driver default (typically unlimited). Intended for short-lived GUI
preview connections.
+ */
+ private int statementQueryTimeoutSeconds;
+
private int commitsize;
private Connection connection;
@@ -205,6 +213,7 @@ public class Database implements IVariables,
ILoggingObject, AutoCloseable {
dbmd = null;
rowlimit = 0;
+ statementQueryTimeoutSeconds = 0;
written = 0;
@@ -265,6 +274,23 @@ public class Database implements IVariables,
ILoggingObject, AutoCloseable {
rowlimit = rows;
}
+ /**
+ * Sets the JDBC {@link Statement#setQueryTimeout(int)} (seconds) for
statements opened by {@link
+ * #openQuery(String, IRowMeta, Object[], int, boolean)} until {@link
#disconnect()}. Use {@code
+ * 0} to use the driver default.
+ *
+ * @param seconds query timeout in whole seconds; values {@code < 0} are
treated as {@code 0}
+ */
+ public void setStatementQueryTimeoutSeconds(int seconds) {
+ this.statementQueryTimeoutSeconds = Math.max(0, seconds);
+ }
+
+ private void applyStatementQueryTimeout(Statement statement) throws
SQLException {
+ if (statement != null && statementQueryTimeoutSeconds > 0) {
+ statement.setQueryTimeout(statementQueryTimeoutSeconds);
+ }
+ }
+
/**
* @return Returns the prepStatementInsert.
*/
@@ -610,6 +636,7 @@ public class Database implements IVariables,
ILoggingObject, AutoCloseable {
+ hde.getMessage());
log.logError(Const.getStackTracker(hde));
}
+ statementQueryTimeoutSeconds = 0;
}
}
@@ -1591,6 +1618,8 @@ public class Database implements IVariables,
ILoggingObject, AutoCloseable {
pstmt.setMaxRows(rowlimit);
}
+ applyStatementQueryTimeout(pstmt);
+
log.snap(Metrics.METRIC_DATABASE_EXECUTE_SQL_START,
databaseMeta.getName());
res = pstmt.executeQuery();
log.snap(Metrics.METRIC_DATABASE_EXECUTE_SQL_STOP,
databaseMeta.getName());
@@ -1612,6 +1641,8 @@ public class Database implements IVariables,
ILoggingObject, AutoCloseable {
selStmt.setMaxRows(rowlimit);
}
+ applyStatementQueryTimeout(selStmt);
+
log.snap(Metrics.METRIC_DATABASE_EXECUTE_SQL_START,
databaseMeta.getName());
res = selStmt.executeQuery(databaseMeta.stripCR(sql));
log.snap(Metrics.METRIC_DATABASE_EXECUTE_SQL_STOP,
databaseMeta.getName());
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/gettablenames.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/gettablenames.adoc
index dc1e443a67..69919de935 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/gettablenames.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/gettablenames.adoc
@@ -37,6 +37,12 @@ The Get Table Names transform gets table names from a
database connection.
!===
|===
+== Preview
+
+The *Preview* button opens the same preview settings dialog as Table Input
(row limit and query timeout in seconds). The JDBC statement timeout is applied
**only in preview mode** from the Hop GUI.
+
+You can set a default for the timeout field with the
xref:variables.adoc#_available_global_variables[`HOP_QUERY_PREVIEW_TIMEOUT`]
application variable. See
xref:pipeline/transforms/tableinput.adoc#_preview[Table Input: Preview] for the
same behaviour details.
+
== Options
[options="header"]
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/tableinput.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/tableinput.adoc
index 270f794cc4..62a7d14b47 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/tableinput.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/tableinput.adoc
@@ -189,6 +189,13 @@ You can inject metadata into the following fields of the
Table Input transform:
- Execute for each row?
- Limit size
+== Preview
+
+The *Preview* button opens a dialog where you set how many rows to fetch and a
**query timeout** in seconds (JDBC `Statement#setQueryTimeout`).
+
+* The timeout applies **only while the pipeline is running in preview mode**
from the Hop GUI (not during a normal pipeline run from a run configuration or
xref:hop-run/index.adoc[Hop Run]).
+* You can set a default for the timeout field with the
xref:variables.adoc#_available_global_variables[`HOP_QUERY_PREVIEW_TIMEOUT`]
application variable (`0` = no default from the variable). When the variable is
`0` or unset, the dialog still suggests `5` seconds for the initial value.
+
== Options
[options="header"]
diff --git a/docs/hop-user-manual/modules/ROOT/pages/variables.adoc
b/docs/hop-user-manual/modules/ROOT/pages/variables.adoc
index 0cf3ce66aa..fa2dbdff78 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/variables.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/variables.adoc
@@ -287,6 +287,7 @@ Set to 0 to keep all rows indefinitely (default)
|HOP_MAX_WORKFLOW_TRACKER_SIZE|5000|The maximum age (in minutes) of a log line
while being kept internally by Hop. Set to 0 to keep all rows indefinitely
(default)
|HOP_PASSWORD_ENCODER_PLUGIN|Hop|Specifies the password encoder plugin to use
by ID (Hop is the default).
|HOP_PIPELINE_ROWSET_SIZE|-|Name of the environment variable that contains the
size of the pipeline rowset size. This overwrites values that you set pipeline
settings
+|HOP_QUERY_PREVIEW_TIMEOUT|0|Default JDBC statement query timeout in
**seconds** for database query preview (for example the initial value in the
preview settings dialog for Table Input / Get table names, and related preview
flows). `0` means no default from this variable. Does not apply to normal
pipeline execution for those transforms' JDBC timeouts in the same way; use the
preview dialog to set the timeout for a given preview run.
|HOP_PLUGIN_CLASSES||A comma delimited list of classes to scan for plugin
annotations
|HOP_ROWSET_GET_TIMEOUT|50|The name of the variable that optionally contains
an alternative rowset get timeout (in ms).
This only makes a difference for extremely short lived pipelines.
diff --git
a/plugins/transforms/gettablenames/src/main/java/org/apache/hop/pipeline/transforms/gettablenames/GetTableNames.java
b/plugins/transforms/gettablenames/src/main/java/org/apache/hop/pipeline/transforms/gettablenames/GetTableNames.java
index 7d98c9d123..2c45593d21 100644
---
a/plugins/transforms/gettablenames/src/main/java/org/apache/hop/pipeline/transforms/gettablenames/GetTableNames.java
+++
b/plugins/transforms/gettablenames/src/main/java/org/apache/hop/pipeline/transforms/gettablenames/GetTableNames.java
@@ -27,6 +27,7 @@ import org.apache.hop.core.exception.HopTransformException;
import org.apache.hop.core.exception.HopValueException;
import org.apache.hop.core.row.RowDataUtil;
import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.util.EnvUtil;
import org.apache.hop.core.util.Utils;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.pipeline.Pipeline;
@@ -424,6 +425,16 @@ public class GetTableNames extends
BaseTransform<GetTableNamesMeta, GetTableName
}
data.db = new Database(this, variables, databaseMeta);
+ if (getPipeline() != null && getPipeline().isPreview()) {
+ String raw =
+ getVariable(
+ Const.HOP_QUERY_PREVIEW_TIMEOUT,
+ EnvUtil.getSystemProperty(Const.HOP_QUERY_PREVIEW_TIMEOUT,
"0"));
+ int statementQueryTimeoutSeconds = Math.max(0,
Const.toInt(resolve(raw), 0));
+ if (statementQueryTimeoutSeconds > 0) {
+
data.db.setStatementQueryTimeoutSeconds(statementQueryTimeoutSeconds);
+ }
+ }
try {
data.db.connect();
if (isDetailed()) {
diff --git
a/plugins/transforms/gettablenames/src/main/java/org/apache/hop/pipeline/transforms/gettablenames/GetTableNamesDialog.java
b/plugins/transforms/gettablenames/src/main/java/org/apache/hop/pipeline/transforms/gettablenames/GetTableNamesDialog.java
index 125839725e..8bb44961b1 100644
---
a/plugins/transforms/gettablenames/src/main/java/org/apache/hop/pipeline/transforms/gettablenames/GetTableNamesDialog.java
+++
b/plugins/transforms/gettablenames/src/main/java/org/apache/hop/pipeline/transforms/gettablenames/GetTableNamesDialog.java
@@ -27,8 +27,8 @@ import org.apache.hop.pipeline.Pipeline;
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.PipelinePreviewFactory;
import org.apache.hop.ui.core.PropsUi;
+import org.apache.hop.ui.core.database.dialog.PreviewTableSettingsDialog;
import org.apache.hop.ui.core.dialog.BaseDialog;
-import org.apache.hop.ui.core.dialog.EnterNumberDialog;
import org.apache.hop.ui.core.dialog.EnterTextDialog;
import org.apache.hop.ui.core.dialog.ErrorDialog;
import org.apache.hop.ui.core.dialog.MessageBox;
@@ -656,43 +656,44 @@ public class GetTableNamesDialog extends
BaseTransformDialog {
return;
}
+ int defaultRows = props.getDefaultPreviewSize();
+ PreviewTableSettingsDialog settingsDialog =
+ new PreviewTableSettingsDialog(shell, Math.max(1, defaultRows),
variables, true);
+ PreviewTableSettingsDialog.Settings settings = settingsDialog.open();
+ if (settings == null) {
+ return;
+ }
+ int previewRows = settings.rowLimit > 0 ? settings.rowLimit : Math.max(1,
defaultRows);
+ IVariables previewVariables =
settingsDialog.getPreviewExecutionVariables();
+
PipelineMeta previewMeta =
PipelinePreviewFactory.generatePreviewPipeline(
pipelineMeta.getMetadataProvider(), oneMeta,
wTransformName.getText());
- EnterNumberDialog numberDialog =
- new EnterNumberDialog(
+ PipelinePreviewProgressDialog progressDialog =
+ new PipelinePreviewProgressDialog(
shell,
- props.getDefaultPreviewSize(),
- BaseMessages.getString(PKG,
"GetTableNamesDialog.PreviewSize.DialogTitle"),
- BaseMessages.getString(PKG,
"GetTableNamesDialog.PreviewSize.DialogMessage"));
- int previewSize = numberDialog.open();
- if (previewSize > 0) {
- PipelinePreviewProgressDialog progressDialog =
- new PipelinePreviewProgressDialog(
- shell,
- variables,
- previewMeta,
- new String[] {wTransformName.getText()},
- new int[] {previewSize});
- progressDialog.open();
-
- if (!progressDialog.isCancelled()) {
- Pipeline pipeline = progressDialog.getPipeline();
- String loggingText = progressDialog.getLoggingText();
-
- if (pipeline.getResult() != null && pipeline.getResult().getNrErrors()
> 0) {
- EnterTextDialog etd =
- new EnterTextDialog(
- shell,
- BaseMessages.getString(PKG, "System.Dialog.Error.Title"),
- BaseMessages.getString(PKG,
"GetTableNamesDialog.ErrorInPreview.DialogMessage"),
- loggingText,
- true);
- etd.setReadOnly();
- etd.open();
- }
-
+ previewVariables,
+ previewMeta,
+ new String[] {wTransformName.getText()},
+ new int[] {previewRows});
+ progressDialog.open();
+
+ if (!progressDialog.isCancelled()) {
+ Pipeline pipeline = progressDialog.getPipeline();
+ String loggingText = progressDialog.getLoggingText();
+
+ if (pipeline.getResult() != null && pipeline.getResult().getNrErrors() >
0) {
+ EnterTextDialog etd =
+ new EnterTextDialog(
+ shell,
+ BaseMessages.getString(PKG, "System.Dialog.Error.Title"),
+ BaseMessages.getString(PKG,
"GetTableNamesDialog.ErrorInPreview.DialogMessage"),
+ loggingText,
+ true);
+ etd.setReadOnly();
+ etd.open();
+ } else {
PreviewRowsDialog prd =
new PreviewRowsDialog(
shell,
diff --git
a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInput.java
b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInput.java
index 2aa42fb7f1..ece2fcfec3 100644
---
a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInput.java
+++
b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInput.java
@@ -31,6 +31,7 @@ import org.apache.hop.core.row.IRowMeta;
import org.apache.hop.core.row.IValueMeta;
import org.apache.hop.core.row.RowDataUtil;
import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.util.EnvUtil;
import org.apache.hop.core.util.Utils;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.pipeline.Pipeline;
@@ -341,6 +342,18 @@ public class TableInput extends
BaseTransform<TableInputMeta, TableInputData> {
data.db = new Database(this, this, databaseMeta);
data.db.setQueryLimit(Const.toInt(resolve(meta.getRowLimit()), 0));
+ // Statement timeout is for transform dialog / pipeline preview only
(Hop GUI sets preview).
+ // Normal pipeline runs use JDBC driver default (0 = no explicit timeout
on the statement).
+ if (getPipeline() != null && getPipeline().isPreview()) {
+ String raw =
+ getVariable(
+ Const.HOP_QUERY_PREVIEW_TIMEOUT,
+ EnvUtil.getSystemProperty(Const.HOP_QUERY_PREVIEW_TIMEOUT,
"0"));
+ int statementQueryTimeoutSeconds = Math.max(0,
Const.toInt(resolve(raw), 0));
+ if (statementQueryTimeoutSeconds > 0) {
+
data.db.setStatementQueryTimeoutSeconds(statementQueryTimeoutSeconds);
+ }
+ }
try {
data.db.connect();
diff --git
a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java
b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java
index 805d14b292..348dd33810 100644
---
a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java
+++
b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java
@@ -39,8 +39,8 @@ import org.apache.hop.pipeline.PipelinePreviewFactory;
import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.ui.core.PropsUi;
import org.apache.hop.ui.core.database.dialog.DatabaseExplorerDialog;
+import org.apache.hop.ui.core.database.dialog.PreviewTableSettingsDialog;
import org.apache.hop.ui.core.dialog.BaseDialog;
-import org.apache.hop.ui.core.dialog.EnterNumberDialog;
import org.apache.hop.ui.core.dialog.EnterTextDialog;
import org.apache.hop.ui.core.dialog.MessageBox;
import org.apache.hop.ui.core.dialog.PreviewRowsDialog;
@@ -592,54 +592,56 @@ public class TableInputDialog extends BaseTransformDialog
{
TableInputMeta oneMeta = new TableInputMeta();
getInfo(oneMeta, true);
- EnterNumberDialog numberDialog =
- new EnterNumberDialog(
+ int defaultRows = props.getDefaultPreviewSize();
+ PreviewTableSettingsDialog settingsDialog =
+ new PreviewTableSettingsDialog(shell, Math.max(1, defaultRows),
variables, true);
+ PreviewTableSettingsDialog.Settings settings = settingsDialog.open();
+ if (settings == null) {
+ return;
+ }
+ int previewRows = settings.rowLimit > 0 ? settings.rowLimit : Math.max(1,
defaultRows);
+ oneMeta.setRowLimit(Integer.toString(previewRows));
+
+ IVariables previewVariables =
settingsDialog.getPreviewExecutionVariables();
+
+ PipelineMeta previewMeta =
+ PipelinePreviewFactory.generatePreviewPipeline(
+ pipelineMeta.getMetadataProvider(), oneMeta,
wTransformName.getText());
+
+ PipelinePreviewProgressDialog progressDialog =
+ new PipelinePreviewProgressDialog(
shell,
- props.getDefaultPreviewSize(),
- BaseMessages.getString(PKG, "TableInputDialog.EnterPreviewSize"),
- BaseMessages.getString(PKG,
"TableInputDialog.NumberOfRowsToPreview"));
- int previewSize = numberDialog.open();
- if (previewSize > 0) {
- oneMeta.setRowLimit(Integer.toString(previewSize));
- PipelineMeta previewMeta =
- PipelinePreviewFactory.generatePreviewPipeline(
- pipelineMeta.getMetadataProvider(), oneMeta,
wTransformName.getText());
-
- PipelinePreviewProgressDialog progressDialog =
- new PipelinePreviewProgressDialog(
- shell,
- variables,
- previewMeta,
- new String[] {wTransformName.getText()},
- new int[] {previewSize});
- progressDialog.open();
-
- Pipeline pipeline = progressDialog.getPipeline();
- String loggingText = progressDialog.getLoggingText();
-
- if (!progressDialog.isCancelled()) {
- if (pipeline.getResult() != null && pipeline.getResult().getNrErrors()
> 0) {
- EnterTextDialog etd =
- new EnterTextDialog(
- shell,
- BaseMessages.getString(PKG,
"System.Dialog.PreviewError.Title"),
- BaseMessages.getString(PKG,
"System.Dialog.PreviewError.Message"),
- loggingText,
- true);
- etd.setReadOnly();
- etd.open();
- } else {
- PreviewRowsDialog prd =
- new PreviewRowsDialog(
- shell,
- variables,
- SWT.NONE,
- wTransformName.getText(),
- progressDialog.getPreviewRowsMeta(wTransformName.getText()),
- progressDialog.getPreviewRows(wTransformName.getText()),
- loggingText);
- prd.open();
- }
+ previewVariables,
+ previewMeta,
+ new String[] {wTransformName.getText()},
+ new int[] {previewRows});
+ progressDialog.open();
+
+ Pipeline pipeline = progressDialog.getPipeline();
+ String loggingText = progressDialog.getLoggingText();
+
+ if (!progressDialog.isCancelled()) {
+ if (pipeline.getResult() != null && pipeline.getResult().getNrErrors() >
0) {
+ EnterTextDialog etd =
+ new EnterTextDialog(
+ shell,
+ BaseMessages.getString(PKG,
"System.Dialog.PreviewError.Title"),
+ BaseMessages.getString(PKG,
"System.Dialog.PreviewError.Message"),
+ loggingText,
+ true);
+ etd.setReadOnly();
+ etd.open();
+ } else {
+ PreviewRowsDialog prd =
+ new PreviewRowsDialog(
+ shell,
+ variables,
+ SWT.NONE,
+ wTransformName.getText(),
+ progressDialog.getPreviewRowsMeta(wTransformName.getText()),
+ progressDialog.getPreviewRows(wTransformName.getText()),
+ loggingText);
+ prd.open();
}
}
}
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/database/DatabaseMetaEditor.java
b/ui/src/main/java/org/apache/hop/ui/core/database/DatabaseMetaEditor.java
index 1f786ce515..a71bde7498 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/database/DatabaseMetaEditor.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/database/DatabaseMetaEditor.java
@@ -1043,7 +1043,9 @@ public class DatabaseMetaEditor extends
MetadataEditor<DatabaseMeta> {
SWT.NONE,
manager.getVariables(),
meta,
- manager.getSerializer().loadAll());
+ manager.getSerializer().loadAll(),
+ true,
+ true);
dialog.open();
} catch (Exception e) {
new ErrorDialog(getShell(), "Error", "Error exploring database", e);
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
index a9f5adb16d..0ba95d9808 100644
---
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
+++
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
@@ -41,7 +41,6 @@ import org.apache.hop.core.variables.IVariables;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.ui.core.ConstUi;
import org.apache.hop.ui.core.PropsUi;
-import org.apache.hop.ui.core.dialog.EnterNumberDialog;
import org.apache.hop.ui.core.dialog.EnterSelectionDialog;
import org.apache.hop.ui.core.dialog.ErrorDialog;
import org.apache.hop.ui.core.dialog.MessageBox;
@@ -57,6 +56,7 @@ import
org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
@@ -118,7 +118,7 @@ public class DatabaseExplorerDialog extends Dialog {
private static final String STRING_SYNONYMS =
BaseMessages.getString(PKG, "DatabaseExplorerDialog.Synonyms.Label");
- private final Shell parent;
+ private final Shell parentShell;
private Shell shell;
private Tree wTree;
private TreeItem tiTree;
@@ -132,6 +132,7 @@ public class DatabaseExplorerDialog extends Dialog {
private boolean splitSchemaAndTable;
private String schemaName;
private Composite buttonsComposite;
+ private Button wOk;
private Button bPrev;
private Button bPrevN;
private Button bCount;
@@ -145,24 +146,24 @@ public class DatabaseExplorerDialog extends Dialog {
private Control toolBar;
public DatabaseExplorerDialog(
- Shell parent,
+ Shell parentShell,
int style,
IVariables variables,
DatabaseMeta conn,
List<DatabaseMeta> databases) {
- this(parent, style, variables, conn, databases, false, true);
+ this(parentShell, style, variables, conn, databases, false, true);
}
public DatabaseExplorerDialog(
- Shell parent,
+ Shell parentShell,
int style,
IVariables variables,
DatabaseMeta conn,
List<DatabaseMeta> databases,
boolean look,
boolean splitSchemaAndTable) {
- super(parent, style);
- this.parent = parent;
+ super(parentShell, style);
+ this.parentShell = parentShell;
this.dbMeta = conn;
this.variables = variables;
this.databases = databases;
@@ -188,9 +189,10 @@ public class DatabaseExplorerDialog extends Dialog {
if (Const.isLinux()) {
shell =
new Shell(
- parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM | SWT.RESIZE |
SWT.MAX | SWT.MIN);
+ parentShell,
+ SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX |
SWT.MIN);
} else {
- shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX |
SWT.MIN);
+ shell = new Shell(parentShell, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX |
SWT.MIN);
}
PropsUi.setLook(shell);
shell.setImage(GuiResource.getInstance().getImageDatabase());
@@ -208,10 +210,11 @@ public class DatabaseExplorerDialog extends Dialog {
// Main buttons at the bottom
//
List<Button> buttons = new ArrayList<>();
- Button wOk = new Button(shell, SWT.PUSH);
+ wOk = new Button(shell, SWT.PUSH);
wOk.setText(BaseMessages.getString(PKG, "System.Button.OK"));
wOk.addListener(SWT.Selection, e -> ok());
buttons.add(wOk);
+ shell.setDefaultButton(wOk);
Button wRefresh = new Button(shell, SWT.PUSH);
wRefresh.setText(BaseMessages.getString(PKG, "System.Button.Refresh"));
@@ -256,6 +259,9 @@ public class DatabaseExplorerDialog extends Dialog {
wTree.setLayoutData(fdTree);
if (!getData()) {
+ if (shell != null && !shell.isDisposed()) {
+ shell.dispose();
+ }
return false;
}
@@ -271,6 +277,9 @@ public class DatabaseExplorerDialog extends Dialog {
});
shell.addListener(SWT.Close, e -> cancel());
+ // Prevent resizing below the complete control layout (notably right-side
action buttons).
+ Point minSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ shell.setMinimumSize(minSize);
BaseTransformDialog.setSize(shell);
shell.open();
@@ -311,7 +320,7 @@ public class DatabaseExplorerDialog extends Dialog {
new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- previewTable(activeSchemaTable, false);
+ previewTable(activeSchemaTable);
}
});
FormData prevData = new FormData();
@@ -329,7 +338,7 @@ public class DatabaseExplorerDialog extends Dialog {
new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- previewTable(activeSchemaTable, true);
+ previewTable(activeSchemaTable);
}
});
FormData prevNData = new FormData();
@@ -443,12 +452,13 @@ public class DatabaseExplorerDialog extends Dialog {
FormData truncateData = new FormData();
truncateData.left = new FormAttachment(0, 0);
truncateData.right = new FormAttachment(100, 0);
- truncateData.top = new FormAttachment(bSql, PropsUi.getMargin() * 7);
+ truncateData.top = new FormAttachment(bSql, PropsUi.getMargin());
bTruncate.setLayoutData(truncateData);
FormData fdComposite = new FormData();
fdComposite.right = new FormAttachment(100, 0);
fdComposite.top = new FormAttachment(0, toolBar.getBounds().height);
+ fdComposite.bottom = new FormAttachment(wOk, -2 * PropsUi.getMargin());
buttonsComposite.setLayoutData(fdComposite);
}
@@ -526,164 +536,181 @@ public class DatabaseExplorerDialog extends Dialog {
GetDatabaseInfoProgressDialog gdipd =
new GetDatabaseInfoProgressDialog(shell, variables, dbMeta);
DatabaseMetaInformation dmi = gdipd.open();
- if (dmi != null) {
- // Clear the tree top entry
- if (tiTree != null && !tiTree.isDisposed()) {
- tiTree.dispose();
- }
-
- // New entry in the tree
- tiTree = new TreeItem(wTree, SWT.NONE);
- tiTree.setImage(GuiResource.getInstance().getImageDatabase());
- tiTree.setText(dbMeta == null ? "" : dbMeta.getName());
-
- // Show the catalogs...
- Catalog[] catalogs = dmi.getCatalogs();
- if (catalogs != null) {
- TreeItem tiCat = new TreeItem(tiTree, SWT.NONE);
- tiCat.setImage(GuiResource.getInstance().getImageFolder());
- tiCat.setText(STRING_CATALOG);
-
- for (Catalog catalog : catalogs) {
- TreeItem newCat = new TreeItem(tiCat, SWT.NONE);
- newCat.setImage(GuiResource.getInstance().getImageFolder());
- newCat.setText(catalog.getCatalogName());
-
- for (int j = 0; j < catalog.getItems().length; j++) {
- String tableName = catalog.getItems()[j];
-
- TreeItem ti = new TreeItem(newCat, SWT.NONE);
- ti.setImage(GuiResource.getInstance().getImageTable());
- ti.setText(tableName);
- }
- }
- }
-
- // The schema's
- Schema[] schemas = dmi.getSchemas();
- if (schemas != null) {
- TreeItem tiSch = new TreeItem(tiTree, SWT.NONE);
- tiSch.setImage(GuiResource.getInstance().getImageFolder());
- tiSch.setText(STRING_SCHEMAS);
-
- for (Schema schema : schemas) {
- TreeItem newSch = new TreeItem(tiSch, SWT.NONE);
- newSch.setImage(GuiResource.getInstance().getImageSchema());
- newSch.setText(schema.getSchemaName());
+ if (dmi == null) {
+ return false;
+ }
+ disposeOldTreeRootIfPresent();
+ createDatabaseRootTreeItem();
+ addCatalogBranches(dmi);
+ addSchemaBranches(dmi);
+ TreeItem tiTab = addTablesBranch(dmi.getTables());
+ TreeItem tiView = addViewsBranch(dmi.getViews());
+ TreeItem tiSyn = addSynonymsBranch(dmi.getSynonyms());
+ selectInitiallySelectedTable(tiTab, tiView, tiSyn);
+ tiTree.setExpanded(true);
+ return true;
+ }
- for (int j = 0; j < schema.getItems().length; j++) {
- String tableName = schema.getItems()[j];
+ private void disposeOldTreeRootIfPresent() {
+ if (tiTree != null && !tiTree.isDisposed()) {
+ tiTree.dispose();
+ }
+ }
- TreeItem ti = new TreeItem(newSch, SWT.NONE);
- ti.setImage(GuiResource.getInstance().getImageTable());
- ti.setText(tableName);
- }
- }
- }
+ private void createDatabaseRootTreeItem() {
+ tiTree = new TreeItem(wTree, SWT.NONE);
+ tiTree.setImage(GuiResource.getInstance().getImageDatabase());
+ tiTree.setText(dbMeta == null ? "" : dbMeta.getName());
+ }
- // The tables in general...
- TreeItem tiTab = null;
- String[] tabnames = dmi.getTables();
- if (tabnames != null) {
- tiTab = new TreeItem(tiTree, SWT.NONE);
- tiTab.setImage(GuiResource.getInstance().getImageFolder());
- tiTab.setText(STRING_TABLES);
- tiTab.setExpanded(true);
-
- for (String tabname : tabnames) {
- TreeItem newTab = new TreeItem(tiTab, SWT.NONE);
- newTab.setImage(GuiResource.getInstance().getImageTable());
- newTab.setText(tabname);
- }
+ private void addCatalogBranches(DatabaseMetaInformation dmi) {
+ Catalog[] catalogs = dmi.getCatalogs();
+ if (catalogs == null) {
+ return;
+ }
+ TreeItem tiCat = new TreeItem(tiTree, SWT.NONE);
+ tiCat.setImage(GuiResource.getInstance().getImageFolder());
+ tiCat.setText(STRING_CATALOG);
+ for (Catalog catalog : catalogs) {
+ TreeItem newCat = new TreeItem(tiCat, SWT.NONE);
+ newCat.setImage(GuiResource.getInstance().getImageFolder());
+ newCat.setText(catalog.getCatalogName());
+ for (String catalogItemName : catalog.getItems()) {
+ TreeItem ti = new TreeItem(newCat, SWT.NONE);
+ ti.setImage(GuiResource.getInstance().getImageTable());
+ ti.setText(catalogItemName);
}
+ }
+ }
- // The views...
- TreeItem tiView = null;
- String[] views = dmi.getViews();
- if (views != null) {
- tiView = new TreeItem(tiTree, SWT.NONE);
- tiView.setImage(GuiResource.getInstance().getImageFolder());
- tiView.setText(STRING_VIEWS);
- for (String view : views) {
- TreeItem newView = new TreeItem(tiView, SWT.NONE);
- newView.setImage(GuiResource.getInstance().getImageView());
- newView.setText(view);
- }
+ private void addSchemaBranches(DatabaseMetaInformation dmi) {
+ Schema[] schemas = dmi.getSchemas();
+ if (schemas == null) {
+ return;
+ }
+ TreeItem tiSch = new TreeItem(tiTree, SWT.NONE);
+ tiSch.setImage(GuiResource.getInstance().getImageFolder());
+ tiSch.setText(STRING_SCHEMAS);
+ for (Schema schema : schemas) {
+ TreeItem newSch = new TreeItem(tiSch, SWT.NONE);
+ newSch.setImage(GuiResource.getInstance().getImageSchema());
+ newSch.setText(schema.getSchemaName());
+ for (String schemaItemName : schema.getItems()) {
+ TreeItem ti = new TreeItem(newSch, SWT.NONE);
+ ti.setImage(GuiResource.getInstance().getImageTable());
+ ti.setText(schemaItemName);
}
+ }
+ }
- // The synonyms
- TreeItem tiSyn = null;
- String[] syn = dmi.getSynonyms();
- if (syn != null) {
- tiSyn = new TreeItem(tiTree, SWT.NONE);
- tiSyn.setImage(GuiResource.getInstance().getImageFolder());
- tiSyn.setText(STRING_SYNONYMS);
- for (String s : syn) {
- TreeItem newSyn = new TreeItem(tiSyn, SWT.NONE);
- newSyn.setImage(GuiResource.getInstance().getImageSynonym());
- newSyn.setText(s);
- }
- }
+ private TreeItem addTablesBranch(String[] tabnames) {
+ if (tabnames == null) {
+ return null;
+ }
+ TreeItem tiTab = new TreeItem(tiTree, SWT.NONE);
+ tiTab.setImage(GuiResource.getInstance().getImageFolder());
+ tiTab.setText(STRING_TABLES);
+ tiTab.setExpanded(true);
+ for (String tabname : tabnames) {
+ TreeItem newTab = new TreeItem(tiTab, SWT.NONE);
+ newTab.setImage(GuiResource.getInstance().getImageTable());
+ newTab.setText(tabname);
+ }
+ return tiTab;
+ }
- // Make sure the selected table is shown...
- if (!StringUtils.isEmpty(selectedTable)) {
- TreeItem ti = null;
- if (ti == null && tiTab != null) {
- ti = ConstUi.findTreeItem(tiTab, selectedSchema, selectedTable);
- }
- if (ti == null && tiView != null) {
- ti = ConstUi.findTreeItem(tiView, selectedSchema, selectedTable);
- }
- if (ti == null && tiTree != null) {
- ti = ConstUi.findTreeItem(tiTree, selectedSchema, selectedTable);
- }
- if (ti == null && tiSyn != null) {
- ti = ConstUi.findTreeItem(tiSyn, selectedSchema, selectedTable);
- }
+ private TreeItem addViewsBranch(String[] views) {
+ if (views == null) {
+ return null;
+ }
+ TreeItem tiView = new TreeItem(tiTree, SWT.NONE);
+ tiView.setImage(GuiResource.getInstance().getImageFolder());
+ tiView.setText(STRING_VIEWS);
+ for (String view : views) {
+ TreeItem newView = new TreeItem(tiView, SWT.NONE);
+ newView.setImage(GuiResource.getInstance().getImageView());
+ newView.setText(view);
+ }
+ return tiView;
+ }
- if (ti != null) {
- wTree.setSelection(new TreeItem[] {ti});
- wTree.showSelection();
- refreshButtons(
- dbMeta.getQuotedSchemaTableCombination(variables,
selectedSchema, selectedTable));
- }
+ private TreeItem addSynonymsBranch(String[] syn) {
+ if (syn == null) {
+ return null;
+ }
+ TreeItem tiSyn = new TreeItem(tiTree, SWT.NONE);
+ tiSyn.setImage(GuiResource.getInstance().getImageFolder());
+ tiSyn.setText(STRING_SYNONYMS);
+ for (String s : syn) {
+ TreeItem newSyn = new TreeItem(tiSyn, SWT.NONE);
+ newSyn.setImage(GuiResource.getInstance().getImageSynonym());
+ newSyn.setText(s);
+ }
+ return tiSyn;
+ }
- selectedTable = null;
+ private void selectInitiallySelectedTable(TreeItem tiTab, TreeItem tiView,
TreeItem tiSyn) {
+ if (StringUtils.isEmpty(selectedTable)) {
+ return;
+ }
+ TreeItem ti = findTreeItemForInitialSelection(tiTab, tiView, tiSyn);
+ if (ti != null) {
+ wTree.setSelection(new TreeItem[] {ti});
+ wTree.showSelection();
+ if (dbMeta != null) {
+ refreshButtons(
+ dbMeta.getQuotedSchemaTableCombination(variables, selectedSchema,
selectedTable));
}
-
- tiTree.setExpanded(true);
- } else {
- return false;
}
+ selectedTable = null;
+ }
- return true;
+ private TreeItem findTreeItemForInitialSelection(
+ TreeItem tiTab, TreeItem tiView, TreeItem tiSyn) {
+ TreeItem ti = null;
+ if (tiTab != null) {
+ ti = ConstUi.findTreeItem(tiTab, selectedSchema, selectedTable);
+ }
+ if (ti == null && tiView != null) {
+ ti = ConstUi.findTreeItem(tiView, selectedSchema, selectedTable);
+ }
+ if (ti == null) {
+ ti = ConstUi.findTreeItem(tiTree, selectedSchema, selectedTable);
+ }
+ if (ti == null && tiSyn != null) {
+ ti = ConstUi.findTreeItem(tiSyn, selectedSchema, selectedTable);
+ }
+ return ti;
}
private String getSchemaTable() {
TreeItem[] ti = wTree.getSelection();
- if (ti.length == 1) {
- // Get the parent.
- TreeItem parent = ti[0].getParentItem();
- if (parent != null) {
- String schemaName = parent.getText();
- String tableName = ti[0].getText();
-
- if (ti[0].getItemCount() == 0) // No children, only the tables
themselves...
- {
- String tab = null;
- if (schemaName.equalsIgnoreCase(STRING_TABLES)
- || schemaName.equalsIgnoreCase(STRING_VIEWS)
- || schemaName.equalsIgnoreCase(STRING_SYNONYMS)
- || (schemaName != null && schemaName.isEmpty())) {
- tab = tableName;
- } else {
- tab = dbMeta.getQuotedSchemaTableCombination(variables,
schemaName, tableName);
- }
- return tab;
- }
- }
+ if (ti.length != 1) {
+ return null;
+ }
+ TreeItem parentItem = ti[0].getParentItem();
+ if (parentItem == null) {
+ return null;
+ }
+ String parentLabel = parentItem.getText();
+ String itemText = ti[0].getText();
+ if (ti[0].getItemCount() != 0) {
+ return null;
+ }
+ if (isRootTableViewSynonymOrEmptyParent(parentLabel)) {
+ return itemText;
}
- return null;
+ if (dbMeta == null) {
+ return null;
+ }
+ return dbMeta.getQuotedSchemaTableCombination(variables, parentLabel,
itemText);
+ }
+
+ private static boolean isRootTableViewSynonymOrEmptyParent(String
parentLabel) {
+ return parentLabel.equalsIgnoreCase(STRING_TABLES)
+ || parentLabel.equalsIgnoreCase(STRING_VIEWS)
+ || parentLabel.equalsIgnoreCase(STRING_SYNONYMS)
+ || parentLabel.isEmpty();
}
public void setTreeMenu() {
@@ -698,7 +725,7 @@ public class DatabaseExplorerDialog extends Dialog {
new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- previewTable(table, false);
+ previewTable(table);
}
});
MenuItem miPrevN = new MenuItem(mTree, SWT.PUSH);
@@ -708,7 +735,7 @@ public class DatabaseExplorerDialog extends Dialog {
new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- previewTable(table, true);
+ previewTable(table);
}
});
MenuItem miCount = new MenuItem(mTree, SWT.PUSH);
@@ -771,37 +798,42 @@ public class DatabaseExplorerDialog extends Dialog {
}
}
- public void previewTable(String tableName, boolean asklimit) {
- int limit = 100;
- if (asklimit) {
- // Ask how many lines we should preview.
- String shellText = BaseMessages.getString(PKG,
"DatabaseExplorerDialog.PreviewTable.Title");
- String lineText = BaseMessages.getString(PKG,
"DatabaseExplorerDialog.PreviewTable.Message");
- EnterNumberDialog end = new EnterNumberDialog(shell, limit, shellText,
lineText);
- int samples = end.open();
- if (samples >= 0) {
- limit = samples;
- }
+ public void previewTable(String qualifiedTableName) {
+ PreviewTableSettingsDialog settingsDialog =
+ new PreviewTableSettingsDialog(shell, 100, variables, false);
+ PreviewTableSettingsDialog.Settings settings = settingsDialog.open();
+ if (settings == null) {
+ return;
}
+ int limit = settings.rowLimit;
+ int queryTimeoutSeconds = settings.queryTimeoutSeconds;
- String[] tableNameParts = tableName.split("\\.");
+ String[] tableNameParts = qualifiedTableName.split("\\.");
GetPreviewTableProgressDialog pd = null;
if (schemaName == null && tableNameParts.length == 2) {
// Table name contains both schema name and table name concatenated
pd =
new GetPreviewTableProgressDialog(
- shell, variables, dbMeta, tableNameParts[0], tableNameParts[1],
limit);
+ shell,
+ variables,
+ dbMeta,
+ tableNameParts[0],
+ tableNameParts[1],
+ limit,
+ queryTimeoutSeconds);
} else {
- pd = new GetPreviewTableProgressDialog(shell, variables, dbMeta, null,
tableName, limit);
+ pd =
+ new GetPreviewTableProgressDialog(
+ shell, variables, dbMeta, null, qualifiedTableName, limit,
queryTimeoutSeconds);
}
List<Object[]> rows = pd.open();
- if (rows != null) // otherwise an already shown error...
- {
+ if (pd.isPreviewSucceeded()) {
if (!rows.isEmpty()) {
PreviewRowsDialog prd =
- new PreviewRowsDialog(shell, variables, SWT.NONE, tableName,
pd.getRowMeta(), rows);
+ new PreviewRowsDialog(
+ shell, variables, SWT.NONE, qualifiedTableName,
pd.getRowMeta(), rows);
prd.open();
} else {
MessageBox mb = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
@@ -812,28 +844,27 @@ public class DatabaseExplorerDialog extends Dialog {
}
}
- public void showTable(String tableName) {
- String sql = dbMeta.getSqlQueryFields(tableName);
+ public void showTable(String qualifiedTableName) {
+ String sql = dbMeta.getSqlQueryFields(qualifiedTableName);
IRowMeta result = null;
- Database db = new Database(HopGui.getInstance().getLoggingObject(),
variables, dbMeta);
- try {
+ try (Database db = new Database(HopGui.getInstance().getLoggingObject(),
variables, dbMeta)) {
db.connect();
result = db.getQueryFields(sql, false);
} catch (Exception e) {
// Do Nothing
- } finally {
- db.disconnect();
}
if (result != null) {
TransformFieldsDialog sfd =
- new TransformFieldsDialog(shell, variables, SWT.NONE, tableName,
result);
+ new TransformFieldsDialog(shell, variables, SWT.NONE,
qualifiedTableName, result);
sfd.open();
}
}
- public void showCount(String tableName) {
+ public void showCount(String qualifiedTableName) {
String realTableName =
- (tableName.contains(".") ? tableName.substring(tableName.indexOf(".")
+ 1) : tableName);
+ (qualifiedTableName.contains(".")
+ ? qualifiedTableName.substring(qualifiedTableName.indexOf(".") + 1)
+ : qualifiedTableName);
GetTableSizeProgressDialog pd =
new GetTableSizeProgressDialog(shell, variables, dbMeta,
realTableName);
Long size = pd.open();
@@ -841,19 +872,23 @@ public class DatabaseExplorerDialog extends Dialog {
MessageBox mb = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
mb.setMessage(
BaseMessages.getString(
- PKG, "DatabaseExplorerDialog.TableSize.Message", tableName,
size.toString()));
+ PKG,
+ "DatabaseExplorerDialog.TableSize.Message",
+ qualifiedTableName,
+ size.toString()));
mb.setText(BaseMessages.getString(PKG,
"DatabaseExplorerDialog.TableSize.Title"));
mb.open();
}
}
- public void getDDL(String tableName) {
- Database db = new Database(loggingObject, variables, dbMeta);
- try {
+ public void getDDL(String qualifiedTableName) {
+ try (Database db = new Database(loggingObject, variables, dbMeta)) {
db.connect();
- IRowMeta r = db.getTableFields(tableName);
+ IRowMeta r = db.getTableFields(qualifiedTableName);
String realTableName =
- (tableName.contains(".") ?
tableName.substring(tableName.indexOf(".") + 1) : tableName);
+ (qualifiedTableName.contains(".")
+ ? qualifiedTableName.substring(qualifiedTableName.indexOf(".") +
1)
+ : qualifiedTableName);
String sql = db.getCreateTableStatement(realTableName, r, null, false,
null, true);
SqlEditor se = new SqlEditor(shell, SWT.NONE, variables, dbMeta,
dbcache, sql);
@@ -864,20 +899,19 @@ public class DatabaseExplorerDialog extends Dialog {
BaseMessages.getString(PKG, "Dialog.Error.Header"),
BaseMessages.getString(PKG,
"DatabaseExplorerDialog.Error.RetrieveLayout"),
dbe);
- } finally {
- db.disconnect();
}
}
- public void getDDLForOther(String tableName) {
+ public void getDDLForOther(String qualifiedTableName) {
if (databases != null) {
- Database database = new Database(loggingObject, variables, dbMeta);
- try {
+ try (Database database = new Database(loggingObject, variables, dbMeta))
{
database.connect();
String realTableName =
- (tableName.contains(".") ?
tableName.substring(tableName.indexOf(".") + 1) : tableName);
+ (qualifiedTableName.contains(".")
+ ? qualifiedTableName.substring(qualifiedTableName.indexOf(".")
+ 1)
+ : qualifiedTableName);
IRowMeta rowMeta = database.getTableFields(realTableName);
// Now select the other connection...
@@ -902,12 +936,14 @@ public class DatabaseExplorerDialog extends Dialog {
String target = enterSelectionDialog.open();
if (target != null) {
DatabaseMeta targetDatabaseMeta =
DatabaseMeta.findDatabase(databaseMetaList, target);
- Database targetDatabase = new Database(loggingObject, variables,
targetDatabaseMeta);
-
- String sql =
- targetDatabase.getCreateTableStatement(tableName, rowMeta, null,
false, null, true);
- SqlEditor sqlEditor = new SqlEditor(shell, SWT.NONE, variables,
dbMeta, dbcache, sql);
- sqlEditor.open();
+ try (Database targetDatabase =
+ new Database(loggingObject, variables, targetDatabaseMeta)) {
+ String sql =
+ targetDatabase.getCreateTableStatement(
+ qualifiedTableName, rowMeta, null, false, null, true);
+ SqlEditor sqlEditor = new SqlEditor(shell, SWT.NONE, variables,
dbMeta, dbcache, sql);
+ sqlEditor.open();
+ }
}
} catch (HopDatabaseException dbe) {
new ErrorDialog(
@@ -915,8 +951,6 @@ public class DatabaseExplorerDialog extends Dialog {
BaseMessages.getString(PKG, "Dialog.Error.Header"),
BaseMessages.getString(PKG, "DatabaseExplorerDialog.Error.GenDDL"),
dbe);
- } finally {
- database.disconnect();
}
} else {
MessageBox mb = new MessageBox(shell, SWT.NONE | SWT.ICON_INFORMATION);
@@ -927,19 +961,26 @@ public class DatabaseExplorerDialog extends Dialog {
}
}
- public void getSql(String tableName) {
+ public void getSql(String qualifiedTableName) {
String realTableName =
- (tableName.contains(".") ? tableName.substring(tableName.indexOf(".")
+ 1) : tableName);
+ (qualifiedTableName.contains(".")
+ ? qualifiedTableName.substring(qualifiedTableName.indexOf(".") + 1)
+ : qualifiedTableName);
SqlEditor sqlEditor =
new SqlEditor(
shell, SWT.NONE, variables, dbMeta, dbcache, "SELECT * FROM " +
realTableName);
sqlEditor.open();
}
- public void getTruncate(String activeSchemaTable) {
+ public void getTruncate(String truncateTableReference) {
SqlEditor sql =
new SqlEditor(
- shell, SWT.NONE, variables, dbMeta, dbcache, "-- TRUNCATE TABLE "
+ activeSchemaTable);
+ shell,
+ SWT.NONE,
+ variables,
+ dbMeta,
+ dbcache,
+ "-- TRUNCATE TABLE " + truncateTableReference);
sql.open();
}
@@ -953,35 +994,63 @@ public class DatabaseExplorerDialog extends Dialog {
dispose();
return;
}
- TreeItem[] ti = wTree.getSelection();
- if (ti.length == 1) {
- // Get the parent.
- String table = ti[0].getText();
- String[] path = ConstUi.getTreeStrings(ti[0]);
- if (path.length == 3
- && (STRING_TABLES.equalsIgnoreCase(path[1])
- || STRING_VIEWS.equalsIgnoreCase(path[1])
- || STRING_SYNONYMS.equalsIgnoreCase(path[1]))) {
- schemaName = null;
- tableName = table;
- String[] st = tableName.split("\\.", 2);
- if (st.length > 1) { // we have a dot in there and need to separate
- schemaName = st[0];
- tableName = st[1];
- }
- dispose();
- }
- if (path.length == 4 && (STRING_SCHEMAS.equals(path[1]) ||
STRING_CATALOG.equals(path[1]))) {
- if (splitSchemaAndTable) {
- schemaName = path[2];
- tableName = path[3];
- } else {
- schemaName = null;
- tableName = dbMeta.getQuotedSchemaTableCombination(variables,
path[2], path[3]);
- }
- dispose();
+ TreeItem[] selection = wTree.getSelection();
+ if (selection.length != 1) {
+ return;
+ }
+ TreeItem selected = selection[0];
+ if (completeOkFromThreePartPath(selected)) {
+ return;
+ }
+ completeOkFromFourPartPath(selected);
+ }
+
+ private boolean completeOkFromThreePartPath(TreeItem selected) {
+ String[] path = ConstUi.getTreeStrings(selected);
+ if (path.length != 3 || !isTablesViewsOrSynonymsPathSegment(path[1])) {
+ return false;
+ }
+ applyOkSelectionForFlatTableFolder(selected.getText());
+ dispose();
+ return true;
+ }
+
+ private void applyOkSelectionForFlatTableFolder(String selectedItemText) {
+ schemaName = null;
+ tableName = selectedItemText;
+ String[] st = tableName.split("\\.", 2);
+ if (st.length > 1) {
+ schemaName = st[0];
+ tableName = st[1];
+ }
+ }
+
+ private void completeOkFromFourPartPath(TreeItem selected) {
+ String[] path = ConstUi.getTreeStrings(selected);
+ if (path.length != 4 || !isCatalogOrSchemasPathSegment(path[1])) {
+ return;
+ }
+ if (splitSchemaAndTable) {
+ schemaName = path[2];
+ tableName = path[3];
+ } else {
+ if (dbMeta == null) {
+ return;
}
+ schemaName = null;
+ tableName = dbMeta.getQuotedSchemaTableCombination(variables, path[2],
path[3]);
}
+ dispose();
+ }
+
+ private static boolean isTablesViewsOrSynonymsPathSegment(String
pathSegment) {
+ return STRING_TABLES.equalsIgnoreCase(pathSegment)
+ || STRING_VIEWS.equalsIgnoreCase(pathSegment)
+ || STRING_SYNONYMS.equalsIgnoreCase(pathSegment);
+ }
+
+ private static boolean isCatalogOrSchemasPathSegment(String pathSegment) {
+ return STRING_SCHEMAS.equals(pathSegment) ||
STRING_CATALOG.equals(pathSegment);
}
public void openSchema(Event e) {
@@ -997,7 +1066,7 @@ public class DatabaseExplorerDialog extends Dialog {
if (!justLook) {
ok();
} else {
- previewTable(tableName, false);
+ previewTable(tableName);
}
}
}
@@ -1012,10 +1081,10 @@ public class DatabaseExplorerDialog extends Dialog {
}
/**
- * @param schemaName the schemaName to set
+ * @param schema the schema name to set
*/
- public void setSchemaName(String schemaName) {
- this.schemaName = schemaName;
+ public void setSchemaName(String schema) {
+ this.schemaName = schema;
}
/**
@@ -1026,10 +1095,10 @@ public class DatabaseExplorerDialog extends Dialog {
}
/**
- * @param tableName the tableName to set
+ * @param name the table name to set
*/
- public void setTableName(String tableName) {
- this.tableName = tableName;
+ public void setTableName(String name) {
+ this.tableName = name;
}
/**
@@ -1060,8 +1129,8 @@ public class DatabaseExplorerDialog extends Dialog {
this.selectedSchema = selectSchema;
}
- public void setSelectedSchemaAndTable(String schemaName, String tableName) {
- this.selectedSchema = schemaName;
- this.selectedTable = tableName;
+ public void setSelectedSchemaAndTable(String schema, String table) {
+ this.selectedSchema = schema;
+ this.selectedTable = table;
}
}
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetDatabaseInfoProgressDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetDatabaseInfoProgressDialog.java
index 7421bb0a83..f60e87d5b6 100644
---
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetDatabaseInfoProgressDialog.java
+++
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetDatabaseInfoProgressDialog.java
@@ -101,10 +101,16 @@ public class GetDatabaseInfoProgressDialog {
* @param e
*/
private void showErrorDialog(Exception e) {
+ String databaseName = databaseMeta == null ? "" : databaseMeta.getName();
+ String message =
+ BaseMessages.getString(PKG,
"GetDatabaseInfoProgressDialog.Error.Message")
+ + (databaseName.isEmpty()
+ ? ""
+ : " Unable to connect to database '" + databaseName + "'.");
new ErrorDialog(
shell,
BaseMessages.getString(PKG,
"GetDatabaseInfoProgressDialog.Error.Title"),
- BaseMessages.getString(PKG,
"GetDatabaseInfoProgressDialog.Error.Message"),
+ message,
e);
}
}
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetPreviewTableProgressDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetPreviewTableProgressDialog.java
index 719871afbc..2209552a3c 100644
---
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetPreviewTableProgressDialog.java
+++
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetPreviewTableProgressDialog.java
@@ -18,13 +18,14 @@
package org.apache.hop.ui.core.database.dialog;
import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
import java.util.List;
+import lombok.Getter;
import org.apache.hop.core.IProgressMonitor;
import org.apache.hop.core.IRunnableWithProgress;
import org.apache.hop.core.ProgressMonitorAdapter;
import org.apache.hop.core.database.Database;
import org.apache.hop.core.database.DatabaseMeta;
-import org.apache.hop.core.exception.HopDatabaseException;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.row.IRowMeta;
import org.apache.hop.core.variables.IVariables;
@@ -49,8 +50,15 @@ public class GetPreviewTableProgressDialog {
private DatabaseMeta dbMeta;
private String tableName;
private int limit;
- private List<Object[]> rows;
- private IRowMeta rowMeta;
+
+ /** JDBC {@link java.sql.Statement#setQueryTimeout(int)} in seconds; 0 =
driver default. */
+ private final int queryTimeoutSeconds;
+
+ private List<Object[]> rows = Collections.emptyList();
+ @Getter private IRowMeta rowMeta;
+
+ /** False when preview failed (error dialog was shown). */
+ @Getter private boolean previewSucceeded = true;
private Database db;
@@ -61,98 +69,127 @@ public class GetPreviewTableProgressDialog {
DatabaseMeta dbInfo,
String schemaName,
String tableName,
- int limit) {
+ int limit,
+ int queryTimeoutSeconds) {
this.shell = shell;
this.variables = variables;
this.dbMeta = dbInfo;
this.tableName = dbInfo.getQuotedSchemaTableCombination(variables,
schemaName, tableName);
this.limit = limit;
+ this.queryTimeoutSeconds = Math.max(0, queryTimeoutSeconds);
}
+ /**
+ * @return preview rows; empty when none or on failure. On failure {@link
#previewSucceeded} is
+ * false; callers typically use the Lombok-generated {@code
isPreviewSucceeded()} accessor.
+ */
public List<Object[]> open() {
+ previewSucceeded = true;
+ rows = Collections.emptyList();
if (!EnvironmentUtils.getInstance().isWeb()) {
- IRunnableWithProgress op =
- monitor -> {
- db = new Database(HopGui.getInstance().getLoggingObject(),
variables, dbMeta);
- try {
- db.connect();
-
- if (limit > 0) {
- db.setQueryLimit(limit);
- }
-
- rows = db.getFirstRows(tableName, limit, new
ProgressMonitorAdapter(monitor));
- rowMeta = db.getReturnRowMeta();
- } catch (HopException e) {
- throw new InvocationTargetException(
- e, "Couldn't find any rows because of an error :" +
e.toString());
- } finally {
- db.disconnect();
- }
- };
- try {
- final ProgressMonitorDialog pmd = new ProgressMonitorDialog(shell);
- // Run something in the background to cancel active database queries,
forecably if needed!
- Runnable run =
- () -> {
- IProgressMonitor monitor = pmd.getProgressMonitor();
- while (pmd.getShell() == null
- || (!pmd.getShell().isDisposed() && !monitor.isCanceled())) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- // Ignore
- }
- }
-
- if (monitor.isCanceled()) { // Disconnect and see what happens!
+ openDesktop();
+ } else {
+ openWeb();
+ }
+ return rows;
+ }
- try {
- db.cancelQuery();
- } catch (Exception e) {
- // Ignore
- }
- }
- };
- // Start the cancel tracker in the background!
- new Thread(run).start();
+ private void openDesktop() {
+ IRunnableWithProgress op = this::runPreviewWithResources;
+ try {
+ ProgressMonitorDialog pmd = new ProgressMonitorDialog(shell);
+ startCancelWatcherThread(pmd);
+ pmd.run(true, op);
+ } catch (InvocationTargetException e) {
+ previewSucceeded = false;
+ showErrorDialog(e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ previewSucceeded = false;
+ showErrorDialog(e);
+ }
+ }
- pmd.run(true, op);
- } catch (InvocationTargetException | InterruptedException e) {
- showErrorDialog(e);
- return null;
+ private void runPreviewWithResources(IProgressMonitor monitor) throws
InvocationTargetException {
+ try (Database database =
+ new Database(HopGui.getInstance().getLoggingObject(), variables,
dbMeta)) {
+ db = database;
+ database.connect();
+ if (queryTimeoutSeconds > 0) {
+ database.setStatementQueryTimeoutSeconds(queryTimeoutSeconds);
}
- } else {
- try {
- Cursor cursor = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
- shell.setCursor(cursor);
+ if (limit > 0) {
+ database.setQueryLimit(limit);
+ }
+ rows = database.getFirstRows(tableName, limit, new
ProgressMonitorAdapter(monitor));
+ rowMeta = database.getReturnRowMeta();
+ } catch (HopException e) {
+ throw new InvocationTargetException(
+ e, "Couldn't find any rows because of an error :" + e.toString());
+ } finally {
+ db = null;
+ }
+ }
- db = new Database(HopGui.getInstance().getLoggingObject(), variables,
dbMeta);
- db.connect();
+ private void startCancelWatcherThread(ProgressMonitorDialog pmd) {
+ Runnable run =
+ () -> {
+ IProgressMonitor monitor = pmd.getProgressMonitor();
+ while (pmd.getShell() == null
+ || (!pmd.getShell().isDisposed() && !monitor.isCanceled())) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ }
+ if (monitor.isCanceled()) {
+ try {
+ if (db != null) {
+ db.cancelQuery();
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ };
+ Thread cancelWatcher = new Thread(run, "Hop-DB-Preview-CancelWatcher");
+ cancelWatcher.setDaemon(true);
+ cancelWatcher.start();
+ }
+ private void openWeb() {
+ Cursor cursor = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
+ try {
+ shell.setCursor(cursor);
+ try (Database database =
+ new Database(HopGui.getInstance().getLoggingObject(), variables,
dbMeta)) {
+ db = database;
+ database.connect();
+ if (queryTimeoutSeconds > 0) {
+ database.setStatementQueryTimeoutSeconds(queryTimeoutSeconds);
+ }
if (limit > 0) {
- db.setQueryLimit(limit);
+ database.setQueryLimit(limit);
}
-
- rows = db.getFirstRows(tableName, limit, null);
- rowMeta = db.getReturnRowMeta();
-
- } catch (HopDatabaseException e) {
- showErrorDialog(e);
- return null;
- } finally {
- db.disconnect();
+ rows = database.getFirstRows(tableName, limit, null);
+ rowMeta = database.getReturnRowMeta();
}
+ } catch (HopException e) {
+ previewSucceeded = false;
+ showErrorDialog(e);
+ } finally {
+ if (!shell.isDisposed()) {
+ shell.setCursor(null);
+ }
+ if (!cursor.isDisposed()) {
+ cursor.dispose();
+ }
+ db = null;
}
-
- return rows;
}
- /**
- * Showing an error dialog
- *
- * @param e
- */
private void showErrorDialog(Exception e) {
new ErrorDialog(
shell,
@@ -160,11 +197,4 @@ public class GetPreviewTableProgressDialog {
BaseMessages.getString(PKG,
"GetPreviewTableProgressDialog.Error.Message"),
e);
}
-
- /**
- * @return the rowMeta
- */
- public IRowMeta getRowMeta() {
- return rowMeta;
- }
}
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/PreviewTableSettingsDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/PreviewTableSettingsDialog.java
new file mode 100644
index 0000000000..9ff9f9e532
--- /dev/null
+++
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/PreviewTableSettingsDialog.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.hop.ui.core.database.dialog;
+
+import org.apache.hop.core.Const;
+import org.apache.hop.core.util.EnvUtil;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.ui.core.PropsUi;
+import org.apache.hop.ui.core.dialog.BaseDialog;
+import org.apache.hop.ui.core.gui.GuiResource;
+import org.apache.hop.ui.core.gui.WindowProperty;
+import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/** Asks for row limit and JDBC query timeout before a database table preview.
*/
+public class PreviewTableSettingsDialog extends Dialog {
+ private static final Class<?> PKG = PreviewTableSettingsDialog.class;
+
+ /** Row limit and {@link java.sql.Statement#setQueryTimeout(int)} in seconds
(0 = no limit). */
+ public static final class Settings {
+ public final int rowLimit;
+ public final int queryTimeoutSeconds;
+
+ public Settings(int rowLimit, int queryTimeoutSeconds) {
+ this.rowLimit = rowLimit;
+ this.queryTimeoutSeconds = queryTimeoutSeconds;
+ }
+ }
+
+ private final Shell parentShell;
+ private final int initialRowLimit;
+
+ /** Hop variable space for default timeout in the dialog; may be {@code
null}. */
+ private final IVariables hopVariables;
+
+ /**
+ * When {@code true}, {@link #open()} on OK clones {@code hopVariables} and
sets {@code
+ * HOP_QUERY_PREVIEW_TIMEOUT} on the clone; use {@link
#getPreviewExecutionVariables()} for
+ * pipeline preview.
+ */
+ private final boolean forTableInputPipelinePreview;
+
+ private Shell shell;
+ private Text wRows;
+ private Text wTimeout;
+ private Settings result;
+ private Variables previewExecutionVariables;
+ private final PropsUi props = PropsUi.getInstance();
+
+ /**
+ * @param hopVariables context for resolving default timeout (may be null
unless {@code
+ * forTableInputPipelinePreview})
+ * @param forTableInputPipelinePreview when true, {@code hopVariables} is
required; OK builds
+ * {@link #getPreviewExecutionVariables()}
+ */
+ public PreviewTableSettingsDialog(
+ Shell parent,
+ int initialRowLimit,
+ IVariables hopVariables,
+ boolean forTableInputPipelinePreview) {
+ super(parent, SWT.NONE);
+ this.parentShell = parent;
+ this.initialRowLimit = initialRowLimit;
+ this.hopVariables = hopVariables;
+ this.forTableInputPipelinePreview = forTableInputPipelinePreview;
+ if (forTableInputPipelinePreview && hopVariables == null) {
+ throw new IllegalArgumentException(
+ "hopVariables is required when forTableInputPipelinePreview is
true");
+ }
+ }
+
+ private static int defaultTimeoutFieldSeconds(IVariables hopVariables) {
+ IVariables forResolve = hopVariables != null ? hopVariables : new
Variables();
+ String raw =
+ forResolve.getVariable(
+ Const.HOP_QUERY_PREVIEW_TIMEOUT,
+ EnvUtil.getSystemProperty(Const.HOP_QUERY_PREVIEW_TIMEOUT, "20"));
+ return Math.max(0, Const.toInt(forResolve.resolve(raw), 0));
+ }
+
+ /**
+ * @return preview settings, or {@code null} if the user cancelled
+ */
+ public Settings open() {
+ result = null;
+ previewExecutionVariables = null;
+ int initialQueryTimeoutSeconds = defaultTimeoutFieldSeconds(hopVariables);
+
+ shell = new Shell(parentShell, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL |
SWT.SHEET);
+ PropsUi.setLook(shell);
+ shell.setImage(GuiResource.getInstance().getImageHopUi());
+ shell.setText(BaseMessages.getString(PKG,
"PreviewTableSettingsDialog.Title"));
+
+ FormLayout formLayout = new FormLayout();
+ formLayout.marginWidth = BaseDialog.MARGIN_SIZE;
+ formLayout.marginHeight = BaseDialog.MARGIN_SIZE;
+ shell.setLayout(formLayout);
+
+ Label wlRows = new Label(shell, SWT.LEFT);
+ wlRows.setText(BaseMessages.getString(PKG,
"PreviewTableSettingsDialog.Rows.Label"));
+ PropsUi.setLook(wlRows);
+ FormData fdlRows = new FormData();
+ fdlRows.left = new FormAttachment(0, 0);
+ fdlRows.top = new FormAttachment(0, 0);
+ wlRows.setLayoutData(fdlRows);
+
+ wRows = new Text(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ PropsUi.setLook(wRows);
+ wRows.setText(Integer.toString(initialRowLimit));
+ FormData fdRows = new FormData();
+ fdRows.left = new FormAttachment(0, 0);
+ fdRows.top = new FormAttachment(wlRows, BaseDialog.LABEL_SPACING);
+ fdRows.right = new FormAttachment(100, 0);
+ wRows.setLayoutData(fdRows);
+
+ Label wlRowsHint = new Label(shell, SWT.LEFT);
+ wlRowsHint.setText(BaseMessages.getString(PKG,
"PreviewTableSettingsDialog.Rows.Hint"));
+ PropsUi.setLook(wlRowsHint);
+ FormData fdlRowsHint = new FormData();
+ fdlRowsHint.left = new FormAttachment(0, 0);
+ fdlRowsHint.top = new FormAttachment(wRows, BaseDialog.LABEL_SPACING);
+ fdlRowsHint.right = new FormAttachment(100, 0);
+ wlRowsHint.setLayoutData(fdlRowsHint);
+
+ Label wlTimeout = new Label(shell, SWT.LEFT);
+ wlTimeout.setText(BaseMessages.getString(PKG,
"PreviewTableSettingsDialog.Timeout.Label"));
+ PropsUi.setLook(wlTimeout);
+ FormData fdlTimeout = new FormData();
+ fdlTimeout.left = new FormAttachment(0, 0);
+ fdlTimeout.top = new FormAttachment(wlRowsHint,
BaseDialog.ELEMENT_SPACING);
+ wlTimeout.setLayoutData(fdlTimeout);
+
+ wTimeout = new Text(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ PropsUi.setLook(wTimeout);
+ wTimeout.setText(Integer.toString(initialQueryTimeoutSeconds));
+ FormData fdTimeout = new FormData();
+ fdTimeout.left = new FormAttachment(0, 0);
+ fdTimeout.top = new FormAttachment(wlTimeout, BaseDialog.LABEL_SPACING);
+ fdTimeout.right = new FormAttachment(100, 0);
+ wTimeout.setLayoutData(fdTimeout);
+
+ Label wlTimeoutHint = new Label(shell, SWT.LEFT);
+ wlTimeoutHint.setText(BaseMessages.getString(PKG,
"PreviewTableSettingsDialog.Timeout.Hint"));
+ PropsUi.setLook(wlTimeoutHint);
+ FormData fdlTimeoutHint = new FormData();
+ fdlTimeoutHint.left = new FormAttachment(0, 0);
+ fdlTimeoutHint.top = new FormAttachment(wTimeout,
BaseDialog.LABEL_SPACING);
+ fdlTimeoutHint.right = new FormAttachment(100, 0);
+ wlTimeoutHint.setLayoutData(fdlTimeoutHint);
+
+ Button wOk = new Button(shell, SWT.PUSH);
+ wOk.setText(BaseMessages.getString("System.Button.OK"));
+ Button wCancel = new Button(shell, SWT.PUSH);
+ wCancel.setText(BaseMessages.getString("System.Button.Cancel"));
+ BaseTransformDialog.positionBottomButtons(
+ shell, new Button[] {wOk, wCancel}, PropsUi.getMargin(),
wlTimeoutHint);
+
+ wOk.addListener(SWT.Selection, e -> ok());
+ wCancel.addListener(SWT.Selection, e -> cancel());
+ shell.setDefaultButton(wOk);
+
+ wRows.setFocus();
+ wRows.selectAll();
+
+ BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel(), false);
+ return result;
+ }
+
+ /**
+ * After a successful {@link #open()} with {@code
forTableInputPipelinePreview}, returns a clone
+ * of the constructor {@code hopVariables} with {@code
HOP_QUERY_PREVIEW_TIMEOUT} set from the
+ * dialog. Otherwise {@code null}.
+ */
+ public Variables getPreviewExecutionVariables() {
+ return previewExecutionVariables;
+ }
+
+ private void ok() {
+ try {
+ int rows = Integer.parseInt(wRows.getText().trim());
+ int timeout = Integer.parseInt(wTimeout.getText().trim());
+ result = new Settings(Math.max(0, rows), Math.max(0, timeout));
+ if (forTableInputPipelinePreview) {
+ previewExecutionVariables = new Variables();
+ previewExecutionVariables.copyFrom(hopVariables);
+ previewExecutionVariables.setVariable(
+ Const.HOP_QUERY_PREVIEW_TIMEOUT,
Integer.toString(result.queryTimeoutSeconds));
+ }
+ dispose();
+ } catch (NumberFormatException e) {
+ MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
+ mb.setMessage(BaseMessages.getString(PKG,
"PreviewTableSettingsDialog.Error.InvalidNumber"));
+ mb.setText(BaseMessages.getString(PKG,
"PreviewTableSettingsDialog.Error.Title"));
+ mb.open();
+ }
+ }
+
+ private void cancel() {
+ result = null;
+ previewExecutionVariables = null;
+ dispose();
+ }
+
+ private void dispose() {
+ props.setScreen(new WindowProperty(shell));
+ shell.dispose();
+ }
+}
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/SqlEditor.java
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/SqlEditor.java
index 67e475f344..e37639050d 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/SqlEditor.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/SqlEditor.java
@@ -17,9 +17,12 @@
package org.apache.hop.ui.core.database.dialog;
+import java.lang.reflect.InvocationTargetException;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.hop.core.Const;
import org.apache.hop.core.DbCache;
+import org.apache.hop.core.IProgressMonitor;
import org.apache.hop.core.Props;
import org.apache.hop.core.database.Database;
import org.apache.hop.core.database.DatabaseMeta;
@@ -32,6 +35,7 @@ import org.apache.hop.core.logging.LogChannel;
import org.apache.hop.core.logging.LoggingObjectType;
import org.apache.hop.core.logging.SimpleLoggingObject;
import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.util.EnvUtil;
import org.apache.hop.core.util.Utils;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.i18n.BaseMessages;
@@ -41,6 +45,7 @@ import org.apache.hop.ui.core.dialog.EnterTextDialog;
import org.apache.hop.ui.core.dialog.ErrorDialog;
import org.apache.hop.ui.core.dialog.MessageBox;
import org.apache.hop.ui.core.dialog.PreviewRowsDialog;
+import org.apache.hop.ui.core.dialog.ProgressMonitorDialog;
import org.apache.hop.ui.core.gui.GuiResource;
import org.apache.hop.ui.core.gui.WindowProperty;
import org.apache.hop.ui.core.widget.SQLStyledTextComp;
@@ -86,6 +91,12 @@ public class SqlEditor {
private final IVariables variables;
+ /**
+ * Holds the active database connection so the cancel watcher can call
cancelQuery(). Use {@link
+ * AtomicReference} for safe publication between the worker and
cancel-watcher threads.
+ */
+ private final AtomicReference<Database> activeDb = new AtomicReference<>();
+
public SqlEditor(
Shell parent, int style, IVariables variables, DatabaseMeta ci, DbCache
dbc, String sql) {
props = PropsUi.getInstance();
@@ -289,120 +300,19 @@ public class SqlEditor {
return;
}
+ String sqlScript =
+ Utils.isEmpty(wScript.getSelectionText()) ? wScript.getText() :
wScript.getSelectionText();
StringBuilder message = new StringBuilder();
+ ProgressMonitorDialog pmd = new ProgressMonitorDialog(shell);
- try (Database db = new Database(loggingObject, variables, databaseMeta)) {
- db.connect();
- String sqlScript =
- Utils.isEmpty(wScript.getSelectionText())
- ? wScript.getText()
- : wScript.getSelectionText();
-
- // Multiple statements in the script need to be split into individual
- // executable statements
- List<SqlScriptStatement> statements =
- databaseMeta.getIDatabase().getSqlScriptStatements(sqlScript +
Const.CR);
-
- int nrstats = 0;
- for (SqlScriptStatement sql : statements) {
- if (sql.isQuery()) {
- // A Query
- log.logDetailed("launch SELECT statement: " + Const.CR + sql);
-
- nrstats++;
- try {
- List<Object[]> rows = db.getRows(sql.getStatement(), 1000);
- IRowMeta rowMeta = db.getReturnRowMeta();
- if (!rows.isEmpty()) {
- PreviewRowsDialog prd =
- new PreviewRowsDialog(
- shell,
- variables,
- SWT.NONE,
- BaseMessages.getString(
- PKG, "SQLEditor.ResultRows.Title",
Integer.toString(nrstats)),
- rowMeta,
- rows);
- prd.open();
- } else {
- MessageBox mb = new MessageBox(shell, SWT.ICON_INFORMATION |
SWT.OK);
- mb.setMessage(BaseMessages.getString(PKG,
"SQLEditor.NoRows.Message", sql));
- mb.setText(BaseMessages.getString(PKG,
"SQLEditor.NoRows.Title"));
- mb.open();
- }
- } catch (HopDatabaseException dbe) {
- new ErrorDialog(
- shell,
- BaseMessages.getString(PKG, "SQLEditor.ErrorExecSQL.Title"),
- BaseMessages.getString(PKG, "SQLEditor.ErrorExecSQL.Message",
sql),
- dbe);
- }
- } else {
- log.logDetailed("launch DDL statement: " + Const.CR + sql);
+ startSqlCancelWatcherVirtualThread(pmd);
- // A DDL statement
- nrstats++;
- int startLogLine = HopLogStore.getLastBufferLineNr();
- try {
-
- log.logDetailed("Executing SQL: " + Const.CR + sql);
- db.execStatement(sql.getStatement());
-
- message.append(BaseMessages.getString(PKG,
"SQLEditor.Log.SQLExecuted", sql));
- message.append(Const.CR);
-
- // Clear the database cache, in case we're using one...
- if (dbcache != null) {
- dbcache.clear(databaseMeta.getName());
- }
-
- // mark the statement in green in the dialog...
- //
- sql.setOk(true);
- } catch (Exception dbe) {
- sql.setOk(false);
- String error =
- BaseMessages.getString(PKG, "SQLEditor.Log.SQLExecError", sql,
dbe.toString());
- message.append(error).append(Const.CR);
- ErrorDialog dialog =
- new ErrorDialog(
- shell,
- BaseMessages.getString(PKG,
"SQLEditor.ErrorExecSQL.Title"),
- error,
- dbe,
- true);
- if (dialog.isCancelled()) {
- break;
- }
- } finally {
- int endLogLine = HopLogStore.getLastBufferLineNr();
- sql.setLoggingText(
- HopLogStore.getAppender()
- .getLogBufferFromTo(db.getLogChannelId(), true,
startLogLine, endLogLine)
- .toString());
- sql.setComplete(true);
- refreshExecutionResults();
- }
- }
+ try {
+ pmd.run(true, monitor -> runSqlScriptWithMonitor(monitor, databaseMeta,
sqlScript, message));
+ } catch (InterruptedException | InvocationTargetException e) {
+ if (e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
}
- message.append(
- BaseMessages.getString(PKG, "SQLEditor.Log.StatsExecuted",
Integer.toString(nrstats)));
-
- message.append(Const.CR);
- } catch (HopDatabaseException dbe) {
- MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
- String error =
- BaseMessages.getString(
- PKG,
- "SQLEditor.Error.CouldNotConnect.Message",
- (connection == null ? "" : connection.getName()),
- dbe.getMessage());
- message.append(error).append(Const.CR);
- mb.setMessage(error);
- mb.setText(BaseMessages.getString(PKG,
"SQLEditor.Error.CouldNotConnect.Title"));
- mb.open();
- } finally {
- refreshExecutionResults();
}
EnterTextDialog dialog =
@@ -415,6 +325,207 @@ public class SqlEditor {
dialog.open();
}
+ private void startSqlCancelWatcherVirtualThread(ProgressMonitorDialog pmd) {
+ Thread.ofVirtual()
+ .name("Hop-SqlEditor-CancelWatcher")
+ .start(
+ () -> {
+ IProgressMonitor monitor = pmd.getProgressMonitor();
+ while (pmd.getShell() == null
+ || (!pmd.getShell().isDisposed() && !monitor.isCanceled())) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+ if (monitor.isCanceled()) {
+ Database db = activeDb.get();
+ if (db != null) {
+ try {
+ db.cancelQuery();
+ } catch (Exception ignored) {
+ // ignore
+ }
+ }
+ }
+ });
+ }
+
+ private void runSqlScriptWithMonitor(
+ IProgressMonitor monitor,
+ DatabaseMeta databaseMeta,
+ String sqlScript,
+ StringBuilder message) {
+ monitor.beginTask(BaseMessages.getString(PKG,
"SQLEditor.Monitor.Execute"), 100);
+ String raw =
+ variables.getVariable(
+ Const.HOP_QUERY_PREVIEW_TIMEOUT,
+ EnvUtil.getSystemProperty(Const.HOP_QUERY_PREVIEW_TIMEOUT, "0"));
+ int timeoutSeconds = Math.max(0, Const.toInt(variables.resolve(raw), 0));
+ try (Database db = new Database(loggingObject, variables, databaseMeta)) {
+ activeDb.set(db);
+ if (timeoutSeconds > 0) {
+ db.setStatementQueryTimeoutSeconds(timeoutSeconds);
+ }
+ db.connect();
+
+ int[] nrStats = {0};
+ runScriptStatementsLoop(db, databaseMeta, sqlScript, message, monitor,
nrStats);
+ message.append(
+ BaseMessages.getString(PKG, "SQLEditor.Log.StatsExecuted",
Integer.toString(nrStats[0])));
+ message.append(Const.CR);
+ } catch (HopDatabaseException dbe) {
+ handleConnectFailure(dbe, message);
+ } finally {
+ activeDb.set(null);
+ shell.getDisplay().asyncExec(this::refreshExecutionResults);
+ monitor.done();
+ }
+ }
+
+ private void runScriptStatementsLoop(
+ Database db,
+ DatabaseMeta databaseMeta,
+ String sqlScript,
+ StringBuilder message,
+ IProgressMonitor monitor,
+ int[] nrStats) {
+ List<SqlScriptStatement> statements =
+ databaseMeta.getIDatabase().getSqlScriptStatements(sqlScript +
Const.CR);
+ boolean aborted = false;
+ for (SqlScriptStatement sql : statements) {
+ if (aborted || monitor.isCanceled()) {
+ break;
+ }
+ if (sql.isQuery()) {
+ nrStats[0]++;
+ executeSelectStatement(db, sql, nrStats[0], message);
+ } else {
+ nrStats[0]++;
+ aborted = executeDdlStatement(db, sql, databaseMeta, message);
+ }
+ }
+ }
+
+ private void executeSelectStatement(
+ Database db, SqlScriptStatement sql, int statNr, StringBuilder message) {
+ log.logDetailed("launch SELECT statement: " + Const.CR + sql);
+ try {
+ List<Object[]> rows = db.getRows(sql.getStatement(), 1000);
+ IRowMeta rowMeta = db.getReturnRowMeta();
+ if (!rows.isEmpty()) {
+ shell
+ .getDisplay()
+ .syncExec(
+ () ->
+ new PreviewRowsDialog(
+ shell,
+ variables,
+ SWT.NONE,
+ BaseMessages.getString(
+ PKG, "SQLEditor.ResultRows.Title",
Integer.toString(statNr)),
+ rowMeta,
+ rows)
+ .open());
+ } else {
+ shell
+ .getDisplay()
+ .syncExec(
+ () -> {
+ MessageBox mb = new MessageBox(shell, SWT.ICON_INFORMATION |
SWT.OK);
+ mb.setMessage(BaseMessages.getString(PKG,
"SQLEditor.NoRows.Message", sql));
+ mb.setText(BaseMessages.getString(PKG,
"SQLEditor.NoRows.Title"));
+ mb.open();
+ });
+ }
+ } catch (HopDatabaseException dbe) {
+ handleSelectExecutionFailure(dbe, sql, message);
+ }
+ }
+
+ private void handleSelectExecutionFailure(
+ HopDatabaseException dbe, SqlScriptStatement sql, StringBuilder message)
{
+ shell
+ .getDisplay()
+ .syncExec(
+ () ->
+ new ErrorDialog(
+ shell,
+ BaseMessages.getString(PKG,
"SQLEditor.ErrorExecSQL.Title"),
+ BaseMessages.getString(PKG,
"SQLEditor.ErrorExecSQL.Message", sql),
+ dbe));
+ }
+
+ private boolean executeDdlStatement(
+ Database db, SqlScriptStatement sql, DatabaseMeta databaseMeta,
StringBuilder message) {
+ log.logDetailed("launch DDL statement: " + Const.CR + sql);
+ int startLogLine = HopLogStore.getLastBufferLineNr();
+ try {
+ log.logDetailed("Executing SQL: " + Const.CR + sql);
+ db.execStatement(sql.getStatement());
+ message.append(BaseMessages.getString(PKG, "SQLEditor.Log.SQLExecuted",
sql));
+ message.append(Const.CR);
+ if (dbcache != null) {
+ dbcache.clear(databaseMeta.getName());
+ }
+ sql.setOk(true);
+ return false;
+ } catch (Exception dbe) {
+ return handleDdlExecutionFailure(dbe, sql, message);
+ } finally {
+ int endLogLine = HopLogStore.getLastBufferLineNr();
+ sql.setLoggingText(
+ HopLogStore.getAppender()
+ .getLogBufferFromTo(db.getLogChannelId(), true, startLogLine,
endLogLine)
+ .toString());
+ sql.setComplete(true);
+ shell.getDisplay().asyncExec(this::refreshExecutionResults);
+ }
+ }
+
+ private boolean handleDdlExecutionFailure(
+ Exception dbe, SqlScriptStatement sql, StringBuilder message) {
+ sql.setOk(false);
+ String error = BaseMessages.getString(PKG, "SQLEditor.Log.SQLExecError",
sql, dbe.toString());
+ message.append(error).append(Const.CR);
+ boolean[] cancelLoop = {false};
+ shell
+ .getDisplay()
+ .syncExec(
+ () -> {
+ ErrorDialog dialog =
+ new ErrorDialog(
+ shell,
+ BaseMessages.getString(PKG,
"SQLEditor.ErrorExecSQL.Title"),
+ error,
+ dbe,
+ true);
+ cancelLoop[0] = dialog.isCancelled();
+ });
+ return cancelLoop[0];
+ }
+
+ private void handleConnectFailure(HopDatabaseException dbe, StringBuilder
message) {
+ String error =
+ BaseMessages.getString(
+ PKG,
+ "SQLEditor.Error.CouldNotConnect.Message",
+ (connection == null ? "" : connection.getName()),
+ dbe.getMessage());
+ message.append(error).append(Const.CR);
+ shell
+ .getDisplay()
+ .syncExec(
+ () -> {
+ MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
+ mb.setMessage(error);
+ mb.setText(BaseMessages.getString(PKG,
"SQLEditor.Error.CouldNotConnect.Title"));
+ mb.open();
+ });
+ }
+
/** During or after an execution we will mark regions of the SQL editor
dialog in green or red. */
protected void refreshExecutionResults() {
wScript.redraw();
diff --git a/ui/src/main/java/org/apache/hop/ui/core/dialog/BaseDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/dialog/BaseDialog.java
index 4543ec9710..9a8e38eafb 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/dialog/BaseDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/BaseDialog.java
@@ -57,6 +57,7 @@ import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
@@ -748,17 +749,41 @@ public abstract class BaseDialog extends Dialog {
*/
public static void defaultShellHandling(
Shell shell, Consumer<Void> okConsumer, Consumer<Void> cancelConsumer) {
+ defaultShellHandling(shell, okConsumer, cancelConsumer, true);
+ }
+
+ /**
+ * Like {@link #defaultShellHandling(Shell, Consumer, Consumer)} but
controls minimum shell size.
+ *
+ * @param useStandardMinimumSize when {@code true}, keeps the legacy minimum
(650x250) suited to
+ * large editor dialogs; when {@code false}, minimum size follows the
laid-out content so
+ * small prompts are not stretched with empty space.
+ */
+ public static void defaultShellHandling(
+ Shell shell,
+ Consumer<Void> okConsumer,
+ Consumer<Void> cancelConsumer,
+ boolean useStandardMinimumSize) {
defaultShellHandling(
shell,
okConsumer,
() -> {
cancelConsumer.accept(null);
return true;
- });
+ },
+ useStandardMinimumSize);
}
public static void defaultShellHandling(
Shell shell, Consumer<Void> okConsumer, Supplier<Boolean>
cancelSupplier) {
+ defaultShellHandling(shell, okConsumer, cancelSupplier, true);
+ }
+
+ public static void defaultShellHandling(
+ Shell shell,
+ Consumer<Void> okConsumer,
+ Supplier<Boolean> cancelSupplier,
+ boolean useStandardMinimumSize) {
// If the shell is closed, cancel the dialog
//
@@ -783,7 +808,13 @@ public abstract class BaseDialog extends Dialog {
//
addSpacesOnTabs(shell);
- shell.setMinimumSize(650, 250);
+ if (useStandardMinimumSize) {
+ shell.setMinimumSize(650, 250);
+ } else {
+ shell.layout(true, true);
+ Point natural = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ shell.setMinimumSize(Math.max(1, natural.x), Math.max(1, natural.y));
+ }
// Set the size as well...
//
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/dialog/EnterNumberDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/dialog/EnterNumberDialog.java
index d192a691f4..f30606417c 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/dialog/EnterNumberDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/EnterNumberDialog.java
@@ -18,13 +18,12 @@
package org.apache.hop.ui.core.dialog;
import org.apache.commons.lang3.StringUtils;
-import org.apache.hop.core.Const;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.ui.core.ConstUi;
-import org.apache.hop.ui.core.FormDataBuilder;
import org.apache.hop.ui.core.PropsUi;
import org.apache.hop.ui.core.gui.GuiResource;
import org.apache.hop.ui.core.gui.WindowProperty;
+import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
@@ -187,32 +186,30 @@ public class EnterNumberDialog extends Dialog {
lastControl = wlFrom;
}
- // Some buttons
+ // Buttons aligned to the bottom of the shell
+ //
wOk = new Button(shell, SWT.PUSH);
- wOk.setText(BaseMessages.getString(PKG, "System.Button.OK"));
+ wOk.setText(BaseMessages.getString("System.Button.OK"));
if (!hideCancelButton) {
wCancel = new Button(shell, SWT.PUSH);
- wCancel.setText(BaseMessages.getString(PKG, "System.Button.Cancel"));
+ wCancel.setText(BaseMessages.getString("System.Button.Cancel"));
+ BaseTransformDialog.positionBottomButtons(
+ shell, new Button[] {wOk, wCancel}, PropsUi.getMargin(),
lastControl);
+ } else {
+ wCancel = null;
+ BaseTransformDialog.positionBottomButtons(
+ shell, new Button[] {wOk}, PropsUi.getMargin(), lastControl);
}
- wCancel.setLayoutData(
- new FormDataBuilder()
- .top(lastControl, BaseDialog.ELEMENT_SPACING * 2)
- .right(100, 0)
- .result());
- wOk.setLayoutData(
- new FormDataBuilder()
- .top(lastControl, BaseDialog.ELEMENT_SPACING * 2)
- .right(wCancel, Const.isOSX() ? 0 : -BaseDialog.LABEL_SPACING)
- .result());
-
- // Add listeners
wOk.addListener(SWT.Selection, e -> ok());
- wCancel.addListener(SWT.Selection, e -> cancel());
+ if (wCancel != null) {
+ wCancel.addListener(SWT.Selection, e -> cancel());
+ }
+ shell.setDefaultButton(wOk);
getData();
- BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
+ BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel(), false);
}
public void dispose() {
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/dialog/PreviewRowsDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/dialog/PreviewRowsDialog.java
index 0441438820..6f9fdbd6f2 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/dialog/PreviewRowsDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/PreviewRowsDialog.java
@@ -154,6 +154,24 @@ public class PreviewRowsDialog {
}
public void open() {
+ if (title == null) {
+ title = BaseMessages.getString(PKG, "PreviewRowsDialog.Title");
+ }
+ if (message == null) {
+ message = BaseMessages.getString(PKG, "PreviewRowsDialog.Header",
transformName);
+ }
+
+ // Empty static preview: show a single modal on the real parent. Parenting
a message to the
+ // preview shell here caused odd modality (that shell is created but never
opened).
+ if (!dynamic && Utils.isEmpty(buffer)) {
+ MessageBox mb =
+ new MessageBox(parentShell, SWT.OK | SWT.ICON_INFORMATION |
SWT.APPLICATION_MODAL);
+ mb.setText(title);
+ mb.setMessage(BaseMessages.getString(PKG,
"PreviewRowsDialog.NoRows.Message"));
+ mb.open();
+ return;
+ }
+
shell = new Shell(parentShell, style);
PropsUi props = PropsUi.getInstance();
@@ -164,13 +182,6 @@ public class PreviewRowsDialog {
formLayout.marginWidth = PropsUi.getFormMargin();
formLayout.marginHeight = PropsUi.getFormMargin();
- if (title == null) {
- title = BaseMessages.getString(PKG, "PreviewRowsDialog.Title");
- }
- if (message == null) {
- message = BaseMessages.getString(PKG, "PreviewRowsDialog.Header",
transformName);
- }
-
if (buffer != null) {
message += " " + BaseMessages.getString(PKG, "PreviewRowsDialog.NrRows",
"" + buffer.size());
}
@@ -275,19 +286,6 @@ public class PreviewRowsDialog {
rowMeta.addValueMeta(new ValueMetaString("<waiting for rows>"));
waitingForRows = true;
}
- if (!dynamic && Utils.isEmpty(buffer)) {
- // Mmm, if we don't get any rows in the buffer: show a dialog box.
- ShowMessageDialog dialog =
- new ShowMessageDialog(
- shell,
- SWT.OK | SWT.ICON_WARNING,
- BaseMessages.getString(PKG, "PreviewRowsDialog.NoRows.Text"),
- BaseMessages.getString(PKG, "PreviewRowsDialog.NoRows.Message"));
- dialog.open();
- shell.dispose();
- return true;
- }
-
ColumnInfo[] columns = new ColumnInfo[rowMeta.size()];
for (int i = 0; i < rowMeta.size(); i++) {
IValueMeta valueMeta = rowMeta.getValueMeta(i);
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/dialog/ProgressMonitorDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/dialog/ProgressMonitorDialog.java
index 153353ef03..865955ba30 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/dialog/ProgressMonitorDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/ProgressMonitorDialog.java
@@ -18,6 +18,7 @@
package org.apache.hop.ui.core.dialog;
import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hop.core.Const;
import org.apache.hop.core.IProgressMonitor;
import org.apache.hop.core.IRunnableWithProgress;
@@ -69,15 +70,29 @@ public class ProgressMonitorDialog {
public void run(boolean cancelable, IRunnableWithProgress runnable)
throws InvocationTargetException, InterruptedException {
+ createModalShell(cancelable);
+ int margin = layoutProgressWidgets();
+ addCancelButtonIfNeeded(cancelable, margin);
+ BaseTransformDialog.setSize(shell);
+ addShellCloseHandler();
+ shell.open();
- PropsUi props = PropsUi.getInstance();
+ Cursor oldCursor = shell.getCursor();
+ parent.setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
+ try {
+ scheduleBackgroundWork(runnable);
+ pumpDisplayUntilShellDisposed();
+ } finally {
+ parent.setCursor(oldCursor);
+ }
+ }
+ private void createModalShell(boolean cancelable) {
shell =
new Shell(parent, SWT.RESIZE | SWT.APPLICATION_MODAL | (cancelable ?
SWT.CLOSE : SWT.NONE));
shell.setText(BaseMessages.getString(PKG,
"ProgressMonitorDialog.Shell.Title"));
shell.setImage(GuiResource.getInstance().getImageHopUi());
PropsUi.setLook(shell);
-
display = shell.getDisplay();
FormLayout formLayout = new FormLayout();
@@ -85,14 +100,12 @@ public class ProgressMonitorDialog {
formLayout.marginLeft = PropsUi.getFormMargin();
formLayout.marginRight = PropsUi.getFormMargin();
formLayout.marginBottom = PropsUi.getFormMargin();
+ shell.setLayout(formLayout);
+ }
+ private int layoutProgressWidgets() {
int margin = PropsUi.getMargin();
- shell.setLayout(formLayout);
-
- // An image at the top right...
- // TODO: rotate this image somehow
- //
Label wlImage = new Label(shell, SWT.NONE);
wlImage.setImage(GuiResource.getInstance().getImageHopUi());
PropsUi.setLook(wlImage);
@@ -129,69 +142,69 @@ public class ProgressMonitorDialog {
fdProgressBar.top = new FormAttachment(wlSubTask, margin);
wProgressBar.setLayoutData(fdProgressBar);
- if (cancelable) {
- Button wCancel = new Button(shell, SWT.PUSH);
- wCancel.setText(BaseMessages.getString("System.Button.Cancel"));
- wCancel.addListener(SWT.Selection, e -> isCancelled = true);
- BaseTransformDialog.positionBottomButtons(
- shell, new Button[] {wCancel}, margin, wProgressBar);
- }
+ return margin;
+ }
- BaseTransformDialog.setSize(shell);
+ private void addCancelButtonIfNeeded(boolean cancelable, int margin) {
+ if (!cancelable) {
+ return;
+ }
+ Button wCancel = new Button(shell, SWT.PUSH);
+ wCancel.setText(BaseMessages.getString("System.Button.Cancel"));
+ wCancel.addListener(
+ SWT.Selection,
+ e -> {
+ isCancelled = true;
+ if (display != null && !display.isDisposed()) {
+ display.wake();
+ }
+ });
+ BaseTransformDialog.positionBottomButtons(shell, new Button[] {wCancel},
margin, wProgressBar);
+ }
+ private void addShellCloseHandler() {
shell.addListener(
SWT.Close,
e -> {
e.doit = false;
isCancelled = true;
+ if (display != null && !display.isDisposed()) {
+ display.wake();
+ }
});
+ }
- shell.open();
-
- Cursor oldCursor = shell.getCursor();
-
- parent.setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
+ private void scheduleBackgroundWork(IRunnableWithProgress runnable) {
+ Runnable launch =
+ () -> new Thread(() -> runMonitoredWork(runnable),
"Hop-ProgressMonitor-Worker").start();
+ display.asyncExec(launch);
+ }
- // Execute the long running task
- //
- Runnable longRunnable =
- () ->
- // Always do the work in a different thread...
- // This keeps the shell updating properly as long as
- // display.asyncExec is used
- //
- new Thread(
- () -> {
- try {
- runnable.run(progressMonitor);
- } catch (InvocationTargetException e) {
- targetException = e;
- } catch (InterruptedException e) {
- interruptedException = e;
- }
- })
- .start();
- display.asyncExec(longRunnable);
-
- // Handle the event loop until we're done with this shell...
- //
+ private void runMonitoredWork(IRunnableWithProgress runnable) {
try {
- while (!shell.isDisposed()) {
- if (interruptedException != null) {
- dispose();
- throw interruptedException;
- }
- if (targetException != null) {
- dispose();
- throw targetException;
- }
-
- if (!display.readAndDispatch()) {
- display.sleep();
- }
+ runnable.run(progressMonitor);
+ } catch (InvocationTargetException e) {
+ targetException = e;
+ } catch (InterruptedException e) {
+ interruptedException = e;
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private void pumpDisplayUntilShellDisposed()
+ throws InvocationTargetException, InterruptedException {
+ while (!shell.isDisposed()) {
+ if (interruptedException != null) {
+ dispose();
+ throw interruptedException;
+ }
+ if (targetException != null) {
+ dispose();
+ throw targetException;
+ }
+ if (!display.readAndDispatch()) {
+ display.sleep();
}
- } finally {
- parent.setCursor(oldCursor);
}
}
@@ -200,17 +213,59 @@ public class ProgressMonitorDialog {
}
private void dispose() {
- display.asyncExec(
+ if (display == null || display.isDisposed()) {
+ return;
+ }
+
+ Runnable disposeRunnable =
() -> {
- PropsUi.getInstance().setScreen(new WindowProperty(shell));
- shell.dispose();
- });
+ if (shell == null || shell.isDisposed()) {
+ return;
+ }
+ try {
+ PropsUi.getInstance().setScreen(new WindowProperty(shell));
+ } catch (Exception e) {
+ // Ignore race condition when shell is already being torn down.
+ }
+ if (!shell.isDisposed()) {
+ shell.dispose();
+ }
+ };
+
+ if (Display.getCurrent() == display) {
+ disposeRunnable.run();
+ } else {
+ display.syncExec(disposeRunnable);
+ }
}
private class ProgressMonitor implements IProgressMonitor {
+ /** IProgressMonitor.worked() reports deltas; we accumulate for the SWT
progress bar. */
+ private int workedAccumulated;
+
+ private final AtomicBoolean workedFlushScheduled = new
AtomicBoolean(false);
+
+ private final Runnable workedFlushRunnable =
+ () -> {
+ workedFlushScheduled.set(false);
+ synchronized (shell) {
+ if (shell.isDisposed() || wlTask.isDisposed()) {
+ return;
+ }
+ try {
+ int max = wProgressBar.getMaximum();
+ int selection = Math.min(workedAccumulated, max > 0 ? max :
workedAccumulated);
+ wProgressBar.setSelection(selection);
+ } catch (Exception e) {
+ // Ignore race condition
+ }
+ }
+ };
+
@Override
public void beginTask(String message, int nrWorks) {
+ workedAccumulated = 0;
display.asyncExec(
() -> {
synchronized (shell) {
@@ -220,7 +275,8 @@ public class ProgressMonitorDialog {
try {
wlTask.setText(Const.NVL(message, ""));
wProgressBar.setMaximum(nrWorks);
- } catch (Throwable e) {
+ wProgressBar.setSelection(0);
+ } catch (Exception e) {
// Ignore race condition
}
}
@@ -237,7 +293,7 @@ public class ProgressMonitorDialog {
}
try {
wlSubTask.setText(Const.NVL(message, ""));
- } catch (Throwable e) {
+ } catch (Exception e) {
// Ignore race condition
}
}
@@ -251,23 +307,19 @@ public class ProgressMonitorDialog {
@Override
public void worked(int nrWorks) {
- display.asyncExec(
- () -> {
- synchronized (shell) {
- if (shell.isDisposed() || wlTask.isDisposed()) {
- return;
- }
- try {
- wProgressBar.setSelection(nrWorks);
- } catch (Throwable e) {
- // Ignore race condition
- }
- }
- });
+ if (nrWorks <= 0) {
+ return;
+ }
+ workedAccumulated += nrWorks;
+ if (workedFlushScheduled.compareAndSet(false, true)) {
+ display.asyncExec(workedFlushRunnable);
+ }
}
@Override
public void done() {
+ workedAccumulated = 0;
+ workedFlushScheduled.set(false);
dispose();
}
@@ -281,7 +333,7 @@ public class ProgressMonitorDialog {
}
try {
wlTask.setText(Const.NVL(taskName, ""));
- } catch (Throwable e) {
+ } catch (Exception e) {
// Ignore race condition
}
}
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/metadata/MetadataEditorDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/metadata/MetadataEditorDialog.java
index ecf7cdf5ca..9ff48c42ec 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/metadata/MetadataEditorDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/metadata/MetadataEditorDialog.java
@@ -79,6 +79,7 @@ public class MetadataEditorDialog extends Dialog implements
IMetadataDialog {
buttons.add(wCancel);
BaseTransformDialog.positionBottomButtons(
shell, buttons.toArray(new Button[0]), PropsUi.getMargin(), null);
+ shell.setDefaultButton(wOk);
Button wHelp = editor.createHelpButton(shell);
FormData fdHelp = new FormData();
diff --git
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_en_US.properties
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_en_US.properties
index efdc5a19c9..f30c00d7bc 100644
---
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_en_US.properties
+++
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_en_US.properties
@@ -32,6 +32,13 @@ DatabaseExplorerDialog.NoRows.Message=The table contains no
rows
DatabaseExplorerDialog.NoRows.Title=No rows
DatabaseExplorerDialog.PreviewTable.Message=Number of lines to preview (0
means all lines)
DatabaseExplorerDialog.PreviewTable.Title=Preview limit
+PreviewTableSettingsDialog.Error.InvalidNumber=Enter valid whole numbers for
rows and timeout.
+PreviewTableSettingsDialog.Error.Title=Invalid input
+PreviewTableSettingsDialog.Rows.Hint=0 means no row limit on the query (fetch
all rows).
+PreviewTableSettingsDialog.Rows.Label=Number of rows to preview
+PreviewTableSettingsDialog.Timeout.Hint=0 means no JDBC statement timeout
(driver default). Otherwise the server should abort the query after this many
seconds. Optional default for this field: variable HOP_QUERY_PREVIEW_TIMEOUT
(see Variables documentation).
+PreviewTableSettingsDialog.Timeout.Label=Query timeout (seconds)
+PreviewTableSettingsDialog.Title=Table preview
DatabaseExplorerDialog.Schemas.Label=Schemas
DatabaseExplorerDialog.Synonyms.Label=Synonyms
DatabaseExplorerDialog.Tables.Label=Tables
@@ -66,6 +73,7 @@ SQLEditor.ErrorExecSQL.Message=An error occured executing the
following sql\:\n\
SQLEditor.ErrorExecSQL.Title=Error executing SQL
SQLEditor.LineNr.Label=Line number\: {0}
SQLEditor.Log.SQLExecError=Error while executing\: {0}\n{1}
+SQLEditor.Monitor.Execute=Executing SQL statements...
SQLEditor.Log.SQLExecuted=SQL executed\: {0}\n
SQLEditor.Log.StatsExecuted={0} SQL statements executed
SQLEditor.NoRows.Message=No results found for SQL statement\:\n\n{0}