Skip to content

Commit

Permalink
[CALCITE-6024] A more efficient implementation of SqlOperatorTable, b…
Browse files Browse the repository at this point in the history
…acked by an immutable multi-map keyed by upper-case operator name

ReflectiveSqlOperatorTable and ListSqlOperatorTable now have
a common base class, the new
class SqlOperatorTables.IndexedSqlOperatorTable,
which stores operators in a multi-map keyed by their
upper-case name. Because it is a multi-map it is OK if there
are several operators with the same name, same name an
different syntax, or the same name with different case. The
map is efficient because there are unlikely to be very many
such operators.

The multi-map is immutable. You can still add an operator to
a ListSqlOperatorTable, but it will rebuild the whole
multi-map, then assign the new map to the field, and so is
not very efficient. The 'register(SqlOperator)' method is now
deprecated, to remind people that tables are better created
in entirety, rather than incrementally.

Previously there were two maps, whose keys were case-sensitive
and case-insensitive names; the keys also inclued the syntax
of the operator. Now the syntax is filtered on retrieval,
which makes sense because the syntax of the call to an
operator does not precisely match the syntax of the operator
(e.g. I can call CURRENT_TIMESTAMP with no parentheses, with
empty parentheses, or with parentheses containing precision).
Different subclasses have slightly different policies for
matching syntax.

Singleton instances of SqlStdOperatorTable and
OracleSqlOperatorTable are now held in a memoizing supplier,
which is simpler than the prefious two-phase initialization.

Close apache#3483
  • Loading branch information
