ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBSQLMonitor.pas
Revision: 291
Committed: Fri Apr 17 10:26:08 2020 UTC (4 years, 7 months ago) by tony
Content type: text/x-pascal
File size: 34233 byte(s)
Log Message:
Changed for 2.3.4 merged into public release

File Contents

# User Rev Content
1 tony 209 {************************************************************************}
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 - 2018 }
31     { }
32     {************************************************************************}
33    
34     {
35     This unit has been significantly revised for the Lazarus port. Specially,
36     there was a need to re-organise the code to isolate the Windows specific
37     IPC and to introduce SV5 IPC as an alternative for Linux and other platforms.
38     }
39    
40     unit IBSQLMonitor;
41    
42     {$Mode Delphi}
43    
44     {$codepage UTF8}
45    
46     interface
47    
48     uses
49     IB, IBUtils, IBSQL, IBCustomDataSet, IBDatabase, DB,
50     IBTypes ,SysUtils, Classes,
51     {$IFDEF WINDOWS }
52     Windows
53     {$ENDIF}
54 tony 215 {$IFDEF UNIX}
55 tony 216 unix
56 tony 215 {$ENDIF}
57 tony 209 ;
58    
59     {Note that the original inter-thread communication between the Reader Thread and
60     the ISQL Monitor used the Windows PostMessage interface. This is currently not
61     useable under the FPC RTL as AllocateHWnd is not functional. It has been replaced
62     by the use of the Synchronize method.}
63    
64     {$IFDEF WINDOWS}
65     {$DEFINE USE_WINDOWS_IPC}
66     {$ENDIF}
67    
68     {$IFDEF UNIX}
69     {$DEFINE USE_SV5_IPC}
70     {$ENDIF}
71    
72     {$IFDEF LINUX}
73     {$DEFINE HAS_SEMTIMEDOP}
74     {$ENDIF}
75    
76     type
77     TIBCustomSQLMonitor = class;
78    
79     TSQLEvent = procedure(EventText: String; EventTime : TDateTime) of object;
80    
81     { TIBCustomSQLMonitor }
82    
83     TIBCustomSQLMonitor = class(TComponent)
84     private
85     FOnSQLEvent: TSQLEvent;
86     FTraceFlags: TTraceFlags;
87     FEnabled: Boolean;
88     procedure SetEnabled(const Value: Boolean);
89     protected
90     procedure ReleaseObject; {Called from Writer Thread}
91     procedure ReceiveMessage(Msg: TObject); {Called from Reader Thread}
92     property OnSQL: TSQLEvent read FOnSQLEvent write FOnSQLEvent;
93     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
94     property Enabled : Boolean read FEnabled write SetEnabled default true;
95     public
96     constructor Create(AOwner: TComponent); override;
97     destructor Destroy; override;
98     procedure Release;
99     end;
100    
101     { TIBSQLMonitor }
102    
103     TIBSQLMonitor = class(TIBCustomSQLMonitor)
104     published
105     property OnSQL;
106     property TraceFlags;
107     property Enabled;
108     end;
109    
110     TIBMonitoredService = class(TComponent)
111     private
112     FTraceFlags: TTraceFlags;
113     protected
114     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
115     end;
116    
117     { TIBXMonitoredService }
118    
119     TIBXMonitoredService = class(TComponent)
120     private
121     FTraceFlags: TTraceFlags;
122     protected
123     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
124     end;
125    
126     TIBXMonitoredConnection = class(TCustomConnection)
127     private
128     FTraceFlags: TTraceFlags;
129     protected
130     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
131     end;
132    
133     IIBSQLMonitorHook = interface
134     ['{CF65434C-9B75-4298-BA7E-E6B85B3C769D}']
135     procedure RegisterMonitor(SQLMonitor : TIBCustomSQLMonitor);
136     procedure UnregisterMonitor(SQLMonitor : TIBCustomSQLMonitor);
137     procedure ReleaseMonitor(Arg : TIBCustomSQLMonitor);
138     procedure SQLPrepare(qry: TIBSQL);
139     procedure SQLExecute(qry: TIBSQL);
140     procedure SQLFetch(qry: TIBSQL);
141     procedure DBConnect(db: TIBDatabase);
142     procedure DBDisconnect(db: TIBDatabase);
143     procedure TRStart(tr: TIBTransaction);
144     procedure TRCommit(tr: TIBTransaction);
145     procedure TRCommitRetaining(tr: TIBTransaction);
146     procedure TRRollback(tr: TIBTransaction);
147     procedure TRRollbackRetaining(tr: TIBTransaction);
148     procedure ServiceAttach(service: TIBMonitoredService); overload;
149     procedure ServiceDetach(service: TIBMonitoredService); overload;
150     procedure ServiceQuery(service: TIBMonitoredService); overload;
151     procedure ServiceStart(service: TIBMonitoredService); overload;
152     procedure ServiceAttach(service: TIBXMonitoredConnection); overload;
153     procedure ServiceDetach(service: TIBXMonitoredConnection); overload;
154     procedure ServiceQuery(service: TIBXMonitoredService); overload;
155     procedure ServiceStart(service: TIBXMonitoredService); overload;
156     procedure SendMisc(Msg : String);
157     function GetTraceFlags : TTraceFlags;
158     function GetMonitorCount : Integer;
159     procedure SetTraceFlags(const Value : TTraceFlags);
160     function GetEnabled : boolean;
161     procedure SetEnabled(const Value : Boolean);
162     property TraceFlags: TTraceFlags read GetTraceFlags write SetTraceFlags;
163     property Enabled : Boolean read GetEnabled write SetEnabled;
164     end;
165    
166    
167     function MonitorHook: IIBSQLMonitorHook;
168     procedure EnableMonitoring;
169     procedure DisableMonitoring;
170     function MonitoringEnabled: Boolean;
171    
172     implementation
173    
174     uses
175 tony 291 contnrs, syncobjs, CustApp, IBMessages
176 tony 209 {$IFDEF USE_SV5_IPC}
177     ,ipc, Errors, baseunix
178     {$ENDIF};
179    
180    
181     const
182     cMonitorHookSize = 1024;
183     cMsgWaitTime = 1000;
184     cWriteMessageAvailable = 'WriterMsgQueue';
185    
186     type
187     { There are two possible objects. One is a trace message object.
188     This object holds the flag of the trace type plus the message.
189     The second object is a Release object. It holds the handle that
190     the CM_RELEASE message is to be queued to. }
191    
192     { TTraceObject }
193    
194     TTraceObject = Class(TObject)
195     FDataType : TTraceFlag;
196     FMsg : String;
197     FTimeStamp : TDateTime;
198     public
199     constructor Create(Msg : String; DataType : TTraceFlag); overload;
200     constructor Create(obj : TTraceObject); overload;
201     constructor Create(obj : TTraceObject; MsgOffset, MsgLen: integer); overload;
202     end;
203    
204     { TReleaseObject }
205    
206     TReleaseObject = Class(TObject)
207     FMonitor : TIBCustomSQLMonitor;
208     public
209     constructor Create(Monitor : TIBCustomSQLMonitor);
210     end;
211    
212     {$IFDEF USE_SV5_IPC}
213     {$I sv5ipc.inc}
214     {$ENDIF}
215     {$IFDEF USE_WINDOWS_IPC}
216     {$I winipc.inc}
217     {$ENDIF}
218    
219     type
220    
221     { TIBSQLMonitorHook }
222    
223     TIBSQLMonitorHook = class(TInterfacedObject, IIBSQLMonitorHook)
224     private
225     FGlobalInterface: TGlobalInterface;
226     FTraceFlags: TTraceFlags;
227     FEnabled: Boolean;
228     protected
229     procedure WriteSQLData(Text: String; DataType: TTraceFlag);
230     public
231     constructor Create;
232     destructor Destroy; override;
233     procedure RegisterMonitor(SQLMonitor : TIBCustomSQLMonitor);
234     procedure UnregisterMonitor(SQLMonitor : TIBCustomSQLMonitor);
235     procedure ReleaseMonitor(Arg : TIBCustomSQLMonitor);
236     procedure SQLPrepare(qry: TIBSQL); virtual;
237     procedure SQLExecute(qry: TIBSQL); virtual;
238     procedure SQLFetch(qry: TIBSQL); virtual;
239     procedure DBConnect(db: TIBDatabase); virtual;
240     procedure DBDisconnect(db: TIBDatabase); virtual;
241     procedure TRStart(tr: TIBTransaction); virtual;
242     procedure TRCommit(tr: TIBTransaction); virtual;
243     procedure TRCommitRetaining(tr: TIBTransaction); virtual;
244     procedure TRRollback(tr: TIBTransaction); virtual;
245     procedure TRRollbackRetaining(tr: TIBTransaction); virtual;
246     procedure ServiceAttach(service: TIBMonitoredService); virtual; overload;
247     procedure ServiceDetach(service: TIBMonitoredService); virtual; overload;
248     procedure ServiceQuery(service: TIBMonitoredService); virtual; overload;
249     procedure ServiceStart(service: TIBMonitoredService); virtual; overload;
250     procedure ServiceAttach(service: TIBXMonitoredConnection); virtual; overload;
251     procedure ServiceDetach(service: TIBXMonitoredConnection); virtual; overload;
252     procedure ServiceQuery(service: TIBXMonitoredService); virtual; overload;
253     procedure ServiceStart(service: TIBXMonitoredService); virtual; overload;
254     procedure SendMisc(Msg : String);
255     function GetEnabled: Boolean;
256     function GetTraceFlags: TTraceFlags;
257     function GetMonitorCount : Integer;
258     procedure SetEnabled(const Value: Boolean);
259     procedure SetTraceFlags(const Value: TTraceFlags);
260     procedure ForceRelease;
261     property TraceFlags: TTraceFlags read GetTraceFlags write SetTraceFlags;
262     property Enabled : Boolean read GetEnabled write SetEnabled default true;
263     end;
264    
265     { TWriterThread }
266    
267     TWriterThread = class(TThread)
268     private
269     { Private declarations }
270     FGlobalInterface: TGlobalInterface;
271     FMsgs : TObjectList;
272     FCriticalSection: TCriticalSection;
273     FMsgAvailable: TEventObject;
274     procedure RemoveFromList;
275     procedure PostRelease;
276     public
277     procedure ReleaseMonitor(Arg : TIBCustomSQLMonitor);
278     protected
279     procedure BeginWrite;
280     procedure EndWrite;
281     procedure Execute; override;
282     procedure WriteToBuffer;
283     public
284     constructor Create(GlobalInterface: TGlobalInterface);
285     destructor Destroy; override;
286     procedure WriteSQLData(Msg : String; DataType : TTraceFlag);
287     end;
288    
289     { TReaderThread }
290    
291     TReaderThread = class(TThread)
292     private
293     { Private declarations }
294     st : TTraceObject;
295     FMonitors : TObjectList;
296     FGlobalInterface: TGlobalInterface;
297     FCriticalSection: TCriticalSection;
298     procedure AlertMonitors;
299     protected
300     procedure BeginRead;
301     procedure EndRead;
302     procedure ReadSQLData;
303     procedure Execute; override;
304     public
305     constructor Create(GlobalInterface: TGlobalInterface);
306     destructor Destroy; override;
307     procedure AddMonitor(Arg : TIBCustomSQLMonitor);
308     procedure RemoveMonitor(Arg : TIBCustomSQLMonitor);
309     end;
310    
311    
312     var
313     FWriterThread : TWriterThread;
314     FReaderThread : TReaderThread;
315     _MonitorHook: TIBSQLMonitorHook;
316     bDone: Boolean;
317     CS : TCriticalSection;
318    
319     const
320     ApplicationTitle: string = 'Unknown';
321    
322     { TIBCustomSQLMonitor }
323    
324     constructor TIBCustomSQLMonitor.Create(AOwner: TComponent);
325     var aParent: TComponent;
326     begin
327     inherited Create(AOwner);
328     FTraceFlags := [tfqPrepare .. tfMisc];
329     if not (csDesigning in ComponentState) then
330     begin
331     aParent := AOwner;
332     while aParent <> nil do
333     begin
334     if aParent is TCustomApplication then
335     begin
336     ApplicationTitle := TCustomApplication(aParent).Title;
337     break;
338     end;
339     aParent := aParent.Owner;
340     end;
341     MonitorHook.RegisterMonitor(self);
342     end;
343     FEnabled := true;
344     end;
345    
346     destructor TIBCustomSQLMonitor.Destroy;
347     begin
348     if not (csDesigning in ComponentState) then
349     begin
350     if FEnabled and assigned(_MonitorHook) then
351     MonitorHook.UnregisterMonitor(self);
352     end;
353     inherited Destroy;
354     end;
355    
356     procedure TIBCustomSQLMonitor.Release;
357     begin
358     MonitorHook.ReleaseMonitor(self);
359     end;
360    
361     procedure TIBCustomSQLMonitor.ReleaseObject;
362     begin
363     Free
364     end;
365    
366     procedure TIBCustomSQLMonitor.ReceiveMessage(Msg: TObject);
367     var
368     st: TTraceObject;
369     begin
370     st := (Msg as TTraceObject);
371     if (Assigned(FOnSQLEvent)) and
372     (st.FDataType in FTraceFlags) then
373     FOnSQLEvent(st.FMsg, st.FTimeStamp);
374     st.Free;
375     end;
376    
377     procedure TIBCustomSQLMonitor.SetEnabled(const Value: Boolean);
378     begin
379     if Value <> FEnabled then
380     begin
381     FEnabled := Value;
382     if not (csDesigning in ComponentState) then
383     if FEnabled then
384     Monitorhook.RegisterMonitor(self)
385     else
386     MonitorHook.UnregisterMonitor(self);
387     end;
388     end;
389    
390     { TIBSQLMonitorHook }
391    
392     constructor TIBSQLMonitorHook.Create;
393     begin
394     inherited Create;
395     FTraceFlags := [tfQPrepare..tfMisc];
396     FEnabled := false;
397     end;
398    
399     destructor TIBSQLMonitorHook.Destroy;
400     begin
401     if assigned(FGlobalInterface) then FGlobalInterface.Free;
402     inherited Destroy;
403     end;
404    
405     procedure TIBSQLMonitorHook.DBConnect(db: TIBDatabase);
406     var
407     st : String;
408     begin
409     if FEnabled then
410     begin
411     if not (tfConnect in FTraceFlags * db.TraceFlags) then
412     Exit;
413     st := db.Name + ': [Connect]'; {do not localize}
414     WriteSQLData(st, tfConnect);
415     end;
416     end;
417    
418     procedure TIBSQLMonitorHook.DBDisconnect(db: TIBDatabase);
419     var
420     st: String;
421     begin
422     if (Self = nil) then exit;
423     if FEnabled then
424     begin
425     if not (tfConnect in FTraceFlags * db.TraceFlags) then
426     Exit;
427     st := db.Name + ': [Disconnect]'; {do not localize}
428     WriteSQLData(st, tfConnect);
429     end;
430     end;
431    
432     function TIBSQLMonitorHook.GetEnabled: Boolean;
433     begin
434     Result := FEnabled;
435     end;
436    
437     function TIBSQLMonitorHook.GetMonitorCount: Integer;
438     begin
439     Result := FGlobalInterface.MonitorCount
440     end;
441    
442     function TIBSQLMonitorHook.GetTraceFlags: TTraceFlags;
443     begin
444     Result := FTraceFlags;
445     end;
446    
447     procedure TIBSQLMonitorHook.RegisterMonitor(SQLMonitor: TIBCustomSQLMonitor);
448     begin
449     {$IFDEF DEBUG}writeln('Register Monitor');{$ENDIF}
450     if not assigned(FGlobalInterface) then
451     FGlobalInterface := TGlobalInterface.Create;
452     if not Assigned(FReaderThread) then
453     FReaderThread := TReaderThread.Create(FGlobalInterface);
454     FReaderThread.AddMonitor(SQLMonitor);
455     end;
456    
457     procedure TIBSQLMonitorHook.ReleaseMonitor(Arg: TIBCustomSQLMonitor);
458     begin
459     FWriterThread.ReleaseMonitor(Arg);
460     end;
461    
462     procedure TIBSQLMonitorHook.SendMisc(Msg: String);
463     begin
464     if FEnabled then
465     begin
466     WriteSQLData(Msg, tfMisc);
467     end;
468     end;
469    
470     procedure TIBSQLMonitorHook.ServiceAttach(service: TIBMonitoredService);
471     var
472     st: String;
473     begin
474     if FEnabled then
475     begin
476     if not (tfService in (FTraceFlags * service.TraceFlags)) then
477     Exit;
478     st := service.Name + ': [Attach]'; {do not localize}
479     WriteSQLData(st, tfService);
480     end;
481     end;
482    
483     procedure TIBSQLMonitorHook.ServiceDetach(service: TIBMonitoredService);
484     var
485     st: String;
486     begin
487     if FEnabled then
488     begin
489     if not (tfService in (FTraceFlags * service.TraceFlags)) then
490     Exit;
491     st := service.Name + ': [Detach]'; {do not localize}
492     WriteSQLData(st, tfService);
493     end;
494     end;
495    
496     procedure TIBSQLMonitorHook.ServiceQuery(service: TIBMonitoredService);
497     var
498     st: String;
499     begin
500     if FEnabled then
501     begin
502     if not (tfService in (FTraceFlags * service.TraceFlags)) then
503     Exit;
504     st := service.Name + ': [Query]'; {do not localize}
505     WriteSQLData(st, tfService);
506     end;
507     end;
508    
509     procedure TIBSQLMonitorHook.ServiceStart(service: TIBMonitoredService);
510     var
511     st: String;
512     begin
513     if FEnabled then
514     begin
515     if not (tfService in (FTraceFlags * service.TraceFlags)) then
516     Exit;
517     st := service.Name + ': [Start]'; {do not localize}
518     WriteSQLData(st, tfService);
519     end;
520     end;
521    
522     procedure TIBSQLMonitorHook.ServiceAttach(service: TIBXMonitoredConnection);
523     var
524     st: String;
525     begin
526     if FEnabled then
527     begin
528     if not (tfService in (FTraceFlags * service.TraceFlags)) then
529     Exit;
530     st := service.Name + ': [Attach]'; {do not localize}
531     WriteSQLData(st, tfService);
532     end;
533     end;
534    
535     procedure TIBSQLMonitorHook.ServiceDetach(service: TIBXMonitoredConnection);
536     var
537     st: String;
538     begin
539     if FEnabled then
540     begin
541     if not (tfService in (FTraceFlags * service.TraceFlags)) then
542     Exit;
543     st := service.Name + ': [Detach]'; {do not localize}
544     WriteSQLData(st, tfService);
545     end;
546     end;
547    
548     procedure TIBSQLMonitorHook.ServiceQuery(service: TIBXMonitoredService);
549     var
550     st: String;
551     begin
552     if FEnabled then
553     begin
554     if not (tfService in (FTraceFlags * service.TraceFlags)) then
555     Exit;
556     st := service.Name + ': [Query]'; {do not localize}
557     WriteSQLData(st, tfService);
558     end;
559     end;
560    
561     procedure TIBSQLMonitorHook.ServiceStart(service: TIBXMonitoredService);
562     var
563     st: String;
564     begin
565     if FEnabled then
566     begin
567     if not (tfService in (FTraceFlags * service.TraceFlags)) then
568     Exit;
569     st := service.Name + ': [Start]'; {do not localize}
570     WriteSQLData(st, tfService);
571     end;
572     end;
573    
574     procedure TIBSQLMonitorHook.SetEnabled(const Value: Boolean);
575     begin
576 tony 263 {$ifdef UNIX}
577 tony 217 if Value and not IsMultiThread then
578 tony 221 IBError(ibxeMultiThreadRequired,['IBSQLMonitor']);
579 tony 263 {$endif}
580 tony 209 if FEnabled <> Value then
581     FEnabled := Value;
582     if (not FEnabled) and (Assigned(FWriterThread)) then
583     begin
584     FWriterThread.Terminate;
585     FWriterThread.WaitFor;
586     FreeAndNil(FWriterThread);
587     end;
588     end;
589    
590     procedure TIBSQLMonitorHook.SetTraceFlags(const Value: TTraceFlags);
591     begin
592     FTraceFlags := Value
593     end;
594    
595     procedure TIBSQLMonitorHook.ForceRelease;
596     begin
597     if Assigned(FReaderThread) then
598     begin
599     FReaderThread.Terminate;
600     if not Assigned(FWriterThread) then
601     FWriterThread := TWriterThread.Create(FGlobalInterface);
602     FWriterThread.WriteSQLData(' ', tfMisc);
603     end;
604     end;
605    
606     procedure TIBSQLMonitorHook.SQLExecute(qry: TIBSQL);
607     var
608     st: String;
609     i: Integer;
610     begin
611     if FEnabled then
612     begin
613     if not ((tfQExecute in (FTraceFlags * qry.Database.TraceFlags)) or
614     (tfStmt in (FTraceFlags * qry.Database.TraceFlags)) ) then
615     Exit;
616     if qry.Owner is TIBCustomDataSet then
617     st := TIBCustomDataSet(qry.Owner).Name
618     else
619     st := qry.Name;
620     st := st + ': [Execute] ' + qry.SQL.Text; {do not localize}
621     if qry.Params.GetCount > 0 then begin
622     for i := 0 to qry.Params.GetCount - 1 do begin
623     st := st + CRLF + ' ' + qry.Params[i].Name + ' = ';
624     try
625     if qry.Params[i].IsNull then
626     st := st + '<NULL>'; {do not localize}
627     st := st + qry.Params[i].AsString;
628     except
629     st := st + '<' + SCantPrintValue + '>';
630     end;
631     end;
632     end;
633     WriteSQLData(st, tfQExecute);
634     end;
635     end;
636    
637     procedure TIBSQLMonitorHook.SQLFetch(qry: TIBSQL);
638     var
639     st: String;
640     begin
641     if FEnabled then
642     begin
643     if not ((tfQFetch in (FTraceFlags * qry.Database.TraceFlags)) or
644     (tfStmt in (FTraceFlags * qry.Database.TraceFlags))) then
645     Exit;
646     if qry.Owner is TIBCustomDataSet then
647     st := TIBCustomDataSet(qry.Owner).Name
648     else
649     st := qry.Name;
650     st := st + ': [Fetch] ' + qry.SQL.Text; {do not localize}
651     if (qry.EOF) then
652     st := st + CRLF + ' ' + SEOFReached;
653     WriteSQLData(st, tfQFetch);
654     end;
655     end;
656    
657     procedure TIBSQLMonitorHook.SQLPrepare(qry: TIBSQL);
658     var
659     st: String;
660     begin
661     if FEnabled then
662     begin
663     if not ((tfQPrepare in (FTraceFlags * qry.Database.TraceFlags)) or
664     (tfStmt in (FTraceFlags * qry.Database.TraceFlags))) then
665     Exit;
666     if qry.Owner is TIBCustomDataSet then
667     st := TIBCustomDataSet(qry.Owner).Name
668     else
669     st := qry.Name;
670     st := st + ': [Prepare] ' + qry.SQL.Text + CRLF; {do not localize}
671     st := st + ' Plan: ' + qry.Plan; {do not localize}
672     WriteSQLData(st, tfQPrepare);
673     end;
674     end;
675    
676     procedure TIBSQLMonitorHook.TRCommit(tr: TIBTransaction);
677     var
678     st: String;
679     begin
680     if FEnabled then
681     begin
682     if Assigned(tr.DefaultDatabase) and
683     (not (tfTransact in (FTraceFlags * tr.DefaultDatabase.TraceFlags))) then
684     Exit;
685     st := tr.Name + ': [Commit (Hard commit)]'; {do not localize}
686     WriteSQLData(st, tfTransact);
687     end;
688     end;
689    
690     procedure TIBSQLMonitorHook.TRCommitRetaining(tr: TIBTransaction);
691     var
692     st: String;
693     begin
694     if FEnabled then
695     begin
696     if Assigned(tr.DefaultDatabase) and
697     (not (tfTransact in (FTraceFlags * tr.DefaultDatabase.TraceFlags))) then
698     Exit;
699     st := tr.Name + ': [Commit retaining (Soft commit)]'; {do not localize}
700     WriteSQLData(st, tfTransact);
701     end;
702     end;
703    
704     procedure TIBSQLMonitorHook.TRRollback(tr: TIBTransaction);
705     var
706     st: String;
707     begin
708     if FEnabled then
709     begin
710     if Assigned(tr.DefaultDatabase) and
711     (not (tfTransact in (FTraceFlags * tr.DefaultDatabase.TraceFlags))) then
712     Exit;
713     st := tr.Name + ': [Rollback]'; {do not localize}
714     WriteSQLData(st, tfTransact);
715     end;
716     end;
717    
718     procedure TIBSQLMonitorHook.TRRollbackRetaining(tr: TIBTransaction);
719     var
720     st: String;
721     begin
722     if FEnabled then
723     begin
724     if Assigned(tr.DefaultDatabase) and
725     (not (tfTransact in (FTraceFlags * tr.DefaultDatabase.TraceFlags))) then
726     Exit;
727     st := tr.Name + ': [Rollback retaining (Soft rollback)]'; {do not localize}
728     WriteSQLData(st, tfTransact);
729     end;
730     end;
731    
732     procedure TIBSQLMonitorHook.TRStart(tr: TIBTransaction);
733     var
734     st: String;
735     begin
736     if FEnabled then
737     begin
738     if Assigned(tr.DefaultDatabase) and
739     (not (tfTransact in (FTraceFlags * tr.DefaultDatabase.TraceFlags))) then
740     Exit;
741     st := tr.Name + ': [Start transaction]'; {do not localize}
742     WriteSQLData(st, tfTransact);
743     end;
744     end;
745    
746     procedure TIBSQLMonitorHook.UnregisterMonitor(SQLMonitor: TIBCustomSQLMonitor);
747     var
748     Created : Boolean;
749     begin
750     {$IFDEF DEBUG}writeln('Unregister Monitor');{$ENDIF}
751     if assigned(FReaderThread) then
752     begin
753     FReaderThread.RemoveMonitor(SQLMonitor);
754     if FReaderThread.FMonitors.Count = 0 then
755     begin
756     FReaderThread.Terminate;
757    
758     { There is a possibility of a reader thread, but no writer one.
759     When in that situation, the reader needs to be released after
760     the terminate is set. To do that, create a Writer thread, send
761     the release code (a string of ' ' and type tfMisc) and then free
762     it up. }
763    
764     Created := false;
765     if not Assigned(FWriterThread) then
766     begin
767     FWriterThread := TWriterThread.Create(FGlobalInterface);
768     Created := true;
769     end;
770     FWriterThread.WriteSQLData(' ', tfMisc);
771     {$IFDEF DEBUG}writeln('Wait for read Terminate');{$ENDIF}
772     FReaderThread.WaitFor;
773     if assigned(FReaderThread.FatalException) then
774     IBError(ibxeThreadFailed,['Reader',Exception(FReaderThread.FatalException).Message]);
775     {$IFDEF DEBUG}writeln('Freeing Reader Thread');{$ENDIF}
776     FreeAndNil(FReaderThread);
777     {$IFDEF DEBUG}writeln('Reader Thread Freed');{$ENDIF}
778     if Created then
779     begin
780     FWriterThread.Terminate;
781     {$IFDEF DEBUG}writeln('Wait for write Terminate');{$ENDIF}
782     FWriterThread.WaitFor;
783     if assigned(FWriterThread.FatalException) then
784     IBError(ibxeThreadFailed,['Writer',Exception(FWriterThread.FatalException).Message]);
785     FreeAndNil(FWriterThread);
786     end;
787     end;
788     end;
789     {$IFDEF DEBUG}writeln('Unregister done'){$ENDIF}
790     end;
791    
792     procedure TIBSQLMonitorHook.WriteSQLData(Text: String;
793     DataType: TTraceFlag);
794     begin
795     {$IFDEF DEBUG}writeln('Write SQL Data: '+Text);{$ENDIF}
796     if not assigned(FGlobalInterface) then
797     FGlobalInterface := TGlobalInterface.Create;
798     Text := CRLF + '[Application: ' + ApplicationTitle + ']' + CRLF + Text; {do not localize}
799     if not Assigned(FWriterThread) then
800     FWriterThread := TWriterThread.Create(FGLobalInterface);
801     FWriterThread.WriteSQLData(Text, DataType);
802     end;
803    
804     { TWriterThread }
805    
806     constructor TWriterThread.Create(GlobalInterface: TGlobalInterface);
807    
808     begin
809     inherited Create(true);
810     {$IFDEF DEBUG}writeln('Write Object Created');{$ENDIF}
811     FGlobalInterface := GlobalInterface;
812     FMsgs := TObjectList.Create(true);
813     FCriticalSection := TCriticalSection.Create;
814     FMsgAvailable := TEventObject.Create(FGlobalInterface.Sa,true,false,cWriteMessageAvailable);
815     Start;
816     end;
817    
818     destructor TWriterThread.Destroy;
819     begin
820     if assigned(FMsgs) then FMsgs.Free;
821     if assigned(FCriticalSection) then FCriticalSection.Free;
822     if assigned(FMsgAvailable) then FMsgAvailable.Free;
823     inherited Destroy;
824     end;
825    
826     procedure TWriterThread.Execute;
827     begin
828     {$IFDEF DEBUG}writeln('Write Thread starts');{$ENDIF}
829     try
830     { Place thread code here }
831     while ((not Terminated) and (not bDone)) or
832     (FMsgs.Count <> 0) do
833     begin
834     FMsgAvailable.WaitFor(cMsgWaitTime);
835     { Any one listening? }
836     if FGlobalInterface.MonitorCount = 0 then
837     begin
838     if FMsgs.Count <> 0 then
839     begin
840     {$IFDEF DEBUG}writeln('Write Thread Drop Message');{$ENDIF}
841     RemoveFromList;
842     end;
843     end
844     else
845     { Anything to process? }
846     if FMsgs.Count <> 0 then
847     begin
848     { If the current queued message is a release release the object }
849     if FMsgs.Items[0] is TReleaseObject then
850     begin
851     {$IFDEF DEBUG}writeln('Post Release');{$ENDIF}
852 tony 215 if not Terminated then
853     Synchronize(PostRelease);
854 tony 209 end
855     else
856     { Otherwise write the TraceObject to the buffer }
857     begin
858     WriteToBuffer;
859     end;
860     end
861     else
862     begin
863     FCriticalSection.Enter;
864     try
865     if FMsgs.Count = 0 then
866     FMsgAvailable.ResetEvent
867     finally
868     FCriticalSection.Leave
869     end;
870     end;
871     end;
872     except on E: Exception do
873     begin
874     {$IFDEF DEBUG}writeln('Write Thread raised Exception: ' + E.Message);{$ENDIF}
875     raise
876     end
877     end;
878     {$IFDEF DEBUG}writeln('Write Thread Ends');{$ENDIF}
879     end;
880    
881     procedure TWriterThread.WriteSQLData(Msg : String; DataType: TTraceFlag);
882     begin
883     FCriticalSection.Enter;
884     try
885     FMsgs.Add(TTraceObject.Create(Msg, DataType));
886     finally
887     FCriticalSection.Leave;
888     end;
889     FMsgAvailable.SetEvent
890     end;
891    
892     procedure TWriterThread.BeginWrite;
893     begin
894     {$IFDEF DEBUG}writeln('Begin Write');{$ENDIF}
895     with FGlobalInterface do
896     begin
897     ReadReadyEvent.PassThroughGate; {Wait for readers to become ready }
898     WriterBusyEvent.Lock; {Set Busy State}
899     end;
900     {$IFDEF DEBUG}writeln('Begin Write Complete');{$ENDIF}
901     end;
902    
903     procedure TWriterThread.EndWrite;
904     begin
905     {$IFDEF DEBUG}writeln('End Write');{$ENDIF}
906     with FGlobalInterface do
907     begin
908     DataAvailableEvent.Unlock; { Signal Data Available. }
909     ReadFinishedEvent.PassThroughGate; {Wait for readers to finish }
910     DataAvailableEvent.Lock; {reset Data Available }
911     WriterBusyEvent.Unlock; {Signal not Busy }
912     end;
913     {$IFDEF DEBUG}writeln('End Write Complete');{$ENDIF}
914     end;
915    
916     procedure TWriterThread.WriteToBuffer;
917     var I, len: integer;
918     Temp: TTraceObject;
919     begin
920     {$IFDEF DEBUG}writeln('Write to Buffer');{$ENDIF}
921     FGlobalInterface.WriteLock.Lock;
922     try
923     { If there are no monitors throw out the message
924     The alternative is to have messages queue up until a
925     monitor is ready.}
926    
927     if FGlobalInterface.MonitorCount = 0 then
928     RemoveFromList
929     else
930     begin
931     i := 1;
932     len := Length(TTraceObject(FMsgs[0]).FMsg);
933     if len <= FGlobalInterface.MaxBufferSize then
934     begin
935     BeginWrite;
936     try
937     FGlobalInterface.SendTrace(TTraceObject(FMsgs[0]))
938     finally
939     RemoveFromList;
940     EndWrite
941     end;
942     end
943     else
944     try
945     while len > 0 do
946     begin
947     {$IFDEF DEBUG}writeln('Sending Partial Message, len = ',len);{$ENDIF}
948     Temp := TTraceObject.Create(TTraceObject(FMsgs[0]),i,Min(len,FGlobalInterface.MaxBufferSize));
949     try
950     BeginWrite;
951     FGlobalInterface.SendTrace(Temp);
952     Inc(i,FGlobalInterface.MaxBufferSize);
953     Dec(len,FGlobalInterface.MaxBufferSize);
954     finally
955     Temp.Free;
956     EndWrite
957     end
958     end;
959     finally
960     RemoveFromList;
961     end
962     end;
963     finally
964     FGlobalInterface.WriteLock.Unlock;
965     end;
966     {$IFDEF DEBUG}writeln('Done Write');{$ENDIF}
967     end;
968    
969     procedure TWriterThread.RemoveFromList;
970     begin
971     {$IFDEF DEBUG}writeln('Write Thread: Remove object From List');{$ENDIF}
972     FCriticalSection.Enter;
973     try
974     FMsgs.Remove(FMsgs[0]); { Pop the written item }
975     finally
976     FCriticalSection.Leave;
977     end;
978     end;
979    
980     procedure TWriterThread.PostRelease;
981     var Monitor: TIBCustomSQLMonitor;
982     begin
983     Monitor := TReleaseObject(FMsgs.Items[0]).FMonitor;
984     Monitor.ReleaseObject
985     end;
986    
987     procedure TWriterThread.ReleaseMonitor(Arg : TIBCustomSQLMonitor);
988     begin
989     FMsgs.Add(TReleaseObject.Create(Arg));
990     end;
991    
992     { TTraceObject }
993    
994     constructor TTraceObject.Create(Msg : String; DataType: TTraceFlag);
995     begin
996     FMsg := Msg;
997     FDataType := DataType;
998     FTimeStamp := Now;
999     end;
1000    
1001     constructor TTraceObject.Create(obj: TTraceObject);
1002     begin
1003     FMsg := obj.FMsg;
1004     FDataType := obj.FDataType;
1005     FTimeStamp := obj.FTimeStamp;
1006     end;
1007    
1008     constructor TTraceObject.Create(obj: TTraceObject; MsgOffset, MsgLen: integer);
1009     begin
1010     FDataType := obj.FDataType;
1011     FTimeStamp := obj.FTimeStamp;
1012     FMsg := copy(obj.FMsg,MsgOffset,MsgLen)
1013     end;
1014    
1015     { TReleaseObject }
1016    
1017     constructor TReleaseObject.Create(Monitor : TIBCustomSQLMonitor);
1018     begin
1019     FMonitor := Monitor;
1020     end;
1021    
1022     { ReaderThread }
1023    
1024     procedure TReaderThread.AddMonitor(Arg: TIBCustomSQLMonitor);
1025     begin
1026     FCriticalSection.Enter;
1027     try
1028     if FMonitors.IndexOf(Arg) < 0 then
1029     FMonitors.Add(Arg);
1030     finally
1031     FCriticalSection.Leave
1032     end;
1033     end;
1034    
1035     procedure TReaderThread.AlertMonitors;
1036     var i : Integer;
1037     FTemp : TTraceObject;
1038     Monitor: TIBCustomSQLMonitor;
1039     begin
1040     for i := 0 to FMonitors.Count - 1 do
1041     begin
1042     {$IFDEF DEBUG}writeln('Sending Message to Monitor ' +IntToStr(i));{$ENDIF}
1043     FTemp := TTraceObject.Create(st);
1044     Monitor := TIBCustomSQLMonitor(FMonitors[i]);
1045     Monitor.ReceiveMessage(FTemp);
1046     end;
1047     end;
1048    
1049     procedure TReaderThread.BeginRead;
1050     begin
1051     {$IFDEF DEBUG}writeln('Begin Read');{$ENDIF}
1052     with FGlobalInterface do
1053     begin
1054     WriterBusyEvent.PassthroughGate; { Wait for Writer not busy}
1055     ReadFinishedEvent.Lock; { Prepare Read Finished Gate}
1056     ReadReadyEvent.Unlock; { Signal read ready }
1057     {$IFDEF DEBUG}writeln('Read Ready Unlocked');{$ENDIF}
1058     DataAvailableEvent.PassthroughGate; { Wait for a Data Available }
1059     end;
1060     {$IFDEF DEBUG}writeln('Begin Read Complete');{$ENDIF}
1061     end;
1062    
1063     constructor TReaderThread.Create(GlobalInterface: TGlobalInterface);
1064     begin
1065     inherited Create(true);
1066     FGlobalInterface := GlobalInterface;
1067     st := TTraceObject.Create('', tfMisc);
1068     FGlobalInterface.IncMonitorCount;
1069     FMonitors := TObjectList.Create(false);
1070     FCriticalSection := TCriticalSection.Create;
1071     {$IFDEF DEBUG}writeln('Reader Thread Created');{$ENDIF}
1072     FGlobalInterface.ReadReadyEvent.Lock; { Initialise Read Ready}
1073     Start;
1074     end;
1075    
1076     destructor TReaderThread.Destroy;
1077     begin
1078     {$IFDEF DEBUG}writeln('Reader Thread Destory');{$ENDIF}
1079     FGlobalInterface.ReadReadyEvent.UnLock;
1080     if assigned(FGlobalInterface) and (FGlobalInterface.MonitorCount > 0) then
1081     FGlobalInterface.DecMonitorCount;
1082     FMonitors.Free;
1083     if assigned(FCriticalSection) then FCriticalSection.Free;
1084     st.Free;
1085     inherited Destroy;
1086     end;
1087    
1088     procedure TReaderThread.EndRead;
1089     begin
1090     {$IFDEF DEBUG}writeln('End Read');{$ENDIF}
1091     FGlobalInterface.ReadReadyEvent.Lock; { reset Read Ready}
1092     FGlobalInterface.ReadFinishedEvent.Unlock; {Signal Read completed }
1093     {$IFDEF DEBUG}writeln('End Read Complete');{$ENDIF}
1094     end;
1095    
1096     procedure TReaderThread.Execute;
1097     begin
1098     {$IFDEF DEBUG}writeln('Read Thread Starts');{$ENDIF}
1099     { Place thread code here }
1100     while (not Terminated) and (not bDone) do
1101     begin
1102     ReadSQLData;
1103     if (st.FMsg <> '') and
1104     not ((st.FMsg = ' ') and (st.FDataType = tfMisc)) then
1105     begin
1106     {$IFDEF DEBUG}writeln('Sending Message to Monitors');{$ENDIF}
1107 tony 215 if not Terminated then
1108     Synchronize(AlertMonitors);
1109 tony 209 end;
1110     end;
1111     {$IFDEF DEBUG}writeln('Read Thread Ends');{$ENDIF}
1112     end;
1113    
1114     procedure TReaderThread.ReadSQLData;
1115     begin
1116     st.FMsg := '';
1117     BeginRead;
1118     if not bDone then
1119     try
1120     FGlobalInterface.ReceiveTrace(st)
1121     finally
1122     EndRead;
1123     end;
1124     end;
1125    
1126     procedure TReaderThread.RemoveMonitor(Arg: TIBCustomSQLMonitor);
1127     begin
1128     FCriticalSection.Enter;
1129     try
1130     FMonitors.Remove(Arg);
1131     finally
1132     FCriticalSection.Leave
1133     end;
1134     end;
1135    
1136     { Misc methods }
1137    
1138     function MonitorHook: IIBSQLMonitorHook;
1139     begin
1140     if (_MonitorHook = nil) and (not bDone) then
1141     begin
1142     CS.Enter;
1143     if (_MonitorHook = nil) and (not bDone) then
1144     begin
1145     _MonitorHook := TIBSQLMonitorHook.Create;
1146     _MonitorHook._AddRef;
1147     end;
1148     CS.Leave;
1149     end;
1150     result := _MonitorHook;
1151     end;
1152    
1153     procedure EnableMonitoring;
1154     begin
1155     MonitorHook.Enabled := True;
1156     end;
1157    
1158     procedure DisableMonitoring;
1159     begin
1160     MonitorHook.Enabled := False;
1161     end;
1162    
1163     function MonitoringEnabled: Boolean;
1164     begin
1165     result := MonitorHook.Enabled;
1166     end;
1167    
1168     procedure CloseThreads;
1169     begin
1170     {$IFDEF DEBUG}writeln('Closed Threads Called');{$ENDIF}
1171     if Assigned(FReaderThread) then
1172     begin
1173     FReaderThread.Terminate;
1174     FReaderThread.WaitFor;
1175     FreeAndNil(FReaderThread);
1176     end;
1177     if Assigned(FWriterThread) then
1178     begin
1179     FWriterThread.Terminate;
1180     FWriterThread.WaitFor;
1181     FreeAndNil(FWriterThread);
1182     end;
1183     end;
1184    
1185     initialization
1186     CS := TCriticalSection.Create;
1187     _MonitorHook := nil;
1188     FWriterThread := nil;
1189     FReaderThread := nil;
1190     bDone := False;
1191     {$IFDEF USE_SV5_IPC}
1192     if GetEnvironmentVariable('FBSQL_IPCFILENAME') <> '' then
1193     IPCFileName := GetEnvironmentVariable('FBSQL_IPCFILENAME')
1194     else
1195     IPCFileName := GetTempDir(true) + IPCFileName + '.' + GetEnvironmentVariable('USER');
1196     {$ENDIF}
1197    
1198     finalization
1199     {$IFDEF DEBUG}writeln('Entered Finalisation');{$ENDIF}
1200     try
1201     { Write an empty string to force the reader to unlock during termination }
1202     bDone := True;
1203     if Assigned(_MonitorHook) then
1204     _MonitorHook.ForceRelease;
1205     CloseThreads;
1206     if Assigned(_MonitorHook) then
1207     _MonitorHook._Release;
1208    
1209     finally
1210     _MonitorHook := nil;
1211     if assigned(CS) then CS.Free;
1212     end;
1213     end.