📄 dbcommon.pas
字号:
{ *************************************************************************** }
{ }
{ Kylix and Delphi Cross-Platform Visual Component Library }
{ }
{ Copyright (c) 1995, 2001 Borland Software Corporation }
{ }
{ *************************************************************************** }
unit DBCommon;
{$T-,H+,X+,R-}
interface
{$IFDEF MSWINDOWS}
uses Windows, Variants, Classes, DB, SqlTimSt;
{$ENDIF}
{$IFDEF LINUX}
uses Libc, Variants, Classes, DB, SqlTimSt;
{$ENDIF}
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 PutConstFMTBCD(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 PutConstSQLTimeStamp(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
FDecimalSeparator: Char;
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;
function GetTableNameFromQuery(const SQL: string): string;
function AddParamSQLForDetail(Params: TParams; SQL: string; Native: Boolean; QuoteChar: string = ''): string;
function IsMultiTableQuery(const SQL: string): Boolean;
implementation
uses SysUtils, DBConsts, FMTBcd;
{ 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -