001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.NumericTypeCompiler
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.compile;
023:
024: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
025:
026: import org.apache.derby.iapi.services.context.ContextService;
027:
028: import org.apache.derby.iapi.services.loader.ClassFactory;
029:
030: import org.apache.derby.iapi.services.sanity.SanityManager;
031:
032: import org.apache.derby.iapi.services.info.JVMInfo;
033: import org.apache.derby.iapi.services.io.StoredFormatIds;
034:
035: import org.apache.derby.iapi.error.StandardException;
036:
037: import org.apache.derby.iapi.types.DataTypeDescriptor;
038: import org.apache.derby.iapi.types.DataTypeDescriptor;
039: import org.apache.derby.iapi.types.DataValueFactory;
040: import org.apache.derby.iapi.types.NumberDataValue;
041: import org.apache.derby.iapi.types.TypeId;
042:
043: import org.apache.derby.iapi.sql.compile.TypeCompiler;
044:
045: import org.apache.derby.iapi.reference.ClassName;
046: import org.apache.derby.iapi.reference.Limits;
047: import org.apache.derby.iapi.reference.SQLState;
048: import org.apache.derby.iapi.services.compiler.LocalField;
049: import org.apache.derby.iapi.services.compiler.MethodBuilder;
050:
051: /**
052: * This class implements TypeId for the SQL numeric datatype.
053: *
054: * @author Jeff Lichtman
055: */
056:
057: public final class NumericTypeCompiler extends BaseTypeCompiler {
058: /** @see TypeCompiler#interfaceName */
059: public String interfaceName() {
060: return ClassName.NumberDataValue;
061: }
062:
063: /**
064: * @see TypeCompiler#getCorrespondingPrimitiveTypeName
065: */
066:
067: public String getCorrespondingPrimitiveTypeName() {
068: /* Only numerics and booleans get mapped to Java primitives */
069: int formatId = getStoredFormatIdFromTypeId();
070: switch (formatId) {
071: case StoredFormatIds.DOUBLE_TYPE_ID:
072: return "double";
073:
074: case StoredFormatIds.INT_TYPE_ID:
075: return "int";
076:
077: case StoredFormatIds.LONGINT_TYPE_ID:
078: return "long";
079:
080: case StoredFormatIds.REAL_TYPE_ID:
081: return "float";
082:
083: case StoredFormatIds.SMALLINT_TYPE_ID:
084: return "short";
085:
086: case StoredFormatIds.TINYINT_TYPE_ID:
087: return "byte";
088:
089: case StoredFormatIds.DECIMAL_TYPE_ID:
090: default:
091: if (SanityManager.DEBUG) {
092: SanityManager
093: .THROWASSERT("unexpected formatId in getCorrespondingPrimitiveTypeName() - "
094: + formatId);
095: }
096: return null;
097: }
098: }
099:
100: /**
101: * Get the method name for getting out the corresponding primitive
102: * Java type.
103: *
104: * @return String The method call name for getting the
105: * corresponding primitive Java type.
106: */
107: public String getPrimitiveMethodName() {
108: int formatId = getStoredFormatIdFromTypeId();
109: switch (formatId) {
110: case StoredFormatIds.DOUBLE_TYPE_ID:
111: return "getDouble";
112:
113: case StoredFormatIds.INT_TYPE_ID:
114: return "getInt";
115:
116: case StoredFormatIds.LONGINT_TYPE_ID:
117: return "getLong";
118:
119: case StoredFormatIds.REAL_TYPE_ID:
120: return "getFloat";
121:
122: case StoredFormatIds.SMALLINT_TYPE_ID:
123: return "getShort";
124:
125: case StoredFormatIds.TINYINT_TYPE_ID:
126: return "getByte";
127:
128: case StoredFormatIds.DECIMAL_TYPE_ID:
129: default:
130: if (SanityManager.DEBUG) {
131: SanityManager
132: .THROWASSERT("unexpected formatId in getPrimitiveMethodName() - "
133: + formatId);
134: }
135: return null;
136: }
137: }
138:
139: /**
140: * @see TypeCompiler#getCastToCharWidth
141: */
142: public int getCastToCharWidth(DataTypeDescriptor dts) {
143: int formatId = getStoredFormatIdFromTypeId();
144: switch (formatId) {
145: case StoredFormatIds.DECIMAL_TYPE_ID:
146: // Need to have space for '-' and decimal point.
147: return dts.getPrecision() + 2;
148:
149: case StoredFormatIds.DOUBLE_TYPE_ID:
150: return TypeCompiler.DOUBLE_MAXWIDTH_AS_CHAR;
151:
152: case StoredFormatIds.INT_TYPE_ID:
153: return TypeCompiler.INT_MAXWIDTH_AS_CHAR;
154:
155: case StoredFormatIds.LONGINT_TYPE_ID:
156: return TypeCompiler.LONGINT_MAXWIDTH_AS_CHAR;
157:
158: case StoredFormatIds.REAL_TYPE_ID:
159: return TypeCompiler.REAL_MAXWIDTH_AS_CHAR;
160:
161: case StoredFormatIds.SMALLINT_TYPE_ID:
162: return TypeCompiler.SMALLINT_MAXWIDTH_AS_CHAR;
163:
164: case StoredFormatIds.TINYINT_TYPE_ID:
165: return TypeCompiler.TINYINT_MAXWIDTH_AS_CHAR;
166:
167: default:
168: if (SanityManager.DEBUG) {
169: SanityManager
170: .THROWASSERT("unexpected formatId in getCastToCharWidth() - "
171: + formatId);
172: }
173: return 0;
174: }
175: }
176:
177: /**
178: * @see TypeCompiler#resolveArithmeticOperation
179: *
180: * @exception StandardException Thrown on error
181: */
182: public DataTypeDescriptor resolveArithmeticOperation(
183: DataTypeDescriptor leftType, DataTypeDescriptor rightType,
184: String operator) throws StandardException {
185: NumericTypeCompiler higherTC;
186: DataTypeDescriptor higherType;
187: boolean nullable;
188: int precision, scale, maximumWidth;
189:
190: /*
191: ** Check the right type to be sure it's a number. By convention,
192: ** we call this method off the TypeId of the left operand, so if
193: ** we get here, we know the left operand is a number.
194: */
195: if (SanityManager.DEBUG)
196: SanityManager
197: .ASSERT(
198: leftType.getTypeId().isNumericTypeId(),
199: "The left type is supposed to be a number because we're resolving an arithmetic operator");
200:
201: TypeId leftTypeId = leftType.getTypeId();
202: TypeId rightTypeId = rightType.getTypeId();
203:
204: boolean supported = true;
205:
206: if (!(rightTypeId.isNumericTypeId())) {
207: supported = false;
208: }
209:
210: if (TypeCompiler.MOD_OP.equals(operator)) {
211: switch (leftTypeId.getJDBCTypeId()) {
212: case java.sql.Types.TINYINT:
213: case java.sql.Types.SMALLINT:
214: case java.sql.Types.INTEGER:
215: case java.sql.Types.BIGINT:
216: break;
217: default:
218: supported = false;
219: break;
220: }
221: switch (rightTypeId.getJDBCTypeId()) {
222: case java.sql.Types.TINYINT:
223: case java.sql.Types.SMALLINT:
224: case java.sql.Types.INTEGER:
225: case java.sql.Types.BIGINT:
226: break;
227: default:
228: supported = false;
229: break;
230: }
231:
232: }
233:
234: if (!supported) {
235: throw StandardException.newException(
236: SQLState.LANG_BINARY_OPERATOR_NOT_SUPPORTED,
237: operator, leftType.getTypeId().getSQLTypeName(),
238: rightType.getTypeId().getSQLTypeName());
239: }
240:
241: /*
242: ** Take left as the higher precedence if equal
243: */
244: if (rightTypeId.typePrecedence() > leftTypeId.typePrecedence()) {
245: higherType = rightType;
246: higherTC = (NumericTypeCompiler) getTypeCompiler(rightTypeId);
247: } else {
248: higherType = leftType;
249: higherTC = (NumericTypeCompiler) getTypeCompiler(leftTypeId);
250: }
251:
252: /* The calculation of precision and scale should be based upon
253: * the type with higher precedence, which is going to be the result
254: * type, this is also to be consistent with maximumWidth. Beetle 3906.
255: */
256: precision = higherTC
257: .getPrecision(operator, leftType, rightType);
258: scale = higherTC.getScale(operator, leftType, rightType);
259:
260: if (higherType.getTypeId().isDecimalTypeId()) {
261: maximumWidth = (scale > 0) ? precision + 3 : precision + 1;
262:
263: /*
264: ** Be careful not to overflow
265: */
266: if (maximumWidth < precision) {
267: maximumWidth = Integer.MAX_VALUE;
268: }
269: } else {
270: maximumWidth = higherType.getMaximumWidth();
271: }
272:
273: /* The result is nullable if either side is nullable */
274: nullable = leftType.isNullable() || rightType.isNullable();
275:
276: /*
277: ** The higher type does not have the right nullability. Create a
278: ** new DataTypeDescriptor that has the correct type and nullability.
279: **
280: ** It's OK to call the implementation of the DataTypeDescriptorFactory
281: ** here, because we're in the same package.
282: */
283: return new DataTypeDescriptor(higherType.getTypeId(),
284: precision, scale, nullable, maximumWidth);
285: }
286:
287: /** @see TypeCompiler#comparable */
288: public boolean comparable(TypeId otherType, boolean forEquals,
289: ClassFactory cf) {
290: return numberComparable(otherType, forEquals, cf);
291: }
292:
293: /** @see TypeCompiler#convertible */
294: public boolean convertible(TypeId otherType,
295: boolean forDataTypeFunction) {
296: return (numberConvertible(otherType, forDataTypeFunction));
297:
298: }
299:
300: /**
301: * Tell whether this type (numeric) is compatible with the given type.
302: *
303: * @param otherType The TypeId of the other type.
304: */
305: public boolean compatible(TypeId otherType) {
306: // Numbers can only be compatible with other numbers.
307: return (otherType.isNumericTypeId());
308: }
309:
310: /** @see TypeCompiler#storable */
311: public boolean storable(TypeId otherType, ClassFactory cf) {
312: return numberStorable(getTypeId(), otherType, cf);
313: }
314:
315: /**
316: Return the method name to get a Derby DataValueDescriptor
317: object of the correct type. This implementation returns "getDataValue".
318: */
319: protected String dataValueMethodName() {
320: if (getStoredFormatIdFromTypeId() == StoredFormatIds.DECIMAL_TYPE_ID)
321: return "getDecimalDataValue";
322: else
323: return super .dataValueMethodName();
324: }
325:
326: protected String nullMethodName() {
327: int formatId = getStoredFormatIdFromTypeId();
328: switch (formatId) {
329: case StoredFormatIds.DECIMAL_TYPE_ID:
330: return "getNullDecimal";
331:
332: case StoredFormatIds.DOUBLE_TYPE_ID:
333: return "getNullDouble";
334:
335: case StoredFormatIds.INT_TYPE_ID:
336: return "getNullInteger";
337:
338: case StoredFormatIds.LONGINT_TYPE_ID:
339: return "getNullLong";
340:
341: case StoredFormatIds.REAL_TYPE_ID:
342: return "getNullFloat";
343:
344: case StoredFormatIds.SMALLINT_TYPE_ID:
345: return "getNullShort";
346:
347: case StoredFormatIds.TINYINT_TYPE_ID:
348: return "getNullByte";
349:
350: default:
351: if (SanityManager.DEBUG) {
352: SanityManager
353: .THROWASSERT("unexpected formatId in nullMethodName() - "
354: + formatId);
355: }
356: return null;
357: }
358: }
359:
360: /**
361: * Get the precision of the operation involving
362: * two of the same types. Only meaningful for
363: * decimals, which override this.
364: *
365: * @param operator a string representing the operator,
366: * null means no operator, just a type merge
367: * @param leftType the left type
368: * @param rightType the left type
369: *
370: * @return the resultant precision
371: */
372: private int getPrecision(String operator,
373: DataTypeDescriptor leftType, DataTypeDescriptor rightType) {
374: // Only meaningful for decimal
375: if (getStoredFormatIdFromTypeId() != StoredFormatIds.DECIMAL_TYPE_ID) {
376: return leftType.getPrecision();
377: }
378:
379: long lscale = (long) leftType.getScale();
380: long rscale = (long) rightType.getScale();
381: long lprec = (long) leftType.getPrecision();
382: long rprec = (long) rightType.getPrecision();
383: long val;
384:
385: /*
386: ** Null means datatype merge. Take the maximum
387: ** left of decimal digits plus the scale.
388: */
389: if (operator == null) {
390: val = this .getScale(operator, leftType, rightType)
391: + Math.max(lprec - lscale, rprec - rscale);
392: } else if (operator.equals(TypeCompiler.TIMES_OP)) {
393: val = lprec + rprec;
394: } else if (operator.equals(TypeCompiler.SUM_OP)) {
395: val = lprec - lscale + rprec - rscale
396: + this .getScale(operator, leftType, rightType);
397: } else if (operator.equals(TypeCompiler.DIVIDE_OP)) {
398: val = Math.min(NumberDataValue.MAX_DECIMAL_PRECISION_SCALE,
399: this .getScale(operator, leftType, rightType)
400: + lprec - lscale + rprec);
401: }
402: /*
403: ** AVG, -, +
404: */
405: else {
406: /*
407: ** Take max scale and max left of decimal
408: ** plus one.
409: */
410: val = this .getScale(operator, leftType, rightType)
411: + Math.max(lprec - lscale, rprec - rscale) + 1;
412:
413: if (val > Limits.DB2_MAX_DECIMAL_PRECISION_SCALE)
414: // then, like DB2, just set it to the max possible.
415: val = Limits.DB2_MAX_DECIMAL_PRECISION_SCALE;
416: }
417:
418: if (val > Integer.MAX_VALUE) {
419: val = Integer.MAX_VALUE;
420: }
421: val = Math
422: .min(NumberDataValue.MAX_DECIMAL_PRECISION_SCALE, val);
423: return (int) val;
424: }
425:
426: /**
427: * Get the scale of the operation involving
428: * two of the same types. Since we don't really
429: * have a good way to pass the resultant scale
430: * and precision around at execution time, we
431: * will model that BigDecimal does by default.
432: * This is good in most cases, though we would
433: * probably like to use something more sophisticated
434: * for division.
435: *
436: * @param operator a string representing the operator,
437: * null means no operator, just a type merge
438: * @param leftType the left type
439: * @param rightType the left type
440: *
441: * @return the resultant precision
442: */
443: private int getScale(String operator, DataTypeDescriptor leftType,
444: DataTypeDescriptor rightType) {
445: // Only meaningful for decimal
446: if (getStoredFormatIdFromTypeId() != StoredFormatIds.DECIMAL_TYPE_ID) {
447: return leftType.getScale();
448: }
449:
450: long val;
451:
452: long lscale = (long) leftType.getScale();
453: long rscale = (long) rightType.getScale();
454: long lprec = (long) leftType.getPrecision();
455: long rprec = (long) rightType.getPrecision();
456:
457: /*
458: ** Retain greatest scale, take sum of left
459: ** of decimal
460: */
461: if (TypeCompiler.TIMES_OP.equals(operator)) {
462: val = lscale + rscale;
463: } else if (TypeCompiler.DIVIDE_OP.equals(operator)) {
464: /*
465: ** Take max left scale + right precision - right scale + 1,
466: ** or 4, whichever is biggest
467: */
468: LanguageConnectionContext lcc = (LanguageConnectionContext) (ContextService
469: .getContext(LanguageConnectionContext.CONTEXT_ID));
470:
471: // Scale: 31 - left precision + left scale - right scale
472: val = Math.max(NumberDataValue.MAX_DECIMAL_PRECISION_SCALE
473: - lprec + lscale - rscale, 0);
474:
475: } else if (TypeCompiler.AVG_OP.equals(operator)) {
476: val = Math.max(Math.max(lscale, rscale),
477: NumberDataValue.MIN_DECIMAL_DIVIDE_SCALE);
478: }
479: /*
480: ** SUM, -, + all take max(lscale,rscale)
481: */
482: else {
483: val = Math.max(lscale, rscale);
484: }
485:
486: if (val > Integer.MAX_VALUE) {
487: val = Integer.MAX_VALUE;
488: }
489: val = Math
490: .min(NumberDataValue.MAX_DECIMAL_PRECISION_SCALE, val);
491: return (int) val;
492: }
493:
494: public void generateDataValue(MethodBuilder mb, LocalField field) {
495: if (!JVMInfo.J2ME && getTypeId().isDecimalTypeId()) {
496: // cast the value to a Number (from BigDecimal) for method resolution
497: // For J2ME there is no implementation of Number for DECIMAL
498: // so values are handled as thier original type, which is just
499: // a String for DECIMAL constants from the parser.
500: mb.upCast("java.lang.Number");
501: }
502:
503: super.generateDataValue(mb, field);
504: }
505:
506: }
|