opensearch-project / opensearch-project/sql
Calcite Janino compilation fails when analytics-engine is parent classloader via extendedPlugins
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 176
- Forks
- 229
- Avg merge
- 2d 21h
- Merged PRs (30d)
- 43
Description
Summary
When analytics-engine is configured as the parent plugin via extendedPlugins, Calcite's Janino runtime compilation fails with CompileException: Cannot determine simple type name "org". This occurs because Janino uses the parent classloader (analytics-engine) to compile generated Java code that references SQL plugin classes (child classloader), and the parent cannot see child classes.
Environment
- Plugin configuration:
extendedPlugins = ['opensearch-job-scheduler', 'analytics-engine']
Classloader Hierarchy
analytics-engine (PARENT classloader)
├── calcite-core-1.41.0.jar (Calcite + Janino)
├── analytics-framework.jar (QueryPlanExecutor interface)
│
└── opensearch-sql (CHILD classloader)
├── core, ppl, sql, opensearch modules
├── CalciteEnumerableIndexScan, UserDefinedFunctionBuilder, etc.
└── (Calcite JARs excluded from bundle to avoid jar hell)
Root Cause
Calcite generates Java source code at runtime and compiles it using Janino. In EnumerableInterpretable.getBindable(), the classloader is hardcoded:
static Bindable getBindable(ClassDeclaration expr, String s, int fieldCount) {
ClassLoader classLoader = EnumerableInterpretable.class.getClassLoader(); // parent classloader
ICompilerFactory compilerFactory = CompilerFactoryFactory.getDefaultCompilerFactory(classLoader);
ISimpleCompiler compiler = compilerFactory.newSimpleCompiler();
compiler.setParentClassLoader(classLoader); // Janino uses PARENT classloader
compiler.cook(generatedCode); // generated code references CHILD classes → CRASH
}
The generated code references SQL plugin classes (e.g., CalciteEnumerableIndexScan), but Janino compiles with the parent classloader, which cannot see child classes.
Affected Code Paths
This same pattern exists in 4 places inside Calcite:
| Code Path | Purpose |
|---|---|
EnumerableInterpretable.getBindable() |
Enumerable plan code generation (UNION ALL, cross-cluster, etc.) |
RexExecutable.compile() |
Pushdown script compilation (parse, grok commands) |
RexExecutorImpl.reduce() |
Constant folding during planning |
Prepare.optimize() |
Overwrites custom RexExecutor before optimization |
Steps to Reproduce
- Configure SQL plugin with
extendedPlugins = ['analytics-engine'] - Exclude Calcite JARs from SQL plugin bundle (to avoid jar hell with parent)
- Run any PPL query that triggers Enumerable code generation:
source=accounts | stats sum(age) by gender | append [ source=accounts | stats count(age) by gender ]
Error
org.codehaus.commons.compiler.CompileException: Line 43, Column 3: Cannot determine simple type name "org"
at org.codehaus.janino.UnitCompiler.getReferenceType(UnitCompiler.java:7144)
at org.codehaus.janino.UnitCompiler.getReferenceType(UnitCompiler.java:7023)
at org.codehaus.janino.UnitCompiler.getReferenceType(UnitCompiler.java:7036)
The generated Java code that fails to compile:
public org.apache.calcite.linq4j.Enumerable bind(final org.apache.calcite.DataContext root) {
// Line 43 - this class is in SQL plugin (child classloader), not visible to Janino
final org.opensearch.sql.opensearch.storage.scan.CalciteEnumerableIndexScan v1stashed =
(org.opensearch.sql.opensearch.storage.scan.CalciteEnumerableIndexScan) root.get("v1stashed");
...
}
Full call chain:
PPL query (append -> UNION ALL)
-> VolcanoPlanner.findBestExp()
-> EnumerableInterpretable.toBindable()
-> EnumerableInterpretable.getBindable()
-> ClassLoader cl = EnumerableInterpretable.class.getClassLoader() // PARENT
-> compiler.setParentClassLoader(cl) // Janino uses PARENT
-> compiler.cook(generatedCode) // code refs CHILD classes
-> CompileException: "Cannot determine simple type name 'org'"
Why extendedPlugins Works for Other Parents (e.g., job-scheduler) but Not analytics-engine
The parent-child classloader works fine for normal Java method calls — the child can see parent classes and call methods on them directly. For example, the SQL plugin extends job-scheduler via extendedPlugins without any issues.
The problem is exclusively with Calcite's Janino runtime compilation:
- Calcite generates Java source code strings at runtime
- Those strings contain SQL plugin class names like
CalciteEnumerableIndexScan - Janino compiles those strings using the parent classloader (
EnumerableInterpretable.class.getClassLoader()) - The parent classloader (analytics-engine) cannot see SQL plugin classes (child)
- Janino fails to resolve
org.opensearch.sql.*classes →CompileException
Most plugins never do runtime code generation, so extendedPlugins works fine for them. Calcite's use of Janino is a unique behavior that breaks the parent-child classloader assumption.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Calcite's EnumerableInterpretable.getBindable(), then trace the related RexExecutable.compile(), RexExecutorImpl.reduce(), and Prepare.optimize() paths described in the issue. Reproduce the extendedPlugins configuration and the append query first. Done means the affected runtime compilation paths no longer fail to resolve SQL plugin classes when analytics-engine is the parent classloader.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100