OK heres the contribution:
I wrote the code, it's mine, and I'm contributing it to H2 for distribution
multiple-licensed under the H2 License, version 1.0, and under the Eclipse
Public License, version 1.0 (http://h2database.com/html/license.html).
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: Alfred Reibenschuh contributed to the H2 Group
C
--
You received this message because you are subscribed to the Google Groups "H2
Database" group.
To view this discussion on the web visit
https://groups.google.com/d/msg/h2-database/-/M_Uhuvhn9x8J.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/h2-database?hl=en.
Index: src/main/org/h2/engine/Database.java
===================================================================
--- src/main/org/h2/engine/Database.java (revision 4285)
+++ src/main/org/h2/engine/Database.java (working copy)
@@ -23,6 +23,7 @@
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.constraint.Constraint;
+import org.h2.ext.FnRegUtil;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
@@ -174,6 +175,8 @@
private final DbSettings dbSettings;
private final int reconnectCheckDelay;
private int logMode;
+ private boolean doFnAutoReg = false;
+ private String fnAutoRegModules = "";
public Database(ConnectionInfo ci, String cipher) {
String name = ci.getName();
@@ -209,6 +212,8 @@
}
this.multiVersion = ci.getProperty("MVCC", false);
this.logMode = ci.getProperty("LOG", PageStore.LOG_MODE_SYNC);
+ this.doFnAutoReg = ci.getProperty("EXT_FN_AUTOREG", false);
+ this.fnAutoRegModules = 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,43 @@
}
getLobStorage().init();
systemSession.commit(true);
+
+ // load extension functions/aggregates into default schema
+ try {
+ if(this.doFnAutoReg)
+ {
+ FnRegUtil.registerFromClasspath(
+ trace,
+ this.getClass().getClassLoader(),
+ systemSession,
+ mainSchema,
+ "");
+
+ for(String fnMod : this.fnAutoRegModules.split(","))
+ {
+ FnRegUtil.registerFromClasspath(
+ trace,
+ this.getClass().getClassLoader(),
+ systemSession,
+ mainSchema,
+ fnMod.trim());
+ }
+ }
+ else
+ {
+ FnRegUtil.registerFromClasspath(
+ trace,
+ this.getClass().getClassLoader(),
+ systemSession,
+ mainSchema,
+ null);
+ }
+ }
+ catch (Exception xe)
+ {
+ trace.info("problem with function auto-register -- {0}", xe.toString());
+ }
+
trace.info("opened {0}", databaseName);
if (checkpointAllowed > 0) {
afterWriting();
Index: src/main/org/h2/ext/FnRegUtil.java
===================================================================
--- src/main/org/h2/ext/FnRegUtil.java (revision 0)
+++ src/main/org/h2/ext/FnRegUtil.java (revision 0)
@@ -0,0 +1,147 @@
+/*
+ * 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;
+
+public abstract class FnRegUtil
+{
+ 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";
+
+ public static final boolean registerFunction(Trace trace, ClassLoader cl, Session session, Schema schema, String clazz, String method, String alias) throws ClassNotFoundException
+ {
+ 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 {0} of {1}/{2}", alias.toUpperCase(), clazz, method);
+ } catch(Exception xe) {
+ trace.info("registration error: function {0} of {1}/{2} -- {3}", alias.toUpperCase(), clazz, method, xe.toString());
+ return false;
+ }
+ return true;
+ }
+
+ public static final boolean registerAggregate(Trace trace, ClassLoader cl, Session session, Schema schema, String clazz, String alias) throws ClassNotFoundException
+ {
+ 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 xe)
+ {
+ trace.info("registration error: aggregate {0} of {1} -- {2}", alias.toUpperCase(), clazz, xe.toString());
+ return false;
+ }
+ return true;
+ }
+
+ public static final boolean register(Trace trace, ClassLoader cl, Session session, Schema schema, String clazz) throws ClassNotFoundException
+ {
+ String alias = null;
+ clazz = clazz.trim();
+
+ if(clazz.lastIndexOf(' ')>0)
+ {
+ alias = clazz.substring(clazz.lastIndexOf(' ')+1).toUpperCase();
+ clazz = clazz.substring(0, clazz.lastIndexOf(' ')).trim();
+ }
+ else
+ {
+ alias = clazz.substring(clazz.lastIndexOf('.')+1).toUpperCase();
+ }
+
+ Class cla = cl.loadClass(clazz);
+
+ for(Class x : cla.getInterfaces())
+ {
+ if("org.h2.api.AggregateFunction".equalsIgnoreCase(x.getCanonicalName()))
+ {
+ return registerAggregate(trace, cl, session, schema, clazz, alias);
+ }
+ }
+
+ // not an aggreagate impl
+ for(Method m : cla.getDeclaredMethods())
+ {
+ if(m.getName().toUpperCase().startsWith("FN_")
+ && m.isAccessible()
+ && (m.getModifiers() & Modifier.STATIC)==Modifier.STATIC)
+ {
+ registerFunction(trace, cl, session, schema, clazz, m.getName(), m.getName().substring(3));
+ }
+ }
+
+ return true;
+ }
+
+ public static final boolean registerFromClasspath(Trace trace, ClassLoader cl, Session session, Schema schema, String tag) throws ClassNotFoundException, IOException
+ {
+ boolean procEnabled = Boolean.parseBoolean(System.getProperty(SYS_PROP_AUTOREG, "false").trim());
+
+ if(procEnabled || tag!=null)
+ {
+ 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 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 xe)
+ {
+
+ }
+ finally
+ {
+ in.close();
+ }
+ }
+ }
+
+ return procEnabled;
+ }
+}
Index: src/docsrc/html/features.html
===================================================================
--- src/docsrc/html/features.html (revision 4285)
+++ src/docsrc/html/features.html (working copy)
@@ -1622,6 +1622,37 @@
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.
Index: src/docsrc/html/changelog.html
===================================================================
--- src/docsrc/html/changelog.html (revision 4285)
+++ src/docsrc/html/changelog.html (working copy)
@@ -18,7 +18,9 @@
<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