提交 573ff799 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 d93bc0ff
......@@ -106,6 +106,7 @@ import org.h2.expression.Parameter;
import org.h2.expression.Rownum;
import org.h2.expression.SequenceValue;
import org.h2.expression.Subquery;
import org.h2.expression.TableFunction;
import org.h2.expression.ValueExpression;
import org.h2.expression.Variable;
import org.h2.expression.Wildcard;
......@@ -1889,7 +1890,8 @@ public class Parser {
read(")");
break;
}
case Function.TABLE: {
case Function.TABLE:
case Function.TABLE_DISTINCT: {
int i = 0;
ObjectArray columns = new ObjectArray();
do {
......@@ -1901,7 +1903,8 @@ public class Parser {
i++;
} while (readIf(","));
read(")");
function.setColumns(columns);
TableFunction tf = (TableFunction) function;
tf.setColumns(columns);
break;
}
default:
......
......@@ -105,7 +105,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
}
if (index == null) {
IndexType indexType = IndexType.createPrimaryKey(table.isPersistent(), primaryKeyHash);
String indexName = getSchema().getUniqueIndexName(table, Constants.PREFIX_PRIMARY_KEY);
String indexName = table.getSchema().getUniqueIndexName(table, Constants.PREFIX_PRIMARY_KEY);
int id = getObjectId(true, false);
try {
index = table.addIndex(session, indexName, id, indexColumns, indexType, Index.EMPTY_HEAD, null);
......@@ -237,7 +237,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
}
indexType.setBelongsToConstraint(true);
String prefix = constraintName == null ? "CONSTRAINT" : constraintName;
String indexName = getSchema().getUniqueIndexName(t, prefix + "_INDEX_");
String indexName = t.getSchema().getUniqueIndexName(t, prefix + "_INDEX_");
Index idx;
try {
idx = t.addIndex(session, indexName, indexId, cols, indexType, Index.EMPTY_HEAD, null);
......@@ -281,11 +281,6 @@ public class AlterTableAddConstraint extends SchemaCommand {
if (index.getTable() != table || !index.getIndexType().isUnique()) {
return false;
}
if (index.getIndexType().belongsToConstraint()) {
// the constraint might be dropped (also in an alter table
// statement)
return false;
}
Column[] indexCols = index.getColumns();
if (indexCols.length > cols.length) {
return false;
......@@ -309,11 +304,6 @@ public class AlterTableAddConstraint extends SchemaCommand {
// can't use the scan index or index of another table
return false;
}
if (index.getIndexType().belongsToConstraint()) {
// the constraint might be dropped (also in an alter table
// statement)
return false;
}
Column[] indexCols = index.getColumns();
if (indexCols.length < cols.length) {
return false;
......
......@@ -71,9 +71,9 @@ public class CreateIndex extends SchemaCommand {
}
int id = getObjectId(true, false);
if (primaryKey) {
indexName = getSchema().getUniqueIndexName(table, Constants.PREFIX_PRIMARY_KEY);
indexName = table.getSchema().getUniqueIndexName(table, Constants.PREFIX_PRIMARY_KEY);
} else if (indexName == null) {
indexName = getSchema().getUniqueIndexName(table, Constants.PREFIX_INDEX);
indexName = table.getSchema().getUniqueIndexName(table, Constants.PREFIX_INDEX);
}
if (getSchema().findIndex(indexName) != null) {
if (ifNotExists) {
......
......@@ -308,6 +308,13 @@ public class ScriptCommand extends ScriptBase {
tempLobTableCreated = false;
}
ObjectArray constraints = db.getAllSchemaObjects(DbObject.CONSTRAINT);
constraints.sort(new Comparator() {
public int compare(Object o1, Object o2) {
Constraint c1 = (Constraint) o1;
Constraint c2 = (Constraint) o2;
return c1.compareTo(c2);
}
});
for (int i = 0; i < constraints.size(); i++) {
Constraint constraint = (Constraint) constraints.get(i);
add(constraint.getCreateSQLWithoutIndexes(), false);
......
......@@ -9,6 +9,7 @@ import java.sql.SQLException;
import org.h2.engine.DbObject;
import org.h2.engine.Session;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.result.Row;
import org.h2.schema.Schema;
......@@ -19,7 +20,7 @@ import org.h2.table.Table;
/**
* The base class for constraint checking.
*/
public abstract class Constraint extends SchemaObjectBase {
public abstract class Constraint extends SchemaObjectBase implements Comparable {
/**
* The constraint type name for check constraints.
......@@ -72,6 +73,13 @@ public abstract class Constraint extends SchemaObjectBase {
*/
public abstract boolean usesIndex(Index index);
/**
* This index is now the owner of the specified index.
*
* @param index
*/
public abstract void setIndexOwner(Index index);
/**
* Check if this constraint contains the given column.
*
......@@ -136,4 +144,29 @@ public abstract class Constraint extends SchemaObjectBase {
return null;
}
private int getConstraintTypeOrder() {
String constraintType = getConstraintType();
if (CHECK.equals(constraintType)) {
return 0;
} else if (PRIMARY_KEY.equals(constraintType)) {
return 1;
} else if (UNIQUE.equals(constraintType)) {
return 2;
} else if (REFERENTIAL.equals(constraintType)) {
return 3;
} else {
throw Message.getInternalError("type: " + constraintType);
}
}
public int compareTo(Object other) {
if (this == other) {
return 0;
}
Constraint otherConstraint = (Constraint) other;
int thisType = getConstraintTypeOrder();
int otherType = otherConstraint.getConstraintTypeOrder();
return thisType - otherType;
}
}
......@@ -98,6 +98,10 @@ public class ConstraintCheck extends Constraint {
return false;
}
public void setIndexOwner(Index index) {
throw Message.getInternalError();
}
public boolean containsColumn(Column col) {
// TODO check constraints / containsColumn: this is cheating, maybe the
// column is not referenced
......
......@@ -202,10 +202,10 @@ public class ConstraintReferential extends Constraint {
table.removeConstraint(this);
refTable.removeConstraint(this);
if (indexOwner) {
database.removeSchemaObject(session, index);
table.removeIndexOrTransferOwnership(session, index);
}
if (refIndexOwner) {
database.removeSchemaObject(session, refIndex);
refTable.removeIndexOrTransferOwnership(session, refIndex);
}
refTable = null;
index = null;
......@@ -515,6 +515,16 @@ public class ConstraintReferential extends Constraint {
return idx == index || idx == refIndex;
}
public void setIndexOwner(Index index) {
if (this.index == index) {
indexOwner = true;
} else if (this.refIndex == index) {
refIndexOwner = true;
} else {
throw Message.getInternalError();
}
}
public boolean containsColumn(Column col) {
for (int i = 0; i < columns.length; i++) {
if (columns[i].column == col) {
......
......@@ -5,6 +5,7 @@
package org.h2.constraint;
import java.sql.SQLException;
import org.h2.command.Parser;
import org.h2.engine.Session;
import org.h2.index.Index;
......@@ -113,7 +114,7 @@ public class ConstraintUnique extends Constraint {
public void removeChildrenAndResources(Session session) throws SQLException {
table.removeConstraint(this);
if (indexOwner) {
database.removeSchemaObject(session, index);
table.removeIndexOrTransferOwnership(session, index);
}
index = null;
columns = null;
......@@ -129,6 +130,10 @@ public class ConstraintUnique extends Constraint {
return idx == index;
}
public void setIndexOwner(Index index) {
indexOwner = true;
}
public boolean containsColumn(Column col) {
for (int i = 0; i < columns.length; i++) {
if (columns[i].column == col) {
......
......@@ -1486,6 +1486,7 @@ public class Database implements DataHandler {
log.setDisabled(!logData);
log.checkpoint();
}
traceSystem.getTrace(Trace.DATABASE).error("SET LOG " + level, null);
logLevel = level;
}
......
......@@ -209,7 +209,7 @@ public class ConditionIn extends Condition {
}
Database db = session.getDatabase();
Schema mainSchema = db.getSchema(Constants.SCHEMA_MAIN);
Function function = Function.getFunction(database, "TABLE_DISTINCT");
TableFunction function = new TableFunction(database, Function.getFunctionInfo("TABLE_DISTINCT"));
Expression[] array = new Expression[values.size()];
for (int i = 0; i < values.size(); i++) {
Expression e = (Expression) values.get(i);
......
......@@ -6,14 +6,15 @@ package org.h2.expression;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.table.Column;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.tools.SimpleResultSet;
import org.h2.util.MathUtils;
import org.h2.util.ObjectArray;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueArray;
......@@ -23,16 +24,26 @@ import org.h2.value.ValueResultSet;
/**
* Implementation of the functions TABLE(..) and TABLE_DISTINCT(..).
*/
public class TableFunction extends Expression implements FunctionCall {
public class TableFunction extends Function implements FunctionCall {
private boolean distinct;
private Expression[] args;
private Column[] columnList;
TableFunction(Database database, FunctionInfo info) {
super(database, info);
distinct = info.type == Function.TABLE_DISTINCT;
}
public Value getValue(Session session) throws SQLException {
int todoClassIsNotUsed;
return getTable(session, args, false, distinct);
}
protected void checkParameterCount(int len) throws SQLException {
if (len < 1) {
throw Message.getSQLException(ErrorCode.INVALID_PARAMETER_COUNT_2, new String[] { getName(),
">0" });
}
}
public String getSQL() {
StringBuffer buff = new StringBuffer();
buff.append(getName());
......@@ -50,6 +61,37 @@ public class TableFunction extends Expression implements FunctionCall {
return buff.toString();
}
public String getName() {
return distinct ? "TABLE_DISTINCT" : "TABLE";
}
public int getRowCount(Session session) throws SQLException {
int len = columnList.length;
int rowCount = 0;
for (int i = 0; i < len; i++) {
Expression expr = args[i];
if (expr.isConstant()) {
Value v = expr.getValue(session);
if (v != ValueNull.INSTANCE) {
ValueArray array = (ValueArray) v.convertTo(Value.ARRAY);
Value[] l = array.getList();
rowCount = Math.max(rowCount, l.length);
}
}
}
return rowCount;
}
public ValueResultSet getValueForColumnList(Session session, Expression[] nullArgs) throws SQLException {
return getTable(session, args, true, false);
}
public void setColumns(ObjectArray columns) {
this.columnList = new Column[columns.size()];
columns.toArray(columnList);
}
public ValueResultSet getTable(Session session, Expression[] args, boolean onlyColumnList, boolean distinct) throws SQLException {
int len = columnList.length;
Expression[] header = new Expression[len];
......@@ -122,114 +164,4 @@ public class TableFunction extends Expression implements FunctionCall {
return simple;
}
public int getScale() {
return 0;
}
public void mapColumns(ColumnResolver resolver, int level) throws SQLException {
for (int i = 0; i < args.length; i++) {
args[i].mapColumns(resolver, level);
}
}
public int getCost() {
int cost = 3;
for (int i = 0; i < args.length; i++) {
cost += args[i].getCost();
}
return cost;
}
public int getDisplaySize() {
return Integer.MAX_VALUE;
}
public long getPrecision() {
return 0;
}
public int getType() {
return Value.RESULT_SET;
}
public boolean isEverything(ExpressionVisitor visitor) {
for (int i = 0; i < args.length; i++) {
Expression e = args[i];
if (e != null && !e.isEverything(visitor)) {
return false;
}
}
return true;
}
public Expression optimize(Session session) throws SQLException {
boolean allConst = true;
for (int i = 0; i < args.length; i++) {
Expression e = args[i].optimize(session);
args[i] = e;
if (!e.isConstant()) {
allConst = false;
}
}
if (allConst) {
return ValueExpression.get(getValue(session));
}
return this;
}
public void setEvaluatable(TableFilter tableFilter, boolean value) {
for (int i = 0; i < args.length; i++) {
Expression e = args[i];
if (e != null) {
e.setEvaluatable(tableFilter, value);
}
}
}
public void updateAggregate(Session session) throws SQLException {
for (int i = 0; i < args.length; i++) {
Expression e = args[i];
if (e != null) {
e.updateAggregate(session);
}
}
}
public boolean canGetRowCount() {
return true;
}
public Expression[] getArgs() {
return args;
}
public String getName() {
return distinct ? "TABLE_DISTINCT" : "TABLE";
}
public int getParameterCount() {
return args.length;
}
public int getRowCount(Session session) throws SQLException {
int len = columnList.length;
int rowCount = 0;
for (int i = 0; i < len; i++) {
Expression expr = args[i];
if (expr.isConstant()) {
Value v = expr.getValue(session);
if (v != ValueNull.INSTANCE) {
ValueArray array = (ValueArray) v.convertTo(Value.ARRAY);
Value[] l = array.getList();
rowCount = Math.max(rowCount, l.length);
}
}
}
return rowCount;
}
public ValueResultSet getValueForColumnList(Session session, Expression[] nullArgs) throws SQLException {
return getTable(session, args, true, false);
}
}
......@@ -666,4 +666,26 @@ public abstract class Table extends SchemaObjectBase {
return false;
}
/**
* If the index is still required by a constraint, transfer the ownership to it.
* Otherwise, the index is removed.
*
* @param session the session
* @param index the index that is no longer required
*/
public void removeIndexOrTransferOwnership(Session session, Index index) throws SQLException {
boolean stillNeeded = false;
for (int i = 0; constraints != null && i < constraints.size(); i++) {
Constraint cons = (Constraint) constraints.get(i);
if (cons.usesIndex(index)) {
cons.setIndexOwner(index);
database.update(session, cons);
stillNeeded = true;
}
}
if (!stillNeeded) {
database.removeSchemaObject(session, index);
}
}
}
......@@ -91,9 +91,9 @@ public class Server implements Runnable, ShutdownHandler {
* </li><li>-tcpShutdown {url} (shutdown the running TCP Server, URL example: tcp://localhost:9094)
* </li><li>-pg (start the PG Server)
* </li><li>-browser (start a browser and open a page to connect to the Web Server)
* </li><li>-log {true|false} (enable or disable logging)
* </li><li>-baseDir {directory} (sets the base directory for database files; not for H2 Console)
* </li><li>-ifExists {true|false} (only existing databases may be opened)
* </li><li>-log {true|false} (enable or disable logging, for all servers)
* </li><li>-baseDir {directory} (sets the base directory for H2 databases, for all servers)
* </li><li>-ifExists {true|false} (only existing databases may be opened, for all servers)
* </li><li>-ftp (start the FTP Server)
* </li></ul>
* For each Server, additional options are available:
......
......@@ -151,14 +151,20 @@ java org.h2.test.TestAll timer
/*
implement max_query_time and use it for TestCrashAPI
recovery tool: bad blocks should be converted to INSERT INTO SYSTEM_ERRORS(...),
and things should go into the .trace.db file
link from h2 console to sourceError
add sourceError as an official link
RECOVER=2 should backup the database, run recovery, open the database
Recovery should work with encrypted databases
java org.h2.tool.Server -baseDir C:\temp\dbtest
web console: jdbc:h2:~/chin
C:\temp\dbtest\~
Automate real power off tests
Adjust cache memory usage
At startup, when corrupted, say if LOG=0 was used before
Extend tests that simulate power off
Automate real power off tests
timer test
// test with garbage at the end of the log file (must be consistently detected as such)
// TestRandomSQL is too random; most statements fails
......@@ -175,6 +181,9 @@ Roadmap:
History:
Statement.setQueryTimeout() is now supported. New session setting QUERY_TIMEOUT, and new system property h2.maxQueryTimeout.
Changing the transaction log level (SET LOG) is now written to the trace file by default.
In a SQL script, primary key constraints are now ordered before foreign key constraints.
It was not possible to create a referential constraint to a table in a different schema in some situations.
*/
......
......@@ -18,7 +18,7 @@ import org.h2.tools.DeleteDbFiles;
/**
* A recovery test that checks the consistency of a database (if it exists),
* then deletes everything and runs in an endless loop executing random operations.
* This loop is usually stopped by turning off the computer.
* This loop is usually stopped by switching off the computer.
*/
public class TestTimer extends TestBase {
......@@ -86,11 +86,25 @@ public class TestTimer extends TestBase {
Connection conn = getConnection("timer");
// TODO validate transactions
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE IF NOT EXISTS TEST(ID IDENTITY, NAME VARCHAR)");
ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
rs.next();
int count = rs.getInt(1);
println("row count: " + count);
int real = 0;
rs = stat.executeQuery("SELECT * FROM TEST");
while (rs.next()) {
real++;
}
if (real != count) {
println("real count: " + real);
throw new Error("COUNT(*)=" + count + " SELECT=" + real);
}
rs = stat.executeQuery("SCRIPT");
while (rs.next()) {
rs.getString(1);
}
conn.close();
println("done, rows: " + count);
} catch (Throwable e) {
logError("validate", e);
backup();
......
......@@ -2417,11 +2417,10 @@ alter table test add constraint fk foreign key(parent) references(id);
select TABLE_NAME, NON_UNIQUE, INDEX_NAME, ORDINAL_POSITION, COLUMN_NAME, CARDINALITY, PRIMARY_KEY from INFORMATION_SCHEMA.INDEXES;
> TABLE_NAME NON_UNIQUE INDEX_NAME ORDINAL_POSITION COLUMN_NAME CARDINALITY PRIMARY_KEY
> ---------- ---------- ------------- ---------------- ----------- ----------- -----------
> TEST FALSE FK_INDEX_2 1 ID 0 FALSE
> TEST FALSE NU_INDEX_2 1 PARENT 0 FALSE
> TEST FALSE PRIMARY_KEY_2 1 ID 0 TRUE
> TEST TRUE NI 1 PARENT 0 FALSE
> rows: 4
> rows: 3
select SEQUENCE_NAME, CURRENT_VALUE, INCREMENT, IS_GENERATED, REMARKS from INFORMATION_SCHEMA.SEQUENCES;
> SEQUENCE_NAME CURRENT_VALUE INCREMENT IS_GENERATED REMARKS
......@@ -6977,10 +6976,13 @@ DROP TABLE PARENT;
DROP TABLE CHILD;
> ok
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR, PARENT INT, CONSTRAINT P FOREIGN KEY(PARENT) REFERENCES(ID));
CREATE TABLE TEST(ID INT, CONSTRAINT PK PRIMARY KEY(ID), NAME VARCHAR, PARENT INT, CONSTRAINT P FOREIGN KEY(PARENT) REFERENCES(ID));
> ok
ALTER TABLE TEST DROP PRIMARY KEY;
> exception
ALTER TABLE TEST DROP CONSTRAINT PK;
> ok
INSERT INTO TEST VALUES(1, 'Frank', 1);
......
create schema Contact;
CREATE TABLE Account (id BIGINT);
CREATE TABLE Person (id BIGINT, FOREIGN KEY (id) REFERENCES Account(id));
CREATE TABLE Contact.Contact (id BIGINT, FOREIGN KEY (id) REFERENCES public.Person(id));
drop schema contact;
drop table account, person;
create schema Contact;
CREATE TABLE Account (id BIGINT primary key);
CREATE TABLE Person (id BIGINT primary key, FOREIGN KEY (id) REFERENCES Account);
CREATE TABLE Contact.Contact (id BIGINT primary key, FOREIGN KEY (id) REFERENCES public.Person);
drop schema contact;
drop table account, person;
select extract(hour from timestamp '2001-02-03 14:15:16');
> 14;
select extract(hour from '2001-02-03 14:15:16');
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论