Skip to content

Commit fc81a29

Browse files
committed
expose profiling in the pyconnection
1 parent 0967d11 commit fc81a29

8 files changed

Lines changed: 113 additions & 1 deletion

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
.sw?
1515
#OS X specific files.
1616
.DS_store
17+
#VSCode specifics
18+
.vscode/
1719

1820
#==============================================================================#
1921
# Build artifacts

_duckdb-stubs/__init__.pyi

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,11 @@ __all__: list[str] = [
8686
"default_connection",
8787
"description",
8888
"df",
89+
"disable_profiling",
8990
"distinct",
9091
"dtype",
9192
"duplicate",
93+
"enable_profiling",
9294
"enum_type",
9395
"execute",
9496
"executemany",
@@ -109,6 +111,7 @@ __all__: list[str] = [
109111
"from_df",
110112
"from_parquet",
111113
"from_query",
114+
"get_profiling_information",
112115
"get_table_names",
113116
"install_extension",
114117
"interrupt",
@@ -313,6 +316,9 @@ class DuckDBPyConnection:
313316
repository_url: str | None = None,
314317
version: str | None = None,
315318
) -> None: ...
319+
def get_profiling_information(self, format: str = "json") -> str: ...
320+
def enable_profiling(self) -> None: ...
321+
def disable_profiling(self) -> None: ...
316322
def interrupt(self) -> None: ...
317323
def list_filesystems(self) -> list[str]: ...
318324
def list_type(self, type: sqltypes.DuckDBPyType) -> sqltypes.DuckDBPyType: ...
@@ -1227,6 +1233,9 @@ def limit(
12271233
*,
12281234
connection: DuckDBPyConnection | None = None,
12291235
) -> DuckDBPyRelation: ...
1236+
def get_profiling_information(*, connection: DuckDBPyConnection | None = None, format: str = "json") -> str: ...
1237+
def enable_profiling(*, connection: DuckDBPyConnection | None = None) -> None: ...
1238+
def disable_profiling(*, connection: DuckDBPyConnection | None = None) -> None: ...
12301239
def list_filesystems(*, connection: DuckDBPyConnection | None = None) -> list[str]: ...
12311240
def list_type(
12321241
type: sqltypes.DuckDBPyType, *, connection: DuckDBPyConnection | None = None

duckdb/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,11 @@
8484
default_connection,
8585
description,
8686
df,
87+
disable_profiling,
8788
distinct,
8889
dtype,
8990
duplicate,
91+
enable_profiling,
9092
enum_type,
9193
execute,
9294
executemany,
@@ -107,6 +109,7 @@
107109
from_df,
108110
from_parquet,
109111
from_query,
112+
get_profiling_information,
110113
get_table_names,
111114
install_extension,
112115
interrupt,
@@ -310,9 +313,11 @@
310313
"default_connection",
311314
"description",
312315
"df",
316+
"disable_profiling",
313317
"distinct",
314318
"dtype",
315319
"duplicate",
320+
"enable_profiling",
316321
"enum_type",
317322
"execute",
318323
"executemany",
@@ -333,6 +338,7 @@
333338
"from_df",
334339
"from_parquet",
335340
"from_query",
341+
"get_profiling_information",
336342
"get_table_names",
337343
"install_extension",
338344
"interrupt",

scripts/connection_methods.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,5 +1093,30 @@
10931093
}
10941094
],
10951095
"return": "None"
1096+
},
1097+
{
1098+
"name": "get_profiling_information",
1099+
"function": "GetProfilingInformation",
1100+
"docs": "Get profiling information for a query",
1101+
"args": [
1102+
{
1103+
"name": "format",
1104+
"default": "JSON",
1105+
"type": "Optional[str]"
1106+
}
1107+
],
1108+
"return": "str"
1109+
},
1110+
{
1111+
"name": "enable_profiling",
1112+
"function": "EnableProfiling",
1113+
"docs": "Enable profiling for a connection",
1114+
"return": "None"
1115+
},
1116+
{
1117+
"name": "disable_profiling",
1118+
"function": "DisableProfiling",
1119+
"docs": "Disable profiling for a connection",
1120+
"return": "None"
10961121
}
10971122
]

scripts/generate_connection_stubs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
os.chdir(Path(__file__).parent)
66

77
JSON_PATH = "connection_methods.json"
8-
DUCKDB_STUBS_FILE = Path("..") / "duckdb" / "__init__.pyi"
8+
DUCKDB_STUBS_FILE = Path("..") / "_duckdb-stubs" / "__init__.pyi"
99

1010
START_MARKER = " # START OF CONNECTION METHODS"
1111
END_MARKER = " # END OF CONNECTION METHODS"

