if (p != null) {
// PlanColumn new_pc = p.getFirst();
assert (p.getFirst() != null);
new_idx = p.getSecond();
TupleValueExpression clone_exp = null;
try {
clone_exp = (TupleValueExpression) orig_pc.getExpression().clone();
} catch (CloneNotSupportedException ex) {
throw new RuntimeException(ex);
}
clone_exp.setColumnIndex(new_idx);
PlanColumn new_col = state.plannerContext.getPlanColumn(clone_exp, orig_pc.getDisplayName(), orig_pc.getSortOrder(), orig_pc.getStorage());
assert (new_col != null);
outer_output_guids.set(new_idx, new_col.guid());
// new_output_guids.add(new_col.guid());
new_sorted_output_guids.put(new_idx, new_col.guid());
if (debug.val)
LOG.debug(String.format("OUTER OFFSET %d => %d [new_guid=%d]", orig_idx, new_idx, new_col.guid()));
}
// If we don't have this PlanColumn, that means that it isn't
// being passed up from the
// outer table and therefore don't want it anymore in our output
else {
new_idx = null;
}
}
if (new_idx != null) {
assert (offset_xref.containsKey(orig_idx) == false) : orig_idx + " ==> " + offset_xref;
offset_xref.put(orig_idx, new_idx);
}
// Just because we couldn't find the offset doesn't mean it's a bad
// thing
// It might be because we projected those columns out down below in
// the tree
// and therefore we don't need to worry about them anymore.
else {
String msg = String.format("[%02d] Failed to find new offset for OUTER %s", orig_idx, orig_pc);
sb.append(msg).append("\n");
if (debug.val)
LOG.warn(msg);
}
} // FOR
if (trace.val) {
LOG.trace("Original Outer Input GUIDs: " + outer_orig_input_guids);
LOG.trace("New Outer Input GUIDs: " + outer_output_guids);
}
if (outer_output_guids.size() != offset_xref.size()) {
LOG.error("Outer Node: " + outer_node);
String temp = "";
for (int i = 0; i < outer_orig_input_guids.size(); i++) {
PlanColumn pc = state.plannerContext.get(outer_orig_input_guids.get(i));
temp += String.format("[%02d] %s\n", i, pc);
temp += ExpressionUtil.debug(pc.getExpression()) + "\n--------\n";
}
temp += "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
LOG.error("Original Outer Input GUIDs: " + outer_orig_input_guids + "\n" + temp);
temp = "";
for (int i = 0; i < outer_output_guids.size(); i++) {
PlanColumn pc = state.plannerContext.get(outer_output_guids.get(i));
temp += String.format("[%02d] %s\n", i, pc);
temp += ExpressionUtil.debug(pc.getExpression()) + "\n--------\n";
}
LOG.error("New Outer Input GUIDs: " + outer_output_guids + "\n" + temp);
LOG.error("Output Xref Offsets: " + offset_xref);
// LOG.info("Trace Information:\n" + sb);
LOG.error("Unexpected Query Plan\n" + PlanNodeUtil.debug(PlanNodeUtil.getRoot(node)));
}
assert (outer_output_guids.size() == offset_xref.size()) : "outer_new_input_guids size: " + outer_output_guids.size() + " offset_xref size: " + offset_xref.size();
// add the sorted columns into new_columns list
final List<Integer> new_output_guids = new ArrayList<Integer>(new_sorted_output_guids.values());
// For the inner table, we always have to offset ourselves based on the
// size of the new outer table
int offset = outer_output_guids.size();
AbstractPlanNode inner_node = null;
// These are the set of expressions for the join clause that we need to
// fix their offsets for
final Collection<AbstractExpression> expressions_to_fix = PlanNodeUtil.getExpressionsForPlanNode(node);
// --------------------------------------------
// NEST LOOP
// --------------------------------------------
if (node.getChildPlanNodeCount() > 1) {
assert (node instanceof NestLoopPlanNode);
inner_node = node.getChild(1);
if (debug.val)
LOG.debug("Calculating INNER offsets from child node: " + inner_node);
List<Integer> inner_orig_input_guids = state.orig_node_output.get(inner_node);
assert (inner_orig_input_guids != null);
List<Integer> inner_new_input_guids = inner_node.getOutputColumnGUIDs();
for (int orig_idx = 0, cnt = inner_orig_input_guids.size(); orig_idx < cnt; orig_idx++) {
int col_guid = inner_orig_input_guids.get(orig_idx);
// Find the new index of this same PlanColumn guid
int new_idx = inner_new_input_guids.indexOf(col_guid);
if (new_idx != -1) {
int offset_orig_idx = outer_orig_input_guids.size() + orig_idx;
int offset_new_idx = offset + new_idx;
if (trace.val)
LOG.trace(String.format("INNER NODE OFFSET %d => %d", offset_orig_idx, offset_new_idx));
assert (offset_xref.containsKey(offset_orig_idx) == false) : orig_idx + " ==> " + offset_xref;
offset_xref.put(offset_orig_idx, offset_new_idx);
new_output_guids.add(col_guid);
// sorted_new_output_guids.put(new_idx, col_guid);
} else {
PlanColumn pc = state.plannerContext.get(col_guid);
LOG.warn("Failed to find new offset for INNER " + pc);
}
} // FOR
if (trace.val)
LOG.trace("Original Inner Input GUIDs: " + inner_orig_input_guids);
if (trace.val)
LOG.trace("New Inner Input GUIDs: " + inner_new_input_guids);
// ---------------------------------------------------
// NEST LOOP INDEX
// ---------------------------------------------------
} else {
// Otherwise, just grab all of the columns for the target table in
// the inline scan
assert (node instanceof NestLoopIndexPlanNode);
IndexScanPlanNode idx_node = node.getInlinePlanNode(PlanNodeType.INDEXSCAN);
assert (idx_node != null);
inner_node = idx_node;
Table catalog_tbl = null;
try {
catalog_tbl = CollectionUtil.first(CatalogUtil.getReferencedTablesForPlanNode(state.catalog_db, idx_node));
} catch (Exception ex) {
LOG.fatal(ex);
throw new RuntimeException(ex);
}
assert (catalog_tbl != null);
if (debug.val)
LOG.debug("Calculating INNER offsets from INLINE Scan: " + catalog_tbl);
for (Column catalog_col : CatalogUtil.getSortedCatalogItems(catalog_tbl.getColumns(), "index")) {
int i = catalog_col.getIndex();
int offset_orig_idx = outer_orig_input_guids.size() + i;
int offset_new_idx = offset + i;
if (trace.val)
LOG.trace(String.format("INNER INLINE OFFSET %d => %d", offset_orig_idx, offset_new_idx));
offset_xref.put(offset_orig_idx, offset_new_idx);
// Since we're going in order, we know what column is at this
// position.
// That means we can grab the catalog object and convert it to a
// PlanColumn GUID
// Always try make a new PlanColumn and update the
// TupleValueExpresion index
// This ensures that we always get the ordering correct
// int orig_guid =
// idx_node.getOutputColumnGUID(offset_orig_idx);
int orig_guid = CollectionUtil.first(state.column_guid_xref.get(catalog_col));
assert (orig_guid != -1);
PlanColumn orig_pc = state.plannerContext.get(orig_guid);
assert (orig_pc != null);
// PlanColumn new_pc = null;
// int new_idx = 0;
// for (Integer guid : idx_node.getOutputColumnGUIDs()) {
// PlanColumn pc = state.m_context.get(guid);
// assert (pc != null);
// if (pc.equals(orig_pc, true, true)) {
// if (trace.val)
// LOG.trace(String.format("[%02d] Found inline output PlanColumn match:\nORIG: %s\nNEW: %s",
// new_idx, orig_pc, pc));
// new_pc = pc;
// break;
// }
// new_idx++;
// } // FOR
// assert (new_pc != null);
idx_node.getOutputColumnGUIDs().set(i, orig_pc.guid());
new_output_guids.add(orig_pc.guid());
// sorted_new_output_guids.put(i,orig_pc.guid());
// TupleValueExpression clone_exp =
// (TupleValueExpression)orig_col.getExpression().clone();
// clone_exp.setColumnIndex(offset_new_idx);
// Storage storage = (catalog_tbl.getIsreplicated() ?
// Storage.kReplicated : Storage.kPartitioned);
// PlanColumn new_col = state.m_context.getPlanColumn(clone_exp,
// orig_col.displayName(), orig_col.getSortOrder(), storage);
// assert(new_col != null);
} // FOR
// We also need to fix all of the search key expressions used in the
// inline scan
expressions_to_fix.addAll(PlanNodeUtil.getExpressionsForPlanNode(idx_node));
// System.out.println("expressions_to_fix: " + expressions_to_fix);
}
if (debug.val) {
LOG.debug("Output Xref Offsets: " + offset_xref);
LOG.debug("New Output Columns GUIDS: " + new_sorted_output_guids);
}
// Get all of the AbstractExpression roots for this node
// Now fix the offsets for everyone
for (AbstractExpression exp : expressions_to_fix) {
new ExpressionTreeWalker() {
@Override
protected void callback(AbstractExpression exp_element) {
if (exp_element instanceof TupleValueExpression) {
TupleValueExpression tv_exp = (TupleValueExpression) exp_element;
int orig_idx = tv_exp.getColumnIndex();
// If we're in a NestLoopJoin (and not a
// NestLoopIndexJoin), then what we need to
// do is take the original offset (which points to a
// column in the original inner input), and s
Integer new_idx = offset_xref.get(orig_idx);
if (new_idx == null) {
LOG.debug(PlanNodeUtil.debug(PlanNodeUtil.getRoot(node)));
LOG.debug(state.plannerContext.debug());
}
assert (new_idx != null) : String.format("Missing New Offset of Original Offset %02d:\n%s", orig_idx, ExpressionUtil.debug(tv_exp));
if (orig_idx != new_idx) {
if (debug.val)
LOG.debug(String.format("Changing offset for %s.%s [%d ==> %d]", tv_exp.getTableName(), tv_exp.getColumnName(), orig_idx, new_idx));
tv_exp.setColumnIndex(new_idx);
}
}
}
}.traverse(exp);
}
// Then update the output columns to reflect the change
node.setOutputColumns(new_output_guids);
for (int new_idx = 0, cnt = node.getOutputColumnGUIDs().size(); new_idx < cnt; new_idx++) {
Integer col_guid = node.getOutputColumnGUIDs().get(new_idx);
PlanColumn pc = state.plannerContext.get(col_guid);
// Look at what our offset used versus what it is needs to be
// If it's different, then we need to make a new PlanColumn.
// Note that we will clone TupleValueExpression so that we do not
// mess with
// other PlanColumns
// Assume that AbstractExpression is always a TupleValueExpression
TupleValueExpression tv_exp = (TupleValueExpression) pc.getExpression();
assert (tv_exp != null);
int orig_idx = tv_exp.getColumnIndex();
// assert(new_idx == offset_xref.get(orig_idx)) :
// String.format("Offset Mismatch [orig_idx=%d] => [%d] != [%d]:\noffset_xref = %s\n%s",
// orig_idx, new_idx, offset_xref.get(orig_idx), offset_xref,
// PlanNodeUtil.debugNode(element));
if (orig_idx != new_idx) {
TupleValueExpression clone_exp = null;
try {
clone_exp = (TupleValueExpression) tv_exp.clone();
} catch (Exception ex) {
LOG.fatal(ex);
throw new RuntimeException(ex);
}
assert (clone_exp != null);
// compare with child's output columns to see whether orig_idx
// or new_idx is correct
assert (node.getChildPlanNodeCount() == 1);
List<Integer> child_output = node.getChild(0).getOutputColumnGUIDs();
if (orig_idx < child_output.size() && pc.guid() == child_output.get(orig_idx)) {
clone_exp.setColumnIndex(orig_idx);
} else {
clone_exp.setColumnIndex(new_idx);
}
PlanColumn new_pc = state.plannerContext.getPlanColumn(clone_exp, pc.getDisplayName(), pc.getSortOrder(), pc.getStorage());
assert (new_pc != null);
node.getOutputColumnGUIDs().set(new_idx, new_pc.guid());
}