| 1 | #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" |
| 2 | #include "duckdb/catalog/catalog_entry/view_catalog_entry.hpp" |
| 3 | #include "duckdb/common/limits.hpp" |
| 4 | #include "duckdb/common/types/column/column_data_collection.hpp" |
| 5 | #include "duckdb/function/table/system_functions.hpp" |
| 6 | #include "duckdb/main/client_context.hpp" |
| 7 | #include "duckdb/main/client_data.hpp" |
| 8 | #include "duckdb/main/query_profiler.hpp" |
| 9 | #include "duckdb/planner/constraints/bound_not_null_constraint.hpp" |
| 10 | |
| 11 | namespace duckdb { |
| 12 | |
| 13 | struct PragmaDetailedProfilingOutputOperatorData : public GlobalTableFunctionState { |
| 14 | explicit PragmaDetailedProfilingOutputOperatorData() : initialized(false) { |
| 15 | } |
| 16 | |
| 17 | ColumnDataScanState scan_state; |
| 18 | bool initialized; |
| 19 | }; |
| 20 | |
| 21 | struct PragmaDetailedProfilingOutputData : public TableFunctionData { |
| 22 | explicit PragmaDetailedProfilingOutputData(vector<LogicalType> &types) : types(types) { |
| 23 | } |
| 24 | unique_ptr<ColumnDataCollection> collection; |
| 25 | vector<LogicalType> types; |
| 26 | }; |
| 27 | |
| 28 | static unique_ptr<FunctionData> PragmaDetailedProfilingOutputBind(ClientContext &context, TableFunctionBindInput &input, |
| 29 | vector<LogicalType> &return_types, |
| 30 | vector<string> &names) { |
| 31 | names.emplace_back(args: "OPERATOR_ID" ); |
| 32 | return_types.emplace_back(args: LogicalType::INTEGER); |
| 33 | |
| 34 | names.emplace_back(args: "ANNOTATION" ); |
| 35 | return_types.emplace_back(args: LogicalType::VARCHAR); |
| 36 | |
| 37 | names.emplace_back(args: "ID" ); |
| 38 | return_types.emplace_back(args: LogicalType::INTEGER); |
| 39 | |
| 40 | names.emplace_back(args: "NAME" ); |
| 41 | return_types.emplace_back(args: LogicalType::VARCHAR); |
| 42 | |
| 43 | names.emplace_back(args: "TIME" ); |
| 44 | return_types.emplace_back(args: LogicalType::DOUBLE); |
| 45 | |
| 46 | names.emplace_back(args: "CYCLES_PER_TUPLE" ); |
| 47 | return_types.emplace_back(args: LogicalType::DOUBLE); |
| 48 | |
| 49 | names.emplace_back(args: "SAMPLE_SIZE" ); |
| 50 | return_types.emplace_back(args: LogicalType::INTEGER); |
| 51 | |
| 52 | names.emplace_back(args: "INPUT_SIZE" ); |
| 53 | return_types.emplace_back(args: LogicalType::INTEGER); |
| 54 | |
| 55 | names.emplace_back(args: "EXTRA_INFO" ); |
| 56 | return_types.emplace_back(args: LogicalType::VARCHAR); |
| 57 | |
| 58 | return make_uniq<PragmaDetailedProfilingOutputData>(args&: return_types); |
| 59 | } |
| 60 | |
| 61 | unique_ptr<GlobalTableFunctionState> PragmaDetailedProfilingOutputInit(ClientContext &context, |
| 62 | TableFunctionInitInput &input) { |
| 63 | return make_uniq<PragmaDetailedProfilingOutputOperatorData>(); |
| 64 | } |
| 65 | |
| 66 | // Insert a row into the given datachunk |
| 67 | static void SetValue(DataChunk &output, int index, int op_id, string annotation, int id, string name, double time, |
| 68 | int sample_counter, int tuple_counter, string ) { |
| 69 | output.SetValue(col_idx: 0, index, val: op_id); |
| 70 | output.SetValue(col_idx: 1, index, val: std::move(annotation)); |
| 71 | output.SetValue(col_idx: 2, index, val: id); |
| 72 | output.SetValue(col_idx: 3, index, val: std::move(name)); |
| 73 | #if defined(RDTSC) |
| 74 | output.SetValue(4, index, Value(nullptr)); |
| 75 | output.SetValue(5, index, time); |
| 76 | #else |
| 77 | output.SetValue(col_idx: 4, index, val: time); |
| 78 | output.SetValue(col_idx: 5, index, val: Value(nullptr)); |
| 79 | |
| 80 | #endif |
| 81 | output.SetValue(col_idx: 6, index, val: sample_counter); |
| 82 | output.SetValue(col_idx: 7, index, val: tuple_counter); |
| 83 | output.SetValue(col_idx: 8, index, val: std::move(extra_info)); |
| 84 | } |
| 85 | |
| 86 | static void ExtractFunctions(ColumnDataCollection &collection, ExpressionInfo &info, DataChunk &chunk, int op_id, |
| 87 | int &fun_id) { |
| 88 | if (info.hasfunction) { |
| 89 | D_ASSERT(info.sample_tuples_count != 0); |
| 90 | SetValue(output&: chunk, index: chunk.size(), op_id, annotation: "Function" , id: fun_id++, name: info.function_name, |
| 91 | time: int(info.function_time) / double(info.sample_tuples_count), sample_counter: info.sample_tuples_count, |
| 92 | tuple_counter: info.tuples_count, extra_info: "" ); |
| 93 | |
| 94 | chunk.SetCardinality(chunk.size() + 1); |
| 95 | if (chunk.size() == STANDARD_VECTOR_SIZE) { |
| 96 | collection.Append(new_chunk&: chunk); |
| 97 | chunk.Reset(); |
| 98 | } |
| 99 | } |
| 100 | if (info.children.empty()) { |
| 101 | return; |
| 102 | } |
| 103 | // extract the children of this node |
| 104 | for (auto &child : info.children) { |
| 105 | ExtractFunctions(collection, info&: *child, chunk, op_id, fun_id); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | static void PragmaDetailedProfilingOutputFunction(ClientContext &context, TableFunctionInput &data_p, |
| 110 | DataChunk &output) { |
| 111 | auto &state = data_p.global_state->Cast<PragmaDetailedProfilingOutputOperatorData>(); |
| 112 | auto &data = data_p.bind_data->CastNoConst<PragmaDetailedProfilingOutputData>(); |
| 113 | |
| 114 | if (!state.initialized) { |
| 115 | // create a ColumnDataCollection |
| 116 | auto collection = make_uniq<ColumnDataCollection>(args&: context, args&: data.types); |
| 117 | |
| 118 | // create a chunk |
| 119 | DataChunk chunk; |
| 120 | chunk.Initialize(context, types: data.types); |
| 121 | |
| 122 | // Initialize ids |
| 123 | int operator_counter = 1; |
| 124 | int function_counter = 1; |
| 125 | int expression_counter = 1; |
| 126 | auto &client_data = ClientData::Get(context); |
| 127 | if (client_data.query_profiler_history->GetPrevProfilers().empty()) { |
| 128 | return; |
| 129 | } |
| 130 | // For each Operator |
| 131 | auto &tree_map = client_data.query_profiler_history->GetPrevProfilers().back().second->GetTreeMap(); |
| 132 | for (auto op : tree_map) { |
| 133 | // For each Expression Executor |
| 134 | for (auto &expr_executor : op.second.get().info.executors_info) { |
| 135 | // For each Expression tree |
| 136 | if (!expr_executor) { |
| 137 | continue; |
| 138 | } |
| 139 | for (auto &expr_timer : expr_executor->roots) { |
| 140 | D_ASSERT(expr_timer->sample_tuples_count != 0); |
| 141 | SetValue(output&: chunk, index: chunk.size(), op_id: operator_counter, annotation: "ExpressionRoot" , id: expression_counter++, |
| 142 | // Sometimes, cycle counter is not accurate, too big or too small. return 0 for |
| 143 | // those cases |
| 144 | name: expr_timer->name, time: int(expr_timer->time) / double(expr_timer->sample_tuples_count), |
| 145 | sample_counter: expr_timer->sample_tuples_count, tuple_counter: expr_timer->tuples_count, extra_info: expr_timer->extra_info); |
| 146 | // Increment cardinality |
| 147 | chunk.SetCardinality(chunk.size() + 1); |
| 148 | // Check whether data chunk is full or not |
| 149 | if (chunk.size() == STANDARD_VECTOR_SIZE) { |
| 150 | collection->Append(new_chunk&: chunk); |
| 151 | chunk.Reset(); |
| 152 | } |
| 153 | // Extract all functions inside the tree |
| 154 | ExtractFunctions(collection&: *collection, info&: *expr_timer->root, chunk, op_id: operator_counter, fun_id&: function_counter); |
| 155 | } |
| 156 | } |
| 157 | operator_counter++; |
| 158 | } |
| 159 | collection->Append(new_chunk&: chunk); |
| 160 | data.collection = std::move(collection); |
| 161 | data.collection->InitializeScan(state&: state.scan_state); |
| 162 | state.initialized = true; |
| 163 | } |
| 164 | |
| 165 | data.collection->Scan(state&: state.scan_state, result&: output); |
| 166 | } |
| 167 | |
| 168 | void PragmaDetailedProfilingOutput::RegisterFunction(BuiltinFunctions &set) { |
| 169 | set.AddFunction(function: TableFunction("pragma_detailed_profiling_output" , {}, PragmaDetailedProfilingOutputFunction, |
| 170 | PragmaDetailedProfilingOutputBind, PragmaDetailedProfilingOutputInit)); |
| 171 | } |
| 172 | |
| 173 | } // namespace duckdb |
| 174 | |