55 */
66package org .elasticsearch .xpack .sql .jdbc .jdbc ;
77
8+ import org .elasticsearch .xpack .sql .type .DataType ;
9+
810import java .io .InputStream ;
911import java .io .Reader ;
1012import java .math .BigDecimal ;
2123import java .sql .ResultSet ;
2224import java .sql .ResultSetMetaData ;
2325import java .sql .RowId ;
26+ import java .sql .SQLDataException ;
2427import java .sql .SQLException ;
2528import java .sql .SQLFeatureNotSupportedException ;
2629import java .sql .SQLXML ;
30+ import java .sql .Struct ;
2731import java .sql .Time ;
2832import java .sql .Timestamp ;
2933import 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 ;
3041import java .util .Calendar ;
42+ import java .util .List ;
43+ import java .util .Locale ;
3144
3245class 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
0 commit comments