All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.apple.foundationdb.record.metadata.expressions.KeyWithValueExpression Maven / Gradle / Ivy

There is a newer version: 2.8.110.0
Show newest version
/*
 * KeyWithValueExpression.java
 *
 * This source file is part of the FoundationDB open source project
 *
 * Copyright 2015-2018 Apple Inc. and the FoundationDB project authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.apple.foundationdb.record.metadata.expressions;

import com.apple.foundationdb.annotation.API;
import com.apple.foundationdb.record.RecordMetaDataProto;
import com.apple.foundationdb.record.metadata.Key;
import com.apple.foundationdb.record.provider.foundationdb.FDBRecord;
import com.google.protobuf.Descriptors;
import com.google.protobuf.Message;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;

/**
 * A KeyWithValue expression is a top level expression that takes as input an expression that
 * produces more than one column, and indicates that all columns before the specified splitPoint
 * should be used in the key of a covering index and all of the values starting at the splitPoint
 * are to be used as the value of a covering index.
 */
@API(API.Status.MAINTAINED)
public class KeyWithValueExpression extends BaseKeyExpression implements KeyExpressionWithChild {
    @Nonnull
    private final KeyExpression innerKey;
    @Nullable
    private final int splitPoint;

    @Nullable
    private List normalizedKeys; // Cached normalized keys
    @Nullable
    private KeyExpression keyExpression; // Cached key
    @Nullable
    private KeyExpression valueExpression; // Cached value

    public KeyWithValueExpression(@Nonnull KeyExpression innerKey, int splitPoint) {
        this.innerKey = innerKey;
        this.splitPoint = splitPoint;
    }

    public KeyWithValueExpression(@Nonnull RecordMetaDataProto.KeyWithValue proto) throws DeserializationException {
        this(KeyExpression.fromProto(proto.getInnerKey()), proto.getSplitPoint());
    }

    @Nonnull
    @Override
    public  List evaluateMessage(@Nullable FDBRecord record, @Nullable Message message) {
        return getInnerKey().evaluateMessage(record, message);
    }

    @Override
    public boolean createsDuplicates() {
        return getInnerKey().createsDuplicates();
    }

    @Override
    public List validate(@Nonnull Descriptors.Descriptor descriptor) {
        KeyExpression key = getInnerKey();
        if (key.getColumnSize() < splitPoint) {
            throw new InvalidExpressionException("Child expression of covering expression returns too few columns")
                    .addLogInfo(
                            "split_point", splitPoint,
                            "child_columns", key.getColumnSize());
        }
        return key.validate(descriptor);
    }

    @Override
    public int getColumnSize() {
        // Because this expression is purely used to feed values to an index, the column size is really
        // the size of the values in the key portion of the index.
        return getSplitPoint();
    }

    @Nonnull
    @Override
    protected KeyExpression getSubKeyImpl(int start, int end) {
        return getInnerKey().getSubKey(start, end);
    }

    @Nonnull
    public KeyExpression getKeyExpression() {
        if (keyExpression == null) {
            keyExpression = getInnerKey().getSubKey(0, splitPoint);
        }
        return keyExpression;
    }

    @Nonnull
    public KeyExpression getValueExpression() {
        if (valueExpression == null) {
            List allKeys = normalizeKeyForPositions();
            if (splitPoint == allKeys.size()) {
                valueExpression = EmptyKeyExpression.EMPTY;
            } else {
                valueExpression = new ThenKeyExpression(allKeys, splitPoint, allKeys.size());
            }
        }
        return valueExpression;
    }

    public int getChildColumnSize() {
        return getInnerKey().getColumnSize();
    }

    @Nonnull
    @Override
    public RecordMetaDataProto.KeyWithValue toProto() throws SerializationException {
        return RecordMetaDataProto.KeyWithValue.newBuilder()
                .setInnerKey(getInnerKey().toKeyExpression())
                .setSplitPoint(splitPoint)
                .build();
    }

    @Nonnull
    @Override
    public RecordMetaDataProto.KeyExpression toKeyExpression() {
        return RecordMetaDataProto.KeyExpression.newBuilder().setKeyWithValue(toProto()).build();
    }

    @Nonnull
    @Override
    public List normalizeKeyForPositions() {
        if (normalizedKeys == null) {
            normalizedKeys = getInnerKey().normalizeKeyForPositions();
        }
        return normalizedKeys;
    }

    @Override
    public int versionColumns() {
        return getInnerKey().versionColumns();
    }

    @Override
    public boolean hasRecordTypeKey() {
        return getInnerKey().hasRecordTypeKey();
    }

    public int getSplitPoint() {
        return splitPoint;
    }

    @Nonnull
    private KeyExpression getInnerKey() {
        return innerKey;
    }

    @Nonnull
    @Override
    public KeyExpression getChild() {
        return getKeyExpression();
    }

    /**
     * Given a Key.Evaluated returned from the evaluation of this expression, splits out
     * the portion of that expression that should be used to represent the key of a covering index.
     * @param wholeKey the whole key with both key and value
     * @return the key portion of the given key
     */
    @Nonnull
    public Key.Evaluated getKey(@Nonnull Key.Evaluated wholeKey) {
        return wholeKey.subKey(0, splitPoint);
    }

    /**
     * Given a Key.Evaluated returned from the evaluation of this expression, splits out
     * the portion of that expression that should be used to represent the value of a covering index.
     * @param wholeKey the whole key with both key and value
     * @return the value portion of the given key
     */
    @Nonnull
    public Key.Evaluated getValue(@Nonnull Key.Evaluated wholeKey) {
        return wholeKey.subKey(splitPoint, wholeKey.size());
    }

    @Override
    public String toString() {
        return "covering(" + getInnerKey().toString() + " split " + splitPoint + ")";
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }

        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        KeyWithValueExpression that = (KeyWithValueExpression)o;
        return this.getInnerKey().equals(that.getInnerKey()) && (this.splitPoint == that.splitPoint);
    }

    @Override
    public int hashCode() {
        int result = getInnerKey().hashCode();
        result = 31 * result + splitPoint;
        return result;
    }

    @Override
    public int planHash() {
        return getInnerKey().planHash() + splitPoint;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy