提交 2ae9a839 authored 作者: Thomas Mueller's avatar Thomas Mueller

Functions now reside within a schema.

上级 8e3b97cb
......@@ -18,7 +18,15 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>Cluster: after a cluster node failed, the second cluster node can now be re-created
<ul><li>New system property h2.functionsInSchema (default is false).
If enabled, the SCRIPT statement always includes the schema name in the CREATE ALIAS statement
(even if the schema is PUBLIC). This is not backward compatible with H2 versions 1.2.134 and older.
</li><li>Functions: it is no longer required to add a space after a comma in the parameter list.
Example: CREATE ALIAS PARSE_INT FOR "java.lang.Integer.parseInt(java.lang.String,int)"
</li><li>Functions now reside within a schema, similar to sequences.
If you do create such functions in schemas other than PUBLIC, then the database
can not be opened with older versions of H2.
</li><li>Cluster: after a cluster node failed, the second cluster node can now be re-created
and started without having to stop the first cluster node, and without having to stop
running applications. To do that, append ;AUTO_RECONNECT=TRUE to the database URL.
</li><li>SET EXCLUSIVE now supports 0 (disable), 1 (enable), and 2 (enable and close all other connections).
......
......@@ -1445,8 +1445,9 @@ The settings in the URL override the settings passed as a separate parameter.
In addition to the built-in functions, this database supports user-defined Java functions.
In this database, Java functions can be used as stored procedures as well.
A function must be declared (registered) before it can be used.
A functions can be defined using source code, or as a reference to
a compiled class that is available in the classpath.
A function can be defined using source code, or as a reference to
a compiled class that is available in the classpath. By default, the
function aliases are stored in the current schema.
</p>
<h3>Referencing a Compiled Method</h3>
......
......@@ -25,6 +25,7 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
<h2>Version 1.3.x: Planned Changes</h2>
<ul><li>Lob storage: enable the system property h2.lobInDatabase by default.
</li><li>Automatic ANALYZE: set the system property h2.analyzeAuto to 2000.
</li><li>Enable h2.functionsInSchema.
</li></ul>
<h2>Priority 1</h2>
......@@ -254,7 +255,6 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
</li><li>Native search: support "phrase search", wildcard search (* and ?), case-insensitive search, boolean operators, and grouping
</li><li>Improve documentation of access rights
</li><li>Support ENUM data type (see MySQL, PostgreSQL, MS SQL Server, maybe others)
</li><li>Support a schema name for Java functions
</li><li>Remember the user defined data type (domain) of a column
</li><li>Support Jackcess (MS Access databases)
</li><li>Built-in methods to write large objects (BLOB and CLOB): FILE_WRITE('test.txt', 'Hello World')
......
......@@ -313,7 +313,7 @@ public class Parser {
} else if (readIf("CREATE")) {
c = parseCreate();
} else if (readIf("CALL")) {
c = parserCall();
c = parseCall();
} else if (readIf("CHECKPOINT")) {
c = parseCheckpoint();
} else if (readIf("COMMENT")) {
......@@ -420,7 +420,7 @@ public class Parser {
case 'v':
case 'V':
if (readIf("VALUES")) {
c = parserCall();
c = parseCall();
}
break;
case 'w':
......@@ -980,6 +980,7 @@ public class Parser {
}
} else {
String tableName = readIdentifierWithSchema(null);
Schema schema = getSchema();
if (readIf("(")) {
Schema mainSchema = database.getSchema(Constants.SCHEMA_MAIN);
if (equalsToken(tableName, RangeTable.NAME)) {
......@@ -989,7 +990,7 @@ public class Parser {
read(")");
table = new RangeTable(mainSchema, min, max);
} else {
Expression func = readFunction(tableName);
Expression func = readFunction(schema, tableName);
if (!(func instanceof FunctionCall)) {
throw getSyntaxError();
}
......@@ -1163,8 +1164,9 @@ public class Parser {
return command;
} else if (readIf("ALIAS")) {
boolean ifExists = readIfExists(false);
DropFunctionAlias command = new DropFunctionAlias(session);
command.setAliasName(readUniqueIdentifier());
String aliasName = readIdentifierWithSchema();
DropFunctionAlias command = new DropFunctionAlias(session, getSchema());
command.setAliasName(aliasName);
ifExists = readIfExists(ifExists);
command.setIfExists(ifExists);
return command;
......@@ -1934,10 +1936,15 @@ public class Parser {
return orderList;
}
private JavaFunction readJavaFunction(String name) {
FunctionAlias functionAlias = database.findFunctionAlias(name);
private JavaFunction readJavaFunction(Schema schema, String functionName) {
FunctionAlias functionAlias = null;
if (schema != null) {
functionAlias = schema.findFunction(functionName);
} else {
functionAlias = findFunctionAlias(session.getCurrentSchemaName(), functionName);
}
if (functionAlias == null) {
throw DbException.get(ErrorCode.FUNCTION_NOT_FOUND_1, name);
throw DbException.get(ErrorCode.FUNCTION_NOT_FOUND_1, functionName);
}
Expression[] args;
ArrayList<Expression> argList = New.arrayList();
......@@ -1967,7 +1974,10 @@ public class Parser {
return agg;
}
private Expression readFunction(String name) {
private Expression readFunction(Schema schema, String name) {
if (schema != null) {
return readJavaFunction(schema, name);
}
int agg = Aggregate.getAggregateType(name);
if (agg >= 0) {
return readAggregate(agg);
......@@ -1978,7 +1988,7 @@ public class Parser {
if (aggregate != null) {
return readJavaAggregate(aggregate);
}
return readJavaFunction(name);
return readJavaFunction(null, name);
}
switch (function.getFunctionType()) {
case Function.CAST: {
......@@ -2145,7 +2155,13 @@ public class Parser {
return expr;
}
String name = readColumnIdentifier();
if (readIf(".")) {
Schema s = database.findSchema(objectName);
if (s != null && readIf("(")) {
// only if the token before the dot is a valid schema name,
// otherwise the old style Oracle outer join doesn't work:
// t.x = t2.x(+)
return readFunction(s, name);
} else if (readIf(".")) {
String schema = objectName;
objectName = name;
expr = readWildcardOrSequenceValue(schema, objectName);
......@@ -2153,7 +2169,15 @@ public class Parser {
return expr;
}
name = readColumnIdentifier();
if (readIf(".")) {
if (readIf("(")) {
String databaseName = schema;
if (!equalsToken(database.getShortName(), databaseName)) {
throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1, databaseName);
}
schema = objectName;
objectName = name;
return readFunction(database.getSchema(schema), name);
} else if (readIf(".")) {
String databaseName = schema;
if (!equalsToken(database.getShortName(), databaseName)) {
throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1, databaseName);
......@@ -2239,7 +2263,7 @@ public class Parser {
if (currentTokenQuoted) {
read();
if (readIf("(")) {
r = readFunction(name);
r = readFunction(null, name);
} else if (readIf(".")) {
r = readTermObjectDot(name);
} else {
......@@ -2264,7 +2288,7 @@ public class Parser {
r = readWhen(left);
}
} else if (readIf("(")) {
r = readFunction(name);
r = readFunction(null, name);
} else if (equalsToken("CURRENT_USER", name)) {
r = readFunctionWithoutParameters("USER");
} else if (equalsToken("CURRENT", name)) {
......@@ -3655,7 +3679,7 @@ public class Parser {
return command;
}
private Call parserCall() {
private Call parseCall() {
Call command = new Call(session);
currentPrepared = command;
command.setExpression(readExpression());
......@@ -3734,11 +3758,12 @@ public class Parser {
boolean ifNotExists = readIfNoExists();
CreateAggregate command = new CreateAggregate(session);
command.setForce(force);
String name = readUniqueIdentifier();
String name = readIdentifierWithSchema();
if (isKeyword(name) || Function.getFunction(database, name) != null || Aggregate.getAggregateType(name) >= 0) {
throw DbException.get(ErrorCode.FUNCTION_ALIAS_ALREADY_EXISTS_1, name);
}
command.setName(name);
command.setSchema(getSchema());
command.setIfNotExists(ifNotExists);
read("FOR");
command.setJavaClassMethod(readUniqueIdentifier());
......@@ -3849,13 +3874,13 @@ public class Parser {
private CreateFunctionAlias parseCreateFunctionAlias(boolean force) {
boolean ifNotExists = readIfNoExists();
CreateFunctionAlias command = new CreateFunctionAlias(session);
command.setForce(force);
String name = readUniqueIdentifier();
if (isKeyword(name) || Function.getFunction(database, name) != null || Aggregate.getAggregateType(name) >= 0) {
throw DbException.get(ErrorCode.FUNCTION_ALIAS_ALREADY_EXISTS_1, name);
String aliasName = readIdentifierWithSchema();
if (isKeyword(aliasName) || Function.getFunction(database, aliasName) != null || Aggregate.getAggregateType(aliasName) >= 0) {
throw DbException.get(ErrorCode.FUNCTION_ALIAS_ALREADY_EXISTS_1, aliasName);
}
command.setAliasName(name);
CreateFunctionAlias command = new CreateFunctionAlias(session, getSchema());
command.setForce(force);
command.setAliasName(aliasName);
command.setIfNotExists(ifNotExists);
command.setDeterministic(readIf("DETERMINISTIC"));
if (readIf("AS")) {
......@@ -4357,24 +4382,43 @@ public class Parser {
throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, tableName);
}
private FunctionAlias findFunctionAlias(String schema, String aliasName) {
FunctionAlias functionAlias = database.getSchema(schema).findFunction(aliasName);
if (functionAlias != null) {
return functionAlias;
}
String[] schemaNames = session.getSchemaSearchPath();
if (schemaNames != null) {
for (String n : schemaNames) {
functionAlias = database.getSchema(n).findFunction(aliasName);
if (functionAlias != null) {
return functionAlias;
}
}
}
return null;
}
private Sequence findSequence(String schema, String sequenceName) {
Sequence sequence = database.getSchema(schema).findSequence(sequenceName);
if (sequence != null) {
return sequence;
}
String[] schemaNames = session.getSchemaSearchPath();
for (int i = 0; schemaNames != null && i < schemaNames.length; i++) {
Schema s = database.getSchema(schemaNames[i]);
sequence = s.findSequence(sequenceName);
if (sequence != null) {
return sequence;
if (schemaNames != null) {
for (String n : schemaNames) {
sequence = database.getSchema(n).findSequence(sequenceName);
if (sequence != null) {
return sequence;
}
}
}
return null;
}
private Sequence readSequence() {
// same algorithm than readTableOrView
// same algorithm as readTableOrView
String sequenceName = readIdentifierWithSchema(null);
if (schemaName != null) {
return getSchema().getSequence(sequenceName);
......
......@@ -11,6 +11,7 @@ import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.UserAggregate;
import org.h2.message.DbException;
import org.h2.schema.Schema;
/**
* This class represents the statement
......@@ -18,6 +19,7 @@ import org.h2.message.DbException;
*/
public class CreateAggregate extends DefineCommand {
private Schema schema;
private String name;
private String javaClassMethod;
private boolean ifNotExists;
......@@ -31,7 +33,7 @@ public class CreateAggregate extends DefineCommand {
session.commit(true);
session.getUser().checkAdmin();
Database db = session.getDatabase();
if (db.findAggregate(name) != null || db.findFunctionAlias(name) != null) {
if (db.findAggregate(name) != null || schema.findFunction(name) != null) {
if (!ifNotExists) {
throw DbException.get(ErrorCode.FUNCTION_ALIAS_ALREADY_EXISTS_1, name);
}
......@@ -43,6 +45,10 @@ public class CreateAggregate extends DefineCommand {
return 0;
}
public void setSchema(Schema schema) {
this.schema = schema;
}
public void setName(String name) {
this.name = name;
}
......
......@@ -11,12 +11,14 @@ import org.h2.engine.Database;
import org.h2.engine.FunctionAlias;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.schema.Schema;
import org.h2.util.StringUtils;
/**
* This class represents the statement
* CREATE ALIAS
*/
public class CreateFunctionAlias extends DefineCommand {
public class CreateFunctionAlias extends SchemaCommand {
private String aliasName;
private String javaClassMethod;
......@@ -25,15 +27,15 @@ public class CreateFunctionAlias extends DefineCommand {
private boolean force;
private String source;
public CreateFunctionAlias(Session session) {
super(session);
public CreateFunctionAlias(Session session, Schema schema) {
super(session, schema);
}
public int update() {
session.commit(true);
session.getUser().checkAdmin();
Database db = session.getDatabase();
if (db.findFunctionAlias(aliasName) != null) {
if (getSchema().findFunction(aliasName) != null) {
if (!ifNotExists) {
throw DbException.get(ErrorCode.FUNCTION_ALIAS_ALREADY_EXISTS_1, aliasName);
}
......@@ -41,12 +43,12 @@ public class CreateFunctionAlias extends DefineCommand {
int id = getObjectId();
FunctionAlias functionAlias;
if (javaClassMethod != null) {
functionAlias = FunctionAlias.newInstance(db, id, aliasName, javaClassMethod, force);
functionAlias = FunctionAlias.newInstance(getSchema(), id, aliasName, javaClassMethod, force);
} else {
functionAlias = FunctionAlias.newInstanceFromSource(db, id, aliasName, source, force);
functionAlias = FunctionAlias.newInstanceFromSource(getSchema(), id, aliasName, source, force);
}
functionAlias.setDeterministic(deterministic);
db.addDatabaseObject(session, functionAlias);
db.addSchemaObject(session, functionAlias);
}
return 0;
}
......@@ -55,8 +57,13 @@ public class CreateFunctionAlias extends DefineCommand {
this.aliasName = name;
}
public void setJavaClassMethod(String string) {
this.javaClassMethod = string;
/**
* Set the qualified method name after removing whitespaces.
*
* @param method the qualified method name
*/
public void setJavaClassMethod(String method) {
this.javaClassMethod = StringUtils.replaceAll(method, " ", "");
}
public void setIfNotExists(boolean ifNotExists) {
......
......@@ -74,6 +74,7 @@ public class DropDatabase extends DefineCommand {
list.addAll(db.getAllSchemaObjects(DbObject.CONSTRAINT));
list.addAll(db.getAllSchemaObjects(DbObject.TRIGGER));
list.addAll(db.getAllSchemaObjects(DbObject.CONSTANT));
list.addAll(db.getAllSchemaObjects(DbObject.FUNCTION_ALIAS));
for (SchemaObject obj : list) {
if (obj.isHidden()) {
continue;
......@@ -94,7 +95,6 @@ public class DropDatabase extends DefineCommand {
}
ArrayList<DbObject> dbObjects = New.arrayList();
dbObjects.addAll(db.getAllRights());
dbObjects.addAll(db.getAllFunctionAliases());
dbObjects.addAll(db.getAllAggregates());
dbObjects.addAll(db.getAllUserDataTypes());
for (DbObject obj : dbObjects) {
......
......@@ -11,31 +11,32 @@ import org.h2.engine.Database;
import org.h2.engine.FunctionAlias;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.schema.Schema;
/**
* This class represents the statement
* DROP ALIAS
*/
public class DropFunctionAlias extends DefineCommand {
public class DropFunctionAlias extends SchemaCommand {
private String aliasName;
private boolean ifExists;
public DropFunctionAlias(Session session) {
super(session);
public DropFunctionAlias(Session session, Schema schema) {
super(session, schema);
}
public int update() {
session.getUser().checkAdmin();
session.commit(true);
Database db = session.getDatabase();
FunctionAlias functionAlias = db.findFunctionAlias(aliasName);
FunctionAlias functionAlias = getSchema().findFunction(aliasName);
if (functionAlias == null) {
if (!ifExists) {
throw DbException.get(ErrorCode.FUNCTION_ALIAS_NOT_FOUND_1, aliasName);
}
} else {
db.removeDatabaseObject(session, functionAlias);
db.removeSchemaObject(session, functionAlias);
}
return 0;
}
......
......@@ -49,8 +49,7 @@ public class SetComment extends DefineCommand {
object = db.getSchema(schemaName).getConstraint(objectName);
break;
case DbObject.FUNCTION_ALIAS:
schemaName = null;
object = db.findFunctionAlias(objectName);
object = db.getSchema(schemaName).findFunction(objectName);
errorCode = ErrorCode.FUNCTION_ALIAS_NOT_FOUND_1;
break;
case DbObject.INDEX:
......
......@@ -24,7 +24,6 @@ import org.h2.engine.Comment;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.FunctionAlias;
import org.h2.engine.Right;
import org.h2.engine.Role;
import org.h2.engine.Session;
......@@ -159,11 +158,11 @@ public class ScriptCommand extends ScriptBase {
Constant constant = (Constant) obj;
add(constant.getCreateSQL(), false);
}
for (FunctionAlias alias : db.getAllFunctionAliases()) {
for (SchemaObject obj : db.getAllSchemaObjects(DbObject.FUNCTION_ALIAS)) {
if (drop) {
add(alias.getDropSQL(), false);
add(obj.getDropSQL(), false);
}
add(alias.getCreateSQL(), false);
add(obj.getCreateSQL(), false);
}
for (UserAggregate agg : db.getAllAggregates()) {
if (drop) {
......
......@@ -260,6 +260,15 @@ public class SysProperties {
*/
public static final int ESTIMATED_FUNCTION_TABLE_ROWS = getIntSetting("h2.estimatedFunctionTableRows", 1000);
/**
* System property <code>h2.functionsInSchema</code> (default:
* false).<br />
* If set, all functions are stored in a schema. Specially, the SCRIPT statement
* will always include the schema name in the CREATE ALIAS statement.
* This is not backward compatible with H2 versions 1.2.134 and older.
*/
public static final boolean FUNCTIONS_IN_SCHEMA = getBooleanSetting("h2.functionsInSchema", false);
/**
* System property <code>h2.identifiersToUpper</code> (default: true).<br />
* Unquoted identifiers in SQL statements are case insensitive and converted
......
......@@ -94,7 +94,6 @@ public class Database implements DataHandler {
private final HashMap<String, Setting> settings = New.hashMap();
private final HashMap<String, Schema> schemas = New.hashMap();
private final HashMap<String, Right> rights = New.hashMap();
private final HashMap<String, FunctionAlias> functionAliases = New.hashMap();
private final HashMap<String, UserDataType> userDataTypes = New.hashMap();
private final HashMap<String, UserAggregate> aggregates = New.hashMap();
private final HashMap<String, Comment> comments = New.hashMap();
......@@ -759,9 +758,6 @@ public class Database implements DataHandler {
case DbObject.RIGHT:
result = rights;
break;
case DbObject.FUNCTION_ALIAS:
result = functionAliases;
break;
case DbObject.SCHEMA:
result = schemas;
break;
......@@ -846,16 +842,6 @@ public class Database implements DataHandler {
return comments.get(key);
}
/**
* Get the user defined function if it exists, or null if not.
*
* @param name the name of the user defined function
* @return the function or null
*/
public FunctionAlias findFunctionAlias(String name) {
return functionAliases.get(name);
}
/**
* Get the role if it exists, or null if not.
*
......@@ -1219,10 +1205,6 @@ public class Database implements DataHandler {
return New.arrayList(comments.values());
}
public ArrayList<FunctionAlias> getAllFunctionAliases() {
return New.arrayList(functionAliases.values());
}
public int getAllowLiterals() {
if (starting) {
return Constants.ALLOW_LITERALS_ALL;
......
......@@ -19,12 +19,14 @@ import org.h2.constant.SysProperties;
import org.h2.expression.Expression;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.schema.Schema;
import org.h2.schema.SchemaObjectBase;
import org.h2.table.Table;
import org.h2.util.Utils;
import org.h2.util.New;
import org.h2.util.SourceCompiler;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -35,7 +37,7 @@ import org.h2.value.ValueNull;
* @author Thomas Mueller
* @author Gary Tong
*/
public class FunctionAlias extends DbObjectBase {
public class FunctionAlias extends SchemaObjectBase {
private String className;
private String methodName;
......@@ -43,8 +45,8 @@ public class FunctionAlias extends DbObjectBase {
private JavaMethod[] javaMethods;
private boolean deterministic;
private FunctionAlias(Database db, int id, String name) {
initDbObjectBase(db, id, name, Trace.FUNCTION);
private FunctionAlias(Schema schema, int id, String name) {
initSchemaObjectBase(schema, id, name, Trace.FUNCTION);
}
/**
......@@ -57,8 +59,8 @@ public class FunctionAlias extends DbObjectBase {
* @param force create the object even if the class or method does not exist
* @return the database object
*/
public static FunctionAlias newInstance(Database db, int id, String name, String javaClassMethod, boolean force) {
FunctionAlias alias = new FunctionAlias(db, id, name);
public static FunctionAlias newInstance(Schema schema, int id, String name, String javaClassMethod, boolean force) {
FunctionAlias alias = new FunctionAlias(schema, id, name);
int paren = javaClassMethod.indexOf('(');
int lastDot = javaClassMethod.lastIndexOf('.', paren < 0 ? javaClassMethod.length() : paren);
if (lastDot < 0) {
......@@ -80,8 +82,8 @@ public class FunctionAlias extends DbObjectBase {
* @param force create the object even if the class or method does not exist
* @return the database object
*/
public static FunctionAlias newInstanceFromSource(Database db, int id, String name, String source, boolean force) {
FunctionAlias alias = new FunctionAlias(db, id, name);
public static FunctionAlias newInstanceFromSource(Schema schema, int id, String name, String source, boolean force) {
FunctionAlias alias = new FunctionAlias(schema, id, name);
alias.source = source;
alias.init(force);
return alias;
......@@ -167,7 +169,9 @@ public class FunctionAlias extends DbObjectBase {
StatementBuilder buff = new StatementBuilder(m.getName());
buff.append('(');
for (Class< ? > p : m.getParameterTypes()) {
buff.appendExceptFirst(", ");
// do not use a space here, because spaces are removed
// in CreateFunctionAlias.setJavaClassMethod()
buff.appendExceptFirst(",");
if (p.isArray()) {
buff.append(p.getComponentType().getName()).append("[]");
} else {
......@@ -185,6 +189,14 @@ public class FunctionAlias extends DbObjectBase {
return "DROP ALIAS IF EXISTS " + getSQL();
}
public String getSQL() {
// TODO can remove this method once FUNCTIONS_IN_SCHEMA is enabled
if (SysProperties.FUNCTIONS_IN_SCHEMA || !getSchema().getName().equals(Constants.SCHEMA_MAIN)) {
return super.getSQL();
}
return Parser.quoteIdentifier(getName());
}
public String getCreateSQL() {
StringBuilder buff = new StringBuilder("CREATE FORCE ALIAS ");
buff.append(getSQL());
......
......@@ -17,6 +17,7 @@ import org.h2.constraint.Constraint;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.DbObjectBase;
import org.h2.engine.FunctionAlias;
import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.index.Index;
......@@ -43,6 +44,7 @@ public class Schema extends DbObjectBase {
private HashMap<String, TriggerObject> triggers = New.hashMap();
private HashMap<String, Constraint> constraints = New.hashMap();
private HashMap<String, Constant> constants = New.hashMap();
private HashMap<String, FunctionAlias> functions = New.hashMap();
/**
* The set of returned unique names that are not yet stored. It is used to
......@@ -121,6 +123,10 @@ public class Schema extends DbObjectBase {
Constant obj = (Constant) constants.values().toArray()[0];
database.removeSchemaObject(session, obj);
}
while (functions != null && functions.size() > 0) {
FunctionAlias obj = (FunctionAlias) functions.values().toArray()[0];
database.removeSchemaObject(session, obj);
}
database.removeMeta(session, getId());
owner = null;
invalidate();
......@@ -161,6 +167,9 @@ public class Schema extends DbObjectBase {
case DbObject.CONSTANT:
result = constants;
break;
case DbObject.FUNCTION_ALIAS:
result = functions;
break;
default:
throw DbException.throwInternalError("type=" + type);
}
......@@ -169,6 +178,8 @@ public class Schema extends DbObjectBase {
/**
* Add an object to this schema.
* This method must not be called within CreateSchemaObject;
* use Database.addSchemaObject() instead
*
* @param obj the object to add
*/
......@@ -291,6 +302,17 @@ public class Schema extends DbObjectBase {
public Constant findConstant(String constantName) {
return constants.get(constantName);
}
/**
* Try to find a user defined function with this name. This method returns
* null if no object with this name exists.
*
* @param functionAlias the object name
* @return the object or null
*/
public FunctionAlias findFunction(String functionAlias) {
return functions.get(functionAlias);
}
/**
* Release a unique object name.
......@@ -449,7 +471,7 @@ public class Schema extends DbObjectBase {
* Get all objects of the given type.
*
* @param type the object type
* @return a (possible empty) list of all objects
* @return a (possible empty) list of all objects
*/
public ArrayList<SchemaObject> getAll(int type) {
HashMap<String, SchemaObject> map = getMap(type);
......@@ -459,11 +481,20 @@ public class Schema extends DbObjectBase {
/**
* Get all tables and views.
*
* @return a (possible empty) list of all objects
* @return a (possible empty) list of all objects
*/
public ArrayList<Table> getAllTablesAndViews() {
return New.arrayList(tablesAndViews.values());
}
/**
* Get all functions.
*
* @return a (possible empty) list of all objects
*/
public ArrayList<FunctionAlias> getAllFunctionAliases() {
return New.arrayList(functions.values());
}
/**
* Remove an object from this schema.
......
......@@ -1061,7 +1061,8 @@ public class MetaTable extends Table {
break;
}
case FUNCTION_ALIASES: {
for (FunctionAlias alias : database.getAllFunctionAliases()) {
for (SchemaObject aliasAsSchemaObject : database.getAllSchemaObjects(DbObject.FUNCTION_ALIAS)) {
FunctionAlias alias = (FunctionAlias) aliasAsSchemaObject;
for (FunctionAlias.JavaMethod method : alias.getJavaMethods()) {
int returnsResult = method.getDataType() == Value.NULL ? DatabaseMetaData.procedureNoResult
: DatabaseMetaData.procedureReturnsResult;
......@@ -1069,7 +1070,7 @@ public class MetaTable extends Table {
// ALIAS_CATALOG
catalog,
// ALIAS_SCHEMA
Constants.SCHEMA_MAIN,
alias.getSchema().getName(),
// ALIAS_NAME
identifier(alias.getName()),
// JAVA_CLASS
......@@ -1119,7 +1120,8 @@ public class MetaTable extends Table {
break;
}
case FUNCTION_COLUMNS: {
for (FunctionAlias alias : database.getAllFunctionAliases()) {
for (SchemaObject aliasAsSchemaObject : database.getAllSchemaObjects(DbObject.FUNCTION_ALIAS)) {
FunctionAlias alias = (FunctionAlias) aliasAsSchemaObject;
for (FunctionAlias.JavaMethod method : alias.getJavaMethods()) {
Class< ? >[] columnList = method.getColumnClasses();
for (int k = 0; k < columnList.length; k++) {
......@@ -1132,7 +1134,7 @@ public class MetaTable extends Table {
// ALIAS_CATALOG
catalog,
// ALIAS_SCHEMA
Constants.SCHEMA_MAIN,
alias.getSchema().getName(),
// ALIAS_NAME
identifier(alias.getName()),
// JAVA_CLASS
......
......@@ -292,8 +292,8 @@ java org.h2.test.TestAll timer
int testing;
System.setProperty("h2.lobInDatabase", "true");
System.setProperty("h2.analyzeAuto", "100");
// System.setProperty("h2.lobInDatabase", "true");
// System.setProperty("h2.analyzeAuto", "100");
/*
......
......@@ -51,6 +51,8 @@ public class TestFunctions extends TestBase implements AggregateFunction {
testSource();
testDynamicArgumentAndReturn();
testUUID();
testWhiteSpacesInParameters();
testSchemaSearchPath();
testDeterministic();
testTransactionId();
testPrecision();
......@@ -90,6 +92,9 @@ public class TestFunctions extends TestBase implements AggregateFunction {
ResultSet rs;
stat.execute("create force alias sayHi as 'String test(String name) {\n" +
"return \"Hello \" + name;\n}'");
rs = stat.executeQuery("SELECT ALIAS_NAME FROM INFORMATION_SCHEMA.FUNCTION_ALIASES");
rs.next();
assertEquals("SAY" + "HI", rs.getString(1));
rs = stat.executeQuery("call sayHi('Joe')");
rs.next();
assertEquals("Hello Joe", rs.getString(1));
......@@ -157,7 +162,8 @@ public class TestFunctions extends TestBase implements AggregateFunction {
rs.next();
assertEquals(0, rs.getInt(1));
stat.execute("drop alias getCount");
rs = stat.executeQuery("SELECT * FROM INFORMATION_SCHEMA.FUNCTION_ALIASES WHERE UPPER(ALIAS_NAME) = 'GETCOUNT'");
assertEquals(false, rs.next());
stat.execute("create alias reverse deterministic for \""+getClass().getName()+".reverse\"");
rs = stat.executeQuery("select reverse(x) from system_range(700, 700)");
rs.next();
......@@ -467,6 +473,58 @@ public class TestFunctions extends TestBase implements AggregateFunction {
conn.close();
}
/**
* White spaces in javaMethodDescriptors are deleted during
* CreateFunctionAlias, and all further processing is normalized.
*/
private void testWhiteSpacesInParameters() throws SQLException {
deleteDb("functions");
Connection conn = getConnection("functions");
Statement stat = conn.createStatement();
// with white space
stat.execute("CREATE ALIAS PARSE_INT2 FOR \"java.lang.Integer.parseInt(java.lang.String, int)\"");
ResultSet rs;
rs = stat.executeQuery("CALL PARSE_INT2('473', 10)");
rs.next();
assertEquals(473, rs.getInt(1));
stat.execute("DROP ALIAS PARSE_INT2");
// without white space
stat.execute("CREATE ALIAS PARSE_INT2 FOR \"java.lang.Integer.parseInt(java.lang.String,int)\"");
stat.execute("DROP ALIAS PARSE_INT2");
}
private void testSchemaSearchPath() throws SQLException {
deleteDb("functions");
Connection conn = getConnection("functions");
Statement stat = conn.createStatement();
ResultSet rs;
stat.execute("CREATE SCHEMA TEST");
stat.execute("SET SCHEMA TEST");
stat.execute("CREATE ALIAS PARSE_INT2 FOR \"java.lang.Integer.parseInt(java.lang.String, int)\";");
rs = stat.executeQuery("SELECT ALIAS_NAME FROM INFORMATION_SCHEMA.FUNCTION_ALIASES WHERE ALIAS_SCHEMA ='TEST'");
rs.next();
assertEquals("PARSE_INT2", rs.getString(1));
stat.execute("DROP ALIAS PARSE_INT2");
stat.execute("SET SCHEMA PUBLIC");
stat.execute("CREATE ALIAS TEST.PARSE_INT2 FOR \"java.lang.Integer.parseInt(java.lang.String, int)\";");
stat.execute("SET SCHEMA_SEARCH_PATH PUBLIC, TEST");
rs = stat.executeQuery("CALL PARSE_INT2('-FF', 16)");
rs.next();
assertEquals(-255, rs.getInt(1));
rs = stat.executeQuery("SELECT ALIAS_NAME FROM INFORMATION_SCHEMA.FUNCTION_ALIASES WHERE ALIAS_SCHEMA ='TEST'");
rs.next();
assertEquals("PARSE_INT2", rs.getString(1));
rs = stat.executeQuery("CALL TEST.PARSE_INT2('-2147483648', 10)");
rs.next();
assertEquals(-2147483648, rs.getInt(1));
rs = stat.executeQuery("CALL FUNCTIONS.TEST.PARSE_INT2('-2147483648', 10)");
rs.next();
assertEquals(-2147483648, rs.getInt(1));
conn.close();
}
private void assertCallResult(String expected, Statement stat, String sql) throws SQLException {
ResultSet rs = stat.executeQuery("CALL " + sql);
rs.next();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论