forked from phoedos/pmd
[plsql] ParseException when using hierarchical query clause
Fixes #1590 * Moves function calls down to primary expression so that functions can be used in a select expression * Restrict FunctionCall to built-in functions. User defined functions are parsed as primary expressions. * Parse function name of built-in function as general ID. * The Tokens _DEFAULT, ELSE and EXCEPTION are reserved words and not available for identifiers.
This commit is contained in:
Andreas Dangel
committed by
Andreas Dangel
parent
544238244b
commit
fcba29a3b7
File diff suppressed because it is too large
Load Diff
@ -986,4 +986,9 @@ public class PLSQLParserVisitorAdapter implements PLSQLParserVisitor {
|
|||||||
public Object visit(ASTValuesClause node, Object data) {
|
public Object visit(ASTValuesClause node, Object data) {
|
||||||
return visit((PLSQLNode) node, data);
|
return visit((PLSQLNode) node, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(ASTHierarchicalQueryClause node, Object data) {
|
||||||
|
return visit((PLSQLNode) node, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1080,6 +1080,11 @@ public abstract class AbstractPLSQLRule extends AbstractRule implements PLSQLPar
|
|||||||
return visit((PLSQLNode) node, data);
|
return visit((PLSQLNode) node, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(ASTHierarchicalQueryClause node, Object data) {
|
||||||
|
return visit((PLSQLNode) node, data);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Treat all Executable Code
|
* Treat all Executable Code
|
||||||
*/
|
*/
|
||||||
|
@ -125,10 +125,14 @@ public class CodeFormatRule extends AbstractPLSQLRule {
|
|||||||
int currentLine = firstLine;
|
int currentLine = firstLine;
|
||||||
for (int i = 0; i < parent.jjtGetNumChildren(); i++) {
|
for (int i = 0; i < parent.jjtGetNumChildren(); i++) {
|
||||||
Node child = parent.jjtGetChild(i);
|
Node child = parent.jjtGetChild(i);
|
||||||
|
String image = child.getImage();
|
||||||
|
if (image == null && child.jjtGetNumChildren() > 0) {
|
||||||
|
image = child.jjtGetChild(0).getImage();
|
||||||
|
}
|
||||||
if (child.getBeginLine() != currentLine) {
|
if (child.getBeginLine() != currentLine) {
|
||||||
addViolationWithMessage(data, child, child.getImage() + " should be on line " + currentLine);
|
addViolationWithMessage(data, child, image + " should be on line " + currentLine);
|
||||||
} else if (i > 0 && child.getBeginColumn() != indentation) {
|
} else if (i > 0 && child.getBeginColumn() != indentation) {
|
||||||
addViolationWithMessage(data, child, child.getImage() + " should begin at column " + indentation);
|
addViolationWithMessage(data, child, image + " should begin at column " + indentation);
|
||||||
}
|
}
|
||||||
// next entry needs to be on the next line
|
// next entry needs to be on the next line
|
||||||
currentLine++;
|
currentLine++;
|
||||||
|
@ -8,7 +8,6 @@ import java.nio.charset.StandardCharsets;
|
|||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import net.sourceforge.pmd.lang.plsql.AbstractPLSQLParserTst;
|
import net.sourceforge.pmd.lang.plsql.AbstractPLSQLParserTst;
|
||||||
@ -16,7 +15,6 @@ import net.sourceforge.pmd.lang.plsql.AbstractPLSQLParserTst;
|
|||||||
public class SelectExpressionsTest extends AbstractPLSQLParserTst {
|
public class SelectExpressionsTest extends AbstractPLSQLParserTst {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
|
||||||
public void parseSelectExpression() throws Exception {
|
public void parseSelectExpression() throws Exception {
|
||||||
String code = IOUtils.toString(this.getClass().getResourceAsStream("SelectExpressions.pls"),
|
String code = IOUtils.toString(this.getClass().getResourceAsStream("SelectExpressions.pls"),
|
||||||
StandardCharsets.UTF_8);
|
StandardCharsets.UTF_8);
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sourceforge.pmd.lang.plsql.ast;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import net.sourceforge.pmd.lang.plsql.AbstractPLSQLParserTst;
|
||||||
|
|
||||||
|
public class SelectHierarchicalTest extends AbstractPLSQLParserTst {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseSelectHierarchicalQueries() throws Exception {
|
||||||
|
String code = IOUtils.toString(this.getClass().getResourceAsStream("SelectHierarchical.pls"),
|
||||||
|
StandardCharsets.UTF_8);
|
||||||
|
ASTInput input = parsePLSQL(code);
|
||||||
|
Assert.assertNotNull(input);
|
||||||
|
}
|
||||||
|
}
|
@ -23,8 +23,12 @@ public class WhereClauseTest extends AbstractPLSQLParserTst {
|
|||||||
List<ASTSelectIntoStatement> selectStatements = input.findDescendantsOfType(ASTSelectIntoStatement.class);
|
List<ASTSelectIntoStatement> selectStatements = input.findDescendantsOfType(ASTSelectIntoStatement.class);
|
||||||
Assert.assertEquals(3, selectStatements.size());
|
Assert.assertEquals(3, selectStatements.size());
|
||||||
|
|
||||||
ASTFunctionCall functionCall = selectStatements.get(2).getFirstDescendantOfType(ASTFunctionCall.class);
|
ASTFunctionCall functionCall = selectStatements.get(0).getFirstDescendantOfType(ASTFunctionCall.class);
|
||||||
Assert.assertEquals("utils.get_colname", functionCall.getImage());
|
Assert.assertEquals("UPPER", functionCall.getImage());
|
||||||
|
|
||||||
|
ASTPrimaryPrefix primaryPrefix = selectStatements.get(2).getFirstDescendantOfType(ASTWhereClause.class)
|
||||||
|
.findDescendantsOfType(ASTPrimaryPrefix.class).get(1);
|
||||||
|
Assert.assertEquals("utils.get_colname", primaryPrefix.getImage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
|
SELECT AVG(sal)*2 INTO foo FROM bar;
|
||||||
|
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
AVG(salary) * 12 "Average Sal"
|
AVG(salary) * 12 "Average Sal"
|
||||||
INTO some_record
|
INTO some_record
|
||||||
@ -14,5 +17,6 @@ SELECT
|
|||||||
INTO some_record
|
INTO some_record
|
||||||
FROM some_table;
|
FROM some_table;
|
||||||
|
|
||||||
|
|
||||||
END;
|
END;
|
||||||
/
|
/
|
@ -0,0 +1,90 @@
|
|||||||
|
--
|
||||||
|
-- Select statement with hierarchical queries
|
||||||
|
--
|
||||||
|
-- https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6
|
||||||
|
-- https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6__I2130004
|
||||||
|
-- https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Hierarchical-Queries.html#GUID-0118DF1D-B9A9-41EB-8556-C6E7D6A5A84E
|
||||||
|
--
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
SELECT id INTO v_id
|
||||||
|
FROM (SELECT separator_in || string_in || separator_in AS token_list FROM DUAL)
|
||||||
|
CONNECT BY col_length <= LENGTH(string_in) - LENGTH(separator_in);
|
||||||
|
|
||||||
|
SELECT last_name, employee_id, manager_id
|
||||||
|
INTO test
|
||||||
|
FROM employees
|
||||||
|
CONNECT BY employee_id = manager_id
|
||||||
|
ORDER BY last_name;
|
||||||
|
|
||||||
|
SELECT last_name, employee_id, manager_id
|
||||||
|
INTO test
|
||||||
|
FROM employees
|
||||||
|
CONNECT BY PRIOR employee_id = manager_id
|
||||||
|
AND salary > commission_pct
|
||||||
|
ORDER BY last_name;
|
||||||
|
|
||||||
|
SELECT employee_id, last_name, manager_id
|
||||||
|
INTO test
|
||||||
|
FROM employees
|
||||||
|
CONNECT BY PRIOR employee_id = manager_id;
|
||||||
|
|
||||||
|
SELECT employee_id, last_name, manager_id, LEVEL
|
||||||
|
INTO test
|
||||||
|
FROM employees
|
||||||
|
CONNECT BY PRIOR employee_id = manager_id;
|
||||||
|
|
||||||
|
SELECT last_name, employee_id, manager_id, LEVEL
|
||||||
|
INTO test
|
||||||
|
FROM employees
|
||||||
|
START WITH employee_id = 100
|
||||||
|
CONNECT BY PRIOR employee_id = manager_id
|
||||||
|
ORDER SIBLINGS BY last_name;
|
||||||
|
|
||||||
|
SELECT last_name "Employee",
|
||||||
|
LEVEL, SYS_CONNECT_BY_PATH(last_name, '/') "Path"
|
||||||
|
INTO test
|
||||||
|
FROM employees
|
||||||
|
WHERE level <= 3 AND department_id = 80
|
||||||
|
START WITH last_name = 'King'
|
||||||
|
CONNECT BY PRIOR employee_id = manager_id AND LEVEL <= 4;
|
||||||
|
|
||||||
|
SELECT last_name "Employee", CONNECT_BY_ISCYCLE "Cycle",
|
||||||
|
LEVEL, SYS_CONNECT_BY_PATH(last_name, '/') "Path"
|
||||||
|
INTO test
|
||||||
|
FROM employees
|
||||||
|
WHERE level <= 3 AND department_id = 80
|
||||||
|
START WITH last_name = 'King'
|
||||||
|
CONNECT BY NOCYCLE PRIOR employee_id = manager_id AND LEVEL <= 4
|
||||||
|
ORDER BY "Employee", "Cycle", LEVEL, "Path";
|
||||||
|
|
||||||
|
SELECT LTRIM(SYS_CONNECT_BY_PATH (warehouse_id,','),',')
|
||||||
|
INTO test
|
||||||
|
FROM
|
||||||
|
(SELECT ROWNUM r, warehouse_id FROM warehouses)
|
||||||
|
WHERE CONNECT_BY_ISLEAF = 1
|
||||||
|
START WITH r = 1
|
||||||
|
CONNECT BY r = PRIOR r + 1
|
||||||
|
ORDER BY warehouse_id;
|
||||||
|
|
||||||
|
SELECT last_name "Employee", CONNECT_BY_ROOT last_name "Manager",
|
||||||
|
LEVEL-1 "Pathlen", SYS_CONNECT_BY_PATH(last_name, '/') "Path"
|
||||||
|
INTO test
|
||||||
|
FROM employees
|
||||||
|
WHERE LEVEL > 1 and department_id = 110
|
||||||
|
CONNECT BY PRIOR employee_id = manager_id
|
||||||
|
ORDER BY "Employee", "Manager", "Pathlen", "Path";
|
||||||
|
|
||||||
|
SELECT name, SUM(salary) "Total_Salary"
|
||||||
|
INTO test
|
||||||
|
FROM (
|
||||||
|
SELECT CONNECT_BY_ROOT last_name as name, Salary
|
||||||
|
FROM employees
|
||||||
|
WHERE department_id = 110
|
||||||
|
CONNECT BY PRIOR employee_id = manager_id)
|
||||||
|
GROUP BY name
|
||||||
|
ORDER BY name, "Total_Salary";
|
||||||
|
|
||||||
|
END;
|
||||||
|
/
|
Reference in New Issue
Block a user