ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBDatabase.pas
Revision: 33
Committed: Sat Jul 18 12:30:52 2015 UTC (8 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 65454 byte(s)
Log Message:
Committing updates for Release R1-3-1

File Contents

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