* Patch from Laco to implement filtering on null values

This commit is contained in:
Michaël Van Canneyt 2022-02-15 23:32:17 +01:00
parent 0f5bd26cba
commit f1043ef099
3 changed files with 111 additions and 26 deletions

View File

@ -65,10 +65,12 @@ type
private private
FField: TField; FField: TField;
FFieldName: string; FFieldName: string;
FFieldIsNull: boolean;
FExprWord: TExprWord; FExprWord: TExprWord;
protected protected
function GetFieldVal: Pointer; virtual; abstract; function GetFieldVal: Pointer; virtual; abstract;
function GetFieldType: TExpressionType; virtual; abstract; function GetFieldType: TExpressionType; virtual; abstract;
function GetFieldIsNull: PBoolean;
public public
constructor Create(UseField: TField); constructor Create(UseField: TField);
@ -78,6 +80,7 @@ type
property FieldDef: TField read FField; property FieldDef: TField read FField;
property FieldType: TExpressionType read GetFieldType; property FieldType: TExpressionType read GetFieldType;
property FieldName: string read FFieldName; property FieldName: string read FFieldName;
property FieldIsNull: PBoolean read GetFieldIsNull;
end; end;
TStringFieldVar = class(TFieldVar) TStringFieldVar = class(TFieldVar)
@ -148,7 +151,6 @@ type
procedure Refresh(Buffer: TRecordBuffer); override; procedure Refresh(Buffer: TRecordBuffer); override;
end; end;
//--TFieldVar---------------------------------------------------------------- //--TFieldVar----------------------------------------------------------------
constructor TFieldVar.Create(UseField: TField); constructor TFieldVar.Create(UseField: TField);
begin begin
@ -160,6 +162,11 @@ begin
FFieldName := UseField.FieldName; FFieldName := UseField.FieldName;
end; end;
function TFieldVar.GetFieldIsNull: PBoolean;
begin
Result := @FFieldIsNull;
end;
//--TStringFieldVar------------------------------------------------------------- //--TStringFieldVar-------------------------------------------------------------
function TStringFieldVar.GetFieldVal: Pointer; function TStringFieldVar.GetFieldVal: Pointer;
begin begin
@ -188,7 +195,8 @@ end;
procedure TStringFieldVar.Refresh(Buffer: TRecordBuffer); procedure TStringFieldVar.Refresh(Buffer: TRecordBuffer);
var Fieldbuf : TStringFieldBuffer; var Fieldbuf : TStringFieldBuffer;
begin begin
if not FField.DataSet.GetFieldData(FField,@Fieldbuf) then FFieldIsNull := not FField.DataSet.GetFieldData(FField,@Fieldbuf);
if FFieldIsNull then
FFieldVal^:=#0 FFieldVal^:=#0
else else
strcopy(FFieldVal,@Fieldbuf[0]); strcopy(FFieldVal,@Fieldbuf[0]);
@ -207,7 +215,8 @@ end;
procedure TFloatFieldVar.Refresh(Buffer: TRecordBuffer); procedure TFloatFieldVar.Refresh(Buffer: TRecordBuffer);
begin begin
if not FField.DataSet.GetFieldData(FField,@FFieldVal) then FFieldIsNull := not FField.DataSet.GetFieldData(FField,@FFieldVal);
if FFieldIsNull then
FFieldVal := 0; FFieldVal := 0;
end; end;
@ -224,7 +233,8 @@ end;
procedure TIntegerFieldVar.Refresh(Buffer: TRecordBuffer); procedure TIntegerFieldVar.Refresh(Buffer: TRecordBuffer);
begin begin
if not FField.DataSet.GetFieldData(FField,@FFieldVal) then FFieldIsNull := not FField.DataSet.GetFieldData(FField,@FFieldVal);
if FFieldIsNull then
FFieldVal := 0; FFieldVal := 0;
end; end;
@ -241,7 +251,8 @@ end;
procedure TLargeIntFieldVar.Refresh(Buffer: TRecordBuffer); procedure TLargeIntFieldVar.Refresh(Buffer: TRecordBuffer);
begin begin
if not FField.DataSet.GetFieldData(FField,@FFieldVal) then FFieldIsNull := not FField.DataSet.GetFieldData(FField,@FFieldVal);
if FFieldIsNull then
FFieldVal := 0; FFieldVal := 0;
end; end;
@ -258,7 +269,8 @@ end;
procedure TDateTimeFieldVar.Refresh(Buffer:TRecordBuffer ); procedure TDateTimeFieldVar.Refresh(Buffer:TRecordBuffer );
begin begin
if not FField.DataSet.GetFieldData(FField,@FFieldVal) then FFieldIsNull := not FField.DataSet.GetFieldData(FField,@FFieldVal);
if FFieldIsNull then
FFieldVal := 0; FFieldVal := 0;
end; end;
@ -275,17 +287,19 @@ end;
procedure TBooleanFieldVar.Refresh(Buffer: TRecordBuffer); procedure TBooleanFieldVar.Refresh(Buffer: TRecordBuffer);
begin begin
if not FField.DataSet.GetFieldData(FField,@FFieldVal) then FFieldIsNull := not FField.DataSet.GetFieldData(FField,@FFieldVal);
if FFieldIsNull then
FFieldVal := False; FFieldVal := False;
end; end;
procedure TBCDFieldVar.Refresh(Buffer: TRecordBuffer); procedure TBCDFieldVar.Refresh(Buffer: TRecordBuffer);
var c: currency; var c: currency;
begin begin
if FField.DataSet.GetFieldData(FField,@c) then FFieldIsNull := not FField.DataSet.GetFieldData(FField,@c);
FFieldVal := c if FFieldIsNull then
FFieldVal := 0
else else
FFieldVal := 0; FFieldVal := c;
end; end;
@ -390,7 +404,7 @@ begin
ftString, ftFixedChar: ftString, ftFixedChar:
begin begin
TempFieldVar := TStringFieldVar.Create(FieldInfo); TempFieldVar := TStringFieldVar.Create(FieldInfo);
TempFieldVar.FExprWord := DefineStringVariable(VarName, TempFieldVar.FieldVal); TempFieldVar.FExprWord := DefineStringVariable(VarName, TempFieldVar.FieldVal, TempFieldVar.FieldIsNull);
TempFieldVar.FExprWord.fixedlen := Fieldinfo.Size; TempFieldVar.FExprWord.fixedlen := Fieldinfo.Size;
end; end;
ftBoolean: ftBoolean:
@ -406,7 +420,7 @@ begin
ftAutoInc, ftInteger, ftSmallInt, ftWord: ftAutoInc, ftInteger, ftSmallInt, ftWord:
begin begin
TempFieldVar := TIntegerFieldVar.Create(FieldInfo); TempFieldVar := TIntegerFieldVar.Create(FieldInfo);
TempFieldVar.FExprWord := DefineIntegerVariable(VarName, TempFieldVar.FieldVal); TempFieldVar.FExprWord := DefineIntegerVariable(VarName, TempFieldVar.FieldVal, TempFieldVar.FieldIsNull);
end; end;
ftLargeInt: ftLargeInt:
begin begin

