ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/fbintf/client/FBArray.pas
Revision: 47
Committed: Mon Jan 9 15:31:51 2017 UTC (7 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 28006 byte(s)
Log Message:
Committing updates for Release R2-0-1

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