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 &gt; ? and id &lt; ?;
54 * &amp;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(&lt;structured-type&gt;) 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 }