提交 6c3455a1 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 74108a40
...@@ -35,6 +35,14 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -35,6 +35,14 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h2>Change Log</h2> <h2>Change Log</h2>
<h3>Version 1.0 (Current)</h3> <h3>Version 1.0 (Current)</h3>
<h3>Version 1.0 / 2007-02-TODO</h3><ul>
<li>SCRIPT did not work correctly with BLOB or CLOB data. Fixed.
</li><li>When a subquery was used in the select list of a query, and GROUP BY was used at the same time,
a NullPointerException could occur. Fixed.
</li><li>ORDER BY did not work when DISTINCT was used at the same timein some situations. Fixed.
</li><li>When using IN(...) on a case insensitive column (VARCHAR_IGNORECASE),
an incorrect optimization was made and the result was wrong sometimes.
</li></ul>
<h3>Version 1.0 / 2007-01-30</h3><ul> <h3>Version 1.0 / 2007-01-30</h3><ul>
<li>Experimental online backup feature using the SQL statement BACKUP TO 'fileName'. <li>Experimental online backup feature using the SQL statement BACKUP TO 'fileName'.
...@@ -1532,6 +1540,8 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -1532,6 +1540,8 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Support curtimestamp (like curtime, curdate) </li><li>Support curtimestamp (like curtime, curdate)
</li><li>Support ANALYZE {TABLE|INDEX} tablename COMPUTE|ESTIMATE|DELETE STATISTICS ptnOption options </li><li>Support ANALYZE {TABLE|INDEX} tablename COMPUTE|ESTIMATE|DELETE STATISTICS ptnOption options
</li><li>Support Sequoia (Continuent.org) </li><li>Support Sequoia (Continuent.org)
</li><li>Dynamic length numbers / special methods for DataPage.writeByte / writeShort / Ronni Nielsen
</li><li>Pluggable tracing system, ThreadPool, (AvalonDB / deebee / Paul Hammant)
</li></ul> </li></ul>
<h3>Not Planned</h3> <h3>Not Planned</h3>
......
...@@ -105,6 +105,7 @@ import org.h2.table.Column; ...@@ -105,6 +105,7 @@ import org.h2.table.Column;
import org.h2.table.FunctionTable; import org.h2.table.FunctionTable;
import org.h2.table.RangeTable; import org.h2.table.RangeTable;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.table.TableView; import org.h2.table.TableView;
import org.h2.util.ByteUtils; import org.h2.util.ByteUtils;
...@@ -361,6 +362,11 @@ public class Parser { ...@@ -361,6 +362,11 @@ public class Parser {
c = parserCall(); c = parserCall();
} }
break; break;
case 'W':
if(readIf("WITH")) {
c = parserWith();
}
break;
default: default:
// TODO exception: unknown command // TODO exception: unknown command
throw getSyntaxError(); throw getSyntaxError();
...@@ -3148,18 +3154,57 @@ public class Parser { ...@@ -3148,18 +3154,57 @@ public class Parser {
return command; return command;
} }
private Query parserWith() throws SQLException {
// String tempViewName = readUniqueIdentifier();
// if(readIf("(")) {
// String[] cols = parseColumnList(false);
// command.setColumnNames(cols);
//
//
// if(recursive) {
// ObjectArray columns = new ObjectArray();
// for(int i=0; i<cols.length; i++) {
// columns.add(new Column(cols[i], Value.STRING, 0, 0));
// }
// recursiveTable = new TableData(getSchema(), viewName, 0, columns, false);
// recursiveTable.setTemporary(true);
// session.addLocalTempTable(recursiveTable);
// }
return null;
}
private CreateView parseCreateView(boolean force) throws SQLException { private CreateView parseCreateView(boolean force) throws SQLException {
int test;
TableData recursiveTable = null;
boolean recursive = readIf("RECURSIVE");
boolean ifNotExists = readIfNoExists(); boolean ifNotExists = readIfNoExists();
String viewName = readIdentifierWithSchema(); String viewName = readIdentifierWithSchema();
CreateView command = new CreateView(session, getSchema()); CreateView command = new CreateView(session, getSchema());
command.setViewName(viewName); command.setViewName(viewName);
command.setIfNotExists(ifNotExists); command.setIfNotExists(ifNotExists);
String select = StringCache.getNew(sqlCommand.substring(parseIndex));
command.setComment(readCommentIf()); command.setComment(readCommentIf());
if(readIf("(")) { if(readIf("(")) {
String[] cols = parseColumnList(false); String[] cols = parseColumnList(false);
command.setColumnNames(cols); command.setColumnNames(cols);
if(recursive) {
ObjectArray columns = new ObjectArray();
for(int i=0; i<cols.length; i++) {
columns.add(new Column(cols[i], Value.STRING, 0, 0));
}
recursiveTable = new TableData(getSchema(), viewName, 0, columns, false);
recursiveTable.setTemporary(true);
session.addLocalTempTable(recursiveTable);
}
} }
String select = StringCache.getNew(sqlCommand.substring(parseIndex));
read("AS"); read("AS");
try { try {
Query query = parseSelect(); Query query = parseSelect();
...@@ -3172,6 +3217,11 @@ public class Parser { ...@@ -3172,6 +3217,11 @@ public class Parser {
throw e; throw e;
} }
} }
if(recursiveTable != null) {
session.removeLocalTempTable(recursiveTable);
}
return command; return command;
} }
......
/* /*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html). * Copyright 2004-2006 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.command.ddl; package org.h2.command.ddl;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.command.dml.Query; import org.h2.command.dml.Query;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.schema.Schema; import org.h2.schema.Schema;
import org.h2.table.TableView; import org.h2.table.TableView;
public class CreateView extends SchemaCommand { public class CreateView extends SchemaCommand {
private Query select; private Query select;
private String viewName; private String viewName;
...@@ -74,4 +74,4 @@ public class CreateView extends SchemaCommand { ...@@ -74,4 +74,4 @@ public class CreateView extends SchemaCommand {
this.comment = comment; this.comment = comment;
} }
} }
...@@ -19,9 +19,9 @@ import org.h2.engine.Database; ...@@ -19,9 +19,9 @@ import org.h2.engine.Database;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.store.DiskFile; import org.h2.store.DiskFile;
import org.h2.store.FileLister;
import org.h2.store.LogFile; import org.h2.store.LogFile;
import org.h2.store.LogSystem; import org.h2.store.LogSystem;
import org.h2.tools.FileBase;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
...@@ -72,7 +72,7 @@ public class Backup extends Prepared { ...@@ -72,7 +72,7 @@ public class Backup extends Prepared {
backupFile(out, fn); backupFile(out, fn);
db.setProgress(DatabaseEventListener.STATE_BACKUP_FILE, name, i, max); db.setProgress(DatabaseEventListener.STATE_BACKUP_FILE, name, i, max);
} }
ArrayList fileList = FileBase.getDatabaseFiles(db.getDatabasePath(), name, true); ArrayList fileList = FileLister.getDatabaseFiles(db.getDatabasePath(), name, true);
for(int i=0; i<fileList.size(); i++) { for(int i=0; i<fileList.size(); i++) {
fn = (String) fileList.get(i); fn = (String) fileList.get(i);
if(fn.endsWith(Constants.SUFFIX_HASH_FILE) || fn.endsWith(Constants.SUFFIX_LOB_FILE)) { if(fn.endsWith(Constants.SUFFIX_HASH_FILE) || fn.endsWith(Constants.SUFFIX_LOB_FILE)) {
......
...@@ -473,7 +473,7 @@ public class Select extends Query { ...@@ -473,7 +473,7 @@ public class Select extends Query {
isQuickQuery = isEverything(optimizable); isQuickQuery = isEverything(optimizable);
} }
cost = preparePlan(); cost = preparePlan();
if(sort != null && !isQuickQuery && !isGroupQuery) { if(sort != null && !isQuickQuery && !isGroupQuery && !distinct) {
Index index = getSortIndex(); Index index = getSortIndex();
Index current = topTableFilter.getIndex(); Index current = topTableFilter.getIndex();
if(index != null && (current.indexType.isScan() || current == index)) { if(index != null && (current.indexType.isScan() || current == index)) {
...@@ -537,6 +537,9 @@ public class Select extends Query { ...@@ -537,6 +537,9 @@ public class Select extends Query {
} }
public String getPlan() { public String getPlan() {
if(topTableFilter == null) {
return sql;
}
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
Expression[] exprList = new Expression[expressions.size()]; Expression[] exprList = new Expression[expressions.size()];
expressions.toArray(exprList); expressions.toArray(exprList);
......
...@@ -302,4 +302,11 @@ public class SelectUnion extends Query { ...@@ -302,4 +302,11 @@ public class SelectUnion extends Query {
return left.isReadOnly() && right.isReadOnly(); return left.isReadOnly() && right.isReadOnly();
} }
public Query getLeftQuery() {
return left;
}
public Query getRightQuery() {
return right;
}
} }
...@@ -73,8 +73,8 @@ package org.h2.engine; ...@@ -73,8 +73,8 @@ package org.h2.engine;
*/ */
public class Constants { public class Constants {
public static final int BUILD_ID = 41; public static final int BUILD_ID = 42;
private static final String BUILD = "2007-01-30"; private static final String BUILD = "2007-02-06";
public static final int VERSION_MAJOR = 1; public static final int VERSION_MAJOR = 1;
public static final int VERSION_MINOR = 0; public static final int VERSION_MINOR = 0;
......
...@@ -95,6 +95,7 @@ public class ConditionIn extends Condition { ...@@ -95,6 +95,7 @@ public class ConditionIn extends Condition {
return expr; return expr;
} }
if(Constants.OPTIMIZE_IN) { if(Constants.OPTIMIZE_IN) {
int dataType = left.getType();
ExpressionVisitor independent = ExpressionVisitor.get(ExpressionVisitor.INDEPENDENT); ExpressionVisitor independent = ExpressionVisitor.get(ExpressionVisitor.INDEPENDENT);
independent.queryLevel = queryLevel; independent.queryLevel = queryLevel;
if(areAllValues(independent)) { if(areAllValues(independent)) {
...@@ -103,6 +104,7 @@ public class ConditionIn extends Condition { ...@@ -103,6 +104,7 @@ public class ConditionIn extends Condition {
for(int i=0; i<values.size(); i++) { for(int i=0; i<values.size(); i++) {
Expression e = (Expression) values.get(i); Expression e = (Expression) values.get(i);
Value v = e.getValue(session); Value v = e.getValue(session);
v = v.convertTo(dataType);
values.set(i, ValueExpression.get(v)); values.set(i, ValueExpression.get(v));
if(min == null || min.compareTo(v, mode) > 0) { if(min == null || min.compareTo(v, mode) > 0) {
min = v; min = v;
......
...@@ -7,6 +7,7 @@ package org.h2.index; ...@@ -7,6 +7,7 @@ package org.h2.index;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.command.dml.Query; import org.h2.command.dml.Query;
import org.h2.command.dml.SelectUnion;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.expression.Comparison; import org.h2.expression.Comparison;
...@@ -35,10 +36,21 @@ public class ViewIndex extends Index { ...@@ -35,10 +36,21 @@ public class ViewIndex extends Index {
private long lastEvaluated; private long lastEvaluated;
private LocalResult lastResult; private LocalResult lastResult;
public ViewIndex(TableView view, String querySQL, ObjectArray originalParameters) { private int todoRecursiveMustBePrivate;
public boolean recursive;
private int recurseLevel;
private LocalResult recursiveResult;
public ViewIndex(TableView view, String querySQL, ObjectArray originalParameters, boolean recursive) {
super(view, 0, null, null, IndexType.createNonUnique(false)); super(view, 0, null, null, IndexType.createNonUnique(false));
this.querySQL = querySQL; this.querySQL = querySQL;
this.originalParameters = originalParameters; this.originalParameters = originalParameters;
int test;
this.recursive = recursive;
columns = new Column[0];
params = new Parameter[0];
} }
public String getPlanSQL() { public String getPlanSQL() {
...@@ -76,6 +88,9 @@ public class ViewIndex extends Index { ...@@ -76,6 +88,9 @@ public class ViewIndex extends Index {
} }
public double getCost(Session session, int[] masks) throws SQLException { public double getCost(Session session, int[] masks) throws SQLException {
if(recursive) {
return 10;
}
IntArray masksArray = new IntArray(masks == null ? new int[0] : masks); IntArray masksArray = new IntArray(masks == null ? new int[0] : masks);
CostElement cachedCost = (CostElement) costCache.get(masksArray); CostElement cachedCost = (CostElement) costCache.get(masksArray);
if(cachedCost != null) { if(cachedCost != null) {
...@@ -85,11 +100,11 @@ public class ViewIndex extends Index { ...@@ -85,11 +100,11 @@ public class ViewIndex extends Index {
} }
} }
Query query = (Query)session.prepare(querySQL, true); Query query = (Query)session.prepare(querySQL, true);
IntArray paramIndex = new IntArray();
if(masks == null) { if(masks == null) {
columns = new Column[0]; columns = new Column[0];
params = new Parameter[0]; params = new Parameter[0];
} else { } else {
IntArray paramIndex = new IntArray();
for(int i=0; i<masks.length; i++) { for(int i=0; i<masks.length; i++) {
int mask = masks[i]; int mask = masks[i];
if(mask == 0) { if(mask == 0) {
...@@ -110,6 +125,9 @@ public class ViewIndex extends Index { ...@@ -110,6 +125,9 @@ public class ViewIndex extends Index {
int comparisonType = getComparisonType(mask); int comparisonType = getComparisonType(mask);
query.addGlobalCondition(param, idx, comparisonType); query.addGlobalCondition(param, idx, comparisonType);
} }
if(recursive) {
return 10;
}
String sql = query.getSQL(); String sql = query.getSQL();
query = (Query)session.prepare(sql); query = (Query)session.prepare(sql);
} }
...@@ -123,6 +141,35 @@ public class ViewIndex extends Index { ...@@ -123,6 +141,35 @@ public class ViewIndex extends Index {
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException { public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
Query query = (Query)session.prepare(querySQL, true); Query query = (Query)session.prepare(querySQL, true);
if(recursive) {
SelectUnion union = (SelectUnion) query;
Query left = union.getLeftQuery();
Query right = union.getRightQuery();
LocalResult completeResult;
if(recurseLevel==0) {
LocalResult result = left.query(0);
completeResult = result;
recurseLevel = 1;
result = left.query(0);
while(true) {
recursiveResult = result;
recurseLevel++;
result = right.query(0);
if(result.getRowCount() == 0) {
break;
}
result = right.query(0);
while(result.next()) {
completeResult.addRow(result.currentRow());
}
}
completeResult.done();
recurseLevel=0;
return new ViewCursor(table, completeResult);
} else {
return new ViewCursor(table, recursiveResult);
}
}
ObjectArray paramList = query.getParameters(); ObjectArray paramList = query.getParameters();
for(int i=0; first != null && i<first.getColumnCount(); i++) { for(int i=0; first != null && i<first.getColumnCount(); i++) {
Value v = first.getValue(i); Value v = first.getValue(i);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html). * Copyright 2004-2006 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.tools; package org.h2.store;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -14,11 +14,7 @@ import org.h2.util.FileUtils; ...@@ -14,11 +14,7 @@ import org.h2.util.FileUtils;
* @author Thomas * @author Thomas
*/ */
public abstract class FileBase { public class FileLister {
protected boolean allFiles() {
return false;
}
/** /**
* Get the list of database files. * Get the list of database files.
...@@ -71,20 +67,4 @@ public abstract class FileBase { ...@@ -71,20 +67,4 @@ public abstract class FileBase {
return files; return files;
} }
protected void processFiles(String dir, String db, boolean log) throws SQLException {
ArrayList files = getDatabaseFiles(dir, db, allFiles());
for(int i=0; i<files.size(); i++) {
String fileName = (String) files.get(i);
process(fileName);
if(log) {
System.out.println("processed: "+fileName);
}
}
if(files.size() == 0 && log) {
System.out.println("No database files found");
}
}
protected abstract void process(String fileName) throws SQLException;
} }
...@@ -170,7 +170,7 @@ public class TableData extends Table implements RecordReader { ...@@ -170,7 +170,7 @@ public class TableData extends Table implements RecordReader {
} }
addRowsToIndex(session, buffer, index); addRowsToIndex(session, buffer, index);
if(Constants.CHECK && remaining != 0) { if(Constants.CHECK && remaining != 0) {
throw Message.getInternalError("rowcount remaining=" + remaining); throw Message.getInternalError("rowcount remaining=" + remaining + " " + getName());
} }
} catch(SQLException e) { } catch(SQLException e) {
try { try {
......
...@@ -18,6 +18,7 @@ import org.h2.result.Row; ...@@ -18,6 +18,7 @@ import org.h2.result.Row;
import org.h2.schema.Schema; import org.h2.schema.Schema;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.Value;
public class TableView extends Table { public class TableView extends Table {
...@@ -28,11 +29,14 @@ public class TableView extends Table { ...@@ -28,11 +29,14 @@ public class TableView extends Table {
private Query viewQuery; private Query viewQuery;
private ViewIndex index; private ViewIndex index;
private int test;
private boolean recursive;
public TableView(Schema schema, int id, String name, String querySQL, ObjectArray params, String[] columnNames, Session session) throws SQLException { public TableView(Schema schema, int id, String name, String querySQL, ObjectArray params, String[] columnNames, Session session) throws SQLException {
super(schema, id, name, false); super(schema, id, name, false);
this.querySQL = querySQL; this.querySQL = querySQL;
this.columnNames = columnNames; this.columnNames = columnNames;
index = new ViewIndex(this, querySQL, params); index = new ViewIndex(this, querySQL, params, recursive);
initColumnsAndTables(session); initColumnsAndTables(session);
} }
...@@ -77,6 +81,18 @@ public class TableView extends Table { ...@@ -77,6 +81,18 @@ public class TableView extends Table {
tables = new ObjectArray(); tables = new ObjectArray();
cols = new Column[0]; cols = new Column[0];
invalid = true; invalid = true;
int testing;
if(columnNames != null) {
cols = new Column[columnNames.length];
for(int i=0; i<columnNames.length; i++) {
cols[i] = new Column(columnNames[i], Value.STRING, 255, 0);
}
invalid = false;
index.recursive=true;
recursive = true;
}
} }
setColumns(cols); setColumns(cols);
} }
......
...@@ -5,15 +5,24 @@ ...@@ -5,15 +5,24 @@
package org.h2.tools; package org.h2.tools;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.store.FileLister;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -153,5 +162,38 @@ public class Backup { ...@@ -153,5 +162,38 @@ public class Backup {
} }
} }
/**
* INTERNAL
*/
public static void backupFiles(String zipFileName, String directory, String db) throws IOException, SQLException {
File file = new File(zipFileName);
if(file.exists()) {
file.delete();
}
FileOutputStream out = null;
try {
out = new FileOutputStream(file);
ZipOutputStream zipOut = new ZipOutputStream(out);
ArrayList list = FileLister.getDatabaseFiles(directory, db, true);
for(int i=0; i<list.size(); i++) {
String fileName = (String) list.get(i);
ZipEntry entry = new ZipEntry(FileUtils.getFileName(fileName));
zipOut.putNextEntry(entry);
FileInputStream in = null;
try {
in = new FileInputStream(fileName);
IOUtils.copyAndCloseInput(in, zipOut);
} finally {
IOUtils.closeSilently(in);
}
zipOut.closeEntry();
}
zipOut.closeEntry();
zipOut.close();
} finally {
IOUtils.closeSilently(out);
}
}
} }
...@@ -6,23 +6,24 @@ package org.h2.tools; ...@@ -6,23 +6,24 @@ package org.h2.tools;
import java.io.IOException; import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.security.SHA256; import org.h2.security.SHA256;
import org.h2.store.FileLister;
import org.h2.store.FileStore; import org.h2.store.FileStore;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
/** /**
* A tools to change, remove or set a file password of a database without opening it. * A tools to change, remove or set a file password of a database without opening it.
*/ */
public class ChangePassword extends FileBase { public class ChangePassword {
private String dir; private String dir;
private String cipher; private String cipher;
private byte[] decrypt; private byte[] decrypt;
private byte[] encrypt; private byte[] encrypt;
private boolean testRenameOnly;
// TODO security: maybe allow functions in the url // TODO security: maybe allow functions in the url
// jdbc:h2:test;action=[decrypt|encrypt|check|reindex|recover|compress...] // jdbc:h2:test;action=[decrypt|encrypt|check|reindex|recover|compress...]
...@@ -113,24 +114,26 @@ public class ChangePassword extends FileBase { ...@@ -113,24 +114,26 @@ public class ChangePassword extends FileBase {
change.encrypt = encrypt; change.encrypt = encrypt;
// first, test only if the file can be renamed (to find errors with locked files early) // first, test only if the file can be renamed (to find errors with locked files early)
change.testRenameOnly = true; ArrayList files = FileLister.getDatabaseFiles(dir, db, true);
change.processFiles(dir, db, !quiet); for (int i = 0; i < files.size(); i++) {
// if this worked, the operation will (hopefully) be successful String fileName = (String) files.get(i);
// TODO changePassword: this is a workaround! make the operation atomic (all files or none)
change.testRenameOnly = false;
change.processFiles(dir, db, !quiet);
}
protected void process(String fileName) throws SQLException {
if(FileUtils.isDirectory(fileName)) {
return;
}
if(testRenameOnly) {
String temp = dir + "/temp.db"; String temp = dir + "/temp.db";
FileUtils.delete(temp); FileUtils.delete(temp);
FileUtils.rename(fileName, temp); FileUtils.rename(fileName, temp);
FileUtils.rename(temp, fileName); FileUtils.rename(temp, fileName);
} else { }
// if this worked, the operation will (hopefully) be successful
// TODO changePassword: this is a workaround! make the operation atomic (all files or none)
for (int i = 0; i < files.size(); i++) {
String fileName = (String) files.get(i);
change.process(fileName);
}
if (files.size() == 0 && !quiet) {
System.out.println("No database files found");
}
}
private void process(String fileName) throws SQLException {
boolean textStorage = Database.isTextStorage(fileName, false); boolean textStorage = Database.isTextStorage(fileName, false);
byte[] magic = Database.getMagic(textStorage); byte[] magic = Database.getMagic(textStorage);
FileStore in; FileStore in;
...@@ -142,7 +145,6 @@ public class ChangePassword extends FileBase { ...@@ -142,7 +145,6 @@ public class ChangePassword extends FileBase {
in.init(); in.init();
copy(fileName, textStorage, in, encrypt); copy(fileName, textStorage, in, encrypt);
} }
}
private void copy(String fileName, boolean textStorage, FileStore in, byte[] key) throws SQLException { private void copy(String fileName, boolean textStorage, FileStore in, byte[] key) throws SQLException {
String temp = dir + "/temp.db"; String temp = dir + "/temp.db";
......
...@@ -5,8 +5,10 @@ ...@@ -5,8 +5,10 @@
package org.h2.tools; package org.h2.tools;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.store.FileLister;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
/** /**
...@@ -15,9 +17,7 @@ import org.h2.util.FileUtils; ...@@ -15,9 +17,7 @@ import org.h2.util.FileUtils;
* @author Thomas * @author Thomas
*/ */
public class DeleteDbFiles extends FileBase { public class DeleteDbFiles {
private boolean quiet;
private void showUsage() { private void showUsage() {
System.out.println("java "+getClass().getName()+" [-dir <dir>] [-db <database>] [-quiet]"); System.out.println("java "+getClass().getName()+" [-dir <dir>] [-db <database>] [-quiet]");
...@@ -70,11 +70,20 @@ public class DeleteDbFiles extends FileBase { ...@@ -70,11 +70,20 @@ public class DeleteDbFiles extends FileBase {
*/ */
public static void execute(String dir, String db, boolean quiet) throws SQLException { public static void execute(String dir, String db, boolean quiet) throws SQLException {
DeleteDbFiles delete = new DeleteDbFiles(); DeleteDbFiles delete = new DeleteDbFiles();
delete.quiet = quiet; ArrayList files = FileLister.getDatabaseFiles(dir, db, true);
delete.processFiles(dir, db, !quiet); for (int i = 0; i < files.size(); i++) {
String fileName = (String) files.get(i);
delete.process(fileName, quiet);
if (!quiet) {
System.out.println("processed: " + fileName);
}
}
if (files.size() == 0 && !quiet) {
System.out.println("No database files found");
}
} }
protected void process(String fileName) throws SQLException { private void process(String fileName, boolean quiet) throws SQLException {
if(quiet || fileName.endsWith(Constants.SUFFIX_TEMP_FILE) || fileName.endsWith(Constants.SUFFIX_TRACE_FILE)) { if(quiet || fileName.endsWith(Constants.SUFFIX_TEMP_FILE) || fileName.endsWith(Constants.SUFFIX_TRACE_FILE)) {
FileUtils.tryDelete(fileName); FileUtils.tryDelete(fileName);
} else { } else {
...@@ -82,8 +91,4 @@ public class DeleteDbFiles extends FileBase { ...@@ -82,8 +91,4 @@ public class DeleteDbFiles extends FileBase {
} }
} }
protected boolean allFiles() {
return true;
}
} }
...@@ -32,6 +32,7 @@ import org.h2.security.SHA256; ...@@ -32,6 +32,7 @@ import org.h2.security.SHA256;
import org.h2.store.DataHandler; import org.h2.store.DataHandler;
import org.h2.store.DataPage; import org.h2.store.DataPage;
import org.h2.store.DiskFile; import org.h2.store.DiskFile;
import org.h2.store.FileLister;
import org.h2.store.FileStore; import org.h2.store.FileStore;
import org.h2.store.FileStoreInputStream; import org.h2.store.FileStoreInputStream;
import org.h2.store.LogFile; import org.h2.store.LogFile;
...@@ -112,7 +113,7 @@ public class Recover implements DataHandler { ...@@ -112,7 +113,7 @@ public class Recover implements DataHandler {
} }
private void removePassword(String dir, String db) throws SQLException { private void removePassword(String dir, String db) throws SQLException {
ArrayList list = FileBase.getDatabaseFiles(dir, db, true); ArrayList list = FileLister.getDatabaseFiles(dir, db, true);
for(int i=0; i<list.size(); i++) { for(int i=0; i<list.size(); i++) {
String fileName = (String) list.get(i); String fileName = (String) list.get(i);
if(fileName.endsWith(Constants.SUFFIX_DATA_FILE)) { if(fileName.endsWith(Constants.SUFFIX_DATA_FILE)) {
...@@ -252,7 +253,7 @@ public class Recover implements DataHandler { ...@@ -252,7 +253,7 @@ public class Recover implements DataHandler {
} }
private void process(String dir, String db) throws SQLException { private void process(String dir, String db) throws SQLException {
ArrayList list = FileBase.getDatabaseFiles(dir, db, true); ArrayList list = FileLister.getDatabaseFiles(dir, db, true);
for(int i=0; i<list.size(); i++) { for(int i=0; i<list.size(); i++) {
String fileName = (String) list.get(i); String fileName = (String) list.get(i);
// TODO recover: should create a working SQL script if possible (2 passes) // TODO recover: should create a working SQL script if possible (2 passes)
...@@ -341,6 +342,49 @@ public class Recover implements DataHandler { ...@@ -341,6 +342,49 @@ public class Recover implements DataHandler {
} }
} }
private void writeLogRecord(PrintWriter writer, DataPage s) {
try {
recordLength = s.readInt();
if(recordLength <= 0) {
writeDataError(writer, "recordLength<0", s.getBytes(), blockCount);
return;
}
Value[] data;
try {
data = new Value[recordLength];
} catch(OutOfMemoryError e) {
writeDataError(writer, "out of memory", s.getBytes(), blockCount);
return;
}
StringBuffer sb = new StringBuffer();
sb.append("// data: ");
for(valueId=0; valueId<recordLength; valueId++) {
try {
Value v = s.readValue();
data[valueId] = v;
if(valueId>0) {
sb.append(", ");
}
sb.append(v.getSQL());
} catch(Exception e) {
writeDataError(writer, "exception " + e, s.getBytes(), blockCount);
continue;
} catch(OutOfMemoryError e) {
writeDataError(writer, "out of memory", s.getBytes(), blockCount);
continue;
}
}
writer.println(sb.toString());
writer.flush();
} catch(IOException e) {
try {
writeDataError(writer, "error: " + e.toString(), s.getBytes(), blockCount);
} catch(IOException e2) {
writeError(writer, e);
}
}
}
private void dumpLog(String fileName) throws SQLException { private void dumpLog(String fileName) throws SQLException {
PrintWriter writer = null; PrintWriter writer = null;
FileStore store = null; FileStore store = null;
...@@ -433,9 +477,11 @@ public class Recover implements DataHandler { ...@@ -433,9 +477,11 @@ public class Recover implements DataHandler {
break; break;
case 'I': case 'I':
writer.println("// insert session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount); writer.println("// insert session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount);
writeLogRecord(writer, s);
break; break;
case 'D': case 'D':
writer.println("// delete session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount); writer.println("// delete session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount);
writeLogRecord(writer, s);
break; break;
default: default:
writer.println("// type?:"+type+" session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount); writer.println("// type?:"+type+" session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount);
......
...@@ -183,7 +183,7 @@ public class IOUtils { ...@@ -183,7 +183,7 @@ public class IOUtils {
} }
off += l; off += l;
} }
return off < 0 ? -1 : off; return off <= 0 ? -1 : off;
} }
public static int readFully(Reader in, char[] buffer, int max) throws IOException { public static int readFully(Reader in, char[] buffer, int max) throws IOException {
...@@ -202,7 +202,7 @@ public class IOUtils { ...@@ -202,7 +202,7 @@ public class IOUtils {
} }
off += l; off += l;
} }
return off < 0 ? -1 : off; return off <= 0 ? -1 : off;
} }
public static Reader getReader(InputStream in) throws SQLException { public static Reader getReader(InputStream in) throws SQLException {
......
...@@ -512,21 +512,12 @@ public class ValueLob extends Value { ...@@ -512,21 +512,12 @@ public class ValueLob extends Value {
try { try {
String s; String s;
if(type == Value.CLOB) { if(type == Value.CLOB) {
if(precision < Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB) {
s = getString(); s = getString();
return StringUtils.quoteStringSQL(s); return StringUtils.quoteStringSQL(s);
} else { } else {
return "READ_CLOB('" + fileName + "', "+precision+")";
// TODO
}
} else {
if(precision < Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB) {
byte[] buff = getBytes(); byte[] buff = getBytes();
s = ByteUtils.convertBytesToString(buff); s = ByteUtils.convertBytesToString(buff);
return "X'" + s + "'"; return "X'" + s + "'";
} else {
return "READ_BLOB('"+ fileName +"', "+precision+")";
}
} }
} catch(SQLException e) { } catch(SQLException e) {
throw Message.convertToInternal(e); throw Message.convertToInternal(e);
......
...@@ -65,6 +65,7 @@ start cmd /k "java org.h2.test.TestAll synth >testSynth.txt" ...@@ -65,6 +65,7 @@ start cmd /k "java org.h2.test.TestAll synth >testSynth.txt"
start cmd /k "java org.h2.test.TestAll all >testAll.txt" start cmd /k "java org.h2.test.TestAll all >testAll.txt"
start cmd /k "java org.h2.test.TestAll random >testRandom.txt" start cmd /k "java org.h2.test.TestAll random >testRandom.txt"
start cmd /k "java org.h2.test.TestAll btree >testBtree.txt" start cmd /k "java org.h2.test.TestAll btree >testBtree.txt"
start cmd /k "java org.h2.test.TestAll halt >testHalt.txt"
Test for hot spots: Test for hot spots:
java -agentlib:yjpagent=sampling,noj2ee,dir=C:\temp\Snapshots org.h2.test.bench.TestPerformance -init -db 1 java -agentlib:yjpagent=sampling,noj2ee,dir=C:\temp\Snapshots org.h2.test.bench.TestPerformance -init -db 1
...@@ -86,7 +87,95 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2 ...@@ -86,7 +87,95 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
TestAll test = new TestAll(); TestAll test = new TestAll();
test.printSystem(); test.printSystem();
// improve TestHalt // run TestHalt
// SELECT (SELECT true)+1 GROUP BY 1;
// DROP TABLE IF EXISTS TESTA, TESTB;
// CREATE TABLE TESTA(ID IDENTITY);
// CREATE TABLE TESTB(ID IDENTITY);
// @LOOP 4 INSERT INTO TESTA() VALUES();
// @LOOP 4 INSERT INTO TESTB() VALUES();
// SELECT TESTA.ID A, TESTB.ID B FROM TESTA, TESTB ORDER BY TESTA.ID, TESTB.ID;
// script.sql /
// UPDATE Supplier_Original_Input SET global_duns = parent_duns
// INSERT INTO SupplierBase (ID, duns, subscriber, watch_list, name, city, state, country, global_duns, Feed, FeedNo, frequency) SELECT ID, duns, subscriber, watch_list, name,city, state, country,global_duns, Feed, FeedNo,frequency FROM Supplier_Original_Input
// number of rows in supplier_original_input is 354704.
// time taken for executing the update statement is 700 seconds.
// deebee.tar.gz
// WHERE FLAG does not use index, but WHERE FLAG=TRUE does
//
// drop table test;
// CREATE TABLE test (id int, flag BIT NOT NULL);
// CREATE INDEX idx_flag ON test(flag);
// CREATE INDEX idx_id ON test(id);
// insert into test values(1, false), (2, true), (3, false), (4, true);
// ALTER TABLE test ALTER COLUMN id SELECTIVITY 100;
// ALTER TABLE test ALTER COLUMN flag SELECTIVITY 1;
// EXPLAIN SELECT * FROM test WHERE id=2 AND flag=true;
// EXPLAIN SELECT * FROM test WHERE id between 2 and 3 AND flag=true;
// EXPLAIN SELECT * FROM test WHERE id=2 AND flag;
//
// ALTER TABLE test ALTER COLUMN id SELECTIVITY 1;
// ALTER TABLE test ALTER COLUMN flag SELECTIVITY 100;
// EXPLAIN SELECT * FROM test WHERE id=2 AND flag=true;
// EXPLAIN SELECT * FROM test WHERE id between 2 and 3 AND flag=true;
// EXPLAIN SELECT * FROM test WHERE id=2 AND flag;
// DROP VIEW IF EXISTS TEST_REC;
// DROP VIEW IF EXISTS TEST_2;
// DROP TABLE IF EXISTS TEST;
//
// CREATE TABLE TEST(ID INT PRIMARY KEY, PARENT INT, NAME VARCHAR(255));
// INSERT INTO TEST VALUES(1, NULL, 'Root');
// INSERT INTO TEST VALUES(2, 1, 'Plant');
// INSERT INTO TEST VALUES(3, 1, 'Animal');
// INSERT INTO TEST VALUES(4, 2, 'Tree');
// INSERT INTO TEST VALUES(5, 2, 'Flower');
// INSERT INTO TEST VALUES(6, 3, 'Elephant');
// INSERT INTO TEST VALUES(7, 3, 'Dog');
//
// CREATE FORCE VIEW TEST_2(ID, PARENT, NAME) AS SELECT ID, PARENT, NAME FROM TEST_REC;
//
// CREATE FORCE VIEW TEST_REC(ID, PARENT, NAME) AS
// SELECT ID, PARENT, NAME FROM TEST T
// WHERE PARENT IS NULL
// UNION ALL
// SELECT T.ID, T.PARENT, T.NAME
// FROM TEST T, TEST_2 R
// WHERE 1=0 AND T.PARENT=R.ID;
//
// SELECT * FROM TEST_REC;
// DROP VIEW IF EXISTS TEST_REC;
// DROP VIEW IF EXISTS TEST_2;
// DROP TABLE IF EXISTS TEST;
//
// CREATE TABLE TEST(ID INT PRIMARY KEY, PARENT INT, NAME VARCHAR(255));
// INSERT INTO TEST VALUES(1, NULL, 'Root');
// INSERT INTO TEST VALUES(2, 1, 'Plant');
// INSERT INTO TEST VALUES(3, 1, 'Animal');
// INSERT INTO TEST VALUES(4, 2, 'Tree');
// INSERT INTO TEST VALUES(5, 2, 'Flower');
// INSERT INTO TEST VALUES(6, 3, 'Elephant');
// INSERT INTO TEST VALUES(7, 3, 'Dog');
//
// CREATE VIEW RECURSIVE TEST_REC(ID, PARENT, NAME, LEVEL) AS
// SELECT ID, PARENT, NAME, 0 FROM TEST T
// WHERE PARENT IS NULL
// UNION ALL
// SELECT T.ID, T.PARENT, T.NAME, CAST(R.LEVEL AS INT)+1
// FROM TEST T, TEST_REC R
// WHERE T.PARENT=R.ID;
//
// SELECT * FROM TEST_REC;
// TODO backup : lobs are not backed up // TODO backup : lobs are not backed up
// DROP TABLE IF EXISTS TEST; // DROP TABLE IF EXISTS TEST;
......
...@@ -10,8 +10,8 @@ import java.sql.PreparedStatement; ...@@ -10,8 +10,8 @@ import java.sql.PreparedStatement;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.store.FileLister;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.FileBase;
public class TestLogFile extends TestBase { public class TestLogFile extends TestBase {
...@@ -23,7 +23,7 @@ public class TestLogFile extends TestBase { ...@@ -23,7 +23,7 @@ public class TestLogFile extends TestBase {
conn.close(); conn.close();
} }
long length = 0; long length = 0;
ArrayList files = FileBase.getDatabaseFiles(BASE_DIR, "logfile", false); ArrayList files = FileLister.getDatabaseFiles(BASE_DIR, "logfile", false);
checkSmaller(files.size(), maxFiles+2); checkSmaller(files.size(), maxFiles+2);
for(int i=0; i<files.size(); i++) { for(int i=0; i<files.size(); i++) {
String fileName = (String) files.get(i); String fileName = (String) files.get(i);
......
...@@ -15,8 +15,8 @@ import java.util.Random; ...@@ -15,8 +15,8 @@ import java.util.Random;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcConnection;
import org.h2.store.FileLister;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.FileBase;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
...@@ -164,7 +164,7 @@ public class TestPowerOff extends TestBase { ...@@ -164,7 +164,7 @@ public class TestPowerOff extends TestBase {
// expected // expected
} }
boolean deleted = false; boolean deleted = false;
ArrayList files = FileBase.getDatabaseFiles(dir, dbName, false); ArrayList files = FileLister.getDatabaseFiles(dir, dbName, false);
for(int i=0; i<files.size(); i++) { for(int i=0; i<files.size(); i++) {
String fileName = (String) files.get(i); String fileName = (String) files.get(i);
if(fileName.endsWith(Constants.SUFFIX_INDEX_FILE)) { if(fileName.endsWith(Constants.SUFFIX_INDEX_FILE)) {
......
...@@ -10,8 +10,8 @@ import java.sql.SQLException; ...@@ -10,8 +10,8 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.store.FileLister;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.FileBase;
public class TestReadOnly extends TestBase { public class TestReadOnly extends TestBase {
...@@ -52,7 +52,7 @@ public class TestReadOnly extends TestBase { ...@@ -52,7 +52,7 @@ public class TestReadOnly extends TestBase {
} }
private void setReadOnly() throws SQLException { private void setReadOnly() throws SQLException {
ArrayList list = FileBase.getDatabaseFiles(TestBase.BASE_DIR, "readonly", true); ArrayList list = FileLister.getDatabaseFiles(TestBase.BASE_DIR, "readonly", true);
for(int i=0; i<list.size(); i++) { for(int i=0; i<list.size(); i++) {
String fileName = (String) list.get(i); String fileName = (String) list.get(i);
File file = new File(fileName); File file = new File(fileName);
......
...@@ -18,6 +18,7 @@ import java.util.LinkedList; ...@@ -18,6 +18,7 @@ import java.util.LinkedList;
import java.util.Random; import java.util.Random;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.Backup;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
...@@ -29,6 +30,10 @@ public abstract class TestHalt extends TestBase { ...@@ -29,6 +30,10 @@ public abstract class TestHalt extends TestBase {
protected int operations, flags, value; protected int operations, flags, value;
protected Connection conn; protected Connection conn;
protected Random random = new Random(); protected Random random = new Random();
private int errorId;
private int sequenceId;
private static final String DATABASE_NAME = "halt";
private static final String TRACE_FILE_NAME = BASE_DIR + "/haltTrace.trace.db";
abstract void testInit() throws Exception; abstract void testInit() throws Exception;
abstract void testCheckAfterCrash() throws Exception; abstract void testCheckAfterCrash() throws Exception;
...@@ -52,7 +57,7 @@ public abstract class TestHalt extends TestBase { ...@@ -52,7 +57,7 @@ public abstract class TestHalt extends TestBase {
Connection getConnection() throws Exception { Connection getConnection() throws Exception {
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
return DriverManager.getConnection("jdbc:h2:test", "sa", "sa"); return DriverManager.getConnection("jdbc:h2:" + BASE_DIR + "/halt", "sa", "sa");
} }
protected void start(String[] args) throws Exception { protected void start(String[] args) throws Exception {
...@@ -67,36 +72,40 @@ public abstract class TestHalt extends TestBase { ...@@ -67,36 +72,40 @@ public abstract class TestHalt extends TestBase {
} }
private void runRandom() throws Exception { private void runRandom() throws Exception {
log("connecting", null);
connect(); connect();
try { try {
log("connected, operations:" + operations + " flags:" + flags + " value:" + value, null); traceOperation("connected, operations:" + operations + " flags:" + flags + " value:" + value);
appStart(); appStart();
System.out.println("READY"); System.out.println("READY");
System.out.println("READY"); System.out.println("READY");
System.out.println("READY"); System.out.println("READY");
appRun(); appRun();
log("done", null); traceOperation("done");
} catch(Exception e) { } catch(Exception e) {
log("run", e); trace("run", e);
} }
disconnect(); disconnect();
} }
private void connect() throws Exception { private void connect() throws Exception {
try { try {
traceOperation("connecting");
conn = getConnection(); conn = getConnection();
} catch(Exception e) { } catch(Exception e) {
log("connect", e); trace("connect", e);
e.printStackTrace(); e.printStackTrace();
throw e; throw e;
} }
} }
protected void log(String s, Exception e) { protected void traceOperation(String s) {
trace(s, null);
}
protected void trace(String s, Exception e) {
FileWriter writer = null; FileWriter writer = null;
try { try {
writer = new FileWriter("log.txt", true); writer = new FileWriter(TRACE_FILE_NAME, true);
PrintWriter w = new PrintWriter(writer); PrintWriter w = new PrintWriter(writer);
s = dateFormat.format(new Date()) + ": " + s; s = dateFormat.format(new Date()) + ": " + s;
w.println(s); w.println(s);
...@@ -111,12 +120,17 @@ public abstract class TestHalt extends TestBase { ...@@ -111,12 +120,17 @@ public abstract class TestHalt extends TestBase {
} }
private void runTest() throws Exception { private void runTest() throws Exception {
DeleteDbFiles.execute(null, "test", true); traceOperation("delete database -----------------------------");
new File("log.txt").delete(); DeleteDbFiles.execute(BASE_DIR, DATABASE_NAME, true);
new File(TRACE_FILE_NAME).delete();
connect(); connect();
testInit(); testInit();
disconnect(); disconnect();
for(int i=0; i<10; i++) { for(int i=0; i<10; i++) {
traceOperation("backing up " + sequenceId);
Backup.backupFiles(BASE_DIR + "/haltSeq"+ sequenceId + ".zip", BASE_DIR, null);
sequenceId++;
// int operations = OP_INSERT; // int operations = OP_INSERT;
// OP_DELETE = 1, OP_UPDATE = 2, OP_SELECT = 4; // OP_DELETE = 1, OP_UPDATE = 2, OP_SELECT = 4;
// int flags = FLAG_NODELAY; // int flags = FLAG_NODELAY;
...@@ -126,7 +140,7 @@ public abstract class TestHalt extends TestBase { ...@@ -126,7 +140,7 @@ public abstract class TestHalt extends TestBase {
// String classPath = "-cp .;D:/data/java/hsqldb.jar;D:/data/java/derby.jar"; // String classPath = "-cp .;D:/data/java/hsqldb.jar;D:/data/java/derby.jar";
String classPath = ""; String classPath = "";
String command = "java " + classPath + " " + getClass().getName() + " " + operations + " " + flags + " " + value; String command = "java " + classPath + " " + getClass().getName() + " " + operations + " " + flags + " " + value;
log("start: " + command); traceOperation("start: " + command);
Process p = Runtime.getRuntime().exec(command); Process p = Runtime.getRuntime().exec(command);
InputStream in = p.getInputStream(); InputStream in = p.getInputStream();
OutputCatcher catcher = new OutputCatcher(in); OutputCatcher catcher = new OutputCatcher(in);
...@@ -135,28 +149,39 @@ public abstract class TestHalt extends TestBase { ...@@ -135,28 +149,39 @@ public abstract class TestHalt extends TestBase {
if(s == null) { if(s == null) {
throw new IOException("No reply from process"); throw new IOException("No reply from process");
} else if(s.startsWith("READY")) { } else if(s.startsWith("READY")) {
log("got reply: " + s); traceOperation("got reply: " + s);
} }
testWaitAfterAppStart(); testWaitAfterAppStart();
p.destroy(); p.destroy();
try {
traceOperation("backing up " + sequenceId);
Backup.backupFiles(BASE_DIR + "/haltSeq"+ sequenceId + ".zip", BASE_DIR, null);
// new File(BASE_DIR + "/haltSeq" + (sequenceId-20) + ".zip").delete();
connect(); connect();
testCheckAfterCrash(); testCheckAfterCrash();
} catch(Exception e) {
File zip = new File(BASE_DIR + "/haltSeq"+ sequenceId + ".zip");
File zipId = new File(BASE_DIR + "/haltSeq" + sequenceId + "-" + errorId + ".zip");
zip.renameTo(zipId);
printTime("ERROR: " + sequenceId + " " + errorId + " " + e.toString());
e.printStackTrace();
errorId++;
} finally {
sequenceId++;
disconnect(); disconnect();
} }
} }
}
protected void disconnect() { protected void disconnect() {
try { try {
traceOperation("disconnect");
conn.close(); conn.close();
} catch(Exception e) { } catch(Exception e) {
log("disconnect", e); trace("disconnect", e);
} }
} }
private void log(String string) {
System.out.println(string);
}
private static class OutputCatcher extends Thread { private static class OutputCatcher extends Thread {
private InputStream in; private InputStream in;
private LinkedList list = new LinkedList(); private LinkedList list = new LinkedList();
......
...@@ -17,22 +17,27 @@ public class TestHaltApp extends TestHalt { ...@@ -17,22 +17,27 @@ public class TestHaltApp extends TestHalt {
new TestHaltApp().start(args); new TestHaltApp().start(args);
} }
private void execute(Statement stat, String sql) throws SQLException {
traceOperation("execute: " + sql);
stat.execute(sql);
}
protected void testInit() throws SQLException { protected void testInit() throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
try { try {
stat.execute("DROP TABLE TEST"); execute(stat, "DROP TABLE TEST");
} catch(SQLException e) { } catch(SQLException e) {
// ignore // ignore
} }
// stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR(255))"); // stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR(255))");
for(int i=0; i< 20; i++) { for(int i=0; i< 20; i++) {
stat.execute("DROP TABLE IF EXISTS TEST" + i); execute(stat, "DROP TABLE IF EXISTS TEST" + i);
stat.execute("CREATE TABLE TEST"+i +"(ID INT PRIMARY KEY, NAME VARCHAR(255))"); execute(stat, "CREATE TABLE TEST"+i +"(ID INT PRIMARY KEY, NAME VARCHAR(255))");
} }
for(int i=0; i< 20; i+=2) { for(int i=0; i< 20; i+=2) {
stat.execute("DROP TABLE TEST" + i); execute(stat, "DROP TABLE TEST" + i);
} }
stat.execute("CREATE TABLE TEST(ID BIGINT GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR(255), DATA CLOB)"); execute(stat, "CREATE TABLE TEST(ID BIGINT GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR(255), DATA CLOB)");
} }
protected void testWaitAfterAppStart() throws Exception { protected void testWaitAfterAppStart() throws Exception {
...@@ -47,6 +52,7 @@ public class TestHaltApp extends TestHalt { ...@@ -47,6 +52,7 @@ public class TestHaltApp extends TestHalt {
int count = rs.getInt(1); int count = rs.getInt(1);
System.out.println("count: " + count); System.out.println("count: " + count);
if(count % 2 == 1) { if(count % 2 == 1) {
traceOperation("row count: " + count);
throw new Exception("Unexpected odd row count"); throw new Exception("Unexpected odd row count");
} }
} }
...@@ -54,17 +60,18 @@ public class TestHaltApp extends TestHalt { ...@@ -54,17 +60,18 @@ public class TestHaltApp extends TestHalt {
protected void appStart() throws SQLException { protected void appStart() throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
if((flags & FLAG_NO_DELAY) != 0) { if((flags & FLAG_NO_DELAY) != 0) {
stat.execute("SET WRITE_DELAY 0"); execute(stat, "SET WRITE_DELAY 0");
stat.execute("SET MAX_LOG_SIZE 1"); execute(stat, "SET MAX_LOG_SIZE 1");
} }
ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST"); ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
rs.next(); rs.next();
rowCount = rs.getInt(1); rowCount = rs.getInt(1);
log("rows: " + rowCount, null); trace("rows: " + rowCount, null);
} }
protected void appRun() throws SQLException { protected void appRun() throws Exception {
conn.setAutoCommit(false); conn.setAutoCommit(false);
traceOperation("setAutoCommit false");
int rows = 10000 + value; int rows = 10000 + value;
PreparedStatement prepInsert = conn.prepareStatement("INSERT INTO TEST(NAME, DATA) VALUES('Hello World', ?)"); PreparedStatement prepInsert = conn.prepareStatement("INSERT INTO TEST(NAME, DATA) VALUES('Hello World', ?)");
PreparedStatement prepUpdate = conn.prepareStatement("UPDATE TEST SET NAME = 'Hallo Welt', DATA = ? WHERE ID = ?"); PreparedStatement prepUpdate = conn.prepareStatement("UPDATE TEST SET NAME = 'Hallo Welt', DATA = ? WHERE ID = ?");
...@@ -72,37 +79,53 @@ public class TestHaltApp extends TestHalt { ...@@ -72,37 +79,53 @@ public class TestHaltApp extends TestHalt {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
if((operations & OP_INSERT) != 0) { if((operations & OP_INSERT) != 0) {
if((flags & FLAG_LOBS) != 0) { if((flags & FLAG_LOBS) != 0) {
prepInsert.setString(1, getRandomString(random.nextInt(200))); String s = getRandomString(random.nextInt(200));
prepInsert.setString(1, s);
traceOperation("insert " + s);
prepInsert.execute(); prepInsert.execute();
} else { } else {
stat.execute("INSERT INTO TEST(NAME) VALUES('Hello World')"); execute(stat, "INSERT INTO TEST(NAME) VALUES('Hello World')");
} }
ResultSet rs = stat.getGeneratedKeys();
rs.next();
int key = rs.getInt(1);
traceOperation("inserted key: " + key);
rowCount++; rowCount++;
} }
if((operations & OP_UPDATE) != 0) { if((operations & OP_UPDATE) != 0) {
if((flags & FLAG_LOBS) != 0) { if((flags & FLAG_LOBS) != 0) {
prepUpdate.setString(1, getRandomString(random.nextInt(200))); String s = getRandomString(random.nextInt(200));
prepUpdate.setInt(2, random.nextInt(rowCount+1)); prepUpdate.setString(1, s);
int x = random.nextInt(rowCount+1);
prepUpdate.setInt(2, x);
traceOperation("update " + s + " " + x);
prepUpdate.execute(); prepUpdate.execute();
} else { } else {
stat.execute("UPDATE TEST SET VALUE = 'Hallo Welt' WHERE ID = " + random.nextInt(rowCount+1)); int x = random.nextInt(rowCount+1);
execute(stat, "UPDATE TEST SET VALUE = 'Hallo Welt' WHERE ID = " + x);
} }
} }
if((operations & OP_DELETE) != 0) { if((operations & OP_DELETE) != 0) {
int uc = stat.executeUpdate("DELETE FROM TEST WHERE ID = " + random.nextInt(rowCount+1)); int x = random.nextInt(rowCount+1);
rowCount-=uc; traceOperation("deleting " + x);
} int uc = stat.executeUpdate("DELETE FROM TEST WHERE ID = " + x);
log("rows now: " + rowCount, null); traceOperation("updated: " + uc);
rowCount -= uc;
}
traceOperation("rowCount " + rowCount);
trace("rows now: " + rowCount, null);
if(rowCount % 2 == 0) { if(rowCount % 2 == 0) {
traceOperation("commit " + rowCount);
conn.commit(); conn.commit();
log("committed: " + rowCount, null); trace("committed: " + rowCount, null);
} }
if((flags & FLAG_NO_DELAY) != 0) { if((flags & FLAG_NO_DELAY) != 0) {
if(random.nextInt(100) == 0) { if(random.nextInt(100) == 0) {
stat.execute("CHECKPOINT"); execute(stat, "CHECKPOINT");
} }
} }
} }
traceOperation("rollback");
conn.rollback(); conn.rollback();
} }
......
...@@ -10,8 +10,8 @@ import java.sql.PreparedStatement; ...@@ -10,8 +10,8 @@ import java.sql.PreparedStatement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Random; import java.util.Random;
import org.h2.store.FileLister;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.FileBase;
public class TestKillProcess { public class TestKillProcess {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
...@@ -32,7 +32,7 @@ public class TestKillProcess { ...@@ -32,7 +32,7 @@ public class TestKillProcess {
for(int i=0; ; i++) { for(int i=0; ; i++) {
long t = System.currentTimeMillis(); long t = System.currentTimeMillis();
if(t > time + 1000) { if(t > time + 1000) {
ArrayList list = FileBase.getDatabaseFiles(BASE_DIR, "kill", true); ArrayList list = FileLister.getDatabaseFiles(BASE_DIR, "kill", true);
System.out.println("inserting... i:"+i+" d:" + d+" files:" + list.size()); System.out.println("inserting... i:"+i+" d:" + d+" files:" + list.size());
time = t; time = t;
} }
......
--- special grammar and test cases --------------------------------------------------------------------------------------------- --- special grammar and test cases ---------------------------------------------------------------------------------------------
CREATE TABLE test (family_name VARCHAR_IGNORECASE(63) NOT NULL);
> ok
INSERT INTO test VALUES('Smith'), ('de Smith'), ('el Smith'), ('von Smith');
> update count: 4
SELECT * FROM test WHERE family_name IN ('de Smith', 'Smith');
> FAMILY_NAME
> -----------
> Smith
> de Smith
> rows: 2
SELECT * FROM test WHERE family_name BETWEEN 'D' AND 'T';
> FAMILY_NAME
> -----------
> Smith
> de Smith
> el Smith
> rows: 3
CREATE INDEX family_name ON test(family_name);
> ok
SELECT * FROM test WHERE family_name IN ('de Smith', 'Smith');
> FAMILY_NAME
> -----------
> Smith
> de Smith
> rows: 2
drop table test;
> ok
create memory table test(id int primary key, data clob);
> ok
insert into test values(1, 'abc' || space(20));
> update count: 1
script nopasswords nosettings blocksize 10;
> SCRIPT
> ------------------------------------------------------------------------------------------------------------------
> -- 1 = SELECT COUNT(*) FROM PUBLIC.TEST
> CALL SYSTEM_COMBINE_BLOB(-1)
> CREATE ALIAS IF NOT EXISTS SYSTEM_COMBINE_BLOB FOR "org.h2.command.dml.Script.combineBlob"
> CREATE ALIAS IF NOT EXISTS SYSTEM_COMBINE_CLOB FOR "org.h2.command.dml.Script.combineClob"
> CREATE MEMORY TABLE PUBLIC.TEST( ID INT NOT NULL, DATA CLOB )
> CREATE PRIMARY KEY ON PUBLIC.TEST(ID)
> CREATE TABLE IF NOT EXISTS SYSTEM_LOB_STREAM(ID INT, PART INT, CDATA VARCHAR, BDATA BINARY, PRIMARY KEY(ID, PART))
> CREATE USER IF NOT EXISTS SA PASSWORD '' ADMIN
> DROP ALIAS IF EXISTS SYSTEM_COMBINE_BLOB
> DROP ALIAS IF EXISTS SYSTEM_COMBINE_CLOB
> DROP TABLE IF EXISTS SYSTEM_LOB_STREAM
> INSERT INTO PUBLIC.TEST(ID, DATA) VALUES(1, SYSTEM_COMBINE_CLOB(0))
> INSERT INTO SYSTEM_LOB_STREAM VALUES(0, 0, 'abc ', NULL);
> INSERT INTO SYSTEM_LOB_STREAM VALUES(0, 1, ' ', NULL);
> INSERT INTO SYSTEM_LOB_STREAM VALUES(0, 2, ' ', NULL);
> rows: 15
drop table test;
> ok
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255));
> ok
INSERT INTO TEST VALUES(1, 'Hello'), (2, 'World');
> update count: 2
SELECT DISTINCT * FROM TEST ORDER BY ID;
> ID NAME
> -- -----
> 1 Hello
> 2 World
> rows (ordered): 2
DROP TABLE TEST;
> ok
create table Foo (A varchar(20), B integer); create table Foo (A varchar(20), B integer);
> ok > ok
......
SELECT (SELECT true)+1 GROUP BY 1;
> 2;
create table FOO (ID int, A number(18, 2)); create table FOO (ID int, A number(18, 2));
insert into FOO (ID, A) values (1, 10.0), (2, 20.0); insert into FOO (ID, A) values (1, 10.0), (2, 20.0);
select SUM (CASE when ID=1 then A ELSE 0 END) col0 from Foo; select SUM (CASE when ID=1 then A ELSE 0 END) col0 from Foo;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论