ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/fbintf/client/FBOutputBlock.pas
Revision: 143
Committed: Fri Feb 23 12:11:21 2018 UTC (6 years, 2 months ago) by tony
Content type: text/x-pascal
File size: 33999 byte(s)
Log Message:
Fixes Merged

File Contents

# User Rev Content
1 tony 45 (*
2     * Firebird Interface (fbintf). The fbintf components provide a set of
3     * Pascal language bindings for the Firebird API.
4     *
5     * The contents of this file are subject to the Initial Developer's
6     * Public License Version 1.0 (the "License"); you may not use this
7     * file except in compliance with the License. You may obtain a copy
8     * of the License here:
9     *
10     * http://www.firebirdsql.org/index.php?op=doc&id=idpl
11     *
12     * Software distributed under the License is distributed on an "AS
13     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14     * implied. See the License for the specific language governing rights
15     * and limitations under the License.
16     *
17     * The Initial Developer of the Original Code is Tony Whyman.
18     *
19     * The Original Code is (C) 2016 Tony Whyman, MWA Software
20     * (http://www.mwasoftware.co.uk).
21     *
22     * All Rights Reserved.
23     *
24     * Contributor(s): ______________________________________.
25     *
26     *)
27     unit FBOutputBlock;
28 tony 56 {$IFDEF MSWINDOWS}
29     {$DEFINE WINDOWS}
30     {$ENDIF}
31 tony 45
32     {$IFDEF FPC}
33 tony 56 {$mode delphi}
34 tony 45 {$codepage UTF8}
35     {$interfaces COM}
36     {$ENDIF}
37    
38     { $DEFINE DEBUGOUTPUTBLOCK}
39    
40     interface
41    
42     {Provides common handling for the DB Info results, SQL Info and Service Response Block}
43    
44     uses
45     Classes, SysUtils, FBClientAPI, IB, FBActivityMonitor;
46    
47     const
48     DefaultBufferSize = 32000;
49 tony 143 DBInfoDefaultBufferSize = DefaultBufferSize; {allow for database page}
50 tony 45
51     type
52     TItemDataType = (dtString, dtString2, dtByte, dtBytes, dtInteger, dtIntegerFixed, dtnone,
53 tony 143 dtList,dtSpecial, dtDateTime, dtOctetString);
54 tony 45
55     POutputBlockItemData = ^TOutputBlockItemData;
56     TOutputBlockItemData = record
57     {Describes a Clumplet in the buffer. FBufPtr always points to the clumplet id
58     the rest of the clumplet up to the FSize is data. The data format is
59     given by FDataType, and the data length is given by FDataLength}
60 tony 56 FBufPtr: PByte;
61 tony 45 FDataLength: integer;
62     FSize: integer;
63     FDataType: TItemDataType;
64     FTruncated: boolean;
65     FError: boolean;
66     FSubItems: array of POutputBlockItemData;
67     end;
68    
69     { TOutputBlock }
70    
71     TOutputBlock = class(TFBInterfacedObject)
72     private
73 tony 56 FBuffer: PByte;
74 tony 45 FBufSize: integer;
75     FBufferParsed: boolean;
76     procedure ParseBuffer;
77     {$IFDEF DEBUGOUTPUTBLOCK}
78     procedure FormattedPrint(const aItems: array of POutputBlockItemData;
79 tony 56 Indent: AnsiString);
80 tony 45 {$ENDIF}
81     procedure PrintBuf;
82     protected
83     FIntegerType: TItemDataType;
84     FError: boolean;
85     FTruncated: boolean;
86     FItems: array of POutputBlockItemData;
87     procedure DoParseBuffer; virtual; abstract;
88 tony 56 function AddItem(BufPtr: PByte): POutputBlockItemData;
89     function AddIntegerItem(BufPtr: PByte): POutputBlockItemData;
90     function AddStringItem(BufPtr: PByte): POutputBlockItemData;
91     function AddShortStringItem(BufPtr: PByte): POutputBlockItemData;
92     function AddByteItem(BufPtr: PByte): POutputBlockItemData;
93     function AddBytesItem(BufPtr: PByte): POutputBlockItemData;
94     function AddListItem(BufPtr: PByte): POutputBlockItemData; virtual;
95     function AddSpecialItem(BufPtr: PByte): POutputBlockItemData; virtual;
96 tony 143 function AddDateTimeItem(BufPtr: PByte): POutputBlockItemData;
97     function AddOctetString(BufPtr: PByte): POutputBlockItemData;
98 tony 45 public
99     constructor Create(aSize: integer = DefaultBufferSize);
100     destructor Destroy; override;
101 tony 56 function Buffer: PByte;
102 tony 45 function getBufSize: integer;
103    
104     public
105     function GetCount: integer;
106     function GetItem(index: integer): POutputBlockItemData;
107     function Find(ItemType: byte): POutputBlockItemData;
108     property Count: integer read GetCount;
109     property Items[index: integer]: POutputBlockItemData read getItem; default;
110     end;
111    
112     { TOutputBlockItem }
113    
114     TOutputBlockItem = class(TFBInterfacedObject,IUnknown)
115     private
116     FOwner: TOutputBlock;
117     FOwnerIntf: IUnknown;
118     FItemData: POutputBlockItemData;
119     protected
120     function GetItem(index: integer): POutputBlockItemData;
121     function Find(ItemType: byte): POutputBlockItemData;
122 tony 56 procedure SetString(out S: AnsiString; Buf: PByte; Len: integer;
123 tony 45 CodePage: TSystemCodePage);
124     property ItemData: POutputBlockItemData read FItemData;
125     property Owner: TOutputBlock read FOwner;
126     public
127     constructor Create(AOwner: TOutputBlock; Data: POutputBlockItemData);
128     public
129     function GetCount: integer;
130     function getItemType: byte;
131     function getSize: integer;
132     procedure getRawBytes(var Buffer);
133     function getAsInteger: integer;
134     function getParamType: byte;
135 tony 56 function getAsString: AnsiString;
136 tony 45 function getAsByte: byte;
137     function getAsBytes: TByteArray;
138 tony 143 function getAsDateTime: TDateTime;
139 tony 45 function CopyTo(stream: TStream; count: integer): integer;
140     end;
141    
142 tony 56 TServiceQueryResultSubItem = class(TOutputBlockItem,IServiceQueryResultSubItem);
143    
144 tony 45 { TCustomOutputBlock }
145    
146 tony 56 {$IFDEF FPC}
147     TCustomOutputBlock<_TItem,_IItem> = class(TOutputBlock)
148     {$ELSE}
149     TOutputBlockItemClass = class of TOutputBlockItem;
150     TCustomOutputBlock<_TItem: TOutputBlockItem;_IItem: IUnknown> = class(TOutputBlock)
151     {$ENDIF}
152 tony 45 public
153     function getItem(index: integer): _IItem;
154     function find(ItemType: byte): _IItem;
155     property Items[index: integer]: _IItem read getItem; default;
156     end;
157    
158     { TOutputBlockItemGroup }
159    
160 tony 56 {$IFDEF FPC}
161     TOutputBlockItemGroup<_TItem,_IItem> = class(TOutputBlockItem)
162     {$ELSE}
163     TOutputBlockItemGroup<_TItem: TOutputBlockItem; _IItem: IUnknown> = class(TOutputBlockItem)
164     {$ENDIF}
165 tony 45 public
166     function GetItem(index: integer): _IItem;
167     function Find(ItemType: byte): _IItem;
168     property Items[index: integer]: _IItem read getItem; default;
169     end;
170    
171     { TDBInfoItem }
172    
173 tony 56 {$IFDEF FPC}
174     TDBInfoItem = class;
175    
176     TDBInfoItem = class(TOutputBlockItemGroup<TDBInfoItem,IDBInfoItem>,IDBInfoItem)
177     {$ELSE}
178     TDBInfoItem = class(TOutputBlockItemGroup<TOutputBlockItem,IDBInfoItem>,IDBInfoItem)
179     {$ENDIF}
180 tony 45 public
181 tony 56 procedure DecodeIDCluster(var ConnectionType: integer; var DBFileName, DBSiteName: AnsiString);
182     procedure DecodeVersionString(var Version: byte; var VersionString: AnsiString);
183 tony 45 procedure DecodeUserNames(UserNames: TStrings);
184     function getOperationCounts: TDBOperationCounts;
185 tony 143 end;
186 tony 45
187     { TDBInformation }
188    
189 tony 56 TDBInformation = class(TCustomOutputBlock<TDBInfoItem,IDBInfoItem>, IDBInformation)
190 tony 45 protected
191 tony 56 function AddSpecialItem(BufPtr: PByte): POutputBlockItemData; override;
192 tony 45 procedure DoParseBuffer; override;
193     public
194     constructor Create(aSize: integer=DBInfoDefaultBufferSize);
195 tony 143 {$IFNDEF FPC}
196     function Find(ItemType: byte): IDBInfoItem;
197     {$ENDIF}
198 tony 45 end;
199    
200     { TServiceQueryResultItem }
201    
202 tony 56 TServiceQueryResultItem = class(TOutputBlockItemGroup<TServiceQueryResultSubItem,IServiceQueryResultSubItem>,
203 tony 45 IServiceQueryResultItem);
204    
205     { TServiceQueryResults }
206    
207 tony 56 TServiceQueryResults = class(TCustomOutputBlock<TServiceQueryResultItem,IServiceQueryResultItem>, IServiceQueryResults)
208 tony 45 protected
209 tony 56 function AddListItem(BufPtr: PByte): POutputBlockItemData; override;
210     function AddSpecialItem(BufPtr: PByte): POutputBlockItemData; override;
211 tony 45 procedure DoParseBuffer; override;
212 tony 143 {$IFNDEF FPC}
213     public
214     function Find(ItemType: byte): IServiceQueryResultItem;
215     {$ENDIF}
216 tony 45 end;
217    
218 tony 56
219 tony 45 { ISQLInfoItem }
220    
221 tony 56 ISQLInfoSubItem = interface
222     ['{39852ee4-4851-44df-8dc0-26b991250098}']
223 tony 45 function getItemType: byte;
224     function getSize: integer;
225 tony 56 function getAsString: AnsiString;
226 tony 45 function getAsInteger: integer;
227 tony 56 end;
228    
229     ISQLInfoItem = interface(ISQLInfoSubItem)
230     ['{34e3c39d-fe4f-4211-a7e3-0266495a359d}']
231 tony 45 function GetCount: integer;
232 tony 56 function GetItem(index: integer): ISQLInfoSubItem;
233     function Find(ItemType: byte): ISQLInfoSubItem;
234 tony 45 property Count: integer read GetCount;
235 tony 56 property Items[index: integer]: ISQLInfoSubItem read getItem; default;
236 tony 45 end;
237    
238     {ISQLInfoResults}
239    
240     ISQLInfoResults = interface
241 tony 56 ['{0b3fbe20-6f80-44e7-85ef-e708bc1f2043}']
242 tony 45 function GetCount: integer;
243     function GetItem(index: integer): ISQLInfoItem;
244     function Find(ItemType: byte): ISQLInfoItem;
245     property Count: integer read GetCount;
246     property Items[index: integer]: ISQLInfoItem read getItem; default;
247     end;
248    
249 tony 56 TSQLInfoResultsSubItem = class(TOutputBlockItem,ISQLInfoSubItem);
250 tony 45
251     { TSQLInfoResultsItem }
252    
253 tony 56 TSQLInfoResultsItem = class(TOutputBlockItemGroup<TSQLInfoResultsSubItem,ISQLInfoSubItem>,ISQLInfoItem);
254 tony 45
255     { TSQLInfoResultsBuffer }
256    
257 tony 56 TSQLInfoResultsBuffer = class(TCustomOutputBlock<TSQLInfoResultsItem,ISQLInfoItem>, ISQLInfoResults)
258 tony 45 protected
259 tony 56 function AddListItem(BufPtr: PByte): POutputBlockItemData; override;
260 tony 45 procedure DoParseBuffer; override;
261     public
262     constructor Create(aSize: integer = 1024);
263     end;
264    
265 tony 56 IBlobInfoItem = interface
266     ['{3a55e558-b97f-4cf3-af95-53b84f4d9a65}']
267     function getItemType: byte;
268     function getSize: integer;
269     function getAsString: AnsiString;
270     function getAsInteger: integer;
271     end;
272    
273     IBlobInfo = interface
274     ['{8a340109-f600-4d26-ab1d-e0be2c759f1c}']
275     function GetCount: integer;
276     function GetItem(index: integer): IBlobInfoItem;
277     function Find(ItemType: byte): IBlobInfoItem;
278     property Count: integer read GetCount;
279     property Items[index: integer]: IBlobInfoItem read getItem; default;
280     end;
281    
282     {$IFDEF FPC}
283     TBlobInfoItem = class;
284    
285     TBlobInfoItem = class(TOutputBlockItemGroup<TBlobInfoItem,IBlobInfoItem>,IBlobInfoItem)
286     {$ELSE}
287     TBlobInfoItem = class(TOutputBlockItemGroup<TOutputBlockItem,IBlobInfoItem>,IBlobInfoItem)
288     {$ENDIF}
289    
290     end;
291    
292     { TBlobInfo }
293    
294     TBlobInfo = class(TCustomOutputBlock<TBlobInfoItem,IBlobInfoItem>, IBlobInfo)
295     protected
296     procedure DoParseBuffer; override;
297     public
298     constructor Create(aSize: integer=DBInfoDefaultBufferSize);
299     end;
300    
301 tony 45 implementation
302    
303 tony 56 uses FBMessages {$IFNDEF FPC}, TypInfo {$ENDIF};
304 tony 45
305 tony 56 {$IFDEF FPC}
306 tony 45 { TOutputBlockItemGroup }
307    
308 tony 56 function TOutputBlockItemGroup<_TItem,_IItem>.GetItem(index: integer): _IItem;
309 tony 45 var P: POutputBlockItemData;
310     begin
311     P := inherited getItem(index);
312     Result := _TItem.Create(self.Owner,P);
313     end;
314    
315 tony 56 function TOutputBlockItemGroup<_TItem,_IItem>.Find(ItemType: byte): _IItem;
316 tony 45 var P: POutputBlockItemData;
317     begin
318     P := inherited Find(ItemType);
319     Result := _TItem.Create(self.Owner,P);
320     end;
321    
322     { TCustomOutputBlock }
323    
324 tony 56 function TCustomOutputBlock<_TItem,_IItem>.getItem(index: integer): _IItem;
325 tony 45 var P: POutputBlockItemData;
326     begin
327     P := inherited getItem(index);
328     Result := _TItem.Create(self,P)
329     end;
330    
331 tony 56 function TCustomOutputBlock<_TItem,_IItem>.find(ItemType: byte): _IItem;
332 tony 45 var P: POutputBlockItemData;
333     begin
334     P := inherited Find(ItemType);
335 tony 143 if P = nil then
336     Result := nil
337     else
338     Result := _TItem.Create(self,P)
339 tony 45 end;
340    
341 tony 56 {$ELSE}
342    
343 tony 45 { TOutputBlockItemGroup }
344    
345 tony 56 function TOutputBlockItemGroup<_TItem,_IItem>.GetItem(index: integer): _IItem;
346     var P: POutputBlockItemData;
347     Obj: TOutputBlockItem;
348     begin
349     P := inherited getItem(index);
350     Obj := TOutputBlockItemClass(_TItem).Create(self.Owner,P);
351     if Obj.QueryInterface(GetTypeData(TypeInfo(_IItem))^.Guid,Result) <> 0 then
352     IBError(ibxeInterfaceNotSupported,[GuidToString(GetTypeData(TypeInfo(_IItem))^.Guid)]);
353     end;
354    
355     function TOutputBlockItemGroup<_TItem,_IItem>.Find(ItemType: byte): _IItem;
356     var P: POutputBlockItemData;
357     Obj: TOutputBlockItem;
358     begin
359     P := inherited Find(ItemType);
360 tony 143 if P = nil then
361     Result := Default(_IITEM)
362     else
363     begin
364     Obj := TOutputBlockItemClass(_TItem).Create(self.Owner,P);
365     if Obj.QueryInterface(GetTypeData(TypeInfo(_IItem))^.Guid,Result) <> 0 then
366     IBError(ibxeInterfaceNotSupported,[GuidToString(GetTypeData(TypeInfo(_IItem))^.Guid)]);
367     end;
368 tony 56 end;
369    
370     { TCustomOutputBlock }
371    
372     function TCustomOutputBlock<_TItem,_IItem>.getItem(index: integer): _IItem;
373     var P: POutputBlockItemData;
374     Obj: TOutputBlockItem;
375     begin
376     P := inherited getItem(index);
377     Obj := TOutputBlockItemClass(_TItem).Create(self,P);
378     if Obj.QueryInterface(GetTypeData(TypeInfo(_IItem))^.Guid,Result) <> 0 then
379     IBError(ibxeInterfaceNotSupported,[GuidToString(GetTypeData(TypeInfo(_IItem))^.Guid)]);
380     end;
381    
382     function TCustomOutputBlock<_TItem,_IItem>.find(ItemType: byte): _IItem;
383     var P: POutputBlockItemData;
384     Obj: TOutputBlockItem;
385     begin
386     P := inherited Find(ItemType);
387     Obj := TOutputBlockItemClass(_TItem).Create(self,P);
388     if Obj.QueryInterface(GetTypeData(TypeInfo(_IItem))^.Guid,Result) <> 0 then
389     IBError(ibxeInterfaceNotSupported,[GuidToString(GetTypeData(TypeInfo(_IItem))^.Guid)]);
390     end;
391    
392     {$ENDIF}
393    
394     { TOutputBlockItem }
395    
396 tony 45 function TOutputBlockItem.GetCount: integer;
397     begin
398     Result := Length(FItemData^.FSubItems);
399     end;
400    
401     function TOutputBlockItem.GetItem(index: integer): POutputBlockItemData;
402     begin
403     if (index >= 0) and (index < Length(FItemData^.FSubItems)) then
404     Result := FItemData^.FSubItems[index]
405     else
406     with FirebirdClientAPI do
407     IBError(ibxeOutputBlockIndexError,[index]);
408     end;
409    
410     function TOutputBlockItem.Find(ItemType: byte): POutputBlockItemData;
411     var i: integer;
412     begin
413     Result := nil;
414     for i := 0 to GetCount - 1 do
415 tony 56 if byte(FItemData^.FSubItems[i]^.FBufPtr^) = ItemType then
416 tony 45 begin
417     Result := FItemData^.FSubItems[i];
418     Exit;
419     end;
420     end;
421    
422     { TOutputBlockItem }
423    
424 tony 56 procedure TOutputBlockItem.SetString(out S: AnsiString; Buf: PByte;
425     Len: integer; CodePage: TSystemCodePage);
426 tony 45 var rs: RawByteString;
427     begin
428 tony 56 system.SetString(rs,PAnsiChar(Buf),len);
429 tony 45 SetCodePage(rs,CodePage,false);
430     S := rs;
431     end;
432    
433     constructor TOutputBlockItem.Create(AOwner: TOutputBlock;
434     Data: POutputBlockItemData);
435     begin
436     inherited Create;
437     FOwner := AOwner;
438     FOwnerIntf := AOwner;
439     FItemData := Data;
440     end;
441    
442     function TOutputBlockItem.getItemType: byte;
443     begin
444     Result := byte(FItemData^.FBufPtr^);
445     end;
446    
447     function TOutputBlockItem.getSize: integer;
448     begin
449 tony 143 if FItemData = nil then
450     Result := 0
451     else
452     Result := FItemData^.FDataLength;
453 tony 45 end;
454    
455     procedure TOutputBlockItem.getRawBytes(var Buffer);
456     begin
457     with FItemData^ do
458     Move(FBufPtr^,Buffer,FDatalength);
459     end;
460    
461     function TOutputBlockItem.getAsInteger: integer;
462     var len: integer;
463     begin
464     with FItemData^ do
465     case FDataType of
466     dtIntegerFixed:
467     with FirebirdClientAPI do
468     Result := DecodeInteger(FBufPtr+1,4);
469    
470     dtByte,
471     dtInteger:
472     with FirebirdClientAPI do
473     begin
474     len := DecodeInteger(FBufPtr+1,2);
475     Result := DecodeInteger(FBufPtr+3,len);
476     end;
477     else
478     IBError(ibxeOutputBlockTypeError,[nil]);
479     end;
480     end;
481    
482     function TOutputBlockItem.getParamType: byte;
483     begin
484     Result := byte(FItemData^.FBufPtr^)
485     end;
486    
487 tony 56 function TOutputBlockItem.getAsString: AnsiString;
488 tony 45 var len: integer;
489     begin
490     Result := '';
491     with FItemData^ do
492     case FDataType of
493     dtInteger:
494     Result := IntToStr(getAsInteger);
495     dtByte:
496     Result := IntToStr(getAsByte);
497     dtString:
498     begin
499     len := byte((FBufPtr+1)^);
500     SetString(Result,FBufPtr+2,len,CP_ACP);
501     end;
502     dtString2:
503     begin
504     with FirebirdClientAPI do
505     len := DecodeInteger(FBufPtr+1,2);
506     SetString(Result,FBufPtr+3,len,CP_ACP);
507     end;
508 tony 143 dtOctetString:
509     begin
510     with FirebirdClientAPI do
511     len := DecodeInteger(FBufPtr+1,2);
512     SetString(Result,FBufPtr+3,len,CP_NONE);
513     end;
514 tony 45 else
515     IBError(ibxeOutputBlockTypeError,[nil]);
516     end;
517     end;
518    
519     function TOutputBlockItem.getAsByte: byte;
520     begin
521     with FItemData^ do
522     if FDataType = dtByte then
523     Result := byte((FBufPtr+2)^)
524     else
525     IBError(ibxeOutputBlockTypeError,[nil]);
526     end;
527    
528     function TOutputBlockItem.getAsBytes: TByteArray;
529     var i: integer;
530 tony 56 P: PByte;
531 tony 45 begin
532     with FItemData^ do
533     if FDataType = dtBytes then
534     begin
535     SetLength(Result,FDataLength);
536     P := FBufPtr;
537     for i := 0 to FDataLength - 1 do
538     begin
539     Result[i] := byte(P^);
540     Inc(P);
541     end
542     end
543     else
544     IBError(ibxeOutputBlockTypeError,[nil]);
545     end;
546    
547 tony 143 function TOutputBlockItem.getAsDateTime: TDateTime;
548     var aDate: integer;
549     aTime: integer;
550     begin
551     with FItemData^, FirebirdClientAPI do
552     if FDataType = dtDateTime then
553     begin
554     aDate := DecodeInteger(FBufPtr+3,4);
555     aTime := DecodeInteger(FBufPtr+7,4);
556     Result := SQLDecodeDate(@aDate) + SQLDecodeTime(@aTime)
557     end
558     else
559     IBError(ibxeOutputBlockTypeError,[nil]);
560     end;
561    
562    
563 tony 45 function TOutputBlockItem.CopyTo(stream: TStream; count: integer): integer;
564     var len: integer;
565     begin
566     if count < 0 then count := 0;
567     with FItemData^ do
568     begin
569     case FDataType of
570     dtString:
571     begin
572     len := byte((FBufPtr+1)^);
573     if (count > 0) and (count < len) then len := count;
574     Result := stream.Write((FBufPtr+2)^,len);
575     end;
576     dtString2:
577     begin
578     with FirebirdClientAPI do
579     len := DecodeInteger(FBufPtr+1,2);
580     if (count > 0) and (count < len) then len := count;
581     Result := stream.Write((FBufPtr+3)^,len);
582     end;
583     else
584     IBError(ibxeOutputBlockTypeError,[nil]);
585     end;
586     end;
587     end;
588    
589     { TOutputBlock }
590    
591     procedure TOutputBlock.ParseBuffer;
592     begin
593     if not FBufferParsed then
594     begin
595     {$IFDEF DEBUGOUTPUTBLOCK}
596     PrintBuf;
597     {$ENDIF}
598     DoParseBuffer;
599     if FError or FTruncated then
600     SetLength(FItems,Length(FItems)-1);
601     {$IFDEF DEBUGOUTPUTBLOCK}
602     FormattedPrint(FItems,'');
603     {$ENDIF}
604     end;
605     FBufferParsed := true;
606     end;
607    
608 tony 56 function TOutputBlock.AddItem(BufPtr: PByte): POutputBlockItemData;
609 tony 45 begin
610     new(Result);
611     with Result^ do
612     begin
613     FDataType := dtNone;
614     FBufPtr := BufPtr;
615     FDataLength := 0;
616     FSize := 1;
617     SetLength(FSubItems,0);
618     end;
619     end;
620    
621 tony 56 function TOutputBlock.AddIntegerItem(BufPtr: PByte): POutputBlockItemData;
622 tony 45 begin
623     new(Result);
624     with Result^ do
625     begin
626     FDataType := FIntegerType;
627     FBufPtr := BufPtr;
628     if FDataType = dtIntegerFixed then
629     begin
630     FDataLength := 4;
631     FSize := 5;
632     end
633     else
634     begin
635     with FirebirdClientAPI do
636     FDataLength := DecodeInteger(FBufPtr+1, 2);
637     FSize := FDataLength + 3;
638     end;
639     SetLength(FSubItems,0);
640     end;
641     end;
642    
643 tony 56 function TOutputBlock.AddStringItem(BufPtr: PByte): POutputBlockItemData;
644 tony 45 begin
645     new(Result);
646     with Result^ do
647     begin
648     FDataType := dtString2;
649     FBufPtr := BufPtr;
650     with FirebirdClientAPI do
651     FDataLength := DecodeInteger(FBufPtr+1, 2);
652     FSize := FDataLength + 3;
653     SetLength(FSubItems,0);
654     end;
655     end;
656    
657 tony 56 function TOutputBlock.AddShortStringItem(BufPtr: PByte): POutputBlockItemData;
658 tony 45 begin
659     new(Result);
660     with Result^ do
661     begin
662     FDataType := dtString;
663     FBufPtr := BufPtr;
664     FDataLength := byte((FBufPtr+1)^);
665     FSize := FDataLength + 2;
666     SetLength(FSubItems,0);
667     end;
668     end;
669    
670 tony 56 function TOutputBlock.AddByteItem(BufPtr: PByte): POutputBlockItemData;
671 tony 45 begin
672     new(Result);
673     with Result^ do
674     begin
675     FDataType := dtByte;
676     FBufPtr := BufPtr;
677     FDataLength := 1;
678     FSize := 2;
679     SetLength(FSubItems,0);
680     end;
681     end;
682    
683 tony 56 function TOutputBlock.AddBytesItem(BufPtr: PByte): POutputBlockItemData;
684 tony 45 begin
685     new(Result);
686     with Result^ do
687     begin
688     FDataType := dtBytes;
689     FBufPtr := BufPtr;
690     with FirebirdClientAPI do
691     FDataLength := DecodeInteger(FBufPtr+1, 2);
692     FSize := FDataLength + 3;
693     SetLength(FSubItems,0);
694     end;
695     end;
696    
697 tony 56 function TOutputBlock.AddListItem(BufPtr: PByte): POutputBlockItemData;
698 tony 45 begin
699     new(Result);
700     with Result^ do
701     begin
702     FDataType := dtList;
703     FBufPtr := BufPtr;
704     FSize := FBuffer + FBufSize - FBufPtr;
705     FDataLength := FSize - 1;
706     SetLength(FSubItems,0);
707     end;
708     end;
709    
710 tony 56 function TOutputBlock.AddSpecialItem(BufPtr: PByte): POutputBlockItemData;
711 tony 45 begin
712     new(Result);
713     with Result^ do
714     begin
715     FDataType := dtSpecial;
716     FBufPtr := BufPtr;
717     FSize := FBuffer + FBufSize - FBufPtr;
718     FDataLength := FSize - 1;
719     SetLength(FSubItems,0);
720     end;
721     end;
722    
723 tony 143 function TOutputBlock.AddDateTimeItem(BufPtr: PByte): POutputBlockItemData;
724     begin
725     new(Result);
726     with Result^ do
727     begin
728     FDataType := dtDateTime;
729     FBufPtr := BufPtr;
730     with FirebirdClientAPI do
731     FDataLength := DecodeInteger(FBufPtr+1, 2);
732     FSize := FDataLength + 3;
733     SetLength(FSubItems,0);
734     end;
735     end;
736    
737     function TOutputBlock.AddOctetString(BufPtr: PByte): POutputBlockItemData;
738     begin
739     new(Result);
740     with Result^ do
741     begin
742     FDataType := dtOctetString;
743     FBufPtr := BufPtr;
744     with FirebirdClientAPI do
745     FDataLength := DecodeInteger(FBufPtr+1, 2);
746     FSize := FDataLength + 3;
747     SetLength(FSubItems,0);
748     end;
749     end;
750    
751 tony 45 constructor TOutputBlock.Create(aSize: integer);
752     begin
753     inherited Create;
754     FBufSize := aSize;
755     GetMem(FBuffer,aSize);
756     if FBuffer = nil then
757     OutOfMemoryError;
758     FillChar(FBuffer^,aSize,255);
759     FBufferParsed := false;
760     FIntegerType := dtIntegerFixed;
761     end;
762    
763     destructor TOutputBlock.Destroy;
764     var i, j: integer;
765     begin
766     for i := 0 to length(FItems) - 1 do
767     begin
768     for j := 0 to Length(FItems[i]^.FSubItems) -1 do
769     dispose(FItems[i]^.FSubItems[j]);
770     dispose(FItems[i]);
771     end;
772     FreeMem(FBuffer);
773     inherited Destroy;
774     end;
775    
776 tony 56 function TOutputBlock.Buffer: PByte;
777 tony 45 begin
778     Result := FBuffer;
779     end;
780    
781     function TOutputBlock.getBufSize: integer;
782     begin
783     Result := FBufSize;
784     end;
785    
786     function TOutputBlock.GetCount: integer;
787     begin
788     ParseBuffer;
789     Result := length(FItems);
790     end;
791    
792     function TOutputBlock.GetItem(index: integer): POutputBlockItemData;
793     begin
794     ParseBuffer;
795     if (index >= 0) and (index < Length(FItems)) then
796     Result := FItems[index]
797     else
798     IBError(ibxeOutputBlockIndexError,[index]);
799     end;
800    
801     function TOutputBlock.Find(ItemType: byte): POutputBlockItemData;
802     var i: integer;
803     begin
804     Result := nil;
805     for i := 0 to getCount - 1 do
806 tony 56 if byte(FItems[i]^.FBufPtr^) = ItemType then
807 tony 45 begin
808     Result := FItems[i];
809     Exit;
810     end;
811     end;
812    
813     {$IFDEF DEBUGOUTPUTBLOCK}
814     procedure TOutputBlock.FormattedPrint(
815 tony 56 const aItems: array of POutputBlockItemData; Indent: AnsiString);
816 tony 45
817     var i: integer;
818     item: TOutputBlockItem;
819     begin
820     if FError then
821     writeln('Error')
822     else
823     if FTruncated then
824     writeln('Truncated')
825     else
826     for i := 0 to Length(aItems) - 1 do
827     with aItems[i]^ do
828     begin
829     if FError then
830     writeln('Error')
831     else
832     if FTruncated then
833     writeln('Truncated')
834     else
835     case FDataType of
836     dtList:
837     begin
838     writeln(Indent,'ItemType = ',byte(FBufPtr^));
839     FormattedPrint(FSubItems,Indent + ' ');
840     end;
841     dtSpecial:
842     writeln(Indent,'ItemType = ',byte(FBufPtr^),' Length = ',FSize);
843     else
844     begin
845     item := TOutputBlockItem.Create(self,(aItems[i]));
846     writeln(Indent,'ItemType = ',byte(FBufPtr^),' Value = ',(item as TOutputBlockItem).GetAsString);
847     end;
848     end;
849     end;
850     end;
851     {$ENDIF}
852    
853     procedure TOutputBlock.PrintBuf;
854     var i: integer;
855     begin
856     write(classname,': ');
857     for i := 0 to getBufSize - 1 do
858     begin
859     write(Format('%x ',[byte(Buffer[i])]));
860     if byte(FBuffer[i]) = isc_info_end then break;
861     end;
862     writeln;
863     end;
864    
865     { TDBInfoItem }
866    
867     procedure TDBInfoItem.DecodeIDCluster(var ConnectionType: integer;
868 tony 56 var DBFileName, DBSiteName: AnsiString);
869     var P: PByte;
870 tony 45 begin
871     with ItemData^ do
872 tony 56 if FBufPtr^ = isc_info_db_id then
873 tony 45 begin
874     P := FBufPtr + 3;
875     if FDataLength > 0 then
876     ConnectionType := integer(P^);
877     Inc(P);
878     SetString(DBFileName,P+1,byte(P^),CP_ACP);
879 tony 56 P := P + Length(DBFileName) + 1;
880 tony 45 SetString(DBSiteName,P+1,byte(P^),CP_ACP);
881     end
882     else
883     IBError(ibxeInfoBufferTypeError,[integer(FBufPtr^)]);
884     end;
885    
886     procedure TDBInfoItem.DecodeVersionString(var Version: byte;
887 tony 56 var VersionString: AnsiString);
888     var P: PByte;
889 tony 45 begin
890     with ItemData^ do
891 tony 56 if FBufPtr^ = isc_info_version then
892 tony 45 begin
893     P := FBufPtr+3;
894     VersionString := '';
895     Version := byte(P^);
896     Inc(P);
897     SetString(VersionString,P+1,byte(P^),CP_ACP);
898     end
899     else
900     IBError(ibxeInfoBufferTypeError,[integer(FBufPtr^)]);
901     end;
902    
903     procedure TDBInfoItem.DecodeUserNames(UserNames: TStrings);
904 tony 56 var P: PByte;
905     s: AnsiString;
906 tony 45 begin
907     with ItemData^ do
908 tony 56 if FBufPtr^ = isc_info_user_names then
909 tony 45 begin
910     P := FBufPtr+3;
911     while (P < FBufPtr + FSize) do
912     begin
913     SetString(s,P+1,byte(P^),CP_ACP);
914     UserNames.Add(s);
915 tony 56 P := P + Length(s) + 1;
916 tony 45 end;
917     end
918     else
919     IBError(ibxeInfoBufferTypeError,[integer(FBufPtr^)]);
920     end;
921    
922     function TDBInfoItem.getOperationCounts: TDBOperationCounts;
923     var tableCounts: integer;
924 tony 56 P: PByte;
925 tony 45 i: integer;
926     begin
927     with ItemData^ do
928     if byte(FBufPtr^) in [isc_info_backout_count, isc_info_delete_count,
929     isc_info_expunge_count,isc_info_insert_count, isc_info_purge_count,
930     isc_info_read_idx_count, isc_info_read_seq_count, isc_info_update_count] then
931     begin
932     tableCounts := FDataLength div 6;
933     SetLength(Result,TableCounts);
934     P := FBufPtr + 3;
935     for i := 0 to TableCounts -1 do
936     with FirebirdClientAPI do
937     begin
938     Result[i].TableID := DecodeInteger(P,2);
939     Inc(P,2);
940     Result[i].Count := DecodeInteger(P,4);
941     Inc(P,4);
942     end;
943     end
944     else
945     IBError(ibxeInfoBufferTypeError,[integer(FBufPtr^)]);
946     end;
947    
948     { TDBInformation }
949    
950 tony 56 function TDBInformation.AddSpecialItem(BufPtr: PByte): POutputBlockItemData;
951 tony 45 begin
952     Result := inherited AddSpecialItem(BufPtr);
953     with Result^ do
954     begin
955     with FirebirdClientAPI do
956     FDataLength := DecodeInteger(FBufPtr+1,2);
957     FSize := FDataLength + 3;
958     end;
959     end;
960    
961     procedure TDBInformation.DoParseBuffer;
962 tony 56 var P: PByte;
963 tony 45 index: integer;
964     begin
965     P := Buffer;
966     index := 0;
967     SetLength(FItems,0);
968 tony 56 while (P^ <> isc_info_end) and (P < Buffer + getBufSize) do
969 tony 45 begin
970     SetLength(FItems,index+1);
971     case byte(P^) of
972 tony 61 isc_info_db_read_only,
973 tony 45 isc_info_no_reserve,
974     isc_info_allocation,
975     isc_info_ods_minor_version,
976     isc_info_ods_version,
977     isc_info_db_SQL_dialect,
978     isc_info_page_size,
979     isc_info_current_memory,
980     isc_info_forced_writes,
981     isc_info_max_memory,
982     isc_info_num_buffers,
983     isc_info_sweep_interval,
984     isc_info_fetches,
985     isc_info_marks,
986     isc_info_reads,
987 tony 143 isc_info_writes,
988     isc_info_active_tran_count,
989     fb_info_pages_used,
990     fb_info_pages_free,
991     fb_info_conn_flags:
992 tony 45 FItems[index] := AddIntegerItem(P);
993    
994     isc_info_implementation,
995     isc_info_base_level:
996     FItems[index] := AddBytesItem(P);
997    
998 tony 143 isc_info_creation_date:
999     FItems[index] := AddDateTimeItem(P);
1000    
1001     fb_info_page_contents:
1002     FItems[index] := AddOctetString(P);
1003    
1004     fb_info_crypt_key:
1005     FItems[index] := AddStringItem(P);
1006    
1007 tony 45 isc_info_db_id,
1008     isc_info_version,
1009     isc_info_backout_count,
1010     isc_info_delete_count,
1011     isc_info_expunge_count,
1012     isc_info_insert_count,
1013     isc_info_purge_count,
1014     isc_info_read_idx_count,
1015     isc_info_read_seq_count,
1016     isc_info_update_count,
1017     isc_info_user_names:
1018     FItems[index] := AddSpecialItem(P);
1019    
1020     else
1021     FItems[index] := AddSpecialItem(P);
1022     end;
1023 tony 56 P := P + FItems[index]^.FSize;
1024 tony 45 Inc(index);
1025     end;
1026     end;
1027    
1028 tony 143 {$IFNDEF FPC}
1029     function TDBInformation.Find(ItemType: byte): IDBInfoItem;
1030     begin
1031     Result := inherited Find(ItemType);
1032     if Result.GetSize = 0 then
1033     Result := nil;
1034     end;
1035     {$ENDIF}
1036    
1037 tony 45 constructor TDBInformation.Create(aSize: integer);
1038     begin
1039     inherited Create(aSize);
1040     FIntegerType := dtInteger;
1041     end;
1042    
1043     { TServiceQueryResults }
1044    
1045 tony 56 function TServiceQueryResults.AddListItem(BufPtr: PByte): POutputBlockItemData;
1046     var P: PByte;
1047 tony 45 i: integer;
1048     group: byte;
1049     begin
1050     Result := inherited AddListItem(BufPtr);
1051     P := BufPtr + 1;
1052     i := 0;
1053     group := byte(BufPtr^);
1054     if group in [isc_info_svc_get_users,isc_info_svc_limbo_trans] then
1055     begin
1056     with FirebirdClientAPI do
1057     Result^.FSize := DecodeInteger(P,2) + 3;
1058     Inc(P,2);
1059     end;
1060     with Result^ do
1061     begin
1062 tony 56 while (P < FBufPtr + FSize) and (P^ <> isc_info_flag_end) do
1063 tony 45 begin
1064     SetLength(FSubItems,i+1);
1065     case group of
1066     isc_info_svc_svr_db_info:
1067     case integer(P^) of
1068     isc_spb_num_att,
1069     isc_spb_num_db:
1070     FSubItems[i] := AddIntegerItem(P);
1071    
1072     isc_spb_dbname:
1073     FSubItems[i] := AddStringItem(P);
1074    
1075     else
1076     IBError(ibxeOutputParsingError, [integer(P^)]);
1077     end;
1078    
1079     isc_info_svc_get_license:
1080     case integer(P^) of
1081     isc_spb_lic_id,
1082     isc_spb_lic_key:
1083     FSubItems[i] := AddIntegerItem(P);
1084     else
1085     IBError(ibxeOutputParsingError, [integer(P^)]);
1086     end;
1087    
1088     isc_info_svc_limbo_trans:
1089     case integer(P^) of
1090     isc_spb_tra_id,
1091     isc_spb_single_tra_id,
1092     isc_spb_multi_tra_id:
1093     FSubItems[i] := AddIntegerItem(P);
1094    
1095     isc_spb_tra_host_site,
1096     isc_spb_tra_remote_site,
1097     isc_spb_tra_db_path:
1098     FSubItems[i] := AddStringItem(P);
1099    
1100     isc_spb_tra_advise,
1101     isc_spb_tra_state:
1102     FSubItems[i] := AddByteItem(P);
1103     else
1104     IBError(ibxeOutputParsingError, [integer(P^)]);
1105     end;
1106    
1107     isc_info_svc_get_users:
1108     case integer(P^) of
1109 tony 143 isc_spb_sec_admin,
1110 tony 45 isc_spb_sec_userid,
1111     isc_spb_sec_groupid:
1112     FSubItems[i] := AddIntegerItem(P);
1113    
1114     isc_spb_sec_username,
1115     isc_spb_sec_password,
1116     isc_spb_sec_firstname,
1117     isc_spb_sec_middlename,
1118     isc_spb_sec_lastname:
1119     FSubItems[i] := AddStringItem(P);
1120    
1121     else
1122     IBError(ibxeOutputParsingError, [integer(P^)]);
1123     end;
1124    
1125     end;
1126 tony 56 P := P + FSubItems[i]^.FSize;
1127 tony 45 Inc(i);
1128     end;
1129     FDataLength := 0;
1130     for i := 0 to Length(FSubItems) - 1 do
1131 tony 56 FDataLength := FDataLength + FSubItems[i]^.FSize;
1132 tony 45 if group in [isc_info_svc_get_users,isc_info_svc_limbo_trans] then
1133     Exit;
1134    
1135 tony 56 if (P < FBufPtr + FSize) and (P^ = isc_info_flag_end) then
1136 tony 45 FSize := FDataLength + 2 {include start and end flag}
1137     else
1138     FSize := FDataLength + 1; {start flag only}
1139     end;
1140     end;
1141    
1142 tony 56 function TServiceQueryResults.AddSpecialItem(BufPtr: PByte
1143 tony 45 ): POutputBlockItemData;
1144 tony 56 var P: PByte;
1145 tony 45 i: integer;
1146     begin
1147     Result := inherited AddSpecialItem(BufPtr);
1148     with Result^ do
1149     begin
1150     with FirebirdClientAPI do
1151     FDataLength := DecodeInteger(FBufPtr+1, 2);
1152    
1153     P := FBufPtr + 3; {skip length bytes}
1154     i := 0;
1155     while P < FBufPtr + FDataLength do
1156     begin
1157     FSubItems[i] := AddIntegerItem(P);
1158 tony 56 P := P + FSubItems[i]^.FSize;
1159 tony 45 Inc(i);
1160     end;
1161     end;
1162     end;
1163    
1164     procedure TServiceQueryResults.DoParseBuffer;
1165 tony 56 var P: PByte;
1166 tony 45 i: integer;
1167     begin
1168     P := Buffer;
1169     i := 0;
1170 tony 56 while (P < Buffer + getBufSize) and (P^ <> isc_info_end) do
1171 tony 45 begin
1172     SetLength(FItems,i+1);
1173     case integer(P^) of
1174     isc_info_svc_line,
1175     isc_info_svc_get_env,
1176     isc_info_svc_get_env_lock,
1177     isc_info_svc_get_env_msg,
1178     isc_info_svc_user_dbpath,
1179     isc_info_svc_server_version,
1180     isc_info_svc_implementation,
1181     isc_info_svc_to_eof:
1182     FItems[i] := AddStringItem(P);
1183    
1184     isc_info_svc_get_license_mask,
1185     isc_info_svc_capabilities,
1186     isc_info_svc_version,
1187     isc_info_svc_running,
1188     isc_info_svc_stdin:
1189     FItems[i] := AddIntegerItem(P);
1190    
1191     isc_info_svc_timeout,
1192     isc_info_data_not_ready,
1193     isc_info_truncated:
1194     FItems[i] := AddItem(P);
1195    
1196     isc_info_svc_svr_db_info,
1197     isc_info_svc_get_license,
1198     isc_info_svc_limbo_trans,
1199     isc_info_svc_get_users:
1200     FItems[i] := AddListItem(P);
1201    
1202     isc_info_svc_get_config:
1203     FItems[i] := AddSpecialItem(P);
1204    
1205    
1206     else
1207     IBError(ibxeOutputParsingError, [integer(P^)]);
1208     end;
1209 tony 56 P := P + FItems[i]^.FSize;
1210 tony 45 Inc(i);
1211     end;
1212     end;
1213    
1214 tony 143 {$IFNDEF FPC}
1215     function TServiceQueryResults.Find(ItemType: byte): IServiceQueryResultItem;
1216     begin
1217     Result := inherited Find(ItemType);
1218     if Result.GetSize = 0 then
1219     Result := nil;
1220     end;
1221     {$ENDIF}
1222    
1223 tony 45 { TSQLInfoResultsBuffer }
1224    
1225 tony 56 function TSQLInfoResultsBuffer.AddListItem(BufPtr: PByte): POutputBlockItemData;
1226     var P: PByte;
1227 tony 45 i: integer;
1228     begin
1229     Result := inherited AddListItem(BufPtr);
1230     P := BufPtr + 1;
1231     i := 0;
1232    
1233     if byte(BufPtr^) = isc_info_sql_records then
1234     begin
1235     with FirebirdClientAPI do
1236     Result^.FSize := DecodeInteger(P,2) + 3;
1237     Inc(P,2);
1238     with Result^ do
1239     begin
1240     while (P < FBufPtr + FSize) and (byte(P^) <> isc_info_end) do
1241     begin
1242     SetLength(FSubItems,i+1);
1243     case integer(P^) of
1244     isc_info_req_select_count,
1245     isc_info_req_insert_count,
1246     isc_info_req_update_count,
1247     isc_info_req_delete_count:
1248     FSubItems[i] := AddIntegerItem(P);
1249    
1250     isc_info_truncated:
1251     begin
1252     FTruncated := true;
1253     Exit;
1254     end;
1255    
1256     isc_info_error:
1257     begin
1258     FError := true;
1259     Exit;
1260     end;
1261     else
1262     FSubItems[i] := AddSpecialItem(P);
1263     end;
1264 tony 56 P := P + FSubItems[i]^.FSize;
1265 tony 45 Inc(i);
1266     end;
1267     end;
1268     end;
1269     end;
1270    
1271     procedure TSQLInfoResultsBuffer.DoParseBuffer;
1272 tony 56 var P: PByte;
1273 tony 45 index: integer;
1274     begin
1275     P := Buffer;
1276     index := 0;
1277     SetLength(FItems,0);
1278 tony 56 while (P^ <> isc_info_end) and (P < Buffer + getBufSize) do
1279 tony 45 begin
1280     SetLength(FItems,index+1);
1281     case byte(P^) of
1282     isc_info_sql_stmt_type:
1283     FItems[index] := AddIntegerItem(P);
1284    
1285     isc_info_sql_get_plan:
1286     FItems[index] := AddStringItem(P);
1287    
1288     isc_info_sql_records:
1289     FItems[index] := AddListItem(P);
1290    
1291     isc_info_truncated:
1292     begin
1293     FTruncated := true;
1294     Exit;
1295     end;
1296    
1297     isc_info_error:
1298     begin
1299     FError := true;
1300     Exit;
1301     end;
1302    
1303     else
1304     FItems[index] := AddSpecialItem(P);
1305     end;
1306 tony 56 P := P + FItems[index]^.FSize;
1307 tony 45 Inc(index);
1308     end;
1309     end;
1310    
1311     constructor TSQLInfoResultsBuffer.Create(aSize: integer);
1312     begin
1313     inherited Create(aSize);
1314     FIntegerType := dtInteger;
1315     end;
1316    
1317 tony 56 { TBlobInfo }
1318    
1319     procedure TBlobInfo.DoParseBuffer;
1320     var P: PByte;
1321     index: integer;
1322     begin
1323     P := Buffer;
1324     index := 0;
1325     SetLength(FItems,0);
1326     while (P^ <> isc_info_end) and (P < Buffer + getBufSize) do
1327     begin
1328     SetLength(FItems,index+1);
1329     case byte(P^) of
1330     isc_info_blob_num_segments,
1331     isc_info_blob_max_segment,
1332     isc_info_blob_total_length,
1333     isc_info_blob_type:
1334     FItems[index] := AddIntegerItem(P);
1335     else
1336     FItems[index] := AddSpecialItem(P);
1337     end;
1338     P := P + FItems[index]^.FSize;
1339     Inc(index);
1340     end;
1341     end;
1342    
1343     constructor TBlobInfo.Create(aSize: integer);
1344     begin
1345     inherited Create(aSize);
1346     FIntegerType := dtInteger;
1347     end;
1348    
1349 tony 45 end.
1350