ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/fbintf/client/FBArray.pas
Revision: 309
Committed: Tue Jul 21 08:00:42 2020 UTC (3 years, 9 months ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/fbintf/client/FBArray.pas
File size: 29056 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 FBArray;
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 interface
39
40 uses
41 Classes, SysUtils, IB, IBHeader, FBTransaction,
42 FBSQLData, FBClientAPI, IBExternals, FBActivityMonitor;
43
44 (*
45
46 COMMENTS (copied from IBPP)
47
48 1)
49 For an array column of type CHAR(X), the internal type returned or expected is blr_text.
50 In such case, the byte array received or submitted to get/put_slice is formatted in
51 elements of X bytes, which correspond to what is reported in array_desc_length.
52 The elements are not '\0' terminated but are right-padded with spaces ' '.
53
54 2)
55 For an array column of type VARCHAR(X), the internal type is blr_varying.
56 The underlying format is rather curious and different than what is used in XSQLDA.
57 The element size is reported in array_desc_length as X.
58 Yet each element of the byte array is expected to be of size X+2 (just as if we were
59 to stuff a short in the first 2 bytes to store the length (as is done with XSQLDA).
60 No. The string of X characters maximum has to be stored in the chunks of X+2 bytes as
61 a zero-terminated c-string. Note that the buffer is indeed one byte too large.
62 Internally, the API probably convert in-place in these chunks the zero-terminated string
63 to a variable-size string with a short in front and the string data non zero-terminated
64 behind.
65
66 *)
67
68 type
69 TFBArray = class;
70
71 { TFBArrayElement }
72
73 TFBArrayElement = class(TSQLDataItem)
74 private
75 FBufPtr: PByte;
76 FArray: TFBArray;
77 protected
78 function GetSQLDialect: integer; override;
79 procedure Changing; override;
80 procedure Changed; override;
81 function SQLData: PByte; override;
82 function GetDataLength: cardinal; override;
83 function GetCodePage: TSystemCodePage; override;
84 function getCharSetID: cardinal; override;
85 procedure SetDataLength(len: cardinal); override;
86 procedure SetSQLType(aValue: cardinal); override;
87 public
88 constructor Create(anArray: TFBArray; P: PByte);
89 function GetSQLType: cardinal; override;
90 function GetName: AnsiString; override;
91 function GetScale: integer; override;
92 function GetSize: integer;
93 function GetCharSetWidth: integer; override;
94 function GetAsString: AnsiString; override;
95 procedure SetAsLong(Value: Long); override;
96 procedure SetAsShort(Value: Short); override;
97 procedure SetAsInt64(Value: Int64); override;
98 procedure SetAsString(Value: AnsiString); override;
99 procedure SetAsDouble(Value: Double); override;
100 procedure SetAsFloat(Value: Float); override;
101 procedure SetAsCurrency(Value: Currency); override;
102 end;
103
104 { TFBArrayMetaData }
105
106 TFBArrayMetaData = class(TFBInterfacedObject,IArrayMetaData)
107 private
108 function GetDType(SQLType: cardinal): UChar;
109 protected
110 FArrayDesc: TISC_ARRAY_DESC;
111 FCharSetID: integer;
112 FAttachment: IAttachment;
113 procedure LoadMetaData(aAttachment: IAttachment; aTransaction: ITransaction;
114 relationName, columnName: AnsiString); virtual; abstract;
115 function NumOfElements: integer;
116 public
117 constructor Create(aAttachment: IAttachment; aTransaction: ITransaction;
118 relationName, columnName: AnsiString); overload;
119 constructor Create(aAttachment: IAttachment;SQLType: cardinal; tableName: AnsiString; columnName: AnsiString;
120 Scale: integer; size: cardinal; charSetID: cardinal;
121 dimensions: cardinal; bounds: TArrayBounds); overload;
122 function GetCodePage: TSystemCodePage; virtual; abstract;
123
124 public
125 {IArrayMetaData}
126 function GetSQLType: cardinal;
127 function GetSQLTypeName: AnsiString;
128 function GetScale: integer;
129 function GetSize: cardinal;
130 function GetCharSetID: cardinal; virtual; abstract;
131 function GetCharSetWidth: integer; virtual; abstract;
132 function GetTableName: AnsiString;
133 function GetColumnName: AnsiString;
134 function GetDimensions: integer;
135 function GetBounds: TArrayBounds;
136 end;
137
138
139 { TFBArray }
140
141 TFBArray = class(TActivityReporter,IArray)
142 private
143 FFirebirdClientAPI: TFBClientAPI;
144 FMetaData: IArrayMetaData;
145 FIsNew: boolean;
146 FLoaded: boolean;
147 FModified: boolean;
148 FAttachment: IAttachment;
149 FTransactionIntf: ITransaction;
150 FTransactionSeqNo: integer;
151 FSQLDialect: integer;
152 FOffsets: array of integer;
153 FElement: TFBArrayElement;
154 FElementIntf: IUnknown;
155 FElementSize: integer;
156 FEventHandlers: array of TArrayEventHandler;
157 procedure GetArraySlice;
158 procedure PutArraySlice(Force: boolean=false);
159 function GetOffset(index: array of integer): PByte;
160 function GetDataLength: short;
161 protected
162 FBuffer: PByte;
163 FBufSize: ISC_LONG;
164 FArrayID: TISC_QUAD;
165 procedure AllocateBuffer; virtual;
166 procedure Changing; virtual;
167 procedure Changed; virtual;
168 function GetArrayDesc: PISC_ARRAY_DESC;
169 procedure InternalGetSlice; virtual; abstract;
170 procedure InternalPutSlice(Force: boolean); virtual; abstract;
171 public
172 constructor Create(aAttachment: IAttachment; aTransaction: TFBTransaction;
173 aField: IArrayMetaData); overload;
174 constructor Create(aAttachment: IAttachment; aTransaction: TFBTransaction;
175 aField: IArrayMetaData; ArrayID: TISC_QUAD); overload;
176 destructor Destroy; override;
177 function GetSQLDialect: integer;
178 procedure TransactionEnding(aTransaction: ITransaction; Force: boolean);
179
180 public
181 {IArrayMetaData}
182 function GetSQLType: cardinal;
183 function GetSQLTypeName: AnsiString;
184 function GetScale: integer;
185 function GetSize: cardinal;
186 function GetCharSetID: cardinal;
187 function GetCharSetWidth: integer;
188 function GetTableName: AnsiString;
189 function GetColumnName: AnsiString;
190 function GetDimensions: integer;
191 function GetBounds: TArrayBounds;
192 {IArray}
193 function GetArrayID: TISC_QUAD;
194 procedure Clear;
195 function IsEmpty: boolean;
196 procedure PreLoad;
197 procedure CancelChanges;
198 procedure SaveChanges;
199 function GetAsInteger(index: array of integer): integer;
200 function GetAsBoolean(index: array of integer): boolean;
201 function GetAsCurrency(index: array of integer): Currency;
202 function GetAsInt64(index: array of integer): Int64;
203 function GetAsDateTime(index: array of integer): TDateTime;
204 function GetAsDouble(index: array of integer): Double;
205 function GetAsFloat(index: array of integer): Float;
206 function GetAsLong(index: array of integer): Long;
207 function GetAsShort(index: array of integer): Short;
208 function GetAsString(index: array of integer): AnsiString;
209 function GetAsVariant(index: array of integer): Variant;
210 procedure SetAsInteger(index: array of integer; AValue: integer);
211 procedure SetAsBoolean(index: array of integer; AValue: boolean);
212 procedure SetAsCurrency(index: array of integer; Value: Currency);
213 procedure SetAsInt64(index: array of integer; Value: Int64);
214 procedure SetAsDate(index: array of integer; Value: TDateTime);
215 procedure SetAsLong(index: array of integer; Value: Long);
216 procedure SetAsTime(index: array of integer; Value: TDateTime);
217 procedure SetAsDateTime(index: array of integer; Value: TDateTime);
218 procedure SetAsDouble(index: array of integer; Value: Double);
219 procedure SetAsFloat(index: array of integer; Value: Float);
220 procedure SetAsShort(index: array of integer; Value: Short);
221 procedure SetAsString(index: array of integer; Value: AnsiString);
222 procedure SetAsVariant(index: array of integer; Value: Variant);
223 procedure SetBounds(dim, UpperBound, LowerBound: integer);
224 function GetAttachment: IAttachment;
225 function GetTransaction: ITransaction;
226 procedure AddEventHandler(Handler: TArrayEventHandler);
227 procedure RemoveEventHandler(Handler: TArrayEventHandler);
228 end;
229
230 implementation
231
232 uses FBMessages;
233
234 { TFBArrayElement }
235
236 function TFBArrayElement.GetSQLDialect: integer;
237 begin
238 Result := FArray.GetSQLDialect;
239 end;
240
241 procedure TFBArrayElement.Changing;
242 begin
243 inherited Changing;
244 FArray.Changing;
245 end;
246
247 procedure TFBArrayElement.Changed;
248 begin
249 inherited Changed;
250 FArray.Changed;
251 end;
252
253 function TFBArrayElement.SQLData: PByte;
254 begin
255 Result := FBufPtr;
256 end;
257
258 function TFBArrayElement.GetDataLength: cardinal;
259 begin
260 Result := FArray.GetDataLength
261 end;
262
263 function TFBArrayElement.GetCodePage: TSystemCodePage;
264 begin
265 Result := (FArray.FMetaData as TFBArrayMetaData).GetCodePage;
266 end;
267
268 function TFBArrayElement.getCharSetID: cardinal;
269 begin
270 Result := (FArray.FMetaData as TFBArrayMetaData).GetCharSetID;
271 end;
272
273 procedure TFBArrayElement.SetDataLength(len: cardinal);
274 begin
275 if len > GetDataLength then
276 IBError(ibxeArrayElementOverFlow,[nil]);
277 end;
278
279 constructor TFBArrayElement.Create(anArray: TFBArray; P: PByte);
280 begin
281 inherited Create(anArray.FFirebirdClientAPI);
282 FArray := anArray;
283 FBufPtr := P;
284 end;
285
286 function TFBArrayElement.GetSQLType: cardinal;
287 begin
288 Result := FArray.FMetaData.GetSQLType;
289 end;
290
291 function TFBArrayElement.GetName: AnsiString;
292 begin
293 Result := FArray.FMetaData.GetColumnName;
294 end;
295
296 function TFBArrayElement.GetScale: integer;
297 begin
298 Result := FArray.FMetaData.GetScale;
299 end;
300
301 function TFBArrayElement.GetSize: integer;
302 begin
303 Result := GetDataLength;
304 end;
305
306 function TFBArrayElement.GetCharSetWidth: integer;
307 begin
308 Result := FArray.FMetaData.GetCharSetWidth;
309 end;
310
311 function TFBArrayElement.GetAsString: AnsiString;
312 var rs: RawByteString;
313 begin
314 case GetSQLType of
315 SQL_VARYING:
316 begin
317 rs := strpas(PAnsiChar(FBufPtr));
318 SetCodePage(rs,GetCodePage,false);
319 Result := rs;
320 end;
321 SQL_TEXT:
322 begin
323 SetString(rs,PAnsiChar(FBufPtr),GetDataLength);
324 SetCodePage(rs,GetCodePage,false);
325 Result := rs;
326 end
327 else
328 Result := inherited GetAsString;
329 end;
330 end;
331
332 procedure TFBArrayElement.SetAsLong(Value: Long);
333 begin
334 AsInt64 := Value;
335 end;
336
337 procedure TFBArrayElement.SetAsShort(Value: Short);
338 begin
339 AsInt64 := Value;
340 end;
341
342 procedure TFBArrayElement.SetAsInt64(Value: Int64);
343 begin
344 CheckActive;
345 case GetSQLType of
346 SQL_LONG:
347 PLong(SQLData)^ := Value;
348 SQL_SHORT:
349 PShort(SQLData)^ := Value;
350 SQL_INT64:
351 PInt64(SQLData)^ := Value;
352 SQL_TEXT, SQL_VARYING:
353 SetAsString(IntToStr(Value));
354 SQL_D_FLOAT,
355 SQL_DOUBLE:
356 PDouble(SQLData)^ := Value;
357 SQL_FLOAT:
358 PSingle(SQLData)^ := Value;
359 else
360 IBError(ibxeInvalidDataConversion, [nil]);
361 end;
362 Changed;
363 end;
364
365 procedure TFBArrayElement.SetAsString(Value: AnsiString);
366 var len: integer;
367 ElementSize: integer;
368 begin
369 CheckActive;
370 case GetSQLType of
371 SQL_BOOLEAN:
372 if AnsiCompareText(Value,STrue) = 0 then
373 AsBoolean := true
374 else
375 if AnsiCompareText(Value,SFalse) = 0 then
376 AsBoolean := false
377 else
378 IBError(ibxeInvalidDataConversion,[nil]);
379
380 SQL_VARYING:
381 begin
382 Value := Transliterate(Value,GetCodePage);
383 len := Length(Value);
384 ElementSize := GetDataLength;
385 if len > ElementSize - 2 then
386 len := ElementSize - 2;
387 if Len > 0 then
388 Move(Value[1],FBufPtr^,len);
389 if Len < ElementSize - 2 then
390 (FBufPtr+len)^ := 0;
391 Changed;
392 end;
393
394 SQL_TEXT:
395 begin
396 Value := Transliterate(Value,GetCodePage);
397 ElementSize := GetDataLength;
398 FillChar(FBufPtr^,ElementSize,' ');
399 len := Length(Value);
400 if len > ElementSize - 1 then len := ElementSize - 1;
401 Move(Value[1],FBufPtr^,len);
402 Changed;
403 end;
404
405 SQL_SHORT,
406 SQL_LONG,
407 SQL_INT64:
408 if trim(Value) = '' then
409 SetAsInt64(0)
410 else
411 SetAsInt64(AdjustScaleFromCurrency(StrToCurr(Value),GetScale));
412
413 SQL_D_FLOAT,
414 SQL_DOUBLE,
415 SQL_FLOAT:
416 if trim(Value) = '' then
417 SetAsDouble(0)
418 else
419 SetAsDouble(StrToFloat(Value));
420
421 SQL_TIMESTAMP:
422 SetAsDateTime(StrToDateTime(Value));
423
424 SQL_TYPE_DATE:
425 SetAsDate(StrToDateTime(Value));
426
427 SQL_TYPE_TIME:
428 SetAsTime(StrToDateTime(Value));
429
430 else
431 IBError(ibxeInvalidDataConversion,[nil]);
432 end;
433 end;
434
435 procedure TFBArrayElement.SetAsDouble(Value: Double);
436 begin
437 CheckActive;
438 case GetSQLType of
439 SQL_D_FLOAT,
440 SQL_DOUBLE:
441 PDouble(SQLData)^ := Value;
442 SQL_FLOAT:
443 PSingle(SQLData)^ := Value;
444 SQL_SHORT:
445 if Scale < 0 then
446 PShort(SQLData)^ := AdjustScaleFromDouble(Value,Scale)
447 else
448 IBError(ibxeInvalidDataConversion, [nil]);
449 SQL_LONG:
450 if Scale < 0 then
451 PLong(SQLData)^ := AdjustScaleFromDouble(Value,Scale)
452 else
453 IBError(ibxeInvalidDataConversion, [nil]);
454 SQL_INT64:
455 if Scale < 0 then
456 PInt64(SQLData)^ := AdjustScaleFromDouble(Value,Scale)
457 else
458 IBError(ibxeInvalidDataConversion, [nil]);
459 SQL_TEXT, SQL_VARYING:
460 AsString := FloatToStr(Value);
461 else
462 IBError(ibxeInvalidDataConversion, [nil]);
463 end;
464 Changed;
465 end;
466
467 procedure TFBArrayElement.SetAsFloat(Value: Float);
468 begin
469 AsDouble := Value;
470 end;
471
472 procedure TFBArrayElement.SetAsCurrency(Value: Currency);
473 begin
474 CheckActive;
475 if (GetSQLDialect < 3) or (SQLType <> SQL_INT64) then
476 AsDouble := Value
477 else
478 begin
479 if Scale = -4 then
480 PCurrency(SQLData)^ := Value
481 else
482 PInt64(SQLData)^ := AdjustScaleFromCurrency(Value,Scale);
483 Changed;
484 end
485 end;
486
487 procedure TFBArrayElement.SetSQLType(aValue: cardinal);
488 begin
489 if aValue = GetSQLType then
490 IBError(ibxeInvalidDataConversion, [nil]);
491 end;
492
493 {TFBArrayMetaData}
494
495 constructor TFBArrayMetaData.Create(aAttachment: IAttachment;
496 aTransaction: ITransaction; relationName, columnName: AnsiString);
497 begin
498 inherited Create;
499 FAttachment := aAttachment;
500 LoadMetaData(aAttachment,aTransaction,relationName, columnName);
501 end;
502
503 constructor TFBArrayMetaData.Create(aAttachment: IAttachment;
504 SQLType: cardinal; tableName: AnsiString; columnName: AnsiString;
505 Scale: integer; size: cardinal; charSetID: cardinal; dimensions: cardinal;
506 bounds: TArrayBounds);
507 var i: integer;
508 begin
509 inherited Create;
510 FAttachment := aAttachment;
511 with FArrayDesc do
512 begin
513 array_desc_dtype := GetDType(SQLType);
514 array_desc_scale := Scale;
515 array_desc_length := UShort(size);
516 StrPLCopy(array_desc_field_name,columnName,sizeof(array_desc_field_name));
517 StrPLCopy(array_desc_relation_name,tableName,sizeof(array_desc_relation_name));
518 array_desc_dimensions := dimensions;
519 array_desc_flags := 0;
520 FCharSetID := charSetID;
521 for i := 0 to Length(bounds) - 1 do
522 begin
523 array_desc_bounds[i].array_bound_lower := bounds[i].LowerBound;
524 array_desc_bounds[i].array_bound_upper := bounds[i].UpperBound;
525 end;
526 end;
527 end;
528
529 function TFBArrayMetaData.GetSQLType: cardinal;
530 begin
531 case FArrayDesc.array_desc_dtype of
532 blr_cstring,
533 blr_cstring2,
534 blr_text,blr_text2:
535 Result := SQL_TEXT;
536 blr_short:
537 Result := SQL_SHORT;
538 blr_long:
539 Result := SQL_LONG;
540 blr_quad, blr_blob_id:
541 Result := SQL_QUAD;
542 blr_float:
543 Result := SQL_FLOAT;
544 blr_double,blr_d_float:
545 Result := SQL_D_FLOAT;
546 blr_timestamp:
547 Result := SQL_TIMESTAMP;
548 blr_varying,blr_varying2:
549 Result := SQL_VARYING;
550 blr_sql_date:
551 Result := SQL_TYPE_DATE;
552 blr_sql_time:
553 Result := SQL_TYPE_TIME;
554 blr_int64:
555 Result := SQL_INT64;
556 end;
557 end;
558
559 function TFBArrayMetaData.GetSQLTypeName: AnsiString;
560 begin
561 Result := TSQLDataItem.GetSQLTypeName(GetSQLType);
562 end;
563
564 function TFBArrayMetaData.GetScale: integer;
565 begin
566 Result := byte(FArrayDesc.array_desc_scale);
567 end;
568
569 function TFBArrayMetaData.GetSize: cardinal;
570 begin
571 Result := FArrayDesc.array_desc_length;
572 end;
573
574 function TFBArrayMetaData.GetTableName: AnsiString;
575 begin
576 with FArrayDesc do
577 SetString(Result,PAnsiChar(@array_desc_relation_name),sizeof(array_desc_relation_name));
578 Result := trim(Result);
579 end;
580
581 function TFBArrayMetaData.GetColumnName: AnsiString;
582 begin
583 with FArrayDesc do
584 SetString(Result,PAnsiChar(@FArrayDesc.array_desc_field_name),sizeof(array_desc_field_name));
585 Result := trim(Result);
586 end;
587
588 function TFBArrayMetaData.GetDimensions: integer;
589 begin
590 Result := FArrayDesc.array_desc_dimensions;
591 end;
592
593 function TFBArrayMetaData.GetBounds: TArrayBounds;
594 var i: integer;
595 begin
596 SetLength(Result,GetDimensions);
597 for i := 0 to GetDimensions - 1 do
598 begin
599 Result[i].UpperBound := FArrayDesc.array_desc_bounds[i].array_bound_upper;
600 Result[i].LowerBound := FArrayDesc.array_desc_bounds[i].array_bound_lower;
601 end;
602 end;
603
604 function TFBArrayMetaData.GetDType(SQLType: cardinal): UChar;
605 begin
606 case SQLType of
607 SQL_TEXT:
608 Result := blr_text;
609 SQL_SHORT:
610 Result := blr_short;
611 SQL_LONG:
612 Result := blr_long;
613 SQL_QUAD:
614 Result := blr_quad;
615 SQL_FLOAT:
616 Result := blr_float;
617 SQL_D_FLOAT:
618 Result := blr_double;
619 SQL_TIMESTAMP:
620 Result := blr_timestamp;
621 SQL_VARYING:
622 Result := blr_varying;
623 SQL_TYPE_DATE:
624 Result := blr_sql_date;
625 SQL_TYPE_TIME:
626 Result := blr_sql_time;
627 SQL_INT64:
628 Result := blr_int64;
629 end;
630 end;
631
632 function TFBArrayMetaData.NumOfElements: integer;
633 var i: integer;
634 Bounds: TArrayBounds;
635 begin
636 Result := 1;
637 Bounds := GetBounds;
638 for i := 0 to Length(Bounds) - 1 do
639 Result := Result * (Bounds[i].UpperBound - Bounds[i].LowerBound + 1);
640 end;
641
642
643 { TFBArray }
644
645 procedure TFBArray.AllocateBuffer;
646 var i: integer;
647 l: integer;
648 Bounds: TArrayBounds;
649 Dims: integer;
650 begin
651 SetLength(FOffsets,0);
652 FreeMem(FBuffer);
653 FBuffer := nil;
654 FLoaded := false;
655
656 with FMetaData as TFBArrayMetaData do
657 begin
658 l := NumOfElements;
659 FElementSize := FArrayDesc.array_desc_length;
660 case GetSQLType of
661 SQL_VARYING:
662 FElementSize := FElementSize + 2;
663 SQL_TEXT:
664 FElementSize := FElementSize + 1;
665 end;
666 FBufSize := FElementSize * l;
667
668 with FFirebirdClientAPI do
669 IBAlloc(FBuffer,0,FBufSize);
670
671 Dims := GetDimensions;
672 SetLength(FOffsets,GetDimensions);
673 Bounds := GetBounds;
674 if FArrayDesc.array_desc_flags = 0 {row major} then
675 begin
676 FOffsets[0] := 1;
677 for i := 0 to Dims - 2 do
678 FOffsets[i+1] := FOffsets[i] * (Bounds[i].UpperBound - Bounds[i].LowerBound + 1);
679 end
680 else
681 begin
682 {column major}
683 FOffsets[Dims-1] := 1;
684 for i := Dims - 1 downto 1 do
685 FOffsets[i-1] := FOffsets[i] * (Bounds[i].UpperBound - Bounds[i].LowerBound + 1);
686 end;
687 end;
688 end;
689
690 procedure TFBArray.Changing;
691 var i: integer;
692 begin
693 for i := 0 to Length(FEventHandlers) - 1 do
694 FEventHandlers[i](self,arChanging);
695 end;
696
697 procedure TFBArray.Changed;
698 var i: integer;
699 begin
700 FModified := true;
701 for i := 0 to Length(FEventHandlers) - 1 do
702 FEventHandlers[i](self,arChanged);
703 end;
704
705 procedure TFBArray.GetArraySlice;
706 begin
707 if FIsNew or FLoaded then Exit;
708 InternalGetSlice;
709 FLoaded := true;
710 end;
711
712 procedure TFBArray.PutArraySlice(Force: boolean);
713 begin
714 if not FModified or not FTransactionIntf.InTransaction or
715 (FTransactionSeqNo < (FTransactionIntf as TFBTransaction).TransactionSeqNo) then Exit;
716
717 InternalPutSlice(Force);
718 FModified := false;
719 FIsNew := false;
720 end;
721
722 function TFBArray.GetOffset(index: array of integer): PByte;
723 var i: integer;
724 Bounds: TArrayBounds;
725 FlatIndex: integer;
726 begin
727 if FMetaData.GetDimensions <> Length(index) then
728 IBError(ibxeInvalidArrayDimensions,[Length(index)]);
729
730 FlatIndex := 0;
731 Bounds := FMetaData.GetBounds;
732 for i := 0 to Length(index) - 1 do
733 begin
734 if (index[i] < Bounds[i].LowerBound) or (index[i] > Bounds[i].UpperBound) then
735 IBError(ibxeInvalidSubscript,[index[i],i]);
736
737 FlatIndex := FlatIndex + FOffsets[i]*(index[i] - Bounds[i].LowerBound);
738 end;
739 Result := FBuffer + FlatIndex*FElementSize;
740 end;
741
742 function TFBArray.GetDataLength: short;
743 begin
744 Result := FElementSize;
745 end;
746
747 function TFBArray.GetArrayDesc: PISC_ARRAY_DESC;
748 begin
749 Result := @((FMetaData as TFBArrayMetaData).FArrayDesc);
750 end;
751
752 constructor TFBArray.Create(aAttachment: IAttachment; aTransaction: TFBTransaction; aField: IArrayMetaData);
753 begin
754 inherited Create(aTransaction);
755 FMetaData := aField;
756 FAttachment := aAttachment;
757 FFirebirdClientAPI := aTransaction.FirebirdAPI;
758 FTransactionIntf := aTransaction;
759 FTransactionSeqNo := aTransaction.TransactionSeqNo;
760 FIsNew := true;
761 FModified := false;
762 FSQLDialect := aAttachment.GetSQLDialect;
763 AllocateBuffer;
764 FElement := TFBArrayElement.Create(self,FBuffer);
765 FElementIntf := FElement;
766 Setlength(FEventHandlers,0);
767 end;
768
769 constructor TFBArray.Create(aAttachment: IAttachment; aTransaction: TFBTransaction;
770 aField: IArrayMetaData; ArrayID: TISC_QUAD);
771 begin
772 inherited Create(aTransaction);
773 FMetaData := aField;
774 FArrayID := ArrayID;
775 FAttachment := aAttachment;
776 FFirebirdClientAPI := aTransaction.FirebirdAPI;
777 FTransactionIntf := aTransaction;
778 FTransactionSeqNo := aTransaction.TransactionSeqNo;
779 FIsNew := false;
780 FModified := false;
781 FSQLDialect := aAttachment.GetSQLDialect;
782 AllocateBuffer;
783 FElement := TFBArrayElement.Create(self,FBuffer);
784 FElementIntf := FElement;
785 Setlength(FEventHandlers,0);
786 end;
787
788 destructor TFBArray.Destroy;
789 begin
790 FreeMem(FBuffer);
791 inherited Destroy;
792 end;
793
794 function TFBArray.GetArrayID: TISC_QUAD;
795 begin
796 PutArraySlice;
797 Result := FArrayID;
798 end;
799
800 procedure TFBArray.Clear;
801 begin
802 FIsNew := true;
803 FModified := false;
804 FArrayID.gds_quad_high := 0;
805 FArrayID.gds_quad_low := 0;
806 AllocateBuffer;
807 end;
808
809 function TFBArray.IsEmpty: boolean;
810 begin
811 Result := FIsNew and not FModified;
812 end;
813
814 procedure TFBArray.PreLoad;
815 begin
816 GetArraySlice;
817 end;
818
819 procedure TFBArray.CancelChanges;
820 begin
821 FModified := false;
822 AllocateBuffer;
823 end;
824
825 procedure TFBArray.SaveChanges;
826 begin
827 PutArraySlice;
828 end;
829
830 function TFBArray.GetSQLDialect: integer;
831 begin
832 Result := FSQLDialect;
833 end;
834
835 procedure TFBArray.TransactionEnding(aTransaction: ITransaction; Force: boolean
836 );
837 begin
838 if (aTransaction = FTransactionIntf) and FModified and not FIsNew then
839 PutArraySlice(Force);
840 end;
841
842 function TFBArray.GetSQLType: cardinal;
843 begin
844 Result := FMetaData.GetSQLType;
845 end;
846
847 function TFBArray.GetSQLTypeName: AnsiString;
848 begin
849 Result := FMetaData.GetSQLTypeName;
850 end;
851
852 function TFBArray.GetScale: integer;
853 begin
854 Result := FMetaData.GetScale;
855 end;
856
857 function TFBArray.GetSize: cardinal;
858 begin
859 Result := FMetaData.GetSize;
860 end;
861
862 function TFBArray.GetCharSetID: cardinal;
863 begin
864 Result := FMetaData.GetCharSetID;
865 end;
866
867 function TFBArray.GetCharSetWidth: integer;
868 begin
869 Result := FMetaData.GetCharSetWidth;
870 end;
871
872 function TFBArray.GetTableName: AnsiString;
873 begin
874 Result := FMetaData.GetTableName;
875 end;
876
877 function TFBArray.GetColumnName: AnsiString;
878 begin
879 Result := FMetaData.GetColumnName;
880 end;
881
882 function TFBArray.GetDimensions: integer;
883 begin
884 Result := FMetaData.GetDimensions;
885 end;
886
887 function TFBArray.GetBounds: TArrayBounds;
888 begin
889 Result := FMetaData.GetBounds;
890 end;
891
892 function TFBArray.GetAsInteger(index: array of integer): integer;
893 begin
894 GetArraySlice;
895 FElement.FBufPtr := GetOffset(index);
896 Result := FElement.GetAsLong;
897 end;
898
899 function TFBArray.GetAsBoolean(index: array of integer): boolean;
900 begin
901 GetArraySlice;
902 FElement.FBufPtr := GetOffset(index);
903 Result := FElement.GetAsBoolean;
904 end;
905
906 function TFBArray.GetAsCurrency(index: array of integer): Currency;
907 begin
908 GetArraySlice;
909 FElement.FBufPtr := GetOffset(index);
910 Result := FElement.GetAsCurrency;
911 end;
912
913 function TFBArray.GetAsInt64(index: array of integer): Int64;
914 begin
915 GetArraySlice;
916 FElement.FBufPtr := GetOffset(index);
917 Result := FElement.GetAsInt64;
918 end;
919
920 function TFBArray.GetAsDateTime(index: array of integer): TDateTime;
921 begin
922 GetArraySlice;
923 FElement.FBufPtr := GetOffset(index);
924 Result := FElement.GetAsDateTime;
925 end;
926
927 function TFBArray.GetAsDouble(index: array of integer): Double;
928 begin
929 GetArraySlice;
930 FElement.FBufPtr := GetOffset(index);
931 Result := FElement.GetAsDouble;
932 end;
933
934 function TFBArray.GetAsFloat(index: array of integer): Float;
935 begin
936 GetArraySlice;
937 FElement.FBufPtr := GetOffset(index);
938 Result := FElement.GetAsFloat;
939 end;
940
941 function TFBArray.GetAsLong(index: array of integer): Long;
942 begin
943 GetArraySlice;
944 FElement.FBufPtr := GetOffset(index);
945 Result := FElement.GetAsLong;
946 end;
947
948 function TFBArray.GetAsShort(index: array of integer): Short;
949 begin
950 GetArraySlice;
951 FElement.FBufPtr := GetOffset(index);
952 Result := FElement.GetAsShort;
953 end;
954
955 function TFBArray.GetAsString(index: array of integer): AnsiString;
956 begin
957 GetArraySlice;
958 FElement.FBufPtr := GetOffset(index);
959 Result := FElement.GetAsString;
960 end;
961
962 function TFBArray.GetAsVariant(index: array of integer): Variant;
963 begin
964 GetArraySlice;
965 FElement.FBufPtr := GetOffset(index);
966 Result := FElement.GetAsVariant;
967 end;
968
969 procedure TFBArray.SetAsInteger(index: array of integer; AValue: integer);
970 begin
971 FElement.FBufPtr := GetOffset(index);
972 FElement.SetAsLong(AValue);
973 end;
974
975 procedure TFBArray.SetAsBoolean(index: array of integer; AValue: boolean);
976 begin
977 FElement.FBufPtr := GetOffset(index);
978 FElement.SetAsBoolean(AValue);
979 end;
980
981 procedure TFBArray.SetAsCurrency(index: array of integer; Value: Currency);
982 begin
983 FElement.FBufPtr := GetOffset(index);
984 FElement.SetAsCurrency(Value);
985 end;
986
987 procedure TFBArray.SetAsInt64(index: array of integer; Value: Int64);
988 begin
989 FElement.FBufPtr := GetOffset(index);
990 FElement.SetAsInt64(Value);
991 end;
992
993 procedure TFBArray.SetAsDate(index: array of integer; Value: TDateTime);
994 begin
995 FElement.FBufPtr := GetOffset(index);
996 FElement.SetAsDate(Value);
997 end;
998
999 procedure TFBArray.SetAsLong(index: array of integer; Value: Long);
1000 begin
1001 FElement.FBufPtr := GetOffset(index);
1002 FElement.SetAsLong(Value);
1003 end;
1004
1005 procedure TFBArray.SetAsTime(index: array of integer; Value: TDateTime);
1006 begin
1007 FElement.FBufPtr := GetOffset(index);
1008 FElement.SetAsTime(Value);
1009 end;
1010
1011 procedure TFBArray.SetAsDateTime(index: array of integer; Value: TDateTime);
1012 begin
1013 FElement.FBufPtr := GetOffset(index);
1014 FElement.SetAsDateTime(Value);
1015 end;
1016
1017 procedure TFBArray.SetAsDouble(index: array of integer; Value: Double);
1018 begin
1019 FElement.FBufPtr := GetOffset(index);
1020 FElement.SetAsDouble(Value);
1021 end;
1022
1023 procedure TFBArray.SetAsFloat(index: array of integer; Value: Float);
1024 begin
1025 FElement.FBufPtr := GetOffset(index);
1026 FElement.SetAsFloat(Value);
1027 end;
1028
1029 procedure TFBArray.SetAsShort(index: array of integer; Value: Short);
1030 begin
1031 FElement.FBufPtr := GetOffset(index);
1032 FElement.SetAsShort(Value);
1033 end;
1034
1035 procedure TFBArray.SetAsString(index: array of integer; Value: AnsiString);
1036 begin
1037 FElement.FBufPtr := GetOffset(index);
1038 FElement.SetAsString(Value);
1039 end;
1040
1041 procedure TFBArray.SetAsVariant(index: array of integer; Value: Variant);
1042 begin
1043 FElement.FBufPtr := GetOffset(index);
1044 FElement.SetAsVariant(Value);
1045 end;
1046
1047 procedure TFBArray.SetBounds(dim, UpperBound, LowerBound: integer);
1048 begin
1049 with (FMetaData as TFBArrayMetaData) do
1050 begin
1051 if (dim < 0) or (dim > GetDimensions) then
1052 IBError(ibxeInvalidArrayDimensions,[dim]);
1053
1054 if (UpperBound > FArrayDesc.array_desc_bounds[dim].array_bound_upper) or
1055 (LowerBound < FArrayDesc.array_desc_bounds[dim].array_bound_lower) or
1056 (UpperBound < FArrayDesc.array_desc_bounds[dim].array_bound_lower) or
1057 (LowerBound > FArrayDesc.array_desc_bounds[dim].array_bound_upper) then
1058 IBError(ibxArrayBoundsCantIncrease,[nil]);
1059
1060 PutArraySlice; {Save any changes}
1061
1062 FArrayDesc.array_desc_bounds[dim].array_bound_upper := UpperBound;
1063 FArrayDesc.array_desc_bounds[dim].array_bound_lower := LowerBound;
1064 end;
1065 AllocateBuffer;
1066 end;
1067
1068 function TFBArray.GetAttachment: IAttachment;
1069 begin
1070 Result := FAttachment;
1071 end;
1072
1073 function TFBArray.GetTransaction: ITransaction;
1074 begin
1075 Result := FTransactionIntf;
1076 end;
1077
1078 procedure TFBArray.AddEventHandler(Handler: TArrayEventHandler);
1079 begin
1080 SetLength(FEventHandlers,Length(FEventHandlers)+1);
1081 FEventHandlers[Length(FEventHandlers)-1] := Handler;
1082 end;
1083
1084 procedure TFBArray.RemoveEventHandler(Handler: TArrayEventHandler);
1085 var i,j : integer;
1086 begin
1087 for i := Length(FEventHandlers) - 1 downto 0 do
1088 if (TMethod(FEventHandlers[i]).Code = TMethod(Handler).Code) and
1089 (TMethod(FEventHandlers[i]).Data = TMethod(Handler).Data) then
1090 begin
1091 for j := i to Length(FEventHandlers) - 2 do
1092 FEventHandlers[i] := FEventHandlers[i+1];
1093 SetLength(FEventHandlers,Length(FEventHandlers) - 1);
1094 end;
1095 end;
1096
1097 end.
1098