You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ma...@apache.org on 2014/04/01 02:34:49 UTC
[2/3] PHOENIX-71 Support sub-joins
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/12bd7aed/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
index fd5174b..6136e4e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
@@ -50,7 +50,6 @@ import org.apache.phoenix.parse.CastParseNode;
import org.apache.phoenix.parse.ColumnDef;
import org.apache.phoenix.parse.ColumnParseNode;
import org.apache.phoenix.parse.ComparisonParseNode;
-import org.apache.phoenix.parse.ConcreteTableNode;
import org.apache.phoenix.parse.DerivedTableNode;
import org.apache.phoenix.parse.EqualParseNode;
import org.apache.phoenix.parse.FunctionParseNode;
@@ -102,179 +101,229 @@ public class JoinCompiler {
GENERAL,
}
- public static class JoinSpec {
- private ColumnResolver origResolver;
- private TableNode mainTableNode;
- private List<ColumnDef> dynamicColumns;
- private TableRef mainTable;
- private List<AliasedNode> select; // all basic nodes related to mainTable, no aggregation.
- private List<ParseNode> preFilters;
- private List<ParseNode> postFilters;
- private List<JoinTable> joinTables;
- private boolean useStarJoin;
- private Map<TableRef, JoinTable> tableRefToJoinTableMap;
- private Map<ColumnRef, ColumnRefType> columnRefs;
-
- private JoinSpec(SelectStatement statement, ColumnResolver resolver) throws SQLException {
- this.origResolver = resolver;
- List<AliasedNode> selectList = statement.getSelect();
- List<TableNode> tableNodes = statement.getFrom();
- assert (tableNodes.size() > 1);
- Iterator<TableNode> iter = tableNodes.iterator();
- Iterator<TableRef> tableRefIter = resolver.getTables().iterator();
- this.mainTableNode = iter.next();
- DynamicColumnsVisitor v = new DynamicColumnsVisitor();
- this.mainTableNode.accept(v);
- this.dynamicColumns = v.getDynamicColumns();
- this.mainTable = tableRefIter.next();
- this.select = extractFromSelect(selectList, mainTable, resolver);
- this.joinTables = new ArrayList<JoinTable>(tableNodes.size() - 1);
- this.preFilters = new ArrayList<ParseNode>();
- this.postFilters = new ArrayList<ParseNode>();
- this.useStarJoin = !statement.getHint().hasHint(Hint.NO_STAR_JOIN);
- this.tableRefToJoinTableMap = new HashMap<TableRef, JoinTable>();
- ColumnParseNodeVisitor generalRefVisitor = new ColumnParseNodeVisitor(resolver);
- ColumnParseNodeVisitor joinLocalRefVisitor = new ColumnParseNodeVisitor(resolver);
- ColumnParseNodeVisitor prefilterRefVisitor = new ColumnParseNodeVisitor(resolver);
- int lastRightJoinIndex = -1;
- TableNode tableNode = null;
- int i = 0;
- while (iter.hasNext()) {
- tableNode = iter.next();
- if (!(tableNode instanceof JoinTableNode))
- throw new SQLFeatureNotSupportedException("Implicit joins not supported.");
- JoinTableNode joinTableNode = (JoinTableNode) tableNode;
- JoinTable joinTable = new JoinTable(joinTableNode, tableRefIter.next(), statement, resolver);
- for (ParseNode condition : joinTable.conditions) {
- ComparisonParseNode comparisonNode = (ComparisonParseNode) condition;
- comparisonNode.getLHS().accept(generalRefVisitor);
- comparisonNode.getRHS().accept(joinLocalRefVisitor);
- }
- if (joinTable.getType() == JoinType.Right) {
- lastRightJoinIndex = i;
- }
- joinTables.add(joinTable);
- tableRefToJoinTableMap.put(joinTable.getTable(), joinTable);
- i++;
- }
- List<TableRef> prefilterAcceptedTables = new ArrayList<TableRef>();
- for (i = lastRightJoinIndex == -1 ? 0 : lastRightJoinIndex; i < joinTables.size(); i++) {
- JoinTable joinTable = joinTables.get(i);
- if (joinTable.getType() != JoinType.Left) {
- prefilterAcceptedTables.add(joinTable.getTable());
- }
- }
- if (statement.getWhere() != null) {
- if (lastRightJoinIndex > -1 && prefilterAcceptedTables.isEmpty()) {
- // conditions can't be pushed down to the scan filter.
- postFilters.add(statement.getWhere());
- } else {
- statement.getWhere().accept(new WhereNodeVisitor(resolver, lastRightJoinIndex > -1, prefilterAcceptedTables));
- for (ParseNode prefilter : preFilters) {
- prefilter.accept(prefilterRefVisitor);
- }
- }
- for (ParseNode postfilter : postFilters) {
- postfilter.accept(generalRefVisitor);
- }
- }
- // Delayed to this point, since pre-filters might have been post-fixed by WhereNodeVisitor.
- for (JoinTable joinTable : joinTables) {
- for (ParseNode prefilter : joinTable.preFilters) {
- prefilter.accept(prefilterRefVisitor);
- }
+ private final SelectStatement statement;
+ private final ColumnResolver origResolver;
+ private final boolean useStarJoin;
+ private final Map<ColumnRef, ColumnRefType> columnRefs;
+
+
+ private JoinCompiler(SelectStatement statement, ColumnResolver resolver) {
+ this.statement = statement;
+ this.origResolver = resolver;
+ this.useStarJoin = !statement.getHint().hasHint(Hint.NO_STAR_JOIN);
+ this.columnRefs = new HashMap<ColumnRef, ColumnRefType>();
+ }
+
+ public static JoinTable compile(SelectStatement statement, ColumnResolver resolver) throws SQLException {
+ JoinCompiler compiler = new JoinCompiler(statement, resolver);
+
+ List<TableNode> from = statement.getFrom();
+ if (from.size() > 1) {
+ throw new SQLFeatureNotSupportedException("Cross join not supported.");
+ }
+
+ JoinTableConstructor constructor = compiler.new JoinTableConstructor();
+ Pair<Table, List<JoinSpec>> res = from.get(0).accept(constructor);
+ JoinTable joinTable = res.getSecond() == null ? compiler.new JoinTable(res.getFirst()) : compiler.new JoinTable(res.getFirst(), res.getSecond());
+ if (statement.getWhere() != null) {
+ joinTable.addFilter(statement.getWhere());
+ }
+
+ ColumnParseNodeVisitor generalRefVisitor = new ColumnParseNodeVisitor(resolver);
+ ColumnParseNodeVisitor joinLocalRefVisitor = new ColumnParseNodeVisitor(resolver);
+ ColumnParseNodeVisitor prefilterRefVisitor = new ColumnParseNodeVisitor(resolver);
+
+ joinTable.pushDownColumnRefVisitors(generalRefVisitor, joinLocalRefVisitor, prefilterRefVisitor);
+
+ for (AliasedNode node : statement.getSelect()) {
+ node.getNode().accept(generalRefVisitor);
+ }
+ if (statement.getGroupBy() != null) {
+ for (ParseNode node : statement.getGroupBy()) {
+ node.accept(generalRefVisitor);
}
- for (AliasedNode node : selectList) {
+ }
+ if (statement.getHaving() != null) {
+ statement.getHaving().accept(generalRefVisitor);
+ }
+ if (statement.getOrderBy() != null) {
+ for (OrderByNode node : statement.getOrderBy()) {
node.getNode().accept(generalRefVisitor);
}
- if (statement.getGroupBy() != null) {
- for (ParseNode node : statement.getGroupBy()) {
- node.accept(generalRefVisitor);
+ }
+
+ for (ColumnRef ref : generalRefVisitor.getColumnRefMap().keySet()) {
+ compiler.columnRefs.put(ref, ColumnRefType.GENERAL);
+ }
+ for (ColumnRef ref : joinLocalRefVisitor.getColumnRefMap().keySet()) {
+ if (!compiler.columnRefs.containsKey(ref))
+ compiler.columnRefs.put(ref, ColumnRefType.JOINLOCAL);
+ }
+ for (ColumnRef ref : prefilterRefVisitor.getColumnRefMap().keySet()) {
+ if (!compiler.columnRefs.containsKey(ref))
+ compiler.columnRefs.put(ref, ColumnRefType.PREFILTER);
+ }
+
+ return joinTable;
+ }
+
+ private class JoinTableConstructor implements TableNodeVisitor<Pair<Table, List<JoinSpec>>> {
+
+ private TableRef resolveTable(String alias, TableName name) throws SQLException {
+ if (alias != null)
+ return origResolver.resolveTable(null, alias);
+
+ return origResolver.resolveTable(name.getSchemaName(), name.getTableName());
+ }
+
+ @Override
+ public Pair<Table, List<JoinSpec>> visit(BindTableNode boundTableNode) throws SQLException {
+ TableRef tableRef = resolveTable(boundTableNode.getAlias(), boundTableNode.getName());
+ List<AliasedNode> selectNodes = extractFromSelect(statement.getSelect(), tableRef, origResolver);
+ Table table = new Table(boundTableNode, Collections.<ColumnDef>emptyList(), selectNodes, tableRef);
+ return new Pair<Table, List<JoinSpec>>(table, null);
+ }
+
+ @Override
+ public Pair<Table, List<JoinSpec>> visit(JoinTableNode joinNode) throws SQLException {
+ Pair<Table, List<JoinSpec>> lhs = joinNode.getLHS().accept(this);
+ Pair<Table, List<JoinSpec>> rhs = joinNode.getRHS().accept(this);
+ JoinTable joinTable = rhs.getSecond() == null ? new JoinTable(rhs.getFirst()) : new JoinTable(rhs.getFirst(), rhs.getSecond());
+ List<JoinSpec> joinSpecs = lhs.getSecond();
+ if (joinSpecs == null) {
+ joinSpecs = new ArrayList<JoinSpec>();
+ }
+ joinSpecs.add(new JoinSpec(joinNode.getType(), joinNode.getOnNode(), joinTable, origResolver));
+
+ return new Pair<Table, List<JoinSpec>>(lhs.getFirst(), joinSpecs);
+ }
+
+ @Override
+ public Pair<Table, List<JoinSpec>> visit(NamedTableNode namedTableNode)
+ throws SQLException {
+ TableRef tableRef = resolveTable(namedTableNode.getAlias(), namedTableNode.getName());
+ List<AliasedNode> selectNodes = extractFromSelect(statement.getSelect(), tableRef, origResolver);
+ Table table = new Table(namedTableNode, namedTableNode.getDynamicColumns(), selectNodes, tableRef);
+ return new Pair<Table, List<JoinSpec>>(table, null);
+ }
+
+ @Override
+ public Pair<Table, List<JoinSpec>> visit(DerivedTableNode subselectNode)
+ throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+ }
+
+ public class JoinTable {
+ private final Table table;
+ private final List<JoinSpec> joinSpecs;
+ private final List<ParseNode> postFilters;
+ private final List<Table> tables;
+ private final List<TableRef> tableRefs;
+ private final boolean hasRightJoin;
+ private final List<JoinTable> prefilterAcceptedTables;
+
+ private JoinTable(Table table) {
+ this.table = table;
+ this.joinSpecs = Collections.<JoinSpec>emptyList();
+ this.postFilters = Collections.<ParseNode>emptyList();
+ this.tables = Collections.<Table>singletonList(table);
+ this.tableRefs = Collections.<TableRef>singletonList(table.getTableRef());
+ this.hasRightJoin = false;
+ this.prefilterAcceptedTables = Collections.<JoinTable>emptyList();
+ }
+
+ private JoinTable(Table table, List<JoinSpec> joinSpecs) {
+ this.table = table;
+ this.joinSpecs = joinSpecs;
+ this.postFilters = new ArrayList<ParseNode>();
+ this.tables = new ArrayList<Table>();
+ this.tableRefs = new ArrayList<TableRef>();
+ this.tables.add(table);
+ int lastRightJoinIndex = -1;
+ for (int i = 0; i < joinSpecs.size(); i++) {
+ this.tables.addAll(joinSpecs.get(i).getJoinTable().getTables());
+ if (joinSpecs.get(i).getType() == JoinType.Right) {
+ lastRightJoinIndex = i;
}
}
- if (statement.getHaving() != null) {
- statement.getHaving().accept(generalRefVisitor);
+ for (Table t : this.tables) {
+ this.tableRefs.add(t.getTableRef());
}
- if (statement.getOrderBy() != null) {
- for (OrderByNode node : statement.getOrderBy()) {
- node.getNode().accept(generalRefVisitor);
+ this.hasRightJoin = lastRightJoinIndex > -1;
+ this.prefilterAcceptedTables = new ArrayList<JoinTable>();
+ for (int i = lastRightJoinIndex == -1 ? 0 : lastRightJoinIndex; i < joinSpecs.size(); i++) {
+ JoinSpec joinSpec = joinSpecs.get(i);
+ if (joinSpec.getType() != JoinType.Left) {
+ prefilterAcceptedTables.add(joinSpec.getJoinTable());
}
}
- this.columnRefs = new HashMap<ColumnRef, ColumnRefType>();
- for (ColumnRef ref : generalRefVisitor.getColumnRefMap().keySet()) {
- columnRefs.put(ref, ColumnRefType.GENERAL);
- }
- for (ColumnRef ref : joinLocalRefVisitor.getColumnRefMap().keySet()) {
- if (!columnRefs.containsKey(ref))
- columnRefs.put(ref, ColumnRefType.JOINLOCAL);
- }
- for (ColumnRef ref : prefilterRefVisitor.getColumnRefMap().keySet()) {
- if (!columnRefs.containsKey(ref))
- columnRefs.put(ref, ColumnRefType.PREFILTER);
- }
}
- private JoinSpec(ColumnResolver resolver, TableNode tableNode, List<ColumnDef> dynamicColumns, TableRef table, List<AliasedNode> select, List<ParseNode> preFilters,
- List<ParseNode> postFilters, List<JoinTable> joinTables, boolean useStarJoin, Map<TableRef, JoinTable> tableRefToJoinTableMap, Map<ColumnRef, ColumnRefType> columnRefs) {
- this.origResolver = resolver;
- this.mainTableNode = tableNode;
- this.dynamicColumns = dynamicColumns;
- this.mainTable = table;
- this.select = select;
- this.preFilters = preFilters;
- this.postFilters = postFilters;
- this.joinTables = joinTables;
- this.useStarJoin = useStarJoin;
- this.tableRefToJoinTableMap = tableRefToJoinTableMap;
- this.columnRefs = columnRefs;
+ public Table getTable() {
+ return table;
}
- public ColumnResolver getOriginalResolver() {
- return origResolver;
+ public List<JoinSpec> getJoinSpecs() {
+ return joinSpecs;
}
- public TableNode getMainTableNode() {
- return mainTableNode;
+ public List<Table> getTables() {
+ return tables;
}
- public List<ColumnDef> getDynamicColumns() {
- return dynamicColumns;
+ public List<TableRef> getTableRefs() {
+ return tableRefs;
}
- public TableRef getMainTable() {
- return mainTable;
+ public ColumnResolver getOriginalResolver() {
+ return origResolver;
}
- public List<AliasedNode> getSelect() {
- return select;
+ public Map<ColumnRef, ColumnRefType> getColumnRefs() {
+ return columnRefs;
}
- public List<ParseNode> getPreFilters() {
- return preFilters;
- }
-
- public List<ParseNode> getPostFilters() {
- return postFilters;
+ public void addFilter(ParseNode filter) throws SQLException {
+ if (joinSpecs.isEmpty()) {
+ table.getPreFilters().add(filter);
+ return;
+ }
+
+ WhereNodeVisitor visitor = new WhereNodeVisitor(origResolver, table.getPreFilters(),
+ postFilters, Collections.<TableRef>singletonList(table.getTableRef()),
+ hasRightJoin, prefilterAcceptedTables);
+ filter.accept(visitor);
}
- public List<JoinTable> getJoinTables() {
- return joinTables;
+ public void pushDownColumnRefVisitors(ColumnParseNodeVisitor generalRefVisitor,
+ ColumnParseNodeVisitor joinLocalRefVisitor,
+ ColumnParseNodeVisitor prefilterRefVisitor) throws SQLException {
+ for (ParseNode node : table.getPreFilters()) {
+ node.accept(prefilterRefVisitor);
+ }
+ for (ParseNode node : postFilters) {
+ node.accept(generalRefVisitor);
+ }
+ for (JoinSpec joinSpec : joinSpecs) {
+ JoinTable joinTable = joinSpec.getJoinTable();
+ boolean hasSubJoin = !joinTable.getJoinSpecs().isEmpty();
+ for (ComparisonParseNode node : joinSpec.getOnConditions()) {
+ node.getLHS().accept(generalRefVisitor);
+ if (hasSubJoin) {
+ node.getRHS().accept(generalRefVisitor);
+ } else {
+ node.getRHS().accept(joinLocalRefVisitor);
+ }
+ }
+ joinTable.pushDownColumnRefVisitors(generalRefVisitor, joinLocalRefVisitor, prefilterRefVisitor);
+ }
}
- public ParseNode getPreFiltersCombined() {
- if (preFilters == null || preFilters.isEmpty())
+ public Expression compilePostFilterExpression(StatementContext context) throws SQLException {
+ if (postFilters == null || postFilters.isEmpty())
return null;
- if (preFilters.size() == 1)
- return preFilters.get(0);
-
- return NODE_FACTORY.and(preFilters);
- }
-
- public Expression compilePostFilterExpression(StatementContext context) throws SQLException {
- if (postFilters == null || postFilters.isEmpty())
- return null;
-
ExpressionCompiler expressionCompiler = new ExpressionCompiler(context);
List<Expression> expressions = new ArrayList<Expression>(postFilters.size());
for (ParseNode postFilter : postFilters) {
@@ -284,7 +333,7 @@ public class JoinCompiler {
}
if (expressions.size() == 1)
- return expressions.get(0);
+ return expressions.get(0);
return AndExpression.create(expressions);
}
@@ -297,25 +346,23 @@ public class JoinCompiler {
* @return a boolean vector for a star join; or null for non star join.
*/
public boolean[] getStarJoinVector() {
- assert(!joinTables.isEmpty());
-
- int count = joinTables.size();
+ int count = joinSpecs.size();
if (!useStarJoin
&& count > 1
- && joinTables.get(count - 1).getType() != JoinType.Left)
+ && joinSpecs.get(count - 1).getType() != JoinType.Left)
return null;
boolean[] vector = new boolean[count];
for (int i = 0; i < count; i++) {
- JoinTable joinTable = joinTables.get(i);
- if (joinTable.getType() != JoinType.Left
- && joinTable.getType() != JoinType.Inner)
+ JoinSpec joinSpec = joinSpecs.get(i);
+ if (joinSpec.getType() != JoinType.Left
+ && joinSpec.getType() != JoinType.Inner)
return null;
vector[i] = true;
- Iterator<TableRef> iter = joinTable.getLeftTableRefs().iterator();
+ Iterator<TableRef> iter = joinSpec.getDependencies().iterator();
while (vector[i] == true && iter.hasNext()) {
TableRef tableRef = iter.next();
- if (!tableRef.equals(mainTable)) {
+ if (!tableRef.equals(table.getTableRef())) {
vector[i] = false;
}
}
@@ -324,328 +371,83 @@ public class JoinCompiler {
return vector;
}
- protected boolean isWildCardSelect(TableRef table) {
- List<AliasedNode> selectList = table.equals(mainTable) ? this.select : tableRefToJoinTableMap.get(table).getSelect();
- return (selectList.size() == 1 && selectList.get(0).getNode() instanceof TableWildcardParseNode);
- }
-
- public void projectColumns(Scan scan, TableRef table) {
- if (isWildCardSelect(table)) {
- scan.getFamilyMap().clear();
- return;
- }
- for (ColumnRef columnRef : columnRefs.keySet()) {
- if (columnRef.getTableRef().equals(table)
- && !SchemaUtil.isPKColumn(columnRef.getColumn())) {
- scan.addColumn(columnRef.getColumn().getFamilyName().getBytes(), columnRef.getColumn().getName().getBytes());
- }
- }
- }
-
- public ProjectedPTableWrapper createProjectedTable(TableRef tableRef, boolean retainPKColumns) throws SQLException {
- List<PColumn> projectedColumns = new ArrayList<PColumn>();
- List<Expression> sourceExpressions = new ArrayList<Expression>();
- ListMultimap<String, String> columnNameMap = ArrayListMultimap.<String, String>create();
- PTable table = tableRef.getTable();
- boolean hasSaltingColumn = retainPKColumns && table.getBucketNum() != null;
- if (retainPKColumns) {
- for (PColumn column : table.getPKColumns()) {
- addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
- column, tableRef, column.getFamilyName(), hasSaltingColumn);
- }
- }
- if (isWildCardSelect(tableRef)) {
- for (PColumn column : table.getColumns()) {
- if (!retainPKColumns || !SchemaUtil.isPKColumn(column)) {
- addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
- column, tableRef, PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), hasSaltingColumn);
- }
- }
- } else {
- for (Map.Entry<ColumnRef, ColumnRefType> e : columnRefs.entrySet()) {
- ColumnRef columnRef = e.getKey();
- if (e.getValue() != ColumnRefType.PREFILTER
- && columnRef.getTableRef().equals(tableRef)
- && (!retainPKColumns || !SchemaUtil.isPKColumn(columnRef.getColumn()))) {
- PColumn column = columnRef.getColumn();
- addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
- column, tableRef, PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), hasSaltingColumn);
- }
- }
- }
-
- PTable t = PTableImpl.makePTable(table.getTenantId(), PNameFactory.newName(PROJECTED_TABLE_SCHEMA), table.getName(), PTableType.JOIN,
- table.getIndexState(), table.getTimeStamp(), table.getSequenceNumber(), table.getPKName(),
- retainPKColumns ? table.getBucketNum() : null, projectedColumns, table.getParentTableName(),
- table.getIndexes(), table.isImmutableRows(), Collections.<PName>emptyList(), null, null, table.isWALDisabled(), table.isMultiTenant(), table.getViewType(), table.getViewIndexId());
- return new ProjectedPTableWrapper(t, columnNameMap, sourceExpressions);
+ public JoinTable getSubJoinTableWithoutPostFilters() {
+ return joinSpecs.size() > 1 ? new JoinTable(table, joinSpecs.subList(0, joinSpecs.size() - 1)) :
+ new JoinTable(table);
}
- private static void addProjectedColumn(List<PColumn> projectedColumns, List<Expression> sourceExpressions,
- ListMultimap<String, String> columnNameMap, PColumn sourceColumn, TableRef sourceTable, PName familyName, boolean hasSaltingColumn)
- throws SQLException {
- if (sourceColumn == SALTING_COLUMN)
- return;
+ public SelectStatement getSubqueryWithoutJoin(boolean asSubquery) {
+ if (asSubquery)
+ return table.getAsSubquery();
- int position = projectedColumns.size() + (hasSaltingColumn ? 1 : 0);
- PTable table = sourceTable.getTable();
- String schemaName = table.getSchemaName().getString();
- String tableName = table.getTableName().getString();
- String colName = sourceColumn.getName().getString();
- String fullName = getProjectedColumnName(schemaName, tableName, colName);
- String aliasedName = sourceTable.getTableAlias() == null ? fullName : getProjectedColumnName(null, sourceTable.getTableAlias(), colName);
-
- columnNameMap.put(colName, aliasedName);
- if (!fullName.equals(aliasedName)) {
- columnNameMap.put(fullName, aliasedName);
- }
-
- PName name = PNameFactory.newName(aliasedName);
- PColumnImpl column = new PColumnImpl(name, familyName, sourceColumn.getDataType(),
- sourceColumn.getMaxLength(), sourceColumn.getScale(), sourceColumn.isNullable(),
- position, sourceColumn.getSortOrder(), sourceColumn.getArraySize(), sourceColumn.getViewConstant(), sourceColumn.isViewReferenced());
- Expression sourceExpression = new ColumnRef(sourceTable, sourceColumn.getPosition()).newColumnExpression();
- projectedColumns.add(column);
- sourceExpressions.add(sourceExpression);
- }
-
- public ColumnResolver getColumnResolver(PTableWrapper table) {
- return new JoinedTableColumnResolver(table, origResolver);
+ return NODE_FACTORY.select(Collections.<TableNode>singletonList(table.getTableNode()), statement.getHint(), statement.isDistinct(), statement.getSelect(), table.getPreFiltersCombined(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate());
}
- public boolean hasPostReference(TableRef table) {
- if (isWildCardSelect(table))
- return true;
-
- for (Map.Entry<ColumnRef, ColumnRefType> e : columnRefs.entrySet()) {
- if (e.getValue() == ColumnRefType.GENERAL && e.getKey().getTableRef().equals(table)) {
+ public boolean hasPostReference() {
+ for (Table table : tables) {
+ if (table.isWildCardSelect()) {
return true;
}
}
- return false;
- }
-
- private class WhereNodeVisitor extends TraverseNoParseNodeVisitor<Void> {
- private ColumnResolver resolver;
- private boolean hasRightJoin;
- private List<TableRef> prefilterAcceptedTables;
-
- public WhereNodeVisitor(ColumnResolver resolver, boolean hasRightJoin, List<TableRef> prefilterAcceptedTables) {
- this.resolver = resolver;
- this.hasRightJoin = hasRightJoin;
- this.prefilterAcceptedTables = prefilterAcceptedTables;
- }
-
- private Void leaveBooleanNode(ParseNode node,
- List<Void> l) throws SQLException {
- ColumnParseNodeVisitor visitor = new ColumnParseNodeVisitor(resolver);
- node.accept(visitor);
- ColumnParseNodeVisitor.ContentType type = visitor.getContentType(mainTable);
- switch (type) {
- case NONE:
- case SELF_ONLY:
- if (!hasRightJoin) {
- preFilters.add(node);
- } else {
- postFilters.add(node);
- }
- break;
- case FOREIGN_ONLY:
- TableRef matched = null;
- for (TableRef table : prefilterAcceptedTables) {
- if (visitor.getContentType(table) == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
- matched = table;
- break;
- }
- }
- if (matched != null) {
- tableRefToJoinTableMap.get(matched).preFilters.add(node);
- } else {
- postFilters.add(node);
- }
- break;
- default:
- postFilters.add(node);
- break;
+ for (Map.Entry<ColumnRef, ColumnRefType> e : columnRefs.entrySet()) {
+ if (e.getValue() == ColumnRefType.GENERAL && tableRefs.contains(e.getKey().getTableRef())) {
+ return true;
}
- return null;
- }
-
- @Override
- public Void visitLeave(LikeParseNode node,
- List<Void> l) throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public boolean visitEnter(AndParseNode node) {
- return true;
}
- @Override
- public Void visitLeave(OrParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(ComparisonParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(NotParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(InListParseNode node,
- List<Void> l) throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(IsNullParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(FunctionParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(BetweenParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(CaseParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(CastParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
+ return false;
}
}
- public static JoinSpec getSubJoinSpecWithoutPostFilters(JoinSpec join) {
- return new JoinSpec(join.origResolver, join.mainTableNode, join.dynamicColumns, join.mainTable, join.select, join.preFilters, new ArrayList<ParseNode>(),
- join.joinTables.subList(0, join.joinTables.size() - 1), join.useStarJoin, join.tableRefToJoinTableMap, join.columnRefs);
- }
-
- public static class JoinTable {
- private JoinType type;
- private TableNode tableNode; // original table node
- private List<ColumnDef> dynamicColumns;
- private TableRef table;
- private List<AliasedNode> select; // all basic nodes related to this table, no aggregation.
- private HintNode hint;
- private List<ParseNode> preFilters;
- private List<ParseNode> conditions;
- private SelectStatement subquery;
-
- private Set<TableRef> leftTableRefs;
+ public static class JoinSpec {
+ private final JoinType type;
+ private final List<ComparisonParseNode> onConditions;
+ private final JoinTable joinTable;
+ private Set<TableRef> dependencies;
- public JoinTable(JoinTableNode node, TableRef tableRef, SelectStatement statement, ColumnResolver resolver) throws SQLException {
- if (!(node.getTable() instanceof ConcreteTableNode))
- throw new SQLFeatureNotSupportedException("Subqueries not supported.");
-
- this.type = node.getType();
- this.tableNode = node.getTable();
- DynamicColumnsVisitor v = new DynamicColumnsVisitor();
- this.tableNode.accept(v);
- this.dynamicColumns = v.getDynamicColumns();
- this.table = tableRef;
- this.select = extractFromSelect(statement.getSelect(),tableRef,resolver);
- this.hint = statement.getHint();
- this.preFilters = new ArrayList<ParseNode>();
- this.conditions = new ArrayList<ParseNode>();
- this.leftTableRefs = new HashSet<TableRef>();
- node.getOnNode().accept(new OnNodeVisitor(resolver));
+ private JoinSpec(JoinType type, ParseNode onNode, JoinTable joinTable,
+ ColumnResolver resolver) throws SQLException {
+ this.type = type;
+ this.onConditions = new ArrayList<ComparisonParseNode>();
+ this.joinTable = joinTable;
+ this.dependencies = new HashSet<TableRef>();
+ OnNodeVisitor visitor = new OnNodeVisitor(resolver, onConditions, dependencies, joinTable);
+ onNode.accept(visitor);
}
public JoinType getType() {
return type;
}
- public TableNode getTableNode() {
- return tableNode;
+ public List<ComparisonParseNode> getOnConditions() {
+ return onConditions;
}
- public List<ColumnDef> getDynamicColumns() {
- return dynamicColumns;
+ public JoinTable getJoinTable() {
+ return joinTable;
}
- public TableRef getTable() {
- return table;
- }
-
- public List<AliasedNode> getSelect() {
- return select;
- }
-
- public List<ParseNode> getPreFilters() {
- return preFilters;
- }
-
- public List<ParseNode> getJoinConditions() {
- return conditions;
- }
-
- public SelectStatement getSubquery() {
- return subquery;
- }
-
- public Set<TableRef> getLeftTableRefs() {
- return leftTableRefs;
- }
-
- public ParseNode getPreFiltersCombined() {
- if (preFilters == null || preFilters.isEmpty())
- return null;
-
- if (preFilters.size() == 1)
- return preFilters.get(0);
-
- return NODE_FACTORY.and(preFilters);
- }
-
- public SelectStatement getAsSubquery() {
- if (subquery != null)
- return subquery;
-
- List<TableNode> from = new ArrayList<TableNode>(1);
- from.add(tableNode);
- return NODE_FACTORY.select(from, hint, false, select, getPreFiltersCombined(), null, null, null, null, 0, false);
+ public Set<TableRef> getDependencies() {
+ return dependencies;
}
public Pair<List<Expression>, List<Expression>> compileJoinConditions(StatementContext context, ColumnResolver leftResolver, ColumnResolver rightResolver) throws SQLException {
- ColumnResolver resolver = context.getResolver();
- List<Pair<Expression, Expression>> compiled = new ArrayList<Pair<Expression, Expression>>(conditions.size());
- context.setResolver(leftResolver);
+ ColumnResolver resolver = context.getResolver();
+ List<Pair<Expression, Expression>> compiled = new ArrayList<Pair<Expression, Expression>>(onConditions.size());
+ context.setResolver(leftResolver);
ExpressionCompiler expressionCompiler = new ExpressionCompiler(context);
- for (ParseNode condition : conditions) {
+ for (ParseNode condition : onConditions) {
assert (condition instanceof EqualParseNode);
EqualParseNode equalNode = (EqualParseNode) condition;
expressionCompiler.reset();
Expression left = equalNode.getLHS().accept(expressionCompiler);
compiled.add(new Pair<Expression, Expression>(left, null));
}
- context.setResolver(rightResolver);
+ context.setResolver(rightResolver);
expressionCompiler = new ExpressionCompiler(context);
Iterator<Pair<Expression, Expression>> iter = compiled.iterator();
- for (ParseNode condition : conditions) {
+ for (ParseNode condition : onConditions) {
Pair<Expression, Expression> p = iter.next();
EqualParseNode equalNode = (EqualParseNode) condition;
expressionCompiler.reset();
@@ -757,127 +559,337 @@ public class JoinCompiler {
return PDataType.VARBINARY;
}
+ }
+
+ public class Table {
+ private final TableNode tableNode;
+ private final List<ColumnDef> dynamicColumns;
+ private final TableRef tableRef;
+ private final List<AliasedNode> selectNodes; // all basic nodes related to this table, no aggregation.
+ private final List<ParseNode> preFilters;
+
+ private Table(TableNode tableNode, List<ColumnDef> dynamicColumns,
+ List<AliasedNode> selectNodes, TableRef tableRef) {
+ this.tableNode = tableNode;
+ this.dynamicColumns = dynamicColumns;
+ this.tableRef = tableRef;
+ this.selectNodes = selectNodes;
+ this.preFilters = new ArrayList<ParseNode>();
+ }
+
+ public TableNode getTableNode() {
+ return tableNode;
+ }
+
+ public List<ColumnDef> getDynamicColumns() {
+ return dynamicColumns;
+ }
+
+ public List<AliasedNode> getSelectNodes() {
+ return selectNodes;
+ }
+
+ public List<ParseNode> getPreFilters() {
+ return preFilters;
+ }
- private class OnNodeVisitor extends TraverseNoParseNodeVisitor<Void> {
- private ColumnResolver resolver;
+ public TableRef getTableRef() {
+ return tableRef;
+ }
+
+ public ParseNode getPreFiltersCombined() {
+ if (preFilters == null || preFilters.isEmpty())
+ return null;
- public OnNodeVisitor(ColumnResolver resolver) {
- this.resolver = resolver;
- }
+ if (preFilters.size() == 1)
+ return preFilters.get(0);
- private Void leaveNonEqBooleanNode(ParseNode node,
- List<Void> l) throws SQLException {
- ColumnParseNodeVisitor visitor = new ColumnParseNodeVisitor(resolver);
- node.accept(visitor);
- ColumnParseNodeVisitor.ContentType type = visitor.getContentType(table);
- if (type == ColumnParseNodeVisitor.ContentType.NONE
- || type == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
- preFilters.add(node);
- } else {
- throwUnsupportedJoinConditionException();
- }
- return null;
- }
-
- @Override
- public Void visitLeave(LikeParseNode node,
- List<Void> l) throws SQLException {
- return leaveNonEqBooleanNode(node, l);
- }
+ return NODE_FACTORY.and(preFilters);
+ }
+
+ public SelectStatement getAsSubquery() {
+ // TODO handle DerivedTableNode differently?
+ List<TableNode> from = Collections.<TableNode>singletonList(tableNode);
+ return NODE_FACTORY.select(from, statement.getHint(), false, selectNodes, getPreFiltersCombined(), null, null, null, null, 0, false);
+ }
+
+ protected boolean isWildCardSelect() {
+ return (selectNodes.size() == 1 && selectNodes.get(0).getNode() instanceof TableWildcardParseNode);
+ }
- @Override
- public boolean visitEnter(AndParseNode node) {
- return true;
- }
-
- @Override
- public Void visitLeave(OrParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ public void projectColumns(Scan scan) {
+ if (isWildCardSelect()) {
+ scan.getFamilyMap().clear();
+ return;
}
-
- @Override
- public Void visitLeave(ComparisonParseNode node, List<Void> l)
- throws SQLException {
- if (!(node instanceof EqualParseNode))
- return leaveNonEqBooleanNode(node, l);
- ColumnParseNodeVisitor lhsVisitor = new ColumnParseNodeVisitor(resolver);
- ColumnParseNodeVisitor rhsVisitor = new ColumnParseNodeVisitor(resolver);
- node.getLHS().accept(lhsVisitor);
- node.getRHS().accept(rhsVisitor);
- ColumnParseNodeVisitor.ContentType lhsType = lhsVisitor.getContentType(table);
- ColumnParseNodeVisitor.ContentType rhsType = rhsVisitor.getContentType(table);
- if ((lhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY || lhsType == ColumnParseNodeVisitor.ContentType.NONE)
- && (rhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY || rhsType == ColumnParseNodeVisitor.ContentType.NONE)) {
- preFilters.add(node);
- } else if (lhsType == ColumnParseNodeVisitor.ContentType.FOREIGN_ONLY
- && rhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
- conditions.add(node);
- leftTableRefs.addAll(lhsVisitor.getTableRefSet());
- } else if (rhsType == ColumnParseNodeVisitor.ContentType.FOREIGN_ONLY
- && lhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
- conditions.add(NODE_FACTORY.equal(node.getRHS(), node.getLHS()));
- leftTableRefs.addAll(rhsVisitor.getTableRefSet());
- } else {
- throwUnsupportedJoinConditionException();
+ for (ColumnRef columnRef : columnRefs.keySet()) {
+ if (columnRef.getTableRef().equals(tableRef)
+ && !SchemaUtil.isPKColumn(columnRef.getColumn())) {
+ scan.addColumn(columnRef.getColumn().getFamilyName().getBytes(), columnRef.getColumn().getName().getBytes());
}
- return null;
}
-
- @Override
- public Void visitLeave(NotParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ }
+
+ public ProjectedPTableWrapper createProjectedTable(boolean retainPKColumns) throws SQLException {
+ List<PColumn> projectedColumns = new ArrayList<PColumn>();
+ List<Expression> sourceExpressions = new ArrayList<Expression>();
+ ListMultimap<String, String> columnNameMap = ArrayListMultimap.<String, String>create();
+ PTable table = tableRef.getTable();
+ boolean hasSaltingColumn = retainPKColumns && table.getBucketNum() != null;
+ if (retainPKColumns) {
+ for (PColumn column : table.getPKColumns()) {
+ addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
+ column, column.getFamilyName(), hasSaltingColumn);
+ }
}
-
- @Override
- public Void visitLeave(InListParseNode node,
- List<Void> l) throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ if (isWildCardSelect()) {
+ for (PColumn column : table.getColumns()) {
+ if (!retainPKColumns || !SchemaUtil.isPKColumn(column)) {
+ addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
+ column, PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), hasSaltingColumn);
+ }
+ }
+ } else {
+ for (Map.Entry<ColumnRef, ColumnRefType> e : columnRefs.entrySet()) {
+ ColumnRef columnRef = e.getKey();
+ if (e.getValue() != ColumnRefType.PREFILTER
+ && columnRef.getTableRef().equals(tableRef)
+ && (!retainPKColumns || !SchemaUtil.isPKColumn(columnRef.getColumn()))) {
+ PColumn column = columnRef.getColumn();
+ addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
+ column, PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), hasSaltingColumn);
+ }
+ }
}
- @Override
- public Void visitLeave(IsNullParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
- }
+ PTable t = PTableImpl.makePTable(table.getTenantId(), PNameFactory.newName(PROJECTED_TABLE_SCHEMA), table.getName(), PTableType.JOIN,
+ table.getIndexState(), table.getTimeStamp(), table.getSequenceNumber(), table.getPKName(),
+ retainPKColumns ? table.getBucketNum() : null, projectedColumns, table.getParentTableName(),
+ table.getIndexes(), table.isImmutableRows(), Collections.<PName>emptyList(), null, null, table.isWALDisabled(), table.isMultiTenant(), table.getViewType(), table.getViewIndexId());
+ return new ProjectedPTableWrapper(t, columnNameMap, sourceExpressions);
+ }
+
+ private void addProjectedColumn(List<PColumn> projectedColumns, List<Expression> sourceExpressions,
+ ListMultimap<String, String> columnNameMap, PColumn sourceColumn, PName familyName, boolean hasSaltingColumn)
+ throws SQLException {
+ if (sourceColumn == SALTING_COLUMN)
+ return;
- @Override
- public Void visitLeave(FunctionParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
- }
+ int position = projectedColumns.size() + (hasSaltingColumn ? 1 : 0);
+ PTable table = tableRef.getTable();
+ String schemaName = table.getSchemaName().getString();
+ String tableName = table.getTableName().getString();
+ String colName = sourceColumn.getName().getString();
+ String fullName = getProjectedColumnName(schemaName, tableName, colName);
+ String aliasedName = tableRef.getTableAlias() == null ? fullName : getProjectedColumnName(null, tableRef.getTableAlias(), colName);
- @Override
- public Void visitLeave(BetweenParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ columnNameMap.put(colName, aliasedName);
+ if (!fullName.equals(aliasedName)) {
+ columnNameMap.put(fullName, aliasedName);
}
- @Override
- public Void visitLeave(CaseParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ PName name = PNameFactory.newName(aliasedName);
+ PColumnImpl column = new PColumnImpl(name, familyName, sourceColumn.getDataType(),
+ sourceColumn.getMaxLength(), sourceColumn.getScale(), sourceColumn.isNullable(),
+ position, sourceColumn.getSortOrder(), sourceColumn.getArraySize(), sourceColumn.getViewConstant(), sourceColumn.isViewReferenced());
+ Expression sourceExpression = new ColumnRef(tableRef, sourceColumn.getPosition()).newColumnExpression();
+ projectedColumns.add(column);
+ sourceExpressions.add(sourceExpression);
+ }
+ }
+
+ private static abstract class ConditionNodeVisitor extends TraverseNoParseNodeVisitor<Void> {
+
+ protected abstract Void leaveBooleanNode(ParseNode node, List<Void> l) throws SQLException;
+
+ @Override
+ public Void visitLeave(LikeParseNode node,
+ List<Void> l) throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public boolean visitEnter(AndParseNode node) {
+ return true;
+ }
+
+ @Override
+ public Void visitLeave(OrParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(ComparisonParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(NotParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(InListParseNode node,
+ List<Void> l) throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(IsNullParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(FunctionParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(BetweenParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(CaseParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(CastParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+ }
+
+ private static class WhereNodeVisitor extends ConditionNodeVisitor {
+ private ColumnResolver resolver;
+ private List<ParseNode> preFilters;
+ private List<ParseNode> postFilters;
+ private List<TableRef> selfTableRefs;
+ private boolean hasRightJoin;
+ private List<JoinTable> prefilterAcceptedTables;
+
+ public WhereNodeVisitor(ColumnResolver resolver, List<ParseNode> preFilters,
+ List<ParseNode> postFilters, List<TableRef> selfTableRefs, boolean hasRightJoin,
+ List<JoinTable> prefilterAcceptedTables) {
+ this.resolver = resolver;
+ this.preFilters = preFilters;
+ this.postFilters = postFilters;
+ this.selfTableRefs = selfTableRefs;
+ this.hasRightJoin = hasRightJoin;
+ this.prefilterAcceptedTables = prefilterAcceptedTables;
+ }
+
+ protected Void leaveBooleanNode(ParseNode node,
+ List<Void> l) throws SQLException {
+ ColumnParseNodeVisitor visitor = new ColumnParseNodeVisitor(resolver);
+ node.accept(visitor);
+ ColumnParseNodeVisitor.ContentType type = visitor.getContentType(selfTableRefs);
+ switch (type) {
+ case NONE:
+ case SELF_ONLY:
+ if (!hasRightJoin) {
+ preFilters.add(node);
+ } else {
+ postFilters.add(node);
+ }
+ break;
+ case FOREIGN_ONLY:
+ JoinTable matched = null;
+ for (JoinTable joinTable : prefilterAcceptedTables) {
+ if (visitor.getContentType(joinTable.getTableRefs()) == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
+ matched = joinTable;
+ break;
+ }
+ }
+ if (matched != null) {
+ matched.addFilter(node);
+ } else {
+ postFilters.add(node);
+ }
+ break;
+ default:
+ postFilters.add(node);
+ break;
}
-
- @Override
- public Void visitLeave(CastParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ return null;
+ }
+ }
+
+ private static class OnNodeVisitor extends ConditionNodeVisitor {
+ private ColumnResolver resolver;
+ private List<ComparisonParseNode> onConditions;
+ private Set<TableRef> dependencies;
+ private JoinTable joinTable;
+
+ public OnNodeVisitor(ColumnResolver resolver, List<ComparisonParseNode> onConditions,
+ Set<TableRef> dependencies, JoinTable joinTable) {
+ this.resolver = resolver;
+ this.onConditions = onConditions;
+ this.dependencies = dependencies;
+ this.joinTable = joinTable;
+ }
+
+ protected Void leaveBooleanNode(ParseNode node,
+ List<Void> l) throws SQLException {
+ ColumnParseNodeVisitor visitor = new ColumnParseNodeVisitor(resolver);
+ node.accept(visitor);
+ ColumnParseNodeVisitor.ContentType type = visitor.getContentType(joinTable.getTableRefs());
+ if (type == ColumnParseNodeVisitor.ContentType.NONE
+ || type == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
+ joinTable.addFilter(node);
+ } else {
+ throwUnsupportedJoinConditionException();
}
+ return null;
+ }
- /*
- * Conditions in the ON clause can only be:
- * 1) an equal test between a self table expression and a foreign
- * table expression.
- * 2) a boolean condition referencing to the self table only.
- * Otherwise, it can be ambiguous.
- */
- private void throwUnsupportedJoinConditionException()
- throws SQLFeatureNotSupportedException {
- throw new SQLFeatureNotSupportedException("Does not support non-standard or non-equi join conditions.");
- }
+ @Override
+ public Void visitLeave(ComparisonParseNode node, List<Void> l)
+ throws SQLException {
+ if (!(node instanceof EqualParseNode))
+ return leaveBooleanNode(node, l);
+ ColumnParseNodeVisitor lhsVisitor = new ColumnParseNodeVisitor(resolver);
+ ColumnParseNodeVisitor rhsVisitor = new ColumnParseNodeVisitor(resolver);
+ node.getLHS().accept(lhsVisitor);
+ node.getRHS().accept(rhsVisitor);
+ ColumnParseNodeVisitor.ContentType lhsType = lhsVisitor.getContentType(joinTable.getTableRefs());
+ ColumnParseNodeVisitor.ContentType rhsType = rhsVisitor.getContentType(joinTable.getTableRefs());
+ if ((lhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY || lhsType == ColumnParseNodeVisitor.ContentType.NONE)
+ && (rhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY || rhsType == ColumnParseNodeVisitor.ContentType.NONE)) {
+ joinTable.addFilter(node);
+ } else if (lhsType == ColumnParseNodeVisitor.ContentType.FOREIGN_ONLY
+ && rhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
+ onConditions.add(node);
+ dependencies.addAll(lhsVisitor.getTableRefSet());
+ } else if (rhsType == ColumnParseNodeVisitor.ContentType.FOREIGN_ONLY
+ && lhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
+ onConditions.add(NODE_FACTORY.equal(node.getRHS(), node.getLHS()));
+ dependencies.addAll(rhsVisitor.getTableRefSet());
+ } else {
+ throwUnsupportedJoinConditionException();
+ }
+ return null;
}
+
+ /*
+ * Conditions in the ON clause can only be:
+ * 1) an equal test between a self table expression and a foreign
+ * table expression.
+ * 2) a boolean condition referencing to the self table only.
+ * Otherwise, it can be ambiguous.
+ */
+ private void throwUnsupportedJoinConditionException()
+ throws SQLFeatureNotSupportedException {
+ throw new SQLFeatureNotSupportedException("Does not support non-standard or non-equi join conditions.");
+ }
}
private static class ColumnParseNodeVisitor extends StatelessTraverseAllParseNodeVisitor {
@@ -914,14 +926,33 @@ public class JoinCompiler {
return columnRefMap;
}
- public ContentType getContentType(TableRef selfTable) {
+ public ContentType getContentType(List<TableRef> selfTableRefs) {
if (tableRefSet.isEmpty())
return ContentType.NONE;
- if (tableRefSet.size() > 1)
- return ContentType.COMPLEX;
- if (tableRefSet.contains(selfTable))
- return ContentType.SELF_ONLY;
- return ContentType.FOREIGN_ONLY;
+
+ ContentType ret = ContentType.NONE;
+ for (TableRef tRef : tableRefSet) {
+ boolean isSelf = selfTableRefs.contains(tRef);
+ switch (ret) {
+ case NONE:
+ ret = isSelf ? ContentType.SELF_ONLY : ContentType.FOREIGN_ONLY;
+ break;
+ case SELF_ONLY:
+ ret = isSelf ? ContentType.SELF_ONLY : ContentType.COMPLEX;
+ break;
+ case FOREIGN_ONLY:
+ ret = isSelf ? ContentType.COMPLEX : ContentType.FOREIGN_ONLY;
+ break;
+ default: // COMPLEX do nothing
+ break;
+ }
+
+ if (ret == ContentType.COMPLEX) {
+ break;
+ }
+ }
+
+ return ret;
}
}
@@ -929,14 +960,14 @@ public class JoinCompiler {
// for creation of new statements
private static ParseNodeFactory NODE_FACTORY = new ParseNodeFactory();
- private static List<AliasedNode> extractFromSelect(List<AliasedNode> select, TableRef table, ColumnResolver resolver) throws SQLException {
+ private static List<AliasedNode> extractFromSelect(List<AliasedNode> select, TableRef tableRef, ColumnResolver resolver) throws SQLException {
List<AliasedNode> ret = new ArrayList<AliasedNode>();
ColumnParseNodeVisitor visitor = new ColumnParseNodeVisitor(resolver);
for (AliasedNode aliasedNode : select) {
ParseNode node = aliasedNode.getNode();
if (node instanceof TableWildcardParseNode) {
TableName tableName = ((TableWildcardParseNode) node).getTableName();
- if (table.equals(resolver.resolveTable(tableName.getSchemaName(), tableName.getTableName()))) {
+ if (tableRef.equals(resolver.resolveTable(tableName.getSchemaName(), tableName.getTableName()))) {
ret.clear();
ret.add(aliasedNode);
return ret;
@@ -945,12 +976,12 @@ public class JoinCompiler {
}
node.accept(visitor);
- ColumnParseNodeVisitor.ContentType type = visitor.getContentType(table);
+ ColumnParseNodeVisitor.ContentType type = visitor.getContentType(Collections.singletonList(tableRef));
if (type == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
ret.add(aliasedNode);
} else if (type == ColumnParseNodeVisitor.ContentType.COMPLEX) {
for (Map.Entry<ColumnRef, ColumnParseNode> entry : visitor.getColumnRefMap().entrySet()) {
- if (entry.getKey().getTableRef().equals(table)) {
+ if (entry.getKey().getTableRef().equals(tableRef)) {
ret.add(NODE_FACTORY.aliasedNode(null, entry.getValue()));
}
}
@@ -960,12 +991,8 @@ public class JoinCompiler {
return ret;
}
- public static JoinSpec getJoinSpec(StatementContext context, SelectStatement statement) throws SQLException {
- return new JoinSpec(statement, context.getResolver());
- }
-
public static SelectStatement optimize(StatementContext context, SelectStatement select, PhoenixStatement statement) throws SQLException {
- ColumnResolver resolver = context.getResolver();
+ final ColumnResolver resolver = context.getResolver();
TableRef groupByTableRef = null;
TableRef orderByTableRef = null;
if (select.getGroupBy() != null && !select.getGroupBy().isEmpty()) {
@@ -987,12 +1014,12 @@ public class JoinCompiler {
orderByTableRef = set.iterator().next();
}
}
- JoinSpec join = getJoinSpec(context, select);
+ JoinTable join = compile(select, context.getResolver());
if (groupByTableRef != null || orderByTableRef != null) {
QueryCompiler compiler = new QueryCompiler(statement, select, resolver);
List<Object> binds = statement.getParameters();
StatementContext ctx = new StatementContext(statement, resolver, new Scan());
- QueryPlan plan = compiler.compileJoinQuery(ctx, select, binds, join, false);
+ QueryPlan plan = compiler.compileJoinQuery(ctx, binds, join, false);
TableRef table = plan.getTableRef();
if (groupByTableRef != null && !groupByTableRef.equals(table)) {
groupByTableRef = null;
@@ -1002,94 +1029,81 @@ public class JoinCompiler {
}
}
- Map<TableRef, TableRef> replacement = new HashMap<TableRef, TableRef>();
+ final Map<TableRef, TableRef> replacement = new HashMap<TableRef, TableRef>();
+
+ for (Table table : join.getTables()) {
+ TableRef tableRef = table.getTableRef();
+ List<ParseNode> groupBy = tableRef.equals(groupByTableRef) ? select.getGroupBy() : null;
+ List<OrderByNode> orderBy = tableRef.equals(orderByTableRef) ? select.getOrderBy() : null;
+ SelectStatement stmt = getSubqueryForOptimizedPlan(select.getHint(), table.getDynamicColumns(), tableRef, join.getColumnRefs(), table.getPreFiltersCombined(), groupBy, orderBy, table.isWildCardSelect());
+ QueryPlan plan = context.getConnection().getQueryServices().getOptimizer().optimize(statement, stmt);
+ if (!plan.getTableRef().equals(tableRef)) {
+ replacement.put(tableRef, plan.getTableRef());
+ }
+ }
+
+ if (replacement.isEmpty())
+ return select;
+
List<TableNode> from = select.getFrom();
List<TableNode> newFrom = Lists.newArrayListWithExpectedSize(from.size());
+ for (TableNode node : from) {
+ newFrom.add(node.accept(new TableNodeVisitor<TableNode>() {
+ private TableRef resolveTable(String alias, TableName name) throws SQLException {
+ if (alias != null)
+ return resolver.resolveTable(null, alias);
- class TableNodeRewriter implements TableNodeVisitor {
- private TableRef table;
- private TableNode replaced;
-
- TableNodeRewriter(TableRef table) {
- this.table = table;
- }
-
- public TableNode getReplacedTableNode() {
- return replaced;
- }
+ return resolver.resolveTable(name.getSchemaName(), name.getTableName());
+ }
- @Override
- public void visit(BindTableNode boundTableNode) throws SQLException {
- String alias = boundTableNode.getAlias();
- replaced = NODE_FACTORY.bindTable(alias == null ? null : '"' + alias + '"', getReplacedTableName());
- }
+ private TableName getReplacedTableName(TableRef tableRef) {
+ String schemaName = tableRef.getTable().getSchemaName().getString();
+ return TableName.create(schemaName.length() == 0 ? null : schemaName, tableRef.getTable().getTableName().getString());
+ }
- @Override
- public void visit(JoinTableNode joinNode) throws SQLException {
- joinNode.getTable().accept(this);
- replaced = NODE_FACTORY.join(joinNode.getType(), joinNode.getOnNode(), replaced);
- }
+ @Override
+ public TableNode visit(BindTableNode boundTableNode) throws SQLException {
+ TableRef tableRef = resolveTable(boundTableNode.getAlias(), boundTableNode.getName());
+ TableRef replaceRef = replacement.get(tableRef);
+ if (replaceRef == null)
+ return boundTableNode;
- @Override
- public void visit(NamedTableNode namedTableNode)
- throws SQLException {
- String alias = namedTableNode.getAlias();
- replaced = NODE_FACTORY.namedTable(alias == null ? null : '"' + alias + '"', getReplacedTableName(), namedTableNode.getDynamicColumns());
- }
+ String alias = boundTableNode.getAlias();
+ return NODE_FACTORY.bindTable(alias == null ? null : '"' + alias + '"', getReplacedTableName(replaceRef));
+ }
- @Override
- public void visit(DerivedTableNode subselectNode)
- throws SQLException {
- throw new SQLFeatureNotSupportedException();
- }
-
- private TableName getReplacedTableName() {
- String schemaName = table.getTable().getSchemaName().getString();
- return TableName.create(schemaName.length() == 0 ? null : schemaName, table.getTable().getTableName().getString());
- }
- };
-
- // get optimized plans for join tables
- for (int i = 1; i < from.size(); i++) {
- TableNode jNode = from.get(i);
- assert (jNode instanceof JoinTableNode);
- TableNode tNode = ((JoinTableNode) jNode).getTable();
- for (JoinTable jTable : join.getJoinTables()) {
- if (jTable.getTableNode() != tNode)
- continue;
- TableRef table = jTable.getTable();
- List<ParseNode> groupBy = table.equals(groupByTableRef) ? select.getGroupBy() : null;
- List<OrderByNode> orderBy = table.equals(orderByTableRef) ? select.getOrderBy() : null;
- SelectStatement stmt = getSubqueryForOptimizedPlan(select.getHint(), join.tableRefToJoinTableMap.get(table).getDynamicColumns(), table, join.columnRefs, jTable.getPreFiltersCombined(), groupBy, orderBy, join.isWildCardSelect(table));
- QueryPlan plan = context.getConnection().getQueryServices().getOptimizer().optimize(statement, stmt);
- if (!plan.getTableRef().equals(table)) {
- TableNodeRewriter rewriter = new TableNodeRewriter(plan.getTableRef());
- jNode.accept(rewriter);
- newFrom.add(rewriter.getReplacedTableNode());
- replacement.put(table, plan.getTableRef());
- } else {
- newFrom.add(jNode);
+ @Override
+ public TableNode visit(JoinTableNode joinNode) throws SQLException {
+ TableNode lhs = joinNode.getLHS();
+ TableNode rhs = joinNode.getRHS();
+ TableNode lhsReplace = lhs.accept(this);
+ TableNode rhsReplace = rhs.accept(this);
+ if (lhs == lhsReplace && rhs == rhsReplace)
+ return joinNode;
+
+ return NODE_FACTORY.join(joinNode.getType(), lhsReplace, rhsReplace, joinNode.getOnNode());
}
- }
- }
- // get optimized plan for main table
- TableRef table = join.getMainTable();
- List<ParseNode> groupBy = table.equals(groupByTableRef) ? select.getGroupBy() : null;
- List<OrderByNode> orderBy = table.equals(orderByTableRef) ? select.getOrderBy() : null;
- SelectStatement stmt = getSubqueryForOptimizedPlan(select.getHint(), join.dynamicColumns, table, join.columnRefs, join.getPreFiltersCombined(), groupBy, orderBy, join.isWildCardSelect(table));
- QueryPlan plan = context.getConnection().getQueryServices().getOptimizer().optimize(statement, stmt);
- if (!plan.getTableRef().equals(table)) {
- TableNodeRewriter rewriter = new TableNodeRewriter(plan.getTableRef());
- from.get(0).accept(rewriter);
- newFrom.add(0, rewriter.getReplacedTableNode());
- replacement.put(table, plan.getTableRef());
- } else {
- newFrom.add(0, from.get(0));
+
+ @Override
+ public TableNode visit(NamedTableNode namedTableNode)
+ throws SQLException {
+ TableRef tableRef = resolveTable(namedTableNode.getAlias(), namedTableNode.getName());
+ TableRef replaceRef = replacement.get(tableRef);
+ if (replaceRef == null)
+ return namedTableNode;
+
+ String alias = namedTableNode.getAlias();
+ return NODE_FACTORY.namedTable(alias == null ? null : '"' + alias + '"', getReplacedTableName(replaceRef), namedTableNode.getDynamicColumns());
+ }
+
+ @Override
+ public TableNode visit(DerivedTableNode subselectNode)
+ throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+ }));
}
- if (replacement.isEmpty())
- return select;
-
return IndexStatementRewriter.translate(NODE_FACTORY.select(select, newFrom), resolver, replacement);
}
@@ -1116,76 +1130,11 @@ public class JoinCompiler {
return NODE_FACTORY.select(from, hintNode, false, selectList, where, groupBy, null, orderBy, null, 0, false);
}
- public static SelectStatement getSubqueryWithoutJoin(SelectStatement statement, JoinSpec join) {
- return NODE_FACTORY.select(statement.getFrom().subList(0, 1), statement.getHint(), statement.isDistinct(), statement.getSelect(), join.getPreFiltersCombined(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate());
- }
-
- // Get the last join table select statement with fixed-up select and where nodes.
- // Currently does NOT support last join table as a subquery.
- public static SelectStatement getSubqueryForLastJoinTable(SelectStatement statement, JoinSpec join) throws SQLException {
- List<JoinTable> joinTables = join.getJoinTables();
- int count = joinTables.size();
- assert (count > 0);
- JoinTable lastJoinTable = joinTables.get(count - 1);
- if (lastJoinTable.getSubquery() != null) {
- throw new SQLFeatureNotSupportedException("Subqueries not supported.");
- }
- List<TableNode> from = new ArrayList<TableNode>(1);
- from.add(lastJoinTable.getTableNode());
-
- return NODE_FACTORY.select(from, statement.getHint(), statement.isDistinct(), statement.getSelect(), lastJoinTable.getPreFiltersCombined(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate());
- }
-
- // Get subquery with fixed select and where nodes
- public static SelectStatement getSubQueryWithoutLastJoin(SelectStatement statement, JoinSpec join) {
- List<TableNode> from = statement.getFrom();
- assert(from.size() > 1);
- List<JoinTable> joinTables = join.getJoinTables();
- int count = joinTables.size();
- assert (count > 0);
- List<AliasedNode> select = new ArrayList<AliasedNode>();
- select.addAll(join.getSelect());
- for (int i = 0; i < count - 1; i++) {
- select.addAll(joinTables.get(i).getSelect());
- }
-
- return NODE_FACTORY.select(from.subList(0, from.size() - 1), statement.getHint(), false, select, join.getPreFiltersCombined(), null, null, null, null, statement.getBindCount(), false);
- }
-
- public static PTableWrapper mergeProjectedTables(PTableWrapper lWrapper, PTableWrapper rWrapper, boolean innerJoin) throws SQLException {
- PTable left = lWrapper.getTable();
- PTable right = rWrapper.getTable();
- List<PColumn> merged = new ArrayList<PColumn>();
- merged.addAll(left.getColumns());
- int position = merged.size();
- for (PColumn c : right.getColumns()) {
- if (!SchemaUtil.isPKColumn(c)) {
- PColumnImpl column = new PColumnImpl(c.getName(),
- PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), c.getDataType(),
- c.getMaxLength(), c.getScale(), innerJoin ? c.isNullable() : true, position++,
- c.getSortOrder(), c.getArraySize(), c.getViewConstant(), c.isViewReferenced());
- merged.add(column);
- }
- }
- if (left.getBucketNum() != null) {
- merged.remove(0);
- }
- PTable t = PTableImpl.makePTable(left.getTenantId(), left.getSchemaName(),
- PNameFactory.newName(SchemaUtil.getTableName(left.getName().getString(), right.getName().getString())), left.getType(), left.getIndexState(), left.getTimeStamp(), left.getSequenceNumber(), left.getPKName(), left.getBucketNum(), merged,
- left.getParentTableName(), left.getIndexes(), left.isImmutableRows(), Collections.<PName>emptyList(), null, null, PTable.DEFAULT_DISABLE_WAL, left.isMultiTenant(), left.getViewType(), left.getViewIndexId());
-
- ListMultimap<String, String> mergedMap = ArrayListMultimap.<String, String>create();
- mergedMap.putAll(lWrapper.getColumnNameMap());
- mergedMap.putAll(rWrapper.getColumnNameMap());
-
- return new PTableWrapper(t, mergedMap);
- }
-
public static ScanProjector getScanProjector(ProjectedPTableWrapper table) {
return new ScanProjector(table);
}
- public static class PTableWrapper {
+ public class PTableWrapper {
protected PTable table;
protected ListMultimap<String, String> columnNameMap;
@@ -1205,9 +1154,42 @@ public class JoinCompiler {
public List<String> getMappedColumnName(String name) {
return columnNameMap.get(name);
}
+
+ public ColumnResolver createColumnResolver() {
+ return new JoinedTableColumnResolver(this, origResolver);
+ }
+
+ public PTableWrapper mergeProjectedTables(PTableWrapper rWrapper, boolean innerJoin) throws SQLException {
+ PTable left = this.getTable();
+ PTable right = rWrapper.getTable();
+ List<PColumn> merged = new ArrayList<PColumn>();
+ merged.addAll(left.getColumns());
+ int position = merged.size();
+ for (PColumn c : right.getColumns()) {
+ if (!SchemaUtil.isPKColumn(c)) {
+ PColumnImpl column = new PColumnImpl(c.getName(),
+ PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), c.getDataType(),
+ c.getMaxLength(), c.getScale(), innerJoin ? c.isNullable() : true, position++,
+ c.getSortOrder(), c.getArraySize(), c.getViewConstant(), c.isViewReferenced());
+ merged.add(column);
+ }
+ }
+ if (left.getBucketNum() != null) {
+ merged.remove(0);
+ }
+ PTable t = PTableImpl.makePTable(left.getTenantId(), left.getSchemaName(),
+ PNameFactory.newName(SchemaUtil.getTableName(left.getName().getString(), right.getName().getString())), left.getType(), left.getIndexState(), left.getTimeStamp(), left.getSequenceNumber(), left.getPKName(), left.getBucketNum(), merged,
+ left.getParentTableName(), left.getIndexes(), left.isImmutableRows(), Collections.<PName>emptyList(), null, null, PTable.DEFAULT_DISABLE_WAL, left.isMultiTenant(), left.getViewType(), left.getViewIndexId());
+
+ ListMultimap<String, String> mergedMap = ArrayListMultimap.<String, String>create();
+ mergedMap.putAll(this.getColumnNameMap());
+ mergedMap.putAll(rWrapper.getColumnNameMap());
+
+ return new PTableWrapper(t, mergedMap);
+ }
}
- public static class ProjectedPTableWrapper extends PTableWrapper {
+ public class ProjectedPTableWrapper extends PTableWrapper {
private List<Expression> sourceExpressions;
protected ProjectedPTableWrapper(PTable table, ListMultimap<String, String> columnNameMap, List<Expression> sourceExpressions) {
@@ -1274,39 +1256,6 @@ public class JoinCompiler {
return SchemaUtil.getColumnName(SchemaUtil.getTableName(schemaName, tableName), colName);
}
- private static class DynamicColumnsVisitor implements TableNodeVisitor {
- private List<ColumnDef> dynamicCols;
-
- public DynamicColumnsVisitor() {
- }
-
- public List<ColumnDef> getDynamicColumns() {
- return dynamicCols == null ? Collections.<ColumnDef> emptyList() : dynamicCols;
- }
-
- @Override
- public void visit(BindTableNode boundTableNode) throws SQLException {
- }
-
- @Override
- public void visit(JoinTableNode joinNode) throws SQLException {
- // should not expect to see this node.
- assert(false);
- }
-
- @Override
- public void visit(NamedTableNode namedTableNode)
- throws SQLException {
- this.dynamicCols = namedTableNode.getDynamicColumns();
- }
-
- @Override
- public void visit(DerivedTableNode subselectNode)
- throws SQLException {
- throw new SQLFeatureNotSupportedException();
- }
- };
-
}