use of org.voltdb.planner.parseinfo.StmtTargetTableScan in project voltdb by VoltDB.
the class AbstractParsedStmt method orderByColumnsCoverUniqueKeys.
/**
* Order by Columns or expressions has to operate on the display columns or expressions.
* @return
*/
protected boolean orderByColumnsCoverUniqueKeys() {
// In theory, if EVERY table in the query has a uniqueness constraint
// (primary key or other unique index) on columns that are all listed in the ORDER BY values,
// the result is deterministic.
// This holds regardless of whether the associated index is actually used in the selected plan,
// so this check is plan-independent.
//
// baseTableAliases associates table aliases with the order by
// expressions which reference them. Presumably by using
// table aliases we will map table scans to expressions rather
// than tables to expressions, and not confuse ourselves with
// different instances of the same table in self joins.
HashMap<String, List<AbstractExpression>> baseTableAliases = new HashMap<>();
for (ParsedColInfo col : orderByColumns()) {
AbstractExpression expr = col.expression;
//
// Compute the set of tables mentioned in the expression.
// 1. Search out all the TVEs.
// 2. Throw the aliases of the tables of each of these into a HashSet.
// The table must have an alias. It might not have a name.
// 3. If the HashSet has size > 1 we can't use this expression.
//
List<TupleValueExpression> baseTVEExpressions = expr.findAllTupleValueSubexpressions();
Set<String> baseTableNames = new HashSet<>();
for (TupleValueExpression tve : baseTVEExpressions) {
String tableAlias = tve.getTableAlias();
assert (tableAlias != null);
baseTableNames.add(tableAlias);
}
if (baseTableNames.size() != 1) {
// Neither are (nonsense) constant (table-less) expressions.
continue;
}
// Everything in the baseTVEExpressions table is a column
// in the same table and has the same alias. So just grab the first one.
// All we really want is the alias.
AbstractExpression baseTVE = baseTVEExpressions.get(0);
String nextTableAlias = ((TupleValueExpression) baseTVE).getTableAlias();
// and disappear.
assert (nextTableAlias != null);
List<AbstractExpression> perTable = baseTableAliases.get(nextTableAlias);
if (perTable == null) {
perTable = new ArrayList<>();
baseTableAliases.put(nextTableAlias, perTable);
}
perTable.add(expr);
}
if (m_tableAliasMap.size() > baseTableAliases.size()) {
// like Unique Index nested loop join, etc.
return false;
}
boolean allScansAreDeterministic = true;
for (Entry<String, List<AbstractExpression>> orderedAlias : baseTableAliases.entrySet()) {
List<AbstractExpression> orderedAliasExprs = orderedAlias.getValue();
StmtTableScan tableScan = getStmtTableScanByAlias(orderedAlias.getKey());
if (tableScan == null) {
assert (false);
return false;
}
if (tableScan instanceof StmtSubqueryScan) {
// don't yet handle FROM clause subquery, here.
return false;
}
Table table = ((StmtTargetTableScan) tableScan).getTargetTable();
// This table's scans need to be proven deterministic.
allScansAreDeterministic = false;
// Search indexes for one that makes the order by deterministic
for (Index index : table.getIndexes()) {
// skip non-unique indexes
if (!index.getUnique()) {
continue;
}
// get the list of expressions for the index
List<AbstractExpression> indexExpressions = new ArrayList<>();
String jsonExpr = index.getExpressionsjson();
// if this is a pure-column index...
if (jsonExpr.isEmpty()) {
for (ColumnRef cref : index.getColumns()) {
Column col = cref.getColumn();
TupleValueExpression tve = new TupleValueExpression(table.getTypeName(), orderedAlias.getKey(), col.getName(), col.getName(), col.getIndex());
indexExpressions.add(tve);
}
} else // if this is a fancy expression-based index...
{
try {
indexExpressions = AbstractExpression.fromJSONArrayString(jsonExpr, tableScan);
} catch (JSONException e) {
e.printStackTrace();
assert (false);
continue;
}
}
// ORDER BY A.unique_id, A.b_id
if (orderedAliasExprs.containsAll(indexExpressions)) {
allScansAreDeterministic = true;
break;
}
}
// ALL tables' scans need to have proved deterministic
if (!allScansAreDeterministic) {
return false;
}
}
return true;
}
use of org.voltdb.planner.parseinfo.StmtTargetTableScan in project voltdb by VoltDB.
the class AbstractParsedStmt method addSimplifiedSubqueryToStmtCache.
/**
* Replace an existing subquery scan with its underlying table scan. The subquery
* has already passed all the checks from the canSimplifySubquery method.
* Subquery ORDER BY clause is ignored if such exists.
*
* @param subqueryScan subquery scan to simplify
* @param tableAlias
* @return StmtTargetTableScan
*/
private StmtTargetTableScan addSimplifiedSubqueryToStmtCache(StmtSubqueryScan subqueryScan, StmtTargetTableScan tableScan) {
String tableAlias = subqueryScan.getTableAlias();
assert (tableAlias != null);
// It is guaranteed by the canSimplifySubquery that there is
// one and only one TABLE in the subquery's FROM clause.
Table promotedTable = tableScan.getTargetTable();
StmtTargetTableScan promotedScan = new StmtTargetTableScan(promotedTable, tableAlias, m_stmtId);
// Keep the original subquery scan to be able to tie the parent
// statement column/table names and aliases to the table's.
promotedScan.setOriginalSubqueryScan(subqueryScan);
// Replace the subquery scan with the table scan
StmtTableScan prior = m_tableAliasMap.put(tableAlias, promotedScan);
assert (prior == subqueryScan);
m_tableList.add(promotedTable);
return promotedScan;
}
use of org.voltdb.planner.parseinfo.StmtTargetTableScan in project voltdb by VoltDB.
the class AbstractParsedStmt method simplifierForSubquery.
/**
* Verify if a subquery can be replaced with a direct select from table(s)
* @param subquery
* @return TRUE/FALSE
*/
private StmtTargetTableScan simplifierForSubquery(AbstractParsedStmt subquery) {
// Must be a SELECT statement (not a SET operation)
if (!(subquery instanceof ParsedSelectStmt)) {
return null;
}
ParsedSelectStmt selectSubquery = (ParsedSelectStmt) subquery;
// No aggregation and/or GROUP BY is allowed
if (selectSubquery.hasAggregateOrGroupby()) {
return null;
}
// No DISTINCT
if (selectSubquery.hasAggregateDistinct()) {
return null;
}
// No windowed aggregate functions like RANK.
if (selectSubquery.hasWindowFunctionExpression()) {
return null;
}
// No LIMIT/OFFSET
if (selectSubquery.hasLimitOrOffset() || selectSubquery.hasLimitOrOffsetParameters()) {
return null;
}
// Only SELECT from a single TARGET TABLE is allowed
int tableCount = 0;
StmtTargetTableScan simpler = null;
for (Map.Entry<String, StmtTableScan> entry : selectSubquery.m_tableAliasMap.entrySet()) {
if (entry.getKey().startsWith(AbstractParsedStmt.TEMP_TABLE_NAME)) {
// This is an artificial table for a subquery expression
continue;
}
if (++tableCount > 1) {
return null;
}
// Only allow one TARGET TABLE, not a nested subquery.
StmtTableScan scan = entry.getValue();
if (scan instanceof StmtTargetTableScan) {
simpler = (StmtTargetTableScan) scan;
} else {
return null;
}
}
return simpler;
}
use of org.voltdb.planner.parseinfo.StmtTargetTableScan in project voltdb by VoltDB.
the class AggregatePlanNode method isTableCountNonDistinctNullableColumn.
public boolean isTableCountNonDistinctNullableColumn() {
if (!isTableNonDistinctCount()) {
return false;
}
// Is the expression a column?
AbstractExpression aggArgument = m_aggregateExpressions.get(0);
if (!aggArgument.getExpressionType().equals(ExpressionType.VALUE_TUPLE)) {
return false;
}
// If the query is a join query then the child will be something like nested loop.
assert (m_children.size() == 1);
if (!(m_children.get(0) instanceof AbstractScanPlanNode)) {
return false;
}
AbstractScanPlanNode asp = (AbstractScanPlanNode) m_children.get(0);
if (!(asp.getTableScan() instanceof StmtTargetTableScan)) {
return false;
}
StmtTargetTableScan sttscan = (StmtTargetTableScan) asp.getTableScan();
Table tbl = sttscan.getTargetTable();
TupleValueExpression tve = (TupleValueExpression) aggArgument;
String columnName = tve.getColumnName();
Column col = tbl.getColumns().get(columnName);
// Is the column nullable?
if (col.getNullable()) {
return false;
}
return true;
}
Aggregations