6
6
#include < Storages/StoragePython.h>
7
7
#include < TableFunctions/TableFunctionFactory.h>
8
8
#include < TableFunctions/TableFunctionPython.h>
9
+ #include < pybind11/gil.h>
9
10
#include < pybind11/pybind11.h>
10
11
#include < pybind11/pytypes.h>
12
+ #include < Poco/Logger.h>
11
13
#include < Common/Exception.h>
12
14
#include < Common/logger_useful.h>
13
15
@@ -21,8 +23,73 @@ extern const int PY_OBJECT_NOT_FOUND;
21
23
extern const int PY_EXCEPTION_OCCURED;
22
24
}
23
25
26
+ // Helper function to check if an object's class is or inherits from PyReader with a maximum depth
27
+ bool is_or_inherits_from_pyreader (const py::handle & obj, int depth = 3 )
28
+ {
29
+ // Base case: if depth limit reached, stop the recursion
30
+ if (depth == 0 )
31
+ return false ;
32
+
33
+ // Check directly if obj is an instance of PyReader
34
+ if (py::isinstance (obj, py::module_::import (" chdb" ).attr (" PyReader" )))
35
+ return true ;
36
+
37
+ // Check if obj's class or any of its bases is PyReader
38
+ py::object cls = obj.attr (" __class__" );
39
+ if (py::hasattr (cls, " __bases__" ))
40
+ {
41
+ for (auto base : cls.attr (" __bases__" ))
42
+ if (py::str (base.attr (" __name__" )).cast <std::string>() == " PyReader" || is_or_inherits_from_pyreader (base, depth - 1 ))
43
+ return true ;
44
+ }
45
+ return false ;
46
+ }
47
+
48
+ // Function to find instances of PyReader or classes derived from PyReader, filtered by variable name
49
+ std::vector<py::object> find_instances_of_pyreader (const std::string & var_name)
50
+ {
51
+ std::vector<py::object> instances;
52
+
53
+ // Access the main module and its global dictionary
54
+ py::dict globals = py::reinterpret_borrow<py::dict>(py::module_::import (" __main__" ).attr (" __dict__" ));
55
+
56
+ // Search in global scope
57
+ if (globals.contains (var_name))
58
+ {
59
+ py::object obj = globals[var_name.data ()];
60
+ if (py::isinstance<py::object>(obj) && py::hasattr (obj, " __class__" ))
61
+ {
62
+ if (is_or_inherits_from_pyreader (obj))
63
+ instances.push_back (obj);
64
+ }
65
+ }
66
+ if (!instances.empty ())
67
+ return instances;
68
+
69
+ // Check objects in the garbage collector if nothing found, filtering by var_name
70
+ // typically used to find objects that are not in the global scope, like in functions
71
+ LOG_DEBUG (&Poco::Logger::get (" TableFunctionPython" ), " Searching for PyReader objects in the garbage collector" );
72
+ py::module_ gc = py::module_::import (" gc" );
73
+ py::list all_objects = gc.attr (" get_objects" )();
74
+
75
+ for (auto obj : all_objects)
76
+ {
77
+ if (py::isinstance<py::object>(obj) && py::hasattr (obj, " __class__" ))
78
+ {
79
+ if (is_or_inherits_from_pyreader (obj) && py::str (obj.attr (" __class__" ).attr (" __name__" )).cast <std::string>() == var_name)
80
+ {
81
+ if (std::find (instances.begin (), instances.end (), obj) == instances.end ())
82
+ instances.push_back (obj.cast <py::object>());
83
+ }
84
+ }
85
+ }
86
+
87
+ return instances;
88
+ }
89
+
24
90
void TableFunctionPython::parseArguments (const ASTPtr & ast_function, ContextPtr context)
25
91
{
92
+ py::gil_scoped_acquire acquire;
26
93
const auto & func_args = ast_function->as <ASTFunction &>();
27
94
28
95
if (!func_args.arguments )
@@ -37,10 +104,10 @@ void TableFunctionPython::parseArguments(const ASTPtr & ast_function, ContextPtr
37
104
38
105
try
39
106
{
40
- py::dict global_vars = py::globals ();
41
- LOG_DEBUG (logger, " Globals content: {}" , String (py::str (global_vars)));
42
- py::dict main_vars = py::reinterpret_borrow<py::dict>(py::module_::import (" __main__" ).attr (" __dict__" ).ptr ());
43
- LOG_DEBUG (logger, " Main content: {}" , String (py::str (main_vars)));
107
+ // py::dict global_vars = py::globals();
108
+ // LOG_DEBUG(logger, "Globals content: {}", String(py::str(global_vars)));
109
+ // py::dict main_vars = py::reinterpret_borrow<py::dict>(py::module_::import("__main__").attr("__dict__").ptr());
110
+ // LOG_DEBUG(logger, "Main content: {}", String(py::str(main_vars)));
44
111
45
112
// get the py_reader_arg without quotes
46
113
auto py_reader_arg_str = py_reader_arg->as <ASTLiteral &>().value .safeGet <String>();
@@ -51,14 +118,19 @@ void TableFunctionPython::parseArguments(const ASTPtr & ast_function, ContextPtr
51
118
std::remove_if (py_reader_arg_str.begin (), py_reader_arg_str.end (), [](char c) { return c == ' \' ' || c == ' \" ' || c == ' `' ; }),
52
119
py_reader_arg_str.end ());
53
120
54
- // try global_vars first, if not found, try main_vars
55
- py::object obj_by_name
56
- = global_vars.contains (py_reader_arg_str.data ()) ? global_vars[py_reader_arg_str.data ()] : main_vars[py_reader_arg_str.data ()];
121
+ auto instances = find_instances_of_pyreader (py_reader_arg_str);
122
+ if (instances.empty ())
123
+ throw Exception (ErrorCodes::PY_OBJECT_NOT_FOUND, " PyReader object not found in the Python environment" );
124
+ if (instances.size () > 1 )
125
+ throw Exception (ErrorCodes::PY_EXCEPTION_OCCURED, " Multiple PyReader objects found in the Python environment" );
126
+
127
+ LOG_DEBUG (
128
+ logger,
129
+ " PyReader object found in Python environment with name: {} type: {}" ,
130
+ py_reader_arg_str,
131
+ py::str (instances[0 ].attr (" __class__" )).cast <std::string>());
57
132
58
- // check if obj_by_name is a PyReader object or a subclass object of PyReader
59
- if (!py::isinstance<PyReader>(obj_by_name))
60
- throw Exception (ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, " Python object is not a PyReader object" );
61
- reader = std::dynamic_pointer_cast<PyReader>(obj_by_name.cast <std::shared_ptr<PyReader>>());
133
+ reader = instances[0 ];
62
134
}
63
135
catch (py::error_already_set & e)
64
136
{
@@ -73,6 +145,7 @@ StoragePtr TableFunctionPython::executeImpl(
73
145
ColumnsDescription /* cached_columns*/ ,
74
146
bool is_insert_query) const
75
147
{
148
+ py::gil_scoped_acquire acquire;
76
149
if (!reader)
77
150
throw Exception (ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, " Python reader not initialized" );
78
151
0 commit comments