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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines