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

--no commit message

--no commit message
上级 8bce218b
......@@ -1254,13 +1254,13 @@ while the database is closed. However in some situations (for example after dele
a lot of data in a database), one sometimes wants to shrink the size of the database
(compact a database). Here is a sample function to do this:
<pre>
public static void compact(String dir, String dbName,
String user, String password) throws Exception
String url = "jdbc:h2:" + dbName;
String script = "test.sql";
Backup.execute(url, user, password, script);
DeleteDbFiles.execute(dir, dbName);
RunScript.execute(url, user, password, script);
public static void compact(String dir, String dbName,
String user, String password) throws Exception {
String url = "jdbc:h2:" + dir + "/" + dbName;
String file = "data/test.sql";
Script.execute(url, user, password, file);
DeleteDbFiles.execute(dir, dbName, true);
RunScript.execute(url, user, password, file, null, false);
}
</pre>
See also the sample application org.h2.samples.Compact.
......
......@@ -36,6 +36,18 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3>
<h3>Version 1.0 / 2007-TODO</h3><ul>
<li>Optimization for WHERE NOT(...) and WHERE [NOT] booleanFlagColumn.
This can be disabled using the system property h2.optimizeNot.
</li><li>Optimization for conditions like WHERE A=B AND B=X (A=X is added). This often appears in joins.
This can be disabled using the system property h2.optimizeTwoEquals.
</li><li>Documentation: the source code in 'Compacting a Database' was incorrect. Fixed.
</li><li>In the H2 Console, result sets could not be modified because the default result set type is now forward only.
For H2, now uses scrollable result sets. Also for other databases, but only when the query starts with @EDIT.
</li><li>Views using UNION did not work correctly. Fixed.
</li><li>Function tables did not work with views and EXPLAIN. Fixed.
</li></ul>
<h3>Version 1.0 / 2007-07-12 (Build 55)</h3><ul>
<li>Support for the system property baseDir. This works for embedded databases as well. The setting is supported
by the H2 Console using -Dh2.baseDir or -baseDir
......
......@@ -94,7 +94,7 @@ public class Delete extends Prepared {
if (condition != null) {
condition.mapColumns(tableFilter, 0);
condition = condition.optimize(session);
condition = condition.createIndexConditions(tableFilter);
condition.createIndexConditions(tableFilter);
}
PlanItem item = tableFilter.getBestPlanItem(session);
tableFilter.setPlanItem(item);
......
......@@ -18,7 +18,6 @@ import org.h2.store.UndoLogRecord;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.value.Value;
/**
......@@ -148,7 +147,7 @@ public class Insert extends Prepared {
if(e == null) {
buff.append("DEFAULT");
} else {
buff.append(StringUtils.unEnclose(e.getSQL()));
buff.append(e.getSQL());
}
}
buff.append(')');
......
......@@ -20,7 +20,6 @@ import org.h2.store.UndoLogRecord;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.value.Value;
/**
......@@ -213,7 +212,7 @@ public class Merge extends Prepared {
if(e == null) {
buff.append("DEFAULT");
} else {
buff.append(StringUtils.unEnclose(e.getSQL()));
buff.append(e.getSQL());
}
}
buff.append(')');
......
......@@ -476,7 +476,7 @@ public class Select extends Query {
condition = condition.optimize(session);
for (int j = 0; j < filters.size(); j++) {
TableFilter f = (TableFilter) filters.get(j);
condition = condition.createIndexConditions(f);
condition.createIndexConditions(f);
}
}
if(condition == null && isGroupQuery && groupIndex == null && havingIndex<0 && filters.size()==1) {
......@@ -564,7 +564,7 @@ public class Select extends Query {
buff.append(", ");
}
Expression expr = exprList[i];
buff.append(StringUtils.unEnclose(expr.getSQL()));
buff.append(expr.getSQL());
}
buff.append("\nFROM ");
TableFilter filter = topTableFilter;
......@@ -716,7 +716,6 @@ public class Select extends Query {
}
}
public boolean isEverything(ExpressionVisitor visitor) {
if(visitor.type == ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID) {
for(int i=0; i<filters.size(); i++) {
......
......@@ -131,7 +131,7 @@ public class Update extends Prepared {
Column column = table.getColumn(i);
buff.append(column.getName());
buff.append(" = ");
buff.append(StringUtils.unEnclose(newExpr.getSQL()));
buff.append(newExpr.getSQL());
}
}
if(condition != null) {
......@@ -144,7 +144,7 @@ public class Update extends Prepared {
if (condition != null) {
condition.mapColumns(tableFilter, 0);
condition = condition.optimize(session);
condition = condition.createIndexConditions(tableFilter);
condition.createIndexConditions(tableFilter);
}
for (int i = 0; i < expressions.length; i++) {
Expression expr = expressions[i];
......
......@@ -251,7 +251,9 @@ public class Constants {
public static final int CACHE_SIZE_INDEX_DEFAULT = CACHE_SIZE_DEFAULT >> CACHE_SIZE_INDEX_SHIFT;
public static String BASE_DIR = getStringSetting("h2.baseDir", null);
public static final int DEFAULT_MAX_MEMORY_UNDO = getIntSetting("h2.defaultMaxMemoryUndo", Integer.MAX_VALUE);
public static final boolean OPTIMIZE_NOT = getBooleanSetting("h2.optimizeNot", true);
public static final boolean OPTIMIZE_2_EQUALS = getBooleanSetting("h2.optimizeTwoEquals", true);
public static void setBaseDir(String dir) {
if(!dir.endsWith("/")) {
dir += "/";
......
......@@ -1499,4 +1499,8 @@ public class Database implements DataHandler {
return lobSyncObject;
}
public int getSessionCount() {
return sessions.size();
}
}
......@@ -61,6 +61,8 @@ public class Session implements SessionInterface {
private HashSet unlinkSet;
private int tempViewIndex;
private HashMap procedures;
private static int nextSerialId;
private int serialId = nextSerialId++;
public Session() {
}
......@@ -514,4 +516,8 @@ public class Session implements SessionInterface {
return schemaSearchPath;
}
public int hashCode() {
return serialId;
}
}
......@@ -103,14 +103,14 @@ public class CompareLike extends Condition {
return esc;
}
public Expression createIndexConditions(TableFilter filter) throws SQLException {
public void createIndexConditions(TableFilter filter) throws SQLException {
Session session = filter.getSession();
if(!(left instanceof ExpressionColumn)) {
return this;
return;
}
ExpressionColumn l = (ExpressionColumn)left;
if(filter != l.getTableFilter()) {
return this;
return;
}
// parameters are always evaluatable, but
// we need to check the actual value now
......@@ -119,10 +119,10 @@ public class CompareLike extends Condition {
// which is maybe slower (but maybe not in this case!)
// TODO optimizer: like: check what other databases do!
if(!right.isConstant()) {
return this;
return;
}
if(escape != null && !escape.isConstant()) {
return this;
return;
}
String p = right.getValue(session).getString();
Value e = escape == null ? null : escape.getValue(session);
......@@ -133,12 +133,12 @@ public class CompareLike extends Condition {
initPattern(p, getEscapeChar(e));
if(patternLength <= 0 || types[0] != MATCH) {
// can't use an index
return this;
return;
}
int dataType = l.getColumn().getType();
if(dataType != Value.STRING && dataType != Value.STRING_IGNORECASE && dataType != Value.STRING_FIXED) {
// column is not a varchar - can't use the index
return this;
return;
}
int maxMatch = 0;
StringBuffer buff = new StringBuffer();
......@@ -146,26 +146,24 @@ public class CompareLike extends Condition {
buff.append(pattern[maxMatch++]);
}
String begin = buff.toString();
Expression condition = this;
if(maxMatch == patternLength) {
condition = filter.addIndexCondition(condition, new IndexCondition(Comparison.EQUAL, l, ValueExpression.get(ValueString.get(begin))));
filter.addIndexCondition(new IndexCondition(Comparison.EQUAL, l, ValueExpression.get(ValueString.get(begin))));
} else {
// TODO check if this is correct according to Unicode rules (code points)
String end;
if(begin.length()>0) {
condition = filter.addIndexCondition(condition, new IndexCondition(Comparison.BIGGER_EQUAL, l, ValueExpression.get(ValueString.get(begin))));
filter.addIndexCondition(new IndexCondition(Comparison.BIGGER_EQUAL, l, ValueExpression.get(ValueString.get(begin))));
char next = begin.charAt(begin.length()-1);
// search the 'next' unicode character (or at least a character that is higher)
for(int i=1; i<2000; i++) {
end = begin.substring(0, begin.length()-1) + (char)(next+i);
if(compareMode.compareString(begin, end, ignoreCase) == -1) {
condition = filter.addIndexCondition(condition, new IndexCondition(Comparison.SMALLER, l, ValueExpression.get(ValueString.get(end))));
filter.addIndexCondition(new IndexCondition(Comparison.SMALLER, l, ValueExpression.get(ValueString.get(end))));
break;
}
}
}
}
return condition;
}
public Value getValue(Session session) throws SQLException {
......
......@@ -198,11 +198,35 @@ public class Comparison extends Condition {
throw Message.getInternalError("type="+compareType);
}
}
private int getNotCompareType(int type) {
switch(compareType) {
case EQUAL:
return NOT_EQUAL;
case NOT_EQUAL:
return EQUAL;
case BIGGER_EQUAL:
return SMALLER;
case BIGGER:
return SMALLER_EQUAL;
case SMALLER_EQUAL:
return BIGGER;
case SMALLER:
return BIGGER_EQUAL;
default:
throw Message.getInternalError("type="+compareType);
}
}
public Expression createIndexConditions(TableFilter filter) {
public Expression getNotIfPossible(Session session) {
int type = getNotCompareType(compareType);
return new Comparison(session, type, left, right);
}
public void createIndexConditions(TableFilter filter) {
if(right==null) {
// TODO index usage: IS [NOT] NULL index usage is possible
return this;
return;
}
ExpressionColumn l = null;
if(left instanceof ExpressionColumn) {
......@@ -220,12 +244,22 @@ public class Comparison extends Condition {
}
// one side must be from the current filter
if(l==null && r==null) {
return this;
return;
}
// filter.addFilterCondition(this, join);
// if both sides are part of the same filter, it can't be used for index lookup
if(l!=null && r!=null) {
return this;
return;
}
if(l == null) {
if(!left.isEverything(ExpressionVisitor.getNotFromResolver(filter))) {
return;
}
} else if(r == null) {
if(!right.isEverything(ExpressionVisitor.getNotFromResolver(filter))) {
return;
}
} else {
// if both sides are part of the same filter, it can't be used for index lookup
return;
}
boolean addIndex;
switch(compareType) {
......@@ -244,13 +278,13 @@ public class Comparison extends Condition {
}
if(addIndex) {
if(l!=null) {
return filter.addIndexCondition(this, new IndexCondition(compareType, l, right));
filter.addIndexCondition(new IndexCondition(compareType, l, right));
} else if(r!=null) {
int compareRev = getReversedCompareType(compareType);
return filter.addIndexCondition(this, new IndexCondition(compareRev, r, left));
filter.addIndexCondition(new IndexCondition(compareRev, r, left));
}
}
return this;
return;
}
public void setEvaluatable(TableFilter tableFilter, boolean b) {
......@@ -293,4 +327,23 @@ public class Comparison extends Condition {
return left.getCost() + (right == null ? 0 : right.getCost()) + 1;
}
public Comparison getAdditional(Session session, Comparison other) {
if(compareType == other.compareType && compareType == EQUAL) {
String l = left.getSQL();
String l2 = other.left.getSQL();
String r = right.getSQL();
String r2 = other.right.getSQL();
if(l.equals(l2)) {
return new Comparison(session, EQUAL, right, other.right);
} else if(l.equals(r2)) {
return new Comparison(session, EQUAL, right, other.left);
} else if(r.equals(l2)) {
return new Comparison(session, EQUAL, left, other.right);
} else if(r.equals(r2)) {
return new Comparison(session, EQUAL, left, other.left);
}
}
return null;
}
}
......@@ -51,12 +51,26 @@ public class ConditionAndOr extends Condition {
return "("+sql+")";
}
public Expression createIndexConditions(TableFilter filter) throws SQLException {
public void createIndexConditions(TableFilter filter) throws SQLException {
if (andOrType == AND) {
left = left.createIndexConditions(filter);
right = right.createIndexConditions(filter);
left.createIndexConditions(filter);
right.createIndexConditions(filter);
}
return this;
}
public Expression getNotIfPossible(Session session) {
// (NOT (A OR B)) > (NOT(A) OR NOT(B))
// (NOT (A AND B)) > (NOT(A) OR NOT(B))
Expression l = left.getNotIfPossible(session);
if(l == null) {
l = new ConditionNot(left);
}
Expression r = right.getNotIfPossible(session);
if(r == null) {
r = new ConditionNot(right);
}
int reversed = andOrType == AND ? OR : AND;
return new ConditionAndOr(reversed, l, r);
}
public Value getValue(Session session) throws SQLException {
......@@ -112,6 +126,20 @@ public class ConditionAndOr extends Condition {
left = right;
right = t;
}
// TODO optimization: convert ((A=1 AND B=2) OR (A=1 AND B=3)) to (A=1 AND (B=2 OR B=3))
if(Constants.OPTIMIZE_2_EQUALS && andOrType == AND) {
// try to add conditions (A=B AND B=1: add A=1)
if(left instanceof Comparison && right instanceof Comparison) {
Comparison compLeft = (Comparison) left;
Comparison compRight = (Comparison) right;
Expression added = compLeft.getAdditional(session, compRight);
if(added != null) {
added = added.optimize(session);
ConditionAndOr a = new ConditionAndOr(AND, this, added);
return a;
}
}
}
Value l = left.isConstant() ? left.getValue(session) : null;
Value r = right.isConstant() ? right.getValue(session) : null;
if(l==null && r==null) {
......
......@@ -120,24 +120,22 @@ public class ConditionIn extends Condition {
return this;
}
public Expression createIndexConditions(TableFilter filter) {
public void createIndexConditions(TableFilter filter) {
if(!Constants.OPTIMIZE_IN) {
return this;
return;
}
if(min == null && max == null) {
return this;
return;
}
if(!(left instanceof ExpressionColumn)) {
return this;
return;
}
ExpressionColumn l = (ExpressionColumn)left;
if(filter != l.getTableFilter()) {
return this;
return;
}
Expression result = this;
result = filter.addIndexCondition(result, new IndexCondition(Comparison.BIGGER_EQUAL, l, ValueExpression.get(min)));
result = filter.addIndexCondition(result, new IndexCondition(Comparison.SMALLER_EQUAL, l, ValueExpression.get(max)));
return result;
filter.addIndexCondition(new IndexCondition(Comparison.BIGGER_EQUAL, l, ValueExpression.get(min)));
filter.addIndexCondition(new IndexCondition(Comparison.SMALLER_EQUAL, l, ValueExpression.get(max)));
}
public void setEvaluatable(TableFilter tableFilter, boolean b) {
......
......@@ -6,6 +6,7 @@ package org.h2.expression;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
......@@ -20,9 +21,8 @@ public class ConditionNot extends Condition {
this.condition = condition;
}
public Expression createIndexConditions(TableFilter filter) {
// TODO optimization: in some cases, index conditions can be created here
return this;
public Expression getNotIfPossible(Session session) {
return condition;
}
public Value getValue(Session session) throws SQLException {
......@@ -38,8 +38,17 @@ public class ConditionNot extends Condition {
}
public Expression optimize(Session session) throws SQLException {
if(!Constants.OPTIMIZE_NOT) {
return this;
}
// TODO optimization: some cases are maybe possible to optimize further: (NOT ID >= 5)
Expression expr = condition.optimize(session);
Expression e2 = expr.getNotIfPossible(session);
if(e2 != null) {
e2 = e2.optimize(session);
expr = e2;
return e2;
}
if(expr.isConstant()) {
Value v = expr.getValue(session);
if(v == ValueNull.INSTANCE) {
......
......@@ -38,6 +38,11 @@ public abstract class Expression {
return isEverything(visitor);
}
public Expression getNotIfPossible(Session session) {
// by default it is not possible
return null;
}
public boolean isConstant() {
return false;
}
......@@ -51,9 +56,8 @@ public abstract class Expression {
return getValue(session).getBoolean();
}
public Expression createIndexConditions(TableFilter filter) throws SQLException {
public void createIndexConditions(TableFilter filter) throws SQLException {
// default is do nothing
return this;
}
public String getColumnName() {
......
......@@ -236,6 +236,8 @@ public class ExpressionColumn extends Expression {
case ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID:
visitor.addDataModificationId(column.getTable().getMaxDataModificationId());
return true;
case ExpressionVisitor.NOT_FROM_RESOLVER:
return resolver != visitor.getResolver();
default:
throw Message.getInternalError("type="+visitor.type);
}
......@@ -245,16 +247,16 @@ public class ExpressionColumn extends Expression {
return 2;
}
public Expression createIndexConditions(TableFilter filter) {
public void createIndexConditions(TableFilter filter) {
TableFilter tf = getTableFilter();
if(filter != tf) {
return this;
}
if(column.getType() == Value.BOOLEAN) {
if(filter == tf && column.getType() == Value.BOOLEAN) {
IndexCondition cond = new IndexCondition(Comparison.EQUAL, this, ValueExpression.get(ValueBoolean.get(true)));
return filter.addIndexCondition(this, cond);
filter.addIndexCondition(cond);
}
return this;
}
public Expression getNotIfPossible(Session session) {
return new Comparison(session, Comparison.EQUAL, this, ValueExpression.get(ValueBoolean.get(false)));
}
}
......@@ -4,6 +4,7 @@
*/
package org.h2.expression;
import org.h2.table.ColumnResolver;
import org.h2.table.Table;
public class ExpressionVisitor {
......@@ -24,11 +25,15 @@ public class ExpressionVisitor {
// Does the expression have no side effects (change the data)?
public static final int READONLY = 5;
// Does an expression have no relation to the given table filter?
public static final int NOT_FROM_RESOLVER = 6;
int queryLevel;
public Table table;
public int type;
private long maxDataModificationId;
private ColumnResolver resolver;
public static ExpressionVisitor get(int type) {
return new ExpressionVisitor(type);
......@@ -45,9 +50,19 @@ public class ExpressionVisitor {
public void queryLevel(int offset) {
queryLevel += offset;
}
public ColumnResolver getResolver() {
return resolver;
}
public void addDataModificationId(long v) {
maxDataModificationId = Math.max(maxDataModificationId, v);
}
public static ExpressionVisitor getNotFromResolver(ColumnResolver resolver) {
ExpressionVisitor v = new ExpressionVisitor(NOT_FROM_RESOLVER);
v.resolver = resolver;
return v;
}
}
......@@ -1411,7 +1411,7 @@ public class Function extends Expression implements FunctionCall {
switch(info.type) {
case CAST:
case CONVERT: {
buff.append(StringUtils.unEnclose(args[0].getSQL()));
buff.append(args[0].getSQL());
buff.append(" AS ");
buff.append(new Column(null, dataType, precision, scale).getCreateSQL());
break;
......@@ -1420,7 +1420,7 @@ public class Function extends Expression implements FunctionCall {
ValueString v = (ValueString)((ValueExpression)args[0]).getValue(null);
buff.append(v.getString());
buff.append(" FROM ");
buff.append(StringUtils.unEnclose(args[1].getSQL()));
buff.append(args[1].getSQL());
break;
}
case TABLE: {
......@@ -1441,7 +1441,7 @@ public class Function extends Expression implements FunctionCall {
buff.append(", ");
}
Expression e = args[i];
buff.append(StringUtils.unEnclose(e.getSQL()));
buff.append(e.getSQL());
}
}
}
......
......@@ -11,6 +11,7 @@ import org.h2.message.Message;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull;
/**
......@@ -103,6 +104,8 @@ public class Parameter extends Expression implements ParameterInterface {
case ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID:
// it is checked independently if the value is the same as the last time
return true;
case ExpressionVisitor.NOT_FROM_RESOLVER:
return true;
default:
throw Message.getInternalError("type="+visitor.type);
}
......@@ -111,5 +114,9 @@ public class Parameter extends Expression implements ParameterInterface {
public int getCost() {
return 0;
}
public Expression getNotIfPossible(Session session) {
return new Comparison(session, Comparison.EQUAL, this, ValueExpression.get(ValueBoolean.get(false)));
}
}
......@@ -70,6 +70,8 @@ public class Rownum extends Expression {
return true;
case ExpressionVisitor.READONLY:
return true;
case ExpressionVisitor.NOT_FROM_RESOLVER:
return true;
default:
throw Message.getInternalError("type="+visitor.type);
}
......
......@@ -79,6 +79,8 @@ public class SequenceValue extends Expression {
case ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID:
visitor.addDataModificationId(sequence.getModificationId());
return true;
case ExpressionVisitor.NOT_FROM_RESOLVER:
return true;
default:
throw Message.getInternalError("type="+visitor.type);
}
......
......@@ -39,14 +39,17 @@ public class ValueExpression extends Expression {
return value.getType();
}
public Expression createIndexConditions(TableFilter filter) {
public void createIndexConditions(TableFilter filter) {
if(value.getType() == Value.BOOLEAN) {
boolean v = ((ValueBoolean)value).getBoolean().booleanValue();
if(!v) {
return filter.addIndexCondition(this, new IndexCondition(Comparison.FALSE, null, this));
filter.addIndexCondition(new IndexCondition(Comparison.FALSE, null, this));
}
}
return this;
}
public Expression getNotIfPossible(Session session) {
return new Comparison(session, Comparison.EQUAL, this, ValueExpression.get(ValueBoolean.get(false)));
}
public void mapColumns(ColumnResolver resolver, int level) throws SQLException {
......@@ -91,6 +94,8 @@ public class ValueExpression extends Expression {
return true;
case ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID:
return true;
case ExpressionVisitor.NOT_FROM_RESOLVER:
return true;
default:
throw Message.getInternalError("type="+visitor.type);
}
......
......@@ -150,11 +150,11 @@ public class PgServerThread implements Runnable {
println(" key: "+readInt());
error("CancelRequest", null);
} else if(version == 80877103) {
println("SSLRequest");
// println("SSLRequest");
out.write('N');
} else {
// println("StartupMessage");
// println(" version " + version + " (" + (version >> 16) + "." + (version & 0xff) + ")");
// println("StartupMessage");
// println(" version " + version + " (" + (version >> 16) + "." + (version & 0xff) + ")");
while(true) {
String param = readString();
if(param.length() == 0) {
......@@ -195,7 +195,6 @@ public class PgServerThread implements Runnable {
Prepared p = new Prepared();
p.name = readString();
p.sql = getSQL(readString());
println(p.sql + ";");
int count = readShort();
p.paramType = new int[count];
for(int i=0; i<count; i++) {
......@@ -366,6 +365,7 @@ public class PgServerThread implements Runnable {
}
int todoNeedToSupportInParser;
s = StringUtils.replaceAll(s, "i.indkey[ia.attnum-1]", "0");
println(s + ";");
return s;
}
......
......@@ -291,3 +291,5 @@ SELECT
AND n.nspname = 'PUBLIC'
AND ct.relname = 'TEST'
ORDER BY NON_UNIQUE, TYPE, INDEX_NAME, ORDINAL_POSITION ;
......@@ -878,7 +878,7 @@ class WebThread extends Thread {
buff.append(PageParser.escapeHtml(s+";"));
buff.append("<br />");
}
buff.append(getResult(conn, i+1, s, list.size()==1));
buff.append(getResult(conn, i+1, s, list.size()==1, false));
buff.append("<br />");
}
result = buff.toString();
......@@ -924,7 +924,7 @@ class WebThread extends Thread {
}
String sql = "@EDIT " + (String) session.get("resultSetSQL");
Connection conn = session.getConnection();
result = error + getResult(conn, -1, sql, true) + result;
result = error + getResult(conn, -1, sql, true, true) + result;
session.put("result", result);
return "result.jsp";
}
......@@ -1153,7 +1153,7 @@ class WebThread extends Thread {
return maxrows;
}
private String getResult(Connection conn, int id, String sql, boolean allowEdit) {
private String getResult(Connection conn, int id, String sql, boolean allowEdit, boolean forceEdit) {
try {
sql = sql.trim();
StringBuffer buff = new StringBuffer();
......@@ -1162,7 +1162,13 @@ class WebThread extends Thread {
String sessionId = attributes.getProperty("jsessionid");
buff.append("<script type=\"text/javascript\">top['h2menu'].location='tables.do?jsessionid="+sessionId+"';</script>");
}
Statement stat = conn.createStatement();
Statement stat;
DbContents contents = session.getContents();
if(forceEdit || (allowEdit && contents.isH2)) {
stat = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
} else {
stat = conn.createStatement();
}
ResultSet rs;
long time = System.currentTimeMillis();
boolean metadata = false;
......@@ -1441,7 +1447,7 @@ class WebThread extends Thread {
buff.append("</tr>");
}
}
boolean isUpdatable = rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE;
boolean isUpdatable = rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE && rs.getType() != ResultSet.TYPE_FORWARD_ONLY;
if(edit) {
ResultSet old = session.result;
if(old != null) {
......
......@@ -144,21 +144,7 @@ public class FunctionTable extends Table {
}
public String getSQL() {
int todoDocument;
return function.getSQL();
// return super.getSQL();
// StringBuffer buff = new StringBuffer();
// buff.append(super.getSQL());
// buff.append('(');
// Expression[] args = function.getArgs();
// for (int i = 0; i < args.length; i++) {
// if (i > 0) {
// buff.append(", ");
// }
// buff.append(args[i].getSQL());
// }
// buff.append(')');
// return buff.toString();
}
}
......@@ -289,8 +289,7 @@ public class TableData extends Table implements RecordReader {
session.addLock(this);
lockExclusive = session;
return;
} else if (lockShared.size() == 1
&& lockShared.contains(session)) {
} else if (lockShared.size() == 1 && lockShared.contains(session)) {
traceLock(session, exclusive, "ok (upgrade)");
lockExclusive = session;
return;
......@@ -388,10 +387,14 @@ public class TableData extends Table implements RecordReader {
if(lockExclusive == s) {
lockExclusive = null;
}
lockShared.remove(s);
if(lockShared.size() > 0) {
lockShared.remove(s);
}
// TODO lock: maybe we need we fifo-queue to make sure nobody starves. check what other databases do
synchronized(database) {
database.notifyAll();
if(database.getSessionCount() > 1) {
database.notifyAll();
}
}
}
}
......
......@@ -5,13 +5,11 @@
package org.h2.table;
import java.sql.SQLException;
import org.h2.command.dml.Select;
import org.h2.engine.Constants;
import org.h2.engine.Mode;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.Expression;
import org.h2.index.Cursor;
......@@ -323,21 +321,8 @@ public class TableFilter implements ColumnResolver {
return table.getName();
}
public Expression addIndexCondition(Expression expr, IndexCondition condition) {
for(int i=0; i<indexConditions.size(); i++) {
IndexCondition c = (IndexCondition) indexConditions.get(i);
if(c.getColumn() == condition.getColumn()) {
if(c.getMask() == IndexCondition.EQUALITY && condition.getMask() == IndexCondition.EQUALITY) {
Expression e1 = c.getExpression();
Expression e2 = condition.getExpression();
// Comparison comp = new Comparison();
System.out.println("add: " + e1.getSQL() + " = " + e2.getSQL());
}
}
}
public void addIndexCondition(IndexCondition condition) {
indexConditions.add(condition);
return expr;
}
public void addFilterCondition(Expression condition, boolean join) {
......@@ -380,7 +365,7 @@ public class TableFilter implements ColumnResolver {
private void mapAndAddFilter(Expression on) throws SQLException {
on.mapColumns(this, 0);
addFilterCondition(on, true);
on = on.createIndexConditions(this);
on.createIndexConditions(this);
for(int i=0; joins != null && i<joins.size(); i++) {
TableFilter join = getTableFilter(i);
join.mapAndAddFilter(on);
......
......@@ -319,7 +319,7 @@ public class Server implements Runnable {
}
/**
* Create a new TCP server, but does not start it yet.
* Create a new ODBC server, but does not start it yet.
* @param args
* @return the server
*/
......
......@@ -260,7 +260,6 @@ public class StringUtils {
}
public static String unEnclose(String s) {
int todoNotCorrectForArrays;
if(s.startsWith("(") && s.endsWith(")")) {
return s.substring(1, s.length()-1);
} else {
......
......@@ -94,54 +94,12 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
/*
H2 Console: the result set is not updatable by default. Need to change in web console, and test other databases
drop table tc, ts, tx;
CREATE MEMORY TABLE tc(ARG0 INT, ARG1 INT);
-- 8 = SELECT COUNT(*) FROM tc;
INSERT INTO tc(ARG0, ARG1) VALUES(1, 0);
INSERT INTO tc(ARG0, ARG1) VALUES(2, 0);
INSERT INTO tc(ARG0, ARG1) VALUES(3, 0);
INSERT INTO tc(ARG0, ARG1) VALUES(4, 0);
INSERT INTO tc(ARG0, ARG1) VALUES(5, 0);
INSERT INTO tc(ARG0, ARG1) VALUES(7, 6);
INSERT INTO tc(ARG0, ARG1) VALUES(8, 6);
INSERT INTO tc(ARG0, ARG1) VALUES(5, 6);
CREATE INDEX tc01 ON tc(ARG0, ARG1);
CREATE INDEX tc1 ON tc(ARG1);
CREATE MEMORY TABLE ts(ARG0 INT, ARG1 INT, ARG2 INT);
-- 0 = SELECT COUNT(*) FROM ts;
CREATE INDEX ts02 ON ts(ARG0, ARG2);
CREATE INDEX ts12 ON ts(ARG1, ARG2);
CREATE INDEX ts2 ON ts(ARG2);
CREATE FORCE VIEW v1(ARG0, ARG1) AS (SELECT ARG0, ARG1 FROM tc UNION SELECT ARG0, ARG2 FROM ts);
create table tx (ARG1 int);
insert into tx values (0),(6);
-- This query produce the correct results only with -Dh2.indexOld=true
select * from v1 natural join tx;
drop view V;
drop table A, B;
CREATE TABLE A(A INT);
INSERT INTO A(A) VALUES(6);
CREATE TABLE B(B INT PRIMARY KEY);
CREATE VIEW V(V) AS (SELECT A FROM A UNION SELECT B FROM B);
drop table C;
create table C(C int);
delete from C;
insert into C values (0), (6);
select * from V, C where V.V = C.C;
I forgot to mention a litte documentation bug.
In the code sample of the feature section "Compacting a Database" there is the line
Backup.execute(url, user, password, script);
Correctly it should be
Script.execute(url, user, password, script);
database is not closed when killing the process (but should? why not?)
set read-committed as the default
SELECT rolcreaterole, rolcreatedb FROM pg_roles WHERE rolname = current_user;
check trace.db/ number of files, limit to 1000 or so
......@@ -169,8 +127,6 @@ make sure INDEX_LOOKUP_NEW = is true by default.
Test Console (batch, javaw, different platforms)
test with openoffice (metadata changes)
set read-committed as the default
testHalt
java org.h2.test.TestAll halt
......
--- special grammar and test cases ---------------------------------------------------------------------------------------------
explain select * from system_range(1, 2) where x=x+1 and x=1;
> PLAN
> -------------------------------------------------------------------------------------------------------
> SELECT SYSTEM_RANGE.X FROM SYSTEM_RANGE(1, 2) /* PUBLIC.RANGE_INDEX */ WHERE (X <> 1) OR ((X * 2) <> 2)
> rows: 1
explain select * from system_range(1, 2) where not (x = 1 and x*2 = 2);
> PLAN
> -------------------------------------------------------------------------------------------------------
> SELECT SYSTEM_RANGE.X FROM SYSTEM_RANGE(1, 2) /* PUBLIC.RANGE_INDEX */ WHERE (X <> 1) OR ((X * 2) <> 2)
> rows: 1
explain select * from system_range(1, 10) where (NOT x >= 5);
> PLAN
> ------------------------------------------------------------------------------------------
> SELECT SYSTEM_RANGE.X FROM SYSTEM_RANGE(1, 10) /* PUBLIC.RANGE_INDEX: X < 5 */ WHERE X < 5
> rows: 1
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255));
> ok
INSERT INTO TEST VALUES(1, 'Hello'), (-1, '-1');
> update count: 2
select * from test where name = -1 and name = id;
> ID NAME
> -- ----
> -1 -1
> rows: 1
explain select * from test where name = -1 and name = id;
> PLAN
> ------------------------------------------------------------------------------------------------------------------------------------------------------------------
> SELECT TEST.ID, TEST.NAME FROM PUBLIC.TEST /* PUBLIC.PRIMARY_KEY_1: ID = -1 */ WHERE ((CAST(NAME AS INTEGER) = -1) AND (CAST(NAME AS INTEGER) = ID)) AND (-1 = ID)
> rows: 1
DROP TABLE TEST;
> ok
select * from system_range(1, 2) where x=x+1 and x=1;
> X
> -
> rows: 0
CREATE TABLE A as select 6 a;
> ok
CREATE TABLE B(B INT PRIMARY KEY);
> ok
CREATE VIEW V(V) AS (SELECT A FROM A UNION SELECT B FROM B);
> ok
create table C as select * from table(c int = (0,6));
> ok
select * from V, C where V.V = C.C;
> V C
> - -
> 6 6
> rows: 1
drop table A, B, C, V;
> ok
explain select * from table(id int = (1, 2), name varchar=('Hello', 'World'));
> PLAN
> ------------------------------------------------------------------------------------------------------
> SELECT TABLE.ID, TABLE.NAME FROM TABLE(ID INT=(1, 2), NAME VARCHAR=('Hello', 'World')) /* PUBLIC."" */
> rows: 1
CREATE TABLE TEST(ID INT PRIMARY KEY, FLAG BOOLEAN, NAME VARCHAR);
> ok
......@@ -6,13 +77,19 @@ CREATE INDEX IDXFLAG ON TEST(FLAG, NAME);
> ok
INSERT INTO TEST VALUES(1, TRUE, 'Hello'), (2, FALSE, 'World');
> ok
> update count: 2
EXPLAIN SELECT * FROM TEST WHERE FLAG;
> ok
> PLAN
> --------------------------------------------------------------------------------------------------
> SELECT TEST.ID, TEST.FLAG, TEST.NAME FROM PUBLIC.TEST /* PUBLIC.IDXFLAG: FLAG = TRUE */ WHERE FLAG
> rows: 1
EXPLAIN SELECT * FROM TEST WHERE FLAG AND NAME>'I';
> ok
> PLAN
> ----------------------------------------------------------------------------------------------------------------------------------
> SELECT TEST.ID, TEST.FLAG, TEST.NAME FROM PUBLIC.TEST /* PUBLIC.IDXFLAG: FLAG = TRUE AND NAME > 'I' */ WHERE FLAG AND (NAME > 'I')
> rows: 1
DROP TABLE TEST;
> ok
......@@ -1060,8 +1137,8 @@ insert into x values(0), (1), (10);
> update count: 3
SELECT t1.ID, (SELECT t1.id || ':' || AVG(t2.ID) FROM X t2) FROM X t1;
> ID SELECT (T1.ID || ':') || AVG(T2.ID) FROM PUBLIC.X T2 /* PUBLIC.X_TABLE_SCAN */ LIMIT 2
> -- --------------------------------------------------------------------------------------
> ID SELECT ((T1.ID || ':') || AVG(T2.ID)) FROM PUBLIC.X T2 /* PUBLIC.X_TABLE_SCAN */ LIMIT 2
> -- ----------------------------------------------------------------------------------------
> 0 0:3
> 1 1:3
> 10 10:3
......@@ -1620,8 +1697,8 @@ SELECT CASE WHEN NOT (false IN (null)) THEN false END;
> rows: 1
select a.v as av, b.v as bv, a.v IN (b.v), not a.v IN (b.v) from test a, test b;
> AV BV A.V = B.V NOT (A.V = B.V)
> ----- ----- --------- ---------------
> AV BV A.V = B.V A.V <> B.V
> ----- ----- --------- ----------
> FALSE FALSE TRUE FALSE
> FALSE TRUE FALSE TRUE
> FALSE null null null
......@@ -3255,14 +3332,14 @@ update test set (id, name)=(select id+1, name || 'Ho' from test t1 where test.id
explain update test set (id, name)=(id+1, name || 'Hi');
> PLAN
> ----------------------------------------------------------------------------------------------------------------------------------------------
> UPDATE PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */ SET ID = ARRAY_GET((ID + 1), (NAME || 'Hi'), 1), NAME = ARRAY_GET((ID + 1), (NAME || 'Hi'), 2)
> --------------------------------------------------------------------------------------------------------------------------------------------------
> UPDATE PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */ SET ID = ARRAY_GET(((ID + 1), (NAME || 'Hi')), 1), NAME = ARRAY_GET(((ID + 1), (NAME || 'Hi')), 2)
> rows: 1
explain update test set (id, name)=(select id+1, name || 'Ho' from test t1 where test.id=t1.id);
> PLAN
> --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> UPDATE PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */ SET ID = ARRAY_GET(SELECT ID + 1, NAME || 'Ho' FROM PUBLIC.TEST T1 /* PUBLIC.PRIMARY_KEY_1: ID = TEST.ID */ WHERE TEST.ID = T1.ID, 1), NAME = ARRAY_GET(SELECT ID + 1, NAME || 'Ho' FROM PUBLIC.TEST T1 /* PUBLIC.PRIMARY_KEY_1: ID = TEST.ID */ WHERE TEST.ID = T1.ID, 2)
> --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> UPDATE PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */ SET ID = ARRAY_GET((SELECT (ID + 1), (NAME || 'Ho') FROM PUBLIC.TEST T1 /* PUBLIC.PRIMARY_KEY_1: ID = TEST.ID */ WHERE TEST.ID = T1.ID), 1), NAME = ARRAY_GET((SELECT (ID + 1), (NAME || 'Ho') FROM PUBLIC.TEST T1 /* PUBLIC.PRIMARY_KEY_1: ID = TEST.ID */ WHERE TEST.ID = T1.ID), 2)
> rows: 1
select * from test;
......@@ -4033,8 +4110,8 @@ SELECT * FROM TEST2COL WHERE A=0 AND B=0;
EXPLAIN SELECT * FROM TEST2COL WHERE A=0 AND B=0;
> PLAN
> ------------------------------------------------------------------------------------------------------------------------------------
> SELECT TEST2COL.A, TEST2COL.B, TEST2COL.C FROM PUBLIC.TEST2COL /* PUBLIC.PRIMARY_KEY_1: A = 0 AND B = 0 */ WHERE (A = 0) AND (B = 0)
> --------------------------------------------------------------------------------------------------------------------------------------------------
> SELECT TEST2COL.A, TEST2COL.B, TEST2COL.C FROM PUBLIC.TEST2COL /* PUBLIC.PRIMARY_KEY_1: A = 0 AND B = 0 */ WHERE ((A = 0) AND (B = 0)) AND (A = B)
> rows: 1
SELECT * FROM TEST2COL WHERE A=0;
......@@ -4202,14 +4279,14 @@ EXPLAIN INSERT INTO TEST VALUES(1, 'Test'), (2, 'World');
EXPLAIN INSERT INTO TEST SELECT DISTINCT ID+1, NAME FROM TEST;
> PLAN
> ------------------------------------------------------------------------------------------------------------
> INSERT INTO PUBLIC.TEST(ID, NAME) SELECT DISTINCT ID + 1, NAME FROM PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */
> --------------------------------------------------------------------------------------------------------------
> INSERT INTO PUBLIC.TEST(ID, NAME) SELECT DISTINCT (ID + 1), NAME FROM PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */
> rows: 1
EXPLAIN SELECT DISTINCT ID + 1, NAME FROM TEST;
> PLAN
> --------------------------------------------------------------------------
> SELECT DISTINCT ID + 1, NAME FROM PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */
> ----------------------------------------------------------------------------
> SELECT DISTINCT (ID + 1), NAME FROM PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */
> rows: 1
EXPLAIN SELECT * FROM TEST WHERE 1=0;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论