Files
lazarus-ccr/components/flashfiler/sourcelaz/lazcommon.pas

1692 lines
50 KiB
ObjectPascal
Raw Normal View History

{ This unit is taked from fssql.
It was named fscommon.pas;
**************************************************************
It looks like he (the author of fssql) take code from delphi!
Most of code looks like in DbCommon.pas from delphi!
**************************************************************
// called only from ffdb.pas
}
Unit lazcommon;
{$T-,H+,X+,R-}
{$I ffdefine.inc}
Interface
Uses Classes,
{$IFDEF DCC6OrLater}
Variants,
{$ENDIF}
Db,
windows;
Type
WCHAR = WideChar;
{$EXTERNALSYM WCHAR}
PWChar = PWideChar;
LPSTR = PAnsiChar;
{$EXTERNALSYM LPSTR}
PLPSTR = ^LPSTR;
{$EXTERNALSYM PLPSTR}
LPCSTR = PAnsiChar;
{$EXTERNALSYM LPCSTR}
LPCTSTR = PAnsiChar; { should be PWideChar if UNICODE }
{$EXTERNALSYM LPCTSTR}
LPTSTR = PAnsiChar; { should be PWideChar if UNICODE }
{$EXTERNALSYM LPTSTR}
LPWSTR = PWideChar;
{$EXTERNALSYM LPWSTR}
PLPWSTR = ^LPWSTR;
{$EXTERNALSYM PLPWSTR}
LPCWSTR = PWideChar;
{$EXTERNALSYM LPCWSTR}
DWORD = Longword;
{$EXTERNALSYM DWORD}
BOOL = LongBool;
{$EXTERNALSYM BOOL}
PBOOL = ^BOOL;
{$EXTERNALSYM PBOOL}
PByte = ^Byte;
PINT = ^Integer;
{$EXTERNALSYM PINT}
PSingle = ^Single;
PWORD = ^Word;
{$EXTERNALSYM PWORD}
PDWORD = ^DWORD;
{$EXTERNALSYM PDWORD}
LPDWORD = PDWORD;
{$EXTERNALSYM LPDWORD}
UCHAR = Byte;
{$EXTERNALSYM UCHAR}
PUCHAR = ^Byte;
{$EXTERNALSYM PUCHAR}
SHORT = Smallint;
{$EXTERNALSYM SHORT}
UINT = Longword;
{$EXTERNALSYM UINT}
PUINT = ^UINT;
{$EXTERNALSYM PUINT}
ULONG = Cardinal;
{$EXTERNALSYM ULONG}
PULONG = ^ULONG;
{$EXTERNALSYM PULONG}
PLongint = ^Longint;
PInteger = ^Integer;
PLongWord = ^Longword;
PSmallInt = ^Smallint;
PDouble = ^Double;
PShortInt = ^Shortint;
LCID = DWORD;
{$EXTERNALSYM LCID}
LANGID = Word;
{$EXTERNALSYM LANGID}
THandle = Longword;
PHandle = ^THandle;
TfsCANOperator = (
coNOTDEFINED, { }
coISBLANK, { coUnary; is operand blank. }
coNOTBLANK, { coUnary; is operand not blank. }
coEQ, { coBinary, coCompare; equal. }
coNE, { coBinary; NOT equal. }
coGT, { coBinary; greater than. }
coLT, { coBinary; less than. }
coGE, { coBinary; greater or equal. }
coLE, { coBinary; less or equal. }
coNOT, { coUnary; NOT }
coAND, { coBinary; AND }
coOR, { coBinary; OR }
coTUPLE2, { coUnary; Entire record is operand. }
coFIELD2, { coUnary; operand is field }
coCONST2, { coUnary; operand is constant }
coMINUS, { coUnary; minus. }
coADD, { coBinary; addition. }
coSUB, { coBinary; subtraction. }
coMUL, { coBinary; multiplication. }
coDIV, { coBinary; division. }
coMOD, { coBinary; modulo division. }
coREM, { coBinary; remainder of division. }
coSUM, { coBinary, accumulate sum of. }
coCOUNT, { coBinary, accumulate count of. }
coMIN, { coBinary, find minimum of. }
coMAX, { coBinary, find maximum of. }
coAVG, { coBinary, find average of. }
coCONT, { coBinary; provides a link between two }
coUDF2, { coBinary; invokes a User defined fn }
coCONTINUE2, { coUnary; Stops evaluating records }
coLIKE, { coCompare, extended binary compare }
coIN, { coBinary field in list of values }
coLIST2, { List of constant values of same type }
coUPPER, { coUnary: upper case }
coLOWER, { coUnary: lower case }
coFUNC2, { coFunc: Function }
coLISTELEM2, { coListElem: List Element }
coASSIGN { coBinary: Field assignment }
);
fsNODEClass = ({ Node Class }
nodeNULL, { Null node }
nodeUNARY, { Node is a unary }
nodeBINARY, { Node is a binary }
nodeCOMPARE, { Node is a compare }
nodeFIELD, { Node is a field }
nodeCONST, { Node is a constant }
nodeTUPLE, { Node is a record }
nodeCONTINUE, { Node is a continue node }
nodeUDF, { Node is a UDF node }
nodeLIST, { Node is a LIST node }
nodeFUNC, { Node is a Function node }
nodeLISTELEM { Node is a List Element node }
);
Const
CANEXPRSIZE = 10; { SizeOf(CANExpr) }
CANHDRSIZE = 8; { SizeOf(CANHdr) }
CANEXPRVERSION = 2;
Type
TfsExprData = Array Of Byte;
TFieldMap = Array[TFieldType] Of Byte;
{ TFilterExpr }
Type
TfsParserOption = (poExtSyntax, poAggregate, poDefaultExpr, poUseOrigNames,
poFieldNameGiven, poFieldDepend);
TfsParserOptions = Set Of TfsParserOption;
TfsExprNodeKind = (enField, enConst, enOperator, enFunc);
TfsExprScopeKind = (skField, skAgg, skConst);
PExprNode = ^TfsExprNode;
TfsExprNode = Record
FNext: PExprNode;
FKind: TfsExprNodeKind;
FPartial: Boolean;
FOperator: TfsCANOperator;
FData: Variant;
FLeft: PExprNode;
FRight: PExprNode;
FDataType: TFieldType;
FDataSize: Integer;
FArgs: TList;
FScopeKind: TfsExprScopeKind;
End ;
TFilterExpr = Class
Private
FDataSet: TDataSet;
FFieldMap: TFieldMap;
FOptions: TFilterOptions;
FParserOptions: TfsParserOptions;
FNodes: PExprNode;
FExprBuffer: TfsExprData;
FExprBufSize: Integer;
FExprNodeSize: Integer;
FExprDataSize: Integer;
FFieldName: String;
FDependentFields: TBits;
Function FieldFromNode(Node: PExprNode): TField;
Function GetExprData(Pos, Size: Integer): PChar;
Function PutConstBCD(Const Value: Variant; Decimals: Integer): Integer;
Function PutConstCurrency(Const Value: Variant): Integer;
Function PutConstBool(Const Value: Variant): Integer;
Function PutConstDate(Const Value: Variant): Integer;
Function PutConstDateTime(Const Value: Variant): Integer;
Function PutConstFloat(Const Value: Variant): Integer;
Function PutConstInt(DataType: TFieldType; Const Value: Variant): Integer;
Function PutConstNode(DataType: TFieldType; Data: PChar;
Size: Integer): Integer;
{$IFDEF DCC6OrLater}
{$HINTS OFF}
{$ENDIF}
Function PutConstNode64(DataType : TFieldType ;
Data : PChar ;
Size : Integer ) : Int64 ;
{$IFDEF DCC6OrLater}
{$HINTS OFF}
{$ENDIF}
Function PutConstStr(Const Value: String): Integer;
Function PutConstFsArrayStr(Const Value: String): Integer;
Function PutConstTime(Const Value: Variant): Integer;
Function PutData(Data: PChar; Size: Integer): Integer;
Function PutExprNode(Node: PExprNode; ParentOp: TfsCANOperator): Integer;
Function PutFieldNode(Field: TField; Node: PExprNode): Integer;
Function PutNode(NodeType: fsNODEClass; OpType: TfsCANOperator;
OpCount: Integer): Integer;
Procedure SetNodeOp(Node, Index, Data: Integer);
Function PutConstant(Node: PExprNode): Integer;
Function GetFieldByName(Name: String): TField;
Public
Constructor Create(DataSet: TDataSet; Options: TFilterOptions;
ParseOptions: TfsParserOptions; Const FieldName: String; DepFields: TBits;
FieldMap: TFieldMap);
Destructor Destroy; Override;
Function NewCompareNode(Field: TField; Operator: TfsCANOperator;
Const Value: Variant): PExprNode;
Function NewNode(Kind: TfsExprNodeKind; Operator: TfsCANOperator;
Const Data: Variant; Left, Right: PExprNode): PExprNode;
Function GetFilterData(Root: PExprNode): TfsExprData;
Property DataSet: TDataSet Write FDataSet;
End ;
{ TExprParser }
TfsExprToken = (etEnd, etSymbol, etName, etLiteral, etLParen, etRParen,
etEQ, etNE, etGE, etLE, etGT, etLT, etADD, etSUB, etMUL, etDIV,
etComma, etLIKE, etISNULL, etISNOTNULL, etIN);
TExprParser = Class
Private
FFilter: TFilterExpr;
FFieldMap: TFieldMap;
FText: String;
FSourcePtr: PChar;
FTokenPtr: PChar;
FTokenString: String;
FStrTrue: String;
FStrFalse: String;
FToken: TfsExprToken;
FPrevToken: TfsExprToken;
FFilterData: TfsExprData;
FNumericLit: Boolean;
FDataSize: Integer;
FParserOptions: TfsParserOptions;
FFieldName: String;
FDataSet: TDataSet;
FDependentFields: TBits;
Procedure NextToken;
Function NextTokenIsLParen: Boolean;
Function ParseExpr: PExprNode;
Function ParseExpr2: PExprNode;
Function ParseExpr3: PExprNode;
Function ParseExpr4: PExprNode;
Function ParseExpr5: PExprNode;
Function ParseExpr6: PExprNode;
Function ParseExpr7: PExprNode;
Function TokenName: String;
Function TokenSymbolIs(Const S: String): Boolean;
Function TokenSymbolIsFunc(Const S: String): Boolean;
Procedure GetFuncResultInfo(Node: PExprNode);
Procedure TypeCheckArithOp(Node: PExprNode);
Procedure GetScopeKind(Root, Left, Right: PExprNode);
Public
Constructor Create(DataSet: TDataSet; Const Text: String;
Options: TFilterOptions; ParserOptions: TfsParserOptions;
Const FieldName: String; DepFields: TBits; FieldMap: TFieldMap);
Destructor Destroy; Override;
Procedure SetExprParams(Const Text: String; Options: TFilterOptions;
ParserOptions: TfsParserOptions; Const FieldName: String);
Property FilterData: TfsExprData Read FFilterData;
Property DataSize: Integer Read FDataSize;
End ;
{ Field Origin parser }
Type
TfsFieldInfo = Record
DataBaseName: String;
TableName: String;
OriginalFieldName: String;
End ;
Function fsGetFieldInfo(Const Origin: String; Var FieldInfo: TfsFieldInfo): Boolean;
Implementation
Uses //soner dont needed: fsllbase,
SysUtils,
{$ifndef fpc} dbconsts, Consts{$else} dbconst, lazconsts{$endif} //soner
;
Function fsGetFieldInfo(Const Origin: String; Var FieldInfo: TfsFieldInfo): Boolean;
Var
Current: PChar;
Values: Array[0..4] Of String;
I: Integer;
Function GetPChar(Const S: String): PChar;
Begin
If S <> '' Then
Result := PChar(Pointer(S))
Else
Result := '';
End ;
Procedure Split(Const S: String);
Begin
Current := PChar(Pointer(S));
End ;
Function NextItem: String;
Var
C: PChar;
I: PChar;
Terminator: Char;
Ident: Array[0..1023] Of Char;
Begin
Result := '';
C := Current;
I := Ident;
While C^ In ['.', ' ', #0] Do
If C^ = #0 Then
Exit
Else
Inc(C);
Terminator := '.';
If C^ = '"' Then
Begin
Terminator := '"';
Inc(C);
End ;
While Not (C^ In [Terminator, #0]) Do
Begin
If C^ In LeadBytes Then
Begin
I^ := C^;
Inc(C);
Inc(I);
End
Else If C^ = '\' Then
Begin
Inc(C);
If C^ In LeadBytes Then
Begin
I^ := C^;
Inc(C);
Inc(I);
End ;
If C^ = #0 Then
Dec(C);
End ;
I^ := C^;
Inc(C);
Inc(I);
End ;
SetString(Result, Ident, I - Ident);
If (Terminator = '"') And (C^ <> #0) Then
Inc(C);
Current := C;
End ;
Function PopValue: PChar;
Begin
If I >= 0 Then
Begin
Result := GetPChar(Values[I]);
Dec(I);
End
Else
Result := '';
End ;
Begin
Result := False;
If (Origin = '') Then
Exit;
Split(Origin);
I := -1;
Repeat
Inc(I);
Values[I] := NextItem;
Until (Values[I] = '') Or (I = High(Values));
If I = High(Values) Then
Exit;
Dec(I);
FieldInfo.OriginalFieldName := StrPas(PopValue);
FieldInfo.TableName := StrPas(PopValue);
FieldInfo.DataBaseName := StrPas(PopValue);
Result := (FieldInfo.OriginalFieldName <> '') And (FieldInfo.TableName <> '');
End ;
Const
StringFieldTypes = [ftString, ftFixedChar, ftWideString, ftBytes];
BlobFieldTypes = [ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle,
ftTypedBinary];
Function IsNumeric(DataType: TFieldType): Boolean;
Begin
Result := DataType In [ftSmallint, ftInteger, ftWord, ftFloat, ftCurrency,
ftBCD, ftAutoInc, ftLargeInt];
End ;
Function IsTemporal(DataType: TFieldType): Boolean;
Begin
Result := DataType In [ftDate, ftTime, ftDateTime];
End ;
{ TFilterExpr }
Constructor TFilterExpr.Create(DataSet: TDataSet; Options: TFilterOptions;
ParseOptions: TfsParserOptions; Const FieldName: String; DepFields: TBits;
FieldMap: TFieldMap);
Begin
FFieldMap := FieldMap;
FDataSet := DataSet;
FOptions := Options;
FFieldName := FieldName;
FParserOptions := ParseOptions;
FDependentFields := DepFields;
End ;
Destructor TFilterExpr.Destroy;
Var
Node: PExprNode;
Begin
SetLength(FExprBuffer, 0);
While FNodes <> Nil Do
Begin
Node := FNodes;
FNodes := Node^.FNext;
If (Node^.FKind = enFunc) And (Node^.FArgs <> Nil) Then
Node^.FArgs.Free;
Dispose(Node);
End ;
End ;
Function TFilterExpr.FieldFromNode(Node: PExprNode): TField;
Begin
Result := GetFieldByName(Node^.FData);
If Not (Result.FieldKind In [fkData, fkInternalCalc]) Then
DatabaseErrorFmt(SExprBadField, [Result.FieldName]);
End ;
Function TFilterExpr.GetExprData(Pos, Size: Integer): PChar;
Begin
SetLength(FExprBuffer, FExprBufSize + Size);
Move(FExprBuffer[Pos], FExprBuffer[Pos + Size], FExprBufSize - Pos);
Inc(FExprBufSize, Size);
Result := PChar(FExprBuffer) + Pos;
End ;
Function TFilterExpr.GetFilterData(Root: PExprNode): TfsExprData;
Begin
FExprBufSize := CANExprSize;
SetLength(FExprBuffer, FExprBufSize);
PutExprNode(Root, coNOTDEFINED);
PWord(@FExprBuffer[0])^ := CANEXPRVERSION; { iVer }
PWord(@FExprBuffer[2])^ := FExprBufSize; { iTotalSize }
PWord(@FExprBuffer[4])^ := $FFFF; { iNodes }
PWord(@FExprBuffer[6])^ := CANEXPRSIZE; { iNodeStart }
PWord(@FExprBuffer[8])^ := FExprNodeSize + CANEXPRSIZE; { iLiteralStart }
Result := FExprBuffer;
End ;
Function TFilterExpr.NewCompareNode(Field: TField; Operator: TfsCANOperator;
Const Value: Variant): PExprNode;
Var
ConstExpr: PExprNode;
Begin
ConstExpr := NewNode(enConst, coNOTDEFINED, Value, Nil, Nil);
ConstExpr^.FDataType := Field.DataType;
ConstExpr^.FDataSize := Field.Size;
Result := NewNode(enOperator, Operator, Unassigned,
NewNode(enField, coNOTDEFINED, Field.FieldName, Nil, Nil), ConstExpr);
End ;
Function TFilterExpr.NewNode(Kind: TfsExprNodeKind; Operator: TfsCANOperator;
Const Data: Variant; Left, Right: PExprNode): PExprNode;
Var
Field: TField;
Begin
New(Result);
With Result^ Do
Begin
FNext := FNodes;
FKind := Kind;
FPartial := False;
FOperator := Operator;
FData := Data;
FLeft := Left;
FRight := Right;
End ;
FNodes := Result;
If Kind = enField Then
Begin
Field := GetFieldByName(Data);
If Field = Nil Then
DatabaseErrorFmt(SFieldNotFound, [Data]);
Result^.FDataType := Field.DataType;
Result^.FDataSize := Field.Size;
End ;
End ;
Function TFilterExpr.PutConstBCD(Const Value : Variant ;
Decimals : Integer ) : Integer ;
// Var
// C: Currency;
//BCD: TBcd;
Begin
// note
// bcd not implemented
{If VarType(Value) = varString Then
C := StrToCurr(String(TVarData(Value).VString))
Else
C := Value;
CurrToBCD(C, BCD, 32, Decimals);
Result := PutConstNode(ftBCD, @BCD, 18); }
Result := 0 ;
End ;
Function TFilterExpr.PutConstCurrency(Const Value: Variant): Integer;
Var
C: Currency;
Begin
If VarType(Value) = varString Then
C := StrToCurr(String(TVarData(Value).VString))
Else
C := Value;
Result := PutConstNode(ftCurrency, @C, 8);
End ;
Function TFilterExpr.PutConstBool(Const Value: Variant): Integer;
Var
B: WordBool;
Begin
B := Value;
Result := PutConstNode(ftBoolean, @B, SizeOf(WordBool));
End ;
Function TFilterExpr.PutConstDate(Const Value: Variant): Integer;
Var
DateTime: TDateTime;
TimeStamp: TTimeStamp;
Begin
If VarType(Value) = varString Then
DateTime := StrToDate(String(TVarData(Value).VString))
Else
DateTime := VarToDateTime(Value);
TimeStamp := DateTimeToTimeStamp(DateTime);
Result := PutConstNode(ftDate, @TimeStamp.Date, 4);
End ;
Function TFilterExpr.PutConstDateTime(Const Value: Variant): Integer;
Var
DateTime: TDateTime;
DateData: Double;
Begin
If VarType(Value) = varString Then
DateTime := StrToDateTime(String(TVarData(Value).VString))
Else
DateTime := VarToDateTime(Value);
DateData := TimeStampToMSecs(DateTimeToTimeStamp(DateTime));
Result := PutConstNode(ftDateTime, @DateData, 8);
End ;
Function TFilterExpr.PutConstFloat(Const Value: Variant): Integer;
Var
F: Extended;
Begin
If VarType(Value) = varString Then
F := StrToFloat(String(TVarData(Value).VString))
Else
F := Value;
Result := PutConstNode(ftFloat, @F, 10);
End ;
Function TFilterExpr.PutConstInt(DataType: TFieldType;
Const Value: Variant): Integer;
Var
Size: Integer;
I: Int64;
Begin
If VarType(Value) = varString Then
I := StrToInt64(String(TVarData(Value).VString))
Else
Begin
{$IFDEF IsNoVariantInt64}
I := Decimal(Value).lo64;
{$ELSE}
I := Value;
{$ENDIF}
End ;
Size := 8;
Case DataType Of
ftSmallint:
If (I < -32768) Or (I > 32767) Then
DatabaseError(SExprRangeError);
ftWord:
If (I < 0) Or (I > 65535) Then
DatabaseError(SExprRangeError);
End ;
Result := PutConstNode(DataType, @I, Size);
End ;
Function TFilterExpr.PutConstNode(DataType: TFieldType; Data: PChar;
Size: Integer): Integer;
Begin
Result := PutNode(nodeCONST, coCONST2, 3);
SetNodeOp(Result, 0, FFieldMap[DataType]);
SetNodeOp(Result, 1, Size);
SetNodeOp(Result, 2, PutData(Data, Size));
End ;
Function TFilterExpr.PutConstNode64(DataType: TFieldType; Data: PChar;
Size: Integer): Int64;
Begin
Result := PutNode(nodeCONST, coCONST2, 3);
SetNodeOp(Result, 0, FFieldMap[DataType]);
SetNodeOp(Result, 1, Size);
SetNodeOp(Result, 2, PutData(Data, Size));
End ;
Function TFilterExpr.PutConstStr(Const Value: String): Integer;
Var
Str: String;
Buffer: Array[0..255] Of Char;
Begin
If Length(Value) >= SizeOf(Buffer) Then
Str := Copy(Value, 1, SizeOf(Buffer) - 1)
Else
Str := Value;
FDataSet.Translate(PChar(Str), Buffer, True);
Result := PutConstNode(ftString, Buffer, Length(Str) + 1);
End ;
Function TFilterExpr.PutConstFsArrayStr(Const Value: String): Integer;
Var
Str: String;
Buffer: Array[0..255] Of Char;
Begin
If Length(Value) >= SizeOf(Buffer) Then
Str := Copy(Value, 1, SizeOf(Buffer) - 1)
Else
Str := Value;
FDataSet.Translate(PChar(Str), Buffer, True);
Result := PutConstNode(ftBytes, Buffer, Length(Str) + 1);
End ;
Function TFilterExpr.PutConstTime(Const Value: Variant): Integer;
Var
DateTime: TDateTime;
TimeStamp: TTimeStamp;
Begin
If VarType(Value) = varString Then
DateTime := StrToTime(String(TVarData(Value).VString))
Else
DateTime := VarToDateTime(Value);
TimeStamp := DateTimeToTimeStamp(DateTime);
Result := PutConstNode(ftTime, @TimeStamp.Time, 4);
End ;
Function TFilterExpr.PutData(Data: PChar; Size: Integer): Integer;
Begin
Move(Data^, GetExprData(FExprBufSize, Size)^, Size);
Result := FExprDataSize;
Inc(FExprDataSize, Size);
End ;
Function TFilterExpr.PutConstant(Node: PExprNode): Integer;
Begin
Result := 0;
Case Node^.FDataType Of
ftSmallint, ftWord, ftAutoInc, ftLargeInt, ftInteger:
Result := PutConstInt(Node^.FDataType, Node^.FData);
ftFloat: Result := PutConstFloat(Node^.FData);
ftString, ftWideString, ftFixedChar:
{$ifdef fpc}
if VarIsArray(Node^.FData) then //soner solves : "Invalid Variant Type Cast":
Result := PutConstStr(Node^.FData[0])
else
{$endif}
Result := PutConstStr(Node^.FData);
ftBytes:
Result := PutConstFsArrayStr(Node^.FData);
ftDate:
Result := PutConstDate(Node^.FData);
ftTime:
Result := PutConstTime(Node^.FData);
ftDateTime:
Result := PutConstDateTime(Node^.FData);
ftBoolean:
Result := PutConstBool(Node^.FData);
ftBCD:
Result := PutConstBCD(Node^.FData, Node^.FDataSize);
ftCurrency:
Result := PutConstCurrency(Node^.FData);
Else
DatabaseErrorFmt(SExprBadConst, [Node^.FData]);
End ;
End ;
Function TFilterExpr.PutExprNode(Node: PExprNode; ParentOp: TfsCANOperator): Integer;
Const
ReverseOperator: Array[coEQ..coLE] Of TfsCANOperator = (coEQ, coNE, coLT,
coGT, coLE, coGE);
BoolFalse: WordBool = False;
Var
Field: TField;
Left, Right, Temp: PExprNode;
LeftPos, RightPos, ListElem, PrevListElem, I: Integer;
Operator: TfsCANOperator;
CaseInsensitive, PartialLength, L: Integer;
S: AnsiString;
Begin
Result := 0;
Case Node^.FKind Of
enField:
Begin
Field := FieldFromNode(Node);
If (ParentOp In [coOR, coNOT, coAND, coNOTDEFINED]) And
(Field.DataType = ftBoolean) Then
Begin
Result := PutNode(nodeBINARY, coNE, 2);
SetNodeOp(Result, 0, PutFieldNode(Field, Node));
SetNodeOp(Result, 1, PutConstNode(ftBoolean, @BoolFalse, SizeOf(WordBool)));
End
Else
Result := PutFieldNode(Field, Node);
End ;
enConst:
Result := PutConstant(Node);
enOperator:
Case Node^.FOperator Of
coIN:
Begin
Result := PutNode(nodeBINARY, coIN, 2);
SetNodeOp(Result, 0, PutExprNode(Node^.FLeft, Node^.FOperator));
ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2);
SetNodeOp(Result, 1, ListElem);
PrevListElem := ListElem;
For I := 0 To Node^.FArgs.Count - 1 Do
Begin
LeftPos := PutExprNode(Node^.FArgs.Items[I], Node^.FOperator);
If I = 0 Then
Begin
SetNodeOp(PrevListElem, 0, LeftPos);
SetNodeOp(PrevListElem, 1, 0);
End
Else
Begin
ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2);
SetNodeOp(ListElem, 0, LeftPos);
SetNodeOp(ListElem, 1, 0);
SetNodeOp(PrevListElem, 1, ListElem);
PrevListElem := ListElem;
End ;
End ;
End ;
coNOT,
coISBLANK,
coNOTBLANK:
Begin
Result := PutNode(nodeUNARY, Node^.FOperator, 1);
SetNodeOp(Result, 0, PutExprNode(Node^.FLeft, Node^.FOperator));
End ;
coEQ..coLE,
coAND, coOR,
coADD..coDIV,
coLIKE,
coASSIGN:
Begin
Operator := Node^.FOperator;
Left := Node^.FLeft;
Right := Node^.FRight;
If (Operator In [coEQ..coLE]) And (Right^.FKind = enField) And
(Left^.FKind <> enField) Then
Begin
Temp := Left;
Left := Right;
Right := Temp;
Operator := ReverseOperator[Operator];
End ;
Result := 0;
If (Left^.FKind = enField) And (Right^.FKind = enConst)
And ((Node^.FOperator = coEQ) Or (Node^.FOperator = coNE)
Or (Node^.FOperator = coLIKE)) Then
Begin
If VarIsNull(Right^.FData) Then
Begin
Case Node^.FOperator Of
coEQ: Operator := coISBLANK;
coNE: Operator := coNOTBLANK;
Else
DatabaseError(SExprBadNullTest);
End ;
Result := PutNode(nodeUNARY, Operator, 1);
SetNodeOp(Result, 0, PutExprNode(Left, Node^.FOperator));
End
Else If (Right^.FDataType In StringFieldTypes) Then
Begin
{$ifdef fpc}
if VarIsArray(Right^.FData) then //soner solves : "Invalid Variant Type Cast":
s:=Right^.FData[0]
else
{$endif}
S := Right^.FData; //soner this dont work, i get "Invalid Variant Type Cast": VarToStr(Right^.FData)
L := Length(S);
If L <> 0 Then
Begin
CaseInsensitive := 0;
PartialLength := 0;
If foCaseInsensitive In FOptions Then
CaseInsensitive := 1;
If Node^.FPartial Then
PartialLength := L
Else If Not (foNoPartialCompare In FOptions) And (L > 1) And
(S[L] = '*') Then
Begin
Delete(S, L, 1);
PartialLength := L - 1;
End ;
If (CaseInsensitive <> 0) Or (PartialLength <> 0) Then
Begin
Result := PutNode(nodeCOMPARE, Operator, 4);
SetNodeOp(Result, 0, CaseInsensitive);
SetNodeOp(Result, 1, PartialLength);
SetNodeOp(Result, 2, PutExprNode(Left, Node^.FOperator));
SetNodeOp(Result, 3, PutConstStr(S));
End ;
End ;
End ;
End ;
If Result = 0 Then
Begin
If (Operator = coISBLANK) Or (Operator = coNOTBLANK) Then
Begin
Result := PutNode(nodeUNARY, Operator, 1);
LeftPos := PutExprNode(Left, Node^.FOperator);
SetNodeOp(Result, 0, LeftPos);
End
Else
Begin
Result := PutNode(nodeBINARY, Operator, 2);
LeftPos := PutExprNode(Left, Node^.FOperator);
RightPos := PutExprNode(Right, Node^.FOperator);
SetNodeOp(Result, 0, LeftPos);
SetNodeOp(Result, 1, RightPos);
End ;
End ;
End ;
End ;
enFunc:
Begin
Result := PutNode(nodeFUNC, coFUNC2, 2);
SetNodeOp(Result, 0, PutData(PChar(String(Node^.FData)),
Length(String(Node^.FData)) + 1));
If Node^.FArgs <> Nil Then
Begin
ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2);
SetNodeOp(Result, 1, ListElem);
PrevListElem := ListElem;
For I := 0 To Node^.FArgs.Count - 1 Do
Begin
LeftPos := PutExprNode(Node^.FArgs.Items[I], Node^.FOperator);
If I = 0 Then
Begin
SetNodeOp(PrevListElem, 0, LeftPos);
SetNodeOp(PrevListElem, 1, 0);
End
Else
Begin
ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2);
SetNodeOp(ListElem, 0, LeftPos);
SetNodeOp(ListElem, 1, 0);
SetNodeOp(PrevListElem, 1, ListElem);
PrevListElem := ListElem;
End ;
End ;
End
Else
SetNodeOp(Result, 1, 0);
End ;
End ;
End ;
Function TFilterExpr.PutFieldNode(Field : TField ;
Node : PExprNode ) : Integer ;
Var
Buffer: Array[0..255] Of Char ;
cStr : String ;
Begin
cStr := Field.FieldName ;
If (poFieldNameGiven in FParserOptions) then
FDataSet.Translate(PChar(cStr) , Buffer , True)
Else
FDataSet.Translate(PChar(String(Node^.FData)) , Buffer , True) ;
Result := PutNode(nodeFIELD , coFIELD2 , 2) ;
SetNodeOp(Result , 0 , Field.FieldNo) ;
SetNodeOp(Result , 1 , PutData(Buffer , StrLen(Buffer) + 1)) ;
End ;
Function TFilterExpr.PutNode(NodeType: fsNODEClass; OpType: TfsCANOperator;
OpCount: Integer): Integer;
Var
Size: Integer;
Data: PChar;
Begin
Size := CANHDRSIZE + OpCount * SizeOf(Word);
Data := GetExprData(CANEXPRSIZE + FExprNodeSize, Size);
PInteger(@Data[0])^ := Integer(NodeType); { CANHdr.fsNODEClass }
PInteger(@Data[4])^ := Integer(OpType); { CANHdr.coOp }
Result := FExprNodeSize;
Inc(FExprNodeSize, Size);
End ;
Procedure TFilterExpr.SetNodeOp(Node, Index, Data: Integer);
Begin
PWordArray(PChar(FExprBuffer) + (CANEXPRSIZE + Node +
CANHDRSIZE))^[Index] := Data;
End ;
Function TFilterExpr.GetFieldByName(Name: String): TField;
Var
I: Integer;
F: TField;
FieldInfo: TfsFieldInfo;
Begin
Result := Nil;
If poFieldNameGiven In FParserOptions Then
Result := FDataSet.FieldByName(FFieldName)
Else If poUseOrigNames In FParserOptions Then
Begin
For I := 0 To FDataset.FieldCount - 1 Do
Begin
F := FDataSet.Fields[I];
If fsGetFieldInfo(F.Origin, FieldInfo) And
(AnsiCompareStr(Name, FieldInfo.OriginalFieldName) = 0) Then
Begin
Result := F;
Exit;
End ;
End ;
End ;
If Result = Nil Then
Result := FDataSet.FieldByName(Name);
If (Result <> Nil) And (Result.FieldKind = fkCalculated) And (poAggregate In FParserOptions) Then
DatabaseErrorFmt(SExprNoAggOnCalcs, [Result.FieldName]);
If (poFieldDepend In FParserOptions) And (Result <> Nil) And
(FDependentFields <> Nil) Then
FDependentFields[Result.FieldNo - 1] := True;
End ;
Constructor TExprParser.Create(DataSet: TDataSet; Const Text: String;
Options: TFilterOptions; ParserOptions: TfsParserOptions; Const FieldName: String;
DepFields: TBits; FieldMap: TFieldMap);
Begin
FFieldMap := FieldMap;
FStrTrue := STextTrue;
FStrFalse := STextFalse;
FDataSet := DataSet;
FDependentFields := DepFields;
FFilter := TFilterExpr.Create(DataSet, Options, ParserOptions, FieldName,
DepFields, FieldMap);
If Text <> '' Then
SetExprParams(Text, Options, ParserOptions, FieldName);
End ;
Destructor TExprParser.Destroy;
Begin
FFilter.Free;
End ;
Procedure TExprParser.SetExprParams(Const Text: String; Options: TFilterOptions;
ParserOptions: TfsParserOptions; Const FieldName: String);
Var
Root, DefField: PExprNode;
Begin
FParserOptions := ParserOptions;
If FFilter <> Nil Then
FFilter.Free;
FFilter := TFilterExpr.Create(FDataSet, Options, ParserOptions, FieldName,
FDependentFields, FFieldMap);
FText := Text;
FSourcePtr := PChar(Text);
FFieldName := FieldName;
NextToken;
Root := ParseExpr;
If FToken <> etEnd Then
DatabaseError(SExprTermination);
If (poAggregate In FParserOptions) And (Root^.FScopeKind <> skAgg) Then
DatabaseError(SExprNotAgg);
If (Not (poAggregate In FParserOptions)) And (Root^.FScopeKind = skAgg) Then
DatabaseError(SExprNoAggFilter);
If poDefaultExpr In ParserOptions Then
Begin
DefField := FFilter.NewNode(enField, coNOTDEFINED, FFieldName, Nil, Nil);
If (IsTemporal(DefField^.FDataType) And (Root^.FDataType In StringFieldTypes)) Or
((DefField^.FDataType = ftBoolean) And (Root^.FDataType In StringFieldTypes)) Then
Root^.FDataType := DefField^.FDataType;
If Not ((IsTemporal(DefField^.FDataType) And IsTemporal(Root^.FDataType))
Or (IsNumeric(DefField^.FDataType) And IsNumeric(Root^.FDataType))
Or ((DefField^.FDataType In StringFieldTypes) And (Root^.FDataType In StringFieldTypes))
Or ((DefField^.FDataType = ftBoolean) And (Root^.FDataType = ftBoolean))) Then
DatabaseError(SExprTypeMis);
Root := FFilter.NewNode(enOperator, coASSIGN, Unassigned, Root, DefField);
End ;
If Not (poAggregate In FParserOptions) And Not (poDefaultExpr In ParserOptions)
And (Root^.FDataType <> ftBoolean) Then
DatabaseError(SExprIncorrect);
FFilterData := FFilter.GetFilterData(Root);
FDataSize := FFilter.FExprBufSize;
End ;
Function TExprParser.NextTokenIsLParen: Boolean;
Var
P: PChar;
Begin
P := FSourcePtr;
While (P^ <> #0) And (P^ <= ' ') Do
Inc(P);
Result := P^ = '(';
End ;
Procedure TExprParser.NextToken;
Type
ASet = Set Of Char;
Var
P, TokenStart: PChar;
L: Integer;
StrBuf: Array[0..255] Of Char;
Function IsKatakana(Const Chr: Byte): Boolean;
Begin
Result := False; // wik (SysLocale.PriLangID = LANG_JAPANESE) and (Chr in [$A1..$DF]);
End ;
Procedure Skip(TheSet: ASet);
Begin
While True Do
Begin
If P^ In LeadBytes Then
Inc(P, 2)
Else If (P^ In TheSet) Or IsKatakana(Byte(P^)) Then
Inc(P)
Else
Exit;
End ;
End ;
Begin
FPrevToken := FToken;
FTokenString := '';
P := FSourcePtr;
While (P^ <> #0) And (P^ <= ' ') Do
Inc(P);
If (P^ <> #0) And (P^ = '/') And (P[1] <> #0) And (P[1] = '*') Then
Begin
P := P + 2;
While (P^ <> #0) And (P^ <> '*') Do
Inc(P);
If (P^ = '*') And (P[1] <> #0) And (P[1] = '/') Then
P := P + 2
Else
DatabaseErrorFmt(SExprInvalidChar, [P^]);
End ;
While (P^ <> #0) And (P^ <= ' ') Do
Inc(P);
FTokenPtr := P;
Case P^ Of
'A'..'Z', 'a'..'z', '_', #$81..#$FE:
Begin
TokenStart := P;
If Not SysLocale.FarEast Then
Begin
Inc(P);
While P^ In ['A'..'Z', 'a'..'z', '0'..'9', '_', '.', '[', ']'] Do
Inc(P);
End
Else
Skip(['A'..'Z', 'a'..'z', '0'..'9', '_', '.', '[', ']']);
SetString(FTokenString, TokenStart, P - TokenStart);
FToken := etSymbol;
If CompareText(FTokenString, 'LIKE') = 0 Then { do not localize }
FToken := etLIKE
Else If CompareText(FTokenString, 'IN') = 0 Then { do not localize }
FToken := etIN
Else If CompareText(FTokenString, 'IS') = 0 Then { do not localize }
Begin
While (P^ <> #0) And (P^ <= ' ') Do
Inc(P);
TokenStart := P;
Skip(['A'..'Z', 'a'..'z']);
SetString(FTokenString, TokenStart, P - TokenStart);
If CompareText(FTokenString, 'NOT') = 0 Then { do not localize }
Begin
While (P^ <> #0) And (P^ <= ' ') Do
Inc(P);
TokenStart := P;
Skip(['A'..'Z', 'a'..'z']);
SetString(FTokenString, TokenStart, P - TokenStart);
If CompareText(FTokenString, 'NULL') = 0 Then
FToken := etISNOTNULL
Else
DatabaseError(SInvalidKeywordUse);
End
Else If CompareText(FTokenString, 'NULL') = 0 Then { do not localize }
Begin
FToken := etISNULL;
End
Else
DatabaseError(SInvalidKeywordUse);
End ;
End ;
'[':
Begin
Inc(P);
TokenStart := P;
P := AnsiStrScan(P, ']');
If P = Nil Then
DatabaseError(SExprNameError);
SetString(FTokenString, TokenStart, P - TokenStart);
FToken := etName;
Inc(P);
End ;
'''':
Begin
Inc(P);
L := 0;
While True Do
Begin
If P^ = #0 Then
DatabaseError(SExprStringError);
If P^ = '''' Then
Begin
Inc(P);
If P^ <> '''' Then
Break;
End ;
If L < SizeOf(StrBuf) Then
Begin
StrBuf[L] := P^;
Inc(L);
End ;
Inc(P);
End ;
SetString(FTokenString, StrBuf, L);
FToken := etLiteral;
FNumericLit := False;
End ;
'-', '0'..'9':
Begin
If (FPrevToken <> etLiteral) And (FPrevToken <> etName) And
(FPrevToken <> etSymbol) And (FPrevToken <> etRParen) Then
Begin
TokenStart := P;
Inc(P);
While (P^ In ['0'..'9', DecimalSeparator, 'e', 'E', '+', '-']) Do
Inc(P);
If ((P - 1)^ = ',') And (DecimalSeparator = ',') And (P^ = ' ') Then
Dec(P);
SetString(FTokenString, TokenStart, P - TokenStart);
FToken := etLiteral;
FNumericLit := True;
End
Else
Begin
FToken := etSUB;
Inc(P);
End ;
End ;
'(':
Begin
Inc(P);
FToken := etLParen;
End ;
')':
Begin
Inc(P);
FToken := etRParen;
End ;
'<':
Begin
Inc(P);
Case P^ Of
'=':
Begin
Inc(P);
FToken := etLE;
End ;
'>':
Begin
Inc(P);
FToken := etNE;
End ;
Else
FToken := etLT;
End ;
End ;
'=':
Begin
Inc(P);
FToken := etEQ;
End ;
'>':
Begin
Inc(P);
If P^ = '=' Then
Begin
Inc(P);
FToken := etGE;
End
Else
FToken := etGT;
End ;
'+':
Begin
Inc(P);
FToken := etADD;
End ;
'*':
Begin
Inc(P);
FToken := etMUL;
End ;
'/':
Begin
Inc(P);
FToken := etDIV;
End ;
',':
Begin
Inc(P);
FToken := etComma;
End ;
#0:
FToken := etEnd ;
Else
DatabaseErrorFmt(SExprInvalidChar, [P^]);
End ;
FSourcePtr := P;
End ;
Function TExprParser.ParseExpr: PExprNode;
Begin
Result := ParseExpr2;
While TokenSymbolIs('OR') Do
Begin
NextToken;
Result := FFilter.NewNode(enOperator, coOR, Unassigned,
Result, ParseExpr2);
GetScopeKind(Result, Result^.FLeft, Result^.FRight);
Result^.FDataType := ftBoolean;
End ;
End ;
Function TExprParser.ParseExpr2: PExprNode;
Begin
Result := ParseExpr3;
While TokenSymbolIs('AND') Do
Begin
NextToken;
Result := FFilter.NewNode(enOperator, coAND, Unassigned,
Result, ParseExpr3);
GetScopeKind(Result, Result^.FLeft, Result^.FRight);
Result^.FDataType := ftBoolean;
End ;
End ;
Function TExprParser.ParseExpr3: PExprNode;
Begin
If TokenSymbolIs('NOT') Then
Begin
NextToken;
Result := FFilter.NewNode(enOperator, coNOT, Unassigned,
ParseExpr4, Nil);
Result^.FDataType := ftBoolean;
End
Else
Result := ParseExpr4;
GetScopeKind(Result, Result^.FLeft, Result^.FRight);
End ;
Function TExprParser.ParseExpr4: PExprNode;
Const
Operators: Array[etEQ..etLT] Of TfsCANOperator = (
coEQ, coNE, coGE, coLE, coGT, coLT);
Var
Operator: TfsCANOperator;
Left, Right: PExprNode;
Begin
Result := ParseExpr5;
If (FToken In [etEQ..etLT]) Or (FToken = etLIKE)
Or (FToken = etISNULL) Or (FToken = etISNOTNULL)
Or (FToken = etIN) Then
Begin
Case FToken Of
etEQ..etLT:
Operator := Operators[FToken];
etLIKE:
Operator := coLIKE;
etISNULL:
Operator := coISBLANK;
etISNOTNULL:
Operator := coNOTBLANK;
etIN:
Operator := coIN;
Else
Operator := coNOTDEFINED;
End ;
NextToken;
Left := Result;
If Operator = coIN Then
Begin
If FToken <> etLParen Then
DatabaseErrorFmt(SExprNoLParen, [TokenName]);
NextToken;
Result := FFilter.NewNode(enOperator, coIN, Unassigned,
Left, Nil);
Result.FDataType := ftBoolean;
If FToken <> etRParen Then
Begin
Result.FArgs := TList.Create;
Repeat
Right := ParseExpr;
If IsTemporal(Left.FDataType) Then
Right.FDataType := Left.FDataType;
Result.FArgs.Add(Right);
If (FToken <> etComma) And (FToken <> etRParen) Then
DatabaseErrorFmt(SExprNoRParenOrComma, [TokenName]);
If FToken = etComma Then
NextToken;
Until (FToken = etRParen) Or (FToken = etEnd);
If FToken <> etRParen Then
DatabaseErrorFmt(SExprNoRParen, [TokenName]);
NextToken;
End
Else
DatabaseError(SExprEmptyInList);
End
Else
Begin
If (Operator <> coISBLANK) And (Operator <> coNOTBLANK) Then
Right := ParseExpr5
Else
Right := Nil;
Result := FFilter.NewNode(enOperator, Operator, Unassigned,
Left, Right);
If Right <> Nil Then
Begin
If (Left^.FKind = enField) And (Right^.FKind = enConst) Then
Begin
Right^.FDataType := Left^.FDataType;
Right^.FDataSize := Left^.FDataSize;
End
Else If (Right^.FKind = enField) And (Left^.FKind = enConst) Then
Begin
Left^.FDataType := Right^.FDataType;
Left^.FDataSize := Right^.FDataSize;
End ;
End ;
If (Left^.FDataType In BlobFieldTypes) And (Operator = coLIKE) Then
Begin
If Right^.FKind = enConst Then
Right^.FDataType := ftString;
End
Else If (Operator <> coISBLANK) And (Operator <> coNOTBLANK)
And ((Left^.FDataType In (BlobFieldTypes {+ [ftBytes]})) Or
((Right <> Nil) And (Right^.FDataType In (BlobFieldTypes {+ [ftBytes]})))) Then
DatabaseError(SExprTypeMis);
Result.FDataType := ftBoolean;
If Right <> Nil Then
Begin
If IsTemporal(Left.FDataType) And (Right.FDataType In StringFieldTypes) Then
Right.FDataType := Left.FDataType
Else If IsTemporal(Right.FDataType) And (Left.FDataType In StringFieldTypes) Then
Left.FDataType := Right.FDataType;
End ;
GetScopeKind(Result, Left, Right);
End ;
End ;
End ;
Function TExprParser.ParseExpr5: PExprNode;
Const
Operators: Array[etADD..etDIV] Of TfsCANOperator = (
coADD, coSUB, coMUL, coDIV);
Var
Operator: TfsCANOperator;
Left, Right: PExprNode;
Begin
Result := ParseExpr6;
While FToken In [etADD, etSUB] Do
Begin
If Not (poExtSyntax In FParserOptions) Then
DatabaseError(SExprNoArith);
Operator := Operators[FToken];
Left := Result;
NextToken;
Right := ParseExpr6;
Result := FFilter.NewNode(enOperator, Operator, Unassigned, Left, Right);
TypeCheckArithOp(Result);
GetScopeKind(Result, Left, Right);
End ;
End ;
Function TExprParser.ParseExpr6: PExprNode;
Const
Operators: Array[etADD..etDIV] Of TfsCANOperator = (
coADD, coSUB, coMUL, coDIV);
Var
Operator: TfsCANOperator;
Left, Right: PExprNode;
Begin
Result := ParseExpr7;
While FToken In [etMUL, etDIV] Do
Begin
If Not (poExtSyntax In FParserOptions) Then
DatabaseError(SExprNoArith);
Operator := Operators[FToken];
Left := Result;
NextToken;
Right := ParseExpr7;
Result := FFilter.NewNode(enOperator, Operator, Unassigned, Left, Right);
TypeCheckArithOp(Result);
GetScopeKind(Result, Left, Right);
End ;
End ;
Function TExprParser.ParseExpr7: PExprNode;
Var
FuncName: String;
Begin
Case FToken Of
etSymbol:
If (poExtSyntax In FParserOptions)
And NextTokenIsLParen And TokenSymbolIsFunc(FTokenString) Then
Begin
Funcname := FTokenString;
NextToken;
If FToken <> etLParen Then
DatabaseErrorFmt(SExprNoLParen, [TokenName]);
NextToken;
If (CompareText(FuncName, 'count') = 0) And (FToken = etMUL) Then
Begin
FuncName := 'COUNT(*)';
NextToken;
End ;
Result := FFilter.NewNode(enFunc, coNOTDEFINED, FuncName,
Nil, Nil);
If FToken <> etRParen Then
Begin
Result.FArgs := TList.Create;
Repeat
Result.FArgs.Add(ParseExpr);
If (FToken <> etComma) And (FToken <> etRParen) Then
DatabaseErrorFmt(SExprNoRParenOrComma, [TokenName]);
If FToken = etComma Then
NextToken;
Until (FToken = etRParen) Or (FToken = etEnd);
End
Else
Result.FArgs := Nil;
GetFuncResultInfo(Result);
End
Else If TokenSymbolIs('NULL') Then
Begin
Result := FFilter.NewNode(enConst, coNOTDEFINED, Null, Nil, Nil);
Result.FScopeKind := skConst;
End
Else If TokenSymbolIs(FStrTrue) Then
Begin
Result := FFilter.NewNode(enConst, coNOTDEFINED, 1, Nil, Nil);
Result.FScopeKind := skConst;
End
Else If TokenSymbolIs(FStrFalse) Then
Begin
Result := FFilter.NewNode(enConst, coNOTDEFINED, 0, Nil, Nil);
Result.FScopeKind := skConst;
End
Else
Begin
Result := FFilter.NewNode(enField, coNOTDEFINED, FTokenString, Nil, Nil);
Result.FScopeKind := skField;
End ;
etName:
Begin
Result := FFilter.NewNode(enField, coNOTDEFINED, FTokenString, Nil, Nil);
Result.FScopeKind := skField;
End ;
etLiteral:
Begin
Result := FFilter.NewNode(enConst, coNOTDEFINED, FTokenString, Nil, Nil);
If FNumericLit Then
Result^.FDataType := ftFloat
Else
Result^.FDataType := ftString;
Result.FScopeKind := skConst;
End ;
etLParen:
Begin
NextToken;
Result := ParseExpr;
If FToken <> etRParen Then
DatabaseErrorFmt(SExprNoRParen, [TokenName]);
End ;
Else
DatabaseErrorFmt(SExprExpected, [TokenName]);
Result := Nil;
End ;
NextToken;
End ;
Procedure TExprParser.GetScopeKind(Root, Left, Right: PExprNode);
Begin
If (Left = Nil) And (Right = Nil) Then
Exit;
If Right = Nil Then
Begin
Root.FScopeKind := Left.FScopeKind;
Exit;
End ;
If ((Left^.FScopeKind = skField) And (Right^.FScopeKind = skAgg))
Or ((Left^.FScopeKind = skAgg) And (Right^.FScopeKind = skField)) Then
DatabaseError(SExprBadScope);
If (Left^.FScopeKind = skConst) And (Right^.FScopeKind = skConst) Then
Root^.FScopeKind := skConst
Else If (Left^.FScopeKind = skAgg) Or (Right^.FScopeKind = skAgg) Then
Root^.FScopeKind := skAgg
Else If (Left^.FScopeKind = skField) Or (Right^.FScopeKind = skField) Then
Root^.FScopeKind := skField;
End ;
Procedure TExprParser.GetFuncResultInfo(Node: PExprNode);
Begin
Node^.FDataType := ftString;
If (CompareText(Node^.FData, 'COUNT(*)') <> 0)
And (CompareText(Node^.FData, 'GETDATE') <> 0)
And ((Node^.FArgs = Nil) Or (Node^.FArgs.Count = 0)) Then
DatabaseError(SExprTypeMis);
If (Node^.FArgs <> Nil) And (Node^.FArgs.Count > 0) Then
Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind;
If (CompareText(Node^.FData, 'SUM') = 0) Or
(CompareText(Node^.FData, 'AVG') = 0) Then
Begin
Node^.FDataType := ftFloat;
Node^.FScopeKind := skAgg;
End
Else If (CompareText(Node^.FData, 'MIN') = 0) Or
(CompareText(Node^.FData, 'MAX') = 0) Then
Begin
Node^.FDataType := PExprNode(Node^.FArgs.Items[0])^.FDataType;
Node^.FScopeKind := skAgg;
End
Else If (CompareText(Node^.FData, 'COUNT') = 0) Or
(CompareText(Node^.FData, 'COUNT(*)') = 0) Then
Begin
Node^.FDataType := ftInteger;
Node^.FScopeKind := skAgg;
End
Else If (CompareText(Node^.FData, 'YEAR') = 0) Or
(CompareText(Node^.FData, 'MONTH') = 0) Or
(CompareText(Node^.FData, 'DAY') = 0) Or
(CompareText(Node^.FData, 'HOUR') = 0) Or
(CompareText(Node^.FData, 'MINUTE') = 0) Or
(CompareText(Node^.FData, 'SECOND') = 0) Then
Begin
Node^.FDataType := ftInteger;
Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind;
End
Else If CompareText(Node^.FData, 'GETDATE') = 0 Then
Begin
Node^.FDataType := ftDateTime;
Node^.FScopeKind := skConst;
End
Else If CompareText(Node^.FData, 'DATE') = 0 Then
Begin
Node^.FDataType := ftDate;
Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind;
End
Else If CompareText(Node^.FData, 'TIME') = 0 Then
Begin
Node^.FDataType := ftTime;
Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind;
End ;
End ;
Function TExprParser.TokenName: String;
Begin
If FSourcePtr = FTokenPtr Then
Result := SExprNothing
Else
Begin
SetString(Result, FTokenPtr, FSourcePtr - FTokenPtr);
Result := '''' + Result + '''';
End ;
End ;
Function TExprParser.TokenSymbolIs(Const S: String): Boolean;
Begin
Result := (FToken = etSymbol) And (CompareText(FTokenString, S) = 0);
End ;
Function TExprParser.TokenSymbolIsFunc(Const S: String): Boolean;
Begin
Result := (CompareText(S, 'UPPER') = 0) Or
(CompareText(S, 'LOWER') = 0) Or
(CompareText(S, 'SUBSTRING') = 0) Or
(CompareText(S, 'TRIM') = 0) Or
(CompareText(S, 'TRIMLEFT') = 0) Or
(CompareText(S, 'TRIMRIGHT') = 0) Or
(CompareText(S, 'YEAR') = 0) Or
(CompareText(S, 'MONTH') = 0) Or
(CompareText(S, 'DAY') = 0) Or
(CompareText(S, 'HOUR') = 0) Or
(CompareText(S, 'MINUTE') = 0) Or
(CompareText(S, 'SECOND') = 0) Or
(CompareText(S, 'GETDATE') = 0) Or
(CompareText(S, 'DATE') = 0) Or
(CompareText(S, 'TIME') = 0) Or
(CompareText(S, 'SUM') = 0) Or
(CompareText(S, 'MIN') = 0) Or
(CompareText(S, 'MAX') = 0) Or
(CompareText(S, 'AVG') = 0) Or
(CompareText(S, 'COUNT') = 0);
End ;
Procedure TExprParser.TypeCheckArithOp(Node: PExprNode);
Begin
With Node^ Do
Begin
If IsNumeric(FLeft.FDataType) And IsNumeric(FRight.FDataType) Then
FDataType := ftFloat
Else If (FLeft.FDataType In StringFieldTypes) And
(FRight.FDataType In StringFieldTypes) And (FOperator = coADD) Then
FDataType := ftString
Else If IsTemporal(FLeft.FDataType) And IsNumeric(FRight.FDataType) And
(FOperator = coADD) Then
FDataType := ftDateTime
Else If IsTemporal(FLeft.FDataType) And IsNumeric(FRight.FDataType) And
(FOperator = coSUB) Then
FDataType := FLeft.FDataType
Else If IsTemporal(FLeft.FDataType) And IsTemporal(FRight.FDataType) And
(FOperator = coSUB) Then
FDataType := ftFloat
Else If (FLeft.FDataType In StringFieldTypes) And IsTemporal(FRight.FDataType) And
(FOperator = coSUB) Then
Begin
FLeft.FDataType := FRight.FDataType;
FDataType := ftFloat;
End
Else If (FLeft.FDataType In StringFieldTypes) And IsNumeric(FRight.FDataType) And
(FLeft.FKind = enConst) Then
FLeft.FDataType := ftDateTime
Else
DatabaseError(SExprTypeMis);
End ;
End ;
End.