package com.taobao.tddl.optimizer.core.ast.query;
import static com.taobao.tddl.optimizer.utils.OptimizerToString.appendField;
import static com.taobao.tddl.optimizer.utils.OptimizerToString.appendln;
import static com.taobao.tddl.optimizer.utils.OptimizerToString.printFilterString;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import com.taobao.tddl.common.jdbc.ParameterContext;
import com.taobao.tddl.common.utils.GeneralUtil;
import com.taobao.tddl.optimizer.config.table.ColumnMeta;
import com.taobao.tddl.optimizer.config.table.IndexMeta;
import com.taobao.tddl.optimizer.config.table.TableMeta;
import com.taobao.tddl.optimizer.core.ASTNodeFactory;
import com.taobao.tddl.optimizer.core.ast.QueryTreeNode;
import com.taobao.tddl.optimizer.core.ast.build.QueryTreeNodeBuilder;
import com.taobao.tddl.optimizer.core.ast.build.TableNodeBuilder;
import com.taobao.tddl.optimizer.core.ast.dml.DeleteNode;
import com.taobao.tddl.optimizer.core.ast.dml.InsertNode;
import com.taobao.tddl.optimizer.core.ast.dml.PutNode;
import com.taobao.tddl.optimizer.core.ast.dml.UpdateNode;
import com.taobao.tddl.optimizer.core.expression.IBooleanFilter;
import com.taobao.tddl.optimizer.core.expression.IFilter;
import com.taobao.tddl.optimizer.core.expression.IFilter.OPERATION;
import com.taobao.tddl.optimizer.core.expression.IFunction;
import com.taobao.tddl.optimizer.core.expression.IOrderBy;
import com.taobao.tddl.optimizer.core.expression.ISelectable;
import com.taobao.tddl.optimizer.core.plan.IQueryTree;
import com.taobao.tddl.optimizer.core.plan.query.IJoin.JoinStrategy;
import com.taobao.tddl.optimizer.exceptions.QueryException;
import com.taobao.tddl.optimizer.utils.FilterUtils;
import com.taobao.tddl.optimizer.utils.OptimizerUtils;
/**
* 查询某个具体的真实表的Node 允许使用这个node,根据查询条件进行树的构建
*
* @author Dreamond
* @author whisper
* @author <a href="jianghang.loujh@taobao.com">jianghang</a>
* @since 5.0.0
*/
public class TableNode extends QueryTreeNode {
private final TableNodeBuilder builder;
private String tableName;
private String actualTableName; // 比如存在水平分表时,tableName代表逻辑表名,actualTableName代表物理表名
private IFilter indexQueryValueFilter = null;
private TableMeta tableMeta;
private IndexMeta indexUsed = null; // 当前逻辑表的使用index
private boolean fullTableScan = false; // 是否需要全表扫描
public TableNode(){
this(null);
}
public TableNode(String tableName){
this.tableName = tableName;
builder = new TableNodeBuilder(this);
}
@Override
public void build() {
if (this.isNeedBuild()) {
this.builder.build();
}
setNeedBuild(false);
}
@Override
public void assignment(Map<Integer, ParameterContext> parameterSettings) {
super.assignment(parameterSettings);
this.indexQueryValueFilter = OptimizerUtils.assignment(indexQueryValueFilter, parameterSettings);
}
@Override
public IQueryTree toDataNodeExecutor() throws QueryException {
return this.convertToJoinIfNeed().toDataNodeExecutor();
}
/**
* 根据索引信息构建查询树,可能需要进行主键join
*
* <pre>
* 分支:
* 1. 没选择索引,直接按照主键进行全表扫描
* 2. 选择了索引
* a. 选择的是主键索引,直接按照主键构造查询
* b. 选择的是非主键索引,需要考虑做主键二次join查询
* i. 如果索引信息里包含了所有的选择字段,直接基于主键查询返回,一次查询就够了
* ii. 包含了非索引中的字段,需要做回表查询.
* 先根据索引信息查询到主键,再根据主键查询出所需的其他字段,对应的join条件即为主键字段
* </pre>
*/
@Override
public QueryTreeNode convertToJoinIfNeed() {
if (this.getIndexUsed() == null || this.getIndexUsed().isPrimaryKeyIndex()) {
// 若不包含索引,则扫描主表即可或者使用主键索引
KVIndexNode keyIndexQuery = new KVIndexNode(this.getTableMeta().getPrimaryIndex().getName());
// 如果有别名,用别名,否则,用逻辑表名替代索引名
keyIndexQuery.alias(this.getName());
keyIndexQuery.setLimitFrom(this.getLimitFrom());
keyIndexQuery.setLimitTo(this.getLimitTo());
keyIndexQuery.select(OptimizerUtils.copySelectables(this.getColumnsSelected(), keyIndexQuery.getAlias()));
keyIndexQuery.setGroupBys(OptimizerUtils.copyOrderBys(this.getGroupBys(), keyIndexQuery.getAlias()));
keyIndexQuery.setOrderBys(OptimizerUtils.copyOrderBys(this.getOrderBys(), keyIndexQuery.getAlias()));
keyIndexQuery.having(OptimizerUtils.copyFilter(this.getHavingFilter(), keyIndexQuery.getAlias()));
keyIndexQuery.setOtherJoinOnFilter(OptimizerUtils.copyFilter(this.getOtherJoinOnFilter(),
keyIndexQuery.getAlias()));
keyIndexQuery.keyQuery(OptimizerUtils.copyFilter(this.getKeyFilter(), keyIndexQuery.getAlias()));
keyIndexQuery.valueQuery(FilterUtils.and(OptimizerUtils.copyFilter(this.getIndexQueryValueFilter(),
keyIndexQuery.getAlias()), OptimizerUtils.copyFilter(this.getResultFilter(), keyIndexQuery.getAlias())));
keyIndexQuery.executeOn(this.getDataNode());
keyIndexQuery.setSubQuery(this.isSubQuery());
keyIndexQuery.setFullTableScan(this.isFullTableScan());
keyIndexQuery.build();
return keyIndexQuery;
} else { // 非主键索引
IndexMeta indexUsed = this.getIndexUsed();
List<ISelectable> indexQuerySelected = new ArrayList<ISelectable>();
KVIndexNode indexQuery = new KVIndexNode(this.getIndexUsed().getName());
// 索引是否都包含在查询字段中
boolean isIndexCover = true;
List<ISelectable> allColumnsRefered = this.getColumnsRefered();
for (ISelectable selected : allColumnsRefered) {
if (selected instanceof IFunction) {
continue;
}
ColumnMeta cm = indexUsed.getColumnMeta(selected.getColumnName());
if (cm == null) {
isIndexCover = false;
} else {
indexQuerySelected.add(ASTNodeFactory.getInstance()
.createColumn()
.setColumnName(selected.getColumnName()));
}
}
indexQuery.select(indexQuerySelected);
// 索引覆盖的情况下,只需要返回索引查询
if (isIndexCover) {
indexQuery.alias(this.getName());
indexQuery.keyQuery(OptimizerUtils.copyFilter(this.getKeyFilter(), indexQuery.getAlias()));
indexQuery.valueQuery(OptimizerUtils.copyFilter(this.getIndexQueryValueFilter(), indexQuery.getAlias()));
indexQuery.select(OptimizerUtils.copySelectables(this.getColumnsSelected(), indexQuery.getAlias()));
indexQuery.setOrderBys(OptimizerUtils.copyOrderBys(this.getOrderBys(), indexQuery.getAlias()));
indexQuery.setGroupBys(OptimizerUtils.copyOrderBys(this.getGroupBys(), indexQuery.getAlias()));
indexQuery.setLimitFrom(this.getLimitFrom());
indexQuery.setLimitTo(this.getLimitTo());
indexQuery.executeOn(this.getDataNode());
indexQuery.setSubQuery(this.isSubQuery());
indexQuery.having(OptimizerUtils.copyFilter(this.getHavingFilter(), indexQuery.getAlias()));
indexQuery.valueQuery(FilterUtils.and(OptimizerUtils.copyFilter(this.getIndexQueryValueFilter(),
indexQuery.getAlias()), OptimizerUtils.copyFilter(this.getResultFilter(), indexQuery.getAlias())));
indexQuery.setOtherJoinOnFilter(OptimizerUtils.copyFilter(this.getOtherJoinOnFilter(),
indexQuery.getAlias()));
indexQuery.build();
return indexQuery;
} else {
indexQuery.alias(indexUsed.getNameWithOutDot());
indexQuery.keyQuery(OptimizerUtils.copyFilter(this.getKeyFilter(), indexQuery.getAlias()));
indexQuery.valueQuery(OptimizerUtils.copyFilter(this.getIndexQueryValueFilter(), indexQuery.getAlias()));
// 不是索引覆盖的情况下,需要回表,就是索引查询和主键查询
IndexMeta pk = this.getTableMeta().getPrimaryIndex();
// 由于按照主键join,主键也是被引用的列
for (ColumnMeta keyColumn : pk.getKeyColumns()) {
boolean has = false;
for (ISelectable s : allColumnsRefered) {
if (keyColumn.getName().equals(s.getColumnName())) {
has = true;
break;
}
}
if (!has) {// 不存在索引字段
allColumnsRefered.add(ASTNodeFactory.getInstance()
.createColumn()
.setColumnName(keyColumn.getName()));
indexQuery.addColumnsSelected(ASTNodeFactory.getInstance()
.createColumn()
.setColumnName(keyColumn.getName()));
}
}
List<ISelectable> keyQuerySelected = new ArrayList<ISelectable>();
KVIndexNode keyQuery = new KVIndexNode(pk.getName());
keyQuery.alias(this.getName());
for (ISelectable selected : allColumnsRefered) {
// 函数应该回表的时候做
if (selected instanceof IFunction) {
continue;
}
keyQuerySelected.add(ASTNodeFactory.getInstance()
.createColumn()
.setColumnName(selected.getColumnName()));
}
keyQuery.select(keyQuerySelected);
// mengshi 如果valueFilter中有index中的列,实际应该在indexQuery中做
keyQuery.valueQuery(OptimizerUtils.copyFilter(this.getResultFilter(), keyQuery.getAlias()));
JoinNode join = indexQuery.join(keyQuery);
// 按照PK进行join
for (ColumnMeta keyColumn : pk.getKeyColumns()) {
IBooleanFilter eq = ASTNodeFactory.getInstance().createBooleanFilter();
eq.setOperation(OPERATION.EQ);
eq.setColumn(ASTNodeFactory.getInstance()
.createColumn()
.setColumnName(keyColumn.getName())
.setTableName(indexUsed.getName()));
eq.setValue(ASTNodeFactory.getInstance()
.createColumn()
.setColumnName(keyColumn.getName())
.setTableName(pk.getName()));
join.addJoinFilter(eq);
}
String tableName = this.getTableName();
if (this.getAlias() != null) {
tableName = this.getAlias();
}
join.select(OptimizerUtils.copySelectables(this.getColumnsSelected(), tableName));
join.setOrderBys(OptimizerUtils.copyOrderBys(this.getOrderBys(), tableName));
join.setGroupBys(OptimizerUtils.copyOrderBys(this.getGroupBys(), tableName));
join.setUsedForIndexJoinPK(true);
join.setLimitFrom(this.getLimitFrom());
join.setLimitTo(this.getLimitTo());
join.executeOn(this.getDataNode());
join.setSubQuery(this.isSubQuery());
// 回表是IndexNestedLoop
join.setJoinStrategy(JoinStrategy.INDEX_NEST_LOOP);
join.setAlias(this.getAlias());
join.setSubAlias(this.getSubAlias());
join.having(OptimizerUtils.copyFilter(this.getHavingFilter(), tableName));
join.setOtherJoinOnFilter(OptimizerUtils.copyFilter(this.getOtherJoinOnFilter(), tableName));
join.build();
return join;
}
}
}
@Override
public List getImplicitOrderBys() {
// 如果有显示group by,直接使用group by
List<IOrderBy> orderByCombineWithGroupBy = getOrderByCombineWithGroupBy();
if (orderByCombineWithGroupBy != null) {
return orderByCombineWithGroupBy;
} else {
// 默认使用主键的索引信息进行order by
List<IOrderBy> implicitOrdersCandidate = OptimizerUtils.getOrderBy(this.tableMeta.getPrimaryIndex());
List<IOrderBy> implicitOrders = new ArrayList();
for (int i = 0; i < implicitOrdersCandidate.size(); i++) {
if (this.getColumnsSelected().contains(implicitOrdersCandidate.get(i).getColumn())) {
implicitOrders.add(implicitOrdersCandidate.get(i));
} else {
break;
}
}
return implicitOrders;
}
}
@Override
public QueryTreeNodeBuilder getBuilder() {
return builder;
}
@Override
public String getName() {
if (this.getAlias() != null) {
return this.getAlias();
}
return this.getTableName();
}
protected ISelectable getColumn(String name) {
if (this.getTableMeta() == null) {
this.build();
}
return this.getBuilder()
.getSelectableFromChild(ASTNodeFactory.getInstance().createColumn().setColumnName(name));
}
public TableNode setIndexUsed(IndexMeta indexUsed) {
this.indexUsed = indexUsed;
return this;
}
// ============= insert/update/delete/put==================
public InsertNode insert(List<ISelectable> columns, List<Object> values) {
InsertNode insert = new InsertNode(this);
insert.setColumns(columns);
insert.setValues(values);
return insert;
}
public InsertNode insert(String columns, Comparable values[]) {
if (columns == null || columns.isEmpty()) {
return this.insert(new String[] {}, values);
}
return this.insert(columns.split(" "), values);
}
public InsertNode insert(String columns[], Object values[]) {
List<ISelectable> cs = new LinkedList<ISelectable>();
for (String name : columns) {
ISelectable s = OptimizerUtils.createColumnFromString(name);
cs.add(s);
}
List<Object> valueList = new ArrayList<Object>(Arrays.asList(values));
return this.insert(cs, valueList);
}
public PutNode put(List<ISelectable> columns, List<Object> values) {
if (columns.size() != values.size()) {
throw new IllegalArgumentException("The size of the columns and values is not matched."
+ " columns' size is " + columns.size() + ". values' size is "
+ values.size());
}
PutNode put = new PutNode(this);
put.setColumns(columns);
put.setValues(values);
return put;
}
public PutNode put(String columns, Comparable values[]) {
return put(StringUtils.split(columns, ' '), values);
}
public PutNode put(String columns[], Object values[]) {
List<ISelectable> cs = new LinkedList<ISelectable>();
for (String name : columns) {
ISelectable s = OptimizerUtils.createColumnFromString(name);
cs.add(s);
}
List<Object> valueList = new ArrayList<Object>(Arrays.asList(values));
return put(cs, valueList);
}
public UpdateNode update(List<ISelectable> columns, List<Object> values) {
if (columns.size() != values.size()) {
throw new IllegalArgumentException("The size of the columns and values is not matched."
+ " columns' size is " + columns.size() + ". values' size is "
+ values.size());
}
UpdateNode update = new UpdateNode(this);
update.setUpdateValues(values);
update.setUpdateColumns(columns);
return update;
}
public UpdateNode update(String columns, Object values[]) {
return update(columns.split(" "), values);
}
public UpdateNode update(String columns[], Object values[]) {
List<ISelectable> cs = new LinkedList<ISelectable>();
for (String name : columns) {
ISelectable s = OptimizerUtils.createColumnFromString(name);
cs.add(s);
}
List<Object> valueList = new ArrayList<Object>(Arrays.asList(values));
return update(cs, valueList);
}
public DeleteNode delete() {
DeleteNode delete = new DeleteNode(this);
return delete;
}
// =============== copy =============
@Override
public TableNode copy() {
TableNode newTableNode = new TableNode(null);
this.copySelfTo(newTableNode);
return newTableNode;
}
@Override
protected void copySelfTo(QueryTreeNode to) {
super.copySelfTo(to);
TableNode toTable = (TableNode) to;
toTable.setFullTableScan(this.isFullTableScan());
toTable.setIndexQueryValueFilter((IFilter) (indexQueryValueFilter == null ? null : indexQueryValueFilter.copy()));
toTable.tableName = this.tableName;
toTable.actualTableName = this.actualTableName;
toTable.setTableMeta(this.getTableMeta());
toTable.useIndex(this.getIndexUsed());
}
@Override
public TableNode deepCopy() {
TableNode newTableNode = new TableNode(null);
this.deepCopySelfTo(newTableNode);
return newTableNode;
}
@Override
protected void deepCopySelfTo(QueryTreeNode to) {
super.deepCopySelfTo(to);
TableNode toTable = (TableNode) to;
toTable.setFullTableScan(this.isFullTableScan());
toTable.setIndexQueryValueFilter((IFilter) (indexQueryValueFilter == null ? null : indexQueryValueFilter.copy()));
toTable.tableName = this.tableName;
toTable.actualTableName = this.actualTableName;
toTable.setTableMeta(this.getTableMeta());
toTable.useIndex(this.getIndexUsed());
}
// ============== setter / getter==================
public boolean isFullTableScan() {
return this.fullTableScan;
}
public void setFullTableScan(boolean fullTableScan) {
this.fullTableScan = fullTableScan;
}
public IndexMeta getIndexUsed() {
return indexUsed;
}
public TableNode useIndex(IndexMeta index) {
this.indexUsed = index;
return this;
}
public List<IndexMeta> getIndexs() {
return this.getTableMeta().getIndexs();
}
public String getTableName() {
return this.tableName;
}
public TableMeta getTableMeta() {
return tableMeta;
}
public void setTableMeta(TableMeta tableMeta) {
this.tableMeta = tableMeta;
}
public IFilter getIndexQueryValueFilter() {
return indexQueryValueFilter;
}
public void setIndexQueryValueFilter(IFilter indexValueFilter) {
this.indexQueryValueFilter = indexValueFilter;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getActualTableName() {
return actualTableName;
}
public void setActualTableName(String actualTableName) {
this.actualTableName = actualTableName;
}
@Override
public String toString(int inden) {
String tabTittle = GeneralUtil.getTab(inden);
String tabContent = GeneralUtil.getTab(inden + 1);
StringBuilder sb = new StringBuilder();
if (this.getAlias() != null) {
appendln(sb, tabTittle + "Query from " + this.getTableName() + " as " + this.getAlias());
} else {
appendln(sb, tabTittle + "Query from " + this.getTableName());
}
appendField(sb, "actualTableName", this.getActualTableName(), tabContent);
appendField(sb, "keyFilter", printFilterString(this.getKeyFilter(), inden + 2), tabContent);
appendField(sb, "resultFilter", printFilterString(this.getResultFilter(), inden + 2), tabContent);
appendField(sb, "whereFilter", printFilterString(this.getWhereFilter(), inden + 2), tabContent);
appendField(sb,
"indexQueryValueFilter",
printFilterString(this.getIndexQueryValueFilter(), inden + 2),
tabContent);
appendField(sb, "otherJoinOnFilter", printFilterString(this.getOtherJoinOnFilter(), inden + 2), tabContent);
appendField(sb, "having", printFilterString(this.getHavingFilter(), inden + 2), tabContent);
if (this.getIndexUsed() != null) {
appendField(sb, "indexUsed", "\n" + this.getIndexUsed().toStringWithInden(inden + 2), tabContent);
}
if (!(this.getLimitFrom() != null && this.getLimitFrom().equals(0L) && this.getLimitTo() != null && this.getLimitTo()
.equals(0L))) {
appendField(sb, "limitFrom", this.getLimitFrom(), tabContent);
appendField(sb, "limitTo", this.getLimitTo(), tabContent);
}
if (this.isSubQuery()) {
appendField(sb, "isSubQuery", this.isSubQuery(), tabContent);
}
appendField(sb, "orderBy", this.getOrderBys(), tabContent);
appendField(sb, "queryConcurrency", this.getQueryConcurrency(), tabContent);
appendField(sb, "lockModel", this.getLockModel(), tabContent);
appendField(sb, "columns", this.getColumnsSelected(), tabContent);
appendField(sb, "groupBys", this.getGroupBys(), tabContent);
appendField(sb, "sql", this.getSql(), tabContent);
appendField(sb, "executeOn", this.getDataNode(), tabContent);
return sb.toString();
}
}