Search in sources :

Example 21 with VoltXMLElement

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());
}
Also used : VoltXMLDiff(org.hsqldb_voltpatches.VoltXMLElement.VoltXMLDiff) VoltXMLElement(org.hsqldb_voltpatches.VoltXMLElement)

Example 22 with VoltXMLElement

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);
}
Also used : TupleValueExpression(org.voltdb.expressions.TupleValueExpression) ArrayList(java.util.ArrayList) JSONException(org.json_voltpatches.JSONException) Index(org.voltdb.catalog.Index) VoltXMLElement(org.hsqldb_voltpatches.VoltXMLElement) AbstractParsedStmt(org.voltdb.planner.AbstractParsedStmt) Constraint(org.voltdb.catalog.Constraint) UnsafeOperatorsForDDL(org.voltdb.expressions.AbstractExpression.UnsafeOperatorsForDDL) AbstractExpression(org.voltdb.expressions.AbstractExpression) Column(org.voltdb.catalog.Column) VoltType(org.voltdb.VoltType) ParsedSelectStmt(org.voltdb.planner.ParsedSelectStmt) ColumnRef(org.voltdb.catalog.ColumnRef) VoltCompilerException(org.voltdb.compiler.VoltCompiler.VoltCompilerException)

Example 23 with VoltXMLElement

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);
}
Also used : HSQLParseException(org.hsqldb_voltpatches.HSQLInterface.HSQLParseException) HSQLInterface(org.hsqldb_voltpatches.HSQLInterface) VoltXMLElement(org.hsqldb_voltpatches.VoltXMLElement) VoltCompilerException(org.voltdb.compiler.VoltCompiler.VoltCompilerException)

Example 24 with VoltXMLElement

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;
}
Also used : AbstractExpression(org.voltdb.expressions.AbstractExpression) VoltXMLElement(org.hsqldb_voltpatches.VoltXMLElement) VoltCompilerException(org.voltdb.compiler.VoltCompiler.VoltCompilerException)

Example 25 with VoltXMLElement

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);
    }
}
Also used : DRTable(org.voltdb.compiler.statements.DRTable) Table(org.voltdb.catalog.Table) ReplicateTable(org.voltdb.compiler.statements.ReplicateTable) TableAnnotation(org.voltdb.compilereport.TableAnnotation) HashMap(java.util.HashMap) Index(org.voltdb.catalog.Index) VoltXMLElement(org.hsqldb_voltpatches.VoltXMLElement) TreeMap(java.util.TreeMap) Constraint(org.voltdb.catalog.Constraint) Column(org.voltdb.catalog.Column) VoltType(org.voltdb.VoltType) VoltCompilerException(org.voltdb.compiler.VoltCompiler.VoltCompilerException)

Aggregations

VoltXMLElement (org.hsqldb_voltpatches.VoltXMLElement)62 AbstractExpression (org.voltdb.expressions.AbstractExpression)14 Constraint (org.voltdb.catalog.Constraint)13 VoltCompilerException (org.voltdb.compiler.VoltCompiler.VoltCompilerException)10 ArrayList (java.util.ArrayList)9 VoltType (org.voltdb.VoltType)7 TupleValueExpression (org.voltdb.expressions.TupleValueExpression)6 HSQLInterface (org.hsqldb_voltpatches.HSQLInterface)5 VoltXMLDiff (org.hsqldb_voltpatches.VoltXMLElement.VoltXMLDiff)5 Column (org.voltdb.catalog.Column)5 Table (org.voltdb.catalog.Table)5 Matcher (java.util.regex.Matcher)4 HashMap (java.util.HashMap)3 HashSet (java.util.HashSet)3 HSQLParseException (org.hsqldb_voltpatches.HSQLInterface.HSQLParseException)3 JSONException (org.json_voltpatches.JSONException)3 Index (org.voltdb.catalog.Index)3 Statement (org.voltdb.catalog.Statement)3 StmtSubqueryScan (org.voltdb.planner.parseinfo.StmtSubqueryScan)3 ExpressionType (org.voltdb.types.ExpressionType)3