Files
email-tracker/external/duckdb/test/api/test_reset.cpp
2025-10-24 19:21:19 -05:00

293 lines
10 KiB
C++

#include "catch.hpp"
#include "test_helpers.hpp"
#include <iostream>
#include <map>
#include <set>
using namespace duckdb;
using namespace std;
struct OptionValuePair {
OptionValuePair() {
}
OptionValuePair(Value val) : input(val), output(val) {
}
OptionValuePair(Value input, Value output) : input(std::move(input)), output(std::move(output)) {
}
Value input;
Value output;
};
struct OptionValueSet {
OptionValueSet() {
}
OptionValueSet(Value val) {
pairs.emplace_back(std::move(val));
}
OptionValueSet(Value input, Value output) {
pairs.emplace_back(std::move(input), std::move(output));
}
OptionValueSet(duckdb::vector<std::string> pairs_p) {
for (auto &pair : pairs_p) {
pairs.emplace_back(pair);
}
}
OptionValueSet(duckdb::vector<OptionValuePair> pairs_p) : pairs(std::move(pairs_p)) {
}
duckdb::vector<OptionValuePair> pairs;
};
void RequireValueEqual(const string &option, const Value &left, const Value &right, int line);
#define REQUIRE_VALUE_EQUAL(option, lhs, rhs) RequireValueEqual(option, lhs, rhs, __LINE__)
OptionValueSet GetValueForOption(const string &name, const LogicalType &type) {
static unordered_map<string, OptionValueSet> value_map = {
{"threads", {Value::BIGINT(42), Value::BIGINT(42)}},
{"checkpoint_threshold", {"4.0 GiB"}},
{"debug_checkpoint_abort", {{"none", "before_truncate", "before_header", "after_free_list_write"}}},
{"default_collation", {"nocase"}},
{"default_order", {"DESC"}},
{"default_null_order", {"NULLS_FIRST"}},
{"disabled_compression_methods", {"RLE"}},
{"disabled_optimizers", {"extension"}},
{"debug_force_external", {Value(true)}},
{"old_implicit_casting", {Value(true)}},
{"prefer_range_joins", {Value(true)}},
{"allow_persistent_secrets", {Value(false)}},
{"secret_directory", {"/tmp/some/path"}},
{"default_secret_storage", {"custom_storage"}},
{"custom_extension_repository", {"duckdb.org/no-extensions-here", "duckdb.org/no-extensions-here"}},
{"autoinstall_extension_repository", {"duckdb.org/no-extensions-here", "duckdb.org/no-extensions-here"}},
{"lambda_syntax", {EnumUtil::ToString(LambdaSyntax::DISABLE_SINGLE_ARROW)}},
{"allow_parser_override_extension", {"fallback"}},
{"profiling_coverage", {EnumUtil::ToString(ProfilingCoverage::ALL)}},
#ifdef DUCKDB_EXTENSION_AUTOLOAD_DEFAULT
{"autoload_known_extensions", {!DUCKDB_EXTENSION_AUTOLOAD_DEFAULT}},
#else
{"autoload_known_extensions", {true}},
#endif
#ifdef DUCKDB_EXTENSION_AUTOINSTALL_DEFAULT
{"autoinstall_known_extensions", {!DUCKDB_EXTENSION_AUTOINSTALL_DEFAULT}},
#else
{"autoinstall_known_extensions", {true}},
#endif
{"enable_profiling", {"json"}},
{"explain_output", {{"all", "optimized_only", "physical_only"}}},
{"file_search_path", {"test"}},
{"force_compression", {"uncompressed", "Uncompressed"}},
{"home_directory", {"test"}},
{"allow_extensions_metadata_mismatch", {"true"}},
{"extension_directory", {"test"}},
{"max_expression_depth", {50}},
{"max_memory", {"4.0 GiB"}},
{"max_temp_directory_size", {"10.0 GiB"}},
{"merge_join_threshold", {73}},
{"nested_loop_join_threshold", {73}},
{"memory_limit", {"4.0 GiB"}},
{"storage_compatibility_version", {"v0.10.0"}},
{"ordered_aggregate_threshold", {Value::UBIGINT(idx_t(1) << 12)}},
{"null_order", {"NULLS_FIRST"}},
{"debug_verify_vector", {"dictionary_expression"}},
{"perfect_ht_threshold", {0}},
{"pivot_filter_threshold", {999}},
{"pivot_limit", {999}},
{"partitioned_write_flush_threshold", {123}},
{"preserve_identifier_case", {false}},
{"preserve_insertion_order", {false}},
{"profile_output", {"test"}},
{"profiling_mode", {"detailed"}},
{"disabled_log_types", {"blabla"}},
{"enabled_log_types", {"blabla"}},
{"enabled_log_types", {"blabla"}},
{"enable_logging", {true}},
{"logging_mode", {"ENABLE_SELECTED"}},
{"logging_level", {"FATAL"}},
{"logging_storage", {"stdout"}},
{"enable_progress_bar_print", {false}},
{"scalar_subquery_error_on_multiple_rows", {false}},
{"ieee_floating_point_ops", {false}},
{"progress_bar_time", {0}},
{"temp_directory", {"tmp"}},
{"wal_autocheckpoint", {"4.0 GiB"}},
{"force_bitpacking_mode", {"constant"}},
{"enable_http_logging", {false}},
{"http_proxy", {"localhost:80"}},
{"http_proxy_username", {"john"}},
{"http_proxy_password", {"doe"}},
{"allocator_flush_threshold", {"4.0 GiB"}},
{"allocator_bulk_deallocation_flush_threshold", {"4.0 GiB"}},
{"arrow_output_version", {"1.5"}},
{"enable_external_file_cache", {false}},
{"experimental_metadata_reuse", {false}},
{"storage_block_prefetch", {"always_prefetch"}},
{"pin_threads", {"off"}}};
// Every option that's not excluded has to be part of this map
if (!value_map.count(name)) {
switch (type.id()) {
case LogicalTypeId::BOOLEAN:
return OptionValueSet(Value::BOOLEAN(true));
case LogicalTypeId::TINYINT:
case LogicalTypeId::SMALLINT:
case LogicalTypeId::INTEGER:
case LogicalTypeId::BIGINT:
case LogicalTypeId::UTINYINT:
case LogicalTypeId::USMALLINT:
case LogicalTypeId::UINTEGER:
case LogicalTypeId::UBIGINT:
return OptionValueSet(Value::Numeric(type, 42));
default:
break;
}
REQUIRE(name == "MISSING_FROM_MAP");
}
return value_map[name];
}
bool OptionIsExcludedFromTest(const string &name) {
static unordered_set<string> excluded_options = {
"access_mode",
"allowed_directories",
"allowed_paths",
"schema",
"search_path",
"debug_window_mode",
"experimental_parallel_csv",
"lock_configuration", // cant change this while db is running
"disabled_filesystems", // cant change this while db is running
"enable_external_access", // cant change this while db is running
"allow_unsigned_extensions", // cant change this while db is running
"allow_community_extensions", // cant change this while db is running
"allow_unredacted_secrets", // cant change this while db is running
"disable_database_invalidation", // cant change this while db is running
"temp_file_encryption",
"enable_object_cache",
"streaming_buffer_size",
"log_query_path",
"password",
"username",
"user",
"external_threads", // tested in test_threads.cpp
"profiling_output", // just an alias
"duckdb_api",
"custom_user_agent",
"custom_profiling_settings",
"custom_user_agent",
"default_block_size",
"index_scan_percentage",
"scheduler_process_partial",
"http_logging_output",
"enable_profiling",
"enable_progress_bar",
"enable_progress_bar_print",
"progress_bar_time",
"index_scan_max_count",
"profiling_mode"};
return excluded_options.count(name) == 1;
}
bool ValueEqual(const Value &left, const Value &right) {
return Value::NotDistinctFrom(left, right);
}
void RequireValueEqual(const string &option_name, const Value &left, const Value &right, int line) {
if (ValueEqual(left, right)) {
return;
}
auto error = StringUtil::Format("\nLINE[%d] (Option:%s) | Expected left:'%s' and right:'%s' to be equal", line,
option_name, left.ToString(), right.ToString());
cerr << error << endl;
REQUIRE(false);
}
Value GetValueForSetting(Connection &con, const string &name, const LogicalType &type) {
string new_value;
auto result = con.Query(StringUtil::Format("SELECT value FROM duckdb_settings() WHERE name = %s", SQLString(name)));
for (auto &row : *result) {
new_value = row.GetValue<string>(0);
}
return Value(new_value).CastAs(*con.context, type);
}
//! New options should be added to the value_map in GetValueForOption
//! Or added to the 'excluded_options' in OptionIsExcludedFromTest
TEST_CASE("Test RESET statement for ClientConfig options", "[api]") {
// Create a connection
DBConfig config;
config.options.load_extensions = false;
DuckDB db(nullptr, &config);
Connection con(db);
con.Query("BEGIN TRANSACTION");
con.Query("PRAGMA disable_profiling");
struct ResetSettingOption {
string name;
Value value;
LogicalType type;
};
duckdb::vector<ResetSettingOption> options;
auto result = con.Query("SELECT name, value, input_type FROM duckdb_settings()");
for (auto row : *result) {
ResetSettingOption option;
option.name = row.GetValue<string>(0);
option.type = DBConfig::ParseLogicalType(row.GetValue<string>(2));
if (row.IsNull(1)) {
option.value = Value(option.type);
} else {
Value str_val = Value(row.GetValue<string>(1));
option.value = str_val.CastAs(*con.context, option.type);
}
if (OptionIsExcludedFromTest(option.name)) {
continue;
}
options.push_back(std::move(option));
}
for (auto &option : options) {
auto value_set = GetValueForOption(option.name, option.type);
// verify that at least one value is different
bool any_different = false;
string options;
for (auto &value_pair : value_set.pairs) {
if (!ValueEqual(option.value, value_pair.output)) {
any_different = true;
} else {
if (!options.empty()) {
options += ", ";
}
options += value_pair.output.ToString();
}
}
if (!any_different) {
auto error = StringUtil::Format(
"\n(Option:%s) | Expected original value '%s' and provided option '%s' to be different", option.name,
option.value.ToString(), options);
cerr << error << endl;
REQUIRE(false);
}
auto original_value = GetValueForSetting(con, option.name, option.type);
for (auto &value_pair : value_set.pairs) {
// Get the new value for the option
auto input = value_pair.input.CastAs(*con.context, option.type);
// Set the new option
REQUIRE_NO_FAIL(con.Query(StringUtil::Format("SET %s = %s", option.name, input.ToSQLString())));
auto changed_value = GetValueForSetting(con, option.name, option.type);
// Get the value of the option again
REQUIRE_VALUE_EQUAL(option.name, changed_value, value_pair.output);
// reset the option again
REQUIRE_NO_FAIL(con.Query(StringUtil::Format("RESET %s", option.name)));
auto reset_value = GetValueForSetting(con, option.name, option.type);
// Get the reset value of the option
REQUIRE_VALUE_EQUAL(option.name, reset_value, original_value);
}
}
}