You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2017/01/13 19:22:39 UTC
[1/4] calcite git commit: Site: Maryann Xue joins PMC
Repository: calcite
Updated Branches:
refs/heads/master 2c2b88391 -> 5f9c01908
Site: Maryann Xue joins PMC
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/46474d74
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/46474d74
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/46474d74
Branch: refs/heads/master
Commit: 46474d74d3a0851037fd7103ba59adeffbebdab0
Parents: 2c2b883
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Jan 9 10:01:35 2017 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Jan 12 10:35:37 2017 -0800
----------------------------------------------------------------------
site/_data/contributors.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/46474d74/site/_data/contributors.yml
----------------------------------------------------------------------
diff --git a/site/_data/contributors.yml b/site/_data/contributors.yml
index fb3e09b..688f716 100644
--- a/site/_data/contributors.yml
+++ b/site/_data/contributors.yml
@@ -76,7 +76,7 @@
apacheId: maryannxue
githubId: maryannxue
org: Intel
- role: Committer
+ role: PMC
- name: Michael Mior
apacheId: mmior
githubId: michaelmior
[3/4] calcite git commit: [CALCITE-1549] More helpful error message
when schema, table or column not found
Posted by jh...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java
index 49f109f..c8eb6b0 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java
@@ -25,13 +25,16 @@ import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlWindow;
import org.apache.calcite.util.Pair;
+import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
/**
* Name-resolution scope. Represents any position in a parse tree than an
@@ -60,27 +63,38 @@ public interface SqlValidatorScope {
* Looks up a node with a given name. Returns null if none is found.
*
* @param names Name of node to find, maybe partially or fully qualified
+ * @param nameMatcher Name matcher
* @param deep Whether to look more than one level deep
* @param resolved Callback wherein to write the match(es) we find
*/
- void resolve(List<String> names, boolean deep, Resolved resolved);
+ void resolve(List<String> names, SqlNameMatcher nameMatcher, boolean deep,
+ Resolved resolved);
+
+ /** @deprecated Use
+ * {@link #findQualifyingTableNames(String, SqlNode, SqlNameMatcher)} */
+ @Deprecated // to be removed before 2.0
+ Pair<String, SqlValidatorNamespace> findQualifyingTableName(String columnName,
+ SqlNode ctx);
/**
- * Finds the table alias which is implicitly qualifying an unqualified
- * column name. Throws an error if there is not exactly one table.
+ * Finds all table aliases which are implicitly qualifying an unqualified
+ * column name.
*
* <p>This method is only implemented in scopes (such as
* {@link org.apache.calcite.sql.validate.SelectScope}) which can be the
* context for name-resolution. In scopes such as
* {@link org.apache.calcite.sql.validate.IdentifierNamespace}, it throws
- * {@link UnsupportedOperationException}.</p>
+ * {@link UnsupportedOperationException}.
*
* @param columnName Column name
* @param ctx Validation context, to appear in any error thrown
- * @return Table alias and namespace
+ * @param nameMatcher Name matcher
+ *
+ * @return Map of applicable table alias and namespaces, never null, empty
+ * if no aliases found
*/
- Pair<String, SqlValidatorNamespace> findQualifyingTableName(String columnName,
- SqlNode ctx);
+ Map<String, ScopeChild> findQualifyingTableNames(String columnName,
+ SqlNode ctx, SqlNameMatcher nameMatcher);
/**
* Collects the {@link SqlMoniker}s of all possible columns in this scope.
@@ -163,34 +177,47 @@ public interface SqlValidatorScope {
*/
void validateExpr(SqlNode expr);
+ /** @deprecated Use
+ * {@link #resolveTable(List, SqlNameMatcher, Path, Resolved)}. */
+ @Deprecated // to be removed before 2.0
+ SqlValidatorNamespace getTableNamespace(List<String> names);
+
/**
- * Looks up a table in this scope from its name. If found, returns the
+ * Looks up a table in this scope from its name. If found, calls
+ * {@link Resolved#resolve(List, SqlNameMatcher, boolean, Resolved)}.
* {@link TableNamespace} that wraps it. If the "table" is defined in a
* {@code WITH} clause it may be a query, not a table after all.
*
+ * <p>The name matcher is not null, and one typically uses
+ * {@link SqlValidatorCatalogReader#nameMatcher()}.
+ *
* @param names Name of table, may be qualified or fully-qualified
- * @return Namespace of table
+ * @param nameMatcher Name matcher
+ * @param path List of names that we have traversed through so far
*/
- SqlValidatorNamespace getTableNamespace(List<String> names);
+ void resolveTable(List<String> names, SqlNameMatcher nameMatcher, Path path,
+ Resolved resolved);
/** Converts the type of an expression to nullable, if the context
* warrants it. */
RelDataType nullifyType(SqlNode node, RelDataType type);
- /** Callback from
- * {@link SqlValidatorScope#resolve(List, boolean, Resolved)}. */
+ /** Callback from {@link SqlValidatorScope#resolve}. */
interface Resolved {
void found(SqlValidatorNamespace namespace, boolean nullable,
- SqlValidatorScope scope, Path path);
+ SqlValidatorScope scope, Path path, List<String> remainingNames);
int count();
- Path emptyPath();
}
- /** A sequence of steps by which an identifier was resolved. */
+ /** A sequence of steps by which an identifier was resolved. Immutable. */
abstract class Path {
- /** Creates a path which consists of this path plus one additional step. */
- Step add(RelDataType rowType, int i, StructKind kind) {
- return new Step(this, rowType, i, kind);
+ /** The empty path. */
+ @SuppressWarnings("StaticInitializerReferencesSubClass")
+ public static final EmptyPath EMPTY = new EmptyPath();
+
+ /** Creates a path that consists of this path plus one additional step. */
+ public Step plus(RelDataType rowType, int i, String name, StructKind kind) {
+ return new Step(this, rowType, i, name, kind);
}
/** Number of steps in this path. */
@@ -205,6 +232,16 @@ public interface SqlValidatorScope {
return paths.build();
}
+ /** Returns a list ["step1", "step2"]. */
+ List<String> stepNames() {
+ return Lists.transform(steps(),
+ new Function<Step, String>() {
+ public String apply(Step input) {
+ return input.name;
+ }
+ });
+ }
+
protected void build(ImmutableList.Builder<Step> paths) {
}
}
@@ -218,12 +255,15 @@ public interface SqlValidatorScope {
final Path parent;
final RelDataType rowType;
public final int i;
+ public final String name;
final StructKind kind;
- Step(Path parent, RelDataType rowType, int i, StructKind kind) {
+ Step(Path parent, RelDataType rowType, int i, String name,
+ StructKind kind) {
this.parent = Preconditions.checkNotNull(parent);
this.rowType = rowType; // may be null
this.i = i;
+ this.name = name;
this.kind = Preconditions.checkNotNull(kind);
}
@@ -241,21 +281,17 @@ public interface SqlValidatorScope {
* {@link org.apache.calcite.sql.validate.SqlValidatorScope.Resolved}. */
class ResolvedImpl implements Resolved {
final List<Resolve> resolves = new ArrayList<>();
- private final EmptyPath emptyPath = new EmptyPath();
public void found(SqlValidatorNamespace namespace, boolean nullable,
- SqlValidatorScope scope, Path path) {
- resolves.add(new Resolve(namespace, nullable, scope, path));
+ SqlValidatorScope scope, Path path, List<String> remainingNames) {
+ resolves.add(
+ new Resolve(namespace, nullable, scope, path, remainingNames));
}
public int count() {
return resolves.size();
}
- public Path emptyPath() {
- return emptyPath;
- }
-
public Resolve only() {
return Iterables.getOnlyElement(resolves);
}
@@ -273,13 +309,17 @@ public interface SqlValidatorScope {
private final boolean nullable;
public final SqlValidatorScope scope; // may be null
public final Path path;
+ /** Names not matched; empty if it was a full match. */
+ final List<String> remainingNames;
Resolve(SqlValidatorNamespace namespace, boolean nullable,
- SqlValidatorScope scope, Path path) {
+ SqlValidatorScope scope, Path path, List<String> remainingNames) {
this.namespace = Preconditions.checkNotNull(namespace);
this.nullable = nullable;
this.scope = scope;
this.path = Preconditions.checkNotNull(path);
+ this.remainingNames = remainingNames == null ? ImmutableList.<String>of()
+ : ImmutableList.copyOf(remainingNames);
}
/** The row type of the found namespace, nullable if the lookup has
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
index c4b07f1..34149f9 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
@@ -108,14 +108,7 @@ public class SqlValidatorUtil {
return table;
}
- /**
- * Looks up a field with a given name, returning null if not found.
- *
- * @param caseSensitive Whether match is case-sensitive
- * @param rowType Row type
- * @param columnName Field name
- * @return Field, or null if not found
- */
+ @Deprecated // to be removed before 2.0
public static RelDataTypeField lookupField(boolean caseSensitive,
final RelDataType rowType, String columnName) {
return rowType.getField(columnName, caseSensitive, false);
@@ -452,7 +445,8 @@ public class SqlValidatorUtil {
RelOptTable table) {
final Table t = table == null ? null : table.unwrap(Table.class);
if (!(t instanceof CustomColumnResolvingTable)) {
- return catalogReader.field(rowType, id.getSimple());
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
+ return nameMatcher.field(rowType, id.getSimple());
}
final List<Pair<RelDataTypeField, List<String>>> entries =
@@ -478,9 +472,11 @@ public class SqlValidatorUtil {
SqlValidatorScope scope,
List<String> names) {
assert names.size() > 0;
+ final SqlNameMatcher nameMatcher =
+ scope.getValidator().getCatalogReader().nameMatcher();
final SqlValidatorScope.ResolvedImpl resolved =
new SqlValidatorScope.ResolvedImpl();
- scope.resolve(ImmutableList.of(names.get(0)), false, resolved);
+ scope.resolve(ImmutableList.of(names.get(0)), nameMatcher, false, resolved);
assert resolved.count() == 1;
SqlValidatorNamespace namespace = resolved.only().namespace;
for (String name : Util.skip(names)) {
@@ -498,15 +494,10 @@ public class SqlValidatorUtil {
List<String> subNames = Util.skipLast(names);
// Try successively with catalog.schema, catalog and no prefix
- List<String> x = catalogReader.getSchemaName();
- for (;;) {
+ for (List<String> x : catalogReader.getSchemaPaths()) {
final List<String> names2 =
ImmutableList.<String>builder().addAll(x).addAll(subNames).build();
hints.addAll(catalogReader.getAllSchemaObjectNames(names2));
- if (x.isEmpty()) {
- break;
- }
- x = Util.skipLast(x);
}
}
@@ -689,9 +680,12 @@ public class SqlValidatorUtil {
String originalRelName = expr.names.get(0);
String originalFieldName = expr.names.get(1);
+ final SqlNameMatcher nameMatcher =
+ scope.getValidator().getCatalogReader().nameMatcher();
final SqlValidatorScope.ResolvedImpl resolved =
new SqlValidatorScope.ResolvedImpl();
- scope.resolve(ImmutableList.of(originalRelName), false, resolved);
+ scope.resolve(ImmutableList.of(originalRelName), nameMatcher, false,
+ resolved);
assert resolved.count() == 1;
final SqlValidatorScope.Resolve resolve = resolved.only();
@@ -714,9 +708,7 @@ public class SqlValidatorUtil {
}
}
- RelDataTypeField field =
- scope.getValidator().getCatalogReader().field(rowType,
- originalFieldName);
+ RelDataTypeField field = nameMatcher.field(rowType, originalFieldName);
int origPos = namespaceOffset + field.getIndex();
groupExprProjection.put(origPos, ref);
@@ -835,6 +827,7 @@ public class SqlValidatorUtil {
/** Copies a list of nodes. */
public static SqlNodeList copy(SqlValidatorScope scope, SqlNodeList list) {
+ //noinspection deprecation
return (SqlNodeList) list.accept(new DeepCopier(scope));
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java b/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java
index 4d7bd65..8a6f29d 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java
@@ -54,17 +54,30 @@ class WithScope extends ListScope {
return super.getTableNamespace(names);
}
- @Override public void resolve(List<String> names, boolean deep,
- Resolved resolved) {
+ @Override public void resolveTable(List<String> names,
+ SqlNameMatcher nameMatcher, Path path, Resolved resolved) {
if (names.size() == 1
&& names.equals(withItem.name.names)) {
final SqlValidatorNamespace ns = validator.getNamespace(withItem);
- final Step path = resolved.emptyPath()
- .add(ns.getRowType(), 0, StructKind.FULLY_QUALIFIED);
- resolved.found(ns, false, null, path);
+ final Step path2 = path
+ .plus(ns.getRowType(), 0, names.get(0), StructKind.FULLY_QUALIFIED);
+ resolved.found(ns, false, null, path2, null);
return;
}
- super.resolve(names, deep, resolved);
+ super.resolveTable(names, nameMatcher, path, resolved);
+ }
+
+ @Override public void resolve(List<String> names, SqlNameMatcher nameMatcher,
+ boolean deep, Resolved resolved) {
+ if (names.size() == 1
+ && names.equals(withItem.name.names)) {
+ final SqlValidatorNamespace ns = validator.getNamespace(withItem);
+ final Step path = Path.EMPTY.plus(ns.getRowType(), 0, names.get(0),
+ StructKind.FULLY_QUALIFIED);
+ resolved.found(ns, false, null, path, null);
+ return;
+ }
+ super.resolve(names, nameMatcher, deep, resolved);
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index fb27449..857eb7c 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -142,6 +142,7 @@ import org.apache.calcite.sql.validate.ListScope;
import org.apache.calcite.sql.validate.ParameterScope;
import org.apache.calcite.sql.validate.SelectScope;
import org.apache.calcite.sql.validate.SqlMonotonicity;
+import org.apache.calcite.sql.validate.SqlNameMatcher;
import org.apache.calcite.sql.validate.SqlQualified;
import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction;
import org.apache.calcite.sql.validate.SqlUserDefinedTableMacro;
@@ -434,7 +435,7 @@ public class SqlToRelConverter {
validator.getTypeFactory().createStructType(
Pair.right(validatedFields),
SqlValidatorUtil.uniquify(Pair.left(validatedFields),
- catalogReader.isCaseSensitive()));
+ catalogReader.nameMatcher().isCaseSensitive()));
final List<RelDataTypeField> convertedFields =
result.getRowType().getFieldList().subList(0, validatedFields.size());
@@ -2202,10 +2203,12 @@ public class SqlToRelConverter {
String originalRelName = lookup.getOriginalRelName();
String originalFieldName = fieldAccess.getField().getName();
- SqlValidatorScope.ResolvedImpl resolved =
+ final SqlNameMatcher nameMatcher =
+ lookup.bb.scope.getValidator().getCatalogReader().nameMatcher();
+ final SqlValidatorScope.ResolvedImpl resolved =
new SqlValidatorScope.ResolvedImpl();
- lookup.bb.scope.resolve(ImmutableList.of(originalRelName), false,
- resolved);
+ lookup.bb.scope.resolve(ImmutableList.of(originalRelName),
+ nameMatcher, false, resolved);
assert resolved.count() == 1;
final SqlValidatorScope.Resolve resolve = resolved.only();
final SqlValidatorNamespace foundNs = resolve.namespace;
@@ -2308,10 +2311,12 @@ public class SqlToRelConverter {
DeferredLookup lookup = mapCorrelToDeferred.get(correlName);
String originalRelName = lookup.getOriginalRelName();
+ final SqlNameMatcher nameMatcher =
+ lookup.bb.scope.getValidator().getCatalogReader().nameMatcher();
final SqlValidatorScope.ResolvedImpl resolved =
new SqlValidatorScope.ResolvedImpl();
- lookup.bb.scope.resolve(ImmutableList.of(originalRelName), false,
- resolved);
+ lookup.bb.scope.resolve(ImmutableList.of(originalRelName), nameMatcher,
+ false, resolved);
SqlValidatorScope ancestorScope = resolved.only().scope;
@@ -2385,6 +2390,7 @@ public class SqlToRelConverter {
private RexNode convertUsing(SqlValidatorNamespace leftNamespace,
SqlValidatorNamespace rightNamespace,
List<String> nameList) {
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
final List<RexNode> list = Lists.newArrayList();
for (String name : nameList) {
List<RexNode> operands = new ArrayList<>();
@@ -2392,7 +2398,7 @@ public class SqlToRelConverter {
for (SqlValidatorNamespace n : ImmutableList.of(leftNamespace,
rightNamespace)) {
final RelDataType rowType = n.getRowType();
- final RelDataTypeField field = catalogReader.field(rowType, name);
+ final RelDataTypeField field = nameMatcher.field(rowType, name);
operands.add(
rexBuilder.makeInputRef(field.getType(),
offset + field.getIndex()));
@@ -3064,8 +3070,9 @@ public class SqlToRelConverter {
// expression list according to the ordinal value returned from
// the table construct, leaving nulls in the list for columns
// that are not referenced.
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
for (Pair<String, RexNode> p : Pair.zip(targetColumnNames, columnExprs)) {
- RelDataTypeField field = catalogReader.field(targetRowType, p.left);
+ RelDataTypeField field = nameMatcher.field(targetRowType, p.left);
assert field != null : "column " + p.left + " not found";
sourceExps.set(field.getIndex(), p.right);
}
@@ -3505,7 +3512,8 @@ public class SqlToRelConverter {
fieldNames.add(deriveAlias(expr, aliases, i));
}
- fieldNames = SqlValidatorUtil.uniquify(fieldNames, catalogReader.isCaseSensitive());
+ fieldNames = SqlValidatorUtil.uniquify(fieldNames,
+ catalogReader.nameMatcher().isCaseSensitive());
bb.setRoot(
RelOptUtil.createProject(bb.root, exprs, fieldNames),
@@ -3902,9 +3910,11 @@ public class SqlToRelConverter {
}
return Pair.of(node, null);
}
+ final SqlNameMatcher nameMatcher =
+ scope.getValidator().getCatalogReader().nameMatcher();
final SqlValidatorScope.ResolvedImpl resolved =
new SqlValidatorScope.ResolvedImpl();
- scope.resolve(qualified.prefix(), false, resolved);
+ scope.resolve(qualified.prefix(), nameMatcher, false, resolved);
if (!(resolved.count() == 1)) {
return null;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/util/Util.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/Util.java b/core/src/main/java/org/apache/calcite/util/Util.java
index 4117a8d..fee522d 100644
--- a/core/src/main/java/org/apache/calcite/util/Util.java
+++ b/core/src/main/java/org/apache/calcite/util/Util.java
@@ -2358,6 +2358,29 @@ public class Util {
return "".equals(v) || "true".equalsIgnoreCase(v);
}
+ /** Returns a copy of a list of lists, making the component lists immutable if
+ * they are not already. */
+ public static <E> List<List<E>>
+ immutableCopy(Iterable<? extends Iterable<E>> lists) {
+ int n = 0;
+ for (Iterable<E> list : lists) {
+ if (!(list instanceof ImmutableList)) {
+ ++n;
+ }
+ }
+ if (n == 0) {
+ // Lists are already immutable. Furthermore, if the outer list is
+ // immutable we will just return "lists" unchanged.
+ return ImmutableList.copyOf((Iterable) lists);
+ }
+ final ImmutableList.Builder<List<E>> builder =
+ ImmutableList.builder();
+ for (Iterable<E> list : lists) {
+ builder.add(ImmutableList.copyOf(list));
+ }
+ return builder.build();
+ }
+
//~ Inner Classes ----------------------------------------------------------
/**
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
----------------------------------------------------------------------
diff --git a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index 5a278b6..7be1266 100644
--- a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -57,9 +57,17 @@ DuplicateTargetColumn=Target column ''{0}'' is assigned more than once
UnmatchInsertColumn=Number of INSERT target columns ({0,number}) does not equal number of source items ({1,number})
TypeNotAssignable=Cannot assign to target field ''{0}'' of type {1} from source field ''{2}'' of type {3}
TableNameNotFound=Table ''{0}'' not found
+TableNotFound=Table ''{0}'' not found
+TableNameNotFoundDidYouMean=Table ''{0}'' not found; did you mean ''{1}''?
+ObjectNotFound=Object ''{0}'' not found
+ObjectNotFoundWithin=Object ''{0}'' not found within ''{1}''
+ObjectNotFoundDidYouMean=Object ''{0}'' not found; did you mean ''{1}''?
+ObjectNotFoundWithinDidYouMean=Object ''{0}'' not found within ''{1}''; did you mean ''{2}''?
NotASequence=Table ''{0}'' is not a sequence
ColumnNotFound=Column ''{0}'' not found in any table
+ColumnNotFoundDidYouMean=Column ''{0}'' not found in any table; did you mean ''{1}''?
ColumnNotFoundInTable=Column ''{0}'' not found in table ''{1}''
+ColumnNotFoundInTableDidYouMean=Column ''{0}'' not found in table ''{1}''; did you mean ''{2}''?
ColumnAmbiguous=Column ''{0}'' is ambiguous
NeedQueryOp=Operand {0} must be a query
NeedSameTypeParameter=Parameters must be of the same type
@@ -199,7 +207,6 @@ CannotStreamValues=Cannot stream VALUES
ModifiableViewMustBeBasedOnSingleTable=Modifiable view must be based on a single table
MoreThanOneMappedColumn=View is not modifiable. More than one expression maps to column ''{0}'' of base table ''{1}''
NoValueSuppliedForViewColumn=View is not modifiable. No value is supplied for NOT NULL column ''{0}'' of base table ''{1}''
-TableNotFound=Table ''{0}'' not found
StarRequiresRecordType=Not a record type. The ''*'' operator requires a record
FilterMustBeBoolean=FILTER expression must be of type BOOLEAN
CannotStreamResultsForNonStreamingInputs=Cannot stream results of a query with no streaming inputs: ''{0}''. At least one input should be convertible to a stream
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/prepare/LookupOperatorOverloadsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/prepare/LookupOperatorOverloadsTest.java b/core/src/test/java/org/apache/calcite/prepare/LookupOperatorOverloadsTest.java
index e760cd0..ddc997d 100644
--- a/core/src/test/java/org/apache/calcite/prepare/LookupOperatorOverloadsTest.java
+++ b/core/src/test/java/org/apache/calcite/prepare/LookupOperatorOverloadsTest.java
@@ -32,6 +32,7 @@ import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction;
import org.apache.calcite.util.Smalls;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.junit.Test;
@@ -141,8 +142,8 @@ public class LookupOperatorOverloadsTest {
statement.createPrepareContext();
final JavaTypeFactory typeFactory = prepareContext.getTypeFactory();
CalciteCatalogReader reader =
- new CalciteCatalogReader(prepareContext.getRootSchema(), false, null,
- typeFactory);
+ new CalciteCatalogReader(prepareContext.getRootSchema(), false,
+ ImmutableList.<String>of(), typeFactory);
final List<SqlOperator> operatorList = new ArrayList<>();
SqlIdentifier myFuncIdentifier =
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/sql/test/DefaultSqlTestFactory.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/DefaultSqlTestFactory.java b/core/src/test/java/org/apache/calcite/sql/test/DefaultSqlTestFactory.java
index be651ff..63c1cdd 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/DefaultSqlTestFactory.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/DefaultSqlTestFactory.java
@@ -16,15 +16,15 @@
*/
package org.apache.calcite.sql.test;
+import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.avatica.util.Casing;
import org.apache.calcite.avatica.util.Quoting;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.sql.SqlOperatorTable;
import org.apache.calcite.sql.advise.SqlAdvisor;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParser;
-import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
import org.apache.calcite.sql.validate.SqlConformance;
import org.apache.calcite.sql.validate.SqlConformanceEnum;
import org.apache.calcite.sql.validate.SqlValidator;
@@ -90,8 +90,8 @@ public class DefaultSqlTestFactory implements SqlTestFactory {
final boolean caseSensitive = (Boolean) factory.get("caseSensitive");
final SqlConformance conformance =
(SqlConformance) factory.get("conformance");
- final RelDataTypeFactory typeFactory =
- new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+ final JavaTypeFactory typeFactory =
+ new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
return SqlValidatorUtil.newValidator(operatorTable,
new MockCatalogReader(typeFactory, caseSensitive).init(),
typeFactory, conformance);
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
index 326a460..24a4626 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
@@ -37,10 +37,12 @@ import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.TreeSet;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -65,6 +67,7 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
protected static final List<String> SALES_TABLES =
Arrays.asList(
+ "SCHEMA(CATALOG.SALES)",
"TABLE(CATALOG.SALES.EMP)",
"TABLE(CATALOG.SALES.EMP_B)",
"TABLE(CATALOG.SALES.EMP_20)",
@@ -359,8 +362,8 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
String sql,
List<String>... expectedLists) throws Exception {
List<String> expectedList = plus(expectedLists);
- Collections.sort(expectedList);
- assertHint(sql, toString(expectedList));
+ final String expected = toString(new TreeSet<>(expectedList));
+ assertHint(sql, expected);
}
/**
@@ -410,8 +413,7 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
String sql,
List<String>... expectedResults) {
List<String> expectedList = plus(expectedResults);
- Collections.sort(expectedList);
- String expected = toString(expectedList);
+ String expected = toString(new TreeSet<>(expectedList));
assertComplete(sql, expected, null);
}
@@ -480,7 +482,7 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
* @param list List
* @return String with one item of the list per line
*/
- private static <T> String toString(List<T> list) {
+ private static <T> String toString(Collection<T> list) {
StringBuilder buf = new StringBuilder();
for (T t : list) {
buf.append(t).append("\n");
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/test/CollectionTypeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CollectionTypeTest.java b/core/src/test/java/org/apache/calcite/test/CollectionTypeTest.java
index 2a2c52b..23d51db 100644
--- a/core/src/test/java/org/apache/calcite/test/CollectionTypeTest.java
+++ b/core/src/test/java/org/apache/calcite/test/CollectionTypeTest.java
@@ -387,6 +387,7 @@ public class CollectionTypeTest {
.build();
}
+
public Statistic getStatistic() {
return Statistics.UNKNOWN;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/test/ExceptionMessageTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/ExceptionMessageTest.java b/core/src/test/java/org/apache/calcite/test/ExceptionMessageTest.java
index 59a3bfa..a2ec482 100644
--- a/core/src/test/java/org/apache/calcite/test/ExceptionMessageTest.java
+++ b/core/src/test/java/org/apache/calcite/test/ExceptionMessageTest.java
@@ -141,7 +141,7 @@ public class ExceptionMessageTest {
fail("Query should fail");
} catch (SQLException e) {
assertThat(e.getMessage(),
- containsString("Table 'nonexistentTable' not found"));
+ containsString("Object 'nonexistentTable' not found"));
}
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 126df14..25d3dcf 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -5192,7 +5192,7 @@ public class JdbcTest {
+ "empid=150; deptno=10; name=Sebastian; salary=7000.0; commission=null\n"
+ "empid=110; deptno=10; name=Theodore; salary=11500.0; commission=250\n");
that.query("select * from \"adhoc\".EMPLOYEES")
- .throws_("Table 'adhoc.EMPLOYEES' not found");
+ .throws_("Object 'EMPLOYEES' not found within 'adhoc'");
}
/** Test case for
@@ -6113,7 +6113,15 @@ public class JdbcTest {
final CalciteAssert.AssertThat with2 =
CalciteAssert.that().with(Lex.JAVA);
with2.query("select COUNT(*) as c from `metaData`.`tAbles`")
- .throws_("Table 'metaData.tAbles' not found");
+ .throws_("Object 'metaData' not found; did you mean 'metadata'?");
+ with2.query("select COUNT(*) as c from `metaData`.`TABLES`")
+ .throws_("Object 'metaData' not found; did you mean 'metadata'?");
+ with2.query("select COUNT(*) as c from `metaData`.`tables`")
+ .throws_("Object 'metaData' not found; did you mean 'metadata'?");
+ with2.query("select COUNT(*) as c from `metaData`.`nonExistent`")
+ .throws_("Object 'metaData' not found; did you mean 'metadata'?");
+ with2.query("select COUNT(*) as c from `metadata`.`tAbles`")
+ .throws_("Object 'tAbles' not found within 'metadata'; did you mean 'TABLES'?");
}
/** Test case for
@@ -6126,7 +6134,7 @@ public class JdbcTest {
// With [CALCITE-1563], the following query succeeded; it queried
// metadata.tables.
with.query("select COUNT(*) as c from `metaData`.`zoo`")
- .throws_("Table 'metaData.zoo' not found");
+ .throws_("Object 'zoo' not found within 'metadata'");
with.query("select COUNT(*) as c from `metaData`.`tAbLes`")
.returns("c=2\n");
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/test/LatticeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/LatticeTest.java b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
index be87b1c..54f4ea7 100644
--- a/core/src/test/java/org/apache/calcite/test/LatticeTest.java
+++ b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
@@ -172,7 +172,7 @@ public class LatticeTest {
@Test public void testLatticeInvalidSqlFails() {
modelWithLattice("star", "select foo from nonexistent")
.connectThrows("Error instantiating JsonLattice(name=star, ")
- .connectThrows("Table 'NONEXISTENT' not found");
+ .connectThrows("Object 'NONEXISTENT' not found");
}
/** Tests a lattice whose SQL is invalid because it contains a GROUP BY. */
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
index a21218b..18ce756 100644
--- a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
+++ b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
@@ -16,13 +16,14 @@
*/
package org.apache.calcite.test;
+import org.apache.calcite.jdbc.CalciteSchema;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.linq4j.QueryProvider;
import org.apache.calcite.linq4j.Queryable;
import org.apache.calcite.linq4j.tree.Expression;
-import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelOptSchema;
import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.prepare.CalciteCatalogReader;
import org.apache.calcite.prepare.Prepare;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelCollations;
@@ -54,25 +55,24 @@ import org.apache.calcite.schema.Path;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.Schemas;
+import org.apache.calcite.schema.Statistic;
+import org.apache.calcite.schema.StreamableTable;
import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.Wrapper;
+import org.apache.calcite.schema.impl.AbstractSchema;
import org.apache.calcite.sql.SqlAccessType;
import org.apache.calcite.sql.SqlCollation;
-import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlIntervalQualifier;
-import org.apache.calcite.sql.SqlOperator;
-import org.apache.calcite.sql.SqlSyntax;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.ObjectSqlType;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlModality;
-import org.apache.calcite.sql.validate.SqlMoniker;
-import org.apache.calcite.sql.validate.SqlMonikerImpl;
-import org.apache.calcite.sql.validate.SqlMonikerType;
import org.apache.calcite.sql.validate.SqlMonotonicity;
+import org.apache.calcite.sql.validate.SqlNameMatcher;
+import org.apache.calcite.sql.validate.SqlNameMatchers;
import org.apache.calcite.sql.validate.SqlValidatorCatalogReader;
-import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.calcite.util.Litmus;
@@ -82,7 +82,6 @@ import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import java.lang.reflect.Type;
@@ -105,22 +104,15 @@ import java.util.Set;
* Also two streams "ORDERS", "SHIPMENTS";
* and a view "EMP_20".
*/
-public class MockCatalogReader implements Prepare.CatalogReader {
+public class MockCatalogReader extends CalciteCatalogReader {
//~ Static fields/initializers ---------------------------------------------
- protected static final String DEFAULT_CATALOG = "CATALOG";
- protected static final String DEFAULT_SCHEMA = "SALES";
-
- public static final Ordering<Iterable<String>>
- CASE_INSENSITIVE_LIST_COMPARATOR =
- Ordering.from(String.CASE_INSENSITIVE_ORDER).lexicographical();
+ static final String DEFAULT_CATALOG = "CATALOG";
+ static final String DEFAULT_SCHEMA = "SALES";
+ static final List<String> PREFIX = ImmutableList.of(DEFAULT_SCHEMA);
//~ Instance fields --------------------------------------------------------
- protected final RelDataTypeFactory typeFactory;
- private final boolean caseSensitive;
- private final Map<List<String>, MockTable> tables;
- protected final Map<String, MockSchema> schemas;
private RelDataType addressType;
//~ Constructors -----------------------------------------------------------
@@ -134,19 +126,18 @@ public class MockCatalogReader implements Prepare.CatalogReader {
*/
public MockCatalogReader(RelDataTypeFactory typeFactory,
boolean caseSensitive) {
- this.typeFactory = typeFactory;
- this.caseSensitive = caseSensitive;
- if (caseSensitive) {
- tables = Maps.newHashMap();
- schemas = Maps.newHashMap();
- } else {
- tables = Maps.newTreeMap(CASE_INSENSITIVE_LIST_COMPARATOR);
- schemas = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
- }
+ super(CalciteSchema.createRootSchema(false, true, DEFAULT_CATALOG),
+ SqlNameMatchers.withCaseSensitive(caseSensitive),
+ ImmutableList.of(PREFIX, ImmutableList.<String>of()),
+ typeFactory);
}
@Override public boolean isCaseSensitive() {
- return caseSensitive;
+ return nameMatcher.isCaseSensitive();
+ }
+
+ public SqlNameMatcher nameMatcher() {
+ return nameMatcher;
}
/**
@@ -449,55 +440,26 @@ public class MockCatalogReader implements Prepare.CatalogReader {
//~ Methods ----------------------------------------------------------------
- public void lookupOperatorOverloads(SqlIdentifier opName,
- SqlFunctionCategory category, SqlSyntax syntax,
- List<SqlOperator> operatorList) {
- }
-
- public List<SqlOperator> getOperatorList() {
- return ImmutableList.of();
- }
-
- public Prepare.CatalogReader withSchemaPath(List<String> schemaPath) {
- return this;
- }
-
- public Prepare.PreparingTable getTableForMember(List<String> names) {
- return getTable(names);
- }
-
- public RelDataTypeFactory getTypeFactory() {
- return typeFactory;
- }
-
- public void registerRules(RelOptPlanner planner) {
- }
-
- protected void registerTable(MockTable table) {
+ protected void registerTable(final MockTable table) {
table.onRegister(typeFactory);
- tables.put(table.getQualifiedName(), table);
+ assert table.names.get(0).equals(DEFAULT_CATALOG);
+ final CalciteSchema schema =
+ rootSchema.getSubSchema(table.names.get(1), true);
+ final WrapperTable wrapperTable = new WrapperTable(table);
+ if (table.stream) {
+ schema.add(table.names.get(2),
+ new StreamableWrapperTable(table) {
+ public Table stream() {
+ return wrapperTable;
+ }
+ });
+ } else {
+ schema.add(table.names.get(2), wrapperTable);
+ }
}
protected void registerSchema(MockSchema schema) {
- schemas.put(schema.name, schema);
- }
-
- public Prepare.PreparingTable getTable(final List<String> names) {
- switch (names.size()) {
- case 1:
- // assume table in SALES schema (the original default)
- // if it's not supplied, because SqlValidatorTest is effectively
- // using SALES as its default schema.
- return tables.get(
- ImmutableList.of(DEFAULT_CATALOG, DEFAULT_SCHEMA, names.get(0)));
- case 2:
- return tables.get(
- ImmutableList.of(DEFAULT_CATALOG, names.get(0), names.get(1)));
- case 3:
- return tables.get(names);
- default:
- return null;
- }
+ rootSchema.add(schema.name, new AbstractSchema());
}
public RelDataType getNamedType(SqlIdentifier typeName) {
@@ -508,65 +470,6 @@ public class MockCatalogReader implements Prepare.CatalogReader {
}
}
- public List<SqlMoniker> getAllSchemaObjectNames(List<String> names) {
- List<SqlMoniker> result;
- switch (names.size()) {
- case 0:
- // looking for catalog and schema names
- return ImmutableList.<SqlMoniker>builder()
- .add(new SqlMonikerImpl(DEFAULT_CATALOG, SqlMonikerType.CATALOG))
- .addAll(getAllSchemaObjectNames(ImmutableList.of(DEFAULT_CATALOG)))
- .build();
- case 1:
- // looking for schema names
- result = Lists.newArrayList();
- for (MockSchema schema : schemas.values()) {
- final String catalogName = names.get(0);
- if (schema.getCatalogName().equals(catalogName)) {
- final ImmutableList<String> names1 =
- ImmutableList.of(catalogName, schema.name);
- result.add(new SqlMonikerImpl(names1, SqlMonikerType.SCHEMA));
- }
- }
- return result;
- case 2:
- // looking for table names in the given schema
- MockSchema schema = schemas.get(names.get(1));
- if (schema == null) {
- return Collections.emptyList();
- }
- result = Lists.newArrayList();
- for (String tableName : schema.tableNames) {
- result.add(
- new SqlMonikerImpl(
- ImmutableList.of(schema.getCatalogName(), schema.name,
- tableName),
- SqlMonikerType.TABLE));
- }
- return result;
- default:
- return Collections.emptyList();
- }
- }
-
- public List<String> getSchemaName() {
- return ImmutableList.of(DEFAULT_CATALOG, DEFAULT_SCHEMA);
- }
-
- public RelDataTypeField field(RelDataType rowType, String alias) {
- return SqlValidatorUtil.lookupField(caseSensitive, rowType, alias);
- }
-
- public boolean matches(String string, String name) {
- return Util.matches(caseSensitive, string, name);
- }
-
- public RelDataType createTypeFromProjection(final RelDataType type,
- final List<String> columnNameList) {
- return SqlValidatorUtil.createTypeFromProjection(type, columnNameList,
- typeFactory, caseSensitive);
- }
-
private static List<RelCollation> deduceMonotonicity(
Prepare.PreparingTable table) {
final List<RelCollation> collationList = Lists.newArrayList();
@@ -1197,6 +1100,63 @@ public class MockCatalogReader implements Prepare.CatalogReader {
});
}
}
+
+ /** Wrapper around a {@link MockTable}, giving it a {@link Table} interface.
+ * You can get the {@code MockTable} by calling {@link #unwrap(Class)}. */
+ private static class WrapperTable implements Table, Wrapper {
+ private final MockTable table;
+
+ WrapperTable(MockTable table) {
+ this.table = table;
+ }
+
+ public <C> C unwrap(Class<C> aClass) {
+ return aClass.isInstance(this) ? aClass.cast(this)
+ : aClass.isInstance(table) ? aClass.cast(table)
+ : null;
+ }
+
+ public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+ return table.getRowType();
+ }
+
+ public Statistic getStatistic() {
+ return new Statistic() {
+ public Double getRowCount() {
+ return table.rowCount;
+ }
+
+ public boolean isKey(ImmutableBitSet columns) {
+ return table.isKey(columns);
+ }
+
+ public List<RelCollation> getCollations() {
+ return table.collationList;
+ }
+
+ public RelDistribution getDistribution() {
+ return table.getDistribution();
+ }
+ };
+ }
+
+ public Schema.TableType getJdbcTableType() {
+ return table.stream ? Schema.TableType.STREAM : Schema.TableType.TABLE;
+ }
+ }
+
+ /** Wrapper around a {@link MockTable}, giving it a {@link StreamableTable}
+ * interface. */
+ private static class StreamableWrapperTable extends WrapperTable
+ implements StreamableTable {
+ StreamableWrapperTable(MockTable table) {
+ super(table);
+ }
+
+ public Table stream() {
+ return this;
+ }
+ }
}
// End MockCatalogReader.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/test/MultiJdbcSchemaJoinTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/MultiJdbcSchemaJoinTest.java b/core/src/test/java/org/apache/calcite/test/MultiJdbcSchemaJoinTest.java
index bf2ad40..80ee5b1 100644
--- a/core/src/test/java/org/apache/calcite/test/MultiJdbcSchemaJoinTest.java
+++ b/core/src/test/java/org/apache/calcite/test/MultiJdbcSchemaJoinTest.java
@@ -196,7 +196,7 @@ public class MultiJdbcSchemaJoinTest {
fail("expected error, got " + rs);
} catch (SQLException e) {
assertThat(e.getCause().getCause().getMessage(),
- equalTo("Table 'DB.TABLE2' not found"));
+ equalTo("Object 'TABLE2' not found within 'DB'"));
}
stmt1.execute("create table table2(id varchar(10) not null primary key, "
@@ -209,7 +209,7 @@ public class MultiJdbcSchemaJoinTest {
fail("expected error, got " + rs);
} catch (SQLException e) {
assertThat(e.getCause().getCause().getMessage(),
- equalTo("Table 'DB.TABLE2' not found"));
+ equalTo("Object 'TABLE2' not found within 'DB'"));
}
// disable caching and table becomes visible
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java b/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
index da6a0f5..606f1d6 100644
--- a/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
+++ b/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
@@ -621,7 +621,7 @@ public class ReflectiveSchemaTest {
// BitSet is not a valid relation type. It's as if "bitSet" field does
// not exist.
with.query("select * from \"s\".\"bitSet\"")
- .throws_("Table 's.bitSet' not found");
+ .throws_("Object 'bitSet' not found within 's'");
// Enumerable field returns 3 records with 0 fields
with.query("select * from \"s\".\"enumerable\"")
.returns("\n"
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java b/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
index 3cfbf35..7520b4a 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
@@ -254,7 +254,8 @@ public abstract class SqlToRelTestBase {
}
public RelOptTable getTableForMember(List<String> names) {
- final SqlValidatorTable table = catalogReader.getTable(names);
+ final SqlValidatorTable table =
+ catalogReader.getTable(names);
final RelDataType rowType = table.getRowType();
final List<RelCollation> collationList = deduceMonotonicity(table);
if (names.size() < 3) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 02d242b..898ce80 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -4661,7 +4661,11 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
*/
@Test public void testStarInFromFails() {
sql("select emp.empno AS x from ^sales.*^")
- .fails("Table 'SALES.\\*' not found");
+ .fails("Object '\\*' not found within 'SALES'");
+ sql("select * from ^emp.*^")
+ .fails("Object '\\*' not found within 'SALES.EMP'");
+ sql("select emp.empno AS x from ^emp.*^")
+ .fails("Object '\\*' not found within 'SALES.EMP'");
sql("select emp.empno from emp where emp.^*^ is not null")
.fails("Unknown field '\\*'");
}
@@ -5552,14 +5556,14 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
"with emp3 as (select * from ^emp2^),\n"
+ " emp2 as (select * from emp)\n"
+ "select * from emp3",
- "Table 'EMP2' not found");
+ "Object 'EMP2' not found");
// forward reference in with-item not used; should still fail
checkFails(
"with emp3 as (select * from ^emp2^),\n"
+ " emp2 as (select * from emp)\n"
+ "select * from emp2",
- "Table 'EMP2' not found");
+ "Object 'EMP2' not found");
// table not used is ok
checkResultType(
@@ -5572,11 +5576,11 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
checkFails("with emp2 as (select * from emp),\n"
+ " emp3 as (select * from ^emp3^)\n"
+ "values (1)",
- "Table 'EMP3' not found");
+ "Object 'EMP3' not found");
// self-reference not ok
checkFails("with emp2 as (select * from ^emp2^)\n"
- + "select * from emp2 where false", "Table 'EMP2' not found");
+ + "select * from emp2 where false", "Object 'EMP2' not found");
// refer to 2 previous tables, not just immediately preceding
checkResultType("with emp2 as (select * from emp),\n"
@@ -7097,7 +7101,10 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
+ " BOOLEAN NOT NULL SLACKER) NOT NULL";
checkResultType("select * from (table emp)", empRecordType);
checkResultType("table emp", empRecordType);
- checkFails("table ^nonexistent^", "Table 'NONEXISTENT' not found");
+ checkFails("table ^nonexistent^", "Object 'NONEXISTENT' not found");
+ checkFails("table ^sales.nonexistent^",
+ "Object 'NONEXISTENT' not found within 'SALES'");
+ checkFails("table ^nonexistent.foo^", "Object 'NONEXISTENT' not found");
}
@Test public void testCollectionTable() {
@@ -7162,7 +7169,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
"RecordType(VARCHAR(1024) NOT NULL NAME) NOT NULL");
checkFails(
"select * from table(dedup(cursor(select * from ^bloop^),'ename'))",
- "Table 'BLOOP' not found");
+ "Object 'BLOOP' not found");
}
@Test public void testScalarSubQuery() {
@@ -7483,11 +7490,16 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
tester1.checkQueryFails(
"select ^e^.EMPNO from [EMP] as [e]",
- "Table 'E' not found");
+ "Table 'E' not found; did you mean 'e'\\?");
tester1.checkQueryFails(
"select ^x^ from (\n"
+ " select [e].EMPNO as [x] from [EMP] as [e])",
+ "Column 'X' not found in any table; did you mean 'x'\\?");
+
+ tester1.checkQueryFails(
+ "select ^x^ from (\n"
+ + " select [e].EMPNO as [x ] from [EMP] as [e])",
"Column 'X' not found in any table");
tester1.checkQueryFails("select EMP.^\"x\"^ from EMP",
@@ -7506,15 +7518,20 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
tester1.checkQueryFails(
"select ^e^.EMPNO from EMP as E",
- "Table 'e' not found");
+ "Table 'e' not found; did you mean 'E'\\?");
tester1.checkQueryFails(
"select ^E^.EMPNO from EMP as e",
- "Table 'E' not found");
+ "Table 'E' not found; did you mean 'e'\\?");
tester1.checkQueryFails(
"select ^x^ from (\n"
+ " select e.EMPNO as X from EMP as e)",
+ "Column 'x' not found in any table; did you mean 'X'\\?");
+
+ tester1.checkQueryFails(
+ "select ^x^ from (\n"
+ + " select e.EMPNO as Xx from EMP as e)",
"Column 'x' not found in any table");
// double-quotes are not valid in this lexical convention
@@ -7546,15 +7563,15 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
"RecordType(INTEGER NOT NULL path, INTEGER NOT NULL x) NOT NULL");
tester1.checkFails(
"select ^PATH^ from (select 1 as path from (values (true)))",
- "Column 'PATH' not found in any table",
+ "Column 'PATH' not found in any table; did you mean 'path'\\?",
false);
tester1.checkFails(
"select t.^PATH^ from (select 1 as path from (values (true))) as t",
- "Column 'PATH' not found in table 't'",
+ "Column 'PATH' not found in table 't'; did you mean 'path'\\?",
false);
tester1.checkQueryFails(
"select t.x, t.^PATH^ from (values (true, 1)) as t(path, x)",
- "Column 'PATH' not found in table 't'");
+ "Column 'PATH' not found in table 't'; did you mean 'path'\\?");
// Built-in functions can be written in any case, even those with no args,
// and regardless of spaces between function name and open parenthesis.
@@ -7598,7 +7615,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
tester2.checkQueryFails(
"select * from emp as [e] where exists (\n"
+ "select 1 from dept where dept.deptno = ^[E]^.deptno)",
- "(?s).*Table 'E' not found");
+ "(?s).*Table 'E' not found; did you mean 'e'\\?");
checkFails("select count(1), ^empno^ from emp",
"Expression 'EMPNO' is not being grouped");
@@ -7643,6 +7660,91 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
tester1.checkQuery("select deptno, count(*) from EMP group by DEPTNO");
}
+ /** Test case for
+ * <a href="https://issues.apache.org/jira/browse/CALCITE-1549">[CALCITE-1549]
+ * Improve error message when table or column not found</a>. */
+ @Test public void testTableNotFoundDidYouMean() {
+ // No table in default schema
+ tester.checkQueryFails("select * from ^unknownTable^",
+ "Object 'UNKNOWNTABLE' not found");
+
+ // Similar table exists in default schema
+ tester.checkQueryFails("select * from ^\"Emp\"^",
+ "Object 'Emp' not found within 'SALES'; did you mean 'EMP'\\?");
+
+ // Schema correct, but no table in specified schema
+ tester.checkQueryFails("select * from ^sales.unknownTable^",
+ "Object 'UNKNOWNTABLE' not found within 'SALES'");
+ // Similar table exists in specified schema
+ tester.checkQueryFails("select * from ^sales.\"Emp\"^",
+ "Object 'Emp' not found within 'SALES'; did you mean 'EMP'\\?");
+
+ // No schema found
+ tester.checkQueryFails("select * from ^unknownSchema.unknownTable^",
+ "Object 'UNKNOWNSCHEMA' not found");
+ // Similar schema found
+ tester.checkQueryFails("select * from ^\"sales\".emp^",
+ "Object 'sales' not found; did you mean 'SALES'\\?");
+ tester.checkQueryFails("select * from ^\"saLes\".\"eMp\"^",
+ "Object 'saLes' not found; did you mean 'SALES'\\?");
+
+ // Spurious after table
+ tester.checkQueryFails("select * from ^emp.foo^",
+ "Object 'FOO' not found within 'SALES\\.EMP'");
+ tester.checkQueryFails("select * from ^sales.emp.foo^",
+ "Object 'FOO' not found within 'SALES\\.EMP'");
+
+ // Alias not found
+ tester.checkQueryFails("select ^aliAs^.\"name\"\n"
+ + "from sales.emp as \"Alias\"",
+ "Table 'ALIAS' not found; did you mean 'Alias'\\?");
+ // Alias not found, fully-qualified
+ tester.checkQueryFails("select ^sales.\"emp\"^.\"name\" from sales.emp",
+ "Table 'SALES\\.emp' not found; did you mean 'EMP'\\?");
+ }
+
+ @Test public void testColumnNotFoundDidYouMean() {
+ // Column not found
+ tester.checkQueryFails("select ^\"unknownColumn\"^ from emp",
+ "Column 'unknownColumn' not found in any table");
+ // Similar column in table, unqualified table name
+ tester.checkQueryFails("select ^\"empNo\"^ from emp",
+ "Column 'empNo' not found in any table; did you mean 'EMPNO'\\?");
+ // Similar column in table, table name qualified with schema
+ tester.checkQueryFails("select ^\"empNo\"^ from sales.emp",
+ "Column 'empNo' not found in any table; did you mean 'EMPNO'\\?");
+ // Similar column in table, table name qualified with catalog and schema
+ tester.checkQueryFails("select ^\"empNo\"^ from catalog.sales.emp",
+ "Column 'empNo' not found in any table; did you mean 'EMPNO'\\?");
+ // With table alias
+ tester.checkQueryFails("select e.^\"empNo\"^ from catalog.sales.emp as e",
+ "Column 'empNo' not found in table 'E'; did you mean 'EMPNO'\\?");
+ // With fully-qualified table alias
+ tester.checkQueryFails("select catalog.sales.emp.^\"empNo\"^\n"
+ + "from catalog.sales.emp",
+ "Column 'empNo' not found in table 'CATALOG\\.SALES\\.EMP'; "
+ + "did you mean 'EMPNO'\\?");
+ // Similar column in table; multiple tables
+ tester.checkQueryFails("select ^\"name\"^ from emp, dept",
+ "Column 'name' not found in any table; did you mean 'NAME'\\?");
+ // Similar column in table; table and a query
+ tester.checkQueryFails("select ^\"name\"^ from emp,\n"
+ + " (select * from dept) as d",
+ "Column 'name' not found in any table; did you mean 'NAME'\\?");
+ // Similar column in table; table and an un-aliased query
+ tester.checkQueryFails("select ^\"name\"^ from emp, (select * from dept)",
+ "Column 'name' not found in any table; did you mean 'NAME'\\?");
+ // Similar column in table, multiple tables
+ tester.checkQueryFails("select ^\"deptno\"^ from emp,\n"
+ + " (select deptno as \"deptNo\" from dept)",
+ "Column 'deptno' not found in any table; "
+ + "did you mean 'DEPTNO', 'deptNo'\\?");
+ tester.checkQueryFails("select ^\"deptno\"^ from emp,\n"
+ + " (select * from dept) as t(\"deptNo\", name)",
+ "Column 'deptno' not found in any table; "
+ + "did you mean 'DEPTNO', 'deptNo'\\?");
+ }
+
/** Tests matching of built-in operator names. */
@Test public void testUnquotedBuiltInFunctionNames() {
final SqlTester mysql = tester
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/java/org/apache/calcite/util/UtilTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/util/UtilTest.java b/core/src/test/java/org/apache/calcite/util/UtilTest.java
index 4b970ce..42f06c6 100644
--- a/core/src/test/java/org/apache/calcite/util/UtilTest.java
+++ b/core/src/test/java/org/apache/calcite/util/UtilTest.java
@@ -1285,7 +1285,8 @@ public class UtilTest {
assertFalse(Util.isDistinct(Arrays.asList("a", null, "b", null)));
}
- /** Unit test for {@link Util#intersects(Collection, Collection)}. */
+ /** Unit test for
+ * {@link Util#intersects(java.util.Collection, java.util.Collection)}. */
@Test public void testIntersects() {
final List<String> empty = Collections.emptyList();
final List<String> listA = Collections.singletonList("a");
@@ -1698,6 +1699,42 @@ public class UtilTest {
}
+ /** Tests {@link Util#immutableCopy(Iterable)}. */
+ @Test public void testImmutableCopy() {
+ final List<Integer> list3 = Arrays.asList(1, 2, 3);
+ final List<Integer> immutableList3 = ImmutableList.copyOf(list3);
+ final List<Integer> list0 = Arrays.asList();
+ final List<Integer> immutableList0 = ImmutableList.copyOf(list0);
+ final List<Integer> list1 = Arrays.asList(1);
+ final List<Integer> immutableList1 = ImmutableList.copyOf(list1);
+
+ final List<List<Integer>> list301 = Arrays.asList(list3, list0, list1);
+ final List<List<Integer>> immutableList301 = Util.immutableCopy(list301);
+ assertThat(immutableList301.size(), is(3));
+ assertThat(immutableList301, is(list301));
+ assertThat(immutableList301, not(sameInstance(list301)));
+ for (List<Integer> list : immutableList301) {
+ assertThat(list, isA((Class) ImmutableList.class));
+ }
+
+ // if you copy the copy, you get the same instance
+ final List<List<Integer>> immutableList301b =
+ Util.immutableCopy(immutableList301);
+ assertThat(immutableList301b, sameInstance(immutableList301));
+ assertThat(immutableList301b, not(sameInstance(list301)));
+
+ // if the elements of the list are immutable lists, they are not copied
+ final List<List<Integer>> list301c =
+ Arrays.asList(immutableList3, immutableList0, immutableList1);
+ final List<List<Integer>> list301d = Util.immutableCopy(list301c);
+ assertThat(list301d.size(), is(3));
+ assertThat(list301d, is(list301));
+ assertThat(list301d, not(sameInstance(list301)));
+ assertThat(list301d.get(0), sameInstance(immutableList3));
+ assertThat(list301d.get(1), sameInstance(immutableList0));
+ assertThat(list301d.get(2), sameInstance(immutableList1));
+ }
+
@Test public void testAsIndexView() {
final List<String> values = Lists.newArrayList("abCde", "X", "y");
final Map<String, String> map = Util.asIndexMap(values,
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/test/resources/sql/misc.iq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/misc.iq b/core/src/test/resources/sql/misc.iq
index 36a2f93..cc3a4af 100644
--- a/core/src/test/resources/sql/misc.iq
+++ b/core/src/test/resources/sql/misc.iq
@@ -64,6 +64,71 @@ group by "hr"."emps"."empid";
!ok
+# Case-sensitive errors
+select empid from "hr"."emps";
+Column 'EMPID' not found in any table; did you mean 'empid'?
+!error
+
+select empid from "hr".emps;
+Object 'EMPS' not found within 'hr'; did you mean 'emps'?
+!error
+
+select empid from hr.emps;
+Object 'HR' not found; did you mean 'hr'?
+!error
+
+select empid from bad_schema.bad_table;
+Object 'BAD_SCHEMA' not found
+!error
+
+select empid from bad_cat.bad_schema.bad_table;
+Object 'BAD_CAT' not found
+!error
+
+select empid from "catalog".bad_schema.bad_table;
+Object 'catalog' not found
+!error
+
+select empid from catalog.bad_schema.bad_table;
+Object 'CATALOG' not found
+!error
+
+select empid from catalog.HR.bad_table;
+Object 'CATALOG' not found
+!error
+
+select empid from catalog."hr".bad_table;
+Object 'CATALOG' not found
+!error
+
+select empid from catalog."hr".emp;
+Object 'CATALOG' not found
+!error
+
+select empid from HR.bad_table;
+Object 'HR' not found; did you mean 'hr'?
+!error
+
+select empid from "HR".bad_table;
+Object 'HR' not found; did you mean 'hr'?
+!error
+
+select empid from HR."emps";
+Object 'HR' not found; did you mean 'hr'?
+!error
+
+select empid from "hr".bad_table;
+Object 'BAD_TABLE' not found within 'hr'
+!error
+
+select empid from "hr".emps;
+Object 'EMPS' not found within 'hr'; did you mean 'emps'?
+!error
+
+select empid from "hr";
+Object 'hr' not found
+!error
+
# [CALCITE-307] CAST(timestamp AS DATE) gives ClassCastException
# Based on [DRILL-1051]
with data(c_row, c_timestamp) as (select * from (values
[2/4] calcite git commit: [CALCITE-1574] Memory leak in maven
Posted by jh...@apache.org.
[CALCITE-1574] Memory leak in maven
Upgrade maven-jar-plugin and maven-source-plugin to versions that
have fixed [MSOURCES-94].
Disable UdfTest.testUserDefinedFunction; logged
[CALCITE-1561] Intermittent test failures
to remind us to re-enable.
Skip javadoc on Travis; with it, builds exceed Travis time limit.
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/28ae333f
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/28ae333f
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/28ae333f
Branch: refs/heads/master
Commit: 28ae333f0c7174b758487dbb5ddef91dacacd213
Parents: 46474d7
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Jan 12 10:36:25 2017 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Jan 13 09:11:39 2017 -0800
----------------------------------------------------------------------
.travis.yml | 4 ++--
.../java/org/apache/calcite/test/UdfTest.java | 2 ++
pom.xml | 19 +++++++++++++++++++
3 files changed, 23 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/28ae333f/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index b63f468..c8fdab8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,8 +31,8 @@ install:
- cd avatica && mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V && cd ..
- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
script:
- - cd avatica && mvn -Dsurefire.useFile=false javadoc:javadoc test && cd ..
- - mvn -Dsurefire.useFile=false javadoc:javadoc test
+ - cd avatica && mvn -Dsurefire.useFile=false test && cd ..
+ - mvn -Dsurefire.useFile=false test
git:
depth: 10000
sudo: false
http://git-wip-us.apache.org/repos/asf/calcite/blob/28ae333f/core/src/test/java/org/apache/calcite/test/UdfTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/UdfTest.java b/core/src/test/java/org/apache/calcite/test/UdfTest.java
index 3372102..774edad 100644
--- a/core/src/test/java/org/apache/calcite/test/UdfTest.java
+++ b/core/src/test/java/org/apache/calcite/test/UdfTest.java
@@ -26,6 +26,7 @@ import org.apache.calcite.util.Smalls;
import com.google.common.collect.ImmutableList;
+import org.junit.Ignore;
import org.junit.Test;
import java.sql.Connection;
@@ -149,6 +150,7 @@ public class UdfTest {
/** Tests a user-defined function that is defined in terms of a class with
* non-static methods. */
+ @Ignore("[CALCITE-1561] Intermittent test failures")
@Test public void testUserDefinedFunction() throws Exception {
final String sql = "select \"adhoc\".my_plus(\"deptno\", 100) as p\n"
+ "from \"adhoc\".EMPLOYEES";
http://git-wip-us.apache.org/repos/asf/calcite/blob/28ae333f/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index f728112..f196350 100644
--- a/pom.xml
+++ b/pom.xml
@@ -676,6 +676,20 @@ limitations under the License.
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
</plugin>
+ <!-- Override base, which uses version 3.0.0. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.0.2</version>
+ <configuration>
+ <archive>
+ <manifest>
+ <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
@@ -683,6 +697,11 @@ limitations under the License.
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>3.0.1</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<threadCount>1</threadCount>
[4/4] calcite git commit: [CALCITE-1549] More helpful error message
when schema, table or column not found
Posted by jh...@apache.org.
[CALCITE-1549] More helpful error message when schema, table or column not found
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/5f9c0190
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/5f9c0190
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/5f9c0190
Branch: refs/heads/master
Commit: 5f9c019080c7231acaf3df80732d915351051d93
Parents: 28ae333
Author: Julian Hyde <jh...@apache.org>
Authored: Tue Jan 3 14:45:27 2017 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Jan 13 09:12:17 2017 -0800
----------------------------------------------------------------------
.../calcite/adapter/enumerable/RexImpTable.java | 5 +-
.../org/apache/calcite/jdbc/CalciteSchema.java | 17 +-
.../calcite/jdbc/JavaTypeFactoryImpl.java | 15 +-
.../calcite/prepare/CalciteCatalogReader.java | 164 ++++++++-----
.../org/apache/calcite/prepare/Prepare.java | 2 +-
.../apache/calcite/prepare/RelOptTableImpl.java | 4 +-
.../apache/calcite/runtime/CalciteResource.java | 32 ++-
.../java/org/apache/calcite/schema/Table.java | 4 +
.../java/org/apache/calcite/schema/Wrapper.java | 27 +++
.../org/apache/calcite/sql/SqlIdentifier.java | 16 +-
.../calcite/sql/validate/AbstractNamespace.java | 2 +-
.../calcite/sql/validate/DelegatingScope.java | 144 +++++++++---
.../DelegatingSqlValidatorCatalogReader.java | 4 +-
.../apache/calcite/sql/validate/EmptyScope.java | 103 ++++++++-
.../sql/validate/IdentifierNamespace.java | 78 ++++++-
.../apache/calcite/sql/validate/ListScope.java | 70 ++++--
.../calcite/sql/validate/OrderByScope.java | 6 +-
.../calcite/sql/validate/SqlNameMatcher.java | 62 +++++
.../calcite/sql/validate/SqlNameMatchers.java | 154 +++++++++++++
.../calcite/sql/validate/SqlQualified.java | 5 +-
.../sql/validate/SqlValidatorCatalogReader.java | 41 +++-
.../calcite/sql/validate/SqlValidatorImpl.java | 67 +++---
.../calcite/sql/validate/SqlValidatorScope.java | 94 +++++---
.../calcite/sql/validate/SqlValidatorUtil.java | 33 ++-
.../apache/calcite/sql/validate/WithScope.java | 25 +-
.../calcite/sql2rel/SqlToRelConverter.java | 30 ++-
.../main/java/org/apache/calcite/util/Util.java | 23 ++
.../calcite/runtime/CalciteResource.properties | 9 +-
.../prepare/LookupOperatorOverloadsTest.java | 5 +-
.../calcite/sql/test/DefaultSqlTestFactory.java | 8 +-
.../apache/calcite/sql/test/SqlAdvisorTest.java | 12 +-
.../apache/calcite/test/CollectionTypeTest.java | 1 +
.../calcite/test/ExceptionMessageTest.java | 2 +-
.../java/org/apache/calcite/test/JdbcTest.java | 14 +-
.../org/apache/calcite/test/LatticeTest.java | 2 +-
.../apache/calcite/test/MockCatalogReader.java | 228 ++++++++-----------
.../calcite/test/MultiJdbcSchemaJoinTest.java | 4 +-
.../calcite/test/ReflectiveSchemaTest.java | 2 +-
.../apache/calcite/test/SqlToRelTestBase.java | 3 +-
.../apache/calcite/test/SqlValidatorTest.java | 130 +++++++++--
.../java/org/apache/calcite/util/UtilTest.java | 39 +++-
core/src/test/resources/sql/misc.iq | 65 ++++++
42 files changed, 1335 insertions(+), 416 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index f06680b..3d87041 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -1661,8 +1661,9 @@ public class RexImpTable {
assert translatedOperands.size() == 1;
ConstantExpression x = (ConstantExpression) translatedOperands.get(0);
List<String> names = Util.stringToList((String) x.value);
- RelOptTable table =
- Prepare.CatalogReader.THREAD_LOCAL.get().getTable(names);
+ final Prepare.CatalogReader catalogReader =
+ Prepare.CatalogReader.THREAD_LOCAL.get();
+ RelOptTable table = catalogReader.getTable(names);
System.out.println("Now, do something with table " + table);
return super.implement(translator, call, translatedOperands);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
index 89e377d..cb61627 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
@@ -376,12 +376,25 @@ public abstract class CalciteSchema {
*/
public static CalciteSchema createRootSchema(boolean addMetadataSchema,
boolean cache) {
+ return createRootSchema(addMetadataSchema, cache, "");
+ }
+
+ /** Creates a root schema.
+ *
+ * @param addMetadataSchema Whether to add a "metadata" schema containing
+ * definitions of tables, columns etc.
+ * @param cache If true create {@link CachingCalciteSchema};
+ * if false create {@link SimpleCalciteSchema}
+ * @param name Schema name
+ */
+ public static CalciteSchema createRootSchema(boolean addMetadataSchema,
+ boolean cache, String name) {
CalciteSchema rootSchema;
final Schema schema = new CalciteConnectionImpl.RootSchema();
if (cache) {
- rootSchema = new CachingCalciteSchema(null, schema, "");
+ rootSchema = new CachingCalciteSchema(null, schema, name);
} else {
- rootSchema = new SimpleCalciteSchema(null, schema, "");
+ rootSchema = new SimpleCalciteSchema(null, schema, name);
}
if (addMetadataSchema) {
rootSchema.add("metadata", MetadataSchema.INSTANCE);
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
index a1f6e3f..c600d6f 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
@@ -22,6 +22,7 @@ import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.linq4j.tree.Primitive;
import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rel.type.RelDataTypeFieldImpl;
import org.apache.calcite.rel.type.RelDataTypeSystem;
@@ -233,19 +234,25 @@ public class JavaTypeFactoryImpl
}
public RelDataType toSql(RelDataType type) {
+ return toSql(this, type);
+ }
+
+ /** Converts a type in Java format to a SQL-oriented type. */
+ public static RelDataType toSql(final RelDataTypeFactory typeFactory,
+ RelDataType type) {
if (type instanceof RelRecordType) {
- return createStructType(
+ return typeFactory.createStructType(
Lists.transform(type.getFieldList(),
new Function<RelDataTypeField, RelDataType>() {
public RelDataType apply(RelDataTypeField a0) {
- return toSql(a0.getType());
+ return toSql(typeFactory, a0.getType());
}
}),
type.getFieldNames());
}
if (type instanceof JavaType) {
- return createTypeWithNullability(
- createSqlType(type.getSqlTypeName()),
+ return typeFactory.createTypeWithNullability(
+ typeFactory.createSqlType(type.getSqlTypeName()),
type.isNullable());
}
return type;
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
index 0e14c83..7f0823c 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
@@ -16,8 +16,8 @@
*/
package org.apache.calcite.prepare;
-import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -30,6 +30,7 @@ import org.apache.calcite.schema.ScalarFunction;
import org.apache.calcite.schema.Table;
import org.apache.calcite.schema.TableFunction;
import org.apache.calcite.schema.TableMacro;
+import org.apache.calcite.schema.Wrapper;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlOperator;
@@ -45,6 +46,8 @@ import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlMoniker;
import org.apache.calcite.sql.validate.SqlMonikerImpl;
import org.apache.calcite.sql.validate.SqlMonikerType;
+import org.apache.calcite.sql.validate.SqlNameMatcher;
+import org.apache.calcite.sql.validate.SqlNameMatchers;
import org.apache.calcite.sql.validate.SqlUserDefinedAggFunction;
import org.apache.calcite.sql.validate.SqlUserDefinedFunction;
import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction;
@@ -52,6 +55,7 @@ import org.apache.calcite.sql.validate.SqlUserDefinedTableMacro;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.util.Util;
+import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
@@ -61,6 +65,7 @@ import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
@@ -71,56 +76,74 @@ import java.util.NavigableSet;
* functions defined schemas.
*/
public class CalciteCatalogReader implements Prepare.CatalogReader {
- final CalciteSchema rootSchema;
- final JavaTypeFactory typeFactory;
- private final List<String> defaultSchema;
- private final boolean caseSensitive;
-
- public CalciteCatalogReader(
- CalciteSchema rootSchema,
- boolean caseSensitive,
- List<String> defaultSchema,
- JavaTypeFactory typeFactory) {
- super();
- assert rootSchema != defaultSchema;
- this.rootSchema = rootSchema;
- this.caseSensitive = caseSensitive;
- this.defaultSchema = defaultSchema;
+ protected final CalciteSchema rootSchema;
+ protected final RelDataTypeFactory typeFactory;
+ private final List<List<String>> schemaPaths;
+ protected final SqlNameMatcher nameMatcher;
+
+ public CalciteCatalogReader(CalciteSchema rootSchema, boolean caseSensitive,
+ List<String> defaultSchema, RelDataTypeFactory typeFactory) {
+ this(rootSchema, SqlNameMatchers.withCaseSensitive(caseSensitive),
+ ImmutableList.of(Preconditions.checkNotNull(defaultSchema),
+ ImmutableList.<String>of()),
+ typeFactory);
+ }
+
+ protected CalciteCatalogReader(CalciteSchema rootSchema,
+ SqlNameMatcher nameMatcher, List<List<String>> schemaPaths,
+ RelDataTypeFactory typeFactory) {
+ this.rootSchema = Preconditions.checkNotNull(rootSchema);
+ this.nameMatcher = nameMatcher;
+ this.schemaPaths =
+ Util.immutableCopy(Util.isDistinct(schemaPaths)
+ ? schemaPaths
+ : new LinkedHashSet<>(schemaPaths));
this.typeFactory = typeFactory;
}
public CalciteCatalogReader withSchemaPath(List<String> schemaPath) {
- return new CalciteCatalogReader(rootSchema, caseSensitive, schemaPath,
- typeFactory);
+ return new CalciteCatalogReader(rootSchema, nameMatcher,
+ ImmutableList.of(schemaPath, ImmutableList.<String>of()), typeFactory);
}
- public RelOptTableImpl getTable(final List<String> names) {
+ public Prepare.PreparingTable getTable(final List<String> names) {
// First look in the default schema, if any.
- if (defaultSchema != null) {
- RelOptTableImpl table = getTableFrom(names, defaultSchema);
+ // If not found, look in the root schema.
+ for (List<String> schemaPath : schemaPaths) {
+ Prepare.PreparingTable table =
+ getTableFrom(names, schemaPath, nameMatcher);
if (table != null) {
return table;
}
}
- // If not found, look in the root schema
- return getTableFrom(names, ImmutableList.<String>of());
+ return null;
}
- private RelOptTableImpl getTableFrom(List<String> names,
- List<String> schemaNames) {
+ private Prepare.PreparingTable getTableFrom(List<String> names,
+ List<String> schemaNames, SqlNameMatcher nameMatcher) {
CalciteSchema schema =
- getSchema(Iterables.concat(schemaNames, Util.skipLast(names)));
+ getSchema(Iterables.concat(schemaNames, Util.skipLast(names)),
+ nameMatcher);
if (schema == null) {
return null;
}
final String name = Util.last(names);
- CalciteSchema.TableEntry entry = schema.getTable(name, caseSensitive);
+ CalciteSchema.TableEntry entry =
+ schema.getTable(name, nameMatcher.isCaseSensitive());
if (entry == null) {
- entry = schema.getTableBasedOnNullaryFunction(name, caseSensitive);
+ entry = schema.getTableBasedOnNullaryFunction(name,
+ nameMatcher.isCaseSensitive());
}
if (entry != null) {
final Table table = entry.getTable();
final String name2 = entry.name;
+ if (table instanceof Wrapper) {
+ final Prepare.PreparingTable relOptTable =
+ ((Wrapper) table).unwrap(Prepare.PreparingTable.class);
+ if (relOptTable != null) {
+ return relOptTable;
+ }
+ }
return RelOptTableImpl.create(this, table.getRowType(typeFactory),
schema.add(name2, table), null);
}
@@ -129,21 +152,27 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
private Collection<Function> getFunctionsFrom(List<String> names) {
final List<Function> functions2 = Lists.newArrayList();
- final List<? extends List<String>> schemaNameList;
+ final List<List<String>> schemaNameList = new ArrayList<>();
if (names.size() > 1) {
- // If name is qualified, ignore path.
- schemaNameList = ImmutableList.of(ImmutableList.<String>of());
- } else {
- CalciteSchema schema = getSchema(defaultSchema);
- if (schema == null) {
- schemaNameList = ImmutableList.of();
+ // Name qualified: ignore path. But we do look in "/catalog" and "/",
+ // the last 2 items in the path.
+ if (schemaPaths.size() > 1) {
+ schemaNameList.addAll(Util.skip(schemaPaths));
} else {
- schemaNameList = schema.getPath();
+ schemaNameList.addAll(schemaPaths);
+ }
+ } else {
+ for (List<String> schemaPath : schemaPaths) {
+ CalciteSchema schema = getSchema(schemaPath, nameMatcher);
+ if (schema != null) {
+ schemaNameList.addAll(schema.getPath());
+ }
}
}
for (List<String> schemaNames : schemaNameList) {
CalciteSchema schema =
- getSchema(Iterables.concat(schemaNames, Util.skipLast(names)));
+ getSchema(Iterables.concat(schemaNames, Util.skipLast(names)),
+ nameMatcher);
if (schema != null) {
final String name = Util.last(names);
functions2.addAll(schema.getFunctions(name, true));
@@ -152,10 +181,15 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
return functions2;
}
- private CalciteSchema getSchema(Iterable<String> schemaNames) {
+ private CalciteSchema getSchema(Iterable<String> schemaNames,
+ SqlNameMatcher nameMatcher) {
CalciteSchema schema = rootSchema;
for (String schemaName : schemaNames) {
- schema = schema.getSubSchema(schemaName, caseSensitive);
+ if (schema == rootSchema
+ && nameMatcher.matches(schemaName, schema.getName())) {
+ continue;
+ }
+ schema = schema.getSubSchema(schemaName, nameMatcher.isCaseSensitive());
if (schema == null) {
return null;
}
@@ -168,51 +202,65 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
}
public List<SqlMoniker> getAllSchemaObjectNames(List<String> names) {
- final CalciteSchema schema = getSchema(names);
+ final CalciteSchema schema = getSchema(names, nameMatcher);
if (schema == null) {
return ImmutableList.of();
}
final List<SqlMoniker> result = new ArrayList<>();
+
+ // Add root schema if not anonymous
+ if (!schema.name.equals("")) {
+ result.add(moniker(schema, null, SqlMonikerType.SCHEMA));
+ }
+
final Map<String, CalciteSchema> schemaMap = schema.getSubSchemaMap();
for (String subSchema : schemaMap.keySet()) {
- result.add(
- new SqlMonikerImpl(schema.path(subSchema), SqlMonikerType.SCHEMA));
+ result.add(moniker(schema, subSchema, SqlMonikerType.SCHEMA));
}
for (String table : schema.getTableNames()) {
- result.add(
- new SqlMonikerImpl(schema.path(table), SqlMonikerType.TABLE));
+ result.add(moniker(schema, table, SqlMonikerType.TABLE));
}
final NavigableSet<String> functions = schema.getFunctionNames();
for (String function : functions) { // views are here as well
- result.add(
- new SqlMonikerImpl(schema.path(function), SqlMonikerType.FUNCTION));
+ result.add(moniker(schema, function, SqlMonikerType.FUNCTION));
}
return result;
}
- public List<String> getSchemaName() {
- return defaultSchema;
+ private SqlMonikerImpl moniker(CalciteSchema schema, String name,
+ SqlMonikerType type) {
+ final List<String> path = schema.path(name);
+ if (path.size() == 1
+ && !schema.root().name.equals("")
+ && type == SqlMonikerType.SCHEMA) {
+ type = SqlMonikerType.CATALOG;
+ }
+ return new SqlMonikerImpl(path, type);
}
- public RelOptTableImpl getTableForMember(List<String> names) {
+ public List<List<String>> getSchemaPaths() {
+ return schemaPaths;
+ }
+
+ public Prepare.PreparingTable getTableForMember(List<String> names) {
return getTable(names);
}
public RelDataTypeField field(RelDataType rowType, String alias) {
- return SqlValidatorUtil.lookupField(caseSensitive, rowType, alias);
+ return nameMatcher.field(rowType, alias);
}
public boolean matches(String string, String name) {
- return Util.matches(caseSensitive, string, name);
+ return nameMatcher.matches(string, name);
}
public RelDataType createTypeFromProjection(final RelDataType type,
final List<String> columnNameList) {
return SqlValidatorUtil.createTypeFromProjection(type, columnNameList,
- typeFactory, caseSensitive);
+ typeFactory, nameMatcher.isCaseSensitive());
}
public void lookupOperatorOverloads(final SqlIdentifier opName,
@@ -327,13 +375,17 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
return typeFactory.createTypeWithNullability(
typeFactory.createSqlType(SqlTypeName.ANY), true);
}
- return typeFactory.toSql(type);
+ return JavaTypeFactoryImpl.toSql(typeFactory, type);
}
public List<SqlOperator> getOperatorList() {
return null;
}
+ public CalciteSchema getRootSchema() {
+ return rootSchema;
+ }
+
public RelDataTypeFactory getTypeFactory() {
return typeFactory;
}
@@ -342,10 +394,12 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
}
@Override public boolean isCaseSensitive() {
- return caseSensitive;
+ return nameMatcher.isCaseSensitive();
}
-
+ public SqlNameMatcher nameMatcher() {
+ return nameMatcher;
+ }
}
// End CalciteCatalogReader.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/prepare/Prepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/Prepare.java b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
index b5ceec8..5de9b35 100644
--- a/core/src/main/java/org/apache/calcite/prepare/Prepare.java
+++ b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
@@ -388,7 +388,7 @@ public abstract class Prepare {
* different schema path. */
CatalogReader withSchemaPath(List<String> schemaPath);
- PreparingTable getTable(List<String> names);
+ @Override PreparingTable getTable(List<String> names);
ThreadLocal<CatalogReader> THREAD_LOCAL = new ThreadLocal<>();
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
----------------------------------------------------------------------
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 6d7ffac..a9c381e 100644
--- a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
@@ -326,11 +326,11 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
return SqlAccessType.ALL;
}
- /** Im0plementation of {@link SchemaPlus} that wraps a regular schema and knows
+ /** Implementation of {@link SchemaPlus} that wraps a regular schema and knows
* its name and parent.
*
* <p>It is read-only, and functionality is limited in other ways, it but
- * allows table expressions to be genenerated. */
+ * allows table expressions to be generated. */
private static class MySchemaPlus implements SchemaPlus {
private final SchemaPlus parent;
private final String name;
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index 77a6bf9..9ada4f2 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -158,15 +158,44 @@ public interface CalciteResource {
@BaseMessage("Table ''{0}'' not found")
ExInst<SqlValidatorException> tableNameNotFound(String a0);
+ @BaseMessage("Table ''{0}'' not found; did you mean ''{1}''?")
+ ExInst<SqlValidatorException> tableNameNotFoundDidYouMean(String a0,
+ String a1);
+
+ /** Same message as {@link #tableNameNotFound(String)} but a different kind
+ * of exception, so it can be used in {@code RelBuilder}. */
+ @BaseMessage("Table ''{0}'' not found")
+ ExInst<CalciteException> tableNotFound(String tableName);
+
+ @BaseMessage("Object ''{0}'' not found")
+ ExInst<SqlValidatorException> objectNotFound(String a0);
+
+ @BaseMessage("Object ''{0}'' not found within ''{1}''")
+ ExInst<SqlValidatorException> objectNotFoundWithin(String a0, String a1);
+
+ @BaseMessage("Object ''{0}'' not found; did you mean ''{1}''?")
+ ExInst<SqlValidatorException> objectNotFoundDidYouMean(String a0, String a1);
+
+ @BaseMessage("Object ''{0}'' not found within ''{1}''; did you mean ''{2}''?")
+ ExInst<SqlValidatorException> objectNotFoundWithinDidYouMean(String a0,
+ String a1, String a2);
+
@BaseMessage("Table ''{0}'' is not a sequence")
ExInst<SqlValidatorException> notASequence(String a0);
@BaseMessage("Column ''{0}'' not found in any table")
ExInst<SqlValidatorException> columnNotFound(String a0);
+ @BaseMessage("Column ''{0}'' not found in any table; did you mean ''{1}''?")
+ ExInst<SqlValidatorException> columnNotFoundDidYouMean(String a0, String a1);
+
@BaseMessage("Column ''{0}'' not found in table ''{1}''")
ExInst<SqlValidatorException> columnNotFoundInTable(String a0, String a1);
+ @BaseMessage("Column ''{0}'' not found in table ''{1}''; did you mean ''{2}''?")
+ ExInst<SqlValidatorException> columnNotFoundInTableDidYouMean(String a0,
+ String a1, String a2);
+
@BaseMessage("Column ''{0}'' is ambiguous")
ExInst<SqlValidatorException> columnAmbiguous(String a0);
@@ -610,9 +639,6 @@ public interface CalciteResource {
@BaseMessage("View is not modifiable. No value is supplied for NOT NULL column ''{0}'' of base table ''{1}''")
ExInst<SqlValidatorException> noValueSuppliedForViewColumn(String columnName, String tableName);
- @BaseMessage("Table ''{0}'' not found")
- ExInst<CalciteException> tableNotFound(String tableName);
-
@BaseMessage("Not a record type. The ''*'' operator requires a record")
ExInst<SqlValidatorException> starRequiresRecordType();
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/schema/Table.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/Table.java b/core/src/main/java/org/apache/calcite/schema/Table.java
index b25745e..8062565 100644
--- a/core/src/main/java/org/apache/calcite/schema/Table.java
+++ b/core/src/main/java/org/apache/calcite/schema/Table.java
@@ -34,6 +34,9 @@ import org.apache.calcite.rel.type.RelDataTypeFactory;
* <a href="http://en.wikipedia.org/wiki/Inode">i-node</a> concept in the UNIX
* filesystem.)</p>
*
+ * <p>A particular table instance may also implement {@link Wrapper},
+ * to give access to sub-objects.
+ *
* @see TableMacro
*/
public interface Table {
@@ -56,6 +59,7 @@ public interface Table {
/** Type of table. */
Schema.TableType getJdbcTableType();
+
}
// End Table.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/schema/Wrapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/Wrapper.java b/core/src/main/java/org/apache/calcite/schema/Wrapper.java
new file mode 100644
index 0000000..c14439d
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/schema/Wrapper.java
@@ -0,0 +1,27 @@
+/*
+ * 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 org.apache.calcite.schema;
+
+/**
+ * Mix-in interface that allows you to find sub-objects.
+ */
+public interface Wrapper {
+ /** Returns an instance of a class, or null. */
+ <C> C unwrap(Class<C> aClass);
+}
+
+// End Wrapper.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java b/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
index 7e2294c..4839805 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
@@ -141,12 +141,22 @@ public class SqlIdentifier extends SqlNode {
return SqlKind.IDENTIFIER;
}
- public SqlNode clone(SqlParserPos pos) {
+ @Override public SqlNode clone(SqlParserPos pos) {
return new SqlIdentifier(names, collation, pos, componentPositions);
}
- public String toString() {
- return Util.sepList(Lists.transform(names, EMPTY_TO_STAR), ".");
+ @Override public String toString() {
+ return getString(names);
+ }
+
+ /** Converts a list of strings to a qualified identifier. */
+ public static String getString(List<String> names) {
+ return Util.sepList(toStar(names), ".");
+ }
+
+ /** Converts empty strings in a list of names to stars. */
+ public static List<String> toStar(List<String> names) {
+ return Lists.transform(names, EMPTY_TO_STAR);
}
/**
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java
index d5f3c18..b6bd058 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java
@@ -149,7 +149,7 @@ abstract class AbstractNamespace implements SqlValidatorNamespace {
public boolean fieldExists(String name) {
final RelDataType rowType = getRowType();
- return validator.catalogReader.field(rowType, name) != null;
+ return validator.catalogReader.nameMatcher().field(rowType, name) != null;
}
public List<Pair<SqlNode, SqlMonotonicity>> getMonotonicExprs() {
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java
index 52983d1..8b004cd 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java
@@ -31,10 +31,12 @@ import org.apache.calcite.sql.SqlSelect;
import org.apache.calcite.sql.SqlWindow;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -83,16 +85,18 @@ public abstract class DelegatingScope implements SqlValidatorScope {
throw new UnsupportedOperationException();
}
- public void resolve(List<String> names, boolean deep, Resolved resolved) {
- parent.resolve(names, deep, resolved);
+ public void resolve(List<String> names, SqlNameMatcher nameMatcher,
+ boolean deep, Resolved resolved) {
+ parent.resolve(names, nameMatcher, deep, resolved);
}
/** If a record type allows implicit references to fields, recursively looks
* into the fields. Otherwise returns immediately. */
void resolveInNamespace(SqlValidatorNamespace ns, boolean nullable,
- List<String> names, Path path, Resolved resolved) {
+ List<String> names, SqlNameMatcher nameMatcher, Path path,
+ Resolved resolved) {
if (names.isEmpty()) {
- resolved.found(ns, nullable, this, path);
+ resolved.found(ns, nullable, this, path, null);
return;
}
final RelDataType rowType = ns.getRowType();
@@ -109,32 +113,33 @@ public abstract class DelegatingScope implements SqlValidatorScope {
final List<String> remainder = entry.getValue();
final SqlValidatorNamespace ns2 =
new FieldNamespace(validator, field.getType());
- final Step path2 = path.add(rowType, field.getIndex(),
- StructKind.FULLY_QUALIFIED);
- resolveInNamespace(ns2, nullable, remainder, path2, resolved);
+ final Step path2 = path.plus(rowType, field.getIndex(),
+ field.getName(), StructKind.FULLY_QUALIFIED);
+ resolveInNamespace(ns2, nullable, remainder, nameMatcher, path2,
+ resolved);
}
return;
}
}
final String name = names.get(0);
- final RelDataTypeField field0 =
- validator.catalogReader.field(rowType, name);
+ final RelDataTypeField field0 = nameMatcher.field(rowType, name);
if (field0 != null) {
final SqlValidatorNamespace ns2 = ns.lookupChild(field0.getName());
- final Step path2 = path.add(rowType, field0.getIndex(),
- StructKind.FULLY_QUALIFIED);
+ final Step path2 = path.plus(rowType, field0.getIndex(),
+ field0.getName(), StructKind.FULLY_QUALIFIED);
resolveInNamespace(ns2, nullable, names.subList(1, names.size()),
- path2, resolved);
+ nameMatcher, path2, resolved);
} else {
for (RelDataTypeField field : rowType.getFieldList()) {
switch (field.getType().getStructKind()) {
case PEEK_FIELDS:
case PEEK_FIELDS_DEFAULT:
- final Step path2 = path.add(rowType, field.getIndex(),
- field.getType().getStructKind());
+ final Step path2 = path.plus(rowType, field.getIndex(),
+ field.getName(), field.getType().getStructKind());
final SqlValidatorNamespace ns2 = ns.lookupChild(field.getName());
- resolveInNamespace(ns2, nullable, names, path2, resolved);
+ resolveInNamespace(ns2, nullable, names, nameMatcher, path2,
+ resolved);
}
}
}
@@ -170,13 +175,20 @@ public abstract class DelegatingScope implements SqlValidatorScope {
public Pair<String, SqlValidatorNamespace>
findQualifyingTableName(String columnName, SqlNode ctx) {
+ //noinspection deprecation
return parent.findQualifyingTableName(columnName, ctx);
}
- protected Map<String, ScopeChild> findQualifyingTables(String columnName) {
+ protected Map<String, ScopeChild> findQualifyingTables(String columnName,
+ SqlNameMatcher nameMatcher) {
return ImmutableMap.of();
}
+ public Map<String, ScopeChild> findQualifyingTableNames(String columnName,
+ SqlNode ctx, SqlNameMatcher nameMatcher) {
+ return parent.findQualifyingTableNames(columnName, ctx, nameMatcher);
+ }
+
public RelDataType resolveColumn(String name, SqlNode ctx) {
return parent.resolveColumn(name, ctx);
}
@@ -189,6 +201,11 @@ public abstract class DelegatingScope implements SqlValidatorScope {
return parent.getTableNamespace(names);
}
+ public void resolveTable(List<String> names, SqlNameMatcher nameMatcher,
+ Path path, Resolved resolved) {
+ parent.resolveTable(names, nameMatcher, path, resolved);
+ }
+
public SqlValidatorScope getOperandScope(SqlCall call) {
if (call instanceof SqlSelect) {
return validator.getSelectScope((SqlSelect) call);
@@ -213,20 +230,51 @@ public abstract class DelegatingScope implements SqlValidatorScope {
}
final SqlIdentifier previous = identifier;
+ final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher();
String columnName;
+ final String tableName;
+ final SqlValidatorNamespace namespace;
switch (identifier.names.size()) {
case 1: {
columnName = identifier.names.get(0);
- final Pair<String, SqlValidatorNamespace> pair =
- findQualifyingTableName(columnName, identifier);
- final String tableName = pair.left;
- final SqlValidatorNamespace namespace = pair.right;
+ final Map<String, ScopeChild> map =
+ findQualifyingTableNames(columnName, identifier, nameMatcher);
+ switch (map.size()) {
+ case 0:
+ if (nameMatcher.isCaseSensitive()) {
+ final SqlNameMatcher liberalMatcher = SqlNameMatchers.liberal();
+ final Map<String, ScopeChild> map2 =
+ findQualifyingTableNames(columnName, identifier, liberalMatcher);
+ if (!map2.isEmpty()) {
+ final List<String> list = new ArrayList<>();
+ for (ScopeChild entry : map2.values()) {
+ final RelDataTypeField field =
+ liberalMatcher.field(entry.namespace.getRowType(),
+ columnName);
+ list.add(field.getName());
+ }
+ Collections.sort(list);
+ throw validator.newValidationError(identifier,
+ RESOURCE.columnNotFoundDidYouMean(columnName,
+ Util.sepList(list, "', '")));
+ }
+ }
+ throw validator.newValidationError(identifier,
+ RESOURCE.columnNotFound(columnName));
+ case 1:
+ tableName = map.keySet().iterator().next();
+ namespace = map.get(tableName).namespace;
+ break;
+ default:
+ throw validator.newValidationError(identifier,
+ RESOURCE.columnAmbiguous(columnName));
+ }
final ResolvedImpl resolved = new ResolvedImpl();
- resolveInNamespace(namespace, false, identifier.names,
- resolved.emptyPath(), resolved);
+ resolveInNamespace(namespace, false, identifier.names, nameMatcher,
+ Path.EMPTY, resolved);
final RelDataTypeField field =
- validator.catalogReader.field(namespace.getRowType(), columnName);
+ nameMatcher.field(namespace.getRowType(), columnName);
if (field != null) {
if (hasAmbiguousUnresolvedStar(namespace.getRowType(), field,
columnName)) {
@@ -253,7 +301,7 @@ public abstract class DelegatingScope implements SqlValidatorScope {
for (; i > 0; i--) {
final SqlIdentifier prefix = identifier.getComponent(0, i);
resolved.clear();
- resolve(prefix.names, false, resolved);
+ resolve(prefix.names, nameMatcher, false, resolved);
if (resolved.count() == 1) {
final Resolve resolve = resolved.only();
fromNs = resolve.namespace;
@@ -261,11 +309,24 @@ public abstract class DelegatingScope implements SqlValidatorScope {
fromRowType = resolve.rowType();
break;
}
+ // Look for a table alias that is the wrong case.
+ if (nameMatcher.isCaseSensitive()) {
+ final SqlNameMatcher liberalMatcher = SqlNameMatchers.liberal();
+ resolved.clear();
+ resolve(prefix.names, liberalMatcher, false, resolved);
+ if (resolved.count() == 1) {
+ final Step lastStep = Util.last(resolved.only().path.steps());
+ throw validator.newValidationError(prefix,
+ RESOURCE.tableNameNotFoundDidYouMean(prefix.toString(),
+ lastStep.name));
+ }
+ }
}
if (fromNs == null || fromNs instanceof SchemaNamespace) {
// Look for a column not qualified by a table alias.
columnName = identifier.names.get(0);
- final Map<String, ScopeChild> map = findQualifyingTables(columnName);
+ final Map<String, ScopeChild> map =
+ findQualifyingTables(columnName, nameMatcher);
switch (map.size()) {
default:
final SqlIdentifier prefix1 = identifier.skipLast(1);
@@ -274,21 +335,22 @@ public abstract class DelegatingScope implements SqlValidatorScope {
case 1: {
final Map.Entry<String, ScopeChild> entry =
map.entrySet().iterator().next();
- final String tableName = entry.getKey();
+ final String tableName2 = map.keySet().iterator().next();
fromNs = entry.getValue().namespace;
- fromPath = resolved.emptyPath();
+ fromPath = Path.EMPTY;
// Adding table name is for RecordType column with StructKind.PEEK_FIELDS or
// StructKind.PEEK_FIELDS only. Access to a field in a RecordType column of
// other StructKind should always be qualified with table name.
final RelDataTypeField field =
- validator.catalogReader.field(fromNs.getRowType(), columnName);
+ nameMatcher.field(fromNs.getRowType(), columnName);
if (field != null) {
switch (field.getType().getStructKind()) {
case PEEK_FIELDS:
case PEEK_FIELDS_DEFAULT:
columnName = field.getName(); // use resolved field name
- resolve(ImmutableList.of(tableName), false, resolved);
+ resolve(ImmutableList.of(tableName2), nameMatcher, false,
+ resolved);
if (resolved.count() == 1) {
final Resolve resolve = resolved.only();
fromNs = resolve.namespace;
@@ -296,7 +358,7 @@ public abstract class DelegatingScope implements SqlValidatorScope {
fromRowType = resolve.rowType();
identifier = identifier
.setName(0, columnName)
- .add(0, tableName, SqlParserPos.ZERO);
+ .add(0, tableName2, SqlParserPos.ZERO);
++i;
++size;
}
@@ -339,11 +401,27 @@ public abstract class DelegatingScope implements SqlValidatorScope {
}
final SqlIdentifier suffix = identifier.getComponent(i, size);
resolved.clear();
- resolveInNamespace(fromNs, false, suffix.names, resolved.emptyPath(),
+ resolveInNamespace(fromNs, false, suffix.names, nameMatcher, Path.EMPTY,
resolved);
final Path path;
switch (resolved.count()) {
case 0:
+ // Maybe the last component was correct, just wrong case
+ if (nameMatcher.isCaseSensitive()) {
+ SqlNameMatcher liberalMatcher = SqlNameMatchers.liberal();
+ resolved.clear();
+ resolveInNamespace(fromNs, false, suffix.names, liberalMatcher,
+ Path.EMPTY, resolved);
+ if (resolved.count() > 0) {
+ int k = size - 1;
+ final SqlIdentifier prefix = identifier.getComponent(0, i);
+ final SqlIdentifier suffix3 = identifier.getComponent(i, k + 1);
+ final Step step = Util.last(resolved.resolves.get(0).path.steps());
+ throw validator.newValidationError(suffix3,
+ RESOURCE.columnNotFoundInTableDidYouMean(suffix3.toString(),
+ prefix.toString(), step.name));
+ }
+ }
// Find the shortest suffix that also fails. Suppose we cannot resolve
// "a.b.c"; we find we cannot resolve "a.b" but can resolve "a". So,
// the error will be "Column 'a.b' not found".
@@ -351,8 +429,8 @@ public abstract class DelegatingScope implements SqlValidatorScope {
for (; k > i; --k) {
SqlIdentifier suffix2 = identifier.getComponent(i, k);
resolved.clear();
- resolveInNamespace(fromNs, false, suffix2.names,
- resolved.emptyPath(), resolved);
+ resolveInNamespace(fromNs, false, suffix2.names, nameMatcher,
+ Path.EMPTY, resolved);
if (resolved.count() > 0) {
break;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/DelegatingSqlValidatorCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingSqlValidatorCatalogReader.java b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingSqlValidatorCatalogReader.java
index b1d9c39..244afb5 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingSqlValidatorCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingSqlValidatorCatalogReader.java
@@ -52,8 +52,8 @@ public abstract class DelegatingSqlValidatorCatalogReader
return catalogReader.getAllSchemaObjectNames(names);
}
- public List<String> getSchemaName() {
- return catalogReader.getSchemaName();
+ public List<List<String>> getSchemaPaths() {
+ return catalogReader.getSchemaPaths();
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java b/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java
index 3e3ce3f..b9a38cf 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java
@@ -16,7 +16,13 @@
*/
package org.apache.calcite.sql.validate;
+import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.prepare.Prepare;
+import org.apache.calcite.prepare.RelOptTableImpl;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.StructKind;
+import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.Wrapper;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlDataTypeSpec;
import org.apache.calcite.sql.SqlDynamicParam;
@@ -26,9 +32,15 @@ import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlWindow;
import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.Util;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import static org.apache.calcite.util.Static.RESOURCE;
@@ -64,7 +76,8 @@ class EmptyScope implements SqlValidatorScope {
throw new UnsupportedOperationException();
}
- public void resolve(List<String> names, boolean deep, Resolved resolved) {
+ public void resolve(List<String> names, SqlNameMatcher nameMatcher,
+ boolean deep, Resolved resolved) {
}
public SqlValidatorNamespace getTableNamespace(List<String> names) {
@@ -74,6 +87,89 @@ class EmptyScope implements SqlValidatorScope {
: null;
}
+ public void resolveTable(List<String> names, SqlNameMatcher nameMatcher,
+ Path path, Resolved resolved) {
+ final List<Resolve> imperfectResolves = new ArrayList<>();
+ final List<Resolve> resolves = ((ResolvedImpl) resolved).resolves;
+
+ // Look in the default schema, then default catalog, then root schema.
+ for (List<String> schemaPath : validator.catalogReader.getSchemaPaths()) {
+ resolve_(validator.catalogReader.getRootSchema(), names, schemaPath,
+ nameMatcher, path, resolved);
+ for (Resolve resolve : resolves) {
+ if (resolve.remainingNames.isEmpty()) {
+ // There is a full match. Return it as the only match.
+ ((ResolvedImpl) resolved).clear();
+ resolves.add(resolve);
+ return;
+ }
+ }
+ imperfectResolves.addAll(resolves);
+ }
+ // If there were no matches in the last round, restore those found in
+ // previous rounds
+ if (resolves.isEmpty()) {
+ resolves.addAll(imperfectResolves);
+ }
+ }
+
+ private void resolve_(final CalciteSchema rootSchema, List<String> names,
+ List<String> schemaNames, SqlNameMatcher nameMatcher, Path path,
+ Resolved resolved) {
+ final List<String> concat = ImmutableList.<String>builder()
+ .addAll(schemaNames).addAll(names).build();
+ CalciteSchema schema = rootSchema;
+ SqlValidatorNamespace namespace = null;
+ List<String> remainingNames = concat;
+ for (String schemaName : concat) {
+ if (schema == rootSchema
+ && nameMatcher.matches(schemaName, schema.name)) {
+ remainingNames = Util.skip(remainingNames);
+ continue;
+ }
+ final CalciteSchema subSchema =
+ schema.getSubSchema(schemaName, nameMatcher.isCaseSensitive());
+ if (subSchema != null) {
+ path = path.plus(null, -1, subSchema.name, StructKind.NONE);
+ remainingNames = Util.skip(remainingNames);
+ schema = subSchema;
+ namespace = new SchemaNamespace(validator,
+ ImmutableList.copyOf(path.stepNames()));
+ continue;
+ }
+ CalciteSchema.TableEntry entry =
+ schema.getTable(schemaName, nameMatcher.isCaseSensitive());
+ if (entry == null) {
+ entry = schema.getTableBasedOnNullaryFunction(schemaName,
+ nameMatcher.isCaseSensitive());
+ }
+ if (entry != null) {
+ path = path.plus(null, -1, entry.name, StructKind.NONE);
+ remainingNames = Util.skip(remainingNames);
+ final Table table = entry.getTable();
+ final String name2 = entry.name;
+ SqlValidatorTable table2 = null;
+ if (table instanceof Wrapper) {
+ table2 = ((Wrapper) table).unwrap(Prepare.PreparingTable.class);
+ }
+ if (table2 == null) {
+ table2 = RelOptTableImpl.create(null,
+ table.getRowType(validator.typeFactory), schema.add(name2, table),
+ null);
+ }
+ namespace = new TableNamespace(validator, table2);
+ resolved.found(namespace, false, this, path, remainingNames);
+ return;
+ }
+ // neither sub-schema nor table
+ if (namespace != null
+ && !remainingNames.equals(names)) {
+ resolved.found(namespace, false, this, path, remainingNames);
+ }
+ return;
+ }
+ }
+
public RelDataType nullifyType(SqlNode node, RelDataType type) {
return type;
}
@@ -105,6 +201,11 @@ class EmptyScope implements SqlValidatorScope {
RESOURCE.columnNotFound(columnName));
}
+ public Map<String, ScopeChild> findQualifyingTableNames(String columnName,
+ SqlNode ctx, SqlNameMatcher nameMatcher) {
+ return ImmutableMap.of();
+ }
+
public void addChild(SqlValidatorNamespace ns, String alias,
boolean nullable) {
// cannot add to the empty scope
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
index 5282ddb..f198389 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
@@ -27,6 +27,7 @@ import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.util.Pair;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -77,8 +78,7 @@ public class IdentifierNamespace extends AbstractNamespace {
super(validator, enclosingNode);
this.id = id;
this.extendList = extendList;
- this.parentScope = parentScope;
- assert parentScope != null;
+ this.parentScope = Preconditions.checkNotNull(parentScope);
}
IdentifierNamespace(SqlValidatorImpl validator, SqlNode node,
@@ -100,13 +100,75 @@ public class IdentifierNamespace extends AbstractNamespace {
}
}
- public RelDataType validateImpl(RelDataType targetRowType) {
- resolvedNamespace = parentScope.getTableNamespace(id.names);
- if (resolvedNamespace == null) {
- throw validator.newValidationError(id,
- RESOURCE.tableNameNotFound(id.toString()));
+ private SqlValidatorNamespace resolveImpl(SqlIdentifier id) {
+ final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher();
+ final SqlValidatorScope.ResolvedImpl resolved =
+ new SqlValidatorScope.ResolvedImpl();
+ final List<String> names = SqlIdentifier.toStar(id.names);
+ parentScope.resolveTable(names, nameMatcher,
+ SqlValidatorScope.Path.EMPTY, resolved);
+ SqlValidatorScope.Resolve previousResolve = null;
+ if (resolved.count() == 1) {
+ final SqlValidatorScope.Resolve resolve =
+ previousResolve = resolved.only();
+ if (resolve.remainingNames.isEmpty()) {
+ return resolve.namespace;
+ }
+ // If we're not case sensitive, give an error.
+ // If we're case sensitive, we'll shortly try again and give an error
+ // then.
+ if (!nameMatcher.isCaseSensitive()) {
+ throw validator.newValidationError(id,
+ RESOURCE.objectNotFoundWithin(resolve.remainingNames.get(0),
+ SqlIdentifier.getString(resolve.path.stepNames())));
+ }
+ }
+
+ // Failed to match. If we're matching case-sensitively, try a more
+ // lenient match. If we find something we can offer a helpful hint.
+ if (nameMatcher.isCaseSensitive()) {
+ final SqlNameMatcher liberalMatcher = SqlNameMatchers.liberal();
+ resolved.clear(); // TODO: remove?
+ parentScope.resolveTable(names, liberalMatcher,
+ SqlValidatorScope.Path.EMPTY, resolved);
+ if (resolved.count() == 1) {
+ final SqlValidatorScope.Resolve resolve = resolved.only();
+ if (resolve.remainingNames.isEmpty()
+ || previousResolve == null) {
+ // We didn't match it case-sensitive, so they must have had the
+ // right identifier, wrong case.
+ //
+ // If previousResolve is null, we matched nothing case-sensitive and
+ // everything case-insensitive, so the mismatch must have been at
+ // position 0.
+ final int i = previousResolve == null ? 0
+ : previousResolve.path.stepCount();
+ final int offset = resolve.path.stepCount()
+ + resolve.remainingNames.size() - names.size();
+ final List<String> prefix =
+ resolve.path.stepNames().subList(0, offset + i);
+ final String next = resolve.path.stepNames().get(i + offset);
+ if (prefix.isEmpty()) {
+ throw validator.newValidationError(id,
+ RESOURCE.objectNotFoundDidYouMean(names.get(i), next));
+ } else {
+ throw validator.newValidationError(id,
+ RESOURCE.objectNotFoundWithinDidYouMean(names.get(i),
+ SqlIdentifier.getString(prefix), next));
+ }
+ } else {
+ throw validator.newValidationError(id,
+ RESOURCE.objectNotFoundWithin(resolve.remainingNames.get(0),
+ SqlIdentifier.getString(resolve.path.stepNames())));
+ }
+ }
}
+ throw validator.newValidationError(id,
+ RESOURCE.objectNotFound(id.getComponent(0).toString()));
+ }
+ public RelDataType validateImpl(RelDataType targetRowType) {
+ resolvedNamespace = Preconditions.checkNotNull(resolveImpl(id));
if (resolvedNamespace instanceof TableNamespace) {
SqlValidatorTable table = resolvedNamespace.getTable();
if (validator.shouldExpandIdentifiers()) {
@@ -117,7 +179,7 @@ public class IdentifierNamespace extends AbstractNamespace {
// identifier, as best we can. We assume that qualification
// adds names to the front, e.g. FOO.BAR becomes BAZ.FOO.BAR.
List<SqlParserPos> poses =
- new ArrayList<SqlParserPos>(
+ new ArrayList<>(
Collections.nCopies(
qualifiedNames.size(), id.getParserPosition()));
int offset = qualifiedNames.size() - id.names.size();
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/ListScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/ListScope.java b/core/src/main/java/org/apache/calcite/sql/validate/ListScope.java
index 7c869c5..7228f63 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/ListScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/ListScope.java
@@ -79,18 +79,19 @@ public abstract class ListScope extends DelegatingScope {
return Lists.transform(children, ScopeChild.NAME_FN);
}
- private int findChild(List<String> names) {
+ private ScopeChild findChild(List<String> names,
+ SqlNameMatcher nameMatcher) {
for (ScopeChild child : children) {
String lastName = Util.last(names);
if (child.name != null) {
- if (!validator.catalogReader.matches(child.name, lastName)) {
+ if (!nameMatcher.matches(child.name, lastName)) {
// Alias does not match last segment. Don't consider the
// fully-qualified name. E.g.
// SELECT sales.emp.name FROM sales.emp AS otherAlias
continue;
}
if (names.size() == 1) {
- return child.ordinal;
+ return child;
}
}
@@ -98,15 +99,18 @@ public abstract class ListScope extends DelegatingScope {
// catalog & schema and the other is not.
final SqlValidatorTable table = child.namespace.getTable();
if (table != null) {
- final SqlValidatorTable table2 =
- validator.catalogReader.getTable(names);
- if (table2 != null
- && table.getQualifiedName().equals(table2.getQualifiedName())) {
- return child.ordinal;
+ final ResolvedImpl resolved = new ResolvedImpl();
+ resolveTable(names, nameMatcher, Path.EMPTY, resolved);
+ if (resolved.count() == 1
+ && resolved.only().remainingNames.isEmpty()
+ && resolved.only().namespace instanceof TableNamespace
+ && resolved.only().namespace.getTable().getQualifiedName().equals(
+ table.getQualifiedName())) {
+ return child;
}
}
}
- return -1;
+ return null;
}
public void findAllColumnNames(List<SqlMoniker> result) {
@@ -125,9 +129,12 @@ public abstract class ListScope extends DelegatingScope {
@Override public Pair<String, SqlValidatorNamespace>
findQualifyingTableName(final String columnName, SqlNode ctx) {
- final Map<String, ScopeChild> map = findQualifyingTables(columnName);
+ final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher();
+ final Map<String, ScopeChild> map =
+ findQualifyingTables(columnName, nameMatcher);
switch (map.size()) {
case 0:
+ //noinspection deprecation
return parent.findQualifyingTableName(columnName, ctx);
case 1:
final Map.Entry<String, ScopeChild> entry =
@@ -140,11 +147,25 @@ public abstract class ListScope extends DelegatingScope {
}
@Override public Map<String, ScopeChild>
- findQualifyingTables(String columnName) {
+ findQualifyingTableNames(String columnName, SqlNode ctx,
+ SqlNameMatcher nameMatcher) {
+ final Map<String, ScopeChild> map =
+ findQualifyingTables(columnName, nameMatcher);
+ switch (map.size()) {
+ case 0:
+ return parent.findQualifyingTableNames(columnName, ctx, nameMatcher);
+ default:
+ return map;
+ }
+ }
+
+ @Override public Map<String, ScopeChild>
+ findQualifyingTables(String columnName, SqlNameMatcher nameMatcher) {
final Map<String, ScopeChild> map = new HashMap<>();
for (ScopeChild child : children) {
final ResolvedImpl resolved = new ResolvedImpl();
- resolve(ImmutableList.of(child.name, columnName), true, resolved);
+ resolve(ImmutableList.of(child.name, columnName), nameMatcher, true,
+ resolved);
if (resolved.count() > 0) {
map.put(child.name, child);
}
@@ -152,15 +173,15 @@ public abstract class ListScope extends DelegatingScope {
return map;
}
- @Override public void resolve(List<String> names, boolean deep,
- Resolved resolved) {
+ @Override public void resolve(List<String> names, SqlNameMatcher nameMatcher,
+ boolean deep, Resolved resolved) {
// First resolve by looking through the child namespaces.
- final int i = findChild(names);
- if (i >= 0) {
+ final ScopeChild child0 = findChild(names, nameMatcher);
+ if (child0 != null) {
final Step path =
- resolved.emptyPath().add(null, i, StructKind.FULLY_QUALIFIED);
- final ScopeChild child = children.get(i);
- resolved.found(child.namespace, child.nullable, this, path);
+ Path.EMPTY.plus(child0.namespace.getRowType(), child0.ordinal,
+ child0.name, StructKind.FULLY_QUALIFIED);
+ resolved.found(child0.namespace, child0.nullable, this, path, null);
return;
}
@@ -170,11 +191,11 @@ public abstract class ListScope extends DelegatingScope {
for (ScopeChild child : children) {
// If identifier starts with table alias, remove the alias.
final List<String> names2 =
- validator.catalogReader.matches(child.name, names.get(0))
+ nameMatcher.matches(child.name, names.get(0))
? names.subList(1, names.size())
: names;
- resolveInNamespace(child.namespace, child.nullable, names2,
- resolved.emptyPath(), resolved);
+ resolveInNamespace(child.namespace, child.nullable, names2, nameMatcher,
+ Path.EMPTY, resolved);
}
if (resolved.count() > 0) {
return;
@@ -183,17 +204,18 @@ public abstract class ListScope extends DelegatingScope {
// Then call the base class method, which will delegate to the
// parent scope.
- super.resolve(names, deep, resolved);
+ super.resolve(names, nameMatcher, deep, resolved);
}
public RelDataType resolveColumn(String columnName, SqlNode ctx) {
+ final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher();
int found = 0;
RelDataType type = null;
for (ScopeChild child : children) {
SqlValidatorNamespace childNs = child.namespace;
final RelDataType childRowType = childNs.getRowType();
final RelDataTypeField field =
- validator.catalogReader.field(childRowType, columnName);
+ nameMatcher.field(childRowType, columnName);
if (field != null) {
found++;
type = field.getType();
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java b/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java
index 65958fd..117390f 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java
@@ -74,7 +74,8 @@ public class OrderByScope extends DelegatingScope {
validator.getNamespace(select);
final RelDataType rowType = selectNs.getRowType();
- final RelDataTypeField field = validator.catalogReader.field(rowType, name);
+ final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher();
+ final RelDataTypeField field = nameMatcher.field(rowType, name);
if (field != null && !field.isDynamicStar()) {
// if identifier is resolved to a dynamic star, use super.fullyQualify() for such case.
return SqlQualified.create(this, 1, selectNs, identifier);
@@ -86,7 +87,8 @@ public class OrderByScope extends DelegatingScope {
public RelDataType resolveColumn(String name, SqlNode ctx) {
final SqlValidatorNamespace selectNs = validator.getNamespace(select);
final RelDataType rowType = selectNs.getRowType();
- final RelDataTypeField field = validator.catalogReader.field(rowType, name);
+ final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher();
+ final RelDataTypeField field = nameMatcher.field(rowType, name);
if (field != null) {
return field.getType();
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java
new file mode 100644
index 0000000..104f3d2
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java
@@ -0,0 +1,62 @@
+/*
+ * 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 org.apache.calcite.sql.validate;
+
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Checks whether two names are the same according to a case-sensitivity policy.
+ *
+ * @see SqlNameMatchers
+ */
+public interface SqlNameMatcher {
+ /** Returns whether name matching is case-sensitive. */
+ boolean isCaseSensitive();
+
+ /** Returns a name matches another.
+ *
+ * @param string Name written in code
+ * @param name Name of object we are trying to match
+ * @return Whether matches
+ */
+ boolean matches(String string, String name);
+
+ /** Looks up an item in a map. */
+ <K extends List<String>, V> V get(Map<K, V> map, List<String> prefixNames,
+ List<String> names);
+
+ /** Returns the most recent match.
+ *
+ * <p>In the default implementation,
+ * throws {@link UnsupportedOperationException}. */
+ String bestString();
+
+ /** Finds a field with a given name, using the currenct case-sensitivity,
+ * returning null if not found.
+ *
+ * @param rowType Row type
+ * @param fieldName Field name
+ * @return Field, or null if not found
+ */
+ RelDataTypeField field(RelDataType rowType, String fieldName);
+}
+
+// End SqlNameMatcher.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatchers.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatchers.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatchers.java
new file mode 100644
index 0000000..0ccc09f
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatchers.java
@@ -0,0 +1,154 @@
+/*
+ * 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 org.apache.calcite.sql.validate;
+
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.util.Util;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helpers for {@link SqlNameMatcher}.
+ */
+public class SqlNameMatchers {
+
+ private static final BaseMatcher CASE_SENSITIVE = new BaseMatcher(true);
+ private static final BaseMatcher CASE_INSENSITIVE = new BaseMatcher(false);
+
+ private SqlNameMatchers() {}
+
+ /** Returns a name matcher with the given case sensitivity. */
+ public static SqlNameMatcher withCaseSensitive(final boolean caseSensitive) {
+ return caseSensitive ? CASE_SENSITIVE : CASE_INSENSITIVE;
+ }
+
+ /** Creates a name matcher that can suggest corrections to what the user
+ * typed. It matches liberally (case-insensitively) and also records the last
+ * match. */
+ public static SqlNameMatcher liberal() {
+ return new LiberalNameMatcher();
+ }
+
+ /** Partial implementation of {@link SqlNameMatcher}. */
+ private static class BaseMatcher implements SqlNameMatcher {
+ private final boolean caseSensitive;
+
+ BaseMatcher(boolean caseSensitive) {
+ this.caseSensitive = caseSensitive;
+ }
+
+ public boolean isCaseSensitive() {
+ return caseSensitive;
+ }
+
+ public boolean matches(String string, String name) {
+ return caseSensitive ? string.equals(name)
+ : string.equalsIgnoreCase(name);
+ }
+
+ protected boolean listMatches(List<String> list0, List<String> list1) {
+ if (list0.size() != list1.size()) {
+ return false;
+ }
+ for (int i = 0; i < list0.size(); i++) {
+ String s0 = list0.get(i);
+ String s1 = list1.get(i);
+ if (!matches(s0, s1)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public <K extends List<String>, V> V get(Map<K, V> map,
+ List<String> prefixNames, List<String> names) {
+ final List<String> key = concat(prefixNames, names);
+ if (caseSensitive) {
+ //noinspection SuspiciousMethodCalls
+ return map.get(key);
+ }
+ for (Map.Entry<K, V> entry : map.entrySet()) {
+ if (listMatches(key, entry.getKey())) {
+ matched(prefixNames, entry.getKey());
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ private List<String> concat(List<String> prefixNames, List<String> names) {
+ if (prefixNames.isEmpty()) {
+ return names;
+ } else {
+ return ImmutableList.<String>builder().addAll(prefixNames).addAll(names)
+ .build();
+ }
+ }
+
+ protected void matched(List<String> prefixNames, List<String> names) {
+ }
+
+ protected List<String> bestMatch() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String bestString() {
+ return SqlIdentifier.getString(bestMatch());
+ }
+
+ public RelDataTypeField field(RelDataType rowType, String fieldName) {
+ return rowType.getField(fieldName, caseSensitive, false);
+ }
+ }
+
+ /** Matcher that remembers the requests that were made of it. */
+ private static class LiberalNameMatcher extends BaseMatcher {
+ List<String> matchedNames;
+
+ LiberalNameMatcher() {
+ super(false);
+ }
+
+ @Override protected boolean listMatches(List<String> list0,
+ List<String> list1) {
+ final boolean b = super.listMatches(list0, list1);
+ if (b) {
+ matchedNames = ImmutableList.copyOf(list1);
+ }
+ return b;
+ }
+
+ @Override protected void matched(List<String> prefixNames,
+ List<String> names) {
+ matchedNames = ImmutableList.copyOf(
+ Util.startsWith(names, prefixNames)
+ ? Util.skip(names, prefixNames.size())
+ : names);
+ }
+
+ @Override public List<String> bestMatch() {
+ return matchedNames;
+ }
+ }
+}
+
+// End SqlNameMatchers.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/SqlQualified.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlQualified.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlQualified.java
index 55c1d01..52d4e1b 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlQualified.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlQualified.java
@@ -60,10 +60,13 @@ public class SqlQualified {
if (scope == null) {
return identifier.names;
}
+ final SqlNameMatcher nameMatcher =
+ scope.getValidator().getCatalogReader().nameMatcher();
final ImmutableList.Builder<String> builder = ImmutableList.builder();
final SqlValidatorScope.ResolvedImpl resolved =
new SqlValidatorScope.ResolvedImpl();
- scope.resolve(Util.skipLast(identifier.names), false, resolved);
+ final List<String> prefix = Util.skipLast(identifier.names);
+ scope.resolve(prefix, nameMatcher, false, resolved);
SqlValidatorNamespace namespace =
resolved.count() == 1 ? resolved.only().namespace : null;
builder.add(identifier.names.get(0));
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorCatalogReader.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorCatalogReader.java
index b43f85c..a71f8f4 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorCatalogReader.java
@@ -16,6 +16,7 @@
*/
package org.apache.calcite.sql.validate;
+import org.apache.calcite.jdbc.CalciteSchema;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.sql.SqlIdentifier;
@@ -35,10 +36,17 @@ public interface SqlValidatorCatalogReader {
//~ Methods ----------------------------------------------------------------
/**
- * Finds a table with the given name, possibly qualified.
+ * Finds a table or schema with the given name, possibly qualified.
*
- * @param names Qualified name of table
- * @return named table, or null if not found
+ * <p>Uses the case-sensitivity policy of the catalog reader.
+ *
+ * <p>If not found, returns null. If you want a more descriptive error
+ * message or to override the case-sensitivity of the match, use
+ * {@link SqlValidatorScope#resolveTable}.
+ *
+ * @param names Name of table, may be qualified or fully-qualified
+ *
+ * @return Table with the given name, or null
*/
SqlValidatorTable getTable(List<String> names);
@@ -68,24 +76,37 @@ public interface SqlValidatorCatalogReader {
List<SqlMoniker> getAllSchemaObjectNames(List<String> names);
/**
- * Returns the name of the current schema.
+ * Returns the paths of all schemas to look in for tables.
*
- * @return name of the current schema
+ * @return paths of current schema and root schema
*/
- List<String> getSchemaName();
+ List<List<String>> getSchemaPaths();
- /**
- * Finds a field with a given name, using the case-sensitivity of the current
- * session.
- */
+ /** @deprecated Use
+ * {@link #nameMatcher()}.{@link SqlNameMatcher#field(RelDataType, String)} */
+ @Deprecated // to be removed before 2.0
RelDataTypeField field(RelDataType rowType, String alias);
+ /** Returns an implementation of
+ * {@link org.apache.calcite.sql.validate.SqlNameMatcher}
+ * that matches the case-sensitivity policy. */
+ SqlNameMatcher nameMatcher();
+
+ /** @deprecated Use
+ * {@link #nameMatcher()}.{@link SqlNameMatcher#matches(String, String)} */
+ @Deprecated // to be removed before 2.0
boolean matches(String string, String name);
RelDataType createTypeFromProjection(RelDataType type,
List<String> columnNameList);
+ /** @deprecated Use
+ * {@link #nameMatcher()}.{@link SqlNameMatcher#isCaseSensitive()} */
+ @Deprecated // to be removed before 2.0
boolean isCaseSensitive();
+
+ /** Returns the root namespace for name resolution. */
+ CalciteSchema getRootSchema();
}
// End SqlValidatorCatalogReader.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/5f9c0190/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index 7b2645c..3766258 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -320,9 +320,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
select,
unknownType,
list,
- catalogReader.isCaseSensitive()
- ? new LinkedHashSet<String>()
- : new TreeSet<String>(String.CASE_INSENSITIVE_ORDER),
+ catalogReader.nameMatcher().isCaseSensitive()
+ ? new LinkedHashSet<String>()
+ : new TreeSet<>(String.CASE_INSENSITIVE_ORDER),
types,
includeSystemVars);
}
@@ -504,7 +504,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final SqlIdentifier prefixId = identifier.skipLast(1);
final SqlValidatorScope.ResolvedImpl resolved =
new SqlValidatorScope.ResolvedImpl();
- scope.resolve(prefixId.names, true, resolved);
+ final SqlNameMatcher nameMatcher =
+ scope.validator.catalogReader.nameMatcher();
+ scope.resolve(prefixId.names, nameMatcher, true, resolved);
if (resolved.count() == 0) {
// e.g. "select s.t.* from e"
// or "select r.* from e"
@@ -737,7 +739,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
if (ns == null) {
final SqlValidatorScope.ResolvedImpl resolved =
new SqlValidatorScope.ResolvedImpl();
- scope.resolve(ImmutableList.of(name), false, resolved);
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
+ scope.resolve(ImmutableList.of(name), nameMatcher, false, resolved);
if (resolved.count() == 1) {
ns = resolved.only().namespace;
}
@@ -971,9 +974,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final SqlValidatorScope parentScope =
((DelegatingScope) scope).getParent();
if (id.isSimple()) {
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
final SqlValidatorScope.ResolvedImpl resolved =
new SqlValidatorScope.ResolvedImpl();
- parentScope.resolve(id.names, false, resolved);
+ parentScope.resolve(id.names, nameMatcher, false, resolved);
if (resolved.count() == 1) {
return resolved.only().namespace;
}
@@ -2929,11 +2933,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
rightRowType);
// Check compatibility of the chosen columns.
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
for (String name : naturalColumnNames) {
final RelDataType leftColType =
- catalogReader.field(leftRowType, name).getType();
+ nameMatcher.field(leftRowType, name).getType();
final RelDataType rightColType =
- catalogReader.field(rightRowType, name).getType();
+ nameMatcher.field(rightRowType, name).getType();
if (!SqlTypeUtil.isComparable(leftColType, rightColType)) {
throw newValidationError(join,
RESOURCE.naturalOrUsingColumnNotCompatible(name,
@@ -2994,7 +2999,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
String name = id.names.get(0);
final SqlValidatorNamespace namespace = getNamespace(leftOrRight);
final RelDataType rowType = namespace.getRowType();
- final RelDataTypeField field = catalogReader.field(rowType, name);
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
+ final RelDataTypeField field = nameMatcher.field(rowType, name);
if (field != null) {
if (Collections.frequency(rowType.getFieldNames(), name) > 1) {
throw newValidationError(id,
@@ -3053,7 +3059,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// Make sure that items in FROM clause have distinct aliases.
final SelectScope fromScope = (SelectScope) getFromScope(select);
List<String> names = fromScope.getChildNames();
- if (!catalogReader.isCaseSensitive()) {
+ if (!catalogReader.nameMatcher().isCaseSensitive()) {
names = Lists.transform(names,
new Function<String, String>() {
public String apply(String s) {
@@ -3325,24 +3331,24 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
public void validateSequenceValue(SqlValidatorScope scope, SqlIdentifier id) {
// Resolve identifier as a table.
- final SqlValidatorNamespace ns = scope.getTableNamespace(id.names);
- if (ns == null) {
+ final SqlValidatorScope.ResolvedImpl resolved =
+ new SqlValidatorScope.ResolvedImpl();
+ scope.resolveTable(id.names, catalogReader.nameMatcher(),
+ SqlValidatorScope.Path.EMPTY, resolved);
+ if (resolved.count() != 1) {
throw newValidationError(id, RESOURCE.tableNameNotFound(id.toString()));
}
-
// We've found a table. But is it a sequence?
- if (!(ns instanceof TableNamespace)) {
- throw newValidationError(id, RESOURCE.notASequence(id.toString()));
- }
- final SqlValidatorTable table = ns.getTable();
- final Table table1 = ((RelOptTable) table).unwrap(Table.class);
- switch (table1.getJdbcTableType()) {
- case SEQUENCE:
- case TEMPORARY_SEQUENCE:
- break;
- default:
- throw newValidationError(id, RESOURCE.notASequence(id.toString()));
+ final SqlValidatorNamespace ns = resolved.only().namespace;
+ if (ns instanceof TableNamespace) {
+ final Table table = ((RelOptTable) ns.getTable()).unwrap(Table.class);
+ switch (table.getJdbcTableType()) {
+ case SEQUENCE:
+ case TEMPORARY_SEQUENCE:
+ return;
+ }
}
+ throw newValidationError(id, RESOURCE.notASequence(id.toString()));
}
public SqlValidatorScope getWithScope(SqlNode withItem) {
@@ -4139,7 +4145,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) {
- final RelDataTypeField field = catalogReader.field(rowType, name);
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
+ final RelDataTypeField field = nameMatcher.field(rowType, name);
return new FieldNamespace(this, field.getType());
}
@@ -4480,9 +4487,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// we could do a better job if they were looked up via
// resolveColumn.
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
final SqlValidatorScope.ResolvedImpl resolved =
new SqlValidatorScope.ResolvedImpl();
- scope.resolve(id.names.subList(0, i), false, resolved);
+ scope.resolve(id.names.subList(0, i), nameMatcher, false, resolved);
if (resolved.count() == 1) {
// There's a namespace with the name we seek.
final SqlValidatorScope.Resolve resolve = resolved.only();
@@ -4522,7 +4530,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
name = "*";
field = null;
} else {
- field = catalogReader.field(type, name);
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
+ field = nameMatcher.field(type, name);
}
if (field == null) {
throw newValidationError(id.getComponent(i),
@@ -4696,8 +4705,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final SqlValidatorNamespace selectNs = getNamespace(select);
final RelDataType rowType =
selectNs.getRowTypeSansSystemColumns();
- RelDataTypeField field =
- catalogReader.field(rowType, alias);
+ final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
+ RelDataTypeField field = nameMatcher.field(rowType, alias);
if (field != null) {
return nthSelectItem(
field.getIndex(),