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 2016/09/16 04:48:19 UTC
[5/6] calcite git commit: Complete [CALCITE-1208] by giving "resolve"
a call-back, so it can make multiple matches
Complete [CALCITE-1208] by giving "resolve" a call-back, so it can make multiple matches
Change parser to make 'SELECT t.*.c' illegal.
Address Jinfeng's review comments.
Add a test for converting a query with UNION in FROM clause.
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/0938c7b6
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/0938c7b6
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/0938c7b6
Branch: refs/heads/master
Commit: 0938c7b6d767e3242874d87a30d9112512d9243a
Parents: a859f93
Author: Julian Hyde <jh...@apache.org>
Authored: Fri May 6 11:57:25 2016 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Sep 15 17:02:42 2016 -0700
----------------------------------------------------------------------
core/src/main/codegen/templates/Parser.jj | 25 +-
.../calcite/prepare/CalciteCatalogReader.java | 15 +-
.../AggregateExpandDistinctAggregatesRule.java | 2 +-
.../calcite/rel/type/DynamicRecordTypeImpl.java | 41 ++-
.../apache/calcite/rel/type/RelDataType.java | 14 +-
.../calcite/rel/type/RelDataTypeFactory.java | 36 ++-
.../rel/type/RelDataTypeFactoryImpl.java | 61 +++-
.../calcite/rel/type/RelDataTypeHolder.java | 27 +-
.../calcite/rel/type/RelDataTypeImpl.java | 58 +---
.../apache/calcite/rel/type/RelRecordType.java | 36 ++-
.../org/apache/calcite/rel/type/StructKind.java | 83 ++++++
.../apache/calcite/runtime/CalciteResource.java | 3 +
.../org/apache/calcite/runtime/FlatLists.java | 41 +++
.../org/apache/calcite/sql/SqlIdentifier.java | 16 +
.../calcite/sql/validate/CatalogScope.java | 10 -
.../calcite/sql/validate/DelegatingScope.java | 265 +++++++++++++----
.../apache/calcite/sql/validate/EmptyScope.java | 6 +-
.../apache/calcite/sql/validate/ListScope.java | 136 +++++----
.../calcite/sql/validate/ParameterScope.java | 9 -
.../calcite/sql/validate/SqlQualified.java | 5 +-
.../sql/validate/SqlValidatorCatalogReader.java | 8 -
.../calcite/sql/validate/SqlValidatorImpl.java | 74 +++--
.../calcite/sql/validate/SqlValidatorScope.java | 120 +++++++-
.../calcite/sql/validate/SqlValidatorUtil.java | 48 ++-
.../apache/calcite/sql/validate/WithScope.java | 15 +-
.../calcite/sql2rel/SqlToRelConverter.java | 61 ++--
.../apache/calcite/util/ImmutableIntList.java | 6 +-
.../main/java/org/apache/calcite/util/Pair.java | 5 +
.../calcite/runtime/CalciteResource.properties | 1 +
.../calcite/sql/parser/SqlParserTest.java | 15 +
.../apache/calcite/sql/test/SqlAdvisorTest.java | 2 +
.../apache/calcite/test/MockCatalogReader.java | 162 ++++++----
.../calcite/test/SqlToRelConverterTest.java | 10 +-
.../apache/calcite/test/SqlValidatorTest.java | 294 +++++++++++++------
.../calcite/test/SqlToRelConverterTest.xml | 31 ++
.../adapter/splunk/SplunkPushDownRule.java | 13 +-
36 files changed, 1165 insertions(+), 589 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/codegen/templates/Parser.jj
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj
index 43d6cd5..2d97cf3 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -3686,20 +3686,19 @@ SqlIdentifier CompoundIdentifier() :
}
(
<DOT>
- (
- p = Identifier() {
- star = false;
- list.add(p);
- posList.add(getPos());
- }
- |
- <STAR> {
- star = true;
- list.add("*");
- posList.add(getPos());
- }
- )
+ p = Identifier() {
+ list.add(p);
+ posList.add(getPos());
+ }
) *
+ (
+ <DOT>
+ <STAR> {
+ star = true;
+ list.add("");
+ posList.add(getPos());
+ }
+ )?
{
SqlParserPos pos = SqlParserPos.sum(posList);
if (star) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 9d8f3ef..0e14c83 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
@@ -74,7 +74,6 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
final CalciteSchema rootSchema;
final JavaTypeFactory typeFactory;
private final List<String> defaultSchema;
- private final boolean elideRecord = true;
private final boolean caseSensitive;
public CalciteCatalogReader(
@@ -203,27 +202,17 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
}
public RelDataTypeField field(RelDataType rowType, String alias) {
- return SqlValidatorUtil.lookupField(caseSensitive, elideRecord, rowType,
- alias);
- }
-
- public int fieldOrdinal(RelDataType rowType, String alias) {
- RelDataTypeField field = field(rowType, alias);
- return field != null ? field.getIndex() : -1;
+ return SqlValidatorUtil.lookupField(caseSensitive, rowType, alias);
}
public boolean matches(String string, String name) {
return Util.matches(caseSensitive, string, name);
}
- public int match(List<String> strings, String name) {
- return Util.findMatch(strings, name, caseSensitive);
- }
-
public RelDataType createTypeFromProjection(final RelDataType type,
final List<String> columnNameList) {
return SqlValidatorUtil.createTypeFromProjection(type, columnNameList,
- typeFactory, caseSensitive, elideRecord);
+ typeFactory, caseSensitive);
}
public void lookupOperatorOverloads(final SqlIdentifier opName,
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
index a5f27f7..de12e42 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
@@ -571,7 +571,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
List<Integer> argList) {
ImmutableIntList list = ImmutableIntList.of();
for (int arg : argList) {
- list = list.add(remap(groupSet, arg));
+ list = list.append(remap(groupSet, arg));
}
return list;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/rel/type/DynamicRecordTypeImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/DynamicRecordTypeImpl.java b/core/src/main/java/org/apache/calcite/rel/type/DynamicRecordTypeImpl.java
index 21206ac..82b6714 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/DynamicRecordTypeImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/DynamicRecordTypeImpl.java
@@ -24,35 +24,34 @@ import java.util.Collections;
import java.util.List;
/**
- * Implementation of RelDataType for dynamic table. It's used in
- * Sql validation phase, where field list is mutable for getField() call.
+ * Implementation of {@link RelDataType} for a dynamic table.
*
- * <p>After Sql validation, a normal RelDataTypeImpl with immutable field list
- * would take place of DynamicRecordTypeImpl instance for dynamic table. </p>
+ * <p>It's used during SQL validation, where the field list is mutable for
+ * the getField() call. After SQL validation, a normal {@link RelDataTypeImpl}
+ * with an immutable field list takes the place of the DynamicRecordTypeImpl
+ * instance.
*/
public class DynamicRecordTypeImpl extends DynamicRecordType {
-
- private final RelDataTypeFactory typeFactory;
private final RelDataTypeHolder holder;
+ /** Creates a DynamicRecordTypeImpl. */
public DynamicRecordTypeImpl(RelDataTypeFactory typeFactory) {
- this.typeFactory = typeFactory;
- this.holder = new RelDataTypeHolder();
- this.holder.setRelDataTypeFactory(typeFactory);
+ this.holder = new RelDataTypeHolder(typeFactory);
computeDigest();
}
- public List<RelDataTypeField> getFieldList() {
- return holder.getFieldList(typeFactory);
+ @Override public List<RelDataTypeField> getFieldList() {
+ return holder.getFieldList();
}
- public int getFieldCount() {
+ @Override public int getFieldCount() {
return holder.getFieldCount();
}
- public RelDataTypeField getField(String fieldName, boolean caseSensitive, boolean elideRecord) {
- Pair<RelDataTypeField, Boolean> pair = holder.getFieldOrInsert(typeFactory, fieldName,
- caseSensitive);
+ @Override public RelDataTypeField getField(String fieldName,
+ boolean caseSensitive, boolean elideRecord) {
+ final Pair<RelDataTypeField, Boolean> pair =
+ holder.getFieldOrInsert(fieldName, caseSensitive);
// If a new field is added, we should re-compute the digest.
if (pair.right) {
computeDigest();
@@ -61,27 +60,27 @@ public class DynamicRecordTypeImpl extends DynamicRecordType {
return pair.left;
}
- public List<String> getFieldNames() {
+ @Override public List<String> getFieldNames() {
return holder.getFieldNames();
}
- public SqlTypeName getSqlTypeName() {
+ @Override public SqlTypeName getSqlTypeName() {
return SqlTypeName.ROW;
}
- public RelDataTypePrecedenceList getPrecedenceList() {
+ @Override public RelDataTypePrecedenceList getPrecedenceList() {
return new SqlTypeExplicitPrecedenceList(Collections.<SqlTypeName>emptyList());
}
protected void generateTypeString(StringBuilder sb, boolean withDetail) {
- sb.append("(DynamicRecordRow" + getFieldNames() + ")");
+ sb.append("(DynamicRecordRow").append(getFieldNames()).append(")");
}
- public boolean isStruct() {
+ @Override public boolean isStruct() {
return true;
}
- public RelDataTypeFamily getFamily() {
+ @Override public RelDataTypeFamily getFamily() {
return getSqlTypeName().getFamily();
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
index 442538e..7b18d67 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
@@ -36,12 +36,6 @@ public interface RelDataType /*extends Type*/ {
int SCALE_NOT_SPECIFIED = Integer.MIN_VALUE;
int PRECISION_NOT_SPECIFIED = -1;
- enum StructKind {
- FULLY_QUALIFIED,
- PEEK_FIELDS,
- PEEK_FIELDS_DEFAULT,
- }
-
//~ Methods ----------------------------------------------------------------
/**
@@ -83,9 +77,10 @@ public interface RelDataType /*extends Type*/ {
int getFieldCount();
/**
- * Gets the StructKind of a structured type.
+ * Returns the rule for resolving the fields of a structured type,
+ * or {@link StructKind#NONE} if this is not a structured type.
*
- * @return the StructKind that determines how its fields are resolved.
+ * @return the StructKind that determines how this type's fields are resolved
*/
StructKind getStructKind();
@@ -242,9 +237,10 @@ public interface RelDataType /*extends Type*/ {
RelDataTypeComparability getComparability();
/**
- *@return whether it has dynamic structure (for "schema-on-read" table)
+ * @return whether it has dynamic structure (for "schema-on-read" table)
*/
boolean isDynamicStruct();
+
}
// End RelDataType.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java
index 7c3e88c..1b3a1be 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java
@@ -50,7 +50,7 @@ public interface RelDataTypeFactory {
RelDataTypeSystem getTypeSystem();
/**
- * Creates a type which corresponds to a Java class.
+ * Creates a type that corresponds to a Java class.
*
* @param clazz the Java class used to define the type
* @return canonical Java type descriptor
@@ -66,28 +66,37 @@ public interface RelDataTypeFactory {
RelDataType createJoinType(RelDataType... types);
/**
- * Creates a type which represents a structured collection of fields, given
+ * Creates a type that represents a structured collection of fields, given
* lists of the names and types of the fields.
*
+ * @param kind Name resolution policy
* @param typeList types of the fields
* @param fieldNameList names of the fields
* @return canonical struct type descriptor
*/
+ RelDataType createStructType(StructKind kind,
+ List<RelDataType> typeList,
+ List<String> fieldNameList);
+
+ /** Creates a type that represents a structured collection of fields.
+ * Shorthand for <code>createStructType(StructKind.FULLY_QUALIFIED, typeList,
+ * fieldNameList)</code>. */
RelDataType createStructType(
List<RelDataType> typeList,
List<String> fieldNameList);
/**
- * Creates a type which represents a structured collection of fields,
+ * Creates a type that represents a structured collection of fields,
* obtaining the field information via a callback.
*
* @param fieldInfo callback for field information
* @return canonical struct type descriptor
*/
+ @Deprecated // to be removed before 2.0
RelDataType createStructType(FieldInfo fieldInfo);
/**
- * Creates a type which represents a structured collection of fieldList,
+ * Creates a type that represents a structured collection of fieldList,
* obtaining the field information from a list of (name, type) pairs.
*
* @param fieldList List of (name, type) pairs
@@ -140,7 +149,7 @@ public interface RelDataTypeFactory {
RelDataType copyType(RelDataType type);
/**
- * Creates a type which is the same as another type but with possibly
+ * Creates a type that is the same as another type but with possibly
* different nullability. The output type may be identical to the input
* type. For type systems without a concept of nullability, the return value
* is always the same as the input.
@@ -156,7 +165,7 @@ public interface RelDataTypeFactory {
boolean nullable);
/**
- * Creates a Type which is the same as another type but with possibly
+ * Creates a type that is the same as another type but with possibly
* different charset or collation. For types without a concept of charset or
* collation this function must throw an error.
*
@@ -179,7 +188,7 @@ public interface RelDataTypeFactory {
/**
* Returns the most general of a set of types (that is, one type to which
* they can all be cast), or null if conversion is not possible. The result
- * may be a new type which is less restrictive than any of the input types,
+ * may be a new type that is less restrictive than any of the input types,
* e.g. <code>leastRestrictive(INT, NUMERIC(3, 2))</code> could be
* {@code NUMERIC(12, 2)}.
*
@@ -288,8 +297,9 @@ public interface RelDataTypeFactory {
//~ Inner Interfaces -------------------------------------------------------
/**
- * Callback which provides enough information to create fields.
+ * Callback that provides enough information to create fields.
*/
+ @Deprecated // to be removed before 2.0
interface FieldInfo {
/**
* Returns the number of fields.
@@ -319,10 +329,11 @@ public interface RelDataTypeFactory {
* Implementation of {@link FieldInfo} that provides a fluid API to build
* a list of fields.
*/
+ @SuppressWarnings("deprecation")
class FieldInfoBuilder implements FieldInfo {
private final List<String> names = new ArrayList<>();
private final List<RelDataType> types = new ArrayList<>();
-
+ private StructKind kind = StructKind.FULLY_QUALIFIED;
private final RelDataTypeFactory typeFactory;
/**
@@ -418,6 +429,11 @@ public interface RelDataTypeFactory {
return this;
}
+ public FieldInfoBuilder kind(StructKind kind) {
+ this.kind = kind;
+ return this;
+ }
+
/**
* Makes sure that field names are unique.
*/
@@ -435,7 +451,7 @@ public interface RelDataTypeFactory {
* Creates a struct type with the current contents of this builder.
*/
public RelDataType build() {
- return typeFactory.createStructType(types, names);
+ return typeFactory.createStructType(kind, types, names);
}
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
index eaff896..774a728 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
@@ -22,7 +22,6 @@ import org.apache.calcite.sql.type.JavaToSqlTypeConversionRules;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
-import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
import com.google.common.base.Preconditions;
@@ -41,6 +40,8 @@ import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import javax.annotation.Nonnull;
/**
* Abstract base for implementations of {@link RelDataTypeFactory}.
@@ -56,21 +57,20 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
.softValues()
.build(
new CacheLoader<Object, RelDataType>() {
- @Override public RelDataType load(Object key) {
- if (key instanceof RelDataType) {
- return (RelDataType) key;
+ @Override public RelDataType load(@Nonnull Object k) {
+ if (k instanceof RelDataType) {
+ return (RelDataType) k;
}
@SuppressWarnings("unchecked")
- final Pair<List<String>, List<RelDataType>> pair =
- (Pair<List<String>, List<RelDataType>>) key;
+ final Key key = (Key) k;
final ImmutableList.Builder<RelDataTypeField> list =
ImmutableList.builder();
- for (int i = 0; i < pair.left.size(); i++) {
+ for (int i = 0; i < key.names.size(); i++) {
list.add(
new RelDataTypeFieldImpl(
- pair.left.get(i), i, pair.right.get(i)));
+ key.names.get(i), i, key.types.get(i)));
}
- return new RelRecordType(list.build());
+ return new RelRecordType(key.kind, list.build());
}
});
@@ -132,18 +132,24 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
new RelCrossType(flattenedTypes, getFieldList(flattenedTypes)));
}
- // implement RelDataTypeFactory
public RelDataType createStructType(
final List<RelDataType> typeList,
final List<String> fieldNameList) {
+ return createStructType(StructKind.FULLY_QUALIFIED, typeList,
+ fieldNameList);
+ }
+
+ public RelDataType createStructType(StructKind kind,
+ final List<RelDataType> typeList,
+ final List<String> fieldNameList) {
assert typeList.size() == fieldNameList.size();
- return canonize(fieldNameList, typeList);
+ return canonize(kind, fieldNameList, typeList);
}
// implement RelDataTypeFactory
public RelDataType createStructType(
final RelDataTypeFactory.FieldInfo fieldInfo) {
- return canonize(
+ return canonize(StructKind.FULLY_QUALIFIED,
new AbstractList<String>() {
@Override public String get(int index) {
return fieldInfo.getFieldName(index);
@@ -346,16 +352,16 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
* key is more expensive, because it must be immutable and not hold
* references into other data structures.</p>
*/
- protected RelDataType canonize(
+ protected RelDataType canonize(final StructKind kind,
final List<String> names,
final List<RelDataType> types) {
- final RelDataType type = CACHE.getIfPresent(Pair.of(names, types));
+ final RelDataType type = CACHE.getIfPresent(new Key(kind, names, types));
if (type != null) {
return type;
}
final ImmutableList<String> names2 = ImmutableList.copyOf(names);
final ImmutableList<RelDataType> types2 = ImmutableList.copyOf(types);
- return CACHE.getUnchecked(Pair.of(names2, types2));
+ return CACHE.getUnchecked(new Key(kind, names2, types2));
}
/**
@@ -646,6 +652,31 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
return typeName;
}
}
+
+ /** Key to the data type cache. */
+ private static class Key {
+ private final StructKind kind;
+ private final List<String> names;
+ private final List<RelDataType> types;
+
+ Key(StructKind kind, List<String> names, List<RelDataType> types) {
+ this.kind = kind;
+ this.names = names;
+ this.types = types;
+ }
+
+ @Override public int hashCode() {
+ return Objects.hash(kind, names, types);
+ }
+
+ @Override public boolean equals(Object obj) {
+ return obj == this
+ || obj instanceof Key
+ && kind == ((Key) obj).kind
+ && names.equals(((Key) obj).names)
+ && types.equals(((Key) obj).types);
+ }
+ }
}
// End RelDataTypeFactoryImpl.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeHolder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeHolder.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeHolder.java
index 414045d..1a777ff 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeHolder.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeHolder.java
@@ -26,12 +26,15 @@ import java.util.List;
/**
* Holding the expandable list of fields for dynamic table.
*/
-public class RelDataTypeHolder {
- List<RelDataTypeField> fields = new ArrayList<>();
+class RelDataTypeHolder {
+ private final List<RelDataTypeField> fields = new ArrayList<>();
+ private final RelDataTypeFactory typeFactory;
- private RelDataTypeFactory typeFactory;
+ RelDataTypeHolder(RelDataTypeFactory typeFactory) {
+ this.typeFactory = typeFactory;
+ }
- public List<RelDataTypeField> getFieldList(RelDataTypeFactory typeFactory) {
+ public List<RelDataTypeField> getFieldList() {
return fields;
}
@@ -43,15 +46,12 @@ public class RelDataTypeHolder {
* Get field if exists, otherwise inserts a new field. The new field by default will have "any"
* type, except for the dynamic star field.
*
- * @param typeFactory RelDataTypeFactory
* @param fieldName Request field name
* @param caseSensitive Case Sensitive
* @return A pair of RelDataTypeField and Boolean. Boolean indicates whether a new field is added
* to this holder.
*/
- public Pair<RelDataTypeField, Boolean> getFieldOrInsert(RelDataTypeFactory typeFactory,
- String fieldName, boolean caseSensitive) {
-
+ Pair<RelDataTypeField, Boolean> getFieldOrInsert(String fieldName, boolean caseSensitive) {
// First check if this field name exists in our field list
for (RelDataTypeField f : fields) {
if (Util.matches(caseSensitive, f.getName(), fieldName)) {
@@ -75,16 +75,7 @@ public class RelDataTypeHolder {
}
public List<String> getFieldNames() {
- List<String> fieldNames = new ArrayList<>();
- for (RelDataTypeField f : fields) {
- fieldNames.add(f.getName());
- }
-
- return fieldNames;
- }
-
- public void setRelDataTypeFactory(RelDataTypeFactory typeFactory) {
- this.typeFactory = typeFactory;
+ return Pair.left(fields);
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java
index 0fc14f3..a559686 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java
@@ -84,36 +84,6 @@ public abstract class RelDataTypeImpl
return field;
}
}
- for (RelDataTypeField field : fieldList) {
- RelDataType dataType = field.getType();
- if (dataType.isStruct()
- && dataType.getStructKind() == StructKind.PEEK_FIELDS_DEFAULT) {
- for (RelDataTypeField subField : dataType.getFieldList()) {
- if (Util.matches(caseSensitive, subField.getName(), fieldName)) {
- return subField;
- }
- }
- }
- }
- RelDataTypeField matched = null;
- for (RelDataTypeField field : fieldList) {
- RelDataType dataType = field.getType();
- if (dataType.isStruct()
- && dataType.getStructKind() == StructKind.PEEK_FIELDS) {
- for (RelDataTypeField subField : dataType.getFieldList()) {
- if (Util.matches(caseSensitive, subField.getName(), fieldName)) {
- if (matched == null) {
- matched = subField;
- } else {
- // TODO throw columnAmbiguous
- }
- }
- }
- }
- }
- if (matched != null) {
- return matched;
- }
if (elideRecord) {
final List<Slot> slots = Lists.newArrayList();
getFieldRecurse(slots, this, 0, fieldName, caseSensitive);
@@ -172,7 +142,6 @@ public abstract class RelDataTypeImpl
}
}
- // implement RelDataType
public List<RelDataTypeField> getFieldList() {
assert isStruct();
return fieldList;
@@ -182,18 +151,15 @@ public abstract class RelDataTypeImpl
return Pair.left(fieldList);
}
- // implement RelDataType
public int getFieldCount() {
assert isStruct() : this;
return fieldList.size();
}
public StructKind getStructKind() {
- assert isStruct() : this;
- return StructKind.FULLY_QUALIFIED;
+ return isStruct() ? StructKind.FULLY_QUALIFIED : StructKind.NONE;
}
- // implement RelDataType
public RelDataType getComponentType() {
// this is not a collection type
return null;
@@ -209,13 +175,11 @@ public abstract class RelDataTypeImpl
return null;
}
- // implement RelDataType
public boolean isStruct() {
return fieldList != null;
}
- // implement RelDataType
- public boolean equals(Object obj) {
+ @Override public boolean equals(Object obj) {
if (obj instanceof RelDataTypeImpl) {
final RelDataTypeImpl that = (RelDataTypeImpl) obj;
return this.digest.equals(that.digest);
@@ -223,52 +187,42 @@ public abstract class RelDataTypeImpl
return false;
}
- // implement RelDataType
- public int hashCode() {
+ @Override public int hashCode() {
return digest.hashCode();
}
- // implement RelDataType
public String getFullTypeString() {
return digest;
}
- // implement RelDataType
public boolean isNullable() {
return false;
}
- // implement RelDataType
public Charset getCharset() {
return null;
}
- // implement RelDataType
public SqlCollation getCollation() {
return null;
}
- // implement RelDataType
public SqlIntervalQualifier getIntervalQualifier() {
return null;
}
- // implement RelDataType
public int getPrecision() {
return PRECISION_NOT_SPECIFIED;
}
- // implement RelDataType
public int getScale() {
return SCALE_NOT_SPECIFIED;
}
- // implement RelDataType
public SqlTypeName getSqlTypeName() {
return null;
}
- // implement RelDataType
public SqlIdentifier getSqlIdentifier() {
SqlTypeName typeName = getSqlTypeName();
if (typeName == null) {
@@ -279,7 +233,6 @@ public abstract class RelDataTypeImpl
SqlParserPos.ZERO);
}
- // implement RelDataType
public RelDataTypeFamily getFamily() {
// by default, put each type into its own family
return this;
@@ -310,14 +263,12 @@ public abstract class RelDataTypeImpl
digest = sb.toString();
}
- // implement RelDataType
- public String toString() {
+ @Override public String toString() {
StringBuilder sb = new StringBuilder();
generateTypeString(sb, false);
return sb.toString();
}
- // implement RelDataType
public RelDataTypePrecedenceList getPrecedenceList() {
// by default, make each type have a precedence list containing
// only other types in the same family
@@ -336,7 +287,6 @@ public abstract class RelDataTypeImpl
};
}
- // implement RelDataType
public RelDataTypeComparability getComparability() {
return RelDataTypeComparability.ALL;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java b/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java
index 8d09cf4..7ae4dda 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java
@@ -19,6 +19,8 @@ package org.apache.calcite.rel.type;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.sql.type.SqlTypeName;
+import com.google.common.base.Preconditions;
+
import java.io.Serializable;
import java.util.List;
@@ -26,37 +28,55 @@ import java.util.List;
* RelRecordType represents a structured type having named fields.
*/
public class RelRecordType extends RelDataTypeImpl implements Serializable {
+ /** Name resolution policy; usually {@link StructKind#FULLY_QUALIFIED}. */
+ private final StructKind kind;
+
//~ Constructors -----------------------------------------------------------
/**
* Creates a <code>RecordType</code>. This should only be called from a
* factory method.
*/
- public RelRecordType(List<RelDataTypeField> fields) {
+ public RelRecordType(StructKind kind, List<RelDataTypeField> fields) {
super(fields);
+ this.kind = Preconditions.checkNotNull(kind);
computeDigest();
}
+ public RelRecordType(List<RelDataTypeField> fields) {
+ this(StructKind.FULLY_QUALIFIED, fields);
+ }
+
//~ Methods ----------------------------------------------------------------
- // implement RelDataType
- public SqlTypeName getSqlTypeName() {
+ @Override public SqlTypeName getSqlTypeName() {
return SqlTypeName.ROW;
}
- // implement RelDataType
- public boolean isNullable() {
+ @Override public boolean isNullable() {
return false;
}
- // implement RelDataType
- public int getPrecision() {
+ @Override public int getPrecision() {
// REVIEW: angel 18-Aug-2005 Put in fake implementation for precision
return 0;
}
+ @Override public StructKind getStructKind() {
+ return kind;
+ }
+
protected void generateTypeString(StringBuilder sb, boolean withDetail) {
- sb.append("RecordType(");
+ sb.append("RecordType");
+ switch (kind) {
+ case PEEK_FIELDS:
+ sb.append(":peek");
+ break;
+ case PEEK_FIELDS_DEFAULT:
+ sb.append(":peek_default");
+ break;
+ }
+ sb.append("(");
for (Ord<RelDataTypeField> ord : Ord.zip(fieldList)) {
if (ord.i > 0) {
sb.append(", ");
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/rel/type/StructKind.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/StructKind.java b/core/src/main/java/org/apache/calcite/rel/type/StructKind.java
new file mode 100644
index 0000000..b10f816
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/type/StructKind.java
@@ -0,0 +1,83 @@
+/*
+ * 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.rel.type;
+
+/**
+ * Describes a policy for resolving fields in record types.
+ *
+ * <p>The usual value is {@link #FULLY_QUALIFIED}.
+ *
+ * <p>A field whose record type is labeled {@link #PEEK_FIELDS} can be omitted.
+ * In Phoenix, column families are represented by fields like this.
+ * {@link #PEEK_FIELDS_DEFAULT} is similar, but represents the default column
+ * family, so it will win in the event of a tie.
+ *
+ * <p>SQL usually disallows a record type. For instance,
+ *
+ * <blockquote>SELECT address.zip FROM Emp AS e</blockquote>
+ *
+ * is disallowed because {@code address} "looks like" a table alias. You'd have
+ * to write
+ *
+ * <blockquote>SELECT e.address.zip FROM Emp AS e</blockquote>
+ *
+ * <p>But if a table has one or more columns that are record-typed and are
+ * labeled {@link #PEEK_FIELDS} or {@link #PEEK_FIELDS_DEFAULT} we suspend that
+ * rule and would allow {@code address.zip}.
+ *
+ * <p>If there are multiple matches, we choose the one that is:
+ * <ol>
+ * <li>Shorter. If you write {@code zipcode}, {@code address.zipcode} will
+ * be preferred over {@code product.supplier.zipcode}.
+ * <li>Uses as little skipping as possible. A match that is fully-qualified
+ * will beat one that uses {@code PEEK_FIELDS_DEFAULT} at some point, which
+ * will beat one that uses {@code PEEK_FIELDS} at some point.
+ * </ol>
+ */
+public enum StructKind {
+ /** This is not a structured type. */
+ NONE,
+
+ /** This is a traditional structured type, where each field must be
+ * referenced explicitly.
+ *
+ * <p>Also, when referencing a struct column, you
+ * need to qualify it with the table alias, per standard SQL. For instance,
+ * {@code SELECT c.address.zipcode FROM customer AS c}
+ * is valid but
+ * {@code SELECT address.zipcode FROM customer}
+ * it not valid.
+ */
+ FULLY_QUALIFIED,
+
+ /** As {@link #PEEK_FIELDS}, but takes priority if another struct-typed
+ * field also has a field of the name being sought.
+ *
+ * <p>In Phoenix, only one of a table's columns is labeled
+ * {@code PEEK_FIELDS_DEFAULT} - the default column family - but in principle
+ * there could be more than one. */
+ PEEK_FIELDS_DEFAULT,
+
+ /** If a field has this type, you can see its fields without qualifying them
+ * with the name of this field.
+ *
+ * <p>For example, if {@code address} is labeled {@code PEEK_FIELDS}, you
+ * could write {@code zipcode} as shorthand for {@code address.zipcode}. */
+ PEEK_FIELDS,
+}
+
+// End StructKind.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 b936859..f261659 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -607,6 +607,9 @@ public interface CalciteResource {
@BaseMessage("Table ''{0}'' not found")
ExInst<CalciteException> tableNotFound(String tableName);
+ @BaseMessage("Not a record type. The ''*'' operator requires a record")
+ ExInst<SqlValidatorException> starRequiresRecordType();
+
@BaseMessage("FILTER expression must be of type BOOLEAN")
ExInst<CalciteException> filterMustBeBoolean();
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/FlatLists.java b/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
index eba51b7..4ab4ef2 100644
--- a/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
+++ b/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
@@ -18,7 +18,10 @@ package org.apache.calcite.runtime;
import org.apache.calcite.util.ImmutableNullableList;
+import com.google.common.collect.ImmutableList;
+
import java.util.AbstractList;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
@@ -238,6 +241,17 @@ public class FlatLists {
}
}
+ /** Returns a list that consists of a given list plus an element. */
+ public static <E> List<E> append(List<E> list, E e) {
+ if (list instanceof AbstractFlatList) {
+ //noinspection unchecked
+ return ((AbstractFlatList) list).append(e);
+ }
+ final List<E> newList = new ArrayList<>(list);
+ newList.add(e);
+ return FlatLists.of(newList);
+ }
+
/** Base class for flat lists. */
public abstract static class AbstractFlatList<T>
extends AbstractImmutableList<T> implements RandomAccess {
@@ -246,6 +260,9 @@ public class FlatLists {
return Arrays.asList((T[]) toArray());
}
+ /** Returns a list that consists of a this list's elements plus a given
+ * element. */
+ public abstract List<T> append(T e);
}
/**
@@ -348,6 +365,10 @@ public class FlatLists {
public int compareTo(List o) {
return ComparableListImpl.compare((List) this, o);
}
+
+ public List<T> append(T e) {
+ return new Flat2List<T>(t0, e);
+ }
}
/**
@@ -469,6 +490,10 @@ public class FlatLists {
public int compareTo(List o) {
return ComparableListImpl.compare((List) this, o);
}
+
+ public List<T> append(T e) {
+ return new Flat3List<T>(t0, t1, e);
+ }
}
/**
@@ -609,6 +634,10 @@ public class FlatLists {
public int compareTo(List o) {
return ComparableListImpl.compare((List) this, o);
}
+
+ public List<T> append(T e) {
+ return new Flat4List<T>(t0, t1, t2, e);
+ }
}
/**
@@ -768,6 +797,10 @@ public class FlatLists {
public int compareTo(List o) {
return ComparableListImpl.compare((List) this, o);
}
+
+ public List<T> append(T e) {
+ return new Flat5List<T>(t0, t1, t2, t3, e);
+ }
}
/**
@@ -946,6 +979,10 @@ public class FlatLists {
public int compareTo(List o) {
return ComparableListImpl.compare((List) this, o);
}
+
+ public List<T> append(T e) {
+ return new Flat6List<T>(t0, t1, t2, t3, t4, e);
+ }
}
/**
@@ -1144,6 +1181,10 @@ public class FlatLists {
public int compareTo(List o) {
return ComparableListImpl.compare((List) this, o);
}
+
+ public List<T> append(T e) {
+ return ImmutableList.of(t0, t1, t2, t3, t5, e);
+ }
}
/** Empty list that implements the {@link Comparable} interface. */
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 d9b44ba..d070150 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
@@ -30,6 +30,7 @@ import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -173,6 +174,21 @@ public class SqlIdentifier extends SqlNode {
}
}
+ /** Returns an identifier that is the same as this except with a component
+ * added at a given position. Does not modify this identifier. */
+ public SqlIdentifier add(int i, String name, SqlParserPos pos) {
+ final List<String> names2 = new ArrayList<>(names);
+ names2.add(i, name);
+ final List<SqlParserPos> pos2;
+ if (componentPositions == null) {
+ pos2 = null;
+ } else {
+ pos2 = new ArrayList<>(componentPositions);
+ pos2.add(i, pos);
+ }
+ return new SqlIdentifier(names2, collation, pos, pos2);
+ }
+
/**
* Returns the position of the <code>i</code>th component of a compound
* identifier, or the position of the whole identifier if that information
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/sql/validate/CatalogScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/CatalogScope.java b/core/src/main/java/org/apache/calcite/sql/validate/CatalogScope.java
index c2ef43e..1420123 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/CatalogScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/CatalogScope.java
@@ -73,16 +73,6 @@ class CatalogScope extends DelegatingScope {
throw new UnsupportedOperationException();
}
- public SqlValidatorNamespace resolve(List<String> names,
- SqlValidatorScope[] ancestorOut, int[] offsetOut) {
- final ImmutableList<String> nameList =
- ImmutableList.<String>builder().addAll(this.names).addAll(names)
- .build();
- if (schemaNames.contains(nameList)) {
- return new SchemaNamespace(validator, nameList);
- }
- return null;
- }
}
// End CatalogScope.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 d48bb50..491ea1d 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
@@ -19,6 +19,7 @@ package org.apache.calcite.sql.validate;
import org.apache.calcite.rel.type.DynamicRecordType;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.StructKind;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlNode;
@@ -29,9 +30,13 @@ import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.util.Pair;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import java.util.Map;
import static org.apache.calcite.util.Static.RESOURCE;
@@ -74,11 +79,42 @@ public abstract class DelegatingScope implements SqlValidatorScope {
throw new UnsupportedOperationException();
}
- public SqlValidatorNamespace resolve(
- List<String> names,
- SqlValidatorScope[] ancestorOut,
- int[] offsetOut) {
- return parent.resolve(names, ancestorOut, offsetOut);
+ public void resolve(List<String> names, boolean deep, Resolved resolved) {
+ parent.resolve(names, deep, resolved);
+ }
+
+ /** If a record type allows implicit references to fields, recursively looks
+ * into the fields. Otherwise returns immediately. */
+ void resolveInNamespace(SqlValidatorNamespace ns, List<String> names,
+ Path path, Resolved resolved) {
+ if (names.isEmpty()) {
+ resolved.found(ns, this, path);
+ return;
+ }
+ final RelDataType rowType = ns.getRowType();
+ if (rowType.isStruct()) {
+ final String name = names.get(0);
+ final RelDataTypeField field0 =
+ validator.catalogReader.field(rowType, name);
+ if (field0 != null) {
+ final SqlValidatorNamespace ns2 = ns.lookupChild(field0.getName());
+ final Step path2 = path.add(rowType, field0.getIndex(),
+ StructKind.FULLY_QUALIFIED);
+ resolveInNamespace(ns2, names.subList(1, names.size()), 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 SqlValidatorNamespace ns2 = ns.lookupChild(field.getName());
+ resolveInNamespace(ns2, names, path2, resolved);
+ }
+ }
+ }
+ }
}
protected void addColumnNames(
@@ -113,6 +149,11 @@ public abstract class DelegatingScope implements SqlValidatorScope {
return parent.findQualifyingTableName(columnName, ctx);
}
+ protected Map<String, SqlValidatorNamespace>
+ findQualifyingTables(String columnName) {
+ return ImmutableMap.of();
+ }
+
public RelDataType resolveColumn(String name, SqlNode ctx) {
return parent.resolveColumn(name, ctx);
}
@@ -148,6 +189,7 @@ public abstract class DelegatingScope implements SqlValidatorScope {
return SqlQualified.create(this, 1, null, identifier);
}
+ final SqlIdentifier previous = identifier;
String columnName;
switch (identifier.names.size()) {
case 1: {
@@ -159,63 +201,167 @@ public abstract class DelegatingScope implements SqlValidatorScope {
final RelDataTypeField field =
validator.catalogReader.field(namespace.getRowType(), columnName);
+ if (field != null) {
+ if (hasAmbiguousUnresolvedStar(namespace.getRowType(), field,
+ columnName)) {
+ throw validator.newValidationError(identifier,
+ RESOURCE.columnAmbiguous(columnName));
+ }
- checkAmbiguousUnresolvedStar(namespace.getRowType(), field, identifier, columnName);
-
+ columnName = field.getName(); // use resolved field name
+ }
// todo: do implicit collation here
final SqlParserPos pos = identifier.getParserPosition();
- SqlIdentifier expanded =
- new SqlIdentifier(
- ImmutableList.of(tableName, field.getName()), // use resolved field name
- null,
- pos,
- ImmutableList.of(SqlParserPos.ZERO, pos));
- validator.setOriginal(expanded, identifier);
- return SqlQualified.create(this, 1, namespace, expanded);
+ identifier =
+ new SqlIdentifier(ImmutableList.of(tableName, columnName), null,
+ pos, ImmutableList.of(SqlParserPos.ZERO, pos));
}
-
+ // fall through
default: {
SqlValidatorNamespace fromNs = null;
+ Path fromPath = null;
+ final ResolvedImpl resolved = new ResolvedImpl();
final int size = identifier.names.size();
int i = size - 1;
for (; i > 0; i--) {
final SqlIdentifier prefix = identifier.getComponent(0, i);
- fromNs = resolve(prefix.names, null, null);
- if (fromNs != null) {
- if (fromNs.getEnclosingNode() != null) {
- String alias =
- SqlValidatorUtil.getAlias(fromNs.getEnclosingNode(), -1);
- if (alias != null
- && !alias.equals(identifier.names.get(i - 1))) {
- identifier = identifier.setName(i - 1, alias);
- }
- }
+ resolved.clear();
+ resolve(prefix.names, false, resolved);
+ if (resolved.count() == 1) {
+ final Resolve resolve = resolved.only();
+ fromNs = resolve.namespace;
+ fromPath = resolve.path;
break;
}
}
if (fromNs == null || fromNs instanceof SchemaNamespace) {
- final SqlIdentifier prefix1 = identifier.skipLast(1);
- throw validator.newValidationError(prefix1,
- RESOURCE.tableNameNotFound(prefix1.toString()));
+ // Look for a column not qualified by a table alias.
+ columnName = identifier.names.get(0);
+ final Map<String, SqlValidatorNamespace> map =
+ findQualifyingTables(columnName);
+ switch (map.size()) {
+ default:
+ final SqlIdentifier prefix1 = identifier.skipLast(1);
+ throw validator.newValidationError(prefix1,
+ RESOURCE.tableNameNotFound(prefix1.toString()));
+ case 1:
+ fromPath = resolved.emptyPath();
+ fromNs = map.entrySet().iterator().next().getValue();
+ }
+
+ // Throw an error if the table was not found.
+ // If one or more of the child namespaces allows peeking
+ // (e.g. if they are Phoenix column families) then we relax the SQL
+ // standard requirement that record fields are qualified by table alias.
+ if (!hasLiberalChild()) {
+ final SqlIdentifier prefix1 = identifier.skipLast(1);
+ throw validator.newValidationError(prefix1,
+ RESOURCE.tableNameNotFound(prefix1.toString()));
+ }
+ }
+
+ // If a table alias is part of the identifier, make sure that the table
+ // alias uses the same case as it was defined. For example, in
+ //
+ // SELECT e.empno FROM Emp as E
+ //
+ // change "e.empno" to "E.empno".
+ if (fromNs.getEnclosingNode() != null) {
+ String alias =
+ SqlValidatorUtil.getAlias(fromNs.getEnclosingNode(), -1);
+ if (alias != null
+ && i > 0
+ && !alias.equals(identifier.names.get(i - 1))) {
+ identifier = identifier.setName(i - 1, alias);
+ }
}
RelDataType fromRowType = fromNs.getRowType();
- for (int j = i; j < size; j++) {
- final SqlIdentifier last = identifier.getComponent(j);
- columnName = last.getSimple();
- final RelDataTypeField field =
- validator.catalogReader.field(fromRowType, columnName);
- if (field == null) {
- throw validator.newValidationError(last,
- RESOURCE.columnNotFoundInTable(columnName,
- identifier.getComponent(0, j).toString()));
+ if (fromPath.stepCount() > 1) {
+ for (Step p : fromPath.steps()) {
+ fromRowType = fromRowType.getFieldList().get(p.i).getType();
}
+ ++i;
+ }
+ final SqlIdentifier suffix = identifier.getComponent(i, size);
+ resolved.clear();
+ resolveInNamespace(fromNs, suffix.names, resolved.emptyPath(), resolved);
+ final Path path;
+ switch (resolved.count()) {
+ case 0:
+ // 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".
+ int k = size - 1;
+ for (; k > i; --k) {
+ SqlIdentifier suffix2 = identifier.getComponent(i, k);
+ resolved.clear();
+ resolveInNamespace(fromNs, suffix2.names, resolved.emptyPath(),
+ resolved);
+ if (resolved.count() > 0) {
+ break;
+ }
+ }
+ final SqlIdentifier prefix = identifier.getComponent(0, i);
+ final SqlIdentifier suffix3 = identifier.getComponent(i, k + 1);
+ throw validator.newValidationError(suffix3,
+ RESOURCE.columnNotFoundInTable(suffix3.toString(), prefix.toString()));
+ case 1:
+ path = resolved.only().path;
+ break;
+ default:
+ final Comparator<Resolve> c =
+ new Comparator<Resolve>() {
+ public int compare(Resolve o1, Resolve o2) {
+ // Name resolution that uses fewer implicit steps wins.
+ int c = Integer.compare(worstKind(o1.path), worstKind(o2.path));
+ if (c != 0) {
+ return c;
+ }
+ // Shorter path wins
+ return Integer.compare(o1.path.stepCount(), o2.path.stepCount());
+ }
+
+ private int worstKind(Path path) {
+ int kind = -1;
+ for (Step step : path.steps()) {
+ kind = Math.max(kind, step.kind.ordinal());
+ }
+ return kind;
+ }
+ };
+ Collections.sort(resolved.resolves, c);
+ if (c.compare(resolved.resolves.get(0), resolved.resolves.get(1)) == 0) {
+ throw validator.newValidationError(suffix,
+ RESOURCE.columnAmbiguous(suffix.toString()));
+ }
+ path = resolved.resolves.get(0).path;
+ }
- checkAmbiguousUnresolvedStar(fromRowType, field, identifier, columnName);
-
- // normalize case to match definition, in a copy of the identifier
- identifier = identifier.setName(j, field.getName());
- fromRowType = field.getType();
+ // Normalize case to match definition, make elided fields explicit,
+ // and check that references to dynamic stars ("**") are unambiguous.
+ int k = i;
+ for (Step step : path.steps()) {
+ final RelDataTypeField field0 =
+ step.rowType.getFieldList().get(step.i);
+ final String fieldName = field0.getName();
+ switch (step.kind) {
+ case PEEK_FIELDS:
+ case PEEK_FIELDS_DEFAULT:
+ identifier = identifier.add(k, fieldName, SqlParserPos.ZERO);
+ break;
+ default:
+ final String name = identifier.names.get(k);
+ if (!fieldName.equals(name)) {
+ identifier = identifier.setName(k, fieldName);
+ }
+ if (hasAmbiguousUnresolvedStar(step.rowType, field0, name)) {
+ throw validator.newValidationError(identifier,
+ RESOURCE.columnAmbiguous(name));
+ }
+ }
+ ++k;
}
+
if (i > 1) {
// Simplify overqualified identifiers.
// For example, schema.emp.deptno becomes emp.deptno.
@@ -226,11 +372,19 @@ public abstract class DelegatingScope implements SqlValidatorScope {
// SELECT schema.emp.deptno FROM schema.emp AS e
identifier = identifier.getComponent(i - 1, identifier.names.size());
}
+
+ if (!previous.equals(identifier)) {
+ validator.setOriginal(identifier, previous);
+ }
return SqlQualified.create(this, i, fromNs, identifier);
}
}
}
+ protected boolean hasLiberalChild() {
+ return false;
+ }
+
public void validateExpr(SqlNode expr) {
// Do not delegate to parent. An expression valid in this scope may not
// be valid in the parent scope.
@@ -248,27 +402,22 @@ public abstract class DelegatingScope implements SqlValidatorScope {
return parent.getOrderList();
}
- private void checkAmbiguousUnresolvedStar(RelDataType fromRowType, RelDataTypeField field,
- SqlIdentifier identifier, String columnName) {
-
- if (field != null
- && field.isDynamicStar()
+ /** Returns whether {@code rowType} contains more than one star column.
+ * Having more than one star columns implies ambiguous column. */
+ private boolean hasAmbiguousUnresolvedStar(RelDataType rowType,
+ RelDataTypeField field, String columnName) {
+ if (field.isDynamicStar()
&& !DynamicRecordType.isDynamicStarColName(columnName)) {
- // Make sure fromRowType only contains one star column.
- // Having more than one star columns implies ambiguous column.
int count = 0;
- for (RelDataTypeField possibleStar : fromRowType.getFieldList()) {
+ for (RelDataTypeField possibleStar : rowType.getFieldList()) {
if (possibleStar.isDynamicStar()) {
- count++;
+ if (++count > 1) {
+ return true;
+ }
}
}
-
- if (count > 1) {
- throw validator.newValidationError(identifier,
- RESOURCE.columnAmbiguous(columnName));
- }
}
-
+ return false;
}
/**
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 9da3bff..e7f0ff8 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
@@ -64,11 +64,7 @@ class EmptyScope implements SqlValidatorScope {
throw new UnsupportedOperationException();
}
- public SqlValidatorNamespace resolve(
- List<String> names,
- SqlValidatorScope[] ancestorOut,
- int[] offsetOut) {
- return null;
+ public void resolve(List<String> names, boolean deep, Resolved resolved) {
}
public SqlValidatorNamespace getTableNamespace(List<String> names) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 8567cc5..c4d292f 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
@@ -16,15 +16,21 @@
*/
package org.apache.calcite.sql.validate;
+import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.StructKind;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
+import com.google.common.collect.ImmutableList;
+
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import static org.apache.calcite.util.Static.RESOURCE;
@@ -39,7 +45,7 @@ public abstract class ListScope extends DelegatingScope {
* List of child {@link SqlValidatorNamespace} objects and their names.
*/
protected final List<Pair<String, SqlValidatorNamespace>> children =
- new ArrayList<Pair<String, SqlValidatorNamespace>>();
+ new ArrayList<>();
//~ Constructors -----------------------------------------------------------
@@ -63,57 +69,30 @@ public abstract class ListScope extends DelegatingScope {
return Pair.right(children);
}
- protected SqlValidatorNamespace getChild(String alias) {
- if (alias == null) {
- if (children.size() != 1) {
- throw Util.newInternal(
- "no alias specified, but more than one table in from list");
- }
- return children.get(0).right;
- } else {
- final int i = validator.catalogReader.match(Pair.left(children), alias);
- if (i >= 0) {
- return children.get(i).right;
- }
- return null;
- }
- }
-
- /** Returns a child namespace that matches a fully-qualified list of names.
- * This will be a schema-qualified table, for example
- *
- * <blockquote><pre>SELECT sales.emp.empno FROM sales.emp</pre></blockquote>
- */
- protected SqlValidatorNamespace getChild(List<String> names) {
- int i = findChild(names);
- return i < 0 ? null : children.get(i).right;
- }
-
- protected int findChild(List<String> names) {
- for (int i = 0; i < children.size(); i++) {
- Pair<String, SqlValidatorNamespace> child = children.get(i);
- if (child.left != null) {
- if (validator.catalogReader.matches(child.left, Util.last(names))) {
- if (names.size() == 1) {
- return i;
- }
- } else {
+ private int findChild(List<String> names) {
+ for (Ord<Pair<String, SqlValidatorNamespace>> child : Ord.zip(children)) {
+ String lastName = Util.last(names);
+ if (child.e.left != null) {
+ if (!validator.catalogReader.matches(child.e.left, 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.i;
+ }
}
// Look up the 2 tables independently, in case one is qualified with
// catalog & schema and the other is not.
- final SqlValidatorTable table = child.right.getTable();
+ final SqlValidatorTable table = child.e.right.getTable();
if (table != null) {
final SqlValidatorTable table2 =
validator.catalogReader.getTable(names);
if (table2 != null
&& table.getQualifiedName().equals(table2.getQualifiedName())) {
- return i;
+ return child.i;
}
}
}
@@ -134,47 +113,84 @@ public abstract class ListScope extends DelegatingScope {
parent.findAliases(result);
}
- public Pair<String, SqlValidatorNamespace>
+ @Override public Pair<String, SqlValidatorNamespace>
findQualifyingTableName(final String columnName, SqlNode ctx) {
- int count = 0;
- Pair<String, SqlValidatorNamespace> tableName = null;
- for (Pair<String, SqlValidatorNamespace> child : children) {
- final RelDataType rowType = child.right.getRowType();
- if (validator.catalogReader.field(rowType, columnName) != null) {
- tableName = child;
- count++;
- }
- }
- switch (count) {
+ final Map<String, SqlValidatorNamespace> map =
+ findQualifyingTables(columnName);
+ switch (map.size()) {
case 0:
return parent.findQualifyingTableName(columnName, ctx);
case 1:
- return tableName;
+ return Pair.of(map.entrySet().iterator().next());
default:
throw validator.newValidationError(ctx,
RESOURCE.columnAmbiguous(columnName));
}
}
- public SqlValidatorNamespace resolve(
- List<String> names,
- SqlValidatorScope[] ancestorOut,
- int[] offsetOut) {
+ @Override public Map<String, SqlValidatorNamespace>
+ findQualifyingTables(String columnName) {
+ final Map<String, SqlValidatorNamespace> map = new HashMap<>();
+ for (Pair<String, SqlValidatorNamespace> child : children) {
+ final ResolvedImpl resolved = new ResolvedImpl();
+ resolve(ImmutableList.of(child.left, columnName), true, resolved);
+ if (resolved.count() > 0) {
+ map.put(child.getKey(), child.getValue());
+ }
+ }
+ return map;
+ }
+
+ @Override protected boolean hasLiberalChild() {
+ for (Pair<String, SqlValidatorNamespace> child : children) {
+ final RelDataType rowType = child.right.getRowType();
+ switch (rowType.getStructKind()) {
+ case PEEK_FIELDS:
+ case PEEK_FIELDS_DEFAULT:
+ return true;
+ }
+ for (RelDataTypeField field : rowType.getFieldList()) {
+ switch (field.getType().getStructKind()) {
+ case PEEK_FIELDS:
+ case PEEK_FIELDS_DEFAULT:
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override public void resolve(List<String> names, boolean deep,
+ Resolved resolved) {
// First resolve by looking through the child namespaces.
final int i = findChild(names);
if (i >= 0) {
- if (ancestorOut != null) {
- ancestorOut[0] = this;
+ final Step path =
+ resolved.emptyPath().add(null, i, StructKind.FULLY_QUALIFIED);
+ resolved.found(children.get(i).right, this, path);
+ return;
+ }
+
+ // Recursively look deeper into the record-valued fields of the namespace,
+ // if it allows skipping fields.
+ if (deep) {
+ for (Ord<Pair<String, SqlValidatorNamespace>> child : Ord.zip(children)) {
+ // If identifier starts with table alias, remove the alias.
+ final List<String> names2 =
+ validator.catalogReader.matches(child.e.left, names.get(0))
+ ? names.subList(1, names.size())
+ : names;
+ resolveInNamespace(child.e.right, names2, resolved.emptyPath(),
+ resolved);
}
- if (offsetOut != null) {
- offsetOut[0] = i;
+ if (resolved.count() > 0) {
+ return;
}
- return children.get(i).right;
}
// Then call the base class method, which will delegate to the
// parent scope.
- return parent.resolve(names, ancestorOut, offsetOut);
+ super.resolve(names, deep, resolved);
}
public RelDataType resolveColumn(String columnName, SqlNode ctx) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/core/src/main/java/org/apache/calcite/sql/validate/ParameterScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/ParameterScope.java b/core/src/main/java/org/apache/calcite/sql/validate/ParameterScope.java
index f1a4e91..c9a0484 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/ParameterScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/ParameterScope.java
@@ -20,7 +20,6 @@ import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlIdentifier;
-import java.util.List;
import java.util.Map;
/**
@@ -57,14 +56,6 @@ public class ParameterScope extends EmptyScope {
return this;
}
- public SqlValidatorNamespace resolve(
- List<String> names,
- SqlValidatorScope[] ancestorOut,
- int[] offsetOut) {
- assert names.size() == 1;
- final RelDataType type = nameToTypeMap.get(names.get(0));
- return new ParameterNamespace(validator, type);
- }
}
// End ParameterScope.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 067c324..55c1d01 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
@@ -61,8 +61,11 @@ public class SqlQualified {
return identifier.names;
}
final ImmutableList.Builder<String> builder = ImmutableList.builder();
+ final SqlValidatorScope.ResolvedImpl resolved =
+ new SqlValidatorScope.ResolvedImpl();
+ scope.resolve(Util.skipLast(identifier.names), false, resolved);
SqlValidatorNamespace namespace =
- scope.resolve(Util.skipLast(identifier.names), null, null);
+ resolved.count() == 1 ? resolved.only().namespace : null;
builder.add(identifier.names.get(0));
for (String name : Util.skip(identifier.names)) {
if (namespace != null) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 b157c71..b43f85c 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
@@ -80,16 +80,8 @@ public interface SqlValidatorCatalogReader {
*/
RelDataTypeField field(RelDataType rowType, String alias);
- /**
- * Finds the ordinal of a field with a given name, using the case-sensitivity
- * of the current session.
- */
- int fieldOrdinal(RelDataType rowType, String alias);
-
boolean matches(String string, String name);
- int match(List<String> strings, String name);
-
RelDataType createTypeFromProjection(RelDataType type,
List<String> columnNameList);
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 44377d1..f4bbb79 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
@@ -445,7 +445,6 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
switch (identifier.names.size()) {
case 1:
for (Pair<String, SqlValidatorNamespace> p : scope.children) {
-
if (p.right.getRowType().isDynamicStruct()) {
// don't expand star if the underneath table is dynamic.
// Treat this star as a special field in validation/conversion and
@@ -482,33 +481,22 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
includeSystemVars);
}
}
-
}
return true;
+
default:
final SqlIdentifier prefixId = identifier.skipLast(1);
- final SqlValidatorNamespace fromNs;
- if (prefixId.names.size() == 1) {
- String tableName = prefixId.names.get(0);
- final SqlValidatorNamespace childNs = scope.getChild(tableName);
- if (childNs == null) {
- // e.g. "select r.* from e"
- throw newValidationError(identifier.getComponent(0),
- RESOURCE.unknownIdentifier(tableName));
- }
- final SqlNode from = childNs.getNode();
- fromNs = getNamespace(from);
- } else {
- fromNs = scope.getChild(prefixId.names);
- if (fromNs == null) {
- // e.g. "select s.t.* from e"
- throw newValidationError(prefixId,
- RESOURCE.unknownIdentifier(prefixId.toString()));
- }
- }
- assert fromNs != null;
+ final SqlValidatorScope.ResolvedImpl resolved =
+ new SqlValidatorScope.ResolvedImpl();
+ scope.resolve(prefixId.names, true, resolved);
+ if (resolved.count() == 0) {
+ // e.g. "select s.t.* from e"
+ // or "select r.* from e"
+ throw newValidationError(prefixId,
+ RESOURCE.unknownIdentifier(prefixId.toString()));
+ }
+ final SqlValidatorNamespace fromNs = resolved.only().namespace;
final RelDataType rowType = fromNs.getRowType();
-
if (rowType.isDynamicStruct()) {
// don't expand star if the underneath table is dynamic.
addToSelectList(
@@ -518,7 +506,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
prefixId.plus(DynamicRecordType.DYNAMIC_STAR_PREFIX, startPosition),
scope,
includeSystemVars);
- } else {
+ } else if (rowType.isStruct()) {
for (RelDataTypeField field : rowType.getFieldList()) {
String columnName = field.getName();
@@ -531,8 +519,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
scope,
includeSystemVars);
}
+ } else {
+ throw newValidationError(prefixId, RESOURCE.starRequiresRecordType());
}
-
return true;
}
}
@@ -701,7 +690,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlValidatorNamespace ns = null;
for (String name : subNames) {
if (ns == null) {
- ns = scope.resolve(ImmutableList.of(name), null, null);
+ final SqlValidatorScope.ResolvedImpl resolved =
+ new SqlValidatorScope.ResolvedImpl();
+ scope.resolve(ImmutableList.of(name), false, resolved);
+ if (resolved.count() == 1) {
+ ns = resolved.only().namespace;
+ }
} else {
ns = ns.lookupChild(name);
}
@@ -932,10 +926,11 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final SqlValidatorScope parentScope =
((DelegatingScope) scope).getParent();
if (id.isSimple()) {
- SqlValidatorNamespace ns =
- parentScope.resolve(id.names, null, null);
- if (ns != null) {
- return ns;
+ final SqlValidatorScope.ResolvedImpl resolved =
+ new SqlValidatorScope.ResolvedImpl();
+ parentScope.resolve(id.names, false, resolved);
+ if (resolved.count() == 1) {
+ return resolved.only().namespace;
}
}
}
@@ -4439,7 +4434,6 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
// Resolve the longest prefix of id that we can
- SqlValidatorNamespace resolvedNs;
int i;
for (i = id.names.size() - 1; i > 0; i--) {
// REVIEW jvs 9-June-2005: The name resolution rules used
@@ -4450,10 +4444,16 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// we could do a better job if they were looked up via
// resolveColumn.
- resolvedNs = scope.resolve(id.names.subList(0, i), null, null);
- if (resolvedNs != null) {
+ final SqlValidatorScope.ResolvedImpl resolved =
+ new SqlValidatorScope.ResolvedImpl();
+ scope.resolve(id.names.subList(0, i), false, resolved);
+ if (resolved.count() == 1) {
// There's a namespace with the name we seek.
- type = resolvedNs.getRowType();
+ final SqlValidatorScope.Resolve resolve = resolved.only();
+ type = resolve.namespace.getRowType();
+ for (SqlValidatorScope.Step p : Util.skip(resolve.path.steps())) {
+ type = type.getFieldList().get(p.i).getType();
+ }
break;
}
}
@@ -4525,9 +4525,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private static class Expander extends SqlScopedShuttle {
private final SqlValidatorImpl validator;
- public Expander(
- SqlValidatorImpl validator,
- SqlValidatorScope scope) {
+ Expander(SqlValidatorImpl validator, SqlValidatorScope scope) {
super(scope);
this.validator = validator;
}
@@ -4549,7 +4547,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// SqlIdentifier "col_name" would be resolved to a dynamic star field in dynTable's rowType.
// Expand such SqlIdentifier to ITEM operator.
if (DynamicRecordType.isDynamicStarColName(Util.last(fqId.names))
- && !DynamicRecordType.isDynamicStarColName(Util.last(id.names))) {
+ && !DynamicRecordType.isDynamicStarColName(Util.last(id.names))) {
SqlNode[] inputs = new SqlNode[2];
inputs[0] = fqId;
inputs[1] = SqlLiteral.createCharString(
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 ee72ffb..c30b9ff 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
@@ -17,6 +17,7 @@
package org.apache.calcite.sql.validate;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.StructKind;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlNode;
@@ -24,6 +25,11 @@ import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlWindow;
import org.apache.calcite.util.Pair;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -53,14 +59,11 @@ public interface SqlValidatorScope {
/**
* Looks up a node with a given name. Returns null if none is found.
*
- * @param names Name of node to find
- * @param ancestorOut If not null, writes the ancestor scope here
- * @param offsetOut If not null, writes the offset within the ancestor here
+ * @param names Name of node to find, maybe partially or fully qualified
+ * @param deep Whether to look more than one level deep
+ * @param resolved Callback wherein to write the match(es) we find
*/
- SqlValidatorNamespace resolve(
- List<String> names,
- SqlValidatorScope[] ancestorOut,
- int[] offsetOut);
+ void resolve(List<String> names, boolean deep, Resolved resolved);
/**
* Finds the table alias which is implicitly qualifying an unqualified
@@ -172,6 +175,109 @@ public interface SqlValidatorScope {
/** 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)}. */
+ interface Resolved {
+ void found(SqlValidatorNamespace namespace, SqlValidatorScope scope,
+ Path path);
+ int count();
+ Path emptyPath();
+ }
+
+ /** A sequence of steps by which an identifier was resolved. */
+ 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);
+ }
+
+ /** Number of steps in this path. */
+ public int stepCount() {
+ return 0;
+ }
+
+ /** Returns the steps in this path. */
+ public List<Step> steps() {
+ ImmutableList.Builder<Step> paths = new ImmutableList.Builder<>();
+ build(paths);
+ return paths.build();
+ }
+
+ protected void build(ImmutableList.Builder<Step> paths) {
+ }
+ }
+
+ /** A path that has no steps. */
+ class EmptyPath extends Path {
+ }
+
+ /** A step in resolving an identifier. */
+ class Step extends Path {
+ final Path parent;
+ final RelDataType rowType;
+ public final int i;
+ final StructKind kind;
+
+ Step(Path parent, RelDataType rowType, int i, StructKind kind) {
+ this.parent = Preconditions.checkNotNull(parent);
+ this.rowType = rowType; // may be null
+ this.i = i;
+ this.kind = Preconditions.checkNotNull(kind);
+ }
+
+ @Override public int stepCount() {
+ return 1 + parent.stepCount();
+ }
+
+ protected void build(ImmutableList.Builder<Step> paths) {
+ parent.build(paths);
+ paths.add(this);
+ }
+ }
+
+ /** Default implementation of
+ * {@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, SqlValidatorScope scope, Path path) {
+ resolves.add(new Resolve(namespace, scope, path));
+ }
+
+ public int count() {
+ return resolves.size();
+ }
+
+ public Path emptyPath() {
+ return emptyPath;
+ }
+
+ public Resolve only() {
+ return Iterables.getOnlyElement(resolves);
+ }
+
+ /** Resets all state. */
+ public void clear() {
+ resolves.clear();
+ }
+
+ }
+
+ /** A match found when looking up a name. */
+ class Resolve {
+ public final SqlValidatorNamespace namespace;
+ public final SqlValidatorScope scope;
+ public final Path path;
+
+ Resolve(SqlValidatorNamespace namespace, SqlValidatorScope scope,
+ Path path) {
+ this.namespace = Preconditions.checkNotNull(namespace);
+ this.scope = scope;
+ this.path = path;
+ }
+ }
}
// End SqlValidatorScope.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 249f678..249f5f5 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
@@ -109,14 +109,13 @@ public class SqlValidatorUtil {
* Looks up a field with a given name, returning null if not found.
*
* @param caseSensitive Whether match is case-sensitive
- * @param elideRecord Whether to find fields nested within records
* @param rowType Row type
* @param columnName Field name
* @return Field, or null if not found
*/
public static RelDataTypeField lookupField(boolean caseSensitive,
- boolean elideRecord, final RelDataType rowType, String columnName) {
- return rowType.getField(columnName, caseSensitive, elideRecord);
+ final RelDataType rowType, String columnName) {
+ return rowType.getField(columnName, caseSensitive, false);
}
public static void checkCharsetAndCollateConsistentIfCharType(
@@ -445,16 +444,15 @@ public class SqlValidatorUtil {
SqlValidatorScope scope,
List<String> names) {
assert names.size() > 0;
- SqlValidatorNamespace namespace = null;
- for (int i = 0; i < names.size(); i++) {
- String name = names.get(i);
- if (i == 0) {
- namespace = scope.resolve(ImmutableList.of(name), null, null);
- } else {
- namespace = namespace.lookupChild(name);
- }
+ final SqlValidatorScope.ResolvedImpl resolved =
+ new SqlValidatorScope.ResolvedImpl();
+ scope.resolve(ImmutableList.of(names.get(0)), false, resolved);
+ assert resolved.count() == 1;
+ SqlValidatorNamespace namespace = resolved.only().namespace;
+ for (String name : Util.skip(names)) {
+ namespace = namespace.lookupChild(name);
+ assert namespace != null;
}
- assert namespace != null;
return namespace;
}
@@ -524,14 +522,14 @@ public class SqlValidatorUtil {
public static RelDataType createTypeFromProjection(RelDataType type,
List<String> columnNameList, RelDataTypeFactory typeFactory,
- boolean caseSensitive, boolean elideRecord) {
+ boolean caseSensitive) {
// If the names in columnNameList and type have case-sensitive differences,
// the resulting type will use those from type. These are presumably more
// canonical.
final List<RelDataTypeField> fields =
new ArrayList<>(columnNameList.size());
for (String name : columnNameList) {
- RelDataTypeField field = type.getField(name, caseSensitive, elideRecord);
+ RelDataTypeField field = type.getField(name, caseSensitive, false);
fields.add(type.getFieldList().get(field.getIndex()));
}
return typeFactory.createStructType(fields);
@@ -657,26 +655,24 @@ public class SqlValidatorUtil {
String originalRelName = expr.names.get(0);
String originalFieldName = expr.names.get(1);
- int[] nsIndexes = {-1};
- final SqlValidatorScope[] ancestorScopes = {null};
- SqlValidatorNamespace foundNs =
- scope.resolve(
- ImmutableList.of(originalRelName),
- ancestorScopes,
- nsIndexes);
+ final SqlValidatorScope.ResolvedImpl resolved =
+ new SqlValidatorScope.ResolvedImpl();
+ scope.resolve(ImmutableList.of(originalRelName), false, resolved);
- assert foundNs != null;
- assert nsIndexes.length == 1;
- int childNamespaceIndex = nsIndexes[0];
+ assert resolved.count() == 1;
+ final SqlValidatorScope.Resolve resolve = resolved.only();
+ final SqlValidatorNamespace foundNs = resolve.namespace;
+ final int childNamespaceIndex = resolve.path.steps().get(0).i;
int namespaceOffset = 0;
if (childNamespaceIndex > 0) {
// If not the first child, need to figure out the width of
// output types from all the preceding namespaces
- assert ancestorScopes[0] instanceof ListScope;
+ final SqlValidatorScope ancestorScope = resolve.scope;
+ assert ancestorScope instanceof ListScope;
List<SqlValidatorNamespace> children =
- ((ListScope) ancestorScopes[0]).getChildren();
+ ((ListScope) ancestorScope).getChildren();
for (int j = 0; j < childNamespaceIndex; j++) {
namespaceOffset +=
http://git-wip-us.apache.org/repos/asf/calcite/blob/0938c7b6/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 9141f83..42c8b99 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
@@ -16,6 +16,7 @@
*/
package org.apache.calcite.sql.validate;
+import org.apache.calcite.rel.type.StructKind;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlWithItem;
@@ -53,14 +54,16 @@ class WithScope extends ListScope {
return super.getTableNamespace(names);
}
- @Override public SqlValidatorNamespace resolve(List<String> names,
- SqlValidatorScope[] ancestorOut,
- int[] offsetOut) {
+ @Override public void resolve(List<String> names, boolean deep, Resolved resolved) {
if (names.size() == 1
- && names.get(0).equals(withItem.name.getSimple())) {
- return validator.getNamespace(withItem);
+ && 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, null, path);
+ return;
}
- return super.resolve(names, ancestorOut, offsetOut);
+ super.resolve(names, deep, resolved);
}
}