Search in sources :

Example 61 with ColumnMetadata

use of org.apache.cassandra.schema.ColumnMetadata in project cassandra by apache.

the class SASIIndex method validateOptions.

public static Map<String, String> validateOptions(Map<String, String> options, TableMetadata metadata) {
    if (!(metadata.partitioner instanceof Murmur3Partitioner))
        throw new ConfigurationException("SASI only supports Murmur3Partitioner.");
    String targetColumn = options.get("target");
    if (targetColumn == null)
        throw new ConfigurationException("unknown target column");
    Pair<ColumnMetadata, IndexTarget.Type> target = TargetParser.parse(metadata, targetColumn);
    if (target == null)
        throw new ConfigurationException("failed to retrieve target column for: " + targetColumn);
    if (target.left.isComplex())
        throw new ConfigurationException("complex columns are not yet supported by SASI");
    IndexMode.validateAnalyzer(options);
    IndexMode mode = IndexMode.getMode(target.left, options);
    if (mode.mode == Mode.SPARSE) {
        if (mode.isLiteral)
            throw new ConfigurationException("SPARSE mode is only supported on non-literal columns.");
        if (mode.isAnalyzed)
            throw new ConfigurationException("SPARSE mode doesn't support analyzers.");
    }
    return Collections.emptyMap();
}
Also used : ColumnMetadata(org.apache.cassandra.schema.ColumnMetadata) AbstractType(org.apache.cassandra.db.marshal.AbstractType) OperationType(org.apache.cassandra.db.compaction.OperationType) ConfigurationException(org.apache.cassandra.exceptions.ConfigurationException) IndexMode(org.apache.cassandra.index.sasi.conf.IndexMode) Murmur3Partitioner(org.apache.cassandra.dht.Murmur3Partitioner)

Example 62 with ColumnMetadata

use of org.apache.cassandra.schema.ColumnMetadata in project cassandra by apache.

the class SASIIndexBuilder method build.

public void build() {
    AbstractType<?> keyValidator = cfs.metadata().partitionKeyType;
    for (Map.Entry<SSTableReader, Map<ColumnMetadata, ColumnIndex>> e : sstables.entrySet()) {
        SSTableReader sstable = e.getKey();
        Map<ColumnMetadata, ColumnIndex> indexes = e.getValue();
        try (RandomAccessReader dataFile = sstable.openDataReader()) {
            PerSSTableIndexWriter indexWriter = SASIIndex.newWriter(keyValidator, sstable.descriptor, indexes, OperationType.COMPACTION);
            long previousKeyPosition = 0;
            try (KeyIterator keys = new KeyIterator(sstable.descriptor, cfs.metadata())) {
                while (keys.hasNext()) {
                    if (isStopRequested())
                        throw new CompactionInterruptedException(getCompactionInfo());
                    final DecoratedKey key = keys.next();
                    final long keyPosition = keys.getKeyPosition();
                    indexWriter.startPartition(key, keyPosition);
                    try {
                        RowIndexEntry indexEntry = sstable.getPosition(key, SSTableReader.Operator.EQ);
                        dataFile.seek(indexEntry.position);
                        // key
                        ByteBufferUtil.readWithShortLength(dataFile);
                        try (SSTableIdentityIterator partition = SSTableIdentityIterator.create(sstable, dataFile, key)) {
                            // if the row has statics attached, it has to be indexed separately
                            if (cfs.metadata().hasStaticColumns())
                                indexWriter.nextUnfilteredCluster(partition.staticRow());
                            while (partition.hasNext()) indexWriter.nextUnfilteredCluster(partition.next());
                        }
                    } catch (IOException ex) {
                        throw new FSReadError(ex, sstable.getFilename());
                    }
                    bytesProcessed += keyPosition - previousKeyPosition;
                    previousKeyPosition = keyPosition;
                }
                completeSSTable(indexWriter, sstable, indexes.values());
            }
        }
    }
}
Also used : ColumnMetadata(org.apache.cassandra.schema.ColumnMetadata) SSTableIdentityIterator(org.apache.cassandra.io.sstable.SSTableIdentityIterator) KeyIterator(org.apache.cassandra.io.sstable.KeyIterator) CompactionInterruptedException(org.apache.cassandra.db.compaction.CompactionInterruptedException) DecoratedKey(org.apache.cassandra.db.DecoratedKey) IOException(java.io.IOException) RowIndexEntry(org.apache.cassandra.db.RowIndexEntry) PerSSTableIndexWriter(org.apache.cassandra.index.sasi.disk.PerSSTableIndexWriter) SSTableReader(org.apache.cassandra.io.sstable.format.SSTableReader) ColumnIndex(org.apache.cassandra.index.sasi.conf.ColumnIndex) RandomAccessReader(org.apache.cassandra.io.util.RandomAccessReader) FSReadError(org.apache.cassandra.io.FSReadError)

