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 2015/01/01 17:15:14 UTC
[1/2] incubator-calcite git commit: [CALCITE-356] Allow column
references of the form schema.table.column
Repository: incubator-calcite
Updated Branches:
refs/heads/master 0fda7afc8 -> 6c88546ea
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 069ba82..ee72ffb 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
@@ -22,7 +22,9 @@ import org.apache.calcite.sql.SqlIdentifier;
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 java.util.Collection;
import java.util.List;
/**
@@ -51,12 +53,12 @@ public interface SqlValidatorScope {
/**
* Looks up a node with a given name. Returns null if none is found.
*
- * @param name Name of node to find
+ * @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
*/
SqlValidatorNamespace resolve(
- String name,
+ List<String> names,
SqlValidatorScope[] ancestorOut,
int[] offsetOut);
@@ -72,10 +74,9 @@ public interface SqlValidatorScope {
*
* @param columnName Column name
* @param ctx Validation context, to appear in any error thrown
- * @return Table alias
+ * @return Table alias and namespace
*/
- String findQualifyingTableName(
- String columnName,
+ Pair<String, SqlValidatorNamespace> findQualifyingTableName(String columnName,
SqlNode ctx);
/**
@@ -91,14 +92,16 @@ public interface SqlValidatorScope {
*
* @param result a list of monikers to add the result to
*/
- void findAliases(List<SqlMoniker> result);
+ void findAliases(Collection<SqlMoniker> result);
/**
* Converts an identifier into a fully-qualified identifier. For example,
* the "empno" in "select empno from emp natural join dept" becomes
* "emp.empno".
+ *
+ * @return A qualified identifier, never null
*/
- SqlIdentifier fullyQualify(SqlIdentifier identifier);
+ SqlQualified fullyQualify(SqlIdentifier identifier);
/**
* Registers a relation in this scope.
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 2ae4af5..b872b1d 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
@@ -268,7 +268,7 @@ public class SqlValidatorUtil {
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (i == 0) {
- namespace = scope.resolve(name, null, null);
+ namespace = scope.resolve(ImmutableList.of(name), null, null);
} else {
namespace = namespace.lookupChild(name);
}
@@ -283,15 +283,17 @@ public class SqlValidatorUtil {
List<SqlMoniker> hints) {
// Assume that the last name is 'dummy' or similar.
List<String> subNames = Util.skipLast(names);
- hints.addAll(catalogReader.getAllSchemaObjectNames(subNames));
- // If the name has length 0, try prepending the name of the default
- // schema. So, the empty name would yield a list of tables in the
- // default schema, as well as a list of schemas from the above code.
- if (subNames.size() == 0) {
- hints.addAll(
- catalogReader.getAllSchemaObjectNames(
- catalogReader.getSchemaName()));
+ // Try successively with catalog.schema, catalog and no prefix
+ List<String> x = catalogReader.getSchemaName();
+ for (;;) {
+ 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);
}
}
@@ -478,7 +480,7 @@ public class SqlValidatorUtil {
final SqlValidatorScope[] ancestorScopes = {null};
SqlValidatorNamespace foundNs =
scope.resolve(
- originalRelName,
+ ImmutableList.of(originalRelName),
ancestorScopes,
nsIndexes);
@@ -607,7 +609,7 @@ public class SqlValidatorUtil {
}
public SqlNode visit(SqlIdentifier id) {
- return getScope().fullyQualify(id);
+ return getScope().fullyQualify(id).identifier;
}
public SqlNode visit(SqlDataTypeSpec type) {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorWithHints.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorWithHints.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorWithHints.java
index d131d94..d796f1e 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorWithHints.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorWithHints.java
@@ -31,7 +31,7 @@ public interface SqlValidatorWithHints extends SqlValidator {
//~ Methods ----------------------------------------------------------------
/**
- * Looks up completion hints for a syntatically correct SQL statement that
+ * Looks up completion hints for a syntactically correct SQL statement that
* has been parsed into an expression tree. (Note this should be called
* after {@link #validate(org.apache.calcite.sql.SqlNode)}.
*
@@ -53,7 +53,7 @@ public interface SqlValidatorWithHints extends SqlValidator {
* Parser Position in a parsed expression tree Note: call this only after
* {@link #validate} has been called.
*
- * @param topNode top of expression tree in which to lookup the qualfied
+ * @param topNode top of expression tree in which to lookup the qualified
* name for the SqlIdentifier
* @param pos indicates the position of the {@link SqlIdentifier} in
* the SQL statement we want to get the qualified
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 1866d7d..b650402 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
@@ -53,13 +53,13 @@ class WithScope extends ListScope {
return super.getTableNamespace(names);
}
- @Override public SqlValidatorNamespace resolve(String name,
+ @Override public SqlValidatorNamespace resolve(List<String> names,
SqlValidatorScope[] ancestorOut,
int[] offsetOut) {
- if (name.equals(withItem.name.getSimple())) {
+ if (names.equals(withItem.name.getSimple())) {
return validator.getNamespace(withItem);
}
- return super.resolve(name, ancestorOut, offsetOut);
+ return super.resolve(names, ancestorOut, offsetOut);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 09b074d..33fc891 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -123,6 +123,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.SqlQualified;
import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction;
import org.apache.calcite.sql.validate.SqlUserDefinedTableMacro;
import org.apache.calcite.sql.validate.SqlValidator;
@@ -1233,7 +1234,7 @@ public class SqlToRelConverter {
boolean isExists) {
SqlCall call = (SqlBasicCall) subQuery.node;
if (subqueryConverter.canConvertSubquery()
- && isSubqNonCorrelated(converted, bb)) {
+ && isSubQueryNonCorrelated(converted, bb)) {
// First check if the subquery has already been converted
// because it's a nested subquery. If so, don't re-evaluate
// it again.
@@ -2070,7 +2071,7 @@ public class SqlToRelConverter {
final SqlValidatorScope[] ancestorScopes = {null};
SqlValidatorNamespace foundNs =
lookup.bb.scope.resolve(
- originalRelName,
+ ImmutableList.of(originalRelName),
ancestorScopes,
nsIndexes);
@@ -2368,7 +2369,7 @@ public class SqlToRelConverter {
* blackboard of the parent query of this subquery
* @return true if the subquery is non-correlated.
*/
- private boolean isSubqNonCorrelated(RelNode subq, Blackboard bb) {
+ private boolean isSubQueryNonCorrelated(RelNode subq, Blackboard bb) {
Set<String> correlatedVariables = RelOptUtil.getVariablesUsed(subq);
for (String correlName : correlatedVariables) {
DeferredLookup lookup = mapCorrelToDeferred.get(correlName);
@@ -2378,7 +2379,7 @@ public class SqlToRelConverter {
final SqlValidatorScope[] ancestorScopes = {null};
SqlValidatorNamespace foundNs =
lookup.bb.scope.resolve(
- originalRelName,
+ ImmutableList.of(originalRelName),
ancestorScopes,
nsIndexes);
@@ -3302,12 +3303,14 @@ public class SqlToRelConverter {
+ "' is not a group expr");
}
- SqlValidatorNamespace namespace = null;
+ final SqlQualified qualified;
if (bb.scope != null) {
- identifier = bb.scope.fullyQualify(identifier);
- namespace = bb.scope.resolve(identifier.names.get(0), null, null);
+ qualified = bb.scope.fullyQualify(identifier);
+ identifier = qualified.identifier;
+ } else {
+ qualified = SqlQualified.create(null, 1, null, identifier);
}
- RexNode e = bb.lookupExp(identifier.names.get(0));
+ RexNode e = bb.lookupExp(qualified);
final String correlationName;
if (e instanceof RexCorrelVariable) {
correlationName = ((RexCorrelVariable) e).getName();
@@ -3315,11 +3318,7 @@ public class SqlToRelConverter {
correlationName = null;
}
- for (String name : Util.skip(identifier.names)) {
- if (namespace != null) {
- name = namespace.translate(name);
- namespace = null;
- }
+ for (String name : qualified.suffixTranslated()) {
final boolean caseSensitive = true; // name already fully-qualified
e = rexBuilder.makeFieldAccess(e, name, caseSensitive);
}
@@ -3970,23 +3969,23 @@ public class SqlToRelConverter {
/**
* Returns an expression with which to reference a from-list item.
*
- * @param name the alias of the from item
+ * @param qualified the alias of the from item
* @return a {@link RexFieldAccess} or {@link RexRangeRef}, or null if
* not found
*/
- RexNode lookupExp(String name) {
- if (nameToNodeMap != null) {
- RexNode node = nameToNodeMap.get(name);
+ RexNode lookupExp(SqlQualified qualified) {
+ if (nameToNodeMap != null && qualified.prefixLength == 1) {
+ RexNode node = nameToNodeMap.get(qualified.identifier.names.get(0));
if (node == null) {
- throw Util.newInternal("Unknown identifier '" + name
- + "' encountered while expanding expression" + node);
+ throw Util.newInternal("Unknown identifier '" + qualified.identifier
+ + "' encountered while expanding expression");
}
return node;
}
int[] offsets = {-1};
final SqlValidatorScope[] ancestorScopes = {null};
SqlValidatorNamespace foundNs =
- scope.resolve(name, ancestorScopes, offsets);
+ scope.resolve(qualified.prefix(), ancestorScopes, offsets);
if (foundNs == null) {
return null;
}
@@ -4007,7 +4006,8 @@ public class SqlToRelConverter {
// e.g. "select from emp as emp join emp.getDepts() as dept".
// Create a temporary expression.
assert isParent;
- DeferredLookup lookup = new DeferredLookup(this, name);
+ DeferredLookup lookup =
+ new DeferredLookup(this, qualified.identifier.names.get(0));
String correlName = createCorrel();
mapCorrelToDeferred.put(correlName, lookup);
final RelDataType rowType = foundNs.getRowType();
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/util/Bug.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/Bug.java b/core/src/main/java/org/apache/calcite/util/Bug.java
index 6d9db47..c91cde4 100644
--- a/core/src/main/java/org/apache/calcite/util/Bug.java
+++ b/core/src/main/java/org/apache/calcite/util/Bug.java
@@ -108,13 +108,6 @@ public abstract class Bug {
public static final boolean FRG78_FIXED = false;
/**
- * Whether <a href="http://issues.eigenbase.org/browse/FRG-140">issue
- * FRG-140: validator does not accept column qualified by schema name</a> is
- * fixed.
- */
- public static final boolean FRG140_FIXED = false;
-
- /**
* Whether <a href="http://issues.eigenbase.org/browse/FRG-187">issue
* FRG-187: FarragoAutoVmOperatorTest.testOverlapsOperator fails</a> is
* fixed.
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 a100315..80fe7ee 100644
--- a/core/src/main/java/org/apache/calcite/util/Util.java
+++ b/core/src/main/java/org/apache/calcite/util/Util.java
@@ -2019,23 +2019,27 @@ public class Util {
return -1;
}
- public static int match2(List<String> strings, String name,
- boolean caseSensitive1) {
- if (caseSensitive1) {
- return strings.indexOf(name);
+ /** Looks for a string within a list of strings, using a given
+ * case-sensitivity policy, and returns the position at which the first match
+ * is found, or -1 if there are no matches. */
+ public static int findMatch(List<String> strings, String seek,
+ boolean caseSensitive) {
+ if (caseSensitive) {
+ return strings.indexOf(seek);
}
for (int i = 0; i < strings.size(); i++) {
String s = strings.get(i);
- if (s.equalsIgnoreCase(name)) {
+ if (s.equalsIgnoreCase(seek)) {
return i;
}
}
return -1;
}
- public static boolean match(boolean caseSensitive, String name1,
- String name0) {
- return caseSensitive ? name0.equals(name1) : name0.equalsIgnoreCase(name1);
+ /** Returns whether a name matches another according to a given
+ * case-sensitivity policy. */
+ public static boolean matches(boolean caseSensitive, String s0, String s1) {
+ return caseSensitive ? s1.equals(s0) : s1.equalsIgnoreCase(s0);
}
/** Returns whether one list is a prefix of another. */
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index d9a5e0f..adcaf93 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -1385,6 +1385,27 @@ public class SqlParserTest {
+ "`DEPT`");
}
+ @Test public void testSchemaTableStar() {
+ sql("select schem.emp.*, emp.empno * dept.deptno\n"
+ + "from schem.emp, dept")
+ .ok("SELECT `SCHEM`.`EMP`.*, (`EMP`.`EMPNO` * `DEPT`.`DEPTNO`)\n"
+ + "FROM `SCHEM`.`EMP`,\n"
+ + "`DEPT`");
+ }
+
+ @Test public void testCatalogSchemaTableStar() {
+ sql("select cat.schem.emp.* from cat.schem.emp")
+ .ok("SELECT `CAT`.`SCHEM`.`EMP`.*\n"
+ + "FROM `CAT`.`SCHEM`.`EMP`");
+ }
+
+ @Test public void testAliasedStar() {
+ // OK in parser; validator will give error
+ sql("select emp.* as foo from emp")
+ .ok("SELECT `EMP`.* AS `FOO`\n"
+ + "FROM `EMP`");
+ }
+
@Test public void testNotExists() {
check(
"select * from dept where not not exists (select * from emp) and true",
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 bd88a5c..65e24a6 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
@@ -63,28 +63,19 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
"KEYWORD(TABLE)",
"KEYWORD(UNNEST)");
- protected static final List<String> AGG_KEYWORDS =
- Arrays.asList(
- "KEYWORD(SELECT)",
- "KEYWORD(TABLE)",
- "KEYWORD(VALUES)",
- "KEYWORD())",
- "KEYWORD(*)",
- "KEYWORD(ALL)",
- "KEYWORD(DISTINCT)");
-
protected static final List<String> SALES_TABLES =
Arrays.asList(
- "TABLE(EMP)",
- "TABLE(EMP_ADDRESS)",
- "TABLE(DEPT)",
- "TABLE(BONUS)",
- "TABLE(SALGRADE)");
+ "TABLE(CATALOG.SALES.EMP)",
+ "TABLE(CATALOG.SALES.EMP_ADDRESS)",
+ "TABLE(CATALOG.SALES.DEPT)",
+ "TABLE(CATALOG.SALES.BONUS)",
+ "TABLE(CATALOG.SALES.SALGRADE)");
private static final List<String> SCHEMAS =
Arrays.asList(
- "SCHEMA(SALES)",
- "SCHEMA(CUSTOMER)");
+ "CATALOG(CATALOG)",
+ "SCHEMA(CATALOG.SALES)",
+ "SCHEMA(CATALOG.CUSTOMER)");
private static final List<String> AB_TABLES =
Arrays.asList(
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 52b3b01..561ede1 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -377,34 +377,35 @@ public class JdbcTest {
*/
@Test public void testSqlAdvisorGetHintsFunction()
throws SQLException, ClassNotFoundException {
- String res = adviseSql("select e.e^ from \"emps\" e");
- assertThat(res,
- equalTo("id=e; names=null; type=MATCH\n"
- + "id=empid; names=[empid]; type=COLUMN\n"));
+ adviseSql("select e.e^ from \"emps\" e",
+ CalciteAssert.checkResultUnordered(
+ "id=e; names=null; type=MATCH",
+ "id=empid; names=[empid]; type=COLUMN"));
}
+
/**
* Tests {@link org.apache.calcite.sql.advise.SqlAdvisorGetHintsFunction}.
*/
@Test public void testSqlAdvisorSchemaNames()
throws SQLException, ClassNotFoundException {
- String res = adviseSql("select empid from \"emps\" e, ^");
- assertThat(res,
- equalTo("id=; names=null; type=MATCH\n"
- + "id=(; names=[(]; type=KEYWORD\n"
- + "id=LATERAL; names=[LATERAL]; type=KEYWORD\n"
- + "id=TABLE; names=[TABLE]; type=KEYWORD\n"
- + "id=UNNEST; names=[UNNEST]; type=KEYWORD\n"
- + "id=hr; names=[hr]; type=SCHEMA\n"
- + "id=metadata; names=[metadata]; type=SCHEMA\n"
- + "id=s; names=[s]; type=SCHEMA\n"
- + "id=hr.dependents; names=[hr, dependents]; type=TABLE\n"
- + "id=hr.depts; names=[hr, depts]; type=TABLE\n"
- + "id=hr.emps; names=[hr, emps]; type=TABLE\n"
- + "id=hr.locations; names=[hr, locations]; type=TABLE\n"));
- }
-
- private String adviseSql(String sql) throws ClassNotFoundException,
- SQLException {
+ adviseSql("select empid from \"emps\" e, ^",
+ CalciteAssert.checkResultUnordered(
+ "id=; names=null; type=MATCH",
+ "id=(; names=[(]; type=KEYWORD",
+ "id=LATERAL; names=[LATERAL]; type=KEYWORD",
+ "id=TABLE; names=[TABLE]; type=KEYWORD",
+ "id=UNNEST; names=[UNNEST]; type=KEYWORD",
+ "id=hr; names=[hr]; type=SCHEMA",
+ "id=metadata; names=[metadata]; type=SCHEMA",
+ "id=s; names=[s]; type=SCHEMA",
+ "id=hr.dependents; names=[hr, dependents]; type=TABLE",
+ "id=hr.depts; names=[hr, depts]; type=TABLE",
+ "id=hr.emps; names=[hr, emps]; type=TABLE",
+ "id=hr.locations; names=[hr, locations]; type=TABLE"));
+ }
+
+ private void adviseSql(String sql, Function<ResultSet, Void> checker)
+ throws ClassNotFoundException, SQLException {
Properties info = new Properties();
info.put("lex", "JAVA");
info.put("quoting", "DOUBLE_QUOTE");
@@ -424,7 +425,10 @@ public class JdbcTest {
SqlParserUtil.StringAndPos sap = SqlParserUtil.findPos(sql);
ps.setString(1, sap.sql);
ps.setInt(2, sap.cursor);
- return CalciteAssert.toString(ps.executeQuery());
+ final ResultSet resultSet = ps.executeQuery();
+ checker.apply(resultSet);
+ resultSet.close();
+ connection.close();
}
/**
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 f070f3d..adf4565 100644
--- a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
+++ b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
@@ -262,16 +262,26 @@ public class MockCatalogReader implements Prepare.CatalogReader {
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 = new ArrayList<SqlMoniker>();
for (MockSchema schema : schemas.values()) {
- result.add(
- new SqlMonikerImpl(schema.name, SqlMonikerType.SCHEMA));
+ 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 1:
+ case 2:
// looking for table names in the given schema
- MockSchema schema = schemas.get(names.get(0));
+ MockSchema schema = schemas.get(names.get(1));
if (schema == null) {
return Collections.emptyList();
}
@@ -279,7 +289,8 @@ public class MockCatalogReader implements Prepare.CatalogReader {
for (String tableName : schema.tableNames) {
result.add(
new SqlMonikerImpl(
- tableName,
+ ImmutableList.of(schema.getCatalogName(), schema.name,
+ tableName),
SqlMonikerType.TABLE));
}
return result;
@@ -289,7 +300,31 @@ public class MockCatalogReader implements Prepare.CatalogReader {
}
public List<String> getSchemaName() {
- return Collections.singletonList(DEFAULT_SCHEMA);
+ return ImmutableList.of(DEFAULT_CATALOG, DEFAULT_SCHEMA);
+ }
+
+ private MockSchema getMockSchema(List<String> names) {
+ return schemas.get(names.get(0));
+ }
+
+ public List<SqlMoniker> getAllSchemaObjectNames2(List<String> names) {
+ List<SqlMoniker> result = new ArrayList<SqlMoniker>();
+ if (names.isEmpty()) {
+ for (MockSchema schema : schemas.values()) {
+ result.add(
+ new SqlMonikerImpl(schema.name, SqlMonikerType.SCHEMA));
+ }
+ }
+ // looking for table names in the given schema
+ MockSchema schema = getMockSchema(names);
+ if (schema != null) {
+ for (String tableName : schema.tableNames) {
+ result.add(
+ new SqlMonikerImpl(
+ tableName, SqlMonikerType.TABLE));
+ }
+ }
+ return result;
}
public RelDataTypeField field(RelDataType rowType, String alias) {
@@ -301,8 +336,12 @@ public class MockCatalogReader implements Prepare.CatalogReader {
return field != null ? field.getIndex() : -1;
}
+ public boolean matches(String string, String name) {
+ return Util.matches(caseSensitive, string, name);
+ }
+
public int match(List<String> strings, String name) {
- return Util.match2(strings, name, caseSensitive);
+ return Util.findMatch(strings, name, caseSensitive);
}
public RelDataType createTypeFromProjection(final RelDataType type,
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 125d393..a1becf6 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -4451,6 +4451,43 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
"Unknown identifier 'EMPNO'");
}
+ @Test public void testStarAliasFails() {
+ sql("select emp.^*^ AS x from emp")
+ .fails("Unknown field '\\*'");
+ }
+
+ @Test public void testNonLocalStar() {
+ // MySQL allows this but we can't, currently
+ sql("select * from emp e where exists (\n"
+ + " select ^e^.* from dept where dept.deptno = e.deptno)")
+ .fails("Unknown identifier 'E'");
+ }
+
+ /**
+ * Parser allows "*" in FROM clause because "*" can occur in any identifier.
+ * But validator must not.
+ *
+ * <p>See also
+ * <a href="https://issues.apache.org/jira/browse/CALCITE-546">[CALCITE-546]
+ * "Allow table, column and field called '*'"</a> (not yet fixed).
+ */
+ @Test public void testStarInFromFails() {
+ sql("select emp.empno AS x from ^sales.*^")
+ .fails("Table 'SALES.\\*' not found");
+ sql("select emp.empno from emp where emp.^*^ is not null")
+ .fails("Unknown field '\\*'");
+ }
+
+ @Test public void testStarDotIdFails() {
+ // Parser allows a star inside (not at end of) compound identifier, but
+ // validator does not
+ sql("select emp.^*^.foo from emp")
+ .fails("Column '\\*' not found in table 'EMP'");
+ // Parser does not allow star dot identifier.
+ sql("select ^*^.foo from emp")
+ .fails("(?s).*Encountered \".\" at .*");
+ }
+
@Test public void testAsColumnList() {
check("select d.a, b from dept as d(a, b)");
checkFails(
@@ -4600,6 +4637,55 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
+ " select 1 from emp where emp.empno = emp.deptno)");
}
+ @Test public void testSchemaTableStar() {
+ sql("select ^sales.e^.* from sales.emp as e")
+ .fails("Unknown identifier 'SALES\\.E'");
+ sql("select sales.dept.* from sales.dept")
+ .type("RecordType(INTEGER NOT NULL DEPTNO,"
+ + " VARCHAR(10) NOT NULL NAME) NOT NULL");
+ sql("select sales.emp.* from emp").ok();
+ sql("select sales.emp.* from emp as emp").ok();
+ // MySQL gives: "Unknown table 'emp'"
+ // (consistent with MySQL)
+ sql("select ^sales.emp^.* from emp as e")
+ .fails("Unknown identifier 'SALES.EMP'");
+ }
+
+ @Test public void testSchemaTableColumn() {
+ sql("select emp.empno from sales.emp").ok();
+ sql("select sales.emp.empno from sales.emp").ok();
+ sql("select sales.emp.empno from sales.emp\n"
+ + "where sales.emp.deptno > 0").ok();
+ sql("select 1 from sales.emp where sales.emp.^bad^ < 0")
+ .fails("Column 'BAD' not found in table 'SALES.EMP'");
+ sql("select ^sales.bad^.empno from sales.emp\n"
+ + "where sales.emp.deptno > 0")
+ .fails("Table 'SALES\\.BAD' not found");
+ sql("select sales.emp.deptno from sales.emp").ok();
+ sql("select 1 from sales.emp where sales.emp.deptno = 10").ok();
+ sql("select 1 from sales.emp order by sales.emp.deptno").ok();
+ // alias does not hide the fully-qualified name if same
+ // (consistent with MySQL)
+ sql("select sales.emp.deptno from sales.emp as emp").ok();
+ // alias hides the fully-qualified name
+ // (consistent with MySQL)
+ sql("select ^sales.emp^.deptno from sales.emp as e")
+ .fails("Table 'SALES\\.EMP' not found");
+ sql("select sales.emp.deptno from sales.emp, ^sales.emp^")
+ .fails("Duplicate relation name 'EMP' in FROM clause");
+ // Table exists but not used in FROM clause
+ sql("select ^sales.emp^.deptno from sales.dept as d1, sales.dept")
+ .fails("Table 'SALES.EMP' not found");
+ // Table does not exist
+ sql("select ^sales.bad^.deptno from sales.dept as d1, sales.dept")
+ .fails("Table 'SALES.BAD' not found");
+ }
+
+ @Ignore("does not work yet")
+ @Test public void testSchemaTableColumnInGroupBy() {
+ sql("select 1 from sales.emp group by sales.emp.deptno").ok(); // TODO:
+ }
+
@Test public void testInvalidGroupBy() {
sql("select ^empno^, deptno from emp group by deptno")
.fails("Expression 'EMPNO' is not being grouped");
@@ -6397,11 +6483,9 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
"RecordType(INTEGER NOT NULL X, INTEGER NOT NULL Y) NOT NULL");
// Qualifying with schema is OK.
- if (Bug.FRG140_FIXED) {
- checkResultType(
- "SELECT customer.contact.coord.x, customer.contact.email, contact.coord.y FROM customer.contact",
- "RecordType(INTEGER NOT NULL X, INTEGER NOT NULL Y) NOT NULL");
- }
+ checkResultType(
+ "SELECT customer.contact.coord.x, customer.contact.email, contact.coord.y FROM customer.contact",
+ "RecordType(INTEGER NOT NULL X, VARCHAR(20) NOT NULL EMAIL, INTEGER NOT NULL Y) NOT NULL");
}
@Test public void testSample() {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/test/resources/sql/misc.oq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/misc.oq b/core/src/test/resources/sql/misc.oq
index ae1b2ea..21eace7 100644
--- a/core/src/test/resources/sql/misc.oq
+++ b/core/src/test/resources/sql/misc.oq
@@ -18,6 +18,21 @@
!use post
!set outputformat mysql
+# [CALCITE-356] Allow column references of the form schema.table.column
+select "hr"."emps"."empid"
+from "hr"."emps";
++-------+
+| empid |
++-------+
+| 100 |
+| 110 |
+| 150 |
+| 200 |
++-------+
+(4 rows)
+
+!ok
+
# CALCITE-307 CAST(timestamp AS DATE) gives ClassCastException
# Based on DRILL-1051
!if (false) {
[2/2] incubator-calcite git commit: [CALCITE-356] Allow column
references of the form schema.table.column
Posted by jh...@apache.org.
[CALCITE-356] Allow column references of the form schema.table.column
SqlValidatorScope.fullyQualify now returns a new class SqlQualified, which contains more information than just name components after an identifier has been fully-qualified in a scope. We plan to cache these, at which point we should be able to make sql-to-rel translation faster and more predictable, since it builds on work already done by the validator.
Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/6c88546e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/6c88546e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/6c88546e
Branch: refs/heads/master
Commit: 6c88546ea8a0deb544df57536ed24466fbb46b85
Parents: 0fda7af
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Dec 18 13:24:37 2014 -0800
Committer: julianhyde <jh...@apache.org>
Committed: Wed Dec 31 20:35:59 2014 -0800
----------------------------------------------------------------------
core/src/main/codegen/templates/Parser.jj | 36 +--
.../calcite/prepare/CalciteCatalogReader.java | 6 +-
.../calcite/rel/type/RelDataTypeImpl.java | 2 +-
.../java/org/apache/calcite/sql/SqlCall.java | 3 +-
.../org/apache/calcite/sql/SqlIdentifier.java | 47 ++-
.../java/org/apache/calcite/sql/SqlNode.java | 5 +-
.../org/apache/calcite/sql/SqlOperator.java | 3 +-
.../java/org/apache/calcite/sql/SqlUtil.java | 99 ++++++
.../apache/calcite/sql/advise/SqlAdvisor.java | 60 +++-
.../calcite/sql/advise/SqlSimpleParser.java | 8 +-
.../apache/calcite/sql/parser/SqlParserPos.java | 116 ++++---
.../apache/calcite/sql/validate/AggChecker.java | 4 +-
.../sql/validate/AggregatingSelectScope.java | 4 +-
.../calcite/sql/validate/CatalogScope.java | 88 ++++++
.../calcite/sql/validate/DelegatingScope.java | 76 +++--
.../apache/calcite/sql/validate/EmptyScope.java | 13 +-
.../apache/calcite/sql/validate/ListScope.java | 59 +++-
.../calcite/sql/validate/OrderByScope.java | 4 +-
.../calcite/sql/validate/ParameterScope.java | 10 +-
.../calcite/sql/validate/SchemaNamespace.java | 60 ++++
.../calcite/sql/validate/SelectScope.java | 2 +-
.../apache/calcite/sql/validate/SqlMoniker.java | 18 ++
.../sql/validate/SqlMonikerComparator.java | 39 ---
.../calcite/sql/validate/SqlMonikerImpl.java | 19 +-
.../calcite/sql/validate/SqlMonikerType.java | 2 +-
.../calcite/sql/validate/SqlQualified.java | 86 ++++++
.../sql/validate/SqlValidatorCatalogReader.java | 2 +
.../calcite/sql/validate/SqlValidatorImpl.java | 306 ++++++++++---------
.../calcite/sql/validate/SqlValidatorScope.java | 17 +-
.../calcite/sql/validate/SqlValidatorUtil.java | 24 +-
.../sql/validate/SqlValidatorWithHints.java | 4 +-
.../apache/calcite/sql/validate/WithScope.java | 6 +-
.../calcite/sql2rel/SqlToRelConverter.java | 42 +--
.../main/java/org/apache/calcite/util/Bug.java | 7 -
.../main/java/org/apache/calcite/util/Util.java | 20 +-
.../calcite/sql/parser/SqlParserTest.java | 21 ++
.../apache/calcite/sql/test/SqlAdvisorTest.java | 25 +-
.../java/org/apache/calcite/test/JdbcTest.java | 50 +--
.../apache/calcite/test/MockCatalogReader.java | 53 +++-
.../apache/calcite/test/SqlValidatorTest.java | 94 +++++-
core/src/test/resources/sql/misc.oq | 15 +
41 files changed, 1078 insertions(+), 477 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 4b28e3b..5f540e7 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -1411,21 +1411,9 @@ SqlNode SelectItem() :
SqlNode SelectExpression() :
{
SqlNode e;
- String id;
- SqlParserPos pos, starPos;
+ SqlParserPos pos;
}
{
- LOOKAHEAD(3)
- id = Identifier() { pos = getPos(); } <DOT> <STAR>
- {
- starPos = getPos();
- return new SqlIdentifier(
- ImmutableList.of(id, "*"),
- null,
- pos.plus(starPos),
- ImmutableList.of(pos, starPos));
- }
- |
<STAR>
{
pos = getPos();
@@ -3504,17 +3492,21 @@ SqlIdentifier CompoundIdentifier() :
list.add(p);
}
(
- <DOT> p = Identifier()
- {
- list.add(p);
- posList.add(getPos());
- }
+ <DOT>
+ (
+ p = Identifier() {
+ list.add(p);
+ posList.add(getPos());
+ }
+ |
+ <STAR> {
+ list.add("*");
+ posList.add(getPos());
+ }
+ )
) *
{
- SqlParserPos[] componentPositions =
- (SqlParserPos []) posList.toArray(
- new SqlParserPos[posList.size()]);
- SqlParserPos pos = SqlParserPos.sum(componentPositions);
+ SqlParserPos pos = SqlParserPos.sum(posList);
return new SqlIdentifier(list, null, pos, posList);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 0f4b4f5..15cf374 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
@@ -209,8 +209,12 @@ public class CalciteCatalogReader implements Prepare.CatalogReader,
return field != null ? field.getIndex() : -1;
}
+ public boolean matches(String string, String name) {
+ return Util.matches(caseSensitive, string, name);
+ }
+
public int match(List<String> strings, String name) {
- return Util.match2(strings, name, caseSensitive);
+ return Util.findMatch(strings, name, caseSensitive);
}
public RelDataType createTypeFromProjection(final RelDataType type,
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 d917667..3b13b9b 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
@@ -79,7 +79,7 @@ public abstract class RelDataTypeImpl
// implement RelDataType
public RelDataTypeField getField(String fieldName, boolean caseSensitive) {
for (RelDataTypeField field : fieldList) {
- if (Util.match(caseSensitive, field.getName(), fieldName)) {
+ if (Util.matches(caseSensitive, field.getName(), fieldName)) {
return field;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/SqlCall.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCall.java b/core/src/main/java/org/apache/calcite/sql/SqlCall.java
index 07f5f5b..7548e01 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlCall.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlCall.java
@@ -26,6 +26,7 @@ import org.apache.calcite.sql.validate.SqlValidatorImpl;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
/**
@@ -110,7 +111,7 @@ public abstract class SqlCall extends SqlNode {
SqlValidator validator,
SqlValidatorScope scope,
SqlParserPos pos,
- List<SqlMoniker> hintList) {
+ Collection<SqlMoniker> hintList) {
for (SqlNode operand : getOperandList()) {
if (operand instanceof SqlIdentifier) {
SqlIdentifier id = (SqlIdentifier) operand;
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 c772331..c5e2420 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
@@ -19,10 +19,9 @@ package org.apache.calcite.sql;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.util.SqlVisitor;
import org.apache.calcite.sql.validate.SqlMonotonicity;
+import org.apache.calcite.sql.validate.SqlQualified;
import org.apache.calcite.sql.validate.SqlValidator;
-import org.apache.calcite.sql.validate.SqlValidatorNamespace;
import org.apache.calcite.sql.validate.SqlValidatorScope;
-import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
@@ -168,8 +167,41 @@ public class SqlIdentifier extends SqlNode {
* available.
*/
public SqlIdentifier getComponent(int ordinal) {
- return new SqlIdentifier(names.get(ordinal),
- getComponentParserPosition(ordinal));
+ return getComponent(ordinal, ordinal + 1);
+ }
+
+ public SqlIdentifier getComponent(int from, int to) {
+ final SqlParserPos pos;
+ final ImmutableList<SqlParserPos> pos2;
+ if (componentPositions == null) {
+ pos2 = null;
+ pos = this.pos;
+ } else {
+ pos2 = componentPositions.subList(from, to);
+ pos = SqlParserPos.sum(pos2);
+ }
+ return new SqlIdentifier(names.subList(from, to), collation, pos, pos2);
+ }
+
+ /**
+ * Creates an identifier that consists of this identifier plus a name segment.
+ * Does not modify this identifier.
+ */
+ public SqlIdentifier plus(String name, SqlParserPos pos) {
+ final ImmutableList<String> names =
+ ImmutableList.<String>builder().addAll(this.names).add(name).build();
+ final ImmutableList.Builder<SqlParserPos> builder = ImmutableList.builder();
+ final ImmutableList<SqlParserPos> componentPositions =
+ builder.addAll(this.componentPositions).add(pos).build();
+ final SqlParserPos pos2 =
+ SqlParserPos.sum(builder.add(this.pos).build());
+ return new SqlIdentifier(names, collation, pos2, componentPositions);
+ }
+
+ /** Creates an identifier that consists of all but the last {@code n}
+ * name segments of this one. */
+ public SqlIdentifier skipLast(int n) {
+ return getComponent(0, names.size() - n);
}
public void unparse(
@@ -270,10 +302,9 @@ public class SqlIdentifier extends SqlNode {
if (call != null) {
return call.getMonotonicity(scope);
}
- final SqlIdentifier fqId = scope.fullyQualify(this);
- final SqlValidatorNamespace ns =
- SqlValidatorUtil.lookup(scope, Util.skipLast(fqId.names));
- return ns.resolve().getMonotonicity(Util.last(fqId.names));
+ final SqlQualified qualified = scope.fullyQualify(this);
+ final SqlIdentifier fqId = qualified.identifier;
+ return qualified.namespace.resolve().getMonotonicity(Util.last(fqId.names));
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/SqlNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlNode.java b/core/src/main/java/org/apache/calcite/sql/SqlNode.java
index 75e3979..6708ba5 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlNode.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlNode.java
@@ -26,6 +26,7 @@ import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.util.Util;
+import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -43,7 +44,7 @@ public abstract class SqlNode implements Cloneable {
//~ Instance fields --------------------------------------------------------
- private final SqlParserPos pos;
+ protected final SqlParserPos pos;
//~ Constructors -----------------------------------------------------------
@@ -212,7 +213,7 @@ public abstract class SqlNode implements Cloneable {
SqlValidator validator,
SqlValidatorScope scope,
SqlParserPos pos,
- List<SqlMoniker> hintList) {
+ Collection<SqlMoniker> hintList) {
// no valid options
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/SqlOperator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java
index cab1ecc..08325ab 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java
@@ -30,6 +30,7 @@ import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.util.Util;
+import java.util.Arrays;
import java.util.List;
import static org.apache.calcite.util.Static.RESOURCE;
@@ -232,7 +233,7 @@ public abstract class SqlOperator {
SqlLiteral functionQualifier,
SqlParserPos pos,
SqlNode... operands) {
- pos = pos.plusAll(operands);
+ pos = pos.plusAll(Arrays.asList(operands));
return new SqlBasicCall(this, operands, pos, false, functionQualifier);
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
index e2430e7..4ee7ece 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
@@ -27,12 +27,15 @@ import org.apache.calcite.sql.parser.SqlParserPos;
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.sql.util.SqlBasicVisitor;
import org.apache.calcite.util.BarfingInvocationHandler;
import org.apache.calcite.util.ConversionUtil;
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.nio.charset.Charset;
@@ -764,6 +767,23 @@ public abstract class SqlUtil {
}
}
+ /** Returns a list of ancestors of {@code predicate} within a given
+ * {@code SqlNode} tree.
+ *
+ * <p>The first element of the list is {@code root}, and the last is
+ * the node that matched {@code predicate}. Throws if no node matches.
+ */
+ public static ImmutableList<SqlNode> getAncestry(SqlNode root,
+ Predicate<SqlNode> predicate, Predicate<SqlNode> postPredicate) {
+ try {
+ new Genealogist(predicate, postPredicate).visitChild(root);
+ throw new AssertionError("not found: " + predicate + " in " + root);
+ } catch (Util.FoundOne e) {
+ //noinspection unchecked
+ return (ImmutableList<SqlNode>) e.getNode();
+ }
+ }
+
//~ Inner Classes ----------------------------------------------------------
/**
@@ -792,6 +812,85 @@ public abstract class SqlUtil {
return identifierQuoteString;
}
}
+
+ /** Walks over a {@link org.apache.calcite.sql.SqlNode} tree and returns the
+ * ancestry stack when it finds a given node. */
+ private static class Genealogist extends SqlBasicVisitor<Void> {
+ private final List<SqlNode> ancestors = Lists.newArrayList();
+ private final Predicate<SqlNode> predicate;
+ private final Predicate<SqlNode> postPredicate;
+
+ Genealogist(Predicate<SqlNode> predicate,
+ Predicate<SqlNode> postPredicate) {
+ this.predicate = predicate;
+ this.postPredicate = postPredicate;
+ }
+
+ private Void check(SqlNode node) {
+ preCheck(node);
+ postCheck(node);
+ return null;
+ }
+
+ private Void preCheck(SqlNode node) {
+ if (predicate.apply(node)) {
+ throw new Util.FoundOne(ImmutableList.copyOf(ancestors));
+ }
+ return null;
+ }
+
+ private Void postCheck(SqlNode node) {
+ if (postPredicate.apply(node)) {
+ throw new Util.FoundOne(ImmutableList.copyOf(ancestors));
+ }
+ return null;
+ }
+
+ private void visitChild(SqlNode node) {
+ if (node == null) {
+ return;
+ }
+ ancestors.add(node);
+ node.accept(this);
+ ancestors.remove(ancestors.size() - 1);
+ }
+
+ @Override public Void visit(SqlIdentifier id) {
+ return check(id);
+ }
+
+ @Override public Void visit(SqlCall call) {
+ preCheck(call);
+ for (SqlNode node : call.getOperandList()) {
+ visitChild(node);
+ }
+ return postCheck(call);
+ }
+
+ @Override public Void visit(SqlIntervalQualifier intervalQualifier) {
+ return check(intervalQualifier);
+ }
+
+ @Override public Void visit(SqlLiteral literal) {
+ return check(literal);
+ }
+
+ @Override public Void visit(SqlNodeList nodeList) {
+ preCheck(nodeList);
+ for (SqlNode node : nodeList) {
+ visitChild(node);
+ }
+ return postCheck(nodeList);
+ }
+
+ @Override public Void visit(SqlDynamicParam param) {
+ return check(param);
+ }
+
+ @Override public Void visit(SqlDataTypeSpec type) {
+ return check(type);
+ }
+ }
}
// End SqlUtil.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisor.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisor.java
index 818d200..06d10c0 100644
--- a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisor.java
+++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisor.java
@@ -20,6 +20,8 @@ import org.apache.calcite.runtime.CalciteContextException;
import org.apache.calcite.runtime.CalciteException;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlSelect;
+import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.parser.SqlAbstractParserImpl;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.sql.parser.SqlParser;
@@ -31,6 +33,10 @@ import org.apache.calcite.sql.validate.SqlValidatorWithHints;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.trace.CalciteTrace;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -46,12 +52,13 @@ public class SqlAdvisor {
//~ Static fields/initializers ---------------------------------------------
public static final Logger LOGGER = CalciteTrace.PARSER_LOGGER;
+ private static final String HINT_TOKEN = "_suggest_";
+ private static final String UPPER_HINT_TOKEN = HINT_TOKEN.toUpperCase();
//~ Instance fields --------------------------------------------------------
// Flags indicating precision/scale combinations
private final SqlValidatorWithHints validator;
- private final String hintToken = "_suggest_";
//~ Constructors -----------------------------------------------------------
@@ -68,7 +75,7 @@ public class SqlAdvisor {
//~ Methods ----------------------------------------------------------------
/**
- * Gets completion hints for a partially completed or syntatically incorrect
+ * Gets completion hints for a partially completed or syntactically incorrect
* sql statement with cursor pointing to the position where completion hints
* are requested.
*
@@ -79,8 +86,8 @@ public class SqlAdvisor {
* whitespace, the replaced string is empty. The replaced string is never
* null.
*
- * @param sql A partial or syntatically incorrect sql statement for which
- * to retrieve completion hints
+ * @param sql A partial or syntactically incorrect sql statement for
+ * which to retrieve completion hints
* @param cursor to indicate the 0-based cursor position in the query at
* @param replaced String which is being replaced (output)
* @return completion hints
@@ -164,7 +171,7 @@ public class SqlAdvisor {
public List<SqlMoniker> getCompletionHints0(String sql, int cursor) {
String simpleSql = simplifySql(sql, cursor);
- int idx = simpleSql.indexOf(hintToken);
+ int idx = simpleSql.indexOf(HINT_TOKEN);
if (idx < 0) {
return Collections.emptyList();
}
@@ -173,10 +180,10 @@ public class SqlAdvisor {
}
/**
- * Gets completion hints for a syntatically correct sql statement with dummy
+ * Gets completion hints for a syntactically correct sql statement with dummy
* SqlIdentifier
*
- * @param sql A syntatically correct sql statement for which to retrieve
+ * @param sql A syntactically correct sql statement for which to retrieve
* completion hints
* @param pos to indicate the line and column position in the query at which
* completion hints need to be retrieved. For example, "select
@@ -205,6 +212,12 @@ public class SqlAdvisor {
+ sql.substring(x);
tryParse(sql, hintList);
+ final SqlMoniker star =
+ new SqlMonikerImpl(ImmutableList.of("*"), SqlMonikerType.KEYWORD);
+ if (hintList.contains(star) && !isSelectListItem(sqlNode, pos)) {
+ hintList.remove(star);
+ }
+
// Add the identifiers which are expected at the point of interest.
try {
validator.validate(sqlNode);
@@ -221,6 +234,29 @@ public class SqlAdvisor {
return hintList;
}
+ private static boolean isSelectListItem(SqlNode root,
+ final SqlParserPos pos) {
+ List<SqlNode> nodes = SqlUtil.getAncestry(root,
+ new Predicate<SqlNode>() {
+
+ public boolean apply(SqlNode input) {
+ return input instanceof SqlIdentifier
+ && Util.last(((SqlIdentifier) input).names)
+ .equals(UPPER_HINT_TOKEN);
+ }
+ },
+ new Predicate<SqlNode>() {
+ public boolean apply(SqlNode input) {
+ return input.getParserPosition().startsAt(pos);
+ }
+ });
+ assert nodes.get(0) == root;
+ nodes = Lists.reverse(nodes);
+ return nodes.size() > 2
+ && nodes.get(2) instanceof SqlSelect
+ && nodes.get(1) == ((SqlSelect) nodes.get(2)).getSelectList();
+ }
+
/**
* Tries to parse a SQL statement.
*
@@ -289,11 +325,11 @@ public class SqlAdvisor {
* Attempts to complete and validate a given partially completed sql
* statement, and returns whether it is valid.
*
- * @param sql A partial or syntatically incorrect sql statement to validate
+ * @param sql A partial or syntactically incorrect sql statement to validate
* @return whether SQL statement is valid
*/
public boolean isValid(String sql) {
- SqlSimpleParser simpleParser = new SqlSimpleParser(hintToken);
+ SqlSimpleParser simpleParser = new SqlSimpleParser(HINT_TOKEN);
String simpleSql = simpleParser.simplifySql(sql);
SqlNode sqlNode;
try {
@@ -351,16 +387,16 @@ public class SqlAdvisor {
}
/**
- * Turns a partially completed or syntatically incorrect sql statement into
+ * Turns a partially completed or syntactically incorrect sql statement into
* a simplified, valid one that can be passed into getCompletionHints()
*
- * @param sql A partial or syntatically incorrect sql statement
+ * @param sql A partial or syntactically incorrect sql statement
* @param cursor to indicate column position in the query at which
* completion hints need to be retrieved.
* @return a completed, valid (and possibly simplified SQL statement
*/
public String simplifySql(String sql, int cursor) {
- SqlSimpleParser parser = new SqlSimpleParser(hintToken);
+ SqlSimpleParser parser = new SqlSimpleParser(HINT_TOKEN);
return parser.simplifySql(sql, cursor);
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/advise/SqlSimpleParser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlSimpleParser.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlSimpleParser.java
index 0bf8ce2..0ad0d32 100644
--- a/core/src/main/java/org/apache/calcite/sql/advise/SqlSimpleParser.java
+++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlSimpleParser.java
@@ -105,7 +105,7 @@ public class SqlSimpleParser {
* Turns a partially completed or syntactically incorrect sql statement into
* a simplified, valid one that can be passed into getCompletionHints().
*
- * @param sql A partial or syntatically incorrect sql statement
+ * @param sql A partial or syntactically incorrect sql statement
* @param cursor to indicate column position in the query at which
* completion hints need to be retrieved.
* @return a completed, valid (and possibly simplified SQL statement
@@ -123,10 +123,10 @@ public class SqlSimpleParser {
}
/**
- * Turns a partially completed or syntatically incorrect sql statement into
+ * Turns a partially completed or syntactically incorrect sql statement into
* a simplified, valid one that can be validated
*
- * @param sql A partial or syntatically incorrect sql statement
+ * @param sql A partial or syntactically incorrect sql statement
* @return a completed, valid (and possibly simplified) SQL statement
*/
public String simplifySql(String sql) {
@@ -140,7 +140,7 @@ public class SqlSimpleParser {
list.add(token);
}
- // Gather consecutive subsequences of tokens into subqueries.
+ // Gather consecutive sub-sequences of tokens into subqueries.
List<Token> outList = new ArrayList<Token>();
consumeQuery(list.listIterator(), outList);
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java
index 95d704c..5028282 100644
--- a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java
+++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java
@@ -18,7 +18,11 @@ package org.apache.calcite.sql.parser;
import org.apache.calcite.sql.SqlNode;
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
import java.io.Serializable;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -125,14 +129,14 @@ public class SqlParserPos implements Serializable {
return endColumnNumber;
}
- // implements Object
- public String toString() {
+ @Override public String toString() {
return RESOURCE.parserContext(lineNumber, columnNumber).str();
}
/**
- * Combines this parser position with another to create a position which
- * spans from the first point in the first to the last point in the other.
+ * Combines this parser position with another to create a
+ * position that spans from the first point in the first to the last point
+ * in the other.
*/
public SqlParserPos plus(SqlParserPos pos) {
return new SqlParserPos(
@@ -144,32 +148,40 @@ public class SqlParserPos implements Serializable {
/**
* Combines this parser position with an array of positions to create a
- * position which spans from the first point in the first to the last point
+ * position that spans from the first point in the first to the last point
* in the other.
*/
public SqlParserPos plusAll(SqlNode[] nodes) {
- int line = getLineNum();
- int column = getColumnNum();
- int endLine = getEndLineNum();
- int endColumn = getEndColumnNum();
- return sum(nodes, line, column, endLine, endColumn);
+ return plusAll(Arrays.asList(nodes));
}
/**
* Combines this parser position with a list of positions.
*/
public SqlParserPos plusAll(Collection<SqlNode> nodeList) {
- final SqlNode[] nodes = nodeList.toArray(new SqlNode[nodeList.size()]);
- return plusAll(nodes);
+ int line = getLineNum();
+ int column = getColumnNum();
+ int endLine = getEndLineNum();
+ int endColumn = getEndColumnNum();
+ return sum(toPos(nodeList), line, column, endLine, endColumn);
}
/**
* Combines the parser positions of an array of nodes to create a position
* which spans from the beginning of the first to the end of the last.
*/
- public static SqlParserPos sum(
- SqlNode[] nodes) {
- return sum(nodes, Integer.MAX_VALUE, Integer.MAX_VALUE, -1, -1);
+ public static SqlParserPos sum(SqlNode[] nodes) {
+ final Iterable<SqlParserPos> poses = toPos(Arrays.asList(nodes));
+ return sum(poses, Integer.MAX_VALUE, Integer.MAX_VALUE, -1, -1);
+ }
+
+ private static Iterable<SqlParserPos> toPos(Iterable<SqlNode> nodes) {
+ return Iterables.transform(nodes,
+ new Function<SqlNode, SqlParserPos>() {
+ public SqlParserPos apply(SqlNode input) {
+ return input.getParserPosition();
+ }
+ });
}
/**
@@ -181,56 +193,10 @@ public class SqlParserPos implements Serializable {
}
/**
- * Computes the parser position which is the sum of the positions of an
- * array of parse tree nodes and of a parser position represented by (line,
- * column, endLine, endColumn).
- *
- * @param nodes Array of parse tree nodes
- * @param line Start line
- * @param column Start column
- * @param endLine End line
- * @param endColumn End column
- * @return Sum of parser positions
- */
- private static SqlParserPos sum(
- SqlNode[] nodes,
- int line,
- int column,
- int endLine,
- int endColumn) {
- int testLine;
- int testColumn;
- for (SqlNode node : nodes) {
- if (node == null) {
- continue;
- }
- SqlParserPos pos = node.getParserPosition();
- if (pos.equals(SqlParserPos.ZERO)) {
- continue;
- }
- testLine = pos.getLineNum();
- testColumn = pos.getColumnNum();
- if (testLine < line || testLine == line && testColumn < column) {
- line = testLine;
- column = testColumn;
- }
-
- testLine = pos.getEndLineNum();
- testColumn = pos.getEndColumnNum();
- if (testLine > endLine || testLine == endLine && testColumn > endColumn) {
- endLine = testLine;
- endColumn = testColumn;
- }
- }
- return new SqlParserPos(line, column, endLine, endColumn);
- }
-
- /**
* Combines an array of parser positions to create a position which spans
* from the beginning of the first to the end of the last.
*/
- public static SqlParserPos sum(
- SqlParserPos[] poses) {
+ public static SqlParserPos sum(Iterable<SqlParserPos> poses) {
return sum(poses, Integer.MAX_VALUE, Integer.MAX_VALUE, -1, -1);
}
@@ -247,7 +213,7 @@ public class SqlParserPos implements Serializable {
* @return Sum of parser positions
*/
private static SqlParserPos sum(
- SqlParserPos[] poses,
+ Iterable<SqlParserPos> poses,
int line,
int column,
int endLine,
@@ -255,7 +221,7 @@ public class SqlParserPos implements Serializable {
int testLine;
int testColumn;
for (SqlParserPos pos : poses) {
- if (pos == null) {
+ if (pos == null || pos.equals(SqlParserPos.ZERO)) {
continue;
}
testLine = pos.getLineNum();
@@ -274,6 +240,28 @@ public class SqlParserPos implements Serializable {
}
return new SqlParserPos(line, column, endLine, endColumn);
}
+
+ public boolean overlaps(SqlParserPos pos) {
+ return startsBefore(pos) && endsAfter(pos)
+ || pos.startsBefore(this) && pos.endsAfter(this);
+ }
+
+ private boolean startsBefore(SqlParserPos pos) {
+ return lineNumber < pos.lineNumber
+ || lineNumber == pos.lineNumber
+ && columnNumber <= pos.columnNumber;
+ }
+
+ private boolean endsAfter(SqlParserPos pos) {
+ return endLineNumber > pos.endLineNumber
+ || endLineNumber == pos.endLineNumber
+ && endColumnNumber >= pos.endColumnNumber;
+ }
+
+ public boolean startsAt(SqlParserPos pos) {
+ return lineNumber == pos.lineNumber
+ && columnNumber == pos.columnNumber;
+ }
}
// End SqlParserPos.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java b/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
index 1c32594..69bf51c 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
@@ -99,8 +99,8 @@ class AggChecker extends SqlBasicVisitor<Void> {
// it fully-qualified.
// TODO: It would be better if we always compared fully-qualified
// to fully-qualified.
- final SqlIdentifier fqId = Stacks.peek(scopes).fullyQualify(id);
- if (isGroupExpr(fqId)) {
+ final SqlQualified fqId = Stacks.peek(scopes).fullyQualify(id);
+ if (isGroupExpr(fqId.identifier)) {
return null;
}
SqlNode originalExpr = validator.getOriginal(id);
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/validate/AggregatingSelectScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AggregatingSelectScope.java b/core/src/main/java/org/apache/calcite/sql/validate/AggregatingSelectScope.java
index 4584d6f..58fda81 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/AggregatingSelectScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/AggregatingSelectScope.java
@@ -88,8 +88,8 @@ public class AggregatingSelectScope
// We deep-copy the group-list in case subsequent validation
// modifies it and makes it no longer equivalent. While copying,
// we fully qualify all identifiers.
- final SqlNodeList groupList =
- SqlValidatorUtil.DeepCopier.copy(parent, select.getGroup());
+ final SqlNodeList groupList = select.getGroup();
+// SqlValidatorUtil.DeepCopier.copy(parent, select.getGroup());
for (SqlNode groupExpr : groupList) {
SqlValidatorUtil.analyzeGroupItem(this, temporaryGroupExprList,
groupExprProjection, builder, groupExpr);
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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
new file mode 100644
index 0000000..c2ef43e
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/validate/CatalogScope.java
@@ -0,0 +1,88 @@
+/*
+ * 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.linq4j.Linq4j;
+import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.linq4j.function.Predicate1;
+import org.apache.calcite.sql.SqlNode;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Implementation of {@link SqlValidatorScope} that can see all schemas in the
+ * current catalog.
+ *
+ * <p>Occurs near the root of the scope stack; its parent is typically
+ * {@link EmptyScope}.
+ *
+ * <p>Helps resolve {@code schema.table.column} column references, such as
+ *
+ * <blockquote><pre>select sales.emp.empno from sales.emp</pre></blockquote>
+ */
+class CatalogScope extends DelegatingScope {
+ /** Fully-qualified name of the catalog. Typically empty or ["CATALOG"]. */
+ final ImmutableList<String> names;
+ private final Set<List<String>> schemaNames;
+
+ //~ Constructors -----------------------------------------------------------
+
+ CatalogScope(SqlValidatorScope parent, List<String> names) {
+ super(parent);
+ this.names = ImmutableList.copyOf(names);
+ this.schemaNames =
+ Linq4j.asEnumerable(
+ validator.getCatalogReader()
+ .getAllSchemaObjectNames(ImmutableList.<String>of()))
+ .where(
+ new Predicate1<SqlMoniker>() {
+ public boolean apply(SqlMoniker input) {
+ return input.getType() == SqlMonikerType.SCHEMA;
+ }
+ })
+ .select(
+ new Function1<SqlMoniker, List<String>>() {
+ public List<String> apply(SqlMoniker input) {
+ return input.getFullyQualifiedNames();
+ }
+ })
+ .into(Sets.<List<String>>newHashSet());
+ }
+
+ //~ Methods ----------------------------------------------------------------
+
+ public SqlNode getNode() {
+ 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/incubator-calcite/blob/6c88546e/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 e23d886..72c533e 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
@@ -25,9 +25,11 @@ import org.apache.calcite.sql.SqlNodeList;
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 com.google.common.collect.ImmutableList;
+import java.util.Collection;
import java.util.List;
import static org.apache.calcite.util.Static.RESOURCE;
@@ -72,10 +74,10 @@ public abstract class DelegatingScope implements SqlValidatorScope {
}
public SqlValidatorNamespace resolve(
- String name,
+ List<String> names,
SqlValidatorScope[] ancestorOut,
int[] offsetOut) {
- return parent.resolve(name, ancestorOut, offsetOut);
+ return parent.resolve(names, ancestorOut, offsetOut);
}
protected void addColumnNames(
@@ -101,11 +103,12 @@ public abstract class DelegatingScope implements SqlValidatorScope {
parent.findAllColumnNames(result);
}
- public void findAliases(List<SqlMoniker> result) {
+ public void findAliases(Collection<SqlMoniker> result) {
parent.findAliases(result);
}
- public String findQualifyingTableName(String columnName, SqlNode ctx) {
+ public Pair<String, SqlValidatorNamespace>
+ findQualifyingTableName(String columnName, SqlNode ctx) {
return parent.findQualifyingTableName(columnName, ctx);
}
@@ -139,19 +142,19 @@ public abstract class DelegatingScope implements SqlValidatorScope {
*
* <p>If the identifier cannot be resolved, throws. Never returns null.
*/
- public SqlIdentifier fullyQualify(SqlIdentifier identifier) {
+ public SqlQualified fullyQualify(SqlIdentifier identifier) {
if (identifier.isStar()) {
- return identifier;
+ return SqlQualified.create(this, 1, null, identifier);
}
- String tableName;
String columnName;
-
switch (identifier.names.size()) {
case 1:
columnName = identifier.names.get(0);
- tableName =
+ final Pair<String, SqlValidatorNamespace> pair =
findQualifyingTableName(columnName, identifier);
+ final String tableName = pair.left;
+ final SqlValidatorNamespace namespace = pair.right;
// todo: do implicit collation here
final SqlParserPos pos = identifier.getParserPosition();
@@ -162,32 +165,39 @@ public abstract class DelegatingScope implements SqlValidatorScope {
pos,
ImmutableList.of(SqlParserPos.ZERO, pos));
validator.setOriginal(expanded, identifier);
- return expanded;
-
- case 2:
- tableName = identifier.names.get(0);
- final SqlValidatorNamespace fromNs = resolve(tableName, null, null);
- if (fromNs == null) {
- throw validator.newValidationError(identifier.getComponent(0),
- RESOURCE.tableNameNotFound(tableName));
- }
- columnName = identifier.names.get(1);
- final RelDataType fromRowType = fromNs.getRowType();
- final RelDataTypeField field =
- validator.catalogReader.field(fromRowType, columnName);
- if (field != null) {
- identifier.setName(1, field.getName()); // normalize case to match defn
- return identifier; // it was fine already
- } else {
- throw validator.newValidationError(identifier.getComponent(1),
- RESOURCE.columnNotFoundInTable(columnName, tableName));
- }
+ return SqlQualified.create(this, 1, namespace, expanded);
default:
- // NOTE jvs 26-May-2004: lengths greater than 2 are possible
- // for row and structured types
- assert identifier.names.size() > 0;
- return identifier;
+ SqlValidatorNamespace fromNs = null;
+ 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) {
+ break;
+ }
+ }
+ if (fromNs == null || fromNs instanceof SchemaNamespace) {
+ final SqlIdentifier prefix1 = identifier.skipLast(1);
+ throw validator.newValidationError(prefix1,
+ RESOURCE.tableNameNotFound(prefix1.toString()));
+ }
+ 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()));
+ }
+ identifier.setName(j, field.getName()); // normalize case to match defn
+ fromRowType = field.getType();
+ }
+ return SqlQualified.create(this, i, fromNs, identifier);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 6376bd7..9da3bff 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
@@ -25,7 +25,9 @@ import org.apache.calcite.sql.SqlLiteral;
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 java.util.Collection;
import java.util.List;
import static org.apache.calcite.util.Static.RESOURCE;
@@ -54,8 +56,8 @@ class EmptyScope implements SqlValidatorScope {
return validator;
}
- public SqlIdentifier fullyQualify(SqlIdentifier identifier) {
- return identifier;
+ public SqlQualified fullyQualify(SqlIdentifier identifier) {
+ return SqlQualified.create(this, 1, null, identifier);
}
public SqlNode getNode() {
@@ -63,7 +65,7 @@ class EmptyScope implements SqlValidatorScope {
}
public SqlValidatorNamespace resolve(
- String name,
+ List<String> names,
SqlValidatorScope[] ancestorOut,
int[] offsetOut) {
return null;
@@ -86,7 +88,7 @@ class EmptyScope implements SqlValidatorScope {
public void findAllTableNames(List<SqlMoniker> result) {
}
- public void findAliases(List<SqlMoniker> result) {
+ public void findAliases(Collection<SqlMoniker> result) {
}
public RelDataType resolveColumn(String name, SqlNode ctx) {
@@ -101,7 +103,8 @@ class EmptyScope implements SqlValidatorScope {
// valid
}
- public String findQualifyingTableName(String columnName, SqlNode ctx) {
+ public Pair<String, SqlValidatorNamespace>
+ findQualifyingTableName(String columnName, SqlNode ctx) {
throw validator.newValidationError(ctx,
RESOURCE.columnNotFound(columnName));
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 80af917..8567cc5 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
@@ -23,6 +23,7 @@ import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import static org.apache.calcite.util.Static.RESOURCE;
@@ -78,6 +79,47 @@ public abstract class ListScope extends DelegatingScope {
}
}
+ /** 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 {
+ // 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;
+ }
+ }
+
+ // 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();
+ if (table != null) {
+ final SqlValidatorTable table2 =
+ validator.catalogReader.getTable(names);
+ if (table2 != null
+ && table.getQualifiedName().equals(table2.getQualifiedName())) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
public void findAllColumnNames(List<SqlMoniker> result) {
for (Pair<String, SqlValidatorNamespace> pair : children) {
addColumnNames(pair.right, result);
@@ -85,22 +127,21 @@ public abstract class ListScope extends DelegatingScope {
parent.findAllColumnNames(result);
}
- public void findAliases(List<SqlMoniker> result) {
+ public void findAliases(Collection<SqlMoniker> result) {
for (Pair<String, SqlValidatorNamespace> pair : children) {
result.add(new SqlMonikerImpl(pair.left, SqlMonikerType.TABLE));
}
parent.findAliases(result);
}
- public String findQualifyingTableName(
- final String columnName,
- SqlNode ctx) {
+ public Pair<String, SqlValidatorNamespace>
+ findQualifyingTableName(final String columnName, SqlNode ctx) {
int count = 0;
- String tableName = null;
+ 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.left;
+ tableName = child;
count++;
}
}
@@ -116,11 +157,11 @@ public abstract class ListScope extends DelegatingScope {
}
public SqlValidatorNamespace resolve(
- String name,
+ List<String> names,
SqlValidatorScope[] ancestorOut,
int[] offsetOut) {
// First resolve by looking through the child namespaces.
- final int i = validator.catalogReader.match(Pair.left(children), name);
+ final int i = findChild(names);
if (i >= 0) {
if (ancestorOut != null) {
ancestorOut[0] = this;
@@ -133,7 +174,7 @@ public abstract class ListScope extends DelegatingScope {
// Then call the base class method, which will delegate to the
// parent scope.
- return parent.resolve(name, ancestorOut, offsetOut);
+ return parent.resolve(names, ancestorOut, offsetOut);
}
public RelDataType resolveColumn(String columnName, SqlNode ctx) {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 ea38156..55b3af9 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
@@ -65,7 +65,7 @@ public class OrderByScope extends DelegatingScope {
addColumnNames(ns, result);
}
- public SqlIdentifier fullyQualify(SqlIdentifier identifier) {
+ public SqlQualified fullyQualify(SqlIdentifier identifier) {
// If it's a simple identifier, look for an alias.
if (identifier.isSimple()
&& validator.getConformance().isSortByAlias()) {
@@ -74,7 +74,7 @@ public class OrderByScope extends DelegatingScope {
validator.getNamespace(select);
final RelDataType rowType = selectNs.getRowType();
if (validator.catalogReader.field(rowType, name) != null) {
- return identifier;
+ return SqlQualified.create(this, 1, selectNs, identifier);
}
}
return super.fullyQualify(identifier);
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 6a5d86c..f1a4e91 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,6 +20,7 @@ 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;
/**
@@ -48,8 +49,8 @@ public class ParameterScope extends EmptyScope {
//~ Methods ----------------------------------------------------------------
- public SqlIdentifier fullyQualify(SqlIdentifier identifier) {
- return identifier;
+ public SqlQualified fullyQualify(SqlIdentifier identifier) {
+ return SqlQualified.create(this, 1, null, identifier);
}
public SqlValidatorScope getOperandScope(SqlCall call) {
@@ -57,10 +58,11 @@ public class ParameterScope extends EmptyScope {
}
public SqlValidatorNamespace resolve(
- String name,
+ List<String> names,
SqlValidatorScope[] ancestorOut,
int[] offsetOut) {
- final RelDataType type = nameToTypeMap.get(name);
+ assert names.size() == 1;
+ final RelDataType type = nameToTypeMap.get(names.get(0));
return new ParameterNamespace(validator, type);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java
new file mode 100644
index 0000000..e8347c4
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java
@@ -0,0 +1,60 @@
+/*
+ * 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.RelDataTypeFactory;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.util.Util;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/** Namespace based on a schema.
+ *
+ * <p>The visible names are tables and sub-schemas.
+ */
+class SchemaNamespace extends AbstractNamespace {
+ /** The path of this schema. */
+ private final ImmutableList<String> names;
+
+ /** Creates a SchemaNamespace. */
+ SchemaNamespace(SqlValidatorImpl validator, ImmutableList<String> names) {
+ super(validator, null);
+ this.names = Preconditions.checkNotNull(names);
+ }
+
+ protected RelDataType validateImpl() {
+ final RelDataTypeFactory.FieldInfoBuilder builder =
+ validator.getTypeFactory().builder();
+ for (SqlMoniker moniker
+ : validator.catalogReader.getAllSchemaObjectNames(names)) {
+ final List<String> names1 = moniker.getFullyQualifiedNames();
+ final SqlValidatorTable table = validator.catalogReader.getTable(names1);
+ builder.add(Util.last(names1), table.getRowType());
+ }
+ return builder.build();
+ }
+
+ public SqlNode getNode() {
+ return null;
+ }
+}
+
+// End SchemaNamespace.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/validate/SelectScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SelectScope.java b/core/src/main/java/org/apache/calcite/sql/validate/SelectScope.java
index 76ddeb1..ea6db19 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SelectScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SelectScope.java
@@ -209,7 +209,7 @@ public class SelectScope extends ListScope {
final SelectScope parentScope = (SelectScope) walker;
return parentScope.existingWindowName(winName);
}
- walker = parent;
+ walker = ((DelegatingScope) walker).parent;
}
return false;
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/validate/SqlMoniker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlMoniker.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlMoniker.java
index 72f8ffd..66c47f5 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlMoniker.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlMoniker.java
@@ -18,12 +18,30 @@ package org.apache.calcite.sql.validate;
import org.apache.calcite.sql.SqlIdentifier;
+import com.google.common.collect.Ordering;
+
+import java.util.Comparator;
import java.util.List;
/**
* An interface of an object identifier that represents a SqlIdentifier
*/
public interface SqlMoniker {
+ Comparator<SqlMoniker> COMPARATOR =
+ new Comparator<SqlMoniker>() {
+ final Ordering<Iterable<String>> listOrdering =
+ Ordering.<String>natural().lexicographical();
+
+ public int compare(SqlMoniker o1, SqlMoniker o2) {
+ int c = o1.getType().compareTo(o2.getType());
+ if (c == 0) {
+ c = listOrdering.compare(o1.getFullyQualifiedNames(),
+ o2.getFullyQualifiedNames());
+ }
+ return c;
+ }
+ };
+
//~ Methods ----------------------------------------------------------------
/**
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerComparator.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerComparator.java
deleted file mode 100644
index 85eb1c0..0000000
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerComparator.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 java.util.Comparator;
-
-/**
- * A general-purpose implementation of {@link Comparator} to compare
- * {@link SqlMoniker} values.
- */
-public class SqlMonikerComparator implements Comparator<SqlMoniker> {
- //~ Methods ----------------------------------------------------------------
-
- public int compare(SqlMoniker m1, SqlMoniker m2) {
- if (m1.getType().ordinal() > m2.getType().ordinal()) {
- return 1;
- } else if (m1.getType().ordinal() < m2.getType().ordinal()) {
- return -1;
- } else {
- return m1.toString().compareTo(m2.toString());
- }
- }
-}
-
-// End SqlMonikerComparator.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerImpl.java
index c7eebd8..d14f76c 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerImpl.java
@@ -20,6 +20,7 @@ import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.util.Util;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -39,13 +40,8 @@ public class SqlMonikerImpl implements SqlMoniker {
* Creates a moniker with an array of names.
*/
public SqlMonikerImpl(List<String> names, SqlMonikerType type) {
- assert names != null;
- assert type != null;
- for (String name : names) {
- assert name != null;
- }
this.names = ImmutableList.copyOf(names);
- this.type = type;
+ this.type = Preconditions.checkNotNull(type);
}
/**
@@ -57,6 +53,17 @@ public class SqlMonikerImpl implements SqlMoniker {
//~ Methods ----------------------------------------------------------------
+ @Override public boolean equals(Object obj) {
+ return this == obj
+ || obj instanceof SqlMonikerImpl
+ && type == ((SqlMonikerImpl) obj).type
+ && names.equals(((SqlMonikerImpl) obj).names);
+ }
+
+ @Override public int hashCode() {
+ return Util.hash(type.ordinal(), names);
+ }
+
public SqlMonikerType getType() {
return type;
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerType.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerType.java
index 4b64859..7bf7a3e 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerType.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerType.java
@@ -22,7 +22,7 @@ package org.apache.calcite.sql.validate;
* <p>Used in {@link SqlMoniker}.
*/
public enum SqlMonikerType {
- COLUMN, TABLE, VIEW, SCHEMA, REPOSITORY, FUNCTION, KEYWORD
+ COLUMN, TABLE, VIEW, SCHEMA, CATALOG, REPOSITORY, FUNCTION, KEYWORD
}
// End SqlMonikerType.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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
new file mode 100644
index 0000000..4b99845
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlQualified.java
@@ -0,0 +1,86 @@
+/*
+ * 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.sql.SqlIdentifier;
+import org.apache.calcite.util.Util;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * Fully-qualified identifier.
+ *
+ * <p>The result of calling
+ * {@link org.apache.calcite.sql.validate.SqlValidatorScope#fullyQualify(org.apache.calcite.sql.SqlIdentifier)},
+ * a fully-qualified identifier contains the name (in correct case),
+ * parser position, type, and scope of each component of the identifier.
+ *
+ * <p>It is immutable.
+ */
+public class SqlQualified {
+ private final SqlValidatorScope scope;
+ public final int prefixLength;
+ public final SqlValidatorNamespace namespace;
+ public final SqlIdentifier identifier;
+
+ private SqlQualified(SqlValidatorScope scope, int prefixLength,
+ SqlValidatorNamespace namespace, SqlIdentifier identifier) {
+ this.scope = scope;
+ this.prefixLength = prefixLength;
+ this.namespace = namespace;
+ this.identifier = identifier;
+ }
+
+ public static SqlQualified create(SqlValidatorScope scope, int prefixLength,
+ SqlValidatorNamespace namespace, SqlIdentifier identifier) {
+ return new SqlQualified(scope, prefixLength, namespace, identifier);
+ }
+
+ public List<String> translatedNames() {
+ if (scope == null) {
+ return identifier.names;
+ }
+ final ImmutableList.Builder<String> builder = ImmutableList.builder();
+ SqlValidatorNamespace namespace =
+ scope.resolve(Util.skipLast(identifier.names), null, null);
+ builder.add(identifier.names.get(0));
+ for (String name : Util.skip(identifier.names)) {
+ if (namespace != null) {
+ name = namespace.translate(name);
+ namespace = null;
+ }
+ builder.add(name);
+ }
+ return builder.build();
+ }
+
+ public final List<String> prefix() {
+ return identifier.names.subList(0, prefixLength);
+ }
+
+ public final List<String> suffix() {
+ return Util.skip(identifier.names, prefixLength);
+ }
+
+ public final List<String> suffixTranslated() {
+ return Util.skip(translatedNames(), prefixLength);
+ }
+}
+
+// End SqlQualified.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 915d9ec..4a1c16b 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
@@ -86,6 +86,8 @@ public interface SqlValidatorCatalogReader {
*/
int fieldOrdinal(RelDataType rowType, String alias);
+ boolean matches(String string, String name);
+
int match(List<String> strings, String name);
RelDataType createTypeFromProjection(RelDataType type,
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6c88546e/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 cabc4d8..6e82f4c 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
@@ -74,7 +74,6 @@ import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.sql.util.SqlShuttle;
import org.apache.calcite.sql.util.SqlVisitor;
import org.apache.calcite.util.BitString;
-import org.apache.calcite.util.Bug;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.trace.CalciteTrace;
@@ -92,6 +91,7 @@ import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
+import java.util.Collection;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
@@ -398,66 +398,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
List<Map.Entry<String, RelDataType>> types,
final boolean includeSystemVars) {
final SelectScope scope = (SelectScope) getWhereScope(select);
- if (selectItem instanceof SqlIdentifier) {
- SqlIdentifier identifier = (SqlIdentifier) selectItem;
- if ((identifier.names.size() == 1)
- && identifier.names.get(0).equals("*")) {
- SqlParserPos starPosition = identifier.getParserPosition();
- for (Pair<String, SqlValidatorNamespace> p : scope.children) {
- final SqlNode from = p.right.getNode();
- final SqlValidatorNamespace fromNs = getNamespace(from, scope);
- assert fromNs != null;
- final RelDataType rowType = fromNs.getRowType();
- for (RelDataTypeField field : rowType.getFieldList()) {
- String columnName = field.getName();
-
- // TODO: do real implicit collation here
- final SqlNode exp =
- new SqlIdentifier(
- ImmutableList.of(p.left, columnName),
- starPosition);
- addToSelectList(
- selectItems,
- aliases,
- types,
- exp,
- scope,
- includeSystemVars);
- }
- }
- return true;
- } else if (identifier.names.size() == 2
- && identifier.names.get(1).equals("*")) {
- final String tableName = identifier.names.get(0);
- SqlParserPos starPosition = identifier.getParserPosition();
- 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();
- final SqlValidatorNamespace fromNs = getNamespace(from);
- assert fromNs != null;
- final RelDataType rowType = fromNs.getRowType();
- for (RelDataTypeField field : rowType.getFieldList()) {
- String columnName = field.getName();
-
- // TODO: do real implicit collation here
- final SqlIdentifier exp =
- new SqlIdentifier(
- ImmutableList.of(tableName, columnName),
- starPosition);
- addToSelectList(
- selectItems,
- aliases,
- types,
- exp,
- scope,
- includeSystemVars);
- }
- return true;
- }
+ if (expandStar(selectItems, aliases, types, includeSystemVars, scope,
+ selectItem)) {
+ return true;
}
// Expand the select item: fully-qualify columns, and convert
@@ -495,8 +438,84 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return false;
}
+ private boolean expandStar(List<SqlNode> selectItems, Set<String> aliases,
+ List<Map.Entry<String, RelDataType>> types, boolean includeSystemVars,
+ SelectScope scope, SqlNode node) {
+ if (!(node instanceof SqlIdentifier)) {
+ return false;
+ }
+ final SqlIdentifier identifier = (SqlIdentifier) node;
+ if (!identifier.isStar()) {
+ return false;
+ }
+ final SqlParserPos starPosition = identifier.getParserPosition();
+ switch (identifier.names.size()) {
+ case 1:
+ for (Pair<String, SqlValidatorNamespace> p : scope.children) {
+ final SqlNode from = p.right.getNode();
+ final SqlValidatorNamespace fromNs = getNamespace(from, scope);
+ assert fromNs != null;
+ final RelDataType rowType = fromNs.getRowType();
+ for (RelDataTypeField field : rowType.getFieldList()) {
+ String columnName = field.getName();
+
+ // TODO: do real implicit collation here
+ final SqlNode exp =
+ new SqlIdentifier(
+ ImmutableList.of(p.left, columnName),
+ starPosition);
+ addToSelectList(
+ selectItems,
+ aliases,
+ types,
+ exp,
+ scope,
+ 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 RelDataType rowType = fromNs.getRowType();
+ for (RelDataTypeField field : rowType.getFieldList()) {
+ String columnName = field.getName();
+
+ // TODO: do real implicit collation here
+ addToSelectList(
+ selectItems,
+ aliases,
+ types,
+ prefixId.plus(columnName, starPosition),
+ scope,
+ includeSystemVars);
+ }
+ return true;
+ }
+ }
+
public SqlNode validate(SqlNode topNode) {
SqlValidatorScope scope = new EmptyScope(this);
+ scope = new CatalogScope(scope, ImmutableList.of("CATALOG"));
final SqlNode topNode2 = validateScopedExpression(topNode, scope);
final RelDataType type = getValidatedNodeType(topNode2);
Util.discard(type);
@@ -520,16 +539,17 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
if (ns == null) {
throw Util.newInternal("Not a query: " + outermostNode);
}
- List<SqlMoniker> hintList = new ArrayList<SqlMoniker>();
+ Collection<SqlMoniker> hintList = Sets.newTreeSet(SqlMoniker.COMPARATOR);
lookupSelectHints(ns, pos, hintList);
- return hintList;
+ return ImmutableList.copyOf(hintList);
}
public SqlMoniker lookupQualifiedName(SqlNode topNode, SqlParserPos pos) {
final String posString = pos.toString();
IdInfo info = idPositions.get(posString);
if (info != null) {
- return new SqlIdentifierMoniker(info.scope.fullyQualify(info.id));
+ final SqlQualified qualified = info.scope.fullyQualify(info.id);
+ return new SqlIdentifierMoniker(qualified.identifier);
} else {
return null;
}
@@ -548,7 +568,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
void lookupSelectHints(
SqlSelect select,
SqlParserPos pos,
- List<SqlMoniker> hintList) {
+ Collection<SqlMoniker> hintList) {
IdInfo info = idPositions.get(pos.toString());
if ((info == null) || (info.scope == null)) {
SqlNode fromNode = select.getFrom();
@@ -563,7 +583,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private void lookupSelectHints(
SqlValidatorNamespace ns,
SqlParserPos pos,
- List<SqlMoniker> hintList) {
+ Collection<SqlMoniker> hintList) {
final SqlNode node = ns.getNode();
if (node instanceof SqlSelect) {
lookupSelectHints((SqlSelect) node, pos, hintList);
@@ -574,7 +594,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlNode node,
SqlValidatorScope scope,
SqlParserPos pos,
- List<SqlMoniker> hintList) {
+ Collection<SqlMoniker> hintList) {
final SqlValidatorNamespace ns = getNamespace(node);
if (ns.isWrapperFor(IdentifierNamespace.class)) {
IdentifierNamespace idNs = ns.unwrap(IdentifierNamespace.class);
@@ -610,7 +630,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlJoin join,
SqlValidatorScope scope,
SqlParserPos pos,
- List<SqlMoniker> hintList) {
+ Collection<SqlMoniker> hintList) {
SqlNode left = join.getLeft();
SqlNode right = join.getRight();
SqlNode condition = join.getCondition();
@@ -648,7 +668,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlValidatorScope scope,
List<String> names,
SqlParserPos pos,
- List<SqlMoniker> hintList) {
+ Collection<SqlMoniker> hintList) {
// Remove the last part of name - it is a dummy
List<String> subNames = Util.skipLast(names);
@@ -657,7 +677,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlValidatorNamespace ns = null;
for (String name : subNames) {
if (ns == null) {
- ns = scope.resolve(name, null, null);
+ ns = scope.resolve(ImmutableList.of(name), null, null);
} else {
ns = ns.lookupChild(name);
}
@@ -705,7 +725,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private static void findAllValidUdfNames(
List<String> names,
SqlValidator validator,
- List<SqlMoniker> result) {
+ Collection<SqlMoniker> result) {
List<SqlMoniker> objNames = new ArrayList<SqlMoniker>();
SqlValidatorUtil.getSchemaObjectMonikers(
validator.getCatalogReader(),
@@ -721,7 +741,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private static void findAllValidFunctionNames(
List<String> names,
SqlValidator validator,
- List<SqlMoniker> result,
+ Collection<SqlMoniker> result,
SqlParserPos pos) {
// a function name can only be 1 part
if (names.size() > 1) {
@@ -878,7 +898,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
((DelegatingScope) scope).getParent();
if (id.isSimple()) {
SqlValidatorNamespace ns =
- parentScope.resolve(id.getSimple(), null, null);
+ parentScope.resolve(id.names, null, null);
if (ns != null) {
return ns;
}
@@ -2523,11 +2543,11 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
public void validateIdentifier(SqlIdentifier id, SqlValidatorScope scope) {
- final SqlIdentifier fqId = scope.fullyQualify(id);
+ final SqlQualified fqId = scope.fullyQualify(id);
if (expandColumnReferences) {
// NOTE jvs 9-Apr-2007: this doesn't cover ORDER BY, which has its
// own ideas about qualification.
- id.assignNamesFrom(fqId);
+ id.assignNamesFrom(fqId.identifier);
} else {
Util.discard(fqId);
}
@@ -3157,10 +3177,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// Validate SELECT list. Expand terms of the form "*" or "TABLE.*".
final SqlValidatorScope selectScope = getSelectScope(select);
- final List<SqlNode> expandedSelectItems = new ArrayList<SqlNode>();
- final Set<String> aliases = new HashSet<String>();
- final List<Map.Entry<String, RelDataType>> fieldList =
- new ArrayList<Map.Entry<String, RelDataType>>();
+ final List<SqlNode> expandedSelectItems = Lists.newArrayList();
+ final Set<String> aliases = Sets.newHashSet();
+ final List<Map.Entry<String, RelDataType>> fieldList = Lists.newArrayList();
for (int i = 0; i < selectItems.size(); i++) {
SqlNode selectItem = selectItems.get(i);
@@ -3848,26 +3867,21 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final List<SqlNode> selectList = scope.getExpandedSelectList();
final SqlNode selectItem = stripAs(selectList.get(i));
if (selectItem instanceof SqlIdentifier) {
- SqlIdentifier id = (SqlIdentifier) selectItem;
- SqlValidatorNamespace namespace = null;
- List<String> origin = new ArrayList<String>();
- for (String name : id.names) {
+ final SqlQualified qualified =
+ scope.fullyQualify((SqlIdentifier) selectItem);
+ SqlValidatorNamespace namespace = qualified.namespace;
+ final SqlValidatorTable table = namespace.getTable();
+ if (table == null) {
+ return null;
+ }
+ final List<String> origin =
+ Lists.newArrayList(table.getQualifiedName());
+ for (String name : qualified.suffix()) {
+ namespace = namespace.lookupChild(name);
if (namespace == null) {
- namespace = scope.resolve(name, null, null);
- final SqlValidatorTable table = namespace.getTable();
- if (table != null) {
- origin.addAll(table.getQualifiedName());
- } else {
- return null;
- }
- } else {
- namespace = namespace.lookupChild(name);
- if (namespace != null) {
- origin.add(name);
- } else {
- return null;
- }
+ return null;
}
+ origin.add(name);
}
return origin;
}
@@ -4030,56 +4044,56 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
RelDataType type = null;
if (!(scope instanceof EmptyScope)) {
- id = scope.fullyQualify(id);
+ id = scope.fullyQualify(id).identifier;
}
- for (Ord<String> ord : Ord.zip(id.names)) {
- String name = ord.e;
- if (ord.i == 0) {
- // REVIEW jvs 9-June-2005: The name resolution rules used
- // here are supposed to match SQL:2003 Part 2 Section 6.6
- // (identifier chain), but we don't currently have enough
- // information to get everything right. In particular,
- // routine parameters are currently looked up via resolve;
- // we could do a better job if they were looked up via
- // resolveColumn.
-
- // TODO jvs 9-June-2005: Support schema-qualified table
- // names here (FRG-140). This was illegal in SQL-92, but
- // became legal in SQL:1999. (SQL:2003 Part 2 Section
- // 6.6 Syntax Rule 8.b.vi)
- Util.discard(Bug.FRG140_FIXED);
-
- SqlValidatorNamespace resolvedNs =
- scope.resolve(name, null, null);
-
- if (resolvedNs != null) {
- // There's a namespace with the name we seek.
- type = resolvedNs.getRowType();
- }
- // Give precedence to namespace found, unless there
- // are no more identifier components.
- if (type == null || id.names.size() == 1) {
- // See if there's a column with the name we seek in
- // precisely one of the namespaces in this scope.
- RelDataType colType = scope.resolveColumn(name, id);
- if (colType != null) {
- type = colType;
- }
- }
+ // 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
+ // here are supposed to match SQL:2003 Part 2 Section 6.6
+ // (identifier chain), but we don't currently have enough
+ // information to get everything right. In particular,
+ // routine parameters are currently looked up via resolve;
+ // 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) {
+ // There's a namespace with the name we seek.
+ type = resolvedNs.getRowType();
+ break;
+ }
+ }
- if (type == null) {
- throw newValidationError(id.getComponent(ord.i),
- RESOURCE.unknownIdentifier(name));
- }
- } else {
- final RelDataTypeField field = catalogReader.field(type, name);
- if (field == null) {
- throw newValidationError(id.getComponent(ord.i),
- RESOURCE.unknownField(name));
- }
- type = field.getType();
+ // Give precedence to namespace found, unless there
+ // are no more identifier components.
+ if (type == null || id.names.size() == 1) {
+ // See if there's a column with the name we seek in
+ // precisely one of the namespaces in this scope.
+ RelDataType colType = scope.resolveColumn(id.names.get(0), id);
+ if (colType != null) {
+ type = colType;
+ }
+ ++i;
+ }
+
+ if (type == null) {
+ final SqlIdentifier last = id.getComponent(i - 1, i);
+ throw newValidationError(last,
+ RESOURCE.unknownIdentifier(last.toString()));
+ }
+
+ // Resolve rest of identifier
+ for (; i < id.names.size(); i++) {
+ String name = id.names.get(i);
+ final RelDataTypeField field = catalogReader.field(type, name);
+ if (field == null) {
+ throw newValidationError(id.getComponent(i),
+ RESOURCE.unknownField(name));
}
+ type = field.getType();
}
type =
SqlTypeUtil.addCharsetAndCollation(
@@ -4129,7 +4143,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
if (call != null) {
return call.accept(this);
}
- final SqlIdentifier fqId = getScope().fullyQualify(id);
+ final SqlIdentifier fqId = getScope().fullyQualify(id).identifier;
validator.setOriginal(fqId, id);
return fqId;
}
@@ -4213,7 +4227,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlNode expr = expandedSelectList.get(ordinal);
expr = stripAs(expr);
if (expr instanceof SqlIdentifier) {
- expr = getScope().fullyQualify((SqlIdentifier) expr);
+ expr = getScope().fullyQualify((SqlIdentifier) expr).identifier;
}
// Create a copy of the expression with the position of the order
@@ -4239,7 +4253,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
// No match. Return identifier unchanged.
- return getScope().fullyQualify(id);
+ return getScope().fullyQualify(id).identifier;
}
protected SqlNode visitScoped(SqlCall call) {