This is an automated email from the ASF dual-hosted git repository.
siddteotia pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new 913f0d81a8 Add support for Base64 Encode/Decode Scalar Functions
(#9114)
913f0d81a8 is described below
commit 913f0d81a8998a2faedd4dc4a581bb53ea9e8a8e
Author: Yifan Zhao <[email protected]>
AuthorDate: Tue Aug 2 14:06:01 2022 -0700
Add support for Base64 Encode/Decode Scalar Functions (#9114)
* before testing on agg
* added null case tests for encode/decode, added invalid check for decode
* roll back style changes
* minor changes
* roll back style changes
* added base64 with user encoding scheme, added nested tranfunc for single
arg
* minor change
* tested binary to base64
* code reformat
* simplied logic, editted all tests, before reformat
* checked style
* fixed format issue
* fixed format issue
* added invalid arg test for toBase64
---
.../LiteralOnlyBrokerRequestTest.java | 92 ++++++++++
.../common/function/scalar/StringFunctions.java | 28 ++++
.../pinot/sql/parsers/CalciteSqlCompilerTest.java | 96 +++++++++++
.../ScalarTransformFunctionWrapperTest.java | 24 +++
.../tests/OfflineClusterIntegrationTest.java | 186 ++++++++++++++++++++-
5 files changed, 423 insertions(+), 3 deletions(-)
diff --git
a/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/LiteralOnlyBrokerRequestTest.java
b/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/LiteralOnlyBrokerRequestTest.java
index 0868213cb4..bd807818ce 100644
---
a/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/LiteralOnlyBrokerRequestTest.java
+++
b/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/LiteralOnlyBrokerRequestTest.java
@@ -20,12 +20,14 @@ package org.apache.pinot.broker.requesthandler;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.Collections;
+import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.apache.pinot.broker.broker.AccessControlFactory;
import org.apache.pinot.broker.broker.AllowAllAccessControlFactory;
import org.apache.pinot.common.metrics.BrokerMetrics;
import org.apache.pinot.common.response.broker.BrokerResponseNative;
+import org.apache.pinot.common.response.broker.ResultTable;
import org.apache.pinot.common.utils.DataSchema;
import org.apache.pinot.spi.env.PinotConfiguration;
import org.apache.pinot.spi.metrics.PinotMetricUtils;
@@ -104,6 +106,22 @@ public class LiteralOnlyBrokerRequestTest {
Assert.assertFalse(BaseBrokerRequestHandler.isLiteralOnlyQuery(CalciteSqlParser
.compileToPinotQuery("SELECT count(*) from foo "
+ "where bar =
decodeUrl('key1%3Dvalue+1%26key2%3Dvalue%40%21%242%26key3%3Dvalue%253')")));
+
Assert.assertTrue(BaseBrokerRequestHandler.isLiteralOnlyQuery(CalciteSqlParser.compileToPinotQuery(
+ "SELECT toUtf8('hello!')," + " fromUtf8(toUtf8('hello!')) FROM
myTable")));
+
Assert.assertFalse(BaseBrokerRequestHandler.isLiteralOnlyQuery(CalciteSqlParser.compileToPinotQuery(
+ "SELECT reverse(fromUtf8(foo))," + " toUtf8('hello!') FROM myTable")));
+
Assert.assertTrue(BaseBrokerRequestHandler.isLiteralOnlyQuery(CalciteSqlParser.compileToPinotQuery(
+ "SELECT toBase64(toUtf8('hello!'))," + " fromBase64('aGVsbG8h') FROM
myTable")));
+
Assert.assertFalse(BaseBrokerRequestHandler.isLiteralOnlyQuery(CalciteSqlParser.compileToPinotQuery(
+ "SELECT reverse(toBase64(foo))," + " toBase64(fromBase64('aGVsbG8h'))
FROM myTable")));
+ Assert.assertFalse(BaseBrokerRequestHandler.isLiteralOnlyQuery(
+ CalciteSqlParser.compileToPinotQuery("SELECT
fromBase64(toBase64(to_utf8(foo))) FROM myTable")));
+ Assert.assertFalse(BaseBrokerRequestHandler.isLiteralOnlyQuery(
+ CalciteSqlParser.compileToPinotQuery("SELECT count(*) from foo " +
"where bar = toBase64(toASCII('hello!'))")));
+ Assert.assertFalse(BaseBrokerRequestHandler.isLiteralOnlyQuery(
+ CalciteSqlParser.compileToPinotQuery("SELECT count(*) from foo " +
"where bar = fromBase64('aGVsbG8h')")));
+
Assert.assertFalse(BaseBrokerRequestHandler.isLiteralOnlyQuery(CalciteSqlParser.compileToPinotQuery(
+ "SELECT count(*) from foo " + "where bar =
fromUtf8(fromBase64('aGVsbG8h'))")));
}
@Test
@@ -115,6 +133,10 @@ public class LiteralOnlyBrokerRequestTest {
Assert.assertTrue(BaseBrokerRequestHandler.isLiteralOnlyQuery(CalciteSqlParser.compileToPinotQuery(
"SELECT encodeUrl('key1=value 1&key2=value@!$2&key3=value%3') AS
encoded, "
+
"decodeUrl('key1%3Dvalue+1%26key2%3Dvalue%40%21%242%26key3%3Dvalue%253') AS
decoded")));
+
Assert.assertTrue(BaseBrokerRequestHandler.isLiteralOnlyQuery(CalciteSqlParser.compileToPinotQuery(
+ "SELECT toUtf8('hello!') AS encoded, " + "fromUtf8(toUtf8('hello!'))
AS decoded")));
+
Assert.assertTrue(BaseBrokerRequestHandler.isLiteralOnlyQuery(CalciteSqlParser.compileToPinotQuery(
+ "SELECT toBase64(toUtf8('hello!')) AS encoded, " +
"fromBase64('aGVsbG8h') AS decoded")));
}
@Test
@@ -211,6 +233,76 @@ public class LiteralOnlyBrokerRequestTest {
Assert.assertEquals(brokerResponse.getResultTable().getRows().get(0)[1].toString(),
"key1=value 1&key2=value@!$2&key3=value%3");
Assert.assertEquals(brokerResponse.getTotalDocs(), 0);
+
+ request = JsonUtils.stringToJsonNode(
+ "{\"sql\":\"SELECT toBase64(toUtf8('hello!')) AS encoded, " +
"fromUtf8(fromBase64('aGVsbG8h')) AS decoded\"}");
+ requestStats = Tracing.getTracer().createRequestScope();
+ brokerResponse = requestHandler.handleRequest(request, null, requestStats);
+ ResultTable resultTable = brokerResponse.getResultTable();
+ DataSchema dataSchema = resultTable.getDataSchema();
+ List<Object[]> rows = resultTable.getRows();
+ Assert.assertEquals(dataSchema.getColumnName(0), "encoded");
+ Assert.assertEquals(dataSchema.getColumnDataType(0),
DataSchema.ColumnDataType.STRING);
+ Assert.assertEquals(dataSchema.getColumnName(1), "decoded");
+ Assert.assertEquals(dataSchema.getColumnDataType(1),
DataSchema.ColumnDataType.STRING);
+ Assert.assertEquals(rows.size(), 1);
+ Assert.assertEquals(rows.get(0).length, 2);
+ Assert.assertEquals(rows.get(0)[0].toString(), "aGVsbG8h");
+ Assert.assertEquals(rows.get(0)[1].toString(), "hello!");
+ Assert.assertEquals(brokerResponse.getTotalDocs(), 0);
+
+ request = JsonUtils.stringToJsonNode(
+ "{\"sql\":\"SELECT fromUtf8(fromBase64(toBase64(toUtf8('nested')))) AS
output\"}");
+ requestStats = Tracing.getTracer().createRequestScope();
+ brokerResponse = requestHandler.handleRequest(request, null, requestStats);
+ resultTable = brokerResponse.getResultTable();
+ dataSchema = resultTable.getDataSchema();
+ rows = resultTable.getRows();
+ Assert.assertEquals(dataSchema.getColumnName(0), "output");
+ Assert.assertEquals(dataSchema.getColumnDataType(0),
DataSchema.ColumnDataType.STRING);
+ Assert.assertEquals(rows.size(), 1);
+ Assert.assertEquals(rows.get(0).length, 1);
+ Assert.assertEquals(rows.get(0)[0].toString(), "nested");
+ Assert.assertEquals(brokerResponse.getTotalDocs(), 0);
+
+ request = JsonUtils.stringToJsonNode(
+ "{\"sql\":\"SELECT toBase64(toUtf8('this is a long string that will
encode to more than 76 characters using "
+ + "base64'))"
+ + " AS encoded\"}");
+ requestStats = Tracing.getTracer().createRequestScope();
+ brokerResponse = requestHandler.handleRequest(request, null, requestStats);
+ resultTable = brokerResponse.getResultTable();
+ dataSchema = resultTable.getDataSchema();
+ rows = resultTable.getRows();
+ Assert.assertEquals(dataSchema.getColumnName(0), "encoded");
+ Assert.assertEquals(dataSchema.getColumnDataType(0),
DataSchema.ColumnDataType.STRING);
+ Assert.assertEquals(rows.size(), 1);
+ Assert.assertEquals(rows.get(0).length, 1);
+ Assert.assertEquals(rows.get(0)[0].toString(),
+
"dGhpcyBpcyBhIGxvbmcgc3RyaW5nIHRoYXQgd2lsbCBlbmNvZGUgdG8gbW9yZSB0aGFuIDc2IGNoYXJhY3RlcnMgdXNpbmcgYmFzZTY0");
+ Assert.assertEquals(brokerResponse.getTotalDocs(), 0);
+
+ request = JsonUtils.stringToJsonNode("{\"sql\":\"SELECT
fromUtf8(fromBase64"
+ +
"('dGhpcyBpcyBhIGxvbmcgc3RyaW5nIHRoYXQgd2lsbCBlbmNvZGUgdG8gbW9yZSB0aGFuIDc2IGNoYXJhY3RlcnMgdXNpbmcgYmFzZTY0"
+ + "')) AS decoded\"}");
+ requestStats = Tracing.getTracer().createRequestScope();
+ brokerResponse = requestHandler.handleRequest(request, null, requestStats);
+ resultTable = brokerResponse.getResultTable();
+ dataSchema = resultTable.getDataSchema();
+ rows = resultTable.getRows();
+ Assert.assertEquals(dataSchema.getColumnName(0), "decoded");
+ Assert.assertEquals(dataSchema.getColumnDataType(0),
DataSchema.ColumnDataType.STRING);
+ Assert.assertEquals(rows.size(), 1);
+ Assert.assertEquals(rows.get(0).length, 1);
+ Assert.assertEquals(rows.get(0)[0].toString(),
+ "this is a long string that will encode to more than 76 characters
using base64");
+ Assert.assertEquals(brokerResponse.getTotalDocs(), 0);
+
+ request = JsonUtils.stringToJsonNode("{\"sql\":\"SELECT fromBase64" + "(0)
AS decoded\"}");
+ requestStats = Tracing.getTracer().createRequestScope();
+ brokerResponse = requestHandler.handleRequest(request, null, requestStats);
+ Assert.assertTrue(
+
brokerResponse.getProcessingExceptions().get(0).getMessage().contains("IllegalArgumentException"));
}
/** Tests for EXPLAIN PLAN for literal only queries. */
diff --git
a/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/StringFunctions.java
b/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/StringFunctions.java
index 7d1d7a7389..08b9c5ac22 100644
---
a/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/StringFunctions.java
+++
b/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/StringFunctions.java
@@ -23,6 +23,7 @@ import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.Normalizer;
+import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
@@ -393,6 +394,15 @@ public class StringFunctions {
return input.getBytes(StandardCharsets.UTF_8);
}
+ /**
+ * @param input bytes
+ * @return UTF8 encoded string
+ */
+ @ScalarFunction
+ public static String fromUtf8(byte[] input) {
+ return new String(input, StandardCharsets.UTF_8);
+ }
+
/**
* @see StandardCharsets#US_ASCII#encode(String)
* @param input
@@ -560,4 +570,22 @@ public class StringFunctions {
throws UnsupportedEncodingException {
return URLDecoder.decode(input, StandardCharsets.UTF_8.toString());
}
+
+ /**
+ * @param input binary data
+ * @return Base64 encoded String
+ */
+ @ScalarFunction
+ public static String toBase64(byte[] input) {
+ return Base64.getEncoder().encodeToString(input);
+ }
+
+ /**
+ * @param input Base64 encoded String
+ * @return decoded binary data
+ */
+ @ScalarFunction
+ public static byte[] fromBase64(String input) {
+ return Base64.getDecoder().decode(input);
+ }
}
diff --git
a/pinot-common/src/test/java/org/apache/pinot/sql/parsers/CalciteSqlCompilerTest.java
b/pinot-common/src/test/java/org/apache/pinot/sql/parsers/CalciteSqlCompilerTest.java
index cd88b02d8a..5801d14f0a 100644
---
a/pinot-common/src/test/java/org/apache/pinot/sql/parsers/CalciteSqlCompilerTest.java
+++
b/pinot-common/src/test/java/org/apache/pinot/sql/parsers/CalciteSqlCompilerTest.java
@@ -1890,6 +1890,68 @@ public class CalciteSqlCompilerTest {
decoded =
and.getOperands().get(1).getFunctionCall().getOperands().get(1).getLiteral().getStringValue();
Assert.assertEquals(encoded,
"key1%3Dvalue+1%26key2%3Dvalue%40%21%242%26key3%3Dvalue%253");
Assert.assertEquals(decoded, "key1=value 1&key2=value@!$2&key3=value%3");
+
+ query = "select toBase64(toUtf8('hello!')),
fromUtf8(fromBase64('aGVsbG8h')) from mytable";
+ pinotQuery = CalciteSqlParser.compileToPinotQuery(query);
+ String encodedBase64 =
pinotQuery.getSelectList().get(0).getLiteral().getStringValue();
+ String decodedBase64 =
pinotQuery.getSelectList().get(1).getLiteral().getStringValue();
+ Assert.assertEquals(encodedBase64, "aGVsbG8h");
+ Assert.assertEquals(decodedBase64, "hello!");
+
+ query = "select toBase64(fromBase64('aGVsbG8h')),
fromUtf8(fromBase64(toBase64(toUtf8('hello!')))) from mytable";
+ pinotQuery = CalciteSqlParser.compileToPinotQuery(query);
+ encodedBase64 =
pinotQuery.getSelectList().get(0).getLiteral().getStringValue();
+ decodedBase64 =
pinotQuery.getSelectList().get(1).getLiteral().getStringValue();
+ Assert.assertEquals(encodedBase64, "aGVsbG8h");
+ Assert.assertEquals(decodedBase64, "hello!");
+
+ query =
+ "select toBase64(toUtf8(upper('hello!'))),
fromUtf8(fromBase64(toBase64(toUtf8(upper('hello!'))))) from "
+ + "mytable";
+ pinotQuery = CalciteSqlParser.compileToPinotQuery(query);
+ encodedBase64 =
pinotQuery.getSelectList().get(0).getLiteral().getStringValue();
+ decodedBase64 =
pinotQuery.getSelectList().get(1).getLiteral().getStringValue();
+ Assert.assertEquals(encodedBase64, "SEVMTE8h");
+ Assert.assertEquals(decodedBase64, "HELLO!");
+
+ query =
+ "select
reverse(fromUtf8(fromBase64(toBase64(toUtf8(upper('hello!')))))) from mytable
where fromUtf8"
+ + "(fromBase64(toBase64(toUtf8(upper('hello!')))))"
+ + " = bar";
+ pinotQuery = CalciteSqlParser.compileToPinotQuery(query);
+ String arg1 =
pinotQuery.getSelectList().get(0).getLiteral().getStringValue();
+ String leftOp =
+
pinotQuery.getFilterExpression().getFunctionCall().getOperands().get(1).getLiteral().getStringValue();
+ Assert.assertEquals(arg1, "!OLLEH");
+ Assert.assertEquals(leftOp, "HELLO!");
+
+ query = "select a from mytable where foo = toBase64(toUtf8('hello!')) and
bar = fromUtf8(fromBase64('aGVsbG8h'))";
+ pinotQuery = CalciteSqlParser.compileToPinotQuery(query);
+ and = pinotQuery.getFilterExpression().getFunctionCall();
+ encoded =
and.getOperands().get(0).getFunctionCall().getOperands().get(1).getLiteral().getStringValue();
+ decoded =
and.getOperands().get(1).getFunctionCall().getOperands().get(1).getLiteral().getStringValue();
+ Assert.assertEquals(encoded, "aGVsbG8h");
+ Assert.assertEquals(decoded, "hello!");
+
+ query = "select fromBase64('hello') from mytable";
+ Exception expectedError = null;
+ try {
+ CalciteSqlParser.compileToPinotQuery(query);
+ } catch (Exception e) {
+ expectedError = e;
+ }
+ Assert.assertNotNull(expectedError);
+ Assert.assertTrue(expectedError instanceof SqlCompilationException);
+
+ query = "select toBase64('hello!') from mytable";
+ expectedError = null;
+ try {
+ CalciteSqlParser.compileToPinotQuery(query);
+ } catch (Exception e) {
+ expectedError = e;
+ }
+ Assert.assertNotNull(expectedError);
+ Assert.assertTrue(expectedError instanceof SqlCompilationException);
}
@Test
@@ -2012,6 +2074,40 @@ public class CalciteSqlCompilerTest {
Assert.assertNotNull(expression.getFunctionCall());
Assert.assertEquals(expression.getFunctionCall().getOperator(), "count");
Assert.assertEquals(expression.getFunctionCall().getOperands().get(0).getIdentifier().getName(),
"*");
+
+ expression =
CalciteSqlParser.compileToExpression("toBase64(toUtf8('hello!'))");
+ Assert.assertNotNull(expression.getFunctionCall());
+ pinotQuery.setFilterExpression(expression);
+ pinotQuery = compileTimeFunctionsInvoker.rewrite(pinotQuery);
+ expression = pinotQuery.getFilterExpression();
+ Assert.assertNotNull(expression.getLiteral());
+ Assert.assertEquals(expression.getLiteral().getFieldValue(), "aGVsbG8h");
+
+ expression =
CalciteSqlParser.compileToExpression("fromUtf8(fromBase64('aGVsbG8h'))");
+ Assert.assertNotNull(expression.getFunctionCall());
+ pinotQuery.setFilterExpression(expression);
+ pinotQuery = compileTimeFunctionsInvoker.rewrite(pinotQuery);
+ expression = pinotQuery.getFilterExpression();
+ Assert.assertNotNull(expression.getLiteral());
+ Assert.assertEquals(expression.getLiteral().getFieldValue(), "hello!");
+
+ expression = CalciteSqlParser.compileToExpression("fromBase64(foo)");
+ Assert.assertNotNull(expression.getFunctionCall());
+ pinotQuery.setFilterExpression(expression);
+ pinotQuery = compileTimeFunctionsInvoker.rewrite(pinotQuery);
+ expression = pinotQuery.getFilterExpression();
+ Assert.assertNotNull(expression.getFunctionCall());
+ Assert.assertEquals(expression.getFunctionCall().getOperator(),
"frombase64");
+
Assert.assertEquals(expression.getFunctionCall().getOperands().get(0).getIdentifier().getName(),
"foo");
+
+ expression = CalciteSqlParser.compileToExpression("toBase64(foo)");
+ Assert.assertNotNull(expression.getFunctionCall());
+ pinotQuery.setFilterExpression(expression);
+ pinotQuery = compileTimeFunctionsInvoker.rewrite(pinotQuery);
+ expression = pinotQuery.getFilterExpression();
+ Assert.assertNotNull(expression.getFunctionCall());
+ Assert.assertEquals(expression.getFunctionCall().getOperator(),
"tobase64");
+
Assert.assertEquals(expression.getFunctionCall().getOperands().get(0).getIdentifier().getName(),
"foo");
}
@Test
diff --git
a/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/ScalarTransformFunctionWrapperTest.java
b/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/ScalarTransformFunctionWrapperTest.java
index 69e4b045a7..5187a41ed0 100644
---
a/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/ScalarTransformFunctionWrapperTest.java
+++
b/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/ScalarTransformFunctionWrapperTest.java
@@ -23,6 +23,7 @@ import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
import java.nio.charset.StandardCharsets;
import java.text.Normalizer;
import java.util.Arrays;
+import java.util.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
@@ -819,4 +820,27 @@ public class ScalarTransformFunctionWrapperTest extends
BaseTransformFunctionTes
}
testTransformFunctionMV(transformFunction, expectedValues);
}
+
+ @Test
+ public void testBase64TransformFunction() {
+ ExpressionContext expression =
RequestContextUtils.getExpression(String.format("toBase64(%s)",
BYTES_SV_COLUMN));
+ TransformFunction transformFunction =
TransformFunctionFactory.get(expression, _dataSourceMap);
+ assertTrue(transformFunction instanceof ScalarTransformFunctionWrapper);
+ assertEquals(transformFunction.getName(), "toBase64");
+ String[] expectedValues = new String[NUM_ROWS];
+ for (int i = 0; i < NUM_ROWS; i++) {
+ expectedValues[i] =
Base64.getEncoder().encodeToString(_bytesSVValues[i]);
+ }
+ testTransformFunction(transformFunction, expectedValues);
+
+ expression =
RequestContextUtils.getExpression(String.format("fromBase64(toBase64(%s))",
BYTES_SV_COLUMN));
+ transformFunction = TransformFunctionFactory.get(expression,
_dataSourceMap);
+ assertTrue(transformFunction instanceof ScalarTransformFunctionWrapper);
+ assertEquals(transformFunction.getName(), "fromBase64");
+ byte[][] expectedBinaryValues = new byte[NUM_ROWS][];
+ for (int i = 0; i < NUM_ROWS; i++) {
+ expectedBinaryValues[i] =
Base64.getDecoder().decode(Base64.getEncoder().encodeToString(_bytesSVValues[i]));
+ }
+ testTransformFunction(transformFunction, expectedBinaryValues);
+ }
}
diff --git
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/OfflineClusterIntegrationTest.java
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/OfflineClusterIntegrationTest.java
index 555cafe49f..c95e3d91e5 100644
---
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/OfflineClusterIntegrationTest.java
+++
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/OfflineClusterIntegrationTest.java
@@ -84,8 +84,7 @@ import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
-import static
org.apache.pinot.common.function.scalar.StringFunctions.decodeUrl;
-import static
org.apache.pinot.common.function.scalar.StringFunctions.encodeUrl;
+import static org.apache.pinot.common.function.scalar.StringFunctions.*;
import static
org.apache.pinot.controller.helix.core.PinotHelixResourceManager.EXTERNAL_VIEW_CHECK_INTERVAL_MS;
import static
org.apache.pinot.controller.helix.core.PinotHelixResourceManager.EXTERNAL_VIEW_ONLINE_SEGMENTS_MAX_WAIT_MS;
import static org.testng.Assert.*;
@@ -606,6 +605,178 @@ public class OfflineClusterIntegrationTest extends
BaseClusterIntegrationTestSet
assertEquals(decodedString, expectedUrlStr);
}
+ @Test
+ public void testBase64Func()
+ throws Exception {
+
+ // string literal
+ String sqlQuery = "SELECT toBase64(toUtf8('hello!')), " +
"fromUtf8(fromBase64('aGVsbG8h')) FROM myTable";
+ JsonNode response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ JsonNode resultTable = response.get("resultTable");
+ JsonNode dataSchema = resultTable.get("dataSchema");
+ assertEquals(dataSchema.get("columnDataTypes").toString(),
"[\"STRING\",\"STRING\"]");
+ JsonNode rows = response.get("resultTable").get("rows");
+
+ String encodedString = rows.get(0).get(0).asText();
+ String expectedEncodedStr = toBase64(toUtf8("hello!"));
+ assertEquals(encodedString, expectedEncodedStr);
+ String decodedString = rows.get(0).get(1).asText();
+ String expectedDecodedStr = fromUtf8(fromBase64("aGVsbG8h"));
+ assertEquals(decodedString, expectedDecodedStr);
+
+ // long string literal encode
+ sqlQuery =
+ "SELECT toBase64(toUtf8('this is a long string that will encode to
more than 76 characters using base64')) "
+ + "FROM "
+ + "myTable";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ resultTable = response.get("resultTable");
+ rows = resultTable.get("rows");
+ encodedString = rows.get(0).get(0).asText();
+ assertEquals(encodedString,
+ toBase64(toUtf8("this is a long string that will encode to more than
76 characters using base64")));
+
+ // long string literal decode
+ sqlQuery = "SELECT fromUtf8(fromBase64"
+ +
"('dGhpcyBpcyBhIGxvbmcgc3RyaW5nIHRoYXQgd2lsbCBlbmNvZGUgdG8gbW9yZSB0aGFuIDc2IGNoYXJhY3RlcnMgdXNpbmcgYmFzZTY0"
+ + "')) FROM myTable";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ resultTable = response.get("resultTable");
+ rows = resultTable.get("rows");
+ decodedString = rows.get(0).get(0).asText();
+ assertEquals(decodedString, fromUtf8(fromBase64(
+
"dGhpcyBpcyBhIGxvbmcgc3RyaW5nIHRoYXQgd2lsbCBlbmNvZGUgdG8gbW9yZSB0aGFuIDc2IGNoYXJhY3RlcnMgdXNpbmcgYmFzZTY0")));
+
+ // non-string literal
+ sqlQuery = "SELECT toBase64(toUtf8(123)),
fromUtf8(fromBase64(toBase64(toUtf8(123)))), 123 FROM myTable";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ resultTable = response.get("resultTable");
+ rows = resultTable.get("rows");
+ encodedString = rows.get(0).get(0).asText();
+ decodedString = rows.get(0).get(1).asText();
+ String originalCol = rows.get(0).get(2).asText();
+ assertEquals(decodedString, originalCol);
+ assertEquals(encodedString, toBase64(toUtf8("123")));
+
+ // identifier
+ sqlQuery =
+ "SELECT Carrier, toBase64(toUtf8(Carrier)),
fromUtf8(fromBase64(toBase64(toUtf8(Carrier)))), fromBase64"
+ + "(toBase64(toUtf8(Carrier))) FROM myTable LIMIT 100";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ resultTable = response.get("resultTable");
+ dataSchema = resultTable.get("dataSchema");
+ assertEquals(dataSchema.get("columnDataTypes").toString(),
"[\"STRING\",\"STRING\",\"STRING\",\"BYTES\"]");
+ rows = response.get("resultTable").get("rows");
+ assertEquals(rows.size(), 100);
+ for (int i = 0; i < 100; i++) {
+ String original = rows.get(0).asText();
+ String encoded = rows.get(1).asText();
+ String decoded = rows.get(2).asText();
+ assertEquals(original, decoded);
+ assertEquals(encoded, toBase64(toUtf8(original)));
+ assertEquals(decoded, fromUtf8(fromBase64(toBase64(toUtf8(original)))));
+ }
+
+ // invalid argument
+ sqlQuery = "SELECT toBase64() FROM myTable";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+
assertTrue(response.get("exceptions").get(0).get("message").toString().startsWith("\"QueryExecutionError"));
+
+ // invalid argument
+ sqlQuery = "SELECT fromBase64() FROM myTable";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+
assertTrue(response.get("exceptions").get(0).get("message").toString().startsWith("\"QueryExecutionError"));
+
+ // invalid argument
+ sqlQuery = "SELECT toBase64('hello!') FROM myTable";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+
assertTrue(response.get("exceptions").get(0).get("message").toString().contains("SqlCompilationException"));
+
+ // invalid argument
+ sqlQuery = "SELECT fromBase64('hello!') FROM myTable";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+
assertTrue(response.get("exceptions").get(0).get("message").toString().contains("IllegalArgumentException"));
+
+ // string literal used in a filter
+ sqlQuery =
+ "SELECT * FROM myTable WHERE fromUtf8(fromBase64('aGVsbG8h')) !=
Carrier AND toBase64(toUtf8('hello!')) != "
+ + "Carrier LIMIT 10";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ resultTable = response.get("resultTable");
+ rows = resultTable.get("rows");
+ assertEquals(rows.size(), 10);
+
+ // non-string literal used in a filter
+ sqlQuery = "SELECT * FROM myTable WHERE
fromUtf8(fromBase64(toBase64(toUtf8(AirlineID)))) != Carrier LIMIT 10";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ resultTable = response.get("resultTable");
+ rows = resultTable.get("rows");
+ assertEquals(rows.size(), 10);
+
+ // string identifier used in a filter
+ sqlQuery = "SELECT * FROM myTable WHERE
fromUtf8(fromBase64(toBase64(toUtf8(Carrier)))) = Carrier LIMIT 10";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ resultTable = response.get("resultTable");
+ rows = resultTable.get("rows");
+ assertEquals(rows.size(), 10);
+
+ // non-string identifier used in a filter
+ sqlQuery =
+ "SELECT fromUtf8(fromBase64(toBase64(toUtf8(AirlineID)))), AirlineID
FROM myTable WHERE fromUtf8(fromBase64"
+ + "(toBase64(toUtf8(AirlineID)))) = "
+ + "AirlineID LIMIT 10";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ resultTable = response.get("resultTable");
+ dataSchema = resultTable.get("dataSchema");
+ assertEquals(dataSchema.get("columnDataTypes").toString(),
"[\"STRING\",\"LONG\"]");
+ rows = resultTable.get("rows");
+ assertEquals(rows.size(), 10);
+
+ // string identifier used in group by order by
+ sqlQuery =
+ "SELECT Carrier as originalCol, toBase64(toUtf8(Carrier)) as encoded,
fromUtf8(fromBase64(toBase64(toUtf8"
+ + "(Carrier)))) as decoded "
+ + "FROM myTable GROUP BY Carrier, toBase64(toUtf8(Carrier)),
fromUtf8(fromBase64(toBase64(toUtf8(Carrier)"
+ + "))) ORDER BY toBase64(toUtf8(Carrier))"
+ + " LIMIT 10";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ resultTable = response.get("resultTable");
+ dataSchema = resultTable.get("dataSchema");
+ assertEquals(dataSchema.get("columnDataTypes").toString(),
"[\"STRING\",\"STRING\",\"STRING\"]");
+ rows = resultTable.get("rows");
+ assertEquals(rows.size(), 10);
+ for (int i = 0; i < 10; i++) {
+ String original = rows.get(0).asText();
+ String encoded = rows.get(1).asText();
+ String decoded = rows.get(2).asText();
+ assertEquals(original, decoded);
+ assertEquals(encoded, toBase64(toUtf8(original)));
+ assertEquals(decoded, fromUtf8(fromBase64(toBase64(toUtf8(original)))));
+ }
+
+ // non-string identifier used in group by order by
+ sqlQuery =
+ "SELECT AirlineID as originalCol, toBase64(toUtf8(AirlineID)) as
encoded, fromUtf8(fromBase64(toBase64(toUtf8"
+ + "(AirlineID)))) as decoded "
+ + "FROM myTable GROUP BY AirlineID, toBase64(toUtf8(AirlineID)),
fromUtf8(fromBase64(toBase64(toUtf8"
+ + "(AirlineID)))) ORDER BY "
+ + "fromUtf8(fromBase64(toBase64(toUtf8(AirlineID)))) LIMIT 10";
+ response = postQuery(sqlQuery, _brokerBaseApiUrl);
+ resultTable = response.get("resultTable");
+ dataSchema = resultTable.get("dataSchema");
+ assertEquals(dataSchema.get("columnDataTypes").toString(),
"[\"LONG\",\"STRING\",\"STRING\"]");
+ rows = resultTable.get("rows");
+ assertEquals(rows.size(), 10);
+ for (int i = 0; i < 10; i++) {
+ String original = rows.get(0).asText();
+ String encoded = rows.get(1).asText();
+ String decoded = rows.get(2).asText();
+ assertEquals(original, decoded);
+ assertEquals(encoded, toBase64(toUtf8(original)));
+ assertEquals(decoded, fromUtf8(fromBase64(toBase64(toUtf8(original)))));
+ }
+ }
+
@Test
public void testLiteralOnlyFunc()
throws Exception {
@@ -614,7 +785,8 @@ public class OfflineClusterIntegrationTest extends
BaseClusterIntegrationTestSet
String sqlQuery =
"SELECT 1, now() as currentTs, ago('PT1H') as oneHourAgoTs, 'abc',
toDateTime(now(), 'yyyy-MM-dd z') as "
+ "today, now(), ago('PT1H'), encodeUrl('key1=value
1&key2=value@!$2&key3=value%3') as encodedUrl, "
- +
"decodeUrl('key1%3Dvalue+1%26key2%3Dvalue%40%21%242%26key3%3Dvalue%253') as
decodedUrl";
+ +
"decodeUrl('key1%3Dvalue+1%26key2%3Dvalue%40%21%242%26key3%3Dvalue%253') as
decodedUrl, toBase64"
+ + "(toUtf8('hello!')) as toBase64,
fromUtf8(fromBase64('aGVsbG8h')) as fromBase64";
JsonNode response = postQuery(sqlQuery, _brokerBaseApiUrl);
long currentTsMax = System.currentTimeMillis();
long oneHourAgoTsMax = currentTsMax - ONE_HOUR_IN_MS;
@@ -628,6 +800,8 @@ public class OfflineClusterIntegrationTest extends
BaseClusterIntegrationTestSet
String oneHourAgoColumnName =
response.get("resultTable").get("dataSchema").get("columnNames").get(6).asText();
assertEquals(response.get("resultTable").get("dataSchema").get("columnNames").get(7).asText(),
"encodedUrl");
assertEquals(response.get("resultTable").get("dataSchema").get("columnNames").get(8).asText(),
"decodedUrl");
+
assertEquals(response.get("resultTable").get("dataSchema").get("columnNames").get(9).asText(),
"toBase64");
+
assertEquals(response.get("resultTable").get("dataSchema").get("columnNames").get(10).asText(),
"fromBase64");
assertTrue(Long.parseLong(nowColumnName) > currentTsMin);
assertTrue(Long.parseLong(nowColumnName) < currentTsMax);
assertTrue(Long.parseLong(oneHourAgoColumnName) > oneHourAgoTsMin);
@@ -642,6 +816,8 @@ public class OfflineClusterIntegrationTest extends
BaseClusterIntegrationTestSet
assertEquals(response.get("resultTable").get("dataSchema").get("columnDataTypes").get(6).asText(),
"LONG");
assertEquals(response.get("resultTable").get("dataSchema").get("columnDataTypes").get(7).asText(),
"STRING");
assertEquals(response.get("resultTable").get("dataSchema").get("columnDataTypes").get(8).asText(),
"STRING");
+
assertEquals(response.get("resultTable").get("dataSchema").get("columnDataTypes").get(9).asText(),
"STRING");
+
assertEquals(response.get("resultTable").get("dataSchema").get("columnDataTypes").get(10).asText(),
"STRING");
int first = response.get("resultTable").get("rows").get(0).get(0).asInt();
long second =
response.get("resultTable").get("rows").get(0).get(1).asLong();
@@ -665,6 +841,10 @@ public class OfflineClusterIntegrationTest extends
BaseClusterIntegrationTestSet
"key1%3Dvalue+1%26key2%3Dvalue%40%21%242%26key3%3Dvalue%253");
assertEquals(response.get("resultTable").get("rows").get(0).get(8).asText(),
"key1=value 1&key2=value@!$2&key3=value%3");
+
assertEquals(response.get("resultTable").get("rows").get(0).get(9).asText(),
+ "aGVsbG8h");
+
assertEquals(response.get("resultTable").get("rows").get(0).get(10).asText(),
+ "hello!");
}
@Test(dependsOnMethods = "testBloomFilterTriggering")
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]