⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gasqlparserhelperclasses.pas

📁 一个sql语法分析程序
💻 PAS
📖 第 1 页 / 共 2 页
字号:
{*******************************************************}
{                                                       }
{       Advanced SQL statement parser                   }
{       Copyright (c) 2001 AS Gaiasoft                  }
{       Created by Gert Kello                           }
{                                                       }
{*******************************************************}

unit gaSQLParserHelperClasses;

interface

uses
  gaAdvancedSQLParser, gaBasicSQLParser;

type
  TgaSQLStatementPart = class (TgaSQLTokenList)
  private
    FIsInvalid: Boolean;
    FParseComplete: Boolean;
    function GetParseComplete: Boolean;
    procedure SetIsInvalid(const Value: Boolean);
    procedure SetParseComplete(const Value: Boolean);
  protected
    procedure CheckModifyAllowed; virtual;
    function GetAsString: string; override;
    function InternalGetParseComplete: Boolean; virtual;
    procedure InternalSetParseComplete; virtual;
    procedure SetAsString(const Value: string); virtual;
  public
    constructor Create(AOwnerStatement: TgaCustomSQLStatement); override;
    property AsString read GetAsString write SetAsString;
    property IsInvalid: Boolean read FIsInvalid write SetIsInvalid;
    property ParseComplete: Boolean read GetParseComplete write 
            SetParseComplete;
  end;
  
  TgaSQLExpression = class (TgaSQLStatementPart)
  private
    FIsCorrect: Boolean;
    FIsExpression: Boolean;
    FOldOnStatementAdd: TgaTokenEvent;
    FParenCount: Integer;
    function GetCanParseEnd: Boolean;
    function GetExpression: string;
  protected
    procedure AddParen(AToken: TgaSQLTokenObj); virtual;
    procedure ParseExpression(AToken: TgaSQLTokenObj); virtual;
    procedure RemoveParen(AToken: TgaSQLTokenObj); virtual;
    procedure SetIsExpression(const Value: Boolean);
    property CanParseEnd: Boolean read GetCanParseEnd;
    property ParenCount: Integer read FParenCount write FParenCount;
  public
    constructor Create(AOwnerStatement: TgaCustomSQLStatement); override;
    procedure ExecuteTokenAdded(Sender: TObject; AToken: TgaSQLTokenObj); 
            override;
    property Expression: string read GetExpression;
    property IsCorrect: Boolean read FIsCorrect;
    property IsExpression: Boolean read FIsExpression;
  end;
  
  TgaSQLStatementPartClass = class of TgaSQLStatementPart;

  TFieldExprParseState = (fepsNoSymbolsParsed, fepsFieldExpression,
    fepsFieldAlias, fepsExpression, fepsFieldAliasParsed, fepsFieldParseFinished);
  TgaSQLField = class (TgaSQLExpression)
  private
    FFieldAlias: TgaSQLTokenListBookmark;
    FFieldName: TgaSQLTokenListBookmark;
    FFieldParseState: TFieldExprParseState;
    FFieldPrefixies: TgaSQLTokenList;
    function GetFieldAlias: string;
    function GetFieldName: string;
    function GetFieldPrefix: string;
    procedure SetFieldAlias(const Value: string);
    procedure SetFieldName(const Value: string);
    procedure SetFieldPrefix(const Value: string);
  protected
    procedure InternalSetParseComplete; override;
    procedure ParseFieldAlias(AToken: TgaSQLTokenObj);
    procedure ParseFieldExpression(AToken: TgaSQLTokenObj);
    procedure ParsePreFieldExpression(AToken: TgaSQLTokenObj);
    procedure StartExpressionParse;
    property FieldParseState: TFieldExprParseState read FFieldParseState;
  public
    constructor Create(AOwnerStatement: TgaCustomSQLStatement); override;
    destructor Destroy; override;
    procedure ExecuteTokenAdded(Sender: TObject; AToken: TgaSQLTokenObj); 
            override;
    property FieldAlias: string read GetFieldAlias write SetFieldAlias;
    property FieldAliasToken: TgaSQLTokenListBookmark read FFieldAlias;
    property FieldName: string read GetFieldName write SetFieldName;
    property FieldNameToken: TgaSQLTokenListBookmark read FFieldName;
    property FieldPrefix: string read GetFieldPrefix write SetFieldPrefix;
    property FieldPrefixies: TgaSQLTokenList read FFieldPrefixies;
  end;
  
  TTableExprParseState = (tepsNoSymbolsParsed, tepsTableName, tepsTableAlias,
    tepsTableAliasParsed, tepsTableParseFinished);
  TgaSQLTable = class (TgaSQLStatementPart)
  private
    FIsAliasAllowed: Boolean;
    FTableAlias: TgaSQLTokenListBookmark;
    FTableName: TgaSQLTokenListBookmark;
    FTableParseState: TTableExprParseState;
    FTablePrefixies: TgaSQLTokenList;
    function GetTableAlias: string;
    function GetTableName: string;
    function GetTablePrefix: string;
    procedure SetTableAlias(const Value: string);
    procedure SetTableName(const Value: string);
    procedure SetTablePrefix(const Value: string);
  protected
    procedure InternalSetParseComplete; override;
    procedure ParsePreTableName(AToken: TgaSQLTokenObj);
    procedure ParseTableAlias(AToken: TgaSQLTokenObj);
    procedure ParseTableName(AToken: TgaSQLTokenObj);
    property TableParseState: TTableExprParseState read FTableParseState write 
            FTableParseState;
  public
    constructor Create(AOwnerStatement: TgaCustomSQLStatement); override;
    destructor Destroy; override;
    procedure ExecuteTokenAdded(Sender: TObject; AToken: TgaSQLTokenObj); 
            override;
    property IsAliasAllowed: Boolean read FIsAliasAllowed write FIsAliasAllowed;
    property TableAlias: string read GetTableAlias write SetTableAlias;
    property TableAliasToken: TgaSQLTokenListBookmark read FTableAlias;
    property TableName: string read GetTableName write SetTableName;
    property TableNameToken: TgaSQLTokenListBookmark read FTableName;
    property TablePrefix: string read GetTablePrefix write SetTablePrefix;
    property TablePrefixies: TgaSQLTokenList read FTablePrefixies;
  end;
  
  TgaSQLWhereExpression = class (TgaSQLExpression)
  protected
    function GetAsString: string; override;
    procedure SetAsString(const Value: string); override;
  end;
  
  TgaSQLStatementPartList = class (TgaListOfSQLTokenLists)
  private
    FCurrentPart: TgaSQLStatementPart;
    FOwnerStatement: TgaCustomSQLStatement;
    FParseComplete: Boolean;
    FStatementPartType: TgaSQLStatementPartClass;
    FTokenList: TgaSQLTokenList;
    function GetParseComplete: Boolean;
    procedure SetParseComplete(const Value: Boolean);
  protected
    function GetAsString: string; override;
    function GetCurrentPart: TgaSQLStatementPart; virtual;
    procedure InternalSetParseComplete; virtual;
    procedure SetAsString(const Value: string); virtual;
    property OwnerStatement: TgaCustomSQLStatement read FOwnerStatement;
    property StatementPartType: TgaSQLStatementPartClass read 
            FStatementPartType;
  public
    constructor Create(AOwnerStatement: TgaCustomSQLStatement; 
            AStatementPartType: TgaSQLStatementPartClass);
    destructor Destroy; override;
    procedure Clear; override;
    procedure ExecuteTokenAdded(Sender: TObject; AToken: TgaSQLTokenObj); 
            override;
    property AsString read GetAsString write SetAsString;
    property CurrentPart: TgaSQLStatementPart read GetCurrentPart;
    property ParseComplete: Boolean read GetParseComplete write 
            SetParseComplete;
    property TokenList: TgaSQLTokenList read FTokenList;
  end;
  
  TgaSQLFieldList = class (TgaSQLStatementPartList)
  public
    constructor Create(AOwnerStatement: TgaCustomSQLStatement);
  end;
  
  TgaSQLTableList = class (TgaSQLStatementPartList)
  public
    constructor Create(AOwnerStatement: TgaCustomSQLStatement);
  end;
  
  TgaSQLOrderByList = class (TgaSQLFieldList)
  protected
    function GetAsString: string; override;
    procedure SetAsString(const Value: string); override;
  end;
  
