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

--no commit message

--no commit message
上级 3f000aa1
...@@ -53,11 +53,12 @@ Roadmap ...@@ -53,11 +53,12 @@ Roadmap
<li>Automatic mode: jdbc:h2:auto: (embedded mode if possible, if not use server mode). Keep the server running until all have disconnected. <li>Automatic mode: jdbc:h2:auto: (embedded mode if possible, if not use server mode). Keep the server running until all have disconnected.
</li><li>Support function overloading as in Java (multiple functions with the same name, but different parameter count or data types). </li><li>Support function overloading as in Java (multiple functions with the same name, but different parameter count or data types).
</li><li>Linked tables that point to the same database should share the connection ([SHARED CONNECTION]). Serialize access to the connection. </li><li>Linked tables that point to the same database should share the connection ([SHARED CONNECTION]). Serialize access to the connection.
</li><li>Procedural language / script language (Javascript)
</li><li>Linked tables should support a schema name: CREATE LINKED TABLE ... (... [schemaName, ] originalTableString) - DB2: SET SCHEMA after connecting </li><li>Linked tables should support a schema name: CREATE LINKED TABLE ... (... [schemaName, ] originalTableString) - DB2: SET SCHEMA after connecting
</li><li>Option to shutdown all the running servers (on the same VM). </li><li>Option to shutdown all the running servers (on the same VM).
</li><li>Support OSGi: http://oscar-osgi.sourceforge.net, http://incubator.apache.org/felix/index.html </li><li>Support OSGi: http://oscar-osgi.sourceforge.net, http://incubator.apache.org/felix/index.html
</li><li>H2 Console: new option 'Open a browser when starting the H2 Console'.
</li><li>Better space re-use in the files after deleting data: shrink the data file without closing the database (if the end of the file is empty) </li><li>Better space re-use in the files after deleting data: shrink the data file without closing the database (if the end of the file is empty)
</li><li>Procedural language / script language (Javascript)
</li><li>Full outer joins </li><li>Full outer joins
</li><li>Support hints for the optimizer (which index to use, enforce the join order). </li><li>Support hints for the optimizer (which index to use, enforce the join order).
</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) </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)
...@@ -362,7 +363,6 @@ Roadmap ...@@ -362,7 +363,6 @@ Roadmap
Also support it when using INSERT ... SELECT. Also support it when using INSERT ... SELECT.
</li><li>HSQLDB compatibility: automatic data type for SUM if value is the value is too big (by default use the same type as the data). </li><li>HSQLDB compatibility: automatic data type for SUM if value is the value is too big (by default use the same type as the data).
</li><li>Improve the optimizer to select the right index for special cases: where id between 2 and 4 and booleanColumn </li><li>Improve the optimizer to select the right index for special cases: where id between 2 and 4 and booleanColumn
</li><li>H2 Console: new option 'Open a browser when starting the H2 Console'.
</li><li>Enable warning for 'Local variable declaration hides another field or variable'. </li><li>Enable warning for 'Local variable declaration hides another field or variable'.
</li><li>Linked tables: make hidden columns available (Oracle: rowid and ora_rowscn columns). </li><li>Linked tables: make hidden columns available (Oracle: rowid and ora_rowscn columns).
</li><li>Support merge join. </li><li>Support merge join.
......
...@@ -131,8 +131,8 @@ public class GrantRevoke extends DefineCommand { ...@@ -131,8 +131,8 @@ public class GrantRevoke extends DefineCommand {
} }
private void grantRole(Role grantedRole) throws SQLException { private void grantRole(Role grantedRole) throws SQLException {
if (grantee.isRoleGranted(grantedRole)) { if (grantedRole != grantee && grantee.isRoleGranted(grantedRole)) {
throw Message.getSQLException(ErrorCode.ROLE_ALREADY_GRANTED_1, grantedRole.getSQL()); return;
} }
if (grantee instanceof Role) { if (grantee instanceof Role) {
Role granteeRole = (Role) grantee; Role granteeRole = (Role) grantee;
...@@ -153,13 +153,10 @@ public class GrantRevoke extends DefineCommand { ...@@ -153,13 +153,10 @@ public class GrantRevoke extends DefineCommand {
Table table = (Table) tables.get(i); Table table = (Table) tables.get(i);
Right right = grantee.getRightForTable(table); Right right = grantee.getRightForTable(table);
if (right == null) { if (right == null) {
throw Message.getSQLException(ErrorCode.RIGHT_NOT_FOUND); continue;
} }
int mask = right.getRightMask(); int mask = right.getRightMask();
if ((mask & rightMask) != rightMask) { int newRight = mask & ~rightMask;
throw Message.getSQLException(ErrorCode.RIGHT_NOT_FOUND);
}
int newRight = mask ^ rightMask;
Database db = session.getDatabase(); Database db = session.getDatabase();
if (newRight == 0) { if (newRight == 0) {
db.removeDatabaseObject(session, right); db.removeDatabaseObject(session, right);
...@@ -173,7 +170,7 @@ public class GrantRevoke extends DefineCommand { ...@@ -173,7 +170,7 @@ public class GrantRevoke extends DefineCommand {
private void revokeRole(Role grantedRole) throws SQLException { private void revokeRole(Role grantedRole) throws SQLException {
Right right = grantee.getRightForRole(grantedRole); Right right = grantee.getRightForRole(grantedRole);
if (right == null) { if (right == null) {
throw Message.getSQLException(ErrorCode.RIGHT_NOT_FOUND); return;
} }
Database db = session.getDatabase(); Database db = session.getDatabase();
db.removeDatabaseObject(session, right); db.removeDatabaseObject(session, right);
......
...@@ -1092,25 +1092,15 @@ public class ErrorCode { ...@@ -1092,25 +1092,15 @@ public class ErrorCode {
*/ */
public static final int ROLES_AND_RIGHT_CANNOT_BE_MIXED = 90072; public static final int ROLES_AND_RIGHT_CANNOT_BE_MIXED = 90072;
/**
* The error with code <code>90073</code> is thrown when
* trying to revoke a right that does not or no longer exist.
* Example:
* <pre>
* CREATE USER TEST_USER PASSWORD 'abc';
* REVOKE SELECT ON TEST FROM TEST_USER;
* </pre>
*/
public static final int RIGHT_NOT_FOUND = 90073;
/** /**
* The error with code <code>90074</code> is thrown when * The error with code <code>90074</code> is thrown when
* trying to grant a role that has already been granted. * trying to grant a role that has already been granted.
* Example: * Example:
* <pre> * <pre>
* CREATE ROLE TEST_ROLE; * CREATE ROLE TEST_A;
* GRANT TEST_ROLE TO SA; * CREATE ROLE TEST_B;
* GRANT TEST_ROLE TO SA; * GRANT TEST_A TO TEST_B;
* GRANT TEST_B TO TEST_A;
* </pre> * </pre>
*/ */
public static final int ROLE_ALREADY_GRANTED_1 = 90074; public static final int ROLE_ALREADY_GRANTED_1 = 90074;
...@@ -1784,7 +1774,7 @@ public class ErrorCode { ...@@ -1784,7 +1774,7 @@ public class ErrorCode {
*/ */
public static final int CAN_ONLY_ASSIGN_TO_VARIABLE_1 = 90137; public static final int CAN_ONLY_ASSIGN_TO_VARIABLE_1 = 90137;
// next is 90108 // next is 90073, 90108
private ErrorCode() { private ErrorCode() {
// utility class // utility class
......
...@@ -118,6 +118,13 @@ public abstract class Constraint extends SchemaObjectBase implements Comparable ...@@ -118,6 +118,13 @@ public abstract class Constraint extends SchemaObjectBase implements Comparable
*/ */
public abstract void checkExistingData(Session session) throws SQLException; public abstract void checkExistingData(Session session) throws SQLException;
/**
* Get the unique index used to enforce this constraint, or null if no index is used.
*
* @return the index
*/
public abstract Index getUniqueIndex();
public void checkRename() { public void checkRename() {
// ok // ok
} }
......
...@@ -139,4 +139,8 @@ public class ConstraintCheck extends Constraint { ...@@ -139,4 +139,8 @@ public class ConstraintCheck extends Constraint {
} }
} }
public Index getUniqueIndex() {
return null;
}
} }
...@@ -667,4 +667,8 @@ public class ConstraintReferential extends Constraint { ...@@ -667,4 +667,8 @@ public class ConstraintReferential extends Constraint {
} }
} }
public Index getUniqueIndex() {
return refIndex;
}
} }
...@@ -144,4 +144,8 @@ public class ConstraintUnique extends Constraint { ...@@ -144,4 +144,8 @@ public class ConstraintUnique extends Constraint {
// no need to check: when creating the unique index any problems are found // no need to check: when creating the unique index any problems are found
} }
public Index getUniqueIndex() {
return index;
}
} }
...@@ -672,7 +672,7 @@ public class Database implements DataHandler { ...@@ -672,7 +672,7 @@ public class Database implements DataHandler {
for (int i = 0; i < storages.size(); i++) { for (int i = 0; i < storages.size(); i++) {
Storage storage = (Storage) storages.get(i); Storage storage = (Storage) storages.get(i);
if (storage != null && storage.getRecordReader() == null) { if (storage != null && storage.getRecordReader() == null) {
storage.delete(session); storage.truncate(session);
} }
} }
} }
......
...@@ -10,8 +10,6 @@ import java.sql.SQLException; ...@@ -10,8 +10,6 @@ import java.sql.SQLException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import org.h2.constant.ErrorCode;
import org.h2.message.Message;
import org.h2.table.Table; import org.h2.table.Table;
/** /**
...@@ -136,11 +134,11 @@ public abstract class RightOwner extends DbObjectBase { ...@@ -136,11 +134,11 @@ public abstract class RightOwner extends DbObjectBase {
*/ */
public void revokeRole(Session session, Role role) throws SQLException { public void revokeRole(Session session, Role role) throws SQLException {
if (grantedRoles == null) { if (grantedRoles == null) {
throw Message.getSQLException(ErrorCode.RIGHT_NOT_FOUND); return;
} }
Right right = (Right) grantedRoles.get(role); Right right = (Right) grantedRoles.get(role);
if (right == null) { if (right == null) {
throw Message.getSQLException(ErrorCode.RIGHT_NOT_FOUND); return;
} }
grantedRoles.remove(role); grantedRoles.remove(role);
if (grantedRoles.size() == 0) { if (grantedRoles.size() == 0) {
......
...@@ -114,7 +114,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader { ...@@ -114,7 +114,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
} }
public void remove(Session session) throws SQLException { public void remove(Session session) throws SQLException {
storage.delete(session); storage.truncate(session);
storage = null; storage = null;
} }
......
...@@ -61,7 +61,7 @@ public class ScanIndex extends BaseIndex { ...@@ -61,7 +61,7 @@ public class ScanIndex extends BaseIndex {
public void remove(Session session) throws SQLException { public void remove(Session session) throws SQLException {
truncate(session); truncate(session);
if (storage != null) { if (storage != null) {
storage.delete(session); storage.truncate(session);
} }
} }
......
...@@ -47,7 +47,11 @@ public class TcpServer implements Service { ...@@ -47,7 +47,11 @@ public class TcpServer implements Service {
// TODO better exception message if the port is already in use, maybe // TODO better exception message if the port is already in use, maybe
// automatically use the next free port? // automatically use the next free port?
/**
* The default port to use for the TCP server.
*/
public static final int DEFAULT_PORT = 9092; public static final int DEFAULT_PORT = 9092;
private static final int SHUTDOWN_NORMAL = 0; private static final int SHUTDOWN_NORMAL = 0;
private static final int SHUTDOWN_FORCE = 1; private static final int SHUTDOWN_FORCE = 1;
...@@ -329,10 +333,17 @@ public class TcpServer implements Service { ...@@ -329,10 +333,17 @@ public class TcpServer implements Service {
return "H2 TCP Server"; return "H2 TCP Server";
} }
public boolean getIfExists() { boolean getIfExists() {
return ifExists; return ifExists;
} }
/**
* Stop the TCP server with the given URL.
*
* @param url the database URL
* @param password the password
* @param force if the server should be stopped immediately
*/
public static synchronized void shutdown(String url, String password, boolean force) throws SQLException { public static synchronized void shutdown(String url, String password, boolean force) throws SQLException {
int port = Constants.DEFAULT_SERVER_PORT; int port = Constants.DEFAULT_SERVER_PORT;
int idx = url.indexOf(':', "jdbc:h2:".length()); int idx = url.indexOf(':', "jdbc:h2:".length());
...@@ -380,7 +391,7 @@ public class TcpServer implements Service { ...@@ -380,7 +391,7 @@ public class TcpServer implements Service {
} }
} }
public void cancelStatement(String sessionId, int statementId) throws SQLException { void cancelStatement(String sessionId, int statementId) throws SQLException {
ArrayList list = new ArrayList(running); ArrayList list = new ArrayList(running);
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
TcpServerThread c = (TcpServerThread) list.get(i); TcpServerThread c = (TcpServerThread) list.get(i);
......
...@@ -47,7 +47,7 @@ public class TcpServerThread implements Runnable { ...@@ -47,7 +47,7 @@ public class TcpServerThread implements Runnable {
private int clientVersion; private int clientVersion;
private String sessionId; private String sessionId;
public TcpServerThread(Socket socket, TcpServer server, int id) { TcpServerThread(Socket socket, TcpServer server, int id) {
this.server = server; this.server = server;
this.id = id; this.id = id;
transfer = new Transfer(null); transfer = new Transfer(null);
...@@ -151,7 +151,7 @@ public class TcpServerThread implements Runnable { ...@@ -151,7 +151,7 @@ public class TcpServerThread implements Runnable {
} }
} }
public void close() { void close() {
try { try {
stop = true; stop = true;
closeSession(); closeSession();
...@@ -352,15 +352,15 @@ public class TcpServerThread implements Runnable { ...@@ -352,15 +352,15 @@ public class TcpServerThread implements Runnable {
} }
} }
public void setThread(Thread thread) { void setThread(Thread thread) {
this.thread = thread; this.thread = thread;
} }
public Thread getThread() { Thread getThread() {
return thread; return thread;
} }
public void cancelStatement(String sessionId, int statementId) throws SQLException { void cancelStatement(String sessionId, int statementId) throws SQLException {
if (StringUtils.equals(sessionId, this.sessionId)) { if (StringUtils.equals(sessionId, this.sessionId)) {
Command cmd = (Command) cache.getObject(statementId, false); Command cmd = (Command) cache.getObject(statementId, false);
cmd.cancel(); cmd.cancel();
......
...@@ -30,8 +30,8 @@ public class DbContextRule implements Rule { ...@@ -30,8 +30,8 @@ public class DbContextRule implements Rule {
static final int COLUMN_ALIAS = 4, SCHEMA = 5; static final int COLUMN_ALIAS = 4, SCHEMA = 5;
private static final boolean SUGGEST_TABLE_ALIAS = false; private static final boolean SUGGEST_TABLE_ALIAS = false;
DbContents contents; private DbContents contents;
int type; private int type;
DbContextRule(DbContents contents, int type) { DbContextRule(DbContents contents, int type) {
this.contents = contents; this.contents = contents;
...@@ -319,7 +319,7 @@ public class DbContextRule implements Rule { ...@@ -319,7 +319,7 @@ public class DbContextRule implements Rule {
return true; return true;
} }
public String matchSchema(Sentence sentence) { private String matchSchema(Sentence sentence) {
String query = sentence.query; String query = sentence.query;
String up = sentence.queryUpper; String up = sentence.queryUpper;
DbSchema[] schemas = contents.schemas; DbSchema[] schemas = contents.schemas;
...@@ -346,7 +346,7 @@ public class DbContextRule implements Rule { ...@@ -346,7 +346,7 @@ public class DbContextRule implements Rule {
return query; return query;
} }
public String matchTable(Sentence sentence) { private String matchTable(Sentence sentence) {
String query = sentence.query; String query = sentence.query;
String up = sentence.queryUpper; String up = sentence.queryUpper;
DbSchema schema = sentence.getLastMatchedSchema(); DbSchema schema = sentence.getLastMatchedSchema();
...@@ -400,7 +400,7 @@ public class DbContextRule implements Rule { ...@@ -400,7 +400,7 @@ public class DbContextRule implements Rule {
return query.substring(alias.length()); return query.substring(alias.length());
} }
public String matchTableAlias(Sentence sentence, boolean add) { private String matchTableAlias(Sentence sentence, boolean add) {
String query = sentence.query; String query = sentence.query;
String up = sentence.queryUpper; String up = sentence.queryUpper;
int i = 0; int i = 0;
...@@ -454,7 +454,7 @@ public class DbContextRule implements Rule { ...@@ -454,7 +454,7 @@ public class DbContextRule implements Rule {
return null; return null;
} }
public String matchColumn(Sentence sentence) { private String matchColumn(Sentence sentence) {
String query = sentence.query; String query = sentence.query;
String up = sentence.queryUpper; String up = sentence.queryUpper;
HashSet set = sentence.getTables(); HashSet set = sentence.getTables();
......
...@@ -59,6 +59,11 @@ public class DbStarter implements ServletContextListener { ...@@ -59,6 +59,11 @@ public class DbStarter implements ServletContextListener {
return value == null ? defaultValue : value; return value == null ? defaultValue : value;
} }
/**
* Get the connection.
*
* @return the connection
*/
public Connection getConnection() { public Connection getConnection() {
return conn; return conn;
} }
......
...@@ -15,15 +15,13 @@ import java.util.Map; ...@@ -15,15 +15,13 @@ import java.util.Map;
* This class is used by the H2 Console. * This class is used by the H2 Console.
*/ */
public class PageParser { public class PageParser {
private WebServer server;
private String page; private String page;
private int pos; private int pos;
private Map settings; private Map settings;
private int len; private int len;
private StringBuffer result; private StringBuffer result;
private PageParser(WebServer server, String page, Map settings, int pos) { private PageParser(String page, Map settings, int pos) {
this.server = server;
this.page = page; this.page = page;
this.pos = pos; this.pos = pos;
this.len = page.length(); this.len = page.length();
...@@ -31,9 +29,16 @@ public class PageParser { ...@@ -31,9 +29,16 @@ public class PageParser {
result = new StringBuffer(len); result = new StringBuffer(len);
} }
public static String parse(WebServer server, String page, Map settings) { /**
PageParser block = new PageParser(server, page, settings, 0); * Replace the tags in the HTML page with the given settings.
return block.parse(); *
* @param page the HTML page
* @param settings the settings
* @return the converted page
*/
public static String parse(String page, Map settings) {
PageParser block = new PageParser(page, settings, 0);
return block.replaceTags();
} }
private void setError(int i) { private void setError(int i) {
...@@ -43,7 +48,7 @@ public class PageParser { ...@@ -43,7 +48,7 @@ public class PageParser {
} }
private String parseBlockUntil(String end) throws Exception { private String parseBlockUntil(String end) throws Exception {
PageParser block = new PageParser(server, page, settings, pos); PageParser block = new PageParser(page, settings, pos);
block.parseAll(); block.parseAll();
if (!block.readIf(end)) { if (!block.readIf(end)) {
throw new Exception(); throw new Exception();
...@@ -52,7 +57,7 @@ public class PageParser { ...@@ -52,7 +57,7 @@ public class PageParser {
return block.result.toString(); return block.result.toString();
} }
private String parse() { private String replaceTags() {
try { try {
parseAll(); parseAll();
if (pos != len) { if (pos != len) {
...@@ -151,7 +156,7 @@ public class PageParser { ...@@ -151,7 +156,7 @@ public class PageParser {
String item = p.substring(i, j).trim(); String item = p.substring(i, j).trim();
i = j; i = j;
String s = (String) get(item); String s = (String) get(item);
append(s); replaceTags(s);
} else { } else {
buff.append(c); buff.append(c);
} }
...@@ -177,9 +182,9 @@ public class PageParser { ...@@ -177,9 +182,9 @@ public class PageParser {
return settings.get(item); return settings.get(item);
} }
private void append(String s) { private void replaceTags(String s) {
if (s != null) { if (s != null) {
result.append(PageParser.parse(server, s, settings)); result.append(PageParser.parse(s, settings));
} }
} }
...@@ -194,7 +199,7 @@ public class PageParser { ...@@ -194,7 +199,7 @@ public class PageParser {
int end = pos; int end = pos;
read("\""); read("\"");
String s = page.substring(start, end); String s = page.substring(start, end);
return PageParser.parse(server, s, settings); return PageParser.parse(s, settings);
} }
private void skipSpaces() { private void skipSpaces() {
......
...@@ -434,7 +434,7 @@ public class WebServer implements Service { ...@@ -434,7 +434,7 @@ public class WebServer implements Service {
return FileUtils.getFileInUserHome(Constants.SERVER_PROPERTIES_FILE); return FileUtils.getFileInUserHome(Constants.SERVER_PROPERTIES_FILE);
} }
Properties loadProperties() { private Properties loadProperties() {
String fileName = getPropertiesFileName(); String fileName = getPropertiesFileName();
try { try {
return FileUtils.loadProperties(fileName); return FileUtils.loadProperties(fileName);
...@@ -481,7 +481,7 @@ public class WebServer implements Service { ...@@ -481,7 +481,7 @@ public class WebServer implements Service {
return settings; return settings;
} }
void sortConnectionInfo(ArrayList list) { private void sortConnectionInfo(ArrayList list) {
for (int i = 1, j; i < list.size(); i++) { for (int i = 1, j; i < list.size(); i++) {
ConnectionInfo t = (ConnectionInfo) list.get(i); ConnectionInfo t = (ConnectionInfo) list.get(i);
for (j = i - 1; j >= 0 && (((ConnectionInfo) list.get(j)).lastAccess < t.lastAccess); j--) { for (j = i - 1; j >= 0 && (((ConnectionInfo) list.get(j)).lastAccess < t.lastAccess); j--) {
......
...@@ -139,7 +139,7 @@ public class WebServlet extends HttpServlet { ...@@ -139,7 +139,7 @@ public class WebServlet extends HttpServlet {
} else { } else {
if (session != null && file.endsWith(".jsp")) { if (session != null && file.endsWith(".jsp")) {
String page = StringUtils.utf8Decode(bytes); String page = StringUtils.utf8Decode(bytes);
page = PageParser.parse(server, page, session.map); page = PageParser.parse(page, session.map);
try { try {
bytes = StringUtils.utf8Encode(page); bytes = StringUtils.utf8Encode(page);
} catch (SQLException e) { } catch (SQLException e) {
......
...@@ -30,9 +30,9 @@ public class WebSession { ...@@ -30,9 +30,9 @@ public class WebSession {
long lastAccess; long lastAccess;
HashMap map = new HashMap(); HashMap map = new HashMap();
Locale locale; Locale locale;
WebServer server;
Statement executingStatement; Statement executingStatement;
ResultSet result; ResultSet result;
private WebServer server;
private ArrayList commandHistory = new ArrayList(); private ArrayList commandHistory = new ArrayList();
......
...@@ -226,7 +226,7 @@ class WebThread extends Thread implements DatabaseEventListener { ...@@ -226,7 +226,7 @@ class WebThread extends Thread implements DatabaseEventListener {
} else { } else {
if (session != null && file.endsWith(".jsp")) { if (session != null && file.endsWith(".jsp")) {
String page = StringUtils.utf8Decode(bytes); String page = StringUtils.utf8Decode(bytes);
page = PageParser.parse(server, page, session.map); page = PageParser.parse(page, session.map);
try { try {
bytes = StringUtils.utf8Encode(page); bytes = StringUtils.utf8Encode(page);
} catch (SQLException e) { } catch (SQLException e) {
......
...@@ -59,13 +59,15 @@ import org.h2.util.ObjectUtils; ...@@ -59,13 +59,15 @@ import org.h2.util.ObjectUtils;
public class DiskFile implements CacheWriter { public class DiskFile implements CacheWriter {
/** /**
* The size of a page in blocks. * The number of bits to shift to divide a position to get the page number.
* Each page contains blocks from the same storage.
*/ */
public static final int BLOCK_PAGE_PAGE_SHIFT = 6; public static final int BLOCK_PAGE_PAGE_SHIFT = 6;
/**
* The size of a page in blocks.
* Each page contains blocks from the same storage.
*/
public static final int BLOCKS_PER_PAGE = 1 << BLOCK_PAGE_PAGE_SHIFT; public static final int BLOCKS_PER_PAGE = 1 << BLOCK_PAGE_PAGE_SHIFT;
public static final int OFFSET = FileStore.HEADER_LENGTH;
/** /**
* The size of a block in bytes. * The size of a block in bytes.
...@@ -73,7 +75,9 @@ public class DiskFile implements CacheWriter { ...@@ -73,7 +75,9 @@ public class DiskFile implements CacheWriter {
*/ */
public static final int BLOCK_SIZE = 128; public static final int BLOCK_SIZE = 128;
static final int FREE_PAGE = -1; private static final int OFFSET = FileStore.HEADER_LENGTH;
private static final int FREE_PAGE = -1;
// TODO storage: header should probably be 4 KB or so // TODO storage: header should probably be 4 KB or so
// (to match block size of operating system) // (to match block size of operating system)
private Database database; private Database database;
...@@ -98,6 +102,16 @@ public class DiskFile implements CacheWriter { ...@@ -98,6 +102,16 @@ public class DiskFile implements CacheWriter {
private String mode; private String mode;
private int nextDeleteId = 1; private int nextDeleteId = 1;
/**
* Create a new disk file.
*
* @param database the database
* @param fileName the file name
* @param mode the file opening mode ("r", "rw", "rws", "rwd")
* @param dataFile if this is the data file
* @param logChanges if changes should be logged
* @param cacheSize the number of cache entries
*/
public DiskFile(Database database, String fileName, String mode, boolean dataFile, boolean logChanges, int cacheSize) throws SQLException { public DiskFile(Database database, String fileName, String mode, boolean dataFile, boolean logChanges, int cacheSize) throws SQLException {
reset(); reset();
this.database = database; this.database = database;
...@@ -152,10 +166,6 @@ public class DiskFile implements CacheWriter { ...@@ -152,10 +166,6 @@ public class DiskFile implements CacheWriter {
} }
} }
int getBlockCount() {
return fileBlockCount;
}
private void create() throws SQLException { private void create() throws SQLException {
file = database.openFile(fileName, mode, false); file = database.openFile(fileName, mode, false);
DataPage header = DataPage.create(database, OFFSET); DataPage header = DataPage.create(database, OFFSET);
...@@ -539,7 +549,7 @@ public class DiskFile implements CacheWriter { ...@@ -539,7 +549,7 @@ public class DiskFile implements CacheWriter {
throw Message.getSQLException(ErrorCode.SIMULATED_POWER_OFF); throw Message.getSQLException(ErrorCode.SIMULATED_POWER_OFF);
} }
blockCount = getPage(blockCount + BLOCKS_PER_PAGE - 1) * BLOCKS_PER_PAGE; blockCount = getPage(blockCount + BLOCKS_PER_PAGE - 1) * BLOCKS_PER_PAGE;
int lastPage = getPage(getBlockCount()); int lastPage = getPage(fileBlockCount);
int pageCount = getPage(blockCount); int pageCount = getPage(blockCount);
int pos = -1; int pos = -1;
boolean found = false; boolean found = false;
...@@ -557,7 +567,7 @@ public class DiskFile implements CacheWriter { ...@@ -557,7 +567,7 @@ public class DiskFile implements CacheWriter {
} }
} }
if (!found) { if (!found) {
int max = getBlockCount(); int max = fileBlockCount;
pos = MathUtils.roundUp(max, BLOCKS_PER_PAGE); pos = MathUtils.roundUp(max, BLOCKS_PER_PAGE);
if (rowBuff instanceof DataPageText) { if (rowBuff instanceof DataPageText) {
if (pos > max) { if (pos > max) {
...@@ -611,7 +621,7 @@ public class DiskFile implements CacheWriter { ...@@ -611,7 +621,7 @@ public class DiskFile implements CacheWriter {
} }
} }
void reuseSpace() throws SQLException { private void reuseSpace() throws SQLException {
if (SysProperties.REUSE_SPACE_QUICKLY) { if (SysProperties.REUSE_SPACE_QUICKLY) {
if (potentiallyFreePages.size() >= SysProperties.REUSE_SPACE_AFTER) { if (potentiallyFreePages.size() >= SysProperties.REUSE_SPACE_AFTER) {
Session[] sessions = database.getSessions(true); Session[] sessions = database.getSessions(true);
...@@ -791,7 +801,7 @@ public class DiskFile implements CacheWriter { ...@@ -791,7 +801,7 @@ public class DiskFile implements CacheWriter {
} }
} }
void writeDirectDeleted(int recordId, int blockCount) throws SQLException { private void writeDirectDeleted(int recordId, int blockCount) throws SQLException {
synchronized (database) { synchronized (database) {
go(recordId); go(recordId);
for (int i = 0; i < blockCount; i++) { for (int i = 0; i < blockCount; i++) {
...@@ -801,7 +811,7 @@ public class DiskFile implements CacheWriter { ...@@ -801,7 +811,7 @@ public class DiskFile implements CacheWriter {
} }
} }
void writeDirect(Storage storage, int pos, byte[] data, int offset) throws SQLException { private void writeDirect(Storage storage, int pos, byte[] data, int offset) throws SQLException {
synchronized (database) { synchronized (database) {
go(pos); go(pos);
file.write(data, offset, BLOCK_SIZE); file.write(data, offset, BLOCK_SIZE);
......
...@@ -70,7 +70,7 @@ public class FileStoreInputStream extends InputStream { ...@@ -70,7 +70,7 @@ public class FileStoreInputStream extends InputStream {
return read == 0 ? -1 : read; return read == 0 ? -1 : read;
} }
public int readBlock(byte[] buff, int off, int len) throws IOException { private int readBlock(byte[] buff, int off, int len) throws IOException {
fillBuffer(); fillBuffer();
if (endOfFile) { if (endOfFile) {
return -1; return -1;
......
...@@ -49,6 +49,11 @@ public abstract class Record extends CacheObject { ...@@ -49,6 +49,11 @@ public abstract class Record extends CacheObject {
// nothing to do // nothing to do
} }
/**
* Check if this record is empty.
*
* @return false
*/
public boolean isEmpty() { public boolean isEmpty() {
return false; return false;
} }
...@@ -65,6 +70,9 @@ public abstract class Record extends CacheObject { ...@@ -65,6 +70,9 @@ public abstract class Record extends CacheObject {
return sessionId; return sessionId;
} }
/**
* This record has been committed. The session id is reset.
*/
public void commit() { public void commit() {
this.sessionId = 0; this.sessionId = 0;
} }
...@@ -81,11 +89,23 @@ public abstract class Record extends CacheObject { ...@@ -81,11 +89,23 @@ public abstract class Record extends CacheObject {
return storageId; return storageId;
} }
/**
* Set the last log file and position where this record needs to be written.
*
* @param log the log file id
* @param pos the position in the log file
*/
public void setLastLog(int log, int pos) { public void setLastLog(int log, int pos) {
lastLog = log; lastLog = log;
lastPos = pos; lastPos = pos;
} }
/**
* Set the last log file and position where this record was written.
*
* @param log the log file id
* @param pos the position in the log file
*/
public void setLogWritten(int log, int pos) { public void setLogWritten(int log, int pos) {
if (log < lastLog) { if (log < lastLog) {
return; return;
...@@ -107,6 +127,11 @@ public abstract class Record extends CacheObject { ...@@ -107,6 +127,11 @@ public abstract class Record extends CacheObject {
return true; return true;
} }
/**
* Check if this record has been written to the log file.
*
* @return true if it is
*/
public boolean isLogWritten() { public boolean isLogWritten() {
return lastLog == LogSystem.LOG_WRITTEN; return lastLog == LogSystem.LOG_WRITTEN;
} }
......
...@@ -34,6 +34,10 @@ import org.h2.util.MathUtils; ...@@ -34,6 +34,10 @@ import org.h2.util.MathUtils;
*/ */
public class Storage { public class Storage {
/**
* This value is used to indicate that the position is not yet known, and
* space needs to be allocated.
*/
public static final int ALLOCATE_POS = -1; public static final int ALLOCATE_POS = -1;
private static final int FREE_LIST_SIZE = Math.max(1024, DiskFile.BLOCKS_PER_PAGE * 4); private static final int FREE_LIST_SIZE = Math.max(1024, DiskFile.BLOCKS_PER_PAGE * 4);
private DiskFile file; private DiskFile file;
...@@ -46,6 +50,14 @@ public class Storage { ...@@ -46,6 +50,14 @@ public class Storage {
private DataPage dummy; private DataPage dummy;
private int pageCheckIndex; private int pageCheckIndex;
/**
* Create a new storage object for this file.
*
* @param database the database
* @param file the file
* @param reader the reader that can parse records
* @param id the storage id
*/
public Storage(Database database, DiskFile file, RecordReader reader, int id) { public Storage(Database database, DiskFile file, RecordReader reader, int id) {
this.database = database; this.database = database;
this.file = file; this.file = file;
...@@ -54,6 +66,11 @@ public class Storage { ...@@ -54,6 +66,11 @@ public class Storage {
dummy = DataPage.create(database, 0); dummy = DataPage.create(database, 0);
} }
/**
* Get the record parser for this storage.
*
* @return the record parser
*/
public RecordReader getRecordReader() { public RecordReader getRecordReader() {
return reader; return reader;
} }
...@@ -62,10 +79,24 @@ public class Storage { ...@@ -62,10 +79,24 @@ public class Storage {
recordCount++; recordCount++;
} }
/**
* Read a record from the file or cache.
*
* @param session the session
* @param pos the position of the record
* @return the record
*/
public Record getRecord(Session session, int pos) throws SQLException { public Record getRecord(Session session, int pos) throws SQLException {
return file.getRecord(session, pos, reader, id); return file.getRecord(session, pos, reader, id);
} }
/**
* Read a record if it is stored at that location.
*
* @param session the session
* @param pos the position where it is stored
* @return the record or null
*/
public Record getRecordIfStored(Session session, int pos) throws SQLException { public Record getRecordIfStored(Session session, int pos) throws SQLException {
return file.getRecordIfStored(session, pos, reader, id); return file.getRecordIfStored(session, pos, reader, id);
} }
...@@ -119,11 +150,24 @@ public class Storage { ...@@ -119,11 +150,24 @@ public class Storage {
} }
} }
/**
* Update an existing record.
*
* @param session the session
* @param record the record
*/
public void updateRecord(Session session, Record record) throws SQLException { public void updateRecord(Session session, Record record) throws SQLException {
record.setDeleted(false); record.setDeleted(false);
file.updateRecord(session, record); file.updateRecord(session, record);
} }
/**
* Add or update a record in the file.
*
* @param session the session
* @param record the record
* @param pos the position (use ALLOCATE_POS to add a new record)
*/
public void addRecord(Session session, Record record, int pos) throws SQLException { public void addRecord(Session session, Record record, int pos) throws SQLException {
record.setStorageId(id); record.setStorageId(id);
int size = file.getRecordOverhead() + record.getByteCount(dummy); int size = file.getRecordOverhead() + record.getByteCount(dummy);
...@@ -142,6 +186,12 @@ public class Storage { ...@@ -142,6 +186,12 @@ public class Storage {
file.addRecord(session, record); file.addRecord(session, record);
} }
/**
* Remove a record.
*
* @param session the session
* @param pos where the record is stored
*/
public void removeRecord(Session session, int pos) throws SQLException { public void removeRecord(Session session, int pos) throws SQLException {
checkOnePage(); checkOnePage();
Record record = getRecord(session, pos); Record record = getRecord(session, pos);
...@@ -168,7 +218,7 @@ public class Storage { ...@@ -168,7 +218,7 @@ public class Storage {
} }
} }
public int allocate(int blockCount) throws SQLException { private int allocate(int blockCount) throws SQLException {
if (freeList.size() > 0) { if (freeList.size() > 0) {
synchronized (database) { synchronized (database) {
BitField used = file.getUsed(); BitField used = file.getUsed();
...@@ -202,10 +252,6 @@ public class Storage { ...@@ -202,10 +252,6 @@ public class Storage {
} }
} }
public void delete(Session session) throws SQLException {
truncate(session);
}
// private int allocateBest(int start, int blocks) { // private int allocateBest(int start, int blocks) {
// while (true) { // while (true) {
// int p = getLastUsedPlusOne(start, blocks); // int p = getLastUsedPlusOne(start, blocks);
...@@ -219,24 +265,49 @@ public class Storage { ...@@ -219,24 +265,49 @@ public class Storage {
// return start; // return start;
// } // }
/**
* Get the unique storage id.
*
* @return the id
*/
public int getId() { public int getId() {
return id; return id;
} }
/**
* Get the number of records in this storage.
*
* @return the number of records
*/
public int getRecordCount() { public int getRecordCount() {
return recordCount; return recordCount;
} }
/**
* Delete all records from this storage.
*
* @param session the session
*/
public void truncate(Session session) throws SQLException { public void truncate(Session session) throws SQLException {
freeList = new IntArray(); freeList = new IntArray();
recordCount = 0; recordCount = 0;
file.truncateStorage(session, this, pages); file.truncateStorage(session, this, pages);
} }
/**
* Set the record parser for this storage.
*
* @param reader the record parser
*/
public void setReader(RecordReader reader) { public void setReader(RecordReader reader) {
this.reader = reader; this.reader = reader;
} }
/**
* Write this record now.
*
* @param rec the record to write
*/
public void flushRecord(Record rec) throws SQLException { public void flushRecord(Record rec) throws SQLException {
file.writeBack(rec); file.writeBack(rec);
} }
...@@ -245,6 +316,11 @@ public class Storage { ...@@ -245,6 +316,11 @@ public class Storage {
file.flush(); file.flush();
} }
/**
* Get the overhead to store a record (header data) in number of bytes.
*
* @return the overhead
*/
public int getRecordOverhead() { public int getRecordOverhead() {
return file.getRecordOverhead(); return file.getRecordOverhead();
} }
...@@ -253,6 +329,11 @@ public class Storage { ...@@ -253,6 +329,11 @@ public class Storage {
return file; return file;
} }
/**
* Update the record count.
*
* @param recordCount the new record count
*/
public void setRecordCount(int recordCount) { public void setRecordCount(int recordCount) {
this.recordCount = recordCount; this.recordCount = recordCount;
} }
......
...@@ -50,6 +50,11 @@ public class WriterThread extends Thread { ...@@ -50,6 +50,11 @@ public class WriterThread extends Thread {
this.writeDelay = writeDelay; this.writeDelay = writeDelay;
} }
/**
* Change the write delay
*
* @param writeDelay the new write delay
*/
public void setWriteDelay(int writeDelay) { public void setWriteDelay(int writeDelay) {
LogSystem log = getLog(); LogSystem log = getLog();
this.writeDelay = writeDelay; this.writeDelay = writeDelay;
...@@ -61,6 +66,13 @@ public class WriterThread extends Thread { ...@@ -61,6 +66,13 @@ public class WriterThread extends Thread {
} }
} }
/**
* Create and start a new writer thread for the given database.
*
* @param database the database
* @param writeDelay the delay
* @return the writer thread object
*/
public static WriterThread create(Database database, int writeDelay) { public static WriterThread create(Database database, int writeDelay) {
WriterThread thread = new WriterThread(database, writeDelay); WriterThread thread = new WriterThread(database, writeDelay);
thread.setName("H2 Log Writer " + database.getShortName()); thread.setName("H2 Log Writer " + database.getShortName());
...@@ -155,11 +167,21 @@ public class WriterThread extends Thread { ...@@ -155,11 +167,21 @@ public class WriterThread extends Thread {
databaseRef = null; databaseRef = null;
} }
/**
* Stop the thread. This method is called when closing the database. Old log
* files are deleted as well.
*/
public void stopThread() throws SQLException { public void stopThread() throws SQLException {
stop = true; stop = true;
deleteLogFileLater(null); deleteLogFileLater(null);
} }
/**
* Delete the following log file later on. If there is already a file to be
* deleted, that one will be deleted immediately.
*
* @param fileName the name of the file to delete
*/
public synchronized void deleteLogFileLater(String fileName) throws SQLException { public synchronized void deleteLogFileLater(String fileName) throws SQLException {
if (oldLogFile != null) { if (oldLogFile != null) {
FileUtils.delete(oldLogFile); FileUtils.delete(oldLogFile);
......
...@@ -375,6 +375,7 @@ public class MetaTable extends Table { ...@@ -375,6 +375,7 @@ public class MetaTable extends Table {
"TABLE_CATALOG", "TABLE_CATALOG",
"TABLE_SCHEMA", "TABLE_SCHEMA",
"TABLE_NAME", "TABLE_NAME",
"UNIQUE_INDEX_NAME",
"CHECK_EXPRESSION", "CHECK_EXPRESSION",
"COLUMN_LIST", "COLUMN_LIST",
"REMARKS", "REMARKS",
...@@ -1276,6 +1277,11 @@ public class MetaTable extends Table { ...@@ -1276,6 +1277,11 @@ public class MetaTable extends Table {
String checkExpression = null; String checkExpression = null;
IndexColumn[] columns = null; IndexColumn[] columns = null;
Table table = constraint.getTable(); Table table = constraint.getTable();
Index index = constraint.getUniqueIndex();
String uniqueIndexName = null;
if (index != null) {
uniqueIndexName = index.getName();
}
String tableName = identifier(table.getName()); String tableName = identifier(table.getName());
if (!checkIndex(session, tableName, indexFrom, indexTo)) { if (!checkIndex(session, tableName, indexFrom, indexTo)) {
continue; continue;
...@@ -1313,6 +1319,8 @@ public class MetaTable extends Table { ...@@ -1313,6 +1319,8 @@ public class MetaTable extends Table {
identifier(table.getSchema().getName()), identifier(table.getSchema().getName()),
// TABLE_NAME // TABLE_NAME
tableName, tableName,
// UNIQUE_INDEX_NAME
uniqueIndexName,
// CHECK_EXPRESSION // CHECK_EXPRESSION
checkExpression, checkExpression,
// COLUMN_LIST // COLUMN_LIST
......
...@@ -146,6 +146,11 @@ public class RandomUtils { ...@@ -146,6 +146,11 @@ public class RandomUtils {
} }
} }
/**
* Get a cryptographically secure pseudo random long value.
*
* @return the random long value
*/
public static long getSecureLong() { public static long getSecureLong() {
SecureRandom sr = getSecureRandom(); SecureRandom sr = getSecureRandom();
synchronized (sr) { synchronized (sr) {
...@@ -153,6 +158,12 @@ public class RandomUtils { ...@@ -153,6 +158,12 @@ public class RandomUtils {
} }
} }
/**
* Get a number of cryptographically secure pseudo random bytes.
*
* @param len the number of bytes
* @return the random bytes
*/
public static byte[] getSecureBytes(int len) { public static byte[] getSecureBytes(int len) {
if (len <= 0) { if (len <= 0) {
len = 1; len = 1;
...@@ -165,8 +176,14 @@ public class RandomUtils { ...@@ -165,8 +176,14 @@ public class RandomUtils {
return buff; return buff;
} }
public static int nextInt(int max) { /**
return random.nextInt(max); * Get a cryptographically secure pseudo random int value between 0 (including and the given value (excluding).
*
* @param lower the value returned will be lower than this value
* @return the random long value
*/
public static int nextInt(int lower) {
return random.nextInt(lower);
} }
static void warn(String s, Throwable t) { static void warn(String s, Throwable t) {
......
...@@ -234,7 +234,7 @@ public class StringUtils { ...@@ -234,7 +234,7 @@ public class StringUtils {
} }
} }
public static String utf8Decode(byte[] bytes, int offset, int length) { private static String utf8Decode(byte[] bytes, int offset, int length) {
try { try {
return new String(bytes, offset, length, Constants.UTF8); return new String(bytes, offset, length, Constants.UTF8);
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
...@@ -466,7 +466,7 @@ public class StringUtils { ...@@ -466,7 +466,7 @@ public class StringUtils {
* @param s the string * @param s the string
* @return the indented string * @return the indented string
*/ */
public static String indent(String s) { private static String indent(String s) {
return indent(s, 4); return indent(s, 4);
} }
...@@ -476,7 +476,7 @@ public class StringUtils { ...@@ -476,7 +476,7 @@ public class StringUtils {
* @param spaces the number of spaces * @param spaces the number of spaces
* @return the indented string * @return the indented string
*/ */
public static String indent(String s, int spaces) { private static String indent(String s, int spaces) {
StringBuffer buff = new StringBuffer(s.length() + spaces); StringBuffer buff = new StringBuffer(s.length() + spaces);
for (int i = 0; i < s.length();) { for (int i = 0; i < s.length();) {
for (int j = 0; j < spaces; j++) { for (int j = 0; j < spaces; j++) {
......
...@@ -22,6 +22,11 @@ import org.h2.tools.RunScript; ...@@ -22,6 +22,11 @@ import org.h2.tools.RunScript;
*/ */
public class Compact { public class Compact {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
DeleteDbFiles.execute("data", "test", true); DeleteDbFiles.execute("data", "test", true);
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
......
...@@ -22,6 +22,11 @@ import org.h2.tools.SimpleResultSet; ...@@ -22,6 +22,11 @@ import org.h2.tools.SimpleResultSet;
*/ */
public class CsvSample { public class CsvSample {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws SQLException { public static void main(String[] args) throws SQLException {
CsvSample.write(); CsvSample.write();
CsvSample.read(); CsvSample.read();
......
...@@ -19,6 +19,11 @@ import java.sql.Statement; ...@@ -19,6 +19,11 @@ import java.sql.Statement;
*/ */
public class FileFunctions { public class FileFunctions {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:mem:", "sa", ""); Connection conn = DriverManager.getConnection("jdbc:h2:mem:", "sa", "");
......
...@@ -22,6 +22,11 @@ import org.h2.tools.SimpleResultSet; ...@@ -22,6 +22,11 @@ import org.h2.tools.SimpleResultSet;
*/ */
public class Function { public class Function {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:mem:", "sa", ""); Connection conn = DriverManager.getConnection("jdbc:h2:mem:", "sa", "");
......
...@@ -24,6 +24,11 @@ import org.h2.tools.SimpleResultSet; ...@@ -24,6 +24,11 @@ import org.h2.tools.SimpleResultSet;
*/ */
public class FunctionMultiReturn { public class FunctionMultiReturn {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:mem:", "sa", ""); Connection conn = DriverManager.getConnection("jdbc:h2:mem:", "sa", "");
......
...@@ -21,6 +21,11 @@ import org.h2.tools.RunScript; ...@@ -21,6 +21,11 @@ import org.h2.tools.RunScript;
*/ */
public class InitDatabaseFromJar { public class InitDatabaseFromJar {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
new InitDatabaseFromJar().createScript(); new InitDatabaseFromJar().createScript();
new InitDatabaseFromJar().initDb(); new InitDatabaseFromJar().initDb();
......
...@@ -19,6 +19,11 @@ import org.h2.tools.Server; ...@@ -19,6 +19,11 @@ import org.h2.tools.Server;
*/ */
public class MixedMode { public class MixedMode {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
// start the server, allows to access the database remotely // start the server, allows to access the database remotely
......
...@@ -25,6 +25,11 @@ import org.h2.util.StringUtils; ...@@ -25,6 +25,11 @@ import org.h2.util.StringUtils;
*/ */
public class Newsfeed { public class Newsfeed {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
String targetDir = args.length == 0 ? "." : args[0]; String targetDir = args.length == 0 ? "." : args[0];
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
......
...@@ -26,6 +26,11 @@ public class SQLInjection { ...@@ -26,6 +26,11 @@ public class SQLInjection {
private Connection conn; private Connection conn;
private Statement stat; private Statement stat;
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
new SQLInjection().run("org.h2.Driver", new SQLInjection().run("org.h2.Driver",
"jdbc:h2:test", "sa", "sa"); "jdbc:h2:test", "sa", "sa");
......
...@@ -19,6 +19,11 @@ import java.util.Properties; ...@@ -19,6 +19,11 @@ import java.util.Properties;
*/ */
public class SecurePassword { public class SecurePassword {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] argv) throws Exception { public static void main(String[] argv) throws Exception {
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
......
...@@ -12,6 +12,11 @@ package org.h2.samples; ...@@ -12,6 +12,11 @@ package org.h2.samples;
*/ */
public class ShutdownServer { public class ShutdownServer {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
org.h2.tools.Server.shutdownTcpServer("tcp://localhost:9094", "", false); org.h2.tools.Server.shutdownTcpServer("tcp://localhost:9094", "", false);
} }
......
...@@ -21,6 +21,11 @@ import org.h2.api.Trigger; ...@@ -21,6 +21,11 @@ import org.h2.api.Trigger;
*/ */
public class TriggerSample { public class TriggerSample {
/**
* This method is called when executing this sample application.
*
* @param args the command line parameters
*/
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:mem:", "sa", ""); Connection conn = DriverManager.getConnection("jdbc:h2:mem:", "sa", "");
......
...@@ -167,12 +167,38 @@ java org.h2.test.TestAll timer ...@@ -167,12 +167,38 @@ java org.h2.test.TestAll timer
/* /*
grant select on test to sa;
C:\download\Data Concurrency and Consistency.pdf
detect deadlock alarm
not tested: not tested:
PreparedProcedure PREPARE <name>(column,...) AS ... PreparedProcedure PREPARE <name>(column,...) AS ...
Procedure Procedure
DeallocateProcedure DEALLOCATE [PLAN] <name> DeallocateProcedure DEALLOCATE [PLAN] <name>
ExecuteProcedure EXECUTE <name>[([p[,...])] ExecuteProcedure EXECUTE <name>[([p[,...])]
> DROP TABLE IF EXISTS TEST;
> CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255));
> INSERT INTO TEST VALUES(1, 'Hello');
> INSERT INTO TEST VALUES(2, 'World');
> CREATE USER IF NOT EXISTS SA PASSWORD '' ADMIN;
> CREATE USER IF NOT EXISTS SERGEY PASSWORD '' ;
> CREATE ROLE IF NOT EXISTS MANAGER;
> GRANT MANAGER TO SERGEY;
> GRANT SELECT ON PUBLIC.TEST TO MANAGER;
>
> SCRIPT DROP TO 'SCRIPT_ERROR.SQL';
> RUNSCRIPT FROM 'SCRIPT_ERROR.SQL';
Ignore errors if role already granted? What do other dbs do?
Maybe add a REVOKE before the GRANT?
Concurrent update in table test: another transaction has updated or deleted the same row
when exactly does it occur in other databases (PostgreSQL, Oracle)?
create an mbean for each database? server? (jconsole) create an mbean for each database? server? (jconsole)
jazoon jazoon
...@@ -230,6 +256,11 @@ Add where required // TODO: change in version 1.1 ...@@ -230,6 +256,11 @@ Add where required // TODO: change in version 1.1
http://www.w3schools.com/sql/ http://www.w3schools.com/sql/
History: History:
New column INFORMATION_SCHEMA.CONSTRAINTS.UNIQUE_INDEX_NAME
that contains the name of the unique index used to enforce this
constraint, if there is such an index.
It is now possible to grant or revoke a role to a user multiple times without
getting an exception.
Roadmap: Roadmap:
......
--- special grammar and test cases --------------------------------------------------------------------------------------------- --- special grammar and test cases ---------------------------------------------------------------------------------------------
CREATE ROLE TEST_A;
> ok
GRANT TEST_A TO TEST_A;
> exception
CREATE ROLE TEST_B;
> ok
GRANT TEST_A TO TEST_B;
> ok
GRANT TEST_B TO TEST_A;
> exception
DROP ROLE TEST_A;
> ok
DROP ROLE TEST_B;
> ok
CREATE ROLE PUBLIC2;
> ok
GRANT PUBLIC2 TO SA;
> ok
GRANT PUBLIC2 TO SA;
> ok
REVOKE PUBLIC2 FROM SA;
> ok
REVOKE PUBLIC2 FROM SA;
> ok
DROP ROLE PUBLIC2;
> ok
create table test(id int primary key, lastname varchar, firstname varchar, parent int references(id));
> ok
alter table test add constraint name unique (lastname, firstname);
> ok
SELECT CONSTRAINT_NAME, UNIQUE_INDEX_NAME, COLUMN_LIST FROM INFORMATION_SCHEMA.CONSTRAINTS ;
> CONSTRAINT_NAME UNIQUE_INDEX_NAME COLUMN_LIST
> --------------- ----------------- ------------------
> CONSTRAINT_2 PRIMARY_KEY_2 ID
> CONSTRAINT_27 PRIMARY_KEY_2 PARENT
> NAME NAME_INDEX_2 LASTNAME,FIRSTNAME
> rows: 3
drop table test;
> ok
alter table information_schema.help rename to information_schema.help2; alter table information_schema.help rename to information_schema.help2;
> exception > exception
......
...@@ -99,7 +99,7 @@ public class GenerateDoc { ...@@ -99,7 +99,7 @@ public class GenerateDoc {
byte[] bytes = IOUtils.readBytesAndClose(in, 0); byte[] bytes = IOUtils.readBytesAndClose(in, 0);
if (fileName.endsWith(".html")) { if (fileName.endsWith(".html")) {
String page = new String(bytes); String page = new String(bytes);
page = PageParser.parse(null, page, session); page = PageParser.parse(page, session);
bytes = page.getBytes(); bytes = page.getBytes();
} }
out.write(bytes); out.write(bytes);
......
...@@ -136,7 +136,7 @@ public class PrepareTranslation { ...@@ -136,7 +136,7 @@ public class PrepareTranslation {
// remove '.jsp' // remove '.jsp'
name = name.substring(0, name.length() - 4); name = name.substring(0, name.length() - 4);
String template = IOUtils.readStringAndClose(new FileReader(templateDir + "/" + name + ".jsp"), -1); String template = IOUtils.readStringAndClose(new FileReader(templateDir + "/" + name + ".jsp"), -1);
String html = PageParser.parse(null, template, prop); String html = PageParser.parse(template, prop);
html = StringUtils.replaceAll(html, "lang=\"" + MAIN_LANGUAGE + "\"", "lang=\"" + language + "\""); html = StringUtils.replaceAll(html, "lang=\"" + MAIN_LANGUAGE + "\"", "lang=\"" + language + "\"");
for (int j = 0; j < fileNames.size(); j++) { for (int j = 0; j < fileNames.size(); j++) {
String n = (String) fileNames.get(j); String n = (String) fileNames.get(j);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论