use of org.hsqldb_voltpatches.VoltXMLElement in project voltdb by VoltDB.
the class TestVoltXMLElement method testNoDiff.
public void testNoDiff() {
VoltXMLElement first = makeNamedElement("element", "element");
VoltXMLElement changedChild1 = makeNamedElement("child", "changedchild1");
first.children.add(changedChild1);
changedChild1.attributes.put("deleteme", "noreally");
VoltXMLElement changedChild2 = makeNamedElement("child", "changedchild2");
first.children.add(changedChild2);
VoltXMLElement changedGrandchild = makeNamedElement("child", "changedgrandchild");
changedChild2.children.add(changedGrandchild);
changedGrandchild.children.add(makeNamedElement("child", "doomeddescendent"));
first.attributes.put("deleted", "doesntmatter");
first.attributes.put("remains", "doesntmatter");
first.attributes.put("changes", "oldvalue");
first.children.add(makeNamedElement("child", "deletedchild"));
first.children.add(makeNamedElement("child", "unchangedchild"));
VoltXMLElement second = first.duplicate();
VoltXMLDiff diff = VoltXMLElement.computeDiff(first, second);
System.out.println("diff: " + diff.toString());
VoltXMLElement third = first.duplicate();
third.applyDiff(diff);
System.out.println(first.toMinString());
System.out.println(second.toMinString());
System.out.println(third.toMinString());
assertEquals(second.toMinString(), third.toMinString());
}
use of org.hsqldb_voltpatches.VoltXMLElement 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.hsqldb_voltpatches.VoltXMLElement in project voltdb by VoltDB.
the class DDLCompiler method validateTupleLimitDeleteStmt.
/** Makes sure that the DELETE statement on a LIMIT PARTITION ROWS EXECUTE (DELETE ...)
* - Contains no parse errors
* - Is actually a DELETE statement
* - Targets the table being constrained
* Throws VoltCompilerException if any of these does not hold
* @param catStmt The catalog statement whose sql text field is the DELETE to be validated
**/
private void validateTupleLimitDeleteStmt(Statement catStmt) throws VoltCompilerException {
String tableName = catStmt.getParent().getTypeName();
String msgPrefix = "Error: Table " + tableName + " has invalid DELETE statement for LIMIT PARTITION ROWS constraint: ";
VoltXMLElement deleteXml = null;
try {
// We parse the statement here and cache the XML below if the statement passes
// validation.
deleteXml = m_hsql.getXMLCompiledStatement(catStmt.getSqltext());
} catch (HSQLInterface.HSQLParseException e) {
throw m_compiler.new VoltCompilerException(msgPrefix + "parse error: " + e.getMessage());
}
if (!deleteXml.name.equals("delete")) {
// Could in theory allow TRUNCATE TABLE here too.
throw m_compiler.new VoltCompilerException(msgPrefix + "not a DELETE statement");
}
String deleteTarget = deleteXml.attributes.get("table");
if (!deleteTarget.equals(tableName)) {
throw m_compiler.new VoltCompilerException(msgPrefix + "target of DELETE must be " + tableName);
}
m_limitDeleteStmtToXml.put(catStmt, deleteXml);
}
use of org.hsqldb_voltpatches.VoltXMLElement 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.hsqldb_voltpatches.VoltXMLElement in project voltdb by VoltDB.
the class DDLCompiler method addTableToCatalog.
private void addTableToCatalog(Database db, VoltXMLElement node, boolean isXDCR) throws VoltCompilerException {
assert node.name.equals("table");
// Construct table-specific maps
HashMap<String, Column> columnMap = new HashMap<>();
HashMap<String, Index> indexMap = new HashMap<>();
final String name = node.attributes.get("name");
// create a table node in the catalog
final Table table = db.getTables().add(name);
// set max value before return for view table
table.setTuplelimit(Integer.MAX_VALUE);
// add the original DDL to the table (or null if it's not there)
TableAnnotation annotation = new TableAnnotation();
table.setAnnotation(annotation);
// handle the case where this is a materialized view
final String query = node.attributes.get("query");
if (query != null) {
assert (query.length() > 0);
m_matViewMap.put(table, query);
}
final boolean isStream = (node.attributes.get("stream") != null);
final String streamTarget = node.attributes.get("export");
final String streamPartitionColumn = node.attributes.get("partitioncolumn");
// all tables start replicated
// if a partition is found in the project file later,
// then this is reversed
table.setIsreplicated(true);
// map of index replacements for later constraint fixup
final Map<String, String> indexReplacementMap = new TreeMap<>();
// Need the columnTypes sorted by column index.
SortedMap<Integer, VoltType> columnTypes = new TreeMap<>();
for (VoltXMLElement subNode : node.children) {
if (subNode.name.equals("columns")) {
int colIndex = 0;
for (VoltXMLElement columnNode : subNode.children) {
if (columnNode.name.equals("column")) {
addColumnToCatalog(table, columnNode, columnTypes, columnMap, m_compiler);
colIndex++;
}
}
// limit the total number of columns in a table
if (colIndex > MAX_COLUMNS) {
String msg = "Table " + name + " has " + colIndex + " columns (max is " + MAX_COLUMNS + ")";
throw m_compiler.new VoltCompilerException(msg);
}
}
if (subNode.name.equals("indexes")) {
// that refer to them.
for (VoltXMLElement indexNode : subNode.children) {
if (indexNode.name.equals("index") == false)
continue;
String indexName = indexNode.attributes.get("name");
if (indexName.startsWith(HSQLInterface.AUTO_GEN_IDX_PREFIX) == false) {
addIndexToCatalog(db, table, indexNode, indexReplacementMap, indexMap, columnMap, m_compiler);
}
}
for (VoltXMLElement indexNode : subNode.children) {
if (indexNode.name.equals("index") == false)
continue;
String indexName = indexNode.attributes.get("name");
if (indexName.startsWith(HSQLInterface.AUTO_GEN_IDX_PREFIX) == true) {
addIndexToCatalog(db, table, indexNode, indexReplacementMap, indexMap, columnMap, m_compiler);
}
}
}
if (subNode.name.equals("constraints")) {
for (VoltXMLElement constraintNode : subNode.children) {
if (constraintNode.name.equals("constraint")) {
addConstraintToCatalog(table, constraintNode, indexReplacementMap, indexMap);
}
}
}
}
// Warn user if DR table don't have any unique index.
if (isXDCR && node.attributes.get("drTable") != null && node.attributes.get("drTable").equalsIgnoreCase("ENABLE")) {
boolean hasUniqueIndex = false;
for (Index index : table.getIndexes()) {
if (index.getUnique()) {
hasUniqueIndex = true;
break;
}
}
if (!hasUniqueIndex) {
String info = String.format("Table %s doesn't have any unique index, it will cause full table scans to update/delete DR record and may become slower as table grow.", table.getTypeName());
m_compiler.addWarn(info);
}
}
table.setSignature(CatalogUtil.getSignatureForTable(name, columnTypes));
/*
* Validate that each variable-length column is below the max value length,
* and that the maximum size for the row is below the max row length.
*/
int maxRowSize = 0;
for (Column c : columnMap.values()) {
VoltType t = VoltType.get((byte) c.getType());
if (t == VoltType.STRING && (!c.getInbytes())) {
if (c.getSize() * MAX_BYTES_PER_UTF8_CHARACTER > VoltType.MAX_VALUE_LENGTH) {
throw m_compiler.new VoltCompilerException("Column " + name + "." + c.getName() + " specifies a maximum size of " + c.getSize() + " characters" + " but the maximum supported size is " + VoltType.humanReadableSize(VoltType.MAX_VALUE_LENGTH / MAX_BYTES_PER_UTF8_CHARACTER) + " characters or " + VoltType.humanReadableSize(VoltType.MAX_VALUE_LENGTH) + " bytes");
}
maxRowSize += 4 + c.getSize() * MAX_BYTES_PER_UTF8_CHARACTER;
} else if (t.isVariableLength()) {
if (c.getSize() > VoltType.MAX_VALUE_LENGTH) {
throw m_compiler.new VoltCompilerException("Column " + name + "." + c.getName() + " specifies a maximum size of " + c.getSize() + " bytes" + " but the maximum supported size is " + VoltType.humanReadableSize(VoltType.MAX_VALUE_LENGTH));
}
maxRowSize += 4 + c.getSize();
} else {
maxRowSize += t.getLengthInBytesForFixedTypes();
}
}
if (maxRowSize > MAX_ROW_SIZE) {
throw m_compiler.new VoltCompilerException("Error: Table " + name + " has a maximum row size of " + maxRowSize + " but the maximum supported row size is " + MAX_ROW_SIZE);
}
// the DDL statement for the VIEW
if (query != null) {
annotation.ddl = query;
} else {
// Get the final DDL for the table rebuilt from the catalog object
// Don't need a real StringBuilder or export state to get the CREATE for a table
annotation.ddl = CatalogSchemaTools.toSchema(new StringBuilder(), table, query, isStream, streamPartitionColumn, streamTarget);
}
}
Aggregations