Skip to content

Commit 400db4f

Browse files
authored
JDBC driver prepared statement set* methods (#31494)
Added setObject functionality and tests for it
1 parent a35b534 commit 400db4f

File tree

5 files changed

+837
-49
lines changed

5 files changed

+837
-49
lines changed

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

+174-28
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66
package org.elasticsearch.xpack.sql.jdbc.jdbc;
77

8+
import org.elasticsearch.xpack.sql.type.DataType;
9+
810
import java.io.InputStream;
911
import java.io.Reader;
1012
import java.math.BigDecimal;
@@ -21,13 +23,24 @@
2123
import java.sql.ResultSet;
2224
import java.sql.ResultSetMetaData;
2325
import java.sql.RowId;
26+
import java.sql.SQLDataException;
2427
import java.sql.SQLException;
2528
import java.sql.SQLFeatureNotSupportedException;
2629
import java.sql.SQLXML;
30+
import java.sql.Struct;
2731
import java.sql.Time;
2832
import java.sql.Timestamp;
2933
import java.sql.Types;
34+
import java.time.LocalDate;
35+
import java.time.LocalDateTime;
36+
import java.time.LocalTime;
37+
import java.time.OffsetDateTime;
38+
import java.time.OffsetTime;
39+
import java.util.ArrayList;
40+
import java.util.Arrays;
3041
import java.util.Calendar;
42+
import java.util.List;
43+
import java.util.Locale;
3144

3245
class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement {
3346
final PreparedQuery query;
@@ -74,67 +87,67 @@ public void setNull(int parameterIndex, int sqlType) throws SQLException {
7487

7588
@Override
7689
public void setBoolean(int parameterIndex, boolean x) throws SQLException {
77-
setParam(parameterIndex, x, Types.BOOLEAN);
90+
setObject(parameterIndex, x, Types.BOOLEAN);
7891
}
7992

8093
@Override
8194
public void setByte(int parameterIndex, byte x) throws SQLException {
82-
setParam(parameterIndex, x, Types.TINYINT);
95+
setObject(parameterIndex, x, Types.TINYINT);
8396
}
8497

8598
@Override
8699
public void setShort(int parameterIndex, short x) throws SQLException {
87-
setParam(parameterIndex, x, Types.SMALLINT);
100+
setObject(parameterIndex, x, Types.SMALLINT);
88101
}
89102

90103
@Override
91104
public void setInt(int parameterIndex, int x) throws SQLException {
92-
setParam(parameterIndex, x, Types.INTEGER);
105+
setObject(parameterIndex, x, Types.INTEGER);
93106
}
94107

95108
@Override
96109
public void setLong(int parameterIndex, long x) throws SQLException {
97-
setParam(parameterIndex, x, Types.BIGINT);
110+
setObject(parameterIndex, x, Types.BIGINT);
98111
}
99112

100113
@Override
101114
public void setFloat(int parameterIndex, float x) throws SQLException {
102-
setParam(parameterIndex, x, Types.REAL);
115+
setObject(parameterIndex, x, Types.REAL);
103116
}
104117

105118
@Override
106119
public void setDouble(int parameterIndex, double x) throws SQLException {
107-
setParam(parameterIndex, x, Types.DOUBLE);
120+
setObject(parameterIndex, x, Types.DOUBLE);
108121
}
109122

110123
@Override
111124
public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
112-
throw new SQLFeatureNotSupportedException("BigDecimal not supported");
125+
setObject(parameterIndex, x, Types.BIGINT);
113126
}
114127

115128
@Override
116129
public void setString(int parameterIndex, String x) throws SQLException {
117-
setParam(parameterIndex, x, Types.VARCHAR);
130+
setObject(parameterIndex, x, Types.VARCHAR);
118131
}
119132

120133
@Override
121134
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
122-
throw new UnsupportedOperationException("Bytes not implemented yet");
135+
setObject(parameterIndex, x, Types.VARBINARY);
123136
}
124137

125138
@Override
126139
public void setDate(int parameterIndex, Date x) throws SQLException {
127-
throw new UnsupportedOperationException("Date/Time not implemented yet");
140+
setObject(parameterIndex, x, Types.TIMESTAMP);
128141
}
129142

130143
@Override
131144
public void setTime(int parameterIndex, Time x) throws SQLException {
132-
throw new UnsupportedOperationException("Date/Time not implemented yet");
145+
setObject(parameterIndex, x, Types.TIMESTAMP);
133146
}
134147

135148
@Override
136149
public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
137-
throw new UnsupportedOperationException("Date/Time not implemented yet");
150+
setObject(parameterIndex, x, Types.TIMESTAMP);
138151
}
139152

