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 1033a5fd54 issue #6369 (#6726)
1033a5fd54 is described below

commit 1033a5fd549887152b0e90f1d53b8ceb09c0ba6c
Author: Matt Casters <[email protected]>
AuthorDate: Sat Mar 7 09:17:50 2026 +0100

    issue #6369 (#6726)
    
    * issue #6369
    
    * issue #6369 (cosmetic)
---
 .../ROOT/pages/pipeline/transforms/mergerows.adoc  |  15 +-
 .../transforms/0077-merge-rows-passthrough.hpl     | 322 +++++++++++++++++
 .../datasets/golden-merge-rows-passthrough.csv     |  13 +
 .../transforms/main-0077-merge-rows.hwf            |   3 +
 .../dataset/golden-merge-rows-passthrough.json     |  88 +++++
 .../0077-merge-rows-passthrough UNIT.json          |  64 ++++
 .../pipeline/transforms/mergerows/MergeRows.java   | 122 +++++--
 .../transforms/mergerows/MergeRowsData.java        |   7 +
 .../transforms/mergerows/MergeRowsDialog.java      | 399 +++++++++++++++------
 .../transforms/mergerows/MergeRowsMeta.java        |  47 +++
 .../{MergeRowsData.java => PassThroughField.java}  |  38 +-
 .../mergerows/messages/messages_en_US.properties   |  13 +-
 12 files changed, 976 insertions(+), 155 deletions(-)

diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/mergerows.adoc 
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/mergerows.adoc
index f8d8b317e6..a6e451b24f 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/mergerows.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/mergerows.adoc
@@ -75,14 +75,21 @@ In the subsequent transform, you can use the flag field 
generated by **Merge row
 |Transform name|Name of the transform.
 |Reference rows origin|Specify the transform that produces the reference rows. 
It's a Stream with original rows (rows that you want to compare the new rows 
to).
 |Compare rows origin|Specify the transform that produces the compare rows. 
It's a Stream with new rows
-|Flag fieldname|Specify the name of the flag field on the output stream.
-|Difference fieldname|Specify the name of the field to contain the difference 
in case the status is **changed**.
+|Flag field name|Specify the name of the flag field on the output stream.
+|Difference field name|Specify the name of the field to contain the difference 
in case the status is **changed**.
 The difference will be in the form of a "changes" array in JSON format.
-|Keys to match|Specify fields containing the keys on which to match. Click 
"Get key fields" to insert all of the fields from the reference rows
-|Values to compare|Specify fields contaning the values to compare. Click "Get 
value fields" to insert all of the fields from the compare rows.
+|Keys to match|Specify fields containing the keys on which to match. Click 
"Get key fields" to insert all the fields from the reference rows
+|Values to compare|Specify fields containing the values to compare. Click "Get 
value fields" to insert all the fields from the compare rows.
 Key fields do not need to be repeated here.
 |===
 
+== Passing through fields
+
+It's possible to pass through fields from the reference or compare data 
streams.
+To do this select the "Extra" tab and specify the fields you need to retain 
from either source transform.  You can use the `[Get fields to pass through]` 
button to add all possible source fields.  You can then select the rows to keep 
in the table view and use the "Keep" toolbar button (CTRL-K).
+
+NOTE: for `new` or `deleted` rows obviously only either reference or compare 
values are available.  The other ones will always be `null`.
+
 == Differences JSON example
 
 If you specify a field name for the differences in JSON, you will changes 
