home *** CD-ROM | disk | FTP | other *** search
-
- {*******************************************************}
- { }
- { Borland Delphi Visual Component Library }
- { Common Database Code }
- { }
- { Copyright (c) 1995,99 Inprise Corporation }
- { }
- {*******************************************************}
-
- unit DBCommon;
-
- {$T-,H+,X+,R-}
-
- interface
-
- uses Windows, Classes, DB;
-
- type
- TCANOperator = (
- 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 }
- );
-
- NODEClass = ( { 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
- TExprData = array of Byte;
- TFieldMap = array[TFieldType] of Byte;
-
- { TFilterExpr }
-
- type
-
- TParserOption = (poExtSyntax, poAggregate, poDefaultExpr, poUseOrigNames,
- poFieldNameGiven, poFieldDepend);
- TParserOptions = set of TParserOption;
-
- TExprNodeKind = (enField, enConst, enOperator, enFunc);
- TExprScopeKind = (skField, skAgg, skConst);
-
- PExprNode = ^TExprNode;
- TExprNode = record
- FNext: PExprNode;
- FKind: TExprNodeKind;
- FPartial: Boolean;
- FOperator: TCANOperator;
- FData: Variant;
- FLeft: PExprNode;
- FRight: PExprNode;
- FDataType: TFieldType;
- FDataSize: Integer;
- FArgs: TList;
- FScopeKind: TExprScopeKind;
- end;
-
- TFilterExpr = class
- private
- FDataSet: TDataSet;
- FFieldMap: TFieldMap;
- FOptions: TFilterOptions;
- FParserOptions: TParserOptions;
- FNodes: PExprNode;
- FExprBuffer: TExprData;
- 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 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;
- function PutConstStr(const Value: string): Integer;
- function PutConstTime(const Value: Variant): Integer;
- function PutData(Data: PChar; Size: Integer): Integer;
- function PutExprNode(Node: PExprNode; ParentOp: TCANOperator): Integer;
- function PutFieldNode(Field: TField; Node: PExprNode): Integer;
- function PutNode(NodeType: NodeClass; OpType: TCANOperator;
- 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: TParserOptions; const FieldName: string; DepFields: TBits;
- FieldMap: TFieldMap);
- destructor Destroy; override;
- function NewCompareNode(Field: TField; Operator: TCANOperator;
- const Value: Variant): PExprNode;
- function NewNode(Kind: TExprNodeKind; Operator: TCANOperator;
- const Data: Variant; Left, Right: PExprNode): PExprNode;
- function GetFilterData(Root: PExprNode): TExprData;
- property DataSet: TDataSet write FDataSet;
- end;
-
- { TExprParser }
-
- TExprToken = (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: TExprToken;
- FPrevToken: TExprToken;
- FFilterData: TExprData;
- FNumericLit: Boolean;
- FDataSize: Integer;
- FParserOptions: TParserOptions;
- 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: TParserOptions;
- const FieldName: string; DepFields: TBits; FieldMap: TFieldMap);
- destructor Destroy; override;
- procedure SetExprParams(const Text: string; Options: TFilterOptions;
- ParserOptions: TParserOptions; const FieldName: string);
- property FilterData: TExprData read FFilterData;
- property DataSize: Integer read FDataSize;
- end;
-
- { Field Origin parser }
-
- type
- TFieldInfo = record
- DatabaseName: string;
- TableName: string;
- OriginalFieldName: string;
- end;
-
- function GetFieldInfo(const Origin: string; var FieldInfo: TFieldInfo): Boolean;
-
- { SQL Parser }
-
- type
- TSQLToken = (stUnknown, stTableName, stFieldName, stAscending, stDescending, stSelect,
- stFrom, stWhere, stGroupBy, stHaving, stUnion, stPlan, stOrderBy, stForUpdate,
- stEnd, stPredicate, stValue, stIsNull, stIsNotNull, stLike, stAnd, stOr,
- stNumber, stAllFields, stComment, stDistinct);
-
- const
- SQLSections = [stSelect, stFrom, stWhere, stGroupBy, stHaving, stUnion,
- stPlan, stOrderBy, stForUpdate];
-
- function NextSQLToken(var p: PChar; out Token: string; CurSection: TSQLToken): TSQLToken;
- function GetIndexForOrderBy(const SQL: string; DataSet: TDataSet): TIndexDef;
- function GetTableNameFromSQL(const SQL: string): string;
-
- implementation
-
- uses SysUtils, DBConsts;
-
- { SQL Parser }
-
- function NextSQLToken(var p: PChar; out Token: string; CurSection: TSQLToken): TSQLToken;
- var
- DotStart: Boolean;
-
- function NextTokenIs(Value: string; var Str: string): Boolean;
- var
- Tmp: PChar;
- S: string;
- begin
- Tmp := p;
- NextSQLToken(Tmp, S, CurSection);
- Result := AnsiCompareText(Value, S) = 0;
- if Result then
- begin
- Str := Str + ' ' + S;
- p := Tmp;
- end;
- end;
-
- function GetSQLToken(var Str: string): TSQLToken;
- var
- l: PChar;
- s: string;
- begin
- if Length(Str) = 0 then
- Result := stEnd else
- if (Str = '*') and (CurSection = stSelect) then
- Result := stAllFields else
- if DotStart then
- Result := stFieldName else
- if (AnsiCompareText('DISTINCT', Str) = 0) and (CurSection = stSelect) then
- Result := stDistinct else
- if (AnsiCompareText('ASC', Str) = 0) or (AnsiCompareText('ASCENDING', Str) = 0)then
- Result := stAscending else
- if (AnsiCompareText('DESC', Str) = 0) or (AnsiCompareText('DESCENDING', Str) = 0)then
- Result := stDescending else
- if AnsiCompareText('SELECT', Str) = 0 then
- Result := stSelect else
- if AnsiCompareText('AND', Str) = 0 then
- Result := stAnd else
- if AnsiCompareText('OR', Str) = 0 then
- Result := stOr else
- if AnsiCompareText('LIKE', Str) = 0 then
- Result := stLike else
- if (AnsiCompareText('IS', Str) = 0) then
- begin
- if NextTokenIs('NULL', Str) then
- Result := stIsNull else
- begin
- l := p;
- s := Str;
- if NextTokenIs('NOT', Str) and NextTokenIs('NULL', Str) then
- Result := stIsNotNull else
- begin
- p := l;
- Str := s;
- Result := stValue;
- end;
- end;
- end else
- if AnsiCompareText('FROM', Str) = 0 then
- Result := stFrom else
- if AnsiCompareText('WHERE', Str) = 0 then
- Result := stWhere else
- if (AnsiCompareText('GROUP', Str) = 0) and NextTokenIs('BY', Str) then
- Result := stGroupBy else
- if AnsiCompareText('HAVING', Str) = 0 then
- Result := stHaving else
- if AnsiCompareText('UNION', Str) = 0 then
- Result := stUnion else
- if AnsiCompareText('PLAN', Str) = 0 then
- Result := stPlan else
- if (AnsiCompareText('FOR', Str) = 0) and NextTokenIs('UPDATE', Str) then
- Result := stForUpdate else
- if (AnsiCompareText('ORDER', Str) = 0) and NextTokenIs('BY', Str) then
- Result := stOrderBy else
- if AnsiCompareText('NULL', Str) = 0 then
- Result := stValue else
- if CurSection = stFrom then
- Result := stTableName else
- Result := stFieldName;
- end;
-
- var
- TokenStart: PChar;
-
- procedure StartToken;
- begin
- if not Assigned(TokenStart) then
- TokenStart := p;
- end;
-
- var
- Literal: Char;
- Mark: PChar;
- begin
- TokenStart := nil;
- DotStart := False;
- while True do
- begin
- case p^ of
- '"','''','`':
- begin
- StartToken;
- Literal := p^;
- Mark := p;
- repeat Inc(p) until (p^ in [Literal,#0]);
- if p^ = #0 then
- begin
- p := Mark;
- Inc(p);
- end else
- begin
- Inc(p);
- SetString(Token, TokenStart, p - TokenStart);
- Mark := PChar(Token);
- Token := AnsiExtractQuotedStr(Mark, Literal);
- if DotStart then
- Result := stFieldName else
- if p^ = '.' then
- Result := stTableName else
- Result := stValue;
- Exit;
- end;
- end;
- '/':
- begin
- StartToken;
- Inc(p);
- if p^ in ['/','*'] then
- begin
- if p^ = '*' then
- begin
- repeat Inc(p) until (p = #0) or ((p^ = '*') and (p[1] = '/'));
- end else
- while not (p^ in [#0, #10, #13]) do Inc(p);
- SetString(Token, TokenStart, p - TokenStart);
- Result := stComment;
- Exit;
- end;
- end;
- ' ', #10, #13, ',':
- begin
- if Assigned(TokenStart) then
- begin
- SetString(Token, TokenStart, p - TokenStart);
- Result := GetSQLToken(Token);
- Exit;
- end else
- while (p^ in [' ', #10, #13, ',']) do Inc(p);
- end;
- '.':
- begin
- if Assigned(TokenStart) then
- begin
- SetString(Token, TokenStart, p - TokenStart);
- Result := stTableName;
- Exit;
- end else
- begin
- DotStart := True;
- Inc(p);
- end;
- end;
- '=','<','>':
- begin
- if not Assigned(TokenStart) then
- begin
- TokenStart := p;
- while p^ in ['=','<','>'] do Inc(p);
- SetString(Token, TokenStart, p - TokenStart);
- Result := stPredicate;
- Exit;
- end;
- Inc(p);
- end;
- '0'..'9':
- begin
- if not Assigned(TokenStart) then
- begin
- TokenStart := p;
- while p^ in ['0'..'9','.'] do Inc(p);
- SetString(Token, TokenStart, p - TokenStart);
- Result := stNumber;
- Exit;
- end else
- Inc(p);
- end;
- #0:
- begin
- if Assigned(TokenStart) then
- begin
- SetString(Token, TokenStart, p - TokenStart);
- Result := GetSQLToken(Token);
- Exit;
- end else
- begin
- Result := stEnd;
- Token := '';
- Exit;
- end;
- end;
- else
- StartToken;
- Inc(p);
- end;
- end;
- end;
-
- function GetTableNameFromSQL(const SQL: string): string;
- var
- Start: PChar;
- Token: string;
- SQLToken, CurSection: TSQLToken;
- begin
- Result := '';
- Start := PChar(SQL);
- CurSection := stUnknown;
- repeat
- SQLToken := NextSQLToken(Start, Token, CurSection);
- if SQLToken in SQLSections then CurSection := SQLToken;
- until SQLToken in [stEnd, stFrom];
- if SQLToken = stFrom then
- begin
- repeat
- SQLToken := NextSQLToken(Start, Token, CurSection);
- if SQLToken in SQLSections then
- CurSection := SQLToken else
- if SQLToken = stTableName then
- begin
- Result := Token;
- Exit;
- end;
- until (CurSection <> stFrom) or (SQLToken in [stEnd, stTableName]);
- end;
- end;
-
- function GetIndexForOrderBy(const SQL: string; DataSet: TDataSet): TIndexDef;
-
- function AddField(const Fields, NewField: string): string;
- begin
- Result := Fields;
- if Fields <> '' then
- Result := Fields + ';' + NewField else
- Result := NewField;
- end;
-
- var
- Start: PChar;
- Token, LastField: string;
- SQLToken, CurSection: TSQLToken;
- FieldIndex: Integer;
- begin
- Result := nil;
- Start := PChar(SQL);
- CurSection := stUnknown;
- repeat
- SQLToken := NextSQLToken(Start, Token, CurSection);
- if SQLToken in SQLSections then CurSection := SQLToken;
- until SQLToken in [stEnd, stOrderBy];
- if SQLToken = stOrderBy then
- begin
- Result := TIndexDef.Create(nil);
- try
- LastField := '';
- repeat
- SQLToken := NextSQLToken(Start, Token, CurSection);
- if SQLToken in SQLSections then
- CurSection := SQLToken else
- case SQLToken of
- stTableName: ;
- stFieldName:
- begin
- LastField := Token;
- Result.Fields := AddField(Result.Fields, LastField);
- end;
- stAscending: ;
- stDescending:
- Result.DescFields := AddField(Result.DescFields, LastField);
- stNumber:
- begin
- FieldIndex := StrToInt(Token);
- if DataSet.FieldCount >= FieldIndex then
- LastField := DataSet.Fields[FieldIndex - 1].FieldName else
- if DataSet.FieldDefs.Count >= FieldIndex then
- LastField := DataSet.FieldDefs[FieldIndex - 1].Name else
- SysUtils.Abort;
- Result.Fields := AddField(Result.Fields, LastField);
- end;
- end;
- until (CurSection <> stOrderBy) or (SQLToken = stEnd);
- except
- Result.Free;
- Result := nil;
- end;
- end;
- end;
-
- function GetFieldInfo(const Origin: string; var FieldInfo: TFieldInfo): 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, ftGuid];
- BlobFieldTypes = [ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle,
- ftTypedBinary, ftOraBlob, ftOraClob];
-
- 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: TParserOptions; 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): TExprData;
- 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: TCANOperator;
- 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: TExprNodeKind; Operator: TCANOperator;
- 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
- if VarType(Value) = varString then
- C := StrToCurr(string(TVarData(Value).VString)) else
- C := Value;
- CurrToBCD(C, BCD, 32, Decimals);
- Result := PutConstNode(ftBCD, @BCD, 18);
- 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: Double;
- begin
- if VarType(Value) = varString then
- F := StrToFloat(string(TVarData(Value).VString)) else
- F := Value;
- Result := PutConstNode(ftFloat, @F, SizeOf(Double));
- end;
-
- function TFilterExpr.PutConstInt(DataType: TFieldType;
- const Value: Variant): Integer;
- var
- I, Size: Integer;
- begin
- if VarType(Value) = varString then
- I := StrToInt(string(TVarData(Value).VString)) else
- I := Value;
- Size := 2;
- case DataType of
- ftSmallint:
- if (I < -32768) or (I > 32767) then DatabaseError(SExprRangeError);
- ftWord:
- if (I < 0) or (I > 65535) then DatabaseError(SExprRangeError);
- else
- Size := 4;
- 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.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.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, ftInteger, ftWord, ftAutoInc:
- Result := PutConstInt(Node^.FDataType, Node^.FData);
- ftFloat, ftCurrency:
- Result := PutConstFloat(Node^.FData);
- ftString, ftWideString, ftFixedChar, ftGuid:
- Result := PutConstStr(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);
- else
- DatabaseErrorFmt(SExprBadConst, [Node^.FData]);
- end;
- end;
-
- function TFilterExpr.PutExprNode(Node: PExprNode; ParentOp: TCANOperator): Integer;
- const
- ReverseOperator: array[coEQ..coLE] of TCANOperator = (coEQ, coNE, coLT,
- coGT, coLE, coGE);
- BoolFalse: WordBool = False;
- var
- Field: TField;
- Left, Right, Temp : PExprNode;
- LeftPos, RightPos, ListElem, PrevListElem, I: Integer;
- Operator: TCANOperator;
- CaseInsensitive, PartialLength, L: Integer;
- S: string;
- 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
- S := 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;
- begin
- if poFieldNameGiven in FParserOptions then
- FDataSet.Translate(PChar(Field.FieldName), 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: NodeClass; OpType: TCANOperator;
- 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.nodeClass }
- 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: TFieldInfo;
- 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 GetFieldInfo(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: TParserOptions; 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: TParserOptions; 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 := (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 TCANOperator = (
- coEQ, coNE, coGE, coLE, coGT, coLT);
- var
- Operator: TCANOperator;
- 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 TCANOperator = (
- coADD, coSUB, coMUL, coDIV);
- var
- Operator: TCANOperator;
- 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 TCANOperator = (
- coADD, coSUB, coMUL, coDIV);
- var
- Operator: TCANOperator;
- 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, System.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.
-