[plsql] Parsing exception OPEN ref_cursor_name FOR statement

Fixes #3487
This commit is contained in:
Andreas Dangel
2021-09-10 14:57:59 +02:00
parent 46e70a0d8f
commit ef3b1a8fa1
6 changed files with 463 additions and 8 deletions

View File

@ -16,6 +16,9 @@ This is a {{ site.pmd.release_type }} release.
### Fixed Issues
* plsql
* [#3487](https://github.com/pmd/pmd/issues/3487): \[plsql] Parsing exception OPEN ref_cursor_name FOR statement
### API Changes
### External Contributions

View File

@ -27,6 +27,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/**
* Add support for Select statement within OPEN FOR Statements
*
* Andreas Dangel 09/2021
*====================================================================
* Add support for XMLROOT, improve ExtractExpression to support xml
*
* Piotr Szymanski 03/2020
@ -84,7 +88,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Andreas Dangel 07/2018
*====================================================================
* Added ASTIsOfTypeCondition node, added support for USING IN|OUT|IN_OUT
* Added ASTIsOfTypeCondition node, added support for USING IN|OUT|IN OUT
* See PMD Bug #1520
*
* Andreas Dangel 11/2016
@ -2741,13 +2745,17 @@ ASTCloseStatement CloseStatement() :
{ jjtThis.setImage(cursor.getImage()) ; return jjtThis ; }
}
/*
* See https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/OPEN-statement.html#GUID-FB5A9CC3-655F-4AF4-8105-14CB39F2FEA8
* and https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/OPEN-FOR-statement.html#GUID-EB7AF439-FDD3-4461-9E3F-B621E8ABFB96
*/
ASTOpenStatement OpenStatement() :
{}
{
<OPEN> [Expression()]
//[LOOKAHEAD(functionCall()) functionCall() | QualifiedName()]
[<FOR> Expression() [<USING> (<IN> | <OUT> | <IN_OUT>)? Expression() ("," (<IN> | <OUT> | <IN_OUT>)? Expression())*]]
{ return jjtThis ; }
<OPEN> [Expression()]
[<FOR> ( SelectStatement() | Expression() ) [ UsingClause() ] ]
{ return jjtThis ; }
}
/*
@ -2768,12 +2776,18 @@ ASTEmbeddedSqlStatement EmbeddedSqlStatement() :
{
<EXECUTE> <IMMEDIATE> StringExpression()
[ <INTO> Name() ("," Name())* ]
[ <USING> [ <IN> [ <OUT> ] | <OUT> ] Expression() ("," [ <IN> [ <OUT> ] | <OUT> ] Expression())* ]
[ UsingClause() ]
[ ( <RETURN> | <RETURNING> ) <INTO> Expression() ("," Expression())*] ";"
// PIPELINED FUNCTION OUTPUT
{ return jjtThis ; }
}
void UsingClause() #void :
{}
{
<USING> [ <IN> [ <OUT> ] | <OUT> ] Expression() ("," [ <IN> [ <OUT> ] | <OUT> ] Expression())*
}
ASTPipelineStatement PipelineStatement() :
{}
{
@ -4900,7 +4914,6 @@ TOKEN [IGNORE_CASE]:
<HOUR: "HOUR"> |
<IMMEDIATE: "IMMEDIATE"> |
<INNER: "INNER"> |
<IN_OUT: "IN_OUT"> |
<INDICES: "INDICES"> |
<INCLUDING: "INCLUDING"> |
<INDEXTYPE: "INDEXTYPE"> |

View File

@ -32,4 +32,8 @@ public class PlsqlTreeDumpTest extends BaseTreeDumpTest {
doTest("ParsingExclusion");
}
@Test
public void parseOpenForStatement() {
doTest("OpenForStatement");
}
}

View File

@ -0,0 +1,62 @@
--
-- See https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/OPEN-FOR-statement.html#GUID-EB7AF439-FDD3-4461-9E3F-B621E8ABFB96
-- https://github.com/pmd/pmd/issues/3487
--
CREATE OR REPLACE PROCEDURE EXAMPLE_PROCEDURE IS
--
TYPE t_ref_cursor IS REF CURSOR;
--
l_ref_cursor t_ref_cursor;
--
BEGIN
--
OPEN l_ref_cursor FOR
SELECT *
FROM DUAL;
--
END EXAMPLE_PROCEDURE;
--
-- Example 6-26 Fetching Data with Cursor Variables
-- https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/static-sql.html#GUID-AA5A2016-1B76-4961-9AFB-EB052F0D0FB2
--
DECLARE
cv SYS_REFCURSOR; -- cursor variable
v_lastname employees.last_name%TYPE; -- variable for last_name
v_jobid employees.job_id%TYPE; -- variable for job_id
query_2 VARCHAR2(200) :=
'SELECT * FROM employees
WHERE REGEXP_LIKE (job_id, ''[ACADFIMKSA]_M[ANGR]'')
ORDER BY job_id';
v_employees employees%ROWTYPE; -- record variable row of table
BEGIN
OPEN cv FOR
SELECT last_name, job_id FROM employees
WHERE REGEXP_LIKE (job_id, 'S[HT]_CLERK')
ORDER BY last_name;
LOOP -- Fetches 2 columns into variables
FETCH cv INTO v_lastname, v_jobid;
EXIT WHEN cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( RPAD(v_lastname, 25, ' ') || v_jobid );
END LOOP;
DBMS_OUTPUT.PUT_LINE( '-------------------------------------' );
OPEN cv FOR query_2;
LOOP -- Fetches entire row into the v_employees record
FETCH cv INTO v_employees;
EXIT WHEN cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( RPAD(v_employees.last_name, 25, ' ') ||
v_employees.job_id );
END LOOP;
CLOSE cv;
END;
/

View File

@ -12,5 +12,5 @@ BEGIN
open cursor for 'query' USING variable;
open cursor for 'query' USING IN variable;
open cursor for 'query' USING OUT variable, IN othervariable;
open cursor for 'query' USING IN_OUT variable;
open cursor for 'query' USING IN OUT variable;
END;