001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.engine;
007:
008: import java.lang.reflect.Method;
009: import java.lang.reflect.Modifier;
010: import java.sql.Connection;
011: import java.sql.SQLException;
012:
013: import org.h2.command.Parser;
014: import org.h2.constant.ErrorCode;
015: import org.h2.expression.Expression;
016: import org.h2.message.Message;
017: import org.h2.message.Trace;
018: import org.h2.table.Table;
019: import org.h2.value.DataType;
020: import org.h2.value.Value;
021: import org.h2.value.ValueNull;
022:
023: /**
024: * Represents a user defined function, or alias.
025: */
026: public class FunctionAlias extends DbObjectBase {
027:
028: private boolean hasConnectionParam;
029: private String className;
030: private String methodName;
031: private Method javaMethod;
032: private int paramCount;
033: private int dataType;
034:
035: public FunctionAlias(Database db, int id, String name,
036: String javaClassMethod, boolean force) throws SQLException {
037: super (db, id, name, Trace.FUNCTION);
038: int paren = javaClassMethod.indexOf('(');
039: int lastDot = javaClassMethod.lastIndexOf('.',
040: paren < 0 ? javaClassMethod.length() : paren);
041: if (lastDot < 0) {
042: throw Message.getSQLException(ErrorCode.SYNTAX_ERROR_1,
043: javaClassMethod);
044: }
045: className = javaClassMethod.substring(0, lastDot);
046: methodName = javaClassMethod.substring(lastDot + 1);
047: try {
048: // at least try to load the class, otherwise the data type is not
049: // initialized if it could be
050: load();
051: } catch (SQLException e) {
052: if (!force) {
053: throw e;
054: }
055: }
056: }
057:
058: private synchronized void load() throws SQLException {
059: if (javaMethod != null) {
060: return;
061: }
062: Class javaClass = database.loadUserClass(className);
063: Method[] methods = javaClass.getMethods();
064: for (int i = 0; i < methods.length; i++) {
065: Method m = methods[i];
066: if (!Modifier.isStatic(m.getModifiers())) {
067: continue;
068: }
069: if (m.getName().equals(methodName)) {
070: javaMethod = m;
071: break;
072: } else if (getMethodSignature(m).equals(methodName)) {
073: javaMethod = m;
074: break;
075: }
076: }
077: if (javaMethod == null) {
078: throw Message.getSQLException(ErrorCode.METHOD_NOT_FOUND_1,
079: methodName + " (" + className + ")");
080: }
081: Class[] paramClasses = javaMethod.getParameterTypes();
082: paramCount = paramClasses.length;
083: if (paramCount > 0) {
084: Class paramClass = paramClasses[0];
085: if (Connection.class.isAssignableFrom(paramClass)) {
086: hasConnectionParam = true;
087: paramCount--;
088: }
089: }
090: Class returnClass = javaMethod.getReturnType();
091: dataType = DataType.getTypeFromClass(returnClass);
092: }
093:
094: private String getMethodSignature(Method m) {
095: StringBuffer buff = new StringBuffer();
096: buff.append(m.getName());
097: buff.append('(');
098: Class[] params = m.getParameterTypes();
099: for (int i = 0; i < params.length; i++) {
100: if (i > 0) {
101: buff.append(", ");
102: }
103: Class p = params[i];
104: if (p.isArray()) {
105: buff.append(p.getComponentType().getName());
106: buff.append("[]");
107: } else {
108: buff.append(p.getName());
109: }
110: }
111: buff.append(')');
112: return buff.toString();
113: }
114:
115: public Class[] getColumnClasses() throws SQLException {
116: load();
117: return javaMethod.getParameterTypes();
118: }
119:
120: public int getDataType() {
121: return dataType;
122: }
123:
124: public String getCreateSQLForCopy(Table table, String quotedName) {
125: throw Message.getInternalError();
126: }
127:
128: public String getDropSQL() {
129: return "DROP ALIAS IF EXISTS " + getSQL();
130: }
131:
132: public String getCreateSQL() {
133: StringBuffer buff = new StringBuffer();
134: buff.append("CREATE FORCE ALIAS ");
135: buff.append(getSQL());
136: buff.append(" FOR ");
137: buff.append(Parser
138: .quoteIdentifier(className + "." + methodName));
139: return buff.toString();
140: }
141:
142: public int getType() {
143: return DbObject.FUNCTION_ALIAS;
144: }
145:
146: public synchronized void removeChildrenAndResources(Session session)
147: throws SQLException {
148: database.removeMeta(session, getId());
149: className = methodName = null;
150: javaMethod = null;
151: invalidate();
152: }
153:
154: public void checkRename() throws SQLException {
155: throw Message.getUnsupportedException();
156: }
157:
158: public Value getValue(Session session, Expression[] args)
159: throws SQLException {
160: return getValue(session, args, false);
161: }
162:
163: /**
164: * Call the user defined function and return the value.
165: *
166: * @param session the session
167: * @param args the argument list
168: * @param columnList true if the function should only return the column list
169: * @return the value
170: */
171: public synchronized Value getValue(Session session,
172: Expression[] args, boolean columnList) throws SQLException {
173: load();
174: Class[] paramClasses = javaMethod.getParameterTypes();
175: Object[] params = new Object[paramClasses.length];
176: int p = 0;
177: if (hasConnectionParam && params.length > 0) {
178: params[p++] = session.createConnection(columnList);
179: }
180: for (int a = 0; a < args.length && p < params.length; a++, p++) {
181: Class paramClass = paramClasses[p];
182: int type = DataType.getTypeFromClass(paramClass);
183: Value v = args[a].getValue(session);
184: v = v.convertTo(type);
185: Object o = v.getObject();
186: if (o == null) {
187: if (paramClass.isPrimitive()) {
188: if (columnList) {
189: // if the column list is requested, the parameters may
190: // be null
191: // need to set to default value otherwise the function
192: // can't be called at all
193: o = DataType
194: .getDefaultForPrimitiveType(paramClass);
195: } else {
196: // NULL for a java primitive: return NULL
197: return ValueNull.INSTANCE;
198: }
199: }
200: } else {
201: if (!paramClass.isAssignableFrom(o.getClass())
202: && !paramClass.isPrimitive()) {
203: o = DataType.convertTo(session, session
204: .createConnection(false), v, paramClass);
205: }
206: }
207: params[p] = o;
208: }
209: boolean old = session.getAutoCommit();
210: try {
211: session.setAutoCommit(false);
212: try {
213: Object returnValue;
214: returnValue = javaMethod.invoke(null, params);
215: if (returnValue == null) {
216: return ValueNull.INSTANCE;
217: }
218: Value ret = DataType.convertToValue(session,
219: returnValue, dataType);
220: return ret.convertTo(dataType);
221: } catch (Exception e) {
222: throw Message.convert(e);
223: }
224: } finally {
225: session.setAutoCommit(old);
226: }
227: }
228:
229: public int getParameterCount() throws SQLException {
230: load();
231: return paramCount;
232: }
233:
234: public String getJavaClassName() {
235: return this .className;
236: }
237:
238: public String getJavaMethodName() {
239: return this .methodName;
240: }
241:
242: public boolean hasConnectionParam() {
243: return this.hasConnectionParam;
244: }
245:
246: }
|