/* * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial agreement. */ package io.crate.analyze; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import io.crate.analyze.expressions.ExpressionToNumberVisitor; import io.crate.analyze.expressions.ExpressionToObjectVisitor; import io.crate.analyze.expressions.ExpressionToStringVisitor; import io.crate.data.Row; import io.crate.metadata.settings.CrateTableSettings; import io.crate.metadata.settings.SettingsApplier; import io.crate.metadata.settings.SettingsAppliers; import io.crate.metadata.table.ColumnPolicy; import io.crate.sql.tree.ArrayLiteral; import io.crate.sql.tree.Expression; import io.crate.sql.tree.GenericProperties; import org.elasticsearch.common.settings.Settings; import java.util.*; public final class TablePropertiesAnalyzer { private TablePropertiesAnalyzer() { } private static final ImmutableBiMap<String, String> CRATE_TO_ES_SETTINGS_MAP = ImmutableBiMap.<String, String>builder() .put(stripIndexPrefix(TableParameterInfo.NUMBER_OF_REPLICAS), TableParameterInfo.NUMBER_OF_REPLICAS) .put(stripIndexPrefix(TableParameterInfo.REFRESH_INTERVAL), TableParameterInfo.REFRESH_INTERVAL) .put(stripIndexPrefix(TableParameterInfo.READ_ONLY), TableParameterInfo.READ_ONLY) .put(stripIndexPrefix(TableParameterInfo.BLOCKS_READ), TableParameterInfo.BLOCKS_READ) .put(stripIndexPrefix(TableParameterInfo.BLOCKS_WRITE), TableParameterInfo.BLOCKS_WRITE) .put(stripIndexPrefix(TableParameterInfo.BLOCKS_METADATA), TableParameterInfo.BLOCKS_METADATA) .put(stripIndexPrefix(TableParameterInfo.FLUSH_THRESHOLD_SIZE), TableParameterInfo.FLUSH_THRESHOLD_SIZE) .put(stripIndexPrefix(TableParameterInfo.ROUTING_ALLOCATION_ENABLE), TableParameterInfo.ROUTING_ALLOCATION_ENABLE) .put(stripIndexPrefix(TableParameterInfo.TRANSLOG_SYNC_INTERVAL), TableParameterInfo.TRANSLOG_SYNC_INTERVAL) .put(stripIndexPrefix(TableParameterInfo.TOTAL_SHARDS_PER_NODE), TableParameterInfo.TOTAL_SHARDS_PER_NODE) .put(stripIndexPrefix(TableParameterInfo.RECOVERY_INITIAL_SHARDS), TableParameterInfo.RECOVERY_INITIAL_SHARDS) .put(stripIndexPrefix(TableParameterInfo.WARMER_ENABLED), TableParameterInfo.WARMER_ENABLED) .put(stripIndexPrefix(TableParameterInfo.UNASSIGNED_NODE_LEFT_DELAYED_TIMEOUT), TableParameterInfo.UNASSIGNED_NODE_LEFT_DELAYED_TIMEOUT) .put(stripIndexPrefix(TableParameterInfo.NUMBER_OF_SHARDS), TableParameterInfo.NUMBER_OF_SHARDS) .put("blobs_path", TableParameterInfo.BLOBS_PATH) .build(); private static final ImmutableBiMap<String, String> ES_TO_CRATE_SETTINGS_MAP = CRATE_TO_ES_SETTINGS_MAP.inverse(); private static final ImmutableBiMap<String, String> CRATE_TO_ES_MAPPINGS_MAP = ImmutableBiMap.<String, String>builder() .put("column_policy", TableParameterInfo.COLUMN_POLICY) .build(); private static final ImmutableBiMap<String, String> ES_TO_CRATE_MAPPINGS_MAP = CRATE_TO_ES_MAPPINGS_MAP.inverse(); private static final ImmutableMap<String, SettingsApplier> SETTINGS_APPLIER = ImmutableMap.<String, SettingsApplier>builder() .put(TableParameterInfo.NUMBER_OF_REPLICAS, new NumberOfReplicasSettingApplier()) .put(TableParameterInfo.REFRESH_INTERVAL, new RefreshIntervalSettingApplier()) .put(TableParameterInfo.READ_ONLY, new SettingsAppliers.BooleanSettingsApplier(CrateTableSettings.READ_ONLY)) .put(TableParameterInfo.BLOCKS_READ, new SettingsAppliers.BooleanSettingsApplier(CrateTableSettings.BLOCKS_READ)) .put(TableParameterInfo.BLOCKS_WRITE, new SettingsAppliers.BooleanSettingsApplier(CrateTableSettings.BLOCKS_WRITE)) .put(TableParameterInfo.BLOCKS_METADATA, new SettingsAppliers.BooleanSettingsApplier(CrateTableSettings.BLOCKS_METADATA)) .put(TableParameterInfo.FLUSH_THRESHOLD_SIZE, new SettingsAppliers.ByteSizeSettingsApplier(CrateTableSettings.FLUSH_THRESHOLD_SIZE)) .put(TableParameterInfo.ROUTING_ALLOCATION_ENABLE, new SettingsAppliers.StringSettingsApplier(CrateTableSettings.ROUTING_ALLOCATION_ENABLE)) .put(TableParameterInfo.TRANSLOG_SYNC_INTERVAL, new SettingsAppliers.TimeSettingsApplier(CrateTableSettings.TRANSLOG_SYNC_INTERVAL)) .put(TableParameterInfo.TOTAL_SHARDS_PER_NODE, new SettingsAppliers.IntSettingsApplier(CrateTableSettings.TOTAL_SHARDS_PER_NODE)) .put(TableParameterInfo.RECOVERY_INITIAL_SHARDS, new RecoveryInitialShardsApplier()) .put(TableParameterInfo.WARMER_ENABLED, new SettingsAppliers.BooleanSettingsApplier(CrateTableSettings.WARMER_ENABLED)) .put(TableParameterInfo.UNASSIGNED_NODE_LEFT_DELAYED_TIMEOUT, new SettingsAppliers.TimeSettingsApplier(CrateTableSettings.UNASSIGNED_NODE_LEFT_DELAYED_TIMEOUT)) .put(TableParameterInfo.NUMBER_OF_SHARDS, new NumberOfShardsSettingsApplier()) .put(TableParameterInfo.BLOBS_PATH, new BlobPathSettingApplier()) .build(); private static final ImmutableMap<String, MappingsApplier> MAPPINGS_APPLIER = ImmutableMap.<String, MappingsApplier>builder() .put(TableParameterInfo.COLUMN_POLICY, new ColumnPolicyMappingApplier()) .build(); private static String stripIndexPrefix(String setting) { if (setting.startsWith("index.")) { return setting.substring("index.".length()); } return setting; } public static String esToCrateSettingName(String esSettingName) { String val = ES_TO_CRATE_SETTINGS_MAP.get(esSettingName); return (val == null) ? esSettingName : val; } public static void analyze(TableParameter tableParameter, TableParameterInfo tableParameterInfo, Optional<GenericProperties> properties, Row parameters) { analyze(tableParameter, tableParameterInfo, properties, parameters, false); } public static void analyze(TableParameter tableParameter, TableParameterInfo tableParameterInfo, Optional<GenericProperties> properties, Row parameters, boolean withDefaults) { if (withDefaults) { SettingsApplier settingsApplier = SETTINGS_APPLIER.get(TableParameterInfo.NUMBER_OF_REPLICAS); tableParameter.settingsBuilder().put(settingsApplier.getDefault()); for (String mappingEntry : tableParameterInfo.supportedMappings()) { MappingsApplier mappingsApplier = MAPPINGS_APPLIER.get(mappingEntry); tableParameter.mappings().put(mappingsApplier.name, mappingsApplier.getDefault()); } } if (properties.isPresent()) { Map<String, Expression> tableProperties = properties.get().properties(); validateTableProperties(tableParameterInfo, tableProperties.keySet()); for (String setting : tableParameterInfo.supportedSettings()) { String settingName = ES_TO_CRATE_SETTINGS_MAP.get(setting); if (tableProperties.containsKey(settingName)) { SettingsApplier settingsApplier = SETTINGS_APPLIER.get(setting); settingsApplier.apply(tableParameter.settingsBuilder(), parameters, tableProperties.get(settingName)); } } for (String mappingEntry : tableParameterInfo.supportedMappings()) { String mappingName = ES_TO_CRATE_MAPPINGS_MAP.get(mappingEntry); if (tableProperties.containsKey(mappingName)) { MappingsApplier mappingsApplier = MAPPINGS_APPLIER.get(mappingEntry); mappingsApplier.apply(tableParameter.mappings(), parameters, tableProperties.get(mappingName)); } } } } public static void analyze(TableParameter tableParameter, TableParameterInfo tableParameterInfo, List<String> properties) { validateTableProperties(tableParameterInfo, properties); for (String setting : tableParameterInfo.supportedSettings()) { String settingName = ES_TO_CRATE_SETTINGS_MAP.get(setting); if (properties.contains(settingName)) { SettingsApplier settingsApplier = SETTINGS_APPLIER.get(setting); tableParameter.settingsBuilder().put(settingsApplier.getDefault()); } } for (String mappingEntry : tableParameterInfo.supportedMappings()) { String mappingName = ES_TO_CRATE_MAPPINGS_MAP.get(mappingEntry); if (properties.contains(mappingName)) { MappingsApplier mappingsApplier = MAPPINGS_APPLIER.get(mappingEntry); tableParameter.mappings().put(mappingsApplier.name, mappingsApplier.getDefault()); } } } private static void validateTableProperties(TableParameterInfo tableParameterInfo, Collection<String> propertyNames) { List<String> supportedParameters = new ArrayList<>(tableParameterInfo.supportedSettings()); supportedParameters.addAll(tableParameterInfo.supportedMappings()); for (String propertyName : propertyNames) { String esName = CRATE_TO_ES_SETTINGS_MAP.get(propertyName); if (esName == null) { esName = CRATE_TO_ES_MAPPINGS_MAP.get(propertyName); } Preconditions.checkArgument(supportedParameters.contains(esName), String.format(Locale.ENGLISH, "Invalid property \"%s\" passed to [ALTER | CREATE] TABLE statement", propertyName)); } } protected static class NumberOfReplicasSettingApplier extends SettingsAppliers.AbstractSettingsApplier { private static final Settings DEFAULT = Settings.builder() .put(TableParameterInfo.NUMBER_OF_REPLICAS, 1) .put(TableParameterInfo.AUTO_EXPAND_REPLICAS, false) .build(); public NumberOfReplicasSettingApplier() { super(ES_TO_CRATE_SETTINGS_MAP.get(TableParameterInfo.NUMBER_OF_REPLICAS), DEFAULT); } @Override public void apply(Settings.Builder settingsBuilder, Row parameters, Expression expression) { Preconditions.checkArgument(!(expression instanceof ArrayLiteral), String.format(Locale.ENGLISH, "array literal not allowed for \"%s\"", ES_TO_CRATE_SETTINGS_MAP.get(TableParameterInfo.NUMBER_OF_REPLICAS))); NumberOfReplicas numberOfReplicas; try { Integer numReplicas = ExpressionToNumberVisitor.convert(expression, parameters).intValue(); numberOfReplicas = new NumberOfReplicas(numReplicas); } catch (IllegalArgumentException e) { String numReplicas = ExpressionToObjectVisitor.convert(expression, parameters).toString(); numberOfReplicas = new NumberOfReplicas(numReplicas); } // in case the number_of_replicas is changing from auto_expand to a fixed number -> disable auto expand settingsBuilder.put(TableParameterInfo.AUTO_EXPAND_REPLICAS, false); settingsBuilder.put(numberOfReplicas.esSettingKey(), numberOfReplicas.esSettingValue()); } @Override public Settings getDefault() { return DEFAULT; } @Override public void applyValue(Settings.Builder settingsBuilder, Object value) { throw new UnsupportedOperationException("Not supported"); } } private static class RefreshIntervalSettingApplier extends SettingsAppliers.AbstractSettingsApplier { public static final Settings DEFAULT = Settings.builder() .put(TableParameterInfo.REFRESH_INTERVAL, CrateTableSettings.REFRESH_INTERVAL.defaultValue().millis() + "ms").build(); private RefreshIntervalSettingApplier() { super(ES_TO_CRATE_SETTINGS_MAP.get(TableParameterInfo.REFRESH_INTERVAL), DEFAULT); } @Override public void apply(Settings.Builder settingsBuilder, Row parameters, Expression expression) { Number refreshIntervalValue; try { refreshIntervalValue = ExpressionToNumberVisitor.convert(expression, parameters); } catch (IllegalArgumentException e) { throw invalidException(e); } settingsBuilder.put(TableParameterInfo.REFRESH_INTERVAL, refreshIntervalValue.toString() + "ms"); } @Override public Settings getDefault() { return DEFAULT; } @Override public void applyValue(Settings.Builder settingsBuilder, Object value) { throw new UnsupportedOperationException("Not supported"); } } private static class RecoveryInitialShardsApplier extends SettingsAppliers.AbstractSettingsApplier { public ImmutableSet<String> ALLOWED_VALUES = ImmutableSet.of( "quorum", "quorum-1", "full", "full-1", "half" ); public static final Settings DEFAULT = Settings.builder() .put(TableParameterInfo.RECOVERY_INITIAL_SHARDS, CrateTableSettings.RECOVERY_INITIAL_SHARDS.defaultValue()) .build(); private RecoveryInitialShardsApplier() { super(ES_TO_CRATE_SETTINGS_MAP.get(TableParameterInfo.RECOVERY_INITIAL_SHARDS), DEFAULT); } @SuppressWarnings("SuspiciousMethodCalls") @Override public void apply(Settings.Builder settingsBuilder, Row parameters, Expression expression) { Object shardsRecoverySettings; try { shardsRecoverySettings = ExpressionToNumberVisitor.convert(expression, parameters).intValue(); } catch (IllegalArgumentException e) { shardsRecoverySettings = ExpressionToObjectVisitor.convert(expression, parameters).toString(); if (!ALLOWED_VALUES.contains(shardsRecoverySettings)) { throw invalidException(); } } settingsBuilder.put(TableParameterInfo.RECOVERY_INITIAL_SHARDS, shardsRecoverySettings); } } private static class NumberOfShardsSettingsApplier extends SettingsAppliers.AbstractSettingsApplier { public static final Settings DEFAULT = Settings.builder() .put(TableParameterInfo.NUMBER_OF_SHARDS, 5).build(); private NumberOfShardsSettingsApplier() { super(ES_TO_CRATE_SETTINGS_MAP.get(TableParameterInfo.NUMBER_OF_SHARDS), DEFAULT); } @Override public void apply(Settings.Builder settingsBuilder, Row parameters, Expression expression) { int numberOfShardsValue = 0; try { numberOfShardsValue = ExpressionToNumberVisitor.convert(expression, parameters).intValue(); } catch (IllegalArgumentException e) { throw invalidException(e); } if (numberOfShardsValue < 1) { throw new IllegalArgumentException(String.format(Locale.ENGLISH, "%s must be greater than 0", name)); } settingsBuilder.put(TableParameterInfo.NUMBER_OF_SHARDS, numberOfShardsValue); } @Override public void applyValue(Settings.Builder settingsBuilder, Object value) { throw new UnsupportedOperationException("Not supported"); } @Override public Settings getDefault() { return DEFAULT; } } private static class BlobPathSettingApplier extends SettingsAppliers.AbstractSettingsApplier { private BlobPathSettingApplier() { super(ES_TO_CRATE_SETTINGS_MAP.get(TableParameterInfo.BLOBS_PATH), Settings.EMPTY); } @Override public void apply(Settings.Builder settingsBuilder, Row parameters, Expression expression) { String blobPath; try { blobPath = SafeExpressionToStringVisitor.convert(expression, parameters); } catch (IllegalArgumentException e) { throw invalidException(e); } settingsBuilder.put(TableParameterInfo.BLOBS_PATH, blobPath); } @Override public void applyValue(Settings.Builder settingsBuilder, Object value) { throw new UnsupportedOperationException("Not supported"); } } private static class ColumnPolicyMappingApplier extends MappingsApplier { private ColumnPolicyMappingApplier() { super(TableParameterInfo.COLUMN_POLICY, ES_TO_CRATE_MAPPINGS_MAP.get(TableParameterInfo.COLUMN_POLICY), true); } @Override public void apply(Map<String, Object> mappings, Row parameters, Expression expression) { ColumnPolicy policy; try { String policyName = ExpressionToStringVisitor.convert(expression, parameters); policy = ColumnPolicy.byName(policyName); } catch (IllegalArgumentException e) { throw invalidException(e); } applyValue(mappings, policy.mappingValue()); } @Override public Object validate(Object value) { if (value == ColumnPolicy.IGNORED.mappingValue()) { throw invalidException(); } return value; } } }