Skip to content

Commit

Permalink
support builtin function right
Browse files Browse the repository at this point in the history
  • Loading branch information
zz-jason committed May 6, 2020
1 parent dd173f2 commit 12b0bf1
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 0 deletions.
30 changes: 30 additions & 0 deletions src/function/scalar/string/left_right.cpp
Expand Up @@ -35,4 +35,34 @@ void LeftFun::RegisterFunction(BuiltinFunctions &set) {
set.AddFunction(ScalarFunction("left", {SQLType::VARCHAR, SQLType::BIGINT}, SQLType::VARCHAR, left_function));
}

static string_t right_scalar_function(Vector &result, const string_t str, int64_t pos, unique_ptr<char[]> &output,
idx_t &current_len) {
int64_t num_characters = LengthFun::Length<string_t, int64_t>(str);
if (pos >= 0) {
int64_t len = std::min(num_characters, pos);
int64_t start = num_characters - len + 1;
return SubstringFun::substring_scalar_function(result, str, start, len, output, current_len);
}

int64_t len = num_characters - std::min(num_characters, -pos);
int64_t start = num_characters - len + 1;
return SubstringFun::substring_scalar_function(result, str, start, len, output, current_len);
}

static void right_function(DataChunk &args, ExpressionState &state, Vector &result) {
assert(args.column_count() == 2 && args.data[0].type == TypeId::VARCHAR && args.data[1].type == TypeId::INT64);
auto &str_vec = args.data[0];
auto &pos_vec = args.data[1];
idx_t current_len = 0;
unique_ptr<char[]> output;

BinaryExecutor::Execute<string_t, int64_t, string_t>(
str_vec, pos_vec, result, args.size(),
[&](string_t str, int64_t pos) { return right_scalar_function(result, str, pos, output, current_len); });
}

