/* Copyright (C) 2002-2012 Albert Tumanov All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 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 * Add support for exception handlers in compound triggers, * Allow multiple exception handlers. Parse DeclarativeSection * correctly in compound triggers. * * Andreas Dangel 06/2024 *==================================================================== * 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 *==================================================================== * Add basic support for with clause in select statements * * Andreas Dangel 09/2019 *==================================================================== * Add support for SELECT with FOR UPDATE OF * * Piotr Szymanski 08/2019 *==================================================================== * Various fixes for expression lists, join clauses, case expression * * Hugo Araya Nash 06/2019 *==================================================================== * Various fixes for INSERT INTO with records, implicit cursor attributes * and trim expression. * * Hugo Araya Nash 05/2019 *==================================================================== * Added support for XMLTABLE, XMLEXISTS, XMLCAST, XMLQUERY, CAST, XMLFOREST * and XMLELEMENT * * Andreas Dangel 03/2019 *==================================================================== * More complete support for UPDATE statements and subqueries and hierarchical * queries in SELECT statements. * Added support for analytic functions such as LISTAGG. * Conditions in WHERE clauses support now REGEX_LIKE and multiset conditions. * * Andreas Dangel 02/2019 *==================================================================== * Added support for TableCollectionExpressions * * Andreas Dangel 01/2019 *==================================================================== * Added support for DELETE Statements * Added support for UPDATE Statements * Fully parse the select statement in CursorForLoops. * * Andreas Dangel 09/2018 *==================================================================== * Added support for OrderBy and RowLimiting clauses for SELECT statements * Removed FROM from the RelationalExpression * Support QueryPartitionClause * Support GroupByClause * * Andreas Dangel 08/2018 *==================================================================== * Added more complete support for CREATE TABLE * Added support for SELECT INTO statement * Avoiding deep AST for *Expression nodes * Added ASTCursorForLoop and ASTSelectStatement * * Andreas Dangel 07/2018 *==================================================================== * Added ASTIsOfTypeCondition node, added support for USING IN|OUT|IN OUT * See PMD Bug #1520 * * Andreas Dangel 11/2016 *==================================================================== * Adjusted ProgramUnit() to allow Pragma(), even though this is not * valid syntax. See PMD Bug #1527. * * Andreas Dangel 11/2016 */ /** * Added ASTIsNullCondition node * * Sergey Yanzin 11/2016 */ options { DEBUG_PARSER = false; DEBUG_TOKEN_MANAGER = false; DEBUG_LOOKAHEAD = false; IGNORE_CASE = true; STATIC = false; LOOKAHEAD= 1; // set the ambiguity checks to higher values, if you need more extensive checks of the grammar // this however make the parser generation very slow, so should only be done once after // modifying the grammar to make sure there are no grammar errors. // Default value is 2. CHOICE_AMBIGUITY_CHECK = 2; OTHER_AMBIGUITY_CHECK = 1; ERROR_REPORTING = true; JAVA_UNICODE_ESCAPE = false; UNICODE_INPUT = true; USER_TOKEN_MANAGER = false; USER_CHAR_STREAM = true; BUILD_PARSER = true; BUILD_TOKEN_MANAGER = true; SANITY_CHECK = true; TRACK_TOKENS = false; CACHE_TOKENS = true; MULTI = true; VISITOR = true; } PARSER_BEGIN(PLSQLParserImpl) /* Copyright (C) 2002 Albert Tumanov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package net.sourceforge.pmd.lang.plsql.ast; import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.plsql.ast.internal.ParsingExclusion; import java.util.ArrayList; import java.util.List; /** * @deprecated Since 7.3.0. PLSQLParserImpl should have been package private because this is an implementation class * that should not be used directly. */ @Deprecated public class PLSQLParserImpl { /** Return canonical version of the Oracle */ static String canonicalName(String name) { StringBuilder s = null ; if (null == name) { return name; } else if (-1 == name.indexOf('"') ) { name = name.toUpperCase(); s = new StringBuilder(name.trim()); } else { StringBuilder oldString = new StringBuilder( name.trim().length()); s = new StringBuilder(name.trim()); boolean quotedCharacter = false ; for (int i=0; i * Usage: LOOKAHEAD({isKeyword("WAIT")}) KEYWORD("WAIT") */ private boolean isKeyword(String keyword) { return getToken(1).kind == IDENTIFIER && getToken(1).getImage().equalsIgnoreCase(keyword) // quoted identifiers are excluded && getToken(1).getText().charAt(0) != '"'; } } PARSER_END(PLSQLParserImpl) TOKEN_MGR_DECLS : { List exclusions = new ArrayList(); } // add names to SINGLE CHARACTER tokens // multiple character operators are sequences of tokens. TOKEN: { < LPAREN: "(" > | < RPAREN: ")" > | < LBRACE: "{" > | < RBRACE: "}" > | < LBRACKET: "[" > | < RBRACKET: "]" > | < SEMICOLON: ";" > | < COMMA: "," > | < DOT: "." > | < AROBASE: "@" > | < ASSIGN: "=" > | < LT: "<" > | < BANG: "!" > | < TILDE: "~" > | < HOOK: "?" > | < COLON: ":" > | < PLUSSIGN: "+" > | < MINUSSIGN: "-" > | < STAR: "*" > | < SLASH: "/" > | < BIT_AND: "&" > | < BIT_OR: "|" > | < XOR: "^" > | < REM: "%" > } /** * 2006-05-22 - Matthias Hendler - Added parsing of triggers and global functions/procedures * Refactored printing of custom tags into the XML/DOM. * Custom tags are language independent. Reserved tags are linked * to the documented language like RETURN, PARAM and THROWS. */ /** * 2006-05-22 - Matthias Hendler - added globalBody() */ ASTInput Input() : { token_source.exclusions.clear(); } { // SRT 2011-04-17 This syntax breaks the parser when fields of record.attach* are referenced (attachLibrary())* ( (LOOKAHEAD(7) PackageSpecification() | LOOKAHEAD(7) PackageBody() | LOOKAHEAD(6) TypeSpecification() | LOOKAHEAD(6) Table() | LOOKAHEAD(6) View() | LOOKAHEAD(6) TriggerUnit() | LOOKAHEAD(6) AlterTrigger() | LOOKAHEAD(6) Synonym() | LOOKAHEAD(6) Directory() | LOOKAHEAD(6) DatabaseLink() | LOOKAHEAD(6) Global() | (LOOKAHEAD(6) DDLCommand())+ | LOOKAHEAD(2) SqlPlusCommand() | UpdateStatement() [";"] | DeleteStatement() [";"] | InsertStatement() [";"] | SelectStatement() [";"] | MergeStatement() [";"] |(||||) ReadPastNextOccurrence(";") //Ignore SQL statements in scripts ) ("/")* )* { List exclusions = token_source.exclusions; if (exclusions != null) { // System.err.println("ParsingExclusions:"); for (ParsingExclusion ex : exclusions) { // System.err.print(" Lines " + ex.getBeginLine() + " - " + ex.getEndLine()); // if (ex.getReason() != null) { // System.err.println(": " + ex.getReason()); // } else { // System.err.println(""); // } jjtThis.addExcludedLineRange(ex.getBeginLine(), ex.getEndLine()); } } return jjtThis ; } } ASTDDLCommand DDLCommand() : { PLSQLNode simpleNode = null ; } { ( ( LOOKAHEAD({isKeyword("COMMENT")}) simpleNode = Comment() ) | ( simpleNode = DDLEvent() ReadPastNextOccurrence(";") ) ) { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } /* * See https://docs.oracle.com/en/database/oracle/oracle-database/21/sqpug/SQL-Plus-reference.html#GUID-C3D4A718-56AD-4872-ADFF-A216FF70EDF2 */ ASTSqlPlusCommand SqlPlusCommand() : { StringBuilder sb = new StringBuilder(); } { ( // e.g. SHOW ERRORS, GRANT EXECUTE ON ... TO ... // SQLPLUS commands ( "@@" ( <_CHARACTER> | | ) * | "@" ( <_CHARACTER> | | ) * | LOOKAHEAD({isKeyword("ACCEPT")}) KEYWORD("ACCEPT") | LOOKAHEAD({isKeyword("ACC")}) KEYWORD("ACC") | LOOKAHEAD({isKeyword("ARCHIVE")}) KEYWORD("ARCHIVE") {sb.append(token.getImage()).append(' ');} "LOG" {sb.append(token.getImage()).append(' ');} KEYWORD("LIST") | | LOOKAHEAD({isKeyword("BREAK")}) KEYWORD("BREAK") | LOOKAHEAD({isKeyword("BTITLE")}) KEYWORD("BTITLE") | LOOKAHEAD({isKeyword("CLEAR")}) KEYWORD("CLEAR") | | LOOKAHEAD({isKeyword("COL")}) KEYWORD("COL") | LOOKAHEAD({isKeyword("COMPUTE")}) KEYWORD("COMPUTE") | LOOKAHEAD({isKeyword("COMP")}) KEYWORD("COMP") | | LOOKAHEAD({isKeyword("CONN")}) KEYWORD("CONN") | LOOKAHEAD({isKeyword("COPY")}) KEYWORD("COPY") | LOOKAHEAD({isKeyword("DEFINE")}) KEYWORD("DEFINE") | LOOKAHEAD({isKeyword("DEF")}) KEYWORD("DEF") | LOOKAHEAD({isKeyword("DESCRIBE")}) KEYWORD("DESCRIBE") | LOOKAHEAD({isKeyword("DESCR")}) KEYWORD("DESCR") | LOOKAHEAD({isKeyword("DESC")}) KEYWORD("DESC") | LOOKAHEAD({isKeyword("DISCONNECT")}) KEYWORD("DISCONNECT") | LOOKAHEAD({isKeyword("DISC")}) KEYWORD("DISC") | | LOOKAHEAD({isKeyword("EXEC")}) KEYWORD("EXEC") | | LOOKAHEAD({isKeyword("HOST")}) KEYWORD("HOST") ( <_CHARACTER> | | ) * | "$" ( <_CHARACTER> | | ) * // only works with a blank after the dollar | "!" ( <_CHARACTER> | | ) * // only works with a blank after the exclamation mark // These characters are platform-specific, anyway... | LOOKAHEAD({isKeyword("INPUT")}) KEYWORD("INPUT") | LOOKAHEAD({isKeyword("PASSWORD")}) KEYWORD("PASSWORD") | LOOKAHEAD({isKeyword("PASSW")}) KEYWORD("PASSW") | LOOKAHEAD({isKeyword("PAUSE")}) KEYWORD("PAUSE") | LOOKAHEAD({isKeyword("PRINT")}) KEYWORD("PRINT") | LOOKAHEAD({isKeyword("PROMPT")}) KEYWORD("PROMPT") ( <_CHARACTER> | | ) * | LOOKAHEAD({isKeyword("QUIT")}) KEYWORD("QUIT") | LOOKAHEAD({isKeyword("RECOVER")}) KEYWORD("RECOVER") | LOOKAHEAD({isKeyword("REMARK")}) KEYWORD("REMARK") ( <_CHARACTER> | | ) * | LOOKAHEAD({isKeyword("REM")}) KEYWORD("REM") ( <_CHARACTER> | | ) * | | LOOKAHEAD({isKeyword("SHOW")}) KEYWORD("SHOW") | LOOKAHEAD({isKeyword("SHO")}) KEYWORD("SHO") | | LOOKAHEAD({isKeyword("SPOOL")}) KEYWORD("SPOOL") | ( <_CHARACTER> | | ) * | | LOOKAHEAD({isKeyword("STORE")}) KEYWORD("STORE") | LOOKAHEAD({isKeyword("TIMING")}) KEYWORD("TIMING") | LOOKAHEAD({isKeyword("TTITLE")}) KEYWORD("TTITLE") | LOOKAHEAD({isKeyword("UNDEFINE")}) KEYWORD("UNDEFINE") | LOOKAHEAD({isKeyword("VARIABLE")}) KEYWORD("VARIABLE") | LOOKAHEAD({isKeyword("VAR")}) KEYWORD("VAR") | LOOKAHEAD({isKeyword("WHENEVER")}) KEYWORD("WHENEVER") // XQUERY is not yet supported, because it is not a single-line command // It should be handled as unknown, skipping to the next stand-alone "/". // DDL that might be encountered | LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT") | | | // Attach Library | "." ) { sb.append(token.getImage()) ; sb.append(" ") ; sb.append(Read2NextTokenOccurrence(EOL)) ; } ) { jjtThis.setImage(sb.toString()) ; return jjtThis ; } } /** * All global definitions of triggers, functions and procedures are evaluated here. * Every occurrence goes under a new PACKAGE-Node in the XML document. * This happens, cause a global "block" does not have a definied start and end token * like a package specification or a package body. * Thats why every construct is handled like a new part of the global package. * To overcome this problem, I could use an infinity lookahead - which should solve the problem * and slow down the whole parsing. * Another idea would be to lookahead the next tokens and decide wether they belong to a package definition or not. * Then I could decide to stay in this global parsing state. By now lookahead gives the parser a hint to * choose the correct way on a given base. So we can't negate it easily. * On the other hand I could also hold the global state in a global variable. * But this does not seems the correct way to solve the problem, I think. * * 2006-05-17 - Matthias Hendler - added */ ASTGlobal Global() : { } { /* Remove triggers from global processing because their schema may be defined in the trigger code itself Still wrap the trigger in a fake package but make the package name dependent on the actual schema defaulting to the globalPackageName if it cannot be found */ ( LOOKAHEAD ( ( Label() )* ( | ) ) ( Label() )* Block() ";" | LOOKAHEAD (4) ProgramUnit() ) { return jjtThis ; } } ASTBlock Block() : { } { // Included as part of statement() [ [ DeclarativeSection() ] ] (Statement())* [ (ExceptionHandler())+] [ ] // Optional END Identifier has to match the label { return jjtThis ; } } ASTPackageSpecification PackageSpecification() : { PLSQLNode simpleNode = null ; } { ( [ [ KEYWORD("REPLACE")] [ | ] ] simpleNode = ObjectNameDeclaration() ( ( ( | )) | AccessibleByClause() )* ( ( WrappedObject() ) | ( ( | ) [ DeclarativeSection() ] [ID()] ";" ) ) ) { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTPackageBody PackageBody() : { PLSQLNode simpleNode = null ; } { ( [ [ KEYWORD("REPLACE")] [ | ] ] ( | ) simpleNode = ObjectNameDeclaration() ( ( WrappedObject() ) | ( ( | ) [ DeclarativeSection() ] //SRT 20110524 Allow PLDOc in Type Bodies [ (Statement())* [ (ExceptionHandler())+] ] [ID()] ";" ) ) ) { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } /** * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/block.html#GUID-9ACEB9ED-567E-4E1A-A16A-B8B35214FC9D__CJAIABJJ */ ASTDeclarativeUnit DeclarativeUnit() : {} { ( Pragma() | LOOKAHEAD(2) ExceptionDeclaration() | LOOKAHEAD((|) QualifiedID() ( | ) ) SubTypeDefinition() | //SRT 20111117 - Special case of parameterless methods:choose method in preference to variable LOOKAHEAD((|) QualifiedID() ) ProgramUnit() | LOOKAHEAD(4) VariableOrConstantDeclaration() | CursorSpecification() | CollectionDeclaration() | //ProgramUnit()| //TypeMethod()| MethodDeclaration() | CompilationDeclarationFragment() ) { return jjtThis; } } /** * "Declare Section" * * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/block.html#GUID-9ACEB9ED-567E-4E1A-A16A-B8B35214FC9D__CJAIABJJ */ ASTDeclarativeSection DeclarativeSection() : {} { DeclarativeUnit() ( LOOKAHEAD(3) DeclarativeUnit() )* { return jjtThis; } } ASTCompilationDeclarationFragment CompilationDeclarationFragment() : { } { ( //SRT 20110601 ConditionalOrExpression() (DeclarativeUnit() | Expression() )* ( ConditionalOrExpression() (DeclarativeUnit() | Expression() )* )* ( (DeclarativeUnit() | Expression() )* )* ) { return jjtThis ; } } /** * 2006-05-22 - Matthias Hendler - Printing of custom tag "@deprecated" removed. * Printing of any custom tag added. Now user can define his own * custom tags which he can evaluate in the XSLT. * This methode also documents global functions/procedures. */ ASTProgramUnit ProgramUnit() : { } { ( [ [ KEYWORD("REPLACE")] [ | ] ] MethodDeclarator() ( WrappedObject() | /* //SRT 20110516 Cope with AUTHID for schema level functions and procedures (tokenIsAs= ( | ))? */ ( ( ( | ) ) | | AccessibleByClause() | [ ParallelClause() ] [ ID() ["." ID()] ] | [ ( ID() ["." ID()] ) | //20110531 (( | ) [ID()] "(" ID() ( "," ID() )* ")" ) ] // drvparx.IndexMapDocuments | [ "(" ID() ["." ID()] ( "," ID() ["." ID()])* ")" ] ) * [ ID() ] // body [ ( | ) //SRT ( | ) ( LOOKAHEAD(2) CallSpecTail() //{ System.err.println("Found CallSpecTail") ; } | ( [ DeclarativeSection() ] [Pragma()] // See PMD Bug #1527 (Statement())* [ (ExceptionHandler())+] [ID()] ) ) ] ";" //SRT 20110416 { System.err.println("Found terminating semi-colon") ; } ) //UnwrappedCode ) { return jjtThis ; } } ASTObjectNameDeclaration ObjectNameDeclaration() : {PLSQLNode schemaName = null, objectName = null ; } { [ LOOKAHEAD(2) schemaName = ID() "." ] objectName = ID() { jjtThis.setImage( (null == schemaName) ? objectName.getImage() : (schemaName.getImage() + "." + objectName.getImage() ) ) ; } { return jjtThis ; } } /* * See https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/formal-parameter-declaration.html#GUID-5BA8E033-96B9-439A-A4FC-4844FEC14AD8 */ ASTFormalParameter FormalParameter() : { PLSQLNode simpleNode = null ; } { ( simpleNode = ID() [ {jjtThis.setIn(true); } ] [ {jjtThis.setOut(true); } ] [ {jjtThis.setNoCopy(true); } ] ("..." | Datatype()) ( (":" "="|<_DEFAULT>) Expression() )? ) { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTMethodDeclaration MethodDeclaration() : {} { ( ProgramUnit() | TypeMethod() ) { return jjtThis ; } } ASTMethodDeclarator MethodDeclarator() : { PLSQLNode simpleNode = null ; } { ( simpleNode = ObjectNameDeclaration() ( [ FormalParameters() ] { Token nextToken; nextToken = getToken(1); //ReadAhead if (!nextToken.getImage().equalsIgnoreCase("WRAPPED") && !nextToken.getImage().equalsIgnoreCase("RETURN") ) { throw new ParseException("FUNCTION must RETURN a value or must be WRAPPED : found \"" + nextToken.getImage() + "\"" ).withLocation(nextToken); } } // There is no RETURN for a WRAPPED object [ Datatype() [ LOOKAHEAD({isKeyword("SQL_MACRO")}) SqlMacroClause() ] ] ) | simpleNode = ObjectNameDeclaration() ( [ FormalParameters() ] ) ) { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTFormalParameters FormalParameters() : { PLSQLNode simpleNode = null ; StringBuilder sb = new StringBuilder(); } { ( "(" {sb.append("(");} [ simpleNode = FormalParameter() { sb.append(simpleNode.getImage());} ( "," simpleNode = FormalParameter() { sb.append(","+simpleNode.getImage());} )* ] ")"{sb.append(")");} ) { jjtThis.setImage(sb.toString()) ; return jjtThis ; } } ASTVariableOrConstantDeclarator VariableOrConstantDeclarator() : { PLSQLNode simpleNode = null ; StringBuilder sb = new StringBuilder(); } { ( simpleNode = VariableOrConstantDeclaratorId() { sb.append(simpleNode.getImage());} [LOOKAHEAD(2) {sb.append(" " + token.getImage());} ] simpleNode = Datatype() { sb.append(" " + simpleNode.getImage());} [[ {sb.append(" " + token.getImage());} ] {sb.append(" " + token.getImage());} ] [ ( ":" "=" {sb.append(" :=");}| <_DEFAULT> {sb.append(" " + token.getImage());}) simpleNode = VariableOrConstantInitializer() { sb.append(" " + simpleNode.getImage());} ] ) { jjtThis.setImage(sb.toString()) ; return jjtThis ; } } ASTVariableOrConstantDeclaratorId VariableOrConstantDeclaratorId() : { PLSQLNode simpleNode = null ; } { simpleNode = ID() { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTVariableOrConstantInitializer VariableOrConstantInitializer() : { PLSQLNode simpleNode = null ; } { simpleNode = Expression() { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTDatatype Datatype() : { PLSQLNode simpleNode = null ; StringBuilder sb = new StringBuilder(); } { ( // this should be first simpleNode = CompilationDataType() {sb.append(simpleNode.getImage());} | LOOKAHEAD(2) simpleNode = ScalarDataTypeName() {sb.append(simpleNode.getImage());} | ( ( [LOOKAHEAD(2) "REF" {sb.append(token.getImage());} ] simpleNode = QualifiedName() {sb.append(simpleNode.getImage());} //Bug 35352414 - datatype may include dblink ["@" simpleNode = QualifiedName() {sb.append("@"+simpleNode.getImage());} ] ["%" (|){sb.append("%"+token.getImage());} ] ) ) ) { jjtThis.setImage(sb.toString()) ; return jjtThis; } } ASTCompilationDataType CompilationDataType() : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( {sb.append(" "); sb.append(token.getImage()) ; } simpleNode= ConditionalOrExpression() {sb.append(" "); sb.append(simpleNode.getImage()); } {sb.append(" "); sb.append(token.getImage()); } simpleNode = Datatype() {sb.append(" "); sb.append(simpleNode.getImage()); } ( {sb.append(" "); sb.append(token.getImage()); } simpleNode = ConditionalOrExpression() {sb.append(" "); sb.append(simpleNode.getImage()); } {sb.append(" "); sb.append(token.getImage()); } simpleNode = Datatype() {sb.append(" "); sb.append(simpleNode.getImage()); } )* ( {sb.append(" "); sb.append(token.getImage()); } simpleNode = Datatype() {sb.append(" "); sb.append(simpleNode.getImage()); } )* {sb.append(" "); sb.append(token.getImage()); } ) { jjtThis.setImage(sb.toString()) ; return jjtThis; } } /** * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/CREATE-TYPE-statement.html#GUID-389D603D-FBD0-452A-8414-240BBBC57034__OBJECT_BASE_TYPE_DEF-DE2DD0FF */ ASTCollectionTypeName CollectionTypeName() : { PLSQLNode size=null, precision=null; StringBuilder sb = new StringBuilder(); } { ( // Collection types
| | ( {sb.append( "VARYING ARRAY") ;}) ) { if (sb.length() == 0) { sb.append(token.getImage()); } } (LOOKAHEAD(2) "(" size = NumericLiteral() { sb.append("(").append(size.getImage()); } ["," precision = NumericLiteral() { sb.append(",").append(precision.getImage()); }] [ {sb.append( " CHAR") ;}] [ {sb.append( " BYTE") ;}] ")" {sb.append( ")");})? { jjtThis.setImage(sb.toString()) ; return jjtThis; } } ASTScalarDataTypeName ScalarDataTypeName() : { PLSQLNode size=null, precision=null ; StringBuilder name = new StringBuilder(); PLSQLNode characterSet = null; } { ( //Base types used in SYS.STANDARD | | | | | | // scalar types - numeric: | | | ( {name.append("DOUBLE PRECISION");}) | | | | | | | | | | | | | | // scalar types - character: ( ( | //SRT | LOOKAHEAD(2) ( {name = "LONG RAW";}) | LOOKAHEAD(2) | | LOOKAHEAD(2) ( {name.append("LONG RAW");}) | | | | | | | | | | | ) ) | // scalar types - boolean: | // composite types //SRT 20090531
| | ( {name = "VARYING ARRAY";}) | // - defined elsewhere // LOB types | | // reference types | //SRT Added to support pre-defined weak REF CURSOR ("REF" {name.append("REF CURSOR");}) | // object_type - defined elsewhere // scalar types - date/time: | LOOKAHEAD(2) ( {name.append("INTERVAL YEAR");}) | LOOKAHEAD(2) ( {name.append("INTERVAL DAY");}) |
{jjtThis.setType(ASTSqlStatement.Type.LOCK_TABLE); } |{jjtThis.setType(ASTSqlStatement.Type.MERGE); } |) Skip2NextTerminator(initiator, terminator) { return jjtThis; } } void AbstractSelectStatement(AbstractSelectStatement node) #void : {} {
"(" (LOOKAHEAD(3) Subquery() | Expression()) ")" [ "(" "+" ")" ] { return jjtThis; } } /** * 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. */ ASTTableReference TableReferenceInJoin() #TableReference : {} { QueryTableExpression() [ LOOKAHEAD(1, ID(), { // ON / USING might follow a table reference inside a inner cross join getToken(1).kind != ON && getToken(1).kind != USING // PARTITION might follow a table reference inside a outer join // or it might precede a outer join && getToken(1).kind != PARTITION // join clauses can be repeated && getToken(1).kind != INNER && getToken(1).kind != JOIN && getToken(1).kind != CROSS && getToken(1).kind != NATURAL && getToken(1).kind != FULL && getToken(1).kind != LEFT && getToken(1).kind != RIGHT && getToken(1).kind != OUTER }) TableAlias() ] { return jjtThis; } } ASTSchemaName SchemaName() : { ASTID id; } { id = ID() {jjtThis.setImage(id.getImage());} { return jjtThis; } } ASTTableName TableName() : { ASTID id; } { id = ID() {jjtThis.setImage(id.getImage());} { return jjtThis; } } /** * @see SELECT */ ASTJoinClause JoinClause() : {} { TableReferenceInJoin() (JoinClausePart())+ { return jjtThis; } } void JoinClausePart() #void : {} { LOOKAHEAD(2) InnerCrossJoinClause() | LOOKAHEAD(2) OuterJoinClause() | LOOKAHEAD(2) CrossOuterApplyClause() } ASTInnerCrossJoinClause InnerCrossJoinClause() : {} { ( [] TableReferenceInJoin() ( ConditionalOrExpression() | "(" Column() ("," Column() )* ")") | ( { jjtThis.setCross(true); } | { jjtThis.setNatural(true); } [] ) TableReferenceInJoin() ) { return jjtThis; } } ASTOuterJoinClause OuterJoinClause() : {} { [ QueryPartitionClause() ] [ { jjtThis.setNatural(true); } ] OuterJoinType() TableReferenceInJoin() [ LOOKAHEAD(2) QueryPartitionClause() ] [ ConditionalOrExpression() | "(" Column() ("," Column() )* ")" ] { return jjtThis; } } /** * @see Query Partition Clause */ ASTQueryPartitionClause QueryPartitionClause() : {} { ( LOOKAHEAD(3) Expression() (LOOKAHEAD(2) "," Expression() )* | LOOKAHEAD(3) "(" Expression() ("," Expression() )* ")" ) { return jjtThis; } } ASTOuterJoinType OuterJoinType() : {} { ( {jjtThis.setType(ASTOuterJoinType.Type.FULL); } | {jjtThis.setType(ASTOuterJoinType.Type.LEFT); } | {jjtThis.setType(ASTOuterJoinType.Type.RIGHT); } ) [ ] { return jjtThis; } } ASTCrossOuterApplyClause CrossOuterApplyClause() : {} { ( | ) ( LOOKAHEAD(2) TableReferenceInJoin() | LOOKAHEAD(2) ID() /*collection_expression*/ ) { return jjtThis; } } /** * 2011-05-15 - SRT - Added to cope with wrapped objects A wrapped function looks like this (always terminated by one or more equals signs "="):-
  "  CREATE OR REPLACE FUNCTION "TESTUSER"."GET_DATE_STRING"
