@@ -358,7 +358,143 @@ public Type getElementType() {
358358 }
359359 };
360360 }
361- return super .implement (root );
361+ return implementEnumerable (root );
362+ }
363+
364+ /**
365+ * Implements the Enumerable path with classloader fix for Janino compilation. Calcite's {@code
366+ * EnumerableInterpretable.getBindable()} hardcodes {@code
367+ * EnumerableInterpretable.class.getClassLoader()} as the parent classloader for Janino. When
368+ * analytics-engine is the parent classloader (via extendedPlugins), this returns the
369+ * analytics-engine classloader which cannot see SQL plugin classes. This method replicates the
370+ * Calcite implementation but uses this class's classloader (SQL plugin, child) which can see
371+ * both parent and child classes.
372+ */
373+ private PreparedResult implementEnumerable (RelRoot root ) {
374+ Hook .PLAN_BEFORE_IMPLEMENTATION .run (root );
375+ RelDataType resultType = root .rel .getRowType ();
376+ boolean isDml = root .kind .belongsTo (SqlKind .DML );
377+ EnumerableRel enumerable = (EnumerableRel ) root .rel ;
378+
379+ if (!root .isRefTrivial ()) {
380+ List <RexNode > projects = new java .util .ArrayList <>();
381+ final RexBuilder rexBuilder = enumerable .getCluster ().getRexBuilder ();
382+ for (java .util .Map .Entry <Integer , String > field : root .fields ) {
383+ projects .add (rexBuilder .makeInputRef (enumerable , field .getKey ()));
384+ }
385+ org .apache .calcite .rex .RexProgram program =
386+ org .apache .calcite .rex .RexProgram .create (
387+ enumerable .getRowType (), projects , null , root .validatedRowType , rexBuilder );
388+ enumerable =
389+ org .apache .calcite .adapter .enumerable .EnumerableCalc .create (enumerable , program );
390+ }
391+
392+ // Access the internalParameters map via reflection. This map is shared with the
393+ // DataContext so stashed values (e.g., table scan references) are available at execution.
394+ java .util .Map <String , Object > parameters ;
395+ try {
396+ java .lang .reflect .Field f =
397+ CalcitePrepareImpl .CalcitePreparingStmt .class .getDeclaredField ("internalParameters" );
398+ f .setAccessible (true );
399+ @ SuppressWarnings ("unchecked" )
400+ java .util .Map <String , Object > p = (java .util .Map <String , Object >) f .get (this );
401+ parameters = p ;
402+ } catch (ReflectiveOperationException e ) {
403+ throw new RuntimeException ("Failed to access internalParameters" , e );
404+ }
405+
406+ // Match original CalcitePreparingStmt.implement() which puts _conformance before toBindable
407+ parameters .put ("_conformance" , context .config ().conformance ());
408+
409+ CatalogReader .THREAD_LOCAL .set (catalogReader );
410+ final Bindable bindable ;
411+ try {
412+ bindable = compileWithPluginClassLoader (enumerable , parameters );
413+ } finally {
414+ CatalogReader .THREAD_LOCAL .remove ();
415+ }
416+
417+ return new PreparedResultImpl (
418+ resultType ,
419+ requireNonNull (parameterRowType , "parameterRowType" ),
420+ requireNonNull (fieldOrigins , "fieldOrigins" ),
421+ root .collation .getFieldCollations ().isEmpty ()
422+ ? ImmutableList .of ()
423+ : ImmutableList .of (root .collation ),
424+ root .rel ,
425+ mapTableModOp (isDml , root .kind ),
426+ isDml ) {
427+ @ Override
428+ public String getCode () {
429+ throw new UnsupportedOperationException ();
430+ }
431+
432+ @ Override
433+ public Bindable getBindable (Meta .CursorFactory cursorFactory ) {
434+ return bindable ;
435+ }
436+
437+ @ Override
438+ public Type getElementType () {
439+ return resultType .getFieldList ().size () == 1 ? Object .class : Object [].class ;
440+ }
441+ };
442+ }
443+
444+ /**
445+ * Compiles an EnumerableRel to a Bindable using the SQL plugin's classloader. This is
446+ * equivalent to {@code EnumerableInterpretable.toBindable()} + {@code getBindable()} but uses
447+ * this class's classloader instead of {@code EnumerableInterpretable.class.getClassLoader()}.
448+ */
449+ private static Bindable compileWithPluginClassLoader (
450+ EnumerableRel rel , java .util .Map <String , Object > parameters ) {
451+ try {
452+ org .apache .calcite .adapter .enumerable .EnumerableRelImplementor relImplementor =
453+ new org .apache .calcite .adapter .enumerable .EnumerableRelImplementor (
454+ rel .getCluster ().getRexBuilder (), parameters );
455+ org .apache .calcite .linq4j .tree .ClassDeclaration expr =
456+ relImplementor .implementRoot (rel , EnumerableRel .Prefer .ARRAY );
457+ String s =
458+ org .apache .calcite .linq4j .tree .Expressions .toString (
459+ expr .memberDeclarations , "\n " , false );
460+ Hook .JAVA_PLAN .run (s );
461+
462+ // Use this class's classloader (SQL plugin) instead of
463+ // EnumerableInterpretable.class.getClassLoader() (analytics-engine parent).
464+ // commons-compiler is in the parent classloader at runtime, so we use reflection.
465+ ClassLoader classLoader = CalciteToolsHelper .class .getClassLoader ();
466+ Class <?> factoryFactoryClass =
467+ classLoader .loadClass ("org.codehaus.commons.compiler.CompilerFactoryFactory" );
468+ Object compilerFactory =
469+ factoryFactoryClass
470+ .getMethod ("getDefaultCompilerFactory" , ClassLoader .class )
471+ .invoke (null , classLoader );
472+ Object compiler =
473+ compilerFactory .getClass ().getMethod ("newSimpleCompiler" ).invoke (compilerFactory );
474+ compiler
475+ .getClass ()
476+ .getMethod ("setParentClassLoader" , ClassLoader .class )
477+ .invoke (compiler , classLoader );
478+
479+ String fullCode =
480+ "public final class "
481+ + expr .name
482+ + " implements "
483+ + Bindable .class .getName ()
484+ + ", "
485+ + org .apache .calcite .runtime .Typed .class .getName ()
486+ + " {\n "
487+ + s
488+ + "\n }\n " ;
489+ compiler .getClass ().getMethod ("cook" , String .class ).invoke (compiler , fullCode );
490+ ClassLoader compiledClassLoader =
491+ (ClassLoader ) compiler .getClass ().getMethod ("getClassLoader" ).invoke (compiler );
492+ @ SuppressWarnings ("unchecked" )
493+ Class <Bindable > clazz = (Class <Bindable >) compiledClassLoader .loadClass (expr .name );
494+ return clazz .getDeclaredConstructors ()[0 ].newInstance () instanceof Bindable b ? b : null ;
495+ } catch (Exception e ) {
496+ throw org .apache .calcite .util .Util .throwAsRuntime (e );
497+ }
362498 }
363499
364500 @ Override
0 commit comments