ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/fbintf/client/FBOutputBlock.pas
Revision: 144
Committed: Sat Feb 24 23:15:51 2018 UTC (6 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 34764 byte(s)
Log Message:
Fixes Merged

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