1 5 package org.h2.command.ddl; 6 7 import java.sql.SQLException ; 8 import java.util.Arrays ; 9 import java.util.HashSet ; 10 11 import org.h2.constraint.Constraint; 12 import org.h2.constraint.ConstraintCheck; 13 import org.h2.constraint.ConstraintReferential; 14 import org.h2.constraint.ConstraintUnique; 15 import org.h2.engine.Database; 16 import org.h2.engine.Right; 17 import org.h2.engine.Session; 18 import org.h2.expression.Expression; 19 import org.h2.index.Index; 20 import org.h2.index.IndexType; 21 import org.h2.message.Message; 22 import org.h2.schema.Schema; 23 import org.h2.table.Column; 24 import org.h2.table.Table; 25 import org.h2.table.TableFilter; 26 import org.h2.util.ObjectArray; 27 28 31 32 public class AlterTableAddConstraint extends SchemaCommand { 33 34 public static final int CHECK = 0, UNIQUE = 1, REFERENTIAL = 2, PRIMARY_KEY = 3; 35 public static final int REFERENTIAL_INTEGRITY_TRUE = 4; 36 public static final int REFERENTIAL_INTEGRITY_FALSE = 5; 37 private int type; 38 private String constraintName; 39 private String tableName; 40 private String [] columnNames; 41 private int deleteAction; 42 private int updateAction; 43 private Schema refSchema; 44 private String refTableName; 45 private String [] refColumnNames; 46 private Expression checkExpression; 47 private Index index, refIndex; 48 private String comment; 49 50 public AlterTableAddConstraint(Session session, Schema schema) { 51 super(session, schema); 52 } 53 54 private String generateConstraintName(int id) throws SQLException { 55 if(constraintName == null) { 56 constraintName = getSchema().getUniqueConstraintName(); 57 } 58 return constraintName; 59 } 60 61 public int update() throws SQLException { 62 session.commit(); 63 Database db = session.getDatabase(); 64 if(getSchema().findConstraint(constraintName)!=null) { 65 throw Message.getSQLException(Message.CONSTRAINT_ALREADY_EXISTS_1, 66 constraintName); 67 } 68 Constraint constraint; 69 Table table = getSchema().getTableOrView(session, tableName); 70 session.getUser().checkRight(table, Right.ALL); 71 table.lock(session, true); 72 switch(type) { 73 case CHECK: { 74 int id = getObjectId(true, true); 75 String name = generateConstraintName(id); 76 ConstraintCheck check = new ConstraintCheck(getSchema(), id, name, table); 77 TableFilter filter = new TableFilter(session, table, null, false); 78 checkExpression.mapColumns(filter, 0); 79 checkExpression = checkExpression.optimize(session); 80 check.setExpression(checkExpression); 81 check.setTableFilter(filter); 82 constraint = check; 83 break; 84 } 85 case UNIQUE: { 86 Column[] columns = table.getColumns(columnNames); 87 boolean isOwner = false; 88 if(index != null && canUseUniqueIndex(index, table, columns)) { 89 isOwner = true; 90 index.getIndexType().setBelongsToConstraint(true); 91 } else { 92 index = getUniqueIndex(table, columns); 93 if(index == null) { 94 index = createIndex(table, columns, true); 95 isOwner = true; 96 } 97 } 98 int id = getObjectId(true, true); 99 String name = generateConstraintName(id); 100 ConstraintUnique unique = new ConstraintUnique(getSchema(), id, name, table); 101 unique.setColumns(columns); 102 unique.setIndex(index, isOwner); 103 constraint = unique; 104 break; 105 } 106 case REFERENTIAL: { 107 Table refTable = refSchema.getTableOrView(session, refTableName); 108 session.getUser().checkRight(refTable, Right.ALL); 109 boolean isOwner = false; 110 Column[] columns = table.getColumns(columnNames); 111 if(index != null && canUseIndex(index, table, columns)) { 112 isOwner = true; 113 index.getIndexType().setBelongsToConstraint(true); 114 } else { 115 index = getIndex(table, columns); 116 if(index == null) { 117 index = createIndex(table, columns, false); 118 isOwner = true; 119 } 120 } 121 Column[] refColumns; 122 if(refColumnNames == null) { 123 Index refIdx = refTable.getPrimaryKey(); 124 refColumns = refIdx.getColumns(); 125 } else { 126 refColumns = refTable.getColumns(refColumnNames); 127 } 128 if(refColumns.length != columns.length) { 129 throw Message.getSQLException(Message.COLUMN_COUNT_DOES_NOT_MATCH); 130 } 131 boolean isRefOwner = false; 132 if(refIndex != null && refIndex.getTable() == refTable) { 133 isRefOwner = true; 134 refIndex.getIndexType().setBelongsToConstraint(true); 135 } else { 136 refIndex = null; 137 } 138 if(refIndex == null) { 139 refIndex = getUniqueIndex(refTable, refColumns); 140 if(refIndex == null) { 141 refIndex = createIndex(refTable, refColumns, true); 142 isRefOwner = true; 143 } 144 } 145 int id = getObjectId(true, true); 146 String name = generateConstraintName(id); 147 ConstraintReferential ref = new ConstraintReferential(getSchema(), id, name, table); 148 ref.setColumns(columns); 149 ref.setIndex(index, isOwner); 150 ref.setRefTable(refTable); 151 ref.setRefColumns(refColumns); 152 ref.setRefIndex(refIndex, isRefOwner); 153 constraint = ref; 154 refTable.addConstraint(constraint); 155 ref.setDeleteAction(session, deleteAction); 156 ref.setUpdateAction(session, updateAction); 157 break; 158 } 159 case REFERENTIAL_INTEGRITY_TRUE: 160 table.setCheckForeignKeyConstraints(true); 161 return 0; 162 case REFERENTIAL_INTEGRITY_FALSE: 163 table.setCheckForeignKeyConstraints(false); 164 return 0; 165 default: 166 throw Message.getInternalError("type="+type); 167 } 168 constraint.setComment(comment); 170 db.addSchemaObject(session, constraint); 171 table.addConstraint(constraint); 172 return 0; 173 } 174 175 private Index createIndex(Table t, Column[] cols, boolean unique) throws SQLException { 176 int indexId = getObjectId(true, false); 177 IndexType indexType; 178 if(unique) { 179 indexType = IndexType.createUnique(t.isPersistent(), false); 181 } else { 182 indexType = IndexType.createNonUnique(t.isPersistent()); 184 } 185 indexType.setBelongsToConstraint(true); 186 String prefix = constraintName == null ? "CONSTRAINT" : constraintName; 187 String indexName = getSchema().getUniqueIndexName(prefix + "_INDEX_"); 188 return t.addIndex(session, indexName, indexId, cols, indexType, Index.EMPTY_HEAD, null); 189 } 190 191 public void setDeleteAction(int action) { 192 this.deleteAction = action; 193 } 194 195 public void setUpdateAction(int action) { 196 this.updateAction = action; 197 } 198 199 private Index getUniqueIndex(Table t, Column[] cols) { 200 ObjectArray list = t.getIndexes(); 201 for(int i=0; i<list.size(); i++) { 202 Index index = (Index) list.get(i); 203 if(canUseUniqueIndex(index, t, cols)) { 204 return index; 205 } 206 } 207 return null; 208 } 209 210 private boolean canUseUniqueIndex(Index index, Table table, Column[] cols) { 211 if(index.getTable() != table || !index.getIndexType().isUnique()) { 212 return false; 213 } 214 if(index.getIndexType().belongsToConstraint()) { 215 return false; 217 } 218 Column[] indexCols = index.getColumns(); 219 if(indexCols.length > cols.length) { 220 return false; 221 } 222 HashSet set = new HashSet (Arrays.asList(cols)); 223 for(int j=0; j<indexCols.length; j++) { 224 if(!set.contains(indexCols[j])) { 227 return false; 228 } 229 } 230 return true; 231 } 232 233 private Index getIndex(Table t, Column[] cols) { 234 ObjectArray list = t.getIndexes(); 235 for(int i=0; i<list.size(); i++) { 236 Index index = (Index) list.get(i); 237 if(canUseIndex(index, t, cols)) { 238 return index; 239 } 240 } 241 return null; 242 } 243 244 private boolean canUseIndex(Index index, Table table, Column[] cols) { 245 if(index.getTable() != table || index.getCreateSQL() == null) { 246 return false; 248 } 249 Column[] indexCols = index.getColumns(); 250 if(indexCols.length < cols.length) { 251 return false; 252 } 253 for(int j=0; j<cols.length; j++) { 254 int idx = index.getColumnIndex(cols[j]); 258 if(idx < 0 || idx >= cols.length) { 259 return false; 260 } 261 } 262 return true; 263 } 264 265 public void setConstraintName(String constraintName) { 266 this.constraintName = constraintName; 267 } 268 269 public void setType(int type) { 270 this.type = type; 271 } 272 273 public void setCheckExpression(Expression expression) { 274 this.checkExpression = expression; 275 } 276 277 public void setTableName(String tableName) { 278 this.tableName = tableName; 279 } 280 281 public void setColumnNames(String [] columnNames) { 282 this.columnNames = columnNames; 283 } 284 285 public void setRefTableName(Schema refSchema, String ref) { 286 this.refSchema = refSchema; 287 this.refTableName = ref; 288 } 289 290 public void setRefColumnNames(String [] cols) { 291 this.refColumnNames = cols; 292 } 293 294 public void setIndex(Index index) { 295 this.index = index; 296 } 297 298 public void setRefIndex(Index refIndex) { 299 this.refIndex = refIndex; 300 } 301 302 public void setComment(String comment) { 303 this.comment = comment; 304 } 305 306 } 307 | Popular Tags |