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 edad1b975e Fix#7298 Dynamic SQL row dialog: restore Template SQL
editor visibility (#7313)
edad1b975e is described below
commit edad1b975ee25af7667aded21f0ae57b430542b4
Author: Lance <[email protected]>
AuthorDate: Fri Jun 19 17:50:22 2026 +0800
Fix#7298 Dynamic SQL row dialog: restore Template SQL editor visibility
(#7313)
* Dynamic SQL row: Template SQL (to retrieve Meta data) editor field is not
visible
Signed-off-by: lance <[email protected]>
* Fix org.apache.hop.core.database.DatabaseMeta.getReservedWords()
DatabaseMeta is null
Signed-off-by: lance <[email protected]>
---------
Signed-off-by: lance <[email protected]>
---
.../apache/hop/core/row/ValueMetaAndDataTests.java | 3 +
.../dynamicsqlrow/DynamicSqlRowData.java | 7 +-
.../dynamicsqlrow/DynamicSqlRowDialog.java | 60 ++---
.../dynamicsqlrow/DynamicSqlRowMeta.java | 124 +---------
.../dynamicsqlrow/DynamicSqlRowDataTest.java} | 57 ++---
.../dynamicsqlrow/DynamicSqlRowMetaTest.java | 250 ++++++++++++++++++++-
.../dynamicsqlrow/DynamicSqlRowTest.java | 211 +++++++++++++++++
7 files changed, 531 insertions(+), 181 deletions(-)
diff --git
a/core/src/test/java/org/apache/hop/core/row/ValueMetaAndDataTests.java
b/core/src/test/java/org/apache/hop/core/row/ValueMetaAndDataTests.java
index 9e1d0e4a36..446c8fc970 100644
--- a/core/src/test/java/org/apache/hop/core/row/ValueMetaAndDataTests.java
+++ b/core/src/test/java/org/apache/hop/core/row/ValueMetaAndDataTests.java
@@ -34,11 +34,14 @@ import org.apache.hop.core.row.value.ValueMetaNumber;
import org.apache.hop.core.row.value.ValueMetaSerializable;
import org.apache.hop.core.row.value.ValueMetaString;
import org.apache.hop.core.xml.XmlHandler;
+import org.apache.hop.junit.rules.RestoreHopEnvironmentExtension;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.w3c.dom.Node;
/** Unit test for {@link ValueMetaAndData} */
+@ExtendWith(RestoreHopEnvironmentExtension.class)
class ValueMetaAndDataTests {
@BeforeAll
diff --git
a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowData.java
b/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowData.java
index 679651b0a7..3669630bed 100644
---
a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowData.java
+++
b/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowData.java
@@ -18,6 +18,7 @@
package org.apache.hop.pipeline.transforms.dynamicsqlrow;
import java.util.ArrayList;
+import java.util.List;
import org.apache.hop.core.database.Database;
import org.apache.hop.core.row.IRowMeta;
import org.apache.hop.pipeline.transform.BaseTransformData;
@@ -29,8 +30,8 @@ public class DynamicSqlRowData extends BaseTransformData
implements ITransformDa
IRowMeta lookupRowMeta;
public Database db;
-
- public Object[] notfound; // Values in case nothing is found...
+ // Values in case nothing is found...
+ public Object[] notfound;
public int indexOfSqlField;
@@ -38,7 +39,7 @@ public class DynamicSqlRowData extends BaseTransformData
implements ITransformDa
public String previousSql;
- public ArrayList<Object[]> previousrowbuffer;
+ public List<Object[]> previousrowbuffer;
public boolean isCanceled;
diff --git
a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDialog.java
b/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDialog.java
index 9993a904b8..a0c76d2d8e 100644
---
a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDialog.java
+++
b/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDialog.java
@@ -46,12 +46,12 @@ import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
@@ -69,7 +69,7 @@ public class DynamicSqlRowDialog extends BaseTransformDialog {
private MetaSelectionLine<DatabaseMeta> wConnection;
- private TextComposite wSql;
+ private TextComposite wSqlComposite;
private Text wLimit;
@@ -100,10 +100,8 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
buildButtonBar().ok(e -> ok()).cancel(e -> cancel()).build();
- ModifyListener lsMod = e -> input.setChanged();
- backupChanged = input.hasChanged();
-
ScrolledComposite sc = new ScrolledComposite(shell, SWT.V_SCROLL |
SWT.H_SCROLL);
+ PropsUi.setLook(sc);
sc.setLayout(new FillLayout());
FormData fdSc = new FormData();
fdSc.left = new FormAttachment(0, margin);
@@ -113,6 +111,7 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
sc.setLayoutData(fdSc);
Composite wContent = new Composite(sc, SWT.NONE);
+ PropsUi.setLook(wContent);
FormLayout formLayout = new FormLayout();
formLayout.marginWidth = PropsUi.getFormMargin();
formLayout.marginHeight = PropsUi.getFormMargin();
@@ -123,7 +122,6 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
if (input.getDatabaseMeta() == null && pipelineMeta.nrDatabases() == 1) {
wConnection.select(0);
}
- wConnection.addModifyListener(lsMod);
wConnection.addListener(SWT.Selection, e -> getSqlReservedWords());
// SQLFieldName field
@@ -266,7 +264,7 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
fdlSql.top = new FormAttachment(wqueryOnlyOnChange, margin);
wlSql.setLayoutData(fdlSql);
- wSql =
+ wSqlComposite =
EnvironmentUtils.getInstance().isWeb()
? new StyledTextComp(
variables,
@@ -276,32 +274,36 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
variables,
wContent,
SWT.MULTI | SWT.LEFT | SWT.BORDER | SWT.H_SCROLL |
SWT.V_SCROLL);
- wSql.addLineStyleListener(getSqlReservedWords());
- PropsUi.setLook(wSql, Props.WIDGET_STYLE_FIXED);
+ wSqlComposite.addLineStyleListener(getSqlReservedWords());
+ PropsUi.setLook(wSqlComposite, Props.WIDGET_STYLE_FIXED);
FormData fdSql = new FormData();
fdSql.left = new FormAttachment(0, 0);
fdSql.top = new FormAttachment(wlSql, margin);
fdSql.right = new FormAttachment(100, 0);
- wSql.setLayoutData(fdSql);
wlPosition = new Label(wContent, SWT.NONE);
PropsUi.setLook(wlPosition);
FormData fdlPosition = new FormData();
fdlPosition.left = new FormAttachment(0, 0);
fdlPosition.right = new FormAttachment(100, 0);
- fdlPosition.top = new FormAttachment(wSql, margin);
+ fdlPosition.bottom = new FormAttachment(100, 0);
wlPosition.setLayoutData(fdlPosition);
fdSql.bottom = new FormAttachment(wlPosition, -margin);
- wSql.setLayoutData(fdSql);
+ fdSql.height = 200;
+ wSqlComposite.setLayoutData(fdSql);
- sc.setContent(wContent);
wContent.pack();
- sc.setMinSize(wContent.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ Rectangle bounds = wContent.getBounds();
+ sc.setContent(wContent);
+ sc.setExpandHorizontal(true);
+ sc.setExpandVertical(true);
+ sc.setMinWidth(bounds.width);
+ sc.setMinHeight(bounds.height);
- wSql.addModifyListener(arg0 -> setPosition());
+ wSqlComposite.addModifyListener(arg0 -> setPosition());
- wSql.addKeyListener(
+ wSqlComposite.addKeyListener(
new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
@@ -313,7 +315,7 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
setPosition();
}
});
- wSql.addFocusListener(
+ wSqlComposite.addFocusListener(
new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
@@ -325,7 +327,7 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
setPosition();
}
});
- wSql.addMouseListener(
+ wSqlComposite.addMouseListener(
new MouseAdapter() {
@Override
public void mouseDoubleClick(MouseEvent e) {
@@ -343,9 +345,10 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
}
});
- wSql.addModifyListener(lsMod);
+ wSqlComposite.addModifyListener(lsMod);
getData();
+ input.setChanged(backupChanged);
focusTransformName();
BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
@@ -353,23 +356,28 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
}
private List<String> getSqlReservedWords() {
+ String connection = wConnection != null ? wConnection.getText() :
input.getConnection();
+
// Do not search keywords when connection is empty
- if (Utils.isEmpty(input.getConnection())) {
+ if (Utils.isEmpty(connection)) {
return List.of();
}
// If connection is a variable that can't be resolved
- if (variables.resolve(input.getConnection()).startsWith("${")) {
+ if (variables.resolve(connection).startsWith("${")) {
return List.of();
}
- DatabaseMeta databaseMeta =
pipelineMeta.findDatabase(input.getConnection(), variables);
+ DatabaseMeta databaseMeta = pipelineMeta.findDatabase(connection,
variables);
+ if (databaseMeta == null) {
+ return List.of();
+ }
return Arrays.stream(databaseMeta.getReservedWords()).toList();
}
public void setPosition() {
- int lineNumber = wSql.getLineNumber();
- int columnNumber = wSql.getColumnNumber();
+ int lineNumber = wSqlComposite.getLineNumber();
+ int columnNumber = wSqlComposite.getColumnNumber();
wlPosition.setText(
BaseMessages.getString(
PKG, "DynamicSQLRowDialog.Position.Label", "" + lineNumber, "" +
columnNumber));
@@ -381,7 +389,7 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
logDebug(BaseMessages.getString(PKG,
"DynamicSQLRowDialog.Log.GettingKeyInfo"));
}
- wSql.setText(Const.NVL(input.getSql(), ""));
+ wSqlComposite.setText(Const.NVL(input.getSql(), ""));
wLimit.setText("" + input.getRowLimit());
wOuter.setSelection(input.isOuterJoin());
wuseVars.setSelection(input.isReplaceVariables());
@@ -409,7 +417,7 @@ public class DynamicSqlRowDialog extends
BaseTransformDialog {
input.setConnection(wConnection.getText());
input.setRowLimit(Const.toInt(wLimit.getText(), 0));
- input.setSql(wSql.getText());
+ input.setSql(wSqlComposite.getText());
input.setSqlFieldName(wSqlFieldName.getText());
input.setOuterJoin(wOuter.getSelection());
input.setReplaceVariables(wuseVars.getSelection());
diff --git
a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMeta.java
b/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMeta.java
index bc864971c7..5ba75d185c 100644
---
a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMeta.java
+++
b/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMeta.java
@@ -18,6 +18,8 @@
package org.apache.hop.pipeline.transforms.dynamicsqlrow;
import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.hop.core.CheckResult;
import org.apache.hop.core.Const;
import org.apache.hop.core.ICheckResult;
@@ -41,6 +43,8 @@ import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.BaseTransformMeta;
import org.apache.hop.pipeline.transform.TransformMeta;
+@Getter
+@Setter
@Transform(
id = "DynamicSqlRow",
image = "dynamicsqlrow.svg",
@@ -111,114 +115,10 @@ public class DynamicSqlRowMeta extends
BaseTransformMeta<DynamicSqlRow, DynamicS
this.sqlFieldName = meta.sqlFieldName;
this.replaceVariables = meta.replaceVariables;
this.rowLimit = meta.rowLimit;
- this.connection = meta.connection;
this.outerJoin = meta.outerJoin;
this.queryOnlyOnChange = meta.queryOnlyOnChange;
}
- public String getConnection() {
- return connection;
- }
-
- public void setConnection(String connection) {
- this.connection = connection;
- }
-
- /**
- * @return Returns the database.
- */
- public DatabaseMeta getDatabaseMeta() {
- return databaseMeta;
- }
-
- /**
- * @param database The database to set.
- */
- public void setDatabaseMeta(DatabaseMeta database) {
- this.databaseMeta = database;
- }
-
- /**
- * @return Returns the outerJoin.
- */
- public boolean isOuterJoin() {
- return outerJoin;
- }
-
- /**
- * @param outerJoin The outerJoin to set.
- */
- public void setOuterJoin(boolean outerJoin) {
- this.outerJoin = outerJoin;
- }
-
- /**
- * @return Returns the replacevars.
- */
- public boolean isReplaceVariables() {
- return replaceVariables;
- }
-
- public void setReplaceVariables(boolean replaceVariables) {
- this.replaceVariables = replaceVariables;
- }
-
- /**
- * @return Returns the queryonlyonchange.
- */
- public boolean isQueryOnlyOnChange() {
- return queryOnlyOnChange;
- }
-
- /**
- * @param queryonlyonchange The queryonlyonchange to set.
- */
- public void setQueryOnlyOnChange(boolean queryonlyonchange) {
- this.queryOnlyOnChange = queryonlyonchange;
- }
-
- /**
- * @return Returns the rowLimit.
- */
- public int getRowLimit() {
- return rowLimit;
- }
-
- /**
- * @param rowLimit The rowLimit to set.
- */
- public void setRowLimit(int rowLimit) {
- this.rowLimit = rowLimit;
- }
-
- /**
- * @return Returns the sql.
- */
- public String getSql() {
- return sql;
- }
-
- /**
- * @param sql The sql to set.
- */
- public void setSql(String sql) {
- this.sql = sql;
- }
-
- /**
- * @return Returns the sqlfieldname.
- */
- public String getSqlFieldName() {
- return sqlFieldName;
- }
-
- /**
- * @param sqlfieldname The sqlfieldname to set.
- */
- public void setSqlFieldName(String sqlfieldname) {
- this.sqlFieldName = sqlfieldname;
- }
-
@Override
public Object clone() {
return new DynamicSqlRowMeta(this);
@@ -251,7 +151,8 @@ public class DynamicSqlRowMeta extends
BaseTransformMeta<DynamicSqlRow, DynamicS
}
Database db = new Database(loggingObject, variables, databaseMeta);
- databases = new Database[] {db}; // Keep track of this one for cancelQuery
+ // Keep track of this one for cancelQuery
+ databases = new Database[] {db};
// First try without connecting to the database... (can be S L O W)
// See if it's in the cache...
@@ -269,8 +170,8 @@ public class DynamicSqlRowMeta extends
BaseTransformMeta<DynamicSqlRow, DynamicS
+ sql,
dbe);
}
-
- if (add != null) { // Cache hit, just return it this...
+ // Cache hit, just return it this...
+ if (add != null) {
for (int i = 0; i < add.size(); i++) {
IValueMeta v = add.getValueMeta(i);
v.setOrigin(name);
@@ -357,10 +258,9 @@ public class DynamicSqlRowMeta extends
BaseTransformMeta<DynamicSqlRow, DynamicS
}
if (databaseMeta != null) {
- Database db = new Database(loggingObject, variables, databaseMeta);
- databases = new Database[] {db}; // Keep track of this one for
cancelQuery
-
- try {
+ try (Database db = new Database(loggingObject, variables, databaseMeta))
{
+ // Keep track of this one for cancelQuery
+ databases = new Database[] {db};
db.connect();
if (!Utils.isEmpty(sql)) {
@@ -387,8 +287,6 @@ public class DynamicSqlRowMeta extends
BaseTransformMeta<DynamicSqlRow, DynamicS
+ e.getMessage();
cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage,
transformMeta);
remarks.add(cr);
- } finally {
- db.disconnect();
}
} else {
errorMessage = BaseMessages.getString(PKG,
"DynamicSQLRowMeta.CheckResult.InvalidConnection");
diff --git
a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowData.java
b/plugins/transforms/dynamicsqlrow/src/test/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDataTest.java
similarity index 50%
copy from
plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowData.java
copy to
plugins/transforms/dynamicsqlrow/src/test/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDataTest.java
index 679651b0a7..d09295a87d 100644
---
a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowData.java
+++
b/plugins/transforms/dynamicsqlrow/src/test/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDataTest.java
@@ -17,39 +17,28 @@
package org.apache.hop.pipeline.transforms.dynamicsqlrow;
-import java.util.ArrayList;
-import org.apache.hop.core.database.Database;
-import org.apache.hop.core.row.IRowMeta;
-import org.apache.hop.pipeline.transform.BaseTransformData;
-import org.apache.hop.pipeline.transform.ITransformData;
-
-@SuppressWarnings("java:S1104")
-public class DynamicSqlRowData extends BaseTransformData implements
ITransformData {
- IRowMeta outputRowMeta;
- IRowMeta lookupRowMeta;
-
- public Database db;
-
- public Object[] notfound; // Values in case nothing is found...
-
- public int indexOfSqlField;
-
- public boolean skipPreviousRow;
-
- public String previousSql;
-
- public ArrayList<Object[]> previousrowbuffer;
-
- public boolean isCanceled;
-
- public DynamicSqlRowData() {
- super();
-
- db = null;
- notfound = null;
- indexOfSqlField = -1;
- skipPreviousRow = false;
- previousSql = null;
- previousrowbuffer = new ArrayList<>();
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+/** Unit test for {@link DynamicSqlRowData} */
+class DynamicSqlRowDataTest {
+
+ @Test
+ void constructorInitializesDefaults() {
+ DynamicSqlRowData data = new DynamicSqlRowData();
+
+ assertNull(data.db);
+ assertNull(data.notfound);
+ assertEquals(-1, data.indexOfSqlField);
+ assertFalse(data.skipPreviousRow);
+ assertNull(data.previousSql);
+ assertNotNull(data.previousrowbuffer);
+ assertTrue(data.previousrowbuffer.isEmpty());
+ assertFalse(data.isCanceled);
}
}
diff --git
a/plugins/transforms/dynamicsqlrow/src/test/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMetaTest.java
b/plugins/transforms/dynamicsqlrow/src/test/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMetaTest.java
index b20d9a74d9..589a599a5f 100644
---
a/plugins/transforms/dynamicsqlrow/src/test/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMetaTest.java
+++
b/plugins/transforms/dynamicsqlrow/src/test/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMetaTest.java
@@ -16,34 +16,63 @@
*/
package org.apache.hop.pipeline.transforms.dynamicsqlrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hop.core.HopEnvironment;
+import org.apache.hop.core.ICheckResult;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.plugins.PluginRegistry;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.IValueMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaString;
import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.pipeline.transforms.loadsave.LoadSaveTester;
import
org.apache.hop.pipeline.transforms.loadsave.validator.IFieldLoadSaveValidator;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+/** Unit test for {@link DynamicSqlRowMeta} */
class DynamicSqlRowMetaTest {
- LoadSaveTester loadSaveTester;
+
+ LoadSaveTester<DynamicSqlRowMeta> loadSaveTester;
Class<DynamicSqlRowMeta> testMetaClass = DynamicSqlRowMeta.class;
@RegisterExtension
static RestoreHopEngineEnvironmentExtension env = new
RestoreHopEngineEnvironmentExtension();
- @BeforeEach
- void setUpLoadSave() throws Exception {
+ @BeforeAll
+ static void setUpBeforeClass() throws HopException {
HopEnvironment.init();
PluginRegistry.init();
+ }
+
+ @BeforeEach
+ void setUpLoadSave() throws HopException {
List<String> attributes =
Arrays.asList(
- "sql", "sqlFieldName", "rowLimit", "outerJoin",
"replaceVariables", "connection");
+ "sql",
+ "sqlFieldName",
+ "rowLimit",
+ "outerJoin",
+ "replaceVariables",
+ "queryOnlyOnChange",
+ "connection");
Map<String, String> getterMap = new HashMap<>();
Map<String, String> setterMap = new HashMap<>();
@@ -51,7 +80,7 @@ class DynamicSqlRowMetaTest {
Map<String, IFieldLoadSaveValidator<?>> typeValidatorMap = new HashMap<>();
loadSaveTester =
- new LoadSaveTester(
+ new LoadSaveTester<>(
testMetaClass, attributes, getterMap, setterMap, attrValidatorMap,
typeValidatorMap);
}
@@ -59,4 +88,215 @@ class DynamicSqlRowMetaTest {
void testSerialization() throws HopException {
loadSaveTester.testSerialization();
}
+
+ @Test
+ void testSetDefault() {
+ // valid default value
+ DynamicSqlRowMeta meta = new DynamicSqlRowMeta();
+ meta.setDefault();
+
+ assertNull(meta.getConnection());
+ assertNull(meta.getDatabaseMeta());
+ assertEquals(0, meta.getRowLimit());
+ assertEquals("", meta.getSql());
+ assertFalse(meta.isOuterJoin());
+ assertFalse(meta.isReplaceVariables());
+ assertNull(meta.getSqlFieldName());
+ assertFalse(meta.isQueryOnlyOnChange());
+ }
+
+ @Test
+ void testGettersAndSetters() {
+ // valid property
+ DynamicSqlRowMeta meta = new DynamicSqlRowMeta();
+
+ meta.setConnection("conn");
+ assertEquals("conn", meta.getConnection());
+
+ meta.setSql("SELECT 1");
+ assertEquals("SELECT 1", meta.getSql());
+
+ meta.setSqlFieldName("sql_field");
+ assertEquals("sql_field", meta.getSqlFieldName());
+
+ meta.setRowLimit(10);
+ assertEquals(10, meta.getRowLimit());
+
+ meta.setOuterJoin(true);
+ assertTrue(meta.isOuterJoin());
+
+ meta.setReplaceVariables(true);
+ assertTrue(meta.isReplaceVariables());
+
+ meta.setQueryOnlyOnChange(true);
+ assertTrue(meta.isQueryOnlyOnChange());
+ }
+
+ @Test
+ void testClone() {
+ // valid clone
+ DynamicSqlRowMeta meta = new DynamicSqlRowMeta();
+ meta.setConnection("conn");
+ meta.setSql("SELECT id FROM t");
+ meta.setSqlFieldName("sql_field");
+ meta.setRowLimit(5);
+ meta.setOuterJoin(true);
+ meta.setReplaceVariables(true);
+ meta.setQueryOnlyOnChange(true);
+
+ DynamicSqlRowMeta cloned = (DynamicSqlRowMeta) meta.clone();
+
+ assertNotNull(cloned);
+ assertEquals(meta.getConnection(), cloned.getConnection());
+ assertEquals(meta.getSql(), cloned.getSql());
+ assertEquals(meta.getSqlFieldName(), cloned.getSqlFieldName());
+ assertEquals(meta.getRowLimit(), cloned.getRowLimit());
+ assertEquals(meta.isOuterJoin(), cloned.isOuterJoin());
+ assertEquals(meta.isReplaceVariables(), cloned.isReplaceVariables());
+ assertEquals(meta.isQueryOnlyOnChange(), cloned.isQueryOnlyOnChange());
+ }
+
+ @Test
+ void testCopyConstructor() {
+ DynamicSqlRowMeta meta = new DynamicSqlRowMeta();
+ meta.setConnection("conn");
+ meta.setSql("SELECT 1");
+ meta.setSqlFieldName("sql_field");
+ meta.setRowLimit(3);
+ meta.setOuterJoin(true);
+ meta.setReplaceVariables(false);
+ meta.setQueryOnlyOnChange(true);
+
+ DynamicSqlRowMeta copy = new DynamicSqlRowMeta(meta);
+
+ assertEquals(meta.getConnection(), copy.getConnection());
+ assertEquals(meta.getSql(), copy.getSql());
+ assertEquals(meta.getSqlFieldName(), copy.getSqlFieldName());
+ assertEquals(meta.getRowLimit(), copy.getRowLimit());
+ assertEquals(meta.isOuterJoin(), copy.isOuterJoin());
+ assertEquals(meta.isReplaceVariables(), copy.isReplaceVariables());
+ assertEquals(meta.isQueryOnlyOnChange(), copy.isQueryOnlyOnChange());
+ }
+
+ @Test
+ void supportsErrorHandlingReturnsTrue() {
+ assertTrue(new DynamicSqlRowMeta().supportsErrorHandling());
+ }
+
+ @Test
+ void checkWithoutInputReportsError() {
+ // valid check
+ DynamicSqlRowMeta meta = new DynamicSqlRowMeta();
+ List<ICheckResult> remarks = new ArrayList<>();
+
+ meta.check(
+ remarks,
+ mock(PipelineMeta.class),
+ mock(TransformMeta.class),
+ new RowMeta(),
+ new String[0],
+ new String[0],
+ mock(IRowMeta.class),
+ null,
+ mock(IHopMetadataProvider.class));
+
+ assertTrue(
+ remarks.stream().anyMatch(r -> r.getType() ==
ICheckResult.TYPE_RESULT_ERROR),
+ "Expected error when no input transforms are connected");
+ }
+
+ @Test
+ void checkWithInputAndMissingSqlFieldNameReportsError() {
+ DynamicSqlRowMeta meta = new DynamicSqlRowMeta();
+ List<ICheckResult> remarks = new ArrayList<>();
+
+ meta.check(
+ remarks,
+ mock(PipelineMeta.class),
+ mock(TransformMeta.class),
+ new RowMeta(),
+ new String[] {"in"},
+ new String[0],
+ mock(IRowMeta.class),
+ null,
+ mock(IHopMetadataProvider.class));
+
+ assertTrue(
+ remarks.stream().anyMatch(r -> r.getType() ==
ICheckResult.TYPE_RESULT_ERROR),
+ "Expected error when SQL field name is missing");
+ }
+
+ @Test
+ void checkWithUnknownSqlFieldReportsError() {
+ DynamicSqlRowMeta meta = new DynamicSqlRowMeta();
+ meta.setSqlFieldName("sql");
+ List<ICheckResult> remarks = new ArrayList<>();
+
+ meta.check(
+ remarks,
+ mock(PipelineMeta.class),
+ mock(TransformMeta.class),
+ new RowMeta(),
+ new String[] {"in"},
+ new String[0],
+ mock(IRowMeta.class),
+ null,
+ mock(IHopMetadataProvider.class));
+
+ assertTrue(
+ remarks.stream().anyMatch(r -> r.getType() ==
ICheckResult.TYPE_RESULT_ERROR),
+ "Expected error when SQL field is not present in previous transform
output");
+ }
+
+ @Test
+ void checkWithValidSqlFieldReportsOk() {
+ DynamicSqlRowMeta meta = new DynamicSqlRowMeta();
+ meta.setSqlFieldName("sql");
+
+ RowMeta prev = new RowMeta();
+ IValueMeta sqlField = new ValueMetaString("sql");
+ sqlField.setOrigin("input");
+ prev.addValueMeta(sqlField);
+
+ List<ICheckResult> remarks = new ArrayList<>();
+ meta.check(
+ remarks,
+ mock(PipelineMeta.class),
+ mock(TransformMeta.class),
+ prev,
+ new String[] {"in"},
+ new String[0],
+ mock(IRowMeta.class),
+ null,
+ mock(IHopMetadataProvider.class));
+
+ assertTrue(
+ remarks.stream().anyMatch(r -> r.getType() ==
ICheckResult.TYPE_RESULT_OK),
+ "Expected OK result when SQL field exists in previous transform
output");
+ }
+
+ @Test
+ void checkWithoutDatabaseMetaReportsInvalidConnection() {
+ DynamicSqlRowMeta meta = new DynamicSqlRowMeta();
+ meta.setSqlFieldName("sql_field");
+
+ RowMeta prev = new RowMeta();
+ prev.addValueMeta(new ValueMetaString("sql_field"));
+
+ List<ICheckResult> remarks = new ArrayList<>();
+ meta.check(
+ remarks,
+ mock(PipelineMeta.class),
+ mock(TransformMeta.class),
+ prev,
+ new String[] {"in"},
+ new String[0],
+ mock(IRowMeta.class),
+ null,
+ mock(IHopMetadataProvider.class));
+
+ assertTrue(
+ remarks.stream().anyMatch(r -> r.getType() ==
ICheckResult.TYPE_RESULT_ERROR),
+ "Expected error when database connection metadata is missing");
+ }
}
diff --git
a/plugins/transforms/dynamicsqlrow/src/test/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowTest.java
b/plugins/transforms/dynamicsqlrow/src/test/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowTest.java
new file mode 100644
index 0000000000..3ea28423d5
--- /dev/null
+++
b/plugins/transforms/dynamicsqlrow/src/test/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.pipeline.transforms.dynamicsqlrow;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.hop.core.IRowSet;
+import org.apache.hop.core.QueueRowSet;
+import org.apache.hop.core.database.Database;
+import org.apache.hop.core.exception.HopDatabaseException;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.logging.ILoggingObject;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaString;
+import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.pipeline.transforms.mock.TransformMockHelper;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/** Unit test for {@link DynamicSqlRow}. */
+@ExtendWith(RestoreHopEngineEnvironmentExtension.class)
+class DynamicSqlRowTest {
+
+ private TransformMockHelper<DynamicSqlRowMeta, DynamicSqlRowData>
transformMockHelper;
+ private DynamicSqlRowMeta meta;
+ private DynamicSqlRowData data;
+ private DynamicSqlRow transform;
+
+ @BeforeEach
+ void setUp() {
+ transformMockHelper =
+ new TransformMockHelper<>(
+ "DYNAMIC_SQL_ROW", DynamicSqlRowMeta.class,
DynamicSqlRowData.class);
+ when(transformMockHelper.logChannelFactory.create(any(),
any(ILoggingObject.class)))
+ .thenReturn(transformMockHelper.iLogChannel);
+ when(transformMockHelper.pipeline.isRunning()).thenReturn(true);
+
+ meta = new DynamicSqlRowMeta();
+ data = new DynamicSqlRowData();
+ transform =
+ new DynamicSqlRow(
+ transformMockHelper.transformMeta,
+ meta,
+ data,
+ 0,
+ transformMockHelper.pipelineMeta,
+ transformMockHelper.pipeline);
+ }
+
+ @AfterEach
+ void tearDown() {
+ transformMockHelper.cleanUp();
+ }
+
+ private static IRowSet inputRowSet(Object[] row, IRowMeta rowMeta) {
+ QueueRowSet rowSet = new QueueRowSet();
+ rowSet.putRow(rowMeta, row);
+ rowSet.setDone();
+ return rowSet;
+ }
+
+ private static RowMeta rowMetaWithField(String fieldName) {
+ RowMeta rowMeta = new RowMeta();
+ rowMeta.addValueMeta(new ValueMetaString(fieldName));
+ return rowMeta;
+ }
+
+ @Test
+ void initReturnsFalseWhenDatabaseMetaIsMissing() {
+ meta.setConnection("missing-connection");
+ when(transformMockHelper.pipelineMeta.findDatabase(anyString(),
any())).thenReturn(null);
+
+ assertFalse(transform.init());
+ }
+
+ @Test
+ void initReturnsFalseWhenConnectionIsEmpty() {
+ meta.setConnection(null);
+
+ assertFalse(transform.init());
+ }
+
+ @Test
+ void disposeDisconnectsDatabase() {
+ data.db = mock(Database.class);
+ transform.dispose();
+
+ verify(data.db).disconnect();
+ }
+
+ @Test
+ void stopRunningDoesNothingWhenAlreadyCanceled() throws HopException {
+ data.db = mock(Database.class);
+ data.isCanceled = true;
+
+ transform.stopRunning();
+
+ verify(data.db, never()).cancelQuery();
+ }
+
+ @Test
+ void stopRunningCancelsQueryWhenDatabaseIsActive() throws HopException {
+ data.db = mock(Database.class);
+ data.isCanceled = false;
+
+ transform.stopRunning();
+
+ verify(data.db).cancelQuery();
+ assertTrue(data.isCanceled);
+ assertTrue(transform.isStopped());
+ }
+
+ @Test
+ void stopRunningDoesNothingWhenDatabaseIsNull() throws HopException {
+ data.db = null;
+ transform.stopRunning();
+
+ assertFalse(data.isCanceled);
+ }
+
+ @Test
+ void processRowReturnsFalseWhenNoMoreInput() throws HopException {
+ QueueRowSet rowSet = new QueueRowSet();
+ rowSet.setDone();
+ transform.addRowSetToInputRowSets(rowSet);
+
+ assertFalse(transform.processRow());
+ }
+
+ @Test
+ void processRowFailsWhenSqlFieldNameIsEmpty() {
+ meta.setSql("SELECT 1");
+ meta.setSqlFieldName(null);
+ transform.addRowSetToInputRowSets(
+ inputRowSet(new Object[] {"SELECT 1"}, rowMetaWithField("sql_field")));
+
+ assertThrows(HopException.class, () -> transform.processRow());
+ }
+
+ @Test
+ void processRowFailsWhenTemplateSqlIsEmpty() {
+ meta.setSql("");
+ meta.setSqlFieldName("sql_field");
+ transform.addRowSetToInputRowSets(
+ inputRowSet(new Object[] {"SELECT 1"}, rowMetaWithField("sql_field")));
+
+ assertThrows(HopException.class, () -> transform.processRow());
+ }
+
+ @Test
+ void processRowFailsWhenSqlFieldIsNotFound() {
+ meta.setSql("SELECT 1");
+ meta.setSqlFieldName("sql_field");
+ // Row meta does not contain "sql_field", so indexOfValue returns -1.
+ transform.addRowSetToInputRowSets(
+ inputRowSet(new Object[] {"SELECT 1"},
rowMetaWithField("other_field")));
+
+ HopException exception = assertThrows(HopException.class, () ->
transform.processRow());
+ assertTrue(exception.getMessage().contains("sql_field"));
+ }
+
+ @Test
+ void processRowUsesErrorHandlingWhenConfigured() throws HopException {
+ meta.setSql("SELECT 1");
+ meta.setSqlFieldName("sql_field");
+
when(transformMockHelper.transformMeta.isDoingErrorHandling()).thenReturn(true);
+
+ data.db = mock(Database.class);
+ when(data.db.openQuery(anyString())).thenThrow(new
HopDatabaseException("Query failed"));
+
+ DynamicSqlRow spyTransform = spy(transform);
+ doNothing()
+ .when(spyTransform)
+ .putError(any(), any(), anyLong(), anyString(), any(), anyString());
+
+ spyTransform.addRowSetToInputRowSets(
+ inputRowSet(new Object[] {"SELECT 1"}, rowMetaWithField("sql_field")));
+
+ assertTrue(spyTransform.processRow());
+ assertFalse(spyTransform.isStopped());
+ }
+}