提交 09dac088 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 da52f772
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
</path> </path>
<target name="all" depends="jar,javadoc,docs"> <target name="all" depends="jar,javadoc,docs">
<delete includeemptydirs="true" verbose="true"> <delete includeemptydirs="true">
<fileset dir="bin" includes="**/*.txt"/> <fileset dir="bin" includes="**/*.txt"/>
<fileset dir="bin" includes="h2-test.exe"/> <fileset dir="bin" includes="h2-test.exe"/>
<fileset dir="bin" includes="org/**/*"/> <fileset dir="bin" includes="org/**/*"/>
...@@ -119,7 +119,7 @@ ...@@ -119,7 +119,7 @@
<target name="compileTest" unless="java.version.ok"> <target name="compileTest" unless="java.version.ok">
<echo message="Java version is ${java.specification.version} but source code is switched to ${jdk}."/> <echo message="Java version is ${java.specification.version} but source code is switched to ${jdk}."/>
<echo message="Run ant codeswitchJdk.. first."/> <echo message="Run ant codeswitchJdk... first."/>
</target> </target>
<target name="createPatch" depends="clean"> <target name="createPatch" depends="clean">
......
...@@ -13,31 +13,32 @@ H2 Database Engine ...@@ -13,31 +13,32 @@ H2 Database Engine
</head><body onload="frameMe();"> </head><body onload="frameMe();">
<table class="content"><tr class="content"><td class="content"><div class="contentDiv"> <table class="content"><tr class="content"><td class="content"><div class="contentDiv">
<h1> Downloads </h1> <h1>Downloads</h1>
<h3> Version 1.0.57 (2008-08-25, Current) </h3> <h3>Version 1.0.57 (2008-08-25, Current)</h3>
<p> <p>
<a href="http://www.h2database.com/h2-setup-2007-08-25.exe"> Windows Installer </a><br /> <a href="http://www.h2database.com/h2-setup-2007-08-25.exe">Windows Installer</a><br />
<a href="http://www.h2database.com/h2-2007-08-25.zip"> Platform-Independent Zip </a><br /> <a href="http://www.h2database.com/h2-2007-08-25.zip">Platform-Independent Zip</a><br />
</p> </p>
<h3> Version 1.0 / 2007-03-04 (Last Stable) </h3> <h3>Version 1.0 / 2007-03-04 (Last Stable)</h3>
<p> <p>
<a href="http://www.h2database.com/h2-setup-2007-03-04.exe"> Windows Installer </a><br /> <a href="http://www.h2database.com/h2-setup-2007-03-04.exe">Windows Installer</a><br />
<a href="http://www.h2database.com/h2-2007-03-04.zip"> Platform-Independent Zip </a><br /> <a href="http://www.h2database.com/h2-2007-03-04.zip">Platform-Independent Zip</a><br />
</p> </p>
<h3> Download Mirror </h3> <h3>Download Mirror</h3>
<p> <p>
<a href="http://code.google.com/p/h2database/downloads/list"> Platform-Independent Zip </a><br /> <a href="http://code.google.com/p/h2database/downloads/list">Platform-Independent Zip</a><br />
</p> </p>
<h3> Subversion Source Repository </h3> <h3>Subversion Source Repository</h3>
<p> <p>
<a href="http://code.google.com/p/h2database/source"> Google Code </a> <a href="http://code.google.com/p/h2database/source">Google Code</a>
</p> </p>
<p> <p>
For details about changes, see the <a href="history.html">Change Log</a> . </p> For details about changes, see the <a href="history.html">Change Log</a>.
</p>
</div></td></tr></table></body></html> </div></td></tr></table></body></html>
\ No newline at end of file
...@@ -44,7 +44,6 @@ Here is the list of known and confirmed issues as of ...@@ -44,7 +44,6 @@ Here is the list of known and confirmed issues as of
<ul> <ul>
<li>Some problems have been found with right outer join. Internally, it is converted to left outer join, which <li>Some problems have been found with right outer join. Internally, it is converted to left outer join, which
does not always produce the same results as other databases when used in combination with other joins. does not always produce the same results as other databases when used in combination with other joins.
</li><li>Using a CAST in a GROUP BY expression that is used in a view does not always work.
</li></ul> </li></ul>
<br /><a name="open_source"></a> <br /><a name="open_source"></a>
......
...@@ -241,6 +241,9 @@ It looks like the development of this database has stopped. The last release was ...@@ -241,6 +241,9 @@ It looks like the development of this database has stopped. The last release was
</tr><tr> </tr><tr>
<td><a href="http://appfuse.org">AppFuse</a></td> <td><a href="http://appfuse.org">AppFuse</a></td>
<td>Helps building web applications.</td> <td>Helps building web applications.</td>
</tr><tr>
<td><a href="http://wiki.blojsom.com">Blojsom</a></td>
<td>Java-based multi-blog, multi-user software package (Mac OS X Weblog Server)</td>
</tr><tr> </tr><tr>
<td><a href="http://bmarks-portlet.sourceforge.net">Bookmarks Portlet</a></td> <td><a href="http://bmarks-portlet.sourceforge.net">Bookmarks Portlet</a></td>
<td>JSR168 compliant bookmarks management portlet application.</td> <td>JSR168 compliant bookmarks management portlet application.</td>
......
...@@ -39,7 +39,22 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -39,7 +39,22 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3> <h3>Version 1.0 (Current)</h3>
<h3>Version 1.0.57 (2008-08-25)</h3><ul> <h3>Version 1.0.x (2007-09-x)</h3><ul>
<li>A database can now be opened even if class of a user defined function is not in the classpath.
Trying to call the function will throws an exception.
</li><li>User defined functions and constants may not overload built-in functions and constants.
This didn't work before, but now trying to create such an object didn't fail.
</li><li>Improved MultiDimension tool (for spatial queries): in the last few releases the tool was actually
slower than using a regular query (because index lookup got faster, and because the tool didn't
support prepared statements) Now the tool generates prepared statements,
and the performance is better again (about 5 times faster for a reasonable amount of data).
</li><li>Adding a foreign key or when re-enabling referential integrity for a table failed when checking
was enabled and the reference contained NULL.
</li><li>For PgServer, character encoding other than UTF-8 did not work correctly. Fixed.
</li><li>Using a function in a GROUP BY expression that is used in a view as a condition did not always work.
</li></ul>
<h3>Version 1.0.57 (2007-08-25)</h3><ul>
<li>New experimental feature MVCC (multi version concurrency control). <li>New experimental feature MVCC (multi version concurrency control).
Can be set as a option when opening the database (jdbc:h2:test;MVCC=TRUE) Can be set as a option when opening the database (jdbc:h2:test;MVCC=TRUE)
or as a system property (-Dh2.mvcc=true). This is work-in-progress, use it at your own risk. Feedback is welcome. or as a system property (-Dh2.mvcc=true). This is work-in-progress, use it at your own risk. Feedback is welcome.
...@@ -488,6 +503,7 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -488,6 +503,7 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Copy database: Tool with config GUI and batch mode, extensible (example: compare) </li><li>Copy database: Tool with config GUI and batch mode, extensible (example: compare)
</li><li>Document, implement tool for long running transactions using user defined compensation statements </li><li>Document, implement tool for long running transactions using user defined compensation statements
</li><li>Support SET TABLE DUAL READONLY </li><li>Support SET TABLE DUAL READONLY
</li><li>Linked schema using CSV files: one schema for a directory of files; support indexes for CSV files
</li><li>Don't write stack traces for common exceptions like duplicate key to the log by default </li><li>Don't write stack traces for common exceptions like duplicate key to the log by default
</li><li>Setting for MAX_QUERY_TIME (default no limit?) </li><li>Setting for MAX_QUERY_TIME (default no limit?)
</li><li>GCJ: what is the state now? </li><li>GCJ: what is the state now?
...@@ -653,7 +669,6 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -653,7 +669,6 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>A way (JDBC driver) to map an URL (jdbc:h2map:c1) to a connection object </li><li>A way (JDBC driver) to map an URL (jdbc:h2map:c1) to a connection object
</li><li>Build script for the embedded functionality only (h2embedded.jar) </li><li>Build script for the embedded functionality only (h2embedded.jar)
</li><li>Option for SCRIPT to only process one or a set of tables, and append to a file </li><li>Option for SCRIPT to only process one or a set of tables, and append to a file
</li><li>Linked schema using CSV files: one schema for a directory of files; support indexes for CSV files
</li><li>Support using a unique index for IS NULL (including linked tables) </li><li>Support using a unique index for IS NULL (including linked tables)
</li><li>Support linked tables to the current database </li><li>Support linked tables to the current database
</li><li>Support dynamic linked schema (automatically adding/updating/removing tables) </li><li>Support dynamic linked schema (automatically adding/updating/removing tables)
...@@ -705,6 +720,8 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -705,6 +720,8 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Trace: write os, file system, vm,... when opening the database </li><li>Trace: write os, file system, vm,... when opening the database
</li><li>Trace: write dangerous operations (set log 0,...) in every case (including when opening the database) </li><li>Trace: write dangerous operations (set log 0,...) in every case (including when opening the database)
</li><li>ParameterMetaData should return correct data type where possible (INSERT for example) </li><li>ParameterMetaData should return correct data type where possible (INSERT for example)
</li><li>Support indexes for views (probably requires materialized views)
</li><li>Linked tables that point to the same database should share the connection
</li></ul> </li></ul>
<h3>Not Planned</h3> <h3>Not Planned</h3>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -141,7 +141,7 @@ ...@@ -141,7 +141,7 @@
90118=Cannot drop table {0} 90118=Cannot drop table {0}
90119=User data type {0} already exists 90119=User data type {0} already exists
90120=User data type {0} not found 90120=User data type {0} not found
90121=Database called at VM shutdown; add ";DB_CLOSE_ON_EXIT\=FALSE" to the db URL to disable automatic database closing 90121=Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT\=FALSE" to the db URL)
90122=Operation not supported for table {0} when there are views on the table\: {1} 90122=Operation not supported for table {0} when there are views on the table\: {1}
90123=Cannot mix indexed and non-indexed parameters 90123=Cannot mix indexed and non-indexed parameters
90124=File not found\: {0} 90124=File not found\: {0}
......
...@@ -1932,7 +1932,9 @@ public class Parser { ...@@ -1932,7 +1932,9 @@ public class Parser {
String name = currentToken; String name = currentToken;
if(currentTokenQuoted) { if(currentTokenQuoted) {
read(); read();
if(readIf(".")) { if (readIf("(")) {
r = readFunction(name);
} else if(readIf(".")) {
r = readTermObjectDot(name); r = readTermObjectDot(name);
} else { } else {
r = new ExpressionColumn(database, currentSelect, null, null, name); r = new ExpressionColumn(database, currentSelect, null, null, name);
...@@ -3064,7 +3066,7 @@ public class Parser { ...@@ -3064,7 +3066,7 @@ public class Parser {
} else if(readIf("VIEW")) { } else if(readIf("VIEW")) {
return parseCreateView(force); return parseCreateView(force);
} else if (readIf("ALIAS")) { } else if (readIf("ALIAS")) {
return parseCreateFunctionAlias(); return parseCreateFunctionAlias(force);
} else if (readIf("SEQUENCE")) { } else if (readIf("SEQUENCE")) {
return parseCreateSequence(); return parseCreateSequence();
} else if (readIf("USER")) { } else if (readIf("USER")) {
...@@ -3243,6 +3245,9 @@ public class Parser { ...@@ -3243,6 +3245,9 @@ public class Parser {
boolean ifNotExists = readIfNoExists(); boolean ifNotExists = readIfNoExists();
String constantName = readIdentifierWithSchema(); String constantName = readIdentifierWithSchema();
Schema schema = getSchema(); Schema schema = getSchema();
if(isKeyword(constantName)) {
throw Message.getSQLException(ErrorCode.CONSTANT_ALREADY_EXISTS_1, constantName);
}
read("VALUE"); read("VALUE");
Expression expr = readExpression(); Expression expr = readExpression();
CreateConstant command = new CreateConstant(session, schema); CreateConstant command = new CreateConstant(session, schema);
...@@ -3340,10 +3345,15 @@ public class Parser { ...@@ -3340,10 +3345,15 @@ public class Parser {
return command; return command;
} }
private CreateFunctionAlias parseCreateFunctionAlias() throws SQLException { private CreateFunctionAlias parseCreateFunctionAlias(boolean force) throws SQLException {
boolean ifNotExists = readIfNoExists(); boolean ifNotExists = readIfNoExists();
CreateFunctionAlias command = new CreateFunctionAlias(session); CreateFunctionAlias command = new CreateFunctionAlias(session);
command.setAliasName(readUniqueIdentifier()); command.setForce(force);
String name = readUniqueIdentifier();
if(isKeyword(name) || Function.getFunction(database, name) != null || Aggregate.getAggregateType(name) >= 0) {
throw Message.getSQLException(ErrorCode.FUNCTION_ALIAS_ALREADY_EXISTS_1, name);
}
command.setAliasName(name);
command.setIfNotExists(ifNotExists); command.setIfNotExists(ifNotExists);
read("FOR"); read("FOR");
command.setJavaClassMethod(readUniqueIdentifier()); command.setJavaClassMethod(readUniqueIdentifier());
......
...@@ -17,7 +17,8 @@ public class CreateFunctionAlias extends DefineCommand { ...@@ -17,7 +17,8 @@ public class CreateFunctionAlias extends DefineCommand {
private String aliasName; private String aliasName;
private String javaClassMethod; private String javaClassMethod;
private boolean ifNotExists; private boolean ifNotExists;
private boolean force;
public CreateFunctionAlias(Session session) { public CreateFunctionAlias(Session session) {
super(session); super(session);
} }
...@@ -32,7 +33,7 @@ public class CreateFunctionAlias extends DefineCommand { ...@@ -32,7 +33,7 @@ public class CreateFunctionAlias extends DefineCommand {
} }
} else { } else {
int id = getObjectId(false, true); int id = getObjectId(false, true);
FunctionAlias functionAlias = new FunctionAlias(db, id, aliasName, javaClassMethod); FunctionAlias functionAlias = new FunctionAlias(db, id, aliasName, javaClassMethod, force);
db.addDatabaseObject(session, functionAlias); db.addDatabaseObject(session, functionAlias);
} }
return 0; return 0;
...@@ -50,4 +51,8 @@ public class CreateFunctionAlias extends DefineCommand { ...@@ -50,4 +51,8 @@ public class CreateFunctionAlias extends DefineCommand {
this.ifNotExists = ifNotExists; this.ifNotExists = ifNotExists;
} }
public void setForce(boolean force) {
this.force = force;
}
} }
...@@ -594,7 +594,7 @@ public class Select extends Query { ...@@ -594,7 +594,7 @@ public class Select extends Query {
} }
} }
if(having != null) { if(having != null) {
// could be set after addGlobalCondition // could be set in addGlobalCondition
// in this case the query is not run directly, just getPlanSQL is called // in this case the query is not run directly, just getPlanSQL is called
Expression h = having; Expression h = having;
buff.append("\nHAVING " + StringUtils.unEnclose(h.getSQL())); buff.append("\nHAVING " + StringUtils.unEnclose(h.getSQL()));
...@@ -685,16 +685,26 @@ public class Select extends Query { ...@@ -685,16 +685,26 @@ public class Select extends Query {
col = col.getNonAliasExpression(); col = col.getNonAliasExpression();
Expression comp = new Comparison(session, comparisonType, col, expr); Expression comp = new Comparison(session, comparisonType, col, expr);
comp = comp.optimize(session); comp = comp.optimize(session);
boolean addToCondition = true;
if(isGroupQuery) { if(isGroupQuery) {
if(havingIndex >= 0) { for(int i=0; groupIndex != null && i<groupIndex.length; i++) {
having = (Expression) expressions.get(havingIndex); if(groupIndex[i] == columnId) {
addToCondition = true;
break;
}
} }
if(having == null) { if(!addToCondition) {
having = comp; if(havingIndex >= 0) {
} else { having = (Expression) expressions.get(havingIndex);
having = new ConditionAndOr(ConditionAndOr.AND, having, comp); }
if(having == null) {
having = comp;
} else {
having = new ConditionAndOr(ConditionAndOr.AND, having, comp);
}
} }
} else { }
if(addToCondition) {
if(condition == null) { if(condition == null) {
condition = comp; condition = comp;
} else { } else {
......
...@@ -547,6 +547,14 @@ public class ConstraintReferential extends Constraint { ...@@ -547,6 +547,14 @@ public class ConstraintReferential extends Constraint {
} }
buff.append(" FROM "); buff.append(" FROM ");
buff.append(table.getSQL()); buff.append(table.getSQL());
buff.append(" WHERE ");
for(int i=0; i<columns.length; i++) {
if(i > 0) {
buff.append(" AND ");
}
buff.append(columns[i].getSQL());
buff.append(" IS NOT NULL ");
}
buff.append(" ORDER BY "); buff.append(" ORDER BY ");
for(int i=0; i<columns.length; i++) { for(int i=0; i<columns.length; i++) {
if(i>0) { if(i>0) {
......
...@@ -57,7 +57,8 @@ import org.h2.constant.SysProperties; ...@@ -57,7 +57,8 @@ import org.h2.constant.SysProperties;
* - Test with hibernate * - Test with hibernate
* - Scan for viruses * - Scan for viruses
* - Newsletter: send to h2database-news@googlegroups.com (http://groups.google.com/group/h2database-news) * - Newsletter: send to h2database-news@googlegroups.com (http://groups.google.com/group/h2database-news)
* - newsletter: prepare, send (always send to BCC!!) * - Newsletter: prepare, send (always send to BCC!!)
* - Newsletter: send to google groups, but without 'to unsubscribe...'
* - http://maven.apache.org/guides/mini/guide-ibiblio-upload.html * - http://maven.apache.org/guides/mini/guide-ibiblio-upload.html
* - Add to freshmeat, http://code.google.com/p/h2database/downloads/list * - Add to freshmeat, http://code.google.com/p/h2database/downloads/list
* *
......
...@@ -26,9 +26,9 @@ public class FunctionAlias extends DbObjectBase { ...@@ -26,9 +26,9 @@ public class FunctionAlias extends DbObjectBase {
private String methodName; private String methodName;
private Method javaMethod; private Method javaMethod;
private int paramCount; private int paramCount;
private final int dataType; private int dataType;
public FunctionAlias(Database db, int id, String name, String javaClassMethod) throws SQLException { public FunctionAlias(Database db, int id, String name, String javaClassMethod, boolean force) throws SQLException {
super(db, id, name, Trace.FUNCTION); super(db, id, name, Trace.FUNCTION);
int paren = javaClassMethod.indexOf('('); int paren = javaClassMethod.indexOf('(');
int lastDot = javaClassMethod.lastIndexOf('.', paren < 0 ? javaClassMethod.length() : paren); int lastDot = javaClassMethod.lastIndexOf('.', paren < 0 ? javaClassMethod.length() : paren);
...@@ -37,6 +37,15 @@ public class FunctionAlias extends DbObjectBase { ...@@ -37,6 +37,15 @@ public class FunctionAlias extends DbObjectBase {
} }
className = javaClassMethod.substring(0, lastDot); className = javaClassMethod.substring(0, lastDot);
methodName = javaClassMethod.substring(lastDot + 1); methodName = javaClassMethod.substring(lastDot + 1);
if(!force) {
load();
}
}
private synchronized void load() throws SQLException {
if(javaMethod != null) {
return;
}
Class javaClass; Class javaClass;
try { try {
javaClass = database.loadClass(className); javaClass = database.loadClass(className);
...@@ -94,7 +103,8 @@ public class FunctionAlias extends DbObjectBase { ...@@ -94,7 +103,8 @@ public class FunctionAlias extends DbObjectBase {
return buff.toString(); return buff.toString();
} }
public Class[] getColumnClasses() { public Class[] getColumnClasses() throws SQLException {
load();
return javaMethod.getParameterTypes(); return javaMethod.getParameterTypes();
} }
...@@ -112,7 +122,7 @@ public class FunctionAlias extends DbObjectBase { ...@@ -112,7 +122,7 @@ public class FunctionAlias extends DbObjectBase {
public String getCreateSQL() { public String getCreateSQL() {
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
buff.append("CREATE ALIAS "); buff.append("CREATE FORCE ALIAS ");
buff.append(getSQL()); buff.append(getSQL());
buff.append(" FOR "); buff.append(" FOR ");
buff.append(Parser.quoteIdentifier(className + "." + methodName)); buff.append(Parser.quoteIdentifier(className + "." + methodName));
...@@ -137,59 +147,59 @@ public class FunctionAlias extends DbObjectBase { ...@@ -137,59 +147,59 @@ public class FunctionAlias extends DbObjectBase {
return getValue(session, args, false); return getValue(session, args, false);
} }
public Value getValue(Session session, Expression[] args, boolean columnList) throws SQLException { public synchronized Value getValue(Session session, Expression[] args, boolean columnList) throws SQLException {
synchronized(this) { load();
Class[] paramClasses = javaMethod.getParameterTypes(); Class[] paramClasses = javaMethod.getParameterTypes();
Object[] params = new Object[paramClasses.length]; Object[] params = new Object[paramClasses.length];
int p = 0; int p = 0;
if(hasConnectionParam && params.length > 0) { if(hasConnectionParam && params.length > 0) {
params[p++] = session.createConnection(columnList); params[p++] = session.createConnection(columnList);
} }
for(int a=0; a<args.length && p<params.length; a++, p++) { for(int a=0; a<args.length && p<params.length; a++, p++) {
Class paramClass = paramClasses[p]; Class paramClass = paramClasses[p];
int type = DataType.getTypeFromClass(paramClass); int type = DataType.getTypeFromClass(paramClass);
Value v = args[a].getValue(session); Value v = args[a].getValue(session);
v = v.convertTo(type); v = v.convertTo(type);
Object o = v.getObject(); Object o = v.getObject();
if(o == null) { if(o == null) {
if(paramClass.isPrimitive()) { if(paramClass.isPrimitive()) {
if(columnList) { if(columnList) {
// if the column list is requested, the parameters may be null // if the column list is requested, the parameters may be null
// need to set to default value otherwise the function can't be called at all // need to set to default value otherwise the function can't be called at all
o = DataType.getDefaultForPrimitiveType(paramClass); o = DataType.getDefaultForPrimitiveType(paramClass);
} else { } else {
// NULL for a java primitive: return NULL // NULL for a java primitive: return NULL
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
}
}
} else {
if(!paramClass.isAssignableFrom(o.getClass()) && !paramClass.isPrimitive()) {
o = DataType.convertTo(session, session.createConnection(false), v, paramClass);
} }
} }
params[p] = o; } else {
if(!paramClass.isAssignableFrom(o.getClass()) && !paramClass.isPrimitive()) {
o = DataType.convertTo(session, session.createConnection(false), v, paramClass);
}
} }
boolean old = session.getAutoCommit(); params[p] = o;
}
boolean old = session.getAutoCommit();
try {
session.setAutoCommit(false);
try { try {
session.setAutoCommit(false); Object returnValue;
try { returnValue = javaMethod.invoke(null, params);
Object returnValue; if(returnValue == null) {
returnValue = javaMethod.invoke(null, params); return ValueNull.INSTANCE;
if(returnValue == null) {
return ValueNull.INSTANCE;
}
Value ret = DataType.convertToValue(session, returnValue, dataType);
return ret.convertTo(dataType);
} catch (Exception e) {
throw Message.convert(e);
} }
} finally { Value ret = DataType.convertToValue(session, returnValue, dataType);
session.setAutoCommit(old); return ret.convertTo(dataType);
} catch (Exception e) {
throw Message.convert(e);
} }
} finally {
session.setAutoCommit(old);
} }
} }
public int getParameterCount() { public int getParameterCount() throws SQLException {
load();
return paramCount; return paramCount;
} }
......
...@@ -13,7 +13,7 @@ import org.h2.value.ValueResultSet; ...@@ -13,7 +13,7 @@ import org.h2.value.ValueResultSet;
public interface FunctionCall { public interface FunctionCall {
String getName(); String getName();
int getParameterCount(); int getParameterCount() throws SQLException;
ValueResultSet getValueForColumnList(Session session, Expression[] nullArgs) throws SQLException; ValueResultSet getValueForColumnList(Session session, Expression[] nullArgs) throws SQLException;
int getType(); int getType();
Expression optimize(Session session) throws SQLException; Expression optimize(Session session) throws SQLException;
......
...@@ -96,7 +96,7 @@ public class JavaFunction extends Expression implements FunctionCall { ...@@ -96,7 +96,7 @@ public class JavaFunction extends Expression implements FunctionCall {
return functionAlias.getName(); return functionAlias.getName();
} }
public int getParameterCount() { public int getParameterCount() throws SQLException {
return functionAlias.getParameterCount(); return functionAlias.getParameterCount();
} }
......
...@@ -36,11 +36,8 @@ public class FunctionIndex extends BaseIndex { ...@@ -36,11 +36,8 @@ public class FunctionIndex extends BaseIndex {
} }
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException { public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
if(result == null) { // TODO sometimes result.reset() would be enough (but not when parameters are used)
result = functionTable.getResult(session); result = functionTable.getResult(session);
} else {
result.reset();
}
return new FunctionCursor(result); return new FunctionCursor(result);
} }
......
...@@ -102,10 +102,6 @@ public class MultiVersionCursor implements Cursor { ...@@ -102,10 +102,6 @@ public class MultiVersionCursor implements Cursor {
} }
} }
int sessionId = deltaRow.getSessionId(); int sessionId = deltaRow.getSessionId();
if(sessionId == 0) {
int testing;
System.out.println("sessionId==0");
}
boolean isThisSession = sessionId == session.getId(); boolean isThisSession = sessionId == session.getId();
boolean isDeleted = deltaRow.getDeleted(); boolean isDeleted = deltaRow.getDeleted();
if(isThisSession && isDeleted) { if(isThisSession && isDeleted) {
......
...@@ -92,14 +92,6 @@ public class MultiVersionIndex implements Index { ...@@ -92,14 +92,6 @@ public class MultiVersionIndex implements Index {
if(removeIfExists(session, row)) { if(removeIfExists(session, row)) {
// added and deleted in the same transaction: no change // added and deleted in the same transaction: no change
} else { } else {
int testing;
if(row.getSessionId() == 0) {
System.out.println("stop! " + row);
System.out.flush();
new Error().printStackTrace();
Runtime.getRuntime().halt(1);
}
delta.add(session, row); delta.add(session, row);
} }
} }
......
...@@ -19,14 +19,14 @@ public class ScanCursor implements Cursor { ...@@ -19,14 +19,14 @@ public class ScanCursor implements Cursor {
private Row row; private Row row;
private final Session session; private final Session session;
private final boolean multiVersion; private final boolean multiVersion;
private Iterator deleted; private Iterator delta;
ScanCursor(Session session, ScanIndex scan, boolean multiVersion) { ScanCursor(Session session, ScanIndex scan, boolean multiVersion) {
this.session = session; this.session = session;
this.scan = scan; this.scan = scan;
this.multiVersion = multiVersion; this.multiVersion = multiVersion;
if(multiVersion) { if(multiVersion) {
deleted = scan.getDeleted(); delta = scan.getDelta();
} }
row = null; row = null;
} }
...@@ -50,9 +50,9 @@ public class ScanCursor implements Cursor { ...@@ -50,9 +50,9 @@ public class ScanCursor implements Cursor {
public boolean next() throws SQLException { public boolean next() throws SQLException {
if(multiVersion) { if(multiVersion) {
while(true) { while(true) {
if(deleted.hasNext()) { if(delta.hasNext()) {
row = (Row) deleted.next(); row = (Row) delta.next();
if(row.getDeleted() && row.getSessionId() == session.getId()) { if(!row.getDeleted() || row.getSessionId() == session.getId()) {
row = null; row = null;
continue; continue;
} }
......
...@@ -35,7 +35,7 @@ public class ScanIndex extends BaseIndex { ...@@ -35,7 +35,7 @@ public class ScanIndex extends BaseIndex {
private boolean containsLargeObject; private boolean containsLargeObject;
private int rowCountDiff; private int rowCountDiff;
private HashMap sessionRowCount; private HashMap sessionRowCount;
private HashSet deleted; private HashSet delta;
public ScanIndex(TableData table, int id, Column[] columns, IndexType indexType) public ScanIndex(TableData table, int id, Column[] columns, IndexType indexType)
throws SQLException { throws SQLException {
...@@ -127,8 +127,12 @@ public class ScanIndex extends BaseIndex { ...@@ -127,8 +127,12 @@ public class ScanIndex extends BaseIndex {
} }
} }
if(database.isMultiVersion()) { if(database.isMultiVersion()) {
if(deleted != null) { if(delta == null) {
deleted.remove(row); delta = new HashSet();
}
boolean wasDeleted = delta.remove(row);
if(!wasDeleted) {
delta.add(row);
} }
incrementRowCount(session.getId(), 1); incrementRowCount(session.getId(), 1);
} }
...@@ -137,8 +141,8 @@ public class ScanIndex extends BaseIndex { ...@@ -137,8 +141,8 @@ public class ScanIndex extends BaseIndex {
public void commit(int operation, Row row) throws SQLException { public void commit(int operation, Row row) throws SQLException {
if(database.isMultiVersion()) { if(database.isMultiVersion()) {
if(deleted != null && operation == UndoLogRecord.DELETE) { if(delta != null && operation == UndoLogRecord.DELETE) {
deleted.remove(row); delta.remove(row);
} }
incrementRowCount(row.getSessionId(), operation == UndoLogRecord.DELETE ? 1 : -1); incrementRowCount(row.getSessionId(), operation == UndoLogRecord.DELETE ? 1 : -1);
} }
...@@ -173,10 +177,13 @@ public class ScanIndex extends BaseIndex { ...@@ -173,10 +177,13 @@ public class ScanIndex extends BaseIndex {
firstFree = key; firstFree = key;
} }
if(database.isMultiVersion()) { if(database.isMultiVersion()) {
if(deleted == null) { if(delta == null) {
deleted = new HashSet(); delta = new HashSet();
}
boolean wasAdded = delta.remove(row);
if(!wasAdded) {
delta.add(row);
} }
deleted.add(row);
incrementRowCount(session.getId(), -1); incrementRowCount(session.getId(), -1);
} }
rowCount--; rowCount--;
...@@ -252,8 +259,8 @@ public class ScanIndex extends BaseIndex { ...@@ -252,8 +259,8 @@ public class ScanIndex extends BaseIndex {
throw Message.getUnsupportedException(); throw Message.getUnsupportedException();
} }
public Iterator getDeleted() { public Iterator getDelta() {
return deleted == null ? Collections.EMPTY_LIST.iterator() : deleted.iterator(); return delta == null ? Collections.EMPTY_LIST.iterator() : delta.iterator();
} }
} }
...@@ -90,15 +90,15 @@ public class PgServerThread implements Runnable { ...@@ -90,15 +90,15 @@ public class PgServerThread implements Runnable {
} }
private String readString() throws IOException { private String readString() throws IOException {
StringBuffer buff = new StringBuffer(); ByteArrayOutputStream buff = new ByteArrayOutputStream();
while(true) { while(true) {
int x = dataIn.read(); int x = dataIn.read();
if(x <= 0) { if(x <= 0) {
break; break;
} }
buff.append((char)x); buff.write(x);
} }
return buff.toString(); return new String(buff.toByteArray(), getEncoding());
} }
private int readInt() throws IOException { private int readInt() throws IOException {
...@@ -676,7 +676,7 @@ public class PgServerThread implements Runnable { ...@@ -676,7 +676,7 @@ public class PgServerThread implements Runnable {
} }
private void writeString(String s) throws IOException { private void writeString(String s) throws IOException {
write(s.getBytes("UTF-8")); write(s.getBytes(getEncoding()));
write(0); write(0);
} }
......
...@@ -4,9 +4,13 @@ ...@@ -4,9 +4,13 @@
*/ */
package org.h2.tools; package org.h2.tools;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import org.h2.util.StringUtils;
/** /**
* A tool to help an application execute multi-dimensional range queries. * A tool to help an application execute multi-dimensional range queries.
...@@ -89,10 +93,60 @@ public class MultiDimension { ...@@ -89,10 +93,60 @@ public class MultiDimension {
// return n; // return n;
// } // }
/**
* Generates an optimized multi-dimensional range query.
* The query contains parameters. It can only be used with the H2 database.
*
* @param table the table name
* @param columns the list of columns
* @param scalarColumn the column name of the computed scalar column
* @return the query
*/
public String generatePreparedQuery(String table, String scalarColumn, String[] columns) {
StringBuffer buff = new StringBuffer("SELECT D.* FROM ");
buff.append(StringUtils.quoteIdentifier(table));
buff.append(" D, TABLE(_FROM_ BIGINT=?, _TO_ BIGINT=?) WHERE ");
buff.append(StringUtils.quoteIdentifier(scalarColumn));
buff.append(" BETWEEN _FROM_ AND _TO_");
for(int i=0; i<columns.length; i++) {
buff.append(" AND ");
buff.append(StringUtils.quoteIdentifier(columns[i]));
buff.append("+1 BETWEEN ?+1 AND ?+1");
}
return buff.toString();
}
/**
* Executes a prepared query that was generated using generatePreparedQuery.
*
* @param prep the prepared statement
* @param min the lower values
* @param max the upper values
* @return the result set
*/
public ResultSet getResult(PreparedStatement prep, int[] min, int[] max) throws SQLException {
long[][] ranges = getMortonRanges(min, max);
int len = ranges.length;
Long[] from = new Long[len];
Long[] to = new Long[len];
for(int i=0; i<len; i++) {
from[i] = new Long(ranges[i][0]);
to[i] = new Long(ranges[i][1]);
}
prep.setObject(1, from);
prep.setObject(2, to);
len = min.length;
for(int i=0, idx = 3; i<len; i++) {
prep.setInt(idx++, min[i]);
prep.setInt(idx++, max[i]);
}
return prep.executeQuery();
}
/** /**
* Generates an optimized multi-dimensional range query. * Generates an optimized multi-dimensional range query.
* This query is database independent, however the performance is
* not as good as when using generatePreparedQuery
* *
* @param table the table name * @param table the table name
* @param columns the list of columns * @param columns the list of columns
...@@ -101,7 +155,7 @@ public class MultiDimension { ...@@ -101,7 +155,7 @@ public class MultiDimension {
* @param scalarColumn the column name of the computed scalar column * @param scalarColumn the column name of the computed scalar column
* @return the query * @return the query
*/ */
public String getMultiDimensionalQuery(String table, String scalarColumn, String[] columns, int[] min, int[] max) { public String generateQuery(String table, String scalarColumn, String[] columns, int[] min, int[] max) {
long[][] ranges = getMortonRanges(min, max); long[][] ranges = getMortonRanges(min, max);
StringBuffer buff = new StringBuffer("SELECT * FROM ("); StringBuffer buff = new StringBuffer("SELECT * FROM (");
for(int i=0; i<ranges.length; i++) { for(int i=0; i<ranges.length; i++) {
......
...@@ -367,6 +367,9 @@ public class Recover implements DataHandler { ...@@ -367,6 +367,9 @@ public class Recover implements DataHandler {
} }
sb.append(v.getSQL()); sb.append(v.getSQL());
} catch(Exception e) { } catch(Exception e) {
if(log) {
logError("log data", e);
}
writeDataError(writer, "exception " + e, s.getBytes(), blockCount); writeDataError(writer, "exception " + e, s.getBytes(), blockCount);
continue; continue;
} catch(OutOfMemoryError e) { } catch(OutOfMemoryError e) {
...@@ -813,7 +816,7 @@ public class Recover implements DataHandler { ...@@ -813,7 +816,7 @@ public class Recover implements DataHandler {
* INTERNAL * INTERNAL
*/ */
public FileStore openFile(String name, String mode, boolean mustExist) throws SQLException { public FileStore openFile(String name, String mode, boolean mustExist) throws SQLException {
return null; return FileStore.open(this, name, "rw", Constants.MAGIC_FILE_HEADER.getBytes());
} }
/** /**
......
...@@ -130,7 +130,7 @@ public class DateTimeUtils { ...@@ -130,7 +130,7 @@ public class DateTimeUtils {
int s1 = s.indexOf('-', 1); int s1 = s.indexOf('-', 1);
int s2 = s.indexOf('-', s1 + 1); int s2 = s.indexOf('-', s1 + 1);
if(s1 <= 0 || s2 <= s1) { if(s1 <= 0 || s2 <= s1) {
throw Message.getSQLException(errorCode, s); throw Message.getSQLException(errorCode, new String[]{s, "format yyyy-mm-dd"});
} }
year = Integer.parseInt(s.substring(0, s1)); year = Integer.parseInt(s.substring(0, s1));
month = Integer.parseInt(s.substring(s1 + 1, s2)); month = Integer.parseInt(s.substring(s1 + 1, s2));
...@@ -143,7 +143,7 @@ public class DateTimeUtils { ...@@ -143,7 +143,7 @@ public class DateTimeUtils {
int s2 = s.indexOf(':', s1 + 1); int s2 = s.indexOf(':', s1 + 1);
int s3 = s.indexOf('.', s2 + 1); int s3 = s.indexOf('.', s2 + 1);
if(s1 <= 0 || s2 <= s1) { if(s1 <= 0 || s2 <= s1) {
throw Message.getSQLException(errorCode, s); throw Message.getSQLException(errorCode, new String[]{s, "format hh:mm:ss"});
} }
if(s.endsWith("Z")) { if(s.endsWith("Z")) {
...@@ -158,7 +158,7 @@ public class DateTimeUtils { ...@@ -158,7 +158,7 @@ public class DateTimeUtils {
String tzName = "GMT" + s.substring(timezoneStart); String tzName = "GMT" + s.substring(timezoneStart);
tz = TimeZone.getTimeZone(tzName); tz = TimeZone.getTimeZone(tzName);
if(!tz.getID().equals(tzName)) { if(!tz.getID().equals(tzName)) {
throw Message.getSQLException(errorCode, s + " " + tz.getID() + "/" + tzName); throw Message.getSQLException(errorCode, new String[]{s, tz.getID() + " <>" + tzName});
} }
s = s.substring(0, timezoneStart).trim(); s = s.substring(0, timezoneStart).trim();
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.h2.util; package org.h2.util;
public class MemoryFile { public class MemoryFile {
private int supportCompression;
private String name; private String name;
private int length; private int length;
private byte[] data; private byte[] data;
......
...@@ -100,29 +100,11 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2 ...@@ -100,29 +100,11 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
/* /*
problem with news mailing list shrink newsletter list (migrate to google groups)
CREATE TABLE TEST(BIRTH TIMESTAMP); see if maven repository is ok, document
INSERT INTO TEST VALUES('2006-04-03 10:20:30'), ('2006-04-03 10:20:31');
SELECT CAST(BIRTH AS DATE) B
FROM TEST GROUP BY CAST(BIRTH AS DATE)
HAVING CAST(BIRTH AS DATE) = '2004-05-05';
SELECT 1 FROM (SELECT CAST(BIRTH AS DATE) B
FROM TEST GROUP BY CAST(BIRTH AS DATE)) A
WHERE A.B = '2004-05-05';
DROP TABLE TEST;
CREATE TABLE TEST (ID integer NOT NULL PRIMARY KEY);
@LOOP 1000 INSERT INTO TEST VALUES(?);
CREATE VIEW TESTVIEW AS SELECT src.ID as VID FROM TEST AS h
INNER JOIN TEST AS src ON h.ID = src.ID GROUP BY src.ID;
-- slow
SELECT COUNT(*) FROM TESTVIEW AS S LEFT JOIN TESTVIEW AS T ON S.VID = T.VID;
DROP VIEW TESTVIEW;
DROP TABLE TEST;
add to maven
http://maven.apache.org/guides/mini/guide-central-repository-upload.html http://maven.apache.org/guides/mini/guide-central-repository-upload.html
http://mirrors.ibiblio.org/pub/mirrors/maven2/com/h2database/h2/1.0.57/
add MVCC add MVCC
...@@ -130,14 +112,10 @@ don't create @~ of not translated ...@@ -130,14 +112,10 @@ don't create @~ of not translated
improve documentation of 'mixed mode' usage. improve documentation of 'mixed mode' usage.
test and document fulltext search test performance and document fulltext search
clustered tables: test, document clustered tables: test, document
Switching off and switching on constraints could be made transactional.
Add version number. Install directory: h2-1.0, jar file: h2-1.0.jar
search for japanese: works, but is it ok? search for japanese: works, but is it ok?
pdf: first page looks empty (because the top part is empty) - title on the top? pdf: first page looks empty (because the top part is empty) - title on the top?
pdf / openoffice: pictures don't work? pdf / openoffice: pictures don't work?
......
db1 = H2 (MVCC), org.h2.Driver, jdbc:h2:data/test_mvcc;MVCC=TRUE, sa, sa db1 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
db2 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa #db1 = H2 (MVCC), org.h2.Driver, jdbc:h2:data/test_mvcc;MVCC=TRUE, sa, sa
#xdb2 = H2 (XTEA), org.h2.Driver, jdbc:h2:data/test_xtea;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=XTEA, sa, sa 123 #xdb2 = H2 (XTEA), org.h2.Driver, jdbc:h2:data/test_xtea;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=XTEA, sa, sa 123
#xdb3 = H2 (AES), org.h2.Driver, jdbc:h2:data/test_aes;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=AES, sa, sa 123 #xdb3 = H2 (AES), org.h2.Driver, jdbc:h2:data/test_aes;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=AES, sa, sa 123
#xdb4 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3;write_mode_log=rws;write_delay=0, sa, sa #xdb4 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3;write_mode_log=rws;write_delay=0, sa, sa
#xdb5 = H2_PG, org.postgresql.Driver, jdbc:postgresql://localhost:5435/h2test, sa, sa #xdb5 = H2_PG, org.postgresql.Driver, jdbc:postgresql://localhost:5435/h2test, sa, sa
#db2 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:data/test;hsqldb.default_table_type=cached;sql.enforce_size=true, sa db2 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:data/test;hsqldb.default_table_type=cached;sql.enforce_size=true, sa
db3 = Derby, org.apache.derby.jdbc.EmbeddedDriver, jdbc:derby:data/test;create=true, sa, sa db3 = Derby, org.apache.derby.jdbc.EmbeddedDriver, jdbc:derby:data/test;create=true, sa, sa
db4 = H2, org.h2.Driver, jdbc:h2:tcp://localhost/data/testServer;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa db4 = H2, org.h2.Driver, jdbc:h2:tcp://localhost/data/testServer;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
......
...@@ -38,6 +38,7 @@ public class TestBackup extends TestBase { ...@@ -38,6 +38,7 @@ public class TestBackup extends TestBase {
stat1.execute("backup to '" + BASE_DIR + "/backup.zip'"); stat1.execute("backup to '" + BASE_DIR + "/backup.zip'");
conn2.rollback(); conn2.rollback();
compareDatabases(stat1, stat2);
Restore.execute(BASE_DIR + "/backup.zip", BASE_DIR, "restored", true); Restore.execute(BASE_DIR + "/backup.zip", BASE_DIR, "restored", true);
conn3 = getConnection("restored"); conn3 = getConnection("restored");
......
...@@ -12,11 +12,32 @@ import org.h2.test.TestBase; ...@@ -12,11 +12,32 @@ import org.h2.test.TestBase;
public class TestLinkedTable extends TestBase { public class TestLinkedTable extends TestBase {
public void test() throws Exception { public void test() throws Exception {
testLinkDrop();
testLinkSchema(); testLinkSchema();
testLinkEmitUpdates(); testLinkEmitUpdates();
testLinkTable(); testLinkTable();
} }
private void testLinkDrop() throws Exception {
Class.forName("org.h2.Driver");
Connection connA = DriverManager.getConnection("jdbc:h2:mem:a");
Statement statA = connA.createStatement();
statA.execute("CREATE TABLE TEST(ID INT)");
Connection connB = DriverManager.getConnection("jdbc:h2:mem:b");
Statement statB = connB.createStatement();
statB.execute("CREATE LINKED TABLE TEST_LINK('', 'jdbc:h2:mem:a', '', '', 'TEST')");
connA.close();
// the connection should be closed now
// (and the table should disappear because the last connection was closed)
statB.execute("DROP TABLE TEST_LINK");
connA = DriverManager.getConnection("jdbc:h2:mem:a");
statA = connA.createStatement();
// table should not exist now
statA.execute("CREATE TABLE TEST(ID INT)");
connA.close();
connB.close();
}
private void testLinkEmitUpdates() throws Exception { private void testLinkEmitUpdates() throws Exception {
deleteDb("linked1"); deleteDb("linked1");
deleteDb("linked2"); deleteDb("linked2");
......
...@@ -35,9 +35,8 @@ public class TestMultiDimension extends TestBase { ...@@ -35,9 +35,8 @@ public class TestMultiDimension extends TestBase {
stat.execute("CREATE INDEX IDX_X ON TEST(X, Y, Z)"); stat.execute("CREATE INDEX IDX_X ON TEST(X, Y, Z)");
stat.execute("CREATE INDEX IDX_XYZ ON TEST(XYZ)"); stat.execute("CREATE INDEX IDX_XYZ ON TEST(XYZ)");
PreparedStatement prep = conn.prepareStatement("INSERT INTO TEST(X, Y, Z, DATA) VALUES(?, ?, ?, ?)"); PreparedStatement prep = conn.prepareStatement("INSERT INTO TEST(X, Y, Z, DATA) VALUES(?, ?, ?, ?)");
// a reasonable max value to see the performance difference is 60; the higher the bigger the difference
// a reasonable value to see the performance difference is 60 int max = getSize(10, 20);
int max = 10;
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
for(int x=0; x<max; x++) { for(int x=0; x<max; x++) {
for(int y=0; y<max; y++) { for(int y=0; y<max; y++) {
...@@ -59,28 +58,63 @@ public class TestMultiDimension extends TestBase { ...@@ -59,28 +58,63 @@ public class TestMultiDimension extends TestBase {
} }
} }
} }
stat.execute("ANALYZE SAMPLE_SIZE 10000");
PreparedStatement prepRegular = conn.prepareStatement(
"SELECT * FROM TEST WHERE X BETWEEN ? AND ? " +
"AND Y BETWEEN ? AND ? AND Z BETWEEN ? AND ? ORDER BY X, Y, Z");
MultiDimension multi = MultiDimension.getInstance();
String sql = multi.generatePreparedQuery("TEST", "XYZ", new String[]{"X", "Y", "Z"});
sql += " ORDER BY X, Y, Z";
PreparedStatement prepMulti = conn.prepareStatement(sql);
long timeMulti = 0, timeRegular = 0;
int timeMax = getSize(100, 2000);
for(int i=0; timeMulti < timeMax; i++) {
int size = rand.nextInt(max / 10);
int minX = rand.nextInt(max-size);
int minY = rand.nextInt(max-size);
int minZ = rand.nextInt(max-size);
int maxX = minX+size, maxY = minY+size, maxZ = minZ+size;
time = System.currentTimeMillis();
ResultSet rs1 = multi.getResult(prepMulti,
new int[]{minX, minY, minZ},
new int[]{maxX, maxY, maxZ});
timeMulti += System.currentTimeMillis() - time;
time = System.currentTimeMillis();
prepRegular.setInt(1, minX);
prepRegular.setInt(2, maxX);
prepRegular.setInt(3, minY);
prepRegular.setInt(4, maxY);
prepRegular.setInt(5, minZ);
prepRegular.setInt(6, maxZ);
ResultSet rs2 = prepRegular.executeQuery();
timeRegular += System.currentTimeMillis() - time;
while(rs1.next()) {
check(rs2.next());
check(rs1.getInt(1), rs2.getInt(1));
check(rs1.getInt(2), rs2.getInt(2));
}
checkFalse(rs2.next());
}
trace("multi: " + timeMulti + " regular: " + timeRegular);
for(int i=0; i<50; i++) { for(int i=0; i<50; i++) {
int size = rand.nextInt(max / 10); int size = rand.nextInt(max / 10);
int minX = rand.nextInt(max-size); int minX = rand.nextInt(max-size);
int minY = rand.nextInt(max-size); int minY = rand.nextInt(max-size);
int minZ = rand.nextInt(max-size); int minZ = rand.nextInt(max-size);
int maxX = minX+size, maxY = minY+size, maxZ = minZ+size; int maxX = minX+size, maxY = minY+size, maxZ = minZ+size;
long time1 = System.currentTimeMillis(); long time1 = System.currentTimeMillis();
String query1 = MultiDimension.getInstance().getMultiDimensionalQuery( String query1 = MultiDimension.getInstance().generateQuery(
"TEST", "XYZ", "TEST", "XYZ",
new String[]{"X", "Y", "Z"}, new String[]{"X", "Y", "Z"},
new int[]{minX, minY, minZ}, new int[]{minX, minY, minZ},
new int[]{minX+size, minY+size, minZ+size}); new int[]{minX+size, minY+size, minZ+size});
ResultSet rs1 = conn.createStatement().executeQuery(query1 + " ORDER BY X, Y, Z"); ResultSet rs1 = conn.createStatement().executeQuery(query1 + " ORDER BY X, Y, Z");
time1 = System.currentTimeMillis() - time1; time1 = System.currentTimeMillis() - time1;
long time2 = System.currentTimeMillis(); long time2 = System.currentTimeMillis();
String query2 = "SELECT * FROM TEST WHERE " String query2 = "SELECT * FROM TEST WHERE "
+"X BETWEEN " + minX + " AND " + maxX + " AND " +"X BETWEEN " + minX + " AND " + maxX + " AND "
+"Y BETWEEN " + minY + " AND " + maxY + " AND " +"Y BETWEEN " + minY + " AND " + maxY + " AND "
+"Z BETWEEN " + minZ + " AND " + maxZ; +"Z BETWEEN " + minZ + " AND " + maxZ;
PreparedStatement prep2 = conn.prepareStatement(query2 + " ORDER BY X, Y, Z"); PreparedStatement prep2 = conn.prepareStatement(query2 + " ORDER BY X, Y, Z");
ResultSet rs2 = prep2.executeQuery(); ResultSet rs2 = prep2.executeQuery();
time2 = System.currentTimeMillis() - time2; time2 = System.currentTimeMillis() - time2;
...@@ -90,7 +124,8 @@ public class TestMultiDimension extends TestBase { ...@@ -90,7 +124,8 @@ public class TestMultiDimension extends TestBase {
check(rs1.getInt(2), rs2.getInt(2)); check(rs1.getInt(2), rs2.getInt(2));
} }
checkFalse(rs2.next()); checkFalse(rs2.next());
trace("t1="+time1+" t2="+time2+" size="+size); // it just has to work, no need to compare the performance
// trace("t1="+time1+" t2="+time2+" size="+size);
} }
conn.close(); conn.close();
} }
......
...@@ -43,6 +43,16 @@ public class TestMVCC { ...@@ -43,6 +43,16 @@ public class TestMVCC {
c1.setAutoCommit(false); c1.setAutoCommit(false);
c2.setAutoCommit(false); c2.setAutoCommit(false);
s1.execute("create table test(id int primary key, name varchar(255))");
s2.execute("insert into test values(4, 'Hello')");
c2.rollback();
test(s1, "select count(*) from test where name = 'Hello'", "0");
test(s2, "select count(*) from test where name = 'Hello'", "0");
c1.commit();
c2.commit();
s1.execute("DROP TABLE TEST");
s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255))"); s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255))");
s1.execute("INSERT INTO TEST VALUES(1, 'Test')"); s1.execute("INSERT INTO TEST VALUES(1, 'Test')");
c1.commit(); c1.commit();
......
--- special grammar and test cases --------------------------------------------------------------------------------------------- --- special grammar and test cases ---------------------------------------------------------------------------------------------
create alias "SYSDATE" for "java.lang.Integer.parseInt(java.lang.String)";
> exception
create alias "MIN" for "java.lang.Integer.parseInt(java.lang.String)";
> exception
create alias "CAST" for "java.lang.Integer.parseInt(java.lang.String)";
> exception
CREATE TABLE PARENT(A INT, B INT, PRIMARY KEY(A, B));
> ok
CREATE TABLE CHILD(A INT, B INT, CONSTRAINT CP FOREIGN KEY(A, B) REFERENCES PARENT(A, B));
> ok
INSERT INTO PARENT VALUES(1, 2);
> update count: 1
INSERT INTO CHILD VALUES(2, NULL), (NULL, 3), (NULL, NULL), (1, 2);
> update count: 4
set autocommit false;
> ok
ALTER TABLE CHILD SET REFERENTIAL_INTEGRITY FALSE;
> ok
ALTER TABLE CHILD SET REFERENTIAL_INTEGRITY TRUE CHECK;
> ok
set autocommit true;
> ok
DROP TABLE CHILD, PARENT;
> ok
CREATE TABLE TEST(BIRTH TIMESTAMP);
> ok
INSERT INTO TEST VALUES('2006-04-03 10:20:30'), ('2006-04-03 10:20:31'), ('2006-05-05 00:00:00'), ('2006-07-03 22:30:00'), ('2006-07-03 22:31:00');
> update count: 5
SELECT * FROM (SELECT CAST(BIRTH AS DATE) B
FROM TEST GROUP BY CAST(BIRTH AS DATE)) A
WHERE A.B >= '2006-05-05';
> B
> ----------
> 2006-05-05
> 2006-07-03
> rows: 2
DROP TABLE TEST;
> ok
CREATE TABLE Parent(ID INT PRIMARY KEY, Name VARCHAR); CREATE TABLE Parent(ID INT PRIMARY KEY, Name VARCHAR);
> ok > ok
...@@ -35,6 +89,9 @@ ALTER TABLE A ADD CONSTRAINT AC FOREIGN KEY(SK) REFERENCES A(ID); ...@@ -35,6 +89,9 @@ ALTER TABLE A ADD CONSTRAINT AC FOREIGN KEY(SK) REFERENCES A(ID);
INSERT INTO A VALUES(1, 1); INSERT INTO A VALUES(1, 1);
> update count: 1 > update count: 1
INSERT INTO A VALUES(-2, NULL);
> update count: 1
ALTER TABLE A SET REFERENTIAL_INTEGRITY FALSE; ALTER TABLE A SET REFERENTIAL_INTEGRITY FALSE;
> ok > ok
...@@ -3925,8 +3982,8 @@ SELECT MY_SQRT(-1.0) MS, SQRT(NULL) S; ...@@ -3925,8 +3982,8 @@ SELECT MY_SQRT(-1.0) MS, SQRT(NULL) S;
SCRIPT NOPASSWORDS NOSETTINGS; SCRIPT NOPASSWORDS NOSETTINGS;
> SCRIPT > SCRIPT
> ---------------------------------------------- > ----------------------------------------------------
> CREATE ALIAS MY_SQRT FOR "java.lang.Math.sqrt" > CREATE FORCE ALIAS MY_SQRT FOR "java.lang.Math.sqrt"
> CREATE USER IF NOT EXISTS SA PASSWORD '' ADMIN > CREATE USER IF NOT EXISTS SA PASSWORD '' ADMIN
> rows: 2 > rows: 2
......
...@@ -8,8 +8,8 @@ select count(scriptSimple.public.test.id) from scriptSimple.public.test; ...@@ -8,8 +8,8 @@ select count(scriptSimple.public.test.id) from scriptSimple.public.test;
update scriptSimple.public.test set scriptSimple.public.test.id=1; update scriptSimple.public.test set scriptSimple.public.test.id=1;
drop table scriptSimple.public.test; drop table scriptSimple.public.test;
select timestamp '2007-07-26 18:44:26.109000 +02:00'; select year(timestamp '2007-07-26 18:44:26.109000 +02:00');
> 2007-07-26 18:44:26.109; > 2007;
create table test(id int primary key); create table test(id int primary key);
begin; begin;
......
...@@ -9,8 +9,6 @@ import java.io.File; ...@@ -9,8 +9,6 @@ import java.io.File;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.store.FileLock; import org.h2.store.FileLock;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.FileUtils;
/** /**
* @author Thomas * @author Thomas
...@@ -28,12 +26,17 @@ public class TestFileLock extends TestBase implements Runnable { ...@@ -28,12 +26,17 @@ public class TestFileLock extends TestBase implements Runnable {
public TestFileLock() {} public TestFileLock() {}
public void test() throws Exception { public void test() throws Exception {
new File(FILE).delete(); test(false);
test(true);
}
void test(boolean allowSockets) throws Exception {
int threadCount = getSize(3, 5); int threadCount = getSize(3, 5);
wait = getSize(20, 200); wait = getSize(20, 200);
Thread[] threads = new Thread[threadCount]; Thread[] threads = new Thread[threadCount];
new File(FILE).delete();
for (int i = 0; i < threadCount; i++) { for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(new TestFileLock(this, false)); threads[i] = new Thread(new TestFileLock(this, allowSockets));
threads[i].start(); threads[i].start();
Thread.sleep(wait + (int) (Math.random() * wait)); Thread.sleep(wait + (int) (Math.random() * wait));
} }
...@@ -45,21 +48,6 @@ public class TestFileLock extends TestBase implements Runnable { ...@@ -45,21 +48,6 @@ public class TestFileLock extends TestBase implements Runnable {
threads[i].join(); threads[i].join();
} }
check(locks, 0); check(locks, 0);
FileUtils.delete(FILE);
stop = false;
for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(new TestFileLock(this, true));
threads[i].start();
Thread.sleep(wait + (int) (Math.random() * wait));
}
trace("wait");
Thread.sleep(100);
stop = true;
trace("STOP sockets");
for (int i = 0; i < threadCount; i++) {
threads[i].join();
}
check(locks, 0);
} }
TestBase base; TestBase base;
......
...@@ -57,7 +57,7 @@ public class LinkChecker { ...@@ -57,7 +57,7 @@ public class LinkChecker {
ArrayList errors = new ArrayList(); ArrayList errors = new ArrayList();
for(Iterator it = links.keySet().iterator(); it.hasNext(); ) { for(Iterator it = links.keySet().iterator(); it.hasNext(); ) {
String link = (String) it.next(); String link = (String) it.next();
if(!link.startsWith("http") && !link.endsWith("h2.pdf")) { if(!link.startsWith("http") && !link.endsWith("h2.pdf") && link.indexOf("_ja.") < 0) {
if(targets.get(link) == null) { if(targets.get(link) == null) {
errors.add(links.get(link) + ": missing link " + link); errors.add(links.get(link) + ": missing link " + link);
} }
......
...@@ -504,4 +504,4 @@ finalizer textbase newsfeeds quicksort ...@@ -504,4 +504,4 @@ finalizer textbase newsfeeds quicksort
prio zvikico incrementally nocheck differently eng admins problog nio though typepad channels rolling prio zvikico incrementally nocheck differently eng admins problog nio though typepad channels rolling
lightweight builder lightweight builder
tunes elephant codewave incorrectly mytunesrss speeds cte honoured httpdocs department whereever dog dept edh oops flower music appends research plant tunes elephant codewave incorrectly mytunesrss speeds cte honoured httpdocs department whereever dog dept edh oops flower music appends research plant
testview gaps birth vid testview gaps birth vid weblog blojsom unsubscribe
\ No newline at end of file \ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论