use of org.voltdb.compiler.VoltCompiler.VoltCompilerException in project voltdb by VoltDB.
the class DropStream method processStatement.
@Override
protected boolean processStatement(DDLStatement ddlStatement, Database db, DdlProceduresToLoad whichProcs) throws VoltCompilerException {
// matches if it is DROP STREAM
// group 1 is stream name
// guard against drop regular table
Matcher statementMatcher = SQLParser.matchDropStream(ddlStatement.statement);
if (statementMatcher.matches()) {
String streamName = checkIdentifierStart(statementMatcher.group(1), ddlStatement.statement);
if (isRegularTable(m_schema, streamName)) {
throw m_compiler.new VoltCompilerException(String.format("Invalid DROP STREAM statement: table %s is not a stream.", streamName));
}
m_returnAfterThis = true;
}
return false;
}
use of org.voltdb.compiler.VoltCompiler.VoltCompilerException in project voltdb by VoltDB.
the class PartitionStatement method processPartitionTable.
private boolean processPartitionTable(String statement) throws VoltCompilerException {
// matches if it is PARTITION TABLE <table> ON COLUMN <column>
Matcher statementMatcher = SQLParser.matchPartitionTable(statement);
if (!statementMatcher.matches()) {
throw m_compiler.new VoltCompilerException(String.format("Invalid PARTITION statement: \"%s\", " + "expected syntax: PARTITION TABLE <table> ON COLUMN <column>", // remove trailing semicolon
statement.substring(0, statement.length() - 1)));
}
// group(1) -> table, group(2) -> column
String tableName = checkIdentifierStart(statementMatcher.group(1), statement);
String columnName = checkIdentifierStart(statementMatcher.group(2), statement);
VoltXMLElement tableXML = m_schema.findChild("table", tableName.toUpperCase());
if (tableXML != null) {
tableXML.attributes.put("partitioncolumn", columnName.toUpperCase());
// Column validity check done by VoltCompiler in post-processing
// mark the table as dirty for the purposes of caching sql statements
m_compiler.markTableAsDirty(tableName);
} else {
throw m_compiler.new VoltCompilerException(String.format("Invalid PARTITION statement: table %s does not exist", tableName));
}
return true;
}
use of org.voltdb.compiler.VoltCompiler.VoltCompilerException in project voltdb by VoltDB.
the class MaterializedViewProcessor method startProcessing.
/**
* Add materialized view info to the catalog for the tables that are
* materialized views.
* @throws VoltCompilerException
*/
public void startProcessing(Database db, HashMap<Table, String> matViewMap, TreeSet<String> exportTableNames) throws VoltCompilerException {
HashSet<String> viewTableNames = new HashSet<>();
for (Entry<Table, String> entry : matViewMap.entrySet()) {
viewTableNames.add(entry.getKey().getTypeName());
}
for (Entry<Table, String> entry : matViewMap.entrySet()) {
Table destTable = entry.getKey();
String query = entry.getValue();
// get the xml for the query
VoltXMLElement xmlquery = null;
try {
xmlquery = m_hsql.getXMLCompiledStatement(query);
} catch (HSQLParseException e) {
e.printStackTrace();
}
assert (xmlquery != null);
// parse the xml like any other sql statement
ParsedSelectStmt stmt = null;
try {
stmt = (ParsedSelectStmt) AbstractParsedStmt.parse(query, xmlquery, null, db, null);
} catch (Exception e) {
throw m_compiler.new VoltCompilerException(e.getMessage());
}
assert (stmt != null);
String viewName = destTable.getTypeName();
// throw an error if the view isn't within voltdb's limited world view
checkViewMeetsSpec(viewName, stmt);
// The primary key index is yet to be defined (below).
for (Index destIndex : destTable.getIndexes()) {
if (destIndex.getUnique() || destIndex.getAssumeunique()) {
String msg = "A UNIQUE or ASSUMEUNIQUE index is not allowed on a materialized view. " + "Remove the qualifier from the index " + destIndex.getTypeName() + "defined on the materialized view \"" + viewName + "\".";
throw m_compiler.new VoltCompilerException(msg);
}
}
// A Materialized view cannot depend on another view.
for (Table srcTable : stmt.m_tableList) {
if (viewTableNames.contains(srcTable.getTypeName())) {
String msg = String.format("A materialized view (%s) can not be defined on another view (%s).", viewName, srcTable.getTypeName());
throw m_compiler.new VoltCompilerException(msg);
}
}
// The existing code base still need this materializer field to tell if a table
// is a materialized view table. Leaving this for future refactoring.
destTable.setMaterializer(stmt.m_tableList.get(0));
List<Column> destColumnArray = CatalogUtil.getSortedCatalogItems(destTable.getColumns(), "index");
List<AbstractExpression> groupbyExprs = null;
if (stmt.hasComplexGroupby()) {
groupbyExprs = new ArrayList<>();
for (ParsedColInfo col : stmt.groupByColumns()) {
groupbyExprs.add(col.expression);
}
}
// Generate query XMLs for min/max recalculation (ENG-8641)
boolean isMultiTableView = stmt.m_tableList.size() > 1;
MatViewFallbackQueryXMLGenerator xmlGen = new MatViewFallbackQueryXMLGenerator(xmlquery, stmt.groupByColumns(), stmt.m_displayColumns, isMultiTableView);
List<VoltXMLElement> fallbackQueryXMLs = xmlGen.getFallbackQueryXMLs();
// index or constraint in order to avoid error and crash.
if (stmt.groupByColumns().size() != 0) {
Index pkIndex = destTable.getIndexes().add(HSQLInterface.AUTO_GEN_MATVIEW_IDX);
pkIndex.setType(IndexType.BALANCED_TREE.getValue());
pkIndex.setUnique(true);
// assume index 1 throuh #grpByCols + 1 are the cols
for (int i = 0; i < stmt.groupByColumns().size(); i++) {
ColumnRef c = pkIndex.getColumns().add(String.valueOf(i));
c.setColumn(destColumnArray.get(i));
c.setIndex(i);
}
Constraint pkConstraint = destTable.getConstraints().add(HSQLInterface.AUTO_GEN_MATVIEW_CONST);
pkConstraint.setType(ConstraintType.PRIMARY_KEY.getValue());
pkConstraint.setIndex(pkIndex);
}
// If we have an unsafe MV message, then
// remember it here. We don't really know how
// to transfer the message through the catalog, but
// we can transmit the existence of the message.
boolean isSafeForDDL = (stmt.getUnsafeMVMessage() == null);
// Here the code path diverges for different kinds of views (single table view and joined table view)
if (isMultiTableView) {
// Materialized view on joined tables
// Add mvHandlerInfo to the destTable:
MaterializedViewHandlerInfo mvHandlerInfo = destTable.getMvhandlerinfo().add("mvHandlerInfo");
mvHandlerInfo.setDesttable(destTable);
for (Table srcTable : stmt.m_tableList) {
// Now we do not support having a view on persistent tables joining streamed tables.
if (exportTableNames.contains(srcTable.getTypeName())) {
String msg = String.format("A materialized view (%s) on joined tables cannot have streamed table (%s) as its source.", viewName, srcTable.getTypeName());
throw m_compiler.new VoltCompilerException(msg);
}
// The view table will need to keep a list of its source tables.
// The list is used to install / uninstall the view reference on the source tables when the
// view handler is constructed / destroyed.
TableRef tableRef = mvHandlerInfo.getSourcetables().add(srcTable.getTypeName());
tableRef.setTable(srcTable);
// There could be more than one partition column candidate, but we will only use the first one we found.
if (destTable.getPartitioncolumn() == null && srcTable.getPartitioncolumn() != null) {
Column partitionColumn = srcTable.getPartitioncolumn();
String partitionColName = partitionColumn.getTypeName();
String srcTableName = srcTable.getTypeName();
destTable.setIsreplicated(false);
if (stmt.hasComplexGroupby()) {
for (int i = 0; i < groupbyExprs.size(); i++) {
AbstractExpression groupbyExpr = groupbyExprs.get(i);
if (groupbyExpr instanceof TupleValueExpression) {
TupleValueExpression tve = (TupleValueExpression) groupbyExpr;
if (tve.getTableName().equals(srcTableName) && tve.getColumnName().equals(partitionColName)) {
// The partition column is set to destColumnArray.get(i), because we have the restriction
// that the non-aggregate columns must come at the very begining, and must exactly match
// the group-by columns.
// If we are going to remove this restriction in the future, then we need to do more work
// in order to find a proper partition column.
destTable.setPartitioncolumn(destColumnArray.get(i));
break;
}
}
}
} else {
for (int i = 0; i < stmt.groupByColumns().size(); i++) {
ParsedColInfo gbcol = stmt.groupByColumns().get(i);
if (gbcol.tableName.equals(srcTableName) && gbcol.columnName.equals(partitionColName)) {
destTable.setPartitioncolumn(destColumnArray.get(i));
break;
}
}
}
}
// end find partition column
}
// end for each source table
compileFallbackQueriesAndUpdateCatalog(db, query, fallbackQueryXMLs, mvHandlerInfo);
compileCreateQueryAndUpdateCatalog(db, query, xmlquery, mvHandlerInfo);
mvHandlerInfo.setGroupbycolumncount(stmt.groupByColumns().size());
for (int i = 0; i < stmt.m_displayColumns.size(); i++) {
ParsedColInfo col = stmt.m_displayColumns.get(i);
Column destColumn = destColumnArray.get(i);
setTypeAttributesForColumn(destColumn, col.expression);
// Set the expression type here to determine the behavior of the merge function.
destColumn.setAggregatetype(col.expression.getExpressionType().getValue());
}
mvHandlerInfo.setIssafewithnonemptysources(isSafeForDDL);
} else {
// =======================================================================================
// Materialized view on single table
// create the materializedviewinfo catalog node for the source table
Table srcTable = stmt.m_tableList.get(0);
MaterializedViewInfo matviewinfo = srcTable.getViews().add(viewName);
matviewinfo.setDest(destTable);
AbstractExpression where = stmt.getSingleTableFilterExpression();
if (where != null) {
String hex = Encoder.hexEncode(where.toJSONString());
matviewinfo.setPredicate(hex);
} else {
matviewinfo.setPredicate("");
}
List<Column> srcColumnArray = CatalogUtil.getSortedCatalogItems(srcTable.getColumns(), "index");
if (stmt.hasComplexGroupby()) {
// Parse group by expressions to json string
String groupbyExprsJson = null;
try {
groupbyExprsJson = DDLCompiler.convertToJSONArray(groupbyExprs);
} catch (JSONException e) {
throw m_compiler.new VoltCompilerException("Unexpected error serializing non-column " + "expressions for group by expressions: " + e.toString());
}
matviewinfo.setGroupbyexpressionsjson(groupbyExprsJson);
} else {
// add the group by columns from the src table
for (int i = 0; i < stmt.groupByColumns().size(); i++) {
ParsedColInfo gbcol = stmt.groupByColumns().get(i);
Column srcCol = srcColumnArray.get(gbcol.index);
ColumnRef cref = matviewinfo.getGroupbycols().add(srcCol.getTypeName());
// groupByColumns is iterating in order of groups. Store that grouping order
// in the column ref index. When the catalog is serialized, it will, naturally,
// scramble this order like a two year playing dominos, presenting the data
// in a meaningless sequence.
// the column offset in the view's grouping order
cref.setIndex(i);
// the source column from the base (non-view) table
cref.setColumn(srcCol);
// parse out the group by columns into the dest table
ParsedColInfo col = stmt.m_displayColumns.get(i);
Column destColumn = destColumnArray.get(i);
processMaterializedViewColumn(srcTable, destColumn, ExpressionType.VALUE_TUPLE, (TupleValueExpression) col.expression);
}
}
// Set up COUNT(*) column
ParsedColInfo countCol = stmt.m_displayColumns.get(stmt.groupByColumns().size());
assert (countCol.expression.getExpressionType() == ExpressionType.AGGREGATE_COUNT_STAR);
assert (countCol.expression.getLeft() == null);
processMaterializedViewColumn(srcTable, destColumnArray.get(stmt.groupByColumns().size()), ExpressionType.AGGREGATE_COUNT_STAR, null);
// prepare info for aggregation columns.
List<AbstractExpression> aggregationExprs = new ArrayList<>();
boolean hasAggregationExprs = false;
ArrayList<AbstractExpression> minMaxAggs = new ArrayList<>();
for (int i = stmt.groupByColumns().size() + 1; i < stmt.m_displayColumns.size(); i++) {
ParsedColInfo col = stmt.m_displayColumns.get(i);
AbstractExpression aggExpr = col.expression.getLeft();
if (aggExpr.getExpressionType() != ExpressionType.VALUE_TUPLE) {
hasAggregationExprs = true;
}
aggregationExprs.add(aggExpr);
if (col.expression.getExpressionType() == ExpressionType.AGGREGATE_MIN || col.expression.getExpressionType() == ExpressionType.AGGREGATE_MAX) {
minMaxAggs.add(aggExpr);
}
}
compileFallbackQueriesAndUpdateCatalog(db, query, fallbackQueryXMLs, matviewinfo);
// set Aggregation Expressions.
if (hasAggregationExprs) {
String aggregationExprsJson = null;
try {
aggregationExprsJson = DDLCompiler.convertToJSONArray(aggregationExprs);
} catch (JSONException e) {
throw m_compiler.new VoltCompilerException("Unexpected error serializing non-column " + "expressions for aggregation expressions: " + e.toString());
}
matviewinfo.setAggregationexpressionsjson(aggregationExprsJson);
}
// Find index for each min/max aggCol/aggExpr (ENG-6511 and ENG-8512)
for (Integer i = 0; i < minMaxAggs.size(); ++i) {
Index found = findBestMatchIndexForMatviewMinOrMax(matviewinfo, srcTable, groupbyExprs, minMaxAggs.get(i));
IndexRef refFound = matviewinfo.getIndexforminmax().add(i.toString());
if (found != null) {
refFound.setName(found.getTypeName());
} else {
refFound.setName("");
}
}
// The COUNT(*) should return a BIGINT column, whereas we found here the COUNT(*) was assigned a INTEGER column.
for (int i = 0; i <= stmt.groupByColumns().size(); i++) {
ParsedColInfo col = stmt.m_displayColumns.get(i);
Column destColumn = destColumnArray.get(i);
setTypeAttributesForColumn(destColumn, col.expression);
}
// parse out the aggregation columns into the dest table
for (int i = stmt.groupByColumns().size() + 1; i < stmt.m_displayColumns.size(); i++) {
ParsedColInfo col = stmt.m_displayColumns.get(i);
Column destColumn = destColumnArray.get(i);
AbstractExpression colExpr = col.expression.getLeft();
TupleValueExpression tve = null;
if (colExpr.getExpressionType() == ExpressionType.VALUE_TUPLE) {
tve = (TupleValueExpression) colExpr;
}
processMaterializedViewColumn(srcTable, destColumn, col.expression.getExpressionType(), tve);
setTypeAttributesForColumn(destColumn, col.expression);
}
if (srcTable.getPartitioncolumn() != null) {
// Set the partitioning of destination tables of associated views.
// If a view's source table is replicated, then a full scan of the
// associated view is single-sited. If the source is partitioned,
// a full scan of the view must be distributed, unless it is filtered
// by the original table's partitioning key, which, to be filtered,
// must also be a GROUP BY key.
destTable.setIsreplicated(false);
setGroupedTablePartitionColumn(matviewinfo, srcTable.getPartitioncolumn());
}
matviewinfo.setIssafewithnonemptysources(isSafeForDDL);
}
// end if single table view materialized view.
}
}
use of org.voltdb.compiler.VoltCompiler.VoltCompilerException in project voltdb by VoltDB.
the class MaterializedViewProcessor method checkViewMeetsSpec.
/**
* Verify the materialized view meets our arcane rules about what can and can't
* go in a materialized view. Throw hopefully helpful error messages when these
* rules are inevitably borked.
*
* @param viewName The name of the view being checked.
* @param stmt The output from the parser describing the select statement that creates the view.
* @throws VoltCompilerException
*/
private void checkViewMeetsSpec(String viewName, ParsedSelectStmt stmt) throws VoltCompilerException {
int groupColCount = stmt.groupByColumns().size();
int displayColCount = stmt.m_displayColumns.size();
StringBuffer msg = new StringBuffer();
msg.append("Materialized view \"" + viewName + "\" ");
if (stmt.getParameters().length > 0) {
msg.append("contains placeholders (?), which are not allowed in the SELECT query for a view.");
throw m_compiler.new VoltCompilerException(msg.toString());
}
List<AbstractExpression> checkExpressions = new ArrayList<>();
int i;
// the beginning of the display list.
for (i = 0; i < groupColCount; i++) {
ParsedColInfo gbcol = stmt.groupByColumns().get(i);
ParsedColInfo outcol = stmt.m_displayColumns.get(i);
// The columns must be equal.
if (!outcol.expression.equals(gbcol.expression)) {
msg.append("must exactly match the GROUP BY clause at index " + String.valueOf(i) + " of SELECT list.");
throw m_compiler.new VoltCompilerException(msg.toString());
}
// check if the expression return type is not unique indexable
StringBuffer exprMsg = new StringBuffer();
if (!outcol.expression.isValueTypeUniqueIndexable(exprMsg)) {
msg.append("with " + exprMsg + " in GROUP BY clause not supported.");
throw m_compiler.new VoltCompilerException(msg.toString());
}
// collect all the expressions and we will check
// for other guards on all of them together
checkExpressions.add(outcol.expression);
}
// check for count star in the display list
boolean countStarFound = false;
if (i < displayColCount) {
AbstractExpression coli = stmt.m_displayColumns.get(i).expression;
if (coli.getExpressionType() == ExpressionType.AGGREGATE_COUNT_STAR) {
countStarFound = true;
}
}
if (countStarFound == false) {
msg.append("must have count(*) after the GROUP BY columns (if any) but before the aggregate functions (if any).");
throw m_compiler.new VoltCompilerException(msg.toString());
}
UnsafeOperatorsForDDL unsafeOps = new UnsafeOperatorsForDDL();
// must be count(), min(), max() or sum().
for (i++; i < displayColCount; i++) {
ParsedColInfo outcol = stmt.m_displayColumns.get(i);
// second one would fail.
if ((outcol.expression.getExpressionType() != ExpressionType.AGGREGATE_COUNT) && (outcol.expression.getExpressionType() != ExpressionType.AGGREGATE_SUM) && (outcol.expression.getExpressionType() != ExpressionType.AGGREGATE_MIN) && (outcol.expression.getExpressionType() != ExpressionType.AGGREGATE_MAX)) {
msg.append("must have non-group by columns aggregated by sum, count, min or max.");
throw m_compiler.new VoltCompilerException(msg.toString());
}
// want to fail on legal aggregate expressions.
if (outcol.expression.getLeft() != null) {
checkExpressions.add(outcol.expression.getLeft());
}
// Check if the aggregation is safe for non-empty view source table.
outcol.expression.findUnsafeOperatorsForDDL(unsafeOps);
assert (outcol.expression.getRight() == null);
assert (outcol.expression.getArgs() == null || outcol.expression.getArgs().size() == 0);
}
AbstractExpression where = stmt.getSingleTableFilterExpression();
if (where != null) {
checkExpressions.add(where);
}
/*
* Gather up all the join expressions. The ParsedSelectStatement
* has not been analyzed yet, so it's not clear where these are. But
* the stmt knows.
*/
stmt.gatherJoinExpressions(checkExpressions);
if (stmt.getHavingPredicate() != null) {
checkExpressions.add(stmt.getHavingPredicate());
}
// Check all the subexpressions we gathered up.
if (!AbstractExpression.validateExprsForIndexesAndMVs(checkExpressions, msg)) {
// The error message will be in the StringBuffer msg.
throw m_compiler.new VoltCompilerException(msg.toString());
}
// views on nonempty tables.
for (AbstractExpression expr : checkExpressions) {
expr.findUnsafeOperatorsForDDL(unsafeOps);
}
if (unsafeOps.isUnsafe()) {
stmt.setUnsafeDDLMessage(unsafeOps.toString());
}
if (stmt.hasSubquery()) {
msg.append("with subquery sources is not supported.");
throw m_compiler.new VoltCompilerException(msg.toString());
}
if (!stmt.m_joinTree.allInnerJoins()) {
throw m_compiler.new VoltCompilerException("Materialized view only supports INNER JOIN.");
}
if (stmt.orderByColumns().size() != 0) {
msg.append("with ORDER BY clause is not supported.");
throw m_compiler.new VoltCompilerException(msg.toString());
}
if (stmt.hasLimitOrOffset()) {
msg.append("with LIMIT or OFFSET clause is not supported.");
throw m_compiler.new VoltCompilerException(msg.toString());
}
if (stmt.getHavingPredicate() != null) {
msg.append("with HAVING clause is not supported.");
throw m_compiler.new VoltCompilerException(msg.toString());
}
if (displayColCount <= groupColCount) {
msg.append("has too few columns.");
throw m_compiler.new VoltCompilerException(msg.toString());
}
checkViewSources(stmt.m_tableList);
}
use of org.voltdb.compiler.VoltCompiler.VoltCompilerException in project voltdb by VoltDB.
the class DDLCompiler method addConstraintToCatalog.
/**
* Add a constraint on a given table to the catalog
* @param table The table on which the constraint will be enforced
* @param node The XML node representing the constraint
* @param indexReplacementMap
* @throws VoltCompilerException
*/
private void addConstraintToCatalog(Table table, VoltXMLElement node, Map<String, String> indexReplacementMap, Map<String, Index> indexMap) throws VoltCompilerException {
assert node.name.equals("constraint");
String name = node.attributes.get("name");
String typeName = node.attributes.get("constrainttype");
ConstraintType type = ConstraintType.valueOf(typeName);
String tableName = table.getTypeName();
if (type == ConstraintType.LIMIT) {
int tupleLimit = Integer.parseInt(node.attributes.get("rowslimit"));
if (tupleLimit < 0) {
throw m_compiler.new VoltCompilerException("Invalid constraint limit number '" + tupleLimit + "'");
}
if (tableLimitConstraintCounter.contains(tableName)) {
throw m_compiler.new VoltCompilerException("Too many table limit constraints for table " + tableName);
} else {
tableLimitConstraintCounter.add(tableName);
}
table.setTuplelimit(tupleLimit);
String deleteStmt = node.attributes.get("rowslimitdeletestmt");
if (deleteStmt != null) {
Statement catStmt = table.getTuplelimitdeletestmt().add("limit_delete");
catStmt.setSqltext(deleteStmt);
validateTupleLimitDeleteStmt(catStmt);
}
return;
}
if (type == ConstraintType.CHECK) {
String msg = "VoltDB does not enforce check constraints. ";
msg += "Constraint on table " + tableName + " will be ignored.";
m_compiler.addWarn(msg);
return;
} else if (type == ConstraintType.FOREIGN_KEY) {
String msg = "VoltDB does not enforce foreign key references and constraints. ";
msg += "Constraint on table " + tableName + " will be ignored.";
m_compiler.addWarn(msg);
return;
} else if (type == ConstraintType.MAIN) {
// should never see these
assert (false);
} else if (type == ConstraintType.NOT_NULL) {
// these get handled by table metadata inspection
return;
} else if (type != ConstraintType.PRIMARY_KEY && type != ConstraintType.UNIQUE) {
throw m_compiler.new VoltCompilerException("Invalid constraint type '" + typeName + "'");
}
// else, create the unique index below
// primary key code is in other places as well
// The constraint is backed by an index, therefore we need to create it
// TODO: We need to be able to use indexes for foreign keys. I am purposely
// leaving those out right now because HSQLDB just makes too many of them.
Constraint catalog_const = table.getConstraints().add(name);
String indexName = node.attributes.get("index");
assert (indexName != null);
// handle replacements from duplicate index pruning
if (indexReplacementMap.containsKey(indexName)) {
indexName = indexReplacementMap.get(indexName);
}
Index catalog_index = indexMap.get(indexName);
// Attach the index to the catalog constraint (catalog_const).
if (catalog_index != null) {
catalog_const.setIndex(catalog_index);
// This may be redundant.
catalog_index.setUnique(true);
boolean assumeUnique = Boolean.parseBoolean(node.attributes.get("assumeunique"));
catalog_index.setAssumeunique(assumeUnique);
}
catalog_const.setType(type.getValue());
}
Aggregations