[fix](nereids)(create-table) fix bug that replication num is not set when create table with no property (#25651)

When executing create partitioned table with Nereids, and replication_num property is not set,
the replication number will be 0, so the tablet will has no replica.
This commit is contained in:
Mingyu Chen
2023-10-21 23:15:08 +08:00
committed by GitHub
parent 387a9c7448
commit 13780e4827
12 changed files with 240 additions and 171 deletions

View File

@ -20,13 +20,11 @@ package org.apache.doris.analysis;
import org.apache.doris.analysis.IndexDef.IndexType;
import org.apache.doris.catalog.AggregateType;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.DatabaseIf;
import org.apache.doris.catalog.DistributionInfo;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.Index;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.PrimitiveType;
import org.apache.doris.catalog.ReplicaAllocation;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
@ -41,7 +39,6 @@ import org.apache.doris.common.util.ParseUtil;
import org.apache.doris.common.util.PrintableMap;
import org.apache.doris.common.util.PropertyAnalyzer;
import org.apache.doris.common.util.Util;
import org.apache.doris.datasource.CatalogIf;
import org.apache.doris.external.elasticsearch.EsUtil;
import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.qe.ConnectContext;
@ -51,7 +48,6 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -559,7 +555,8 @@ public class CreateTableStmt extends DdlStmt {
if (engineName.equals("olap")) {
// before analyzing partition, handle the replication allocation info
properties = rewriteReplicaAllocationProperties(properties);
properties = PropertyAnalyzer.rewriteReplicaAllocationProperties(
tableName.getCtl(), tableName.getDb(), properties);
// analyze partition
if (partitionDesc != null) {
if (partitionDesc instanceof ListPartitionDesc || partitionDesc instanceof RangePartitionDesc
@ -650,74 +647,6 @@ public class CreateTableStmt extends DdlStmt {
}
}
private Map<String, String> rewriteReplicaAllocationProperties(Map<String, String> properties)
throws AnalysisException {
if (Config.force_olap_table_replication_num <= 0) {
return rewriteReplicaAllocationPropertiesByDatabase(properties);
}
// if force_olap_table_replication_num is set, use this value to rewrite the replication_num or
// replication_allocation properties
Map<String, String> newProperties = properties;
if (newProperties == null) {
newProperties = Maps.newHashMap();
}
boolean rewrite = false;
if (newProperties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM)) {
newProperties.put(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM,
String.valueOf(Config.force_olap_table_replication_num));
rewrite = true;
}
if (newProperties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION)) {
newProperties.put(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION,
new ReplicaAllocation((short) Config.force_olap_table_replication_num).toCreateStmt());
rewrite = true;
}
if (!rewrite) {
newProperties.put(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM,
String.valueOf(Config.force_olap_table_replication_num));
}
return newProperties;
}
private Map<String, String> rewriteReplicaAllocationPropertiesByDatabase(Map<String, String> properties)
throws AnalysisException {
// if table contain `replication_allocation` or `replication_allocation`,not need rewrite by db
if (properties != null && (properties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION)
|| properties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM))) {
return properties;
}
CatalogIf catalog = Env.getCurrentEnv().getCatalogMgr().getCatalogNullable(tableName.getCtl());
if (catalog == null) {
return properties;
}
DatabaseIf db = catalog.getDbNullable(tableName.getDb());
if (db == null) {
return properties;
}
// if db not have properties,not need rewrite
if (db.getDbProperties() == null) {
return properties;
}
Map<String, String> dbProperties = db.getDbProperties().getProperties();
if (dbProperties == null) {
return properties;
}
if (properties == null) {
properties = Maps.newHashMap();
}
if (dbProperties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION) && StringUtils
.isNotEmpty(dbProperties.get(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION))) {
properties.put(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION,
dbProperties.get(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION));
}
if (dbProperties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM) && StringUtils
.isNotEmpty(dbProperties.get(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM))) {
properties.put(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM,
dbProperties.get(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM));
}
return properties;
}
private void analyzeEngineName() throws AnalysisException {
if (Strings.isNullOrEmpty(engineName)) {
engineName = "olap";

View File

@ -21,6 +21,7 @@ import org.apache.doris.analysis.DataSortInfo;
import org.apache.doris.analysis.DateLiteral;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.DataProperty;
import org.apache.doris.catalog.DatabaseIf;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.EsResource;
import org.apache.doris.catalog.KeysType;
@ -32,6 +33,7 @@ import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
import org.apache.doris.common.DdlException;
import org.apache.doris.datasource.CatalogIf;
import org.apache.doris.datasource.CatalogMgr;
import org.apache.doris.policy.Policy;
import org.apache.doris.policy.StoragePolicy;
@ -48,6 +50,7 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -1176,4 +1179,73 @@ public class PropertyAnalyzer {
}
}
}
public static Map<String, String> rewriteReplicaAllocationProperties(
String ctl, String db, Map<String, String> properties) {
if (Config.force_olap_table_replication_num <= 0) {
return rewriteReplicaAllocationPropertiesByDatabase(ctl, db, properties);
}
// if force_olap_table_replication_num is set, use this value to rewrite the replication_num or
// replication_allocation properties
Map<String, String> newProperties = properties;
if (newProperties == null) {
newProperties = Maps.newHashMap();
}
boolean rewrite = false;
if (newProperties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM)) {
newProperties.put(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM,
String.valueOf(Config.force_olap_table_replication_num));
rewrite = true;
}
if (newProperties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION)) {
newProperties.put(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION,
new ReplicaAllocation((short) Config.force_olap_table_replication_num).toCreateStmt());
rewrite = true;
}
if (!rewrite) {
newProperties.put(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM,
String.valueOf(Config.force_olap_table_replication_num));
}
return newProperties;
}
private static Map<String, String> rewriteReplicaAllocationPropertiesByDatabase(
String ctl, String database, Map<String, String> properties) {
// if table contain `replication_allocation` or `replication_allocation`,not need rewrite by db
if (properties != null && (properties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION)
|| properties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM))) {
return properties;
}
CatalogIf catalog = Env.getCurrentEnv().getCatalogMgr().getCatalogNullable(ctl);
if (catalog == null) {
return properties;
}
DatabaseIf db = catalog.getDbNullable(database);
if (db == null) {
return properties;
}
// if db not have properties,not need rewrite
if (db.getDbProperties() == null) {
return properties;
}
Map<String, String> dbProperties = db.getDbProperties().getProperties();
if (dbProperties == null) {
return properties;
}
if (properties == null) {
properties = Maps.newHashMap();
}
if (dbProperties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION) && StringUtils
.isNotEmpty(dbProperties.get(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION))) {
properties.put(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION,
dbProperties.get(PropertyAnalyzer.PROPERTIES_REPLICATION_ALLOCATION));
}
if (dbProperties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM) && StringUtils
.isNotEmpty(dbProperties.get(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM))) {
properties.put(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM,
dbProperties.get(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM));
}
return properties;
}
}

