Return-Path: X-Original-To: apmail-drill-commits-archive@www.apache.org Delivered-To: apmail-drill-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 464F518569 for ; Fri, 31 Jul 2015 03:16:44 +0000 (UTC) Received: (qmail 16884 invoked by uid 500); 31 Jul 2015 03:16:44 -0000 Delivered-To: apmail-drill-commits-archive@drill.apache.org Received: (qmail 16838 invoked by uid 500); 31 Jul 2015 03:16:44 -0000 Mailing-List: contact commits-help@drill.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: commits@drill.apache.org Delivered-To: mailing list commits@drill.apache.org Received: (qmail 16817 invoked by uid 99); 31 Jul 2015 03:16:44 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 31 Jul 2015 03:16:44 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id DE307DFC3B; Fri, 31 Jul 2015 03:16:43 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: mehant@apache.org To: commits@drill.apache.org Date: Fri, 31 Jul 2015 03:16:45 -0000 Message-Id: In-Reply-To: <4848bd45d7ba4583bf34357e1322bfa5@git.apache.org> References: <4848bd45d7ba4583bf34357e1322bfa5@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [3/3] drill git commit: DRILL-3151: Fix many ResultSetMetaData method return values. DRILL-3151: Fix many ResultSetMetaData method return values. 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. Project: http://git-wip-us.apache.org/repos/asf/drill/repo Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/80835082 Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/80835082 Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/80835082 Branch: refs/heads/master Commit: 80835082414d274fda8bf0c66fd01386180fdde5 Parents: 9932246 Author: dbarclay Authored: Fri Jun 19 19:05:39 2015 -0700 Committer: Mehant Baid Committed: Thu Jul 30 18:46:02 2015 -0700 ---------------------------------------------------------------------- .../org/apache/drill/common/types/Types.java | 163 ++- .../main/codegen/templates/SqlAccessors.java | 29 + .../vector/accessor/BoundCheckingAccessor.java | 5 + .../exec/vector/accessor/GenericAccessor.java | 6 + .../drill/exec/vector/accessor/SqlAccessor.java | 8 + .../jdbc/impl/AvaticaDrillSqlAccessor.java | 7 + .../drill/jdbc/impl/DrillAccessorList.java | 8 +- .../jdbc/impl/DrillColumnMetaDataList.java | 185 ++- .../org/apache/drill/jdbc/impl/DrillCursor.java | 38 +- .../jdbc/impl/TypeConvertingSqlAccessor.java | 5 + .../jdbc/DatabaseMetaDataGetColumnsTest.java | 323 +++-- .../drill/jdbc/DrillColumnMetaDataListTest.java | 20 +- .../jdbc/ResultSetGetMethodConversionsTest.java | 71 +- .../drill/jdbc/ResultSetMetaDataTest.java | 1107 ++++++++++++++++++ .../impl/TypeConvertingSqlAccessorTest.java | 5 + .../jdbc/test/TestInformationSchemaColumns.java | 203 ++-- 16 files changed, 1745 insertions(+), 438 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/drill/blob/80835082/common/src/main/java/org/apache/drill/common/types/Types.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/drill/common/types/Types.java b/common/src/main/java/org/apache/drill/common/types/Types.java index df484b7..69b1b4c 100644 --- a/common/src/main/java/org/apache/drill/common/types/Types.java +++ b/common/src/main/java/org/apache/drill/common/types/Types.java @@ -82,9 +82,85 @@ public class Types { } /*** + * 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 }; 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. */ - public static int getJdbcType(final MajorType type) { + public static int getJdbcTypeCode(final MajorType type) { if (type.getMode() == DataMode.REPEATED) { return java.sql.Types.ARRAY; } @@ -120,10 +196,13 @@ public class Types { case MONEY: return java.sql.Types.DECIMAL; case NULL: + return java.sql.Types.NULL; case INTERVAL: case INTERVALYEAR: case INTERVALDAY: + return java.sql.Types.OTHER; // JDBC (4.1) has nothing for INTERVAL case LATE: + return java.sql.Types.OTHER; case SMALLINT: return java.sql.Types.SMALLINT; case TIME: @@ -132,7 +211,7 @@ public class Types { case TIMESTAMP: return java.sql.Types.TIMESTAMP; case TIMETZ: - return java.sql.Types.DATE; + return java.sql.Types.TIME; case TINYINT: return java.sql.Types.TINYINT; case UINT1: @@ -154,22 +233,80 @@ public class Types { // is an unexpected, code-out-of-sync-with-itself case, so use an // exception intended for that. 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()) { - case UINT1: - case UINT2: - case UINT4: - case UINT8: - return true; - default: - return false; + /** + * Reports whether given RPC-level type is a signed type (per semantics of + * {@link ResultSetMetaData#isSigned(int)}). + */ + public static boolean isJdbcSignedType( final MajorType type ) { + final boolean isSigned; + switch ( type.getMode() ) { + case REPEATED: + 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) { if (type.getMode() == REPEATED) { return true; http://git-wip-us.apache.org/repos/asf/drill/blob/80835082/exec/java-exec/src/main/codegen/templates/SqlAccessors.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/codegen/templates/SqlAccessors.java b/exec/java-exec/src/main/codegen/templates/SqlAccessors.java index c50a3e2..3257499 100644 --- a/exec/java-exec/src/main/codegen/templates/SqlAccessors.java +++ b/exec/java-exec/src/main/codegen/templates/SqlAccessors.java @@ -24,6 +24,10 @@ import java.lang.Override; <#list ["", "Nullable"] as mode> <#assign name = mode + minor.class?cap_first /> <#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" /> <#include "/@includes/license.ftl" /> @@ -60,6 +64,11 @@ public class ${name}Accessor extends AbstractSqlAccessor { } <#if minor.class != "TimeStamp" && minor.class != "Time" && minor.class != "Date"> + @Override + public Class getObjectClass() { + return ${jdbcObjectClass}.class; + } + public Object getObject(int index) { <#if mode == "Nullable"> if (ac.isNull(index)) { @@ -161,6 +170,11 @@ public class ${name}Accessor extends AbstractSqlAccessor { <#if minor.class == "TimeStampTZ"> + @Override + public Class getObjectClass() { + return Timestamp.class; + } + public Object getObject(int index) { return getTimestamp(index); } @@ -200,6 +214,11 @@ public class ${name}Accessor extends AbstractSqlAccessor { } <#elseif minor.class == "Date"> + @Override + public Class getObjectClass() { + return Date.class; + } + public Object getObject(int index) { <#if mode == "Nullable"> if (ac.isNull(index)) { @@ -223,6 +242,11 @@ public class ${name}Accessor extends AbstractSqlAccessor { <#elseif minor.class == "TimeStamp"> + @Override + public Class getObjectClass() { + return Timestamp.class; + } + public Object getObject(int index) { <#if mode == "Nullable"> if (ac.isNull(index)) { @@ -246,6 +270,11 @@ public class ${name}Accessor extends AbstractSqlAccessor { <#elseif minor.class == "Time"> + @Override + public Class getObjectClass() { + return Time.class; + } + public Object getObject(int index) { return getTime(index); } http://git-wip-us.apache.org/repos/asf/drill/blob/80835082/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/BoundCheckingAccessor.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/BoundCheckingAccessor.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/BoundCheckingAccessor.java index 3d3683e..bfef947 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/BoundCheckingAccessor.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/BoundCheckingAccessor.java @@ -45,6 +45,11 @@ public class BoundCheckingAccessor implements SqlAccessor { } @Override + public Class getObjectClass() { + return delegate.getObjectClass(); + } + + @Override public boolean isNull(int rowOffset) { return delegate.isNull(rowOffset); } http://git-wip-us.apache.org/repos/asf/drill/blob/80835082/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/GenericAccessor.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/GenericAccessor.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/GenericAccessor.java index 65c34ad..d8f2995 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/GenericAccessor.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/GenericAccessor.java @@ -20,6 +20,7 @@ package org.apache.drill.exec.vector.accessor; import org.apache.drill.common.types.TypeProtos; import org.apache.drill.exec.vector.ValueVector; + public class GenericAccessor extends AbstractSqlAccessor { private ValueVector v; @@ -29,6 +30,11 @@ public class GenericAccessor extends AbstractSqlAccessor { } @Override + public Class getObjectClass() { + return Object.class; + } + + @Override public boolean isNull(int index) { return v.getAccessor().isNull(index); } http://git-wip-us.apache.org/repos/asf/drill/blob/80835082/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/SqlAccessor.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/SqlAccessor.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/SqlAccessor.java index 19e6fcf..636456d 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/SqlAccessor.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/SqlAccessor.java @@ -63,6 +63,14 @@ public interface SqlAccessor { MajorType getType(); /** + * Reports the class returned by getObject() of this accessor. + *

