Source code: org/hsqldb/Function.java
1 /* Copyrights and Licenses
2 *
3 * This product includes Hypersonic SQL.
4 * Originally developed by Thomas Mueller and the Hypersonic SQL Group.
5 *
6 * Copyright (c) 1995-2000 by the Hypersonic SQL Group. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 * - Redistributions of source code must retain the above copyright notice, this list of conditions
10 * and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright notice, this list of
12 * conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 * - All advertising materials mentioning features or use of this software must display the
15 * following acknowledgment: "This product includes Hypersonic SQL."
16 * - Products derived from this software may not be called "Hypersonic SQL" nor may
17 * "Hypersonic SQL" appear in their names without prior written permission of the
18 * Hypersonic SQL Group.
19 * - Redistributions of any form whatsoever must retain the following acknowledgment: "This
20 * product includes Hypersonic SQL."
21 * This software is provided "as is" and any expressed or implied warranties, including, but
22 * not limited to, the implied warranties of merchantability and fitness for a particular purpose are
23 * disclaimed. In no event shall the Hypersonic SQL Group or its contributors be liable for any
24 * direct, indirect, incidental, special, exemplary, or consequential damages (including, but
25 * not limited to, procurement of substitute goods or services; loss of use, data, or profits;
26 * or business interruption). However caused any on any theory of liability, whether in contract,
27 * strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this
28 * software, even if advised of the possibility of such damage.
29 * This software consists of voluntary contributions made by many individuals on behalf of the
30 * Hypersonic SQL Group.
31 *
32 *
33 * For work added by the HSQL Development Group:
34 *
35 * Copyright (c) 2001-2002, The HSQL Development Group
36 * All rights reserved.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions are met:
40 *
41 * Redistributions of source code must retain the above copyright notice, this
42 * list of conditions and the following disclaimer, including earlier
43 * license statements (above) and comply with all above license conditions.
44 *
45 * Redistributions in binary form must reproduce the above copyright notice,
46 * this list of conditions and the following disclaimer in the documentation
47 * and/or other materials provided with the distribution, including earlier
48 * license statements (above) and comply with all above license conditions.
49 *
50 * Neither the name of the HSQL Development Group nor the names of its
51 * contributors may be used to endorse or promote products derived from this
52 * software without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
55 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
58 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
59 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
60 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
61 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
62 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
63 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65 */
66
67
68 package org.hsqldb;
69
70 import java.sql.SQLException;
71 import java.lang.reflect.Method;
72 import java.lang.reflect.Modifier;
73 import java.util.Hashtable;
74
75 /**
76 * Provides services to evaluate Java methods in the context of
77 * SQL function and stored procedure calls.
78 *
79 * @version 1.7.0
80 */
81
82 // fredt@users 20020912 - patch 1.7.1 - shortcut treatment of identity() call
83 // fredt@users 20020912 - patch 1.7.1 - cache java.lang.reflect.Method objects
84 // fredt@users 20021013 - patch 1.7.1 - ignore non-static methods
85 class Function {
86
87 private Session cSession;
88 private String sFunction;
89 private Method mMethod;
90 private int iReturnType;
91 private int iArgCount;
92 private int iArgType[];
93 private boolean bArgNullable[];
94 private Object oArg[];
95 private Expression eArg[];
96 private boolean bConnection;
97 private boolean isIdentityFunction;
98 private static Hashtable methodCache = new Hashtable();
99
100 /**
101 * Constructs a new Function object with the given function call name
102 * and using the specified Session context. <p>
103 *
104 * The call name is the fully qualified name of a static Java method, as
105 * opposed to the method's canonical signature. That is, the name
106 * is of the form "package.class.method." This implies that Java
107 * methods with the same fully qualified name but different signatures
108 * cannot be used properly as HSQLDB SQL functions or stored procedures.
109 * For instance, it is impossible to call both System.getProperty(String)
110 * and System.getProperty(String,String) under this arrangement, because
111 * the HSQLDB Function object is unable to differentiate between the two;
112 * it simply chooses the first method matching the FQN in the array of
113 * methods obtained from calling getMethods() on an instance of the
114 * Class indicated in the FQN, hiding all other methods with the same
115 * FQN. <p>
116 *
117 * The function FQN must match at least one static Java method FQN in the
118 * specified class or construction cannot procede and a SQLException is
119 * thrown. <p>
120 *
121 * The Session paramter is the connected context in which this
122 * Function object will evaluate. If it is determined that the
123 * connected user does not have the right to evaluate this Function,
124 * construction cannot proceed and a SQLException is thrown.
125 *
126 *
127 * @param function the fully qualified name of a Java method
128 * @param session the connected context in which this Function object will
129 * evaluate
130 * @throws SQLException if the specified function FQN corresponds to no
131 * Java method or the session user at the time of
132 * construction does not have the right to evaluate
133 * this Function.
134 */
135 Function(String function, Session session) throws SQLException {
136
137 cSession = session;
138 sFunction = function;
139
140 if (function.equals("org.hsqldb.Library.identity")) {
141 isIdentityFunction = true;
142 }
143
144 int i = function.lastIndexOf('.');
145
146 Trace.check(i != -1, Trace.UNEXPECTED_TOKEN, function);
147
148 String classname = function.substring(0, i);
149
150 session.check("CLASS \"" + classname + "\"", UserManager.ALL);
151
152 mMethod = (Method) methodCache.get(function);
153
154 if (mMethod == null) {
155 String methodname = function.substring(i + 1);
156 Class classinstance = null;
157
158 try {
159 classinstance = Class.forName(classname);
160 } catch (Exception e) {
161 throw Trace.error(Trace.ERROR_IN_FUNCTION,
162 classname + " " + e);
163 }
164
165 Method method[] = classinstance.getMethods();
166
167 for (i = 0; i < method.length; i++) {
168 Method m = method[i];
169
170 if (m.getName().equals(methodname)
171 && Modifier.isStatic(m.getModifiers())) {
172 mMethod = m;
173
174 break;
175 }
176 }
177
178 Trace.check(mMethod != null, Trace.UNKNOWN_FUNCTION, methodname);
179 methodCache.put(function, mMethod);
180 }
181
182 Class returnclass = mMethod.getReturnType();
183
184 iReturnType = Column.getTypeNr(returnclass.getName());
185
186 Class arg[] = mMethod.getParameterTypes();
187
188 iArgCount = arg.length;
189 iArgType = new int[iArgCount];
190 bArgNullable = new boolean[iArgCount];
191
192 for (i = 0; i < arg.length; i++) {
193 Class a = arg[i];
194 String type = a.getName();
195
196 if ((i == 0) && type.equals("java.sql.Connection")) {
197
198 // only the first parameter can be a Connection
199 bConnection = true;
200 } else {
201
202 // fredt@users - byte[] is now supported directly as "[B"
203 // if (type.equals("[B")) {
204 // type = "byte[]";
205 // }
206 iArgType[i] = Column.getTypeNr(type);
207 bArgNullable[i] = !a.isPrimitive();
208 }
209 }
210
211 eArg = new Expression[iArgCount];
212 oArg = new Object[iArgCount];
213 }
214
215 /**
216 * Retrieves the value this Function evaluates to, given the current
217 * state of this object's {@link #resolve(TableFilter) resolved}
218 * TableFilter, if any, and any mapping of expressions to this
219 * Function's parameter list that has been performed via
220 * {link #setArgument(int,Expression) setArgument}.
221 *
222 *
223 * @return the value resulting from evaluating this Function
224 * @throws SQLException if an invocation exception is encountered when
225 * calling the Java
226 * method underlying this object
227 */
228 Object getValue() throws SQLException {
229
230 int i = 0;
231
232 if (isIdentityFunction) {
233 return new Integer(cSession.getLastIdentity());
234 }
235
236 if (bConnection) {
237 oArg[i] = cSession.getInternalConnection();
238
239 i++;
240 }
241
242 for (; i < iArgCount; i++) {
243 Expression e = eArg[i];
244 Object o = null;
245
246 if (e != null) {
247
248 // no argument: null
249 o = e.getValue(iArgType[i]);
250 }
251
252 if ((o == null) &&!bArgNullable[i]) {
253
254 // null argument for primitive datatype: don't call
255 return null;
256 }
257
258 oArg[i] = o;
259 }
260
261 try {
262 return mMethod.invoke(null, oArg);
263 } catch (Exception e) {
264 String s = sFunction + ": " + e.toString();
265
266 throw Trace.getError(Trace.FUNCTION_NOT_SUPPORTED, s);
267 }
268 }
269
270 /**
271 * Retrieves the number of parameters that must be supplied to evaluate
272 * this Function object from SQL. <p>
273 *
274 * This value may be different than the number of parameters of the
275 * underlying Java method. This is because HSQLDB automatically detects
276 * if the first parameter is of type java.sql.Connection, and supplies a
277 * live Connection object constructed from the evaluating session context
278 * if so.
279 *
280 *
281 * @return the number of arguments this Function takes, as known to the
282 * calling SQL context
283 */
284 int getArgCount() {
285 return iArgCount - (bConnection ? 1
286 : 0);
287 }
288
289 /**
290 * Resolves the arguments supplied to this Function object against the
291 * specified TableFilter.
292 *
293 *
294 * @param f the TableFilter against which to resolve this Function
295 * object's arguments
296 * @throws SQLException if there is a problem resolving an argument
297 * against the specified TableFilter
298 */
299 void resolve(TableFilter f) throws SQLException {
300
301 for (int i = 0; i < iArgCount; i++) {
302 if (eArg[i] != null) {
303 eArg[i].resolve(f);
304 }
305 }
306 }
307
308 /**
309 * Checks each of this object's arguments for resolution, throwing a
310 * SQLException if any arguments have not yet been resolved.
311 *
312 *
313 * @throws SQLException if any arguments have not yet been resolved
314 */
315 void checkResolved() throws SQLException {
316
317 for (int i = 0; i < iArgCount; i++) {
318 if (eArg[i] != null) {
319 eArg[i].checkResolved();
320 }
321 }
322 }
323
324 /**
325 * Retrieves the java.sql.Types type of the argument at the specified
326 * offset in this Function object's paramter list
327 *
328 *
329 * @param i the offset of the desired argument in this Function object's
330 * paramter list
331 * @return the specified argument's java.sql.Types type
332 */
333 int getArgType(int i) {
334 return iArgType[i];
335 }
336
337 /**
338 * Retrieves the java.sql.Types type of this Function
339 * object's return type
340 *
341 *
342 * @return this Function object's java.sql.Types return type
343 */
344 int getReturnType() {
345 return iReturnType;
346 }
347
348 /**
349 * Binds the specified expression to the specified argument in this
350 * Function object's paramter list.
351 *
352 *
353 * @param i the position of the agument to bind to
354 * @param e the expression to bind
355 */
356 void setArgument(int i, Expression e) {
357
358 if (bConnection) {
359 i++;
360 }
361
362 eArg[i] = e;
363 }
364 }