src/duckdb_py/duckdb_python.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,34 @@ static void InitializeConnectionMethods(py::module_ &m) {
124124
},
125125
"Check if a filesystem with the provided name is currently registered", py::arg("name"), py::kw_only(),
126126
py::arg("connection") = py::none());
127+
m.def(
128+
"get_profiling_information",
129+
[](const py::str &format, shared_ptr<DuckDBPyConnection> conn = nullptr) {
130+
if (!conn) {
131+
conn = DuckDBPyConnection::DefaultConnection();
132+
}
133+
return conn->GetProfilingInformation(format);
134+
},
135+
"Get profiling information from a query", py::kw_only(), py::arg("format") = "json",
136+
py::arg("connection") = py::none());
137+
m.def(
138+
"enable_profiling",
139+
[](shared_ptr<DuckDBPyConnection> conn = nullptr) {
140+
if (!conn) {
141+
conn = DuckDBPyConnection::DefaultConnection();
142+
}
143+
return conn->EnableProfiling();
144+
},
145+
"Enable profiling for the current connection", py::kw_only(), py::arg("connection") = py::none());
146+
m.def(
147+
"disable_profiling",
148+
[](shared_ptr<DuckDBPyConnection> conn = nullptr) {
149+
if (!conn) {
150+
conn = DuckDBPyConnection::DefaultConnection();
151+
}
152+
return conn->DisableProfiling();
153+
},
154+
"Disable profiling for the current connection", py::kw_only(), py::arg("connection") = py::none());
127155
m.def(
128156
"create_function",
129157
[](const string &name, const py::function &udf, const py::object &arguments = py::none(),

src/duckdb_py/include/duckdb_python/pyconnection/pyconnection.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,11 @@ struct DuckDBPyConnection : public enable_shared_from_this<DuckDBPyConnection> {
337337
py::list ListFilesystems();
338338
bool FileSystemIsRegistered(const string &name);
339339

340+
// Profiling info
341+
py::str GetProfilingInformation(const py::str &format = "json");
342+
void EnableProfiling();
343+
void DisableProfiling();
344+
340345
//! Default connection to an in-memory database
341346
static DefaultConnectionHolder default_connection;
342347
//! Caches and provides an interface to get frequently used modules+subtypes

src/duckdb_py/pyconnection.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "duckdb/catalog/default/default_types.hpp"
44
#include "duckdb/common/arrow/arrow.hpp"
55
#include "duckdb/common/enums/file_compression_type.hpp"
6+
#include "duckdb/common/enums/profiler_format.hpp"
67
#include "duckdb/common/printer.hpp"
78
#include "duckdb/common/types.hpp"
89
#include "duckdb/common/types/vector.hpp"
@@ -285,6 +286,9 @@ static void InitializeConnectionMethods(py::class_<DuckDBPyConnection, shared_pt
285286
py::arg("extension"), py::kw_only(), py::arg("force_install") = false, py::arg("repository") = py::none(),
286287
py::arg("repository_url") = py::none(), py::arg("version") = py::none());
287288
m.def("load_extension", &DuckDBPyConnection::LoadExtension, "Load an installed extension", py::arg("extension"));
289+
m.def("get_profiling_information", &DuckDBPyConnection::GetProfilingInformation, "Get profiling information for a query", py::arg("format") = "json");
290+
m.def("enable_profiling", &DuckDBPyConnection::EnableProfiling, "Enable profiling for subsequent queries");
291+
m.def("disable_profiling", &DuckDBPyConnection::DisableProfiling, "Disable profiling for subsequent queries");
288292
} // END_OF_CONNECTION_METHODS
289293

290294
void DuckDBPyConnection::UnregisterFilesystem(const py::str &name) {
@@ -331,6 +335,39 @@ py::list DuckDBPyConnection::ListFilesystems() {
331335
return names;
332336
}
333337

338+
py::str DuckDBPyConnection::GetProfilingInformation(const py::str &format) {
339+
// We want to expose ProfilerPrintFormat as a string to Python users
340+
ProfilerPrintFormat format_enum;
341+
if (format == "query_tree") {
342+
format_enum = ProfilerPrintFormat::QUERY_TREE;
343+
} else if (format == "json") {
344+
format_enum = ProfilerPrintFormat::JSON;
345+
} else if (format == "query_tree_optimizer") {
346+
format_enum = ProfilerPrintFormat::QUERY_TREE_OPTIMIZER;
347+
} else if (format == "no_output") {
348+
format_enum = ProfilerPrintFormat::NO_OUTPUT;
349+
} else if (format == "html") {
350+
format_enum = ProfilerPrintFormat::HTML;
351+
} else if (format == "graphviz") {
352+
format_enum = ProfilerPrintFormat::GRAPHVIZ;
353+
} else {
354+
throw InvalidInputException("Invalid ProfilerPrintFormat string: " + std::string(format) + ". Valid options are: query_tree, json, query_tree_optimizer, no_output, html, graphviz.");
355+
}
356+
auto &connection = con.GetConnection();
357+
py::str profiling_info = connection.GetProfilingInformation(format_enum);
358+
return profiling_info;
359+
}
360+
361+
void DuckDBPyConnection::EnableProfiling() {
362+
auto &connection = con.GetConnection();
363+
connection.EnableProfiling();
364+
}
365+
366+
void DuckDBPyConnection::DisableProfiling() {
367+
auto &connection = con.GetConnection();
368+
connection.DisableProfiling();
369+
}
370+
334371
py::list DuckDBPyConnection::ExtractStatements(const string &query) {
335372
py::list result;
336373
auto &connection = con.GetConnection();

0 commit comments

Comments
 (0)