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