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