ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/client/FBAttachment.pas
(Generate patch)

Comparing:
ibx/trunk/fbintf/client/FBAttachment.pas (file contents), Revision 56 by tony, Mon Mar 6 10:20:02 2017 UTC vs.
ibx/branches/udr/client/FBAttachment.pas (file contents), Revision 389 by tony, Thu Jan 20 23:33:40 2022 UTC

# Line 16 | Line 16
16   *
17   *  The Initial Developer of the Original Code is Tony Whyman.
18   *
19 < *  The Original Code is (C) 2016 Tony Whyman, MWA Software
19 > *  The Original Code is (C) 2016-2021 Tony Whyman, MWA Software
20   *  (http://www.mwasoftware.co.uk).
21   *
22   *  All Rights Reserved.
# Line 32 | Line 32 | unit FBAttachment;
32   {$IFDEF FPC}
33   {$mode delphi}
34   {$interfaces COM}
35 + {$define HASREQEX}
36   {$ENDIF}
37  
38   interface
39  
40   uses
41 <  Classes, SysUtils, IB,  FBParamBlock, FBActivityMonitor;
41 >  Classes, SysUtils, {$IFDEF WINDOWS} windows, {$ENDIF} IB,  FBParamBlock,
42 >  FBActivityMonitor, FBClientAPI, IBUtils;
43 >
44 > const
45 >  DefaultMaxInlineBlobLimit = 8192;
46  
47   type
48 +  TCharsetMap = record
49 +    CharsetID: integer;
50 +    CharSetName: AnsiString;
51 +    CharSetWidth: integer;
52 +    CodePage: TSystemCodePage;
53 +    AllowReverseLookup: boolean; {used to ensure that lookup of CP_UTF* does not return UNICODE_FSS}
54 +  end;
55 +
56 +  { Database Journalling.
57 +
58 +    This class is intended to support a client side journal of all database
59 +    updates, inserts and deletes made by the client during a session. It also records
60 +    the transaction each update was made under.
61 +
62 +    The database schema is required to include a control table "IBX$JOURNALS" and
63 +    an SQL Sequence IBX$SESSIONS. These are created by the class when the
64 +    database is opened, if they are not already present. However, it is recommended
65 +    that they are created as an orginal part of the database schema in order to
66 +    unnecessarily avoid each user being given sufficient priviledge to create tables
67 +    and Sequences.
68 +
69 +    Syntax:
70 +
71 +    Transaction Start:
72 +    *S:<date/time>,<attachmentid>,<session id>,<transaction no.>,<string length>:<transaction Name>,<string length>:<TPB>,<default Completion>
73 +
74 +    Transaction Commit:
75 +    *C:<date/time>,<attachmentid>,<session id>,<transaction no.>
76 +
77 +    Transaction Commit retaining :
78 +    *c:<date/time>,<attachmentid>,<session id>,<transaction no.><old transaction no.>
79 +
80 +    Transaction Rollback:
81 +    *R:<date/time>,<attachmentid>,<session id>,<transaction no.>
82 +
83 +    Transaction Rollback retaining:
84 +    *r:<date/time>,<attachmentid>,<session id>,<transaction no.><old transaction no.>
85 +
86 +    Update/Insert/Delete
87 +    *Q:<date/time>,<attachmentid>,<session id>,<transaction no.>,<length of query text in bytes>:<query text>
88 +
89 +  }
90 +
91 +  { TFBJournaling }
92 +
93 +  TFBJournaling = class(TActivityHandler, IJournallingHook)
94 +  private
95 +    {Logfile}
96 +    const sQueryJournal          = '*Q:''%s'',%d,%d,%d,%d:%s' + LineEnding;
97 +    const sTransStartJnl         = '*S:''%s'',%d,%d,%d,%d:%s,%d:%s,%d' + LineEnding;
98 +    const sTransCommitJnl        = '*C:''%s'',%d,%d,%d' + LineEnding;
99 +    const sTransCommitFailJnl    = '*F:''%s'',%d,%d,%d' + LineEnding;
100 +    const sTransCommitRetJnl     = '*c:''%s'',%d,%d,%d,%d' + LineEnding;
101 +    const sTransRollBackJnl      = '*R:''%s'',%d,%d,%d' + LineEnding;
102 +    const sTransRollBackFailJnl  = '*f:''%s'',%d,%d,%d' + LineEnding;
103 +    const sTransRollBackRetJnl   = '*r:''%s'',%d,%d,%d,%d' + LineEnding;
104 +  private
105 +    FOptions: TJournalOptions;
106 +    FJournalFilePath: string;
107 +    FJournalFileStream: TStream;
108 +    FSessionID: integer;
109 +    FDoNotJournal: boolean;
110 +    function GetDateTimeFmt: AnsiString;
111 +  protected
112 +    procedure EndSession(RetainJournal: boolean);
113 +    function GetAttachment: IAttachment; virtual; abstract;
114 +  public
115 +    {IAttachment}
116 +    procedure Disconnect(Force: boolean=false); virtual;
117 +  public
118 +    {IJournallingHook}
119 +    procedure TransactionStart(Tr: ITransaction);
120 +    function TransactionEnd( TransactionID: integer; Completion: TTrCompletionState): boolean;
121 +    procedure TransactionRetained(Tr: ITransaction; OldTransactionID: integer;
122 +      Action: TTransactionAction);
123 +    procedure ExecQuery(Stmt: IStatement);
124 +    procedure ExecImmediateJnl(sql: AnsiString; tr: ITransaction);
125 +  public
126 +    {Client side Journaling}
127 +    function JournalingActive: boolean;
128 +    function GetJournalOptions: TJournalOptions;
129 +    function StartJournaling(aJournalLogFile: AnsiString): integer; overload;
130 +    function StartJournaling(aJournalLogFile: AnsiString; Options: TJournalOptions): integer; overload;
131 +    function StartJournaling(S: TStream; Options: TJournalOptions): integer; overload;
132 +    procedure StopJournaling(RetainJournal: boolean);
133 +  end;
134  
135    { TFBAttachment }
136  
137 <  TFBAttachment = class(TActivityHandler)
137 >  TFBAttachment = class(TFBJournaling)
138    private
139      FDPB: IDPB;
140      FFirebirdAPI: IFirebirdAPI;
141 <  protected
142 <    FDatabaseName: AnsiString;
143 <    FRaiseExceptionOnConnectError: boolean;
141 >    FODSMajorVersion: integer;
142 >    FODSMinorVersion: integer;
143 >    FUserCharSetMap: array of TCharSetMap;
144 >    FSecDatabase: AnsiString;
145 >    FInlineBlobLimit: integer;
146 >    FAttachmentID: integer;
147      FSQLDialect: integer;
148      FHasDefaultCharSet: boolean;
149      FCharSetID: integer;
150      FCodePage: TSystemCodePage;
151 <    constructor Create(DatabaseName: AnsiString; DPB: IDPB;
151 >    FRemoteProtocol: AnsiString;
152 >    FAuthMethod: AnsiString;
153 >    FHasConnectionInfo: boolean;
154 >    procedure NeedDBInfo;
155 >    procedure NeedConnectionInfo;
156 >  protected
157 >    FDatabaseName: AnsiString;
158 >    FRaiseExceptionOnConnectError: boolean;
159 >    constructor Create(api: TFBClientAPI; DatabaseName: AnsiString; DPB: IDPB;
160        RaiseExceptionOnConnectError: boolean);
161      procedure CheckHandle; virtual; abstract;
162 +    procedure ClearCachedInfo;
163      function GenerateCreateDatabaseSQL(DatabaseName: AnsiString; aDPB: IDPB): AnsiString;
164 +    function GetDBInfo(ReqBuffer: PByte; ReqBufLen: integer): IDBInformation; virtual; abstract;
165 +    function IsConnected: boolean; virtual; abstract;
166      procedure EndAllTransactions;
167 +    procedure DPBFromCreateSQL(CreateSQL: AnsiString);
168      procedure SetParameters(SQLParams: ISQLParams; params: array of const);
169 +    procedure SetSQLDialect(aValue: integer);
170 +    procedure UseServerICUChanged; virtual;
171    public
172      destructor Destroy; override;
173 +    procedure Disconnect(Force: boolean); override;
174 +    function getFirebirdAPI: IFirebirdAPI;
175      function getDPB: IDPB;
176      function AllocateBPB: IBPB;
177 <    function StartTransaction(TPB: array of byte; DefaultCompletion: TTransactionCompletion): ITransaction; overload; virtual; abstract;
178 <    function StartTransaction(TPB: ITPB; DefaultCompletion: TTransactionCompletion): ITransaction; overload; virtual; abstract;
179 <    procedure Disconnect(Force: boolean=false); virtual; abstract;
177 >    function AllocateDIRB: IDIRB;
178 >    function StartTransaction(TPB: array of byte;
179 >      DefaultCompletion: TTransactionCompletion;
180 >      aName: AnsiString=''): ITransaction; overload; virtual; abstract;
181 >    function StartTransaction(TPB: ITPB; DefaultCompletion: TTransactionCompletion;
182 >      aName: AnsiString=''): ITransaction; overload; virtual; abstract;
183      procedure ExecImmediate(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer); overload; virtual; abstract;
184      procedure ExecImmediate(TPB: array of byte; sql: AnsiString; aSQLDialect: integer); overload;
185      procedure ExecImmediate(transaction: ITransaction; sql: AnsiString); overload;
# Line 75 | Line 188 | type
188      function ExecuteSQL(transaction: ITransaction; sql: AnsiString; SQLDialect: integer; params: array of const): IResults; overload;
189      function ExecuteSQL(TPB: array of byte; sql: AnsiString; params: array of const): IResults; overload;
190      function ExecuteSQL(transaction: ITransaction; sql: AnsiString; params: array of const): IResults; overload;
191 <    function OpenCursor(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer): IResultSet; overload;
191 >    function OpenCursor(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
192 >                             Scrollable: boolean=false): IResultSet; overload;
193      function OpenCursor(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
194                               params: array of const): IResultSet; overload;
195 <    function OpenCursor(transaction: ITransaction; sql: AnsiString): IResultSet; overload;
195 >    function OpenCursor(transaction: ITransaction; sql: AnsiString; Scrollable: boolean=false): IResultSet; overload;
196      function OpenCursor(transaction: ITransaction; sql: AnsiString;
197                               params: array of const): IResultSet; overload;
198 <    function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer): IResultSet; overload;
198 >    function OpenCursor(transaction: ITransaction; sql: AnsiString; Scrollable: boolean;
199 >                             params: array of const): IResultSet; overload;
200 >    function OpenCursor(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer; Scrollable: boolean;
201 >                             params: array of const): IResultSet; overload;
202 >    function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
203 >                             Scrollable: boolean=false): IResultSet; overload;
204      function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
205                               params: array of const): IResultSet; overload;
206 <    function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString): IResultSet; overload;
206 >    function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer; Scrollable: boolean;
207 >                             params: array of const): IResultSet; overload;
208 >    function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString; Scrollable: boolean=false): IResultSet; overload;
209      function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString;
210                               params: array of const): IResultSet; overload;
211 <    function OpenCursorAtStart(sql: AnsiString): IResultSet; overload;
211 >    function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString; Scrollable: boolean;
212 >                             params: array of const): IResultSet; overload;
213 >    function OpenCursorAtStart(sql: AnsiString;Scrollable: boolean=false): IResultSet; overload;
214 >    function OpenCursorAtStart(sql: AnsiString; Scrollable: boolean;
215 >                             params: array of const): IResultSet; overload;
216      function OpenCursorAtStart(sql: AnsiString;
217                               params: array of const): IResultSet; overload;
218 <    function Prepare(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer): IStatement; overload; virtual; abstract;
219 <    function Prepare(transaction: ITransaction; sql: AnsiString): IStatement; overload;
218 >    function Prepare(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer; CursorName: AnsiString=''): IStatement; overload; virtual; abstract;
219 >    function Prepare(transaction: ITransaction; sql: AnsiString; CursorName: AnsiString=''): IStatement; overload;
220      function PrepareWithNamedParameters(transaction: ITransaction; sql: AnsiString;
221 <                       aSQLDialect: integer; GenerateParamNames: boolean=false): IStatement; overload; virtual; abstract;
221 >                       aSQLDialect: integer; GenerateParamNames: boolean=false;
222 >                       CaseSensitiveParams: boolean = false; CursorName: AnsiString=''): IStatement; overload; virtual; abstract;
223      function PrepareWithNamedParameters(transaction: ITransaction; sql: AnsiString;
224 <                       GenerateParamNames: boolean=false): IStatement; overload;
224 >                       GenerateParamNames: boolean=false;
225 >                       CaseSensitiveParams: boolean = false; CursorName: AnsiString=''): IStatement; overload;
226      function GetEventHandler(Events: TStrings): IEvents; overload; virtual; abstract;
227      function GetEventHandler(Event: AnsiString): IEvents; overload;
228  
229      function GetSQLDialect: integer;
230 +    function GetAttachmentID: integer;
231 +    function CreateBlob(transaction: ITransaction; RelationName, ColumnName: AnsiString; BPB: IBPB=nil): IBlob; overload;
232 +    function CreateBlob(transaction: ITransaction; BlobMetaData: IBlobMetaData; BPB: IBPB=nil): IBlob; overload; virtual; abstract;
233      function OpenBlob(transaction: ITransaction; BlobMetaData: IBlobMetaData; BlobID: TISC_QUAD; BPB: IBPB=nil): IBlob; overload; virtual; abstract;
234 +    function OpenBlob(transaction: ITransaction; RelationName, ColumnName: AnsiString; BlobID: TISC_QUAD; BPB: IBPB=nil): IBlob; overload;
235      function OpenBlob(transaction: ITransaction; Field: ISQLData; BPB: IBPB=nil): IBlob; overload;
236 <    property SQLDialect: integer read FSQLDialect;
237 <    property HasDefaultCharSet: boolean read FHasDefaultCharSet;
238 <    property CharSetID: integer read FCharSetID;
239 <    property CodePage: TSystemCodePage read FCodePage;
236 >    function CreateArray(transaction: ITransaction; RelationName, ColumnName: AnsiString
237 >      ): IArray; overload;
238 >    function CreateArray(transaction: ITransaction; ArrayMetaData: IArrayMetaData): IArray; overload; virtual; abstract;
239 >    function OpenArray(transaction: ITransaction; RelationName, ColumnName: AnsiString; ArrayID: TISC_QUAD): IArray; overload;
240 >    function OpenArray(transaction: ITransaction; ArrayMetaData: IArrayMetaData; ArrayID: TISC_QUAD): IArray; overload; virtual; abstract;
241 >    property SQLDialect: integer read GetSQLDialect;
242      property DPB: IDPB read FDPB;
243 +  public
244 +    function GetDBInformation(Requests: array of byte): IDBInformation; overload;
245 +    function GetDBInformation(Request: byte): IDBInformation; overload;
246 +    function GetDBInformation(Requests: IDIRB): IDBInformation; overload;
247 +    function GetConnectString: AnsiString;
248 +    function GetRemoteProtocol: AnsiString;
249 +    function GetAuthenticationMethod: AnsiString;
250 +    function GetSecurityDatabase: AnsiString;
251 +    function GetODSMajorVersion: integer;
252 +    function GetODSMinorVersion: integer;
253 +    function GetCharSetID: integer;
254 +    function HasDecFloatSupport: boolean; virtual;
255 +    function GetInlineBlobLimit: integer;
256 +    procedure SetInlineBlobLimit(limit: integer);
257 +    function HasBatchMode: boolean; virtual;
258 +    function HasTable(aTableName: AnsiString): boolean;
259 +    function HasFunction(aFunctionName: AnsiString): boolean;
260 +    function HasProcedure(aProcName: AnsiString): boolean;
261 +
262 +  public
263 +    {Character Sets}
264 +    function HasDefaultCharSet: boolean;
265 +    function GetDefaultCharSetID: integer;
266 +    function GetCharsetName(CharSetID: integer): AnsiString;
267 +    function CharSetID2CodePage(CharSetID: integer; var CodePage: TSystemCodePage): boolean;
268 +    function CodePage2CharSetID(CodePage: TSystemCodePage; var CharSetID: integer): boolean;
269 +    function CharSetName2CharSetID(CharSetName: AnsiString; var CharSetID: integer): boolean;
270 +    function CharSetWidth(CharSetID: integer; var Width: integer): boolean;
271 +    procedure RegisterCharSet(CharSetName: AnsiString; CodePage: TSystemCodePage;
272 +      AllowReverseLookup:boolean; out CharSetID: integer);
273 +    function GetBlobMetaData(Transaction: ITransaction; tableName, columnName: AnsiString): IBlobMetaData; virtual; abstract;
274 +    function GetArrayMetaData(Transaction: ITransaction; tableName, columnName: AnsiString): IArrayMetaData; virtual; abstract;
275 +    property CharSetID: integer read GetCharSetID;
276 +    property CodePage: TSystemCodePage read FCodePage;
277 +
278 +  public
279 +    {Time Zone Support}
280 +    function GetTimeZoneServices: ITimeZoneServices; virtual;
281 +    function HasTimeZoneSupport: boolean; virtual;
282 +
283 +  end;
284 +
285 +  { TDPBItem }
286 +
287 +  TDPBItem = class(TParamBlockItem,IDPBItem)
288 +  public
289 +   function getParamTypeName: AnsiString; override;
290 +  end;
291 +
292 +  { TDPB }
293 +
294 +  TDPB = class (TCustomParamBlock<TDPBItem,IDPBItem>, IDPB)
295 +  protected
296 +   function LookupItemType(ParamTypeName: AnsiString): byte; override;
297 +  public
298 +    constructor Create(api: TFBClientAPI);
299 +    function GetParamTypeName(ParamType: byte): Ansistring;
300 +    {$IFDEF FPC}
301 +    function IDPB.GetDPBParamTypeName = GetParamTypeName;
302 +    {$ELSE}
303 +    function GetDPBParamTypeName(ParamType: byte): Ansistring;
304 +    {$ENDIF}
305    end;
306  
307   implementation
308  
309 < uses FBMessages, FBTransaction;
309 > uses FBMessages, IBErrorCodes, FBTransaction {$IFDEF HASREQEX}, RegExpr{$ENDIF};
310 >
311 > const
312 >  {Journaling}
313 >  sJournalTableName = 'IBX$JOURNALS';
314 >  sSequenceName = 'IBX$SESSIONS';
315 >
316 >  sqlCreateJournalTable =
317 >    'Create Table ' + sJournalTableName + '(' +
318 >    '  IBX$SessionID Integer not null, '+
319 >    '  IBX$TransactionID Integer not null, '+
320 >    '  IBX$OldTransactionID Integer, '+
321 >    '  IBX$USER VarChar(32) Default CURRENT_USER, '+
322 >    '  IBX$CREATED TIMESTAMP Default CURRENT_TIMESTAMP, '+
323 >    '  Primary Key(IBX$SessionID,IBX$TransactionID)' +
324 >    ')';
325 >
326 >  sqlCreateSequence = 'CREATE SEQUENCE ' + sSequenceName;
327 >
328 >  sqlGetNextSessionID = 'Select Gen_ID(' + sSequenceName + ',1) as SessionID From RDB$DATABASE';
329 >
330 >  sqlRecordJournalEntry = 'Insert into ' + sJournalTableName + '(IBX$SessionID,IBX$TransactionID,IBX$OldTransactionID) '+
331 >                        'Values(?,?,?)';
332 >
333 >  sqlCleanUpSession = 'Delete From ' + sJournalTableName + ' Where IBX$SessionID = ?';
334 >
335 > const
336 >  CharSetMap: array [0..69] of TCharsetMap = (
337 >  (CharsetID: 0; CharSetName: 'NONE'; CharSetWidth: 1; CodePage: CP_ACP; AllowReverseLookup: true),
338 >  (CharsetID: 1; CharSetName: 'OCTETS'; CharSetWidth: 1; CodePage: CP_NONE; AllowReverseLookup: true),
339 >  (CharsetID: 2; CharSetName: 'ASCII'; CharSetWidth: 1; CodePage: CP_ASCII; AllowReverseLookup: true),
340 >  (CharsetID: 3; CharSetName: 'UNICODE_FSS'; CharSetWidth: 3; CodePage: CP_UTF8; AllowReverseLookup: false),
341 >  (CharsetID: 4; CharSetName: 'UTF8'; CharSetWidth: 4; CodePage: CP_UTF8; AllowReverseLookup: true),
342 >  (CharsetID: 5; CharSetName: 'SJIS_0208'; CharSetWidth: 2; CodePage: 20932; AllowReverseLookup: true),
343 >  (CharsetID: 6; CharSetName: 'EUCJ_0208'; CharSetWidth: 2; CodePage: 20932; AllowReverseLookup: true),
344 >  (CharsetID: 7; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: true),
345 >  (CharsetID: 8; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: true),
346 >  (CharsetID: 9; CharSetName: 'DOS737'; CharSetWidth: 1; CodePage: 737; AllowReverseLookup: true),
347 >  (CharsetID: 10; CharSetName: 'DOS437'; CharSetWidth: 1; CodePage: 437; AllowReverseLookup: true),
348 >  (CharsetID: 11; CharSetName: 'DOS850'; CharSetWidth: 1; CodePage: 850; AllowReverseLookup: true),
349 >  (CharsetID: 12; CharSetName: 'DOS865'; CharSetWidth: 1; CodePage: 865; AllowReverseLookup: true),
350 >  (CharsetID: 13; CharSetName: 'DOS860'; CharSetWidth: 1; CodePage: 860; AllowReverseLookup: true),
351 >  (CharsetID: 14; CharSetName: 'DOS863'; CharSetWidth: 1; CodePage: 863; AllowReverseLookup: true),
352 >  (CharsetID: 15; CharSetName: 'DOS775'; CharSetWidth: 1; CodePage: 775; AllowReverseLookup: true),
353 >  (CharsetID: 16; CharSetName: 'DOS858'; CharSetWidth: 1; CodePage: 858; AllowReverseLookup: true),
354 >  (CharsetID: 17; CharSetName: 'DOS862'; CharSetWidth: 1; CodePage: 862; AllowReverseLookup: true),
355 >  (CharsetID: 18; CharSetName: 'DOS864'; CharSetWidth: 1; CodePage: 864; AllowReverseLookup: true),
356 >  (CharsetID: 19; CharSetName: 'NEXT'; CharSetWidth: 1; CodePage: CP_NONE; AllowReverseLookup: true),
357 >  (CharsetID: 20; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: true),
358 >  (CharsetID: 21; CharSetName: 'ISO8859_1'; CharSetWidth: 1; CodePage: 28591; AllowReverseLookup: true),
359 >  (CharsetID: 22; CharSetName: 'ISO8859_2'; CharSetWidth: 1; CodePage: 28592; AllowReverseLookup: true),
360 >  (CharsetID: 23; CharSetName: 'ISO8859_3'; CharSetWidth: 1; CodePage: 28593; AllowReverseLookup: true),
361 >  (CharsetID: 24; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
362 >  (CharsetID: 25; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
363 >  (CharsetID: 26; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
364 >  (CharsetID: 27; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
365 >  (CharsetID: 28; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
366 >  (CharsetID: 29; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
367 >  (CharsetID: 30; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
368 >  (CharsetID: 31; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
369 >  (CharsetID: 32; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
370 >  (CharsetID: 33; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
371 >  (CharsetID: 34; CharSetName: 'ISO8859_4'; CharSetWidth: 1; CodePage: 28594; AllowReverseLookup: true),
372 >  (CharsetID: 35; CharSetName: 'ISO8859_5'; CharSetWidth: 1; CodePage: 28595; AllowReverseLookup: true),
373 >  (CharsetID: 36; CharSetName: 'ISO8859_6'; CharSetWidth: 1; CodePage: 28596; AllowReverseLookup: true),
374 >  (CharsetID: 37; CharSetName: 'ISO8859_7'; CharSetWidth: 1; CodePage: 28597; AllowReverseLookup: true),
375 >  (CharsetID: 38; CharSetName: 'ISO8859_8'; CharSetWidth: 1; CodePage: 28598; AllowReverseLookup: true),
376 >  (CharsetID: 39; CharSetName: 'ISO8859_9'; CharSetWidth: 1; CodePage: 28599; AllowReverseLookup: true),
377 >  (CharsetID: 40; CharSetName: 'ISO8859_13'; CharSetWidth: 1; CodePage: 28603; AllowReverseLookup: true),
378 >  (CharsetID: 41; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
379 >  (CharsetID: 42; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
380 >  (CharsetID: 43; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
381 >  (CharsetID: 44; CharSetName: 'KSC_5601'; CharSetWidth: 2; CodePage: 949; AllowReverseLookup: true),
382 >  (CharsetID: 45; CharSetName: 'DOS852'; CharSetWidth: 1; CodePage: 852; AllowReverseLookup: true),
383 >  (CharsetID: 46; CharSetName: 'DOS857'; CharSetWidth: 1; CodePage: 857; AllowReverseLookup: true),
384 >  (CharsetID: 47; CharSetName: 'DOS861'; CharSetWidth: 1; CodePage: 861; AllowReverseLookup: true),
385 >  (CharsetID: 48; CharSetName: 'DOS866'; CharSetWidth: 1; CodePage: 866; AllowReverseLookup: true),
386 >  (CharsetID: 49; CharSetName: 'DOS869'; CharSetWidth: 1; CodePage: 869; AllowReverseLookup: true),
387 >  (CharsetID: 50; CharSetName: 'CYRL'; CharSetWidth: 1; CodePage: 1251; AllowReverseLookup: true),
388 >  (CharsetID: 51; CharSetName: 'WIN1250'; CharSetWidth: 1; CodePage: 1250; AllowReverseLookup: true),
389 >  (CharsetID: 52; CharSetName: 'WIN1251'; CharSetWidth: 1; CodePage: 1251; AllowReverseLookup: true),
390 >  (CharsetID: 53; CharSetName: 'WIN1252'; CharSetWidth: 1; CodePage: 1252; AllowReverseLookup: true),
391 >  (CharsetID: 54; CharSetName: 'WIN1253'; CharSetWidth: 1; CodePage: 1253; AllowReverseLookup: true),
392 >  (CharsetID: 55; CharSetName: 'WIN1254'; CharSetWidth: 1; CodePage: 1254; AllowReverseLookup: true),
393 >  (CharsetID: 56; CharSetName: 'BIG_5'; CharSetWidth: 2; CodePage: 950; AllowReverseLookup: true),
394 >  (CharsetID: 57; CharSetName: 'GB_2312'; CharSetWidth: 2; CodePage: 936; AllowReverseLookup: true),
395 >  (CharsetID: 58; CharSetName: 'WIN1255'; CharSetWidth: 1; CodePage: 1255; AllowReverseLookup: true),
396 >  (CharsetID: 59; CharSetName: 'WIN1256'; CharSetWidth: 1; CodePage: 1256; AllowReverseLookup: true),
397 >  (CharsetID: 60; CharSetName: 'WIN1257'; CharSetWidth: 1; CodePage: 1257; AllowReverseLookup: true),
398 >  (CharsetID: 61; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
399 >  (CharsetID: 62; CharSetName: 'Unknown'; CharSetWidth: 0; CodePage: CP_NONE; AllowReverseLookup: false),
400 >  (CharsetID: 63; CharSetName: 'KOI8R'; CharSetWidth: 1; CodePage: 20866; AllowReverseLookup: true),
401 >  (CharsetID: 64; CharSetName: 'KOI8U'; CharSetWidth: 1; CodePage: 21866; AllowReverseLookup: true),
402 >  (CharsetID: 65; CharSetName: 'WIN1258'; CharSetWidth: 1; CodePage: 1258; AllowReverseLookup: true),
403 >  (CharsetID: 66; CharSetName: 'TIS620'; CharSetWidth: 1; CodePage: 874; AllowReverseLookup: true),
404 >  (CharsetID: 67; CharSetName: 'GBK'; CharSetWidth: 2; CodePage: 936; AllowReverseLookup: true),
405 >  (CharsetID: 68; CharSetName: 'CP943C'; CharSetWidth: 2; CodePage: 943; AllowReverseLookup: true),
406 >  (CharsetID: 69; CharSetName: 'GB18030'; CharSetWidth: 4; CodePage: 54936; AllowReverseLookup: true)
407 > );
408 >
409 > const
410 >  isc_dpb_last_dpb_constant = isc_dpb_decfloat_traps;
411 >
412 >  DPBConstantNames: array[1..isc_dpb_last_dpb_constant] of string = (
413 >    'cdd_pathname',
414 >    'allocation',
415 >    'journal',
416 >    'page_size',
417 >    'num_buffers',
418 >    'buffer_length',
419 >    'debug',
420 >    'garbage_collect',
421 >    'verify',
422 >    'sweep',
423 >    'enable_journal',
424 >    'disable_journal',
425 >    'dbkey_scope',
426 >    'number_of_users',
427 >    'trace',
428 >    'no_garbage_collect',
429 >    'damaged',
430 >    'license',
431 >    'sys_user_name',
432 >    'encrypt_key',
433 >    'activate_shadow',
434 >    'sweep_interval',
435 >    'delete_shadow',
436 >    'force_write',
437 >    'begin_log',
438 >    'quit_log',
439 >    'no_reserve',
440 >    'user_name',
441 >    'password',
442 >    'password_enc',
443 >    'sys_user_name_enc',
444 >    'interp',
445 >    'online_dump',
446 >    'old_file_size',
447 >    'old_num_files',
448 >    'old_file',
449 >    'old_start_page',
450 >    'old_start_seqno',
451 >    'old_start_file',
452 >    'drop_walfile',
453 >    'old_dump_id',
454 >    'wal_backup_dir',
455 >    'wal_chkptlen',
456 >    'wal_numbufs',
457 >    'wal_bufsize',
458 >    'wal_grp_cmt_wait',
459 >    'lc_messages',
460 >    'lc_ctype',
461 >    'cache_manager',
462 >    'shutdown',
463 >    'online',
464 >    'shutdown_delay',
465 >    'reserved',
466 >    'overwrite',
467 >    'sec_attach',
468 >    'disable_wal',
469 >    'connect_timeout',
470 >    'dummy_packet_interval',
471 >    'gbak_attach',
472 >    'sql_role_name',
473 >    'set_page_buffers',
474 >    'working_directory',
475 >    'sql_dialect',
476 >    'set_db_readonly',
477 >    'set_db_sql_dialect',
478 >    'gfix_attach',
479 >    'gstat_attach',
480 >    'set_db_charset',
481 >    'gsec_attach',
482 >    'address_path' ,
483 >    'process_id',
484 >    'no_db_triggers',
485 >    'trusted_auth',
486 >    'process_name',
487 >    'trusted_role',
488 >    'org_filename',
489 >    'utf8_filename',
490 >    'ext_call_depth',
491 >    'auth_block',
492 >    'client_version',
493 >    'remote_protocol',
494 >    'host_name',
495 >    'os_user',
496 >    'specific_auth_data',
497 >    'auth_plugin_list',
498 >    'auth_plugin_name',
499 >    'config',
500 >    'nolinger',
501 >    'reset_icu',
502 >    'map_attach',
503 >    'session_time_zone',
504 >    'set_db_replica',
505 >    'set_bind',
506 >    'decfloat_round',
507 >    'decfloat_traps'
508 >    );
509 >
510 > type
511 >
512 >  { TQueryProcessor }
513 >
514 >  TQueryProcessor=class(TSQLTokeniser)
515 >  private
516 >    FInString: AnsiString;
517 >    FIndex: integer;
518 >    FStmt: IStatement;
519 >    function DoExecute: AnsiString;
520 >    function GetParamValue(ParamIndex: integer): AnsiString;
521 >  protected
522 >    function GetChar: AnsiChar; override;
523 >  public
524 >    class function Execute(Stmt: IStatement): AnsiString;
525 >  end;
526 >
527 >  { TQueryProcessor }
528 >
529 > function TQueryProcessor.DoExecute: AnsiString;
530 > var token: TSQLTokens;
531 >    ParamIndex: integer;
532 > begin
533 >  Result := '';
534 >  ParamIndex := 0;
535 >
536 >  while not EOF do
537 >  begin
538 >    token := GetNextToken;
539 >    case token of
540 >    sqltPlaceHolder:
541 >      begin
542 >        Result := Result + GetParamValue(ParamIndex);
543 >        Inc(ParamIndex);
544 >      end;
545 >    else
546 >      Result := Result + TokenText;
547 >    end;
548 >  end;
549 > end;
550 >
551 > function TQueryProcessor.GetParamValue(ParamIndex: integer): AnsiString;
552 > begin
553 >  with FStmt.SQLParams[ParamIndex] do
554 >  begin
555 >    if IsNull then
556 >      Result := 'NULL'
557 >    else
558 >    case GetSQLType of
559 >    SQL_BLOB:
560 >      if getSubType = 1 then {string}
561 >        Result := '''' + SQLSafeString(GetAsString) + ''''
562 >      else
563 >        Result := TSQLXMLReader.FormatBlob(GetAsString,getSubType);
564 >
565 >    SQL_ARRAY:
566 >        Result := TSQLXMLReader.FormatArray(getAsArray);
567 >
568 >    SQL_VARYING,
569 >    SQL_TEXT,
570 >    SQL_TIMESTAMP,
571 >    SQL_TYPE_DATE,
572 >    SQL_TYPE_TIME,
573 >    SQL_TIMESTAMP_TZ_EX,
574 >    SQL_TIME_TZ_EX,
575 >    SQL_TIMESTAMP_TZ,
576 >    SQL_TIME_TZ:
577 >      Result := '''' + SQLSafeString(GetAsString) + '''';
578 >    else
579 >      Result := GetAsString;
580 >    end;
581 >  end;
582 > end;
583 >
584 > function TQueryProcessor.GetChar: AnsiChar;
585 > begin
586 >  if FIndex <= Length(FInString) then
587 >  begin
588 >    Result := FInString[FIndex];
589 >    Inc(FIndex);
590 >  end
591 >  else
592 >    Result := #0;
593 > end;
594 >
595 > class function TQueryProcessor.Execute(Stmt: IStatement): AnsiString;
596 > begin
597 >  if not Stmt.IsPrepared then
598 >    IBError(ibxeSQLClosed,[]);
599 >  with self.Create do
600 >  try
601 >    FStmt := Stmt;
602 >    FInString := Stmt.GetProcessedSQLText;
603 >    FIndex := 1;
604 >    Result := Trim(DoExecute);
605 >  finally
606 >    Free;
607 >  end;
608 > end;
609 >
610 > { TFBJournaling }
611 >
612 > function TFBJournaling.GetDateTimeFmt: AnsiString;
613 > begin
614 >  {$IF declared(DefaultFormatSettings)}
615 >  with DefaultFormatSettings do
616 >  {$ELSE}
617 >  {$IF declared(FormatSettings)}
618 >  with FormatSettings do
619 >  {$IFEND}
620 >  {$IFEND}
621 >  Result := ShortDateFormat + ' ' + LongTimeFormat + '.zzzz'
622 > end;
623 >
624 > procedure TFBJournaling.EndSession(RetainJournal: boolean);
625 > begin
626 >  if JournalingActive and (FJournalFilePath <> '') then
627 >  begin
628 >    FreeAndNil(FJournalFileStream);
629 >    if not (joNoServerTable in FOptions) and not RetainJournal then
630 >    try
631 >        GetAttachment.ExecuteSQL([isc_tpb_write,isc_tpb_wait,isc_tpb_consistency],
632 >             sqlCleanUpSession,[FSessionID]);
633 >        sysutils.DeleteFile(FJournalFilePath);
634 >    except On E: EIBInterBaseError do
635 >      if E.IBErrorCode <> isc_lost_db_connection then
636 >        raise;
637 >      {ignore - do not delete journal if database gone away}
638 >    end;
639 >    FSessionID := -1;
640 >  end;
641 > end;
642 >
643 > procedure TFBJournaling.Disconnect(Force: boolean);
644 > begin
645 >  if JournalingActive then
646 >    EndSession(Force);
647 > end;
648 >
649 > procedure TFBJournaling.TransactionStart(Tr: ITransaction);
650 > var LogEntry: AnsiString;
651 >    TPBText: AnsiString;
652 > begin
653 >  if not (joNoServerTable in FOptions) then
654 >  try
655 >    FDoNotJournal := true;
656 >    GetAttachment.ExecuteSQL(Tr,sqlRecordJournalEntry,[FSessionID,Tr.GetTransactionID,NULL]);
657 >  finally
658 >    FDoNotJournal := false;
659 >  end;
660 >  TPBText := Tr.getTPB.AsText;
661 >  LogEntry := Format(sTransStartJnl,[FBFormatDateTime(GetDateTimeFmt,Now),
662 >                                     GetAttachment.GetAttachmentID,
663 >                                     FSessionID,
664 >                                     Tr.GetTransactionID,
665 >                                     Length(Tr.TransactionName),
666 >                                     Tr.TransactionName,
667 >                                     Length(TPBText),TPBText,
668 >                                     ord(tr.GetDefaultCompletion)]);
669 >  if assigned(FJournalFileStream) then
670 >    FJournalFileStream.Write(LogEntry[1],Length(LogEntry));
671 > end;
672 >
673 > function TFBJournaling.TransactionEnd(TransactionID: integer;
674 >  Completion: TTrCompletionState): boolean;
675 >
676 > var LogEntry: AnsiString;
677 > begin
678 >  Result := false;
679 >    case Completion of
680 >    trRolledback:
681 >      begin
682 >        LogEntry := Format(sTransRollbackJnl,[FBFormatDateTime(GetDateTimeFmt,Now),
683 >                                              GetAttachment.GetAttachmentID,
684 >                                              FSessionID,TransactionID]);
685 >        Result := true;
686 >      end;
687 >
688 >    trRollbackFailed:
689 >      begin
690 >        LogEntry := Format(sTransRollbackFailJnl,[FBFormatDateTime(GetDateTimeFmt,Now),
691 >                                              GetAttachment.GetAttachmentID,
692 >                                              FSessionID,TransactionID]);
693 >        Result := true;
694 >      end;
695 >
696 >    trCommitted:
697 >      begin
698 >        LogEntry := Format(sTransCommitJnl,[FBFormatDateTime(GetDateTimeFmt,Now),
699 >                                            GetAttachment.GetAttachmentID,
700 >                                            FSessionID,TransactionID]);
701 >        Result := true;
702 >      end;
703 >
704 >    trCommitFailed:
705 >      begin
706 >        LogEntry := Format(sTransCommitFailJnl,[FBFormatDateTime(GetDateTimeFmt,Now),
707 >                                            GetAttachment.GetAttachmentID,
708 >                                            FSessionID,TransactionID]);
709 >        Result := true;
710 >      end;
711 >    end;
712 >    if assigned(FJournalFileStream) then
713 >      FJournalFileStream.Write(LogEntry[1],Length(LogEntry));
714 > end;
715 >
716 > procedure TFBJournaling.TransactionRetained(Tr: ITransaction;
717 >  OldTransactionID: integer; Action: TTransactionAction);
718 > var LogEntry: AnsiString;
719 > begin
720 >    case Action of
721 >      TACommitRetaining:
722 >          LogEntry := Format(sTransCommitRetJnl,[FBFormatDateTime(GetDateTimeFmt,Now),
723 >                                  GetAttachment.GetAttachmentID,
724 >                                  FSessionID,Tr.GetTransactionID,OldTransactionID]);
725 >      TARollbackRetaining:
726 >          LogEntry := Format(sTransRollbackRetJnl,[FBFormatDateTime(GetDateTimeFmt,Now),
727 >                                      GetAttachment.GetAttachmentID,
728 >                                      FSessionID,Tr.GetTransactionID,OldTransactionID]);
729 >    end;
730 >    if assigned(FJournalFileStream) then
731 >      FJournalFileStream.Write(LogEntry[1],Length(LogEntry));
732 >
733 >    if not (joNoServerTable in FOptions) then
734 >    try
735 >      FDoNotJournal := true;
736 >      GetAttachment.ExecuteSQL(Tr,sqlRecordJournalEntry,[FSessionID,Tr.GetTransactionID,OldTransactionID]);
737 >    finally
738 >      FDoNotJournal := false;
739 >   end;
740 > end;
741 >
742 > procedure TFBJournaling.ExecQuery(Stmt: IStatement);
743 > var SQL: AnsiString;
744 >    LogEntry: AnsiString;
745 > begin
746 >  SQL := TQueryProcessor.Execute(Stmt);
747 >  LogEntry := Format(sQueryJournal,[FBFormatDateTime(GetDateTimeFmt,Now),
748 >                                      GetAttachment.GetAttachmentID,
749 >                                      FSessionID,
750 >                                      Stmt.GetTransaction.GetTransactionID,
751 >                                      Length(SQL),SQL]);
752 >  if assigned(FJournalFileStream) then
753 >    FJournalFileStream.Write(LogEntry[1],Length(LogEntry));
754 > end;
755 >
756 > procedure TFBJournaling.ExecImmediateJnl(sql: AnsiString; tr: ITransaction);
757 > var LogEntry: AnsiString;
758 > begin
759 >  LogEntry := Format(sQueryJournal,[FBFormatDateTime(GetDateTimeFmt,Now),
760 >                                      GetAttachment.GetAttachmentID,
761 >                                      FSessionID,
762 >                                      tr.GetTransactionID,
763 >                                      Length(sql),sql]);
764 >  if assigned(FJournalFileStream) then
765 >    FJournalFileStream.Write(LogEntry[1],Length(LogEntry));
766 > end;
767 >
768 > function TFBJournaling.JournalingActive: boolean;
769 > begin
770 >  Result := (FJournalFileStream <> nil) and not FDoNotJournal;
771 > end;
772 >
773 > function TFBJournaling.GetJournalOptions: TJournalOptions;
774 > begin
775 >  Result := FOptions;
776 > end;
777 >
778 > function TFBJournaling.StartJournaling(aJournalLogFile: AnsiString): integer;
779 > begin
780 >  Result := StartJournaling(aJournalLogFile,[joReadWriteTransactions,joModifyQueries]);
781 > end;
782 >
783 > function TFBJournaling.StartJournaling(aJournalLogFile: AnsiString;
784 >  Options: TJournalOptions): integer;
785 > begin
786 >  try
787 >    StartJournaling(TFileStream.Create(aJournalLogFile,fmCreate),Options);
788 >  finally
789 >    FJournalFilePath := aJournalLogFile;
790 >  end;
791 > end;
792 >
793 > function TFBJournaling.StartJournaling(S: TStream; Options: TJournalOptions
794 >  ): integer;
795 > begin
796 >  FOptions := Options;
797 >  if not (joNoServerTable in FOptions) then
798 >  with GetAttachment do
799 >  begin
800 >    if  not HasTable(sJournalTableName) then
801 >    begin
802 >      ExecImmediate([isc_tpb_write,isc_tpb_wait,isc_tpb_consistency],sqlCreateJournalTable);
803 >      ExecImmediate([isc_tpb_write,isc_tpb_wait,isc_tpb_consistency],sqlCreateSequence);
804 >    end;
805 >    FSessionID := OpenCursorAtStart(sqlGetNextSessionID)[0].AsInteger;
806 >  end;
807 >  FJournalFileStream := S;
808 >  Result := FSessionID;
809 > end;
810 >
811 > procedure TFBJournaling.StopJournaling(RetainJournal: boolean);
812 > begin
813 >  EndSession(RetainJournal);
814 > end;
815  
816   { TFBAttachment }
817  
818 < constructor TFBAttachment.Create(DatabaseName: AnsiString; DPB: IDPB;
819 <  RaiseExceptionOnConnectError: boolean);
818 > procedure TFBAttachment.NeedConnectionInfo;
819 > var Stmt: IStatement;
820 >    ResultSet: IResultSet;
821 >    Param: IDPBItem;
822 > begin
823 >  if not IsConnected or FHasConnectionInfo then Exit;
824 >  NeedDBInfo;
825 >  FCharSetID := 0;
826 >  FRemoteProtocol := '';
827 >  FAuthMethod := 'Legacy_Auth';
828 >  FSecDatabase := 'Default';
829 >  if FODSMajorVersion > 11 then
830 >  begin
831 >    Stmt := Prepare(StartTransaction([isc_tpb_read,isc_tpb_nowait,isc_tpb_concurrency],taCommit),
832 >                    'Select MON$CHARACTER_SET_ID, MON$REMOTE_PROTOCOL, MON$AUTH_METHOD, MON$SEC_DATABASE From MON$ATTACHMENTS, MON$DATABASE '+
833 >                    'Where MON$ATTACHMENT_ID = CURRENT_CONNECTION ');
834 >    ResultSet := Stmt.OpenCursor;
835 >    if ResultSet.FetchNext then
836 >    begin
837 >      FCharSetID := ResultSet[0].AsInteger;
838 >      FRemoteProtocol := Trim(ResultSet[1].AsString);
839 >      FAuthMethod := Trim(ResultSet[2].AsString);
840 >      FSecDatabase := Trim(ResultSet[3].AsString);
841 >    end
842 >  end
843 >  else
844 >  if (FODSMajorVersion = 11) and (FODSMinorVersion >= 1) then
845 >  begin
846 >    Stmt := Prepare(StartTransaction([isc_tpb_read,isc_tpb_nowait,isc_tpb_concurrency],taCommit),
847 >                    'Select MON$CHARACTER_SET_ID, MON$REMOTE_PROTOCOL From MON$ATTACHMENTS '+
848 >                    'Where MON$ATTACHMENT_ID = CURRENT_CONNECTION');
849 >    ResultSet := Stmt.OpenCursor;
850 >    if ResultSet.FetchNext then
851 >    begin
852 >      FCharSetID := ResultSet[0].AsInteger;
853 >      FRemoteProtocol := Trim(ResultSet[1].AsString);
854 >    end
855 >  end
856 >  else
857 >  if DPB <> nil then
858 >  begin
859 >    Param :=  DPB.Find(isc_dpb_lc_ctype);
860 >    if (Param = nil) or not CharSetName2CharSetID(Param.AsString,FCharSetID) then
861 >      FCharSetID := 0;
862 >    case GetProtocol(FDatabaseName) of
863 >    TCP:       FRemoteProtocol := 'TCPv4';
864 >    Local:     FRemoteProtocol := '';
865 >    NamedPipe: FRemoteProtocol := 'Netbui';
866 >    SPX:       FRemoteProtocol := 'SPX'
867 >    end;
868 >  end;
869 >  FHasDefaultCharSet := CharSetID2CodePage(FCharSetID,FCodePage) and (FCharSetID > 1);
870 >  FHasConnectionInfo := true;
871 > end;
872 >
873 > procedure TFBAttachment.NeedDBInfo;
874 > var DBInfo: IDBInformation;
875 >    i: integer;
876 > begin
877 >  if not IsConnected or (FAttachmentID > 0) then Exit;
878 >  DBInfo := GetDBInformation([isc_info_db_id,isc_info_ods_version,isc_info_ods_minor_version,
879 >                               isc_info_db_SQL_Dialect, isc_info_attachment_id]);
880 >  for i := 0 to DBInfo.GetCount - 1 do
881 >    with DBInfo[i] do
882 >      case getItemType of
883 >      isc_info_ods_minor_version:
884 >        FODSMinorVersion := getAsInteger;
885 >      isc_info_ods_version:
886 >        FODSMajorVersion := getAsInteger;
887 >      isc_info_db_SQL_Dialect:
888 >        FSQLDialect := getAsInteger;
889 >      isc_info_attachment_id:
890 >        FAttachmentID := getAsInteger;
891 >      end;
892 > end;
893 >
894 > constructor TFBAttachment.Create(api: TFBClientAPI; DatabaseName: AnsiString;
895 >  DPB: IDPB; RaiseExceptionOnConnectError: boolean);
896   begin
897    inherited Create;
898 <  FFirebirdAPI := FirebirdAPI; {Keep reference to interface}
898 >  FFirebirdAPI := api.GetAPI; {Keep reference to interface}
899    FSQLDialect := 3;
900    FDatabaseName := DatabaseName;
901 +  SetLength(FUserCharSetMap,0);
902 +  ClearCachedInfo;
903 +  FInlineBlobLimit := DefaultMaxInlineBlobLimit;
904    FDPB := DPB;
905    FRaiseExceptionOnConnectError := RaiseExceptionOnConnectError;
906   end;
907  
908 + procedure TFBAttachment.ClearCachedInfo;
909 + begin
910 +  FHasDefaultCharSet := false;
911 +  FAttachmentID := 0;
912 +  FODSMajorVersion := 0;
913 +  FODSMinorVersion := 0;
914 +  FCodePage := CP_NONE;
915 +  FCharSetID := 0;
916 +  FRemoteProtocol := '';
917 +  FAuthMethod := '';
918 +  FSecDatabase := '';
919 +  FHasConnectionInfo := false;
920 + end;
921 +
922   function TFBAttachment.GenerateCreateDatabaseSQL(DatabaseName: AnsiString;  aDPB: IDPB): AnsiString;
923   var CreateParams: AnsiString;
924      DPBItem: IDPBItem;
# Line 170 | Line 963 | begin
963    end;
964   end;
965  
966 + {$IFDEF HASREQEX}
967 + procedure TFBAttachment.DPBFromCreateSQL(CreateSQL: AnsiString);
968 + var RegexObj: TRegExpr;
969 + begin
970 +  FDPB := FFirebirdAPI.AllocateDPB;
971 +  RegexObj := TRegExpr.Create;
972 +  try
973 +    {extact database file spec}
974 +    RegexObj.ModifierG := false; {turn off greedy matches}
975 +    RegexObj.ModifierI := true; {case insensitive match}
976 +    RegexObj.Expression := '^ *CREATE +(DATABASE|SCHEMA) +''.*'' +USER +''(.+)'' PASSWORD +''(.+)''';
977 +    if RegexObj.Exec(CreateSQL) then
978 +    begin
979 +      DPB.Add(isc_dpb_user_name).AsString := system.copy(CreateSQL,RegexObj.MatchPos[2],RegexObj.MatchLen[2]);
980 +      DPB.Add(isc_dpb_password).AsString := system.copy(CreateSQL,RegexObj.MatchPos[3],RegexObj.MatchLen[3]);
981 +    end
982 +    else
983 +    begin
984 +      RegexObj.Expression := '^ *CREATE +(DATABASE|SCHEMA) +(''.*'') +USER +''(.+)''';
985 +      if RegexObj.Exec(CreateSQL) then
986 +        DPB.Add(isc_dpb_user_name).AsString := system.copy(CreateSQL,RegexObj.MatchPos[2],RegexObj.MatchLen[2]);
987 +    end;
988 +  finally
989 +    RegexObj.Free;
990 +  end;
991 +  if FCharSetID > 0 then
992 +    DPB.Add(isc_dpb_lc_ctype).AsString := GetCharSetName(FCharSetID);
993 +  DPB.Add(isc_dpb_set_db_SQL_dialect).setAsByte(FSQLDialect);
994 + end;
995 + {$ELSE}
996 + procedure TFBAttachment.DPBFromCreateSQL(CreateSQL: AnsiString);
997 + begin
998 +  FDPB := FFirebirdAPI.AllocateDPB;
999 +  if FCharSetID > 0 then
1000 +    DPB.Add(isc_dpb_lc_ctype).AsString := GetCharSetName(FCharSetID);
1001 +  DPB.Add(isc_dpb_set_db_SQL_dialect).setAsByte(FSQLDialect);
1002 + end;
1003 + {$ENDIF}
1004 +
1005   procedure TFBAttachment.SetParameters(SQLParams: ISQLParams;
1006    params: array of const);
1007   var i: integer;
# Line 182 | Line 1014 | begin
1014      case params[i].vtype of
1015        vtinteger    :
1016          SQLParams[i].AsInteger := params[i].vinteger;
1017 +      vtInt64:
1018 +        SQLParams[i].AsInt64 := params[i].VInt64^;
1019 +      {$IF declared (vtQWord)}
1020 +      vtQWord:
1021 +        SQLParams[i].AsInt64 := params[i].VQWord^;
1022 +      {$IFEND}
1023        vtboolean    :
1024          SQLParams[i].AsBoolean :=  params[i].vboolean;
1025        vtchar       :
# Line 191 | Line 1029 | begin
1029        vtCurrency:
1030          SQLParams[i].AsDouble := params[i].VCurrency^;
1031        vtString     :
1032 <        SQLParams[i].AsString := params[i].VString^;
1032 >        SQLParams[i].AsString := strpas(PChar(params[i].VString));
1033        vtPChar      :
1034          SQLParams[i].AsString := strpas(params[i].VPChar);
1035        vtAnsiString :
1036 <        SQLParams[i].AsString := AnsiString(params[i].VAnsiString^);
1036 >        SQLParams[i].AsString := strpas(PAnsiChar(params[i].VAnsiString));
1037        vtVariant:
1038          SQLParams[i].AsVariant := params[i].VVariant^;
1039 +      vtWideChar:
1040 +        SQLParams[i].AsString := UTF8Encode(WideCharLenToString(@params[i].VWideChar,1));
1041 +      vtPWideChar:
1042 +        SQLParams[i].AsString := UTF8Encode(strpas(PWideChar(params[i].VPWideChar)));
1043 +      vtWideString:
1044 +        SQLParams[i].AsString := UTF8Encode(strpas(PWideChar(params[i].VWideString)));
1045 +      vtUnicodeString:
1046 +        SQLParams[i].AsString := UTF8Encode(strpas(PWideChar(params[i].VUnicodeString)));
1047      else
1048          IBError(ibxeInvalidVariantType,[nil]);
1049      end;
1050    end;
1051   end;
1052  
1053 + procedure TFBAttachment.SetSQLDialect(aValue: integer);
1054 + begin
1055 +  FSQLDialect := aValue;
1056 + end;
1057 +
1058 + procedure TFBAttachment.UseServerICUChanged;
1059 + begin
1060 +  // Do nothing by default
1061 + end;
1062 +
1063   destructor TFBAttachment.Destroy;
1064   begin
1065    Disconnect(true);
1066    inherited Destroy;
1067   end;
1068  
1069 + procedure TFBAttachment.Disconnect(Force: boolean);
1070 + begin
1071 +  inherited Disconnect(Force);
1072 +  ClearCachedInfo;
1073 + end;
1074 +
1075 + function TFBAttachment.getFirebirdAPI: IFirebirdAPI;
1076 + begin
1077 +  Result := FFirebirdAPI;
1078 + end;
1079 +
1080   function TFBAttachment.getDPB: IDPB;
1081   begin
1082    Result := FDPB;
# Line 217 | Line 1084 | end;
1084  
1085   function TFBAttachment.AllocateBPB: IBPB;
1086   begin
1087 <  Result := TBPB.Create;
1087 >  Result := TBPB.Create(FFirebirdAPI as TFBClientAPI);
1088 > end;
1089 >
1090 > function TFBAttachment.AllocateDIRB: IDIRB;
1091 > begin
1092 >  Result := TDIRB.Create(FFirebirdAPI as TFBClientAPI);
1093   end;
1094  
1095   procedure TFBAttachment.ExecImmediate(TPB: array of byte; sql: AnsiString;
1096    aSQLDialect: integer);
1097 + var tr: ITransaction;
1098   begin
1099 <  ExecImmediate(StartTransaction(TPB,taCommit),sql,aSQLDialect);
1099 >  tr := StartTransaction(TPB,taCommit);
1100 >  try
1101 >    ExecImmediate(tr,sql,aSQLDialect);
1102 >    tr.Commit;
1103 >  except
1104 >    tr.Rollback(true);
1105 >    raise;
1106 >  end;
1107   end;
1108  
1109   procedure TFBAttachment.ExecImmediate(transaction: ITransaction; sql: AnsiString);
# Line 233 | Line 1113 | end;
1113  
1114   procedure TFBAttachment.ExecImmediate(TPB: array of byte; sql: AnsiString);
1115   begin
1116 <  ExecImmediate(StartTransaction(TPB,taCommit),sql,FSQLDialect);
1116 >  ExecImmediate(TPB,sql,FSQLDialect);
1117   end;
1118  
1119   function TFBAttachment.ExecuteSQL(TPB: array of byte; sql: AnsiString;
1120    SQLDialect: integer; params: array of const): IResults;
1121 + var tr: ITransaction;
1122   begin
1123 <  Result := ExecuteSQL(StartTransaction(TPB,taCommit),sql,FSQLDialect,params);
1123 >  tr := StartTransaction(TPB,taCommit);
1124 >  try
1125 >    Result := ExecuteSQL(tr,sql,SQLDialect,params);
1126 >  except
1127 >    tr.Rollback(true);
1128 >    raise;
1129 >  end;
1130   end;
1131  
1132   function TFBAttachment.ExecuteSQL(transaction: ITransaction; sql: AnsiString;
# Line 255 | Line 1142 | end;
1142   function TFBAttachment.ExecuteSQL(TPB: array of byte; sql: AnsiString;
1143    params: array of const): IResults;
1144   begin
1145 <   Result := ExecuteSQL(StartTransaction(TPB,taCommit),sql,params);
1145 >   Result := ExecuteSQL(TPB,sql,FSQLDialect,params);
1146   end;
1147  
1148   function TFBAttachment.ExecuteSQL(transaction: ITransaction; sql: AnsiString;
1149    params: array of const): IResults;
1150   begin
1151 <  with Prepare(transaction,sql,FSQLDialect) do
265 <  begin
266 <    SetParameters(SQLParams,params);
267 <    Result := Execute;
268 <  end;
1151 >  Result := ExecuteSQL(transaction,sql,FSQLDialect,params);
1152   end;
1153  
1154   function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString;
1155 <  aSQLDialect: integer): IResultSet;
1155 >  aSQLDialect: integer; Scrollable: boolean): IResultSet;
1156   begin
1157 <  Result := OpenCursor(transaction,sql,aSQLDialect,[]);
1157 >  Result := OpenCursor(transaction,sql,aSQLDialect,Scrollable,[]);
1158   end;
1159  
1160   function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString;
1161    aSQLDialect: integer; params: array of const): IResultSet;
1162 < var Statement: IStatement;
1162 >
1163   begin
1164 <  CheckHandle;
282 <  Statement := Prepare(transaction,sql,aSQLDialect);
283 <  SetParameters(Statement.SQLParams,params);
284 <  Result := Statement.OpenCursor;
1164 >  Result := OpenCursor(transaction,sql,FSQLDialect,false,params);
1165   end;
1166  
1167 < function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString
1168 <  ): IResultSet;
1167 > function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString;
1168 >  Scrollable: boolean): IResultSet;
1169   begin
1170 <  Result := OpenCursor(transaction,sql,FSQLDialect,[]);
1170 >  Result := OpenCursor(transaction,sql,FSQLDialect,Scrollable,[]);
1171   end;
1172  
1173   function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString;
1174    params: array of const): IResultSet;
1175   begin
1176 <  Result := OpenCursor(transaction,sql,FSQLDialect,params);
1176 >  Result := OpenCursor(transaction,sql,FSQLDialect,false,params);
1177 > end;
1178 >
1179 > function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString;
1180 >  Scrollable: boolean; params: array of const): IResultSet;
1181 > begin
1182 >  Result := OpenCursor(transaction,sql,FSQLDialect,Scrollable,params);
1183 > end;
1184 >
1185 > function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString;
1186 >  aSQLDialect: integer; Scrollable: boolean;
1187 >  params: array of const): IResultSet;
1188 > var Statement: IStatement;
1189 > begin
1190 >  CheckHandle;
1191 >  Statement := Prepare(transaction,sql,aSQLDialect);
1192 >  SetParameters(Statement.SQLParams,params);
1193 >  Result := Statement.OpenCursor(Scrollable);
1194   end;
1195  
1196   function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
1197 <  sql: AnsiString; aSQLDialect: integer): IResultSet;
1197 >  sql: AnsiString; aSQLDialect: integer; Scrollable: boolean): IResultSet;
1198   begin
1199 <  Result := OpenCursor(transaction,sql,aSQLDialect,[]);
1199 >  Result := OpenCursor(transaction,sql,aSQLDialect,Scrollable,[]);
1200    Result.FetchNext;
1201   end;
1202  
# Line 310 | Line 1207 | begin
1207    Result.FetchNext;
1208   end;
1209  
1210 < function TFBAttachment.OpenCursorAtStart(transaction: ITransaction; sql: AnsiString
1211 <  ): IResultSet;
1210 > function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
1211 >  sql: AnsiString; aSQLDialect: integer; Scrollable: boolean;
1212 >  params: array of const): IResultSet;
1213   begin
1214 <  Result := OpenCursorAtStart(transaction,sql,FSQLDialect,[]);
1214 >  Result := OpenCursor(transaction,sql,aSQLDialect,Scrollable,params);
1215 >  Result.FetchNext;
1216 > end;
1217 >
1218 > function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
1219 >  sql: AnsiString; Scrollable: boolean): IResultSet;
1220 > begin
1221 >  Result := OpenCursorAtStart(transaction,sql,FSQLDialect,Scrollable,[]);
1222   end;
1223  
1224   function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
# Line 322 | Line 1227 | begin
1227    Result := OpenCursorAtStart(transaction,sql,FSQLDialect,params);
1228   end;
1229  
1230 < function TFBAttachment.OpenCursorAtStart(sql: AnsiString): IResultSet;
1230 > function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
1231 >  sql: AnsiString; Scrollable: boolean; params: array of const): IResultSet;
1232 > begin
1233 >  Result := OpenCursorAtStart(transaction,sql,FSQLDialect,Scrollable,params);
1234 > end;
1235 >
1236 > function TFBAttachment.OpenCursorAtStart(sql: AnsiString; Scrollable: boolean
1237 >  ): IResultSet;
1238   begin
1239 <  Result := OpenCursorAtStart(sql,[]);
1239 >  Result := OpenCursorAtStart(sql,Scrollable,[]);
1240 > end;
1241 >
1242 > function TFBAttachment.OpenCursorAtStart(sql: AnsiString; Scrollable: boolean;
1243 >  params: array of const): IResultSet;
1244 > var tr: ITransaction;
1245 > begin
1246 >  tr := StartTransaction([isc_tpb_read,isc_tpb_wait,isc_tpb_concurrency],taCommit);
1247 >  try
1248 >    Result := OpenCursorAtStart(tr,sql,FSQLDialect,Scrollable,params);
1249 >  except
1250 >    tr.Rollback(true);
1251 >    raise;
1252 >  end;
1253   end;
1254  
1255   function TFBAttachment.OpenCursorAtStart(sql: AnsiString;
1256    params: array of const): IResultSet;
1257   begin
1258 <  Result := OpenCursorAtStart(StartTransaction([isc_tpb_read,isc_tpb_wait,isc_tpb_concurrency],taCommit),sql,FSQLDialect,params);
1258 >  Result := OpenCursorAtStart(sql,false,params);
1259   end;
1260  
1261 < function TFBAttachment.Prepare(transaction: ITransaction; sql: AnsiString
1262 <  ): IStatement;
1261 > function TFBAttachment.Prepare(transaction: ITransaction; sql: AnsiString;
1262 >  CursorName: AnsiString): IStatement;
1263   begin
1264 <  Result := Prepare(transaction,sql,FSQLDialect);
1264 >  Result := Prepare(transaction,sql,FSQLDialect,CursorName);
1265   end;
1266  
1267   function TFBAttachment.PrepareWithNamedParameters(transaction: ITransaction;
1268 <  sql: AnsiString; GenerateParamNames: boolean): IStatement;
1268 >  sql: AnsiString; GenerateParamNames: boolean; CaseSensitiveParams: boolean;
1269 >  CursorName: AnsiString): IStatement;
1270   begin
1271 <  Result := PrepareWithNamedParameters(transaction,sql,FSQLDialect,GenerateParamNames);
1271 >  Result := PrepareWithNamedParameters(transaction,sql,FSQLDialect,GenerateParamNames,CaseSensitiveParams,CursorName);
1272   end;
1273  
1274   function TFBAttachment.GetEventHandler(Event: AnsiString): IEvents;
# Line 359 | Line 1285 | end;
1285  
1286   function TFBAttachment.GetSQLDialect: integer;
1287   begin
1288 +  NeedDBInfo;
1289    Result := FSQLDialect;
1290   end;
1291  
1292 + function TFBAttachment.GetAttachmentID: integer;
1293 + begin
1294 +  NeedDBInfo;
1295 +  Result := FAttachmentID;
1296 + end;
1297 +
1298 + function TFBAttachment.CreateBlob(transaction: ITransaction; RelationName,
1299 +  ColumnName: AnsiString; BPB: IBPB): IBlob;
1300 + begin
1301 +  Result := CreateBlob(transaction,GetBlobMetaData(Transaction,RelationName,ColumnName),BPB);
1302 + end;
1303 +
1304 + function TFBAttachment.OpenBlob(transaction: ITransaction; RelationName,
1305 +  ColumnName: AnsiString; BlobID: TISC_QUAD; BPB: IBPB): IBlob;
1306 + begin
1307 +  Result := OpenBlob(Transaction,
1308 +                GetBlobMetaData(Transaction,RelationName,ColumnName),
1309 +                BlobID,BPB);
1310 + end;
1311 +
1312   function TFBAttachment.OpenBlob(transaction: ITransaction; Field: ISQLData;
1313    BPB: IBPB): IBlob;
1314   begin
1315    Result := OpenBlob(Transaction,Field.GetBlobMetadata, Field.AsQuad,BPB);
1316   end;
1317  
1318 + function TFBAttachment.CreateArray(transaction: ITransaction; RelationName,
1319 +  ColumnName: AnsiString): IArray;
1320 + begin
1321 +  Result := CreateArray(transaction,GetArrayMetaData(transaction,RelationName,ColumnName));
1322 + end;
1323 +
1324 + function TFBAttachment.OpenArray(transaction: ITransaction; RelationName,
1325 +  ColumnName: AnsiString; ArrayID: TISC_QUAD): IArray;
1326 + begin
1327 +  Result := OpenArray(transaction,
1328 +    GetArrayMetaData(transaction,RelationName,ColumnName),ArrayID);
1329 + end;
1330 +
1331 + function TFBAttachment.GetDBInformation(Requests: array of byte
1332 +  ): IDBInformation;
1333 + var ReqBuffer: PByte;
1334 +    i: integer;
1335 + begin
1336 +  CheckHandle;
1337 +  if Length(Requests) = 1 then
1338 +    Result := GetDBInformation(Requests[0])
1339 +  else
1340 +  begin
1341 +    GetMem(ReqBuffer,Length(Requests));
1342 +    try
1343 +      for i := 0 to Length(Requests) - 1 do
1344 +        ReqBuffer[i] := Requests[i];
1345 +
1346 +      Result := GetDBInfo(ReqBuffer,Length(Requests));
1347 +
1348 +    finally
1349 +      FreeMem(ReqBuffer);
1350 +    end;
1351 +  end;
1352 + end;
1353 +
1354 + function TFBAttachment.GetDBInformation(Request: byte): IDBInformation;
1355 + begin
1356 +  CheckHandle;
1357 +  Result := GetDBInfo(@Request,1);
1358 + end;
1359 +
1360 + function TFBAttachment.GetDBInformation(Requests: IDIRB): IDBInformation;
1361 + begin
1362 +  CheckHandle;
1363 +  with Requests as TDIRB do
1364 +    Result := GetDBInfo(getBuffer,getDataLength);
1365 + end;
1366 +
1367 + function TFBAttachment.GetConnectString: AnsiString;
1368 + begin
1369 +  Result := FDatabaseName;
1370 + end;
1371 +
1372 + function TFBAttachment.GetRemoteProtocol: AnsiString;
1373 + begin
1374 +  NeedConnectionInfo;
1375 +  Result := FRemoteProtocol;
1376 + end;
1377 +
1378 + function TFBAttachment.GetAuthenticationMethod: AnsiString;
1379 + begin
1380 +  NeedConnectionInfo;
1381 +  Result := FAuthMethod;
1382 + end;
1383 +
1384 + function TFBAttachment.GetSecurityDatabase: AnsiString;
1385 + begin
1386 +  NeedConnectionInfo;
1387 +  Result := FSecDatabase;
1388 + end;
1389 +
1390 + function TFBAttachment.GetODSMajorVersion: integer;
1391 + begin
1392 +  NeedDBInfo;
1393 +  Result := FODSMajorVersion;
1394 + end;
1395 +
1396 + function TFBAttachment.GetODSMinorVersion: integer;
1397 + begin
1398 +  NeedDBInfo;
1399 +  Result := FODSMinorVersion;
1400 + end;
1401 +
1402 + function TFBAttachment.GetCharSetID: integer;
1403 + begin
1404 +  NeedConnectionInfo;
1405 +  Result := FCharSetID;
1406 + end;
1407 +
1408 + function TFBAttachment.HasDecFloatSupport: boolean;
1409 + begin
1410 +  Result := false;
1411 + end;
1412 +
1413 + function TFBAttachment.GetInlineBlobLimit: integer;
1414 + begin
1415 +  Result := FInlineBlobLimit;
1416 + end;
1417 +
1418 + procedure TFBAttachment.SetInlineBlobLimit(limit: integer);
1419 + begin
1420 +  if limit > 32*1024 then
1421 +     FInlineBlobLimit := 32*1024
1422 +  else
1423 +    FInlineBlobLimit := limit;
1424 + end;
1425 +
1426 + function TFBAttachment.HasBatchMode: boolean;
1427 + begin
1428 +  Result := false;
1429 + end;
1430 +
1431 + function TFBAttachment.HasTable(aTableName: AnsiString): boolean;
1432 + begin
1433 +  Result := OpenCursorAtStart(
1434 +       'Select count(*) From RDB$RELATIONS Where RDB$RELATION_NAME = ?',
1435 +          [aTableName])[0].AsInteger > 0;
1436 + end;
1437 +
1438 + function TFBAttachment.HasFunction(aFunctionName: AnsiString): boolean;
1439 + begin
1440 +  Result := OpenCursorAtStart(
1441 +       'Select count(*) From RDB$FUNCTIONS Where RDB$FUNCTION_NAME = ?',
1442 +          [aFunctionName])[0].AsInteger > 0;
1443 + end;
1444 +
1445 + function TFBAttachment.HasProcedure(aProcName: AnsiString): boolean;
1446 + begin
1447 +  Result := OpenCursorAtStart(
1448 +       'Select count(*) From RDB$PROCEDURES Where RDB$PROCEDURE_NAME = ?',
1449 +          [aProcName])[0].AsInteger > 0;
1450 + end;
1451 +
1452 + function TFBAttachment.HasDefaultCharSet: boolean;
1453 + begin
1454 +  NeedConnectionInfo;
1455 +  Result := FHasDefaultCharSet
1456 + end;
1457 +
1458 + function TFBAttachment.GetDefaultCharSetID: integer;
1459 + begin
1460 +  NeedConnectionInfo;
1461 +  Result := FCharsetID;
1462 + end;
1463 +
1464 + function TFBAttachment.GetCharsetName(CharSetID: integer): AnsiString;
1465 + var i: integer;
1466 + begin
1467 +  Result := '';
1468 +  if (CharSetID >= Low(CharSetMap)) and (CharSetID <= High(CharSetMap)) and
1469 +                                  (CharSetMap[CharSetID].CharSetID = CharSetID) then
1470 +    begin
1471 +      Result := CharSetMap[CharSetID].CharSetName;
1472 +      Exit;
1473 +    end;
1474 +
1475 +  for i := 0 to Length(FUserCharSetMap) - 1 do
1476 +    if FUserCharSetMap[i].CharSetID = CharSetID then
1477 +    begin
1478 +      Result := FUserCharSetMap[i].CharSetName;
1479 +      Exit;
1480 +    end;
1481 + end;
1482 +
1483 + function TFBAttachment.CharSetID2CodePage(CharSetID: integer;
1484 +  var CodePage: TSystemCodePage): boolean;
1485 + var i: integer;
1486 + begin
1487 +  Result := (CharSetID >= Low(CharSetMap)) and (CharSetID <= High(CharSetMap))
1488 +               and (CharSetMap[CharSetID].CharSetID = CharSetID);
1489 +  if Result then
1490 +    begin
1491 +      CodePage := CharSetMap[CharSetID].CodePage;
1492 +      Result := true;
1493 +      Exit;
1494 +    end;
1495 +
1496 +  for i := 0 to Length(FUserCharSetMap) - 1 do
1497 +    if FUserCharSetMap[i].CharSetID = CharSetID then
1498 +    begin
1499 +      CodePage := FUserCharSetMap[i].CodePage;
1500 +      Result := true;
1501 +      Exit;
1502 +    end;
1503 + end;
1504 +
1505 + function TFBAttachment.CodePage2CharSetID(CodePage: TSystemCodePage;
1506 +  var CharSetID: integer): boolean;
1507 + var i: integer;
1508 + begin
1509 +  Result := false;
1510 +  for i := Low(CharSetMap) to High(CharSetMap) do
1511 +    if (CharSetMap[i].AllowReverseLookup) and (CharSetMap[i].CodePage = CodePage) then
1512 +    begin
1513 +      CharSetID := CharSetMap[i].CharSetID;
1514 +      Result := true;
1515 +      Exit;
1516 +    end;
1517 +
1518 +  for i := 0 to Length(FUserCharSetMap) - 1 do
1519 +    if (FUserCharSetMap[i].AllowReverseLookup) and (FUserCharSetMap[i].CodePage = CodePage) then
1520 +    begin
1521 +      CharSetID := FUserCharSetMap[i].CharSetID;
1522 +      Result := true;
1523 +      Exit;
1524 +    end;
1525 + end;
1526 +
1527 + function TFBAttachment.CharSetName2CharSetID(CharSetName: AnsiString;
1528 +  var CharSetID: integer): boolean;
1529 + var i: integer;
1530 + begin
1531 +  Result := false;
1532 +  for i := Low(CharSetMap) to High(CharSetMap) do
1533 +    if AnsiCompareText(CharSetMap[i].CharSetName, CharSetName) = 0 then
1534 +    begin
1535 +      CharSetID := CharSetMap[i].CharSetID;
1536 +      Result := true;
1537 +      Exit;
1538 +    end;
1539 +
1540 +    for i := 0 to Length(FUserCharSetMap) - 1 do
1541 +      if AnsiCompareText(FUserCharSetMap[i].CharSetName, CharSetName) = 0 then
1542 +      begin
1543 +        CharSetID := FUserCharSetMap[i].CharSetID;
1544 +        Result := true;
1545 +        Exit;
1546 +      end;
1547 + end;
1548 +
1549 + function TFBAttachment.CharSetWidth(CharSetID: integer; var Width: integer
1550 +  ): boolean;
1551 + var i: integer;
1552 + begin
1553 +  Result := (CharSetID >= Low(CharSetMap)) and (CharSetID <= High(CharSetMap))
1554 +               and (CharSetMap[CharSetID].CharSetID = CharSetID);
1555 +  if Result then
1556 +    begin
1557 +      Width := CharSetMap[CharSetID].CharSetWidth;
1558 +      Result := true;
1559 +      Exit;
1560 +    end;
1561 +
1562 +  for i := 0 to Length(FUserCharSetMap) - 1 do
1563 +    if FUserCharSetMap[i].CharSetID = CharSetID then
1564 +    begin
1565 +      Width := FUserCharSetMap[i].CharSetWidth;
1566 +      Result := true;
1567 +      Exit;
1568 +    end;
1569 + end;
1570 +
1571 + const
1572 +  sqlLookupCharSet = 'Select RDB$CHARACTER_SET_ID, RDB$BYTES_PER_CHARACTER From RDB$CHARACTER_SETS '+
1573 +                     'Where RDB$SYSTEM_FLAG = 0 and RDB$CHARACTER_SET_NAME = UPPER(?)';
1574 +
1575 + procedure TFBAttachment.RegisterCharSet(CharSetName: AnsiString;
1576 +  CodePage: TSystemCodePage; AllowReverseLookup: boolean; out CharSetID: integer
1577 +  );
1578 + var CharSets: IResultSet;
1579 +    idx: integer;
1580 + begin
1581 +  if CharSetName2CharSetID(CharSetName,CharSetID) then
1582 +    IBError(ibxeCharacterSetExists,[CharSetName]);
1583 +
1584 +  CharSets := OpenCursorAtStart(sqlLookupCharSet,[CharSetName]);
1585 +  if CharSets.IsEof then
1586 +    IBError(ibxeUnknownUserCharSet,[CharSetName]);
1587 +
1588 +  idx := Length(FUserCharSetMap);
1589 +  SetLength(FUserCharSetMap,idx+1);
1590 +  FUserCharSetMap[idx].AllowReverseLookup := AllowReverseLookup;
1591 +  FUserCharSetMap[idx].CharSetID := CharSets[0].AsInteger;
1592 +  FUserCharSetMap[idx].CharSetName := AnsiUpperCase(CharSetName);
1593 +  FUserCharSetMap[idx].CharSetWidth := CharSets[1].AsInteger;
1594 +  FUserCharSetMap[idx].CodePage := CodePage;
1595 +  CharSetID := CharSets[0].AsInteger;
1596 + end;
1597 +
1598 + function TFBAttachment.GetTimeZoneServices: ITimeZoneServices;
1599 + begin
1600 +  IBError(ibxeNotSupported,[]);
1601 + end;
1602 +
1603 + function TFBAttachment.HasTimeZoneSupport: boolean;
1604 + begin
1605 +  Result := false;
1606 + end;
1607 +
1608 + { TDPBItem }
1609 +
1610 + function TDPBItem.getParamTypeName: AnsiString;
1611 + begin
1612 +  Result := DPBPrefix + DPBConstantNames[getParamType];
1613 + end;
1614 +
1615 + { TDPB }
1616 +
1617 + constructor TDPB.Create(api: TFBClientAPI);
1618 + begin
1619 +  inherited Create(api);
1620 +  FDataLength := 1;
1621 +  FBuffer^ := isc_dpb_version1;
1622 + end;
1623 +
1624 + function TDPB.GetParamTypeName(ParamType: byte): Ansistring;
1625 + begin
1626 +  if ParamType <= isc_dpb_last_dpb_constant then
1627 +    Result := DPBConstantNames[ParamType]
1628 +  else
1629 +    Result := '';
1630 + end;
1631 +
1632 + {$IFNDEF FPC}
1633 + function TDPB.GetDPBParamTypeName(ParamType: byte): Ansistring;
1634 + begin
1635 +  Result := GetParamTypeName(ParamType);
1636 + end;
1637 + {$ENDIF}
1638 +
1639 + function TDPB.LookupItemType(ParamTypeName: AnsiString): byte;
1640 + var i: byte;
1641 + begin
1642 +  Result := 0;
1643 +  ParamTypeName := LowerCase(ParamTypeName);
1644 +  if (Pos(DPBPrefix, ParamTypeName) = 1) then
1645 +    Delete(ParamTypeName, 1, Length(DPBPrefix));
1646 +
1647 +  for i := 1 to isc_dpb_last_dpb_constant do
1648 +    if (ParamTypeName = DPBConstantNames[i]) then
1649 +    begin
1650 +      Result := i;
1651 +      break;
1652 +    end;
1653 + end;
1654 +
1655   end.
1656  

Comparing:
ibx/trunk/fbintf/client/FBAttachment.pas (property svn:eol-style), Revision 56 by tony, Mon Mar 6 10:20:02 2017 UTC vs.
ibx/branches/udr/client/FBAttachment.pas (property svn:eol-style), Revision 389 by tony, Thu Jan 20 23:33:40 2022 UTC

# Line 0 | Line 1
1 + native

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines