This is an automated email from the ASF dual-hosted git repository. luchunliang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/inlong.git
The following commit(s) were added to refs/heads/master by this push: new 7aa0247228 [INLONG-10902][SDK] Transform support HEX(numeric or string) function (#10904) 7aa0247228 is described below commit 7aa0247228a4439123e5096dc6ac669ec2167f3f Author: emptyOVO <118812562+empty...@users.noreply.github.com> AuthorDate: Sat Aug 31 18:32:45 2024 +0800 [INLONG-10902][SDK] Transform support HEX(numeric or string) function (#10904) * [INLONG-10902][SDK] Transform support HEX(numeric or string) function * fix: add functions to check data type * fix: test data comment * fix: add anotation * fix: code conflicts * fix: add isBigDecimal function to check whether it can be parsed to BigDecimal --------- Co-authored-by: AloysZhang <aloyszh...@apache.org> --- .../transform/process/function/HexFunction.java | 108 +++++++++++++++++++++ .../TestTransformArithmeticFunctionsProcessor.java | 29 ++++++ 2 files changed, 137 insertions(+) diff --git a/inlong-sdk/transform-sdk/src/main/java/org/apache/inlong/sdk/transform/process/function/HexFunction.java b/inlong-sdk/transform-sdk/src/main/java/org/apache/inlong/sdk/transform/process/function/HexFunction.java new file mode 100644 index 0000000000..c641209162 --- /dev/null +++ b/inlong-sdk/transform-sdk/src/main/java/org/apache/inlong/sdk/transform/process/function/HexFunction.java @@ -0,0 +1,108 @@ +/* + * 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. + */ + +package org.apache.inlong.sdk.transform.process.function; + +import org.apache.inlong.sdk.transform.decode.SourceData; +import org.apache.inlong.sdk.transform.process.Context; +import org.apache.inlong.sdk.transform.process.operator.OperatorTools; +import org.apache.inlong.sdk.transform.process.parser.ValueParser; + +import net.sf.jsqlparser.expression.Function; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Optional; +import java.util.regex.Pattern; + +/** + * HexFunction + * description: + * - If the input argument is a numeric value (such as an integer), the HEX function converts the value to the corresponding hexadecimal string. + * - If the input argument is a string, the HEX function converts each character in the string to its corresponding hexadecimal ASCII encoding and returns the hexadecimal representation of the entire string. + */ +@TransformFunction(names = {"hex"}) +class HexFunction implements ValueParser { + + private static final Pattern BIG_DECIMAL_PATTERN = Pattern.compile("^[-+]?\\d+(\\.\\d+)?([eE][-+]?\\d+)?$"); + + private ValueParser valueParser; + + public HexFunction(Function expr) { + valueParser = OperatorTools.buildParser(expr.getParameters().getExpressions().get(0)); + } + + @Override + public Object parse(SourceData sourceData, int rowIndex, Context context) { + Object valueObj = valueParser.parse(sourceData, rowIndex, context); + if (isBigDecimal(valueObj)) { + return hex(OperatorTools.parseBigDecimal(valueObj)).toUpperCase(); + } + return hex(OperatorTools.parseString(valueObj)).toUpperCase(); + } + + private boolean isBigDecimal(Object valueObj) { + if (valueObj instanceof BigDecimal) { + return true; + } + if (valueObj instanceof String) { + String str = (String) valueObj; + return BIG_DECIMAL_PATTERN.matcher(str).matches(); + } + return false; + } + + // Handle Integer type + private String hex(int number) { + return Integer.toHexString(number).toUpperCase(); + } + + // Handle long type + private String hex(long number) { + return Long.toHexString(number).toUpperCase(); + } + + // Handle String type + private String hex(String input) { + StringBuilder hexString = new StringBuilder(); + for (char c : input.toCharArray()) { + hexString.append(Integer.toHexString((int) c).toUpperCase()); + } + return hexString.toString(); + } + + // Handle BigDecimal type + private String hex(BigDecimal number) { + // keep the integer part + BigDecimal integerValue = number.setScale(0, RoundingMode.DOWN); + return tryConvert(integerValue, BigDecimal::intValueExact, this::hex) + // If it cannot convert to integer, try converting to long + .orElseGet(() -> tryConvert(integerValue, BigDecimal::longValueExact, this::hex) + .orElseThrow(() -> new IllegalArgumentException("Number out of range"))); + } + + // Common conversion and processing methods + private <T> Optional<String> tryConvert(BigDecimal number, java.util.function.Function<BigDecimal, T> converter, + java.util.function.Function<T, String> handler) { + try { + T value = converter.apply(number); + return Optional.ofNullable(handler.apply(value)); + } catch (ArithmeticException e) { + return Optional.empty(); + } + } +} diff --git a/inlong-sdk/transform-sdk/src/test/java/org/apache/inlong/sdk/transform/process/TestTransformArithmeticFunctionsProcessor.java b/inlong-sdk/transform-sdk/src/test/java/org/apache/inlong/sdk/transform/process/TestTransformArithmeticFunctionsProcessor.java index 1467636e65..87ce145df6 100644 --- a/inlong-sdk/transform-sdk/src/test/java/org/apache/inlong/sdk/transform/process/TestTransformArithmeticFunctionsProcessor.java +++ b/inlong-sdk/transform-sdk/src/test/java/org/apache/inlong/sdk/transform/process/TestTransformArithmeticFunctionsProcessor.java @@ -619,4 +619,33 @@ public class TestTransformArithmeticFunctionsProcessor { Assert.assertTrue(result >= 0.0 && result < 1.0); } + @Test + public void testHexFunction() throws Exception { + String transformSql1 = "select hex(numeric1) from source"; + TransformConfig config1 = new TransformConfig(transformSql1); + TransformProcessor<String, String> processor1 = TransformProcessor + .create(config1, SourceDecoderFactory.createCsvDecoder(csvSource), + SinkEncoderFactory.createKvEncoder(kvSink)); + // case: hex(1007) + List<String> output1 = processor1.transform("1007|4|6|8", new HashMap<>()); + Assert.assertEquals(1, output1.size()); + Assert.assertEquals(output1.get(0), "result=3EF"); + // case: hex(3.14) + List<String> output2 = processor1.transform("3.14|4|6|8", new HashMap<>()); + Assert.assertEquals(1, output2.size()); + Assert.assertEquals(output2.get(0), "result=3"); + // case: hex(3.141592653589793) + List<String> output3 = processor1.transform("3.141592653589793|4|6|8", new HashMap<>()); + Assert.assertEquals(1, output3.size()); + Assert.assertEquals(output3.get(0), "result=3"); + // case: hex(-9223372036854775808) + List<String> output4 = processor1.transform("-9223372036854775808|4|6|8", new HashMap<>()); + Assert.assertEquals(1, output4.size()); + Assert.assertEquals(output4.get(0), "result=8000000000000000"); + // case: hex(abc) + List<String> output5 = processor1.transform("abc|4|6|8", new HashMap<>()); + Assert.assertEquals(1, output5.size()); + Assert.assertEquals(output5.get(0), "result=616263"); + } + }