This is an automated email from the ASF dual-hosted git repository.

stoty pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/master by this push:
     new 622abbd488 PHOENIX-7424 Support toStringBinary/toBytesBinary 
conversion (#2020)
622abbd488 is described below

commit 622abbd488366ea63a0a819a7711259c662dc3da
Author: szucsvillo <[email protected]>
AuthorDate: Mon Nov 25 09:10:17 2024 +0100

    PHOENIX-7424 Support toStringBinary/toBytesBinary conversion (#2020)
---
 .../apache/phoenix/expression/ExpressionType.java  |   4 +-
 .../expression/function/DecodeBinaryFunction.java  |  44 +++++
 .../expression/function/DecodeFunction.java        |  13 +-
 .../expression/function/EncodeBinaryFunction.java  | 123 ++++++++++++
 .../phoenix/expression/function/EncodeFormat.java  |   3 +-
 .../phoenix/end2end/DecodeBinaryFunctionIT.java    | 179 ++++++++++++++++++
 .../apache/phoenix/end2end/DecodeFunctionIT.java   |  23 +++
 .../phoenix/end2end/EncodeBinaryFunctionIT.java    | 207 +++++++++++++++++++++
 8 files changed, 591 insertions(+), 5 deletions(-)

diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/ExpressionType.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/ExpressionType.java
index bc059d9642..1fb68d81ae 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/ExpressionType.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/ExpressionType.java
@@ -200,7 +200,9 @@ public enum ExpressionType {
     BsonConditionExpressionFunction(BsonConditionExpressionFunction.class),
     BsonUpdateExpressionFunction(BsonUpdateExpressionFunction.class),
     BsonValueFunction(BsonValueFunction.class),
-    PartitionIdFunction(PartitionIdFunction.class);
+    PartitionIdFunction(PartitionIdFunction.class),
+    DecodeBinaryFunction(DecodeBinaryFunction.class),
+    EncodeBinaryFunction(EncodeBinaryFunction.class);
 
     ExpressionType(Class<? extends Expression> clazz) {
         this.clazz = clazz;
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/DecodeBinaryFunction.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/DecodeBinaryFunction.java
new file mode 100644
index 0000000000..2e1469c7b6
--- /dev/null
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/DecodeBinaryFunction.java
@@ -0,0 +1,44 @@
+/*
+ * 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.phoenix.expression.function;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.parse.FunctionParseNode;
+import org.apache.phoenix.schema.types.PVarchar;
+
+/**
+ * Decodes binary data in various formats (Hex, Base64, HBase) by extending 
the DecodeFunction.
+ */
[email protected](name = DecodeBinaryFunction.NAME, args = {
+    @FunctionParseNode.Argument(allowedTypes = {PVarchar.class}),
+    @FunctionParseNode.Argument(enumeration = "EncodeFormat")})
+public class DecodeBinaryFunction extends DecodeFunction {
+
+    public static final String NAME = "DECODE_BINARY";
+
+    public DecodeBinaryFunction() {
+        super();
+    }
+
+    public DecodeBinaryFunction(List<Expression> children) throws SQLException 
{
+        super(children);
+    }
+
+}
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/DecodeFunction.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/DecodeFunction.java
index 7b6ef38246..f42c25050e 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/DecodeFunction.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/DecodeFunction.java
@@ -18,9 +18,11 @@
 package org.apache.phoenix.expression.function;
 
 import java.sql.SQLException;
+import java.util.Base64;
 import java.util.List;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.expression.Expression;
@@ -58,8 +60,7 @@ public class DecodeFunction extends ScalarFunction {
                        return true; // expression was evaluated, but evaluated 
to null
                }
 
-               PDataType type = expression.getDataType();
-               String stringToDecode = (String) type.toObject(ptr);
+               String stringToDecode = (String) 
PVarchar.INSTANCE.toObject(ptr);
 
                Expression encodingExpression = getEncodingExpression();
                if (!encodingExpression.evaluate(tuple, ptr)) {
@@ -71,7 +72,7 @@ public class DecodeFunction extends ScalarFunction {
                .setMessage("Missing bytes encoding").build().buildException());
                }
 
-               type = encodingExpression.getDataType();
+               PDataType type = encodingExpression.getDataType();
                String encoding = ((String) type.toObject(ptr)).toUpperCase();
 
                byte out[];
@@ -81,6 +82,12 @@ public class DecodeFunction extends ScalarFunction {
                        case HEX:
                                out = decodeHex(stringToDecode);
                                break;
+                       case BASE64:
+                               out = 
Base64.getDecoder().decode(stringToDecode);
+                               break;
+                       case HBASE:
+                               out = Bytes.toBytesBinary(stringToDecode);
+                               break;
                        default:
                                throw new IllegalDataException("Unsupported 
encoding \"" + encoding + "\"");
                }
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/EncodeBinaryFunction.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/EncodeBinaryFunction.java
new file mode 100644
index 0000000000..7db5b968df
--- /dev/null
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/EncodeBinaryFunction.java
@@ -0,0 +1,123 @@
+/*
+ * 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.phoenix.expression.function;
+
+import java.sql.SQLException;
+import java.util.Base64;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.parse.FunctionParseNode;
+import org.apache.phoenix.schema.IllegalDataException;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.schema.types.PVarchar;
+import org.apache.phoenix.schema.types.PVarbinary;
+
+import static org.apache.hadoop.hbase.util.Bytes.toHex;
+
+/**
+ * Encodes binary data into a string to various formats such as Hex, Base64, 
or HBase binary.
+ */
[email protected](name = EncodeBinaryFunction.NAME, args = {
+    @FunctionParseNode.Argument(allowedTypes = {PVarbinary.class}),
+    @FunctionParseNode.Argument(enumeration = "EncodeFormat")})
+public class EncodeBinaryFunction extends ScalarFunction {
+
+    public static final String NAME = "ENCODE_BINARY";
+
+    public EncodeBinaryFunction() {
+    }
+
+    public EncodeBinaryFunction(List<Expression> children) throws SQLException 
{
+        super(children);
+    }
+
+    @Override
+    public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+        Expression expression = getExpression();
+        if (!expression.evaluate(tuple, ptr)) {
+            return false;
+        }
+        if (ptr.getLength() == 0) {
+            return true;
+        }
+
+        byte[] bytesToEncode = ptr.copyBytes();
+        Expression encodingExpression = getEncodingExpression();
+
+        if (!encodingExpression.evaluate(tuple, ptr)) {
+            return false;
+        }
+
+        if (ptr.getLength() == 0) {
+            throw new IllegalDataException(getMissingEncodeFormatMsg());
+        }
+
+        PDataType type = encodingExpression.getDataType();
+        String encodingFormat = ((String) type.toObject(ptr)).toUpperCase();
+        EncodeFormat format = EncodeFormat.valueOf(encodingFormat);
+        String encodedString;
+
+        switch (format) {
+        case HEX:
+            encodedString = toHex(bytesToEncode);
+            break;
+        case BASE64:
+            encodedString = Base64.getEncoder().encodeToString(bytesToEncode);
+            break;
+        case HBASE:
+            encodedString = Bytes.toStringBinary(bytesToEncode);
+            break;
+        default:
+            throw new 
IllegalDataException(getUnsupportedEncodeFormatMsg(encodingFormat));
+        }
+
+        ptr.set(PVarchar.INSTANCE.toBytes(encodedString));
+        return true;
+    }
+
+    public static String getMissingEncodeFormatMsg() {
+        return "Missing Encode Format";
+    }
+
+    public static String getUnsupportedEncodeFormatMsg(String encodeFormat) {
+        return "Unsupported Encode Format : " + encodeFormat;
+    }
+
+    @Override
+    public PDataType getDataType() {
+        return PVarchar.INSTANCE;
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    private Expression getExpression() {
+        return children.get(0);
+    }
+
+    private Expression getEncodingExpression() {
+        return children.get(1);
+    }
+
+}
+
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/EncodeFormat.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/EncodeFormat.java
index 8130228baa..664f331efd 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/EncodeFormat.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/function/EncodeFormat.java
@@ -22,5 +22,6 @@ public enum EncodeFormat {
        HEX, //format for encoding HEX value to bytes
        BASE62, //format for encoding a base 10 long value to base 62 string
        BASE64, //format for encoding a base 10 long value to base 64 string
-       ASCII // Plain Text
+    ASCII, //Plain Text
+    HBASE //HBase-specific escaping format
 };
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DecodeBinaryFunctionIT.java
 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DecodeBinaryFunctionIT.java
new file mode 100644
index 0000000000..5f1067be7a
--- /dev/null
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DecodeBinaryFunctionIT.java
@@ -0,0 +1,179 @@
+/*
+ * 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.phoenix.end2end;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Arrays;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(ParallelStatsDisabledTest.class)
+public class DecodeBinaryFunctionIT extends ParallelStatsDisabledIT {
+
+    private static String testTable;
+    private static final String helloPhoenixString = "HelloPhoenix";
+    private static final byte[] helloPhoenixBytes = "HelloPhoenix".getBytes();
+    private static final String base64Chunk = "SGVsbG9QaG9lbml4";
+    private static final String hex48String = "48656c6c6f";
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        testTable = generateUniqueName();
+        String ddl = "CREATE TABLE " + testTable + " (id INTEGER PRIMARY KEY, 
data VARCHAR)";
+        conn.createStatement().execute(ddl);
+
+        PreparedStatement ps = conn.prepareStatement("UPSERT INTO " + 
testTable + " (id, data) VALUES (1, ?)");
+        ps.setString(1, helloPhoenixString);
+        ps.execute();
+        conn.commit();
+        conn.close();
+    }
+
+    @Test
+    public void testDecodeHBaseFromHexLiteral() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement().executeQuery(
+                "SELECT 
DECODE_BINARY('\\x48\\x65\\x6C\\x6C\\x6F\\x50\\x68\\x6F\\x65\\x6E\\x69\\x78', " 
+
+                        "'HBASE') FROM " + testTable);
+        assertTrue(rs.next());
+        byte[] actualBytes = rs.getBytes(1);
+        assertTrue(Arrays.equals(helloPhoenixBytes, actualBytes));
+    }
+
+    @Test
+    public void testNullAndEmptyStringDecoding() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement()
+                .executeQuery("SELECT DECODE_BINARY(data, 'HBASE') FROM " + 
testTable + " WHERE ID=-10");
+        assertFalse(rs.next());
+
+        PreparedStatement ps = conn.prepareStatement("UPSERT INTO " + 
testTable + " (id, data) VALUES (6, ?)");
+        ps.setString(1, StringUtils.EMPTY);
+        ps.execute();
+        conn.commit();
+
+        ResultSet rs2 = conn.createStatement()
+                .executeQuery("SELECT DECODE_BINARY(data, 'HEX') FROM " + 
testTable + " WHERE ID=6");
+        assertTrue(rs2.next());
+        assertEquals(null, rs2.getString(1));
+    }
+
+    @Test
+    public void testLongBase64Decoding() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        StringBuilder base64String = new StringBuilder();
+        for (int i = 0; i < 20; i++) {
+            base64String.append(base64Chunk);
+        }
+
+        PreparedStatement ps = conn.prepareStatement("UPSERT INTO " + 
testTable + " (id, data) VALUES (3, ?)");
+        ps.setString(1, base64String.toString());
+        ps.execute();
+        conn.commit();
+
+        ResultSet rs = conn.createStatement()
+                .executeQuery("SELECT DECODE_BINARY(data, 'BASE64') FROM " + 
testTable + " WHERE ID=3");
+        assertTrue(rs.next());
+
+        StringBuilder expectedString = new StringBuilder();
+        for (int i = 0; i < 20; i++) {
+            expectedString.append(helloPhoenixString);
+        }
+
+        byte[] actualBytes = rs.getBytes(1);
+        assertTrue(Arrays.equals(expectedString.toString().getBytes(), 
actualBytes));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testDecodeBase64WithPadding() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        PreparedStatement ps = conn.prepareStatement("UPSERT INTO " + 
testTable + " (id, data) VALUES (7, ?)");
+        ps.setString(1, "SQ==");
+        ps.execute();
+        conn.commit();
+
+        ResultSet rs = conn.createStatement()
+                .executeQuery("SELECT DECODE_BINARY(data, 'BASE64') FROM " + 
testTable + " WHERE ID=7");
+        assertTrue(rs.next());
+
+        byte[] actualBytes = rs.getBytes(1);
+        assertTrue(Arrays.equals("I".getBytes(), actualBytes));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testDecodeEncodeRoundHex() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        PreparedStatement ps = conn.prepareStatement("UPSERT INTO " + 
testTable + " (id, data) VALUES (4, ?)");
+        ps.setString(1, hex48String);
+        ps.execute();
+        conn.commit();
+
+        ResultSet rs = conn.createStatement().executeQuery(
+                "SELECT ENCODE_BINARY(DECODE_BINARY(data, 'HEX'), 'HEX') FROM 
" + testTable + " WHERE ID=4");
+        assertTrue(rs.next());
+        String actualString = rs.getString(1);
+        assertEquals(hex48String, actualString);
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testDecodeEncodeRoundBase64() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        PreparedStatement ps = conn.prepareStatement("UPSERT INTO " + 
testTable + " (id, data) VALUES (5, ?)");
+        ps.setString(1, base64Chunk);
+        ps.execute();
+        conn.commit();
+
+        ResultSet rs = conn.createStatement().executeQuery(
+                "SELECT ENCODE_BINARY(DECODE_BINARY(data, 'BASE64'), 'BASE64') 
FROM " + testTable + " WHERE ID=5");
+        assertTrue(rs.next());
+        String actualString = rs.getString(1);
+        assertEquals(base64Chunk, actualString);
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testDecodeEncodeRoundHbase() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement().executeQuery(
+                "SELECT ENCODE_BINARY(DECODE_BINARY(data, 'HBASE'), 'HBASE') 
FROM " + testTable + " WHERE ID=1");
+        assertTrue(rs.next());
+        String actualString = rs.getString(1);
+        assertEquals(helloPhoenixString, actualString);
+        assertFalse(rs.next());
+    }
+}
\ No newline at end of file
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DecodeFunctionIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DecodeFunctionIT.java
index c3cbe0ddae..f4e949958c 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DecodeFunctionIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DecodeFunctionIT.java
@@ -27,6 +27,7 @@ import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.Arrays;
 
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.exception.SQLExceptionCode;
@@ -151,4 +152,26 @@ public class DecodeFunctionIT extends 
ParallelStatsDisabledIT {
             assertEquals(SQLExceptionCode.TYPE_MISMATCH.getErrorCode(), 
e.getErrorCode());
         }
        }
+
+    @Test
+    public void testDecodeBase642() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        String testTable = generateUniqueName();
+        String ddl = "CREATE TABLE " + testTable + " (id INTEGER PRIMARY KEY, 
data VARCHAR)";
+
+        conn.createStatement().execute(ddl);
+
+        PreparedStatement ps =
+                conn.prepareStatement("UPSERT INTO " + testTable + " (id, 
data) VALUES (1, 'SGVsbG9QaG9lbml4')");
+        ps.execute();
+        conn.commit();
+
+        ResultSet rs = conn.createStatement().executeQuery("SELECT 
DECODE(data, 'BASE64') FROM " + testTable);
+        assertTrue(rs.next());
+        byte[] actualBytes = rs.getBytes(1);
+        assertTrue(Arrays.equals("HelloPhoenix".getBytes(), actualBytes));
+        assertFalse(rs.next());
+       }
+
 }
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/EncodeBinaryFunctionIT.java
 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/EncodeBinaryFunctionIT.java
new file mode 100644
index 0000000000..010ec12635
--- /dev/null
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/EncodeBinaryFunctionIT.java
@@ -0,0 +1,207 @@
+/*
+ * 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.phoenix.end2end;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+
+@Category(ParallelStatsDisabledTest.class)
+public class EncodeBinaryFunctionIT extends ParallelStatsDisabledIT {
+
+    private static String testTable;
+    private static final byte[] originalBytes = "HelloPhoenix".getBytes();
+    private static final String encoded48String = "48656c6c6f50686f656e6978";
+    private static final String helloPhoenixString = "HelloPhoenix";
+    private static final String expectedBase64Chunk = "SGVsbG9QaG9lbml4";
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        testTable = generateUniqueName();
+        String ddl = "CREATE TABLE " + testTable + " (id INTEGER PRIMARY KEY, 
data VARBINARY)";
+        conn.createStatement().execute(ddl);
+
+        PreparedStatement ps = conn.prepareStatement("UPSERT INTO " + 
testTable + " (id, data) VALUES (1, ?)");
+        ps.setBytes(1, originalBytes);
+        ps.execute();
+
+        PreparedStatement ps2 = conn.prepareStatement(
+                "UPSERT INTO " + testTable + " (id, data) VALUES (2, 
X'48656c6c6f50686f656e6978')");
+        ps2.execute();
+        conn.commit();
+        conn.close();
+    }
+
+    @Test
+    public void testEncodeHex() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement()
+                .executeQuery("SELECT ENCODE_BINARY(data, 'HEX') FROM " + 
testTable + " WHERE ID=2");
+        assertTrue(rs.next());
+        String actualString = rs.getString(1);
+        assertEquals(encoded48String, actualString);
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testMixedCaseHexDecoding() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement()
+                .executeQuery("SELECT ENCODE_BINARY(data, 'HEX') FROM " + 
testTable + " WHERE ID=2");
+        assertTrue(rs.next());
+        String actualString = rs.getString(1);
+        assertEquals(encoded48String, actualString);
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testEncodeBase64() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement()
+                .executeQuery("SELECT ENCODE_BINARY(data, 'BASE64') FROM " + 
testTable + " WHERE ID=1");
+        assertTrue(rs.next());
+        String actualString = rs.getString(1);
+        assertEquals(expectedBase64Chunk, actualString);
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testLongBase64Decoding() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        StringBuilder base64String = new StringBuilder();
+        for (int i = 0; i < 20; i++) {
+            base64String.append(helloPhoenixString);
+        }
+
+        PreparedStatement ps = conn.prepareStatement("UPSERT INTO " + 
testTable + " (id, data) VALUES (3, ?)");
+        ps.setBytes(1, base64String.toString().getBytes());
+        ps.execute();
+        conn.commit();
+
+        ResultSet rs = conn.createStatement()
+                .executeQuery("SELECT ENCODE_BINARY(data, 'BASE64') FROM " + 
testTable + " WHERE ID=3");
+        assertTrue(rs.next());
+
+        StringBuilder expectedString = new StringBuilder();
+        for (int i = 0; i < 20; i++) {
+            expectedString.append(expectedBase64Chunk);
+        }
+
+        String actualString = rs.getString(1);
+        assertEquals(expectedString.toString(), actualString);
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testEncodeHBase() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement()
+                .executeQuery("SELECT ENCODE_BINARY(data, 'HBASE') FROM " + 
testTable + " WHERE ID=1");
+        assertTrue(rs.next());
+        String actualString = rs.getString(1);
+        assertEquals(helloPhoenixString, actualString);
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testInvalidDecodingFormat() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        try {
+            conn.createStatement().executeQuery("SELECT ENCODE_BINARY(data, 
'INVALIDFORMAT') FROM " + testTable);
+            fail("Expected an exception for invalid encoding format");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.TYPE_MISMATCH.getErrorCode(), 
e.getErrorCode());
+        }
+    }
+
+    @Test
+    public void testNullAndEmptyStringDecoding() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement()
+                .executeQuery("SELECT ENCODE_BINARY(data, 'HEX') FROM " + 
testTable + " WHERE ID=-10");
+        assertFalse(rs.next());
+
+        PreparedStatement ps = conn.prepareStatement("UPSERT INTO " + 
testTable + " (id, data) VALUES (4, ?)");
+        ps.setBytes(1, new byte[0]);
+        ps.execute();
+        conn.commit();
+
+        ResultSet rs2 = conn.createStatement()
+                .executeQuery("SELECT ENCODE_BINARY(data, 'HEX') FROM " + 
testTable + " WHERE ID=4");
+        assertTrue(rs2.next());
+        String actualString = rs2.getString(1);
+        assertEquals(null, actualString);
+        assertFalse(rs2.next());
+    }
+
+    @Test
+    public void testEncodeDecodeRoundHex() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement().executeQuery(
+                "SELECT DECODE_BINARY(ENCODE_BINARY(data, 'HEX'), 'HEX') FROM 
" + testTable + " WHERE ID=1");
+        assertTrue(rs.next());
+        byte[] roundTripEncoded = rs.getBytes(1);
+        assertTrue(Arrays.equals(originalBytes, roundTripEncoded));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testEncodeDecodeRoundBase64() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement().executeQuery(
+                "SELECT DECODE_BINARY(ENCODE_BINARY(data, 'BASE64'), 'BASE64') 
FROM " + testTable + " WHERE ID=1");
+        assertTrue(rs.next());
+        byte[] roundTripEncoded = rs.getBytes(1);
+        assertTrue(Arrays.equals(originalBytes, roundTripEncoded));
+    }
+
+    @Test
+    public void testEncodeDecodeRoundHbase() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        ResultSet rs = conn.createStatement().executeQuery(
+                "SELECT DECODE_BINARY(ENCODE_BINARY(data, 'HBASE'), 'HBASE') 
FROM " + testTable + " WHERE ID=1");
+        assertTrue(rs.next());
+        byte[] roundTripEncoded = rs.getBytes(1);
+        assertTrue(Arrays.equals(originalBytes, roundTripEncoded));
+        assertFalse(rs.next());
+    }
+}
\ No newline at end of file

Reply via email to