Skip to content

Commit

Permalink
DRILL-3151: Fix many ResultSetMetaData method return values.
Browse files Browse the repository at this point in the history
Added ~unit test for ResultSetMetaData implementation.

Made getObject return classes available to implementation of getColumnClassName:
- Added SqlAccessor.getObjectClass() (to put that metadata right next to code
  to which it corresponds rather than in far-away parallel code).
- Added similar AvaticaDrillSqlAccessor.getObjectClass().
- Changed DrillAccessorList.accessors from Accessor[] to
  AvaticaDrillSqlAccessor[] for better access to JDBC getObject return class.
- Extracted return classes from accessors to pass to updateColumnMetaData.

Reworked some data type mapping and utilities:
- Added Added Types.getSqlTypeName(...).
- Renamed Types.getJdbcType(...) to getJdbcTypeCode(...)
- Replaced Types.isUnSigned with isJdbcSignedType.
- Fixed various bogus RPC-type XXX -> java.sql.Types.SMALLINT mappings.
- Removed DrillColumnMetaDataList.getJdbcTypeName.
- Moved getAvaticaType up (for bottom-up order).
- Revised DrillColumnMetaDataList.getAvaticaType(...).

MAIN:
- Updated updateColumnMetaData(...) to change many calculations of metadata
  input to ColumnMetaData construction.  [DrillColumnMetaDataList]

Updated other metadata tests per changes.
  • Loading branch information
dsbos authored and mehant committed Jul 31, 2015
1 parent 9932246 commit 8083508
Show file tree
Hide file tree
Showing 16 changed files with 1,745 additions and 438 deletions.
163 changes: 150 additions & 13 deletions common/src/main/java/org/apache/drill/common/types/Types.java
Expand Up @@ -81,10 +81,86 @@ public static boolean isNumericType(final MajorType type) {
} }
} }


/***
* Gets SQL data type name for given Drill RPC-/protobuf-level data type.
* @return
* canonical keyword sequence for SQL data type (leading keywords in
* corresponding {@code <data type>}; what
* {@code INFORMATION_SCHEMA.COLUMNS.TYPE_NAME} would list)
*/
public static String getSqlTypeName(final MajorType type) {
if (type.getMode() == DataMode.REPEATED) {
return "ARRAY";
}

switch (type.getMinorType()) {

// Standard SQL atomic data types:

case BIT: return "BOOLEAN";

case SMALLINT: return "SMALLINT";
case INT: return "INTEGER";
case BIGINT: return "BIGINT";

case FLOAT4: return "FLOAT";
case FLOAT8: return "DOUBLE";

case DECIMAL9:
case DECIMAL18:
case DECIMAL28DENSE:
case DECIMAL28SPARSE:
case DECIMAL38DENSE:
case DECIMAL38SPARSE: return "DECIMAL";

case VARCHAR: return "CHARACTER VARYING";
case FIXEDCHAR: return "CHARACTER";

case VAR16CHAR: return "NATIONAL CHARACTER VARYING";
case FIXED16CHAR: return "NATIONAL CHARACTER";

case VARBINARY: return "BINARY VARYING";
case FIXEDBINARY: return "BINARY";

case DATE: return "DATE";
case TIME: return "TIME";
case TIMETZ: return "TIME WITH TIME ZONE";
case TIMESTAMP: return "TIMESTAMP";
case TIMESTAMPTZ: return "TIMESTAMP WITH TIME ZONE";

case INTERVALYEAR:
case INTERVALDAY: return "INTERVAL";

// Non-standard SQL atomic data types:

case INTERVAL: return "INTERVAL";
case MONEY: return "DECIMAL";
case TINYINT: return "TINYINT";

// Composite types and other types that are not atomic types (SQL standard
// or not) except ARRAY types (handled above):

case MAP: return "MAP";
case LATE: return "ANY";
case NULL: return "NULL";

// Internal types not actually used at level of SQL types(?):

case UINT1: return "TINYINT";
case UINT2: return "SMALLINT";
case UINT4: return "INTEGER";
case UINT8: return "BIGINT";

default:
throw new AssertionError(
"Unexpected/unhandled MinorType value " + type.getMinorType() );
}
}

