You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@drill.apache.org by pa...@apache.org on 2015/03/27 18:49:24 UTC

[6/6] drill git commit: DRILL-2465: Fix multiple DatabaseMetaData.getColumns() bugs.

DRILL-2465: Fix multiple DatabaseMetaData.getColumns() bugs.

- Added test DatabaseMetaDataGetColumnsTest.
- (Renamed Drill2128GetColumnsBugsTest to
  Drill2128GetColumnsDataTypeNotTypeCodeIntBugsTest.)
- Fixed/implemented various columns:
  - Added COLUMN_SIZE (big CASE expression handling lots of cases).
  - Fixed DECIMAL_DIGITS.
  - Fixed NUM_PREC_RADIX.
  - ~Fixed REMARKS ('' -> NULL).
  - ~Fixed COLUMN_DEF ('' -> NULL).
  - Fixed CHARACTER_OCTET_LENGTH.
  - Fixed ORDINAL_POSITION.
  - Fixed SCOPE_CATALOG, SCOPE_SCHEMA, SCOPE_TABLE,
  - Fixed SOURCE_DATA_TYPE.
- Note:  INTERVAL types have only *interim* implementations.
  (INFORMATION_SCHEMA.COLUMNS fixes are needed for completion.)
- Added canonical data type name strings in DATA_TYPE -> TYPE_NAME CASE
  expression (for robustness for expected upcoming INFORMATION_SCHEMA.COLUMNS
  standard-compliance bug fixing).


Project: http://git-wip-us.apache.org/repos/asf/drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/20efb2fb
Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/20efb2fb
Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/20efb2fb

Branch: refs/heads/master
Commit: 20efb2fbc8176242c94505bcaab662586d66ba4d
Parents: 8796fd1
Author: dbarclay <db...@maprtech.com>
Authored: Sat Mar 21 00:45:41 2015 -0700
Committer: Parth Chandra <pc...@maprtech.com>
Committed: Fri Mar 27 10:19:43 2015 -0700

----------------------------------------------------------------------
 .../java/org/apache/drill/jdbc/MetaImpl.java    |  409 ++-
 .../jdbc/DatabaseMetaDataGetColumnsTest.java    | 2779 ++++++++++++++++++
 .../jdbc/test/Drill2128GetColumnsBugsTest.java  |  169 --
 ...etColumnsDataTypeNotTypeCodeIntBugsTest.java |  169 ++
 4 files changed, 3261 insertions(+), 265 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/drill/blob/20efb2fb/exec/jdbc/src/main/java/org/apache/drill/jdbc/MetaImpl.java
----------------------------------------------------------------------
diff --git a/exec/jdbc/src/main/java/org/apache/drill/jdbc/MetaImpl.java b/exec/jdbc/src/main/java/org/apache/drill/jdbc/MetaImpl.java
index 99e0d22..4ff626e 100644
--- a/exec/jdbc/src/main/java/org/apache/drill/jdbc/MetaImpl.java
+++ b/exec/jdbc/src/main/java/org/apache/drill/jdbc/MetaImpl.java
@@ -34,10 +34,51 @@ import org.apache.drill.common.util.DrillStringUtils;
 
 
 public class MetaImpl implements Meta {
+  private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MetaImpl.class);
+
+  // TODO:  Use more central version of these constants if availabe.
+
+  /** Radix used to report precision and scale of integral exact numeric types. */
+  private static final int RADIX_INTEGRAL = 10;
+  /** Radix used to report precision and scale of non-integral exact numeric
+      types (DECIMAL). */
+  private static final int RADIX_DECIMAL = 10;
+  /** Radix used to report precision and scale of approximate numeric types
+      (FLOAT, etc.). */
+  private static final int RADIX_APPROXIMATE = 10;
+  /** Radix used to report precisions of interval types. */
+  private static final int RADIX_INTERVAL = 10;
+
+  /** (Maximum) precision of TINYINT. */
+  private static final int PREC_TINYINT  = 3;
+  /** (Maximum) precision of SMALLINT. */
+  private static final int PREC_SMALLINT = 5;
+  /** (Maximum) precision of INTEGER. */
+  private static final int PREC_INTEGER  = 10;
+  /** (Maximum) precision of BIGINT. */
+  private static final int PREC_BIGINT   = 19;
+
+  /** Precision of FLOAT. */
+  private static final int PREC_FLOAT  =  7;
+  /** Precision of DOUBLE. */
+  private static final int PREC_DOUBLE = 15;
+  /** Precision of REAL. */
+  private static final int PREC_REAL   = PREC_DOUBLE;
+
+  /** Scale of INTEGER types. */
+  private static final int SCALE_INTEGRAL = 0;
+  /** JDBC conventional(?) scale value for FLOAT. */
+  private static final int SCALE_FLOAT = 7;
+  /** JDBC conventional(?) scale value for DOUBLE. */
+  private static final int SCALE_DOUBLE = 15;
+  /** JDBC conventional(?) scale value for REAL. */
+  private static final int SCALE_REAL = SCALE_DOUBLE;
+
+  /** (Apparent) maximum precision for starting unit of INTERVAL type. */
+  private static final int PREC_INTERVAL_LEAD_MAX = 10;
+  /** (Apparent) maximum fractional seconds precision for INTERVAL type. */
+  private static final int PREC_INTERVAL_TRAIL_MAX = 9;
 
