提交 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.
<h2>Change Log</h2>
<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>
<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.
</li><li>Support curtimestamp (like curtime, curdate)
</li><li>Support ANALYZE {TABLE|INDEX} tablename COMPUTE|ESTIMATE|DELETE STATISTICS ptnOption options
</li><li>Support Sequoia (Continuent.org)
</li><li>Dynamic length numbers / special methods for DataPage.writeByte / writeShort / Ronni Nielsen
</li><li>Pluggable tracing system, ThreadPool, (AvalonDB / deebee / Paul Hammant)
</li></ul>
<h3>Not Planned</h3>
......
......@@ -105,6 +105,7 @@ import org.h2.table.Column;
import org.h2.table.FunctionTable;
import org.h2.table.RangeTable;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.TableFilter;
import org.h2.table.TableView;
import org.h2.util.ByteUtils;
......@@ -361,6 +362,11 @@ public class Parser {
c = parserCall();
}
break;
case 'W':
if(readIf("WITH")) {
c = parserWith();
}
break;
default:
// TODO exception: unknown command
throw getSyntaxError();
......@@ -1065,7 +1071,7 @@ public class Parser {
command.init();
return command;
}
private Query parseQueryWithParams() throws SQLException {
int paramIndex = parameters.size();
Query command = parseSelectUnion();
......@@ -3147,19 +3153,58 @@ public class Parser {
command.setJavaClassMethod(readUniqueIdentifier());
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 {
int test;
TableData recursiveTable = null;
boolean recursive = readIf("RECURSIVE");
boolean ifNotExists = readIfNoExists();
String viewName = readIdentifierWithSchema();
CreateView command = new CreateView(session, getSchema());
command.setViewName(viewName);
command.setIfNotExists(ifNotExists);
String select = StringCache.getNew(sqlCommand.substring(parseIndex));
command.setComment(readCommentIf());
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);
}
}
String select = StringCache.getNew(sqlCommand.substring(parseIndex));
read("AS");
try {
Query query = parseSelect();
......@@ -3172,6 +3217,11 @@ public class Parser {
throw e;
}
}
if(recursiveTable != null) {
session.removeLocalTempTable(recursiveTable);
}
return command;
}
......
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.command.ddl;
import java.sql.SQLException;
import org.h2.command.dml.Query;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.TableView;
public class CreateView extends SchemaCommand {
private Query select;
private String viewName;
private boolean ifNotExists;
private String selectSQL;
private String[] columnNames;
private String comment;
public CreateView(Session session, Schema schema) {
super(session, schema);
}
public void setViewName(String name) {
viewName = name;
}
public void setSelect(Query select) {
this.select = select;
}
public int update() throws SQLException {
// TODO rights: what rights are required to create a view?
session.commit();
Database db = session.getDatabase();
if(getSchema().findTableOrView(session, viewName)!=null) {
if (ifNotExists) {
return 0;
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.command.ddl;
import java.sql.SQLException;
import org.h2.command.dml.Query;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.TableView;
public class CreateView extends SchemaCommand {
private Query select;
private String viewName;
private boolean ifNotExists;
private String selectSQL;
private String[] columnNames;
private String comment;
public CreateView(Session session, Schema schema) {
super(session, schema);
}
public void setViewName(String name) {
viewName = name;
}
public void setSelect(Query select) {
this.select = select;
}
public int update() throws SQLException {
// TODO rights: what rights are required to create a view?
session.commit();
Database db = session.getDatabase();
if(getSchema().findTableOrView(session, viewName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.VIEW_ALREADY_EXISTS_1,
viewName);
}
int id = getObjectId(true, true);
String querySQL;
if(select == null) {
querySQL = selectSQL;
} else {
querySQL = select.getSQL();
}
throw Message.getSQLException(Message.VIEW_ALREADY_EXISTS_1,
viewName);
TableView view = new TableView(getSchema(), id, viewName, querySQL, null, columnNames, session);
view.setComment(comment);
db.addSchemaObject(session, view);
return 0;
}
int id = getObjectId(true, true);
String querySQL;
if(select == null) {
querySQL = selectSQL;
} else {
querySQL = select.getSQL();
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
TableView view = new TableView(getSchema(), id, viewName, querySQL, null, columnNames, session);
view.setComment(comment);
db.addSchemaObject(session, view);
return 0;
}
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
public void setSelectSQL(String selectSQL) {
this.selectSQL = selectSQL;
}
public void setColumnNames(String[] cols) {
this.columnNames = cols;
}
public void setComment(String comment) {
this.comment = comment;
public void setSelectSQL(String selectSQL) {
this.selectSQL = selectSQL;
}
public void setColumnNames(String[] cols) {
this.columnNames = cols;
}
public void setComment(String comment) {
this.comment = comment;
}
}
}
......@@ -19,9 +19,9 @@ import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.store.DiskFile;
import org.h2.store.FileLister;
import org.h2.store.LogFile;
import org.h2.store.LogSystem;
import org.h2.tools.FileBase;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.ObjectArray;
......@@ -72,7 +72,7 @@ public class Backup extends Prepared {
backupFile(out, fn);
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++) {
fn = (String) fileList.get(i);
if(fn.endsWith(Constants.SUFFIX_HASH_FILE) || fn.endsWith(Constants.SUFFIX_LOB_FILE)) {
......
......@@ -473,7 +473,7 @@ public class Select extends Query {
isQuickQuery = isEverything(optimizable);
}
cost = preparePlan();
if(sort != null && !isQuickQuery && !isGroupQuery) {
if(sort != null && !isQuickQuery && !isGroupQuery && !distinct) {
Index index = getSortIndex();
Index current = topTableFilter.getIndex();
if(index != null && (current.indexType.isScan() || current == index)) {
......@@ -537,6 +537,9 @@ public class Select extends Query {
}
public String getPlan() {
if(topTableFilter == null) {
return sql;
}
StringBuffer buff = new StringBuffer();
Expression[] exprList = new Expression[expressions.size()];
expressions.toArray(exprList);
......
......@@ -302,4 +302,11 @@ public class SelectUnion extends Query {
return left.isReadOnly() && right.isReadOnly();
}
public Query getLeftQuery() {
return left;
}
public Query getRightQuery() {
return right;
}
}
......@@ -73,8 +73,8 @@ package org.h2.engine;
*/
public class Constants {
public static final int BUILD_ID = 41;
private static final String BUILD = "2007-01-30";
public static final int BUILD_ID = 42;
private static final String BUILD = "2007-02-06";
public static final int VERSION_MAJOR = 1;
public static final int VERSION_MINOR = 0;
......
......@@ -95,6 +95,7 @@ public class ConditionIn extends Condition {
return expr;
}
if(Constants.OPTIMIZE_IN) {
int dataType = left.getType();
ExpressionVisitor independent = ExpressionVisitor.get(ExpressionVisitor.INDEPENDENT);
independent.queryLevel = queryLevel;
if(areAllValues(independent)) {
......@@ -103,6 +104,7 @@ public class ConditionIn extends Condition {
for(int i=0; i<values.size(); i++) {
Expression e = (Expression) values.get(i);
Value v = e.getValue(session);
v = v.convertTo(dataType);
values.set(i, ValueExpression.get(v));
if(min == null || min.compareTo(v, mode) > 0) {
min = v;
......
......@@ -7,6 +7,7 @@ package org.h2.index;
import java.sql.SQLException;
import org.h2.command.dml.Query;
import org.h2.command.dml.SelectUnion;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
......@@ -35,10 +36,21 @@ public class ViewIndex extends Index {
private long lastEvaluated;
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));
this.querySQL = querySQL;
this.originalParameters = originalParameters;
int test;
this.recursive = recursive;
columns = new Column[0];
params = new Parameter[0];
}
public String getPlanSQL() {
......@@ -76,6 +88,9 @@ public class ViewIndex extends Index {
}
public double getCost(Session session, int[] masks) throws SQLException {
if(recursive) {
return 10;
}
IntArray masksArray = new IntArray(masks == null ? new int[0] : masks);
CostElement cachedCost = (CostElement) costCache.get(masksArray);
if(cachedCost != null) {
......@@ -85,11 +100,11 @@ public class ViewIndex extends Index {
}
}
Query query = (Query)session.prepare(querySQL, true);
IntArray paramIndex = new IntArray();
if(masks == null) {
columns = new Column[0];
params = new Parameter[0];
} else {
IntArray paramIndex = new IntArray();
for(int i=0; i<masks.length; i++) {
int mask = masks[i];
if(mask == 0) {
......@@ -110,6 +125,9 @@ public class ViewIndex extends Index {
int comparisonType = getComparisonType(mask);
query.addGlobalCondition(param, idx, comparisonType);
}
if(recursive) {
return 10;
}
String sql = query.getSQL();
query = (Query)session.prepare(sql);
}
......@@ -123,6 +141,35 @@ public class ViewIndex extends Index {
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
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();
for(int i=0; first != null && i<first.getColumnCount(); i++) {
Value v = first.getValue(i);
......
......@@ -2,7 +2,7 @@
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.tools;
package org.h2.store;
import java.sql.SQLException;
import java.util.ArrayList;
......@@ -14,11 +14,7 @@ import org.h2.util.FileUtils;
* @author Thomas
*/
public abstract class FileBase {
protected boolean allFiles() {
return false;
}
public class FileLister {
/**
* Get the list of database files.
......@@ -71,20 +67,4 @@ public abstract class FileBase {
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 {
}
addRowsToIndex(session, buffer, index);
if(Constants.CHECK && remaining != 0) {
throw Message.getInternalError("rowcount remaining=" + remaining);
throw Message.getInternalError("rowcount remaining=" + remaining + " " + getName());
}
} catch(SQLException e) {
try {
......
......@@ -18,6 +18,7 @@ import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.value.Value;
public class TableView extends Table {
......@@ -27,12 +28,15 @@ public class TableView extends Table {
private boolean invalid;
private Query viewQuery;
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 {
super(schema, id, name, false);
this.querySQL = querySQL;
this.columnNames = columnNames;
index = new ViewIndex(this, querySQL, params);
index = new ViewIndex(this, querySQL, params, recursive);
initColumnsAndTables(session);
}
......@@ -77,6 +81,18 @@ public class TableView extends Table {
tables = new ObjectArray();
cols = new Column[0];
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);
}
......
......@@ -5,15 +5,24 @@
package org.h2.tools;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
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.store.FileLister;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils;
......@@ -152,6 +161,39 @@ public class Backup {
IOUtils.closeSilently(fileWriter);
}
}
/**
* 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;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import org.h2.engine.Database;
import org.h2.message.Message;
import org.h2.security.SHA256;
import org.h2.store.FileLister;
import org.h2.store.FileStore;
import org.h2.util.FileUtils;
/**
* 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 cipher;
private byte[] decrypt;
private byte[] encrypt;
private boolean testRenameOnly;
// TODO security: maybe allow functions in the url
// jdbc:h2:test;action=[decrypt|encrypt|check|reindex|recover|compress...]
......@@ -113,35 +114,36 @@ public class ChangePassword extends FileBase {
change.encrypt = encrypt;
// first, test only if the file can be renamed (to find errors with locked files early)
change.testRenameOnly = true;
change.processFiles(dir, db, !quiet);
// if this worked, the operation will (hopefully) be successful
// 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) {
ArrayList files = FileLister.getDatabaseFiles(dir, db, true);
for (int i = 0; i < files.size(); i++) {
String fileName = (String) files.get(i);
String temp = dir + "/temp.db";
FileUtils.delete(temp);
FileUtils.rename(fileName, temp);
FileUtils.rename(temp, fileName);
}
// 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);
byte[] magic = Database.getMagic(textStorage);
FileStore in;
if(decrypt == null) {
in = FileStore.open(null, fileName, magic);
} else {
boolean textStorage = Database.isTextStorage(fileName, false);
byte[] magic = Database.getMagic(textStorage);
FileStore in;
if(decrypt == null) {
in = FileStore.open(null, fileName, magic);
} else {
in = FileStore.open(null, fileName, magic, cipher, decrypt);
}
in.init();
copy(fileName, textStorage, in, encrypt);
in = FileStore.open(null, fileName, magic, cipher, decrypt);
}
in.init();
copy(fileName, textStorage, in, encrypt);
}
private void copy(String fileName, boolean textStorage, FileStore in, byte[] key) throws SQLException {
......
......@@ -5,8 +5,10 @@
package org.h2.tools;
import java.sql.SQLException;
import java.util.ArrayList;
import org.h2.engine.Constants;
import org.h2.store.FileLister;
import org.h2.util.FileUtils;
/**
......@@ -15,10 +17,8 @@ import org.h2.util.FileUtils;
* @author Thomas
*/
public class DeleteDbFiles extends FileBase {
public class DeleteDbFiles {
private boolean quiet;
private void showUsage() {
System.out.println("java "+getClass().getName()+" [-dir <dir>] [-db <database>] [-quiet]");
}
......@@ -70,20 +70,25 @@ public class DeleteDbFiles extends FileBase {
*/
public static void execute(String dir, String db, boolean quiet) throws SQLException {
DeleteDbFiles delete = new DeleteDbFiles();
delete.quiet = quiet;
delete.processFiles(dir, db, !quiet);
ArrayList files = FileLister.getDatabaseFiles(dir, db, true);
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)) {
FileUtils.tryDelete(fileName);
} else {
FileUtils.delete(fileName);
}
}
protected boolean allFiles() {
return true;
}
}
}
......@@ -32,6 +32,7 @@ import org.h2.security.SHA256;
import org.h2.store.DataHandler;
import org.h2.store.DataPage;
import org.h2.store.DiskFile;
import org.h2.store.FileLister;
import org.h2.store.FileStore;
import org.h2.store.FileStoreInputStream;
import org.h2.store.LogFile;
......@@ -112,7 +113,7 @@ public class Recover implements DataHandler {
}
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++) {
String fileName = (String) list.get(i);
if(fileName.endsWith(Constants.SUFFIX_DATA_FILE)) {
......@@ -252,7 +253,7 @@ public class Recover implements DataHandler {
}
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++) {
String fileName = (String) list.get(i);
// TODO recover: should create a working SQL script if possible (2 passes)
......@@ -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 {
PrintWriter writer = null;
FileStore store = null;
......@@ -433,9 +477,11 @@ public class Recover implements DataHandler {
break;
case 'I':
writer.println("// insert session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount);
writeLogRecord(writer, s);
break;
case 'D':
writer.println("// delete session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount);
writeLogRecord(writer, s);
break;
default:
writer.println("// type?:"+type+" session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount);
......
......@@ -183,7 +183,7 @@ public class IOUtils {
}
off += l;
}
return off < 0 ? -1 : off;
return off <= 0 ? -1 : off;
}
public static int readFully(Reader in, char[] buffer, int max) throws IOException {
......@@ -202,7 +202,7 @@ public class IOUtils {
}
off += l;
}
return off < 0 ? -1 : off;
return off <= 0 ? -1 : off;
}
public static Reader getReader(InputStream in) throws SQLException {
......
......@@ -512,22 +512,13 @@ public class ValueLob extends Value {
try {
String s;
if(type == Value.CLOB) {
if(precision < Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB) {
s = getString();
return StringUtils.quoteStringSQL(s);
} else {
return "READ_CLOB('" + fileName + "', "+precision+")";
// TODO
}
s = getString();
return StringUtils.quoteStringSQL(s);
} else {
if(precision < Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB) {
byte[] buff = getBytes();
s = ByteUtils.convertBytesToString(buff);
return "X'" + s + "'";
} else {
return "READ_BLOB('"+ fileName +"', "+precision+")";
}
}
byte[] buff = getBytes();
s = ByteUtils.convertBytesToString(buff);
return "X'" + s + "'";
}
} catch(SQLException e) {
throw Message.convertToInternal(e);
}
......
......@@ -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 random >testRandom.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:
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
TestAll test = new TestAll();
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
// DROP TABLE IF EXISTS TEST;
......
......@@ -10,8 +10,8 @@ import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.ArrayList;
import org.h2.store.FileLister;
import org.h2.test.TestBase;
import org.h2.tools.FileBase;
public class TestLogFile extends TestBase {
......@@ -23,7 +23,7 @@ public class TestLogFile extends TestBase {
conn.close();
}
long length = 0;
ArrayList files = FileBase.getDatabaseFiles(BASE_DIR, "logfile", false);
ArrayList files = FileLister.getDatabaseFiles(BASE_DIR, "logfile", false);
checkSmaller(files.size(), maxFiles+2);
for(int i=0; i<files.size(); i++) {
String fileName = (String) files.get(i);
......
......@@ -15,8 +15,8 @@ import java.util.Random;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.jdbc.JdbcConnection;
import org.h2.store.FileLister;
import org.h2.test.TestBase;
import org.h2.tools.FileBase;
import org.h2.util.FileUtils;
import org.h2.util.JdbcUtils;
......@@ -164,7 +164,7 @@ public class TestPowerOff extends TestBase {
// expected
}
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++) {
String fileName = (String) files.get(i);
if(fileName.endsWith(Constants.SUFFIX_INDEX_FILE)) {
......
......@@ -10,8 +10,8 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import org.h2.store.FileLister;
import org.h2.test.TestBase;
import org.h2.tools.FileBase;
public class TestReadOnly extends TestBase {
......@@ -52,7 +52,7 @@ public class TestReadOnly extends TestBase {
}
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++) {
String fileName = (String) list.get(i);
File file = new File(fileName);
......
......@@ -18,6 +18,7 @@ import java.util.LinkedList;
import java.util.Random;
import org.h2.test.TestBase;
import org.h2.tools.Backup;
import org.h2.tools.DeleteDbFiles;
import org.h2.util.IOUtils;
......@@ -29,6 +30,10 @@ public abstract class TestHalt extends TestBase {
protected int operations, flags, value;
protected Connection conn;
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 testCheckAfterCrash() throws Exception;
......@@ -52,7 +57,7 @@ public abstract class TestHalt extends TestBase {
Connection getConnection() throws Exception {
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 {
......@@ -67,36 +72,40 @@ public abstract class TestHalt extends TestBase {
}
private void runRandom() throws Exception {
log("connecting", null);
connect();
try {
log("connected, operations:" + operations + " flags:" + flags + " value:" + value, null);
traceOperation("connected, operations:" + operations + " flags:" + flags + " value:" + value);
appStart();
System.out.println("READY");
System.out.println("READY");
System.out.println("READY");
appRun();
log("done", null);
traceOperation("done");
} catch(Exception e) {
log("run", e);
trace("run", e);
}
disconnect();
}
private void connect() throws Exception {
try {
traceOperation("connecting");
conn = getConnection();
} catch(Exception e) {
log("connect", e);
trace("connect", e);
e.printStackTrace();
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;
try {
writer = new FileWriter("log.txt", true);
writer = new FileWriter(TRACE_FILE_NAME, true);
PrintWriter w = new PrintWriter(writer);
s = dateFormat.format(new Date()) + ": " + s;
w.println(s);
......@@ -111,12 +120,17 @@ public abstract class TestHalt extends TestBase {
}
private void runTest() throws Exception {
DeleteDbFiles.execute(null, "test", true);
new File("log.txt").delete();
traceOperation("delete database -----------------------------");
DeleteDbFiles.execute(BASE_DIR, DATABASE_NAME, true);
new File(TRACE_FILE_NAME).delete();
connect();
testInit();
disconnect();
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;
// OP_DELETE = 1, OP_UPDATE = 2, OP_SELECT = 4;
// int flags = FLAG_NODELAY;
......@@ -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 = "";
String command = "java " + classPath + " " + getClass().getName() + " " + operations + " " + flags + " " + value;
log("start: " + command);
traceOperation("start: " + command);
Process p = Runtime.getRuntime().exec(command);
InputStream in = p.getInputStream();
OutputCatcher catcher = new OutputCatcher(in);
......@@ -135,27 +149,38 @@ public abstract class TestHalt extends TestBase {
if(s == null) {
throw new IOException("No reply from process");
} else if(s.startsWith("READY")) {
log("got reply: " + s);
traceOperation("got reply: " + s);
}
testWaitAfterAppStart();
p.destroy();
connect();
testCheckAfterCrash();
disconnect();
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();
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();
}
}
}
protected void disconnect() {
try {
traceOperation("disconnect");
conn.close();
} 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 InputStream in;
......
......@@ -17,22 +17,27 @@ public class TestHaltApp extends TestHalt {
new TestHaltApp().start(args);
}
private void execute(Statement stat, String sql) throws SQLException {
traceOperation("execute: " + sql);
stat.execute(sql);
}
protected void testInit() throws SQLException {
Statement stat = conn.createStatement();
try {
stat.execute("DROP TABLE TEST");
execute(stat, "DROP TABLE TEST");
} catch(SQLException e) {
// ignore
}
// stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR(255))");
for(int i=0; i< 20; i++) {
stat.execute("DROP TABLE IF EXISTS TEST" + i);
stat.execute("CREATE TABLE TEST"+i +"(ID INT PRIMARY KEY, NAME VARCHAR(255))");
execute(stat, "DROP TABLE IF EXISTS TEST" + i);
execute(stat, "CREATE TABLE TEST"+i +"(ID INT PRIMARY KEY, NAME VARCHAR(255))");
}
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 {
......@@ -47,6 +52,7 @@ public class TestHaltApp extends TestHalt {
int count = rs.getInt(1);
System.out.println("count: " + count);
if(count % 2 == 1) {
traceOperation("row count: " + count);
throw new Exception("Unexpected odd row count");
}
}
......@@ -54,17 +60,18 @@ public class TestHaltApp extends TestHalt {
protected void appStart() throws SQLException {
Statement stat = conn.createStatement();
if((flags & FLAG_NO_DELAY) != 0) {
stat.execute("SET WRITE_DELAY 0");
stat.execute("SET MAX_LOG_SIZE 1");
execute(stat, "SET WRITE_DELAY 0");
execute(stat, "SET MAX_LOG_SIZE 1");
}
ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
rs.next();
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);
traceOperation("setAutoCommit false");
int rows = 10000 + value;
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 = ?");
......@@ -72,37 +79,53 @@ public class TestHaltApp extends TestHalt {
Statement stat = conn.createStatement();
if((operations & OP_INSERT) != 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();
} 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++;
}
if((operations & OP_UPDATE) != 0) {
if((flags & FLAG_LOBS) != 0) {
prepUpdate.setString(1, getRandomString(random.nextInt(200)));
prepUpdate.setInt(2, random.nextInt(rowCount+1));
String s = getRandomString(random.nextInt(200));
prepUpdate.setString(1, s);
int x = random.nextInt(rowCount+1);
prepUpdate.setInt(2, x);
traceOperation("update " + s + " " + x);
prepUpdate.execute();
} 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) {
int uc = stat.executeUpdate("DELETE FROM TEST WHERE ID = " + random.nextInt(rowCount+1));
rowCount-=uc;
int x = random.nextInt(rowCount+1);
traceOperation("deleting " + x);
int uc = stat.executeUpdate("DELETE FROM TEST WHERE ID = " + x);
traceOperation("updated: " + uc);
rowCount -= uc;
}
log("rows now: " + rowCount, null);
traceOperation("rowCount " + rowCount);
trace("rows now: " + rowCount, null);
if(rowCount % 2 == 0) {
traceOperation("commit " + rowCount);
conn.commit();
log("committed: " + rowCount, null);
trace("committed: " + rowCount, null);
}
if((flags & FLAG_NO_DELAY) != 0) {
if(random.nextInt(100) == 0) {
stat.execute("CHECKPOINT");
execute(stat, "CHECKPOINT");
}
}
}
traceOperation("rollback");
conn.rollback();
}
......
......@@ -10,8 +10,8 @@ import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.Random;
import org.h2.store.FileLister;
import org.h2.test.TestBase;
import org.h2.tools.FileBase;
public class TestKillProcess {
public static void main(String[] args) throws Exception {
......@@ -32,7 +32,7 @@ public class TestKillProcess {
for(int i=0; ; i++) {
long t = System.currentTimeMillis();
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());
time = t;
}
......
--- 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);
> ok
......
SELECT (SELECT true)+1 GROUP BY 1;
> 2;
create table FOO (ID int, A number(18, 2));
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;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论