SQL: Consolidate more type information into DataType (elastic/x-pack-elasticsearch#3850)
Consolidates type handling into DataType, makes DataType available to JDBC by moving to sql-proto and removes support for all parameter types that cannot be handled by the server. Original commit: elastic/x-pack-elasticsearch@b8024f5c46
This commit is contained in:
parent
5dbbe8fef8
commit
c82fdad41d
|
@ -6,10 +6,8 @@
|
|||
package org.elasticsearch.xpack.sql.jdbc.jdbc;
|
||||
|
||||
import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Date;
|
||||
import java.sql.JDBCType;
|
||||
import java.sql.SQLException;
|
||||
|
@ -38,13 +36,10 @@ import static java.util.Calendar.YEAR;
|
|||
/**
|
||||
* Conversion utilities for conversion of JDBC types to Java type and back
|
||||
* <p>
|
||||
* The following JDBC types are supported as part of Elasticsearch Response. See org.elasticsearch.xpack.sql.type.DataType for details.
|
||||
* Only the following JDBC types are supported as part of Elasticsearch response and parameters.
|
||||
* See org.elasticsearch.xpack.sql.type.DataType for details.
|
||||
* <p>
|
||||
* NULL, BOOLEAN, TINYINT, SMALLINT, INTEGER, BIGINT, DOUBLE, REAL, FLOAT, VARCHAR, VARBINARY and TIMESTAMP
|
||||
* <p>
|
||||
* The following additional types are also supported as parameters:
|
||||
* <p>
|
||||
* NUMERIC, DECIMAL, BIT, BINARY, LONGVARBINARY, CHAR, LONGVARCHAR, DATE, TIME, BLOB, CLOB, TIMESTAMP_WITH_TIMEZONE
|
||||
*/
|
||||
final class TypeConverter {
|
||||
|
||||
|
@ -172,57 +167,17 @@ final class TypeConverter {
|
|||
* https://db.apache.org/derby/docs/10.5/ref/rrefjdbc20377.html
|
||||
*/
|
||||
public static String classNameOf(JDBCType jdbcType) throws JdbcSQLException {
|
||||
switch (jdbcType) {
|
||||
|
||||
// ES - supported types
|
||||
case BOOLEAN:
|
||||
return Boolean.class.getName();
|
||||
case TINYINT: // BYTE DataType
|
||||
return Byte.class.getName();
|
||||
case SMALLINT: // SHORT DataType
|
||||
return Short.class.getName();
|
||||
case INTEGER:
|
||||
return Integer.class.getName();
|
||||
case BIGINT: // LONG DataType
|
||||
return Long.class.getName();
|
||||
case DOUBLE:
|
||||
return Double.class.getName();
|
||||
case REAL: // FLOAT DataType
|
||||
return Float.class.getName();
|
||||
case FLOAT: // HALF_FLOAT DataType
|
||||
return Double.class.getName(); // TODO: Is this correct?
|
||||
case VARCHAR: // KEYWORD or TEXT DataType
|
||||
return String.class.getName();
|
||||
case VARBINARY: // BINARY DataType
|
||||
return byte[].class.getName();
|
||||
case TIMESTAMP: // DATE DataType
|
||||
return Timestamp.class.getName();
|
||||
|
||||
// Parameters data types that cannot be returned by ES but can appear in client - supplied parameters
|
||||
case NUMERIC:
|
||||
case DECIMAL:
|
||||
return BigDecimal.class.getName();
|
||||
case BIT:
|
||||
return Boolean.class.getName();
|
||||
case BINARY:
|
||||
case LONGVARBINARY:
|
||||
return byte[].class.getName();
|
||||
case CHAR:
|
||||
case LONGVARCHAR:
|
||||
return String.class.getName();
|
||||
case DATE:
|
||||
return Date.class.getName();
|
||||
case TIME:
|
||||
return Time.class.getName();
|
||||
case BLOB:
|
||||
return Blob.class.getName();
|
||||
case CLOB:
|
||||
return Clob.class.getName();
|
||||
case TIMESTAMP_WITH_TIMEZONE:
|
||||
return Long.class.getName();
|
||||
default:
|
||||
throw new JdbcSQLException("Unsupported JDBC type [" + jdbcType + "]");
|
||||
final DataType dataType;
|
||||
try {
|
||||
dataType = DataType.fromJdbcType(jdbcType);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Convert unsupported exception to JdbcSQLException
|
||||
throw new JdbcSQLException(ex, ex.getMessage());
|
||||
}
|
||||
if (dataType.javaName == null) {
|
||||
throw new JdbcSQLException("Unsupported JDBC type [" + jdbcType + "]");
|
||||
}
|
||||
return dataType.javaName;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,8 +190,6 @@ final class TypeConverter {
|
|||
case NULL:
|
||||
return null;
|
||||
case BOOLEAN:
|
||||
case BINARY:
|
||||
case VARBINARY:
|
||||
case VARCHAR:
|
||||
return v; // These types are already represented correctly in JSON
|
||||
case TINYINT:
|
||||
|
@ -265,43 +218,15 @@ final class TypeConverter {
|
|||
* <p>
|
||||
* It needs to support both params and column types
|
||||
*/
|
||||
static boolean isSigned(JDBCType type) throws SQLException {
|
||||
switch (type) {
|
||||
// ES Supported types
|
||||
case BIGINT:
|
||||
case DOUBLE:
|
||||
case FLOAT:
|
||||
case INTEGER:
|
||||
case TINYINT:
|
||||
case SMALLINT:
|
||||
return true;
|
||||
case NULL:
|
||||
case BOOLEAN:
|
||||
case VARCHAR:
|
||||
case VARBINARY:
|
||||
case TIMESTAMP:
|
||||
return false;
|
||||
|
||||
// Parameter types
|
||||
case REAL:
|
||||
case DECIMAL:
|
||||
case NUMERIC:
|
||||
return true;
|
||||
case BIT:
|
||||
case BINARY:
|
||||
case LONGVARBINARY:
|
||||
case CHAR:
|
||||
case LONGVARCHAR:
|
||||
case DATE:
|
||||
case TIME:
|
||||
case BLOB:
|
||||
case CLOB:
|
||||
case TIMESTAMP_WITH_TIMEZONE:
|
||||
return false;
|
||||
|
||||
default:
|
||||
throw new SQLException("Unexpected column or parameter type [" + type + "]");
|
||||
static boolean isSigned(JDBCType jdbcType) throws SQLException {
|
||||
final DataType dataType;
|
||||
try {
|
||||
dataType = DataType.fromJdbcType(jdbcType);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Convert unsupported exception to JdbcSQLException
|
||||
throw new JdbcSQLException(ex, ex.getMessage());
|
||||
}
|
||||
return dataType.isSigned();
|
||||
}
|
||||
|
||||
private static Double doubleValue(Object v) {
|
||||
|
@ -427,12 +352,7 @@ final class TypeConverter {
|
|||
case FLOAT:
|
||||
case DOUBLE:
|
||||
return safeToLong(((Number) val).doubleValue());
|
||||
case DATE:
|
||||
return utcMillisRemoveTime(((Number) val).longValue());
|
||||
case TIME:
|
||||
return utcMillisRemoveDate(((Number) val).longValue());
|
||||
case TIMESTAMP:
|
||||
case TIMESTAMP_WITH_TIMEZONE:
|
||||
return ((Number) val).longValue();
|
||||
default:
|
||||
}
|
||||
|
@ -479,47 +399,23 @@ final class TypeConverter {
|
|||
}
|
||||
|
||||
private static Date asDate(Object val, JDBCType columnType) throws SQLException {
|
||||
switch (columnType) {
|
||||
case TIME:
|
||||
// time has no date component
|
||||
return new Date(0);
|
||||
case DATE:
|
||||
case TIMESTAMP:
|
||||
case TIMESTAMP_WITH_TIMEZONE:
|
||||
return new Date(utcMillisRemoveTime(((Number) val).longValue()));
|
||||
default:
|
||||
if (columnType == JDBCType.TIMESTAMP) {
|
||||
return new Date(utcMillisRemoveTime(((Number) val).longValue()));
|
||||
}
|
||||
|
||||
throw new SQLException("Conversion from type [" + columnType + "] to [Date] not supported");
|
||||
}
|
||||
|
||||
private static Time asTime(Object val, JDBCType columnType) throws SQLException {
|
||||
switch (columnType) {
|
||||
case DATE:
|
||||
// date has no time component
|
||||
return new Time(0);
|
||||
case TIME:
|
||||
case TIMESTAMP:
|
||||
case TIMESTAMP_WITH_TIMEZONE:
|
||||
return new Time(utcMillisRemoveDate(((Number) val).longValue()));
|
||||
default:
|
||||
if (columnType == JDBCType.TIMESTAMP) {
|
||||
return new Time(utcMillisRemoveDate(((Number) val).longValue()));
|
||||
}
|
||||
|
||||
throw new SQLException("Conversion from type [" + columnType + "] to [Time] not supported");
|
||||
}
|
||||
|
||||
private static Timestamp asTimestamp(Object val, JDBCType columnType) throws SQLException {
|
||||
switch (columnType) {
|
||||
case DATE:
|
||||
return new Timestamp(utcMillisRemoveTime(((Number) val).longValue()));
|
||||
case TIME:
|
||||
return new Timestamp(utcMillisRemoveDate(((Number) val).longValue()));
|
||||
case TIMESTAMP:
|
||||
case TIMESTAMP_WITH_TIMEZONE:
|
||||
return new Timestamp(((Number) val).longValue());
|
||||
default:
|
||||
if (columnType == JDBCType.TIMESTAMP) {
|
||||
return new Timestamp(((Number) val).longValue());
|
||||
}
|
||||
|
||||
throw new SQLException("Conversion from type [" + columnType + "] to [Timestamp] not supported");
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.type;
|
||||
|
||||
import java.sql.JDBCType;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Elasticsearch data types that supported by SQL interface
|
||||
*/
|
||||
public enum DataType {
|
||||
// @formatter:off
|
||||
// jdbc type, Java Class size, defPrecision, dispSize, int, rat, docvals
|
||||
NULL( JDBCType.NULL, null, 0, 0, 0),
|
||||
UNSUPPORTED( JDBCType.OTHER, null, 0, 0, 0),
|
||||
BOOLEAN( JDBCType.BOOLEAN, Boolean.class, 1, 1, 1),
|
||||
BYTE( JDBCType.TINYINT, Byte.class, Byte.BYTES, 3, 5, true, false, true),
|
||||
SHORT( JDBCType.SMALLINT, Short.class, Short.BYTES, 5, 6, true, false, true),
|
||||
INTEGER( JDBCType.INTEGER, Integer.class, Integer.BYTES, 10, 11, true, false, true),
|
||||
LONG( JDBCType.BIGINT, Long.class, Long.BYTES, 19, 20, true, false, true),
|
||||
// 53 bits defaultPrecision ~ 16(15.95) decimal digits (53log10(2)),
|
||||
DOUBLE( JDBCType.DOUBLE, Double.class, Double.BYTES, 16, 25, false, true, true),
|
||||
// 24 bits defaultPrecision - 24*log10(2) =~ 7 (7.22)
|
||||
FLOAT( JDBCType.REAL, Float.class, Float.BYTES, 7, 15, false, true, true),
|
||||
HALF_FLOAT( JDBCType.FLOAT, Double.class, Double.BYTES, 16, 25, false, true, true),
|
||||
// precision is based on long
|
||||
SCALED_FLOAT(JDBCType.FLOAT, Double.class, Double.BYTES, 19, 25, false, true, true),
|
||||
KEYWORD( JDBCType.VARCHAR, String.class, Integer.MAX_VALUE, 256, 0),
|
||||
TEXT( JDBCType.VARCHAR, String.class, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, false, false, false),
|
||||
OBJECT( JDBCType.STRUCT, null, -1, 0, 0),
|
||||
NESTED( JDBCType.STRUCT, null, -1, 0, 0),
|
||||
BINARY( JDBCType.VARBINARY, byte[].class, -1, Integer.MAX_VALUE, 0),
|
||||
DATE( JDBCType.TIMESTAMP, Timestamp.class, Long.BYTES, 19, 20);
|
||||
// @formatter:on
|
||||
|
||||
private static final Map<JDBCType, DataType> jdbcToEs;
|
||||
|
||||
static {
|
||||
jdbcToEs = Arrays.stream(DataType.values())
|
||||
.filter(dataType -> dataType != TEXT && dataType != NESTED && dataType != SCALED_FLOAT) // Remove duplicates
|
||||
.collect(Collectors.toMap(dataType -> dataType.jdbcType, dataType -> dataType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Elasticsearch type name
|
||||
*/
|
||||
public final String esType;
|
||||
|
||||
/**
|
||||
* Compatible JDBC type
|
||||
*/
|
||||
public final JDBCType jdbcType;
|
||||
|
||||
/**
|
||||
* Name of corresponding java class
|
||||
*/
|
||||
public final String javaName;
|
||||
|
||||
/**
|
||||
* Size of the type in bytes
|
||||
* <p>
|
||||
* -1 if the size can vary
|
||||
*/
|
||||
public final int size;
|
||||
|
||||
/**
|
||||
* Precision
|
||||
* <p>
|
||||
* Specified 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 defaultPrecision of the fractional seconds component).
|
||||
*/
|
||||
public final int defaultPrecision;
|
||||
|
||||
|
||||
/**
|
||||
* Display Size
|
||||
* <p>
|
||||
* Normal maximum width in characters.
|
||||
*/
|
||||
public final int displaySize;
|
||||
|
||||
/**
|
||||
* True if the type represents an integer number
|
||||
*/
|
||||
public final boolean isInteger;
|
||||
|
||||
/**
|
||||
* True if the type represents a rational number
|
||||
*/
|
||||
public final boolean isRational;
|
||||
|
||||
/**
|
||||
* True if the type supports doc values by default
|
||||
*/
|
||||
public final boolean defaultDocValues;
|
||||
|
||||
DataType(JDBCType jdbcType, Class<?> javaClass, int size, int defaultPrecision, int displaySize, boolean isInteger, boolean isRational,
|
||||
boolean defaultDocValues) {
|
||||
this.esType = name().toLowerCase(Locale.ROOT);
|
||||
this.javaName = javaClass == null ? null : javaClass.getName();
|
||||
this.jdbcType = jdbcType;
|
||||
this.size = size;
|
||||
this.defaultPrecision = defaultPrecision;
|
||||
this.displaySize = displaySize;
|
||||
this.isInteger = isInteger;
|
||||
this.isRational = isRational;
|
||||
this.defaultDocValues = defaultDocValues;
|
||||
}
|
||||
|
||||
DataType(JDBCType jdbcType, Class<?> javaClass, int size, int defaultPrecision, int displaySize) {
|
||||
this(jdbcType, javaClass, size, defaultPrecision, displaySize, false, false, true);
|
||||
}
|
||||
|
||||
public String sqlName() {
|
||||
return jdbcType.getName();
|
||||
}
|
||||
|
||||
public boolean isNumeric() {
|
||||
return isInteger || isRational;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if value is signed, false if it is unsigned or null if the type doesn't represent a number
|
||||
*/
|
||||
public Boolean isSigned() {
|
||||
// For now all numeric values that es supports are signed
|
||||
return isNumeric() ? true : null;
|
||||
}
|
||||
|
||||
public boolean isString() {
|
||||
return this == KEYWORD || this == TEXT;
|
||||
}
|
||||
|
||||
public boolean isPrimitive() {
|
||||
return this != OBJECT && this != NESTED;
|
||||
}
|
||||
|
||||
public static DataType fromJdbcType(JDBCType jdbcType) {
|
||||
if (jdbcToEs.containsKey(jdbcType) == false) {
|
||||
throw new IllegalArgumentException("Unsupported JDBC type [" + jdbcType + "]");
|
||||
}
|
||||
return jdbcToEs.get(jdbcType);
|
||||
}
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.type;
|
||||
|
||||
import java.sql.JDBCType;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Elasticsearch data types that supported by SQL interface
|
||||
*/
|
||||
public enum DataType {
|
||||
// @formatter:off
|
||||
// jdbc type, size, defPrecision, dispSize, int, rat, docvals
|
||||
NULL( JDBCType.NULL, 0, 0, 0),
|
||||
UNSUPPORTED( JDBCType.OTHER, 0, 0, 0),
|
||||
BOOLEAN( JDBCType.BOOLEAN, 1, 1, 1),
|
||||
BYTE( JDBCType.TINYINT, Byte.BYTES, 3, 5, true, false, true),
|
||||
SHORT( JDBCType.SMALLINT, Short.BYTES, 5, 6, true, false, true),
|
||||
INTEGER( JDBCType.INTEGER, Integer.BYTES, 10, 11, true, false, true),
|
||||
LONG( JDBCType.BIGINT, Long.BYTES, 19, 20, true, false, true),
|
||||
// 53 bits defaultPrecision ~ 16(15.95) decimal digits (53log10(2)),
|
||||
DOUBLE( JDBCType.DOUBLE, Double.BYTES, 16, 25, false, true, true),
|
||||
// 24 bits defaultPrecision - 24*log10(2) =~ 7 (7.22)
|
||||
FLOAT( JDBCType.REAL, Float.BYTES, 7, 15, false, true, true),
|
||||
HALF_FLOAT( JDBCType.FLOAT, Double.BYTES, 16, 25, false, true, true),
|
||||
// precision is based on long
|
||||
SCALED_FLOAT(JDBCType.FLOAT, Double.BYTES, 19, 25, false, true, true),
|
||||
KEYWORD( JDBCType.VARCHAR, Integer.MAX_VALUE, 256, 0),
|
||||
TEXT( JDBCType.VARCHAR, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, false, false, false),
|
||||
OBJECT( JDBCType.STRUCT, -1, 0, 0),
|
||||
NESTED( JDBCType.STRUCT, -1, 0, 0),
|
||||
BINARY( JDBCType.VARBINARY, -1, Integer.MAX_VALUE, 0),
|
||||
DATE( JDBCType.TIMESTAMP, Long.BYTES, 19, 20);
|
||||
// @formatter:on
|
||||
|
||||
/**
|
||||
* Elasticsearch type name
|
||||
*/
|
||||
public final String esType;
|
||||
|
||||
/**
|
||||
* Compatible JDBC type
|
||||
*/
|
||||
public final JDBCType jdbcType;
|
||||
|
||||
|
||||
/**
|
||||
* Size of the type in bytes
|
||||
* <p>
|
||||
* -1 if the size can vary
|
||||
*/
|
||||
public final int size;
|
||||
|
||||
/**
|
||||
* Precision
|
||||
* <p>
|
||||
* Specified 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 defaultPrecision of the fractional seconds component).
|
||||
*/
|
||||
public final int defaultPrecision;
|
||||
|
||||
|
||||
/**
|
||||
* Display Size
|
||||
* <p>
|
||||
* Normal maximum width in characters.
|
||||
*/
|
||||
public final int displaySize;
|
||||
|
||||
/**
|
||||
* True if the type represents an integer number
|
||||
*/
|
||||
public final boolean isInteger;
|
||||
|
||||
/**
|
||||
* True if the type represents a rational number
|
||||
*/
|
||||
public final boolean isRational;
|
||||
|
||||
/**
|
||||
* True if the type supports doc values by default
|
||||
*/
|
||||
public final boolean defaultDocValues;
|
||||
|
||||
DataType(JDBCType jdbcType, int size, int defaultPrecision, int displaySize, boolean isInteger, boolean isRational,
|
||||
boolean defaultDocValues) {
|
||||
this.esType = name().toLowerCase(Locale.ROOT);
|
||||
this.jdbcType = jdbcType;
|
||||
this.size = size;
|
||||
this.defaultPrecision = defaultPrecision;
|
||||
this.displaySize = displaySize;
|
||||
this.isInteger = isInteger;
|
||||
this.isRational = isRational;
|
||||
this.defaultDocValues = defaultDocValues;
|
||||
}
|
||||
|
||||
DataType(JDBCType jdbcType, int size, int defaultPrecision, int displaySize) {
|
||||
this(jdbcType, size, defaultPrecision, displaySize, false, false, true);
|
||||
}
|
||||
|
||||
public String sqlName() {
|
||||
return jdbcType.getName();
|
||||
}
|
||||
|
||||
public boolean isNumeric() {
|
||||
return isInteger || isRational;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if value is signed, false if it is unsigned or null if the type doesn't represent a number
|
||||
*/
|
||||
public Boolean isSigned() {
|
||||
// For now all numeric values that es supports are signed
|
||||
return isNumeric() ? true : null;
|
||||
}
|
||||
|
||||
public boolean isString() {
|
||||
return this == KEYWORD || this == TEXT;
|
||||
}
|
||||
|
||||
public boolean isPrimitive() {
|
||||
return this != OBJECT && this != NESTED;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue