diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java index f40b9c9a011c..54330d327fac 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java @@ -41,6 +41,7 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Optional; import java.util.Set; import java.util.function.Supplier; import javax.sql.DataSource; @@ -68,8 +69,8 @@ public class JdbcCatalogSchema extends JdbcBaseSchema implements Wrapper { /** default schema name, lazily initialized. */ @SuppressWarnings({"method.invocation.invalid", "Convert2MethodRef"}) - private final Supplier defaultSchemaName = - Suppliers.memoize(() -> computeDefaultSchemaName()); + private final Supplier> defaultSchemaName = + Suppliers.memoize(() -> Optional.ofNullable(computeDefaultSchemaName())); /** Creates a JdbcCatalogSchema. */ public JdbcCatalogSchema(DataSource dataSource, SqlDialect dialect, @@ -150,7 +151,7 @@ public static JdbcCatalogSchema create( return subSchemas; } - private String computeDefaultSchemaName() { + private @Nullable String computeDefaultSchemaName() { try (Connection connection = dataSource.getConnection()) { return connection.getSchema(); } catch (SQLException e) { @@ -160,7 +161,7 @@ private String computeDefaultSchemaName() { /** Returns the name of the default sub-schema. */ public @Nullable String getDefaultSubSchemaName() { - return defaultSchemaName.get(); + return defaultSchemaName.get().orElse(null); } /** Returns the data source. */ diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java index 20fb07f1f4fa..5a351177e7aa 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java @@ -34,6 +34,7 @@ import org.apache.calcite.schema.Table; import org.apache.calcite.schema.Wrapper; import org.apache.calcite.schema.lookup.IgnoreCaseLookup; +import org.apache.calcite.schema.lookup.LazyReference; import org.apache.calcite.schema.lookup.LikePattern; import org.apache.calcite.schema.lookup.Lookup; import org.apache.calcite.sql.SqlDialect; @@ -47,7 +48,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; -import org.checkerframework.checker.initialization.qual.UnknownInitialization; import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -88,7 +88,7 @@ public class JdbcSchema extends JdbcBaseSchema implements Schema, Wrapper { final @Nullable String schema; public final SqlDialect dialect; final JdbcConvention convention; - private final Lookup tables; + private final LazyReference> tables = new LazyReference<>(); private final Lookup subSchemas = Lookup.empty(); @Experimental @@ -113,21 +113,6 @@ public JdbcSchema(DataSource dataSource, SqlDialect dialect, this.convention = convention; this.catalog = catalog; this.schema = schema; - @UnknownInitialization - JdbcSchema self = this; - this.tables = new IgnoreCaseLookup
() { - @Override public @Nullable Table get(String name) { - try (Stream s = self.getMetaTableStream(name)) { - return s.findFirst().map(it -> jdbcTableMapper(it)).orElse(null); - } - } - - @Override public Set getNames(LikePattern pattern) { - try (Stream s = self.getMetaTableStream(pattern.pattern)) { - return s.map(it -> it.tableName).collect(Collectors.toSet()); - } - } - }; } public static JdbcSchema create( @@ -229,7 +214,19 @@ public static DataSource dataSource(String url, @Nullable String driverClassName } @Override public Lookup
tables() { - return tables; + return tables.getOrCompute(() -> new IgnoreCaseLookup
() { + @Override public @Nullable Table get(String name) { + try (Stream s = getMetaTableStream(name)) { + return s.findFirst().map(it -> jdbcTableMapper(it)).orElse(null); + } + } + + @Override public Set getNames(LikePattern pattern) { + try (Stream s = getMetaTableStream(pattern.pattern)) { + return s.map(it -> it.tableName).collect(Collectors.toSet()); + } + } + }); } @Override public Lookup subSchemas() { diff --git a/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java b/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java index d9c622d8e404..2d52edf60093 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java +++ b/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java @@ -30,7 +30,6 @@ import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; -import org.checkerframework.checker.initialization.qual.UnderInitialization; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; @@ -101,8 +100,7 @@ private SimpleCalciteSchema(@Nullable CalciteSchema parent, return null; } - @Override protected CalciteSchema createSubSchema(@UnderInitialization SimpleCalciteSchema this, - Schema schema, String name) { + @Override protected CalciteSchema createSubSchema(Schema schema, String name) { return new SimpleCalciteSchema(this, schema, name); } diff --git a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java index 3b89a791acfc..2786149cd523 100644 --- a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java @@ -47,6 +47,7 @@ import org.apache.calcite.schema.TemporalTable; import org.apache.calcite.schema.TranslatableTable; import org.apache.calcite.schema.Wrapper; +import org.apache.calcite.schema.lookup.LazyReference; import org.apache.calcite.schema.lookup.LikePattern; import org.apache.calcite.schema.lookup.Lookup; import org.apache.calcite.sql.SqlAccessType; @@ -60,7 +61,6 @@ import com.google.common.collect.ImmutableList; -import org.checkerframework.checker.initialization.qual.UnknownInitialization; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.AbstractList; @@ -432,16 +432,13 @@ private static class MySchemaPlus implements SchemaPlus { private final @Nullable SchemaPlus parent; private final String name; private final Schema schema; - private final Lookup subSchemas; + private final LazyReference> subSchemas = new LazyReference<>(); MySchemaPlus(@Nullable SchemaPlus parent, String name, Schema schema) { this.parent = parent; this.name = name; this.schema = schema; - @UnknownInitialization - MySchemaPlus self = this; - this.subSchemas = schema.subSchemas().map((s, key) -> new MySchemaPlus(self, key, s)); } public static MySchemaPlus create(Path path) { @@ -463,8 +460,8 @@ public static MySchemaPlus create(Path path) { return name; } - @Deprecated @Override public @Nullable SchemaPlus getSubSchema(String name) { - return subSchemas.get(name); + @Override public @Nullable SchemaPlus getSubSchema(String name) { + return subSchemas().get(name); } @Override public SchemaPlus add(String name, Schema schema) { @@ -517,7 +514,8 @@ public static MySchemaPlus create(Path path) { } @Override public Lookup subSchemas() { - return subSchemas; + return subSchemas.getOrCompute( + () -> schema.subSchemas().map((s, key) -> new MySchemaPlus(this, key, s))); } @Override public @Nullable Table getTable(String name) { diff --git a/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java b/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java index f69275519ffc..93bdcb9f585d 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java @@ -26,13 +26,13 @@ import org.apache.calcite.schema.Schemas; import org.apache.calcite.schema.Table; import org.apache.calcite.schema.lookup.CompatibilityLookup; +import org.apache.calcite.schema.lookup.LazyReference; import org.apache.calcite.schema.lookup.Lookup; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; -import org.checkerframework.checker.initialization.qual.UnknownInitialization; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; @@ -60,23 +60,17 @@ */ public class AbstractSchema implements Schema { - private Lookup
tables = new CompatibilityLookup<>(this::getTable, this::getTableNames); - private Lookup subSchemas = - new CompatibilityLookup<>(this::getSubSchema, this::getSubSchemaNames); - - public AbstractSchema() { - @UnknownInitialization - AbstractSchema self = this; - tables = new CompatibilityLookup<>(self::getTable, self::getTableNames); - subSchemas = new CompatibilityLookup<>(self::getSubSchema, self::getSubSchemaNames); - } + private LazyReference> tables = new LazyReference<>(); + private LazyReference> subSchemas = new LazyReference<>(); @Override public Lookup
tables() { - return tables; + return tables.getOrCompute( + () -> new CompatibilityLookup<>(this::getTable, this::getTableNames)); } @Override public Lookup subSchemas() { - return subSchemas; + return subSchemas.getOrCompute( + () -> new CompatibilityLookup<>(this::getSubSchema, this::getSubSchemaNames)); } @Override public boolean isMutable() { diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/CachedLookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/CachedLookup.java index 707914c22e37..bde2b8673dd1 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/CachedLookup.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/CachedLookup.java @@ -21,6 +21,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; /** * This class can be used to make a snapshot of a lookups. @@ -30,12 +31,11 @@ public class CachedLookup implements Lookup { private final Lookup delegate; - private volatile Lookup cachedDelegate; + private AtomicReference> cachedDelegate = new AtomicReference<>(); private boolean enabled = true; public CachedLookup(Lookup delegate) { this.delegate = delegate; - this.cachedDelegate = null; } @Override public @Nullable T get(final String name) { @@ -54,26 +54,28 @@ private Lookup delegate() { if (!enabled) { return delegate; } - if (cachedDelegate == null) { - synchronized (this) { - if (cachedDelegate == null) { - NameMap map = new NameMap<>(); - for (String name : delegate.getNames(LikePattern.any())) { - T entry = delegate.get(name); - if (entry != null) { - map.put(name, delegate.get(name)); - } - } - cachedDelegate = new NameMapLookup<>(map); + while (true) { + Lookup cached = cachedDelegate.get(); + if (cached != null) { + return cached; + } + NameMap map = new NameMap<>(); + for (String name : delegate.getNames(LikePattern.any())) { + T entry = delegate.get(name); + if (entry != null) { + map.put(name, delegate.get(name)); } } + cached = new NameMapLookup<>(map); + if (cachedDelegate.compareAndSet(null, cached)) { + return cached; + } } - return cachedDelegate; } public void enable(boolean enabled) { if (!enabled) { - cachedDelegate = null; + cachedDelegate.set(null); } this.enabled = enabled; } diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/IgnoreCaseLookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/IgnoreCaseLookup.java index d4651529d5f6..a33d9354d23c 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/IgnoreCaseLookup.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/IgnoreCaseLookup.java @@ -30,10 +30,7 @@ */ public abstract class IgnoreCaseLookup implements Lookup { - private volatile NameMap nameMap = null; - - public IgnoreCaseLookup() { - } + private LazyReference> nameMap = new LazyReference<>(); /** * Returns a named entity with a given name, or null if not found. @@ -50,31 +47,30 @@ public IgnoreCaseLookup() { * @return Entity, or null */ @Override @Nullable public Named getIgnoreCase(String name) { - Map.Entry entry = getNameMap(false).range(name, false).firstEntry(); - if (entry == null) { - entry = getNameMap(true).range(name, false).firstEntry(); - if (entry == null) { + int retryCounter = 0; + while (true) { + Map.Entry entry = nameMap.getOrCompute(this::loadNames) + .range(name, false) + .firstEntry(); + if (entry != null) { + T result = get(entry.getValue()); + return result == null ? null : new Named<>(entry.getKey(), result); + } + retryCounter++; + if (retryCounter > 1) { return null; } + nameMap.reset(); } - T result = get(entry.getValue()); - return result == null ? null : new Named<>(entry.getKey(), result); } @Override public abstract Set getNames(LikePattern pattern); - private NameMap getNameMap(boolean forceReload) { - if (nameMap == null || forceReload) { - synchronized (this) { - if (nameMap == null || forceReload) { - NameMap tmp = new NameMap<>(); - for (String name : getNames(LikePattern.any())) { - tmp.put(name, name); - } - nameMap = tmp; - } - } + private NameMap loadNames() { + NameMap result = new NameMap<>(); + for (String name : getNames(LikePattern.any())) { + result.put(name, name); } - return nameMap; + return result; } } diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/LazyReference.java b/core/src/main/java/org/apache/calcite/schema/lookup/LazyReference.java index dd53415d34b3..fec234a2a1f8 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/LazyReference.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/LazyReference.java @@ -40,4 +40,8 @@ public T getOrCompute(Supplier supplier) { } } } + + public void reset() { + value.set(null); + } }