View File

@ -85,14 +85,14 @@ type
destructor Destroy; override; destructor Destroy; override;
function DefineFloatVariable(AVarName: string; AValue: PDouble): TExprWord; function DefineFloatVariable(AVarName: string; AValue: PDouble): TExprWord;
function DefineIntegerVariable(AVarName: string; AValue: PInteger): TExprWord; function DefineIntegerVariable(AVarName: string; AValue: PInteger; AIsNull: PBoolean = nil): TExprWord;
// procedure DefineSmallIntVariable(AVarName: string; AValue: PSmallInt); // procedure DefineSmallIntVariable(AVarName: string; AValue: PSmallInt);
{$ifdef SUPPORT_INT64} {$ifdef SUPPORT_INT64}
function DefineLargeIntVariable(AVarName: string; AValue: PLargeInt): TExprWord; function DefineLargeIntVariable(AVarName: string; AValue: PLargeInt): TExprWord;
{$endif} {$endif}
function DefineDateTimeVariable(AVarName: string; AValue: PDateTimeRec): TExprWord; function DefineDateTimeVariable(AVarName: string; AValue: PDateTimeRec): TExprWord;
function DefineBooleanVariable(AVarName: string; AValue: PBoolean): TExprWord; function DefineBooleanVariable(AVarName: string; AValue: PBoolean): TExprWord;
function DefineStringVariable(AVarName: string; AValue: PPChar): TExprWord; function DefineStringVariable(AVarName: string; AValue: PPChar; AIsNull: PBoolean = nil): TExprWord;
function DefineFunction(AFunctName, AShortName, ADescription, ATypeSpec: string; function DefineFunction(AFunctName, AShortName, ADescription, ATypeSpec: string;
AMinFunctionArg: Integer; AResultType: TExpressionType; AFuncAddress: TExprFunc): TExprWord; AMinFunctionArg: Integer; AResultType: TExpressionType; AFuncAddress: TExprFunc): TExprWord;
procedure Evaluate(AnExpression: string); procedure Evaluate(AnExpression: string);
@ -265,6 +265,8 @@ begin
Args[0] := ExprWord.AsPointer; Args[0] := ExprWord.AsPointer;
// store length as second parameter // store length as second parameter
Args[1] := PChar(ExprWord.LenAsPointer); Args[1] := PChar(ExprWord.LenAsPointer);
// and NULL indicator as third parameter
Args[2] := PChar(ExprWord.IsNullAsPointer);
end; end;
end; end;
end; end;
@ -501,6 +503,7 @@ begin
etLargeInt:ExprWord := TLargeIntConstant.Create(PInt64(FExpResult)^); etLargeInt:ExprWord := TLargeIntConstant.Create(PInt64(FExpResult)^);
{$endif} {$endif}
etString: ExprWord := TStringConstant.Create(FExpResult); etString: ExprWord := TStringConstant.Create(FExpResult);
etUnknown: ExprWord := TNullConstant.Create;
else raise EParserException.CreateFmt('No support for resulttype %d. Please fix the TDBF code.',[ResultType]); else raise EParserException.CreateFmt('No support for resulttype %d. Please fix the TDBF code.',[ResultType]);
end; end;
@ -778,6 +781,8 @@ begin
end; end;
procedure TCustomExpressionParser.ParseString(AnExpression: string; DestCollection: TExprCollection); procedure TCustomExpressionParser.ParseString(AnExpression: string; DestCollection: TExprCollection);
const
NullWord='NULL';
var var
isConstant: Boolean; isConstant: Boolean;
I, I1, I2, Len, DecSep: Integer; I, I1, I2, Len, DecSep: Integer;
@ -971,6 +976,13 @@ begin
DestCollection.Add(TempWord); DestCollection.Add(TempWord);
FConstantsList.Add(TempWord); FConstantsList.Add(TempWord);
end end
else if UpCase(W) = NullWord then
begin
// NULL
TempWord := TNullConstant.Create;
DestCollection.Add(TempWord);
FConstantsList.Add(TempWord);
end
else if Length(W) > 0 then else if Length(W) > 0 then
if FWordsList.Search(PChar(W), I) then if FWordsList.Search(PChar(W), I) then
begin begin
@ -1132,9 +1144,9 @@ begin
FWordsList.Add(Result); FWordsList.Add(Result);
end; end;
function TCustomExpressionParser.DefineIntegerVariable(AVarName: string; AValue: PInteger): TExprWord; function TCustomExpressionParser.DefineIntegerVariable(AVarName: string; AValue: PInteger; AIsNull: PBoolean): TExprWord;
begin begin
Result := TIntegerVariable.Create(AVarName, AValue); Result := TIntegerVariable.Create(AVarName, AValue, AIsNull);
FWordsList.Add(Result); FWordsList.Add(Result);
end; end;
@ -1166,9 +1178,9 @@ begin
FWordsList.Add(Result); FWordsList.Add(Result);
end; end;
function TCustomExpressionParser.DefineStringVariable(AVarName: string; AValue: PPChar): TExprWord; function TCustomExpressionParser.DefineStringVariable(AVarName: string; AValue: PPChar; AIsNull: PBoolean): TExprWord;
begin begin
Result := TStringVariable.Create(AVarName, AValue); Result := TStringVariable.Create(AVarName, AValue, AIsNull);
FWordsList.Add(Result); FWordsList.Add(Result);
end; end;
@ -1864,6 +1876,12 @@ begin
Res.MemoryPos^^ := Char(AnsiStrComp(Args[0], Args[1]) >= 0); Res.MemoryPos^^ := Char(AnsiStrComp(Args[0], Args[1]) >= 0);
end; end;
procedure Func_SU_EQ(Param: PExpressionRec);
begin
with Param^ do
Res.MemoryPos^^ := Char(PBoolean(Args[0]+StrLen(Args[0])+1)^);
end;
procedure Func_FF_EQ(Param: PExpressionRec); procedure Func_FF_EQ(Param: PExpressionRec);
begin begin
with Param^ do with Param^ do
@ -1941,6 +1959,11 @@ begin
with Param^ do with Param^ do
Res.MemoryPos^^ := Char(PInteger(Args[0])^ = PInteger(Args[1])^); Res.MemoryPos^^ := Char(PInteger(Args[0])^ = PInteger(Args[1])^);
end; end;
procedure Func_IU_EQ(Param: PExpressionRec);
begin
with Param^ do
Res.MemoryPos^^ := Char(PBoolean(Args[0]+8)^);
end;
procedure Func_II_NEQ(Param: PExpressionRec); procedure Func_II_NEQ(Param: PExpressionRec);
begin begin
@ -2294,6 +2317,7 @@ initialization
Add(TFunction.CreateOper('>=','FI', etBoolean, Func_FI_GTE, 80)); Add(TFunction.CreateOper('>=','FI', etBoolean, Func_FI_GTE, 80));
Add(TFunction.CreateOper('<>','FI', etBoolean, Func_FI_NEQ, 80)); Add(TFunction.CreateOper('<>','FI', etBoolean, Func_FI_NEQ, 80));
Add(TFunction.CreateOper('=', 'II', etBoolean, Func_II_EQ , 80)); Add(TFunction.CreateOper('=', 'II', etBoolean, Func_II_EQ , 80));
Add(TFunction.CreateOper('=', 'IU', etBoolean, Func_IU_EQ , 80));
Add(TFunction.CreateOper('<', 'II', etBoolean, Func_II_LT , 80)); Add(TFunction.CreateOper('<', 'II', etBoolean, Func_II_LT , 80));
Add(TFunction.CreateOper('>', 'II', etBoolean, Func_II_GT , 80)); Add(TFunction.CreateOper('>', 'II', etBoolean, Func_II_GT , 80));
Add(TFunction.CreateOper('<=','II', etBoolean, Func_II_LTE, 80)); Add(TFunction.CreateOper('<=','II', etBoolean, Func_II_LTE, 80));
@ -2337,6 +2361,7 @@ initialization
Add(TFunction.CreateOper('>=','IL', etBoolean, Func_IL_GTE, 80)); Add(TFunction.CreateOper('>=','IL', etBoolean, Func_IL_GTE, 80));
Add(TFunction.CreateOper('<>','IL', etBoolean, Func_IL_NEQ, 80)); Add(TFunction.CreateOper('<>','IL', etBoolean, Func_IL_NEQ, 80));
{$endif} {$endif}
Add(TFunction.CreateOper('=', 'SU', etBoolean, Func_SU_EQ , 80));
Add(TFunction.CreateOper('NOT', 'B', etBoolean, Func_NOT, 85)); Add(TFunction.CreateOper('NOT', 'B', etBoolean, Func_NOT, 85));
Add(TFunction.CreateOper('AND', 'BB', etBoolean, Func_AND, 90)); Add(TFunction.CreateOper('AND', 'BB', etBoolean, Func_AND, 90));

