Browse Source

sql parser: support A.* syntax

git-svn-id: trunk@46436 -
ondrej 5 years ago
parent
commit
5bac4c25e0

+ 33 - 11
packages/fcl-db/src/sql/fpsqlparser.pas

@@ -435,7 +435,9 @@ procedure TSQLParser.ParseSelectFieldList(AParent: TSQLSelectStatement;
   AList: TSQLElementList; Singleton: Boolean);
   AList: TSQLElementList; Singleton: Boolean);
 Var
 Var
   F : TSQLSelectField;
   F : TSQLSelectField;
+  A : TSQLSelectAsterisk;
   B : Boolean;
   B : Boolean;
+  Expression : TSQLExpression;
 
 
 begin
 begin
   // On entry, we're on the token preceding the field list.
   // On entry, we're on the token preceding the field list.
@@ -479,18 +481,20 @@ begin
         end;
         end;
       B:=False;
       B:=False;
       end;
       end;
-    If (CurrentToken=tsqlMul) then
+    Expression:=ParseExprLevel1(AParent,[eoSelectvalue]);
+    if Expression is TSQLAsteriskExpression then
       begin
       begin
       If Singleton then
       If Singleton then
         Error(SErrNoAsteriskInSingleTon);
         Error(SErrNoAsteriskInSingleTon);
-      AList.Add(CreateElement(TSQLSelectAsterisk,AParent));
-      GetNextToken;
+      A:=TSQLSelectAsterisk(CreateElement(TSQLSelectAsterisk,AParent));
+      AList.Add(A);
+      A.Expression:=TSQLAsteriskExpression(Expression);
       end
       end
     else
     else
       begin
       begin
       F:=TSQLSelectField(CreateElement(TSQLSelectField,AParent));
       F:=TSQLSelectField(CreateElement(TSQLSelectField,AParent));
       AList.Add(F);
       AList.Add(F);
-      F.Expression:=ParseExprLevel1(AParent,[eoSelectvalue]);
+      F.Expression:=Expression;
       If CurrentToken in [tsqlAs,Tsqlidentifier] then
       If CurrentToken in [tsqlAs,Tsqlidentifier] then
         begin
         begin
         If currentToken=tsqlAs then
         If currentToken=tsqlAs then
@@ -2741,6 +2745,7 @@ Var
   N : String;
   N : String;
   C : TSQLElementClass;
   C : TSQLElementClass;
   E : TSQLExtractElement;
   E : TSQLExtractElement;
+  IdentifierPath : TSQLIdentifierPath;
 
 
 begin
 begin
   Result:=Nil;
   Result:=Nil;
@@ -2852,6 +2857,11 @@ begin
         TSQLParameterExpression(Result).Identifier:=CreateIdentifier(Result,N);
         TSQLParameterExpression(Result).Identifier:=CreateIdentifier(Result,N);
         Consume(tsqlIdentifier);
         Consume(tsqlIdentifier);
         end;
         end;
+      tsqlMUL:
+        begin
+        Result:=TSQLAsteriskExpression(CreateElement(TSQLAsteriskExpression,APArent));
+        GetNextToken;
+        end;
       tsqlIdentifier:
       tsqlIdentifier:
         begin
         begin
         N:=CurrentTokenString;
         N:=CurrentTokenString;
@@ -2860,18 +2870,30 @@ begin
           If (eoCheckConstraint in EO) and not (eoTableConstraint in EO) then
           If (eoCheckConstraint in EO) and not (eoTableConstraint in EO) then
             Error(SErrUnexpectedToken,[CurrentTokenString]);
             Error(SErrUnexpectedToken,[CurrentTokenString]);
           // Plain identifier
           // Plain identifier
-          Result:=TSQLIdentifierExpression(CreateElement(TSQLIdentifierExpression,APArent));
-          TSQLIdentifierExpression(Result).IdentifierPath.Add(CreateIdentifier(Result,N));
+          IdentifierPath:=TSQLIdentifierPath.Create;
+          IdentifierPath.Add(CreateIdentifier(Result,N));
           while (CurrentToken=tsqlDot) do
           while (CurrentToken=tsqlDot) do
             begin
             begin
             GetNextToken;
             GetNextToken;
