org.h2.constraint.ConstraintDomain Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of h2-mvstore Show documentation
Show all versions of h2-mvstore Show documentation
Fork of h2database to maintain Java 8 compatibility
The newest version!
/*
* Copyright 2004-2023 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.command.Parser;
import org.h2.command.ddl.AlterDomain;
import org.h2.command.query.AllColumnsForPlan;
import org.h2.engine.SessionLocal;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.index.Index;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.schema.Domain;
import org.h2.schema.Schema;
import org.h2.table.Column;
import org.h2.table.PlanItem;
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 domain constraint.
*/
public class ConstraintDomain extends Constraint {
private Domain domain;
private Expression expr;
private DomainColumnResolver resolver;
public ConstraintDomain(Schema schema, int id, String name, Domain domain) {
super(schema, id, name, null);
this.domain = domain;
resolver = new DomainColumnResolver(domain.getDataType());
}
@Override
public Type getConstraintType() {
return Constraint.Type.DOMAIN;
}
/**
* Returns the domain of this constraint.
*
* @return the domain
*/
public Domain getDomain() {
return domain;
}
/**
* Set the expression.
*
* @param session the session
* @param expr the expression
*/
public void setExpression(SessionLocal session, Expression expr) {
expr.mapColumns(resolver, 0, Expression.MAP_INITIAL);
expr = expr.optimize(session);
// check if the column is mapped
synchronized (this) {
resolver.setValue(ValueNull.INSTANCE);
expr.getValue(session);
}
this.expr = expr;
}
@Override
public String getCreateSQLWithoutIndexes() {
return getCreateSQL();
}
@Override
public String getCreateSQL() {
StringBuilder builder = new StringBuilder("ALTER DOMAIN ");
domain.getSQL(builder, DEFAULT_SQL_FLAGS).append(" ADD CONSTRAINT ");
getSQL(builder, DEFAULT_SQL_FLAGS);
if (comment != null) {
builder.append(" COMMENT ");
StringUtils.quoteStringSQL(builder, comment);
}
builder.append(" CHECK");
expr.getEnclosedSQL(builder, DEFAULT_SQL_FLAGS).append(" NOCHECK");
return builder.toString();
}
@Override
public void removeChildrenAndResources(SessionLocal session) {
domain.removeConstraint(this);
database.removeMeta(session, getId());
domain = null;
expr = null;
invalidate();
}
@Override
public void checkRow(SessionLocal session, Table t, Row oldRow, Row newRow) {
throw DbException.getInternalError(toString());
}
/**
* Check the specified value.
*
* @param session
* the session
* @param value
* the value to check
*/
public void check(SessionLocal session, Value value) {
Value v;
synchronized (this) {
resolver.setValue(value);
v = expr.getValue(session);
}
// Both TRUE and NULL are OK
if (v.isFalse()) {
throw DbException.get(ErrorCode.CHECK_CONSTRAINT_VIOLATED_1, expr.getTraceSQL());
}
}
/**
* Get the check constraint expression for this column.
*
* @param session the session
* @param columnName the column name
* @return the expression
*/
public Expression getCheckConstraint(SessionLocal session, String columnName) {
String sql;
if (columnName != null) {
synchronized (this) {
try {
resolver.setColumnName(columnName);
sql = expr.getSQL(DEFAULT_SQL_FLAGS);
} finally {
resolver.resetColumnName();
}
}
return new Parser(session).parseExpression(sql);
} else {
synchronized (this) {
sql = expr.getSQL(DEFAULT_SQL_FLAGS);
}
return new Parser(session).parseDomainConstraintExpression(sql);
}
}
@Override
public boolean usesIndex(Index index) {
return false;
}
@Override
public void setIndexOwner(Index index) {
throw DbException.getInternalError(toString());
}
@Override
public HashSet getReferencedColumns(Table table) {
HashSet columns = new HashSet<>();
expr.isEverything(ExpressionVisitor.getColumnsVisitor(columns, table));
return columns;
}
@Override
public Expression getExpression() {
return expr;
}
@Override
public boolean isBefore() {
return true;
}
@Override
public void checkExistingData(SessionLocal session) {
if (session.getDatabase().isStarting()) {
// don't check at startup
return;
}
new CheckExistingData(session, domain);
}
@Override
public void rebuild() {
// nothing to do
}
@Override
public boolean isEverything(ExpressionVisitor visitor) {
return expr.isEverything(visitor);
}
private class CheckExistingData {
private final SessionLocal session;
CheckExistingData(SessionLocal session, Domain domain) {
this.session = session;
checkDomain(null, domain);
}
private boolean checkColumn(Domain domain, Column targetColumn) {
Table table = targetColumn.getTable();
TableFilter filter = new TableFilter(session, table, null, true, null, 0, null);
TableFilter[] filters = { filter };
PlanItem item = filter.getBestPlanItem(session, filters, 0, new AllColumnsForPlan(filters));
filter.setPlanItem(item);
filter.prepare();
filter.startQuery(session);
filter.reset();
while (filter.next()) {
check(session, filter.getValue(targetColumn));
}
return false;
}
private boolean checkDomain(Domain domain, Domain targetDomain) {
AlterDomain.forAllDependencies(session, targetDomain, this::checkColumn, this::checkDomain, false);
return false;
}
}
}