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

--no commit message

--no commit message
上级 da52f772
......@@ -12,7 +12,7 @@
</path>
<target name="all" depends="jar,javadoc,docs">
<delete includeemptydirs="true" verbose="true">
<delete includeemptydirs="true">
<fileset dir="bin" includes="**/*.txt"/>
<fileset dir="bin" includes="h2-test.exe"/>
<fileset dir="bin" includes="org/**/*"/>
......@@ -119,7 +119,7 @@
<target name="compileTest" unless="java.version.ok">
<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 name="createPatch" depends="clean">
......
......@@ -13,31 +13,32 @@ H2 Database Engine
</head><body onload="frameMe();">
<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>
<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-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 />
</p>
<h3> Version 1.0 / 2007-03-04 (Last Stable) </h3>
<h3>Version 1.0 / 2007-03-04 (Last Stable)</h3>
<p>
<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-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 />
</p>
<h3> Download Mirror </h3>
<h3>Download Mirror</h3>
<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>
<h3> Subversion Source Repository </h3>
<h3>Subversion Source Repository</h3>
<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>
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>
\ No newline at end of file
......@@ -44,7 +44,6 @@ Here is the list of known and confirmed issues as of
<ul>
<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.
</li><li>Using a CAST in a GROUP BY expression that is used in a view does not always work.
</li></ul>
<br /><a name="open_source"></a>
......
......@@ -241,6 +241,9 @@ It looks like the development of this database has stopped. The last release was
</tr><tr>
<td><a href="http://appfuse.org">AppFuse</a></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>
<td><a href="http://bmarks-portlet.sourceforge.net">Bookmarks Portlet</a></td>
<td>JSR168 compliant bookmarks management portlet application.</td>
......
......@@ -39,7 +39,22 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<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).
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.
......@@ -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>Document, implement tool for long running transactions using user defined compensation statements
</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>Setting for MAX_QUERY_TIME (default no limit?)
</li><li>GCJ: what is the state now?
......@@ -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>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>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 linked tables to the current database
</li><li>Support dynamic linked schema (automatically adding/updating/removing tables)
......@@ -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 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>Support indexes for views (probably requires materialized views)
</li><li>Linked tables that point to the same database should share the connection
</li></ul>
<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 @@
90118=Cannot drop table {0}
90119=User data type {0} already exists
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}
90123=Cannot mix indexed and non-indexed parameters
90124=File not found\: {0}
......
......@@ -1932,7 +1932,9 @@ public class Parser {
String name = currentToken;
if(currentTokenQuoted) {
read();
if(readIf(".")) {
if (readIf("(")) {
r = readFunction(name);
} else if(readIf(".")) {
r = readTermObjectDot(name);
} else {
r = new ExpressionColumn(database, currentSelect, null, null, name);
......@@ -3064,7 +3066,7 @@ public class Parser {
} else if(readIf("VIEW")) {
return parseCreateView(force);
} else if (readIf("ALIAS")) {
return parseCreateFunctionAlias();
return parseCreateFunctionAlias(force);
} else if (readIf("SEQUENCE")) {
return parseCreateSequence();
} else if (readIf("USER")) {
......@@ -3243,6 +3245,9 @@ public class Parser {
boolean ifNotExists = readIfNoExists();
String constantName = readIdentifierWithSchema();
Schema schema = getSchema();
if(isKeyword(constantName)) {
throw Message.getSQLException(ErrorCode.CONSTANT_ALREADY_EXISTS_1, constantName);
}
read("VALUE");
Expression expr = readExpression();
CreateConstant command = new CreateConstant(session, schema);
......@@ -3340,10 +3345,15 @@ public class Parser {
return command;
}
private CreateFunctionAlias parseCreateFunctionAlias() throws SQLException {
private CreateFunctionAlias parseCreateFunctionAlias(boolean force) throws SQLException {
boolean ifNotExists = readIfNoExists();
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);
read("FOR");
command.setJavaClassMethod(readUniqueIdentifier());
......
......@@ -17,7 +17,8 @@ public class CreateFunctionAlias extends DefineCommand {
private String aliasName;
private String javaClassMethod;
private boolean ifNotExists;
private boolean force;
public CreateFunctionAlias(Session session) {
super(session);
}
......@@ -32,7 +33,7 @@ public class CreateFunctionAlias extends DefineCommand {
}
} else {
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);
}
return 0;
......@@ -50,4 +51,8 @@ public class CreateFunctionAlias extends DefineCommand {
this.ifNotExists = ifNotExists;
}
public void setForce(boolean force) {
this.force = force;
}
}
......@@ -594,7 +594,7 @@ public class Select extends Query {
}
}
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
Expression h = having;
buff.append("\nHAVING " + StringUtils.unEnclose(h.getSQL()));
......@@ -685,16 +685,26 @@ public class Select extends Query {
col = col.getNonAliasExpression();
Expression comp = new Comparison(session, comparisonType, col, expr);
comp = comp.optimize(session);
boolean addToCondition = true;
if(isGroupQuery) {
if(havingIndex >= 0) {
having = (Expression) expressions.get(havingIndex);
for(int i=0; groupIndex != null && i<groupIndex.length; i++) {
if(groupIndex[i] == columnId) {
addToCondition = true;
break;
}
}
if(having == null) {
having = comp;
} else {
having = new ConditionAndOr(ConditionAndOr.AND, having, comp);
if(!addToCondition) {
if(havingIndex >= 0) {
having = (Expression) expressions.get(havingIndex);
}
if(having == null) {
having = comp;
} else {
having = new ConditionAndOr(ConditionAndOr.AND, having, comp);
}
}
} else {
}
if(addToCondition) {
if(condition == null) {
condition = comp;
} else {
......
......@@ -547,6 +547,14 @@ public class ConstraintReferential extends Constraint {
}
buff.append(" FROM ");
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 ");
for(int i=0; i<columns.length; i++) {
if(i>0) {
......
......@@ -57,7 +57,8 @@ import org.h2.constant.SysProperties;
* - Test with hibernate
* - Scan for viruses
* - 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
* - Add to freshmeat, http://code.google.com/p/h2database/downloads/list
*
......
......@@ -26,9 +26,9 @@ public class FunctionAlias extends DbObjectBase {
private String methodName;
private Method javaMethod;
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);
int paren = javaClassMethod.indexOf('(');
int lastDot = javaClassMethod.lastIndexOf('.', paren < 0 ? javaClassMethod.length() : paren);
......@@ -37,6 +37,15 @@ public class FunctionAlias extends DbObjectBase {
}
className = javaClassMethod.substring(0, lastDot);
methodName = javaClassMethod.substring(lastDot + 1);
if(!force) {
load();
}
}
private synchronized void load() throws SQLException {
if(javaMethod != null) {
return;
}
Class javaClass;
try {
javaClass = database.loadClass(className);
......@@ -94,7 +103,8 @@ public class FunctionAlias extends DbObjectBase {
return buff.toString();
}
public Class[] getColumnClasses() {
public Class[] getColumnClasses() throws SQLException {
load();
return javaMethod.getParameterTypes();
}
......@@ -112,7 +122,7 @@ public class FunctionAlias extends DbObjectBase {
public String getCreateSQL() {
StringBuffer buff = new StringBuffer();
buff.append("CREATE ALIAS ");
buff.append("CREATE FORCE ALIAS ");
buff.append(getSQL());
buff.append(" FOR ");
buff.append(Parser.quoteIdentifier(className + "." + methodName));
......@@ -137,59 +147,59 @@ public class FunctionAlias extends DbObjectBase {
return getValue(session, args, false);
}
public Value getValue(Session session, Expression[] args, boolean columnList) throws SQLException {
synchronized(this) {
Class[] paramClasses = javaMethod.getParameterTypes();
Object[] params = new Object[paramClasses.length];
int p = 0;
if(hasConnectionParam && params.length > 0) {
params[p++] = session.createConnection(columnList);
}
for(int a=0; a<args.length && p<params.length; a++, p++) {
Class paramClass = paramClasses[p];
int type = DataType.getTypeFromClass(paramClass);
Value v = args[a].getValue(session);
v = v.convertTo(type);
Object o = v.getObject();
if(o == null) {
if(paramClass.isPrimitive()) {
if(columnList) {
// 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
o = DataType.getDefaultForPrimitiveType(paramClass);
} else {
// NULL for a java primitive: return NULL
return ValueNull.INSTANCE;
}
}
} else {
if(!paramClass.isAssignableFrom(o.getClass()) && !paramClass.isPrimitive()) {
o = DataType.convertTo(session, session.createConnection(false), v, paramClass);
public synchronized Value getValue(Session session, Expression[] args, boolean columnList) throws SQLException {
load();
Class[] paramClasses = javaMethod.getParameterTypes();
Object[] params = new Object[paramClasses.length];
int p = 0;
if(hasConnectionParam && params.length > 0) {
params[p++] = session.createConnection(columnList);
}
for(int a=0; a<args.length && p<params.length; a++, p++) {
Class paramClass = paramClasses[p];
int type = DataType.getTypeFromClass(paramClass);
Value v = args[a].getValue(session);
v = v.convertTo(type);
Object o = v.getObject();
if(o == null) {
if(paramClass.isPrimitive()) {
if(columnList) {
// 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
o = DataType.getDefaultForPrimitiveType(paramClass);
} else {
// NULL for a java primitive: return NULL
return ValueNull.INSTANCE;
}
}
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 {
session.setAutoCommit(false);
try {
Object returnValue;
returnValue = javaMethod.invoke(null, params);
if(returnValue == null) {
return ValueNull.INSTANCE;
}
Value ret = DataType.convertToValue(session, returnValue, dataType);
return ret.convertTo(dataType);
} catch (Exception e) {
throw Message.convert(e);
Object returnValue;
returnValue = javaMethod.invoke(null, params);
if(returnValue == null) {
return ValueNull.INSTANCE;
}
} finally {
session.setAutoCommit(old);
Value ret = DataType.convertToValue(session, returnValue, dataType);
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;
}
......
......@@ -13,7 +13,7 @@ import org.h2.value.ValueResultSet;
public interface FunctionCall {
String getName();
int getParameterCount();
int getParameterCount() throws SQLException;
ValueResultSet getValueForColumnList(Session session, Expression[] nullArgs) throws SQLException;
int getType();
Expression optimize(Session session) throws SQLException;
......
......@@ -96,7 +96,7 @@ public class JavaFunction extends Expression implements FunctionCall {
return functionAlias.getName();
}
public int getParameterCount() {
public int getParameterCount() throws SQLException {
return functionAlias.getParameterCount();
}
......
......@@ -36,11 +36,8 @@ public class FunctionIndex extends BaseIndex {
}
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
if(result == null) {
result = functionTable.getResult(session);
} else {
result.reset();
}
// TODO sometimes result.reset() would be enough (but not when parameters are used)
result = functionTable.getResult(session);
return new FunctionCursor(result);
}
......
......@@ -102,10 +102,6 @@ public class MultiVersionCursor implements Cursor {
}
}
int sessionId = deltaRow.getSessionId();
if(sessionId == 0) {
int testing;
System.out.println("sessionId==0");
}
boolean isThisSession = sessionId == session.getId();
boolean isDeleted = deltaRow.getDeleted();
if(isThisSession && isDeleted) {
......
......@@ -92,14 +92,6 @@ public class MultiVersionIndex implements Index {
if(removeIfExists(session, row)) {
// added and deleted in the same transaction: no change
} 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);
}
}
......
......@@ -19,14 +19,14 @@ public class ScanCursor implements Cursor {
private Row row;
private final Session session;
private final boolean multiVersion;
private Iterator deleted;
private Iterator delta;
ScanCursor(Session session, ScanIndex scan, boolean multiVersion) {
this.session = session;
this.scan = scan;
this.multiVersion = multiVersion;
if(multiVersion) {
deleted = scan.getDeleted();
delta = scan.getDelta();
}
row = null;
}
......@@ -50,9 +50,9 @@ public class ScanCursor implements Cursor {
public boolean next() throws SQLException {
if(multiVersion) {
while(true) {
if(deleted.hasNext()) {
row = (Row) deleted.next();
if(row.getDeleted() && row.getSessionId() == session.getId()) {
if(delta.hasNext()) {
row = (Row) delta.next();
if(!row.getDeleted() || row.getSessionId() == session.getId()) {
row = null;
continue;
}
......
......@@ -35,7 +35,7 @@ public class ScanIndex extends BaseIndex {
private boolean containsLargeObject;
private int rowCountDiff;
private HashMap sessionRowCount;
private HashSet deleted;
private HashSet delta;
public ScanIndex(TableData table, int id, Column[] columns, IndexType indexType)
throws SQLException {
......@@ -127,8 +127,12 @@ public class ScanIndex extends BaseIndex {
}
}
if(database.isMultiVersion()) {
if(deleted != null) {
deleted.remove(row);
if(delta == null) {
delta = new HashSet();
}
boolean wasDeleted = delta.remove(row);
if(!wasDeleted) {
delta.add(row);
}
incrementRowCount(session.getId(), 1);
}
......@@ -137,8 +141,8 @@ public class ScanIndex extends BaseIndex {
public void commit(int operation, Row row) throws SQLException {
if(database.isMultiVersion()) {
if(deleted != null && operation == UndoLogRecord.DELETE) {
deleted.remove(row);
if(delta != null && operation == UndoLogRecord.DELETE) {
delta.remove(row);
}
incrementRowCount(row.getSessionId(), operation == UndoLogRecord.DELETE ? 1 : -1);
}
......@@ -173,10 +177,13 @@ public class ScanIndex extends BaseIndex {
firstFree = key;
}
if(database.isMultiVersion()) {
if(deleted == null) {
deleted = new HashSet();
if(delta == null) {
delta = new HashSet();
}
boolean wasAdded = delta.remove(row);
if(!wasAdded) {
delta.add(row);
}
deleted.add(row);
incrementRowCount(session.getId(), -1);
}
rowCount--;
......@@ -252,8 +259,8 @@ public class ScanIndex extends BaseIndex {
throw Message.getUnsupportedException();
}
public Iterator getDeleted() {
return deleted == null ? Collections.EMPTY_LIST.iterator() : deleted.iterator();
public Iterator getDelta() {
return delta == null ? Collections.EMPTY_LIST.iterator() : delta.iterator();
}
}
......@@ -90,15 +90,15 @@ public class PgServerThread implements Runnable {
}
private String readString() throws IOException {
StringBuffer buff = new StringBuffer();
ByteArrayOutputStream buff = new ByteArrayOutputStream();
while(true) {
int x = dataIn.read();
if(x <= 0) {
break;
}
buff.append((char)x);
buff.write(x);
}
return buff.toString();
return new String(buff.toByteArray(), getEncoding());
}
private int readInt() throws IOException {
......@@ -676,7 +676,7 @@ public class PgServerThread implements Runnable {
}
private void writeString(String s) throws IOException {
write(s.getBytes("UTF-8"));
write(s.getBytes(getEncoding()));
write(0);
}
......
......@@ -4,9 +4,13 @@
*/
package org.h2.tools;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import org.h2.util.StringUtils;
/**
* A tool to help an application execute multi-dimensional range queries.
......@@ -89,10 +93,60 @@ public class MultiDimension {
// 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.
* This query is database independent, however the performance is
* not as good as when using generatePreparedQuery
*
* @param table the table name
* @param columns the list of columns
......@@ -101,7 +155,7 @@ public class MultiDimension {
* @param scalarColumn the column name of the computed scalar column
* @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);
StringBuffer buff = new StringBuffer("SELECT * FROM (");
for(int i=0; i<ranges.length; i++) {
......
......@@ -367,6 +367,9 @@ public class Recover implements DataHandler {
}
sb.append(v.getSQL());
} catch(Exception e) {
if(log) {
logError("log data", e);
}
writeDataError(writer, "exception " + e, s.getBytes(), blockCount);
continue;
} catch(OutOfMemoryError e) {
......@@ -813,7 +816,7 @@ public class Recover implements DataHandler {
* INTERNAL
*/
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 {
int s1 = s.indexOf('-', 1);
int s2 = s.indexOf('-', s1 + 1);
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));
month = Integer.parseInt(s.substring(s1 + 1, s2));
......@@ -143,7 +143,7 @@ public class DateTimeUtils {
int s2 = s.indexOf(':', s1 + 1);
int s3 = s.indexOf('.', s2 + 1);
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")) {
......@@ -158,7 +158,7 @@ public class DateTimeUtils {
String tzName = "GMT" + s.substring(timezoneStart);
tz = TimeZone.getTimeZone(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();
}
......
......@@ -5,6 +5,7 @@
package org.h2.util;
public class MemoryFile {
private int supportCompression;
private String name;
private int length;
private byte[] data;
......
......@@ -100,29 +100,11 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
/*
problem with news mailing list
CREATE TABLE TEST(BIRTH TIMESTAMP);
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
shrink newsletter list (migrate to google groups)
see if maven repository is ok, document
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
......@@ -130,14 +112,10 @@ don't create @~ of not translated
improve documentation of 'mixed mode' usage.
test and document fulltext search
test performance and document fulltext search
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?
pdf: first page looks empty (because the top part is empty) - title on the top?
pdf / openoffice: pictures don't work?
......
db1 = H2 (MVCC), org.h2.Driver, jdbc:h2:data/test_mvcc;MVCC=TRUE, sa, sa
db2 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
db1 = 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
#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
#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
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 {
stat1.execute("backup to '" + BASE_DIR + "/backup.zip'");
conn2.rollback();
compareDatabases(stat1, stat2);
Restore.execute(BASE_DIR + "/backup.zip", BASE_DIR, "restored", true);
conn3 = getConnection("restored");
......
......@@ -12,11 +12,32 @@ import org.h2.test.TestBase;
public class TestLinkedTable extends TestBase {
public void test() throws Exception {
testLinkDrop();
testLinkSchema();
testLinkEmitUpdates();
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 {
deleteDb("linked1");
deleteDb("linked2");
......
......@@ -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_XYZ ON TEST(XYZ)");
PreparedStatement prep = conn.prepareStatement("INSERT INTO TEST(X, Y, Z, DATA) VALUES(?, ?, ?, ?)");
// a reasonable value to see the performance difference is 60
int max = 10;
// a reasonable max value to see the performance difference is 60; the higher the bigger the difference
int max = getSize(10, 20);
long time = System.currentTimeMillis();
for(int x=0; x<max; x++) {
for(int y=0; y<max; y++) {
......@@ -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++) {
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;
long time1 = System.currentTimeMillis();
String query1 = MultiDimension.getInstance().getMultiDimensionalQuery(
String query1 = MultiDimension.getInstance().generateQuery(
"TEST", "XYZ",
new String[]{"X", "Y", "Z"},
new int[]{minX, minY, minZ},
new int[]{minX+size, minY+size, minZ+size});
ResultSet rs1 = conn.createStatement().executeQuery(query1 + " ORDER BY X, Y, Z");
time1 = System.currentTimeMillis() - time1;
long time2 = System.currentTimeMillis();
String query2 = "SELECT * FROM TEST WHERE "
+"X BETWEEN " + minX + " AND " + maxX + " AND "
+"Y BETWEEN " + minY + " AND " + maxY + " AND "
+"Z BETWEEN " + minZ + " AND " + maxZ;
PreparedStatement prep2 = conn.prepareStatement(query2 + " ORDER BY X, Y, Z");
ResultSet rs2 = prep2.executeQuery();
time2 = System.currentTimeMillis() - time2;
......@@ -90,7 +124,8 @@ public class TestMultiDimension extends TestBase {
check(rs1.getInt(2), rs2.getInt(2));
}
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();
}
......
......@@ -43,6 +43,16 @@ public class TestMVCC {
c1.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("INSERT INTO TEST VALUES(1, 'Test')");
c1.commit();
......
--- 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);
> ok
......@@ -35,6 +89,9 @@ ALTER TABLE A ADD CONSTRAINT AC FOREIGN KEY(SK) REFERENCES A(ID);
INSERT INTO A VALUES(1, 1);
> update count: 1
INSERT INTO A VALUES(-2, NULL);
> update count: 1
ALTER TABLE A SET REFERENTIAL_INTEGRITY FALSE;
> ok
......@@ -3925,8 +3982,8 @@ SELECT MY_SQRT(-1.0) MS, SQRT(NULL) S;
SCRIPT NOPASSWORDS NOSETTINGS;
> 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
> rows: 2
......
......@@ -8,8 +8,8 @@ select count(scriptSimple.public.test.id) from scriptSimple.public.test;
update scriptSimple.public.test set scriptSimple.public.test.id=1;
drop table scriptSimple.public.test;
select timestamp '2007-07-26 18:44:26.109000 +02:00';
> 2007-07-26 18:44:26.109;
select year(timestamp '2007-07-26 18:44:26.109000 +02:00');
> 2007;
create table test(id int primary key);
begin;
......
......@@ -9,8 +9,6 @@ import java.io.File;
import org.h2.message.TraceSystem;
import org.h2.store.FileLock;
import org.h2.test.TestBase;
import org.h2.util.FileUtils;
/**
* @author Thomas
......@@ -28,12 +26,17 @@ public class TestFileLock extends TestBase implements Runnable {
public TestFileLock() {}
public void test() throws Exception {
new File(FILE).delete();
test(false);
test(true);
}
void test(boolean allowSockets) throws Exception {
int threadCount = getSize(3, 5);
wait = getSize(20, 200);
Thread[] threads = new Thread[threadCount];
new File(FILE).delete();
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();
Thread.sleep(wait + (int) (Math.random() * wait));
}
......@@ -45,21 +48,6 @@ public class TestFileLock extends TestBase implements Runnable {
threads[i].join();
}
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;
......
......@@ -57,7 +57,7 @@ public class LinkChecker {
ArrayList errors = new ArrayList();
for(Iterator it = links.keySet().iterator(); it.hasNext(); ) {
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) {
errors.add(links.get(link) + ": missing link " + link);
}
......
......@@ -504,4 +504,4 @@ finalizer textbase newsfeeds quicksort
prio zvikico incrementally nocheck differently eng admins problog nio though typepad channels rolling
lightweight builder
tunes elephant codewave incorrectly mytunesrss speeds cte honoured httpdocs department whereever dog dept edh oops flower music appends research plant
testview gaps birth vid
\ No newline at end of file
testview gaps birth vid weblog blojsom unsubscribe
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论