提交 180841cb authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 778946d4
#Fri Nov 09 12:31:47 CET 2007
benchmark.drivers.dir=C\:/data/java
#Mon Nov 19 20:09:20 CET 2007
javac=javac
benchmark.drivers.dir=C\:/data/java
path.servlet.jar=C\:/data/classpath/servlet-api.jar
path.lucene.jar=C\:/data/classpath/lucene-core-2.2.0.jar
version.name.maven=1.0.61
path.lucene.jar=C\:/data/classpath/lucene-core-2.2.0.jar
jdk=1.4
......@@ -50,12 +50,21 @@
<javac executable="${javac}" srcdir="src/tools" destdir="bin" debug="true" includes="org/h2/tools/code/CodeSwitch.java"/>
</target>
<target name="codeswitchAndroid" depends="codeswitchPrepare">
<propertyfile file="ant-build.properties">
<entry key="jdk" value="1.3" />
</propertyfile>
<java classname="org.h2.tools.code.CodeSwitch" fork="true" dir="bin">
<arg line="+JDK13 -JDK14 -JDK16 -AWT ../src/main/org/h2"/>
</java>
</target>
<target name="codeswitchJdk13" depends="codeswitchPrepare">
<propertyfile file="ant-build.properties">
<entry key="jdk" value="1.3" />
</propertyfile>
<java classname="org.h2.tools.code.CodeSwitch" fork="true" dir="bin">
<arg line="+JDK13 -JDK14 -JDK16 ../src/main/org/h2"/>
<arg line="+JDK13 -JDK14 -JDK16 +AWT ../src/main/org/h2"/>
</java>
</target>
......@@ -64,7 +73,7 @@
<entry key="jdk" value="1.4" />
</propertyfile>
<java classname="org.h2.tools.code.CodeSwitch" fork="true" dir="bin">
<arg line="-JDK13 +JDK14 -JDK16 ../src/main/org/h2"/>
<arg line="-JDK13 +JDK14 -JDK16 +AWT ../src/main/org/h2"/>
</java>
</target>
......@@ -73,7 +82,7 @@
<entry key="jdk" value="1.6" />
</propertyfile>
<java classname="org.h2.tools.code.CodeSwitch" fork="true" dir="bin">
<arg line="-JDK13 +JDK16 +JDK14 ../src/main/org/h2"/>
<arg line="-JDK13 +JDK14 +JDK16 +AWT ../src/main/org/h2"/>
</java>
</target>
......
......@@ -155,12 +155,8 @@ However, the startup of the GCJ version is faster than when using a VM.
<br /><a name="translate"></a>
<h3>How to Translate this Project?</h3>
<p>
The following files can be translated at the moment:
<pre>
src/main/org/h2/server/web/res/_text_*.properties
src/main/org/h2/res/_messages_*.properties
src/docsrc/text/_docs_*.utf8.txt
</pre>
For more information, see
<a href="build.html#translating">Build/Translating</a>.
</p>
</div></td></tr></table></body></html>
\ No newline at end of file
......@@ -4,6 +4,7 @@
*/
package org.h2.fulltext;
//#ifdef JDK14
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
......@@ -33,26 +34,33 @@ import org.h2.store.fs.FileSystem;
import org.h2.tools.SimpleResultSet;
import org.h2.util.ByteUtils;
import org.h2.util.StringUtils;
//#endif
/**
* This class implements the full text search based on Apache Lucene.
*/
public class FullTextLucene implements Trigger {
public class FullTextLucene
//#ifdef JDK14
implements Trigger
//#endif
{
//#ifdef JDK14
private static HashMap indexers = new HashMap();
private static final String FIELD_DATA = "data";
private static final String FIELD_QUERY = "query";
private static final String FIELD_COLUMN_PREFIX = "_";
private static final String TRIGGER_PREFIX = "FTL_";
private static final String SCHEMA = "FTL";
private IndexModifier indexer;
private String schemaName;
private String tableName;
private int[] keys;
private int[] indexColumns;
private String[] columnNames;
private int[] dataTypes;
private IndexModifier indexer;
//#endif
/**
* Create a new full text index for a table and column list. Each table may only have one index at any time.
*
......@@ -62,6 +70,7 @@ public class FullTextLucene implements Trigger {
* @param table the table name
* @param columnList the column list (null for all columns)
*/
//#ifdef JDK14
public static void createIndex(Connection conn, String schema, String table, String columnList) throws SQLException {
init(conn);
PreparedStatement prep = conn.prepareStatement("INSERT INTO "+SCHEMA+".INDEXES(SCHEMA, TABLE, COLUMNS) VALUES(?, ?, ?)");
......@@ -72,42 +81,14 @@ public class FullTextLucene implements Trigger {
createTrigger(conn, schema, table);
indexExistingRows(conn, schema, table);
}
private static void createTrigger(Connection conn, String schema, String table) throws SQLException {
Statement stat = conn.createStatement();
String trigger = StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(TRIGGER_PREFIX + table);
stat.execute("DROP TRIGGER IF EXISTS " + trigger);
StringBuffer buff = new StringBuffer("CREATE TRIGGER IF NOT EXISTS ");
buff.append(trigger);
buff.append(" AFTER INSERT, UPDATE, DELETE ON ");
buff.append(StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table));
buff.append(" FOR EACH ROW CALL \"");
buff.append(FullTextLucene.class.getName());
buff.append("\"");
stat.execute(buff.toString());
}
private static void indexExistingRows(Connection conn, String schema, String table) throws SQLException {
FullTextLucene existing = new FullTextLucene();
existing.init(conn, schema, null, table);
StringBuffer buff = new StringBuffer("SELECT * FROM ");
buff.append(StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table));
ResultSet rs = conn.createStatement().executeQuery(buff.toString());
int columnCount = rs.getMetaData().getColumnCount();
while (rs.next()) {
Object[] row = new Object[columnCount];
for (int i = 0; i < columnCount; i++) {
row[i] = rs.getObject(i + 1);
}
existing.fire(conn, null, row);
}
}
//#endif
/**
* Re-creates the full text index for this database
*
* @param conn the connection
*/
//#ifdef JDK14
public static void reindex(Connection conn) throws SQLException {
init(conn);
removeAllTriggers(conn);
......@@ -121,47 +102,21 @@ public class FullTextLucene implements Trigger {
indexExistingRows(conn, schema, table);
}
}
private static void removeAllTriggers(Connection conn) throws SQLException {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT * FROM INFORMATION_SCHEMA.TRIGGERS");
Statement stat2 = conn.createStatement();
while (rs.next()) {
String schema = rs.getString("TRIGGER_SCHEMA");
String name = rs.getString("TRIGGER_NAME");
if (name.startsWith(TRIGGER_PREFIX)) {
name = StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(name);
stat2.execute("DROP TRIGGER " + name);
}
}
}
private static void removeIndexFiles(Connection conn) throws SQLException {
String path = getIndexPath(conn);
IndexModifier index = (IndexModifier) indexers.get(path);
if (index != null) {
indexers.remove(path);
try {
index.flush();
index.close();
} catch (IOException e) {
throw convertException(e);
}
}
FileSystem.getInstance(path).deleteRecursive(path);
}
//#endif
/**
* Drops all full text indexes from the database.
*
* @param conn the connection
*/
//#ifdef JDK14
public static void dropAll(Connection conn) throws SQLException {
Statement stat = conn.createStatement();
stat.execute("DROP SCHEMA IF EXISTS " + SCHEMA);
removeAllTriggers(conn);
removeIndexFiles(conn);
}
//#endif
/**
* Initializes full text search functionality for this database. This adds the following Java functions to the
......@@ -182,6 +137,7 @@ public class FullTextLucene implements Trigger {
*
* @param conn
*/
//#ifdef JDK14
public static void init(Connection conn) throws SQLException {
Statement stat = conn.createStatement();
stat.execute("CREATE SCHEMA IF NOT EXISTS " + SCHEMA);
......@@ -191,40 +147,12 @@ public class FullTextLucene implements Trigger {
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_REINDEX FOR \"" + FullTextLucene.class.getName() + ".reindex\"");
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_DROP_ALL FOR \"" + FullTextLucene.class.getName() + ".dropAll\"");
}
private static IndexModifier getIndexModifier(Connection conn) throws SQLException {
try {
String path = getIndexPath(conn);
IndexModifier indexer;
synchronized (indexers) {
indexer = (IndexModifier) indexers.get(path);
if (indexer == null) {
// TODO: create flag = true means re-create
indexer = new IndexModifier(path, new StandardAnalyzer(), true);
indexers.put(path, indexer);
}
}
return indexer;
} catch (IOException e) {
throw convertException(e);
}
}
private static String getIndexPath(Connection conn) throws SQLException {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("CALL DATABASE_PATH()");
rs.next();
String path = rs.getString(1);
if (path == null) {
throw new SQLException("FULLTEXT", "Fulltext search for in-memory databases is not supported.");
}
rs.close();
return path;
}
//#endif
/**
* INTERNAL
*/
//#ifdef JDK14
public void init(Connection conn, String schemaName, String triggerName, String tableName) throws SQLException {
init(conn);
this.schemaName = schemaName;
......@@ -275,27 +203,12 @@ public class FullTextLucene implements Trigger {
indexColumns = new int[indexList.size()];
setColumns(indexColumns, indexList, columnList);
}
private void setColumns(int[] index, ArrayList keys, ArrayList columns) throws SQLException {
for (int i = 0; i < keys.size(); i++) {
String key = (String) keys.get(i);
int found = -1;
for (int j = 0; found == -1 && j < columns.size(); j++) {
String column = (String) columns.get(j);
if (column.equals(key)) {
found = j;
}
}
if (found < 0) {
throw new SQLException("FULLTEXT", "Column not found: " + key);
}
index[i] = found;
}
}
//#endif
/**
* INTERNAL
*/
//#ifdef JDK14
public void fire(Connection conn, Object[] oldRow, Object[] newRow) throws SQLException {
if (oldRow != null) {
delete(oldRow);
......@@ -304,7 +217,81 @@ public class FullTextLucene implements Trigger {
insert(newRow);
}
}
//#endif
/**
* Searches from the full text index for this database.
*
* @param conn the connection
* @param text the search query
* @param limit the maximum number of rows or 0 for no limit
* @param offset the offset or 0 for no offset
* @return the result set
*/
//#ifdef JDK14
public static ResultSet search(Connection conn, String text, int limit, int offset) throws SQLException {
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn(FIELD_QUERY, Types.VARCHAR, 0, 0);
if (text == null) {
// this is just to query the result set columns
return rs;
}
String path = getIndexPath(conn);
try {
IndexModifier indexer = getIndexModifier(conn);
indexer.flush();
IndexReader reader = IndexReader.open(path);
Analyzer analyzer = new StandardAnalyzer();
Searcher searcher = new IndexSearcher(reader);
QueryParser parser = new QueryParser(FIELD_DATA, analyzer);
Query query = parser.parse(text);
Hits hits = searcher.search(query);
int max = hits.length();
if (limit == 0) {
limit = max;
}
for (int i = 0; i < limit && i + offset < max; i++) {
Document doc = hits.doc(i + offset);
String q = doc.get(FIELD_QUERY);
rs.addRow(new Object[] { q });
}
// TODO keep it open if possible
reader.close();
} catch (Exception e) {
throw convertException(e);
}
return rs;
}
private static void removeAllTriggers(Connection conn) throws SQLException {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT * FROM INFORMATION_SCHEMA.TRIGGERS");
Statement stat2 = conn.createStatement();
while (rs.next()) {
String schema = rs.getString("TRIGGER_SCHEMA");
String name = rs.getString("TRIGGER_NAME");
if (name.startsWith(TRIGGER_PREFIX)) {
name = StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(name);
stat2.execute("DROP TRIGGER " + name);
}
}
}
private static void removeIndexFiles(Connection conn) throws SQLException {
String path = getIndexPath(conn);
IndexModifier index = (IndexModifier) indexers.get(path);
if (index != null) {
indexers.remove(path);
try {
index.flush();
index.close();
} catch (IOException e) {
throw convertException(e);
}
}
FileSystem.getInstance(path).deleteRecursive(path);
}
private String getQuery(Object[] row) throws SQLException {
StringBuffer buff = new StringBuffer();
if (schemaName != null) {
......@@ -475,48 +462,85 @@ public class FullTextLucene implements Trigger {
SQLException e2 = new SQLException("FULLTEXT", "Error while indexing document");
e2.initCause(e);
return e2;
}
private static void createTrigger(Connection conn, String schema, String table) throws SQLException {
Statement stat = conn.createStatement();
String trigger = StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(TRIGGER_PREFIX + table);
stat.execute("DROP TRIGGER IF EXISTS " + trigger);
StringBuffer buff = new StringBuffer("CREATE TRIGGER IF NOT EXISTS ");
buff.append(trigger);
buff.append(" AFTER INSERT, UPDATE, DELETE ON ");
buff.append(StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table));
buff.append(" FOR EACH ROW CALL \"");
buff.append(FullTextLucene.class.getName());
buff.append("\"");
stat.execute(buff.toString());
}
/**
* Searches from the full text index for this database.
*
* @param conn the connection
* @param text the search query
* @param limit the maximum number of rows or 0 for no limit
* @param offset the offset or 0 for no offset
* @return the result set
*/
public static ResultSet search(Connection conn, String text, int limit, int offset) throws SQLException {
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn(FIELD_QUERY, Types.VARCHAR, 0, 0);
if (text == null) {
// this is just to query the result set columns
return rs;
private static void indexExistingRows(Connection conn, String schema, String table) throws SQLException {
FullTextLucene existing = new FullTextLucene();
existing.init(conn, schema, null, table);
StringBuffer buff = new StringBuffer("SELECT * FROM ");
buff.append(StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table));
ResultSet rs = conn.createStatement().executeQuery(buff.toString());
int columnCount = rs.getMetaData().getColumnCount();
while (rs.next()) {
Object[] row = new Object[columnCount];
for (int i = 0; i < columnCount; i++) {
row[i] = rs.getObject(i + 1);
}
existing.fire(conn, null, row);
}
String path = getIndexPath(conn);
}
private static IndexModifier getIndexModifier(Connection conn) throws SQLException {
try {
IndexModifier indexer = getIndexModifier(conn);
indexer.flush();
IndexReader reader = IndexReader.open(path);
Analyzer analyzer = new StandardAnalyzer();
Searcher searcher = new IndexSearcher(reader);
QueryParser parser = new QueryParser(FIELD_DATA, analyzer);
Query query = parser.parse(text);
Hits hits = searcher.search(query);
int max = hits.length();
if (limit == 0) {
limit = max;
}
for (int i = 0; i < limit && i + offset < max; i++) {
Document doc = hits.doc(i + offset);
String q = doc.get(FIELD_QUERY);
rs.addRow(new Object[] { q });
String path = getIndexPath(conn);
IndexModifier indexer;
synchronized (indexers) {
indexer = (IndexModifier) indexers.get(path);
if (indexer == null) {
// TODO: create flag = true means re-create
indexer = new IndexModifier(path, new StandardAnalyzer(), true);
indexers.put(path, indexer);
}
}
// TODO keep it open if possible
reader.close();
} catch (Exception e) {
return indexer;
} catch (IOException e) {
throw convertException(e);
}
return rs;
}
private static String getIndexPath(Connection conn) throws SQLException {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("CALL DATABASE_PATH()");
rs.next();
String path = rs.getString(1);
if (path == null) {
throw new SQLException("FULLTEXT", "Fulltext search for in-memory databases is not supported.");
}
rs.close();
return path;
}
private void setColumns(int[] index, ArrayList keys, ArrayList columns) throws SQLException {
for (int i = 0; i < keys.size(); i++) {
String key = (String) keys.get(i);
int found = -1;
for (int j = 0; found == -1 && j < columns.size(); j++) {
String column = (String) columns.get(j);
if (column.equals(key)) {
found = j;
}
}
if (found < 0) {
throw new SQLException("FULLTEXT", "Column not found: " + key);
}
index[i] = found;
}
}
//#endif
}
......@@ -6,16 +6,16 @@ package org.h2.jdbcx;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
//#ifdef JDK14
import java.io.Serializable;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
//#ifdef JDK14
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.PooledConnection;
......@@ -36,11 +36,11 @@ import org.h2.message.Message;
* A data source for H2 database connections. It is a factory for XAConnection and Connection objects.
* This class is usually registered in a JNDI naming service.
*/
public class JdbcDataSource extends TraceObject implements
public class JdbcDataSource extends TraceObject
//#ifdef JDK14
XADataSource, DataSource, ConnectionPoolDataSource,
implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Referenceable
//#endif
Serializable, Referenceable {
{
private static final long serialVersionUID = 1288136338451857771L;
......@@ -211,6 +211,7 @@ Serializable, Referenceable {
*
* @return the new reference
*/
//#ifdef JDK14
public Reference getReference() throws NamingException {
debugCodeCall("getReference");
String factoryClassName = JdbcDataSourceFactory.class.getName();
......@@ -221,9 +222,14 @@ Serializable, Referenceable {
ref.add(new StringRefAddr("loginTimeout", String.valueOf(loginTimeout)));
return ref;
}
//#endif
/**
*
*/
//#ifdef JDK14
public XAConnection getXAConnection() throws SQLException {
int document;
debugCodeCall("getXAConnection");
int id = getNextId(XA_DATA_SOURCE);
return new JdbcXAConnection(factory, id, url, user, password);
......
......@@ -6,10 +6,12 @@ package org.h2.jdbcx;
import java.util.Hashtable;
//#ifdef JDK14
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
//#endif
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
......@@ -19,7 +21,11 @@ import org.h2.message.TraceSystem;
/**
* This class is used to create new JdbcDataSource objects.
*/
public class JdbcDataSourceFactory implements ObjectFactory {
public class JdbcDataSourceFactory
//#ifdef JDK14
implements ObjectFactory
//#endif
{
private static TraceSystem traceSystem;
private Trace trace;
......@@ -46,6 +52,7 @@ public class JdbcDataSourceFactory implements ObjectFactory {
* @param environment unused
* @return the new JdbcDataSource, or null if the reference class name is not JdbcDataSource.
*/
//#ifdef JDK14
public synchronized Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception {
trace.debug("getObjectInstance obj=" + obj + " name=" + name + " nameCtx=" + nameCtx + " environment=" + environment);
Reference ref = (Reference) obj;
......@@ -60,6 +67,7 @@ public class JdbcDataSourceFactory implements ObjectFactory {
}
return null;
}
//#endif
TraceSystem getTraceSystem() {
return traceSystem;
......
......@@ -64,21 +64,25 @@ implements XAConnection, XAResource, JdbcConnectionListener
connSentinel = openConnection();
getConnection();
}
//#endif
/**
* Get the XAResource object.
*
* @return itself
*/
//#ifdef JDK14
public XAResource getXAResource() throws SQLException {
debugCodeCall("getXAResource");
return this;
}
//#endif
/**
* Close the physical connection.
* This method is usually called by the connection pool.
*/
//#ifdef JDK14
public void close() throws SQLException {
debugCodeCall("close");
try {
......@@ -89,31 +93,15 @@ implements XAConnection, XAResource, JdbcConnectionListener
connSentinel = null;
}
}
//#endif
private void closeConnection(JdbcConnection conn) throws SQLException {
if (conn != null) {
conn.closeConnection();
}
}
private JdbcConnection openConnection() throws SQLException {
Properties info = new Properties();
info.setProperty("user", user);
info.setProperty("password", password);
JdbcConnection conn = new JdbcConnection(url, info);
conn.setJdbcConnectionListener(this);
if (currentTransaction != null) {
conn.setAutoCommit(false);
}
return conn;
}
/**
* Get a new connection.
* This method is usually called by the connection pool when there are no more connections in the pool.
*
* @return the connection
*/
//#ifdef JDK14
public Connection getConnection() throws SQLException {
debugCodeCall("getConnection");
if (conn != null) {
......@@ -124,12 +112,14 @@ implements XAConnection, XAResource, JdbcConnectionListener
conn.setJdbcConnectionListener(this);
return conn;
}
//#endif
/**
* Register a new listener for the connection.
*
* @param listener the event listener
*/
//#ifdef JDK14
public void addConnectionEventListener(ConnectionEventListener listener) {
debugCode("addConnectionEventListener(listener)");
listeners.add(listener);
......@@ -137,20 +127,24 @@ implements XAConnection, XAResource, JdbcConnectionListener
conn.setJdbcConnectionListener(this);
}
}
//#endif
/**
* Remove the event listener.
*
* @param listener the event listener
*/
//#ifdef JDK14
public void removeConnectionEventListener(ConnectionEventListener listener) {
debugCode("removeConnectionEventListener(listener)");
listeners.remove(listener);
}
//#endif
/**
* INTERNAL
*/
//#ifdef JDK14
public void fatalErrorOccurred(JdbcConnection conn, SQLException e) throws SQLException {
debugCode("fatalErrorOccurred(conn, e)");
for (int i = 0; i < listeners.size(); i++) {
......@@ -160,10 +154,12 @@ implements XAConnection, XAResource, JdbcConnectionListener
}
close();
}
//#endif
/**
* INTERNAL
*/
//#ifdef JDK14
public void closed(JdbcConnection conn) {
debugCode("closed(conn)");
for (int i = 0; i < listeners.size(); i++) {
......@@ -172,16 +168,19 @@ implements XAConnection, XAResource, JdbcConnectionListener
listener.connectionClosed(event);
}
}
//#endif
/**
* Get the transaction timeout.
*
* @return 0
*/
//#ifdef JDK14
public int getTransactionTimeout() throws XAException {
debugCodeCall("getTransactionTimeout");
return 0;
}
//#endif
/**
* Set the transaction timeout.
......@@ -189,22 +188,26 @@ implements XAConnection, XAResource, JdbcConnectionListener
* @param seconds ignored
* @return false
*/
//#ifdef JDK14
public boolean setTransactionTimeout(int seconds) throws XAException {
debugCodeCall("setTransactionTimeout", seconds);
return false;
}
//#endif
/**
* Checks if this is the same XAResource.
*
* @param xares the other object
* @return true if this is the same object
*/
//#ifdef JDK14
public boolean isSameRM(XAResource xares) throws XAException {
debugCode("isSameRM(xares)");
return xares == this;
}
//#endif
/**
* Get the list of prepared transaction branches.
* This method is called by the transaction manager during recovery.
......@@ -213,6 +216,7 @@ implements XAConnection, XAResource, JdbcConnectionListener
* TMNOFLAGS must be used.
* @return zero or more Xid objects
*/
//#ifdef JDK14
public Xid[] recover(int flag) throws XAException {
debugCodeCall("recover", quoteFlags(flag));
checkOpen();
......@@ -238,19 +242,14 @@ implements XAConnection, XAResource, JdbcConnectionListener
JdbcUtils.closeSilently(stat);
}
}
//#endif
private void checkOpen() throws XAException {
if (conn == null) {
getTrace().debug("conn==null");
throw new XAException(XAException.XAER_RMERR);
}
}
/**
* Prepare a transaction.
*
* @param xid the transaction id
*/
//#ifdef JDK14
public int prepare(Xid xid) throws XAException {
debugCode("prepare("+quoteXid(xid)+")");
checkOpen();
......@@ -271,22 +270,26 @@ implements XAConnection, XAResource, JdbcConnectionListener
getTrace().debug("return XA_OK");
return XA_OK;
}
//#endif
/**
* Forget a transaction.
* This method does not have an effect for this database.
*
* @param xid the transaction id
*/
//#ifdef JDK14
public void forget(Xid xid) throws XAException {
debugCode("forget("+quoteXid(xid)+")");
}
//#endif
/**
* Roll back a transaction.
*
* @param xid the transaction id
*/
//#ifdef JDK14
public void rollback(Xid xid) throws XAException {
debugCode("rollback("+quoteXid(xid)+")");
try {
......@@ -297,13 +300,15 @@ implements XAConnection, XAResource, JdbcConnectionListener
getTrace().debug("rolled back");
currentTransaction = null;
}
//#endif
/**
* End a transaction.
*
* @param xid the transaction id
* @param flags TMSUCCESS, TMFAIL, or TMSUSPEND
*/
//#ifdef JDK14
public void end(Xid xid, int flags) throws XAException {
debugCode("end("+quoteXid(xid)+", "+quoteFlags(flags)+")");
// TODO transaction end: implement this method
......@@ -315,65 +320,15 @@ implements XAConnection, XAResource, JdbcConnectionListener
throw new XAException(XAException.XAER_OUTSIDE);
}
}
//#endif
private String quoteFlags(int flags) {
StringBuffer buff = new StringBuffer();
if ((flags & XAResource.TMENDRSCAN) != 0) {
buff.append("|XAResource.TMENDRSCAN");
}
if ((flags & XAResource.TMFAIL) != 0) {
buff.append("|XAResource.TMFAIL");
}
if ((flags & XAResource.TMJOIN) != 0) {
buff.append("|XAResource.TMJOIN");
}
if ((flags & XAResource.TMONEPHASE) != 0) {
buff.append("|XAResource.TMONEPHASE");
}
if ((flags & XAResource.TMRESUME) != 0) {
buff.append("|XAResource.TMRESUME");
}
if ((flags & XAResource.TMSTARTRSCAN) != 0) {
buff.append("|XAResource.TMSTARTRSCAN");
}
if ((flags & XAResource.TMSUCCESS) != 0) {
buff.append("|XAResource.TMSUCCESS");
}
if ((flags & XAResource.TMSUSPEND) != 0) {
buff.append("|XAResource.TMSUSPEND");
}
if ((flags & XAResource.XA_OK) != 0) {
buff.append("|XAResource.XA_OK");
}
if ((flags & XAResource.XA_RDONLY) != 0) {
buff.append("|XAResource.XA_RDONLY");
}
if (buff.length() == 0) {
buff.append("|XAResource.TMNOFLAGS");
}
return buff.toString().substring(1);
}
private String quoteXid(Xid xid) {
StringBuffer buff = new StringBuffer();
buff.append("\"f:");
buff.append(xid.getFormatId());
buff.append(",bq:");
buff.append(ByteUtils.convertBytesToString(xid.getBranchQualifier()));
buff.append(",gx:");
buff.append(ByteUtils.convertBytesToString(xid.getGlobalTransactionId()));
buff.append(",c:");
buff.append(xid.getClass().getName());
buff.append("\"");
return buff.toString();
}
/**
* Start or continue to work on a transaction.
*
* @param xid the transaction id
* @param flags TMNOFLAGS, TMJOIN, or TMRESUME
*/
//#ifdef JDK14
public void start(Xid xid, int flags) throws XAException {
debugCode("start("+quoteXid(xid)+", "+quoteFlags(flags)+")");
if (flags == TMRESUME) {
......@@ -391,18 +346,15 @@ implements XAConnection, XAResource, JdbcConnectionListener
getTrace().debug("currentTransaction=xid");
currentTransaction = xid;
}
//#endif
private XAException convertException(SQLException e) {
getTrace().debug("throw XAException("+e.getMessage()+")");
return new XAException(e.getMessage());
}
/**
* Commit a transaction.
*
* @param xid the transaction id
* @param onePhase use a one-phase protocol if true
*/
//#ifdef JDK14
public void commit(Xid xid, boolean onePhase) throws XAException {
debugCode("commit("+quoteXid(xid)+", "+onePhase+")");
Statement stat = null;
......@@ -442,8 +394,92 @@ implements XAConnection, XAResource, JdbcConnectionListener
/**
* INTERNAL
*/
//#ifdef JDK14
public String toString() {
return getTraceObjectName() + ": url=" + url + " user=" + user;
}
private void closeConnection(JdbcConnection conn) throws SQLException {
if (conn != null) {
conn.closeConnection();
}
}
private JdbcConnection openConnection() throws SQLException {
Properties info = new Properties();
info.setProperty("user", user);
info.setProperty("password", password);
JdbcConnection conn = new JdbcConnection(url, info);
conn.setJdbcConnectionListener(this);
if (currentTransaction != null) {
conn.setAutoCommit(false);
}
return conn;
}
private XAException convertException(SQLException e) {
getTrace().debug("throw XAException("+e.getMessage()+")");
return new XAException(e.getMessage());
}
private String quoteXid(Xid xid) {
StringBuffer buff = new StringBuffer();
buff.append("\"f:");
buff.append(xid.getFormatId());
buff.append(",bq:");
buff.append(ByteUtils.convertBytesToString(xid.getBranchQualifier()));
buff.append(",gx:");
buff.append(ByteUtils.convertBytesToString(xid.getGlobalTransactionId()));
buff.append(",c:");
buff.append(xid.getClass().getName());
buff.append("\"");
return buff.toString();
}
private String quoteFlags(int flags) {
StringBuffer buff = new StringBuffer();
if ((flags & XAResource.TMENDRSCAN) != 0) {
buff.append("|XAResource.TMENDRSCAN");
}
if ((flags & XAResource.TMFAIL) != 0) {
buff.append("|XAResource.TMFAIL");
}
if ((flags & XAResource.TMJOIN) != 0) {
buff.append("|XAResource.TMJOIN");
}
if ((flags & XAResource.TMONEPHASE) != 0) {
buff.append("|XAResource.TMONEPHASE");
}
if ((flags & XAResource.TMRESUME) != 0) {
buff.append("|XAResource.TMRESUME");
}
if ((flags & XAResource.TMSTARTRSCAN) != 0) {
buff.append("|XAResource.TMSTARTRSCAN");
}
if ((flags & XAResource.TMSUCCESS) != 0) {
buff.append("|XAResource.TMSUCCESS");
}
if ((flags & XAResource.TMSUSPEND) != 0) {
buff.append("|XAResource.TMSUSPEND");
}
if ((flags & XAResource.XA_OK) != 0) {
buff.append("|XAResource.XA_OK");
}
if ((flags & XAResource.XA_RDONLY) != 0) {
buff.append("|XAResource.XA_RDONLY");
}
if (buff.length() == 0) {
buff.append("|XAResource.TMNOFLAGS");
}
return buff.toString().substring(1);
}
private void checkOpen() throws XAException {
if (conn == null) {
getTrace().debug("conn==null");
throw new XAException(XAException.XAER_RMERR);
}
}
//#endif
}
......@@ -47,13 +47,6 @@ implements Xid
}
}
// private JdbcXid(JdbcDataSourceFactory factory, int id, Xid xid) {
// setTrace(factory.getTrace(), TraceObject.XID, id);
// this.formatId = xid.getFormatId();
// this.branchQualifier = clone(xid.getBranchQualifier());
// this.globalTransactionId = clone(xid.getGlobalTransactionId());
// }
/**
* INTERNAL
*/
......@@ -68,12 +61,6 @@ implements Xid
return buff.toString();
}
// private byte[] clone(byte[] data) {
// byte[] d2 = new byte[data.length];
// System.arraycopy(data, 0, d2, 0, data.length);
// return d2;
// }
/**
* Get the format id.
*
......
......@@ -600,7 +600,7 @@ public class PgServerThread implements Runnable {
}
}
}
r = new InputStreamReader(getClass().getResourceAsStream("pg_catalog.sql"));
r = new InputStreamReader(PgServerThread.class.getResourceAsStream("pg_catalog.sql"));
ScriptReader reader = new ScriptReader(new BufferedReader(r));
while (true) {
String sql = reader.readStatement();
......
......@@ -4,6 +4,7 @@
*/
package org.h2.tools;
//#ifdef AWT
import java.awt.Button;
import java.awt.Dimension;
import java.awt.Font;
......@@ -24,11 +25,12 @@ import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import org.h2.util.IOUtils;
import java.io.InputStream;
//#endif
import java.sql.SQLException;
import org.h2.server.ShutdownHandler;
import org.h2.util.IOUtils;
import org.h2.util.StartBrowser;
/**
......@@ -37,11 +39,17 @@ import org.h2.util.StartBrowser;
* Otherwise, a small window opens.
*
*/
public class Console implements ActionListener, MouseListener, ShutdownHandler {
public class Console implements
//#ifdef AWT
ActionListener, MouseListener,
//#endif
ShutdownHandler {
//#ifdef AWT
private Font font;
private Image icon;
private Frame frame;
//#endif
private static final int EXIT_ERROR = 1;
private Server web, tcp, pg;
......@@ -92,10 +100,11 @@ public class Console implements ActionListener, MouseListener, ShutdownHandler {
System.out.println(pg.getStatus());
}
}
//#ifdef AWT
if (!GraphicsEnvironment.isHeadless()) {
font = new Font("Dialog", Font.PLAIN, 11);
try {
InputStream in = getClass().getResourceAsStream("/org/h2/res/h2.png");
InputStream in = Console.class.getResourceAsStream("/org/h2/res/h2.png");
if (in != null) {
byte[] imageData = IOUtils.readBytesAndClose(in, -1);
icon = Toolkit.getDefaultToolkit().createImage(imageData);
......@@ -107,6 +116,8 @@ public class Console implements ActionListener, MouseListener, ShutdownHandler {
e.printStackTrace();
}
}
//#endif
// start browser anyway (even if the server is already running)
// because some people don't look at the output,
// but are wondering why nothing happens
......@@ -137,13 +148,16 @@ public class Console implements ActionListener, MouseListener, ShutdownHandler {
pg.stop();
pg = null;
}
//#ifdef AWT
if (frame != null) {
frame.dispose();
frame = null;
}
//#endif
System.exit(0);
}
//#ifdef AWT
private boolean createTrayIcon() {
try {
// SystemTray.isSupported();
......@@ -260,10 +274,18 @@ public class Console implements ActionListener, MouseListener, ShutdownHandler {
frame.setLocation((screenSize.width - width) / 2, (screenSize.height - height) / 2);
frame.setVisible(true);
}
private void startBrowser() {
if (web != null) {
StartBrowser.openURL(web.getURL());
}
}
//#endif
/**
* INTERNAL
*/
//#ifdef AWT
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if ("exit".equals(command)) {
......@@ -274,44 +296,49 @@ public class Console implements ActionListener, MouseListener, ShutdownHandler {
showWindow(false);
}
}
private void startBrowser() {
if (web != null) {
StartBrowser.openURL(web.getURL());
}
}
//#endif
/**
* INTERNAL
*/
//#ifdef AWT
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
startBrowser();
}
}
//#endif
/**
* INTERNAL
*/
//#ifdef AWT
public void mouseEntered(MouseEvent e) {
}
//#endif
/**
* INTERNAL
*/
//#ifdef AWT
public void mouseExited(MouseEvent e) {
}
//#endif
/**
* INTERNAL
*/
//#ifdef AWT
public void mousePressed(MouseEvent e) {
}
//#endif
/**
* INTERNAL
*/
//#ifdef AWT
public void mouseReleased(MouseEvent e) {
}
//#endif
}
......@@ -86,6 +86,7 @@ public class JdbcUtils {
Class d = ClassUtils.loadClass(driver);
if (java.sql.Driver.class.isAssignableFrom(d)) {
return DriverManager.getConnection(url, prop);
//#ifdef JDK14
} else if (javax.naming.Context.class.isAssignableFrom(d)) {
// JNDI context
try {
......@@ -101,6 +102,7 @@ public class JdbcUtils {
} catch (NamingException e) {
throw Message.convert(e);
}
//#endif
} else {
// Don't know, but maybe it loaded a JDBC Driver
return DriverManager.getConnection(url, prop);
......
......@@ -38,18 +38,6 @@ public class ValueArray extends Value {
return values;
}
public int compareTo(ValueArray other, CompareMode mode) throws SQLException {
for (int i = 0; i < values.length; i++) {
Value v1 = values[i];
Value v2 = other.values[i];
int comp = v1.compareTo(v2, mode);
if (comp != 0) {
return comp;
}
}
return 0;
}
public int getType() {
return Value.ARRAY;
}
......@@ -71,43 +59,23 @@ public class ValueArray extends Value {
return buff.toString();
}
// public String getJavaString() {
// StringBuffer buff = new StringBuffer();
// buff.append('{');
// for (int i = 0; i < values.length; i++) {
// if (i > 0) {
// buff.append(", ");
// }
// buff.append(values[i].getJavaString());
// }
// buff.append('}');
// return buff.toString();
// }
protected int compareSecure(Value o, CompareMode mode) throws SQLException {
ValueArray v = (ValueArray) o;
if (values == v.values) {
return 0;
}
if (values.length != v.values.length) {
return values.length > v.values.length ? 1 : -1;
}
for (int i = 0; i < values.length; i++) {
int l = values.length;
int ol = v.values.length;
int len = Math.min(l, ol);
for (int i = 0; i < len; i++) {
Value v1 = values[i];
Value v2 = v.values[i];
int c;
if (v1 == ValueNull.INSTANCE) {
c = v2 == ValueNull.INSTANCE ? 0 : -1;
} else if (v2 == ValueNull.INSTANCE) {
c = 1;
} else {
c = v1.compareSecure(v2, mode);
}
if (c != 0) {
return c;
int comp = v1.compareTo(v2, mode);
if (comp != 0) {
return comp;
}
}
return 0;
return l > ol ? 1 : l == ol ? 0 : -1;
}
public Object getObject() throws SQLException {
......
......@@ -150,49 +150,11 @@ java org.h2.test.TestAll timer
/*
FAQ, build? translation
History
Now using custom toString() for most JDBC objects and commands.
Nested temporary views (SELECT * FROM (SELECT ...)) with parameters didn't work in some cases. Fixed.
CSV: Using an empty field delimiter didn't work (a workaround was using char(0)). Fixed.
Performance: use BufferedReader, BufferedWriter everywhere where possible (BufferInputStream alone doesn't help)
document Memory-Only databases > In-Memory Databases
By default, when the last connection to a in-memory database is closed, the contents are lost.
This can be disabled by adding this to the database URL: ;DB_CLOSE_DELAY=-1
That means to keep the contents of an in-memory database as long as the virtual machine is alive, use
jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
History:
The default value for h2.emergencySpaceInitial is now 256 KB (to speed up creating encrypted databases)
Eduardo Velasques has translated the H2 Console and the error messages to Brazilian Portuguese. Thanks a lot!
Creating a table from GROUP_CONCAT didn't work if the data was longer than 255 characters
document translation (what files to translate)
Known Problems:
link to history page, bug page
Add a link to the google code bug page
Email: h2@olivercomputing.com
Message:
Very cool project, I sent you a few euros yesterday.
I have a feature suggestion in the way that you sort array columns in ORDER BY. To take a concrete example, given
(0,1)
(0,1,1)
(0,2)
then PostgreSQL would order them as above, but H2 orders them as:
(0,1)
(0,2)
(0,1,1)
i.e. the ordering is evidently applied so that the array length is used in the count. While either way could be arguable, I would argue for the PostgreSQL ordering for two reasons:
- PostgreSQL compatibility itself
- Because, at least for my use case, I am using the arrays to represent XML hierarchy information. The first (PG) way of ordering naturally represents the XML document node order, but the current H2 way does not. Given the variable hierarchy of an XML document, such ordering can be difficult to do efficiently in a relational "shredded" node representation.
So, given that arrays are noted as experimental at this point, I thought I would ask if you could change their ordering scheme to match PostgreSQL's. Alternatively, perhaps a system variable could be used to pre-select the desired ordering. Thanks.
Phil Oliver
Google Android support: use 'ant codeswitchAndroid' to switch the source code to Android.
implement & test: checkpoint commits running transactions
......
......@@ -84,6 +84,7 @@ public class TestCluster extends TestBase {
n1.stop();
// re-create the cluster
DeleteDbFiles.main(new String[] { "-dir", baseDir + "/node2", "-quiet" });
CreateCluster.main(new String[] { "-urlSource", "jdbc:h2:file:" + baseDir + "/node1/test", "-urlTarget",
"jdbc:h2:file:" + baseDir + "/node2/test", "-user", "sa", "-serverlist",
"localhost:9091,localhost:9092" });
......@@ -92,6 +93,8 @@ public class TestCluster extends TestBase {
n2 = org.h2.tools.Server.createTcpServer(
new String[] { "-tcpPort", "9092", "-baseDir", baseDir + "/node2" }).start();
conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:9091,localhost:9092/test", "sa", "");
stat = conn.createStatement();
stat.execute("CREATE TABLE BOTH(ID INT)");
n1.stop();
......
--- special grammar and test cases ---------------------------------------------------------------------------------------------
create table test(id int primary key, data array);
> ok
insert into test values(1, (1, 1)), (2, (1, 2)), (3, (1, 1, 1));
> update count: 3
select * from test order by data;
> ID DATA
> -- ---------
> 1 (1, 1)
> 3 (1, 1, 1)
> 2 (1, 2)
> rows (ordered): 3
drop table test;
> ok
CREATE TABLE FOO (A CHAR(10));
> ok
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论