Is it possible, in any way, to pass a Generic Record in an array of const argument to a function call ?
I would like to use the Nullable record from Allen Bauer in a kind of home made ORM using "Plain Old Delphi Objects" to map database rows :
type
Nullable = record
...
end;
TMyTableObject = class(TDbOject)
private
FId: Integer;
FOptionalField: Nullable;
protected
procedure InternalSave; override;
public
property Id: Integer read FId write SetId;
property OptionalField: Nullable read FOptionalField write SetOptionalField;
end;
...
implementation
procedure TMyTableObject.InternalSave;
begin
{ FDbController is declared and managed in TDbObject, it contains fonction to run queries
on the database }
FDbController.Execute(
'update or insert into MY_TABLE(TABLE_ID, OPTIONAL_FIELD) ' +
'values (?, ?) ' +
'matching(TABLE_ID) returning TABLE_ID', [FId, FOptionalField],
procedure (Fields: TSQLResult)
begin
FId := Fields.AsInteger[0];
end;
end;
end;
The above code results in an error : "E2250 : no overriden version of the "Execute" function can be called with these arguments" (translated from French : E2250 Aucune version surchargée de 'Execute' ne peut être appelée avec ces arguments)
I could explicitly convert FOptionalField to string or anything else since Nullable overrides ad-hoc operators but I really have to know if the Nullable has a value or not until I map the array of const into the Params field of the database query object :
procedure TDbContext.MapParams(Q: TUIBQuery; Params: TConstArray);
const
BOOL_STR: array[Boolean] of string = ('F', 'V');
var
i: Integer;
begin
for i := 0 to High(Params) do
case Params[i].VType of
vtInteger : Q.Params.AsInteger[i] := Params[i].VInteger;
vtInt64 : Q.Params.AsInt64[i] := Params[i].VInt64^;
vtBoolean : Q.Params.AsString[i] := BOOL_STR[Params[i].VBoolean];
vtChar : Q.Params.AsAnsiString[i] := Params[i].VChar;
vtWideChar: Q.Params.AsString[i] := Params[i].VWideChar;
vtExtended: Q.Params.AsDouble[i] := Params[i].VExtended^;
vtCurrency: Q.Params.AsCurrency[i] := Params[i].VCurrency^;
vtString : Q.Params.AsString[i] := string(Params[i].VString^);
vtPChar : Q.Params.AsAnsiString[i] := Params[i].VPChar^;
vtAnsiString: Q.Params.AsAnsiString[i] := AnsiString(Params[i].VAnsiString);
vtWideString: Q.Params.AsUnicodeString[i] := PWideChar(Params[i].VWideString);
vtVariant : Q.Params.AsVariant[i] := Params[i].VVariant^;
vtUnicodeString: Q.Params.AsUnicodeString[i] := string(Params[i].VUnicodeString);
vtPointer :
begin
if Params[i].VPointer = nil then
Q.Params.IsNull[i] := True
else
Assert(False, 'not nil pointer is not supported');
end
else
Assert(False, 'not supported');
end;
end;
Do you have any idea on how to make this kind of construct possible ? I find a way by adding an explicit cast operator override to Variant in Nullable using RTTI but that's a bit tricky because it needs an explicit cast to Variant in the array of const call :
class operator Nullable.Explicit(Value: Nullable): Variant;
begin
if Value.HasValue then
Result := TValue.From(Value.Value).AsVariant
else
Result := Null;
end;
...
FDbController.Execute(
'update or insert into MY_TABLE(TABLE_ID, OPTIONAL_FIELD) ' +
'values (?, ?) ' +
'matching(TABLE_ID) returning TABLE_ID', [FId, Variant(FOptionalField)],
procedure (Fields: TSQLResult)
begin
FId := Fields.AsInteger[0];
end;
end;