1#include "duckdb/function/scalar/string_functions.hpp"
2
3#include "duckdb/common/exception.hpp"
4#include "duckdb/common/vector_operations/vector_operations.hpp"
5#include "duckdb/common/vector_operations/ternary_executor.hpp"
6
7#include <string.h>
8#include <ctype.h>
9#include <unordered_map>
10#include <algorithm> // std::max
11
12using namespace std;
13
14namespace duckdb {
15
16static idx_t next_needle(const char *input_haystack, idx_t size_haystack, const char *input_needle,
17 const idx_t size_needle) {
18 // Needle needs something to proceed
19 if (size_needle > 0) {
20 // Haystack should be bigger or equal size to the needle
21 for (idx_t string_position = 0; (size_haystack - string_position) >= size_needle; ++string_position) {
22 // Compare Needle to the Haystack
23 if ((memcmp(input_haystack + string_position, input_needle, size_needle) == 0)) {
24 return string_position;
25 }
26 }
27
28 return size_haystack;
29 }
30 // Did not find the needle
31 return size_haystack;
32}
33
34static string_t replace_scalar_function(const string_t &haystack, const string_t &needle, const string_t &thread,
35 vector<char> &result) {
36 // Get information about the needle, the haystack and the "thread"
37 auto input_haystack = haystack.GetData();
38 auto size_haystack = haystack.GetSize();
39
40 const auto input_needle = needle.GetData();
41 const auto size_needle = needle.GetSize();
42
43 const auto input_thread = thread.GetData();
44 const auto size_thread = thread.GetSize();
45
46 // Reuse the buffer
47 result.clear();
48
49 for (;;) {
50 // Append the non-matching characters
51 auto string_position = next_needle(input_haystack, size_haystack, input_needle, size_needle);
52 result.insert(result.end(), input_haystack, input_haystack + string_position);
53 input_haystack += string_position;
54 size_haystack -= string_position;
55
56 // Stop when we have read the entire haystack
57 if (size_haystack == 0)
58 break;
59
60 // Replace the matching characters
61 result.insert(result.end(), input_thread, input_thread + size_thread);
62 input_haystack += size_needle;
63 size_haystack -= size_needle;
64 }
65
66 return string_t(result.data(), result.size());
67}
68
69static void replace_function(DataChunk &args, ExpressionState &state, Vector &result) {
70 assert(args.column_count() == 3 && args.data[0].type == TypeId::VARCHAR && args.data[1].type == TypeId::VARCHAR &&
71 args.data[2].type == TypeId::VARCHAR);
72 auto &haystack_vector = args.data[0];
73 auto &needle_vector = args.data[1];
74 auto &thread_vector = args.data[2];
75
76 vector<char> buffer;
77 TernaryExecutor::Execute<string_t, string_t, string_t, string_t>(
78 haystack_vector, needle_vector, thread_vector, result, args.size(),
79 [&](string_t input_string, string_t needle_string, string_t thread_string) {
80 return StringVector::AddString(result,
81 replace_scalar_function(input_string, needle_string, thread_string, buffer));
82 });
83}
84
85void ReplaceFun::RegisterFunction(BuiltinFunctions &set) {
86 set.AddFunction(ScalarFunction("replace", // name of the function
87 {SQLType::VARCHAR, // argument list
88 SQLType::VARCHAR, SQLType::VARCHAR},
89 SQLType::VARCHAR, // return type
90 replace_function)); // pointer to function implementation
91}
92
93} // namespace duckdb
94