提交 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 ...@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <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. 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>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 </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"; ...@@ -1622,6 +1622,37 @@ CREATE ALIAS MATRIX FOR "org.h2.samples.Function.getMatrix";
SELECT * FROM MATRIX(4) ORDER BY X, Y; SELECT * FROM MATRIX(4) ORDER BY X, Y;
</pre> </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> <h2 id="triggers">Triggers</h2>
<p> <p>
This database supports Java triggers that are called before or after a row is updated, inserted or deleted. 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; ...@@ -23,6 +23,7 @@ import org.h2.constant.DbSettings;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.constraint.Constraint; import org.h2.constraint.Constraint;
import org.h2.ext.AutoRegisterFunctionAliases;
import org.h2.index.Cursor; import org.h2.index.Cursor;
import org.h2.index.Index; import org.h2.index.Index;
import org.h2.index.IndexType; import org.h2.index.IndexType;
...@@ -174,6 +175,8 @@ public class Database implements DataHandler { ...@@ -174,6 +175,8 @@ public class Database implements DataHandler {
private final DbSettings dbSettings; private final DbSettings dbSettings;
private final int reconnectCheckDelay; private final int reconnectCheckDelay;
private int logMode; private int logMode;
private boolean autoRegisterFnAliases = false;
private String autoRegisterFnModules = "";
public Database(ConnectionInfo ci, String cipher) { public Database(ConnectionInfo ci, String cipher) {
String name = ci.getName(); String name = ci.getName();
...@@ -209,6 +212,8 @@ public class Database implements DataHandler { ...@@ -209,6 +212,8 @@ public class Database implements DataHandler {
} }
this.multiVersion = ci.getProperty("MVCC", false); this.multiVersion = ci.getProperty("MVCC", false);
this.logMode = ci.getProperty("LOG", PageStore.LOG_MODE_SYNC); 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; boolean closeAtVmShutdown = dbSettings.dbCloseOnExit;
int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE); int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT, int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT,
...@@ -655,6 +660,26 @@ public class Database implements DataHandler { ...@@ -655,6 +660,26 @@ public class Database implements DataHandler {
} }
getLobStorage().init(); getLobStorage().init();
systemSession.commit(true); 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); trace.info("opened {0}", databaseName);
if (checkpointAllowed > 0) { if (checkpointAllowed > 0) {
afterWriting(); 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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论