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 4719ab085f Fix Combination Lookup SQL button NPE when parent pipeline 
is null (#8431)
4719ab085f is described below

commit 4719ab085f0352e8f5870a922f0b935caf96aa49
Author: Lance <[email protected]>
AuthorDate: Fri Sep 18 16:17:49 2026 +0800

    Fix Combination Lookup SQL button NPE when parent pipeline is null (#8431)
    
    Signed-off-by: leehaut <[email protected]>
---
 .../combinationlookup/CombinationLookupMeta.java   |   9 +-
 .../transforms/combinationlookup/KeyField.java     |  44 +----
 .../transforms/combinationlookup/ReturnFields.java |  82 +--------
 .../CombinationLookupMetaTest.java                 | 201 +++++++++++++++++++++
 4 files changed, 219 insertions(+), 117 deletions(-)

diff --git 
a/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupMeta.java
 
b/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupMeta.java
index 146e4ea688..4f9b7f2fee 100644
--- 
a/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupMeta.java
+++ 
b/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupMeta.java
@@ -421,8 +421,7 @@ public class CombinationLookupMeta
       IRowMeta prev,
       IHopMetadataProvider metadataProvider) {
 
-    DatabaseMeta databaseMeta =
-        
getParentTransformMeta().getParentPipelineMeta().findDatabase(connectionName, 
variables);
+    DatabaseMeta databaseMeta = pipelineMeta.findDatabase(connectionName, 
variables);
 
     SqlStatement retval =
         new SqlStatement(transformMeta.getName(), databaseMeta, null); // 
default: nothing to do!
@@ -673,8 +672,10 @@ public class CombinationLookupMeta
       IRowMeta info,
       IHopMetadataProvider metadataProvider) {
 
-    DatabaseMeta databaseMeta =
-        
getParentTransformMeta().getParentPipelineMeta().findDatabase(connectionName, 
variables);
+    DatabaseMeta databaseMeta = pipelineMeta.findDatabase(connectionName, 
variables);
+    if (databaseMeta == null || prev == null) {
+      return;
+    }
 
     // The keys are read-only...
     for (int i = 0; i < fields.getKeyFields().size(); i++) {
diff --git 
a/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/KeyField.java
 
b/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/KeyField.java
index b9058d2917..bb60297e71 100644
--- 
a/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/KeyField.java
+++ 
b/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/KeyField.java
@@ -18,21 +18,25 @@
 
 package org.apache.hop.pipeline.transforms.combinationlookup;
 
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.HopMetadataPropertyType;
 
+@Getter
+@Setter
 public class KeyField {
   @HopMetadataProperty(
       injectionKey = "KEY_FIELD",
       injectionKeyDescription = "CombinationLookup.Injection.KEY_FIELD",
       hopMetadataPropertyType = HopMetadataPropertyType.STREAM_FIELD)
-  String name;
+  private String name;
 
   @HopMetadataProperty(
       injectionKey = "KEY_LOOKUP",
       injectionKeyDescription = "CombinationLookup.Injection.KEY_LOOKUP",
       hopMetadataPropertyType = HopMetadataPropertyType.RDBMS_COLUMN)
-  String lookup;
+  private String lookup;
 
   public KeyField() {}
 
@@ -45,40 +49,4 @@ public class KeyField {
     this.name = f.name;
     this.lookup = f.lookup;
   }
-
-  /**
-   * Gets name
-   *
-   * @return value of name
-   */
-  public String getName() {
-    return name;
-  }
-
-  /**
-   * Sets name
-   *
-   * @param name value of name
-   */
-  public void setName(String name) {
-    this.name = name;
-  }
-
-  /**
-   * Gets lookup
-   *
-   * @return value of lookup
-   */
-  public String getLookup() {
-    return lookup;
-  }
-
-  /**
-   * Sets lookup
-   *
-   * @param lookup value of lookup
-   */
-  public void setLookup(String lookup) {
-    this.lookup = lookup;
-  }
 }
diff --git 
a/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/ReturnFields.java
 
b/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/ReturnFields.java
index 0359d8aff5..1bfc9c4d53 100644
--- 
a/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/ReturnFields.java
+++ 
b/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/ReturnFields.java
@@ -18,8 +18,14 @@
 
 package org.apache.hop.pipeline.transforms.combinationlookup;
 
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
 import org.apache.hop.metadata.api.HopMetadataProperty;
 
+@Getter
+@Setter
+@NoArgsConstructor
 public class ReturnFields {
   /** Technical Key field to return */
   @HopMetadataProperty(
@@ -46,9 +52,7 @@ public class ReturnFields {
       key = "creation_method",
       injectionKey = "TECHNICAL_KEY_CREATION",
       injectionKeyDescription = 
"CombinationLookup.Injection.TECHNICAL_KEY_CREATION")
-  private String techKeyCreation = null;
-
-  public ReturnFields() {}
+  private String techKeyCreation;
 
   public ReturnFields(ReturnFields f) {
     this.technicalKeyField = f.technicalKeyField;
@@ -56,76 +60,4 @@ public class ReturnFields {
     this.lastUpdateField = f.lastUpdateField;
     this.techKeyCreation = f.techKeyCreation;
   }
-
-  /**
-   * Gets technicalKeyField
-   *
-   * @return value of technicalKeyField
-   */
-  public String getTechnicalKeyField() {
-    return technicalKeyField;
-  }
-
-  /**
-   * Sets technicalKeyField
-   *
-   * @param technicalKeyField value of technicalKeyField
-   */
-  public void setTechnicalKeyField(String technicalKeyField) {
-    this.technicalKeyField = technicalKeyField;
-  }
-
-  /**
-   * Gets useAutoinc
-   *
-   * @return value of useAutoinc
-   */
-  public boolean isUseAutoIncrement() {
-    return useAutoIncrement;
-  }
-
-  /**
-   * Sets useAutoinc
-   *
-   * @param useAutoIncrement value of useAutoinc
-   */
-  public void setUseAutoIncrement(boolean useAutoIncrement) {
-    this.useAutoIncrement = useAutoIncrement;
-  }
-
-  /**
-   * Gets lastUpdateField
-   *
-   * @return value of lastUpdateField
-   */
-  public String getLastUpdateField() {
-    return lastUpdateField;
-  }
-
-  /**
-   * Sets lastUpdateField
-   *
-   * @param lastUpdateField value of lastUpdateField
-   */
-  public void setLastUpdateField(String lastUpdateField) {
-    this.lastUpdateField = lastUpdateField;
-  }
-
-  /**
-   * Gets techKeyCreation
-   *
-   * @return value of techKeyCreation
-   */
-  public String getTechKeyCreation() {
-    return techKeyCreation;
-  }
-
-  /**
-   * Sets techKeyCreation
-   *
-   * @param techKeyCreation value of techKeyCreation
-   */
-  public void setTechKeyCreation(String techKeyCreation) {
-    this.techKeyCreation = techKeyCreation;
-  }
 }
diff --git 
a/plugins/transforms/combinationlookup/src/test/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupMetaTest.java
 
b/plugins/transforms/combinationlookup/src/test/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupMetaTest.java
index c40e23a604..6cf97afd9f 100644
--- 
a/plugins/transforms/combinationlookup/src/test/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupMetaTest.java
+++ 
b/plugins/transforms/combinationlookup/src/test/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupMetaTest.java
@@ -17,13 +17,34 @@
 
 package org.apache.hop.pipeline.transforms.combinationlookup;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+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 java.util.ArrayList;
+import java.util.List;
 import org.apache.hop.core.HopEnvironment;
+import org.apache.hop.core.SqlStatement;
+import org.apache.hop.core.database.DatabaseMeta;
 import org.apache.hop.core.plugins.PluginRegistry;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaString;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider;
+import org.apache.hop.pipeline.DatabaseImpact;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
 import org.apache.hop.pipeline.transform.TransformSerializationTestUtil;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 class CombinationLookupMetaTest {
+  private static final Class<?> PKG = CombinationLookupMeta.class;
+  private static final String CONNECTION_NAME = "h2";
+  private static final String TRANSFORM_NAME = "Combination lookup/update";
+
   @BeforeEach
   void setUpLoadSave() throws Exception {
     HopEnvironment.init();
@@ -35,4 +56,184 @@ class CombinationLookupMetaTest {
     TransformSerializationTestUtil.testSerialization(
         "/combination-lookup-transform.xml", CombinationLookupMeta.class);
   }
+
+  /**
+   * CombinationLookupDialog.create() wraps dialog contents in a new 
TransformMeta that never gets a
+   * parent pipeline. getSqlStatements must use the PipelineMeta argument, not 
the parent chain.
+   */
+  @Test
+  void getSqlStatementsDoesNotNpeWhenParentPipelineIsNull() throws Exception {
+    CombinationLookupMeta info = dialogStyleMeta();
+    TransformMeta transformMeta = new TransformMeta(TRANSFORM_NAME, info);
+    assertNull(transformMeta.getParentPipelineMeta());
+
+    PipelineMeta pipelineMeta = pipelineWithoutConnection();
+    RowMeta prev = incomingRow();
+
+    SqlStatement sql =
+        info.getSqlStatements(
+            new Variables(), pipelineMeta, transformMeta, prev, 
pipelineMeta.getMetadataProvider());
+
+    assertNotNull(sql);
+    assertTrue(sql.hasError());
+    assertEquals(
+        BaseMessages.getString(PKG, 
"CombinationLookupMeta.ReturnValue.NotConnectionDefined"),
+        sql.getError());
+  }
+
+  @Test
+  void getSqlStatementsUsesPipelineMetaToResolveConnection() throws Exception {
+    CombinationLookupMeta info = dialogStyleMeta();
+    info.setTableName("");
+    TransformMeta transformMeta = new TransformMeta(TRANSFORM_NAME, info);
+    assertNull(transformMeta.getParentPipelineMeta());
+
+    PipelineMeta pipelineMeta = pipelineWithConnection();
+
+    SqlStatement sql =
+        info.getSqlStatements(
+            new Variables(),
+            pipelineMeta,
+            transformMeta,
+            incomingRow(),
+            pipelineMeta.getMetadataProvider());
+
+    assertNotNull(sql);
+    assertNotNull(sql.getDatabase());
+    assertEquals(CONNECTION_NAME, sql.getDatabase().getName());
+    assertTrue(sql.hasError());
+    assertEquals(
+        BaseMessages.getString(PKG, 
"CombinationLookupMeta.ReturnValue.NotTableDefined"),
+        sql.getError());
+  }
+
+  @Test
+  void getSqlStatementsReportsMissingInputFieldsWithoutNpe() throws Exception {
+    CombinationLookupMeta info = dialogStyleMeta();
+    TransformMeta transformMeta = new TransformMeta(TRANSFORM_NAME, info);
+    PipelineMeta pipelineMeta = pipelineWithConnection();
+
+    SqlStatement sql =
+        info.getSqlStatements(
+            new Variables(),
+            pipelineMeta,
+            transformMeta,
+            new RowMeta(),
+            pipelineMeta.getMetadataProvider());
+
+    assertTrue(sql.hasError());
+    assertEquals(
+        BaseMessages.getString(PKG, 
"CombinationLookupMeta.ReturnValue.NotReceivingField"),
+        sql.getError());
+  }
+
+  @Test
+  void analyseImpactDoesNotNpeWhenParentPipelineIsNull() throws Exception {
+    CombinationLookupMeta info = dialogStyleMeta();
+    TransformMeta transformMeta = new TransformMeta(TRANSFORM_NAME, info);
+    assertNull(transformMeta.getParentPipelineMeta());
+
+    PipelineMeta pipelineMeta = pipelineWithConnection();
+    pipelineMeta.setName("issue-8283");
+    List<DatabaseImpact> impact = new ArrayList<>();
+
+    info.analyseImpact(
+        new Variables(),
+        impact,
+        pipelineMeta,
+        transformMeta,
+        incomingRow(),
+        new String[] {"Data grid"},
+        new String[0],
+        null,
+        pipelineMeta.getMetadataProvider());
+
+    assertEquals(1, impact.size());
+    DatabaseImpact first = impact.get(0);
+    assertEquals(DatabaseImpact.TYPE_IMPACT_READ_WRITE, first.getType());
+    assertEquals("issue-8283", first.getPipelineName());
+    assertEquals(TRANSFORM_NAME, first.getTransformName());
+    assertEquals("junk_customer", first.getTable());
+    assertEquals("customer_code", first.getField());
+  }
+
+  @Test
+  void analyseImpactSkipsWorkWhenConnectionIsMissing() throws Exception {
+    CombinationLookupMeta info = dialogStyleMeta();
+    TransformMeta transformMeta = new TransformMeta(TRANSFORM_NAME, info);
+    PipelineMeta pipelineMeta = pipelineWithoutConnection();
+    List<DatabaseImpact> impact = new ArrayList<>();
+
+    info.analyseImpact(
+        new Variables(),
+        impact,
+        pipelineMeta,
+        transformMeta,
+        incomingRow(),
+        new String[0],
+        new String[0],
+        null,
+        pipelineMeta.getMetadataProvider());
+
+    assertTrue(impact.isEmpty());
+  }
+
+  @Test
+  void analyseImpactIncludesHashFieldWhenEnabled() throws Exception {
+    CombinationLookupMeta info = dialogStyleMeta();
+    info.setUseHash(true);
+    info.setHashField("hashcode");
+    TransformMeta transformMeta = new TransformMeta(TRANSFORM_NAME, info);
+    PipelineMeta pipelineMeta = pipelineWithConnection();
+    List<DatabaseImpact> impact = new ArrayList<>();
+
+    info.analyseImpact(
+        new Variables(),
+        impact,
+        pipelineMeta,
+        transformMeta,
+        incomingRow(),
+        new String[0],
+        new String[0],
+        null,
+        pipelineMeta.getMetadataProvider());
+
+    assertEquals(2, impact.size());
+    assertEquals("hashcode", impact.get(1).getField());
+  }
+
+  private static CombinationLookupMeta dialogStyleMeta() {
+    CombinationLookupMeta info = new CombinationLookupMeta();
+    info.setConnectionName(CONNECTION_NAME);
+    info.setTableName("junk_customer");
+    info.getFields().getKeyFields().add(new KeyField("customer_code", 
"customer_code"));
+    info.getFields().getReturnFields().setTechnicalKeyField("customer_tk");
+    info.getFields()
+        .getReturnFields()
+        .setTechKeyCreation(CombinationLookupMeta.CREATION_METHOD_TABLEMAX);
+    return info;
+  }
+
+  private static PipelineMeta pipelineWithoutConnection() {
+    MemoryMetadataProvider metadataProvider = new MemoryMetadataProvider();
+    PipelineMeta pipelineMeta = new PipelineMeta();
+    pipelineMeta.setMetadataProvider(metadataProvider);
+    return pipelineMeta;
+  }
+
+  private static PipelineMeta pipelineWithConnection() throws Exception {
+    MemoryMetadataProvider metadataProvider = new MemoryMetadataProvider();
+    metadataProvider
+        .getSerializer(DatabaseMeta.class)
+        .save(new DatabaseMeta(CONNECTION_NAME, "NONE", "Native", "", "", "", 
"", ""));
+    PipelineMeta pipelineMeta = new PipelineMeta();
+    pipelineMeta.setMetadataProvider(metadataProvider);
+    return pipelineMeta;
+  }
+
+  private static RowMeta incomingRow() {
+    RowMeta prev = new RowMeta();
+    prev.addValueMeta(new ValueMetaString("customer_code"));
+    return prev;
+  }
 }

Reply via email to