140153
@Override
@@ -161,12 +174,22 @@ public void clearParameters() throws SQLException {
161174

162175
@Override
163176
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
164-
throw new UnsupportedOperationException("Object not implemented yet");
177+
// the value of scaleOrLength parameter doesn't matter, as it's not used in the called method below
178+
setObject(parameterIndex, x, targetSqlType, 0);
165179
}
166180

167181
@Override
168182
public void setObject(int parameterIndex, Object x) throws SQLException {
169-
throw new SQLFeatureNotSupportedException("CharacterStream not supported");
183+
if (x == null) {
184+
setParam(parameterIndex, null, Types.NULL);
185+
return;
186+
}
187+
188+
// check also here the unsupported types so that any unsupported interfaces ({@code java.sql.Struct},
189+
// {@code java.sql.Array} etc) will generate the correct exception message. Otherwise, the method call
190+
// {@code TypeConverter.fromJavaToJDBC(x.getClass())} will report the implementing class as not being supported.
191+
checkKnownUnsupportedTypes(x);
192+
setObject(parameterIndex, x, TypeConverter.fromJavaToJDBC(x.getClass()).getVendorTypeNumber(), 0);
170193
}
171194

172195
@Override
@@ -181,22 +204,22 @@ public void setCharacterStream(int parameterIndex, Reader reader, int length) th
181204

182205
@Override
183206
public void setRef(int parameterIndex, Ref x) throws SQLException {
184-
throw new SQLFeatureNotSupportedException("Ref not supported");
207+
setObject(parameterIndex, x);
185208
}
186209

187210
@Override
188211
public void setBlob(int parameterIndex, Blob x) throws SQLException {
189-
throw new SQLFeatureNotSupportedException("Blob not supported");
212+
setObject(parameterIndex, x);
190213
}
191214

192215
@Override
193216
public void setClob(int parameterIndex, Clob x) throws SQLException {
194-
throw new SQLFeatureNotSupportedException("Clob not supported");
217+
setObject(parameterIndex, x);
195218
}
196219

197220
@Override
198221
public void setArray(int parameterIndex, Array x) throws SQLException {
199-
throw new SQLFeatureNotSupportedException("Array not supported");
222+
setObject(parameterIndex, x);
200223
}
201224

