| 1 | #include "duckdb/execution/operator/helper/physical_pragma.hpp" |
| 2 | |
| 3 | #include "duckdb/main/client_context.hpp" |
| 4 | #include "duckdb/main/database.hpp" |
| 5 | #include "duckdb/storage/storage_manager.hpp" |
| 6 | #include "duckdb/storage/buffer_manager.hpp" |
| 7 | #include "duckdb/common/operator/cast_operators.hpp" |
| 8 | #include "duckdb/planner/expression_binder.hpp" |
| 9 | |
| 10 | #include <cctype> |
| 11 | |
| 12 | using namespace duckdb; |
| 13 | using namespace std; |
| 14 | |
| 15 | static idx_t ParseMemoryLimit(string arg); |
| 16 | |
| 17 | void PhysicalPragma::GetChunkInternal(ClientContext &context, DataChunk &chunk, PhysicalOperatorState *state) { |
| 18 | auto &pragma = *info; |
| 19 | auto &keyword = pragma.name; |
| 20 | if (keyword == "enable_profile" || keyword == "enable_profiling" ) { |
| 21 | // enable profiling |
| 22 | if (pragma.pragma_type == PragmaType::ASSIGNMENT) { |
| 23 | // enable_profiling with assignment |
| 24 | // this is either enable_profiling = json, or enable_profiling = query_tree |
| 25 | string assignment = pragma.parameters[0].ToString(); |
| 26 | if (assignment == "json" ) { |
| 27 | context.profiler.automatic_print_format = ProfilerPrintFormat::JSON; |
| 28 | } else if (assignment == "query_tree" ) { |
| 29 | context.profiler.automatic_print_format = ProfilerPrintFormat::QUERY_TREE; |
| 30 | } else { |
| 31 | throw ParserException("Unrecognized print format %s, supported formats: [json, query_tree]" , |
| 32 | assignment.c_str()); |
| 33 | } |
| 34 | } else if (pragma.pragma_type == PragmaType::NOTHING) { |
| 35 | context.profiler.automatic_print_format = ProfilerPrintFormat::QUERY_TREE; |
| 36 | } else { |
| 37 | throw ParserException("Cannot call PRAGMA enable_profiling" ); |
| 38 | } |
| 39 | context.profiler.Enable(); |
| 40 | } else if (keyword == "disable_profile" || keyword == "disable_profiling" ) { |
| 41 | if (pragma.pragma_type != PragmaType::NOTHING) { |
| 42 | throw ParserException("disable_profiling cannot take parameters!" ); |
| 43 | } |
| 44 | // enable profiling |
| 45 | context.profiler.Disable(); |
| 46 | context.profiler.automatic_print_format = ProfilerPrintFormat::NONE; |
| 47 | } else if (keyword == "profiling_output" || keyword == "profile_output" ) { |
| 48 | // set file location of where to save profiling output |
| 49 | if (pragma.pragma_type != PragmaType::ASSIGNMENT || pragma.parameters[0].type != TypeId::VARCHAR) { |
| 50 | throw ParserException( |
| 51 | "Profiling output must be an assignment (e.g. PRAGMA profile_output='/tmp/test.json')" ); |
| 52 | } |
| 53 | context.profiler.save_location = pragma.parameters[0].str_value; |
| 54 | } else if (keyword == "memory_limit" ) { |
| 55 | if (pragma.pragma_type != PragmaType::ASSIGNMENT) { |
| 56 | throw ParserException("Memory limit must be an assignment (e.g. PRAGMA memory_limit='1GB')" ); |
| 57 | } |
| 58 | if (pragma.parameters[0].type == TypeId::VARCHAR) { |
| 59 | idx_t new_limit = ParseMemoryLimit(pragma.parameters[0].str_value); |
| 60 | // set the new limit in the buffer manager |
| 61 | context.db.storage->buffer_manager->SetLimit(new_limit); |
| 62 | } else { |
| 63 | int64_t value = pragma.parameters[0].GetValue<int64_t>(); |
| 64 | if (value < 0) { |
| 65 | // limit < 0, set limit to infinite |
| 66 | context.db.storage->buffer_manager->SetLimit(); |
| 67 | } else { |
| 68 | throw ParserException( |
| 69 | "Memory limit must be an assignment with a memory unit (e.g. PRAGMA memory_limit='1GB')" ); |
| 70 | } |
| 71 | } |
| 72 | } else if (keyword == "collation" || keyword == "default_collation" ) { |
| 73 | if (pragma.pragma_type != PragmaType::ASSIGNMENT) { |
| 74 | throw ParserException("Collation must be an assignment (e.g. PRAGMA default_collation=NOCASE)" ); |
| 75 | } |
| 76 | auto collation_param = StringUtil::Lower(pragma.parameters[0].CastAs(TypeId::VARCHAR).str_value); |
| 77 | // bind the collation to verify that it exists |
| 78 | ExpressionBinder::PushCollation(context, nullptr, collation_param); |
| 79 | context.db.collation = collation_param; |
| 80 | } else { |
| 81 | throw ParserException("Unrecognized PRAGMA keyword: %s" , keyword.c_str()); |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | idx_t ParseMemoryLimit(string arg) { |
| 86 | // split based on the number/non-number |
| 87 | idx_t idx = 0; |
| 88 | while (std::isspace(arg[idx])) { |
| 89 | idx++; |
| 90 | } |
| 91 | idx_t num_start = idx; |
| 92 | while ((arg[idx] >= '0' && arg[idx] <= '9') || arg[idx] == '.' || arg[idx] == 'e' || arg[idx] == 'E' || |
| 93 | arg[idx] == '-') { |
| 94 | idx++; |
| 95 | } |
| 96 | if (idx == num_start) { |
| 97 | throw ParserException("Memory limit must have a number (e.g. PRAGMA memory_limit=1GB" ); |
| 98 | } |
| 99 | string number = arg.substr(num_start, idx - num_start); |
| 100 | |
| 101 | // try to parse the number |
| 102 | double limit = Cast::Operation<string_t, double>(number.c_str()); |
| 103 | |
| 104 | // now parse the memory limit unit (e.g. bytes, gb, etc) |
| 105 | while (std::isspace(arg[idx])) { |
| 106 | idx++; |
| 107 | } |
| 108 | idx_t start = idx; |
| 109 | while (idx < arg.size() && !std::isspace(arg[idx])) { |
| 110 | idx++; |
| 111 | } |
| 112 | if (limit < 0) { |
| 113 | // limit < 0, set limit to infinite |
| 114 | return (idx_t)-1; |
| 115 | } |
| 116 | string unit = StringUtil::Lower(arg.substr(start, idx - start)); |
| 117 | idx_t multiplier; |
| 118 | if (unit == "byte" || unit == "bytes" || unit == "b" ) { |
| 119 | multiplier = 1; |
| 120 | } else if (unit == "kilobyte" || unit == "kilobytes" || unit == "kb" || unit == "k" ) { |
| 121 | multiplier = 1000LL; |
| 122 | } else if (unit == "megabyte" || unit == "megabytes" || unit == "mb" || unit == "m" ) { |
| 123 | multiplier = 1000LL * 1000LL; |
| 124 | } else if (unit == "gigabyte" || unit == "gigabytes" || unit == "gb" || unit == "g" ) { |
| 125 | multiplier = 1000LL * 1000LL * 1000LL; |
| 126 | } else if (unit == "terabyte" || unit == "terabytes" || unit == "tb" || unit == "t" ) { |
| 127 | multiplier = 1000LL * 1000LL * 1000LL * 1000LL; |
| 128 | } else { |
| 129 | throw ParserException("Unknown unit for memory_limit: %s (expected: b, mb, gb or tb)" , unit.c_str()); |
| 130 | } |
| 131 | return (idx_t)multiplier * limit; |
| 132 | } |
| 133 | |