1 | #include "duckdb/transaction/meta_transaction.hpp" |
2 | #include "duckdb/main/client_context.hpp" |
3 | #include "duckdb/main/attached_database.hpp" |
4 | #include "duckdb/transaction/transaction_manager.hpp" |
5 | |
6 | namespace duckdb { |
7 | |
8 | MetaTransaction::MetaTransaction(ClientContext &context_p, timestamp_t start_timestamp_p, idx_t catalog_version_p) |
9 | : context(context_p), start_timestamp(start_timestamp_p), catalog_version(catalog_version_p), read_only(true), |
10 | active_query(MAXIMUM_QUERY_ID), modified_database(nullptr) { |
11 | } |
12 | |
13 | MetaTransaction &MetaTransaction::Get(ClientContext &context) { |
14 | return context.transaction.ActiveTransaction(); |
15 | } |
16 | |
17 | ValidChecker &ValidChecker::Get(MetaTransaction &transaction) { |
18 | return transaction.transaction_validity; |
19 | } |
20 | |
21 | Transaction &Transaction::Get(ClientContext &context, AttachedDatabase &db) { |
22 | auto &meta_transaction = MetaTransaction::Get(context); |
23 | return meta_transaction.GetTransaction(db); |
24 | } |
25 | |
26 | Transaction &MetaTransaction::GetTransaction(AttachedDatabase &db) { |
27 | auto entry = transactions.find(x: &db); |
28 | if (entry == transactions.end()) { |
29 | auto new_transaction = db.GetTransactionManager().StartTransaction(context); |
30 | if (!new_transaction) { |
31 | throw InternalException("StartTransaction did not return a valid transaction" ); |
32 | } |
33 | new_transaction->active_query = active_query; |
34 | all_transactions.push_back(x: &db); |
35 | transactions[&db] = new_transaction; |
36 | return *new_transaction; |
37 | } else { |
38 | D_ASSERT(entry->second->active_query == active_query); |
39 | return *entry->second; |
40 | } |
41 | } |
42 | |
43 | Transaction &Transaction::Get(ClientContext &context, Catalog &catalog) { |
44 | return Transaction::Get(context, db&: catalog.GetAttached()); |
45 | } |
46 | |
47 | string MetaTransaction::Commit() { |
48 | string error; |
49 | // commit transactions in reverse order |
50 | for (idx_t i = all_transactions.size(); i > 0; i--) { |
51 | auto db = all_transactions[i - 1]; |
52 | auto entry = transactions.find(x: db.get()); |
53 | if (entry == transactions.end()) { |
54 | throw InternalException("Could not find transaction corresponding to database in MetaTransaction" ); |
55 | } |
56 | auto &transaction_manager = db->GetTransactionManager(); |
57 | auto transaction = entry->second; |
58 | if (error.empty()) { |
59 | // commit |
60 | error = transaction_manager.CommitTransaction(context, transaction); |
61 | } else { |
62 | // we have encountered an error previously - roll back subsequent entries |
63 | transaction_manager.RollbackTransaction(transaction); |
64 | } |
65 | } |
66 | return error; |
67 | } |
68 | |
69 | void MetaTransaction::Rollback() { |
70 | // rollback transactions in reverse order |
71 | for (idx_t i = all_transactions.size(); i > 0; i--) { |
72 | auto db = all_transactions[i - 1]; |
73 | auto &transaction_manager = db->GetTransactionManager(); |
74 | auto entry = transactions.find(x: db.get()); |
75 | D_ASSERT(entry != transactions.end()); |
76 | auto transaction = entry->second; |
77 | transaction_manager.RollbackTransaction(transaction); |
78 | } |
79 | } |
80 | |
81 | idx_t MetaTransaction::GetActiveQuery() { |
82 | return active_query; |
83 | } |
84 | |
85 | void MetaTransaction::SetActiveQuery(transaction_t query_number) { |
86 | active_query = query_number; |
87 | for (auto &entry : transactions) { |
88 | entry.second->active_query = query_number; |
89 | } |
90 | } |
91 | |
92 | void MetaTransaction::ModifyDatabase(AttachedDatabase &db) { |
93 | if (db.IsSystem() || db.IsTemporary()) { |
94 | // we can always modify the system and temp databases |
95 | return; |
96 | } |
97 | if (!modified_database) { |
98 | modified_database = &db; |
99 | return; |
100 | } |
101 | if (&db != modified_database.get()) { |
102 | throw TransactionException( |
103 | "Attempting to write to database \"%s\" in a transaction that has already modified database \"%s\" - a " |
104 | "single transaction can only write to a single attached database." , |
105 | db.GetName(), modified_database->GetName()); |
106 | } |
107 | } |
108 | |
109 | } // namespace duckdb |
110 | |