Mercurial > hg > monetdb-java
comparison src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java @ 391:f523727db392
Moved Java classes from packages starting with nl.cwi.monetdb.* to package org.monetdb.*
This naming complies to the Java Package Naming convention as MonetDB's main website is www.monetdb.org.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 12 Nov 2020 22:02:01 +0100 (2020-11-12) |
parents | src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java@4f54264f29d7 |
children | bf9f6b6ecf40 |
comparison
equal
deleted
inserted
replaced
390:6199e0be3c6e | 391:f523727db392 |
---|---|
1 /* | |
2 * This Source Code Form is subject to the terms of the Mozilla Public | |
3 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 * | |
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V. | |
7 */ | |
8 | |
9 package org.monetdb.jdbc; | |
10 | |
11 import java.io.InputStream; | |
12 import java.io.IOException; | |
13 import java.io.Reader; | |
14 import java.math.BigDecimal; | |
15 import java.math.BigInteger; | |
16 import java.net.URL; | |
17 import java.nio.CharBuffer; | |
18 import java.sql.Array; | |
19 import java.sql.Blob; | |
20 import java.sql.Clob; | |
21 import java.sql.NClob; | |
22 import java.sql.ParameterMetaData; | |
23 import java.sql.PreparedStatement; | |
24 import java.sql.Ref; | |
25 import java.sql.ResultSet; | |
26 import java.sql.ResultSetMetaData; | |
27 import java.sql.RowId; | |
28 import java.sql.SQLData; | |
29 import java.sql.SQLDataException; | |
30 import java.sql.SQLException; | |
31 import java.sql.SQLFeatureNotSupportedException; | |
32 import java.sql.SQLOutput; | |
33 import java.sql.SQLType; // new as of Java 1.8 | |
34 import java.sql.SQLXML; | |
35 import java.sql.Struct; | |
36 import java.sql.Time; | |
37 import java.sql.Timestamp; | |
38 import java.sql.Types; | |
39 import java.text.SimpleDateFormat; | |
40 import java.util.Calendar; | |
41 import java.util.Map; | |
42 | |
43 /** | |
44 * A {@link PreparedStatement} suitable for the MonetDB database. | |
45 * | |
46 * This implementation of the PreparedStatement interface uses the | |
47 * capabilities of the MonetDB/SQL backend to prepare and execute | |
48 * statements. The backend takes care of finding the '?'s in the input and | |
49 * returns the types it expects for them. | |
50 * | |
51 * An example of a server response on a prepare query is: | |
52 * <pre> | |
53 * % prepare select name from tables where id > ? and id < ?; | |
54 * &5 0 2 3 2 | |
55 * # prepare, prepare, prepare # table_name | |
56 * # type, digits, scale # name | |
57 * # varchar, int, int # type | |
58 * # 0, 0, 0 # length | |
59 * [ "int", 9, 0 ] | |
60 * [ "int", 9, 0 ] | |
61 * </pre> | |
62 * | |
63 * @author Fabian Groffen | |
64 * @author Martin van Dinther | |
65 * @version 0.6 | |
66 */ | |
67 public class MonetPreparedStatement | |
68 extends MonetStatement | |
69 implements PreparedStatement, AutoCloseable | |
70 { | |
71 private final String[] monetdbType; | |
72 private final int[] javaType; | |
73 private final int[] digits; | |
74 private final int[] scale; | |
75 private final String[] schema; | |
76 private final String[] table; | |
77 private final String[] column; | |
78 private final int id; | |
79 private final int size; | |
80 private final int rscolcnt; | |
81 | |
82 private final String[] values; | |
83 | |
84 /* placeholders for date/time pattern formats created once (only when needed), used multiple times */ | |
85 /** Format of a timestamp with RFC822 time zone */ | |
86 private SimpleDateFormat mTimestampZ; | |
87 /** Format of a timestamp */ | |
88 private SimpleDateFormat mTimestamp; | |
89 /** Format of a time with RFC822 time zone */ | |
90 private SimpleDateFormat mTimeZ; | |
91 /** Format of a time */ | |
92 private SimpleDateFormat mTime; | |
93 /** Format of a date used by mserver */ | |
94 private SimpleDateFormat mDate; | |
95 | |
96 /** | |
97 * MonetPreparedStatement constructor which checks the arguments for | |
98 * validity. A MonetPreparedStatement is backed by a | |
99 * {@link MonetStatement}, which deals with most of the required stuff of | |
100 * this class. | |
101 * | |
102 * @param connection the connection that created this Statement | |
103 * @param resultSetType type of {@link ResultSet} to produce | |
104 * @param resultSetConcurrency concurrency of ResultSet to produce | |
105 * @param prepareQuery the query string to prepare | |
106 * @throws SQLException if an error occurs during login | |
107 * @throws IllegalArgumentException is one of the arguments is null or empty | |
108 */ | |
109 MonetPreparedStatement( | |
110 final MonetConnection connection, | |
111 final int resultSetType, | |
112 final int resultSetConcurrency, | |
113 final int resultSetHoldability, | |
114 final String prepareQuery) | |
115 throws SQLException, IllegalArgumentException | |
116 { | |
117 super( | |
118 connection, | |
119 resultSetType, | |
120 resultSetConcurrency, | |
121 resultSetHoldability | |
122 ); | |
123 | |
124 if (!super.execute("PREPARE " + prepareQuery)) | |
125 throw new SQLException("Unexpected server response", "M0M10"); | |
126 | |
127 // cheat a bit to get the ID and the number of columns | |
128 id = ((MonetConnection.ResultSetResponse)header).id; | |
129 size = (int)((MonetConnection.ResultSetResponse)header).tuplecount; | |
130 rscolcnt = ((MonetConnection.ResultSetResponse)header).columncount; | |
131 | |
132 // initialise blank finals | |
133 monetdbType = new String[size]; | |
134 javaType = new int[size]; | |
135 digits = new int[size]; | |
136 scale = new int[size]; | |
137 schema = new String[size]; | |
138 table = new String[size]; | |
139 column = new String[size]; | |
140 values = new String[size]; | |
141 | |
142 // fill the arrays | |
143 final ResultSet rs = super.getResultSet(); | |
144 if (rs != null) { | |
145 // System.out.println("After super.getResultSet();"); | |
146 final int type_colnr = rs.findColumn("type"); | |
147 final int digits_colnr = rs.findColumn("digits"); | |
148 final int scale_colnr = rs.findColumn("scale"); | |
149 final int schema_colnr = rs.findColumn("schema"); | |
150 final int table_colnr = rs.findColumn("table"); | |
151 final int column_colnr = rs.findColumn("column"); | |
152 for (int i = 0; rs.next(); i++) { | |
153 monetdbType[i] = rs.getString(type_colnr); | |
154 javaType[i] = MonetDriver.getJdbcSQLType(monetdbType[i]); | |
155 if (javaType[i] == Types.CLOB) { | |
156 if (connection.mapClobAsVarChar()) | |
157 javaType[i] = Types.VARCHAR; | |
158 } else | |
159 if (javaType[i] == Types.BLOB) { | |
160 if (connection.mapBlobAsVarBinary()) | |
161 javaType[i] = Types.VARBINARY; | |
162 } | |
163 digits[i] = rs.getInt(digits_colnr); | |
164 scale[i] = rs.getInt(scale_colnr); | |
165 if (rscolcnt == 3) | |
166 continue; | |
167 schema[i] = rs.getString(schema_colnr); | |
168 table[i] = rs.getString(table_colnr); | |
169 column[i] = rs.getString(column_colnr); | |
170 /* when column[i] != null it is a result column of the prepared query, see getColumnIdx(int), | |
171 when column[i] == null it is a parameter for the prepared statement, see getParamIdx(int). */ | |
172 // System.out.println("column " + i + " has value: " + column[i]); | |
173 } | |
174 rs.close(); | |
175 } | |
176 | |
177 // PreparedStatements are by default poolable | |
178 poolable = true; | |
179 } | |
180 | |
181 /** | |
182 * Constructs an empty MonetPreparedStatement. This constructor is | |
183 * in particular useful for extensions of this class. | |
184 * | |
185 * @param connection the connection that created this Statement | |
186 * @param resultSetType type of ResultSet to produce | |
187 * @param resultSetConcurrency concurrency of ResultSet to produce | |
188 * @throws SQLException if an error occurs during login | |
189 */ | |
190 /* Disabled this constructor code as it is not part of the JDBC interface | |
191 It may be enabled again when a subclass is constructed which needs it. | |
192 MonetPreparedStatement( | |
193 MonetConnection connection, | |
194 int resultSetType, | |
195 int resultSetConcurrency, | |
196 int resultSetHoldability) | |
197 throws SQLException | |
198 { | |
199 super( | |
200 connection, | |
201 resultSetType, | |
202 resultSetConcurrency, | |
203 resultSetHoldability | |
204 ); | |
205 // initialise blank finals | |
206 monetdbType = null; | |
207 javaType = null; | |
208 digits = null; | |
209 scale = null; | |
210 schema = null; | |
211 table = null; | |
212 column = null; | |
213 values = null; | |
214 id = -1; | |
215 size = -1; | |
216 rscolcnt = -1; | |
217 } | |
218 */ | |
219 | |
220 //== methods interface PreparedStatement | |
221 | |
222 /** | |
223 * Adds a set of parameters to this PreparedStatement object's batch | |
224 * of commands. | |
225 * | |
226 * @throws SQLException if a database access error occurs | |
227 */ | |
228 @Override | |
229 public void addBatch() throws SQLException { | |
230 super.addBatch(transform()); | |
231 } | |
232 | |
233 /** override the addBatch from the Statement to throw an SQLException */ | |
234 @Override | |
235 public void addBatch(final String q) throws SQLException { | |
236 throw new SQLException("This method is not available in a PreparedStatement!", "M1M05"); | |
237 } | |
238 | |
239 /** | |
240 * Clears the current parameter values immediately. | |
241 * | |
242 * In general, parameter values remain in force for repeated use of a | |
243 * statement. Setting a parameter value automatically clears its previous | |
244 * value. However, in some cases it is useful to immediately release the | |
245 * resources used by the current parameter values; this can be done by | |
246 * calling the method clearParameters. | |
247 */ | |
248 @Override | |
249 public void clearParameters() { | |
250 for (int i = 0; i < size; i++) { | |
251 values[i] = null; | |
252 } | |
253 } | |
254 | |
255 /** | |
256 * Executes the SQL statement in this PreparedStatement object, | |
257 * which may be any kind of SQL statement. Some prepared statements | |
258 * return multiple results; the execute method handles these complex | |
259 * statements as well as the simpler form of statements handled by | |
260 * the methods executeQuery and executeUpdate. | |
261 * | |
262 * The execute method returns a boolean to indicate the form of the | |
263 * first result. You must call either the method getResultSet or | |
264 * getUpdateCount to retrieve the result; you must call | |
265 * getMoreResults to move to any subsequent result(s). | |
266 * | |
267 * @return true if the first result is a ResultSet object; false if the | |
268 * first result is an update count or there is no result | |
269 * @throws SQLException if a database access error occurs or an argument | |
270 * is supplied to this method | |
271 */ | |
272 @Override | |
273 public boolean execute() throws SQLException { | |
274 return super.execute(transform()); | |
275 } | |
276 | |
277 /** override the execute from the Statement to throw an SQLException */ | |
278 @Override | |
279 public boolean execute(final String q) throws SQLException { | |
280 throw new SQLException("This method is not available in a PreparedStatement!", "M1M05"); | |
281 } | |
282 | |
283 /** | |
284 * Executes the SQL query in this PreparedStatement object and returns the | |
285 * ResultSet object generated by the query. | |
286 * | |
287 * @return a ResultSet object that contains the data produced by the query; | |
288 * never null | |
289 * @throws SQLException if a database access error occurs or the SQL | |
290 * statement does not return a ResultSet object | |
291 */ | |
292 @Override | |
293 public ResultSet executeQuery() throws SQLException { | |
294 if (execute() != true) | |
295 throw new SQLException("Query did not produce a result set", "M1M19"); | |
296 | |
297 return getResultSet(); | |
298 } | |
299 | |
300 /** override the executeQuery from the Statement to throw an SQLException */ | |
301 @Override | |
302 public ResultSet executeQuery(final String q) throws SQLException { | |
303 throw new SQLException("This method is not available in a PreparedStatement!", "M1M05"); | |
304 } | |
305 | |
306 /** | |
307 * Executes the SQL statement in this PreparedStatement object, which must | |
308 * be an SQL INSERT, UPDATE or DELETE statement; or an SQL statement that | |
309 * returns nothing, such as a DDL statement. | |
310 * | |
311 * @return either (1) the row count for INSERT, UPDATE, or DELETE | |
312 * statements or (2) 0 for SQL statements that return nothing | |
313 * @throws SQLException if a database access error occurs or the SQL | |
314 * statement returns a ResultSet object | |
315 */ | |
316 @Override | |
317 public int executeUpdate() throws SQLException { | |
318 if (execute() != false) | |
319 throw new SQLException("Query produced a result set", "M1M17"); | |
320 | |
321 return getUpdateCount(); | |
322 } | |
323 | |
324 /** override the executeUpdate from the Statement to throw an SQLException */ | |
325 @Override | |
326 public int executeUpdate(final String q) throws SQLException { | |
327 throw new SQLException("This method is not available in a PreparedStatement!", "M1M05"); | |
328 } | |
329 | |
330 /** | |
331 * Returns the index (0..size-1) in the backing arrays for the given | |
332 * resultset column number or an SQLException when not found | |
333 */ | |
334 private final int getColumnIdx(final int colnr) throws SQLException { | |
335 int curcol = 0; | |
336 for (int i = 0; i < size; i++) { | |
337 /* when column[i] == null it is a parameter, when column[i] != null it is a result column of the prepared query */ | |
338 if (column[i] == null) | |
339 continue; | |
340 curcol++; | |
341 if (curcol == colnr) | |
342 return i; | |
343 } | |
344 throw new SQLException("No such column with index: " + colnr, "M1M05"); | |
345 } | |
346 /** | |
347 * Returns the index (0..size-1) in the backing arrays for the given | |
348 * parameter number or an SQLException when not found | |
349 */ | |
350 private final int getParamIdx(final int paramnr) throws SQLException { | |
351 int curparam = 0; | |
352 for (int i = 0; i < size; i++) { | |
353 /* when column[i] == null it is a parameter, when column[i] != null it is a result column of the prepared query */ | |
354 if (column[i] != null) | |
355 continue; | |
356 curparam++; | |
357 if (curparam == paramnr) | |
358 return i; | |
359 } | |
360 throw new SQLException("No such parameter with index: " + paramnr, "M1M05"); | |
361 } | |
362 | |
363 | |
364 /* helper for the anonymous class inside getMetaData */ | |
365 private abstract class rsmdw extends MonetWrapper implements ResultSetMetaData {} | |
366 /** | |
367 * Retrieves a ResultSetMetaData object that contains information | |
368 * about the columns of the ResultSet object that will be returned | |
369 * when this PreparedStatement object is executed. | |
370 * | |
371 * Because a PreparedStatement object is precompiled, it is possible | |
372 * to know about the ResultSet object that it will return without | |
373 * having to execute it. Consequently, it is possible to invoke the | |
374 * method getMetaData on a PreparedStatement object rather than | |
375 * waiting to execute it and then invoking the ResultSet.getMetaData | |
376 * method on the ResultSet object that is returned. | |
377 * | |
378 * @return the description of a ResultSet object's columns or null if the | |
379 * driver cannot return a ResultSetMetaData object | |
380 * @throws SQLException if a database access error occurs | |
381 */ | |
382 @Override | |
383 public ResultSetMetaData getMetaData() throws SQLException { | |
384 if (rscolcnt == 3) | |
385 return null; // not sufficient data with pre-Dec2011 PREPARE | |
386 | |
387 // return inner class which implements the ResultSetMetaData interface | |
388 return new rsmdw() { | |
389 /** | |
390 * Returns the number of columns in this ResultSet object. | |
391 * | |
392 * @return the number of columns | |
393 */ | |
394 @Override | |
395 public int getColumnCount() { | |
396 int cnt = 0; | |
397 | |
398 for (int i = 0; i < size; i++) { | |
399 if (column[i] != null) | |
400 cnt++; | |
401 } | |
402 return cnt; | |
403 } | |
404 | |
405 /** | |
406 * Indicates whether the designated column is automatically numbered. | |
407 * | |
408 * @param column the first column is 1, the second is 2, ... | |
409 * @return true if so; false otherwise | |
410 * @throws SQLException if a database access error occurs | |
411 */ | |
412 @Override | |
413 public boolean isAutoIncrement(final int column) throws SQLException { | |
414 /* TODO: in MonetDB only numeric (int, decimal) columns could be autoincrement/serial | |
415 * This however requires an expensive dbmd.getColumns(null, schema, table, column) | |
416 * query call to pull the IS_AUTOINCREMENT value for this column. | |
417 * See also ResultSetMetaData.isAutoIncrement() | |
418 */ | |
419 // For now we simply allways return false. | |
420 return false; | |
421 } | |
422 | |
423 /** | |
424 * Indicates whether a column's case matters. | |
425 * | |
426 * @param column the first column is 1, the second is 2, ... | |
427 * @return false | |
428 */ | |
429 @Override | |
430 public boolean isCaseSensitive(final int column) throws SQLException { | |
431 switch (getColumnType(column)) { | |
432 case Types.CHAR: | |
433 case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness | |
434 case Types.CLOB: | |
435 return true; | |
436 case Types.VARCHAR: | |
437 final String monettype = getColumnTypeName(column); | |
438 if (monettype != null) { | |
439 // data of type inet or uuid is not case sensitive | |
440 if ("inet".equals(monettype) | |
441 || "uuid".equals(monettype)) | |
442 return false; | |
443 } | |
444 return true; | |
445 } | |
446 | |
447 return false; | |
448 } | |
449 | |
450 /** | |
451 * Indicates whether the designated column can be used in a | |
452 * where clause. | |
453 * | |
454 * Returning true for all here, even for CLOB, BLOB. | |
455 * | |
456 * @param column the first column is 1, the second is 2, ... | |
457 * @return true | |
458 */ | |
459 @Override | |
460 public boolean isSearchable(final int column) { | |
461 return true; | |
462 } | |
463 | |
464 /** | |
465 * Indicates whether the designated column is a cash value. | |
466 * From the MonetDB database perspective it is by definition | |
467 * unknown whether the value is a currency, because there are | |
468 * no currency datatypes such as MONEY. With this knowledge | |
469 * we can always return false here. | |
470 * | |
471 * @param column the first column is 1, the second is 2, ... | |
472 * @return false | |
473 */ | |
474 @Override | |
475 public boolean isCurrency(final int column) { | |
476 return false; | |
477 } | |
478 | |
479 /** | |
480 * Indicates whether values in the designated column are signed | |
481 * numbers. | |
482 * Within MonetDB all numeric types (except oid and ptr) are signed. | |
483 * | |
484 * @param column the first column is 1, the second is 2, ... | |
485 * @return true if so; false otherwise | |
486 */ | |
487 @Override | |
488 public boolean isSigned(final int column) throws SQLException { | |
489 // we can hardcode this, based on the colum type | |
490 switch (getColumnType(column)) { | |
491 case Types.NUMERIC: | |
492 case Types.DECIMAL: | |
493 case Types.TINYINT: | |
494 case Types.SMALLINT: | |
495 case Types.INTEGER: | |
496 case Types.REAL: | |
497 case Types.FLOAT: | |
498 case Types.DOUBLE: | |
499 return true; | |
500 case Types.BIGINT: | |
501 final String monettype = getColumnTypeName(column); | |
502 if (monettype != null) { | |
503 if ("oid".equals(monettype) | |
504 || "ptr".equals(monettype)) | |
505 return false; | |
506 } | |
507 return true; | |
508 // All other types should return false | |
509 // case Types.BOOLEAN: | |
510 // case Types.DATE: | |
511 // case Types.TIME: | |
512 // case Types.TIME_WITH_TIMEZONE: | |
513 // case Types.TIMESTAMP: | |
514 // case Types.TIMESTAMP_WITH_TIMEZONE: | |
515 default: | |
516 return false; | |
517 } | |
518 } | |
519 | |
520 /** | |
521 * Indicates the designated column's normal maximum width in | |
522 * characters. | |
523 * | |
524 * @param column the first column is 1, the second is 2, ... | |
525 * @return the normal maximum number of characters allowed as the | |
526 * width of the designated column | |
527 * @throws SQLException if there is no such column | |
528 */ | |
529 @Override | |
530 public int getColumnDisplaySize(final int column) throws SQLException { | |
531 try { | |
532 return digits[getColumnIdx(column)]; | |
533 } catch (IndexOutOfBoundsException e) { | |
534 throw MonetResultSet.newSQLInvalidColumnIndexException(column); | |
535 } | |
536 } | |
537 | |
538 /** | |
539 * Get the designated column's table's schema. | |
540 * | |
541 * @param column the first column is 1, the second is 2, ... | |
542 * @return schema name or "" if not applicable | |
543 * @throws SQLException if a database access error occurs | |
544 */ | |
545 @Override | |
546 public String getSchemaName(final int column) throws SQLException { | |
547 try { | |
548 return schema[getColumnIdx(column)]; | |
549 } catch (IndexOutOfBoundsException e) { | |
550 throw MonetResultSet.newSQLInvalidColumnIndexException(column); | |
551 } | |
552 } | |
553 | |
554 /** | |
555 * Gets the designated column's table name. | |
556 * | |
557 * @param column the first column is 1, the second is 2, ... | |
558 * @return table name or "" if not applicable | |
559 */ | |
560 @Override | |
561 public String getTableName(final int column) throws SQLException { | |
562 try { | |
563 return table[getColumnIdx(column)]; | |
564 } catch (IndexOutOfBoundsException e) { | |
565 throw MonetResultSet.newSQLInvalidColumnIndexException(column); | |
566 } | |
567 } | |
568 | |
569 /** | |
570 * Get the designated column's number of decimal digits. | |
571 * This method is currently very expensive as it needs to | |
572 * retrieve the information from the database using an SQL | |
573 * query. | |
574 * | |
575 * @param column the first column is 1, the second is 2, ... | |
576 * @return precision | |
577 * @throws SQLException if a database access error occurs | |
578 */ | |
579 @Override | |
580 public int getPrecision(final int column) throws SQLException { | |
581 try { | |
582 return digits[getColumnIdx(column)]; | |
583 } catch (IndexOutOfBoundsException e) { | |
584 throw MonetResultSet.newSQLInvalidColumnIndexException(column); | |
585 } | |
586 } | |
587 | |
588 /** | |
589 * Gets the designated column's number of digits to right of | |
590 * the decimal point. This method is currently very | |
591 * expensive as it needs to retrieve the information from | |
592 * the database using an SQL query. | |
593 * | |
594 * @param column the first column is 1, the second is 2, ... | |
595 * @return scale | |
596 * @throws SQLException if a database access error occurs | |
597 */ | |
598 @Override | |
599 public int getScale(final int column) throws SQLException { | |
600 try { | |
601 return scale[getColumnIdx(column)]; | |
602 } catch (IndexOutOfBoundsException e) { | |
603 throw MonetResultSet.newSQLInvalidColumnIndexException(column); | |
604 } | |
605 } | |
606 | |
607 /** | |
608 * Indicates the nullability of values in the designated | |
609 * column. This method is currently very expensive as it | |
610 * needs to retrieve the information from the database using | |
611 * an SQL query. | |
612 * | |
613 * @param column the first column is 1, the second is 2, ... | |
614 * @return nullability | |
615 * @throws SQLException if a database access error occurs | |
616 */ | |
617 @Override | |
618 public int isNullable(final int column) throws SQLException { | |
619 return columnNullableUnknown; | |
620 } | |
621 | |
622 /** | |
623 * Gets the designated column's table's catalog name. | |
624 * MonetDB does not support the catalog naming concept as in: catalog.schema.table naming scheme | |
625 * | |
626 * @param column the first column is 1, the second is 2, ... | |
627 * @return the name of the catalog for the table in which the given | |
628 * column appears or "" if not applicable | |
629 */ | |
630 @Override | |
631 public String getCatalogName(final int column) throws SQLException { | |
632 return null; // MonetDB does NOT support catalogs | |
633 } | |
634 | |
635 /** | |
636 * Indicates whether the designated column is definitely not | |
637 * writable. MonetDB does not support cursor updates, so | |
638 * nothing is writable. | |
639 * | |
640 * @param column the first column is 1, the second is 2, ... | |
641 * @return true if so; false otherwise | |
642 */ | |
643 @Override | |
644 public boolean isReadOnly(final int column) { | |
645 return true; | |
646 } | |
647 | |
648 /** | |
649 * Indicates whether it is possible for a write on the | |
650 * designated column to succeed. | |
651 * | |
652 * @param column the first column is 1, the second is 2, ... | |
653 * @return true if so; false otherwise | |
654 */ | |
655 @Override | |
656 public boolean isWritable(final int column) { | |
657 return false; | |
658 } | |
659 | |
660 /** | |
661 * Indicates whether a write on the designated column will | |
662 * definitely succeed. | |
663 * | |
664 * @param column the first column is 1, the second is 2, ... | |
665 * @return true if so; false otherwise | |
666 */ | |
667 @Override | |
668 public boolean isDefinitelyWritable(final int column) { | |
669 return false; | |
670 } | |
671 | |
672 /** | |
673 * Returns the fully-qualified name of the Java class whose | |
674 * instances are manufactured if the method | |
675 * ResultSet.getObject is called to retrieve a value from | |
676 * the column. ResultSet.getObject may return a subclass of | |
677 * the class returned by this method. | |
678 * | |
679 * @param column the first column is 1, the second is 2, ... | |
680 * @return the fully-qualified name of the class in the Java | |
681 * programming language that would be used by the method | |
682 * ResultSet.getObject to retrieve the value in the | |
683 * specified column. This is the class name used for custom | |
684 * mapping. | |
685 * @throws SQLException if there is no such column | |
686 */ | |
687 @Override | |
688 public String getColumnClassName(final int column) throws SQLException { | |
689 final String typeName = getColumnTypeName(column); | |
690 final Map<String,Class<?>> map = getConnection().getTypeMap(); | |
691 final Class<?> c; | |
692 if (map.containsKey(typeName)) { | |
693 c = (Class)map.get(typeName); | |
694 } else { | |
695 c = MonetResultSet.getClassForType(getColumnType(column)); | |
696 } | |
697 return c.getName(); | |
698 } | |
699 | |
700 /** | |
701 * Gets the designated column's suggested title for use in | |
702 * printouts and displays. This is currently equal to | |
703 * getColumnName(). | |
704 * | |
705 * @param column the first column is 1, the second is 2, ... | |
706 * @return the suggested column title | |
707 * @throws SQLException if there is no such column | |
708 */ | |
709 @Override | |
710 public String getColumnLabel(final int column) throws SQLException { | |
711 return getColumnName(column); | |
712 } | |
713 | |
714 /** | |
715 * Gets the designated column's name | |
716 * | |
717 * @param column the first column is 1, the second is 2, ... | |
718 * @return the column name | |
719 * @throws SQLException if there is no such column | |
720 */ | |
721 @Override | |
722 public String getColumnName(final int colnr) throws SQLException { | |
723 try { | |
724 return column[getColumnIdx(colnr)]; | |
725 } catch (IndexOutOfBoundsException e) { | |
726 throw MonetResultSet.newSQLInvalidColumnIndexException(colnr); | |
727 } | |
728 } | |
729 | |
730 /** | |
731 * Retrieves the designated column's SQL type. | |
732 * | |
733 * @param column the first column is 1, the second is 2, ... | |
734 * @return SQL type from java.sql.Types | |
735 * @throws SQLException if there is no such column | |
736 */ | |
737 @Override | |
738 public int getColumnType(final int column) throws SQLException { | |
739 try { | |
740 return javaType[getColumnIdx(column)]; | |
741 } catch (IndexOutOfBoundsException e) { | |
742 throw MonetResultSet.newSQLInvalidColumnIndexException(column); | |
743 } | |
744 } | |
745 | |
746 /** | |
747 * Retrieves the designated column's database-specific type name. | |
748 * | |
749 * @param column the first column is 1, the second is 2, ... | |
750 * @return type name used by the database. If the column type is a | |
751 * user-defined type, then a fully-qualified type name is | |
752 * returned. | |
753 * @throws SQLException if there is no such column | |
754 */ | |
755 @Override | |
756 public String getColumnTypeName(final int column) throws SQLException { | |
757 try { | |
758 return monetdbType[getColumnIdx(column)]; | |
759 } catch (IndexOutOfBoundsException e) { | |
760 throw MonetResultSet.newSQLInvalidColumnIndexException(column); | |
761 } | |
762 } | |
763 }; | |
764 } | |
765 | |
766 /* helper class for the anonymous class in getParameterMetaData */ | |
767 private abstract class pmdw extends MonetWrapper implements ParameterMetaData {} | |
768 /** | |
769 * Retrieves the number, types and properties of this | |
770 * PreparedStatement object's parameters. | |
771 * | |
772 * @return a ParameterMetaData object that contains information | |
773 * about the number, types and properties of this | |
774 * PreparedStatement object's parameters | |
775 * @throws SQLException if a database access error occurs | |
776 */ | |
777 @Override | |
778 public ParameterMetaData getParameterMetaData() throws SQLException { | |
779 return new pmdw() { | |
780 /** | |
781 * Retrieves the number of parameters in the | |
782 * PreparedStatement object for which this ParameterMetaData | |
783 * object contains information. | |
784 * | |
785 * @return the number of parameters | |
786 * @throws SQLException if a database access error occurs | |
787 */ | |
788 @Override | |
789 public int getParameterCount() throws SQLException { | |
790 int cnt = 0; | |
791 | |
792 for (int i = 0; i < size; i++) { | |
793 if (column[i] == null) | |
794 cnt++; | |
795 } | |
796 return cnt; | |
797 } | |
798 | |
799 /** | |
800 * Retrieves whether null values are allowed in the | |
801 * designated parameter. | |
802 * | |
803 * This is currently always unknown for MonetDB/SQL. | |
804 * | |
805 * @param param the first parameter is 1, the second is 2, ... | |
806 * @return the nullability status of the given parameter; | |
807 * one of ParameterMetaData.parameterNoNulls, | |
808 * ParameterMetaData.parameterNullable, or | |
809 * ParameterMetaData.parameterNullableUnknown | |
810 * @throws SQLException if a database access error occurs | |
811 */ | |
812 @Override | |
813 public int isNullable(final int param) throws SQLException { | |
814 return ParameterMetaData.parameterNullableUnknown; | |
815 } | |
816 | |
817 /** | |
818 * Retrieves whether values for the designated parameter can | |
819 * be signed numbers. | |
820 * | |
821 * @param param the first parameter is 1, the second is 2, ... | |
822 * @return true if so; false otherwise | |
823 * @throws SQLException if a database access error occurs | |
824 */ | |
825 @Override | |
826 public boolean isSigned(final int param) throws SQLException { | |
827 // we can hardcode this, based on the colum type | |
828 switch (getParameterType(param)) { | |
829 case Types.NUMERIC: | |
830 case Types.DECIMAL: | |
831 case Types.TINYINT: | |
832 case Types.SMALLINT: | |
833 case Types.INTEGER: | |
834 case Types.REAL: | |
835 case Types.FLOAT: | |
836 case Types.DOUBLE: | |
837 return true; | |
838 case Types.BIGINT: | |
839 final String monettype = getParameterTypeName(param); | |
840 if (monettype != null) { | |
841 if ("oid".equals(monettype) | |
842 || "ptr".equals(monettype)) | |
843 return false; | |
844 } | |
845 return true; | |
846 // All other types should return false | |
847 // case Types.BOOLEAN: | |
848 // case Types.DATE: | |
849 // case Types.TIME: | |
850 // case Types.TIME_WITH_TIMEZONE: | |
851 // case Types.TIMESTAMP: | |
852 // case Types.TIMESTAMP_WITH_TIMEZONE: | |
853 default: | |
854 return false; | |
855 } | |
856 } | |
857 | |
858 /** | |
859 * Retrieves the designated parameter's number of decimal | |
860 * digits. | |
861 * | |
862 * @param param the first parameter is 1, the second is 2, ... | |
863 * @return precision | |
864 * @throws SQLException if a database access error occurs | |
865 */ | |
866 @Override | |
867 public int getPrecision(final int param) throws SQLException { | |
868 try { | |
869 return digits[getParamIdx(param)]; | |
870 } catch (IndexOutOfBoundsException e) { | |
871 throw newSQLInvalidParameterIndexException(param); | |
872 } | |
873 } | |
874 | |
875 /** | |
876 * Retrieves the designated parameter's number of digits to | |
877 * right of the decimal point. | |
878 * | |
879 * @param param the first parameter is 1, the second is 2, ... | |
880 * @return scale | |
881 * @throws SQLException if a database access error occurs | |
882 */ | |
883 @Override | |
884 public int getScale(final int param) throws SQLException { | |
885 try { | |
886 return scale[getParamIdx(param)]; | |
887 } catch (IndexOutOfBoundsException e) { | |
888 throw newSQLInvalidParameterIndexException(param); | |
889 } | |
890 } | |
891 | |
892 /** | |
893 * Retrieves the designated parameter's SQL type. | |
894 * | |
895 * @param param the first parameter is 1, the second is 2, ... | |
896 * @return SQL type from java.sql.Types | |
897 * @throws SQLException if a database access error occurs | |
898 */ | |
899 @Override | |
900 public int getParameterType(final int param) throws SQLException { | |
901 try { | |
902 return javaType[getParamIdx(param)]; | |
903 } catch (IndexOutOfBoundsException e) { | |
904 throw newSQLInvalidParameterIndexException(param); | |
905 } | |
906 } | |
907 | |
908 /** | |
909 * Retrieves the designated parameter's database-specific | |
910 * type name. | |
911 * | |
912 * @param param the first parameter is 1, the second is 2, ... | |
913 * @return type the name used by the database. If the | |
914 * parameter type is a user-defined type, then a | |
915 * fully-qualified type name is returned. | |
916 * @throws SQLException if a database access error occurs | |
917 */ | |
918 @Override | |
919 public String getParameterTypeName(final int param) throws SQLException { | |
920 try { | |
921 return monetdbType[getParamIdx(param)]; | |
922 } catch (IndexOutOfBoundsException e) { | |
923 throw newSQLInvalidParameterIndexException(param); | |
924 } | |
925 } | |
926 | |
927 /** | |
928 * Retrieves the fully-qualified name of the Java class | |
929 * whose instances should be passed to the method | |
930 * PreparedStatement.setObject. | |
931 * | |
932 * @param param the first parameter is 1, the second is 2, ... | |
933 * @return the fully-qualified name of the class in the Java | |
934 * programming language that would be used by the | |
935 * method PreparedStatement.setObject to set the | |
936 * value in the specified parameter. This is the | |
937 * class name used for custom mapping. | |
938 * @throws SQLException if a database access error occurs | |
939 */ | |
940 @Override | |
941 public String getParameterClassName(final int param) throws SQLException { | |
942 final String typeName = getParameterTypeName(param); | |
943 final Map<String,Class<?>> map = getConnection().getTypeMap(); | |
944 final Class<?> c; | |
945 if (map.containsKey(typeName)) { | |
946 c = (Class)map.get(typeName); | |
947 } else { | |
948 c = MonetResultSet.getClassForType(getParameterType(param)); | |
949 } | |
950 return c.getName(); | |
951 } | |
952 | |
953 /** | |
954 * Retrieves the designated parameter's mode. | |
955 * For MonetDB/SQL we currently only support INput parameters. | |
956 * | |
957 * @param param - the first parameter is 1, the second is 2, ... | |
958 * @return mode of the parameter; one of | |
959 * ParameterMetaData.parameterModeIn, | |
960 * ParameterMetaData.parameterModeOut, or | |
961 * ParameterMetaData.parameterModeInOut | |
962 * ParameterMetaData.parameterModeUnknown. | |
963 * @throws SQLException if a database access error occurs | |
964 */ | |
965 @Override | |
966 public int getParameterMode(final int param) throws SQLException { | |
967 return ParameterMetaData.parameterModeIn; | |
968 } | |
969 }; | |
970 } | |
971 | |
972 /** | |
973 * Sets the designated parameter to the given Array object. The | |
974 * driver converts this to an SQL ARRAY value when it sends it to | |
975 * the database. | |
976 * | |
977 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
978 * @param x an Array object that maps an SQL ARRAY value | |
979 * @throws SQLException if a database access error occurs | |
980 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
981 * not support this method | |
982 */ | |
983 @Override | |
984 public void setArray(final int parameterIndex, final Array x) throws SQLException { | |
985 throw newSQLFeatureNotSupportedException("setArray"); | |
986 } | |
987 | |
988 /** | |
989 * Sets the designated parameter to the given input stream, which will have | |
990 * the specified number of bytes. When a very large ASCII value is input to | |
991 * a LONGVARCHAR parameter, it may be more practical to send it via a | |
992 * java.io.InputStream. Data will be read from the stream as needed until | |
993 * end-of-file is reached. The JDBC driver will do any necessary conversion | |
994 * from ASCII to the database char format. | |
995 * | |
996 * Note: This stream object can either be a standard Java stream object or | |
997 * your own subclass that implements the standard interface. | |
998 * | |
999 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1000 * @param x the Java input stream that contains the ASCII parameter value | |
1001 * @throws SQLException if a database access error occurs | |
1002 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1003 * not support this method | |
1004 */ | |
1005 @Override | |
1006 public void setAsciiStream(final int parameterIndex, final InputStream x) | |
1007 throws SQLException | |
1008 { | |
1009 throw newSQLFeatureNotSupportedException("setAsciiStream"); | |
1010 } | |
1011 | |
1012 /** | |
1013 * Sets the designated parameter to the given input stream, which will have | |
1014 * the specified number of bytes. When a very large ASCII value is input to | |
1015 * a LONGVARCHAR parameter, it may be more practical to send it via a | |
1016 * java.io.InputStream. Data will be read from the stream as needed until | |
1017 * end-of-file is reached. The JDBC driver will do any necessary conversion | |
1018 * from ASCII to the database char format. | |
1019 * | |
1020 * Note: This stream object can either be a standard Java stream object or | |
1021 * your own subclass that implements the standard interface. | |
1022 * | |
1023 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1024 * @param x the Java input stream that contains the ASCII parameter value | |
1025 * @param length the number of bytes in the stream | |
1026 * @throws SQLException if a database access error occurs | |
1027 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1028 * not support this method | |
1029 */ | |
1030 @Override | |
1031 public void setAsciiStream(final int parameterIndex, final InputStream x, final int length) | |
1032 throws SQLException | |
1033 { | |
1034 throw newSQLFeatureNotSupportedException("setAsciiStream"); | |
1035 } | |
1036 | |
1037 /** | |
1038 * Sets the designated parameter to the given input stream, which | |
1039 * will have the specified number of bytes. When a very large ASCII | |
1040 * value is input to a LONGVARCHAR parameter, it may be more | |
1041 * practical to send it via a java.io.InputStream. Data will be read | |
1042 * from the stream as needed until end-of-file is reached. The JDBC | |
1043 * driver will do any necessary conversion from ASCII to the | |
1044 * database char format. | |
1045 * | |
1046 * Note: This stream object can either be a standard Java stream object or | |
1047 * your own subclass that implements the standard interface. | |
1048 * | |
1049 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1050 * @param x the Java input stream that contains the ASCII parameter value | |
1051 * @param length the number of bytes in the stream | |
1052 * @throws SQLException if a database access error occurs | |
1053 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1054 * not support this method | |
1055 */ | |
1056 @Override | |
1057 public void setAsciiStream(final int parameterIndex, final InputStream x, final long length) | |
1058 throws SQLException | |
1059 { | |
1060 throw newSQLFeatureNotSupportedException("setAsciiStream"); | |
1061 } | |
1062 | |
1063 /** | |
1064 * Sets the designated parameter to the given java.math.BigDecimal value. | |
1065 * The driver converts this to an SQL NUMERIC value when it sends it to the | |
1066 * database. | |
1067 * | |
1068 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1069 * @param x the parameter value | |
1070 * @throws SQLException if a database access error occurs | |
1071 */ | |
1072 @Override | |
1073 public void setBigDecimal(final int parameterIndex, BigDecimal x) throws SQLException { | |
1074 // get array position | |
1075 final int i = getParamIdx(parameterIndex); | |
1076 | |
1077 // round to the scale of the DB: | |
1078 x = x.setScale(scale[i], java.math.RoundingMode.HALF_UP); | |
1079 | |
1080 // if precision is now greater than that of the db, throw an error: | |
1081 if (x.precision() > digits[i]) { | |
1082 throw new SQLDataException("DECIMAL value exceeds allowed digits/scale: " + x.toPlainString() + " (" + digits[i] + "/" + scale[i] + ")", "22003"); | |
1083 } | |
1084 | |
1085 // MonetDB doesn't like leading 0's, since it counts them as part of | |
1086 // the precision, so let's strip them off. (But be careful not to do | |
1087 // this to the exact number "0".) Also strip off trailing | |
1088 // numbers that are inherent to the double representation. | |
1089 String xStr = x.toPlainString(); | |
1090 final int dot = xStr.indexOf('.'); | |
1091 if (dot >= 0) | |
1092 xStr = xStr.substring(0, Math.min(xStr.length(), dot + 1 + scale[i])); | |
1093 while (xStr.startsWith("0") && xStr.length() > 1) | |
1094 xStr = xStr.substring(1); | |
1095 setValue(parameterIndex, xStr); | |
1096 } | |
1097 | |
1098 /** | |
1099 * Sets the designated parameter to the given input stream, which will have | |
1100 * the specified number of bytes. When a very large binary value is input | |
1101 * to a LONGVARBINARY parameter, it may be more practical to send it via a | |
1102 * java.io.InputStream object. The data will be read from the stream as | |
1103 * needed until end-of-file is reached. | |
1104 * | |
1105 * Note: This stream object can either be a standard Java stream object or | |
1106 * your own subclass that implements the standard interface. | |
1107 * | |
1108 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1109 * @param x the java input stream which contains the binary parameter value | |
1110 * @throws SQLException if a database access error occurs | |
1111 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1112 * not support this method | |
1113 */ | |
1114 @Override | |
1115 public void setBinaryStream(final int parameterIndex, final InputStream x) | |
1116 throws SQLException | |
1117 { | |
1118 throw newSQLFeatureNotSupportedException("setBinaryStream"); | |
1119 } | |
1120 | |
1121 /** | |
1122 * Sets the designated parameter to the given input stream, which will have | |
1123 * the specified number of bytes. When a very large binary value is input | |
1124 * to a LONGVARBINARY parameter, it may be more practical to send it via a | |
1125 * java.io.InputStream object. The data will be read from the stream as | |
1126 * needed until end-of-file is reached. | |
1127 * | |
1128 * Note: This stream object can either be a standard Java stream object or | |
1129 * your own subclass that implements the standard interface. | |
1130 * | |
1131 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1132 * @param x the java input stream which contains the binary parameter value | |
1133 * @param length the number of bytes in the stream | |
1134 * @throws SQLException if a database access error occurs | |
1135 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1136 * not support this method | |
1137 */ | |
1138 @Override | |
1139 public void setBinaryStream(final int parameterIndex, final InputStream x, final int length) | |
1140 throws SQLException | |
1141 { | |
1142 throw newSQLFeatureNotSupportedException("setBinaryStream"); | |
1143 } | |
1144 | |
1145 /** | |
1146 * Sets the designated parameter to the given input stream, which will have | |
1147 * the specified number of bytes. When a very large binary value is input | |
1148 * to a LONGVARBINARY parameter, it may be more practical to send it via a | |
1149 * java.io.InputStream object. The data will be read from the stream as | |
1150 * needed until end-of-file is reached. | |
1151 * | |
1152 * Note: This stream object can either be a standard Java stream object or | |
1153 * your own subclass that implements the standard interface. | |
1154 * | |
1155 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1156 * @param x the java input stream which contains the binary parameter value | |
1157 * @param length the number of bytes in the stream | |
1158 * @throws SQLException if a database access error occurs | |
1159 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1160 * not support this method | |
1161 */ | |
1162 @Override | |
1163 public void setBinaryStream(final int parameterIndex, final InputStream x, final long length) | |
1164 throws SQLException | |
1165 { | |
1166 throw newSQLFeatureNotSupportedException("setBinaryStream"); | |
1167 } | |
1168 | |
1169 /** | |
1170 * Sets the designated parameter to the given Blob object. The driver | |
1171 * converts this to an SQL BLOB value when it sends it to the database. | |
1172 * | |
1173 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1174 * @param x a Blob object that maps an SQL BLOB value | |
1175 * @throws SQLException if a database access error occurs | |
1176 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1177 * not support this method | |
1178 */ | |
1179 @Override | |
1180 public void setBlob(final int parameterIndex, final InputStream x) throws SQLException { | |
1181 throw newSQLFeatureNotSupportedException("setBlob"); | |
1182 } | |
1183 | |
1184 /** | |
1185 * Sets the designated parameter to the given Blob object. The driver | |
1186 * converts this to an SQL BLOB value when it sends it to the database. | |
1187 * | |
1188 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1189 * @param x a Blob object that maps an SQL BLOB value | |
1190 * @throws SQLException if a database access error occurs | |
1191 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1192 * not support this method | |
1193 */ | |
1194 @Override | |
1195 public void setBlob(final int parameterIndex, final Blob x) throws SQLException { | |
1196 throw newSQLFeatureNotSupportedException("setBlob"); | |
1197 } | |
1198 | |
1199 /** | |
1200 * Sets the designated parameter to a InputStream object. The | |
1201 * inputstream must contain the number of characters specified by | |
1202 * length otherwise a SQLException will be generated when the | |
1203 * PreparedStatement is executed. This method differs from the | |
1204 * setBinaryStream (int, InputStream, int) method because it informs | |
1205 * the driver that the parameter value should be sent to the server | |
1206 * as a BLOB. When the setBinaryStream method is used, the driver | |
1207 * may have to do extra work to determine whether the parameter data | |
1208 * should be sent to the server as a LONGVARBINARY or a BLOB. | |
1209 * | |
1210 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1211 * @param is an object that contains the data to set the parameter | |
1212 * value to | |
1213 * @param length the number of bytes in the parameter data | |
1214 * @throws SQLException if a database access error occurs | |
1215 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1216 * not support this method | |
1217 */ | |
1218 @Override | |
1219 public void setBlob(final int parameterIndex, final InputStream is, final long length) throws SQLException { | |
1220 throw newSQLFeatureNotSupportedException("setBlob"); | |
1221 } | |
1222 | |
1223 /** | |
1224 * Sets the designated parameter to the given Java boolean value. The | |
1225 * driver converts this to an SQL BIT value when it sends it to the | |
1226 * database. | |
1227 * | |
1228 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1229 * @param x the parameter value | |
1230 * @throws SQLException if a database access error occurs | |
1231 */ | |
1232 @Override | |
1233 public void setBoolean(final int parameterIndex, final boolean x) throws SQLException { | |
1234 setValue(parameterIndex, Boolean.toString(x)); | |
1235 } | |
1236 | |
1237 /** | |
1238 * Sets the designated parameter to the given Java byte value. The driver | |
1239 * converts this to an SQL TINYINT value when it sends it to the database. | |
1240 * | |
1241 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1242 * @param x the parameter value | |
1243 * @throws SQLException if a database access error occurs | |
1244 */ | |
1245 @Override | |
1246 public void setByte(final int parameterIndex, final byte x) throws SQLException { | |
1247 setValue(parameterIndex, Byte.toString(x)); | |
1248 } | |
1249 | |
1250 static final String HEXES = "0123456789ABCDEF"; | |
1251 /** | |
1252 * Sets the designated parameter to the given Java array of bytes. The | |
1253 * driver converts this to an SQL VARBINARY or LONGVARBINARY (depending | |
1254 * on the argument's size relative to the driver's limits on VARBINARY | |
1255 * values) when it sends it to the database. | |
1256 * | |
1257 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1258 * @param x the parameter value | |
1259 * @throws SQLException if a database access error occurs | |
1260 */ | |
1261 @Override | |
1262 public void setBytes(final int parameterIndex, final byte[] x) throws SQLException { | |
1263 if (x == null) { | |
1264 setNull(parameterIndex, -1); | |
1265 return; | |
1266 } | |
1267 | |
1268 final int len = x.length; | |
1269 final StringBuilder hex = new StringBuilder(8 + (len * 2)); | |
1270 hex.append("blob '"); // add a casting prefix | |
1271 // convert the bytes into hex codes | |
1272 for (int i = 0; i < len; i++) { | |
1273 hex.append(HEXES.charAt((x[i] & 0xF0) >> 4)) | |
1274 .append(HEXES.charAt((x[i] & 0x0F))); | |
1275 } | |
1276 hex.append("'"); // end of hex string value | |
1277 setValue(parameterIndex, hex.toString()); | |
1278 } | |
1279 | |
1280 /** | |
1281 * Sets the designated parameter to the given Reader object, which is the | |
1282 * given number of characters long. When a very large UNICODE value is | |
1283 * input to a LONGVARCHAR parameter, it may be more practical to send it | |
1284 * via a java.io.Reader object. The data will be read from the stream as | |
1285 * needed until end-of-file is reached. The JDBC driver will do any | |
1286 * necessary conversion from UNICODE to the database char format. | |
1287 * | |
1288 * Note: This stream object can either be a standard Java stream object or | |
1289 * your own subclass that implements the standard interface. | |
1290 * | |
1291 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1292 * @param reader the java.io.Reader object that contains the Unicode data | |
1293 * @param length the number of characters in the stream | |
1294 * @throws SQLException if a database access error occurs | |
1295 */ | |
1296 @Override | |
1297 public void setCharacterStream(final int parameterIndex, final Reader reader, final int length) | |
1298 throws SQLException | |
1299 { | |
1300 setClob(parameterIndex, reader, (long)length); | |
1301 } | |
1302 | |
1303 /** | |
1304 * Sets the designated parameter to the given Reader object, which is the | |
1305 * given number of characters long. When a very large UNICODE value is | |
1306 * input to a LONGVARCHAR parameter, it may be more practical to send it | |
1307 * via a java.io.Reader object. The data will be read from the stream as | |
1308 * needed until end-of-file is reached. The JDBC driver will do any | |
1309 * necessary conversion from UNICODE to the database char format. | |
1310 * | |
1311 * Note: This stream object can either be a standard Java stream object or | |
1312 * your own subclass that implements the standard interface. | |
1313 * | |
1314 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1315 * @param reader the java.io.Reader object that contains the Unicode data | |
1316 * @throws SQLException if a database access error occurs | |
1317 */ | |
1318 @Override | |
1319 public void setCharacterStream(final int parameterIndex, final Reader reader) | |
1320 throws SQLException | |
1321 { | |
1322 setClob(parameterIndex, reader); | |
1323 } | |
1324 | |
1325 /** | |
1326 * Sets the designated parameter to the given Reader object, which is the | |
1327 * given number of characters long. When a very large UNICODE value is | |
1328 * input to a LONGVARCHAR parameter, it may be more practical to send it | |
1329 * via a java.io.Reader object. The data will be read from the stream as | |
1330 * needed until end-of-file is reached. The JDBC driver will do any | |
1331 * necessary conversion from UNICODE to the database char format. | |
1332 * | |
1333 * Note: This stream object can either be a standard Java stream object or | |
1334 * your own subclass that implements the standard interface. | |
1335 * | |
1336 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1337 * @param reader the java.io.Reader object that contains the Unicode data | |
1338 * @param length the number of characters in the stream | |
1339 * @throws SQLException if a database access error occurs | |
1340 */ | |
1341 @Override | |
1342 public void setCharacterStream(final int parameterIndex, final Reader reader, final long length) | |
1343 throws SQLException | |
1344 { | |
1345 setClob(parameterIndex, reader, length); | |
1346 } | |
1347 | |
1348 /** | |
1349 * Sets the designated parameter to the given Clob object. The driver | |
1350 * converts this to an SQL CLOB value when it sends it to the database. | |
1351 * | |
1352 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1353 * @param x a Clob object that maps an SQL CLOB value | |
1354 * @throws SQLException if a database access error occurs | |
1355 */ | |
1356 @Override | |
1357 public void setClob(final int parameterIndex, final Clob x) throws SQLException { | |
1358 if (x == null) { | |
1359 setNull(parameterIndex, -1); | |
1360 return; | |
1361 } | |
1362 | |
1363 // simply serialise the CLOB into a String for now... far from | |
1364 // efficient, but might work for a few cases... | |
1365 // be on your marks: we have to cast the length down! | |
1366 setString(parameterIndex, x.getSubString(1L, (int)(x.length()))); | |
1367 } | |
1368 | |
1369 /** | |
1370 * Sets the designated parameter to the given Clob object. The driver | |
1371 * converts this to an SQL CLOB value when it sends it to the database. | |
1372 * | |
1373 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1374 * @param reader an object that contains the data to set the parameter value to | |
1375 * @throws SQLException if a database access error occurs | |
1376 */ | |
1377 @Override | |
1378 public void setClob(final int parameterIndex, final Reader reader) throws SQLException { | |
1379 if (reader == null) { | |
1380 setNull(parameterIndex, -1); | |
1381 return; | |
1382 } | |
1383 | |
1384 // Some buffer. Size of 8192 is default for BufferedReader, so... | |
1385 final int size = 8192; | |
1386 final char[] arr = new char[size]; | |
1387 final StringBuilder buf = new StringBuilder(size * 32); | |
1388 try { | |
1389 int numChars; | |
1390 while ((numChars = reader.read(arr, 0, size)) > 0) { | |
1391 buf.append(arr, 0, numChars); | |
1392 } | |
1393 setString(parameterIndex, buf.toString()); | |
1394 } catch (IOException e) { | |
1395 throw new SQLException("failed to read from stream: " + e.getMessage(), "M1M25"); | |
1396 } | |
1397 } | |
1398 | |
1399 /** | |
1400 * Sets the designated parameter to a Reader object. The reader must | |
1401 * contain the number of characters specified by length otherwise a | |
1402 * SQLException will be generated when the PreparedStatement is | |
1403 * executed. This method differs from the setCharacterStream (int, | |
1404 * Reader, int) method because it informs the driver that the | |
1405 * parameter value should be sent to the server as a CLOB. When the | |
1406 * setCharacterStream method is used, the driver may have to do | |
1407 * extra work to determine whether the parameter data should be sent | |
1408 * to the server as a LONGVARCHAR or a CLOB. | |
1409 * | |
1410 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1411 * @param reader An object that contains the data to set the parameter value to. | |
1412 * @param length the number of characters in the parameter data. | |
1413 * @throws SQLException if a database access error occurs | |
1414 */ | |
1415 @Override | |
1416 public void setClob(final int parameterIndex, final Reader reader, final long length) throws SQLException { | |
1417 if (reader == null) { | |
1418 setNull(parameterIndex, -1); | |
1419 return; | |
1420 } | |
1421 if (length < 0 || length > Integer.MAX_VALUE) { | |
1422 throw new SQLException("Invalid length value: " + length, "M1M05"); | |
1423 } | |
1424 | |
1425 // simply serialise the Reader data into a large buffer | |
1426 final CharBuffer buf = CharBuffer.allocate((int)length); // have to down cast | |
1427 try { | |
1428 reader.read(buf); | |
1429 // We have to rewind the buffer, because otherwise toString() returns "". | |
1430 buf.rewind(); | |
1431 setString(parameterIndex, buf.toString()); | |
1432 } catch (IOException e) { | |
1433 throw new SQLException("failed to read from stream: " + e.getMessage(), "M1M25"); | |
1434 } | |
1435 } | |
1436 | |
1437 /** | |
1438 * Sets the designated parameter to the given java.sql.Date value. The | |
1439 * driver converts this to an SQL DATE value when it sends it to the | |
1440 * database. | |
1441 * | |
1442 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1443 * @param x the parameter value | |
1444 * @throws SQLException if a database access error occurs | |
1445 */ | |
1446 @Override | |
1447 public void setDate(final int parameterIndex, final java.sql.Date x) | |
1448 throws SQLException | |
1449 { | |
1450 setDate(parameterIndex, x, null); | |
1451 } | |
1452 | |
1453 /** | |
1454 * Sets the designated parameter to the given java.sql.Date value, using | |
1455 * the given Calendar object. The driver uses the Calendar object to | |
1456 * construct an SQL DATE value, which the driver then sends to the | |
1457 * database. With a Calendar object, the driver can calculate the date | |
1458 * taking into account a custom timezone. If no Calendar object is | |
1459 * specified, the driver uses the default timezone, which is that of the | |
1460 * virtual machine running the application. | |
1461 * | |
1462 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1463 * @param x the parameter value | |
1464 * @param cal the Calendar object the driver will use to construct the date | |
1465 * @throws SQLException if a database access error occurs | |
1466 */ | |
1467 @Override | |
1468 public void setDate(final int parameterIndex, final java.sql.Date x, final Calendar cal) | |
1469 throws SQLException | |
1470 { | |
1471 if (x == null) { | |
1472 setNull(parameterIndex, -1); | |
1473 return; | |
1474 } | |
1475 | |
1476 if (cal == null) { | |
1477 setValue(parameterIndex, "date '" + x.toString() + "'"); | |
1478 } else { | |
1479 if (mDate == null) { | |
1480 // first time usage, create and keep the mDate object for next usage | |
1481 mDate = new SimpleDateFormat("yyyy-MM-dd"); | |
1482 } | |
1483 mDate.setTimeZone(cal.getTimeZone()); | |
1484 setValue(parameterIndex, "date '" + mDate.format(x) + "'"); | |
1485 } | |
1486 } | |
1487 | |
1488 /** | |
1489 * Sets the designated parameter to the given Java double value. The driver | |
1490 * converts this to an SQL DOUBLE value when it sends it to the database. | |
1491 * | |
1492 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1493 * @param x the parameter value | |
1494 * @throws SQLException if a database access error occurs | |
1495 */ | |
1496 @Override | |
1497 public void setDouble(final int parameterIndex, final double x) throws SQLException { | |
1498 setValue(parameterIndex, Double.toString(x)); | |
1499 } | |
1500 | |
1501 /** | |
1502 * Sets the designated parameter to the given Java float value. The driver | |
1503 * converts this to an SQL FLOAT value when it sends it to the database. | |
1504 * | |
1505 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1506 * @param x the parameter value | |
1507 * @throws SQLException if a database access error occurs | |
1508 */ | |
1509 @Override | |
1510 public void setFloat(final int parameterIndex, final float x) throws SQLException { | |
1511 setValue(parameterIndex, Float.toString(x)); | |
1512 } | |
1513 | |
1514 /** | |
1515 * Sets the designated parameter to the given Java int value. The driver | |
1516 * converts this to an SQL INTEGER value when it sends it to the database. | |
1517 * | |
1518 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1519 * @param x the parameter value | |
1520 * @throws SQLException if a database access error occurs | |
1521 */ | |
1522 @Override | |
1523 public void setInt(final int parameterIndex, final int x) throws SQLException { | |
1524 setValue(parameterIndex, Integer.toString(x)); | |
1525 } | |
1526 | |
1527 /** | |
1528 * Sets the designated parameter to the given Java long value. The driver | |
1529 * converts this to an SQL BIGINT value when it sends it to the database. | |
1530 * | |
1531 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1532 * @param x the parameter value | |
1533 * @throws SQLException if a database access error occurs | |
1534 */ | |
1535 @Override | |
1536 public void setLong(final int parameterIndex, final long x) throws SQLException { | |
1537 setValue(parameterIndex, Long.toString(x)); | |
1538 } | |
1539 | |
1540 /** | |
1541 * Sets the designated parameter to a Reader object. The Reader | |
1542 * reads the data till end-of-file is reached. The driver does the | |
1543 * necessary conversion from Java character format to the national | |
1544 * character set in the database. | |
1545 * | |
1546 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1547 * @param value the parameter value | |
1548 * @throws SQLException if a database access error occurs | |
1549 */ | |
1550 @Override | |
1551 public void setNCharacterStream(final int parameterIndex, final Reader value) throws SQLException { | |
1552 setCharacterStream(parameterIndex, value); | |
1553 } | |
1554 | |
1555 /** | |
1556 * Sets the designated parameter to a Reader object. The Reader | |
1557 * reads the data till end-of-file is reached. The driver does the | |
1558 * necessary conversion from Java character format to the national | |
1559 * character set in the database. | |
1560 * | |
1561 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1562 * @param value the parameter value | |
1563 * @param length the number of characters in the parameter data. | |
1564 * @throws SQLException if a database access error occurs | |
1565 */ | |
1566 @Override | |
1567 public void setNCharacterStream(final int parameterIndex, final Reader value, final long length) | |
1568 throws SQLException | |
1569 { | |
1570 setCharacterStream(parameterIndex, value, length); | |
1571 } | |
1572 | |
1573 /** | |
1574 * Sets the designated parameter to a java.sql.NClob object. The | |
1575 * driver converts this to a SQL NCLOB value when it sends it to the | |
1576 * database. | |
1577 * | |
1578 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1579 * @param value the parameter value | |
1580 * @throws SQLException if a database access error occurs | |
1581 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1582 * not support this method | |
1583 */ | |
1584 @Override | |
1585 public void setNClob(final int parameterIndex, final Reader value) throws SQLException { | |
1586 throw newSQLFeatureNotSupportedException("setNClob"); | |
1587 } | |
1588 | |
1589 /** | |
1590 * Sets the designated parameter to a java.sql.NClob object. The | |
1591 * driver converts this to a SQL NCLOB value when it sends it to the | |
1592 * database. | |
1593 * | |
1594 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1595 * @param value the parameter value | |
1596 * @throws SQLException if a database access error occurs | |
1597 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1598 * not support this method | |
1599 */ | |
1600 @Override | |
1601 public void setNClob(final int parameterIndex, final NClob value) throws SQLException { | |
1602 throw newSQLFeatureNotSupportedException("setNClob"); | |
1603 } | |
1604 | |
1605 /** | |
1606 * Sets the designated parameter to a Reader object. The reader must | |
1607 * contain the number of characters specified by length otherwise a | |
1608 * SQLException will be generated when the PreparedStatement is | |
1609 * executed. This method differs from the setCharacterStream (int, | |
1610 * Reader, int) method because it informs the driver that the | |
1611 * parameter value should be sent to the server as a NCLOB. When the | |
1612 * setCharacterStream method is used, the driver may have to do | |
1613 * extra work to determine whether the parameter data should be sent | |
1614 * to the server as a LONGNVARCHAR or a NCLOB. | |
1615 * | |
1616 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1617 * @param r An object that contains the data to set the parameter | |
1618 * value to | |
1619 * @param length the number of characters in the parameter data | |
1620 * @throws SQLException if a database access error occurs | |
1621 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1622 * not support this method | |
1623 */ | |
1624 @Override | |
1625 public void setNClob(final int parameterIndex, final Reader r, final long length) throws SQLException { | |
1626 throw newSQLFeatureNotSupportedException("setNClob"); | |
1627 } | |
1628 | |
1629 /** | |
1630 * Sets the designated paramter to the given String object. The | |
1631 * driver converts this to a SQL NCHAR or NVARCHAR or LONGNVARCHAR | |
1632 * value (depending on the argument's size relative to the driver's | |
1633 * limits on NVARCHAR values) when it sends it to the database. | |
1634 * | |
1635 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1636 * @param value the parameter value | |
1637 * @throws SQLException if a database access error occurs | |
1638 */ | |
1639 @Override | |
1640 public void setNString(final int parameterIndex, final String value) throws SQLException { | |
1641 setString(parameterIndex, value); | |
1642 } | |
1643 | |
1644 /** | |
1645 * Sets the designated parameter to SQL NULL. | |
1646 * | |
1647 * Note: You must specify the parameter's SQL type. | |
1648 * | |
1649 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1650 * @param sqlType the SQL type code defined in java.sql.Types | |
1651 * @throws SQLException if a database access error occurs | |
1652 */ | |
1653 @Override | |
1654 public void setNull(final int parameterIndex, final int sqlType) throws SQLException { | |
1655 // we discard the given type here, the backend converts the | |
1656 // value NULL to whatever it needs for the column | |
1657 setValue(parameterIndex, "NULL"); | |
1658 } | |
1659 | |
1660 /** | |
1661 * Sets the designated parameter to SQL NULL. This version of the method | |
1662 * setNull should be used for user-defined types and REF type parameters. | |
1663 * Examples of user-defined types include: STRUCT, DISTINCT, JAVA_OBJECT, | |
1664 * and named array types. | |
1665 * | |
1666 * Note: To be portable, applications must give the SQL type code and the | |
1667 * fully-qualified SQL type name when specifying a NULL user-defined or REF | |
1668 * parameter. In the case of a user-defined type the name is the type name | |
1669 * of the parameter itself. For a REF parameter, the name is the type name | |
1670 * of the referenced type. If a JDBC driver does not need the type code or | |
1671 * type name information, it may ignore it. Although it is intended for | |
1672 * user-defined and Ref parameters, this method may be used to set a null | |
1673 * parameter of any JDBC type. If the parameter does not have a | |
1674 * user-defined or REF type, the given typeName is ignored. | |
1675 * | |
1676 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1677 * @param sqlType a value from java.sql.Types | |
1678 * @param typeName the fully-qualified name of an SQL user-defined type; | |
1679 * ignored if the parameter is not a user-defined type or | |
1680 * REF | |
1681 * @throws SQLException if a database access error occurs | |
1682 */ | |
1683 @Override | |
1684 public void setNull(final int parameterIndex, final int sqlType, final String typeName) | |
1685 throws SQLException | |
1686 { | |
1687 // MonetDB/SQL's NULL needs no type | |
1688 setNull(parameterIndex, sqlType); | |
1689 } | |
1690 | |
1691 /** | |
1692 * Sets the value of the designated parameter using the given | |
1693 * object. The second parameter must be of type Object; therefore, | |
1694 * the java.lang equivalent objects should be used for built-in | |
1695 * types. | |
1696 * | |
1697 * The JDBC specification specifies a standard mapping from Java | |
1698 * Object types to SQL types. The given argument will be converted | |
1699 * to the corresponding SQL type before being sent to the database. | |
1700 * | |
1701 * Note that this method may be used to pass datatabase-specific | |
1702 * abstract data types, by using a driver-specific Java type. If the | |
1703 * object is of a class implementing the interface SQLData, the JDBC | |
1704 * driver should call the method SQLData.writeSQL to write it to the | |
1705 * SQL data stream. If, on the other hand, the object is of a class | |
1706 * implementing Ref, Blob, Clob, Struct, or Array, the driver should | |
1707 * pass it to the database as a value of the corresponding SQL type. | |
1708 * | |
1709 * This method throws an exception if there is an ambiguity, for | |
1710 * example, if the object is of a class implementing more than one | |
1711 * of the interfaces named above. | |
1712 * | |
1713 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1714 * @param x the object containing the input parameter value | |
1715 * @throws SQLException if a database access error occurs or the type of | |
1716 * the given object is ambiguous | |
1717 */ | |
1718 @Override | |
1719 public void setObject(final int parameterIndex, final Object x) throws SQLException { | |
1720 setObject(parameterIndex, x, javaType[getParamIdx(parameterIndex)], 0); | |
1721 } | |
1722 | |
1723 /** | |
1724 * Sets the value of the designated parameter with the given object. This | |
1725 * method is like the method setObject below, except that it assumes a scale | |
1726 * of zero. | |
1727 * | |
1728 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1729 * @param x the object containing the input parameter value | |
1730 * @param targetSqlType the SQL type (as defined in java.sql.Types) to be | |
1731 * sent to the database | |
1732 * @throws SQLException if a database access error occurs | |
1733 */ | |
1734 @Override | |
1735 public void setObject(final int parameterIndex, final Object x, final int targetSqlType) | |
1736 throws SQLException | |
1737 { | |
1738 setObject(parameterIndex, x, targetSqlType, 0); | |
1739 } | |
1740 | |
1741 /** | |
1742 * Sets the value of the designated parameter with the given object. The | |
1743 * second argument must be an object type; for integral values, the | |
1744 * java.lang equivalent objects should be used. | |
1745 * | |
1746 * The given Java object will be converted to the given targetSqlType | |
1747 * before being sent to the database. If the object has a custom mapping | |
1748 * (is of a class implementing the interface SQLData), the JDBC driver | |
1749 * should call the method SQLData.writeSQL to write it to the SQL data | |
1750 * stream. If, on the other hand, the object is of a class implementing | |
1751 * Ref, Blob, Clob, Struct, or Array, the driver should pass it to the | |
1752 * database as a value of the corresponding SQL type. | |
1753 * | |
1754 * Note that this method may be used to pass database-specific abstract | |
1755 * data types. | |
1756 * | |
1757 * To meet the requirements of this interface, the Java object is | |
1758 * converted in the driver, instead of using a SQL CAST construct. | |
1759 * | |
1760 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
1761 * @param x the object containing the input parameter value | |
1762 * @param targetSqlType the SQL type (as defined in java.sql.Types) to | |
1763 * be sent to the database. The scale argument may | |
1764 * further qualify this type. | |
1765 * @param scale for java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types, | |
1766 * this is the number of digits after the decimal | |
1767 * point. For Java Object types InputStream and Reader, | |
1768 * this is the length of the data in the stream or | |
1769 * reader. For all other types, this value will be | |
1770 * ignored. | |
1771 * @throws SQLException if a database access error occurs | |
1772 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
1773 * not support this method | |
1774 * @see Types | |
1775 */ | |
1776 @Override | |
1777 public void setObject(final int parameterIndex, final Object x, final int targetSqlType, final int scale) | |
1778 throws SQLException | |
1779 { | |
1780 if (x == null) { | |
1781 setNull(parameterIndex, -1); | |
1782 return; | |
1783 } | |
1784 | |
1785 // this is according to table B-5 | |
1786 if (x instanceof String) { | |
1787 setString(parameterIndex, (String)x); | |
1788 } else if (x instanceof BigDecimal || | |
1789 x instanceof Byte || | |
1790 x instanceof Short || | |
1791 x instanceof Integer || | |
1792 x instanceof Long || | |
1793 x instanceof Float || | |
1794 x instanceof Double) | |
1795 { | |
1796 final Number num = (Number)x; | |
1797 switch (targetSqlType) { | |
1798 case Types.TINYINT: | |
1799 setByte(parameterIndex, num.byteValue()); | |
1800 break; | |
1801 case Types.SMALLINT: | |
1802 setShort(parameterIndex, num.shortValue()); | |
1803 break; | |
1804 case Types.INTEGER: | |
1805 setInt(parameterIndex, num.intValue()); | |
1806 break; | |
1807 case Types.BIGINT: | |
1808 if (x instanceof BigDecimal) { | |
1809 BigDecimal bd = (BigDecimal)x; | |
1810 setLong(parameterIndex, bd.setScale(scale, java.math.RoundingMode.HALF_UP).longValue()); | |
1811 } else { | |
1812 setLong(parameterIndex, num.longValue()); | |
1813 } | |
1814 break; | |
1815 case Types.REAL: | |
1816 setFloat(parameterIndex, num.floatValue()); | |
1817 break; | |
1818 case Types.FLOAT: | |
1819 case Types.DOUBLE: | |
1820 setDouble(parameterIndex, num.doubleValue()); | |
1821 break; | |
1822 case Types.DECIMAL: | |
1823 case Types.NUMERIC: | |
1824 if (x instanceof BigDecimal) { | |
1825 setBigDecimal(parameterIndex, (BigDecimal)x); | |
1826 } else { | |
1827 setBigDecimal(parameterIndex, new BigDecimal(num.doubleValue())); | |
1828 } | |
1829 break; | |
1830 case Types.BOOLEAN: | |
1831 if (num.doubleValue() != 0.0) { | |
1832 setBoolean(parameterIndex, true); | |
1833 } else { | |
1834 setBoolean(parameterIndex, false); | |
1835 } | |
1836 break; | |
1837 case Types.CHAR: | |
1838 case Types.VARCHAR: | |
1839 case Types.LONGVARCHAR: | |
1840 case Types.CLOB: | |
1841 setString(parameterIndex, x.toString()); | |
1842 break; | |
1843 default: | |
1844 throw new SQLException("Conversion not allowed", "M1M05"); | |
1845 } | |
1846 } else if (x instanceof Boolean) { | |
1847 final boolean val = ((Boolean)x).booleanValue(); | |
1848 switch (targetSqlType) { | |
1849 case Types.TINYINT: | |
1850 setByte(parameterIndex, (byte)(val ? 1 : 0)); | |
1851 break; | |
1852 case Types.SMALLINT: | |
1853 setShort(parameterIndex, (short)(val ? 1 : 0)); | |
1854 break; | |
1855 case Types.INTEGER: | |
1856 setInt(parameterIndex, (val ? 1 : 0)); // do not cast to (int) as it generates a compiler warning | |
1857 break; | |
1858 case Types.BIGINT: | |
1859 setLong(parameterIndex, (long)(val ? 1 : 0)); | |
1860 break; | |
1861 case Types.REAL: | |
1862 setFloat(parameterIndex, (float)(val ? 1.0 : 0.0)); | |
1863 break; | |
1864 case Types.FLOAT: | |
1865 case Types.DOUBLE: | |
1866 setDouble(parameterIndex, (val ? 1.0 : 0.0)); // do no cast to (double) as it generates a compiler warning | |
1867 break; | |
1868 case Types.DECIMAL: | |
1869 case Types.NUMERIC: | |
1870 { | |
1871 final BigDecimal dec; | |
1872 try { | |
1873 dec = new BigDecimal(val ? 1.0 : 0.0); | |
1874 } catch (NumberFormatException e) { | |
1875 throw new SQLException("Internal error: unable to create template BigDecimal: " + e.getMessage(), "M0M03"); | |
1876 } | |
1877 setBigDecimal(parameterIndex, dec); | |
1878 } break; | |
1879 case Types.BOOLEAN: | |
1880 setBoolean(parameterIndex, val); | |
1881 break; | |
1882 case Types.CHAR: | |
1883 case Types.VARCHAR: | |
1884 case Types.LONGVARCHAR: | |
1885 case Types.CLOB: | |
1886 setString(parameterIndex, x.toString()); | |
1887 break; | |
1888 default: | |
1889 throw new SQLException("Conversion not allowed", "M1M05"); | |
1890 } | |
1891 } else if (x instanceof BigInteger) { | |
1892 final BigInteger num = (BigInteger)x; | |
1893 switch (targetSqlType) { | |
1894 case Types.BIGINT: | |
1895 setLong(parameterIndex, num.longValue()); | |
1896 break; | |
1897 case Types.DECIMAL: | |
1898 case Types.NUMERIC: | |
1899 { | |
1900 final BigDecimal dec; | |
1901 try { | |
1902 dec = new BigDecimal(num); | |
1903 } catch (NumberFormatException e) { | |
1904 throw new SQLException("Internal error: unable to create template BigDecimal: " + e.getMessage(), "M0M03"); | |
1905 } | |
1906 setBigDecimal(parameterIndex, dec); | |
1907 } break; | |
1908 case Types.CHAR: | |
1909 case Types.VARCHAR: | |
1910 case Types.LONGVARCHAR: | |
1911 case Types.CLOB: | |
1912 setString(parameterIndex, x.toString()); | |
1913 break; | |
1914 default: | |
1915 throw new SQLException("Conversion not allowed", "M1M05"); | |
1916 } | |
1917 } else if (x instanceof byte[]) { | |
1918 switch (targetSqlType) { | |
1919 case Types.BINARY: | |
1920 case Types.VARBINARY: | |
1921 case Types.LONGVARBINARY: | |
1922 setBytes(parameterIndex, (byte[])x); | |
1923 break; | |
1924 default: | |
1925 throw new SQLException("Conversion not allowed", "M1M05"); | |
1926 } | |
1927 } else if (x instanceof java.sql.Date || | |
1928 x instanceof Timestamp || | |
1929 x instanceof Time || | |
1930 x instanceof Calendar || | |
1931 x instanceof java.util.Date) | |
1932 { | |
1933 switch (targetSqlType) { | |
1934 case Types.DATE: | |
1935 if (x instanceof java.sql.Date) { | |
1936 setDate(parameterIndex, (java.sql.Date)x); | |
1937 } else if (x instanceof Timestamp) { | |
1938 setDate(parameterIndex, new java.sql.Date(((Timestamp)x).getTime())); | |
1939 } else if (x instanceof java.util.Date) { | |
1940 setDate(parameterIndex, new java.sql.Date( | |
1941 ((java.util.Date)x).getTime())); | |
1942 } else if (x instanceof Calendar) { | |
1943 setDate(parameterIndex, new java.sql.Date( | |
1944 ((Calendar)x).getTimeInMillis())); | |
1945 } else { | |
1946 throw new SQLException("Conversion not allowed", "M1M05"); | |
1947 } | |
1948 break; | |
1949 case Types.TIME: | |
1950 case Types.TIME_WITH_TIMEZONE: | |
1951 if (x instanceof Time) { | |
1952 setTime(parameterIndex, (Time)x); | |
1953 } else if (x instanceof Timestamp) { | |
1954 setTime(parameterIndex, new Time(((Timestamp)x).getTime())); | |
1955 } else if (x instanceof java.util.Date) { | |
1956 setTime(parameterIndex, new java.sql.Time( | |
1957 ((java.util.Date)x).getTime())); | |
1958 } else if (x instanceof Calendar) { | |
1959 setTime(parameterIndex, new java.sql.Time( | |
1960 ((Calendar)x).getTimeInMillis())); | |
1961 } else { | |
1962 throw new SQLException("Conversion not allowed", "M1M05"); | |
1963 } | |
1964 break; | |
1965 case Types.TIMESTAMP: | |
1966 case Types.TIMESTAMP_WITH_TIMEZONE: | |
1967 if (x instanceof Timestamp) { | |
1968 setTimestamp(parameterIndex, (Timestamp)x); | |
1969 } else if (x instanceof java.sql.Date) { | |
1970 setTimestamp(parameterIndex, new Timestamp(((java.sql.Date)x).getTime())); | |
1971 } else if (x instanceof java.util.Date) { | |
1972 setTimestamp(parameterIndex, new java.sql.Timestamp( | |
1973 ((java.util.Date)x).getTime())); | |
1974 } else if (x instanceof Calendar) { | |
1975 setTimestamp(parameterIndex, new java.sql.Timestamp( | |
1976 ((Calendar)x).getTimeInMillis())); | |
1977 } else { | |
1978 throw new SQLException("Conversion not allowed", "M1M05"); | |
1979 } | |
1980 break; | |
1981 case Types.CHAR: | |
1982 case Types.VARCHAR: | |
1983 case Types.LONGVARCHAR: | |
1984 case Types.CLOB: | |
1985 setString(parameterIndex, x.toString()); | |
1986 break; | |
1987 default: | |
1988 throw new SQLException("Conversion not allowed", "M1M05"); | |
1989 } | |
1990 } else if (x instanceof Array) { | |
1991 setArray(parameterIndex, (Array)x); | |
1992 } else if (x instanceof MonetBlob || x instanceof Blob) { | |
1993 setBlob(parameterIndex, (Blob)x); | |
1994 } else if (x instanceof MonetClob || x instanceof Clob) { | |
1995 setClob(parameterIndex, (Clob)x); | |
1996 } else if (x instanceof Struct) { | |
1997 // I have no idea how to do this... | |
1998 throw newSQLFeatureNotSupportedException("setObject() with object of type Struct"); | |
1999 } else if (x instanceof Ref) { | |
2000 setRef(parameterIndex, (Ref)x); | |
2001 } else if (x instanceof java.net.URL) { | |
2002 setURL(parameterIndex, (java.net.URL)x); | |
2003 } else if (x instanceof java.util.UUID) { | |
2004 setString(parameterIndex, x.toString()); | |
2005 } else if (x instanceof RowId) { | |
2006 setRowId(parameterIndex, (RowId)x); | |
2007 } else if (x instanceof NClob) { | |
2008 setNClob(parameterIndex, (NClob)x); | |
2009 } else if (x instanceof SQLXML) { | |
2010 setSQLXML(parameterIndex, (SQLXML)x); | |
2011 } else if (x instanceof SQLData) { // not in JDBC4.1??? | |
2012 final SQLData sx = (SQLData)x; | |
2013 final int paramnr = parameterIndex; | |
2014 final String sqltype = sx.getSQLTypeName(); | |
2015 final SQLOutput out = new SQLOutput() { | |
2016 @Override | |
2017 public void writeString(String x) throws SQLException { | |
2018 // special situation, this is when a string | |
2019 // representation is given, but we need to prefix it | |
2020 // with the actual sqltype the server expects, or we | |
2021 // will get an error back | |
2022 setValue(paramnr, sqltype + " '" + connection.escapeSpecialChars(x) + "'"); | |
2023 } | |
2024 | |
2025 @Override | |
2026 public void writeBoolean(boolean x) throws SQLException { | |
2027 setBoolean(paramnr, x); | |
2028 } | |
2029 | |
2030 @Override | |
2031 public void writeByte(byte x) throws SQLException { | |
2032 setByte(paramnr, x); | |
2033 } | |
2034 | |
2035 @Override | |
2036 public void writeShort(short x) throws SQLException { | |
2037 setShort(paramnr, x); | |
2038 } | |
2039 | |
2040 @Override | |
2041 public void writeInt(int x) throws SQLException { | |
2042 setInt(paramnr, x); | |
2043 } | |
2044 | |
2045 @Override | |
2046 public void writeLong(long x) throws SQLException { | |
2047 setLong(paramnr, x); | |
2048 } | |
2049 | |
2050 @Override | |
2051 public void writeFloat(float x) throws SQLException { | |
2052 setFloat(paramnr, x); | |
2053 } | |
2054 | |
2055 @Override | |
2056 public void writeDouble(double x) throws SQLException { | |
2057 setDouble(paramnr, x); | |
2058 } | |
2059 | |
2060 @Override | |
2061 public void writeBigDecimal(BigDecimal x) throws SQLException { | |
2062 setBigDecimal(paramnr, x); | |
2063 } | |
2064 | |
2065 @Override | |
2066 public void writeBytes(byte[] x) throws SQLException { | |
2067 setBytes(paramnr, x); | |
2068 } | |
2069 | |
2070 @Override | |
2071 public void writeDate(java.sql.Date x) throws SQLException { | |
2072 setDate(paramnr, x); | |
2073 } | |
2074 | |
2075 @Override | |
2076 public void writeTime(java.sql.Time x) throws SQLException { | |
2077 setTime(paramnr, x); | |
2078 } | |
2079 | |
2080 @Override | |
2081 public void writeTimestamp(Timestamp x) throws SQLException { | |
2082 setTimestamp(paramnr, x); | |
2083 } | |
2084 | |
2085 @Override | |
2086 public void writeCharacterStream(Reader x) throws SQLException { | |
2087 setCharacterStream(paramnr, x); | |
2088 } | |
2089 | |
2090 @Override | |
2091 public void writeAsciiStream(InputStream x) throws SQLException { | |
2092 setAsciiStream(paramnr, x); | |
2093 } | |
2094 | |
2095 @Override | |
2096 public void writeBinaryStream(InputStream x) throws SQLException { | |
2097 setBinaryStream(paramnr, x); | |
2098 } | |
2099 | |
2100 @Override | |
2101 public void writeObject(SQLData x) throws SQLException { | |
2102 setObject(paramnr, x); | |
2103 } | |
2104 | |
2105 @Override | |
2106 public void writeRef(Ref x) throws SQLException { | |
2107 setRef(paramnr, x); | |
2108 } | |
2109 | |
2110 @Override | |
2111 public void writeBlob(Blob x) throws SQLException { | |
2112 setBlob(paramnr, x); | |
2113 } | |
2114 | |
2115 @Override | |
2116 public void writeClob(Clob x) throws SQLException { | |
2117 setClob(paramnr, x); | |
2118 } | |
2119 | |
2120 @Override | |
2121 public void writeStruct(Struct x) throws SQLException { | |
2122 setObject(paramnr, x); | |
2123 } | |
2124 | |
2125 @Override | |
2126 public void writeArray(Array x) throws SQLException { | |
2127 setArray(paramnr, x); | |
2128 } | |
2129 | |
2130 @Override | |
2131 public void writeURL(URL x) throws SQLException { | |
2132 setURL(paramnr, x); | |
2133 } | |
2134 | |
2135 @Override | |
2136 public void writeNString(String x) throws SQLException { | |
2137 setNString(paramnr, x); | |
2138 } | |
2139 | |
2140 @Override | |
2141 public void writeNClob(NClob x) throws SQLException { | |
2142 setNClob(paramnr, x); | |
2143 } | |
2144 | |
2145 @Override | |
2146 public void writeRowId(RowId x) throws SQLException { | |
2147 setRowId(paramnr, x); | |
2148 } | |
2149 | |
2150 @Override | |
2151 public void writeSQLXML(SQLXML x) throws SQLException { | |
2152 setSQLXML(paramnr, x); | |
2153 } | |
2154 }; | |
2155 sx.writeSQL(out); | |
2156 } else { // java Class | |
2157 throw newSQLFeatureNotSupportedException("setObject() with object of type Class " + x.getClass().getName()); | |
2158 } | |
2159 } | |
2160 | |
2161 /** | |
2162 * Sets the designated parameter to the given REF(<structured-type>) value. | |
2163 * The driver converts this to an SQL REF value when it sends it to the | |
2164 * database. | |
2165 * | |
2166 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2167 * @param x an SQL REF value | |
2168 * @throws SQLException if a database access error occurs | |
2169 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
2170 * not support this method | |
2171 */ | |
2172 @Override | |
2173 public void setRef(final int parameterIndex, final Ref x) throws SQLException { | |
2174 throw newSQLFeatureNotSupportedException("setRef"); | |
2175 } | |
2176 | |
2177 /** | |
2178 * Sets the designated parameter to the given java.sql.RowId object. | |
2179 * The driver converts this to a SQL ROWID value when it sends it to | |
2180 * the database. | |
2181 * | |
2182 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2183 * @param x the parameter value | |
2184 * @throws SQLException if a database access error occurs | |
2185 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
2186 * not support this method | |
2187 */ | |
2188 @Override | |
2189 public void setRowId(final int parameterIndex, final RowId x) throws SQLException { | |
2190 throw newSQLFeatureNotSupportedException("setRowId"); | |
2191 } | |
2192 | |
2193 /** | |
2194 * Sets the designated parameter to the given Java short value. The driver | |
2195 * converts this to an SQL SMALLINT value when it sends it to the database. | |
2196 * | |
2197 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2198 * @param x the parameter value | |
2199 * @throws SQLException if a database access error occurs | |
2200 */ | |
2201 @Override | |
2202 public void setShort(final int parameterIndex, final short x) throws SQLException { | |
2203 setValue(parameterIndex, Short.toString(x)); | |
2204 } | |
2205 | |
2206 /** | |
2207 * Sets the designated parameter to the given Java String value. The driver | |
2208 * converts this to an SQL VARCHAR or LONGVARCHAR value (depending on the | |
2209 * argument's size relative to the driver's limits on VARCHAR values) when | |
2210 * it sends it to the database. | |
2211 * | |
2212 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2213 * @param x the parameter value | |
2214 * @throws SQLException if a database access error occurs | |
2215 */ | |
2216 @Override | |
2217 public void setString(final int parameterIndex, final String x) throws SQLException { | |
2218 if (x == null) { | |
2219 setNull(parameterIndex, -1); | |
2220 return; | |
2221 } | |
2222 | |
2223 final int paramIdx = getParamIdx(parameterIndex); // this will throw a SQLException if parameter can not be found | |
2224 | |
2225 /* depending on the parameter data type (as expected by MonetDB) we | |
2226 may need to add the data type as cast prefix to the parameter value */ | |
2227 final int paramJdbcType = javaType[paramIdx]; | |
2228 final String paramMonetdbType = monetdbType[paramIdx]; | |
2229 | |
2230 switch (paramJdbcType) { | |
2231 case Types.CHAR: | |
2232 case Types.VARCHAR: | |
2233 case Types.LONGVARCHAR: | |
2234 case Types.CLOB: | |
2235 case Types.NCHAR: | |
2236 case Types.NVARCHAR: | |
2237 case Types.LONGNVARCHAR: | |
2238 { | |
2239 String castprefix = ""; | |
2240 switch (paramMonetdbType) { | |
2241 // some MonetDB specific data types require a cast prefix | |
2242 case "inet": | |
2243 try { | |
2244 // check if x represents a valid inet string to prevent | |
2245 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 | |
2246 org.monetdb.jdbc.types.INET inet_obj = new org.monetdb.jdbc.types.INET(); | |
2247 inet_obj.fromString(x); | |
2248 } catch (SQLException se) { | |
2249 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + se.getMessage(), "22M29"); | |
2250 } | |
2251 castprefix = "inet"; | |
2252 break; | |
2253 case "json": | |
2254 // There is no support for JSON in standard java class libraries. | |
2255 // Possibly we could use org.json.simple.JSONObject or other/faster libs | |
2256 // javax.json.Json is not released yet (see https://json-processing-spec.java.net/) | |
2257 // see also https://github.com/fabienrenaud/java-json-benchmark | |
2258 // Note that it would make our JDBC driver dependent of an external jar | |
2259 // and we don't want that. | |
2260 | |
2261 // do simplistic check if x represents a valid json string to prevent | |
2262 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 | |
2263 if (x.isEmpty() || | |
2264 (x.startsWith("{") && !x.endsWith("}")) || | |
2265 (x.startsWith("[") && !x.endsWith("]")) | |
2266 // TODO check completely if x represents a valid json string | |
2267 ) | |
2268 throw new SQLDataException("Invalid json string. It does not start with { or [ and end with } or ]", "22M32"); | |
2269 | |
2270 // TODO check completely if x represents a valid json string | |
2271 | |
2272 castprefix = "json"; | |
2273 break; | |
2274 case "url": | |
2275 try { | |
2276 // also check if x represents a valid url string to prevent | |
2277 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 | |
2278 java.net.URL url_obj = new java.net.URL(x); | |
2279 } catch (java.net.MalformedURLException mue) { | |
2280 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + mue.getMessage(), "22M30"); | |
2281 } | |
2282 castprefix = "url"; | |
2283 break; | |
2284 case "uuid": | |
2285 try { | |
2286 // also check if x represents a valid uuid string to prevent | |
2287 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 | |
2288 java.util.UUID uuid_obj = java.util.UUID.fromString(x); | |
2289 } catch (IllegalArgumentException iae) { | |
2290 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + iae.getMessage(), "22M31"); | |
2291 } | |
2292 castprefix = "uuid"; | |
2293 break; | |
2294 } | |
2295 /* in specific cases prefix the string with: inet or json or url or uuid */ | |
2296 setValue(parameterIndex, castprefix + " '" + connection.escapeSpecialChars(x) + "'"); | |
2297 | |
2298 break; | |
2299 } | |
2300 case Types.TINYINT: | |
2301 case Types.SMALLINT: | |
2302 case Types.INTEGER: | |
2303 case Types.BIGINT: | |
2304 case Types.REAL: | |
2305 case Types.FLOAT: | |
2306 case Types.DOUBLE: | |
2307 case Types.DECIMAL: | |
2308 case Types.NUMERIC: | |
2309 try { | |
2310 // check (by calling parse) if the string represents a valid number to prevent | |
2311 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 | |
2312 if (paramJdbcType == Types.INTEGER || paramJdbcType == Types.SMALLINT || paramJdbcType == Types.TINYINT) { | |
2313 int number = Integer.parseInt(x); | |
2314 } else | |
2315 if (paramJdbcType == Types.BIGINT) { | |
2316 long number = Long.parseLong(x); | |
2317 } else | |
2318 if (paramJdbcType == Types.REAL || paramJdbcType == Types.DOUBLE || paramJdbcType == Types.FLOAT) { | |
2319 double number = Double.parseDouble(x); | |
2320 } else | |
2321 if (paramJdbcType == Types.DECIMAL || paramJdbcType == Types.NUMERIC) { | |
2322 BigDecimal number = new BigDecimal(x); | |
2323 } | |
2324 } catch (NumberFormatException nfe) { | |
2325 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + nfe.getMessage(), "22003"); | |
2326 } | |
2327 setValue(parameterIndex, x); | |
2328 break; | |
2329 case Types.BOOLEAN: | |
2330 if (x.equalsIgnoreCase("false") || x.equalsIgnoreCase("true") || x.equals("0") || x.equals("1")) { | |
2331 setValue(parameterIndex, x); | |
2332 } else { | |
2333 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed", "22000"); | |
2334 } | |
2335 break; | |
2336 case Types.DATE: | |
2337 case Types.TIME: | |
2338 case Types.TIME_WITH_TIMEZONE: | |
2339 case Types.TIMESTAMP: | |
2340 case Types.TIMESTAMP_WITH_TIMEZONE: | |
2341 try { | |
2342 // check if the string represents a valid calendar date or time or timestamp to prevent | |
2343 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 | |
2344 if (paramJdbcType == Types.DATE) { | |
2345 java.sql.Date datum = java.sql.Date.valueOf(x); | |
2346 } else | |
2347 if (paramJdbcType == Types.TIME || paramJdbcType == Types.TIME_WITH_TIMEZONE) { | |
2348 Time tijdstip = Time.valueOf(x); | |
2349 } else | |
2350 if (paramJdbcType == Types.TIMESTAMP || paramJdbcType == Types.TIMESTAMP_WITH_TIMEZONE) { | |
2351 Timestamp tijdstip = Timestamp.valueOf(x); | |
2352 } | |
2353 } catch (IllegalArgumentException iae) { | |
2354 throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + iae.getMessage(), "22007"); | |
2355 } | |
2356 /* prefix the string with: date or time or timetz or timestamp or timestamptz */ | |
2357 setValue(parameterIndex, paramMonetdbType + " '" + x + "'"); | |
2358 break; | |
2359 case Types.BINARY: | |
2360 case Types.VARBINARY: | |
2361 case Types.LONGVARBINARY: | |
2362 case Types.BLOB: | |
2363 // check if the string x contains pairs of hex chars to prevent | |
2364 // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 | |
2365 final int xlen = x.length(); | |
2366 for (int i = 0; i < xlen; i++) { | |
2367 char c = x.charAt(i); | |
2368 if (c < '0' || c > '9') { | |
2369 if (c < 'A' || c > 'F') { | |
2370 if (c < 'a' || c > 'f') { | |
2371 throw new SQLDataException("Invalid string for parameter data type " + paramMonetdbType + ". The string may contain only hex chars", "22M28"); | |
2372 } | |
2373 } | |
2374 } | |
2375 } | |
2376 /* prefix the string with: blob */ | |
2377 setValue(parameterIndex, "blob '" + x + "'"); | |
2378 break; | |
2379 default: | |
2380 throw new SQLException("Conversion of string to parameter data type " + paramMonetdbType + " is not (yet) supported", "M1M05"); | |
2381 } | |
2382 } | |
2383 | |
2384 /** | |
2385 * Sets the designated parameter to the given java.sql.SQLXML | |
2386 * object. The driver converts this to an SQL XML value when it | |
2387 * sends it to the database. | |
2388 * | |
2389 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2390 * @param x a SQLXML object that maps an SQL XML value | |
2391 * @throws SQLException if a database access error occurs | |
2392 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
2393 * not support this method | |
2394 */ | |
2395 @Override | |
2396 public void setSQLXML(final int parameterIndex, final SQLXML x) throws SQLException { | |
2397 throw newSQLFeatureNotSupportedException("setSQLXML"); | |
2398 } | |
2399 | |
2400 /** | |
2401 * Sets the designated parameter to the given java.sql.Time value. | |
2402 * The driver converts this to an SQL TIME value when it sends it to | |
2403 * the database. | |
2404 * | |
2405 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2406 * @param x the parameter value | |
2407 * @throws SQLException if a database access error occurs | |
2408 */ | |
2409 @Override | |
2410 public void setTime(final int parameterIndex, final Time x) throws SQLException { | |
2411 setTime(parameterIndex, x, null); | |
2412 } | |
2413 | |
2414 /** | |
2415 * Sets the designated parameter to the given java.sql.Time value, | |
2416 * using the given Calendar object. The driver uses the Calendar | |
2417 * object to construct an SQL TIME value, which the driver then | |
2418 * sends to the database. With a Calendar object, the driver can | |
2419 * calculate the time taking into account a custom timezone. If no | |
2420 * Calendar object is specified, the driver uses the default | |
2421 * timezone, which is that of the virtual machine running the | |
2422 * application. | |
2423 * | |
2424 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2425 * @param x the parameter value | |
2426 * @param cal the Calendar object the driver will use to construct the time | |
2427 * @throws SQLException if a database access error occurs | |
2428 */ | |
2429 @Override | |
2430 public void setTime(final int parameterIndex, final Time x, final Calendar cal) | |
2431 throws SQLException | |
2432 { | |
2433 if (x == null) { | |
2434 setNull(parameterIndex, -1); | |
2435 return; | |
2436 } | |
2437 | |
2438 final String MonetDBType = monetdbType[getParamIdx(parameterIndex)]; | |
2439 final boolean hasTimeZone = ("timetz".equals(MonetDBType) || "timestamptz".equals(MonetDBType)); | |
2440 if (hasTimeZone) { | |
2441 // timezone shouldn't matter, since the server is timezone | |
2442 // aware in this case | |
2443 if (mTimeZ == null) { | |
2444 // first time usage, create and keep the mTimeZ object for next usage | |
2445 mTimeZ = new SimpleDateFormat("HH:mm:ss.SSSZ"); | |
2446 } | |
2447 final String RFC822 = mTimeZ.format(x); | |
2448 setValue(parameterIndex, "timetz '" + RFC822.substring(0, 15) + ":" + RFC822.substring(15) + "'"); | |
2449 } else { | |
2450 // server is not timezone aware for this field, and no | |
2451 // calendar given, since we told the server our timezone at | |
2452 // connection creation, we can just write a plain timestamp | |
2453 // here | |
2454 if (cal == null) { | |
2455 setValue(parameterIndex, "time '" + x.toString() + "'"); | |
2456 } else { | |
2457 if (mTime == null) { | |
2458 // first time usage, create and keep the mTime object for next usage | |
2459 mTime = new SimpleDateFormat("HH:mm:ss.SSS"); | |
2460 } | |
2461 mTime.setTimeZone(cal.getTimeZone()); | |
2462 setValue(parameterIndex, "time '" + mTime.format(x) + "'"); | |
2463 } | |
2464 } | |
2465 } | |
2466 | |
2467 /** | |
2468 * Sets the designated parameter to the given java.sql.Timestamp | |
2469 * value. The driver converts this to an SQL TIMESTAMP value when | |
2470 * it sends it to the database. | |
2471 * | |
2472 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2473 * @param x the parameter value | |
2474 * @throws SQLException if a database access error occurs | |
2475 */ | |
2476 @Override | |
2477 public void setTimestamp(final int parameterIndex, final Timestamp x) | |
2478 throws SQLException | |
2479 { | |
2480 setTimestamp(parameterIndex, x, null); | |
2481 } | |
2482 | |
2483 /** | |
2484 * Sets the designated parameter to the given java.sql.Timestamp | |
2485 * value, using the given Calendar object. The driver uses the | |
2486 * Calendar object to construct an SQL TIMESTAMP value, which the | |
2487 * driver then sends to the database. With a Calendar object, the | |
2488 * driver can calculate the timestamp taking into account a custom | |
2489 * timezone. If no Calendar object is specified, the driver uses the | |
2490 * default timezone, which is that of the virtual machine running | |
2491 * the application. | |
2492 * | |
2493 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2494 * @param x the parameter value | |
2495 * @param cal the Calendar object the driver will use to construct the | |
2496 * timestamp | |
2497 * @throws SQLException if a database access error occurs | |
2498 */ | |
2499 @Override | |
2500 public void setTimestamp(final int parameterIndex, final Timestamp x, final Calendar cal) | |
2501 throws SQLException | |
2502 { | |
2503 if (x == null) { | |
2504 setNull(parameterIndex, -1); | |
2505 return; | |
2506 } | |
2507 | |
2508 final String MonetDBType = monetdbType[getParamIdx(parameterIndex)]; | |
2509 final boolean hasTimeZone = ("timestamptz".equals(MonetDBType) || "timetz".equals(MonetDBType)); | |
2510 if (hasTimeZone) { | |
2511 // timezone shouldn't matter, since the server is timezone | |
2512 // aware in this case | |
2513 if (mTimestampZ == null) { | |
2514 // first time usage, create and keep the mTimestampZ object for next usage | |
2515 mTimestampZ = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); | |
2516 } | |
2517 final String RFC822 = mTimestampZ.format(x); | |
2518 setValue(parameterIndex, "timestamptz '" + RFC822.substring(0, 26) + ":" + RFC822.substring(26) + "'"); | |
2519 } else { | |
2520 // server is not timezone aware for this field, and no | |
2521 // calendar given, since we told the server our timezone at | |
2522 // connection creation, we can just write a plain timestamp here | |
2523 if (cal == null) { | |
2524 setValue(parameterIndex, "timestamp '" + x.toString() + "'"); | |
2525 } else { | |
2526 if (mTimestamp == null) { | |
2527 // first time usage, create and keep the mTimestamp object for next usage | |
2528 mTimestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); | |
2529 } | |
2530 mTimestamp.setTimeZone(cal.getTimeZone()); | |
2531 setValue(parameterIndex, "timestamp '" + mTimestamp.format(x) + "'"); | |
2532 } | |
2533 } | |
2534 } | |
2535 | |
2536 /** | |
2537 * Sets the designated parameter to the given input stream, which will have | |
2538 * the specified number of bytes. A Unicode character has two bytes, with | |
2539 * the first byte being the high byte, and the second being the low byte. | |
2540 * When a very large Unicode value is input to a LONGVARCHAR parameter, it | |
2541 * may be more practical to send it via a java.io.InputStream object. The | |
2542 * data will be read from the stream as needed until end-of-file is | |
2543 * reached. The JDBC driver will do any necessary conversion from Unicode | |
2544 * to the database char format. | |
2545 * | |
2546 * Note: This stream object can either be a standard Java stream object or | |
2547 * your own subclass that implements the standard interface. | |
2548 * | |
2549 * @deprecated Use setCharacterStream | |
2550 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2551 * @param x a java.io.InputStream object that contains the Unicode | |
2552 * parameter value as two-byte Unicode characters | |
2553 * @param length the number of bytes in the stream | |
2554 * @throws SQLException if a database access error occurs | |
2555 * @throws SQLFeatureNotSupportedException the JDBC driver does | |
2556 * not support this method | |
2557 */ | |
2558 @Override | |
2559 @Deprecated | |
2560 public void setUnicodeStream(final int parameterIndex, final InputStream x, final int length) | |
2561 throws SQLException | |
2562 { | |
2563 throw newSQLFeatureNotSupportedException("setUnicodeStream"); | |
2564 } | |
2565 | |
2566 /** | |
2567 * Sets the designated parameter to the given java.net.URL value. The | |
2568 * driver converts this to an SQL DATALINK value when it sends it to the | |
2569 * database. | |
2570 * | |
2571 * @param parameterIndex the first parameter is 1, the second is 2, ... | |
2572 * @param x the java.net.URL object to be set | |
2573 * @throws SQLException if a database access error occurs | |
2574 */ | |
2575 @Override | |
2576 public void setURL(final int parameterIndex, final URL x) throws SQLException { | |
2577 if (x == null) { | |
2578 setNull(parameterIndex, -1); | |
2579 return; | |
2580 } | |
2581 | |
2582 setValue(parameterIndex, "url '" + connection.escapeSpecialChars(x.toString()) + "'"); | |
2583 } | |
2584 | |
2585 /** | |
2586 * Releases this PreparedStatement object's database and JDBC | |
2587 * resources immediately instead of waiting for this to happen when | |
2588 * it is automatically closed. It is generally good practice to | |
2589 * release resources as soon as you are finished with them to avoid | |
2590 * tying up database resources. | |
2591 * | |
2592 * Calling the method close on a PreparedStatement object that is | |
2593 * already closed has no effect. | |
2594 * | |
2595 * <b>Note:</b> A PreparedStatement object is automatically closed | |
2596 * when it is garbage collected. When a Statement object is closed, | |
2597 * its current ResultSet object, if one exists, is also closed. | |
2598 */ | |
2599 @Override | |
2600 public void close() { | |
2601 try { | |
2602 if (!closed && id != -1) | |
2603 connection.sendControlCommand("release " + id); | |
2604 } catch (SQLException e) { | |
2605 // probably server closed connection | |
2606 } | |
2607 super.close(); | |
2608 } | |
2609 | |
2610 /** | |
2611 * Call close to release the server-sided handle for this | |
2612 * PreparedStatement. | |
2613 * | |
2614 * @deprecated (since="9") | |
2615 */ | |
2616 @Override | |
2617 @Deprecated | |
2618 protected void finalize() { | |
2619 close(); | |
2620 } | |
2621 | |
2622 //== Java 1.8 methods (JDBC 4.2) | |
2623 | |
2624 @Override | |
2625 public void setObject(final int parameterIndex, final Object x, final SQLType targetSqlType, final int scaleOrLength) throws SQLException { | |
2626 // setObject(parameterIndex, x, convertSQLType(targetSqlType), scaleOrLength); // TODO implement convertSQLType(targetSqlType) | |
2627 throw newSQLFeatureNotSupportedException("setObject"); | |
2628 } | |
2629 | |
2630 @Override | |
2631 public void setObject(final int parameterIndex, final Object x, final SQLType targetSqlType) throws SQLException { | |
2632 // setObject(parameterIndex, x, convertSQLType(targetSqlType)); // TODO implement convertSQLType(targetSqlType) | |
2633 throw newSQLFeatureNotSupportedException("setObject"); | |
2634 } | |
2635 | |
2636 /** | |
2637 * Executes the SQL statement in this PreparedStatement object, which must be | |
2638 * an SQL Data Manipulation Language (DML) statement, such as INSERT, UPDATE or DELETE statement; | |
2639 * or an SQL statement that returns nothing, such as a DDL statement. | |
2640 * | |
2641 * This method should be used when the returned row count may exceed Integer.MAX_VALUE. | |
2642 * The default implementation will throw UnsupportedOperationException | |
2643 * | |
2644 * @return either (1) the row count for SQL Data Manipulation Language (DML) statements | |
2645 * or (2) 0 for SQL statements that return nothing | |
2646 * @throws SQLException if a database access error occurs; this method is called on a closed PreparedStatement | |
2647 * or the SQL statement returns a ResultSet object | |
2648 */ | |
2649 @Override | |
2650 public long executeLargeUpdate() throws SQLException { | |
2651 if (execute() != false) | |
2652 throw new SQLException("Query produced a result set", "M1M17"); | |
2653 | |
2654 return getLargeUpdateCount(); | |
2655 } | |
2656 | |
2657 //== end methods interface PreparedStatement | |
2658 | |
2659 | |
2660 //== internal helper methods which do not belong to the JDBC interface | |
2661 | |
2662 /** | |
2663 * Sets the given index with the supplied value. If the given index is | |
2664 * out of bounds, and SQLException is thrown. The given value should | |
2665 * never be null. | |
2666 * | |
2667 * @param parameterIndex the parameter index | |
2668 * @param val the exact String representation to set | |
2669 * @throws SQLException if the given index is out of bounds | |
2670 */ | |
2671 private final void setValue(final int parameterIndex, final String val) throws SQLException { | |
2672 values[getParamIdx(parameterIndex)] = (val == null ? "NULL" : val); | |
2673 } | |
2674 | |
2675 /** | |
2676 * Transforms the prepare query into a simple SQL query by replacing | |
2677 * the ?'s with the given column contents. | |
2678 * Mind that the JDBC specs allow `reuse' of a value for a column over | |
2679 * multiple executes. | |
2680 * | |
2681 * @return the simple SQL string for the prepare query | |
2682 * @throws SQLException if not all columns are set | |
2683 */ | |
2684 private final String transform() throws SQLException { | |
2685 final StringBuilder buf = new StringBuilder(8 + 12 * size); | |
2686 buf.append("exec ").append(id).append('('); | |
2687 // check if all columns are set and do a replace | |
2688 int col = 0; | |
2689 for (int i = 0; i < size; i++) { | |
2690 if (column[i] != null) | |
2691 continue; | |
2692 col++; | |
2693 if (col > 1) | |
2694 buf.append(','); | |
2695 if (values[i] == null) | |
2696 throw new SQLException("Cannot execute, parameter " + col + " is missing.", "M1M05"); | |
2697 | |
2698 buf.append(values[i]); | |
2699 } | |
2700 buf.append(')'); | |
2701 | |
2702 return buf.toString(); | |
2703 } | |
2704 | |
2705 /** | |
2706 * Small helper method that formats the "Invalid Parameter Index number ..." message | |
2707 * and creates a new SQLDataException object whose SQLState is set | |
2708 * to "22010": invalid indicator parameter value. | |
2709 * | |
2710 * @param paramIdx the parameter index number | |
2711 * @return a new created SQLDataException object with SQLState 22010 | |
2712 */ | |
2713 private static final SQLDataException newSQLInvalidParameterIndexException(final int paramIdx) { | |
2714 return new SQLDataException("Invalid Parameter Index number: " + paramIdx, "22010"); | |
2715 } | |
2716 } |