-            Expect(tsqlIdentifier);
-            N:=CurrentTokenString;
-            TSQLIdentifierExpression(Result).IdentifierPath.Add(CreateIdentifier(Result,N));
-            GetNextToken;
+            if CurrentToken=tsqlMUL then
+              begin
+              Result:=TSQLAsteriskExpression(CreateElement(TSQLAsteriskExpression,APArent));
+              GetNextToken;
+              break;
+              end
+            else
+              begin
+              Expect(tsqlIdentifier);
+              N:=CurrentTokenString;
+              IdentifierPath.Add(CreateIdentifier(Result,N));
+              GetNextToken;
+              end;
             end;
             end;
+          if not Assigned(Result) then
+            Result:=TSQLIdentifierExpression(CreateElement(TSQLIdentifierExpression,APArent));
+          TSQLIdentifierPathExpression(Result).IdentifierPath:=IdentifierPath;
           // Array access ?
           // Array access ?
-          If (CurrentToken=tsqlSquareBraceOpen) then
+          If (CurrentToken=tsqlSquareBraceOpen) and (Result is TSQLIdentifierExpression) then
             // Either something like array[5] or,
             // Either something like array[5] or,
             // in procedures etc array[i:] where i is a variable
             // in procedures etc array[i:] where i is a variable
             begin
             begin

+ 112 - 35
packages/fcl-db/src/sql/fpsqltree.pp

@@ -195,24 +195,39 @@ Type
     Property Identifiers[AIndex : Integer] : TSQLIdentifierName Read GetI Write SetI; default;
     Property Identifiers[AIndex : Integer] : TSQLIdentifierName Read GetI Write SetI; default;
   end;
   end;
 
 
+  { TSQLIdentifierPathExpression }
+
+  TSQLIdentifierPathExpression = Class(TSQLExpression)
+  private
+    FIdentifierPath: TSQLIdentifierPath;
+  Public
+    Destructor Destroy; override;
+    Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
+    Property IdentifierPath: TSQLIdentifierPath Read FIdentifierPath Write FIdentifierPath;
+  end;
+
   { TSQLIdentifierExpression }
   { TSQLIdentifierExpression }
 
 
-  TSQLIdentifierExpression = Class(TSQLExpression)
+  TSQLIdentifierExpression = Class(TSQLIdentifierPathExpression)
   private
   private
     FElementIndex: Integer;
     FElementIndex: Integer;
-    FIdentifierPath: TSQLIdentifierPath;
     function GetIdentifier: TSQLIdentifierName;
     function GetIdentifier: TSQLIdentifierName;
     procedure SetIdentifier(const AName: TSQLIdentifierName);
     procedure SetIdentifier(const AName: TSQLIdentifierName);
   Public
   Public
     Constructor Create(AParent : TSQLElement); override;
     Constructor Create(AParent : TSQLElement); override;
-    Destructor Destroy; override;
     Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
     Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
     Property Identifier : TSQLIdentifierName Read GetIdentifier Write SetIdentifier;
     Property Identifier : TSQLIdentifierName Read GetIdentifier Write SetIdentifier;
-    Property IdentifierPath: TSQLIdentifierPath Read FIdentifierPath;
     // For array types: index of element in array
     // For array types: index of element in array
     Property ElementIndex : Integer Read FElementIndex Write FElementIndex;
     Property ElementIndex : Integer Read FElementIndex Write FElementIndex;
   end;
   end;
 
 
+  { TSQLAsteriskExpression }
+
+  TSQLAsteriskExpression = Class(TSQLIdentifierPathExpression)
+  Public
+    Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
+  end;
+
   { TSQLParameterExpression }
   { TSQLParameterExpression }
 
 
   TSQLParameterExpression = Class(TSQLExpression)
   TSQLParameterExpression = Class(TSQLExpression)
@@ -597,19 +612,33 @@ Type
 
 
   { TSelectField }
   { TSelectField }
 
 
