/** Creates a DrillScan. */ public DrillScanRel( final RelOptCluster cluster, final RelTraitSet traits, final RelOptTable table) { // By default, scan does not support project pushdown. // Decision whether push projects into scan will be made solely in DrillPushProjIntoScanRule. this(cluster, traits, table, table.getRowType(), GroupScan.ALL_COLUMNS); this.settings = PrelUtil.getPlannerSettings(cluster.getPlanner()); }
/** Creates a DrillScanRel for a particular GroupScan */ public DrillScanRel( final RelOptCluster cluster, final RelTraitSet traits, final RelOptTable table, final GroupScan groupScan, final RelDataType rowType, final List<SchemaPath> columns) { super(DRILL_LOGICAL, cluster, traits, table); this.rowType = rowType; this.columns = columns; this.groupScan = groupScan; this.settings = PrelUtil.getPlannerSettings(cluster.getPlanner()); }
/// TODO: this method is same as the one for ScanPrel...eventually we should consolidate /// this and few other methods in a common base class which would be extended /// by both logical and physical rels. @Override public RelOptCost computeSelfCost(final RelOptPlanner planner) { final ScanStats stats = groupScan.getScanStats(settings); int columnCount = getRowType().getFieldCount(); double ioCost = 0; boolean isStarQuery = Iterables.tryFind( getRowType().getFieldNames(), new Predicate<String>() { @Override public boolean apply(String input) { return Preconditions.checkNotNull(input).equals("*"); } }) .isPresent(); if (isStarQuery) { columnCount = STAR_COLUMN_COST; } // double rowCount = RelMetadataQuery.getRowCount(this); double rowCount = stats.getRecordCount(); if (rowCount < 1) { rowCount = 1; } if (PrelUtil.getSettings(getCluster()).useDefaultCosting()) { return planner .getCostFactory() .makeCost(rowCount * columnCount, stats.getCpuCost(), stats.getDiskCost()); } double cpuCost = rowCount * columnCount; // for now, assume cpu cost is proportional to row count. // Even though scan is reading from disk, in the currently generated plans all plans will // need to read the same amount of data, so keeping the disk io cost 0 is ok for now. // In the future we might consider alternative scans that go against projections or // different compression schemes etc that affect the amount of data read. Such alternatives // would affect both cpu and io cost. DrillCostFactory costFactory = (DrillCostFactory) planner.getCostFactory(); return costFactory.makeCost(rowCount, cpuCost, ioCost, 0); }
/** Creates a DrillScan. */ public DrillScanRel( final RelOptCluster cluster, final RelTraitSet traits, final RelOptTable table, final RelDataType rowType, final List<SchemaPath> columns) { super(DRILL_LOGICAL, cluster, traits, table); this.settings = PrelUtil.getPlannerSettings(cluster.getPlanner()); this.rowType = rowType; if (columns == null) { // planner asks to scan all of the columns this.columns = ColumnList.all(); } else if (columns.size() == 0) { // planner asks to skip all of the columns this.columns = ColumnList.none(); } else { // planner asks to scan some columns this.columns = ColumnList.some(columns); } try { this.groupScan = drillTable.getGroupScan().clone(this.columns); } catch (final IOException e) { throw new DrillRuntimeException("Failure creating scan.", e); } }
protected void doMatch( RelOptRuleCall call, ScanPrel scan, ProjectPrel project, FilterPrel filter) { final RexNode condition = filter.getCondition(); InfoSchemaGroupScan groupScan = (InfoSchemaGroupScan) scan.getGroupScan(); if (groupScan.isFilterPushedDown()) { return; } LogicalExpression conditionExp = DrillOptiq.toDrill( new DrillParseContext(PrelUtil.getPlannerSettings(call.getPlanner())), project != null ? project : scan, condition); InfoSchemaFilterBuilder filterBuilder = new InfoSchemaFilterBuilder(conditionExp); InfoSchemaFilter infoSchemaFilter = filterBuilder.build(); if (infoSchemaFilter == null) { return; // no filter pushdown ==> No transformation. } final InfoSchemaGroupScan newGroupsScan = new InfoSchemaGroupScan(groupScan.getTable(), infoSchemaFilter); newGroupsScan.setFilterPushedDown(true); RelNode input = ScanPrel.create(scan, filter.getTraitSet(), newGroupsScan, scan.getRowType()); if (project != null) { input = project.copy(project.getTraitSet(), input, project.getProjects(), filter.getRowType()); } if (filterBuilder.isAllExpressionsConverted()) { // Filter can be removed as all expressions in the filter are converted and pushed to scan call.transformTo(input); } else { call.transformTo(filter.copy(filter.getTraitSet(), ImmutableList.of(input))); } }
@Override public Prel visitProject(ProjectPrel project, Object unused) throws RelConversionException { // Apply the rule to the child RelNode originalInput = ((Prel) project.getInput(0)).accept(this, null); project = (ProjectPrel) project.copy(project.getTraitSet(), Lists.newArrayList(originalInput)); List<RexNode> exprList = new ArrayList<>(); List<RelDataTypeField> relDataTypes = new ArrayList(); List<RelDataTypeField> origRelDataTypes = new ArrayList(); int i = 0; final int lastColumnReferenced = PrelUtil.getLastUsedColumnReference(project.getProjects()); if (lastColumnReferenced == -1) { return project; } final int lastRexInput = lastColumnReferenced + 1; RexVisitorComplexExprSplitter exprSplitter = new RexVisitorComplexExprSplitter(factory, funcReg, lastRexInput); for (RexNode rex : project.getChildExps()) { origRelDataTypes.add(project.getRowType().getFieldList().get(i)); i++; exprList.add(rex.accept(exprSplitter)); } List<RexNode> complexExprs = exprSplitter.getComplexExprs(); if (complexExprs.size() == 1 && findTopComplexFunc(project.getChildExps()).size() == 1) { return project; } ProjectPrel childProject; List<RexNode> allExprs = new ArrayList(); int exprIndex = 0; List<String> fieldNames = originalInput.getRowType().getFieldNames(); for (int index = 0; index < lastRexInput; index++) { RexBuilder builder = new RexBuilder(factory); allExprs.add( builder.makeInputRef(new RelDataTypeDrillImpl(new RelDataTypeHolder(), factory), index)); if (fieldNames.get(index).contains(StarColumnHelper.STAR_COLUMN)) { relDataTypes.add( new RelDataTypeFieldImpl( fieldNames.get(index), allExprs.size(), factory.createSqlType(SqlTypeName.ANY))); } else { relDataTypes.add( new RelDataTypeFieldImpl( "EXPR$" + exprIndex, allExprs.size(), factory.createSqlType(SqlTypeName.ANY))); exprIndex++; } } RexNode currRexNode; int index = lastRexInput - 1; // if the projection expressions contained complex outputs, split them into their own individual // projects if (complexExprs.size() > 0) { while (complexExprs.size() > 0) { if (index >= lastRexInput) { allExprs.remove(allExprs.size() - 1); RexBuilder builder = new RexBuilder(factory); allExprs.add( builder.makeInputRef( new RelDataTypeDrillImpl(new RelDataTypeHolder(), factory), index)); } index++; exprIndex++; currRexNode = complexExprs.remove(0); allExprs.add(currRexNode); relDataTypes.add( new RelDataTypeFieldImpl( "EXPR$" + exprIndex, allExprs.size(), factory.createSqlType(SqlTypeName.ANY))); childProject = new ProjectPrel( project.getCluster(), project.getTraitSet(), originalInput, ImmutableList.copyOf(allExprs), new RelRecordType(relDataTypes)); originalInput = childProject; } // copied from above, find a better way to do this allExprs.remove(allExprs.size() - 1); RexBuilder builder = new RexBuilder(factory); allExprs.add( builder.makeInputRef(new RelDataTypeDrillImpl(new RelDataTypeHolder(), factory), index)); relDataTypes.add( new RelDataTypeFieldImpl( "EXPR$" + index, allExprs.size(), factory.createSqlType(SqlTypeName.ANY))); } return (Prel) project.copy( project.getTraitSet(), originalInput, exprList, new RelRecordType(origRelDataTypes)); }