ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBDatabase.pas
Revision: 21
Committed: Thu Feb 26 10:33:34 2015 UTC (9 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 60805 byte(s)
Log Message:
Committing updates for Release R1-2-0

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