提交 34d36553 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 5d7da357
...@@ -40,7 +40,13 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -40,7 +40,13 @@ 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.59 (2007-09-TODO)</h3><ul> <h3>Version 1.0.59 (2007-09-TODO)</h3><ul>
<li>When using a subquery with group by as a table, some columns could not be used in the where condition <li>When creating a table using CREATE TABLE .. AS SELECT, the precision for some data types (for example VARCHAR)
was set to the default precision. Fixed.
</li><li>When using the (undocumented) in-memory file system (jdbc:h2:memFS:x or jdbc:h2:memLZF:x), and using
multiple connections, a ConcurrentModificationException could occur. Fixed.
</li><li>REGEXP compatibility: So far String.matches was used, but for compatibility with MySQL, now Matcher.find is used.
</li><li>SCRIPT: the SQL statements in the result set now include the terminating semicolon as well. Simplifies copy and paste.
</li><li>When using a subquery with group by as a table, some columns could not be used in the where condition
in the outer query. Example: SELECT * FROM (SELECT ID, COUNT(*) C FROM TEST) WHERE C > 100. Fixed. in the outer query. Example: SELECT * FROM (SELECT ID, COUNT(*) C FROM TEST) WHERE C > 100. Fixed.
</li><li>Views with subqueries as tables and queries with nested subqueries as tables did not always work. Fixed. </li><li>Views with subqueries as tables and queries with nested subqueries as tables did not always work. Fixed.
</li><li>Compatibility: comparing columns with constants that are out of range does not throw an exception. </li><li>Compatibility: comparing columns with constants that are out of range does not throw an exception.
......
...@@ -186,7 +186,8 @@ public class CreateTable extends SchemaCommand { ...@@ -186,7 +186,8 @@ public class CreateTable extends SchemaCommand {
String name = expr.getColumnName(); String name = expr.getColumnName();
long precision = expr.getPrecision(); long precision = expr.getPrecision();
DataType dt = DataType.getDataType(type); DataType dt = DataType.getDataType(type);
if (precision > 0 && (dt.defaultPrecision == 0 || dt.defaultPrecision > precision)) { if (precision > 0 && (dt.defaultPrecision == 0 || (dt.defaultPrecision > precision && dt.defaultPrecision < Byte.MAX_VALUE))) {
// dont' set precision to MAX_VALUE if this is the default
precision = dt.defaultPrecision; precision = dt.defaultPrecision;
} }
int scale = expr.getScale(); int scale = expr.getScale();
......
...@@ -304,13 +304,9 @@ public class ScriptCommand extends ScriptBase { ...@@ -304,13 +304,9 @@ public class ScriptCommand extends ScriptBase {
private int writeLobStream(ValueLob v) throws IOException, SQLException { private int writeLobStream(ValueLob v) throws IOException, SQLException {
if (!tempLobTableCreated) { if (!tempLobTableCreated) {
add( add("CREATE TABLE IF NOT EXISTS SYSTEM_LOB_STREAM(ID INT, PART INT, CDATA VARCHAR, BDATA BINARY, PRIMARY KEY(ID, PART))", true);
"CREATE TABLE IF NOT EXISTS SYSTEM_LOB_STREAM(ID INT, PART INT, CDATA VARCHAR, BDATA BINARY, PRIMARY KEY(ID, PART))", add("CREATE ALIAS IF NOT EXISTS SYSTEM_COMBINE_CLOB FOR \"" + this.getClass().getName() + ".combineClob\"", true);
true); add("CREATE ALIAS IF NOT EXISTS SYSTEM_COMBINE_BLOB FOR \"" + this.getClass().getName() + ".combineBlob\"", true);
add("CREATE ALIAS IF NOT EXISTS SYSTEM_COMBINE_CLOB FOR \"" + this.getClass().getName() + ".combineClob\"",
true);
add("CREATE ALIAS IF NOT EXISTS SYSTEM_COMBINE_BLOB FOR \"" + this.getClass().getName() + ".combineBlob\"",
true);
tempLobTableCreated = true; tempLobTableCreated = true;
} }
int id = nextLobId++; int id = nextLobId++;
...@@ -405,8 +401,9 @@ public class ScriptCommand extends ScriptBase { ...@@ -405,8 +401,9 @@ public class ScriptCommand extends ScriptBase {
if (s == null) { if (s == null) {
return; return;
} }
s += ";";
if (out != null) { if (out != null) {
byte[] buff = StringUtils.utf8Encode(s + ";"); byte[] buff = StringUtils.utf8Encode(s);
int len = MathUtils.roundUp(buff.length + lineSeparator.length, Constants.FILE_BLOCK_SIZE); int len = MathUtils.roundUp(buff.length + lineSeparator.length, Constants.FILE_BLOCK_SIZE);
buffer = ByteUtils.copy(buff, buffer); buffer = ByteUtils.copy(buff, buffer);
......
...@@ -8,6 +8,8 @@ import java.sql.SQLException; ...@@ -8,6 +8,8 @@ import java.sql.SQLException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Properties; import java.util.Properties;
import org.h2.api.DatabaseEventListener;
import org.h2.command.dml.SetTypes; import org.h2.command.dml.SetTypes;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
...@@ -164,6 +166,17 @@ public class ConnectionInfo { ...@@ -164,6 +166,17 @@ public class ConnectionInfo {
} }
} }
} }
DatabaseEventListener removeDatabaseEventListenerObject() throws SQLException {
Object p = prop.remove("DATABASE_EVENT_LISTENER_OBJECT");
if (p == null) {
return null;
}
if (p instanceof DatabaseEventListener) {
return (DatabaseEventListener) p;
}
throw Message.getSQLException(ErrorCode.DATA_CONVERSION_ERROR_1, p.getClass().getName());
}
private char[] removePassword() { private char[] removePassword() {
Object p = prop.remove("PASSWORD"); Object p = prop.remove("PASSWORD");
......
...@@ -153,15 +153,18 @@ public class Database implements DataHandler { ...@@ -153,15 +153,18 @@ public class Database implements DataHandler {
this.fileLockMethod = FileLock.getFileLockMethod(lockMethodName); this.fileLockMethod = FileLock.getFileLockMethod(lockMethodName);
this.textStorage = ci.getTextStorage(); this.textStorage = ci.getTextStorage();
this.databaseURL = ci.getURL(); this.databaseURL = ci.getURL();
String listener = ci.removeProperty("DATABASE_EVENT_LISTENER", null); this.eventListener = ci.removeDatabaseEventListenerObject();
if (listener != null) { if (eventListener == null) {
if (listener.startsWith("'")) { String listener = ci.removeProperty("DATABASE_EVENT_LISTENER", null);
listener = listener.substring(1); if (listener != null) {
} if (listener.startsWith("'")) {
if (listener.endsWith("'")) { listener = listener.substring(1);
listener = listener.substring(0, listener.length() - 1); }
if (listener.endsWith("'")) {
listener = listener.substring(0, listener.length() - 1);
}
setEventListener(listener);
} }
setEventListener(listener);
} }
String log = ci.getProperty(SetTypes.LOG, null); String log = ci.getProperty(SetTypes.LOG, null);
if (log != null) { if (log != null) {
......
...@@ -203,7 +203,8 @@ public class CompareLike extends Condition { ...@@ -203,7 +203,8 @@ public class CompareLike extends Condition {
String value = l.getString(); String value = l.getString();
boolean result; boolean result;
if (regexp) { if (regexp) {
result = patternRegexp.matcher(value).matches(); // result = patternRegexp.matcher(value).matches();
result = patternRegexp.matcher(value).find();
} else { } else {
result = compareAt(value, 0, 0, value.length()); result = compareAt(value, 0, 0, value.length());
} }
......
...@@ -195,7 +195,7 @@ public class FullText implements Trigger { ...@@ -195,7 +195,7 @@ public class FullText implements Trigger {
stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".ROWS(ID IDENTITY, HASH INT, INDEXID INT, KEY VARCHAR, UNIQUE(HASH, INDEXID, KEY))"); stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".ROWS(ID IDENTITY, HASH INT, INDEXID INT, KEY VARCHAR, UNIQUE(HASH, INDEXID, KEY))");
// 3391, 1484 // 3391, 1484
// stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".MAP(ROWID INT, WORDID INT, UNIQUE(ROWID, WORDID), UNIQUE(WORDID, ROWID))"); // stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".MAP(ROWID INT, WORDID INT, UNIQUE(ROWID, WORDID), UNIQUE(WORDID, ROWID))");
// 3063, 1484 // 3063, 1484
stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".MAP(ROWID INT, WORDID INT, PRIMARY KEY(WORDID, ROWID))"); stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".MAP(ROWID INT, WORDID INT, PRIMARY KEY(WORDID, ROWID))");
......
...@@ -153,10 +153,6 @@ public class ViewIndex extends BaseIndex { ...@@ -153,10 +153,6 @@ public class ViewIndex extends BaseIndex {
if (first != null) { if (first != null) {
Value v = first.getValue(i); Value v = first.getValue(i);
if (v != null) { if (v != null) {
if(paramList.size() <= idx) {
int tst;
System.out.println("stop!");
}
Parameter param = (Parameter) paramList.get(idx++); Parameter param = (Parameter) paramList.get(idx++);
param.setValue(v); param.setValue(v);
} }
......
...@@ -1183,7 +1183,7 @@ compare { {{ALL|ANY|SOME}(select)} | operand } ...@@ -1183,7 +1183,7 @@ compare { {{ALL|ANY|SOME}(select)} | operand }
"," ","
The right hand side of a condition. The right hand side of a condition.
When comparing with LIKE, the wildcards characters are _ (any one character) and % (any characters). When comparing with LIKE, the wildcards characters are _ (any one character) and % (any characters).
When comparing with REGEXP, regular expression matching is used. See Java String.matches for details. When comparing with REGEXP, regular expression matching is used. See Java Matcher.find for details.
"," ","
LIKE 'Jo%' LIKE 'Jo%'
" "
......
...@@ -20,6 +20,7 @@ import java.util.Locale; ...@@ -20,6 +20,7 @@ import java.util.Locale;
import java.util.Properties; import java.util.Properties;
import java.util.TimeZone; import java.util.TimeZone;
import org.h2.api.DatabaseEventListener;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
...@@ -418,22 +419,23 @@ public class WebServer implements Service { ...@@ -418,22 +419,23 @@ public class WebServer implements Service {
} }
} }
Connection getConnection(String driver, String url, String user, String password) throws Exception { Connection getConnection(String driver, String url, String user, String password, DatabaseEventListener listener) throws Exception {
driver = driver.trim(); driver = driver.trim();
url = url.trim(); url = url.trim();
user = user.trim();
password = password.trim();
org.h2.Driver.load(); org.h2.Driver.load();
Properties p = new Properties();
p.setProperty("user", user.trim());
p.setProperty("password", password.trim());
if (url.startsWith("jdbc:h2:")) {
p.put("DATABASE_EVENT_LISTENER_OBJECT", listener);
}
// try { // try {
// Driver dr = (Driver) urlClassLoader.loadClass(driver).newInstance(); // Driver dr = (Driver) urlClassLoader.loadClass(driver).newInstance();
// Properties p = new Properties();
// p.setProperty("user", user);
// p.setProperty("password", password);
// return dr.connect(url, p); // return dr.connect(url, p);
// } catch(ClassNotFoundException e2) { // } catch(ClassNotFoundException e2) {
// throw e2; // throw e2;
// } // }
return JdbcUtils.getConnection(driver, url, user, password); return JdbcUtils.getConnection(driver, url, p);
} }
void shutdown() { void shutdown() {
......
...@@ -31,6 +31,7 @@ import java.util.Random; ...@@ -31,6 +31,7 @@ import java.util.Random;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.Map.Entry; import java.util.Map.Entry;
import org.h2.api.DatabaseEventListener;
import org.h2.bnf.Bnf; import org.h2.bnf.Bnf;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
...@@ -43,7 +44,7 @@ import org.h2.util.ObjectArray; ...@@ -43,7 +44,7 @@ import org.h2.util.ObjectArray;
import org.h2.util.ScriptReader; import org.h2.util.ScriptReader;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
class WebThread extends Thread { class WebThread extends Thread implements DatabaseEventListener {
private WebServer server; private WebServer server;
private WebSession session; private WebSession session;
private Properties attributes; private Properties attributes;
...@@ -53,6 +54,8 @@ class WebThread extends Thread { ...@@ -53,6 +54,8 @@ class WebThread extends Thread {
private String ifModifiedSince; private String ifModifiedSince;
private String mimeType; private String mimeType;
private boolean cache; private boolean cache;
private int listenerLastState;
private long listenerLastEvent;
// TODO web: support online data editing like http://numsum.com/ // TODO web: support online data editing like http://numsum.com/
...@@ -800,7 +803,7 @@ class WebThread extends Thread { ...@@ -800,7 +803,7 @@ class WebThread extends Thread {
session.put("url", url); session.put("url", url);
session.put("user", user); session.put("user", user);
try { try {
Connection conn = server.getConnection(driver, url, user, password); Connection conn = server.getConnection(driver, url, user, password, this);
JdbcUtils.closeSilently(conn); JdbcUtils.closeSilently(conn);
session.put("error", "${text.login.testSuccessful}"); session.put("error", "${text.login.testSuccessful}");
return "index.jsp"; return "index.jsp";
...@@ -824,7 +827,7 @@ class WebThread extends Thread { ...@@ -824,7 +827,7 @@ class WebThread extends Thread {
String user = attributes.getProperty("user", ""); String user = attributes.getProperty("user", "");
String password = attributes.getProperty("password", ""); String password = attributes.getProperty("password", "");
try { try {
Connection conn = server.getConnection(driver, url, user, password); Connection conn = server.getConnection(driver, url, user, password, this);
session.setConnection(conn); session.setConnection(conn);
session.put("url", url); session.put("url", url);
session.put("user", user); session.put("user", user);
...@@ -1563,4 +1566,57 @@ class WebThread extends Thread { ...@@ -1563,4 +1566,57 @@ class WebThread extends Thread {
return session; return session;
} }
private void log(String s) {
int test;
System.out.println(s);
}
public void closingDatabase() {
log("Closing database");
}
public void diskSpaceIsLow(long stillAvailable) throws SQLException {
log("Disk space is low; still available: " + stillAvailable);
}
public void exceptionThrown(SQLException e, String sql) {
log("Exception: " + e.toString() + " SQL: " + sql);
}
public void init(String url) {
log("Init: " + url);
}
public void opened() {
log("Database was opened");
}
public void setProgress(int state, String name, int x, int max) {
if (state == listenerLastState) {
long time = System.currentTimeMillis();
if (listenerLastEvent + 500 < time) {
return;
}
listenerLastEvent = time;
} else {
listenerLastState = state;
}
switch(state) {
case DatabaseEventListener.STATE_BACKUP_FILE:
log("Backing up " + name + " " + (100L * max / x) + "%");
break;
case DatabaseEventListener.STATE_CREATE_INDEX:
log("Creating index " + name + " " + (100L * max / x) + "%");
break;
case DatabaseEventListener.STATE_RECOVER:
log("Recovering " + name + " " + (100L * max / x) + "%");
break;
case DatabaseEventListener.STATE_SCAN_FILE:
log("Scanning file " + name + " " + (100L * max / x) + "%");
break;
default:
log("Unknown state: " + state);
}
}
} }
...@@ -132,7 +132,9 @@ public class FileUtils { ...@@ -132,7 +132,9 @@ public class FileUtils {
if (isInMemory(oldName)) { if (isInMemory(oldName)) {
MemoryFile f = getMemoryFile(oldName); MemoryFile f = getMemoryFile(oldName);
f.setName(newName); f.setName(newName);
MEMORY_FILES.put(newName, f); synchronized (MEMORY_FILES) {
MEMORY_FILES.put(newName, f);
}
return; return;
} }
File oldFile = new File(oldName); File oldFile = new File(oldName);
...@@ -239,7 +241,9 @@ public class FileUtils { ...@@ -239,7 +241,9 @@ public class FileUtils {
public static void delete(String fileName) throws SQLException { public static void delete(String fileName) throws SQLException {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
if (isInMemory(fileName)) { if (isInMemory(fileName)) {
MEMORY_FILES.remove(fileName); synchronized (MEMORY_FILES) {
MEMORY_FILES.remove(fileName);
}
return; return;
} }
File file = new File(fileName); File file = new File(fileName);
...@@ -309,7 +313,9 @@ public class FileUtils { ...@@ -309,7 +313,9 @@ public class FileUtils {
public static void tryDelete(String fileName) { public static void tryDelete(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
if (isInMemory(fileName)) { if (isInMemory(fileName)) {
MEMORY_FILES.remove(fileName); synchronized (MEMORY_FILES) {
MEMORY_FILES.remove(fileName);
}
return; return;
} }
trace("tryDelete", fileName, null); trace("tryDelete", fileName, null);
...@@ -328,19 +334,23 @@ public class FileUtils { ...@@ -328,19 +334,23 @@ public class FileUtils {
public static boolean exists(String fileName) { public static boolean exists(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
if (isInMemory(fileName)) { if (isInMemory(fileName)) {
return MEMORY_FILES.get(fileName) != null; synchronized (MEMORY_FILES) {
return MEMORY_FILES.get(fileName) != null;
}
} }
return new File(fileName).exists(); return new File(fileName).exists();
} }
public static MemoryFile getMemoryFile(String fileName) { public static MemoryFile getMemoryFile(String fileName) {
MemoryFile m = (MemoryFile) MEMORY_FILES.get(fileName); synchronized (MEMORY_FILES) {
if (m == null) { MemoryFile m = (MemoryFile) MEMORY_FILES.get(fileName);
boolean compress = fileName.startsWith(MEMORY_PREFIX_LZF); if (m == null) {
m = new MemoryFile(fileName, compress); boolean compress = fileName.startsWith(MEMORY_PREFIX_LZF);
MEMORY_FILES.put(fileName, m); m = new MemoryFile(fileName, compress);
MEMORY_FILES.put(fileName, m);
}
return m;
} }
return m;
} }
public static long length(String fileName) { public static long length(String fileName) {
...@@ -396,13 +406,15 @@ public class FileUtils { ...@@ -396,13 +406,15 @@ public class FileUtils {
public static String[] listFiles(String path) throws SQLException { public static String[] listFiles(String path) throws SQLException {
path = translateFileName(path); path = translateFileName(path);
if (isInMemory(path)) { if (isInMemory(path)) {
String[] list = new String[MEMORY_FILES.size()]; synchronized (MEMORY_FILES) {
MemoryFile[] l = new MemoryFile[MEMORY_FILES.size()]; String[] list = new String[MEMORY_FILES.size()];
MEMORY_FILES.values().toArray(l); MemoryFile[] l = new MemoryFile[MEMORY_FILES.size()];
for (int i = 0; i < list.length; i++) { MEMORY_FILES.values().toArray(l);
list[i] = l[i].getName(); for (int i = 0; i < list.length; i++) {
list[i] = l[i].getName();
}
return list;
} }
return list;
} }
File f = new File(path); File f = new File(path);
try { try {
......
...@@ -9,6 +9,7 @@ import java.sql.DriverManager; ...@@ -9,6 +9,7 @@ import java.sql.DriverManager;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Properties;
//#ifdef JDK14 //#ifdef JDK14
import javax.naming.Context; import javax.naming.Context;
...@@ -73,16 +74,25 @@ public class JdbcUtils { ...@@ -73,16 +74,25 @@ public class JdbcUtils {
//#endif //#endif
public static Connection getConnection(String driver, String url, String user, String password) throws SQLException { public static Connection getConnection(String driver, String url, String user, String password) throws SQLException {
Properties prop = new Properties();
prop.setProperty("user", user);
prop.setProperty("password", password);
return getConnection(driver, url, prop);
}
public static Connection getConnection(String driver, String url, Properties prop) throws SQLException {
if (!StringUtils.isNullOrEmpty(driver)) { if (!StringUtils.isNullOrEmpty(driver)) {
try { try {
Class d = ClassUtils.loadClass(driver); Class d = ClassUtils.loadClass(driver);
if (java.sql.Driver.class.isAssignableFrom(d)) { if (java.sql.Driver.class.isAssignableFrom(d)) {
return DriverManager.getConnection(url, user, password); return DriverManager.getConnection(url, prop);
} else if (javax.naming.Context.class.isAssignableFrom(d)) { } else if (javax.naming.Context.class.isAssignableFrom(d)) {
// JNDI context // JNDI context
try { try {
Context context = (Context) d.newInstance(); Context context = (Context) d.newInstance();
DataSource ds = (DataSource) context.lookup(url); DataSource ds = (DataSource) context.lookup(url);
String user = prop.getProperty("user");
String password = prop.getProperty("password");
return ds.getConnection(user, password); return ds.getConnection(user, password);
} catch (InstantiationException e) { } catch (InstantiationException e) {
throw Message.convert(e); throw Message.convert(e);
...@@ -93,13 +103,13 @@ public class JdbcUtils { ...@@ -93,13 +103,13 @@ public class JdbcUtils {
} }
} else { } else {
// Don't know, but maybe it loaded a JDBC Driver // Don't know, but maybe it loaded a JDBC Driver
return DriverManager.getConnection(url, user, password); return DriverManager.getConnection(url, prop);
} }
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
throw Message.getSQLException(ErrorCode.CLASS_NOT_FOUND_1, new String[]{driver}, e); throw Message.getSQLException(ErrorCode.CLASS_NOT_FOUND_1, new String[]{driver}, e);
} }
} }
return DriverManager.getConnection(url, user, password); return DriverManager.getConnection(url, prop);
} }
} }
...@@ -48,6 +48,7 @@ import org.h2.test.db.TestTwoPhaseCommit; ...@@ -48,6 +48,7 @@ import org.h2.test.db.TestTwoPhaseCommit;
import org.h2.test.db.TestView; import org.h2.test.db.TestView;
import org.h2.test.jdbc.TestCancel; import org.h2.test.jdbc.TestCancel;
import org.h2.test.jdbc.TestDataSource; import org.h2.test.jdbc.TestDataSource;
import org.h2.test.jdbc.TestDatabaseEventListener;
import org.h2.test.jdbc.TestManyJdbcObjects; import org.h2.test.jdbc.TestManyJdbcObjects;
import org.h2.test.jdbc.TestMetaData; import org.h2.test.jdbc.TestMetaData;
import org.h2.test.jdbc.TestNativeSQL; import org.h2.test.jdbc.TestNativeSQL;
...@@ -143,82 +144,80 @@ java org.h2.test.TestAll timer ...@@ -143,82 +144,80 @@ java org.h2.test.TestAll timer
/* /*
DROP TABLE IF EXISTS TEST; web page translation
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255));
INSERT INTO TEST VALUES(1, 'Hello'); Full Text Search
INSERT INTO TEST VALUES(2, 'HelloHello'); H2 supports Lucene full text search and native full text search implementation.
SELECT * FROM TEST WHERE NAME REGEXP 'He';
Using the Native Full Text Search
java.util.ConcurrentModificationException To initialize, call:
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:841)
at java.util.HashMap$ValueIterator.next(HashMap.java:871) CREATE ALIAS IF NOT EXISTS FT_INIT FOR "org.h2.fulltext.FullText.init";
at java.util.AbstractCollection.toArray(AbstractCollection.java:176) CALL FT_INIT();
at org.h2.util.FileUtils.listFiles(FileUtils.java:395)
at org.h2.engine.Database.deleteOldTempFiles(Database.java:1055) Afterwards, you can create a full text index for a table using:
at org.h2.engine.Database.closeOpenFilesAndUnlock(Database.java:853)
at org.h2.engine.Database.close(Database.java:814) CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR);
at org.h2.engine.Database.removeSession(Database.java:755) INSERT INTO TEST VALUES(1, 'Hello World');
at org.h2.engine.Session.close(Session.java:260)
at org.h2.engine.Database.close(Database.java:783) CALL FT_CREATE_INDEX('PUBLIC', 'TEST', NULL);
where PUBLIC is the schema, TEST is the table name. The list of column names (column separated) is optional, in this case all columns are indexed. The index is updated in read time. To search the index, use the following query:
SCRIPT: append ; also in result set (copy paste problem)
SELECT * FROM FT_SEARCH('Hello', 0, 0);
You can also call the index from within a Java application:
org.h2.fulltext.FullText.search(conn, text, limit, offset)
Using the Lucene Full Text Search
To use the Lucene full text search, you first need to rename the file FullTextLucene.java.txt to FullTestLucene.java and compile it. Also, you need the Lucene library in the classpath.
To initialize, call:
CREATE ALIAS IF NOT EXISTS FTL_INIT FOR "org.h2.fulltext.FullTextLucene.init";
CALL FTL_INIT();
Afterwards, you can create a full text index for a table using:
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR);
INSERT INTO TEST VALUES(1, 'Hello World');
CALL FTL_CREATE_INDEX('PUBLIC', 'TEST', NULL);
where PUBLIC is the schema, TEST is the table name. The list of column names (column separated) is optional, in this case all columns are indexed. The index is updated in read time. To search the index, use the following query:
SELECT * FROM FTL_SEARCH('Hello', 0, 0);
You can also call the index from within a Java application:
org.h2.fulltext.FullTextLucene.search(conn, text, limit, offset)
create table test(id int, name varchar(255));
SELECT * FROM TEST a natural JOIN TEST b;
drop table test;
H2:
ID, NAME, ID, NAME
MySQL, PostgreSQL:
ID, NAME
Derby, HSQLDB, MS SQL Server:
no supported
H2 supports cancel for the embedded mode but not yet for the client / server mode. I will add a feature request for this.
set log 0;
create table test(id int primary key, name varchar);
@LOOP 10000 insert into test values(?, space(100000));
shutdown;
a SHUTDOWN command is not enough? >
No, currently SHUTDOWN will not correctly close the objects if LOG is set to 0.
It _should_ be enough however. I will change the code so that in the future SHUTDOWN will be enough, and regular database closing will be enough.
run benchmark with newest version of apache derby and hsqldb
java org.h2.tools.RunScript -url jdbc:h2:file:bug -user SA -script \temp\test\data.sql TestMultiThreadedKernel and integrate in unit tests; use also in-memory and so on
data.sql, test.java
I have a big problem with PreparedStatements and version 2007-07-12 or above.
With 2007-04-29 or lower it works as expected.
In the test query i use a UNION ALL (i know, not well tested), but also with a simple SELECT/UPDATE/INSERT prepared statement with a where clause and parameters the problem happens, if i run the query more than once with different parameters. It always returns the result from the first executeQuery() or updates the rows return from the first query.
But the funny thing is, i cannot reproduce the bug with a simple SELECT in the test app.
If i run the test app, the output is with ver 2007-07-12:
Row Count 9
Sum 714.8621259
Row Count 9
Sum 714.8621259
Row Count 9
Sum 714.8621259
(looks like it takes every time the first result)
with ver 2007-04-29
Row Count 9
Sum 714.8621259
Row Count 7
Sum 0.0
Row Count 10
Sum 381.230477
drop table multi_pages;
drop table bib_holdings;
create table multi_pages(dir_num int, bh_id int);
insert into multi_pages values(1, 1);
insert into multi_pages values(2, 2);
insert into multi_pages values(3, 3);
create table bib_holdings(id int primary key, site varchar(255));
insert into bib_holdings values(1, 'WSTIAC');
insert into bib_holdings values(2, 'WSTIAC');
insert into bib_holdings values(3, 'WSTIAC');
select * from (select dir_num, count(*) as cnt
from multi_pages t, bib_holdings bh
where t.bh_id=bh.id and bh.site='WSTIAC' group by dir_num) as x
where cnt < 1000 order by dir_num asc;
explain select * from (select dir_num, count(*) as cnt
from multi_pages t, bib_holdings bh
where t.bh_id=bh.id and bh.site='WSTIAC' group by dir_num) as x
where cnt < 1000 order by dir_num asc;
select dir_num, count(*) as cnt
from multi_pages t, bib_holdings bh
where t.bh_id=bh.id and bh.site='WSTIAC'
group by dir_num
having count(*) < 1000
order by dir_num asc;
...@@ -624,6 +623,7 @@ TRUNC, NVL2, TO_CHAR, TO_DATE, TO_NUMBER; ...@@ -624,6 +623,7 @@ TRUNC, NVL2, TO_CHAR, TO_DATE, TO_NUMBER;
// jdbc // jdbc
new TestCancel().runTest(this); new TestCancel().runTest(this);
// new TestDatabaseEventListener().runTest(this);
new TestDataSource().runTest(this); new TestDataSource().runTest(this);
new TestManyJdbcObjects().runTest(this); new TestManyJdbcObjects().runTest(this);
new TestMetaData().runTest(this); new TestMetaData().runTest(this);
......
...@@ -63,11 +63,11 @@ public class TestTriggersConstraints extends TestBase implements Trigger { ...@@ -63,11 +63,11 @@ public class TestTriggersConstraints extends TestBase implements Trigger {
rs = stat.executeQuery("SCRIPT"); rs = stat.executeQuery("SCRIPT");
checkRows(rs, new String[] { checkRows(rs, new String[] {
"CREATE TRIGGER PUBLIC.INS_BEFORE BEFORE INSERT ON PUBLIC.TEST FOR EACH ROW NOWAIT CALL \"" "CREATE TRIGGER PUBLIC.INS_BEFORE BEFORE INSERT ON PUBLIC.TEST FOR EACH ROW NOWAIT CALL \""
+ getClass().getName() + "\"", + getClass().getName() + "\";",
"CREATE TRIGGER PUBLIC.INS_AFTER AFTER INSERT ON PUBLIC.TEST FOR EACH ROW NOWAIT CALL \"" "CREATE TRIGGER PUBLIC.INS_AFTER AFTER INSERT ON PUBLIC.TEST FOR EACH ROW NOWAIT CALL \""
+ getClass().getName() + "\"", + getClass().getName() + "\";",
"CREATE TRIGGER PUBLIC.UPD_BEFORE BEFORE UPDATE ON PUBLIC.TEST FOR EACH ROW NOWAIT CALL \"" "CREATE TRIGGER PUBLIC.UPD_BEFORE BEFORE UPDATE ON PUBLIC.TEST FOR EACH ROW NOWAIT CALL \""
+ getClass().getName() + "\"" }); + getClass().getName() + "\";" });
while (rs.next()) { while (rs.next()) {
String sql = rs.getString(1); String sql = rs.getString(1);
if (sql.startsWith("CREATE TRIGGER")) { if (sql.startsWith("CREATE TRIGGER")) {
......
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255));
INSERT INTO TEST VALUES(1, 'Hello'), (2, 'HelloWorld'), (3, 'HelloWorldWorld');
SELECT COUNT(*) FROM TEST WHERE NAME REGEXP 'World';
> 2;
SELECT NAME FROM TEST WHERE NAME REGEXP 'WorldW';
> HelloWorldWorld;
drop table test;
select * from (select x from (select x from dual)) where 1=x; select * from (select x from (select x from dual)) where 1=x;
> 1; > 1;
CREATE VIEW TEST_VIEW AS SELECT X FROM (SELECT X FROM DUAL); CREATE VIEW TEST_VIEW AS SELECT X FROM (SELECT X FROM DUAL);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论