1 {*********************************************************}
3 { Zeos Database Objects }
4 { Interbase Database Connectivity Classes }
6 { Copyright (c) 1999-2003 Zeos Development Group }
7 { Written by Sergey Merkuriev }
9 {*********************************************************}
11 {@********************************************************}
12 { Copyright (c) 1999-2012 Zeos Development Group }
14 { License Agreement: }
16 { This library is distributed in the hope that it will be }
17 { useful, but WITHOUT ANY WARRANTY; without even the }
18 { implied warranty of MERCHANTABILITY or FITNESS FOR }
19 { A PARTICULAR PURPOSE. See the GNU Lesser General }
20 { Public License for more details. }
22 { The source code of the ZEOS Libraries and packages are }
23 { distributed under the Library GNU General Public }
24 { License (see the file COPYING / COPYING.ZEOS) }
25 { with the following modification: }
26 { As a special exception, the copyright holders of this }
27 { library give you permission to link this library with }
28 { independent modules to produce an executable, }
29 { regardless of the license terms of these independent }
30 { modules, and to copy and distribute the resulting }
31 { executable under terms of your choice, provided that }
32 { you also meet, for each linked independent module, }
33 { the terms and conditions of the license of that module. }
34 { An independent module is a module which is not derived }
35 { from or based on this library. If you modify this }
36 { library, you may extend this exception to your version }
37 { of the library, but you are not obligated to do so. }
38 { If you do not wish to do so, delete this exception }
39 { statement from your version. }
42 { The project web site is located on: }
43 { http://zeos.firmos.at (FORUM) }
44 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
45 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
47 { http://www.sourceforge.net/projects/zeoslib. }
50 { Zeos Development Group. }
51 {********************************************************@}
61 {$IF defined(MSWINDOWS)and not defined(FPC)}
64 ZDbcInterbase6, ZConnection, ZDbcIntfs,
65 ZPlainFirebirdDriver, ZPlainFirebirdInterbaseConstants;
69 TEventAlert = procedure(Sender: TObject; EventName: string; EventCount: longint;
70 var CancelAlerts: boolean) of object;
71 TErrorEvent = procedure(Sender: TObject; ErrorCode: integer) of object;
73 TZIBEventAlerter = class(TComponent)
76 FOnEventAlert: TEventAlert;
78 FNativeHandle: PISC_DB_HANDLE;
79 ThreadException: boolean;
80 FConnection: TZConnection;
81 FOnError: TErrorEvent;
82 FAutoRegister: boolean;
85 procedure SetConnection(Value: TZConnection);
86 procedure SetEvents(Value: TStrings);
87 function GetRegistered: boolean;
88 procedure SetRegistered(const Value: boolean);
89 function GetPlainDriver: IZInterbasePlainDriver;
91 { Protected declarations }
92 function GetNativeHandle: PISC_DB_HANDLE; virtual;
93 procedure EventChange(Sender: TObject); virtual;
94 procedure ThreadEnded(Sender: TObject); virtual;
95 procedure Notification(AComponent: TComponent; Operation: TOperation); override;
97 { Public declarations }
98 constructor Create(AOwner: TComponent); override;
99 destructor Destroy; override;
100 procedure RegisterEvents; virtual;
101 procedure UnRegisterEvents; virtual;
102 property NativeHandle: PISC_DB_HANDLE read GetNativeHandle;
103 property PlainDriver: IZInterbasePlainDriver read GetPlainDriver;
104 procedure SetAutoRegister(const Value: boolean);
105 function GetAutoRegister: boolean;
107 { Published declarations }
108 property AutoRegister: boolean read GetAutoRegister write SetAutoRegister;
109 property Connection: TZConnection read FConnection write SetConnection;
110 property Events: TStrings read FEvents write SetEvents;
111 property Registered: boolean read GetRegistered write SetRegistered;
112 property OnEventAlert: TEventAlert read FOnEventAlert write FOnEventAlert;
113 property OnError: TErrorEvent read FOnError write FOnError;
122 IB_MAX_EVENT_BLOCK = 15; // maximum events handled per block by InterBase
123 IB_MAX_EVENT_LENGTH = 64; // maximum event name length
125 FStatusVector: TARRAY_ISC_STATUS;
130 TIBEventThread = class(TThread)
132 // IB API call parameters
135 EventBuffer: PAnsiChar;
136 EventBufferLen: Short;
137 ResultBuffer: PAnsiChar;
138 // Local use variables
139 Signal: TSimpleEvent;
144 Parent: TZIBEventAlerter;
145 FExceptObject: TObject;
146 FExceptAddr: Pointer;
147 FCancelAlerts: boolean;
149 procedure Execute; override;
150 procedure SignalEvent; virtual;
151 procedure SignalTerminate; virtual;
152 procedure RegisterEvents; virtual;
153 procedure UnRegisterEvents; virtual;
154 procedure QueueEvents; virtual;
155 procedure SQueEvents;
156 procedure ProcessEvents; virtual;
158 procedure DoHandleException;
159 function HandleException: boolean; virtual;
160 procedure UpdateResultBuffer(Length: UShort; Updated: PAnsiChar);
162 constructor Create(Owner: TZIBEventAlerter; EventGrp: integer;
163 TermEvent: TNotifyEvent); virtual;
164 destructor Destroy; override;
167 Tsib_event_block = function(EventBuffer, ResultBuffer: PPAnsiChar; IDCount: UShort;
168 Event1, Event2, Event3, Event4, Event5, Event6, Event7, Event8, Event9,
169 Event10, Event11, Event12, Event13, Event14, Event15: PAnsiChar): ISC_LONG;
172 function TZIBEventAlerter.GetNativeHandle: PISC_DB_HANDLE;
174 Result := (FConnection.DbcConnection as IZInterbase6Connection).GetDBHandle;
177 function StatusVector: PISC_STATUS;
179 Result := @FStatusVector;
182 function StatusVectorArray: TARRAY_ISC_STATUS;
184 Result := FStatusVector;
189 constructor TZIBEventAlerter.Create(AOwner: TComponent);
191 inherited Create(AOwner);
193 ThreadException := False;
194 FOnEventAlert := nil;
195 FNativeHandle := nil;
197 FAutoRegister := False;
198 FEvents := TStringList.Create;
199 with TStringList(FEvents) do
201 Sorted := True; // dupIgnore only works when the TStringList is sorted
202 OnChange := EventChange; // assign the routine which validates the event lenghts
203 Duplicates := dupIgnore; // don't allow duplicate events
205 FThreads := TList.Create;
208 destructor TZIBEventAlerter.Destroy;
214 // silence any exceptions which might be raised
215 // by UnRegisterEvents during destruction
218 { If Assigned(FConnection) then
219 FConnection.RemoveEventNotifier(Self);
228 procedure TZIBEventAlerter.Notification(AComponent: TComponent;
229 Operation: TOperation);
231 inherited Notification(AComponent, Operation);
232 if (Operation = opRemove) and (AComponent = FConnection) then
241 // -> ms, 18/08/2004:
242 // Modified so that now the DB connection will be made when events are registered
243 // this is because the method UnregisterEvents of TIBEventThread needs a native
244 // DB handle that can only be retrieved when DB connection is active. If the events
245 // are registered correctly the DB connection must be established. If it is not
246 // established this will be done here. This means that whenever events are registered
247 // (by setting AutoRegister := True or calling RegisterEvents explicitly) and the
248 // DB connection ist not established, this will be done here automatically (including
249 // the retrieval of the native DB handle).
250 Procedure TZIBEventAlerter.RegisterEvents;
253 If (not (csDesigning in ComponentState)) and (Assigned(FConnection)) Then Begin
255 If (FThreads.Count = 0) Then Begin
256 If (FEvents.Count > 0) Then Begin
257 For i := 0 To ((FEvents.Count - 1) div IB_MAX_EVENT_BLOCK) Do
258 FThreads.Add(TIBEventThread.Create(Self, i, ThreadEnded));
262 FRegistered := FThreads.Count <> 0;
263 If FRegistered Then Begin
264 If not FConnection.Connected Then
266 FNativeHandle := GetNativeHandle;
270 End; // RegisterEvents
273 // -> ms, 18/08/2004:
274 // Modified so that the native DB handle will now be retrieved by
275 // method RegisterEvents. Retrieving it here caused an Exception
276 // even if DB was connected.
277 Procedure TZIBEventAlerter.SetConnection(Value: TZConnection);
279 WasRegistered: boolean;
281 If (Value <> FConnection) Then Begin
282 If (csDesigning in ComponentState) Then
285 WasRegistered := Registered;
286 If WasRegistered Then
288 FConnection := Value;
289 If WasRegistered Then
293 End; // SetConnection
296 procedure TZIBEventAlerter.SetEvents(Value: TStrings);
298 FEvents.Assign(Value);
301 procedure TZIBEventAlerter.SetRegistered(const Value: boolean);
303 FRegistered := Value;
304 if csDesigning in ComponentState then
312 procedure TZIBEventAlerter.UnregisterEvents;
315 Temp: TIBEventThread;
317 if csDesigning in ComponentState then
319 if (FThreads.Count > 0) then
321 for i := (FThreads.Count - 1) downto 0 do
323 Temp := TIBEventThread(FThreads[i]);
326 Temp.SignalTerminate;
331 FRegistered := FThreads.Count <> 0;
334 function TZIBEventAlerter.GetPlainDriver: IZInterbasePlainDriver;
336 Result := (FConnection.DbcConnection as IZInterbase6Connection).GetPlainDriver;
341 procedure EventCallback(P: Pointer; Length: Short; Updated: PAnsiChar); cdecl;
343 if (Assigned(P) and Assigned(Updated)) then
345 TIBEventThread(P).UpdateResultBuffer(Length, Updated);
346 TIBEventThread(P).SignalEvent;
350 procedure TIBEventThread.DoEvent;
352 Parent.FOnEventAlert(Parent, Parent.FEvents[((EventGroup * IB_MAX_EVENT_BLOCK) + WhichEvent)],
353 StatusVectorArray[WhichEvent], FCancelAlerts)
356 procedure TIBEventThread.UpdateResultBuffer(Length: UShort; Updated: PAnsiChar);
358 Move(Updated[0], ResultBuffer[0], Length);
361 procedure TIBEventThread.QueueEvents;
363 EventsReceived := False;
365 Synchronize(SQueEvents);
368 procedure TIBEventThread.ProcessEvents;
372 Parent.PlainDriver.isc_event_counts(StatusVector, EventBufferLen,
373 EventBuffer, ResultBuffer);
374 if (Assigned(Parent.FOnEventAlert) and (not FirstTime)) then
376 FCancelAlerts := False;
377 for i := 0 to (EventCount - 1) do
379 if (StatusVectorArray[i] <> 0) then
389 procedure TIBEventThread.UnRegisterEvents;
391 Parent.PlainDriver.isc_cancel_events(StatusVector, Parent.FNativeHandle, @EventID);
392 Parent.PlainDriver.isc_free(EventBuffer);
394 Parent.PlainDriver.isc_free(ResultBuffer);
398 procedure TIBEventThread.RegisterEvents;
400 sib_event_block: Tsib_event_block;
402 function EBP(Index: integer): PAnsiChar;
404 Inc(Index, (EventGroup * IB_MAX_EVENT_BLOCK));
405 if (Index > Parent.FEvents.Count) then
409 Result := PAnsiChar(AnsiString(Parent.FEvents[Index - 1]));
411 Result := PAnsiChar(Parent.FEvents[Index - 1]);
419 EventCount := (Parent.FEvents.Count - (EventGroup * IB_MAX_EVENT_BLOCK));
420 if (EventCount > IB_MAX_EVENT_BLOCK) then
421 EventCount := IB_MAX_EVENT_BLOCK;
424 if Parent.Connection.Protocol='interbase-6' then
425 sib_event_block := Tsib_event_block(ZPlainInterbase6.isc_event_block)
426 else if Parent.Connection.Protocol='firebird-1.0' then
427 sib_event_block := Tsib_event_block(ZPlainFirebird10.isc_event_block)
428 else if Parent.Connection.Protocol='firebird-1.5' then
429 sib_event_block := Tsib_event_block(ZPlainFirebird15.isc_event_block)
430 else if Parent.Connection.Protocol='firebirdd-1.5' then
431 sib_event_block := Tsib_event_block(ZPlainFirebird15.isc_event_block)
432 else if Parent.Connection.Protocol='firebird-2.0' then
433 sib_event_block := Tsib_event_block(ZPlainFirebird20.isc_event_block)
434 else if Parent.Connection.Protocol='firebirdd-2.0' then
435 sib_event_block := Tsib_event_block(ZPlainFirebird20.isc_event_block)
436 else if Parent.Connection.Protocol='firebird-2.1' then
437 sib_event_block := Tsib_event_block(ZPlainFirebird21.isc_event_block)
438 else if Parent.Connection.Protocol='firebirdd-2.1' then
439 sib_event_block := Tsib_event_block(ZPlainFirebird21.isc_event_block)
442 sib_event_block := Tsib_event_block(ZPlainInterbase6.isc_event_block);
444 sib_event_block := Tsib_event_block(Parent.GetPlainDriver.GetFirebirdAPI.isc_event_block);
445 EventBufferLen := sib_event_block(@EventBuffer,
446 @ResultBuffer, EventCount,
447 EBP(1), EBP(2), EBP(3), EBP(4), EBP(5), EBP(6), EBP(7), EBP(8),
448 EBP(9), EBP(10), EBP(11), EBP(12), EBP(13), EBP(14), EBP(15));
453 procedure TIBEventThread.SignalEvent;
455 EventsReceived := True;
459 procedure TIBEventThread.SignalTerminate;
461 if not Terminated then
468 procedure TIBEventThread.DoHandleException;
470 SysUtils.ShowException(FExceptObject, FExceptAddr);
473 function TIBEventThread.HandleException: boolean;
475 if not Parent.ThreadException then
478 Parent.ThreadException := True;
479 FExceptObject := ExceptObject;
480 FExceptAddr := ExceptAddr;
482 if not (FExceptObject is EAbort) then
483 Synchronize(DoHandleException);
485 FExceptObject := nil;
493 procedure TIBEventThread.Execute;
499 Signal.WaitFor(INFINITE);
500 if EventsReceived then
508 if HandleException then
516 constructor TIBEventThread.Create(Owner: TZIBEventAlerter;
517 EventGrp: integer; TermEvent: TNotifyEvent);
519 inherited Create(True);
520 FCancelAlerts := False;
521 Signal := TSimpleEvent.Create;
523 EventGroup := EventGrp;
524 OnTerminate := TermEvent;
529 destructor TIBEventThread.Destroy;
534 if HandleException then
543 procedure TZIBEventAlerter.EventChange(Sender: TObject);
546 WasRegistered: boolean;
548 WasRegistered := Registered;
550 if WasRegistered then
552 TStringList(FEvents).OnChange := nil;
554 for i := (FEvents.Count - 1) downto 0 do
556 if (FEvents[i] = EmptyStr) then
560 else if (Length(FEvents[i]) > (IB_MAX_EVENT_LENGTH - 1)) then
562 FEvents[i] := Copy(FEvents[i], 1, (IB_MAX_EVENT_LENGTH - 1));
566 TStringList(FEvents).OnChange := EventChange;
569 if WasRegistered then
574 function TZIBEventAlerter.GetRegistered: boolean;
576 Result := FRegistered;
579 procedure TZIBEventAlerter.ThreadEnded(Sender: TObject);
583 if (Sender is TIBEventThread) then
585 ThreadIdx := FThreads.IndexOf(Sender);
586 if (ThreadIdx > -1) then
587 FThreads.Delete(ThreadIdx);
588 if (TIBEventThread(Sender).ReturnValue = 1) then
592 ThreadException := False;
597 procedure TZIBEventAlerter.SetAutoRegister(const Value: boolean);
599 if FAutoRegister <> Value then
601 FAutoRegister := Value;
602 if FAutoRegister and (not Registered) and
603 Assigned(FConnection) and FConnection.Connected then
608 function TZIBEventAlerter.GetAutoRegister: boolean;
610 Result := FAutoRegister;
613 procedure TIBEventThread.SQueEvents;
619 Status := Parent.PlainDriver.isc_que_events(StatusVector,
620 Parent.FNativeHandle, @EventID, EventBufferLen,
621 EventBuffer, TISC_CALLBACK(@EventCallback), PVoid(Self));
624 if Status <> -999999 then
625 if Assigned(Parent.OnError) then
626 if E is EZSQLException then
627 Parent.OnError(Parent, EZSQLException(E).ErrorCode)
629 Parent.OnError(Parent, 0);