/*** /***
* Gets JDBC type code for given Drill RPC-/protobuf-level type. * Gets JDBC type code for given Drill RPC-/protobuf-level type.
*/ */
public static int getJdbcType(final MajorType type) { public static int getJdbcTypeCode(final MajorType type) {
if (type.getMode() == DataMode.REPEATED) { if (type.getMode() == DataMode.REPEATED) {
return java.sql.Types.ARRAY; return java.sql.Types.ARRAY;
} }
Expand Down Expand Up @@ -120,10 +196,13 @@ public static int getJdbcType(final MajorType type) {
case MONEY: case MONEY:
return java.sql.Types.DECIMAL; return java.sql.Types.DECIMAL;
case NULL: case NULL:
return java.sql.Types.NULL;
case INTERVAL: case INTERVAL:
case INTERVALYEAR: case INTERVALYEAR:
case INTERVALDAY: case INTERVALDAY:
return java.sql.Types.OTHER; // JDBC (4.1) has nothing for INTERVAL
case LATE: case LATE:
return java.sql.Types.OTHER;
case SMALLINT: case SMALLINT:
return java.sql.Types.SMALLINT; return java.sql.Types.SMALLINT;
case TIME: case TIME:
Expand All @@ -132,7 +211,7 @@ public static int getJdbcType(final MajorType type) {
case TIMESTAMP: case TIMESTAMP:
return java.sql.Types.TIMESTAMP; return java.sql.Types.TIMESTAMP;
case TIMETZ: case TIMETZ:
return java.sql.Types.DATE; return java.sql.Types.TIME;
case TINYINT: case TINYINT:
return java.sql.Types.TINYINT; return java.sql.Types.TINYINT;
case UINT1: case UINT1:
Expand All @@ -154,22 +233,80 @@ public static int getJdbcType(final MajorType type) {
// is an unexpected, code-out-of-sync-with-itself case, so use an // is an unexpected, code-out-of-sync-with-itself case, so use an
// exception intended for that. // exception intended for that.
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Unexpected/unhandled " + type.getMinorType() + " value " + type.getMinorType() ); "Unexpected/unhandled MinorType value " + type.getMinorType() );
} }
} }


public static boolean isUnSigned(final MajorType type) { /**
switch(type.getMinorType()) { * Reports whether given RPC-level type is a signed type (per semantics of
case UINT1: * {@link ResultSetMetaData#isSigned(int)}).
case UINT2: */
case UINT4: public static boolean isJdbcSignedType( final MajorType type ) {
case UINT8: final boolean isSigned;
return true; switch ( type.getMode() ) {
default: case REPEATED:
return false; isSigned = false; // SQL ARRAY
break;
case REQUIRED:
case OPTIONAL:
switch ( type.getMinorType() ) {
// Verified signed types:
case SMALLINT:
case INT: // SQL INTEGER
case BIGINT:
case FLOAT4: // SQL REAL / FLOAT(N)
case FLOAT8: // SQL DOUBLE PRECISION / FLOAT(N)
case INTERVALYEAR: // SQL INTERVAL w/YEAR and/or MONTH
case INTERVALDAY: // SQL INTERVAL w/DAY, HOUR, MINUTE and/or SECOND
// Not-yet seen/verified signed types:
case DECIMAL9: // SQL DECIMAL (if used)
case DECIMAL18: // SQL DECIMAL (if used)
case DECIMAL28SPARSE: // SQL DECIMAL (if used)
case DECIMAL38SPARSE: // SQL DECIMAL (if used)
case DECIMAL28DENSE: // SQL DECIMAL (if used)
case DECIMAL38DENSE: // SQL DECIMAL (if used)
case TINYINT: // (not standard SQL)
case MONEY: // (not standard SQL)
case INTERVAL: // unknown (given INTERVALYEAR and INTERVALDAY)
isSigned = true;
break;
// Verified unsigned types:
case BIT: // SQL BOOLEAN
case VARCHAR:
case FIXEDCHAR: // SQL CHARACTER
case VARBINARY:
case FIXEDBINARY: // SQL BINARY
case DATE:
case TIME: // SQL TIME WITHOUT TIME ZONE
case TIMESTAMP: // SQL TIMESTAMP WITHOUT TIME ZONE
// Not-yet seen/verified unsigned types:
case UINT1:
case UINT2:
case UINT4:
case UINT8:
case FIXED16CHAR:
case VAR16CHAR:
case GENERIC_OBJECT:
case LATE:
case LIST:
case MAP:
case NULL:
case TIMETZ: // SQL TIME WITH TIME ZONE
case TIMESTAMPTZ: // SQL TIMESTAMP WITH TIME ZONE
isSigned = false;
break;
default:
throw new UnsupportedOperationException(
"Unexpected/unhandled MinorType value " + type.getMinorType() );
}
break;
default:
throw new UnsupportedOperationException(
"Unexpected/unhandled DataMode value " + type.getMode() );
} }

return isSigned;
} }

