Browse Source

fcl-db: sqldb: when parsing identifiers (TableName) take into account delimited identifiers (double quoted) separated by point + test. Bug #34511

git-svn-id: trunk@40240 -
lacak 6 years ago
parent
commit
475fed4e9a
2 changed files with 47 additions and 12 deletions
  1. 13 12
      packages/fcl-db/src/sqldb/sqldb.pp
  2. 34 0
      packages/fcl-db/tests/testsqldb.pas

+ 13 - 12
packages/fcl-db/src/sqldb/sqldb.pp

@@ -1518,9 +1518,10 @@ end;
 
 function TSQLConnection.GetStatementInfo(const ASQL: string): TSQLStatementInfo;
 
-type TParsePart = (ppStart,ppWith,ppSelect,ppTableName,ppFrom,ppWhere,ppGroup,ppOrder,ppBogus);
-     TPhraseSeparator = (sepNone, sepWhiteSpace, sepComma, sepComment, sepParentheses, sepDoubleQuote, sepEnd);
-     TKeyword = (kwWITH, kwSELECT, kwINSERT, kwUPDATE, kwDELETE, kwFROM, kwJOIN, kwWHERE, kwGROUP, kwORDER, kwUNION, kwROWS, kwLIMIT, kwUnknown);
+type
+  TParsePart = (ppStart,ppWith,ppSelect,ppTableName,ppFrom,ppWhere,ppGroup,ppOrder,ppBogus);
+  TPhraseSeparator = (sepNone, sepWhiteSpace, sepComma, sepComment, sepParentheses, sepDoubleQuote, sepEnd);
+  TKeyword = (kwWITH, kwSELECT, kwINSERT, kwUPDATE, kwDELETE, kwFROM, kwJOIN, kwWHERE, kwGROUP, kwORDER, kwUNION, kwROWS, kwLIMIT, kwUnknown);
 
 const
   KeywordNames: array[TKeyword] of string =
@@ -1536,7 +1537,7 @@ var
   Keyword, K              : TKeyword;
 
 begin
-  PSQL:=Pchar(ASQL);
+  PSQL:=PChar(ASQL);
   ParsePart := ppStart;
 
   CurrentP := PSQL-1;
@@ -1548,7 +1549,6 @@ begin
   Result.WhereStopPos := 0;
 
   repeat
-    begin
     inc(CurrentP);
     SavedP := CurrentP;
 
@@ -1582,12 +1582,12 @@ begin
           Separator := sepNone;
     end;
 
-    if (CurrentP > SavedP) and (SavedP > PhraseP) then
-      CurrentP := SavedP;  // there is something before comment or left parenthesis
-
     if Separator <> sepNone then
       begin
-      if ((Separator in [sepWhitespace,sepComment]) and (PhraseP = SavedP)) then
+      if (CurrentP > SavedP) and (SavedP > PhraseP) then
+        CurrentP := SavedP;  // there is something before comment or left parenthesis or double quote
+
+      if (Separator in [sepWhitespace,sepComment]) and (SavedP = PhraseP) then
         PhraseP := CurrentP;  // skip comments (but not parentheses) and white spaces
 
       if (CurrentP-PhraseP > 0) or (Separator = sepEnd) then
@@ -1633,10 +1633,12 @@ begin
                      //  and/or derived tables are also not updateable
                      if Separator in [sepWhitespace, sepComment, sepDoubleQuote, sepEnd] then
                        begin
-                       Result.TableName := s;
+                       Result.TableName := Result.TableName + s;
                        Result.Updateable := True;
                        end;
-                     ParsePart := ppFrom;
+                     // compound delimited classifier like: "schema name"."table name"
+                     if not (CurrentP^ in ['.','"']) then
+                       ParsePart := ppFrom;
                      end;
           ppFrom   : begin
                      if (Keyword in [kwWHERE, kwGROUP, kwORDER, kwLIMIT, kwROWS]) or
@@ -1683,7 +1685,6 @@ begin
         dec(CurrentP);
       PhraseP := CurrentP+1;
       end
-    end;
   until CurrentP^=#0;
 end;
 

+ 34 - 0
packages/fcl-db/tests/testsqldb.pas

@@ -72,6 +72,7 @@ type
     procedure TestUseImplicitTransaction;
     procedure TestUseExplicitTransaction;
     procedure TestExplicitConnect;
+    procedure TestGetStatementInfo;
   end;
 
   { TTestTSQLScript }
@@ -838,6 +839,39 @@ begin
   AssertException('toExplicitStart raises exception on implicit start',EDatabaseError,@TryOpen)
 end;
 
+procedure TTestTSQLConnection.TestGetStatementInfo;
+var StmtInfo: TSQLStatementInfo;
+begin
+  // single table
+  StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM tab1');
+  AssertEquals('StatementType', ord(stSELECT), ord(StmtInfo.StatementType));
+  AssertEquals('TableName', 'tab1', StmtInfo.TableName);
+  AssertEquals('Updateable', True, StmtInfo.Updateable);
+  StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM tab2 WHERE col1=1');
+  AssertEquals('TableName', 'tab2', StmtInfo.TableName);
+  AssertEquals('Updateable', True, StmtInfo.Updateable);
+  // single table with schema
+  StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM dbo.tab2 WHERE col1=1');
+  AssertEquals('TableName', 'dbo.tab2', StmtInfo.TableName);
+  AssertEquals('Updateable', True, StmtInfo.Updateable);
+  // single table with quoted schema
+  StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM "dbo".tab2 WHERE col1=1');
+  AssertEquals('TableName', '"dbo".tab2', StmtInfo.TableName);
+  AssertEquals('Updateable', True, StmtInfo.Updateable);
+  StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM "dbo"."tab2" WHERE col1=1');
+  AssertEquals('TableName', '"dbo"."tab2"', StmtInfo.TableName);
+  AssertEquals('Updateable', True, StmtInfo.Updateable);
+  // multiple tables
+  StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM tab3,tab4 WHERE col1=1');
+  AssertEquals('TableName', '', StmtInfo.TableName);
+  AssertEquals('Updateable', False, StmtInfo.Updateable);
+  // function
+  StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM dbo.fn1(1)');
+  AssertEquals('TableName', '', StmtInfo.TableName);
+  AssertEquals('Updateable', False, StmtInfo.Updateable);
+end;
+
+
 { TTestTSQLScript }
 
 procedure TTestTSQLScript.TestExecuteScript;