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 4c889cfb01 fix NPE in Select Values when converting String to 
Timestamp. fixes #6306 (#6315)
4c889cfb01 is described below

commit 4c889cfb010660bda0d542a36409a7a3eb8d1507
Author: Bart Maertens <[email protected]>
AuthorDate: Fri Jan 9 10:58:45 2026 +0000

    fix NPE in Select Values when converting String to Timestamp. fixes #6306 
(#6315)
---
 .../row/value/timestamp/SimpleTimestampFormat.java |   6 +-
 .../value/timestamp/SimpleTimestampFormatTest.java |  14 ++
 .../transforms/0084-string-to-timestamp-6306.hpl   | 181 +++++++++++++++++++++
 .../datasets/golden-string-to-timestamp.csv        |  13 ++
 .../main-0084-string-to-timestamp-6306.hwf         |  80 +++++++++
 .../dataset/golden-string-to-timestamp.json        |  48 ++++++
 .../0084-string-to-timestamp-6306 UNIT.json        |  48 ++++++
 7 files changed, 389 insertions(+), 1 deletion(-)

diff --git 
a/core/src/main/java/org/apache/hop/core/row/value/timestamp/SimpleTimestampFormat.java
 
b/core/src/main/java/org/apache/hop/core/row/value/timestamp/SimpleTimestampFormat.java
index 4eb53771e9..86c10a530a 100644
--- 
a/core/src/main/java/org/apache/hop/core/row/value/timestamp/SimpleTimestampFormat.java
+++ 
b/core/src/main/java/org/apache/hop/core/row/value/timestamp/SimpleTimestampFormat.java
@@ -103,7 +103,8 @@ public class SimpleTimestampFormat extends SimpleDateFormat 
{
       final Class<?>[] paramTypes = new Class<?>[] {localeCategoryClass};
       getDefaultLocaleMethod = localeClass.getMethod("getDefault", paramTypes);
       final java.lang.reflect.Field formatField = 
localeCategoryClass.getField("FORMAT");
-      // we pass null because the FORMAT is an enumeration constant(the same 
applies for class
+      // we pass null because the FORMAT is an enumeration constant(the same 
applies
+      // for class
       // variables)
       formatCategory = formatField.get(null);
     } catch (Exception e) {
@@ -338,6 +339,9 @@ public class SimpleTimestampFormat extends SimpleDateFormat 
{
     Date tempDate;
     if (compatibleToSuperPattern) {
       tempDate = super.parse(text, pos);
+      if (tempDate == null) {
+        return null;
+      }
       return new Timestamp(tempDate.getTime());
     }
 
diff --git 
a/core/src/test/java/org/apache/hop/core/row/value/timestamp/SimpleTimestampFormatTest.java
 
b/core/src/test/java/org/apache/hop/core/row/value/timestamp/SimpleTimestampFormatTest.java
index 2ec59a70fe..695104d0dc 100644
--- 
a/core/src/test/java/org/apache/hop/core/row/value/timestamp/SimpleTimestampFormatTest.java
+++ 
b/core/src/test/java/org/apache/hop/core/row/value/timestamp/SimpleTimestampFormatTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
 
 import java.sql.Timestamp;
 import java.text.ParseException;
+import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Date;
@@ -293,4 +294,17 @@ public class SimpleTimestampFormatTest {
       checkFormat("LOCALE.DEFAULT", stf);
     }
   }
+
+  @Test
+  public void testParseNull() throws Exception {
+    String pattern = "yyyy-MM-dd HH:mm:ss.S";
+    SimpleTimestampFormat stf = new SimpleTimestampFormat(pattern);
+    // This value mimics a bad parse that might return null from super.parse()
+    // but previously caused an NPE in SimpleTimestampFormat
+    String invalidValue = "this-is-not-a-date";
+    ParsePosition pos = new ParsePosition(0);
+    java.util.Date result = stf.parse(invalidValue, pos);
+    // Should return null (and set error index) instead of throwing NPE
+    assertEquals(null, result);
+  }
 }
diff --git a/integration-tests/transforms/0084-string-to-timestamp-6306.hpl 
b/integration-tests/transforms/0084-string-to-timestamp-6306.hpl
new file mode 100644
index 0000000000..adcf247a3f
--- /dev/null
+++ b/integration-tests/transforms/0084-string-to-timestamp-6306.hpl
@@ -0,0 +1,181 @@
+<?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>0084-string-to-timestamp-6306</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>2026/01/05 15:49:38.689</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2026/01/05 15:49:38.689</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>input data</from>
+      <to>str -> timestamp</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>str -> timestamp</from>
+      <to>ok</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>str -> timestamp</from>
+      <to>error</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>error</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>416</xloc>
+      <yloc>352</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>input data</name>
+    <type>DataGrid</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <data>
+      <line>
+        <item>01-01-2000 00:00:00</item>
+      </line>
+      <line>
+        <item>01-01-2000 00:01:01</item>
+      </line>
+      <line>
+        <item>I am not a timestamp</item>
+      </line>
+      <line>
+        <item>null</item>
+      </line>
+      <line>
+        <item/>
+      </line>
+    </data>
+    <fields>
+      <field>
+        <length>-1</length>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <name>timestamp_string</name>
+        <type>String</type>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>128</xloc>
+      <yloc>160</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>ok</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>640</xloc>
+      <yloc>160</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>str -> timestamp</name>
+    <type>SelectValues</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <fields>
+      <meta>
+        <conversion_mask>MM-dd-yyyy HH:mm:ss</conversion_mask>
+        <date_format_lenient>N</date_format_lenient>
+        <length>-2</length>
+        <lenient_string_to_number>N</lenient_string_to_number>
+        <name>timestamp_string</name>
+        <precision>-2</precision>
+        <rename>timestamp_string</rename>
+        <roundingType>half_even</roundingType>
+        <storage_type/>
+        <type>Timestamp</type>
+      </meta>
+      <select_unspecified>N</select_unspecified>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>416</xloc>
+      <yloc>160</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+    <error>
+      <source_transform>str -> timestamp</source_transform>
+      <target_transform>error</target_transform>
+      <is_enabled>Y</is_enabled>
+      <nr_valuename>nb_errors</nr_valuename>
+      <descriptions_valuename>error_description</descriptions_valuename>
+      <fields_valuename>error_field</fields_valuename>
+      <codes_valuename>error_codes</codes_valuename>
+      <max_errors/>
+      <max_pct_errors/>
+      <min_pct_rows/>
+    </error>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git 
a/integration-tests/transforms/datasets/golden-string-to-timestamp.csv 
b/integration-tests/transforms/datasets/golden-string-to-timestamp.csv
new file mode 100644
index 0000000000..dd6f8da080
--- /dev/null
+++ b/integration-tests/transforms/datasets/golden-string-to-timestamp.csv
@@ -0,0 +1,13 @@
+timestamp_string,nb_errors,error_description,error_field,error_codes
+I am not a timestamp,1,"
+
+timestamp_string Timestamp : couldn't convert string [I am not a timestamp] to 
a timestamp, expecting format [yyyy-mm-dd hh:mm:ss.ffffff]
+Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
+
+",timestamp_string,SELECT001
+null,1,"
+
+timestamp_string Timestamp : couldn't convert string [null] to a timestamp, 
expecting format [yyyy-mm-dd hh:mm:ss.ffffff]
+Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
+
+",timestamp_string,SELECT001
diff --git 
a/integration-tests/transforms/main-0084-string-to-timestamp-6306.hwf 
b/integration-tests/transforms/main-0084-string-to-timestamp-6306.hwf
new file mode 100644
index 0000000000..4b1bc0541d
--- /dev/null
+++ b/integration-tests/transforms/main-0084-string-to-timestamp-6306.hwf
@@ -0,0 +1,80 @@
+<?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.
+
+-->
+<workflow>
+  <name>main-0084-string-to-timestamp-6306</name>
+  <name_sync_with_filename>Y</name_sync_with_filename>
+  <description/>
+  <extended_description/>
+  <workflow_version/>
+  <created_user>-</created_user>
+  <created_date>2026/01/07 13:36:59.890</created_date>
+  <modified_user>-</modified_user>
+  <modified_date>2026/01/07 13:36:59.890</modified_date>
+  <parameters>
+    </parameters>
+  <actions>
+    <action>
+      <name>Start</name>
+      <description/>
+      <type>SPECIAL</type>
+      <attributes/>
+      <DayOfMonth>1</DayOfMonth>
+      <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
+      <hour>12</hour>
+      <intervalMinutes>60</intervalMinutes>
+      <intervalSeconds>0</intervalSeconds>
+      <minutes>0</minutes>
+      <repeat>N</repeat>
+      <schedulerType>0</schedulerType>
+      <weekDay>1</weekDay>
+      <parallel>N</parallel>
+      <xloc>128</xloc>
+      <yloc>64</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>Run Pipeline Unit Tests</name>
+      <description/>
+      <type>RunPipelineTests</type>
+      <attributes/>
+      <test_names>
+        <test_name>
+          <name>0084-string-to-timestamp-6306 UNIT</name>
+        </test_name>
+      </test_names>
+      <parallel>N</parallel>
+      <xloc>384</xloc>
+      <yloc>64</yloc>
+      <attributes_hac/>
+    </action>
+  </actions>
+  <hops>
+    <hop>
+      <from>Start</from>
+      <to>Run Pipeline Unit Tests</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>Y</unconditional>
+    </hop>
+  </hops>
+  <notepads>
+  </notepads>
+  <attributes/>
+</workflow>
diff --git 
a/integration-tests/transforms/metadata/dataset/golden-string-to-timestamp.json 
b/integration-tests/transforms/metadata/dataset/golden-string-to-timestamp.json
new file mode 100644
index 0000000000..7ac2b0f2ee
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/dataset/golden-string-to-timestamp.json
@@ -0,0 +1,48 @@
+{
+  "base_filename": "golden-string-to-timestamp.csv",
+  "name": "golden-string-to-timestamp",
+  "description": "",
+  "dataset_fields": [
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "timestamp_string",
+      "field_format": ""
+    },
+    {
+      "field_comment": "",
+      "field_length": 3,
+      "field_type": 5,
+      "field_precision": 0,
+      "field_name": "nb_errors",
+      "field_format": "####0;-####0"
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "error_description",
+      "field_format": ""
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "error_field",
+      "field_format": ""
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "error_codes",
+      "field_format": ""
+    }
+  ],
+  "folder_name": ""
+}
\ No newline at end of file
diff --git 
a/integration-tests/transforms/metadata/unit-test/0084-string-to-timestamp-6306 
UNIT.json 
b/integration-tests/transforms/metadata/unit-test/0084-string-to-timestamp-6306 
UNIT.json
new file mode 100644
index 0000000000..970a0e90b1
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/unit-test/0084-string-to-timestamp-6306 
UNIT.json   
@@ -0,0 +1,48 @@
+{
+  "database_replacements": [],
+  "autoOpening": true,
+  "description": "",
+  "persist_filename": "",
+  "test_type": "UNIT_TEST",
+  "variableValues": [],
+  "basePath": "${HOP_UNIT_TESTS_FOLDER}",
+  "golden_data_sets": [
+    {
+      "field_mappings": [
+        {
+          "transform_field": "timestamp_string",
+          "data_set_field": "timestamp_string"
+        },
+        {
+          "transform_field": "nb_errors",
+          "data_set_field": "nb_errors"
+        },
+        {
+          "transform_field": "error_description",
+          "data_set_field": "error_description"
+        },
+        {
+          "transform_field": "error_field",
+          "data_set_field": "error_field"
+        },
+        {
+          "transform_field": "error_codes",
+          "data_set_field": "error_codes"
+        }
+      ],
+      "field_order": [
+        "timestamp_string",
+        "nb_errors",
+        "error_description",
+        "error_field",
+        "error_codes"
+      ],
+      "data_set_name": "golden-string-to-timestamp",
+      "transform_name": "error"
+    }
+  ],
+  "input_data_sets": [],
+  "name": "0084-string-to-timestamp-6306 UNIT",
+  "trans_test_tweaks": [],
+  "pipeline_filename": "./0084-string-to-timestamp-6306.hpl"
+}
\ No newline at end of file

Reply via email to