public static boolean usesHolderForGet(final MajorType type) { public static boolean usesHolderForGet(final MajorType type) {
if (type.getMode() == REPEATED) { if (type.getMode() == REPEATED) {
return true; return true;
Expand Down
29 changes: 29 additions & 0 deletions exec/java-exec/src/main/codegen/templates/SqlAccessors.java
Expand Up @@ -24,6 +24,10 @@
<#list ["", "Nullable"] as mode> <#list ["", "Nullable"] as mode>
<#assign name = mode + minor.class?cap_first /> <#assign name = mode + minor.class?cap_first />
<#assign javaType = (minor.javaType!type.javaType) /> <#assign javaType = (minor.javaType!type.javaType) />
<#assign friendlyType = (minor.friendlyType!minor.boxedType!type.boxedType) />
<#-- Class returned by ResultSet.getObject(...): -->
<#assign jdbcObjectClass = minor.jdbcObjectClass ! friendlyType />

<@pp.changeOutputFile name="/org/apache/drill/exec/vector/accessor/${name}Accessor.java" /> <@pp.changeOutputFile name="/org/apache/drill/exec/vector/accessor/${name}Accessor.java" />
<#include "/@includes/license.ftl" /> <#include "/@includes/license.ftl" />


Expand Down Expand Up @@ -60,6 +64,11 @@ public boolean isNull(int index) {
} }


<#if minor.class != "TimeStamp" && minor.class != "Time" && minor.class != "Date"> <#if minor.class != "TimeStamp" && minor.class != "Time" && minor.class != "Date">
@Override
public Class<?> getObjectClass() {
return ${jdbcObjectClass}.class;
}

public Object getObject(int index) { public Object getObject(int index) {
<#if mode == "Nullable"> <#if mode == "Nullable">
if (ac.isNull(index)) { if (ac.isNull(index)) {
Expand Down Expand Up @@ -161,6 +170,11 @@ public String getString(int index) {


<#if minor.class == "TimeStampTZ"> <#if minor.class == "TimeStampTZ">


@Override
public Class<?> getObjectClass() {
return Timestamp.class;
}

public Object getObject(int index) { public Object getObject(int index) {
return getTimestamp(index); return getTimestamp(index);
} }
Expand Down Expand Up @@ -200,6 +214,11 @@ public BigDecimal getBigDecimal(int index) {
} }
<#elseif minor.class == "Date"> <#elseif minor.class == "Date">


@Override
public Class<?> getObjectClass() {
return Date.class;
}

public Object getObject(int index) { public Object getObject(int index) {
<#if mode == "Nullable"> <#if mode == "Nullable">
if (ac.isNull(index)) { if (ac.isNull(index)) {
Expand All @@ -223,6 +242,11 @@ public Date getDate(int index) {


<#elseif minor.class == "TimeStamp"> <#elseif minor.class == "TimeStamp">


@Override
public Class<?> getObjectClass() {
return Timestamp.class;
}

public Object getObject(int index) { public Object getObject(int index) {
<#if mode == "Nullable"> <#if mode == "Nullable">
if (ac.isNull(index)) { if (ac.isNull(index)) {
Expand All @@ -246,6 +270,11 @@ public Timestamp getTimestamp(int index) {


<#elseif minor.class == "Time"> <#elseif minor.class == "Time">


@Override
public Class<?> getObjectClass() {
return Time.class;
}

public Object getObject(int index) { public Object getObject(int index) {
return getTime(index); return getTime(index);
} }
Expand Down
Expand Up @@ -44,6 +44,11 @@ public MajorType getType() {
return delegate.getType(); return delegate.getType();
} }


@Override
public Class<?> getObjectClass() {
return delegate.getObjectClass();
}

@Override @Override
public boolean isNull(int rowOffset) { public boolean isNull(int rowOffset) {
return delegate.isNull(rowOffset); return delegate.isNull(rowOffset);
Expand Down
Expand Up @@ -20,6 +20,7 @@
import org.apache.drill.common.types.TypeProtos; import org.apache.drill.common.types.TypeProtos;
import org.apache.drill.exec.vector.ValueVector; import org.apache.drill.exec.vector.ValueVector;



public class GenericAccessor extends AbstractSqlAccessor { public class GenericAccessor extends AbstractSqlAccessor {


private ValueVector v; private ValueVector v;
Expand All @@ -28,6 +29,11 @@ public GenericAccessor(ValueVector v) {
this.v = v; this.v = v;
} }


@Override
public Class<?> getObjectClass() {
return Object.class;
}

@Override @Override
public boolean isNull(int index) { public boolean isNull(int index) {
return v.getAccessor().isNull(index); return v.getAccessor().isNull(index);
Expand Down
Expand Up @@ -62,6 +62,14 @@ public interface SqlAccessor {
*/ */
MajorType getType(); MajorType getType();


/**
* Reports the class returned by getObject() of this accessor.
* <p>
* (Is for {@link ResultSetMetaData#getColumnClassName(...)}.)
* </p>
*/
Class<?> getObjectClass();

/** /**
* Reports whether the logical value is a SQL NULL. * Reports whether the logical value is a SQL NULL.
*/ */
Expand Down
Expand Up @@ -78,6 +78,13 @@ else if ( cursor.getResultSet().isBeforeFirst() ) {
} }
} }


/**
* @see SQLAccesstor#getObjectClass()
*/
public Class<?> getObjectClass() {
return underlyingAccessor.getObjectClass();
}

@Override @Override
public boolean wasNull() throws SQLException { public boolean wasNull() throws SQLException {
return underlyingAccessor.isNull(getCurrentRecordNumber()); return underlyingAccessor.isNull(getCurrentRecordNumber());
Expand Down
Expand Up @@ -31,14 +31,14 @@
class DrillAccessorList extends BasicList<Accessor>{ class DrillAccessorList extends BasicList<Accessor>{
static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillAccessorList.class); static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillAccessorList.class);


private Accessor[] accessors = new Accessor[0]; private AvaticaDrillSqlAccessor[] accessors = new AvaticaDrillSqlAccessor[0];
// TODO Rename to lastColumnAccessed and/or document. // TODO Rename to lastColumnAccessed and/or document.
// TODO Why 1, rather than, say, -1? // TODO Why 1, rather than, say, -1?
private int lastColumn = 1; private int lastColumn = 1;


void generateAccessors(DrillCursor cursor, RecordBatchLoader currentBatch){ void generateAccessors(DrillCursor cursor, RecordBatchLoader currentBatch) {
int cnt = currentBatch.getSchema().getFieldCount(); int cnt = currentBatch.getSchema().getFieldCount();
accessors = new Accessor[cnt]; accessors = new AvaticaDrillSqlAccessor[cnt];
for(int i =0; i < cnt; i++){ for(int i =0; i < cnt; i++){
final ValueVector vector = currentBatch.getValueAccessorById(null, i).getValueVector(); final ValueVector vector = currentBatch.getValueAccessorById(null, i).getValueVector();
final SqlAccessor acc = final SqlAccessor acc =
Expand All @@ -50,7 +50,7 @@ void generateAccessors(DrillCursor cursor, RecordBatchLoader currentBatch){
} }


@Override @Override
public Accessor get(int index) { public AvaticaDrillSqlAccessor get(int index) {
lastColumn = index; lastColumn = index;
return accessors[index]; return accessors[index];
} }
Expand Down

0 comments on commit 8083508

Please sign in to comment.