julianhyde committed Oct 27, 2023
1 parent b11f179 commit 7b9660f
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 140 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,7 @@ private List<RexNode> toRexList(RelInput relInput, List operands) {
SqlSyntax sqlSyntax = SqlSyntax.valueOf(syntax);
List<SqlOperator> operators = new ArrayList<>();
operatorTable.lookupOperatorOverloads(
new SqlIdentifier(name, new SqlParserPos(0, 0)),
new SqlIdentifier(name, SqlParserPos.ZERO),
null,
sqlSyntax,
operators,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.util.ReflectiveSqlOperatorTable;

import org.checkerframework.checker.nullness.qual.Nullable;
import com.google.common.base.Suppliers;

import java.util.function.Supplier;

/**
* Operator table that contains only Oracle-specific functions and operators.
Expand All @@ -33,9 +35,11 @@ public class OracleSqlOperatorTable extends ReflectiveSqlOperatorTable {
//~ Static fields/initializers ---------------------------------------------

/**
* The table of contains Oracle-specific operators.
* The table of Oracle-specific operators.
*/
private static @Nullable OracleSqlOperatorTable instance;
private static final Supplier<OracleSqlOperatorTable> INSTANCE =
Suppliers.memoize(() ->
(OracleSqlOperatorTable) new OracleSqlOperatorTable().init());

@Deprecated // to be removed before 2.0
public static final SqlFunction DECODE = SqlLibraryOperators.DECODE;
Expand Down Expand Up @@ -64,16 +68,7 @@ public class OracleSqlOperatorTable extends ReflectiveSqlOperatorTable {
/**
* Returns the Oracle operator table, creating it if necessary.
*/
public static synchronized OracleSqlOperatorTable instance() {
OracleSqlOperatorTable instance = OracleSqlOperatorTable.instance;
if (instance == null) {
// Creates and initializes the standard operator table.
// Uses two-phase construction, because we can't initialize the
// table until the constructor of the sub-class has completed.
instance = new OracleSqlOperatorTable();
instance.init();
OracleSqlOperatorTable.instance = instance;
}
return instance;
public static OracleSqlOperatorTable instance() {
return INSTANCE.get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,15 @@
import org.apache.calcite.util.Optionality;
import org.apache.calcite.util.Pair;

import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;

import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;

import static org.apache.calcite.linq4j.Nullness.castNonNull;

Expand All @@ -98,7 +100,9 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
/**
* The standard operator table.
*/
private static @MonotonicNonNull SqlStdOperatorTable instance;
private static final Supplier<SqlStdOperatorTable> INSTANCE =
Suppliers.memoize(() ->
(SqlStdOperatorTable) new SqlStdOperatorTable().init());

//-------------------------------------------------------------
// SET OPERATORS
Expand Down Expand Up @@ -1364,6 +1368,7 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
/**
* The <code>UNNEST WITH ORDINALITY</code> operator.
*/
@LibraryOperator(libraries = {}) // do not include in index
public static final SqlUnnestOperator UNNEST_WITH_ORDINALITY =
new SqlUnnestOperator(true);

Expand Down Expand Up @@ -2540,15 +2545,16 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
/**
* Returns the standard operator table, creating it if necessary.
*/
public static synchronized SqlStdOperatorTable instance() {
if (instance == null) {
// Creates and initializes the standard operator table.
// Uses two-phase construction, because we can't initialize the
// table until the constructor of the sub-class has completed.
instance = new SqlStdOperatorTable();
instance.init();
}
return instance;
public static SqlStdOperatorTable instance() {
return INSTANCE.get();
}

@Override protected void lookUpOperators(String name,
boolean caseSensitive, Consumer<SqlOperator> consumer) {
// Only UDFs are looked up using case-sensitive search.
// Always look up built-in operators case-insensitively. Even in sessions
// with unquotedCasing=UNCHANGED and caseSensitive=true.
super.lookUpOperators(name, false, consumer);
}

/** Returns the group function for which a given kind is an auxiliary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.calcite.sql.util;

import org.apache.calcite.runtime.ConsList;
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlIdentifier;
Expand All @@ -24,19 +25,19 @@
import org.apache.calcite.sql.SqlSyntax;
import org.apache.calcite.sql.validate.SqlNameMatcher;

import com.google.common.collect.ImmutableSet;

import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
* Implementation of the {@link SqlOperatorTable} interface by using a list of
* {@link SqlOperator operators}.
*/
public class ListSqlOperatorTable implements SqlOperatorTable {
//~ Instance fields --------------------------------------------------------

private final List<SqlOperator> operatorList;
public class ListSqlOperatorTable
extends SqlOperatorTables.IndexedSqlOperatorTable
implements SqlOperatorTable {

//~ Constructors -----------------------------------------------------------

Expand All @@ -46,7 +47,7 @@ public class ListSqlOperatorTable implements SqlOperatorTable {
* table. */
@Deprecated // to be removed before 2.0
public ListSqlOperatorTable() {
this(new ArrayList<>(), false);
this(ImmutableSet.of());
}

/** Creates a mutable ListSqlOperatorTable backed by a given list.
Expand All @@ -55,12 +56,12 @@ public ListSqlOperatorTable() {
* table. */
@Deprecated // to be removed before 2.0
public ListSqlOperatorTable(List<SqlOperator> operatorList) {
this(operatorList, false);
this((Iterable<SqlOperator>) operatorList);
}

// internal constructor
ListSqlOperatorTable(List<SqlOperator> operatorList, boolean ignored) {
this.operatorList = operatorList;
ListSqlOperatorTable(Iterable<? extends SqlOperator> operatorList) {
super(operatorList);
}

//~ Methods ----------------------------------------------------------------
Expand All @@ -71,33 +72,35 @@ public ListSqlOperatorTable(List<SqlOperator> operatorList) {
* table. */
@Deprecated // to be removed before 2.0
public void add(SqlOperator op) {
operatorList.add(op);
// Rebuild the immutable collections with their current contents plus one.
setOperators(buildIndex(ConsList.of(op, getOperatorList())));
}

@Override public void lookupOperatorOverloads(SqlIdentifier opName,
@Nullable SqlFunctionCategory category,
SqlSyntax syntax,
List<SqlOperator> operatorList,
SqlNameMatcher nameMatcher) {
for (SqlOperator op : this.operatorList) {
if (!opName.isSimple()
|| !nameMatcher.matches(op.getName(), opName.getSimple())) {
continue;
if (!opName.isSimple()) {
return;
}
final String simpleName = opName.getSimple();
lookUpOperators(simpleName, nameMatcher.isCaseSensitive(), op -> {
if (op.getSyntax() != syntax
&& op.getSyntax().family != syntax.family) {
// Allow retrieval on exact syntax or family; for example,
// CURRENT_DATETIME has FUNCTION_ID syntax but can also be called with
// both FUNCTION_ID and FUNCTION syntax (e.g. SELECT CURRENT_DATETIME,
// CURRENT_DATETIME('UTC')).
return;
}
if (category != null
&& category != category(op)
&& !category.isUserDefinedNotSpecificFunction()) {
continue;
}
if (op.getSyntax() == syntax) {
operatorList.add(op);
} else if (syntax == SqlSyntax.FUNCTION
&& op instanceof SqlFunction) {
// this special case is needed for operators like CAST,
// which are treated as functions but have special syntax
operatorList.add(op);
return;
}
}
operatorList.add(op);
});
}

protected static SqlFunctionCategory category(SqlOperator operator) {
Expand All @@ -107,8 +110,4 @@ protected static SqlFunctionCategory category(SqlOperator operator) {
return SqlFunctionCategory.SYSTEM;
}
}

@Override public List<SqlOperator> getOperatorList() {
return operatorList;
}
}
Loading

0 comments on commit 7b9660f

Please sign in to comment.