提交 ce7fd11d authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 bcaeb7f4
......@@ -37,20 +37,25 @@ 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>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>PooledConnection.getConnection took a long time if only one connection was open at any time. Fixed.
</li><li>Referential integrity violation: Two different SQL states are now used for missing parent / existing child.
</li><li>DatabaseEventListener.exceptionThrown has a new parameter: SQL
</li><li>For compatibility reasons, the catalog name can now be used in queries: SELECT * FROM TESTDB.PUBLIC.TEST
</li><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).
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 new view implementation did not work with &lt; and &lt;= comparison,
and did not allow conditions on aggregates or grouped columns (HAVING). Fixed.. Fixed.
</li><li>Both view implementations did not work with multiple levels of nested temporary views (FROM (SELECT...)). 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.
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!
</li><li>Windows service: the CLASSPATH was not included when starting the service. Fixed.
......@@ -878,6 +883,7 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Performance / server mode: use UDP optionally?
</li><li>Version check: docs / web console (using javascript), and maybe in the library (using TCP/IP)
</li><li>Aggregates: support MEDIAN
</li><li>Option to globally disable / enable referential integrity checks
</li><li>Web server classloader: override findResource / getResourceFrom
</li><li>Cost for embedded temporary view is calculated wrong, if result is constant
</li><li>Comparison: pluggable sort order: natural sort
......@@ -1027,7 +1033,6 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Support ARRAY data type
</li><li>Implement more JDBC 4.0 features
</li><li>H2 Console: implement a servlet to allow simple web app integration
</li><li>Option to globally disable / enable referential integrity checks
</li><li>Support ISO 8601 timestamp / date / time with timezone
</li><li>Support TRANSFORM / PIVOT as in MS Access
</li><li>Sequence: PostgreSQL compatibility (rename, create) (http://www.postgresql.org/docs/8.2/static/sql-altersequence.html)
......
......@@ -39,11 +39,12 @@ public interface DatabaseEventListener extends EventListener {
void diskSpaceIsLow(long stillAvailable) throws SQLException;
/**
* This method is called if an exception occurred.
* This method is called if an exception occurred during database recovery
*
* @param e the exception
* @param sql the SQL statement
*/
void exceptionThrown(SQLException e);
void exceptionThrown(SQLException e, String sql);
/**
* This method is called for long running events, such as recovering, scanning a file or building an index.
......
......@@ -23,14 +23,16 @@ public abstract class Command implements CommandInterface {
protected long startTime;
protected Trace trace;
private volatile boolean cancel;
private final String sql;
public abstract boolean isTransactional();
public abstract boolean isQuery();
public abstract ObjectArray getParameters();
public abstract boolean isReadOnly();
public Command(Parser parser) {
public Command(Parser parser, String sql) {
this.session = parser.getSession();
this.sql = sql;
trace = session.getDatabase().getTrace(Trace.COMMAND);
}
......@@ -42,21 +44,6 @@ public abstract class Command implements CommandInterface {
throw Message.getSQLException(Message.METHOD_ONLY_ALLOWED_FOR_QUERY);
}
// TODO insert parameters into the original query, or allow this syntax
// if(parameters != null && parameters.size() > 0) {
// buff.append(" /* ");
// for(int i=0; i<parameters.size(); i++) {
// if(i>0) {
// buff.append(", ");
// }
// Parameter param = (Parameter) parameters.get(i);
// buff.append(i+1);
// buff.append(" = ");
// buff.append(param.getSQL());
// }
// buff.append(" */");
// }
public ResultInterface executeQuery(int maxrows, boolean scrollable) throws SQLException {
return executeQueryLocal(maxrows);
}
......@@ -73,7 +60,7 @@ public abstract class Command implements CommandInterface {
return result;
} catch(Throwable e) {
SQLException s = Message.convert(e);
database.exceptionThrown(s);
database.exceptionThrown(s, sql);
throw s;
} finally {
stop();
......@@ -119,7 +106,7 @@ public abstract class Command implements CommandInterface {
int result = update();
return result;
} catch (SQLException e) {
database.exceptionThrown(e);
database.exceptionThrown(e, sql);
database.checkPowerOff();
session.rollbackTo(rollback);
throw e;
......@@ -136,5 +123,8 @@ public abstract class Command implements CommandInterface {
public void cancel() {
this.cancel = true;
}
public String getSQL() {
return null;
}
}
......@@ -16,8 +16,8 @@ public class CommandContainer extends Command {
private Prepared prepared;
CommandContainer(Parser parser, Prepared prepared) {
super(parser);
CommandContainer(Parser parser, String sql, Prepared prepared) {
super(parser, sql);
prepared.setCommand(this);
this.prepared = prepared;
}
......
......@@ -16,8 +16,8 @@ public class CommandList extends Command {
// TODO lock if possible!
public CommandList(Parser parser, Command c, String remaining) {
super(parser);
public CommandList(Parser parser, String sql, Command c, String remaining) {
super(parser, sql);
this.command = c;
this.remaining = remaining;
}
......
......@@ -152,6 +152,7 @@ public class Parser {
private char[] sqlCommandChars;
private int lastParseIndex;
private int parseIndex;
private Prepared prepared;
private Prepared currentPrepared;
private Select currentSelect;
private Session session;
......@@ -191,12 +192,12 @@ public class Parser {
try {
Prepared p = parse(sql);
p.prepare();
Command c = new CommandContainer(this, p);
Command c = new CommandContainer(this, sql, p);
p.setCommand(c);
if (isToken(";")) {
String remaining = originalSQL.substring(parseIndex);
if(remaining.trim().length()!=0) {
CommandList list = new CommandList(this, c, remaining);
CommandList list = new CommandList(this, sql, c, remaining);
// list.addCommand(c);
// do {
// c = parseCommand();
......@@ -243,6 +244,7 @@ public class Parser {
int start = lastParseIndex;
currentSelect = null;
currentPrepared = null;
prepared = null;
Prepared c = null;
recompileAlways = false;
indexedParameterList = null;
......@@ -740,10 +742,18 @@ public class Parser {
if(isToken("SELECT") || isToken("FROM")) {
Query query = parseQueryWithParams();
String querySQL = query.getSQL();
String tempViewName = session.getNextTempViewName();
table = new TableView(mainSchema, 0, tempViewName, querySQL, query.getParameters(), null, session, false);
table.setOnCommitDrop(true);
session.addLocalTempTable(table);
Session s;
if(prepared != null && prepared instanceof CreateView) {
s = database.getSystemSession();
} else {
s = session;
}
String tempViewName = s.getNextTempViewName();
table = new TableView(mainSchema, 0, tempViewName, querySQL, query.getParameters(), null, s, false);
if(s != database.getSystemSession()) {
table.setOnCommitDrop(true);
}
s.addLocalTempTable(table);
read(")");
} else {
TableFilter top = readTableFilter(fromOuter);
......@@ -2051,6 +2061,15 @@ public class Parser {
s = currentToken;
read();
}
if (".".equals(currentToken) && schemaName.equals(database.getShortName())) {
read(".");
schemaName = s;
if (currentTokenType != IDENTIFIER) {
throw Message.getSyntaxError(sqlCommand, parseIndex, "identifier");
}
s = currentToken;
read();
}
return s;
}
......@@ -3197,7 +3216,7 @@ public class Parser {
columns.add(new Column(cols[i], Value.STRING, 0, 0));
}
int id = database.allocateObjectId(true, true);
recursiveTable = new TableData(schema, tempViewName, id, columns, false);
recursiveTable = schema.createTable(tempViewName, id, columns, false);
recursiveTable.setTemporary(true);
session.addLocalTempTable(recursiveTable);
String querySQL = StringCache.getNew(sqlCommand.substring(parseIndex));
......@@ -3221,6 +3240,7 @@ public class Parser {
boolean ifNotExists = readIfNoExists();
String viewName = readIdentifierWithSchema();
CreateView command = new CreateView(session, getSchema());
this.prepared = command;
command.setViewName(viewName);
command.setIfNotExists(ifNotExists);
command.setComment(readCommentIf());
......
......@@ -197,7 +197,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
// can't just use this table, because most column objects are 'shared' with the old table
// still need a new id because using 0 would mean: the new table tries to use the rows of the table 0 (the script table)
int id = -1;
TableData newTable = new TableData(getSchema(), tempName, id, newColumns, persistent);
TableData newTable = getSchema().createTable(tempName, id, newColumns, persistent);
newTable.setComment(table.getComment());
execute(newTable.getCreateSQL());
newTable = (TableData) newTable.getSchema().getTableOrView(session, newTable.getName());
......
......@@ -70,7 +70,7 @@ public class CreateLinkedTable extends SchemaCommand {
tableName);
}
int id = getObjectId(false, true);
TableLink table = new TableLink(getSchema(), id, tableName, driver, url, user, password, originalTable, emitUpdates, force);
TableLink table = getSchema().createTableLink(id, tableName, driver, url, user, password, originalTable, emitUpdates, force);
table.setComment(comment);
db.addSchemaObject(session, table);
return 0;
......
......@@ -119,7 +119,7 @@ public class CreateTable extends SchemaCommand {
}
}
int id = getObjectId(true, true);
TableData table = new TableData(getSchema(), tableName, id, columns, persistent);
TableData table = getSchema().createTable(tableName, id, columns, persistent);
table.setComment(comment);
table.setTemporary(temporary);
table.setGlobalTemporary(globalTemporary);
......
......@@ -57,7 +57,8 @@
} else {
querySQL = select.getSQL();
}
TableView view = new TableView(getSchema(), id, viewName, querySQL, null, columnNames, session, recursive);
Session s = db.getSystemSession();
TableView view = new TableView(getSchema(), id, viewName, querySQL, null, columnNames, s, recursive);
view.setComment(comment);
db.addSchemaObject(session, view);
return 0;
......
......@@ -277,6 +277,10 @@ public class Select extends Query {
Value[] row = new Value[columnCount];
for (int i = 0; i < columnCount; i++) {
Expression expr = (Expression) expressions.get(i);
int testing;
if(expr == null) {
System.out.println("stop");
}
row[i] = expr.getValue(session);
}
result.addRow(row);
......@@ -586,7 +590,12 @@ public class Select extends Query {
buff.append(StringUtils.unEnclose(g.getSQL()));
}
}
if(havingIndex >= 0) {
if(having != null) {
// could be set after addGlobalCondition
// in this case the query is not run directly, just getPlanSQL is called
Expression h = having;
buff.append("\nHAVING " + StringUtils.unEnclose(h.getSQL()));
} else if(havingIndex >= 0) {
Expression h = exprList[havingIndex];
buff.append("\nHAVING " + StringUtils.unEnclose(h.getSQL()));
}
......@@ -674,7 +683,10 @@ public class Select extends Query {
Expression comp = new Comparison(session, comparisonType, col, expr);
comp = comp.optimize(session);
if(isGroupQuery) {
if(having == null) {
if(havingIndex >= 0) {
having = (Expression) expressions.get(havingIndex);
}
if(having == null) {
having = comp;
} else {
having = new ConditionAndOr(ConditionAndOr.AND, having, comp);
......
......@@ -275,7 +275,7 @@ public class ConstraintReferential extends Constraint {
check.setValue(refIdx, v.convertTo(refCol.getType()));
}
if(!found(session, refIndex, check)) {
throw Message.getSQLException(Message.CHECK_CONSTRAINT_VIOLATED_1, getShortDescription());
throw Message.getSQLException(Message.REFERENTIAL_INTEGRITY_VIOLATED_PARENT_MISSING_1, getShortDescription());
}
}
......@@ -339,7 +339,7 @@ public class ConstraintReferential extends Constraint {
check.setValue(idx, v);
}
if(found(session, index, check)) {
throw Message.getSQLException(Message.CHECK_CONSTRAINT_VIOLATED_1, getShortDescription());
throw Message.getSQLException(Message.REFERENTIAL_INTEGRITY_VIOLATED_CHILD_EXISTS_1, getShortDescription());
}
}
......
......@@ -4,6 +4,8 @@
*/
package org.h2.engine;
import org.h2.message.TraceSystem;
/*
* Coding rules:
* - boolean CHECK = x > boolean CHECK = Database.CHECK
......@@ -246,8 +248,9 @@ public class Constants {
public static int MAX_FILE_RETRY = Math.max(1, getIntSetting("h2.maxFileRetry", 16));
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 final boolean INDEX_LOOKUP_NEW = getBooleanSetting("h2.indexLookupNew", true);
public static final boolean TRACE_IO = getBooleanSetting("h2.traceIO", false);
public static final int DATASOURCE_TRACE_LEVEL = getIntSetting("h2.dataSourceTraceLevel", TraceSystem.ERROR);
public static boolean getBooleanSetting(String name, boolean defaultValue) {
String s = System.getProperty(name);
......
......@@ -467,7 +467,7 @@ public class Database implements DataHandler {
cols.add(new Column("HEAD", Value.INT, 0, 0));
cols.add(new Column("TYPE", Value.INT, 0, 0));
cols.add(new Column("SQL", Value.STRING, 0, 0));
meta = new TableData(mainSchema, "SYS", 0, cols, persistent);
meta = mainSchema.createTable("SYS", 0, cols, persistent);
metaIdIndex = meta.addIndex(systemSession, "SYS_ID", 0, new Column[]{columnId}, IndexType.createPrimaryKey(false, false), Index.EMPTY_HEAD, null);
objectIds.set(0);
// there could be views on system tables, so they must be added first
......@@ -1269,10 +1269,10 @@ public class Database implements DataHandler {
}
}
public void exceptionThrown(SQLException e) {
public void exceptionThrown(SQLException e, String sql) {
if(eventListener != null) {
try {
eventListener.exceptionThrown(e);
eventListener.exceptionThrown(e, sql);
} catch(Exception e2) {
// ignore this second (user made) exception
}
......
......@@ -65,10 +65,10 @@ public class MetaRecord {
command.setHeadPos(headPos);
command.update();
} catch(Throwable e) {
SQLException s = Message.convert(e);
SQLException s = Message.addSQL(Message.convert(e), sql);
db.getTrace(Trace.DATABASE).error(sql, s);
if(listener != null) {
listener.exceptionThrown(s);
listener.exceptionThrown(s, sql);
// continue startup in this case
} else {
throw s;
......
......@@ -284,9 +284,10 @@ public class SessionRemote implements SessionInterface, DataHandler {
if (status == STATUS_ERROR) {
String sqlstate = transfer.readString();
String message = transfer.readString();
String sql = transfer.readString();
int errorCode = transfer.readInt();
String trace = transfer.readString();
throw new JdbcSQLException(message, sqlstate, errorCode, null, trace);
throw new JdbcSQLException(message, sql, sqlstate, errorCode, null, trace);
} else if(status == STATUS_CLOSED) {
transferList = null;
}
......
......@@ -339,7 +339,7 @@ public class Function extends Expression implements FunctionCall {
varArgs.add(param);
} else {
if(index >= args.length) {
throw Message.getSQLException(Message.INVALID_PARAMETER_COUNT_1, ""+args.length);
throw Message.getSQLException(Message.INVALID_PARAMETER_COUNT_2, new String[] {info.name, "" + args.length}, null);
}
args[index] = param;
}
......@@ -947,7 +947,7 @@ public class Function extends Expression implements FunctionCall {
private static int getDatePart(String part) throws SQLException {
Integer p = (Integer) datePart.get(StringUtils.toUpperEnglish(part));
if(p==null) {
throw Message.getSQLException(Message.INVALID_VALUE_2, new String[] { "part", part }, null);
throw Message.getSQLException(Message.INVALID_VALUE_2, new String[] { "date part", part }, null);
}
return p.intValue();
}
......@@ -1057,7 +1057,7 @@ public class Function extends Expression implements FunctionCall {
// avoid out of memory
return s;
}
StringBuffer buff = new StringBuffer();
StringBuffer buff = new StringBuffer(s.length());
int start = 0;
int len = replace.length();
while (true) {
......@@ -1290,7 +1290,7 @@ public class Function extends Expression implements FunctionCall {
}
boolean ok = (len >= min) && (len <= max);
if(!ok) {
throw Message.getSQLException(Message.INVALID_PARAMETER_COUNT_1, min + ".." + max);
throw Message.getSQLException(Message.INVALID_PARAMETER_COUNT_2, new String[]{info.name, min + ".." + max}, null);
}
args = new Expression[len];
varArgs.toArray(args);
......@@ -1298,7 +1298,7 @@ public class Function extends Expression implements FunctionCall {
} else {
int len = args.length;
if(len>0 && args[len-1] == null) {
throw Message.getSQLException(Message.INVALID_PARAMETER_COUNT_1, info.name + ": " + len);
throw Message.getSQLException(Message.INVALID_PARAMETER_COUNT_2, new String[]{info.name, "" + len}, null);
}
}
}
......
......@@ -1713,11 +1713,11 @@ public class JdbcDatabaseMetaData extends TraceObject implements DatabaseMetaDat
/**
* Returns whether the catalog name in INSERT, UPDATE, DELETE is supported.
*
* @return false
* @return true
*/
public boolean supportsCatalogsInDataManipulation() {
debugCodeCall("supportsCatalogsInDataManipulation");
return false;
return true;
}
/**
......@@ -1733,31 +1733,31 @@ public class JdbcDatabaseMetaData extends TraceObject implements DatabaseMetaDat
/**
* Returns whether the catalog name in CREATE TABLE is supported.
*
* @return false
* @return true
*/
public boolean supportsCatalogsInTableDefinitions() {
debugCodeCall("supportsCatalogsInTableDefinitions");
return false;
return true;
}
/**
* Returns whether the catalog name in CREATE INDEX is supported.
*
* @return false
* @return true
*/
public boolean supportsCatalogsInIndexDefinitions() {
debugCodeCall("supportsCatalogsInIndexDefinitions");
return false;
return true;
}
/**
* Returns whether the catalog name in GRANT is supported.
*
* @return false
* @return true
*/
public boolean supportsCatalogsInPrivilegeDefinitions() {
debugCodeCall("supportsCatalogsInPrivilegeDefinitions");
return false;
return true;
}
/**
......
......@@ -16,9 +16,10 @@ import org.h2.engine.Constants;
public class JdbcSQLException extends SQLException {
private static final long serialVersionUID = -8200821788226954151L;
private Throwable cause;
private String originalMessage;
private String trace;
private final String originalMessage;
private final String sql;
private final Throwable cause;
private final String trace;
/**
* Creates a SQLException a message, sqlstate and cause.
......@@ -27,15 +28,25 @@ public class JdbcSQLException extends SQLException {
* @param state the SQL state
* @param cause the exception that was the reason for this exception
*/
public JdbcSQLException(String message, String state, int errorCode, Throwable cause, String trace) {
super(message + " [" + state + "-" + Constants.BUILD_ID + "]", state, errorCode);
public JdbcSQLException(String message, String sql, String state, int errorCode, Throwable cause, String trace) {
super(buildMessage(message, sql, state), state, errorCode);
this.originalMessage = message;
this.sql = sql;
this.cause = cause;
this.trace = trace;
//#ifdef JDK14
initCause(cause);
//#endif
}
private static String buildMessage(String message, String sql, String state) {
StringBuffer buff = new StringBuffer(message);
if(sql != null) {
buff.append("; SQL statement: ");
buff.append(sql);
}
return message + " [" + state + "-" + Constants.BUILD_ID + "]";
}
/**
* INTERNAL
......@@ -110,6 +121,15 @@ public class JdbcSQLException extends SQLException {
return cause;
}
/**
* Returns the SQL statement.
*
* @return the SQL statement
*/
public String getSQL() {
return sql;
}
/**
* Returns the class name, the message, and in the server mode, the stack trace of the server
*
......
......@@ -23,7 +23,7 @@ public class JdbcDataSourceFactory implements ObjectFactory {
static {
org.h2.Driver.load();
traceSystem = new TraceSystem(Constants.CLIENT_TRACE_DIRECTORY + "h2datasource" + Constants.SUFFIX_TRACE_FILE);
traceSystem.setLevelFile(TraceSystem.DEBUG);
traceSystem.setLevelFile(Constants.DATASOURCE_TRACE_LEVEL);
}
public JdbcDataSourceFactory() {
......
......@@ -39,6 +39,7 @@ implements XAConnection, XAResource, JdbcConnectionListener
//#ifdef JDK14
private JdbcDataSourceFactory factory;
private String url, user, password;
private JdbcConnection connSentinel;
private JdbcConnection conn;
private ArrayList listeners = new ArrayList();
private Xid currentTransaction;
......@@ -55,7 +56,8 @@ implements XAConnection, XAResource, JdbcConnectionListener
this.url = url;
this.user = user;
this.password = password;
getConnection();
connSentinel = openConnection();
getConnection();
}
public XAResource getXAResource() throws SQLException {
......@@ -65,19 +67,37 @@ implements XAConnection, XAResource, JdbcConnectionListener
public void close() throws SQLException {
debugCodeCall("close");
try {
closeConnection(conn);
closeConnection(connSentinel);
} finally {
conn = null;
connSentinel = null;
}
}
private void closeConnection(JdbcConnection conn) throws SQLException {
if(conn != null) {
conn.closeConnection();
conn = null;
}
}
public Connection getConnection() throws SQLException {
debugCodeCall("getConnection");
close();
private JdbcConnection openConnection() throws SQLException {
Properties info = new Properties();
info.setProperty("user", user);
info.setProperty("password", password);
conn = new JdbcConnection(url, info);
JdbcConnection conn = new JdbcConnection(url, info);
conn.setJdbcConnectionListener(this);
return conn;
}
public Connection getConnection() throws SQLException {
debugCodeCall("getConnection");
if(conn != null) {
closeConnection(conn);
conn = null;
}
conn = openConnection();
conn.setJdbcConnectionListener(this);
return conn;
}
......
......@@ -62,7 +62,7 @@ public class Message {
public static JdbcSQLException getSQLException(int errorCode, String[] param, Throwable cause) {
String sqlstate = getState(errorCode);
String message = translate(sqlstate, param);
return new JdbcSQLException(message, sqlstate, errorCode, cause, null);
return new JdbcSQLException(message, null, sqlstate, errorCode, cause, null);
}
public static SQLException getSyntaxError(String sql, int index) {
......@@ -119,7 +119,7 @@ public class Message {
case NO_DATA_AVAILABLE: return "02000";
// 07: dynamic SQL error
case INVALID_PARAMETER_COUNT_1: return "07001";
case INVALID_PARAMETER_COUNT_2: return "07001";
// 08: connection exception
case ERROR_OPENING_DATABASE: return "08000";
......@@ -128,17 +128,6 @@ public class Message {
// 21: cardinality violation
case COLUMN_COUNT_DOES_NOT_MATCH: return "21S02";
// 22: data exception
case NUMERIC_VALUE_OUT_OF_RANGE: return "22003";
case DIVISION_BY_ZERO_1: return "22012";
case LIKE_ESCAPE_ERROR_1: return "22025";
// 23: integrity constraint violation
case CHECK_CONSTRAINT_VIOLATED_1: return "23000";
case DUPLICATE_KEY_1: return "23001"; // integrity constraint violation
// 3B: savepoint exception
// 42: syntax error or access rule violation
case SYNTAX_ERROR_1: return "42000";
case SYNTAX_ERROR_2: return "42001";
......@@ -154,7 +143,7 @@ public class Message {
// HZ: remote database access
//
// HY
case GENERAL_ERROR_1: return "HY000";
case UNKNOWN_DATA_TYPE_1: return "HY004";
......@@ -169,7 +158,7 @@ public class Message {
public static final int NO_DATA_AVAILABLE = 2000;
// 07: dynamic SQL error
public static final int INVALID_PARAMETER_COUNT_1 = 7001;
public static final int INVALID_PARAMETER_COUNT_2 = 7001;
// 08: connection exception
public static final int ERROR_OPENING_DATABASE = 8000;
......@@ -185,8 +174,10 @@ public class Message {
// 23: integrity constraint violation
public static final int CHECK_CONSTRAINT_VIOLATED_1 = 23000;
public static final int DUPLICATE_KEY_1 = 23001; // integrity constraint violation
public static final int DUPLICATE_KEY_1 = 23001;
public static final int REFERENTIAL_INTEGRITY_VIOLATED_PARENT_MISSING_1 = 23002;
public static final int REFERENTIAL_INTEGRITY_VIOLATED_CHILD_EXISTS_1 = 23003;
// 3B: savepoint exception
// 42: syntax error or access rule violation
......@@ -341,16 +332,16 @@ public class Message {
public static final int RESULT_SET_NOT_UPDATABLE = 90127;
public static SQLException addSQL(SQLException e, String sql) {
if(e.getMessage().indexOf("SQL")>=0) {
return e;
}
if(e instanceof JdbcSQLException) {
JdbcSQLException j = (JdbcSQLException) e;
return new JdbcSQLException(j.getOriginalMessage()+"; SQL statement: "+sql,
if(j.getSQL() != null) {
return j;
}
return new JdbcSQLException(j.getOriginalMessage(), j.getSQL(),
j.getSQLState(),
j.getErrorCode(), j, null);
} else {
return new JdbcSQLException(e.getMessage()+"; SQL statement: "+sql,
return new JdbcSQLException(e.getMessage(), sql,
e.getSQLState(),
e.getErrorCode(), e, null);
}
......
......@@ -709,7 +709,7 @@ SET AUTOCOMMIT OFF
SET CACHE_SIZE int
","
Sets the size of the cache.
A cache entry contains about 128 bytes. The default value is 32768.
A cache entry contains about 128 bytes. The default value is 65536.
This setting is persistent and affects all connections as there is only one cache per database.
Admin rights are required to execute this command.
This setting can be appended to the database URL: jdbc:h2:test;CACHE_SIZE=8192
......
# If the word 'SQL' appears then the whole SQL statement must be a parameter. Otherwise this may be added: '; SQL statement: ' + sql
02000=No data is available
07001=Invalid parameter count, expected count: {0}
07001=Invalid parameter count for {0}, expected count: {1}
08000=Error opening database
08004=Wrong user name or password
21S02=Column count does not match
......@@ -10,6 +10,8 @@
22025=Error in LIKE ESCAPE: {0}
23000=Check constraint violation: {0}
23001=Unique index or primary key violation: {0}
23002=Referential integrity constraint violation: {0}
23003=Referential integrity constraint violation: {0}
42000=Syntax error in SQL statement {0}
42001=Syntax error in SQL statement {0}; expected {1}
42S01=Table {0} already exists
......
......@@ -18,6 +18,8 @@ import org.h2.jdbc.JdbcSQLException;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.TableLink;
import org.h2.util.ObjectArray;
public class Schema extends DbObject {
......@@ -250,5 +252,13 @@ public class Schema extends DbObject {
}
map.remove(objName);
}
public TableData createTable(String tempName, int id, ObjectArray newColumns, boolean persistent) throws SQLException {
return new TableData(this, tempName, id, newColumns, persistent);
}
public TableLink createTableLink(int id, String tableName, String driver, String url, String user, String password, String originalTable, boolean emitUpdates, boolean force) throws SQLException {
return new TableLink(this, id, tableName, driver, url, user, password, originalTable, emitUpdates, force);
}
}
......@@ -130,14 +130,19 @@ public class TcpServerThread implements Runnable {
e.printStackTrace(new PrintWriter(writer));
String trace = writer.toString();
String message;
String sql;
if(e instanceof JdbcSQLException) {
message = ((JdbcSQLException) e).getOriginalMessage();
JdbcSQLException j = (JdbcSQLException) e;
message = j.getOriginalMessage();
sql = j.getSQL();
} else {
message = e.getMessage();
sql = null;
}
transfer.writeInt(SessionRemote.STATUS_ERROR).
writeString(s.getSQLState()).
writeString(message).
writeString(sql).
writeInt(s.getErrorCode()).
writeString(trace).
flush();
......
......@@ -83,7 +83,7 @@ public class LogFile {
}
String s = fileName.substring(fileNamePrefix.length()+1, fileName.length()-Constants.SUFFIX_LOG_FILE.length());
int id = Integer.parseInt(s);
return new LogFile(log, id, fileNamePrefix);
return new LogFile(log, id, fileNamePrefix);
}
public String getFileName() {
......
......@@ -12,6 +12,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.Trace;
import org.h2.util.FileUtils;
import org.h2.util.ObjectArray;
......@@ -217,7 +218,15 @@ public class LogSystem {
activeLogs = new ObjectArray();
for (int i = 0; i < list.length; i++) {
String s = list[i];
LogFile l = LogFile.openIfLogFile(this, fileNamePrefix, s);
LogFile l = null;
try {
l = LogFile.openIfLogFile(this, fileNamePrefix, s);
} catch(SQLException e) {
database.getTrace(Trace.LOG).debug("Error opening log file, header corrupt: "+s, e);
// this can happen if the system crashes just after creating a new file (before writing the header)
// rename it, so that it doesn't get in the way the next time
FileUtils.rename(s, s + ".corrupt");
}
if (l != null) {
if (l.getPos() == LOG_WRITTEN) {
closeOldFile(l);
......
......@@ -241,7 +241,7 @@ public class Csv implements SimpleRowSource {
return data;
}
}
StringBuffer buff = new StringBuffer();
StringBuffer buff = new StringBuffer(data.length());
for(int i=0; i<data.length(); i++) {
char ch = data.charAt(i);
if(ch == fieldDelimiter || ch == escapeCharacter) {
......@@ -405,7 +405,7 @@ public class Csv implements SimpleRowSource {
}
private String unEscape(String s) {
StringBuffer buff = new StringBuffer();
StringBuffer buff = new StringBuffer(s.length());
int start = 0;
while(true) {
int idx = s.indexOf(escapeCharacter, start);
......
......@@ -5,6 +5,7 @@
package org.h2.util;
public class ClassUtils {
public static Class loadClass(String className) throws ClassNotFoundException {
// TODO support special syntax to load classes using another classloader
return Class.forName(className);
......
......@@ -223,7 +223,7 @@ public class StringUtils {
if(array == null) {
return "null";
}
StringBuffer buff = new StringBuffer();
StringBuffer buff = new StringBuffer(5 * array.length);
buff.append("new String[]{");
for(int i=0; i<array.length; i++) {
if(i>0) {
......@@ -339,7 +339,7 @@ public class StringUtils {
}
public static String arrayCombine(String[] list, char separatorChar) {
StringBuffer buff=new StringBuffer();
StringBuffer buff=new StringBuffer(5 * list.length);
for(int i=0;i<list.length;i++) {
if(i>0) {
buff.append(separatorChar);
......@@ -581,7 +581,8 @@ public class StringUtils {
}
public static String quoteIdentifier(String s) {
StringBuffer buff = new StringBuffer("\"");
StringBuffer buff = new StringBuffer(s.length() + 2);
buff.append('\"');
for(int i=0; i<s.length(); i++) {
char c = s.charAt(i);
if(c == '"') {
......
......@@ -68,7 +68,8 @@ public class ShowProgress implements DatabaseEventListener {
System.out.println("diskSpaceIsLow stillAvailable="+stillAvailable);
}
public void exceptionThrown(SQLException e) {
public void exceptionThrown(SQLException e, String sql) {
System.out.println("Error executing " + sql);
e.printStackTrace();
}
......
......@@ -7,6 +7,7 @@ package org.h2.test;
import java.sql.SQLException;
import java.util.Properties;
import org.h2.message.TraceSystem;
import org.h2.server.TcpServer;
import org.h2.test.jdbc.*;
import org.h2.test.jdbc.xa.TestXA;
......@@ -93,46 +94,37 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
test.printSystem();
/*
The database name must be at least 3 characters
jdbc:h2:te
runscript from 'C:\download\backup.sql';
alter table download_link add html_ltarget_ord INTEGER(10) default 0;
alter table navigation_link add html_ltarget_ord INTEGER(10) default 0;
alter table web_link add html_ltarget_ord INTEGER(10) default 0;
ALTER TABLE navigation_page_content DROP COLUMN parent_type_id;
ALTER TABLE entity_info DROP COLUMN parent_type_id;
ALTER TABLE navigation_point_content DROP COLUMN parent_type_id;
ALTER TABLE paragraph_proxy DROP COLUMN parent_type_id;
ALTER TABLE patch DROP COLUMN parent_type_id;
ALTER TABLE page_content DROP COLUMN parent_type_id;
ALTER TABLE image_gallery_image_usage_ref DROP COLUMN ref_image_gallery;
ALTER TABLE navigation_page_content_reference DROP COLUMN ref_element_provider;
ALTER TABLE paragraph_proxy DROP COLUMN ref_page_content;
alter table navigation_page_content_reference add contained BOOLEAN(1) default false;
update navigation_page_content_reference set contained=true where state=7;
alter table navigation_page_content add contained BOOLEAN(1) default false;
update navigation_page_content set contained=true where state=7;
alter table navigation_point_content add contained BOOLEAN(1) default false;
update navigation_point_content set contained=true where state=7;
alter table weblica_link add contained BOOLEAN(1) default false;
update weblica_link set contained=true where state=7;
alter table image_usage add contained BOOLEAN(1) default false;
update image_usage set contained=true where state=7;
alter table paragraph_proxy add contained BOOLEAN(1) default false;
traceSystem.setLevelFile(TraceSystem.ERROR);
because of temp file limitations, database names with less than 3 characters are not supported
add test case:
prepare: select * from (select * from (select * from dual) a) b;
select * from dual;
execute prepared
add test case:
try to create a view without access rights
try to create a view with a subquery without access right
make sure INDEX_LOOKUP_NEW = is true by default.
Test Console (batch, javaw, different platforms)
test with openoffice (metadata changes)
testHalt
java org.h2.test.TestAll halt
>testHalt.txt
timer test
make sure INDEX_LOOKUP_NEW = is true by default.
Test Console (batch, javaw, different platforms)
backup.sql / lob file problem
Change documentation and default database for H2 Console: jdbc:h2:~/test
testHalt
Mail http://sf.net/projects/samooha
java.lang.Exception: query was too quick; result: 0 time:968
......@@ -152,9 +144,8 @@ MySQL, PostgreSQL
http://semmle.com/
try out, find bugs
Currently there is no such feature, however it is quite simple to add a user defined function
READ_TEXT(fileName String) returning a CLOB. The performance would probably not be optimal,
but it should work. I am not sure if this will read the CLOB in memory however.
READ_TEXT(fileName String) returning a CLOB.
I am not sure if this will read the CLOB in memory however.
I will add this to the todo list.
Docs: Fix Quickstart
......@@ -176,21 +167,25 @@ Test Eclipse DTP 1.5 (HSQLDB / H2 connection bug fixed)
Automate real power off tests
how to make -baseDir work for H2 Console?
how to make -baseDir work for H2 Console (embedded mode)?
-Dh2.baseDir=x {$baseDir}/...
http://db.apache.org/ddlutils/ (write a H2 driver)
Negative dictionary:
Please note that
timer test
support translated exceptions (translated, then english at the end, for Hibernate compatibility)
support translated exceptions (english + translated)
keep db open as long as there are PooledConnections
select * from H2.PUBLIC.ORDERS
make static member variables final (this helps find forgotten initializers)
Merge more from diff.zip (Pavel Ganelin)
keep db open (independent of DB_CLOSE_DELAY) while a PooledConnections exists.
*/
/*
......@@ -260,7 +255,6 @@ SELECT COUNT(*) AS A FROM TEST GROUP BY ID HAVING A>0;
-- Yes: MySQL, HSQLDB
-- Fail: Oracle, MS SQL Server, PostgreSQL, H2, Derby
*/
// TODO: fix Hibernate dialect bug / Bordea Felix (lost email)
// run TestHalt
......
......@@ -46,8 +46,8 @@ public class TestListener extends TestBase implements DatabaseEventListener {
System.out.println("diskSpaceIsLow stillAvailable="+stillAvailable);
}
public void exceptionThrown(SQLException e) {
TestBase.logError("exceptionThrown", e);
public void exceptionThrown(SQLException e, String sql) {
TestBase.logError("exceptionThrown sql=" + sql, e);
}
public void setProgress(int state, String name, int current, int max) {
......
......@@ -117,7 +117,7 @@ public class TestMultiConn extends TestBase implements DatabaseEventListener {
public void diskSpaceIsLow(long stillAvailable) throws SQLException {
}
public void exceptionThrown(SQLException e) {
public void exceptionThrown(SQLException e, String sql) {
}
public void setProgress(int state, String name, int x, int max) {
......
......@@ -137,8 +137,8 @@ public class TestOpenClose extends TestBase implements DatabaseEventListener {
throw new SQLException("unexpected");
}
public void exceptionThrown(SQLException e) {
throw new Error("unexpected: " + e);
public void exceptionThrown(SQLException e, String sql) {
throw new Error("unexpected: " + e + " sql: " + sql);
}
public void setProgress(int state, String name, int current, int max) {
......
......@@ -33,11 +33,11 @@ public class TestScriptSimple extends TestBase {
}
sql = sql.trim();
// System.out.println(sql);
if("@reconnect".equals(sql)) {
if("@reconnect".equals(sql.toLowerCase())) {
reconnect();
} else if(sql.length() == 0) {
// ignore
} else if(sql.startsWith("select")) {
} else if(sql.toLowerCase().startsWith("select")) {
ResultSet rs = conn.createStatement().executeQuery(sql);
while(rs.next()) {
String expected = reader.readStatement().trim();
......
......@@ -388,11 +388,11 @@ public class TestMetaData extends TestBase {
checkFalse(meta.supportsANSI92IntermediateSQL());
checkFalse(meta.supportsANSI92FullSQL());
check(meta.supportsBatchUpdates());
checkFalse(meta.supportsCatalogsInDataManipulation());
checkFalse(meta.supportsCatalogsInIndexDefinitions());
checkFalse(meta.supportsCatalogsInPrivilegeDefinitions());
check(meta.supportsCatalogsInDataManipulation());
check(meta.supportsCatalogsInIndexDefinitions());
check(meta.supportsCatalogsInPrivilegeDefinitions());
checkFalse(meta.supportsCatalogsInProcedureCalls());
checkFalse(meta.supportsCatalogsInTableDefinitions());
check(meta.supportsCatalogsInTableDefinitions());
check(meta.supportsColumnAliasing());
check(meta.supportsConvert());
check(meta.supportsConvert(Types.INTEGER, Types.VARCHAR));
......
......@@ -47,7 +47,7 @@ public abstract class TestHalt extends TestBase {
for(int i=0;; i++) {
operations = OP_INSERT | i;
flags = i >> 4;
// flags = FLAG_NODELAY | FLAG_LOBS;
// flags |= FLAG_NO_DELAY; // | FLAG_LOBS;
try {
runTest();
} catch(Throwable t) {
......
......@@ -25,11 +25,6 @@ public class TestHaltApp extends TestHalt {
protected void testInit() throws SQLException {
Statement stat = conn.createStatement();
try {
execute(stat, "DROP TABLE TEST");
} catch(SQLException e) {
// ignore
}
// stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR(255))");
for(int i=0; i< 20; i++) {
execute(stat, "DROP TABLE IF EXISTS TEST" + i);
......@@ -38,11 +33,15 @@ public class TestHaltApp extends TestHalt {
for(int i=0; i< 20; i+=2) {
execute(stat, "DROP TABLE TEST" + i);
}
execute(stat, "DROP TABLE IF EXISTS TEST");
execute(stat, "CREATE TABLE TEST(ID BIGINT GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR(255), DATA CLOB)");
}
protected void testWaitAfterAppStart() throws Exception {
int sleep = 10 + random.nextInt(300);
if((flags & FLAG_NO_DELAY) == 0) {
sleep += 1000;
}
Thread.sleep(sleep);
}
......
--- special grammar and test cases ---------------------------------------------------------------------------------------------
create table script.public.x(a int);
> ok
select * from script.PUBLIC.x;
> A
> -
> rows: 0
create index script.public.idx on script.public.x(a);
> ok
drop table script.public.x;
> ok
create table t1 (i int);
> ok
......
create table test_table(column_a integer);
insert into test_table values(1);
create view test_view AS SELECT * FROM (SELECT DISTINCT * FROM test_table) AS subquery;
select * FROM test_view;
> 1;
drop view test_view;
drop table test_table;
CREATE TABLE TEST(ID INT);
INSERT INTO TEST VALUES(1);
CREATE VIEW TEST_VIEW AS SELECT COUNT(ID) X FROM TEST;
explain SELECT * FROM TEST_VIEW WHERE X>1;
DROP VIEW TEST_VIEW;
DROP TABLE TEST;
create table test1(id int);
insert into test1 values(1), (1), (2), (3);
select sum(C0) from (select count(*) AS C0 from (select distinct * from test1) as temp);
......
......@@ -96,7 +96,7 @@ public class TestExit extends TestBase implements DatabaseEventListener {
public void diskSpaceIsLow(long stillAvailable) throws SQLException {
}
public void exceptionThrown(SQLException e) {
public void exceptionThrown(SQLException e, String sql) {
}
public void closingDatabase() {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论