Merge pull request #64991 from arenadata/ADQM-1893

Add functions to encode and decode base64url
This commit is contained in:
Robert Schulze 2024-06-14 12:41:38 +00:00 committed by GitHub
commit 06b27f0922
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 423 additions and 34 deletions

View File

@ -1136,16 +1136,136 @@ SELECT tryBase58Decode('3dc8KtHrwM') as res, tryBase58Decode('invalid') as res_i
## base64Encode
Encodes a String or FixedString as base64.
Encodes a String or FixedString as base64, according to [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648#section-4).
Alias: `TO_BASE64`.
**Syntax**
```sql
base64Encode(plaintext)
```
**Arguments**
- `plaintext` — [String](../data-types/string.md) column or constant.
**Returned value**
- A string containing the encoded value of the argument.
**Example**
``` sql
SELECT base64Encode('clickhouse');
```
Result:
```result
┌─base64Encode('clickhouse')─┐
│ Y2xpY2tob3VzZQ== │
└────────────────────────────┘
```
## base64UrlEncode
Encodes an URL (String or FixedString) as base64 with URL-specific modifications, according to [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648#section-5).
**Syntax**
```sql
base64UrlEncode(url)
```
**Arguments**
- `url` — [String](../data-types/string.md) column or constant.
**Returned value**
- A string containing the encoded value of the argument.
**Example**
``` sql
SELECT base64UrlEncode('https://clickhouse.com');
```
Result:
```result
┌─base64UrlEncode('https://clickhouse.com')─┐
│ aHR0cDovL2NsaWNraG91c2UuY29t │
└───────────────────────────────────────────┘
```
## base64Decode
Decodes a base64-encoded String or FixedString. Throws an exception in case of error.
Accepts a String and decodes it from base64, according to [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648#section-4). Throws an exception in case of an error.
Alias: `FROM_BASE64`.
**Syntax**
```sql
base64Decode(encoded)
```
**Arguments**
- `encoded` — [String](../data-types/string.md) column or constant. If the string is not a valid Base64-encoded value, an exception is thrown.
**Returned value**
- A string containing the decoded value of the argument.
**Example**
``` sql
SELECT base64Decode('Y2xpY2tob3VzZQ==');
```
Result:
```result
┌─base64Decode('Y2xpY2tob3VzZQ==')─┐
│ clickhouse │
└──────────────────────────────────┘
```
## base64UrlDecode
Accepts a base64-encoded URL and decodes it from base64 with URL-specific modifications, according to [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648#section-5). Throws an exception in case of an error.
**Syntax**
```sql
base64UrlDecode(encodedUrl)
```
**Arguments**
- `encodedUrl` — [String](../data-types/string.md) column or constant. If the string is not a valid Base64-encoded value with URL-specific modifications, an exception is thrown.
**Returned value**
- A string containing the decoded value of the argument.
**Example**
``` sql
SELECT base64UrlDecode('aHR0cDovL2NsaWNraG91c2UuY29t');
```
Result:
```result
┌─base64UrlDecode('aHR0cDovL2NsaWNraG91c2UuY29t')─┐
│ https://clickhouse.com │
└─────────────────────────────────────────────────┘
```
## tryBase64Decode
Like `base64Decode` but returns an empty string in case of error.
@ -1156,9 +1276,13 @@ Like `base64Decode` but returns an empty string in case of error.
tryBase64Decode(encoded)
```
**Parameters**
**Arguments**
- `encoded`: [String](../data-types/string.md) column or constant. If the string is not a valid Base58-encoded value, returns an empty string in case of error.
- `encoded`: [String](../data-types/string.md) column or constant. If the string is not a valid Base64-encoded value, returns an empty string.
**Returned value**
- A string containing the decoded value of the argument.
**Examples**
@ -1169,9 +1293,41 @@ SELECT tryBase64Decode('RW5jb2RlZA==') as res, tryBase64Decode('invalid') as res
```
```response
┌─res─────┬─res_invalid─┐
│ Encoded │ │
└─────────┴─────────────┘
┌─res────────┬─res_invalid─┐
│ clickhouse │ │
└────────────┴─────────────┘
```
## tryBase64UrlDecode
Like `base64UrlDecode` but returns an empty string in case of error.
**Syntax**
```sql
tryBase64UrlDecode(encodedUrl)
```
**Parameters**
- `encodedUrl`: [String](../data-types/string.md) column or constant. If the string is not a valid Base64-encoded value with URL-specific modifications, returns an empty string.
**Returned value**
- A string containing the decoded value of the argument.
**Examples**
Query:
```sql
SELECT tryBase64UrlDecode('aHR0cDovL2NsaWNraG91c2UuY29t') as res, tryBase64Decode('aHR0cHM6Ly9jbGlja') as res_invalid;
```
```response
┌─res────────────────────┬─res_invalid─┐
│ https://clickhouse.com │ │
└────────────────────────┴─────────────┘
```
## endsWith {#endswith}

View File

@ -538,16 +538,28 @@ SELECT base58Decode('3dc8KtHrwM');
Синоним: `TO_BASE64`.
## base64UrlEncode(s)
Производит кодирование URL (String или FixedString) в base64-представление в соответствии с [RFC 4648](https://tools.ietf.org/html/rfc4648).
## base64Decode(s) {#base64decode}
Декодирует base64-представление s в исходную строку. При невозможности декодирования выбрасывает исключение
Синоним: `FROM_BASE64`.
## base64UrlDecode(s)
Декодирует base64-представление URL в исходную строку в соответствии с [RFC 4648](https://tools.ietf.org/html/rfc4648). При невозможности декодирования выбрасывает исключение
## tryBase64Decode(s) {#trybase64decode}
Функционал аналогичен base64Decode, но при невозможности декодирования возвращает пустую строку.
## tryBase64UrlDecode(s)
Функционал аналогичен base64UrlDecode, но при невозможности декодирования возвращает пустую строку.
## endsWith(s, suffix) {#endswith}
Возвращает 1, если строка завершается указанным суффиксом, и 0 в противном случае.

View File

@ -12,7 +12,7 @@
# include <Common/MemorySanitizer.h>
# include <cstddef>
# include <span>
# include <string_view>
namespace DB
{
@ -22,36 +22,125 @@ namespace ErrorCodes
extern const int INCORRECT_DATA;
}
enum class Base64Variant : uint8_t
{
Normal,
Url
};
inline std::string preprocessBase64Url(std::string_view src)
{
std::string padded_src;
padded_src.reserve(src.size() + 3);
// Do symbol substitution as described in https://datatracker.ietf.org/doc/html/rfc4648#section-5
for (auto s : src)
{
switch (s)
{
case '_':
padded_src += '/';
break;
case '-':
padded_src += '+';
break;
default:
padded_src += s;
break;
}
}
/// Insert padding to please aklomp library
size_t remainder = src.size() % 4;
switch (remainder)
{
case 0:
break; // no padding needed
case 1:
padded_src.append("==="); // this case is impossible to occur with valid base64-URL encoded input, however, we'll insert padding anyway
break;
case 2:
padded_src.append("=="); // two bytes padding
break;
default: // remainder == 3
padded_src.append("="); // one byte padding
break;
}
return padded_src;
}
inline size_t postprocessBase64Url(UInt8 * dst, size_t out_len)
{
// Do symbol substitution as described in https://datatracker.ietf.org/doc/html/rfc4648#section-5
for (size_t i = 0; i < out_len; ++i)
{
switch (dst[i])
{
case '/':
dst[i] = '_';
break;
case '+':
dst[i] = '-';
break;
case '=': // stop when padding is detected
return i;
default:
break;
}
}
return out_len;
}
template <Base64Variant variant>
struct Base64Encode
{
static constexpr auto name = "base64Encode";
static constexpr auto name = (variant == Base64Variant::Normal) ? "base64Encode" : "base64UrlEncode";
static size_t getBufferSize(size_t string_length, size_t string_count)
{
return ((string_length - string_count) / 3 + string_count) * 4 + string_count;
}
static size_t perform(const std::span<const UInt8> src, UInt8 * dst)
static size_t perform(std::string_view src, UInt8 * dst)
{
size_t outlen = 0;
base64_encode(reinterpret_cast<const char *>(src.data()), src.size(), reinterpret_cast<char *>(dst), &outlen, 0);
base64_encode(src.data(), src.size(), reinterpret_cast<char *>(dst), &outlen, 0);
/// Base64 library is using AVX-512 with some shuffle operations.
/// Memory sanitizer doesn't understand if there was uninitialized memory in SIMD register but it was not used in the result of shuffle.
__msan_unpoison(dst, outlen);
if constexpr (variant == Base64Variant::Url)
outlen = postprocessBase64Url(dst, outlen);
return outlen;
}
};
template <Base64Variant variant>
struct Base64Decode
{
static constexpr auto name = "base64Decode";
static constexpr auto name = (variant == Base64Variant::Normal) ? "base64Decode" : "base64UrlDecode";
static size_t getBufferSize(size_t string_length, size_t string_count)
{
return ((string_length - string_count) / 4 + string_count) * 3 + string_count;
}
static size_t perform(const std::span<const UInt8> src, UInt8 * dst)
static size_t perform(std::string_view src, UInt8 * dst)
{
int rc;
size_t outlen = 0;
int rc = base64_decode(reinterpret_cast<const char *>(src.data()), src.size(), reinterpret_cast<char *>(dst), &outlen, 0);
if constexpr (variant == Base64Variant::Url)
{
std::string src_padded = preprocessBase64Url(src);
rc = base64_decode(src_padded.data(), src_padded.size(), reinterpret_cast<char *>(dst), &outlen, 0);
}
else
{
rc = base64_decode(src.data(), src.size(), reinterpret_cast<char *>(dst), &outlen, 0);
}
if (rc != 1)
throw Exception(
@ -64,19 +153,29 @@ struct Base64Decode
}
};
template <Base64Variant variant>
struct TryBase64Decode
{
static constexpr auto name = "tryBase64Decode";
static constexpr auto name = (variant == Base64Variant::Normal) ? "tryBase64Decode" : "tryBase64UrlDecode";
static size_t getBufferSize(size_t string_length, size_t string_count)
{
return Base64Decode::getBufferSize(string_length, string_count);
return Base64Decode<variant>::getBufferSize(string_length, string_count);
}
static size_t perform(const std::span<const UInt8> src, UInt8 * dst)
static size_t perform(std::string_view src, UInt8 * dst)
{
int rc;
size_t outlen = 0;
int rc = base64_decode(reinterpret_cast<const char *>(src.data()), src.size(), reinterpret_cast<char *>(dst), &outlen, 0);
if constexpr (variant == Base64Variant::Url)
{
std::string src_padded = preprocessBase64Url(src);
rc = base64_decode(src_padded.data(), src_padded.size(), reinterpret_cast<char *>(dst), &outlen, 0);
}
else
{
rc = base64_decode(src.data(), src.size(), reinterpret_cast<char *>(dst), &outlen, 0);
}
if (rc != 1)
outlen = 0;
@ -139,7 +238,7 @@ private:
auto * dst = dst_chars.data();
auto * dst_pos = dst;
const auto * src = src_chars.data();
const auto * src = reinterpret_cast<const char *>(src_chars.data());
size_t src_offset_prev = 0;
for (size_t row = 0; row < src_row_count; ++row)
@ -147,10 +246,6 @@ private:
const size_t src_length = src_offsets[row] - src_offset_prev - 1;
const size_t outlen = Func::perform({src, src_length}, dst_pos);
/// Base64 library is using AVX-512 with some shuffle operations.
/// Memory sanitizer don't understand if there was uninitialized memory in SIMD register but it was not used in the result of shuffle.
__msan_unpoison(dst_pos, outlen);
src += src_length + 1;
dst_pos += outlen;
*dst_pos = '\0';
@ -179,16 +274,12 @@ private:
auto * dst = dst_chars.data();
auto * dst_pos = dst;
const auto * src = src_chars.data();
const auto * src = reinterpret_cast<const char *>(src_chars.data());
for (size_t row = 0; row < src_row_count; ++row)
{
const auto outlen = Func::perform({src, src_n}, dst_pos);
/// Base64 library is using AVX-512 with some shuffle operations.
/// Memory sanitizer don't understand if there was uninitialized memory in SIMD register but it was not used in the result of shuffle.
__msan_unpoison(dst_pos, outlen);
src += src_n;
dst_pos += outlen;
*dst_pos = '\0';

View File

@ -7,7 +7,14 @@ namespace DB
{
REGISTER_FUNCTION(Base64Decode)
{
factory.registerFunction<FunctionBase64Conversion<Base64Decode>>();
FunctionDocumentation::Description description = R"(Accepts a String and decodes it from base64, according to RFC 4648 (https://datatracker.ietf.org/doc/html/rfc4648#section-4). Throws an exception in case of an error. Alias: FROM_BASE64.)";
FunctionDocumentation::Syntax syntax = "base64Decode(encoded)";
FunctionDocumentation::Arguments arguments = {{"encoded", "String column or constant. If the string is not a valid Base64-encoded value, an exception is thrown."}};
FunctionDocumentation::ReturnedValue returned_value = "A string containing the decoded value of the argument.";
FunctionDocumentation::Examples examples = {{"Example", "SELECT base64Decode('Y2xpY2tob3VzZQ==')", "clickhouse"}};
FunctionDocumentation::Categories categories = {"String encoding"};
factory.registerFunction<FunctionBase64Conversion<Base64Decode<Base64Variant::Normal>>>({description, syntax, arguments, returned_value, examples, categories});
/// MySQL compatibility alias.
factory.registerAlias("FROM_BASE64", "base64Decode", FunctionFactory::CaseInsensitive);

View File

@ -7,7 +7,14 @@ namespace DB
{
REGISTER_FUNCTION(Base64Encode)
{
factory.registerFunction<FunctionBase64Conversion<Base64Encode>>();
FunctionDocumentation::Description description = R"(Encodes a String as base64, according to RFC 4648 (https://datatracker.ietf.org/doc/html/rfc4648#section-4). Alias: TO_BASE64.)";
FunctionDocumentation::Syntax syntax = "base64Encode(plaintext)";
FunctionDocumentation::Arguments arguments = {{"plaintext", "String column or constant."}};
FunctionDocumentation::ReturnedValue returned_value = "A string containing the encoded value of the argument.";
FunctionDocumentation::Examples examples = {{"Example", "SELECT base64Encode('clickhouse')", "Y2xpY2tob3VzZQ=="}};
FunctionDocumentation::Categories categories = {"String encoding"};
factory.registerFunction<FunctionBase64Conversion<Base64Encode<Base64Variant::Normal>>>({description, syntax, arguments, returned_value, examples, categories});
/// MySQL compatibility alias.
factory.registerAlias("TO_BASE64", "base64Encode", FunctionFactory::CaseInsensitive);

View File

@ -0,0 +1,21 @@
#include <Functions/FunctionBase64Conversion.h>
#if USE_BASE64
#include <Functions/FunctionFactory.h>
namespace DB
{
REGISTER_FUNCTION(Base64UrlDecode)
{
FunctionDocumentation::Description description = R"(Accepts a base64-encoded URL and decodes it from base64 with URL-specific modifications, according to RFC 4648 (https://datatracker.ietf.org/doc/html/rfc4648#section-5).)";
FunctionDocumentation::Syntax syntax = "base64UrlDecode(encodedUrl)";
FunctionDocumentation::Arguments arguments = {{"encodedUrl", "String column or constant. If the string is not a valid Base64-encoded value, an exception is thrown."}};
FunctionDocumentation::ReturnedValue returned_value = "A string containing the decoded value of the argument.";
FunctionDocumentation::Examples examples = {{"Example", "SELECT base64UrlDecode('aHR0cDovL2NsaWNraG91c2UuY29t')", "https://clickhouse.com"}};
FunctionDocumentation::Categories categories = {"String encoding"};
factory.registerFunction<FunctionBase64Conversion<Base64Decode<Base64Variant::Url>>>({description, syntax, arguments, returned_value, examples, categories});
}
}
#endif

View File

@ -0,0 +1,21 @@
#include <Functions/FunctionBase64Conversion.h>
#if USE_BASE64
#include <Functions/FunctionFactory.h>
namespace DB
{
REGISTER_FUNCTION(Base64UrlEncode)
{
FunctionDocumentation::Description description = R"(Encodes an URL (String or FixedString) as base64 with URL-specific modifications, according to RFC 4648 (https://datatracker.ietf.org/doc/html/rfc4648#section-5).)";
FunctionDocumentation::Syntax syntax = "base64UrlEncode(url)";
FunctionDocumentation::Arguments arguments = {{"url", "String column or constant."}};
FunctionDocumentation::ReturnedValue returned_value = "A string containing the encoded value of the argument.";
FunctionDocumentation::Examples examples = {{"Example", "SELECT base64UrlEncode('https://clickhouse.com')", "aHR0cHM6Ly9jbGlja2hvdXNlLmNvbQ"}};
FunctionDocumentation::Categories categories = {"String encoding"};
factory.registerFunction<FunctionBase64Conversion<Base64Encode<Base64Variant::Url>>>({description, syntax, arguments, returned_value, examples, categories});
}
}
#endif

View File

@ -7,7 +7,14 @@ namespace DB
{
REGISTER_FUNCTION(TryBase64Decode)
{
factory.registerFunction<FunctionBase64Conversion<TryBase64Decode>>();
FunctionDocumentation::Description description = R"(Decodes a String or FixedString from base64, like base64Decode but returns an empty string in case of an error.)";
FunctionDocumentation::Syntax syntax = "tryBase64Decode(encoded)";
FunctionDocumentation::Arguments arguments = {{"encoded", "String column or constant. If the string is not a valid Base64-encoded value, returns an empty string."}};
FunctionDocumentation::ReturnedValue returned_value = "A string containing the decoded value of the argument.";
FunctionDocumentation::Examples examples = {{"valid", "SELECT tryBase64Decode('Y2xpY2tob3VzZQ==')", "clickhouse"}, {"invalid", "SELECT tryBase64Decode('invalid')", ""}};
FunctionDocumentation::Categories categories = {"String encoding"};
factory.registerFunction<FunctionBase64Conversion<TryBase64Decode<Base64Variant::Normal>>>({description, syntax, arguments, returned_value, examples, categories});
}
}

View File

@ -0,0 +1,21 @@
#include <Functions/FunctionBase64Conversion.h>
#if USE_BASE64
#include <Functions/FunctionFactory.h>
namespace DB
{
REGISTER_FUNCTION(TryBase64UrlDecode)
{
FunctionDocumentation::Description description = R"(Decodes an URL from base64, like base64UrlDecode but returns an empty string in case of an error.)";
FunctionDocumentation::Syntax syntax = "tryBase64UrlDecode(encodedUrl)";
FunctionDocumentation::Arguments arguments = {{"encodedUrl", "String column or constant. If the string is not a valid Base64-encoded value with URL-specific modifications, returns an empty string."}};
FunctionDocumentation::ReturnedValue returned_value = "A string containing the decoded value of the argument.";
FunctionDocumentation::Examples examples = {{"valid", "SELECT tryBase64UrlDecode('aHR0cHM6Ly9jbGlja2hvdXNlLmNvbQ')", "https://clickhouse.com"}, {"invalid", "SELECT tryBase64UrlDecode('aHR0cHM6Ly9jbGlja')", ""}};
FunctionDocumentation::Categories categories = {"String encoding"};
factory.registerFunction<FunctionBase64Conversion<TryBase64Decode<Base64Variant::Url>>>({description, syntax, arguments, returned_value, examples, categories});
}
}
#endif

View File

@ -1,6 +1,5 @@
-- Tags: no-fasttest
SET send_logs_level = 'fatal';
-- no-fasttest because aklomp-base64 library is required
SELECT base64Encode(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
SELECT base64Decode(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }

View File

@ -3,7 +3,6 @@
SELECT name FROM system.functions WHERE NOT is_aggregate AND origin = 'System' AND alias_to = '' AND length(description) < 10
AND name NOT IN (
'aes_decrypt_mysql', 'aes_encrypt_mysql', 'decrypt', 'encrypt',
'base64Decode', 'base64Encode', 'tryBase64Decode',
'convertCharset',
'detectLanguage', 'detectLanguageMixed',
'geoToH3',

View File

@ -0,0 +1,10 @@
https://clickhouse.com aHR0cHM6Ly9jbGlja2hvdXNlLmNvbQ https://clickhouse.com https://clickhouse.com
12? MTI_ 12? 12?
https://www.google.com/search?q=clickhouse+base64+decode&sca_esv=739f8bb380e4c7ed&ei=TfRiZqCDIrmnwPAP2KLRkA8&ved=0ahUKEwjg3ZHitsmGAxW5ExAIHVhRFPIQ4dUDCBA&uact=5&oq=clickhouse+base64+decode aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS9zZWFyY2g_cT1jbGlja2hvdXNlK2Jhc2U2NCtkZWNvZGUmc2NhX2Vzdj03MzlmOGJiMzgwZTRjN2VkJmVpPVRmUmlacUNESXJtbndQQVAyS0xSa0E4JnZlZD0wYWhVS0V3amczWkhpdHNtR0F4VzVFeEFJSFZoUkZQSVE0ZFVEQ0JBJnVhY3Q9NSZvcT1jbGlja2hvdXNlK2Jhc2U2NCtkZWNvZGU https://www.google.com/search?q=clickhouse+base64+decode&sca_esv=739f8bb380e4c7ed&ei=TfRiZqCDIrmnwPAP2KLRkA8&ved=0ahUKEwjg3ZHitsmGAxW5ExAIHVhRFPIQ4dUDCBA&uact=5&oq=clickhouse+base64+decode https://www.google.com/search?q=clickhouse+base64+decode&sca_esv=739f8bb380e4c7ed&ei=TfRiZqCDIrmnwPAP2KLRkA8&ved=0ahUKEwjg3ZHitsmGAxW5ExAIHVhRFPIQ4dUDCBA&uact=5&oq=clickhouse+base64+decode
aHR0cHM6Ly9jbGlj https://clic https://clic
aHR0cHM6Ly9jbGlja2g https://clickh https://clickh
aHR0cHM6Ly9jbGljaw https://click https://click
https://clickhouse.com aHR0cHM6Ly9jbGlja2hvdXNlLmNvbQ https://clickhouse.com https://clickhouse.com

View File

@ -0,0 +1,36 @@
-- Tags: no-fasttest
-- no-fasttest because aklomp-base64 library is required
-- incorrect number of arguments
SELECT base64UrlEncode(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
SELECT base64UrlDecode(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
SELECT tryBase64UrlDecode(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
SELECT base64UrlEncode('foo', 'excess argument'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
SELECT base64UrlDecode('foo', 'excess argument'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
SELECT tryBase64UrlDecode('foo', 'excess argument'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
-- test with valid inputs
SELECT 'https://clickhouse.com' AS original, base64UrlEncode(original) AS encoded, base64UrlDecode(encoded), tryBase64UrlDecode(encoded);
SELECT '12?' AS original, base64UrlEncode(original) AS encoded, base64UrlDecode(encoded), tryBase64UrlDecode(encoded);
SELECT 'https://www.google.com/search?q=clickhouse+base64+decode&sca_esv=739f8bb380e4c7ed&ei=TfRiZqCDIrmnwPAP2KLRkA8&ved=0ahUKEwjg3ZHitsmGAxW5ExAIHVhRFPIQ4dUDCBA&uact=5&oq=clickhouse+base64+decode' AS original, base64UrlEncode(original) AS encoded, base64UrlDecode(encoded), tryBase64UrlDecode(encoded);
-- encoded value has no padding
SELECT 'aHR0cHM6Ly9jbGlj' AS encoded, base64UrlDecode(encoded), tryBase64UrlDecode(encoded);
-- encoded value has one-byte padding
SELECT 'aHR0cHM6Ly9jbGlja2g' AS encoded, base64UrlDecode(encoded), tryBase64UrlDecode(encoded);
-- encoded value has two-bytes padding
SELECT 'aHR0cHM6Ly9jbGljaw' AS encoded, base64UrlDecode(encoded), tryBase64UrlDecode(encoded);
-- test with invalid inputs
SELECT base64UrlDecode('https://clickhouse.com'); -- { serverError INCORRECT_DATA }
SELECT tryBase64UrlDecode('https://clickhouse.com');
SELECT base64UrlDecode('12?'); -- { serverError INCORRECT_DATA }
SELECT tryBase64UrlDecode('12?');
SELECT base64UrlDecode('aHR0cHM6Ly9jbGlja'); -- { serverError INCORRECT_DATA }
SELECT tryBase64UrlDecode('aHR0cHM6Ly9jbGlja');
-- test FixedString argument
SELECT toFixedString('https://clickhouse.com', 22) AS original, base64UrlEncode(original) AS encoded, base64UrlDecode(encoded), tryBase64UrlDecode(encoded);

View File

@ -1011,6 +1011,8 @@ Updatable
Uppercased
Uptime
Uptrace
UrlDecode
UrlEncode
UserID
Util
VARCHAR