void RightFun::RegisterFunction(BuiltinFunctions &set) {
set.AddFunction(ScalarFunction("right", {SQLType::VARCHAR, SQLType::BIGINT}, SQLType::VARCHAR, right_function));
}

} // namespace duckdb
1 change: 1 addition & 0 deletions src/function/scalar/string_functions.cpp
Expand Up @@ -16,6 +16,7 @@ void BuiltinFunctions::RegisterStringFunctions() {
Register<LpadFun>();
Register<LtrimFun>();
Register<LeftFun>();
Register<RightFun>();
Register<PrintfFun>();
Register<RegexpFun>();
Register<SubstringFun>();
Expand Down
4 changes: 4 additions & 0 deletions src/include/duckdb/function/scalar/string_functions.hpp
Expand Up @@ -80,6 +80,10 @@ struct LeftFun {
static void RegisterFunction(BuiltinFunctions &set);
};

struct RightFun {
static void RegisterFunction(BuiltinFunctions &set);
};

struct RegexpFun {
static void RegisterFunction(BuiltinFunctions &set);
};
Expand Down
86 changes: 86 additions & 0 deletions test/sql/function/test_stringfunctions.cpp
Expand Up @@ -548,6 +548,92 @@ TEST_CASE("LEFT test", "[function]") {
REQUIRE(CHECK_COLUMN(result, 0, {"", "", ""}));
}

TEST_CASE("RIGHT test", "[function]") {
unique_ptr<QueryResult> result;
DuckDB db(nullptr);
Connection con(db);
con.EnableQueryVerification();

// test RIGHT on positive positions
result = con.Query("SELECT RIGHT('abcd', 0), RIGHT('abc', 1), RIGHT('abc', 2), RIGHT('abc', 3), RIGHT('abc', 4)");
REQUIRE(CHECK_COLUMN(result, 0, {""}));
REQUIRE(CHECK_COLUMN(result, 1, {"c"}));
REQUIRE(CHECK_COLUMN(result, 2, {"bc"}));
REQUIRE(CHECK_COLUMN(result, 3, {"abc"}));
REQUIRE(CHECK_COLUMN(result, 4, {"abc"}));

result = con.Query(
"SELECT RIGHT('🦆ab', 0), RIGHT('🦆ab', 1), RIGHT('🦆ab', 2), RIGHT('🦆ab', 3), RIGHT('🦆ab', 4)");
REQUIRE(CHECK_COLUMN(result, 0, {""}));
REQUIRE(CHECK_COLUMN(result, 1, {"b"}));
REQUIRE(CHECK_COLUMN(result, 2, {"ab"}));
REQUIRE(CHECK_COLUMN(result, 3, {"🦆ab"}));
REQUIRE(CHECK_COLUMN(result, 4, {"🦆ab"}));

result = con.Query(
"SELECT RIGHT('🦆🤦S̈', 0), RIGHT('🦆🤦S̈', 1), RIGHT('🦆🤦S̈', 2), RIGHT('🦆🤦S̈', 3)");
REQUIRE(CHECK_COLUMN(result, 0, {""}));
REQUIRE(CHECK_COLUMN(result, 1, {""}));
REQUIRE(CHECK_COLUMN(result, 2, {"🤦S̈"}));
REQUIRE(CHECK_COLUMN(result, 3, {"🦆🤦S̈"}));

// test RIGHT on negative positions
result =
con.Query("SELECT RIGHT('abcd', 0), RIGHT('abc', -1), RIGHT('abc', -2), RIGHT('abc', -3), RIGHT('abc', -4)");
REQUIRE(CHECK_COLUMN(result, 0, {""}));
REQUIRE(CHECK_COLUMN(result, 1, {"bc"}));
REQUIRE(CHECK_COLUMN(result, 2, {"c"}));
REQUIRE(CHECK_COLUMN(result, 3, {""}));
REQUIRE(CHECK_COLUMN(result, 4, {""}));

result = con.Query("SELECT RIGHT('🦆ab', 0), RIGHT('🦆ab', -1), RIGHT('🦆ab', -2), RIGHT('🦆ab', -3), "
"RIGHT('🦆ab', -4)");
REQUIRE(CHECK_COLUMN(result, 0, {""}));
REQUIRE(CHECK_COLUMN(result, 1, {"ab"}));
REQUIRE(CHECK_COLUMN(result, 2, {"b"}));
REQUIRE(CHECK_COLUMN(result, 3, {""}));
REQUIRE(CHECK_COLUMN(result, 4, {""}));

result = con.Query(
"SELECT RIGHT('🦆🤦S̈', 0), RIGHT('🦆🤦S̈', -1), RIGHT('🦆🤦S̈', -2), RIGHT('🦆🤦S̈', -3)");
REQUIRE(CHECK_COLUMN(result, 0, {""}));
REQUIRE(CHECK_COLUMN(result, 1, {"🤦S̈"}));
REQUIRE(CHECK_COLUMN(result, 2, {""}));
REQUIRE(CHECK_COLUMN(result, 3, {""}));

// test RIGHT on NULL values
result = con.Query("SELECT RIGHT(NULL, 0), RIGHT('abc', NULL), RIGHT(NULL, NULL)");
REQUIRE(CHECK_COLUMN(result, 0, {""}));
REQUIRE(CHECK_COLUMN(result, 1, {""}));
REQUIRE(CHECK_COLUMN(result, 2, {""}));

result = con.Query("SELECT RIGHT(NULL, 0), RIGHT('🦆ab', NULL), RIGHT(NULL, NULL)");
REQUIRE(CHECK_COLUMN(result, 0, {""}));
REQUIRE(CHECK_COLUMN(result, 1, {""}));
REQUIRE(CHECK_COLUMN(result, 2, {""}));

// test on tables
REQUIRE_NO_FAIL(con.Query("DROP TABLE IF EXISTS strings"));
REQUIRE_NO_FAIL(con.Query("CREATE TABLE strings(a STRING, b BIGINT)"));
REQUIRE_NO_FAIL(
con.Query("INSERT INTO STRINGS VALUES ('abcd', 0), ('abc', 1), ('abc', 2), ('abc', 3), ('abc', 4)"));
result = con.Query("SELECT RIGHT(a, b) FROM strings");
REQUIRE(CHECK_COLUMN(result, 0, {"", "c", "bc", "abc", "abc"}));

REQUIRE_NO_FAIL(con.Query("DROP TABLE IF EXISTS strings"));
REQUIRE_NO_FAIL(con.Query("CREATE TABLE strings(a STRING, b BIGINT)"));
REQUIRE_NO_FAIL(
con.Query("INSERT INTO STRINGS VALUES ('abcd', 0), ('abc', -1), ('abc', -2), ('abc', -3), ('abc', -4)"));
result = con.Query("SELECT RIGHT(a, b) FROM strings");
REQUIRE(CHECK_COLUMN(result, 0, {"", "bc", "c", "", ""}));

REQUIRE_NO_FAIL(con.Query("DROP TABLE IF EXISTS strings"));
REQUIRE_NO_FAIL(con.Query("CREATE TABLE strings(a STRING, b BIGINT)"));
REQUIRE_NO_FAIL(con.Query("INSERT INTO STRINGS VALUES (NULL, 0), ('abc', NULL), (NULL, NULL)"));
result = con.Query("SELECT RIGHT(a, b) FROM strings");
REQUIRE(CHECK_COLUMN(result, 0, {"", "", ""}));
}

TEST_CASE("BIT_LENGTH test", "[function]") {
unique_ptr<QueryResult> result;
DuckDB db(nullptr);
Expand Down

0 comments on commit 12b0bf1

Please sign in to comment.