Source code: org/apache/derby/impl/sql/compile/CoalesceFunctionNode.java
1 /*
2
3 Derby - Class org.apache.derby.impl.sql.compile.CoalesceFunctionNode
4
5 Copyright 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.compile;
22
23 import org.apache.derby.iapi.reference.ClassName;
24 import org.apache.derby.iapi.reference.SQLState;
25
26 import org.apache.derby.iapi.services.classfile.VMOpcode;
27
28 import org.apache.derby.iapi.services.sanity.SanityManager;
29
30 import org.apache.derby.iapi.error.StandardException;
31
32 import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
33
34 import org.apache.derby.iapi.services.compiler.LocalField;
35 import org.apache.derby.iapi.services.compiler.MethodBuilder;
36
37 import java.lang.reflect.Modifier;
38
39 import java.util.Vector;
40
41 /**
42 * This node represents coalesce/value function which returns the first argument that is not null.
43 * The arguments are evaluated in the order in which they are specified, and the result of the
44 * function is the first argument that is not null. The result can be null only if all the arguments
45 * can be null. The selected argument is converted, if necessary, to the attributes of the result.
46 *
47 *
48 * SQL Reference Guide for DB2 has section titled "Rules for result data types" at the following url
49 * http://publib.boulder.ibm.com/infocenter/db2help/index.jsp?topic=/com.ibm.db2.udb.doc/admin/r0008480.htm
50
51 * I have constructed following table based on various tables and information under "Rules for result data types"
52 * This table has FOR BIT DATA TYPES broken out into separate columns for clarity
53 *
54 * Note that are few differences between Cloudscape and DB2
55 * 1)there are few differences between what datatypes are consdiered compatible
56 * In DB2, CHAR FOR BIT DATA datatypes are compatible with CHAR datatypes
57 * ie in addition to following table, CHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA
58 * ie in addition to following table, VARCHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA
59 * ie in addition to following table, LONG VARCHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA
60 * ie in addition to following table, CHAR FOR BIT DATA is compatible with DATE, TIME, TIMESTAMP
61 * ie in addition to following table, VARCHAR FOR BIT DATA is compatible with DATE, TIME, TIMESTAMP
62 *
63 * 2)few datatypes donot have matching precision in Cloudscape and DB2
64 * In DB2, precision of TIME is 8. In Cloudscape, precision of TIME is 0.
65 * In DB2, precision,scale of TIMESTAMP is 26,6. In Cloudscape, precision of TIMESTAMP is 0,0.
66 * In DB2, precision of DOUBLE is 15. In Cloudscape, precision of DOUBLE is 52.
67 * In DB2, precision of REAL is 23. In Cloudscape, precision of REAL is 7.
68 * In DB2, precision calculation equation is incorrect when we have int and decimal arguments.
69 * The equation should be p=x+max(w-x,10) since precision of integer is 10 in both db2 and cloudscape. Instead, DB2 has p=x+max(w-x,11)
70 *
71 * Types. S I B D R D C V L C V L C D T T B
72 * M N I E E O H A O H A O L A I I L
73 * A T G C A U A R N A R N O T M M O
74 * L E I I L B R C G R C G B E E E B
75 * L G N M L H V . H V S
76 * I E T A E A A B A A T
77 * N R L R R I R R A
78 * T C T . . M
79 * H B B P
80 * A I I
81 * R T T
82 * SMALLINT { "SMALLINT", "INTEGER", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
83 * INTEGER { "INTEGER", "INTEGER", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
84 * BIGINT { "BIGINT", "BIGINT", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
85 * DECIMAL { "DECIMAL", "DECIMAL", "DECIMAL", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
86 * REAL { "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "REAL", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
87 * DOUBLE { "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
88 * CHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "CHAR", "VARCHAR", "LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "DATE", "TIME", "TIMESTAMP", "ERROR" },
89 * VARCHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "VARCHAR", "VARCHAR","LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "DATE", "TIME", "TIMESTAMP", "ERROR" },
90 * LONGVARCHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "LONG VARCHAR", "LONG VARCHAR", "LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "ERROR", "ERROR", "ERROR", "ERROR" },
91 * CHAR FOR BIT { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BIT", "BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
92 * VARCH. BIT { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BIT VARYING", "BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
93 * LONGVAR. BIT { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "LONG BIT VARYING", "LONG BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
94 * CLOB { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "CLOB", "CLOB", "CLOB", "ERROR", "ERROR", "ERROR", "CLOB", "ERROR", "ERROR", "ERROR", "ERROR" },
95 * DATE { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "DATE", "DATE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "DATE", "ERROR", "ERROR", "ERROR" },
96 * TIME { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIME", "TIME", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIME", "ERROR", "ERROR" },
97 * TIMESTAMP { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIMESTAMP", "TIMESTAMP", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIMESTAMP", "ERROR" },
98 * BLOB { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BLOB" }
99 */
100
101 public class CoalesceFunctionNode extends ValueNode
102 {
103 String functionName; //Are we here because of COALESCE function or VALUE function
104 ValueNodeList argumentsList; //this is the list of arguments to the function. We are interested in the first not-null argument
105 ValueNode firstNonParameterNode;//The generated method will generate code to call coalesce on this non-parameter argument
106
107 /**
108 * Initializer for a CalesceFunctionNode
109 *
110 * @param functionName Tells if the function was called with name COALESCE or with name VALUE
111 * @param argumentsList The list of arguments to the coalesce/value function
112 */
113 public void init(Object functionName, Object argumentsList)
114 {
115 this.functionName = (String) functionName;
116 this.argumentsList = (ValueNodeList) argumentsList;
117 }
118
119 /**
120 * Binding this expression means setting the result DataTypeServices.
121 * In this case, the result type is based on the rules in the table listed earlier.
122 *
123 * @param fromList The FROM list for the statement.
124 * @param subqueryList The subquery list being built as we find SubqueryNodes.
125 * @param aggregateVector The aggregate vector being built as we find AggregateNodes.
126 *
127 * @return The new top of the expression tree.
128 *
129 * @exception StandardException Thrown on error
130 */
131 public ValueNode bindExpression(FromList fromList, SubqueryList subqueryList,
132 Vector aggregateVector)
133 throws StandardException
134 {
135 //bind all the arguments
136 argumentsList.bindExpression(fromList, subqueryList, aggregateVector);
137
138 //There should be more than one argument
139 if (argumentsList.size() < 2)
140 throw StandardException.newException(SQLState.LANG_DB2_NUMBER_OF_ARGS_INVALID, functionName);
141
142 //check if all the arguments are parameters. If yes, then throw an exception
143 if (argumentsList.containsAllParameterNodes())
144 throw StandardException.newException(SQLState.LANG_DB2_COALESCE_FUNCTION_ALL_PARAMS);
145
146 int argumentsListSize = argumentsList.size();
147 //find the first non-param argument. The generated method will generate code to call coalesce on this argument
148 for (int index = 0; index < argumentsListSize; index++)
149 {
150 if (!(((ValueNode) argumentsList.elementAt(index)).isParameterNode()))
151 {
152 firstNonParameterNode = (ValueNode) argumentsList.elementAt(index);
153 break;
154 }
155 }
156
157 //make sure these arguments are compatible to each other before coalesce can be allowed
158 for (int index = 0; index < argumentsListSize; index++)
159 {
160 if (((ValueNode) argumentsList.elementAt(index)).isParameterNode()) //since we don't know the type of param, can't check for compatibility
161 continue;
162 argumentsList.compatible((ValueNode) argumentsList.elementAt(index));
163 }
164
165 //set the result type to the most dominant datatype in the arguments list and based on the table listed above
166 setType(argumentsList.getDominantTypeServices());
167
168 //set all the parameter types to the type of the result type
169 for (int index = 0; index < argumentsListSize; index++)
170 {
171 if (((ValueNode) argumentsList.elementAt(index)).isParameterNode())
172 {
173 ((ParameterNode) argumentsList.elementAt(index)).setDescriptor(getTypeServices());
174 break;
175 }
176 }
177 return this;
178 }
179
180 /**
181 * Do code generation for coalese/value
182 *
183 * @param acb The ExpressionClassBuilder for the class we're generating
184 * @param mb The method the expression will go into
185 *
186 * @exception StandardException Thrown on error
187 */
188
189 public void generateExpression(ExpressionClassBuilder acb,
190 MethodBuilder mb)
191 throws StandardException
192 {
193 int argumentsListSize = argumentsList.size();
194 String receiverType = ClassName.DataValueDescriptor;
195 String argumentsListInterfaceType = ClassName.DataValueDescriptor + "[]";
196
197 // Generate the code to build the array
198 LocalField arrayField =
199 acb.newFieldDeclaration(Modifier.PRIVATE, argumentsListInterfaceType);
200
201 /* The array gets created in the constructor.
202 * All constant elements in the array are initialized
203 * in the constructor.
204 */
205 /* Assign the initializer to the DataValueDescriptor[] field */
206 MethodBuilder cb = acb.getConstructor();
207 cb.pushNewArray(ClassName.DataValueDescriptor, argumentsListSize);
208 cb.setField(arrayField);
209
210 /* Set the array elements that are constant */
211 int numConstants = 0;
212 MethodBuilder nonConstantMethod = null;
213 MethodBuilder currentConstMethod = cb;
214 for (int index = 0; index < argumentsListSize; index++)
215 {
216 MethodBuilder setArrayMethod;
217
218 if (argumentsList.elementAt(index) instanceof ConstantNode)
219 {
220 numConstants++;
221
222 /*if too many statements are added to a method,
223 *size of method can hit 65k limit, which will
224 *lead to the class format errors at load time.
225 *To avoid this problem, when number of statements added
226 *to a method is > 2048, remaing statements are added to a new function
227 *and called from the function which created the function.
228 *See Beetle 5135 or 4293 for further details on this type of problem.
229 */
230 if(currentConstMethod.statementNumHitLimit(1))
231 {
232 MethodBuilder genConstantMethod = acb.newGeneratedFun("void", Modifier.PRIVATE);
233 currentConstMethod.pushThis();
234 currentConstMethod.callMethod(VMOpcode.INVOKEVIRTUAL,
235 (String) null,
236 genConstantMethod.getName(),
237 "void", 0);
238 //if it is a generate function, close the metod.
239 if(currentConstMethod != cb){
240 currentConstMethod.methodReturn();
241 currentConstMethod.complete();
242 }
243 currentConstMethod = genConstantMethod;
244 }
245 setArrayMethod = currentConstMethod;
246 } else {
247 if (nonConstantMethod == null)
248 nonConstantMethod = acb.newGeneratedFun("void", Modifier.PROTECTED);
249 setArrayMethod = nonConstantMethod;
250
251 }
252
253 setArrayMethod.getField(arrayField);
254 ((ValueNode) argumentsList.elementAt(index)).generateExpression(acb, setArrayMethod);
255 setArrayMethod.upCast(receiverType);
256 setArrayMethod.setArrayElement(index);
257 }
258
259 //if a generated function was created to reduce the size of the methods close the functions.
260 if(currentConstMethod != cb){
261 currentConstMethod.methodReturn();
262 currentConstMethod.complete();
263 }
264
265 if (nonConstantMethod != null) {
266 nonConstantMethod.methodReturn();
267 nonConstantMethod.complete();
268 mb.pushThis();
269 mb.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, nonConstantMethod.getName(), "void", 0);
270 }
271
272 /*
273 ** Call the method for coalesce/value function.
274 ** First generate following
275 ** <first non-param argument in the list>.method(<all the arguments>, <resultType>)
276 ** Next, if we are dealing with result type that is variable length, then generate a call to setWidth.
277 */
278
279 firstNonParameterNode.generateExpression(acb, mb); //coalesce will be called on this non-parameter argument
280 mb.upCast(ClassName.DataValueDescriptor);
281
282 mb.getField(arrayField); // first arg to the coalesce function
283
284 //Following is for the second arg. This arg will be used to pass the return value.
285 //COALESCE method expects this to be initialized to NULL SQLxxx type object.
286 LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE, receiverType);
287 acb.generateNull(mb, getTypeCompiler());
288 mb.upCast(ClassName.DataValueDescriptor);
289 mb.putField(field);
290
291 mb.callMethod(VMOpcode.INVOKEINTERFACE, receiverType, "coalesce", receiverType, 2);
292 if (getTypeId().variableLength())//since result type is variable length, generate setWidth code.
293 {
294 boolean isNumber = getTypeId().isNumericTypeId();
295 mb.push(isNumber ? getTypeServices().getPrecision() : getTypeServices().getMaximumWidth());
296 mb.push(getTypeServices().getScale());
297 mb.push(true);
298 mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.VariableSizeDataValue, "setWidth", ClassName.DataValueDescriptor, 3);
299 }
300 }
301
302 /*
303 print the non-node subfields
304 */
305 public String toString() {
306 if (SanityManager.DEBUG)
307 {
308 return super.toString()+functionName+"("+argumentsList+")\n";
309 }
310 else
311 {
312 return "";
313 }
314 }
315
316 }