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 fb235a21a6 fix field rename edge case and flaky tests, fixes #6995
(#6997)
fb235a21a6 is described below
commit fb235a21a6cc7fa48a83350f926858b6943c86ac
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Tue Apr 14 13:26:46 2026 +0200
fix field rename edge case and flaky tests, fixes #6995 (#6997)
---
.../main/java/org/apache/hop/core/row/RowMeta.java | 27 +++++-
.../hop/core/row/RowMetaAddRemoveValueTest.java | 21 +++++
.../org/apache/hop/pipeline/PipelineMetaTest.java | 2 +
.../copy/DefaultTransformMetaCopyFactoryTest.java | 8 +-
.../hop/pipeline/transforms/sql/ExecSql.java | 9 ++
.../hop/pipeline/transforms/sql/ExecSqlDialog.java | 3 +
.../sql/messages/messages_en_US.properties | 1 +
.../hop/pipeline/transforms/xml/xmloutput/one.txt | 92 ++++++++++-----------
.../hop/pipeline/transforms/xml/xmloutput/two.txt | 96 +++++++++++-----------
9 files changed, 162 insertions(+), 97 deletions(-)
diff --git a/core/src/main/java/org/apache/hop/core/row/RowMeta.java
b/core/src/main/java/org/apache/hop/core/row/RowMeta.java
index 4b3deeb5d3..5ea4ffdc5d 100644
--- a/core/src/main/java/org/apache/hop/core/row/RowMeta.java
+++ b/core/src/main/java/org/apache/hop/core/row/RowMeta.java
@@ -230,6 +230,29 @@ public class RowMeta implements IRowMeta {
return (meta != null) && searchValueMeta(meta.getName()) != null;
}
+ /**
+ * Resolves an existing column index for duplicate detection. Must be called
with the write lock
+ * held. After {@link #removeValueMeta(int)} the name cache is cleared; this
method still finds
+ * matches by scanning {@link #valueMetaList} (same idea as {@link
#indexOfValue(String)}).
+ */
+ private Integer indexOfExistingValueMetaIgnoreCase(String name) {
+ if (Utils.isEmpty(name)) {
+ return null;
+ }
+ Integer index = cache.findAndCompare(name, valueMetaList);
+ if (index != null) {
+ return index;
+ }
+ for (int i = 0; i < valueMetaList.size(); i++) {
+ if (name.equalsIgnoreCase(valueMetaList.get(i).getName())) {
+ cache.storeMapping(name, i);
+ needRealClone = null;
+ return i;
+ }
+ }
+ return null;
+ }
+
/**
* Add a metadata value. If a value with the same name already exists, it
gets renamed.
*
@@ -241,7 +264,7 @@ public class RowMeta implements IRowMeta {
lock.writeLock().lock();
try {
IValueMeta newMeta;
- Integer existsIdx = cache.findAndCompare(meta.getName(),
valueMetaList);
+ Integer existsIdx = indexOfExistingValueMetaIgnoreCase(meta.getName());
if (existsIdx == null) {
newMeta = meta;
} else {
@@ -270,7 +293,7 @@ public class RowMeta implements IRowMeta {
lock.writeLock().lock();
try {
IValueMeta newMeta;
- Integer existsIdx = cache.findAndCompare(meta.getName(),
valueMetaList);
+ Integer existsIdx = indexOfExistingValueMetaIgnoreCase(meta.getName());
if (existsIdx == null) {
newMeta = meta;
} else {
diff --git
a/core/src/test/java/org/apache/hop/core/row/RowMetaAddRemoveValueTest.java
b/core/src/test/java/org/apache/hop/core/row/RowMetaAddRemoveValueTest.java
index 1c686f437a..4e39c49205 100644
--- a/core/src/test/java/org/apache/hop/core/row/RowMetaAddRemoveValueTest.java
+++ b/core/src/test/java/org/apache/hop/core/row/RowMetaAddRemoveValueTest.java
@@ -173,6 +173,27 @@ class RowMetaAddRemoveValueTest {
assertEquals(0, rowMeta.size());
}
+ /**
+ * After removeValueMeta the name cache is cleared; adding a field that
collides with an existing
+ * name must still rename (e.g. second Database Lookup after Select Values
dropped {@code *_1}).
+ */
+ @Test
+ void duplicateRenameAfterRemovingRenamedField() throws Exception {
+ IRowMeta rowMeta = new RowMeta();
+ rowMeta.addValueMeta(ValueMetaFactory.createValueMeta("brand",
IValueMeta.TYPE_INTEGER));
+ rowMeta.addValueMeta(ValueMetaFactory.createValueMeta("brand",
IValueMeta.TYPE_STRING));
+ assertEquals("brand", rowMeta.getValueMeta(0).getName());
+ assertEquals("brand_1", rowMeta.getValueMeta(1).getName());
+
+ rowMeta.removeValueMeta("brand_1");
+ assertEquals(1, rowMeta.size());
+
+ rowMeta.addValueMeta(ValueMetaFactory.createValueMeta("brand",
IValueMeta.TYPE_STRING));
+ assertEquals(2, rowMeta.size());
+ assertEquals("brand", rowMeta.getValueMeta(0).getName());
+ assertEquals("brand_1", rowMeta.getValueMeta(1).getName());
+ }
+
@Test
void testAddRemoveValueCaseInsensitive() throws Exception {
IRowMeta rowMeta = new RowMeta();
diff --git a/engine/src/test/java/org/apache/hop/pipeline/PipelineMetaTest.java
b/engine/src/test/java/org/apache/hop/pipeline/PipelineMetaTest.java
index c1f924c383..d97c1d6d60 100644
--- a/engine/src/test/java/org/apache/hop/pipeline/PipelineMetaTest.java
+++ b/engine/src/test/java/org/apache/hop/pipeline/PipelineMetaTest.java
@@ -101,6 +101,8 @@ class PipelineMetaTest {
PluginRegistry registry = PluginRegistry.getInstance();
registry.registerPluginClass(
FakeMeta.class.getName(), TransformPluginType.class, Transform.class);
+ registry.registerPluginClass(
+ DummyMeta.class.getName(), TransformPluginType.class, Transform.class);
}
@Test
diff --git
a/engine/src/test/java/org/apache/hop/pipeline/transform/copy/DefaultTransformMetaCopyFactoryTest.java
b/engine/src/test/java/org/apache/hop/pipeline/transform/copy/DefaultTransformMetaCopyFactoryTest.java
index 62d7cfbc1c..54fa97b14b 100644
---
a/engine/src/test/java/org/apache/hop/pipeline/transform/copy/DefaultTransformMetaCopyFactoryTest.java
+++
b/engine/src/test/java/org/apache/hop/pipeline/transform/copy/DefaultTransformMetaCopyFactoryTest.java
@@ -24,6 +24,9 @@ import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.apache.hop.core.annotations.Transform;
+import org.apache.hop.core.plugins.PluginRegistry;
+import org.apache.hop.core.plugins.TransformPluginType;
import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.pipeline.transforms.dummy.DummyMeta;
import org.junit.jupiter.api.BeforeEach;
@@ -40,7 +43,10 @@ class DefaultTransformMetaCopyFactoryTest {
private DummyMeta dummyMeta;
@BeforeEach
- void setUp() {
+ void setUp() throws Exception {
+ PluginRegistry.getInstance()
+ .registerPluginClass(DummyMeta.class.getName(),
TransformPluginType.class, Transform.class);
+
factory = DefaultTransformMetaCopyFactory.getInstance();
// Create a test transform metadata
diff --git
a/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSql.java
b/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSql.java
index 10719be404..bf63f2900c 100644
---
a/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSql.java
+++
b/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSql.java
@@ -274,6 +274,15 @@ public class ExecSql extends BaseTransform<ExecSqlMeta,
ExecSqlData> {
return false;
}
DatabaseMeta databaseMeta =
getPipelineMeta().findDatabase(meta.getConnection(), variables);
+ if (databaseMeta == null) {
+ logError(
+ BaseMessages.getString(
+ PKG,
+ "ExecSql.Init.ConnectionNotFound",
+ variables.resolve(meta.getConnection()),
+ getTransformName()));
+ return false;
+ }
data.db = new Database(this, this, databaseMeta);
// Connect to the database
diff --git
a/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSqlDialog.java
b/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSqlDialog.java
index 0a96593ed1..8e35d63e40 100644
---
a/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSqlDialog.java
+++
b/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSqlDialog.java
@@ -524,6 +524,9 @@ public class ExecSqlDialog extends BaseTransformDialog {
}
DatabaseMeta databaseMeta =
pipelineMeta.findDatabase(input.getConnection(), variables);
+ if (databaseMeta == null) {
+ return List.of();
+ }
return Arrays.stream(databaseMeta.getReservedWords()).toList();
}
diff --git
a/plugins/transforms/sql/src/main/resources/org/apache/hop/pipeline/transforms/sql/messages/messages_en_US.properties
b/plugins/transforms/sql/src/main/resources/org/apache/hop/pipeline/transforms/sql/messages/messages_en_US.properties
index b4aa4c7d42..d35622f66a 100644
---
a/plugins/transforms/sql/src/main/resources/org/apache/hop/pipeline/transforms/sql/messages/messages_en_US.properties
+++
b/plugins/transforms/sql/src/main/resources/org/apache/hop/pipeline/transforms/sql/messages/messages_en_US.properties
@@ -18,6 +18,7 @@
ExecSql.Description=Execute an SQL script, optionally parameterized using
input rows
ExecSql.Exception.CouldNotFindField=Couldn''t find field ''{0}'' in row\!
ExecSql.Init.ConnectionMissing=Database connection is missing for transform
[{0}]\!
+ExecSql.Init.ConnectionNotFound=No database connection named ''{0}'' could be
found for transform [{1}]\!
ExecSql.Log.ConnectedToDB=Connected to database...
ExecSql.Log.ErrorFindingField=Error finding field\:
ExecSql.Log.ErrorInTransform=Error while running this transform\!
diff --git
a/plugins/transforms/xml/src/test/resources/org/apache/hop/pipeline/transforms/xml/xmloutput/one.txt
b/plugins/transforms/xml/src/test/resources/org/apache/hop/pipeline/transforms/xml/xmloutput/one.txt
index b92e03afce..786f4d8c8e 100644
---
a/plugins/transforms/xml/src/test/resources/org/apache/hop/pipeline/transforms/xml/xmloutput/one.txt
+++
b/plugins/transforms/xml/src/test/resources/org/apache/hop/pipeline/transforms/xml/xmloutput/one.txt
@@ -1,47 +1,47 @@
- <encoding>UTF-8</encoding>
- <name_space/>
- <xml_main_element>Rows</xml_main_element>
- <xml_repeat_element>Row</xml_repeat_element>
- <file>
- <name>xmlOutputFile</name>
- <extention>apache_hop.xml</extention>
- <servlet_output>N</servlet_output>
- <do_not_open_newfile_init>N</do_not_open_newfile_init>
- <split>Y</split>
- <add_date>Y</add_date>
- <add_time>Y</add_time>
- <SpecifyFormat>N</SpecifyFormat>
- <omit_null_values>Y</omit_null_values>
- <date_time_format/>
- <add_to_result_filenames>N</add_to_result_filenames>
- <zipped>N</zipped>
- <splitevery>0</splitevery>
- </file>
- <fields>
- <field>
- <content_type>Element</content_type>
- <name>fieldOne</name>
- <element/>
- <type>Number</type>
- <format/>
- <currency/>
- <decimal/>
- <group/>
- <nullif/>
- <length>-1</length>
- <precision>-1</precision>
- </field>
- <field>
- <content_type>Attribute</content_type>
- <name>fieldTwo</name>
- <element/>
- <type>String</type>
- <format/>
- <currency/>
- <decimal/>
- <group/>
- <nullif/>
- <length>-1</length>
- <precision>-1</precision>
- </field>
+ <encoding>UTF-8</encoding>
+ <name_space/>
+ <xml_main_element>Rows</xml_main_element>
+ <xml_repeat_element>Row</xml_repeat_element>
+ <file>
+ <name>xmlOutputFile</name>
+ <extention>apache_hop.xml</extention>
+ <servlet_output>N</servlet_output>
+ <do_not_open_newfile_init>N</do_not_open_newfile_init>
+ <split>Y</split>
+ <add_date>Y</add_date>
+ <add_time>Y</add_time>
+ <SpecifyFormat>N</SpecifyFormat>
+ <omit_null_values>Y</omit_null_values>
+ <date_time_format/>
+ <add_to_result_filenames>N</add_to_result_filenames>
+ <zipped>N</zipped>
+ <splitevery>0</splitevery>
+ </file>
+ <fields>
+ <field>
+ <content_type>Element</content_type>
+ <name>fieldOne</name>
+ <element/>
+ <type>Number</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ <field>
+ <content_type>Attribute</content_type>
+ <name>fieldTwo</name>
+ <element/>
+ <type>String</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
</fields>
\ No newline at end of file
diff --git
a/plugins/transforms/xml/src/test/resources/org/apache/hop/pipeline/transforms/xml/xmloutput/two.txt
b/plugins/transforms/xml/src/test/resources/org/apache/hop/pipeline/transforms/xml/xmloutput/two.txt
index df23a3708e..8301f6e4b8 100644
---
a/plugins/transforms/xml/src/test/resources/org/apache/hop/pipeline/transforms/xml/xmloutput/two.txt
+++
b/plugins/transforms/xml/src/test/resources/org/apache/hop/pipeline/transforms/xml/xmloutput/two.txt
@@ -1,48 +1,48 @@
-
- <encoding>UTF-8</encoding>
- <name_space/>
- <xml_main_element>Rows</xml_main_element>
- <xml_repeat_element>Row</xml_repeat_element>
- <file>
- <name>xmlOutputFile</name>
- <extention>apache_pentaho.xml</extention>
- <servlet_output>N</servlet_output>
- <do_not_open_newfile_init>N</do_not_open_newfile_init>
- <split>Y</split>
- <add_date>Y</add_date>
- <add_time>Y</add_time>
- <SpecifyFormat>N</SpecifyFormat>
- <omit_null_values>Y</omit_null_values>
- <date_time_format/>
- <add_to_result_filenames>N</add_to_result_filenames>
- <zipped>N</zipped>
- <splitevery>0</splitevery>
- </file>
- <fields>
- <field>
- <content_type>Element</content_type>
- <name>fieldOne</name>
- <element/>
- <type>Number</type>
- <format/>
- <currency/>
- <decimal/>
- <group/>
- <nullif/>
- <length>-1</length>
- <precision>-1</precision>
- </field>
- <field>
- <content_type>Attribute</content_type>
- <name>fieldTwo</name>
- <element/>
- <type>String</type>
- <format/>
- <currency/>
- <decimal/>
- <group/>
- <nullif/>
- <length>-1</length>
- <precision>-1</precision>
- </field>
- </fields>
+
+ <encoding>UTF-8</encoding>
+ <name_space/>
+ <xml_main_element>Rows</xml_main_element>
+ <xml_repeat_element>Row</xml_repeat_element>
+ <file>
+ <name>xmlOutputFile</name>
+ <extention>apache_pentaho.xml</extention>
+ <servlet_output>N</servlet_output>
+ <do_not_open_newfile_init>N</do_not_open_newfile_init>
+ <split>Y</split>
+ <add_date>Y</add_date>
+ <add_time>Y</add_time>
+ <SpecifyFormat>N</SpecifyFormat>
+ <omit_null_values>Y</omit_null_values>
+ <date_time_format/>
+ <add_to_result_filenames>N</add_to_result_filenames>
+ <zipped>N</zipped>
+ <splitevery>0</splitevery>
+ </file>
+ <fields>
+ <field>
+ <content_type>Element</content_type>
+ <name>fieldOne</name>
+ <element/>
+ <type>Number</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ <field>
+ <content_type>Attribute</content_type>
+ <name>fieldTwo</name>
+ <element/>
+ <type>String</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ </fields>