private LanguageNode handleInFunction(FunctionCall fc) { ExpressionNode lhs = fc.getParameters().get(0); if (EngineConstant.COLUMN.has(lhs) && parent.isQualifyingColumn((ColumnInstance) lhs)) { // only matches if all the rhs are constant for (ExpressionNode en : fc.getParameters(1)) { if (!EngineConstant.CONSTANT.has(en)) return fc; } ColumnInstance ci = (ColumnInstance) lhs; if (!parent.isQualifyingColumn(ci.getPEColumn())) return fc; ArrayList<ExpressionNode> subexprs = new ArrayList<ExpressionNode>(); ArrayList<Part> parts = new ArrayList<Part>(); for (ExpressionNode en : fc.getParameters(1)) { ColumnInstance tci = (ColumnInstance) ci.copy(null); ConstantExpression litex = (ConstantExpression) en; ExpressionNode subeq = new FunctionCall(FunctionName.makeEquals(), tci, litex); Part p = buildPart(subeq, tci, litex); parts.add(p); subexprs.add((ExpressionNode) p.getParent()); } if (subexprs.size() > 1) { FunctionCall orcall = new FunctionCall(FunctionName.makeOr(), subexprs); OredParts pc = parent.buildOredParts(orcall, parts); if (pc.isComplete()) setComplete(pc); orcall.setGrouped(); state.put(orcall, pc); return orcall; } else { Part p = parts.get(0); return p.getParent(); } } return fc; }
protected SelectStatement buildAggCommand(SelectStatement in) { SelectStatement out = CopyVisitor.copy(in); // we're going to build // select Name, max(Engine), max(Version), max(Row_format), // sum(Rows), avg(Avg_row_length), sum(Data_length), // max(Max_data_length), sum(Index_length), // sum(Data_free), max(Auto_increment), min(Create_time), // max(Update_time), max(Check_time), // max(Collation), null /*Checksum*/, max(Create_options), max(Comment) from temp1 group by Name // eventually we will build another temp table of rewrite info (visible name, auto_inc values) // and join // but for now this is good enough List<ExpressionNode> proj = new ArrayList<ExpressionNode>(); ExpressionAlias nameColumn = null; for (ExpressionNode en : out.getProjection()) { ExpressionAlias ea = (ExpressionAlias) en; ColumnInstance ci = (ColumnInstance) ea.getTarget(); String cn = ci.getColumn().getName().getUnquotedName().get(); FunctionCall fc = null; if (maxedColumns.contains(cn)) { fc = new FunctionCall(FunctionName.makeMax(), ci); } else if (summedColumns.contains(cn)) { fc = new FunctionCall(FunctionName.makeSum(), ci); } else if ("Name".equals(cn)) { fc = null; nameColumn = ea; } else if ("Avg_row_length".equals(cn)) { fc = new FunctionCall( FunctionName.makeRound(), new FunctionCall(FunctionName.makeAvg(), ci)); } else if ("Create_time".equals(cn)) { fc = new FunctionCall(FunctionName.makeMin(), ci); } else if ("Checksum".equals(cn)) { fc = null; ea = new ExpressionAlias( LiteralExpression.makeNullLiteral(), new NameAlias(ci.getColumn().getName().getUnqualified()), false); } else { throw new SchemaException(Pass.PLANNER, "Unknown show status column: " + cn); } if (fc != null) { ea = new ExpressionAlias( fc, new NameAlias(ci.getColumn().getName().getUnqualified()), false); } proj.add(ea); } out.setProjection(proj); SortingSpecification ngb = new SortingSpecification(nameColumn.buildAliasInstance(), true); ngb.setOrdering(Boolean.FALSE); out.getGroupBysEdge().add(ngb); return out; }
private EqualityPart makeNewEqualityPart( EqualityPart existing, PEColumn c, ConstantExpression litex) { TableKey tk = existing.getColumn().getColumnKey().getTableKey(); ColumnInstance nc = new ColumnInstance(c, tk.toInstance()); FunctionCall eq = new FunctionCall(FunctionName.makeEquals(), nc, litex); EqualityPart eqp = buildEqualityPart(eq, nc, litex); return eqp; }
protected AndedParts completeKey(AndedParts existing, PEColumn c, ConstantExpression litex) { EqualityPart ep = (EqualityPart) existing.getParts().get(0); EqualityPart np = makeNewEqualityPart(ep, c, litex); ArrayList<Part> subp = new ArrayList<Part>(); subp.addAll(existing.getParts()); subp.add(np); List<ExpressionNode> subexprs = Functional.apply(subp, Part.castToExpression); FunctionCall andEx = new FunctionCall(FunctionName.makeAnd(), subexprs); return buildAndedParts(andEx, subp); }
protected AndedParts completeKey(EqualityPart existing, PEColumn c, ConstantExpression litex) { EqualityPart np = makeNewEqualityPart(existing, c, litex); FunctionCall andEx = new FunctionCall( FunctionName.makeAnd(), (ExpressionNode) existing.getParent(), (ExpressionNode) np.getParent()); ArrayList<Part> subp = new ArrayList<Part>(); subp.add(existing); subp.add(np); return buildAndedParts(andEx, subp); }
private LanguageNode handleAndFunction(FunctionCall fc) { // and functions take incomplete simple parts and turn them into complete parts, if so desired ArrayList<Part> incompletes = new ArrayList<Part>(); ArrayList<ExpressionNode> ok = new ArrayList<ExpressionNode>(); ArrayList<Part> subparts = new ArrayList<Part>(); for (ExpressionNode en : fc.getParameters()) { Part p = state.get(en); if (p == null || p.isComplete()) { ok.add(en); if (p != null) subparts.add(p); continue; } incompletes.add(p); } if (incompletes.isEmpty()) return fc; // now we have the problem of a mishmash of incompletes. some may be complex, some may be // simple // some may be collections. we need to handle cases like the following: // (a = 1) and (b = 2) {a,b} (1 key) // (a = 1) and (b = 2 or b = 3) {a,b} (2 keys) // (a = 1 or a = 2) and (b = 3) {a,b} (2 keys) // (a = 1 or a = 2) and (b = 3 or b = 4) {a,b} (4 keys here) // all of the above, where the result is still not complete due to missing tenant column MultiMap<Part, ColumnKey> needed = new MultiMap<Part, ColumnKey>(); MultiMap<ColumnKey, Part> classified = new MultiMap<ColumnKey, Part>(); for (Part p : incompletes) { ListSet<ColumnKey> has = new ListSet<ColumnKey>(); has.addAll(p.getColumns()); needed.putAll(p, parent.getNeeded(has)); for (ColumnKey ck : has) { classified.put(ck, p); } } // so let's say we have a part that is (a = 1 and b = 2), needs c and tenant, and we have a // part // that is c in (1,2,3). The needed for (a = 1 and b = 2) is {c,tenant}. we'll pull (c in // (1,2,3)) // so we'll get at least (a = 1 and b = 2 and c = 3) or (a = 1 and b =2 and c = 3) ... // these we can then individually try to complete. while (!needed.isEmpty()) { combineParts(needed, classified, ok, subparts); } for (Part p : subparts) state.put(p.getParent(), p); if (ok.size() == 1) return ok.get(0); else { // what's left is a mix of unrelated and complete or incomplete subexprs. unrelated nodes // would come in from above, as would previously complete. return new FunctionCall(FunctionName.makeAnd(), ok); } }
private Part buildNewMultiMultiPart(OredParts lp, OredParts rp) { ArrayList<Part> newParts = new ArrayList<Part>(); for (Part lpc : lp.getParts()) { for (Part rpc : rp.getParts()) { newParts.add(combineParts(lpc.copy(), rpc.copy())); } } FunctionCall orcall = new FunctionCall( FunctionName.makeOr(), Functional.apply(newParts, Part.castToExpression)); OredParts op = parent.buildOredParts(orcall, newParts); if (op.isComplete()) setComplete(op); return op; }
private Part buildNewPartCollection(OredParts lp, Part rp) { // rp could be simple or complex, lp has either simple or complex elements. // the strategy here is to build a new complex part for each item in the collection, and // return a new collection. ArrayList<Part> newParts = new ArrayList<Part>(); for (Part sp : lp.getParts()) { newParts.add(buildNewComplexPart(sp, rp.copy())); } FunctionCall orcall = new FunctionCall( FunctionName.makeOr(), Functional.apply(newParts, Part.castToExpression)); OredParts op = parent.buildOredParts(orcall, newParts); if (op.isComplete()) setComplete(op); return op; }
@Override public Part copy() { List<Part> newparts = new ArrayList<Part>(); for (Part p : parts) newparts.add(p.copy()); FunctionCall newp = new FunctionCall( FunctionName.makeAnd(), Functional.apply( newparts, new UnaryFunction<ExpressionNode, Part>() { @Override public ExpressionNode evaluate(Part object) { return (ExpressionNode) object.getParent(); } })); return new AndedParts(newp, getTableKey(), newparts, complete); }
private Part buildNewComplexPart(Part lp, Part rp) { ListSet<Part> allParts = new ListSet<Part>(); allParts.addAll(lp.getParts()); allParts.addAll(rp.getParts()); FunctionCall andCall = new FunctionCall( FunctionName.makeAnd(), Functional.apply(allParts, Part.castToExpression)); AndedParts cp = parent.buildAndedParts(andCall, allParts); if (parent.isComplete(cp)) { setComplete(cp); } else if (!parent.isComplete(cp)) { AndedParts ncp = parent.maybeMakeComplete(cp); if (ncp != null) { setComplete(ncp); return ncp; } } return cp; }
public static RedistFeatureStep buildLookupJoinRedist( PlannerContext pc, RedistFeatureStep lookupTable, List<ExpressionNode> lookupJoinColumns, PEStorageGroup targetGroup, JoinEntry origEntry, List<ExpressionNode> origJoinColumns, PartitionEntry nonLookupSide) throws PEException { // we will modify the existing non lookup side projecting feature step, and add the lookup table // as a requirement to it. ProjectingFeatureStep nonLookupStep = (ProjectingFeatureStep) nonLookupSide.getStep(null); nonLookupSide.maybeForceDoublePrecision(nonLookupStep); SelectStatement lookupSelect = lookupTable.getTargetTempTable().buildSelect(pc.getContext()); SelectStatement nonLookupSelect = (SelectStatement) nonLookupStep.getPlannedStatement(); SelectStatement actualJoinStatement = DMLStatementUtils.compose(pc.getContext(), nonLookupSelect, lookupSelect); List<ExpressionNode> ands = ExpressionUtils.decomposeAndClause(actualJoinStatement.getWhereClause()); // instead of building the join spec directly, map forward the original join condition from the // joined table if it's available IndexCollector ic = new IndexCollector(); if (origEntry.getJoin().getJoin() != null) { FunctionCall mapped = (FunctionCall) actualJoinStatement .getMapper() .copyForward(origEntry.getJoin().getJoin().getJoinOn()); ands.add(mapped); ListSet<ColumnInstance> cols = ColumnInstanceCollector.getColumnInstances(mapped); for (ColumnInstance ci : cols) ic.addColumnInstance(ci); } else { Map<RewriteKey, ExpressionNode> projEntries = null; for (int i = 0; i < origJoinColumns.size(); i++) { ColumnKey mck = actualJoinStatement.getMapper().mapExpressionToColumn(origJoinColumns.get(i)); ColumnKey muck = actualJoinStatement.getMapper().mapExpressionToColumn(lookupJoinColumns.get(i)); ExpressionNode mc = null; ExpressionNode muc = null; if (mck == null || muck == null) { if (projEntries == null) { projEntries = new HashMap<RewriteKey, ExpressionNode>(); for (ExpressionNode en : actualJoinStatement.getProjectionEdge()) { ExpressionNode actual = ExpressionUtils.getTarget(en); if (actual instanceof ColumnInstance) { projEntries.put(((ColumnInstance) actual).getColumnKey(), actual); } else { projEntries.put(new ExpressionKey(actual), actual); } } } if (mck == null) mc = (ExpressionNode) projEntries.get(new ExpressionKey(origJoinColumns.get(i))).copy(null); if (muck == null) mc = (ExpressionNode) projEntries.get(new ExpressionKey(lookupJoinColumns.get(i))).copy(null); } if (mc == null) mc = mck.toInstance(); if (muc == null) muc = muck.toInstance(); if (mc instanceof ColumnInstance) ic.addColumnInstance((ColumnInstance) mc); if (muc instanceof ColumnInstance) ic.addColumnInstance((ColumnInstance) muc); FunctionCall eq = new FunctionCall(FunctionName.makeEquals(), mc, muc); ands.add(eq); } } ic.setIndexes(origEntry.getSchemaContext()); TempTable lookupEntryTarget = lookupTable.getTargetTempTable(); // everything from the lhs that's in the projection should be cleared - it's invisible // note that we do it after building the where clause so that the original join condition can be // mapped for (Iterator<ExpressionNode> iter = actualJoinStatement.getProjectionEdge().iterator(); iter.hasNext(); ) { ExpressionNode en = iter.next(); ExpressionNode targ = ExpressionUtils.getTarget(en); if (targ instanceof ColumnInstance) { ColumnInstance ci = (ColumnInstance) targ; if (ci.getTableInstance().getAbstractTable() == lookupEntryTarget) iter.remove(); } } actualJoinStatement.setWhereClause(ExpressionUtils.safeBuildAnd(ands)); actualJoinStatement.normalize(origEntry.getSchemaContext()); // build a new projecting feature step ProjectingFeatureStep lookupJoinStep = DefaultFeatureStepBuilder.INSTANCE.buildProjectingStep( origEntry.getPlannerContext(), origEntry.getFeaturePlanner(), actualJoinStatement, new ExecutionCost(false, false, null, -1), nonLookupStep.getSourceGroup(), actualJoinStatement.getDatabase(origEntry.getSchemaContext()), nonLookupStep.getDistributionVector(), null, DMLExplainReason.LOOKUP_JOIN.makeRecord()); // arrange for the children of the nonlookup side to become my children // and add the lookup table step as well lookupJoinStep.getSelfChildren().addAll(nonLookupStep.getAllChildren()); lookupJoinStep.getSelfChildren().add(lookupTable); // children must be sequential - no need to modify here List<Integer> mappedRedistOn = nonLookupSide.mapDistributedOn(origJoinColumns, actualJoinStatement); // now remove the lookup table from the mapper - have to do this pretty late - at this point // the query won't be manipulated any more actualJoinStatement.getMapper().remove(lookupEntryTarget); RedistFeatureStep out = lookupJoinStep.redist( origEntry.getPlannerContext(), origEntry.getFeaturePlanner(), new TempTableCreateOptions(Model.STATIC, targetGroup) .distributeOn(mappedRedistOn) .withRowCount(origEntry.getScore().getRowCount()), null, DMLExplainReason.LOOKUP_JOIN.makeRecord()); return out; }
/** * The redundant column is eliminated and the column order is correct according to standard SQL: * * <p>First, coalesced common columns of the two joined tables, in the order in which they occur * in the first table. * * <p>Second, columns unique to the first table, in order in which they occur in that table. * * <p>Third, columns unique to the second table, in order in which they occur in that table. */ private Collection<ExpressionNode> buildProjection( final SchemaContext sc, final Map<TableInstance, Set<ColumnKey>> tablesAndJoinColumns) { final ListSetMap<ColumnKey, ExpressionNode> projection = new ListSetMap<ColumnKey, ExpressionNode>(); /* * We need to coalesce the join columns by their unqualified names. * Build a lookup map. */ final Map<ColumnKey, Set<ColumnInstance>> toCoalesceName = new TreeMap<ColumnKey, Set<ColumnInstance>>( new Comparator<ColumnKey>() { @Override public int compare(ColumnKey c1, ColumnKey c2) { final String n1 = getColumnName(c1).get(); final String n2 = getColumnName(c2).get(); return n1.compareTo(n2); } }); /* * Here we the projection. Join columns are inserted in order in which * they occur in the first table and coalesced by name in set * containers. */ for (final Map.Entry<TableInstance, Set<ColumnKey>> te : tablesAndJoinColumns.entrySet()) { final Set<ColumnKey> entryJoinColumns = te.getValue(); for (final ColumnKey ck : entryJoinColumns) { final ColumnInstance ci = ck.toInstance(); if (!toCoalesceName.containsKey(ck)) { projection.put(ck, ci); final Set<ColumnInstance> coalescedColumns = new HashSet<ColumnInstance>(); coalescedColumns.add(ci); toCoalesceName.put(ck, coalescedColumns); } else { toCoalesceName.get(ck).add(ci); } } addColumns(sc, projection, te.getKey(), entryJoinColumns); } /* * We already have the right projection. */ if (toCoalesceName.isEmpty()) { return projection.values(); } /* * It turns out that in case of multi-join statements MySQL also sorts * adjacent join columns by the number of coalesced columns (by the * frequency with which they appear in the USING clauses). * * i.e. * "SELECT * FROM pe251C c RIGHT JOIN pe251D d USING (id1,id2,id3) LEFT OUTER JOIN pe251E e USING (id1,id3) ORDER BY c.id1;" * * Although the first-table order would be (id2, id1, id3): * "COALESCE(id2, id2), COALESCE(id1, id1, id1), COALESCE(id3, id3, id3)" * * MySQL sorts the columns as (id1, id3, id2): * "COALESCE(id1, id1, id1), COALESCE(id3, id3, id3), COALESCE(id2, id2)" * * Sort the adjacent coalesced column sets by their size using a stable * algorithm. */ final Map<ColumnKey, Set<ColumnInstance>> toCoalesceByEntry = new HashMap<ColumnKey, Set<ColumnInstance>>(toCoalesceName); final int endIndex = projection.size(); for (int i = 0; i < endIndex; ++i) { final ColumnKey ik = projection.getEntryAt(i).getKey(); if (toCoalesceByEntry.containsKey(ik)) { int j = i; while (j > 0) { final ColumnKey jk1 = projection.getEntryAt(j).getKey(); final ColumnKey jk2 = projection.getEntryAt(j - 1).getKey(); if (toCoalesceByEntry.containsKey(jk1) && toCoalesceByEntry.containsKey(jk2)) { final Set<ColumnInstance> jv1 = toCoalesceByEntry.get(jk1); final Set<ColumnInstance> jv2 = toCoalesceByEntry.get(jk2); if (jv1.size() > jv2.size()) { projection.swap(jk1, jk2); } else { break; } } else { break; } --j; } } } /* * Finally, convert the coalesced column sets into COALESCE() function * calls. */ for (final Map.Entry<ColumnKey, Set<ColumnInstance>> coalesceEntry : toCoalesceName.entrySet()) { final ColumnKey key = coalesceEntry.getKey(); final FunctionCall coalesce = new FunctionCall( FunctionName.makeCoalesce(), new ArrayList<ExpressionNode>(coalesceEntry.getValue())); final ExpressionAlias coalesceAlias = new ExpressionAlias(coalesce, new NameAlias(getColumnName(key)), false); projection.put(key, coalesceAlias); } return projection.values(); }