ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/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

# User Rev Content
1 tony 45 (*
2     * Firebird Interface (fbintf). The fbintf components provide a set of
3     * Pascal language bindings for the Firebird API.
4     *
5     * The contents of this file are subject to the Initial Developer's
6     * Public License Version 1.0 (the "License"); you may not use this
7     * file except in compliance with the License. You may obtain a copy
8     * of the License here:
9     *
10     * http://www.firebirdsql.org/index.php?op=doc&id=idpl
11     *
12     * Software distributed under the License is distributed on an "AS
13     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14     * implied. See the License for the specific language governing rights
15     * and limitations under the License.
16     *
17     * The Initial Developer of the Original Code is Tony Whyman.
18     *
19     * The Original Code is (C) 2016 Tony Whyman, MWA Software
20     * (http://www.mwasoftware.co.uk).
21     *
22     * All Rights Reserved.
23     *
24     * Contributor(s): ______________________________________.
25     *
26     *)
27     unit FBArray;
28 tony 56 {$IFDEF MSWINDOWS}
29     {$DEFINE WINDOWS}
30     {$ENDIF}
31 tony 45
32     {$IFDEF FPC}
33 tony 56 {$mode delphi}
34 tony 45 {$codepage UTF8}
35     {$interfaces COM}
36     {$ENDIF}
37    
38     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 tony 56 FBufPtr: PByte;
76 tony 45 FArray: TFBArray;
77     protected
78     function GetSQLDialect: integer; override;
79     procedure Changing; override;
80     procedure Changed; override;
81 tony 56 function SQLData: PByte; override;
82 tony 45 function GetDataLength: cardinal; override;
83     function GetCodePage: TSystemCodePage; override;
84 tony 47 function getCharSetID: cardinal; override;
85 tony 45 procedure SetDataLength(len: cardinal); override;
86     procedure SetSQLType(aValue: cardinal); override;
87     public
88 tony 56 constructor Create(anArray: TFBArray; P: PByte);
89 tony 45 function GetSQLType: cardinal; override;
90 tony 56 function GetName: AnsiString; override;
91 tony 45 function GetScale: integer; override;
92     function GetSize: integer;
93 tony 309 function GetCharSetWidth: integer; override;
94 tony 56 function GetAsString: AnsiString; override;
95 tony 45 procedure SetAsLong(Value: Long); override;
96     procedure SetAsShort(Value: Short); override;
97     procedure SetAsInt64(Value: Int64); override;
98 tony 56 procedure SetAsString(Value: AnsiString); override;
99 tony 45 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 tony 47 private
108     function GetDType(SQLType: cardinal): UChar;
109 tony 45 protected
110     FArrayDesc: TISC_ARRAY_DESC;
111 tony 47 FCharSetID: integer;
112 tony 60 FAttachment: IAttachment;
113 tony 45 procedure LoadMetaData(aAttachment: IAttachment; aTransaction: ITransaction;
114 tony 56 relationName, columnName: AnsiString); virtual; abstract;
115 tony 45 function NumOfElements: integer;
116     public
117     constructor Create(aAttachment: IAttachment; aTransaction: ITransaction;
118 tony 56 relationName, columnName: AnsiString); overload;
119 tony 60 constructor Create(aAttachment: IAttachment;SQLType: cardinal; tableName: AnsiString; columnName: AnsiString;
120 tony 47 Scale: integer; size: cardinal; charSetID: cardinal;
121     dimensions: cardinal; bounds: TArrayBounds); overload;
122 tony 45 function GetCodePage: TSystemCodePage; virtual; abstract;
123    
124     public
125     {IArrayMetaData}
126     function GetSQLType: cardinal;
127 tony 56 function GetSQLTypeName: AnsiString;
128 tony 45 function GetScale: integer;
129     function GetSize: cardinal;
130     function GetCharSetID: cardinal; virtual; abstract;
131 tony 309 function GetCharSetWidth: integer; virtual; abstract;
132 tony 56 function GetTableName: AnsiString;
133     function GetColumnName: AnsiString;
134 tony 45 function GetDimensions: integer;
135     function GetBounds: TArrayBounds;
136     end;
137    
138    
139     { TFBArray }
140    
141     TFBArray = class(TActivityReporter,IArray)
142     private
143 tony 263 FFirebirdClientAPI: TFBClientAPI;
144 tony 45 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 tony 56 function GetOffset(index: array of integer): PByte;
160 tony 45 function GetDataLength: short;
161     protected
162 tony 56 FBuffer: PByte;
163 tony 45 FBufSize: ISC_LONG;
164     FArrayID: TISC_QUAD;
165     procedure AllocateBuffer; virtual;
166 tony 291 procedure Changing; virtual;
167     procedure Changed; virtual;
168 tony 45 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 tony 56 function GetSQLTypeName: AnsiString;
184 tony 45 function GetScale: integer;
185     function GetSize: cardinal;
186     function GetCharSetID: cardinal;
187 tony 309 function GetCharSetWidth: integer;
188 tony 56 function GetTableName: AnsiString;
189     function GetColumnName: AnsiString;
190 tony 45 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 tony 56 function GetAsString(index: array of integer): AnsiString;
209 tony 45 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 tony 56 procedure SetAsString(index: array of integer; Value: AnsiString);
222 tony 45 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 tony 56 function TFBArrayElement.SQLData: PByte;
254 tony 45 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 tony 47 function TFBArrayElement.getCharSetID: cardinal;
269     begin
270     Result := (FArray.FMetaData as TFBArrayMetaData).GetCharSetID;
271     end;
272    
273 tony 45 procedure TFBArrayElement.SetDataLength(len: cardinal);
274     begin
275     if len > GetDataLength then
276     IBError(ibxeArrayElementOverFlow,[nil]);
277     end;
278    
279 tony 56 constructor TFBArrayElement.Create(anArray: TFBArray; P: PByte);
280 tony 45 begin
281 tony 263 inherited Create(anArray.FFirebirdClientAPI);
282 tony 45 FArray := anArray;
283     FBufPtr := P;
284     end;
285    
286     function TFBArrayElement.GetSQLType: cardinal;
287     begin
288     Result := FArray.FMetaData.GetSQLType;
289     end;
290    
291 tony 56 function TFBArrayElement.GetName: AnsiString;
292 tony 45 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 tony 309 function TFBArrayElement.GetCharSetWidth: integer;
307     begin
308     Result := FArray.FMetaData.GetCharSetWidth;
309     end;
310    
311 tony 56 function TFBArrayElement.GetAsString: AnsiString;
312 tony 45 var rs: RawByteString;
313     begin
314     case GetSQLType of
315     SQL_VARYING:
316     begin
317 tony 56 rs := strpas(PAnsiChar(FBufPtr));
318 tony 45 SetCodePage(rs,GetCodePage,false);
319     Result := rs;
320     end;
321     SQL_TEXT:
322     begin
323 tony 56 SetString(rs,PAnsiChar(FBufPtr),GetDataLength);
324 tony 45 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 tony 56 procedure TFBArrayElement.SetAsString(Value: AnsiString);
366 tony 45 var len: integer;
367     ElementSize: integer;
368     begin
369     CheckActive;
370     case GetSQLType of
371     SQL_BOOLEAN:
372 tony 56 if AnsiCompareText(Value,STrue) = 0 then
373 tony 45 AsBoolean := true
374     else
375 tony 56 if AnsiCompareText(Value,SFalse) = 0 then
376 tony 45 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 tony 56 (FBufPtr+len)^ := 0;
391 tony 45 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 tony 59 SetAsInt64(AdjustScaleFromCurrency(StrToCurr(Value),GetScale));
412 tony 45
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 tony 56 aTransaction: ITransaction; relationName, columnName: AnsiString);
497 tony 45 begin
498     inherited Create;
499 tony 60 FAttachment := aAttachment;
500 tony 45 LoadMetaData(aAttachment,aTransaction,relationName, columnName);
501     end;
502    
503 tony 60 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 tony 47 var i: integer;
508     begin
509     inherited Create;
510 tony 60 FAttachment := aAttachment;
511 tony 47 with FArrayDesc do
512     begin
513     array_desc_dtype := GetDType(SQLType);
514 tony 56 array_desc_scale := Scale;
515 tony 47 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 tony 45 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 tony 56 function TFBArrayMetaData.GetSQLTypeName: AnsiString;
560 tony 45 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 tony 56 function TFBArrayMetaData.GetTableName: AnsiString;
575 tony 45 begin
576     with FArrayDesc do
577 tony 56 SetString(Result,PAnsiChar(@array_desc_relation_name),sizeof(array_desc_relation_name));
578 tony 45 Result := trim(Result);
579     end;
580    
581 tony 56 function TFBArrayMetaData.GetColumnName: AnsiString;
582 tony 45 begin
583     with FArrayDesc do
584 tony 56 SetString(Result,PAnsiChar(@FArrayDesc.array_desc_field_name),sizeof(array_desc_field_name));
585 tony 45 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 tony 47 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 tony 45 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 tony 56 Result := Result * (Bounds[i].UpperBound - Bounds[i].LowerBound + 1);
640 tony 45 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 tony 56 FElementSize := FElementSize + 2;
663 tony 45 SQL_TEXT:
664 tony 56 FElementSize := FElementSize + 1;
665 tony 45 end;
666     FBufSize := FElementSize * l;
667    
668 tony 263 with FFirebirdClientAPI do
669 tony 45 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 tony 56 function TFBArray.GetOffset(index: array of integer): PByte;
723 tony 45 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 tony 56 FlatIndex := FlatIndex + FOffsets[i]*(index[i] - Bounds[i].LowerBound);
738 tony 45 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 tony 263 FFirebirdClientAPI := aTransaction.FirebirdAPI;
758 tony 45 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 tony 291 FFirebirdClientAPI := aTransaction.FirebirdAPI;
777 tony 45 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 tony 56 function TFBArray.GetSQLTypeName: AnsiString;
848 tony 45 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 tony 309 function TFBArray.GetCharSetWidth: integer;
868     begin
869     Result := FMetaData.GetCharSetWidth;
870     end;
871    
872 tony 56 function TFBArray.GetTableName: AnsiString;
873 tony 45 begin
874     Result := FMetaData.GetTableName;
875     end;
876    
877 tony 56 function TFBArray.GetColumnName: AnsiString;
878 tony 45 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 tony 56 function TFBArray.GetAsString(index: array of integer): AnsiString;
956 tony 45 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 tony 56 procedure TFBArray.SetAsString(index: array of integer; Value: AnsiString);
1036 tony 45 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 tony 56 if (TMethod(FEventHandlers[i]).Code = TMethod(Handler).Code) and
1089     (TMethod(FEventHandlers[i]).Data = TMethod(Handler).Data) then
1090 tony 45 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