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
11namespace duckdb {
12
13struct PragmaDetailedProfilingOutputOperatorData : public GlobalTableFunctionState {
14 explicit PragmaDetailedProfilingOutputOperatorData() : initialized(false) {
15 }
16
17 ColumnDataScanState scan_state;
18 bool initialized;
19};
20
21struct PragmaDetailedProfilingOutputData : public TableFunctionData {
22 explicit PragmaDetailedProfilingOutputData(vector<LogicalType> &types) : types(types) {
23 }
24 unique_ptr<ColumnDataCollection> collection;
25 vector<LogicalType> types;
26};
27
28static 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
61unique_ptr<GlobalTableFunctionState> PragmaDetailedProfilingOutputInit(ClientContext &context,
62 TableFunctionInitInput &input) {
63 return make_uniq<PragmaDetailedProfilingOutputOperatorData>();
64}
65
66// Insert a row into the given datachunk
67static 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 extra_info) {
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
86static 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
109static 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
168void PragmaDetailedProfilingOutput::RegisterFunction(BuiltinFunctions &set) {
169 set.AddFunction(function: TableFunction("pragma_detailed_profiling_output", {}, PragmaDetailedProfilingOutputFunction,
170 PragmaDetailedProfilingOutputBind, PragmaDetailedProfilingOutputInit));
171}
172
173} // namespace duckdb
174