-  TSQLSelectElement = Class(TSQLElement);
-  TSQLSelectAsterisk = Class(TSQLSelectElement);
+  TSQLSelectElement = Class(TSQLElement)
+  private
+    FExpression: TSQLExpression;
+  Public
+    Destructor Destroy; override;
+    Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
+    Property Expression : TSQLExpression Read FExpression Write FExpression;
+  end;
+
+  { TSQLSelectAsterisk }
+
+  TSQLSelectAsterisk = Class(TSQLSelectElement)
+  private
+    function GetExpression: TSQLAsteriskExpression;
+    procedure SetExpression(const AExpression: TSQLAsteriskExpression);
+  Public
+    Property Expression : TSQLAsteriskExpression Read GetExpression Write SetExpression;
+  end;
 
 
   { TSQLSelectField }
   { TSQLSelectField }
 
 
   TSQLSelectField = Class(TSQLSelectElement)
   TSQLSelectField = Class(TSQLSelectElement)
   private
   private
     FAliasName: TSQLIdentifierName;
     FAliasName: TSQLIdentifierName;
-    FExpression: TSQLExpression;
   Public
   Public
     Destructor Destroy; override;
     Destructor Destroy; override;
     Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
     Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
-    Property Expression : TSQLExpression Read FExpression Write FExpression;
     Property AliasName : TSQLIdentifierName Read FAliasName Write FAliasName;
     Property AliasName : TSQLIdentifierName Read FAliasName Write FAliasName;
   end;
   end;
 
 
@@ -1972,6 +2001,73 @@ begin
     Sep:=', ';
     Sep:=', ';
 end;
 end;
 
 
+{ TSQLAsteriskExpression }
+
+function TSQLAsteriskExpression.GetAsSQL(Options: TSQLFormatOptions; AIndent: Integer): TSQLStringType;
+begin
+  Result := inherited GetAsSQL(Options, AIndent);
+  if Result<>'' then
+    Result:=Result+'.';
+  Result:=Result+'*';
+end;
+
+{ TSQLIdentifierExpression }
+
+constructor TSQLIdentifierExpression.Create(AParent: TSQLElement);
+begin
+  inherited Create(AParent);
+  FElementIndex:=-1;
+end;
+
+function TSQLIdentifierExpression.GetAsSQL(Options: TSQLFormatOptions; AIndent: Integer): TSQLStringType;
+begin
+  Result := inherited GetAsSQL(Options, AIndent);
+  If (ElementIndex<>-1) then
+    Result:=Result+Format('[%d]',[Elementindex]);
+end;
+
+function TSQLIdentifierExpression.GetIdentifier: TSQLIdentifierName;
+begin
+  Result := TSQLIdentifierName(FIdentifierPath.Last);
+end;
+
+procedure TSQLIdentifierExpression.SetIdentifier(const AName: TSQLIdentifierName);
+begin
+  if Assigned(FIdentifierPath) then
+    FIdentifierPath.Clear
+  else
+    FIdentifierPath:=TSQLIdentifierPath.Create;
+  FIdentifierPath.Add(AName);
+end;
+
+{ TSQLSelectElement }
+
+destructor TSQLSelectElement.Destroy;
+begin
+  FreeAndNil(FExpression);
+  inherited Destroy;
+end;
+
+function TSQLSelectElement.GetAsSQL(Options: TSQLFormatOptions; AIndent: Integer): TSQLStringType;
+begin
+  If Assigned(FExpression) then
+    Result:=FExpression.GetAsSQL(Options)
+  Else
+    Result:='';
+end;
+
+{ TSQLSelectAsterisk }
+
+function TSQLSelectAsterisk.GetExpression: TSQLAsteriskExpression;
+begin
+  Result:=TSQLAsteriskExpression(inherited Expression)
+end;
+
+procedure TSQLSelectAsterisk.SetExpression(const AExpression: TSQLAsteriskExpression);
+begin
+  inherited Expression:=AExpression;
+end;
+
 { TSQLIdentifierPath }
 { TSQLIdentifierPath }
 
 
 function TSQLIdentifierPath.Add(AName: TSQLIdentifierName): Integer;
 function TSQLIdentifierPath.Add(AName: TSQLIdentifierName): Integer;
@@ -3245,15 +3341,13 @@ end;
 
 
 destructor TSQLSelectField.Destroy;
 destructor TSQLSelectField.Destroy;
 begin
 begin