202225
@Override
@@ -206,17 +229,44 @@ public ResultSetMetaData getMetaData() throws SQLException {
206229

207230
@Override
208231
public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException {
209-
throw new UnsupportedOperationException("Dates not implemented yet");
232+
if (cal == null) {
233+
setObject(parameterIndex, x, Types.TIMESTAMP);
234+
return;
235+
}
236+
if (x == null) {
237+
setNull(parameterIndex, Types.TIMESTAMP);
238+
return;
239+
}
240+
// converting to UTC since this is what ES is storing internally
241+
setObject(parameterIndex, new Date(TypeConverter.convertFromCalendarToUTC(x.getTime(), cal)), Types.TIMESTAMP);
210242
}
211243

212244
@Override
213245
public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {
214-
throw new UnsupportedOperationException("Dates not implemented yet");
246+
if (cal == null) {
247+
setObject(parameterIndex, x, Types.TIMESTAMP);
248+
return;
249+
}
250+
if (x == null) {
251+
setNull(parameterIndex, Types.TIMESTAMP);
252+
return;
253+
}
254+
// converting to UTC since this is what ES is storing internally
255+
setObject(parameterIndex, new Time(TypeConverter.convertFromCalendarToUTC(x.getTime(), cal)), Types.TIMESTAMP);
215256
}
216257

217258
@Override
218259
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
219-
throw new UnsupportedOperationException("Dates not implemented yet");
260+
if (cal == null) {
261+
setObject(parameterIndex, x, Types.TIMESTAMP);
262+
return;
263+
}
264+
if (x == null) {
265+
setNull(parameterIndex, Types.TIMESTAMP);
266+
return;
267+
}
268+
// converting to UTC since this is what ES is storing internally
269+
setObject(parameterIndex, new Timestamp(TypeConverter.convertFromCalendarToUTC(x.getTime(), cal)), Types.TIMESTAMP);
220270
}
221271

222272
@Override
@@ -226,7 +276,7 @@ public void setNull(int parameterIndex, int sqlType, String typeName) throws SQL
226276

227277
@Override
228278
public void setURL(int parameterIndex, URL x) throws SQLException {
229-
throw new SQLFeatureNotSupportedException("Datalink not supported");
279+
setObject(parameterIndex, x);
230280
}
231281

