提交 51343b61 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 da206bee
......@@ -79,12 +79,12 @@ Helps building web applications.
<p><a href="http://wiki.blojsom.com">
Blojsom</a><br />
Java-based multi-blog, multi-user software package (Mac OS X Weblog Server)
Java-based multi-blog, multi-user software package (Mac OS X Weblog Server).
</p>
<p><a href="http://bmarks-portlet.sourceforge.net">
Bookmarks Portlet</a><br />
JSR168 compliant bookmarks management portlet application.
JSR 168 compliant bookmarks management portlet application.
</p>
<p><a href="http://www.claros.org">
......@@ -114,14 +114,20 @@ Database utility written in Java.
<p><a href="http://www.goldenstudios.or.id">
Golden T Studios</a><br />
Fun-to-play games with a simple interface.</p>
Fun-to-play games with a simple interface.
</p>
<p><a href="http://www.gs.sjts.co.jp">
Group Session</a><br />
Open source web groupware.
</p>
<p><a href="http://ha-jdbc.sourceforge.net/">
<p><a href="http://ha-jdbc.sourceforge.net">
HA-JDBC</a><br />
High-Availability JDBC: A JDBC proxy that provides light-weight, transparent, fault tolerant clustering capability to any underlying JDBC driver.
</p>
<p><a href="http://henplus.sourceforge.net/">
<p><a href="http://henplus.sourceforge.net">
HenPlus</a><br />
HenPlus is a SQL shell written in Java.
</p>
......@@ -133,7 +139,7 @@ Relational persistence for idiomatic Java (O-R mapping tool).
<p><a href="http://www.willuhn.de/projects/hibiscus/">
Hibicius</a><br />
Online Banking Client for the HBCI protocol
Online Banking Client for the HBCI protocol.
</p>
<p><a href="http://geosysin.iict.ch/irstv-trac/wiki/H2spatial/Why">
......@@ -148,7 +154,7 @@ Java-based Wiki engine.
<p><a href="http://dev.orf.at/trac/jala">
Jala</a><br />
Open source collection of JavaScript modules
Open source collection of JavaScript modules.
</p>
<p><a href="http://mywebpage.netscape.com/davidlbarron/javaplayer.html">
......@@ -168,7 +174,7 @@ Java persistent objects.
<p><a href="http://code.google.com/p/liftweb/">
Liftweb</a><br />
A Scala-based, secure, developer friendly web framework
A Scala-based, secure, developer friendly web framework.
</p>
<p><a href="http://liquibase.sourceforge.net">
......@@ -178,7 +184,8 @@ A tool to manage database changes and refactorings.
<p><a href="http://luntbuild.javaforge.com">
Luntbuild</a><br />
Build automation and management tool.</p>
Build automation and management tool.
</p>
<p><a href="http://www.source-code.biz/snippets/java/8.htm">
MiniConnectionPoolManager</a><br />
......@@ -235,7 +242,7 @@ ETL (Extract-Transform-Load) and script execution tool.
<p><a href="http://www.seasar.org">
Sesar</a><br />
Dependency Injection Container with Aspect Oriented Programming
Dependency Injection Container with Aspect Oriented Programming.
</p>
<p><a href="http://semmle.com">
......
......@@ -55,6 +55,7 @@ Roadmap
</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>Pluggable tracing system
</li><li>Full outer joins
</li><li>Procedural language / script language (Javascript)
</li><li>Change LOB mechanism (less files, keep index of lob files, point to files and row, delete unused files earlier, maybe bundle files into a tar file)
......@@ -249,7 +250,7 @@ Roadmap
</li><li>Support ANALYZE {TABLE|INDEX} tableName COMPUTE|ESTIMATE|DELETE STATISTICS ptnOption options
</li><li>Support Sequoia (Continuent.org)
</li><li>Dynamic length numbers / special methods for DataPage.writeByte / writeShort / Ronni Nielsen
</li><li>Pluggable tracing system, ThreadPool, (AvalonDB / deebee / Paul Hammant)
</li><li>Pluggable ThreadPool, (AvalonDB / deebee / Paul Hammant)
</li><li>Recursive Queries (see details)
</li><li>Use index on boolean flag (see details)
</li><li>Add build for embedded database only
......@@ -388,6 +389,11 @@ Roadmap
</li><li>Use ant 'get' to download dependencies
</li><li>Index usage for UPDATE ... WHERE .. IN (SELECT...)
</li><li>The RunScript tool should support interactive mode (reading from system in). Password using a second thread.
</li><li>Add regular javadocs to the homepage.
</li><li>The database should be kept open for a longer time when using the server mode.
</li><li>Javadocs: for each tool, add a copy & paste sample in the class level.
</li><li>Add google site search to web page.
</li><li>Javadocs: add @author tags.
</li></ul>
<h2>Not Planned</h2>
......
......@@ -32,10 +32,14 @@ Tutorial
Using OpenOffice Base</a><br />
<a href="#web_start">
Java Web Start / JNLP</a><br />
<a href="#connection_pool">
Using a Connection Pool</a><br />
<a href="#fulltext">
Fulltext Search</a><br />
<a href="#user_defined_variables">
User Defined Variables</a><br />
<a href="#date_time">
Date and Time</a><br />
<br /><a name="tutorial_starting_h2_console"></a>
<h2>Starting and Using the H2 Console</h2>
......@@ -547,6 +551,19 @@ Example permission tags:
</pre>
</p>
<br /><a name="connection_pool"></a>
<h2>Using a Connection Pool</h2>
<p>
For many databases, opening a connection is slow, and it is a good idea to use a connection pool
to re-use connections. For H2 however opening a connection usually is fast if the database is already
open. Using a connection pool manager for H2 actually slows down the process a bit, except if
file encryption is used (in this case opening a connection is about half as fast are using
a connection pool). A simple connection pool is:
<a href="http://www.source-code.biz/snippets/java/8.htm">Mini Connection Pool Manager</a>.
There are other, more complex connection pools available, for example
<a href="http://jakarta.apache.org/commons/dbcp/">DBCP</a>.
</p>
<br /><a name="fulltext"></a>
<h2>Fulltext Search</h2>
<p>
......@@ -642,4 +659,19 @@ of the value assigned to it, that means it is not necessary (or possible) to dec
There are no restrictions on the assigned values, large objects (LOBs) are supported as well.
</p>
<br /><a name="date_time"></a>
<h2>Date and Time</h2>
<p>
Date, time and timestamp values support ISO 8601 formatting, including timezone:
<pre>
CALL TIMESTAMP '2008-01-01 12:00:00+01:00';
</pre>
If the timezone is not set, the value is parsed using the current timezone setting of the system.
Date and time information is stored in H2 database files in GMT (Greenwich Mean Time).
If the database is opened using another system timezone, the date and time will change accordingly.
If you want to move a database from one timezone to the other and don't want this to happen,
you need to create a SQL script file using the SCRIPT command or Script tool, and then load
the database using the RUNSCRIPT command or the RunScript tool in the new timezone.
</p>
</div></td></tr></table><!-- analytics --></body></html>
......@@ -478,7 +478,7 @@ public class Parser {
private Prepared parseBackup() throws SQLException {
BackupCommand command = new BackupCommand(session);
read("TO");
command.setFileName(readString());
command.setFileName(readExpression());
return command;
}
......@@ -3514,15 +3514,15 @@ public class Parser {
command.setUserName(readUniqueIdentifier());
command.setComment(readCommentIf());
if (readIf("PASSWORD")) {
command.setPassword(readString());
command.setPassword(readExpression());
} else if (readIf("SALT")) {
command.setSalt(readString());
command.setSalt(readExpression());
read("HASH");
command.setHash(readString());
command.setHash(readExpression());
} else if (readIf("IDENTIFIED")) {
read("BY");
// uppercase if not quoted
command.setPassword(readColumnIdentifier());
command.setPassword(ValueExpression.get(ValueString.get(readColumnIdentifier())));
} else {
throw getSyntaxError();
}
......@@ -3686,11 +3686,11 @@ public class Parser {
command.setType(AlterUser.SET_PASSWORD);
command.setUser(database.getUser(userName));
if (readIf("PASSWORD")) {
command.setPassword(readString());
command.setPassword(readExpression());
} else if (readIf("SALT")) {
command.setSalt(readString());
command.setSalt(readExpression());
read("HASH");
command.setHash(readString());
command.setHash(readExpression());
} else {
throw getSyntaxError();
}
......@@ -3761,16 +3761,16 @@ public class Parser {
AlterUser command = new AlterUser(session);
command.setType(AlterUser.SET_PASSWORD);
command.setUser(session.getUser());
command.setPassword(readString());
command.setPassword(readExpression());
return command;
} else if (readIf("SALT")) {
readIfEqualOrTo();
AlterUser command = new AlterUser(session);
command.setType(AlterUser.SET_PASSWORD);
command.setUser(session.getUser());
command.setSalt(readString());
command.setSalt(readExpression());
read("HASH");
command.setHash(readString());
command.setHash(readExpression());
return command;
} else if (readIf("MODE")) {
readIfEqualOrTo();
......@@ -3942,7 +3942,7 @@ public class Parser {
private RunScriptCommand parseRunScript() throws SQLException {
RunScriptCommand command = new RunScriptCommand(session);
read("FROM");
command.setFileName(readString());
command.setFile(readExpression());
if (readIf("COMPRESSION")) {
command.setCompressionAlgorithm(readUniqueIdentifier());
}
......@@ -3986,7 +3986,7 @@ public class Parser {
command.setDrop(dropTables);
command.setSimple(simple);
if (readIf("TO")) {
command.setFileName(readString());
command.setFile(readExpression());
if (readIf("COMPRESSION")) {
command.setCompressionAlgorithm(readUniqueIdentifier());
}
......
......@@ -210,6 +210,10 @@ public abstract class Prepared {
public String getSQL() {
return sql;
}
protected int getCurrentObjectId() {
return objectId;
}
protected int getObjectId(boolean needFresh, boolean dataFile) {
Database db = session.getDatabase();
......
......@@ -35,7 +35,26 @@ import org.h2.util.ObjectArray;
*/
public class AlterTableAddConstraint extends SchemaCommand {
public static final int CHECK = 0, UNIQUE = 1, REFERENTIAL = 2, PRIMARY_KEY = 3;
/**
* The type of a ALTER TABLE ADD CHECK statement.
*/
public static final int CHECK = 0;
/**
* The type of a ALTER TABLE ADD UNIQUE statement.
*/
public static final int UNIQUE = 1;
/**
* The type of a ALTER TABLE ADD FOREIGN KEY statement.
*/
public static final int REFERENTIAL = 2;
/**
* The type of a ALTER TABLE ADD PRIMARY KEY statement.
*/
public static final int PRIMARY_KEY = 3;
private int type;
private String constraintName;
private String tableName;
......
......@@ -40,8 +40,41 @@ import org.h2.util.ObjectArray;
*/
public class AlterTableAlterColumn extends SchemaCommand {
public static final int NOT_NULL = 0, NULL = 1, DEFAULT = 2, CHANGE_TYPE = 3;
public static final int ADD = 4, DROP = 5, SELECTIVITY = 6;
/**
* The type of a ALTER TABLE ALTER COLUMN SET NOT NULL statement.
*/
public static final int NOT_NULL = 0;
/**
* The type of a ALTER TABLE ALTER COLUMN SET NULL statement.
*/
public static final int NULL = 1;
/**
* The type of a ALTER TABLE ALTER COLUMN SET DEFAULT statement.
*/
public static final int DEFAULT = 2;
/**
* The type of a ALTER TABLE ALTER COLUMN statement that changes the column
* data type.
*/
public static final int CHANGE_TYPE = 3;
/**
* The type of a ALTER TABLE ADD statement.
*/
public static final int ADD = 4;
/**
* The type of a ALTER TABLE DROP COLUMN statement.
*/
public static final int DROP = 5;
/**
* The type of a ALTER TABLE ALTER COLUMN SELECTIVITY statement.
*/
public static final int SELECTIVITY = 6;
private Table table;
private Column oldColumn;
......
......@@ -11,6 +11,7 @@ import org.h2.constant.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.expression.Expression;
import org.h2.message.Message;
import org.h2.security.SHA256;
import org.h2.util.ByteUtils;
......@@ -41,9 +42,9 @@ public class AlterUser extends DefineCommand {
private int type;
private User user;
private String newName;
private byte[] userPasswordHash;
private byte[] salt;
private byte[] hash;
private Expression password;
private Expression salt;
private Expression hash;
private boolean admin;
public AlterUser(Session session) {
......@@ -66,18 +67,24 @@ public class AlterUser extends DefineCommand {
this.admin = admin;
}
public void setSalt(String s) throws SQLException {
salt = ByteUtils.convertStringToBytes(s);
public void setSalt(Expression e) throws SQLException {
salt = e;
}
public void setHash(String s) throws SQLException {
hash = ByteUtils.convertStringToBytes(s);
public void setHash(Expression e) throws SQLException {
hash = e;
}
public void setPassword(String password) {
SHA256 sha = new SHA256();
String name = newName == null ? user.getName() : newName;
this.userPasswordHash = sha.getKeyPasswordHash(name, password.toCharArray());
public void setPassword(Expression password) {
this.password = password;
}
private char[] getCharArray(Expression e) throws SQLException {
return e.getValue(session).getString().toCharArray();
}
private byte[] getByteArray(Expression e) throws SQLException {
return ByteUtils.convertStringToBytes(e.getValue(session).getString());
}
public int update() throws SQLException {
......@@ -89,8 +96,12 @@ public class AlterUser extends DefineCommand {
session.getUser().checkAdmin();
}
if (hash != null && salt != null) {
user.setSaltAndHash(salt, hash);
user.setSaltAndHash(getByteArray(salt), getByteArray(hash));
} else {
String name = newName == null ? user.getName() : newName;
SHA256 sha = new SHA256();
char[] passwordChars = getCharArray(password);
byte[] userPasswordHash = sha.getKeyPasswordHash(name, passwordChars);
user.setUserPasswordHash(userPasswordHash);
}
break;
......@@ -104,7 +115,7 @@ public class AlterUser extends DefineCommand {
case ADMIN:
session.getUser().checkAdmin();
if (!admin) {
user.checkNoSchemas();
user.checkOwnsNoSchemas();
}
user.setAdmin(admin);
break;
......
......@@ -39,9 +39,6 @@ public class CreateRole extends DefineCommand {
session.commit(true);
Database db = session.getDatabase();
if (db.findUser(roleName) != null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(ErrorCode.USER_ALREADY_EXISTS_1, roleName);
}
if (db.findRole(roleName) != null) {
......
......@@ -11,6 +11,7 @@ import org.h2.constant.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.expression.Expression;
import org.h2.message.Message;
import org.h2.security.SHA256;
import org.h2.util.ByteUtils;
......@@ -23,9 +24,9 @@ public class CreateUser extends DefineCommand {
private String userName;
private boolean admin;
private byte[] userPasswordHash;
private byte[] salt;
private byte[] hash;
private Expression password;
private Expression salt;
private Expression hash;
private boolean ifNotExists;
private String comment;
......@@ -41,15 +42,25 @@ public class CreateUser extends DefineCommand {
this.userName = userName;
}
public void setPassword(String password) {
SHA256 sha = new SHA256();
this.userPasswordHash = sha.getKeyPasswordHash(userName, password.toCharArray());
public void setPassword(Expression password) {
this.password = password;
}
private char[] getCharArray(Expression e) throws SQLException {
return e.getValue(session).getString().toCharArray();
}
private byte[] getByteArray(Expression e) throws SQLException {
return ByteUtils.convertStringToBytes(e.getValue(session).getString());
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit(true);
Database db = session.getDatabase();
if (db.findRole(userName) != null) {
throw Message.getSQLException(ErrorCode.ROLE_ALREADY_EXISTS_1, userName);
}
if (db.findUser(userName) != null) {
if (ifNotExists) {
return 0;
......@@ -61,20 +72,23 @@ public class CreateUser extends DefineCommand {
user.setAdmin(admin);
user.setComment(comment);
if (hash != null && salt != null) {
user.setSaltAndHash(salt, hash);
user.setSaltAndHash(getByteArray(salt), getByteArray(hash));
} else {
SHA256 sha = new SHA256();
char[] passwordChars = getCharArray(password);
byte[] userPasswordHash = sha.getKeyPasswordHash(userName, passwordChars);
user.setUserPasswordHash(userPasswordHash);
}
db.addDatabaseObject(session, user);
return 0;
}
public void setSalt(String s) throws SQLException {
salt = ByteUtils.convertStringToBytes(s);
public void setSalt(Expression e) throws SQLException {
salt = e;
}
public void setHash(String s) throws SQLException {
hash = ByteUtils.convertStringToBytes(s);
public void setHash(Expression e) throws SQLException {
hash = e;
}
public void setAdmin(boolean b) {
......
......@@ -47,7 +47,7 @@ public class DropUser extends DefineCommand {
if (user == session.getUser()) {
throw Message.getSQLException(ErrorCode.CANNOT_DROP_CURRENT_USER);
}
user.checkNoSchemas();
user.checkOwnsNoSchemas();
db.removeDatabaseObject(session, user);
}
return 0;
......
......@@ -19,6 +19,7 @@ import org.h2.constant.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.log.LogFile;
import org.h2.log.LogSystem;
import org.h2.message.Message;
......@@ -35,19 +36,20 @@ import org.h2.util.ObjectArray;
*/
public class BackupCommand extends Prepared {
private String fileName;
private Expression fileName;
public BackupCommand(Session session) {
super(session);
}
public void setFileName(String fileName) {
public void setFileName(Expression fileName) {
this.fileName = fileName;
}
public int update() throws SQLException {
String name = fileName.getValue(session).getString();
session.getUser().checkAdmin();
backupTo(fileName);
backupTo(name);
return 0;
}
......
......@@ -56,6 +56,11 @@ public class Insert extends Prepared {
this.query = query;
}
/**
* Add a row to this merge statement.
*
* @param expr the list of values
*/
public void addRow(Expression[] expr) {
list.add(expr);
}
......
......@@ -64,6 +64,11 @@ public class Merge extends Prepared {
this.query = query;
}
/**
* Add a row to this merge statement.
*
* @param expr the list of values
*/
public void addRow(Expression[] expr) {
list.add(expr);
}
......
......@@ -59,7 +59,7 @@ public abstract class Query extends Prepared {
* @param limit the limit as specified in the JDBC method call
* @return the result
*/
abstract LocalResult queryWithoutCache(int limit) throws SQLException;
protected abstract LocalResult queryWithoutCache(int limit) throws SQLException;
/**
* Initialize the query.
......
......@@ -18,6 +18,7 @@ import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.message.Message;
import org.h2.security.SHA256;
import org.h2.store.DataHandler;
......@@ -48,7 +49,8 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
/**
* The file name (if set).
*/
protected String fileName;
private Expression file;
private String fileName;
private String cipher;
private byte[] key;
......@@ -72,11 +74,19 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
key = sha.getKeyPasswordHash("script", password);
}
public void setFileName(String fileName) {
if (fileName == null || fileName.trim().length() == 0) {
fileName = "script.sql";
public void setFile(Expression file) {
this.file = file;
}
protected String getFileName() throws SQLException {
if (file != null) {
fileName = file == null ? null : file.getValue(session).getString();
if (fileName == null || fileName.trim().length() == 0) {
fileName = "script.sql";
}
fileName = SysProperties.scriptDirectory + fileName;
}
this.fileName = SysProperties.scriptDirectory + fileName;
return fileName;
}
public boolean isTransactional() {
......@@ -84,6 +94,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
}
protected void deleteStore() throws SQLException {
String fileName = getFileName();
if (fileName != null) {
FileUtils.delete(fileName);
}
......@@ -93,12 +104,14 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
byte[] magic = Database.getMagic(true);
Database db = session.getDatabase();
// script files are always in text format
String fileName = getFileName();
store = FileStore.open(db, fileName, "rw", magic, cipher, key);
store.setCheckedWriting(false);
store.init();
}
protected void openOutput() throws SQLException {
String fileName = getFileName();
if (fileName == null) {
return;
}
......@@ -115,6 +128,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
}
protected void openInput() throws SQLException {
String fileName = getFileName();
if (fileName == null) {
return;
}
......
......@@ -340,7 +340,7 @@ public class ScriptCommand extends ScriptBase {
out.close();
}
} catch (IOException e) {
throw Message.convertIOException(e, fileName);
throw Message.convertIOException(e, getFileName());
} finally {
closeIO();
}
......
......@@ -71,7 +71,7 @@ public class Select extends Query {
private boolean isGroupQuery, isGroupSortedQuery;
private boolean isForUpdate;
private double cost;
private boolean isQuickQuery, isDistinctQuery;
private boolean isQuickAggregateQuery, isDistinctQuery;
private boolean isPrepared, checkInit;
private boolean sortUsingIndex;
private SortOrder sort;
......@@ -109,6 +109,9 @@ public class Select extends Query {
this.expressions = expressions;
}
/**
* Called if this query contains aggregate functions.
*/
public void setGroupQuery() {
isGroupQuery = true;
}
......@@ -499,7 +502,7 @@ public class Select extends Query {
return result;
}
public LocalResult queryWithoutCache(int maxRows) throws SQLException {
protected LocalResult queryWithoutCache(int maxRows) throws SQLException {
int limitRows = maxRows;
if (limit != null) {
int l = limit.getValue(session).getInt();
......@@ -520,7 +523,7 @@ public class Select extends Query {
topTableFilter.startQuery(session);
topTableFilter.reset();
topTableFilter.lock(session, isForUpdate, isForUpdate);
if (isQuickQuery) {
if (isQuickAggregateQuery) {
queryQuick(columnCount, result);
} else if (isGroupQuery) {
if (isGroupSortedQuery) {
......@@ -690,7 +693,7 @@ public class Select extends Query {
if (condition == null) {
ExpressionVisitor optimizable = ExpressionVisitor.get(ExpressionVisitor.OPTIMIZABLE_MIN_MAX_COUNT_ALL);
optimizable.table = ((TableFilter) filters.get(0)).getTable();
isQuickQuery = isEverything(optimizable);
isQuickAggregateQuery = isEverything(optimizable);
}
}
cost = preparePlan();
......@@ -717,7 +720,7 @@ public class Select extends Query {
}
}
}
if (sort != null && !isQuickQuery && !isGroupQuery) {
if (sort != null && !isQuickAggregateQuery && !isGroupQuery) {
Index index = getSortIndex();
Index current = topTableFilter.getIndex();
if (index != null && (current.getIndexType().isScan() || current == index)) {
......@@ -729,7 +732,7 @@ public class Select extends Query {
}
}
}
if (SysProperties.OPTIMIZE_GROUP_SORTED && !isQuickQuery && isGroupQuery && getGroupByExpressionCount() > 0) {
if (SysProperties.OPTIMIZE_GROUP_SORTED && !isQuickAggregateQuery && isGroupQuery && getGroupByExpressionCount() > 0) {
Index index = getGroupSortedIndex();
Index current = topTableFilter.getIndex();
if (index != null && (current.getIndexType().isScan() || current == index)) {
......@@ -873,7 +876,7 @@ public class Select extends Query {
if (isForUpdate) {
buff.append("\nFOR UPDATE");
}
if (isQuickQuery) {
if (isQuickAggregateQuery) {
buff.append("\n/* direct lookup */");
}
if (isDistinctQuery) {
......@@ -931,8 +934,15 @@ public class Select extends Query {
}
}
public boolean isQuickQuery() {
return isQuickQuery;
/**
* Check if this is an aggregate query with direct lookup, for example a
* query of the type SELECT COUNT(*) FROM TEST or
* SELECT MAX(ID) FROM TEST.
*
* @return true if a direct lookup is possible
*/
public boolean isQuickAggregateQuery() {
return isQuickAggregateQuery;
}
public void addGlobalCondition(Parameter param, int columnId, int comparisonType) throws SQLException {
......
......@@ -31,7 +31,27 @@ import org.h2.value.ValueInt;
* Represents a union SELECT statement.
*/
public class SelectUnion extends Query {
public static final int UNION = 0, UNION_ALL = 1, EXCEPT = 2, INTERSECT = 3;
/**
* The type of a UNION statement.
*/
public static final int UNION = 0;
/**
* The type of a UNION ALL statement.
*/
public static final int UNION_ALL = 1;
/**
* The type of an EXCEPT statement.
*/
public static final int EXCEPT = 2;
/**
* The type of an INTERSECT statement.
*/
public static final int INTERSECT = 3;
private int unionType;
private Query left, right;
private ObjectArray expressions;
......@@ -78,13 +98,7 @@ public class SelectUnion extends Query {
return result;
}
/**
* Execute the query without using the cache.
*
* @param maxrows the maximum number of rows to return
* @return the result
*/
public LocalResult queryWithoutCache(int maxrows) throws SQLException {
protected LocalResult queryWithoutCache(int maxrows) throws SQLException {
if (maxrows != 0) {
if (limit != null) {
maxrows = Math.min(limit.getValue(session).getInt(), maxrows);
......
......@@ -81,13 +81,21 @@ public class Set extends Prepared {
break;
case SetTypes.TRACE_LEVEL_SYSTEM_OUT:
session.getUser().checkAdmin();
database.getTraceSystem().setLevelSystemOut(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
if (getCurrentObjectId() == 0) {
// don't set the property when opening the database
// this is for compatibility with older versions, because
// this setting was persistent
database.getTraceSystem().setLevelSystemOut(getIntValue());
}
break;
case SetTypes.TRACE_LEVEL_FILE:
session.getUser().checkAdmin();
database.getTraceSystem().setLevelFile(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
if (getCurrentObjectId() == 0) {
// don't set the property when opening the database
// this is for compatibility with older versions, because
// this setting was persistent
database.getTraceSystem().setLevelFile(getIntValue());
}
break;
case SetTypes.TRACE_MAX_FILE_SIZE: {
session.getUser().checkAdmin();
......
......@@ -12,17 +12,185 @@ import org.h2.util.ObjectArray;
*/
public class SetTypes {
public static final int IGNORECASE = 1, MAX_LOG_SIZE = 2, MODE = 3, READONLY = 4, LOCK_TIMEOUT = 5;
public static final int DEFAULT_LOCK_TIMEOUT = 6, DEFAULT_TABLE_TYPE = 7;
/**
* The type of a SET IGNORECASE statement.
*/
public static final int IGNORECASE = 1;
/**
* The type of a SET MAX_LOG_SIZE statement.
*/
public static final int MAX_LOG_SIZE = 2;
/**
* The type of a SET MODE statement.
*/
public static final int MODE = 3;
/**
* The type of a SET READONLY statement.
*/
public static final int READONLY = 4;
/**
* The type of a SET LOCK_TIMEOUT statement.
*/
public static final int LOCK_TIMEOUT = 5;
/**
* The type of a SET DEFAULT_LOCK_TIMEOUT statement.
*/
public static final int DEFAULT_LOCK_TIMEOUT = 6;
/**
* The type of a SET DEFAULT_TABLE_TYPE statement.
*/
public static final int DEFAULT_TABLE_TYPE = 7;
/**
* The type of a SET CACHE_SIZE statement.
*/
public static final int CACHE_SIZE = 8;
public static final int TRACE_LEVEL_SYSTEM_OUT = 9, TRACE_LEVEL_FILE = 10, TRACE_MAX_FILE_SIZE = 11;
public static final int COLLATION = 12, CLUSTER = 13, WRITE_DELAY = 14, DATABASE_EVENT_LISTENER = 15;
public static final int MAX_MEMORY_ROWS = 16, LOCK_MODE = 17, DB_CLOSE_DELAY = 18;
public static final int LOG = 19, THROTTLE = 20, MAX_MEMORY_UNDO = 21, MAX_LENGTH_INPLACE_LOB = 22;
public static final int COMPRESS_LOB = 23, ALLOW_LITERALS = 24, MULTI_THREADED = 25, SCHEMA = 26;
public static final int OPTIMIZE_REUSE_RESULTS = 27, SCHEMA_SEARCH_PATH = 28, UNDO_LOG = 29;
public static final int REFERENTIAL_INTEGRITY = 30, MVCC = 31, MAX_OPERATION_MEMORY = 32, EXCLUSIVE = 33;
public static final int CREATE_BUILD = 34, VARIABLE = 35, QUERY_TIMEOUT = 36;
/**
* The type of a SET TRACE_LEVEL_SYSTEM_OUT statement.
*/
public static final int TRACE_LEVEL_SYSTEM_OUT = 9;
/**
* The type of a SET TRACE_LEVEL_FILE statement.
*/
public static final int TRACE_LEVEL_FILE = 10;
/**
* The type of a SET TRACE_MAX_FILE_SIZE statement.
*/
public static final int TRACE_MAX_FILE_SIZE = 11;
/**
* The type of a SET COLLATION statement.
*/
public static final int COLLATION = 12;
/**
* The type of a SET CLUSTER statement.
*/
public static final int CLUSTER = 13;
/**
* The type of a SET WRITE_DELAY statement.
*/
public static final int WRITE_DELAY = 14;
/**
* The type of a SET DATABASE_EVENT_LISTENER statement.
*/
public static final int DATABASE_EVENT_LISTENER = 15;
/**
* The type of a SET MAX_MEMORY_ROWS statement.
*/
public static final int MAX_MEMORY_ROWS = 16;
/**
* The type of a SET LOCK_MODE statement.
*/
public static final int LOCK_MODE = 17;
/**
* The type of a SET DB_CLOSE_DELAY statement.
*/
public static final int DB_CLOSE_DELAY = 18;
/**
* The type of a SET LOG statement.
*/
public static final int LOG = 19;
/**
* The type of a SET THROTTLE statement.
*/
public static final int THROTTLE = 20;
/**
* The type of a SET MAX_MEMORY_UNDO statement.
*/
public static final int MAX_MEMORY_UNDO = 21;
/**
* The type of a SET MAX_LENGTH_INPLACE_LOB statement.
*/
public static final int MAX_LENGTH_INPLACE_LOB = 22;
/**
* The type of a SET COMPRESS_LOB statement.
*/
public static final int COMPRESS_LOB = 23;
/**
* The type of a SET ALLOW_LITERALS statement.
*/
public static final int ALLOW_LITERALS = 24;
/**
* The type of a SET MULTI_THREADED statement.
*/
public static final int MULTI_THREADED = 25;
/**
* The type of a SET SCHEMA statement.
*/
public static final int SCHEMA = 26;
/**
* The type of a SET OPTIMIZE_REUSE_RESULTS statement.
*/
public static final int OPTIMIZE_REUSE_RESULTS = 27;
/**
* The type of a SET SCHEMA_SEARCH_PATH statement.
*/
public static final int SCHEMA_SEARCH_PATH = 28;
/**
* The type of a SET UNDO_LOG statement.
*/
public static final int UNDO_LOG = 29;
/**
* The type of a SET REFERENTIAL_INTEGRITY statement.
*/
public static final int REFERENTIAL_INTEGRITY = 30;
/**
* The type of a SET MVCC statement.
*/
public static final int MVCC = 31;
/**
* The type of a SET MAX_OPERATION_MEMORY statement.
*/
public static final int MAX_OPERATION_MEMORY = 32;
/**
* The type of a SET EXCLUSIVE statement.
*/
public static final int EXCLUSIVE = 33;
/**
* The type of a SET CREATE_BUILD statement.
*/
public static final int CREATE_BUILD = 34;
/**
* The type of a SET \@VARIABLE statement.
*/
public static final int VARIABLE = 35;
/**
* The type of a SET QUERY_TIMEOUT statement.
*/
public static final int QUERY_TIMEOUT = 36;
private static ObjectArray types = new ObjectArray();
......
......@@ -19,19 +19,75 @@ import org.h2.util.TempFileDeleter;
* Represents a transactional statement.
*/
public class TransactionCommand extends Prepared {
/**
* The type of a SET AUTOCOMMIT TRUE statement.
*/
public static final int AUTOCOMMIT_TRUE = 1;
/**
* The type of a SET AUTOCOMMIT FALSE statement.
*/
public static final int AUTOCOMMIT_FALSE = 2;
/**
* The type of a COMMIT statement.
*/
public static final int COMMIT = 3;
/**
* The type of a ROLLBACK statement.
*/
public static final int ROLLBACK = 4;
/**
* The type of a CHECKPOINT statement.
*/
public static final int CHECKPOINT = 5;
/**
* The type of a SAVEPOINT statement.
*/
public static final int SAVEPOINT = 6;
/**
* The type of a ROLLBACK TO SAVEPOINT statement.
*/
public static final int ROLLBACK_TO_SAVEPOINT = 7;
/**
* The type of a CHECKPOINT SYNC statement.
*/
public static final int CHECKPOINT_SYNC = 8;
/**
* The type of a PREPARE COMMIT statement.
*/
public static final int PREPARE_COMMIT = 9;
/**
* The type of a COMMIT TRANSACTION statement.
*/
public static final int COMMIT_TRANSACTION = 10;
/**
* The type of a ROLLBACK TRANSACTION statement.
*/
public static final int ROLLBACK_TRANSACTION = 11;
/**
* The type of a SHUTDOWN statement.
*/
public static final int SHUTDOWN = 12;
/**
* The type of a SHUTDOWN IMMEDIATELY statement.
*/
public static final int SHUTDOWN_IMMEDIATELY = 13;
/**
* The type of a BEGIN {WORK|TRANSACTION} statement.
*/
public static final int BEGIN = 14;
private int type;
......
......@@ -120,7 +120,7 @@ public abstract class Constraint extends SchemaObjectBase implements Comparable
public abstract void checkExistingData(Session session) throws SQLException;
public Constraint(Schema schema, int id, String name, Table table) {
super(schema, id, name, Trace.CONSTRAINT);
initSchemaObjectBase(schema, id, name, Trace.CONSTRAINT);
this.table = table;
this.setTemporary(table.getTemporary());
}
......
......@@ -32,7 +32,26 @@ import org.h2.value.ValueNull;
* A referential constraint.
*/
public class ConstraintReferential extends Constraint {
public static final int RESTRICT = 0, CASCADE = 1, SET_DEFAULT = 2, SET_NULL = 3;
/**
* The action is to restrict the operation.
*/
public static final int RESTRICT = 0;
/**
* The action is to cascade the operation.
*/
public static final int CASCADE = 1;
/**
* The action is to set the value to the default value.
*/
public static final int SET_DEFAULT = 2;
/**
* The action is to set the value to NULL.
*/
public static final int SET_NULL = 3;
private int deleteAction;
private int updateAction;
......@@ -205,11 +224,25 @@ public class ConstraintReferential extends Constraint {
}
}
/**
* Set the index to use for this constraint.
*
* @param index the index
* @param isOwner true if the index is generated by the system and belongs
* to this constraint
*/
public void setIndex(Index index, boolean isOwner) {
this.index = index;
this.indexOwner = isOwner;
}
/**
* Set the index of the referenced table to use for this constraint.
*
* @param refIndex the index
* @param isRefOwner true if the index is generated by the system and
* belongs to this constraint
*/
public void setRefIndex(Index refIndex, boolean isRefOwner) {
this.refIndex = refIndex;
this.refIndexOwner = isRefOwner;
......@@ -431,6 +464,12 @@ public class ConstraintReferential extends Constraint {
return deleteAction;
}
/**
* Set the action to apply (restrict, cascade,...) on a delete.
*
* @param session the session
* @param action the action
*/
public void setDeleteAction(Session session, int action) throws SQLException {
if (action == deleteAction) {
return;
......@@ -462,6 +501,12 @@ public class ConstraintReferential extends Constraint {
return updateAction;
}
/**
* Set the action to apply (restrict, cascade,...) on an update.
*
* @param session the session
* @param action the action
*/
public void setUpdateAction(Session session, int action) throws SQLException {
if (action == updateAction) {
return;
......
......@@ -107,6 +107,13 @@ public class ConstraintUnique extends Constraint {
return columns;
}
/**
* Set the index to use for this unique constraint.
*
* @param index the index
* @param isOwner true if the index is generated by the system and belongs
* to this constraint
*/
public void setIndex(Index index, boolean isOwner) {
this.index = index;
this.indexOwner = isOwner;
......
......@@ -22,7 +22,7 @@ public class Comment extends DbObjectBase {
private String commentText;
public Comment(Database database, int id, DbObject obj) {
super(database, id, getKey(obj), Trace.DATABASE);
initDbObjectBase(database, id, getKey(obj), Trace.DATABASE);
this.objectType = obj.getType();
this.objectName = obj.getSQL();
}
......
......@@ -550,8 +550,6 @@ public class Database implements DataHandler {
starting = false;
addDefaultSetting(systemSession, SetTypes.DEFAULT_LOCK_TIMEOUT, null, Constants.INITIAL_LOCK_TIMEOUT);
addDefaultSetting(systemSession, SetTypes.DEFAULT_TABLE_TYPE, null, Constants.DEFAULT_TABLE_TYPE);
addDefaultSetting(systemSession, SetTypes.TRACE_LEVEL_FILE, null, traceSystem.getLevelFile());
addDefaultSetting(systemSession, SetTypes.TRACE_LEVEL_SYSTEM_OUT, null, traceSystem.getLevelSystemOut());
addDefaultSetting(systemSession, SetTypes.CACHE_SIZE, null, SysProperties.CACHE_SIZE_DEFAULT);
addDefaultSetting(systemSession, SetTypes.CLUSTER, Constants.CLUSTERING_DISABLED, 0);
addDefaultSetting(systemSession, SetTypes.WRITE_DELAY, null, Constants.DEFAULT_WRITE_DELAY);
......@@ -781,6 +779,18 @@ public class Database implements DataHandler {
return (UserDataType) userDataTypes.get(name);
}
/**
* Get the user with the given name. If there is no such user, this method
* waits a short amount of time (to make rainbow table attacks harder) and
* then throws the exception that is passed. There is only one exception
* both for wrong user and for wrong password, to make it harder to get the
* list of user names.
*
* @param name the user name
* @param notFound the exception that should be thrown if the user does not
* exist
* @throws SQLException if the user does not exist
*/
public User getUser(String name, SQLException notFound) throws SQLException {
User user = (User) users.get(name);
if (user == null) {
......
......@@ -38,6 +38,14 @@ public abstract class DbObjectBase implements DbObject {
private long modificationId;
private boolean temporary;
protected void initDbObjectBase(Database database, int id, String name, String traceModule) {
this.database = database;
this.trace = database.getTrace(traceModule);
this.id = id;
this.objectName = name;
this.modificationId = database.getModificationMetaId();
}
/**
* Build a SQL statement to re-create the object, or to create a copy of the
* object with a different name or referencing a different table
......@@ -84,14 +92,6 @@ public abstract class DbObjectBase implements DbObject {
*/
public abstract void checkRename() throws SQLException;
protected DbObjectBase(Database database, int id, String name, String traceModule) {
this.database = database;
this.trace = database.getTrace(traceModule);
this.id = id;
this.objectName = name;
this.modificationId = database.getModificationMetaId();
}
public void setModified() {
this.modificationId = database == null ? -1 : database.getNextModificationMetaId();
}
......
......@@ -33,7 +33,7 @@ public class FunctionAlias extends DbObjectBase {
private int dataType;
public FunctionAlias(Database db, int id, String name, String javaClassMethod, boolean force) throws SQLException {
super(db, id, name, Trace.FUNCTION);
initDbObjectBase(db, id, name, Trace.FUNCTION);
int paren = javaClassMethod.indexOf('(');
int lastDot = javaClassMethod.lastIndexOf('.', paren < 0 ? javaClassMethod.length() : paren);
if (lastDot < 0) {
......
......@@ -25,13 +25,13 @@ public class Right extends DbObjectBase {
private RightOwner grantee;
public Right(Database db, int id, RightOwner grantee, Role grantedRole) {
super(db, id, "RIGHT_"+id, Trace.USER);
initDbObjectBase(db, id, "RIGHT_"+id, Trace.USER);
this.grantee = grantee;
this.grantedRole = grantedRole;
}
public Right(Database db, int id, RightOwner grantee, int grantedRight, Table grantedRightOnTable) {
super(db, id, ""+id, Trace.USER);
initDbObjectBase(db, id, ""+id, Trace.USER);
this.grantee = grantee;
this.grantedRight = grantedRight;
this.grantedTable = grantedRightOnTable;
......
......@@ -33,7 +33,7 @@ public abstract class RightOwner extends DbObjectBase {
private HashMap grantedRights;
protected RightOwner(Database database, int id, String name, String traceModule) {
super(database, id, name, traceModule);
initDbObjectBase(database, id, name, traceModule);
}
public boolean isRoleGranted(Role grantedRole) {
......
......@@ -107,6 +107,9 @@ public class SessionRemote implements SessionInterface, DataHandler {
this.autoCommit = autoCommit;
}
/**
* Calls COMMIT if the session is in cluster mode.
*/
public void autoCommitIfCluster() throws SQLException {
if (autoCommit && transferList != null && transferList.size() > 1) {
// server side auto commit is off because of race conditions
......@@ -231,6 +234,12 @@ public class SessionRemote implements SessionInterface, DataHandler {
ci.executeUpdate();
}
/**
* Remove a server from the list of cluster nodes and disables the cluster
* mode.
*
* @param i the index of the server to remove
*/
public void removeServer(int i) throws SQLException {
transferList.remove(i);
checkClosed();
......@@ -244,6 +253,11 @@ public class SessionRemote implements SessionInterface, DataHandler {
}
}
/**
* Check if this session is closed and throws an exception if so.
*
* @throws SQLException if the session is closed
*/
public void checkClosed() throws SQLException {
if (isClosed()) {
// TODO broken connection: try to reconnect automatically
......@@ -283,6 +297,16 @@ public class SessionRemote implements SessionInterface, DataHandler {
return nextId;
}
/**
* Called to flush the output after data has been sent to the server and
* just before receiving data. This method also reads the status code from
* the server and throws any exception the server sent.
*
* @param transfer the transfer object
* @throws SQLException if the server sent an exception
* @throws IOException if there is a communication problem between client
* and server
*/
public void done(Transfer transfer) throws SQLException, IOException {
transfer.flush();
int status = transfer.readInt();
......@@ -298,6 +322,11 @@ public class SessionRemote implements SessionInterface, DataHandler {
}
}
/**
* Returns true if the connection is in cluster mode.
*
* @return true if it is
*/
public boolean isClustered() {
return transferList.size() > 1;
}
......@@ -306,6 +335,12 @@ public class SessionRemote implements SessionInterface, DataHandler {
return transferList == null || transferList.size() == 0;
}
/**
* Write the operation to the trace system if debug trace is enabled.
*
* @param operation the operation performed
* @param id the id of the operation
*/
public void traceOperation(String operation, int id) {
if (trace.debug()) {
trace.debug(operation + " " + id);
......
......@@ -20,7 +20,7 @@ public class Setting extends DbObjectBase {
private String stringValue;
public Setting(Database database, int id, String settingName) {
super(database, id, settingName, Trace.SETTING);
initDbObjectBase(database, id, settingName, Trace.SETTING);
}
public void setIntValue(int value) {
......
......@@ -44,6 +44,12 @@ public class User extends RightOwner {
return admin;
}
/**
* Set the salt and hash of the password for this user.
*
* @param salt the salt
* @param hash the password hash
*/
public void setSaltAndHash(byte[] salt, byte[] hash) {
this.salt = salt;
this.passwordHash = hash;
......@@ -69,6 +75,13 @@ public class User extends RightOwner {
return null;
}
/**
* Checks that this user has the given rights for this database object.
*
* @param table the database object
* @param rightMask the rights required
* @throws SQLException if this user does not have the required rights
*/
public void checkRight(Table table, int rightMask) throws SQLException {
if (rightMask != Right.SELECT && !systemUser) {
database.checkWritingAllowed();
......@@ -101,6 +114,14 @@ public class User extends RightOwner {
}
}
/**
* Get the CREATE SQL statement for this object.
*
* @param password true if the password (actually the salt and hash) should
* be returned
* @param ifNotExists true if IF NOT EXISTS should be used
* @return the SQL statement
*/
public String getCreateSQL(boolean password, boolean ifNotExists) {
StringBuffer buff = new StringBuffer();
buff.append("CREATE USER ");
......@@ -127,9 +148,21 @@ public class User extends RightOwner {
return buff.toString();
}
public void checkUserPasswordHash(byte[] buff, SQLException onError) throws SQLException {
/**
* Check the password of this user. If the password is wrong, this method
* waits a short amount of time (to make S attacks harder) and then throws
* the exception that is passed. There is only one exception both for wrong
* user and for wrong password, to make it harder to get the list of user
* names.
*
* @param userPasswordHash the password data (the user password hash)
* @param onError the exception that should be thrown if the password does
* not match
* @throws SQLException if the password does not match
*/
public void checkUserPasswordHash(byte[] userPasswordHash, SQLException onError) throws SQLException {
SHA256 sha = new SHA256();
byte[] hash = sha.getHashWithSalt(buff, salt);
byte[] hash = sha.getHashWithSalt(userPasswordHash, salt);
if (!ByteUtils.compareSecure(hash, passwordHash)) {
try {
Thread.sleep(Constants.DELAY_WRONG_PASSWORD);
......@@ -140,6 +173,12 @@ public class User extends RightOwner {
}
}
/**
* Check if this user has admin rights. An exception is thrown if he does
* not have them.
*
* @throws SQLException if this user is not an admin
*/
public void checkAdmin() throws SQLException {
if (!admin) {
throw Message.getSQLException(ErrorCode.ADMIN_RIGHTS_REQUIRED);
......@@ -188,7 +227,13 @@ public class User extends RightOwner {
// ok
}
public void checkNoSchemas() throws SQLException {
/**
* Check that this user does not own any schema. An exception is thrown if he
* owns one or more schemas.
*
* @throws SQLException if this user owns a schema
*/
public void checkOwnsNoSchemas() throws SQLException {
ObjectArray schemas = database.getAllSchemas();
for (int i = 0; i < schemas.size(); i++) {
Schema s = (Schema) schemas.get(i);
......
......@@ -22,7 +22,7 @@ public class UserAggregate extends DbObjectBase {
private Class javaClass;
public UserAggregate(Database db, int id, String name, String className, boolean force) throws SQLException {
super(db, id, name, Trace.FUNCTION);
initDbObjectBase(db, id, name, Trace.FUNCTION);
this.className = className;
if (!force) {
getInstance();
......
......@@ -20,7 +20,7 @@ public class UserDataType extends DbObjectBase {
private Column column;
public UserDataType(Database database, int id, String name) {
super(database, id, name, Trace.DATABASE);
initDbObjectBase(database, id, name, Trace.DATABASE);
}
public String getCreateSQLForCopy(Table table, String quotedName) {
......
......@@ -154,7 +154,7 @@ public class Aggregate extends Expression {
}
public Value getValue(Session session) throws SQLException {
if (select.isQuickQuery()) {
if (select.isQuickAggregateQuery()) {
switch (type) {
case COUNT_ALL:
Table table = select.getTopTableFilter().getTable();
......
......@@ -235,7 +235,7 @@ public class FullText implements Trigger {
stat.execute("CREATE SCHEMA IF NOT EXISTS " + SCHEMA);
stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA
+ ".INDEXES(ID INT AUTO_INCREMENT PRIMARY KEY, SCHEMA VARCHAR, TABLE VARCHAR, COLUMNS VARCHAR, UNIQUE(SCHEMA, TABLE))");
stat.execute("CREATE MEMORY TABLE IF NOT EXISTS " + SCHEMA
stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA
+ ".WORDS(ID INT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR, UNIQUE(NAME))");
stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA
+ ".ROWS(ID IDENTITY, HASH INT, INDEXID INT, KEY VARCHAR, UNIQUE(HASH, INDEXID, KEY))");
......
......@@ -37,6 +37,22 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
protected IndexType indexType;
protected long rowCount;
public void initBaseIndex(Table table, int id, String name, IndexColumn[] indexColumns, IndexType indexType) {
initSchemaObjectBase(table.getSchema(), id, name, Trace.INDEX);
this.indexType = indexType;
this.table = table;
if (indexColumns != null) {
this.indexColumns = indexColumns;
columns = new Column[indexColumns.length];
columnIds = new int[columns.length];
for (int i = 0; i < columns.length; i++) {
Column col = indexColumns[i].column;
columns[i] = col;
columnIds[i] = col.getColumnId();
}
}
}
/**
* Close this index.
*
......@@ -113,22 +129,6 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
*/
public abstract boolean needRebuild();
public BaseIndex(Table table, int id, String name, IndexColumn[] indexColumns, IndexType indexType) {
super(table.getSchema(), id, name, Trace.INDEX);
this.indexType = indexType;
this.table = table;
if (indexColumns != null) {
this.indexColumns = indexColumns;
columns = new Column[indexColumns.length];
columnIds = new int[columns.length];
for (int i = 0; i < columns.length; i++) {
Column col = indexColumns[i].column;
columns[i] = col;
columnIds[i] = col.getColumnId();
}
}
}
public String getDropSQL() {
return null;
}
......@@ -171,6 +171,14 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
return 2;
}
/**
* Calculate the cost for the given mask as if this index was a typical
* b-tree range index.
*
* @param mask the search mask
* @param rowCount the number of rows in the index
* @return the calculated cost
*/
public long getCostRangeIndex(int[] masks, long rowCount) throws SQLException {
rowCount += Constants.COST_ROW_OFFSET;
long cost = rowCount;
......
......@@ -57,7 +57,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
public BtreeIndex(Session session, TableData table, int id, String indexName, IndexColumn[] columns,
IndexType indexType, int headPos) throws SQLException {
// TODO we need to log index data
super(table, id, indexName, columns, indexType);
initBaseIndex(table, id, indexName, columns, indexType);
this.tableData = table;
Database db = table.getDatabase();
storage = db.getStorage(this, id, false);
......
......@@ -26,7 +26,7 @@ public class FunctionIndex extends BaseIndex {
private LocalResult result;
public FunctionIndex(FunctionTable functionTable, IndexColumn[] columns, FunctionCall function) {
super(functionTable, 0, null, columns, IndexType.createNonUnique(true));
initBaseIndex(functionTable, 0, null, columns, IndexType.createNonUnique(true));
this.functionTable = functionTable;
}
......
......@@ -30,7 +30,7 @@ public class HashIndex extends BaseIndex {
private TableData tableData;
public HashIndex(TableData table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
super(table, id, indexName, columns, indexType);
initBaseIndex(table, id, indexName, columns, indexType);
this.tableData = table;
reset();
}
......
......@@ -74,6 +74,8 @@ public interface Index extends SchemaObject {
/**
* Estimate the cost to search for rows given the search mask.
* There is one element per column in the search mask.
* For possible search masks, see IndexCondition.
*
* @param session the session
* @param masks the search mask
......
......@@ -22,7 +22,32 @@ import org.h2.value.Value;
* expression that maps to each index condition.
*/
public class IndexCondition {
public static final int EQUALITY = 1, START = 2, END = 4, RANGE = START | END, ALWAYS_FALSE = 8;
/**
* A bit of a search mask meaning 'equal'.
*/
public static final int EQUALITY = 1;
/**
* A bit of a search mask meaning 'larger or equal'.
*/
public static final int START = 2;
/**
* A bit of a search mask meaning 'smaller or equal'.
*/
public static final int END = 4;
/**
* A search mask meaning 'between'.
*/
public static final int RANGE = START | END;
/**
* A bit of a search mask meaning 'the condition is always false'.
*/
public static final int ALWAYS_FALSE = 8;
private Column column;
private Expression expression;
private int compareType;
......
/*
* Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.store.DataPage;
import org.h2.store.Record;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
/**
* A index page of a linear hash index.
*/
public class LinearHashBucket extends Record {
private LinearHashIndex index;
private int nextBucket;
private ObjectArray records;
private boolean writePos;
public LinearHashBucket(LinearHashIndex index, DataPage s) throws SQLException {
this.index = index;
writePos = s.readByte() == 'P';
nextBucket = s.readInt();
int len = s.readInt();
records = new ObjectArray();
for (int i = 0; i < len; i++) {
LinearHashEntry entry = new LinearHashEntry();
if (!writePos) {
Value key = s.readValue();
entry.key = key;
}
entry.hash = s.readInt();
entry.value = s.readInt();
entry.home = index.getPos(entry.hash);
records.add(entry);
}
}
public LinearHashBucket(LinearHashIndex index) {
this.index = index;
this.records = new ObjectArray();
this.nextBucket = -1;
}
private void update(Session session) throws SQLException {
index.updateBucket(session, this);
}
void setNext(Session session, int nextBucket) throws SQLException {
this.nextBucket = nextBucket;
update(session);
}
int getNextBucket() {
return nextBucket;
}
LinearHashEntry getRecord(int i) {
return (LinearHashEntry) records.get(i);
}
void addRecord(Session session, LinearHashEntry r) throws SQLException {
records.add(r);
update(session);
}
void removeRecord(Session session, int i) throws SQLException {
records.remove(i);
update(session);
}
int getRecordSize() {
return records.size();
}
public void write(DataPage buff) throws SQLException {
getRealByteCount(buff);
buff.writeByte((byte) 'B');
if (writePos) {
buff.writeByte((byte) 'P');
} else {
buff.writeByte((byte) 'D');
}
buff.writeInt(nextBucket);
buff.writeInt(records.size());
for (int i = 0; i < records.size(); i++) {
LinearHashEntry record = (LinearHashEntry) records.get(i);
// TODO index: just add the hash if the key is too large
if (!writePos) {
buff.writeValue(record.key);
}
buff.writeInt(record.hash);
buff.writeInt(record.value);
}
}
public int getByteCount(DataPage dummy) throws SQLException {
return index.getBucketSize();
}
public int getRealByteCount(DataPage dummy) throws SQLException {
int size = 2 + dummy.getIntLen() + dummy.getIntLen();
int dataSize = 0;
for (int i = 0; i < records.size(); i++) {
LinearHashEntry record = (LinearHashEntry) records.get(i);
// TODO index: just add the hash if the key is too large
dataSize += dummy.getValueLen(record.key);
size += 2 * dummy.getIntLen();
}
if (size + dataSize >= index.getBucketSize()) {
writePos = true;
return size;
} else {
writePos = false;
return size + dataSize;
}
}
public boolean isEmpty() {
return false;
}
}
/*
* Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
import java.sql.SQLException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* The cursor implementation for the linear hash index.
*/
public class LinearHashCursor implements Cursor {
private Row row;
private boolean end;
LinearHashCursor(Row row) {
this.row = row;
}
public Row get() {
return row;
}
public SearchRow getSearchRow() throws SQLException {
return row;
}
public int getPos() {
return row.getPos();
}
public boolean next() {
if (row == null || end) {
row = null;
return false;
}
end = true;
return true;
}
}
/*
* Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
import org.h2.value.Value;
/**
* An index entry of a linear hash index.
*/
public class LinearHashEntry {
// private LinearHashEntry(int home, int hash, Value key, int value) {
// this.home = home;
// this.hash = hash;
// this.key = key;
// this.value = value;
// }
public int home;
public int hash;
public Value key;
public int value;
}
/*
* Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
import java.sql.SQLException;
import org.h2.store.DataPage;
import org.h2.store.Record;
/**
* The head page of a linear hash index.
*/
public class LinearHashHead extends Record {
private LinearHashIndex index;
int baseSize;
int nextToSplit;
int recordCount, bucketCount;
LinearHashHead(LinearHashIndex index) {
this.index = index;
}
LinearHashHead(LinearHashIndex index, DataPage s) {
this.index = index;
baseSize = s.readInt();
nextToSplit = s.readInt();
recordCount = s.readInt();
bucketCount = s.readInt();
}
public int getByteCount(DataPage dummy) throws SQLException {
return index.getBucketSize();
}
public void write(DataPage buff) throws SQLException {
buff.writeByte((byte) 'H');
buff.writeInt(baseSize);
buff.writeInt(nextToSplit);
buff.writeInt(recordCount);
buff.writeInt(bucketCount);
}
public boolean isPinned() {
return true;
}
}
......@@ -30,7 +30,7 @@ public class LinkedIndex extends BaseIndex {
private String targetTableName;
public LinkedIndex(TableLink table, int id, IndexColumn[] columns, IndexType indexType) {
super(table, id, null, columns, indexType);
initBaseIndex(table, id, null, columns, indexType);
link = table;
targetTableName = link.getQualifiedTable();
}
......
......@@ -24,7 +24,7 @@ public class MetaIndex extends BaseIndex {
private boolean scan;
public MetaIndex(MetaTable meta, IndexColumn[] columns, boolean scan) {
super(meta, 0, null, columns, IndexType.createNonUnique(true));
initBaseIndex(meta, 0, null, columns, IndexType.createNonUnique(true));
this.meta = meta;
this.scan = scan;
}
......
......@@ -25,7 +25,7 @@ public class RangeIndex extends BaseIndex {
private long min, max;
public RangeIndex(RangeTable table, IndexColumn[] columns, long min, long max) {
super(table, 0, "RANGE_INDEX", columns, IndexType.createNonUnique(true));
initBaseIndex(table, 0, "RANGE_INDEX", columns, IndexType.createNonUnique(true));
this.min = min;
this.max = max;
}
......
......@@ -42,7 +42,7 @@ public class ScanIndex extends BaseIndex {
private HashSet delta;
public ScanIndex(TableData table, int id, IndexColumn[] columns, IndexType indexType) throws SQLException {
super(table, id, table.getName() + "_TABLE_SCAN", columns, indexType);
initBaseIndex(table, id, table.getName() + "_TABLE_SCAN", columns, indexType);
if (database.isMultiVersion()) {
sessionRowCount = new HashMap();
}
......
......@@ -26,7 +26,7 @@ public class TreeIndex extends BaseIndex {
private TableData tableData;
public TreeIndex(TableData table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
super(table, id, indexName, columns, indexType);
initBaseIndex(table, id, indexName, columns, indexType);
tableData = table;
}
......
......@@ -38,7 +38,7 @@ public class ViewIndex extends BaseIndex {
private Session session;
public ViewIndex(TableView view, String querySQL, ObjectArray originalParameters, boolean recursive) {
super(view, 0, null, null, IndexType.createNonUnique(false));
initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false));
this.querySQL = querySQL;
this.originalParameters = originalParameters;
this.recursive = recursive;
......@@ -46,7 +46,7 @@ public class ViewIndex extends BaseIndex {
}
public ViewIndex(TableView view, ViewIndex index, Session session, int[] masks) throws SQLException {
super(view, 0, null, null, IndexType.createNonUnique(false));
initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false));
this.querySQL = index.querySQL;
this.originalParameters = index.originalParameters;
this.recursive = index.recursive;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论