ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/udr/source/FBUDRController.pas
Revision: 386
Committed: Tue Jan 18 12:05:35 2022 UTC (2 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 82703 byte(s)
Log Message:
Silent exceptions bug fixed

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 Result := nil;
878 try
879 if loLogProcedures in FBUDRControllerOptions.LogOptions then
880 FController.WriteToLog(SOpenExecuteProc + FName);
881
882 if FRoutineMetadata.QueryInterface(IFBUDRProcMetadata,aProcMetadata) <> S_OK then
883 FBUDRError(ibxeNoProcMetadata,[nil])
884 else
885 begin
886 FBContext := TFBUDRExternalContext.Create(Controller,context);
887 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
888 FController.WriteToLog((FBContext as TFBUDRExternalContext).AsText);
889
890 if FRoutineMetadata.HasInputMetadata then
891 begin
892 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getInputMetadata;
893 try
894 InputParamsSQLDA := TFBUDRInParamsSQLDA.Create(FBContext,
895 metadata,
896 inMsg);
897 SetFieldNames(InputParamsSQLDA);
898 finally
899 metadata.release;
900 end;
901 end;
902
903 try
904 if InputParamsSQLDA <> nil then
905 begin
906 InputParams := TFBUDRInputParams.Create(InputParamsSQLDA);
907 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
908 FController.WriteToLog(SInputParams,InputParams);
909 end;
910
911 metadata := nil;
912 if FRoutineMetadata.HasOutputMetadata then
913 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getOutputMetadata;
914
915 try
916 Result := TFBUDRSingletonRow.Create(self, FBContext, FOutArgNames,
917 metadata,
918 outMsg);
919
920 finally
921 if metadata <> nil then
922 metadata.release;
923 end;
924
925 Execute(FBContext,aProcMetadata,InputParams,
926 (Result as TFBUDRSingletonRow).OutputData);
927 finally
928 InputParams := nil;
929 if InputParamsSQLDA <> nil then
930 InputParamsSQLDA.Free;
931 end;
932
933 end;
934 except on E: Exception do
935 begin
936 if Result <> nil then
937 Result.dispose;
938 Result := nil;
939 FController.FBSetStatusFromException(E,status);
940 end;
941 end;
942 end;
943
944 { TFBUDRSelectProcedure }
945
946 function TFBUDRSelectProcedure.fetch(OutputData: IFBUDROutputData): boolean;
947 begin
948 Result := false;
949 end;
950
951 procedure TFBUDRSelectProcedure.close;
952 begin
953 //override this method to tidy up once all rows have been returned
954 end;
955
956 function TFBUDRSelectProcedure.open(status: Firebird.IStatus;
957 context: Firebird.IExternalContext; inMsg: Pointer; outMsg: Pointer
958 ): Firebird.IExternalResultSet;
959 var aProcMetadata: IFBUDRProcMetadata;
960 InputParamsSQLDA: TFBUDRInParamsSQLDA;
961 InputParams: IFBUDRInputParams;
962 metadata: Firebird.IMessageMetadata;
963 FBContext: IFBUDRExternalContext;
964 begin
965 Result := nil;
966 try
967 if loLogProcedures in FBUDRControllerOptions.LogOptions then
968 FController.WriteToLog(SOpenSelectProc + FName);
969
970 InputParamsSQLDA := nil;
971 InputParams := nil;
972 if FRoutineMetadata.QueryInterface(IFBUDRProcMetadata,aProcMetadata) <> S_OK then
973 FBUDRError(ibxeNoProcMetadata,[nil])
974 else
975 begin
976 FBContext := TFBUDRExternalContext.Create(Controller,context);
977 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
978 FController.WriteToLog((FBContext as TFBUDRExternalContext).AsText);
979
980 if FRoutineMetadata.HasInputMetadata then
981 begin
982 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getInputMetadata;
983 try
984 InputParamsSQLDA := TFBUDRInParamsSQLDA.Create(FBContext,
985 metadata,
986 inMsg);
987 SetFieldNames(InputParamsSQLDA);
988 finally
989 metadata.release;
990 end;
991 end;
992
993 try
994 if InputParamsSQLDA <> nil then
995 begin
996 InputParams := TFBUDRInputParams.Create(InputParamsSQLDA);
997 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
998 FController.WriteToLog(SInputParams,InputParams);
999 end;
1000
1001 metadata := nil;
1002 if FRoutineMetadata.HasOutputMetadata then
1003 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getOutputMetadata;
1004
1005 try
1006 open(FBContext,aProcMetadata,InputParams);
1007 Result := TFBUDRResultsCursor.Create(self, FBContext,
1008 FOutArgNames,
1009 metadata,
1010 outMsg);
1011 finally
1012 if metadata <> nil then
1013 metadata.release;
1014 end;
1015 finally
1016 InputParams := nil;
1017 if InputParamsSQLDA <> nil then
1018 InputParamsSQLDA.Free;
1019 end;
1020 end;
1021 except on E: Exception do
1022 begin
1023 if Result <> nil then
1024 Result.dispose;
1025 Result := nil;
1026 FController.FBSetStatusFromException(E,status);
1027 end;
1028 end;
1029 end;
1030
1031 const
1032 sGetFieldNamesSQL = 'Select Trim(RF.RDB$FIELD_NAME) as ColumnName '+
1033 'FROM RDB$RELATION_FIELDS RF '+
1034 'JOIN RDB$FIELDS B On B.RDB$FIELD_NAME = RF.RDB$FIELD_SOURCE '+
1035 'Where RF.RDB$RELATION_NAME = ? ' +
1036 'order by RF.RDB$FIELD_POSITION asc';
1037
1038 { TFBUDRTriggerFactory }
1039
1040 procedure TFBUDRTriggerFactory.UpdateFieldNames(att: IAttachment;
1041 aTableName: AnsiString);
1042 var FieldNames: IResultSet;
1043 begin
1044 FFieldNames.Clear;
1045 if aTableName = '' then
1046 Exit;
1047 FieldNames := att.OpenCursorAtStart(sGetFieldNamesSQL,[aTableName]);
1048 while not FieldNames.IsEOF do
1049 begin
1050 FFieldNames.Add(FieldNames[0].AsString);
1051 FieldNames.FetchNext;
1052 end;
1053 end;
1054
1055 procedure TFBUDRTriggerFactory.SetController(AValue: TFBUDRController);
1056 begin
1057 if FController = AValue then Exit;
1058 FController := AValue;
1059 end;
1060
1061 constructor TFBUDRTriggerFactory.Create(aName: AnsiString;
1062 aTrigger: TFBUDRTriggerClass);
1063 begin
1064 inherited Create;
1065 FName := aName;
1066 FTrigger := aTrigger;
1067 FFieldNames := TStringList.Create;
1068 end;
1069
1070 destructor TFBUDRTriggerFactory.Destroy;
1071 begin
1072 if FFieldNames <> nil then
1073 FFieldNames.Free;
1074 inherited Destroy;
1075 end;
1076
1077 procedure TFBUDRTriggerFactory.dispose();
1078 begin
1079 Free;
1080 end;
1081
1082 procedure TFBUDRTriggerFactory.setup(status: Firebird.IStatus;
1083 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata;
1084 fieldsBuilder: Firebird.IMetadataBuilder);
1085 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1086 FBFieldsBuilder: IFBUDRMetadataBuilder;
1087 FBContext: IFBUDRExternalContext;
1088 begin
1089 FBFieldsBuilder := nil;
1090 try
1091 if loLogTriggers in FBUDRControllerOptions.LogOptions then
1092 FController.WriteToLog(SSetupTrigger + FName);
1093
1094 FBContext := TFBUDRExternalContext.Create(Controller,context);
1095 FController.StartJournaling(FBContext);
1096
1097 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1098
1099 if fieldsBuilder <> nil then
1100 FBFieldsBuilder := TFBUDRMetadataBuilder.Create(FBContext,fieldsBuilder);
1101 if [loLogTriggers, loDetails] <= FBUDRControllerOptions.LogOptions then
1102 FController.WriteToLog(SRoutineMetadata + LineEnding + (FBRoutineMetadata as TFBUDRRoutineMetadata).AsText);
1103 TFBUDRTrigger.setup(FBContext,FBRoutineMetadata,FBFieldsBuilder)
1104 except on E: Exception do
1105 FController.FBSetStatusFromException(E,status);
1106 end;
1107 end;
1108
1109 function TFBUDRTriggerFactory.newItem(status: Firebird.IStatus;
1110 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata
1111 ): Firebird.IExternalTrigger;
1112 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1113 FBContext: IFBUDRExternalContext;
1114 begin
1115 try
1116 FBContext := TFBUDRExternalContext.Create(Controller,context);
1117 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1118 {Now get the Field Names}
1119 UpdateFieldNames(FBContext.GetAttachment,FBRoutineMetadata.getTriggerTable);
1120 Result := FTrigger.Create(FController,FName,FBRoutineMetadata,FFieldNames);
1121 except on E: Exception do
1122 FController.FBSetStatusFromException(E,status);
1123 end;
1124 end;
1125
1126 { TFBUDRTrigger }
1127
1128 procedure TFBUDRTrigger.SetFieldNames(SQLDA: TSQLDataArea);
1129 var i: integer;
1130 begin
1131 for i := 0 to FFieldNames.Count - 1 do
1132 if i < SQLDA.Count then
1133 SQLDA[i].Name := FFieldNames[i];
1134 end;
1135
1136 constructor TFBUDRTrigger.Create(aController: TFBUDRController;
1137 aName: AnsiString; routineMetadata: IFBUDRRoutineMetadata;
1138 aFieldNames: TStrings);
1139 begin
1140 inherited Create;
1141 FName := aName;
1142 FController := aController;
1143 FRoutineMetaData := routineMetadata;
1144 FFieldNames := aFieldNames;
1145 end;
1146
1147 function TFBUDRTrigger.getCharSet(context: IFBUDRExternalContext): AnsiString;
1148 begin
1149 Result := '';
1150 end;
1151
1152 procedure TFBUDRTrigger.AfterTrigger(context: IFBUDRExternalContext;
1153 TriggerMetaData: IFBUDRTriggerMetaData; action: TFBUDRTriggerAction;
1154 OldParams: IFBUDRInputParams; NewParams: IFBUDRInputParams);
1155 begin
1156 raise Exception.CreateFmt(STriggerIsNotImplemented,[FName]);
1157 end;
1158
1159 procedure TFBUDRTrigger.BeforeTrigger(context: IFBUDRExternalContext;
1160 TriggerMetaData: IFBUDRTriggerMetaData; action: TFBUDRTriggerAction;
1161 OldParams: IFBUDRInputParams; NewParams: IFBUDROutputData);
1162 begin
1163 raise Exception.CreateFmt(STriggerIsNotImplemented,[FName]);
1164 end;
1165
1166 procedure TFBUDRTrigger.DatabaseTrigger(context: IFBUDRExternalContext;
1167 TriggerMetaData: IFBUDRTriggerMetaData);
1168 begin
1169 raise Exception.CreateFmt(STriggerIsNotImplemented,[FName]);
1170 end;
1171
1172 class procedure TFBUDRTrigger.setup(context: IFBUDRExternalContext;
1173 metadata: IFBUDRRoutineMetadata; fieldsBuilder: IFBUDRMetadataBuilder);
1174 begin
1175 //Override in subclass
1176 end;
1177
1178 procedure TFBUDRTrigger.dispose();
1179 begin
1180 if loLogTriggers in FBUDRControllerOptions.LogOptions then
1181 FController.WriteToLog(Format(STriggerDisposed,[FName]));
1182
1183 Free;
1184 end;
1185
1186 procedure TFBUDRTrigger.getCharSet(status: Firebird.IStatus;
1187 context: Firebird.IExternalContext; name: PAnsiChar; nameSize: Cardinal);
1188 var charset: AnsiString;
1189 FBContext: IFBUDRExternalContext;
1190 begin
1191 try
1192 FBContext := TFBUDRExternalContext.Create(Controller,context);
1193 charset := getCharSet(FBContext);
1194 if charset <> '' then
1195 begin
1196 StrPLCopy(name,charset,nameSize);
1197 if loLogTriggers in FBUDRControllerOptions.LogOptions then
1198 FController.WriteToLog(Format(STriggerCharset,[FName,charset]));
1199 end;
1200 except on E: Exception do
1201 FController.FBSetStatusFromException(E,status);
1202 end;
1203 end;
1204
1205 procedure TFBUDRTrigger.execute(status: Firebird.IStatus;
1206 context: Firebird.IExternalContext; action: Cardinal; oldMsg: Pointer;
1207 newMsg: Pointer);
1208 var aTriggerMetadata: IFBUDRTriggerMetaData;
1209 OldParamsSQLDA: TFBUDRInParamsSQLDA;
1210 NewParamsSQLDA: TFBUDRInParamsSQLDA;
1211 WritableParamsSQLDA: TFBUDRTriggerNewValuesSQLDA;
1212 OldParams: IFBUDRInputParams;
1213 NewParams: IFBUDRInputParams;
1214 NewWritableParams: IFBUDROutputData;
1215 TriggerAction: TFBUDRTriggerAction;
1216 FBContext: IFBUDRExternalContext;
1217
1218 procedure SetUpOldParams;
1219 var metadata: Firebird.IMessageMetadata;
1220 begin
1221 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getTriggerMetadata;
1222 try
1223 OldParamsSQLDA := TFBUDRInParamsSQLDA.Create(FBContext,
1224 metadata,
1225 oldMsg);
1226 SetFieldNames(OldParamsSQLDA);
1227 OldParams := TFBUDRInputParams.Create(OldParamsSQLDA);
1228 if [loLogTriggers,loDetails] <= FBUDRControllerOptions.LogOptions then
1229 begin
1230 FController.WriteToLog(STriggerOld,OldParams);
1231 end;
1232 finally
1233 metadata.release;
1234 end;
1235 end;
1236
1237 procedure SetupNewParams;
1238 var metadata: Firebird.IMessageMetadata;
1239 begin
1240 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getTriggerMetadata;
1241 try
1242 NewParamsSQLDA := TFBUDRInParamsSQLDA.Create(FBContext,
1243 metadata,
1244 newMsg);
1245 SetFieldNames(NewParamsSQLDA);
1246 NewParams := TFBUDRInputParams.Create(NewParamsSQLDA);
1247 if [loLogTriggers,loDetails] <= FBUDRControllerOptions.LogOptions then
1248 begin
1249 FController.WriteToLog(STriggerNew,NewParams);
1250 end;
1251 finally
1252 metadata.release;
1253 end;
1254 end;
1255
1256 procedure SetupWritableNewParams;
1257 var metadata: Firebird.IMessageMetadata;
1258 begin
1259 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getTriggerMetadata;
1260 try
1261 WritableParamsSQLDA := TFBUDRTriggerNewValuesSQLDA.Create(FBContext,
1262 metadata,
1263 newMsg);
1264 SetFieldNames(WritableParamsSQLDA);
1265 NewWritableParams := TFBUDROutputParams.Create(WritableParamsSQLDA);
1266 if [loLogTriggers,loDetails] <= FBUDRControllerOptions.LogOptions then
1267 begin
1268 FController.WriteToLog(STriggerNew,NewWritableParams);
1269 end;
1270 finally
1271 metadata.release;
1272 end;
1273 end;
1274
1275 begin
1276 try
1277 if loLogTriggers in FBUDRControllerOptions.LogOptions then
1278 FController.WriteToLog(STriggerExecute + FName);
1279 OldParamsSQLDA := nil;
1280 NewParamsSQLDA := nil;
1281 WritableParamsSQLDA := nil;
1282 OldParams := nil;
1283 NewParams := nil;
1284 NewWritableParams := nil;
1285
1286 if FRoutineMetadata.QueryInterface(IFBUDRTriggerMetaData,aTriggerMetadata) <> S_OK then
1287 FBUDRError(ibxeNoTriggerMetadata,[nil])
1288 else
1289 begin
1290 FBContext := TFBUDRExternalContext.Create(Controller,context);
1291
1292 with Firebird.IExternalTrigger do
1293 case action of
1294 ACTION_INSERT:
1295 TriggerAction := taInsert;
1296 ACTION_UPDATE:
1297 TriggerAction := taUpdate;
1298 ACTION_DELETE:
1299 TriggerAction := taDelete;
1300 ACTION_CONNECT:
1301 TriggerAction := taConnect;
1302 ACTION_DISCONNECT:
1303 TriggerAction := taDisconnect;
1304 ACTION_TRANS_START:
1305 TriggerAction := taTransactionStart;
1306 ACTION_TRANS_COMMIT:
1307 TriggerAction := taTransactionCommit;
1308 ACTION_TRANS_ROLLBACK:
1309 TriggerAction := taTransactionRollback;
1310 ACTION_DDL:
1311 TriggerAction := taDDL;
1312 else
1313 FBUDRError(ibxeUnknownTransactionAction,[action]);
1314 end;
1315
1316 try
1317 case FRoutineMetadata.getTriggerType of
1318 ttBefore:
1319 begin
1320 if FRoutineMetadata.HasTriggerMetadata then
1321 begin
1322 if TriggerAction in [taUpdate, taDelete] then
1323 SetUpOldParams;
1324 if TriggerAction in [taInsert,taUpdate] then
1325 SetupWritableNewParams;
1326 end;
1327 BeforeTrigger(FBContext,aTriggerMetadata,TriggerAction,OldParams,NewWritableParams);
1328 WritableParamsSQLDA.Finalise;
1329 if [loLogTriggers,loDetails] <= FBUDRControllerOptions.LogOptions then
1330 FController.WriteToLog(STriggerNewAfter,NewWritableParams);
1331 end;
1332 ttAfter:
1333 begin
1334 if FRoutineMetadata.HasTriggerMetadata then
1335 begin
1336 if TriggerAction in [taUpdate, taDelete] then
1337 SetUpOldParams;
1338 if TriggerAction in [taInsert,taUpdate] then
1339 SetupNewParams;
1340 AfterTrigger(FBContext,aTriggerMetadata,TriggerAction,OldParams,NewParams);
1341 end;
1342 end;
1343 ttDatabase:
1344 begin
1345 DatabaseTrigger(FBContext,aTriggerMetadata);
1346 end;
1347 end;
1348 finally
1349 NewParams := nil;
1350 OldParams := nil;
1351 NewWritableParams := nil;
1352 if OldParamsSQLDA <> nil then
1353 OldParamsSQLDA.Free;
1354 if NewParamsSQLDA <> nil then
1355 NewParamsSQLDA.Free;
1356 if WritableParamsSQLDA <> nil then
1357 WritableParamsSQLDA.Free;
1358 end;
1359 end;
1360 except on E: Exception do
1361 FController.FBSetStatusFromException(E,status);
1362 end;
1363 end;
1364
1365 const
1366 sGetProcArgsSQL =
1367 'SELECT Trim(RDB$PARAMETER_NAME) ' +
1368 ' FROM RDB$PROCEDURE_PARAMETERS PRM JOIN RDB$FIELDS FLD ON ' +
1369 ' PRM.RDB$FIELD_SOURCE = FLD.RDB$FIELD_NAME ' +
1370 'WHERE ' +
1371 ' Trim(PRM.RDB$PROCEDURE_NAME) = ? AND ' +
1372 ' PRM.RDB$PARAMETER_TYPE = ? ' +
1373 'ORDER BY PRM.RDB$PARAMETER_NUMBER';
1374
1375
1376 { TFBUDRProcedureFactory }
1377
1378 procedure TFBUDRProcedureFactory.UpdateArgNames(att: IAttachment;
1379 aProcName: AnsiString);
1380
1381 procedure UpdateFieldNames(paramType: integer; list: TStrings);
1382 var FieldNames: IResultSet;
1383 begin
1384 list.Clear;
1385 if aProcName = '' then
1386 Exit;
1387 FieldNames := att.OpenCursorAtStart(sGetProcArgsSQL,[aProcName,paramType]);
1388 while not FieldNames.IsEOF do
1389 begin
1390 list.Add(FieldNames[0].AsString);
1391 FieldNames.FetchNext;
1392 end;
1393 end;
1394
1395 begin
1396 UpdateFieldNames(0,FInArgNames);
1397 UpdateFieldNames(1,FOutArgNames);
1398 end;
1399
1400 procedure TFBUDRProcedureFactory.SetController(AValue: TFBUDRController);
1401 begin
1402 if FController = AValue then Exit;
1403 FController := AValue;
1404 end;
1405
1406 constructor TFBUDRProcedureFactory.Create(aName: AnsiString;
1407 aProcedure: TFBUDRProcedureClass);
1408 begin
1409 inherited Create;
1410 FName := aName;
1411 FProcedure := aProcedure;
1412 FInArgNames := TStringList.Create;
1413 FOutArgNames := TStringList.Create;
1414 end;
1415
1416 destructor TFBUDRProcedureFactory.Destroy;
1417 begin
1418 if FInArgNames <> nil then
1419 FInArgNames.Free;
1420 if FOutArgNames <> nil then
1421 FOutArgNames.Free;
1422 inherited Destroy;
1423 end;
1424
1425 procedure TFBUDRProcedureFactory.dispose();
1426 begin
1427 Free;
1428 end;
1429
1430 procedure TFBUDRProcedureFactory.setup(status: Firebird.IStatus;
1431 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata;
1432 inBuilder: Firebird.IMetadataBuilder; outBuilder: Firebird.IMetadataBuilder);
1433 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1434 FBInBuilder: IFBUDRMetadataBuilder;
1435 FBOutBuilder: IFBUDRMetadataBuilder;
1436 FBContext: IFBUDRExternalContext;
1437 begin
1438 try
1439 if loLogProcedures in FBUDRControllerOptions.LogOptions then
1440 FController.WriteToLog(SProcSetup + FName);
1441
1442 FBInBuilder := nil;
1443 FBOutBuilder := nil;
1444 FBContext := TFBUDRExternalContext.Create(Controller,context);
1445 FController.StartJournaling(FBContext);
1446
1447 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1448 if inBuilder <> nil then
1449 FBInBuilder := TFBUDRMetadataBuilder.Create(FBContext,inBuilder);
1450 if outBuilder <> nil then
1451 FBOutBuilder := TFBUDRMetadataBuilder.Create(FBContext,outBuilder);
1452 if [loLogProcedures, loDetails] <= FBUDRControllerOptions.LogOptions then
1453 FController.WriteToLog(SRoutineMetadata + LineEnding + (FBRoutineMetadata as TFBUDRRoutineMetadata).AsText);
1454 TFBUDRProcedure.setup(FBContext,FBRoutineMetadata,FBInBuilder,FBOutBuilder)
1455 except on E: Exception do
1456 FController.FBSetStatusFromException(E,status);
1457 end;
1458 end;
1459
1460 function TFBUDRProcedureFactory.newItem(status: Firebird.IStatus;
1461 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata
1462 ): IExternalProcedure;
1463 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1464 FBContext: IFBUDRExternalContext;
1465 begin
1466 try
1467 FBContext := TFBUDRExternalContext.Create(Controller,context);
1468 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1469 {Now get the Field Names}
1470 UpdateArgNames(FBContext.GetAttachment,FBRoutineMetadata.getName);
1471 Result := FProcedure.Create(FController,FName,FBRoutineMetadata,
1472 FInArgNames,FOutArgNames);
1473 except on E: Exception do
1474 FController.FBSetStatusFromException(E,status);
1475 end;
1476 end;
1477
1478 { TFBUDRExternalResultsSet }
1479
1480 procedure TFBUDRExternalResultsSet.SetFieldNames(SQLDA: TSQLDataArea);
1481 var i: integer;
1482 begin
1483 for i := 0 to FOutArgNames.Count - 1 do
1484 if i < SQLDA.Count then
1485 SQLDA[i].Name := FOutArgNames[i];
1486 end;
1487
1488 procedure TFBUDRExternalResultsSet.Close;
1489 begin
1490 //do nothing by default
1491 end;
1492
1493 constructor TFBUDRExternalResultsSet.Create(UDRProcedure: TFBUDRProcedure;
1494 context: IFBUDRExternalContext; aOutArgNames: TStrings;
1495 metadata: Firebird.IMessageMetadata; outMsg: pointer);
1496 begin
1497 inherited Create;
1498 FUDRProcedure := UDRProcedure;
1499 FOutArgNames := aOutArgNames;
1500 Inc(FUDRProcedure.FRefCount);
1501 if metadata <> nil then
1502 begin
1503 FOutputDataSQLDA := TFBUDROutParamsSQLDA.Create(context,metadata,outMsg);
1504 SetFieldNames(FOutputDataSQLDA);
1505 FOutputData := TFBUDROutputParams.Create(FOutputDataSQLDA);
1506 if [loLogProcedures,loDetails] <= FBUDRControllerOptions.LogOptions then
1507 FUDRProcedure.FController.WriteToLog(SOutputParams,FOutputData);
1508 end;
1509 end;
1510
1511 destructor TFBUDRExternalResultsSet.Destroy;
1512 begin
1513 FOutputData := nil;
1514 if FOutputDataSQLDA <> nil then
1515 FOutputDataSQLDA.Free;
1516 inherited Destroy;
1517 end;
1518
1519 procedure TFBUDRExternalResultsSet.dispose();
1520 begin
1521 Close;
1522 FUDRProcedure.dispose;
1523 Free;
1524 end;
1525
1526 { TFBUDRProcedure }
1527
1528 procedure TFBUDRProcedure.SetFieldNames(SQLDA: TSQLDataArea);
1529 var i: integer;
1530 begin
1531 for i := 0 to FInArgNames.Count - 1 do
1532 if i < SQLDA.Count then
1533 SQLDA[i].Name := FInArgNames[i];
1534 end;
1535
1536 constructor TFBUDRProcedure.Create(aController: TFBUDRController;
1537 aName: AnsiString; routineMetadata: IFBUDRRoutineMetadata; aInArgNames,
1538 aOutArgNames: TStrings);
1539 begin
1540 inherited Create;
1541 FController := aController;
1542 FName := aName;
1543 FRefCount := 1;
1544 FRoutineMetaData := routineMetadata;
1545 FInArgNames := aInArgNames;
1546 FOutArgNames := aOutArgNames;
1547 end;
1548
1549 function TFBUDRProcedure.getCharSet(context: IFBUDRExternalContext): AnsiString;
1550 begin
1551 Result := '';
1552 end;
1553
1554 class procedure TFBUDRProcedure.setup(context: IFBUDRExternalContext;
1555 metadata: IFBUDRRoutineMetadata; inBuilder: IFBUDRMetadataBuilder;
1556 outBuilder: IFBUDRMetadataBuilder);
1557 begin
1558 //Override in subclass
1559 end;
1560
1561 procedure TFBUDRProcedure.dispose();
1562 begin
1563 if loLogProcedures in FBUDRControllerOptions.LogOptions then
1564 FController.WriteToLog(Format(SProcDispose,[FName,FRefCount]));
1565
1566 Dec(FRefCount);
1567 if FRefCount = 0 then Free;
1568 end;
1569
1570 procedure TFBUDRProcedure.getCharSet(status: Firebird.IStatus;
1571 context: Firebird.IExternalContext; name: PAnsiChar; nameSize: Cardinal);
1572 var charset: AnsiString;
1573 FBContext: IFBUDRExternalContext;
1574 begin
1575 try
1576 FBContext := TFBUDRExternalContext.Create(Controller,context);
1577 charset := getCharSet(FBContext);
1578 if charset <> '' then
1579 begin
1580 StrPLCopy(name,charset,nameSize);
1581 if loLogProcedures in FBUDRControllerOptions.LogOptions then
1582 FController.WriteToLog(Format(SProcCharset,[FName,charset]));
1583 end;
1584 except on E: Exception do
1585 FController.FBSetStatusFromException(E,status);
1586 end;
1587 end;
1588
1589 { TFBUDRInParamsSQLDA }
1590
1591 procedure TFBUDRInParamsSQLDA.AllocMessageBuffer(len: integer);
1592 begin
1593 FMessageBuffer := FBuffer;
1594 FMsgLength := len;
1595 end;
1596
1597 procedure TFBUDRInParamsSQLDA.FreeMessageBuffer;
1598 begin
1599 FBuffer := nil;
1600 FMsgLength := 0;
1601 end;
1602
1603 function TFBUDRInParamsSQLDA.GetAttachment: IAttachment;
1604 begin
1605 Result := FAttachment;
1606 end;
1607
1608 function TFBUDRInParamsSQLDA.GetTransaction: ITransaction;
1609 begin
1610 Result := FTransaction;
1611 end;
1612
1613 constructor TFBUDRInParamsSQLDA.Create(context: IFBUDRExternalContext;
1614 aMetadata: Firebird.IMessageMetaData; aBuffer: PByte);
1615 begin
1616 inherited Create(context.GetFirebirdAPI);
1617 FAttachment := context.GetAttachment;
1618 FTransaction := context.GetTransaction;
1619 FBuffer := aBuffer;
1620 Bind(aMetaData);
1621 end;
1622
1623 { TFBUDROutParamsSQLDA }
1624
1625 procedure TFBUDROutParamsSQLDA.AllocMessageBuffer(len: integer);
1626 begin
1627 FillChar(FBuffer^,len,0);
1628 FMessageBuffer := FBuffer;
1629 FMsgLength := len;
1630 end;
1631
1632 procedure TFBUDROutParamsSQLDA.FreeMessageBuffer;
1633 begin
1634 FMessageBuffer := nil;
1635 FMsgLength := 0;
1636 end;
1637
1638 function TFBUDROutParamsSQLDA.GetAttachment: IAttachment;
1639 begin
1640 Result := FAttachment;
1641 end;
1642
1643 function TFBUDROutParamsSQLDA.GetTransaction: ITransaction;
1644 begin
1645 Result := FTransaction;
1646 end;
1647
1648 constructor TFBUDROutParamsSQLDA.Create(context: IFBUDRExternalContext;
1649 aMetadata: Firebird.IMessageMetaData; aBuffer: PByte);
1650 begin
1651 inherited Create(context.GetFirebirdAPI);
1652 FAttachment := context.GetAttachment;
1653 FTransaction := context.GetTransaction;
1654 FBuffer := aBuffer;
1655 Bind(aMetadata);
1656 end;
1657
1658 function TFBUDROutParamsSQLDA.CanChangeMetaData: boolean;
1659 begin
1660 Result := false;
1661 end;
1662
1663 procedure TFBUDROutParamsSQLDA.Finalise;
1664 begin
1665 PackBuffer;
1666 end;
1667
1668 { TFBUDRFunction }
1669
1670 procedure TFBUDRFunction.SetFieldNames(SQLDA: TFBUDRInParamsSQLDA);
1671 var i: integer;
1672 begin
1673 for i := 0 to FFieldNames.Count - 1 do
1674 if i < SQLDA.Count then
1675 SQLDA[i].Name := FFieldNames[i];
1676 end;
1677
1678 constructor TFBUDRFunction.Create(aController: TFBUDRController;
1679 aName: AnsiString; routineMetadata: IFBUDRRoutineMetadata;
1680 aFieldNames: TStrings);
1681 begin
1682 inherited Create;
1683 FController := aController;
1684 FName := aName;
1685 FFieldNames := aFieldNames;
1686 FRoutineMetaData := routineMetadata;
1687 if loLogFunctions in FBUDRControllerOptions.LogOptions then
1688 begin
1689 FController.WriteToLog(Format(sFuncCreated,[aName]));
1690 if loDetails in FBUDRControllerOptions.LogOptions then
1691 FController.WriteToLog((FRoutineMetaData as TFBUDRRoutineMetadata).AsText);
1692 end;
1693 end;
1694
1695 function TFBUDRFunction.getCharSet(context: IFBUDRExternalContext): AnsiString;
1696 begin
1697 Result := '';
1698 end;
1699
1700 function TFBUDRFunction.Execute(context: IFBUDRExternalContext;
1701 ProcMetadata: IFBUDRProcMetadata; InputParams: IFBUDRInputParams;
1702 ResultSQLType: cardinal): variant;
1703 begin
1704 raise Exception.CreateFmt(SFuncNotOverridden,[FName]);
1705 end;
1706
1707 procedure TFBUDRFunction.Execute(context: IFBUDRExternalContext;
1708 ProcMetadata: IFBUDRProcMetadata; InputParams: IFBUDRInputParams;
1709 ReturnValue: ISQLParam);
1710 begin
1711 ReturnValue.AsVariant := Execute(context,ProcMetadata,InputParams,ReturnValue.GetSQLType);
1712 end;
1713
1714 class procedure TFBUDRFunction.setup(context: IFBUDRExternalContext;
1715 metadata: IFBUDRRoutineMetadata; inBuilder: IFBUDRMetadataBuilder;
1716 outBuilder: IFBUDRMetadataBuilder);
1717 begin
1718 //Do nothing be default
1719 end;
1720
1721 procedure TFBUDRFunction.dispose();
1722 begin
1723 if loLogFunctions in FBUDRControllerOptions.LogOptions then
1724 FController.WriteToLog(Format(SFuncDispose,[FName]));
1725
1726 Free;
1727 end;
1728
1729 procedure TFBUDRFunction.getCharSet(status: Firebird.IStatus;
1730 context: Firebird.IExternalContext; name: PAnsiChar; nameSize: Cardinal);
1731 var charset: AnsiString;
1732 FBContext: IFBUDRExternalContext;
1733 begin
1734 try
1735 FBContext := TFBUDRExternalContext.Create(Controller,context);
1736 charset := getCharSet(FBContext);
1737 if charset <> '' then
1738 begin
1739 StrPLCopy(name,charset,nameSize);
1740 if loLogFunctions in FBUDRControllerOptions.LogOptions then
1741 FController.WriteToLog(Format(SFuncCharset,[FName,charset]));
1742 end;
1743 except on E: Exception do
1744 FController.FBSetStatusFromException(E,status);
1745 end;
1746 end;
1747
1748 procedure TFBUDRFunction.execute(status: Firebird.IStatus;
1749 context: Firebird.IExternalContext; inMsg: Pointer; outMsg: Pointer);
1750 var aProcMetadata: IFBUDRProcMetadata;
1751 OutParamsSQLDA: TFBUDROutParamsSQLDA;
1752 InParamsSQLDA: TFBUDRInParamsSQLDA;
1753 InputParams: IFBUDRInputParams;
1754 OutputData: IFBUDROutputData;
1755 metadata: Firebird.IMessageMetadata;
1756 FBContext: IFBUDRExternalContext;
1757 begin
1758 try
1759 if loLogFunctions in FBUDRControllerOptions.LogOptions then
1760 FController.WriteToLog(SFuncExecute + FName);
1761
1762 OutParamsSQLDA := nil;
1763 InParamsSQLDA := nil;
1764 InputParams := nil;
1765 OutputData := nil;
1766 if FRoutineMetadata.QueryInterface(IFBUDRProcMetadata,aProcMetadata) <> S_OK then
1767 FBUDRError(ibxeNoProcMetadata,[nil])
1768 else
1769 begin
1770 FBContext := TFBUDRExternalContext.Create(Controller,context);
1771 if [loLogFunctions,loDetails] <= FBUDRControllerOptions.LogOptions then
1772 FController.WriteToLog((FBContext as TFBUDRExternalContext).AsText);
1773
1774 try
1775 if FRoutineMetadata.HasInputMetadata then
1776 begin
1777 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getInputMetadata;
1778 try
1779 InParamsSQLDA := TFBUDRInParamsSQLDA.Create(FBContext,
1780 metadata,
1781 inMsg);
1782 SetFieldNames(InParamsSQLDA);
1783 finally
1784 metadata.release;
1785 end;
1786
1787 InputParams := TFBUDRInputParams.Create(InParamsSQLDA);
1788 if [loLogFunctions,loDetails] <= FBUDRControllerOptions.LogOptions then
1789 FController.WriteToLog(SInputParams,InputParams);
1790 end;
1791
1792 if FRoutineMetadata.HasOutputMetadata then
1793 begin
1794 metadata := (FRoutineMetadata as TFBUDRRoutineMetadata).getOutputMetadata;
1795 try
1796 OutParamsSQLDA := TFBUDROutParamsSQLDA.Create(FBContext,
1797 metadata,
1798 outMsg);
1799 finally
1800 metadata.release;
1801 end;
1802 OutputData := TFBUDROutputParams.Create(OutParamsSQLDA);
1803 end
1804 else
1805 raise Exception.CreateFmt(SNoReturnValue,[FName]);
1806
1807 Execute(FBContext,aProcMetadata,InputParams,OutputData[0]);
1808
1809 if [loLogFunctions,loDetails] <= FBUDRControllerOptions.LogOptions then
1810 FController.WriteToLog(SOutputData,OutputData);
1811
1812 OutParamsSQLDA.Finalise; {copy result to OutMsg buffer}
1813 finally
1814 OutputData := nil;
1815 InputParams := nil;
1816 if OutParamsSQLDA <> nil then
1817 OutParamsSQLDA.Free;
1818 if InParamsSQLDA <> nil then
1819 InParamsSQLDA.Free;
1820 end;
1821 end;
1822 except on E: Exception do
1823 FController.FBSetStatusFromException(E,status);
1824 end;
1825 end;
1826
1827 { TFBUDRFunctionFactory }
1828
1829 const
1830 FunctionArgsSQL =
1831 'SELECT Trim(RDB$ARGUMENT_NAME) FROM RDB$FUNCTION_ARGUMENTS RFA JOIN RDB$FIELDS FLD ' +
1832 'ON RFA.RDB$FIELD_SOURCE = FLD.RDB$FIELD_NAME '+
1833 'WHERE RDB$FUNCTION_NAME = ? AND RDB$ARGUMENT_POSITION > 0' +
1834 'ORDER BY RDB$ARGUMENT_POSITION';
1835
1836 procedure TFBUDRFunctionFactory.UpdateFieldNames(att: IAttachment;
1837 aFunctionName: AnsiString);
1838 var FieldNames: IResultSet;
1839 begin
1840 FFieldNames.Clear;
1841 if aFunctionName = '' then
1842 Exit;
1843 FieldNames := att.OpenCursorAtStart(FunctionArgsSQL,[aFunctionName]);
1844 while not FieldNames.IsEOF do
1845 begin
1846 FFieldNames.Add(FieldNames[0].AsString);
1847 FieldNames.FetchNext;
1848 end;
1849 end;
1850
1851 procedure TFBUDRFunctionFactory.SetController(AValue: TFBUDRController);
1852 begin
1853 if FController = AValue then Exit;
1854 FController := AValue;
1855 end;
1856
1857 constructor TFBUDRFunctionFactory.Create(aName: AnsiString;
1858 aFunction: TFBUDRFunctionClass);
1859 begin
1860 inherited Create;
1861 FName := aName;
1862 FFunction := aFunction;
1863 FFieldNames := TStringList.Create;
1864 end;
1865
1866 destructor TFBUDRFunctionFactory.Destroy;
1867 begin
1868 if FFieldNames <> nil then
1869 FFieldNames.Free;
1870 inherited Destroy;
1871 end;
1872
1873 procedure TFBUDRFunctionFactory.dispose();
1874 begin
1875 Free;
1876 end;
1877
1878 procedure TFBUDRFunctionFactory.setup(status: Firebird.IStatus;
1879 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata;
1880 inBuilder: Firebird.IMetadataBuilder; outBuilder: Firebird.IMetadataBuilder);
1881 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1882 FBInBuilder: IFBUDRMetadataBuilder;
1883 FBOutBuilder: IFBUDRMetadataBuilder;
1884 FBContext: IFBUDRExternalContext;
1885 begin
1886 FBInBuilder := nil;
1887 FBOutBuilder := nil;
1888 try
1889 if loLogFunctions in FBUDRControllerOptions.LogOptions then
1890 FController.WriteToLog(SFuncSetup + FName);
1891
1892 FBContext := TFBUDRExternalContext.Create(Controller,context);
1893
1894 FController.StartJournaling(FBContext);
1895
1896 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1897
1898 if inBuilder <> nil then
1899 FBInBuilder := TFBUDRMetadataBuilder.Create(FBContext,inBuilder);
1900 if outBuilder <> nil then
1901 FBOutBuilder := TFBUDRMetadataBuilder.Create(FBContext,outBuilder);
1902 if [loLogFunctions, loDetails] <= FBUDRControllerOptions.LogOptions then
1903 FController.WriteToLog(SRoutineMetadata + LineEnding + (FBRoutineMetadata as TFBUDRRoutineMetadata).AsText);
1904
1905 TFBUDRFunction.setup(FBContext,FBRoutineMetadata,FBInBuilder,FBOutBuilder)
1906 except on E: Exception do
1907 FController.FBSetStatusFromException(E,status);
1908 end;
1909 end;
1910
1911 function TFBUDRFunctionFactory.newItem(status: Firebird.IStatus;
1912 context: Firebird.IExternalContext; metadata: Firebird.IRoutineMetadata
1913 ): Firebird.IExternalFunction;
1914 var FBRoutineMetadata: IFBUDRRoutineMetadata;
1915 FBContext: IFBUDRExternalContext;
1916 begin
1917 try
1918 FBContext := TFBUDRExternalContext.Create(Controller,context);
1919 FBRoutineMetadata := TFBUDRRoutineMetadata.Create(FBContext,metadata);
1920 {Now get the argument Names}
1921 UpdateFieldNames(FBContext.GetAttachment,FBRoutineMetadata.getName);
1922 Result := FFunction.Create(FController,FName,FBRoutineMetadata,FFieldNames);
1923 except on E: Exception do
1924 FController.FBSetStatusFromException(E,status);
1925 end;
1926 end;
1927
1928 { TFBUDRController }
1929
1930 function TFBUDRController.GetDateTimeFmt: AnsiString;
1931 begin
1932 {$IF declared(DefaultFormatSettings)}
1933 with DefaultFormatSettings do
1934 {$ELSE}
1935 {$IF declared(FormatSettings)}
1936 with FormatSettings do
1937 {$IFEND}
1938 {$IFEND}
1939 Result := ShortDateFormat + ' ' + LongTimeFormat + '.zzzz'
1940 end;
1941
1942 function TFBUDRController.ProcessTemplateMacros(aTemplate: AnsiString
1943 ): AnsiString;
1944
1945 function CleanDirName(aDirName: PAnsiChar): AnsiString;
1946 begin
1947 Result := Trim(strpas(aDirName));
1948 {$IFDEF WINDOWS}
1949 Result := StringReplace(Result,'/',DirectorySeparator,[rfReplaceAll]);
1950 {$ELSE}
1951 Result := StringReplace(Result,'\',DirectorySeparator,[rfReplaceAll]);
1952 {$ENDIF}
1953 if (Length(Result) > 0) and (Result[Length(aDirName)] <> DirectorySeparator) then
1954 Result := Result + DirectorySeparator;
1955 end;
1956
1957 var udr_config: Firebird.IConfig;
1958 config_entry: Firebird.IConfigEntry;
1959 aStatus: Firebird.IStatus;
1960 begin
1961 if assigned(FMaster) then
1962 with FMaster.getConfigManager do
1963 begin
1964 Result := StringReplace(aTemplate,'$LOGDIR',CleanDirName(getDirectory(DIR_LOG)),[rfReplaceAll, rfIgnoreCase]);
1965 udr_config := getPluginConfig('UDR');
1966 if udr_config <> nil then
1967 try
1968 aStatus := FMaster.getStatus;
1969 try
1970 config_entry := udr_config.find(aStatus,'path');
1971 with aStatus do
1972 if (getState and STATE_ERRORS) <> 0 then
1973 raise EFBUDRException.Create(aStatus);
1974 finally
1975 aStatus.dispose;
1976 end;
1977
1978 if config_entry <> nil then
1979 try
1980 with config_entry do
1981 Result := StringReplace(Result,'$UDRDIR',CleanDirName(config_entry.getValue),[rfReplaceAll, rfIgnoreCase]);
1982 finally
1983 config_entry.release;
1984 end;
1985 finally
1986 udr_config.release();
1987 end;
1988 end;
1989 Result := StringReplace(Result,'$TEMP',GetTempDir,[rfReplaceAll, rfIgnoreCase]);
1990 Result := StringReplace(Result,'$MODULE',FBUDRControllerOptions.ModuleName,[rfReplaceAll, rfIgnoreCase]);
1991 Result := StringReplace(Result,'$TIMESTAMP',FormatDateTime('yyyymmddhhnnss',Now),[rfReplaceAll, rfIgnoreCase]);
1992 end;
1993
1994 procedure TFBUDRController.RegisterUDRFactories(status: Firebird.IStatus;
1995 udrPlugin: Firebird.IUdrPlugin);
1996 var i: integer;
1997 begin
1998 if FUDRFactoryList <> nil then
1999 for i := 0 to FUDRFactoryList.Count - 1 do
2000 try
2001 RegisterUDRFactory(status,udrPlugin,FUDRFactoryList[i], FUDRFactoryList.Objects[i]);
2002 with status do
2003 if (getState and STATE_ERRORS) <> 0 then break;
2004 except on E: Exception do
2005 FBSetStatusFromException(E,status);
2006 end;
2007 end;
2008
2009 procedure TFBUDRController.RegisterUDRFactory(status: Firebird.IStatus;
2010 udrPlugin: Firebird.IUdrPlugin; aName: AnsiString; factory: TObject);
2011 begin
2012 if factory is TFBUDRFunctionFactory then
2013 begin
2014 if loLogFunctions in FBUDRControllerOptions.LogOptions then
2015 WriteToLog(SFuncRegister + aName);
2016 udrPlugin.registerFunction(status,PAnsiChar(aName),Firebird.IUdrFunctionFactory(factory));
2017 TFBUDRFunctionFactory(factory).Controller := self;
2018 end
2019 else
2020 if factory is TFBUDRProcedureFactory then
2021 begin
2022 if loLogProcedures in FBUDRControllerOptions.LogOptions then
2023 WriteToLog(SProcRegister + aName);
2024 udrPlugin.registerProcedure(status,PAnsiChar(aName),Firebird.IUdrProcedureFactory(factory));
2025 TFBUDRProcedureFactory(factory).Controller := self;
2026 end
2027 else
2028 if factory is TFBUDRTriggerFactory then
2029 begin
2030 if loLogTriggers in FBUDRControllerOptions.LogOptions then
2031 WriteToLog(STriggerRegister + aName);
2032 udrPlugin.registerTrigger(status,PAnsiChar(aName),Firebird.IUdrTriggerFactory(factory));
2033 TFBUDRTriggerFactory(factory).Controller := self;
2034 end
2035 else
2036 FBUDRError(ibxeInvalidFactoryObject,[factory.ClassName,aName]);
2037 end;
2038
2039 procedure TFBUDRController.FreeFactoryList;
2040 //var i: integer;
2041 begin
2042 {if FUDRFactoryList <> nil then
2043 for i := 0 to FUDRFactoryList.Count - 1 do
2044 if FUDRFactoryList.Objects[i] <> nil then
2045 FUDRFactoryList.Objects[i].Free;} {disposed of by Firebird Engine}
2046 FreeAndNil(FUDRFactoryList);
2047 end;
2048
2049 const
2050 LogOptionsTable: array [TFBUDRControllerLogOption] of AnsiString = (
2051 'loLogFunctions',
2052 'loLogProcedures',
2053 'loLogTriggers',
2054 'loLogFetches',
2055 'loModifyQueries',
2056 'loReadOnlyQueries',
2057 'loDetails'
2058 );
2059
2060 procedure TFBUDRController.LoadConfig;
2061
2062 function GetLogOptions(LogOptionsStr: AnsiString; var aLogOptions: TFBUDRControllerLogOptions): boolean;
2063 var s: AnsiString;
2064 p1, p2, len: integer;
2065 i: TFBUDRControllerLogOption;
2066 found: boolean;
2067 begin
2068 Result := LogOptionsStr <> '';
2069 if Result then
2070 begin
2071 aLogOptions := [];
2072 p2 := 1;
2073 {skip past opening square bracket}
2074 while (p2 <= length(LogOptionsStr)) and (LogOptionsStr[p2] <> '[') do
2075 Inc(p2);
2076
2077 {parse into words separated by commas}
2078 Inc(p2);
2079 p1 := p2;
2080 while p2 <= length(LogOptionsStr) do
2081 begin
2082 if LogOptionsStr[p2] in [',',']'] then
2083 begin
2084 s := Trim(system.copy(LogOptionsStr,p1,p2-p1));
2085
2086 {Now convert string to LogOption}
2087 found := false;
2088 for i := low(TFBUDRControllerLogOption) to high(TFBUDRControllerLogOption) do
2089 if CompareText(s,LogOptionsTable[i]) = 0 then
2090 begin
2091 aLogOptions := aLogOptions + [i];
2092 found := true;
2093 break;
2094 end;
2095 if not found then
2096 WriteToLog(Format(SBadLogOptionsStr,[LogOptionsStr,p2]));
2097 if LogOptionsStr[p2] = ']' then
2098 break;
2099 p1 := p2 + 1;
2100 end;
2101 Inc(p2);
2102 end;
2103 if p2 > length(LogOptionsStr) then
2104 WriteToLog(Format(SBadLogOptionsStr,[LogOptionsStr,p2]));
2105 Result := true;
2106 end;
2107 end;
2108
2109 var aLogOptions: TFBUDRControllerLogOptions;
2110 aConfigFileName: Ansistring;
2111 begin
2112 aConfigFileName := ProcessTemplateMacros(FBUDRControllerOptions.ConfigFileNameTemplate);
2113 if (FConfigFile = nil) and (aConfigFileName <> '') and FileExists(aConfigFileName) then
2114 begin
2115 FConfigFile := TIniFile.Create(aConfigFileName);
2116 {$if declared(TStringArray)}
2117 FConfigFile.BoolFalseStrings := FalseStrings;
2118 FConfigFile.BoolTrueStrings := TrueStrings;
2119 {$ifend}
2120 WriteToLog(Format(SReadingConfigFile,[aConfigFileName]));
2121 with FBUDRControllerOptions do
2122 if AllowConfigFileOverrides then
2123 begin
2124 LogFileNameTemplate := FConfigFile.ReadString('Controller','LogFileNameTemplate',LogFileNameTemplate);
2125 WriteToLog('LogFileNameTemplate = ' + LogFileNameTemplate);
2126 ForceWriteLogEntries := FConfigFile.ReadBool('Controller','ForceWriteLogEntries',ForceWriteLogEntries);
2127 WriteToLog('ForceWriteLogEntries = ' + BooleanToStr(ForceWriteLogEntries ,'true','false'));
2128 ThreadSafeLogging := FConfigFile.ReadBool('Controller','ThreadSafeLogging',ThreadSafeLogging);
2129 WriteToLog('ThreadSafeLogging = ' + BooleanToStr(ThreadSafeLogging,'true','false'));
2130 if GetLogOptions( FConfigFile.ReadString('Controller','LogOptions',''),aLogOptions) then
2131 LogOptions := aLogOptions;
2132 WriteToLog('LogOptions = ' + LogOptionsToStr(LogOptions));
2133 end;
2134 end;
2135 end;
2136
2137 function TFBUDRController.NeedLogStream: boolean;
2138 var FilePathName: AnsiString;
2139 begin
2140 Result := false;
2141 if FLogStream = nil then
2142 begin
2143 FilePathName := ProcessTemplateMacros(FBUDRControllerOptions.LogFileNameTemplate);
2144 if FilePathName = '' then
2145 Exit;
2146 if FJnlOpenAppend then
2147 begin
2148 FLogStream := TFileStream.Create(FilePathName,fmOpenWrite or fmShareDenyNone);
2149 FLogStream.Seek(0, soFromEnd);
2150 end
2151 else
2152 begin
2153 FLogStream := TFileStream.Create(FilePathName,fmCreate or fmShareDenyNone);
2154 FJnlOpenAppend := true;
2155 end;
2156 end;
2157 Result := true;
2158 end;
2159
2160 function TFBUDRController.LogOptionsToStr(aLogOptions: TFBUDRControllerLogOptions): AnsiString;
2161 var i: TFBUDRControllerLogOption;
2162 separator: AnsiString;
2163 begin
2164 Result := '[';
2165 separator := '';
2166 for i := low(TFBUDRControllerLogOption) to high(TFBUDRControllerLogOption) do
2167 if i in aLogOptions then
2168 begin
2169 Result := Result + separator + LogOptionsTable[i];
2170 separator := ',';
2171 end;
2172 Result := Result + ']';
2173 end;
2174
2175 constructor TFBUDRController.Create(status: Firebird.IStatus;
2176 udrPlugin: Firebird.IUdrPlugin; aTheirUnloadFlag: booleanPtr;
2177 var aMyUnloadFlag: booleanPtr);
2178 begin
2179 try
2180 inherited Create;
2181 FFBController := self;
2182 FTheirUnloadFlag := aTheirUnloadFlag;
2183 FMyUnloadFlag := false;
2184 aMyUnloadFlag := @FMyUnloadFlag;
2185 FMaster := udrPlugin.getMaster;
2186 FCriticalSection := TCriticalSection.Create;
2187 RegisterUDRFactories(status,udrPlugin);
2188 LoadConfig;
2189 except on E: Exception do
2190 FBSetStatusFromException(E,status);
2191 end;
2192 end;
2193
2194 destructor TFBUDRController.Destroy;
2195 begin
2196 if FConfigFile <> nil then
2197 FConfigFile.Free;
2198 if FLogStream <> nil then
2199 FLogStream.Free;
2200 FreeFactoryList;
2201 if FCriticalSection <> nil then
2202 FCriticalSection.Free;
2203 if (FTheirUnloadFlag <> nil) and not FMyUnloadFlag then
2204 FTheirUnloadFlag^ := true; {notify unload of module}
2205 inherited Destroy;
2206 end;
2207
2208 procedure TFBUDRController.FBSetStatusFromException(E: Exception; aStatus: Firebird.IStatus);
2209 var StatusVector: TStatusVector;
2210 ErrorVector: NativeIntPtr;
2211 begin
2212 if E is EFBUDRException then
2213 aStatus.setErrors((E as EFBUDRException).Status.getErrors())
2214 else
2215 if E is EIBInterBaseError then
2216 begin
2217 ErrorVector := ((E as EIBInterBaseError).Status as TFB30Status).GetStatus.getErrors();
2218 astatus.setErrors(ErrorVector);
2219 end
2220 else
2221 begin
2222 FMessageBuffer := E.Message;
2223 StatusVector[0] := isc_arg_gds;
2224 StatusVector[1] := NativeInt(isc_random);
2225 StatusVector[2] := isc_arg_string;
2226 StatusVector[3] := NativeInt(PAnsiChar(FMessageBuffer));
2227 StatusVector[4] := isc_arg_end;
2228 astatus.setErrors(@StatusVector);
2229 end;
2230 WriteToLog(SExceptionRaised + LineEnding + E.Message);
2231 end;
2232
2233 procedure TFBUDRController.WriteToLog(Msg: AnsiString);
2234 var LogEntry: AnsiString;
2235 begin
2236 if not NeedLogStream then
2237 Exit; {no log file available}
2238 LogEntry := Format(sLogFormat,[FBFormatDateTime(GetDateTimeFmt,Now),Msg]) + LineEnding;
2239 if FBUDRControllerOptions.ThreadSafeLogging then
2240 begin
2241 FCriticalSection.Enter;
2242 try
2243 FLogStream.Write(LogEntry[1],Length(LogEntry));
2244 if FBUDRControllerOptions.ForceWriteLogEntries then
2245 FreeAndNil(FLogStream);
2246 finally
2247 FCriticalSection.Leave;
2248 end;
2249 end
2250 else
2251 FLogStream.Write(LogEntry[1],Length(LogEntry));
2252 end;
2253
2254 function TFBUDRController.CharSetIDToText(att: IAttachment; id: integer): AnsiString;
2255 begin
2256 if att = nil then
2257 Result := IntToStr(id)
2258 else
2259 Result := att.GetCharsetName(id);
2260 end;
2261
2262 procedure TFBUDRController.WriteToLog(aTitle: AnsiString; Params: IFBUDRInputParams
2263 );
2264 var i: integer;
2265 Msg: AnsiString;
2266 begin
2267 Msg := aTitle + LineEnding;
2268 for i := 0 to Params.getCount - 1 do
2269 with Params[i] do
2270 begin
2271 Msg := Msg +
2272 'Parameter ' + IntToStr(i) + ':' + NewLineTAB +
2273 'Field Name = ' + getName + NewLineTab +
2274 'Alias Name = ' + getAliasName + NewLineTab +
2275 'SQLType = ' + GetSQLTypeName + NewLineTAB +
2276 'sub type = ' + IntToStr(getSubType) + NewLineTAB +
2277 'Scale = ' + IntToStr(getScale) + NewLineTAB +
2278 'Charset = ' + CharSetIDToText((Params as TFBUDRInputParams).GetAttachment,getCharSetID) + NewLineTAB +
2279 BooleanToStr(getIsNullable,'Nullable','Not Nullable') + NewLineTAB +
2280 'Size = ' + IntToStr(GetSize) + NewLineTAB +
2281 'Value = ' + BooleanToStr(IsNull,'NULL',GetStrValue(Params[i] as TColumnMetaData)) + LineEnding;
2282 end;
2283 WriteToLog(Msg);
2284 end;
2285
2286 function TFBUDRController.GetStrValue(item: TColumnMetaData): Ansistring;
2287
2288 function HexString(s: AnsiString): AnsiString;
2289 var i: integer;
2290 begin
2291 Result := '';
2292 for i := 1 to length(s) do
2293 Result := Result + Format('%x ',[byte(s[i])]);
2294 end;
2295
2296 begin
2297 with Item do
2298 case SQLType of
2299 SQL_ARRAY:
2300 Result := '(array)';
2301 SQL_BLOB:
2302 if getSubtype = 1 then
2303 begin
2304 if GetCharSetID = 1 then
2305 Result := HexString(AsString)
2306 else
2307 Result := AsString;
2308 end
2309 else
2310 Result := '(Blob)';
2311 SQL_TEXT,SQL_VARYING:
2312 if GetCharSetID = 1 then
2313 Result := HexString(AsString)
2314 else
2315 Result := TrimRight(AsString);
2316 else
2317 Result := AsString;
2318 end;
2319 end;
2320
2321 procedure TFBUDRController.WriteToLog(aTitle: AnsiString; OutputData: IFBUDROutputData);
2322 var i: integer;
2323 Msg: AnsiString;
2324 begin
2325 Msg := aTitle + LineEnding;
2326 for i := 0 to OutputData.getCount - 1 do
2327 with OutputData[i] do
2328 begin
2329 Msg := Msg + 'Column ' + IntToStr(i) + NewLineTAB +
2330 'Field Name = ' + getName + NewLineTab +
2331 'SQLType = ' + GetSQLTypeName + NewLineTAB +
2332 'sub type = ' + IntToStr(getSubType) + NewLineTAB +
2333 'Scale = ' + IntToStr(getScale) + NewLineTAB +
2334 'Charset = ' + CharSetIDToText((OutputData as TFBUDROutputParams).GetAttachment,getCharSetID) + NewLineTAB +
2335 BooleanToStr(getIsNullable,'Nullable','Not Nullable') + NewLineTAB +
2336 'Size = ' + IntToStr(GetSize) + NewLineTAB +
2337 'Value = ' + BooleanToStr(IsNull,'NULL', GetStrValue(OutputData[i] as TColumnMetaData)) + LineEnding;
2338 end;
2339 WriteToLog(Msg);
2340 end;
2341
2342 procedure TFBUDRController.StartJournaling(context: IFBUDRExternalContext);
2343 var JnlOptions: TJournalOptions;
2344 begin
2345 JnlOptions := [joNoServerTable];
2346 if loModifyQueries in FBUDRControllerOptions.LogOptions then
2347 JnlOptions := JnlOptions + [joModifyQueries];
2348 if loReadOnlyQueries in FBUDRControllerOptions.LogOptions then
2349 JnlOptions := JnlOptions + [joReadOnlyQueries];
2350 if JnlOptions <> [] then
2351 begin
2352 if NeedLogStream then
2353 context.GetAttachment.StartJournaling(FLogStream,JnlOptions);
2354 end;
2355 end;
2356
2357 function TFBUDRController.HasConfigFile: boolean;
2358 begin
2359 Result := FConfigFile <> nil;
2360 end;
2361
2362 function TFBUDRController.ReadConfigString(Section, Ident,
2363 DefaultValue: AnsiString): AnsiString;
2364 begin
2365 if HasConfigFile then
2366 Result := FConfigFile.ReadString(Section, Ident, DefaultValue)
2367 else
2368 raise Exception.Create(SNoConfigFile);
2369 end;
2370
2371 function TFBUDRController.ReadConfigInteger(Section, Ident: AnsiString;
2372 DefaultValue: integer): integer;
2373 begin
2374 if HasConfigFile then
2375 Result := FConfigFile.ReadInteger(Section, Ident, DefaultValue)
2376 else
2377 raise Exception.Create(SNoConfigFile);
2378 end;
2379
2380 function TFBUDRController.ReadConfigBool(Section, Ident: AnsiString;
2381 DefaultValue: boolean): boolean;
2382 begin
2383 if HasConfigFile then
2384 Result := FConfigFile.ReadBool(Section, Ident, DefaultValue)
2385 else
2386 raise Exception.Create(SNoConfigFile);
2387 end;
2388
2389 end.
2390

Properties

Name Value
svn:eol-style native