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