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

tanner pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 4e6c403842 [CALCITE-5935] Add CODE_POINTS_TO_BYTES function (enabled 
in BigQuery library)
4e6c403842 is described below

commit 4e6c403842bebc519b1a1ededc9c2311059d7cb2
Author: macroguo <[email protected]>
AuthorDate: Tue Aug 22 17:59:02 2023 +0800

    [CALCITE-5935] Add CODE_POINTS_TO_BYTES function (enabled in BigQuery 
library)
---
 babel/src/test/resources/sql/big-query.iq          | 58 ++++++++++++++++++++++
 .../calcite/adapter/enumerable/RexImpTable.java    |  3 ++
 .../apache/calcite/runtime/CalciteResource.java    |  3 ++
 .../org/apache/calcite/runtime/SqlFunctions.java   | 22 ++++++++
 .../calcite/sql/fun/SqlLibraryOperators.java       |  7 +++
 .../org/apache/calcite/sql/type/OperandTypes.java  | 32 ++++++++++++
 .../org/apache/calcite/util/BuiltInMethod.java     |  1 +
 .../calcite/runtime/CalciteResource.properties     |  1 +
 site/_docs/reference.md                            |  1 +
 .../org/apache/calcite/test/SqlOperatorTest.java   | 31 ++++++++++++
 10 files changed, 159 insertions(+)

