File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\command\ddl\AlterTableAddConstraint.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.command.ddl;
8
9 import java.util.ArrayList;
10 import java.util.HashSet;
11 import org.h2.command.CommandInterface;
12 import org.h2.constant.ErrorCode;
13 import org.h2.constraint.Constraint;
14 import org.h2.constraint.ConstraintCheck;
15 import org.h2.constraint.ConstraintReferential;
16 import org.h2.constraint.ConstraintUnique;
17 import org.h2.engine.Constants;
18 import org.h2.engine.Database;
19 import org.h2.engine.Right;
20 import org.h2.engine.Session;
21 import org.h2.expression.Expression;
22 import org.h2.index.Index;
23 import org.h2.index.IndexType;
24 import org.h2.message.DbException;
25 import org.h2.schema.Schema;
26 import org.h2.table.Column;
27 import org.h2.table.IndexColumn;
28 import org.h2.table.Table;
29 import org.h2.table.TableFilter;
30 import org.h2.util.New;
31
32 /**
33 * This class represents the statement
34 * ALTER TABLE ADD CONSTRAINT
35 */
36 public class AlterTableAddConstraint extends SchemaCommand {
37
38 private int type;
39 private String constraintName;
40 private String tableName;
41 private IndexColumn[] indexColumns;
42 private int deleteAction;
43 private int updateAction;
44 private Schema refSchema;
45 private String refTableName;
46 private IndexColumn[] refIndexColumns;
47 private Expression checkExpression;
48 private Index index, refIndex;
49 private String comment;
50 private boolean checkExisting;
51 private boolean primaryKeyHash;
52 private final boolean ifNotExists;
53
54 public AlterTableAddConstraint(Session session, Schema schema, boolean ifNotExists) {
55 super(session, schema);
56 this.ifNotExists = ifNotExists;
57 }
58
59 private String generateConstraintName(Table table) {
60 if (constraintName == null) {
61 constraintName = getSchema().getUniqueConstraintName(session, table);
62 }
63 return constraintName;
64 }
65
66 public int update() {
67 try {
68 return tryUpdate();
69 } finally {
70 getSchema().freeUniqueName(constraintName);
71 }
72 }
73
74 /**
75 * Try to execute the statement.
76 *
77 * @return the update count
78 */
79 private int tryUpdate() {
80 if (!transactional) {
81 session.commit(true);
82 }
83 Database db = session.getDatabase();
84 Table table = getSchema().getTableOrView(session, tableName);
85 if (getSchema().findConstraint(session, constraintName) != null) {
86 if (ifNotExists) {
87 return 0;
88 }
89 throw DbException.get(ErrorCode.CONSTRAINT_ALREADY_EXISTS_1, constraintName);
90 }
91 session.getUser().checkRight(table, Right.ALL);
92 db.lockMeta(session);
93 table.lock(session, true, true);
94 Constraint constraint;
95 switch (type) {
96 case CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_PRIMARY_KEY: {
97 IndexColumn.mapColumns(indexColumns, table);
98 index = table.findPrimaryKey();
99 ArrayList<Constraint> constraints = table.getConstraints();
100 for (int i = 0; constraints != null && i < constraints.size(); i++) {
101 Constraint c = constraints.get(i);
102 if (Constraint.PRIMARY_KEY.equals(c.getConstraintType())) {
103 throw DbException.get(ErrorCode.SECOND_PRIMARY_KEY);
104 }
105 }
106 if (index != null) {
107 // if there is an index, it must match with the one declared
108 // we don't test ascending / descending
109 IndexColumn[] pkCols = index.getIndexColumns();
110 if (pkCols.length != indexColumns.length) {
111 throw DbException.get(ErrorCode.SECOND_PRIMARY_KEY);
112 }
113 for (int i = 0; i < pkCols.length; i++) {
114 if (pkCols[i].column != indexColumns[i].column) {
115 throw DbException.get(ErrorCode.SECOND_PRIMARY_KEY);
116 }
117 }
118 }
119 if (index == null) {
120 IndexType indexType = IndexType.createPrimaryKey(table.isPersistIndexes(), primaryKeyHash);
121 String indexName = table.getSchema().getUniqueIndexName(session, table, Constants.PREFIX_PRIMARY_KEY);
122 int id = getObjectId();
123 try {
124 index = table.addIndex(session, indexName, id, indexColumns, indexType, true, null);
125 } finally {
126 getSchema().freeUniqueName(indexName);
127 }
128 }
129 index.getIndexType().setBelongsToConstraint(true);
130 int constraintId = getObjectId();
131 String name = generateConstraintName(table);
132 ConstraintUnique pk = new ConstraintUnique(getSchema(), constraintId, name, table, true);
133 pk.setColumns(indexColumns);
134 pk.setIndex(index, true);
135 constraint = pk;
136 break;
137 }
138 case CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_UNIQUE: {
139 IndexColumn.mapColumns(indexColumns, table);
140 boolean isOwner = false;
141 if (index != null && canUseUniqueIndex(index, table, indexColumns)) {
142 isOwner = true;
143 index.getIndexType().setBelongsToConstraint(true);
144 } else {
145 index = getUniqueIndex(table, indexColumns);
146 if (index == null) {
147 index = createIndex(table, indexColumns, true);
148 isOwner = true;
149 }
150 }
151 int id = getObjectId();
152 String name = generateConstraintName(table);
153 ConstraintUnique unique = new ConstraintUnique(getSchema(), id, name, table, false);
154 unique.setColumns(indexColumns);
155 unique.setIndex(index, isOwner);
156 constraint = unique;
157 break;
158 }
159 case CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_CHECK: {
160 int id = getObjectId();
161 String name = generateConstraintName(table);
162 ConstraintCheck check = new ConstraintCheck(getSchema(), id, name, table);
163 TableFilter filter = new TableFilter(session, table, null, false, null);
164 checkExpression.mapColumns(filter, 0);
165 checkExpression = checkExpression.optimize(session);
166 check.setExpression(checkExpression);
167 check.setTableFilter(filter);
168 constraint = check;
169 if (checkExisting) {
170 check.checkExistingData(session);
171 }
172 break;
173 }
174 case CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_REFERENTIAL: {
175 Table refTable = refSchema.getTableOrView(session, refTableName);
176 session.getUser().checkRight(refTable, Right.ALL);
177 if (!refTable.canReference()) {
178 throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1, "Reference " + refTable.getSQL());
179 }
180 boolean isOwner = false;
181 IndexColumn.mapColumns(indexColumns, table);
182 if (index != null && canUseIndex(index, table, indexColumns)) {
183 isOwner = true;
184 index.getIndexType().setBelongsToConstraint(true);
185 } else {
186 index = getIndex(table, indexColumns);
187 if (index == null) {
188 index = createIndex(table, indexColumns, false);
189 isOwner = true;
190 }
191 }
192 if (refIndexColumns == null) {
193 Index refIdx = refTable.getPrimaryKey();
194 refIndexColumns = refIdx.getIndexColumns();
195 } else {
196 IndexColumn.mapColumns(refIndexColumns, refTable);
197 }
198 if (refIndexColumns.length != indexColumns.length) {
199 throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
200 }
201 boolean isRefOwner = false;
202 if (refIndex != null && refIndex.getTable() == refTable &&
203 canUseIndex(refIndex, refTable, refIndexColumns)) {
204 isRefOwner = true;
205 refIndex.getIndexType().setBelongsToConstraint(true);
206 } else {
207 refIndex = null;
208 }
209 if (refIndex == null) {
210 refIndex = getIndex(refTable, refIndexColumns);
211 if (refIndex == null) {
212 refIndex = createIndex(refTable, refIndexColumns, true);
213 isRefOwner = true;
214 }
215 }
216 int id = getObjectId();
217 String name = generateConstraintName(table);
218 ConstraintReferential ref = new ConstraintReferential(getSchema(), id, name, table);
219 ref.setColumns(indexColumns);
220 ref.setIndex(index, isOwner);
221 ref.setRefTable(refTable);
222 ref.setRefColumns(refIndexColumns);
223 ref.setRefIndex(refIndex, isRefOwner);
224 if (checkExisting) {
225 ref.checkExistingData(session);
226 }
227 constraint = ref;
228 refTable.addConstraint(constraint);
229 ref.setDeleteAction(deleteAction);
230 ref.setUpdateAction(updateAction);
231 break;
232 }
233 default:
234 throw DbException.throwInternalError("type=" + type);
235 }
236 // parent relationship is already set with addConstraint
237 constraint.setComment(comment);
238 if (table.isTemporary() && !table.isGlobalTemporary()) {
239 session.addLocalTempTableConstraint(constraint);
240 } else {
241 db.addSchemaObject(session, constraint);
242 }
243 table.addConstraint(constraint);
244 return 0;
245 }
246
247 private Index createIndex(Table t, IndexColumn[] cols, boolean unique) {
248 int indexId = getObjectId();
249 IndexType indexType;
250 if (unique) {
251 // for unique constraints
252 indexType = IndexType.createUnique(t.isPersistIndexes(), false);
253 } else {
254 // constraints
255 indexType = IndexType.createNonUnique(t.isPersistIndexes());
256 }
257 indexType.setBelongsToConstraint(true);
258 String prefix = constraintName == null ? "CONSTRAINT" : constraintName;
259 String indexName = t.getSchema().getUniqueIndexName(session, t, prefix + "_INDEX_");
260 try {
261 return t.addIndex(session, indexName, indexId, cols, indexType, true, null);
262 } finally {
263 getSchema().freeUniqueName(indexName);
264 }
265 }
266
267 public void setDeleteAction(int action) {
268 this.deleteAction = action;
269 }
270
271 public void setUpdateAction(int action) {
272 this.updateAction = action;
273 }
274
275 private static Index getUniqueIndex(Table t, IndexColumn[] cols) {
276 for (Index idx : t.getIndexes()) {
277 if (canUseUniqueIndex(idx, t, cols)) {
278 return idx;
279 }
280 }
281 return null;
282 }
283
284 private static Index getIndex(Table t, IndexColumn[] cols) {
285 for (Index idx : t.getIndexes()) {
286 if (canUseIndex(idx, t, cols)) {
287 return idx;
288 }
289 }
290 return null;
291 }
292
293 private static boolean canUseUniqueIndex(Index idx, Table table, IndexColumn[] cols) {
294 if (idx.getTable() != table || !idx.getIndexType().isUnique()) {
295 return false;
296 }
297 Column[] indexCols = idx.getColumns();
298 if (indexCols.length > cols.length) {
299 return false;
300 }
301 HashSet<Column> set = New.hashSet();
302 for (IndexColumn c : cols) {
303 set.add(c.column);
304 }
305 for (Column c : indexCols) {
306 // all columns of the index must be part of the list,
307 // but not all columns of the list need to be part of the index
308 if (!set.contains(c)) {
309 return false;
310 }
311 }
312 return true;
313 }
314
315 private static boolean canUseIndex(Index existingIndex, Table table, IndexColumn[] cols) {
316 if (existingIndex.getTable() != table || existingIndex.getCreateSQL() == null) {
317 // can't use the scan index or index of another table
318 return false;
319 }
320 Column[] indexCols = existingIndex.getColumns();
321 if (indexCols.length < cols.length) {
322 return false;
323 }
324 for (IndexColumn col : cols) {
325 // all columns of the list must be part of the index,
326 // but not all columns of the index need to be part of the list
327 // holes are not allowed (index=a,b,c & list=a,b is ok; but list=a,c
328 // is not)
329 int idx = existingIndex.getColumnIndex(col.column);
330 if (idx < 0 || idx >= cols.length) {
331 return false;
332 }
333 }
334 return true;
335 }
336
337 public void setConstraintName(String constraintName) {
338 this.constraintName = constraintName;
339 }
340
341 public void setType(int type) {
342 this.type = type;
343 }
344
345 public int getType() {
346 return type;
347 }
348
349 public void setCheckExpression(Expression expression) {
350 this.checkExpression = expression;
351 }
352
353 public void setTableName(String tableName) {
354 this.tableName = tableName;
355 }
356
357 public void setIndexColumns(IndexColumn[] indexColumns) {
358 this.indexColumns = indexColumns;
359 }
360
361 public IndexColumn[] getIndexColumns() {
362 return indexColumns;
363 }
364
365 /**
366 * Set the referenced table.
367 *
368 * @param refSchema the schema
369 * @param ref the table name
370 */
371 public void setRefTableName(Schema refSchema, String ref) {
372 this.refSchema = refSchema;
373 this.refTableName = ref;
374 }
375
376 public void setRefIndexColumns(IndexColumn[] indexColumns) {
377 this.refIndexColumns = indexColumns;
378 }
379
380 public void setIndex(Index index) {
381 this.index = index;
382 }
383
384 public void setRefIndex(Index refIndex) {
385 this.refIndex = refIndex;
386 }
387
388 public void setComment(String comment) {
389 this.comment = comment;
390 }
391
392 public void setCheckExisting(boolean b) {
393 this.checkExisting = b;
394 }
395
396 public void setPrimaryKeyHash(boolean b) {
397 this.primaryKeyHash = b;
398 }
399
400 }
401