View File

@ -1865,8 +1865,9 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
@Override
public LogicalPlan visitCreateTable(CreateTableContext ctx) {
String ctlName = null;
String dbName = null;
String tableName;
String tableName = null;
List<String> nameParts = visitMultipartIdentifier(ctx.name);
// TODO: support catalog
if (nameParts.size() == 1) {
@ -1874,8 +1875,12 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
} else if (nameParts.size() == 2) {
dbName = nameParts.get(0);
tableName = nameParts.get(1);
} else if (nameParts.size() == 3) {
ctlName = nameParts.get(0);
dbName = nameParts.get(1);
tableName = nameParts.get(2);
} else {
throw new AnalysisException("nameParts in create table should be 1 or 2");
throw new AnalysisException("nameParts in create table should be [ctl.][db.]tbl");
}
KeysType keysType = null;
if (ctx.DUPLICATE() != null) {
@ -1906,6 +1911,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
}
return new CreateTableCommand(Optional.empty(), new CreateTableInfo(
ctx.EXISTS() != null,
ctlName,
dbName,
tableName,
visitColumnDefs(ctx.columnDefs()),
@ -1923,6 +1929,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
} else if (ctx.AS() != null) {
return new CreateTableCommand(Optional.of(visitQuery(ctx.query())), new CreateTableInfo(
ctx.EXISTS() != null,
ctlName,
dbName,
tableName,
ctx.ctasCols != null ? visitIdentifierList(ctx.ctasCols) : null,

View File

@ -30,17 +30,21 @@ import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.Index;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.PartitionType;
import org.apache.doris.catalog.Type;
import org.apache.doris.cluster.ClusterNamespace;
import org.apache.doris.common.Config;
import org.apache.doris.common.FeConstants;
import org.apache.doris.common.FeNameFormat;
import org.apache.doris.common.util.PropertyAnalyzer;
import org.apache.doris.datasource.InternalCatalog;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.util.Utils;
import org.apache.doris.qe.ConnectContext;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@ -57,6 +61,7 @@ import java.util.stream.Collectors;
*/
public class CreateTableInfo {
private final boolean ifNotExists;
private String ctlName;
private String dbName;
private final String tableName;
private List<ColumnDefinition> columns;
@ -77,11 +82,13 @@ public class CreateTableInfo {
/**
* constructor for create table
*/
public CreateTableInfo(boolean ifNotExists, String dbName, String tableName, List<ColumnDefinition> columns,
List<IndexDefinition> indexes, String engineName, KeysType keysType, List<String> keys, String comment,
public CreateTableInfo(boolean ifNotExists, String ctlName, String dbName, String tableName,
List<ColumnDefinition> columns, List<IndexDefinition> indexes, String engineName,
KeysType keysType, List<String> keys, String comment,
String partitionType, List<String> partitionColumns, List<PartitionDefinition> partitions,
DistributionDescriptor distribution, List<RollupDefinition> rollups, Map<String, String> properties) {
this.ifNotExists = ifNotExists;
this.ctlName = ctlName;
this.dbName = dbName;
this.tableName = tableName;
this.ctasColumns = null;
@ -102,11 +109,12 @@ public class CreateTableInfo {
/**
* constructor for create table as select
*/
public CreateTableInfo(boolean ifNotExists, String dbName, String tableName, List<String> cols,
public CreateTableInfo(boolean ifNotExists, String ctlName, String dbName, String tableName, List<String> cols,
String engineName, KeysType keysType, List<String> keys, String comment,
String partitionType, List<String> partitionColumns, List<PartitionDefinition> partitions,
DistributionDescriptor distribution, List<RollupDefinition> rollups, Map<String, String> properties) {
this.ifNotExists = ifNotExists;
this.ctlName = ctlName;
this.dbName = dbName;
this.tableName = tableName;
this.ctasColumns = cols;
@ -128,6 +136,10 @@ public class CreateTableInfo {
return ctasColumns;
}
public String getCtlName() {
return ctlName;
}
public String getDbName() {
return dbName;
}
@ -163,20 +175,30 @@ public class CreateTableInfo {
try {
FeNameFormat.checkTableName(tableName);
if (dbName != null) {
FeNameFormat.checkDbName(dbName);
}
} catch (Exception e) {
throw new AnalysisException(e.getMessage(), e);
}
// analyze catalog name
if (Strings.isNullOrEmpty(ctlName)) {
if (ctx.getCurrentCatalog() != null) {
ctlName = ctx.getCurrentCatalog().getName();
} else {
ctlName = InternalCatalog.INTERNAL_CATALOG_NAME;
}
}
// analyze table name
if (dbName == null) {
if (Strings.isNullOrEmpty(dbName)) {
dbName = ClusterNamespace.getFullName(ctx.getClusterName(), ctx.getDatabase());
} else {
dbName = ClusterNamespace.getFullName(ctx.getClusterName(), dbName);
}
Preconditions.checkState(!Strings.isNullOrEmpty(ctlName), "catalog name is null or empty");
Preconditions.checkState(!Strings.isNullOrEmpty(dbName), "database name is null or empty");
properties = PropertyAnalyzer.rewriteReplicaAllocationProperties(ctlName, dbName, properties);
boolean enableDuplicateWithoutKeysByDefault = false;
if (properties != null) {
try {
@ -367,14 +389,15 @@ public class CreateTableInfo {
* check partitions types.
*/
private boolean checkPartitionsTypes() {
if (partitionType.equalsIgnoreCase("RANGE")) {
if (partitionType.equalsIgnoreCase(PartitionType.RANGE.name())) {
if (partitions.stream().allMatch(p -> p instanceof StepPartition)) {
return true;
}
return partitions.stream().allMatch(p -> (p instanceof LessThanPartition)
|| (p instanceof FixedRangePartition));
}
return partitionType.equalsIgnoreCase("LIST") && partitions.stream().allMatch(p -> p instanceof InPartition);
return partitionType.equalsIgnoreCase(PartitionType.LIST.name())
&& partitions.stream().allMatch(p -> p instanceof InPartition);
}
private void validatePartitionColumn(ColumnDefinition column, ConnectContext ctx) {
@ -395,7 +418,7 @@ public class CreateTableInfo {
if (!ctx.getSessionVariable().isAllowPartitionColumnNullable() && column.isNullable()) {
throw new AnalysisException("The partition column must be NOT NULL");
}
if (partitionType.equalsIgnoreCase("LIST") && column.isNullable()) {
if (partitionType.equalsIgnoreCase(PartitionType.LIST.name()) && column.isNullable()) {
throw new AnalysisException("The list partition column must be NOT NULL");
}
}
@ -415,7 +438,7 @@ public class CreateTableInfo {
List<AllPartitionDesc> partitionDescs = partitions.stream()
.map(PartitionDefinition::translateToCatalogStyle).collect(Collectors.toList());
try {
if (partitionType.equals("RANGE")) {
if (partitionType.equals(PartitionType.RANGE.name())) {
partitionDesc = new RangePartitionDesc(partitionColumns, partitionDescs);
} else {
partitionDesc = new ListPartitionDesc(partitionColumns, partitionDescs);

View File

@ -20,9 +20,6 @@ package org.apache.doris.nereids.trees.plans.commands.info;
import org.apache.doris.analysis.PartitionKeyDesc;
import org.apache.doris.analysis.PartitionValue;
import org.apache.doris.analysis.SinglePartitionDesc;
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.types.DataType;
@ -39,7 +36,6 @@ public class FixedRangePartition extends PartitionDefinition {
private final String partitionName;
private List<Expression> lowerBounds;
private List<Expression> upperBounds;
private ReplicaAllocation replicaAllocation = ReplicaAllocation.DEFAULT_ALLOCATION;
public FixedRangePartition(String partitionName, List<Expression> lowerBounds, List<Expression> upperBounds) {
this.partitionName = partitionName;
@ -49,11 +45,7 @@ public class FixedRangePartition extends PartitionDefinition {
@Override
public void validate(Map<String, String> properties) {
try {
replicaAllocation = PropertyAnalyzer.analyzeReplicaAllocation(properties, "");
} catch (Exception e) {
throw new AnalysisException(e.getMessage(), e.getCause());
}
super.validate(properties);
final DataType type = partitionTypes.get(0);
lowerBounds = lowerBounds.stream().map(e -> e.castTo(type)).collect(Collectors.toList());
upperBounds = upperBounds.stream().map(e -> e.castTo(type)).collect(Collectors.toList());

View File

@ -21,8 +21,6 @@ import org.apache.doris.analysis.AllPartitionDesc;
import org.apache.doris.analysis.PartitionKeyDesc;
import org.apache.doris.analysis.PartitionValue;
import org.apache.doris.analysis.SinglePartitionDesc;
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;
@ -38,7 +36,6 @@ import java.util.stream.Collectors;
public class InPartition extends PartitionDefinition {
private final String partitionName;
private final List<List<Expression>> values;
private ReplicaAllocation replicaAllocation = ReplicaAllocation.DEFAULT_ALLOCATION;
public InPartition(String partitionName, List<List<Expression>> values) {
this.partitionName = partitionName;
@ -47,11 +44,7 @@ public class InPartition extends PartitionDefinition {
@Override
public void validate(Map<String, String> properties) {
try {
replicaAllocation = PropertyAnalyzer.analyzeReplicaAllocation(properties, "");
} catch (Exception e) {
throw new AnalysisException(e.getMessage(), e.getCause());
}
super.validate(properties);
if (values.stream().anyMatch(l -> l.stream().anyMatch(MaxValue.class::isInstance))) {
throw new AnalysisException("MAXVALUE cannot be used in 'in partition'");
}

View File

@ -20,9 +20,6 @@ package org.apache.doris.nereids.trees.plans.commands.info;
import org.apache.doris.analysis.PartitionKeyDesc;
import org.apache.doris.analysis.PartitionValue;
import org.apache.doris.analysis.SinglePartitionDesc;
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 com.google.common.collect.Maps;
@ -37,7 +34,6 @@ import java.util.stream.Collectors;
public class LessThanPartition extends PartitionDefinition {
private final String partitionName;
private final List<Expression> values;
private ReplicaAllocation replicaAllocation = ReplicaAllocation.DEFAULT_ALLOCATION;
public LessThanPartition(String partitionName, List<Expression> values) {
this.partitionName = partitionName;
@ -46,11 +42,7 @@ public class LessThanPartition extends PartitionDefinition {
@Override
public void validate(Map<String, String> properties) {
try {
replicaAllocation = PropertyAnalyzer.analyzeReplicaAllocation(properties, "");
} catch (Exception e) {
throw new AnalysisException(e.getMessage(), e.getCause());
}
super.validate(properties);
}
public String getPartitionName() {

View File

@ -19,6 +19,8 @@ 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.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;
@ -34,16 +36,30 @@ import java.util.Map;
*/
public abstract class PartitionDefinition {
protected List<DataType> partitionTypes;
protected Map<String, String> propreties;
protected Map<String, String> properties;
protected ReplicaAllocation replicaAllocation = ReplicaAllocation.DEFAULT_ALLOCATION;
public PartitionDefinition withProperties(Map<String, String> properties) {
this.propreties = properties;
this.properties = properties;
return this;
}
public abstract AllPartitionDesc translateToCatalogStyle();
public abstract void validate(Map<String, String> properties);
/**
* Validate the properties.
* Derived class can override this method to do more validation.
*/
public void validate(Map<String, String> properties) {
try {
replicaAllocation = PropertyAnalyzer.analyzeReplicaAllocation(properties, "");
if (replicaAllocation.isNotSet()) {
replicaAllocation = ReplicaAllocation.DEFAULT_ALLOCATION;
}
} catch (Exception e) {
throw new AnalysisException(e.getMessage(), e.getCause());
}
}
/**
* get partition name

View File

@ -20,8 +20,6 @@ package org.apache.doris.nereids.trees.plans.commands.info;
import org.apache.doris.analysis.MultiPartitionDesc;
import org.apache.doris.analysis.PartitionKeyDesc;
import org.apache.doris.analysis.PartitionValue;
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;
@ -39,7 +37,6 @@ public class StepPartition extends PartitionDefinition {
private final List<Expression> toExpression;
private final long unit;
private final String unitString;
private ReplicaAllocation replicaAllocation = ReplicaAllocation.DEFAULT_ALLOCATION;
public StepPartition(List<Expression> fromExpression, List<Expression> toExpression, long unit,
String unitString) {
@ -51,15 +48,11 @@ public class StepPartition extends PartitionDefinition {
@Override
public void validate(Map<String, String> properties) {
super.validate(properties);
if (fromExpression.stream().anyMatch(MaxValue.class::isInstance)
|| toExpression.stream().anyMatch(MaxValue.class::isInstance)) {
throw new AnalysisException("MAXVALUE cannot be used in step partition");
}
try {
replicaAllocation = PropertyAnalyzer.analyzeReplicaAllocation(properties, "");
} catch (Exception e) {
throw new AnalysisException(e.getMessage(), e.getCause());
}
}
/**