alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

HSQLDB example source code file (Function.java)

This example HSQLDB source code file (Function.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - HSQLDB tags/keywords

binary, class, expression, expression, hashmap, hsqlexception, hsqlexception, javaobject, jdbc, method, object, object, reflection, sql, string, string, stringbuffer

The HSQLDB Function.java source code

/*
 * For work developed by the HSQL Development Group:
 *
 * Copyright (c) 2001-2010, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *
 * For work originally developed by the Hypersonic SQL Group:
 *
 * Copyright (c) 1995-2000, The Hypersonic SQL Group.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Hypersonic SQL Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * on behalf of the Hypersonic SQL Group.
 */


package org.hsqldb;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Connection;

import org.hsqldb.lib.HashMap;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.StringConverter;
import org.hsqldb.types.Binary;
import org.hsqldb.types.JavaObject;

// fredt@users 20020912 - patch 1.7.1 - shortcut treatment of identity() call
// fredt@users 20020912 - patch 1.7.1 - cache java.lang.reflect.Method objects
// fredt@users 20021013 - patch 1.7.1 - ignore non-static methods
// boucherb@users 20030201 - patch 1.7.2 - direct calls for org.hsqldb.Library
// fredt@users 20030621 - patch 1.7.2 - shortcut treatment of session calls
// boucherb@users 200404xx - doc 1.7.2 - updates toward 1.7.2 final

/**
 * Provides services to evaluate SQL function and stored procedure calls,
 * by invoking Java methods.
 *
 * Extended in successive versions of HSQLDB.
 *
 * @author Thomas Mueller (Hypersonic SQL Group)
 * @version 1.8.0
 * @since Hypersonic SQL
 */
class Function {

    private String         sFunction;
    private Method         mMethod;
    private String         returnClassName;
    private Class[]        aArgClasses;
    private int            iReturnType;
    private int            iArgCount;
    private int            iSqlArgCount;
    private int            iSqlArgStart;
    private int[]          iArgType;
    private boolean[]      bArgNullable;
    Expression[]           eArg;
    private boolean        bConnection;
    private static HashMap methodCache = new HashMap();
    private int            fID;
    String                 name;        // name used to call function
    boolean                isSimple;    //CURRENT_TIME, NOW etc.
    boolean                hasAggregate;

    /**
     * Constructs a new Function object with the given function call name
     * and using the specified Session context. <p>
     *
     * The call name is the fully qualified name of a static Java method, in
     * the form "package.class.method."  This implies that Java
     * methods with the same fully qualified name but different signatures
     * cannot be used properly as HSQLDB SQL functions or stored procedures.
     * For instance, it is impossible to call both System.getProperty(String)
     * and System.getProperty(String,String) under this arrangement, because
     * the HSQLDB Function object is unable to differentiate between the two;
     * it simply chooses the first method matching the FQN in the array of
     * methods obtained from calling getMethods() on an instance of the
     * Class indicated in the FQN, hiding all other methods with the same
     * FQN. <p>
     *
     * The function FQN must match at least one static Java method FQN in the
     * specified class or construction cannot procede and an HsqlException is
     * thrown. <p>
     *
     * The isSimple parameter is true when certain SQL standard functions
     * that are used without brackets are invokded.
     *
     * @param name this Function object's call name
     * @param fqn the fully qualified name of a Java method
     * @param isSimple if true, used to evalate CURRENT_TIME, NOW etc.
     *      evaluate
     * @throws HsqlException if the specified function FQN corresponds to no
     *      Java method
     */
    Function(String name, String fqn, boolean isSimple) throws HsqlException {

        this.name     = name;
        this.isSimple = isSimple;

//        cSession      = session;
        sFunction = fqn;
        fID       = Library.functionID(fqn);

        int i = fqn.lastIndexOf('.');

        Trace.check(i != -1, Trace.UNEXPECTED_TOKEN, fqn);

        String classname = fqn.substring(0, i);

        mMethod = (Method) methodCache.get(fqn);

        if (mMethod == null) {
            String methodname    = fqn.substring(i + 1);
            Class  classinstance = null;

            try {
                classinstance = Class.forName(classname);
            } catch (Exception e) {
                throw Trace.error(Trace.FUNCTION_NOT_FOUND,
                                  Trace.Message_Pair, new Object[] {
                    classname, e
                });
            }

            // public only, but includes those inherited from
            // superclasses and superinterfaces.  List is unordered.
            Method[] methods = classinstance.getMethods();

            for (i = 0; i < methods.length; i++) {
                Method m = methods[i];

                if (m.getName().equals(methodname)
                        && Modifier.isStatic(m.getModifiers())) {
                    mMethod = m;

                    break;
                }
            }

            Trace.check(mMethod != null, Trace.UNKNOWN_FUNCTION, methodname);
            methodCache.put(fqn, mMethod);
        }

        Class returnClass = mMethod.getReturnType();

        if (returnClass.equals(org.hsqldb.Result.class)) {

            // For now, we can write stored procedures whose
            // descriptor explicitly specifies the above return type.
            // Later, this will be modified or replaced to provide proper
            // support for jdbcCallableStatement OUT mode return parameter,
            // multiple results (Result.MULTI etc.)
            iReturnType = Types.OTHER;
        } else {

            // Now we support the following construction-time return type
            // Classes, as specified by the method descriptor:
            //
            // 1.) any primitive or primitive wrapper type, except Byte(.TYPE),
            //     Short(.TYPE) and Float(.TYPE) (TBD; narrow if no truncation)
            //
            // 2.) any primitive array type
            //
            // 3.) any non-primitive array whose base component implements
            // java.io.Serializable
            //
            // 4.) any class implementing java.io.Serializable, except those
            //     described in 1.) as currently unsupported
            //
            // 5.) java.lang.Object
            //
            // For java.lang.Object, checking is deferred from the construction
            // stage to the evaluation stage.  In general, for the evaluation
            // to succeed, the runtime class of the retrieved Object must be
            //
            // 1.) any primitive or primitive wrapper type, except Byte(.TYPE),
            //     Short(.TYPE) and Float(.TYPE) (TBD; narrow if no trunction)
            //
            // 2.) any primitive array type
            // 3.) any non-primitive array whose base component implements
            //     java.io.Serializable
            //
            // 4.) any class implementing java.io.Serializable, except those
            //     described in 1.) as currently unsupported
            //
            // Additionally, it is possible for the evaluation to succeed under
            // an SQL CALL if the runtime Class of the returned Object is not
            // from the list above but is from the list below:
            //
            // 1.) is org.hsqldb.Result
            // 2.) is org.hsqldb.jdbc.jdbcResultSet
            //
            // In these special cases, the statement executor notices the
            // types and presents the client with a view the underlying result
            // rather than with a view of the object as an opaque scalar value
            //
            iReturnType = Types.getParameterTypeNr(returnClass);
        }

        returnClassName =
            Types.getFunctionReturnClassName(returnClass.getName());
        aArgClasses  = mMethod.getParameterTypes();
        iArgCount    = aArgClasses.length;
        iArgType     = new int[iArgCount];
        bArgNullable = new boolean[iArgCount];

        for (i = 0; i < aArgClasses.length; i++) {
            Class  a    = aArgClasses[i];
            String type = a.getName();

            if ((i == 0) && a.equals(Connection.class)) {

                // TODO: provide jdbc:default:connection url functionality
                //
                // only the first parameter can be a Connection
                bConnection = true;
            } else {

                // see discussion above for iReturnType
                iArgType[i]     = Types.getParameterTypeNr(a);
                bArgNullable[i] = !a.isPrimitive();
            }
        }

        iSqlArgCount = iArgCount;

        if (bConnection) {
            iSqlArgCount--;

            iSqlArgStart = 1;
        } else {
            iSqlArgStart = 0;
        }

        eArg = new Expression[iArgCount];
    }

    /**
     * Evaluates and returns this Function in the context of the session.<p>
     */
    Object getValue(Session session) throws HsqlException {

        switch (fID) {

            case Library.curtime :
                return session.getCurrentTime();

            case Library.curdate :
                return session.getCurrentDate();

            case Library.database :
                return session.getDatabase().getPath();

            case Library.getAutoCommit :
                return session.isAutoCommit() ? Boolean.TRUE
                                              : Boolean.FALSE;

            case Library.identity :
                return session.getLastIdentity();

            case Library.isReadOnlyDatabase :
                return session.getDatabase().databaseReadOnly ? Boolean.TRUE
                                                              : Boolean.FALSE;

            case Library.isReadOnlyConnection :
                return session.isReadOnly() ? Boolean.TRUE
                                            : Boolean.FALSE;

            case Library.isReadOnlyDatabaseFiles :
                return session.getDatabase().isFilesReadOnly() ? Boolean.TRUE
                                                               : Boolean
                                                               .FALSE;

            case Library.now :
                return session.getCurrentTimestamp();

            case Library.user :
                return session.getUsername();
        }

        Object[] oArg = getArguments(session);

        if (oArg == null) {
            return null;
        }

        return getValue(session, oArg);
    }

    /**
     * Evaluates the Function with the given arguments in the session context.
     */
    Object getValue(Session session,
                    Object[] arguments) throws HsqlException {

        if (bConnection) {
            arguments[0] = session.getInternalConnection();
        }

        try {
            Object ret = (fID >= 0) ? Library.invoke(fID, arguments)
                                    : mMethod.invoke(null, arguments);

            return Column.convertObject(ret, iReturnType);
        } catch (InvocationTargetException e) {

            // thrown by user functions
            Throwable t = e.getTargetException();
            String    s = sFunction + " : " + t.toString();

            throw Trace.error(Trace.FUNCTION_CALL_ERROR, s);
        } catch (IllegalAccessException e) {

            // never thrown in this method
            throw Trace.error(Trace.FUNCTION_CALL_ERROR);
        }

        // Library function throw HsqlException
    }

    private Object[] getArguments(Session session) throws HsqlException {

        int      i    = bConnection ? 1
                                    : 0;
        Object[] oArg = new Object[iArgCount];

        for (; i < iArgCount; i++) {
            Expression e = eArg[i];
            Object     o = null;

            if (e != null) {

                // no argument: null
                o = e.getValue(session, iArgType[i]);
            }

            if ((o == null) &&!bArgNullable[i]) {

                // null argument for primitive datatype: don't call
                return null;
            }

            if (o instanceof JavaObject) {
                o = ((JavaObject) o).getObject();
            } else if (o instanceof Binary) {
                o = ((Binary) o).getBytes();
            }

            oArg[i] = o;
        }

        return oArg;
    }

    /**
     * returns null if any non-nullable element of values is null
     */
    private Object[] getNotNull(Object[] values) throws HsqlException {

        int i = bConnection ? 1
                            : 0;

        for (; i < iArgCount; i++) {
            Object o = values[i];

            if (o == null &&!bArgNullable[i]) {

                // null argument for primitive datatype: don't call
                return null;
            }
        }

        return values;
    }

    void collectInGroupByExpressions(HsqlArrayList colExps) {

        for (int i = 0; i < iArgCount; i++) {
            Expression e = eArg[i];

            if (e != null) {
                e.collectInGroupByExpressions(colExps);
            }
        }
    }

    Object getAggregatedValue(Session session,
                              Object currValue) throws HsqlException {

        Object[] valueArray = (Object[]) currValue;

        if (valueArray == null) {
            valueArray = new Object[iArgCount];
        }

        for (int i = 0; i < iArgCount; i++) {
            Expression e = eArg[i];

            if (eArg[i] != null) {
                if (eArg[i].isAggregate()) {
                    valueArray[i] = Column.convertObject(
                        e.getAggregatedValue(session, valueArray[i]),
                        iArgType[i]);
                } else {
                    valueArray[i] = e.getValue(session, iArgType[i]);
                }
            }
        }

        valueArray = getNotNull(valueArray);

        if (valueArray == null) {
            return null;
        }

        return getValue(session, valueArray);
    }

    Object updateAggregatingValue(Session session,
                                  Object currValue) throws HsqlException {

        Object[] valueArray = (Object[]) currValue;

        if (valueArray == null) {
            valueArray = new Object[iArgCount];
        }

        for (int i = 0; i < iArgCount; i++) {
            Expression e = eArg[i];

            if (eArg[i] != null) {
                valueArray[i] = e.updateAggregatingValue(session,
                        valueArray[i]);
            }
        }

        return valueArray;
    }

    /**
     * Returns the number of parameters that must be supplied to evaluate
     * this Function object from SQL.  <p>
     *
     * This value may be different than the number of parameters of the
     * underlying Java method.  This is because HSQLDB automatically detects
     * if the first parameter is of type java.sql.Connection, and supplies a
     * live Connection object constructed from the evaluating session context
     * if so.
     */
    int getArgCount() {
        return iSqlArgCount;
    }

    /**
     * Remnoves the Table filters from Expression parameters to this Function.
     *
     * @throws HsqlException if there is a problem resolving a parameter
     * against the specified TableFilter
     */
/*
    void removeFilters() throws HsqlException {

        Expression e;

        for (int i = iSqlArgStart; i < iArgCount; i++) {
            e = eArg[i];

            if (e != null) {
                e.removeFilters();
            }
        }
    }
*/
    void replaceAliases(Expression[] columns,
                        int length) throws HsqlException {

        Expression e;

        for (int i = iSqlArgStart; i < iArgCount; i++) {
            e = eArg[i];

            if (e != null) {
                if (e.exprType == Expression.COLUMN) {
                    eArg[i] = e.getExpressionForAlias(columns, length);
                } else {
                    e.replaceAliases(columns, length);
                }
            }
        }
    }

    /**
     * Checks the Expresion parameters to this Function object against the
     * set of TableFilter.
     */
    void checkTables(HsqlArrayList fa) throws HsqlException {

        Expression e;

        for (int i = iSqlArgStart; i < iArgCount; i++) {
            e = eArg[i];

            if (e != null) {
                e.checkTables(fa);
            }
        }
    }

    /**
     * Resolves the Expression parameters to this Function object against the
     * specified TableFilter.
     */
    void resolveTables(TableFilter f) throws HsqlException {

        Expression e;

        for (int i = iSqlArgStart; i < iArgCount; i++) {
            e = eArg[i];

            if (e != null) {
                e.resolveTables(f);
            }
        }
    }

    /**
     * Resolves the type of this expression and performs certain
     * transformations and optimisations of the expression tree.
     */
    void resolveType(Session session) throws HsqlException {

        Expression e;

        for (int i = iSqlArgStart; i < iArgCount; i++) {
            e = eArg[i];

            if (e != null) {
                if (e.isParam()) {
                    e.setDataType(iArgType[i]);

                    e.nullability    = getArgNullability(i);
                    e.valueClassName = getArgClass(i).getName();
                } else {
                    e.resolveTypes(session);
                }
            }
        }
    }

    /**
     * Checks each of this object's arguments for resolution, throwing an
     * HsqlException if any arguments have not yet been resolved. <p>
     * The check boolean argument is passed on to further check calls.<p>
     */
    boolean checkResolved(boolean check) throws HsqlException {

        boolean result = true;

        for (int i = iSqlArgStart; i < iArgCount; i++) {
            if (eArg[i] != null) {
                result = result && eArg[i].checkResolved(check);
            }
        }

        return result;
    }

    /**
     * Returns the type of the argument at the specified
     * offset in this Function object's paramter list. <p>
     */
    int getArgType(int i) {
        return iArgType[i];
    }

    /**
     * Returns the type of this Function
     * object's return type. <p>
     */
    int getReturnType() {
        return iReturnType;
    }

    /**
     * Binds the specified expression to the specified position in this
     * Function object's parameter list. <p>
     */
    void setArgument(int i, Expression e) {

        if (bConnection) {
            i++;
        }

        eArg[i]      = e;
        hasAggregate = hasAggregate || (e != null && e.isAggregate());
    }

    /**
     * Returns a DDL representation of this object. <p>
     */
    String getDLL() throws HsqlException {

        StringBuffer sb = new StringBuffer();

        // get the name as used by the CHECK statement
        String ddlName = name;

        if (isSimple) {
            return name;
        } else if (Token.T_TRIM.equals(name)) {

            // special case for TRIM
            sb.append(name).append('(');

            boolean leading  = eArg[2].testCondition(null);
            boolean trailing = eArg[3].testCondition(null);

            if (leading && trailing) {
                sb.append(Token.T_BOTH);
            } else {
                sb.append(leading ? Token.T_LEADING
                                  : Token.T_TRAILING);
            }

            // to do change to string
            sb.append(' ');

            String charval = (String) eArg[1].getValue(null);

            sb.append(Column.createSQLString(charval)).append(' ');
            sb.append(Token.T_FROM).append(' ');
            sb.append(eArg[0].getDDL()).append(')');

            return sb.toString();
        }

        if (sFunction.equals(name)) {
            ddlName = StringConverter.toQuotedString(name, '"', true);
        }

        sb.append(ddlName).append('(');

        for (int i = iSqlArgStart; i < eArg.length; i++) {
            sb.append(eArg[i].getDDL());

            if (i < eArg.length - 1) {
                sb.append(',');
            }
        }

        sb.append(')');

        return sb.toString();
    }

    /**
     * Returns a String representation of this object. <p>
     */
    public String describe(Session session) {

        StringBuffer sb = new StringBuffer();

        sb.append(super.toString()).append("=[\n");
        sb.append(sFunction).append("(");

        for (int i = iSqlArgStart; i < eArg.length; i++) {
            sb.append("[").append(eArg[i].describe(session)).append("]");
        }

        sb.append(") returns ").append(Types.getTypeString(getReturnType()));
        sb.append("]\n");

        return sb.toString();
    }

    /**
     * Returns the Java Class of the object returned by getValue(). <p>
     */
    String getReturnClassName() {
        return returnClassName;
    }

    /**
     * Returns the Java Class of the i'th argument. <p>
     */
    Class getArgClass(int i) {
        return aArgClasses[i];
    }

    /**
     * Returns the SQL nullability code of the i'th argument. <p>
     */
    int getArgNullability(int i) {
        return bArgNullable[i] ? Expression.NULLABLE
                               : Expression.NO_NULLS;
    }

    Method getMethod() {
        return mMethod;
    }
}

Other HSQLDB examples (source code examples)

Here is a short list of links related to this HSQLDB Function.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.