Repository: duckdb/duckdb
File: extension/core_functions/scalar/blob/encode.cpp (C++)

Code before:
#include "core_functions/scalar/blob_functions.hpp"
#include "utf8proc_wrapper.hpp"
#include "duckdb/common/exception/conversion_exception.hpp"

namespace duckdb {

namespace {

void EncodeFunction(DataChunk &args, ExpressionState &state, Vector &result) {
	// encode is essentially a nop cast from varchar to blob
	// we only need to reinterpret the data using the blob type
	result.Reinterpret(args.data[0]);
}

enum class DecodeErrorBehavior : uint8_t {
	ABORT = 1,  // raise error
	STRICT = 2, // return null
	REPLACE = 3 // replace invalid characters with '?'
};

bool TryGetDecodeErrorBehavior(const string &specifier_p, DecodeErrorBehavior &result) {
	auto specifier = StringUtil::Lower(specifier_p);
	if (specifier == "abort" || specifier == "a") {
		result = DecodeErrorBehavior::ABORT;
	} else if (specifier == "strict" || specifier == "s") {
		result = DecodeErrorBehavior::STRICT;
	} else if (specifier == "replace" || specifier == "r") {
		result = DecodeErrorBehavior::REPLACE;
	} else {
		return false;
	}
	return true;
}

DecodeErrorBehavior GetDecodeErrorBehavior(const string &specifier) {
	DecodeErrorBehavior result;
	if (!TryGetDecodeErrorBehavior(specifier, result)) {
		throw ConversionException("decode error behavior specifier \"%s\" not recognized", specifier);
	}
	return result;
}

struct UnaryBlobDecodeOperator {
	template <class INPUT_TYPE, class RESULT_TYPE>
	static RESULT_TYPE Operation(INPUT_TYPE input) {
		auto input_data = input.GetData();
		auto input_length = input.GetSize();
		if (Utf8Proc::Analyze(input_data, input_length) == UnicodeType::INVALID) {

Review comment (performance):
Using a string here is quite inefficient since this creates a copy of the string for every call - it would be better to use `StringUtil::CIEquals` directly on the data in the `string_t`, e.g.:

```cpp
DecodeErrorBehavior GetDecodeErrorBehavior(const string_t &specifier) {
    auto size = specifier.GetSize();
    auto data = specifier.GetData();
    if (StringUtil::CIEquals(data, size, "strict", 6)) {
        return DecodeErrorBehavior::STRICT;
    } ....
}
```

Code after:
#include "core_functions/scalar/blob_functions.hpp"
#include "utf8proc_wrapper.hpp"
#include "duckdb/common/string_util.hpp"
#include "duckdb/common/exception/conversion_exception.hpp"

namespace duckdb {

namespace {

void EncodeFunction(DataChunk &args, ExpressionState &state, Vector &result) {
	// encode is essentially a nop cast from varchar to blob
	// we only need to reinterpret the data using the blob type
	result.Reinterpret(args.data[0]);
}

enum class DecodeErrorBehavior : uint8_t {
	STRICT = 1,  // raise an error
	REPLACE = 2, // replace invalid characters with '?'
	IGNORE = 3   // ignore invalid characters (remove from string)
};

DecodeErrorBehavior GetDecodeErrorBehavior(const string_t &specifier_p) {
	auto size = specifier_p.GetSize();
	auto data = specifier_p.GetData();
	if (StringUtil::CIEquals(data, size, "strict", 6)) {
		return DecodeErrorBehavior::STRICT;
	} else if (StringUtil::CIEquals(data, size, "replace", 7)) {
		return DecodeErrorBehavior::REPLACE;
	} else if (StringUtil::CIEquals(data, size, "ignore", 6)) {
		return DecodeErrorBehavior::IGNORE;
	} else {
		throw ConversionException("decode error behavior specifier \"%s\" not recognized", specifier_p.GetString());
	}
}

struct UnaryBlobDecodeOperator {
	template <class INPUT_TYPE, class RESULT_TYPE>
	static RESULT_TYPE Operation(INPUT_TYPE input) {
		auto input_data = input.GetData();
		auto input_length = input.GetSize();
		if (Utf8Proc::Analyze(input_data, input_length) == UnicodeType::INVALID) {
			throw ConversionException(
			    "Failure in decode: could not convert blob to UTF8 string, the blob "
			    "contained invalid UTF8 characters. \n"
			    "Use try(decode(BLOB)) to return NULL and continue instead of returning an error. "
			    "Specify decode(BLOB, 'replace') to replace invalid characters with '?'. "
			    "Specify decode(BLOB, 'ignore') to remove invalid characters when encountered.");
		}