提交 a64e0c09 authored 作者: noelgrandin's avatar noelgrandin

New feature from Alfred Reibenschuh (terefang@gmail.com)

auto-registration of function aliases
上级 55edf736
......@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>New system property "h2.serializeJavaObject" (default: true) that allows to disable
<ul><li>New system property "h2.ext.fnAutoreg" (default: false) and jdbc-property "EXT_FN_AUTOREG"
that allows to autoregister aggregates and functions from special provided jars.
</li><li>New system property "h2.serializeJavaObject" (default: true) that allows to disable
serializing Java objects, so that the objects compareTo and toString methods can be used.
</li><li>Dylan has translated the H2 Console tool to Korean. Thanks a lot!
</li><li>Executing the statement CREATE INDEX IF ALREADY EXISTS if the index already exists
......
......@@ -1622,6 +1622,37 @@ CREATE ALIAS MATRIX FOR "org.h2.samples.Function.getMatrix";
SELECT * FROM MATRIX(4) ORDER BY X, Y;
</pre>
<h3>Auto-Referencing Compiled Methods / Aggregates from prepared JARs</h3>
<p>
You can enable auto-registration of methods by adding special prepared JARs
and setting the system-property "h2.ext.fnAutoreg" to "true".
The Classpath will be scanned for files named "/META-INF/org.h2.ext.FunctionRegistry",
which should look like:
</p>
<pre>
my.aggregate.Method my_AGGR
my.aggregate.my_AGGR2
my.function.MySQL_FN_Library
</pre>
<p>
If "my.function.MySQL_FN_Library" implements any static methods prefixed with "FN_", they will be registered in the database by calling <code>CREATE ALIAS ... FOR</code>:
</p>
<pre>
CREATE ALIAS UNIX_TIMESTAMP FOR "my.function.MySQL_FN_Library.FN_unix_TIMEstamp";
</pre>
<p>
If "my.aggregate.my_AGGR2" implements the "org.h2.api.AggregateFunction" interface it will be registered in the database by calling <code>CREATE AGGREGATE ... FOR</code>:
</p>
<pre>
CREATE AGGREGATE MY_AGGR2 FOR "my.aggregate.my_AGGR2";
</pre>
<p>
If "my.aggregate.Method" implements the "org.h2.api.AggregateFunction" interface it will be registered in the database by calling <code>CREATE AGGREGATE ... FOR</code>:
</p>
<pre>
CREATE AGGREGATE MY_AGGR FOR "my.aggregate.Method";
</pre>
<h2 id="triggers">Triggers</h2>
<p>
This database supports Java triggers that are called before or after a row is updated, inserted or deleted.
......
......@@ -23,6 +23,7 @@ import org.h2.constant.DbSettings;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.constraint.Constraint;
import org.h2.ext.AutoRegisterFunctionAliases;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
......@@ -174,6 +175,8 @@ public class Database implements DataHandler {
private final DbSettings dbSettings;
private final int reconnectCheckDelay;
private int logMode;
private boolean autoRegisterFnAliases = false;
private String autoRegisterFnModules = "";
public Database(ConnectionInfo ci, String cipher) {
String name = ci.getName();
......@@ -209,6 +212,8 @@ public class Database implements DataHandler {
}
this.multiVersion = ci.getProperty("MVCC", false);
this.logMode = ci.getProperty("LOG", PageStore.LOG_MODE_SYNC);
this.autoRegisterFnAliases = ci.getProperty("EXT_FN_AUTOREG", false);
this.autoRegisterFnModules = ci.getProperty("EXT_FN_AUTOREG_MODULE", "common");
boolean closeAtVmShutdown = dbSettings.dbCloseOnExit;
int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT,
......@@ -655,6 +660,26 @@ public class Database implements DataHandler {
}
getLobStorage().init();
systemSession.commit(true);
// load extension functions/aggregates into default schema
try {
if (autoRegisterFnAliases) {
AutoRegisterFunctionAliases.registerFromClasspath(trace, getClass().getClassLoader(), systemSession,
mainSchema, "");
for (String fnMod : this.autoRegisterFnModules.split(",")) {
AutoRegisterFunctionAliases.registerFromClasspath(trace, getClass().getClassLoader(),
systemSession, mainSchema, fnMod.trim());
}
} else if (Boolean.parseBoolean(System.getProperty(AutoRegisterFunctionAliases.SYS_PROP_AUTOREG, "false")
.trim())) {
AutoRegisterFunctionAliases.registerFromClasspath(trace, getClass().getClassLoader(), systemSession,
mainSchema, null);
}
} catch (Exception ex) {
trace.error(ex, "problem with function auto-register");
}
trace.info("opened {0}", databaseName);
if (checkpointAllowed > 0) {
afterWriting();
......
/*
* Copyright 2012 H2 Group. Multiple-Licensed under the H2 License, Version 1.0,
* and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package org.h2.ext;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.Collections;
import org.h2.command.ddl.CreateAggregate;
import org.h2.command.ddl.CreateFunctionAlias;
import org.h2.engine.Session;
import org.h2.message.Trace;
import org.h2.schema.Schema;
/**
* Implements auto-registration of FUNCTION ALIAS's at database open time from
* specially prepared jars.
*/
public final class AutoRegisterFunctionAliases {
/** Not meant to be instantiated */
private AutoRegisterFunctionAliases() {
}
public static final String META_INF_NAME = "/META-INF/org.h2.ext.FunctionRegistry";
public static final String SYS_PROP_AUTOREG = "h2.ext.fnAutoreg";
public static final String JDBC_PROP_AUTOREG = "extFnAutoreg";
private static final void registerFunction(Trace trace, Session session, Schema schema, String clazz,
String method, String alias) {
CreateFunctionAlias cfn = new CreateFunctionAlias(session, schema);
cfn.setIfNotExists(true);
cfn.setJavaClassMethod(clazz + "." + method);
cfn.setAliasName(alias.toUpperCase());
cfn.setDeterministic(true);
try {
cfn.update();
trace.info("registered function alias {0} of {1}/{2}", alias.toUpperCase(), clazz, method);
} catch (Exception ex) {
trace.error(ex, "registration error: function {0} of {1}/{2}", alias.toUpperCase(), clazz, method);
}
}
private static final void registerAggregate(Trace trace, Session session, Schema schema, String clazz, String alias) {
CreateAggregate ca = new CreateAggregate(session);
ca.setIfNotExists(true);
ca.setJavaClassMethod(clazz);
ca.setName(alias.toUpperCase());
ca.setSchema(schema);
try {
ca.update();
trace.info("registered aggregate {0} of {1}", alias.toUpperCase(), clazz);
} catch (Exception ex) {
trace.error(ex, "registration error: aggregate {0} of {1}", alias.toUpperCase(), clazz);
}
}
private static final void register(Trace trace, ClassLoader cl, Session session, Schema schema, String className)
throws ClassNotFoundException {
String alias = null;
className = className.trim();
if (className.lastIndexOf(' ') > 0) {
alias = className.substring(className.lastIndexOf(' ') + 1).toUpperCase();
className = className.substring(0, className.lastIndexOf(' ')).trim();
} else {
alias = className.substring(className.lastIndexOf('.') + 1).toUpperCase();
}
Class clazz = cl.loadClass(className);
for (Class x : clazz.getInterfaces()) {
if ("org.h2.api.AggregateFunction".equalsIgnoreCase(x.getCanonicalName())) {
registerAggregate(trace, session, schema, className, alias);
return;
}
}
// not an aggregate implementation
for (Method m : clazz.getDeclaredMethods()) {
if (m.getName().toUpperCase().startsWith("FN_") && m.isAccessible()
&& (m.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
registerFunction(trace, session, schema, className, m.getName(), m.getName().substring(3));
}
}
}
public static final void registerFromClasspath(Trace trace, ClassLoader cl, Session session, Schema schema,
String tag) throws IOException {
String metaName = META_INF_NAME;
if (tag != null && !"".equals(tag)) {
metaName += "." + (tag.toUpperCase());
}
for (URL url : Collections.list(cl.getResources(metaName))) {
trace.info("found function alias registration at {0}", url.toString());
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
try {
String line = null;
while ((line = in.readLine()) != null) {
line = line.trim();
if (!"".equals(line)) {
trace.info("registration of {0}", line);
register(trace, cl, session, schema, line);
}
}
} catch (Exception ex) {
trace.error(ex, "error reading from function auto-register file: {0}", url.toString());
} finally {
in.close();
}
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论