|
1 | 1 | #include "duckdb_python/pyconnection/pyconnection.hpp" |
2 | 2 |
|
| 3 | +#include "duckdb/catalog/catalog.hpp" |
3 | 4 | #include "duckdb/catalog/default/default_types.hpp" |
4 | 5 | #include "duckdb/common/arrow/arrow.hpp" |
5 | 6 | #include "duckdb/common/enums/file_compression_type.hpp" |
@@ -73,6 +74,17 @@ shared_ptr<PythonImportCache> DuckDBPyConnection::import_cache = nullptr; |
73 | 74 | PythonEnvironmentType DuckDBPyConnection::environment = PythonEnvironmentType::NORMAL; // NOLINT: allow global |
74 | 75 | std::string DuckDBPyConnection::formatted_python_version = ""; |
75 | 76 |
|
| 77 | +static shared_ptr<PythonRegisteredObjectState> GetPythonRegisteredObjectState(ClientContext &context) { |
| 78 | + return context.registered_state->GetOrCreate<PythonRegisteredObjectState>(PythonRegisteredObjectState::Key); |
| 79 | +} |
| 80 | + |
| 81 | +static bool TemporaryObjectExists(ClientContext &context, const string &name) { |
| 82 | + auto &catalog = Catalog::GetCatalog(context, TEMP_CATALOG); |
| 83 | + EntryLookupInfo lookup_info(CatalogType::TABLE_ENTRY, name); |
| 84 | + auto entry = catalog.GetEntry(context, DEFAULT_SCHEMA, lookup_info, OnEntryNotFound::RETURN_NULL); |
| 85 | + return entry != nullptr; |
| 86 | +} |
| 87 | + |
76 | 88 | DuckDBPyConnection::~DuckDBPyConnection() { |
77 | 89 | try { |
78 | 90 | // The native Connection / DuckDB teardown is pure C++ work — release |
@@ -774,11 +786,16 @@ shared_ptr<DuckDBPyConnection> DuckDBPyConnection::RegisterPythonObject(const st |
774 | 786 | const py::object &python_object) { |
775 | 787 | auto &connection = con.GetConnection(); |
776 | 788 | auto &client = *connection.context; |
777 | | - auto object = PythonReplacementScan::ReplacementObject(python_object, name, client); |
778 | | - auto view_rel = make_shared_ptr<ViewRelation>(connection.context, std::move(object), name); |
779 | | - bool replace = registered_objects.count(name); |
780 | | - view_rel->CreateView(name, replace, true); |
781 | | - registered_objects.insert(name); |
| 789 | + auto registered_state = GetPythonRegisteredObjectState(client); |
| 790 | + if (!registered_state->Contains(name)) { |
| 791 | + bool temp_object_exists = false; |
| 792 | + client.RunFunctionInTransaction([&]() { temp_object_exists = TemporaryObjectExists(client, name); }, false); |
| 793 | + if (temp_object_exists) { |
| 794 | + throw CatalogException("View with name \"%s\" already exists!", name); |
| 795 | + } |
| 796 | + } |
| 797 | + PythonReplacementScan::ReplacementObject(python_object, name, client); |
| 798 | + registered_state->Register(name, python_object); |
782 | 799 | return shared_from_this(); |
783 | 800 | } |
784 | 801 |
|
@@ -1852,15 +1869,12 @@ unordered_set<string> DuckDBPyConnection::GetTableNames(const string &query, boo |
1852 | 1869 |
|
1853 | 1870 | shared_ptr<DuckDBPyConnection> DuckDBPyConnection::UnregisterPythonObject(const string &name) { |
1854 | 1871 | auto &connection = con.GetConnection(); |
1855 | | - if (!registered_objects.count(name)) { |
| 1872 | + auto registered_state = GetPythonRegisteredObjectState(*connection.context); |
| 1873 | + if (!registered_state->Contains(name)) { |
1856 | 1874 | return shared_from_this(); |
1857 | 1875 | } |
1858 | 1876 | D_ASSERT(py::gil_check()); |
1859 | | - py::gil_scoped_release release; |
1860 | | - // FIXME: DROP TEMPORARY VIEW? doesn't exist? |
1861 | | - const auto quoted_name = KeywordHelper::WriteOptionallyQuoted(name, '\"'); |
1862 | | - connection.Query("DROP VIEW " + quoted_name + ""); |
1863 | | - registered_objects.erase(name); |
| 1877 | + registered_state->Unregister(name); |
1864 | 1878 | return shared_from_this(); |
1865 | 1879 | } |
1866 | 1880 |
|
|
0 commit comments