org.h2.constraint.ConstraintCheck Maven / Gradle / Ivy
/*
* Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (https://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.constraint;
import java.util.HashSet;
import org.h2.api.ErrorCode;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.index.Index;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* A check constraint.
*/
public class ConstraintCheck extends Constraint {
private TableFilter filter;
private Expression expr;
public ConstraintCheck(Schema schema, int id, String name, Table table) {
super(schema, id, name, table);
}
@Override
public Type getConstraintType() {
return Constraint.Type.CHECK;
}
public void setTableFilter(TableFilter filter) {
this.filter = filter;
}
public void setExpression(Expression expr) {
this.expr = expr;
}
@Override
public String getCreateSQLForCopy(Table forTable, String quotedName) {
StringBuilder buff = new StringBuilder("ALTER TABLE ");
forTable.getSQL(buff, true).append(" ADD CONSTRAINT ");
if (forTable.isHidden()) {
buff.append("IF NOT EXISTS ");
}
buff.append(quotedName);
if (comment != null) {
buff.append(" COMMENT ");
StringUtils.quoteStringSQL(buff, comment);
}
buff.append(" CHECK(");
expr.getUnenclosedSQL(buff, true).append(") NOCHECK");
return buff.toString();
}
private String getShortDescription() {
StringBuilder builder = new StringBuilder().append(getName()).append(": ");
expr.getSQL(builder, false);
return builder.toString();
}
@Override
public String getCreateSQLWithoutIndexes() {
return getCreateSQL();
}
@Override
public String getCreateSQL() {
return getCreateSQLForCopy(table, getSQL(true));
}
@Override
public void removeChildrenAndResources(Session session) {
table.removeConstraint(this);
database.removeMeta(session, getId());
filter = null;
expr = null;
table = null;
invalidate();
}
@Override
public void checkRow(Session session, Table t, Row oldRow, Row newRow) {
if (newRow == null) {
return;
}
boolean b;
try {
Value v;
synchronized (this) {
filter.set(newRow);
v = expr.getValue(session);
}
// Both TRUE and NULL are ok
b = v == ValueNull.INSTANCE || v.getBoolean();
} catch (DbException ex) {
throw DbException.get(ErrorCode.CHECK_CONSTRAINT_INVALID, ex,
getShortDescription());
}
if (!b) {
throw DbException.get(ErrorCode.CHECK_CONSTRAINT_VIOLATED_1,
getShortDescription());
}
}
@Override
public boolean usesIndex(Index index) {
return false;
}
@Override
public void setIndexOwner(Index index) {
DbException.throwInternalError(toString());
}
@Override
public HashSet getReferencedColumns(Table table) {
HashSet columns = new HashSet<>();
expr.isEverything(ExpressionVisitor.getColumnsVisitor(columns, table));
return columns;
}
public Expression getExpression() {
return expr;
}
@Override
public boolean isBefore() {
return true;
}
@Override
public void checkExistingData(Session session) {
if (session.getDatabase().isStarting()) {
// don't check at startup
return;
}
StringBuilder builder = new StringBuilder().append("SELECT 1 FROM ");
filter.getTable().getSQL(builder, true).append(" WHERE NOT(");
expr.getSQL(builder, true).append(')');
String sql = builder.toString();
ResultInterface r = session.prepare(sql).query(1);
if (r.next()) {
throw DbException.get(ErrorCode.CHECK_CONSTRAINT_VIOLATED_1, getName());
}
}
@Override
public Index getUniqueIndex() {
return null;
}
@Override
public void rebuild() {
// nothing to do
}
@Override
public boolean isEverything(ExpressionVisitor visitor) {
return expr.isEverything(visitor);
}
}