Skip to content

Commit

Permalink
[CALCITE-5709] Add TO_BASE32 and FROM_BASE32 functions (enabled in Bi…
Browse files Browse the repository at this point in the history
…gQuery library)
  • Loading branch information
zoudan authored and tanclary committed Jun 26, 2023
1 parent e969f87 commit 00db001
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@
import static org.apache.calcite.sql.fun.SqlLibraryOperators.FORMAT_DATETIME;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.FORMAT_TIME;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.FORMAT_TIMESTAMP;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.FROM_BASE32;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.FROM_BASE64;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.ILIKE;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.JSON_DEPTH;
Expand Down Expand Up @@ -236,6 +237,7 @@
import static org.apache.calcite.sql.fun.SqlLibraryOperators.TIMESTAMP_SECONDS;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.TIMESTAMP_TRUNC;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.TIME_TRUNC;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.TO_BASE32;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.TO_BASE64;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.TO_CHAR;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.TRANSLATE3;
Expand Down Expand Up @@ -485,6 +487,8 @@ Builder populate() {
defineMethod(INITCAP, BuiltInMethod.INITCAP.method, NullPolicy.STRICT);
defineMethod(TO_BASE64, BuiltInMethod.TO_BASE64.method, NullPolicy.STRICT);
defineMethod(FROM_BASE64, BuiltInMethod.FROM_BASE64.method, NullPolicy.STRICT);
defineMethod(TO_BASE32, BuiltInMethod.TO_BASE32.method, NullPolicy.STRICT);
defineMethod(FROM_BASE32, BuiltInMethod.FROM_BASE32.method, NullPolicy.STRICT);
defineMethod(MD5, BuiltInMethod.MD5.method, NullPolicy.STRICT);
defineMethod(SHA1, BuiltInMethod.SHA1.method, NullPolicy.STRICT);
defineMethod(SHA256, BuiltInMethod.SHA256.method, NullPolicy.STRICT);
Expand Down
22 changes: 22 additions & 0 deletions core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.apache.calcite.util.format.FormatElement;
import org.apache.calcite.util.format.FormatModels;

import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.language.Soundex;

Expand Down Expand Up @@ -142,6 +143,8 @@ public class SqlFunctions {

private static final Pattern FROM_BASE64_REGEXP = Pattern.compile("[\\t\\n\\r\\s]");

private static final Base32 BASE_32 = new Base32();

private static final Function1<List<Object>, Enumerable<Object>> LIST_AS_ENUMERABLE =
a0 -> a0 == null ? Linq4j.emptyEnumerable() : Linq4j.asEnumerable(a0);

Expand Down Expand Up @@ -259,6 +262,25 @@ private static String toBase64_(byte[] bytes) {
}
}

/** SQL TO_BASE32(string) function. */
public static String toBase32(String string) {
return toBase32_(string.getBytes(UTF_8));
}

/** SQL TO_BASE32(string) function for binary string. */
public static String toBase32(ByteString string) {
return toBase32_(string.getBytes());
}

private static String toBase32_(byte[] bytes) {
return BASE_32.encodeToString(bytes);
}

/** SQL FROM_BASE32(string) function. */
public static ByteString fromBase32(String base32) {
return new ByteString(BASE_32.decode(base32));
}

/** SQL MD5(string) function. */
public static String md5(String string) {
return DigestUtils.md5Hex(string.getBytes(UTF_8));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,8 +469,7 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding,
@LibraryOperator(libraries = {MYSQL})
public static final SqlFunction COMPRESS =
SqlBasicFunction.create("COMPRESS",
ReturnTypes.explicit(SqlTypeName.VARBINARY)
.andThen(SqlTypeTransforms.TO_NULLABLE),
ReturnTypes.VARBINARY_NULLABLE,
OperandTypes.STRING, SqlFunctionCategory.STRING);

@LibraryOperator(libraries = {MYSQL})
Expand Down Expand Up @@ -1225,8 +1224,7 @@ private static RelDataType deriveTypeMapFromArrays(SqlOperatorBinding opBinding)
@LibraryOperator(libraries = {BIG_QUERY, MYSQL})
public static final SqlFunction FROM_BASE64 =
SqlBasicFunction.create("FROM_BASE64",
ReturnTypes.explicit(SqlTypeName.VARBINARY)
.andThen(SqlTypeTransforms.TO_NULLABLE),
ReturnTypes.VARBINARY_NULLABLE,
OperandTypes.STRING, SqlFunctionCategory.STRING);

@LibraryOperator(libraries = {MYSQL})
Expand All @@ -1236,6 +1234,19 @@ private static RelDataType deriveTypeMapFromArrays(SqlOperatorBinding opBinding)
OperandTypes.STRING.or(OperandTypes.BINARY),
SqlFunctionCategory.STRING);

@LibraryOperator(libraries = {BIG_QUERY})
public static final SqlFunction FROM_BASE32 =
SqlBasicFunction.create("FROM_BASE32",
ReturnTypes.VARBINARY_NULLABLE,
OperandTypes.CHARACTER, SqlFunctionCategory.STRING);

@LibraryOperator(libraries = {BIG_QUERY})
public static final SqlFunction TO_BASE32 =
SqlBasicFunction.create("TO_BASE32",
ReturnTypes.VARCHAR_NULLABLE,
OperandTypes.STRING,
SqlFunctionCategory.STRING);

/** The "TO_CHAR(timestamp, format)" function;
* converts {@code timestamp} to string according to the given {@code format}.
*/
Expand Down
13 changes: 13 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,19 @@ public static SqlCall stripSeparator(SqlCall call) {
public static final SqlReturnTypeInference VARCHAR_NULLABLE =
VARCHAR.andThen(SqlTypeTransforms.TO_NULLABLE);

/**
* Type-inference strategy that always returns "VARBINARY".
*/
public static final SqlReturnTypeInference VARBINARY =
ReturnTypes.explicit(SqlTypeName.VARBINARY);

/**
* Type-inference strategy that always returns "VARBINARY" with nulls
* allowed if any of the operands allow nulls.
*/
public static final SqlReturnTypeInference VARBINARY_NULLABLE =
VARBINARY.andThen(SqlTypeTransforms.TO_NULLABLE);

/**
* Type-inference strategy for Histogram agg support.
*/
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,8 @@ public enum BuiltInMethod {
RIGHT(SqlFunctions.class, "right", String.class, int.class),
TO_BASE64(SqlFunctions.class, "toBase64", String.class),
FROM_BASE64(SqlFunctions.class, "fromBase64", String.class),
TO_BASE32(SqlFunctions.class, "toBase32", String.class),
FROM_BASE32(SqlFunctions.class, "fromBase32", String.class),
MD5(SqlFunctions.class, "md5", String.class),
SHA1(SqlFunctions.class, "sha1", String.class),
SHA256(SqlFunctions.class, "sha256", String.class),
Expand Down
2 changes: 2 additions & 0 deletions site/_docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2748,6 +2748,8 @@ BigQuery's type system uses confusingly different names for types and functions:
| b | LENGTH(string) | Equivalent to `CHAR_LENGTH(string)`
| b | LOG(numeric1 [, numeric2 ]) | Returns the logarithm of *numeric1* to base *numeric2*, or base e if *numeric2* is not present
| b o | LPAD(string, length [, pattern ]) | Returns a string or bytes value that consists of *string* prepended to *length* with *pattern*
| b | TO_BASE32(string) | Converts the *string* to base-32 encoded form and returns an encoded string
| b | FROM_BASE32(string) | Returns the decoded result of a base-32 *string* as a string
| m | TO_BASE64(string) | Converts the *string* to base-64 encoded form and returns a encoded string
| b m | FROM_BASE64(string) | Returns the decoded result of a base-64 *string* as a string
| b o | LTRIM(string) | Returns *string* with all blanks removed from the start
Expand Down
36 changes: 36 additions & 0 deletions testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4033,6 +4033,42 @@ static void checkRlikeFails(SqlOperatorFixture f) {
f0.forEachLibrary(list(SqlLibrary.BIG_QUERY, SqlLibrary.MYSQL), consumer);
}

@Test void testToBase32() {
final SqlOperatorFixture f0 = fixture().setFor(SqlLibraryOperators.TO_BASE32);
f0.checkFails("^to_base32('')^",
"No match found for function signature TO_BASE32\\(<CHARACTER>\\)",
false);
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.BIG_QUERY);
f.checkString("to_base32(x'436f6e766572747320612073657175656e6365206f6620425954"
+ "455320696e746f2061206261736533322d656e636f64656420535452494e472e')",
"INXW45TFOJ2HGIDBEBZWK4LVMVXGGZJAN5TCAQSZKRCVGIDJNZ2G6IDBEBRGC43FGMZC2ZLOMNXWIZ"
+ "LEEBJVIUSJJZDS4===",
"VARCHAR NOT NULL");
f.checkString("to_base32('Converts a sequence of BYTES into a base32-encoded STRING.')",
"INXW45TFOJ2HGIDBEBZWK4LVMVXGGZJAN5TCAQSZKRCVGIDJNZ2G6IDBEBRGC43FGMZC2ZLOMNXWIZ"
+ "LEEBJVIUSJJZDS4===",
"VARCHAR NOT NULL");
f.checkNull("to_base32(cast (null as varchar))");
f.checkString("to_base32(x'')", "", "VARCHAR NOT NULL");
f.checkString("to_base32('')", "", "VARCHAR NOT NULL");
}

@Test void testFromBase32() {
final SqlOperatorFixture f0 = fixture().setFor(SqlLibraryOperators.FROM_BASE32);
f0.checkFails("^from_base32('')^",
"No match found for function signature FROM_BASE32\\(<CHARACTER>\\)",
false);
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.BIG_QUERY);
f.checkString("from_base32('INXW45TFOJ2HGIDBEBZWK4LVMVXGGZJAN5TCAQSZKRCVGIDJNZ2"
+ "G6IDBEBRGC43FGMZC2ZLOMNXWIZLEEBJVIUSJJZDS4===')",
"436f6e766572747320612073657175656e6365206f6620425954455320696e746f206120626173"
+ "6533322d656e636f64656420535452494e472e",
"VARBINARY NOT NULL");

f.checkString("from_base32('')", "", "VARBINARY NOT NULL");
f.checkNull("from_base32(cast (null as varchar))");
}

@Test void testMd5() {
final SqlOperatorFixture f0 = fixture().setFor(SqlLibraryOperators.MD5);
f0.checkFails("^md5(x'')^",
Expand Down

0 comments on commit 00db001

Please sign in to comment.