1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.sql.GenericParameterValueSet |
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.sql; |
22 | |
23 | import org.apache.derby.iapi.services.loader.ClassFactory; |
24 | import org.apache.derby.iapi.services.loader.ClassInspector; |
25 | import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; |
26 | |
27 | import org.apache.derby.iapi.sql.ParameterValueSet; |
28 | |
29 | import org.apache.derby.iapi.types.DataTypeDescriptor; |
30 | import org.apache.derby.iapi.types.DataValueFactory; |
31 | import org.apache.derby.iapi.types.DataValueDescriptor; |
32 | import org.apache.derby.iapi.types.UserDataValue; |
33 | |
34 | import org.apache.derby.iapi.reference.SQLState; |
35 | |
36 | import org.apache.derby.iapi.error.StandardException; |
37 | |
38 | import org.apache.derby.iapi.services.sanity.SanityManager; |
39 | |
40 | import java.io.InputStream; |
41 | import java.sql.Date; |
42 | import java.sql.Time; |
43 | import java.sql.Timestamp; |
44 | import java.sql.Types; |
45 | import org.apache.derby.iapi.reference.JDBC30Translation; |
46 | |
47 | /** |
48 | * Implementation of ParameterValueSet |
49 | * |
50 | * @see ParameterValueSet |
51 | * |
52 | * @author Jeff Lichtman |
53 | */ |
54 | |
55 | final class GenericParameterValueSet implements ParameterValueSet |
56 | { |
57 | //all this has to be copied in the clone constructor |
58 | private final GenericParameter[] parms; |
59 | final ClassInspector ci; |
60 | private final boolean hasReturnOutputParam; |
61 | |
62 | |
63 | /** |
64 | * Constructor for a GenericParameterValueSet |
65 | * |
66 | * @param numParms The number of parameters in the new ParameterValueSet |
67 | * @param hasReturnOutputParam if we have a ? = call syntax. Note that |
68 | * this is NOT the same thing as an output parameter -- return |
69 | * output parameters are special cases of output parameters. |
70 | */ |
71 | GenericParameterValueSet(ClassInspector ci, int numParms, boolean hasReturnOutputParam) |
72 | { |
73 | this.ci = ci; |
74 | this.hasReturnOutputParam = hasReturnOutputParam; |
75 | parms = new GenericParameter[numParms]; |
76 | for (int i = 0; i < numParms; i++) |
77 | { |
78 | /* |
79 | ** Last param is if this is a return output param. True if |
80 | ** we have an output param and we are on the 1st parameter. |
81 | */ |
82 | parms[i] = new GenericParameter(this, (hasReturnOutputParam && i == 0)); |
83 | } |
84 | } |
85 | |
86 | /* |
87 | ** Construct a pvs by cloning a pvs. |
88 | */ |
89 | private GenericParameterValueSet(int numParms, GenericParameterValueSet pvs) |
90 | { |
91 | this.hasReturnOutputParam = pvs.hasReturnOutputParam; |
92 | this.ci = pvs.ci; |
93 | parms = new GenericParameter[numParms]; |
94 | for (int i = 0; i < numParms; i++) |
95 | { |
96 | parms[i] = pvs.getGenericParameter(i).getClone(this); |
97 | } |
98 | } |
99 | |
100 | /* |
101 | ** ParameterValueSet interface methods |
102 | */ |
103 | |
104 | /** |
105 | * Initialize the set by allocating a holder DataValueDescriptor object |
106 | * for each parameter. |
107 | */ |
108 | public void initialize(DataTypeDescriptor[] types) |
109 | { |
110 | for (int i = 0; i < parms.length; i++) |
111 | { |
112 | DataTypeDescriptor dtd = types[i]; |
113 | |
114 | parms[i].initialize(dtd.getNull(), |
115 | dtd.getJDBCTypeId(), dtd.getTypeId().getCorrespondingJavaTypeName()); |
116 | } |
117 | } |
118 | |
119 | public void setParameterMode(int position, int mode) { |
120 | parms[position].parameterMode = (short) mode; |
121 | } |
122 | |
123 | /** |
124 | * @see ParameterValueSet#clearParameters |
125 | */ |
126 | public void clearParameters() |
127 | { |
128 | for (int i = 0; i < parms.length; i++) |
129 | { |
130 | parms[i].clear(); |
131 | } |
132 | } |
133 | |
134 | /** |
135 | * Returns the number of parameters in this set. |
136 | * |
137 | * @return The number of parameters in this set. |
138 | */ |
139 | public int getParameterCount() |
140 | { |
141 | return parms.length; |
142 | } |
143 | |
144 | /** |
145 | * Returns the parameter value at the given position. |
146 | * |
147 | * @return The parameter at the given position. |
148 | * @exception StandardException Thrown on error |
149 | */ |
150 | public DataValueDescriptor getParameter( int position ) throws StandardException |
151 | { |
152 | try { |
153 | return parms[position].getValue(); |
154 | } catch (ArrayIndexOutOfBoundsException e) { |
155 | checkPosition(position); |
156 | return null; |
157 | } |
158 | } |
159 | |
160 | |
161 | |
162 | public DataValueDescriptor getParameterForSet(int position) throws StandardException { |
163 | |
164 | try { |
165 | |
166 | GenericParameter gp = parms[position]; |
167 | if (gp.parameterMode == JDBC30Translation.PARAMETER_MODE_OUT) |
168 | throw StandardException.newException(SQLState.LANG_RETURN_OUTPUT_PARAM_CANNOT_BE_SET); |
169 | |
170 | gp.isSet = true; |
171 | |
172 | return gp.getValue(); |
173 | } catch (ArrayIndexOutOfBoundsException e) { |
174 | checkPosition(position); |
175 | return null; |
176 | } |
177 | |
178 | } |
179 | |
180 | public DataValueDescriptor getParameterForGet(int position) throws StandardException { |
181 | |
182 | try { |
183 | |
184 | GenericParameter gp = parms[position]; |
185 | |
186 | switch (gp.parameterMode) { |
187 | case JDBC30Translation.PARAMETER_MODE_IN: |
188 | case JDBC30Translation.PARAMETER_MODE_UNKNOWN: |
189 | throw StandardException.newException(SQLState.LANG_NOT_OUTPUT_PARAMETER, Integer.toString(position + 1)); |
190 | } |
191 | |
192 | return gp.getValue(); |
193 | } catch (ArrayIndexOutOfBoundsException e) { |
194 | checkPosition(position); |
195 | return null; |
196 | } |
197 | } |
198 | |
199 | public void setParameterAsObject(int position, Object value) throws StandardException { |
200 | |
201 | UserDataValue dvd = (UserDataValue) getParameterForSet(position); |
202 | |
203 | GenericParameter gp = parms[position]; |
204 | if (value != null) { |
205 | |
206 | { |
207 | |
208 | boolean throwError; |
209 | ClassNotFoundException t = null; |
210 | try { |
211 | throwError = !ci.instanceOf(gp.declaredClassName, value); |
212 | } catch (ClassNotFoundException cnfe) { |
213 | t = cnfe; |
214 | throwError = true; |
215 | } |
216 | |
217 | if (throwError) { |
218 | throw StandardException.newException(SQLState.LANG_DATA_TYPE_SET_MISMATCH, t, |
219 | ClassInspector.readableClassName(value.getClass()), gp.declaredClassName); |
220 | } |
221 | } |
222 | |
223 | } |
224 | |
225 | dvd.setValue(value); |
226 | } |
227 | |
228 | /** |
229 | * @see ParameterValueSet#allAreSet |
230 | */ |
231 | public boolean allAreSet() |
232 | { |
233 | for (int i = 0; i < parms.length; i++) |
234 | { |
235 | GenericParameter gp = parms[i]; |
236 | if (!gp.isSet) |
237 | { |
238 | switch (gp.parameterMode) { |
239 | case JDBC30Translation.PARAMETER_MODE_OUT: |
240 | break; |
241 | case JDBC30Translation.PARAMETER_MODE_IN_OUT: |
242 | case JDBC30Translation.PARAMETER_MODE_UNKNOWN: |
243 | case JDBC30Translation.PARAMETER_MODE_IN: |
244 | return false; |
245 | } |
246 | } |
247 | } |
248 | |
249 | return true; |
250 | } |
251 | |
252 | /** |
253 | * @see ParameterValueSet#transferDataValues |
254 | */ |
255 | public void transferDataValues(ParameterValueSet pvstarget) throws StandardException |
256 | { |
257 | // don't take application's values for return output parameters |
258 | int firstParam = pvstarget.hasReturnOutputParameter() ? 1 : 0; |
259 | for (int i = firstParam; i < parms.length;i++) |
260 | { |
261 | |
262 | GenericParameter oldp = parms[i]; |
263 | |
264 | if (oldp.registerOutType != Types.NULL) { |
265 | |
266 | pvstarget.registerOutParameter(i, oldp.registerOutType, oldp.registerOutScale); |
267 | |
268 | } |
269 | |
270 | if (oldp.isSet) |
271 | { |
272 | pvstarget.getParameterForSet(i).setValue(oldp.getValue()); |
273 | } |
274 | } |
275 | } |
276 | |
277 | GenericParameter getGenericParameter(int position) |
278 | { |
279 | return(parms[position]); |
280 | } |
281 | |
282 | /* Class implementation */ |
283 | public String toString() |
284 | { |
285 | /* This method needed for derby.language.logStatementText=true. |
286 | * Do not put under SanityManager.DEBUG. |
287 | */ |
288 | StringBuffer strbuf = new StringBuffer(); |
289 | |
290 | for (int ctr = 0; ctr < parms.length; ctr++) |
291 | { |
292 | strbuf.append("begin parameter #" + (ctr + 1) + ": "); |
293 | strbuf.append(parms[ctr].toString()); |
294 | strbuf.append(" :end parameter "); |
295 | } |
296 | |
297 | return strbuf.toString(); |
298 | } |
299 | |
300 | /** |
301 | * Check the position number for a parameter and throw an exception if |
302 | * it is out of range. |
303 | * |
304 | * @param position The position number to check |
305 | * |
306 | * @exception StandardException Thrown if position number is |
307 | * out of range. |
308 | */ |
309 | private void checkPosition(int position) throws StandardException |
310 | { |
311 | if (position < 0 || position >= parms.length) |
312 | { |
313 | |
314 | if (parms.length == 0) |
315 | throw StandardException.newException(SQLState.NO_INPUT_PARAMETERS); |
316 | |
317 | throw StandardException.newException(SQLState.LANG_INVALID_PARAM_POSITION, |
318 | String.valueOf(position+1), |
319 | String.valueOf(parms.length)); |
320 | } |
321 | } |
322 | |
323 | |
324 | public ParameterValueSet getClone() |
325 | { |
326 | return(new GenericParameterValueSet(parms.length, this)); |
327 | } |
328 | |
329 | ////////////////////////////////////////////////////////////////// |
330 | // |
331 | // CALLABLE STATEMENT |
332 | // |
333 | ////////////////////////////////////////////////////////////////// |
334 | |
335 | /** |
336 | * Mark the parameter as an output parameter. |
337 | * |
338 | * @param parameterIndex The ordinal parameterIndex of a parameter to set |
339 | * to the given value. |
340 | * @param sqlType A type from java.sql.Types |
341 | * @param scale the scale to use. -1 means ignore scale |
342 | * |
343 | * @exception StandardException on error |
344 | */ |
345 | public void registerOutParameter(int parameterIndex, int sqlType, int scale) |
346 | throws StandardException |
347 | { |
348 | checkPosition(parameterIndex); |
349 | parms[parameterIndex].setOutParameter(sqlType, scale); |
350 | } |
351 | |
352 | /** |
353 | * Validate the parameters. This is done for situations where |
354 | * we cannot validate everything in the setXXX() calls. In |
355 | * particular, before we do an execute() on a CallableStatement, |
356 | * we need to go through the parameters and make sure that |
357 | * all parameters are set up properly. The motivator for this |
358 | * is that setXXX() can be called either before or after |
359 | * registerOutputParamter(), we cannot be sure we have the types |
360 | * correct until we get to execute(). |
361 | * |
362 | * @exception StandardException if the parameters aren't valid |
363 | */ |
364 | public void validate() throws StandardException |
365 | { |
366 | for (int i = 0; i < parms.length; i++) |
367 | { |
368 | parms[i].validate(); |
369 | } |
370 | } |
371 | |
372 | |
373 | /** |
374 | * Return the parameter number (in jdbc lingo, i.e. 1 based) |
375 | * for the given parameter. Linear search. |
376 | * |
377 | * @return the parameter number, or 0 if not found |
378 | */ |
379 | public int getParameterNumber(GenericParameter theParam) |
380 | { |
381 | for (int i = 0; i < parms.length; i++) |
382 | { |
383 | if (parms[i] == theParam) |
384 | { |
385 | return i+1; |
386 | } |
387 | } |
388 | return 0; |
389 | } |
390 | |
391 | /** |
392 | Check that there are not output parameters defined |
393 | by the parameter set. If there are unknown parameter |
394 | types they are forced to input types. i.e. Cloudscape static method |
395 | calls with parameters that are array. |
396 | |
397 | @return true if a declared Java Procedure INOUT or OUT parameter is in the set, false otherwise. |
398 | */ |
399 | public boolean checkNoDeclaredOutputParameters() { |
400 | |
401 | boolean hasDeclaredOutputParameter = false; |
402 | for (int i=0; i<parms.length; i++) { |
403 | |
404 | GenericParameter gp = parms[i]; |
405 | |
406 | switch (gp.parameterMode) { |
407 | case JDBC30Translation.PARAMETER_MODE_IN: |
408 | break; |
409 | case JDBC30Translation.PARAMETER_MODE_IN_OUT: |
410 | case JDBC30Translation.PARAMETER_MODE_OUT: |
411 | hasDeclaredOutputParameter = true; |
412 | break; |
413 | case JDBC30Translation.PARAMETER_MODE_UNKNOWN: |
414 | gp.parameterMode = JDBC30Translation.PARAMETER_MODE_IN; |
415 | break; |
416 | } |
417 | } |
418 | return hasDeclaredOutputParameter; |
419 | } |
420 | |
421 | /** |
422 | Return the mode of the parameter according to JDBC 3.0 ParameterMetaData |
423 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
424 | * |
425 | */ |
426 | public short getParameterMode(int parameterIndex) |
427 | { |
428 | short mode = parms[parameterIndex - 1].parameterMode; |
429 | //if (mode == (short) JDBC30Translation.PARAMETER_MODE_UNKNOWN) |
430 | // mode = (short) JDBC30Translation.PARAMETER_MODE_IN; |
431 | return mode; |
432 | } |
433 | |
434 | /** |
435 | * Is there a return output parameter in this pvs. A return |
436 | * parameter is from a CALL statement of the following |
437 | * syntax: ? = CALL myMethod() |
438 | * |
439 | * @return true if it has a return parameter |
440 | * |
441 | */ |
442 | public boolean hasReturnOutputParameter() |
443 | { |
444 | return hasReturnOutputParam; |
445 | } |
446 | |
447 | /** |
448 | * Get the value of the return parameter in order to set it. |
449 | * |
450 | * |
451 | * @exception StandardException if a database-access error occurs. |
452 | */ |
453 | public DataValueDescriptor getReturnValueForSet() throws StandardException |
454 | { |
455 | checkPosition(0); |
456 | |
457 | if (SanityManager.DEBUG) |
458 | { |
459 | if (!hasReturnOutputParam) |
460 | SanityManager.THROWASSERT("getReturnValueForSet called on non-return parameter"); |
461 | } |
462 | |
463 | return parms[0].getValue(); |
464 | } |
465 | |
466 | /** |
467 | * Return the scale of the given parameter index in this pvs. |
468 | * |
469 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
470 | * |
471 | * @return scale |
472 | */ |
473 | public int getScale(int parameterIndex) |
474 | { |
475 | return parms[parameterIndex-1].getScale(); |
476 | } |
477 | |
478 | /** |
479 | * Return the precision of the given parameter index in this pvs. |
480 | * |
481 | * @param parameterIndex the first parameter is 1, the second is 2, ... |
482 | * |
483 | * @return precision |
484 | */ |
485 | public int getPrecision(int parameterIndex) |
486 | { |
487 | return parms[parameterIndex-1].getPrecision(); |
488 | } |
489 | |
490 | } |