diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/dotdrill/View.java b/exec/java-exec/src/main/java/org/apache/drill/exec/dotdrill/View.java
index a7b496be148..c5c62d85704 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/dotdrill/View.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/dotdrill/View.java
@@ -22,9 +22,13 @@
import org.apache.drill.exec.planner.StarColumnHelper;
import org.apache.drill.exec.planner.types.RelDataTypeDrillImpl;
import org.apache.drill.exec.planner.types.RelDataTypeHolder;
+
import org.eigenbase.reltype.RelDataType;
import org.eigenbase.reltype.RelDataTypeFactory;
import org.eigenbase.reltype.RelDataTypeField;
+import org.eigenbase.sql.SqlIntervalQualifier;
+import org.eigenbase.sql.parser.SqlParserPos;
+import org.eigenbase.sql.type.SqlTypeFamily;
import org.eigenbase.sql.type.SqlTypeName;
import com.fasterxml.jackson.annotation.JsonCreator;
@@ -48,36 +52,50 @@ public class View {
@JsonInclude(Include.NON_NULL)
public static class FieldType {
- public final String name;
- public final SqlTypeName type;
- public final Integer precision;
- public final Integer scale;
- public final Boolean isNullable;
+
+ private final String name;
+ private final SqlTypeName type;
+ private final Integer precision;
+ private final Integer scale;
+ private SqlIntervalQualifier intervalQualifier;
+ private final Boolean isNullable;
+
@JsonCreator
public FieldType(
- @JsonProperty("name") String name,
- @JsonProperty("type") SqlTypeName type,
- @JsonProperty("precision") Integer precision,
- @JsonProperty("scale") Integer scale,
- @JsonProperty("isNullable") Boolean isNullable){
+ @JsonProperty("name") String name,
+ @JsonProperty("type") SqlTypeName type,
+ @JsonProperty("precision") Integer precision,
+ @JsonProperty("scale") Integer scale,
+ @JsonProperty("startUnit") SqlIntervalQualifier.TimeUnit startUnit,
+ @JsonProperty("endUnit") SqlIntervalQualifier.TimeUnit endUnit,
+ @JsonProperty("fractionalSecondPrecision") Integer fractionalSecondPrecision,
+ @JsonProperty("isNullable") Boolean isNullable) {
this.name = name;
this.type = type;
this.precision = precision;
this.scale = scale;
+ this.intervalQualifier =
+ null == startUnit
+ ? null
+ : new SqlIntervalQualifier(
+ startUnit, precision, endUnit, fractionalSecondPrecision, SqlParserPos.ZERO );
- // Property "isNullable" is not part of the initial view definition and added in DRILL-2342. If the
- // default value is null, consider it as "true". It is safe to default to "nullable" than "required" type.
- this.isNullable = (isNullable == null) ? true : isNullable;
+ // Property "isNullable" is not part of the initial view definition and
+ // was added in DRILL-2342. If the default value is null, consider it as
+ // "true". It is safe to default to "nullable" than "required" type.
+ this.isNullable = isNullable == null ? true : isNullable;
}
- public FieldType(String name, RelDataType dataType){
+ public FieldType(String name, RelDataType dataType) {
this.name = name;
this.type = dataType.getSqlTypeName();
+
Integer p = null;
Integer s = null;
+ Integer fractionalSecondPrecision = null;
- switch(dataType.getSqlTypeName()){
+ switch (dataType.getSqlTypeName()) {
case CHAR:
case BINARY:
case VARBINARY:
@@ -88,19 +106,100 @@ public FieldType(String name, RelDataType dataType){
p = dataType.getPrecision();
s = dataType.getScale();
break;
+ case INTERVAL_YEAR_MONTH:
+ case INTERVAL_DAY_TIME:
+ p = dataType.getIntervalQualifier().getStartPrecision();
+ default:
+ break;
}
this.precision = p;
this.scale = s;
+ this.intervalQualifier = dataType.getIntervalQualifier();
this.isNullable = dataType.isNullable();
}
+
+ /**
+ * Gets the name of this field.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Gets the data type of this field.
+ * (Data type only; not full datatype descriptor.)
+ */
+ public SqlTypeName getType() {
+ return type;
+ }
+
+ /**
+ * Gets the precision of the data type descriptor of this field.
+ * The precision is the precision for a numeric type, the length for a
+ * string type, or the start unit precision for an interval type.
+ * */
+ public Integer getPrecision() {
+ return precision;
+ }
+
+ /**
+ * Gets the numeric scale of the data type descriptor of this field,
+ * for numeric types.
+ */
+ public Integer getScale() {
+ return scale;
+ }
+
+ /**
+ * Gets the interval type qualifier of the interval data type descriptor of
+ * this field (iff interval type). */
+ @JsonIgnore
+ public SqlIntervalQualifier getIntervalQualifier() {
+ return intervalQualifier;
+ }
+
+ /**
+ * Gets the time range start unit of the type qualifier of the interval data
+ * type descriptor of this field (iff interval type).
+ */
+ public SqlIntervalQualifier.TimeUnit getStartUnit() {
+ return null == intervalQualifier ? null : intervalQualifier.getStartUnit();
+ }
+
+ /**
+ * Gets the time range end unit of the type qualifier of the interval data
+ * type descriptor of this field (iff interval type).
+ */
+ public SqlIntervalQualifier.TimeUnit getEndUnit() {
+ return null == intervalQualifier ? null : intervalQualifier.getEndUnit();
+ }
+
+ /**
+ * Gets the fractional second precision of the type qualifier of the interval
+ * data type descriptor of this field (iff interval type).
+ * Gets the interval type descriptor's fractional second precision
+ * (iff interval type).
+ */
+ public Integer getFractionalSecondPrecision() {
+ return null == intervalQualifier ? null : intervalQualifier.getFractionalSecondPrecision();
+ }
+
+ /**
+ * Gets the nullability of the data type desription of this field.
+ */
+ public Boolean getIsNullable() {
+ return isNullable;
+ }
+
}
- public View(String name, String sql, RelDataType rowType, List workspaceSchemaPath){
+
+ public View(String name, String sql, RelDataType rowType, List workspaceSchemaPath) {
this.name = name;
this.sql = sql;
fields = Lists.newArrayList();
- for(RelDataTypeField f : rowType.getFieldList()){
+ for (RelDataTypeField f : rowType.getFieldList()) {
fields.add(new FieldType(f.getName(), f.getType()));
}
this.workspaceSchemaPath =
@@ -119,28 +218,31 @@ public View(@JsonProperty("name") String name,
workspaceSchemaPath == null ? ImmutableList.of() : ImmutableList.copyOf(workspaceSchemaPath);
}
- public RelDataType getRowType(RelDataTypeFactory factory){
+ public RelDataType getRowType(RelDataTypeFactory factory) {
// if there are no fields defined, this is a dynamic view.
- if(isDynamic()){
+ if (isDynamic()) {
return new RelDataTypeDrillImpl(new RelDataTypeHolder(), factory);
}
List types = Lists.newArrayList();
List names = Lists.newArrayList();
- for(FieldType field : fields){
- names.add(field.name);
+ for (FieldType field : fields) {
+ names.add(field.getName());
RelDataType type;
- if(field.precision == null && field.scale == null){
- type = factory.createSqlType(field.type);
- }else if(field.precision != null && field.scale == null){
- type = factory.createSqlType(field.type, field.precision);
- }else{
- type = factory.createSqlType(field.type, field.precision, field.scale);
+ if ( SqlTypeFamily.INTERVAL_YEAR_MONTH == field.getType().getFamily()
+ || SqlTypeFamily.INTERVAL_DAY_TIME == field.getType().getFamily() ) {
+ type = factory.createSqlIntervalType( field.getIntervalQualifier() );
+ } else if (field.getPrecision() == null && field.getScale() == null) {
+ type = factory.createSqlType(field.getType());
+ } else if (field.getPrecision() != null && field.getScale() == null) {
+ type = factory.createSqlType(field.getType(), field.getPrecision());
+ } else {
+ type = factory.createSqlType(field.getType(), field.getPrecision(), field.getScale());
}
- if (field.isNullable) {
+ if (field.getIsNullable()) {
types.add(factory.createTypeWithNullability(type, true));
} else {
types.add(type);
@@ -157,7 +259,7 @@ public boolean isDynamic(){
@JsonIgnore
public boolean hasStar() {
for (FieldType field : fields) {
- if (StarColumnHelper.isNonPrefixedStarColumn(field.name)) {
+ if (StarColumnHelper.isNonPrefixedStarColumn(field.getName())) {
return true;
}
}
diff --git a/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2461IntervalsBreakInfoSchemaBugTest.java b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2461IntervalsBreakInfoSchemaBugTest.java
new file mode 100644
index 00000000000..fce7923be96
--- /dev/null
+++ b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2461IntervalsBreakInfoSchemaBugTest.java
@@ -0,0 +1,91 @@
+/**
+ * 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.jdbc.test;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.*;
+
+import org.apache.drill.common.util.TestTools;
+import org.apache.drill.jdbc.Driver;
+import org.apache.drill.jdbc.JdbcTest;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.sql.SQLException;
+
+
+public class Drill2461IntervalsBreakInfoSchemaBugTest extends JdbcTest {
+
+ private static final String VIEW_NAME =
+ Drill2461IntervalsBreakInfoSchemaBugTest.class.getSimpleName() + "_View";
+
+ private static Connection connection;
+
+
+ @BeforeClass
+ public static void setUpConnection() throws Exception {
+ connection = connect( "jdbc:drill:zk=local" );
+ }
+
+ @AfterClass
+ public static void tearDownConnection() throws Exception {
+ connection.close();
+ }
+
+
+ @Test
+ public void testIntervalInViewDoesntCrashInfoSchema() throws Exception {
+ final Statement stmt = connection.createStatement();
+ ResultSet util;
+
+ // Create a view using an INTERVAL type:
+ util = stmt.executeQuery( "USE dfs.tmp" );
+ assert util.next();
+ assert util.getBoolean( 1 )
+ : "Error setting schema to dfs.tmp: " + util.getString( 2 );
+ util = stmt.executeQuery(
+ "CREATE OR REPLACE VIEW " + VIEW_NAME + " AS "
+ + "\n SELECT CAST( NULL AS INTERVAL HOUR(4) TO MINUTE ) AS optINTERVAL_HM "
+ + "\n FROM INFORMATION_SCHEMA.CATALOGS "
+ + "\n LIMIT 1 " );
+ assert util.next();
+ assert util.getBoolean( 1 )
+ : "Error creating temporary test-columns view " + VIEW_NAME + ": "
+ + util.getString( 2 );
+
+ // Test whether query INFORMATION_SCHEMA.COLUMNS works (doesn't crash):
+ util = stmt.executeQuery( "SELECT * FROM INFORMATION_SCHEMA.COLUMNS" );
+ assert util.next();
+
+ // Clean up the test view:
+ util = connection.createStatement().executeQuery( "DROP VIEW " + VIEW_NAME );
+ assert util.next();
+ assert util.getBoolean( 1 )
+ : "Error dropping temporary test-columns view " + VIEW_NAME + ": "
+ + util.getString( 2 );
+ }
+
+}