Skip to content

Commit

Permalink
DRILL-2461: Fix: INTERVAL in view makes INFORMATION_SCHEMA.COLUMN fail.
Browse files Browse the repository at this point in the history
- Created test.
- Handled INTERVAL data types in View:
  - Added <interval qualifier> data to FieldType.
  - Switch calling of createSqlType to createSqlIntervalType for interval types
  • Loading branch information
dsbos authored and parthchandra committed Mar 27, 2015
1 parent 9a5b50e commit 8796fd1
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 29 deletions.
160 changes: 131 additions & 29 deletions exec/java-exec/src/main/java/org/apache/drill/exec/dotdrill/View.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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:
Expand All @@ -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 (<i>iff</i> 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 (<i>iff</i> 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 (<i>iff</i> 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 (<i>iff</i> interval type).
* Gets the interval type descriptor's fractional second precision
* (<i>iff</i> 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<String> workspaceSchemaPath){

public View(String name, String sql, RelDataType rowType, List<String> 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 =
Expand All @@ -119,28 +218,31 @@ public View(@JsonProperty("name") String name,
workspaceSchemaPath == null ? ImmutableList.<String>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<RelDataType> types = Lists.newArrayList();
List<String> 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);
Expand All @@ -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;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 );
}

}

0 comments on commit 8796fd1

Please sign in to comment.