You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ja...@apache.org on 2025/07/20 08:57:04 UTC
(iotdb) branch ty/to_base64 created (now 5d0063568c5)
This is an automated email from the ASF dual-hosted git repository.
jackietien pushed a change to branch ty/to_base64
in repository https://gitbox.apache.org/repos/asf/iotdb.git
at 5d0063568c5 Add built-in scalar function to_base64 for the IoTDB table model, supporting STRING, TEXT, and BLOB types, with full integration, unit tests, and integration tests.
This branch includes the following new commits:
new 5d0063568c5 Add built-in scalar function to_base64 for the IoTDB table model, supporting STRING, TEXT, and BLOB types, with full integration, unit tests, and integration tests.
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(iotdb) 01/01: Add built-in scalar function to_base64 for the IoTDB table model, supporting STRING, TEXT, and BLOB types, with full integration, unit tests, and integration tests.
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jackietien pushed a commit to branch ty/to_base64
in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 5d0063568c5e820a6a1c169073821c01258830c8
Author: JackieTien97 <ja...@gmail.com>
AuthorDate: Sun Jul 20 16:56:21 2025 +0800
Add built-in scalar function to_base64 for the IoTDB table model, supporting STRING, TEXT, and BLOB types, with full integration, unit tests, and integration tests.
---
.../scalar/IoTDBToBase64FunctionTableIT.java | 130 +++++++++
.../relational/ColumnTransformerBuilder.java | 8 +
.../relational/metadata/TableMetadataImpl.java | 11 +
.../unary/scalar/ToBase64ColumnTransformer.java | 58 ++++
.../scalar/ToBase64ColumnTransformerTest.java | 321 +++++++++++++++++++++
.../relational/TableBuiltinScalarFunction.java | 1 +
6 files changed, 529 insertions(+)
diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/builtinfunction/scalar/IoTDBToBase64FunctionTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/builtinfunction/scalar/IoTDBToBase64FunctionTableIT.java
new file mode 100644
index 00000000000..6a70aeec2e2
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/builtinfunction/scalar/IoTDBToBase64FunctionTableIT.java
@@ -0,0 +1,130 @@
+/*
+ * 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.iotdb.relational.it.query.old.builtinfunction.scalar;
+
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.TableClusterIT;
+import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
+import org.apache.iotdb.itbase.env.BaseEnv;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.nio.charset.StandardCharsets;
+import java.sql.Connection;
+import java.sql.Statement;
+import java.util.Base64;
+
+import static org.apache.iotdb.db.it.utils.TestUtils.tableAssertTestFail;
+import static org.apache.iotdb.db.it.utils.TestUtils.tableResultSetEqualTest;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({TableLocalStandaloneIT.class, TableClusterIT.class})
+public class IoTDBToBase64FunctionTableIT {
+
+ private static final String DATABASE_NAME = "db_base64";
+
+ private static final String[] SQLs =
+ new String[] {
+ "CREATE DATABASE " + DATABASE_NAME,
+ "USE " + DATABASE_NAME,
+ "CREATE TABLE t1(device_id STRING TAG, s1 STRING FIELD, s2 TEXT FIELD, s3 BLOB FIELD, s4 STRING FIELD)",
+ "INSERT INTO t1(time, device_id, s1, s2, s3, s4) VALUES (1, 'd1', 'iotdb', 'iotdb', X'0102030405', null)",
+ "INSERT INTO t1(time, device_id, s1, s2, s3, s4) VALUES (2, 'd2', '', '', X'', null)",
+ // Setup for unsupported types
+ "CREATE TABLE t2(device_id STRING TAG, i INT32 FIELD, b BOOLEAN FIELD, f FLOAT FIELD, d DOUBLE FIELD, dt DATE FIELD, ts TIMESTAMP FIELD)",
+ "INSERT INTO t2(time, device_id, i, b, f, d, dt, ts) VALUES (1, 'd1', 1, true, 1.1, 2.2, '2024-01-01', 123456789)"
+ };
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ EnvFactory.getEnv().getConfig().getCommonConfig().setTimestampPrecision("ns");
+ EnvFactory.getEnv().initClusterEnvironment();
+ insertData();
+ }
+
+ protected static void insertData() {
+ try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ Statement statement = connection.createStatement()) {
+ for (String sql : SQLs) {
+ statement.execute(sql);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ EnvFactory.getEnv().cleanClusterEnvironment();
+ }
+
+ @Test
+ public void testToBase64() {
+ String expectedStr =
+ Base64.getEncoder().encodeToString("iotdb".getBytes(StandardCharsets.UTF_8));
+ String expectedBlob = Base64.getEncoder().encodeToString(new byte[] {1, 2, 3, 4, 5});
+ String expectedEmpty = "";
+ tableResultSetEqualTest(
+ "SELECT to_base64(s1), to_base64(s2), to_base64(s3), to_base64(s4) FROM t1 WHERE time = 1",
+ new String[] {"_col0", "_col1", "_col2", "_col3"},
+ new String[] {expectedStr + "," + expectedStr + "," + expectedBlob + ",null,"},
+ DATABASE_NAME);
+
+ tableResultSetEqualTest(
+ "SELECT to_base64(s1), to_base64(s2), to_base64(s3), to_base64(s4) FROM t1 WHERE time = 2",
+ new String[] {"_col0", "_col1", "_col2", "_col3"},
+ new String[] {expectedEmpty + "," + expectedEmpty + "," + expectedEmpty + ",null,"},
+ DATABASE_NAME);
+ }
+
+ @Test
+ public void testToBase64UnsupportedTypes() {
+ // Assert that to_base64 on unsupported types throws an error
+ tableAssertTestFail(
+ "SELECT to_base64(i) FROM t2",
+ "701: Scalar function to_base64 only accepts one argument and it must be STRING, TEXT or BLOB data type.",
+ DATABASE_NAME);
+ tableAssertTestFail(
+ "SELECT to_base64(b) FROM t2",
+ "701: Scalar function to_base64 only accepts one argument and it must be STRING, TEXT or BLOB data type.",
+ DATABASE_NAME);
+ tableAssertTestFail(
+ "SELECT to_base64(f) FROM t2",
+ "701: Scalar function to_base64 only accepts one argument and it must be STRING, TEXT or BLOB data type.",
+ DATABASE_NAME);
+ tableAssertTestFail(
+ "SELECT to_base64(d) FROM t2",
+ "701: Scalar function to_base64 only accepts one argument and it must be STRING, TEXT or BLOB data type.",
+ DATABASE_NAME);
+ tableAssertTestFail(
+ "SELECT to_base64(dt) FROM t2",
+ "701: Scalar function to_base64 only accepts one argument and it must be STRING, TEXT or BLOB data type.",
+ DATABASE_NAME);
+ tableAssertTestFail(
+ "SELECT to_base64(ts) FROM t2",
+ "701: Scalar function to_base64 only accepts one argument and it must be STRING, TEXT or BLOB data type.",
+ DATABASE_NAME);
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java
index 0e915ab258c..bdbfb2800ca 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java
@@ -1022,6 +1022,14 @@ public class ColumnTransformerBuilder
Type returnType = columnTransformers.get(0).getType();
return AbstractGreatestLeastColumnTransformer.getLeastColumnTransformer(
returnType, columnTransformers);
+ } else if (TableBuiltinScalarFunction.TO_BASE64
+ .getFunctionName()
+ .equalsIgnoreCase(functionName)) {
+ ColumnTransformer first = this.process(children.get(0), context);
+ if (children.size() == 1) {
+ return new org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar
+ .ToBase64ColumnTransformer(org.apache.tsfile.read.common.type.StringType.STRING, first);
+ }
} else {
// user defined function
if (TableUDFUtils.isScalarFunction(functionName)) {
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
index 8934de172e9..21cfe04892a 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
@@ -570,6 +570,17 @@ public class TableMetadataImpl implements Metadata {
+ " must have at least two arguments, and all type must be the same.");
}
return argumentTypes.get(0);
+ } else if (TableBuiltinScalarFunction.TO_BASE64
+ .getFunctionName()
+ .equalsIgnoreCase(functionName)) {
+ if (!(argumentTypes.size() == 1
+ && (isCharType(argumentTypes.get(0)) || isBlobType(argumentTypes.get(0))))) {
+ throw new SemanticException(
+ "Scalar function "
+ + functionName.toLowerCase(Locale.ENGLISH)
+ + " only accepts one argument and it must be STRING, TEXT or BLOB data type.");
+ }
+ return STRING;
}
// builtin aggregation function
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/ToBase64ColumnTransformer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/ToBase64ColumnTransformer.java
new file mode 100644
index 00000000000..2bc2c3c8ab1
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/ToBase64ColumnTransformer.java
@@ -0,0 +1,58 @@
+/*
+ * 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.iotdb.db.queryengine.transformation.dag.column.unary.scalar;
+
+import org.apache.iotdb.db.queryengine.transformation.dag.column.unary.UnaryColumnTransformer;
+
+import org.apache.tsfile.block.column.Column;
+import org.apache.tsfile.block.column.ColumnBuilder;
+import org.apache.tsfile.utils.Binary;
+
+import java.util.Base64;
+
+public class ToBase64ColumnTransformer extends UnaryColumnTransformer {
+
+ public ToBase64ColumnTransformer(
+ org.apache.tsfile.read.common.type.Type returnType,
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer
+ childColumnTransformer) {
+ super(returnType, childColumnTransformer);
+ }
+
+ @Override
+ protected void doTransform(Column column, ColumnBuilder columnBuilder) {
+ doTransform(column, columnBuilder, null);
+ }
+
+ @Override
+ protected void doTransform(Column column, ColumnBuilder columnBuilder, boolean[] selection) {
+ int positionCount = column.getPositionCount();
+ for (int i = 0; i < positionCount; i++) {
+ if ((selection != null && !selection[i]) || column.isNull(i)) {
+ columnBuilder.appendNull();
+ continue;
+ }
+ Binary value = column.getBinary(i);
+ byte[] bytes = value.getValues();
+ byte[] encoded = Base64.getEncoder().encode(bytes);
+ columnBuilder.writeBinary(new Binary(encoded));
+ }
+ }
+}
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/ToBase64ColumnTransformerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/ToBase64ColumnTransformerTest.java
new file mode 100644
index 00000000000..3cf6ff88f1d
--- /dev/null
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/ToBase64ColumnTransformerTest.java
@@ -0,0 +1,321 @@
+/*
+ * 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.iotdb.db.queryengine.transformation.dag.column.unary.scalar;
+
+import org.apache.tsfile.block.column.Column;
+import org.apache.tsfile.read.common.block.column.BinaryColumn;
+import org.apache.tsfile.read.common.type.StringType;
+import org.apache.tsfile.utils.Binary;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Optional;
+
+public class ToBase64ColumnTransformerTest {
+
+ private Column mockColumn(Binary[] values, boolean[] valueIsNull) {
+ return new BinaryColumn(
+ values.length, valueIsNull == null ? Optional.empty() : Optional.of(valueIsNull), values);
+ }
+
+ private org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer
+ mockChildTransformer(Column column) {
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer mock =
+ Mockito.mock(
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer.class);
+ Mockito.when(mock.getColumn()).thenReturn(column);
+ Mockito.doNothing().when(mock).tryEvaluate();
+ Mockito.doNothing().when(mock).evaluateWithSelection(Mockito.any());
+ Mockito.doNothing().when(mock).clearCache();
+ return mock;
+ }
+
+ @Test
+ public void testStringType() {
+ String input = "iotdb";
+ Binary[] values = new Binary[] {new Binary(input, StandardCharsets.UTF_8)};
+ Column column = mockColumn(values, null);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+ transformer.evaluate();
+ Column result = transformer.getColumn();
+ String expected = Base64.getEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_8));
+ Assert.assertEquals(expected, result.getBinary(0).toString());
+ }
+
+ @Test
+ public void testStringTypeNull() {
+ boolean[] valueIsNull = new boolean[] {true};
+ Binary[] values = new Binary[] {null};
+ Column column = mockColumn(values, valueIsNull);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+ transformer.evaluate();
+ Column result = transformer.getColumn();
+ Assert.assertTrue(result.isNull(0));
+ }
+
+ @Test
+ public void testBlobType() {
+ byte[] input = new byte[] {1, 2, 3, 4, 5};
+ Binary[] values = new Binary[] {new Binary(input)};
+ Column column = mockColumn(values, null);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+ transformer.evaluate();
+ Column result = transformer.getColumn();
+ String expected = Base64.getEncoder().encodeToString(input);
+ Assert.assertEquals(expected, result.getBinary(0).toString());
+ }
+
+ @Test
+ public void testBlobTypeNull() {
+ boolean[] valueIsNull = new boolean[] {true};
+ Binary[] values = new Binary[] {null};
+ Column column = mockColumn(values, valueIsNull);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+ transformer.evaluate();
+ Column result = transformer.getColumn();
+ Assert.assertTrue(result.isNull(0));
+ }
+
+ @Test
+ public void testTextType() {
+ String input = "iotdb";
+ Binary[] values = new Binary[] {new Binary(input, StandardCharsets.UTF_8)};
+ Column column = mockColumn(values, null);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+ transformer.evaluate();
+ Column result = transformer.getColumn();
+ String expected = Base64.getEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_8));
+ Assert.assertEquals(expected, result.getBinary(0).toString());
+ }
+
+ @Test
+ public void testTextTypeNull() {
+ boolean[] valueIsNull = new boolean[] {true};
+ Binary[] values = new Binary[] {null};
+ Column column = mockColumn(values, valueIsNull);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+ transformer.evaluate();
+ Column result = transformer.getColumn();
+ Assert.assertTrue(result.isNull(0));
+ }
+
+ @Test
+ public void testStringTypeMultiRowsWithNull() {
+ String input1 = "iotdb";
+ String input2 = "iotdb2";
+ boolean[] valueIsNull = new boolean[] {false, true, false};
+ Binary[] values =
+ new Binary[] {
+ new Binary(input1, StandardCharsets.UTF_8),
+ null,
+ new Binary(input2, StandardCharsets.UTF_8)
+ };
+ Column column = mockColumn(values, valueIsNull);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+ transformer.evaluate();
+ Column result = transformer.getColumn();
+ String expected1 = Base64.getEncoder().encodeToString(input1.getBytes(StandardCharsets.UTF_8));
+ String expected2 = Base64.getEncoder().encodeToString(input2.getBytes(StandardCharsets.UTF_8));
+ Assert.assertEquals(expected1, result.getBinary(0).toString());
+ Assert.assertTrue(result.isNull(1));
+ Assert.assertEquals(expected2, result.getBinary(2).toString());
+ }
+
+ @Test
+ public void testBlobTypeMultiRowsWithNull() {
+ byte[] input1 = new byte[] {1, 2, 3};
+ byte[] input2 = new byte[] {4, 5, 6};
+ boolean[] valueIsNull = new boolean[] {false, true, false};
+ Binary[] values = new Binary[] {new Binary(input1), null, new Binary(input2)};
+ Column column = mockColumn(values, valueIsNull);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+ transformer.evaluate();
+ Column result = transformer.getColumn();
+ String expected1 = Base64.getEncoder().encodeToString(input1);
+ String expected2 = Base64.getEncoder().encodeToString(input2);
+ Assert.assertEquals(expected1, result.getBinary(0).toString());
+ Assert.assertTrue(result.isNull(1));
+ Assert.assertEquals(expected2, result.getBinary(2).toString());
+ }
+
+ @Test
+ public void testTextTypeMultiRowsWithNull() {
+ String input1 = "iotdb";
+ String input2 = "iotdb2";
+ boolean[] valueIsNull = new boolean[] {false, true, false};
+ Binary[] values =
+ new Binary[] {
+ new Binary(input1, StandardCharsets.UTF_8),
+ null,
+ new Binary(input2, StandardCharsets.UTF_8)
+ };
+ Column column = mockColumn(values, valueIsNull);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+ transformer.evaluate();
+ Column result = transformer.getColumn();
+ String expected1 = Base64.getEncoder().encodeToString(input1.getBytes(StandardCharsets.UTF_8));
+ String expected2 = Base64.getEncoder().encodeToString(input2.getBytes(StandardCharsets.UTF_8));
+ Assert.assertEquals(expected1, result.getBinary(0).toString());
+ Assert.assertTrue(result.isNull(1));
+ Assert.assertEquals(expected2, result.getBinary(2).toString());
+ }
+
+ @Test
+ public void testEvaluateWithSelection() {
+ String input1 = "iotdb";
+ String input2 = "iotdb2";
+ boolean[] valueIsNull = new boolean[] {false, false, false};
+ Binary[] values =
+ new Binary[] {
+ new Binary(input1, StandardCharsets.UTF_8),
+ new Binary(input2, StandardCharsets.UTF_8),
+ new Binary(input1, StandardCharsets.UTF_8)
+ };
+ Column column = mockColumn(values, valueIsNull);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+
+ // Only select the first and third row
+ boolean[] selection = new boolean[] {true, false, true};
+ transformer.evaluateWithSelection(selection);
+ Column result = transformer.getColumn();
+
+ String expected1 = Base64.getEncoder().encodeToString(input1.getBytes(StandardCharsets.UTF_8));
+ String expected2 = Base64.getEncoder().encodeToString(input2.getBytes(StandardCharsets.UTF_8));
+ // Row 0: selected, should be encoded
+ Assert.assertEquals(expected1, result.getBinary(0).toString());
+ // Row 1: not selected, should be null
+ Assert.assertTrue(result.isNull(1));
+ // Row 2: selected, should be encoded
+ Assert.assertEquals(expected1, result.getBinary(2).toString());
+ }
+
+ @Test
+ public void testStringTypeEvaluateWithSelection() {
+ String input1 = "iotdb";
+ String input2 = "iotdb2";
+ boolean[] valueIsNull = new boolean[] {false, false, false};
+ Binary[] values =
+ new Binary[] {
+ new Binary(input1, StandardCharsets.UTF_8),
+ new Binary(input2, StandardCharsets.UTF_8),
+ new Binary(input1, StandardCharsets.UTF_8)
+ };
+ Column column = mockColumn(values, valueIsNull);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+
+ // Only select the first and third row
+ boolean[] selection = new boolean[] {true, false, true};
+ transformer.evaluateWithSelection(selection);
+ Column result = transformer.getColumn();
+
+ String expected1 = Base64.getEncoder().encodeToString(input1.getBytes(StandardCharsets.UTF_8));
+ String expected2 = Base64.getEncoder().encodeToString(input2.getBytes(StandardCharsets.UTF_8));
+ Assert.assertEquals(expected1, result.getBinary(0).toString());
+ Assert.assertTrue(result.isNull(1));
+ Assert.assertEquals(expected1, result.getBinary(2).toString());
+ }
+
+ @Test
+ public void testBlobTypeEvaluateWithSelection() {
+ byte[] input1 = new byte[] {1, 2, 3};
+ byte[] input2 = new byte[] {4, 5, 6};
+ boolean[] valueIsNull = new boolean[] {false, false, false};
+ Binary[] values = new Binary[] {new Binary(input1), new Binary(input2), new Binary(input1)};
+ Column column = mockColumn(values, valueIsNull);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+
+ boolean[] selection = new boolean[] {true, false, true};
+ transformer.evaluateWithSelection(selection);
+ Column result = transformer.getColumn();
+
+ String expected1 = Base64.getEncoder().encodeToString(input1);
+ String expected2 = Base64.getEncoder().encodeToString(input2);
+ Assert.assertEquals(expected1, result.getBinary(0).toString());
+ Assert.assertTrue(result.isNull(1));
+ Assert.assertEquals(expected1, result.getBinary(2).toString());
+ }
+
+ @Test
+ public void testTextTypeEvaluateWithSelection() {
+ String input1 = "iotdb";
+ String input2 = "iotdb2";
+ boolean[] valueIsNull = new boolean[] {false, false, false};
+ Binary[] values =
+ new Binary[] {
+ new Binary(input1, StandardCharsets.UTF_8),
+ new Binary(input2, StandardCharsets.UTF_8),
+ new Binary(input1, StandardCharsets.UTF_8)
+ };
+ Column column = mockColumn(values, valueIsNull);
+ org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer child =
+ mockChildTransformer(column);
+ ToBase64ColumnTransformer transformer = new ToBase64ColumnTransformer(StringType.STRING, child);
+ transformer.addReferenceCount();
+
+ boolean[] selection = new boolean[] {true, false, true};
+ transformer.evaluateWithSelection(selection);
+ Column result = transformer.getColumn();
+
+ String expected1 = Base64.getEncoder().encodeToString(input1.getBytes(StandardCharsets.UTF_8));
+ String expected2 = Base64.getEncoder().encodeToString(input2.getBytes(StandardCharsets.UTF_8));
+ Assert.assertEquals(expected1, result.getBinary(0).toString());
+ Assert.assertTrue(result.isNull(1));
+ Assert.assertEquals(expected1, result.getBinary(2).toString());
+ }
+}
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/TableBuiltinScalarFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/TableBuiltinScalarFunction.java
index 6d5b1ef4721..985f7ab1b9e 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/TableBuiltinScalarFunction.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/TableBuiltinScalarFunction.java
@@ -67,6 +67,7 @@ public enum TableBuiltinScalarFunction {
FORMAT("format"),
GREATEST("greatest"),
LEAST("least"),
+ TO_BASE64("to_base64"),
;
private final String functionName;