/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb.planner.parseinfo;
import java.util.ArrayList;
import java.util.List;
import org.voltdb.catalog.Column;
import org.voltdb.catalog.Index;
import org.voltdb.catalog.Table;
import org.voltdb.expressions.AbstractExpression;
import org.voltdb.expressions.ExpressionUtil;
import org.voltdb.expressions.TupleValueExpression;
import org.voltdb.plannodes.SchemaColumn;
import org.voltdb.utils.CatalogUtil;
/**
* StmtTableScan caches data related to a given instance of a table within the statement scope
*/
public class StmtTargetTableScan extends StmtTableScan {
// Catalog table
private final Table m_table;
private List<Index> m_indexes;
private List<Column> m_columns;
// An original subquery scan that was optimized out and replaced by this table scan
// It's required for the column indexes resolution
private StmtSubqueryScan m_origSubqueryScan = null;
public StmtTargetTableScan(Table table, String tableAlias, int stmtId) {
super(tableAlias, stmtId);
assert (table != null);
m_table = table;
findPartitioningColumns();
}
public StmtTargetTableScan(Table table) {
this(table, table.getTypeName(), 0);
}
@Override
public String getTableName() {
return m_table.getTypeName();
}
public Table getTargetTable() {
assert(m_table != null);
return m_table;
}
@Override
public boolean getIsReplicated() {
return m_table.getIsreplicated();
}
private List<SchemaColumn> findPartitioningColumns() {
if (m_partitioningColumns != null) {
return m_partitioningColumns;
}
if (getIsReplicated()) {
return null;
}
Column partitionCol = m_table.getPartitioncolumn();
// "(partitionCol != null)" tests around an obscure edge case.
// The table is declared non-replicated yet specifies no partitioning column.
// This can occur legitimately when views based on partitioned tables neglect to group by the partition column.
// The interpretation of this edge case is that the table has "randomly distributed data".
// In such a case, the table is valid for use by MP queries only and can only be joined with replicated tables
// because it has no recognized partitioning join key.
if (partitionCol == null) {
return null;
}
String tbName = m_table.getTypeName();
TupleValueExpression tve = new TupleValueExpression(
tbName, m_tableAlias, partitionCol, partitionCol.getIndex());
String colName = partitionCol.getTypeName();
SchemaColumn scol =
new SchemaColumn(tbName, m_tableAlias, colName, colName, tve);
m_partitioningColumns = new ArrayList<SchemaColumn>();
m_partitioningColumns.add(scol);
return m_partitioningColumns;
}
@Override
public List<Index> getIndexes() {
if (m_indexes == null) {
m_indexes = new ArrayList<Index>();
for (Index index : m_table.getIndexes()) {
m_indexes.add(index);
}
}
return m_indexes;
}
@Override
public String getColumnName(int columnIndex) {
if (m_columns == null) {
m_columns = CatalogUtil.getSortedCatalogItems(m_table.getColumns(), "index");
}
return m_columns.get(columnIndex).getTypeName();
}
@Override
public AbstractExpression processTVE(TupleValueExpression tve, String columnName) {
if (m_origSubqueryScan == null) {
tve.resolveForTable(m_table);
return tve;
}
// Example:
// SELECT TA1.CA CA1 FROM (SELECT T.C CA FROM T TA) TA1;
// gets simplified into
// SELECT TA1.C CA1 FROM T TA1;
// The TA1(TA1).(CA)CA1 TVE needs to be adjusted to be T(TA1).C(CA)
// since the original SELECT T.C CA FROM T TA subquery was optimized out.
// Table name TA1 is replaced with the original table name T
// Column name CA is replaced with the original column name C
// Expression differentiator to be replaced with the differentiator
// from the original column (T.C)
Integer columnIndex = m_origSubqueryScan.getColumnIndex(columnName,
tve.getDifferentiator());
assert(columnIndex != null);
SchemaColumn origColumnSchema = m_origSubqueryScan.getSchemaColumn(columnIndex);
assert(origColumnSchema != null);
String origColumnName = origColumnSchema.getColumnName();
// Get the original column expression and adjust its aliases
AbstractExpression colExpr = origColumnSchema.getExpression();
List<TupleValueExpression> tves = ExpressionUtil.getTupleValueExpressions(colExpr);
for (TupleValueExpression subqTve : tves) {
if (subqTve == colExpr) {
subqTve.setTableName(getTableName());
subqTve.setColumnName(origColumnName);
subqTve.setColumnAlias(tve.getColumnAlias());
}
subqTve.setTableAlias(tve.getTableAlias());
subqTve.resolveForTable(m_table);
}
return colExpr;
}
public void setOriginalSubqueryScan(StmtSubqueryScan origSubqueryScan) {
m_origSubqueryScan = origSubqueryScan;
}
}