@@ -358,7 +358,140 @@ 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+ CatalogReader .THREAD_LOCAL .set (catalogReader );
407+ final Bindable bindable ;
408+ try {
409+ bindable = compileWithPluginClassLoader (enumerable , parameters );
410+ } finally {
411+ CatalogReader .THREAD_LOCAL .remove ();
412+ }
413+
414+ return new PreparedResultImpl (
415+ resultType ,
416+ requireNonNull (parameterRowType , "parameterRowType" ),
417+ requireNonNull (fieldOrigins , "fieldOrigins" ),
418+ root .collation .getFieldCollations ().isEmpty ()
419+ ? ImmutableList .of ()
420+ : ImmutableList .of (root .collation ),
421+ root .rel ,
422+ mapTableModOp (isDml , root .kind ),
423+ isDml ) {
424+ @ Override
425+ public String getCode () {
426+ throw new UnsupportedOperationException ();
427+ }
428+
429+ @ Override
430+ public Bindable getBindable (Meta .CursorFactory cursorFactory ) {
431+ return bindable ;
432+ }
433+
434+ @ Override
435+ public Type getElementType () {
436+ return resultType .getFieldList ().size () == 1 ? Object .class : Object [].class ;
437+ }
438+ };
439+ }
440+
441+ /**
442+ * Compiles an EnumerableRel to a Bindable using the SQL plugin's classloader. This is
443+ * equivalent to {@code EnumerableInterpretable.toBindable()} + {@code getBindable()} but uses
444+ * this class's classloader instead of {@code EnumerableInterpretable.class.getClassLoader()}.
445+ */
446+ private static Bindable compileWithPluginClassLoader (
447+ EnumerableRel rel , java .util .Map <String , Object > parameters ) {
448+ try {
449+ org .apache .calcite .adapter .enumerable .EnumerableRelImplementor relImplementor =
450+ new org .apache .calcite .adapter .enumerable .EnumerableRelImplementor (
451+ rel .getCluster ().getRexBuilder (), parameters );
452+ org .apache .calcite .linq4j .tree .ClassDeclaration expr =
453+ relImplementor .implementRoot (rel , EnumerableRel .Prefer .ARRAY );
454+ String s =
455+ org .apache .calcite .linq4j .tree .Expressions .toString (
456+ expr .memberDeclarations , "\n " , false );
457+ Hook .JAVA_PLAN .run (s );
458+
459+ // Use this class's classloader (SQL plugin) instead of
460+ // EnumerableInterpretable.class.getClassLoader() (analytics-engine parent).
461+ // commons-compiler is in the parent classloader at runtime, so we use reflection.
462+ ClassLoader classLoader = CalciteToolsHelper .class .getClassLoader ();
463+ Class <?> factoryFactoryClass =
464+ classLoader .loadClass ("org.codehaus.commons.compiler.CompilerFactoryFactory" );
465+ Object compilerFactory =
466+ factoryFactoryClass
467+ .getMethod ("getDefaultCompilerFactory" , ClassLoader .class )
468+ .invoke (null , classLoader );
469+ Object compiler =
470+ compilerFactory .getClass ().getMethod ("newSimpleCompiler" ).invoke (compilerFactory );
471+ compiler
472+ .getClass ()
473+ .getMethod ("setParentClassLoader" , ClassLoader .class )
474+ .invoke (compiler , classLoader );
475+
476+ String fullCode =
477+ "public final class "
478+ + expr .name
479+ + " implements "
480+ + Bindable .class .getName ()
481+ + ", "
482+ + org .apache .calcite .runtime .Typed .class .getName ()
483+ + " {\n "
484+ + s
485+ + "\n }\n " ;
486+ compiler .getClass ().getMethod ("cook" , String .class ).invoke (compiler , fullCode );
487+ ClassLoader compiledClassLoader =
488+ (ClassLoader ) compiler .getClass ().getMethod ("getClassLoader" ).invoke (compiler );
489+ @ SuppressWarnings ("unchecked" )
490+ Class <Bindable > clazz = (Class <Bindable >) compiledClassLoader .loadClass (expr .name );
491+ return clazz .getDeclaredConstructors ()[0 ].newInstance () instanceof Bindable b ? b : null ;
492+ } catch (Exception e ) {
493+ throw org .apache .calcite .util .Util .throwAsRuntime (e );
494+ }
362495 }
363496
364497 @ Override
0 commit comments