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

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