1 | /* |
2 | * PROGRAM: JRD Access Method |
3 | * MODULE: tra.h |
4 | * DESCRIPTION: Transaction block definitions |
5 | * |
6 | * The contents of this file are subject to the Interbase Public |
7 | * License Version 1.0 (the "License"); you may not use this file |
8 | * except in compliance with the License. You may obtain a copy |
9 | * of the License at http://www.Inprise.com/IPL.html |
10 | * |
11 | * Software distributed under the License is distributed on an |
12 | * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express |
13 | * or implied. See the License for the specific language governing |
14 | * rights and limitations under the License. |
15 | * |
16 | * The Original Code was created by Inprise Corporation |
17 | * and its predecessors. Portions created by Inprise Corporation are |
18 | * Copyright (C) Inprise Corporation. |
19 | * |
20 | * All Rights Reserved. |
21 | * Contributor(s): ______________________________________. |
22 | * 2001.6.25 Claudio Valderrama: add dfw_delete_generator and dfw_delete_udf |
23 | * to the dfw_t enumeration. |
24 | * 2002.10.29 Nickolay Samofatov: Added support for savepoints |
25 | */ |
26 | |
27 | #ifndef JRD_TRA_H |
28 | #define JRD_TRA_H |
29 | |
30 | /* |
31 | * TMN: Fix this header! It should include any header it needs |
32 | * to define the types this header uses. |
33 | */ |
34 | |
35 | #include "../include/fb_blk.h" |
36 | #include "../common/classes/tree.h" |
37 | #include "../common/classes/GenericMap.h" |
38 | #include "../jrd/exe.h" |
39 | #include "../jrd/rpb_chain.h" |
40 | #include "../jrd/blb.h" // For bid structure |
41 | #include "../jrd/sbm.h" // For bid structure |
42 | #include "../jrd/sort.h" |
43 | |
44 | #include "../jrd/DatabaseSnapshot.h" |
45 | #include "../jrd/TempSpace.h" |
46 | #include "../jrd/obj.h" |
47 | #include "../jrd/EngineInterface.h" |
48 | |
49 | namespace EDS { |
50 | class Transaction; |
51 | } |
52 | |
53 | namespace Jrd { |
54 | |
55 | class blb; |
56 | class Lock; |
57 | class jrd_rel; |
58 | template <typename T> class vec; |
59 | class Savepoint; |
60 | class Record; |
61 | class VerbAction; |
62 | class ArrayField; |
63 | class Attachment; |
64 | class DeferredWork; |
65 | class DeferredJob; |
66 | class dsql_opn; |
67 | class UserManagement; |
68 | class thread_db; |
69 | |
70 | |
71 | //Moved to fb_types.h |
72 | //typedef ULONG TraNumber; |
73 | |
74 | // Moved to constants.h |
75 | //const TraNumber MAX_TRA_NUMBER = ~TraNumber(0); |
76 | |
77 | // Blobs active in transaction identified by bli_temp_id. Please keep this |
78 | // structure small as there can be huge amount of them floating in memory. |
79 | struct BlobIndex |
80 | { |
81 | ULONG bli_temp_id; |
82 | bool bli_materialized; |
83 | jrd_req* bli_request; |
84 | union |
85 | { |
86 | bid bli_blob_id; // ID of materialized blob |
87 | blb* bli_blob_object; // Blob object |
88 | }; |
89 | static const ULONG& generate(const void* /*sender*/, const BlobIndex& item) |
90 | { |
91 | return item.bli_temp_id; |
92 | } |
93 | // Empty default constructor to make it behave like POD structure |
94 | BlobIndex() {} |
95 | BlobIndex(ULONG temp_id, blb* blob_object) : |
96 | bli_temp_id(temp_id), bli_materialized(false), bli_request(NULL), |
97 | bli_blob_object(blob_object) |
98 | { } |
99 | }; |
100 | |
101 | typedef Firebird::BePlusTree<BlobIndex, ULONG, MemoryPool, BlobIndex> BlobIndexTree; |
102 | |
103 | // Transaction block |
104 | |
105 | struct CallerName |
106 | { |
107 | CallerName(int aType, const Firebird::MetaName& aName) |
108 | : type(aType), |
109 | name(aName) |
110 | { |
111 | } |
112 | |
113 | CallerName() |
114 | : type(obj_type_MAX) |
115 | { |
116 | } |
117 | |
118 | CallerName(const CallerName& o) |
119 | : type(o.type), |
120 | name(o.name) |
121 | { |
122 | } |
123 | |
124 | void operator =(const CallerName& o) |
125 | { |
126 | if (&o != this) |
127 | { |
128 | type = o.type; |
129 | name = o.name; |
130 | } |
131 | } |
132 | |
133 | int type; |
134 | Firebird::MetaName name; |
135 | }; |
136 | |
137 | const int DEFAULT_LOCK_TIMEOUT = -1; // infinite |
138 | const char* const TRA_BLOB_SPACE = "fb_blob_" ; |
139 | const char* const TRA_UNDO_SPACE = "fb_undo_" ; |
140 | |
141 | class jrd_tra : public pool_alloc<type_tra> |
142 | { |
143 | typedef Firebird::GenericMap<Firebird::Pair<Firebird::NonPooled<USHORT, SINT64> > > GenIdCache; |
144 | |
145 | public: |
146 | enum wait_t { |
147 | tra_no_wait, |
148 | tra_probe, |
149 | tra_wait |
150 | }; |
151 | |
152 | jrd_tra(MemoryPool* p, Firebird::MemoryStats* parent_stats, |
153 | Attachment* attachment, jrd_tra* outer) |
154 | : tra_attachment(attachment), |
155 | tra_pool(p), |
156 | tra_memory_stats(parent_stats), |
157 | tra_blobs_tree(p), |
158 | tra_blobs(outer ? outer->tra_blobs : &tra_blobs_tree), |
159 | tra_arrays(NULL), |
160 | tra_deferred_job(NULL), |
161 | tra_resources(*p), |
162 | tra_context_vars(*p), |
163 | tra_lock_timeout(DEFAULT_LOCK_TIMEOUT), |
164 | tra_timestamp(Firebird::TimeStamp::getCurrentTimeStamp()), |
165 | tra_stats(*p), |
166 | tra_open_cursors(*p), |
167 | tra_outer(outer), |
168 | tra_transactions(*p), |
169 | tra_sorts(*p), |
170 | tra_public_interface(NULL), |
171 | tra_gen_ids(NULL), |
172 | tra_interface(NULL), |
173 | tra_blob_space(NULL), |
174 | tra_undo_space(NULL), |
175 | tra_undo_record(NULL), |
176 | tra_user_management(NULL), |
177 | tra_autonomous_pool(NULL), |
178 | tra_autonomous_cnt(0) |
179 | { |
180 | } |
181 | |
182 | ~jrd_tra(); |
183 | |
184 | static jrd_tra* create(MemoryPool* pool, Attachment* attachment, jrd_tra* outer) |
185 | { |
186 | jrd_tra* const transaction = |
187 | FB_NEW(*pool) jrd_tra(pool, &attachment->att_memory_stats, attachment, outer); |
188 | |
189 | if (!outer) |
190 | { |
191 | pool->setStatsGroup(transaction->tra_memory_stats); |
192 | } |
193 | |
194 | return transaction; |
195 | } |
196 | |
197 | static void destroy(Attachment* const attachment, jrd_tra* const transaction) |
198 | { |
199 | if (transaction) |
200 | { |
201 | if (!attachment) |
202 | delete transaction; |
203 | else if (transaction->tra_outer) |
204 | { |
205 | jrd_tra* outer = transaction->tra_outer; |
206 | MemoryPool* const pool = transaction->tra_pool; |
207 | delete transaction; |
208 | outer->releaseAutonomousPool(pool); |
209 | } |
210 | else |
211 | { |
212 | MemoryPool* const pool = transaction->tra_pool; |
213 | Firebird::MemoryStats temp_stats; |
214 | pool->setStatsGroup(temp_stats); |
215 | delete transaction; |
216 | attachment->deletePool(pool); |
217 | } |
218 | } |
219 | } |
220 | |
221 | Attachment* getAttachment() |
222 | { |
223 | return tra_attachment; |
224 | } |
225 | |
226 | dsql_dbb* getDsqlAttachment() |
227 | { |
228 | return tra_attachment->att_dsql_instance; |
229 | } |
230 | |
231 | JTransaction* getInterface(); |
232 | void setInterface(JTransaction* jt); |
233 | |
234 | FB_API_HANDLE tra_public_handle; // Public handle |
235 | Attachment* tra_attachment; // database attachment |
236 | TraNumber tra_number; // transaction number |
237 | TraNumber tra_top; // highest transaction in snapshot |
238 | TraNumber tra_oldest; // oldest interesting transaction |
239 | TraNumber tra_oldest_active; // record versions older than this can be |
240 | // gargage-collected by this tx |
241 | TraNumber tra_att_oldest_active; // oldest active transaction in the same attachment |
242 | jrd_tra* tra_next; // next transaction in attachment |
243 | MemoryPool* const tra_pool; // pool for transaction |
244 | Firebird::MemoryStats tra_memory_stats; |
245 | BlobIndexTree tra_blobs_tree; // list of active blobs |
246 | BlobIndexTree* tra_blobs; // pointer to actual list of active blobs |
247 | ArrayField* tra_arrays; // Linked list of active arrays |
248 | Lock* tra_lock; // lock for transaction |
249 | vec<Lock*>* tra_relation_locks; // locks for relations |
250 | UInt32Bitmap* tra_commit_sub_trans; // commited sub-transactions |
251 | Savepoint* tra_save_point; // list of savepoints |
252 | Savepoint* tra_save_free; // free savepoints |
253 | SLONG tra_save_point_number; // next save point number to use |
254 | ULONG tra_flags; |
255 | DeferredJob* tra_deferred_job; // work deferred to commit time |
256 | ResourceList tra_resources; // resource existence list |
257 | Firebird::StringMap tra_context_vars; // Context variables for the transaction |
258 | traRpbList* tra_rpblist; // active record_param's of given transaction |
259 | UCHAR tra_use_count; // use count for safe AST delivery |
260 | UCHAR tra_callback_count; // callback count for 'execute statement' |
261 | SSHORT tra_lock_timeout; // in seconds, -1 means infinite, 0 means NOWAIT |
262 | ULONG tra_next_blob_id; // ID of the previous blob or array created in this transaction |
263 | const Firebird::TimeStamp tra_timestamp; // transaction start time |
264 | jrd_req* tra_requests; // Doubly linked list of requests active in this transaction |
265 | DatabaseSnapshot* tra_db_snapshot; // Database state snapshot (for monitoring purposes) |
266 | RuntimeStatistics tra_stats; |
267 | Firebird::Array<dsql_req*> tra_open_cursors; |
268 | bool tra_in_use; // transaction in use (can't be committed or rolled back) |
269 | jrd_tra* const tra_outer; // outer transaction of an autonomous transaction |
270 | CallerName tra_caller_name; // caller object name |
271 | Firebird::Array<UCHAR> tra_transactions; |
272 | SortOwner tra_sorts; |
273 | |
274 | EDS::Transaction *tra_ext_common; |
275 | //Transaction *tra_ext_two_phase; |
276 | Firebird::ITransaction* tra_public_interface; |
277 | GenIdCache* tra_gen_ids; |
278 | |
279 | private: |
280 | JTransaction* tra_interface; |
281 | TempSpace* tra_blob_space; // temp blob storage |
282 | TempSpace* tra_undo_space; // undo log storage |
283 | |
284 | Record* tra_undo_record; // temporary record used for the undo purposes |
285 | UserManagement* tra_user_management; |
286 | MemoryPool* tra_autonomous_pool; |
287 | USHORT tra_autonomous_cnt; |
288 | static const USHORT TRA_AUTONOMOUS_PER_POOL = 64; |
289 | |
290 | public: |
291 | MemoryPool* getAutonomousPool(); |
292 | void releaseAutonomousPool(MemoryPool* toRelease); |
293 | jrd_tra* getOuter(); |
294 | |
295 | SSHORT getLockWait() const |
296 | { |
297 | return -tra_lock_timeout; |
298 | } |
299 | |
300 | TempSpace* getBlobSpace() |
301 | { |
302 | if (tra_outer) |
303 | return tra_outer->getBlobSpace(); |
304 | |
305 | if (!tra_blob_space) |
306 | { |
307 | fb_assert(!tra_outer); |
308 | tra_blob_space = FB_NEW(*tra_pool) TempSpace(*tra_pool, TRA_BLOB_SPACE); |
309 | } |
310 | |
311 | return tra_blob_space; |
312 | } |
313 | |
314 | TempSpace* getUndoSpace() |
315 | { |
316 | if (!tra_undo_space) |
317 | { |
318 | tra_undo_space = FB_NEW(*tra_pool) TempSpace(*tra_pool, TRA_UNDO_SPACE); |
319 | } |
320 | |
321 | return tra_undo_space; |
322 | } |
323 | |
324 | Record* getUndoRecord(ULONG length) |
325 | { |
326 | if (!tra_undo_record || tra_undo_record->rec_length < length) |
327 | { |
328 | delete tra_undo_record; |
329 | tra_undo_record = FB_NEW_RPT(*tra_pool, length) Record(*tra_pool); |
330 | } |
331 | |
332 | memset(tra_undo_record, 0, sizeof(Record) + length); |
333 | |
334 | return tra_undo_record; |
335 | } |
336 | |
337 | UserManagement* getUserManagement(); |
338 | |
339 | GenIdCache* getGenIdCache() |
340 | { |
341 | if (!tra_gen_ids) |
342 | { |
343 | tra_gen_ids = FB_NEW(*tra_pool) GenIdCache(*tra_pool); |
344 | } |
345 | |
346 | return tra_gen_ids; |
347 | } |
348 | }; |
349 | |
350 | // System transaction is always transaction 0. |
351 | const TraNumber TRA_system_transaction = 0; |
352 | |
353 | // Flag definitions for tra_flags. |
354 | |
355 | const ULONG TRA_system = 0x1L; // system transaction |
356 | const ULONG TRA_prepared = 0x2L; // transaction is in limbo |
357 | const ULONG TRA_reconnected = 0x4L; // reconnect in progress |
358 | const ULONG TRA_degree3 = 0x8L; // serializeable transaction |
359 | const ULONG TRA_write = 0x10L; // transaction has written |
360 | const ULONG TRA_readonly = 0x20L; // transaction is readonly |
361 | const ULONG TRA_prepare2 = 0x40L; // transaction has updated RDB$TRANSACTIONS |
362 | const ULONG TRA_ignore_limbo = 0x80L; // ignore transactions in limbo |
363 | const ULONG TRA_invalidated = 0x100L; // transaction invalidated by failed write |
364 | const ULONG TRA_deferred_meta = 0x200L; // deferred meta work posted |
365 | const ULONG TRA_read_committed = 0x400L; // can see latest committed records |
366 | const ULONG TRA_autocommit = 0x800L; // autocommits all updates |
367 | const ULONG TRA_perform_autocommit = 0x1000L; // indicates autocommit is necessary |
368 | const ULONG TRA_rec_version = 0x2000L; // don't wait for uncommitted versions |
369 | const ULONG TRA_restart_requests = 0x4000L; // restart all requests in attachment |
370 | const ULONG TRA_no_auto_undo = 0x8000L; // don't start a savepoint in TRA_start |
371 | const ULONG TRA_precommitted = 0x10000L; // transaction committed at startup |
372 | const ULONG TRA_own_interface = 0x20000L; // tra_interface was created for internal needs |
373 | |
374 | // flags derived from TPB, see also transaction_options() at tra.cpp |
375 | const ULONG TRA_OPTIONS_MASK = (TRA_degree3 | TRA_readonly | TRA_ignore_limbo | TRA_read_committed | |
376 | TRA_autocommit | TRA_rec_version | TRA_no_auto_undo | TRA_restart_requests); |
377 | |
378 | const int TRA_MASK = 3; |
379 | //const int TRA_BITS_PER_TRANS = 2; |
380 | //const int TRA_TRANS_PER_BYTE = 4; |
381 | const int TRA_SHIFT = 2; |
382 | |
383 | #define TRANS_SHIFT(number) (((number) & TRA_MASK) << 1) |
384 | #define TRANS_OFFSET(number) ((number) >> TRA_SHIFT) |
385 | |
386 | // Transaction cleanup. If a database is never quiescent, look |
387 | // for "dead" active transactions every so often at transaction |
388 | // startup |
389 | |
390 | const int TRA_ACTIVE_CLEANUP = 100; |
391 | |
392 | // Transaction states. The first four are states found |
393 | // in the transaction inventory page; the last two are |
394 | // returned internally |
395 | |
396 | const int tra_active = 0; // Transaction is active |
397 | const int tra_limbo = 1; |
398 | const int tra_dead = 2; |
399 | const int tra_committed = 3; |
400 | const int tra_us = 4; // Transaction is us |
401 | const int tra_precommitted = 5; // Transaction is precommitted |
402 | |
403 | // The highest transaction number possible. This is 0x7fffffff if SLONG is 32 bits. |
404 | //#define MAX_TRA_NUMBER (~(1L << (BITS_PER_LONG - 1))) |
405 | |
406 | // Savepoint block |
407 | |
408 | class Savepoint : public pool_alloc<type_sav> |
409 | { |
410 | public: |
411 | ~Savepoint() |
412 | { |
413 | deleteActions(sav_verb_actions); |
414 | deleteActions(sav_verb_free); |
415 | } |
416 | |
417 | VerbAction* sav_verb_actions; // verb action list |
418 | VerbAction* sav_verb_free; // free verb actions |
419 | USHORT sav_verb_count; // Active verb count |
420 | SLONG sav_number; // save point number |
421 | Savepoint* sav_next; |
422 | USHORT sav_flags; |
423 | TEXT sav_name[MAX_SQL_IDENTIFIER_SIZE]; // Savepoint name |
424 | |
425 | private: |
426 | void deleteActions(VerbAction* list); |
427 | }; |
428 | |
429 | // Savepoint block flags. |
430 | |
431 | const int SAV_trans_level = 1; // savepoint was started by TRA_start |
432 | const int SAV_force_dfw = 2; // DFW is present even if savepoint is empty |
433 | const int SAV_user = 4; // named user savepoint as opposed to system ones |
434 | |
435 | // Maximum size in bytes of transaction-level savepoint data. |
436 | // When transaction-level savepoint gets past this size we drop it and use GC |
437 | // mechanisms to clean out changes done in transaction |
438 | const IPTR SAV_LARGE = 1024 * 32; |
439 | |
440 | // Deferred work blocks are used by the meta data handler to keep track |
441 | // of work deferred to commit time. This are usually used to perform |
442 | // meta data updates |
443 | |
444 | enum dfw_t { |
445 | dfw_null, |
446 | dfw_create_relation, |
447 | dfw_delete_relation, |
448 | dfw_update_format, |
449 | dfw_create_index, |
450 | dfw_delete_index, |
451 | dfw_compute_security, |
452 | dfw_add_file, |
453 | dfw_add_shadow, |
454 | dfw_delete_shadow, |
455 | dfw_modify_file, |
456 | dfw_erase_file, |
457 | dfw_create_field, |
458 | dfw_delete_field, |
459 | dfw_modify_field, |
460 | dfw_delete_global, |
461 | dfw_delete_rfr, |
462 | dfw_post_event, |
463 | dfw_create_trigger, |
464 | dfw_delete_trigger, |
465 | dfw_modify_trigger, |
466 | //dfw_load_triggers, |
467 | dfw_grant, |
468 | dfw_revoke, |
469 | dfw_scan_relation, |
470 | dfw_create_expression_index, |
471 | dfw_delete_expression_index, |
472 | dfw_create_procedure, |
473 | dfw_modify_procedure, |
474 | dfw_delete_procedure, |
475 | dfw_delete_prm, |
476 | dfw_create_collation, |
477 | dfw_delete_collation, |
478 | dfw_delete_exception, |
479 | //dfw_unlink_file, |
480 | dfw_delete_generator, |
481 | dfw_create_function, |
482 | dfw_modify_function, |
483 | dfw_delete_function, |
484 | dfw_add_difference, |
485 | dfw_delete_difference, |
486 | dfw_begin_backup, |
487 | dfw_end_backup, |
488 | dfw_user_management, |
489 | , |
490 | dfw_drop_package_body, |
491 | dfw_check_not_null, |
492 | dfw_store_view_context_type, |
493 | dfw_set_generator, |
494 | |
495 | // deferred works argument types |
496 | dfw_arg_index_name, // index name for dfw_delete_expression_index, mandatory |
497 | dfw_arg_partner_rel_id, // partner relation id for dfw_delete_index if index is FK, optional |
498 | dfw_arg_proc_name, // procedure name for dfw_delete_prm, mandatory |
499 | dfw_arg_force_computed, // we need to drop dependencies from a field that WAS computed |
500 | dfw_arg_check_blr, // check if BLR is still compilable |
501 | dfw_arg_rel_name, // relation name of a trigger |
502 | dfw_arg_trg_type, // trigger type |
503 | dfw_arg_new_name, // new name |
504 | dfw_arg_field_not_null, // set domain to not nullable |
505 | dfw_db_crypt, // change database encryption status |
506 | dfw_set_linger // set database linger |
507 | }; |
508 | |
509 | // Verb actions |
510 | |
511 | class UndoItem |
512 | { |
513 | public: |
514 | static const SINT64& generate(const void* /*sender*/, const UndoItem& item) |
515 | { |
516 | return item.number; |
517 | } |
518 | |
519 | UndoItem() {} |
520 | |
521 | UndoItem(RecordNumber recordNumber, UCHAR recordFlags) |
522 | : number(recordNumber.getValue()), |
523 | flags(recordFlags), |
524 | length(0), offset(0), format(NULL) |
525 | {} |
526 | |
527 | UndoItem(jrd_tra* transaction, RecordNumber recordNumber, const Record* record, UCHAR recordFlags) |
528 | : number(recordNumber.getValue()), |
529 | flags(recordFlags), |
530 | length(record->rec_length), |
531 | offset(0), |
532 | format(record->rec_format) |
533 | { |
534 | if (length) |
535 | { |
536 | offset = transaction->getUndoSpace()->allocateSpace(length); |
537 | transaction->getUndoSpace()->write(offset, record->rec_data, length); |
538 | } |
539 | } |
540 | |
541 | Record* setupRecord(jrd_tra* transaction, UCHAR newFlags = 0) |
542 | { |
543 | flags |= newFlags; |
544 | |
545 | Record* const record = transaction->getUndoRecord(length); |
546 | record->rec_number.setValue(number); |
547 | record->rec_flags = flags; |
548 | record->rec_length = length; |
549 | record->rec_format = format; |
550 | |
551 | if (length) |
552 | transaction->getUndoSpace()->read(offset, record->rec_data, length); |
553 | |
554 | return record; |
555 | } |
556 | |
557 | void release(jrd_tra* transaction) |
558 | { |
559 | if (length) |
560 | { |
561 | transaction->getUndoSpace()->releaseSpace(offset, length); |
562 | length = 0; |
563 | format = NULL; |
564 | } |
565 | } |
566 | |
567 | const UCHAR getFlags() const |
568 | { |
569 | return flags; |
570 | } |
571 | |
572 | ULONG getLength() const |
573 | { |
574 | return length; |
575 | } |
576 | |
577 | private: |
578 | SINT64 number; |
579 | UCHAR flags; |
580 | ULONG length; |
581 | offset_t offset; |
582 | const Format* format; |
583 | }; |
584 | |
585 | typedef Firebird::BePlusTree<UndoItem, SINT64, MemoryPool, UndoItem> UndoItemTree; |
586 | |
587 | class VerbAction : public pool_alloc<type_vct> |
588 | { |
589 | public: |
590 | ~VerbAction() |
591 | { |
592 | delete vct_records; |
593 | delete vct_undo; |
594 | } |
595 | |
596 | VerbAction* vct_next; // Next action within verb |
597 | jrd_rel* vct_relation; // Relation involved |
598 | RecordBitmap* vct_records; // Record involved |
599 | UndoItemTree* vct_undo; // Data for undo records |
600 | }; |
601 | |
602 | |
603 | inline void Savepoint::deleteActions(VerbAction* list) |
604 | { |
605 | while (list) |
606 | { |
607 | VerbAction* next = list->vct_next; |
608 | delete list; |
609 | list = next; |
610 | } |
611 | }; |
612 | |
613 | } //namespace Jrd |
614 | |
615 | #endif // JRD_TRA_H |
616 | |