+ * (Is for {@link ResultSetMetaData#getColumnClassName(...)}.) + *

+ */ + Class getObjectClass(); + + /** * Reports whether the logical value is a SQL NULL. */ boolean isNull(int rowOffset); http://git-wip-us.apache.org/repos/asf/drill/blob/80835082/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/AvaticaDrillSqlAccessor.java ---------------------------------------------------------------------- diff --git a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/AvaticaDrillSqlAccessor.java b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/AvaticaDrillSqlAccessor.java index 64f5b87..7e53a07 100644 --- a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/AvaticaDrillSqlAccessor.java +++ b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/AvaticaDrillSqlAccessor.java @@ -78,6 +78,13 @@ class AvaticaDrillSqlAccessor implements Accessor { } } + /** + * @see SQLAccesstor#getObjectClass() + */ + public Class getObjectClass() { + return underlyingAccessor.getObjectClass(); + } + @Override public boolean wasNull() throws SQLException { return underlyingAccessor.isNull(getCurrentRecordNumber()); http://git-wip-us.apache.org/repos/asf/drill/blob/80835082/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillAccessorList.java ---------------------------------------------------------------------- diff --git a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillAccessorList.java b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillAccessorList.java index 25ca1ba..9284875 100644 --- a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillAccessorList.java +++ b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillAccessorList.java @@ -31,14 +31,14 @@ import org.apache.drill.exec.vector.accessor.SqlAccessor; class DrillAccessorList extends BasicList{ 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 Why 1, rather than, say, -1? private int lastColumn = 1; - void generateAccessors(DrillCursor cursor, RecordBatchLoader currentBatch){ + void generateAccessors(DrillCursor cursor, RecordBatchLoader currentBatch) { int cnt = currentBatch.getSchema().getFieldCount(); - accessors = new Accessor[cnt]; + accessors = new AvaticaDrillSqlAccessor[cnt]; for(int i =0; i < cnt; i++){ final ValueVector vector = currentBatch.getValueAccessorById(null, i).getValueVector(); final SqlAccessor acc = @@ -50,7 +50,7 @@ class DrillAccessorList extends BasicList{ } @Override - public Accessor get(int index) { + public AvaticaDrillSqlAccessor get(int index) { lastColumn = index; return accessors[index]; } http://git-wip-us.apache.org/repos/asf/drill/blob/80835082/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillColumnMetaDataList.java ---------------------------------------------------------------------- diff --git a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillColumnMetaDataList.java b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillColumnMetaDataList.java index d43755e..1813cc7 100644 --- a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillColumnMetaDataList.java +++ b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillColumnMetaDataList.java @@ -41,7 +41,7 @@ public class DrillColumnMetaDataList extends BasicList{ @Override public int size() { - return (columns.size()); + return columns.size(); } @Override @@ -49,121 +49,80 @@ public class DrillColumnMetaDataList extends BasicList{ return columns.get(index); } - public void updateColumnMetaData(String catalogName, String schemaName, String tableName, BatchSchema schema){ - - columns = new ArrayList(schema.getFieldCount()); - for(int i = 0; i < schema.getFieldCount(); i++){ - MaterializedField f = schema.getColumn(i); - MajorType t = f.getType(); - ColumnMetaData col = new ColumnMetaData( // - i, // ordinal - false, // autoIncrement - true, // caseSensitive - false, // searchable - false, // currency - f.getDataMode() == DataMode.OPTIONAL ? ResultSetMetaData.columnNullable : ResultSetMetaData.columnNoNulls, //nullability - !Types.isUnSigned(t), // signed - 10, // display size. - f.getAsSchemaPath().getRootSegment().getPath(), // label - f.getAsSchemaPath().getRootSegment().getPath(), // columnname - schemaName, // schemaname - t.hasPrecision() ? t.getPrecision() : 0, // precision - t.hasScale() ? t.getScale() : 0, // scale - null, // tablename is null so sqlline doesn't try to retrieve primary keys. - catalogName, // catalogname - getAvaticaType(t), // sql type - true, // readonly - false, // writable - false, // definitely writable - "none" // column class name - ); - columns.add(col); - } + /** + * Gets AvaticaType carrying both JDBC {@code java.sql.Type.*} type code + * and SQL type name for given RPC-level type (from batch schema). + */ + private static AvaticaType getAvaticaType( MajorType rpcDateType ) { + final String sqlTypeName = Types.getSqlTypeName( rpcDateType ); + final int jdbcTypeId = Types.getJdbcTypeCode( rpcDateType ); + return ColumnMetaData.scalar( jdbcTypeId, sqlTypeName, + Rep.BOOLEAN /* dummy value, unused */ ); } - private static AvaticaType getAvaticaType(MajorType t){ - final int jdbcTypeId = Types.getJdbcType(t); - return ColumnMetaData.scalar(jdbcTypeId, getJdbcTypeName(jdbcTypeId), Rep.BOOLEAN /* dummy value, unused */); - } - - private static String getJdbcTypeName(int type) { - switch (type) { - case java.sql.Types.BIT: - return "BIT"; - case java.sql.Types.TINYINT: - return "TINYINT"; - case java.sql.Types.SMALLINT: - return "SMALLINT"; - case java.sql.Types.INTEGER: - return "INTEGER"; - case java.sql.Types.BIGINT: - return "BIGINT"; - case java.sql.Types.FLOAT: - return "FLOAT"; - case java.sql.Types.REAL: - return "REAL"; - case java.sql.Types.DOUBLE: - return "DOUBLE"; - case java.sql.Types.NUMERIC: - return "NUMERIC"; - case java.sql.Types.DECIMAL: - return "DECIMAL"; - case java.sql.Types.CHAR: - return "CHAR"; - case java.sql.Types.VARCHAR: - return "VARCHAR"; - case java.sql.Types.LONGVARCHAR: - return "LONGVARCHAR"; - case java.sql.Types.DATE: - return "DATE"; - case java.sql.Types.TIME: - return "TIME"; - case java.sql.Types.TIMESTAMP: - return "TIMESTAMP"; - case java.sql.Types.BINARY: - return "BINARY"; - case java.sql.Types.VARBINARY: - return "VARBINARY"; - case java.sql.Types.LONGVARBINARY: - return "LONGVARBINARY"; - case java.sql.Types.NULL: - return "NULL"; - case java.sql.Types.OTHER: - return "OTHER"; - case java.sql.Types.JAVA_OBJECT: - return "JAVA_OBJECT"; - case java.sql.Types.DISTINCT: - return "DISTINCT"; - case java.sql.Types.STRUCT: - return "STRUCT"; - case java.sql.Types.ARRAY: - return "ARRAY"; - case java.sql.Types.BLOB: - return "BLOB"; - case java.sql.Types.CLOB: - return "CLOB"; - case java.sql.Types.REF: - return "REF"; - case java.sql.Types.DATALINK: - return "DATALINK"; - case java.sql.Types.BOOLEAN: - return "BOOLEAN"; - case java.sql.Types.ROWID: - return "ROWID"; - case java.sql.Types.NCHAR: - return "NCHAR"; - case java.sql.Types.NVARCHAR: - return "NVARCHAR"; - case java.sql.Types.LONGNVARCHAR: - return "LONGNVARCHAR"; - case java.sql.Types.NCLOB: - return "NCLOB"; - case java.sql.Types.SQLXML: - return "SQLXML"; - default: - logger.error( "Unexpected java.sql.Types value {}", type ); - return "unknown java.sql.Types value " + type; + public void updateColumnMetaData(String catalogName, String schemaName, + String tableName, BatchSchema schema, + List> getObjectClasses ) { + final List newColumns = + new ArrayList(schema.getFieldCount()); + for (int colOffset = 0; colOffset < schema.getFieldCount(); colOffset++) { + final MaterializedField field = schema.getColumn(colOffset); + Class objectClass = getObjectClasses.get( colOffset ); + + final String columnName = field.getPath().getRootSegment().getPath(); + + final MajorType rpcDataType = field.getType(); + final AvaticaType bundledSqlDataType = getAvaticaType(rpcDataType); + final String columnClassName = objectClass.getName(); + + final int nullability; + switch ( field.getDataMode() ) { + case OPTIONAL: nullability = ResultSetMetaData.columnNullable; break; + case REQUIRED: nullability = ResultSetMetaData.columnNoNulls; break; + // Should REPEATED still map to columnNoNulls? or to columnNullable? + case REPEATED: nullability = ResultSetMetaData.columnNoNulls; break; + default: + throw new AssertionError( "Unexpected new DataMode value '" + + field.getDataMode().name() + "'" ); + } + final boolean isSigned = Types.isJdbcSignedType( rpcDataType ); + + // TODO(DRILL-3355): TODO(DRILL-3356): When string lengths, precisions, + // interval kinds, etc., are available from RPC-level data, implement: + // - precision for ResultSetMetadata.getPrecision(...) (like + // getColumns()'s COLUMN_SIZE) + // - scale for getScale(...), and + // - and displaySize for getColumnDisplaySize(...). + final int precision = + rpcDataType.hasPrecision() ? rpcDataType.getPrecision() : 0; + final int scale = rpcDataType.hasScale() ? rpcDataType.getScale() : 0; + final int displaySize = 10; + + ColumnMetaData col = new ColumnMetaData( + colOffset, // (zero-based ordinal (for Java arrays/lists).) + false, /* autoIncrement */ + false, /* caseSensitive */ + true, /* searchable */ + false, /* currency */ + nullability, + isSigned, + displaySize, + columnName, /* label */ + columnName, /* columnName */ + schemaName, + precision, + scale, + tableName, + catalogName, + bundledSqlDataType, + true, /* readOnly */ + false, /* writable */ + false, /* definitelyWritable */ + columnClassName + ); + newColumns.add(col); } + columns = newColumns; } @Override http://git-wip-us.apache.org/repos/asf/drill/blob/80835082/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillCursor.java ---------------------------------------------------------------------- diff --git a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillCursor.java b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillCursor.java index 5ae7509..07d7066 100644 --- a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillCursor.java +++ b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillCursor.java @@ -18,6 +18,7 @@ package org.apache.drill.jdbc.impl; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -31,6 +32,7 @@ import org.apache.drill.exec.exception.SchemaChangeException; import org.apache.drill.exec.record.BatchSchema; import org.apache.drill.exec.record.RecordBatchLoader; import org.apache.drill.exec.rpc.user.QueryDataBatch; +import org.apache.drill.exec.store.ischema.InfoSchemaConstants; import org.slf4j.Logger; import static org.slf4j.LoggerFactory.getLogger; @@ -38,7 +40,8 @@ import static org.slf4j.LoggerFactory.getLogger; class DrillCursor implements Cursor { private static final Logger logger = getLogger( DrillCursor.class ); - private static final String UNKNOWN = "--UNKNOWN--"; + /** JDBC-specified string for unknown catalog, schema, and table names. */ + private static final String UNKNOWN_NAME_STRING = ""; /** The associated {@link java.sql.ResultSet} implementation. */ private final DrillResultSetImpl resultSet; @@ -104,6 +107,10 @@ class DrillCursor implements Cursor { return currentRecordNumber; } + // (Overly restrictive Avatica uses List instead of List, so accessors/DrillAccessorList can't be of type + // List, and we have to cast from Accessor to + // AvaticaDrillSqlAccessor in updateColumns().) @Override public List createAccessors(List types, Calendar localCalendar, Factory factory) { @@ -111,9 +118,31 @@ class DrillCursor implements Cursor { return accessors; } + /** + * Updates column accessors and metadata from current record batch. + */ private void updateColumns() { + // First update accessors and schema from batch: accessors.generateAccessors(this, currentBatchHolder); - columnMetaDataList.updateColumnMetaData(UNKNOWN, UNKNOWN, UNKNOWN, schema); + + // Extract Java types from accessors for metadata's getColumnClassName: + final List> getObjectClasses = new ArrayList<>(); + // (Can't use modern for loop because, for some incompletely clear reason, + // DrillAccessorList blocks iterator() (throwing exception).) + for ( int ax = 0; ax < accessors.size(); ax++ ) { + final AvaticaDrillSqlAccessor accessor = + (AvaticaDrillSqlAccessor) accessors.get( ax ); + getObjectClasses.add( accessor.getObjectClass() ); + } + + // Update metadata for result set. + columnMetaDataList.updateColumnMetaData( + InfoSchemaConstants.IS_CATALOG_NAME, + UNKNOWN_NAME_STRING, // schema name + UNKNOWN_NAME_STRING, // table name + schema, + getObjectClasses ); + if (getResultSet().changeListener != null) { getResultSet().changeListener.schemaChanged(schema); } @@ -180,8 +209,9 @@ class DrillCursor implements Cursor { afterLastRow = true; return false; } else { - // Got next (or first) batch--reset record offset to beginning, - // assimilate schema if changed, ... ??? + // Got next (or first) batch--reset record offset to beginning; + // assimilate schema if changed; set up return value for first call + // to next(). currentRecordNumber = 0; http://git-wip-us.apache.org/repos/asf/drill/blob/80835082/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/TypeConvertingSqlAccessor.java ---------------------------------------------------------------------- diff --git a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/TypeConvertingSqlAccessor.java b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/TypeConvertingSqlAccessor.java index b542f94..2e17e33 100644 --- a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/TypeConvertingSqlAccessor.java +++ b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/TypeConvertingSqlAccessor.java @@ -46,6 +46,11 @@ class TypeConvertingSqlAccessor implements SqlAccessor { } @Override + public Class getObjectClass() { + return innerAccessor.getObjectClass(); + } + + @Override public boolean isNull( int rowOffset ) { return innerAccessor.isNull( rowOffset ); }