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

Merge pull request #113 from h2database/master

Prepare "transfer ownership"
.checkstyle
.classpath
.project
.settings
benchmark.html
bin
data
docs
ext
error.*
......
......@@ -912,7 +912,8 @@ COMMIT TRANSACTION XID_TEST
"Commands (Other)","GRANT RIGHT","
GRANT { SELECT | INSERT | UPDATE | DELETE | ALL } [,...] ON
tableName [,...] TO { PUBLIC | userName | roleName }
{ { SCHEMA schemaName } | { tableName [,...] } }
TO { PUBLIC | userName | roleName }
","
Grants rights for a table to a user or role.
......@@ -963,7 +964,8 @@ PREPARE COMMIT XID_TEST
"Commands (Other)","REVOKE RIGHT","
REVOKE { SELECT | INSERT | UPDATE | DELETE | ALL } [,...] ON
tableName [,...] FROM { PUBLIC | userName | roleName }
{ { SCHEMA schemaName } | { tableName [,...] } }
FROM { PUBLIC | userName | roleName }
","
Removes rights for a table from a user or role.
......
......@@ -191,10 +191,11 @@ Afterwards, you can include the database in your Maven 2 project as a dependency
To create an Eclipse project for H2, use the following steps:
</p>
<ul><li>Install Subversion and <a href="http://www.eclipse.org">Eclipse</a>.
</li><li>Get the H2 source code from the Subversion repository:<br />
<code>svn checkout http://h2database.googlecode.com/svn/trunk h2database-read-only</code>
</li><li>Download all dependencies (Windows):<br />
<code>build.bat download</code>
</li><li>Get the H2 source code from Github:<br />
<code>git clone https://github.com/h2database/h2database</code>
</li><li>Download all dependencies:<br />
<code>build.bat download</code>(Windows)<br />
<code>./build.sh download</code>(otherwise)<br />
</li><li>In Eclipse, create a new Java project from existing source code:
<code>File, New, Project, Java Project, Create project from existing source</code>.
</li><li>Select the <code>h2</code> folder, click <code>Next</code> and <code>Finish</code>.
......@@ -251,7 +252,7 @@ If you like to provide patches, please consider the following guidelines to simp
<p>
For legal reasons, patches need to be public in the form of an email to the
<a href="http://groups.google.com/group/h2-database">group</a>, or in the form
of an <a href="http://code.google.com/p/h2database/issues/list">issue report or attachment</a>.
of an <a href="https://github.com/h2database/h2database/issues">issue report or attachment</a>.
Significant contributions need to include the following statement:
</p>
<p>
......@@ -284,7 +285,7 @@ or if you have a feature request:
Please keep test cases as simple and short as possible,
but so that the problem can still be reproduced.
As a template, use:
<a href="http://h2database.googlecode.com/svn/trunk/h2/src/test/org/h2/samples/HelloWorld.java">HelloWorld.java</a>.
<a href="https://github.com/h2database/h2database/tree/master/h2/src/test/org/h2/samples/HelloWorld.java">HelloWorld.java</a>.
Method that simply call other methods should be avoided,
as well as unnecessary exception handling.
Please use the JDBC API and no external tools or libraries.
......@@ -297,7 +298,7 @@ or if you have a feature request:
<a href="http://groups.google.com/group/h2-database">Google Group</a>
for questions or if you are not sure it's a bug.
If you are sure it's a bug, you can create an
<a href="http://code.google.com/p/h2database/issues/list">issue</a>,
<a href="https://github.com/h2database/h2database/issues">issue</a>,
but you don't need to (sending an email to the group is enough).
Please note that only few people monitor the issue tracking system.
</li><li>For out-of-memory problems, please analyze the problem yourself first,
......
......@@ -20,7 +20,22 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>Java 8 compatibility for "regexp_replace".
<ul><li>Issue 609: the spatial index did not support NULL.
</li><li>Granting a schema is now supported.
</li><li>Linked tables did not work when a function-based index is present (Oracle).
</li><li>Creating a user with a null password, salt, or hash threw a NullPointerException.
</li><li>Foreign key: don't add a single column index if column
is leading key of existing index.
</li><li>Pull request #4: Creating and removing temporary tables was getting
slower and slower over time, because an internal object id was allocated but
never de-allocated.
</li><li>Issue 609: the spatial index did not support NULL with update and delete operations.
</li><li>Pull request #2: Add external metadata type support (table type "external")
</li><li>MS SQL Server: the CONVERT method did not work in views
and derived tables.
</li><li>Java 8 compatibility for "regexp_replace".
</li><li>When in cluster mode, and one of the nodes goes down,
we need to log the problem with priority "error", not "debug"
</li></ul>
<h2>Version 1.4.187 Beta (2015-04-10)</h2>
......@@ -434,4 +449,3 @@ Change Log
</li></ul>
<!-- [close] { --></div></td></tr></table><!-- } --><!-- analytics --></body></html>
......@@ -109,7 +109,7 @@ li {
<h2>Using H2</h2>
<ul><li><a href="http://h2database.com">H2</a> is
<a href="http://code.google.com/p/h2database/source">open source</a>,
<a href="https://github.com/h2database/h2database">open source</a>,
<a href="license.html">free to use and distribute</a>.
</li><li><a href="http://h2database.com/html/download.html">Download</a>:
<a href="http://repo1.maven.org/maven2/com/h2database/h2/${version}/h2-${version}.jar" class="link">jar</a>,
......
......@@ -33,7 +33,7 @@ Downloads
<a href="http://www.h2database.com/h2-${stableVersionDate}.zip">Platform-Independent Zip</a><br />
</p>
<h3>Download Mirror and Older Versions</h3>
<h3>Old Versions</h3>
<p>
<a href="http://code.google.com/p/h2database/downloads/list">Platform-Independent Zip</a><br />
</p>
......@@ -57,9 +57,9 @@ Downloads
<a href="http://h2database.com/h2mig_pagestore_addon.jar">Upgrade database from 1.1 to the current version</a>
</p>
<h3>Subversion Source Repository</h3>
<h3>Git Source Repository</h3>
<p>
<a href="http://code.google.com/p/h2database/source">Google Code</a>
<a href="https://github.com/h2database/h2database">Github</a>
</p>
<p>
......
......@@ -91,7 +91,7 @@ Here is the list of known and confirmed issues:
This problem is solved in Install4j 4.1.4.
</li></ul>
<p>
For a complete list, see <a href="http://code.google.com/p/h2database/issues/list">Open Issues</a>.
For a complete list, see <a href="https://github.com/h2database/h2database/issues">Open Issues</a>.
</p>
<h3 id="open_source">Is this Database Engine Open Source?</h3>
......
......@@ -371,7 +371,7 @@ H2 1.3,
*9 When using MVCC (multi version concurrency).<br />
*10 Derby and HSQLDB
<a href="http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29">don't hide data patterns well</a>.<br />
*11 The MULTI_THREADED option is not enabled by default, and not yet supported when using MVCC.<br />
*11 The MULTI_THREADED option is not enabled by default, and with version 1.3.x not supported when using MVCC.<br />
*12 Derby doesn't support the <code>EXPLAIN</code> statement, but it supports runtime statistics and retrieving statement execution plans.<br />
*13 Derby doesn't support the syntax <code>LIMIT .. [OFFSET ..]</code>, however it supports <code>FETCH FIRST .. ROW[S] ONLY</code>.<br />
*14 Using collations.
......@@ -853,15 +853,17 @@ other processes (that could reside on other computers as well) connect to the se
<h3>Multithreading Support</h3>
<p>
This database is multithreading-safe. That means, if an application is multi-threaded, it does not need
to worry about synchronizing access to the database. Internally, most requests to the same database
are synchronized. That means an application can use multiple threads that access the same database
at the same time, however if one thread executes a long running query, the other threads
need to wait.
This database is multithreading-safe.
If an application is multi-threaded, it does not need to worry about synchronizing access to the database.
An application should normally use one connection per thread.
This database synchronizes access to the same connection, but other databases may not do this.
To get higher concurrency, you need to use multiple connections.
</p>
<p>
An application should normally use one connection per thread. This database synchronizes
access to the same connection, but other databases may not do this.
By default, requests to the same database are synchronized.
That means an application can use multiple threads that access the same database
at the same time, however if one thread executes a long running query, the other threads need to wait.
To enable concurrent database usage, see the setting <code>MULTI_THREADED</code>.
</p>
<h3>Locking, Lock-Timeout, Deadlocks</h3>
......@@ -1382,7 +1384,7 @@ a regular database.
<p>
If the database is larger than a few megabytes, performance is much better if the database file is split into multiple smaller files,
because random access in compressed files is not possible.
See also the sample application <a href="http://code.google.com/p/h2database/source/browse/trunk/h2/src/test/org/h2/samples/ReadOnlyDatabaseInZip.java">ReadOnlyDatabaseInZip</a>.
See also the sample application <a href="https://github.com/h2database/h2database/tree/master/h2/src/test/org/h2/samples/ReadOnlyDatabaseInZip.java">ReadOnlyDatabaseInZip</a>.
</p>
<h3>Opening a Corrupted Database</h3>
......
......@@ -55,11 +55,6 @@
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="IllegalInstantiation"/>
<module name="RedundantThrows">
<property name="allowUnchecked" value="true"/>
<property name="allowSubclasses" value="true"/>
<property name="suppressLoadErrors" value="true"/>
</module>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="InterfaceIsType"/>
......
......@@ -15,8 +15,8 @@
</license>
</licenses>
<scm>
<connection>scm:svn:http://h2database.googlecode.com/svn/trunk</connection>
<url>http://h2database.googlecode.com/svn/trunk</url>
<connection>scm:git:https://github.com/h2database/h2database</connection>
<url>https://github.com/h2database/h2database</url>
</scm>
<developers>
<developer>
......
......@@ -15,8 +15,8 @@
</license>
</licenses>
<scm>
<connection>scm:svn:http://h2database.googlecode.com/svn/trunk</connection>
<url>http://h2database.googlecode.com/svn/trunk</url>
<connection>scm:git:https://github.com/h2database/h2database</connection>
<url>https://github.com/h2database/h2database</url>
</scm>
<developers>
<developer>
......
......@@ -27,7 +27,7 @@ Newsletter: prepare (always to BCC)
Newsletter: send to h2-database-jp@googlegroups.com; h2-database@googlegroups.com; h2database-news@googlegroups.com; ...
Add to http://twitter.com
- tweet: add @geospatialnews for the new geometry type and disk spatial index
Close bugs: http://code.google.com/p/h2database/issues/list
Close bugs: https://github.com/h2database/h2database/issues
Update statistics
......@@ -4262,10 +4262,15 @@ public class Parser {
}
if (tableClauseExpected) {
if (readIf("ON")) {
do {
Table table = readTableOrView();
command.addTable(table);
} while (readIf(","));
if (readIf("SCHEMA")) {
Schema schema = database.getSchema(readAliasIdentifier());
command.setSchema(schema);
} else {
do {
Table table = readTableOrView();
command.addTable(table);
} while (readIf(","));
}
}
}
if (operationType == CommandInterface.GRANT) {
......
......@@ -199,15 +199,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
isOwner = true;
index.getIndexType().setBelongsToConstraint(true);
} else {
if (db.isStarting()) {
// before version 1.3.176, an existing index was used:
// must do the same to avoid
// Unique index or primary key violation:
// "PRIMARY KEY ON """".PAGE_INDEX"
index = getIndex(table, indexColumns, true);
} else {
index = getIndex(table, indexColumns, false);
}
index = getIndex(table, indexColumns, true);
if (index == null) {
index = createIndex(table, indexColumns, false);
isOwner = true;
......
......@@ -12,8 +12,6 @@ import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.expression.Expression;
import org.h2.message.DbException;
import org.h2.security.SHA256;
import org.h2.util.StringUtils;
/**
* This class represents the statements
......@@ -63,15 +61,6 @@ public class AlterUser extends DefineCommand {
this.password = password;
}
private char[] getCharArray(Expression e) {
return e.optimize(session).getValue(session).getString().toCharArray();
}
private byte[] getByteArray(Expression e) {
return StringUtils.convertHexToBytes(
e.optimize(session).getValue(session).getString());
}
@Override
public int update() {
session.commit(true);
......@@ -82,12 +71,9 @@ public class AlterUser extends DefineCommand {
session.getUser().checkAdmin();
}
if (hash != null && salt != null) {
user.setSaltAndHash(getByteArray(salt), getByteArray(hash));
CreateUser.setSaltAndHash(user, session, salt, hash);
} else {
String name = newName == null ? user.getName() : newName;
char[] passwordChars = getCharArray(password);
byte[] userPasswordHash = SHA256.getKeyPasswordHash(name, passwordChars);
user.setUserPasswordHash(userPasswordHash);
CreateUser.setPassword(user, session, password);
}
break;
case CommandInterface.ALTER_USER_RENAME:
......
......@@ -45,13 +45,41 @@ public class CreateUser extends DefineCommand {
this.password = password;
}
private char[] getCharArray(Expression e) {
return e.optimize(session).getValue(session).getString().toCharArray();
/**
* Set the salt and hash for the given user.
*
* @param user the user
* @param session the session
* @param salt the salt
* @param hash the hash
*/
static void setSaltAndHash(User user, Session session, Expression salt, Expression hash) {
user.setSaltAndHash(getByteArray(session, salt), getByteArray(session, hash));
}
private byte[] getByteArray(Expression e) {
return StringUtils.convertHexToBytes(
e.optimize(session).getValue(session).getString());
private static byte[] getByteArray(Session session, Expression e) {
String s = e.optimize(session).getValue(session).getString();
return s == null ? new byte[0] : StringUtils.convertHexToBytes(s);
}
/**
* Set the password for the given user.
*
* @param user the user
* @param session the session
* @param password the password
*/
static void setPassword(User user, Session session, Expression password) {
String pwd = password.optimize(session).getValue(session).getString();
char[] passwordChars = pwd == null ? new char[0] : pwd.toCharArray();
byte[] userPasswordHash;
String userName = user.getName();
if (userName.length() == 0 && passwordChars.length == 0) {
userPasswordHash = new byte[0];
} else {
userPasswordHash = SHA256.getKeyPasswordHash(userName, passwordChars);
}
user.setUserPasswordHash(userPasswordHash);
}
@Override
......@@ -73,16 +101,9 @@ public class CreateUser extends DefineCommand {
user.setAdmin(admin);
user.setComment(comment);
if (hash != null && salt != null) {
user.setSaltAndHash(getByteArray(salt), getByteArray(hash));
setSaltAndHash(user, session, salt, hash);
} else if (password != null) {
char[] passwordChars = getCharArray(password);
byte[] userPasswordHash;
if (userName.length() == 0 && passwordChars.length == 0) {
userPasswordHash = new byte[0];
} else {
userPasswordHash = SHA256.getKeyPasswordHash(userName, passwordChars);
}
user.setUserPasswordHash(userPasswordHash);
setPassword(user, session, password);
} else {
throw DbException.throwInternalError();
}
......
......@@ -10,11 +10,13 @@ import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.Right;
import org.h2.engine.RightOwner;
import org.h2.engine.Role;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.schema.Schema;
import org.h2.table.Table;
import org.h2.util.New;
......@@ -31,6 +33,7 @@ public class GrantRevoke extends DefineCommand {
private int operationType;
private int rightMask;
private final ArrayList<Table> tables = New.arrayList();
private Schema schema;
private RightOwner grantee;
public GrantRevoke(Session session) {
......@@ -105,18 +108,25 @@ public class GrantRevoke extends DefineCommand {
}
private void grantRight() {
Database db = session.getDatabase();
if (schema != null) {
grantRight(schema);
}
for (Table table : tables) {
Right right = grantee.getRightForTable(table);
if (right == null) {
int id = getObjectId();
right = new Right(db, id, grantee, rightMask, table);
grantee.grantRight(table, right);
db.addDatabaseObject(session, right);
} else {
right.setRightMask(right.getRightMask() | rightMask);
db.updateMeta(session, right);
}
grantRight(table);
}
}
private void grantRight(DbObject object) {
Database db = session.getDatabase();
Right right = grantee.getRightForObject(object);
if (right == null) {
int id = getObjectId();
right = new Right(db, id, grantee, rightMask, object);
grantee.grantRight(object, right);
db.addDatabaseObject(session, right);
} else {
right.setRightMask(right.getRightMask() | rightMask);
db.updateMeta(session, right);
}
}
......@@ -139,23 +149,31 @@ public class GrantRevoke extends DefineCommand {
}
private void revokeRight() {
if (schema != null) {
revokeRight(schema);
}
for (Table table : tables) {
Right right = grantee.getRightForTable(table);
if (right == null) {
continue;
}
int mask = right.getRightMask();
int newRight = mask & ~rightMask;
Database db = session.getDatabase();
if (newRight == 0) {
db.removeDatabaseObject(session, right);
} else {
right.setRightMask(newRight);
db.updateMeta(session, right);
}
revokeRight(table);
}
}
private void revokeRight(DbObject object) {
Right right = grantee.getRightForObject(object);
if (right == null) {
return;
}
int mask = right.getRightMask();
int newRight = mask & ~rightMask;
Database db = session.getDatabase();
if (newRight == 0) {
db.removeDatabaseObject(session, right);
} else {
right.setRightMask(newRight);
db.updateMeta(session, right);
}
}
private void revokeRole(Role grantedRole) {
Right right = grantee.getRightForRole(grantedRole);
if (right == null) {
......@@ -179,6 +197,15 @@ public class GrantRevoke extends DefineCommand {
tables.add(table);
}
/**
* Set the specified schema
*
* @param schema the schema
*/
public void setSchema(Schema schema) {
this.schema = schema;
}
@Override
public int getType() {
return operationType;
......
......@@ -352,13 +352,20 @@ public class ScriptCommand extends ScriptBase {
}
// Generate GRANT ...
for (Right right : db.getAllRights()) {
Table table = right.getGrantedTable();
if (table != null) {
if (excludeSchema(table.getSchema())) {
continue;
}
if (excludeTable(table)) {
continue;
DbObject object = right.getGrantedObject();
if (object != null) {
if (object instanceof Schema) {
if (excludeSchema((Schema) object)) {
continue;
}
} else if (object instanceof Table) {
Table table = (Table) object;
if (excludeSchema(table.getSchema())) {
continue;
}
if (excludeTable(table)) {
continue;
}
}
}
add(right.getCreateSQL(), false);
......
......@@ -919,7 +919,6 @@ public class Database implements DataHandler {
// the moment
session.log(meta, UndoLogRecord.DELETE, found);
}
objectIds.clear(id);
if (SysProperties.CHECK) {
checkMetaFree(session, id);
}
......@@ -929,6 +928,7 @@ public class Database implements DataHandler {
meta.unlock(session);
session.unlock(meta);
}
objectIds.clear(id);
}
}
......
......@@ -7,6 +7,7 @@ package org.h2.engine;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.schema.Schema;
import org.h2.table.Table;
/**
......@@ -46,10 +47,25 @@ public class Right extends DbObjectBase {
*/
public static final int ALL = SELECT | DELETE | INSERT | UPDATE;
/**
* To whom the right is granted.
*/
private RightOwner grantee;
/**
* The granted role, or null if a right was granted.
*/
private Role grantedRole;
/**
* The granted right.
*/
private int grantedRight;
private Table grantedTable;
private RightOwner grantee;
/**
* The object. If the right is global, this is null.
*/
private DbObject grantedObject;
public Right(Database db, int id, RightOwner grantee, Role grantedRole) {
initDbObjectBase(db, id, "RIGHT_" + id, Trace.USER);
......@@ -58,11 +74,11 @@ public class Right extends DbObjectBase {
}
public Right(Database db, int id, RightOwner grantee, int grantedRight,
Table grantedRightOnTable) {
DbObject grantedObject) {
initDbObjectBase(db, id, "" + id, Trace.USER);
this.grantee = grantee;
this.grantedRight = grantedRight;
this.grantedTable = grantedRightOnTable;
this.grantedObject = grantedObject;
}
private static boolean appendRight(StringBuilder buff, int right, int mask,
......@@ -97,8 +113,8 @@ public class Right extends DbObjectBase {
return grantedRole;
}
public Table getGrantedTable() {
return grantedTable;
public DbObject getGrantedObject() {
return grantedObject;
}
public DbObject getGrantee() {
......@@ -112,14 +128,22 @@ public class Right extends DbObjectBase {
@Override
public String getCreateSQLForCopy(Table table, String quotedName) {
return getCreateSQLForCopy(table);
}
private String getCreateSQLForCopy(DbObject object) {
StringBuilder buff = new StringBuilder();
buff.append("GRANT ");
if (grantedRole != null) {
buff.append(grantedRole.getSQL());
} else {
buff.append(getRights());
if (table != null) {
buff.append(" ON ").append(table.getSQL());
if (object != null) {
if (object instanceof Schema) {
buff.append(" ON SCHEMA ").append(object.getSQL());
} else if (object instanceof Table) {
buff.append(" ON ").append(object.getSQL());
}
}
}
buff.append(" TO ").append(grantee.getSQL());
......@@ -128,7 +152,7 @@ public class Right extends DbObjectBase {
@Override
public String getCreateSQL() {
return getCreateSQLForCopy(grantedTable, null);
return getCreateSQLForCopy(grantedObject);
}
@Override
......@@ -138,14 +162,14 @@ public class Right extends DbObjectBase {
@Override
public void removeChildrenAndResources(Session session) {
if (grantedTable != null) {
grantee.revokeRight(grantedTable);
} else {
if (grantedRole != null) {
grantee.revokeRole(grantedRole);
} else {
grantee.revokeRight(grantedObject);
}
database.removeMeta(session, getId());
grantedRole = null;
grantedTable = null;
grantedObject = null;
grantee = null;
invalidate();
}
......
......@@ -23,7 +23,7 @@ public abstract class RightOwner extends DbObjectBase {
/**
* The map of granted rights.
*/
private HashMap<Table, Right> grantedRights;
private HashMap<DbObject, Right> grantedRights;
protected RightOwner(Database database, int id, String name,
String traceModule) {
......@@ -55,7 +55,9 @@ public abstract class RightOwner extends DbObjectBase {
/**
* Check if a right is already granted to this object or to objects that
* were granted to this object.
* were granted to this object. The rights for schemas takes
* precedence over rights of tables, in other words, the rights of schemas
* will be valid for every each table in the related schema.
*
* @param table the table to check
* @param rightMask the right mask to check
......@@ -64,6 +66,14 @@ public abstract class RightOwner extends DbObjectBase {
boolean isRightGrantedRecursive(Table table, int rightMask) {
Right right;
if (grantedRights != null) {
if (table != null) {
right = grantedRights.get(table.getSchema());
if (right != null) {
if ((right.getRightMask() & rightMask) == rightMask) {
return true;
}
}
}
right = grantedRights.get(table);
if (right != null) {
if ((right.getRightMask() & rightMask) == rightMask) {
......@@ -85,26 +95,26 @@ public abstract class RightOwner extends DbObjectBase {
* Grant a right for the given table. Only one right object per table is
* supported.
*
* @param table the table
* @param object the object (table or schema)
* @param right the right
*/
public void grantRight(Table table, Right right) {
public void grantRight(DbObject object, Right right) {
if (grantedRights == null) {
grantedRights = New.hashMap();
}
grantedRights.put(table, right);
grantedRights.put(object, right);
}
/**
* Revoke the right for the given table.
* Revoke the right for the given object (table or schema).
*
* @param table the table
* @param object the object
*/
void revokeRight(Table table) {
void revokeRight(DbObject object) {
if (grantedRights == null) {
return;
}
grantedRights.remove(table);
grantedRights.remove(object);
if (grantedRights.size() == 0) {
grantedRights = null;
}
......@@ -143,16 +153,16 @@ public abstract class RightOwner extends DbObjectBase {
}
/**
* Get the 'grant table' right of this object.
* Get the 'grant schema' right of this object.
*
* @param table the granted table
* @param object the granted object (table or schema)
* @return the right or null if the right has not been granted
*/
public Right getRightForTable(Table table) {
public Right getRightForObject(DbObject object) {
if (grantedRights == null) {
return null;
}
return grantedRights.get(table);
return grantedRights.get(object);
}
/**
......
......@@ -2479,9 +2479,15 @@ public class Function extends Expression implements FunctionCall {
break;
}
case CONVERT: {
buff.append(args[0].getSQL()).append(',').
append(new Column(null, dataType, precision,
if (database.getMode().swapConvertFunctionParameters) {
buff.append(new Column(null, dataType, precision,
scale, displaySize).getCreateSQL()).
append(',').append(args[0].getSQL());
} else {
buff.append(args[0].getSQL()).append(',').
append(new Column(null, dataType, precision,
scale, displaySize).getCreateSQL());
}
break;
}
case EXTRACT: {
......
......@@ -769,10 +769,8 @@ public class JdbcDatabaseMetaData extends TraceObject implements
}
/**
* Gets the list of table types. This call returns a result set with three
* records: "SYSTEM TABLE", "TABLE", "and "VIEW".
* The result set is sorted by TABLE_TYPE.
*
* Gets the list of table types. This call returns a result set with five
* records: "SYSTEM TABLE", "TABLE", "VIEW", "TABLE LINK" and "EXTERNAL".
* <ul>
* <li>1 TABLE_TYPE (String) table type
* </li></ul>
......
......@@ -6,7 +6,6 @@
package org.h2.mvstore;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
......@@ -471,14 +470,18 @@ public class Page {
public void setChild(int index, Page c) {
if (c == null) {
long oldCount = children[index].count;
children = Arrays.copyOf(children, children.length);
// this is slightly slower:
// children = Arrays.copyOf(children, children.length);
children = children.clone();
PageReference ref = new PageReference(null, 0, 0);
children[index] = ref;
totalCount -= oldCount;
} else if (c != children[index].page ||
c.getPos() != children[index].pos) {
long oldCount = children[index].count;
children = Arrays.copyOf(children, children.length);
// this is slightly slower:
// children = Arrays.copyOf(children, children.length);
children = children.clone();
PageReference ref = new PageReference(c, c.pos, c.totalCount);
children[index] = ref;
totalCount += c.totalCount - oldCount;
......@@ -492,7 +495,9 @@ public class Page {
* @param key the new key
*/
public void setKey(int index, Object key) {
keys = Arrays.copyOf(keys, keys.length);
// this is slightly slower:
// keys = Arrays.copyOf(keys, keys.length);
keys = keys.clone();
Object old = keys[index];
DataType keyType = map.getKeyType();
int mem = keyType.getMemory(key);
......@@ -512,7 +517,9 @@ public class Page {
*/
public Object setValue(int index, Object value) {
Object old = values[index];
values = Arrays.copyOf(values, values.length);
// this is slightly slower:
// values = Arrays.copyOf(values, values.length);
values = values.clone();
DataType valueType = map.getValueType();
addMemory(valueType.getMemory(value) -
valueType.getMemory(old));
......
......@@ -6,7 +6,6 @@
package org.h2.mvstore.db;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
......@@ -125,7 +124,7 @@ public class MVSecondaryIndex extends BaseIndex implements MVIndex {
if (indexType.isUnique()) {
Value[] array = ((ValueArray) v).getList();
// don't change the original value
array = Arrays.copyOf(array, array.length);
array = array.clone();
array[keyColumns - 1] = ValueLong.get(Long.MIN_VALUE);
ValueArray unique = ValueArray.get(array);
SearchRow row = convertToSearchRow((ValueArray) v);
......
......@@ -171,6 +171,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
try {
Value old = map.remove(key);
if (old == null) {
old = map.remove(key);
throw DbException.get(ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1,
getSQL() + ": " + row.getKey());
}
......@@ -211,12 +212,9 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
}
private SpatialKey getKey(SearchRow row) {
if (row == null) {
return null;
}
Value v = row.getValue(columnIds[0]);
if (v == ValueNull.INSTANCE) {
return null;
return new SpatialKey(row.getKey());
}
Geometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy();
Envelope env = g.getEnvelopeInternal();
......
......@@ -10,6 +10,7 @@ import java.util.ArrayList;
import org.h2.mvstore.DataUtils;
import org.h2.mvstore.WriteBuffer;
import org.h2.mvstore.type.DataType;
import org.h2.util.New;
/**
* A spatial data type. This class supports up to 31 dimensions. Each dimension
......@@ -83,11 +84,12 @@ public class SpatialDataType implements DataType {
@Override
public void write(WriteBuffer buff, Object obj) {
if (obj == null) {
SpatialKey k = (SpatialKey) obj;
if (k.isNull()) {
buff.putVarInt(-1);
buff.putVarLong(k.getId());
return;
}
SpatialKey k = (SpatialKey) obj;
int flags = 0;
for (int i = 0; i < dimensions; i++) {
if (k.min(i) == k.max(i)) {
......@@ -108,7 +110,8 @@ public class SpatialDataType implements DataType {
public Object read(ByteBuffer buff) {
int flags = DataUtils.readVarInt(buff);
if (flags == -1) {
return null;
long id = DataUtils.readVarLong(buff);
return new SpatialKey(id);
}
float[] minMax = new float[dimensions * 2];
for (int i = 0; i < dimensions; i++) {
......@@ -136,6 +139,9 @@ public class SpatialDataType implements DataType {
public boolean isOverlap(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
if (a.isNull() || b.isNull()) {
return false;
}
for (int i = 0; i < dimensions; i++) {
if (a.max(i) < b.min(i) || a.min(i) > b.max(i)) {
return false;
......@@ -151,8 +157,11 @@ public class SpatialDataType implements DataType {
* @param add the value
*/
public void increaseBounds(Object bounds, Object add) {
SpatialKey b = (SpatialKey) bounds;
SpatialKey a = (SpatialKey) add;
SpatialKey b = (SpatialKey) bounds;
if (a.isNull() || b.isNull()) {
return;
}
for (int i = 0; i < dimensions; i++) {
b.setMin(i, Math.min(b.min(i), a.min(i)));
b.setMax(i, Math.max(b.max(i), a.max(i)));
......@@ -167,8 +176,11 @@ public class SpatialDataType implements DataType {
* @return the area
*/
public float getAreaIncrease(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
SpatialKey a = (SpatialKey) objA;
if (a.isNull() || b.isNull()) {
return 0;
}
float min = a.min(0);
float max = a.max(0);
float areaOld = max - min;
......@@ -196,6 +208,11 @@ public class SpatialDataType implements DataType {
float getCombinedArea(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
if (a.isNull()) {
return getArea(b);
} else if (b.isNull()) {
return getArea(a);
}
float area = 1;
for (int i = 0; i < dimensions; i++) {
float min = Math.min(a.min(i), b.min(i));
......@@ -205,6 +222,17 @@ public class SpatialDataType implements DataType {
return area;
}
private float getArea(SpatialKey a) {
if (a.isNull()) {
return 0;
}
float area = 1;
for (int i = 0; i < dimensions; i++) {
area *= a.max(i) - a.min(i);
}
return area;
}
/**
* Check whether a contains b.
*
......@@ -215,6 +243,9 @@ public class SpatialDataType implements DataType {
public boolean contains(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
if (a.isNull() || b.isNull()) {
return false;
}
for (int i = 0; i < dimensions; i++) {
if (a.min(i) > b.min(i) || a.max(i) < b.max(i)) {
return false;
......@@ -234,6 +265,9 @@ public class SpatialDataType implements DataType {
public boolean isInside(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
if (a.isNull() || b.isNull()) {
return false;
}
for (int i = 0; i < dimensions; i++) {
if (a.min(i) <= b.min(i) || a.max(i) >= b.max(i)) {
return false;
......@@ -249,8 +283,11 @@ public class SpatialDataType implements DataType {
* @return the bounding box
*/
Object createBoundingBox(Object objA) {
float[] minMax = new float[dimensions * 2];
SpatialKey a = (SpatialKey) objA;
if (a.isNull()) {
return a;
}
float[] minMax = new float[dimensions * 2];
for (int i = 0; i < dimensions; i++) {
minMax[i + i] = a.min(i);
minMax[i + i + 1] = a.max(i);
......@@ -267,6 +304,10 @@ public class SpatialDataType implements DataType {
* @return the indexes of the extremes
*/
public int[] getExtremes(ArrayList<Object> list) {
list = getNotNull(list);
if (list.size() == 0) {
return null;
}
SpatialKey bounds = (SpatialKey) createBoundingBox(list.get(0));
SpatialKey boundsInner = (SpatialKey) createBoundingBox(bounds);
for (int i = 0; i < dimensions; i++) {
......@@ -311,6 +352,27 @@ public class SpatialDataType implements DataType {
return new int[] { firstIndex, lastIndex };
}
ArrayList<Object> getNotNull(ArrayList<Object> list) {
ArrayList<Object> result = null;
for (Object o : list) {
SpatialKey a = (SpatialKey) o;
if (a.isNull()) {
result = New.arrayList();
break;
}
}
if (result == null) {
return list;
}
for (Object o : list) {
SpatialKey a = (SpatialKey) o;
if (!a.isNull()) {
result.add(a);
}
}
return result;
}
private void increaseMaxInnerBounds(Object bounds, Object add) {
SpatialKey b = (SpatialKey) bounds;
SpatialKey a = (SpatialKey) add;
......
......@@ -70,6 +70,10 @@ public class SpatialKey {
return id;
}
public boolean isNull() {
return minMax.length == 0;
}
@Override
public String toString() {
StringBuilder buff = new StringBuilder();
......
......@@ -299,7 +299,8 @@ COMMIT TRANSACTION transactionName
Sets the resolution of an in-doubt transaction to 'commit'."
"Commands (Other)","GRANT RIGHT","
GRANT { SELECT | INSERT | UPDATE | DELETE | ALL } [,...] ON
tableName [,...] TO { PUBLIC | userName | roleName }
{ { SCHEMA schemaName } | { tableName [,...] } }
TO { PUBLIC | userName | roleName }
","
Grants rights for a table to a user or role."
"Commands (Other)","GRANT ALTER ANY SCHEMA","
......@@ -320,7 +321,8 @@ PREPARE COMMIT newTransactionName
Prepares committing a transaction."
"Commands (Other)","REVOKE RIGHT","
REVOKE { SELECT | INSERT | UPDATE | DELETE | ALL } [,...] ON
tableName [,...] FROM { PUBLIC | userName | roleName }
{ { SCHEMA schemaName } | { tableName [,...] } }
FROM { PUBLIC | userName | roleName }
","
Removes rights for a table from a user or role."
"Commands (Other)","REVOKE ROLE","
......
......@@ -151,7 +151,7 @@ public class WebApp {
"jsp".equals(suffix)) {
cache = false;
mimeType = "text/html";
if (session == null && !file.startsWith(WebServer.TRANSFER)) {
if (session == null) {
session = server.createNewSession(hostAddr);
if (!"notAllowed.jsp".equals(file)) {
file = "index.do";
......@@ -239,8 +239,6 @@ public class WebApp {
file = autoCompleteList();
} else if ("tools.do".equals(file)) {
file = tools();
} else if ("transfer.do".equals(file)) {
file = "transfer.jsp";
} else {
file = "error.jsp";
}
......
......@@ -6,7 +6,6 @@
package org.h2.server.web;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
......@@ -32,7 +31,6 @@ import org.h2.message.DbException;
import org.h2.server.Service;
import org.h2.server.ShutdownHandler;
import org.h2.store.fs.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
......@@ -48,8 +46,6 @@ import org.h2.util.Utils;
*/
public class WebServer implements Service {
static final String TRANSFER = "transfer";
static final String[][] LANGUAGES = {
{ "cs", "\u010ce\u0161tina" },
{ "de", "Deutsch" },
......@@ -170,17 +166,6 @@ public class WebServer implements Service {
*/
byte[] getFile(String file) throws IOException {
trace("getFile <" + file + ">");
if (file.startsWith(TRANSFER + "/") && new File(TRANSFER).exists()) {
file = file.substring(TRANSFER.length() + 1);
if (!isSimpleName(file)) {
return null;
}
File f = new File(TRANSFER, file);
if (!f.exists()) {
return null;
}
return IOUtils.readBytesAndClose(new FileInputStream(f), -1);
}
byte[] data = Utils.getResource("/org/h2/server/web/res/" + file);
if (data == null) {
trace(" null");
......
......@@ -7,12 +7,9 @@ package org.h2.server.web;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Iterator;
......@@ -37,7 +34,6 @@ class WebThread extends WebApp implements Runnable {
protected final Socket socket;
private final Thread thread;
private InputStream input;
private int headerBytes;
private String ifModifiedSince;
WebThread(Socket socket, WebServer server) {
......@@ -210,12 +206,10 @@ class WebThread extends WebApp implements Runnable {
private String readHeaderLine() throws IOException {
StringBuilder buff = new StringBuilder();
while (true) {
headerBytes++;
int c = input.read();
if (c == -1) {
throw new IOException("Unexpected EOF");
} else if (c == '\r') {
headerBytes++;
if (input.read() == '\n') {
return buff.length() > 0 ? buff.toString() : null;
}
......@@ -320,7 +314,7 @@ class WebThread extends WebApp implements Runnable {
}
}
if (multipart) {
uploadMultipart(input, len);
// not supported
} else if (session != null && len > 0) {
byte[] bytes = DataUtils.newBytes(len);
for (int pos = 0; pos < len;) {
......@@ -332,45 +326,6 @@ class WebThread extends WebApp implements Runnable {
return keepAlive;
}
private void uploadMultipart(InputStream in, int len) throws IOException {
if (!new File(WebServer.TRANSFER).exists()) {
return;
}
String fileName = "temp.bin";
headerBytes = 0;
String boundary = readHeaderLine();
while (true) {
String line = readHeaderLine();
if (line == null) {
break;
}
int index = line.indexOf("filename=\"");
if (index > 0) {
fileName = line.substring(index +
"filename=\"".length(), line.lastIndexOf('"'));
}
trace(" " + line);
}
if (!WebServer.isSimpleName(fileName)) {
return;
}
len -= headerBytes;
File file = new File(WebServer.TRANSFER, fileName);
OutputStream out = new FileOutputStream(file);
IOUtils.copy(in, out, len);
out.close();
// remove the boundary
RandomAccessFile f = new RandomAccessFile(file, "rw");
int testSize = (int) Math.min(f.length(), Constants.IO_BUFFER_SIZE);
f.seek(f.length() - testSize);
byte[] bytes = DataUtils.newBytes(Constants.IO_BUFFER_SIZE);
f.readFully(bytes, 0, testSize);
String s = new String(bytes, "ASCII");
int x = s.lastIndexOf(boundary);
f.setLength(f.length() - testSize + x - 2);
f.close();
}
private static String getHeaderLineValue(String line) {
return line.substring(line.indexOf(':') + 1).trim();
}
......
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!--
Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
and the EPL 1.0 (http://h2database.com/html/license.html).
Initial Developer: H2 Group
-->
<html><head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>Upload</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css" />
</head>
<body>
<form action="transfer.do" method="post" enctype="multipart/form-data">
<p>
File:<br />
<input name="file" type="file" size="50" /><br /><br />
<input type="submit" value="Upload" />
</p>
</form>
</body></html>
......@@ -352,7 +352,7 @@ public class FileLock implements Runnable {
save();
sleep(2 * sleep);
if (!load().equals(properties)) {
throw getExceptionAlreadyInUse("Locked by another process");
throw getExceptionAlreadyInUse("Locked by another process: " + fileName);
}
FileUtils.delete(fileName);
if (!FileUtils.createFile(fileName)) {
......
......@@ -246,7 +246,7 @@ public class LobStorageMap implements LobStorageInterface {
throw DbException.throwInternalError("Length is different");
}
Object[] value = lobMap.get(oldLobId);
value = Arrays.copyOf(value, value.length);
value = value.clone();
byte[] streamStoreId = (byte[]) value[0];
long lobId = generateLobId();
value[1] = tableId;
......
......@@ -474,7 +474,7 @@ public class FilePathEncrypt extends FilePathWrapper {
updateTweak(tweak);
if (i + CIPHER_BLOCK_SIZE + CIPHER_BLOCK_SIZE > len &&
i + CIPHER_BLOCK_SIZE < len) {
tweakEnd = Arrays.copyOf(tweak, CIPHER_BLOCK_SIZE);
tweakEnd = tweak.clone();
updateTweak(tweak);
}
}
......
......@@ -312,14 +312,14 @@ public class FileUtils {
}
/**
* Try to delete a file (ignore errors).
* Try to delete a file or directory (ignoring errors).
*
* @param fileName the file name
* @param path the file or directory name
* @return true if it worked
*/
public static boolean tryDelete(String fileName) {
public static boolean tryDelete(String path) {
try {
FilePath.get(fileName).delete();
FilePath.get(path).delete();
return true;
} catch (Exception e) {
return false;
......
......@@ -893,6 +893,7 @@ public class MetaTable extends Table {
add(rows, Table.TABLE_LINK);
add(rows, Table.SYSTEM_TABLE);
add(rows, Table.VIEW);
add(rows, Table.EXTERNAL_TABLE_ENGINE);
break;
}
case CATALOGS: {
......@@ -1127,8 +1128,19 @@ public class MetaTable extends Table {
String rightType = grantee.getType() == DbObject.USER ?
"USER" : "ROLE";
if (role == null) {
Table granted = r.getGrantedTable();
String tableName = identifier(granted.getName());
DbObject object = r.getGrantedObject();
Schema schema = null;
Table table = null;
if (object != null) {
if (object instanceof Schema) {
schema = (Schema) object;
} else if (object instanceof Table) {
table = (Table) object;
schema = table.getSchema();
}
}
String tableName = (table != null) ? identifier(table.getName()) : "";
String schemaName = (schema != null) ? identifier(schema.getName()) : "";
if (!checkIndex(session, tableName, indexFrom, indexTo)) {
continue;
}
......@@ -1142,9 +1154,9 @@ public class MetaTable extends Table {
// RIGHTS
r.getRights(),
// TABLE_SCHEMA
identifier(granted.getSchema().getName()),
schemaName,
// TABLE_NAME
identifier(granted.getName()),
tableName,
// ID
"" + r.getId()
);
......@@ -1374,7 +1386,11 @@ public class MetaTable extends Table {
}
case TABLE_PRIVILEGES: {
for (Right r : database.getAllRights()) {
Table table = r.getGrantedTable();
DbObject object = r.getGrantedObject();
if (!(object instanceof Table)) {
continue;
}
Table table = (Table) object;
if (table == null || hideTable(table, session)) {
continue;
}
......@@ -1389,7 +1405,11 @@ public class MetaTable extends Table {
}
case COLUMN_PRIVILEGES: {
for (Right r : database.getAllRights()) {
Table table = r.getGrantedTable();
DbObject object = r.getGrantedObject();
if (!(object instanceof Table)) {
continue;
}
Table table = (Table) object;
if (table == null || hideTable(table, session)) {
continue;
}
......
......@@ -385,7 +385,7 @@ public abstract class Table extends SchemaObjectBase {
}
ArrayList<Right> rights = database.getAllRights();
for (Right right : rights) {
if (right.getGrantedTable() == this) {
if (right.getGrantedObject() == this) {
children.add(right);
}
}
......@@ -510,7 +510,7 @@ public abstract class Table extends SchemaObjectBase {
database.removeSchemaObject(session, constraint);
}
for (Right right : database.getAllRights()) {
if (right.getGrantedTable() == this) {
if (right.getGrantedObject() == this) {
database.removeDatabaseObject(session, right);
}
}
......
......@@ -14,6 +14,7 @@ import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.h2.api.ErrorCode;
import org.h2.command.Prepared;
......@@ -334,7 +335,19 @@ public class TableLink extends Table {
return columnName;
}
private void addIndex(ArrayList<Column> list, IndexType indexType) {
private void addIndex(List<Column> list, IndexType indexType) {
// bind the index to the leading recognized columns in the index
// (null columns might come from a function-based index)
int firstNull = list.indexOf(null);
if (firstNull == 0) {
trace.info("Omitting linked index - no recognized columns.");
return;
} else if (firstNull > 0) {
trace.info("Unrecognized columns in linked index. " +
"Registering the index against the leading {0} " +
"recognized columns of {1} total columns.", firstNull, list.size());
list = list.subList(0, firstNull);
}
Column[] cols = new Column[list.size()];
list.toArray(cols);
Index index = new LinkedIndex(this, 0, IndexColumn.wrap(cols), indexType);
......
......@@ -45,14 +45,16 @@ public class Profiler implements Runnable {
"sun," +
"com.sun.," +
"com.google.common.," +
"com.mongodb."
"com.mongodb.," +
"org.bson.,"
).split(",");
private final String[] ignorePackages = (
"java," +
"sun," +
"com.sun.," +
"com.google.common.," +
"com.mongodb."
"com.mongodb.," +
"org.bson"
).split(",");
private final String[] ignoreThreads = (
"java.lang.Object.wait," +
......
......@@ -282,8 +282,11 @@ public class SourceCompiler {
.getStandardFileManager(null, null, null));
ArrayList<JavaFileObject> compilationUnits = new ArrayList<JavaFileObject>();
compilationUnits.add(new StringJavaFileObject(fullClassName, source));
JAVA_COMPILER.getTask(writer, fileManager, null, null,
// can not concurrently compile
synchronized (JAVA_COMPILER) {
JAVA_COMPILER.getTask(writer, fileManager, null, null,
null, compilationUnits).call();
}
String err = writer.toString();
throwSyntaxError(err);
try {
......@@ -333,7 +336,7 @@ public class SourceCompiler {
}.execute();
}
private static void javacSun(File javaFile) {
private static synchronized void javacSun(File javaFile) {
PrintStream old = System.err;
ByteArrayOutputStream buff = new ByteArrayOutputStream();
PrintStream temp = new PrintStream(buff);
......@@ -344,6 +347,7 @@ public class SourceCompiler {
Object javac = JAVAC_SUN.newInstance();
compile.invoke(javac, (Object) new String[] {
"-sourcepath", COMPILE_DIR,
// "-Xlint:unchecked",
"-d", COMPILE_DIR,
"-encoding", "UTF-8",
javaFile.getAbsolutePath() });
......
......@@ -95,7 +95,7 @@ $$<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:rdf="http://www.w3.org/19
<category rdf:resource="http://projects.apache.org/category/library"/>
<category rdf:resource="http://projects.apache.org/category/network-server"/>
<license rdf:resource="http://usefulinc.com/doap/licenses/mpl"/>
<bug-database rdf:resource="http://code.google.com/p/h2database/issues/list"/>
<bug-database rdf:resource="https://github.com/h2database/h2database/issues"/>
<download-page rdf:resource="http://h2database.com/html/download.html"/>
<shortdesc xml:lang="en">H2 Database Engine</shortdesc>
<description xml:lang="en">
......@@ -110,8 +110,8 @@ $$<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:rdf="http://www.w3.org/19
</description>
<repository>
<SVNRepository>
<browse rdf:resource="http://code.google.com/p/h2database/source/browse"/>
<location rdf:resource="http://h2database.googlecode.com/svn/trunk"/>
<browse rdf:resource="https://github.com/h2database/h2database"/>
<location rdf:resource="https://github.com/h2database/h2database"/>
</SVNRepository>
</repository>
<mailing-list rdf:resource="http://groups.google.com/group/h2-database"/>
......
......@@ -33,8 +33,8 @@ public class TestAlter extends TestBase {
@Override
public void test() throws Exception {
deleteDb("alter");
conn = getConnection("alter");
deleteDb(getTestName());
conn = getConnection(getTestName());
stat = conn.createStatement();
testAlterTableAlterColumnAsSelfColumn();
testAlterTableDropColumnWithReferences();
......@@ -49,7 +49,7 @@ public class TestAlter extends TestBase {
testAlterTableAddColumnAfter();
testAlterTableModifyColumn();
conn.close();
deleteDb("alter");
deleteDb(getTestName());
}
private void testAlterTableAlterColumnAsSelfColumn() throws SQLException {
......@@ -121,7 +121,7 @@ public class TestAlter extends TestBase {
stat.execute("alter table test alter id rename to id2");
// disconnect and reconnect
conn.close();
conn = getConnection("alter");
conn = getConnection(getTestName());
stat = conn.createStatement();
stat.execute("insert into test values(1)");
assertThrows(ErrorCode.CHECK_CONSTRAINT_VIOLATED_1, stat).
......
......@@ -145,6 +145,18 @@ public class TestCases extends TestBase {
"foreign key(a_id) references a(id)");
stat.execute("update a set x=200");
stat.execute("drop table if exists a, b");
stat.execute("drop all objects");
stat.execute("create table parent(id int primary key)");
stat.execute("create table child(id int, parent_id int, x int)");
stat.execute("create index y on child(parent_id, x)");
stat.execute("alter table child add constraint z " +
"foreign key(parent_id) references parent(id)");
ResultSet rs = stat.executeQuery(
"select * from information_schema.indexes where table_name = 'CHILD'");
while (rs.next()) {
assertEquals("Y", rs.getString("index_name"));
}
conn.close();
}
......
差异被折叠。
......@@ -771,3 +771,4 @@ young sweep clearer accounting disappeared donor oome ken jorissen nesterov
degradation failures fashion disjunctive mentioned conjunctive misses broke
authenticate orphaned registrations topology planner
zepfred frederico thimel arnaud manipulating strongly lots aquiles younger needing
unrecognized five omitting registering
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论