提交 22752adb authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 ded04941
......@@ -4,4 +4,4 @@ if exist bin/org/h2/build/Build.class goto buildOK
if not exist bin mkdir bin
javac -sourcepath src/tools -d bin src/tools/org/h2/build/*.java
:buildOK
java -cp "bin;%JAVA_HOME%/lib/tools.jar;temp" org.h2.build.Build %1 %2 %3 %4 %5
\ No newline at end of file
java -Xmx512m -cp "bin;%JAVA_HOME%/lib/tools.jar;temp" org.h2.build.Build %1 %2 %3 %4 %5
\ No newline at end of file
......@@ -8,4 +8,4 @@ if [ ! -f "bin/org/h2/build/Build.class" ] ; then
fi
javac -sourcepath src/tools -d bin src/tools/org/h2/build/*.java
fi
java -cp "bin:$JAVA_HOME/lib/tools.jar:temp" org.h2.build.Build $@
java -Xms512m -cp "bin:$JAVA_HOME/lib/tools.jar:temp" org.h2.build.Build $@
......@@ -40,7 +40,7 @@ H2 Database Engine
</p>
<p>
For details about changes, see the <a href="history.html">Change Log</a>.
For details about changes, see the <a href="changelog.html">Change Log</a>.
</p>
</div></td></tr></table><!-- analytics --></body></html>
......@@ -37,26 +37,22 @@ Roadmap
<h2>Priority 1</h2>
<ul>
<li>Write more tests and documentation for MVCC (Multi Version Concurrency Control)
</li><li>Row level locking
</li><li>RECOVER=1 should automatically recover, =2 should run the recovery tool if required
</li><li>More tests with MULTI_THREADED=1
</li><li>Test with Spatial DB in a box / JTS (http://docs.codehaus.org/display/GEOS/SpatialDBBox)
</li><li>Optimization: result set caching (like MySQL)
</li><li>Server side cursors
</li><li>Row level locking
</li><li>Long running queries / errors / trace system table
</li><li>Migrate database tool (also from other database engines)
</li><li>Shutdown compact
</li><li>Document server mode, embedded mode, web app mode, dual mode (server+embedded)
</li><li>Updatable result sets: DatabaseMetaData.ownUpdatesAreVisible = true (for insert, delete, update) Simple solution: automatically calls 'refresh' when the result was changed. Compare with other databases.
</li></ul>
<h2>Priority 2</h2>
<ul>
<li>Automatic mode: jdbc:h2:auto: (embedded mode if possible, if not use server mode). Keep the server running until all have disconnected.
</li><li>Support OSGi: http://oscar-osgi.sourceforge.net, http://incubator.apache.org/felix/index.html
</li><li>Better space re-use in the files after deleting data (shrink the files)
</li><li>Shrink the data file without closing the database (if the end of the file is empty)
</li><li>ParameterMetaData should return correct data type where possible (INSERT, UPDATE; supported by PostgreSQL, Derby, HSQLDB)
</li><li>Better space re-use in the files after deleting data: shrink the data file without closing the database (if the end of the file is empty)
</li><li>Pluggable tracing system
</li><li>Full outer joins
</li><li>Procedural language / script language (Javascript)
......@@ -99,6 +95,7 @@ Roadmap
</li><li>Comparison: pluggable sort order: natural sort
</li><li>Count index range query (count(*) where id between 10 and 20)
</li><li>Eclipse plugin
</li><li>Asynchronous queries to support publish/subscribe: SELECT ... FOR READ WAIT [maxMillisToWait]
</li><li>iReport to support H2
</li><li>Implement missing JDBC API (CallableStatement,...)
</li><li>Compression of the cache
......@@ -129,7 +126,6 @@ Roadmap
</li><li>Profiler option or profiling tool to find long running and often repeated queries (using DatabaseEventListener API)
</li><li>Allow custom settings (@PATH for RUNSCRIPT for example)
</li><li>EXE file: maybe use http://jsmooth.sourceforge.net
</li><li>SELECT ... FOR READ WAIT [maxMillisToWait]
</li><li>Automatically delete the index file if opening it fails
</li><li>Performance: Automatically build in-memory indexes if the whole table is in memory
</li><li>H2 Console: The webclient could support more features like phpMyAdmin.
......@@ -233,8 +229,6 @@ Roadmap
</li><li>Autocomplete: schema support: "Other Grammar","Table Expression","{[schemaName.]tableName | (select)} [[AS] newTableAlias]
</li><li>Functions: options readonly, deterministic (pure, always return the same value)
</li><li>Document FTP server, including -ftpTask option to execute / kill remote processes
</li><li>Add jdbcx to the javadocs
</li><li>Delay reading the row if data is not required
</li><li>Eliminate undo log records if stored on disk (just one pointer per block, not per record)
</li><li>Feature matrix like in <a href="http://www.inetsoftware.de/products/jdbc/mssql/features/default.asp">i-net software</a>.
</li><li>Updatable result set on table without primary key or unique index
......@@ -300,7 +294,6 @@ Roadmap
</li><li>Access rights: Finer grained access control (grant access for specific functions)
</li><li>Support N'text'
</li><li>Support SCOPE_IDENTITY() to avoid problems when inserting rows in a trigger
</li><li>Support DESCRIBE like MySQL or Oracle (DESC|DESCRIBE {[schema.]object[@connect_identifier]})
</li><li>Set a connection read only (Connection.setReadOnly)
</li><li>In MySQL mode, for AUTO_INCREMENT columns, don't set the primary key
</li><li>Use JDK 1.4 file locking to create the lock file (but not yet by default); writing a system property to detect concurrent access from the same VM (different classloaders).
......@@ -398,7 +391,7 @@ Roadmap
</li><li>Javadocs: add @author tags.
</li><li>SET LOG_SYSTEM {NATIVE|LOG4J|COMMONS|DRIVER_MANAGER}
</li><li>Fluent API for tools: Server.createTcpServer().setPort(9081).setPassword(password).start();
</li><li>MySQL compatibility: SHOW TABLES, DESCRIBE TEST (then remove from Shell)
</li><li>MySQL compatibility: real SQL statements for SHOW TABLES, DESCRIBE TEST (then remove from Shell)
</li><li>Use a default delay of 1 second before closing a database.
</li><li>Maven: upload source code and javadocs as well.
</li><li>Write (log) to system table before adding to internal data structures.
......@@ -411,6 +404,7 @@ Roadmap
</li><li>MySQL compatibility: SELECT @variable := x FROM SYSTEM_RANGE(1, 50);
</li><li>Oracle compatibility: support NLS_DATE_FORMAT.
</li><li>Support flashback queries as in Oracle.
</li><li>Import / Export of fixed with text files.
</li></ul>
<h2>Not Planned</h2>
......
......@@ -10,6 +10,7 @@ import java.io.IOException;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.SessionRemote;
import org.h2.expression.ParameterInterface;
import org.h2.expression.ParameterRemote;
......@@ -42,28 +43,43 @@ public class CommandRemote implements CommandInterface {
trace = session.getTrace();
this.sql = sql;
parameters = new ObjectArray();
prepare(session);
for (int i = 0; i < paramCount; i++) {
parameters.add(new ParameterRemote(i));
}
prepare(session, true);
// set session late because prepare might fail - in this case we don't
// need to close the object
this.session = session;
this.fetchSize = fetchSize;
}
private void prepare(SessionRemote session) throws SQLException {
private void prepare(SessionRemote session, boolean createParams) throws SQLException {
id = session.getNextId();
paramCount = 0;
boolean readParams = session.getClientVersion() >= Constants.TCP_DRIVER_VERSION_6;
for (int i = 0; i < transferList.size(); i++) {
try {
Transfer transfer = (Transfer) transferList.get(i);
session.traceOperation("SESSION_PREPARE", id);
transfer.writeInt(SessionRemote.SESSION_PREPARE).writeInt(id).writeString(sql);
if (readParams) {
session.traceOperation("SESSION_PREPARE_READ_PARAMS", id);
transfer.writeInt(SessionRemote.SESSION_PREPARE_READ_PARAMS).writeInt(id).writeString(sql);
} else {
session.traceOperation("SESSION_PREPARE", id);
transfer.writeInt(SessionRemote.SESSION_PREPARE).writeInt(id).writeString(sql);
}
session.done(transfer);
isQuery = transfer.readBoolean();
readonly = transfer.readBoolean();
paramCount = transfer.readInt();
if (createParams) {
parameters.clear();
for (int j = 0; j < paramCount; j++) {
if (readParams) {
ParameterRemote p = new ParameterRemote(j);
p.read(transfer);
parameters.add(p);
} else {
parameters.add(new ParameterRemote(j));
}
}
}
} catch (IOException e) {
session.removeServer(i--);
}
......@@ -86,7 +102,7 @@ public class CommandRemote implements CommandInterface {
}
if (id <= session.getCurrentId() - SysProperties.SERVER_CACHED_OBJECTS) {
// object is too old - we need to prepare again
prepare(session);
prepare(session, false);
}
int objectId = session.getNextId();
ResultRemote result = null;
......@@ -115,7 +131,7 @@ public class CommandRemote implements CommandInterface {
session.checkClosed();
if (id <= session.getCurrentId() - SysProperties.SERVER_CACHED_OBJECTS) {
// object is too old - we need to prepare again
prepare(session);
prepare(session, false);
}
int objectId = session.getNextId();
ResultRemote result = null;
......@@ -160,7 +176,7 @@ public class CommandRemote implements CommandInterface {
session.checkClosed();
if (id <= session.getCurrentId() - SysProperties.SERVER_CACHED_OBJECTS) {
// object is too old - we need to prepare again
prepare(session);
prepare(session, false);
}
int updateCount = 0;
boolean autoCommit = false;
......
......@@ -14,6 +14,7 @@ import org.h2.constant.ErrorCode;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.Parameter;
import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.result.LocalResult;
......@@ -184,7 +185,12 @@ public class Insert extends Prepared {
for (int i = 0; i < expr.length; i++) {
Expression e = expr[i];
if (e != null) {
expr[i] = e.optimize(session);
e = e.optimize(session);
if (e instanceof Parameter) {
Parameter p = (Parameter) e;
p.setColumn(columns[i]);
}
expr[i] = e;
}
}
}
......
......@@ -13,6 +13,7 @@ import org.h2.constant.ErrorCode;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.Parameter;
import org.h2.expression.ValueExpression;
import org.h2.message.Message;
import org.h2.result.LocalResult;
......@@ -63,6 +64,10 @@ public class Update extends Prepared {
.getName());
}
expressions[id] = expression;
if (expression instanceof Parameter) {
Parameter p = (Parameter) expression;
p.setColumn(column);
}
}
public int update() throws SQLException {
......
......@@ -35,8 +35,8 @@ package org.h2.engine;
* - No " Message.get" (must be "throw Message.get")
* - No TODO in the docs, remove @~ in .utf8.txt files
* - Run regression test with JDK 1.4 and 1.5
*
* - Change version(s) in performance.html; use latest versions of other dbs
* - Use latest versions of other dbs (Derby 10.4.1.3; PostgreSQL 8.3.1; MySQL 5.0.51)
* - Change version(s) in performance.html
* - Run 'ant benchmark' (with JDK 1.4 currently)
* - Copy the benchmark results and update the performance page and diagram
*
......@@ -76,8 +76,8 @@ package org.h2.engine;
*/
public class Constants {
public static final int BUILD_ID = 71;
private static final String BUILD = "2008-04-25";
public static final int BUILD_ID = 72;
private static final String BUILD = "2008-04-30";
public static final boolean ALLOW_EMPTY_BTREE_PAGES = true;
public static final int ALLOW_LITERALS_NONE = 0;
......@@ -93,7 +93,8 @@ public class Constants {
public static final int FILE_BLOCK_SIZE = 16;
public static final String MAGIC_FILE_HEADER_TEXT = "-- H2 0.5/T -- ".substring(0, FILE_BLOCK_SIZE - 1) + "\n";
public static final String MAGIC_FILE_HEADER = "-- H2 0.5/B -- ".substring(0, FILE_BLOCK_SIZE - 1) + "\n";
public static final int TCP_DRIVER_VERSION = 5;
public static final int TCP_DRIVER_VERSION_5 = 5;
public static final int TCP_DRIVER_VERSION_6 = 6;
public static final int VERSION_JDBC_MAJOR = 3;
public static final int VERSION_JDBC_MINOR = 0;
......
......@@ -472,7 +472,13 @@ public class Database implements DataHandler {
} catch (Throwable e) {
if (recovery) {
traceSystem.getTrace(Trace.DATABASE).error("opening index", e);
fileIndex.close();
ArrayList list = new ArrayList(storageMap.values());
for (int i = 0; i < list.size(); i++) {
Storage s = (Storage) list.get(i);
if (s.getDiskFile() == fileIndex) {
removeStorage(s.getId(), fileIndex);
}
}
fileIndex.delete();
openFileIndex();
} else {
......@@ -939,7 +945,11 @@ public class Database implements DataHandler {
private synchronized void closeOpenFilesAndUnlock() throws SQLException {
if (log != null) {
stopWriter();
log.close();
try {
log.close();
} catch (Throwable e) {
traceSystem.getTrace(Trace.DATABASE).error("close", e);
}
log = null;
}
closeFiles();
......
......@@ -15,10 +15,12 @@ import org.h2.command.CommandRemote;
import org.h2.command.dml.SetTypes;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.expression.ParameterInterface;
import org.h2.jdbc.JdbcSQLException;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.result.ResultInterface;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.util.FileUtils;
......@@ -29,6 +31,7 @@ import org.h2.util.SmallLRUCache;
import org.h2.util.StringUtils;
import org.h2.value.Transfer;
import org.h2.value.Value;
import org.h2.value.ValueString;
/**
* The client side part of a session when using the server mode.
......@@ -47,6 +50,7 @@ public class SessionRemote implements SessionInterface, DataHandler {
public static final int COMMAND_COMMIT = 8;
public static final int CHANGE_ID = 9;
public static final int COMMAND_GET_META_DATA = 10;
public static final int SESSION_PREPARE_READ_PARAMS = 11;
public static final int STATUS_ERROR = 0;
public static final int STATUS_OK = 1;
......@@ -64,13 +68,14 @@ public class SessionRemote implements SessionInterface, DataHandler {
private String cipher;
private byte[] fileEncryptionKey;
private Object lobSyncObject = new Object();
private int clientVersion = Constants.TCP_DRIVER_VERSION_5;
private Transfer initTransfer(ConnectionInfo ci, String db, String server) throws IOException, SQLException {
Socket socket = NetUtils.createSocket(server, Constants.DEFAULT_SERVER_PORT, ci.isSSL());
Transfer trans = new Transfer(this);
trans.setSocket(socket);
trans.init();
trans.writeInt(Constants.TCP_DRIVER_VERSION);
trans.writeInt(clientVersion);
trans.writeString(db);
trans.writeString(ci.getOriginalURL());
trans.writeString(ci.getUserName());
......@@ -228,6 +233,28 @@ public class SessionRemote implements SessionInterface, DataHandler {
traceSystem.close();
throw e;
}
upgradeClientVersionIfPossible();
}
private void upgradeClientVersionIfPossible() {
try {
// TODO check if a newer client version can be used - not required when sending TCP_DRIVER_VERSION_6
CommandInterface command = prepareCommand("SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME=?", 1);
ParameterInterface param = (ParameterInterface) command.getParameters().get(0);
param.setValue(ValueString.get("info.BUILD_ID"));
ResultInterface result = command.executeQuery(1, false);
if (result.next()) {
Value[] v = result.currentRow();
String version = v[0].getString();
if (version.compareTo("71") > 0) {
clientVersion = Constants.TCP_DRIVER_VERSION_6;
}
}
result.close();
} catch (Exception e) {
trace.error("Error trying to upgrade client version", e);
// ignore
}
}
private void switchOffCluster() throws SQLException {
......@@ -439,4 +466,8 @@ public class SessionRemote implements SessionInterface, DataHandler {
return null;
}
public int getClientVersion() {
return clientVersion;
}
}
......@@ -11,6 +11,7 @@ import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.table.Column;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.Value;
......@@ -23,6 +24,7 @@ import org.h2.value.ValueNull;
public class Parameter extends Expression implements ParameterInterface {
private Value value;
private Column column;
private int index;
public Parameter(int index) {
......@@ -51,7 +53,13 @@ public class Parameter extends Expression implements ParameterInterface {
}
public int getType() {
return value == null ? Value.UNKNOWN : value.getType();
if (value != null) {
return value.getType();
}
if (column != null) {
return column.getType();
}
return Value.UNKNOWN;
}
public void mapColumns(ColumnResolver resolver, int level) {
......@@ -81,15 +89,33 @@ public class Parameter extends Expression implements ParameterInterface {
}
public int getScale() {
return value == null ? 0 : value.getScale();
if (value != null) {
return value.getScale();
}
if (column != null) {
return column.getScale();
}
return 0;
}
public long getPrecision() {
return value == null ? 0 : value.getPrecision();
if (value != null) {
return value.getPrecision();
}
if (column != null) {
return column.getPrecision();
}
return 0;
}
public int getDisplaySize() {
return value == null ? 0 : value.getDisplaySize();
if (value != null) {
return value.getDisplaySize();
}
if (column != null) {
return column.getDisplaySize();
}
return 0;
}
public void updateAggregate(Session session) {
......@@ -128,4 +154,8 @@ public class Parameter extends Expression implements ParameterInterface {
return new Comparison(session, Comparison.EQUAL, this, ValueExpression.get(ValueBoolean.get(false)));
}
public void setColumn(Column column) {
this.column = column;
}
}
......@@ -35,4 +35,34 @@ public interface ParameterInterface {
* @throws SQLException if not set.
*/
void checkSet() throws SQLException;
/**
* Get the expected data type of the parameter if no value is set, or the
* data type of the value if one is set.
*
* @return the data type
*/
int getType();
/**
* Get the expected precision of this parameter.
*
* @return the expected precision
*/
long getPrecision();
/**
* Get the expected scale of this parameter.
*
* @return the expected scale
*/
int getScale();
/**
* Check if this column is nullable.
*
* @return Column.NULLABLE_*
*/
int getNullable();
}
......@@ -6,10 +6,13 @@
*/
package org.h2.expression;
import java.io.IOException;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.message.Message;
import org.h2.value.Transfer;
import org.h2.value.Value;
/**
......@@ -19,6 +22,10 @@ public class ParameterRemote implements ParameterInterface {
private Value value;
private int index;
private int dataType = Value.UNKNOWN;
private long precision;
private int scale;
private int nullable = ResultSetMetaData.columnNullableUnknown;
public ParameterRemote(int index) {
this.index = index;
......@@ -37,5 +44,36 @@ public class ParameterRemote implements ParameterInterface {
throw Message.getSQLException(ErrorCode.PARAMETER_NOT_SET_1, "#" + (index + 1));
}
}
public int getType() {
return value == null ? dataType : value.getType();
}
public long getPrecision() {
return value == null ? precision : value.getPrecision();
}
public int getScale() {
return value == null ? scale : value.getScale();
}
public int getNullable() {
return nullable;
}
public void read(Transfer transfer) throws IOException {
dataType = transfer.readInt();
precision = transfer.readLong();
scale = transfer.readInt();
nullable = transfer.readInt();
}
public static void write(Transfer transfer, ParameterInterface p) throws IOException {
transfer.writeInt(p.getType());
transfer.writeLong(p.getPrecision());
transfer.writeInt(p.getScale());
transfer.writeInt(p.getNullable());
}
}
......@@ -2039,11 +2039,11 @@ public class JdbcDatabaseMetaData extends TraceObject implements DatabaseMetaDat
/**
* Returns whether own updates are visible.
*
* @return false
* @return true
*/
public boolean ownUpdatesAreVisible(int type) {
debugCodeCall("ownUpdatesAreVisible", type);
return false;
return true;
}
/**
......
......@@ -7,14 +7,17 @@
package org.h2.jdbc;
import java.sql.ParameterMetaData;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import org.h2.command.CommandInterface;
import org.h2.engine.SessionInterface;
import org.h2.expression.ParameterInterface;
import org.h2.message.Message;
import org.h2.message.TraceObject;
import org.h2.util.MathUtils;
import org.h2.util.ObjectArray;
import org.h2.value.DataType;
import org.h2.value.Value;
/**
* Information about the parameters of a prepared statement.
......@@ -27,6 +30,7 @@ implements ParameterMetaData
private JdbcPreparedStatement prep;
private int paramCount;
private ObjectArray parameters;
/**
* Returns the number of parameters.
......@@ -45,15 +49,16 @@ implements ParameterMetaData
/**
* Returns the parameter mode.
* Always returns parameterModeIn
* Always returns parameterModeIn.
*
* @param param the column index (1,2,...)
* @return parameterModeIn
*/
//## Java 1.4 begin ##
public int getParameterMode(int param) throws SQLException {
try {
debugCodeCall("getParameterMode", param);
checkParameterIndex(param);
getParameter(param);
return parameterModeIn;
} catch (Throwable e) {
throw logAndConvert(e);
......@@ -63,15 +68,20 @@ implements ParameterMetaData
/**
* Returns the parameter type.
* Always returns Types.VARCHAR as everything can be passed as a VARCHAR.
*
* @return Types.VARCHAR
* java.sql.Types.VARCHAR is returned if the data type is not known.
*
* @param param the column index (1,2,...)
* @return the data type
*/
public int getParameterType(int param) throws SQLException {
try {
debugCodeCall("getParameterType", param);
checkParameterIndex(param);
return Types.VARCHAR;
ParameterInterface p = getParameter(param);
int type = p.getType();
if (type == Value.UNKNOWN) {
type = Value.STRING;
}
return DataType.getDataType(type).sqlType;
} catch (Throwable e) {
throw logAndConvert(e);
}
......@@ -79,31 +89,33 @@ implements ParameterMetaData
/**
* Returns the parameter precision.
* Always returns 0.
* 0 is returned if the precision is not known.
*
* @return 0
* @param param the column index (1,2,...)
* @return the precision
*/
public int getPrecision(int param) throws SQLException {
try {
debugCodeCall("getPrecision", param);
checkParameterIndex(param);
return 0;
ParameterInterface p = getParameter(param);
return MathUtils.convertLongToInt(p.getPrecision());
} catch (Throwable e) {
throw logAndConvert(e);
}
}
/**
* Returns the parameter precision.
* Always returns 0.
* Returns the parameter scale.
* 0 is returned if the scale is not known.
*
* @return 0
* @param param the column index (1,2,...)
* @return the scale
*/
public int getScale(int param) throws SQLException {
try {
debugCodeCall("getScale", param);
checkParameterIndex(param);
return 0;
ParameterInterface p = getParameter(param);
return p.getScale();
} catch (Throwable e) {
throw logAndConvert(e);
}
......@@ -113,13 +125,13 @@ implements ParameterMetaData
* Checks if this is nullable parameter.
* Returns ResultSetMetaData.columnNullableUnknown..
*
* @param param the column index (1,2,...)
* @return ResultSetMetaData.columnNullableUnknown
*/
public int isNullable(int param) throws SQLException {
try {
debugCodeCall("isNullable", param);
checkParameterIndex(param);
return ResultSetMetaData.columnNullableUnknown;
return getParameter(param).getNullable();
} catch (Throwable e) {
throw logAndConvert(e);
}
......@@ -129,12 +141,13 @@ implements ParameterMetaData
* Checks if this parameter is signed.
* It always returns true.
*
* @param param the column index (1,2,...)
* @return true
*/
public boolean isSigned(int param) throws SQLException {
try {
debugCodeCall("isSigned", param);
checkParameterIndex(param);
getParameter(param);
return true;
} catch (Throwable e) {
throw logAndConvert(e);
......@@ -142,16 +155,21 @@ implements ParameterMetaData
}
/**
* Returns the parameter class name.
* Always returns java.lang.String.
* Returns the Java class name of the parameter.
* "java.lang.String" is returned if the type is not known.
*
* @return "java.lang.String"
* @param param the column index (1,2,...)
* @return the Java class name
*/
public String getParameterClassName(int param) throws SQLException {
try {
debugCodeCall("getParameterClassName", param);
checkParameterIndex(param);
return String.class.getName();
ParameterInterface p = getParameter(param);
int type = p.getType();
if (type == Value.UNKNOWN) {
type = Value.STRING;
}
return DataType.getTypeClassName(type);
} catch (Throwable e) {
throw logAndConvert(e);
}
......@@ -159,15 +177,20 @@ implements ParameterMetaData
/**
* Returns the parameter type name.
* Always returns VARCHAR.
* "VARCHAR" is returned if the type is not known.
*
* @return "VARCHAR"
* @param param the column index (1,2,...)
* @return the type name
*/
public String getParameterTypeName(int param) throws SQLException {
try {
debugCodeCall("getParameterTypeName", param);
checkParameterIndex(param);
return "VARCHAR";
ParameterInterface p = getParameter(param);
int type = p.getType();
if (type == Value.UNKNOWN) {
type = Value.STRING;
}
return DataType.getDataType(type).name;
} catch (Throwable e) {
throw logAndConvert(e);
}
......@@ -176,17 +199,19 @@ implements ParameterMetaData
JdbcParameterMetaData(SessionInterface session, JdbcPreparedStatement prep, CommandInterface command, int id) {
setTrace(session.getTrace(), TraceObject.PARAMETER_META_DATA, id);
this.prep = prep;
this.paramCount = command.getParameters().size();
this.parameters = command.getParameters();
this.paramCount = parameters.size();
}
void checkParameterIndex(int param) throws SQLException {
private ParameterInterface getParameter(int param) throws SQLException {
checkClosed();
if (param < 1 || param > paramCount) {
throw Message.getInvalidValueException("" + param, "param");
}
return (ParameterInterface) parameters.get(param - 1);
}
void checkClosed() throws SQLException {
private void checkClosed() throws SQLException {
prep.checkClosed();
}
......
......@@ -2818,7 +2818,9 @@ public class JdbcResultSet extends TraceObject implements ResultSet {
}
checkOnValidRow();
if (updateRow != null) {
getUpdatableRow().updateRow(result.currentRow(), updateRow);
UpdatableRow row = getUpdatableRow();
row.updateRow(result.currentRow(), updateRow);
row.refreshRow(result.currentRow());
updateRow = null;
}
} catch (Throwable e) {
......
......@@ -14,6 +14,7 @@ import org.h2.api.DatabaseEventListener;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.store.DataPage;
import org.h2.store.DiskFile;
......@@ -156,6 +157,9 @@ public class LogSystem {
}
} catch (SQLException e) {
closeException = e;
} catch (Throwable e) {
// for example out of memory exception
closeException = Message.convert(e);
}
for (int i = 0; i < activeLogs.size(); i++) {
LogFile l = (LogFile) activeLogs.get(i);
......
......@@ -21,6 +21,7 @@ import org.h2.engine.Engine;
import org.h2.engine.Session;
import org.h2.engine.SessionRemote;
import org.h2.expression.Parameter;
import org.h2.expression.ParameterRemote;
import org.h2.jdbc.JdbcSQLException;
import org.h2.message.Message;
import org.h2.result.LocalResult;
......@@ -42,6 +43,7 @@ public class TcpServerThread implements Runnable {
private Command commit;
private SmallMap cache = new SmallMap(SysProperties.SERVER_CACHED_OBJECTS);
private int id;
private int clientVersion;
public TcpServerThread(Socket socket, TcpServer server, int id) {
this.server = server;
......@@ -61,13 +63,16 @@ public class TcpServerThread implements Runnable {
// TODO server: should support a list of allowed databases and a
// list of allowed clients
try {
int version = transfer.readInt();
clientVersion = transfer.readInt();
if (!server.allow(transfer.getSocket())) {
throw Message.getSQLException(ErrorCode.REMOTE_CONNECTION_NOT_ALLOWED);
}
if (version != Constants.TCP_DRIVER_VERSION) {
throw Message.getSQLException(ErrorCode.DRIVER_VERSION_ERROR_2, new String[] { "" + version,
"" + Constants.TCP_DRIVER_VERSION });
if (clientVersion == Constants.TCP_DRIVER_VERSION_6) {
// version 6 and newer: read max version (currently not used)
transfer.readInt();
} else if (clientVersion != Constants.TCP_DRIVER_VERSION_5) {
throw Message.getSQLException(ErrorCode.DRIVER_VERSION_ERROR_2, new String[] { "" + clientVersion,
"" + Constants.TCP_DRIVER_VERSION_5 });
}
String db = transfer.readString();
String originalURL = transfer.readString();
......@@ -93,7 +98,12 @@ public class TcpServerThread implements Runnable {
Engine engine = Engine.getInstance();
session = engine.getSession(ci);
transfer.setSession(session);
transfer.writeInt(SessionRemote.STATUS_OK).flush();
transfer.writeInt(SessionRemote.STATUS_OK);
if (clientVersion >= Constants.TCP_DRIVER_VERSION_6) {
// version 6: reply what version to use
transfer.writeInt(Constants.TCP_DRIVER_VERSION_6);
}
transfer.flush();
server.addConnection(id, originalURL, ci.getUserName());
trace("Connected");
} catch (Throwable e) {
......@@ -179,6 +189,7 @@ public class TcpServerThread implements Runnable {
private void process() throws IOException, SQLException {
int operation = transfer.readInt();
switch (operation) {
case SessionRemote.SESSION_PREPARE_READ_PARAMS:
case SessionRemote.SESSION_PREPARE: {
int id = transfer.readInt();
String sql = transfer.readString();
......@@ -186,9 +197,17 @@ public class TcpServerThread implements Runnable {
boolean readonly = command.isReadOnly();
cache.addObject(id, command);
boolean isQuery = command.isQuery();
int paramCount = command.getParameters().size();
ObjectArray params = command.getParameters();
int paramCount = params.size();
transfer.writeInt(SessionRemote.STATUS_OK).writeBoolean(isQuery).writeBoolean(readonly)
.writeInt(paramCount).flush();
.writeInt(paramCount);
if (operation == SessionRemote.SESSION_PREPARE_READ_PARAMS) {
for (int i = 0; i < paramCount; i++) {
Parameter p = (Parameter) params.get(i);
ParameterRemote.write(transfer, p);
}
}
transfer.flush();
break;
}
case SessionRemote.SESSION_CLOSE: {
......
......@@ -649,7 +649,7 @@ public class Recover extends Tool implements DataHandler {
writeDataError(writer, "blockCount<0", s.getBytes(), 1);
blockCount = 1;
continue;
} else if ((blockCount * blockSize) >= Integer.MAX_VALUE / 4) {
} else if (((long) blockCount * blockSize) >= Integer.MAX_VALUE / 4) {
writeDataError(writer, "blockCount=" + blockCount, s.getBytes(), 1);
blockCount = 1;
continue;
......@@ -688,6 +688,7 @@ public class Recover extends Tool implements DataHandler {
writer.close();
} catch (Throwable e) {
writeError(writer, e);
e.printStackTrace();
} finally {
IOUtils.closeSilently(writer);
closeSilently(store);
......@@ -744,7 +745,7 @@ public class Recover extends Tool implements DataHandler {
writeDataError(writer, "blockCount<0", s.getBytes(), 1);
blockCount = 1;
continue;
} else if ((blockCount * blockSize) >= Integer.MAX_VALUE / 4 || (blockCount * blockSize) < 0) {
} else if (((long) blockCount * blockSize) >= Integer.MAX_VALUE / 4 || (blockCount * blockSize) < 0) {
writeDataError(writer, "blockCount=" + blockCount, s.getBytes(), 1);
blockCount = 1;
continue;
......
......@@ -154,20 +154,33 @@ public class Shell {
} else if (upper.startsWith("DESCRIBE")) {
String tableName = upper.substring("DESCRIBE".length()).trim();
if (tableName.length() == 0) {
out.println("Usage: describe <table name>");
out.println("Usage: describe [<schema name>.]<table name>");
} else {
String schemaName = null;
int dot = tableName.indexOf('.');
if (dot >= 0) {
schemaName = tableName.substring(0, dot);
tableName = tableName.substring(dot + 1);
}
PreparedStatement prep = null;
ResultSet rs = null;
try {
prep = conn.prepareStatement(
"SELECT CAST(COLUMN_NAME AS VARCHAR(32)) \"Column Name\", " +
"CAST(TYPE_NAME AS VARCHAR(14)) \"Type\", " +
"NUMERIC_PRECISION \"Precision\", " +
"CAST(IS_NULLABLE AS VARCHAR(8)) \"Nullable\", " +
"CAST(COLUMN_DEFAULT AS VARCHAR(20)) \"Default\" " +
"FROM INFORMATION_SCHEMA.COLUMNS " +
"WHERE UPPER(TABLE_NAME)=? ORDER BY ORDINAL_POSITION");
String sql = "SELECT CAST(COLUMN_NAME AS VARCHAR(32)) \"Column Name\", " +
"CAST(TYPE_NAME AS VARCHAR(14)) \"Type\", " +
"NUMERIC_PRECISION \"Precision\", " +
"CAST(IS_NULLABLE AS VARCHAR(8)) \"Nullable\", " +
"CAST(COLUMN_DEFAULT AS VARCHAR(20)) \"Default\" " +
"FROM INFORMATION_SCHEMA.COLUMNS " +
"WHERE UPPER(TABLE_NAME)=?";
if (schemaName != null) {
sql += " AND UPPER(TABLE_SCHEMA)=?";
}
sql += " ORDER BY ORDINAL_POSITION";
prep = conn.prepareStatement(sql);
prep.setString(1, tableName.toUpperCase());
if (schemaName != null) {
prep.setString(2, schemaName.toUpperCase());
}
rs = prep.executeQuery();
printResult(rs, false);
} catch (SQLException e) {
......
......@@ -140,6 +140,7 @@ public class CompareMode {
result = Collator.getInstance(locale);
}
} else if (name.length() == 5) {
// LL_CC (language_country)
int idx = name.indexOf('_');
if (idx >= 0) {
String language = name.substring(0, idx).toLowerCase();
......
......@@ -160,10 +160,16 @@ java org.h2.test.TestAll timer
/*
upload jazoon
deactivate triggers during alter table (during re-creating a table)
improve javadocs
Pluggable tracing system
test japanese translation
upload jazoon
test case for out of memory (try to corrupt the database using out of memory)
analyzer configuration option for the fulltext search
......@@ -211,6 +217,16 @@ Add where required // TODO: change in version 1.1
http://www.w3schools.com/sql/
History:
Some databases could not be opened when appending
;RECOVER=1 to the database URL.
The Japanese translation of the error messages and the H2 Console has been completed
by Masahiro Ikemoto (Arizona Design Inc.)
Updates made to updatable rows are now visible within the same result set.
DatabaseMetaData.ownUpdatesAreVisible now returns true.
ParameterMetaData now returns the correct data
for INSERT and UPDATE statements.
H2 Shell: DESCRIBE now supports an schema name.
Roadmap:
......@@ -417,48 +433,48 @@ Roadmap:
beforeTest();
// db
new TestScriptSimple().runTest(this);
new TestScript().runTest(this);
new TestAutoRecompile().runTest(this);
new TestBackup().runTest(this);
new TestBigDb().runTest(this);
new TestBigResult().runTest(this);
new TestCases().runTest(this);
new TestCheckpoint().runTest(this);
new TestCluster().runTest(this);
new TestCompatibility().runTest(this);
new TestCsv().runTest(this);
new TestEncryptedDb().runTest(this);
new TestExclusive().runTest(this);
new TestFullText().runTest(this);
new TestFunctions().runTest(this);
new TestIndex().runTest(this);
new TestLinkedTable().runTest(this);
new TestListener().runTest(this);
new TestLob().runTest(this);
new TestLogFile().runTest(this);
new TestMemoryUsage().runTest(this);
new TestMultiConn().runTest(this);
new TestMultiDimension().runTest(this);
new TestMultiThread().runTest(this);
new TestOpenClose().runTest(this);
new TestOptimizations().runTest(this);
new TestPowerOff().runTest(this);
new TestReadOnly().runTest(this);
new TestRights().runTest(this);
new TestRunscript().runTest(this);
new TestSQLInjection().runTest(this);
new TestSessionsLocks().runTest(this);
new TestSequence().runTest(this);
new TestSpaceReuse().runTest(this);
new TestSpeed().runTest(this);
new TestTempTables().runTest(this);
new TestTransaction().runTest(this);
new TestTriggersConstraints().runTest(this);
new TestTwoPhaseCommit().runTest(this);
new TestView().runTest(this);
// jdbc
// new TestScriptSimple().runTest(this);
// new TestScript().runTest(this);
// new TestAutoRecompile().runTest(this);
// new TestBackup().runTest(this);
// new TestBigDb().runTest(this);
// new TestBigResult().runTest(this);
// new TestCases().runTest(this);
// new TestCheckpoint().runTest(this);
// new TestCluster().runTest(this);
// new TestCompatibility().runTest(this);
// new TestCsv().runTest(this);
// new TestEncryptedDb().runTest(this);
// new TestExclusive().runTest(this);
// new TestFullText().runTest(this);
// new TestFunctions().runTest(this);
// new TestIndex().runTest(this);
// new TestLinkedTable().runTest(this);
// new TestListener().runTest(this);
// new TestLob().runTest(this);
// new TestLogFile().runTest(this);
// new TestMemoryUsage().runTest(this);
// new TestMultiConn().runTest(this);
// new TestMultiDimension().runTest(this);
// new TestMultiThread().runTest(this);
// new TestOpenClose().runTest(this);
// new TestOptimizations().runTest(this);
// new TestPowerOff().runTest(this);
// new TestReadOnly().runTest(this);
// new TestRights().runTest(this);
// new TestRunscript().runTest(this);
// new TestSQLInjection().runTest(this);
// new TestSessionsLocks().runTest(this);
// new TestSequence().runTest(this);
// new TestSpaceReuse().runTest(this);
// new TestSpeed().runTest(this);
// new TestTempTables().runTest(this);
// new TestTransaction().runTest(this);
// new TestTriggersConstraints().runTest(this);
// new TestTwoPhaseCommit().runTest(this);
// new TestView().runTest(this);
//
// // jdbc
new TestBatchUpdates().runTest(this);
new TestCallableStatement().runTest(this);
new TestCancel().runTest(this);
......@@ -473,57 +489,57 @@ Roadmap:
new TestUpdatableResultSet().runTest(this);
new TestZloty().runTest(this);
// jdbcx
new TestConnectionPool().runTest(this);
new TestDataSource().runTest(this);
new TestXA().runTest(this);
new TestXASimple().runTest(this);
// server
new TestNestedLoop().runTest(this);
new TestWeb().runTest(this);
new TestPgServer().runTest(this);
// mvcc
new TestMvcc1().runTest(this);
new TestMvcc2().runTest(this);
new TestMvcc3().runTest(this);
// synth
new TestCrashAPI().runTest(this);
new TestRandomSQL().runTest(this);
new TestKillRestart().runTest(this);
new TestKillRestartMulti().runTest(this);
// unit
new TestBitField().runTest(this);
new TestCache().runTest(this);
new TestCompress().runTest(this);
new TestDataPage().runTest(this);
new TestDate().runTest(this);
new TestExit().runTest(this);
new TestFile().runTest(this);
new TestFileLock().runTest(this);
new TestFtp().runTest(this);
new TestFileSystem().runTest(this);
new TestIntArray().runTest(this);
new TestIntIntHashMap().runTest(this);
new TestMultiThreadedKernel().runTest(this);
new TestOverflow().runTest(this);
new TestPattern().runTest(this);
new TestReader().runTest(this);
new TestRecovery().runTest(this);
new TestSampleApps().runTest(this);
new TestScriptReader().runTest(this);
runTest("org.h2.test.unit.TestServlet");
new TestSecurity().runTest(this);
new TestStreams().runTest(this);
new TestStringCache().runTest(this);
new TestStringUtils().runTest(this);
new TestTools().runTest(this);
new TestValue().runTest(this);
new TestValueHashMap().runTest(this);
new TestValueMemory().runTest(this);
// // jdbcx
// new TestConnectionPool().runTest(this);
// new TestDataSource().runTest(this);
// new TestXA().runTest(this);
// new TestXASimple().runTest(this);
//
// // server
// new TestNestedLoop().runTest(this);
// new TestWeb().runTest(this);
// new TestPgServer().runTest(this);
//
// // mvcc
// new TestMvcc1().runTest(this);
// new TestMvcc2().runTest(this);
// new TestMvcc3().runTest(this);
//
// // synth
// new TestCrashAPI().runTest(this);
// new TestRandomSQL().runTest(this);
// new TestKillRestart().runTest(this);
// new TestKillRestartMulti().runTest(this);
//
// // unit
// new TestBitField().runTest(this);
// new TestCache().runTest(this);
// new TestCompress().runTest(this);
// new TestDataPage().runTest(this);
// new TestDate().runTest(this);
// new TestExit().runTest(this);
// new TestFile().runTest(this);
// new TestFileLock().runTest(this);
// new TestFtp().runTest(this);
// new TestFileSystem().runTest(this);
// new TestIntArray().runTest(this);
// new TestIntIntHashMap().runTest(this);
// new TestMultiThreadedKernel().runTest(this);
// new TestOverflow().runTest(this);
// new TestPattern().runTest(this);
// new TestReader().runTest(this);
// new TestRecovery().runTest(this);
// new TestSampleApps().runTest(this);
// new TestScriptReader().runTest(this);
// runTest("org.h2.test.unit.TestServlet");
// new TestSecurity().runTest(this);
// new TestStreams().runTest(this);
// new TestStringCache().runTest(this);
// new TestStringUtils().runTest(this);
// new TestTools().runTest(this);
// new TestValue().runTest(this);
// new TestValueHashMap().runTest(this);
// new TestValueMemory().runTest(this);
afterTest();
}
......
......@@ -414,9 +414,9 @@ public class TestMetaData extends TestBase {
checkFalse(meta.ownInsertsAreVisible(ResultSet.TYPE_FORWARD_ONLY));
checkFalse(meta.ownInsertsAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE));
checkFalse(meta.ownInsertsAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE));
checkFalse(meta.ownUpdatesAreVisible(ResultSet.TYPE_FORWARD_ONLY));
checkFalse(meta.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE));
checkFalse(meta.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE));
check(meta.ownUpdatesAreVisible(ResultSet.TYPE_FORWARD_ONLY));
check(meta.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE));
check(meta.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_SENSITIVE));
checkFalse(meta.storesLowerCaseIdentifiers());
checkFalse(meta.storesLowerCaseQuotedIdentifiers());
checkFalse(meta.storesMixedCaseIdentifiers());
......
......@@ -361,6 +361,27 @@ public class TestPreparedStatement extends TestBase {
} catch (SQLException e) {
checkNotGeneralException(e);
}
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE TEST3(ID INT, NAME VARCHAR(255), DATA DECIMAL(10,2))");
PreparedStatement prep1 = conn.prepareStatement("UPDATE TEST3 SET ID=?, NAME=?, DATA=?");
PreparedStatement prep2 = conn.prepareStatement("INSERT INTO TEST3 VALUES(?, ?, ?)");
checkParameter(prep1, 1, "java.lang.Integer", 4, "INTEGER", 10, 0);
checkParameter(prep1, 2, "java.lang.String", 12, "VARCHAR", 255, 0);
checkParameter(prep1, 3, "java.math.BigDecimal", 3, "DECIMAL", 10, 2);
checkParameter(prep2, 1, "java.lang.Integer", 4, "INTEGER", 10, 0);
checkParameter(prep2, 2, "java.lang.String", 12, "VARCHAR", 255, 0);
checkParameter(prep2, 3, "java.math.BigDecimal", 3, "DECIMAL", 10, 2);
stat.execute("DROP TABLE TEST3");
}
private void checkParameter(PreparedStatement prep, int index, String className, int type, String typeName, int precision, int scale) throws Exception {
ParameterMetaData meta = prep.getParameterMetaData();
check(className, meta.getParameterClassName(index));
check(type, meta.getParameterType(index));
check(typeName, meta.getParameterTypeName(index));
check(precision, meta.getPrecision(index));
check(scale, meta.getScale(index));
}
private void testLikeIndex(Connection conn) throws Exception {
......
......@@ -13,6 +13,7 @@ import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.sql.Array;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
......@@ -40,6 +41,7 @@ public class TestResultSet extends TestBase {
stat = conn.createStatement();
testOwnUpdates();
testFindColumn();
testSubstringPrecision();
testColumnLength();
......@@ -65,6 +67,29 @@ public class TestResultSet extends TestBase {
}
private void testOwnUpdates() throws Exception {
DatabaseMetaData meta = conn.getMetaData();
for (int i = 0; i < 3; i++) {
int type = i == 0 ? ResultSet.TYPE_FORWARD_ONLY : i == 1 ? ResultSet.TYPE_SCROLL_INSENSITIVE : ResultSet.TYPE_SCROLL_SENSITIVE;
check(meta.ownUpdatesAreVisible(type));
checkFalse(meta.ownDeletesAreVisible(type));
checkFalse(meta.ownInsertsAreVisible(type));
}
Statement stat = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255))");
stat.execute("INSERT INTO TEST VALUES(1, 'Hello')");
stat.execute("INSERT INTO TEST VALUES(2, 'World')");
ResultSet rs;
rs = stat.executeQuery("SELECT ID, NAME FROM TEST ORDER BY ID");
rs.next();
rs.next();
rs.updateString(2, "Hallo");
rs.updateRow();
check("Hallo", rs.getString(2));
stat.execute("DROP TABLE TEST");
}
private void checkPrecision(int expected, String sql) throws Exception {
ResultSetMetaData meta = stat.executeQuery(sql).getMetaData();
check(expected, meta.getPrecision(1));
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论