ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/udr/source/FBUDRController.pas
Revision: 392
Committed: Wed Feb 9 16:17:50 2022 UTC (2 years, 2 months ago) by tony
Content type: text/x-pascal
File size: 83839 byte(s)
Log Message:
cloneAttachment and GetServiceManager added

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

Properties

Name Value
svn:eol-style native