ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/fbintf/client/FBOutputBlock.pas
Revision: 209
Committed: Wed Mar 14 12:48:51 2018 UTC (6 years, 8 months ago) by tony
Content type: text/x-pascal
File size: 35113 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 i: integer;
434 begin
435 {There seems to be a memory manager problem with SetString that can cause
436 an unhandled exception at the end of a program if it is used to set the
437 string. Safer to copy characters one by one. Note that Setlength does
438 not work around the bug either.}
439 rs := '';
440 for i := 0 to len-1 do
441 rs := rs + PAnsiChar(buf+i)^;
442 // system.SetString(rs,PAnsiChar(Buf),len);
443 SetCodePage(rs,CodePage,false);
444 S := rs;
445 end;
446
447 constructor TOutputBlockItem.Create(AOwner: TOutputBlock;
448 Data: POutputBlockItemData);
449 begin
450 inherited Create;
451 FOwner := AOwner;
452 FOwnerIntf := AOwner;
453 FItemData := Data;
454 end;
455
456 function TOutputBlockItem.getItemType: byte;
457 begin
458 Result := byte(FItemData^.FBufPtr^);
459 end;
460
461 function TOutputBlockItem.getSize: integer;
462 begin
463 if FItemData = nil then
464 Result := 0
465 else
466 Result := FItemData^.FDataLength;
467 end;
468
469 procedure TOutputBlockItem.getRawBytes(var Buffer);
470 begin
471 with FItemData^ do
472 Move(FBufPtr^,Buffer,FDatalength);
473 end;
474
475 function TOutputBlockItem.getAsInteger: integer;
476 var len: integer;
477 begin
478 with FItemData^ do
479 case FDataType of
480 dtIntegerFixed:
481 with FirebirdClientAPI do
482 Result := DecodeInteger(FBufPtr+1,4);
483
484 dtByte,
485 dtInteger:
486 with FirebirdClientAPI do
487 begin
488 len := DecodeInteger(FBufPtr+1,2);
489 Result := DecodeInteger(FBufPtr+3,len);
490 end;
491 else
492 IBError(ibxeOutputBlockTypeError,[nil]);
493 end;
494 end;
495
496 function TOutputBlockItem.getParamType: byte;
497 begin
498 Result := byte(FItemData^.FBufPtr^)
499 end;
500
501 function TOutputBlockItem.getAsString: AnsiString;
502 var len: integer;
503 begin
504 Result := '';
505 with FItemData^ do
506 case FDataType of
507 dtIntegerFixed,
508 dtInteger:
509 Result := IntToStr(getAsInteger);
510 dtByte:
511 Result := IntToStr(getAsByte);
512 dtString:
513 begin
514 len := byte((FBufPtr+1)^);
515 SetString(Result,FBufPtr+2,len,CP_ACP);
516 end;
517 dtString2:
518 begin
519 with FirebirdClientAPI do
520 len := DecodeInteger(FBufPtr+1,2);
521 SetString(Result,FBufPtr+3,len,CP_ACP);
522 end;
523 dtOctetString:
524 begin
525 with FirebirdClientAPI do
526 len := DecodeInteger(FBufPtr+1,2);
527 SetString(Result,FBufPtr+3,len,CP_NONE);
528 end;
529 else
530 IBError(ibxeOutputBlockTypeError,[nil]);
531 end;
532 end;
533
534 function TOutputBlockItem.getAsByte: byte;
535 begin
536 with FItemData^ do
537 if FDataType = dtByte then
538 Result := byte((FBufPtr+2)^)
539 else
540 IBError(ibxeOutputBlockTypeError,[nil]);
541 end;
542
543 function TOutputBlockItem.getAsBytes: TByteArray;
544 var i: integer;
545 P: PByte;
546 begin
547 with FItemData^ do
548 if FDataType = dtBytes then
549 begin
550 SetLength(Result,FDataLength);
551 P := FBufPtr;
552 for i := 0 to FDataLength - 1 do
553 begin
554 Result[i] := byte(P^);
555 Inc(P);
556 end
557 end
558 else
559 IBError(ibxeOutputBlockTypeError,[nil]);
560 end;
561
562 function TOutputBlockItem.getAsDateTime: TDateTime;
563 var aDate: integer;
564 aTime: integer;
565 begin
566 with FItemData^, FirebirdClientAPI do
567 if FDataType = dtDateTime then
568 begin
569 aDate := DecodeInteger(FBufPtr+3,4);
570 aTime := DecodeInteger(FBufPtr+7,4);
571 Result := SQLDecodeDate(@aDate) + SQLDecodeTime(@aTime)
572 end
573 else
574 IBError(ibxeOutputBlockTypeError,[nil]);
575 end;
576
577
578 function TOutputBlockItem.CopyTo(stream: TStream; count: integer): integer;
579 var len: integer;
580 begin
581 if count < 0 then count := 0;
582 with FItemData^ do
583 begin
584 case FDataType of
585 dtString:
586 begin
587 len := byte((FBufPtr+1)^);
588 if (count > 0) and (count < len) then len := count;
589 Result := stream.Write((FBufPtr+2)^,len);
590 end;
591 dtString2:
592 begin
593 with FirebirdClientAPI do
594 len := DecodeInteger(FBufPtr+1,2);
595 if (count > 0) and (count < len) then len := count;
596 Result := stream.Write((FBufPtr+3)^,len);
597 end;
598 else
599 IBError(ibxeOutputBlockTypeError,[nil]);
600 end;
601 end;
602 end;
603
604 { TOutputBlock }
605
606 procedure TOutputBlock.ParseBuffer;
607 begin
608 if not FBufferParsed then
609 begin
610 {$IFDEF DEBUGOUTPUTBLOCK}
611 PrintBuf;
612 {$ENDIF}
613 DoParseBuffer;
614 if FError or FTruncated then
615 SetLength(FItems,Length(FItems)-1);
616 {$IFDEF DEBUGOUTPUTBLOCK}
617 FormattedPrint(FItems,'');
618 {$ENDIF}
619 end;
620 FBufferParsed := true;
621 end;
622
623 function TOutputBlock.AddItem(BufPtr: PByte): POutputBlockItemData;
624 begin
625 new(Result);
626 with Result^ do
627 begin
628 FDataType := dtNone;
629 FBufPtr := BufPtr;
630 FDataLength := 0;
631 FSize := 1;
632 SetLength(FSubItems,0);
633 end;
634 end;
635
636 function TOutputBlock.AddIntegerItem(BufPtr: PByte): POutputBlockItemData;
637 begin
638 new(Result);
639 with Result^ do
640 begin
641 FDataType := FIntegerType;
642 FBufPtr := BufPtr;
643 if FDataType = dtIntegerFixed then
644 begin
645 FDataLength := 4;
646 FSize := 5;
647 end
648 else
649 begin
650 with FirebirdClientAPI do
651 FDataLength := DecodeInteger(FBufPtr+1, 2);
652 FSize := FDataLength + 3;
653 end;
654 SetLength(FSubItems,0);
655 end;
656 end;
657
658 function TOutputBlock.AddStringItem(BufPtr: PByte): POutputBlockItemData;
659 begin
660 new(Result);
661 with Result^ do
662 begin
663 FDataType := dtString2;
664 FBufPtr := BufPtr;
665 with FirebirdClientAPI do
666 FDataLength := DecodeInteger(FBufPtr+1, 2);
667 FSize := FDataLength + 3;
668 SetLength(FSubItems,0);
669 end;
670 end;
671
672 function TOutputBlock.AddShortStringItem(BufPtr: PByte): POutputBlockItemData;
673 begin
674 new(Result);
675 with Result^ do
676 begin
677 FDataType := dtString;
678 FBufPtr := BufPtr;
679 FDataLength := byte((FBufPtr+1)^);
680 FSize := FDataLength + 2;
681 SetLength(FSubItems,0);
682 end;
683 end;
684
685 function TOutputBlock.AddByteItem(BufPtr: PByte): POutputBlockItemData;
686 begin
687 new(Result);
688 with Result^ do
689 begin
690 FDataType := dtByte;
691 FBufPtr := BufPtr;
692 FDataLength := 1;
693 FSize := 2;
694 SetLength(FSubItems,0);
695 end;
696 end;
697
698 function TOutputBlock.AddBytesItem(BufPtr: PByte): POutputBlockItemData;
699 begin
700 new(Result);
701 with Result^ do
702 begin
703 FDataType := dtBytes;
704 FBufPtr := BufPtr;
705 with FirebirdClientAPI do
706 FDataLength := DecodeInteger(FBufPtr+1, 2);
707 FSize := FDataLength + 3;
708 SetLength(FSubItems,0);
709 end;
710 end;
711
712 function TOutputBlock.AddListItem(BufPtr: PByte): POutputBlockItemData;
713 begin
714 new(Result);
715 with Result^ do
716 begin
717 FDataType := dtList;
718 FBufPtr := BufPtr;
719 FSize := FBuffer + FBufSize - FBufPtr;
720 FDataLength := FSize - 1;
721 SetLength(FSubItems,0);
722 end;
723 end;
724
725 function TOutputBlock.AddSpecialItem(BufPtr: PByte): POutputBlockItemData;
726 begin
727 new(Result);
728 with Result^ do
729 begin
730 FDataType := dtSpecial;
731 FBufPtr := BufPtr;
732 FSize := FBuffer + FBufSize - FBufPtr;
733 FDataLength := FSize - 1;
734 SetLength(FSubItems,0);
735 end;
736 end;
737
738 function TOutputBlock.AddDateTimeItem(BufPtr: PByte): POutputBlockItemData;
739 begin
740 new(Result);
741 with Result^ do
742 begin
743 FDataType := dtDateTime;
744 FBufPtr := BufPtr;
745 with FirebirdClientAPI do
746 FDataLength := DecodeInteger(FBufPtr+1, 2);
747 FSize := FDataLength + 3;
748 SetLength(FSubItems,0);
749 end;
750 end;
751
752 function TOutputBlock.AddOctetString(BufPtr: PByte): POutputBlockItemData;
753 begin
754 new(Result);
755 with Result^ do
756 begin
757 FDataType := dtOctetString;
758 FBufPtr := BufPtr;
759 with FirebirdClientAPI do
760 FDataLength := DecodeInteger(FBufPtr+1, 2);
761 FSize := FDataLength + 3;
762 SetLength(FSubItems,0);
763 end;
764 end;
765
766 constructor TOutputBlock.Create(aSize: integer);
767 begin
768 inherited Create;
769 FBufSize := aSize;
770 GetMem(FBuffer,aSize);
771 if FBuffer = nil then
772 OutOfMemoryError;
773 FillChar(FBuffer^,aSize,255);
774 FBufferParsed := false;
775 FIntegerType := dtIntegerFixed;
776 end;
777
778 destructor TOutputBlock.Destroy;
779 var i, j: integer;
780 begin
781 for i := 0 to length(FItems) - 1 do
782 begin
783 if FItems[i] <> nil then
784 begin
785 for j := 0 to Length(FItems[i]^.FSubItems) -1 do
786 if FItems[i]^.FSubItems[j] <> nil then
787 dispose(FItems[i]^.FSubItems[j]);
788 dispose(FItems[i]);
789 end;
790 end;
791 FreeMem(FBuffer);
792 inherited Destroy;
793 end;
794
795 function TOutputBlock.Buffer: PByte;
796 begin
797 Result := FBuffer;
798 end;
799
800 function TOutputBlock.getBufSize: integer;
801 begin
802 Result := FBufSize;
803 end;
804
805 function TOutputBlock.GetCount: integer;
806 begin
807 ParseBuffer;
808 Result := length(FItems);
809 end;
810
811 function TOutputBlock.GetItem(index: integer): POutputBlockItemData;
812 begin
813 ParseBuffer;
814 if (index >= 0) and (index < Length(FItems)) then
815 Result := FItems[index]
816 else
817 IBError(ibxeOutputBlockIndexError,[index]);
818 end;
819
820 function TOutputBlock.Find(ItemType: byte): POutputBlockItemData;
821 var i: integer;
822 begin
823 Result := nil;
824 for i := 0 to getCount - 1 do
825 if byte(FItems[i]^.FBufPtr^) = ItemType then
826 begin
827 Result := FItems[i];
828 Exit;
829 end;
830 end;
831
832 {$IFDEF DEBUGOUTPUTBLOCK}
833 procedure TOutputBlock.FormattedPrint(
834 const aItems: array of POutputBlockItemData; Indent: AnsiString);
835
836 var i: integer;
837 item: TOutputBlockItem;
838 begin
839 if FError then
840 writeln('Error')
841 else
842 if FTruncated then
843 writeln('Truncated')
844 else
845 for i := 0 to Length(aItems) - 1 do
846 with aItems[i]^ do
847 begin
848 if FError then
849 writeln('Error')
850 else
851 if FTruncated then
852 writeln('Truncated')
853 else
854 case FDataType of
855 dtList:
856 begin
857 writeln(Indent,'ItemType = ',byte(FBufPtr^));
858 FormattedPrint(FSubItems,Indent + ' ');
859 end;
860 dtSpecial:
861 writeln(Indent,'ItemType = ',byte(FBufPtr^),' Length = ',FSize);
862 else
863 begin
864 item := TOutputBlockItem.Create(self,(aItems[i]));
865 try
866 writeln(Indent,'ItemType = ',byte(FBufPtr^),' Value = ',(item as TOutputBlockItem).GetAsString);
867 except
868 writeln(Indent,'Unknown ItemType = ',byte(FBufPtr^));
869 end;
870 end;
871 end;
872 end;
873 end;
874 {$ENDIF}
875
876 procedure TOutputBlock.PrintBuf;
877 var i: integer;
878 begin
879 write(classname,': ');
880 for i := 0 to getBufSize - 1 do
881 begin
882 write(Format('%x ',[byte(Buffer[i])]));
883 if byte(FBuffer[i]) = isc_info_end then break;
884 end;
885 writeln;
886 for i := 0 to getBufSize - 1 do
887 begin
888 if chr(FBuffer[i]) in [' '..'~'] then
889 write(chr(Buffer[i]))
890 else
891 write('.');
892 if byte(FBuffer[i]) = isc_info_end then break;
893 end;
894 writeln;
895 end;
896
897 { TDBInfoItem }
898
899 procedure TDBInfoItem.DecodeIDCluster(var ConnectionType: integer;
900 var DBFileName, DBSiteName: AnsiString);
901 var P: PByte;
902 begin
903 with ItemData^ do
904 if FBufPtr^ = isc_info_db_id then
905 begin
906 P := FBufPtr + 3;
907 if FDataLength > 0 then
908 ConnectionType := integer(P^);
909 Inc(P);
910 SetString(DBFileName,P+1,byte(P^),CP_ACP);
911 P := P + Length(DBFileName) + 1;
912 SetString(DBSiteName,P+1,byte(P^),CP_ACP);
913 end
914 else
915 IBError(ibxeInfoBufferTypeError,[integer(FBufPtr^)]);
916 end;
917
918 procedure TDBInfoItem.DecodeVersionString(var Version: byte;
919 var VersionString: AnsiString);
920 var P: PByte;
921 begin
922 with ItemData^ do
923 if FBufPtr^ = isc_info_version then
924 begin
925 P := FBufPtr+3;
926 VersionString := '';
927 Version := byte(P^);
928 Inc(P);
929 SetString(VersionString,P+1,byte(P^),CP_ACP);
930 end
931 else
932 IBError(ibxeInfoBufferTypeError,[integer(FBufPtr^)]);
933 end;
934
935 procedure TDBInfoItem.DecodeUserNames(UserNames: TStrings);
936 var P: PByte;
937 s: AnsiString;
938 begin
939 with ItemData^ do
940 if FBufPtr^ = isc_info_user_names then
941 begin
942 P := FBufPtr+3;
943 while (P < FBufPtr + FSize) do
944 begin
945 SetString(s,P+1,byte(P^),CP_ACP);
946 UserNames.Add(s);
947 P := P + Length(s) + 1;
948 end;
949 end
950 else
951 IBError(ibxeInfoBufferTypeError,[integer(FBufPtr^)]);
952 end;
953
954 function TDBInfoItem.getOperationCounts: TDBOperationCounts;
955 var tableCounts: integer;
956 P: PByte;
957 i: integer;
958 begin
959 with ItemData^ do
960 if byte(FBufPtr^) in [isc_info_backout_count, isc_info_delete_count,
961 isc_info_expunge_count,isc_info_insert_count, isc_info_purge_count,
962 isc_info_read_idx_count, isc_info_read_seq_count, isc_info_update_count] then
963 begin
964 tableCounts := FDataLength div 6;
965 SetLength(Result,TableCounts);
966 P := FBufPtr + 3;
967 for i := 0 to TableCounts -1 do
968 with FirebirdClientAPI do
969 begin
970 Result[i].TableID := DecodeInteger(P,2);
971 Inc(P,2);
972 Result[i].Count := DecodeInteger(P,4);
973 Inc(P,4);
974 end;
975 end
976 else
977 IBError(ibxeInfoBufferTypeError,[integer(FBufPtr^)]);
978 end;
979
980 { TDBInformation }
981
982 function TDBInformation.AddSpecialItem(BufPtr: PByte): POutputBlockItemData;
983 begin
984 Result := inherited AddSpecialItem(BufPtr);
985 with Result^ do
986 begin
987 with FirebirdClientAPI do
988 FDataLength := DecodeInteger(FBufPtr+1,2);
989 FSize := FDataLength + 3;
990 end;
991 end;
992
993 procedure TDBInformation.DoParseBuffer;
994 var P: PByte;
995 index: integer;
996 begin
997 P := Buffer;
998 index := 0;
999 SetLength(FItems,0);
1000 while (P^ <> isc_info_end) and (P < Buffer + getBufSize) do
1001 begin
1002 SetLength(FItems,index+1);
1003 case byte(P^) of
1004 isc_info_db_read_only,
1005 isc_info_no_reserve,
1006 isc_info_allocation,
1007 isc_info_ods_minor_version,
1008 isc_info_ods_version,
1009 isc_info_db_SQL_dialect,
1010 isc_info_page_size,
1011 isc_info_current_memory,
1012 isc_info_forced_writes,
1013 isc_info_max_memory,
1014 isc_info_num_buffers,
1015 isc_info_sweep_interval,
1016 isc_info_fetches,
1017 isc_info_marks,
1018 isc_info_reads,
1019 isc_info_writes,
1020 isc_info_active_tran_count,
1021 fb_info_pages_used,
1022 fb_info_pages_free,
1023 fb_info_conn_flags:
1024 FItems[index] := AddIntegerItem(P);
1025
1026 isc_info_implementation,
1027 isc_info_base_level:
1028 FItems[index] := AddBytesItem(P);
1029
1030 isc_info_creation_date:
1031 FItems[index] := AddDateTimeItem(P);
1032
1033 fb_info_page_contents:
1034 FItems[index] := AddOctetString(P);
1035
1036 fb_info_crypt_key:
1037 FItems[index] := AddStringItem(P);
1038
1039 isc_info_db_id,
1040 isc_info_version,
1041 isc_info_backout_count,
1042 isc_info_delete_count,
1043 isc_info_expunge_count,
1044 isc_info_insert_count,
1045 isc_info_purge_count,
1046 isc_info_read_idx_count,
1047 isc_info_read_seq_count,
1048 isc_info_update_count,
1049 isc_info_user_names:
1050 FItems[index] := AddSpecialItem(P);
1051
1052 else
1053 FItems[index] := AddSpecialItem(P);
1054 end;
1055 P := P + FItems[index]^.FSize;
1056 Inc(index);
1057 end;
1058 end;
1059
1060 {$IFNDEF FPC}
1061 function TDBInformation.Find(ItemType: byte): IDBInfoItem;
1062 begin
1063 Result := inherited Find(ItemType);
1064 if Result.GetSize = 0 then
1065 Result := nil;
1066 end;
1067 {$ENDIF}
1068
1069 constructor TDBInformation.Create(aSize: integer);
1070 begin
1071 inherited Create(aSize);
1072 FIntegerType := dtInteger;
1073 end;
1074
1075 { TServiceQueryResults }
1076
1077 function TServiceQueryResults.AddListItem(BufPtr: PByte): POutputBlockItemData;
1078 var P: PByte;
1079 i: integer;
1080 group: byte;
1081 begin
1082 Result := inherited AddListItem(BufPtr);
1083 P := BufPtr + 1;
1084 i := 0;
1085 group := byte(BufPtr^);
1086 if group in [isc_info_svc_get_users,isc_info_svc_limbo_trans] then
1087 begin
1088 with FirebirdClientAPI do
1089 Result^.FSize := DecodeInteger(P,2) + 3;
1090 Inc(P,2);
1091 end;
1092 with Result^ do
1093 begin
1094 while (P < FBufPtr + FSize) and (P^ <> isc_info_flag_end) do
1095 begin
1096 SetLength(FSubItems,i+1);
1097 FSubItems[i] := nil;
1098 case group of
1099 isc_info_svc_svr_db_info:
1100 case integer(P^) of
1101 isc_spb_num_att,
1102 isc_spb_num_db:
1103 FSubItems[i] := AddIntegerItem(P);
1104
1105 isc_spb_dbname:
1106 FSubItems[i] := AddStringItem(P);
1107
1108 else
1109 IBError(ibxeOutputParsingError, [integer(P^),BufToStr(P,FSize - (P-FBufPtr))]);
1110 end;
1111
1112 isc_info_svc_get_license:
1113 case integer(P^) of
1114 isc_spb_lic_id,
1115 isc_spb_lic_key:
1116 FSubItems[i] := AddIntegerItem(P);
1117 else
1118 IBError(ibxeOutputParsingError, [integer(P^),BufToStr(P,FSize - (P-FBufPtr))]);
1119 end;
1120
1121 isc_info_svc_limbo_trans:
1122 case integer(P^) of
1123 isc_spb_tra_id,
1124 isc_spb_single_tra_id,
1125 isc_spb_multi_tra_id:
1126 FSubItems[i] := AddIntegerItem(P);
1127
1128 isc_spb_tra_host_site,
1129 isc_spb_tra_remote_site,
1130 isc_spb_tra_db_path:
1131 FSubItems[i] := AddStringItem(P);
1132
1133 isc_spb_tra_advise,
1134 isc_spb_tra_state:
1135 FSubItems[i] := AddByteItem(P);
1136 else
1137 IBError(ibxeOutputParsingError, [integer(P^),BufToStr(P,FSize - (P-FBufPtr))]);
1138 end;
1139
1140 isc_info_svc_get_users:
1141 case integer(P^) of
1142 isc_spb_sec_admin,
1143 isc_spb_sec_userid,
1144 isc_spb_sec_groupid:
1145 FSubItems[i] := AddIntegerItem(P);
1146
1147 isc_spb_sec_username,
1148 isc_spb_sec_password,
1149 isc_spb_sec_firstname,
1150 isc_spb_sec_middlename,
1151 isc_spb_sec_lastname:
1152 FSubItems[i] := AddStringItem(P);
1153
1154 else
1155 IBError(ibxeOutputParsingError, [integer(P^),BufToStr(P,FSize - (P-FBufPtr))]);
1156 end;
1157
1158 end;
1159 P := P + FSubItems[i]^.FSize;
1160 Inc(i);
1161 end;
1162 FDataLength := 0;
1163 for i := 0 to Length(FSubItems) - 1 do
1164 FDataLength := FDataLength + FSubItems[i]^.FSize;
1165 if group in [isc_info_svc_get_users,isc_info_svc_limbo_trans] then
1166 Exit;
1167
1168 if (P < FBufPtr + FSize) and (P^ = isc_info_flag_end) then
1169 FSize := FDataLength + 2 {include start and end flag}
1170 else
1171 FSize := FDataLength + 1; {start flag only}
1172 end;
1173 end;
1174
1175 function TServiceQueryResults.AddSpecialItem(BufPtr: PByte
1176 ): POutputBlockItemData;
1177 var P: PByte;
1178 i: integer;
1179 begin
1180 Result := inherited AddSpecialItem(BufPtr);
1181 with Result^ do
1182 begin
1183 with FirebirdClientAPI do
1184 FDataLength := DecodeInteger(FBufPtr+1, 2);
1185
1186 P := FBufPtr + 3; {skip length bytes}
1187 i := 0;
1188 while P < FBufPtr + FDataLength do
1189 begin
1190 FSubItems[i] := AddIntegerItem(P);
1191 P := P + FSubItems[i]^.FSize;
1192 Inc(i);
1193 end;
1194 end;
1195 end;
1196
1197 procedure TServiceQueryResults.DoParseBuffer;
1198 var P: PByte;
1199 i: integer;
1200 begin
1201 P := Buffer;
1202 i := 0;
1203 while (P < Buffer + getBufSize) and (P^ <> isc_info_end) do
1204 begin
1205 SetLength(FItems,i+1);
1206 FItems[i] := nil;
1207 case integer(P^) of
1208 isc_info_svc_line,
1209 isc_info_svc_get_env,
1210 isc_info_svc_get_env_lock,
1211 isc_info_svc_get_env_msg,
1212 isc_info_svc_user_dbpath,
1213 isc_info_svc_server_version,
1214 isc_info_svc_implementation,
1215 isc_info_svc_to_eof:
1216 FItems[i] := AddStringItem(P);
1217
1218 isc_info_svc_get_license_mask,
1219 isc_info_svc_capabilities,
1220 isc_info_svc_version,
1221 isc_info_svc_running,
1222 isc_info_svc_stdin:
1223 FItems[i] := AddIntegerItem(P);
1224
1225 isc_info_svc_timeout,
1226 isc_info_data_not_ready,
1227 isc_info_truncated:
1228 FItems[i] := AddItem(P);
1229
1230 isc_info_svc_svr_db_info,
1231 isc_info_svc_get_license,
1232 isc_info_svc_limbo_trans,
1233 isc_info_svc_get_users:
1234 FItems[i] := AddListItem(P);
1235
1236 isc_info_svc_get_config:
1237 FItems[i] := AddSpecialItem(P);
1238
1239
1240 else
1241 IBError(ibxeOutputParsingError, [integer(P^),BufToStr(P,getBufSize - (P-Buffer))]);
1242 end;
1243 P := P + FItems[i]^.FSize;
1244 Inc(i);
1245 end;
1246 end;
1247
1248 {$IFNDEF FPC}
1249 function TServiceQueryResults.Find(ItemType: byte): IServiceQueryResultItem;
1250 begin
1251 Result := inherited Find(ItemType);
1252 if Result.GetSize = 0 then
1253 Result := nil;
1254 end;
1255 {$ENDIF}
1256
1257 { TSQLInfoResultsBuffer }
1258
1259 function TSQLInfoResultsBuffer.AddListItem(BufPtr: PByte): POutputBlockItemData;
1260 var P: PByte;
1261 i: integer;
1262 begin
1263 Result := inherited AddListItem(BufPtr);
1264 P := BufPtr + 1;
1265 i := 0;
1266
1267 if byte(BufPtr^) = isc_info_sql_records then
1268 begin
1269 with FirebirdClientAPI do
1270 Result^.FSize := DecodeInteger(P,2) + 3;
1271 Inc(P,2);
1272 with Result^ do
1273 begin
1274 while (P < FBufPtr + FSize) and (byte(P^) <> isc_info_end) do
1275 begin
1276 SetLength(FSubItems,i+1);
1277 case integer(P^) of
1278 isc_info_req_select_count,
1279 isc_info_req_insert_count,
1280 isc_info_req_update_count,
1281 isc_info_req_delete_count:
1282 FSubItems[i] := AddIntegerItem(P);
1283
1284 isc_info_truncated:
1285 begin
1286 FTruncated := true;
1287 Exit;
1288 end;
1289
1290 isc_info_error:
1291 begin
1292 FError := true;
1293 Exit;
1294 end;
1295 else
1296 FSubItems[i] := AddSpecialItem(P);
1297 end;
1298 P := P + FSubItems[i]^.FSize;
1299 Inc(i);
1300 end;
1301 end;
1302 end;
1303 end;
1304
1305 procedure TSQLInfoResultsBuffer.DoParseBuffer;
1306 var P: PByte;
1307 index: integer;
1308 begin
1309 P := Buffer;
1310 index := 0;
1311 SetLength(FItems,0);
1312 while (P^ <> isc_info_end) and (P < Buffer + getBufSize) do
1313 begin
1314 SetLength(FItems,index+1);
1315 case byte(P^) of
1316 isc_info_sql_stmt_type:
1317 FItems[index] := AddIntegerItem(P);
1318
1319 isc_info_sql_get_plan:
1320 FItems[index] := AddStringItem(P);
1321
1322 isc_info_sql_records:
1323 FItems[index] := AddListItem(P);
1324
1325 isc_info_truncated:
1326 begin
1327 FTruncated := true;
1328 Exit;
1329 end;
1330
1331 isc_info_error:
1332 begin
1333 FError := true;
1334 Exit;
1335 end;
1336
1337 else
1338 FItems[index] := AddSpecialItem(P);
1339 end;
1340 P := P + FItems[index]^.FSize;
1341 Inc(index);
1342 end;
1343 end;
1344
1345 constructor TSQLInfoResultsBuffer.Create(aSize: integer);
1346 begin
1347 inherited Create(aSize);
1348 FIntegerType := dtInteger;
1349 end;
1350
1351 { TBlobInfo }
1352
1353 procedure TBlobInfo.DoParseBuffer;
1354 var P: PByte;
1355 index: integer;
1356 begin
1357 P := Buffer;
1358 index := 0;
1359 SetLength(FItems,0);
1360 while (P^ <> isc_info_end) and (P < Buffer + getBufSize) do
1361 begin
1362 SetLength(FItems,index+1);
1363 case byte(P^) of
1364 isc_info_blob_num_segments,
1365 isc_info_blob_max_segment,
1366 isc_info_blob_total_length,
1367 isc_info_blob_type:
1368 FItems[index] := AddIntegerItem(P);
1369 else
1370 FItems[index] := AddSpecialItem(P);
1371 end;
1372 P := P + FItems[index]^.FSize;
1373 Inc(index);
1374 end;
1375 end;
1376
1377 constructor TBlobInfo.Create(aSize: integer);
1378 begin
1379 inherited Create(aSize);
1380 FIntegerType := dtInteger;
1381 end;
1382
1383 end.
1384