View File

@ -90,6 +90,8 @@ type
ExprFunc: TExprFunc; ExprFunc: TExprFunc;
end; end;
{ TExprWord }
TExprWord = class(TObject) TExprWord = class(TObject)
private private
FName: string; FName: string;
@ -112,8 +114,9 @@ type
public public
constructor Create(AName: string; AExprFunc: TExprFunc); constructor Create(AName: string; AExprFunc: TExprFunc);
function LenAsPointer: PInteger; virtual;
function AsPointer: PChar; virtual; function AsPointer: PChar; virtual;
function LenAsPointer: PInteger; virtual;
function IsNullAsPointer: PBoolean; virtual;
function IsFunction: Boolean; virtual; function IsFunction: Boolean; virtual;
property ExprFunc: TExprFunc read FExprFunc; property ExprFunc: TExprFunc read FExprFunc;
@ -225,14 +228,26 @@ type
property Value: Boolean read FValue write FValue; property Value: Boolean read FValue write FValue;
end; end;
{ TNullConstant }
TNullConstant = class(TConstant)
private
public
constructor Create;
end;
{ TVariable }
TVariable = class(TExprWord) TVariable = class(TExprWord)
private private
FResultType: TExpressionType; FResultType: TExpressionType;
FIsNull: PBoolean;
protected protected
function GetCanVary: Boolean; override; function GetCanVary: Boolean; override;
function GetResultType: TExpressionType; override; function GetResultType: TExpressionType; override;
public public
constructor Create(AName: string; AVarType: TExpressionType; AExprFunc: TExprFunc); constructor Create(AName: string; AVarType: TExpressionType; AExprFunc: TExprFunc);
function IsNullAsPointer: PBoolean; override;
end; end;
TFloatVariable = class(TVariable) TFloatVariable = class(TVariable)
@ -252,7 +267,7 @@ type
function GetFixedLen: Integer; override; function GetFixedLen: Integer; override;
procedure SetFixedLen(NewLen: integer); override; procedure SetFixedLen(NewLen: integer); override;
public public
constructor Create(AName: string; AValue: PPChar); constructor Create(AName: string; AValue: PPChar; AIsNull: PBoolean);
function LenAsPointer: PInteger; override; function LenAsPointer: PInteger; override;
function AsPointer: PChar; override; function AsPointer: PChar; override;
@ -273,7 +288,7 @@ type
private private
FValue: PInteger; FValue: PInteger;
public public
constructor Create(AName: string; AValue: PInteger); constructor Create(AName: string; AValue: PInteger; AIsNull: PBoolean);
function AsPointer: PChar; override; function AsPointer: PChar; override;
end; end;
@ -372,6 +387,7 @@ begin
'F': Result := etFloat; 'F': Result := etFloat;
'D': Result := etDateTime; 'D': Result := etDateTime;
'S': Result := etString; 'S': Result := etString;
'U': Result := etUnknown;
else else
Result := etUnknown; Result := etUnknown;
end; end;
@ -405,6 +421,10 @@ begin
if length = -1 then if length = -1 then
length := StrLen(PPChar(Args[0])^); length := StrLen(PPChar(Args[0])^);
Res.Append(PPChar(Args[0])^, length); Res.Append(PPChar(Args[0])^, length);
// NULL indicator (placed after NULL terminated string)
length := StrLen(PPChar(Args[0])^)+1;
Res.AssureSpace(length+1);
PBoolean(Res.Memory^+length)^ := Assigned(Args[2]) and PBoolean(Args[2])^;
end; end;
end; end;
@ -416,8 +436,10 @@ end;
procedure _IntegerVariable(Param: PExpressionRec); procedure _IntegerVariable(Param: PExpressionRec);
begin begin
with Param^ do with Param^ do begin
PInteger(Res.MemoryPos^)^ := PInteger(Args[0])^; PInteger(Res.MemoryPos^)^ := PInteger(Args[0])^;
PBoolean(Res.MemoryPos^+8)^ := Assigned(Args[2]) and PBoolean(Args[2])^; // NULL indicator
end;
end; end;
{ {
@ -429,15 +451,18 @@ end;
} }
{$ifdef SUPPORT_INT64} {$ifdef SUPPORT_INT64}
procedure _LargeIntVariable(Param: PExpressionRec); procedure _LargeIntVariable(Param: PExpressionRec);
begin begin
with Param^ do with Param^ do
PLargeInt(Res.MemoryPos^)^ := PLargeInt(Args[0])^; PLargeInt(Res.MemoryPos^)^ := PLargeInt(Args[0])^;
end; end;
{$endif} {$endif}
procedure _NullConstant(Param: PExpressionRec);
begin
// NOP
end;
{ TExpressionWord } { TExpressionWord }
constructor TExprWord.Create(AName: string; AExprFunc: TExprFunc); constructor TExprWord.Create(AName: string; AExprFunc: TExprFunc);
@ -536,6 +561,11 @@ begin
Result := nil; Result := nil;
end; end;
function TExprWord.IsNullAsPointer: PBoolean;
begin
Result := nil;
end;
function TExprWord.IsFunction: Boolean; function TExprWord.IsFunction: Boolean;
begin begin
Result := False; Result := False;
@ -665,6 +695,13 @@ begin
end; end;
{$endif} {$endif}
{ TNullConstant }
constructor TNullConstant.Create;
begin
inherited Create('NULL', etUnknown, _NullConstant);
end;
{ TVariable } { TVariable }
constructor TVariable.Create(AName: string; AVarType: TExpressionType; AExprFunc: TExprFunc); constructor TVariable.Create(AName: string; AVarType: TExpressionType; AExprFunc: TExprFunc);
@ -684,6 +721,11 @@ begin
Result := FResultType; Result := FResultType;
end; end;
function TVariable.IsNullAsPointer: PBoolean;
begin
Result := FIsNull;
end;
{ TFloatVariable } { TFloatVariable }
constructor TFloatVariable.Create(AName: string; AValue: PDouble); constructor TFloatVariable.Create(AName: string; AValue: PDouble);
@ -699,7 +741,7 @@ end;
{ TStringVariable } { TStringVariable }
constructor TStringVariable.Create(AName: string; AValue: PPChar); constructor TStringVariable.Create(AName: string; AValue: PPChar; AIsNull: PBoolean);
begin begin
// variable or fixed length? // variable or fixed length?
inherited Create(AName, etString, _StringVariable); inherited Create(AName, etString, _StringVariable);
@ -707,6 +749,7 @@ begin
// store pointer to string // store pointer to string
FValue := AValue; FValue := AValue;
FFixedLen := -1; FFixedLen := -1;
FIsNull := AIsNull;
end; end;
function TStringVariable.AsPointer: PChar; function TStringVariable.AsPointer: PChar;
@ -744,10 +787,11 @@ end;
{ TIntegerVariable } { TIntegerVariable }
constructor TIntegerVariable.Create(AName: string; AValue: PInteger); constructor TIntegerVariable.Create(AName: string; AValue: PInteger; AIsNull: PBoolean);
begin begin
inherited Create(AName, etInteger, _IntegerVariable); inherited Create(AName, etInteger, _IntegerVariable);
FValue := AValue; FValue := AValue;
FIsNull := AIsNull;
end; end;
function TIntegerVariable.AsPointer: PChar; function TIntegerVariable.AsPointer: PChar;
@ -1070,6 +1114,7 @@ begin
Inc(FMemoryPos^, Length); Inc(FMemoryPos^, Length);
// null-terminate // null-terminate
FMemoryPos^^ := #0; FMemoryPos^^ := #0;
Inc(FMemoryPos^);
end; end;
procedure TDynamicType.AppendInteger(Source: Integer); procedure TDynamicType.AppendInteger(Source: Integer);
@ -1078,6 +1123,7 @@ begin
AssureSpace(12); AssureSpace(12);
Inc(FMemoryPos^, GetStrFromInt(Source, FMemoryPos^)); Inc(FMemoryPos^, GetStrFromInt(Source, FMemoryPos^));
FMemoryPos^^ := #0; FMemoryPos^^ := #0;
Inc(FMemoryPos^);
end; end;
end. end.