ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/client/FBTransaction.pas
Revision: 387
Committed: Wed Jan 19 13:34:42 2022 UTC (2 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 20804 byte(s)
Log Message:
Transactions started within a UDR are not forcibly closed if still active immediately prior to UDR exit

File Contents

# Content
1 (*
2 * Firebird Interface (fbintf). The fbintf components provide a set of
3 * Pascal language bindings for the Firebird API. Although predominantly
4 * a new development they include source code taken from IBX and may be
5 * considered a derived product. This software thus also includes the copyright
6 * notice and license conditions from IBX.
7 *
8 * Except for those parts dervied from IBX, contents of this file are subject
9 * to the Initial Developer's Public License Version 1.0 (the "License"); you
10 * may not use this file except in compliance with the License. You may obtain a
11 * copy of the License here:
12 *
13 * http://www.firebirdsql.org/index.php?op=doc&id=idpl
14 *
15 * Software distributed under the License is distributed on an "AS
16 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17 * implied. See the License for the specific language governing rights
18 * and limitations under the License.
19 *
20 * The Initial Developer of the Original Code is Tony Whyman.
21 *
22 * The Original Code is (C) 2016 Tony Whyman, MWA Software
23 * (http://www.mwasoftware.co.uk).
24 *
25 * All Rights Reserved.
26 *
27 * Contributor(s): ______________________________________.
28 *
29 *)
30 {************************************************************************}
31 { }
32 { Borland Delphi Visual Component Library }
33 { InterBase Express core components }
34 { }
35 { Copyright (c) 1998-2000 Inprise Corporation }
36 { }
37 { InterBase Express is based in part on the product }
38 { Free IB Components, written by Gregory H. Deatz for }
39 { Hoagland, Longo, Moran, Dunst & Doukas Company. }
40 { Free IB Components is used under license. }
41 { }
42 { The contents of this file are subject to the InterBase }
43 { Public License Version 1.0 (the "License"); you may not }
44 { use this file except in compliance with the License. You }
45 { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
46 { Software distributed under the License is distributed on }
47 { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
48 { express or implied. See the License for the specific language }
49 { governing rights and limitations under the License. }
50 { The Original Code was created by InterBase Software Corporation }
51 { and its successors. }
52 { Portions created by Inprise Corporation are Copyright (C) Inprise }
53 { Corporation. All Rights Reserved. }
54 { Contributor(s): Jeff Overcash }
55 { }
56 { IBX For Lazarus (Firebird Express) }
57 { Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
58 { Portions created by MWA Software are copyright McCallum Whyman }
59 { Associates Ltd 2011 - 2015 }
60 { }
61 {************************************************************************}
62 unit FBTransaction;
63 {$IFDEF MSWINDOWS}
64 {$DEFINE WINDOWS}
65 {$ENDIF}
66
67 {$IFDEF FPC}
68 {$mode delphi}
69 {$interfaces COM}
70 {$ENDIF}
71
72 interface
73
74 uses
75 Classes, SysUtils, IB, FBParamBlock, FBActivityMonitor, FBClientAPI, FBOutputBlock;
76
77 type
78 { TFBTransaction }
79
80 TFBTransaction = class(TActivityReporter, IActivityMonitor,ITransaction)
81 private
82 FFirebirdAPI: TFBClientAPI;
83 function GenerateTPB(sl: array of byte): ITPB;
84 protected
85 FTPB: ITPB;
86 FSeqNo: integer;
87 FDefaultCompletion: TTransactionCompletion;
88 FAttachments: array of IAttachment; {Keep reference to attachment - ensures
89 attachment cannot be freed before transaction}
90 FTransactionName: AnsiString;
91 FForeignHandle: boolean;
92 procedure CheckHandle;
93 function GetActivityIntf(att: IAttachment): IActivityMonitor; virtual; abstract;
94 function GetJournalIntf(Attachment: IAttachment): IJournallingHook;
95 procedure SetInterface(api: TFBClientAPI); virtual;
96 function GetTrInfo(ReqBuffer: PByte; ReqBufLen: integer): ITrInformation; virtual; abstract;
97 procedure InternalStartSingle(attachment: IAttachment); virtual; abstract;
98 procedure InternalStartMultiple; virtual; abstract;
99 function InternalCommit(Force: boolean): TTrCompletionState; virtual; abstract;
100 procedure InternalCommitRetaining; virtual; abstract;
101 function InternalRollback(Force: boolean): TTrCompletionState; virtual; abstract;
102 procedure InternalRollbackRetaining; virtual; abstract;
103 public
104 constructor Create(api: TFBClientAPI; Attachments: array of IAttachment; Params: array of byte; DefaultCompletion: TTransactionAction; aName: AnsiString); overload;
105 constructor Create(api: TFBClientAPI; Attachments: array of IAttachment; TPB: ITPB; DefaultCompletion: TTransactionAction; aName: AnsiString); overload;
106 constructor Create(api: TFBClientAPI; Attachment: IAttachment; Params: array of byte; DefaultCompletion: TTransactionAction; aName: AnsiString); overload;
107 constructor Create(api: TFBClientAPI; Attachment: IAttachment; TPB: ITPB; DefaultCompletion: TTransactionAction; aName: AnsiString); overload;
108 destructor Destroy; override;
109 procedure DoDefaultTransactionEnd(Force: boolean);
110 property FirebirdAPI: TFBClientAPI read FFirebirdAPI;
111
112 public
113 {ITransaction}
114 function getTPB: ITPB;
115 procedure PrepareForCommit;virtual; abstract;
116 function Commit(Force: boolean=false): TTrCompletionState;
117 procedure CommitRetaining;
118 function GetInTransaction: boolean; virtual; abstract;
119 function GetIsReadOnly: boolean;
120 function GetTransactionID: integer;
121 function GetAttachmentCount: integer;
122 function GetAttachment(index: integer): IAttachment;
123 function GetJournalingActive(attachment: IAttachment): boolean;
124 function GetDefaultCompletion: TTransactionCompletion;
125 function Rollback(Force: boolean=false): TTrCompletionState;
126 procedure RollbackRetaining;
127 procedure Start(DefaultCompletion: TTransactionCompletion=taCommit); overload;
128 procedure Start(TPB: ITPB; DefaultCompletion: TTransactionCompletion=taCommit); overload;
129 function GetTrInformation(Requests: array of byte): ITrInformation; overload;
130 function GetTrInformation(Request: byte): ITrInformation; overload;
131 function GetTransactionName: AnsiString;
132 procedure SetTransactionName(aValue: AnsiString);
133
134 property InTransaction: boolean read GetInTransaction;
135 property TransactionSeqNo: integer read FSeqNo;
136 end;
137
138 {The transaction user interface is used to force an action on the end of the
139 transaction.}
140
141 ITransactionUser = interface
142 ['{156fcdc9-a326-44b3-a82d-f23c6fb9f97c}']
143 procedure TransactionEnding(aTransaction: ITransaction; Force: boolean);
144 end;
145
146 { TTPBItem }
147
148 TTPBItem = class(TParamBlockItem,ITPBItem)
149 public
150 function getParamTypeName: AnsiString; override;
151 end;
152
153 { TTPB }
154
155 TTPB = class (TCustomParamBlock<TTPBItem,ITPBItem>, ITPB)
156 protected
157 function LookupItemType(ParamTypeName: AnsiString): byte; override;
158 public
159 constructor Create(api: TFBClientAPI);
160 function GetParamTypeName(ParamType: byte): Ansistring;
161 {$IFDEF FPC}
162 function ITPB.GetDPBParamTypeName = GetParamTypeName;
163 {$ELSE}
164 function GetDPBParamTypeName(ParamType: byte): Ansistring;
165 {$ENDIF}
166 function AsText: AnsiString;
167 end;
168
169 {$IFDEF FPC}
170 TTrInfoItem = class;
171
172 { TTrInfoItem }
173
174 TTrInfoItem = class(TOutputBlockItemGroup<TTrInfoItem,ITrInfoItem>,ITrInfoItem)
175 {$ELSE}
176 TTrInfoItem = class(TOutputBlockItemGroup<TOutputBlockItem,ITrInfoItem>,ITrInfoItem)
177 {$ENDIF}
178 public
179 procedure DecodeTraIsolation(var IsolationType, RecVersion: byte);
180 end;
181
182 { TTrInformation }
183
184 TTrInformation = class(TCustomOutputBlock<TTrInfoItem,ITrInfoItem>, ITrInformation)
185 protected
186 procedure DoParseBuffer; override;
187 public
188 constructor Create(api: TFBClientAPI; aSize: integer = DefaultBufferSize);
189 {$IFNDEF FPC}
190 function Find(ItemType: byte): ITrInfoItem;
191 {$ENDIF}
192 end;
193
194
195 implementation
196
197 uses FBMessages;
198
199 const
200 isc_tpb_last_tpb_constant = isc_tpb_at_snapshot_number;
201
202 TPBConstantNames: array[1..isc_tpb_last_tpb_constant] of string = (
203 'consistency',
204 'concurrency',
205 'shared',
206 'protected',
207 'exclusive',
208 'wait',
209 'nowait',
210 'read',
211 'write',
212 'lock_read',
213 'lock_write',
214 'verb_time',
215 'commit_time',
216 'ignore_limbo',
217 'read_committed',
218 'autocommit',
219 'rec_version',
220 'no_rec_version',
221 'restart_requests',
222 'no_auto_undo',
223 'lock_timeout',
224 'read_consistency',
225 'at_snapshot_number'
226 );
227
228 { TFBTransaction }
229
230 function TFBTransaction.GenerateTPB(sl: array of byte): ITPB;
231 var
232 i: Integer;
233 begin
234 Result := TTPB.Create(FFirebirdAPI);
235 for i := 0 to Length(sl) - 1 do
236 Result.Add(sl[i]);
237 end;
238
239 procedure TFBTransaction.CheckHandle;
240 begin
241 if not InTransaction then
242 IBError(ibxeNotInTransaction,[]);
243 end;
244
245 function TFBTransaction.GetJournalIntf(Attachment: IAttachment): IJournallingHook;
246 begin
247 Attachment.QueryInterface(IJournallingHook,Result)
248 end;
249
250 procedure TFBTransaction.SetInterface(api: TFBClientAPI);
251 begin
252 FFirebirdAPI := api;
253 end;
254
255 constructor TFBTransaction.Create(api: TFBClientAPI; Attachments: array of IAttachment;
256 Params: array of byte; DefaultCompletion: TTransactionAction; aName: AnsiString);
257 begin
258 Create(api, Attachments,GenerateTPB(Params), DefaultCompletion, aName);
259 end;
260
261 constructor TFBTransaction.Create(api: TFBClientAPI; Attachments: array of IAttachment; TPB: ITPB;
262 DefaultCompletion: TTransactionAction; aName: AnsiString);
263 var
264 i: Integer;
265 begin
266 inherited Create(nil);
267 FTransactionName := aName;
268 SetInterface(api);
269 if Length(Attachments) = 0 then
270 IBError(ibxeEmptyAttachmentsList,[nil]);
271
272 {make sure all attachments use same Firebird API}
273 for i := 0 to Length(Attachments) - 1 do
274 if Attachments[i].getFirebirdAPI.GetFBLibrary.GetHandle <> FFirebirdAPI.GetFBLibrary.GetHandle then
275 IBError(ibxeDifferentAPIs,[nil]);
276
277 SetLength(FAttachments,Length(Attachments));
278 for i := 0 to Length(Attachments) - 1 do
279 begin
280 AddMonitor(GetActivityIntf(Attachments[i]));
281 FAttachments[i] := Attachments[i];
282 end;
283 FTPB := TPB;
284 Start(DefaultCompletion);
285 end;
286
287 constructor TFBTransaction.Create(api: TFBClientAPI; Attachment: IAttachment;
288 Params: array of byte; DefaultCompletion: TTransactionAction; aName: AnsiString);
289 begin
290 Create(api,Attachment,GenerateTPB(Params),DefaultCompletion,aName);
291 end;
292
293 constructor TFBTransaction.Create(api: TFBClientAPI; Attachment: IAttachment; TPB: ITPB;
294 DefaultCompletion: TTransactionAction; aName: AnsiString);
295 begin
296 inherited Create(nil);
297 SetInterface(api);
298 AddMonitor(GetActivityIntf(Attachment));
299 SetLength(FAttachments,1);
300 FAttachments[0] := Attachment;
301 FTPB := TPB;
302 FTransactionName := aName;
303 Start(DefaultCompletion);
304 end;
305
306 destructor TFBTransaction.Destroy;
307 begin
308 DoDefaultTransactionEnd(false);
309 inherited Destroy;
310 end;
311
312 procedure TFBTransaction.DoDefaultTransactionEnd(Force: boolean);
313 var i: integer;
314 intf: IUnknown;
315 user: ITransactionUser;
316 begin
317 if InTransaction and not FForeignHandle then
318 begin
319 for i := 0 to InterfaceCount - 1 do
320 begin
321 intf := GetInterface(i);
322 if (intf <> nil) and (intf.QueryInterface(ITransactionUser,user) = S_OK) then
323 user.TransactionEnding(self,Force);
324 end;
325 case FDefaultCompletion of
326 taRollback:
327 Rollback(Force);
328 taCommit:
329 Commit(Force);
330 end;
331 end;
332 end;
333
334 function TFBTransaction.getTPB: ITPB;
335 begin
336 Result := FTPB;
337 end;
338
339 function TFBTransaction.Commit(Force: boolean): TTrCompletionState;
340 var i: integer;
341 TransactionID: integer;
342 TransactionEndNeeded: array of boolean;
343 begin
344 if not GetInTransaction then Exit;
345
346 if FForeignHandle then
347 IBError(ibxeTransactionNotOwned,[nil]);
348
349 SetLength(TransactionEndNeeded,Length(FAttachments));
350 TransactionID := GetTransactionID;
351 for i := 0 to Length(FAttachments) - 1 do
352 if (FAttachments[i] <> nil) then
353 TransactionEndNeeded[i] := GetJournalingActive(FAttachments[i])
354 else
355 TransactionEndNeeded[i] := false;
356 Result := InternalCommit(Force);
357 for i := 0 to Length(FAttachments) - 1 do
358 if TransactionEndNeeded[i] then
359 GetJournalIntf(FAttachments[i]).TransactionEnd(TransactionID, Result);
360 end;
361
362 procedure TFBTransaction.CommitRetaining;
363 var i: integer;
364 TransactionID: integer;
365 begin
366 if not GetInTransaction then Exit;
367
368 TransactionID := GetTransactionID;
369 InternalCommitRetaining;
370 for i := 0 to Length(FAttachments) - 1 do
371 if (FAttachments[i] <> nil) and GetJournalingActive(FAttachments[i]) then
372 GetJournalIntf(FAttachments[i]).TransactionRetained(self,TransactionID, TACommitRetaining);
373 end;
374
375 function TFBTransaction.GetIsReadOnly: boolean;
376 var Info: ITrInformation;
377 begin
378 Info := GetTrInformation(isc_info_tra_access);
379 if (Info.Count > 0) and (Info[0].getItemType = isc_info_tra_access) then
380 Result := Info[0].getAsInteger = isc_info_tra_readonly
381 else
382 Result := false;
383 end;
384
385 function TFBTransaction.GetTransactionID: integer;
386 var Info: ITrInformation;
387 begin
388 Result := -1;
389 Info := GetTrInformation(isc_info_tra_id);
390 if (Info.Count > 0) and (Info[0].getItemType = isc_info_tra_id) then
391 Result := Info[0].getAsInteger;
392 end;
393
394 function TFBTransaction.GetAttachmentCount: integer;
395 begin
396 Result := Length(FAttachments);
397 end;
398
399 function TFBTransaction.GetAttachment(index: integer): IAttachment;
400 begin
401 if (index >= 0) and (index < Length(FAttachments)) then
402 Result := FAttachments[index]
403 else
404 IBError(ibxeAttachmentListIndexError,[index]);
405 end;
406
407 function TFBTransaction.GetJournalingActive(attachment: IAttachment): boolean;
408 begin
409 Result := false;
410 if (attachment = nil) and (length(FAttachments) > 0) then
411 attachment := FAttachments[0];
412 if attachment <> nil then
413 with attachment do
414 Result := self.GetInTransaction and JournalingActive and
415 ((((joReadOnlyTransactions in GetJournalOptions) and self.GetIsReadOnly)) or
416 ((joReadWriteTransactions in GetJournalOptions) and not self.GetIsReadOnly));
417 end;
418
419 function TFBTransaction.GetDefaultCompletion: TTransactionCompletion;
420 begin
421 Result := FDefaultCompletion;
422 end;
423
424 function TFBTransaction.Rollback(Force: boolean): TTrCompletionState;
425 var i: integer;
426 TransactionID: integer;
427 TransactionEndNeeded: array of boolean;
428 begin
429 if not GetInTransaction then Exit;
430
431 if FForeignHandle then
432 IBError(ibxeTransactionNotOwned,[nil]);
433
434 SetLength(TransactionEndNeeded,Length(FAttachments));
435 TransactionID := GetTransactionID;
436 for i := 0 to Length(FAttachments) - 1 do
437 if (FAttachments[i] <> nil) then
438 TransactionEndNeeded[i] := GetJournalingActive(FAttachments[i])
439 else
440 TransactionEndNeeded[i] := false;
441 Result := InternalRollback(Force);
442 for i := 0 to Length(FAttachments) - 1 do
443 if TransactionEndNeeded[i] then
444 GetJournalIntf(FAttachments[i]).TransactionEnd(TransactionID, Result);
445 end;
446
447 procedure TFBTransaction.RollbackRetaining;
448 var i: integer;
449 TransactionID: integer;
450 begin
451 if not GetInTransaction then Exit;
452
453 TransactionID := GetTransactionID;
454 InternalRollbackRetaining;
455 for i := 0 to Length(FAttachments) - 1 do
456 if (FAttachments[i] <> nil) and GetJournalingActive(FAttachments[i]) then
457 GetJournalIntf(FAttachments[i]).TransactionRetained(self,TransactionID,TARollbackRetaining);
458 end;
459
460 procedure TFBTransaction.Start(DefaultCompletion: TTransactionCompletion);
461 var i: integer;
462 begin
463 if GetInTransaction then
464 Exit;
465
466 FDefaultCompletion := DefaultCompletion;
467
468 if Length(FAttachments) = 1 then
469 InternalStartSingle(FAttachments[0])
470 else
471 InternalStartMultiple;
472 for i := 0 to Length(FAttachments) - 1 do
473 if (FAttachments[i] <> nil) and GetJournalingActive(FAttachments[i]) then
474 GetJournalIntf(FAttachments[i]).TransactionStart(self);
475 Inc(FSeqNo);
476 end;
477
478 procedure TFBTransaction.Start(TPB: ITPB; DefaultCompletion: TTransactionCompletion
479 );
480 begin
481 FTPB := TPB;
482 Start(DefaultCompletion);
483 end;
484
485 function TFBTransaction.GetTrInformation(Requests: array of byte
486 ): ITrInformation;
487 var ReqBuffer: PByte;
488 i: integer;
489 begin
490 CheckHandle;
491 if Length(Requests) = 1 then
492 Result := GetTrInformation(Requests[0])
493 else
494 begin
495 GetMem(ReqBuffer,Length(Requests));
496 try
497 for i := 0 to Length(Requests) - 1 do
498 ReqBuffer[i] := Requests[i];
499
500 Result := GetTrInfo(ReqBuffer,Length(Requests));
501
502 finally
503 FreeMem(ReqBuffer);
504 end;
505 end;
506 end;
507
508 function TFBTransaction.GetTrInformation(Request: byte): ITrInformation;
509 begin
510 CheckHandle;
511 Result := GetTrInfo(@Request,1);
512 end;
513
514 function TFBTransaction.GetTransactionName: AnsiString;
515 begin
516 Result := FTransactionName;
517 end;
518
519 procedure TFBTransaction.SetTransactionName(aValue: AnsiString);
520 begin
521 FTransactionName := aValue;
522 end;
523
524 { TTPBItem }
525
526 function TTPBItem.getParamTypeName: AnsiString;
527 begin
528 Result := TPBPrefix + TPBConstantNames[getParamType];
529 end;
530
531
532 {TTPB}
533
534 constructor TTPB.Create(api: TFBClientAPI);
535 begin
536 inherited Create(api);
537 FDataLength := 1;
538 FBuffer^ := isc_tpb_version3;
539 end;
540
541 function TTPB.GetParamTypeName(ParamType: byte): Ansistring;
542 begin
543 if ParamType <= isc_tpb_last_tpb_constant then
544 Result := TPBPrefix + TPBConstantNames[ParamType]
545 else
546 Result := '';
547 end;
548
549 function TTPB.AsText: AnsiString;
550 var i: integer;
551 begin
552 Result := '[';
553 for i := 0 to getCount - 1 do
554 begin
555 Result := Result + GetParamTypeName(getItems(i).getParamType);
556 if i < getCount - 1 then
557 Result := Result + ',';
558 end;
559 Result := Result + ']';
560 end;
561
562 {$IFNDEF FPC}
563 function TTPB.GetDPBParamTypeName(ParamType: byte): Ansistring;
564 begin
565 Result := GetParamTypeName(ParamType);
566 end;
567 {$ENDIF}
568
569
570 function TTPB.LookupItemType(ParamTypeName: AnsiString): byte;
571 var i: byte;
572 begin
573 Result := 0;
574 ParamTypeName := LowerCase(ParamTypeName);
575 if (Pos(TPBPrefix, ParamTypeName) = 1) then
576 Delete(ParamTypeName, 1, Length(TPBPrefix));
577
578 for i := 1 to isc_tpb_last_tpb_constant do
579 if (ParamTypeName = TPBConstantNames[i]) then
580 begin
581 Result := i;
582 break;
583 end;
584 end;
585
586 { TTrInfoItem }
587
588 procedure TTrInfoItem.DecodeTraIsolation(var IsolationType, RecVersion: byte);
589 begin
590 with FFirebirdClientAPI, ItemData^ do
591 if getItemType = isc_info_tra_isolation then
592 begin
593 if FDataLength = 1 then
594 begin
595 IsolationType := getAsInteger;
596 RecVersion := 0;
597 end
598 else
599 begin
600 IsolationType := (FBufPtr + 3)^;
601 RecVersion := (FBufPtr + 4)^;
602 end
603 end
604 else
605 IBError(ibxeInfoBufferTypeError,[integer(FBufPtr^)]);
606 end;
607
608 { TTrInformation }
609
610 procedure TTrInformation.DoParseBuffer;
611 var P: PByte;
612 index: integer;
613 begin
614 P := Buffer;
615 index := 0;
616 SetLength(FItems,0);
617 while (P^ <> isc_info_end) and (P < Buffer + getBufSize) do
618 begin
619 SetLength(FItems,index+1);
620 case byte(P^) of
621 isc_info_tra_id,
622 isc_info_tra_oldest_interesting,
623 isc_info_tra_oldest_active,
624 isc_info_tra_oldest_snapshot,
625 fb_info_tra_snapshot_number,
626 isc_info_tra_lock_timeout:
627 FItems[index] := AddIntegerItem(P);
628
629 isc_info_tra_isolation,
630 {return transaction isolation mode of current transaction.
631 format of returned clumplets is following:
632
633 isc_info_tra_isolation,
634 1, isc_info_tra_consistency | isc_info_tra_concurrency
635 |
636 2, isc_info_tra_read_committed,
637 isc_info_tra_no_rec_version | isc_info_tra_rec_version
638
639 i.e. for read committed transactions returned 2 items while for
640 other transactions returned 1 item}
641
642 isc_info_tra_access:
643 FItems[index] := AddIntegerItem(P);
644 fb_info_tra_dbpath:
645 FItems[index] := AddStringItem(P);
646 else
647 FItems[index] := AddItem(P);
648 end;
649 P := P + FItems[index]^.FSize;
650 Inc(index);
651 end;
652 end;
653
654 constructor TTrInformation.Create(api: TFBClientAPI; aSize: integer);
655 begin
656 inherited Create(api,aSize);
657 FIntegerType := dtInteger;
658 end;
659
660 {$IFNDEF FPC}
661 function TTrInformation.Find(ItemType: byte): ITrInfoItem;
662 begin
663 Result := inherited Find(ItemType);
664 if Result.GetSize = 0 then
665 Result := nil;
666 end;
667 {$ENDIF}
668
669 end.
670

Properties

Name Value
svn:eol-style native