diff --git a/babel/src/test/resources/sql/big-query.iq 
b/babel/src/test/resources/sql/big-query.iq
index d50969b81a..2479eb3663 100755
--- a/babel/src/test/resources/sql/big-query.iq
+++ b/babel/src/test/resources/sql/big-query.iq
@@ -1971,6 +1971,64 @@ SELECT CONTAINS_SUBSTR('{"lunch":"soup"}', "lunch", 
json_scope=>"JSON_KEYS_AND_V
 
 !ok
 
+#####################################################################
+# CODE_POINTS_TO_BYTES(array<integer>)
+#
+# Takes an array of extended ASCII code points as ARRAY<INT64>
+# and returns BYTES.
+#
+SELECT CODE_POINTS_TO_BYTES(array[65, 66, 67, 68]) as result;
++----------+
+| result   |
++----------+
+| 41424344 |
++----------+
+(1 row)
+
+!ok
+
+SELECT CODE_POINTS_TO_BYTES(array[255, 254, 65, 64]) as result;
++----------+
+| result   |
++----------+
+| fffe4140 |
++----------+
+(1 row)
+
+!ok
+
+SELECT CODE_POINTS_TO_BYTES(null) as result;
++--------+
+| result |
++--------+
+|        |
++--------+
+(1 row)
+
+!ok
+
+SELECT CODE_POINTS_TO_BYTES(array[65, null]) as result;
++--------+
+| result |
++--------+
+|        |
++--------+
+(1 row)
+
+!ok
+
+SELECT CODE_POINTS_TO_BYTES('abc') as result;
+Cannot apply 'CODE_POINTS_TO_BYTES' to arguments of type 
'CODE_POINTS_TO_BYTES(<CHAR(3)>)'. Supported form(s): 
CODE_POINTS_TO_BYTES(<INTEGER ARRAY>)
+!error
+
+SELECT CODE_POINTS_TO_BYTES(array[-1]) as result;
+Input arguments of CODE_POINTS_TO_BYTES out of range: -1
+!error
+
+SELECT CODE_POINTS_TO_BYTES(array[2147483648, 1]);
+Input arguments of CODE_POINTS_TO_BYTES out of range: 2147483648
+!error
+
 #####################################################################
 # DATE
 #
diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 91a7aed9b0..03d7a07c0f 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -150,6 +150,7 @@ import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.BOOL_OR;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.CEIL_BIG_QUERY;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.CHAR;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.CHR;
+import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.CODE_POINTS_TO_BYTES;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.COMPRESS;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT2;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT_FUNCTION;
@@ -564,6 +565,8 @@ public class RexImpTable {
       defineMethod(ASCII, BuiltInMethod.ASCII.method, NullPolicy.STRICT);
       defineMethod(CHAR, BuiltInMethod.CHAR_FROM_ASCII.method,
           NullPolicy.SEMI_STRICT);
+      defineMethod(CODE_POINTS_TO_BYTES, 
BuiltInMethod.CODE_POINTS_TO_BYTES.method,
+          NullPolicy.STRICT);
       defineMethod(REPEAT, BuiltInMethod.REPEAT.method, NullPolicy.STRICT);
       defineMethod(SPACE, BuiltInMethod.SPACE.method, NullPolicy.STRICT);
       defineMethod(STRCMP, BuiltInMethod.STRCMP.method, NullPolicy.STRICT);
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java 
b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index 230f2ea0ca..832e3715d4 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -298,6 +298,9 @@ public interface CalciteResource {
   @BaseMessage("Date literal ''{0}'' out of range")
   ExInst<SqlValidatorException> dateLiteralOutOfRange(String a0);
 
+  @BaseMessage("Input arguments of CODE_POINTS_TO_BYTES out of range: 
{0,number,#}")
+  ExInst<CalciteException> inputArgumentsOfCodePointsToBytesOutOfRange(long 
a0);
+
   @BaseMessage("String literal continued on same line")
   ExInst<SqlValidatorException> stringFragsOnSameLine();
 
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java 
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index efeac7d0ef..835eb068b4 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -1116,6 +1116,28 @@ public class SqlFunctions {
     return String.valueOf(Character.toChars(n));
   }
 
+  /**
+   * SQL CODE_POINTS_TO_BYTES function.
+   */
+  public static @Nullable ByteString codePointsToBytes(List codePoints) {
+    int length = codePoints.size();
+    byte[] bytes = new byte[length];
+    for (int i = 0; i < length; i++) {
+      Object codePoint = codePoints.get(i);
+      if (codePoint == null) {
+        return null;
+      }
+      assert codePoint instanceof Number;
+      long cp = ((Number) codePoint).longValue();
+      if (cp < 0 || cp > 255) {
+        throw RESOURCE.inputArgumentsOfCodePointsToBytesOutOfRange(cp).ex();
+      }
+      bytes[i] = (byte) cp;
+    }
+
+    return new ByteString(bytes);
+  }
+
   /** SQL OCTET_LENGTH(binary) function. */
   public static int octetLength(ByteString s) {
     return s.length();
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
index 42492d97e7..98ecf893c2 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
@@ -1786,6 +1786,13 @@ public abstract class SqlLibraryOperators {
           OperandTypes.INTEGER,
           SqlFunctionCategory.STRING);
 
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction CODE_POINTS_TO_BYTES =
+      SqlBasicFunction.create("CODE_POINTS_TO_BYTES",
+          ReturnTypes.VARBINARY_NULLABLE,
+          OperandTypes.ARRAY_OF_INTEGER,
+          SqlFunctionCategory.STRING);
+
   @LibraryOperator(libraries = {ALL})
   public static final SqlFunction TANH =
       SqlBasicFunction.create("TANH",
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java 
b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index d60c6fa72c..8f77ddedd9 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -513,6 +513,38 @@ public abstract class OperandTypes {
         }
       };
 
+  public static final SqlSingleOperandTypeChecker ARRAY_OF_INTEGER =
+      new FamilyOperandTypeChecker(ImmutableList.of(SqlTypeFamily.ARRAY), i -> 
false) {
+        @Override public boolean checkSingleOperandType(SqlCallBinding 
callBinding, SqlNode operand,
+            int iFormalOperand, SqlTypeFamily family, boolean throwOnFailure) {
+          if (!super.checkSingleOperandType(
+              callBinding,
+              operand,
+              iFormalOperand,
+              family,
+              throwOnFailure)) {
+            return false;
+          }
+          RelDataType type = SqlTypeUtil.deriveType(callBinding, operand);
+          if (SqlTypeUtil.isNull(type)) {
+            return true;
+          }
+          RelDataType componentType =
+              requireNonNull(type.getComponentType(), "componentType");
+          if (SqlTypeUtil.isIntType(componentType) || 
SqlTypeUtil.isNull(componentType)) {
+            return true;
+          }
+
+          if (throwOnFailure) {
+            throw callBinding.newValidationSignatureError();
+          }
+          return false;
+        }
+
+        @Override public String getAllowedSignatures(SqlOperator op, String 
opName) {
+          return opName + "(<INTEGER ARRAY>)";
+        }
+      };
 
   /** Checks that returns whether a value is a multiset or an array.
    * Cf Java, where list and set are collections but a map is not. */
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java 
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 3b8b3f4910..6d7eedd10f 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -356,6 +356,7 @@ public enum BuiltInMethod {
   ASCII(SqlFunctions.class, "ascii", String.class),
   CHAR_FROM_ASCII(SqlFunctions.class, "charFromAscii", int.class),
   CHAR_FROM_UTF8(SqlFunctions.class, "charFromUtf8", int.class),
+  CODE_POINTS_TO_BYTES(SqlFunctions.class, "codePointsToBytes", List.class),
   CONVERT(SqlFunctions.class, "convertWithCharset", String.class, String.class,
       String.class),
   EXP(SqlFunctions.class, "exp", double.class),
diff --git 
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties 
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index 1033899a30..a1ed50de6d 100644
--- 
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ 
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -102,6 +102,7 @@ OperandNotComparable=Operands {0} not comparable to each 
other
 TypeNotComparableEachOther=Types {0} not comparable to each other
 NumberLiteralOutOfRange=Numeric literal ''{0}'' out of range
 DateLiteralOutOfRange=Date literal ''{0}'' out of range
+InputArgumentsOfCodePointsToBytesOutOfRange=Input arguments of 
CODE_POINTS_TO_BYTES out of range: {0,number,#}
 StringFragsOnSameLine=String literal continued on same line
 AliasMustBeSimpleIdentifier=Table or column alias must be a simple identifier
 CharLiteralAliasNotValid=Expecting alias, found character literal
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index ccb79bc1f4..6c0b9c2a63 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2685,6 +2685,7 @@ BigQuery's type system uses confusingly different names 
for types and functions:
 | b | CEIL(value)                                    | Similar to standard 
`CEIL(value)` except if *value* is an integer type, the return type is a double
 | m s | CHAR(integer)                                | Returns the character 
whose ASCII code is *integer* % 256, or null if *integer* &lt; 0
 | b o p | CHR(integer)                               | Returns the character 
whose UTF-8 code is *integer*
+| b | CODE_POINTS_TO_BYTES(integers)                 | Converts *integers*, an 
array of integers between 0 and 255 inclusive, into bytes; throws error if any 
element is out of range
 | o | CONCAT(string, string)                         | Concatenates two 
strings, returns null only when both string arguments are null, otherwise 
treats null as empty string
 | b m | CONCAT(string [, string ]*)                  | Concatenates one or 
more strings, returns null if any of the arguments is null
 | p q | CONCAT(string [, string ]*)                  | Concatenates one or 
more strings, null is treated as empty string
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java 
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index 5352779c16..f2ad858e95 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -1804,6 +1804,37 @@ public class SqlOperatorTest {
         consumer);
   }
 
+  @Test void testCodePointsToBytes() {
+    final SqlOperatorFixture f = fixture()
+        .setFor(SqlLibraryOperators.CODE_POINTS_TO_BYTES, VM_FENNEL, VM_JAVA)
+        .withLibrary(SqlLibrary.BIG_QUERY);
+    f.checkFails("^code_points_to_bytes('abc')^",
+        "Cannot apply 'CODE_POINTS_TO_BYTES' to arguments of type "
+            + "'CODE_POINTS_TO_BYTES\\(<CHAR\\(3\\)>\\)'\\. "
+            + "Supported form\\(s\\): CODE_POINTS_TO_BYTES\\(<INTEGER 
ARRAY>\\)",
+        false);
+    f.checkFails("^code_points_to_bytes(array['abc'])^",
+        "Cannot apply 'CODE_POINTS_TO_BYTES' to arguments of type "
+            + "'CODE_POINTS_TO_BYTES\\(<CHAR\\(3\\) ARRAY>\\)'\\. "
+            + "Supported form\\(s\\): CODE_POINTS_TO_BYTES\\(<INTEGER 
ARRAY>\\)",
+        false);
+
+    f.checkFails("code_points_to_bytes(array[-1])",
+        "Input arguments of CODE_POINTS_TO_BYTES out of range: -1", true);
+    f.checkFails("code_points_to_bytes(array[2147483648, 1])",
+        "Input arguments of CODE_POINTS_TO_BYTES out of range: 2147483648", 
true);
+
+    f.checkString("code_points_to_bytes(array[65, 66, 67, 68])", "41424344", 
"VARBINARY NOT NULL");
+    f.checkString("code_points_to_bytes(array[255, 254, 65, 64])", "fffe4140",
+        "VARBINARY NOT NULL");
+    f.checkString("code_points_to_bytes(array[1+2, 3, 4])", "030304",
+        "VARBINARY NOT NULL");
+
+    f.checkNull("code_points_to_bytes(null)");
+    f.checkNull("code_points_to_bytes(array[null])");
+    f.checkNull("code_points_to_bytes(array[65, null])");
+  }
+
   @Test void testSelect() {
     final SqlOperatorFixture f = fixture();
     f.check("select * from (values(1))", SqlTests.INTEGER_TYPE_CHECKER, 1);

Reply via email to