com.hazelcast.org.apache.calcite.config.CalciteSystemProperty Maven / Gradle / Ivy
/*
* 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 com.hazelcast.org.apache.calcite.config;
import com.hazelcast.com.google.common.base.MoreObjects;
import com.hazelcast.com.google.common.collect.ImmutableSet;
import com.hazelcast.org.checkerframework.checker.nullness.qual.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.function.Function;
import java.util.function.IntPredicate;
import java.util.stream.Stream;
import static java.util.Objects.requireNonNull;
/**
* A Calcite specific system property that is used to configure various aspects of the framework.
*
* Calcite system properties must always be in the "calcite" root namespace.
*
* @param the type of the property value
*/
public final class CalciteSystemProperty {
/**
* Holds all system properties related with the Calcite.
*
* Deprecated "saffron.properties"
(in namespaces"saffron" and "net.sf.saffron")
* are also kept here but under "calcite" namespace.
*/
private static final Properties PROPERTIES = loadProperties();
/**
* Whether to run Calcite in debug mode.
*
* When debug mode is activated significantly more information is gathered and printed to
* STDOUT. It is most commonly used to print and identify problems in generated java code. Debug
* mode is also used to perform more verifications at runtime, which are not performed during
* normal execution.
*/
public static final CalciteSystemProperty DEBUG =
booleanProperty("calcite.debug", false);
/**
* Whether to exploit join commutative property.
*/
// TODO review zabetak:
// Does the property control join commutativity or rather join associativity? The property is
// associated with {@link com.hazelcast.org.apache.calcite.rel.rules.JoinAssociateRule} and not with
// {@link com.hazelcast.org.apache.calcite.rel.rules.JoinCommuteRule}.
public static final CalciteSystemProperty COMMUTE =
booleanProperty("calcite.enable.join.commute", false);
/** Whether to enable the collation trait in the default planner configuration.
*
* Some extra optimizations are possible if enabled, but queries should
* work either way. At some point this will become a preference, or we will
* run multiple phases: first disabled, then enabled. */
public static final CalciteSystemProperty ENABLE_COLLATION_TRAIT =
booleanProperty("calcite.enable.collation.trait", true);
/** Whether the enumerable convention is enabled in the default planner configuration. */
public static final CalciteSystemProperty ENABLE_ENUMERABLE =
booleanProperty("calcite.enable.enumerable", true);
/** Whether the EnumerableTableScan should support ARRAY fields. */
public static final CalciteSystemProperty ENUMERABLE_ENABLE_TABLESCAN_ARRAY =
booleanProperty("calcite.enable.enumerable.tablescan.array", false);
/** Whether the EnumerableTableScan should support MAP fields. */
public static final CalciteSystemProperty ENUMERABLE_ENABLE_TABLESCAN_MAP =
booleanProperty("calcite.enable.enumerable.tablescan.map", false);
/** Whether the EnumerableTableScan should support MULTISET fields. */
public static final CalciteSystemProperty ENUMERABLE_ENABLE_TABLESCAN_MULTISET =
booleanProperty("calcite.enable.enumerable.tablescan.multiset", false);
/** Whether streaming is enabled in the default planner configuration. */
public static final CalciteSystemProperty ENABLE_STREAM =
booleanProperty("calcite.enable.stream", true);
/**
* Whether RexNode digest should be normalized (e.g. call operands ordered).
* Normalization helps to treat $0=$1 and $1=$0 expressions equal, thus it saves efforts
* on planning.
*/
public static final CalciteSystemProperty ENABLE_REX_DIGEST_NORMALIZE =
booleanProperty("calcite.enable.rexnode.digest.normalize", true);
/**
* Whether to follow the SQL standard strictly.
*/
public static final CalciteSystemProperty STRICT =
booleanProperty("calcite.strict.sql", false);
/**
* Whether to include a GraphViz representation when dumping the state of the Volcano planner.
*/
public static final CalciteSystemProperty DUMP_GRAPHVIZ =
booleanProperty("calcite.volcano.dump.graphviz", true);
/**
* Whether to include RelSet
information when dumping the state of the Volcano
* planner.
*/
public static final CalciteSystemProperty DUMP_SETS =
booleanProperty("calcite.volcano.dump.sets", true);
/**
* Whether to enable top-down optimization. This config can be overridden
* by {@link CalciteConnectionProperty#TOPDOWN_OPT}.
*
* Note: Enabling top-down optimization will automatically disable
* the use of AbstractConverter and related rules.
*/
public static final CalciteSystemProperty TOPDOWN_OPT =
booleanProperty("calcite.planner.topdown.opt", false);
/**
* Whether to run integration tests.
*/
// TODO review zabetak:
// The property is used in only one place and it is associated with mongodb. Should we drop this
// property and just use TEST_MONGODB?
public static final CalciteSystemProperty INTEGRATION_TEST =
booleanProperty("calcite.integrationTest", false);
/**
* Which database to use for tests that require a JDBC data source.
*
* The property can take one of the following values:
*
*
* - HSQLDB (default)
* - H2
* - MYSQL
* - ORACLE
* - POSTGRESQL
*
*
* If the specified value is not included in the previous list, the default
* is used.
*
*
We recommend that casual users use hsqldb, and frequent Calcite
* developers use MySQL. The test suite runs faster against the MySQL database
* (mainly because of the 0.1 second versus 6 seconds startup time). You have
* to populate MySQL manually with the foodmart data set, otherwise there will
* be test failures.
*/
public static final CalciteSystemProperty TEST_DB =
stringProperty("calcite.test.db", "HSQLDB",
ImmutableSet.of(
"HSQLDB",
"H2",
"MYSQL",
"ORACLE",
"POSTGRESQL"));
/**
* Path to the dataset file that should used for integration tests.
*
* If a path is not set, then one of the following values will be used:
*
*
* - ../calcite-test-dataset
* - ../../calcite-test-dataset
* - .
*
* The first valid path that exists in the filesystem will be chosen.
*/
public static final CalciteSystemProperty TEST_DATASET_PATH =
new CalciteSystemProperty<>("calcite.test.dataset", v -> {
if (v != null) {
return v;
}
final String[] dirs = {
"../calcite-test-dataset",
"../../calcite-test-dataset"
};
for (String s : dirs) {
if (new File(s).exists() && new File(s, "vm").exists()) {
return s;
}
}
return ".";
});
/**
* Whether to run MongoDB tests.
*/
public static final CalciteSystemProperty TEST_MONGODB =
booleanProperty("calcite.test.mongodb", true);
/**
* Whether to run Splunk tests.
*
* Disabled by default, because we do not expect Splunk to be installed
* and populated with the data set necessary for testing.
*/
public static final CalciteSystemProperty TEST_SPLUNK =
booleanProperty("calcite.test.splunk", false);
/**
* Whether to run Druid tests.
*/
public static final CalciteSystemProperty TEST_DRUID =
booleanProperty("calcite.test.druid", false);
/**
* Whether to run Cassandra tests.
*/
public static final CalciteSystemProperty TEST_CASSANDRA =
booleanProperty("calcite.test.cassandra", true);
/**
* Whether to run InnoDB tests.
*/
public static final CalciteSystemProperty TEST_INNODB =
booleanProperty("calcite.test.innodb", true);
/**
* Whether to run Redis tests.
*/
public static final CalciteSystemProperty TEST_REDIS =
booleanProperty("calcite.test.redis", true);
/**
* Whether to use Docker containers (https://www.testcontainers.org/) in tests.
*
* If the property is set to true
, affected tests will attempt to start Docker
* containers; when Docker is not available tests fallback to other execution modes and if it's
* not possible they are skipped entirely.
*
* If the property is set to false
, Docker containers are not used at all and
* affected tests either fallback to other execution modes or skipped entirely.
*
* Users can override the default behavior to force non-Dockerized execution even when Docker
* is installed on the machine; this can be useful for replicating an issue that appears only in
* non-docker test mode or for running tests both with and without containers in CI.
*/
public static final CalciteSystemProperty TEST_WITH_DOCKER_CONTAINER =
booleanProperty("calcite.test.docker", true);
/**
* A list of ids designating the queries
* (from query.json in new.hydromatic:foodmart-queries:0.4.1)
* that should be run as part of FoodmartTest.
*/
// TODO review zabetak:
// The name of the property is not appropriate. A better alternative would be
// calcite.test.foodmart.queries.ids. Moreover, I am not in favor of using system properties for
// parameterized tests.
public static final CalciteSystemProperty<@Nullable String> TEST_FOODMART_QUERY_IDS =
new CalciteSystemProperty<>("calcite.ids", Function.<@Nullable String>identity());
/**
* Whether the optimizer will consider adding converters of infinite cost in
* order to convert a relational expression from one calling convention to
* another.
*/
public static final CalciteSystemProperty ALLOW_INFINITE_COST_CONVERTERS =
booleanProperty("calcite.opt.allowInfiniteCostConverters", true);
/**
* The name of the default character set.
*
* It is used by {@link com.hazelcast.org.apache.calcite.sql.validate.SqlValidator}.
*/
// TODO review zabetak:
// What happens if a wrong value is specified?
public static final CalciteSystemProperty DEFAULT_CHARSET =
stringProperty("calcite.default.charset", "ISO-8859-1");
/**
* The name of the default national character set.
*
* It is used with the N'string' construct in
* {@link com.hazelcast.org.apache.calcite.sql.SqlLiteral#SqlLiteral}
* and may be different from the {@link #DEFAULT_CHARSET}.
*/
// TODO review zabetak:
// What happens if a wrong value is specified?
public static final CalciteSystemProperty DEFAULT_NATIONAL_CHARSET =
stringProperty("calcite.default.nationalcharset", "ISO-8859-1");
/**
* The name of the default collation.
*
* It is used in {@link com.hazelcast.org.apache.calcite.sql.SqlCollation} and
* {@link com.hazelcast.org.apache.calcite.sql.SqlLiteral#SqlLiteral}.
*/
// TODO review zabetak:
// What happens if a wrong value is specified?
public static final CalciteSystemProperty DEFAULT_COLLATION =
stringProperty("calcite.default.collation.name", "ISO-8859-1$en_US");
/**
* The strength of the default collation.
* Allowed values (as defined in {@link java.text.Collator}) are: primary, secondary,
* tertiary, identical.
*
* It is used in {@link com.hazelcast.org.apache.calcite.sql.SqlCollation} and
* {@link com.hazelcast.org.apache.calcite.sql.SqlLiteral#SqlLiteral}.
*/
// TODO review zabetak:
// What happens if a wrong value is specified?
public static final CalciteSystemProperty DEFAULT_COLLATION_STRENGTH =
stringProperty("calcite.default.collation.strength", "primary");
/**
* The maximum size of the cache of metadata handlers.
*
* A typical value is the number of queries being concurrently prepared multiplied by the
* number of types of metadata.
*
* If the value is less than 0, there is no limit.
*/
public static final CalciteSystemProperty METADATA_HANDLER_CACHE_MAXIMUM_SIZE =
intProperty("calcite.metadata.handler.cache.maximum.size", 1000);
/**
* The maximum size of the cache used for storing Bindable objects, instantiated via
* dynamically generated Java classes.
*
* The default value is 0.
*
* The property can take any value between [0, {@link Integer#MAX_VALUE}] inclusive. If the
* value is not valid (or not specified) then the default value is used.
*
* The cached objects may be quite big so it is suggested to use a rather small cache size
* (e.g., 1000). For the most common use cases a number close to 1000 should be enough to
* alleviate the performance penalty of compiling and loading classes.
*
* Setting this property to 0 disables the cache.
*/
public static final CalciteSystemProperty BINDABLE_CACHE_MAX_SIZE =
intProperty("calcite.bindable.cache.maxSize", 0, v -> v >= 0 && v <= Integer.MAX_VALUE);
/**
* The concurrency level of the cache used for storing Bindable objects, instantiated via
* dynamically generated Java classes.
*
* The default value is 1.
*
* The property can take any value between [1, {@link Integer#MAX_VALUE}] inclusive. If the
* value is not valid (or not specified) then the default value is used.
*
* This property has no effect if the cache is disabled (i.e., {@link #BINDABLE_CACHE_MAX_SIZE}
* set to 0.
*/
public static final CalciteSystemProperty BINDABLE_CACHE_CONCURRENCY_LEVEL =
intProperty("calcite.bindable.cache.concurrencyLevel", 1,
v -> v >= 1 && v <= Integer.MAX_VALUE);
private static CalciteSystemProperty booleanProperty(String key,
boolean defaultValue) {
// Note that "" -> true (convenient for command-lines flags like '-Dflag')
return new CalciteSystemProperty<>(key,
v -> v == null ? defaultValue
: "".equals(v) || Boolean.parseBoolean(v));
}
private static CalciteSystemProperty intProperty(String key, int defaultValue) {
return intProperty(key, defaultValue, v -> true);
}
/**
* Returns the value of the system property with the specified name as {@code
* int}. If any of the conditions below hold, returns the
* defaultValue
:
*
*
* - the property is not defined;
*
- the property value cannot be transformed to an int;
*
- the property value does not satisfy the checker.
*
*/
private static CalciteSystemProperty intProperty(String key, int defaultValue,
IntPredicate valueChecker) {
return new CalciteSystemProperty<>(key, v -> {
if (v == null) {
return defaultValue;
}
try {
int intVal = Integer.parseInt(v);
return valueChecker.test(intVal) ? intVal : defaultValue;
} catch (NumberFormatException nfe) {
return defaultValue;
}
});
}
private static CalciteSystemProperty stringProperty(String key, String defaultValue) {
return new CalciteSystemProperty<>(key, v -> v == null ? defaultValue : v);
}
private static CalciteSystemProperty stringProperty(
String key,
String defaultValue,
Set allowedValues) {
return new CalciteSystemProperty<>(key, v -> {
if (v == null) {
return defaultValue;
}
String normalizedValue = v.toUpperCase(Locale.ROOT);
return allowedValues.contains(normalizedValue) ? normalizedValue : defaultValue;
});
}
private static Properties loadProperties() {
Properties saffronProperties = new Properties();
ClassLoader classLoader = MoreObjects.firstNonNull(
Thread.currentThread().getContextClassLoader(),
CalciteSystemProperty.class.getClassLoader());
// Read properties from the file "saffron.properties", if it exists in classpath
try (InputStream stream = requireNonNull(classLoader, "classLoader")
.getResourceAsStream("saffron.properties")) {
if (stream != null) {
saffronProperties.load(stream);
}
} catch (IOException e) {
throw new RuntimeException("while reading from saffron.properties file", e);
} catch (RuntimeException e) {
if (!"java.security.AccessControlException".equals(e.getClass().getName())) {
throw e;
}
}
// Merge system and saffron properties, mapping deprecated saffron
// namespaces to calcite
final Properties allProperties = new Properties();
Stream.concat(
saffronProperties.entrySet().stream(),
System.getProperties().entrySet().stream())
.forEach(prop -> {
String deprecatedKey = (String) prop.getKey();
String newKey = deprecatedKey
.replace("net.sf.saffron.", "calcite.")
.replace("saffron.", "calcite.");
if (newKey.startsWith("calcite.")) {
allProperties.setProperty(newKey, (String) prop.getValue());
}
});
return allProperties;
}
private final T value;
private CalciteSystemProperty(String key,
Function super @Nullable String, ? extends T> valueParser) {
this.value = valueParser.apply(PROPERTIES.getProperty(key));
}
/**
* Returns the value of this property.
*
* @return the value of this property or null
if a default value has not been
* defined for this property.
*/
public T value() {
return value;
}
}