| 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 |  | 
|---|