/*****************************************************************************
* Copyright (C) 2008 EnterpriseDB Corporation.
* Copyright (C) 2011 Stado Global Development Group.
*
* This file is part of Stado.
*
* Stado is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Stado is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Stado. If not, see <http://www.gnu.org/licenses/>.
*
* You can find Stado at http://www.stado.us
*
****************************************************************************/
package org.postgresql.stado.optimizer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.postgresql.stado.engine.XDBSessionContext;
import org.postgresql.stado.exception.ColumnNotFoundException;
import org.postgresql.stado.exception.ErrorMessageRepository;
import org.postgresql.stado.exception.XDBServerException;
import org.postgresql.stado.metadata.DBNode;
import org.postgresql.stado.metadata.SysColumn;
import org.postgresql.stado.metadata.SysDatabase;
import org.postgresql.stado.metadata.SysTable;
import org.postgresql.stado.metadata.partitions.HashPartitionMap;
import org.postgresql.stado.metadata.partitions.PartitionMap;
import org.postgresql.stado.parser.handler.IdentifierHandler;
//-------------------------------------------------------------
public class RelationNode implements IRebuildString {
private static AtomicInteger nextCount = new AtomicInteger(1);
// nodeTypes
public static final int FAKE = 1;
public static final int TABLE = 2;
public static final int SUBQUERY_SCALAR = 3;
public static final int SUBQUERY_RELATION = 4;
public static final int SUBQUERY_NONCORRELATED = 5;
public static final int SUBQUERY_CORRELATED = 6;
public static final int SUBQUERY_CORRELATED_PH = 7; // placeholder in subq
private int nodeId;
private int nodeType;
private List<SqlExpression> projectionList;
private List<QueryCondition> conditionList;
private List<RelationNode> joinList;
private List<AttributeColumn> condColumnList;
/** When SUBQUERY_CORRELATED, lists all columns that we get
* from parent query.
* We need this to make sure that we always project these up
* the query tree
*/
private List<AttributeColumn> correlatedColumnList;
/** The table name that this node represents.
* Note that if this node represents a subquery that is
* part of a FROM clause, tableName is set to the AS relation name.
*/
private String tableName;
private String alias;
/** to keep track for if this relation has own alias or alias assigned
* by sub query.
*/
private boolean hasNotOwnAlias = false;
private int rowsize;
private long estCost;
private long estRowsReturned;
private String relationString = "";
private boolean isTemporaryTable;
/* if ONLY is present with table */
private boolean isOnly;
private XDBSessionContext client;
// This is used only in the case our QueryNode is a noncorrelated
// subquery. It points back to the expression in the parent tree,
// so it can be rewritten
private SqlExpression parentNoncorExpr;
private SqlExpression parentCorrelatedExpr;
// for OUTER join handling
private short outerLevel = 0; // for nested outers (0 = no OUTER)
private List<RelationNode> childrenNodes = new ArrayList<RelationNode>();
private List<RelationNode> parentNodes = new ArrayList<RelationNode>();
private QueryTree queryTree;
private QueryTree subqueryTree;
// This allows us to track what temp table the columns involved in
// the underlying table are currently in, when building up the plan
private String currentTempTableName = "";
// Is this relation from a WITH clause
private boolean isWith = false;
// If this represents a WITH, track if it is used in
// the top most tree. Important to avoid having
// it added as an extra relation if it appears deeper
// and feeds into other relations
private boolean isTopMostUsedWith = false;
// A WITH relation may be referenced more than once,
// like any other. Since we try and use the existing
// code for FROM subquery handling, we track its usage
public int withReferenceCount = 0;
// What underlying WITH relation this really is
private RelationNode baseWithRelation;
private SysTable sysTable = null;
private boolean isLookupSet = false;
private boolean isLookup = false;
// For materialized relations (WITH)
private PartitionMap matPartitionMap;
/**
* Constructor
*/
public RelationNode() {
projectionList = new ArrayList<SqlExpression>();
conditionList = new ArrayList<QueryCondition>();
joinList = new ArrayList<RelationNode>();
condColumnList = new ArrayList<AttributeColumn>();
correlatedColumnList = new ArrayList<AttributeColumn>();
nodeType = TABLE;
}
/**
*
* @param aQueryTree what query tree this belongs to
*/
public void setQueryTree(QueryTree aQueryTree) {
queryTree = aQueryTree;
}
/**
*
* @return
*/
public QueryTree getQueryTree() {
return queryTree;
}
/**
*
* @return
*/
public boolean isSubquery() {
return nodeType == SUBQUERY_SCALAR || nodeType == SUBQUERY_RELATION
|| nodeType == SUBQUERY_NONCORRELATED
|| nodeType == SUBQUERY_CORRELATED;
}
// BUILD_CUT_START
// for helping to debug
/**
*
* @param prepend
*/
public void toString(String prepend) {
StringBuffer sbNode = new StringBuffer(512);
sbNode.append(prepend).append("--------------------------------------\n");
sbNode.append(prepend).append(" nodeType = ");
sbNode.append(nodeType);
sbNode.append('\n');
sbNode.append(prepend).append(" ").append(getTypeString());
sbNode.append('\n');
sbNode.append(prepend).append(" rowsize = ").append(rowsize);
sbNode.append('\n');
sbNode.append(prepend).append(" estCost = ").append(estCost);
sbNode.append('\n');
sbNode.append(prepend).append(" estRowsReturned = ").append(estRowsReturned);
sbNode.append('\n');
sbNode.append(prepend);
sbNode.append('\n');
sbNode.append(prepend).append(" ---Joins--- ");
sbNode.append('\n');
for (RelationNode joinNode : joinList) {
sbNode.append(prepend).append(" ").append(joinNode.tableName).append(joinNode.alias);
sbNode.append('\n');
}
sbNode.append(prepend);
sbNode.append('\n');
sbNode.append(prepend).append(" ---Projections--- ");
sbNode.append('\n');
for (SqlExpression aSqlExpression : projectionList) {
sbNode.append(aSqlExpression.toString(prepend));
sbNode.append('\n');
}
sbNode.append(prepend);
sbNode.append('\n');
sbNode.append(prepend).append(" ---Conditions--- ");
sbNode.append('\n');
sbNode.append(prepend).append(" Condition count: ").append(conditionList.size());
sbNode.append('\n');
for (QueryCondition aCondition : conditionList) {
sbNode.append(prepend).append(" ").append(aCondition.getCondString());
sbNode.append('\n');
}
sbNode.append(prepend);
sbNode.append('\n');
sbNode.append(prepend).append(" ---Condition Columns--- ");
sbNode.append('\n');
for (AttributeColumn aColumn : condColumnList) {
sbNode.append(prepend).append(" ").append(aColumn.columnName);
sbNode.append('\n');
}
}
// BUILD_CUT_ALT
// public void toString (String prepend) { return; }
// BUILD_CUT_END
// BUILD_CUT_START
// -----------------------------------------------------------------------
/**
*
* @return
*/
public String getTypeString() {
String typeString = "";
switch (nodeType) {
case TABLE:
typeString = "TABLE Table & Alias = " + tableName + " "
+ alias;
break;
case SUBQUERY_SCALAR:
typeString = "SUBQUERY_SCALAR";
break;
case SUBQUERY_RELATION:
typeString = "SUBQUERY_RELATION";
break;
case SUBQUERY_NONCORRELATED:
typeString = "SUBQUERY_NONCORRELATED";
break;
case SUBQUERY_CORRELATED:
typeString = "SUBQUERY_CORRELATED";
break;
case SUBQUERY_CORRELATED_PH:
typeString = "SUBQUERY_CORRELATED_PH";
break;
}
return typeString;
}
// BUILD_CUT_END
/**
* Given a column it tries to find the matching sql expression Test Cases :
* column ColName AliasName TableName Alias Name * First check for colname -
* as this should be the alias name of the column in the from expression.
*
* Incase we have the colname equals for more than one columns - check for
* the alias last of the table. Check if the alias are the same -
*
*
* @return
* @param column
* @throws org.postgresql.stado.exception.ColumnNotFoundException
*/
public SqlExpression getMatchingSqlExpression(AttributeColumn column)
throws ColumnNotFoundException {
/*
* If the node type is a relation sub query -
*/
if (nodeType == RelationNode.SUBQUERY_RELATION && subqueryTree != null) {
// extract all the sql expressions from the projection list
/*
* For each sql expression
*/
for (SqlExpression aSqlExpression : subqueryTree.getProjectionList()) {
/*
* Check if the outerAlias is the same name as the
* columnName as a Rule : 1.OuterAlias = Alias = ColumnAlias =
* ColumnName if (Expression is a column)
*
*/
if (!aSqlExpression.getOuterAlias()
.equalsIgnoreCase(column.columnName)) {
/*
* If the Outer Alias Match fails we look for a weaker
* match - This implies that a user can use an aliasName
* even when the OuterAlias Name is specifed provided
* there is no ambiguity.
*
* select n_nationkey from (select n_nationkey ns from
* nation ) z(n_s) The user can use n_s , n_nationkey or
* ns as there is no ambiguity.
*/
if(aSqlExpression.getAlias().equals("") &&
aSqlExpression.getProjectionLabel().equalsIgnoreCase(column.columnName)) {
return aSqlExpression;
}
if (!(aSqlExpression.getAlias()
.equalsIgnoreCase(column.columnName) &&
(tableName.equalsIgnoreCase(column.getTableName()) || alias.equalsIgnoreCase(column.getTableName())))) {
continue;
} else {
if(aSqlExpression.getOuterAlias().equals("")) {
aSqlExpression.setOuterAlias(aSqlExpression.getAlias());
}
return aSqlExpression;
}
} else {
return aSqlExpression;
}
}
} else if (nodeType == RelationNode.SUBQUERY_RELATION
&& baseWithRelation != null) { //refers to WITH
// extract all the sql expressions from the projection list
/*
* For each sql expression
*/
for (SqlExpression aSqlExpression : getProjectionList()) {
/*
* Check if the outerAlias is the same name as the
* columnName as a Rule : 1.OuterAlias = Alias = ColumnAlias =
* ColumnName if (Expression is a column)
*
*/
if (!aSqlExpression.getOuterAlias()
.equalsIgnoreCase(column.columnName)) {
/*
* If the Outer Alias Match fails we look for a weaker
* match - This implies that a user can use an aliasName
* even when the OuterAlias Name is specifed provided
* there is no ambiguity.
*
* select n_nationkey from (select n_nationkey ns from
* nation ) z(n_s) The user can use n_s , n_nationkey or
* ns as there is no ambiguity.
*/
if((aSqlExpression.getAlias().equals("")
|| aSqlExpression.getAlias().equalsIgnoreCase(column.columnName))
&& aSqlExpression.getProjectionLabel().equalsIgnoreCase(column.columnName)) {
return aSqlExpression;
}
if (!(aSqlExpression.getAlias()
.equalsIgnoreCase(column.columnName) &&
aSqlExpression.getColumn().getTableName().equalsIgnoreCase(column.getTableName()))) {
continue;
} else {
if(aSqlExpression.getOuterAlias().equals("")) {
aSqlExpression.setOuterAlias(aSqlExpression.getAlias());
}
return aSqlExpression;
}
} else {
return aSqlExpression;
}
}
}
throw new ColumnNotFoundException("columns " + column.columnName,
this.alias + "Table " + this.tableName);
}
public void handleAliasForSingleTableSubQueryNode() {
if(this.nodeType == RelationNode.SUBQUERY_RELATION && subqueryTree != null
&& ( tableName.equals("") || tableName.equalsIgnoreCase(alias))) {
if(subqueryTree.getRelationNodeList().size() == 1) {
RelationNode rn = subqueryTree.getRelationNodeList().get(0);
if(rn.nodeType == RelationNode.SUBQUERY_RELATION && subqueryTree != null) {
rn.handleAliasForSingleTableSubQueryNode();
}
if(rn.tableName == null && rn.alias == null) {
rn.tableName = this.alias;
rn.alias = this.alias;
}
if(rn.tableName.equalsIgnoreCase(rn.alias)) {
rn.alias = this.alias;
for(SqlExpression aSqlExp : rn.projectionList) {
aSqlExp.getColumn().setTableAlias(rn.alias);
aSqlExp.rebuildExpression();
}
for(SqlExpression aSqlExp : subqueryTree.getGroupByList()) {
aSqlExp.rebuildExpression();
}
for(SqlExpression aSqlExp : subqueryTree.getOrderByOrphans()) {
aSqlExp.rebuildExpression();
}
for(SqlExpression aSqlExp : subqueryTree.getScalarSubqueryList()) {
aSqlExp.rebuildExpression();
}
for(SqlExpression aSqlExp : subqueryTree.getSelectOrphans()) {
aSqlExp.rebuildExpression();
}
if(subqueryTree.getWhereRootCondition() != null) {
subqueryTree.getWhereRootCondition().rebuildCondString();
}
}
}
}
}
/**
*
* @return
*/
public static int getNextCount() {
return nextCount.incrementAndGet();
}
/**
*
*/
private String rebuildTableString() {
if (isOnly) {
relationString = " ONLY ";
} else {
relationString = "";
}
if (currentTempTableName.equals("")) {
relationString += IdentifierHandler.quote(tableName);
if (!tableName.equals(alias)) {
relationString += " as " + IdentifierHandler.quote(alias);
}
} else {
relationString += IdentifierHandler.quote(currentTempTableName);
}
return relationString;
}
/**
*
* @return
*/
public String rebuildString() {
switch (nodeType) {
case TABLE:
rebuildTableString();
break;
case RelationNode.SUBQUERY_RELATION:
if (isWithDerived()) {
if (baseWithRelation != null
&& currentTempTableName.isEmpty()
&& !baseWithRelation.currentTempTableName.isEmpty()) {
// Make sure we have set temp table name if possible
currentTempTableName = baseWithRelation.currentTempTableName;
}
rebuildTableString();
} else {
relationString = "("
+ subqueryTree.rebuildString()
+ ") as "
+ IdentifierHandler.quote(alias);
}
break;
// TODO : All the various types of subqueries are to be handled later
case RelationNode.SUBQUERY_CORRELATED_PH:
break;
case RelationNode.SUBQUERY_NONCORRELATED:
break;
case RelationNode.SUBQUERY_SCALAR:
break;
case RelationNode.SUBQUERY_CORRELATED:
break;
case RelationNode.FAKE:
break;
default:
throw new XDBServerException(
ErrorMessageRepository.ILLEGAL_RELATIONNODE_TYPE, 0,
ErrorMessageRepository.ILLEGAL_RELATIONNODE_TYPE_CODE);
}
return relationString;
}
/**
*
* @return
*/
public String getTableName() {
return tableName;
}
/**
*
* @param tableName
*/
public void setTableName(String tableName) {
this.tableName = tableName;
}
/**
*
* @return
*/
public String getTableAlis() {
return alias;
}
/**
*
* @return
*/
public boolean isTemporaryTable() {
return isTemporaryTable;
}
/**
*
* @param temporaryTable
*/
public void setTemporaryTable(boolean temporaryTable) {
isTemporaryTable = temporaryTable;
}
/**
*
* @return
*/
public boolean isOnly() {
return isOnly;
}
/**
*
* @param isOnly
*/
public void setOnly(boolean onlyTable) {
isOnly = onlyTable;
}
/**
*
* @return
*/
public XDBSessionContext getClient() {
return client;
}
/**
*
* @param client
*/
public void setClient(XDBSessionContext client) {
this.client = client;
}
// For join handling
/**
*
* @param parents
* @param isOuter
* @param newOuterLevel
*/
public void addParentNodes(Collection<RelationNode> parents,
boolean isOuter, short newOuterLevel) {
parentNodes.addAll(parents);
for (RelationNode parent : parents) {
parent.childrenNodes.add(this);
// This should probably not ever happen
if (parent.outerLevel > outerLevel) {
outerLevel = parent.outerLevel;
}
if (isOuter) {
outerLevel = newOuterLevel;
}
}
}
// For inner join handling
/**
*
* @param siblingNode
*/
public void addSiblingJoin(RelationNode siblingNode) {
// Set outer level to max of its siblings
if (siblingNode.outerLevel > outerLevel) {
outerLevel = siblingNode.outerLevel;
}
for (RelationNode parent : siblingNode.getParentNodes()) {
parentNodes.add(parent);
parent.childrenNodes.add(this);
}
}
/**
* @return true if this is a correlated RelationNode
*/
public boolean isCorrelatedSubquery () {
return nodeType == RelationNode.SUBQUERY_CORRELATED;
}
/**
* @return true if this is a correlated RelationNode
*/
public boolean isCorrelatedPlaceholder () {
return nodeType == RelationNode.SUBQUERY_CORRELATED_PH;
}
/**
* @return true if this is an uncorrelated RelationNode
*/
public boolean isUncorrelatedSubquery() {
return nodeType == RelationNode.SUBQUERY_NONCORRELATED;
}
/**
* @return true if this is a scalar subquery
*/
public boolean isScalarSubquery() {
return nodeType == RelationNode.SUBQUERY_SCALAR;
}
/**
* @return true if this is a relation subquery
*/
public boolean isRelationSubquery() {
return nodeType == RelationNode.SUBQUERY_RELATION;
}
/**
* @return true if this is a relation subquery
*/
public boolean isTable() {
return nodeType == RelationNode.TABLE;
}
//
/**
*
* @return
*/
public List<RelationNode> getParentNodes() {
return parentNodes;
}
//
/**
*
* @param node
* @param inserted
*/
public void notifyOuterLevelInserted(RelationNode node, short inserted) {
if (node != this && outerLevel >= inserted) {
outerLevel++;
}
}
/**
* Get the SysTable for this
*
* @param database the database to look up for this column
*
* @return the SysTable that this column belongs to
*/
public SysTable getSysTable ()
throws IllegalArgumentException, XDBServerException {
if (!isTable()) {
return null;
}
if (sysTable == null) {
if (client == null) {
throw new XDBServerException("Internal Error: RelationNode's cleint not set");
} else {
SysDatabase database = client.getSysDatabase();
sysTable = database.getSysTable(tableName);
}
}
return sysTable;
}
/**
* @return the outerLevel
*/
public short getOuterLevel() {
return outerLevel;
}
/**
* @param alias the alias to set
*/
public void setAlias(String alias) {
this.alias = alias;
}
/**
* @return the alias
*/
public String getAlias() {
return alias;
}
/**
* @return the condColumnList
*/
public List<AttributeColumn> getCondColumnList() {
return condColumnList;
}
/**
* @return the conditionList
*/
public List<QueryCondition> getConditionList() {
return conditionList;
}
/**
* @return the correlatedColumnList
*/
public List<AttributeColumn> getCorrelatedColumnList() {
return correlatedColumnList;
}
/**
* @param currentTempTableName the currentTempTableName to set
*/
public void setCurrentTempTableName(String currentTempTableName) {
if (currentTempTableName == null || currentTempTableName.isEmpty()) {
throw new XDBServerException("Setting empty currentTempTableName");
}
this.currentTempTableName = currentTempTableName;
}
/**
* @return the currentTempTableName
*/
public String getCurrentTempTableName() {
return currentTempTableName;
}
/**
* @param estCost the estCost to set
*/
public void setEstCost(long estCost) {
this.estCost = estCost;
}
/**
* @return the estCost
*/
public long getEstCost() {
return estCost;
}
/**
* @param estRowsReturned the estRowsReturned to set
*/
public void setEstRowsReturned(long estRowsReturned) {
this.estRowsReturned = estRowsReturned;
}
/**
* @return the estRowsReturned
*/
public long getEstRowsReturned() {
return estRowsReturned;
}
/**
* @return the hasNotOwnAlias
*/
public boolean hasNotOwnAlias() {
return hasNotOwnAlias;
}
/**
* @param joinList the joinList to set
*/
public void setJoinList(List<RelationNode> joinList) {
this.joinList = joinList;
}
/**
* @return the joinList
*/
public List<RelationNode> getJoinList() {
return joinList;
}
/**
* @param nodeId the nodeId to set
*/
public void setNodeId(int nodeId) {
this.nodeId = nodeId;
}
/**
* @return the nodeId
*/
public int getNodeId() {
return nodeId;
}
/**
* @param nodeType the nodeType to set
*/
public void setNodeType(int nodeType) {
this.nodeType = nodeType;
}
/**
* @return the nodeType
*/
public int getNodeType() {
return nodeType;
}
/**
* @param parentCorrelatedExpr the parentCorrelatedExpr to set
*/
public void setParentCorrelatedExpr(SqlExpression parentCorrelatedExpr) {
this.parentCorrelatedExpr = parentCorrelatedExpr;
}
/**
* @return the parentCorrelatedExpr
*/
public SqlExpression getParentCorrelatedExpr() {
return parentCorrelatedExpr;
}
/**
* @param parentNoncorExpr the parentNoncorExpr to set
*/
public void setParentNoncorExpr(SqlExpression parentNoncorExpr) {
this.parentNoncorExpr = parentNoncorExpr;
}
/**
* @return the parentNoncorExpr
*/
public SqlExpression getParentNoncorExpr() {
return parentNoncorExpr;
}
/**
* @return the projectionList
*/
public List<SqlExpression> getProjectionList() {
return projectionList;
}
/**
* @return the relationString
*/
public String getRelationString() {
return relationString;
}
/**
* @param rowsize the rowsize to set
*/
public void setRowsize(int rowsize) {
this.rowsize = rowsize;
}
/**
* @return the rowsize
*/
public int getRowsize() {
return rowsize;
}
/**
* @param subqueryTree the subqueryTree to set
*/
public void setSubqueryTree(QueryTree subqueryTree) {
if (getQueryTree() == subqueryTree) {
Exception e = new Exception("Cannot set subtree equal to tree a member of.");
e.printStackTrace();
}
this.subqueryTree = subqueryTree;
}
/**
* @return the subqueryTree
*/
public QueryTree getSubqueryTree() {
return subqueryTree;
}
public void setIsWith(boolean isWith) {
this.isWith = isWith;
}
public boolean isWith() {
return isWith;
}
public void setIsTopMostUsedWith(boolean isTopMostUsedWith) {
this.isTopMostUsedWith = isTopMostUsedWith;
}
public boolean isTopMostUsedWith() {
return isTopMostUsedWith;
}
/**
*
* @param baseWithRelation to set
*/
public void setBaseWithRelation(RelationNode baseWithRelation) {
this.baseWithRelation = baseWithRelation;
}
/**
*
* @return the base WITH relation that is actually being used
*/
public RelationNode getBaseWithRelation() {
return baseWithRelation;
}
/**
*
* @return whether or not this relation has been derived
* from a WITH statement. Note it does not mean that it
* is a WITH statement (isWith()), just that it is a
* wrapper node.
*/
public boolean isWithDerived() {
return baseWithRelation != null;
}
/**
* TODO: Expand for materialized relations
*
* @return if this relation consists of just a lookup table.
*
*/
public boolean isLookup() {
if (!isLookupSet) {
if (!isTable()) {
isLookup = false;
} else {
isLookup = getSysTable().isLookup();
}
isLookupSet = true;
}
return isLookup;
}
/**
* the node list for the relation
* @return the node list for the relation
*/
public Collection<DBNode> getNodeList() {
if (!isTable()) {
// if not a table assume all until more intelligence
// added
return client.getSysDatabase().getDBNodeList();
}
return getSysTable().getNodeList();
}
/**
*
* @return the column name the relation is partitioned on, or null
* if not partitioned
*/
public String getPartitionColumnName() {
if (isWithDerived()) {
// WITHs currently always partition by the first column
SqlExpression projExpr = projectionList.get(0);
if (projExpr.getExprType() != SqlExpression.SQLEX_COLUMN) {
return null;
}
return projExpr.getColumn().columnName;
} else {
SysColumn sysColumn = getPartitionedColumn();
if (sysColumn == null) {
return null;
}
return sysColumn.getColName();
}
}
/**
*
* @return the column the relation is partitioned on, or null
* if not partitioned
*/
private SysColumn getPartitionedColumn() {
if (!isTable()) {
return null;
}
return getSysTable().getPartitionedColumn();
}
/**
*
* @return whether or not the relation is hashPArtitioned
*/
public boolean isHashPartitioned() {
// If we are from a WITH, we assume that it is partitioned
if (isWithDerived()) {
return true;
} else {
return getSysTable().getPartitionScheme() == SysTable.PTYPE_HASH;
}
}
//public boolean onSameNodes(RelationNode otherRelationNode) {
// return getSysTable().onSameNodes(otherRelationNode.getSysTable(database)));
//}
/**
* @return if the relation exists only on a single node
*/
public boolean isOnSingleNode() {
return getSysTable().getPartitionScheme() == SysTable.PTYPE_ONE;
}
/**
*
*/
private void initMaterializedPartitionMap() {
if (client == null || client.getSysDatabase() == null) {
return;
}
matPartitionMap = new HashPartitionMap();
Collection<DBNode> dbNodeList = client.getSysDatabase().getDBNodeList();
ArrayList<Integer> nodeIdList = new ArrayList<Integer>(dbNodeList.size());
for (DBNode aDBNode : dbNodeList) {
nodeIdList.add(aDBNode.getNodeId());
}
matPartitionMap.generateDistribution(nodeIdList);
}
/**
*
* @return the partition map for the relation
*/
public PartitionMap getPartitionMap() {
if (!isTable()) {
if (isWithDerived()) {
if (matPartitionMap == null) {
initMaterializedPartitionMap();
}
return matPartitionMap;
} else {
return null;
}
}
return getSysTable().getPartitionMap();
}
public boolean onSameNodes(RelationNode otherRelationNode) {
/*
Collection myPartitions = getNodeList();
Collection otherPartitions = otherTable.getNodeList();
if (myPartitions.size() != otherPartitions.size()) {
// trivial
return false;
}
*/
return getPartitionMap().equals(otherRelationNode.getPartitionMap());
}
}