ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBDatabase.pas
Revision: 5
Committed: Fri Feb 18 16:26:16 2011 UTC (13 years, 2 months ago) by tony
Content type: text/x-pascal
File size: 55770 byte(s)
Log Message:
Committing updates for Release pre-release

File Contents

# User Rev Content
1 tony 5 {************************************************************************}
2     { }
3     { Borland Delphi Visual Component Library }
4     { InterBase Express core components }
5     { }
6     { Copyright (c) 1998-2000 Inprise Corporation }
7     { }
8     { InterBase Express is based in part on the product }
9     { Free IB Components, written by Gregory H. Deatz for }
10     { Hoagland, Longo, Moran, Dunst & Doukas Company. }
11     { Free IB Components is used under license. }
12     { }
13     { The contents of this file are subject to the InterBase }
14     { Public License Version 1.0 (the "License"); you may not }
15     { use this file except in compliance with the License. You }
16     { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
17     { Software distributed under the License is distributed on }
18     { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
19     { express or implied. See the License for the specific language }
20     { governing rights and limitations under the License. }
21     { The Original Code was created by InterBase Software Corporation }
22     { and its successors. }
23     { Portions created by Inprise Corporation are Copyright (C) Inprise }
24     { Corporation. All Rights Reserved. }
25     { Contributor(s): Jeff Overcash }
26     { }
27     {************************************************************************}
28    
29     unit IBDatabase;
30    
31     {$Mode Delphi}
32    
33     interface
34    
35     uses
36     {$IFDEF LINUX }
37     unix,
38     {$ELSE}
39     {$DEFINE HAS_SQLMONITOR}
40     Windows,
41     {$ENDIF}
42     Dialogs, Controls, StdCtrls, SysUtils, Classes, Forms, ExtCtrls, IBHeader, IBExternals, DB,
43     IB, DBLoginDlg;
44    
45     const
46     DPBPrefix = 'isc_dpb_';
47     DPBConstantNames: array[1..isc_dpb_last_dpb_constant] of string = (
48     'cdd_pathname',
49     'allocation',
50     'journal',
51     'page_size',
52     'num_buffers',
53     'buffer_length',
54     'debug',
55     'garbage_collect',
56     'verify',
57     'sweep',
58     'enable_journal',
59     'disable_journal',
60     'dbkey_scope',
61     'number_of_users',
62     'trace',
63     'no_garbage_collect',
64     'damaged',
65     'license',
66     'sys_user_name',
67     'encrypt_key',
68     'activate_shadow',
69     'sweep_interval',
70     'delete_shadow',
71     'force_write',
72     'begin_log',
73     'quit_log',
74     'no_reserve',
75     'user_name',
76     'password',
77     'password_enc',
78     'sys_user_name_enc',
79     'interp',
80     'online_dump',
81     'old_file_size',
82     'old_num_files',
83     'old_file',
84     'old_start_page',
85     'old_start_seqno',
86     'old_start_file',
87     'drop_walfile',
88     'old_dump_id',
89     'wal_backup_dir',
90     'wal_chkptlen',
91     'wal_numbufs',
92     'wal_bufsize',
93     'wal_grp_cmt_wait',
94     'lc_messages',
95     'lc_ctype',
96     'cache_manager',
97     'shutdown',
98     'online',
99     'shutdown_delay',
100     'reserved',
101     'overwrite',
102     'sec_attach',
103     'disable_wal',
104     'connect_timeout',
105     'dummy_packet_interval',
106     'gbak_attach',
107     'sql_role_name',
108     'set_page_buffers',
109     'working_directory',
110     'sql_dialect',
111     'set_db_readonly',
112     'set_db_sql_dialect',
113     'gfix_attach',
114     'gstat_attach'
115     );
116    
117     TPBPrefix = 'isc_tpb_';
118     TPBConstantNames: array[1..isc_tpb_last_tpb_constant] of string = (
119     'consistency',
120     'concurrency',
121     'shared',
122     'protected',
123     'exclusive',
124     'wait',
125     'nowait',
126     'read',
127     'write',
128     'lock_read',
129     'lock_write',
130     'verb_time',
131     'commit_time',
132     'ignore_limbo',
133     'read_committed',
134     'autocommit',
135     'rec_version',
136     'no_rec_version',
137     'restart_requests',
138     'no_auto_undo'
139     );
140    
141     type
142    
143     TIBDatabase = class;
144     TIBTransaction = class;
145     TIBBase = class;
146    
147     TIBDatabaseLoginEvent = procedure(Database: TIBDatabase;
148     LoginParams: TStrings) of object;
149    
150     TIBFileName = type string;
151     { TIBDatabase }
152     TIBDataBase = class(TCustomConnection)
153     private
154     FHiddenPassword: string;
155     FIBLoaded: Boolean;
156     FOnLogin: TIBDatabaseLoginEvent;
157     FTraceFlags: TTraceFlags;
158     FDBSQLDialect: Integer;
159     FSQLDialect: Integer;
160     FOnDialectDowngradeWarning: TNotifyEvent;
161     FCanTimeout: Boolean;
162     FSQLObjects: TList;
163     FTransactions: TList;
164     FDBName: TIBFileName;
165     FDBParams: TStrings;
166     FDBParamsChanged: Boolean;
167     FDPB: PChar;
168     FDPBLength: Short;
169     FHandle: TISC_DB_HANDLE;
170     FHandleIsShared: Boolean;
171     FOnIdleTimer: TNotifyEvent;
172     FDefaultTransaction: TIBTransaction;
173     FInternalTransaction: TIBTransaction;
174     FStreamedConnected: Boolean;
175     FTimer: TTimer;
176     FUserNames: TStringList;
177     FDataSets: TList;
178     procedure EnsureInactive;
179     function GetDBSQLDialect: Integer;
180     function GetSQLDialect: Integer;
181     procedure SetSQLDialect(const Value: Integer);
182     procedure ValidateClientSQLDialect;
183     procedure DBParamsChange(Sender: TObject);
184     procedure DBParamsChanging(Sender: TObject);
185     function GetSQLObject(Index: Integer): TIBBase;
186     function GetSQLObjectCount: Integer;
187     function GetDBParamByDPB(const Idx: Integer): String;
188     function GetIdleTimer: Integer;
189     function GetTransaction(Index: Integer): TIBTransaction;
190     function GetTransactionCount: Integer;
191     function Login: Boolean;
192     procedure SetDatabaseName(const Value: TIBFileName);
193     procedure SetDBParamByDPB(const Idx: Integer; Value: String);
194     procedure SetDBParams(Value: TStrings);
195     procedure SetDefaultTransaction(Value: TIBTransaction);
196     procedure SetIdleTimer(Value: Integer);
197     procedure TimeoutConnection(Sender: TObject);
198     function GetIsReadOnly: Boolean;
199     function AddSQLObject(ds: TIBBase): Integer;
200     procedure RemoveSQLObject(Idx: Integer);
201     procedure RemoveSQLObjects;
202     procedure InternalClose(Force: Boolean);
203    
204     protected
205     procedure DoConnect; override;
206     procedure DoDisconnect; override;
207     function GetConnected: Boolean; override;
208     procedure Loaded; override;
209     procedure Notification( AComponent: TComponent; Operation: TOperation); override;
210     function GetDataset(Index : longint) : TDataset; override;
211     function GetDataSetCount : Longint; override;
212    
213     public
214     constructor Create(AOwner: TComponent); override;
215     destructor Destroy; override;
216     procedure ApplyUpdates(const DataSets: array of TDataSet);
217     procedure CloseDataSets;
218     procedure CheckActive;
219     procedure CheckInactive;
220     procedure CreateDatabase;
221     procedure DropDatabase;
222     procedure ForceClose;
223     procedure GetFieldNames(const TableName: string; List: TStrings);
224     procedure GetTableNames(List: TStrings; SystemTables: Boolean = False);
225     function IndexOfDBConst(st: String): Integer;
226     function TestConnected: Boolean;
227     procedure CheckDatabaseName;
228     function Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
229     function AddTransaction(TR: TIBTransaction): Integer;
230     function FindTransaction(TR: TIBTransaction): Integer;
231     function FindDefaultTransaction(): TIBTransaction;
232     procedure RemoveTransaction(Idx: Integer);
233     procedure RemoveTransactions;
234     procedure SetHandle(Value: TISC_DB_HANDLE);
235    
236     property Handle: TISC_DB_HANDLE read FHandle;
237     property IsReadOnly: Boolean read GetIsReadOnly;
238     property DBParamByDPB[const Idx: Integer]: String read GetDBParamByDPB
239     write SetDBParamByDPB;
240     property SQLObjectCount: Integer read GetSQLObjectCount;
241     property SQLObjects[Index: Integer]: TIBBase read GetSQLObject;
242     property HandleIsShared: Boolean read FHandleIsShared;
243     property TransactionCount: Integer read GetTransactionCount;
244     property Transactions[Index: Integer]: TIBTransaction read GetTransaction;
245     property InternalTransaction: TIBTransaction read FInternalTransaction;
246    
247     published
248     property Connected;
249     property StreamedConnected;
250     property DatabaseName: TIBFileName read FDBName write SetDatabaseName;
251     property Params: TStrings read FDBParams write SetDBParams;
252     property LoginPrompt default True;
253     property DefaultTransaction: TIBTransaction read FDefaultTransaction
254     write SetDefaultTransaction;
255     property IdleTimer: Integer read GetIdleTimer write SetIdleTimer;
256     property SQLDialect : Integer read GetSQLDialect write SetSQLDialect default 3;
257     property DBSQLDialect : Integer read FDBSQLDialect;
258     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
259     property AfterConnect;
260     property AfterDisconnect;
261     property BeforeConnect;
262     property BeforeDisconnect;
263     property OnLogin: TIBDatabaseLoginEvent read FOnLogin write FOnLogin;
264     property OnIdleTimer: TNotifyEvent read FOnIdleTimer write FOnIdleTimer;
265     property OnDialectDowngradeWarning: TNotifyEvent read FOnDialectDowngradeWarning write FOnDialectDowngradeWarning;
266     end;
267    
268     { TIBTransaction }
269    
270     TTransactionAction = (TARollback, TACommit, TARollbackRetaining, TACommitRetaining);
271    
272     TIBTransaction = class(TComponent)
273     private
274     FIBLoaded: Boolean;
275     FCanTimeout : Boolean;
276     FDatabases : TList;
277     FSQLObjects : TList;
278     FDefaultDatabase : TIBDatabase;
279     FHandle : TISC_TR_HANDLE;
280     FHandleIsShared : Boolean;
281     FOnIdleTimer : TNotifyEvent;
282     FStreamedActive : Boolean;
283     FTPB : PChar;
284     FTPBLength : Short;
285     FTimer : TTimer;
286     FDefaultAction : TTransactionAction;
287     FTRParams : TStrings;
288     FTRParamsChanged : Boolean;
289     procedure EnsureNotInTransaction;
290     procedure EndTransaction(Action: TTransactionAction; Force: Boolean);
291     function GetDatabase(Index: Integer): TIBDatabase;
292     function GetDatabaseCount: Integer;
293     function GetSQLObject(Index: Integer): TIBBase;
294     function GetSQLObjectCount: Integer;
295     function GetInTransaction: Boolean;
296     function GetIdleTimer: Integer;
297     procedure BeforeDatabaseDisconnect(DB: TIBDatabase);
298     procedure SetActive(Value: Boolean);
299     procedure SetDefaultAction(Value: TTransactionAction);
300     procedure SetDefaultDatabase(Value: TIBDatabase);
301     procedure SetIdleTimer(Value: Integer);
302     procedure SetTRParams(Value: TStrings);
303     procedure TimeoutTransaction(Sender: TObject);
304     procedure TRParamsChange(Sender: TObject);
305     procedure TRParamsChanging(Sender: TObject);
306     function AddSQLObject(ds: TIBBase): Integer;
307     procedure RemoveSQLObject(Idx: Integer);
308     procedure RemoveSQLObjects;
309    
310     protected
311     procedure Loaded; override;
312     procedure SetHandle(Value: TISC_TR_HANDLE);
313     procedure Notification( AComponent: TComponent; Operation: TOperation); override;
314    
315     public
316     constructor Create(AOwner: TComponent); override;
317     destructor Destroy; override;
318     function Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
319     procedure Commit;
320     procedure CommitRetaining;
321     procedure Rollback;
322     procedure RollbackRetaining;
323     procedure StartTransaction;
324     procedure CheckInTransaction;
325     procedure CheckNotInTransaction;
326    
327     function AddDatabase(db: TIBDatabase): Integer;
328     function FindDatabase(db: TIBDatabase): Integer;
329     function FindDefaultDatabase: TIBDatabase;
330     procedure RemoveDatabase(Idx: Integer);
331     procedure RemoveDatabases;
332     procedure CheckDatabasesInList;
333    
334     property DatabaseCount: Integer read GetDatabaseCount;
335     property Databases[Index: Integer]: TIBDatabase read GetDatabase;
336     property SQLObjectCount: Integer read GetSQLObjectCount;
337     property SQLObjects[Index: Integer]: TIBBase read GetSQLObject;
338     property Handle: TISC_TR_HANDLE read FHandle;
339     property HandleIsShared: Boolean read FHandleIsShared;
340     property InTransaction: Boolean read GetInTransaction;
341     property TPB: PChar read FTPB;
342     property TPBLength: Short read FTPBLength;
343     published
344     property Active: Boolean read GetInTransaction write SetActive;
345     property DefaultDatabase: TIBDatabase read FDefaultDatabase
346     write SetDefaultDatabase;
347     property IdleTimer: Integer read GetIdleTimer write SetIdleTimer default 0;
348     property DefaultAction: TTransactionAction read FDefaultAction write SetDefaultAction default taCommit;
349     property Params: TStrings read FTRParams write SetTRParams;
350     property OnIdleTimer: TNotifyEvent read FOnIdleTimer write FOnIdleTimer;
351     end;
352    
353     { TIBBase }
354    
355     { Virtually all components in IB are "descendents" of TIBBase.
356     It is to more easily manage the database and transaction
357     connections. }
358     TIBBase = class(TObject)
359     protected
360     FDatabase: TIBDatabase;
361     FIndexInDatabase: Integer;
362     FTransaction: TIBTransaction;
363     FIndexInTransaction: Integer;
364     FOwner: TObject;
365     FBeforeDatabaseDisconnect: TNotifyEvent;
366     FAfterDatabaseDisconnect: TNotifyEvent;
367     FOnDatabaseFree: TNotifyEvent;
368     FBeforeTransactionEnd: TNotifyEvent;
369     FAfterTransactionEnd: TNotifyEvent;
370     FOnTransactionFree: TNotifyEvent;
371    
372     procedure DoBeforeDatabaseDisconnect; virtual;
373     procedure DoAfterDatabaseDisconnect; virtual;
374     procedure DoDatabaseFree; virtual;
375     procedure DoBeforeTransactionEnd; virtual;
376     procedure DoAfterTransactionEnd; virtual;
377     procedure DoTransactionFree; virtual;
378     function GetDBHandle: PISC_DB_HANDLE; virtual;
379     function GetTRHandle: PISC_TR_HANDLE; virtual;
380     procedure SetDatabase(Value: TIBDatabase); virtual;
381     procedure SetTransaction(Value: TIBTransaction); virtual;
382     public
383     constructor Create(AOwner: TObject);
384     destructor Destroy; override;
385     procedure CheckDatabase; virtual;
386     procedure CheckTransaction; virtual;
387     public
388     property BeforeDatabaseDisconnect: TNotifyEvent read FBeforeDatabaseDisconnect
389     write FBeforeDatabaseDisconnect;
390     property AfterDatabaseDisconnect: TNotifyEvent read FAfterDatabaseDisconnect
391     write FAfterDatabaseDisconnect;
392     property OnDatabaseFree: TNotifyEvent read FOnDatabaseFree write FOnDatabaseFree;
393     property BeforeTransactionEnd: TNotifyEvent read FBeforeTransactionEnd write FBeforeTransactionEnd;
394     property AfterTransactionEnd: TNotifyEvent read FAfterTransactionEnd write FAfterTransactionEnd;
395     property OnTransactionFree: TNotifyEvent read FOnTransactionFree write FOnTransactionFree;
396     property Database: TIBDatabase read FDatabase
397     write SetDatabase;
398     property DBHandle: PISC_DB_HANDLE read GetDBHandle;
399     property Owner: TObject read FOwner;
400     property TRHandle: PISC_TR_HANDLE read GetTRHandle;
401     property Transaction: TIBTransaction read FTransaction
402     write SetTransaction;
403     end;
404    
405     procedure GenerateDPB(sl: TStrings; var DPB: string; var DPBLength: Short);
406     procedure GenerateTPB(sl: TStrings; var TPB: string; var TPBLength: Short);
407    
408    
409     implementation
410    
411     uses IBIntf,{$IFDEF HAS_SQLMONITOR}IBSQLMonitor,{$ENDIF} IBCustomDataSet, IBDatabaseInfo, IBSQL, IBUtils, typInfo;
412    
413     { TIBDatabase }
414    
415     constructor TIBDatabase.Create(AOwner: TComponent);
416     begin
417     inherited Create(AOwner);
418     FIBLoaded := False;
419     CheckIBLoaded;
420     FIBLoaded := True;
421     LoginPrompt := True;
422     FSQLObjects := TList.Create;
423     FTransactions := TList.Create;
424     FDBName := '';
425     FDBParams := TStringList.Create;
426     FDBParamsChanged := True;
427     TStringList(FDBParams).OnChange := DBParamsChange;
428     TStringList(FDBParams).OnChanging := DBParamsChanging;
429     FDPB := nil;
430     FHandle := nil;
431     FUserNames := nil;
432     FInternalTransaction := TIBTransaction.Create(self);
433     FInternalTransaction.DefaultDatabase := Self;
434     FTimer := TTimer.Create(Self);
435     FTimer.Enabled := False;
436     FTimer.Interval := 0;
437     FTimer.OnTimer := TimeoutConnection;
438     FDBSQLDialect := 1;
439     FSQLDialect := 3;
440     FTraceFlags := [];
441     FDataSets := TList.Create;
442     end;
443    
444     destructor TIBDatabase.Destroy;
445     var
446     i: Integer;
447     begin
448     if FIBLoaded then
449     begin
450     IdleTimer := 0;
451     if FHandle <> nil then
452     ForceClose;
453     for i := 0 to FSQLObjects.Count - 1 do
454     if FSQLObjects[i] <> nil then
455     SQLObjects[i].DoDatabaseFree;
456     RemoveSQLObjects;
457     RemoveTransactions;
458     FInternalTransaction.Free;
459     FreeMem(FDPB);
460     FDPB := nil;
461     FDBParams.Free;
462     FSQLObjects.Free;
463     FUserNames.Free;
464     FTransactions.Free;
465     end;
466     FDataSets.Free;
467     inherited Destroy;
468     end;
469    
470     function TIBDatabase.Call(ErrCode: ISC_STATUS;
471     RaiseError: Boolean): ISC_STATUS;
472     begin
473     result := ErrCode;
474     FCanTimeout := False;
475     if RaiseError and (ErrCode > 0) then
476     IBDataBaseError;
477     end;
478    
479     procedure TIBDatabase.CheckActive;
480     begin
481     if StreamedConnected and (not Connected) then
482     Loaded;
483     if FHandle = nil then
484     IBError(ibxeDatabaseClosed, [nil]);
485     end;
486    
487     procedure TIBDatabase.EnsureInactive;
488     begin
489     if csDesigning in ComponentState then
490     begin
491     if FHandle <> nil then
492     Close;
493     end
494     end;
495    
496     procedure TIBDatabase.CheckInactive;
497     begin
498     if FHandle <> nil then
499     IBError(ibxeDatabaseOpen, [nil]);
500     end;
501    
502     procedure TIBDatabase.CheckDatabaseName;
503     begin
504     if (FDBName = '') then
505     IBError(ibxeDatabaseNameMissing, [nil]);
506     end;
507    
508     function TIBDatabase.AddSQLObject(ds: TIBBase): Integer;
509     begin
510     result := 0;
511     if (ds.Owner is TIBCustomDataSet) then
512     {$IFDEF LINUX}
513     FDataSets.Add(TDataSet(ds.Owner));
514     {$ELSE}
515     RegisterClient(TDataSet(ds.Owner));
516     {$ENDIF}
517     while (result < FSQLObjects.Count) and (FSQLObjects[result] <> nil) do
518     Inc(result);
519     if (result = FSQLObjects.Count) then
520     FSQLObjects.Add(ds)
521     else
522     FSQLObjects[result] := ds;
523     end;
524    
525     function TIBDatabase.AddTransaction(TR: TIBTransaction): Integer;
526     begin
527     result := FindTransaction(TR);
528     if result <> -1 then
529     begin
530     result := -1;
531     exit;
532     end;
533     result := 0;
534     while (result < FTransactions.Count) and (FTransactions[result] <> nil) do
535     Inc(result);
536     if (result = FTransactions.Count) then
537     FTransactions.Add(TR)
538     else
539     FTransactions[result] := TR;
540     end;
541    
542     procedure TIBDatabase.DoDisconnect;
543     begin
544     if Connected then
545     InternalClose(False);
546     FDBSQLDialect := 1;
547     end;
548    
549     procedure TIBDatabase.CreateDatabase;
550     var
551     tr_handle: TISC_TR_HANDLE;
552     begin
553     CheckInactive;
554     tr_handle := nil;
555     Call(
556     isc_dsql_execute_immediate(StatusVector, @FHandle, @tr_handle, 0,
557     PChar('CREATE DATABASE ''' + FDBName + ''' ' + {do not localize}
558     Params.Text), SQLDialect, nil),
559     True);
560     end;
561    
562     procedure TIBDatabase.DropDatabase;
563     begin
564     CheckActive;
565     Call(isc_drop_database(StatusVector, @FHandle), True);
566     end;
567    
568     procedure TIBDatabase.DBParamsChange(Sender: TObject);
569     begin
570     FDBParamsChanged := True;
571     end;
572    
573     procedure TIBDatabase.DBParamsChanging(Sender: TObject);
574     begin
575     EnsureInactive;
576     CheckInactive;
577     end;
578    
579     function TIBDatabase.FindTransaction(TR: TIBTransaction): Integer;
580     var
581     i: Integer;
582     begin
583     result := -1;
584     for i := 0 to FTransactions.Count - 1 do
585     if TR = Transactions[i] then
586     begin
587     result := i;
588     break;
589     end;
590     end;
591    
592     function TIBDatabase.FindDefaultTransaction(): TIBTransaction;
593     var
594     i: Integer;
595     begin
596     result := FDefaultTransaction;
597     if result = nil then
598     begin
599     for i := 0 to FTransactions.Count - 1 do
600     if (Transactions[i] <> nil) and
601     (TIBTransaction(Transactions[i]).DefaultDatabase = self) and
602     (TIBTransaction(Transactions[i]) <> FInternalTransaction) then
603     begin
604     result := TIBTransaction(Transactions[i]);
605     break;
606     end;
607     end;
608     end;
609    
610     procedure TIBDatabase.ForceClose;
611     begin
612     if Connected then
613     InternalClose(True);
614     end;
615    
616     function TIBDatabase.GetConnected: Boolean;
617     begin
618     result := FHandle <> nil;
619     end;
620    
621     function TIBDatabase.GetSQLObject(Index: Integer): TIBBase;
622     begin
623     result := FSQLObjects[Index];
624     end;
625    
626     function TIBDatabase.GetSQLObjectCount: Integer;
627     var
628     i: Integer;
629     begin
630     result := 0;
631     for i := 0 to FSQLObjects.Count - 1 do if FSQLObjects[i] <> nil then
632     Inc(result);
633     end;
634    
635     function TIBDatabase.GetDBParamByDPB(const Idx: Integer): String;
636     var
637     ConstIdx, EqualsIdx: Integer;
638     begin
639     if (Idx > 0) and (Idx <= isc_dpb_last_dpb_constant) then
640     begin
641     ConstIdx := IndexOfDBConst(DPBConstantNames[Idx]);
642     if ConstIdx = -1 then
643     result := ''
644     else
645     begin
646     result := Params[ConstIdx];
647     EqualsIdx := Pos('=', result); {mbcs ok}
648     if EqualsIdx = 0 then
649     result := ''
650     else
651     result := Copy(result, EqualsIdx + 1, Length(result));
652     end;
653     end
654     else
655     result := '';
656     end;
657    
658     function TIBDatabase.GetIdleTimer: Integer;
659     begin
660     result := FTimer.Interval;
661     end;
662    
663     function TIBDatabase.GetTransaction(Index: Integer): TIBTransaction;
664     begin
665     result := FTransactions[Index];
666     end;
667    
668     function TIBDatabase.GetTransactionCount: Integer;
669     var
670     i: Integer;
671     begin
672     result := 0;
673     for i := 0 to FTransactions.Count - 1 do
674     if FTransactions[i] <> nil then
675     Inc(result);
676     end;
677    
678     function TIBDatabase.IndexOfDBConst(st: String): Integer;
679     var
680     i, pos_of_str: Integer;
681     begin
682     result := -1;
683     for i := 0 to Params.Count - 1 do
684     begin
685     pos_of_str := Pos(st, AnsiLowerCase(Params[i])); {mbcs ok}
686     if (pos_of_str = 1) or (pos_of_str = Length(DPBPrefix) + 1) then
687     begin
688     result := i;
689     break;
690     end;
691     end;
692     end;
693    
694     procedure TIBDatabase.InternalClose(Force: Boolean);
695     var
696     i: Integer;
697     begin
698     CheckActive;
699     { Tell all connected transactions that we're disconnecting.
700     This is so transactions can commit/rollback, accordingly
701     }
702     for i := 0 to FTransactions.Count - 1 do
703     begin
704     try
705     if FTransactions[i] <> nil then
706     Transactions[i].BeforeDatabaseDisconnect(Self);
707     except
708     if not Force then
709     raise;
710     end;
711     end;
712     for i := 0 to FSQLObjects.Count - 1 do
713     begin
714     try
715     if FSQLObjects[i] <> nil then
716     SQLObjects[i].DoBeforeDatabaseDisconnect;
717     except
718     if not Force then
719     raise;
720     end;
721     end;
722    
723     if (not HandleIsShared) and
724     (Call(isc_detach_database(StatusVector, @FHandle), False) > 0) and
725     (not Force) then
726     IBDataBaseError
727     else
728     begin
729     FHandle := nil;
730     FHandleIsShared := False;
731     end;
732    
733     {$IFDEF HAS_SQLMONITOR}
734     if not (csDesigning in ComponentState) then
735     MonitorHook.DBDisconnect(Self);
736     {$ENDIF}
737    
738     for i := 0 to FSQLObjects.Count - 1 do
739     if FSQLObjects[i] <> nil then
740     SQLObjects[i].DoAfterDatabaseDisconnect;
741     end;
742    
743     procedure TIBDatabase.Loaded;
744     var
745     i: integer;
746     begin
747     try
748     if StreamedConnected and (not Connected) then
749     begin
750     inherited Loaded;
751     for i := 0 to FTransactions.Count - 1 do
752     if FTransactions[i] <> nil then
753     begin
754     with TIBTransaction(FTransactions[i]) do
755     if not Active then
756     if FStreamedActive and not InTransaction then
757     begin
758     StartTransaction;
759     FStreamedActive := False;
760     end;
761     end;
762     if (FDefaultTransaction <> nil) and
763     (FDefaultTransaction.FStreamedActive) and
764     (not FDefaultTransaction.InTransaction) then
765     FDefaultTransaction.StartTransaction;
766     FStreamedConnected := False;
767     end;
768     except
769     if csDesigning in ComponentState then
770     Application.HandleException(Self)
771     else
772     raise;
773     end;
774     end;
775    
776     procedure TIBDatabase.Notification( AComponent: TComponent;
777     Operation: TOperation);
778     var
779     i: Integer;
780     begin
781     inherited Notification( AComponent, Operation);
782     if (Operation = opRemove) and (AComponent = FDefaultTransaction) then
783     begin
784     i := FindTransaction(FDefaultTransaction);
785     if (i <> -1) then
786     RemoveTransaction(i);
787     FDefaultTransaction := nil;
788     end;
789     end;
790    
791     function TIBDatabase.Login: Boolean;
792     var
793     IndexOfUser, IndexOfPassword: Integer;
794     Username, Password, OldPassword: String;
795     LoginParams: TStrings;
796    
797     procedure HidePassword;
798     var
799     I: Integer;
800     IndexAt: Integer;
801     begin
802     IndexAt := 0;
803     for I := 0 to Params.Count -1 do
804     if Pos('password', LowerCase(Trim(Params.Names[i]))) = 1 then {mbcs ok}
805     begin
806     FHiddenPassword := Params.Values[Params.Names[i]];
807     IndexAt := I;
808     break;
809     end;
810     if IndexAt <> 0 then
811     Params.Delete(IndexAt);
812     end;
813    
814     begin
815     if Assigned(FOnLogin) then
816     begin
817     result := True;
818     LoginParams := TStringList.Create;
819     try
820     LoginParams.Assign(Params);
821     FOnLogin(Self, LoginParams);
822     Params.Assign (LoginParams);
823     HidePassword;
824     finally
825     LoginParams.Free;
826     end;
827     end
828     else
829     begin
830     IndexOfUser := IndexOfDBConst(DPBConstantNames[isc_dpb_user_name]);
831     if IndexOfUser <> -1 then
832     Username := Copy(Params[IndexOfUser],
833     Pos('=', Params[IndexOfUser]) + 1, {mbcs ok}
834     Length(Params[IndexOfUser]));
835     IndexOfPassword := IndexOfDBConst(DPBConstantNames[isc_dpb_password]);
836     if IndexOfPassword <> -1 then
837     begin
838     Password := Copy(Params[IndexOfPassword],
839     Pos('=', Params[IndexOfPassword]) + 1, {mbcs ok}
840     Length(Params[IndexOfPassword]));
841     OldPassword := password;
842     end;
843     result := LoginDialogEx(DatabaseName, Username, Password, False);
844     if result then
845     begin
846     if IndexOfUser = -1 then
847     Params.Add(DPBConstantNames[isc_dpb_user_name] + '=' + Username)
848     else
849     Params[IndexOfUser] := DPBConstantNames[isc_dpb_user_name] +
850     '=' + Username;
851     if (Password = OldPassword) then
852     FHiddenPassword := ''
853     else
854     begin
855     FHiddenPassword := Password;
856     if OldPassword <> '' then
857     HidePassword;
858     end;
859     end;
860     end;
861     end;
862    
863     procedure TIBDatabase.DoConnect;
864     var
865     DPB: String;
866     TempDBParams: TStrings;
867    
868     begin
869     CheckInactive;
870     CheckDatabaseName;
871     if (not LoginPrompt) and (FHiddenPassword <> '') then
872     begin
873     FHiddenPassword := '';
874     FDBParamsChanged := True;
875     end;
876     { Use builtin login prompt if requested }
877     if LoginPrompt and not Login then
878     IBError(ibxeOperationCancelled, [nil]);
879     { Generate a new DPB if necessary }
880     if (FDBParamsChanged) then
881     begin
882     FDBParamsChanged := False;
883     if (not LoginPrompt) or (FHiddenPassword = '') then
884     GenerateDPB(FDBParams, DPB, FDPBLength)
885     else
886     begin
887     TempDBParams := TStringList.Create;
888     try
889     TempDBParams.Assign(FDBParams);
890     TempDBParams.Add('password=' + FHiddenPassword);
891     GenerateDPB(TempDBParams, DPB, FDPBLength);
892     finally
893     TempDBParams.Free;
894     end;
895     end;
896     IBAlloc(FDPB, 0, FDPBLength);
897     Move(DPB[1], FDPB[0], FDPBLength);
898     end;
899     if Call(isc_attach_database(StatusVector, Length(FDBName),
900     PChar(FDBName), @FHandle,
901     FDPBLength, FDPB), False) > 0 then
902     begin
903     FHandle := nil;
904     IBDataBaseError;
905     end;
906     FDBSQLDialect := GetDBSQLDialect;
907     ValidateClientSQLDialect;
908     {$IFDEF HAS_SQLMONITOR}
909     if not (csDesigning in ComponentState) then
910     MonitorHook.DBConnect(Self);
911     {$ENDIF}
912     end;
913    
914     procedure TIBDatabase.RemoveSQLObject(Idx: Integer);
915     var
916     ds: TIBBase;
917     begin
918     if (Idx >= 0) and (FSQLObjects[Idx] <> nil) then
919     begin
920     ds := SQLObjects[Idx];
921     FSQLObjects[Idx] := nil;
922     ds.Database := nil;
923     if (ds.owner is TDataSet) then
924     {$IFDEF LINUX}
925     FDataSets.Remove(TDataSet(ds.Owner));
926     {$ELSE}
927     UnregisterClient(TDataSet(ds.Owner));
928     {$ENDIF}
929     end;
930     end;
931    
932     procedure TIBDatabase.RemoveSQLObjects;
933     var
934     i: Integer;
935     begin
936     for i := 0 to FSQLObjects.Count - 1 do if FSQLObjects[i] <> nil then
937     begin
938     RemoveSQLObject(i);
939     if (TIBBase(FSQLObjects[i]).owner is TDataSet) then
940     {$IFDEF LINUX}
941     FDataSets.Remove(TDataSet(TIBBase(FSQLObjects[i]).owner));
942     {$ELSE}
943     UnregisterClient(TDataSet(TIBBase(FSQLObjects[i]).owner));
944     {$ENDIF}
945     end;
946     end;
947    
948     procedure TIBDatabase.RemoveTransaction(Idx: Integer);
949     var
950     TR: TIBTransaction;
951     begin
952     if ((Idx >= 0) and (FTransactions[Idx] <> nil)) then
953     begin
954     TR := Transactions[Idx];
955     FTransactions[Idx] := nil;
956     TR.RemoveDatabase(TR.FindDatabase(Self));
957     if TR = FDefaultTransaction then
958     FDefaultTransaction := nil;
959     end;
960     end;
961    
962     procedure TIBDatabase.RemoveTransactions;
963     var
964     i: Integer;
965     begin
966     for i := 0 to FTransactions.Count - 1 do if FTransactions[i] <> nil then
967     RemoveTransaction(i);
968     end;
969    
970     procedure TIBDatabase.SetDatabaseName(const Value: TIBFileName);
971     begin
972     if FDBName <> Value then
973     begin
974     EnsureInactive;
975     CheckInactive;
976     FDBName := Value;
977     end;
978     end;
979    
980     procedure TIBDatabase.SetDBParamByDPB(const Idx: Integer; Value: String);
981     var
982     ConstIdx: Integer;
983     begin
984     ConstIdx := IndexOfDBConst(DPBConstantNames[Idx]);
985     if (Value = '') then
986     begin
987     if ConstIdx <> -1 then
988     Params.Delete(ConstIdx);
989     end
990     else
991     begin
992     if (ConstIdx = -1) then
993     Params.Add(DPBConstantNames[Idx] + '=' + Value)
994     else
995     Params[ConstIdx] := DPBConstantNames[Idx] + '=' + Value;
996     end;
997     end;
998    
999     procedure TIBDatabase.SetDBParams(Value: TStrings);
1000     begin
1001     FDBParams.Assign(Value);
1002     end;
1003    
1004     procedure TIBDatabase.SetDefaultTransaction(Value: TIBTransaction);
1005     var
1006     i: Integer;
1007     begin
1008     if (FDefaultTransaction <> nil) and (FDefaultTransaction <> Value) then
1009     begin
1010     i := FindTransaction(FDefaultTransaction);
1011     if (i <> -1) and (FDefaultTransaction.DefaultDatabase <> self) then
1012     RemoveTransaction(i);
1013     end;
1014     if (Value <> nil) and (FDefaultTransaction <> Value) then
1015     begin
1016     Value.AddDatabase(Self);
1017     AddTransaction(Value);
1018     end;
1019     FDefaultTransaction := Value;
1020     end;
1021    
1022     procedure TIBDatabase.SetHandle(Value: TISC_DB_HANDLE);
1023     begin
1024     if HandleIsShared then
1025     Close
1026     else
1027     CheckInactive;
1028     FHandle := Value;
1029     FHandleIsShared := (Value <> nil);
1030     end;
1031    
1032     procedure TIBDatabase.SetIdleTimer(Value: Integer);
1033     begin
1034     if Value < 0 then
1035     IBError(ibxeTimeoutNegative, [nil])
1036     else
1037     if (Value = 0) then
1038     begin
1039     FTimer.Enabled := False;
1040     FTimer.Interval := 0;
1041     end
1042     else
1043     if (Value > 0) then
1044     begin
1045     FTimer.Interval := Value;
1046     if not (csDesigning in ComponentState) then
1047     FTimer.Enabled := True;
1048     end;
1049     end;
1050    
1051     function TIBDatabase.TestConnected: Boolean;
1052     var
1053     DatabaseInfo: TIBDatabaseInfo;
1054     begin
1055     result := Connected;
1056     if result then
1057     begin
1058     DatabaseInfo := TIBDatabaseInfo.Create(self);
1059     try
1060     DatabaseInfo.Database := self;
1061     { poke the server to see if connected }
1062     if DatabaseInfo.BaseLevel = 0 then ;
1063     DatabaseInfo.Free;
1064     except
1065     ForceClose;
1066     result := False;
1067     DatabaseInfo.Free;
1068     end;
1069     end;
1070     end;
1071    
1072     procedure TIBDatabase.TimeoutConnection(Sender: TObject);
1073     begin
1074     if Connected then
1075     begin
1076     if FCanTimeout then
1077     begin
1078     ForceClose;
1079     if Assigned(FOnIdleTimer) then
1080     FOnIdleTimer(Self);
1081     end
1082     else
1083     FCanTimeout := True;
1084     end;
1085     end;
1086    
1087     function TIBDatabase.GetIsReadOnly: Boolean;
1088     var
1089     DatabaseInfo: TIBDatabaseInfo;
1090     begin
1091     DatabaseInfo := TIBDatabaseInfo.Create(self);
1092     DatabaseInfo.Database := self;
1093     if (DatabaseInfo.ODSMajorVersion < 10) then
1094     result := false
1095     else
1096     begin
1097     if (DatabaseInfo.ReadOnly = 0) then
1098     result := false
1099     else
1100     result := true;
1101     end;
1102     DatabaseInfo.Free;
1103     end;
1104    
1105     function TIBDatabase.GetSQLDialect: Integer;
1106     begin
1107     Result := FSQLDialect;
1108     end;
1109    
1110     procedure TIBDatabase.SetSQLDialect(const Value: Integer);
1111     begin
1112     if (Value < 1) then IBError(ibxeSQLDialectInvalid, [nil]);
1113     if ((FHandle = nil) or (Value <= FDBSQLDialect)) then
1114     FSQLDialect := Value
1115     else
1116     IBError(ibxeSQLDialectInvalid, [nil]);
1117     end;
1118    
1119     function TIBDatabase.GetDBSQLDialect: Integer;
1120     var
1121     DatabaseInfo: TIBDatabaseInfo;
1122     begin
1123     DatabaseInfo := TIBDatabaseInfo.Create(self);
1124     DatabaseInfo.Database := self;
1125     result := DatabaseInfo.DBSQLDialect;
1126     DatabaseInfo.Free;
1127     end;
1128    
1129     procedure TIBDatabase.ValidateClientSQLDialect;
1130     begin
1131     if (FDBSQLDialect < FSQLDialect) then
1132     begin
1133     FSQLDialect := FDBSQLDialect;
1134     if Assigned (FOnDialectDowngradeWarning) then
1135     FOnDialectDowngradeWarning(self);
1136     end;
1137     end;
1138    
1139     procedure TIBDatabase.ApplyUpdates(const DataSets: array of TDataSet);
1140     var
1141     I: Integer;
1142     DS: TIBCustomDataSet;
1143     TR: TIBTransaction;
1144     begin
1145     TR := nil;
1146     for I := 0 to High(DataSets) do
1147     begin
1148     DS := TIBCustomDataSet(DataSets[I]);
1149     if DS.Database <> Self then
1150     IBError(ibxeUpdateWrongDB, [nil]);
1151     if TR = nil then
1152     TR := DS.Transaction;
1153     if (DS.Transaction <> TR) or (TR = nil) then
1154     IBError(ibxeUpdateWrongTR, [nil]);
1155     end;
1156     TR.CheckInTransaction;
1157     for I := 0 to High(DataSets) do
1158     begin
1159     DS := TIBCustomDataSet(DataSets[I]);
1160     DS.ApplyUpdates;
1161     end;
1162     TR.CommitRetaining;
1163     end;
1164    
1165     procedure TIBDatabase.CloseDataSets;
1166     var
1167     i: Integer;
1168     begin
1169     for i := 0 to DataSetCount - 1 do
1170     if (DataSets[i] <> nil) then
1171     DataSets[i].close;
1172     end;
1173    
1174     function TIBDatabase.GetDataset(Index : longint) : TDataset;
1175     begin
1176     if (Index >= 0) and (Index < FDataSets.Count) then
1177     Result := TDataSet(FDataSets[Index])
1178     else
1179     raise Exception.Create('Invalid Index to DataSets');
1180     end;
1181    
1182     function TIBDatabase.GetDataSetCount : Longint;
1183     begin
1184     Result := FDataSets.Count;
1185     end;
1186    
1187     procedure TIBDatabase.GetFieldNames(const TableName: string; List: TStrings);
1188     var
1189     Query: TIBSQL;
1190     begin
1191     if TableName = '' then
1192     IBError(ibxeNoTableName, [nil]);
1193     if not Connected then
1194     Open;
1195     if not FInternalTransaction.Active then
1196     FInternalTransaction.StartTransaction;
1197     Query := TIBSQL.Create(self);
1198     try
1199     Query.GoToFirstRecordOnExecute := False;
1200     Query.Database := Self;
1201     Query.Transaction := FInternalTransaction;
1202     Query.SQL.Text := 'Select R.RDB$FIELD_NAME ' + {do not localize}
1203     'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
1204     'where R.RDB$RELATION_NAME = ' + {do not localize}
1205     '''' +
1206     FormatIdentifierValue(SQLDialect, TableName) +
1207     ''' ' +
1208     'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '; {do not localize}
1209     Query.Prepare;
1210     Query.ExecQuery;
1211     with List do
1212     begin
1213     BeginUpdate;
1214     try
1215     Clear;
1216     while (not Query.EOF) and (Query.Next <> nil) do
1217     List.Add(TrimRight(Query.Current.ByName('RDB$FIELD_NAME').AsString)); {do not localize}
1218     finally
1219     EndUpdate;
1220     end;
1221     end;
1222     finally
1223     Query.free;
1224     FInternalTransaction.Commit;
1225     end;
1226     end;
1227    
1228     procedure TIBDatabase.GetTableNames(List: TStrings; SystemTables: Boolean);
1229     var
1230     Query : TIBSQL;
1231     begin
1232     if not (csReading in ComponentState) then
1233     begin
1234     if not Connected then
1235     Open;
1236     if not FInternalTransaction.Active then
1237     FInternalTransaction.StartTransaction;
1238     Query := TIBSQL.Create(self);
1239     try
1240     Query.GoToFirstRecordOnExecute := False;
1241     Query.Database := Self;
1242     Query.Transaction := FInternalTransaction;
1243     if SystemTables then
1244     Query.SQL.Text := 'Select RDB$RELATION_NAME from RDB$RELATIONS' + {do not localize}
1245     ' where RDB$VIEW_BLR is NULL' {do not localize}
1246     else
1247     Query.SQL.Text := 'Select RDB$RELATION_NAME from RDB$RELATIONS' + {do not localize}
1248     ' where RDB$VIEW_BLR is NULL and RDB$SYSTEM_FLAG = 0'; {do not localize}
1249     Query.Prepare;
1250     Query.ExecQuery;
1251     with List do
1252     begin
1253     BeginUpdate;
1254     try
1255     Clear;
1256     while (not Query.EOF) and (Query.Next <> nil) do
1257     List.Add(TrimRight(Query.Current[0].AsString));
1258     finally
1259     EndUpdate;
1260     end;
1261     end;
1262     finally
1263     Query.Free;
1264     FInternalTransaction.Commit;
1265     end;
1266     end;
1267     end;
1268    
1269     { TIBTransaction }
1270    
1271     constructor TIBTransaction.Create(AOwner: TComponent);
1272     begin
1273     inherited Create(AOwner);
1274     FIBLoaded := False;
1275     CheckIBLoaded;
1276     FIBLoaded := True;
1277     CheckIBLoaded;
1278     FDatabases := TList.Create;
1279     FSQLObjects := TList.Create;
1280     FHandle := nil;
1281     FTPB := nil;
1282     FTPBLength := 0;
1283     FTRParams := TStringList.Create;
1284     FTRParamsChanged := True;
1285     TStringList(FTRParams).OnChange := TRParamsChange;
1286     TStringList(FTRParams).OnChanging := TRParamsChanging;
1287     FTimer := TTimer.Create(Self);
1288     FTimer.Enabled := False;
1289     FTimer.Interval := 0;
1290     FTimer.OnTimer := TimeoutTransaction;
1291     FDefaultAction := taCommit;
1292     end;
1293    
1294     destructor TIBTransaction.Destroy;
1295     var
1296     i: Integer;
1297     begin
1298     if FIBLoaded then
1299     begin
1300     if InTransaction then
1301     EndTransaction(FDefaultAction, True);
1302     for i := 0 to FSQLObjects.Count - 1 do
1303     if FSQLObjects[i] <> nil then
1304     SQLObjects[i].DoTransactionFree;
1305     RemoveSQLObjects;
1306     RemoveDatabases;
1307     FreeMem(FTPB);
1308     FTPB := nil;
1309     FTRParams.Free;
1310     FSQLObjects.Free;
1311     FDatabases.Free;
1312     end;
1313     inherited Destroy;
1314     end;
1315    
1316     function TIBTransaction.Call(ErrCode: ISC_STATUS;
1317     RaiseError: Boolean): ISC_STATUS;
1318     var
1319     i: Integer;
1320     begin
1321     result := ErrCode;
1322     for i := 0 to FDatabases.Count - 1 do if FDatabases[i] <> nil then
1323     Databases[i].FCanTimeout := False;
1324     FCanTimeout := False;
1325     if RaiseError and (result > 0) then
1326     IBDataBaseError;
1327     end;
1328    
1329     procedure TIBTransaction.CheckDatabasesInList;
1330     begin
1331     if GetDatabaseCount = 0 then
1332     IBError(ibxeNoDatabasesInTransaction, [nil]);
1333     end;
1334    
1335     procedure TIBTransaction.CheckInTransaction;
1336     begin
1337     if FStreamedActive and (not InTransaction) then
1338     Loaded;
1339     if (FHandle = nil) then
1340     IBError(ibxeNotInTransaction, [nil]);
1341     end;
1342    
1343     procedure TIBTransaction.EnsureNotInTransaction;
1344     begin
1345     if csDesigning in ComponentState then
1346     begin
1347     if FHandle <> nil then
1348     Rollback;
1349     end;
1350     end;
1351    
1352     procedure TIBTransaction.CheckNotInTransaction;
1353     begin
1354     if (FHandle <> nil) then
1355     IBError(ibxeInTransaction, [nil]);
1356     end;
1357    
1358     function TIBTransaction.AddDatabase(db: TIBDatabase): Integer;
1359     var
1360     i: Integer;
1361     NilFound: Boolean;
1362     begin
1363     i := FindDatabase(db);
1364     if i <> -1 then
1365     begin
1366     result := i;
1367     exit;
1368     end;
1369     NilFound := False;
1370     i := 0;
1371     while (not NilFound) and (i < FDatabases.Count) do
1372     begin
1373     NilFound := (FDatabases[i] = nil);
1374     if (not NilFound) then
1375     Inc(i);
1376     end;
1377     if (NilFound) then
1378     begin
1379     FDatabases[i] := db;
1380     result := i;
1381     end
1382     else
1383     begin
1384     result := FDatabases.Count;
1385     FDatabases.Add(db);
1386     end;
1387     end;
1388    
1389     function TIBTransaction.AddSQLObject(ds: TIBBase): Integer;
1390     begin
1391     result := 0;
1392     while (result < FSQLObjects.Count) and (FSQLObjects[result] <> nil) do
1393     Inc(result);
1394     if (result = FSQLObjects.Count) then
1395     FSQLObjects.Add(ds)
1396     else
1397     FSQLObjects[result] := ds;
1398     end;
1399    
1400     procedure TIBTransaction.Commit;
1401     begin
1402     EndTransaction(TACommit, False);
1403     end;
1404    
1405     procedure TIBTransaction.CommitRetaining;
1406     begin
1407     EndTransaction(TACommitRetaining, False);
1408     end;
1409    
1410     procedure TIBTransaction.EndTransaction(Action: TTransactionAction;
1411     Force: Boolean);
1412     var
1413     status: ISC_STATUS;
1414     i: Integer;
1415     begin
1416     CheckInTransaction;
1417     case Action of
1418     TARollback, TACommit:
1419     begin
1420     if (HandleIsShared) and
1421     (Action <> FDefaultAction) and
1422     (not Force) then
1423     IBError(ibxeCantEndSharedTransaction, [nil]);
1424     for i := 0 to FSQLObjects.Count - 1 do if FSQLObjects[i] <> nil then
1425     SQLObjects[i].DoBeforeTransactionEnd;
1426     if InTransaction then
1427     begin
1428     if HandleIsShared then
1429     begin
1430     FHandle := nil;
1431     FHandleIsShared := False;
1432     status := 0;
1433     end
1434     else
1435     if (Action = TARollback) then
1436     status := Call(isc_rollback_transaction(StatusVector, @FHandle), False)
1437     else
1438     status := Call(isc_commit_transaction(StatusVector, @FHandle), False);
1439     if ((Force) and (status > 0)) then
1440     status := Call(isc_rollback_transaction(StatusVector, @FHandle), False);
1441     if Force then
1442     FHandle := nil
1443     else
1444     if (status > 0) then
1445     IBDataBaseError;
1446     for i := 0 to FSQLObjects.Count - 1 do if FSQLObjects[i] <> nil then
1447     SQLObjects[i].DoAfterTransactionEnd;
1448     end;
1449     end;
1450     TACommitRetaining:
1451     Call(isc_commit_retaining(StatusVector, @FHandle), True);
1452     TARollbackRetaining:
1453     Call(isc_rollback_retaining(StatusVector, @FHandle), True);
1454     end;
1455     {$IFDEF HAS_SQLMONITOR}
1456     if not (csDesigning in ComponentState) then
1457     begin
1458     case Action of
1459     TACommit:
1460     MonitorHook.TRCommit(Self);
1461     TARollback:
1462     MonitorHook.TRRollback(Self);
1463     TACommitRetaining:
1464     MonitorHook.TRCommitRetaining(Self);
1465     TARollbackRetaining:
1466     MonitorHook.TRRollbackRetaining(Self);
1467     end;
1468     end;
1469     {$ENDIF}
1470     end;
1471    
1472     function TIBTransaction.GetDatabase(Index: Integer): TIBDatabase;
1473     begin
1474     result := FDatabases[Index];
1475     end;
1476    
1477     function TIBTransaction.GetDatabaseCount: Integer;
1478     var
1479     i, Cnt: Integer;
1480     begin
1481     result := 0;
1482     Cnt := FDatabases.Count - 1;
1483     for i := 0 to Cnt do if FDatabases[i] <> nil then
1484     Inc(result);
1485     end;
1486    
1487     function TIBTransaction.GetSQLObject(Index: Integer): TIBBase;
1488     begin
1489     result := FSQLObjects[Index];
1490     end;
1491    
1492     function TIBTransaction.GetSQLObjectCount: Integer;
1493     var
1494     i, Cnt: Integer;
1495     begin
1496     result := 0;
1497     Cnt := FSQLObjects.Count - 1;
1498     for i := 0 to Cnt do if FSQLObjects[i] <> nil then
1499     Inc(result);
1500     end;
1501    
1502     function TIBTransaction.GetInTransaction: Boolean;
1503     begin
1504     result := (FHandle <> nil);
1505     end;
1506    
1507     function TIBTransaction.FindDatabase(db: TIBDatabase): Integer;
1508     var
1509     i: Integer;
1510     begin
1511     result := -1;
1512     for i := 0 to FDatabases.Count - 1 do
1513     if db = TIBDatabase(FDatabases[i]) then
1514     begin
1515     result := i;
1516     break;
1517     end;
1518     end;
1519    
1520     function TIBTransaction.FindDefaultDatabase: TIBDatabase;
1521     var
1522     i: Integer;
1523     begin
1524     result := FDefaultDatabase;
1525     if result = nil then
1526     begin
1527     for i := 0 to FDatabases.Count - 1 do
1528     if (TIBDatabase(FDatabases[i]) <> nil) and
1529     (TIBDatabase(FDatabases[i]).DefaultTransaction = self) then
1530     begin
1531     result := TIBDatabase(FDatabases[i]);
1532     break;
1533     end;
1534     end;
1535     end;
1536    
1537    
1538     function TIBTransaction.GetIdleTimer: Integer;
1539     begin
1540     result := FTimer.Interval;
1541     end;
1542    
1543     procedure TIBTransaction.Loaded;
1544     begin
1545     inherited Loaded;
1546     end;
1547    
1548     procedure TIBTransaction.BeforeDatabaseDisconnect(DB: TIBDatabase);
1549     begin
1550     if InTransaction then
1551     EndTransaction(FDefaultAction, True);
1552     end;
1553    
1554     procedure TIBTransaction.RemoveDatabase(Idx: Integer);
1555     var
1556     DB: TIBDatabase;
1557     begin
1558     if ((Idx >= 0) and (FDatabases[Idx] <> nil)) then
1559     begin
1560     DB := Databases[Idx];
1561     FDatabases[Idx] := nil;
1562     DB.RemoveTransaction(DB.FindTransaction(Self));
1563     if DB = FDefaultDatabase then
1564     FDefaultDatabase := nil;
1565     end;
1566     end;
1567    
1568     procedure TIBTransaction.RemoveDatabases;
1569     var
1570     i: Integer;
1571     begin
1572     for i := 0 to FDatabases.Count - 1 do if FDatabases[i] <> nil then
1573     RemoveDatabase(i);
1574     end;
1575    
1576     procedure TIBTransaction.RemoveSQLObject(Idx: Integer);
1577     var
1578     ds: TIBBase;
1579     begin
1580     if ((Idx >= 0) and (FSQLObjects[Idx] <> nil)) then
1581     begin
1582     ds := SQLObjects[Idx];
1583     FSQLObjects[Idx] := nil;
1584     ds.Transaction := nil;
1585     end;
1586     end;
1587    
1588     procedure TIBTransaction.RemoveSQLObjects;
1589     var
1590     i: Integer;
1591     begin
1592     for i := 0 to FSQLObjects.Count - 1 do if FSQLObjects[i] <> nil then
1593     RemoveSQLObject(i);
1594     end;
1595    
1596     procedure TIBTransaction.Rollback;
1597     begin
1598     EndTransaction(TARollback, False);
1599     end;
1600    
1601     procedure TIBTransaction.RollbackRetaining;
1602     begin
1603     EndTransaction(TARollbackRetaining, False);
1604     end;
1605    
1606     procedure TIBTransaction.SetActive(Value: Boolean);
1607     begin
1608     if csReading in ComponentState then
1609     FStreamedActive := Value
1610     else
1611     if Value and not InTransaction then
1612     StartTransaction
1613     else
1614     if not Value and InTransaction then
1615     Rollback;
1616     end;
1617    
1618     procedure TIBTransaction.SetDefaultAction(Value: TTransactionAction);
1619     begin
1620     (* if (Value = taRollbackRetaining) and (GetIBClientVersion < 6) then
1621     IBError(ibxeIB60feature, [nil]);*)
1622     FDefaultAction := Value;
1623     end;
1624    
1625     procedure TIBTransaction.SetDefaultDatabase(Value: TIBDatabase);
1626     var
1627     i: integer;
1628     begin
1629     if (FDefaultDatabase <> nil) and (FDefaultDatabase <> Value) then
1630     begin
1631     i := FDefaultDatabase.FindTransaction(self);
1632     if (i <> -1) then
1633     FDefaultDatabase.RemoveTransaction(i);
1634     end;
1635     if (Value <> nil) and (FDefaultDatabase <> Value) then
1636     begin
1637     Value.AddTransaction(Self);
1638     AddDatabase(Value);
1639     for i := 0 to FSQLObjects.Count - 1 do
1640     if (FSQLObjects[i] <> nil) and
1641     (TIBBase(FSQLObjects[i]).Database = nil) then
1642     SetOrdProp(TIBBase(FSQLObjects[i]).Owner, 'Database', Integer(Value));
1643     end;
1644     FDefaultDatabase := Value;
1645     end;
1646    
1647     procedure TIBTransaction.SetHandle(Value: TISC_TR_HANDLE);
1648     begin
1649     if (HandleIsShared) then
1650     EndTransaction(DefaultAction, True)
1651     else
1652     CheckNotInTransaction;
1653     FHandle := Value;
1654     FHandleIsShared := (Value <> nil);
1655     end;
1656    
1657     procedure TIBTransaction.Notification( AComponent: TComponent;
1658     Operation: TOperation);
1659     var
1660     i: Integer;
1661     begin
1662     inherited Notification( AComponent, Operation);
1663     if (Operation = opRemove) and (AComponent = FDefaultDatabase) then
1664     begin
1665     i := FindDatabase(FDefaultDatabase);
1666     if (i <> -1) then
1667     RemoveDatabase(i);
1668     FDefaultDatabase := nil;
1669     end;
1670     end;
1671    
1672     procedure TIBTransaction.SetIdleTimer(Value: Integer);
1673     begin
1674     if Value < 0 then
1675     IBError(ibxeTimeoutNegative, [nil])
1676     else
1677     if (Value = 0) then
1678     begin
1679     FTimer.Enabled := False;
1680     FTimer.Interval := 0;
1681     end
1682     else
1683     if (Value > 0) then
1684     begin
1685     FTimer.Interval := Value;
1686     if not (csDesigning in ComponentState) then
1687     FTimer.Enabled := True;
1688     end;
1689     end;
1690    
1691     procedure TIBTransaction.SetTRParams(Value: TStrings);
1692     begin
1693     FTRParams.Assign(Value);
1694     end;
1695    
1696     procedure TIBTransaction.StartTransaction;
1697     var
1698     pteb: PISC_TEB_ARRAY;
1699     TPB: String;
1700     i: Integer;
1701     begin
1702     CheckNotInTransaction;
1703     CheckDatabasesInList;
1704     for i := 0 to FDatabases.Count - 1 do
1705     if FDatabases[i] <> nil then
1706     begin
1707     with TIBDatabase(FDatabases[i]) do
1708     if not Connected then
1709     if FStreamedConnected then
1710     begin
1711     Open;
1712     FStreamedConnected := False;
1713     end
1714     else
1715     IBError(ibxeDatabaseClosed, [nil]);
1716     end;
1717     if FTRParamsChanged then
1718     begin
1719     FTRParamsChanged := False;
1720     GenerateTPB(FTRParams, TPB, FTPBLength);
1721     if FTPBLength > 0 then
1722     begin
1723     IBAlloc(FTPB, 0, FTPBLength);
1724     Move(TPB[1], FTPB[0], FTPBLength);
1725     end;
1726     end;
1727    
1728     pteb := nil;
1729     IBAlloc(pteb, 0, DatabaseCount * SizeOf(TISC_TEB));
1730     try
1731     for i := 0 to DatabaseCount - 1 do if Databases[i] <> nil then
1732     begin
1733     pteb^[i].db_handle := @(Databases[i].Handle);
1734     pteb^[i].tpb_length := FTPBLength;
1735     pteb^[i].tpb_address := FTPB;
1736     end;
1737     if Call(isc_start_multiple(StatusVector, @FHandle,
1738     DatabaseCount, PISC_TEB(pteb)), False) > 0 then
1739     begin
1740     FHandle := nil;
1741     IBDataBaseError;
1742     end;
1743     {$IFDEF HAS_SQLMONITOR}
1744     if not (csDesigning in ComponentState) then
1745     MonitorHook.TRStart(Self);
1746     {$ENDIF}
1747     finally
1748     FreeMem(pteb);
1749     end;
1750     end;
1751    
1752     procedure TIBTransaction.TimeoutTransaction(Sender: TObject);
1753     begin
1754     if InTransaction then
1755     begin
1756     if FCanTimeout then
1757     begin
1758     EndTransaction(FDefaultAction, True);
1759     if Assigned(FOnIdleTimer) then
1760     FOnIdleTimer(Self);
1761     end
1762     else
1763     FCanTimeout := True;
1764     end;
1765     end;
1766    
1767     procedure TIBTransaction.TRParamsChange(Sender: TObject);
1768     begin
1769     FTRParamsChanged := True;
1770     end;
1771    
1772     procedure TIBTransaction.TRParamsChanging(Sender: TObject);
1773     begin
1774     EnsureNotInTransaction;
1775     CheckNotInTransaction;
1776     end;
1777    
1778     { TIBBase }
1779     constructor TIBBase.Create(AOwner: TObject);
1780     begin
1781     FOwner := AOwner;
1782     end;
1783    
1784     destructor TIBBase.Destroy;
1785     begin
1786     SetDatabase(nil);
1787     SetTransaction(nil);
1788     inherited Destroy;
1789     end;
1790    
1791     procedure TIBBase.CheckDatabase;
1792     begin
1793     if (FDatabase = nil) then
1794     IBError(ibxeDatabaseNotAssigned, [nil]);
1795     FDatabase.CheckActive;
1796     end;
1797    
1798     procedure TIBBase.CheckTransaction;
1799     begin
1800     if FTransaction = nil then
1801     IBError(ibxeTransactionNotAssigned, [nil]);
1802     FTransaction.CheckInTransaction;
1803     end;
1804    
1805     function TIBBase.GetDBHandle: PISC_DB_HANDLE;
1806     begin
1807     CheckDatabase;
1808     result := @FDatabase.Handle;
1809     end;
1810    
1811     function TIBBase.GetTRHandle: PISC_TR_HANDLE;
1812     begin
1813     CheckTransaction;
1814     result := @FTransaction.Handle;
1815     end;
1816    
1817     procedure TIBBase.DoBeforeDatabaseDisconnect;
1818     begin
1819     if Assigned(BeforeDatabaseDisconnect) then
1820     BeforeDatabaseDisconnect(Self);
1821     end;
1822    
1823     procedure TIBBase.DoAfterDatabaseDisconnect;
1824     begin
1825     if Assigned(AfterDatabaseDisconnect) then
1826     AfterDatabaseDisconnect(Self);
1827     end;
1828    
1829     procedure TIBBase.DoDatabaseFree;
1830     begin
1831     if Assigned(OnDatabaseFree) then
1832     OnDatabaseFree(Self);
1833     SetDatabase(nil);
1834     SetTransaction(nil);
1835     end;
1836    
1837     procedure TIBBase.DoBeforeTransactionEnd;
1838     begin
1839     if Assigned(BeforeTransactionEnd) then
1840     BeforeTransactionEnd(Self);
1841     end;
1842    
1843     procedure TIBBase.DoAfterTransactionEnd;
1844     begin
1845     if Assigned(AfterTransactionEnd) then
1846     AfterTransactionEnd(Self);
1847     end;
1848    
1849     procedure TIBBase.DoTransactionFree;
1850     begin
1851     if Assigned(OnTransactionFree) then
1852     OnTransactionFree(Self);
1853     FTransaction := nil;
1854     end;
1855    
1856     procedure TIBBase.SetDatabase(Value: TIBDatabase);
1857     begin
1858     if (FDatabase <> nil) then
1859     FDatabase.RemoveSQLObject(FIndexInDatabase);
1860     FDatabase := Value;
1861     if (FDatabase <> nil) then
1862     begin
1863     FIndexInDatabase := FDatabase.AddSQLObject(Self);
1864     if (FTransaction = nil) then
1865     Transaction := FDatabase.FindDefaultTransaction;
1866     end;
1867     end;
1868    
1869     procedure TIBBase.SetTransaction(Value: TIBTransaction);
1870     begin
1871     if (FTransaction <> nil) then
1872     FTransaction.RemoveSQLObject(FIndexInTransaction);
1873     FTransaction := Value;
1874     if (FTransaction <> nil) then
1875     begin
1876     FIndexInTransaction := FTransaction.AddSQLObject(Self);
1877     if (FDatabase = nil) then
1878     Database := FTransaction.FindDefaultDatabase;
1879     end;
1880     end;
1881    
1882     { GenerateDPB -
1883     Given a string containing a textual representation
1884     of the database parameters, generate a database
1885     parameter buffer, and return it and its length
1886     in DPB and DPBLength, respectively. }
1887    
1888     procedure GenerateDPB(sl: TStrings; var DPB: string; var DPBLength: Short);
1889     var
1890     i, j, pval: Integer;
1891     DPBVal: UShort;
1892     ParamName, ParamValue: string;
1893     begin
1894     { The DPB is initially empty, with the exception that
1895     the DPB version must be the first byte of the string. }
1896     DPBLength := 1;
1897     DPB := Char(isc_dpb_version1);
1898    
1899     {Iterate through the textual database parameters, constructing
1900     a DPB on-the-fly }
1901     for i := 0 to sl.Count - 1 do
1902     begin
1903     { Get the parameter's name and value from the list,
1904     and make sure that the name is all lowercase with
1905     no leading 'isc_dpb_' prefix
1906     }
1907     if (Trim(sl.Names[i]) = '') then
1908     continue;
1909     ParamName := LowerCase(sl.Names[i]); {mbcs ok}
1910     ParamValue := Copy(sl[i], Pos('=', sl[i]) + 1, Length(sl[i])); {mbcs ok}
1911     if (Pos(DPBPrefix, ParamName) = 1) then {mbcs ok}
1912     Delete(ParamName, 1, Length(DPBPrefix));
1913     { We want to translate the parameter name to some Integer
1914     value. We do this by scanning through a list of known
1915     database parameter names (DPBConstantNames, defined above) }
1916     DPBVal := 0;
1917     { Find the parameter }
1918     for j := 1 to isc_dpb_last_dpb_constant do
1919     if (ParamName = DPBConstantNames[j]) then
1920     begin
1921     DPBVal := j;
1922     break;
1923     end;
1924     { A database parameter either contains a string value (case 1)
1925     or an Integer value (case 2)
1926     or no value at all (case 3)
1927     or an error needs to be generated (case else) }
1928     case DPBVal of
1929     isc_dpb_user_name, isc_dpb_password, isc_dpb_password_enc,
1930     isc_dpb_sys_user_name, isc_dpb_license, isc_dpb_encrypt_key,
1931     isc_dpb_lc_messages, isc_dpb_lc_ctype,
1932     isc_dpb_sql_role_name, isc_dpb_sql_dialect:
1933     begin
1934     if DPBVal = isc_dpb_sql_dialect then
1935     ParamValue[1] := Char(Ord(ParamValue[1]) - 48);
1936     DPB := DPB +
1937     Char(DPBVal) +
1938     Char(Length(ParamValue)) +
1939     ParamValue;
1940     Inc(DPBLength, 2 + Length(ParamValue));
1941     end;
1942     isc_dpb_num_buffers, isc_dpb_dbkey_scope, isc_dpb_force_write,
1943     isc_dpb_no_reserve, isc_dpb_damaged, isc_dpb_verify:
1944     begin
1945     DPB := DPB +
1946     Char(DPBVal) +
1947     #1 +
1948     Char(StrToInt(ParamValue));
1949     Inc(DPBLength, 3);
1950     end;
1951     isc_dpb_sweep:
1952     begin
1953     DPB := DPB +
1954     Char(DPBVal) +
1955     #1 +
1956     Char(isc_dpb_records);
1957     Inc(DPBLength, 3);
1958     end;
1959     isc_dpb_sweep_interval:
1960     begin
1961     pval := StrToInt(ParamValue);
1962     DPB := DPB +
1963     Char(DPBVal) +
1964     #4 +
1965     PChar(@pval)[0] +
1966     PChar(@pval)[1] +
1967     PChar(@pval)[2] +
1968     PChar(@pval)[3];
1969     Inc(DPBLength, 6);
1970     end;
1971     isc_dpb_activate_shadow, isc_dpb_delete_shadow, isc_dpb_begin_log,
1972     isc_dpb_quit_log:
1973     begin
1974     DPB := DPB +
1975     Char(DPBVal) +
1976     #1 + #0;
1977     Inc(DPBLength, 3);
1978     end;
1979     else
1980     begin
1981     if (DPBVal > 0) and
1982     (DPBVal <= isc_dpb_last_dpb_constant) then
1983     IBError(ibxeDPBConstantNotSupported, [DPBConstantNames[DPBVal]])
1984     else
1985     IBError(ibxeDPBConstantUnknownEx, [sl.Names[i]]);
1986     end;
1987     end;
1988     end;
1989     end;
1990    
1991     { GenerateTPB -
1992     Given a string containing a textual representation
1993     of the transaction parameters, generate a transaction
1994     parameter buffer, and return it and its length in
1995     TPB and TPBLength, respectively. }
1996     procedure GenerateTPB(sl: TStrings; var TPB: string; var TPBLength: Short);
1997     var
1998     i, j, TPBVal, ParamLength: Integer;
1999     ParamName, ParamValue: string;
2000     begin
2001     TPB := '';
2002     if (sl.Count = 0) then
2003     TPBLength := 0
2004     else
2005     begin
2006     TPBLength := sl.Count + 1;
2007     TPB := TPB + Char(isc_tpb_version3);
2008     end;
2009     for i := 0 to sl.Count - 1 do
2010     begin
2011     if (Trim(sl[i]) = '') then
2012     begin
2013     Dec(TPBLength);
2014     Continue;
2015     end;
2016     if (Pos('=', sl[i]) = 0) then {mbcs ok}
2017     ParamName := LowerCase(sl[i]) {mbcs ok}
2018     else
2019     begin
2020     ParamName := LowerCase(sl.Names[i]); {mbcs ok}
2021     ParamValue := Copy(sl[i], Pos('=', sl[i]) + 1, Length(sl[i])); {mbcs ok}
2022     end;
2023     if (Pos(TPBPrefix, ParamName) = 1) then {mbcs ok}
2024     Delete(ParamName, 1, Length(TPBPrefix));
2025     TPBVal := 0;
2026     { Find the parameter }
2027     for j := 1 to isc_tpb_last_tpb_constant do
2028     if (ParamName = TPBConstantNames[j]) then
2029     begin
2030     TPBVal := j;
2031     break;
2032     end;
2033     { Now act on it }
2034     case TPBVal of
2035     isc_tpb_consistency, isc_tpb_exclusive, isc_tpb_protected,
2036     isc_tpb_concurrency, isc_tpb_shared, isc_tpb_wait, isc_tpb_nowait,
2037     isc_tpb_read, isc_tpb_write, isc_tpb_ignore_limbo,
2038     isc_tpb_read_committed, isc_tpb_rec_version, isc_tpb_no_rec_version:
2039     TPB := TPB + Char(TPBVal);
2040     isc_tpb_lock_read, isc_tpb_lock_write:
2041     begin
2042     TPB := TPB + Char(TPBVal);
2043     { Now set the string parameter }
2044     ParamLength := Length(ParamValue);
2045     Inc(TPBLength, ParamLength + 1);
2046     TPB := TPB + Char(ParamLength) + ParamValue;
2047     end;
2048     else
2049     begin
2050     if (TPBVal > 0) and
2051     (TPBVal <= isc_tpb_last_tpb_constant) then
2052     IBError(ibxeTPBConstantNotSupported, [TPBConstantNames[TPBVal]])
2053     else
2054     IBError(ibxeTPBConstantUnknownEx, [sl.Names[i]]);
2055     end;
2056     end;
2057     end;
2058     end;
2059    
2060     end.
2061    
2062    
2063    
2064    
2065