001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.GenericRIChecker
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.sanity.SanityManager;
025: import org.apache.derby.iapi.error.StandardException;
026:
027: import org.apache.derby.iapi.types.DataValueDescriptor;
028: import org.apache.derby.iapi.types.RowLocation;
029: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
030: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
031: import org.apache.derby.iapi.sql.execute.ExecRow;
032: import org.apache.derby.iapi.sql.execute.ExecIndexRow;
033:
034: import org.apache.derby.iapi.store.access.ConglomerateController;
035: import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
036: import org.apache.derby.iapi.store.access.ScanController;
037: import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
038: import org.apache.derby.iapi.store.access.TransactionController;
039:
040: import org.apache.derby.iapi.services.io.FormatableBitSet;
041: import java.util.Enumeration;
042: import java.util.Hashtable;
043:
044: /**
045: * Generic implementation of a Referential Integrity
046: * checker. Abstract.
047: */
048: public abstract class GenericRIChecker {
049: protected FKInfo fkInfo;
050: protected DynamicCompiledOpenConglomInfo[] fkDcocis;
051: protected StaticCompiledOpenConglomInfo[] fkScocis;
052: protected DynamicCompiledOpenConglomInfo refDcoci;
053: protected StaticCompiledOpenConglomInfo refScoci;
054: protected TransactionController tc;
055:
056: private Hashtable scanControllers;
057: private int numColumns;
058: private IndexRow indexQualifierRow;
059:
060: /**
061: * @param tc the xact controller
062: * @param fkinfo the foreign key information
063: *
064: * @exception StandardException Thrown on failure
065: */
066: GenericRIChecker(TransactionController tc, FKInfo fkinfo)
067: throws StandardException {
068: this .fkInfo = fkinfo;
069: this .tc = tc;
070: scanControllers = new Hashtable();
071: numColumns = fkInfo.colArray.length;
072: indexQualifierRow = new IndexRow(numColumns);
073:
074: fkDcocis = new DynamicCompiledOpenConglomInfo[fkInfo.fkConglomNumbers.length];
075: fkScocis = new StaticCompiledOpenConglomInfo[fkInfo.fkConglomNumbers.length];
076: for (int index = 0; index < fkInfo.fkConglomNumbers.length; index++) {
077: fkDcocis[index] = tc
078: .getDynamicCompiledConglomInfo(fkInfo.fkConglomNumbers[index]);
079: fkScocis[index] = tc
080: .getStaticCompiledConglomInfo(fkInfo.fkConglomNumbers[index]);
081: }
082: refDcoci = tc
083: .getDynamicCompiledConglomInfo(fkInfo.refConglomNumber);
084: refScoci = tc
085: .getStaticCompiledConglomInfo(fkInfo.refConglomNumber);
086: }
087:
088: /**
089: * Check the validity of this row
090: *
091: * @param row the row to check
092: *
093: * @exception StandardException on error
094: */
095: abstract void doCheck(ExecRow row, boolean restrictCheckOnly)
096: throws StandardException;
097:
098: public void doCheck(ExecRow row) throws StandardException {
099: doCheck(row, false); //Check all the referential Actions
100: }
101:
102: /**
103: * Get a scan controller positioned using searchRow as
104: * the start/stop position. The assumption is that searchRow
105: * is of the same format as the index being opened.
106: * The scan is set up to return no columns.
107: * NOTE: We only need an instantaneous lock on the
108: * table that we are probing as we are just checking
109: * for the existance of a row. All updaters, whether
110: * to the primary or foreign key tables, will hold an
111: * X lock on the table that they are updating and will
112: * be probing the other table, so instantaneous locks
113: * will not change the semantics.
114: *
115: * RESOLVE: Due to the current RI implementation
116: * we cannot always get instantaneous locks. We
117: * will call a method to find out what kind of
118: * locking to do until the implementation changes.
119: *
120: * @param conglomNumber the particular conglomerate we
121: * are interested in
122: * @param searchRow the row to match
123: *
124: * @exception StandardException on error
125: */
126: protected ScanController getScanController(long conglomNumber,
127: StaticCompiledOpenConglomInfo scoci,
128: DynamicCompiledOpenConglomInfo dcoci, ExecRow searchRow)
129: throws StandardException {
130: int isoLevel = getRICheckIsolationLevel();
131: ScanController scan;
132: Long hashKey = new Long(conglomNumber);
133:
134: /*
135: ** If we haven't already opened this scan controller,
136: ** we'll open it now and stick it in the hash table.
137: */
138: if ((scan = (ScanController) scanControllers.get(hashKey)) == null) {
139: setupQualifierRow(searchRow);
140: scan = tc.openCompiledScan(false, // hold
141: 0, // read only
142: TransactionController.MODE_RECORD, // row locking
143: isoLevel, (FormatableBitSet) null, // retrieve all fields
144: indexQualifierRow.getRowArray(), // startKeyValue
145: ScanController.GE, // startSearchOp
146: null, // qualifier
147: indexQualifierRow.getRowArray(), // stopKeyValue
148: ScanController.GT, // stopSearchOp
149: scoci, dcoci);
150: scanControllers.put(hashKey, scan);
151: } else {
152: /*
153: ** If the base row is the same row as the previous
154: ** row, this call to setupQualfierRow is redundant,
155: ** but it is safer this way so we'll take the
156: ** marginal performance hit (marginal relative
157: ** to the index scans that we are making).
158: */
159: setupQualifierRow(searchRow);
160: scan.reopenScan(indexQualifierRow.getRowArray(), // startKeyValue
161: ScanController.GE, // startSearchOp
162: null, // qualifier
163: indexQualifierRow.getRowArray(), // stopKeyValue
164: ScanController.GT // stopSearchOp
165: );
166: }
167:
168: return scan;
169: }
170:
171: /*
172: ** Do reference copy for the qualifier row. No cloning.
173: ** So we cannot get another row until we are done with
174: ** this one.
175: */
176: private void setupQualifierRow(ExecRow baseRow) {
177: DataValueDescriptor[] indexColArray = indexQualifierRow
178: .getRowArray();
179: DataValueDescriptor[] baseColArray = baseRow.getRowArray();
180:
181: for (int i = 0; i < numColumns; i++) {
182: indexColArray[i] = baseColArray[fkInfo.colArray[i] - 1];
183: }
184: }
185:
186: /**
187: * Are any of the fields null in the row passed
188: * in. The only fields that are checked are those
189: * corresponding to the colArray in fkInfo.
190: */
191: boolean isAnyFieldNull(ExecRow baseRow) {
192: DataValueDescriptor[] baseColArray = baseRow.getRowArray();
193:
194: for (int i = 0; i < numColumns; i++) {
195: DataValueDescriptor storable = baseColArray[fkInfo.colArray[i] - 1];
196: if (storable.isNull()) {
197: return true;
198: }
199: }
200: return false;
201: }
202:
203: /**
204: * Clean up all scan controllers
205: *
206: * @exception StandardException on error
207: */
208: void close() throws StandardException {
209: Enumeration e = scanControllers.elements();
210: while (e.hasMoreElements()) {
211: ScanController scan = (ScanController) e.nextElement();
212: scan.close();
213: }
214: scanControllers.clear();
215: }
216:
217: /**
218: * Get the isolation level for the scan for
219: * the RI check.
220: *
221: * NOTE: The level will eventually be instantaneous
222: * locking once the implemenation changes.
223: *
224: * @return The isolation level for the scan for
225: * the RI check.
226: */
227: int getRICheckIsolationLevel() {
228: return TransactionController.ISOLATION_READ_COMMITTED_NOHOLDLOCK;
229: }
230: }
|