ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/udr/source/FBUDRController.pas
Revision: 385
Committed: Mon Jan 17 15:56:35 2022 UTC (2 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 82539 byte(s)
Log Message:
Return nil result for UDR procedures when an exception occurs

File Contents

# Content
1 (*
2 * Firebird UDR Support (fbudr). The fbudr components provide a set of
3 * Pascal language bindings for the Firebird API in support of server
4 * side User Defined Routines (UDRs). The fbudr package is an extension
5 * to the Firebird Pascal API.
6 *
7 * The contents of this file are subject to the Initial Developer's
8 * Public License Version 1.0 (the "License"); you may not use this
9 * file except in compliance with the License. You may obtain a copy
10 * of the License here:
11 *
12 * http://www.firebirdsql.org/index.php?op=doc&id=idpl
13 *
14 * Software distributed under the License is distributed on an "AS
15 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
16 * implied. See the License for the specific language governing rights
17 * and limitations under the License.
18 *
19 * The Initial Developer of the Original Code is Tony Whyman.
20 *
21 * The Original Code is (C) 2021 Tony Whyman, MWA Software
22 * (http://www.mwasoftware.co.uk).
23 *
24 * All Rights Reserved.
25 *
26 * Contributor(s): ______________________________________.
27 *
28 *)
29 unit FBUDRController;
30
31 {$IFDEF MSWINDOWS}
32 {$DEFINE WINDOWS}
33 {$WRITEABLECONST ON}
34 {$ENDIF}
35
36 {$IFDEF FPC}
37 {$mode delphi}
38 {$codepage UTF8}
39 {$interfaces COM}
40 {$ENDIF}
41
42 interface
43
44 uses
45 Classes, SysUtils, SyncObjs,Firebird, IB, FBUDRIntf, FBSQLData,
46 FB30Statement, IniFiles;
47
48 type
49 {The Controller is based on the UnloadDetector class in <firebird>/src/include/firebird/UdrCppEngine.h
50 It provides the entry point for the library and is also responsible for cleaning
51 up on library unload.
52
53 It order to operate similar to a C++ static class such as UnloadDetector, when
54 instantiated, it saves a reference to itself as a COM interface in a private
55 class var. This is a managed variable and hence automatically initialised to
56 nil, and automatically freed on exit.
57
58 The controller also handles the registration of UDRs, access to a configuration
59 file and logging.
60 }
61
62 TFBUDRControllerLogOption = (loLogFunctions, loLogProcedures, loLogTriggers, loLogFetches,
63 loModifyQueries, loReadOnlyQueries, loDetails);
64 TFBUDRControllerLogOptions = set of TFBUDRControllerLogOption;
65
66 { In order to customise log handling, the logging options may be
67 statically set by overridding the values of the writable constant
68 FBControllerLogOptions, or through configuration file overrides.
69 }
70
71 TFBUDRControllerOptions = record
72 ModuleName: AnsiString;
73 AllowConfigFileOverrides: boolean;
74 LogFileNameTemplate: AnsiString;
75 ConfigFileNameTemplate: AnsiString;
76 ForceWriteLogEntries: boolean;
77 LogOptions: TFBUDRControllerLogOptions;
78 ThreadSafeLogging: boolean;
79 end;
80
81 {LogFileNameTemplate and ConfigFileName macros:
82 $LOGDIR = Firebird log directory
83 $UDRDIR = Firebird UDR directory
84 $TEMP = System temp directory
85 $MODULE = Module Name
86 $TIMESTAMP = date/time in "yyyymmddhhnnss format
87 }
88
89 const FBUDRControllerOptions: TFBUDRControllerOptions = (
90 ModuleName: 'untitled';
91 AllowConfigFileOverrides: false;
92 LogFileNameTemplate:'$LOGDIR$TIMESTAMP$MODULE.log';
93 ConfigFileNameTemplate: '$UDRDIR$MODULE.conf';
94 ForceWriteLogEntries: false;
95 LogOptions: [];
96 ThreadSafeLogging: false);
97
98 {$if declared(TStringArray)}
99 FalseStrings: TStringArray = ['false','no'];
100 TrueStrings: TStringArray = ['true','yes'];
101 {$ifend}
102 type
103
104 { TFBUDRController }
105
106 TFBUDRController = class(TInterfacedObject)
107 private
108 class var FFBController: IUnknown; {A Managed object and hence should be initialised
109 by the compiler to nil. Also, guaranteed to be
110 destoyed on exit. No need for finalization clause.}
111 class var FUDRFactoryList: TStringList;
112 class var FMyUnloadFlag: boolean;
113 function CharSetIDToText(att: IAttachment; id: integer): AnsiString;
114 function GetStrValue(item: TColumnMetaData): Ansistring;
115 function LogOptionsToStr(aLogOptions: TFBUDRControllerLogOptions
116 ): AnsiString;
117 private
118 const sLogFormat = '@%s:%s';
119 private
120 FTheirUnloadFlag: Firebird.BooleanPtr;
121 FLogStream: TStream;
122 FCriticalSection: TCriticalSection;
123 FMaster: IMaster;
124 FMessageBuffer: AnsiString;
125 FConfigFile: TIniFile;
126 FJnlOpenAppend: boolean;
127 function GetDateTimeFmt: AnsiString;
128 procedure RegisterUDRFactories(status: Firebird.IStatus; udrPlugin: Firebird.IUdrPlugin);
129 procedure RegisterUDRFactory(status: Firebird.IStatus; udrPlugin: Firebird.IUdrPlugin;
130 aName: AnsiString; factory: TObject);
131 procedure FreeFactoryList;
132 procedure LoadConfig;
133 function NeedLogStream: boolean;
134 public
135 constructor Create(status: Firebird.IStatus; udrPlugin: Firebird.IUdrPlugin;
136 aTheirUnloadFlag: booleanPtr; var aMyUnloadFlag: booleanPtr);
137 destructor Destroy; override;
138 procedure FBSetStatusFromException(E: Exception; aStatus: Firebird.IStatus);
139 function ProcessTemplateMacros(aTemplate: AnsiString): AnsiString;
140 procedure WriteToLog(Msg: AnsiString); overload;
141 procedure WriteToLog(aTitle: AnsiString; Params: IFBUDRInputParams); overload;
142 procedure WriteToLog(aTitle: AnsiString; OutputData: IFBUDROutputData); overload;
143 procedure StartJournaling(context: IFBUDRExternalContext);
144 function HasConfigFile: boolean;
145 function ReadConfigString(Section, Ident, DefaultValue: AnsiString): AnsiString;
146 function ReadConfigInteger(Section, Ident: AnsiString; DefaultValue: integer): integer;
147 function ReadConfigBool(Section, Ident: AnsiString; DefaultValue: boolean): boolean;
148 end;
149
150 { TFBUDRInputParams }
151
152 TFBUDRInputParams = class(TResults,IFBUDRInputParams)
153 public
154 function ParamExists(Idx: AnsiString): boolean;
155 function ByName(Idx: AnsiString): ISQLData ; override;
156 end;
157
158 { TFBUDROutputParams }
159
160 TFBUDROutputParams = class(TSQLParams,IFBUDROutputData)
161 public
162 function ParamExists(Idx: AnsiString): boolean;
163 function ByName(Idx: AnsiString): ISQLParam ; override;
164 end;
165
166 {TFBUDROutParamsSQLDA subclasses a TIBXINPUTSQLDA. TIBXINPUTSQLDA is defined
167 in support of executable statements and is usually used to prepare the
168 input parameters to a query. Here, a TFBUDROutParamsSQLDA is used to record the
169 values returned by a function or procedure in the "outMsg" buffer provided
170 when the UDR is invoked.
171 }
172
173 { TFBUDROutParamsSQLDA }
174
175 TFBUDROutParamsSQLDA = class(TIBXINPUTSQLDA) {note semantics reversed}
176 private
177 FBuffer: PByte;
178 FAttachment: IAttachment;
179 FTransaction: ITransaction;
180 protected
181 procedure AllocMessageBuffer(len: integer); override;
182 procedure FreeMessageBuffer; override;
183 function GetAttachment: IAttachment; override;
184 function GetTransaction: ITransaction; override;
185 public
186 {created with the UDR output metadata and a pointer to the outMsg buffer.}
187 constructor Create(context: IFBUDRExternalContext; aMetadata: Firebird.IMessageMetaData; aBuffer: PByte);
188
189 {We override CanChangeMetaData to stop a UDR writer trying to change the output
190 metadata and hence invalidate the outMsg buffer.}
191 function CanChangeMetaData: boolean; override;
192
193 {Finalise is called after the UDR completes and copies the output variable values
194 into the outMsg buffer}
195 procedure Finalise;
196 end;
197
198 {TFBUDRTriggerNewValuesSQLDA additionally initialises the field values to
199 those found in the messagebuffer. This is appropriate for "before" triggers
200 only}
201
202 TFBUDRTriggerNewValuesSQLDA = class(TFBUDROutParamsSQLDA)
203 public
204 {created with the UDR output metadata and a pointer to the outMsg buffer.}
205 constructor Create(context: IFBUDRExternalContext; aMetadata: Firebird.IMessageMetaData; aBuffer: PByte);
206 end;
207
208 {TFBUDRInParamsSQLDA subclasses TIBXOUTPUTSQLDA. TIBXOUTPUTSQLDA is defined
209 in support of executable statements and is usually used to return either
210 a singleton or multiple rows from a query. Here, a TFBUDRInParamsSQLDA is used to
211 hold the input parameter values to a function, procedure or trigger, as provided
212 in the "inMsg" buffer.
213 }
214
215 { TFBUDRInParamsSQLDA }
216
217 TFBUDRInParamsSQLDA = class(TIBXOUTPUTSQLDA) {note semantics reversed}
218 private
219 FBuffer: PByte;
220 FAttachment: IAttachment;
221 FTransaction: ITransaction;
222 protected
223 procedure AllocMessageBuffer(len: integer); override;
224 procedure FreeMessageBuffer; override;
225 function GetAttachment: IAttachment; override;
226 function GetTransaction: ITransaction; override;
227 public
228 {created with the input messge metadata and a pointer to the inMsg buffer}
229 constructor Create(context: IFBUDRExternalContext;
230 aMetadata: Firebird.IMessageMetaData; aBuffer: PByte);
231 end;
232
233 {A TFBUDRFunction object is instantiated by a TUDRFunctionFactory when a
234 "newItem" is requested. This is an abstract class and is subclassed by a
235 UDR writer for each UDR function required. TFBUDRFunction subclasses and their
236 "Routine Name" are registered at initialisation time by the RegisterUDRFunction
237 procedure.
238
239 { TFBUDRFunction }
240
241 { TFBUDRFunction }
242
243 TFBUDRFunction = class(Firebird.IExternalFunctionImpl)
244 private
245 FController: TFBUDRController;
246 FName: AnsiString;
247 FRoutineMetadata: IFBUDRRoutineMetadata;
248 FFieldNames: TStrings;
249 procedure SetFieldNames(SQLDA: TFBUDRInParamsSQLDA);
250 public
251 constructor Create(aController: TFBUDRController;
252 aName: AnsiString;
253 routineMetadata: IFBUDRRoutineMetadata;
254 aFieldNames: TStrings);
255 public
256 {External Function Implementation}
257
258 {Override getCharSet when the function returns strings in a different charset
259 to that used by the database connection.}
260 function getCharSet(context: IFBUDRExternalContext): AnsiString; overload; virtual;
261
262 {Execute must be overridden by each subclass in order to define a new UDR
263 function. The function returns its output value in a variant. The value
264 contained in the variant must be type compatible with the result type in
265 the SQL function declaration.}
266 function Execute(context: IFBUDRExternalContext;
267 ProcMetadata: IFBUDRProcMetadata;
268 InputParams: IFBUDRInputParams;
269 ResultSQLType: cardinal): variant; overload; virtual;
270
271 {alternatively, you can override this version to return any value that ISQLParam
272 can take. The Result is returned as a procedure parameter rather than
273 as a function result}
274 procedure Execute(context: IFBUDRExternalContext;
275 ProcMetadata: IFBUDRProcMetadata;
276 InputParams: IFBUDRInputParams;
277 ReturnValue: ISQLParam); overload; virtual;
278
279 {setup is a class procedure and called by the UDR's Function factory when its
280 setup procedure is called. This is typically called the first time a
281 function factory is first used and may be used to initialise any class vars
282 used by the Execute function. This is normally only required with stateful
283 functions.}
284 class procedure setup(context: IFBUDRExternalContext;
285 metadata: IFBUDRRoutineMetadata;
286 inBuilder: IFBUDRMetadataBuilder;
287 outBuilder: IFBUDRMetadataBuilder); virtual;
288 property Name: AnsiString read FName;
289 property Controller: TFBUDRController read FController;
290 public
291 {IExternalFunction}
292 procedure dispose(); override;
293 procedure getCharSet(status: Firebird.IStatus; context: Firebird.IExternalContext;
294 name: PAnsiChar; nameSize: Cardinal); overload; override;
295 procedure execute(status: Firebird.IStatus; context: Firebird.IExternalContext;
296 inMsg: Pointer; outMsg: Pointer); overload; override;
297 end;
298
299 TFBUDRFunctionClass = class of TFBUDRFunction;
300
301 {A new instance of TFBUDRFunctionFactory is instantiated for each UDR function
302 registered with the controller. It is called by the Firebird engine to "setup"
303 each UDR function and to create a new instance of each UDR function.
304 TFBUDRFunctionFactory is used internally by the UDR Controller and may be ignored
305 by a UDR writer.}
306
307 {see <firebird>/src/plugins/udr_engine/UdrEngine.cpp for how Firebird UDR Engine
308 uses the factory classes}
309
310 { TFBUDRFunctionFactory }
311
312 TFBUDRFunctionFactory = class(Firebird.IUdrFunctionFactoryImpl)
313 private
314 FController: TFBUDRController;
315 FName: AnsiString;
316 FFieldNames: TStringList;
317 FFunction: TFBUDRFunctionClass;
318 procedure SetController(AValue: TFBUDRController);
319 procedure UpdateFieldNames(att: IAttachment; aFunctionName: AnsiString);
320 public
321 constructor Create(aName: AnsiString; aFunction: TFBUDRFunctionClass);
322 destructor Destroy; override;
323 property Controller: TFBUDRController read FController write SetController;
324 public
325 {IUdrFunctionFactory}
326 procedure dispose(); override;
327 procedure setup(status: Firebird.IStatus; context: Firebird.IExternalContext;
328 metadata: Firebird.IRoutineMetadata;
329 inBuilder: Firebird.IMetadataBuilder;
330 outBuilder: Firebird.IMetadataBuilder); override;
331 function newItem(status: Firebird.IStatus; context: Firebird.IExternalContext; metadata:
332 Firebird.IRoutineMetadata): Firebird.IExternalFunction; override;
333 end;
334
335 {A TFBUDRProcedure object is instantiated by a TUDRPRocedureFactory when a
336 "newItem" is requested. This is an abstract class and is the base class for
337 UDR Execute Procedures (TUDRExecuteProcedure) and UDR Select Procedures (TUDRSelectProcedure).
338 }
339
340 { TFBUDRProcedure }
341
342 TFBUDRProcedure = class(Firebird.IExternalProcedureImpl)
343 private
344 FController: TFBUDRController;
345 FName: AnsiString;
346 FRoutineMetadata: IFBUDRRoutineMetadata;
347 FRefCount: integer;
348 protected
349 FInArgNames: TStrings;
350 FOutArgNames: TStrings;
351 procedure SetFieldNames(SQLDA: TSQLDataArea);
352 public
353 constructor Create(aController: TFBUDRController;
354 aName: AnsiString;
355 routineMetadata: IFBUDRRoutineMetadata;
356 aInArgNames, aOutArgNames: TStrings);
357 public
358 {External Procedure Implementation}
359
360 {Override getCharSet when the procedure returns strings in a different charset
361 to that used by the database connection.}
362 function getCharSet(context: IFBUDRExternalContext): AnsiString; overload; virtual;
363
364 {setup is a class procedure and called by the UDR's Procedure factory when its
365 setup procedure is called. This is typically called the first time a
366 procedure factory is first used and may be used to initialise any class vars
367 used by the procedure. This is normally only required with stateful
368 procedures.}
369 class procedure setup(context: IFBUDRExternalContext;
370 metadata: IFBUDRRoutineMetadata;
371 inBuilder: IFBUDRMetadataBuilder;
372 outBuilder: IFBUDRMetadataBuilder); virtual;
373 property Name: AnsiString read FName;
374 property Controller: TFBUDRController read FController;
375 public
376 {IExternalProcedure}
377 procedure dispose(); override;
378 procedure getCharSet(status: Firebird.IStatus; context: Firebird.IExternalContext;
379 name: PAnsiChar; nameSize: Cardinal); overload; override;
380 end;
381
382 TFBUDRProcedureClass = class of TFBUDRProcedure;
383
384 {The Firebird UDR model requires that a procedure's "open" function returns a
385 Firebird.IExternalResultSet object. The TFBUDRExternalResultsSet is used to implement
386 this object and is subclassed separately to provide the results set for
387 Execute and Select Procedures.
388 }
389
390 { TFBUDRExternalResultsSet }
391
392 TFBUDRExternalResultsSet = class(Firebird.IExternalResultSetImpl)
393 private
394 FUDRProcedure: TFBUDRProcedure;
395 FOutputDataSQLDA: TFBUDROutParamsSQLDA;
396 FOutputData: IFBUDROutputData;
397 FOutArgNames: TStrings;
398 procedure SetFieldNames(SQLDA: TSQLDataArea);
399 protected
400 procedure Close; virtual;
401 public
402 constructor Create(UDRProcedure: TFBUDRProcedure; context: IFBUDRExternalContext;
403 aOutArgNames: TStrings;
404 metadata: Firebird.IMessageMetadata;
405 outMsg: pointer);
406 destructor Destroy; override;
407 property OutputData: IFBUDROutputData read FOutputData;
408 public
409 {IExternalResultSetImpl}
410 procedure dispose(); override;
411 end;
412
413 {TFBUDRSingletonRow subclasses TFBUDRExternalResultsSet in order to provide the
414 Firebird.IExternalResultSet object returned by an Execute procedure. In a
415 TFBUDRExecuteProcedure, the output parameters are set in the main body of
416 the UDR writer's Execute procedure and are returned to the Firebird engine
417 via the "fetch" function implemented by this object.
418 }
419
420 { TFBUDRSingletonRow }
421
422 TFBUDRSingletonRow = class(TFBUDRExternalResultsSet)
423 private
424 FFetchCalled: boolean;
425 public
426 {IExternalResultSetImpl}
427 function fetch(status: Firebird.IStatus): Boolean; override;
428 end;
429
430 {TFBUDRResultsCursor subclasses TFBUDRExternalResultsSet in order to provide the
431 Firebird.IExternalResultSet object returned by s Select procedure. In a
432 TUDRSelectProcedure, the work is divided into two methods "open" and "fetch".
433 The former initialises procedure, while "fetch" is called to return each row
434 in the output dataset. A "close" nethod may also be provided to perform any
435 tidy up following the last call to "fetch".
436
437 }
438 { TFBUDRResultsCursor }
439
440 TFBUDRResultsCursor = class(TFBUDRExternalResultsSet)
441 private
442 FDone: boolean;
443 protected
444 procedure Close; override;
445 public
446 {IExternalResultSetImpl}
447 function fetch(status: Firebird.IStatus): Boolean; override;
448 end;
449
450 {TFBUDRExecuteProcedure subclasses a TFBUDRProcedure for a UDR Execute Procedure.
451 A TFBUDRExecuteProcedure object is instantiated by a TUDRProcedureFactory when a
452 "newItem" is requested. This is an abstract class and is subclassed by a
453 UDR writer for each UDR Execute procedure required. TFBUDRExecuteProcedure
454 subclasses and their "Routine Name" are registered at initialisation time by
455 the RegisterUDRProcedure procedure.
456 }
457
458 { TFBUDRExecuteProcedure }
459
460 TFBUDRExecuteProcedure = class(TFBUDRProcedure)
461 public
462 {Execute must be overridden by each subclass in order to define a new UDR
463 Execute Procedure. The procedure may read its input parameter values from the
464 InputParams interface, and return the output parameter values using the
465 outputData interface.}
466 procedure Execute(context: IFBUDRExternalContext;
467 ProcMetadata: IFBUDRProcMetadata;
468 InputParams: IFBUDRInputParams;
469 OutputData: IFBUDROutputData); virtual; abstract;
470 public
471 {IExternalProcedure}
472 function open(status: Firebird.IStatus; context: Firebird.IExternalContext;
473 inMsg: Pointer; outMsg: Pointer): Firebird.IExternalResultSet; override;
474 end;
475
476 {TFBUDRSelectProcedure subclasses a TFBUDRProcedure for a UDR Select Procedure.
477 A TFBUDRSelectProcedure object is instantiated by a TUDRProcedureFactory when a
478 "newItem" is requested. This is an abstract class and is subclassed by a
479 UDR writer for each UDR Select procedure required. TFBUDRSelectProcedure
480 subclasses and their "Routine Name" are registered at initialisation time by
481 the RegisterUDRProcedure procedure.
482 }
483
484 { TFBUDRSelectProcedure }
485
486 TFBUDRSelectProcedure = class(TFBUDRProcedure)
487 public
488 {open must be overridden by a subclass in order to process the input parameter
489 values and to set up the select procedure. }
490 procedure open(context: IFBUDRExternalContext;
491 ProcMetadata: IFBUDRProcMetadata;
492 InputParams: IFBUDRInputParams); overload; virtual; abstract;
493
494 {After "open" returns the "fetch" method is called by the Firebird engine
495 to return each row in the output dataset. The values of the row's columns
496 are retuned in the Outputdata. Fetch returns false to indicate End of data.}
497 function fetch(OutputData: IFBUDROutputData): boolean; virtual;
498
499 {The "close" method is called after "fetch" returns false and may be overridden
500 in order to tidy up if necessary.}
501 procedure close; virtual;
502 public
503 {IExternalProcedure}
504 function open(status: Firebird.IStatus; context: Firebird.IExternalContext;
505 inMsg: Pointer; outMsg: Pointer): Firebird.IExternalResultSet; overload; override;
506 end;
507
508 {A new instance of TFBUDRProcedureFactory is instantiated for each UDR procedure
509 registered with the controller. It is called by the Firebird engine to "setup"
510 each UDR procedure and to create a new instance of each UDR procedure.
511 TFBUDRProcedureFactory is used internally by the UDR Controller and may be ignored
512 by a UDR writer.}
513
514 { TFBUDRProcedureFactory }
515
516 TFBUDRProcedureFactory = class(Firebird.IUdrProcedureFactoryImpl)
517 private
518 FController: TFBUDRController;
519 FName: AnsiString;
520 FProcedure: TFBUDRProcedureClass;
521 FInArgNames: TStringList;
522 FOutArgNames: TStringList;
523 procedure SetController(AValue: TFBUDRController);
524 procedure UpdateArgNames(att: IAttachment; aProcName: AnsiString);
525 public
526 constructor Create(aName: AnsiString; aProcedure: TFBUDRProcedureClass);
527 destructor Destroy; override;
528 property Controller: TFBUDRController read FController write SetController;
529 public
530 {IUdrProcedureFactory}
531 procedure dispose(); override;
532 procedure setup(status: Firebird.IStatus; context: Firebird.IExternalContext;
533 metadata:Firebird.IRoutineMetadata; inBuilder: Firebird.IMetadataBuilder;
534 outBuilder: Firebird.IMetadataBuilder); override;
535 function newItem(status: Firebird.IStatus; context: Firebird.IExternalContext;
536 metadata: Firebird.IRoutineMetadata): IExternalProcedure; override;
537 end;
538
539 TFBUDRTriggerAction = (taInsert, taUpdate, taDelete, taConnect, taDisconnect,
540 taTransactionStart, taTransactionCommit, taTransactionRollback,
541 taDDL);
542 {A TFBUDRTrigger object is instantiated by a TUDRTriggerFactory when a
543 "newItem" is requested. This is an abstract class and is subclassed by a
544 UDR writer for each UDR Trigger required. TFBUDRTrigger subclasses and their
545 "Routine Name" are registered at initialisation time by the RegisterUDRTrigger
546 procedure.}
547
548 { TFBUDRTrigger }
549
550 TFBUDRTrigger = class(Firebird.IExternalTriggerImpl)
551 private
552 FController: TFBUDRController;
553 FName: AnsiString;
554 FRoutineMetadata: IFBUDRRoutineMetadata;
555 FFieldNames: TStrings;
556 procedure SetFieldNames(SQLDA: TSQLDataArea);
557 public
558 constructor Create(aController: TFBUDRController;
559 aName: AnsiString;
560 routineMetadata: IFBUDRRoutineMetadata;
561 aFieldNames: TStrings);
562 public
563 {External Trigger Implementation}
564
565 {Override getCharSet when the Trigger returns (?) strings in a different charset
566 to that used by the database connection.}
567 function getCharSet(context: IFBUDRExternalContext): AnsiString; overload; virtual;
568
569 {Override AfterTrigger in order to carry out an after trigger's function.
570 Separate interfaces are used to provide the "old" and "new" values of each
571 of the parent dataset's columns. Note that these are both read only for an
572 after trigger}
573
574 procedure AfterTrigger(context: IFBUDRExternalContext;
575 TriggerMetaData: IFBUDRTriggerMetaData;
576 action: TFBUDRTriggerAction;
577 OldParams: IFBUDRInputParams;
578 NewParams: IFBUDRInputParams); virtual;
579
580
581 {Override BeforeTrigger in order to carry out a before trigger's function.
582 Separate interfaces are used to provide the "old" and "new" values of each
583 of the parent dataset's columns. Note that the new values are writeable for an
584 after trigger}
585
586 procedure BeforeTrigger(context: IFBUDRExternalContext;
587 TriggerMetaData: IFBUDRTriggerMetaData;
588 action: TFBUDRTriggerAction;
589 OldParams: IFBUDRInputParams;
590 NewParams: IFBUDROutputData); virtual;
591
592
593 {Override DatabaseTrigger in order to carry out a before trigger's function.
594 Note that a database trigger does not have any input values}
595
596 procedure DatabaseTrigger(context: IFBUDRExternalContext;
597 TriggerMetaData: IFBUDRTriggerMetaData); virtual;
598
599 {setup is a class procedure and called by the UDR's Trigger factory when its
600 setup procedure is called. This is typically called the first time a
601 trigger factory is first used and may be used to initialise any class vars
602 used by the Execute function. This is normally only required with stateful
603 triggers.}
604
605 class procedure setup(context: IFBUDRExternalContext;
606 metadata: IFBUDRRoutineMetadata;
607 fieldsBuilder: IFBUDRMetadataBuilder); virtual;
608
609 property Name: AnsiString read FName;
610 property Controller: TFBUDRController read FController;
611 public
612 {IExternalTrigger}
613 procedure dispose(); override;
614 procedure getCharSet(status: Firebird.IStatus; context: Firebird.IExternalContext;
615 name: PAnsiChar; nameSize: Cardinal); overload; override;
616 procedure execute(status: Firebird.IStatus; context: Firebird.IExternalContext;
617 action: Cardinal; oldMsg: Pointer; newMsg: Pointer); overload; override;
618 end;
619
620 TFBUDRTriggerClass = class of TFBUDRTrigger;
621
622 {A new instance of TFBUDRTriggerFactory is instantiated for each UDR trigger
623 registered with the controller. It is called by the Firebird engine to "setup"
624 each UDR trigger and to create a new instance of each UDR trigger.
625 TFBUDRTriggerFactory is used internally by the UDR Controller and may be ignored
626 by a UDR writer.}
627
628 { TFBUDRTriggerFactory }
629
630 TFBUDRTriggerFactory = class(Firebird.IUdrTriggerFactoryImpl)
631 private
632 FController: TFBUDRController;
633 FName: AnsiString;
634 FTrigger: TFBUDRTriggerClass;
635 FFieldNames: TStringList;
636 procedure SetController(AValue: TFBUDRController);
637 procedure UpdateFieldNames(att: IAttachment; aTableName: AnsiString);
638 public
639 constructor Create(aName: AnsiString; aTrigger: TFBUDRTriggerClass);
640 destructor Destroy; override;
641 property Controller: TFBUDRController read FController write SetController;
642 public
643 procedure dispose(); override;
644 procedure setup(status: Firebird.IStatus; context: Firebird.IExternalContext;
645 metadata: Firebird.IRoutineMetadata;
646 fieldsBuilder: Firebird.IMetadataBuilder); override;
647 function newItem(status: Firebird.IStatus; context: Firebird.IExternalContext;
648 metadata: Firebird.IRoutineMetadata): Firebird.IExternalTrigger; override;
649 end;
650
651 {firebird_udr_plugin is the UDR library entry point and must be exported by the
652 library.}
653
654 function firebird_udr_plugin(status: Firebird.IStatus; aTheirUnloadFlag: Firebird.BooleanPtr;
655 udrPlugin: Firebird.IUdrPlugin): Firebird.BooleanPtr; cdecl;
656
657 {The register functions are called at initialisation time to register each function,
658 procedure and trigger defined by the library. Note: "aName" is the routine name
659 in the corresponding SQL declaration for the function, procedure or trigger.}
660
661 procedure FBRegisterUDRFunction(aName: AnsiString; aFunction: TFBUDRFunctionClass);
662 procedure FBRegisterUDRProcedure (aName: AnsiString; aProcedure: TFBUDRProcedureClass);
663 procedure FBRegisterUDRTrigger(aName: AnsiString; aTrigger: TFBUDRTriggerClass);
664
665 implementation
666
667 uses FBUDRUtils, FBUDRMessage, IBUtils, FBClientAPI, FB30ClientAPI, IBErrorCodes,
668 IBExternals;
669
670 resourcestring
671 SFetchCalled = 'Fetch called for ';
672 SOpenExecuteProc = 'Open Execute Procedure ';
673 SOpenSelectProc = 'Open Select Procedure ';
674 SSetupTrigger = 'Setup Trigger ';
675 STriggerDisposed = 'Trigger %s: dispose called';
676 STriggerCharset = 'GetCharSet for Trigger %s charset name = "%s"';
677 STriggerExecute = 'Execute Trigger ';
678 STriggerNew = 'New Field Values:';
679 STriggerOld = 'Old Field Values:';
680 SProcSetup = 'Setup Procedure ';
681 SProcDispose = 'Procedure %s: dispose called with refcount = %d';
682 SProcCharset = 'GetCharSet for Procedure %s charset name = "%s"';
683 sFuncCreated = 'Function %s created';
684 SInputParams = 'Input Parameters';
685 SOutputParams = 'Output Parameters';
686 SOutputData = 'Output Parameters with data';
687 SFuncDispose = 'Function %s: dispose called';
688 SFuncCharset = 'GetCharSet for Function %s charset name = "%s"';
689 SFuncExecute = 'Execute Function ';
690 SFuncSetup = 'Setup Function ';
691 SRoutineMetadata = 'Routine Metadata';
692 SExceptionRaised = 'Exception raised';
693 SFuncRegister = 'Registering Function ';
694 SProcRegister = 'Registering Procedure ';
695 STriggerRegister = 'Registering Trigger ';
696 SBadLogOptionsStr = 'Malformed LogOptions Config string "%s" at position %d';
697 SNoConfigFile = 'Unable to find/load configuration file';
698 SReadingConfigFile = 'Reading Configuration File: %s';
699 SFuncNotOverridden = 'UDR Function %s is undefined';
700 SNoReturnValue = 'Function %s does not have a return value!';
701 STriggerIsNotImplemented = 'Trigger %s is not implemented';
702 STriggerNewAfter = 'New Field Values after trigger execution';
703 SUnknownFieldName = 'Unknown Field Name - %s';
704 SEof = 'No More Rows';
705
706 function firebird_udr_plugin(status: Firebird.IStatus;
707 aTheirUnloadFlag: Firebird.BooleanPtr; udrPlugin: Firebird.IUdrPlugin
708 ): Firebird.BooleanPtr; cdecl;
709 begin
710 if TFBUDRController.FFBController = nil then
711 TFBUDRController.Create(status,udrPlugin,aTheirUnloadFlag,Result); {create a default instance}
712 end;
713
714 procedure RegisterUDRFactory(aName: AnsiString; aFactory: TObject);
715 begin
716 if TFBUDRController.FUDRFactoryList = nil then
717 TFBUDRController.FUDRFactoryList := TStringList.Create;
718
719 TFBUDRController.FUDRFactoryList.AddObject(aName,aFactory);
720 end;
721
722 procedure FBRegisterUDRFunction(aName: AnsiString; aFunction: TFBUDRFunctionClass);
723 begin
724 RegisterUDRFactory(aName,TFBUDRFunctionFactory.Create(aName,aFunction));
725 end;
726
727 procedure FBRegisterUDRProcedure(aName: AnsiString;
728 aProcedure: TFBUDRProcedureClass);
729 begin
730 RegisterUDRFactory(aName,TFBUDRProcedureFactory.Create(aName,aProcedure));
731 end;
732
733 procedure FBRegisterUDRTrigger(aName: AnsiString; aTrigger: TFBUDRTriggerClass);
734 begin
735 RegisterUDRFactory(aName,TFBUDRTriggerFactory.Create(aName,aTrigger));
736 end;
737
738 { TFBUDRInputParams }
739
740 function TFBUDRInputParams.ParamExists(Idx: AnsiString): boolean;
741 begin
742 Result := inherited ByName(Idx) <> nil;
743 end;
744
745 function TFBUDRInputParams.ByName(Idx: AnsiString): ISQLData;
746 begin
747 Result := inherited ByName(Idx);
748 if Result = nil then
749 raise Exception.CreateFmt(SUnknownFieldName,[idx]);
750 end;
751
752 { TFBUDROutputParams }
753
754 function TFBUDROutputParams.ParamExists(Idx: AnsiString): boolean;
755 begin
756 Result := inherited ByName(Idx) <> nil;
757 end;
758
759 function TFBUDROutputParams.ByName(Idx: AnsiString): ISQLParam;
760 begin
761 Result := inherited ByName(Idx);
762 if Result = nil then
763 raise Exception.CreateFmt(SUnknownFieldName,[idx]);
764 end;
765
766 { TFBUDRTriggerNewValuesSQLDA }
767
768 constructor TFBUDRTriggerNewValuesSQLDA.Create(context: IFBUDRExternalContext;
769 aMetadata: Firebird.IMessageMetaData; aBuffer: PByte);
770 var i: integer;
771 SQLNullIndicator: PShort;
772 data: PByte;
773 strlen: integer;
774 rs: rawbytestring;
775 begin
776 inherited Create(context,aMetadata,aBuffer);
777 for i := 0 to Count - 1 do
778 with TIBXSQLVar(Column[i]) do
779 begin
780 SQLNullIndicator := PShort(aBuffer + aMetaData.getNullOffset(context.getStatus,i));
781 context.CheckStatus;
782 if SQLNullIndicator^ = -1 then
783 IsNull := true
784 else
785 begin
786 IsNull := false;
787 data := aBuffer + aMetaData.getOffset(context.getStatus,i);
788 context.CheckStatus;
789 if SQLType = SQL_VARYING then
790 begin
791 strlen := (context.GetFirebirdAPI as TFBClientAPI).DecodeInteger(data,2);
792 DataLength := strlen;
793 setLength(rs,strlen);
794 Move((data+2)^,rs[1],strlen);
795 SetCodePage(rs,CodePage,false);
796 SetString(rs);
797 end
798 else
799 Move(data^,SQLData^,DataLength);
800 end;
801 end;
802 end;
803
804 { TFBUDRResultsCursor }
805
806 procedure TFBUDRResultsCursor.Close;
807 begin
808 inherited Close;
809 if not FDone then
810 (FUDRProcedure as TFBUDRSelectProcedure).Close;
811 FDone := true;
812 end;
813
814 function TFBUDRResultsCursor.fetch(status: Firebird.IStatus): Boolean;
815 begin
816 try
817 if loLogFetches in FBUDRControllerOptions.LogOptions then
818 FUDRProcedure.FController.WriteToLog(SFetchCalled + FUDRProcedure.FName);
819
820 if FOutputDataSQLDA <> nil then
821 begin
822 Result := (FUDRProcedure as TFBUDRSelectProcedure).fetch(FOutputData);
823 if [loLogFetches,loDetails] <= FBUDRControllerOptions.LogOptions then
824 if Result then
825 FUDRProcedure.FController.WriteToLog(SOutputData,FOutputData)
826 else
827 FUDRProcedure.FController.WriteToLog(SEof);
828 FOutputDataSQLDA.Finalise;
829 end
830 else
831 Result := false;
832
833 if not Result then
834 begin
835 if not FDone then
836 (FUDRProcedure as TFBUDRSelectProcedure).Close;
837 FDone := true;
838 end;
839 except on E: Exception do
840 FUDRProcedure.FController.FBSetStatusFromException(E,status);
841 end;
842 end;
843
844 { TFBUDRSingletonRow }
845
846 function TFBUDRSingletonRow.fetch(status: Firebird.IStatus): Boolean;
847 begin
848 try
849 if FOutputDataSQLDA <> nil then
850 begin
851 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
852 FUDRProcedure.FController.WriteToLog(SOutputData,FOutputData);
853 FOutputDataSQLDA.Finalise; {copy output row to outMsg}
854 Result := not FFetchCalled;
855 FFetchCalled := true;
856 end
857 else
858 Result := false;
859 except on E: Exception do
860 FUDRProcedure.FController.FBSetStatusFromException(E,status);
861 end;
862 end;
863
864 { TFBUDRExecuteProcedure }
865
866 function TFBUDRExecuteProcedure.open(status: Firebird.IStatus;
867 context: Firebird.IExternalContext; inMsg: Pointer; outMsg: Pointer
868 ): Firebird.IExternalResultSet;
869 var aProcMetadata: IFBUDRProcMetadata;
870 InputParamsSQLDA: TFBUDRInParamsSQLDA;
871 InputParams: IFBUDRInputParams;
872 metadata: Firebird.IMessageMetadata;
873 FBContext: IFBUDRExternalContext;
874 begin
875 InputParams := nil;
876 InputParamsSQLDA := nil;
877 try
878 if loLogProcedures in FBUDRControllerOptions.LogOptions then
879 FController.WriteToLog(SOpenExecuteProc + FName);
880
881 if FRoutineMetadata.QueryInterface(IFBUDRProcMetadata,aProcMetadata) <> S_OK then
882 FBUDRError(ibxeNoProcMetadata,[nil])
883 else
884 begin
885 FBContext := TFBUDRExternalContext.Create(Controller,context);
886 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
887 FController.WriteToLog((FBContext as TFBUDRExternalContext).AsText);
888
889 if FRoutineMetadata.HasInputMetadata then
890 begin
891 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getInputMetadata;
892 try
893 InputParamsSQLDA := TFBUDRInParamsSQLDA.Create(FBContext,
894 metadata,
895 inMsg);
896 SetFieldNames(InputParamsSQLDA);
897 finally
898 metadata.release;
899 end;
900 end;
901
902 try
903 if InputParamsSQLDA <> nil then
904 begin
905 InputParams := TFBUDRInputParams.Create(InputParamsSQLDA);
906 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
907 FController.WriteToLog(SInputParams,InputParams);
908 end;
909
910 metadata := nil;
911 if FRoutineMetadata.HasOutputMetadata then
912 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getOutputMetadata;
913
914 try
915 Result := TFBUDRSingletonRow.Create(self, FBContext, FOutArgNames,
916 metadata,
917 outMsg);
918
919 finally
920 if metadata <> nil then
921 metadata.release;
922 end;
923
924 Execute(FBContext,aProcMetadata,InputParams,
925 (Result as TFBUDRSingletonRow).OutputData);
926 finally
927 InputParams := nil;
928 if InputParamsSQLDA <> nil then
929 InputParamsSQLDA.Free;
930 end;
931
932 end;
933 except on E: Exception do
934 begin
935 Result.dispose;
936 Result := nil;
937 FController.FBSetStatusFromException(E,status);
938 end;
939 end;
940 end;
941
942 { TFBUDRSelectProcedure }
943
944 function TFBUDRSelectProcedure.fetch(OutputData: IFBUDROutputData): boolean;
945 begin
946 Result := false;
947 end;
948
949 procedure TFBUDRSelectProcedure.close;
950 begin
951 //override this method to tidy up once all rows have been returned
952 end;
953
954 function TFBUDRSelectProcedure.open(status: Firebird.IStatus;
955 context: Firebird.IExternalContext; inMsg: Pointer; outMsg: Pointer
956 ): Firebird.IExternalResultSet;
957 var aProcMetadata: IFBUDRProcMetadata;
958 InputParamsSQLDA: TFBUDRInParamsSQLDA;
959 InputParams: IFBUDRInputParams;
960 metadata: Firebird.IMessageMetadata;
961 FBContext: IFBUDRExternalContext;
962 begin
963 try
964 if loLogProcedures in FBUDRControllerOptions.LogOptions then
965 FController.WriteToLog(SOpenSelectProc + FName);
966
967 InputParamsSQLDA := nil;
968 InputParams := nil;
969 if FRoutineMetadata.QueryInterface(IFBUDRProcMetadata,aProcMetadata) <> S_OK then
970 FBUDRError(ibxeNoProcMetadata,[nil])
971 else
972 begin
973 FBContext := TFBUDRExternalContext.Create(Controller,context);
974 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
975 FController.WriteToLog((FBContext as TFBUDRExternalContext).AsText);
976
977 if FRoutineMetadata.HasInputMetadata then
978 begin
979 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getInputMetadata;
980 try
981 InputParamsSQLDA := TFBUDRInParamsSQLDA.Create(FBContext,
982 metadata,
983 inMsg);
984 SetFieldNames(InputParamsSQLDA);
985 finally
986 metadata.release;
987 end;
988 end;
989
990 try
991 if InputParamsSQLDA <> nil then
992 begin
993 InputParams := TFBUDRInputParams.Create(InputParamsSQLDA);
994 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
995 FController.WriteToLog(SInputParams,InputParams);
996 end;
997
998 metadata := nil;
999 if FRoutineMetadata.HasOutputMetadata then
1000 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getOutputMetadata;
1001
1002 try
1003 open(FBContext,aProcMetadata,InputParams);
1004 Result := TFBUDRResultsCursor.Create(self, FBContext,
1005 FOutArgNames,
1006 metadata,
1007 outMsg);
1008 finally
1009 if metadata <> nil then
1010 metadata.release;
1011 end;
1012 finally
1013 InputParams := nil;
1014 if InputParamsSQLDA <> nil then
1015 InputParamsSQLDA.Free;
1016 end;
1017 end;
1018 except on E: Exception do
1019 begin
1020 Result.dispose;
1021 Result := nil;
1022 FController.FBSetStatusFromException(E,status);
1023 end;
1024 end;
1025 end;
1026
1027 const
1028 sGetFieldNamesSQL = 'Select Trim(RF.RDB$FIELD_NAME) as ColumnName '+
1029 'FROM RDB$RELATION_FIELDS RF '+
1030 'JOIN RDB$FIELDS B On B.RDB$FIELD_NAME = RF.RDB$FIELD_SOURCE '+
1031 'Where RF.RDB$RELATION_NAME = ? ' +
1032 'order by RF.RDB$FIELD_POSITION asc';
1033
1034 { TFBUDRTriggerFactory }
1035
1036 procedure TFBUDRTriggerFactory.UpdateFieldNames(att: IAttachment;
1037 aTableName: AnsiString);
1038 var FieldNames: IResultSet;
1039 begin
1040 FFieldNames.Clear;
1041 if aTableName = '' then
1042 Exit;
1043 FieldNames := att.OpenCursorAtStart(sGetFieldNamesSQL,[aTableName]);
1044 while not FieldNames.IsEOF do
1045 begin
1046 FFieldNames.Add(FieldNames[0].AsString);
1047 FieldNames.FetchNext;
1048 end;
1049 end;
1050
1051 procedure TFBUDRTriggerFactory.SetController(AValue: TFBUDRController);
1052 begin
1053 if FController = AValue then Exit;
1054 FController := AValue;
1055 end;
1056
1057 constructor TFBUDRTriggerFactory.Create(aName: AnsiString;
1058 aTrigger: TFBUDRTriggerClass);
1059 begin
1060 inherited Create;
1061 FName := aName;
1062 FTrigger := aTrigger;
1063 FFieldNames := TStringList.Create;
1064 end;
1065
1066 destructor TFBUDRTriggerFactory.Destroy;
1067 begin
1068 if FFieldNames <> nil then
1069 FFieldNames.Free;
1070 inherited Destroy;
1071 end;
1072
1073 procedure TFBUDRTriggerFactory.dispose();
1074 begin
1075 Free;
1076 end;
1077
1078 procedure TFBUDRTriggerFactory.setup(status: Firebird.IStatus;
1079 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata;
1080 fieldsBuilder: Firebird.IMetadataBuilder);
1081 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1082 FBFieldsBuilder: IFBUDRMetadataBuilder;
1083 FBContext: IFBUDRExternalContext;
1084 begin
1085 FBFieldsBuilder := nil;
1086 try
1087 if loLogTriggers in FBUDRControllerOptions.LogOptions then
1088 FController.WriteToLog(SSetupTrigger + FName);
1089
1090 FBContext := TFBUDRExternalContext.Create(Controller,context);
1091 FController.StartJournaling(FBContext);
1092
1093 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1094
1095 if fieldsBuilder <> nil then
1096 FBFieldsBuilder := TFBUDRMetadataBuilder.Create(FBContext,fieldsBuilder);
1097 if [loLogTriggers, loDetails] <= FBUDRControllerOptions.LogOptions then
1098 FController.WriteToLog(SRoutineMetadata + LineEnding + (FBRoutineMetadata as TFBUDRRoutineMetadata).AsText);
1099 TFBUDRTrigger.setup(FBContext,FBRoutineMetadata,FBFieldsBuilder)
1100 except on E: Exception do
1101 FController.FBSetStatusFromException(E,status);
1102 end;
1103 end;
1104
1105 function TFBUDRTriggerFactory.newItem(status: Firebird.IStatus;
1106 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata
1107 ): Firebird.IExternalTrigger;
1108 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1109 FBContext: IFBUDRExternalContext;
1110 begin
1111 try
1112 FBContext := TFBUDRExternalContext.Create(Controller,context);
1113 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1114 {Now get the Field Names}
1115 UpdateFieldNames(FBContext.GetAttachment,FBRoutineMetadata.getTriggerTable);
1116 Result := FTrigger.Create(FController,FName,FBRoutineMetadata,FFieldNames);
1117 except on E: Exception do
1118 FController.FBSetStatusFromException(E,status);
1119 end;
1120 end;
1121
1122 { TFBUDRTrigger }
1123
1124 procedure TFBUDRTrigger.SetFieldNames(SQLDA: TSQLDataArea);
1125 var i: integer;
1126 begin
1127 for i := 0 to FFieldNames.Count - 1 do
1128 if i < SQLDA.Count then
1129 SQLDA[i].Name := FFieldNames[i];
1130 end;
1131
1132 constructor TFBUDRTrigger.Create(aController: TFBUDRController;
1133 aName: AnsiString; routineMetadata: IFBUDRRoutineMetadata;
1134 aFieldNames: TStrings);
1135 begin
1136 inherited Create;
1137 FName := aName;
1138 FController := aController;
1139 FRoutineMetaData := routineMetadata;
1140 FFieldNames := aFieldNames;
1141 end;
1142
1143 function TFBUDRTrigger.getCharSet(context: IFBUDRExternalContext): AnsiString;
1144 begin
1145 Result := '';
1146 end;
1147
1148 procedure TFBUDRTrigger.AfterTrigger(context: IFBUDRExternalContext;
1149 TriggerMetaData: IFBUDRTriggerMetaData; action: TFBUDRTriggerAction;
1150 OldParams: IFBUDRInputParams; NewParams: IFBUDRInputParams);
1151 begin
1152 raise Exception.CreateFmt(STriggerIsNotImplemented,[FName]);
1153 end;
1154
1155 procedure TFBUDRTrigger.BeforeTrigger(context: IFBUDRExternalContext;
1156 TriggerMetaData: IFBUDRTriggerMetaData; action: TFBUDRTriggerAction;
1157 OldParams: IFBUDRInputParams; NewParams: IFBUDROutputData);
1158 begin
1159 raise Exception.CreateFmt(STriggerIsNotImplemented,[FName]);
1160 end;
1161
1162 procedure TFBUDRTrigger.DatabaseTrigger(context: IFBUDRExternalContext;
1163 TriggerMetaData: IFBUDRTriggerMetaData);
1164 begin
1165 raise Exception.CreateFmt(STriggerIsNotImplemented,[FName]);
1166 end;
1167
1168 class procedure TFBUDRTrigger.setup(context: IFBUDRExternalContext;
1169 metadata: IFBUDRRoutineMetadata; fieldsBuilder: IFBUDRMetadataBuilder);
1170 begin
1171 //Override in subclass
1172 end;
1173
1174 procedure TFBUDRTrigger.dispose();
1175 begin
1176 if loLogTriggers in FBUDRControllerOptions.LogOptions then
1177 FController.WriteToLog(Format(STriggerDisposed,[FName]));
1178
1179 Free;
1180 end;
1181
1182 procedure TFBUDRTrigger.getCharSet(status: Firebird.IStatus;
1183 context: Firebird.IExternalContext; name: PAnsiChar; nameSize: Cardinal);
1184 var charset: AnsiString;
1185 FBContext: IFBUDRExternalContext;
1186 begin
1187 try
1188 FBContext := TFBUDRExternalContext.Create(Controller,context);
1189 charset := getCharSet(FBContext);
1190 if charset <> '' then
1191 begin
1192 StrPLCopy(name,charset,nameSize);
1193 if loLogTriggers in FBUDRControllerOptions.LogOptions then
1194 FController.WriteToLog(Format(STriggerCharset,[FName,charset]));
1195 end;
1196 except on E: Exception do
1197 FController.FBSetStatusFromException(E,status);
1198 end;
1199 end;
1200
1201 procedure TFBUDRTrigger.execute(status: Firebird.IStatus;
1202 context: Firebird.IExternalContext; action: Cardinal; oldMsg: Pointer;
1203 newMsg: Pointer);
1204 var aTriggerMetadata: IFBUDRTriggerMetaData;
1205 OldParamsSQLDA: TFBUDRInParamsSQLDA;
1206 NewParamsSQLDA: TFBUDRInParamsSQLDA;
1207 WritableParamsSQLDA: TFBUDRTriggerNewValuesSQLDA;
1208 OldParams: IFBUDRInputParams;
1209 NewParams: IFBUDRInputParams;
1210 NewWritableParams: IFBUDROutputData;
1211 TriggerAction: TFBUDRTriggerAction;
1212 FBContext: IFBUDRExternalContext;
1213
1214 procedure SetUpOldParams;
1215 var metadata: Firebird.IMessageMetadata;
1216 begin
1217 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getTriggerMetadata;
1218 try
1219 OldParamsSQLDA := TFBUDRInParamsSQLDA.Create(FBContext,
1220 metadata,
1221 oldMsg);
1222 SetFieldNames(OldParamsSQLDA);
1223 OldParams := TFBUDRInputParams.Create(OldParamsSQLDA);
1224 if [loLogTriggers,loDetails] <= FBUDRControllerOptions.LogOptions then
1225 begin
1226 FController.WriteToLog(STriggerOld,OldParams);
1227 end;
1228 finally
1229 metadata.release;
1230 end;
1231 end;
1232
1233 procedure SetupNewParams;
1234 var metadata: Firebird.IMessageMetadata;
1235 begin
1236 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getTriggerMetadata;
1237 try
1238 NewParamsSQLDA := TFBUDRInParamsSQLDA.Create(FBContext,
1239 metadata,
1240 newMsg);
1241 SetFieldNames(NewParamsSQLDA);
1242 NewParams := TFBUDRInputParams.Create(NewParamsSQLDA);
1243 if [loLogTriggers,loDetails] <= FBUDRControllerOptions.LogOptions then
1244 begin
1245 FController.WriteToLog(STriggerNew,NewParams);
1246 end;
1247 finally
1248 metadata.release;
1249 end;
1250 end;
1251
1252 procedure SetupWritableNewParams;
1253 var metadata: Firebird.IMessageMetadata;
1254 begin
1255 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getTriggerMetadata;
1256 try
1257 WritableParamsSQLDA := TFBUDRTriggerNewValuesSQLDA.Create(FBContext,
1258 metadata,
1259 newMsg);
1260 SetFieldNames(WritableParamsSQLDA);
1261 NewWritableParams := TFBUDROutputParams.Create(WritableParamsSQLDA);
1262 if [loLogTriggers,loDetails] <= FBUDRControllerOptions.LogOptions then
1263 begin
1264 FController.WriteToLog(STriggerNew,NewWritableParams);
1265 end;
1266 finally
1267 metadata.release;
1268 end;
1269 end;
1270
1271 begin
1272 try
1273 if loLogTriggers in FBUDRControllerOptions.LogOptions then
1274 FController.WriteToLog(STriggerExecute + FName);
1275 OldParamsSQLDA := nil;
1276 NewParamsSQLDA := nil;
1277 WritableParamsSQLDA := nil;
1278 OldParams := nil;
1279 NewParams := nil;
1280 NewWritableParams := nil;
1281
1282 if FRoutineMetadata.QueryInterface(IFBUDRTriggerMetaData,aTriggerMetadata) <> S_OK then
1283 FBUDRError(ibxeNoTriggerMetadata,[nil])
1284 else
1285 begin
1286 FBContext := TFBUDRExternalContext.Create(Controller,context);
1287
1288 with Firebird.IExternalTrigger do
1289 case action of
1290 ACTION_INSERT:
1291 TriggerAction := taInsert;
1292 ACTION_UPDATE:
1293 TriggerAction := taUpdate;
1294 ACTION_DELETE:
1295 TriggerAction := taDelete;
1296 ACTION_CONNECT:
1297 TriggerAction := taConnect;
1298 ACTION_DISCONNECT:
1299 TriggerAction := taDisconnect;
1300 ACTION_TRANS_START:
1301 TriggerAction := taTransactionStart;
1302 ACTION_TRANS_COMMIT:
1303 TriggerAction := taTransactionCommit;
1304 ACTION_TRANS_ROLLBACK:
1305 TriggerAction := taTransactionRollback;
1306 ACTION_DDL:
1307 TriggerAction := taDDL;
1308 else
1309 FBUDRError(ibxeUnknownTransactionAction,[action]);
1310 end;
1311
1312 try
1313 case FRoutineMetadata.getTriggerType of
1314 ttBefore:
1315 begin
1316 if FRoutineMetadata.HasTriggerMetadata then
1317 begin
1318 if TriggerAction in [taUpdate, taDelete] then
1319 SetUpOldParams;
1320 if TriggerAction in [taInsert,taUpdate] then
1321 SetupWritableNewParams;
1322 end;
1323 BeforeTrigger(FBContext,aTriggerMetadata,TriggerAction,OldParams,NewWritableParams);
1324 WritableParamsSQLDA.Finalise;
1325 if [loLogTriggers,loDetails] <= FBUDRControllerOptions.LogOptions then
1326 FController.WriteToLog(STriggerNewAfter,NewWritableParams);
1327 end;
1328 ttAfter:
1329 begin
1330 if FRoutineMetadata.HasTriggerMetadata then
1331 begin
1332 if TriggerAction in [taUpdate, taDelete] then
1333 SetUpOldParams;
1334 if TriggerAction in [taInsert,taUpdate] then
1335 SetupNewParams;
1336 AfterTrigger(FBContext,aTriggerMetadata,TriggerAction,OldParams,NewParams);
1337 end;
1338 end;
1339 ttDatabase:
1340 begin
1341 DatabaseTrigger(FBContext,aTriggerMetadata);
1342 end;
1343 end;
1344 finally
1345 NewParams := nil;
1346 OldParams := nil;
1347 NewWritableParams := nil;
1348 if OldParamsSQLDA <> nil then
1349 OldParamsSQLDA.Free;
1350 if NewParamsSQLDA <> nil then
1351 NewParamsSQLDA.Free;
1352 if WritableParamsSQLDA <> nil then
1353 WritableParamsSQLDA.Free;
1354 end;
1355 end;
1356 except on E: Exception do
1357 FController.FBSetStatusFromException(E,status);
1358 end;
1359 end;
1360
1361 const
1362 sGetProcArgsSQL =
1363 'SELECT Trim(RDB$PARAMETER_NAME) ' +
1364 ' FROM RDB$PROCEDURE_PARAMETERS PRM JOIN RDB$FIELDS FLD ON ' +
1365 ' PRM.RDB$FIELD_SOURCE = FLD.RDB$FIELD_NAME ' +
1366 'WHERE ' +
1367 ' Trim(PRM.RDB$PROCEDURE_NAME) = ? AND ' +
1368 ' PRM.RDB$PARAMETER_TYPE = ? ' +
1369 'ORDER BY PRM.RDB$PARAMETER_NUMBER';
1370
1371
1372 { TFBUDRProcedureFactory }
1373
1374 procedure TFBUDRProcedureFactory.UpdateArgNames(att: IAttachment;
1375 aProcName: AnsiString);
1376
1377 procedure UpdateFieldNames(paramType: integer; list: TStrings);
1378 var FieldNames: IResultSet;
1379 begin
1380 list.Clear;
1381 if aProcName = '' then
1382 Exit;
1383 FieldNames := att.OpenCursorAtStart(sGetProcArgsSQL,[aProcName,paramType]);
1384 while not FieldNames.IsEOF do
1385 begin
1386 list.Add(FieldNames[0].AsString);
1387 FieldNames.FetchNext;
1388 end;
1389 end;
1390
1391 begin
1392 UpdateFieldNames(0,FInArgNames);
1393 UpdateFieldNames(1,FOutArgNames);
1394 end;
1395
1396 procedure TFBUDRProcedureFactory.SetController(AValue: TFBUDRController);
1397 begin
1398 if FController = AValue then Exit;
1399 FController := AValue;
1400 end;
1401
1402 constructor TFBUDRProcedureFactory.Create(aName: AnsiString;
1403 aProcedure: TFBUDRProcedureClass);
1404 begin
1405 inherited Create;
1406 FName := aName;
1407 FProcedure := aProcedure;
1408 FInArgNames := TStringList.Create;
1409 FOutArgNames := TStringList.Create;
1410 end;
1411
1412 destructor TFBUDRProcedureFactory.Destroy;
1413 begin
1414 if FInArgNames <> nil then
1415 FInArgNames.Free;
1416 if FOutArgNames <> nil then
1417 FOutArgNames.Free;
1418 inherited Destroy;
1419 end;
1420
1421 procedure TFBUDRProcedureFactory.dispose();
1422 begin
1423 Free;
1424 end;
1425
1426 procedure TFBUDRProcedureFactory.setup(status: Firebird.IStatus;
1427 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata;
1428 inBuilder: Firebird.IMetadataBuilder; outBuilder: Firebird.IMetadataBuilder);
1429 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1430 FBInBuilder: IFBUDRMetadataBuilder;
1431 FBOutBuilder: IFBUDRMetadataBuilder;
1432 FBContext: IFBUDRExternalContext;
1433 begin
1434 try
1435 if loLogProcedures in FBUDRControllerOptions.LogOptions then
1436 FController.WriteToLog(SProcSetup + FName);
1437
1438 FBInBuilder := nil;
1439 FBOutBuilder := nil;
1440 FBContext := TFBUDRExternalContext.Create(Controller,context);
1441 FController.StartJournaling(FBContext);
1442
1443 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1444 if inBuilder <> nil then
1445 FBInBuilder := TFBUDRMetadataBuilder.Create(FBContext,inBuilder);
1446 if outBuilder <> nil then
1447 FBOutBuilder := TFBUDRMetadataBuilder.Create(FBContext,outBuilder);
1448 if [loLogProcedures, loDetails] <= FBUDRControllerOptions.LogOptions then
1449 FController.WriteToLog(SRoutineMetadata + LineEnding + (FBRoutineMetadata as TFBUDRRoutineMetadata).AsText);
1450 TFBUDRProcedure.setup(FBContext,FBRoutineMetadata,FBInBuilder,FBOutBuilder)
1451 except on E: Exception do
1452 FController.FBSetStatusFromException(E,status);
1453 end;
1454 end;
1455
1456 function TFBUDRProcedureFactory.newItem(status: Firebird.IStatus;
1457 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata
1458 ): IExternalProcedure;
1459 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1460 FBContext: IFBUDRExternalContext;
1461 begin
1462 try
1463 FBContext := TFBUDRExternalContext.Create(Controller,context);
1464 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1465 {Now get the Field Names}
1466 UpdateArgNames(FBContext.GetAttachment,FBRoutineMetadata.getName);
1467 Result := FProcedure.Create(FController,FName,FBRoutineMetadata,
1468 FInArgNames,FOutArgNames);
1469 except on E: Exception do
1470 FController.FBSetStatusFromException(E,status);
1471 end;
1472 end;
1473
1474 { TFBUDRExternalResultsSet }
1475
1476 procedure TFBUDRExternalResultsSet.SetFieldNames(SQLDA: TSQLDataArea);
1477 var i: integer;
1478 begin
1479 for i := 0 to FOutArgNames.Count - 1 do
1480 if i < SQLDA.Count then
1481 SQLDA[i].Name := FOutArgNames[i];
1482 end;
1483
1484 procedure TFBUDRExternalResultsSet.Close;
1485 begin
1486 //do nothing by default
1487 end;
1488
1489 constructor TFBUDRExternalResultsSet.Create(UDRProcedure: TFBUDRProcedure;
1490 context: IFBUDRExternalContext; aOutArgNames: TStrings;
1491 metadata: Firebird.IMessageMetadata; outMsg: pointer);
1492 begin
1493 inherited Create;
1494 FUDRProcedure := UDRProcedure;
1495 FOutArgNames := aOutArgNames;
1496 Inc(FUDRProcedure.FRefCount);
1497 if metadata <> nil then
1498 begin
1499 FOutputDataSQLDA := TFBUDROutParamsSQLDA.Create(context,metadata,outMsg);
1500 SetFieldNames(FOutputDataSQLDA);
1501 FOutputData := TFBUDROutputParams.Create(FOutputDataSQLDA);
1502 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
1503 FUDRProcedure.FController.WriteToLog(SOutputParams,FOutputData);
1504 end;
1505 end;
1506
1507 destructor TFBUDRExternalResultsSet.Destroy;
1508 begin
1509 FOutputData := nil;
1510 if FOutputDataSQLDA <> nil then
1511 FOutputDataSQLDA.Free;
1512 inherited Destroy;
1513 end;
1514
1515 procedure TFBUDRExternalResultsSet.dispose();
1516 begin
1517 Close;
1518 FUDRProcedure.dispose;
1519 Free;
1520 end;
1521
1522 { TFBUDRProcedure }
1523
1524 procedure TFBUDRProcedure.SetFieldNames(SQLDA: TSQLDataArea);
1525 var i: integer;
1526 begin
1527 for i := 0 to FInArgNames.Count - 1 do
1528 if i < SQLDA.Count then
1529 SQLDA[i].Name := FInArgNames[i];
1530 end;
1531
1532 constructor TFBUDRProcedure.Create(aController: TFBUDRController;
1533 aName: AnsiString; routineMetadata: IFBUDRRoutineMetadata; aInArgNames,
1534 aOutArgNames: TStrings);
1535 begin
1536 inherited Create;
1537 FController := aController;
1538 FName := aName;
1539 FRefCount := 1;
1540 FRoutineMetaData := routineMetadata;
1541 FInArgNames := aInArgNames;
1542 FOutArgNames := aOutArgNames;
1543 end;
1544
1545 function TFBUDRProcedure.getCharSet(context: IFBUDRExternalContext): AnsiString;
1546 begin
1547 Result := '';
1548 end;
1549
1550 class procedure TFBUDRProcedure.setup(context: IFBUDRExternalContext;
1551 metadata: IFBUDRRoutineMetadata; inBuilder: IFBUDRMetadataBuilder;
1552 outBuilder: IFBUDRMetadataBuilder);
1553 begin
1554 //Override in subclass
1555 end;
1556
1557 procedure TFBUDRProcedure.dispose();
1558 begin
1559 if loLogProcedures in FBUDRControllerOptions.LogOptions then
1560 FController.WriteToLog(Format(SProcDispose,[FName,FRefCount]));
1561
1562 Dec(FRefCount);
1563 if FRefCount = 0 then Free;
1564 end;
1565
1566 procedure TFBUDRProcedure.getCharSet(status: Firebird.IStatus;
1567 context: Firebird.IExternalContext; name: PAnsiChar; nameSize: Cardinal);
1568 var charset: AnsiString;
1569 FBContext: IFBUDRExternalContext;
1570 begin
1571 try
1572 FBContext := TFBUDRExternalContext.Create(Controller,context);
1573 charset := getCharSet(FBContext);
1574 if charset <> '' then
1575 begin
1576 StrPLCopy(name,charset,nameSize);
1577 if loLogProcedures in FBUDRControllerOptions.LogOptions then
1578 FController.WriteToLog(Format(SProcCharset,[FName,charset]));
1579 end;
1580 except on E: Exception do
1581 FController.FBSetStatusFromException(E,status);
1582 end;
1583 end;
1584
1585 { TFBUDRInParamsSQLDA }
1586
1587 procedure TFBUDRInParamsSQLDA.AllocMessageBuffer(len: integer);
1588 begin
1589 FMessageBuffer := FBuffer;
1590 FMsgLength := len;
1591 end;
1592
1593 procedure TFBUDRInParamsSQLDA.FreeMessageBuffer;
1594 begin
1595 FBuffer := nil;
1596 FMsgLength := 0;
1597 end;
1598
1599 function TFBUDRInParamsSQLDA.GetAttachment: IAttachment;
1600 begin
1601 Result := FAttachment;
1602 end;
1603
1604 function TFBUDRInParamsSQLDA.GetTransaction: ITransaction;
1605 begin
1606 Result := FTransaction;
1607 end;
1608
1609 constructor TFBUDRInParamsSQLDA.Create(context: IFBUDRExternalContext;
1610 aMetadata: Firebird.IMessageMetaData; aBuffer: PByte);
1611 begin
1612 inherited Create(context.GetFirebirdAPI);
1613 FAttachment := context.GetAttachment;
1614 FTransaction := context.GetTransaction;
1615 FBuffer := aBuffer;
1616 Bind(aMetaData);
1617 end;
1618
1619 { TFBUDROutParamsSQLDA }
1620
1621 procedure TFBUDROutParamsSQLDA.AllocMessageBuffer(len: integer);
1622 begin
1623 FillChar(FBuffer^,len,0);
1624 FMessageBuffer := FBuffer;
1625 FMsgLength := len;
1626 end;
1627
1628 procedure TFBUDROutParamsSQLDA.FreeMessageBuffer;
1629 begin
1630 FMessageBuffer := nil;
1631 FMsgLength := 0;
1632 end;
1633
1634 function TFBUDROutParamsSQLDA.GetAttachment: IAttachment;
1635 begin
1636 Result := FAttachment;
1637 end;
1638
1639 function TFBUDROutParamsSQLDA.GetTransaction: ITransaction;
1640 begin
1641 Result := FTransaction;
1642 end;
1643
1644 constructor TFBUDROutParamsSQLDA.Create(context: IFBUDRExternalContext;
1645 aMetadata: Firebird.IMessageMetaData; aBuffer: PByte);
1646 begin
1647 inherited Create(context.GetFirebirdAPI);
1648 FAttachment := context.GetAttachment;
1649 FTransaction := context.GetTransaction;
1650 FBuffer := aBuffer;
1651 Bind(aMetadata);
1652 end;
1653
1654 function TFBUDROutParamsSQLDA.CanChangeMetaData: boolean;
1655 begin
1656 Result := false;
1657 end;
1658
1659 procedure TFBUDROutParamsSQLDA.Finalise;
1660 begin
1661 PackBuffer;
1662 end;
1663
1664 { TFBUDRFunction }
1665
1666 procedure TFBUDRFunction.SetFieldNames(SQLDA: TFBUDRInParamsSQLDA);
1667 var i: integer;
1668 begin
1669 for i := 0 to FFieldNames.Count - 1 do
1670 if i < SQLDA.Count then
1671 SQLDA[i].Name := FFieldNames[i];
1672 end;
1673
1674 constructor TFBUDRFunction.Create(aController: TFBUDRController;
1675 aName: AnsiString; routineMetadata: IFBUDRRoutineMetadata;
1676 aFieldNames: TStrings);
1677 begin
1678 inherited Create;
1679 FController := aController;
1680 FName := aName;
1681 FFieldNames := aFieldNames;
1682 FRoutineMetaData := routineMetadata;
1683 if loLogFunctions in FBUDRControllerOptions.LogOptions then
1684 begin
1685 FController.WriteToLog(Format(sFuncCreated,[aName]));
1686 if loDetails in FBUDRControllerOptions.LogOptions then
1687 FController.WriteToLog((FRoutineMetaData as TFBUDRRoutineMetadata).AsText);
1688 end;
1689 end;
1690
1691 function TFBUDRFunction.getCharSet(context: IFBUDRExternalContext): AnsiString;
1692 begin
1693 Result := '';
1694 end;
1695
1696 function TFBUDRFunction.Execute(context: IFBUDRExternalContext;
1697 ProcMetadata: IFBUDRProcMetadata; InputParams: IFBUDRInputParams;
1698 ResultSQLType: cardinal): variant;
1699 begin
1700 raise Exception.CreateFmt(SFuncNotOverridden,[FName]);
1701 end;
1702
1703 procedure TFBUDRFunction.Execute(context: IFBUDRExternalContext;
1704 ProcMetadata: IFBUDRProcMetadata; InputParams: IFBUDRInputParams;
1705 ReturnValue: ISQLParam);
1706 begin
1707 ReturnValue.AsVariant := Execute(context,ProcMetadata,InputParams,ReturnValue.GetSQLType);
1708 end;
1709
1710 class procedure TFBUDRFunction.setup(context: IFBUDRExternalContext;
1711 metadata: IFBUDRRoutineMetadata; inBuilder: IFBUDRMetadataBuilder;
1712 outBuilder: IFBUDRMetadataBuilder);
1713 begin
1714 //Do nothing be default
1715 end;
1716
1717 procedure TFBUDRFunction.dispose();
1718 begin
1719 if loLogFunctions in FBUDRControllerOptions.LogOptions then
1720 FController.WriteToLog(Format(SFuncDispose,[FName]));
1721
1722 Free;
1723 end;
1724
1725 procedure TFBUDRFunction.getCharSet(status: Firebird.IStatus;
1726 context: Firebird.IExternalContext; name: PAnsiChar; nameSize: Cardinal);
1727 var charset: AnsiString;
1728 FBContext: IFBUDRExternalContext;
1729 begin
1730 try
1731 FBContext := TFBUDRExternalContext.Create(Controller,context);
1732 charset := getCharSet(FBContext);
1733 if charset <> '' then
1734 begin
1735 StrPLCopy(name,charset,nameSize);
1736 if loLogFunctions in FBUDRControllerOptions.LogOptions then
1737 FController.WriteToLog(Format(SFuncCharset,[FName,charset]));
1738 end;
1739 except on E: Exception do
1740 FController.FBSetStatusFromException(E,status);
1741 end;
1742 end;
1743
1744 procedure TFBUDRFunction.execute(status: Firebird.IStatus;
1745 context: Firebird.IExternalContext; inMsg: Pointer; outMsg: Pointer);
1746 var aProcMetadata: IFBUDRProcMetadata;
1747 OutParamsSQLDA: TFBUDROutParamsSQLDA;
1748 InParamsSQLDA: TFBUDRInParamsSQLDA;
1749 InputParams: IFBUDRInputParams;
1750 OutputData: IFBUDROutputData;
1751 metadata: Firebird.IMessageMetadata;
1752 FBContext: IFBUDRExternalContext;
1753 begin
1754 try
1755 if loLogFunctions in FBUDRControllerOptions.LogOptions then
1756 FController.WriteToLog(SFuncExecute + FName);
1757
1758 OutParamsSQLDA := nil;
1759 InParamsSQLDA := nil;
1760 InputParams := nil;
1761 OutputData := nil;
1762 if FRoutineMetadata.QueryInterface(IFBUDRProcMetadata,aProcMetadata) <> S_OK then
1763 FBUDRError(ibxeNoProcMetadata,[nil])
1764 else
1765 begin
1766 FBContext := TFBUDRExternalContext.Create(Controller,context);
1767 if [loLogFunctions,loDetails] <= FBUDRControllerOptions.LogOptions then
1768 FController.WriteToLog((FBContext as TFBUDRExternalContext).AsText);
1769
1770 try
1771 if FRoutineMetadata.HasInputMetadata then
1772 begin
1773 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getInputMetadata;
1774 try
1775 InParamsSQLDA := TFBUDRInParamsSQLDA.Create(FBContext,
1776 metadata,
1777 inMsg);
1778 SetFieldNames(InParamsSQLDA);
1779 finally
1780 metadata.release;
1781 end;
1782
1783 InputParams := TFBUDRInputParams.Create(InParamsSQLDA);
1784 if [loLogFunctions,loDetails] <= FBUDRControllerOptions.LogOptions then
1785 FController.WriteToLog(SInputParams,InputParams);
1786 end;
1787
1788 if FRoutineMetadata.HasOutputMetadata then
1789 begin
1790 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getOutputMetadata;
1791 try
1792 OutParamsSQLDA := TFBUDROutParamsSQLDA.Create(FBContext,
1793 metadata,
1794 outMsg);
1795 finally
1796 metadata.release;
1797 end;
1798 OutputData := TFBUDROutputParams.Create(OutParamsSQLDA);
1799 end
1800 else
1801 raise Exception.CreateFmt(SNoReturnValue,[FName]);
1802
1803 Execute(FBContext,aProcMetadata,InputParams,OutputData[0]);
1804
1805 if [loLogFunctions,loDetails] <= FBUDRControllerOptions.LogOptions then
1806 FController.WriteToLog(SOutputData,OutputData);
1807
1808 OutParamsSQLDA.Finalise; {copy result to OutMsg buffer}
1809 finally
1810 OutputData := nil;
1811 InputParams := nil;
1812 if OutParamsSQLDA <> nil then
1813 OutParamsSQLDA.Free;
1814 if InParamsSQLDA <> nil then
1815 InParamsSQLDA.Free;
1816 end;
1817 end;
1818 except on E: Exception do
1819 FController.FBSetStatusFromException(E,status);
1820 end;
1821 end;
1822
1823 { TFBUDRFunctionFactory }
1824
1825 const
1826 FunctionArgsSQL =
1827 'SELECT Trim(RDB$ARGUMENT_NAME) FROM RDB$FUNCTION_ARGUMENTS RFA JOIN RDB$FIELDS FLD ' +
1828 'ON RFA.RDB$FIELD_SOURCE = FLD.RDB$FIELD_NAME '+
1829 'WHERE RDB$FUNCTION_NAME = ? AND RDB$ARGUMENT_POSITION > 0' +
1830 'ORDER BY RDB$ARGUMENT_POSITION';
1831
1832 procedure TFBUDRFunctionFactory.UpdateFieldNames(att: IAttachment;
1833 aFunctionName: AnsiString);
1834 var FieldNames: IResultSet;
1835 begin
1836 FFieldNames.Clear;
1837 if aFunctionName = '' then
1838 Exit;
1839 FieldNames := att.OpenCursorAtStart(FunctionArgsSQL,[aFunctionName]);
1840 while not FieldNames.IsEOF do
1841 begin
1842 FFieldNames.Add(FieldNames[0].AsString);
1843 FieldNames.FetchNext;
1844 end;
1845 end;
1846
1847 procedure TFBUDRFunctionFactory.SetController(AValue: TFBUDRController);
1848 begin
1849 if FController = AValue then Exit;
1850 FController := AValue;
1851 end;
1852
1853 constructor TFBUDRFunctionFactory.Create(aName: AnsiString;
1854 aFunction: TFBUDRFunctionClass);
1855 begin
1856 inherited Create;
1857 FName := aName;
1858 FFunction := aFunction;
1859 FFieldNames := TStringList.Create;
1860 end;
1861
1862 destructor TFBUDRFunctionFactory.Destroy;
1863 begin
1864 if FFieldNames <> nil then
1865 FFieldNames.Free;
1866 inherited Destroy;
1867 end;
1868
1869 procedure TFBUDRFunctionFactory.dispose();
1870 begin
1871 Free;
1872 end;
1873
1874 procedure TFBUDRFunctionFactory.setup(status: Firebird.IStatus;
1875 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata;
1876 inBuilder: Firebird.IMetadataBuilder; outBuilder: Firebird.IMetadataBuilder);
1877 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1878 FBInBuilder: IFBUDRMetadataBuilder;
1879 FBOutBuilder: IFBUDRMetadataBuilder;
1880 FBContext: IFBUDRExternalContext;
1881 begin
1882 FBInBuilder := nil;
1883 FBOutBuilder := nil;
1884 try
1885 if loLogFunctions in FBUDRControllerOptions.LogOptions then
1886 FController.WriteToLog(SFuncSetup + FName);
1887
1888 FBContext := TFBUDRExternalContext.Create(Controller,context);
1889
1890 FController.StartJournaling(FBContext);
1891
1892 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1893
1894 if inBuilder <> nil then
1895 FBInBuilder := TFBUDRMetadataBuilder.Create(FBContext,inBuilder);
1896 if outBuilder <> nil then
1897 FBOutBuilder := TFBUDRMetadataBuilder.Create(FBContext,outBuilder);
1898 if [loLogFunctions, loDetails] <= FBUDRControllerOptions.LogOptions then
1899 FController.WriteToLog(SRoutineMetadata + LineEnding + (FBRoutineMetadata as TFBUDRRoutineMetadata).AsText);
1900
1901 TFBUDRFunction.setup(FBContext,FBRoutineMetadata,FBInBuilder,FBOutBuilder)
1902 except on E: Exception do
1903 FController.FBSetStatusFromException(E,status);
1904 end;
1905 end;
1906
1907 function TFBUDRFunctionFactory.newItem(status: Firebird.IStatus;
1908 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata
1909 ): Firebird.IExternalFunction;
1910 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1911 FBContext: IFBUDRExternalContext;
1912 begin
1913 try
1914 FBContext := TFBUDRExternalContext.Create(Controller,context);
1915 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1916 {Now get the argument Names}
1917 UpdateFieldNames(FBContext.GetAttachment,FBRoutineMetadata.getName);
1918 Result := FFunction.Create(FController,FName,FBRoutineMetadata,FFieldNames);
1919 except on E: Exception do
1920 FController.FBSetStatusFromException(E,status);
1921 end;
1922 end;
1923
1924 { TFBUDRController }
1925
1926 function TFBUDRController.GetDateTimeFmt: AnsiString;
1927 begin
1928 {$IF declared(DefaultFormatSettings)}
1929 with DefaultFormatSettings do
1930 {$ELSE}
1931 {$IF declared(FormatSettings)}
1932 with FormatSettings do
1933 {$IFEND}
1934 {$IFEND}
1935 Result := ShortDateFormat + ' ' + LongTimeFormat + '.zzzz'
1936 end;
1937
1938 function TFBUDRController.ProcessTemplateMacros(aTemplate: AnsiString
1939 ): AnsiString;
1940
1941 function CleanDirName(aDirName: PAnsiChar): AnsiString;
1942 begin
1943 Result := Trim(strpas(aDirName));
1944 {$IFDEF WINDOWS}
1945 Result := StringReplace(Result,'/',DirectorySeparator,[rfReplaceAll]);
1946 {$ELSE}
1947 Result := StringReplace(Result,'\',DirectorySeparator,[rfReplaceAll]);
1948 {$ENDIF}
1949 if (Length(Result) > 0) and (Result[Length(aDirName)] <> DirectorySeparator) then
1950 Result := Result + DirectorySeparator;
1951 end;
1952
1953 var udr_config: Firebird.IConfig;
1954 config_entry: Firebird.IConfigEntry;
1955 aStatus: Firebird.IStatus;
1956 begin
1957 if assigned(FMaster) then
1958 with FMaster.getConfigManager do
1959 begin
1960 Result := StringReplace(aTemplate,'$LOGDIR',CleanDirName(getDirectory(DIR_LOG)),[rfReplaceAll, rfIgnoreCase]);
1961 udr_config := getPluginConfig('UDR');
1962 if udr_config <> nil then
1963 try
1964 aStatus := FMaster.getStatus;
1965 try
1966 config_entry := udr_config.find(aStatus,'path');
1967 with aStatus do
1968 if (getState and STATE_ERRORS) <> 0 then
1969 raise EFBUDRException.Create(aStatus);
1970 finally
1971 aStatus.dispose;
1972 end;
1973
1974 if config_entry <> nil then
1975 try
1976 with config_entry do
1977 Result := StringReplace(Result,'$UDRDIR',CleanDirName(config_entry.getValue),[rfReplaceAll, rfIgnoreCase]);
1978 finally
1979 config_entry.release;
1980 end;
1981 finally
1982 udr_config.release();
1983 end;
1984 end;
1985 Result := StringReplace(Result,'$TEMP',GetTempDir,[rfReplaceAll, rfIgnoreCase]);
1986 Result := StringReplace(Result,'$MODULE',FBUDRControllerOptions.ModuleName,[rfReplaceAll, rfIgnoreCase]);
1987 Result := StringReplace(Result,'$TIMESTAMP',FormatDateTime('yyyymmddhhnnss',Now),[rfReplaceAll, rfIgnoreCase]);
1988 end;
1989
1990 procedure TFBUDRController.RegisterUDRFactories(status: Firebird.IStatus;
1991 udrPlugin: Firebird.IUdrPlugin);
1992 var i: integer;
1993 begin
1994 if FUDRFactoryList <> nil then
1995 for i := 0 to FUDRFactoryList.Count - 1 do
1996 try
1997 RegisterUDRFactory(status,udrPlugin,FUDRFactoryList[i], FUDRFactoryList.Objects[i]);
1998 with status do
1999 if (getState and STATE_ERRORS) <> 0 then break;
2000 except on E: Exception do
2001 FBSetStatusFromException(E,status);
2002 end;
2003 end;
2004
2005 procedure TFBUDRController.RegisterUDRFactory(status: Firebird.IStatus;
2006 udrPlugin: Firebird.IUdrPlugin; aName: AnsiString; factory: TObject);
2007 begin
2008 if factory is TFBUDRFunctionFactory then
2009 begin
2010 if loLogFunctions in FBUDRControllerOptions.LogOptions then
2011 WriteToLog(SFuncRegister + aName);
2012 udrPlugin.registerFunction(status,PAnsiChar(aName),Firebird.IUdrFunctionFactory(factory));
2013 TFBUDRFunctionFactory(factory).Controller := self;
2014 end
2015 else
2016 if factory is TFBUDRProcedureFactory then
2017 begin
2018 if loLogProcedures in FBUDRControllerOptions.LogOptions then
2019 WriteToLog(SProcRegister + aName);
2020 udrPlugin.registerProcedure(status,PAnsiChar(aName),Firebird.IUdrProcedureFactory(factory));
2021 TFBUDRProcedureFactory(factory).Controller := self;
2022 end
2023 else
2024 if factory is TFBUDRTriggerFactory then
2025 begin
2026 if loLogTriggers in FBUDRControllerOptions.LogOptions then
2027 WriteToLog(STriggerRegister + aName);
2028 udrPlugin.registerTrigger(status,PAnsiChar(aName),Firebird.IUdrTriggerFactory(factory));
2029 TFBUDRTriggerFactory(factory).Controller := self;
2030 end
2031 else
2032 FBUDRError(ibxeInvalidFactoryObject,[factory.ClassName,aName]);
2033 end;
2034
2035 procedure TFBUDRController.FreeFactoryList;
2036 //var i: integer;
2037 begin
2038 {if FUDRFactoryList <> nil then
2039 for i := 0 to FUDRFactoryList.Count - 1 do
2040 if FUDRFactoryList.Objects[i] <> nil then
2041 FUDRFactoryList.Objects[i].Free;} {disposed of by Firebird Engine}
2042 FreeAndNil(FUDRFactoryList);
2043 end;
2044
2045 const
2046 LogOptionsTable: array [TFBUDRControllerLogOption] of AnsiString = (
2047 'loLogFunctions',
2048 'loLogProcedures',
2049 'loLogTriggers',
2050 'loLogFetches',
2051 'loModifyQueries',
2052 'loReadOnlyQueries',
2053 'loDetails'
2054 );
2055
2056 procedure TFBUDRController.LoadConfig;
2057
2058 function GetLogOptions(LogOptionsStr: AnsiString; var aLogOptions: TFBUDRControllerLogOptions): boolean;
2059 var s: AnsiString;
2060 p1, p2, len: integer;
2061 i: TFBUDRControllerLogOption;
2062 found: boolean;
2063 begin
2064 Result := LogOptionsStr <> '';
2065 if Result then
2066 begin
2067 aLogOptions := [];
2068 p2 := 1;
2069 {skip past opening square bracket}
2070 while (p2 <= length(LogOptionsStr)) and (LogOptionsStr[p2] <> '[') do
2071 Inc(p2);
2072
2073 {parse into words separated by commas}
2074 Inc(p2);
2075 p1 := p2;
2076 while p2 <= length(LogOptionsStr) do
2077 begin
2078 if LogOptionsStr[p2] in [',',']'] then
2079 begin
2080 s := Trim(system.copy(LogOptionsStr,p1,p2-p1));
2081
2082 {Now convert string to LogOption}
2083 found := false;
2084 for i := low(TFBUDRControllerLogOption) to high(TFBUDRControllerLogOption) do
2085 if CompareText(s,LogOptionsTable[i]) = 0 then
2086 begin
2087 aLogOptions := aLogOptions + [i];
2088 found := true;
2089 break;
2090 end;
2091 if not found then
2092 WriteToLog(Format(SBadLogOptionsStr,[LogOptionsStr,p2]));
2093 if LogOptionsStr[p2] = ']' then
2094 break;
2095 p1 := p2 + 1;
2096 end;
2097 Inc(p2);
2098 end;
2099 if p2 > length(LogOptionsStr) then
2100 WriteToLog(Format(SBadLogOptionsStr,[LogOptionsStr,p2]));
2101 Result := true;
2102 end;
2103 end;
2104
2105 var aLogOptions: TFBUDRControllerLogOptions;
2106 aConfigFileName: Ansistring;
2107 begin
2108 aConfigFileName := ProcessTemplateMacros(FBUDRControllerOptions.ConfigFileNameTemplate);
2109 if (FConfigFile = nil) and (aConfigFileName <> '') and FileExists(aConfigFileName) then
2110 begin
2111 FConfigFile := TIniFile.Create(aConfigFileName);
2112 {$if declared(TStringArray)}
2113 FConfigFile.BoolFalseStrings := FalseStrings;
2114 FConfigFile.BoolTrueStrings := TrueStrings;
2115 {$ifend}
2116 WriteToLog(Format(SReadingConfigFile,[aConfigFileName]));
2117 with FBUDRControllerOptions do
2118 if AllowConfigFileOverrides then
2119 begin
2120 LogFileNameTemplate := FConfigFile.ReadString('Controller','LogFileNameTemplate',LogFileNameTemplate);
2121 WriteToLog('LogFileNameTemplate = ' + LogFileNameTemplate);
2122 ForceWriteLogEntries := FConfigFile.ReadBool('Controller','ForceWriteLogEntries',ForceWriteLogEntries);
2123 WriteToLog('ForceWriteLogEntries = ' + BooleanToStr(ForceWriteLogEntries ,'true','false'));
2124 ThreadSafeLogging := FConfigFile.ReadBool('Controller','ThreadSafeLogging',ThreadSafeLogging);
2125 WriteToLog('ThreadSafeLogging = ' + BooleanToStr(ThreadSafeLogging,'true','false'));
2126 if GetLogOptions( FConfigFile.ReadString('Controller','LogOptions',''),aLogOptions) then
2127 LogOptions := aLogOptions;
2128 WriteToLog('LogOptions = ' + LogOptionsToStr(LogOptions));
2129 end;
2130 end;
2131 end;
2132
2133 function TFBUDRController.NeedLogStream: boolean;
2134 var FilePathName: AnsiString;
2135 begin
2136 Result := false;
2137 if FLogStream = nil then
2138 begin
2139 FilePathName := ProcessTemplateMacros(FBUDRControllerOptions.LogFileNameTemplate);
2140 if FilePathName = '' then
2141 Exit;
2142 if FJnlOpenAppend then
2143 begin
2144 FLogStream := TFileStream.Create(FilePathName,fmOpenWrite or fmShareDenyNone);
2145 FLogStream.Seek(0, soFromEnd);
2146 end
2147 else
2148 begin
2149 FLogStream := TFileStream.Create(FilePathName,fmCreate or fmShareDenyNone);
2150 FJnlOpenAppend := true;
2151 end;
2152 end;
2153 Result := true;
2154 end;
2155
2156 function TFBUDRController.LogOptionsToStr(aLogOptions: TFBUDRControllerLogOptions): AnsiString;
2157 var i: TFBUDRControllerLogOption;
2158 separator: AnsiString;
2159 begin
2160 Result := '[';
2161 separator := '';
2162 for i := low(TFBUDRControllerLogOption) to high(TFBUDRControllerLogOption) do
2163 if i in aLogOptions then
2164 begin
2165 Result := Result + separator + LogOptionsTable[i];
2166 separator := ',';
2167 end;
2168 Result := Result + ']';
2169 end;
2170
2171 constructor TFBUDRController.Create(status: Firebird.IStatus;
2172 udrPlugin: Firebird.IUdrPlugin; aTheirUnloadFlag: booleanPtr;
2173 var aMyUnloadFlag: booleanPtr);
2174 begin
2175 try
2176 inherited Create;
2177 FFBController := self;
2178 FTheirUnloadFlag := aTheirUnloadFlag;
2179 FMyUnloadFlag := false;
2180 aMyUnloadFlag := @FMyUnloadFlag;
2181 FMaster := udrPlugin.getMaster;
2182 FCriticalSection := TCriticalSection.Create;
2183 RegisterUDRFactories(status,udrPlugin);
2184 LoadConfig;
2185 except on E: Exception do
2186 FBSetStatusFromException(E,status);
2187 end;
2188 end;
2189
2190 destructor TFBUDRController.Destroy;
2191 begin
2192 if FConfigFile <> nil then
2193 FConfigFile.Free;
2194 if FLogStream <> nil then
2195 FLogStream.Free;
2196 FreeFactoryList;
2197 if FCriticalSection <> nil then
2198 FCriticalSection.Free;
2199 if (FTheirUnloadFlag <> nil) and not FMyUnloadFlag then
2200 FTheirUnloadFlag^ := true; {notify unload of module}
2201 inherited Destroy;
2202 end;
2203
2204 procedure TFBUDRController.FBSetStatusFromException(E: Exception; aStatus: Firebird.IStatus);
2205 var StatusVector: TStatusVector;
2206 begin
2207 if E is EFBUDRException then
2208 aStatus.setErrors((E as EFBUDRException).Status.getErrors())
2209 else
2210 if E is EIBInterBaseError then
2211 aStatus.setErrors(NativeIntPtr(((E as EIBInterBaseError).Status as TFB30Status).GetStatus.getErrors))
2212 else
2213 begin
2214 FMessageBuffer := E.Message;
2215 StatusVector[0] := isc_arg_gds;
2216 StatusVector[1] := NativeInt(isc_random);
2217 StatusVector[2] := isc_arg_string;
2218 StatusVector[3] := NativeInt(PAnsiChar(FMessageBuffer));
2219 StatusVector[4] := isc_arg_end;
2220 astatus.setErrors(@StatusVector);
2221 end;
2222 WriteToLog(SExceptionRaised + LineEnding + E.Message);
2223 end;
2224
2225 procedure TFBUDRController.WriteToLog(Msg: AnsiString);
2226 var LogEntry: AnsiString;
2227 begin
2228 if not NeedLogStream then
2229 Exit; {no log file available}
2230 LogEntry := Format(sLogFormat,[FBFormatDateTime(GetDateTimeFmt,Now),Msg]) + LineEnding;
2231 if FBUDRControllerOptions.ThreadSafeLogging then
2232 begin
2233 FCriticalSection.Enter;
2234 try
2235 FLogStream.Write(LogEntry[1],Length(LogEntry));
2236 if FBUDRControllerOptions.ForceWriteLogEntries then
2237 FreeAndNil(FLogStream);
2238 finally
2239 FCriticalSection.Leave;
2240 end;
2241 end
2242 else
2243 FLogStream.Write(LogEntry[1],Length(LogEntry));
2244 end;
2245
2246 function TFBUDRController.CharSetIDToText(att: IAttachment; id: integer): AnsiString;
2247 begin
2248 if att = nil then
2249 Result := IntToStr(id)
2250 else
2251 Result := att.GetCharsetName(id);
2252 end;
2253
2254 procedure TFBUDRController.WriteToLog(aTitle: AnsiString; Params: IFBUDRInputParams
2255 );
2256 var i: integer;
2257 Msg: AnsiString;
2258 begin
2259 Msg := aTitle + LineEnding;
2260 for i := 0 to Params.getCount - 1 do
2261 with Params[i] do
2262 begin
2263 Msg := Msg +
2264 'Parameter ' + IntToStr(i) + ':' + NewLineTAB +
2265 'Field Name = ' + getName + NewLineTab +
2266 'Alias Name = ' + getAliasName + NewLineTab +
2267 'SQLType = ' + GetSQLTypeName + NewLineTAB +
2268 'sub type = ' + IntToStr(getSubType) + NewLineTAB +
2269 'Scale = ' + IntToStr(getScale) + NewLineTAB +
2270 'Charset = ' + CharSetIDToText((Params as TFBUDRInputParams).GetAttachment,getCharSetID) + NewLineTAB +
2271 BooleanToStr(getIsNullable,'Nullable','Not Nullable') + NewLineTAB +
2272 'Size = ' + IntToStr(GetSize) + NewLineTAB +
2273 'Value = ' + BooleanToStr(IsNull,'NULL',GetStrValue(Params[i] as TColumnMetaData)) + LineEnding;
2274 end;
2275 WriteToLog(Msg);
2276 end;
2277
2278 function TFBUDRController.GetStrValue(item: TColumnMetaData): Ansistring;
2279
2280 function HexString(s: AnsiString): AnsiString;
2281 var i: integer;
2282 begin
2283 Result := '';
2284 for i := 1 to length(s) do
2285 Result := Result + Format('%x ',[byte(s[i])]);
2286 end;
2287
2288 begin
2289 with Item do
2290 case SQLType of
2291 SQL_ARRAY:
2292 Result := '(array)';
2293 SQL_BLOB:
2294 if getSubtype = 1 then
2295 begin
2296 if GetCharSetID = 1 then
2297 Result := HexString(AsString)
2298 else
2299 Result := AsString;
2300 end
2301 else
2302 Result := '(Blob)';
2303 SQL_TEXT,SQL_VARYING:
2304 if GetCharSetID = 1 then
2305 Result := HexString(AsString)
2306 else
2307 Result := TrimRight(AsString);
2308 else
2309 Result := AsString;
2310 end;
2311 end;
2312
2313 procedure TFBUDRController.WriteToLog(aTitle: AnsiString; OutputData: IFBUDROutputData);
2314 var i: integer;
2315 Msg: AnsiString;
2316 begin
2317 Msg := aTitle + LineEnding;
2318 for i := 0 to OutputData.getCount - 1 do
2319 with OutputData[i] do
2320 begin
2321 Msg := Msg + 'Column ' + IntToStr(i) + NewLineTAB +
2322 'Field Name = ' + getName + NewLineTab +
2323 'SQLType = ' + GetSQLTypeName + NewLineTAB +
2324 'sub type = ' + IntToStr(getSubType) + NewLineTAB +
2325 'Scale = ' + IntToStr(getScale) + NewLineTAB +
2326 'Charset = ' + CharSetIDToText((OutputData as TFBUDROutputParams).GetAttachment,getCharSetID) + NewLineTAB +
2327 BooleanToStr(getIsNullable,'Nullable','Not Nullable') + NewLineTAB +
2328 'Size = ' + IntToStr(GetSize) + NewLineTAB +
2329 'Value = ' + BooleanToStr(IsNull,'NULL', GetStrValue(OutputData[i] as TColumnMetaData)) + LineEnding;
2330 end;
2331 WriteToLog(Msg);
2332 end;
2333
2334 procedure TFBUDRController.StartJournaling(context: IFBUDRExternalContext);
2335 var JnlOptions: TJournalOptions;
2336 begin
2337 JnlOptions := [joNoServerTable];
2338 if loModifyQueries in FBUDRControllerOptions.LogOptions then
2339 JnlOptions := JnlOptions + [joModifyQueries];
2340 if loReadOnlyQueries in FBUDRControllerOptions.LogOptions then
2341 JnlOptions := JnlOptions + [joReadOnlyQueries];
2342 if JnlOptions <> [] then
2343 begin
2344 if NeedLogStream then
2345 context.GetAttachment.StartJournaling(FLogStream,JnlOptions);
2346 end;
2347 end;
2348
2349 function TFBUDRController.HasConfigFile: boolean;
2350 begin
2351 Result := FConfigFile <> nil;
2352 end;
2353
2354 function TFBUDRController.ReadConfigString(Section, Ident,
2355 DefaultValue: AnsiString): AnsiString;
2356 begin
2357 if HasConfigFile then
2358 Result := FConfigFile.ReadString(Section, Ident, DefaultValue)
2359 else
2360 raise Exception.Create(SNoConfigFile);
2361 end;
2362
2363 function TFBUDRController.ReadConfigInteger(Section, Ident: AnsiString;
2364 DefaultValue: integer): integer;
2365 begin
2366 if HasConfigFile then
2367 Result := FConfigFile.ReadInteger(Section, Ident, DefaultValue)
2368 else
2369 raise Exception.Create(SNoConfigFile);
2370 end;
2371
2372 function TFBUDRController.ReadConfigBool(Section, Ident: AnsiString;
2373 DefaultValue: boolean): boolean;
2374 begin
2375 if HasConfigFile then
2376 Result := FConfigFile.ReadBool(Section, Ident, DefaultValue)
2377 else
2378 raise Exception.Create(SNoConfigFile);
2379 end;
2380
2381 end.
2382

Properties

Name Value
svn:eol-style native