001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.SetConstraintsConstantAction
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.execute;
023:
024: import org.apache.derby.iapi.services.context.ContextManager;
025:
026: import org.apache.derby.iapi.services.sanity.SanityManager;
027:
028: import org.apache.derby.catalog.UUID;
029: import org.apache.derby.iapi.services.uuid.UUIDFactory;
030:
031: import org.apache.derby.iapi.error.StandardException;
032:
033: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
034:
035: import org.apache.derby.iapi.sql.StatementType;
036:
037: import org.apache.derby.iapi.sql.dictionary.CheckConstraintDescriptor;
038: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
039: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
040: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList;
041: import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
042: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
043: import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
044: import org.apache.derby.iapi.sql.dictionary.ForeignKeyConstraintDescriptor;
045: import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
046: import org.apache.derby.iapi.sql.dictionary.ReferencedKeyConstraintDescriptor;
047: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
048: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
049:
050: import org.apache.derby.iapi.types.DataValueFactory;
051: import org.apache.derby.iapi.types.RowLocation;
052:
053: import org.apache.derby.iapi.sql.depend.DependencyManager;
054:
055: import org.apache.derby.iapi.sql.execute.ConstantAction;
056: import org.apache.derby.iapi.sql.execute.ExecIndexRow;
057: import org.apache.derby.iapi.sql.execute.ExecRow;
058:
059: import org.apache.derby.iapi.sql.Activation;
060:
061: import org.apache.derby.iapi.store.access.ConglomerateController;
062: import org.apache.derby.iapi.store.access.TransactionController;
063:
064: import org.apache.derby.iapi.services.io.FormatableBitSet;
065:
066: import java.util.Hashtable;
067: import java.util.Enumeration;
068:
069: /**
070: * This class describes actions that are performed for a
071: * set constraint at Execution time.
072: * <p>
073: * Note that the dependency action we send is SET_CONSTRAINTS
074: * rather than ALTER_TABLE. We do this because we want
075: * to distinguish SET_CONSTRAINTS from ALTER_TABLE for
076: * error messages.
077: *
078: * @author jamie
079: */
080: class SetConstraintsConstantAction extends DDLConstantAction {
081:
082: private boolean enable;
083: private boolean unconditionallyEnforce;
084:
085: /*
086: ** For the following fields, never access directly, always
087: ** get the constraint descript list via the private
088: ** method getConstraintDescriptorList() defined herein.
089: */
090: private ConstraintDescriptorList cdl;
091: private UUID[] cuuids;
092: private UUID[] tuuids;
093:
094: // CONSTRUCTORS
095: /**
096: *Boilerplate
097: *
098: * @param cdl ConstraintDescriptorList
099: * @param enable true == turn them on, false == turn them off
100: * @param unconditionallyEnforce Replication sets this to true at
101: * the end of REFRESH. This forces us
102: * to run the included foreign key constraints even
103: * if they're already marked ENABLED.
104: */
105: SetConstraintsConstantAction(ConstraintDescriptorList cdl,
106: boolean enable, boolean unconditionallyEnforce) {
107: this .cdl = cdl;
108: this .enable = enable;
109: this .unconditionallyEnforce = unconditionallyEnforce;
110: }
111:
112: //////////////////////////////////////////////////////////////
113: //
114: // OBJECT SHADOWS
115: //
116: //////////////////////////////////////////////////////////////
117:
118: public String toString() {
119: // Do not put this under SanityManager.DEBUG - it is needed for
120: // error reporting.
121: return "SET CONSTRAINTS";
122: }
123:
124: // INTERFACE METHODS
125:
126: /**
127: * This is the guts of the Execution-time logic for DROP CONSTRAINT.
128: *
129: * @see ConstantAction#executeConstantAction
130: *
131: * @exception StandardException Thrown on failure
132: */
133: public void executeConstantAction(Activation activation)
134: throws StandardException {
135: ConstraintDescriptor cd;
136: TableDescriptor td;
137: ConstraintDescriptorList tmpCdl;
138: boolean enforceThisConstraint;
139:
140: LanguageConnectionContext lcc = activation
141: .getLanguageConnectionContext();
142: DataDictionary dd = lcc.getDataDictionary();
143: DependencyManager dm = dd.getDependencyManager();
144: TransactionController tc = lcc.getTransactionExecute();
145:
146: tmpCdl = getConstraintDescriptorList(dd);
147:
148: int[] enabledCol = new int[1];
149: enabledCol[0] = ConstraintDescriptor.SYSCONSTRAINTS_STATE_FIELD;
150: /*
151: ** Inform the data dictionary that we are about to write to it.
152: ** There are several calls to data dictionary "get" methods here
153: ** that might be done in "read" mode in the data dictionary, but
154: ** it seemed safer to do this whole operation in "write" mode.
155: **
156: ** We tell the data dictionary we're done writing at the end of
157: ** the transaction.
158: */
159: dd.startWriting(lcc);
160:
161: /*
162: ** Callback to rep subclass
163: */
164: publishToTargets(activation);
165:
166: boolean skipFKs = false;
167:
168: /*
169: ** If the constraint list is empty, then we are getting
170: ** all constraints. In this case, don't bother going
171: ** after referencing keys (foreign keys) when we are
172: ** disabling a referenced key (pk or unique key) since
173: ** we know we'll hit it eventually.
174: */
175: if (tmpCdl == null) {
176: skipFKs = true;
177: tmpCdl = dd
178: .getConstraintDescriptors((TableDescriptor) null);
179: }
180:
181: Hashtable checkConstraintTables = null;
182: int cdlSize = tmpCdl.size();
183: for (int index = 0; index < cdlSize; index++) {
184: cd = tmpCdl.elementAt(index);
185:
186: /*
187: ** We are careful to enable this constraint before trying
188: ** to enable constraints that reference it. Similarly,
189: ** we disabled constraints that reference us before we
190: ** disable ourselves, to make sure everything works ok.
191: */
192: if (unconditionallyEnforce) {
193: enforceThisConstraint = true;
194: } else {
195: enforceThisConstraint = (enable && !cd.isEnabled());
196: }
197:
198: if (enforceThisConstraint) {
199: if (cd instanceof ForeignKeyConstraintDescriptor) {
200: validateFKConstraint(
201: (ForeignKeyConstraintDescriptor) cd, dd,
202: tc, lcc.getContextManager());
203: }
204: /*
205: ** For check constraints, we build up a list of check constriants
206: ** by table descriptor. Once we have collected them all, we
207: ** execute them in a single query per table descriptor.
208: */
209: else if (cd instanceof CheckConstraintDescriptor) {
210: td = cd.getTableDescriptor();
211:
212: if (checkConstraintTables == null) {
213: checkConstraintTables = new Hashtable(10);
214: }
215:
216: ConstraintDescriptorList tabCdl = (ConstraintDescriptorList) checkConstraintTables
217: .get(td.getUUID());
218: if (tabCdl == null) {
219: tabCdl = new ConstraintDescriptorList();
220: checkConstraintTables.put(td.getUUID(), tabCdl);
221: }
222: tabCdl.add(cd);
223: }
224: /*
225: ** If we are enabling a constraint, we need to issue
226: ** the invalidation on the underlying table rather than
227: ** the constraint we are enabling. This is because
228: ** stmts that were compiled against a disabled constraint
229: ** have no depedency on that disabled constriant.
230: */
231: dm.invalidateFor(cd.getTableDescriptor(),
232: DependencyManager.SET_CONSTRAINTS_ENABLE, lcc);
233: cd.setEnabled();
234: dd.updateConstraintDescriptor(cd, cd.getUUID(),
235: enabledCol, tc);
236: }
237:
238: /*
239: ** If we are dealing with a referenced constraint, then
240: ** we find all of the constraints that reference this constraint.
241: ** Turn them on/off based on what we are doing to this
242: ** constraint.
243: */
244: if (!skipFKs
245: && (cd instanceof ReferencedKeyConstraintDescriptor)) {
246: ForeignKeyConstraintDescriptor fkcd;
247: ReferencedKeyConstraintDescriptor refcd;
248: ConstraintDescriptorList fkcdl;
249:
250: refcd = (ReferencedKeyConstraintDescriptor) cd;
251: fkcdl = refcd
252: .getForeignKeyConstraints(ReferencedKeyConstraintDescriptor.ALL);
253:
254: int fkcdlSize = fkcdl.size();
255: for (int inner = 0; inner < fkcdlSize; inner++) {
256: fkcd = (ForeignKeyConstraintDescriptor) fkcdl
257: .elementAt(inner);
258: if (enable && !fkcd.isEnabled()) {
259: dm
260: .invalidateFor(
261: fkcd.getTableDescriptor(),
262: DependencyManager.SET_CONSTRAINTS_ENABLE,
263: lcc);
264: validateFKConstraint(fkcd, dd, tc, lcc
265: .getContextManager());
266: fkcd.setEnabled();
267: dd.updateConstraintDescriptor(fkcd, fkcd
268: .getUUID(), enabledCol, tc);
269: } else if (!enable && fkcd.isEnabled()) {
270: dm
271: .invalidateFor(
272: fkcd,
273: DependencyManager.SET_CONSTRAINTS_DISABLE,
274: lcc);
275: fkcd.setDisabled();
276: dd.updateConstraintDescriptor(fkcd, fkcd
277: .getUUID(), enabledCol, tc);
278: }
279: }
280: }
281:
282: if (!enable && cd.isEnabled()) {
283: dm.invalidateFor(cd,
284: DependencyManager.SET_CONSTRAINTS_DISABLE, lcc);
285: cd.setDisabled();
286: dd.updateConstraintDescriptor(cd, cd.getUUID(),
287: enabledCol, tc);
288: }
289: }
290:
291: validateAllCheckConstraints(lcc, checkConstraintTables);
292: }
293:
294: private void validateAllCheckConstraints(
295: LanguageConnectionContext lcc, Hashtable ht)
296: throws StandardException {
297: ConstraintDescriptorList cdl;
298: ConstraintDescriptor cd = null;
299: TableDescriptor td;
300: StringBuffer text;
301: StringBuffer constraintNames;
302:
303: if (ht == null) {
304: return;
305: }
306:
307: for (Enumeration e = ht.elements(); e.hasMoreElements();) {
308:
309: cdl = (ConstraintDescriptorList) e.nextElement();
310: text = null;
311: constraintNames = null;
312:
313: /*
314: ** Build up the text of all the constraints into one big
315: ** predicate. Also, we unfortunately have to build up a big
316: ** comma separated list of constraint names in case
317: ** there is an error (we are favoring speed over a very
318: ** explicit check constraint xxxx failed error message).
319: */
320: int cdlSize = cdl.size();
321: for (int index = 0; index < cdlSize; index++) {
322: cd = (CheckConstraintDescriptor) cdl.elementAt(index);
323: if (text == null) {
324: text = new StringBuffer("(").append(
325: cd.getConstraintText()).append(") ");
326: constraintNames = new StringBuffer(cd
327: .getConstraintName());
328: } else {
329: text.append(" AND (")
330: .append(cd.getConstraintText())
331: .append(") ");
332: constraintNames.append(", ").append(
333: cd.getConstraintName());
334: }
335: }
336:
337: if (SanityManager.DEBUG) {
338: SanityManager.ASSERT(text != null,
339: "internal error, badly built hastable");
340: }
341:
342: ConstraintConstantAction.validateConstraint(constraintNames
343: .toString(), text.toString(), cd
344: .getTableDescriptor(), lcc, true);
345: }
346: }
347:
348: /*
349: **
350: */
351: private void validateFKConstraint(
352: ForeignKeyConstraintDescriptor fk, DataDictionary dd,
353: TransactionController tc, ContextManager cm)
354: throws StandardException {
355: /*
356: ** Construct a template row
357: */
358: IndexRowGenerator irg = fk.getIndexConglomerateDescriptor(dd)
359: .getIndexDescriptor();
360: ExecIndexRow indexTemplateRow = irg.getIndexRowTemplate();
361: TableDescriptor td = fk.getTableDescriptor();
362: ExecRow baseRow = td.getEmptyExecRow(cm);
363: irg.getIndexRow(baseRow, getRowLocation(dd, td, tc),
364: indexTemplateRow, (FormatableBitSet) null);
365:
366: /*
367: ** The moment of truth
368: */
369: ConstraintConstantAction.validateFKConstraint(tc, dd, fk, fk
370: .getReferencedConstraint(), indexTemplateRow);
371: }
372:
373: /*
374: ** Get a row location template. Note that we are assuming that
375: ** the same row location can be used for all tables participating
376: ** in the fk. For example, if there are multiple foreign keys,
377: ** we are using the row location of one of the tables and assuming
378: ** that it is the right shape for all tables. Currently, this
379: ** is a legitimate assumption.
380: */
381: private RowLocation getRowLocation(DataDictionary dd,
382: TableDescriptor td, TransactionController tc)
383: throws StandardException {
384: RowLocation rl;
385: ConglomerateController heapCC = null;
386:
387: long tableId = td.getHeapConglomerateId();
388: heapCC = tc.openConglomerate(tableId, false, 0, tc.MODE_RECORD,
389: tc.ISOLATION_READ_COMMITTED);
390: try {
391: rl = heapCC.newRowLocationTemplate();
392: } finally {
393: heapCC.close();
394: }
395:
396: return rl;
397: }
398:
399: /*
400: ** Wrapper for constraint descriptor list -- always use
401: ** this to get the constriant descriptor list. It is
402: ** used to hide serialization.
403: */
404: private ConstraintDescriptorList getConstraintDescriptorList(
405: DataDictionary dd) throws StandardException {
406: if (cdl != null) {
407: return cdl;
408: }
409: if (tuuids == null) {
410: return null;
411: }
412:
413: /*
414: ** Reconstitute the cdl from the uuids
415: */
416: cdl = new ConstraintDescriptorList();
417:
418: for (int i = 0; i < tuuids.length; i++) {
419: TableDescriptor td = dd.getTableDescriptor(tuuids[i]);
420: if (SanityManager.DEBUG) {
421: if (td == null) {
422: SanityManager
423: .THROWASSERT("couldn't locate table descriptor "
424: + "in SET CONSTRAINTS for uuid "
425: + tuuids[i]);
426: }
427: }
428:
429: ConstraintDescriptor cd = dd.getConstraintDescriptorById(
430: td, cuuids[i]);
431:
432: if (SanityManager.DEBUG) {
433: if (cd == null) {
434: SanityManager
435: .THROWASSERT("couldn't locate constraint descriptor "
436: + " in SET CONSTRAINTS for uuid "
437: + cuuids[i]);
438: }
439: }
440:
441: cdl.add(cd);
442: }
443: return cdl;
444: }
445:
446: ///////////////////////////////////////////////
447: //
448: // MISC
449: //
450: ///////////////////////////////////////////////
451:
452: /**
453: * Do the work of publishing any this action to any
454: * replication targets. On a non-replicated source,
455: * this is a no-op.
456: *
457: * @param activation the activation
458: *
459: * @exception StandardException on error
460: */
461: protected void publishToTargets(Activation activation)
462: throws StandardException {
463: }
464: }
|