ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
(Generate patch)

Comparing ibx/trunk/runtime/IBCustomDataSet.pas (file contents):
Revision 66 by tony, Wed Aug 23 08:23:42 2017 UTC vs.
Revision 108 by tony, Thu Jan 18 14:37:46 2018 UTC

# Line 27 | Line 27
27   {    IBX For Lazarus (Firebird Express)                                  }
28   {    Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
29   {    Portions created by MWA Software are copyright McCallum Whyman      }
30 < {    Associates Ltd 2011 - 2015                                                }
30 > {    Associates Ltd 2011 - 2015                                          }
31   {                                                                        }
32   {************************************************************************}
33  
# Line 53 | Line 53 | uses
53    unix,
54   {$ENDIF}
55    SysUtils, Classes, IBDatabase, IBExternals, IB,  IBSQL, Db,
56 <  IBUtils, IBBlob, IBSQLParser;
56 >  IBUtils, IBBlob, IBSQLParser, IBDatabaseInfo;
57  
58   const
59    BufferCacheSize    =  1000;  { Allocate cache in this many record chunks}
# Line 74 | Line 74 | type
74      procedure SetDataSet(ADataSet: TIBCustomDataSet); virtual; abstract;
75      procedure Apply(UpdateKind: TUpdateKind; buff: PChar); virtual; abstract;
76      function GetSQL(UpdateKind: TUpdateKind): TStrings; virtual; abstract;
77 <    procedure InternalSetParams(Query: TIBSQL; buff: PChar);
77 >    procedure InternalSetParams(Params: ISQLParams; buff: PChar); overload;
78 >    procedure InternalSetParams(Query: TIBSQL; buff: PChar); overload;
79 >    procedure UpdateRecordFromQuery(QryResults: IResults; Buffer: PChar);
80      property DataSet: TIBCustomDataSet read GetDataSet write SetDataSet;
81    public
82      constructor Create(AOwner: TComponent); override;
# Line 207 | Line 209 | type
209       Note: y > 4 will default to Floats
210    }
211    TIBBCDField = class(TBCDField)
212 +  private
213 +    FIdentityColumn: boolean;
214    protected
215 +    procedure Bind(Binding: Boolean); override;
216      class procedure CheckTypeSize(Value: Integer); override;
217      function GetAsCurrency: Currency; override;
218      function GetAsString: string; override;
# Line 215 | Line 220 | type
220      function GetDataSize: Integer; override;
221    public
222      constructor Create(AOwner: TComponent); override;
223 +    property IdentityColumn: boolean read FIdentityColumn;
224    published
225      property Size default 8;
226    end;
227  
228 +  {The following integer field types extend the built in versions to enable IBX appplications
229 +   to check for an Identity column}
230 +
231 +  { TIBSmallintField }
232 +
233 +  TIBSmallintField = class(TSmallintField)
234 +  private
235 +    FIdentityColumn: boolean;
236 +  protected
237 +    procedure Bind(Binding: Boolean); override;
238 +  public
239 +    property IdentityColumn: boolean read FIdentityColumn;
240 +  end;
241 +
242 +  { TIBIntegerField }
243 +
244 +  TIBIntegerField = class(TIntegerField)
245 +  private
246 +    FIdentityColumn: boolean;
247 +  protected
248 +    procedure Bind(Binding: Boolean); override;
249 +  public
250 +    property IdentityColumn: boolean read FIdentityColumn;
251 +  end;
252 +
253 +  { TIBLargeIntField }
254 +
255 +  TIBLargeIntField = class(TLargeIntField)
256 +  private
257 +    FIdentityColumn: boolean;
258 +  protected
259 +    procedure Bind(Binding: Boolean); override;
260 +  public
261 +    property IdentityColumn: boolean read FIdentityColumn;
262 +  end;
263 +
264    {TIBMemoField}
265    {Allows us to show truncated text in DBGrids and anything else that uses
266     DisplayText}
# Line 273 | Line 315 | type
315      FFieldName: string;
316      FGeneratorName: string;
317      FIncrement: integer;
318 +    FQuery: TIBSQL;
319 +    function GetDatabase: TIBDatabase;
320 +    function GetTransaction: TIBTransaction;
321 +    procedure SetDatabase(AValue: TIBDatabase);
322 +    procedure SetGeneratorName(AValue: string);
323      procedure SetIncrement(const AValue: integer);
324 +    procedure SetTransaction(AValue: TIBTransaction);
325 +    procedure SetQuerySQL;
326    protected
327 <    function GetNextValue(ADatabase: TIBDatabase; ATransaction: TIBTransaction): integer;
327 >    function GetNextValue: integer;
328    public
329      constructor Create(Owner: TIBCustomDataSet);
330 +    destructor Destroy; override;
331      procedure Apply;
332      property Owner: TIBCustomDataSet read FOwner;
333 +    property Database: TIBDatabase read GetDatabase write SetDatabase;
334 +    property Transaction: TIBTransaction read GetTransaction write SetTransaction;
335    published
336 <    property Generator: string read FGeneratorName write FGeneratorName;
336 >    property Generator: string read FGeneratorName write SetGeneratorName;
337      property Field: string read FFieldName write FFieldName;
338      property Increment: integer read FIncrement write SetIncrement default 1;
339      property ApplyOnEvent: TIBGeneratorApplyOnEvent read FApplyOnEvent write FApplyOnEvent;
# Line 359 | Line 411 | type
411      FQRefresh,
412      FQSelect,
413      FQModify: TIBSQL;
414 +    FDatabaseInfo: TIBDatabaseInfo;
415      FRecordBufferSize: Integer;
416      FRecordCount: Integer;
417      FRecordSize: Integer;
# Line 388 | Line 441 | type
441      FInTransactionEnd: boolean;
442      FIBLinks: TList;
443      FFieldColumns: PFieldColumns;
444 +    FBufferUpdatedOnQryReturn: boolean;
445 +    procedure ColumnDataToBuffer(QryResults: IResults; ColumnIndex,
446 +      FieldIndex: integer; Buffer: PChar);
447      procedure InitModelBuffer(Qry: TIBSQL; Buffer: PChar);
448      function GetSelectStmtIntf: IStatement;
449      procedure SetUpdateMode(const Value: TUpdateMode);
# Line 435 | Line 491 | type
491      procedure SetDatabase(Value: TIBDatabase);
492      procedure SetDeleteSQL(Value: TStrings);
493      procedure SetInsertSQL(Value: TStrings);
494 <    procedure SetInternalSQLParams(Qry: TIBSQL; Buffer: Pointer);
494 >    procedure SetInternalSQLParams(Params: ISQLParams; Buffer: Pointer);
495      procedure SetRefreshSQL(Value: TStrings);
496      procedure SetSelectSQL(Value: TStrings);
497      procedure SetModifySQL(Value: TStrings);
498      procedure SetTransaction(Value: TIBTransaction);
499      procedure SetUpdateRecordTypes(Value: TIBUpdateRecordTypes);
500      procedure SetUniDirectional(Value: Boolean);
501 +    procedure UpdateRecordFromQuery(QryResults: IResults; Buffer: PChar);
502      procedure RefreshParams;
503      function AdjustPosition(FCache: PChar; Offset: DWORD;
504                              Origin: Integer): DWORD;
# Line 629 | Line 686 | type
686      procedure Post; override;
687      function ParamByName(ParamName: String): ISQLParam;
688      property ArrayFieldCount: integer read FArrayFieldCount;
689 +    property DatabaseInfo: TIBDatabaseInfo read FDatabaseInfo;
690      property UpdateObject: TIBDataSetUpdateObject read FUpdateObject write SetUpdateObject;
691      property UpdatesPending: Boolean read FUpdatesPending;
692      property UpdateRecordTypes: TIBUpdateRecordTypes read FUpdateRecordTypes
# Line 790 | Line 848 | type
848      FCharacterSetName: RawByteString;
849      FCharacterSetSize: integer;
850      FCodePage: TSystemCodePage;
851 +    FIdentityColumn: boolean;
852      FRelationName: string;
853      FDataSize: integer;
854    published
# Line 800 | Line 859 | type
859      property RelationName: string read FRelationName write FRelationName;
860      property ArrayDimensions: integer read FArrayDimensions write FArrayDimensions;
861      property ArrayBounds: TArrayBounds read FArrayBounds write FArrayBounds;
862 +    property IdentityColumn: boolean read FIdentityColumn write FIdentityColumn default false;
863    end;
864  
865   const
866   DefaultFieldClasses: array[TFieldType] of TFieldClass = (
867      nil,                { ftUnknown }
868      TIBStringField,     { ftString }
869 <    TSmallintField,     { ftSmallint }
870 <    TIntegerField,      { ftInteger }
869 >    TIBSmallintField,   { ftSmallint }
870 >    TIBIntegerField,      { ftInteger }
871      TWordField,         { ftWord }
872      TBooleanField,      { ftBoolean }
873      TFloatField,        { ftFloat }
# Line 829 | Line 889 | DefaultFieldClasses: array[TFieldType] o
889      nil,                { ftCursor }
890      TStringField,       { ftFixedChar }
891      nil,    { ftWideString }
892 <    TLargeIntField,     { ftLargeInt }
892 >    TIBLargeIntField,     { ftLargeInt }
893      nil,          { ftADT }
894      TIBArrayField,        { ftArray }
895      nil,    { ftReference }
# Line 873 | Line 933 | type
933      FieldName : String;
934      COMPUTED_BLR : Boolean;
935      DEFAULT_VALUE : boolean;
936 +    IDENTITY_COLUMN : boolean;
937      NextField : TFieldNode;
938    end;
939  
# Line 925 | Line 986 | type
986      Result := str;
987    end;
988  
989 + { TIBLargeIntField }
990 +
991 + procedure TIBLargeIntField.Bind(Binding: Boolean);
992 + begin
993 +  inherited Bind(Binding);
994 +  if Binding and (FieldDef <> nil) then
995 +     FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
996 + end;
997 +
998 + { TIBIntegerField }
999 +
1000 + procedure TIBIntegerField.Bind(Binding: Boolean);
1001 + begin
1002 +  inherited Bind(Binding);
1003 +  if Binding and (FieldDef <> nil) then
1004 +     FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
1005 + end;
1006 +
1007 + { TIBSmallintField }
1008 +
1009 + procedure TIBSmallintField.Bind(Binding: Boolean);
1010 + begin
1011 +  inherited Bind(Binding);
1012 +  if Binding and (FieldDef <> nil) then
1013 +     FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
1014 + end;
1015 +
1016   { TIBArray }
1017  
1018   procedure TIBArray.EventHandler(Sender: IArray; Reason: TArrayEventReason);
# Line 1034 | Line 1122 | begin
1122         {2: case 2 ignored. This should be handled by TIBWideMemo}
1123  
1124         3, {Assume UNICODE_FSS is really UTF8}
1125 <       4: {Include GB18030 - assuming UTF8 routine work for this codeset}
1125 >       4: {Include GB18030 - assuming UTF8 routines work for this codeset}
1126           if DisplayWidth = 0 then
1127             Result := ValidUTF8String(TextToSingleLine(Result))
1128           else
# Line 1236 | Line 1324 | begin
1324    Size := 8;
1325   end;
1326  
1327 + procedure TIBBCDField.Bind(Binding: Boolean);
1328 + begin
1329 +  inherited Bind(Binding);
1330 +  if Binding and (FieldDef <> nil) then
1331 +     FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
1332 + end;
1333 +
1334   class procedure TIBBCDField.CheckTypeSize(Value: Integer);
1335   begin
1336   { No need to check as the base type is currency, not BCD }
# Line 1321 | Line 1416 | constructor TIBCustomDataSet.Create(AOwn
1416   begin
1417    inherited Create(AOwner);
1418    FBase := TIBBase.Create(Self);
1419 +  FDatabaseInfo := TIBDatabaseInfo.Create(self);
1420    FIBLinks := TList.Create;
1421    FCurrentRecord := -1;
1422    FDeletedRecords := 0;
# Line 1959 | Line 2055 | begin
2055    end;
2056   end;
2057  
2058 + {Update Buffer Fields from Query Results}
2059 +
2060 + procedure TIBCustomDataSet.UpdateRecordFromQuery(QryResults: IResults;
2061 +  Buffer: PChar);
2062 + var i, j: integer;
2063 + begin
2064 +  for i := 0 to QryResults.Count - 1 do
2065 +  begin
2066 +    j := GetFieldPosition(QryResults[i].GetAliasName);
2067 +    if j > 0 then
2068 +    begin
2069 +      ColumnDataToBuffer(QryResults,i,j,Buffer);
2070 +      FBufferUpdatedOnQryReturn := true;
2071 +    end;
2072 +  end;
2073 + end;
2074 +
2075 +
2076 + {Move column data returned from query to row buffer}
2077 +
2078 + procedure TIBCustomDataSet.ColumnDataToBuffer(QryResults: IResults;
2079 +               ColumnIndex, FieldIndex: integer; Buffer: PChar);
2080 + var
2081 +  LocalData: PByte;
2082 +  LocalDate: TDateTime;
2083 +  LocalDouble: Double;
2084 +  LocalInt: Integer;
2085 +  LocalBool: wordBool;
2086 +  LocalInt64: Int64;
2087 +  LocalCurrency: Currency;
2088 +  ColData: ISQLData;
2089 + begin
2090 +  LocalData := nil;
2091 +  with PRecordData(Buffer)^.rdFields[FieldIndex], FFieldColumns^[FieldIndex] do
2092 +  begin
2093 +    QryResults.GetData(ColumnIndex,fdIsNull,fdDataLength,LocalData);
2094 +    if not fdIsNull then
2095 +    begin
2096 +      ColData := QryResults[ColumnIndex];
2097 +      case fdDataType of  {Get Formatted data for column types that need formatting}
2098 +        SQL_TYPE_DATE,
2099 +        SQL_TYPE_TIME,
2100 +        SQL_TIMESTAMP:
2101 +        begin
2102 +          {This is an IBX native format and not the TDataset approach. See also GetFieldData}
2103 +          LocalDate := ColData.AsDateTime;
2104 +          LocalData := PByte(@LocalDate);
2105 +        end;
2106 +        SQL_SHORT, SQL_LONG:
2107 +        begin
2108 +          if (fdDataScale = 0) then
2109 +          begin
2110 +            LocalInt := ColData.AsLong;
2111 +            LocalData := PByte(@LocalInt);
2112 +          end
2113 +          else
2114 +          if (fdDataScale >= (-4)) then
2115 +          begin
2116 +            LocalCurrency := ColData.AsCurrency;
2117 +            LocalData := PByte(@LocalCurrency);
2118 +          end
2119 +          else
2120 +          begin
2121 +           LocalDouble := ColData.AsDouble;
2122 +           LocalData := PByte(@LocalDouble);
2123 +          end;
2124 +        end;
2125 +        SQL_INT64:
2126 +        begin
2127 +          if (fdDataScale = 0) then
2128 +          begin
2129 +            LocalInt64 := ColData.AsInt64;
2130 +            LocalData := PByte(@LocalInt64);
2131 +          end
2132 +          else
2133 +          if (fdDataScale >= (-4)) then
2134 +          begin
2135 +            LocalCurrency := ColData.AsCurrency;
2136 +            LocalData := PByte(@LocalCurrency);
2137 +            end
2138 +            else
2139 +            begin
2140 +              LocalDouble := ColData.AsDouble;
2141 +              LocalData := PByte(@LocalDouble);
2142 +            end
2143 +        end;
2144 +        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2145 +        begin
2146 +          LocalDouble := ColData.AsDouble;
2147 +          LocalData := PByte(@LocalDouble);
2148 +        end;
2149 +        SQL_BOOLEAN:
2150 +        begin
2151 +          LocalBool := ColData.AsBoolean;
2152 +          LocalData := PByte(@LocalBool);
2153 +        end;
2154 +      end;
2155 +
2156 +      if fdDataType = SQL_VARYING then
2157 +        Move(LocalData^, Buffer[fdDataOfs], fdDataLength)
2158 +      else
2159 +        Move(LocalData^, Buffer[fdDataOfs], fdDataSize)
2160 +    end
2161 +    else {Null column}
2162 +    if fdDataType = SQL_VARYING then
2163 +      FillChar(Buffer[fdDataOfs],fdDataLength,0)
2164 +    else
2165 +      FillChar(Buffer[fdDataOfs],fdDataSize,0);
2166 +  end;
2167 + end;
2168 +
2169   { Read the record from FQSelect.Current into the record buffer
2170    Then write the buffer to in memory cache }
2171   procedure TIBCustomDataSet.FetchCurrentRecordToBuffer(Qry: TIBSQL;
# Line 1967 | Line 2174 | var
2174    pbd: PBlobDataArray;
2175    pda: PArrayDataArray;
2176    i, j: Integer;
1970  LocalData: PByte;
1971  LocalDate, LocalDouble: Double;
1972  LocalInt: Integer;
1973  LocalBool: wordBool;
1974  LocalInt64: Int64;
1975  LocalCurrency: Currency;
2177    FieldsLoaded: Integer;
2178    p: PRecordData;
2179   begin
# Line 2023 | Line 2224 | begin
2224          continue;
2225        end;
2226      if j > 0 then
2227 <    begin
2027 <      LocalData := nil;
2028 <      with p^.rdFields[j], FFieldColumns^[j] do
2029 <      begin
2030 <        Qry.Current.GetData(i,fdIsNull,fdDataLength,LocalData);
2031 <        if not fdIsNull then
2032 <        begin
2033 <          case fdDataType of  {Get Formatted data for column types that need formatting}
2034 <            SQL_TIMESTAMP:
2035 <            begin
2036 <              LocalDate := TimeStampToMSecs(DateTimeToTimeStamp(Qry[i].AsDateTime));
2037 <              LocalData := PByte(@LocalDate);
2038 <            end;
2039 <            SQL_TYPE_DATE:
2040 <            begin
2041 <              LocalInt := DateTimeToTimeStamp(Qry[i].AsDateTime).Date;
2042 <              LocalData := PByte(@LocalInt);
2043 <            end;
2044 <            SQL_TYPE_TIME:
2045 <            begin
2046 <              LocalInt := DateTimeToTimeStamp(Qry[i].AsDateTime).Time;
2047 <              LocalData := PByte(@LocalInt);
2048 <            end;
2049 <            SQL_SHORT, SQL_LONG:
2050 <            begin
2051 <              if (fdDataScale = 0) then
2052 <              begin
2053 <                LocalInt := Qry[i].AsLong;
2054 <                LocalData := PByte(@LocalInt);
2055 <              end
2056 <              else
2057 <              if (fdDataScale >= (-4)) then
2058 <              begin
2059 <                LocalCurrency := Qry[i].AsCurrency;
2060 <                LocalData := PByte(@LocalCurrency);
2061 <              end
2062 <              else
2063 <              begin
2064 <               LocalDouble := Qry[i].AsDouble;
2065 <               LocalData := PByte(@LocalDouble);
2066 <              end;
2067 <            end;
2068 <            SQL_INT64:
2069 <            begin
2070 <              if (fdDataScale = 0) then
2071 <              begin
2072 <                LocalInt64 := Qry[i].AsInt64;
2073 <                LocalData := PByte(@LocalInt64);
2074 <              end
2075 <              else
2076 <              if (fdDataScale >= (-4)) then
2077 <              begin
2078 <                LocalCurrency := Qry[i].AsCurrency;
2079 <                LocalData := PByte(@LocalCurrency);
2080 <                end
2081 <                else
2082 <                begin
2083 <                  LocalDouble := Qry[i].AsDouble;
2084 <                  LocalData := PByte(@LocalDouble);
2085 <                end
2086 <            end;
2087 <            SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2088 <            begin
2089 <              LocalDouble := Qry[i].AsDouble;
2090 <              LocalData := PByte(@LocalDouble);
2091 <            end;
2092 <            SQL_BOOLEAN:
2093 <            begin
2094 <              LocalBool := Qry[i].AsBoolean;
2095 <              LocalData := PByte(@LocalBool);
2096 <            end;
2097 <          end;
2098 <
2099 <          if fdDataType = SQL_VARYING then
2100 <            Move(LocalData^, Buffer[fdDataOfs], fdDataLength)
2101 <          else
2102 <            Move(LocalData^, Buffer[fdDataOfs], fdDataSize)
2103 <        end
2104 <        else {Null column}
2105 <        if fdDataType = SQL_VARYING then
2106 <          FillChar(Buffer[fdDataOfs],fdDataLength,0)
2107 <        else
2108 <          FillChar(Buffer[fdDataOfs],fdDataSize,0);
2109 <      end;
2110 <    end;
2227 >      ColumnDataToBuffer(Qry.Current,i,j,Buffer);
2228    end;
2229    WriteRecordCache(RecordNumber, Buffer);
2230   end;
# Line 2202 | Line 2319 | begin
2319      FUpdateObject.Apply(ukDelete,Buff)
2320    else
2321    begin
2322 <    SetInternalSQLParams(FQDelete, Buff);
2322 >    SetInternalSQLParams(FQDelete.Params, Buff);
2323      FQDelete.ExecQuery;
2324    end;
2325    with PRecordData(Buff)^ do
# Line 2341 | Line 2458 | begin
2458        end;
2459        Inc(arr);
2460      end;
2461 +  FBufferUpdatedOnQryReturn := false;
2462    if Assigned(FUpdateObject) then
2463    begin
2464      if (Qry = FQDelete) then
# Line 2351 | Line 2469 | begin
2469        FUpdateObject.Apply(ukModify,Buff);
2470    end
2471    else begin
2472 <    SetInternalSQLParams(Qry, Buff);
2472 >    SetInternalSQLParams(Qry.Params, Buff);
2473      Qry.ExecQuery;
2474 +    if Qry.FieldCount > 0 then {Has RETURNING Clause}
2475 +      UpdateRecordFromQuery(Qry.Current,Buff);
2476    end;
2477    PRecordData(Buff)^.rdUpdateStatus := usUnmodified;
2478    PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
2479    SetModified(False);
2480    WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
2481 <  if (FForcedRefresh or FNeedsRefresh) and CanRefresh then
2481 >  if (FForcedRefresh or (FNeedsRefresh and not FBufferUpdatedOnQryReturn)) and CanRefresh then
2482      InternalRefreshRow;
2483   end;
2484  
# Line 2385 | Line 2505 | begin
2505          end
2506          else
2507            Qry := FQRefresh;
2508 <        SetInternalSQLParams(Qry, Buff);
2508 >        SetInternalSQLParams(Qry.Params, Buff);
2509          Qry.ExecQuery;
2510          try
2511            if (Qry.SQLStatementType = SQLExecProcedure) or Qry.Next then
# Line 2594 | Line 2714 | end;
2714  
2715   procedure TIBCustomDataSet.SetDatabase(Value: TIBDatabase);
2716   begin
2717 <  if (FBase.Database <> Value) then
2717 >  if (csLoading in ComponentState) or (FBase.Database <> Value) then
2718    begin
2719      CheckDatasetClosed;
2720 +    InternalUnPrepare;
2721      FBase.Database := Value;
2722      FQDelete.Database := Value;
2723      FQInsert.Database := Value;
2724      FQRefresh.Database := Value;
2725      FQSelect.Database := Value;
2726      FQModify.Database := Value;
2727 +    FDatabaseInfo.Database := Value;
2728 +    FGeneratorField.Database := Value;
2729    end;
2730   end;
2731  
# Line 2624 | Line 2747 | begin
2747    end;
2748   end;
2749  
2750 < procedure TIBCustomDataSet.SetInternalSQLParams(Qry: TIBSQL; Buffer: Pointer);
2750 > procedure TIBCustomDataSet.SetInternalSQLParams(Params: ISQLParams; Buffer: Pointer);
2751   var
2752    i, j: Integer;
2753    cr, data: PChar;
# Line 2640 | Line 2763 | begin
2763      InternalPrepare;
2764    OldBuffer := nil;
2765    try
2766 <    for i := 0 to Qry.Params.GetCount - 1 do
2766 >    for i := 0 to Params.GetCount - 1 do
2767      begin
2768 <      Param := Qry.Params[i];
2768 >      Param := Params[i];
2769        fn := Param.Name;
2770        if (Pos('OLD_', fn) = 1) then {mbcs ok}
2771        begin
# Line 2706 | Line 2829 | begin
2829              end;
2830              SQL_BLOB, SQL_ARRAY, SQL_QUAD:
2831                Param.AsQuad := PISC_QUAD(data)^;
2832 <            SQL_TYPE_DATE:
2833 <            begin
2711 <              ts.Date := PInt(data)^;
2712 <              ts.Time := 0;
2713 <              Param.AsDate := TimeStampToDateTime(ts);
2714 <            end;
2715 <            SQL_TYPE_TIME:
2716 <            begin
2717 <              ts.Date := 0;
2718 <              ts.Time := PInt(data)^;
2719 <              Param.AsTime := TimeStampToDateTime(ts);
2720 <            end;
2832 >            SQL_TYPE_DATE,
2833 >            SQL_TYPE_TIME,
2834              SQL_TIMESTAMP:
2835 <              Param.AsDateTime :=
2836 <                       TimeStampToDateTime(MSecsToTimeStamp(trunc(PDouble(data)^)));
2835 >            {This is an IBX native format and not the TDataset approach. See also SetFieldData}
2836 >              Param.AsDateTime := PDateTime(data)^;
2837              SQL_BOOLEAN:
2838                Param.AsBoolean := PWordBool(data)^;
2839            end;
# Line 2771 | Line 2884 | begin
2884      FQRefresh.Transaction := Value;
2885      FQSelect.Transaction := Value;
2886      FQModify.Transaction := Value;
2887 +    FGeneratorField.Transaction := Value;
2888    end;
2889   end;
2890  
# Line 3672 | Line 3786 | const
3786                 'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3787                 'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3788                 '     (not F.RDB$DEFAULT_VALUE is NULL)) '; {do not localize}
3789 +
3790 +  DefaultSQLODS12 = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
3791 +               'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME, R.RDB$IDENTITY_TYPE ' + {do not localize}
3792 +               'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
3793 +               'where R.RDB$RELATION_NAME = :RELATION ' +  {do not localize}
3794 +               'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
3795 +               'and ((not F.RDB$COMPUTED_BLR is NULL) or ' + {do not localize}
3796 +               '     (not F.RDB$DEFAULT_VALUE is NULL) or ' + {do not localize}
3797 +               '     ( not R.RDB$IDENTITY_TYPE is NULL))' ; {do not localize}
3798 +
3799   var
3800    FieldType: TFieldType;
3801    FieldSize: Word;
# Line 3712 | Line 3836 | var
3836        FField.FieldName := Query.Fields[2].AsString;
3837        FField.DEFAULT_VALUE := not Query.Fields[1].IsNull;
3838        FField.COMPUTED_BLR := not Query.Fields[0].IsNull;
3839 +      FField.IDENTITY_COLUMN := (Query.FieldCount > 3) and not Query.Fields[3].IsNull;
3840        FField.NextField := Result.FieldNodes;
3841        Result.FieldNodes := FField;
3842        Query.Next;
# Line 3765 | Line 3890 | var
3890          FField := Ffield.NextField;
3891    end;
3892  
3893 +  function Is_IDENTITY_COLUMN(Relation, Field : String) : Boolean;
3894 +  var
3895 +    FRelation : TRelationNode;
3896 +    FField : TFieldNode;
3897 +  begin
3898 +    FRelation := FRelationNodes;
3899 +    while Assigned(FRelation) and
3900 +         (FRelation.RelationName <> Relation) do
3901 +      FRelation := FRelation.NextRelation;
3902 +    if not Assigned(FRelation) then
3903 +      FRelation := Add_Node(Relation, Field);
3904 +    Result := false;
3905 +    FField := FRelation.FieldNodes;
3906 +    while Assigned(FField) do
3907 +      if FField.FieldName = Field then
3908 +      begin
3909 +        Result := Ffield.IDENTITY_COLUMN;
3910 +        Exit;
3911 +      end
3912 +      else
3913 +        FField := Ffield.NextField;
3914 +  end;
3915 +
3916    Procedure FreeNodes;
3917    var
3918      FRelation : TRelationNode;
# Line 3798 | Line 3946 | begin
3946      FieldIndex := 0;
3947      if (Length(FMappedFieldPosition) < SourceQuery.MetaData.Count) then
3948        SetLength(FMappedFieldPosition, SourceQuery.MetaData.Count);
3949 <    Query.SQL.Text := DefaultSQL;
3949 >    if FDatabaseInfo.ODSMajorVersion >= 12 then
3950 >      Query.SQL.Text := DefaultSQLODS12
3951 >    else
3952 >      Query.SQL.Text := DefaultSQL;
3953      Query.Prepare;
3954      SetLength(FAliasNameMap, SourceQuery.MetaData.Count);
3955      SetLength(FAliasNameList, SourceQuery.MetaData.Count);
# Line 3812 | Line 3963 | begin
3963          FieldName := getSQLName;
3964          FAliasNameList[i] := DBAliasName;
3965          FieldSize := 0;
3966 <        FieldDataSize := 0;
3966 >        FieldDataSize := GetSize;
3967          FieldPrecision := 0;
3968          FieldNullable := IsNullable;
3969          CharSetSize := 0;
# Line 3829 | Line 3980 | begin
3980                CharSetSize := 1;
3981              CharSetName := Database.Attachment.GetCharsetName(getCharSetID);
3982              Database.Attachment.CharSetID2CodePage(getCharSetID,FieldCodePage);
3832            FieldDataSize := GetSize;
3983              FieldSize := FieldDataSize div CharSetSize;
3984              FieldType := ftString;
3985            end;
# Line 3844 | Line 3994 | begin
3994                FieldType := ftBCD;
3995                FieldPrecision := 4;
3996                FieldSize := -getScale;
3847              FieldDataSize := FieldSize;
3997              end;
3998            end;
3999            SQL_LONG:
# Line 3869 | Line 4018 | begin
4018                FieldPrecision := 9;
4019                FieldSize := -getScale;
4020              end;
3872            FieldDataSize := FieldSize;
4021            end;
4022  
4023            SQL_INT64:
# Line 3884 | Line 4032 | begin
4032              end
4033              else
4034                FieldType := ftFloat;
3887            FieldDataSize := FieldSize;
4035            end;
4036            SQL_TIMESTAMP: FieldType := ftDateTime;
4037            SQL_TYPE_TIME: FieldType := ftTime;
# Line 3902 | Line 4049 | begin
4049              end
4050              else
4051                FieldType := ftBlob;
3905            FieldDataSize := FieldSize;
4052            end;
4053            SQL_ARRAY:
4054            begin
4055              FieldSize := sizeof (TISC_QUAD);
3910            FieldDataSize := FieldSize;
4056              FieldType := ftArray;
4057              ArrayMetaData := GetArrayMetaData;
4058              if ArrayMetaData <> nil then
# Line 3943 | Line 4088 | begin
4088              ArrayBounds := aArrayBounds;
4089              if (FieldName <> '') and (RelationName <> '') then
4090              begin
4091 +              IdentityColumn := Is_IDENTITY_COLUMN(RelationName, FieldName);
4092                if Has_COMPUTED_BLR(RelationName, FieldName) then
4093                begin
4094                  Attributes := [faReadOnly];
# Line 4014 | Line 4160 | begin
4160      for i := 0 to SQLParams.GetCount - 1 do
4161      begin
4162        cur_field := DataSource.DataSet.FindField(SQLParams[i].Name);
4163 <      cur_param := SQLParams[i];
4164 <      if (cur_field <> nil) then begin
4163 >      if (cur_field <> nil) then
4164 >      begin
4165 >        cur_param := SQLParams[i];
4166          if (cur_field.IsNull) then
4167            cur_param.IsNull := True
4168 <        else case cur_field.DataType of
4168 >        else
4169 >        case cur_field.DataType of
4170            ftString:
4171              cur_param.AsString := cur_field.AsString;
4172            ftBoolean:
# Line 4028 | Line 4176 | begin
4176            ftInteger:
4177              cur_param.AsLong := cur_field.AsInteger;
4178            ftLargeInt:
4179 <            cur_param.AsInt64 := TLargeIntField(cur_field).AsLargeInt;
4179 >            cur_param.AsInt64 := cur_field.AsLargeInt;
4180            ftFloat, ftCurrency:
4181             cur_param.AsDouble := cur_field.AsFloat;
4182            ftBCD:
# Line 4687 | Line 4835 | begin
4835    Transaction.StartTransaction;
4836   end;
4837  
4838 < function TIBCustomDataSet.PSGetTableName: string;
4838 > function TIBCustomDataSet.PsGetTableName: string;
4839   begin
4840   //  if not FInternalPrepared then
4841   //    InternalPrepare;
# Line 4782 | Line 4930 | end;
4930   function TIBCustomDataSet.GetFieldData(Field: TField; Buffer: Pointer;
4931    NativeFormat: Boolean): Boolean;
4932   begin
4933 <  if (Field.DataType = ftBCD) and not NativeFormat then
4933 >  {These datatypes use IBX conventions and not TDataset conventions}
4934 >  if (Field.DataType in [ftBCD,ftDateTime,ftDate,ftTime]) and not NativeFormat then
4935      Result := InternalGetFieldData(Field, Buffer)
4936    else
4937      Result := inherited GetFieldData(Field, Buffer, NativeFormat);
# Line 4808 | Line 4957 | end;
4957   procedure TIBCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer;
4958    NativeFormat: Boolean);
4959   begin
4960 <  if (not NativeFormat) and (Field.DataType = ftBCD) then
4960 >  {These datatypes use IBX conventions and not TDataset conventions}
4961 >  if (not NativeFormat) and (Field.DataType in [ftBCD,ftDateTime,ftDate,ftTime]) then
4962      InternalSetfieldData(Field, Buffer)
4963    else
4964      inherited SetFieldData(Field, buffer, NativeFormat);
# Line 4828 | Line 4978 | begin
4978    inherited Destroy;
4979   end;
4980  
4981 < procedure TIBDataSetUpdateObject.SetRefreshSQL(Value: TStrings);
4981 > procedure TIBDataSetUpdateObject.SetRefreshSQL(value: TStrings);
4982   begin
4983    FRefreshSQL.Assign(Value);
4984   end;
4985  
4986 + procedure TIBDataSetUpdateObject.InternalSetParams(Params: ISQLParams;
4987 +  buff: PChar);
4988 + begin
4989 +  if not Assigned(DataSet) then Exit;
4990 +  DataSet.SetInternalSQLParams(Params, buff);
4991 + end;
4992 +
4993   procedure TIBDataSetUpdateObject.InternalSetParams(Query: TIBSQL; buff: PChar);
4994   begin
4995 +  InternalSetParams(Query.Params,buff);
4996 + end;
4997 +
4998 + procedure TIBDataSetUpdateObject.UpdateRecordFromQuery(QryResults: IResults;
4999 +  Buffer: PChar);
5000 + begin
5001    if not Assigned(DataSet) then Exit;
5002 <  DataSet.SetInternalSQLParams(Query, buff);
5002 >  DataSet.UpdateRecordFromQuery(QryResults, Buffer);
5003   end;
5004  
5005   function TIBDSBlobStream.GetSize: Int64;
# Line 4899 | Line 5062 | end;
5062  
5063   procedure TIBGenerator.SetIncrement(const AValue: integer);
5064   begin
5065 +  if FIncrement = AValue then Exit;
5066    if AValue < 0 then
5067 <     raise Exception.Create('A Generator Increment cannot be negative');
5068 <  FIncrement := AValue
5067 >    IBError(ibxeNegativeGenerator,[]);
5068 >  FIncrement := AValue;
5069 >  SetQuerySQL;
5070   end;
5071  
5072 < function TIBGenerator.GetNextValue(ADatabase: TIBDatabase;
4908 <  ATransaction: TIBTransaction): integer;
5072 > procedure TIBGenerator.SetTransaction(AValue: TIBTransaction);
5073   begin
5074 <  with TIBSQL.Create(nil) do
5075 <  try
5076 <    Database := ADatabase;
5077 <    Transaction := ATransaction;
5078 <    if not assigned(Database) then
5079 <       IBError(ibxeCannotSetDatabase,[]);
5080 <    if not assigned(Transaction) then
5081 <       IBError(ibxeCannotSetTransaction,[]);
5082 <    with Transaction do
5083 <      if not InTransaction then StartTransaction;
5084 <    SQL.Text := Format('Select Gen_ID(%s,%d) as ID From RDB$Database',[FGeneratorName,Increment]);
5085 <    Prepare;
5074 >  FQuery.Transaction := AValue;
5075 > end;
5076 >
5077 > procedure TIBGenerator.SetQuerySQL;
5078 > begin
5079 >  FQuery.SQL.Text := Format('Select Gen_ID(%s,%d) From RDB$Database',[FGeneratorName,Increment]);
5080 > end;
5081 >
5082 > function TIBGenerator.GetDatabase: TIBDatabase;
5083 > begin
5084 >  Result := FQuery.Database;
5085 > end;
5086 >
5087 > function TIBGenerator.GetTransaction: TIBTransaction;
5088 > begin
5089 >  Result := FQuery.Transaction;
5090 > end;
5091 >
5092 > procedure TIBGenerator.SetDatabase(AValue: TIBDatabase);
5093 > begin
5094 >  FQuery.Database := AValue;
5095 > end;
5096 >
5097 > procedure TIBGenerator.SetGeneratorName(AValue: string);
5098 > begin
5099 >  if FGeneratorName = AValue then Exit;
5100 >  FGeneratorName := AValue;
5101 >  SetQuerySQL;
5102 > end;
5103 >
5104 > function TIBGenerator.GetNextValue: integer;
5105 > begin
5106 >  with FQuery do
5107 >  begin
5108 >    Transaction.Active := true;
5109      ExecQuery;
5110      try
5111 <      Result := FieldByName('ID').AsInteger
5111 >      Result := Fields[0].AsInteger
5112      finally
5113        Close
5114      end;
4928  finally
4929    Free
5115    end;
5116   end;
5117  
# Line 4934 | Line 5119 | constructor TIBGenerator.Create(Owner: T
5119   begin
5120    FOwner := Owner;
5121    FIncrement := 1;
5122 +  FQuery := TIBSQL.Create(nil);
5123 + end;
5124 +
5125 + destructor TIBGenerator.Destroy;
5126 + begin
5127 +  if assigned(FQuery) then FQuery.Free;
5128 +  inherited Destroy;
5129   end;
5130  
5131  
5132   procedure TIBGenerator.Apply;
5133   begin
5134 <  if (FGeneratorName <> '') and (FFieldName <> '') and Owner.FieldByName(FFieldName).IsNull then
5135 <    Owner.FieldByName(FFieldName).AsInteger := GetNextValue(Owner.Database,Owner.Transaction);
5134 >  if assigned(Database) and assigned(Transaction) and
5135 >       (FGeneratorName <> '') and (FFieldName <> '') and Owner.FieldByName(FFieldName).IsNull then
5136 >    Owner.FieldByName(FFieldName).AsInteger := GetNextValue;
5137   end;
5138  
5139  

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines