Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,53 @@ public static Slice fromBase64UrlVarbinary(@SqlType(StandardTypes.VARBINARY) Sli
}
}

@Description("Encode binary data as base32")
@ScalarFunction
@SqlType(StandardTypes.VARCHAR)
public static Slice toBase32(@SqlType(StandardTypes.VARBINARY) Slice slice)
{
String encoded;
if (slice.hasByteArray()) {
encoded = BaseEncoding.base32().encode(slice.byteArray(), slice.byteArrayOffset(), slice.length());
}
else {
encoded = BaseEncoding.base32().encode(slice.getBytes());
}
return Slices.utf8Slice(encoded);
}

@Description("Decode base32 encoded binary data")
@ScalarFunction("from_base32")
@LiteralParameters("x")
@SqlType(StandardTypes.VARBINARY)
public static Slice fromBase32Varchar(@SqlType("varchar(x)") Slice slice)
{
return decodeBase32(slice);
}

@Description("Decode base32 encoded binary data")
@ScalarFunction("from_base32")
@SqlType(StandardTypes.VARBINARY)
public static Slice fromBase32Varbinary(@SqlType(StandardTypes.VARBINARY) Slice slice)
{
return decodeBase32(slice);
}

private static Slice decodeBase32(Slice slice)
{
try {
return Slices.wrappedBuffer(BaseEncoding.base32().decode(slice.toStringUtf8()));
}
catch (IllegalArgumentException e) {
// Get cause because the root exception contains the package name in the message:
// com.google.common.io.BaseEncoding$DecodingException: Invalid input length 1
if (e.getCause() instanceof BaseEncoding.DecodingException) {
throw new TrinoException(INVALID_FUNCTION_ARGUMENT, e.getCause().getMessage(), e);
}
throw new TrinoException(INVALID_FUNCTION_ARGUMENT, e);
}
}

@Description("Encode binary data as hex")
@ScalarFunction
@SqlType(StandardTypes.VARCHAR)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
package io.trino.operator.scalar;

import com.google.common.io.BaseEncoding;
import org.testng.annotations.Test;

import java.util.Base64;
Expand Down Expand Up @@ -135,6 +136,43 @@ public void testFromBase64Url()
assertFunction(format("to_base64url(from_base64url('%s'))", encodeBase64Url(ALL_BYTES)), VARCHAR, encodeBase64Url(ALL_BYTES));
}

@Test
public void testToBase32()
{
assertFunction("to_base32(CAST('' AS VARBINARY))", VARCHAR, encodeBase32(""));
assertFunction("to_base32(CAST('a' AS VARBINARY))", VARCHAR, encodeBase32("a"));
assertFunction("to_base32(CAST('abc' AS VARBINARY))", VARCHAR, encodeBase32("abc"));
assertFunction("to_base32(CAST('hello world' AS VARBINARY))", VARCHAR, "NBSWY3DPEB3W64TMMQ======");
assertFunction("to_base32(NULL)", VARCHAR, null);
}

@Test
public void testFromBase32()
{
assertFunction("from_base32('')", VARBINARY, sqlVarbinary(""));
assertFunction("from_base32('ME======')", VARBINARY, sqlVarbinary("a"));
assertFunction("from_base32('MFRGG===')", VARBINARY, sqlVarbinary("abc"));
assertFunction("from_base32('NBSWY3DPEB3W64TMMQ======')", VARBINARY, sqlVarbinary("hello world"));

assertFunction("from_base32(to_base32(CAST('' AS VARBINARY)))", VARBINARY, sqlVarbinary(""));
assertFunction("from_base32(to_base32(CAST('a' AS VARBINARY)))", VARBINARY, sqlVarbinary("a"));
assertFunction("from_base32(to_base32(CAST('abc' AS VARBINARY)))", VARBINARY, sqlVarbinary("abc"));
assertFunction("from_base32(to_base32(CAST('hello world' AS VARBINARY)))", VARBINARY, sqlVarbinary("hello world"));
assertFunction("from_base32(CAST(to_base32(CAST('' AS VARBINARY)) AS VARBINARY))", VARBINARY, sqlVarbinary(""));
assertFunction("from_base32(CAST(to_base32(CAST('a' AS VARBINARY)) AS VARBINARY))", VARBINARY, sqlVarbinary("a"));
assertFunction("from_base32(CAST(to_base32(CAST('abc' AS VARBINARY)) AS VARBINARY))", VARBINARY, sqlVarbinary("abc"));
assertFunction("from_base32(CAST(to_base32(CAST('hello world' AS VARBINARY)) AS VARBINARY))", VARBINARY, sqlVarbinary("hello world"));
assertFunction(format("to_base32(from_base32('%s'))", encodeBase32(ALL_BYTES)), VARCHAR, encodeBase32(ALL_BYTES));

assertFunction("from_base32(CAST(NULL AS VARCHAR))", VARBINARY, null);
assertFunction("from_base32(CAST(NULL AS VARBINARY))", VARBINARY, null);

assertInvalidFunction("from_base32('1=')", "Invalid input length 1");
assertInvalidFunction("from_base32('M1======')", "Unrecognized character: 1");
assertInvalidFunction("from_base32(CAST('1=' AS VARBINARY))", "Invalid input length 1");
assertInvalidFunction("from_base32(CAST('M1======' AS VARBINARY))", "Unrecognized character: 1");
}

@Test
public void testToHex()
{
Expand Down Expand Up @@ -459,6 +497,16 @@ private static String encodeBase64Url(String value)
return encodeBase64Url(value.getBytes(UTF_8));
}

private static String encodeBase32(String value)
{
return encodeBase32(value.getBytes(UTF_8));
}

private static String encodeBase32(byte[] value)
{
return BaseEncoding.base32().encode(value);
}

private static String encodeHex(String value)
{
return base16().encode(value.getBytes(UTF_8));
Expand Down
8 changes: 8 additions & 0 deletions docs/src/main/sphinx/functions/binary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ The Base64 functions implement the encoding specified in :rfc:`4648`.

Encodes ``binary`` into a base64 string representation using the URL safe alphabet.

.. function:: from_base32(string) -> varbinary

Decodes binary data from the base32 encoded ``string``.

.. function:: to_base32(binary) -> varchar

Encodes ``binary`` into a base32 string representation.

Hex encoding functions
----------------------

Expand Down
2 changes: 2 additions & 0 deletions docs/src/main/sphinx/functions/list-by-topic.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ For more details, see :doc:`binary`

* ``concat()``
* :func:`crc32`
* :func:`from_base32`
* :func:`from_base64`
* :func:`from_base64url`
* :func:`from_big_endian_32`
Expand All @@ -118,6 +119,7 @@ For more details, see :doc:`binary`
* :func:`spooky_hash_v2_32`
* :func:`spooky_hash_v2_64`
* ``substr()``
* :func:`to_base32`
* :func:`to_base64`
* :func:`to_base64url`
* :func:`to_big_endian_32`
Expand Down
2 changes: 2 additions & 0 deletions docs/src/main/sphinx/functions/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ F
- :func:`format_datetime`
- :func:`format_number`
- :func:`from_base`
- :func:`from_base32`
- :func:`from_base64`
- :func:`from_base64url`
- :func:`from_big_endian_32`
Expand Down Expand Up @@ -467,6 +468,7 @@ T
- :func:`timezone_hour`
- :func:`timezone_minute`
- :func:`to_base`
- :func:`to_base32`
- :func:`to_base64`
- :func:`to_base64url`
- :func:`to_big_endian_32`
Expand Down