-  FreeAndNil(FExpression);
   FreeAndNil(FAliasName);
   FreeAndNil(FAliasName);
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
 function TSQLSelectField.GetAsSQL(Options: TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType;
 function TSQLSelectField.GetAsSQL(Options: TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType;
 begin
 begin
-  If Assigned(FExpression) then
-    Result:=FExpression.GetAsSQL(Options);
+  Result := inherited GetAsSQL(Options, AIndent);
   If Assigned(FAliasName) then
   If Assigned(FAliasName) then
     Result:=Result+' AS '+FAliasName.GetAsSQL(Options);
     Result:=Result+' AS '+FAliasName.GetAsSQL(Options);
 end;
 end;
@@ -4289,37 +4383,20 @@ begin
   Result:=Result+sp+SQLKeyWord('MODULE_NAME ',Options)+SQLFormatString(ModuleName,Options);
   Result:=Result+sp+SQLKeyWord('MODULE_NAME ',Options)+SQLFormatString(ModuleName,Options);
 end;
 end;
 
 
-{ TSQLIdentifierExpression }
-
-constructor TSQLIdentifierExpression.Create(AParent: TSQLElement);
-begin
-  inherited Create(AParent);
-  FIdentifierPath:=TSQLIdentifierPath.Create;
-  FElementIndex:=-1;
-end;
+{ TSQLIdentifierPathExpression }
 
 
-destructor TSQLIdentifierExpression.Destroy;
+destructor TSQLIdentifierPathExpression.Destroy;
 begin
 begin
   FreeAndNil(FIdentifierPath);
   FreeAndNil(FIdentifierPath);
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
-function TSQLIdentifierExpression.GetAsSQL(Options: TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType;
-begin
-  Result := FIdentifierPath.GetAsSQL(Options, AIndent);
-  If (ElementIndex<>-1) then
-    Result:=Result+Format('[%d]',[Elementindex]);
-end;
-
-function TSQLIdentifierExpression.GetIdentifier: TSQLIdentifierName;
-begin
-  Result := TSQLIdentifierName(FIdentifierPath.Last);
-end;
-
-procedure TSQLIdentifierExpression.SetIdentifier(const AName: TSQLIdentifierName);
+function TSQLIdentifierPathExpression.GetAsSQL(Options: TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType;
 begin
 begin
-  FIdentifierPath.Clear;
-  FIdentifierPath.Add(AName);
+  if Assigned(FIdentifierPath) then
+    Result:=FIdentifierPath.GetAsSQL(Options, AIndent)
+  else
+    Result:='';
 end;
 end;
 
 
 { TSQLSelectExpression }
 { TSQLSelectExpression }

+ 12 - 0
packages/fcl-db/tests/tcparser.pas

@@ -400,6 +400,7 @@ type
     procedure TestSelectOneAllFieldOneTable;
     procedure TestSelectOneAllFieldOneTable;
     procedure TestSelectAsteriskOneTable;
     procedure TestSelectAsteriskOneTable;
     procedure TestSelectDistinctAsteriskOneTable;
     procedure TestSelectDistinctAsteriskOneTable;
+    procedure TestSelectAsteriskWithPath;
     procedure TestSelectOneFieldOneTableAlias;
     procedure TestSelectOneFieldOneTableAlias;
     procedure TestSelectOneFieldOneTableAsAlias;
     procedure TestSelectOneFieldOneTableAsAlias;
     procedure TestSelectTwoFieldsTwoTables;
     procedure TestSelectTwoFieldsTwoTables;
@@ -3993,6 +3994,17 @@ begin
   AssertTable(Select.Tables[0],'A');
   AssertTable(Select.Tables[0],'A');
 end;
 end;
 
 
+procedure TTestSelectParser.TestSelectAsteriskWithPath;
+begin
+  TestSelect('SELECT A.* FROM A');
+  AssertEquals('One field',1,Select.Fields.Count);
+  CheckClass(Select.Fields[0],TSQLSelectAsterisk);
+  AssertEquals('Path count = 1',1,TSQLSelectAsterisk(Select.Fields[0]).Expression.IdentifierPath.Count);
+  AssertEquals('Path table = A','A',TSQLSelectAsterisk(Select.Fields[0]).Expression.IdentifierPath[0].Name);
+  AssertEquals('One table',1,Select.Tables.Count);
+  AssertTable(Select.Tables[0],'A');
+end;
+
 procedure TTestSelectParser.TestSelectDistinctAsteriskOneTable;
 procedure TTestSelectParser.TestSelectDistinctAsteriskOneTable;
 begin
 begin
   TestSelect('SELECT DISTINCT * FROM A');
   TestSelect('SELECT DISTINCT * FROM A');