1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.constraint; |
7 | |
8 | import java.util.HashSet; |
9 | import java.util.Iterator; |
10 | |
11 | import org.h2.api.ErrorCode; |
12 | import org.h2.engine.Session; |
13 | import org.h2.expression.Expression; |
14 | import org.h2.expression.ExpressionVisitor; |
15 | import org.h2.index.Index; |
16 | import org.h2.message.DbException; |
17 | import org.h2.result.ResultInterface; |
18 | import org.h2.result.Row; |
19 | import org.h2.schema.Schema; |
20 | import org.h2.table.Column; |
21 | import org.h2.table.Table; |
22 | import org.h2.table.TableFilter; |
23 | import org.h2.util.New; |
24 | import org.h2.util.StringUtils; |
25 | |
26 | /** |
27 | * A check constraint. |
28 | */ |
29 | public class ConstraintCheck extends Constraint { |
30 | |
31 | private TableFilter filter; |
32 | private Expression expr; |
33 | |
34 | public ConstraintCheck(Schema schema, int id, String name, Table table) { |
35 | super(schema, id, name, table); |
36 | } |
37 | |
38 | @Override |
39 | public String getConstraintType() { |
40 | return Constraint.CHECK; |
41 | } |
42 | |
43 | public void setTableFilter(TableFilter filter) { |
44 | this.filter = filter; |
45 | } |
46 | |
47 | public void setExpression(Expression expr) { |
48 | this.expr = expr; |
49 | } |
50 | |
51 | @Override |
52 | public String getCreateSQLForCopy(Table forTable, String quotedName) { |
53 | StringBuilder buff = new StringBuilder("ALTER TABLE "); |
54 | buff.append(forTable.getSQL()).append(" ADD CONSTRAINT "); |
55 | if (forTable.isHidden()) { |
56 | buff.append("IF NOT EXISTS "); |
57 | } |
58 | buff.append(quotedName); |
59 | if (comment != null) { |
60 | buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment)); |
61 | } |
62 | buff.append(" CHECK").append(StringUtils.enclose(expr.getSQL())) |
63 | .append(" NOCHECK"); |
64 | return buff.toString(); |
65 | } |
66 | |
67 | private String getShortDescription() { |
68 | return getName() + ": " + expr.getSQL(); |
69 | } |
70 | |
71 | @Override |
72 | public String getCreateSQLWithoutIndexes() { |
73 | return getCreateSQL(); |
74 | } |
75 | |
76 | @Override |
77 | public String getCreateSQL() { |
78 | return getCreateSQLForCopy(table, getSQL()); |
79 | } |
80 | |
81 | @Override |
82 | public void removeChildrenAndResources(Session session) { |
83 | table.removeConstraint(this); |
84 | database.removeMeta(session, getId()); |
85 | filter = null; |
86 | expr = null; |
87 | table = null; |
88 | invalidate(); |
89 | } |
90 | |
91 | @Override |
92 | public void checkRow(Session session, Table t, Row oldRow, Row newRow) { |
93 | if (newRow == null) { |
94 | return; |
95 | } |
96 | filter.set(newRow); |
97 | Boolean b; |
98 | try { |
99 | b = expr.getValue(session).getBoolean(); |
100 | } catch (DbException ex) { |
101 | throw DbException.get(ErrorCode.CHECK_CONSTRAINT_INVALID, ex, |
102 | getShortDescription()); |
103 | } |
104 | // Both TRUE and NULL are ok |
105 | if (Boolean.FALSE.equals(b)) { |
106 | throw DbException.get(ErrorCode.CHECK_CONSTRAINT_VIOLATED_1, |
107 | getShortDescription()); |
108 | } |
109 | } |
110 | |
111 | @Override |
112 | public boolean usesIndex(Index index) { |
113 | return false; |
114 | } |
115 | |
116 | @Override |
117 | public void setIndexOwner(Index index) { |
118 | DbException.throwInternalError(); |
119 | } |
120 | |
121 | @Override |
122 | public HashSet<Column> getReferencedColumns(Table table) { |
123 | HashSet<Column> columns = New.hashSet(); |
124 | expr.isEverything(ExpressionVisitor.getColumnsVisitor(columns)); |
125 | for (Iterator<Column> it = columns.iterator(); it.hasNext();) { |
126 | if (it.next().getTable() != table) { |
127 | it.remove(); |
128 | } |
129 | } |
130 | return columns; |
131 | } |
132 | |
133 | public Expression getExpression() { |
134 | return expr; |
135 | } |
136 | |
137 | @Override |
138 | public boolean isBefore() { |
139 | return true; |
140 | } |
141 | |
142 | @Override |
143 | public void checkExistingData(Session session) { |
144 | if (session.getDatabase().isStarting()) { |
145 | // don't check at startup |
146 | return; |
147 | } |
148 | String sql = "SELECT 1 FROM " + filter.getTable().getSQL() + |
149 | " WHERE NOT(" + expr.getSQL() + ")"; |
150 | ResultInterface r = session.prepare(sql).query(1); |
151 | if (r.next()) { |
152 | throw DbException.get(ErrorCode.CHECK_CONSTRAINT_VIOLATED_1, getName()); |
153 | } |
154 | } |
155 | |
156 | @Override |
157 | public Index getUniqueIndex() { |
158 | return null; |
159 | } |
160 | |
161 | @Override |
162 | public void rebuild() { |
163 | // nothing to do |
164 | } |
165 | |
166 | @Override |
167 | public boolean isEverything(ExpressionVisitor visitor) { |
168 | return expr.isEverything(visitor); |
169 | } |
170 | |
171 | } |