/ ** Return SYSDATE formatted using the provided template.
 *
 *
 *  @param p_date_format normal TO_CHARE/TO_DATE date template
 *  @return formatted datestring
 *  @see http://www.oracle-base.com/articles/10g/WrapAndDBMS_DDL_10gR2.php#dbms_ddl
 * /
wrapped
a000000
369
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
8
89 b6
/SBrhM8+1iUO4QAih+qD2SK8kSowg8eZgcfLCNL+XlquYvSuoVah8JbRPpdHDLHn479SdFLw
v04omzJ0zOfHdMAzuHQlw+fAsr2ym9YI8I521pRTbnFVAHOOUw4JqPkIyj7wj4VwyL17nhYb
3qPVuL6SvhZTmEBnRtaErHpzaDuIpqZ0G4s=
  "
  
*/ void WrappedObject() : {} { { Token nextToken; nextToken = getToken(1); //ReadAhead while ( null != nextToken && nextToken.kind!=EOF ) { nextToken = getNextToken(); //Execute manual readahead nextToken = getToken(1); //ReadAhead 1 Token } return; } } // ============================================================================ // S T A T E M E N T S // ============================================================================ /** * 2006-05-24 - Matthias Hendler - added MERGE, EXECUTE choice and LOOKAHEAD at <LOOP> */ ASTUnlabelledStatement UnlabelledStatement() : {} { ( // small optimization: SelectIntoStatement and SelectStatement both begin with WITH or SELECT // but to distinguish the two, a complete lookahead of SelectIntoStatement needs to be parsed. // Using a lookahead of a single token first avoids this the syntatic lookahead for all other choices // not related to SELECT statements. LOOKAHEAD(|||||||||
||) SqlStatement(null,";") [";"] | LOOKAHEAD(3) ContinueStatement() ";" // CONTINUE keyword was added in 11G, so Oracle compilation supports CONTINUE as a variable name | CaseStatement() ";" | IfStatement() ";" | LOOKAHEAD( ID() "("
| | )["(" NumericLiteral() ")"] Datatype() ( )? ( Datatype())?) | "REF" [ Datatype()] //Enumeration | ( "(" Expression() ( "," Expression() )* ")" ) //Alias for existing type | Datatype() ) ) ) ";" { jjtThis.setImage(name.getImage()) ; return jjtThis ; } } ASTFieldDeclaration FieldDeclaration() : { PLSQLNode name; PLSQLNode dataType; PLSQLNode defaultValue = null; } { name = ID() Datatype() [[] ] [ (":" "=" | <_DEFAULT>) Expression() ] { jjtThis.setImage(name.getImage()) ; return jjtThis ; } } ASTCollectionTypeDefinition CollectionTypeDefinition() : {Token t = null ; } { t = { jjtThis.setImage(t.getImage()) ; return jjtThis ; } } ASTCollectionDeclaration CollectionDeclaration() : {Token t = null ; } { t = { jjtThis.setImage(t.getImage()) ; return jjtThis ; } } ASTObjectDeclaration ObjectDeclaration() : {Token t = null ; } { t = { jjtThis.setImage(t.getImage()) ; return jjtThis ; } } /** Java stored procedure, external function*/ ASTCallSpecTail CallSpecTail() : { } { // /* (/*"C"*/ | ) ( ( | (/*"C"*/ | ) )//SRT 20110516 { System.err.println("Found EXTERNAL or LANG ") ; } //These can appear in any order .... ( ( ( | | StringLiteral() ) [ "." ( | | StringLiteral() ) ] ) | ( ( | /* C */| StringLiteral() /* JAVA */ ) ) //SRT 20110517 Need to cope with CallSpecTails in ObjectTypes // Skip2NextTerminator(null,";") //SkipPastNextOccurrence(")") // Read until we have eaten the closing bracket ")" | ( ) | ( SkipPastNextOccurrence(")") // Read until we have eaten the closing bracket ")" ) )* ) /* ### or: library_name [NAME c_string_literal_name] [WITH CONTEXT] [PARAMETERS (external_parameter[, external_parameter]...)]; Where library_name is the name of your alias library, c_string_literal_name is the name of your external C procedure, and external_parameter stands for: { CONTEXT | SELF [{TDO | property}] | {parameter_name | RETURN} [property] [BY REFERENCE] [external_datatype] } where property stands for: {INDICATOR [{STRUCT | TDO}] | LENGTH | MAXLEN | CHARSETID | CHARSETFORM} ( [ | ] | | | | ) */ /* " ( ( | ( | ) ) [ ] [ "(" ( "," ( [ | [ ( [ | ] ) | | | | ] ] | ( ( | ) [ | [ ( [ | ] ) | | | | ] ] [ ] [ ] ) ) )* ")" ] ) { return; } */ { return jjtThis ; } } /** * Cursor (common part of specification and body) * * See https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/explicit-cursor-declaration-and-definition.html#GUID-38C5DBA3-9DEC-4AF2-9B5E-7B721D11A77C */ ASTCursorUnit CursorUnit() : { PLSQLNode simpleNode = null ; } { simpleNode = ID() [ FormalParameters() ] [ Datatype() ] // cursor definition [ SelectStatement() ] ";" { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } /** * This is in plsql actually called CursorDeclaration * or CursorDefinition. */ ASTCursorSpecification CursorSpecification() : {} { CursorUnit() { return jjtThis ; } } ASTExpression Expression() : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { // Need syntactic lookahead to discriminate between Assignment and a procedure call ( LOOKAHEAD( PrimaryExpression() ":" "=" ) (simpleNode = Assignment()) | (simpleNode = ConditionalOrExpression() ) | (simpleNode = CompilationExpression() ) ) {sb.append(simpleNode.getImage()); } { jjtThis.setImage(sb.toString()); return jjtThis; } } /** * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/implicit-cursor-attribute.html */ ASTImplicitCursorAttribute ImplicitCursorAttribute() : {} { "%" ( LOOKAHEAD({isKeyword("isopen")}) KEYWORD("ISOPEN") | LOOKAHEAD({isKeyword("found")}) KEYWORD("FOUND") | LOOKAHEAD({isKeyword("notfound")}) KEYWORD("NOTFOUND") | LOOKAHEAD({isKeyword("rowcount")}) KEYWORD("ROWCOUNT") | LOOKAHEAD({isKeyword("bulk_rowcount")}) KEYWORD("BULK_ROWCOUNT") "(" PrimaryExpression() ")" | LOOKAHEAD({isKeyword("bulk_exceptions")}) KEYWORD("BULK_EXCEPTIONS") ( "." "COUNT" | "(" PrimaryExpression() ")" "." (LOOKAHEAD({isKeyword("error_index")}) KEYWORD("ERROR_INDEX") | LOOKAHEAD({isKeyword("error_code")}) KEYWORD("ERROR_CODE")) ) ) { return jjtThis; } } ASTCompilationExpression CompilationExpression() : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( {sb.append(" "); sb.append(token.getImage()) ; } simpleNode = ConditionalOrExpression() {sb.append(" "); sb.append(simpleNode.getImage()); } {sb.append(" "); sb.append(token.getImage()); } simpleNode = Expression() {sb.append(" "); sb.append(simpleNode.getImage()); } ( {sb.append(" "); sb.append(token.getImage()); } simpleNode = ConditionalOrExpression() {sb.append(" "); sb.append(simpleNode.getImage()); } {sb.append(" "); sb.append(token.getImage()); } simpleNode = Expression() {sb.append(" "); sb.append(simpleNode.getImage()); } )* ( {sb.append(" "); sb.append(token.getImage()); } simpleNode = Expression() {sb.append(" "); sb.append(simpleNode.getImage()); } )* {sb.append(" "); sb.append(token.getImage()); } ) { jjtThis.setImage(sb.toString()); return jjtThis; } } ASTAssignment Assignment() : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( (simpleNode = PrimaryExpression() ) {sb.append(simpleNode.getImage());} (":" "=" ) {sb.append(" := ");} (simpleNode = Expression()) {sb.append(simpleNode.getImage());} ) { jjtThis.setImage(sb.toString()); return jjtThis; } } ASTCaseExpression CaseExpression() : {} { ( Expression() ( Expression() Expression() )+ | ( Condition() Expression() )+ ) [ Expression() ] { return jjtThis; } } /* LIKE ( Expression ) [ */ ASTLikeExpression LikeExpression() : { Token thisToken; PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( simpleNode = PrimaryExpression() { sb.append(" "); sb.append(simpleNode.getImage()); } (thisToken = ) { sb.append(thisToken.getImage());} //["(" { sb.append("(");} ] ( simpleNode = StringExpression() { sb.append(" "); sb.append(simpleNode.getImage()); } ) //[ ")" { sb.append(")");} ] [ { sb.append(" ESCAPE ");} ( { sb.append(" "); sb.append(token.getImage()); } | simpleNode = StringLiteral() { sb.append(" "); sb.append(simpleNode.getImage()); } ) ] ) ( "." simpleNode = Expression() { sb.append("."); sb.append(simpleNode.getImage()); } )* { jjtThis.setImage(sb.toString()); return jjtThis; } } /** * https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/TRIM.html */ ASTTrimExpression TrimExpression() : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( "(" { sb.append("(");} [ ( | | ) { sb.append(" "); sb.append(token.getImage()); } ] [ LOOKAHEAD(StringExpression() ) simpleNode = StringExpression() { sb.append(" "); sb.append(simpleNode.getImage()); } ] [ { sb.append(" ").append(token.getImage()); } ] simpleNode = StringExpression() { sb.append(" "); sb.append(simpleNode.getImage()); } ")" { sb.append(")");} ) { jjtThis.setImage(sb.toString()); return jjtThis; } } /* TREAT ( Expression AS datatype) CAST ( Expression AS datatype) */ ASTObjectExpression ObjectExpression() : { Token thisToken; PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( (thisToken = | thisToken = ) { sb.append(thisToken.getImage());} "(" { sb.append("(");} ( simpleNode = Expression() { sb.append(" "); sb.append(simpleNode.getImage()); } ) [
ObjectNameDeclaration() [ "=" ( | | | ) ] //object_table ( Datatype() [ [ ] KEYWORD("LEVELS") ]| //relational_table [ "(" ( LOOKAHEAD(2) OutOfLineConstraint() | TableColumn() ) ("," ( LOOKAHEAD(2) OutOfLineConstraint() | TableColumn() ))* ")" ] [LOOKAHEAD(4) ( | ) ] [LOOKAHEAD(4) ( | ) ] ) //### [PhysicalProperties()] //### [TableProperties()] Skip2NextOccurrence(";") [";"] { return jjtThis ; } } ASTTableColumn TableColumn() : { } { ID() Datatype() [ ( <_DEFAULT> [ ] (LOOKAHEAD(FunctionName() "(") FunctionCall()|LOOKAHEAD(1) UnaryExpression(true)) | LOOKAHEAD(2) [ ( <_DEFAULT> [ ] | KEYWORD("ALWAYS")) ] KEYWORD("IDENTITY") ) ] (LOOKAHEAD(1) InlineConstraint())* { return jjtThis ; } } ASTInlineConstraint InlineConstraint() : {} { [ ID() ] ( [] | { jjtThis.setType(ConstraintType.UNIQUE); } | { jjtThis.setType(ConstraintType.PRIMARY); } | { jjtThis.setType(ConstraintType.CHECK); } "(" Condition() ")" | | [ LOOKAHEAD(2) TableName() "." ] Column() | ReferencesClause() ) (ConstraintState())* { return jjtThis; } } ASTOutOfLineConstraint OutOfLineConstraint() : {} { [ ID() ] ( { jjtThis.setType(ConstraintType.UNIQUE); } "(" ID() ("," ID())* ")" | { jjtThis.setType(ConstraintType.PRIMARY); } "(" ID() ("," ID())* ")" | { jjtThis.setType(ConstraintType.FOREIGN); } "(" ID() ("," ID())* ")" ReferencesClause() | { jjtThis.setType(ConstraintType.CHECK); } "(" Condition() ")" ) (ConstraintState())* { return jjtThis; } } void ConstraintState() #void: {} { ( ( LOOKAHEAD(2) | LOOKAHEAD(2) "(" TableName() "(" Column() ("," Column())* ")" ")" | LOOKAHEAD(2) ( NumericLiteral() | ( | Skip2NextTerminator("(",")") ) )* ) | (|) | | | ) } ASTReferencesClause ReferencesClause() : {} { ObjectNameDeclaration() [ "(" ID() ( LOOKAHEAD(2) "," ID() )* ")" ] [ ( | ) ] { return jjtThis; } } ASTView View() : { PLSQLNode simpleNode = null; } { [ KEYWORD("REPLACE")] [[] ] simpleNode = ObjectNameDeclaration() ["(" ViewColumn() ("," ViewColumn())* ")"] //### OF ... WITH OBJECT IDENTIFIER... Subquery() [ SubqueryRestrictionClause() ] (";" | "/") { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTSynonym Synonym() : { PLSQLNode simpleNode = null; } { [ KEYWORD("REPLACE")] [] simpleNode = ObjectNameDeclaration() ObjectNameDeclaration() (";" | "/") { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTDirectory Directory() : { PLSQLNode simpleNode = null; } { [ KEYWORD("REPLACE")] simpleNode = ObjectNameDeclaration() StringLiteral() (";" | "/") { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTDatabaseLink DatabaseLink() : { PLSQLNode simpleNode = null; } { [ ] [ ] simpleNode = ObjectNameDeclaration() ( ( ( ) | ( UnqualifiedID() UnqualifiedID() UnqualifiedID() UnqualifiedID() ) ) | UnqualifiedID() UnqualifiedID() ) [ StringLiteral() ] (";" | "/") { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTViewColumn ViewColumn() : { } { ID() { return jjtThis ; } } /** * https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/COMMENT.html#GUID-65F447C4-6914-4823-9691-F15D52DB74D7 */ ASTComment Comment() : { } { LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT") { jjtThis.setImage(token.getImage()); } ( ((
| | ) [LOOKAHEAD(2) ID()"."] ID()) | ( [LOOKAHEAD(ID()"."ID()"."ID()) ID()"."] ID() "." ID()) ) StringLiteral() (";" | "/") { return jjtThis ; } } ASTTypeMethod TypeMethod() : { } { //inheritance CLAUSE { getToken(1); //System.err.println("typeMethod: Result of getToken(1) is \"" + starttoken.getImage() + "\"" ); } //SRT added to check Token List walking ( [ ] ( | | ) )* //[ MAP | ORDER ] //[ [] ] [ [] ] [ | ] ( | | ) MethodDeclarator() [] [] [] [] // Java or C function wrapped with PL/SQL wrapper declaration [ ( | ) ( CallSpecTail() [ ";" ] // This only exists in the type body | // SRT 20110524 Not really a Declaration any more ... ( [ DeclarativeSection() ] (Statement())* [ (ExceptionHandler())+] [ID()] ";" // This only exists in the type body ) ) ] { return jjtThis ; } } ASTTypeSpecification TypeSpecification() : { PLSQLNode simpleNode = null; } { [ [ KEYWORD("REPLACE")] [ | ] ] simpleNode = ObjectNameDeclaration() [ ] // incomplete_type_spec (empty) // object_type_spec // varray_type_spec // nested_table_type [ LOOKAHEAD(2) ] ( LOOKAHEAD(2) // ( | ) ( ( ( | )) | AccessibleByClause() ) )* //(tokenIsAsUnder= | tokenIsAs= ) //{ // // possibly, package comment is here // processPackageComment(tokenIsAsUnder); //} [ ( ObjectNameDeclaration() //SUBTYPE | LOOKAHEAD(2) ( | ) // OBJECT TYPE | LOOKAHEAD(2) ( | ) "(" "*" ")" // OPAQUE TYPE ( | | StringLiteral() ) [ "." ( | | StringLiteral() ) ] | LOOKAHEAD(2) ( | ) CollectionTypeName() Datatype() ) ] // [ LOOKAHEAD(8) //tokenIsAsUnder= // "NAME" //JavaInterfaceClass() //( | | ) //JavaInterfaceClass() //( | | ) ] [ WrappedObject() ] // //incomplete OBJECT TYPE and COLLECTION TYPEs will not have this [ ("(" ) //Try matching against keywords first to allow FINCTION and procedure as a fallback (LOOKAHEAD(2) TypeMethod() | AttributeDeclaration() | PragmaClause() )* //SRT 20111125 This part may be completely empty if is a subtype which is effectively an alias for the supertype ( "," ( TypeMethod() | LOOKAHEAD(2) AttributeDeclaration() | PragmaClause() ) )* ")" ] ( [ ] ( /*OBJECTS TYPES ONLY */ | //OBJECT TYPES ONLY | //COLLECTION TYPES ONLY ) )* //Original Type Specification may be followed by a series of modifications ( AlterTypeSpec() ) * [ (";" | "/" ) ( AlterTypeSpec() ( "/" | ";" ) )* //SRT 20110529 There may be more after the first terminator ] { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTAlterTypeSpec AlterTypeSpec() : { PLSQLNode simpleNode = null; } { simpleNode = QualifiedName() // REPLACE invoker_rights_clause AS OBJECT //alter_method_spec //(tokenAlterType = |tokenAlterType = |tokenAlterType = |tokenAlterType = ) //SRT 20110529 Each ALTER TYPE statement may contaon 0 or more ADD or DROP clause //When more than one clause exists, each clause is separated by comma "," ( [","] ( ( | ) ( // Move typeMethidMatching above attribure matvhing becausse FUNCTION is a valid attribute name ( (TypeMethod() ) //( "," ( typeMethod(,typeVersion, tokenAlterTypeString) ) )* ) | ( ( "(" )* (AttributeDeclaration() ) ( "," ( AttributeDeclaration() ) )* ( ")" )* ) | ( NumericLiteral() ) | ( Datatype() ) )+ ) | ( () ( ( ( "(" )* (Attribute() ) ( "," ( Attribute() ) )* ( ")" )* ) | ( (TypeMethod() ) //( "," ( typeMethod(,typeVersion, tokenAlterTypeString) ) )* ) )+ ) )* /* ) { System.err.println("Alter Type is " + tokenAlterType.toString()); } | ( ( (TypeMethod() ) ( "," ( TypeMethod() ) )* ) | ( [ "(" ] (QualifiedName() ) ( "," ( QualifiedName() ) )* [ ")" ] ) ) | //alter_collection_clause ( ( tokenCollectionSize = NumericLiteral() ) | ( baseType = Datatype() ) ) | */ [ KEYWORD("REPLACE") ( LOOKAHEAD(2) // ( | ) ( ( ( | )) | AccessibleByClause() ) )* ( ( | ) // OBJECT TYPE ) ("(" ) (LOOKAHEAD(2) TypeMethod() | AttributeDeclaration() ) ( "," ( LOOKAHEAD(2) TypeMethod() | AttributeDeclaration() ) )* ")" ] /* */ ( [ ] ( /*OBJECTS TYPES ONLY */ | //OBJECT TYPES ONLY | //COLLECTION TYPES ONLY ) )* //DEPENDENT HANDLING CLAUSE [ ( ) | ( ( ( [ ]
) | ( ) )* [ [ ] QualifiedName() ] ) ] { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } /* ASTTypeBody TypeBody() : { Token name=null; } { [ [ ]] (LOOKAHEAD(2) ID()".")? name=ID() ( ( WrappedObject() ) | ( ( | ) [ DeclarativeSection() ] //SRT 20110524 Allow PLDOc in Type Bodies [ (Statement())* [ (ExceptionHandler())+] ] [ID()] ";" ) ) } */ /** * Method **/ ASTAttributeDeclaration AttributeDeclaration() : { PLSQLNode simpleNode = null; } { simpleNode = ID() Datatype() { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } ASTAttribute Attribute() : { PLSQLNode simpleNode = null; } { simpleNode = ID() { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } /* This was quick cut from PRAGMA */ ASTPragmaClause PragmaClause() : { } { ( | | "(" (ID() /*| <_DEFAULT>*/) ("," (ID() | StringLiteral() /* 20110526 */) )+ ")" | "(" "," ["+"|"-"] NumericLiteral() ")" | "(" "," "," NumericLiteral() ")" ) { return jjtThis ; } } /** * Trigger * Triggers are always outside of a package. * * 2006-05-17 - Matthias Hendler - added */ /* 11g Trigger Syntax create_trigger : CREATE [OR REPLACE] TRIGGER [schema.]trigger ( simple_dml_trigger | compound_dml_trigger | non_dml_trigger ) [ FOLLOWS ( [schema.]trigger) ( , [schema.]trigger)* ] [ ENABLE | DISABLE ] ( WHEN ( trigger_condition ) trigger_body simple_dml_trigger : (BEFORE |AFTER | INSTEAD OF) dml_event_clause [ referencing_clause ] [ FOR EACH ROW ] compound_dml_trigger : FOR dml_event_clause [ referencing_clause ] non_dml_trigger : (BEFORE> | ) (DDLEvent | database_event) ( OR (DDLEvent | database_event))* ON (DATABASE | [schema.]SCHEMA trigger_body : (plsql_block | compound_trigger_block | CALL routine_clause) dml_event_clause: ( DELETE | INSERT | UPDATE [ OF column (, column ) ] ) ON ( (schema.table | NESTED TABLE nested_table_column OF [schema.]view ) referencing_clause: REFERENCING (OLD AS old_alias | NEW AS new_alias | PARENT AS parent_alias )* compound_trigger_block : COMPOUND TRIGGER declare_section (timing_point_section)+ END [trigger_name] ; timing_point_section: ( BEFORE STATEMENT IS tps_body END BEFORE STATEMENT |BEFORE EACH ROW IS tps_body END BEFORE EACH ROW |AFTER STATEMENT IS tps_body END AFTER STATEMENT |AFTER EACH ROW IS tps_body END AFTER EACH ROW ) tps_body: (statement)+ (EXCEPTION exception_handler )* */ ASTTriggerUnit TriggerUnit() : { PLSQLNode simpleNode = null ; } { [ [ KEYWORD("REPLACE")] [ | ] ] () simpleNode = ObjectNameDeclaration() // simple_dml_trigger | compound_dml_trigger | non_dml_trigger // simple_dml_trigger : ( | | | // Incorporate 11G Compound DML Trigger ) //dml_event_clause ( (( | | ) [LOOKAHEAD(6) ID() ("," ID() )*] ) | NonDMLEvent() ) ( ( (( | | ) [LOOKAHEAD(6) ID() ("," ID() )* ] ) | NonDMLEvent() ) )* ( //11G non_dml_trigger |LOOKAHEAD(2)
ID() [LOOKAHEAD(2) ID()"."] ID() |[LOOKAHEAD(2) ID()"."] ID() //includes 11g schema. table === ) // referencing_clause - there may be ZERO subclauses [LOOKAHEAD({isKeyword("REFERENCING")}) KEYWORD("REFERENCING") (( | | ) ID())*] [] // end of simple_dml_trigger (incorporating compound_dml_trigger ) [ [|] ] // 11G Syntax to specify Cross Edition Trigger [ (|) ( [LOOKAHEAD(2) ID() "."] ID() ) ( "," ( [ LOOKAHEAD(2) ID() "."] ID() ) )* ] // 11G Syntax to specify trigger firing order [ | ] // 11G Syntax can create the trigger enabled or disabled explicitly [ "(" ConditionalOrExpression() ")" ] //Trigger Body follows : //plsql_block | compound_trigger_block | routine ( PrimaryExpression() ";" //compound_trigger_block | CompoundTriggerBlock() |//plsql_block Block() ";" //| // routine ) { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } /** * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/CREATE-TRIGGER-statement.html#GUID-AF9E33F1-64D1-4382-A6A4-EC33C36F237B__GUID-2CD49225-7507-458B-8BDF-21C56AFC3527 * * Note: The DeclarativeSection (declare_section) before BEGIN is not documented, but might be valid. See #4270 */ ASTTriggerTimingPointSection TriggerTimingPointSection() : { StringBuilder sb = new StringBuilder(); } { ( | | ) { sb.append(token.getImage()) ; } ( | ) {sb.append(" "); sb.append(token.getImage()) ; } [ DeclarativeSection() ] (Statement())+ [ (ExceptionHandler())+ ] ( | | ) ( | ) ";" { //Add a TRIGGER ENTRY for each timing point section } { jjtThis.setImage(sb.toString()) ; return jjtThis ; } } /** * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/CREATE-TRIGGER-statement.html#GUID-AF9E33F1-64D1-4382-A6A4-EC33C36F237B__CJACFCDJ */ ASTCompoundTriggerBlock CompoundTriggerBlock() : {} { [ DeclarativeSection() ] (TriggerTimingPointSection())+ [ID()] ";" { return jjtThis; } } /* non_dml_trigger : (BEFORE> | ) (DDLEvent | database_event) ( OR (DDLEvent | database_event))* ON (DATABASE | [schema.]SCHEMA */ ASTNonDMLTrigger NonDMLTrigger() : { } { ( | ) ( DDLEvent() | DatabaseEvent() ) ( ( DDLEvent() | DatabaseEvent() ) )* ( | [LOOKAHEAD(2) ID()"."] ) { return jjtThis ; } } /** * https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Types-of-SQL-Statements.html#GUID-FD9A8CB4-6B9A-44E5-B114-EFB8DA76FC88 */ ASTDDLEvent DDLEvent(): {} { ( | | | | LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT") | | | // | | | // | | | | ) { jjtThis.setImage(token.getImage()) ; jjtThis.value = token ; return jjtThis ; } } ASTDatabaseEvent DatabaseEvent(): {} { ( | | | | | ) { jjtThis.setImage(token.getImage()) ; jjtThis.value = token ; return jjtThis ; } } ASTNonDMLEvent NonDMLEvent(): {} { (DDLEvent() | DatabaseEvent()) { return jjtThis; } } /* When DBMS_METADATA.GET_DDL returns a trigger, it can come in 2 DDL statements. The first is the CREATE OR REPLACE TRIGER statement; the second is an ALTER TRIGGER statement, enabling or disabling the trigger. Unlike the ALTER TYPE, it does not seem to alter the structure of the object. */ void AlterTrigger() : {} { Skip2NextTerminator(null,";") ";" { return; } } // Copyright (C) 2002 Albert Tumanov /* WHITE SPACE */ SKIP : { //Tracker Issue 1433480 - Skip2 and SkipPast need to be able to check for EOL - so the characters //cannot be SKIPped //" " | "\t" | "\n" | "\r" | "\f" " " | "\t" | "\f" } /* This Grammar (or JavaCC) has several bugs. Currently (2021-02-26) it fails for things like - dbms_lob.trim(...), - scalar subqueries in the WHERE-clause, - TREAT(object AS type), just to name a few. Running PMD over a code base of approx. 1100 DDL scripts from a commercial product, the parser failed for 182 files. About 10% of these parsing errors actually showed flaws in the code, e.g. "\u2013" instead of "-", or utf-8 encoding instead of windows-1252 (which is the standard for the product's scripts). Still, ~ 15% of perfectly valid DDL scripts could not be parsed by PMD. Nevertheless it should be best practice to call PMD for _every_ DDL script. See /docs/pages/languages/plsql.md for further explanations on how to use PMD-EXCLUDE-BEGIN and PMD-EXCLUDE-END. */ /* COMMENTS */ MORE : { <"/**" ~["/"]> : IN_FORMAL_COMMENT | "/*" : IN_MULTI_LINE_COMMENT | <"--" (" ")* "PMD-EXCLUDE-BEGIN" (" ")* (":")? (" ")* (~["\n", "\r"])*> : IN_PARSING_EXCLUSION } SPECIAL_TOKEN : { { String excluded_source = matchedToken.getImage(); exclusions.add(new ParsingExclusion(matchedToken.getReportLocation().getStartLine(), matchedToken.getReportLocation().getEndLine(), excluded_source)); } : DEFAULT } SPECIAL_TOKEN : { //Remove terminating EOL from Single Comment Token definition: it causes failurs if no other newline exists before next production } //Tracker Issue 1433480 - Skip2 and SkipPast need to be able to check for EOL - so it cannot be SKIPped SPECIAL_TOKEN : { } SPECIAL_TOKEN : { : DEFAULT } SPECIAL_TOKEN : { : DEFAULT } /* SKIP : { : DEFAULT } */ MORE : { < ~[] > } /* PSEUDO-RESERVED WORDS */ TOKEN [IGNORE_CASE]: { | | | | | | | // PRAGMA INLINE } /** * PL/SQL RESERVED WORDS * https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/plsql-reserved-words-keywords.html * * See also pseudo-production #RESERVED_WORD(). */ TOKEN [IGNORE_CASE]: { | | | | | | | | | | | | | | | | | | | | | | <_DEFAULT: "DEFAULT"> | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | } /** * Tokens, that are not reserved words, but used as keywords... * These are therefore valid identifiers */ TOKEN [IGNORE_CASE]: { | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | // Needed for 11G Trigger Syntax | | | | | | // Needed for 11G Trigger Syntax | | | | | | | // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | // | // Mark the start of a Q-quoted string, e.g. Q'[ This string contains a single-quote(') ]' | | | | | | | | | | //SRT 2011-04-17 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | //SRT 2011-04-17 // are they reserved or not ? // most are not reserved, but cannot use just define them - might be resolved as identifiers // | // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //XE testing | //XE testing | //XE testing non-PLSQL functions | //XE testing non-PLSQL functions | //XE testing non-PLSQL functions | //XE testing non-PLSQL functions | //XE testing non-PLSQL functions | //XE testing non-PLSQL functions | //XE testing non-PLSQL functions | //XE testing non-PLSQL functions | //XE testing non-PLSQL functions //SQLPlus Commands | | | | | | | | | | | | | | | | | | | | | | | | | | | | } /** * 2006-05-20 - Matthias Hendler - Added #GERMAN_SPECIAL_CHARACTERS and #SPECIAL_CHARACTERS. * Added ":" to */ TOKEN : { | | < #ID_SIMPLE: ("$" | ":" | ) ( | | "$" | "_" | "#" )* > | < IDENTIFIER: | ( ( | | "$" | "_" | "#" )* ) | // todo separate quoted identifier into other token ( "\"" "\"" ) > | < #LEXICAL_PARAMETER: ( ("&&" | "&") ( ( | | "$" | "_" | "#" )+ (".")? )? ) > | < UNSIGNED_NUMERIC_LITERAL: ( ["e","E"] (["-","+"])? )? (["f","F","d","D"])? > | < #FLOAT_LITERAL: ( "." )? | "." > | < #INTEGER_LITERAL: ( )+ > | < #_WHATEVER_CHARACTER_WO_APOSTROPHE: (~["'"]) > | < CHARACTER_LITERAL: "'" (<_WHATEVER_CHARACTER_WO_APOSTROPHE> | )? "'" > | < #_WHATEVER_CHARACTER_WO_QUOTE: (~["\""]) > | < QUOTED_LITERAL: "\"" (<_WHATEVER_CHARACTER_WO_QUOTE> | | "\\\"")* "\"" > | < JAVA_INTERFACE_CLASS: ( "SQLData" | "CustomDatum" | "OraData" ) > //| //< #BOOLEAN_LITERAL: "TRUE" | "FALSE" > | < #GERMAN_SPECIAL_CHARACTERS: "Ä" | "Ö" | "Ü" | "ä" | "ü" | "ö" | "ß" > | < #LETTER: ["A"-"Z"] | ["a"-"z"] | > | < #DIGIT: ["0"-"9"]> | < _CHARACTER: <_CHARACTER_WO_ASTERISK> | "'" > | < #_CHARACTER_WO_ASTERISK: | | "(" | ")" | "+" | "-" | "*" | "/" | "<" | ">" | "=" | "!" | "~" | "^" | ";" | ":" | "." | "@" | "%" | "," | "\"" | "#" | "$" | "_" | "|" | "{" | "}" | "?" | "[" | "]" | " " | "\t" > | < SPECIAL_CHARACTERS: "á" | "Ž" | "™" | "š" | "„" | "”" | "ý" | "²" | "€" | "³" | "µ"> | < DELIMITER: "+" | "%" | "'" | "\"" | "." | "/" | "(" | ")" | ":" | "," | "*" | "=" | "<" | ">" | "@" | ";" | "-"> } /** * https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA */ MORE : { < #_ALTERNATIVE_QUOTING_STRING_LITERAL: (["q","Q"]) "'[" (~["]"] | "]" ~["'"] )* "]" | (["q","Q"]) "'{" (~["}"] | "}" ~["'"] )* "}" | (["q","Q"]) "'<" (~[">"] | ">" ~["'"] )* ">" | (["q","Q"]) "'(" (~[")"] | ")" ~["'"] )* ")" > | <(["n","N"])? "'" (<_WHATEVER_CHARACTER_WO_APOSTROPHE> | | "''")*> { input_stream.backup(1); } : IN_STRING_LITERAL_TOKENIZE | <(["n","N"])? <_ALTERNATIVE_QUOTING_STRING_LITERAL>> { input_stream.backup(1); } : IN_STRING_LITERAL_TOKENIZE // special handling for custom quote delimiters | <(["n","N"])? (["q","Q"]) "'" (~[" ", "\t", "\r", "\n", "[", "{", "<", "("])> : IN_STRING_LITERAL } MORE : { <~["'"]> | <"'"> { Chars image = input_stream.getTokenImageCs(); int quoteDelimiter = image.charAt(2); if (image.charAt(0) == 'n' || image.charAt(0) == 'N') { quoteDelimiter = image.charAt(3); } int beforeQuote = image.charAt(image.length() - 2); if (quoteDelimiter == beforeQuote) { input_stream.backup(2); SwitchTo(IN_STRING_LITERAL_TOKENIZE); } } } TOKEN : { : DEFAULT } /** * PL/SQL Reserved words * * https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/plsql-reserved-words-keywords.html * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/plsql-language-fundamentals.html#GUID-53E09662-5AD4-4530-8C6B-FF3F7C7430D5 * * Note: This production is not used, it is just here for reference of collecting all reserved words. * Reserved words _cannot_ be used a identifiers. */ void RESERVED_WORD() #void: {} { | | | | | | | | | | | | | | | | | | | | | | <_DEFAULT> | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | } void KEYWORD(String id) #void: {} { { if (!isKeyword(id)) { throw new ParseException("Encountered \"" + getToken(1).getImage() + "\" " + "Was expecting: \"" + id + "\"").withLocation(token); } } } /** * PL/SQL Keywords. They can be used as ordinary identifiers, but it is not recommended. * * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/plsql-language-fundamentals.html#GUID-53E09662-5AD4-4530-8C6B-FF3F7C7430D5 * * @deprecated This is only used to generate a node class */ // @Deprecated ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {}{KEYWORD_NOT_RESERVED() {return jjtThis;}} private void KEYWORD_NOT_RESERVED () #void: {} { // PL/SQL UNRESERVED KEYWORDS - V$RESERVED.RESERVED='N' "REF" | "LAST" | "TRIM" | "OVER" | "UNBOUNDED" | "PRECEDING" | "FOLLOWING" | "WITHIN" | "OVERFLOW" | "ERROR" | "WITHOUT" | "COUNT" | "SUBPARTITION" | "LOG" | "ERRORS" | "REJECT" | "UNLIMITED" | | | //| //| //| test_unreserved_keyword.pks //| //| | //| test_unreserved_keyword.pks //| //| //| //| | | //| //| //| //| //| | //| //| //| //| | //| //| | | | | | //PLDoc Bug 3512149 test_unreserved_keyword.pks //| | | //| | //| //| //| //| //| //| //| //| //| | //| test_unreserved_keyword.pks //| | //-test_unreserved_keyword.pks //| //| //| //| //| //| //| //| //| //| //| //| | //| //| //| //| | | //| //| //| //| test_unreserved_keyword.pks //| //| | //| //| //| // RESERVED WORD !!! | //test_unreserved_keyword.pks //| //| //| //| | //| //| | | //| //| //| //| //| | | | | //| //| //| //| //| //| //| | //| | //| //| | //| | | //| //| //| | //| //| //| //| //| //| | //| //| //| //| //| //| | //| //| //| | //| | //| //| | | //| | //| //| //| //| //| //| | | //| | //| //| //| //| | //| //| //| //| | | //| //| //| //| | //| //| //| //| | //| //| | //| //| | | //| | | //| //| //| //| //| //| //| //| //| //| | | //| | //| //| //| //| //| //| //| //| //| //| //| | //| //| //| | //| //| //| //| | | //| //| //| | | //| //| //| //| //| //| //| //| //| //| | //| //| //| //| | | | //| //| | //| //| //| //| //| //| //| | | //| //| | //| | //| //| //| //| //| //| //| //| //| //| | //| //| //| //| //| //| //| //| | | //| | //| //| //| //| | //| //| //| //| | | //| //| //| //| | //| //| //| //| | //| //| //| //|
| //| //| //| //| | //| //| //| //| //| //| //| //| //| | //| //| //| | //| //| //| //| //| //| //| //| //| //| //| //| //| //| //| //| //| //| //| //| //| | //| | | // FORALL i I INDICES OF collection - SPARSE COLLECTIONS | //| //| //| | //| //| //| | //| //| | //| //| | //| //| //| //| | | | //| //| //| | //| | //| //| //| | //| //| //| //| | | //| //| //| //| | | | //| | | //| | | | | | //| //| | //| //| //| //| //| //| //| //| //| //| | | //| //|
//| //| //| //| | // Bug 3512150 //| //| //| //| //| //| //| //| //| //| //| | //| //| //| //| //| //| //| | //| | //| //| //| | //| //| //| //| //| //| //| | //| //| | //| //| //| //| //| //| //| //| | //| | //| //| //| | //| | //| | //| //| //| | //|