{ 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.