procedure ParseStringToTokens(const AString: string; ATokenList: TgaSQLTokenHolderList);

implementation

uses
  SysUtils, TypInfo, gaSQLParserConsts;

procedure ParseStringToTokens(const AString: string; ATokenList: TgaSQLTokenHolderList);
var
  lSQLParser: TgaBasicSQLParser;
  lTokenObj: TgaSQLTokenObj;
begin
  ATokenList.Clear;
  lSQLParser := TgaBasicSQLParser.Create(nil);
  try
    lSQLParser.SQLText.Text := AString;
    while lSQLParser.TokenType <> stEnd do
    begin
      lTokenObj := ATokenList.NewToken;
      lTokenObj.AssignTokenInfo(lSQLParser);
      lSQLParser.NextToken;
    end;
  finally
    lSQLParser.Free;
  end;
end;

{
********************************* TgaSQLTable **********************************
}
constructor TgaSQLTable.Create(AOwnerStatement: TgaCustomSQLStatement);
begin
  inherited;
  FIsAliasAllowed := True;
  OwnerStatement.AddTable(Self);
end;

destructor TgaSQLTable.Destroy;
begin
  OwnerStatement.RemoveTable(Self);
  FTableAlias.Free;
  FTableName.Free;
  FTablePrefixies.Free;
  inherited Destroy;
