Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,99 +3,70 @@
import io.substrait.examples.IsthmusAppExamples.Action;
import io.substrait.isthmus.ConverterProvider;
import io.substrait.isthmus.SubstraitToCalcite;
import io.substrait.isthmus.SubstraitToSql;
import io.substrait.plan.Plan;
import io.substrait.plan.Plan.Root;
import io.substrait.plan.ProtoPlanConverter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.hep.HepMatchOrder;
import org.apache.calcite.plan.hep.HepPlanner;
import org.apache.calcite.plan.hep.HepProgramBuilder;
import org.apache.calcite.rel.RelRoot;
import org.apache.calcite.rel.rel2sql.RelToSqlConverter;
import org.apache.calcite.rel.rules.CoreRules;
import org.apache.calcite.sql.SqlDialect;

/**
* Substrait to SQL conversions using Calcite Optimization.
*
* <p>This example follows the same structure as the "ToSql" example but shows how the
* ConverterProvider can be subclassed to update the Calcite configuration.
*
* <p>This shows how the HepPlanner can be used to optimize the plan before it's conversion to SQL.
* <p>This example shows how to convert a Substrait plan to an optimized SQL string. Isthmus handles
* the Substrait-to-Calcite conversion, after which optimization and SQL generation are standard
* Calcite operations performed by the caller.
*/
public class ToOptimizedSql implements Action {

/**
* Custom ConverterProvider.
*
* <p>Specifically overrides the SubstraitToCalcite to allow the plan to be optimised
*/
static final class OptimizingConverterProvider extends ConverterProvider {

/**
* Set of Calcite rules to use.
*
* <p>Can be configured as you wish.
*
* @return List of rules
*/
public static List<RelOptRule> simplificationRules() {
return List.of(CoreRules.FILTER_INTO_JOIN, CoreRules.FILTER_PROJECT_TRANSPOSE);
}

/** Returns a subclass of the SubstraitToCalcite class. */
@Override
protected SubstraitToCalcite getSubstraitToCalcite() {

return new SubstraitToCalcite(this) {

@Override
public RelRoot convert(Root root) {
final HepProgramBuilder programBuilder = new HepProgramBuilder();
programBuilder
.addMatchOrder(HepMatchOrder.BOTTOM_UP)
.addRuleCollection(simplificationRules());

final RelOptPlanner hepPlanner = new HepPlanner(programBuilder.build());
// convert the Substrait to the Calcite relation tree
final RelRoot convertedRoot = super.convert(root);
hepPlanner.setRoot(convertedRoot.project());

// and then call the optimizer and return the result
return convertedRoot.withRel(hepPlanner.findBestExp());
}
};
}
}
private static final List<RelOptRule> SIMPLIFICATION_RULES =
List.of(CoreRules.FILTER_INTO_JOIN, CoreRules.FILTER_PROJECT_TRANSPOSE);

@Override
public void run(String[] args) {

try {

// Load the protobuf binary file into a Substrait Plan POJO
System.out.println("Reading from " + args[0]);
final byte[] buffer = Files.readAllBytes(Paths.get(args[0]));

final io.substrait.proto.Plan proto = io.substrait.proto.Plan.parseFrom(buffer);
final ProtoPlanConverter protoToPlan = new ProtoPlanConverter();
final Plan substraitPlan = protoToPlan.from(proto);
final Plan substraitPlan = new ProtoPlanConverter().from(proto);

// Determine which SQL Dialect we want the converted queries to be in
final SqlDialect sqlDialect = SqlDialect.DatabaseProduct.MYSQL.getDialect();

// Use a custom ConverterProvider
final SubstraitToSql substraitToSql = new SubstraitToSql(new OptimizingConverterProvider());

// Convert each of the Substrait plan roots to SQL
substraitToSql.convert(substraitPlan, sqlDialect).stream()
.forEachOrdered(System.out::println);
// Configure Isthmus Utilities
final SubstraitToCalcite substraitToCalcite = new SubstraitToCalcite(new ConverterProvider());

// Configure Calcite Utilities
final SqlDialect sqlDialect = SqlDialect.DatabaseProduct.MYSQL.getDialect();
final RelToSqlConverter relToSql = new RelToSqlConverter(sqlDialect);
final HepProgramBuilder programBuilder = new HepProgramBuilder();
programBuilder.addMatchOrder(HepMatchOrder.BOTTOM_UP).addRuleCollection(SIMPLIFICATION_RULES);
final HepPlanner planner = new HepPlanner(programBuilder.build());

// Convert Substrait to SQL
for (Plan.Root root : substraitPlan.getRoots()) {
// Convert Substrait Plan Root to Calcite RelRoot
final RelRoot relRoot = substraitToCalcite.convert(root);

// Optimize Calcite RelRoot using HepPlanner
planner.setRoot(relRoot.project());
final RelRoot optimized = relRoot.withRel(planner.findBestExp());

// Generate SQL using Calcite RelToSqlConverter
final String sql =
relToSql
.visitRoot(optimized.project(true))
.asStatement()
.toSqlString(sqlDialect)
.getSql();
System.out.println(sql);
}
} catch (IOException e) {
e.printStackTrace();
}
Expand Down
Loading