001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.GenericParameter
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql;
023:
024: import org.apache.derby.iapi.services.loader.ClassInspector;
025:
026: import org.apache.derby.iapi.sql.ParameterValueSet;
027:
028: import org.apache.derby.iapi.types.DataValueDescriptor;
029: import org.apache.derby.iapi.types.BooleanDataValue;
030: import org.apache.derby.iapi.types.BitDataValue;
031: import org.apache.derby.iapi.types.DateTimeDataValue;
032: import org.apache.derby.iapi.types.NumberDataValue;
033: import org.apache.derby.iapi.types.StringDataValue;
034: import org.apache.derby.iapi.types.UserDataValue;
035: import org.apache.derby.iapi.types.TypeId;
036: import org.apache.derby.iapi.types.DataTypeDescriptor;
037: import org.apache.derby.iapi.types.*;
038:
039: import org.apache.derby.iapi.reference.SQLState;
040:
041: import org.apache.derby.iapi.reference.JDBC30Translation;
042:
043: import org.apache.derby.iapi.error.StandardException;
044:
045: import org.apache.derby.iapi.services.sanity.SanityManager;
046:
047: import org.apache.derby.iapi.types.*;
048: import org.apache.derby.iapi.types.*;
049:
050: import java.sql.Types;
051:
052: import java.lang.reflect.Array;
053:
054: /**
055: * A parameter. Originally lifted from ParameterValueSet.
056: *
057: * @author jamie
058: */
059: final class GenericParameter {
060:
061: // These defaults match the Network Server/ JCC max precision and
062: // The JCC "guessed" scale. They are used as the defaults for
063: // Decimal out params.
064: private static int DECIMAL_PARAMETER_DEFAULT_PRECISION = 31;
065: private static int DECIMAL_PARAMETER_DEFAULT_SCALE = 15;
066:
067: /*
068: ** The parameter set we are part of
069: */
070: private final GenericParameterValueSet pvs;
071:
072: /**
073: ** Our value
074: */
075: private DataValueDescriptor value;
076:
077: /**
078: Compile time JDBC type identifier.
079: */
080: int jdbcTypeId;
081:
082: /**
083: Compile time Java class name.
084: */
085: String declaredClassName;
086:
087: /**
088: Mode of the parameter, from ParameterMetaData
089: */
090: short parameterMode;
091:
092: /*
093: ** If we are set
094: */
095: boolean isSet;
096:
097: /*
098: ** Output parameter values
099: */
100: private final boolean isReturnOutputParameter;
101:
102: /**
103: Type that has been registered.
104: */
105: int registerOutType = Types.NULL;
106: /**
107: Scale that has been registered.
108: */
109: int registerOutScale = -1;
110:
111: /**
112: * When a decimal output parameter is registered we give it a
113: * precision
114: */
115:
116: int registerOutPrecision = -1;
117:
118: /**
119: * Constructor for a Parameter
120: *
121: * @param pvs the parameter set that this is part of
122: * @param isReturnOutputParameter true if this is a return output parameter
123: */
124: GenericParameter(GenericParameterValueSet pvs,
125: boolean isReturnOutputParameter) {
126: this .pvs = pvs;
127: parameterMode = (this .isReturnOutputParameter = isReturnOutputParameter) ? (short) JDBC30Translation.PARAMETER_MODE_OUT
128: : (short) JDBC30Translation.PARAMETER_MODE_IN;
129: }
130:
131: /**
132: * Clone myself. It is a shallow copy for everything but
133: * the underlying data wrapper and its value -- e.g. for
134: * everything but the underlying SQLInt and its int.
135: *
136: * @param pvs the parameter value set
137: *
138: * @return a new generic parameter.
139: */
140: public GenericParameter getClone(GenericParameterValueSet pvs) {
141: GenericParameter gpClone = new GenericParameter(pvs,
142: isReturnOutputParameter);
143: gpClone.initialize(this .getValue().getClone(), jdbcTypeId,
144: declaredClassName);
145: gpClone.isSet = true;
146:
147: return gpClone;
148: }
149:
150: /**
151: * Set the DataValueDescriptor and type information for this parameter
152: *
153: */
154: void initialize(DataValueDescriptor value, int jdbcTypeId,
155: String className) {
156: this .value = value;
157: this .jdbcTypeId = jdbcTypeId;
158: this .declaredClassName = className;
159: }
160:
161: /**
162: * Clear the parameter, unless it is a return
163: * output parameter
164: */
165: void clear() {
166: isSet = false;
167: }
168:
169: /**
170: * Get the parameter value. Doesn't check to
171: * see if it has been initialized or not.
172: *
173: * @return the parameter value, may return null
174: */
175: DataValueDescriptor getValue() {
176: return value;
177: }
178:
179: //////////////////////////////////////////////////////////////////
180: //
181: // CALLABLE STATEMENT
182: //
183: //////////////////////////////////////////////////////////////////
184:
185: /**
186: * Mark the parameter as an output parameter.
187: *
188: * @param sqlType A type from java.sql.Types
189: * @param scale scale, -1 if no scale arg
190: *
191: * @exception StandardException on error
192: */
193: void setOutParameter(int sqlType, int scale)
194: throws StandardException {
195: // fast case duplicate registrations.
196: if (registerOutType == sqlType) {
197: if (scale == registerOutScale)
198: return;
199: }
200:
201: switch (parameterMode) {
202: case JDBC30Translation.PARAMETER_MODE_IN:
203: case JDBC30Translation.PARAMETER_MODE_UNKNOWN:
204: default:
205: throw StandardException.newException(
206: SQLState.LANG_NOT_OUT_PARAM,
207: getJDBCParameterNumberStr());
208:
209: case JDBC30Translation.PARAMETER_MODE_IN_OUT:
210: case JDBC30Translation.PARAMETER_MODE_OUT:
211: // Declared/Java procedure parameter.
212: if (!DataTypeDescriptor.isJDBCTypeEquivalent(jdbcTypeId,
213: sqlType))
214: throw throwInvalidOutParamMap(sqlType);
215: break;
216:
217: }
218:
219: registerOutType = sqlType;
220:
221: }
222:
223: private StandardException throwInvalidOutParamMap(int sqlType) {
224:
225: //TypeId typeId = TypeId.getBuiltInTypeId(sqlType);
226: // String sqlTypeName = typeId == null ? "OTHER" : typeId.getSQLTypeName();
227:
228: String jdbcTypesName = org.apache.derby.impl.jdbc.Util
229: .typeName(sqlType);
230:
231: TypeId typeId = TypeId.getBuiltInTypeId(jdbcTypeId);
232: String this TypeName = typeId == null ? declaredClassName
233: : typeId.getSQLTypeName();
234:
235: StandardException e = StandardException.newException(
236: SQLState.LANG_INVALID_OUT_PARAM_MAP,
237: getJDBCParameterNumberStr(), jdbcTypesName,
238: this TypeName);
239:
240: return e;
241: }
242:
243: /**
244: * Validate the parameters. This is done for situations where
245: * we cannot validate everything in the setXXX() calls. In
246: * particular, before we do an execute() on a CallableStatement,
247: * we need to go through the parameters and make sure that
248: * all parameters are set up properly. The motivator for this
249: * is that setXXX() can be called either before or after
250: * registerOutputParamter(), we cannot be sure we have the types
251: * correct until we get to execute().
252: *
253: * @exception StandardException if the parameters aren't valid
254: */
255: void validate() throws StandardException {
256: switch (parameterMode) {
257: case JDBC30Translation.PARAMETER_MODE_UNKNOWN:
258: break;
259: case JDBC30Translation.PARAMETER_MODE_IN:
260: break;
261: case JDBC30Translation.PARAMETER_MODE_IN_OUT:
262: case JDBC30Translation.PARAMETER_MODE_OUT:
263: if (registerOutType == Types.NULL) {
264: throw StandardException.newException(
265: SQLState.NEED_TO_REGISTER_PARAM,
266: getJDBCParameterNumberStr(),
267: org.apache.derby.catalog.types.RoutineAliasInfo
268: .parameterMode(parameterMode));
269: }
270: break;
271: }
272: }
273:
274: /**
275: * Return the scale of the parameter.
276: *
277: * @return scale
278: */
279: int getScale() {
280: //when the user doesn't pass any scale, the registerOutScale gets set to -1
281: return (registerOutScale == -1 ? 0 : registerOutScale);
282: }
283:
284: int getPrecision() {
285: return registerOutPrecision;
286:
287: }
288:
289: ////////////////////////////////////////////////////
290: //
291: // CLASS IMPLEMENTATION
292: //
293: ////////////////////////////////////////////////////
294:
295: /**
296: * get string for param number
297: */
298: String getJDBCParameterNumberStr() {
299: return Integer.toString(pvs.getParameterNumber(this ));
300: }
301:
302: public String toString() {
303: /* This method is used for debugging.
304: * It is called when derby.language.logStatementText=true,
305: * so there is no check of SanityManager.DEBUG.
306: * Anyway, we need to call value.getString() instead of
307: * value.toString() because the user may have done a
308: * a setStream() on the parameter. (toString() could get
309: * an assertion failure in that case as it would be in an
310: * unexpected state since this is a very weird codepath.)
311: * getString() can throw an exception which we eat and
312: * and reflect in the returned string.
313: */
314: if (value == null) {
315: return "null";
316: } else {
317: try {
318: return value.getTraceString();
319: } catch (StandardException se) {
320: return "unexpected exception from getTraceString() - "
321: + se;
322: }
323: }
324: }
325: }
|