/* * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, * and the EPL 1.0 (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.constraint; import java.util.HashSet; import java.util.Iterator; 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.New; import org.h2.util.StringUtils; /** * 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 String getConstraintType() { return Constraint.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 "); buff.append(forTable.getSQL()).append(" ADD CONSTRAINT "); if (forTable.isHidden()) { buff.append("IF NOT EXISTS "); } buff.append(quotedName); if (comment != null) { buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment)); } buff.append(" CHECK").append(StringUtils.enclose(expr.getSQL())) .append(" NOCHECK"); return buff.toString(); } private String getShortDescription() { return getName() + ": " + expr.getSQL(); } @Override public String getCreateSQLWithoutIndexes() { return getCreateSQL(); } @Override public String getCreateSQL() { return getCreateSQLForCopy(table, getSQL()); } @Override public void removeChildrenAndResources(Session session) { table.removeConstraint(this); database.removeMeta(session, getId()); filter = null; expr = null; table = null; invalidate(); } //只用于insert和update @Override public void checkRow(Session session, Table t, Row oldRow, Row newRow) { if (newRow == null) { return; } filter.set(newRow); //为了在expr.getValue能取到当前newRow Boolean b; try { b = expr.getValue(session).getBoolean(); } catch (DbException ex) { throw DbException.get(ErrorCode.CHECK_CONSTRAINT_INVALID, ex, getShortDescription()); } // Both TRUE and NULL are ok if (Boolean.FALSE.equals(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<Column> getReferencedColumns(Table table) { HashSet<Column> columns = New.hashSet(); expr.isEverything(ExpressionVisitor.getColumnsVisitor(columns)); for (Iterator<Column> it = columns.iterator(); it.hasNext();) { if (it.next().getTable() != table) { it.remove(); } } return columns; } public Expression getExpression() { return expr; } @Override public boolean isBefore() { return true; } //通常是在构建约束对象之后马上根据CHECK和NOCHECK调用与不调用 @Override public void checkExistingData(Session session) { //比如用于alter时 if (session.getDatabase().isStarting()) { // don't check at startup return; } //用NOT,意思就是说只要找到一个反例就与约束冲突了 //比如,如果是CHECK f1 not null, //如果此时表中的f1字段存在null值,那么这个约束就创建失败 String sql = "SELECT 1 FROM " + filter.getTable().getSQL() + " WHERE NOT(" + expr.getSQL() + ")"; 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); } }