提交 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);
......
......@@ -22,7 +22,6 @@ import org.h2.engine.Database;
import org.h2.engine.Mode;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.schema.Sequence;
import org.h2.security.BlockCipher;
import org.h2.security.CipherFactory;
......@@ -33,14 +32,12 @@ import org.h2.table.LinkSchema;
import org.h2.table.TableFilter;
import org.h2.tools.CompressTool;
import org.h2.tools.Csv;
import org.h2.tools.SimpleResultSet;
import org.h2.util.MathUtils;
import org.h2.util.MemoryUtils;
import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
import org.h2.util.RandomUtils;
import org.h2.util.StringUtils;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueBoolean;
......@@ -86,45 +83,42 @@ public class Function extends Expression implements FunctionCall {
public static final int IFNULL = 200, CASEWHEN = 201, CONVERT = 202, CAST = 203, COALESCE = 204, NULLIF = 205,
CASE = 206, NEXTVAL = 207, CURRVAL = 208, ARRAY_GET = 209, CSVREAD = 210, CSVWRITE = 211,
MEMORY_FREE = 212, MEMORY_USED = 213, LOCK_MODE = 214, SCHEMA = 215, SESSION_ID = 216, ARRAY_LENGTH = 217,
LINK_SCHEMA = 218, TABLE = 219, LEAST = 220, GREATEST = 221, TABLE_DISTINCT = 222, CANCEL_SESSION = 223, SET = 224;
LINK_SCHEMA = 218, GREATEST = 219, LEAST = 220, CANCEL_SESSION = 221, SET = 222, TABLE = 223, TABLE_DISTINCT = 224;
private static final int VAR_ARGS = -1;
private static HashMap functions;
private static final HashMap FUNCTIONS = new HashMap();
private static final HashMap DATE_PART = new HashMap();
private static final SimpleDateFormat FORMAT_DAYNAME = new SimpleDateFormat("EEEE", Locale.ENGLISH);
private static final SimpleDateFormat FORMAT_MONTHNAME = new SimpleDateFormat("MMMM", Locale.ENGLISH);
private static final char[] SOUNDEX_INDEX = new char[128];
private FunctionInfo info;
private Expression[] args;
protected Expression[] args;
private ObjectArray varArgs;
private int dataType, scale;
private long precision;
private int displaySize;
private Column[] columnList;
private Database database;
private static HashMap datePart;
private static final SimpleDateFormat FORMAT_DAYNAME = new SimpleDateFormat("EEEE", Locale.ENGLISH);
private static final SimpleDateFormat FORMAT_MONTHNAME = new SimpleDateFormat("MMMM", Locale.ENGLISH);
private static final char[] SOUNDEX_INDEX = new char[128];
static {
datePart = new HashMap();
datePart.put("YY", ObjectUtils.getInteger(Calendar.YEAR));
datePart.put("YEAR", ObjectUtils.getInteger(Calendar.YEAR));
datePart.put("MM", ObjectUtils.getInteger(Calendar.MONTH));
datePart.put("MONTH", ObjectUtils.getInteger(Calendar.MONTH));
datePart.put("DD", ObjectUtils.getInteger(Calendar.DATE));
datePart.put("DAY", ObjectUtils.getInteger(Calendar.DATE));
datePart.put("HH", ObjectUtils.getInteger(Calendar.HOUR_OF_DAY));
datePart.put("HOUR", ObjectUtils.getInteger(Calendar.HOUR_OF_DAY));
datePart.put("MI", ObjectUtils.getInteger(Calendar.MINUTE));
datePart.put("MINUTE", ObjectUtils.getInteger(Calendar.MINUTE));
datePart.put("SS", ObjectUtils.getInteger(Calendar.SECOND));
datePart.put("SECOND", ObjectUtils.getInteger(Calendar.SECOND));
datePart.put("MS", ObjectUtils.getInteger(Calendar.MILLISECOND));
datePart.put("MILLISECOND", ObjectUtils.getInteger(Calendar.MILLISECOND));
}
static {
// DATE_PART
DATE_PART.put("YY", ObjectUtils.getInteger(Calendar.YEAR));
DATE_PART.put("YEAR", ObjectUtils.getInteger(Calendar.YEAR));
DATE_PART.put("MM", ObjectUtils.getInteger(Calendar.MONTH));
DATE_PART.put("MONTH", ObjectUtils.getInteger(Calendar.MONTH));
DATE_PART.put("DD", ObjectUtils.getInteger(Calendar.DATE));
DATE_PART.put("DAY", ObjectUtils.getInteger(Calendar.DATE));
DATE_PART.put("HH", ObjectUtils.getInteger(Calendar.HOUR_OF_DAY));
DATE_PART.put("HOUR", ObjectUtils.getInteger(Calendar.HOUR_OF_DAY));
DATE_PART.put("MI", ObjectUtils.getInteger(Calendar.MINUTE));
DATE_PART.put("MINUTE", ObjectUtils.getInteger(Calendar.MINUTE));
DATE_PART.put("SS", ObjectUtils.getInteger(Calendar.SECOND));
DATE_PART.put("SECOND", ObjectUtils.getInteger(Calendar.SECOND));
DATE_PART.put("MS", ObjectUtils.getInteger(Calendar.MILLISECOND));
DATE_PART.put("MILLISECOND", ObjectUtils.getInteger(Calendar.MILLISECOND));
// SOUNDEX_INDEX
String index = "7AEIOUY8HW1BFPV2CGJKQSXZ3DT4L5MN6R";
char number = 0;
for (int i = 0; i < index.length(); i++) {
......@@ -136,10 +130,8 @@ public class Function extends Expression implements FunctionCall {
SOUNDEX_INDEX[Character.toLowerCase(c)] = number;
}
}
}
static {
functions = new HashMap();
// FUNCTIONS
addFunction("ABS", ABS, 1, Value.NULL);
addFunction("ACOS", ACOS, 1, Value.DOUBLE);
addFunction("ASIN", ASIN, 1, Value.DOUBLE);
......@@ -284,12 +276,14 @@ public class Function extends Expression implements FunctionCall {
addFunctionNotConst("SESSION_ID", SESSION_ID, 0, Value.INT);
addFunction("ARRAY_LENGTH", ARRAY_LENGTH, 1, Value.INT);
addFunction("LINK_SCHEMA", LINK_SCHEMA, 6, Value.RESULT_SET);
addFunctionWithNull("TABLE", TABLE, VAR_ARGS, Value.RESULT_SET);
addFunctionWithNull("TABLE_DISTINCT", TABLE_DISTINCT, VAR_ARGS, Value.RESULT_SET);
addFunctionWithNull("LEAST", LEAST, VAR_ARGS, Value.NULL);
addFunctionWithNull("GREATEST", GREATEST, VAR_ARGS, Value.NULL);
addFunction("CANCEL_SESSION", CANCEL_SESSION, 1, Value.BOOLEAN);
addFunction("SET", SET, 2, Value.NULL, false, false);
// TableFunction
addFunctionWithNull("TABLE", TABLE, VAR_ARGS, Value.RESULT_SET);
addFunctionWithNull("TABLE_DISTINCT", TABLE_DISTINCT, VAR_ARGS, Value.RESULT_SET);
}
private static void addFunction(String name, int type, int parameterCount, int dataType,
......@@ -301,7 +295,7 @@ public class Function extends Expression implements FunctionCall {
info.dataType = dataType;
info.nullIfParameterIsNull = nullIfParameterIsNull;
info.isDeterministic = isDeterm;
functions.put(name, info);
FUNCTIONS.put(name, info);
}
private static void addFunctionNotConst(String name, int type, int parameterCount, int dataType) {
......@@ -316,15 +310,25 @@ public class Function extends Expression implements FunctionCall {
addFunction(name, type, parameterCount, dataType, false, true);
}
public static FunctionInfo getFunctionInfo(String name) {
return (FunctionInfo) FUNCTIONS.get(name);
}
public static Function getFunction(Database database, String name) throws SQLException {
FunctionInfo info = (FunctionInfo) functions.get(name);
FunctionInfo info = getFunctionInfo(name);
if (info == null) {
return null;
}
return new Function(database, info);
switch(info.type) {
case TABLE:
case TABLE_DISTINCT:
return new TableFunction(database, info);
default:
return new Function(database, info);
}
}
private Function(Database database, FunctionInfo info) {
protected Function(Database database, FunctionInfo info) {
this.database = database;
this.info = info;
if (info.parameterCount == VAR_ARGS) {
......@@ -638,12 +642,6 @@ public class Function extends Expression implements FunctionCall {
result = v0;
break;
}
case TABLE:
result = getTable(session, args, false, false);
break;
case TABLE_DISTINCT:
result = getTable(session, args, false, true);
break;
case MEMORY_FREE:
session.getUser().checkAdmin();
result = ValueInt.get(MemoryUtils.getMemoryFree());
......@@ -1130,7 +1128,7 @@ public class Function extends Expression implements FunctionCall {
// }
private static int getDatePart(String part) throws SQLException {
Integer p = (Integer) datePart.get(StringUtils.toUpperEnglish(part));
Integer p = (Integer) DATE_PART.get(StringUtils.toUpperEnglish(part));
if (p == null) {
throw Message.getSQLException(ErrorCode.INVALID_VALUE_2, new String[] { "date part", part });
}
......@@ -1431,67 +1429,69 @@ public class Function extends Expression implements FunctionCall {
}
}
protected void checkParameterCount(int len) throws SQLException {
int min = 0, max = Integer.MAX_VALUE;
switch (info.type) {
case COALESCE:
case CSVREAD:
case LEAST:
case GREATEST:
min = 1;
break;
case NOW:
case CURRENT_TIMESTAMP:
case RAND:
max = 1;
break;
case COMPRESS:
case LTRIM:
case RTRIM:
case TRIM:
max = 2;
break;
case REPLACE:
case LOCATE:
case INSTR:
case SUBSTR:
case SUBSTRING:
case LPAD:
case RPAD:
min = 2;
max = 3;
break;
case CASE:
case CONCAT:
case CSVWRITE:
min = 2;
break;
case XMLNODE:
min = 1;
max = 3;
break;
case FORMATDATETIME:
case PARSEDATETIME:
min = 2;
max = 4;
break;
case CURRVAL:
case NEXTVAL:
min = 1;
max = 2;
break;
default:
throw Message.getInternalError("type=" + info.type);
}
boolean ok = (len >= min) && (len <= max);
if (!ok) {
throw Message.getSQLException(ErrorCode.INVALID_PARAMETER_COUNT_2, new String[] { info.name,
min + ".." + max });
}
}
public void doneWithParameters() throws SQLException {
if (info.parameterCount == VAR_ARGS) {
int len = varArgs.size();
int min = 0, max = Integer.MAX_VALUE;
switch (info.type) {
case COALESCE:
case CSVREAD:
case TABLE:
case TABLE_DISTINCT:
case LEAST:
case GREATEST:
min = 1;
break;
case NOW:
case CURRENT_TIMESTAMP:
case RAND:
max = 1;
break;
case COMPRESS:
case LTRIM:
case RTRIM:
case TRIM:
max = 2;
break;
case REPLACE:
case LOCATE:
case INSTR:
case SUBSTR:
case SUBSTRING:
case LPAD:
case RPAD:
min = 2;
max = 3;
break;
case CASE:
case CONCAT:
case CSVWRITE:
min = 2;
break;
case XMLNODE:
min = 1;
max = 3;
break;
case FORMATDATETIME:
case PARSEDATETIME:
min = 2;
max = 4;
break;
case CURRVAL:
case NEXTVAL:
min = 1;
max = 2;
break;
default:
throw Message.getInternalError("type=" + info.type);
}
boolean ok = (len >= min) && (len <= max);
if (!ok) {
throw Message.getSQLException(ErrorCode.INVALID_PARAMETER_COUNT_2, new String[] { info.name,
min + ".." + max });
}
checkParameterCount(len);
args = new Expression[len];
varArgs.toArray(args);
varArgs = null;
......@@ -1716,19 +1716,6 @@ public class Function extends Expression implements FunctionCall {
buff.append(args[1].getSQL());
break;
}
case TABLE_DISTINCT:
case TABLE: {
for (int i = 0; i < args.length; i++) {
if (i > 0) {
buff.append(", ");
}
buff.append(columnList[i].getCreateSQL());
buff.append("=");
Expression e = args[i];
buff.append(e.getSQL());
}
break;
}
default: {
for (int i = 0; i < args.length; i++) {
if (i > 0) {
......@@ -1784,12 +1771,6 @@ public class Function extends Expression implements FunctionCall {
ValueResultSet vr = ValueResultSet.getCopy(rs, 0);
return vr;
}
case TABLE: {
return getTable(session, args, true, false);
}
case TABLE_DISTINCT: {
return getTable(session, args, true, true);
}
default:
break;
}
......@@ -1839,81 +1820,4 @@ public class Function extends Expression implements FunctionCall {
return cost;
}
public ValueResultSet getTable(Session session, Expression[] args, boolean onlyColumnList, boolean distinct) throws SQLException {
int len = columnList.length;
Expression[] header = new Expression[len];
Database db = session.getDatabase();
for (int i = 0; i < len; i++) {
Column c = columnList[i];
ExpressionColumn col = new ExpressionColumn(db, c);
header[i] = col;
}
LocalResult result = new LocalResult(session, header, len);
if (distinct) {
result.setDistinct();
}
if (!onlyColumnList) {
Value[][] list = new Value[len][];
int rowCount = 0;
for (int i = 0; i < len; i++) {
Value v = args[i].getValue(session);
if (v == ValueNull.INSTANCE) {
list[i] = new Value[0];
} else {
ValueArray array = (ValueArray) v.convertTo(Value.ARRAY);
Value[] l = array.getList();
list[i] = l;
rowCount = Math.max(rowCount, l.length);
}
}
for (int row = 0; row < rowCount; row++) {
Value[] r = new Value[len];
for (int j = 0; j < len; j++) {
Value[] l = list[j];
Value v;
if (l.length <= row) {
v = ValueNull.INSTANCE;
} else {
Column c = columnList[j];
v = l[row];
v = v.convertTo(c.getType());
v = v.convertPrecision(c.getPrecision());
v = v.convertScale(true, c.getScale());
}
r[j] = v;
}
result.addRow(r);
}
}
result.done();
ValueResultSet vr = ValueResultSet.get(getSimpleResultSet(result, Integer.MAX_VALUE));
return vr;
}
SimpleResultSet getSimpleResultSet(LocalResult rs, int maxrows) throws SQLException {
int columnCount = rs.getVisibleColumnCount();
SimpleResultSet simple = new SimpleResultSet();
for (int i = 0; i < columnCount; i++) {
String name = rs.getColumnName(i);
int sqlType = DataType.convertTypeToSQLType(rs.getColumnType(i));
int precision = MathUtils.convertLongToInt(rs.getColumnPrecision(i));
int scale = rs.getColumnScale(i);
simple.addColumn(name, sqlType, precision, scale);
}
rs.reset();
for (int i = 0; i < maxrows && rs.next(); i++) {
Object[] list = new Object[columnCount];
for (int j = 0; j < columnCount; j++) {
list[j] = rs.currentRow()[j].getObject();
}
simple.addRow(list);
}
return simple;
}
public void setColumns(ObjectArray columns) {
this.columnList = new Column[columns.size()];
columns.toArray(columnList);
}
}
......@@ -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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论