ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBSQLMonitor.pas
Revision: 380
Committed: Mon Jan 10 10:13:17 2022 UTC (2 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 32178 byte(s)
Log Message:
propset for eol-style

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

Properties

Name Value
svn:eol-style native