Search code examples
pythonantlr4

How to get parameter name, type and default value of Oracle PLSQL function body?


I have the following PLSQL code which I am processing with Antlr4 in Python. I ma having trouble extracting the function parameter name and related details.

CREATE OR REPLACE
  FUNCTION getcost ( p_prod_id IN VARCHAR2 , p_date IN DATE) RETURN number AS 

The ParseTree output for this:

╚═ sql_script
   ╠═ unit_statement
   ║  ╚═ create_function_body
   ║     ╠═ "CREATE" (CREATE)
   ║     ╠═ "OR" (OR)
   ║     ╠═ "REPLACE" (REPLACE)
   ║     ╠═ "FUNCTION" (FUNCTION)
   ║     ╠═ function_name
   ║     ║  ╚═ identifier
   ║     ║     ╚═ id_expression
   ║     ║        ╚═ regular_id
   ║     ║           ╚═ "getcost" (REGULAR_ID)
   ║     ╠═ "(" (LEFT_PAREN)
   ║     ╠═ parameter
   ║     ║  ╠═ parameter_name
   ║     ║  ║  ╚═ identifier
   ║     ║  ║     ╚═ id_expression
   ║     ║  ║        ╚═ regular_id
   ║     ║  ║           ╚═ "p_prod_id" (REGULAR_ID)
   ║     ║  ╠═ "IN" (IN)
   ║     ║  ╚═ type_spec
   ║     ║     ╚═ datatype
   ║     ║        ╚═ native_datatype_element
   ║     ║           ╚═ "VARCHAR2" (VARCHAR2)
   ║     ╠═ "," (COMMA)
   ║     ╠═ parameter
   ║     ║  ╠═ parameter_name
   ║     ║  ║  ╚═ identifier
   ║     ║  ║     ╚═ id_expression
   ║     ║  ║        ╚═ regular_id
   ║     ║  ║           ╚═ "p_date" (REGULAR_ID)
   ║     ║  ╠═ "IN" (IN)
   ║     ║  ╚═ type_spec
   ║     ║     ╚═ datatype
   ║     ║        ╚═ native_datatype_element
   ║     ║           ╚═ "DATE" (DATE)
   ║     ╠═ ")" (RIGHT_PAREN)
   ║     ╠═ "RETURN" (RETURN)
   ║     ╠═ type_spec
   ║     ║  ╚═ datatype
   ║     ║     ╚═ native_datatype_element
   ║     ║        ╚═ "number" (NUMBER)
   ║ 

The python code is:

        def enterParameter(self, ctx:PlSqlParser.ParameterContext):

            print(ctx.toStringTree(recog=parser))

            param_name = None
            if ctx.parameter_name:
                if ctx.parameter_name.identifier() and ctx.parameter_name.identifier().id_expression():
                    param_name = ctx.parameter_name.identifier().id_expression().regular_id().getText()

            param_type = None
            if ctx.type_spec and ctx.type_spec.datatype:
                if ctx.type_spec.datatype.native_datatype_element():
                    param_type = ctx.type_spec.datatype.native_datatype_element().getText()

            default_value = None 


            print(f"Parameter: {param_name}, Type: {param_type}, Def: {default_value}")

The output from the print statement is:

(parameter (parameter_name (identifier (id_expression (regular_id p_prod_id)))) IN (type_spec (datatype (native_datatype_element VARCHAR2))))

But I get the following error:

    if ctx.parameter_name.identifier() and ctx.parameter_name.identifier().id_expression():
AttributeError: 'function' object has no attribute 'identifier'

If I use ctx.getChild(0).getText() to get the parameter name it works, but I don't want to rely on hardcoded indices, and I also do not understand why this isn't working.


Solution

  • The parameter_name in the context is a fuction, beacuse if the context can contain more than one parameter the integer parameter defines the index of it.

    so please change you code to this:

    ctx.parameter_name().identifier()
    

    And I wonder why your function has no return value, so for multiple parameters the param_name variable will always change. Of course, this could be intentional.