should be it
This commit is contained in:
64
external/duckdb/tools/sqlite3_api_wrapper/test/include/sqlite_db_wrapper.hpp
vendored
Normal file
64
external/duckdb/tools/sqlite3_api_wrapper/test/include/sqlite_db_wrapper.hpp
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "sqlite3.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "util_functions.hpp"
|
||||
|
||||
// C++ wrapper class for the C wrapper API that wraps our C++ API, because why not
|
||||
class SQLiteDBWrapper {
|
||||
public:
|
||||
SQLiteDBWrapper() : db(nullptr) {
|
||||
}
|
||||
~SQLiteDBWrapper() {
|
||||
if (db) {
|
||||
sqlite3_close(db);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3 *db;
|
||||
std::vector<std::vector<std::string>> results;
|
||||
|
||||
public:
|
||||
int Open(std::string filename) {
|
||||
return sqlite3_open(filename.c_str(), &db) == SQLITE_OK;
|
||||
}
|
||||
|
||||
std::string GetErrorMessage() {
|
||||
auto err = sqlite3_errmsg(db);
|
||||
return err ? std::string(err) : std::string();
|
||||
}
|
||||
|
||||
bool Execute(std::string query) {
|
||||
results.clear();
|
||||
char *errmsg = nullptr;
|
||||
int rc = sqlite3_exec(db, query.c_str(), concatenate_results, &results, &errmsg);
|
||||
if (errmsg) {
|
||||
sqlite3_free(errmsg);
|
||||
}
|
||||
return rc == SQLITE_OK;
|
||||
}
|
||||
|
||||
void PrintResult() {
|
||||
print_result(results);
|
||||
}
|
||||
|
||||
bool CheckColumn(size_t column, std::vector<std::string> expected_data) {
|
||||
if (column >= results.size()) {
|
||||
fprintf(stderr, "Column index is out of range!\n");
|
||||
PrintResult();
|
||||
return false;
|
||||
}
|
||||
if (results[column].size() != expected_data.size()) {
|
||||
fprintf(stderr, "Row counts do not match!\n");
|
||||
PrintResult();
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < expected_data.size(); i++) {
|
||||
if (expected_data[i] != results[column][i]) {
|
||||
fprintf(stderr, "Value does not match: expected \"%s\" but got \"%s\"\n", expected_data[i].c_str(),
|
||||
results[column][i].c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
80
external/duckdb/tools/sqlite3_api_wrapper/test/include/sqlite_stmt_wrapper.hpp
vendored
Normal file
80
external/duckdb/tools/sqlite3_api_wrapper/test/include/sqlite_stmt_wrapper.hpp
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "sqlite3.h"
|
||||
#include "util_functions.hpp"
|
||||
|
||||
class SQLiteStmtWrapper {
|
||||
public:
|
||||
SQLiteStmtWrapper() : stmt(nullptr) {
|
||||
}
|
||||
~SQLiteStmtWrapper() {
|
||||
Finalize();
|
||||
}
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
std::string error_message;
|
||||
|
||||
int Prepare(sqlite3 *db, const char *zSql, int nByte, const char **pzTail) {
|
||||
Finalize();
|
||||
return sqlite3_prepare_v2(db, zSql, nByte, &stmt, pzTail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a prepared statement previously "groomed" and print the result
|
||||
*/
|
||||
int ExecutePreparedStmt() {
|
||||
if (!stmt) {
|
||||
fprintf(stderr, "There is no a prepared statement: Prepare(...) must be invoked firstly.\n");
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
int rc = SQLITE_ROW;
|
||||
std::vector<std::vector<std::string>> results; /* To print the result */
|
||||
|
||||
size_t nCol = sqlite3_column_count(stmt);
|
||||
char **azCols = (char **)malloc(nCol * sizeof(const char *)); /* Names of result columns */
|
||||
char **azVals = (char **)malloc(nCol * sizeof(const char *)); /* Result values */
|
||||
if (!azCols || !azVals) {
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
for (duckdb::idx_t i = 0; i < nCol; i++) {
|
||||
azCols[i] = (char *)sqlite3_column_name(stmt, i);
|
||||
}
|
||||
|
||||
while (rc == SQLITE_ROW) {
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc == SQLITE_ROW) {
|
||||
for (duckdb::idx_t i = 0; i < nCol; i++) {
|
||||
azVals[i] = (char *)sqlite3_column_text(stmt, i);
|
||||
if (!azVals[i] && sqlite3_column_type(stmt, i) != SQLITE_NULL) {
|
||||
rc = SQLITE_NOMEM;
|
||||
fprintf(stderr, "sqlite3_exec: out of memory.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (concatenate_results(&results, nCol, azVals, azCols)) {
|
||||
/* EVIDENCE-OF: R-38229-40159 If the callback function to
|
||||
** sqlite3_exec() returns non-zero, then sqlite3_exec() will
|
||||
** return SQLITE_ABORT. */
|
||||
rc = SQLITE_ABORT;
|
||||
fprintf(stderr, "sqlite3_exec: callback returned non-zero. "
|
||||
"Aborting.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == SQLITE_DONE) {
|
||||
print_result(results);
|
||||
}
|
||||
Finalize();
|
||||
sqlite3_free(azCols);
|
||||
sqlite3_free(azVals);
|
||||
return (rc == SQLITE_DONE) ? SQLITE_OK : rc;
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
if (stmt) {
|
||||
sqlite3_finalize(stmt);
|
||||
stmt = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
232
external/duckdb/tools/sqlite3_api_wrapper/test/include/udf_scalar_functions.hpp
vendored
Normal file
232
external/duckdb/tools/sqlite3_api_wrapper/test/include/udf_scalar_functions.hpp
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
#include "sqlite3.h"
|
||||
#include "udf_struct_sqlite3.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// SQLite UDF to be register on DuckDB
|
||||
static void multiply10(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc == 1);
|
||||
int v = sqlite3_value_int(argv[0]);
|
||||
v *= 10;
|
||||
sqlite3_result_int(context, v);
|
||||
}
|
||||
|
||||
static void sum_cols_int(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc > 0);
|
||||
auto sum = sqlite3_value_int(argv[0]);
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
sum += sqlite3_value_int(argv[i]);
|
||||
}
|
||||
sqlite3_result_int(context, sum);
|
||||
}
|
||||
|
||||
static void sum_cols_int_check_nulls(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc > 0);
|
||||
auto sum = 0;
|
||||
int res_type = SQLITE_INTEGER;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
int type = sqlite3_value_type(argv[i]);
|
||||
if (type == SQLITE_NULL) {
|
||||
res_type = SQLITE_NULL;
|
||||
} else {
|
||||
sum += sqlite3_value_int(argv[i]);
|
||||
}
|
||||
}
|
||||
if (res_type == SQLITE_NULL) {
|
||||
sqlite3_result_null(context);
|
||||
} else {
|
||||
sqlite3_result_int(context, sum);
|
||||
}
|
||||
}
|
||||
|
||||
static void sum_cols_double(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc > 0);
|
||||
auto sum = sqlite3_value_double(argv[0]);
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
sum += sqlite3_value_double(argv[i]);
|
||||
}
|
||||
sqlite3_result_double(context, sum);
|
||||
}
|
||||
|
||||
static void check_text(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc == 1);
|
||||
char *str = (char *)sqlite3_value_text(argv[0]);
|
||||
int len = sqlite3_value_bytes(argv[0]);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
str[i] = 'T';
|
||||
}
|
||||
sqlite3_result_text(context, str, len, nullptr);
|
||||
}
|
||||
|
||||
static void check_null_terminated_string(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc == 1);
|
||||
char *str = (char *)sqlite3_value_text(argv[0]);
|
||||
|
||||
// strlen expects a null-terminated string
|
||||
// otherwise, the result is undefined, it's likely to happen a 'heap-buffer-overflow'
|
||||
size_t str_len = strlen(str);
|
||||
|
||||
// both length must be equal
|
||||
REQUIRE(str_len == (size_t)sqlite3_value_bytes(argv[0]));
|
||||
|
||||
sqlite3_result_text(context, str, str_len, nullptr);
|
||||
}
|
||||
|
||||
static void check_blob(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc == 1);
|
||||
auto blob = (char *)sqlite3_value_blob(argv[0]);
|
||||
int len = sqlite3_value_bytes(argv[0]);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
blob[i] = 'B';
|
||||
}
|
||||
sqlite3_result_blob(context, blob, len, nullptr);
|
||||
}
|
||||
|
||||
static void check_type(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc == 1);
|
||||
int type_id = sqlite3_value_type(argv[0]);
|
||||
switch (type_id) {
|
||||
case SQLITE_INTEGER: {
|
||||
auto value = sqlite3_value_int(argv[0]);
|
||||
sqlite3_result_int(context, value * 10); // value x 10
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
auto value = sqlite3_value_double(argv[0]);
|
||||
sqlite3_result_double(context, value * 100); // value x 100
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
auto value = (char *)sqlite3_value_text(argv[0]);
|
||||
value[0] = 'T';
|
||||
value[1] = 'E';
|
||||
value[2] = 'X';
|
||||
value[3] = 'T';
|
||||
auto len = sqlite3_value_bytes(argv[0]);
|
||||
sqlite3_result_text(context, value, len, nullptr);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
auto value = sqlite3_value_blob(argv[0]);
|
||||
((char *)value)[0] = 'B';
|
||||
((char *)value)[1] = 'L';
|
||||
((char *)value)[2] = 'O';
|
||||
((char *)value)[3] = 'B';
|
||||
auto len = sqlite3_value_bytes(argv[0]);
|
||||
sqlite3_result_blob(context, value, len, nullptr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_null(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc == 1);
|
||||
sqlite3_result_null(context);
|
||||
}
|
||||
|
||||
// get user data and replace the input value
|
||||
static void get_user_data(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc == 1);
|
||||
auto *pData = sqlite3_user_data(context);
|
||||
if (pData) {
|
||||
char *str = (char *)sqlite3_value_text(argv[0]);
|
||||
int str_len = sqlite3_value_bytes(argv[0]);
|
||||
int undescore_idx = -1;
|
||||
// find first '_' underscore
|
||||
for (int i = 0; i < str_len; ++i) {
|
||||
if (str[i] == '_') {
|
||||
undescore_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (undescore_idx > -1) {
|
||||
char *userData = (char *)pData;
|
||||
int u_len = strlen(userData);
|
||||
// replace from the first undescore, case there is memory space in the string
|
||||
if (u_len + undescore_idx <= str_len) {
|
||||
memcpy(str + undescore_idx, userData, u_len);
|
||||
}
|
||||
}
|
||||
sqlite3_result_text(context, str, str_len, nullptr);
|
||||
} else {
|
||||
sqlite3_result_null(context);
|
||||
}
|
||||
}
|
||||
|
||||
// get text value from integer or float types
|
||||
static void cast_numbers_to_text(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE((sqlite3_value_type(argv[0]) == SQLITE_INTEGER || sqlite3_value_type(argv[0]) == SQLITE_FLOAT));
|
||||
char *str = (char *)sqlite3_value_text(argv[0]); // argv[0] is a an integer
|
||||
REQUIRE(sqlite3_value_type(argv[0]) == SQLITE_TEXT);
|
||||
size_t len = sqlite3_value_bytes(argv[0]);
|
||||
sqlite3_result_text(context, str, len, nullptr);
|
||||
}
|
||||
|
||||
static void cast_to_int32(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
// argv[0] is not a 32-bit integer, internal casting must occur
|
||||
int value = sqlite3_value_int(argv[0]);
|
||||
if (sqlite3_errcode(argv[0]->db) == SQLITE_MISMATCH) {
|
||||
sqlite3_result_null(context);
|
||||
} else {
|
||||
sqlite3_result_int(context, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void cast_to_int64(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
// argv[0] is not a 64-bit integer, internal casting must occur
|
||||
int64_t value = sqlite3_value_int64(argv[0]);
|
||||
if (sqlite3_errcode(argv[0]->db) == SQLITE_MISMATCH) {
|
||||
sqlite3_result_null(context);
|
||||
} else {
|
||||
sqlite3_result_int64(context, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void cast_to_float(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(sqlite3_value_type(argv[0]) != SQLITE_FLOAT);
|
||||
// argv[0] is not a float, internal casting must occur
|
||||
double value = sqlite3_value_double(argv[0]);
|
||||
if (sqlite3_errcode(argv[0]->db) == SQLITE_MISMATCH) {
|
||||
sqlite3_result_null(context);
|
||||
} else {
|
||||
sqlite3_result_double(context, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void sum_overload_function(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE(argc > 0);
|
||||
int value1, value2, value3;
|
||||
value2 = 0;
|
||||
value3 = 0;
|
||||
value1 = sqlite3_value_int(argv[0]);
|
||||
if (argc == 2) {
|
||||
value2 = sqlite3_value_int(argv[1]);
|
||||
} else if (argc == 3) {
|
||||
value2 = sqlite3_value_int(argv[1]);
|
||||
value3 = sqlite3_value_int(argv[2]);
|
||||
}
|
||||
|
||||
sqlite3_result_int(context, value1 + value2 + value3);
|
||||
}
|
||||
|
||||
// calling sqlite3_value_text() multiple times
|
||||
static void calling_value_text_multiple_times(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
||||
REQUIRE((sqlite3_value_type(argv[0]) == SQLITE_INTEGER || sqlite3_value_type(argv[0]) == SQLITE_FLOAT ||
|
||||
sqlite3_value_type(argv[0]) == SQLITE_TEXT));
|
||||
char *str = (char *)sqlite3_value_text(argv[0]);
|
||||
REQUIRE(sqlite3_value_type(argv[0]) == SQLITE_TEXT);
|
||||
auto len = strlen(str);
|
||||
size_t len2;
|
||||
char *str2;
|
||||
// calling sqlite3_value_text multiple times, i.e., 10x
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
str2 = (char *)sqlite3_value_text(argv[0]);
|
||||
REQUIRE(str == str2);
|
||||
len2 = strlen(str2);
|
||||
REQUIRE(len == len2);
|
||||
}
|
||||
len = sqlite3_value_bytes(argv[0]);
|
||||
sqlite3_result_text(context, str, len, nullptr);
|
||||
}
|
||||
30
external/duckdb/tools/sqlite3_api_wrapper/test/include/util_functions.hpp
vendored
Normal file
30
external/duckdb/tools/sqlite3_api_wrapper/test/include/util_functions.hpp
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "duckdb/common/constants.hpp"
|
||||
|
||||
static int concatenate_results(void *arg, int ncols, char **vals, char **colnames) {
|
||||
auto &results = *((std::vector<std::vector<std::string>> *)arg);
|
||||
if (results.size() == 0) {
|
||||
results.resize(ncols);
|
||||
}
|
||||
for (int i = 0; i < ncols; i++) {
|
||||
results[i].push_back(vals[i] ? vals[i] : "NULL");
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static void print_result(std::vector<std::vector<std::string>> &results) {
|
||||
if (results.empty()) {
|
||||
return;
|
||||
}
|
||||
for (duckdb::idx_t row_idx = 0; row_idx < results[0].size(); row_idx++) {
|
||||
for (duckdb::idx_t col_idx = 0; col_idx < results.size(); col_idx++) {
|
||||
printf("%s|", results[col_idx][row_idx].c_str());
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user