Example 63 with ColumnMetadata

use of org.apache.cassandra.schema.ColumnMetadata in project cassandra by apache.

the class Operation method analyzeGroup.

@VisibleForTesting
protected static ListMultimap<ColumnMetadata, Expression> analyzeGroup(QueryController controller, OperationType op, List<RowFilter.Expression> expressions) {
    ListMultimap<ColumnMetadata, Expression> analyzed = ArrayListMultimap.create();
    // sort all of the expressions in the operation by name and priority of the logical operator
    // this gives us an efficient way to handle inequality and combining into ranges without extra processing
    // and converting expressions from one type to another.
    Collections.sort(expressions, (a, b) -> {
        int cmp = a.column().compareTo(b.column());
        return cmp == 0 ? -Integer.compare(getPriority(a.operator()), getPriority(b.operator())) : cmp;
    });
    for (final RowFilter.Expression e : expressions) {
        ColumnIndex columnIndex = controller.getIndex(e);
        List<Expression> perColumn = analyzed.get(e.column());
        if (columnIndex == null)
            columnIndex = new ColumnIndex(controller.getKeyValidator(), e.column(), null);
        AbstractAnalyzer analyzer = columnIndex.getAnalyzer();
        analyzer.reset(e.getIndexValue());
        // EQ/LIKE_*/NOT_EQ can have multiple expressions e.g. text = "Hello World",
        // becomes text = "Hello" OR text = "World" because "space" is always interpreted as a split point (by analyzer),
        // NOT_EQ is made an independent expression only in case of pre-existing multiple EQ expressions, or
        // if there is no EQ operations and NOT_EQ is met or a single NOT_EQ expression present,
        // in such case we know exactly that there would be no more EQ/RANGE expressions for given column
        // since NOT_EQ has the lowest priority.
        boolean isMultiExpression = false;
        switch(e.operator()) {
            case EQ:
                isMultiExpression = false;
                break;
            case LIKE_PREFIX:
            case LIKE_SUFFIX:
            case LIKE_CONTAINS:
            case LIKE_MATCHES:
                isMultiExpression = true;
                break;
            case NEQ:
                isMultiExpression = (perColumn.size() == 0 || perColumn.size() > 1 || (perColumn.size() == 1 && perColumn.get(0).getOp() == Op.NOT_EQ));
                break;
        }
        if (isMultiExpression) {
            while (analyzer.hasNext()) {
                final ByteBuffer token = analyzer.next();
                perColumn.add(new Expression(controller, columnIndex).add(e.operator(), token));
            }
        } else // "range" or not-equals operator, combines both bounds together into the single expression,
        // iff operation of the group is AND, otherwise we are forced to create separate expressions,
        // not-equals is combined with the range iff operator is AND.
        {
            Expression range;
            if (perColumn.size() == 0 || op != OperationType.AND)
                perColumn.add((range = new Expression(controller, columnIndex)));
            else
                range = Iterables.getLast(perColumn);
            while (analyzer.hasNext()) range.add(e.operator(), analyzer.next());
        }
    }
    return analyzed;
}
Also used : ColumnMetadata(org.apache.cassandra.schema.ColumnMetadata) RowFilter(org.apache.cassandra.db.filter.RowFilter) ColumnIndex(org.apache.cassandra.index.sasi.conf.ColumnIndex) AbstractAnalyzer(org.apache.cassandra.index.sasi.analyzer.AbstractAnalyzer) ByteBuffer(java.nio.ByteBuffer) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Example 64 with ColumnMetadata

use of org.apache.cassandra.schema.ColumnMetadata in project cassandra by apache.

the class Operation method localSatisfiedBy.