appear when a change was detected:
diff --git a/integration-tests/transforms/0077-merge-rows-passthrough.hpl 
b/integration-tests/transforms/0077-merge-rows-passthrough.hpl
new file mode 100644
index 0000000000..5b563396a5
--- /dev/null
+++ b/integration-tests/transforms/0077-merge-rows-passthrough.hpl
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+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.
+
+-->
+<pipeline>
+  <info>
+    <name>0077-merge-rows-passthrough</name>
+    <name_sync_with_filename>Y</name_sync_with_filename>
+    <description/>
+    <extended_description/>
+    <pipeline_version/>
+    <pipeline_type>Normal</pipeline_type>
+    <parameters>
+    </parameters>
+    <capture_transform_performance>N</capture_transform_performance>
+    
<transform_performance_capturing_delay>1000</transform_performance_capturing_delay>
+    
<transform_performance_capturing_size_limit>100</transform_performance_capturing_size_limit>
+    <created_user>-</created_user>
+    <created_date>2025/09/02 10:00:26.808</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2025/09/02 10:00:26.808</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>source1</from>
+      <to>Merge rows (diff)</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>source2</from>
+      <to>Merge rows (diff)</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Merge rows (diff)</from>
+      <to>validate</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>Merge rows (diff)</name>
+    <type>MergeRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <compare>source2</compare>
+    <flag_field>flagfield</flag_field>
+    <keys>
+      <key>id</key>
+    </keys>
+    <passthrough-fields>
+      <passthrough-field>
+        <referenceField>Y</referenceField>
+        <renameTo>ref-id</renameTo>
+        <sourceField>id</sourceField>
+      </passthrough-field>
+      <passthrough-field>
+        <referenceField>Y</referenceField>
+        <renameTo>ref-name</renameTo>
+        <sourceField>name</sourceField>
+      </passthrough-field>
+      <passthrough-field>
+        <referenceField>Y</referenceField>
+        <renameTo>ref-score</renameTo>
+        <sourceField>score</sourceField>
+      </passthrough-field>
+      <passthrough-field>
+        <referenceField>N</referenceField>
+        <renameTo>cmp-id</renameTo>
+        <sourceField>id</sourceField>
+      </passthrough-field>
+      <passthrough-field>
+        <referenceField>N</referenceField>
+        <renameTo>cmp-name</renameTo>
+        <sourceField>name</sourceField>
+      </passthrough-field>
+      <passthrough-field>
+        <referenceField>N</referenceField>
+        <renameTo>cmp-score</renameTo>
+        <sourceField>score</sourceField>
+      </passthrough-field>
+    </passthrough-fields>
+    <reference>source1</reference>
+    <values>
+      <value>name</value>
+      <value>score</value>
+    </values>
+    <attributes/>
+    <GUI>
+      <xloc>240</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>source1</name>
+    <type>DataGrid</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <data>
+      <line>
+        <item>1</item>
+        <item>James</item>
+        <item>3.6</item>
+      </line>
+      <line>
+        <item>2</item>
+        <item>Marie</item>
+        <item>8.3</item>
+      </line>
+      <line>
+        <item>3</item>
+        <item>Michael</item>
+        <item>9.1</item>
+      </line>
+      <line>
+        <item>4</item>
+        <item>Patrcia</item>
+        <item>4.2</item>
+      </line>
+      <line>
+        <item>5</item>
+        <item>Robert</item>
+        <item>5.0</item>
+      </line>
+      <line>
+        <item>6</item>
+        <item>Linda</item>
+        <item>1.6</item>
+      </line>
+      <line>
+        <item>7</item>
+        <item>David</item>
+        <item>7.2</item>
+      </line>
+      <line>
+        <item>8</item>
+        <item>Elizabeth</item>
+        <item>6.9</item>
+      </line>
+      <line>
+        <item>9</item>
+        <item>Willian</item>
+        <item>4.2</item>
+      </line>
+      <line>
+        <item>10</item>
+        <item>Barbara</item>
+        <item>0.5</item>
+      </line>
+    </data>
+    <fields>
+      <field>
+        <length>-1</length>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <name>id</name>
+        <type>Integer</type>
+      </field>
+      <field>
+        <length>-1</length>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <name>name</name>
+        <type>String</type>
+      </field>
+      <field>
+        <length>-1</length>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <name>score</name>
+        <format>#.#</format>
+        <type>Number</type>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>96</xloc>
+      <yloc>48</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>source2</name>
+    <type>DataGrid</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <data>
+      <line>
+        <item>1</item>
+        <item>James</item>
+        <item>3.6</item>
+      </line>
+      <line>
+        <item>3</item>
+        <item>Michael</item>
+        <item>9.1</item>
+      </line>
+      <line>
+        <item>4</item>
+        <item>Patricia</item>
+        <item>4.2</item>
+      </line>
+      <line>
+        <item>5</item>
+        <item>Robert</item>
+        <item>5.0</item>
+      </line>
+      <line>
+        <item>7</item>
+        <item>David</item>
+        <item>7.2</item>
+      </line>
+      <line>
+        <item>8</item>
+        <item>Elizabeth</item>
+        <item>6.9</item>
+      </line>
+      <line>
+        <item>9</item>
+        <item>William</item>
+        <item>4.2</item>
+      </line>
+      <line>
+        <item>10</item>
+        <item>Barbara</item>
+        <item>0.5</item>
+      </line>
+      <line>
+        <item>11</item>
+        <item>Richard</item>
+        <item>9.6</item>
+      </line>
+      <line>
+        <item>12</item>
+        <item>Susan</item>
+        <item>5.4</item>
+      </line>
+    </data>
+    <fields>
+      <field>
+        <length>-1</length>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <name>id</name>
+        <type>Integer</type>
+      </field>
+      <field>
+        <length>-1</length>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <name>name</name>
+        <type>String</type>
+      </field>
+      <field>
+        <length>-1</length>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <name>score</name>
+        <format>#.#</format>
+        <type>Number</type>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>96</xloc>
+      <yloc>144</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>validate</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>384</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git 
a/integration-tests/transforms/datasets/golden-merge-rows-passthrough.csv 
b/integration-tests/transforms/datasets/golden-merge-rows-passthrough.csv
new file mode 100644
index 0000000000..e8a9fec66e
--- /dev/null
+++ b/integration-tests/transforms/datasets/golden-merge-rows-passthrough.csv
@@ -0,0 +1,13 @@
+id,name,score,flagfield,ref-id,ref-name,ref-score,cmp-id,cmp-name,cmp-score
+1,James,3.6,identical,1,James,3.6,1,James,3.6
+2,Marie,8.3,deleted,2,Marie,8.3,,,
+3,Michael,9.1,identical,3,Michael,9.1,3,Michael,9.1
+4,Patricia,4.2,changed,4,Patrcia,4.2,4,Patricia,4.2
+5,Robert,5,identical,5,Robert,5,5,Robert,5
+6,Linda,1.6,deleted,6,Linda,1.6,,,
+7,David,7.2,identical,7,David,7.2,7,David,7.2
+8,Elizabeth,6.9,identical,8,Elizabeth,6.9,8,Elizabeth,6.9
+9,William,4.2,changed,9,Willian,4.2,9,William,4.2
+10,Barbara,0.5,identical,10,Barbara,0.5,10,Barbara,0.5
+11,Richard,9.6,new,,,,11,Richard,9.6
+12,Susan,5.4,new,,,,12,Susan,5.4
diff --git a/integration-tests/transforms/main-0077-merge-rows.hwf 
b/integration-tests/transforms/main-0077-merge-rows.hwf
index 3a2878fbad..381b71ece9 100644
--- a/integration-tests/transforms/main-0077-merge-rows.hwf
+++ b/integration-tests/transforms/main-0077-merge-rows.hwf
@@ -61,6 +61,9 @@ limitations under the License.
         <test_name>
           <name>0077-merge-rows-diff UNIT</name>
         </test_name>
+        <test_name>
+          <name>0077-merge-rows-passthrough UNIT</name>
+        </test_name>
       </test_names>
       <parallel>N</parallel>
       <xloc>272</xloc>
diff --git 
a/integration-tests/transforms/metadata/dataset/golden-merge-rows-passthrough.json
 
b/integration-tests/transforms/metadata/dataset/golden-merge-rows-passthrough.json
new file mode 100644
index 0000000000..4c88ab3e7d
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/dataset/golden-merge-rows-passthrough.json
@@ -0,0 +1,88 @@
+{
+  "base_filename": "golden-merge-rows-passthrough.csv",
+  "name": "golden-merge-rows-passthrough",
+  "description": "",
+  "dataset_fields": [
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 5,
+      "field_precision": 0,
+      "field_name": "id",
+      "field_format": "####0;-####0"
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "name",
+      "field_format": ""
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 1,
+      "field_precision": -1,
+      "field_name": "score",
+      "field_format": "#.#"
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "flagfield",
+      "field_format": ""
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 5,
+      "field_precision": 0,
+      "field_name": "ref-id",
+      "field_format": "####0;-####0"
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "ref-name",
+      "field_format": ""
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 1,
+      "field_precision": -1,
+      "field_name": "ref-score",
+      "field_format": "#.#"
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 5,
+      "field_precision": 0,
+      "field_name": "cmp-id",
+      "field_format": "####0;-####0"
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "cmp-name",
+      "field_format": ""
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 1,
+      "field_precision": -1,
+      "field_name": "cmp-score",
+      "field_format": "#.#"
+    }
+  ],
+  "folder_name": ""
+}
\ No newline at end of file
diff --git 
a/integration-tests/transforms/metadata/unit-test/0077-merge-rows-passthrough 
UNIT.json 
b/integration-tests/transforms/metadata/unit-test/0077-merge-rows-passthrough 
UNIT.json
new file mode 100644
index 0000000000..bf6e8c88cd
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/unit-test/0077-merge-rows-passthrough 
UNIT.json     
@@ -0,0 +1,64 @@
+{
+  "database_replacements": [],
+  "autoOpening": true,
+  "description": "",
+  "persist_filename": "",
+  "test_type": "UNIT_TEST",
+  "variableValues": [],
+  "basePath": "${HOP_UNIT_TESTS_FOLDER}",
+  "golden_data_sets": [
+    {
+      "field_mappings": [
+        {
+          "transform_field": "id",
+          "data_set_field": "id"
+        },
+        {
+          "transform_field": "name",
+          "data_set_field": "name"
+        },
+        {
+          "transform_field": "score",
+          "data_set_field": "score"
+        },
+        {
+          "transform_field": "flagfield",
+          "data_set_field": "flagfield"
+        },
+        {
+          "transform_field": "ref-id",
+          "data_set_field": "ref-id"
+        },
+        {
+          "transform_field": "ref-name",
+          "data_set_field": "ref-name"
+        },
+        {
+          "transform_field": "ref-score",
+          "data_set_field": "ref-score"
+        },
+        {
+          "transform_field": "cmp-id",
+          "data_set_field": "cmp-id"
+        },
+        {
+          "transform_field": "cmp-name",
+          "data_set_field": "cmp-name"
+        },
+        {
+          "transform_field": "cmp-score",
+          "data_set_field": "cmp-score"
+        }
+      ],
+      "field_order": [
+        "id"
+      ],
+      "data_set_name": "golden-merge-rows-passthrough",
+      "transform_name": "validate"
+    }
+  ],
+  "input_data_sets": [],
+  "name": "0077-merge-rows-passthrough UNIT",
+  "trans_test_tweaks": [],
+  "pipeline_filename": "./0077-merge-rows-passthrough.hpl"
+}
\ No newline at end of file
diff --git 
a/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRows.java
 