232282
@Override
@@ -236,7 +286,7 @@ public ParameterMetaData getParameterMetaData() throws SQLException {
236286

237287
@Override
238288
public void setRowId(int parameterIndex, RowId x) throws SQLException {
239-
throw new SQLFeatureNotSupportedException("RowId not supported");
289+
setObject(parameterIndex, x);
240290
}
241291

242292
@Override
@@ -251,7 +301,7 @@ public void setNCharacterStream(int parameterIndex, Reader value, long length) t
251301

252302
@Override
253303
public void setNClob(int parameterIndex, NClob value) throws SQLException {
254-
throw new SQLFeatureNotSupportedException("NClob not supported");
304+
setObject(parameterIndex, value);
255305
}
256306

257307
@Override
@@ -271,12 +321,108 @@ public void setNClob(int parameterIndex, Reader reader, long length) throws SQLE
271321

272322
@Override
273323
public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
274-
throw new SQLFeatureNotSupportedException("SQLXML not supported");
324+
setObject(parameterIndex, xmlObject);
275325
}
276-
326+
277327
@Override
278328
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException {
279-
throw new UnsupportedOperationException("Object not implemented yet");
329+
checkOpen();
330+
331+
JDBCType targetJDBCType;
332+
try {
333+
// this is also a way to check early for the validity of the desired sql type
334+
targetJDBCType = JDBCType.valueOf(targetSqlType);
335+
} catch (IllegalArgumentException e) {
336+
throw new SQLDataException(e.getMessage());
337+
}
338+
339+
// set the null value on the type and exit
340+
if (x == null) {
341+
setParam(parameterIndex, null, targetSqlType);
342+
return;
343+
}
344+
345+
checkKnownUnsupportedTypes(x);
346+
if (x instanceof byte[]) {
347+
if (targetJDBCType != JDBCType.VARBINARY) {
348+
throw new SQLFeatureNotSupportedException(
349+
"Conversion from type byte[] to " + targetJDBCType + " not supported");
350+
}
351+
setParam(parameterIndex, x, Types.VARBINARY);
352+
return;
353+
}
354+
355+
if (x instanceof Timestamp
356+
|| x instanceof Calendar
357+
|| x instanceof Date
358+
|| x instanceof LocalDateTime
359+
|| x instanceof Time
360+
|| x instanceof java.util.Date)
361+
{
362+
if (targetJDBCType == JDBCType.TIMESTAMP) {
363+
// converting to {@code java.util.Date} because this is the type supported by {@code XContentBuilder} for serialization
364+
java.util.Date dateToSet;
365+
if (x instanceof Timestamp) {
366+
dateToSet = new java.util.Date(((Timestamp) x).getTime());
367+
} else if (x instanceof Calendar) {
368+
dateToSet = ((Calendar) x).getTime();
369+
} else if (x instanceof Date) {
370+
dateToSet = new java.util.Date(((Date) x).getTime());
371+
} else if (x instanceof LocalDateTime){
372+
LocalDateTime ldt = (LocalDateTime) x;
373+
Calendar cal = getDefaultCalendar();
374+
cal.set(ldt.getYear(), ldt.getMonthValue() - 1, ldt.getDayOfMonth(), ldt.getHour(), ldt.getMinute(), ldt.getSecond());
375+
376+
dateToSet = cal.getTime();
377+
} else if (x instanceof Time) {
378+
dateToSet = new java.util.Date(((Time) x).getTime());
379+
} else {
380+
dateToSet = (java.util.Date) x;
381+
}
382+
383+
setParam(parameterIndex, dateToSet, Types.TIMESTAMP);
384+
return;
385+
} else if (targetJDBCType == JDBCType.VARCHAR) {
386+
setParam(parameterIndex, String.valueOf(x), Types.VARCHAR);
387+
return;
388+
}
389+
// anything else other than VARCHAR and TIMESTAMP is not supported in this JDBC driver
390+
throw new SQLFeatureNotSupportedException(
391+
"Conversion from type " + x.getClass().getName() + " to " + targetJDBCType + " not supported");
392+
}
393+
394+
if (x instanceof Boolean
395+
|| x instanceof Byte
396+
|| x instanceof Short
397+
|| x instanceof Integer
398+
|| x instanceof Long
399+
|| x instanceof Float
400+
|| x instanceof Double
401+
|| x instanceof String) {
402+
setParam(parameterIndex,
403+
TypeConverter.convert(x, TypeConverter.fromJavaToJDBC(x.getClass()), DataType.fromJdbcTypeToJava(targetJDBCType)),
404+
targetSqlType);
405+
return;
406+
}
407+
408+
throw new SQLFeatureNotSupportedException(
409+
"Conversion from type " + x.getClass().getName() + " to " + targetJDBCType + " not supported");
410+
}
411+
412+
private void checkKnownUnsupportedTypes(Object x) throws SQLFeatureNotSupportedException {
413+
List<Class<?>> unsupportedTypes = new ArrayList<Class<?>>(Arrays.asList(Struct.class, Array.class, SQLXML.class,
414+
RowId.class, Ref.class, Blob.class, NClob.class, Clob.class, LocalDate.class, LocalTime.class,
415+
OffsetTime.class, OffsetDateTime.class, URL.class, BigDecimal.class));
416+
417+
for (Class<?> clazz:unsupportedTypes) {
418+
if (clazz.isAssignableFrom(x.getClass())) {
419+
throw new SQLFeatureNotSupportedException("Objects of type " + clazz.getName() + " are not supported");
420+
}
421+
}
422+
}
423+
424+
private Calendar getDefaultCalendar() {
425+
return Calendar.getInstance(cfg.timeZone(), Locale.ROOT);
280426
}
281427

282428
@Override

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

-8
Original file line numberDiff line numberDiff line change
@@ -359,14 +359,6 @@ private <T> T convert(int columnIndex, Class<T> type) throws SQLException {
359359
return null;
360360
}
361361

362-
if (type != null && type.isInstance(val)) {
363-
try {
364-
return type.cast(val);
365-
} catch (ClassCastException cce) {
366-
throw new SQLException("unable to convert column " + columnIndex + " to " + type, cce);
367-
}
368-
}
369-
370362
JDBCType columnType = cursor.columns().get(columnIndex - 1).type;
371363

372364
return TypeConverter.convert(val, columnType, type);

0 commit comments

Comments
 (0)