From 44ecb87c43ea13f13b9eceeb27333afefbea4c32 Mon Sep 17 00:00:00 2001 From: Arjen Duursma Date: Thu, 27 Jun 2024 14:18:30 +0200 Subject: [PATCH] Add support for SQL_MACRO --- pmd-plsql/etc/grammar/PLSQL.jjt | 24 +++- .../pmd/lang/plsql/ast/ASTSqlMacroClause.java | 31 +++++ .../lang/plsql/ast/ASTSqlMacroClauseTest.java | 31 +++++ .../plsql/ast/MergeStatementIssue1934.pls | 7 -- .../plsql/ast/MergeStatementIssue1934.txt | 109 +----------------- .../pmd/lang/plsql/ast/SqlMacroClause.pls | 38 ++++++ 6 files changed, 124 insertions(+), 116 deletions(-) create mode 100644 pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/ASTSqlMacroClause.java create mode 100644 pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/ASTSqlMacroClauseTest.java create mode 100644 pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/SqlMacroClause.pls diff --git a/pmd-plsql/etc/grammar/PLSQL.jjt b/pmd-plsql/etc/grammar/PLSQL.jjt index d307bc68b3..f62bc0d255 100644 --- a/pmd-plsql/etc/grammar/PLSQL.jjt +++ b/pmd-plsql/etc/grammar/PLSQL.jjt @@ -27,6 +27,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** + * Add support for SQL_MACRO + * + * Arjen Duursma 06/2024 + *==================================================================== * Add support for MERGE (INTO) statement * Add support Error Logging for INSERT, UPDATE, DELETE * @@ -728,7 +732,7 @@ ASTMethodDeclarator MethodDeclarator() : } } // There is no RETURN for a WRAPPED object - [ Datatype() ] + [ Datatype() [ SqlMacroClause() ] ] ) | @@ -2115,7 +2119,7 @@ void QueryTableExpression() #void : {} { ( - LOOKAHEAD(2) [ LOOKAHEAD(2) SchemaName() "." ] TableName() + LOOKAHEAD(2) [ LOOKAHEAD(2) SchemaName() "." ] ( LOOKAHEAD(2) FunctionCall() | TableName() ) | TableCollectionExpression() | @@ -2146,6 +2150,7 @@ ASTTableCollectionExpression TableCollectionExpression() : /** * Special production, used in joins. The table reference might have * a table alias, but this should not match any following NATURAL, CROSS, etc. + * keywords, although these are allowed as alias names since these are * not reserved words. */ @@ -2690,7 +2695,7 @@ ASTDeleteStatement DeleteStatement() : ASTMergeStatement MergeStatement() : {} { - [ LOOKAHEAD(2) SchemaName() "." ] TableName() [ LOOKAHEAD(1, ID(), { getToken(1).kind != USING } ) TableAlias() ] + [ LOOKAHEAD(2) SchemaName() "." ] TableName() [ TableAlias() ] ( LOOKAHEAD(3) "(" ValuesClause() ")" @@ -5133,6 +5138,7 @@ TOKEN [IGNORE_CASE]: | | | + | | | | @@ -5146,6 +5152,7 @@ TOKEN [IGNORE_CASE]: | | | + | | | | @@ -7058,3 +7065,14 @@ ASTJavaInterfaceClass JavaInterfaceClass(): {} ) { jjtThis.setImage(token.getImage()) ; jjtThis.value = token ; return jjtThis ; } } + +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/SQL_MACRO-clause.html#LNPLS-GUID-292C3A17-2A4B-4EFB-AD38-68DF6380E5F7 + */ +ASTSqlMacroClause SqlMacroClause(): {} +{ + ( + [ [ "=>" ] ( | ) { jjtThis.setType(token.getImage()); } ] + ) + { return jjtThis; } +} diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/ASTSqlMacroClause.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/ASTSqlMacroClause.java new file mode 100644 index 0000000000..38b708d2f1 --- /dev/null +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/ASTSqlMacroClause.java @@ -0,0 +1,31 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.plsql.ast; + +import java.util.Locale; + +public final class ASTSqlMacroClause extends AbstractPLSQLNode { + private String type = "TABLE"; + + ASTSqlMacroClause(int id) { + super(id); + } + + @Override + protected R acceptPlsqlVisitor(PlsqlVisitor visitor, P data) { + return visitor.visit(this, data); + } + + public String getType() { + return type; + } + + void setType(String type) { + this.type = type; + if (this.type != null) { + this.type = this.type.toUpperCase(Locale.ROOT); + } + } +} diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/ASTSqlMacroClauseTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/ASTSqlMacroClauseTest.java new file mode 100644 index 0000000000..73165712af --- /dev/null +++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/ASTSqlMacroClauseTest.java @@ -0,0 +1,31 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.plsql.ast; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import net.sourceforge.pmd.lang.plsql.AbstractPLSQLParserTst; + +class ASTSqlMacroClauseTest extends AbstractPLSQLParserTst { + + @Test + void testEmpty() { + ASTInput input = plsql.parseResource("SqlMacroClause.pls"); + + List sqlMacros = input.descendants(ASTSqlMacroClause.class).toList(); + assertFalse(sqlMacros.isEmpty()); + assertEquals("TABLE", sqlMacros.get(0).getType()); + assertEquals("SCALAR", sqlMacros.get(1).getType()); + assertEquals("SCALAR", sqlMacros.get(2).getType()); + assertEquals("TABLE", sqlMacros.get(3).getType()); + + //assertEquals("=", conditions.get(0).getOperator()); + } +} diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MergeStatementIssue1934.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MergeStatementIssue1934.pls index 782d8cffba..b0826ea40a 100644 --- a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MergeStatementIssue1934.pls +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MergeStatementIssue1934.pls @@ -14,12 +14,5 @@ BEGIN WHEN NOT MATCHED THEN INSERT (ID,KEY1, TEXT,LCE_ID) values (JHS_SEQ.NEXTVAL,'PROM_EDIT_PROM_NR','Edycja promocji nr',123123); - MERGE INTO b - USING ( SELECT 'PROM_EDIT_PROM_NR' key1,'Edycja promocji nr' text,123123 lce_id FROM dual ) e - ON (b.key1 = e.key1 and b.lce_id=e.lce_id) - WHEN MATCHED - THEN UPDATE SET b.text = e.text - WHEN NOT MATCHED - THEN INSERT (ID,KEY1, TEXT,LCE_ID) values (JHS_SEQ.NEXTVAL,'PROM_EDIT_PROM_NR','Edycja promocji nr',123123); END; / diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MergeStatementIssue1934.txt b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MergeStatementIssue1934.txt index bda27d276c..c473d6143c 100644 --- a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MergeStatementIssue1934.txt +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MergeStatementIssue1934.txt @@ -1,115 +1,12 @@ +- Input[@CanonicalImage = null, @ExcludedLinesCount = 0, @ExcludedRangesCount = 0] +- Global[@CanonicalImage = null] +- Block[@CanonicalImage = null] - +- Statement[@CanonicalImage = null] - | +- UnlabelledStatement[@CanonicalImage = null] - | +- MergeStatement[@CanonicalImage = null] - | +- TableName[@CanonicalImage = "JHS_TRANSLATIONS", @Image = "jhs_translations"] - | | +- ID[@CanonicalImage = "JHS_TRANSLATIONS", @Image = "jhs_translations"] - | +- TableAlias[@CanonicalImage = "B", @Image = "b"] - | | +- ID[@CanonicalImage = "B", @Image = "b"] - | +- QueryBlock[@All = false, @CanonicalImage = null, @Distinct = false, @Unique = false] - | | +- SelectList[@CanonicalImage = null] - | | | +- SqlExpression[@CanonicalImage = "\'PROM_EDIT_PROM_NR\'", @Image = "\'PROM_EDIT_PROM_NR\'"] - | | | | +- PrimaryPrefix[@CanonicalImage = "\'PROM_EDIT_PROM_NR\'", @Image = "\'PROM_EDIT_PROM_NR\'", @SelfModifier = false] - | | | | +- Literal[@CanonicalImage = "\'PROM_EDIT_PROM_NR\'", @Image = "\'PROM_EDIT_PROM_NR\'"] - | | | | +- StringLiteral[@CanonicalImage = "\'PROM_EDIT_PROM_NR\'", @Image = "\'PROM_EDIT_PROM_NR\'", @String = "PROM_EDIT_PROM_NR"] - | | | +- ColumnAlias[@CanonicalImage = "KEY1", @Image = "key1"] - | | | | +- ID[@CanonicalImage = "KEY1", @Image = "key1"] - | | | +- SqlExpression[@CanonicalImage = "\'EDYCJA PROMOCJI NR\'", @Image = "\'Edycja promocji nr\'"] - | | | | +- PrimaryPrefix[@CanonicalImage = "\'EDYCJA PROMOCJI NR\'", @Image = "\'Edycja promocji nr\'", @SelfModifier = false] - | | | | +- Literal[@CanonicalImage = "\'EDYCJA PROMOCJI NR\'", @Image = "\'Edycja promocji nr\'"] - | | | | +- StringLiteral[@CanonicalImage = "\'EDYCJA PROMOCJI NR\'", @Image = "\'Edycja promocji nr\'", @String = "Edycja promocji nr"] - | | | +- ColumnAlias[@CanonicalImage = "TEXT", @Image = "text"] - | | | | +- ID[@CanonicalImage = "TEXT", @Image = "text"] - | | | +- SqlExpression[@CanonicalImage = "123123", @Image = "123123"] - | | | | +- PrimaryPrefix[@CanonicalImage = "123123", @Image = "123123", @SelfModifier = false] - | | | | +- Literal[@CanonicalImage = "123123", @Image = "123123"] - | | | | +- NumericLiteral[@CanonicalImage = "123123", @Image = "123123"] - | | | +- ColumnAlias[@CanonicalImage = "LCE_ID", @Image = "lce_id"] - | | | +- ID[@CanonicalImage = "LCE_ID", @Image = "lce_id"] - | | +- FromClause[@CanonicalImage = null] - | | +- TableReference[@CanonicalImage = null] - | | +- TableName[@CanonicalImage = "DUAL", @Image = "dual"] - | | +- ID[@CanonicalImage = "DUAL", @Image = "dual"] - | +- TableAlias[@CanonicalImage = "E", @Image = "e"] - | | +- ID[@CanonicalImage = "E", @Image = "e"] - | +- Condition[@CanonicalImage = null] - | | +- CompoundCondition[@CanonicalImage = null, @Type = "AND"] - | | +- ComparisonCondition[@CanonicalImage = null, @Operator = "="] - | | | +- SqlExpression[@CanonicalImage = "B.KEY1", @Image = "b.key1"] - | | | | +- PrimaryPrefix[@CanonicalImage = "B.KEY1", @Image = "b.key1", @SelfModifier = false] - | | | | +- SimpleExpression[@CanonicalImage = "B.KEY1", @Image = "b.key1"] - | | | | +- TableName[@CanonicalImage = "B", @Image = "b"] - | | | | | +- ID[@CanonicalImage = "B", @Image = "b"] - | | | | +- Column[@CanonicalImage = "KEY1", @Image = "key1"] - | | | | +- ID[@CanonicalImage = "KEY1", @Image = "key1"] - | | | +- SqlExpression[@CanonicalImage = "E.KEY1", @Image = "e.key1"] - | | | +- PrimaryPrefix[@CanonicalImage = "E.KEY1", @Image = "e.key1", @SelfModifier = false] - | | | +- SimpleExpression[@CanonicalImage = "E.KEY1", @Image = "e.key1"] - | | | +- TableName[@CanonicalImage = "E", @Image = "e"] - | | | | +- ID[@CanonicalImage = "E", @Image = "e"] - | | | +- Column[@CanonicalImage = "KEY1", @Image = "key1"] - | | | +- ID[@CanonicalImage = "KEY1", @Image = "key1"] - | | +- Condition[@CanonicalImage = null] - | | +- CompoundCondition[@CanonicalImage = null, @Type = null] - | | +- ComparisonCondition[@CanonicalImage = null, @Operator = "="] - | | +- SqlExpression[@CanonicalImage = "B.LCE_ID", @Image = "b.lce_id"] - | | | +- PrimaryPrefix[@CanonicalImage = "B.LCE_ID", @Image = "b.lce_id", @SelfModifier = false] - | | | +- SimpleExpression[@CanonicalImage = "B.LCE_ID", @Image = "b.lce_id"] - | | | +- TableName[@CanonicalImage = "B", @Image = "b"] - | | | | +- ID[@CanonicalImage = "B", @Image = "b"] - | | | +- Column[@CanonicalImage = "LCE_ID", @Image = "lce_id"] - | | | +- ID[@CanonicalImage = "LCE_ID", @Image = "lce_id"] - | | +- SqlExpression[@CanonicalImage = "E.LCE_ID", @Image = "e.lce_id"] - | | +- PrimaryPrefix[@CanonicalImage = "E.LCE_ID", @Image = "e.lce_id", @SelfModifier = false] - | | +- SimpleExpression[@CanonicalImage = "E.LCE_ID", @Image = "e.lce_id"] - | | +- TableName[@CanonicalImage = "E", @Image = "e"] - | | | +- ID[@CanonicalImage = "E", @Image = "e"] - | | +- Column[@CanonicalImage = "LCE_ID", @Image = "lce_id"] - | | +- ID[@CanonicalImage = "LCE_ID", @Image = "lce_id"] - | +- MergeUpdateClause[@CanonicalImage = null] - | | +- TableName[@CanonicalImage = "B", @Image = "b"] - | | | +- ID[@CanonicalImage = "B", @Image = "b"] - | | +- Column[@CanonicalImage = "TEXT", @Image = "text"] - | | | +- ID[@CanonicalImage = "TEXT", @Image = "text"] - | | +- Expression[@CanonicalImage = "E.TEXT", @Image = "e.text"] - | | +- PrimaryPrefix[@CanonicalImage = "E.TEXT", @Image = "e.text", @SelfModifier = false] - | | +- SimpleExpression[@CanonicalImage = "E.TEXT", @Image = "e.text"] - | | +- TableName[@CanonicalImage = "E", @Image = "e"] - | | | +- ID[@CanonicalImage = "E", @Image = "e"] - | | +- Column[@CanonicalImage = "TEXT", @Image = "text"] - | | +- ID[@CanonicalImage = "TEXT", @Image = "text"] - | +- MergeInsertClause[@CanonicalImage = null] - | +- Column[@CanonicalImage = "ID", @Image = "ID"] - | | +- ID[@CanonicalImage = "ID", @Image = "ID"] - | +- Column[@CanonicalImage = "KEY1", @Image = "KEY1"] - | | +- ID[@CanonicalImage = "KEY1", @Image = "KEY1"] - | +- Column[@CanonicalImage = "TEXT", @Image = "TEXT"] - | | +- ID[@CanonicalImage = "TEXT", @Image = "TEXT"] - | +- Column[@CanonicalImage = "LCE_ID", @Image = "LCE_ID"] - | | +- ID[@CanonicalImage = "LCE_ID", @Image = "LCE_ID"] - | +- ValuesClause[@CanonicalImage = null] - | +- Expression[@CanonicalImage = "", @Image = ""] - | | +- PrimaryPrefix[@CanonicalImage = "", @Image = "", @SelfModifier = false] - | | +- SimpleExpression[@CanonicalImage = "", @Image = ""] - | | +- ID[@CanonicalImage = "JHS_SEQ", @Image = "JHS_SEQ"] - | +- Expression[@CanonicalImage = "\'PROM_EDIT_PROM_NR\'", @Image = "\'PROM_EDIT_PROM_NR\'"] - | | +- PrimaryPrefix[@CanonicalImage = "\'PROM_EDIT_PROM_NR\'", @Image = "\'PROM_EDIT_PROM_NR\'", @SelfModifier = false] - | | +- Literal[@CanonicalImage = "\'PROM_EDIT_PROM_NR\'", @Image = "\'PROM_EDIT_PROM_NR\'"] - | | +- StringLiteral[@CanonicalImage = "\'PROM_EDIT_PROM_NR\'", @Image = "\'PROM_EDIT_PROM_NR\'", @String = "PROM_EDIT_PROM_NR"] - | +- Expression[@CanonicalImage = "\'EDYCJA PROMOCJI NR\'", @Image = "\'Edycja promocji nr\'"] - | | +- PrimaryPrefix[@CanonicalImage = "\'EDYCJA PROMOCJI NR\'", @Image = "\'Edycja promocji nr\'", @SelfModifier = false] - | | +- Literal[@CanonicalImage = "\'EDYCJA PROMOCJI NR\'", @Image = "\'Edycja promocji nr\'"] - | | +- StringLiteral[@CanonicalImage = "\'EDYCJA PROMOCJI NR\'", @Image = "\'Edycja promocji nr\'", @String = "Edycja promocji nr"] - | +- Expression[@CanonicalImage = "123123", @Image = "123123"] - | +- PrimaryPrefix[@CanonicalImage = "123123", @Image = "123123", @SelfModifier = false] - | +- Literal[@CanonicalImage = "123123", @Image = "123123"] - | +- NumericLiteral[@CanonicalImage = "123123", @Image = "123123"] +- Statement[@CanonicalImage = null] +- UnlabelledStatement[@CanonicalImage = null] +- MergeStatement[@CanonicalImage = null] - +- TableName[@CanonicalImage = "B", @Image = "b"] + +- TableName[@CanonicalImage = "JHS_TRANSLATIONS", @Image = "jhs_translations"] + | +- ID[@CanonicalImage = "JHS_TRANSLATIONS", @Image = "jhs_translations"] + +- TableAlias[@CanonicalImage = "B", @Image = "b"] | +- ID[@CanonicalImage = "B", @Image = "b"] +- QueryBlock[@All = false, @CanonicalImage = null, @Distinct = false, @Unique = false] | +- SelectList[@CanonicalImage = null] diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/SqlMacroClause.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/SqlMacroClause.pls new file mode 100644 index 0000000000..7ad7f36298 --- /dev/null +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/SqlMacroClause.pls @@ -0,0 +1,38 @@ +create or replace package body test is + + function test1() return clob sql_macro is + begin + + return 'q[select * from dual]'; + + end test1; + + function test2() return clob sql_macro(TYPE => SCALAR) is + begin + + return 'q[select * from dual]'; + + end test2; + + function test3() return clob sql_macro(SCALAR) is + begin + + return 'q[select * from dual]'; + + end test3; + + function test3() return clob sql_macro(TABLE) is + begin + + return 'q[select * from dual]'; + + end test4; + + function test5() return int is + cursor c is select 1 from test.test1(); + begin + + return 1; + end test5; + +end test;