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

mrhhsg pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 768b3b55e33 [feature](function) Support Hive-compatible encode and 
decode (#68131)
768b3b55e33 is described below

commit 768b3b55e33784517d4c73fe1e74602ffe240565
Author: hacklu-tu <[email protected]>
AuthorDate: Mon Oct 12 10:43:51 2026 +0800

    [feature](function) Support Hive-compatible encode and decode (#68131)
    
    ### What problem does this PR solve?
    
    Issue Number: #48203
    
    Related PR: apache/doris-website#4151
    
    Problem Summary:
    
    Doris does not provide `encode(string, charset)` and `decode(binary,
    charset)` scalar functions with Hive-compatible character conversion
    semantics, which makes Hive workload migration harder.
    
    This PR:
    
    - adds Nereids signatures and FE constant folding for `encode` and
    `decode`;
    - requires the charset argument to be a supported string literal or
    `NULL`; columns and other expressions are rejected during analysis so
    the vectorized BE can select the conversion path once per query;
    - adds vectorized BE implementations backed by simdutf with strict
    malformed and unmappable input handling;
    - supports `US-ASCII`, `ISO-8859-1`, `UTF-8`, `UTF-16BE`, `UTF-16LE`,
    and `UTF-16`, matched case-insensitively;
    - follows Java/Hive UTF-16 BOM behavior and propagates nulls after
    validating the charset literal, so an unsupported charset remains an
    error even when the data argument is `NULL`;
    - adds FE/BE unit tests and SQL regression coverage for supported
    conversions and error cases.
    
    The conversion semantics are Hive-compatible for the supported character
    sets. Doris intentionally restricts the charset argument to a literal
    (or `NULL`) rather than supporting a per-row charset column.
    
    ### Release note
    
    Add `encode` and `decode` scalar functions with Hive-compatible
    conversion semantics for `US-ASCII`, `ISO-8859-1`, `UTF-8`, `UTF-16BE`,
    `UTF-16LE`, and `UTF-16`. The charset argument must be a supported
    string literal or `NULL`.
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [x] Regression test
    - `test_encode_decode` passed in generated-output and comparison modes
    on a native Linux Doris cluster.
        - [x] Unit Test
    - FE `EncodeDecodePlannerTest`: 2 tests passed, covering planner folding
    and charset-validation precedence over NULL folding.
            - FE `EncodeDecodeTest`: 4 tests passed.
            - FE Maven reactor succeeded.
    - BE `function_character_encoding_test.*`: 3 tests passed before the
    simdutf refactor; the same suite covers the simdutf implementation in
    CI.
            - Native Linux FE/BE build succeeded.
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    - Behavior changed:
        - [ ] No.
    - [x] Yes. Adds `encode` and `decode`, including UTF-16 BOM semantics,
    strict conversion errors, FE constant folding, and the literal charset
    contract.
    
    - Does this need documentation?
        - [ ] No.
        - [x] Yes. https://github.com/apache/doris-website/pull/4151
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 be/benchmark/benchmark_character_encoding.hpp      | 180 ++++++++
 be/benchmark/benchmark_main.cpp                    |   1 +
 .../exprs/function/function_character_encoding.cpp | 461 +++++++++++++++++++++
 be/src/exprs/function/function_compress.cpp        |   1 +
 be/src/exprs/function/function_regexp.cpp          |   1 +
 be/src/exprs/function/function_search.h            |   8 +
 be/src/exprs/function/simple_function_factory.h    |   2 +
 .../function/function_character_encoding_test.cpp  | 263 ++++++++++++
 .../doris/catalog/BuiltinScalarFunctions.java      |   4 +
 .../functions/executable/StringArithmetic.java     |  66 +++
 .../functions/scalar/CharacterSetLiterals.java     |  71 ++++
 .../trees/expressions/functions/scalar/Decode.java |  84 ++++
 .../trees/expressions/functions/scalar/Encode.java |  84 ++++
 .../expressions/visitor/ScalarFunctionVisitor.java |  10 +
 .../functions/executable/StringArithmeticTest.java |  65 +++
 .../functions/scalar/EncodeDecodePlannerTest.java  |  96 +++++
 .../functions/scalar/EncodeDecodeTest.java         | 172 ++++++++
 .../binary_functions/test_encode_decode.out        |  35 ++
 .../binary_functions/test_encode_decode.groovy     | 180 ++++++++
 19 files changed, 1784 insertions(+)

diff --git a/be/benchmark/benchmark_character_encoding.hpp 
b/be/benchmark/benchmark_character_encoding.hpp
new file mode 100644
index 00000000000..e6e84a34a3b
--- /dev/null
+++ b/be/benchmark/benchmark_character_encoding.hpp
@@ -0,0 +1,180 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+#include <benchmark/benchmark.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include "core/block/block.h"
+#include "core/column/column_const.h"
+#include "core/column/column_string.h"
+#include "core/column/column_varbinary.h"
+#include "core/data_type/data_type_string.h"
+#include "core/data_type/data_type_varbinary.h"
+#include "exprs/function/simple_function_factory.h"
+#include "exprs/function_context.h"
+#include "util/defer_op.h"
+
+namespace doris {
+namespace {
+
+struct CharacterEncodingData {
+    std::string input;
+    std::string expected;
+    std::string charset;
+};
+
+template <int Scenario>
+CharacterEncodingData make_character_encoding_data(size_t length) {
+    CharacterEncodingData data;
+    auto& [input, expected, charset] = data;
+    if constexpr (Scenario == 0) {
+        charset = "UTF-16BE";
+        input.assign(length, 'A');
+        for (size_t i = 0; i < length; ++i) {
+            expected.append("\0A", 2);
+        }
+    } else if constexpr (Scenario == 1) {
+        charset = "ISO-8859-1";
+        input.assign(length, '\xE9');
+        for (size_t i = 0; i < length; ++i) {
+            expected += "é";
+        }
+    } else if constexpr (Scenario == 2) {
+        charset = "UTF-8";
+        input.assign(length - 7, 'A');
+        input += "中😀";
+        expected = input;
+    } else if constexpr (Scenario == 3) {
+        charset = "UTF-16BE";
+        for (size_t i = 0; i < length / 2; ++i) {
+            input += "N-"; // The UTF-16BE byte pair for 中.
+            expected += "中";
+        }
+    } else {
+        charset = "US-ASCII";
+        input.assign(length, 'A');
+        expected = input;
+    }
+    return data;
+}
+
+// Benchmark actual block execution, including result allocation and converter 
setup, not just
+// ICU calls. Inputs are materialized columns so constant folding cannot 
eliminate conversion.
+// The charset is constant by contract. Large rows use smaller blocks to bound 
working memory.
+template <bool Encode, int Scenario>
+void BM_character_encoding(benchmark::State& state) {
+    const size_t length = state.range(0);
+    const size_t rows = std::max<size_t>(1, std::min<size_t>(4096, (1 << 20) / 
length));
+    const auto [input, expected, charset] = 
make_character_encoding_data<Scenario>(length);
+    DataTypePtr string_type = std::make_shared<DataTypeString>();
+    DataTypePtr binary_type = std::make_shared<DataTypeVarbinary>();
+    DataTypePtr input_type = Encode ? string_type : binary_type;
+    DataTypePtr result_type = Encode ? binary_type : string_type;
+    auto values = input_type->create_column();
+    for (size_t i = 0; i < rows; ++i) {
+        values->insert_data(input.data(), input.size());
+    }
+    auto charsets = ColumnString::create();
+    charsets->insert_data(charset.data(), charset.size());
+    ColumnPtr charset_column = ColumnConst::create(std::move(charsets), rows);
+    Block block {{std::move(values), input_type, "input"},
+                 {std::move(charset_column), string_type, "charset"}};
+    auto function = SimpleFunctionFactory::instance().get_function(
+            Encode ? "encode" : "decode", 
block.get_columns_with_type_and_name(), result_type);
+    if (function == nullptr) {
+        state.SkipWithError("Character encoding function not registered");
+        return;
+    }
+    auto context = FunctionContext::create_context(nullptr, result_type, 
{input_type, string_type});
+    auto status = function->open(context.get(), 
FunctionContext::FRAGMENT_LOCAL);
+    if (!status.ok()) {
+        state.SkipWithError(status.to_string());
+        return;
+    }
+    Defer close_fragment {[&] {
+        auto close_status = function->close(context.get(), 
FunctionContext::FRAGMENT_LOCAL);
+        if (!close_status.ok()) {
+            state.SkipWithError(close_status.to_string());
+        }
+    }};
+    status = function->open(context.get(), FunctionContext::THREAD_LOCAL);
+    if (!status.ok()) {
+        state.SkipWithError(status.to_string());
+        return;
+    }
+    Defer close_thread {[&] {
+        auto close_status = function->close(context.get(), 
FunctionContext::THREAD_LOCAL);
+        if (!close_status.ok()) {
+            state.SkipWithError(close_status.to_string());
+        }
+    }};
+    block.insert({nullptr, result_type, "result"});
+
+    // Verify every output outside the measured loop.
+    status = function->execute(context.get(), block, {0, 1}, 2, rows);
+    if (!status.ok()) {
+        state.SkipWithError(status.to_string());
+        return;
+    }
+    for (size_t i = 0; i < rows; ++i) {
+        auto actual = block.get_by_position(2).column->get_data_at(i);
+        if (std::string_view(actual.data, actual.size) != expected) {
+            state.SkipWithError("Character encoding result mismatch");
+            return;
+        }
+    }
+    for (auto _ : state) {
+        status = function->execute(context.get(), block, {0, 1}, 2, rows);
+        if (!status.ok()) {
+            state.SkipWithError(status.to_string());
+            break;
+        }
+        benchmark::DoNotOptimize(block.get_by_position(2).column);
+        benchmark::ClobberMemory();
+    }
+    state.SetItemsProcessed(state.iterations() * rows);
+    state.SetBytesProcessed(state.iterations() * rows * input.size());
+}
+
+BENCHMARK_TEMPLATE(BM_character_encoding, true, 0)
+        ->Name("encode_utf16be_ascii")
+        ->ArgsProduct({{15, 63, 1023, 65535}});
+BENCHMARK_TEMPLATE(BM_character_encoding, false, 1)
+        ->Name("decode_latin1_nonascii")
+        ->ArgsProduct({{15, 63, 1023, 65535}});
+BENCHMARK_TEMPLATE(BM_character_encoding, true, 2)
+        ->Name("encode_utf8_mixed")
+        ->ArgsProduct({{15, 63, 1023, 65535}});
+BENCHMARK_TEMPLATE(BM_character_encoding, false, 2)
+        ->Name("decode_utf8_mixed")
+        ->ArgsProduct({{15, 63, 1023, 65535}});
+BENCHMARK_TEMPLATE(BM_character_encoding, false, 3)
+        ->Name("decode_utf16be_cjk")
+        ->ArgsProduct({{16, 64, 1024, 65536}});
+BENCHMARK_TEMPLATE(BM_character_encoding, true, 4)
+        ->Name("encode_ascii")
+        ->ArgsProduct({{15, 63, 1023, 65535, 1 << 22}});
+
+} // namespace
+} // namespace doris
diff --git a/be/benchmark/benchmark_main.cpp b/be/benchmark/benchmark_main.cpp
index 4eb676e5030..229310d5e22 100644
--- a/be/benchmark/benchmark_main.cpp
+++ b/be/benchmark/benchmark_main.cpp
@@ -27,6 +27,7 @@
 #include "benchmark_binary_arithmetic.hpp"
 #include "benchmark_bit_pack.hpp"
 #include "benchmark_case_expr.hpp"
+#include "benchmark_character_encoding.hpp"
 #include "benchmark_column_array_view.hpp"
 #include "benchmark_column_array_view_distance.hpp"
 #include "benchmark_fastunion.hpp"
diff --git a/be/src/exprs/function/function_character_encoding.cpp 
b/be/src/exprs/function/function_character_encoding.cpp
new file mode 100644
index 00000000000..2a0abc379d5
--- /dev/null
+++ b/be/src/exprs/function/function_character_encoding.cpp
@@ -0,0 +1,461 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <simdutf.h>
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <limits>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <vector>
+
+#include "common/status.h"
+#include "core/assert_cast.h"
+#include "core/block/block.h"
+#include "core/column/column.h"
+#include "core/column/column_const.h"
+#include "core/column/column_nullable.h"
+#include "core/column/column_string.h"
+#include "core/column/column_varbinary.h"
+#include "core/data_type/data_type_nullable.h"
+#include "core/data_type/data_type_string.h"
+#include "core/data_type/data_type_varbinary.h"
+#include "core/string_ref.h"
+#include "exprs/function/function.h"
+#include "exprs/function/simple_function_factory.h"
+
+namespace doris {
+namespace {
+
+enum class CharacterSet : uint8_t {
+    US_ASCII,
+    ISO_8859_1,
+    UTF_8,
+    UTF_16BE,
+    UTF_16LE,
+    UTF_16,
+    SIZE,
+};
+
+constexpr std::array<std::string_view, static_cast<size_t>(CharacterSet::SIZE)>
+        SUPPORTED_CHARACTER_SETS = {"US-ASCII", "ISO-8859-1", "UTF-8",
+                                    "UTF-16BE", "UTF-16LE",   "UTF-16"};
+
+bool equals_ignore_case(StringRef value, std::string_view expected) {
+    if (value.size != expected.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < value.size; ++i) {
+        const char current = value.data[i] >= 'a' && value.data[i] <= 'z'
+                                     ? value.data[i] - ('a' - 'A')
+                                     : value.data[i];
+        if (current != expected[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+Status parse_character_set(StringRef value, CharacterSet& character_set) {
+    for (size_t i = 0; i < SUPPORTED_CHARACTER_SETS.size(); ++i) {
+        if (equals_ignore_case(value, SUPPORTED_CHARACTER_SETS[i])) {
+            character_set = static_cast<CharacterSet>(i);
+            return Status::OK();
+        }
+    }
+    return Status::InvalidArgument(
+            "Unsupported character set '{}'. Supported character sets are 
US-ASCII, "
+            "ISO-8859-1, UTF-8, UTF-16BE, UTF-16LE, and UTF-16",
+            std::string(value.data, value.size));
+}
+
+Status conversion_error(std::string_view character_set_name, 
simdutf::error_code error) {
+    return Status::InvalidArgument("Character conversion using '{}' failed: 
{}", character_set_name,
+                                   simdutf::error_to_string(error));
+}
+
+Status reject_too_large(std::string_view character_set_name) {
+    return Status::InvalidArgument("Input is too large for character 
conversion using '{}'",
+                                   character_set_name);
+}
+
+// Grow the byte buffer, then return the address of the newly reserved range.
+char* reserve_output(ColumnString::Chars& output, size_t extra) {
+    const size_t start = output.size();
+    ColumnString::check_chars_length(start + extra, 0);
+    output.resize(start + extra);
+    return reinterpret_cast<char*>(output.data() + start);
+}
+
+Status copy_validated(StringRef input, std::string_view character_set_name,
+                      simdutf::result validation, ColumnString::Chars& output) 
{
+    if (validation.error != simdutf::SUCCESS) {
+        return conversion_error(character_set_name, validation.error);
+    }
+    memcpy(reserve_output(output, input.size), input.data, input.size);
+    return Status::OK();
+}
+
+// Doris string bytes are not guaranteed to be char16_t-aligned.
+const char16_t* utf16_units(StringRef input, std::vector<char16_t>& aligned) {
+    if (reinterpret_cast<uintptr_t>(input.data) % alignof(char16_t) == 0) {
+        return reinterpret_cast<const char16_t*>(input.data);
+    }
+    const size_t units = input.size / 2;
+    aligned.resize(units);
+    memcpy(aligned.data(), input.data, input.size);
+    return aligned.data();
+}
+
+template <bool Encode>
+class FunctionCharacterEncoding : public IFunction {
+public:
+    static constexpr auto name = Encode ? "encode" : "decode";
+
+    static FunctionPtr create() { return 
std::make_shared<FunctionCharacterEncoding>(); }
+
+    String get_name() const override { return name; }
+
+    size_t get_number_of_arguments() const override { return 2; }
+
+    DataTypePtr get_return_type_impl(const DataTypes& arguments) const 
override {
+        DataTypePtr result_type;
+        if constexpr (Encode) {
+            result_type = std::make_shared<DataTypeVarbinary>();
+        } else {
+            result_type = std::make_shared<DataTypeString>();
+        }
+        return have_nullable(arguments) ? make_nullable(result_type) : 
result_type;
+    }
+
+    ColumnNumbers get_arguments_that_are_always_constant() const override { 
return {1}; }
+
+    bool use_default_implementation_for_nulls() const override { return false; 
}
+
+    Status execute_impl(FunctionContext* /*context*/, Block& block, const 
ColumnNumbers& arguments,
+                        uint32_t result, size_t input_rows_count) const 
override {
+        auto [input_column, input_is_const] =
+                unpack_if_const(block.get_by_position(arguments[0]).column);
+        auto [character_set_column, character_set_is_const] =
+                unpack_if_const(block.get_by_position(arguments[1]).column);
+        DCHECK(character_set_is_const);
+        const auto* input_nullable = 
check_and_get_column<ColumnNullable>(input_column.get());
+        const auto* character_set_nullable =
+                
check_and_get_column<ColumnNullable>(character_set_column.get());
+        const IColumn* input_nested =
+                input_nullable ? &input_nullable->get_nested_column() : 
input_column.get();
+        const auto* input_values = get_input_values(input_nested);
+        const IColumn* character_set_nested = character_set_nullable
+                                                      ? 
&character_set_nullable->get_nested_column()
+                                                      : 
character_set_column.get();
+        const auto& character_sets = assert_cast<const 
ColumnString&>(*character_set_nested);
+        const NullMap* input_null_map =
+                input_nullable ? &input_nullable->get_null_map_data() : 
nullptr;
+        const NullMap* character_set_null_map =
+                character_set_nullable ? 
&character_set_nullable->get_null_map_data() : nullptr;
+        const bool has_nullable = input_null_map != nullptr || 
character_set_null_map != nullptr;
+        auto result_column = create_result_column();
+        if constexpr (Encode) {
+            result_column->get_data().reserve(input_rows_count);
+        } else {
+            result_column->reserve(input_rows_count);
+        }
+        ColumnUInt8::MutablePtr result_null_column;
+        if (has_nullable) {
+            result_null_column = ColumnUInt8::create(input_rows_count, 0);
+        }
+        // Varbinary owns out-of-line values in an arena and inlines small 
values. Share one
+        // tracked scratch buffer across rows so that its capacity is not 
retained per row.
+        ColumnString::Chars scratch;
+        std::vector<char16_t> utf16_scratch;
+        CharacterSet constant_character_set = CharacterSet::UTF_8;
+        if (character_set_is_const && input_rows_count != 0 &&
+            !(character_set_null_map && (*character_set_null_map)[0])) {
+            RETURN_IF_ERROR(
+                    parse_character_set(character_sets.get_data_at(0), 
constant_character_set));
+        }
+        const bool copy_bytes = Encode && (constant_character_set == 
CharacterSet::US_ASCII ||
+                                           constant_character_set == 
CharacterSet::UTF_8);
+
+        for (size_t row = 0; row < input_rows_count; ++row) {
+            const size_t input_index = index_check_const(row, input_is_const);
+            const bool input_is_null = input_null_map && 
(*input_null_map)[input_index];
+            const bool character_set_is_null =
+                    character_set_null_map && (*character_set_null_map)[0];
+            if (input_is_null || character_set_is_null) {
+                result_column->insert_default();
+                result_null_column->get_data()[row] = 1;
+                continue;
+            }
+
+            const StringRef input = input_values->get_data_at(input_index);
+            if constexpr (Encode) {
+                RETURN_IF_ERROR(append_encoded(input, constant_character_set, 
copy_bytes,
+                                               *result_column, scratch, 
utf16_scratch));
+            } else {
+                // Write straight into the result column, including when the 
buffer grows.
+                auto& chars = result_column->get_chars();
+                RETURN_IF_ERROR(convert_input(input, constant_character_set, 
utf16_scratch, chars));
+                result_column->get_offsets().push_back(chars.size());
+            }
+        }
+
+        if (has_nullable) {
+            block.replace_by_position(result,
+                                      
ColumnNullable::create(std::move(result_column),
+                                                             
std::move(result_null_column)));
+        } else {
+            block.replace_by_position(result, std::move(result_column));
+        }
+        return Status::OK();
+    }
+
+private:
+    using ResultColumn = std::conditional_t<Encode, ColumnVarbinary, 
ColumnString>;
+
+    static const auto* get_input_values(const IColumn* input_nested) {
+        if constexpr (Encode) {
+            return &assert_cast<const ColumnString&>(*input_nested);
+        } else {
+            return input_nested;
+        }
+    }
+
+    static Status insert_validated_bytes(StringRef input, CharacterSet 
character_set,
+                                         ColumnVarbinary& output) {
+        const std::string_view character_set_name = 
charset_name(character_set);
+        if (input.size > 
static_cast<size_t>(std::numeric_limits<int32_t>::max())) {
+            return reject_too_large(character_set_name);
+        }
+        if (input.size != 0) {
+            const simdutf::result validation =
+                    character_set == CharacterSet::US_ASCII
+                            ? simdutf::validate_ascii_with_errors(input.data, 
input.size)
+                            : simdutf::validate_utf8_with_errors(input.data, 
input.size);
+            if (validation.error != simdutf::SUCCESS) {
+                return conversion_error(character_set_name, validation.error);
+            }
+        }
+        output.insert_data(input.data, input.size);
+        return Status::OK();
+    }
+
+    static Status append_encoded(StringRef input, CharacterSet character_set, 
bool copy_bytes,
+                                 ColumnVarbinary& output, ColumnString::Chars& 
scratch,
+                                 std::vector<char16_t>& utf16_scratch) {
+        if (copy_bytes) {
+            return insert_validated_bytes(input, character_set, output);
+        }
+        scratch.clear();
+        RETURN_IF_ERROR(convert_input(input, character_set, utf16_scratch, 
scratch));
+        output.insert_data(reinterpret_cast<const char*>(scratch.data()), 
scratch.size());
+        return Status::OK();
+    }
+
+    static typename ResultColumn::MutablePtr create_result_column() {
+        return ResultColumn::create();
+    }
+
+    static std::string_view charset_name(CharacterSet character_set) {
+        return SUPPORTED_CHARACTER_SETS[static_cast<size_t>(character_set)];
+    }
+
+    static Status convert_input(StringRef input, CharacterSet character_set,
+                                std::vector<char16_t>& utf16_scratch,
+                                ColumnString::Chars& converted) {
+        if (input.size == 0) {
+            return Status::OK();
+        }
+        const std::string_view character_set_name = 
charset_name(character_set);
+        if (input.size > 
static_cast<size_t>(std::numeric_limits<int32_t>::max())) {
+            return reject_too_large(character_set_name);
+        }
+        if constexpr (Encode) {
+            return encode_input(input, character_set, character_set_name, 
utf16_scratch, converted);
+        } else {
+            return decode_input(input, character_set, character_set_name, 
utf16_scratch, converted);
+        }
+    }
+
+    static Status encode_input(StringRef input, CharacterSet character_set,
+                               std::string_view character_set_name,
+                               std::vector<char16_t>& utf16_scratch, 
ColumnString::Chars& output) {
+        switch (character_set) {
+        case CharacterSet::US_ASCII:
+            return copy_validated(input, character_set_name,
+                                  
simdutf::validate_ascii_with_errors(input.data, input.size),
+                                  output);
+        case CharacterSet::UTF_8:
+            return copy_validated(input, character_set_name,
+                                  
simdutf::validate_utf8_with_errors(input.data, input.size),
+                                  output);
+        case CharacterSet::ISO_8859_1:
+            return encode_latin1(input, character_set_name, output);
+        case CharacterSet::UTF_16BE:
+            return encode_utf16(input, character_set_name, false, false, 
utf16_scratch, output);
+        case CharacterSet::UTF_16LE:
+            return encode_utf16(input, character_set_name, true, false, 
utf16_scratch, output);
+        case CharacterSet::UTF_16:
+            // Java's UTF-16 encoder always emits a big-endian BOM.
+            return encode_utf16(input, character_set_name, false, true, 
utf16_scratch, output);
+        default:
+            return Status::InvalidArgument("Unsupported character set '{}'", 
character_set_name);
+        }
+    }
+
+    static Status decode_input(StringRef input, CharacterSet character_set,
+                               std::string_view character_set_name,
+                               std::vector<char16_t>& utf16_scratch, 
ColumnString::Chars& output) {
+        switch (character_set) {
+        case CharacterSet::US_ASCII:
+            return copy_validated(input, character_set_name,
+                                  
simdutf::validate_ascii_with_errors(input.data, input.size),
+                                  output);
+        case CharacterSet::UTF_8:
+            return copy_validated(input, character_set_name,
+                                  
simdutf::validate_utf8_with_errors(input.data, input.size),
+                                  output);
+        case CharacterSet::ISO_8859_1:
+            return decode_latin1(input, character_set_name, output);
+        case CharacterSet::UTF_16BE:
+            return decode_utf16(input, character_set_name, false, 
utf16_scratch, output);
+        case CharacterSet::UTF_16LE:
+            return decode_utf16(input, character_set_name, true, 
utf16_scratch, output);
+        case CharacterSet::UTF_16:
+            return decode_utf16_with_bom(input, character_set_name, 
utf16_scratch, output);
+        default:
+            return Status::InvalidArgument("Unsupported character set '{}'", 
character_set_name);
+        }
+    }
+
+    static Status encode_latin1(StringRef input, std::string_view 
character_set_name,
+                                ColumnString::Chars& output) {
+        const size_t start = output.size();
+        char* dest = reserve_output(output, input.size);
+        const size_t written = simdutf::convert_utf8_to_latin1(input.data, 
input.size, dest);
+        if (written == 0) {
+            const simdutf::result detail =
+                    simdutf::convert_utf8_to_latin1_with_errors(input.data, 
input.size, dest);
+            output.resize(start);
+            const simdutf::error_code error =
+                    detail.error == simdutf::SUCCESS ? simdutf::OTHER : 
detail.error;
+            return conversion_error(character_set_name, error);
+        }
+        output.resize(start + written);
+        return Status::OK();
+    }
+
+    static Status decode_latin1(StringRef input, std::string_view 
character_set_name,
+                                ColumnString::Chars& output) {
+        const size_t need = simdutf::utf8_length_from_latin1(input.data, 
input.size);
+        char* dest = reserve_output(output, need);
+        const size_t written = simdutf::convert_latin1_to_utf8(input.data, 
input.size, dest);
+        if (written != need) {
+            output.resize(output.size() - need);
+            return conversion_error(character_set_name, simdutf::OTHER);
+        }
+        return Status::OK();
+    }
+
+    static Status encode_utf16(StringRef input, std::string_view 
character_set_name,
+                               bool little_endian, bool write_bom,
+                               std::vector<char16_t>& utf16_scratch, 
ColumnString::Chars& output) {
+        utf16_scratch.resize(input.size);
+        const simdutf::result result =
+                little_endian ? 
simdutf::convert_utf8_to_utf16le_with_errors(input.data, input.size,
+                                                                             
utf16_scratch.data())
+                              : 
simdutf::convert_utf8_to_utf16be_with_errors(input.data, input.size,
+                                                                             
utf16_scratch.data());
+        if (result.error != simdutf::SUCCESS) {
+            return conversion_error(character_set_name, result.error);
+        }
+        const size_t payload_bytes = result.count * sizeof(char16_t);
+        char* dest = reserve_output(output, payload_bytes + (write_bom ? 2 : 
0));
+        if (write_bom) {
+            auto* bytes = reinterpret_cast<uint8_t*>(dest);
+            bytes[0] = 0xFE;
+            bytes[1] = 0xFF;
+            dest += 2;
+        }
+        memcpy(dest, utf16_scratch.data(), payload_bytes);
+        return Status::OK();
+    }
+
+    // Java's UTF-16 decoder honors either BOM and defaults to big endian 
without one.
+    static Status decode_utf16_with_bom(StringRef input, std::string_view 
character_set_name,
+                                        std::vector<char16_t>& utf16_scratch,
+                                        ColumnString::Chars& output) {
+        if (input.size < 2) {
+            return conversion_error(character_set_name, simdutf::TOO_SHORT);
+        }
+        const auto first = static_cast<uint8_t>(input.data[0]);
+        const auto second = static_cast<uint8_t>(input.data[1]);
+        bool little_endian = false;
+        if (first == 0xFE && second == 0xFF) {
+            input = input.substring(2);
+        } else if (first == 0xFF && second == 0xFE) {
+            input = input.substring(2);
+            little_endian = true;
+        }
+        if (input.size == 0) {
+            return Status::OK();
+        }
+        return decode_utf16(input, character_set_name, little_endian, 
utf16_scratch, output);
+    }
+
+    static Status decode_utf16(StringRef input, std::string_view 
character_set_name,
+                               bool little_endian, std::vector<char16_t>& 
utf16_scratch,
+                               ColumnString::Chars& output) {
+        if (input.size % 2 != 0) {
+            return conversion_error(character_set_name, simdutf::TOO_SHORT);
+        }
+        const size_t units = input.size / 2;
+        if (units > (std::numeric_limits<size_t>::max() / 3)) {
+            return reject_too_large(character_set_name);
+        }
+        const char16_t* units_ptr = utf16_units(input, utf16_scratch);
+        const size_t start = output.size();
+        char* dest = reserve_output(output, units * 3);
+        const simdutf::result result =
+                little_endian
+                        ? 
simdutf::convert_utf16le_to_utf8_with_errors(units_ptr, units, dest)
+                        : 
simdutf::convert_utf16be_to_utf8_with_errors(units_ptr, units, dest);
+        if (result.error != simdutf::SUCCESS) {
+            output.resize(start);
+            return conversion_error(character_set_name, result.error);
+        }
+        output.resize(start + result.count);
+        return Status::OK();
+    }
+};
+
+using FunctionEncode = FunctionCharacterEncoding<true>;
+using FunctionDecode = FunctionCharacterEncoding<false>;
+
+} // namespace
+
+void register_function_character_encoding(SimpleFunctionFactory& factory) {
+    factory.register_function<FunctionEncode>();
+    factory.register_function<FunctionDecode>();
+}
+
+} // namespace doris
diff --git a/be/src/exprs/function/function_compress.cpp 
b/be/src/exprs/function/function_compress.cpp
index ba727aac7d0..cc537d77e3b 100644
--- a/be/src/exprs/function/function_compress.cpp
+++ b/be/src/exprs/function/function_compress.cpp
@@ -14,6 +14,7 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
+#include <gen_cpp/segment_v2.pb.h>
 #include <glog/logging.h>
 
 #include <array>
diff --git a/be/src/exprs/function/function_regexp.cpp 
b/be/src/exprs/function/function_regexp.cpp
index f7f1a952953..a791e975c66 100644
--- a/be/src/exprs/function/function_regexp.cpp
+++ b/be/src/exprs/function/function_regexp.cpp
@@ -53,6 +53,7 @@
 #include "exprs/function/simple_function_factory.h"
 #include "exprs/function_context.h"
 #include "exprs/string_functions.h"
+#include "runtime/runtime_state.h"
 
 namespace doris {
 
diff --git a/be/src/exprs/function/function_search.h 
b/be/src/exprs/function/function_search.h
index df417e0bca9..29d817d0c38 100644
--- a/be/src/exprs/function/function_search.h
+++ b/be/src/exprs/function/function_search.h
@@ -17,7 +17,15 @@
 
 #pragma once
 
+// CLucene's conversion warnings must not depend on the unity include order.
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#endif
 #include <CLucene.h>
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
 #include <gen_cpp/Exprs_types.h>
 
 #include <map>
diff --git a/be/src/exprs/function/simple_function_factory.h 
b/be/src/exprs/function/simple_function_factory.h
index 5dd09e0847d..60d5bb14827 100644
--- a/be/src/exprs/function/simple_function_factory.h
+++ b/be/src/exprs/function/simple_function_factory.h
@@ -123,6 +123,7 @@ void register_function_binary(SimpleFunctionFactory& 
factory);
 void register_function_levenshtein(SimpleFunctionFactory& factory);
 void register_function_hamming_distance(SimpleFunctionFactory& factory);
 void register_function_soundex(SimpleFunctionFactory& factory);
+void register_function_character_encoding(SimpleFunctionFactory& factory);
 
 #if defined(BE_TEST) && !defined(BE_BENCHMARK)
 void register_function_throw_exception(SimpleFunctionFactory& factory);
@@ -366,6 +367,7 @@ public:
             register_function_levenshtein(instance);
             register_function_hamming_distance(instance);
             register_function_soundex(instance);
+            register_function_character_encoding(instance);
             register_function_json_transform(instance);
             register_function_json_hash(instance);
 #if defined(BE_TEST) && !defined(BE_BENCHMARK)
diff --git a/be/test/exprs/function/function_character_encoding_test.cpp 
b/be/test/exprs/function/function_character_encoding_test.cpp
new file mode 100644
index 00000000000..bf9f1fdd87c
--- /dev/null
+++ b/be/test/exprs/function/function_character_encoding_test.cpp
@@ -0,0 +1,263 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <string>
+#include <string_view>
+
+#include "core/data_type/data_type_string.h"
+#include "core/data_type/data_type_varbinary.h"
+#include "exprs/function/function_test_util.h"
+
+namespace doris {
+
+using namespace ut_type;
+
+template <typename ReturnType>
+void check_character_encoding(const std::string& function_name, PrimitiveType 
input_type,
+                              const DataSet& data_set) {
+    for (bool input_is_const : {false, true}) {
+        for (const auto& line : data_set) {
+            InputTypeSet input_types;
+            if (input_is_const) {
+                input_types.emplace_back(Consted {input_type});
+            } else {
+                input_types.emplace_back(input_type);
+            }
+            input_types.emplace_back(Consted {PrimitiveType::TYPE_VARCHAR});
+            ASSERT_TRUE(
+                    (check_function<ReturnType, true>(function_name, 
input_types, {line}).ok()));
+        }
+    }
+}
+
+TEST(function_character_encoding_test, encode_supported_charsets) {
+    // The UTF-16 byte pairs 0x4E2D and 0x2D4E are "N-" and "-N" as raw bytes.
+    DataSet data_set = {
+            {{std::string("A"), std::string("US-ASCII")}, VARBINARY("A")},
+            {{std::string(""), std::string("US-ASCII")}, VARBINARY("")},
+            {{std::string("A\0B", 3), std::string("US-ASCII")},
+             VARBINARY(std::string_view("A\0B", 3))},
+            {{std::string("é"), std::string("ISO-8859-1")}, VARBINARY("\xE9")},
+            {{std::string("中"), std::string("UTF-8")}, 
VARBINARY("\xE4\xB8\xAD")},
+            {{std::string(""), std::string("UTF-8")}, VARBINARY("")},
+            {{std::string("中"), std::string("UTF-16BE")}, VARBINARY("N-")},
+            {{std::string("中"), std::string("UTF-16LE")}, VARBINARY("-N")},
+            {{std::string("中"), std::string("UTF-16")}, 
VARBINARY("\xFE\xFF\x4E\x2D")},
+            {{std::string("😀"), std::string("utf-16be")},
+             VARBINARY(std::string_view("\xD8\x3D\xDE\x00", 4))},
+            {{std::string("A\0中", 5), std::string("UTF-8")},
+             VARBINARY(std::string_view("A\0\xE4\xB8\xAD", 5))},
+            {{std::string(""), std::string("UTF-16")}, VARBINARY("")},
+            {{Null(), std::string("UTF-8")}, Null()},
+            {{std::string("text"), Null()}, Null()},
+    };
+
+    check_character_encoding<DataTypeVarbinary>("encode", 
PrimitiveType::TYPE_VARCHAR, data_set);
+}
+
+TEST(function_character_encoding_test, decode_supported_charsets) {
+    // The UTF-16 byte pairs 0x4E2D and 0x2D4E are "N-" and "-N" as raw bytes.
+    DataSet data_set = {
+            {{VARBINARY("A"), std::string("US-ASCII")}, std::string("A")},
+            {{VARBINARY("\xE9"), std::string("ISO-8859-1")}, std::string("é")},
+            {{VARBINARY("\xE4\xB8\xAD"), std::string("UTF-8")}, 
std::string("中")},
+            {{VARBINARY("N-"), std::string("UTF-16BE")}, std::string("中")},
+            {{VARBINARY("-N"), std::string("UTF-16LE")}, std::string("中")},
+            {{VARBINARY("\xFE\xFF\x4E\x2D"), std::string("UTF-16")}, 
std::string("中")},
+            {{VARBINARY("\xFF\xFE\x2D\x4E"), std::string("utf-16")}, 
std::string("中")},
+            {{VARBINARY("N-"), std::string("UTF-16")}, std::string("中")},
+            {{VARBINARY("\xFE\xFF"), std::string("UTF-16")}, std::string("")},
+            {{VARBINARY(std::string_view("\xD8\x3D\xDE\x00", 4)), 
std::string("UTF-16BE")},
+             std::string("😀")},
+            {{VARBINARY(std::string_view("A\0\xE4\xB8\xAD", 5)), 
std::string("UTF-8")},
+             std::string("A\0中", 5)},
+            {{VARBINARY(""), std::string("UTF-16")}, std::string("")},
+            {{Null(), std::string("UTF-8")}, Null()},
+            {{VARBINARY("text"), Null()}, Null()},
+    };
+
+    check_character_encoding<DataTypeString>("decode", 
PrimitiveType::TYPE_VARBINARY, data_set);
+}
+
+TEST(function_character_encoding_test, rejects_invalid_conversions) {
+    {
+        InputTypeSet input_types = {PrimitiveType::TYPE_VARCHAR,
+                                    Consted {PrimitiveType::TYPE_VARCHAR}};
+        DataSet data_set = {
+                {{std::string("text"), std::string("GBK")}, VARBINARY("")},
+        };
+
+        Status status = check_function<DataTypeVarbinary, true>("encode", 
input_types, data_set, -1,
+                                                                -1, true);
+        ASSERT_TRUE(status.is<ErrorCode::INVALID_ARGUMENT>()) << status;
+        EXPECT_NE(status.to_string().find("Unsupported character set"), 
std::string::npos);
+    }
+
+    {
+        InputTypeSet input_types = {PrimitiveType::TYPE_VARCHAR,
+                                    Consted {PrimitiveType::TYPE_VARCHAR}};
+        DataSet data_set = {
+                {{std::string("中"), std::string("US-ASCII")}, VARBINARY("")},
+        };
+
+        Status status = check_function<DataTypeVarbinary, true>("encode", 
input_types, data_set, -1,
+                                                                -1, true);
+        ASSERT_TRUE(status.is<ErrorCode::INVALID_ARGUMENT>()) << status;
+        EXPECT_NE(status.to_string().find("Character conversion using 
'US-ASCII' failed"),
+                  std::string::npos);
+    }
+
+    {
+        InputTypeSet input_types = {PrimitiveType::TYPE_VARBINARY,
+                                    Consted {PrimitiveType::TYPE_VARCHAR}};
+        DataSet data_set = {
+                {{VARBINARY("\xE4\xB8"), std::string("UTF-8")}, 
std::string("")},
+        };
+
+        Status status =
+                check_function<DataTypeString, true>("decode", input_types, 
data_set, -1, -1, true);
+        ASSERT_TRUE(status.is<ErrorCode::INVALID_ARGUMENT>()) << status;
+        EXPECT_NE(status.to_string().find("Character conversion using 'UTF-8' 
failed"),
+                  std::string::npos);
+    }
+
+    {
+        InputTypeSet input_types = {PrimitiveType::TYPE_VARCHAR,
+                                    Consted {PrimitiveType::TYPE_VARCHAR}};
+        DataSet data_set = {
+                {{std::string("\xE4\xB8", 2), std::string("UTF-8")}, 
VARBINARY("")},
+        };
+
+        Status status = check_function<DataTypeVarbinary, true>("encode", 
input_types, data_set, -1,
+                                                                -1, true);
+        ASSERT_TRUE(status.is<ErrorCode::INVALID_ARGUMENT>()) << status;
+        EXPECT_NE(status.to_string().find("Character conversion using 'UTF-8' 
failed"),
+                  std::string::npos);
+    }
+}
+
+TEST(function_character_encoding_test, 
invalid_constant_character_set_precedes_null_input) {
+    DataSet encode_data = {
+            {{Null(), std::string("GBK")}, Null()},
+    };
+    Status encode_status = check_function<DataTypeVarbinary, true>(
+            "encode", {PrimitiveType::TYPE_VARCHAR, Consted 
{PrimitiveType::TYPE_VARCHAR}},
+            encode_data, -1, -1, true);
+    ASSERT_TRUE(encode_status.is<ErrorCode::INVALID_ARGUMENT>()) << 
encode_status;
+    EXPECT_NE(encode_status.to_string().find("Unsupported character set"), 
std::string::npos);
+
+    DataSet decode_data = {
+            {{Null(), std::string("GBK")}, Null()},
+    };
+    Status decode_status = check_function<DataTypeString, true>(
+            "decode", {PrimitiveType::TYPE_VARBINARY, Consted 
{PrimitiveType::TYPE_VARCHAR}},
+            decode_data, -1, -1, true);
+    ASSERT_TRUE(decode_status.is<ErrorCode::INVALID_ARGUMENT>()) << 
decode_status;
+    EXPECT_NE(decode_status.to_string().find("Unsupported character set"), 
std::string::npos);
+}
+
+TEST(function_character_encoding_test, requires_constant_character_set) {
+    DataSet encode_data = {
+            {{std::string("A"), std::string("UTF-8")}, VARBINARY("A")},
+    };
+    Status encode_status = check_function<DataTypeVarbinary, true>(
+            "encode", {PrimitiveType::TYPE_VARCHAR, 
PrimitiveType::TYPE_VARCHAR}, encode_data, -1,
+            -1, true);
+    ASSERT_TRUE(encode_status.is<ErrorCode::INVALID_ARGUMENT>()) << 
encode_status;
+    EXPECT_NE(encode_status.to_string().find("must be constant"), 
std::string::npos);
+
+    DataSet decode_data = {
+            {{VARBINARY("A"), std::string("UTF-8")}, std::string("A")},
+    };
+    Status decode_status = check_function<DataTypeString, true>(
+            "decode", {PrimitiveType::TYPE_VARBINARY, 
PrimitiveType::TYPE_VARCHAR}, decode_data, -1,
+            -1, true);
+    ASSERT_TRUE(decode_status.is<ErrorCode::INVALID_ARGUMENT>()) << 
decode_status;
+    EXPECT_NE(decode_status.to_string().find("must be constant"), 
std::string::npos);
+}
+
+TEST(function_character_encoding_test, streaming_boundaries_and_row_reuse) {
+    // Cross the pivot boundary and force output expansion, including pending 
surrogate pairs.
+    for (size_t length : {1, 15, 1023, 1024, 1025, 65535}) {
+        std::string ascii(length, 'A');
+        std::string utf16;
+        for (size_t i = 0; i < length; ++i) {
+            utf16.append("\0A", 2);
+        }
+        const std::string utf16_bom = std::string("\xFE\xFF", 2) + utf16;
+        const std::string supplementary = ascii + "😀";
+        const std::string supplementary_utf16 = utf16 + 
std::string("\xD8\x3D\xDE\0", 4);
+        DataSet encoded = {
+                {{ascii, std::string("UTF-16BE")}, VARBINARY(utf16)},
+                {{supplementary, std::string("UTF-16BE")}, 
VARBINARY(supplementary_utf16)},
+                {{Null(), std::string("UTF-16BE")}, Null()},
+                {{std::string(""), std::string("UTF-16BE")}, VARBINARY("")},
+                {{ascii, std::string("UTF-16")}, VARBINARY(utf16_bom)},
+                {{std::string("A"), std::string("UTF-16BE")},
+                 VARBINARY(std::string_view("\0A", 2))},
+        };
+        check_character_encoding<DataTypeVarbinary>("encode", 
PrimitiveType::TYPE_VARCHAR, encoded);
+
+        std::string latin1(length, '\xE9');
+        std::string expanded;
+        for (size_t i = 0; i < length; ++i) {
+            expanded += "é";
+        }
+        DataSet decoded = {
+                {{VARBINARY(latin1), std::string("ISO-8859-1")}, expanded},
+                {{VARBINARY(supplementary_utf16), std::string("UTF-16BE")}, 
supplementary},
+                {{Null(), std::string("ISO-8859-1")}, Null()},
+                {{VARBINARY(""), std::string("ISO-8859-1")}, std::string("")},
+                {{VARBINARY(utf16_bom), std::string("UTF-16")}, ascii},
+                {{VARBINARY("\xFF\xFE\x2D\x4E"), std::string("UTF-16")}, 
std::string("中")},
+                {{VARBINARY("\xFE\xFF"), std::string("UTF-16")}, 
std::string("")},
+                {{VARBINARY("\xE9"), std::string("ISO-8859-1")}, 
std::string("é")},
+        };
+        check_character_encoding<DataTypeString>("decode", 
PrimitiveType::TYPE_VARBINARY, decoded);
+    }
+}
+
+TEST(function_character_encoding_test, rejects_invalid_input_after_streaming) {
+    const std::string invalid_utf8 = std::string(4096, 'A') + "\xE4\xB8";
+    const std::string unrepresentable = std::string(4096, 'A') + "中";
+    for (const auto& input : {invalid_utf8, unrepresentable}) {
+        DataSet data_set = {{{input, std::string("US-ASCII")}, VARBINARY("")}};
+        Status status = check_function<DataTypeVarbinary, true>(
+                "encode", {PrimitiveType::TYPE_VARCHAR, Consted 
{PrimitiveType::TYPE_VARCHAR}},
+                data_set, -1, -1, true);
+        ASSERT_TRUE(status.is<ErrorCode::INVALID_ARGUMENT>()) << status;
+    }
+    const std::string expanding_invalid_utf8 = std::string(50000, 'A') + 
"\xE4\xB8";
+    DataSet invalid_encode = {{{expanding_invalid_utf8, 
std::string("UTF-16BE")}, VARBINARY("")}};
+    Status encode_status = check_function<DataTypeVarbinary, true>(
+            "encode", {PrimitiveType::TYPE_VARCHAR, Consted 
{PrimitiveType::TYPE_VARCHAR}},
+            invalid_encode, -1, -1, true);
+    ASSERT_TRUE(encode_status.is<ErrorCode::INVALID_ARGUMENT>()) << 
encode_status;
+
+    std::string invalid_utf16;
+    for (size_t i = 0; i < 25000; ++i) {
+        invalid_utf16 += "N-"; // The UTF-16BE byte pair for 中.
+    }
+    invalid_utf16.append("\xD8\x3D", 2); // An unpaired high surrogate after 
several pivot fills.
+    DataSet data_set = {{{VARBINARY(invalid_utf16), std::string("UTF-16BE")}, 
std::string("")}};
+    Status status = check_function<DataTypeString, true>(
+            "decode", {PrimitiveType::TYPE_VARBINARY, Consted 
{PrimitiveType::TYPE_VARCHAR}},
+            data_set, -1, -1, true);
+    ASSERT_TRUE(status.is<ErrorCode::INVALID_ARGUMENT>()) << status;
+}
+
+} // namespace doris
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
index bf5cd8dec3c..be0802713db 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
@@ -184,6 +184,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.DaysAdd;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysDiff;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysSub;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Dceil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Decode;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.DecodeAsVarchar;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.DeduplicateMap;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Degrees;
@@ -201,6 +202,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Dsqrt;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.E;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.ElementAt;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Elt;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Encode;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.EncodeAsBigInt;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.EncodeAsInt;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.EncodeAsLargeInt;
@@ -788,6 +790,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(DaysDiff.class, "days_diff"),
             scalar(DaysSub.class, "days_sub", "date_sub", "subdate"),
             scalar(Dceil.class, "dceil"),
+            scalar(Decode.class, "decode"),
             scalar(DecodeAsVarchar.class, "decode_as_varchar"),
             scalar(DeduplicateMap.class, "deduplicate_map"),
             scalar(Degrees.class, "degrees"),
@@ -807,6 +810,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(ElementAt.class, "element_at", "struct_element"),
             scalar(Elt.class, "elt"),
             scalar(Embed.class, "embed"),
+            scalar(Encode.class, "encode"),
             scalar(EncodeAsSmallInt.class, "encode_as_smallint"),
             scalar(EncodeAsInt.class, "encode_as_int"),
             scalar(EncodeAsBigInt.class, "encode_as_bigint"),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmetic.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmetic.java
index f1ab9c1a676..68e18fee9c0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmetic.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmetic.java
@@ -40,6 +40,7 @@ import 
org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.TimeStampNsLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.VarBinaryLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
 import org.apache.doris.nereids.types.ArrayType;
 
@@ -52,7 +53,9 @@ import java.math.BigInteger;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
 import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
 import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
 import java.nio.charset.CodingErrorAction;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
@@ -70,6 +73,8 @@ import java.util.regex.Pattern;
  */
 public class StringArithmetic {
     private static final long MAX_DAMERAU_LEVENSHTEIN_MATRIX_CELLS = 16L * 
1024L * 1024L;
+    private static final List<String> SUPPORTED_CHARACTER_SETS = 
ImmutableList.of(
+            "US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16BE", "UTF-16LE", 
"UTF-16");
 
     private static Literal castStringLikeLiteral(StringLikeLiteral first, 
String value) {
         if (first instanceof StringLiteral) {
@@ -1183,6 +1188,67 @@ public class StringArithmetic {
         }
     }
 
+    /**
+     * Executable arithmetic function encode
+     */
+    @ExecFunction(name = "encode")
+    public static Expression encode(StringLikeLiteral source, 
StringLikeLiteral characterSet) {
+        Charset charset = supportedCharacterSet(characterSet.getValue());
+        try {
+            ByteBuffer encoded = charset.newEncoder()
+                    .onMalformedInput(CodingErrorAction.REPORT)
+                    .onUnmappableCharacter(CodingErrorAction.REPORT)
+                    .encode(CharBuffer.wrap(source.getValue()));
+            byte[] bytes = new byte[encoded.remaining()];
+            encoded.get(bytes);
+            return new VarBinaryLiteral(bytes);
+        } catch (CharacterCodingException e) {
+            throw new IllegalArgumentException("Failed to encode value using " 
+ characterSet.getValue(), e);
+        }
+    }
+
+    /**
+     * Executable arithmetic function decode
+     */
+    @ExecFunction(name = "decode")
+    public static Expression decode(VarBinaryLiteral binary, StringLikeLiteral 
characterSet) {
+        Charset charset = supportedCharacterSet(characterSet.getValue());
+        try {
+            CharBuffer decoded = charset.newDecoder()
+                    .onMalformedInput(CodingErrorAction.REPORT)
+                    .onUnmappableCharacter(CodingErrorAction.REPORT)
+                    .decode(ByteBuffer.wrap((byte[]) binary.getValue()));
+            return new StringLiteral(decoded.toString());
+        } catch (CharacterCodingException e) {
+            throw new IllegalArgumentException("Failed to decode value using " 
+ characterSet.getValue(), e);
+        }
+    }
+
+    private static Charset supportedCharacterSet(String name) {
+        for (String supportedCharacterSet : SUPPORTED_CHARACTER_SETS) {
+            if (equalsIgnoreAsciiCase(name, supportedCharacterSet)) {
+                return Charset.forName(supportedCharacterSet);
+            }
+        }
+        throw new IllegalArgumentException("Unsupported character set: " + 
name);
+    }
+
+    private static boolean equalsIgnoreAsciiCase(String value, String 
expected) {
+        if (value.length() != expected.length()) {
+            return false;
+        }
+        for (int i = 0; i < value.length(); i++) {
+            char current = value.charAt(i);
+            if (current >= 'a' && current <= 'z') {
+                current -= 'a' - 'A';
+            }
+            if (current != expected.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
      * Executable arithmetic functions append_trailing_char_if_absent
      */
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CharacterSetLiterals.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CharacterSetLiterals.java
new file mode 100644
index 00000000000..67c7cff0eca
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CharacterSetLiterals.java
@@ -0,0 +1,71 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/** Character sets accepted by encode and decode. */
+final class CharacterSetLiterals {
+    private static final List<String> SUPPORTED = ImmutableList.of(
+            "US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16BE", "UTF-16LE", 
"UTF-16");
+
+    private CharacterSetLiterals() {
+    }
+
+    static void checkSecondArgument(ScalarFunction function) {
+        Expression characterSet = function.getArgument(1);
+        if (characterSet.isNullLiteral()) {
+            return;
+        }
+        if (!(characterSet instanceof StringLikeLiteral)) {
+            throw new AnalysisException("the second argument of function "
+                    + function.getName() + " must be a string literal: " + 
function.toSql());
+        }
+        String value = ((StringLikeLiteral) characterSet).getValue();
+        for (String supported : SUPPORTED) {
+            if (equalsIgnoreAsciiCase(value, supported)) {
+                return;
+            }
+        }
+        throw new AnalysisException("Unsupported character set '" + value
+                + "'. Supported character sets are US-ASCII, ISO-8859-1, 
UTF-8, "
+                + "UTF-16BE, UTF-16LE, and UTF-16");
+    }
+
+    private static boolean equalsIgnoreAsciiCase(String value, String 
expected) {
+        if (value.length() != expected.length()) {
+            return false;
+        }
+        for (int i = 0; i < value.length(); i++) {
+            char current = value.charAt(i);
+            if (current >= 'a' && current <= 'z') {
+                current -= 'a' - 'A';
+            }
+            if (current != expected.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Decode.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Decode.java
new file mode 100644
index 00000000000..4cb1296e6dc
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Decode.java
@@ -0,0 +1,84 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
+import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.VarBinaryType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * ScalarFunction 'decode'. This class is generated by GenerateFunction.
+ */
+public class Decode extends ScalarFunction
+        implements BinaryExpression, ExplicitlyCastableSignature, 
PropagateNullable {
+
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            
FunctionSignature.ret(StringType.INSTANCE).args(VarBinaryType.INSTANCE, 
StringType.INSTANCE)
+    );
+
+    /**
+     * constructor with 2 arguments.
+     */
+    public Decode(Expression binary, Expression characterSet) {
+        super("decode", binary, characterSet);
+    }
+
+    /** constructor for withChildren and reuse signature */
+    private Decode(ScalarFunctionParams functionParams) {
+        super(functionParams);
+    }
+
+    @Override
+    public void checkLegalityBeforeTypeCoercion() {
+        CharacterSetLiterals.checkSecondArgument(this);
+    }
+
+    @Override
+    public void checkLegalityAfterRewrite() {
+        checkLegalityBeforeTypeCoercion();
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public Decode withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new Decode(getFunctionParams(children));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitDecode(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Encode.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Encode.java
new file mode 100644
index 00000000000..1bab3f65ed5
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Encode.java
@@ -0,0 +1,84 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
+import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.VarBinaryType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * ScalarFunction 'encode'. This class is generated by GenerateFunction.
+ */
+public class Encode extends ScalarFunction
+        implements BinaryExpression, ExplicitlyCastableSignature, 
PropagateNullable {
+
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            
FunctionSignature.ret(VarBinaryType.INSTANCE).args(StringType.INSTANCE, 
StringType.INSTANCE)
+    );
+
+    /**
+     * constructor with 2 arguments.
+     */
+    public Encode(Expression source, Expression characterSet) {
+        super("encode", source, characterSet);
+    }
+
+    /** constructor for withChildren and reuse signature */
+    private Encode(ScalarFunctionParams functionParams) {
+        super(functionParams);
+    }
+
+    @Override
+    public void checkLegalityBeforeTypeCoercion() {
+        CharacterSetLiterals.checkSecondArgument(this);
+    }
+
+    @Override
+    public void checkLegalityAfterRewrite() {
+        checkLegalityBeforeTypeCoercion();
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public Encode withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new Encode(getFunctionParams(children));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitEncode(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
index a3ffcf66889..a888b2af817 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
@@ -201,6 +201,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.DaysAdd;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysDiff;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysSub;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Dceil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Decode;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.DecodeAsVarchar;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Degrees;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Dexp;
@@ -217,6 +218,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Dsqrt;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.E;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.ElementAt;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Elt;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Encode;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.EncodeAsBigInt;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.EncodeAsInt;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.EncodeAsLargeInt;
@@ -1095,6 +1097,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(cutToFirstSignificantSubdomain, context);
     }
 
+    default R visitEncode(Encode encode, C context) {
+        return visitScalarFunction(encode, context);
+    }
+
     default R visitEncodeAsSmallInt(EncodeAsSmallInt encode, C context) {
         return visitScalarFunction(encode, context);
     }
@@ -1300,6 +1306,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(digitalMasking, context);
     }
 
+    default R visitDecode(Decode decode, C context) {
+        return visitScalarFunction(decode, context);
+    }
+
     default R visitDecodeAsVarchar(DecodeAsVarchar decode, C context) {
         return visitScalarFunction(decode, context);
     }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmeticTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmeticTest.java
index 22f0292f7c9..51bafdac632 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmeticTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmeticTest.java
@@ -19,6 +19,8 @@ package 
org.apache.doris.nereids.trees.expressions.functions.executable;
 
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.ExpressionEvaluator;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Decode;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Encode;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.UrlDecode;
 import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.FloatLiteral;
@@ -27,6 +29,7 @@ import 
org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.TimeStampNsLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.VarBinaryLiteral;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -96,11 +99,73 @@ class StringArithmeticTest {
         assertUrlDecodeValue("%EF%BF%BD", "�");
     }
 
+    @Test
+    void testEncodeFoldsSupportedCharsets() {
+        assertEncodeValue("A", "US-ASCII", new byte[] {0x41});
+        assertEncodeValue("é", "ISO-8859-1", new byte[] {(byte) 0xE9});
+        assertEncodeValue("中", "UTF-8", new byte[] {(byte) 0xE4, (byte) 0xB8, 
(byte) 0xAD});
+        assertEncodeValue("中", "UTF-16BE", new byte[] {0x4E, 0x2D});
+        assertEncodeValue("中", "UTF-16LE", new byte[] {0x2D, 0x4E});
+        assertEncodeValue("中", "utf-16", new byte[] {(byte) 0xFE, (byte) 0xFF, 
0x4E, 0x2D});
+        assertEncodeValue("😀", "UTF-16BE", new byte[] {(byte) 0xD8, 0x3D, 
(byte) 0xDE, 0x00});
+        assertEncodeValue("", "UTF-16", new byte[] {});
+    }
+
+    @Test
+    void testDecodeFoldsSupportedCharsets() {
+        assertDecodeValue(new byte[] {0x41}, "US-ASCII", "A");
+        assertDecodeValue(new byte[] {(byte) 0xE9}, "ISO-8859-1", "é");
+        assertDecodeValue(new byte[] {(byte) 0xE4, (byte) 0xB8, (byte) 0xAD}, 
"UTF-8", "中");
+        assertDecodeValue(new byte[] {0x4E, 0x2D}, "UTF-16BE", "中");
+        assertDecodeValue(new byte[] {0x2D, 0x4E}, "UTF-16LE", "中");
+        assertDecodeValue(new byte[] {(byte) 0xFE, (byte) 0xFF, 0x4E, 0x2D}, 
"UTF-16", "中");
+        assertDecodeValue(new byte[] {(byte) 0xFF, (byte) 0xFE, 0x2D, 0x4E}, 
"utf-16", "中");
+        assertDecodeValue(new byte[] {0x4E, 0x2D}, "UTF-16", "中");
+        assertDecodeValue(new byte[] {(byte) 0xFE, (byte) 0xFF}, "UTF-16", "");
+        assertDecodeValue(new byte[] {(byte) 0xD8, 0x3D, (byte) 0xDE, 0x00}, 
"UTF-16BE", "😀");
+        assertDecodeValue(new byte[] {}, "UTF-16", "");
+    }
+
+    @Test
+    void testInvalidCharacterConversionDoesNotFold() {
+        Encode unmappable = new Encode(new StringLiteral("中"), new 
StringLiteral("US-ASCII"));
+        Decode malformed = new Decode(new VarBinaryLiteral(new byte[] {(byte) 
0xE4, (byte) 0xB8}),
+                new StringLiteral("UTF-8"));
+        Encode unsupported = new Encode(new StringLiteral("text"), new 
StringLiteral("GBK"));
+
+        Assertions.assertSame(unmappable, 
ExpressionEvaluator.INSTANCE.eval(unmappable));
+        Assertions.assertSame(malformed, 
ExpressionEvaluator.INSTANCE.eval(malformed));
+        Assertions.assertSame(unsupported, 
ExpressionEvaluator.INSTANCE.eval(unsupported));
+    }
+
+    @Test
+    void testUnicodeCaseFoldedCharacterSetDoesNotFold() {
+        String unicodeCaseFoldedCharset = "U\u017F-ASCII"; // U+017F LATIN 
SMALL LETTER LONG S
+        Encode encode = new Encode(new StringLiteral("A"), new 
StringLiteral(unicodeCaseFoldedCharset));
+        Decode decode = new Decode(new VarBinaryLiteral(new byte[] {0x41}),
+                new StringLiteral(unicodeCaseFoldedCharset));
+
+        Assertions.assertSame(encode, 
ExpressionEvaluator.INSTANCE.eval(encode));
+        Assertions.assertSame(decode, 
ExpressionEvaluator.INSTANCE.eval(decode));
+    }
+
     private void assertUrlDecodeValue(String encoded, String expected) {
         Expression result = ExpressionEvaluator.INSTANCE.eval(new 
UrlDecode(new StringLiteral(encoded)));
         Assertions.assertEquals(expected, ((StringLikeLiteral) 
result).getValue());
     }
 
+    private void assertEncodeValue(String value, String characterSet, byte[] 
expected) {
+        Expression result = ExpressionEvaluator.INSTANCE.eval(
+                new Encode(new StringLiteral(value), new 
StringLiteral(characterSet)));
+        Assertions.assertArrayEquals(expected, (byte[]) ((VarBinaryLiteral) 
result).getValue());
+    }
+
+    private void assertDecodeValue(byte[] value, String characterSet, String 
expected) {
+        Expression result = ExpressionEvaluator.INSTANCE.eval(
+                new Decode(new VarBinaryLiteral(value), new 
StringLiteral(characterSet)));
+        Assertions.assertEquals(expected, ((StringLikeLiteral) 
result).getValue());
+    }
+
     @Test
     void testParseUrlQueryStopsAtFragment() {
         // The only '?' is inside the fragment, so the url has no query 
component.
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/EncodeDecodePlannerTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/EncodeDecodePlannerTest.java
new file mode 100644
index 00000000000..f498b32f5dd
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/EncodeDecodePlannerTest.java
@@ -0,0 +1,96 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.VarBinaryLiteral;
+import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.VarBinaryType;
+import org.apache.doris.nereids.util.MemoPatternMatchSupported;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.utframe.TestWithFeService;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class EncodeDecodePlannerTest extends TestWithFeService implements 
MemoPatternMatchSupported {
+
+    @Test
+    void testLiteralCallsFoldDuringPlanRewrite() {
+        VarBinaryLiteral encoded = new VarBinaryLiteral(new byte[] {0x4E, 
0x2D});
+        PlanChecker.from(connectContext)
+                .analyze("select encode('中', 'UTF-16BE')")
+                .rewrite()
+                .matches(logicalResultSink(
+                        logicalOneRowRelation().when(oneRow ->
+                                
oneRow.getProjects().get(0).child(0).equals(encoded))));
+
+        StringLiteral decoded = new StringLiteral("中");
+        PlanChecker.from(connectContext)
+                .analyze("select decode(X'E4B8AD', 'UTF-8')")
+                .rewrite()
+                .matches(logicalResultSink(
+                        logicalOneRowRelation().when(oneRow ->
+                                
oneRow.getProjects().get(0).child(0).equals(decoded))));
+    }
+
+    @Test
+    void testEncodeWithNullCharsetFoldsToNull() {
+        NullLiteral encoded = new NullLiteral(VarBinaryType.INSTANCE);
+        for (String sql : new String[] {
+                "select encode('hello', NULL)",
+                "select encode('', NULL)",
+                "select encode(NULL, NULL)"}) {
+            PlanChecker.from(connectContext)
+                    .analyze(sql)
+                    .rewrite()
+                    .matches(logicalResultSink(
+                            logicalOneRowRelation().when(oneRow ->
+                                    
oneRow.getProjects().get(0).child(0).equals(encoded))));
+        }
+    }
+
+    @Test
+    void testDecodeWithNullCharsetFoldsToNull() {
+        NullLiteral decoded = new NullLiteral(StringType.INSTANCE);
+        for (String sql : new String[] {
+                "select decode(X'6869', NULL)",
+                "select decode(X'', NULL)",
+                "select decode(NULL, NULL)"}) {
+            PlanChecker.from(connectContext)
+                    .analyze(sql)
+                    .rewrite()
+                    .matches(logicalResultSink(
+                            logicalOneRowRelation().when(oneRow ->
+                                    
oneRow.getProjects().get(0).child(0).equals(decoded))));
+        }
+    }
+
+    @Test
+    void testInvalidCharsetRejectedBeforeNullFolding() {
+        AnalysisException encodeError = 
Assertions.assertThrows(AnalysisException.class,
+                () -> PlanChecker.from(connectContext).analyze("select 
encode(NULL, 'GBK')"));
+        Assertions.assertTrue(encodeError.getMessage().contains("Unsupported 
character set"));
+
+        AnalysisException decodeError = 
Assertions.assertThrows(AnalysisException.class,
+                () -> PlanChecker.from(connectContext).analyze("select 
decode(NULL, 'GBK')"));
+        Assertions.assertTrue(decodeError.getMessage().contains("Unsupported 
character set"));
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/EncodeDecodeTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/EncodeDecodeTest.java
new file mode 100644
index 00000000000..6c671054deb
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/EncodeDecodeTest.java
@@ -0,0 +1,172 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.VarBinaryLiteral;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.VarBinaryType;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class EncodeDecodeTest {
+
+    @Test
+    public void testEncodeExpressionContract() {
+        StringLiteral source = new StringLiteral("hello");
+        StringLiteral characterSet = new StringLiteral("UTF-8");
+        Encode encode = new Encode(source, characterSet);
+
+        Assertions.assertEquals("encode", encode.getName());
+        Assertions.assertEquals(2, encode.arity());
+        Assertions.assertSame(source, encode.child(0));
+        Assertions.assertSame(characterSet, encode.child(1));
+
+        FunctionSignature signature = encode.getSignatures().get(0);
+        Assertions.assertEquals(VarBinaryType.INSTANCE, signature.returnType);
+        Assertions.assertEquals(StringType.INSTANCE, signature.getArgType(0));
+        Assertions.assertEquals(StringType.INSTANCE, signature.getArgType(1));
+
+        StringLiteral replacementSource = new StringLiteral("world");
+        StringLiteral replacementCharacterSet = new StringLiteral("UTF-16");
+        Encode rewritten = encode.withChildren(
+                ImmutableList.of(replacementSource, replacementCharacterSet));
+        Assertions.assertNotSame(encode, rewritten);
+        Assertions.assertSame(replacementSource, rewritten.child(0));
+        Assertions.assertSame(replacementCharacterSet, rewritten.child(1));
+        Assertions.assertThrows(IllegalArgumentException.class,
+                () -> 
encode.withChildren(ImmutableList.of(replacementSource)));
+    }
+
+    @Test
+    public void testDecodeExpressionContract() {
+        VarBinaryLiteral binary = new VarBinaryLiteral(new byte[] {0x68, 
0x69});
+        StringLiteral characterSet = new StringLiteral("UTF-8");
+        Decode decode = new Decode(binary, characterSet);
+
+        Assertions.assertEquals("decode", decode.getName());
+        Assertions.assertEquals(2, decode.arity());
+        Assertions.assertSame(binary, decode.child(0));
+        Assertions.assertSame(characterSet, decode.child(1));
+
+        FunctionSignature signature = decode.getSignatures().get(0);
+        Assertions.assertEquals(StringType.INSTANCE, signature.returnType);
+        Assertions.assertEquals(VarBinaryType.INSTANCE, 
signature.getArgType(0));
+        Assertions.assertEquals(StringType.INSTANCE, signature.getArgType(1));
+
+        VarBinaryLiteral replacementBinary = new VarBinaryLiteral(new byte[] 
{0x41});
+        StringLiteral replacementCharacterSet = new StringLiteral("US-ASCII");
+        Decode rewritten = decode.withChildren(
+                ImmutableList.of(replacementBinary, replacementCharacterSet));
+        Assertions.assertNotSame(decode, rewritten);
+        Assertions.assertSame(replacementBinary, rewritten.child(0));
+        Assertions.assertSame(replacementCharacterSet, rewritten.child(1));
+        Assertions.assertThrows(IllegalArgumentException.class,
+                () -> 
decode.withChildren(ImmutableList.of(replacementBinary)));
+    }
+
+    @Test
+    public void testVisitorDelegatesToScalarFunction() {
+        Encode encode = new Encode(new StringLiteral("hello"), new 
StringLiteral("UTF-8"));
+        Decode decode = new Decode(new VarBinaryLiteral(new byte[] {0x68, 
0x69}),
+                new StringLiteral("UTF-8"));
+        ExpressionVisitor<Expression, Void> visitor = new 
ExpressionVisitor<Expression, Void>() {
+            @Override
+            public Expression visit(Expression expression, Void context) {
+                return expression;
+            }
+        };
+
+        Assertions.assertSame(encode, encode.accept(visitor, null));
+        Assertions.assertSame(decode, decode.accept(visitor, null));
+    }
+
+    @Test
+    public void testCharacterSetMustBeConstant() {
+        SlotReference characterSetColumn = new SlotReference("charset", 
StringType.INSTANCE);
+        Encode encode = new Encode(new StringLiteral("hello"), 
characterSetColumn);
+        Decode decode = new Decode(new VarBinaryLiteral(new byte[] {0x68, 
0x69}),
+                characterSetColumn);
+
+        AnalysisException encodeException = Assertions.assertThrows(
+                AnalysisException.class, 
encode::checkLegalityBeforeTypeCoercion);
+        Assertions.assertTrue(encodeException.getMessage().contains(
+                "second argument of function encode must be a string 
literal"));
+        AnalysisException decodeException = Assertions.assertThrows(
+                AnalysisException.class, 
decode::checkLegalityBeforeTypeCoercion);
+        Assertions.assertTrue(decodeException.getMessage().contains(
+                "second argument of function decode must be a string 
literal"));
+
+        AnalysisException encodeUpper = 
Assertions.assertThrows(AnalysisException.class,
+                new Encode(new StringLiteral("hello"), new Upper(new 
StringLiteral("utf-8")))
+                        ::checkLegalityBeforeTypeCoercion);
+        Assertions.assertTrue(encodeUpper.getMessage().contains("must be a 
string literal"));
+
+        AnalysisException decodeUpper = 
Assertions.assertThrows(AnalysisException.class,
+                new Decode(new VarBinaryLiteral(new byte[] {0x68, 0x69}),
+                        new Upper(new 
StringLiteral("utf-8")))::checkLegalityBeforeTypeCoercion);
+        Assertions.assertTrue(decodeUpper.getMessage().contains("must be a 
string literal"));
+
+        Encode literalEncode = new Encode(new StringLiteral("hello"), new 
StringLiteral("utf-8"));
+        Decode literalDecode = new Decode(new VarBinaryLiteral(new byte[] 
{0x68, 0x69}),
+                new StringLiteral("UTF-8"));
+        
Assertions.assertDoesNotThrow(literalEncode::checkLegalityBeforeTypeCoercion);
+        
Assertions.assertDoesNotThrow(literalEncode::checkLegalityAfterRewrite);
+        
Assertions.assertDoesNotThrow(literalDecode::checkLegalityBeforeTypeCoercion);
+        
Assertions.assertDoesNotThrow(literalDecode::checkLegalityAfterRewrite);
+
+        AnalysisException unsupported = 
Assertions.assertThrows(AnalysisException.class,
+                new Encode(new StringLiteral("hello"), new 
StringLiteral("GBK"))
+                        ::checkLegalityBeforeTypeCoercion);
+        Assertions.assertTrue(unsupported.getMessage().contains("Unsupported 
character set"));
+    }
+
+    @Test
+    public void testNullCharsetIsAccepted() {
+        for (NullLiteral characterSet : new NullLiteral[] {
+                NullLiteral.INSTANCE, new NullLiteral(StringType.INSTANCE)}) {
+            Encode encode = new Encode(new StringLiteral("hello"), 
characterSet);
+            Decode decode = new Decode(new VarBinaryLiteral(new byte[] {0x68, 
0x69}), characterSet);
+            
Assertions.assertDoesNotThrow(encode::checkLegalityBeforeTypeCoercion);
+            Assertions.assertDoesNotThrow(encode::checkLegalityAfterRewrite);
+            
Assertions.assertDoesNotThrow(decode::checkLegalityBeforeTypeCoercion);
+            Assertions.assertDoesNotThrow(decode::checkLegalityAfterRewrite);
+        }
+    }
+
+    @Test
+    public void testNonStringCharsetLiteralIsRejected() {
+        Encode encode = new Encode(new StringLiteral("hello"), new 
IntegerLiteral(1));
+        Decode decode = new Decode(new VarBinaryLiteral(new byte[] {0x68, 
0x69}), new IntegerLiteral(1));
+        AnalysisException encodeError = Assertions.assertThrows(
+                AnalysisException.class, 
encode::checkLegalityBeforeTypeCoercion);
+        Assertions.assertTrue(encodeError.getMessage().contains("must be a 
string literal"));
+        AnalysisException decodeError = Assertions.assertThrows(
+                AnalysisException.class, 
decode::checkLegalityBeforeTypeCoercion);
+        Assertions.assertTrue(decodeError.getMessage().contains("must be a 
string literal"));
+    }
+}
diff --git 
a/regression-test/data/query_p0/sql_functions/binary_functions/test_encode_decode.out
 
b/regression-test/data/query_p0/sql_functions/binary_functions/test_encode_decode.out
new file mode 100644
index 00000000000..fa44a7072e3
--- /dev/null
+++ 
b/regression-test/data/query_p0/sql_functions/binary_functions/test_encode_decode.out
@@ -0,0 +1,35 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !encode_supported_charsets --
+1      41
+10     FEFF4E2D
+11     \N
+12     \N
+2      E9
+3      E4B8AD
+4      4E2D
+5      2D4E
+6      FEFF4E2D
+7      D83DDE00
+8      
+9      FEFF4E2D
+
+-- !decode_supported_charsets --
+1      A
+10     中
+11     \N
+12     \N
+2      é
+3      中
+4      中
+5      中
+6      中
+7      😀
+8      
+9      中
+
+-- !encode_null_valid_charset --
+\N
+
+-- !decode_null_valid_charset --
+\N
+
diff --git 
a/regression-test/suites/query_p0/sql_functions/binary_functions/test_encode_decode.groovy
 
b/regression-test/suites/query_p0/sql_functions/binary_functions/test_encode_decode.groovy
new file mode 100644
index 00000000000..50e1f9fd28b
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/binary_functions/test_encode_decode.groovy
@@ -0,0 +1,180 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_encode_decode") {
+    sql "drop table if exists test_encode_decode"
+    sql """
+        create table test_encode_decode (
+            id int,
+            plain_text string,
+            binary_value string,
+            charset varchar(32)
+        ) duplicate key(id)
+        distributed by hash(id) buckets 1
+        properties ("replication_num" = "1")
+    """
+
+    sql """
+        insert into test_encode_decode values
+            (1, 'A', unhex('41'), 'US-ASCII'),
+            (2, 'é', unhex('E9'), 'ISO-8859-1'),
+            (3, '中', unhex('E4B8AD'), 'UTF-8'),
+            (4, '中', unhex('4E2D'), 'UTF-16BE'),
+            (5, '中', unhex('2D4E'), 'UTF-16LE'),
+            (6, '中', unhex('FEFF4E2D'), 'UTF-16'),
+            (7, '😀', unhex('D83DDE00'), 'UTF-16BE'),
+            (8, '', unhex(''), 'UTF-16'),
+            (9, '中', unhex('FFFE2D4E'), 'utf-16'),
+            (10, '中', unhex('4E2D'), 'UTF-16'),
+            (11, null, null, 'UTF-8'),
+            (12, 'text', unhex('74657874'), null)
+    """
+
+    // Vectorized CASE evaluates every branch for every row, so mixed-charset
+    // CASE encode/decode queries fail on strict conversion. Filter each
+    // constant charset down to compatible rows instead.
+    order_qt_encode_supported_charsets """
+        select * from (
+            select id, hex(encode(plain_text, 'US-ASCII')) as encoded from 
test_encode_decode where id = 1
+            union all
+            select id, hex(encode(plain_text, 'ISO-8859-1')) from 
test_encode_decode where id = 2
+            union all
+            select id, hex(encode(plain_text, 'UTF-8')) from 
test_encode_decode where id = 3
+            union all
+            select id, hex(encode(plain_text, 'UTF-16BE')) from 
test_encode_decode where id = 4
+            union all
+            select id, hex(encode(plain_text, 'UTF-16LE')) from 
test_encode_decode where id = 5
+            union all
+            select id, hex(encode(plain_text, 'UTF-16')) from 
test_encode_decode where id = 6
+            union all
+            select id, hex(encode(plain_text, 'UTF-16BE')) from 
test_encode_decode where id = 7
+            union all
+            select id, hex(encode(plain_text, 'UTF-16')) from 
test_encode_decode where id = 8
+            union all
+            select id, hex(encode(plain_text, 'utf-16')) from 
test_encode_decode where id = 9
+            union all
+            select id, hex(encode(plain_text, 'UTF-16')) from 
test_encode_decode where id = 10
+            union all
+            select id, hex(encode(plain_text, 'UTF-8')) from 
test_encode_decode where id = 11
+            union all
+            select id, hex(encode(plain_text, null)) from test_encode_decode 
where id = 12
+        ) t
+        order by id
+    """
+
+    order_qt_decode_supported_charsets """
+        select * from (
+            select id, decode(cast(binary_value as varbinary), 'US-ASCII') as 
decoded from test_encode_decode where id = 1
+            union all
+            select id, decode(cast(binary_value as varbinary), 'ISO-8859-1') 
from test_encode_decode where id = 2
+            union all
+            select id, decode(cast(binary_value as varbinary), 'UTF-8') from 
test_encode_decode where id = 3
+            union all
+            select id, decode(cast(binary_value as varbinary), 'UTF-16BE') 
from test_encode_decode where id = 4
+            union all
+            select id, decode(cast(binary_value as varbinary), 'UTF-16LE') 
from test_encode_decode where id = 5
+            union all
+            select id, decode(cast(binary_value as varbinary), 'UTF-16') from 
test_encode_decode where id = 6
+            union all
+            select id, decode(cast(binary_value as varbinary), 'UTF-16BE') 
from test_encode_decode where id = 7
+            union all
+            select id, decode(cast(binary_value as varbinary), 'UTF-16') from 
test_encode_decode where id = 8
+            union all
+            select id, decode(cast(binary_value as varbinary), 'utf-16') from 
test_encode_decode where id = 9
+            union all
+            select id, decode(cast(binary_value as varbinary), 'UTF-16') from 
test_encode_decode where id = 10
+            union all
+            select id, decode(cast(binary_value as varbinary), 'UTF-8') from 
test_encode_decode where id = 11
+            union all
+            select id, decode(cast(binary_value as varbinary), null) from 
test_encode_decode where id = 12
+        ) t
+        order by id
+    """
+
+    qt_encode_null_valid_charset "select encode(null, 'UTF-8')"
+    qt_decode_null_valid_charset "select decode(null, 'UTF-8')"
+
+    test {
+        sql "select encode('text', 'GBK')"
+        exception "Unsupported character set"
+    }
+
+    test {
+        sql "select encode('中', 'US-ASCII')"
+        exception "Character conversion using 'US-ASCII' failed"
+    }
+
+    test {
+        sql "select decode(X'E4B8', 'UTF-8')"
+        exception "Character conversion using 'UTF-8' failed"
+    }
+
+    test {
+        sql "select encode('A', 'Uſ-ASCII')"
+        exception "Unsupported character set"
+    }
+
+    test {
+        sql "select encode(plain_text, charset) from test_encode_decode where 
id = 1"
+        exception "second argument of function encode must be a string literal"
+    }
+
+    test {
+        sql "select decode(cast(binary_value as varbinary), charset) from 
test_encode_decode where id = 1"
+        exception "second argument of function decode must be a string literal"
+    }
+
+    test {
+        sql "select hex(encode('中', upper('utf-8')))"
+        exception "must be a string literal"
+    }
+
+    test {
+        sql "select decode(X'E4B8AD', upper('utf-8'))"
+        exception "must be a string literal"
+    }
+
+    test {
+        sql "select encode(null, 'GBK')"
+        exception "Unsupported character set"
+    }
+
+    test {
+        sql "select encode(cast(null as string), 'GBK')"
+        exception "Unsupported character set"
+    }
+
+    test {
+        sql "select encode(plain_text, 'GBK') from test_encode_decode where id 
= 11"
+        exception "Unsupported character set"
+    }
+
+    test {
+        sql "select decode(null, 'GBK')"
+        exception "Unsupported character set"
+    }
+
+    test {
+        sql "select decode(cast(null as varbinary), 'GBK')"
+        exception "Unsupported character set"
+    }
+
+    test {
+        sql "select decode(cast(binary_value as varbinary), 'GBK') from 
test_encode_decode where id = 11"
+        exception "Unsupported character set"
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to