Search code examples
javaoracle-databaseplsqlddl

Oracle ORA-29536 on DDL-loading java source into the database


I'm looking into wrapping some java functions in pl/sql but am encountering an ORA-29536 on the colon character in for-loops when loading the java source.
I was hoping to understand why the database has difficulty with this, as it doesn't appear to be misinterpreting the colon and trying to bind, and to get any suggestions on a path forward.
I'd like to avoid refactoring the source to abolish all for-loops, and would like to avoid using the loadjava tool if possible.
Database is 12cR1, with ojdk is 1.6.0_71.

Here's an example.
Given these two trivial hello-world-type classes:

public final class HelloWorld {
    public static String greet(final String userName) {
        return "Hello " + userName;
    }
}

And:

import java.util.Arrays;
import java.util.List;

public final class LoopingTest {
    public static String greet(final String userName) {
        final List<String> emptyList = Arrays.asList(userName);
        for (final String string : emptyList) {
            System.out.println(string);
        }
        return "Hello " + userName;
    }
}

When I compile into the database, the first goes in fine and registers JAVA SOURCE and JAVA CLASS objects ok (I'm using dynamic SQL and a clob in this example since the eventual target will be >32K characters, with enough @, &, etc. to complicate escaping in a raw script):

BEGIN
    EXECUTE IMMEDIATE
    '
        CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "HelloWorld" AS 
        public final class HelloWorld {
            public static String greet(final String userName) {
                return "Hello " + userName;
            }
        }
    ';
END;
/

PL/SQL procedure successfully completed.

But the second fails to import:

BEGIN
    EXECUTE IMMEDIATE
    '
        CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "HelloWorld" AS 
        import java.util.Arrays;
        import java.util.List;

        public final class LoopingTest {
            public static String greet(final String userName) {
                final List<String> emptyList = Arrays.asList(userName);
                for (final String string : emptyList) {
                    System.out.println(string);
                }
                return "Hello " + userName;
            }
        }
    ';
END;
/

ERROR at line 1:
ORA-29536: badly formed source: Encountered "final String string :" at line 1,
column 195.

I wondered if there are any changes I can make to coax oracle into loading this.
If using loadjava is the only way to import I can accept that, but I'd prefer loading in one step DDL if possible. Thanks


Solution

  • Oracle seems to be doing some strange pre-processing on the Java code that is flagging the final String string in the for-each loop as a problem. The error isn't coming from the Java compiler - as you'd expect, since this is valid. I can't see anything about this issue, including on My Oracle Support.

    It isn't, as @JonHeller noted in an edit comment, anything to do with the string concatenation, but also isn't anything to do with dynamic SQL - in 11gR2 anyway I still see the same ORA-29536 error if I just run the DDL directly from a SQL prompt, using your second code block.

    CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "LoopingTest" AS 
    import java.util.Arrays;
    import java.util.List;
    
    public final class LoopingTest {
        public static String greet(final String userName) {
            final List<String> emptyList = Arrays.asList(userName);
            for (final String string : emptyList) {
                System.out.println(string);
            }
            return "Hello " + userName;
        }
    }
    /
    
    Error report -
    ORA-29536: badly formed source: Encountered "final String string :" at line 7, column 14.
    Was expecting one of:
        "boolean" ...
    ...
    

    As a workaround you can just remove the final keyword:

    CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "LoopingTest" AS 
    import java.util.Arrays;
    import java.util.List;
    
    public final class LoopingTest {
        public static String greet(final String userName) {
            final List<String> emptyList = Arrays.asList(userName);
            // remove 'final' to avoid ORA-29536
            // for (final String string : emptyList) {
            for (String string : emptyList) {
                System.out.println(string);
            }
            return "Hello " + userName;
        }
    }
    /
    
    Java source LoopingTest compiled
    

    Or in your original PL/SQL code:

    DECLARE
      V_SQL_TEXT CLOB := ' ' ||
                         'CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "LoopingTest" AS ' ||
                         'import java.util.Arrays; ' ||
                         'import java.util.List; ' ||
                         ' ' ||
                         'public final class LoopingTest { ' ||
                         'public static String greet(final String userName){ ' ||
                         'final List<String> emptyList = Arrays.asList(userName); ' ||
                         -- remove 'final' to avoid ORA-29536
                         -- 'for (final String string : emptyList){ ' ||
                         'for (String string : emptyList){ ' ||
                         'System.out.println(string); ' ||
                         '} ' ||
                         'return "Hello " + userName; ' ||
                         '} ' ||
                         '} ';
    BEGIN
      EXECUTE IMMEDIATE V_SQL_TEXT;
    END;
    /
    
    PL/SQL procedure successfully completed.
    

    You should consider raising a service request with Oracle, to see if they can explain and/or fix the problem.