/*
 * Copyright 2004-2019 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 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.
/** * 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; } filter.set(newRow); boolean b; try { Value 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<Column> getReferencedColumns(Table table) { HashSet<Column> 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); } }