Skip to content

Commit f0a3d32

Browse files
authored
SQL: Introduce INTERVAL support (#35521)
Introduce INTERVAL as a DataType Add INTERVAL to the grammar which supports the standard SQL declaration (without precision): > INTERVAL '1 23:45:01.123456789' DAY TO SECOND but also number for single unit intervals: > INTERVAL 1 YEAR as well as the plurals of the units: > INTERVAL 2 YEARS Interval are internally supported as just another Literal being backed by java.time.Period and java.time.Duration Move JDBC away from JDBCType enum to SQLType interface Refactor DataType by moving it into server core and adding dedicated (and much simpler) JDBC driver type Improve internal JDBC conversion by normalizing on the DataType Rename JDBC columnInfo to JdbcColumnInfo to differentiate between it and the SQL ColumnInfo Fix #29990
1 parent bdf632b commit f0a3d32

File tree

95 files changed

+5538
-2755
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+5538
-2755
lines changed

x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
import org.elasticsearch.xpack.sql.client.Version;
1010
import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException;
1111
import org.elasticsearch.xpack.sql.jdbc.net.client.Cursor;
12-
import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo;
12+
import org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcColumnInfo;
13+
import org.elasticsearch.xpack.sql.jdbc.type.DataType;
1314

1415
import java.sql.Connection;
1516
import java.sql.DatabaseMetaData;
@@ -19,12 +20,12 @@
1920
import java.sql.RowIdLifetime;
2021
import java.sql.SQLException;
2122
import java.sql.SQLFeatureNotSupportedException;
22-
import java.sql.SQLType;
2323
import java.util.ArrayList;
2424
import java.util.List;
2525

2626
import static java.sql.JDBCType.INTEGER;
2727
import static java.sql.JDBCType.SMALLINT;
28+
import static org.elasticsearch.xpack.sql.client.StringUtils.EMPTY;
2829

2930
/**
3031
* Implementation of {@link DatabaseMetaData} for Elasticsearch. Draws inspiration
@@ -175,7 +176,7 @@ public String getIdentifierQuoteString() throws SQLException {
175176
@Override
176177
public String getSQLKeywords() throws SQLException {
177178
// TODO: sync this with the grammar
178-
return "";
179+
return EMPTY;
179180
}
180181

181182
@Override
@@ -212,7 +213,7 @@ public String getStringFunctions() throws SQLException {
212213
@Override
213214
public String getSystemFunctions() throws SQLException {
214215
// TODO: sync this with the grammar
215-
return "";
216+
return EMPTY;
216217
}
217218

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

236237
@Override
237238
public String getExtraNameCharacters() throws SQLException {
238-
return "";
239+
return EMPTY;
239240
}
240241

241242
@Override
@@ -716,15 +717,15 @@ private String defaultCatalog() throws SQLException {
716717
private boolean isDefaultCatalog(String catalog) throws SQLException {
717718
// null means catalog info is irrelevant
718719
// % means return all catalogs
719-
// "" means return those without a catalog
720-
return catalog == null || catalog.equals("") || catalog.equals("%") || catalog.equals(defaultCatalog());
720+
// EMPTY means return those without a catalog
721+
return catalog == null || catalog.equals(EMPTY) || catalog.equals("%") || catalog.equals(defaultCatalog());
721722
}
722723

723724
private boolean isDefaultSchema(String schema) {
724725
// null means schema info is irrelevant
725726
// % means return all schemas`
726-
// "" means return those without a schema
727-
return schema == null || schema.equals("") || schema.equals("%");
727+
// EMPTY means return those without a schema
728+
return schema == null || schema.equals(EMPTY) || schema.equals("%");
728729
}
729730

730731
@Override
@@ -756,21 +757,21 @@ public ResultSet getTables(String catalog, String schemaPattern, String tableNam
756757

757758
@Override
758759
public ResultSet getSchemas() throws SQLException {
759-
Object[][] data = { { "", defaultCatalog() } };
760+
Object[][] data = { { EMPTY, defaultCatalog() } };
760761
return memorySet(con.cfg, columnInfo("SCHEMATA",
761762
"TABLE_SCHEM",
762763
"TABLE_CATALOG"), data);
763764
}
764765

765766
@Override
766767
public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
767-
List<ColumnInfo> info = columnInfo("SCHEMATA",
768+
List<JdbcColumnInfo> info = columnInfo("SCHEMATA",
768769
"TABLE_SCHEM",
769770
"TABLE_CATALOG");
770771
if (!isDefaultCatalog(catalog) || !isDefaultSchema(schemaPattern)) {
771772
return emptySet(con.cfg, info);
772773
}
773-
Object[][] data = { { "", defaultCatalog() } };
774+
Object[][] data = { { EMPTY, defaultCatalog() } };
774775
return memorySet(con.cfg, info, data);
775776
}
776777

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

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

11231124
for (int i = 0; i < cols.length; i++) {
11241125
Object obj = cols[i];
11251126
if (obj instanceof String) {
11261127
String name = obj.toString();
1127-
SQLType type = JDBCType.VARCHAR;
1128+
DataType type = DataType.KEYWORD;
11281129
if (i + 1 < cols.length) {
1130+
Object next = cols[i + 1];
11291131
// check if the next item it's a type
1130-
if (cols[i + 1] instanceof SQLType) {
1131-
type = (SQLType) cols[i + 1];
1132-
i++;
1132+
if (next instanceof DataType || next instanceof JDBCType) {
1133+
try {
1134+
type = TypeUtils.of((JDBCType) next);
1135+
i++;
1136+
} catch (SQLException ex) {
1137+
throw new JdbcSQLException(ex, "Invalid metadata schema definition");
1138+
}
11331139
}
11341140
// it's not, use the default and move on
11351141
}
1136-
columns.add(new ColumnInfo(name, type, tableName, "INFORMATION_SCHEMA", "", "", 0));
1142+
columns.add(new JdbcColumnInfo(name, type, tableName, "INFORMATION_SCHEMA", EMPTY, EMPTY, 0));
11371143
}
11381144
else {
11391145
throw new JdbcSQLException("Invalid metadata schema definition");
@@ -1146,28 +1152,28 @@ private static ResultSet emptySet(JdbcConfiguration cfg, String tableName, Objec
11461152
return new JdbcResultSet(cfg, null, new InMemoryCursor(columnInfo(tableName, cols), null));
11471153
}
11481154

1149-
private static ResultSet emptySet(JdbcConfiguration cfg, List<ColumnInfo> columns) {
1155+
private static ResultSet emptySet(JdbcConfiguration cfg, List<JdbcColumnInfo> columns) {
11501156
return memorySet(cfg, columns, null);
11511157
}
11521158

1153-
private static ResultSet memorySet(JdbcConfiguration cfg, List<ColumnInfo> columns, Object[][] data) {
1159+
private static ResultSet memorySet(JdbcConfiguration cfg, List<JdbcColumnInfo> columns, Object[][] data) {
11541160
return new JdbcResultSet(cfg, null, new InMemoryCursor(columns, data));
11551161
}
11561162

11571163
static class InMemoryCursor implements Cursor {
11581164

1159-
private final List<ColumnInfo> columns;
1165+
private final List<JdbcColumnInfo> columns;
11601166
private final Object[][] data;
11611167

11621168
private int row = -1;
11631169

1164-
InMemoryCursor(List<ColumnInfo> info, Object[][] data) {
1170+
InMemoryCursor(List<JdbcColumnInfo> info, Object[][] data) {
11651171
this.columns = info;
11661172
this.data = data;
11671173
}
11681174

11691175
@Override
1170-
public List<ColumnInfo> columns() {
1176+
public List<JdbcColumnInfo> columns() {
11711177
return columns;
11721178
}
11731179

x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcParameterMetaData.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public int isNullable(int param) throws SQLException {
3232

3333
@Override
3434
public boolean isSigned(int param) throws SQLException {
35-
return TypeConverter.isSigned(paramInfo(param).type);
35+
return TypeUtils.isSigned(paramInfo(param).type);
3636
}
3737

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

5050
@Override
5151
public int getParameterType(int param) throws SQLException {
52-
return paramInfo(param).type.getVendorTypeNumber();
52+
return paramInfo(param).type.getVendorTypeNumber().intValue();
5353
}
5454

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

6060
@Override
6161
public String getParameterClassName(int param) throws SQLException {
62-
return TypeConverter.classNameOf(paramInfo(param).type);
62+
return TypeUtils.classOf(paramInfo(param).type).getName();
6363
}
6464

6565
@Override

x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66
package org.elasticsearch.xpack.sql.jdbc.jdbc;
77

8-
import org.elasticsearch.xpack.sql.type.DataType;
8+
import org.elasticsearch.xpack.sql.jdbc.type.DataType;
99

1010
import java.io.InputStream;
1111
import java.io.Reader;
@@ -15,15 +15,13 @@
1515
import java.sql.Blob;
1616
import java.sql.Clob;
1717
import java.sql.Date;
18-
import java.sql.JDBCType;
1918
import java.sql.NClob;
2019
import java.sql.ParameterMetaData;
2120
import java.sql.PreparedStatement;
2221
import java.sql.Ref;
2322
import java.sql.ResultSet;
2423
import java.sql.ResultSetMetaData;
2524
import java.sql.RowId;
26-
import java.sql.SQLDataException;
2725
import java.sql.SQLException;
2826
import java.sql.SQLFeatureNotSupportedException;
2927
import java.sql.SQLType;
@@ -70,7 +68,11 @@ public int executeUpdate() throws SQLException {
7068
throw new SQLFeatureNotSupportedException("Writes not supported");
7169
}
7270

73-
private void setParam(int parameterIndex, Object value, SQLType type) throws SQLException {
71+
private void setParam(int parameterIndex, Object value, int sqlType) throws SQLException {
72+
setParam(parameterIndex, value, TypeUtils.of(sqlType));
73+
}
74+
75+
private void setParam(int parameterIndex, Object value, DataType type) throws SQLException {
7476
checkOpen();
7577

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

8486
@Override
8587
public void setNull(int parameterIndex, int sqlType) throws SQLException {
86-
setParam(parameterIndex, null, JDBCType.valueOf(sqlType));
88+
setParam(parameterIndex, null, sqlType);
8789
}
8890

8991
@Override
@@ -179,20 +181,26 @@ public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQ
179181
setObject(parameterIndex, x, targetSqlType, 0);
180182
}
181183

184+
@Override
185+
public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException {
186+
setObject(parameterIndex, x, targetSqlType, 0);
187+
}
188+
182189
@Override
183190
public void setObject(int parameterIndex, Object x) throws SQLException {
184191
if (x == null) {
185-
setParam(parameterIndex, null, JDBCType.NULL);
192+
setParam(parameterIndex, null, DataType.NULL);
186193
return;
187194
}
188195

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

203+
196204
@Override
197205
public void addBatch() throws SQLException {
198206
throw new SQLFeatureNotSupportedException("Batching not supported");
@@ -327,29 +335,30 @@ public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException
327335

328336
@Override
329337
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException {
338+
setObject(parameterIndex, x, TypeUtils.asSqlType(targetSqlType), scaleOrLength);
339+
}
340+
341+
@Override
342+
public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
343+
setObject(parameterIndex, x, TypeUtils.of(targetSqlType), targetSqlType.getName());
344+
}
345+
346+
private void setObject(int parameterIndex, Object x, DataType dataType, String typeString) throws SQLException {
330347
checkOpen();
331348

332-
JDBCType targetJDBCType;
333-
try {
334-
// this is also a way to check early for the validity of the desired sql type
335-
targetJDBCType = JDBCType.valueOf(targetSqlType);
336-
} catch (IllegalArgumentException e) {
337-
throw new SQLDataException(e.getMessage());
338-
}
339-
340349
// set the null value on the type and exit
341350
if (x == null) {
342-
setParam(parameterIndex, null, JDBCType.valueOf(targetSqlType));
351+
setParam(parameterIndex, null, dataType);
343352
return;
344353
}
345354

346355
checkKnownUnsupportedTypes(x);
347356
if (x instanceof byte[]) {
348-
if (targetJDBCType != JDBCType.VARBINARY) {
357+
if (dataType != DataType.BINARY) {
349358
throw new SQLFeatureNotSupportedException(
350-
"Conversion from type byte[] to " + targetJDBCType + " not supported");
359+
"Conversion from type [byte[]] to [" + typeString + "] not supported");
351360
}
352-
setParam(parameterIndex, x, JDBCType.VARBINARY);
361+
setParam(parameterIndex, x, DataType.BINARY);
353362
return;
354363
}
355364

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

384-
setParam(parameterIndex, dateToSet, JDBCType.TIMESTAMP);
393+
setParam(parameterIndex, dateToSet, dataType);
385394
return;
386-
} else if (targetJDBCType == JDBCType.VARCHAR) {
387-
setParam(parameterIndex, String.valueOf(x), JDBCType.VARCHAR);
395+
} else if (TypeUtils.isString(dataType)) {
396+
setParam(parameterIndex, String.valueOf(x), dataType);
388397
return;
389398
}
390399
// anything else other than VARCHAR and TIMESTAMP is not supported in this JDBC driver
391400
throw new SQLFeatureNotSupportedException(
392-
"Conversion from type " + x.getClass().getName() + " to " + targetJDBCType + " not supported");
401+
"Conversion from type [" + x.getClass().getName() + "] to [" + typeString + "] not supported");
393402
}
394403

395404
if (x instanceof Boolean
@@ -401,13 +410,13 @@ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale
401410
|| x instanceof Double
402411
|| x instanceof String) {
403412
setParam(parameterIndex,
404-
TypeConverter.convert(x, TypeConverter.fromJavaToJDBC(x.getClass()), DataType.fromJdbcTypeToJava(targetJDBCType)),
405-
JDBCType.valueOf(targetSqlType));
413+
TypeConverter.convert(x, TypeUtils.of(x.getClass()), (Class<?>) TypeUtils.classOf(dataType), typeString),
414+
dataType);
406415
return;
407416
}
408417

409418
throw new SQLFeatureNotSupportedException(
410-
"Conversion from type " + x.getClass().getName() + " to " + targetJDBCType + " not supported");
419+
"Conversion from type [" + x.getClass().getName() + "] to [" + typeString + "] not supported");
411420
}
412421

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

418427
for (Class<?> clazz:unsupportedTypes) {
419428
if (clazz.isAssignableFrom(x.getClass())) {
420-
throw new SQLFeatureNotSupportedException("Objects of type " + clazz.getName() + " are not supported");
429+
throw new SQLFeatureNotSupportedException("Objects of type [" + clazz.getName() + "] are not supported");
421430
}
422431
}
423432
}

0 commit comments

Comments
 (0)