PartitionDefinition.java

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 org.apache.doris.nereids.trees.plans.commands.info;

import org.apache.doris.analysis.AllPartitionDesc;
import org.apache.doris.analysis.PartitionValue;
import org.apache.doris.catalog.DataProperty;
import org.apache.doris.catalog.ReplicaAllocation;
import org.apache.doris.common.util.PropertyAnalyzer;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
import org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.thrift.TTabletType;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;

import java.util.List;
import java.util.Map;

/**
 * abstract class for partition definition
 */
public abstract class PartitionDefinition {

    protected boolean ifNotExists;
    protected String partitionName;
    protected Map<String, String> properties;

    protected List<DataType> partitionTypes;
    protected DataProperty partitionDataProperty =
            new DataProperty(DataProperty.DEFAULT_STORAGE_MEDIUM);
    protected ReplicaAllocation replicaAllocation = ReplicaAllocation.DEFAULT_ALLOCATION;
    protected boolean isInMemory = false;
    protected TTabletType tabletType = TTabletType.TABLET_TYPE_DISK;
    protected Long versionInfo;
    protected String storagePolicy = "";
    protected boolean isMutable;

    public PartitionDefinition(boolean ifNotExists, String partName) {
        this.ifNotExists = ifNotExists;
        this.partitionName = partName;
        this.properties = Maps.newHashMap();
    }

    public PartitionDefinition withProperties(Map<String, String> properties) {
        this.properties = properties;
        return this;
    }

    public abstract AllPartitionDesc translateToCatalogStyle();

    /**
     * Validate the properties.
     * Derived class can override this method to do more validation.
     */
    public void validate(Map<String, String> otherProperties) {
        try {
            if (PropertyAnalyzer.analyzeUniqueKeyMergeOnWrite(otherProperties)) {
                String storagePolicy = PropertyAnalyzer.analyzeStoragePolicy(properties);
                if (!storagePolicy.isEmpty()) {
                    throw new AnalysisException(
                            "Can not create UNIQUE KEY table that enables Merge-On-write"
                                    + " with storage policy(" + storagePolicy + ")");
                }
            }
            boolean hasStoragePolicy = false;
            if (properties != null) {
                hasStoragePolicy = properties.keySet().stream().anyMatch(iter -> {
                    boolean equal = iter
                            .compareToIgnoreCase(PropertyAnalyzer.PROPERTIES_STORAGE_POLICY) == 0;
                    // when find has storage policy properties, here will set it in partition
                    if (equal) {
                        storagePolicy = properties.get(iter);
                    }
                    return equal;
                });
            }

            Map<String, String> mergedMap = Maps.newHashMap();
            // Should putAll `otherProperties` before `this.properties`,
            // because the priority of partition is higher than table
            if (otherProperties != null) {
                mergedMap.putAll(otherProperties);
            }
            if (this.properties != null) {
                mergedMap.putAll(this.properties);
            }
            this.properties = mergedMap;

            // analyze data property
            partitionDataProperty = PropertyAnalyzer.analyzeDataProperty(properties,
                    new DataProperty(DataProperty.DEFAULT_STORAGE_MEDIUM));
            Preconditions.checkNotNull(partitionDataProperty);
            replicaAllocation = PropertyAnalyzer.analyzeReplicaAllocation(properties, "");
            if (replicaAllocation.isNotSet()) {
                replicaAllocation = ReplicaAllocation.DEFAULT_ALLOCATION;
            }
            // analyze version info
            versionInfo = PropertyAnalyzer.analyzeVersionInfo(properties);

            // analyze in memory
            isInMemory = PropertyAnalyzer.analyzeBooleanProp(properties,
                    PropertyAnalyzer.PROPERTIES_INMEMORY, false);
            if (isInMemory == true) {
                throw new AnalysisException("Not support set 'in_memory'='true' now!");
            }

            // analyze is mutable
            isMutable = PropertyAnalyzer.analyzeBooleanProp(properties,
                    PropertyAnalyzer.PROPERTIES_MUTABLE, true);

            tabletType = PropertyAnalyzer.analyzeTabletType(properties);

            if (otherProperties == null) {
                // check unknown properties
                if (properties != null && !properties.isEmpty()) {
                    if (!hasStoragePolicy) {
                        Joiner.MapJoiner mapJoiner = Joiner.on(", ").withKeyValueSeparator(" = ");
                        throw new AnalysisException(
                                "Unknown properties: " + mapJoiner.join(properties));
                    }
                }
            }
        } catch (Exception e) {
            throw new AnalysisException(e.getMessage(), e.getCause());
        }
    }

    /**
     * get partition name
     */
    public String getPartitionName() {
        throw new UnsupportedOperationException("Should not get partition name from step partition");
    }

    public void setPartitionTypes(List<DataType> partitionTypes) {
        this.partitionTypes = partitionTypes;
    }

    /**
     * translate partition value.
     */
    protected PartitionValue toLegacyPartitionValueStmt(Expression e) {
        if (e.isLiteral()) {
            return new PartitionValue(((Literal) e).getStringValue(), e.isNullLiteral());
        } else if (e instanceof MaxValue) {
            return PartitionValue.MAX_VALUE;
        }
        throw new AnalysisException("Unsupported partition value");
    }

    /**
     * partition maxvalue
     */
    public static class MaxValue extends Expression implements LeafExpression {
        public static MaxValue INSTANCE = new MaxValue();

        @Override
        public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
            throw new UnsupportedOperationException("Unsupported for MaxValue");
        }

        @Override
        public boolean nullable() {
            return false;
        }

        @Override
        protected Expression uncheckedCastTo(DataType targetType) throws AnalysisException {
            return this;
        }
    }
}