1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.jdbc.EmbedPreparedStatement |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.jdbc; |
22 | |
23 | import org.apache.derby.iapi.services.sanity.SanityManager; |
24 | |
25 | import org.apache.derby.iapi.types.VariableSizeDataValue; |
26 | |
27 | import org.apache.derby.iapi.sql.dictionary.DataDictionary; |
28 | import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; |
29 | import org.apache.derby.iapi.sql.PreparedStatement; |
30 | import org.apache.derby.iapi.sql.execute.ExecPreparedStatement; |
31 | import org.apache.derby.iapi.sql.ResultSet; |
32 | import org.apache.derby.iapi.sql.Activation; |
33 | import org.apache.derby.iapi.sql.ParameterValueSet; |
34 | import org.apache.derby.iapi.sql.ResultDescription; |
35 | import org.apache.derby.iapi.types.DataTypeDescriptor; |
36 | import org.apache.derby.iapi.types.DataValueDescriptor; |
37 | import org.apache.derby.iapi.types.RawToBinaryFormatStream; |
38 | import org.apache.derby.iapi.types.ReaderToUTF8Stream; |
39 | |
40 | import org.apache.derby.iapi.error.StandardException; |
41 | |
42 | import org.apache.derby.iapi.services.io.LimitReader; |
43 | |
44 | import org.apache.derby.iapi.reference.SQLState; |
45 | import org.apache.derby.iapi.reference.JDBC30Translation; |
46 | import org.apache.derby.iapi.reference.JDBC20Translation; |
47 | |
48 | import java.util.Calendar; |
49 | import java.util.Vector; |
50 | |
51 | /* |
52 | We would import these, but have name-overlap |
53 | import java.sql.PreparedStatement; |
54 | import java.sql.ResultSet; |
55 | */ |
56 | import java.sql.ResultSetMetaData; |
57 | import java.sql.SQLException; |
58 | import java.sql.Date; |
59 | import java.sql.Time; |
60 | import java.sql.Timestamp; |
61 | import java.sql.Clob; |
62 | import java.sql.Blob; |
63 | |
64 | import java.io.InputStream; |
65 | import java.io.DataInputStream; |
66 | import java.io.IOException; |
67 | import java.io.EOFException; |
68 | import java.io.Reader; |
69 | import java.sql.Types; |
70 | |
71 | import org.apache.derby.iapi.jdbc.BrokeredConnectionControl; |
72 | |
73 | /** |
74 | * |
75 | * EmbedPreparedStatement is a local JDBC statement. |
76 | <P><B>Supports</B> |
77 | <UL> |
78 | <LI> JSR169 |
79 | </UL> |
80 | */ |
81 | public abstract class EmbedPreparedStatement |
82 | extends EmbedStatement |
83 | implements java.sql.PreparedStatement |
84 | { |
85 | |
86 | //Moving jdbc2.0 batch related code in this class because callableStatement in jdbc 20 needs |
87 | //this code too and it doesn't derive from prepared statement in jdbc 20 in our implementation. |
88 | |
89 | protected ResultSetMetaData rMetaData; |
90 | //bug 4579-If the prepared statement was revalidated after doing getMetaData(), we |
91 | //should get the metadata info again on next getMetaData(). We store the generated |
92 | //class name in following variable during getMetaData() call. If it differs from the |
93 | //current generated class name, then that indicates a refetch of metadata is required. |
94 | private String gcDuringGetMetaData; |
95 | |
96 | protected PreparedStatement preparedStatement; |
97 | private Activation activation; |
98 | |
99 | private BrokeredConnectionControl bcc=null; |
100 | |
101 | /* |
102 | Constructor assumes caller will setup context stack |
103 | and restore it. |
104 | */ |
105 | public EmbedPreparedStatement (EmbedConnection conn, String sql, boolean forMetaData, |
106 | int resultSetType, int resultSetConcurrency, |
107 | int resultSetHoldability, |
108 | int autoGeneratedKeys, |
109 | int[] columnIndexes, |
110 | String[] columnNames) |
111 | throws SQLException { |
112 | |
113 | super(conn, forMetaData, resultSetType, resultSetConcurrency, resultSetHoldability); |
114 | // PreparedStatement is poolable by default |
115 | isPoolable = true; |
116 | |
117 | // if the sql string is null, raise an error |
118 | if (sql == null) |
119 | throw newSQLException(SQLState.NULL_SQL_TEXT); |
120 | |
121 | // set up the SQLText in EmbedStatement |
122 | SQLText = sql; |
123 | |
124 | try { |
125 | preparedStatement = lcc.prepareInternalStatement |
126 | (lcc.getDefaultSchema(), sql, resultSetConcurrency==JDBC20Translation.CONCUR_READ_ONLY, forMetaData); |
127 | |
128 | addWarning(preparedStatement.getCompileTimeWarnings()); |
129 | |
130 | activation = preparedStatement.getActivation(lcc, resultSetType == JDBC20Translation.TYPE_SCROLL_INSENSITIVE); |
131 | |
132 | checkRequiresCallableStatement(activation); |
133 | |
134 | //bug 4838 - save the auto-generated key information in activation. keeping this |
135 | //information in lcc will not work work as it can be tampered by a nested trasaction |
136 | if (autoGeneratedKeys == JDBC30Translation.RETURN_GENERATED_KEYS) |
137 | activation.setAutoGeneratedKeysResultsetInfo(columnIndexes, columnNames); |
138 | |
139 | } catch (Throwable t) { |
140 | throw handleException(t); |
141 | } |
142 | } |
143 | |
144 | /** |
145 | JDBC states that a Statement is closed when garbage collected. |
146 | |
147 | @exception Throwable Allows any exception to be thrown during finalize |
148 | */ |
149 | protected void finalize() throws Throwable { |
150 | super.finalize(); |
151 | |
152 | /* |
153 | ** We mark the activation as not being used and |
154 | ** that is it. We rely on the connection to sweep |
155 | ** through the activations to find the ones that |
156 | ** aren't in use, and to close them. We cannot |
157 | ** do a activation.close() here because there are |
158 | ** synchronized methods under close that cannot |
159 | ** be called during finalization. |
160 | */ |
161 | if (activation != null) |
162 | { |
163 | activation.markUnused(); |
164 | } |
165 | } |
166 | |
167 | /* |
168 | * Statement interface |
169 | we override all Statement methods that take a SQL |
170 | string as they must thrown an exception in a PreparedStatement. |
171 | See the JDBC 3.0 spec. |
172 | */ |
173 | public final boolean execute(String sql) throws SQLException { |
174 | throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "execute(String)"); |
175 | } |
176 | public final boolean execute(String sql, int autoGenKeys) throws SQLException { |
177 | throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "execute(String, int)"); |
178 | } |
179 | public final boolean execute(String sql, int[] columnIndexes) throws SQLException { |
180 | throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "execute(String, int[])"); |
181 | } |
182 | public final boolean execute(String sql, String[] columnNames) throws SQLException { |
183 | throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "execute(String, String[])"); |
184 | } |
185 | public final java.sql.ResultSet executeQuery(String sql) throws SQLException { |
186 | throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "executeQuery(String)"); |
187 | } |
188 | public final int executeUpdate(String sql) throws SQLException { |
189 | throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "executeUpdate(String)"); |
190 | } |
191 | public final int executeUpdate(String sql, int autoGenKeys) throws SQLException { |
192 | throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "executeUpdate(String, int)"); |
193 | } |
194 | public final int executeUpdate(String sql, int[] columnIndexes) throws SQLException { |
195 | throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "executeUpdate(String, int[])"); |
196 | } |
197 | public final int executeUpdate(String sql, String[] columnNames) throws SQLException { |
198 | throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "executeUpdate(String, String[])"); |
199 | } |
200 | public final void addBatch(String sql) throws SQLException { |
201 | throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "addBatch(String)"); |
202 | } |
203 | |
204 | |
205 | /** |
206 | * Additional close to close our activation. |
207 | * In the case that a XAConnection is involved in the creation of this |
208 | * PreparedStatement for e.g in the following case |
209 | * |
210 | * <code> |
211 | * XAConnection xaconn = xadatasource.getXAConnection();//where xadatasource is an object of XADataSource |
212 | * Connection conn = xaconnection.getConnection(); |
213 | * PreparedStatement ps = conn.preparedStatement("values 1"); |
214 | * </code> |
215 | * |
216 | * In the above case the PreparedStatement will actually be a |
217 | * BrokeredPreparedStatement40 object. Hence when we call |
218 | * bcc.onStatementClose and pass the PreparedStatement that caused it |
219 | * applicationStatement will be the appropriate choice since it will |
220 | * contain the appropriate instance of PreparedStatement in each case |
221 | * |
222 | * @throws SQLException upon failure |
223 | * |
224 | */ |
225 | void closeActions() throws SQLException { |
226 | |
227 | if (bcc!=null) { |
228 | java.sql.PreparedStatement ps_app = |
229 | (java.sql.PreparedStatement)applicationStatement; |
230 | bcc.onStatementClose(ps_app); |
231 | } |
232 | //we release the resource for preparedStatement |
233 | preparedStatement = null; |
234 | |
235 | try{ |
236 | setupContextStack(); |
237 | } catch (SQLException se) { |
238 | //we may have already committed the transaction in which case |
239 | //setupContextStack will fail, the close should just return |
240 | return; |
241 | } |
242 | try |
243 | { |
244 | activation.close(); |
245 | activation = null; |
246 | } catch (Throwable t) |
247 | { |
248 | throw handleException(t); |
249 | } finally { |
250 | restoreContextStack(); |
251 | } |
252 | } |
253 | |
254 | /* |
255 | * PreparedStatement interface; we have inherited from |
256 | * EmbedStatement to get the Statement interface for |
257 | * EmbedPreparedStatement (needed by PreparedStatement) |
258 | * These are the JDBC interface comments, so we know |
259 | * what to do. |
260 | */ |
261 | |
262 | /** |
263 | * A prepared SQL query is executed and its ResultSet is returned. |
264 | * |
265 | * @return a ResultSet that contains the data produced by the |
266 | * query; never null |
267 | * @exception SQLException thrown on failure. |
268 | */ |
269 | public final java.sql.ResultSet executeQuery() throws SQLException { |
270 | try { |
271 | executeStatement(activation, true, false); |
272 | } catch(SQLException sqle) { |
273 | checkStatementValidity(sqle); |
274 | } |
275 | |
276 | if (SanityManager.DEBUG) { |
277 | if (results == null) |
278 | SanityManager.THROWASSERT("no results returned on executeQuery()"); |
279 | } |
280 | |
281 | return results; |
282 | } |
283 | |
284 | /** |
285 | * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, |
286 | * SQL statements that return nothing such as SQL DDL statements |
287 | * can be executed. |
288 | * |
289 | * @return either the row count for INSERT, UPDATE or DELETE; or 0 |
290 | * for SQL statements that return nothing |
291 | * @exception SQLException thrown on failure. |
292 | */ |
293 | public final int executeUpdate() throws SQLException { |
294 | try { |
295 | executeStatement(activation, false, true); |
296 | } catch(SQLException sqle) { |
297 | checkStatementValidity(sqle); |
298 | } |
299 | return updateCount; |
300 | } |
301 | |
302 | /** |
303 | * Set a parameter to SQL NULL. |
304 | * |
305 | * <P><B>Note:</B> You must specify the parameter's SQL type. |
306 | * |
307 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
308 | * @param sqlType SQL type code defined by java.sql.Types |
309 | * @exception SQLException thrown on failure. |
310 | */ |
311 | public void setNull(int parameterIndex, int sqlType) throws SQLException { |
312 | |
313 | checkStatus(); |
314 | |
315 | int jdbcTypeId = getParameterJDBCType(parameterIndex); |
316 | |
317 | if (!DataTypeDescriptor.isJDBCTypeEquivalent(jdbcTypeId, sqlType)) { |
318 | |
319 | throw dataTypeConversion(parameterIndex, Util.typeName(sqlType)); |
320 | } |
321 | |
322 | try { |
323 | /* JDBC is one-based, DBMS is zero-based */ |
324 | getParms().getParameterForSet(parameterIndex - 1).setToNull(); |
325 | } catch (StandardException t) { |
326 | throw EmbedResultSet.noStateChangeException(t); |
327 | } |
328 | |
329 | } |
330 | |
331 | /** |
332 | * Set a parameter to a Java boolean value. According to the JDBC API spec, |
333 | * the driver converts this to a SQL BIT value when it sends it to the |
334 | * database. But we don't have to do this, since the database engine |
335 | * supports a boolean type. |
336 | * |
337 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
338 | * @param x the parameter value |
339 | * @exception SQLException thrown on failure. |
340 | */ |
341 | public void setBoolean(int parameterIndex, boolean x) throws SQLException { |
342 | |
343 | checkStatus(); |
344 | try { |
345 | /* JDBC is one-based, DBMS is zero-based */ |
346 | getParms().getParameterForSet(parameterIndex - 1).setValue(x); |
347 | |
348 | } catch (StandardException t) { |
349 | throw EmbedResultSet.noStateChangeException(t); |
350 | } |
351 | } |
352 | |
353 | /** |
354 | * Set a parameter to a Java byte value. The driver converts this |
355 | * to a SQL TINYINT value when it sends it to the database. |
356 | * |
357 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
358 | * @param x the parameter value |
359 | * @exception SQLException thrown on failure. |
360 | */ |
361 | public void setByte(int parameterIndex, byte x) throws SQLException { |
362 | |
363 | checkStatus(); |
364 | try { |
365 | |
366 | getParms().getParameterForSet(parameterIndex - 1).setValue(x); |
367 | |
368 | } catch (Throwable t) { |
369 | throw EmbedResultSet.noStateChangeException(t); |
370 | } |
371 | } |
372 | |
373 | /** |
374 | * Set a parameter to a Java short value. The driver converts this |
375 | * to a SQL SMALLINT value when it sends it to the database. |
376 | * |
377 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
378 | * @param x the parameter value |
379 | * @exception SQLException thrown on failure. |
380 | */ |
381 | public void setShort(int parameterIndex, short x) throws SQLException { |
382 | |
383 | checkStatus(); |
384 | try { |
385 | /* JDBC is one-based, DBMS is zero-based */ |
386 | getParms().getParameterForSet(parameterIndex - 1).setValue(x); |
387 | |
388 | } catch (Throwable t) { |
389 | throw EmbedResultSet.noStateChangeException(t); |
390 | } |
391 | } |
392 | |
393 | /** |
394 | * Set a parameter to a Java int value. The driver converts this |
395 | * to a SQL INTEGER value when it sends it to the database. |
396 | * |
397 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
398 | * @param x the parameter value |
399 | * @exception SQLException thrown on failure. |
400 | */ |
401 | public void setInt(int parameterIndex, int x) throws SQLException { |
402 | checkStatus(); |
403 | |
404 | try { |
405 | /* JDBC is one-based, DBMS is zero-based */ |
406 | getParms().getParameterForSet(parameterIndex - 1).setValue(x); |
407 | } catch (Throwable t) { |
408 | throw EmbedResultSet.noStateChangeException(t); |
409 | } |
410 | } |
411 | |
412 | /** |
413 | * Set a parameter to a Java long value. The driver converts this |
414 | * to a SQL BIGINT value when it sends it to the database. |
415 | * |
416 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
417 | * @param x the parameter value |
418 | * @exception SQLException thrown on failure. |
419 | */ |
420 | public void setLong(int parameterIndex, long x) throws SQLException { |
421 | checkStatus(); |
422 | try { |
423 | /* JDBC is one-based, DBMS is zero-based */ |
424 | getParms().getParameterForSet(parameterIndex - 1).setValue(x); |
425 | |
426 | } catch (Throwable t) { |
427 | throw EmbedResultSet.noStateChangeException(t); |
428 | } |
429 | |
430 | } |
431 | |
432 | /** |
433 | * Set a parameter to a Java float value. The driver converts this |
434 | * to a SQL FLOAT value when it sends it to the database. |
435 | * |
436 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
437 | * @param x the parameter value |
438 | * @exception SQLException thrown on failure. |
439 | */ |
440 | public void setFloat(int parameterIndex, float x) throws SQLException { |
441 | checkStatus(); |
442 | try { |
443 | /* JDBC is one-based, DBMS is zero-based */ |
444 | getParms().getParameterForSet(parameterIndex - 1).setValue(x); |
445 | |
446 | } catch (Throwable t) { |
447 | throw EmbedResultSet.noStateChangeException(t); |
448 | } |
449 | |
450 | } |
451 | |
452 | /** |
453 | * Set a parameter to a Java double value. The driver converts this |
454 | * to a SQL DOUBLE value when it sends it to the database. |
455 | * |
456 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
457 | * @param x the parameter value |
458 | * @exception SQLException thrown on failure. |
459 | */ |
460 | public void setDouble(int parameterIndex, double x) throws SQLException { |
461 | checkStatus(); |
462 | |
463 | try { |
464 | /* JDBC is one-based, DBMS is zero-based */ |
465 | getParms().getParameterForSet(parameterIndex - 1).setValue(x); |
466 | |
467 | } catch (Throwable t) { |
468 | throw EmbedResultSet.noStateChangeException(t); |
469 | } |
470 | |
471 | } |
472 | |
473 | /** |
474 | * Set a parameter to a Java String value. The driver converts this |
475 | * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments |
476 | * size relative to the driver's limits on VARCHARs) when it sends |
477 | * it to the database. |
478 | * |
479 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
480 | * @param x the parameter value |
481 | * @exception SQLException thrown on failure. |
482 | */ |
483 | public void setString(int parameterIndex, String x) throws SQLException { |
484 | checkStatus(); |
485 | try { |
486 | /* JDBC is one-based, DBMS is zero-based */ |
487 | getParms().getParameterForSet(parameterIndex - 1).setValue(x); |
488 | |
489 | } catch (Throwable t) { |
490 | throw EmbedResultSet.noStateChangeException(t); |
491 | } |
492 | } |
493 | |
494 | /** |
495 | * Set a parameter to a Java array of bytes. The driver converts |
496 | * this to a SQL VARBINARY or LONGVARBINARY (depending on the |
497 | * argument's size relative to the driver's limits on VARBINARYs) |
498 | * when it sends it to the database. |
499 | * |
500 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
501 | * @param x the parameter value |
502 | * @exception SQLException thrown on failure. |
503 | */ |
504 | public void setBytes(int parameterIndex, byte x[]) throws SQLException { |
505 | checkStatus(); |
506 | |
507 | try { |
508 | /* JDBC is one-based, DBMS is zero-based */ |
509 | getParms().getParameterForSet(parameterIndex - 1).setValue(x); |
510 | |
511 | } catch (Throwable t) { |
512 | throw EmbedResultSet.noStateChangeException(t); |
513 | } |
514 | |
515 | } |
516 | |
517 | /** |
518 | * Set a parameter to a java.sql.Date value. The driver converts this |
519 | * to a SQL DATE value when it sends it to the database. |
520 | * |
521 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
522 | * @param x the parameter value |
523 | * @exception SQLException thrown on failure. |
524 | */ |
525 | public void setDate(int parameterIndex, Date x) throws SQLException { |
526 | setDate( parameterIndex, x, (Calendar) null); |
527 | } |
528 | |
529 | /** |
530 | * Set a parameter to a java.sql.Time value. The driver converts this |
531 | * to a SQL TIME value when it sends it to the database. |
532 | * |
533 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
534 | * @param x the parameter value |
535 | * @exception SQLException thrown on failure. |
536 | */ |
537 | public void setTime(int parameterIndex, Time x) throws SQLException { |
538 | setTime( parameterIndex, x, (Calendar) null); |
539 | } |
540 | |
541 | /** |
542 | * Set a parameter to a java.sql.Timestamp value. The driver |
543 | * converts this to a SQL TIMESTAMP value when it sends it to the |
544 | * database. |
545 | * |
546 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
547 | * @param x the parameter value |
548 | * @exception SQLException thrown on failure. |
549 | */ |
550 | public void setTimestamp(int parameterIndex, Timestamp x) |
551 | throws SQLException { |
552 | setTimestamp( parameterIndex, x, (Calendar) null); |
553 | } |
554 | |
555 | /** |
556 | * We do this inefficiently and read it all in here. The target type |
557 | * is assumed to be a String. |
558 | * |
559 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
560 | * @param x the java input stream which contains the ASCII parameter value |
561 | * @param length the number of bytes in the stream |
562 | * @exception SQLException thrown on failure. |
563 | */ |
564 | public final void setAsciiStream(int parameterIndex, InputStream x, int length) |
565 | throws SQLException { |
566 | checkStatus(); |
567 | |
568 | int jdbcTypeId = getParameterJDBCType(parameterIndex); |
569 | |
570 | switch (jdbcTypeId) { |
571 | case Types.CHAR: |
572 | case Types.VARCHAR: |
573 | case Types.LONGVARCHAR: |
574 | case Types.CLOB: |
575 | break; |
576 | default: |
577 | throw dataTypeConversion(parameterIndex, "java.io.InputStream(ASCII)"); |
578 | } |
579 | |
580 | java.io.Reader r = null; |
581 | |
582 | if (x != null) |
583 | { |
584 | // Use ISO-8859-1 and not US-ASCII as JDBC seems to define |
585 | // ASCII as 8 bits. US-ASCII is 7. |
586 | try { |
587 | r = new java.io.InputStreamReader(x, "ISO-8859-1"); |
588 | } catch (java.io.UnsupportedEncodingException uee) { |
589 | throw new SQLException(uee.getMessage()); |
590 | } |
591 | } |
592 | |
593 | setCharacterStream(parameterIndex, r, length); |
594 | } |
595 | |
596 | /** |
597 | Deprecated in JDBC 3.0 |
598 | * |
599 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
600 | * @param x the java input stream which contains the |
601 | * UNICODE parameter value |
602 | * @param length the number of bytes in the stream |
603 | * @exception SQLException thrown on failure. |
604 | */ |
605 | public void setUnicodeStream(int parameterIndex, InputStream x, int length) |
606 | throws SQLException |
607 | { |
608 | throw Util.notImplemented("setUnicodeStream"); |
609 | } |
610 | |
611 | /** |
612 | * JDBC 2.0 |
613 | * |
614 | * When a very large UNICODE value is input to a LONGVARCHAR |
615 | * parameter, it may be more practical to send it via a |
616 | * java.io.Reader. JDBC will read the data from the stream |
617 | * as needed, until it reaches end-of-file. The JDBC driver will |
618 | * do any necessary conversion from UNICODE to the database char format. |
619 | * |
620 | * <P><B>Note:</B> This stream object can either be a standard |
621 | * Java stream object or your own subclass that implements the |
622 | * standard interface. |
623 | * |
624 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
625 | * @param reader the java reader which contains the UNICODE data |
626 | * @param length the number of characters in the stream |
627 | * @exception SQLException if a database-access error occurs. |
628 | */ |
629 | public final void setCharacterStream(int parameterIndex, |
630 | java.io.Reader reader, |
631 | int length) throws SQLException |
632 | { |
633 | checkStatus(); |
634 | int jdbcTypeId = getParameterJDBCType(parameterIndex); |
635 | switch (jdbcTypeId) { |
636 | case Types.CHAR: |
637 | case Types.VARCHAR: |
638 | case Types.LONGVARCHAR: |
639 | case Types.CLOB: |
640 | break; |
641 | default: |
642 | throw dataTypeConversion(parameterIndex, "java.io.Reader"); |
643 | } |
644 | |
645 | |
646 | setCharacterStreamInternal(parameterIndex, reader, length); |
647 | } |
648 | |
649 | private void setCharacterStreamInternal(int parameterIndex, |
650 | Reader reader, long length) |
651 | throws SQLException |
652 | { |
653 | // check for -ve length |
654 | if (length < 0) |
655 | throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH); |
656 | |
657 | int jdbcTypeId = getParameterJDBCType(parameterIndex); |
658 | |
659 | |
660 | if (reader == null) { |
661 | setNull(parameterIndex, jdbcTypeId); |
662 | return; |
663 | } |
664 | |
665 | /* |
666 | The value stored should not exceed the maximum value that can be |
667 | stored in an integer |
668 | This checking needs to be done because currently derby does not |
669 | support Clob sizes greater than 2G-1 |
670 | */ |
671 | if (length > Integer.MAX_VALUE) |
672 | throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, |
673 | preparedStatement.getParameterTypes() |
674 | [parameterIndex-1].getSQLstring()); |
675 | /* |
676 | We cast the length from long to int. This would'nt be appropriate if |
677 | the limit of 2G-1 is decided to be increased at a later stage. |
678 | */ |
679 | int intLength = (int)length; |
680 | |
681 | try { |
682 | |
683 | ParameterValueSet pvs = getParms(); |
684 | int usableLength = intLength; |
685 | int truncationLength = 0; |
686 | |
687 | // Currently long varchar does not allow for truncation of trailing |
688 | // blanks. |
689 | // For char and varchar types, current mechanism of materializing |
690 | // when using streams seems fine given their max limits. |
691 | // This change is fix for DERBY-352: Insert of clobs using streams |
692 | // should not materialize the entire stream into memory |
693 | // In case of clobs, the truncation of trailing blanks is factored |
694 | // in when reading from the stream without materializing the entire |
695 | // stream, and so the special casing for clob below. |
696 | if (jdbcTypeId == Types.CLOB) |
697 | { |
698 | // Need column width to figure out if truncation is needed |
699 | DataTypeDescriptor dtd[] = preparedStatement |
700 | .getParameterTypes(); |
701 | int colWidth = dtd[parameterIndex - 1].getMaximumWidth(); |
702 | |
703 | // It is possible that the length of the stream passed in is |
704 | // greater than the column width, in which case the data from |
705 | // the stream needs to be truncated. |
706 | // usableLength is the length of the data from stream that can |
707 | // be inserted which is min(colWidth,length) provided |
708 | // length - colWidth has trailing blanks only |
709 | // we have used intLength into which the length variable had |
710 | // been cast to an int and stored |
711 | if (intLength > colWidth) { |
712 | usableLength = colWidth; |
713 | truncationLength = intLength - usableLength; |
714 | } |
715 | } |
716 | |
717 | ReaderToUTF8Stream utfIn = new ReaderToUTF8Stream( |
718 | reader, usableLength, truncationLength); |
719 | |
720 | /* JDBC is one-based, DBMS is zero-based */ |
721 | pvs.getParameterForSet( |
722 | parameterIndex - 1).setValue(utfIn, usableLength); |
723 | |
724 | } catch (StandardException t) { |
725 | throw EmbedResultSet.noStateChangeException(t); |
726 | } |
727 | } |
728 | |
729 | /** |
730 | * |
731 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
732 | * @param x the java input stream which contains the binary parameter value |
733 | * @param length the number of bytes in the stream |
734 | * @exception SQLException thrown on failure. |
735 | */ |
736 | public final void setBinaryStream(int parameterIndex, InputStream x, int length) |
737 | throws SQLException |
738 | { |
739 | |
740 | checkStatus(); |
741 | |
742 | int jdbcTypeId = getParameterJDBCType(parameterIndex); |
743 | switch (jdbcTypeId) { |
744 | case Types.BINARY: |
745 | case Types.VARBINARY: |
746 | case Types.LONGVARBINARY: |
747 | case Types.BLOB: |
748 | break; |
749 | default: |
750 | throw dataTypeConversion(parameterIndex, "java.io.InputStream"); |
751 | } |
752 | |
753 | setBinaryStreamInternal(parameterIndex, x, length); |
754 | } |
755 | |
756 | private void setBinaryStreamInternal(int parameterIndex, InputStream x, |
757 | long length) |
758 | throws SQLException |
759 | { |
760 | |
761 | if ( length < 0 ) |
762 | throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH); |
763 | |
764 | int jdbcTypeId = getParameterJDBCType(parameterIndex); |
765 | if (x == null) { |
766 | setNull(parameterIndex, jdbcTypeId); |
767 | return; |
768 | } |
769 | |
770 | // max number of bytes that can be set to be inserted |
771 | // in Derby is 2Gb-1 (ie Integer.MAX_VALUE). |
772 | // (e.g into a blob column). |
773 | // For now, we cast the length from long to int as a result. |
774 | // If we ever decide to increase these limits for lets say blobs, |
775 | // in that case the cast to int would not be appropriate. |
776 | if ( length > Integer.MAX_VALUE ) { |
777 | throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, |
778 | getEmbedParameterSetMetaData().getParameterTypeName( |
779 | parameterIndex)); |
780 | } |
781 | |
782 | try { |
783 | |
784 | getParms().getParameterForSet(parameterIndex - 1).setValue(new RawToBinaryFormatStream(x, (int)length), (int)length); |
785 | |
786 | } catch (StandardException t) { |
787 | throw EmbedResultSet.noStateChangeException(t); |
788 | } |
789 | } |
790 | |
791 | ///////////////////////////////////////////////////////////////////////// |
792 | // |
793 | // JDBC 2.0 - New public methods |
794 | // |
795 | ///////////////////////////////////////////////////////////////////////// |
796 | |
797 | /** |
798 | * JDBC 2.0 |
799 | * |
800 | * Set null for user-named types and REF type parameters |
801 | * |
802 | * @exception SQLException if a database-access error occurs. |
803 | */ |
804 | public void setNull(int paramIndex, |
805 | int sqlType, |
806 | String typeName) |
807 | throws SQLException |
808 | { |
809 | throw Util.notImplemented("setNull"); |
810 | } |
811 | |
812 | /** |
813 | * JDBC 2.0 |
814 | * |
815 | * Add a set of parameters to the batch. |
816 | * |
817 | * @exception SQLException if a database-access error occurs. |
818 | */ |
819 | public void addBatch() throws SQLException { |
820 | checkStatus(); |
821 | |
822 | // need to synchronized to ensure that two threads |
823 | // don't both create a Vector at the same time. This |
824 | // would lead to one of the set of parameters being thrown |
825 | // away |
826 | synchronized (getConnectionSynchronization()) { |
827 | if (batchStatements == null) |
828 | batchStatements = new Vector(); |
829 | |
830 | //get a clone of the parameterValueSet and save it in the vector |
831 | //which will be used later on at the time of batch execution. |
832 | //This way we will get a copy of the current statement's parameter |
833 | //values rather than a pointer to the statement's parameter value |
834 | //set which will change with every new statement in the batch. |
835 | batchStatements.addElement(getParms().getClone()); |
836 | clearParameters(); |
837 | } |
838 | } |
839 | |
840 | boolean executeBatchElement(Object batchElement) throws SQLException, StandardException { |
841 | |
842 | ParameterValueSet temp = (ParameterValueSet) batchElement; |
843 | |
844 | int numberOfParameters = temp.getParameterCount(); |
845 | |
846 | for (int j=0; j<numberOfParameters; j++) { |
847 | temp.getParameter(j).setInto(this, j + 1); |
848 | } |
849 | |
850 | return super.executeStatement(activation, false, true); |
851 | } |
852 | |
853 | |
854 | |
855 | /** |
856 | * <P>In general, parameter values remain in force for repeated use of a |
857 | * Statement. Setting a parameter value automatically clears its |
858 | * previous value. However, in some cases it is useful to immediately |
859 | * release the resources used by the current parameter values; this can |
860 | * be done by calling clearParameters. |
861 | * @exception SQLException thrown on failure. |
862 | */ |
863 | public void clearParameters() throws SQLException { |
864 | checkStatus(); |
865 | |
866 | ParameterValueSet pvs = getParms(); |
867 | if (pvs != null) |
868 | pvs.clearParameters(); |
869 | } |
870 | |
871 | /** |
872 | * JDBC 2.0 |
873 | * |
874 | * The number, types and properties of a ResultSet's columns |
875 | * are provided by the getMetaData method. |
876 | * |
877 | * @return the description of a ResultSet's columns |
878 | * @exception SQLException Feature not implemented for now. |
879 | */ |
880 | public java.sql.ResultSetMetaData getMetaData() throws SQLException |
881 | { |
882 | checkExecStatus(); |
883 | synchronized (getConnectionSynchronization()) |
884 | { |
885 | //reason for casting is getActivationClass is not available on PreparedStatement |
886 | ExecPreparedStatement execp = (ExecPreparedStatement)preparedStatement; |
887 | |
888 | setupContextStack(); // make sure there's context |
889 | |
890 | try { |
891 | //bug 4579 - if the statement is invalid, regenerate the metadata info |
892 | if (preparedStatement.isValid() == false) |
893 | { |
894 | //need to revalidate the statement here, otherwise getResultDescription would |
895 | //still have info from previous valid statement |
896 | preparedStatement.rePrepare(lcc); |
897 | rMetaData = null; |
898 | } |
899 | //bug 4579 - gcDuringGetMetaData will be null if this is the first time |
900 | //getMetaData call is made. |
901 | //Second check - if the statement was revalidated since last getMetaData call, |
902 | //then gcDuringGetMetaData wouldn't match with current generated class name |
903 | if (gcDuringGetMetaData == null || gcDuringGetMetaData.equals(execp.getActivationClass().getName()) == false) |
904 | { |
905 | rMetaData = null; |
906 | gcDuringGetMetaData = execp.getActivationClass().getName(); |
907 | } |
908 | if (rMetaData == null) |
909 | { |
910 | ResultDescription resd = preparedStatement.getResultDescription(); |
911 | if (resd != null) |
912 | { |
913 | // Internally, the result description has information |
914 | // which is used for insert, update and delete statements |
915 | // Externally, we decided that statements which don't |
916 | // produce result sets such as insert, update and delete |
917 | // should not return ResultSetMetaData. This is enforced |
918 | // here |
919 | String statementType = resd.getStatementType(); |
920 | if (statementType.equals("INSERT") || |
921 | statementType.equals("UPDATE") || |
922 | statementType.equals("DELETE")) |
923 | rMetaData = null; |
924 | else |
925 | rMetaData = newEmbedResultSetMetaData(resd); |
926 | } |
927 | } |
928 | } catch (Throwable t) { |
929 | throw handleException(t); |
930 | } finally { |
931 | restoreContextStack(); |
932 | } |
933 | } |
934 | return rMetaData; |
935 | } |
936 | |
937 | //---------------------------------------------------------------------- |
938 | // Advanced features: |
939 | |
940 | /** |
941 | * The interface says that the type of the Object parameter must |
942 | * be compatible with the type of the targetSqlType. We check that, |
943 | * and if it flies, we expect the underlying engine to do the |
944 | * required conversion once we pass in the value using its type. |
945 | * So, an Integer converting to a CHAR is done via setInteger() |
946 | * support on the underlying CHAR type. |
947 | * |
948 | * <p>If x is null, it won't tell us its type, so we pass it on to setNull |
949 | * |
950 | * @param parameterIndex The first parameter is 1, the second is 2, ... |
951 | * @param x The object containing the input parameter value |
952 | * @param targetSqlType The SQL type (as defined in java.sql.Types) to be |
953 | * sent to the database. The scale argument may further qualify this type. |
954 | * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types |
955 | * this is the number of digits after the decimal. For all other |
956 | * types this value will be ignored, |
957 | * @exception SQLException thrown on failure. |
958 | */ |
959 | public final void setObject(int parameterIndex, Object x, int targetSqlType, int scale) |
960 | throws SQLException { |
961 | |
962 | if (x == null) { |
963 | setNull(parameterIndex, targetSqlType); |
964 | return; |
965 | } |
966 | |
967 | int paramJDBCType = getParameterJDBCType(parameterIndex); |
968 | |
969 | if (paramJDBCType != java.sql.Types.JAVA_OBJECT) { |
970 | if (!DataTypeDescriptor.isJDBCTypeEquivalent(paramJDBCType, targetSqlType)) { |
971 | throw dataTypeConversion(parameterIndex, Util.typeName(targetSqlType)); |
972 | } |
973 | } |
974 | |
975 | setObject(parameterIndex, x); |
976 | |
977 | /* |
978 | * If the parameter type is DECIMAL or NUMERIC, then |
979 | * we need to set the correct scale or set it |
980 | * to the default which is zero for setObject. |
981 | */ |
982 | if ((paramJDBCType == Types.DECIMAL) || |
983 | (paramJDBCType == Types.NUMERIC)) |
984 | { |
985 | setScale(parameterIndex, scale); |
986 | } |
987 | } |
988 | |
989 | /** |
990 | * This method is like setObject above, but assumes a scale of zero. |
991 | * @exception SQLException thrown on failure. |
992 | */ |
993 | public final void setObject(int parameterIndex, Object x, int targetSqlType) |
994 | throws SQLException { |
995 | setObject(parameterIndex, x, targetSqlType, 0); |
996 | } |
997 | |
998 | /** |
999 | * <p>Set the value of a parameter using an object; use the |
1000 | * java.lang equivalent objects for integral values. |
1001 | * |
1002 | * <p>The JDBC specification specifies a standard mapping from |
1003 | * Java Object types to SQL types. The given argument java object |
1004 | * will be converted to the corresponding SQL type before being |
1005 | * sent to the database. |
1006 | * |
1007 | * <p>Note that this method may be used to pass datatabase |
1008 | * specific abstract data types, by using a Driver specific Java |
1009 | * type. |
1010 | * |
1011 | * @param parameterIndex The first parameter is 1, the second is 2, ... |
1012 | * @param x The object containing the input parameter value |
1013 | * @exception SQLException thrown on failure. |
1014 | */ |
1015 | public final void setObject(int parameterIndex, Object x) throws SQLException { |
1016 | checkStatus(); |
1017 | |
1018 | |
1019 | int colType = getParameterJDBCType(parameterIndex); |
1020 | |
1021 | // JDBC Tutorial and Reference books states in the PreparedStatement |
1022 | // overview, that passing a untyped null into setObject() is not allowed. |
1023 | // JCC disallows this, basically SQL can not handle a untyped NULL. |
1024 | // Section 25.1.6 (Third edition), 24.1.5 (Second Edition) |
1025 | |
1026 | if (x == null) { |
1027 | //setNull(parameterIndex, colType); |
1028 | //return; |
1029 | throw dataTypeConversion(parameterIndex, "null"); |
1030 | } |
1031 | |
1032 | if (colType == org.apache.derby.iapi.reference.JDBC20Translation.SQL_TYPES_JAVA_OBJECT) { |
1033 | try { |
1034 | /* JDBC is one-based, DBMS is zero-based */ |
1035 | getParms().setParameterAsObject(parameterIndex - 1, x); |
1036 | return; |
1037 | |
1038 | } catch (Throwable t) { |
1039 | throw EmbedResultSet.noStateChangeException(t); |
1040 | } |
1041 | } |
1042 | |
1043 | |
1044 | // Need to do instanceof checks here so that the behaviour |
1045 | // for these calls is consistent with the matching setXXX() value. |
1046 | |
1047 | // These are the supported setObject conversions from JDBC 3.0 table B5 |
1048 | |
1049 | if (x instanceof String) { |
1050 | setString(parameterIndex, (String) x); |
1051 | return; |
1052 | } |
1053 | |
1054 | if (x instanceof Boolean) { |
1055 | setBoolean(parameterIndex, ((Boolean) x).booleanValue()); |
1056 | return; |
1057 | } |
1058 | if (x instanceof Integer) { |
1059 | setInt(parameterIndex, ((Integer) x).intValue()); |
1060 | return; |
1061 | } |
1062 | if (x instanceof Long) { |
1063 | setLong(parameterIndex, ((Long) x).longValue()); |
1064 | return; |
1065 | } |
1066 | |
1067 | if (x instanceof Float) { |
1068 | setFloat(parameterIndex, ((Float) x).floatValue()); |
1069 | return; |
1070 | } |
1071 | if (x instanceof Double) { |
1072 | setDouble(parameterIndex, ((Double) x).doubleValue()); |
1073 | return; |
1074 | } |
1075 | |
1076 | if (x instanceof byte[]) { |
1077 | setBytes(parameterIndex, (byte[]) x); |
1078 | return; |
1079 | } |
1080 | |
1081 | if (x instanceof Date) { |
1082 | setDate(parameterIndex, (Date) x); |
1083 | return; |
1084 | } |
1085 | if (x instanceof Time) { |
1086 | setTime(parameterIndex, (Time) x); |
1087 | return; |
1088 | } |
1089 | if (x instanceof Timestamp) { |
1090 | setTimestamp(parameterIndex, (Timestamp) x); |
1091 | return; |
1092 | } |
1093 | |
1094 | if (x instanceof Blob) { |
1095 | setBlob(parameterIndex, (Blob) x); |
1096 | return; |
1097 | } |
1098 | if (x instanceof Clob) { |
1099 | setClob(parameterIndex, (Clob) x); |
1100 | return; |
1101 | } |
1102 | |
1103 | if (setObjectConvert(parameterIndex, x)) |
1104 | return; |
1105 | |
1106 | |
1107 | throw dataTypeConversion(parameterIndex, x.getClass().getName()); |
1108 | |
1109 | } |
1110 | |
1111 | /** |
1112 | Allow explict setObject conversions by sub-classes for classes |
1113 | not supported by this variant. E.g. BigDecimal |
1114 | This top-level implementation always returns false. |
1115 | |
1116 | @return true if the object was set successfully, false if no valid |
1117 | conversion exists. |
1118 | |
1119 | @exception SQLException value could not be set. |
1120 | */ |
1121 | boolean setObjectConvert(int parameterIndex, Object x) throws SQLException |
1122 | { |
1123 | return false; |
1124 | } |
1125 | |
1126 | /** |
1127 | * @see java.sql.Statement#execute |
1128 | * @exception SQLException thrown on failure. |
1129 | */ |
1130 | public final boolean execute() throws SQLException { |
1131 | boolean ret=false; |
1132 | try{ |
1133 | ret = executeStatement(activation, false, false); |
1134 | } catch(SQLException sqle) { |
1135 | checkStatementValidity(sqle); |
1136 | } |
1137 | return ret; |
1138 | } |
1139 | /** |
1140 | * Set a parameter to a java.sql.Date value. The driver converts this |
1141 | * to a SQL DATE value when it sends it to the database. |
1142 | * |
1143 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
1144 | * @param x the parameter value |
1145 | * @exception SQLException Feature not implemented for now. |
1146 | */ |
1147 | public final void setDate(int parameterIndex, java.sql.Date x, Calendar cal) |
1148 | throws SQLException |
1149 | { |
1150 | checkStatus(); |
1151 | try { |
1152 | /* JDBC is one-based, DBMS is zero-based */ |
1153 | getParms().getParameterForSet(parameterIndex - 1).setValue(x, cal); |
1154 | |
1155 | } catch (Throwable t) { |
1156 | throw EmbedResultSet.noStateChangeException(t); |
1157 | } |
1158 | } |
1159 | |
1160 | /** |
1161 | * Set a parameter to a java.sql.Time value. The driver converts this |
1162 | * to a SQL TIME value when it sends it to the database. |
1163 | * |
1164 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
1165 | * @param x the parameter value |
1166 | * @exception SQLException Feature not implemented for now. |
1167 | */ |
1168 | public final void setTime(int parameterIndex, java.sql.Time x, Calendar cal) |
1169 | throws SQLException |
1170 | { |
1171 | checkStatus(); |
1172 | try { |
1173 | /* JDBC is one-based, DBMS is zero-based */ |
1174 | getParms().getParameterForSet(parameterIndex - 1).setValue(x, cal); |
1175 | |
1176 | } catch (Throwable t) { |
1177 | throw EmbedResultSet.noStateChangeException(t); |
1178 | } |
1179 | } |
1180 | |
1181 | /** |
1182 | * Set a parameter to a java.sql.Timestamp value. The driver |
1183 | * converts this to a SQL TIMESTAMP value when it sends it to the |
1184 | * database. |
1185 | * |
1186 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
1187 | * @param x the parameter value |
1188 | * @exception SQLException Feature not implemented for now. |
1189 | */ |
1190 | public final void setTimestamp(int parameterIndex, java.sql.Timestamp x, Calendar cal) |
1191 | throws SQLException |
1192 | { |
1193 | checkStatus(); |
1194 | try { |
1195 | /* JDBC is one-based, DBMS is zero-based */ |
1196 | getParms().getParameterForSet(parameterIndex - 1).setValue(x, cal); |
1197 | |
1198 | } catch (StandardException t) { |
1199 | throw EmbedResultSet.noStateChangeException(t); |
1200 | } |
1201 | } |
1202 | |
1203 | |
1204 | /** |
1205 | * JDBC 2.0 |
1206 | * |
1207 | * Set a BLOB parameter. |
1208 | * |
1209 | * @param i the first parameter is 1, the second is 2, ... |
1210 | * @param x an object representing a BLOB |
1211 | */ |
1212 | public void setBlob (int i, Blob x) |
1213 | throws SQLException |
1214 | { |
1215 | checkStatus(); |
1216 | int colType; |
1217 | synchronized (getConnectionSynchronization()) |
1218 | { |
1219 | colType = getParameterJDBCType(i); |
1220 | } |
1221 | // DB2: only allow setBlob on a BLOB column. |
1222 | if (colType != Types.BLOB) |
1223 | throw dataTypeConversion(i, "java.sql.Blob"); |
1224 | |
1225 | if (x == null) |
1226 | setNull(i, Types.BLOB); |
1227 | else |
1228 | { |
1229 | // Note, x.length() needs to be called before retrieving the |
1230 | // stream using x.getBinaryStream() because EmbedBlob.length() |
1231 | // will read from the stream and drain some part of the stream |
1232 | // Hence the need to declare this local variable - streamLength |
1233 | long streamLength = x.length(); |
1234 | setBinaryStreamInternal(i, x.getBinaryStream(), streamLength); |
1235 | } |
1236 | } |
1237 | |
1238 | /** |
1239 | * JDBC 2.0 |
1240 | * |
1241 | * Set a CLOB parameter. |
1242 | * |
1243 | * @param i the first parameter is 1, the second is 2, ... |
1244 | * @param x an object representing a CLOB |
1245 | */ |
1246 | public void setClob (int i, Clob x) |
1247 | throws SQLException |
1248 | { |
1249 | checkStatus(); |
1250 | int colType; |
1251 | synchronized (getConnectionSynchronization()) |
1252 | { |
1253 | colType = getParameterJDBCType(i); |
1254 | } |
1255 | |
1256 | // DB2, only allow setClob on a CLOB column. |
1257 | if (colType != Types.CLOB) |
1258 | throw dataTypeConversion(i, "java.sql.Clob"); |
1259 | |
1260 | if (x == null) |
1261 | setNull(i, Types.CLOB); |
1262 | else |
1263 | { |
1264 | // 1. max number of characters that can be inserted into a clob column |
1265 | // is 2Gb-1 which is Integer.MAX_INT. |
1266 | // This means that we do not allow any inserts of clobs where |
1267 | // clob.length() > Integer.MAX_INT. For now, we cast the x.length() |
1268 | // to int as a result. This will work ok for valid clob values that |
1269 | // derby supports. If we ever decide to increase these limits for clobs, in that |
1270 | // case the cast of x.Length() to int would not be appropriate. |
1271 | // 2. Note, x.length() needs to be called before retrieving the |
1272 | // stream using x.getCharacterStream() because EmbedClob.length() |
1273 | // will read from the stream and drain the stream. |
1274 | // Hence the need to declare this local variable - streamLength |
1275 | long streamLength = x.length(); |
1276 | |
1277 | setCharacterStreamInternal(i, x.getCharacterStream(), streamLength); |
1278 | } |
1279 | |
1280 | } |
1281 | |
1282 | /** |
1283 | * Get the ParameterValueSet from the activation |
1284 | * |
1285 | * |
1286 | * @return The ParameterValueSet for the activation |
1287 | * |
1288 | */ |
1289 | public final ParameterValueSet getParms() { |
1290 | |
1291 | return activation.getParameterValueSet(); |
1292 | } |
1293 | |
1294 | |
1295 | /** |
1296 | Check the parameterINdex is in range and return the |
1297 | array of type descriptors. |
1298 | |
1299 | @exception SQLException parameter is out of range |
1300 | */ |
1301 | protected final DataTypeDescriptor[] getTypes(int parameterIndex) |
1302 | throws SQLException { |
1303 | |
1304 | DataTypeDescriptor[] types = preparedStatement.getParameterTypes(); |
1305 | |
1306 | if (types == null) { |
1307 | throw newSQLException(SQLState.NO_INPUT_PARAMETERS); |
1308 | } |
1309 | |
1310 | /* Check that the parameterIndex is in range. */ |
1311 | if (parameterIndex < 1 || |
1312 | parameterIndex > types.length) { |
1313 | |
1314 | /* This message matches the one used by the DBMS */ |
1315 | throw newSQLException(SQLState.LANG_INVALID_PARAM_POSITION, |
1316 | new Integer(parameterIndex), new Integer(types.length)); |
1317 | } |
1318 | return types; |
1319 | } |
1320 | |
1321 | /** |
1322 | Get the target JDBC type for a parameter. Will throw exceptions |
1323 | if the parameter index is out of range |
1324 | |
1325 | @exception SQLException parameter is out of range |
1326 | */ |
1327 | protected int getParameterJDBCType(int parameterIndex) |
1328 | throws SQLException { |
1329 | |
1330 | DataTypeDescriptor[] types = getTypes(parameterIndex); |
1331 | |
1332 | int type = types[parameterIndex -1] == null ? |
1333 | Types.OTHER : |
1334 | types[parameterIndex - 1].getTypeId().getJDBCTypeId(); |
1335 | |
1336 | if (SanityManager.DEBUG) { |
1337 | //int pmType = getEmbedParameterSetMetaData().getParameterType(parameterIndex); |
1338 | //if (type != pmType) { |
1339 | //SanityManager.THROWASSERT("MISMATCH PARAMETER META DATA param " + parameterIndex + " types " + type + " != " + pmType + "\n" + SQLText); |
1340 | //} |
1341 | } |
1342 | |
1343 | return type; |
1344 | } |
1345 | |
1346 | /** |
1347 | * Set the scale of a parameter. |
1348 | * |
1349 | * @param parameterIndex The first parameter is 1, the second is 2, ... |
1350 | * @param scale The scale |
1351 | * @exception SQLException thrown on failure. |
1352 | */ |
1353 | private void setScale(int parameterIndex, int scale) |
1354 | throws SQLException |
1355 | { |
1356 | checkStatus(); |
1357 | |
1358 | if (scale < 0) |
1359 | throw newSQLException(SQLState.BAD_SCALE_VALUE, new Integer(scale)); |
1360 | |
1361 | try { |
1362 | |
1363 | ParameterValueSet pvs = getParms(); |
1364 | |
1365 | /* JDBC is one-based, DBMS is zero-based */ |
1366 | DataValueDescriptor value = pvs.getParameter(parameterIndex - 1); |
1367 | |
1368 | |
1369 | int origvaluelen = value.getLength(); |
1370 | ((VariableSizeDataValue) |
1371 | value).setWidth(VariableSizeDataValue.IGNORE_PRECISION, |
1372 | scale, |
1373 | false); |
1374 | |
1375 | if (value.getLength() < origvaluelen) |
1376 | { |
1377 | activation.addWarning(StandardException.newWarning(SQLState.LANG_VALUE_TRUNCATED, value.getString())); |
1378 | } |
1379 | |
1380 | } catch (StandardException t) { |
1381 | throw EmbedResultSet.noStateChangeException(t); |
1382 | } |
1383 | } |
1384 | |
1385 | |
1386 | /** |
1387 | * Immitate the function in JDBC 3.0 |
1388 | * |
1389 | * Retrieves the number, types and properties of this PreparedStatement |
1390 | * object's parameters. |
1391 | * |
1392 | * @return a EmbedParameterSetMetaData object that contains information about the |
1393 | * number, types and properties of this PreparedStatement object's parameters. |
1394 | * @exception SQLException if a database access error occurs |
1395 | */ |
1396 | public EmbedParameterSetMetaData getEmbedParameterSetMetaData() |
1397 | throws SQLException |
1398 | { |
1399 | checkExecStatus(); |
1400 | return new EmbedParameterSetMetaData( |
1401 | getParms(), preparedStatement.getParameterTypes()); |
1402 | |
1403 | } |
1404 | /** |
1405 | * JDBC 3.0 |
1406 | * |
1407 | * Sets the designated parameter to the given java.net.URL value. The driver |
1408 | * converts this to an SQL DATALINK value when it sends it to the database. |
1409 | * |
1410 | * @param parameterIndex - the first parameter is 1, the second is 2, ... |
1411 | * @param x - the java.net.URL object to be set |
1412 | * @exception SQLException Feature not implemented for now. |
1413 | */ |
1414 | public final void setURL(int parameterIndex, java.net.URL x) |
1415 | throws SQLException |
1416 | { |
1417 | throw Util.notImplemented(); |
1418 | } |
1419 | |
1420 | // |
1421 | // methods to be overridden in subimplementations |
1422 | // that want to stay within their subimplementation. |
1423 | // |
1424 | protected EmbedResultSetMetaData newEmbedResultSetMetaData(ResultDescription resultDesc) { |
1425 | |
1426 | return factory.newEmbedResultSetMetaData(resultDesc.getColumnInfo()); |
1427 | } |
1428 | |
1429 | public String toString() { |
1430 | |
1431 | if (activation != null) |
1432 | return activation.getPreparedStatement().getObjectName(); |
1433 | return super.toString(); |
1434 | } |
1435 | |
1436 | /* |
1437 | ** |
1438 | */ |
1439 | public void transferParameters(EmbedPreparedStatement newStatement) throws SQLException { |
1440 | |
1441 | try { |
1442 | newStatement.activation.setParameters(getParms(), preparedStatement.getParameterTypes()); |
1443 | } catch (StandardException se) { |
1444 | throw EmbedResultSet.noStateChangeException(se); |
1445 | } |
1446 | } |
1447 | |
1448 | boolean executeStatement(Activation a, |
1449 | boolean executeQuery, boolean executeUpdate) |
1450 | throws SQLException { |
1451 | |
1452 | checkExecStatus(); |
1453 | checkIfInMiddleOfBatch(); |
1454 | clearResultSets(); |
1455 | return super.executeStatement(a, executeQuery, executeUpdate); |
1456 | } |
1457 | |
1458 | final SQLException dataTypeConversion(int column, String sourceType) |
1459 | throws SQLException { |
1460 | SQLException se = newSQLException(SQLState.LANG_DATA_TYPE_GET_MISMATCH, getEmbedParameterSetMetaData().getParameterTypeName(column), |
1461 | sourceType); |
1462 | return se; |
1463 | } |
1464 | /* |
1465 | * This method is used to initialize the BrokeredConnectionControl |
1466 | * variable with its implementation. This method will be called in the |
1467 | * BrokeredConnectionControl class |
1468 | * |
1469 | * @param control used to call the onStatementClose and |
1470 | * onStatementErrorOccurred methods that have logic to |
1471 | * raise StatementEvents for the close and error events |
1472 | * on the PreparedStatement |
1473 | * |
1474 | */ |
1475 | public void setBrokeredConnectionControl(BrokeredConnectionControl control) { |
1476 | bcc = control; |
1477 | } |
1478 | |
1479 | /* |
1480 | * Method calls onStatementError occurred on the |
1481 | * BrokeredConnectionControl class after checking the |
1482 | * SQLState of the SQLException thrown. |
1483 | * |
1484 | * In the case that a XAConnection is involved in the creation of this |
1485 | * PreparedStatement for e.g in the following case |
1486 | * |
1487 | * <code> |
1488 | * XAConnection xaconn = xadatasource.getXAConnection();//where xadatasource is an object of XADataSource |
1489 | * Connection conn = xaconnection.getConnection(); |
1490 | * PreparedStatement ps = conn.preparedStatement("values 1"); |
1491 | * </code> |
1492 | * |
1493 | * In the above case the PreparedStatement will actually be a |
1494 | * BrokeredPreparedStatement40 object. Hence when we call |
1495 | * bcc.onStatementClose and pass the PreparedStatement that caused it |
1496 | * applicationStatement will be the appropriate choice since it will |
1497 | * contain the appropriate instance of PreparedStatement in each case |
1498 | * |
1499 | */ |
1500 | |
1501 | private void checkStatementValidity(SQLException sqle) throws SQLException { |
1502 | /* |
1503 | * Check if the exception has occurred because the connection |
1504 | * associated with the PreparedStatement has been closed |
1505 | */ |
1506 | if(bcc != null && isClosed()) { |
1507 | //call the BrokeredConnectionControl interface method |
1508 | //onStatementErrorOccurred |
1509 | bcc.onStatementErrorOccurred((java.sql.PreparedStatement) |
1510 | applicationStatement,sqle); |
1511 | } |
1512 | throw sqle; |
1513 | } |
1514 | |
1515 | |
1516 | //jdbc 4.0 methods |
1517 | |
1518 | |
1519 | /** |
1520 | * Sets the designated parameter to a Reader object. |
1521 | * |
1522 | * @param parameterIndex index of the first parameter is 1, the second is 2, ... |
1523 | * @param reader An object that contains the data to set the parameter value to. |
1524 | * @param length the number of characters in the parameter data. |
1525 | * @throws SQLException if parameterIndex does not correspond to a parameter |
1526 | * marker in the SQL statement, or if the length specified is less than zero. |
1527 | * |
1528 | */ |
1529 | |
1530 | |
1531 | public void setClob(int parameterIndex, Reader reader, long length) |
1532 | throws SQLException{ |
1533 | checkStatus(); |
1534 | int colType; |
1535 | synchronized(getConnectionSynchronization()) { |
1536 | colType = getParameterJDBCType(parameterIndex); |
1537 | if(colType != Types.CLOB) |
1538 | throw dataTypeConversion(parameterIndex, "java.sql.Clob"); |
1539 | |
1540 | setCharacterStreamInternal(parameterIndex,reader,length); |
1541 | } |
1542 | } |
1543 | |
1544 | /** |
1545 | * Sets the designated parameter to a InputStream object. |
1546 | * |
1547 | * @param parameterIndex index of the first parameter is 1, |
1548 | * the second is 2, ... |
1549 | * @param inputStream An object that contains the data to set the parameter |
1550 | * value to. |
1551 | * @param length the number of bytes in the parameter data. |
1552 | * @throws SQLException if parameterIndex does not correspond |
1553 | * to a parameter marker in the SQL statement, if the length specified |
1554 | * is less than zero or if the number of bytes in the inputstream does not match |
1555 | * the specfied length. |
1556 | */ |
1557 | |
1558 | |
1559 | public void setBlob(int parameterIndex, InputStream inputStream, long length) |
1560 | throws SQLException{ |
1561 | checkStatus(); |
1562 | int colType; |
1563 | synchronized (getConnectionSynchronization()) { |
1564 | colType = getParameterJDBCType(parameterIndex); |
1565 | if (colType != Types.BLOB) |
1566 | throw dataTypeConversion(parameterIndex, "java.sql.Blob"); |
1567 | |
1568 | setBinaryStreamInternal(parameterIndex,inputStream,length); |
1569 | } |
1570 | } |
1571 | } |