提交 4fc52e91 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 9d35faf1
......@@ -108,10 +108,10 @@ That is not easy to say. It is still a quite new product. A lot of tests have be
and the code coverage of these tests is very high. Randomized stress tests
are run regularly. But as this is a relatively new product, there are probably
some problems that have not yet been found.
Areas that are not completely tested:
Areas that are not 100% tested:
</p>
<ul>
<li>Platforms other than Windows XP and the Sun JVM 1.4
<li>Platforms other than Windows XP and the Sun JVM 1.4 and 1.5
</li><li>Data types BLOB, CLOB, VARCHAR_IGNORECASE, OTHER
</li><li>Cluster mode, 2-Phase Commit, Savepoints
</li><li>Server mode (well tested, but not as well as Embedded mode)
......
......@@ -280,6 +280,9 @@ It looks like the development of this database has stopped. The last release was
</tr><tr>
<td><a href="http://www.shellbook.com">Shellbook</a></td>
<td>Desktop publishing application.</td>
</tr><tr>
<td><a href="http://www.intellibo.com">Signsoft intelliBO</a></td>
<td>Persistence middleware supporting the JDO specification.</td>
</tr><tr>
<td><a href="http://sql-workbench.net">SQL Workbench/J</a></td>
<td>Free DBMS-independent SQL tool.</td>
......
......@@ -37,8 +37,18 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3>
<h3>Version 1.0 / TODO (Build TODO)</h3><ul>
<li>The H2 Console can now be run as a standalone web application, or it can be embedded as a servlet into any
existing web application. To build the 'H2 Console' web application, execute 'ant warConsole'.
<li>If SHUTDOWN IMMEDIATELY was called, then the connection was not closed and the database
opened from somebody else at the same time, in some cases this could result in errors with LOB files. Fixed.
</li><li>The new view implementation is now enabled by default.
To use the old implementation, set the system property 'h2.indexOld' to true
(java -Dh2.indexOld=true ..., or in source code Constants.INDEX_OLD = true).
If no problems are found, the old implementation will be removed in the next release.
The old implementation does not work with multi-level nested temporary views
(select * from (select * from (select * from test))).
</li><li>The new view implementation did not work with &lt; and &lt;= comparison. Fixed.
</li><li>The H2 Console can now be run as a standalone web application,
or it can be embedded as a servlet into any existing web application. To build the
'H2 Console' web application, execute 'ant warConsole'.
See src/tools/org/h2/server/web and src/tools/WEB-INF for details.
</li><li>Deleting database files didn't work for Windows if the database was on the root directory of a drive.
</li><li>The Polish translation is available. Thanks a lot to Tomek!
......@@ -192,7 +202,7 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<li>Setting the collation (SET COLLATOR) was very slow on some systems (up to 24 seconds).
Thanks a lot to Martina Nissler for finding this problem!
</li><li>The Console is now translated to Japanese thanks to IKEMOTO, Masahiro (ikeyan (at) arizona (dot) ne (dot) jp)
</li><li>The database engine can now be compiled with JDK 1.3 using ant codeswitch_jdk13.
</li><li>The database engine can now be compiled with JDK 1.3 using ant codeswitch.
There are still some limitations, and the ant script to build the jar does not work yet.
</li><li>Fixed a problem where data in the log file was not written to the data file (recovery failure) after a crash,
if an index was deleted previously.
......@@ -220,7 +230,7 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Using ;RECOVER=1 in the database URL threw a syntax exception. Fixed.
</li><li>If a CLOB or BLOB was deleted in a transaction and the database crashed before the transaction was committed or rolled back,
the object was lost if it was large. Fixed.
</li><li>Now using ant-build.properties. The jdk is automatically updated when using ant codeswitch_...
</li><li>Now using ant-build.properties. The jdk is automatically updated when using ant codeswitch...
</li><li>Cluster: Now the server can detect if a query is read-only, and in this case the result is only read from the first cluster node.
However, there is currently no load balancing made to avoid problems with transactions / locking.
</li><li>Many settings are now initialized from system properties and can be changed on the command line without having
......@@ -777,420 +787,6 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
New functions: MEMORY_FREE() and MEMORY_USED().
</li></ul>
<h3>Version 0.9 / 2006-06-16</h3><ul>
<li>
Implemented distributing lob files into directories, and only keep up to 255 files in one directory.
However this is disabled by default; it will be enabled the next time the file format changes
(maybe not before 1.1). It can be enabled by the application by setting
Constants.LOB_FILES_IN_DIRECTORIES = true;
</li><li>
If a connection is closed while there is still an operation running, this operation is stopped (like when calling Statement.cancel).
</li><li>
Issue #115: If three or more threads / connections are used, sometimes lock timeout exceptions can occur when they should not. Solved.
</li><li>
Calling Server.start...Server now doesn't return until the server socket is ready to accept connections.
</li><li>
Issue #112: Two threads could not open the same database at the same time. Solved.
This was only a problem when the database was opened in a non-standard was, without using DriverManager.getConnection.
</li><li>
Implemented DROP TRIGGER.
</li><li>
Issue #113: Drop is now restricted: can only drop sequences / functions / tables if nothing depends on them.
</li><li>
Issue #114: Support large index data size. Before, there was a limit of around 1000 bytes per row.
</li><li>
Blob.getLength() and Clob.getLength() are now fast operations and don't read the whole object any longer.
</li><li>
Issue #111: The catalog name in the DatabaseMetaData calls and Connection.getCatalog was lowercase;
some applications may not work because they expect it to be uppercase.
</li><li>
Issue #110: PreparedStatement.setCharacterStream(int parameterIndex, Reader reader, int length) and
ResultSet.updateCharacterStream(...) didn't work correctly for 'length' larger than 0 and using Unicode characters
higher than 127 (or 0). The number of UTF-8 bytes where counted instead of the number of characters.
</li><li>
The catalog name is now uppercase, to conform the JDBC standard for DatabaseMetaData.storesUpperCaseIdentifiers().
</li><li>
Creating or opening a small encrypted database is now a lot faster.
</li><li>
New functions FORMATDATETIME and PARSEDATETIME.
</li><li>
New XML encoding functions (XMLATTR, XMLNODE, XMLCOMMENT, XMLCDATA, XMLSTARTDOC, XMLTEXT).
</li><li>
Performance: improved opening of a large databases (about 3 times faster now for 500 MB databases).
</li><li>
Documented ALTER TABLE DROP COLUMN. The functionality was there already, but the documentation not.
</li></ul>
<h3>Version 0.9 / 2006-06-02 (Build 10)</h3><ul>
<li>
Removed the GCJ h2-server.exe from download. It was not stable on Windows.
</li><li>
Issue #109: ALTER TABLE ADD COLUMN can make the database unusable if the original table contained a IDENTITY column.
</li><li>
New option to disable automatic closing of a database when the virtual machine exits.
Database URL: jdbc:h2:test;db_close_on_exit=false
</li><li>
New event: DatabaseEventListener.closingDatabase() is called before closing the database.
</li><li>
Connection.getCatalog() now returns the database name (CALL DATABASE()).
This name is also used in all system tables and DatabaseMetaData calls where applicable.
</li><li>
The function DATABASE() now return the short name of the database (without path),
and 'Unnamed' for anonymous in-memory databases.
</li><li>
Issue #108: There is a concurrency problem when multi threads access the same database at the same time,
and one is closing the connection and the other is executing CHECKPOINT at the exact the same time. Fixed.
</li><li>
Statements containing LIKE are now re-compiled when executed. Depending on the data,
an index on the column is used or not.
</li><li>
Issue# 107: When executing scripts that contained inserts with many columns, an OutOfMemory error could occur.
The problem was usage of shared Strings (String.substring). Now each String value is copied if required, releasing memory.
</li><li>
Issue #106: SET commands where not persisted if they where the first DDL commands for a connection. Fixed.
</li><li>
Issue #105: RUNSCRIPT (the command) didn't commit after each command if autocommit was on, therefore
large scripts run out of memory. Now it automatically commits.
</li><li>
Automatic starting of a web browser for Mac OS X should work now.
</li><li>
Starting the server is not a bit more intuitive. Just setting the -baseDir option for example will still start all servers.
By default, -tcp, -web, -browser and -odbc are started.
</li><li>
Issue #104: A HAVING condition on a column that was not in the GROUP BY list didn't work correctly in all cases. Fixed.
</li><li>
ORDER BY now uses an index if possible. Queries with LIMIT with ORDER BY are faster when the index can be used.
</li><li>
New option '-ifExists' for the TCP and ODBC server to disallow creating new databases remotely.
</li><li>
Shutdown of a TCP Server: Can now specify a password (tcpPassword). Passwords used at startup and
shutdown must match to shutdown. Uses a management database now for each server / port.
New option tcpShutdownForce (default is false) to kill the server without waiting for other connections to close.
</li><li>
Issue #103: Shutdown of a TCP Server from command line didn't always work.
</li></ul>
<h3>Version 0.9 / 2006-05-14</h3><ul>
<li>
New functions: CSVREAD and CSVWRITE to access CSV (comma separated values) files.
</li><li>
Locking: the synchronization was too restrictive, locking out the connection holding a lock
when another connection tried to lock the same object. Fixed.
</li><li>
Outer Join: currently, the table order of outer joins is kept, the tables are evaluated left to right
(with the exception of right outer join tables). This is a temporary solution only to solve problems
with join conditions.
</li><li>
Bugfix for SCRIPT: the rights where created before the objects. Fixed.
</li><li>
Implemented function LOCK_TIMEOUT().
</li><li>
Compatibility with DBPool: Support 'holdability' in the Connection methods (however the value it is currently ignored).
</li><li>
Connection.setTypeMap does not throw an exception any more if the type map is empty (null or 0 size).
</li><li>
Issue #102: INSTR('123456','34') should return 2.
</li><li>
Issue #101: A big result set with order by on a column or value that is not in the result list didn't work. Fixed.
</li><li>
Referential integrity: cascade didn't work for more than one level when it was self-referencing. Fixed.
</li><li>
New parameter charsetName in RunScript.execute.
New option CHARSET in the RUNSCRIPT command.
</li><li>
A backslash in the database URL (database name) didn't work, fixed. But a backslash in the settings part of the URL
is used as an escape for ';', that means database URLs of the form jdbc:h2:test;SETTING=a\;b\;c
are possible (but hopefully this is never required).
</li><li>
Added an interface SimpleRowSource so that an application / tool can create a dynamic result set
that produces rows on demand (for streaming result sets).
</li><li>
Foreign key constraints with different data types on the references / referencing column did not work. Fixed.
</li><li>
Fixed a problems that lead to a database (index file) corruption when killing the process.
Added a test case.
</li><li>
If a user has SELECT privileges for a view, he can now retrieve the data even if he does not have
privileges for the underlying table(s).
</li><li>
New system table INFORMATION_SCHEMA.CONSTRAINT that returns information about constraints
(mainly for check and unique constraints; for referential constraints see CROSS_REFERENCES).
</li><li>
Alter table alter column: the data type of a column could not be changed, and columns could not be dropped
if there was a constraint on the table. Now the data type can always be changed.
Alter table drop column: now the column can be dropped if it is not part of a constraint.
</li><li>
Views can now be 'invalid', for example if the table does not exist.
New syntax CREATE FORCE VIEW.
New column STATUS (invalid / valid) in INFORMATION_SCHEMA.VIEWS table.
New SQL statement ALTER VIEW viewName RECOMPILE.
</li><li>
Issue #100: Altering a table with a self-referencing constraint didn't work. Fixed.
</li><li>
Support for IDENTITY(start, increment) as in CREATE TABLE TEST(ID BIGINT NOT NULL IDENTITY(10, 5));
</li><li>
Recovery did not always work correctly when the log file was switched.
Added tests for this use case.
</li><li>
Bugfix for CREATE ROLE IF NOT EXISTS X.
</li><li>
Removed support for LZMA. It was slow, and code coverage could not run.
If somebody needs this algorithm, it is better to add an open compression API.
</li><li>
Starting a server took 1 second before, now it takes only about 100 ms.
</li><li>
Server mode: If a parameter was not set in a prepared statement when using the server mode,
the connection to the server broke. Fixed.
</li><li>
Referential integrity: If there was a unique index with fewer columns than the foreign key on the child table,
it did not work. Fixed.
</li><li>
Javadoc / Doclet: ResultSet.getBytes was documented to return byte instead of byte[]. Fixed.
</li><li>
New setting RECOVER in the database URL (jdbc:h2:test;RECOVER=1) to open corrupted databases
(wrong checksum, corrupted index file or summary).
</li><li>
Bugfix: In server mode, big scrollable result sets didn't work. Fixed.
</li><li>
MERGE: instead of delete-insert, now use update-[insert]. This solves problems with foreign key constraints.
</li><li>
Bugfix for LIKE: A null pointer exception was thrown for WHERE NAME LIKE CAST(? AS VARCHAR).
</li><li>
Join optimization: expressions are now evaluated as early as possible, avoiding unnecessary lookups.
</li><li>
ResultSetMetaData.isAutoIncrement is implemented.
</li><li>
SCRIPT: STRINGENCODE instead of STRINGDECODE was used. The implementation
of STRINGDECODE was not correct for Unicode characters > 127. Fixed.
</li><li>
Documentation: [[NOT] NULL] instead of [NOT [NULL]]
</li><li>
Because of the REAL support, databases are not compatible with the old version.
Backup (with the old version), replace 'STRINGENCODE' in the script with
'STRINGDECODE', and restore is required to upgrade to the new version.
</li><li>
Support for REAL data type (Java 'float'; so far the DOUBLE type was used internally).
</li></ul>
<h3>Version 0.9 / 2006-04-20</h3><ul>
<li>
Performance improvement for AND, OR, IFNULL, CASEWHEN, CASE, COALESCE and ARRAY_GET:
only the necessary parameters / operators are evaluated.
</li><li>
Bugfix for CASEWHEN: data type of return value was not evaluated, and this was a problem
when using prepared statements like this: WHERE CASEWHEN(ID &lt; 10, A, B)=?
</li><li>
New function DATABASE_PATH to retrieve the path and file name of a database.
</li><li>
Made the code more modular. New ant target: jarClient to compile the JDBC driver and the classes used
to remotely connect to a H2 server.
</li><li>
JdbcDataSourceFactory constructor is now public.
</li><li>
SET THROTTLE allows to throttle down the resource usage of a connection.
After each 50ms, the session sleeps for the specified amount of time.
</li><li>
ALTER TABLE ALTER COLUMN for a table that was referenced by another table didn't work. Fixed.
</li><li>
If index log is disabled (the default), index changes are flushed automatically each second
if the index was not changed. Like this index rebuilding is only required
(after a unexpected program termination, like power off) for indexes that
where recently changed.
</li><li>
Bugfix: ids of large objects (LOBs) are now correctly reserved when opening the database.
This improves the performance when using many LOBs.
</li><li>
Replaced log_index=1 with log=2. There are 3 log options now: 0 (disabled), 1 (default), and 2 (log index changes as well).
</li><li>
Got rid of the summary (.sum.db) file. The data is now stored in the log file instead.
</li><li>
If the application stopped without closing all connections (for example calling System.exit),
some committed transactions may have not been written to disk. Fixed.
</li><li>
Bugfix if index changes where logged (default is off): not all changes where logged, in some situations recovery would not work fast
(but no data loss as the indexes can be rebuilt; just slower database opening). Fixed.
</li><li>
DatabaseMetaData.getTypeInfo: compatibility with HSQLDB improved.
</li><li>
Parser: quoted keywords where not allowed as table or column names. Fixed.
</li><li>
An exception was thrown in FileStoreInputStream when reading 0 bytes. But this is allowed according to the specs.
</li><li>
Performance of LIMIT was slow when the result set was very big and the number of rows was small. Fixed
</li><li>
The RunScript tool (org.h2.tools.RunScript) now uses the same algorithm to parse scripts.
</li><li>
Bugfix: it was allowed to use jdbc:h2:test;database_event_listener=Test,
but only for the first connection. Now, the class name must be quoted in all cases:
jdbc:h2:test;database_event_listener='Test'
</li><li>
Implemented SET LOG 0 to disable logging for improved performance (about twice as fast).
</li><li>
Implemented TRUNCATE TABLE.
</li><li>
SCRIPT: New option 'DROP' to drop tables and views before creating them.
</li><li>
Chinese translation updates.
</li><li>
It was not possible to create views on system tables. Fixed.
</li><li>
If a database can't be opened because there is a bug in the startup information
(which is basically the SQL script of CREATE statements), then the database can now be opened
by specifying a database event listener.
</li><li>
Parser / MySQL compatibility: in the last release, for MySQL compatibility, this syntax was supported:
CREATE TABLE TEST(ID INT, INDEX(ID), KEY(ID));
But that means INDEX and KEY can't be used as column names.
Changed the parser so that this syntax is only supported in MySQL mode.
</li><li>
Optimizer: now chose a plan with index even if there are no or only a few rows in a table.
To avoid doing a table scan when no rows are in the table at prepare time, and many rows at execution time.
Using a row count offset of 1000.
</li><li>
MERGE: new SQL command to insert or update a row. Sometimes this is called UPSERT for update or insert.
The grammar is a lot simpler than what Oracle supports.
</li><li>
SCRIPT: now the CREATE INDEX statements are after the INSERT statements, to improve performance.
</li><li>
Console: Bugfix for newline in a view definition.
</li><li>
Bugfix for CONCAT with more than 2 parameters.
</li></ul>
<h3>Version 0.9 / 2006-04-09</h3><ul>
<li>
Bugfix for VARCHAR_IGNORECASE.
</li><li>
Open database: Improved the performance for opening a database if it was not closed correctly before
(due to abnormal program termination or power failure).
</li><li>
Compact database: Added a sample application and documented how to do this.
</li><li>
Bugfix: the SCRIPT command created insert statements for views. Removed.
</li><li>
Implemented a way to shutdown a TCP server.
From command line, run: java org.h2.tools.Server -tcpShutdown tcp://localhost:9092
</li><li>
LOG_INDEX: new option in the database URL to log changes to the index file. This is allows fast recovery
from system crash for big databases. Sample URL: jdbc:h2:test;LOG_INDEX=1
</li><li>
Bugfix: using a low MAX_MEMORY_ROWS didn't work for GROUP BY queries with more groups. Fixed.
</li><li>
Improved the trigger API (provide a Connection to the Java function) and added a sample application.
</li><li>
Support setting parameters in the statement:
INSERT INTO TEST VALUES(?, ?) {1: 5000, 2: 'This is a test'}.
However, this feature is not documented yet as it's not clear if this is the final syntax.
Currently, used in the trace feature to create sql script files instead of Java class files
(in many cases, the classes just get too big and can't be compiled).
</li><li>
Support multiline statement in RUNSCRIPT.
</li><li>
Improved parser performance. Re-parsing of statements is avoided now if the database schema didn't change.
This should improve the performance for Hibernate, when many CREATE / DROP statements are executed.
</li><li>
Performance improvement for LIMIT when using simple queries without ORDER BY.
</li><li>
Improve compression ratio and performance for LZF.
</li><li>
Improve performance for Connection.getReadOnly() (Hibernate calls it a lot).
</li><li>
Server mode: Increased the socket buffer size to 64 KB. This should help performance
for medium size and bigger result sets. May thanks to James Devenish again!
</li><li>
Support for IPv6 addresses in JDBC URLs. RFC 2732 format is '[a:b:c:d:e:f:g:h]' or '[a:b:c:d:e:f:g:h]:port'.
May thanks to James Devenish
</li><li>
Bugfix for Linked Tables: The table definition was not stored correctly (with ''driver'' instead of 'driver' and so on). Fixed.
This was found by 'junheng'
</li><li>
Chinese localization of the H2 Console.
</li><li>
Bugfix: The connection was automatically closed sometimes when using a function with a connection parameter.
</li><li>
Bugfix in the Console: Auto-refresh of the object list for DROP / ALTER / CREATE statements, updatable result sets.
</li><li>
Improved support for MySQL syntax: ` is the same as ", except that the identifiers are converter to upper case.
Added support for special cases of MySQL CREATE TABLE syntax.
</li><li>
Java Functions: functions can now return ResultSet.
The SQL statement CALL GET_RESULT_SET(123)
then returns the result set created by the function.
Implemented a simple result set / result set meta data class that can be used
to create result sets in an application from scratch.
</li><li>
Web Server: the command line options where ignored. Fixed.
Renamed the options 'httpAllowOthers', 'httpPort', 'httpSSL' to 'web...'.
</li><li>
Oracle compatibility: CASE...END [CASE] (the last word is new).
Oracle compatibility: Date addition/subtraction: SYSDATE+1, (SYSDATE-1)-SYSDATE and so on.
Limited support for old Oracle outer join syntax (single column outer join, (+) must be on the far right).
This works:
CREATE TABLE Customers(CustomerID int); CREATE TABLE Orders(CustomerID int);
INSERT INTO Customers VALUES(1), (2), (3); INSERT INTO Orders VALUES(1), (3);
SELECT * FROM Customers LEFT OUTER JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
SELECT * FROM Customers, Orders WHERE Customers.CustomerID = Orders.CustomerID(+);
</li></ul>
<h3>Version 0.9 / 2006-04-23</h3><ul>
<li>
Improved Hibernate Dialect for SQuirreL DB Copy.
</li><li>
Support for UPDATE TEST SET (column [,...])=(select | expr [,...]').
For Compiere compatibility.
</li><li>
Bugfixes for SCRIPT: write sequences before tables; write CLOB and BLOB data.
</li><li>
Added PolePosition benchmark results.
</li><li>
Bugfix in the server implementation: free up the memory. The new implementation does not rely on finalizers
any more. Removed finalizers, this improves the performance.
</li><li>
Implemented CROSS JOIN and NATURAL JOIN.
</li><li>
New keywords for join syntax: CROSS, NATURAL, FULL. However full outer join is not supported yet.
</li><li>
Improved documentation of the tools (Server port and so on).
</li><li>
The schema name of constraints is now automatically set to the table.
</li><li>
Compatibility for Derby style column level constraints.
</li><li>
New command ANALYZE to update the selectivity statistics.
New command ALTER TABLE ALTER COLUMN SELECTIVITY to manually set the selectivity of a column.
</li><li>
Allow a java.sql.Connection parameter in the Java function (as in HSQLDB).
</li><li>
Optimizer improvements.
</li><li>
Implemented SET DB_CLOSE_DELAY numberOfSeconds to be able to delay, or disable
(-1) closing the database. Default is still 0 (close database when closing the last connection).
</li><li>
After finding that HSQLDB was faster in the PolePosition test, and unsuccessful tries to
improve the performance of H2 even more, the reason is finally found: H2 always closed
the database when closing the last connection, but HSQLDB leaves the database open.
This also explains OutOfMemory problems when testing multiple databases with PolePosition.
But leaving the database open is a useful feature for some applications.
</li><li>
New system table CROSS_REFERENCES.
Implemented DatabaseMetaData.getImportedKeys, getExportedKeys, getCrossReferences.
</li><li>
Performance improvements in the binary tree and other places.
</li><li>
A new aggregate function SELECTIVITY is implemented. It calculates the selectivity of a column
by counting the distinct rows, but only up to 10000 values are kept in memory.
SELECT SELECTIVITY(ID), SELECTIVITY(NAME) FROM TEST LIMIT 1 SAMPLE_SIZE 10000 scans only 10000 rows.
</li><li>
A new option SAMPLE_SIZE is added to the LIMIT clause to specify the number of rows to scan for a aggregated
query.
</li><li>
Bugfixes for temporary tables. Implemented ON COMMIT DROP / DELETE ROWS, but
not documented because it is only here for compatibility, and I would advise against using it
because it is not available in most databases.
</li><li>
Improved parser performance and memory usage.
</li></ul>
<h3>Version 0.9 / 2005-12-13</h3><ul>
<li>
First public release.
......@@ -1497,6 +1093,7 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Sequence: add features [NO] MINVALUE, MAXVALUE, CACHE, CYCLE
</li><li>Allow execution time prepare for SELECT * FROM CSVREAD(?, 'columnNameString')
</li><li>Support multiple directories (on different hard drives) for the same database
</li><li>Server protocol: use challenge response authentication, but client sends hash(user+password) encrypted with response
</li></ul>
<h3>Not Planned</h3>
......
......@@ -162,7 +162,6 @@ public class Parser {
private boolean rightsChecked;
private boolean recompileAlways;
private ObjectArray indexedParameterList;
private int tempViewId;
public Parser(Session session) {
this.session = session;
......@@ -741,8 +740,14 @@ public class Parser {
if(isToken("SELECT") || isToken("FROM")) {
Query query = parseQueryWithParams();
String querySQL = query.getSQL();
int id = tempViewId++;
table = new TableView(mainSchema, 0, "TEMP_VIEW_" + id, querySQL, query.getParameters(), null, session, false);
String tempViewName = session.getNextTempViewName();
table = new TableView(mainSchema, 0, tempViewName, querySQL, query.getParameters(), null, session, false);
int testing;
table.setOnCommitDrop(true);
//this.recompileAlways = true;
session.addLocalTempTable(table);
read(")");
} else {
TableFilter top = readTableFilter(fromOuter);
......
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.command;
import org.h2.engine.Session;
public class ParserInt extends Parser {
public ParserInt(Session session) {
super(session);
}
}
......@@ -8,6 +8,7 @@ import java.sql.SQLException;
import org.h2.command.Parser;
import org.h2.command.Prepared;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.Parameter;
......@@ -281,7 +282,12 @@ public class ConstraintReferential extends Constraint {
private boolean found(Session session, Index index, SearchRow check) throws SQLException {
Cursor cursor = index.find(session, check, check);
while(cursor.next()) {
Row found = cursor.get();
SearchRow found;
if(Constants.INDEX_LOOKUP_NEW) {
found = cursor.getSearchRow();
} else {
found = cursor.get();
}
Column[] cols = index.getColumns();
boolean allEqual = true;
for(int i=0; i<columns.length && i<cols.length; i++) {
......
......@@ -18,13 +18,13 @@ package org.h2.engine;
* - Compile with JDK 1.3, 1.4, 1.5 and 1.6:
* set path=C:\jdk1.3.1_19\bin;%PATH%
* set JAVA_HOME=C:\jdk1.3.1_19
* ant codeswitch_jdk13
* ant codeswitchJdk13
* ant compile
* set path=C:\Programme\Java\jdk1.6.0\bin;%PATH%
* set JAVA_HOME=C:\Programme\Java\jdk1.6.0
* ant codeswitch_jdk16
* ant codeswitchJdk16
* ant compile
* ant codeswitch_jdk14
* ant codeswitchJdk14
*
* - Change FAQ (next release planned, known bugs)
* - Check version, change build number in Constants.java and ant-build.properties
......@@ -245,8 +245,9 @@ public class Constants {
public static final int OBJECT_CACHE_MAX_PER_ELEMENT_SIZE = getIntSetting("h2.objectCacheMaxPerElementSize", 4096);
public static final String CLIENT_TRACE_DIRECTORY = getStringSetting("h2.clientTraceDirectory", "trace.db/");
public static int MAX_FILE_RETRY = Math.max(1, getIntSetting("h2.maxFileRetry", 16));
public static boolean INDEX_NEW = getBooleanSetting("h2.indexNew", false);
public static boolean LOB_CLOSE_BETWEEN_READS = getBooleanSetting("h2.lobCloseBetweenReads", false);
public static boolean INDEX_OLD = getBooleanSetting("h2.indexOld", false);
public static final boolean INDEX_LOOKUP_NEW = getBooleanSetting("h2.indexLookupNew", true);
public static boolean getBooleanSetting(String name, boolean defaultValue) {
String s = System.getProperty(name);
......
......@@ -832,6 +832,16 @@ public class Database implements DataHandler {
}
private void closeOpenFilesAndUnlock() throws SQLException {
int testing;
if (persistent && lock == null && fileLockMethod != FileLock.LOCK_NO) {
// everything already closed (maybe in checkPowerOff)
// don't delete temp files in this case because
// the database could be open now (even from within another process)
System.out.println("lock = null!");
// return;
}
if (log != null) {
stopWriter();
log.close();
......@@ -843,9 +853,9 @@ public class Database implements DataHandler {
systemSession.close();
systemSession = null;
}
if (lock != null) {
lock.unlock();
lock = null;
if(lock != null) {
lock.unlock();
lock = null;
}
}
......
......@@ -60,6 +60,7 @@ public class Session implements SessionInterface {
private String currentSchemaName;
private String traceModuleName;
private HashSet unlinkSet;
private int tempViewIndex;
public Table findLocalTempTable(String name) {
Table t = null;
......@@ -89,7 +90,6 @@ public class Session implements SessionInterface {
}
public void addLocalTempTable(Table table) throws SQLException {
cleanTempTables(false);
if(localTempTables == null) {
localTempTables = new HashMap();
}
......@@ -480,5 +480,9 @@ public class Session implements SessionInterface {
unlinkSet.remove(v);
}
}
public String getNextTempViewName() {
return "TEMP_VIEW_" + tempViewIndex++;
}
}
......@@ -17,7 +17,8 @@ import org.h2.result.SearchRow;
public class BtreeCursor implements Cursor {
private BtreeIndex index;
private BtreePosition top;
private Row current;
private SearchRow currentSearchRow;
private Row currentRow;
private boolean first;
private SearchRow last;
......@@ -47,30 +48,39 @@ public class BtreeCursor implements Cursor {
return t;
}
void setCurrentRow(int pos) throws SQLException {
current = pos == POS_NO_ROW ? null : index.getRow(pos);
void setCurrentRow(SearchRow searchRow) throws SQLException {
this.currentSearchRow = searchRow;
currentRow = null;
}
public Row get() throws SQLException {
return current;
if(currentRow == null && currentSearchRow != null) {
currentRow = index.getRow(currentSearchRow.getPos());
}
return currentRow;
}
public SearchRow getSearchRow() throws SQLException {
return currentSearchRow;
}
public int getPos() {
return current.getPos();
return currentSearchRow.getPos();
}
public boolean next() throws SQLException {
if (first) {
first = false;
return current != null;
return currentSearchRow != null;
}
top.page.next(this, top.position);
if(current != null && last != null) {
if (index.compareRows(current, last) > 0) {
current = null;
if(currentSearchRow != null && last != null) {
if (index.compareRows(currentSearchRow, last) > 0) {
currentSearchRow = null;
currentRow = null;
}
}
return current != null;
return currentSearchRow != null;
}
}
......@@ -183,7 +183,7 @@ public class BtreeIndex extends Index implements RecordReader {
} else {
BtreeCursor cursor = new BtreeCursor(this, last);
if (!root.findFirst(cursor, first)) {
cursor.setCurrentRow(Cursor.POS_NO_ROW);
cursor.setCurrentRow(null);
}
return cursor;
}
......
......@@ -160,7 +160,7 @@ public class BtreeLeaf extends BtreePage {
}
cursor.push(this, l);
SearchRow row = (SearchRow) pageData.get(l);
cursor.setCurrentRow(row.getPos());
cursor.setCurrentRow(row);
return true;
}
......@@ -168,7 +168,7 @@ public class BtreeLeaf extends BtreePage {
i++;
if (i < pageData.size()) {
SearchRow r = (SearchRow) pageData.get(i);
cursor.setCurrentRow(r.getPos());
cursor.setCurrentRow(r);
cursor.setStackPosition(i);
return;
}
......@@ -186,13 +186,13 @@ public class BtreeLeaf extends BtreePage {
}
cursor.push(this, 0);
SearchRow row = (SearchRow) pageData.get(0);
cursor.setCurrentRow(row.getPos());
cursor.setCurrentRow(row);
}
private void nextUpper(BtreeCursor cursor) throws SQLException {
BtreePosition upper = cursor.pop();
if (upper == null) {
cursor.setCurrentRow(Cursor.POS_NO_ROW);
cursor.setCurrentRow(null);
} else {
cursor.push(upper.page, upper.position);
upper.page.next(cursor, upper.position);
......
......@@ -268,7 +268,7 @@ public class BtreeNode extends BtreePage {
cursor.pop();
BtreePosition upper = cursor.pop();
if (upper == null) {
cursor.setCurrentRow(Cursor.POS_NO_ROW);
cursor.setCurrentRow(null);
} else {
cursor.push(upper.page, upper.position);
upper.page.next(cursor, upper.position);
......
......@@ -7,6 +7,7 @@ package org.h2.index;
import java.sql.SQLException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
public interface Cursor {
......@@ -14,6 +15,7 @@ public interface Cursor {
int POS_NO_ROW = -1;
Row get() throws SQLException;
SearchRow getSearchRow() throws SQLException;
int getPos();
boolean next() throws SQLException;
......
......@@ -9,6 +9,7 @@ import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.result.SearchRow;
public class FunctionCursor implements Cursor {
......@@ -23,6 +24,10 @@ public class FunctionCursor implements Cursor {
return row;
}
public SearchRow getSearchRow() throws SQLException {
return row;
}
public int getPos() {
throw Message.getInternalError();
}
......
......@@ -4,7 +4,10 @@
*/
package org.h2.index;
import java.sql.SQLException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* @author Thomas
......@@ -21,6 +24,10 @@ public class HashCursor implements Cursor {
return row;
}
public SearchRow getSearchRow() throws SQLException {
return row;
}
public int getPos() {
return row == null ? -1 : row.getPos();
}
......
......@@ -4,7 +4,10 @@
*/
package org.h2.index;
import java.sql.SQLException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* @author Thomas
......@@ -21,6 +24,10 @@ public class LinearHashCursor implements Cursor {
return row;
}
public SearchRow getSearchRow() throws SQLException {
return row;
}
public int getPos() {
return row == null ? -1 : row.getPos();
}
......
......@@ -10,6 +10,7 @@ import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.value.DataType;
......@@ -36,6 +37,10 @@ public class LinkedCursor implements Cursor {
return current;
}
public SearchRow getSearchRow() throws SQLException {
return current;
}
public int getPos() {
throw Message.getInternalError();
}
......
......@@ -8,6 +8,7 @@ import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.util.ObjectArray;
......@@ -29,6 +30,10 @@ public class MetaCursor implements Cursor {
return current;
}
public SearchRow getSearchRow() throws SQLException {
return current;
}
public int getPos() {
throw Message.getInternalError();
}
......
......@@ -8,6 +8,7 @@ import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.value.Value;
import org.h2.value.ValueLong;
......@@ -27,6 +28,10 @@ public class RangeCursor implements Cursor {
public Row get() {
return currentRow;
}
public SearchRow getSearchRow() throws SQLException {
return currentRow;
}
public int getPos() {
throw Message.getInternalError();
......
......@@ -7,6 +7,7 @@ package org.h2.index;
import java.sql.SQLException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
......@@ -24,7 +25,11 @@ public class ScanCursor implements Cursor {
public Row get() {
return row;
}
public SearchRow getSearchRow() {
return row;
}
public int getPos() {
return row == null ? -1 : row.getPos();
}
......
......@@ -28,6 +28,10 @@ public class TreeCursor implements Cursor {
return node == null ? null : node.row;
}
public SearchRow getSearchRow() {
return get();
}
public int getPos() {
return node == null ? -1 : node.row.getPos();
}
......
......@@ -9,6 +9,7 @@ import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.Table;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -28,6 +29,10 @@ public class ViewCursor implements Cursor {
return current;
}
public SearchRow getSearchRow() {
return current;
}
public int getPos() {
throw Message.getInternalError();
}
......
......@@ -7,12 +7,10 @@ package org.h2.index;
import java.sql.SQLException;
import org.h2.command.dml.Query;
import org.h2.command.dml.SelectUnion;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
import org.h2.expression.Parameter;
import org.h2.expression.ValueExpression;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
......@@ -28,18 +26,11 @@ public class ViewIndex extends Index {
private String querySQL;
private ObjectArray originalParameters;
private Parameter[] params;
private SmallLRUCache costCache = new SmallLRUCache(Constants.VIEW_INDEX_CACHE_SIZE);
private Value[] lastParameters;
private long lastEvaluated;
private LocalResult lastResult;
private boolean recursive;
private int recurseLevel;
private LocalResult recursiveResult;
private int[] masks;
private String planSQL;
private Query query;
public ViewIndex(TableView view, String querySQL, ObjectArray originalParameters, boolean recursive) {
super(view, 0, null, null, IndexType.createNonUnique(false));
......@@ -47,17 +38,22 @@ public class ViewIndex extends Index {
this.originalParameters = originalParameters;
this.recursive = recursive;
columns = new Column[0];
params = new Parameter[0];
}
public ViewIndex(TableView view, ViewIndex index, Session session, int[] masks) throws SQLException {
super(view, 0, null, null, IndexType.createNonUnique(false));
int testing;
if(index == null) {
System.out.println("top");
}
this.querySQL = index.querySQL;
this.originalParameters = index.originalParameters;
this.recursive = index.recursive;
this.masks = masks;
columns = new Column[0];
params = new Parameter[0];
planSQL = getQuerySQL(session, masks);
query = getQuery(session, masks);
planSQL = query.getPlanSQL();
}
public String getPlanSQL() {
......@@ -74,57 +70,12 @@ public class ViewIndex extends Index {
public void remove(Session session, Row row) throws SQLException {
throw Message.getUnsupportedException();
}
private int getComparisonType(int mask) {
if((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
return Comparison.EQUAL;
} else if ((mask & IndexCondition.RANGE) == IndexCondition.RANGE) {
// TODO view: how to do range queries?
return Comparison.BIGGER_EQUAL;
} else if ((mask & IndexCondition.START) == IndexCondition.START) {
return Comparison.BIGGER_EQUAL;
} else if ((mask & IndexCondition.END) == IndexCondition.END) {
return Comparison.SMALLER_EQUAL;
}
throw Message.getInternalError("unsupported mask "+mask);
}
private static class CostElement {
long evaluatedAt;
double cost;
}
private String getQuerySQL(Session session, int[] masks) throws SQLException {
if(masks == null) {
return querySQL;
}
Query query = (Query)session.prepare(querySQL, true);
IntArray paramIndex = new IntArray();
for(int i=0; i<masks.length; i++) {
int mask = masks[i];
if(mask == 0) {
continue;
}
paramIndex.add(i);
}
int len = paramIndex.size();
columns = new Column[len];
params = new Parameter[len];
for(int i=0; i<len; i++) {
int idx = paramIndex.get(i);
Column col = table.getColumn(idx);
columns[i] = col;
Parameter param = new Parameter(i);
params[i] = param;
int mask = masks[idx];
int comparisonType = getComparisonType(mask);
query.addGlobalCondition(param, idx, comparisonType);
}
String sql = query.getPlanSQL();
query = (Query)session.prepare(sql, true);
return query.getPlanSQL();
}
public double getCost(Session session, int[] masks) throws SQLException {
IntArray masksArray = new IntArray(masks == null ? new int[0] : masks);
CostElement cachedCost = (CostElement) costCache.get(masksArray);
......@@ -137,7 +88,6 @@ public class ViewIndex extends Index {
Query query = (Query)session.prepare(querySQL, true);
if(masks == null) {
columns = new Column[0];
params = new Parameter[0];
} else {
IntArray paramIndex = new IntArray();
for(int i=0; i<masks.length; i++) {
......@@ -149,25 +99,29 @@ public class ViewIndex extends Index {
}
int len = paramIndex.size();
columns = new Column[len];
params = new Parameter[len];
for(int i=0; i<len; i++) {
int idx = paramIndex.get(i);
Column col = table.getColumn(idx);
columns[i] = col;
Parameter param = new Parameter(0);
params[i] = param;
int mask = masks[idx];
int comparisonType = getComparisonType(mask);
query.addGlobalCondition(param, idx, comparisonType);
if((mask & IndexCondition.EQUALITY) != 0) {
Parameter param = new Parameter(0);
query.addGlobalCondition(param, idx, Comparison.EQUAL);
} else {
if((mask & IndexCondition.START) != 0) {
Parameter param = new Parameter(0);
query.addGlobalCondition(param, idx, Comparison.BIGGER_EQUAL);
}
if((mask & IndexCondition.END) != 0) {
Parameter param = new Parameter(0);
query.addGlobalCondition(param, idx, Comparison.SMALLER_EQUAL);
}
}
}
if(recursive) {
return 10;
}
int testing;
// String sql = query.getSQL();
String sql = query.getPlanSQL();
query = (Query)session.prepare(sql);
}
double cost = query.getCost();
......@@ -179,91 +133,88 @@ public class ViewIndex extends Index {
}
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
Query query = (Query)session.prepare(querySQL, true);
if(recursive) {
SelectUnion union = (SelectUnion) query;
Query left = union.getLeftQuery();
Query right = union.getRightQuery();
LocalResult completeResult;
if(recurseLevel==0) {
LocalResult result = left.query(0);
completeResult = result;
recurseLevel = 1;
result = left.query(0);
while(true) {
recursiveResult = result;
recurseLevel++;
result = right.query(0);
if(result.getRowCount() == 0) {
break;
}
result = right.query(0);
while(result.next()) {
completeResult.addRow(result.currentRow());
}
}
completeResult.done();
recurseLevel=0;
return new ViewCursor(table, completeResult);
} else {
return new ViewCursor(table, recursiveResult);
}
}
ObjectArray paramList = query.getParameters();
for(int i=0; first != null && i<first.getColumnCount(); i++) {
Value v = first.getValue(i);
if(v != null) {
query.addGlobalCondition(ValueExpression.get(v), i, Comparison.BIGGER_EQUAL);
}
}
for(int i=0; last != null && i<last.getColumnCount(); i++) {
Value v = last.getValue(i);
if(v != null) {
query.addGlobalCondition(ValueExpression.get(v), i, Comparison.SMALLER_EQUAL);
}
}
int idx = 0;
for(int i=0; originalParameters != null && i<originalParameters.size(); i++) {
Parameter orig = (Parameter) originalParameters.get(i);
Parameter param = (Parameter) paramList.get(i);
Value value = orig.getValue(session);
Parameter param = (Parameter) paramList.get(idx++);
param.setValue(value);
}
boolean canReuse = first == null && last == null;
long now = session.getDatabase().getModificationDataId();
Value[] params = query.getParameterValues();
if(session.getDatabase().getOptimizeReuseResults()) {
if(lastResult != null && canReuse) {
if(query.sameResultAsLast(session, params, lastParameters, lastEvaluated)) {
lastResult = lastResult.createShallowCopy(session);
if(lastResult != null) {
lastResult.reset();
return new ViewCursor(table, lastResult);
}
int len;
if(first != null) {
len = first.getColumnCount();
} else if(last != null) {
len = last.getColumnCount();
} else {
len = 0;
}
for(int i=0; i < len; i++) {
if(first != null) {
Value v = first.getValue(i);
if(v != null) {
Parameter param = (Parameter) paramList.get(idx++);
param.setValue(v);
}
}
// for equality, only one parameter is used (first == last)
if(last != null && masks[i] != IndexCondition.EQUALITY) {
Value v = last.getValue(i);
if(v != null) {
Parameter param = (Parameter) paramList.get(idx++);
param.setValue(v);
}
}
}
String sql = query.getPlanSQL();
Query q2 = (Query)session.prepare(sql, true);
ObjectArray a2 = q2.getParameters();
for(int i=0; i<a2.size(); i++) {
Parameter param = (Parameter) originalParameters.get(i);
Value value = param.getParamValue();
Parameter p = (Parameter) a2.get(i);
p.setValue(value);
LocalResult result = query.query(0);
return new ViewCursor(table, result);
}
private Query getQuery(Session session, int[] masks) throws SQLException {
Query query = (Query)session.prepare(querySQL, true);
if(masks == null) {
return query;
}
LocalResult result = q2.query(0);
int test;
// query.setSession(session);
// LocalResult result = query.query(0);
if(canReuse) {
lastResult = result;
lastParameters = params;
lastEvaluated = now;
int firstIndexParam = query.getParameters().size();
IntArray paramIndex = new IntArray();
for(int i=0; i<masks.length; i++) {
int mask = masks[i];
if(mask == 0) {
continue;
}
paramIndex.add(i);
if((mask & IndexCondition.RANGE) == IndexCondition.RANGE) {
// two parameters for range queries: >= x AND <= y
paramIndex.add(i);
}
}
return new ViewCursor(table, result);
int len = paramIndex.size();
columns = new Column[len];
for(int i=0; i<len;) {
int idx = paramIndex.get(i);
Column col = table.getColumn(idx);
columns[i] = col;
int mask = masks[idx];
if((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
Parameter param = new Parameter(firstIndexParam + i);
query.addGlobalCondition(param, idx, Comparison.EQUAL);
i++;
} else {
if((mask & IndexCondition.START) == IndexCondition.START) {
Parameter param = new Parameter(firstIndexParam + i);
query.addGlobalCondition(param, idx, Comparison.BIGGER_EQUAL);
i++;
}
if((mask & IndexCondition.END) == IndexCondition.END) {
Parameter param = new Parameter(firstIndexParam + i);
query.addGlobalCondition(param, idx, Comparison.SMALLER_EQUAL);
i++;
}
}
}
String sql = query.getPlanSQL();
query = (Query)session.prepare(sql, true);
return query;
}
public long getCost(int[] masks) throws SQLException {
......@@ -288,7 +239,7 @@ public class ViewIndex extends Index {
public boolean needRebuild() {
return false;
}
public boolean canGetFirstOrLast(boolean first) {
return false;
}
......
......@@ -7,10 +7,12 @@ package org.h2.index;
import java.sql.SQLException;
import org.h2.command.dml.Query;
import org.h2.command.dml.SelectUnion;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
import org.h2.expression.Parameter;
import org.h2.expression.ValueExpression;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
......@@ -22,33 +24,40 @@ import org.h2.util.SmallLRUCache;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
public class ViewIndexNew extends Index {
public class ViewIndexOld extends Index {
private String querySQL;
private ObjectArray originalParameters;
private Parameter[] params;
private SmallLRUCache costCache = new SmallLRUCache(Constants.VIEW_INDEX_CACHE_SIZE);
private Value[] lastParameters;
private long lastEvaluated;
private LocalResult lastResult;
private boolean recursive;
private int[] masks;
private int recurseLevel;
private LocalResult recursiveResult;
private String planSQL;
private Query query;
public ViewIndexNew(TableView view, String querySQL, ObjectArray originalParameters, boolean recursive) {
public ViewIndexOld(TableView view, String querySQL, ObjectArray originalParameters, boolean recursive) {
super(view, 0, null, null, IndexType.createNonUnique(false));
this.querySQL = querySQL;
this.originalParameters = originalParameters;
this.recursive = recursive;
columns = new Column[0];
params = new Parameter[0];
}
public ViewIndexNew(TableView view, ViewIndexNew index, Session session, int[] masks) throws SQLException {
public ViewIndexOld(TableView view, ViewIndexOld index, Session session, int[] masks) throws SQLException {
super(view, 0, null, null, IndexType.createNonUnique(false));
this.querySQL = index.querySQL;
this.originalParameters = index.originalParameters;
this.recursive = index.recursive;
this.masks = masks;
columns = new Column[0];
query = getQuery(session, masks);
planSQL = query.getPlanSQL();
params = new Parameter[0];
planSQL = getQuerySQL(session, masks);
}
public String getPlanSQL() {
......@@ -65,12 +74,57 @@ public class ViewIndexNew extends Index {
public void remove(Session session, Row row) throws SQLException {
throw Message.getUnsupportedException();
}
private int getComparisonType(int mask) {
if((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
return Comparison.EQUAL;
} else if ((mask & IndexCondition.RANGE) == IndexCondition.RANGE) {
// TODO view: how to do range queries?
return Comparison.BIGGER_EQUAL;
} else if ((mask & IndexCondition.START) == IndexCondition.START) {
return Comparison.BIGGER_EQUAL;
} else if ((mask & IndexCondition.END) == IndexCondition.END) {
return Comparison.SMALLER_EQUAL;
}
throw Message.getInternalError("unsupported mask "+mask);
}
private static class CostElement {
long evaluatedAt;
double cost;
}
private String getQuerySQL(Session session, int[] masks) throws SQLException {
if(masks == null) {
return querySQL;
}
Query query = (Query)session.prepare(querySQL, true);
IntArray paramIndex = new IntArray();
for(int i=0; i<masks.length; i++) {
int mask = masks[i];
if(mask == 0) {
continue;
}
paramIndex.add(i);
}
int len = paramIndex.size();
columns = new Column[len];
params = new Parameter[len];
for(int i=0; i<len; i++) {
int idx = paramIndex.get(i);
Column col = table.getColumn(idx);
columns[i] = col;
Parameter param = new Parameter(i);
params[i] = param;
int mask = masks[idx];
int comparisonType = getComparisonType(mask);
query.addGlobalCondition(param, idx, comparisonType);
}
String sql = query.getPlanSQL();
query = (Query)session.prepare(sql, true);
return query.getPlanSQL();
}
public double getCost(Session session, int[] masks) throws SQLException {
IntArray masksArray = new IntArray(masks == null ? new int[0] : masks);
CostElement cachedCost = (CostElement) costCache.get(masksArray);
......@@ -83,6 +137,7 @@ public class ViewIndexNew extends Index {
Query query = (Query)session.prepare(querySQL, true);
if(masks == null) {
columns = new Column[0];
params = new Parameter[0];
} else {
IntArray paramIndex = new IntArray();
for(int i=0; i<masks.length; i++) {
......@@ -94,29 +149,25 @@ public class ViewIndexNew extends Index {
}
int len = paramIndex.size();
columns = new Column[len];
params = new Parameter[len];
for(int i=0; i<len; i++) {
int idx = paramIndex.get(i);
Column col = table.getColumn(idx);
columns[i] = col;
Parameter param = new Parameter(0);
params[i] = param;
int mask = masks[idx];
if((mask & IndexCondition.EQUALITY) != 0) {
Parameter param = new Parameter(0);
query.addGlobalCondition(param, idx, Comparison.EQUAL);
} else {
if((mask & IndexCondition.START) != 0) {
Parameter param = new Parameter(0);
query.addGlobalCondition(param, idx, Comparison.BIGGER_EQUAL);
}
if((mask & IndexCondition.END) != 0) {
Parameter param = new Parameter(0);
query.addGlobalCondition(param, idx, Comparison.SMALLER_EQUAL);
}
}
int comparisonType = getComparisonType(mask);
query.addGlobalCondition(param, idx, comparisonType);
}
if(recursive) {
return 10;
}
int testing;
// String sql = query.getSQL();
String sql = query.getPlanSQL();
query = (Query)session.prepare(sql);
}
double cost = query.getCost();
......@@ -128,80 +179,91 @@ public class ViewIndexNew extends Index {
}
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
Query query = (Query)session.prepare(querySQL, true);
if(recursive) {
SelectUnion union = (SelectUnion) query;
Query left = union.getLeftQuery();
Query right = union.getRightQuery();
LocalResult completeResult;
if(recurseLevel==0) {
LocalResult result = left.query(0);
completeResult = result;
recurseLevel = 1;
result = left.query(0);
while(true) {
recursiveResult = result;
recurseLevel++;
result = right.query(0);
if(result.getRowCount() == 0) {
break;
}
result = right.query(0);
while(result.next()) {
completeResult.addRow(result.currentRow());
}
}
completeResult.done();
recurseLevel=0;
return new ViewCursor(table, completeResult);
} else {
return new ViewCursor(table, recursiveResult);
}
}
ObjectArray paramList = query.getParameters();
int idx = 0;
for(int i=0; first != null && i<first.getColumnCount(); i++) {
Value v = first.getValue(i);
if(v != null) {
query.addGlobalCondition(ValueExpression.get(v), i, Comparison.BIGGER_EQUAL);
}
}
for(int i=0; last != null && i<last.getColumnCount(); i++) {
Value v = last.getValue(i);
if(v != null) {
query.addGlobalCondition(ValueExpression.get(v), i, Comparison.SMALLER_EQUAL);
}
}
for(int i=0; originalParameters != null && i<originalParameters.size(); i++) {
Parameter orig = (Parameter) originalParameters.get(i);
Parameter param = (Parameter) paramList.get(i);
Value value = orig.getValue(session);
Parameter param = (Parameter) paramList.get(idx++);
param.setValue(value);
}
for(int i=0; first != null && i<first.getColumnCount(); i++) {
if(first != null) {
Value v = first.getValue(i);
if(v != null) {
Parameter param = (Parameter) paramList.get(idx++);
param.setValue(v);
}
}
// for equality, only one parameter is used (first == last)
if(masks[i] != IndexCondition.EQUALITY && last != null) {
Value v = last.getValue(i);
if(v != null) {
Parameter param = (Parameter) paramList.get(idx++);
param.setValue(v);
boolean canReuse = first == null && last == null;
long now = session.getDatabase().getModificationDataId();
Value[] params = query.getParameterValues();
if(session.getDatabase().getOptimizeReuseResults()) {
if(lastResult != null && canReuse) {
if(query.sameResultAsLast(session, params, lastParameters, lastEvaluated)) {
lastResult = lastResult.createShallowCopy(session);
if(lastResult != null) {
lastResult.reset();
return new ViewCursor(table, lastResult);
}
}
}
}
LocalResult result = query.query(0);
return new ViewCursor(table, result);
}
private Query getQuery(Session session, int[] masks) throws SQLException {
Query query = (Query)session.prepare(querySQL, true);
if(masks == null) {
return query;
}
int firstIndexParam = query.getParameters().size();
IntArray paramIndex = new IntArray();
for(int i=0; i<masks.length; i++) {
int mask = masks[i];
if(mask == 0) {
continue;
}
paramIndex.add(i);
if((mask & IndexCondition.RANGE) == IndexCondition.RANGE) {
// two parameters for range queries: >= x AND <= y
paramIndex.add(i);
}
String sql = query.getPlanSQL();
Query q2 = (Query)session.prepare(sql, true);
ObjectArray a2 = q2.getParameters();
for(int i=0; i<a2.size(); i++) {
Parameter param = (Parameter) originalParameters.get(i);
Value value = param.getParamValue();
Parameter p = (Parameter) a2.get(i);
p.setValue(value);
}
int len = paramIndex.size();
columns = new Column[len];
for(int i=0; i<len;) {
int idx = paramIndex.get(i);
Column col = table.getColumn(idx);
columns[i] = col;
int mask = masks[idx];
if((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
Parameter param = new Parameter(firstIndexParam + i);
query.addGlobalCondition(param, idx, Comparison.EQUAL);
i++;
} else {
if((mask & IndexCondition.START) == IndexCondition.START) {
Parameter param = new Parameter(firstIndexParam + i);
query.addGlobalCondition(param, idx, Comparison.BIGGER_EQUAL);
i++;
}
if((mask & IndexCondition.END) == IndexCondition.END) {
Parameter param = new Parameter(firstIndexParam + i);
query.addGlobalCondition(param, idx, Comparison.SMALLER_EQUAL);
i++;
}
}
LocalResult result = q2.query(0);
int test;
// query.setSession(session);
// LocalResult result = query.query(0);
if(canReuse) {
lastResult = result;
lastParameters = params;
lastEvaluated = now;
}
String sql = query.getPlanSQL();
query = (Query)session.prepare(sql, true);
return query;
return new ViewCursor(table, result);
}
public long getCost(int[] masks) throws SQLException {
......@@ -226,7 +288,7 @@ public class ViewIndexNew extends Index {
public boolean needRebuild() {
return false;
}
public boolean canGetFirstOrLast(boolean first) {
return false;
}
......
......@@ -1273,8 +1273,8 @@ A
"Other Grammar","Quoted Name","
""anythingExceptDoubleQuote""
","
Quoted names are case sensitive, and can contain spaces.
There is no maximum name length.
Quoted names are case sensitive, and can contain spaces. There is no maximum name length.
Two double quotes can be used to create a single double quote inside an identifier.
","
""FirstName""
"
......@@ -1284,7 +1284,7 @@ There is no maximum name length.
A string starts and ends with a single quote.
Two single quotes can be used to create a single quote inside a string.
","
'Jon''s car'
'John''s car'
"
"Other Grammar","Int","
[- | +] digit [...]
......
......@@ -9,6 +9,7 @@ import org.h2.value.Value;
public class SimpleRowValue implements SearchRow {
private int pos;
private int index;
private int virtualColumnCount;
private Value data;
......@@ -22,14 +23,15 @@ public class SimpleRowValue implements SearchRow {
public int getPos() {
return pos;
}
public Value getValue(int index) {
return data;
public Value getValue(int idx) {
return idx == index ? data : null;
}
public void setPos(int pos) {
this.pos = pos;
}
public void setValue(int idx, Value v) {
index = idx;
data = v;
}
......
......@@ -59,7 +59,6 @@ public class FileStore {
this.mode = mode;
try {
FileUtils.createDirs(name);
// File f = new File(name);
if(FileUtils.exists(name) && !FileUtils.canWrite(name)) {
mode = "r";
this.mode = mode;
......
......@@ -247,11 +247,11 @@ public class Storage {
this.recordCount = recordCount;
}
public void addPage(int i) {
void addPage(int i) {
pages.addValueSorted(i);
}
public void removePage(int i) {
void removePage(int i) {
pages.removeValue(i);
}
......
......@@ -4,6 +4,8 @@
*/
package org.h2.table;
import java.sql.SQLException;
import org.h2.command.dml.Select;
import org.h2.value.Value;
......@@ -12,7 +14,7 @@ public interface ColumnResolver {
String getTableAlias();
Column[] getColumns();
String getSchemaName();
Value getValue(Column column);
Value getValue(Column column) throws SQLException;
TableFilter getTableFilter();
Select getSelect();
......
......@@ -299,10 +299,12 @@ public abstract class Table extends SchemaObject {
}
private void remove(ObjectArray list, DbObject obj) {
int i = list.indexOf(obj);
if(i>=0) {
list.remove(i);
}
if(list != null) {
int i = list.indexOf(obj);
if(i>=0) {
list.remove(i);
}
}
}
public void removeIndex(Index index) {
......
......@@ -44,6 +44,7 @@ public class TableFilter implements ColumnResolver {
// the complete join condition
private Expression joinCondition;
private SearchRow currentSearchRow;
private Row current;
private int state;
......@@ -244,7 +245,9 @@ public class TableFilter implements ColumnResolver {
} else {
scanCount++;
if(cursor.next()) {
current = cursor.get();
currentSearchRow = cursor.getSearchRow();
current = null;
// cursor.get();
state = FOUND;
} else {
state = AFTER_LAST;
......@@ -255,6 +258,7 @@ public class TableFilter implements ColumnResolver {
if(outerJoin && !foundOne) {
state = NULL_ROW;
current = table.getNullRow();
currentSearchRow = current;
} else {
break;
}
......@@ -296,13 +300,17 @@ public class TableFilter implements ColumnResolver {
return Boolean.TRUE.equals(condition.getBooleanValue(session));
}
public Row get() {
public Row get() throws SQLException {
if(current == null && currentSearchRow != null) {
current = cursor.get();
}
return current;
}
public void set(Row current) {
// this is currently only used so that check constraints work - to set the current (new) row
this.current = current;
this.currentSearchRow = current;
}
public String getTableAlias() {
......@@ -506,8 +514,30 @@ public class TableFilter implements ColumnResolver {
return table.getColumns();
}
public Value getValue(Column column) {
return current == null ? null : current.getValue(column.getColumnId());
public Value getValue(Column column) throws SQLException {
if(Constants.INDEX_LOOKUP_NEW) {
if(currentSearchRow == null) {
return null;
}
int columnId = column.getColumnId();
if(current == null) {
Value v = currentSearchRow.getValue(columnId);
if(v != null) {
return v;
}
current = cursor.get();
}
return current.getValue(columnId);
} else {
if(currentSearchRow == null) {
return null;
}
if(current == null) {
current = cursor.get();
}
int columnId = column.getColumnId();
return current.getValue(columnId);
}
}
public TableFilter getTableFilter() {
......
......@@ -13,8 +13,8 @@ import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.index.ViewIndexOld;
import org.h2.index.ViewIndex;
import org.h2.index.ViewIndexNew;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.schema.Schema;
......@@ -28,8 +28,8 @@ public class TableView extends Table {
private ObjectArray tables;
private String[] columnNames;
private Query viewQuery;
private ViewIndex indexOld;
private ViewIndexNew indexNew;
private ViewIndexOld indexOld;
private ViewIndex indexNew;
private boolean recursive;
private SQLException createException;
......@@ -39,10 +39,10 @@ public class TableView extends Table {
this.columnNames = columnNames;
this.recursive = recursive;
int todoRemoveIndexOld;
if(Constants.INDEX_NEW) {
indexNew = new ViewIndexNew(this, querySQL, params, recursive);
if(Constants.INDEX_OLD) {
indexOld = new ViewIndexOld(this, querySQL, params, recursive);
} else {
indexOld = new ViewIndex(this, querySQL, params, recursive);
indexNew = new ViewIndex(this, querySQL, params, recursive);
}
initColumnsAndTables(session);
}
......@@ -95,10 +95,10 @@ public class TableView extends Table {
for(int i=0; i<columnNames.length; i++) {
cols[i] = new Column(columnNames[i], Value.STRING, 255, 0);
}
if(Constants.INDEX_NEW) {
indexNew.setRecursive(true);
} else {
if(Constants.INDEX_OLD) {
indexOld.setRecursive(true);
} else {
indexNew.setRecursive(true);
}
recursive = true;
createException = null;
......@@ -115,12 +115,12 @@ public class TableView extends Table {
public PlanItem getBestPlanItem(Session session, int[] masks) throws SQLException {
PlanItem item = new PlanItem();
Index i2;
if(Constants.INDEX_NEW) {
item.cost = indexNew.getCost(session, masks);
i2 = new ViewIndexNew(this, indexNew, session, masks);
} else {
if(Constants.INDEX_OLD) {
item.cost = indexOld.getCost(session, masks);
i2 = new ViewIndex(this, indexOld, session, masks);
i2 = new ViewIndexOld(this, indexOld, session, masks);
} else {
item.cost = indexNew.getCost(session, masks);
i2 = new ViewIndex(this, indexNew, session, masks);
}
item.setIndex(i2);
return item;
......
......@@ -132,10 +132,10 @@ public class FileUtils {
throw Message.getInternalError("rename file old=new");
}
if(!oldFile.exists()) {
throw Message.getSQLException(Message.FILE_RENAME_FAILED_2, new String[]{oldName, newName}, null);
throw Message.getSQLException(Message.FILE_RENAME_FAILED_2, new String[]{oldName + " (not found)", newName}, null);
}
if(newFile.exists()) {
throw Message.getSQLException(Message.FILE_RENAME_FAILED_2, new String[]{oldName, newName}, null);
throw Message.getSQLException(Message.FILE_RENAME_FAILED_2, new String[]{oldName, newName + " (exists)"}, null);
}
for(int i=0; i<Constants.MAX_FILE_RETRY; i++) {
boolean ok = oldFile.renameTo(newFile);
......@@ -401,20 +401,6 @@ public class FileUtils {
} catch (IOException e) {
throw Message.convertIOException(e, path);
}
// try {
// File[] files = new File(path).listFiles();
// if(files == null) {
// return new String[0];
// }
// String[] list = new String[files.length];
// for(int i=0; i<files.length; i++) {
// list[i] = files[i].getCanonicalPath();
// }
// return list;
// } catch (IOException e) {
// throw Message.convert(e);
// }
}
public static boolean isDirectory(String fileName) {
......
......@@ -96,25 +96,6 @@ public class ValueLob extends Value {
return new ValueLob(type, handler, fileName, tableId, objectId, true, precision, compression);
}
// public static ValueLob createClobFromReader(Reader in, long length) throws SQLException {
// try {
// String s = IOUtils.readStringAndClose(in, (int)length);
// byte[] buff = StringUtils.utf8Encode(s);
// return new ValueLob(CLOB, buff);
// } catch (IOException e) {
// throw Message.convert(e);
// }
// }
// public static ValueLob createBlobFromInputStream(InputStream in, long length) throws SQLException {
// try {
// byte[] buff = IOUtils.readBytesAndClose(in, (int)length);
// return new ValueLob(BLOB, buff);
// } catch (IOException e) {
// throw Message.convert(e);
// }
// }
public static ValueLob createClob(Reader in, long length, DataHandler handler) throws SQLException {
try {
boolean compress = handler.getLobCompressionAlgorithm(Value.CLOB) != null;
......@@ -533,11 +514,6 @@ public class ValueLob extends Value {
return small;
}
// public String getJavaString() {
// // TODO value: maybe use another trick (at least the size should be ok?)
// return StringUtils.quoteJavaString(getSQL());
// }
public int getDisplaySize() {
// TODO display size of lob?
return 40;
......
......@@ -228,7 +228,7 @@ INSERT INTO ITEM VALUES(20,
<ul>
<li>The Console is now translated to Japanese thanks to
IKEMOTO, Masahiro (ikeyan (at) arizona (dot) ne (dot) jp).
</li><li>The database engine can now be compiled with JDK 1.3 using ant codeswitch_jdk13.
</li><li>The database engine can now be compiled with JDK 1.3 using ant codeswitch.
There are still some limitations, and the ant script to build the jar does not work yet.
</li><li>SCRIPT NODATA now writes the row count for each table.
</li><li>Timestamps with timezone information (Z or +/-hh:mm) and dates before year 1
......@@ -273,7 +273,7 @@ INSERT INTO ITEM VALUES(19,
longer the application directory. This can be changed using the system
property h2.clientTraceDirectory.
</li><li>Build: Now using ant-build.properties. The JDK is automatically updated
when using ant codeswitch_...
when using ant codeswitch...
</li><li>Cluster: Now the server can detect if a query is read-only, and in this
case the result is only read from the first cluster node. However, there
is currently no load balancing made to avoid problems with transactions
......
......@@ -93,6 +93,42 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
test.printSystem();
/*
create local temporary table a(id int) on commit drop;
select * from a;
create local temporary table b(id int) on commit drop;
select * from a;
select * from b;
commit;
dropping session temp views doesn't work, why?
create table t1 (i int);
create table t2 (i int);
create table t3 (i int);
select a.i from t1 a inner join (select a.i from t2 a inner join (select i from t3) b on a.i=b.i) b on a.i=b.i;
SELECT A.I FROM T2 A INNER JOIN TEMP_VIEW_0 B WHERE (A.I = B.I) AND (A.I = 1)
create table t1 (i int);
create table t2 (i int);
create table t3 (i int);
select a.i
from t1 a
inner join (
select a.i
from t2 a
inner join (
select i
from t3) b on a.i=b.i
) b on a.i=b.i;
Wre es nicht besser, unabhngig von DB_CLOSE_DELAY eine Datenbank offen
zu halten, solange dafr offene PooledConnections vorhanden sind?
Change documentation and default database for H2 Console: jdbc:h2:~/test
public static final boolean INDEX_LOOKUP_NEW = getBooleanSetting("h2.indexLookupNew", false);
"com.mysql.jdbc.NotUpdatable: Result Set not updatable.This result set must come from a statement that was created with a result set type of ResultSet.CONCUR_UPDATABLE, the query must select only one table, and must select all primary keys from that table. See the JDBC 2.1 API Specification, section 5.6 for more details."
set new console to be the default (still support old)
......
......@@ -13,8 +13,8 @@ public class TestBigResult extends TestBase {
if(config.memory) {
return;
}
testLimitBufferedResult();
testOrderGroup();
testLimitBufferedResult();
}
private void testLimitBufferedResult() throws Exception {
......
......@@ -35,23 +35,23 @@ public class TestLob extends TestBase {
if(config.memory) {
return;
}
testLobNoClose();
testLobTransactions(10);
// testLobNoClose();
// testLobTransactions(10);
testLobTransactions(10000);
testLobRollbackStop();
testLobCopy();
testLobHibernate();
testLobCopy(false);
testLobCopy(true);
testLobCompression(false);
testLobCompression(true);
testManyLobs();
testClob();
testUpdateLob();
testLobReconnect();
testLob(false);
testLob(true);
testJavaObject();
// testLobRollbackStop();
// testLobCopy();
// testLobHibernate();
// testLobCopy(false);
// testLobCopy(true);
// testLobCompression(false);
// testLobCompression(true);
// testManyLobs();
// testClob();
// testUpdateLob();
// testLobReconnect();
// testLob(false);
// testLob(true);
// testJavaObject();
}
private void testLobNoClose() throws Exception {
......@@ -90,6 +90,9 @@ public class TestLob extends TestBase {
if(config.logMode == 0) {
return;
}
int testing;
Constants.LOB_CLOSE_BETWEEN_READS = true;
deleteDb("lob");
Connection conn = reconnect(null);
conn.createStatement().execute("CREATE TABLE TEST(ID IDENTITY, DATA CLOB, DATA2 VARCHAR)");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论