b/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRows.java
index 0d2a10aa3b..9776f959de 100644
--- 
a/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRows.java
+++ 
b/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRows.java
@@ -17,12 +17,14 @@
 
 package org.apache.hop.pipeline.transforms.mergerows;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import org.apache.commons.lang.StringUtils;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.exception.HopRowException;
 import org.apache.hop.core.exception.HopTransformException;
+import org.apache.hop.core.exception.HopValueException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.row.RowDataUtil;
@@ -115,6 +117,43 @@ public class MergeRows extends 
BaseTransform<MergeRowsMeta, MergeRowsData> {
           }
         }
       }
+
+      // Calculate the indices for the passthrough fields.
+      //
+      data.passThroughIndexes = new ArrayList<>();
+      data.oneRowMeta = data.oneRowSet.getRowMeta();
+      if (data.oneRowMeta == null) {
+        data.oneRowMeta =
+            getPipelineMeta().getPrevTransformFields(this, 
meta.getReferenceTransform());
+      }
+      data.twoRowMeta = data.twoRowSet.getRowMeta();
+      if (data.twoRowMeta == null) {
+        data.twoRowMeta =
+            getPipelineMeta().getPrevTransformFields(this, 
meta.getCompareTransform());
+      }
+      for (PassThroughField field : meta.getPassThroughFields()) {
+        int index;
+        if (field.isReferenceField()) {
+          index = data.oneRowMeta.indexOfValue(field.getSourceField());
+          if (index < 0) {
+            throw new HopTransformException(
+                "Unable to find passthrough field '"
+                    + field.getSourceField()
+                    + "' from reference transform "
+                    + meta.getReferenceTransform());
+          }
+        } else {
+          index = data.twoRowMeta.indexOfValue(field.getSourceField());
+          if (index < 0) {
+            throw new HopTransformException(
+                "Unable to find passthrough field '"
+                    + field.getSourceField()
+                    + "' from compare transform "
+                    + meta.getCompareTransform());
+          }
+        }
+        data.passThroughIndexes.add(index);
+      }
     }
 
     if (isRowLevel()) {
@@ -130,23 +169,13 @@ public class MergeRows extends 
BaseTransform<MergeRowsMeta, MergeRowsData> {
 
     if (data.outputRowMeta == null) {
       data.outputRowMeta = new RowMeta();
-      if (data.one != null) {
-        meta.getFields(
-            data.outputRowMeta,
-            getTransformName(),
-            new IRowMeta[] {data.oneRowSet.getRowMeta()},
-            null,
-            this,
-            metadataProvider);
-      } else {
-        meta.getFields(
-            data.outputRowMeta,
-            getTransformName(),
-            new IRowMeta[] {data.twoRowSet.getRowMeta()},
-            null,
-            this,
-            metadataProvider);
-      }
+      meta.getFields(
+          data.outputRowMeta,
+          getTransformName(),
+          new IRowMeta[] {data.oneRowMeta, data.twoRowMeta},
+          null,
+          this,
+          metadataProvider);
     }
 
     Object[] outputRow;
@@ -154,24 +183,27 @@ public class MergeRows extends 
BaseTransform<MergeRowsMeta, MergeRowsData> {
     String differenceJson = "{\"changes\":[]}";
     boolean getDifference = StringUtils.isNotEmpty(meta.getDiffJsonField());
 
-    if (data.one == null && data.two != null) { // Record 2 is flagged as new!
+    // Remember the rows used to compare: copy rows 'one' and 'two'.
+    copyOneTwo();
 
+    if (data.one == null && data.two != null) { // Record 2 is flagged as new!
       outputRow = data.two;
       flagField = VALUE_NEW;
 
       // Also get a next row from compare rowset...
       data.two = getRowFrom(data.twoRowSet);
+
     } else if (data.one != null && data.two == null) { // Record 1 is flagged 
as deleted!
       outputRow = data.one;
       flagField = VALUE_DELETED;
 
       // Also get a next row from reference rowset...
       data.one = getRowFrom(data.oneRowSet);
+
     } else { // OK, Here is the real start of the compare code!
 
       int compare = data.oneRowSet.getRowMeta().compare(data.one, data.two, 
data.keyNrs);
       if (compare == 0) { // The Key matches, we CAN compare the two rows...
-
         int compareValues = 0;
         if (getDifference) {
           JSONObject j = new JSONObject();
@@ -214,27 +246,52 @@ public class MergeRows extends 
BaseTransform<MergeRowsMeta, MergeRowsData> {
         data.two = getRowFrom(data.twoRowSet);
       } else {
         if (compare < 0) { // one < two
-
           outputRow = data.one;
           flagField = VALUE_DELETED;
-
           data.one = getRowFrom(data.oneRowSet);
         } else {
           outputRow = data.two;
           flagField = VALUE_NEW;
-
           data.two = getRowFrom(data.twoRowSet);
         }
       }
     }
 
+    int extraPassthroughFields = meta.getPassThroughFields().size();
+
     // Optionally add the difference JSON field
     //
     outputRow = RowDataUtil.resizeArray(outputRow, data.outputRowMeta.size());
+    int tailIndex = data.outputRowMeta.size() - 2 - extraPassthroughFields;
     if (getDifference && differenceJson != null) {
-      outputRow[data.outputRowMeta.size() - 2] = differenceJson;
+      outputRow[tailIndex++] = differenceJson;
+    } else {
+      tailIndex++;
+    }
+    outputRow[tailIndex++] = flagField;
+
+    // Copy the passthrough fields
+    //
+    for (int i = 0; i < extraPassthroughFields; i++) {
+      int sourceIndex = data.passThroughIndexes.get(i);
+      PassThroughField field = meta.getPassThroughFields().get(i);
+      if (field.isReferenceField()) {
+        // If the record is deleted, identical or changed we copy data
+        // from the reference row.
+        //
+        if (data.oneCopy != null && !VALUE_NEW.equals(flagField)) {
+          outputRow[tailIndex] = data.oneCopy[sourceIndex];
+        }
+      } else {
+        // If the record is new, identical or changed we copy data
+        // from the compared-to row.
+        //
+        if (data.twoCopy != null && !VALUE_DELETED.equals(flagField)) {
+          outputRow[tailIndex] = data.twoCopy[sourceIndex];
+        }
+      }
+      tailIndex++;
     }
-    outputRow[data.outputRowMeta.size() - 1] = flagField;
 
     // send the row to the next transforms...
     putRow(data.outputRowMeta, outputRow);
@@ -246,6 +303,23 @@ public class MergeRows extends 
BaseTransform<MergeRowsMeta, MergeRowsData> {
     return true;
   }
 
+  private void copyOneTwo() throws HopValueException {
+    data.oneCopy = null;
+    data.twoCopy = null;
+
+    // We only need the exact current record values in case we want to pass 
through fields
+    //
+    if (meta.getPassThroughFields().isEmpty()) {
+      return;
+    }
+    if (data.one != null) {
+      data.oneCopy = data.oneRowMeta.cloneRow(data.one);
+    }
+    if (data.two != null) {
+      data.twoCopy = data.twoRowMeta.cloneRow(data.two);
+    }
+  }
+
   /**
    * @see ITransform#init
    */
diff --git 
a/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsData.java
 
b/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsData.java
index 3f2cefc3c8..b65426267d 100644
--- 
a/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsData.java
+++ 
b/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsData.java
@@ -17,6 +17,7 @@
 
 package org.apache.hop.pipeline.transforms.mergerows;
 
+import java.util.List;
 import org.apache.hop.core.IRowSet;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.pipeline.transform.BaseTransformData;
@@ -32,7 +33,13 @@ public class MergeRowsData extends BaseTransformData 
implements ITransformData {
   public int[] valueNrs;
 
   public IRowSet oneRowSet;
+  public IRowMeta oneRowMeta;
   public IRowSet twoRowSet;
+  public IRowMeta twoRowMeta;
+
+  public List<Integer> passThroughIndexes;
+  public Object[] oneCopy;
+  public Object[] twoCopy;
 
   public MergeRowsData() {
     super();
diff --git 
a/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsDialog.java
 
b/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsDialog.java
index 4cd8e42908..6bcf8975d1 100644
--- 
a/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsDialog.java
+++ 
b/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsDialog.java
@@ -19,8 +19,10 @@ package org.apache.hop.pipeline.transforms.mergerows;
 
 import java.util.List;
 import org.apache.hop.core.Const;
+import org.apache.hop.core.Props;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.i18n.BaseMessages;
@@ -31,17 +33,19 @@ import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageDialogWithToggle;
+import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
 import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.layout.FormAttachment;
 import org.eclipse.swt.layout.FormData;
 import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.TableItem;
@@ -51,16 +55,19 @@ public class MergeRowsDialog extends BaseTransformDialog {
   private static final Class<?> PKG = MergeRowsMeta.class;
   public static final String STRING_SORT_WARNING_PARAMETER = 
"MergeRowsSortWarning";
 
-  private CCombo wReference;
+  private static final String YES = BaseMessages.getString("System.Combo.Yes");
+  private static final String NO = BaseMessages.getString("System.Combo.No");
 
-  private CCombo wCompare;
+  private CTabFolder wTabFolder;
 
+  private CCombo wReference;
+  private CCombo wCompare;
   private Text wFlagField;
   private Text wDiffField;
 
   private TableView wKeys;
-
   private TableView wValues;
+  private TableView wExtra;
 
   private final MergeRowsMeta input;
 
@@ -79,43 +86,46 @@ public class MergeRowsDialog extends BaseTransformDialog {
     ModifyListener lsMod = e -> input.setChanged();
     backupChanged = input.hasChanged();
 
-    Button wbKeys = new Button(shell, SWT.PUSH);
-    wbKeys.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.KeyFields.Button"));
-    FormData fdbKeys = new FormData();
-    fdbKeys.bottom = new FormAttachment(wOk, -margin);
-    fdbKeys.left = new FormAttachment(0, 0);
-    fdbKeys.right = new FormAttachment(50, -margin);
-    wbKeys.setLayoutData(fdbKeys);
-    wbKeys.addSelectionListener(
-        new SelectionAdapter() {
+    // We want 4 tabs: Sources, Keys, Values, Extra
+    //
+    wTabFolder = new CTabFolder(shell, SWT.BORDER);
+    PropsUi.setLook(wTabFolder, Props.WIDGET_STYLE_TAB);
 
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            getKeys();
-          }
-        });
+    addSourcesTab();
+    addKeysTab();
+    addValuesTab();
+    addExtraTab();
 
-    Button wbValues = new Button(shell, SWT.PUSH);
-    wbValues.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.ValueFields.Button"));
-    FormData fdbValues = new FormData();
-    fdbValues.bottom = new FormAttachment(wOk, -margin);
-    fdbValues.left = new FormAttachment(50, 0);
-    fdbValues.right = new FormAttachment(100, 0);
-    wbValues.setLayoutData(fdbValues);
-    wbValues.addSelectionListener(
-        new SelectionAdapter() {
+    FormData fdTabFolder = new FormData();
+    fdTabFolder.left = new FormAttachment(0, 0);
+    fdTabFolder.right = new FormAttachment(100, 0);
+    fdTabFolder.top = new FormAttachment(wTransformName, margin);
+    fdTabFolder.bottom = new FormAttachment(wOk, -2 * margin);
+    wTabFolder.setLayoutData(fdTabFolder);
+    wTabFolder.setSelection(0);
 
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            getValues();
-          }
-        });
+    getData();
+    input.setChanged(backupChanged);
+    focusTransformName();
+    BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
+
+    return transformName;
+  }
+
+  private void addSourcesTab() {
+    CTabItem wSourcesTab = new CTabItem(wTabFolder, SWT.NONE);
+    wSourcesTab.setFont(GuiResource.getInstance().getFontDefault());
+    wSourcesTab.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.SourcesTab.CTabItem"));
+
+    Composite wSourcesComp = new Composite(wTabFolder, SWT.NONE);
+    PropsUi.setLook(wSourcesComp);
+    wSourcesComp.setLayout(props.createFormLayout());
 
     // Get the previous transforms...
     String[] previousTransforms = 
pipelineMeta.getPrevTransformNames(transformName);
 
     // Send 'True' data to...
-    Label wlReference = new Label(shell, SWT.RIGHT);
+    Label wlReference = new Label(wSourcesComp, SWT.RIGHT);
     wlReference.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.Reference.Label"));
     PropsUi.setLook(wlReference);
     FormData fdlReference = new FormData();
@@ -123,13 +133,11 @@ public class MergeRowsDialog extends BaseTransformDialog {
     fdlReference.right = new FormAttachment(middle, -margin);
     fdlReference.top = new FormAttachment(wSpacer, margin);
     wlReference.setLayoutData(fdlReference);
-    wReference = new CCombo(shell, SWT.BORDER);
+    wReference = new CCombo(wSourcesComp, SWT.BORDER);
     PropsUi.setLook(wReference);
-
     if (previousTransforms != null) {
       wReference.setItems(previousTransforms);
     }
-
     wReference.addModifyListener(lsMod);
     FormData fdReference = new FormData();
     fdReference.left = new FormAttachment(middle, 0);
@@ -138,7 +146,7 @@ public class MergeRowsDialog extends BaseTransformDialog {
     wReference.setLayoutData(fdReference);
 
     // Send 'False' data to...
-    Label wlCompare = new Label(shell, SWT.RIGHT);
+    Label wlCompare = new Label(wSourcesComp, SWT.RIGHT);
     wlCompare.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.Compare.Label"));
     PropsUi.setLook(wlCompare);
     FormData fdlCompare = new FormData();
@@ -146,13 +154,11 @@ public class MergeRowsDialog extends BaseTransformDialog {
     fdlCompare.right = new FormAttachment(middle, -margin);
     fdlCompare.top = new FormAttachment(wReference, margin);
     wlCompare.setLayoutData(fdlCompare);
-    wCompare = new CCombo(shell, SWT.BORDER);
+    wCompare = new CCombo(wSourcesComp, SWT.BORDER);
     PropsUi.setLook(wCompare);
-
     if (previousTransforms != null) {
       wCompare.setItems(previousTransforms);
     }
-
     wCompare.addModifyListener(lsMod);
     FormData fdCompare = new FormData();
     fdCompare.top = new FormAttachment(wReference, margin);
@@ -161,52 +167,80 @@ public class MergeRowsDialog extends BaseTransformDialog {
     wCompare.setLayoutData(fdCompare);
 
     // The flag field line
-    Label wlFlagField = new Label(shell, SWT.RIGHT);
+    Label wlFlagField = new Label(wSourcesComp, SWT.RIGHT);
     wlFlagField.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.FlagField.Label"));
     PropsUi.setLook(wlFlagField);
-    FormData fdlFlagfield = new FormData();
-    fdlFlagfield.left = new FormAttachment(0, 0);
-    fdlFlagfield.right = new FormAttachment(middle, -margin);
-    fdlFlagfield.top = new FormAttachment(wCompare, margin);
-    wlFlagField.setLayoutData(fdlFlagfield);
-    wFlagField = new Text(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    FormData fdlFlagField = new FormData();
+    fdlFlagField.left = new FormAttachment(0, 0);
+    fdlFlagField.right = new FormAttachment(middle, -margin);
+    fdlFlagField.top = new FormAttachment(wCompare, margin);
+    wlFlagField.setLayoutData(fdlFlagField);
+    wFlagField = new Text(wSourcesComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
     PropsUi.setLook(wFlagField);
     wFlagField.addModifyListener(lsMod);
-    FormData fdFlagfield = new FormData();
-    fdFlagfield.top = new FormAttachment(wCompare, margin);
-    fdFlagfield.left = new FormAttachment(middle, 0);
-    fdFlagfield.right = new FormAttachment(100, 0);
-    wFlagField.setLayoutData(fdFlagfield);
+    FormData fdFlagField = new FormData();
+    fdFlagField.top = new FormAttachment(wCompare, margin);
+    fdFlagField.left = new FormAttachment(middle, 0);
+    fdFlagField.right = new FormAttachment(100, 0);
+    wFlagField.setLayoutData(fdFlagField);
 
     // The flag field line
-    Label wlDiffField = new Label(shell, SWT.RIGHT);
+    Label wlDiffField = new Label(wSourcesComp, SWT.RIGHT);
     wlDiffField.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.DiffField.Label"));
     PropsUi.setLook(wlDiffField);
-    FormData fdlDifffield = new FormData();
-    fdlDifffield.left = new FormAttachment(0, 0);
-    fdlDifffield.right = new FormAttachment(middle, -margin);
-    fdlDifffield.top = new FormAttachment(wFlagField, margin);
-    wlDiffField.setLayoutData(fdlDifffield);
-    wDiffField = new Text(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    FormData fdlDiffField = new FormData();
+    fdlDiffField.left = new FormAttachment(0, 0);
+    fdlDiffField.right = new FormAttachment(middle, -margin);
+    fdlDiffField.top = new FormAttachment(wFlagField, margin);
+    wlDiffField.setLayoutData(fdlDiffField);
+    wDiffField = new Text(wSourcesComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
     PropsUi.setLook(wDiffField);
     wDiffField.addModifyListener(lsMod);
-    FormData fdDifffield = new FormData();
-    fdDifffield.top = new FormAttachment(wFlagField, margin);
-    fdDifffield.left = new FormAttachment(middle, 0);
-    fdDifffield.right = new FormAttachment(100, 0);
-    wDiffField.setLayoutData(fdDifffield);
+    FormData fdDiffField = new FormData();
+    fdDiffField.top = new FormAttachment(wFlagField, margin);
+    fdDiffField.left = new FormAttachment(middle, 0);
+    fdDiffField.right = new FormAttachment(100, 0);
+    wDiffField.setLayoutData(fdDiffField);
+
+    FormData fdSourcesComp = new FormData();
+    fdSourcesComp.left = new FormAttachment(0, 0);
+    fdSourcesComp.top = new FormAttachment(0, 0);
+    fdSourcesComp.right = new FormAttachment(100, 0);
+    fdSourcesComp.bottom = new FormAttachment(100, 0);
+    wSourcesComp.setLayoutData(fdSourcesComp);
+
+    wSourcesComp.layout();
+    wSourcesTab.setControl(wSourcesComp);
+  }
+
+  private void addKeysTab() {
+    CTabItem wKeysTab = new CTabItem(wTabFolder, SWT.NONE);
+    wKeysTab.setFont(GuiResource.getInstance().getFontDefault());
+    wKeysTab.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.KeysTab.CTabItem"));
+
+    Composite wKeysComp = new Composite(wTabFolder, SWT.NONE);
+    PropsUi.setLook(wKeysComp);
+    wKeysComp.setLayout(props.createFormLayout());
+
+    // The [Get key fields] button at the bottom:
+    Button wbKeys = new Button(wKeysComp, SWT.PUSH);
+    wbKeys.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.KeyFields.Button"));
+    FormData fdbKeys = new FormData();
+    fdbKeys.bottom = new FormAttachment(100, -margin);
+    fdbKeys.left = new FormAttachment(0, 0);
+    wbKeys.setLayoutData(fdbKeys);
+    wbKeys.addListener(SWT.Selection, e -> getKeys());
 
     // THE KEYS TO MATCH...
-    Label wlKeys = new Label(shell, SWT.NONE);
+
+    Label wlKeys = new Label(wKeysComp, SWT.NONE);
     wlKeys.setText(BaseMessages.getString(PKG, "MergeRowsDialog.Keys.Label"));
     PropsUi.setLook(wlKeys);
     FormData fdlKeys = new FormData();
     fdlKeys.left = new FormAttachment(0, 0);
-    fdlKeys.top = new FormAttachment(wDiffField, margin);
+    fdlKeys.top = new FormAttachment(0, 0);
     wlKeys.setLayoutData(fdlKeys);
 
-    int nrKeyRows = (input.getKeyFields() != null ? 
input.getKeyFields().size() : 1);
-
     ColumnInfo[] ciKeys =
         new ColumnInfo[] {
           new ColumnInfo(
@@ -218,10 +252,10 @@ public class MergeRowsDialog extends BaseTransformDialog {
     wKeys =
         new TableView(
             variables,
-            shell,
+            wKeysComp,
             SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL,
             ciKeys,
-            nrKeyRows,
+            1,
             lsMod,
             props);
 
@@ -229,20 +263,46 @@ public class MergeRowsDialog extends BaseTransformDialog {
     fdKeys.top = new FormAttachment(wlKeys, margin);
     fdKeys.left = new FormAttachment(0, 0);
     fdKeys.bottom = new FormAttachment(wbKeys, -margin);
-    fdKeys.right = new FormAttachment(50, -margin);
+    fdKeys.right = new FormAttachment(100, 0);
     wKeys.setLayoutData(fdKeys);
 
+    FormData fdKeysComp = new FormData();
+    fdKeysComp.left = new FormAttachment(0, 0);
+    fdKeysComp.top = new FormAttachment(0, 0);
+    fdKeysComp.right = new FormAttachment(100, 0);
+    fdKeysComp.bottom = new FormAttachment(100, 0);
+    wKeysComp.setLayoutData(fdKeysComp);
+
+    wKeysComp.layout();
+    wKeysTab.setControl(wKeysComp);
+  }
+
+  private void addValuesTab() {
+    CTabItem wValuesTab = new CTabItem(wTabFolder, SWT.NONE);
+    wValuesTab.setFont(GuiResource.getInstance().getFontDefault());
+    wValuesTab.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.ValuesTab.CTabItem"));
+
+    Composite wValuesComp = new Composite(wTabFolder, SWT.NONE);
+    PropsUi.setLook(wValuesComp);
+    wValuesComp.setLayout(props.createFormLayout());
+
+    Button wbValues = new Button(wValuesComp, SWT.PUSH);
+    wbValues.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.ValueFields.Button"));
+    FormData fdbValues = new FormData();
+    fdbValues.bottom = new FormAttachment(100, -margin);
+    fdbValues.left = new FormAttachment(0, 0);
+    wbValues.setLayoutData(fdbValues);
+    wbValues.addListener(SWT.Selection, e -> getValues());
+
     // VALUES TO COMPARE
-    Label wlValues = new Label(shell, SWT.NONE);
+    Label wlValues = new Label(wValuesComp, SWT.NONE);
     wlValues.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.Values.Label"));
     PropsUi.setLook(wlValues);
     FormData fdlValues = new FormData();
-    fdlValues.left = new FormAttachment(50, 0);
+    fdlValues.left = new FormAttachment(0, 0);
     fdlValues.top = new FormAttachment(wDiffField, margin);
     wlValues.setLayoutData(fdlValues);
 
-    int nrValueRows = (input.getValueFields() != null ? 
input.getValueFields().size() : 1);
-
     ColumnInfo[] ciValues =
         new ColumnInfo[] {
           new ColumnInfo(
@@ -254,26 +314,129 @@ public class MergeRowsDialog extends BaseTransformDialog 
{
     wValues =
         new TableView(
             variables,
-            shell,
+            wValuesComp,
             SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL,
             ciValues,
-            nrValueRows,
+            1,
             lsMod,
             props);
 
     FormData fdValues = new FormData();
     fdValues.top = new FormAttachment(wlValues, margin);
-    fdValues.left = new FormAttachment(50, 0);
+    fdValues.left = new FormAttachment(0, 0);
     fdValues.bottom = new FormAttachment(wbValues, -margin);
     fdValues.right = new FormAttachment(100, 0);
     wValues.setLayoutData(fdValues);
 
-    getData();
-    input.setChanged(backupChanged);
-    focusTransformName();
-    BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
+    FormData fdValuesComp = new FormData();
+    fdValuesComp.left = new FormAttachment(0, 0);
+    fdValuesComp.top = new FormAttachment(0, 0);
+    fdValuesComp.right = new FormAttachment(100, 0);
+    fdValuesComp.bottom = new FormAttachment(100, 0);
+    wValuesComp.setLayoutData(fdValuesComp);
 
-    return transformName;
+    wValuesComp.layout();
+    wValuesTab.setControl(wValuesComp);
+  }
+
+  private void addExtraTab() {
+    CTabItem wExtraTab = new CTabItem(wTabFolder, SWT.NONE);
+    wExtraTab.setFont(GuiResource.getInstance().getFontDefault());
+    wExtraTab.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.ExtraTab.CTabItem"));
+
+    Composite wExtraComp = new Composite(wTabFolder, SWT.NONE);
+    PropsUi.setLook(wExtraComp);
+    wExtraComp.setLayout(props.createFormLayout());
+
+    // The [Get fields] button at the bottom
+    Button wbExtra = new Button(wExtraComp, SWT.PUSH);
+    wbExtra.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.ExtraFields.Button"));
+    FormData fdbExtra = new FormData();
+    fdbExtra.bottom = new FormAttachment(100, -margin);
+    fdbExtra.left = new FormAttachment(0, 0);
+    wbExtra.setLayoutData(fdbExtra);
+    wbExtra.addListener(SWT.Selection, e -> getPassThroughFields());
+
+    // Extra fields to pass through to the output
+    Label wlExtra = new Label(wExtraComp, SWT.NONE);
+    wlExtra.setText(BaseMessages.getString(PKG, 
"MergeRowsDialog.Extra.Label"));
+    PropsUi.setLook(wlExtra);
+    FormData fdlExtra = new FormData();
+    fdlExtra.left = new FormAttachment(0, 0);
+    fdlExtra.top = new FormAttachment(wDiffField, margin);
+    wlExtra.setLayoutData(fdlExtra);
+
+    ColumnInfo[] ciExtra =
+        new ColumnInfo[] {
+          new ColumnInfo(
+              BaseMessages.getString(PKG, 
"MergeRowsDialog.ExtraColumn.Reference.Label"),
+              ColumnInfo.COLUMN_TYPE_CCOMBO,
+              YES,
+              NO),
+          new ColumnInfo(
+              BaseMessages.getString(PKG, 
"MergeRowsDialog.ExtraColumn.Field.Label"),
+              ColumnInfo.COLUMN_TYPE_TEXT,
+              false),
+          new ColumnInfo(
+              BaseMessages.getString(PKG, 
"MergeRowsDialog.ExtraColumn.RenameTo.Label"),
+              ColumnInfo.COLUMN_TYPE_TEXT,
+              false),
+        };
+
+    wExtra =
+        new TableView(
+            variables,
+            wExtraComp,
+            SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL,
+            ciExtra,
+            1,
+            lsMod,
+            props);
+
+    FormData fdExtra = new FormData();
+    fdExtra.top = new FormAttachment(wlExtra, margin);
+    fdExtra.left = new FormAttachment(0, 0);
+    fdExtra.bottom = new FormAttachment(wbExtra, -margin);
+    fdExtra.right = new FormAttachment(100, 0);
+    wExtra.setLayoutData(fdExtra);
+
+    FormData fdExtraComp = new FormData();
+    fdExtraComp.left = new FormAttachment(0, 0);
+    fdExtraComp.top = new FormAttachment(0, 0);
+    fdExtraComp.right = new FormAttachment(100, 0);
+    fdExtraComp.bottom = new FormAttachment(100, 0);
+    wExtraComp.setLayoutData(fdExtraComp);
+
+    wExtraComp.layout();
+    wExtraTab.setControl(wExtraComp);
+  }
+
+  private void getPassThroughFields() {
+    MergeRowsMeta meta = new MergeRowsMeta();
+    getInfo(meta);
+
+    // Get all fields from both reference and compare transforms
+    // and add them to the wExtra table view.
+    try {
+      IRowMeta refRowMeta =
+          pipelineMeta.getTransformFields(variables, 
meta.getReferenceTransform());
+      for (IValueMeta refValueMeta : refRowMeta.getValueMetaList()) {
+        TableItem item = new TableItem(wExtra.table, SWT.NONE);
+        item.setText(1, YES);
+        item.setText(2, refValueMeta.getName());
+        item.setText(3, "ref-" + refValueMeta.getName());
+      }
+      IRowMeta cmpRowMeta = pipelineMeta.getTransformFields(variables, 
meta.getCompareTransform());
+      for (IValueMeta cmpValueMeta : cmpRowMeta.getValueMetaList()) {
+        TableItem item = new TableItem(wExtra.table, SWT.NONE);
+        item.setText(1, NO);
+        item.setText(2, cmpValueMeta.getName());
+        item.setText(3, "cmp-" + cmpValueMeta.getName());
+      }
+      wExtra.optimizeTableView();
+    } catch (Exception e) {
+      new ErrorDialog(shell, "Error", "Error adding passthrough fields", e);
+    }
   }
 
   /** Copy information from the meta-data input to the dialog fields. */
@@ -286,13 +449,23 @@ public class MergeRowsDialog extends BaseTransformDialog {
     wDiffField.setText(Const.NVL(input.getDiffJsonField(), ""));
 
     for (int i = 0; i < input.getKeyFields().size(); i++) {
-      TableItem item = wKeys.table.getItem(i);
+      TableItem item = new TableItem(wKeys.table, SWT.NONE);
       item.setText(1, Const.NVL(input.getKeyFields().get(i), ""));
     }
+    wKeys.optimizeTableView();
     for (int i = 0; i < input.getValueFields().size(); i++) {
-      TableItem item = wValues.table.getItem(i);
+      TableItem item = new TableItem(wValues.table, SWT.NONE);
       item.setText(1, Const.NVL(input.getValueFields().get(i), ""));
     }
+    wValues.optimizeTableView();
+    for (int i = 0; i < input.getPassThroughFields().size(); i++) {
+      PassThroughField field = input.getPassThroughFields().get(i);
+      TableItem item = new TableItem(wExtra.table, SWT.NONE);
+      item.setText(1, field.isReferenceField() ? YES : NO);
+      item.setText(2, Const.NVL(field.getSourceField(), ""));
+      item.setText(3, Const.NVL(field.getRenameTo(), ""));
+    }
+    wExtra.optimizeTableView();
   }
 
   private void cancel() {
@@ -305,33 +478,11 @@ public class MergeRowsDialog extends BaseTransformDialog {
     if (Utils.isEmpty(wTransformName.getText())) {
       return;
     }
-
-    input.setReferenceTransform(wReference.getText());
-    input.setCompareTransform(wCompare.getText());
-    List<IStream> infoStreams = input.getTransformIOMeta().getInfoStreams();
-    
infoStreams.get(0).setTransformMeta(pipelineMeta.findTransform(wReference.getText()));
-    
infoStreams.get(1).setTransformMeta(pipelineMeta.findTransform(wCompare.getText()));
-    input.setFlagField(wFlagField.getText());
-    input.setDiffJsonField(wDiffField.getText());
-
-    int nrKeys = wKeys.nrNonEmpty();
-    int nrValues = wValues.nrNonEmpty();
-
-    input.getKeyFields().clear();
-    for (int i = 0; i < nrKeys; i++) {
-      TableItem item = wKeys.getNonEmpty(i);
-      input.getKeyFields().add(item.getText(1));
-    }
-
-    input.getValueFields().clear();
-    for (int i = 0; i < nrValues; i++) {
-      TableItem item = wValues.getNonEmpty(i);
-      input.getValueFields().add(item.getText(1));
-    }
+    getInfo(input);
 
     transformName = wTransformName.getText(); // return value
 
-    if (nrKeys > 0
+    if (!input.getKeyFields().isEmpty()
         && 
"Y".equalsIgnoreCase(props.getCustomParameter(STRING_SORT_WARNING_PARAMETER, 
"Y"))) {
       MessageDialogWithToggle md =
           new MessageDialogWithToggle(
@@ -353,6 +504,34 @@ public class MergeRowsDialog extends BaseTransformDialog {
     dispose();
   }
 
+  private void getInfo(MergeRowsMeta meta) {
+    meta.setReferenceTransform(wReference.getText());
+    meta.setCompareTransform(wCompare.getText());
+    List<IStream> infoStreams = meta.getTransformIOMeta().getInfoStreams();
+    
infoStreams.get(0).setTransformMeta(pipelineMeta.findTransform(wReference.getText()));
+    
infoStreams.get(1).setTransformMeta(pipelineMeta.findTransform(wCompare.getText()));
+    meta.setFlagField(wFlagField.getText());
+    meta.setDiffJsonField(wDiffField.getText());
+
+    meta.getKeyFields().clear();
+    for (TableItem item : wKeys.getNonEmptyItems()) {
+      meta.getKeyFields().add(item.getText(1));
+    }
+
+    meta.getValueFields().clear();
+    for (TableItem item : wValues.getNonEmptyItems()) {
+      meta.getValueFields().add(item.getText(1));
+    }
+
+    meta.getPassThroughFields().clear();
+    for (TableItem item : wExtra.getNonEmptyItems()) {
+      boolean reference = YES.equalsIgnoreCase(item.getText(1));
+      String fieldName = item.getText(2);
+      String renameTo = item.getText(3);
+      meta.getPassThroughFields().add(new PassThroughField(fieldName, 
renameTo, reference));
+    }
+  }
+
   private void getKeys() {
     try {
       TransformMeta transformMeta = 
pipelineMeta.findTransform(wReference.getText());
diff --git 
a/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsMeta.java
 
b/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsMeta.java
index c72bff7080..1f1f31ff3d 100644
--- 
a/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsMeta.java
+++ 
b/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsMeta.java
@@ -99,18 +99,30 @@ public class MergeRowsMeta extends 
BaseTransformMeta<MergeRows, MergeRowsData> {
       injectionKeyDescription = "MergeRows.Injection.DIFF_FIELD")
   private String diffJsonField;
 
+  @HopMetadataProperty(
+      groupKey = "passthrough-fields",
+      key = "passthrough-field",
+      injectionGroupKey = "PASSTHROUGH_FIELDS",
+      injectionGroupDescription = "MergeRows.Injection.PASSTHROUGH_FIELDS",
+      injectionKey = "PASSTHROUGH_FIELD",
+      injectionKeyDescription = "MergeRows.Injection.PASSTHROUGH_FIELD")
+  private List<PassThroughField> passThroughFields;
+
   public MergeRowsMeta() {
     super();
     keyFields = new ArrayList<>();
     valueFields = new ArrayList<>();
+    passThroughFields = new ArrayList<>();
   }
 
   public MergeRowsMeta(MergeRowsMeta m) {
+    this();
     this.flagField = m.flagField;
     this.keyFields = new ArrayList<>(m.keyFields);
     this.valueFields = new ArrayList<>(m.valueFields);
     this.referenceTransform = m.referenceTransform;
     this.compareTransform = m.compareTransform;
+    m.getPassThroughFields().forEach(f -> this.passThroughFields.add(new 
PassThroughField(f)));
   }
 
   @Override
@@ -165,6 +177,41 @@ public class MergeRowsMeta extends 
BaseTransformMeta<MergeRows, MergeRowsData> {
     IValueMeta flagFieldValue = new ValueMetaString(flagField);
     flagFieldValue.setOrigin(name);
     r.addValueMeta(flagFieldValue);
+
+    if (info == null || info.length != 2) {
+      return;
+    }
+
+    // Add the passthrough fields
+    //
+    for (PassThroughField field : passThroughFields) {
+      IValueMeta valueMeta = null;
+      if (field.isReferenceField()) {
+        if (info[0] != null) {
+          valueMeta = info[0].searchValueMeta(field.getSourceField());
+          if (valueMeta == null) {
+            throw new HopTransformException(
+                "Unable to find passthrough reference field '" + 
field.getSourceField() + "'");
+          }
+        }
+      } else {
+        if (info[1] != null) {
+          valueMeta = info[1].searchValueMeta(field.getSourceField());
+          if (valueMeta == null) {
+            throw new HopTransformException(
+                "Unable to find passthrough compare field '" + 
field.getSourceField() + "'");
+          }
+        }
+      }
+      if (valueMeta != null) {
+        // create a copy to prevent renaming fields from the reference/compare 
transforms.
+        valueMeta = valueMeta.clone();
+        if (StringUtils.isNotEmpty(field.getRenameTo())) {
+          valueMeta.setName(field.getRenameTo());
+        }
+        r.addValueMeta(valueMeta);
+      }
+    }
   }
 
   @Override
diff --git 
a/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsData.java
 
b/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/PassThroughField.java
similarity index 52%
copy from 
plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsData.java
copy to 
plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/PassThroughField.java
index 3f2cefc3c8..03cc7dde70 100644
--- 
a/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/MergeRowsData.java
+++ 
b/plugins/transforms/mergerows/src/main/java/org/apache/hop/pipeline/transforms/mergerows/PassThroughField.java
@@ -6,7 +6,7 @@
  * (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
+ *       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,
@@ -17,24 +17,30 @@
 
 package org.apache.hop.pipeline.transforms.mergerows;
 
-import org.apache.hop.core.IRowSet;
-import org.apache.hop.core.row.IRowMeta;
-import org.apache.hop.pipeline.transform.BaseTransformData;
-import org.apache.hop.pipeline.transform.ITransformData;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.hop.metadata.api.HopMetadataProperty;
 
-@SuppressWarnings("java:S1104")
-public class MergeRowsData extends BaseTransformData implements ITransformData 
{
-  public IRowMeta outputRowMeta;
+@Getter
+@Setter
+public class PassThroughField {
+  @HopMetadataProperty private String sourceField;
 
-  public Object[] one;
-  public Object[] two;
-  public int[] keyNrs;
-  public int[] valueNrs;
+  @HopMetadataProperty private String renameTo;
 
-  public IRowSet oneRowSet;
-  public IRowSet twoRowSet;
+  @HopMetadataProperty private boolean referenceField;
 
-  public MergeRowsData() {
-    super();
+  public PassThroughField() {}
+
+  public PassThroughField(String sourceField, String renameTo, boolean 
referenceField) {
+    this.sourceField = sourceField;
+    this.renameTo = renameTo;
+    this.referenceField = referenceField;
+  }
+
+  public PassThroughField(PassThroughField f) {
+    this.sourceField = f.sourceField;
+    this.renameTo = f.renameTo;
+    this.referenceField = f.referenceField;
   }
 }
diff --git 
a/plugins/transforms/mergerows/src/main/resources/org/apache/hop/pipeline/transforms/mergerows/messages/messages_en_US.properties
 
b/plugins/transforms/mergerows/src/main/resources/org/apache/hop/pipeline/transforms/mergerows/messages/messages_en_US.properties
index 566d8c9e3f..8adc64c5fc 100644
--- 
a/plugins/transforms/mergerows/src/main/resources/org/apache/hop/pipeline/transforms/mergerows/messages/messages_en_US.properties
+++ 
b/plugins/transforms/mergerows/src/main/resources/org/apache/hop/pipeline/transforms/mergerows/messages/messages_en_US.properties
@@ -24,6 +24,8 @@ MergeRows.Injection.KEY_FIELDS=Specify key fields to compare.
 MergeRows.Injection.KEY_FIELD=The key field to match with
 MergeRows.Injection.VALUE_FIELDS=Specify value fields to compare.
 MergeRows.Injection.VALUE_FIELD=The field to compare
+MergeRows.Injection.PASSTHROUGH_FIELDS = The fields to pass through to the 
result
+MergeRows.Injection.PASSTHROUGH_FIELD = One fields to pass through to the 
result
 MergeRows.LineNumber=linenr
 MergeRows.Log.BothTrueAndFalseNeeded=Both the ''true'' and the ''false'' 
transforms need to be supplied, or neither
 MergeRows.Log.DataInfo=ONE\: {0} / TWO\: 
@@ -56,4 +58,13 @@ MergeRowsMeta.Exception.FlagFieldNotSpecified=The flag field 
is not specified.
 MergeRowsMeta.Exception.UnableToLoadTransformMeta=Unable to load transform 
info from XML
 MergeRowsMeta.InfoStream.FirstStream.Description=Reference stream to merge
 MergeRowsMeta.InfoStream.SecondStream.Description=Compare (changed data) 
stream to merge
-MergeRowsMeta.keyword=merge,row
+MergeRowsMeta.keyword=merge,row,difference
+MergeRowsDialog.ExtraFields.Button = Get fields to pass through
+MergeRowsDialog.Extra.Label=Extra fields to pass through from sources to the 
output\:
+MergeRowsDialog.ExtraColumn.Reference.Label=Reference?
+MergeRowsDialog.ExtraColumn.Field.Label=Source field
+MergeRowsDialog.ExtraColumn.RenameTo.Label=Rename to
+MergeRowsDialog.SourcesTab.CTabItem = Sources
+MergeRowsDialog.KeysTab.CTabItem = Keys
+MergeRowsDialog.ValuesTab.CTabItem = Values
+MergeRowsDialog.ExtraTab.CTabItem = Extra

Reply via email to