Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }