ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBDatabase.pas
Revision: 17
Committed: Sat Dec 28 19:22:24 2013 UTC (10 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 59696 byte(s)
Log Message:
Committing updates for Release R1-0-5

File Contents

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