/**
     * Check every expression in the analyzed list to figure out if the
     * columns in the give row match all of the based on the operation
     * set to the current operation node.
     *
     * The algorithm is as follows: for every given expression from analyzed
     * list get corresponding column from the Row:
     *   - apply {@link Expression#isSatisfiedBy(ByteBuffer)}
     *     method to figure out if it's satisfied;
     *   - apply logical operation between boolean accumulator and current boolean result;
     *   - if result == false and node's operation is AND return right away;
     *
     * After all of the expressions have been evaluated return resulting accumulator variable.
     *
     * Example:
     *
     * Operation = (op: AND, columns: [first_name = p, 5 < age < 7, last_name: y])
     * Row = (first_name: pavel, last_name: y, age: 6, timestamp: 15)
     *
     * #1 get "first_name" = p (expressions)
     *      - row-get "first_name"                      => "pavel"
     *      - compare "pavel" against "p"               => true (current)
     *      - set accumulator current                   => true (because this is expression #1)
     *
     * #2 get "last_name" = y (expressions)
     *      - row-get "last_name"                       => "y"
     *      - compare "y" against "y"                   => true (current)
     *      - set accumulator to accumulator & current  => true
     *
     * #3 get 5 < "age" < 7 (expressions)
     *      - row-get "age"                             => "6"
     *      - compare 5 < 6 < 7                         => true (current)
     *      - set accumulator to accumulator & current  => true
     *
     * #4 return accumulator => true (row satisfied all of the conditions)
     *
     * @param currentCluster The row cluster to check.
     * @param staticRow The static row associated with current cluster.
     * @param allowMissingColumns allow columns value to be null.
     * @return true if give Row satisfied all of the analyzed expressions,
     *         false otherwise.
     */
private boolean localSatisfiedBy(Unfiltered currentCluster, Row staticRow, boolean allowMissingColumns) {
    if (currentCluster == null || !currentCluster.isRow())
        return false;
    final int now = FBUtilities.nowInSeconds();
    boolean result = false;
    int idx = 0;
    for (ColumnMetadata column : expressions.keySet()) {
        if (column.kind == Kind.PARTITION_KEY)
            continue;
        ByteBuffer value = ColumnIndex.getValueOf(column, column.kind == Kind.STATIC ? staticRow : (Row) currentCluster, now);
        boolean isMissingColumn = value == null;
        if (!allowMissingColumns && isMissingColumn)
            throw new IllegalStateException("All indexed columns should be included into the column slice, missing: " + column);
        boolean isMatch = false;
        // If there is a column with multiple expressions that effectively means an OR
        // e.g. comment = 'x y z' could be split into 'comment' EQ 'x', 'comment' EQ 'y', 'comment' EQ 'z'
        // by analyzer, in situation like that we only need to check if at least one of expressions matches,
        // and there is no hit on the NOT_EQ (if any) which are always at the end of the filter list.
        // Loop always starts from the end of the list, which makes it possible to break after the last
        // NOT_EQ condition on first EQ/RANGE condition satisfied, instead of checking every
        // single expression in the column filter list.
        List<Expression> filters = expressions.get(column);
        for (int i = filters.size() - 1; i >= 0; i--) {
            Expression expression = filters.get(i);
            isMatch = !isMissingColumn && expression.isSatisfiedBy(value);
            if (expression.getOp() == Op.NOT_EQ) {
                // since this is NOT_EQ operation we have to
                // inverse match flag (to check against other expressions),
                // and break in case of negative inverse because that means
                // that it's a positive hit on the not-eq clause.
                isMatch = !isMatch;
                if (!isMatch)
                    break;
            } else // if it was a match on EQ/RANGE or column is missing
            if (isMatch || isMissingColumn)
                break;
        }
        if (idx++ == 0) {
            result = isMatch;
            continue;
        }
        result = op.apply(result, isMatch);
        // exit early because we already got a single false
        if (op == OperationType.AND && !result)
            return false;
    }
    return idx == 0 || result;
}
Also used : ColumnMetadata(org.apache.cassandra.schema.ColumnMetadata) Row(org.apache.cassandra.db.rows.Row) ByteBuffer(java.nio.ByteBuffer)

Example 65 with ColumnMetadata

use of org.apache.cassandra.schema.ColumnMetadata in project cassandra by apache.

the class CreateIndexStatement method validate.

