diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SchemaUtilites.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SchemaUtilites.java
index 51c3cb10771..7d42e57e9ad 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SchemaUtilites.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SchemaUtilites.java
@@ -77,6 +77,29 @@ public static SchemaPlus findSchema(final SchemaPlus defaultSchema, final String
return findSchema(defaultSchema, schemaPathAsList);
}
+ /**
+ * Utility function to get the commonPrefix schema between two supplied schemas.
+ *
+ * Eg: if the defaultSchema: dfs and the schemaPath is dfs.tmp.`cicks.json`
+ * then this function returns dfs if (caseSensitive is not true
+ * otherwise it returns empty string.
+ *
+ * @param defaultSchema default schema
+ * @param schemaPath current schema path
+ * @param isCaseSensitive true if caseSensitive comparision is required.
+ * @return common prefix schemaPath
+ */
+ public static String getPrefixSchemaPath(final String defaultSchema,
+ final String schemaPath,
+ final boolean isCaseSensitive) {
+ if (!isCaseSensitive) {
+ return Strings.commonPrefix(defaultSchema.toLowerCase(), schemaPath.toLowerCase());
+ }
+ else {
+ return Strings.commonPrefix(defaultSchema, schemaPath);
+ }
+ }
+
/** Utility method to search for schema path starting from the given schema reference */
private static SchemaPlus searchSchemaTree(SchemaPlus schema, final List schemaPath) {
for (String schemaName : schemaPath) {
@@ -93,7 +116,7 @@ private static SchemaPlus searchSchemaTree(SchemaPlus schema, final List
* @return true if the given schema is root schema. False otherwise.
*/
public static boolean isRootSchema(SchemaPlus schema) {
- return schema.getParentSchema() == null;
+ return schema == null || schema.getParentSchema() == null;
}
/**
@@ -149,6 +172,16 @@ public static void throwSchemaNotFoundException(final SchemaPlus defaultSchema,
.build(logger);
}
+ /** Utility method to throw {@link UserException} with context information */
+ public static void throwSchemaNotFoundException(final SchemaPlus defaultSchema, final List givenSchemaPath) {
+ throw UserException.validationError()
+ .message("Schema [%s] is not valid with respect to either root schema or current default schema.",
+ givenSchemaPath)
+ .addContext("Current default schema: ",
+ isRootSchema(defaultSchema) ? "No default schema selected" : getSchemaPath(defaultSchema))
+ .build(logger);
+ }
+
/**
* Given reference to default schema in schema tree, search for schema with given schemaPath. Once a schema is
* found resolve it into a mutable AbstractDrillSchema instance. A {@link UserException} is throws when:
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SqlConverter.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SqlConverter.java
index 577804141b2..798e3a4479d 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SqlConverter.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SqlConverter.java
@@ -21,6 +21,7 @@
import java.util.List;
import java.util.Set;
+import com.google.common.base.Strings;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.jdbc.CalciteSchema;
import org.apache.calcite.jdbc.CalciteSchemaImpl;
@@ -53,6 +54,8 @@
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.sql2rel.RelDecorrelator;
import org.apache.calcite.sql2rel.SqlToRelConverter;
+import org.apache.calcite.util.Util;
+import org.apache.commons.collections.ListUtils;
import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.common.exceptions.UserException;
import org.apache.drill.common.types.Types;
@@ -114,7 +117,7 @@ public SqlConverter(QueryContext context) {
this.session = context.getSession();
this.drillConfig = context.getConfig();
this.catalog = new DrillCalciteCatalogReader(
- CalciteSchemaImpl.from(rootSchema),
+ this.rootSchema,
parserConfig.caseSensitive(),
CalciteSchemaImpl.from(defaultSchema).path(null),
typeFactory,
@@ -281,7 +284,7 @@ public Expander() {
@Override
public RelNode expandView(RelDataType rowType, String queryString, List schemaPath) {
final DrillCalciteCatalogReader catalogReader = new DrillCalciteCatalogReader(
- CalciteSchemaImpl.from(rootSchema),
+ rootSchema,
parserConfig.caseSensitive(),
schemaPath,
typeFactory,
@@ -294,7 +297,7 @@ public RelNode expandView(RelDataType rowType, String queryString, List
@Override
public RelNode expandView(RelDataType rowType, String queryString, SchemaPlus rootSchema, List schemaPath) {
final DrillCalciteCatalogReader catalogReader = new DrillCalciteCatalogReader(
- CalciteSchemaImpl.from(rootSchema), // new root schema
+ rootSchema, // new root schema
parserConfig.caseSensitive(),
schemaPath,
typeFactory,
@@ -431,17 +434,20 @@ private class DrillCalciteCatalogReader extends CalciteCatalogReader {
private final DrillConfig drillConfig;
private final UserSession session;
private boolean allowTemporaryTables;
+ private final SchemaPlus rootSchema;
- DrillCalciteCatalogReader(CalciteSchema rootSchema,
+
+ DrillCalciteCatalogReader(SchemaPlus rootSchema,
boolean caseSensitive,
List defaultSchema,
JavaTypeFactory typeFactory,
DrillConfig drillConfig,
UserSession session) {
- super(rootSchema, caseSensitive, defaultSchema, typeFactory);
+ super(CalciteSchemaImpl.from(rootSchema), caseSensitive, defaultSchema, typeFactory);
this.drillConfig = drillConfig;
this.session = session;
this.allowTemporaryTables = true;
+ this.rootSchema = rootSchema;
}
/**
@@ -481,7 +487,39 @@ public RelOptTableImpl getTable(final List names) {
.message("Temporary tables usage is disallowed. Used temporary table name: %s.", names)
.build(logger);
}
- return super.getTable(names);
+
+ RelOptTableImpl table = super.getTable(names);
+
+ // Check the schema and throw a valid SchemaNotFound exception instead of TableNotFound exception.
+ if (table == null) {
+ isValidSchema(names);
+ }
+
+ return table;
+ }
+
+ /**
+ * check if the schema provided is a valid schema:
+ * schema is not indicated (only one element in the names list)
+ *
+ * @param names list of schema and table names, table name is always the last element
+ * @return throws a userexception if the schema is not valid.
+ */
+ private void isValidSchema(final List names) throws UserException {
+ SchemaPlus defaultSchema = session.getDefaultSchema(this.rootSchema);
+ String defaultSchemaCombinedPath = SchemaUtilites.getSchemaPath(defaultSchema);
+ List schemaPath = Util.skipLast(names);
+ String schemaPathCombined = SchemaUtilites.getSchemaPath(schemaPath);
+ String commonPrefix = SchemaUtilites.getPrefixSchemaPath(defaultSchemaCombinedPath,
+ schemaPathCombined,
+ parserConfig.caseSensitive());
+ boolean isPrefixDefaultPath = commonPrefix.length() == defaultSchemaCombinedPath.length();
+ List fullSchemaPath = Strings.isNullOrEmpty(defaultSchemaCombinedPath) ? schemaPath :
+ isPrefixDefaultPath ? schemaPath : ListUtils.union(SchemaUtilites.getSchemaPathAsList(defaultSchema), schemaPath);
+ if (names.size() > 1 && (SchemaUtilites.findSchema(this.rootSchema, fullSchemaPath) == null &&
+ SchemaUtilites.findSchema(this.rootSchema, schemaPath) == null)) {
+ SchemaUtilites.throwSchemaNotFoundException(defaultSchema, schemaPath);
+ }
}
/**
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestFileSelection.java b/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestFileSelection.java
index 82f45ae62f3..d23cd1fba4a 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestFileSelection.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestFileSelection.java
@@ -26,9 +26,7 @@
import org.apache.drill.BaseTestQuery;
import org.apache.drill.common.util.TestTools;
import org.apache.hadoop.fs.FileStatus;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
public class TestFileSelection extends BaseTestQuery {
private static final List EMPTY_STATUSES = ImmutableList.of();
@@ -62,5 +60,4 @@ public void testEmptyFolderThrowsTableNotFound() throws Exception {
throw ex;
}
}
-
}
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestSchemaNotFoundException.java b/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestSchemaNotFoundException.java
new file mode 100644
index 00000000000..cca2bd07953
--- /dev/null
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestSchemaNotFoundException.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.drill.exec.store.dfs;
+
+import org.apache.drill.BaseTestQuery;
+import org.apache.drill.common.util.TestTools;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+public class TestSchemaNotFoundException extends BaseTestQuery {
+
+ @Test(expected = Exception.class)
+ public void testSchemaNotFoundForWrongStoragePlgn() throws Exception {
+ final String table = String.format("%s/empty", TestTools.getTestResourcesPath());
+ final String query = String.format("select * from dfs1.`%s`", table);
+ try {
+ testNoResult(query);
+ } catch (Exception ex) {
+ final String pattern = String.format("[[dfs1]] is not valid with respect to either root schema or current default schema").toLowerCase();
+ final boolean isSchemaNotFound = ex.getMessage().toLowerCase().contains(pattern);
+ assertTrue(isSchemaNotFound);
+ throw ex;
+ }
+ }
+
+ @Test(expected = Exception.class)
+ public void testSchemaNotFoundForWrongWorkspace() throws Exception {
+ final String table = String.format("%s/empty", TestTools.getTestResourcesPath());
+ final String query = String.format("select * from dfs.tmp1.`%s`", table);
+ try {
+ testNoResult(query);
+ } catch (Exception ex) {
+ final String pattern = String.format("[[dfs, tmp1]] is not valid with respect to either root schema or current default schema").toLowerCase();
+ final boolean isSchemaNotFound = ex.getMessage().toLowerCase().contains(pattern);
+ assertTrue(isSchemaNotFound);
+ throw ex;
+ }
+ }
+
+ @Test(expected = Exception.class)
+ public void testSchemaNotFoundForWrongWorkspaceUsingDefaultWorkspace() throws Exception {
+ final String table = String.format("%s/empty", TestTools.getTestResourcesPath());
+ final String query = String.format("select * from tmp1.`%s`", table);
+ try {
+ testNoResult("use dfs");
+ testNoResult(query);
+ } catch (Exception ex) {
+ final String pattern = String.format("[[tmp1]] is not valid with respect to either root schema or current default schema").toLowerCase();
+ final boolean isSchemaNotFound = ex.getMessage().toLowerCase().contains(pattern);
+ assertTrue(isSchemaNotFound);
+ throw ex;
+ }
+ }
+
+ @Test(expected = Exception.class)
+ public void testTableNotFoundException() throws Exception {
+ final String table = String.format("%s/empty1", TestTools.getTestResourcesPath());
+ final String query = String.format("select * from tmp.`%s`", table);
+ try {
+ testNoResult("use dfs");
+ testNoResult(query);
+ } catch (Exception ex) {
+ final String pattern = String.format("[[dfs, tmp1]] is not valid with respect to either root schema or current default schema").toLowerCase();
+ final boolean isSchemaNotFound = ex.getMessage().toLowerCase().contains(pattern);
+ final boolean isTableNotFound = ex.getMessage().toLowerCase().contains(String.format("%s' not found", table).toLowerCase());
+ assertTrue(!isSchemaNotFound && isTableNotFound);
+ throw ex;
+ }
+ }
+}