@@ -7,38 +7,49 @@ std::unordered_map<string, unique_ptr<std::mutex>> RefreshLocks::view_mutexes_;
77std::unordered_map<string, unique_ptr<std::mutex>> RefreshLocks::delta_mutexes_;
88std::unordered_map<const Catalog *, unique_ptr<DeltaCatalogPhaseGate>> RefreshLocks::delta_catalog_gates_;
99
10- void DeltaCatalogPhaseGate::EnterWrite () {
10+ static bool HasOtherOwner (const unordered_map<ClientContext *, idx_t > &owners, ClientContext &owner) {
11+ return !owners.empty () && (owners.size () > 1 || owners.find (&owner) == owners.end ());
12+ }
13+
14+ void DeltaCatalogPhaseGate::EnterWrite (ClientContext &owner) {
1115 std::unique_lock<mutex> guard (lock);
1216 // Once a refresh is waiting, stop admitting new writers so the finite set of
13- // active transactions can drain. Writers otherwise run concurrently.
14- condition.wait (guard, [&]() { return active_refreshes == 0 && waiting_refreshes == 0 ; });
15- active_writers++;
17+ // active transactions can drain. The refresh owner's own later DML remains
18+ // reentrant, which lets transaction-local DML and refresh compose safely.
19+ condition.wait (guard, [&]() {
20+ bool owner_is_active = active_writers.find (&owner) != active_writers.end () ||
21+ active_refreshes.find (&owner) != active_refreshes.end ();
22+ return !HasOtherOwner (active_refreshes, owner) && (waiting_refreshes == 0 || owner_is_active);
23+ });
24+ active_writers[&owner]++;
1625}
1726
18- void DeltaCatalogPhaseGate::ExitWrite () {
27+ void DeltaCatalogPhaseGate::ExitWrite (ClientContext &owner ) {
1928 lock_guard<mutex> guard (lock);
20- D_ASSERT (active_writers > 0 );
21- active_writers-- ;
22- if (active_writers == 0 ) {
23- condition. notify_all ( );
29+ auto entry = active_writers. find (&owner );
30+ D_ASSERT (entry != active_writers. end () && entry-> second > 0 ) ;
31+ if (--entry-> second == 0 ) {
32+ active_writers. erase (entry );
2433 }
34+ condition.notify_all ();
2535}
2636
27- void DeltaCatalogPhaseGate::EnterRefresh () {
37+ void DeltaCatalogPhaseGate::EnterRefresh (ClientContext &owner ) {
2838 std::unique_lock<mutex> guard (lock);
2939 waiting_refreshes++;
30- condition.wait (guard, [&]() { return active_writers == 0 ; });
40+ condition.wait (guard, [&]() { return ! HasOtherOwner ( active_writers, owner) ; });
3141 waiting_refreshes--;
32- active_refreshes++;
42+ active_refreshes[&owner] ++;
3343}
3444
35- void DeltaCatalogPhaseGate::ExitRefresh () {
45+ void DeltaCatalogPhaseGate::ExitRefresh (ClientContext &owner ) {
3646 lock_guard<mutex> guard (lock);
37- D_ASSERT (active_refreshes > 0 );
38- active_refreshes-- ;
39- if (active_refreshes == 0 ) {
40- condition. notify_all ( );
47+ auto entry = active_refreshes. find (&owner );
48+ D_ASSERT (entry != active_refreshes. end () && entry-> second > 0 ) ;
49+ if (--entry-> second == 0 ) {
50+ active_refreshes. erase (entry );
4151 }
52+ condition.notify_all ();
4253}
4354
4455std::mutex &RefreshLocks::GetViewMutex (const string &view_name) {
@@ -88,27 +99,52 @@ void RefreshLocks::UnlockDelta(const string &delta_table_name) {
8899 GetDeltaMutex (delta_table_name).unlock ();
89100}
90101
91- void RefreshLocks::EnterDeltaWrite (Catalog &catalog) {
92- GetDeltaCatalogGate (catalog).EnterWrite ();
102+ void RefreshLocks::EnterDeltaWrite (ClientContext &owner, Catalog &catalog) {
103+ GetDeltaCatalogGate (catalog).EnterWrite (owner );
93104}
94105
95- void RefreshLocks::ExitDeltaWrite (Catalog &catalog) {
96- GetDeltaCatalogGate (catalog).ExitWrite ();
106+ void RefreshLocks::ExitDeltaWrite (ClientContext &owner, Catalog &catalog) {
107+ GetDeltaCatalogGate (catalog).ExitWrite (owner );
97108}
98109
99- void RefreshLocks::EnterDeltaRefresh (Catalog &catalog) {
100- GetDeltaCatalogGate (catalog).EnterRefresh ();
110+ void RefreshLocks::EnterDeltaRefresh (ClientContext &owner, Catalog &catalog) {
111+ GetDeltaCatalogGate (catalog).EnterRefresh (owner );
101112}
102113
103- void RefreshLocks::ExitDeltaRefresh (Catalog &catalog) {
104- GetDeltaCatalogGate (catalog).ExitRefresh ();
114+ void RefreshLocks::ExitDeltaRefresh (ClientContext &owner, Catalog &catalog) {
115+ GetDeltaCatalogGate (catalog).ExitRefresh (owner );
105116}
106117
107118TransactionalMVLockState &TransactionalMVLockState::Get (ClientContext &context) {
108- return *context.registered_state ->GetOrCreate <TransactionalMVLockState>(" openivm_transactional_mv_locks" );
119+ auto state = context.registered_state ->GetOrCreate <TransactionalMVLockState>(" openivm_transactional_mv_locks" );
120+ state->owner = &context;
121+ return *state;
122+ }
123+
124+ optional_ptr<TransactionalMVLockState> TransactionalMVLockState::TryGet (ClientContext &context) {
125+ return context.registered_state ->Get <TransactionalMVLockState>(" openivm_transactional_mv_locks" );
126+ }
127+
128+ bool TransactionalMVLockState::OwnsRefreshDelta (Catalog &catalog, const string &delta_table_name) const {
129+ return locked_catalogs.find (&catalog) != locked_catalogs.end () &&
130+ locked_delta_tables.find (delta_table_name) != locked_delta_tables.end ();
109131}
110132
111- void TransactionalMVLockState::Acquire (const vector<string> &view_names, const vector<string> &delta_table_names) {
133+ void TransactionalMVLockState::Acquire (const vector<string> &view_names, const vector<string> &delta_table_names,
134+ const vector<Catalog *> &source_catalogs) {
135+ if (!owner) {
136+ throw InternalException (" OpenIVM transactional lock state has no owning client context" );
137+ }
138+ auto sorted_catalogs = source_catalogs;
139+ std::sort (sorted_catalogs.begin (), sorted_catalogs.end (),
140+ [](Catalog *left, Catalog *right) { return left->GetName () < right->GetName (); });
141+ sorted_catalogs.erase (std::unique (sorted_catalogs.begin (), sorted_catalogs.end ()), sorted_catalogs.end ());
142+ for (auto catalog : sorted_catalogs) {
143+ if (locked_catalogs.insert (catalog).second ) {
144+ catalog_guards.push_back (make_uniq<DeltaCatalogRefreshGuard>(*owner, *catalog));
145+ }
146+ }
147+
112148 auto sorted_views = view_names;
113149 std::sort (sorted_views.begin (), sorted_views.end ());
114150 sorted_views.erase (std::unique (sorted_views.begin (), sorted_views.end ()), sorted_views.end ());
@@ -139,8 +175,10 @@ void TransactionalMVLockState::TransactionRollback(MetaTransaction &transaction,
139175void TransactionalMVLockState::Release () {
140176 delta_guards.clear ();
141177 view_guards.clear ();
178+ catalog_guards.clear ();
142179 locked_delta_tables.clear ();
143180 locked_views.clear ();
181+ locked_catalogs.clear ();
144182}
145183
146184} // namespace duckdb
0 commit comments