public void validate(ClientState state) throws RequestValidationException {
    TableMetadata table = Schema.instance.validateTable(keyspace(), columnFamily());
    if (table.isCounter())
        throw new InvalidRequestException("Secondary indexes are not supported on counter tables");
    if (table.isView())
        throw new InvalidRequestException("Secondary indexes are not supported on materialized views");
    if (table.isCompactTable() && !table.isStaticCompactTable())
        throw new InvalidRequestException("Secondary indexes are not supported on COMPACT STORAGE tables that have clustering columns");
    List<IndexTarget> targets = new ArrayList<>(rawTargets.size());
    for (IndexTarget.Raw rawTarget : rawTargets) targets.add(rawTarget.prepare(table));
    if (targets.isEmpty() && !properties.isCustom)
        throw new InvalidRequestException("Only CUSTOM indexes can be created without specifying a target column");
    if (targets.size() > 1)
        validateTargetsForMultiColumnIndex(targets);
    for (IndexTarget target : targets) {
        ColumnMetadata cd = table.getColumn(target.column);
        if (cd == null)
            throw new InvalidRequestException("No column definition found for column " + target.column);
        if (cd.type.referencesDuration()) {
            checkFalse(cd.type.isCollection(), "Secondary indexes are not supported on collections containing durations");
            checkFalse(cd.type.isTuple(), "Secondary indexes are not supported on tuples containing durations");
            checkFalse(cd.type.isUDT(), "Secondary indexes are not supported on UDTs containing durations");
            throw invalidRequest("Secondary indexes are not supported on duration columns");
        }
        // TODO: we could lift that limitation
        if (table.isCompactTable() && cd.isPrimaryKeyColumn())
            throw new InvalidRequestException("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables");
        if (cd.kind == ColumnMetadata.Kind.PARTITION_KEY && table.partitionKeyColumns().size() == 1)
            throw new InvalidRequestException(String.format("Cannot create secondary index on partition key column %s", target.column));
        boolean isMap = cd.type instanceof MapType;
        boolean isFrozenCollection = cd.type.isCollection() && !cd.type.isMultiCell();
        if (isFrozenCollection) {
            validateForFrozenCollection(target);
        } else {
            validateNotFullIndex(target);
            validateIsSimpleIndexIfTargetColumnNotCollection(cd, target);
            validateTargetColumnIsMapIfIndexInvolvesKeys(isMap, target);
        }
    }
    if (!Strings.isNullOrEmpty(indexName)) {
        if (Schema.instance.getKeyspaceMetadata(keyspace()).existingIndexNames(null).contains(indexName)) {
            if (ifNotExists)
                return;
            else
                throw new InvalidRequestException(String.format("Index %s already exists", indexName));
        }
    }
    properties.validate();
}
Also used : TableMetadata(org.apache.cassandra.schema.TableMetadata) ColumnMetadata(org.apache.cassandra.schema.ColumnMetadata) InvalidRequestException(org.apache.cassandra.exceptions.InvalidRequestException) MapType(org.apache.cassandra.db.marshal.MapType)

Aggregations

ColumnMetadata (org.apache.cassandra.schema.ColumnMetadata)123 Test (org.junit.Test)35 ByteBuffer (java.nio.ByteBuffer)23 Row (org.apache.cassandra.db.rows.Row)17 TableMetadata (org.apache.cassandra.schema.TableMetadata)16 ColumnIdentifier (org.apache.cassandra.cql3.ColumnIdentifier)15 AbstractType (org.apache.cassandra.db.marshal.AbstractType)8 ConfigurationException (org.apache.cassandra.exceptions.ConfigurationException)5 java.util (java.util)4 IndexTarget (org.apache.cassandra.cql3.statements.IndexTarget)4 CollectionType (org.apache.cassandra.db.marshal.CollectionType)4 IndexMetadata (org.apache.cassandra.schema.IndexMetadata)4 IOException (java.io.IOException)3 Collectors (java.util.stream.Collectors)3 ColumnFamilyStore (org.apache.cassandra.db.ColumnFamilyStore)3 ColumnFilter (org.apache.cassandra.db.filter.ColumnFilter)3 ImmutableBTreePartition (org.apache.cassandra.db.partitions.ImmutableBTreePartition)3 Cell (org.apache.cassandra.db.rows.Cell)3 RowIterator (org.apache.cassandra.db.rows.RowIterator)3 InvalidRequestException (org.apache.cassandra.exceptions.InvalidRequestException)3