-  static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MetaImpl.class);
-
-  static final Driver DRIVER = new Driver();
 
   final DrillConnectionImpl connection;
 
@@ -126,145 +167,323 @@ public class MetaImpl implements Meta {
     return s(sb.toString());
   }
 
-  public ResultSet getColumns(String catalog, Pat schemaPattern, Pat tableNamePattern, Pat columnNamePattern) {
+  /**
+   * Implements @link DatabaseMetaData#getColumns()}.
+   */
+  public ResultSet getColumns(String catalog, Pat schemaPattern,
+                              Pat tableNamePattern, Pat columnNamePattern) {
     StringBuilder sb = new StringBuilder();
-    // TODO:  Fix the various remaining bugs and resolve the various questions
-    // noted below.
+    // TODO:  Resolve the various questions noted below.
     sb.append(
-        "SELECT \n"
-        // getColumns INFORMATION_SCHEMA.COLUMNS   getColumns()
-        // column     source column or             column name
-        // number     expression
-        // -------    ------------------------     -------------
-        + /*  1 */ "  TABLE_CATALOG            as  TABLE_CAT, \n"
-        + /*  2 */ "  TABLE_SCHEMA             as  TABLE_SCHEM, \n"
-        + /*  3 */ "  TABLE_NAME               as  TABLE_NAME, \n"
-        + /*  4 */ "  COLUMN_NAME              as  COLUMN_NAME, \n"
-
+        "SELECT "
+        // getColumns   INFORMATION_SCHEMA.COLUMNS        getColumns()
+        // column       source column or                  column name
+        // number       expression
+        // -------      ------------------------          -------------
+        + /*  1 */ "\n  TABLE_CATALOG                 as  TABLE_CAT, "
+        + /*  2 */ "\n  TABLE_SCHEMA                  as  TABLE_SCHEM, "
+        + /*  3 */ "\n  TABLE_NAME                    as  TABLE_NAME, "
+        + /*  4 */ "\n  COLUMN_NAME                   as  COLUMN_NAME, "
+
+        /*    5                                           DATA_TYPE */
         // TODO:  Resolve the various questions noted below for DATA_TYPE.
-        /*    5  (DATA_TYPE) */
-        + "  CASE \n"
+        + "\n  CASE DATA_TYPE "
         // (All values in JDBC 4.0/Java 7 java.sql.Types except for types.NULL:)
 
-        // TODO:  RESOLVE:  How does ARRAY appear in COLUMNS.DATA_TYPE?
-        // - Only at end (with no maximum size, as "VARCHAR(65535) ARRAY")?
-        // - Possibly with maximum size (as "... ARRAY[10]")?
-        // (SQL source syntax:
-        //   <array type> ::=
-        //     <data type> ARRAY
-        //       [ <left bracket or trigraph> <maximum cardinality> <right bracket or trigraph> ]
-        + "    WHEN DATA_TYPE LIKE '% ARRAY'    THEN " + Types.ARRAY
-
-        + "    WHEN DATA_TYPE = 'BIGINT'        THEN " + Types.BIGINT
-        + "    WHEN DATA_TYPE = 'BINARY'        THEN " + Types.BINARY
+        // Exact-match cases:
+        + "\n    WHEN 'BIGINT'                      THEN " + Types.BIGINT
+        + "\n    WHEN 'BINARY'                      THEN " + Types.BINARY
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'BIT'           THEN " + Types.BIT
+        + "\n    WHEN 'BIT'                         THEN " + Types.BIT
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'BLOB'          THEN " + Types.BLOB
-        + "    WHEN DATA_TYPE = 'BOOLEAN'       THEN " + Types.BOOLEAN
+        + "\n    WHEN 'BLOB', 'BINARY LARGE OBJECT' THEN " + Types.BLOB
+        + "\n    WHEN 'BOOLEAN'                     THEN " + Types.BOOLEAN
 
-        + "    WHEN DATA_TYPE = 'CHAR'          THEN " + Types.CHAR
+        + "\n    WHEN 'CHAR', 'CHARACTER'           THEN " + Types.CHAR
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'CLOB'          THEN " + Types.CLOB
+        + "\n    WHEN 'CLOB', 'CHARACTER LARGE OBJECT' "
+        + "\n                                       THEN " + Types.CLOB
 
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'DATALINK'      THEN " + Types.DATALINK
-        + "    WHEN DATA_TYPE = 'DATE'          THEN " + Types.DATE
-        + "    WHEN DATA_TYPE = 'DECIMAL'       THEN " + Types.DECIMAL
+        + "\n    WHEN 'DATALINK'                    THEN " + Types.DATALINK
+        + "\n    WHEN 'DATE'                        THEN " + Types.DATE
+        + "\n    WHEN 'DECIMAL'                     THEN " + Types.DECIMAL
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'DISTINCT'      THEN " + Types.DISTINCT
-        + "    WHEN DATA_TYPE = 'DOUBLE'        THEN " + Types.DOUBLE
+        + "\n    WHEN 'DISTINCT'                    THEN " + Types.DISTINCT
+        + "\n    WHEN 'DOUBLE', 'DOUBLE PRECISION'  THEN " + Types.DOUBLE
+
+        + "\n    WHEN 'FLOAT'                       THEN " + Types.FLOAT
 
-        + "    WHEN DATA_TYPE = 'FLOAT'         THEN " + Types.FLOAT
+        + "\n    WHEN 'INTEGER'                     THEN " + Types.INTEGER
 
-        + "    WHEN DATA_TYPE = 'INTEGER'       THEN " + Types.INTEGER
+        // Drill's INFORMATION_SCHEMA's COLUMNS currently has
+        // "INTERVAL_YEAR_MONTH" and "INTERVAL_DAY_TIME" instead of SQL standard
+        // 'INTERVAL'.
+        + "\n    WHEN 'INTERVAL', "
+        + "\n         'INTERVAL_YEAR_MONTH', "
+        + "\n         'INTERVAL_DAY_TIME'           THEN " + Types.OTHER
 
         // Resolve:  Not seen in Drill yet.  Can it ever appear?:
-        + "    WHEN DATA_TYPE = 'JAVA_OBJECT'   THEN " + Types.JAVA_OBJECT
+        + "\n    WHEN 'JAVA_OBJECT'                 THEN " + Types.JAVA_OBJECT
 
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'LONGNVARCHAR'  THEN " + Types.LONGNVARCHAR
+        + "\n    WHEN 'LONGNVARCHAR'                THEN " + Types.LONGNVARCHAR
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'LONGVARBINARY' THEN " + Types.LONGVARBINARY
+        + "\n    WHEN 'LONGVARBINARY'               THEN " + Types.LONGVARBINARY
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'LONGVARCHAR'   THEN " + Types.LONGVARCHAR
+        + "\n    WHEN 'LONGVARCHAR'                 THEN " + Types.LONGVARCHAR
 
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'NCHAR'         THEN " + Types.NCHAR
+        + "\n    WHEN 'NCHAR', 'NATIONAL CHARACTER' THEN " + Types.NCHAR
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'NCLOB'         THEN " + Types.NCLOB
+        + "\n    WHEN 'NCLOB', 'NATIONAL CHARACTER LARGE OBJECT' "
+        + "\n                                       THEN " + Types.NCLOB
         // TODO:  Resolve following about NULL (and then update comment and code):
         // It is not clear whether Types.NULL can represent a type (perhaps the
         // type of the literal NULL when no further type information is known?) or
         // whether 'NULL' can appear in INFORMATION_SCHEMA.COLUMNS.DATA_TYPE.
         // For now, since it shouldn't hurt, include 'NULL'/Types.NULL in mapping.
-        + "    WHEN DATA_TYPE = 'NULL'          THEN " + Types.NULL
+        + "\n    WHEN 'NULL'                        THEN " + Types.NULL
         // (No NUMERIC--Drill seems to map any to DECIMAL currently.)
-        + "    WHEN DATA_TYPE = 'NUMERIC'       THEN " + Types.NUMERIC
+        + "\n    WHEN 'NUMERIC'                     THEN " + Types.NUMERIC
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'NVARCHAR'      THEN " + Types.NVARCHAR
+        + "\n    WHEN 'NVARCHAR', 'NATIONAL CHARACTER VARYING' "
+        + "\n                                       THEN " + Types.NVARCHAR
 
         // Resolve:  Unexpectedly, has appeared in Drill.  Should it?
-        + "    WHEN DATA_TYPE = 'OTHER'         THEN " + Types.OTHER
+        + "\n    WHEN 'OTHER'                       THEN " + Types.OTHER
 
-        + "    WHEN DATA_TYPE = 'REAL'          THEN " + Types.REAL
+        + "\n    WHEN 'REAL'                        THEN " + Types.REAL
         // SQL source syntax:
         //   <reference type> ::=
         //     REF <left paren> <referenced type> <right paren> [ <scope clause> ]
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'REF'           THEN " + Types.REF
+        + "\n    WHEN 'REF'                         THEN " + Types.REF
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'ROWID'         THEN " + Types.ROWID
+        + "\n    WHEN 'ROWID'                       THEN " + Types.ROWID
 
-        + "    WHEN DATA_TYPE = 'SMALLINT'      THEN " + Types.SMALLINT
+        + "\n    WHEN 'SMALLINT'                    THEN " + Types.SMALLINT
         // Resolve:  Not seen in Drill yet.  Can it appear?:
-        + "    WHEN DATA_TYPE = 'SQLXML'        THEN " + Types.SQLXML
+        + "\n    WHEN 'SQLXML'                      THEN " + Types.SQLXML
 
-        // TODO:  RESOLVE:  How does "STRUCT" appear?
-        // - Only at beginning (as "STRUCT(INTEGER sint, BOOLEAN sboolean")?
-        // - Otherwise too?
-        + "    WHEN DATA_TYPE LIKE 'STRUCT(%'   THEN " + Types.STRUCT
+        + "\n    WHEN 'TIME'                        THEN " + Types.TIME
+        + "\n    WHEN 'TIMESTAMP'                   THEN " + Types.TIMESTAMP
+        + "\n    WHEN 'TINYINT'                     THEN " + Types.TINYINT
 
-        + "    WHEN DATA_TYPE = 'TIME'          THEN " + Types.TIME
-        + "    WHEN DATA_TYPE = 'TIMESTAMP'     THEN " + Types.TIMESTAMP
-        + "    WHEN DATA_TYPE = 'TINYINT'       THEN " + Types.TINYINT
+        + "\n    WHEN 'VARBINARY', 'BINARY VARYING' THEN " + Types.VARBINARY
+        + "\n    WHEN 'VARCHAR', 'CHARACTER VARYING' "
+        + "\n                                       THEN " + Types.VARCHAR
 
-        + "    WHEN DATA_TYPE = 'VARBINARY'     THEN " + Types.VARBINARY
-        + "    WHEN DATA_TYPE = 'VARCHAR'       THEN " + Types.VARCHAR
+        + "\n    ELSE"
+        // Pattern-match cases:
+        + "\n      CASE "
+
+        // TODO:  RESOLVE:  How does ARRAY appear in COLUMNS.DATA_TYPE?
+        // - Only at end (with no maximum size, as "VARCHAR(65535) ARRAY")?
+        // - Possibly with maximum size (as "... ARRAY[10]")?
+        // - Then, how should it appear in JDBC ("ARRAY"? "... ARRAY"?)
+        // (SQL source syntax:
+        //   <array type> ::=
+        //     <data type> ARRAY
+        //       [ <left bracket or trigraph> <maximum cardinality>
+        //         <right bracket or trigraph> ]
+        + "\n        WHEN DATA_TYPE LIKE '% ARRAY'  THEN " + Types.ARRAY
 
         // TODO:  RESOLVE:  How does MAP appear in COLUMNS.DATA_TYPE?
         // - Only at end?
         // - Otherwise?
         // TODO:  RESOLVE:  Should it map to Types.OTHER or something else?
         // Has appeared in Drill.  Should it?
-        + "    WHEN DATA_TYPE LIKE '% MAP'      THEN " + Types.OTHER
-
-        + "    ELSE                                  " + Types.OTHER
-        + "  END                               as  DATA_TYPE, \n"
-
-        + /*  6 */ "  DATA_TYPE                as  TYPE_NAME, \n"
-        ///*  7 */  FIXME:  BUG:  There should be: COLUMN_SIZE
-        + /*  8 */ "  CHARACTER_MAXIMUM_LENGTH as  BUFFER_LENGTH, \n"
-        //  FIXME:  BUG:  Many of the following are wrong.
-        + /*  9 */ "  NUMERIC_PRECISION        as  DECIMAL_PRECISION, \n" // FIXME:  BUG:  Should be "DECIMAL_DIGITS"
-        + /* 10 */ "  NUMERIC_PRECISION_RADIX  as  NUM_PREC_RADIX, \n"
-        + /* 11 */ "  " + DatabaseMetaData.columnNullableUnknown
-        +             "                        as  NULLABLE, \n"
-        + /* 12 */ "  ''                       as  REMARKS, \n"
-        + /* 13 */ "  ''                       as  COLUMN_DEF, \n"
-        + /* 14 */ "  0                        as  SQL_DATA_TYPE, \n"
-        + /* 15 */ "  0                        as  SQL_DATETIME_SUB, \n"
-        + /* 16 */ "  4                        as  CHAR_OCTET_LENGTH, \n"
-        + /* 17 */ "  1                        as  ORDINAL_POSITION, \n"
-        + /* 18 */ "  'YES'                    as  IS_NULLABLE, \n"
-        + /* 19 */ "  ''                       as  SCOPE_CATALOG,"
-        + /* 20 */ "  ''                       as  SCOPE_SCHEMA, \n"
-        + /* 21 */ "  ''                       as  SCOPE_TABLE, \n"
-        + /* 22 */ "  ''                       as  SOURCE_DATA_TYPE, \n"
-        + /* 23 */ "  ''                       as  IS_AUTOINCREMENT, \n"
-        + /* 24 */ "  ''                       as  IS_GENERATEDCOLUMN \n"
-        + "FROM INFORMATION_SCHEMA.COLUMNS \n"
-        + "WHERE 1=1 ");
+        + "\n        WHEN DATA_TYPE LIKE '% MAP'    THEN " + Types.OTHER
+
+        // TODO:  RESOLVE:  How does "STRUCT" appear?
+        // - Only at beginning (as "STRUCT(INTEGER sint, BOOLEAN sboolean")?
+        // - Otherwise too?
+        // - Then, how should it appear in JDBC ("STRUCT"? "STRUCT(...)"?)
+        + "\n        WHEN DATA_TYPE LIKE 'STRUCT(%' THEN " + Types.STRUCT
+
+        + "\n        ELSE                                " + Types.OTHER
+        + "\n      END "
+        + "\n  END                                    as  DATA_TYPE, "
+
+        /*    6                                           TYPE_NAME */
+        // Map Drill's current info. schema values to what SQL standard
+        // specifies (for DATA_TYPE)--and assume that that's what JDBC wants.
+        + "\n  CASE DATA_TYPE "
+        + "\n    WHEN 'INTERVAL_YEAR_MONTH', "
+        + "\n         'INTERVAL_DAY_TIME'     THEN 'INTERVAL'"
+        // TODO:  Resolve how non-scalar types should appear in
+        // INFORMATION_SCHEMA.COLUMNS and here in JDBC:
+        // - "ARRAY" or "... ARRAY"?
+        // - "MAP" or "... MAP"?
+        // - "STRUCT" or "STRUCT(...)"?
+        + "\n    ELSE                               DATA_TYPE "
+        + "\n  END                                    as TYPE_NAME, "
+
+        /*    7                                           COLUMN_SIZE */
+        /* "... COLUMN_SIZE ....
+         * For numeric data, this is the maximum precision.
+         * For character data, this is the length in characters.
+         * For datetime datatypes, this is the length in characters of the String
+         *   representation (assuming the maximum allowed precision of the
+         *   fractional seconds component).
+         * For binary data, this is the length in bytes.
+         * For the ROWID datatype, this is the length in bytes.
+         * Null is returned for data types where the column size is not applicable."
+         *
+         * Note:  "Maximum precision" seems to mean the maximum number of
+         * significant decimal digits that can appear (not the number of digits
+         * that can be counted on, and not the maximum number of characters
+         * needed to display a value).
+         */
+        + "\n  CASE DATA_TYPE "
+
+        // "For numeric data, ... the maximum precision":
+        //   TODO:  Change literals to references to declared constant fields:
+        // - exact numeric types:
+        //   (in decimal digits, coordinated with NUM_PREC_RADIX = 10)
+        + "\n    WHEN 'TINYINT'                      THEN " + PREC_TINYINT
+        + "\n    WHEN 'SMALLINT'                     THEN " + PREC_SMALLINT
+        + "\n    WHEN 'INTEGER'                      THEN " + PREC_INTEGER
+        + "\n    WHEN 'BIGINT'                       THEN " + PREC_BIGINT
+        + "\n    WHEN 'DECIMAL', 'NUMERIC'           THEN NUMERIC_PRECISION "
+        // - approximate numeric types:
+        //   (in decimal digits, coordinated with NUM_PREC_RADIX = 10)
+        // TODO:  REVISIT:  Should these be in bits or decimal digits (with
+        //   NUM_PREC_RADIX coordinated)?  INFORMATION_SCHEMA.COLUMNS's value
+        //   are supposed to be in bits (per the SQL spec.).  What does JDBC
+        //   require and allow?
+        + "\n    WHEN 'FLOAT'                        THEN " + PREC_FLOAT
+        + "\n    WHEN 'DOUBLE'                       THEN " + PREC_DOUBLE
+        + "\n    WHEN 'REAL'                         THEN " + PREC_REAL
+
+        // "For character data, ... the length in characters":
+        // TODO:  BUG:  DRILL-2459:  For CHARACTER / CHAR, length is not in
+        // CHARACTER_MAXIMUM_LENGTH but in NUMERIC_PRECISION.
+        // Workaround:
+        + "\n    WHEN 'VARCHAR', 'CHARACTER VARYING' "
+        + "\n                                    THEN CHARACTER_MAXIMUM_LENGTH "
+        + "\n    WHEN 'CHAR', 'CHARACTER', "
+        + "\n         'NCHAR', 'NATIONAL CHAR', 'NATIONAL CHARACTER' "
+        + "\n                                        THEN NUMERIC_PRECISION "
+
+        // "For datetime datatypes ... length ... String representation
+        // (assuming the maximum ... precision of ... fractional seconds ...)":
+        + "\n    WHEN 'DATE'            THEN 10 "              // YYYY-MM-DD
+        + "\n    WHEN 'TIME'            THEN "
+        + "\n      CASE "
+        + "\n        WHEN NUMERIC_PRECISION > 0 "              // HH:MM:SS.sss
+        + "\n                           THEN          8 + 1 + NUMERIC_PRECISION"
+        + "\n        ELSE                             8"       // HH:MM:SS
+        + "\n      END "
+        + "\n    WHEN 'TIMESTAMP'       THEN "
+        + "\n      CASE "                          // date + "T" + time (above)
+        + "\n        WHEN NUMERIC_PRECISION > 0 "
+        + "                             THEN 10 + 1 + 8 + 1 + NUMERIC_PRECISION"
+        + "\n        ELSE                    10 + 1 + 8"
+        + "\n      END "
+
+        // TODO:  DRILL-2531:  When DRILL-2519 is fixed, use start and end unit
+        // and start-unit precision to implement maximum width more precisely
+        // (narrowly) than this workaround:
+        // For INTERVAL_YEAR_MONTH, maximum width is from "P1234567890Y12M"
+        // (5 + apparent maximum start unit precision of 10)
+        // unit precision):
+        + "\n    WHEN 'INTERVAL_YEAR_MONTH' "
+        + "\n                                        THEN 5 + "
+                                                          + PREC_INTERVAL_LEAD_MAX
+        // For INTERVAL_DAY_TIME, maximum width is from
+        // "P1234567890D12H12M12.123456789S" (12 + apparent maximum start unit
+        // precision of 10 + apparent maximum seconds fractional precision of 9):
+        + "\n    WHEN 'INTERVAL_DAY_TIME' "
+        + "\n                                        THEN 12 + "
+                                                          + ( PREC_INTERVAL_LEAD_MAX
+                                                             + PREC_INTERVAL_TRAIL_MAX )
+
+        // "For binary data, ... the length in bytes":
+        // BUG:  DRILL-2459:  BINARY and BINARY VARYING / VARBINARY length is
+        // not in CHARACTER_MAXIMUM_LENGTH but in NUMERIC_PRECISION.
+        // Workaround:
+        + "\n    WHEN 'VARBINARY', 'BINARY VARYING', "
+        + "\n         'BINARY'                       THEN NUMERIC_PRECISION "
+
+        // "For ... ROWID datatype...": Not in Drill?
+
+        // "Null ... for data types [for which] ... not applicable.":
+        + "\n    ELSE                                     NULL "
+        + "\n  END                                    as  COLUMN_SIZE, "
+
+        + /*  8 */ "\n  CHARACTER_MAXIMUM_LENGTH      as  BUFFER_LENGTH, "
+
+        /*    9                                           DECIMAL_DIGITS */
+        + "\n  CASE  DATA_TYPE"
+        + "\n    WHEN 'TINYINT', "
+        + "\n         'SMALLINT', "
+        + "\n         'INTEGER', "
+        + "\n         'BIGINT'                       THEN " + SCALE_INTEGRAL
+        + "\n    WHEN 'DECIMAL', "
+        + "\n         'NUMERIC'                      THEN NUMERIC_SCALE "
+        + "\n    WHEN 'FLOAT'                        THEN " + SCALE_FLOAT
+        + "\n    WHEN 'DOUBLE'                       THEN " + SCALE_DOUBLE
+        + "\n    WHEN 'REAL'                         THEN " + SCALE_REAL
+        + "\n    WHEN 'INTERVAL'                     THEN NUMERIC_SCALE "
+        + "\n    WHEN 'INTERVAL_YEAR_MONTH'          THEN 0 "
+        + "\n    WHEN 'INTERVAL_DAY_TIME'            THEN NUMERIC_SCALE "
+        + "\n  END                                    as  DECIMAL_DIGITS, "
+
+        /*   10                                           NUM_PREC_RADIX */
+        + "\n  CASE DATA_TYPE "
+        + "\n    WHEN 'TINYINT', "
+        + "\n         'SMALLINT', "
+        + "\n         'INTEGER', "
+        + "\n         'BIGINT'                       THEN " + RADIX_INTEGRAL
+        + "\n    WHEN 'DECIMAL', "
+        + "\n         'NUMERIC'                      THEN " + RADIX_DECIMAL
+        + "\n    WHEN 'FLOAT', "
+        + "\n         'DOUBLE', "
+        + "\n         'REAL'                         THEN " + RADIX_APPROXIMATE
+        + "\n    WHEN 'INTERVAL_YEAR_MONTH', "
+        + "\n         'INTERVAL_DAY_TIME'            THEN " + RADIX_INTERVAL
+        + "\n    ELSE                                     NULL"
+        + "\n  END                                    as  NUM_PREC_RADIX, "
+
+        /*   11                                           NULLABLE */
+        + "\n  CASE IS_NULLABLE "
+        + "\n    WHEN 'YES'      THEN " + DatabaseMetaData.columnNullable
+        + "\n    WHEN 'NO'       THEN " + DatabaseMetaData.columnNoNulls
+        + "\n    WHEN ''         THEN " + DatabaseMetaData.columnNullableUnknown
+        + "\n    ELSE                 -1"
+        + "\n  END                                    as  NULLABLE, "
+
+        + /* 12 */ "\n  CAST( NULL as VARCHAR )       as  REMARKS, "
+        + /* 13 */ "\n  CAST( NULL as VARCHAR )       as  COLUMN_DEF, "
+        + /* 14 */ "\n  0                             as  SQL_DATA_TYPE, "
+        + /* 15 */ "\n  0                             as  SQL_DATETIME_SUB, "
+
+        /*   16                                           CHAR_OCTET_LENGTH */
+        + "\n  CASE DATA_TYPE"
+        + "\n    WHEN 'VARCHAR', 'CHARACTER VARYING' "
+        + "\n                                 THEN 4 * CHARACTER_MAXIMUM_LENGTH "
+        + "\n    WHEN 'CHAR', 'CHARACTER', "
+        + "\n         'NCHAR', 'NATIONAL CHAR', 'NATIONAL CHARACTER' "
+        // TODO:  BUG:  DRILL-2459:  For CHARACTER / CHAR, length is not in
+        // CHARACTER_MAXIMUM_LENGTH but in NUMERIC_PRECISION.  Workaround:
+        + "\n                                 THEN 4 * NUMERIC_PRECISION "
+        + "\n    ELSE                              NULL "
+        + "\n  END                                    as  CHAR_OCTET_LENGTH, "
+
+        + /* 17 */ "\n  1 + ORDINAL_POSITION          as  ORDINAL_POSITION, "
+        + /* 18 */ "\n  IS_NULLABLE                   as  IS_NULLABLE, "
+        + /* 19 */ "\n  CAST( NULL as VARCHAR )       as  SCOPE_CATALOG, "
+        + /* 20 */ "\n  CAST( NULL as VARCHAR )       as  SCOPE_SCHEMA, "
+        + /* 21 */ "\n  CAST( NULL as VARCHAR )       as  SCOPE_TABLE, "
+        // TODO:  Change to SMALLINT when it's implemented (DRILL-2470):
+        + /* 22 */ "\n  CAST( NULL as INTEGER )       as  SOURCE_DATA_TYPE, "
+        + /* 23 */ "\n  ''                            as  IS_AUTOINCREMENT, "
+        + /* 24 */ "\n  ''                            as  IS_GENERATEDCOLUMN "
+
+        + "\n  FROM INFORMATION_SCHEMA.COLUMNS "
+        + "\n  WHERE 1=1 ");
 
     if (catalog != null) {
       sb.append("\n  AND TABLE_CATALOG = '" + DrillStringUtils.escapeSql(catalog) + "'");
@@ -272,16 +491,14 @@ public class MetaImpl implements Meta {
     if (schemaPattern.s != null) {
       sb.append("\n  AND TABLE_SCHEMA like '" + DrillStringUtils.escapeSql(schemaPattern.s) + "'");
     }
-
     if (tableNamePattern.s != null) {
       sb.append("\n  AND TABLE_NAME like '" + DrillStringUtils.escapeSql(tableNamePattern.s) + "'");
     }
-
     if (columnNamePattern.s != null) {
       sb.append("\n  AND COLUMN_NAME like '" + DrillStringUtils.escapeSql(columnNamePattern.s) + "'");
     }
 
-    sb.append(" ORDER BY TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME");
+    sb.append("\n ORDER BY TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME");
 
     return s(sb.toString());
   }