ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/client/FBAttachment.pas
Revision: 363
Committed: Tue Dec 7 13:30:05 2021 UTC (2 years, 11 months ago) by tony
Content type: text/x-pascal
Original Path: ibx/branches/journaling/fbintf/client/FBAttachment.pas
File size: 53936 byte(s)
Log Message:
add fbintf

File Contents

# User Rev Content
1 tony 45 (*
2     * Firebird Interface (fbintf). The fbintf components provide a set of
3     * Pascal language bindings for the Firebird API.
4     *
5     * The contents of this file are subject to the Initial Developer's
6     * Public License Version 1.0 (the "License"); you may not use this
7     * file except in compliance with the License. You may obtain a copy
8     * of the License here:
9     *
10     * http://www.firebirdsql.org/index.php?op=doc&id=idpl
11     *
12     * Software distributed under the License is distributed on an "AS
13     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14     * implied. See the License for the specific language governing rights
15     * and limitations under the License.
16     *
17     * The Initial Developer of the Original Code is Tony Whyman.
18     *
19     * The Original Code is (C) 2016 Tony Whyman, MWA Software
20     * (http://www.mwasoftware.co.uk).
21     *
22     * All Rights Reserved.
23     *
24     * Contributor(s): ______________________________________.
25     *
26     *)
27     unit FBAttachment;
28 tony 56 {$IFDEF MSWINDOWS}
29     {$DEFINE WINDOWS}
30     {$ENDIF}
31 tony 45
32     {$IFDEF FPC}
33 tony 56 {$mode delphi}
34 tony 45 {$interfaces COM}
35 tony 118 {$define HASREQEX}
36 tony 45 {$ENDIF}
37    
38     interface
39    
40     uses
41 tony 263 Classes, SysUtils, {$IFDEF WINDOWS} windows, {$ENDIF} IB, FBParamBlock,
42 tony 363 FBActivityMonitor, FBClientAPI, IBUtils;
43 tony 45
44 tony 345 const
45     DefaultMaxInlineBlobLimit = 8192;
46    
47 tony 45 type
48 tony 60 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 tony 45
56 tony 363 { 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 tony 45 { TFBAttachment }
132    
133 tony 363 TFBAttachment = class(TFBJournaling)
134 tony 45 private
135     FDPB: IDPB;
136     FFirebirdAPI: IFirebirdAPI;
137 tony 117 FODSMajorVersion: integer;
138     FODSMinorVersion: integer;
139 tony 60 FUserCharSetMap: array of TCharSetMap;
140 tony 209 FSecDatabase: AnsiString;
141 tony 345 FInlineBlobLimit: integer;
142 tony 45 protected
143 tony 56 FDatabaseName: AnsiString;
144 tony 45 FRaiseExceptionOnConnectError: boolean;
145     FSQLDialect: integer;
146     FHasDefaultCharSet: boolean;
147     FCharSetID: integer;
148     FCodePage: TSystemCodePage;
149 tony 117 FRemoteProtocol: AnsiString;
150 tony 143 FAuthMethod: AnsiString;
151 tony 263 constructor Create(api: TFBClientAPI; DatabaseName: AnsiString; DPB: IDPB;
152 tony 45 RaiseExceptionOnConnectError: boolean);
153     procedure CheckHandle; virtual; abstract;
154 tony 56 function GenerateCreateDatabaseSQL(DatabaseName: AnsiString; aDPB: IDPB): AnsiString;
155 tony 117 procedure GetODSAndConnectionInfo;
156 tony 143 function GetDBInfo(ReqBuffer: PByte; ReqBufLen: integer): IDBInformation; virtual; abstract;
157 tony 117 function IsConnected: boolean; virtual; abstract;
158 tony 45 procedure EndAllTransactions;
159 tony 117 procedure DPBFromCreateSQL(CreateSQL: AnsiString);
160 tony 45 procedure SetParameters(SQLParams: ISQLParams; params: array of const);
161 tony 315 procedure UseServerICUChanged; virtual;
162 tony 45 public
163     destructor Destroy; override;
164 tony 263 function getFirebirdAPI: IFirebirdAPI;
165 tony 45 function getDPB: IDPB;
166     function AllocateBPB: IBPB;
167 tony 143 function AllocateDIRB: IDIRB;
168 tony 363 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 tony 56 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;
176     procedure ExecImmediate(TPB: array of byte; sql: AnsiString); overload;
177     function ExecuteSQL(TPB: array of byte; sql: AnsiString; SQLDialect: integer; params: array of const): IResults; overload;
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;
182 tony 350 Scrollable: boolean=false): IResultSet; overload;
183     function OpenCursor(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
184 tony 45 params: array of const): IResultSet; overload;
185 tony 350 function OpenCursor(transaction: ITransaction; sql: AnsiString; Scrollable: boolean=false): IResultSet; overload;
186 tony 56 function OpenCursor(transaction: ITransaction; sql: AnsiString;
187 tony 45 params: array of const): IResultSet; overload;
188 tony 350 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 tony 56 function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
193 tony 350 Scrollable: boolean=false): IResultSet; overload;
194     function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
195 tony 45 params: array of const): IResultSet; overload;
196 tony 350 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 tony 56 function OpenCursorAtStart(transaction: ITransaction; sql: AnsiString;
200 tony 45 params: array of const): IResultSet; overload;
201 tony 350 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 tony 56 function OpenCursorAtStart(sql: AnsiString;
207 tony 45 params: array of const): IResultSet; overload;
208 tony 350 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 tony 56 function PrepareWithNamedParameters(transaction: ITransaction; sql: AnsiString;
211 tony 270 aSQLDialect: integer; GenerateParamNames: boolean=false;
212 tony 350 CaseSensitiveParams: boolean = false; CursorName: AnsiString=''): IStatement; overload; virtual; abstract;
213 tony 56 function PrepareWithNamedParameters(transaction: ITransaction; sql: AnsiString;
214 tony 270 GenerateParamNames: boolean=false;
215 tony 350 CaseSensitiveParams: boolean = false; CursorName: AnsiString=''): IStatement; overload;
216 tony 45 function GetEventHandler(Events: TStrings): IEvents; overload; virtual; abstract;
217 tony 56 function GetEventHandler(Event: AnsiString): IEvents; overload;
218 tony 45
219     function GetSQLDialect: integer;
220 tony 291 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 tony 56 function OpenBlob(transaction: ITransaction; BlobMetaData: IBlobMetaData; BlobID: TISC_QUAD; BPB: IBPB=nil): IBlob; overload; virtual; abstract;
223 tony 291 function OpenBlob(transaction: ITransaction; RelationName, ColumnName: AnsiString; BlobID: TISC_QUAD; BPB: IBPB=nil): IBlob; overload;
224 tony 45 function OpenBlob(transaction: ITransaction; Field: ISQLData; BPB: IBPB=nil): IBlob; overload;
225 tony 291 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 tony 45 property SQLDialect: integer read FSQLDialect;
231     property DPB: IDPB read FDPB;
232 tony 315 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 tony 363 function GetAttachmentID: integer;
237 tony 315 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 tony 345 function GetInlineBlobLimit: integer;
245     procedure SetInlineBlobLimit(limit: integer);
246     function HasBatchMode: boolean; virtual;
247 tony 363 function HasTable(aTableName: AnsiString): boolean;
248 tony 315
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    
265     public
266     {Time Zone Support}
267     function GetTimeZoneServices: ITimeZoneServices; virtual;
268     function HasTimeZoneSupport: boolean; virtual;
269    
270 tony 45 end;
271    
272 tony 315 { 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 tony 345 function GetParamTypeName(ParamType: byte): Ansistring;
287     {$IFDEF FPC}
288     function IDPB.GetDPBParamTypeName = GetParamTypeName;
289     {$ELSE}
290 tony 315 function GetDPBParamTypeName(ParamType: byte): Ansistring;
291 tony 345 {$ENDIF}
292 tony 315 end;
293    
294 tony 45 implementation
295    
296 tony 363 uses FBMessages, IBErrorCodes, FBTransaction {$IFDEF HASREQEX}, RegExpr{$ENDIF};
297 tony 45
298 tony 60 const
299 tony 363 {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 tony 60 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 tony 315 const
397     isc_dpb_last_dpb_constant = isc_dpb_decfloat_traps;
398 tony 60
399 tony 315 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 tony 60
497 tony 363 type
498 tony 60
499 tony 363 { TQueryProcessor }
500 tony 315
501 tony 363 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 tony 45 { TFBAttachment }
758    
759 tony 117 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 tony 143 FCharSetID := 0;
781     FRemoteProtocol := '';
782     FAuthMethod := 'Legacy_Auth';
783 tony 209 FSecDatabase := 'Default';
784 tony 143 if FODSMajorVersion > 11 then
785 tony 117 begin
786     Stmt := Prepare(StartTransaction([isc_tpb_read,isc_tpb_nowait,isc_tpb_concurrency],taCommit),
787 tony 209 'Select MON$CHARACTER_SET_ID, MON$REMOTE_PROTOCOL, MON$AUTH_METHOD, MON$SEC_DATABASE From MON$ATTACHMENTS, MON$DATABASE '+
788 tony 315 'Where MON$ATTACHMENT_ID = CURRENT_CONNECTION ');
789 tony 143 ResultSet := Stmt.OpenCursor;
790     if ResultSet.FetchNext then
791     begin
792     FCharSetID := ResultSet[0].AsInteger;
793 tony 209 FRemoteProtocol := Trim(ResultSet[1].AsString);
794     FAuthMethod := Trim(ResultSet[2].AsString);
795     FSecDatabase := Trim(ResultSet[3].AsString);
796 tony 143 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 tony 117 '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 tony 209 FRemoteProtocol := Trim(ResultSet[1].AsString);
809 tony 117 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 tony 143 case GetProtocol(FDatabaseName) of
818     TCP: FRemoteProtocol := 'TCPv4';
819     Local: FRemoteProtocol := '';
820     NamedPipe: FRemoteProtocol := 'Netbui';
821     SPX: FRemoteProtocol := 'SPX'
822     end;
823 tony 117 end;
824     FHasDefaultCharSet := CharSetID2CodePage(FCharSetID,FCodePage) and (FCharSetID > 1);
825     end;
826    
827 tony 263 constructor TFBAttachment.Create(api: TFBClientAPI; DatabaseName: AnsiString;
828     DPB: IDPB; RaiseExceptionOnConnectError: boolean);
829 tony 45 begin
830     inherited Create;
831 tony 263 FFirebirdAPI := api.GetAPI; {Keep reference to interface}
832 tony 45 FSQLDialect := 3;
833     FDatabaseName := DatabaseName;
834     FDPB := DPB;
835 tony 60 SetLength(FUserCharSetMap,0);
836 tony 45 FRaiseExceptionOnConnectError := RaiseExceptionOnConnectError;
837 tony 117 FODSMajorVersion := 0;
838     FODSMinorVersion := 0;
839 tony 345 FInlineBlobLimit := DefaultMaxInlineBlobLimit;
840 tony 45 end;
841    
842 tony 56 function TFBAttachment.GenerateCreateDatabaseSQL(DatabaseName: AnsiString; aDPB: IDPB): AnsiString;
843     var CreateParams: AnsiString;
844 tony 45 DPBItem: IDPBItem;
845     begin
846     CreateParams := '';
847    
848     if aDPB <> nil then
849     begin
850     DPBItem := aDPB.Find(isc_dpb_user_name);
851     if DPBItem <> nil then
852 tony 56 CreateParams := CreateParams + ' USER ''' + DPBItem.AsString + '''';
853 tony 45
854     DPBItem := aDPB.Find(isc_dpb_password);
855     if DPBItem <> nil then
856 tony 56 CreateParams := CreateParams + ' Password ''' + DPBItem.AsString + '''';
857 tony 45
858     DPBItem := aDPB.Find(isc_dpb_page_size);
859     if DPBItem <> nil then
860 tony 56 CreateParams := CreateParams + ' PAGE_SIZE ' + DPBItem.AsString;
861 tony 45
862     DPBItem := aDPB.Find(isc_dpb_lc_ctype);
863     if DPBItem <> nil then
864 tony 56 CreateParams := CreateParams + ' DEFAULT CHARACTER SET ' + DPBItem.AsString;
865 tony 45
866     DPBItem := aDPB.Find(isc_dpb_sql_dialect);
867     if DPBItem <> nil then
868     FSQLDialect := DPBItem.AsInteger;
869     end;
870    
871     Result := 'CREATE DATABASE ''' + DatabaseName + ''' ' + CreateParams; {do not localize}
872     end;
873    
874     procedure TFBAttachment.EndAllTransactions;
875     var i: integer;
876     intf: TInterfacedObject;
877     begin
878     for i := 0 to InterfaceCount - 1 do
879     begin
880     intf := GetInterface(i);
881     if (intf <> nil) and (intf is TFBTransaction) then
882     TFBTransaction(intf).DoDefaultTransactionEnd(true);
883     end;
884     end;
885    
886 tony 118 {$IFDEF HASREQEX}
887 tony 117 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 tony 118 {$ELSE}
916     procedure TFBAttachment.DPBFromCreateSQL(CreateSQL: AnsiString);
917 tony 119 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 tony 118 end;
923     {$ENDIF}
924 tony 117
925 tony 45 procedure TFBAttachment.SetParameters(SQLParams: ISQLParams;
926     params: array of const);
927     var i: integer;
928     begin
929     if SQLParams.Count <> Length(params) then
930     IBError(ibxeInvalidParamCount,[SQLParams.Count,Length(params)]);
931    
932     for i := 0 to High(params) do
933     begin
934     case params[i].vtype of
935     vtinteger :
936     SQLParams[i].AsInteger := params[i].vinteger;
937 tony 70 vtInt64:
938     SQLParams[i].AsInt64 := params[i].VInt64^;
939     {$IF declared (vtQWord)}
940     vtQWord:
941     SQLParams[i].AsInt64 := params[i].VQWord^;
942     {$IFEND}
943 tony 45 vtboolean :
944     SQLParams[i].AsBoolean := params[i].vboolean;
945     vtchar :
946     SQLParams[i].AsString := params[i].vchar;
947     vtextended :
948     SQLParams[i].AsDouble := params[i].VExtended^;
949     vtCurrency:
950     SQLParams[i].AsDouble := params[i].VCurrency^;
951     vtString :
952 tony 70 SQLParams[i].AsString := strpas(PChar(params[i].VString));
953 tony 45 vtPChar :
954     SQLParams[i].AsString := strpas(params[i].VPChar);
955     vtAnsiString :
956 tony 70 SQLParams[i].AsString := strpas(PAnsiChar(params[i].VAnsiString));
957 tony 45 vtVariant:
958     SQLParams[i].AsVariant := params[i].VVariant^;
959 tony 70 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 tony 45 else
968     IBError(ibxeInvalidVariantType,[nil]);
969     end;
970     end;
971     end;
972    
973 tony 315 procedure TFBAttachment.UseServerICUChanged;
974     begin
975     // Do nothing by default
976     end;
977    
978 tony 45 destructor TFBAttachment.Destroy;
979     begin
980     Disconnect(true);
981     inherited Destroy;
982     end;
983    
984 tony 263 function TFBAttachment.getFirebirdAPI: IFirebirdAPI;
985     begin
986     Result := FFirebirdAPI;
987     end;
988    
989 tony 45 function TFBAttachment.getDPB: IDPB;
990     begin
991     Result := FDPB;
992     end;
993    
994     function TFBAttachment.AllocateBPB: IBPB;
995     begin
996 tony 263 Result := TBPB.Create(FFirebirdAPI as TFBClientAPI);
997 tony 45 end;
998    
999 tony 143 function TFBAttachment.AllocateDIRB: IDIRB;
1000     begin
1001 tony 263 Result := TDIRB.Create(FFirebirdAPI as TFBClientAPI);
1002 tony 143 end;
1003    
1004 tony 56 procedure TFBAttachment.ExecImmediate(TPB: array of byte; sql: AnsiString;
1005 tony 45 aSQLDialect: integer);
1006     begin
1007     ExecImmediate(StartTransaction(TPB,taCommit),sql,aSQLDialect);
1008     end;
1009    
1010 tony 56 procedure TFBAttachment.ExecImmediate(transaction: ITransaction; sql: AnsiString);
1011 tony 45 begin
1012     ExecImmediate(transaction,sql,FSQLDialect);
1013     end;
1014    
1015 tony 56 procedure TFBAttachment.ExecImmediate(TPB: array of byte; sql: AnsiString);
1016 tony 45 begin
1017     ExecImmediate(StartTransaction(TPB,taCommit),sql,FSQLDialect);
1018     end;
1019    
1020 tony 56 function TFBAttachment.ExecuteSQL(TPB: array of byte; sql: AnsiString;
1021 tony 45 SQLDialect: integer; params: array of const): IResults;
1022     begin
1023 tony 117 Result := ExecuteSQL(StartTransaction(TPB,taCommit),sql,SQLDialect,params);
1024 tony 45 end;
1025    
1026 tony 56 function TFBAttachment.ExecuteSQL(transaction: ITransaction; sql: AnsiString;
1027 tony 45 SQLDialect: integer; params: array of const): IResults;
1028     begin
1029     with Prepare(transaction,sql,SQLDialect) do
1030     begin
1031     SetParameters(SQLParams,params);
1032     Result := Execute;
1033     end;
1034     end;
1035    
1036 tony 56 function TFBAttachment.ExecuteSQL(TPB: array of byte; sql: AnsiString;
1037 tony 45 params: array of const): IResults;
1038     begin
1039     Result := ExecuteSQL(StartTransaction(TPB,taCommit),sql,params);
1040     end;
1041    
1042 tony 56 function TFBAttachment.ExecuteSQL(transaction: ITransaction; sql: AnsiString;
1043 tony 45 params: array of const): IResults;
1044     begin
1045     with Prepare(transaction,sql,FSQLDialect) do
1046     begin
1047     SetParameters(SQLParams,params);
1048     Result := Execute;
1049     end;
1050     end;
1051    
1052 tony 56 function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString;
1053 tony 350 aSQLDialect: integer; Scrollable: boolean): IResultSet;
1054 tony 45 begin
1055 tony 350 Result := OpenCursor(transaction,sql,aSQLDialect,Scrollable,[]);
1056 tony 45 end;
1057    
1058 tony 56 function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString;
1059 tony 45 aSQLDialect: integer; params: array of const): IResultSet;
1060 tony 350
1061 tony 45 begin
1062 tony 350 Result := OpenCursor(transaction,sql,FSQLDialect,false,params);
1063 tony 45 end;
1064    
1065 tony 350 function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString;
1066     Scrollable: boolean): IResultSet;
1067 tony 45 begin
1068 tony 350 Result := OpenCursor(transaction,sql,FSQLDialect,Scrollable,[]);
1069 tony 45 end;
1070    
1071 tony 56 function TFBAttachment.OpenCursor(transaction: ITransaction; sql: AnsiString;
1072 tony 45 params: array of const): IResultSet;
1073     begin
1074 tony 350 Result := OpenCursor(transaction,sql,FSQLDialect,false,params);
1075 tony 45 end;
1076    
1077 tony 350 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 tony 45 function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
1095 tony 350 sql: AnsiString; aSQLDialect: integer; Scrollable: boolean): IResultSet;
1096 tony 45 begin
1097 tony 350 Result := OpenCursor(transaction,sql,aSQLDialect,Scrollable,[]);
1098 tony 45 Result.FetchNext;
1099     end;
1100    
1101     function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
1102 tony 56 sql: AnsiString; aSQLDialect: integer; params: array of const): IResultSet;
1103 tony 45 begin
1104     Result := OpenCursor(transaction,sql,aSQLDialect,params);
1105     Result.FetchNext;
1106     end;
1107    
1108 tony 350 function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
1109     sql: AnsiString; aSQLDialect: integer; Scrollable: boolean;
1110     params: array of const): IResultSet;
1111 tony 45 begin
1112 tony 350 Result := OpenCursor(transaction,sql,aSQLDialect,Scrollable,params);
1113     Result.FetchNext;
1114 tony 45 end;
1115    
1116     function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
1117 tony 350 sql: AnsiString; Scrollable: boolean): IResultSet;
1118     begin
1119     Result := OpenCursorAtStart(transaction,sql,FSQLDialect,Scrollable,[]);
1120     end;
1121    
1122     function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
1123 tony 56 sql: AnsiString; params: array of const): IResultSet;
1124 tony 45 begin
1125     Result := OpenCursorAtStart(transaction,sql,FSQLDialect,params);
1126     end;
1127    
1128 tony 350 function TFBAttachment.OpenCursorAtStart(transaction: ITransaction;
1129     sql: AnsiString; Scrollable: boolean; params: array of const): IResultSet;
1130 tony 45 begin
1131 tony 350 Result := OpenCursorAtStart(transaction,sql,FSQLDialect,Scrollable,params);
1132 tony 45 end;
1133    
1134 tony 350 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 tony 56 function TFBAttachment.OpenCursorAtStart(sql: AnsiString;
1148 tony 45 params: array of const): IResultSet;
1149     begin
1150 tony 350 Result := OpenCursorAtStart(StartTransaction([isc_tpb_read,isc_tpb_wait,isc_tpb_concurrency],taCommit),sql,FSQLDialect,
1151     false,params);
1152 tony 45 end;
1153    
1154 tony 350 function TFBAttachment.Prepare(transaction: ITransaction; sql: AnsiString;
1155     CursorName: AnsiString): IStatement;
1156 tony 45 begin
1157 tony 350 Result := Prepare(transaction,sql,FSQLDialect,CursorName);
1158 tony 45 end;
1159    
1160     function TFBAttachment.PrepareWithNamedParameters(transaction: ITransaction;
1161 tony 350 sql: AnsiString; GenerateParamNames: boolean; CaseSensitiveParams: boolean;
1162     CursorName: AnsiString): IStatement;
1163 tony 45 begin
1164 tony 350 Result := PrepareWithNamedParameters(transaction,sql,FSQLDialect,GenerateParamNames,CaseSensitiveParams,CursorName);
1165 tony 45 end;
1166    
1167 tony 56 function TFBAttachment.GetEventHandler(Event: AnsiString): IEvents;
1168 tony 45 var S: TStringList;
1169     begin
1170     S := TStringList.Create;
1171     try
1172     S.Add(Event);
1173     Result := GetEventHandler(S);
1174     finally
1175     S.Free;
1176     end;
1177     end;
1178    
1179     function TFBAttachment.GetSQLDialect: integer;
1180     begin
1181     Result := FSQLDialect;
1182     end;
1183    
1184 tony 291 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 tony 45 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 tony 291 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 tony 143 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 tony 363 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 tony 117 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 tony 143 function TFBAttachment.GetAuthenticationMethod: AnsiString;
1274     begin
1275     Result := FAuthMethod;
1276     end;
1277    
1278 tony 209 function TFBAttachment.GetSecurityDatabase: AnsiString;
1279     begin
1280     Result := FSecDatabase;
1281     end;
1282    
1283 tony 117 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 tony 315 function TFBAttachment.HasDecFloatSupport: boolean;
1294     begin
1295     Result := false;
1296     end;
1297    
1298 tony 345 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 tony 363 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 tony 109 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 tony 60 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 tony 233 if AnsiCompareText(CharSetMap[i].CharSetName, CharSetName) = 0 then
1403 tony 60 begin
1404     CharSetID := CharSetMap[i].CharSetID;
1405     Result := true;
1406     Exit;
1407     end;
1408    
1409     for i := 0 to Length(FUserCharSetMap) - 1 do
1410 tony 233 if AnsiCompareText(FUserCharSetMap[i].CharSetName, CharSetName) = 0 then
1411 tony 60 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 tony 315 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 tony 345 function TDPB.GetParamTypeName(ParamType: byte): Ansistring;
1494 tony 315 begin
1495     if ParamType <= isc_dpb_last_dpb_constant then
1496     Result := DPBConstantNames[ParamType]
1497     else
1498     Result := '';
1499     end;
1500    
1501 tony 345 {$IFNDEF FPC}
1502     function TDPB.GetDPBParamTypeName(ParamType: byte): Ansistring;
1503     begin
1504     Result := GetParamTypeName(ParamType);
1505     end;
1506     {$ENDIF}
1507    
1508 tony 315 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 tony 45 end.
1525