Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SQL: Introduce INTERVAL support #35521

Merged
merged 9 commits into from
Nov 21, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
import org.elasticsearch.xpack.sql.client.Version;
import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException;
import org.elasticsearch.xpack.sql.jdbc.net.client.Cursor;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcColumnInfo;
import org.elasticsearch.xpack.sql.jdbc.type.DataType;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
Expand All @@ -19,12 +20,12 @@
import java.sql.RowIdLifetime;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLType;
import java.util.ArrayList;
import java.util.List;

import static java.sql.JDBCType.INTEGER;
import static java.sql.JDBCType.SMALLINT;
import static org.elasticsearch.xpack.sql.client.StringUtils.EMPTY;

/**
* Implementation of {@link DatabaseMetaData} for Elasticsearch. Draws inspiration
Expand Down Expand Up @@ -175,7 +176,7 @@ public String getIdentifierQuoteString() throws SQLException {
@Override
public String getSQLKeywords() throws SQLException {
// TODO: sync this with the grammar
return "";
return EMPTY;
}

@Override
Expand Down Expand Up @@ -212,7 +213,7 @@ public String getStringFunctions() throws SQLException {
@Override
public String getSystemFunctions() throws SQLException {
// TODO: sync this with the grammar
return "";
return EMPTY;
}

@Override
Expand All @@ -235,7 +236,7 @@ public String getSearchStringEscape() throws SQLException {

@Override
public String getExtraNameCharacters() throws SQLException {
return "";
return EMPTY;
}

@Override
Expand Down Expand Up @@ -716,15 +717,15 @@ private String defaultCatalog() throws SQLException {
private boolean isDefaultCatalog(String catalog) throws SQLException {
// null means catalog info is irrelevant
// % means return all catalogs
// "" means return those without a catalog
return catalog == null || catalog.equals("") || catalog.equals("%") || catalog.equals(defaultCatalog());
// EMPTY means return those without a catalog
return catalog == null || catalog.equals(EMPTY) || catalog.equals("%") || catalog.equals(defaultCatalog());
}

private boolean isDefaultSchema(String schema) {
// null means schema info is irrelevant
// % means return all schemas`
// "" means return those without a schema
return schema == null || schema.equals("") || schema.equals("%");
// EMPTY means return those without a schema
return schema == null || schema.equals(EMPTY) || schema.equals("%");
}

@Override
Expand Down Expand Up @@ -756,21 +757,21 @@ public ResultSet getTables(String catalog, String schemaPattern, String tableNam

@Override
public ResultSet getSchemas() throws SQLException {
Object[][] data = { { "", defaultCatalog() } };
Object[][] data = { { EMPTY, defaultCatalog() } };
return memorySet(con.cfg, columnInfo("SCHEMATA",
"TABLE_SCHEM",
"TABLE_CATALOG"), data);
}

@Override
public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
List<ColumnInfo> info = columnInfo("SCHEMATA",
List<JdbcColumnInfo> info = columnInfo("SCHEMATA",
"TABLE_SCHEM",
"TABLE_CATALOG");
if (!isDefaultCatalog(catalog) || !isDefaultSchema(schemaPattern)) {
return emptySet(con.cfg, info);
}
Object[][] data = { { "", defaultCatalog() } };
Object[][] data = { { EMPTY, defaultCatalog() } };
return memorySet(con.cfg, info, data);
}

Expand All @@ -789,7 +790,7 @@ public ResultSet getColumns(String catalog, String schemaPattern, String tableNa
throws SQLException {
PreparedStatement ps = con.prepareStatement("SYS COLUMNS CATALOG ? TABLE LIKE ? LIKE ?");
// TODO: until passing null works, pass an empty string
ps.setString(1, catalog != null ? catalog.trim() : "");
ps.setString(1, catalog != null ? catalog.trim() : EMPTY);
ps.setString(2, tableNamePattern != null ? tableNamePattern.trim() : "%");
ps.setString(3, columnNamePattern != null ? columnNamePattern.trim() : "%");
return ps.executeQuery();
Expand Down Expand Up @@ -1117,23 +1118,28 @@ public boolean generatedKeyAlwaysReturned() throws SQLException {
return false;
}

private static List<ColumnInfo> columnInfo(String tableName, Object... cols) throws JdbcSQLException {
List<ColumnInfo> columns = new ArrayList<>();
private static List<JdbcColumnInfo> columnInfo(String tableName, Object... cols) throws JdbcSQLException {
List<JdbcColumnInfo> columns = new ArrayList<>();

for (int i = 0; i < cols.length; i++) {
Object obj = cols[i];
if (obj instanceof String) {
String name = obj.toString();
SQLType type = JDBCType.VARCHAR;
DataType type = DataType.KEYWORD;
if (i + 1 < cols.length) {
Object next = cols[i + 1];
// check if the next item it's a type
if (cols[i + 1] instanceof SQLType) {
type = (SQLType) cols[i + 1];
i++;
if (next instanceof DataType || next instanceof JDBCType) {
try {
type = TypeUtils.of((JDBCType) next);
i++;
} catch (SQLException ex) {
throw new JdbcSQLException(ex, "Invalid metadata schema definition");
}
}
// it's not, use the default and move on
}
columns.add(new ColumnInfo(name, type, tableName, "INFORMATION_SCHEMA", "", "", 0));
columns.add(new JdbcColumnInfo(name, type, tableName, "INFORMATION_SCHEMA", EMPTY, EMPTY, 0));
}
else {
throw new JdbcSQLException("Invalid metadata schema definition");
Expand All @@ -1146,28 +1152,28 @@ private static ResultSet emptySet(JdbcConfiguration cfg, String tableName, Objec
return new JdbcResultSet(cfg, null, new InMemoryCursor(columnInfo(tableName, cols), null));
}

private static ResultSet emptySet(JdbcConfiguration cfg, List<ColumnInfo> columns) {
private static ResultSet emptySet(JdbcConfiguration cfg, List<JdbcColumnInfo> columns) {
return memorySet(cfg, columns, null);
}

private static ResultSet memorySet(JdbcConfiguration cfg, List<ColumnInfo> columns, Object[][] data) {
private static ResultSet memorySet(JdbcConfiguration cfg, List<JdbcColumnInfo> columns, Object[][] data) {
return new JdbcResultSet(cfg, null, new InMemoryCursor(columns, data));
}

static class InMemoryCursor implements Cursor {

private final List<ColumnInfo> columns;
private final List<JdbcColumnInfo> columns;
private final Object[][] data;

private int row = -1;

InMemoryCursor(List<ColumnInfo> info, Object[][] data) {
InMemoryCursor(List<JdbcColumnInfo> info, Object[][] data) {
this.columns = info;
this.data = data;
}

@Override
public List<ColumnInfo> columns() {
public List<JdbcColumnInfo> columns() {
return columns;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public int isNullable(int param) throws SQLException {

@Override
public boolean isSigned(int param) throws SQLException {
return TypeConverter.isSigned(paramInfo(param).type);
return TypeUtils.isSigned(paramInfo(param).type);
}

@Override
Expand All @@ -49,7 +49,7 @@ public int getScale(int param) throws SQLException {

@Override
public int getParameterType(int param) throws SQLException {
return paramInfo(param).type.getVendorTypeNumber();
return paramInfo(param).type.getVendorTypeNumber().intValue();
}

@Override
Expand All @@ -59,7 +59,7 @@ public String getParameterTypeName(int param) throws SQLException {

@Override
public String getParameterClassName(int param) throws SQLException {
return TypeConverter.classNameOf(paramInfo(param).type);
return TypeUtils.classOf(paramInfo(param).type).getName();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
package org.elasticsearch.xpack.sql.jdbc.jdbc;

import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.jdbc.type.DataType;

import java.io.InputStream;
import java.io.Reader;
Expand All @@ -15,15 +15,13 @@
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.JDBCType;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLDataException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLType;
Expand Down Expand Up @@ -70,7 +68,11 @@ public int executeUpdate() throws SQLException {
throw new SQLFeatureNotSupportedException("Writes not supported");
}

private void setParam(int parameterIndex, Object value, SQLType type) throws SQLException {
private void setParam(int parameterIndex, Object value, int sqlType) throws SQLException {
setParam(parameterIndex, value, TypeUtils.of(sqlType));
}

private void setParam(int parameterIndex, Object value, DataType type) throws SQLException {
checkOpen();

if (parameterIndex < 0 || parameterIndex > query.paramCount()) {
Expand All @@ -83,7 +85,7 @@ private void setParam(int parameterIndex, Object value, SQLType type) throws SQL

@Override
public void setNull(int parameterIndex, int sqlType) throws SQLException {
setParam(parameterIndex, null, JDBCType.valueOf(sqlType));
setParam(parameterIndex, null, sqlType);
}

@Override
Expand Down Expand Up @@ -179,20 +181,26 @@ public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQ
setObject(parameterIndex, x, targetSqlType, 0);
}

@Override
public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException {
setObject(parameterIndex, x, targetSqlType, 0);
}

@Override
public void setObject(int parameterIndex, Object x) throws SQLException {
if (x == null) {
setParam(parameterIndex, null, JDBCType.NULL);
setParam(parameterIndex, null, DataType.NULL);
return;
}

// check also here the unsupported types so that any unsupported interfaces ({@code java.sql.Struct},
// {@code java.sql.Array} etc) will generate the correct exception message. Otherwise, the method call
// {@code TypeConverter.fromJavaToJDBC(x.getClass())} will report the implementing class as not being supported.
checkKnownUnsupportedTypes(x);
setObject(parameterIndex, x, TypeConverter.fromJavaToJDBC(x.getClass()).getVendorTypeNumber(), 0);
setObject(parameterIndex, x, TypeUtils.of(x.getClass()).getVendorTypeNumber(), 0);
}


@Override
public void addBatch() throws SQLException {
throw new SQLFeatureNotSupportedException("Batching not supported");
Expand Down Expand Up @@ -327,29 +335,30 @@ public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException

@Override
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException {
setObject(parameterIndex, x, TypeUtils.asSqlType(targetSqlType), scaleOrLength);
}

@Override
public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
setObject(parameterIndex, x, TypeUtils.of(targetSqlType), targetSqlType.getName());
}

private void setObject(int parameterIndex, Object x, DataType dataType, String typeString) throws SQLException {
checkOpen();

JDBCType targetJDBCType;
try {
// this is also a way to check early for the validity of the desired sql type
targetJDBCType = JDBCType.valueOf(targetSqlType);
} catch (IllegalArgumentException e) {
throw new SQLDataException(e.getMessage());
}

// set the null value on the type and exit
if (x == null) {
setParam(parameterIndex, null, JDBCType.valueOf(targetSqlType));
setParam(parameterIndex, null, dataType);
return;
}

checkKnownUnsupportedTypes(x);
if (x instanceof byte[]) {
if (targetJDBCType != JDBCType.VARBINARY) {
if (dataType != DataType.BINARY) {
throw new SQLFeatureNotSupportedException(
"Conversion from type byte[] to " + targetJDBCType + " not supported");
"Conversion from type [byte[]] to [" + typeString + "] not supported");
}
setParam(parameterIndex, x, JDBCType.VARBINARY);
setParam(parameterIndex, x, DataType.BINARY);
return;
}

Expand All @@ -360,7 +369,7 @@ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale
|| x instanceof Time
|| x instanceof java.util.Date)
{
if (targetJDBCType == JDBCType.TIMESTAMP) {
if (dataType == DataType.DATE) {
// converting to {@code java.util.Date} because this is the type supported by {@code XContentBuilder} for serialization
java.util.Date dateToSet;
if (x instanceof Timestamp) {
Expand All @@ -381,15 +390,15 @@ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale
dateToSet = (java.util.Date) x;
}

setParam(parameterIndex, dateToSet, JDBCType.TIMESTAMP);
setParam(parameterIndex, dateToSet, dataType);
return;
} else if (targetJDBCType == JDBCType.VARCHAR) {
setParam(parameterIndex, String.valueOf(x), JDBCType.VARCHAR);
} else if (TypeUtils.isString(dataType)) {
setParam(parameterIndex, String.valueOf(x), dataType);
return;
}
// anything else other than VARCHAR and TIMESTAMP is not supported in this JDBC driver
throw new SQLFeatureNotSupportedException(
"Conversion from type " + x.getClass().getName() + " to " + targetJDBCType + " not supported");
"Conversion from type [" + x.getClass().getName() + "] to [" + typeString + "] not supported");
}

if (x instanceof Boolean
Expand All @@ -401,13 +410,13 @@ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale
|| x instanceof Double
|| x instanceof String) {
setParam(parameterIndex,
TypeConverter.convert(x, TypeConverter.fromJavaToJDBC(x.getClass()), DataType.fromJdbcTypeToJava(targetJDBCType)),
JDBCType.valueOf(targetSqlType));
TypeConverter.convert(x, TypeUtils.of(x.getClass()), (Class<?>) TypeUtils.classOf(dataType), typeString),
dataType);
return;
}

throw new SQLFeatureNotSupportedException(
"Conversion from type " + x.getClass().getName() + " to " + targetJDBCType + " not supported");
"Conversion from type [" + x.getClass().getName() + "] to [" + typeString + "] not supported");
}

private void checkKnownUnsupportedTypes(Object x) throws SQLFeatureNotSupportedException {
Expand All @@ -417,7 +426,7 @@ private void checkKnownUnsupportedTypes(Object x) throws SQLFeatureNotSupportedE

for (Class<?> clazz:unsupportedTypes) {
if (clazz.isAssignableFrom(x.getClass())) {
throw new SQLFeatureNotSupportedException("Objects of type " + clazz.getName() + " are not supported");
throw new SQLFeatureNotSupportedException("Objects of type [" + clazz.getName() + "] are not supported");
}
}
}
Expand Down