text: #include "duckdb/main/settings.hpp"
  #include "duckdb/common/string_util.hpp"
  #include "duckdb/main/config.hpp"
  #include "duckdb/main/client_context.hpp"
  #include "duckdb/catalog/catalog_search_path.hpp"
  #include "duckdb/storage/buffer_manager.hpp"
  #include "duckdb/parallel/task_scheduler.hpp"
  #include "duckdb/planner/expression_binder.hpp"
  #include "duckdb/main/query_profiler.hpp"
  #include "duckdb/storage/storage_manager.hpp"
  #include "duckdb/parser/parser.hpp"
  
  namespace duckdb {
  
  //===--------------------------------------------------------------------===//
  // Access Mode
  //===--------------------------------------------------------------------===//
  void AccessModeSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	auto parameter = StringUtil::Lower(input.ToString());
  	if (parameter == "automatic") {
  		config.access_mode = AccessMode::AUTOMATIC;
  	} else if (parameter == "read_only") {
  		config.access_mode = AccessMode::READ_ONLY;
  	} else if (parameter == "read_write") {
  		config.access_mode = AccessMode::READ_WRITE;
  	} else {
  		throw InvalidInputException(
  		    "Unrecognized parameter for option ACCESS_MODE \"%s\". Expected READ_ONLY or READ_WRITE.", parameter);
  	}
  }
  
  Value AccessModeSetting::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	switch (config.access_mode) {
  	case AccessMode::AUTOMATIC:
  		return "automatic";
  	case AccessMode::READ_ONLY:
  		return "read_only";
  	case AccessMode::READ_WRITE:
  		return "read_write";
  	default:
  		throw InternalException("Unknown access mode setting");
  	}
  }
  
  //===--------------------------------------------------------------------===//
  // Checkpoint Threshold
  //===--------------------------------------------------------------------===//
  void CheckpointThresholdSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	idx_t new_limit = DBConfig::ParseMemoryLimit(input.ToString());
  	config.checkpoint_wal_size = new_limit;
  }
  
  Value CheckpointThresholdSetting::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	return Value(StringUtil::BytesToHumanReadableString(config.checkpoint_wal_size));
  }
  
  //===--------------------------------------------------------------------===//
  // Debug Checkpoint Abort
  //===--------------------------------------------------------------------===//
  void DebugCheckpointAbort::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	auto checkpoint_abort = StringUtil::Lower(input.ToString());
  	if (checkpoint_abort == "none") {
  		config.checkpoint_abort = CheckpointAbort::NO_ABORT;
  	} else if (checkpoint_abort == "before_truncate") {
  		config.checkpoint_abort = CheckpointAbort::DEBUG_ABORT_BEFORE_TRUNCATE;
  	} else if (checkpoint_abort == "before_header") {
  		config.checkpoint_abort = CheckpointAbort::DEBUG_ABORT_BEFORE_HEADER;
  	} else if (checkpoint_abort == "after_free_list_write") {
  		config.checkpoint_abort = CheckpointAbort::DEBUG_ABORT_AFTER_FREE_LIST_WRITE;
  	} else {
  		throw ParserException(
  		    "Unrecognized option for PRAGMA debug_checkpoint_abort, expected none, before_truncate or before_header");
  	}
  }
  
  Value DebugCheckpointAbort::GetSetting(ClientContext &context) {
  	return Value();
  }
  
  //===--------------------------------------------------------------------===//
  // Debug Force External
  //===--------------------------------------------------------------------===//
  void DebugForceExternal::SetLocal(ClientContext &context, const Value &input) {
  	ClientConfig::GetConfig(context).force_external = input.GetValue<bool>();
  }
  
  Value DebugForceExternal::GetSetting(ClientContext &context) {
  	return Value::BOOLEAN(ClientConfig::GetConfig(context).force_external);
  }
  
  //===--------------------------------------------------------------------===//
  // Debug Many Free List blocks
  //===--------------------------------------------------------------------===//
  void DebugManyFreeListBlocks::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	config.debug_many_free_list_blocks = input.GetValue<bool>();
  }
  
  Value DebugManyFreeListBlocks::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	return Value::BOOLEAN(config.debug_many_free_list_blocks);
  }
  
  //===--------------------------------------------------------------------===//
  // Debug Window Mode
  //===--------------------------------------------------------------------===//
  void DebugWindowMode::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	auto param = StringUtil::Lower(input.ToString());
  	if (param == "window") {
  		config.window_mode = WindowAggregationMode::WINDOW;
  	} else if (param == "combine") {
  		config.window_mode = WindowAggregationMode::COMBINE;
  	} else if (param == "separate") {
  		config.window_mode = WindowAggregationMode::SEPARATE;
  	} else {
  		throw ParserException("Unrecognized option for PRAGMA debug_window_mode, expected window, combine or separate");
  	}
  }
  
  Value DebugWindowMode::GetSetting(ClientContext &context) {
  	return Value();
  }
  
  //===--------------------------------------------------------------------===//
  // Default Collation
  //===--------------------------------------------------------------------===//
  void DefaultCollationSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	auto parameter = StringUtil::Lower(input.ToString());
  	config.collation = parameter;
  }
  
  void DefaultCollationSetting::SetLocal(ClientContext &context, const Value &input) {
  	auto parameter = input.ToString();
  	// bind the collation to verify that it exists
  	ExpressionBinder::TestCollation(context, parameter);
  	auto &config = DBConfig::GetConfig(context);
  	config.collation = parameter;
  }
  
  Value DefaultCollationSetting::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	return Value(config.collation);
  }
  
  //===--------------------------------------------------------------------===//
  // Default Order
  //===--------------------------------------------------------------------===//
  void DefaultOrderSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	auto parameter = StringUtil::Lower(input.ToString());
  	if (parameter == "ascending" || parameter == "asc") {
  		config.default_order_type = OrderType::ASCENDING;
  	} else if (parameter == "descending" || parameter == "desc") {
  		config.default_order_type = OrderType::DESCENDING;
  	} else {
  		throw InvalidInputException("Unrecognized parameter for option DEFAULT_ORDER \"%s\". Expected ASC or DESC.",
  		                            parameter);
  	}
  }
  
  Value DefaultOrderSetting::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	switch (config.default_order_type) {
  	case OrderType::ASCENDING:
  		return "asc";
  	case OrderType::DESCENDING:
  		return "desc";
  	default:
  		throw InternalException("Unknown order type setting");
  	}
  }
  
  //===--------------------------------------------------------------------===//
  // Default Null Order
  //===--------------------------------------------------------------------===//
  void DefaultNullOrderSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	auto parameter = StringUtil::Lower(input.ToString());
  
  	if (parameter == "nulls_first" || parameter == "nulls first" || parameter == "null first" || parameter == "first") {
  		config.default_null_order = OrderByNullType::NULLS_FIRST;
  	} else if (parameter == "nulls_last" || parameter == "nulls last" || parameter == "null last" ||
  	           parameter == "last") {
  		config.default_null_order = OrderByNullType::NULLS_LAST;
  	} else {
  		throw ParserException(
  		    "Unrecognized parameter for option NULL_ORDER \"%s\", expected either NULLS FIRST or NULLS LAST",
  		    parameter);
  	}
  }
  
  Value DefaultNullOrderSetting::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	switch (config.default_null_order) {
  	case OrderByNullType::NULLS_FIRST:
  		return "nulls_first";
  	case OrderByNullType::NULLS_LAST:
  		return "nulls_last";
  	default:
  		throw InternalException("Unknown null order setting");
  	}
  }
  
  //===--------------------------------------------------------------------===//
  // Disabled Optimizer
  //===--------------------------------------------------------------------===//
  void DisabledOptimizersSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	auto list = StringUtil::Split(input.ToString(), ",");
  	set<OptimizerType> disabled_optimizers;
  	for (auto &entry : list) {
  		auto param = StringUtil::Lower(entry);
  		StringUtil::Trim(param);
  		if (param.empty()) {
  			continue;
  		}
  		disabled_optimizers.insert(OptimizerTypeFromString(param));
  	}
  	config.disabled_optimizers = move(disabled_optimizers);
  }
  
  Value DisabledOptimizersSetting::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	string result;
  	for (auto &optimizer : config.disabled_optimizers) {
  		if (!result.empty()) {
  			result += ",";
  		}
  		result += OptimizerTypeToString(optimizer);
  	}
  	return Value(result);
  }
  
  //===--------------------------------------------------------------------===//
  // Enable External Access
  //===--------------------------------------------------------------------===//
  void EnableExternalAccessSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	auto new_value = input.GetValue<bool>();
  	if (db && new_value) {
  		throw InvalidInputException("Cannot change enable_external_access setting while database is running");
  	}
  	config.enable_external_access = new_value;
  }
  
  Value EnableExternalAccessSetting::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	return Value::BOOLEAN(config.enable_external_access);
  }
  
  //===--------------------------------------------------------------------===//
  // Enable Object Cache
  //===--------------------------------------------------------------------===//
  void EnableObjectCacheSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	config.object_cache_enable = input.GetValue<bool>();
  }
  
  Value EnableObjectCacheSetting::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	return Value::BOOLEAN(config.object_cache_enable);
  }
  
  //===--------------------------------------------------------------------===//
  // Enable Profiling
  //===--------------------------------------------------------------------===//
  void EnableProfilingSetting::SetLocal(ClientContext &context, const Value &input) {
  	auto parameter = StringUtil::Lower(input.ToString());
  
  	auto &config = ClientConfig::GetConfig(context);
  	if (parameter == "json") {
  		config.profiler_print_format = ProfilerPrintFormat::JSON;
  	} else if (parameter == "query_tree") {
  		config.profiler_print_format = ProfilerPrintFormat::QUERY_TREE;
  	} else if (parameter == "query_tree_optimizer") {
  		config.profiler_print_format = ProfilerPrintFormat::QUERY_TREE_OPTIMIZER;
  	} else {
  		throw ParserException(
  		    "Unrecognized print format %s, supported formats: [json, query_tree, query_tree_optimizer]", parameter);
  	}
  	config.enable_profiler = true;
  }
  
  Value EnableProfilingSetting::GetSetting(ClientContext &context) {
  	auto &config = ClientConfig::GetConfig(context);
  	if (!config.enable_profiler) {
  		return Value();
  	}
  	switch (config.profiler_print_format) {
  	case ProfilerPrintFormat::NONE:
  		return Value("none");
  	case ProfilerPrintFormat::JSON:
  		return Value("json");
  	case ProfilerPrintFormat::QUERY_TREE:
  		return Value("query_tree");
  	case ProfilerPrintFormat::QUERY_TREE_OPTIMIZER:
  		return Value("query_tree_optimizer");
  	default:
  		throw InternalException("Unsupported profiler print format");
  	}
  }
  
  //===--------------------------------------------------------------------===//
  // Enable Progress Bar
  //===--------------------------------------------------------------------===//
  void EnableProgressBarSetting::SetLocal(ClientContext &context, const Value &input) {
  	ClientConfig::GetConfig(context).enable_progress_bar = input.GetValue<bool>();
  }
  
  Value EnableProgressBarSetting::GetSetting(ClientContext &context) {
  	return Value::BOOLEAN(ClientConfig::GetConfig(context).enable_progress_bar);
  }
  
  //===--------------------------------------------------------------------===//
  // Explain Output
  //===--------------------------------------------------------------------===//
  void ExplainOutputSetting::SetLocal(ClientContext &context, const Value &input) {
  	auto parameter = StringUtil::Lower(input.ToString());
  	if (parameter == "all") {
  		ClientConfig::GetConfig(context).explain_output_type = ExplainOutputType::ALL;
  	} else if (parameter == "optimized_only") {
  		ClientConfig::GetConfig(context).explain_output_type = ExplainOutputType::OPTIMIZED_ONLY;
  	} else if (parameter == "physical_only") {
  		ClientConfig::GetConfig(context).explain_output_type = ExplainOutputType::PHYSICAL_ONLY;
  	} else {
  		throw ParserException("Unrecognized output type \"%s\", expected either ALL, OPTIMIZED_ONLY or PHYSICAL_ONLY",
  		                      parameter);
  	}
  }
  
  Value ExplainOutputSetting::GetSetting(ClientContext &context) {
  	switch (ClientConfig::GetConfig(context).explain_output_type) {
  	case ExplainOutputType::ALL:
  		return "all";
  	case ExplainOutputType::OPTIMIZED_ONLY:
  		return "optimized_only";
  	case ExplainOutputType::PHYSICAL_ONLY:
  		return "physical_only";
  	default:
  		throw InternalException("Unrecognized explain output type");
  	}
  }
  
  //===--------------------------------------------------------------------===//
  // Force Compression
  //===--------------------------------------------------------------------===//
  void ForceCompressionSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	auto compression = StringUtil::Lower(input.ToString());
  	if (compression == "none") {
  		config.force_compression = CompressionType::COMPRESSION_AUTO;
  	} else {
  		auto compression_type = CompressionTypeFromString(compression);
  		if (compression_type == CompressionType::COMPRESSION_AUTO) {
  			throw ParserException("Unrecognized option for PRAGMA force_compression, expected none, uncompressed, rle, "
  			                      "dictionary, pfor, bitpacking or fsst");
  		}
  		config.force_compression = compression_type;
  	}
  }
  
  Value ForceCompressionSetting::GetSetting(ClientContext &context) {
  	return Value();
  }
  
  //===--------------------------------------------------------------------===//
  // Log Query Path
  //===--------------------------------------------------------------------===//
  void LogQueryPathSetting::SetLocal(ClientContext &context, const Value &input) {
  	auto path = input.ToString();
  	if (path.empty()) {
  		// empty path: clean up query writer
  		context.log_query_writer = nullptr;
  	} else {
  		context.log_query_writer =
  		    make_unique<BufferedFileWriter>(FileSystem::GetFileSystem(context), path,
  		                                    BufferedFileWriter::DEFAULT_OPEN_FLAGS, context.file_opener.get());
  	}
  }
  
  Value LogQueryPathSetting::GetSetting(ClientContext &context) {
  	return context.log_query_writer ? Value(context.log_query_writer->path) : Value();
  }
  
  //===--------------------------------------------------------------------===//
  // Maximum Memory
  //===--------------------------------------------------------------------===//
  void MaximumMemorySetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	config.maximum_memory = DBConfig::ParseMemoryLimit(input.ToString());
  	if (db) {
  		BufferManager::GetBufferManager(*db).SetLimit(config.maximum_memory);
  	}
  }
  
  Value MaximumMemorySetting::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	return Value(StringUtil::BytesToHumanReadableString(config.maximum_memory));
  }
  
  //===--------------------------------------------------------------------===//
  // Perfect Hash Threshold
  //===--------------------------------------------------------------------===//
  void PerfectHashThresholdSetting::SetLocal(ClientContext &context, const Value &input) {
  	auto bits = input.GetValue<int32_t>();
  	if (bits < 0 || bits > 32) {
  		throw ParserException("Perfect HT threshold out of range: should be within range 0 - 32");
  	}
  	ClientConfig::GetConfig(context).perfect_ht_threshold = bits;
  }
  
  Value PerfectHashThresholdSetting::GetSetting(ClientContext &context) {
  	return Value::BIGINT(ClientConfig::GetConfig(context).perfect_ht_threshold);
  }
  
  //===--------------------------------------------------------------------===//
  // PreserveIdentifierCase
  //===--------------------------------------------------------------------===//
  void PreserveIdentifierCase::SetLocal(ClientContext &context, const Value &input) {
  	ClientConfig::GetConfig(context).preserve_identifier_case = input.GetValue<bool>();
  }
  
  Value PreserveIdentifierCase::GetSetting(ClientContext &context) {
  	return Value::BOOLEAN(ClientConfig::GetConfig(context).preserve_identifier_case);
  }
  
  //===--------------------------------------------------------------------===//
  // Profiler History Size
  //===--------------------------------------------------------------------===//
  void ProfilerHistorySize::SetLocal(ClientContext &context, const Value &input) {
  	auto size = input.GetValue<int64_t>();
  	if (size <= 0) {
  		throw ParserException("Size should be >= 0");
  	}
  	context.query_profiler_history->SetProfilerHistorySize(size);
  }
  
  Value ProfilerHistorySize::GetSetting(ClientContext &context) {
  	return Value();
  }
  
  //===--------------------------------------------------------------------===//
  // Profile Output
  //===--------------------------------------------------------------------===//
  void ProfileOutputSetting::SetLocal(ClientContext &context, const Value &input) {
  	auto &config = ClientConfig::GetConfig(context);
  	auto parameter = input.ToString();
  	config.profiler_save_location = parameter;
  }
  
  Value ProfileOutputSetting::GetSetting(ClientContext &context) {
  	auto &config = ClientConfig::GetConfig(context);
  	return Value(config.profiler_save_location);
  }
  
  //===--------------------------------------------------------------------===//
  // Profiling Mode
  //===--------------------------------------------------------------------===//
  void ProfilingModeSetting::SetLocal(ClientContext &context, const Value &input) {
  	auto parameter = StringUtil::Lower(input.ToString());
  	auto &config = ClientConfig::GetConfig(context);
  	if (parameter == "standard") {
  		config.enable_profiler = true;
  		config.enable_detailed_profiling = false;
  	} else if (parameter == "detailed") {
  		config.enable_profiler = true;
  		config.enable_detailed_profiling = true;
  	} else {
  		throw ParserException("Unrecognized profiling mode \"%s\", supported formats: [standard, detailed]", parameter);
  	}
  }
  
  Value ProfilingModeSetting::GetSetting(ClientContext &context) {
  	auto &config = ClientConfig::GetConfig(context);
  	if (!config.enable_profiler) {
  		return Value();
  	}
  	return Value(config.enable_detailed_profiling ? "detailed" : "standard");
  }
  
  //===--------------------------------------------------------------------===//
  // Progress Bar Time
  //===--------------------------------------------------------------------===//
  void ProgressBarTimeSetting::SetLocal(ClientContext &context, const Value &input) {
  	ClientConfig::GetConfig(context).wait_time = input.GetValue<int32_t>();
  	ClientConfig::GetConfig(context).enable_progress_bar = true;
  }
  
  Value ProgressBarTimeSetting::GetSetting(ClientContext &context) {
  	return Value::BIGINT(ClientConfig::GetConfig(context).wait_time);
  }
  
  //===--------------------------------------------------------------------===//
  // Schema
  //===--------------------------------------------------------------------===//
  void SchemaSetting::SetLocal(ClientContext &context, const Value &input) {
  	auto parameter = input.ToString();
  	context.catalog_search_path->Set(parameter, true);
  }
  
  Value SchemaSetting::GetSetting(ClientContext &context) {
  	return SearchPathSetting::GetSetting(context);
  }
  
  //===--------------------------------------------------------------------===//
  // Search Path
  //===--------------------------------------------------------------------===//
  void SearchPathSetting::SetLocal(ClientContext &context, const Value &input) {
  	auto parameter = input.ToString();
  	context.catalog_search_path->Set(parameter, false);
  }
  
  Value SearchPathSetting::GetSetting(ClientContext &context) {
  	return Value(StringUtil::Join(context.catalog_search_path->GetSetPaths(), ","));
  }
  
  //===--------------------------------------------------------------------===//
  // Temp Directory
  //===--------------------------------------------------------------------===//
  void TempDirectorySetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	config.temporary_directory = input.ToString();
  	config.use_temporary_directory = !config.temporary_directory.empty();
  	if (db) {
  		auto &buffer_manager = BufferManager::GetBufferManager(*db);
  		buffer_manager.SetTemporaryDirectory(config.temporary_directory);
  	}
  }
  
  Value TempDirectorySetting::GetSetting(ClientContext &context) {
  	auto &buffer_manager = BufferManager::GetBufferManager(context);
  	return Value(buffer_manager.GetTemporaryDirectory());
  }
  
  //===--------------------------------------------------------------------===//
  // Threads Setting
  //===--------------------------------------------------------------------===//
  void ThreadsSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
  	config.maximum_threads = input.GetValue<int64_t>();
  	if (db) {
  		TaskScheduler::GetScheduler(*db).SetThreads(config.maximum_threads);
  	}
  }
  
  Value ThreadsSetting::GetSetting(ClientContext &context) {
  	auto &config = DBConfig::GetConfig(context);
  	return Value::BIGINT(config.maximum_threads);
  }
  
  } // namespace duckdb
  