ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/client/FBAttachment.pas
Revision: 371
Committed: Wed Jan 5 15:21:22 2022 UTC (2 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 55643 byte(s)
Log Message:
Beta Release 0.1

File Contents

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