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 100 by tony, Mon Jan 1 11:31:07 2018 UTC vs.
Revision 101 by tony, Thu Jan 18 14:37:18 2018 UTC

# 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 76 | Line 76 | type
76      function GetSQL(UpdateKind: TUpdateKind): TStrings; virtual; abstract;
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 208 | 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 216 | 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 360 | Line 401 | type
401      FQRefresh,
402      FQSelect,
403      FQModify: TIBSQL;
404 +    FDatabaseInfo: TIBDatabaseInfo;
405      FRecordBufferSize: Integer;
406      FRecordCount: Integer;
407      FRecordSize: Integer;
# Line 389 | Line 431 | type
431      FInTransactionEnd: boolean;
432      FIBLinks: TList;
433      FFieldColumns: PFieldColumns;
434 +    procedure ColumnDataToBuffer(QryResults: IResults; ColumnIndex,
435 +      FieldIndex: integer; Buffer: PChar);
436      procedure InitModelBuffer(Qry: TIBSQL; Buffer: PChar);
437      function GetSelectStmtIntf: IStatement;
438      procedure SetUpdateMode(const Value: TUpdateMode);
# Line 443 | Line 487 | type
487      procedure SetTransaction(Value: TIBTransaction);
488      procedure SetUpdateRecordTypes(Value: TIBUpdateRecordTypes);
489      procedure SetUniDirectional(Value: Boolean);
490 +    procedure UpdateRecordFromQuery(QryResults: IResults; Buffer: PChar);
491      procedure RefreshParams;
492      function AdjustPosition(FCache: PChar; Offset: DWORD;
493                              Origin: Integer): DWORD;
# Line 791 | Line 836 | type
836      FCharacterSetName: RawByteString;
837      FCharacterSetSize: integer;
838      FCodePage: TSystemCodePage;
839 +    FIdentityColumn: boolean;
840      FRelationName: string;
841      FDataSize: integer;
842    published
# Line 801 | Line 847 | type
847      property RelationName: string read FRelationName write FRelationName;
848      property ArrayDimensions: integer read FArrayDimensions write FArrayDimensions;
849      property ArrayBounds: TArrayBounds read FArrayBounds write FArrayBounds;
850 +    property IdentityColumn: boolean read FIdentityColumn write FIdentityColumn default false;
851    end;
852  
853   const
854   DefaultFieldClasses: array[TFieldType] of TFieldClass = (
855      nil,                { ftUnknown }
856      TIBStringField,     { ftString }
857 <    TSmallintField,     { ftSmallint }
858 <    TIntegerField,      { ftInteger }
857 >    TIBSmallintField,   { ftSmallint }
858 >    TIBIntegerField,      { ftInteger }
859      TWordField,         { ftWord }
860      TBooleanField,      { ftBoolean }
861      TFloatField,        { ftFloat }
# Line 830 | Line 877 | DefaultFieldClasses: array[TFieldType] o
877      nil,                { ftCursor }
878      TStringField,       { ftFixedChar }
879      nil,    { ftWideString }
880 <    TLargeIntField,     { ftLargeInt }
880 >    TIBLargeIntField,     { ftLargeInt }
881      nil,          { ftADT }
882      TIBArrayField,        { ftArray }
883      nil,    { ftReference }
# Line 874 | Line 921 | type
921      FieldName : String;
922      COMPUTED_BLR : Boolean;
923      DEFAULT_VALUE : boolean;
924 +    IDENTITY_COLUMN : boolean;
925      NextField : TFieldNode;
926    end;
927  
# Line 926 | Line 974 | type
974      Result := str;
975    end;
976  
977 + { TIBLargeIntField }
978 +
979 + procedure TIBLargeIntField.Bind(Binding: Boolean);
980 + begin
981 +  inherited Bind(Binding);
982 +  if Binding and (FieldDef <> nil) then
983 +     FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
984 + end;
985 +
986 + { TIBIntegerField }
987 +
988 + procedure TIBIntegerField.Bind(Binding: Boolean);
989 + begin
990 +  inherited Bind(Binding);
991 +  if Binding and (FieldDef <> nil) then
992 +     FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
993 + end;
994 +
995 + { TIBSmallintField }
996 +
997 + procedure TIBSmallintField.Bind(Binding: Boolean);
998 + begin
999 +  inherited Bind(Binding);
1000 +  if Binding and (FieldDef <> nil) then
1001 +     FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
1002 + end;
1003 +
1004   { TIBArray }
1005  
1006   procedure TIBArray.EventHandler(Sender: IArray; Reason: TArrayEventReason);
# Line 1035 | Line 1110 | begin
1110         {2: case 2 ignored. This should be handled by TIBWideMemo}
1111  
1112         3, {Assume UNICODE_FSS is really UTF8}
1113 <       4: {Include GB18030 - assuming UTF8 routine work for this codeset}
1113 >       4: {Include GB18030 - assuming UTF8 routines work for this codeset}
1114           if DisplayWidth = 0 then
1115             Result := ValidUTF8String(TextToSingleLine(Result))
1116           else
# Line 1237 | Line 1312 | begin
1312    Size := 8;
1313   end;
1314  
1315 + procedure TIBBCDField.Bind(Binding: Boolean);
1316 + begin
1317 +  inherited Bind(Binding);
1318 +  if Binding and (FieldDef <> nil) then
1319 +     FIdentityColumn := (FieldDef as TIBFieldDef).IdentityColumn;
1320 + end;
1321 +
1322   class procedure TIBBCDField.CheckTypeSize(Value: Integer);
1323   begin
1324   { No need to check as the base type is currency, not BCD }
# Line 1322 | Line 1404 | constructor TIBCustomDataSet.Create(AOwn
1404   begin
1405    inherited Create(AOwner);
1406    FBase := TIBBase.Create(Self);
1407 +  FDatabaseInfo := TIBDatabaseInfo.Create(self);
1408    FIBLinks := TList.Create;
1409    FCurrentRecord := -1;
1410    FDeletedRecords := 0;
# Line 1960 | Line 2043 | begin
2043    end;
2044   end;
2045  
2046 + {Update Buffer Fields from Query Results}
2047 +
2048 + procedure TIBCustomDataSet.UpdateRecordFromQuery(QryResults: IResults;
2049 +  Buffer: PChar);
2050 + var i, j: integer;
2051 + begin
2052 +  for i := 0 to QryResults.Count - 1 do
2053 +  begin
2054 +    j := GetFieldPosition(QryResults[i].GetAliasName);
2055 +    if j > 0 then
2056 +      ColumnDataToBuffer(QryResults,i,j,Buffer);
2057 +  end;
2058 + end;
2059 +
2060 +
2061 + {Move column data returned from query to row buffer}
2062 +
2063 + procedure TIBCustomDataSet.ColumnDataToBuffer(QryResults: IResults;
2064 +               ColumnIndex, FieldIndex: integer; Buffer: PChar);
2065 + var
2066 +  LocalData: PByte;
2067 +  LocalDate, LocalDouble: Double;
2068 +  LocalInt: Integer;
2069 +  LocalBool: wordBool;
2070 +  LocalInt64: Int64;
2071 +  LocalCurrency: Currency;
2072 +  p: PRecordData;
2073 +  ColData: ISQLData;
2074 + begin
2075 +  p := PRecordData(Buffer);
2076 +  LocalData := nil;
2077 +  with p^.rdFields[FieldIndex], FFieldColumns^[FieldIndex] do
2078 +  begin
2079 +    QryResults.GetData(ColumnIndex,fdIsNull,fdDataLength,LocalData);
2080 +    if not fdIsNull then
2081 +    begin
2082 +      ColData := QryResults[ColumnIndex];
2083 +      case fdDataType of  {Get Formatted data for column types that need formatting}
2084 +        SQL_TIMESTAMP:
2085 +        begin
2086 +          LocalDate := TimeStampToMSecs(DateTimeToTimeStamp(ColData.AsDateTime));
2087 +          LocalData := PByte(@LocalDate);
2088 +        end;
2089 +        SQL_TYPE_DATE:
2090 +        begin
2091 +          LocalInt := DateTimeToTimeStamp(ColData.AsDateTime).Date;
2092 +          LocalData := PByte(@LocalInt);
2093 +        end;
2094 +        SQL_TYPE_TIME:
2095 +        begin
2096 +          LocalInt := DateTimeToTimeStamp(ColData.AsDateTime).Time;
2097 +          LocalData := PByte(@LocalInt);
2098 +        end;
2099 +        SQL_SHORT, SQL_LONG:
2100 +        begin
2101 +          if (fdDataScale = 0) then
2102 +          begin
2103 +            LocalInt := ColData.AsLong;
2104 +            LocalData := PByte(@LocalInt);
2105 +          end
2106 +          else
2107 +          if (fdDataScale >= (-4)) then
2108 +          begin
2109 +            LocalCurrency := ColData.AsCurrency;
2110 +            LocalData := PByte(@LocalCurrency);
2111 +          end
2112 +          else
2113 +          begin
2114 +           LocalDouble := ColData.AsDouble;
2115 +           LocalData := PByte(@LocalDouble);
2116 +          end;
2117 +        end;
2118 +        SQL_INT64:
2119 +        begin
2120 +          if (fdDataScale = 0) then
2121 +          begin
2122 +            LocalInt64 := ColData.AsInt64;
2123 +            LocalData := PByte(@LocalInt64);
2124 +          end
2125 +          else
2126 +          if (fdDataScale >= (-4)) then
2127 +          begin
2128 +            LocalCurrency := ColData.AsCurrency;
2129 +            LocalData := PByte(@LocalCurrency);
2130 +            end
2131 +            else
2132 +            begin
2133 +              LocalDouble := ColData.AsDouble;
2134 +              LocalData := PByte(@LocalDouble);
2135 +            end
2136 +        end;
2137 +        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2138 +        begin
2139 +          LocalDouble := ColData.AsDouble;
2140 +          LocalData := PByte(@LocalDouble);
2141 +        end;
2142 +        SQL_BOOLEAN:
2143 +        begin
2144 +          LocalBool := ColData.AsBoolean;
2145 +          LocalData := PByte(@LocalBool);
2146 +        end;
2147 +      end;
2148 +
2149 +      if fdDataType = SQL_VARYING then
2150 +        Move(LocalData^, Buffer[fdDataOfs], fdDataLength)
2151 +      else
2152 +        Move(LocalData^, Buffer[fdDataOfs], fdDataSize)
2153 +    end
2154 +    else {Null column}
2155 +    if fdDataType = SQL_VARYING then
2156 +      FillChar(Buffer[fdDataOfs],fdDataLength,0)
2157 +    else
2158 +      FillChar(Buffer[fdDataOfs],fdDataSize,0);
2159 +  end;
2160 + end;
2161 +
2162   { Read the record from FQSelect.Current into the record buffer
2163    Then write the buffer to in memory cache }
2164   procedure TIBCustomDataSet.FetchCurrentRecordToBuffer(Qry: TIBSQL;
# Line 1968 | Line 2167 | var
2167    pbd: PBlobDataArray;
2168    pda: PArrayDataArray;
2169    i, j: Integer;
1971  LocalData: PByte;
1972  LocalDate, LocalDouble: Double;
1973  LocalInt: Integer;
1974  LocalBool: wordBool;
1975  LocalInt64: Int64;
1976  LocalCurrency: Currency;
2170    FieldsLoaded: Integer;
2171    p: PRecordData;
2172   begin
# Line 2024 | Line 2217 | begin
2217          continue;
2218        end;
2219      if j > 0 then
2220 <    begin
2028 <      LocalData := nil;
2029 <      with p^.rdFields[j], FFieldColumns^[j] do
2030 <      begin
2031 <        Qry.Current.GetData(i,fdIsNull,fdDataLength,LocalData);
2032 <        if not fdIsNull then
2033 <        begin
2034 <          case fdDataType of  {Get Formatted data for column types that need formatting}
2035 <            SQL_TIMESTAMP:
2036 <            begin
2037 <              LocalDate := TimeStampToMSecs(DateTimeToTimeStamp(Qry[i].AsDateTime));
2038 <              LocalData := PByte(@LocalDate);
2039 <            end;
2040 <            SQL_TYPE_DATE:
2041 <            begin
2042 <              LocalInt := DateTimeToTimeStamp(Qry[i].AsDateTime).Date;
2043 <              LocalData := PByte(@LocalInt);
2044 <            end;
2045 <            SQL_TYPE_TIME:
2046 <            begin
2047 <              LocalInt := DateTimeToTimeStamp(Qry[i].AsDateTime).Time;
2048 <              LocalData := PByte(@LocalInt);
2049 <            end;
2050 <            SQL_SHORT, SQL_LONG:
2051 <            begin
2052 <              if (fdDataScale = 0) then
2053 <              begin
2054 <                LocalInt := Qry[i].AsLong;
2055 <                LocalData := PByte(@LocalInt);
2056 <              end
2057 <              else
2058 <              if (fdDataScale >= (-4)) then
2059 <              begin
2060 <                LocalCurrency := Qry[i].AsCurrency;
2061 <                LocalData := PByte(@LocalCurrency);
2062 <              end
2063 <              else
2064 <              begin
2065 <               LocalDouble := Qry[i].AsDouble;
2066 <               LocalData := PByte(@LocalDouble);
2067 <              end;
2068 <            end;
2069 <            SQL_INT64:
2070 <            begin
2071 <              if (fdDataScale = 0) then
2072 <              begin
2073 <                LocalInt64 := Qry[i].AsInt64;
2074 <                LocalData := PByte(@LocalInt64);
2075 <              end
2076 <              else
2077 <              if (fdDataScale >= (-4)) then
2078 <              begin
2079 <                LocalCurrency := Qry[i].AsCurrency;
2080 <                LocalData := PByte(@LocalCurrency);
2081 <                end
2082 <                else
2083 <                begin
2084 <                  LocalDouble := Qry[i].AsDouble;
2085 <                  LocalData := PByte(@LocalDouble);
2086 <                end
2087 <            end;
2088 <            SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2089 <            begin
2090 <              LocalDouble := Qry[i].AsDouble;
2091 <              LocalData := PByte(@LocalDouble);
2092 <            end;
2093 <            SQL_BOOLEAN:
2094 <            begin
2095 <              LocalBool := Qry[i].AsBoolean;
2096 <              LocalData := PByte(@LocalBool);
2097 <            end;
2098 <          end;
2099 <
2100 <          if fdDataType = SQL_VARYING then
2101 <            Move(LocalData^, Buffer[fdDataOfs], fdDataLength)
2102 <          else
2103 <            Move(LocalData^, Buffer[fdDataOfs], fdDataSize)
2104 <        end
2105 <        else {Null column}
2106 <        if fdDataType = SQL_VARYING then
2107 <          FillChar(Buffer[fdDataOfs],fdDataLength,0)
2108 <        else
2109 <          FillChar(Buffer[fdDataOfs],fdDataSize,0);
2110 <      end;
2111 <    end;
2220 >      ColumnDataToBuffer(Qry.Current,i,j,Buffer);
2221    end;
2222    WriteRecordCache(RecordNumber, Buffer);
2223   end;
# Line 2355 | Line 2464 | begin
2464      SetInternalSQLParams(Qry.Params, Buff);
2465      Qry.ExecQuery;
2466    end;
2467 +  if Qry.FieldCount > 0 then {Has RETURNING Clause}
2468 +    UpdateRecordFromQuery(Qry.Current,Buff);
2469    PRecordData(Buff)^.rdUpdateStatus := usUnmodified;
2470    PRecordData(Buff)^.rdCachedUpdateStatus := cusUnmodified;
2471    SetModified(False);
# Line 2605 | Line 2716 | begin
2716      FQRefresh.Database := Value;
2717      FQSelect.Database := Value;
2718      FQModify.Database := Value;
2719 +    FDatabaseInfo.Database := Value;
2720    end;
2721   end;
2722  
# Line 3674 | 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 3714 | 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 3767 | 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 3800 | 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 3939 | 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 4841 | Line 4991 | begin
4991    InternalSetParams(Query.Params,buff);
4992   end;
4993  
4994 + procedure TIBDataSetUpdateObject.UpdateRecordFromQuery(QryResults: IResults;
4995 +  Buffer: PChar);
4996 + begin
4997 +  if not Assigned(DataSet) then Exit;
4998 +  DataSet.UpdateRecordFromQuery(QryResults, Buffer);
4999 + end;
5000 +
5001   function TIBDSBlobStream.GetSize: Int64;
5002   begin
5003    Result := FBlobStream.BlobSize;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines