]> git.stg.codes - stg.git/blob - libs/ibpp/transaction.cpp
Start replacing notifiers with subscriptions.
[stg.git] / libs / ibpp / transaction.cpp
1 ///////////////////////////////////////////////////////////////////////////////\r
2 //\r
3 //      File    : $Id: transaction.cpp,v 1.1 2007/05/05 17:00:43 faust Exp $\r
4 //      Subject : IBPP, Database class implementation\r
5 //\r
6 ///////////////////////////////////////////////////////////////////////////////\r
7 //\r
8 //      (C) Copyright 2000-2006 T.I.P. Group S.A. and the IBPP Team (www.ibpp.org)\r
9 //\r
10 //      The contents of this file are subject to the IBPP License (the "License");\r
11 //      you may not use this file except in compliance with the License.  You may\r
12 //      obtain a copy of the License at http://www.ibpp.org or in the 'license.txt'\r
13 //      file which must have been distributed along with this file.\r
14 //\r
15 //      This software, distributed under the License, is distributed on an "AS IS"\r
16 //      basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the\r
17 //      License for the specific language governing rights and limitations\r
18 //      under the License.\r
19 //\r
20 ///////////////////////////////////////////////////////////////////////////////\r
21 //\r
22 //      COMMENTS\r
23 //      * Tabulations should be set every four characters when editing this file.\r
24 //\r
25 ///////////////////////////////////////////////////////////////////////////////\r
26 \r
27 #ifdef _MSC_VER\r
28 #pragma warning(disable: 4786 4996)\r
29 #ifndef _DEBUG\r
30 #pragma warning(disable: 4702)\r
31 #endif\r
32 #endif\r
33 \r
34 #include "_ibpp.h"\r
35 \r
36 #ifdef HAS_HDRSTOP\r
37 #pragma hdrstop\r
38 #endif\r
39 \r
40 #include <algorithm>\r
41 \r
42 using namespace ibpp_internals;\r
43 \r
44 //      (((((((( OBJECT INTERFACE IMPLEMENTATION ))))))))\r
45 \r
46 void TransactionImpl::AttachDatabase(IBPP::Database db,\r
47         IBPP::TAM am, IBPP::TIL il, IBPP::TLR lr, IBPP::TFF flags)\r
48 {\r
49         if (db.intf() == 0)\r
50                 throw LogicExceptionImpl("Transaction::AttachDatabase",\r
51                                 _("Can't attach an unbound Database."));\r
52 \r
53         AttachDatabaseImpl(dynamic_cast<DatabaseImpl*>(db.intf()), am, il, lr, flags);\r
54 }\r
55 \r
56 void TransactionImpl::DetachDatabase(IBPP::Database db)\r
57 {\r
58         if (db.intf() == 0)\r
59                 throw LogicExceptionImpl("Transaction::DetachDatabase",\r
60                                 _("Can't detach an unbound Database."));\r
61 \r
62         DetachDatabaseImpl(dynamic_cast<DatabaseImpl*>(db.intf()));\r
63 }\r
64 \r
65 void TransactionImpl::AddReservation(IBPP::Database db,\r
66         const std::string& table, IBPP::TTR tr)\r
67 {\r
68         if (mHandle != 0)\r
69                 throw LogicExceptionImpl("Transaction::AddReservation",\r
70                                 _("Can't add table reservation if Transaction started."));\r
71         if (db.intf() == 0)\r
72                 throw LogicExceptionImpl("Transaction::AddReservation",\r
73                                 _("Can't add table reservation on an unbound Database."));\r
74 \r
75         // Find the TPB associated with this database\r
76         std::vector<DatabaseImpl*>::iterator pos =\r
77                 std::find(mDatabases.begin(), mDatabases.end(), dynamic_cast<DatabaseImpl*>(db.intf()));\r
78         if (pos != mDatabases.end())\r
79         {\r
80                 size_t index = pos - mDatabases.begin();\r
81                 TPB* tpb = mTPBs[index];\r
82                 \r
83                 // Now add the reservations to the TPB\r
84                 switch (tr)\r
85                 {\r
86                         case IBPP::trSharedWrite :\r
87                                         tpb->Insert(isc_tpb_lock_write);\r
88                                         tpb->Insert(table);\r
89                                         tpb->Insert(isc_tpb_shared);\r
90                                         break;\r
91                         case IBPP::trSharedRead :\r
92                                         tpb->Insert(isc_tpb_lock_read);\r
93                                         tpb->Insert(table);\r
94                                         tpb->Insert(isc_tpb_shared);\r
95                                         break;\r
96                         case IBPP::trProtectedWrite :\r
97                                         tpb->Insert(isc_tpb_lock_write);\r
98                                         tpb->Insert(table);\r
99                                         tpb->Insert(isc_tpb_protected);\r
100                                         break;\r
101                         case IBPP::trProtectedRead :\r
102                                         tpb->Insert(isc_tpb_lock_read);\r
103                                         tpb->Insert(table);\r
104                                         tpb->Insert(isc_tpb_protected);\r
105                                         break;\r
106                         /*default :\r
107                                         throw LogicExceptionImpl("Transaction::AddReservation",\r
108                                                 _("Illegal TTR value detected."));*/\r
109                 }\r
110         }\r
111         else throw LogicExceptionImpl("Transaction::AddReservation",\r
112                         _("The database connection you specified is not attached to this transaction."));\r
113 }\r
114 \r
115 void TransactionImpl::Start()\r
116 {\r
117         if (mHandle != 0) return;       // Already started anyway\r
118 \r
119         if (mDatabases.empty())\r
120                 throw LogicExceptionImpl("Transaction::Start", _("No Database is attached."));\r
121 \r
122         struct ISC_TEB\r
123         {\r
124                 ISC_LONG* db_ptr;\r
125                 ISC_LONG tpb_len;\r
126                 char* tpb_ptr;\r
127         } * teb = new ISC_TEB[mDatabases.size()];\r
128 \r
129         unsigned i;\r
130         for (i = 0; i < mDatabases.size(); i++)\r
131         {\r
132                 if (mDatabases[i]->GetHandle() == 0)\r
133                 {\r
134                         // All Databases must be connected to Start the transaction !\r
135                         delete [] teb;\r
136                         throw LogicExceptionImpl("Transaction::Start",\r
137                                         _("All attached Database should have been connected."));\r
138                 }\r
139                 teb[i].db_ptr = (ISC_LONG*) mDatabases[i]->GetHandlePtr();\r
140                 teb[i].tpb_len = mTPBs[i]->Size();\r
141                 teb[i].tpb_ptr = mTPBs[i]->Self();\r
142         }\r
143 \r
144         IBS status;\r
145         (*gds.Call()->m_start_multiple)(status.Self(), &mHandle, (short)mDatabases.size(), teb);\r
146         delete [] teb;\r
147         if (status.Errors())\r
148         {\r
149                 mHandle = 0;    // Should be, but better be sure...\r
150                 throw SQLExceptionImpl(status, "Transaction::Start");\r
151         }\r
152 }\r
153 \r
154 void TransactionImpl::Commit()\r
155 {\r
156         if (mHandle == 0)\r
157                 throw LogicExceptionImpl("Transaction::Commit", _("Transaction is not started."));\r
158                 \r
159         IBS status;\r
160 \r
161         (*gds.Call()->m_commit_transaction)(status.Self(), &mHandle);\r
162         if (status.Errors())\r
163                 throw SQLExceptionImpl(status, "Transaction::Commit");\r
164         mHandle = 0;    // Should be, better be sure\r
165 }\r
166 \r
167 void TransactionImpl::CommitRetain()\r
168 {\r
169         if (mHandle == 0)\r
170                 throw LogicExceptionImpl("Transaction::CommitRetain", _("Transaction is not started."));\r
171 \r
172         IBS status;\r
173 \r
174         (*gds.Call()->m_commit_retaining)(status.Self(), &mHandle);\r
175         if (status.Errors())\r
176                 throw SQLExceptionImpl(status, "Transaction::CommitRetain");\r
177 }\r
178 \r
179 void TransactionImpl::Rollback()\r
180 {\r
181         if (mHandle == 0) return;       // Transaction not started anyway\r
182 \r
183         IBS status;\r
184 \r
185         (*gds.Call()->m_rollback_transaction)(status.Self(), &mHandle);\r
186         if (status.Errors())\r
187                 throw SQLExceptionImpl(status, "Transaction::Rollback");\r
188         mHandle = 0;    // Should be, better be sure\r
189 }\r
190 \r
191 void TransactionImpl::RollbackRetain()\r
192 {\r
193         if (mHandle == 0)\r
194                 throw LogicExceptionImpl("Transaction::RollbackRetain", _("Transaction is not started."));\r
195 \r
196         IBS status;\r
197 \r
198         (*gds.Call()->m_rollback_retaining)(status.Self(), &mHandle);\r
199         if (status.Errors())\r
200                 throw SQLExceptionImpl(status, "Transaction::RollbackRetain");\r
201 }\r
202 \r
203 IBPP::ITransaction* TransactionImpl::AddRef()\r
204 {\r
205         ASSERTION(mRefCount >= 0);\r
206         ++mRefCount;\r
207         return this;\r
208 }\r
209 \r
210 void TransactionImpl::Release()\r
211 {\r
212         // Release cannot throw, except in DEBUG builds on assertion\r
213         ASSERTION(mRefCount >= 0);\r
214         --mRefCount;\r
215         try { if (mRefCount <= 0) delete this; }\r
216                 catch (...) { }\r
217 }\r
218 \r
219 //      (((((((( OBJECT INTERNAL METHODS ))))))))\r
220 \r
221 void TransactionImpl::Init()\r
222 {\r
223         mHandle = 0;\r
224         mDatabases.clear();\r
225         mTPBs.clear();\r
226         mStatements.clear();\r
227         mBlobs.clear();\r
228         mArrays.clear();\r
229 }\r
230 \r
231 void TransactionImpl::AttachStatementImpl(StatementImpl* st)\r
232 {\r
233         if (st == 0)\r
234                 throw LogicExceptionImpl("Transaction::AttachStatement",\r
235                                         _("Can't attach a 0 Statement object."));\r
236 \r
237         mStatements.push_back(st);\r
238 }\r
239 \r
240 void TransactionImpl::DetachStatementImpl(StatementImpl* st)\r
241 {\r
242         if (st == 0)\r
243                 throw LogicExceptionImpl("Transaction::DetachStatement",\r
244                                 _("Can't detach a 0 Statement object."));\r
245 \r
246         mStatements.erase(std::find(mStatements.begin(), mStatements.end(), st));\r
247 }\r
248 \r
249 void TransactionImpl::AttachBlobImpl(BlobImpl* bb)\r
250 {\r
251         if (bb == 0)\r
252                 throw LogicExceptionImpl("Transaction::AttachBlob",\r
253                                         _("Can't attach a 0 BlobImpl object."));\r
254 \r
255         mBlobs.push_back(bb);\r
256 }\r
257 \r
258 void TransactionImpl::DetachBlobImpl(BlobImpl* bb)\r
259 {\r
260         if (bb == 0)\r
261                 throw LogicExceptionImpl("Transaction::DetachBlob",\r
262                                 _("Can't detach a 0 BlobImpl object."));\r
263 \r
264         mBlobs.erase(std::find(mBlobs.begin(), mBlobs.end(), bb));\r
265 }\r
266 \r
267 void TransactionImpl::AttachArrayImpl(ArrayImpl* ar)\r
268 {\r
269         if (ar == 0)\r
270                 throw LogicExceptionImpl("Transaction::AttachArray",\r
271                                         _("Can't attach a 0 ArrayImpl object."));\r
272 \r
273         mArrays.push_back(ar);\r
274 }\r
275 \r
276 void TransactionImpl::DetachArrayImpl(ArrayImpl* ar)\r
277 {\r
278         if (ar == 0)\r
279                 throw LogicExceptionImpl("Transaction::DetachArray",\r
280                                 _("Can't detach a 0 ArrayImpl object."));\r
281 \r
282         mArrays.erase(std::find(mArrays.begin(), mArrays.end(), ar));\r
283 }\r
284 \r
285 void TransactionImpl::AttachDatabaseImpl(DatabaseImpl* dbi,\r
286         IBPP::TAM am, IBPP::TIL il, IBPP::TLR lr, IBPP::TFF flags)\r
287 {\r
288         if (mHandle != 0)\r
289                 throw LogicExceptionImpl("Transaction::AttachDatabase",\r
290                                 _("Can't attach a Database if Transaction started."));\r
291         if (dbi == 0)\r
292                 throw LogicExceptionImpl("Transaction::AttachDatabase",\r
293                                 _("Can't attach a null Database."));\r
294 \r
295         mDatabases.push_back(dbi);\r
296 \r
297         // Prepare a new TPB\r
298         TPB* tpb = new TPB;\r
299     if (am == IBPP::amRead) tpb->Insert(isc_tpb_read);\r
300     else tpb->Insert(isc_tpb_write);\r
301 \r
302         switch (il)\r
303         {\r
304                 case IBPP::ilConsistency :              tpb->Insert(isc_tpb_consistency); break;\r
305                 case IBPP::ilReadDirty :                tpb->Insert(isc_tpb_read_committed);\r
306                                                                 tpb->Insert(isc_tpb_rec_version); break;\r
307                 case IBPP::ilReadCommitted :    tpb->Insert(isc_tpb_read_committed);\r
308                                                                                 tpb->Insert(isc_tpb_no_rec_version); break;\r
309                 case IBPP::ilConcurrency :                                              tpb->Insert(isc_tpb_concurrency); break;\r
310         }\r
311 \r
312     if (lr == IBPP::lrNoWait) tpb->Insert(isc_tpb_nowait);\r
313     else tpb->Insert(isc_tpb_wait);\r
314 \r
315         if (flags & IBPP::tfIgnoreLimbo)        tpb->Insert(isc_tpb_ignore_limbo);\r
316         if (flags & IBPP::tfAutoCommit)         tpb->Insert(isc_tpb_autocommit);\r
317         if (flags & IBPP::tfNoAutoUndo)         tpb->Insert(isc_tpb_no_auto_undo);\r
318 \r
319         mTPBs.push_back(tpb);\r
320 \r
321         // Signals the Database object that it has been attached to the Transaction\r
322         dbi->AttachTransactionImpl(this);\r
323 }\r
324 \r
325 void TransactionImpl::DetachDatabaseImpl(DatabaseImpl* dbi)\r
326 {\r
327         if (mHandle != 0)\r
328                 throw LogicExceptionImpl("Transaction::DetachDatabase",\r
329                                 _("Can't detach a Database if Transaction started."));\r
330         if (dbi == 0)\r
331                 throw LogicExceptionImpl("Transaction::DetachDatabase",\r
332                                 _("Can't detach a null Database."));\r
333 \r
334         std::vector<DatabaseImpl*>::iterator pos =\r
335                 std::find(mDatabases.begin(), mDatabases.end(), dbi);\r
336         if (pos != mDatabases.end())\r
337         {\r
338                 size_t index = pos - mDatabases.begin();\r
339                 TPB* tpb = mTPBs[index];\r
340                 mDatabases.erase(pos);\r
341                 mTPBs.erase(mTPBs.begin()+index);\r
342                 delete tpb;\r
343         }\r
344 \r
345         // Signals the Database object that it has been detached from the Transaction\r
346         dbi->DetachTransactionImpl(this);\r
347 }\r
348 \r
349 TransactionImpl::TransactionImpl(DatabaseImpl* db,\r
350         IBPP::TAM am, IBPP::TIL il, IBPP::TLR lr, IBPP::TFF flags)\r
351         : mRefCount(0)\r
352 {\r
353         Init();\r
354         AttachDatabaseImpl(db, am, il, lr, flags);\r
355 }\r
356 \r
357 TransactionImpl::~TransactionImpl()\r
358 {\r
359         // Rollback the transaction if it was Started\r
360         try { if (Started()) Rollback(); }\r
361                 catch (...) { }\r
362 \r
363         // Let's detach cleanly all Blobs from this Transaction.\r
364         // No Blob object can still maintain pointers to this\r
365         // Transaction which is disappearing.\r
366         //\r
367         // We use a reverse traversal of the array to avoid loops.\r
368         // The array shrinks on each loop (mBbCount decreases).\r
369         // And during the deletion, there is a packing of the array through a\r
370         // copy of elements from the end to the beginning of the array.\r
371         try {\r
372                 while (mBlobs.size() > 0)\r
373                         mBlobs.back()->DetachTransactionImpl();\r
374         } catch (...) { }\r
375 \r
376         // Let's detach cleanly all Arrays from this Transaction.\r
377         // No Array object can still maintain pointers to this\r
378         // Transaction which is disappearing.\r
379         try {\r
380                 while (mArrays.size() > 0)\r
381                         mArrays.back()->DetachTransactionImpl();\r
382         } catch (...) { }\r
383 \r
384         // Let's detach cleanly all Statements from this Transaction.\r
385         // No Statement object can still maintain pointers to this\r
386         // Transaction which is disappearing.\r
387         try {\r
388                 while (mStatements.size() > 0)\r
389                         mStatements.back()->DetachTransactionImpl();\r
390         } catch (...) { }\r
391 \r
392         // Very important : let's detach cleanly all Databases from this\r
393         // Transaction. No Database object can still maintain pointers to this\r
394         // Transaction which is disappearing.\r
395         try {\r
396                 while (mDatabases.size() > 0)\r
397                 {\r
398                         size_t i = mDatabases.size()-1;\r
399                         DetachDatabaseImpl(mDatabases[i]);      // <-- remove link to database from mTPBs\r
400                                                                                         // array and destroy TPB object\r
401                                                                                         // Fixed : Maxim Abrashkin on 12 Jun 2002\r
402                         //mDatabases.back()->DetachTransaction(this);\r
403                 }\r
404         } catch (...) { }\r
405 }\r
406 \r
407 //\r
408 //      EOF\r
409 //\r