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 370 by tony, Wed Jan 5 14:59:15 2022 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines