How to Parse Oracle PL/SQL in Java¶
To parse Oracle PL/SQL in Java, create a TGSqlParser with EDbVendor.dbvoracle, call parse(), and cast each result to its PL/SQL statement class — TPlsqlCreateProcedure, TPlsqlCreateFunction, TPlsqlCreatePackage, or TPlsqlCreateTrigger. Each stored-program statement exposes getBodyStatements() for the executable section and getDeclareStatements() for declarations, so you can walk every SQL statement embedded in procedure code — including dynamic SQL issued via EXECUTE IMMEDIATE.
This guide covers the full workflow: parsing a procedure, enumerating the members of a CREATE PACKAGE BODY, extracting the plain DML (SELECT/INSERT/UPDATE/DELETE) buried inside procedure bodies, and handling dynamic SQL.
Prerequisites¶
- GSP installed per the Quick Start
- Familiarity with basic SQL parsing
Parse a PL/SQL Procedure¶
EDbVendor.dbvoracle handles both plain Oracle SQL and full PL/SQL — there is no separate "PL/SQL mode". A successful parse() returns 0, and each top-level statement is available from getSqlstatements().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | |
Output:
1 2 3 4 5 6 | |
Key PL/SQL statement classes¶
All classes live in gudusoft.gsqlparser.stmt.oracle and extend TCommonStoredProcedureSqlStatement:
| Class | sqlstatementtype |
Name getter |
|---|---|---|
TPlsqlCreateProcedure |
sstplsql_createprocedure |
getProcedureName() |
TPlsqlCreateFunction |
sstplsql_createfunction |
getFunctionName(), plus getReturnDataType() |
TPlsqlCreatePackage |
sstplsql_createpackage |
getPackageName() — spec and body, distinguished by getKind() |
TPlsqlCreateTrigger |
sstplsql_createtrigger |
getTriggerName(), plus getTriggerBody() |
All of them inherit three important accessors:
getParameterDeclarations()— aTParameterDeclarationListof formal parametersgetDeclareStatements()— aTStatementListfor the declaration sectiongetBodyStatements()— aTStatementListfor theBEGIN ... ENDexecutable section
Anonymous DECLARE ... BEGIN ... END; blocks parse with statement type ESqlStatementType.sst_plsql_block and expose the same getDeclareStatements() / getBodyStatements() pair.
Parse a CREATE PACKAGE Body and Enumerate Its Members¶
A package's procedures and functions are declarations of the package, so they appear in getDeclareStatements() — not in getBodyStatements(), which holds only the optional package initialization block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | |
Extract All SQL Statements from Procedure Bodies¶
Real PL/SQL nests statements arbitrarily deep — an UPDATE inside an IF inside a LOOP inside a procedure inside a package. Rather than special-casing every container, recurse through getStatements(), the generic child-statement list every TCustomSqlStatement exposes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
Two lists that are easy to confuse:
getBodyStatements()— the syntacticBEGIN ... ENDsection of a block statementgetStatements()— the generic child list on every statement; use it for recursive traversal
stmt.toString() reconstructs the original SQL text of a statement from its token range, and stmt.getStartToken().lineNo gives its position in the source — useful when reporting findings back against the original file.
This recursive pattern is the foundation for table and column extraction, data lineage, and security auditing over stored-procedure code.
Handle Dynamic SQL: EXECUTE IMMEDIATE¶
EXECUTE IMMEDIATE parses to TExecImmeStmt (package gudusoft.gsqlparser.stmt, statement type sstplsql_execimmestmt). GSP exposes the dynamic string expression and — when the string is a literal the parser can resolve — the parsed inner statements as well:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
Inside procedure bodies, find TExecImmeStmt nodes with the same recursive getStatements() walk shown above. Useful accessors:
getDynamicStringExpr()— theTExpressionafterEXECUTE IMMEDIATEgetDynamicSQL()— the resolved SQL string when the expression is a literalgetDynamicStatements()— aTStatementListof the parsed inner statement(s)getIntoVariables()/getBindArguments()—INTOtargets andUSINGbind arguments
When the dynamic string is assembled at runtime (concatenated from variables), no static parser can know the final SQL. Treat getDynamicStringExpr() as the analysis boundary in that case: flag the statement for manual review, or walk the concatenation expression to extract the literal fragments and approximate the tables involved.
Common Pitfalls¶
- Package members are in
getDeclareStatements().getBodyStatements()onTPlsqlCreatePackageholds only the initialization block. - Check the parse result before casting. Only cast statements after
parse()returns0; see Error Handling for diagnosing failures. - One vendor per parser. PL/SQL requires
EDbVendor.dbvoracle; feeding the same text todbvmssqlordbvpostgresqlproduces syntax errors, not a best-effort tree. - Large codebases: parse files independently and reuse parser instances — see Performance Optimization.
Related Pages¶
- Handle SQL Server T-SQL — the equivalent guide for T-SQL batches and procedures
- Error Handling — syntax errors, line/column locations, batch validation
- Oracle SQL syntax support — coverage reference
- SQL Parser Use Cases — lineage, impact analysis, auditing built on these APIs