提交 8da94fcb authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 2e63b5ff
...@@ -155,6 +155,8 @@ ...@@ -155,6 +155,8 @@
90132=Aggregate {0} not found 90132=Aggregate {0} not found
90133=Cannot change the setting {0} when the database is already open 90133=Cannot change the setting {0} when the database is already open
90134=Access to the class {0} is denied 90134=Access to the class {0} is denied
90135=The database is open in exclusive mode; can not open additional connections
90136=Unsupported outer join condition\: {0}
HY000=General error\: {0} HY000=General error\: {0}
HY004=Unknown data type\: {0} HY004=Unknown data type\: {0}
HYC00=Feature not supported HYC00=Feature not supported
......
...@@ -2397,7 +2397,10 @@ public class Parser { ...@@ -2397,7 +2397,10 @@ public class Parser {
if (types[i] == CHAR_SPECIAL_2) { if (types[i] == CHAR_SPECIAL_2) {
i++; i++;
} }
// fall through currentToken = sqlCommand.substring(start, i);
currentTokenType = getSpecialType(currentToken);
parseIndex = i;
return;
case CHAR_SPECIAL_1: case CHAR_SPECIAL_1:
currentToken = sqlCommand.substring(start, i); currentToken = sqlCommand.substring(start, i);
currentTokenType = getSpecialType(currentToken); currentTokenType = getSpecialType(currentToken);
...@@ -2600,7 +2603,6 @@ public class Parser { ...@@ -2600,7 +2603,6 @@ public class Parser {
command[i] = ' '; command[i] = ' ';
command[i + 1] = ' '; command[i + 1] = ' ';
i++; i++;
break;
} else if (command[i + 1] == '/') { } else if (command[i + 1] == '/') {
// single line comment // single line comment
changed = true; changed = true;
...@@ -2613,9 +2615,10 @@ public class Parser { ...@@ -2613,9 +2615,10 @@ public class Parser {
command[i++] = ' '; command[i++] = ' ';
checkRunOver(i, len, startLoop); checkRunOver(i, len, startLoop);
} }
break; } else {
type = CHAR_SPECIAL_1;
} }
// fall through break;
case '-': case '-':
if (command[i + 1] == '-') { if (command[i + 1] == '-') {
// single line comment // single line comment
...@@ -2629,9 +2632,10 @@ public class Parser { ...@@ -2629,9 +2632,10 @@ public class Parser {
command[i++] = ' '; command[i++] = ' ';
checkRunOver(i, len, startLoop); checkRunOver(i, len, startLoop);
} }
break; } else {
type = CHAR_SPECIAL_1;
} }
// fall through break;
case '(': case '(':
case ')': case ')':
case '{': case '{':
......
...@@ -549,6 +549,9 @@ public class Select extends Query { ...@@ -549,6 +549,9 @@ public class Select extends Query {
Expression on = f.getJoinCondition(); Expression on = f.getJoinCondition();
if (on != null) { if (on != null) {
if (!on.isEverything(ExpressionVisitor.EVALUATABLE)) { if (!on.isEverything(ExpressionVisitor.EVALUATABLE)) {
if (f.isJoinOuter()) {
throw Message.getSQLException(ErrorCode.UNSUPPORTED_OUTER_JOIN_CONDITION_1, on.getSQL());
}
f.removeJoinCondition(); f.removeJoinCondition();
// need to check that all added are bound to a table // need to check that all added are bound to a table
on = on.optimize(session); on = on.optimize(session);
......
...@@ -316,6 +316,7 @@ public class ErrorCode { ...@@ -316,6 +316,7 @@ public class ErrorCode {
public static final int CANNOT_CHANGE_SETTING_WHEN_OPEN_1 = 90133; public static final int CANNOT_CHANGE_SETTING_WHEN_OPEN_1 = 90133;
public static final int ACCESS_DENIED_TO_CLASS_1 = 90134; public static final int ACCESS_DENIED_TO_CLASS_1 = 90134;
public static final int DATABASE_IS_IN_EXCLUSIVE_MODE = 90135; public static final int DATABASE_IS_IN_EXCLUSIVE_MODE = 90135;
public static final int UNSUPPORTED_OUTER_JOIN_CONDITION_1 = 90136;
/** /**
* INTERNAL * INTERNAL
......
...@@ -127,6 +127,7 @@ public class ScanIndex extends BaseIndex { ...@@ -127,6 +127,7 @@ public class ScanIndex extends BaseIndex {
row.setPos(key); row.setPos(key);
rows.set(key, row); rows.set(key, row);
} }
row.setDeleted(false);
} }
if (database.isMultiVersion()) { if (database.isMultiVersion()) {
if (delta == null) { if (delta == null) {
......
...@@ -155,7 +155,8 @@ ...@@ -155,7 +155,8 @@
90132=Aggregat-Funktion {0} nicht gefunden 90132=Aggregat-Funktion {0} nicht gefunden
90133=Kann das Setting {0} nicht \u00E4ndern wenn die Datenbank bereits ge\u00F6ffnet ist 90133=Kann das Setting {0} nicht \u00E4ndern wenn die Datenbank bereits ge\u00F6ffnet ist
90134=Der Zugriff auf die Klasse {0} ist nicht erlaubt 90134=Der Zugriff auf die Klasse {0} ist nicht erlaubt
90135=Die Datenbank befindet sich im Exclusiv Modus; es können keine zusätzlichen Verbindungen geöffnet werden 90135=Die Datenbank befindet sich im Exclusiv Modus; es k\u00F6nnen keine zus\u00E4tzlichen Verbindungen ge\u00F6ffnet werden
90136=Diese Outer Join Bedingung wird nicht unterst\u00FCtzt\: {0}
HY000=Allgemeiner Fehler\: {0} HY000=Allgemeiner Fehler\: {0}
HY004=Unbekannter Datentyp\: {0} HY004=Unbekannter Datentyp\: {0}
HYC00=Dieses Feature wird unterst\u00FCtzt HYC00=Dieses Feature wird unterst\u00FCtzt
......
...@@ -156,6 +156,7 @@ ...@@ -156,6 +156,7 @@
90133=Cannot change the setting {0} when the database is already open 90133=Cannot change the setting {0} when the database is already open
90134=Access to the class {0} is denied 90134=Access to the class {0} is denied
90135=The database is open in exclusive mode; can not open additional connections 90135=The database is open in exclusive mode; can not open additional connections
90136=Unsupported outer join condition\: {0}
HY000=General error\: {0} HY000=General error\: {0}
HY004=Unknown data type\: {0} HY004=Unknown data type\: {0}
HYC00=Feature not supported HYC00=Feature not supported
......
...@@ -156,6 +156,7 @@ ...@@ -156,6 +156,7 @@
90133=\#Cannot change the setting {0} when the database is already open 90133=\#Cannot change the setting {0} when the database is already open
90134=\#Access to the class {0} is denied 90134=\#Access to the class {0} is denied
90135=\#The database is open in exclusive mode; can not open additional connections 90135=\#The database is open in exclusive mode; can not open additional connections
90136=\#Unsupported outer join condition\: {0}
HY000=\u4E00\u822C\u30A8\u30E9\u30FC\: {0} HY000=\u4E00\u822C\u30A8\u30E9\u30FC\: {0}
HY004=\u4E0D\u660E\u306A\u30C7\u30FC\u30BF\u578B\: {0} HY004=\u4E0D\u660E\u306A\u30C7\u30FC\u30BF\u578B\: {0}
HYC00=\u6A5F\u80FD\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093 HYC00=\u6A5F\u80FD\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093
......
...@@ -156,6 +156,7 @@ ...@@ -156,6 +156,7 @@
90133=\#Cannot change the setting {0} when the database is already open 90133=\#Cannot change the setting {0} when the database is already open
90134=\#Access to the class {0} is denied 90134=\#Access to the class {0} is denied
90135=\#The database is open in exclusive mode; can not open additional connections 90135=\#The database is open in exclusive mode; can not open additional connections
90136=\#Unsupported outer join condition\: {0}
HY000=Blad ogolny\: {0} HY000=Blad ogolny\: {0}
HY004=Nieznany typ danyche\: {0} HY004=Nieznany typ danyche\: {0}
HYC00=Cecha nie jest wspierana HYC00=Cecha nie jest wspierana
......
...@@ -156,6 +156,7 @@ ...@@ -156,6 +156,7 @@
90133=\#Cannot change the setting {0} when the database is already open 90133=\#Cannot change the setting {0} when the database is already open
90134=\#Access to the class {0} is denied 90134=\#Access to the class {0} is denied
90135=\#The database is open in exclusive mode; can not open additional connections 90135=\#The database is open in exclusive mode; can not open additional connections
90136=\#Unsupported outer join condition\: {0}
HY000=Erro geral\: {0} HY000=Erro geral\: {0}
HY004=Tipo de dados desconhecido\: {0} HY004=Tipo de dados desconhecido\: {0}
HYC00=Recurso n\u00E3o suportado HYC00=Recurso n\u00E3o suportado
......
...@@ -91,6 +91,12 @@ public class FtpControl extends Thread { ...@@ -91,6 +91,12 @@ public class FtpControl extends Thread {
return; return;
} }
server.log(">" + command); server.log(">" + command);
FtpEventListener listener = server.getEventListener();
FtpEvent event = null;
if (listener != null) {
event = new FtpEvent(this, command, param);
listener.beforeCommand(event);
}
replied = false; replied = false;
if (connected) { if (connected) {
processConnected(command, param); processConnected(command, param);
...@@ -126,8 +132,12 @@ public class FtpControl extends Thread { ...@@ -126,8 +132,12 @@ public class FtpControl extends Thread {
} }
} }
if (!replied) { if (!replied) {
listener.onUnsupportedCommand(event);
reply(506, "Invalid command"); reply(506, "Invalid command");
} }
if (listener != null) {
listener.afterCommand(event);
}
} }
private void processConnected(String command, String param) throws SQLException, IOException { private void processConnected(String command, String param) throws SQLException, IOException {
......
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.server.ftp;
/**
* Describes an FTP event. This class is used by the FtpEventListener.
*/
public class FtpEvent {
private final FtpControl control;
private final String command;
private final String param;
FtpEvent(FtpControl control, String command, String param) {
this.control = control;
this.command = command;
this.param = param;
}
/**
* Get the FTP command. Example: RETR
*
* @return the command
*/
public String getCommand() {
return command;
}
/**
* Get the FTP control object.
*
* @return the control object
*/
public FtpControl getControl() {
return control;
}
/**
* Get the parameter of the FTP command (if any).
*
* @return the parameter
*/
public String getParam() {
return param;
}
}
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.server.ftp;
/**
* Event listener for the FTP Server.
*/
public interface FtpEventListener {
/**
* Called before the given command is processed.
*
* @param event the event
*/
void beforeCommand(FtpEvent event);
/**
* Called after the command has been processed.
*
* @param event the event
*/
void afterCommand(FtpEvent event);
/**
* Called when an unsupported command is processed.
* This method is called after beforeCommand.
*
* @param event the event
*/
void onUnsupportedCommand(FtpEvent event);
}
...@@ -56,6 +56,8 @@ public class FtpServer implements Service { ...@@ -56,6 +56,8 @@ public class FtpServer implements Service {
private boolean allowTask; private boolean allowTask;
static final String TASK_SUFFIX = ".task"; static final String TASK_SUFFIX = ".task";
private FtpEventListener eventListener;
public void listen() { public void listen() {
try { try {
while (serverSocket != null) { while (serverSocket != null) {
...@@ -311,8 +313,31 @@ public class FtpServer implements Service { ...@@ -311,8 +313,31 @@ public class FtpServer implements Service {
p.destroy(); p.destroy();
} }
/**
* Get the file system used by this FTP server.
*
* @return the file system
*/
public FileSystem getFileSystem() { public FileSystem getFileSystem() {
return fs; return fs;
} }
/**
* Set the event listener. Only one listener can be registered.
*
* @param eventListener the new listener, or null to de-register
*/
public void setEventListener(FtpEventListener eventListener) {
this.eventListener = eventListener;
}
/**
* Get the registered event listener.
*
* @return the event listener, or null if non is registered
*/
public FtpEventListener getEventListener() {
return eventListener;
}
} }
...@@ -200,32 +200,32 @@ function keyDown(event) { ...@@ -200,32 +200,32 @@ function keyDown(event) {
return false; return false;
} }
if (key == 13 && event.ctrlKey) { if (key == 13 && event.ctrlKey) {
// ctrl + return // ctrl + return
document.h2query.submit(); document.h2query.submit();
return false; return false;
} else if(key == 32 && event.ctrlKey) { } else if(key == 32 && event.ctrlKey) {
// ctrl + space // ctrl + space
autoCompleteManual = true; autoCompleteManual = true;
lastQuery = null; lastQuery = null;
lastList = ''; lastList = '';
showAutoCompleteNow(); showAutoCompleteNow();
return false; return false;
} else if(key == 190 && autoComplete==0) { } else if(key == 190 && autoComplete==0) {
// dot // dot
help(); help();
return true; return true;
} }
var table = getAutoCompleteTable(); var table = getAutoCompleteTable();
if(table.rows.length > 0) { if(table.rows.length > 0) {
if(key == 27) { if(key == 27) {
// escape // escape
while(table.rows.length > 0) { while(table.rows.length > 0) {
table.deleteRow(0); table.deleteRow(0);
} }
showOutput(''); showOutput('');
return false; return false;
} else if((key == 13 && !event.shiftKey) || (key==9 && !event.shiftKey)) { } else if((key == 13 && !event.shiftKey) || (key==9 && !event.shiftKey)) {
// enter or tab // enter or tab
if(table.rows.length > selectedRow) { if(table.rows.length > selectedRow) {
var row = table.rows[selectedRow]; var row = table.rows[selectedRow];
if(row.cells.length>1) { if(row.cells.length>1) {
...@@ -237,14 +237,14 @@ function keyDown(event) { ...@@ -237,14 +237,14 @@ function keyDown(event) {
return false; return false;
} }
} else if(key == 38 && !event.shiftKey) { } else if(key == 38 && !event.shiftKey) {
// up // up
if(table.rows.length > selectedRow) { if(table.rows.length > selectedRow) {
selectedRow = selectedRow <= 0 ? table.rows.length-1 : selectedRow-1; selectedRow = selectedRow <= 0 ? table.rows.length-1 : selectedRow-1;
highlightRow(selectedRow); highlightRow(selectedRow);
} }
return false; return false;
} else if(key == 40 && !event.shiftKey) { } else if(key == 40 && !event.shiftKey) {
// down // down
if(table.rows.length > selectedRow) { if(table.rows.length > selectedRow) {
selectedRow = selectedRow >= table.rows.length-1 ? 0 : selectedRow+1; selectedRow = selectedRow >= table.rows.length-1 ? 0 : selectedRow+1;
highlightRow(selectedRow); highlightRow(selectedRow);
...@@ -262,11 +262,11 @@ function keyDown(event) { ...@@ -262,11 +262,11 @@ function keyDown(event) {
function keyUp(event) { function keyUp(event) {
if(autoComplete != 0) { if(autoComplete != 0) {
var key=event == null ? 0 : (event.keyCode? event.keyCode : event.charCode); var key=event == null ? 0 : (event.keyCode? event.keyCode : event.charCode);
if(key != 37 && key != 38 && key != 39 && key != 40) { if(key != 37 && key != 38 && key != 39 && key != 40) {
// left, right, up, down: don't show autocomplete // left, right, up, down: don't show autocomplete
showAutoComplete(); showAutoComplete();
} }
} }
return true; return true;
} }
......
...@@ -61,32 +61,32 @@ function editRow(row, session, write, undo) { ...@@ -61,32 +61,32 @@ function editRow(row, session, write, undo) {
} }
function editCancel(row) { function editCancel(row) {
var editing = document.getElementById('editing'); var editing = document.getElementById('editing');
editing.row.value = row; editing.row.value = row;
editing.op.value='3'; editing.op.value='3';
editing.submit(); editing.submit();
} }
function editOk(row) { function editOk(row) {
var editing = document.getElementById('editing'); var editing = document.getElementById('editing');
editing.row.value = row; editing.row.value = row;
editing.op.value='1'; editing.op.value='1';
editing.submit(); editing.submit();
} }
function editKeyDown(row, object, event) { function editKeyDown(row, object, event) {
var key=event.keyCode? event.keyCode : event.charCode; var key=event.keyCode? event.keyCode : event.charCode;
if(key == 46 && event.ctrlKey) { if(key == 46 && event.ctrlKey) {
// ctrl + delete // ctrl + delete
object.value = 'null'; object.value = 'null';
return false; return false;
} else if(key == 13) { } else if(key == 13) {
editOk(row); editOk(row);
return false; return false;
} else if(key == 27) { } else if(key == 27) {
editCancel(row); editCancel(row);
return false; return false;
} }
} }
function getInnerText(el) { function getInnerText(el) {
......
...@@ -336,8 +336,6 @@ public class DiskFile implements CacheWriter { ...@@ -336,8 +336,6 @@ public class DiskFile implements CacheWriter {
Record rec = (Record) list.get(i); Record rec = (Record) list.get(i);
writeBack(rec); writeBack(rec);
} }
// TODO flush performance: maybe it would be faster to write records in
// the same loop
for (int i = 0; i < fileBlockCount; i++) { for (int i = 0; i < fileBlockCount; i++) {
i = deleted.nextSetBit(i); i = deleted.nextSetBit(i);
if (i < 0) { if (i < 0) {
...@@ -351,32 +349,33 @@ public class DiskFile implements CacheWriter { ...@@ -351,32 +349,33 @@ public class DiskFile implements CacheWriter {
} }
} }
public void flushNew() throws SQLException { // this implementation accesses the file in a linear way
int todoTest; // public void flushNew() throws SQLException {
synchronized (database) { // int todoTest;
database.checkPowerOff(); // synchronized (database) {
ObjectArray list = cache.getAllChanged(); // database.checkPowerOff();
CacheObject.sort(list); // ObjectArray list = cache.getAllChanged();
int deletePos = deleted.nextSetBit(0); // CacheObject.sort(list);
int writeIndex = 0; // int deletePos = deleted.nextSetBit(0);
Record writeRecord = null; // int writeIndex = 0;
while (true) { // Record writeRecord = null;
if (writeRecord == null && writeIndex < list.size()) { // while (true) {
writeRecord = (Record) list.get(writeIndex++); // if (writeRecord == null && writeIndex < list.size()) {
} // writeRecord = (Record) list.get(writeIndex++);
if (writeRecord != null && (deletePos < 0 || writeRecord.getPos() < deletePos)) { // }
writeBack(writeRecord); // if (writeRecord != null && (deletePos < 0 || writeRecord.getPos() < deletePos)) {
writeRecord = null; // writeBack(writeRecord);
} else if (deletePos < fileBlockCount && deletePos >= 0) { // writeRecord = null;
writeDirectDeleted(deletePos, 1); // } else if (deletePos < fileBlockCount && deletePos >= 0) {
deleted.clear(deletePos); // writeDirectDeleted(deletePos, 1);
deletePos = deleted.nextSetBit(deletePos); // deleted.clear(deletePos);
} else { // deletePos = deleted.nextSetBit(deletePos);
break; // } else {
} // break;
} // }
} // }
} // }
// }
public void close() throws SQLException { public void close() throws SQLException {
synchronized (database) { synchronized (database) {
......
...@@ -24,14 +24,13 @@ public class FileLister { ...@@ -24,14 +24,13 @@ public class FileLister {
/** /**
* Get the list of database files. * Get the list of database files.
* *
* @param dir the directory (null for the current directory) * @param dir the directory (null for the current directory)
* @param db the database name (null for all databases) * @param db the database name (null for all databases)
* @param all if true, files such as the lock, trace, hash index, and lob * @param all if true, files such as the lock, trace, hash index, and lob
* files are included. If false, only data, index and log files * files are included. If false, only data, index and log files
* are returned * are returned
* @return the list of files * @return the list of files
* @throws SQLException
*/ */
public static ArrayList getDatabaseFiles(String dir, String db, boolean all) throws SQLException { public static ArrayList getDatabaseFiles(String dir, String db, boolean all) throws SQLException {
if (dir == null || dir.equals("")) { if (dir == null || dir.equals("")) {
......
...@@ -15,13 +15,13 @@ import org.h2.util.IntArray; ...@@ -15,13 +15,13 @@ import org.h2.util.IntArray;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
/** /**
* This class represents an persistent container that stores data of a table or an index. * This class represents an persistent container that stores data of a table or an index.
* An object contains a list of records, see {@link Record}. * An object contains a list of records, see {@link Record}.
* For each storage there is a {@link RecordReader} object that knows how to * For each storage there is a {@link RecordReader} object that knows how to
* convert records into a byte array and vice versa. * convert records into a byte array and vice versa.
* The data is stored in a {@link DiskFile}. A storage occupies a number of pages * The data is stored in a {@link DiskFile}. A storage occupies a number of pages
* in a file. * in a file.
* *
* File format: * File format:
* <pre> * <pre>
* int block size * int block size
...@@ -262,7 +262,7 @@ public class Storage { ...@@ -262,7 +262,7 @@ public class Storage {
void removePage(int i) { void removePage(int i) {
pages.removeValue(i); pages.removeValue(i);
} }
private void checkOnePage() throws SQLException { private void checkOnePage() throws SQLException {
pageCheckIndex = (pageCheckIndex + 1) % pages.size(); pageCheckIndex = (pageCheckIndex + 1) % pages.size();
int page = pages.get(pageCheckIndex); int page = pages.get(pageCheckIndex);
......
...@@ -53,7 +53,7 @@ public class Csv implements SimpleRowSource { ...@@ -53,7 +53,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Get a new object of this class. * Get a new object of this class.
* *
* @return the new instance * @return the new instance
*/ */
public static Csv getInstance() { public static Csv getInstance() {
...@@ -86,7 +86,7 @@ public class Csv implements SimpleRowSource { ...@@ -86,7 +86,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Writes the result set to a file in the CSV format. * Writes the result set to a file in the CSV format.
* *
* @param writer * @param writer
* the writer * the writer
* @param rs * @param rs
...@@ -102,7 +102,7 @@ public class Csv implements SimpleRowSource { ...@@ -102,7 +102,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Writes the result set to a file in the CSV format. * Writes the result set to a file in the CSV format.
* *
* @param fileName * @param fileName
* the name of the csv file * the name of the csv file
* @param rs * @param rs
...@@ -124,7 +124,7 @@ public class Csv implements SimpleRowSource { ...@@ -124,7 +124,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Writes the result set of a query to a file in the CSV format. * Writes the result set of a query to a file in the CSV format.
* *
* @param conn * @param conn
* the connection * the connection
* @param fileName * @param fileName
...@@ -148,7 +148,7 @@ public class Csv implements SimpleRowSource { ...@@ -148,7 +148,7 @@ public class Csv implements SimpleRowSource {
* Reads from the CSV file and returns a result set. The rows in the result * Reads from the CSV file and returns a result set. The rows in the result
* set are created on demand, that means the file is kept open until all * set are created on demand, that means the file is kept open until all
* rows are read or the result set is closed. * rows are read or the result set is closed.
* *
* @param fileName the file name * @param fileName the file name
* @param colNames or null if the column names should be read from the CSV file * @param colNames or null if the column names should be read from the CSV file
* @param charset the charset or null to use UTF-8 * @param charset the charset or null to use UTF-8
...@@ -168,7 +168,7 @@ public class Csv implements SimpleRowSource { ...@@ -168,7 +168,7 @@ public class Csv implements SimpleRowSource {
* Reads CSV data from a reader and returns a result set. The rows in the * Reads CSV data from a reader and returns a result set. The rows in the
* result set are created on demand, that means the reader is kept open * result set are created on demand, that means the reader is kept open
* until all rows are read or the result set is closed. * until all rows are read or the result set is closed.
* *
* @param reader the reader * @param reader the reader
* @param colNames or null if the column names should be read from the CSV file * @param colNames or null if the column names should be read from the CSV file
* @return the result set * @return the result set
...@@ -347,15 +347,6 @@ public class Csv implements SimpleRowSource { ...@@ -347,15 +347,6 @@ public class Csv implements SimpleRowSource {
continue; continue;
} else if (ch == fieldSeparatorRead) { } else if (ch == fieldSeparatorRead) {
break; break;
} else if (ch == commentLineStart) {
while (true) {
ch = readChar();
if (ch < 0 || ch == '\r' || ch == '\n') {
break;
}
}
endOfLine = true;
break;
} else if (ch == fieldDelimiter) { } else if (ch == fieldDelimiter) {
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
boolean containsEscape = false; boolean containsEscape = false;
...@@ -406,6 +397,15 @@ public class Csv implements SimpleRowSource { ...@@ -406,6 +397,15 @@ public class Csv implements SimpleRowSource {
} }
} }
break; break;
} else if (ch == commentLineStart) {
while (true) {
ch = readChar();
if (ch < 0 || ch == '\r' || ch == '\n') {
break;
}
}
endOfLine = true;
break;
} else { } else {
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
buff.append((char) ch); buff.append((char) ch);
...@@ -433,13 +433,22 @@ public class Csv implements SimpleRowSource { ...@@ -433,13 +433,22 @@ public class Csv implements SimpleRowSource {
private String unEscape(String s) { private String unEscape(String s) {
StringBuffer buff = new StringBuffer(s.length()); StringBuffer buff = new StringBuffer(s.length());
int start = 0; int start = 0;
char[] chars = null;
while (true) { while (true) {
int idx = s.indexOf(escapeCharacter, start); int idx = s.indexOf(escapeCharacter, start);
if (idx < 0) { if (idx < 0) {
break; break;
} }
buff.append(s.toCharArray(), start, idx); if (chars == null) {
start = idx + 1; chars = s.toCharArray();
}
buff.append(chars, start, idx - start);
if (idx == s.length() - 1) {
start = s.length();
break;
}
buff.append(chars[idx + 1]);
start = idx + 2;
} }
buff.append(s.substring(start)); buff.append(s.substring(start));
return buff.toString(); return buff.toString();
...@@ -496,7 +505,7 @@ public class Csv implements SimpleRowSource { ...@@ -496,7 +505,7 @@ public class Csv implements SimpleRowSource {
IOUtils.closeSilently(writer); IOUtils.closeSilently(writer);
writer = null; writer = null;
} }
/** /**
* INTERNAL * INTERNAL
*/ */
...@@ -506,7 +515,7 @@ public class Csv implements SimpleRowSource { ...@@ -506,7 +515,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Override the field separator for writing. The default is ",". * Override the field separator for writing. The default is ",".
* *
* @param fieldSeparatorWrite the field separator * @param fieldSeparatorWrite the field separator
*/ */
public void setFieldSeparatorWrite(String fieldSeparatorWrite) { public void setFieldSeparatorWrite(String fieldSeparatorWrite) {
...@@ -515,7 +524,7 @@ public class Csv implements SimpleRowSource { ...@@ -515,7 +524,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Get the current field separator for writing. * Get the current field separator for writing.
* *
* @return the field separator * @return the field separator
*/ */
public String getFieldSeparatorWrite() { public String getFieldSeparatorWrite() {
...@@ -524,7 +533,7 @@ public class Csv implements SimpleRowSource { ...@@ -524,7 +533,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Override the field separator for reading. The default is ','. * Override the field separator for reading. The default is ','.
* *
* @param fieldSeparatorRead the field separator * @param fieldSeparatorRead the field separator
*/ */
public void setFieldSeparatorRead(char fieldSeparatorRead) { public void setFieldSeparatorRead(char fieldSeparatorRead) {
...@@ -533,7 +542,7 @@ public class Csv implements SimpleRowSource { ...@@ -533,7 +542,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Get the current field separator for reading. * Get the current field separator for reading.
* *
* @return the field separator * @return the field separator
*/ */
public char getFieldSeparatorRead() { public char getFieldSeparatorRead() {
...@@ -542,7 +551,7 @@ public class Csv implements SimpleRowSource { ...@@ -542,7 +551,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Get the current row separator for writing. * Get the current row separator for writing.
* *
* @return the row separator * @return the row separator
*/ */
public String getRowSeparatorWrite() { public String getRowSeparatorWrite() {
...@@ -552,7 +561,7 @@ public class Csv implements SimpleRowSource { ...@@ -552,7 +561,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Override the end-of-row marker for writing. The default is null. * Override the end-of-row marker for writing. The default is null.
* After writing the end-of-row marker, a line feed is written (\n or \r\n depending on the system settings). * After writing the end-of-row marker, a line feed is written (\n or \r\n depending on the system settings).
* *
* @param rowSeparatorWrite the row separator * @param rowSeparatorWrite the row separator
*/ */
public void setRowSeparatorWrite(String rowSeparatorWrite) { public void setRowSeparatorWrite(String rowSeparatorWrite) {
...@@ -562,7 +571,7 @@ public class Csv implements SimpleRowSource { ...@@ -562,7 +571,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Set the field delimiter. The default is " (a double quote). * Set the field delimiter. The default is " (a double quote).
* 0 means no field delimiter is used. * 0 means no field delimiter is used.
* *
* @param fieldDelimiter the field delimiter * @param fieldDelimiter the field delimiter
*/ */
public void setFieldDelimiter(char fieldDelimiter) { public void setFieldDelimiter(char fieldDelimiter) {
...@@ -572,7 +581,7 @@ public class Csv implements SimpleRowSource { ...@@ -572,7 +581,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Get the current field delimiter. * Get the current field delimiter.
* 0 means no field delimiter is used. * 0 means no field delimiter is used.
* *
* @return the field delimiter * @return the field delimiter
*/ */
public char getFieldDelimiter() { public char getFieldDelimiter() {
...@@ -582,7 +591,7 @@ public class Csv implements SimpleRowSource { ...@@ -582,7 +591,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Set the escape character (used to escape the field delimiter). The default is " (a double quote). * Set the escape character (used to escape the field delimiter). The default is " (a double quote).
* 0 means no escape character is used. * 0 means no escape character is used.
* *
* @param escapeCharacter the escape character * @param escapeCharacter the escape character
*/ */
public void setEscapeCharacter(char escapeCharacter) { public void setEscapeCharacter(char escapeCharacter) {
...@@ -592,7 +601,7 @@ public class Csv implements SimpleRowSource { ...@@ -592,7 +601,7 @@ public class Csv implements SimpleRowSource {
/** /**
* Get the current escape character. * Get the current escape character.
* 0 means no escape character is used. * 0 means no escape character is used.
* *
* @return the escape character * @return the escape character
*/ */
public char getEscapeCharacter() { public char getEscapeCharacter() {
......
...@@ -534,4 +534,13 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -534,4 +534,13 @@ public class Server implements Runnable, ShutdownHandler {
stopAll(); stopAll();
} }
} }
/**
* Get the service attached to this server.
*
* @return the service
*/
public Service getService() {
return service;
}
} }
...@@ -12,6 +12,11 @@ import org.h2.tools.Script; ...@@ -12,6 +12,11 @@ import org.h2.tools.Script;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
import org.h2.tools.RunScript; import org.h2.tools.RunScript;
/**
* This sample application shows how to compact the database files.
* This is done by creating a SQL script, and then re-creating the database
* using this script.
*/
public class Compact { public class Compact {
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);
...@@ -21,13 +26,13 @@ public class Compact { ...@@ -21,13 +26,13 @@ public class Compact {
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)"); stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
stat.execute("INSERT INTO TEST VALUES(1, 'Hello'), (2, 'World');"); stat.execute("INSERT INTO TEST VALUES(1, 'Hello'), (2, 'World');");
conn.close(); conn.close();
System.out.println("Compacting..."); System.out.println("Compacting...");
compact("data", "test", "sa", ""); compact("data", "test", "sa", "");
System.out.println("Done."); System.out.println("Done.");
} }
public static void compact(String dir, String dbName, String user, String password) throws Exception { public static void compact(String dir, String dbName, String user, String password) throws Exception {
String url = "jdbc:h2:" + dir + "/" + dbName; String url = "jdbc:h2:" + dir + "/" + dbName;
String file = "data/test.sql"; String file = "data/test.sql";
......
...@@ -11,6 +11,11 @@ import java.sql.Types; ...@@ -11,6 +11,11 @@ import java.sql.Types;
import org.h2.tools.Csv; import org.h2.tools.Csv;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
/**
* This sample application shows how to use the CSV tool
* to write CSV (comma separated values) files, and
* how to use the tool to read such files.
*/
public class CsvSample { public class CsvSample {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
CsvSample.write(); CsvSample.write();
......
...@@ -11,6 +11,10 @@ import java.sql.DriverManager; ...@@ -11,6 +11,10 @@ import java.sql.DriverManager;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.Statement; import java.sql.Statement;
/**
* This sample application shows how to create a user defined function
* to read a file from the file system.
*/
public class FileFunctions { public class FileFunctions {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
......
...@@ -14,8 +14,12 @@ import java.sql.Types; ...@@ -14,8 +14,12 @@ import java.sql.Types;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
/**
* This sample application shows how to define and use
* custom (user defined) functions in this database.
*/
public class Function { public class Function {
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", "");
...@@ -36,18 +40,18 @@ public class Function { ...@@ -36,18 +40,18 @@ public class Function {
public static boolean isPrime(int value) { public static boolean isPrime(int value) {
return new BigInteger(String.valueOf(value)).isProbablePrime(100); return new BigInteger(String.valueOf(value)).isProbablePrime(100);
} }
public static ResultSet query(Connection conn, String sql) throws SQLException { public static ResultSet query(Connection conn, String sql) throws SQLException {
return conn.createStatement().executeQuery(sql); return conn.createStatement().executeQuery(sql);
} }
public static ResultSet simpleResultSet() throws SQLException { public static ResultSet simpleResultSet() throws SQLException {
SimpleResultSet rs = new SimpleResultSet(); SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("ID", Types.INTEGER, 10, 0); rs.addColumn("ID", Types.INTEGER, 10, 0);
rs.addColumn("NAME", Types.VARCHAR, 255, 0); rs.addColumn("NAME", Types.VARCHAR, 255, 0);
rs.addRow(new Object[] { new Integer(0), "Hello" }); rs.addRow(new Object[] { new Integer(0), "Hello" });
return rs; return rs;
} }
public static ResultSet getMatrix(Connection conn, Integer id) throws SQLException { public static ResultSet getMatrix(Connection conn, Integer id) throws SQLException {
SimpleResultSet rs = new SimpleResultSet(); SimpleResultSet rs = new SimpleResultSet();
......
...@@ -14,8 +14,14 @@ import java.sql.Types; ...@@ -14,8 +14,14 @@ import java.sql.Types;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
/**
* User defined functions can return a result set,
* and can therefore be used like a table.
* This sample application uses such a function to convert
* polar to cartesian coordinates.
*/
public class FunctionMultiReturn { public class FunctionMultiReturn {
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", "");
...@@ -30,7 +36,7 @@ public class FunctionMultiReturn { ...@@ -30,7 +36,7 @@ public class FunctionMultiReturn {
double y = rs.getDouble(2); double y = rs.getDouble(2);
System.out.println("result: (x=" + x + ", y="+y+")"); System.out.println("result: (x=" + x + ", y="+y+")");
} }
stat.execute("CREATE TABLE TEST(ID IDENTITY, R DOUBLE, A DOUBLE)"); stat.execute("CREATE TABLE TEST(ID IDENTITY, R DOUBLE, A DOUBLE)");
stat.execute("INSERT INTO TEST(R, A) VALUES(5.0, 0.5), (10.0, 0.6)"); stat.execute("INSERT INTO TEST(R, A) VALUES(5.0, 0.5), (10.0, 0.6)");
stat.execute("CREATE ALIAS P2C_SET FOR \"org.h2.samples.FunctionMultiReturn.polar2CartesianSet\" "); stat.execute("CREATE ALIAS P2C_SET FOR \"org.h2.samples.FunctionMultiReturn.polar2CartesianSet\" ");
...@@ -41,8 +47,8 @@ public class FunctionMultiReturn { ...@@ -41,8 +47,8 @@ public class FunctionMultiReturn {
double x = rs.getDouble("X"); double x = rs.getDouble("X");
double y = rs.getDouble("Y"); double y = rs.getDouble("Y");
System.out.println("(r="+r+" a="+a+") : (x=" + x + ", y="+y+")"); System.out.println("(r="+r+" a="+a+") : (x=" + x + ", y="+y+")");
} }
stat.execute("CREATE ALIAS P2C_A FOR \"org.h2.samples.FunctionMultiReturn.polar2CartesianArray\" "); stat.execute("CREATE ALIAS P2C_A FOR \"org.h2.samples.FunctionMultiReturn.polar2CartesianArray\" ");
rs = conn.createStatement().executeQuery("SELECT R, A, P2C_A(R, A) FROM TEST"); rs = conn.createStatement().executeQuery("SELECT R, A, P2C_A(R, A) FROM TEST");
while (rs.next()) { while (rs.next()) {
...@@ -54,7 +60,7 @@ public class FunctionMultiReturn { ...@@ -54,7 +60,7 @@ public class FunctionMultiReturn {
double y = ((Double) xy[1]).doubleValue(); double y = ((Double) xy[1]).doubleValue();
System.out.println("(r=" + r + " a=" + a + ") : (x=" + x + ", y=" + y + ")"); System.out.println("(r=" + r + " a=" + a + ") : (x=" + x + ", y=" + y + ")");
} }
rs = conn.createStatement().executeQuery("SELECT R, A, ARRAY_GET(E, 1), ARRAY_GET(E, 2) FROM (SELECT R, A, P2C_A(R, A) E FROM TEST)"); rs = conn.createStatement().executeQuery("SELECT R, A, ARRAY_GET(E, 1), ARRAY_GET(E, 2) FROM (SELECT R, A, P2C_A(R, A) E FROM TEST)");
while (rs.next()) { while (rs.next()) {
double r = rs.getDouble(1); double r = rs.getDouble(1);
...@@ -62,13 +68,13 @@ public class FunctionMultiReturn { ...@@ -62,13 +68,13 @@ public class FunctionMultiReturn {
double x = rs.getDouble(3); double x = rs.getDouble(3);
double y = rs.getDouble(4); double y = rs.getDouble(4);
System.out.println("(r="+r+" a="+a+") : (x=" + x + ", y="+y+")"); System.out.println("(r="+r+" a="+a+") : (x=" + x + ", y="+y+")");
} }
conn.close(); conn.close();
} }
/** /**
* The function may be called twice, once to retrieve the result columns (with null parameters), * The function may be called twice, once to retrieve the result columns (with null parameters),
* and the second time to return the data. * and the second time to return the data.
*/ */
public static ResultSet polar2Cartesian(Double r, Double alpha) throws SQLException { public static ResultSet polar2Cartesian(Double r, Double alpha) throws SQLException {
...@@ -81,17 +87,17 @@ public class FunctionMultiReturn { ...@@ -81,17 +87,17 @@ public class FunctionMultiReturn {
rs.addRow(new Object[] { new Double(x), new Double(y) }); rs.addRow(new Object[] { new Double(x), new Double(y) });
} }
return rs; return rs;
} }
/** /**
* The function may be called twice, once to retrieve the result columns (with null parameters), * The function may be called twice, once to retrieve the result columns (with null parameters),
* and the second time to return the data. * and the second time to return the data.
*/ */
public static Object[] polar2CartesianArray(Double r, Double alpha) throws SQLException { public static Object[] polar2CartesianArray(Double r, Double alpha) throws SQLException {
double x = r.doubleValue() * Math.cos(alpha.doubleValue()); double x = r.doubleValue() * Math.cos(alpha.doubleValue());
double y = r.doubleValue() * Math.sin(alpha.doubleValue()); double y = r.doubleValue() * Math.sin(alpha.doubleValue());
return new Object[]{new Double(x), new Double(y)}; return new Object[]{new Double(x), new Double(y)};
} }
public static ResultSet polar2CartesianSet(Connection conn, String query) throws SQLException { public static ResultSet polar2CartesianSet(Connection conn, String query) throws SQLException {
SimpleResultSet result = new SimpleResultSet(); SimpleResultSet result = new SimpleResultSet();
...@@ -110,6 +116,6 @@ public class FunctionMultiReturn { ...@@ -110,6 +116,6 @@ public class FunctionMultiReturn {
} }
} }
return result; return result;
} }
} }
...@@ -13,13 +13,16 @@ import java.sql.Statement; ...@@ -13,13 +13,16 @@ import java.sql.Statement;
import org.h2.tools.RunScript; import org.h2.tools.RunScript;
/**
* In this example a database is initialized from compressed script in a jar file.
*/
public class InitDatabaseFromJar { public class InitDatabaseFromJar {
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();
} }
private void createScript() throws Exception { private void createScript() throws Exception {
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:mem:test"); Connection conn = DriverManager.getConnection("jdbc:h2:mem:test");
......
...@@ -10,26 +10,31 @@ import java.sql.SQLException; ...@@ -10,26 +10,31 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import org.h2.tools.Server; import org.h2.tools.Server;
/**
* This sample program opens the same database once in embedded mode,
* and once in the server mode. The embedded mode is faster, but only
* the server mode supports remote connections.
*/
public class MixedMode { public class MixedMode {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
// start the server, allows to access the database remotly // start the server, allows to access the database remotly
Server server = Server.createTcpServer(new String[]{"-tcpPort", "9081"}); Server server = Server.createTcpServer(new String[] { "-tcpPort", "9081" });
server.start(); server.start();
System.out.println("You can access the database remotely now, using the URL:"); System.out.println("You can access the database remotely now, using the URL:");
System.out.println("jdbc:h2:tcp://localhost:9081/~/test (user: sa, password: sa)"); System.out.println("jdbc:h2:tcp://localhost:9081/~/test (user: sa, password: sa)");
// now use the database in your application in embedded mode // now use the database in your application in embedded mode
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:~/test", "sa", "sa"); Connection conn = DriverManager.getConnection("jdbc:h2:~/test", "sa", "sa");
// some simple 'business usage' // some simple 'business usage'
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("DROP TABLE TIMER IF EXISTS"); stat.execute("DROP TABLE TIMER IF EXISTS");
stat.execute("CREATE TABLE TIMER(ID INT PRIMARY KEY, TIME VARCHAR)"); stat.execute("CREATE TABLE TIMER(ID INT PRIMARY KEY, TIME VARCHAR)");
System.out.println("Execute this a few times: SELECT TIME FROM TIMER"); System.out.println("Execute this a few times: SELECT TIME FROM TIMER");
System.out.println("To stop this application (and the server), run: DROP TABLE TIMER"); System.out.println("To stop this application (and the server), run: DROP TABLE TIMER");
try { try {
while (true) { while (true) {
// runs forever, except if you drop the table remotely // runs forever, except if you drop the table remotely
stat.execute("MERGE INTO TIMER VALUES(1, NOW())"); stat.execute("MERGE INTO TIMER VALUES(1, NOW())");
...@@ -38,9 +43,9 @@ public class MixedMode { ...@@ -38,9 +43,9 @@ public class MixedMode {
} catch (SQLException e) { } catch (SQLException e) {
System.out.println("Error: " + e.toString()); System.out.println("Error: " + e.toString());
} }
conn.close(); conn.close();
// stop the server // stop the server
server.stop(); server.stop();
} }
} }
...@@ -16,6 +16,10 @@ import java.sql.ResultSet; ...@@ -16,6 +16,10 @@ import java.sql.ResultSet;
import org.h2.tools.RunScript; import org.h2.tools.RunScript;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/**
* The newsfeed application uses XML functions to create an RSS and Atom feed
* from a simple SQL script. A textual representation of the data is created as well.
*/
public class Newsfeed { public class Newsfeed {
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");
......
...@@ -13,25 +13,31 @@ import java.sql.ResultSet; ...@@ -13,25 +13,31 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
/**
* SQL Injection is a common security vulnerability for applications that use database.
* It is one of the most common security vulnerabilities for web applications today.
* This sample application shows how SQL injection works, and how to protect
* the application from it.
*/
public class SQLInjection { public class SQLInjection {
Connection conn; private Connection conn;
Statement stat; private Statement stat;
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");
// new SQLInjection().run("org.postgresql.Driver", // new SQLInjection().run("org.postgresql.Driver",
// "jdbc:postgresql:jpox2", "sa", "sa"); // "jdbc:postgresql:jpox2", "sa", "sa");
// new SQLInjection().run("com.mysql.jdbc.Driver", // new SQLInjection().run("com.mysql.jdbc.Driver",
// "jdbc:mysql://localhost/test", "sa", "sa"); // "jdbc:mysql://localhost/test", "sa", "sa");
// new SQLInjection().run("org.hsqldb.jdbcDriver", // new SQLInjection().run("org.hsqldb.jdbcDriver",
// "jdbc:hsqldb:test", "sa", ""); // "jdbc:hsqldb:test", "sa", "");
// new SQLInjection().run( // new SQLInjection().run(
// "org.apache.derby.jdbc.EmbeddedDriver", // "org.apache.derby.jdbc.EmbeddedDriver",
// "jdbc:derby:test3;create=true", "sa", "sa"); // "jdbc:derby:test3;create=true", "sa", "sa");
} }
void run(String driver, String url, String user, String password) throws Exception { void run(String driver, String url, String user, String password) throws Exception {
Class.forName(driver); Class.forName(driver);
conn = DriverManager.getConnection(url, user, password); conn = DriverManager.getConnection(url, user, password);
...@@ -41,37 +47,37 @@ public class SQLInjection { ...@@ -41,37 +47,37 @@ public class SQLInjection {
} catch (SQLException e) { } catch (SQLException e) {
// ignore // ignore
} }
stat.execute("CREATE TABLE USERS(ID INT PRIMARY KEY, " + stat.execute("CREATE TABLE USERS(ID INT PRIMARY KEY, " +
"NAME VARCHAR(255), PASSWORD VARCHAR(255))"); "NAME VARCHAR(255), PASSWORD VARCHAR(255))");
stat.execute("INSERT INTO USERS VALUES(1, 'admin', 'super')"); stat.execute("INSERT INTO USERS VALUES(1, 'admin', 'super')");
stat.execute("INSERT INTO USERS VALUES(2, 'guest', '123456')"); stat.execute("INSERT INTO USERS VALUES(2, 'guest', '123456')");
stat.execute("INSERT INTO USERS VALUES(3, 'test', 'abc')"); stat.execute("INSERT INTO USERS VALUES(3, 'test', 'abc')");
// loginByNameInsecure(); loginByNameInsecure();
if (url.startsWith("jdbc:h2:")) { if (url.startsWith("jdbc:h2:")) {
// loginStoredProcedureInsecure(); loginStoredProcedureInsecure();
limitRowAccess(); limitRowAccess();
} }
// loginByNameSecure(); loginByNameSecure();
if (url.startsWith("jdbc:h2:")) { if (url.startsWith("jdbc:h2:")) {
stat.execute("SET ALLOW_LITERALS NONE"); stat.execute("SET ALLOW_LITERALS NONE");
stat.execute("SET ALLOW_LITERALS NUMBERS"); stat.execute("SET ALLOW_LITERALS NUMBERS");
stat.execute("SET ALLOW_LITERALS ALL"); stat.execute("SET ALLOW_LITERALS ALL");
} }
// loginByIdInsecure(); loginByIdInsecure();
// loginByIdSecure(); loginByIdSecure();
try { try {
stat.execute("DROP TABLE ITEMS"); stat.execute("DROP TABLE ITEMS");
} catch (SQLException e) { } catch (SQLException e) {
// ignore // ignore
} }
stat.execute("CREATE TABLE ITEMS(ID INT PRIMARY KEY, " + stat.execute("CREATE TABLE ITEMS(ID INT PRIMARY KEY, " +
"NAME VARCHAR(255), ACTIVE INT)"); "NAME VARCHAR(255), ACTIVE INT)");
stat.execute("INSERT INTO ITEMS VALUES(0, 'XBox', 0)"); stat.execute("INSERT INTO ITEMS VALUES(0, 'XBox', 0)");
stat.execute("INSERT INTO ITEMS VALUES(1, 'XBox 360', 1)"); stat.execute("INSERT INTO ITEMS VALUES(1, 'XBox 360', 1)");
...@@ -88,23 +94,23 @@ public class SQLInjection { ...@@ -88,23 +94,23 @@ public class SQLInjection {
stat.execute("CREATE CONSTANT TYPE_ACTIVE VALUE 1"); stat.execute("CREATE CONSTANT TYPE_ACTIVE VALUE 1");
listActiveItemsUsingConstants(); listActiveItemsUsingConstants();
} }
// listItemsSortedInsecure(); listItemsSortedInsecure();
// listItemsSortedSecure(); listItemsSortedSecure();
if (url.startsWith("jdbc:h2:")) { if (url.startsWith("jdbc:h2:")) {
listItemsSortedSecureParam(); listItemsSortedSecureParam();
// storePasswordHashWithSalt(); storePasswordHashWithSalt();
} }
conn.close(); conn.close();
} }
void loginByNameInsecure() throws Exception { void loginByNameInsecure() throws Exception {
System.out.println("Insecure Systems Inc. - login"); System.out.println("Insecure Systems Inc. - login");
String name = input("Name?"); String name = input("Name?");
String password = input("Password?"); String password = input("Password?");
ResultSet rs = stat.executeQuery("SELECT * FROM USERS WHERE " + ResultSet rs = stat.executeQuery("SELECT * FROM USERS WHERE " +
"NAME='" + name + "' AND PASSWORD='" + password + "'"); "NAME='" + name + "' AND PASSWORD='" + password + "'");
if (rs.next()) { if (rs.next()) {
System.out.println("Welcome!"); System.out.println("Welcome!");
...@@ -112,7 +118,7 @@ public class SQLInjection { ...@@ -112,7 +118,7 @@ public class SQLInjection {
System.out.println("Access denied!"); System.out.println("Access denied!");
} }
} }
public static ResultSet getUser(Connection conn, String userName, String password) throws Exception { public static ResultSet getUser(Connection conn, String userName, String password) throws Exception {
PreparedStatement prep = conn.prepareStatement( PreparedStatement prep = conn.prepareStatement(
"SELECT * FROM USERS WHERE NAME=? AND PASSWORD=?"); "SELECT * FROM USERS WHERE NAME=? AND PASSWORD=?");
...@@ -132,9 +138,9 @@ public class SQLInjection { ...@@ -132,9 +138,9 @@ public class SQLInjection {
void loginStoredProcedureInsecure() throws Exception { void loginStoredProcedureInsecure() throws Exception {
System.out.println("Insecure Systems Inc. - login using a stored procedure"); System.out.println("Insecure Systems Inc. - login using a stored procedure");
stat.execute("CREATE ALIAS IF NOT EXISTS " + stat.execute("CREATE ALIAS IF NOT EXISTS " +
"GET_USER FOR \"org.h2.samples.SQLInjection.getUser\""); "GET_USER FOR \"org.h2.samples.SQLInjection.getUser\"");
stat.execute("CREATE ALIAS IF NOT EXISTS " + stat.execute("CREATE ALIAS IF NOT EXISTS " +
"CHANGE_PASSWORD FOR \"org.h2.samples.SQLInjection.changePassword\""); "CHANGE_PASSWORD FOR \"org.h2.samples.SQLInjection.changePassword\"");
String name = input("Name?"); String name = input("Name?");
String password = input("Password?"); String password = input("Password?");
...@@ -146,13 +152,13 @@ public class SQLInjection { ...@@ -146,13 +152,13 @@ public class SQLInjection {
System.out.println("Access denied!"); System.out.println("Access denied!");
} }
} }
void loginByNameSecure() throws Exception { void loginByNameSecure() throws Exception {
System.out.println("Secure Systems Inc. - login using placeholders"); System.out.println("Secure Systems Inc. - login using placeholders");
String name = input("Name?"); String name = input("Name?");
String password = input("Password?"); String password = input("Password?");
PreparedStatement prep = conn.prepareStatement( PreparedStatement prep = conn.prepareStatement(
"SELECT * FROM USERS WHERE " + "SELECT * FROM USERS WHERE " +
"NAME=? AND PASSWORD=?"); "NAME=? AND PASSWORD=?");
prep.setString(1, name); prep.setString(1, name);
prep.setString(2, password); prep.setString(2, password);
...@@ -169,8 +175,8 @@ public class SQLInjection { ...@@ -169,8 +175,8 @@ public class SQLInjection {
stat.execute("DROP TABLE IF EXISTS SESSION_USER"); stat.execute("DROP TABLE IF EXISTS SESSION_USER");
stat.execute("CREATE TABLE SESSION_USER(ID INT, USER INT)"); stat.execute("CREATE TABLE SESSION_USER(ID INT, USER INT)");
stat.execute("DROP VIEW IF EXISTS MY_USER"); stat.execute("DROP VIEW IF EXISTS MY_USER");
stat.execute("CREATE VIEW MY_USER AS " + stat.execute("CREATE VIEW MY_USER AS " +
"SELECT U.* FROM SESSION_USER S, USERS U " + "SELECT U.* FROM SESSION_USER S, USERS U " +
"WHERE S.ID=SESSION_ID() AND S.USER=U.ID"); "WHERE S.ID=SESSION_ID() AND S.USER=U.ID");
stat.execute("INSERT INTO SESSION_USER VALUES(SESSION_ID(), 1)"); stat.execute("INSERT INTO SESSION_USER VALUES(SESSION_ID(), 1)");
ResultSet rs = stat.executeQuery("SELECT ID, NAME FROM MY_USER"); ResultSet rs = stat.executeQuery("SELECT ID, NAME FROM MY_USER");
...@@ -185,7 +191,7 @@ public class SQLInjection { ...@@ -185,7 +191,7 @@ public class SQLInjection {
String password = input("Password?"); String password = input("Password?");
try { try {
PreparedStatement prep = conn.prepareStatement( PreparedStatement prep = conn.prepareStatement(
"SELECT * FROM USERS WHERE " + "SELECT * FROM USERS WHERE " +
"ID=" + id + " AND PASSWORD=?"); "ID=" + id + " AND PASSWORD=?");
prep.setString(1, password); prep.setString(1, password);
ResultSet rs = prep.executeQuery(); ResultSet rs = prep.executeQuery();
...@@ -205,7 +211,7 @@ public class SQLInjection { ...@@ -205,7 +211,7 @@ public class SQLInjection {
String password = input("Password?"); String password = input("Password?");
try { try {
PreparedStatement prep = conn.prepareStatement( PreparedStatement prep = conn.prepareStatement(
"SELECT * FROM USERS WHERE " + "SELECT * FROM USERS WHERE " +
"ID=? AND PASSWORD=?"); "ID=? AND PASSWORD=?");
prep.setInt(1, Integer.parseInt(id)); prep.setInt(1, Integer.parseInt(id));
prep.setString(2, password); prep.setString(2, password);
...@@ -219,7 +225,7 @@ public class SQLInjection { ...@@ -219,7 +225,7 @@ public class SQLInjection {
System.out.println(e); System.out.println(e);
} }
} }
void listActiveItems() throws Exception { void listActiveItems() throws Exception {
System.out.println("Half Secure Systems Inc. - list active items"); System.out.println("Half Secure Systems Inc. - list active items");
ResultSet rs = stat.executeQuery( ResultSet rs = stat.executeQuery(
...@@ -251,7 +257,7 @@ public class SQLInjection { ...@@ -251,7 +257,7 @@ public class SQLInjection {
System.out.println(e); System.out.println(e);
} }
} }
void listItemsSortedSecure() throws Exception { void listItemsSortedSecure() throws Exception {
System.out.println("Secure Systems Inc. - list items"); System.out.println("Secure Systems Inc. - list items");
String order = input("order (id, name)?"); String order = input("order (id, name)?");
...@@ -288,9 +294,9 @@ public class SQLInjection { ...@@ -288,9 +294,9 @@ public class SQLInjection {
void storePasswordHashWithSalt() throws Exception { void storePasswordHashWithSalt() throws Exception {
System.out.println("Very Secure Systems Inc. - login"); System.out.println("Very Secure Systems Inc. - login");
stat.execute("DROP TABLE IF EXISTS USERS2"); stat.execute("DROP TABLE IF EXISTS USERS2");
stat.execute("CREATE TABLE USERS2(ID INT PRIMARY KEY, " + stat.execute("CREATE TABLE USERS2(ID INT PRIMARY KEY, " +
"NAME VARCHAR, SALT BINARY, HASH BINARY)"); "NAME VARCHAR, SALT BINARY, HASH BINARY)");
stat.execute("INSERT INTO USERS2 VALUES" + stat.execute("INSERT INTO USERS2 VALUES" +
"(1, 'admin', SECURE_RAND(16), NULL)"); "(1, 'admin', SECURE_RAND(16), NULL)");
stat.execute("DROP CONSTANT IF EXISTS HASH_ITERATIONS"); stat.execute("DROP CONSTANT IF EXISTS HASH_ITERATIONS");
stat.execute("DROP CONSTANT IF EXISTS HASH_ALGORITHM"); stat.execute("DROP CONSTANT IF EXISTS HASH_ALGORITHM");
...@@ -303,7 +309,7 @@ public class SQLInjection { ...@@ -303,7 +309,7 @@ public class SQLInjection {
String password = input("password?"); String password = input("password?");
stat.execute("SET ALLOW_LITERALS NONE"); stat.execute("SET ALLOW_LITERALS NONE");
PreparedStatement prep = conn.prepareStatement( PreparedStatement prep = conn.prepareStatement(
"SELECT * FROM USERS2 WHERE NAME=? AND " + "SELECT * FROM USERS2 WHERE NAME=? AND " +
"HASH=HASH(HASH_ALGORITHM, STRINGTOUTF8(? || SALT), HASH_ITERATIONS)"); "HASH=HASH(HASH_ALGORITHM, STRINGTOUTF8(? || SALT), HASH_ITERATIONS)");
prep.setString(1, user); prep.setString(1, user);
prep.setString(2, password); prep.setString(2, password);
...@@ -321,5 +327,5 @@ public class SQLInjection { ...@@ -321,5 +327,5 @@ public class SQLInjection {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
return reader.readLine(); return reader.readLine();
} }
} }
...@@ -13,10 +13,15 @@ import java.sql.Statement; ...@@ -13,10 +13,15 @@ import java.sql.Statement;
import org.h2.api.DatabaseEventListener; import org.h2.api.DatabaseEventListener;
import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcConnection;
/**
* This example application implements a database event listener.
* This is useful to display progress information while opening a large database,
* or to log database exceptions.
*/
public class ShowProgress implements DatabaseEventListener { public class ShowProgress implements DatabaseEventListener {
private long last, start; private long last, start;
public ShowProgress() { public ShowProgress() {
start = last = System.currentTimeMillis(); start = last = System.currentTimeMillis();
} }
...@@ -24,7 +29,7 @@ public class ShowProgress implements DatabaseEventListener { ...@@ -24,7 +29,7 @@ public class ShowProgress implements DatabaseEventListener {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
new ShowProgress().test(); new ShowProgress().test();
} }
void test() throws Exception { void test() throws Exception {
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:test;LOG=2", "sa", ""); Connection conn = DriverManager.getConnection("jdbc:h2:test;LOG=2", "sa", "");
...@@ -54,14 +59,14 @@ public class ShowProgress implements DatabaseEventListener { ...@@ -54,14 +59,14 @@ public class ShowProgress implements DatabaseEventListener {
} else { } else {
conn.close(); conn.close();
} }
System.out.println("Open connection..."); System.out.println("Open connection...");
time = System.currentTimeMillis(); time = System.currentTimeMillis();
conn = DriverManager.getConnection("jdbc:h2:test;LOG=2;DATABASE_EVENT_LISTENER='" + getClass().getName() + "'", "sa", ""); conn = DriverManager.getConnection("jdbc:h2:test;LOG=2;DATABASE_EVENT_LISTENER='" + getClass().getName() + "'", "sa", "");
time = System.currentTimeMillis() - time; time = System.currentTimeMillis() - time;
System.out.println("Done after " + time + " ms"); System.out.println("Done after " + time + " ms");
conn.close(); conn.close();
} }
public void diskSpaceIsLow(long stillAvailable) throws SQLException { public void diskSpaceIsLow(long stillAvailable) throws SQLException {
...@@ -106,7 +111,7 @@ public class ShowProgress implements DatabaseEventListener { ...@@ -106,7 +111,7 @@ public class ShowProgress implements DatabaseEventListener {
public void init(String url) { public void init(String url) {
System.out.println("Initializing the event listener for database " + url); System.out.println("Initializing the event listener for database " + url);
} }
public void opened() { public void opened() {
} }
......
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
*/ */
package org.h2.samples; package org.h2.samples;
/**
* This very simple sample application stops a H2 TCP server
* if it is running.
*/
public class ShutdownServer { public class ShutdownServer {
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);
......
...@@ -14,6 +14,9 @@ import java.sql.Statement; ...@@ -14,6 +14,9 @@ import java.sql.Statement;
import org.h2.api.Trigger; import org.h2.api.Trigger;
/**
* This sample application shows how to use database triggers.
*/
public class TriggerSample { public class TriggerSample {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
......
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>H2 Database Engine</title>
<link>http://www.h2database.com</link>
<description>H2 Database Engine</description>
<language>en-us</language>
<pubDate>Thu, 15 Jun 2006 05:59:06 GMT</pubDate>
<lastBuildDate>Thu, 15 Jun 2006 05:59:06 GMT</lastBuildDate>
<item>
<title>New version available: 0.9 Alpha / 2006-06-02</title>
<link>http://www.h2database.com</link>
<description>
<![CDATA[A new version of H2 is available for download at http://www.h2database.com
Changes and new functionality:
- The GCJ version for Windows is no longer included in the download.
It was not stable in this release.
- ORDER BY now uses an index if possible.
Queries with LIMIT with ORDER BY
are faster when the index can be used.
- Statements containing LIKE are now re-compiled when executed.
Depending on the data, an index on the column is used or not.
- New option to disable automatic closing of a database
when the virtual machine exits.
Database URL: jdbc:h2:test;db_close_on_exit=false
- New event: DatabaseEventListener.closingDatabase()
- Connection.getCatalog() now returns the database name
- The function DATABASE() now return the short name
- Automatic starting of a web browser for Mac OS X should work now.
Security:
- TCP Server: Can now specify a password (tcpPassword).
- New option -ifExists for the TCP and ODBC server
to disallow creating new databases.
Bugfixes:
- Issue #103: Shutdown of a TCP Server from command line
didn't always work.
- Issue #104: A HAVING condition on a column
that was not in the GROUP BY list didn't work.
- Issue #105: RUNSCRIPT (the command) didn't commit
after each command if autocommit was on.
- Issue #106: SET commands where not persisted
- Issue# 107: When executing scripts that contained inserts
with many columns, an OutOfMemory error could occur.
- Issue #108: There is a concurrency problem when multi threads
access the same database.
- Issue #109: ALTER TABLE ADD COLUMN can make
the database unusable.
For details see also the history. The plans for the next release are:
- Bugfixes, write more tests, more bugfixes, more tests
- Define which modules are alpha, beta and production quality
- Proposal for changed license (still pending...)
- For other plans, see the new 'Roadmap' part on the web site
]]>
</description>
</item>
</channel>
</rss>
...@@ -63,7 +63,8 @@ import org.h2.test.jdbc.TestTransactionIsolation; ...@@ -63,7 +63,8 @@ import org.h2.test.jdbc.TestTransactionIsolation;
import org.h2.test.jdbc.TestUpdatableResultSet; import org.h2.test.jdbc.TestUpdatableResultSet;
import org.h2.test.jdbc.TestZloty; import org.h2.test.jdbc.TestZloty;
import org.h2.test.jdbc.xa.TestXA; import org.h2.test.jdbc.xa.TestXA;
import org.h2.test.mvcc.TestMVCC; import org.h2.test.mvcc.TestMvcc1;
import org.h2.test.mvcc.TestMvcc2;
import org.h2.test.server.TestNestedLoop; import org.h2.test.server.TestNestedLoop;
import org.h2.test.server.TestPgServer; import org.h2.test.server.TestPgServer;
import org.h2.test.server.TestWeb; import org.h2.test.server.TestWeb;
...@@ -153,7 +154,19 @@ java org.h2.test.TestAll timer ...@@ -153,7 +154,19 @@ java org.h2.test.TestAll timer
/* /*
C:\temp\test\db documentation: package.html
write to the db file what version was used to create a database
History:
CSV tool: the character # could not be used as a separator when reading.
CSV tool: some escape/separator character combinations did not work. Fixed.
Roadmap:
remove
* Stop the server: close all open databases first
Web site: Web site:
link to history page, bug page link to history page, bug page
...@@ -188,12 +201,8 @@ CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR); ...@@ -188,12 +201,8 @@ CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR);
<reconnect> <reconnect>
out of memory? out of memory?
shrink newsletter list (migrate to google groups)
don't create @~ of not translated don't create @~ of not translated
clustered tables: test, document
extend tests that simulate power off extend tests that simulate power off
HSQLDB compatibility: HSQLDB compatibility:
...@@ -214,20 +223,16 @@ move Performance Tuning > Advanced Topics ...@@ -214,20 +223,16 @@ move Performance Tuning > Advanced Topics
testHalt testHalt
java org.h2.test.TestAll halt java org.h2.test.TestAll halt
Automate real power off tests
timer test timer test
java.lang.Exception: query was too quick; result: 0 time:968
at org.h2.test.TestBase.logError(TestBase.java:220)
at org.h2.test.db.TestCases$1.run(TestCases.java:170)
at java.lang.Thread.run(Thread.java:595)
ftp server: problem with multithreading? ftp server: problem with multithreading?
h2\src\docsrc\html\images\SQLInjection.txt h2\src\docsrc\html\images\SQLInjection.txt
Convert SQL-injection-2.txt to html document, include SQLInjection.java sample
send http://thecodist.com/fiche/thecodist/article/sql-injections-how-not-to-get-stuck to JavaWorld, TheServerSide, send http://thecodist.com/fiche/thecodist/article/sql-injections-how-not-to-get-stuck to JavaWorld, TheServerSide,
Send SQL Injection solution proposal to PostgreSQL, MySQL, Derby, HSQLDB,... Send SQL Injection solution proposal to PostgreSQL, MySQL, Derby, HSQLDB,...
Convert SQL-injection-2.txt to html document, include SQLInjection.java sample
MySQL, PostgreSQL MySQL, PostgreSQL
READ_TEXT(fileName String) returning a CLOB. READ_TEXT(fileName String) returning a CLOB.
...@@ -235,8 +240,6 @@ I am not sure if this will read the CLOB in memory however. ...@@ -235,8 +240,6 @@ I am not sure if this will read the CLOB in memory however.
Improve LOB in directories performance Improve LOB in directories performance
Automate real power off tests
http://fastutil.dsi.unimi.it/ http://fastutil.dsi.unimi.it/
http://javolution.org/ http://javolution.org/
http://joda-time.sourceforge.net/ http://joda-time.sourceforge.net/
...@@ -251,8 +254,6 @@ glossary ...@@ -251,8 +254,6 @@ glossary
spell check / word list per language spell check / word list per language
translated .pdf translated .pdf
write tests using the PostgreSQL JDBC driver
*/ */
// run TestHalt // run TestHalt
...@@ -280,6 +281,9 @@ write tests using the PostgreSQL JDBC driver ...@@ -280,6 +281,9 @@ write tests using the PostgreSQL JDBC driver
Features of H2 Features of H2
- Case insensitive string data type - Case insensitive string data type
- GROUP_CONCAT aggregate, User defined aggregates - GROUP_CONCAT aggregate, User defined aggregates
- Fulltext search
- MVCC
- User defined types
*/ */
if (args.length > 0) { if (args.length > 0) {
...@@ -499,6 +503,8 @@ Features of H2 ...@@ -499,6 +503,8 @@ Features of H2
cache2Q = false; cache2Q = false;
testAll(); testAll();
memory = true;
testAll();
} }
void testAll() throws Exception { void testAll() throws Exception {
...@@ -539,9 +545,6 @@ Features of H2 ...@@ -539,9 +545,6 @@ Features of H2
System.out.println("test big:"+big+" net:"+networked+" cipher:"+cipher+" memory:"+memory+" log:"+logMode+" diskResult:"+diskResult + " mvcc:" + mvcc); System.out.println("test big:"+big+" net:"+networked+" cipher:"+cipher+" memory:"+memory+" log:"+logMode+" diskResult:"+diskResult + " mvcc:" + mvcc);
beforeTest(); beforeTest();
// int testMvcc;
// mvcc = true;
// db // db
new TestScriptSimple().runTest(this); new TestScriptSimple().runTest(this);
new TestScript().runTest(this); new TestScript().runTest(this);
...@@ -607,7 +610,8 @@ Features of H2 ...@@ -607,7 +610,8 @@ Features of H2
new TestZloty().runTest(this); new TestZloty().runTest(this);
// mvcc // mvcc
new TestMVCC().runTest(this); new TestMvcc1().runTest(this);
new TestMvcc2().runTest(this);
// synthetic // synthetic
new TestCrashAPI().runTest(this); new TestCrashAPI().runTest(this);
......
...@@ -52,9 +52,6 @@ public abstract class TestBase { ...@@ -52,9 +52,6 @@ public abstract class TestBase {
} }
public void runTest(TestAll conf) { public void runTest(TestAll conf) {
if (conf.networked) {
return;
}
try { try {
init(conf); init(conf);
start = System.currentTimeMillis(); start = System.currentTimeMillis();
......
...@@ -8,9 +8,12 @@ import java.io.File; ...@@ -8,9 +8,12 @@ import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList;
import java.util.Random;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.Csv; import org.h2.tools.Csv;
...@@ -20,6 +23,7 @@ import org.h2.util.StringUtils; ...@@ -20,6 +23,7 @@ import org.h2.util.StringUtils;
public class TestCsv extends TestBase { public class TestCsv extends TestBase {
public void test() throws Exception { public void test() throws Exception {
testRandomData();
testEmptyFieldDelimiter(); testEmptyFieldDelimiter();
testFieldDelimiter(); testFieldDelimiter();
testAsTable(); testAsTable();
...@@ -27,7 +31,49 @@ public class TestCsv extends TestBase { ...@@ -27,7 +31,49 @@ public class TestCsv extends TestBase {
testRead(); testRead();
testPipe(); testPipe();
} }
private void testRandomData() throws Exception {
deleteDb("csv");
Connection conn = getConnection("csv");
Statement stat = conn.createStatement();
stat.execute("drop table if exists test");
stat.execute("create table test(a varchar, b varchar)");
int len = getSize(1000, 10000);
PreparedStatement prep = conn.prepareStatement("insert into test values(?, ?)");
ArrayList list = new ArrayList();
Random random = new Random(1);
for (int i = 0; i < len; i++) {
String a = randomData(random), b = randomData(random);
prep.setString(1, a);
prep.setString(2, b);
list.add(new String[]{a, b});
prep.execute();
}
stat.execute("CALL CSVWRITE('test.csv', 'SELECT * FROM test', 'UTF-8', '|', '#')");
Csv csv = Csv.getInstance();
csv.setFieldSeparatorRead('|');
csv.setFieldDelimiter('#');
ResultSet rs = csv.read("test.csv", null, "UTF-8");
for (int i = 0; i < len; i++) {
check(rs.next());
String[] pair = (String[]) list.get(i);
check(pair[0], rs.getString(1));
check(pair[1], rs.getString(2));
}
checkFalse(rs.next());
conn.close();
}
private String randomData(Random random) {
int len = random.nextInt(5);
StringBuffer buff = new StringBuffer();
String chars = "\\\'\",\r\n\t ;.-123456|#";
for (int i = 0; i < len; i++) {
buff.append(chars.charAt(random.nextInt(chars.length())));
}
return buff.toString();
}
private void testEmptyFieldDelimiter() throws Exception { private void testEmptyFieldDelimiter() throws Exception {
File f = new File(baseDir + "/test.csv"); File f = new File(baseDir + "/test.csv");
f.delete(); f.delete();
......
...@@ -15,8 +15,11 @@ import org.h2.constant.ErrorCode; ...@@ -15,8 +15,11 @@ import org.h2.constant.ErrorCode;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
public class TestMVCC extends TestBase { /**
* Basic MVCC (multi version concurrency) test cases.
*/
public class TestMvcc1 extends TestBase {
Connection c1, c2; Connection c1, c2;
Statement s1, s2; Statement s1, s2;
...@@ -41,10 +44,10 @@ public class TestMVCC extends TestBase { ...@@ -41,10 +44,10 @@ public class TestMVCC extends TestBase {
} }
rs = stat.executeQuery("select * from information_schema.settings where name='MVCC'"); rs = stat.executeQuery("select * from information_schema.settings where name='MVCC'");
rs.next(); rs.next();
check("FALSE", rs.getString("VALUE")); check("FALSE", rs.getString("VALUE"));
c1.close(); c1.close();
} }
private void testCases() throws Exception { private void testCases() throws Exception {
if (!config.mvcc) { if (!config.mvcc) {
return; return;
...@@ -71,7 +74,7 @@ public class TestMVCC extends TestBase { ...@@ -71,7 +74,7 @@ public class TestMVCC extends TestBase {
s2 = c2.createStatement(); s2 = c2.createStatement();
c1.setAutoCommit(false); c1.setAutoCommit(false);
c2.setAutoCommit(false); c2.setAutoCommit(false);
// it should not be possible to drop a table when an uncommitted transaction changed something // it should not be possible to drop a table when an uncommitted transaction changed something
s1.execute("create table test(id int primary key)"); s1.execute("create table test(id int primary key)");
s1.execute("insert into test values(1)"); s1.execute("insert into test values(1)");
...@@ -85,11 +88,11 @@ public class TestMVCC extends TestBase { ...@@ -85,11 +88,11 @@ public class TestMVCC extends TestBase {
c1.rollback(); c1.rollback();
s2.execute("drop table test"); s2.execute("drop table test");
c2.rollback(); c2.rollback();
// referential integrity problem // referential integrity problem
s1.execute("create table a (id integer identity not null, code varchar(10) not null, primary key(id))"); s1.execute("create table a (id integer identity not null, code varchar(10) not null, primary key(id))");
s1.execute("create table b (name varchar(100) not null, a integer, primary key(name), foreign key(a) references a(id))"); s1.execute("create table b (name varchar(100) not null, a integer, primary key(name), foreign key(a) references a(id))");
s1.execute("insert into a(code) values('one')"); s1.execute("insert into a(code) values('one')");
try { try {
s2.execute("insert into b values('un B', 1)"); s2.execute("insert into b values('un B', 1)");
error("Unexpected success"); error("Unexpected success");
...@@ -100,7 +103,7 @@ public class TestMVCC extends TestBase { ...@@ -100,7 +103,7 @@ public class TestMVCC extends TestBase {
c1.rollback(); c1.rollback();
s1.execute("drop table a, b"); s1.execute("drop table a, b");
c2.commit(); c2.commit();
// select for update should do an exclusive lock, even with mvcc // select for update should do an exclusive lock, even with mvcc
s1.execute("create table test(id int primary key, name varchar(255))"); s1.execute("create table test(id int primary key, name varchar(255))");
s1.execute("insert into test values(1, 'y')"); s1.execute("insert into test values(1, 'y')");
...@@ -117,7 +120,7 @@ public class TestMVCC extends TestBase { ...@@ -117,7 +120,7 @@ public class TestMVCC extends TestBase {
s1.execute("drop table test"); s1.execute("drop table test");
c1.commit(); c1.commit();
c2.commit(); c2.commit();
s1.execute("create table test(id int primary key, name varchar(255))"); s1.execute("create table test(id int primary key, name varchar(255))");
s2.execute("insert into test values(4, 'Hello')"); s2.execute("insert into test values(4, 'Hello')");
c2.rollback(); c2.rollback();
...@@ -126,7 +129,7 @@ public class TestMVCC extends TestBase { ...@@ -126,7 +129,7 @@ public class TestMVCC extends TestBase {
c1.commit(); c1.commit();
c2.commit(); c2.commit();
s1.execute("DROP TABLE TEST"); s1.execute("DROP TABLE TEST");
s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255))"); s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255))");
s1.execute("INSERT INTO TEST VALUES(1, 'Test')"); s1.execute("INSERT INTO TEST VALUES(1, 'Test')");
c1.commit(); c1.commit();
...@@ -138,7 +141,7 @@ public class TestMVCC extends TestBase { ...@@ -138,7 +141,7 @@ public class TestMVCC extends TestBase {
c2.commit(); c2.commit();
s1.execute("DROP TABLE TEST"); s1.execute("DROP TABLE TEST");
s1.execute("create table test as select * from table(id int=(1, 2))"); s1.execute("create table test as select * from table(id int=(1, 2))");
s1.execute("update test set id=1 where id=1"); s1.execute("update test set id=1 where id=1");
s1.execute("select max(id) from test"); s1.execute("select max(id) from test");
...@@ -172,7 +175,7 @@ public class TestMVCC extends TestBase { ...@@ -172,7 +175,7 @@ public class TestMVCC extends TestBase {
c1.commit(); c1.commit();
test(s1, "SELECT COUNT(*) FROM TEST", "0"); test(s1, "SELECT COUNT(*) FROM TEST", "0");
s1.execute("DROP TABLE TEST"); s1.execute("DROP TABLE TEST");
s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)"); s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
s1.execute("INSERT INTO TEST VALUES(1, 'Hello'), (2, 'World')"); s1.execute("INSERT INTO TEST VALUES(1, 'Hello'), (2, 'World')");
test(s2, "SELECT COUNT(*) FROM TEST", "0"); test(s2, "SELECT COUNT(*) FROM TEST", "0");
...@@ -205,7 +208,7 @@ public class TestMVCC extends TestBase { ...@@ -205,7 +208,7 @@ public class TestMVCC extends TestBase {
s1.execute("INSERT INTO TEST(NAME) VALUES('Ruebezahl')"); s1.execute("INSERT INTO TEST(NAME) VALUES('Ruebezahl')");
s1.execute("DROP TABLE TEST"); s1.execute("DROP TABLE TEST");
c1.commit(); c1.commit();
s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)"); s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
s1.execute("INSERT INTO TEST VALUES(1, 'Hello')"); s1.execute("INSERT INTO TEST VALUES(1, 'Hello')");
c1.commit(); c1.commit();
...@@ -213,7 +216,7 @@ public class TestMVCC extends TestBase { ...@@ -213,7 +216,7 @@ public class TestMVCC extends TestBase {
c1.rollback(); c1.rollback();
s1.execute("DROP TABLE TEST"); s1.execute("DROP TABLE TEST");
c1.commit(); c1.commit();
Random random = new Random(1); Random random = new Random(1);
s1.execute("CREATE TABLE TEST(ID INT IDENTITY, NAME VARCHAR)"); s1.execute("CREATE TABLE TEST(ID INT IDENTITY, NAME VARCHAR)");
Statement s; Statement s;
...@@ -291,7 +294,7 @@ public class TestMVCC extends TestBase { ...@@ -291,7 +294,7 @@ public class TestMVCC extends TestBase {
s1.execute("DROP TABLE TEST"); s1.execute("DROP TABLE TEST");
c1.commit(); c1.commit();
c2.commit(); c2.commit();
s1.execute("CREATE TABLE TEST(ID INT, NAME VARCHAR)"); s1.execute("CREATE TABLE TEST(ID INT, NAME VARCHAR)");
s1.execute("INSERT INTO TEST VALUES(1, 'Hello')"); s1.execute("INSERT INTO TEST VALUES(1, 'Hello')");
test(s2, "SELECT COUNT(*) FROM TEST WHERE NAME!='X'", "0"); test(s2, "SELECT COUNT(*) FROM TEST WHERE NAME!='X'", "0");
...@@ -302,7 +305,7 @@ public class TestMVCC extends TestBase { ...@@ -302,7 +305,7 @@ public class TestMVCC extends TestBase {
s1.execute("DROP TABLE TEST"); s1.execute("DROP TABLE TEST");
c1.commit(); c1.commit();
c2.commit(); c2.commit();
s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)"); s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
s1.execute("INSERT INTO TEST VALUES(1, 'Hello')"); s1.execute("INSERT INTO TEST VALUES(1, 'Hello')");
test(s2, "SELECT COUNT(*) FROM TEST WHERE ID<100", "0"); test(s2, "SELECT COUNT(*) FROM TEST WHERE ID<100", "0");
...@@ -313,7 +316,7 @@ public class TestMVCC extends TestBase { ...@@ -313,7 +316,7 @@ public class TestMVCC extends TestBase {
s1.execute("DROP TABLE TEST"); s1.execute("DROP TABLE TEST");
c1.commit(); c1.commit();
c2.commit(); c2.commit();
s1.execute("CREATE TABLE TEST(ID INT, NAME VARCHAR, PRIMARY KEY(ID, NAME))"); s1.execute("CREATE TABLE TEST(ID INT, NAME VARCHAR, PRIMARY KEY(ID, NAME))");
s1.execute("INSERT INTO TEST VALUES(1, 'Hello')"); s1.execute("INSERT INTO TEST VALUES(1, 'Hello')");
c1.commit(); c1.commit();
...@@ -324,8 +327,8 @@ public class TestMVCC extends TestBase { ...@@ -324,8 +327,8 @@ public class TestMVCC extends TestBase {
s1.execute("DROP TABLE TEST"); s1.execute("DROP TABLE TEST");
c1.commit(); c1.commit();
c2.commit(); c2.commit();
s1.execute("create table test(id int primary key, name varchar(255))"); s1.execute("create table test(id int primary key, name varchar(255))");
s1.execute("insert into test values(1, 'Hello'), (2, 'World')"); s1.execute("insert into test values(1, 'Hello'), (2, 'World')");
c1.commit(); c1.commit();
...@@ -343,7 +346,7 @@ public class TestMVCC extends TestBase { ...@@ -343,7 +346,7 @@ public class TestMVCC extends TestBase {
check(rs.getInt(1), 2); check(rs.getInt(1), 2);
check(rs.getString(2), "World"); check(rs.getString(2), "World");
checkFalse(rs.next()); checkFalse(rs.next());
rs = s2.executeQuery("select * from test order by id"); rs = s2.executeQuery("select * from test order by id");
check(rs.next()); check(rs.next());
check(rs.getInt(1), 1); check(rs.getInt(1), 1);
...@@ -355,12 +358,10 @@ public class TestMVCC extends TestBase { ...@@ -355,12 +358,10 @@ public class TestMVCC extends TestBase {
s1.execute("drop table test"); s1.execute("drop table test");
c1.commit(); c1.commit();
c2.commit(); c2.commit();
c1.close(); c1.close();
c2.close(); c2.close();
} }
private void test(Statement stat, String sql, String expected) throws Exception { private void test(Statement stat, String sql, String expected) throws Exception {
...@@ -377,7 +378,5 @@ public class TestMVCC extends TestBase { ...@@ -377,7 +378,5 @@ public class TestMVCC extends TestBase {
throw new Error("expected: " + expected + ", got: no rows"); throw new Error("expected: " + expected + ", got: no rows");
} }
} }
// TODO Auto-generated method stub
} }
} }
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.mvcc;
import java.sql.Connection;
import java.sql.Statement;
import org.h2.test.TestBase;
/**
* Additional MVCC (multi version concurrency) test cases.
*/
public class TestMvcc2 extends TestBase {
private static final String DROP_TABLE = "DROP TABLE IF EXISTS EMPLOYEE";
private static final String CREATE_TABLE = "CREATE TABLE EMPLOYEE (id BIGINT, version BIGINT, NAME VARCHAR(255))";
private static final String INSERT = "INSERT INTO EMPLOYEE (id, version, NAME) VALUES (1, 1, 'Jones')";
private static final String UPDATE = "UPDATE EMPLOYEE SET NAME = 'Miller' WHERE version = 1";
public void test() throws Exception {
if (!config.mvcc) {
return;
}
deleteDb("mvcc2");
testInsertUpdateRollback();
testInsertRollback();
}
Connection getConnection() throws Exception {
return getConnection("mvcc2");
}
public void testInsertUpdateRollback() throws Exception {
Connection conn = getConnection();
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
stmt.execute(DROP_TABLE);
stmt.execute(CREATE_TABLE);
conn.commit();
stmt.execute(INSERT);
stmt.execute(UPDATE);
conn.rollback();
conn.close();
}
public void testInsertRollback() throws Exception {
Connection conn = getConnection();
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
stmt.execute(DROP_TABLE);
stmt.execute(CREATE_TABLE);
conn.commit();
stmt.execute(INSERT);
conn.rollback();
conn.close();
}
}
--- special grammar and test cases --------------------------------------------------------------------------------------------- --- special grammar and test cases ---------------------------------------------------------------------------------------------
select * from dual a left join dual b on b.x=(select max(x) from dual);
> exception
select count(*) from system_range(1, 2) where x in(1, 1, 1); select count(*) from system_range(1, 2) where x in(1, 1, 1);
> COUNT(*) > COUNT(*)
> -------- > --------
......
Defect report:
What steps will reproduce the problem?
(simple SQL scripts or simple standalone applications are preferred)
1.
2.
3.
What is the expected output? What do you see instead?
What version of the product are you using? On what operating system, file system, and virtual machine?
Do you know a workaround?
How important/urgent is the problem for you?
In your view, is this a defect or a feature request?
Please provide any additional information below.
-----------------
Corrupted database
I am sorry to say that, but it looks like a corruption problem. I am very interested in analyzing and solving this problem. It has top priority for me. I have a few question:
- What is your database URL?
- What version H2 are you using?
- Do you use any settings or special features (for example, the setting LOG=0, or two phase commit, linked tables, cache settings)?
- On what operating system, file system, and virtual machine?
- How big is the database?
- Is the database usually closed normally, or is process terminated forcefully or the computer switched off?
- Is it possible to reproduce this problem using a fresh database?
- Are there any other exceptions (maybe in the .trace.db file)? Could you post them please?
- Was the database originally created with an older version of H2?
- Do you still have any .trace.db files, and if yes could you send them?
- Could you send me the .data.db file where this exception occurs?
/* /*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html). * Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.test.unit; package org.h2.test.unit;
import org.h2.server.ftp.FtpEvent;
import org.h2.server.ftp.FtpEventListener;
import org.h2.server.ftp.FtpServer;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.Server; import org.h2.tools.Server;
public class TestFtp extends TestBase { public class TestFtp extends TestBase implements FtpEventListener {
private FtpEvent lastEvent;
public void test() throws Exception { public void test() throws Exception {
test(baseDir); test(baseDir);
...@@ -15,17 +20,33 @@ public class TestFtp extends TestBase { ...@@ -15,17 +20,33 @@ public class TestFtp extends TestBase {
private void test(String dir) throws Exception { private void test(String dir) throws Exception {
Server server = Server.createFtpServer(new String[]{"-ftpDir", dir}).start(); Server server = Server.createFtpServer(new String[]{"-ftpDir", dir}).start();
FtpServer ftp = (FtpServer) server.getService();
ftp.setEventListener(this);
FtpClient client = FtpClient.open("localhost:8021"); FtpClient client = FtpClient.open("localhost:8021");
client.login("sa", "sa"); client.login("sa", "sa");
client.makeDirectory("test"); client.makeDirectory("test");
client.changeWorkingDirectory("test"); client.changeWorkingDirectory("test");
check(lastEvent.getCommand(), "CWD");
client.makeDirectory("hello"); client.makeDirectory("hello");
client.changeWorkingDirectory("hello"); client.changeWorkingDirectory("hello");
client.changeDirectoryUp(); client.changeDirectoryUp();
check(lastEvent.getCommand(), "CDUP");
client.nameList("hello"); client.nameList("hello");
client.removeDirectory("hello"); client.removeDirectory("hello");
client.close(); client.close();
server.stop(); server.stop();
} }
public void beforeCommand(FtpEvent event) {
lastEvent = event;
}
public void afterCommand(FtpEvent event) {
lastEvent = event;
}
public void onUnsupportedCommand(FtpEvent event) {
lastEvent = event;
}
} }
...@@ -237,6 +237,9 @@ public class TestTools extends TestBase { ...@@ -237,6 +237,9 @@ public class TestTools extends TestBase {
} }
private void testManagementDb() throws Exception { private void testManagementDb() throws Exception {
if (config.networked) {
return;
}
int count = getSize(2, 10); int count = getSize(2, 10);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
Server server = Server.createTcpServer(new String[] {}).start(); Server server = Server.createTcpServer(new String[] {}).start();
...@@ -331,11 +334,13 @@ public class TestTools extends TestBase { ...@@ -331,11 +334,13 @@ public class TestTools extends TestBase {
Server.shutdownTcpServer("tcp://localhost", "", true); Server.shutdownTcpServer("tcp://localhost", "", true);
error("shouldn't work and should throw an exception"); error("shouldn't work and should throw an exception");
} catch (SQLException e) { } catch (SQLException e) {
// expected checkNotGeneralException(e);
} }
conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/test", "sa", ""); conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/test", "sa", "");
conn.close(); // conn.close();
Server.shutdownTcpServer("tcp://localhost", "abc", true); Server.shutdownTcpServer("tcp://localhost", "abc", true);
// check that the database is closed
deleteDb("test");
try { try {
conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/test", "sa", ""); conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/test", "sa", "");
error("server must have been closed"); error("server must have been closed");
......
...@@ -517,4 +517,4 @@ russian backward alexahin vlad ffffffffffff bfff ffffffff webapp undeploy initia ...@@ -517,4 +517,4 @@ russian backward alexahin vlad ffffffffffff bfff ffffffff webapp undeploy initia
llc computing oliver road inaccessible android velasques duplicates eduardo chunk brazilian near langpair xrmd xmkd llc computing oliver road inaccessible android velasques duplicates eduardo chunk brazilian near langpair xrmd xmkd
encapsulates negating igor midnight fulfill prefixes communicates nesting convenience negated resides optimizing principal applets dobrovolskyi encapsulates negating igor midnight fulfill prefixes communicates nesting convenience negated resides optimizing principal applets dobrovolskyi
involves ukrainian chile machines restricting summer aliased backus naur multiples avl operates grow normalized rijndael involves ukrainian chile machines restricting summer aliased backus naur multiples avl operates grow normalized rijndael
countdown paused javac countdown paused javac analyzing accesses solving forcefully urgent originally defect coordinates
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论