end;

procedure TgaSQLTable.ExecuteTokenAdded(Sender: TObject; AToken: 
        TgaSQLTokenObj);
begin
  inherited;
  if AToken.TokenType = stComment then
    Exit;
  case TableParseState of
    tepsNoSymbolsParsed:
      ParsePreTableName(AToken);
    tepsTableName:
      ParseTableName(AToken);
    tepsTableAlias:
      ParseTableAlias(AToken);
    tepsTableAliasParsed:
      if not (AToken.TokenType in [stDelimitier, stComma, stEnd]) then
        IsInvalid := True;
    tepsTableParseFinished:
      raise Exception.Create(SerrTableParseFinised);
    { fepsExpression:
      Shouldn't get here as IsExpression should be true then }
    else
      raise Exception.CreateFmt(SerrUnecpectedFieldParseState,
        [GetEnumName(TypeInfo(TTableExprParseState), Ord(TableParseState))]);
  end;
  if (AToken.TokenType = stComma) then
    ParseComplete := True;
end;

function TgaSQLTable.GetTableAlias: string;
begin
  if Assigned(FTableAlias) then
    Result := GetTokenObjAsString(FTableAlias.TokenObj)
  else
    Result := EmptyStr;
end;

function TgaSQLTable.GetTableName: string;
begin
  if Assigned(FTableName) then
    Result := GetTokenObjAsString(FTableName.TokenObj)
  else
    Result := EmptyStr;
end;

function TgaSQLTable.GetTablePrefix: string;
begin
  Result := TablePrefixies.AsString;
end;

procedure TgaSQLTable.InternalSetParseComplete;
begin
  inherited;
  TableParseState := tepsTableParseFinished;
end;

procedure TgaSQLTable.ParsePreTableName(AToken: TgaSQLTokenObj);
begin
  case AToken.TokenType of
    stSymbol, stQuotedSymbol, stPeriod:
    begin
      FTablePrefixies := TgaSQLTokenList.CreateMirror(OwnerStatement, OwnerStatement.CurrentSQL);
      FTablePrefixies.SetStartPos(OwnerStatement.CurrentSQL, True);
      FTableParseState := tepsTableName;
    end;
    stDelimitier:
      {skip leading delimitiers};
    else
      IsInvalid := True;
  end;
end;

procedure TgaSQLTable.ParseTableAlias(AToken: TgaSQLTokenObj);
begin
  case AToken.TokenType of
    stDelimitier:
      {no special processing};
    stSymbol, stQuotedSymbol, stString:
      if not AToken.TokenSymbolIs('AS') then
      begin
        FTableAlias := GetBookmark;
        FTableParseState := tepsTableAliasParsed;
      end;
    stOther:
      if AToken.TokenString = ';' then
        FTableParseState := tepsTableAliasParsed
      else
        IsInvalid := True;
    else
      IsInvalid := True;
  end;
  
end;

procedure TgaSQLTable.ParseTableName(AToken: TgaSQLTokenObj);
begin
  case AToken.TokenType of
    stSymbol, stQuotedSymbol, stPeriod:
      ; { Do nothing as added tokens are inserted at the end of the prefixies
          list automatically }
    stDelimitier, stComma:
    begin
      FTablePrefixies.Last;
      // last one should be the current comma or delimitier
      FTablePrefixies.Previous;
      if FTablePrefixies.CurrentItem.TokenType <> stPeriod then
      begin
        Locate(FTablePrefixies.CurrentItem);
        FTableName := GetBookmark;
        FTablePrefixies.Previous;
      end;
      FTablePrefixies.SetEndPos(FTablePrefixies, True);
      if IsAliasAllowed then
        FTableParseState := tepsTableAlias
      else
        FTableParseState := tepsTableAliasParsed;
    end;
    stOther:
      if AToken.TokenString = ';' then
        FTableParseState := tepsTableAliasParsed
      else
        IsInvalid := True;
    else
      IsInvalid := True;
  end;
end;

procedure TgaSQLTable.SetTableAlias(const Value: string);
var
  tmpTokenList: TgaSQLTokenHolderList;
  tmpToken: TgaSQLTokenObj;
begin
  if TableAlias <> Value then
  begin
    CheckModifyAllowed;
    if (not IsAliasAllowed) and (Value <> EmptyStr) then
      raise Exception.Create(SerrTableAliasNotAllowed);
    if not Assigned(FTableName) then
      raise Exception.Create(SerrNoTableForAlias);
    tmpTokenList := TgaSQLTokenHolderList.Create(nil);
    try
      ParseStringToTokens(Value, tmpTokenList);
      TrimTokenList(tmpTokenList, True);
      if tmpTokenList.Count > 1 then
        raise Exception.CreateFmt(SerrWrongTokenCountInArg, ['Table alias', 1, tmpTokenList.Count]);
      tmpTokenList.First;
      if Assigned(FTableAlias) then
        FTableAlias.TokenObj := tmpTokenList.CurrentItem
      else begin
        tmpToken := TgaSQLTokenObj.Create;
        tmpToken.SetTokenInfo(' ', stDelimitier, False, #0);
        GotoBookmark(FTableName);
        InsertAfterCurrent(tmpToken, True);
        InsertAfterCurrent(tmpTokenList.CurrentItem, True);
        FTableAlias := GetBookmark;
      end;
    finally
      tmpTokenList.Free;
    end;
  end;
end;

procedure TgaSQLTable.SetTableName(const Value: string);
var
  tmpTokenList: TgaSQLTokenHolderList;
begin
  if TableName <> Value then
  begin
    CheckModifyAllowed;
    tmpTokenList := TgaSQLTokenHolderList.Create(nil);
    try
      ParseStringToTokens(Value, tmpTokenList);
      TrimTokenList(tmpTokenList, True);
      if tmpTokenList.Count <> 1 then
        raise Exception.CreateFmt(SerrWrongTokenCountInArg, ['Table name', 1, tmpTokenList.Count]);
      tmpTokenList.First;
      FTableName.TokenObj := tmpTokenList.CurrentItem;
    finally
      tmpTokenList.Free;
    end;
  end;
end;

procedure TgaSQLTable.SetTablePrefix(const Value: string);
var
  tmpStr: string;
  tmpTokenList: TgaSQLTokenHolderList;
begin
  if Value[Length(Value)] = '.' then
    tmpStr := Value
  else
    tmpStr := Value + '.';
  if (TablePrefix <> tmpStr) then
  begin
    CheckModifyAllowed;
    tmpTokenList := TgaSQLTokenHolderList.Create(nil);
    try
      ParseStringToTokens(tmpStr, tmpTokenList);
      TrimTokenList(tmpTokenList, True);
      TablePrefixies.CopyListContest(tmpTokenList);
    finally
      tmpTokenList.Free;
    end;
  end;
end;

{
**************************** TgaSQLWhereExpression *****************************
}
function TgaSQLWhereExpression.GetAsString: string;
begin
  Result := EmptyStr;
  if IsEmpty then
    Exit;
  First;
  if CurrentItem.TokenSymbolIs('WHERE') then
  begin
    Next;
    while (not Eof) and (CurrentItem.TokenType = stDelimitier) do
      Next;
  end;
  while not Eof do
  begin
    Result := Result + CurrentItem.TokenAsString;
    Next;
  end;
end;

procedure TgaSQLWhereExpression.SetAsString(const Value: string);
var
  tmpStr: string;
  tmpToken: TgaSQLTokenObj;
begin
  if (Trim(Value) = '') or (SameText('where ', Copy(Value, 1, 6))) then
    tmpStr := Value
  else
    tmpStr := 'where ' + Value;
  inherited SetAsString(tmpStr);
  Last;
  if CurrentItem.TokenType <> stDelimitier then
  begin
    tmpToken := TgaSQLTokenObj.Create;
    tmpToken.SetTokenInfo(#13#10, stDelimitier, False, #0);
    Add(tmpToken);
    ExecuteTokenAdded(Self, tmpToken);
  end;
end;

{
********************************* TgaSQLField **********************************
}
constructor TgaSQLField.Create(AOwnerStatement: TgaCustomSQLStatement);
begin
  inherited;
  FFieldPrefixies := TgaSQLTokenList.CreateMirror(AOwnerStatement, Self);
  SetIsExpression(False);
  OwnerStatement.AddField(Self);
end;

destructor TgaSQLField.Destroy;
begin
  if Assigned(OwnerStatement) then
    OwnerStatement.RemoveField(Self);
  FFieldAlias.Free;
  FFieldName.Free;
  FFieldPrefixies.Free;
  inherited Destroy;
end;

procedure TgaSQLField.ExecuteTokenAdded(Sender: TObject; AToken: 
        TgaSQLTokenObj);
begin
  inherited;
  { Field is written in SQL as follows:
    [table alias][.]FieldName[whitespace][FieldAlias],}
  if AToken.TokenType = stComment then
    Exit;
  if not IsExpression then
  begin
    case FieldParseState of
      fepsNoSymbolsParsed:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -