use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.
the class TestUnion method checkOrderByNode.
private void checkOrderByNode(AbstractPlanNode pn, String[] columns, int[] idxs) {
assertTrue(pn != null);
assertTrue(pn instanceof OrderByPlanNode);
OrderByPlanNode opn = (OrderByPlanNode) pn;
assertEquals(columns.length, opn.getOutputSchema().size());
for (int i = 0; i < columns.length; ++i) {
SchemaColumn col = opn.getOutputSchema().getColumns().get(i);
assertEquals(columns[i], col.getColumnAlias());
AbstractExpression colExpr = col.getExpression();
assertEquals(ExpressionType.VALUE_TUPLE, colExpr.getExpressionType());
assertEquals(idxs[i], ((TupleValueExpression) colExpr).getColumnIndex());
}
}
use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.
the class DDLCompiler method addIndexToCatalog.
private static void addIndexToCatalog(Database db, Table table, VoltXMLElement node, Map<String, String> indexReplacementMap, HashMap<String, Index> indexMap, HashMap<String, Column> columnMap, VoltCompiler compiler) throws VoltCompilerException {
assert node.name.equals("index");
String name = node.attributes.get("name");
boolean unique = Boolean.parseBoolean(node.attributes.get("unique"));
boolean assumeUnique = Boolean.parseBoolean(node.attributes.get("assumeunique"));
AbstractParsedStmt dummy = new ParsedSelectStmt(null, db);
dummy.setDDLIndexedTable(table);
StringBuffer msg = new StringBuffer(String.format("Index \"%s\" ", name));
// "parse" the expression trees for an expression-based index (vs. a simple column value index)
List<AbstractExpression> exprs = null;
// "parse" the WHERE expression for partial index if any
AbstractExpression predicate = null;
// Some expressions have special validation in indices. Not all the expression
// can be indexed. We scan for result type at first here and block those which
// can't be indexed like boolean, geo ... We gather rest of expression into
// checkExpressions list. We will check on them all at once.
List<AbstractExpression> checkExpressions = new ArrayList<>();
for (VoltXMLElement subNode : node.children) {
if (subNode.name.equals("exprs")) {
exprs = new ArrayList<>();
for (VoltXMLElement exprNode : subNode.children) {
AbstractExpression expr = dummy.parseExpressionTree(exprNode);
expr.resolveForTable(table);
expr.finalizeValueTypes();
// string will be populated with an expression's details when
// its value type is not indexable
StringBuffer exprMsg = new StringBuffer();
if (!expr.isValueTypeIndexable(exprMsg)) {
// indexing on expression with boolean result is not supported.
throw compiler.new VoltCompilerException("Cannot create index \"" + name + "\" because it contains " + exprMsg + ", which is not supported.");
}
if ((unique || assumeUnique) && !expr.isValueTypeUniqueIndexable(exprMsg)) {
// indexing on expression with boolean result is not supported.
throw compiler.new VoltCompilerException("Cannot create unique index \"" + name + "\" because it contains " + exprMsg + ", which is not supported.");
}
// rest of the validity guards will be evaluated after collecting all the expressions.
checkExpressions.add(expr);
exprs.add(expr);
}
} else if (subNode.name.equals("predicate")) {
assert (subNode.children.size() == 1);
VoltXMLElement predicateXML = subNode.children.get(0);
assert (predicateXML != null);
predicate = buildPartialIndexPredicate(dummy, name, predicateXML, table, compiler);
}
}
// Check all the subexpressions we gathered up.
if (!AbstractExpression.validateExprsForIndexesAndMVs(checkExpressions, msg)) {
// The error message will be in the StringBuffer msg.
throw compiler.new VoltCompilerException(msg.toString());
}
String colList = node.attributes.get("columns");
String[] colNames = colList.split(",");
Column[] columns = new Column[colNames.length];
boolean has_nonint_col = false;
boolean has_geo_col = false;
String nonint_col_name = null;
for (int i = 0; i < colNames.length; i++) {
columns[i] = columnMap.get(colNames[i]);
if (columns[i] == null) {
return;
}
}
UnsafeOperatorsForDDL unsafeOps = new UnsafeOperatorsForDDL();
if (exprs == null) {
for (int i = 0; i < colNames.length; i++) {
VoltType colType = VoltType.get((byte) columns[i].getType());
if (!colType.isIndexable()) {
String emsg = "Cannot create index \"" + name + "\" because " + colType.getName() + " values are not currently supported as index keys: \"" + colNames[i] + "\"";
throw compiler.new VoltCompilerException(emsg);
}
if ((unique || assumeUnique) && !colType.isUniqueIndexable()) {
String emsg = "Cannot create index \"" + name + "\" because " + colType.getName() + " values are not currently supported as unique index keys: \"" + colNames[i] + "\"";
throw compiler.new VoltCompilerException(emsg);
}
if (!colType.isBackendIntegerType()) {
has_nonint_col = true;
nonint_col_name = colNames[i];
has_geo_col = colType.equals(VoltType.GEOGRAPHY);
if (has_geo_col && colNames.length > 1) {
String emsg = "Cannot create index \"" + name + "\" because " + colType.getName() + " values must be the only component of an index key: \"" + nonint_col_name + "\"";
throw compiler.new VoltCompilerException(emsg);
}
}
}
} else {
for (AbstractExpression expression : exprs) {
VoltType colType = expression.getValueType();
if (!colType.isIndexable()) {
String emsg = "Cannot create index \"" + name + "\" because " + colType.getName() + " valued expressions are not currently supported as index keys.";
throw compiler.new VoltCompilerException(emsg);
}
if ((unique || assumeUnique) && !colType.isUniqueIndexable()) {
String emsg = "Cannot create index \"" + name + "\" because " + colType.getName() + " valued expressions are not currently supported as unique index keys.";
throw compiler.new VoltCompilerException(emsg);
}
if (!colType.isBackendIntegerType()) {
has_nonint_col = true;
nonint_col_name = "<expression>";
has_geo_col = colType.equals(VoltType.GEOGRAPHY);
if (has_geo_col) {
if (exprs.size() > 1) {
String emsg = "Cannot create index \"" + name + "\" because " + colType.getName() + " values must be the only component of an index key.";
throw compiler.new VoltCompilerException(emsg);
} else if (!(expression instanceof TupleValueExpression)) {
String emsg = "Cannot create index \"" + name + "\" because " + colType.getName() + " expressions must be simple column expressions.";
throw compiler.new VoltCompilerException(emsg);
}
}
}
expression.findUnsafeOperatorsForDDL(unsafeOps);
}
}
Index index = table.getIndexes().add(name);
index.setCountable(false);
index.setIssafewithnonemptysources(!unsafeOps.isUnsafe());
// Set the index type. It will be one of:
// - Covering cell index (geo index for CONTAINS predicates)
// - HASH index (set in HSQL because "hash" is in the name of the
// constraint or the index
// - TREE index, which is the default
boolean isHashIndex = node.attributes.get("ishashindex").equals("true");
if (has_geo_col) {
index.setType(IndexType.COVERING_CELL_INDEX.getValue());
} else if (isHashIndex) {
// warn user that hash index will be deprecated
compiler.addWarn("Hash indexes are deprecated. In a future release, VoltDB will only support tree indexes, even if the index name contains the string \"hash\"");
// make the index a hash.
if (has_nonint_col) {
String emsg = "Index " + name + " in table " + table.getTypeName() + " uses a non-hashable column " + nonint_col_name;
throw compiler.new VoltCompilerException(emsg);
}
index.setType(IndexType.HASH_TABLE.getValue());
} else {
index.setType(IndexType.BALANCED_TREE.getValue());
index.setCountable(true);
}
// but they still represent the columns that will trigger an index update when their values change.
for (int i = 0; i < columns.length; i++) {
ColumnRef cref = index.getColumns().add(columns[i].getTypeName());
cref.setColumn(columns[i]);
cref.setIndex(i);
}
if (exprs != null) {
try {
index.setExpressionsjson(convertToJSONArray(exprs));
} catch (JSONException e) {
throw compiler.new VoltCompilerException("Unexpected error serializing non-column expressions for index '" + name + "' on type '" + table.getTypeName() + "': " + e.toString());
}
}
index.setUnique(unique);
if (assumeUnique) {
index.setUnique(true);
}
index.setAssumeunique(assumeUnique);
if (predicate != null) {
try {
index.setPredicatejson(convertToJSONObject(predicate));
} catch (JSONException e) {
throw compiler.new VoltCompilerException("Unexpected error serializing predicate for partial index '" + name + "' on type '" + table.getTypeName() + "': " + e.toString());
}
}
// will make two indexes different
for (Index existingIndex : table.getIndexes()) {
// skip thineself
if (existingIndex == index) {
continue;
}
if (indexesAreDups(existingIndex, index)) {
// replace any constraints using one index with the other
//for () TODO
// get ready for replacements from constraints created later
indexReplacementMap.put(index.getTypeName(), existingIndex.getTypeName());
// if the index is a user-named index...
if (index.getTypeName().startsWith(HSQLInterface.AUTO_GEN_PREFIX) == false) {
// on dup-detection, add a warning but don't fail
String emsg = String.format("Dropping index %s on table %s because it duplicates index %s.", index.getTypeName(), table.getTypeName(), existingIndex.getTypeName());
compiler.addWarn(emsg);
}
// drop the index and GTFO
table.getIndexes().delete(index.getTypeName());
return;
}
}
String smsg = "Created index: " + name + " on table: " + table.getTypeName() + " of type: " + IndexType.get(index.getType()).name();
compiler.addInfo(smsg);
indexMap.put(name, index);
}
use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.
the class DDLCompiler method buildPartialIndexPredicate.
/**
* Build the abstract expression representing the partial index predicate.
* Verify it satisfies the rules. Throw error messages otherwise.
*
* @param dummy AbstractParsedStmt
* @param indexName The name of the index being checked.
* @param predicateXML The XML representing the predicate.
* @param table Table
* @throws VoltCompilerException
* @return AbstractExpression
*/
private static AbstractExpression buildPartialIndexPredicate(AbstractParsedStmt dummy, String indexName, VoltXMLElement predicateXML, Table table, VoltCompiler compiler) throws VoltCompilerException {
// Make sure all column expressions refer to the same index table
// before we can parse the XML to avoid the AbstractParsedStmt
// exception/assertion
String tableName = table.getTypeName();
assert (tableName != null);
String msg = "Partial index \"" + indexName + "\" ";
// Make sure all column expressions refer the index table
List<VoltXMLElement> columnRefs = predicateXML.findChildrenRecursively("columnref");
for (VoltXMLElement columnRef : columnRefs) {
String columnRefTableName = columnRef.attributes.get("table");
if (columnRefTableName != null && !tableName.equals(columnRefTableName)) {
msg += "with expression(s) involving other tables is not supported.";
throw compiler.new VoltCompilerException(msg);
}
}
// Now it safe to parse the expression tree
AbstractExpression predicate = dummy.parseExpressionTree(predicateXML);
if (predicate.hasAnySubexpressionOfClass(AggregateExpression.class)) {
msg += "with aggregate expression(s) is not supported.";
throw compiler.new VoltCompilerException(msg);
}
if (predicate.hasAnySubexpressionOfClass(AbstractSubqueryExpression.class)) {
msg += "with subquery expression(s) is not supported.";
throw compiler.new VoltCompilerException(msg);
}
return predicate;
}
use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.
the class MaterializedViewProcessor method findBestMatchIndexForMatviewMinOrMax.
// if the materialized view has MIN / MAX, try to find an index defined on the source table
// covering all group by cols / exprs to avoid expensive tablescan.
// For now, the only acceptable index is defined exactly on the group by columns IN ORDER.
// This allows the same key to be used to do lookups on the grouped table index and the
// base table index.
// TODO: More flexible (but usually less optimal*) indexes may be allowed here and supported
// in the EE in the future including:
// -- *indexes on the group keys listed out of order
// -- *indexes on the group keys as a prefix before other indexed values.
// -- (ENG-6511) indexes on the group keys PLUS the MIN/MAX argument value (to eliminate post-filtering)
// This function is mostly re-written for the fix of ENG-6511. --yzhang
private static Index findBestMatchIndexForMatviewMinOrMax(MaterializedViewInfo matviewinfo, Table srcTable, List<AbstractExpression> groupbyExprs, AbstractExpression minMaxAggExpr) {
CatalogMap<Index> allIndexes = srcTable.getIndexes();
StmtTableScan tableScan = new StmtTargetTableScan(srcTable);
// Candidate index. If we can find an index covering both group-by columns and aggExpr (optimal) then we will
// return immediately.
// If the index found covers only group-by columns (sub-optimal), we will first cache it here.
Index candidate = null;
for (Index index : allIndexes) {
// indexOptimalForMinMax == true if the index covered both the group-by columns and the min/max aggExpr.
boolean indexOptimalForMinMax = false;
// If minMaxAggExpr is not null, the diff can be zero or one.
// Otherwise, for a usable index, its number of columns must agree with that of the group-by columns.
final int diffAllowance = minMaxAggExpr == null ? 0 : 1;
// Get all indexed exprs if there is any.
String expressionjson = index.getExpressionsjson();
List<AbstractExpression> indexedExprs = null;
if (!expressionjson.isEmpty()) {
try {
indexedExprs = AbstractExpression.fromJSONArrayString(expressionjson, tableScan);
} catch (JSONException e) {
e.printStackTrace();
assert (false);
return null;
}
}
// Get source table columns.
List<Column> srcColumnArray = CatalogUtil.getSortedCatalogItems(srcTable.getColumns(), "index");
MatViewIndexMatchingGroupby matchingCase = null;
if (groupbyExprs == null) {
// This means group-by columns are all simple columns.
// It also means we can only access the group-by columns by colref.
List<ColumnRef> groupbyColRefs = CatalogUtil.getSortedCatalogItems(matviewinfo.getGroupbycols(), "index");
if (indexedExprs == null) {
matchingCase = MatViewIndexMatchingGroupby.GB_COL_IDX_COL;
// All the columns in the index are also simple columns, EASY! colref vs. colref
List<ColumnRef> indexedColRefs = CatalogUtil.getSortedCatalogItems(index.getColumns(), "index");
// indexedColRefs.size() == groupbyColRefs.size() + 1 (optimal, diffAllowance == 1)
if (isInvalidIndexCandidate(indexedColRefs.size(), groupbyColRefs.size(), diffAllowance)) {
continue;
}
if (!isGroupbyMatchingIndex(matchingCase, groupbyColRefs, null, indexedColRefs, null, null)) {
continue;
}
if (isValidIndexCandidateForMinMax(indexedColRefs.size(), groupbyColRefs.size(), diffAllowance)) {
if (!isIndexOptimalForMinMax(matchingCase, minMaxAggExpr, indexedColRefs, null, srcColumnArray)) {
continue;
}
indexOptimalForMinMax = true;
}
} else {
matchingCase = MatViewIndexMatchingGroupby.GB_COL_IDX_EXP;
// for index columns: convert tve => col
if (isInvalidIndexCandidate(indexedExprs.size(), groupbyColRefs.size(), diffAllowance)) {
continue;
}
if (!isGroupbyMatchingIndex(matchingCase, groupbyColRefs, null, null, indexedExprs, srcColumnArray)) {
continue;
}
if (isValidIndexCandidateForMinMax(indexedExprs.size(), groupbyColRefs.size(), diffAllowance)) {
if (!isIndexOptimalForMinMax(matchingCase, minMaxAggExpr, null, indexedExprs, null)) {
continue;
}
indexOptimalForMinMax = true;
}
}
} else {
matchingCase = MatViewIndexMatchingGroupby.GB_EXP_IDX_EXP;
// AND, indexedExprs must not be null in this case. (yeah!)
if (indexedExprs == null) {
continue;
}
if (isInvalidIndexCandidate(indexedExprs.size(), groupbyExprs.size(), diffAllowance)) {
continue;
}
if (!isGroupbyMatchingIndex(matchingCase, null, groupbyExprs, null, indexedExprs, null)) {
continue;
}
if (isValidIndexCandidateForMinMax(indexedExprs.size(), groupbyExprs.size(), diffAllowance)) {
if (!isIndexOptimalForMinMax(matchingCase, minMaxAggExpr, null, indexedExprs, null)) {
continue;
}
indexOptimalForMinMax = true;
}
}
// NOW index at least covered all group-by columns (sub-optimal candidate)
if (!index.getPredicatejson().isEmpty()) {
// Additional check for partial indexes to make sure matview WHERE clause
// covers the partial index predicate
List<AbstractExpression> coveringExprs = new ArrayList<>();
List<AbstractExpression> exactMatchCoveringExprs = new ArrayList<>();
try {
String encodedPredicate = matviewinfo.getPredicate();
if (!encodedPredicate.isEmpty()) {
String predicate = Encoder.hexDecodeToString(encodedPredicate);
AbstractExpression matViewPredicate = AbstractExpression.fromJSONString(predicate, tableScan);
coveringExprs.addAll(ExpressionUtil.uncombineAny(matViewPredicate));
}
} catch (JSONException e) {
e.printStackTrace();
assert (false);
return null;
}
String predicatejson = index.getPredicatejson();
if (!predicatejson.isEmpty() && !SubPlanAssembler.isPartialIndexPredicateCovered(tableScan, coveringExprs, predicatejson, exactMatchCoveringExprs)) {
// where clause -- give up on this index
continue;
}
}
// it is already the best index we can get, return immediately.
if (indexOptimalForMinMax) {
return index;
}
// otherwise wait to see if we can find something better!
candidate = index;
}
return candidate;
}
use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.
the class MaterializedViewProcessor method isGroupbyMatchingIndex.
private static boolean isGroupbyMatchingIndex(MatViewIndexMatchingGroupby matchingCase, List<ColumnRef> groupbyColRefs, List<AbstractExpression> groupbyExprs, List<ColumnRef> indexedColRefs, List<AbstractExpression> indexedExprs, List<Column> srcColumnArray) {
// Compare group-by columns/expressions for different cases
switch(matchingCase) {
case GB_COL_IDX_COL:
for (int i = 0; i < groupbyColRefs.size(); ++i) {
int groupbyColIndex = groupbyColRefs.get(i).getColumn().getIndex();
int indexedColIndex = indexedColRefs.get(i).getColumn().getIndex();
if (groupbyColIndex != indexedColIndex) {
return false;
}
}
break;
case GB_COL_IDX_EXP:
for (int i = 0; i < groupbyColRefs.size(); ++i) {
AbstractExpression indexedExpr = indexedExprs.get(i);
if (!(indexedExpr instanceof TupleValueExpression)) {
// Group-by columns are all simple columns, so indexedExpr must be tve.
return false;
}
int indexedColIdx = ((TupleValueExpression) indexedExpr).getColumnIndex();
Column indexedColumn = srcColumnArray.get(indexedColIdx);
Column groupbyColumn = groupbyColRefs.get(i).getColumn();
if (!indexedColumn.equals(groupbyColumn)) {
return false;
}
}
break;
case GB_EXP_IDX_EXP:
for (int i = 0; i < groupbyExprs.size(); ++i) {
if (!indexedExprs.get(i).equals(groupbyExprs.get(i))) {
return false;
}
}
break;
default:
assert (false);
// invalid option
return false;
}
// group-by columns/expressions are matched with the corresponding index
return true;
}
Aggregations