#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT3
#include "text/bstring.h"
#include "text/rstring.h"
#include "text/utf8/utf8.h"
#pragma region Substrings
static void text_substring2(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
if (sqlite3_value_type(argv[1]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "start parameter should be integer", -1);
return;
}
int start = sqlite3_value_int(argv[1]);
start = start > 0 ? start - 1 : 0;
RuneString s_src = rstring_from_cstring(src);
RuneString s_res = rstring_slice(s_src, start, s_src.length);
char* res = rstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, free);
rstring_free(s_src);
rstring_free(s_res);
}
static void text_substring3(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 3);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
if (sqlite3_value_type(argv[1]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "start parameter should be integer", -1);
return;
}
int start = sqlite3_value_int(argv[1]);
if (sqlite3_value_type(argv[2]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "length parameter should be integer", -1);
return;
}
int length = sqlite3_value_int(argv[2]);
if (length < 0) {
sqlite3_result_error(context, "length parameter should >= 0", -1);
return;
}
start -= 1;
if (start < 0) {
length += start;
start = 0;
}
if (length <= 0) {
sqlite3_result_text(context, "", -1, SQLITE_TRANSIENT);
return;
}
RuneString s_src = rstring_from_cstring(src);
if ((size_t)length > s_src.length) {
length = s_src.length;
}
RuneString s_res = rstring_substring(s_src, start, length);
char* res = rstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, free);
rstring_free(s_src);
rstring_free(s_res);
}
static void text_slice2(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
if (sqlite3_value_type(argv[1]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "start parameter should be integer", -1);
return;
}
int start = sqlite3_value_int(argv[1]);
start = start > 0 ? start - 1 : start;
RuneString s_src = rstring_from_cstring(src);
if (start < -(int)s_src.length) {
sqlite3_result_text(context, src, -1, SQLITE_TRANSIENT);
rstring_free(s_src);
return;
}
RuneString s_res = rstring_slice(s_src, start, s_src.length);
char* res = rstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, free);
rstring_free(s_src);
rstring_free(s_res);
}
static void text_slice3(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 3);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
if (sqlite3_value_type(argv[1]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "start parameter should be integer", -1);
return;
}
int start = sqlite3_value_int(argv[1]);
start = start > 0 ? start - 1 : start;
if (sqlite3_value_type(argv[2]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "end parameter should be integer", -1);
return;
}
int end = sqlite3_value_int(argv[2]);
end = end > 0 ? end - 1 : end;
RuneString s_src = rstring_from_cstring(src);
RuneString s_res = rstring_slice(s_src, start, end);
char* res = rstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, free);
rstring_free(s_src);
rstring_free(s_res);
}
static void text_left(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
if (sqlite3_value_type(argv[1]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "length parameter should be integer", -1);
return;
}
int length = sqlite3_value_int(argv[1]);
RuneString s_src = rstring_from_cstring(src);
if (length < 0) {
length = s_src.length + length;
length = length >= 0 ? length : 0;
}
RuneString s_res = rstring_substring(s_src, 0, length);
char* res = rstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, free);
rstring_free(s_src);
rstring_free(s_res);
}
static void text_right(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
if (sqlite3_value_type(argv[1]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "length parameter should be integer", -1);
return;
}
int length = sqlite3_value_int(argv[1]);
RuneString s_src = rstring_from_cstring(src);
length = (length < 0) ? (int)s_src.length + length : length;
int start = (int)s_src.length - length;
start = start < 0 ? 0 : start;
RuneString s_res = rstring_substring(s_src, start, length);
char* res = rstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, free);
rstring_free(s_src);
rstring_free(s_res);
}
#pragma endregion
#pragma region Search and match
static void text_index(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* other = (char*)sqlite3_value_text(argv[1]);
if (other == NULL) {
sqlite3_result_null(context);
return;
}
RuneString s_src = rstring_from_cstring(src);
RuneString s_other = rstring_from_cstring(other);
int idx = rstring_index(s_src, s_other);
sqlite3_result_int64(context, idx + 1);
rstring_free(s_src);
rstring_free(s_other);
}
static void text_last_index(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* other = (char*)sqlite3_value_text(argv[1]);
if (other == NULL) {
sqlite3_result_null(context);
return;
}
RuneString s_src = rstring_from_cstring(src);
RuneString s_other = rstring_from_cstring(other);
int idx = rstring_last_index(s_src, s_other);
sqlite3_result_int64(context, idx + 1);
rstring_free(s_src);
rstring_free(s_other);
}
static void text_contains(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* other = (char*)sqlite3_value_text(argv[1]);
if (other == NULL) {
sqlite3_result_null(context);
return;
}
ByteString s_src = bstring_from_cstring(src, sqlite3_value_bytes(argv[0]));
ByteString s_other = bstring_from_cstring(other, sqlite3_value_bytes(argv[1]));
bool found = bstring_contains(s_src, s_other);
sqlite3_result_int(context, found);
bstring_free(s_src);
bstring_free(s_other);
}
static void text_has_prefix(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* other = (char*)sqlite3_value_text(argv[1]);
if (other == NULL) {
sqlite3_result_null(context);
return;
}
ByteString s_src = bstring_from_cstring(src, sqlite3_value_bytes(argv[0]));
ByteString s_other = bstring_from_cstring(other, sqlite3_value_bytes(argv[1]));
bool found = bstring_has_prefix(s_src, s_other);
sqlite3_result_int(context, found);
bstring_free(s_src);
bstring_free(s_other);
}
static void text_has_suffix(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* other = (char*)sqlite3_value_text(argv[1]);
if (other == NULL) {
sqlite3_result_null(context);
return;
}
ByteString s_src = bstring_from_cstring(src, sqlite3_value_bytes(argv[0]));
ByteString s_other = bstring_from_cstring(other, sqlite3_value_bytes(argv[1]));
bool found = bstring_has_suffix(s_src, s_other);
sqlite3_result_int(context, found);
bstring_free(s_src);
bstring_free(s_other);
}
static void text_count(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* other = (char*)sqlite3_value_text(argv[1]);
if (other == NULL) {
sqlite3_result_null(context);
return;
}
ByteString s_src = bstring_from_cstring(src, sqlite3_value_bytes(argv[0]));
ByteString s_other = bstring_from_cstring(other, sqlite3_value_bytes(argv[1]));
size_t count = bstring_count(s_src, s_other);
sqlite3_result_int(context, count);
bstring_free(s_src);
bstring_free(s_other);
}
static void text_like(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* pattern = (char*)sqlite3_value_text(argv[0]);
if (pattern == NULL) {
sqlite3_result_null(context);
return;
}
const char* str = (char*)sqlite3_value_text(argv[1]);
if (str == NULL) {
sqlite3_result_null(context);
return;
}
RuneString s_pattern = rstring_from_cstring(pattern);
RuneString s_str = rstring_from_cstring(str);
bool match = rstring_like(s_pattern, s_str);
sqlite3_result_int(context, match);
rstring_free(s_pattern);
rstring_free(s_str);
}
#pragma endregion
#pragma region Split and join
static void text_split(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 3);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* sep = (const char*)sqlite3_value_text(argv[1]);
if (sep == NULL) {
sqlite3_result_null(context);
return;
}
if (sqlite3_value_type(argv[2]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "part parameter should be integer", -1);
return;
}
int part = sqlite3_value_int(argv[2]);
if (part == 0) {
sqlite3_result_error(context, "part parameter should not be 0", -1);
return;
}
part = part > 0 ? part - 1 : part;
ByteString s_src = bstring_from_cstring(src, strlen(src));
ByteString s_sep = bstring_from_cstring(sep, strlen(sep));
if (part < 0) {
int n_parts = bstring_count(s_src, s_sep) + 1;
part = n_parts + part;
}
ByteString s_part = bstring_split_part(s_src, s_sep, part);
sqlite3_result_text(context, s_part.bytes, -1, SQLITE_TRANSIENT);
bstring_free(s_src);
bstring_free(s_sep);
bstring_free(s_part);
}
static void text_join(sqlite3_context* context, int argc, sqlite3_value** argv) {
if (argc < 2) {
sqlite3_result_error(context, "expected at least 2 parameters", -1);
return;
}
const char* sep = (char*)sqlite3_value_text(argv[0]);
if (sep == NULL) {
sqlite3_result_null(context);
return;
}
ByteString s_sep = bstring_from_cstring(sep, sqlite3_value_bytes(argv[0]));
size_t n_parts = argc - 1;
ByteString* s_parts = malloc(n_parts * sizeof(ByteString));
if (s_parts == NULL) {
sqlite3_result_null(context);
return;
}
for (size_t i = 1, part_idx = 0; i < (size_t)argc; i++) {
if (sqlite3_value_type(argv[i]) == SQLITE_NULL) {
n_parts--;
continue;
}
const char* part = (char*)sqlite3_value_text(argv[i]);
int part_len = sqlite3_value_bytes(argv[i]);
s_parts[part_idx] = bstring_from_cstring(part, part_len);
part_idx++;
}
ByteString s_res = bstring_join(s_parts, n_parts, s_sep);
const char* res = bstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, SQLITE_TRANSIENT);
bstring_free(s_sep);
bstring_free(s_res);
free(s_parts);
}
static void text_concat(sqlite3_context* context, int argc, sqlite3_value** argv) {
if (argc < 1) {
sqlite3_result_error(context, "expected at least 1 parameter", -1);
return;
}
size_t n_parts = argc;
ByteString* s_parts = malloc(n_parts * sizeof(ByteString));
if (s_parts == NULL) {
sqlite3_result_null(context);
return;
}
for (size_t i = 0, part_idx = 0; i < (size_t)argc; i++) {
if (sqlite3_value_type(argv[i]) == SQLITE_NULL) {
n_parts--;
continue;
}
const char* part = (char*)sqlite3_value_text(argv[i]);
int part_len = sqlite3_value_bytes(argv[i]);
s_parts[part_idx] = bstring_from_cstring(part, part_len);
part_idx++;
}
ByteString s_res = bstring_concat(s_parts, n_parts);
const char* res = bstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, SQLITE_TRANSIENT);
bstring_free(s_res);
free(s_parts);
}
static void text_repeat(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 2);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
if (sqlite3_value_type(argv[1]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "count parameter should be integer", -1);
return;
}
int count = sqlite3_value_int(argv[1]);
count = count >= 0 ? count : 0;
ByteString s_src = bstring_from_cstring(src, sqlite3_value_bytes(argv[0]));
ByteString s_res = bstring_repeat(s_src, count);
const char* res = bstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, SQLITE_TRANSIENT);
bstring_free(s_src);
bstring_free(s_res);
}
#pragma endregion
#pragma region Trim and pad
static void text_trim(sqlite3_context* context, int argc, sqlite3_value** argv) {
if (argc != 1 && argc != 2) {
sqlite3_result_error(context, "expected 1 or 2 parameters", -1);
return;
}
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* chars = argc == 2 ? (char*)sqlite3_value_text(argv[1]) : " ";
if (chars == NULL) {
sqlite3_result_null(context);
return;
}
RuneString (*trim_func)(RuneString, RuneString) = (void*)sqlite3_user_data(context);
RuneString s_src = rstring_from_cstring(src);
RuneString s_chars = rstring_from_cstring(chars);
RuneString s_res = trim_func(s_src, s_chars);
const char* res = rstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, free);
rstring_free(s_src);
rstring_free(s_chars);
rstring_free(s_res);
}
static void text_pad(sqlite3_context* context, int argc, sqlite3_value** argv) {
if (argc != 2 && argc != 3) {
sqlite3_result_error(context, "expected 2 or 3 parameters", -1);
return;
}
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
if (sqlite3_value_type(argv[1]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "length parameter should be integer", -1);
return;
}
int length = sqlite3_value_int(argv[1]);
length = length < 0 ? 0 : length;
const char* fill = argc == 3 ? (char*)sqlite3_value_text(argv[2]) : " ";
if (fill == NULL) {
sqlite3_result_null(context);
return;
}
RuneString (*pad_func)(RuneString, size_t, RuneString) = (void*)sqlite3_user_data(context);
RuneString s_src = rstring_from_cstring(src);
RuneString s_fill = rstring_from_cstring(fill);
RuneString s_res = pad_func(s_src, length, s_fill);
const char* res = rstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, free);
rstring_free(s_src);
rstring_free(s_fill);
rstring_free(s_res);
}
#pragma endregion
#pragma region Change case
static void text_change_case(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 1);
const char* src = (const char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
size_t n = sqlite3_value_bytes(argv[0]);
char* res = malloc(n + 1);
if (res == NULL) {
sqlite3_result_error_nomem(context);
return;
}
memcpy(res, src, n);
res[n] = '\0';
bool (*fn)(char*, size_t) = sqlite3_user_data(context);
fn(res, n);
sqlite3_result_text(context, res, n, free);
}
#pragma endregion
#pragma region Other modifications
static void text_replace_all(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 3);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* old = (char*)sqlite3_value_text(argv[1]);
if (old == NULL) {
sqlite3_result_null(context);
return;
}
const char* new = (char*)sqlite3_value_text(argv[2]);
if (new == NULL) {
sqlite3_result_null(context);
return;
}
ByteString s_src = bstring_from_cstring(src, sqlite3_value_bytes(argv[0]));
ByteString s_old = bstring_from_cstring(old, sqlite3_value_bytes(argv[1]));
ByteString s_new = bstring_from_cstring(new, sqlite3_value_bytes(argv[2]));
ByteString s_res = bstring_replace_all(s_src, s_old, s_new);
const char* res = bstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, SQLITE_TRANSIENT);
bstring_free(s_src);
bstring_free(s_old);
bstring_free(s_new);
bstring_free(s_res);
}
static void text_replace(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 4);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* old = (char*)sqlite3_value_text(argv[1]);
if (old == NULL) {
sqlite3_result_null(context);
return;
}
const char* new = (char*)sqlite3_value_text(argv[2]);
if (new == NULL) {
sqlite3_result_null(context);
return;
}
if (sqlite3_value_type(argv[3]) != SQLITE_INTEGER) {
sqlite3_result_error(context, "count parameter should be integer", -1);
return;
}
int count = sqlite3_value_int(argv[3]);
count = count < 0 ? 0 : count;
ByteString s_src = bstring_from_cstring(src, sqlite3_value_bytes(argv[0]));
ByteString s_old = bstring_from_cstring(old, sqlite3_value_bytes(argv[1]));
ByteString s_new = bstring_from_cstring(new, sqlite3_value_bytes(argv[2]));
ByteString s_res = bstring_replace(s_src, s_old, s_new, count);
const char* res = bstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, SQLITE_TRANSIENT);
bstring_free(s_src);
bstring_free(s_old);
bstring_free(s_new);
bstring_free(s_res);
}
static void text_translate(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 3);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
const char* from = (char*)sqlite3_value_text(argv[1]);
if (from == NULL) {
sqlite3_result_null(context);
return;
}
const char* to = (char*)sqlite3_value_text(argv[2]);
if (to == NULL) {
sqlite3_result_null(context);
return;
}
RuneString s_src = rstring_from_cstring(src);
RuneString s_from = rstring_from_cstring(from);
RuneString s_to = rstring_from_cstring(to);
RuneString s_res = rstring_translate(s_src, s_from, s_to);
char* res = rstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, free);
rstring_free(s_src);
rstring_free(s_from);
rstring_free(s_to);
rstring_free(s_res);
}
static void text_reverse(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 1);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
RuneString s_src = rstring_from_cstring(src);
RuneString s_res = rstring_reverse(s_src);
char* res = rstring_to_cstring(s_res);
sqlite3_result_text(context, res, -1, free);
rstring_free(s_src);
rstring_free(s_res);
}
#pragma endregion
#pragma region Properties
static void text_length(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 1);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
RuneString s_src = rstring_from_cstring(src);
sqlite3_result_int64(context, s_src.length);
rstring_free(s_src);
}
static void text_size(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 1);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
sqlite3_result_int64(context, sqlite3_value_bytes(argv[0]));
}
static void text_bit_size(sqlite3_context* context, int argc, sqlite3_value** argv) {
assert(argc == 1);
const char* src = (char*)sqlite3_value_text(argv[0]);
if (src == NULL) {
sqlite3_result_null(context);
return;
}
int size = sqlite3_value_bytes(argv[0]);
sqlite3_result_int64(context, 8 * size);
}
#pragma endregion
#pragma region Collation
static int collate_nocase(void* unused, int n1, const void* s1, int n2, const void* s2) {
(void)unused;
return utf8_icmp((const char*)s1, (size_t)n1, (const char*)s2, (size_t)n2);
}
#pragma endregion
int text_init(sqlite3* db) {
static const int flags = SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC;
sqlite3_create_function(db, "text_substring", 2, flags, 0, text_substring2, 0, 0);
sqlite3_create_function(db, "text_substring", 3, flags, 0, text_substring3, 0, 0);
sqlite3_create_function(db, "text_slice", 2, flags, 0, text_slice2, 0, 0);
sqlite3_create_function(db, "text_slice", 3, flags, 0, text_slice3, 0, 0);
sqlite3_create_function(db, "text_left", 2, flags, 0, text_left, 0, 0);
sqlite3_create_function(db, "left", 2, flags, 0, text_left, 0, 0);
sqlite3_create_function(db, "text_right", 2, flags, 0, text_right, 0, 0);
sqlite3_create_function(db, "right", 2, flags, 0, text_right, 0, 0);
sqlite3_create_function(db, "text_index", 2, flags, 0, text_index, 0, 0);
sqlite3_create_function(db, "strpos", 2, flags, 0, text_index, 0, 0);
sqlite3_create_function(db, "text_last_index", 2, flags, 0, text_last_index, 0, 0);
sqlite3_create_function(db, "text_contains", 2, flags, 0, text_contains, 0, 0);
sqlite3_create_function(db, "text_has_prefix", 2, flags, 0, text_has_prefix, 0, 0);
sqlite3_create_function(db, "starts_with", 2, flags, 0, text_has_prefix, 0, 0);
sqlite3_create_function(db, "text_has_suffix", 2, flags, 0, text_has_suffix, 0, 0);
sqlite3_create_function(db, "text_count", 2, flags, 0, text_count, 0, 0);
sqlite3_create_function(db, "text_like", 2, flags, 0, text_like, 0, 0);
sqlite3_create_function(db, "text_split", 3, flags, 0, text_split, 0, 0);
sqlite3_create_function(db, "split_part", 3, flags, 0, text_split, 0, 0);
sqlite3_create_function(db, "text_join", -1, flags, 0, text_join, 0, 0);
sqlite3_create_function(db, "concat_ws", -1, flags, 0, text_join, 0, 0);
sqlite3_create_function(db, "text_concat", -1, flags, 0, text_concat, 0, 0);
sqlite3_create_function(db, "concat", -1, flags, 0, text_concat, 0, 0);
sqlite3_create_function(db, "text_repeat", 2, flags, 0, text_repeat, 0, 0);
sqlite3_create_function(db, "repeat", 2, flags, 0, text_repeat, 0, 0);
sqlite3_create_function(db, "text_ltrim", -1, flags, rstring_trim_left, text_trim, 0, 0);
sqlite3_create_function(db, "ltrim", -1, flags, rstring_trim_left, text_trim, 0, 0);
sqlite3_create_function(db, "text_rtrim", -1, flags, rstring_trim_right, text_trim, 0, 0);
sqlite3_create_function(db, "rtrim", -1, flags, rstring_trim_right, text_trim, 0, 0);
sqlite3_create_function(db, "text_trim", -1, flags, rstring_trim, text_trim, 0, 0);
sqlite3_create_function(db, "btrim", -1, flags, rstring_trim, text_trim, 0, 0);
sqlite3_create_function(db, "text_lpad", -1, flags, rstring_pad_left, text_pad, 0, 0);
sqlite3_create_function(db, "lpad", -1, flags, rstring_pad_left, text_pad, 0, 0);
sqlite3_create_function(db, "text_rpad", -1, flags, rstring_pad_right, text_pad, 0, 0);
sqlite3_create_function(db, "rpad", -1, flags, rstring_pad_right, text_pad, 0, 0);
sqlite3_create_function(db, "text_upper", 1, flags, utf8_toupper, text_change_case, 0, 0);
sqlite3_create_function(db, "text_lower", 1, flags, utf8_tolower, text_change_case, 0, 0);
sqlite3_create_function(db, "text_title", 1, flags, utf8_totitle, text_change_case, 0, 0);
sqlite3_create_function(db, "text_casefold", 1, flags, utf8_casefold, text_change_case, 0, 0);
sqlite3_create_function(db, "text_replace", 3, flags, 0, text_replace_all, 0, 0);
sqlite3_create_function(db, "text_replace", 4, flags, 0, text_replace, 0, 0);
sqlite3_create_function(db, "text_translate", 3, flags, 0, text_translate, 0, 0);
sqlite3_create_function(db, "translate", 3, flags, 0, text_translate, 0, 0);
sqlite3_create_function(db, "text_reverse", 1, flags, 0, text_reverse, 0, 0);
sqlite3_create_function(db, "reverse", 1, flags, 0, text_reverse, 0, 0);
sqlite3_create_function(db, "text_length", 1, flags, 0, text_length, 0, 0);
sqlite3_create_function(db, "char_length", 1, flags, 0, text_length, 0, 0);
sqlite3_create_function(db, "character_length", 1, flags, 0, text_length, 0, 0);
sqlite3_create_function(db, "text_size", 1, flags, 0, text_size, 0, 0);
sqlite3_create_function(db, "octet_length", 1, flags, 0, text_size, 0, 0);
sqlite3_create_function(db, "text_bitsize", 1, flags, 0, text_bit_size, 0, 0);
sqlite3_create_function(db, "bit_length", 1, flags, 0, text_bit_size, 0, 0);
sqlite3_create_collation(db, "text_nocase", SQLITE_UTF8, NULL, collate_nocase);
return SQLITE_OK;
}