001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.FKInfo
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.catalog.UUID;
025:
026: import org.apache.derby.iapi.error.StandardException;
027:
028: import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
029:
030: import org.apache.derby.iapi.types.RowLocation;
031:
032: import org.apache.derby.iapi.services.monitor.Monitor;
033:
034: import org.apache.derby.iapi.services.sanity.SanityManager;
035: import org.apache.derby.iapi.services.io.StoredFormatIds;
036: import org.apache.derby.iapi.services.io.FormatIdUtil;
037: import org.apache.derby.iapi.services.io.ArrayUtil;
038: import org.apache.derby.iapi.services.io.Formatable;
039:
040: import java.io.StreamCorruptedException;
041: import java.io.ObjectOutput;
042: import java.io.ObjectInput;
043: import java.io.IOException;
044:
045: import java.util.Vector;
046:
047: /**
048: * This is a simple class used to store the run time information
049: * about a foreign key. Used by DML to figure out what to
050: * check.
051: *
052: * @author jamie
053: */
054: public class FKInfo implements Formatable {
055: /********************************************************
056: **
057: ** This class implements Formatable. That means that it
058: ** can write itself to and from a formatted stream. If
059: ** you add more fields to this class, make sure that you
060: ** also write/read them with the writeExternal()/readExternal()
061: ** methods.
062: **
063: ** If, inbetween releases, you add more fields to this class,
064: ** then you should bump the version number emitted by the getTypeFormatId()
065: ** method. OR, since this is something that is used
066: ** in stored prepared statements, it is ok to change it
067: ** if you make sure that stored prepared statements are
068: ** invalidated across releases.
069: **
070: ********************************************************/
071:
072: public static final int FOREIGN_KEY = 1;
073: public static final int REFERENCED_KEY = 2;
074:
075: /*
076: ** See the constructor for the meaning of these fields
077: */
078: public String fkConstraintNames[];
079: public String tableName;
080: public int type;
081: public UUID refUUID;
082: public long refConglomNumber;
083: public UUID[] fkUUIDs;
084: public long[] fkConglomNumbers;
085: public boolean[] fkIsSelfReferencing;
086: public int[] colArray;
087: public int stmtType;
088: public RowLocation rowLocation;
089: public int[] raRules;
090:
091: /**
092: * Niladic constructor for Formattable
093: */
094: public FKInfo() {
095: }
096:
097: /**
098: * Consructor for FKInfo
099: *
100: * @param fkConstraintNames the foreign key constraint names
101: * @param tableName the name of the table being modified
102: * @param stmtType the type of the statement: e.g. StatementType.INSERT
103: * @param type either FKInfo.REFERENCED_KEY or FKInfo.FOREIGN_KEY
104: * @param refUUID UUID of the referenced constraint
105: * @param refConglomNumber congomerate number of the referenced key
106: * @param fkUUIDs an array of fkUUIDs of backing indexes. if
107: * FOREIGN_KEY, then just one element, the backing
108: * index of the referrenced keys. if REFERENCED_KEY,
109: * then all the foreign keys
110: * @param fkConglomNumbers array of conglomerate numbers, corresponds
111: * to fkUUIDs
112: * @param fkIsSelfReferencing array of conglomerate booleans indicating
113: * whether the fk references a key in the same table
114: * @param colArray map of columns to the base row that DML
115: * is changing. 1 based. Note that this maps the
116: * constraint index to a row in the target table of
117: * the current dml operation.
118: * @param rowLocation a row location template for the target table
119: * used to pass in a template row to tc.openScan()
120: */
121: public FKInfo(String[] fkConstraintNames, String tableName,
122: int stmtType, int type, UUID refUUID,
123: long refConglomNumber, UUID[] fkUUIDs,
124: long[] fkConglomNumbers, boolean[] fkIsSelfReferencing,
125: int[] colArray, RowLocation rowLocation, int[] raRules) {
126: this .fkConstraintNames = fkConstraintNames;
127: this .tableName = tableName;
128: this .stmtType = stmtType;
129: this .type = type;
130: this .refUUID = refUUID;
131: this .refConglomNumber = refConglomNumber;
132: this .fkUUIDs = fkUUIDs;
133: this .fkConglomNumbers = fkConglomNumbers;
134: this .fkIsSelfReferencing = fkIsSelfReferencing;
135: this .colArray = colArray;
136: this .rowLocation = rowLocation;
137: this .raRules = raRules;
138:
139: if (SanityManager.DEBUG) {
140: if (fkUUIDs.length != fkConglomNumbers.length) {
141: SanityManager
142: .THROWASSERT("number of ForeignKey UUIDS ("
143: + fkUUIDs.length
144: + ") doesn't match the number of conglomerate numbers"
145: + " (" + fkConglomNumbers.length + ")");
146: }
147: if (type == FOREIGN_KEY) {
148: SanityManager
149: .ASSERT(
150: fkUUIDs.length == 1,
151: "unexpected number of fkUUIDs for a foreign key, should only have the uuid of the key it references");
152: } else if (type == REFERENCED_KEY) {
153: SanityManager
154: .ASSERT(fkUUIDs.length >= 1,
155: "too few fkUUIDs for a referenced key, expect at least one foreign key");
156: } else {
157: SanityManager.THROWASSERT("bad type: " + type);
158: }
159: }
160: }
161:
162: /**
163: * Comb through the FKInfo structures and pick out the
164: * ones that have columns that intersect with the input
165: * columns.
166: *
167: * @param fkInfo array of fkinfos
168: * @param cols array of columns
169: * @param addAllTypeIsFK take all with type == FORIEGN_KEY
170: *
171: * @return array of relevant fkinfos
172: */
173: public static FKInfo[] chooseRelevantFKInfos(FKInfo[] fkInfo,
174: int[] cols, boolean addAllTypeIsFK) {
175: if (fkInfo == null) {
176: return (FKInfo[]) null;
177: }
178:
179: Vector newfksVector = new Vector();
180: FKInfo[] newfks = null;
181:
182: /*
183: ** For each FKInfo
184: */
185: for (int i = 0; i < fkInfo.length; i++) {
186: if (addAllTypeIsFK && (fkInfo[i].type == FOREIGN_KEY)) {
187: newfksVector.addElement(fkInfo[i]);
188: continue;
189: }
190:
191: int fkcollen = fkInfo[i].colArray.length;
192: for (int fkCols = 0; fkCols < fkcollen; fkCols++) {
193: for (int chcol = 0; chcol < cols.length; chcol++) {
194: /*
195: ** If any column intersects, the FKInfo is
196: ** relevant.
197: */
198: if (fkInfo[i].colArray[fkCols] == cols[chcol]) {
199: newfksVector.addElement(fkInfo[i]);
200:
201: // go to the next fk
202: fkCols = fkcollen;
203: break;
204: }
205: }
206: }
207: }
208:
209: /*
210: ** Now convert the vector into an array.
211: */
212: int size = newfksVector.size();
213: if (size > 0) {
214: newfks = new FKInfo[size];
215: for (int i = 0; i < size; i++) {
216: newfks[i] = (FKInfo) newfksVector.elementAt(i);
217: }
218: }
219: return newfks;
220: }
221:
222: //////////////////////////////////////////////
223: //
224: // FORMATABLE
225: //
226: //////////////////////////////////////////////
227: /**
228: * Write this object out
229: *
230: * @param out write bytes here
231: *
232: * @exception IOException thrown on error
233: */
234: public void writeExternal(ObjectOutput out) throws IOException {
235: /*
236: ** Row locations cannot be written unless they
237: ** have a valid value. So we'll just write out
238: ** the format id, and create a new RowLocation
239: ** when we read it back in.
240: */
241: FormatIdUtil.writeFormatIdInteger(out, rowLocation
242: .getTypeFormatId());
243:
244: out.writeObject(tableName);
245: out.writeInt(type);
246: out.writeInt(stmtType);
247: out.writeObject(refUUID);
248: out.writeLong(refConglomNumber);
249:
250: ArrayUtil.writeArray(out, fkConstraintNames);
251: ArrayUtil.writeArray(out, fkUUIDs);
252: ArrayUtil.writeLongArray(out, fkConglomNumbers);
253: ArrayUtil.writeBooleanArray(out, fkIsSelfReferencing);
254: ArrayUtil.writeIntArray(out, colArray);
255: ArrayUtil.writeIntArray(out, raRules);
256:
257: }
258:
259: /**
260: * Read this object from a stream of stored objects.
261: *
262: * @param in read this.
263: *
264: * @exception IOException thrown on error
265: * @exception ClassNotFoundException thrown on error
266: */
267: public void readExternal(ObjectInput in) throws IOException,
268: ClassNotFoundException {
269: try {
270: /*
271: ** Create a new RowLocation from the format id.
272: */
273: int formatid = FormatIdUtil.readFormatIdInteger(in);
274: rowLocation = (RowLocation) Monitor
275: .newInstanceFromIdentifier(formatid);
276: if (SanityManager.DEBUG) {
277: SanityManager.ASSERT(rowLocation != null,
278: "row location is null in readExternal");
279: }
280:
281: tableName = (String) in.readObject();
282: type = in.readInt();
283: stmtType = in.readInt();
284: refUUID = (UUID) in.readObject();
285: refConglomNumber = in.readLong();
286:
287: fkConstraintNames = new String[ArrayUtil
288: .readArrayLength(in)];
289: ArrayUtil.readArrayItems(in, fkConstraintNames);
290:
291: fkUUIDs = new UUID[ArrayUtil.readArrayLength(in)];
292: ArrayUtil.readArrayItems(in, fkUUIDs);
293:
294: fkConglomNumbers = ArrayUtil.readLongArray(in);
295: fkIsSelfReferencing = ArrayUtil.readBooleanArray(in);
296: colArray = ArrayUtil.readIntArray(in);
297: raRules = ArrayUtil.readIntArray(in);
298: } catch (StandardException exception) {
299: throw new StreamCorruptedException(exception.toString());
300: }
301: }
302:
303: /**
304: * Get the formatID which corresponds to this class.
305: *
306: * @return the formatID of this class
307: */
308: public int getTypeFormatId() {
309: return StoredFormatIds.FK_INFO_V01_ID;
310: }
311:
312: //////////////////////////////////////////////////////////////
313: //
314: // Misc
315: //
316: //////////////////////////////////////////////////////////////
317: public String toString() {
318: if (SanityManager.DEBUG) {
319: StringBuffer str = new StringBuffer();
320: str.append("\nTableName:\t\t\t");
321: str.append(tableName);
322:
323: str.append("\ntype:\t\t\t\t");
324: str.append((type == FOREIGN_KEY) ? "FOREIGN_KEY"
325: : "REFERENCED_KEY");
326:
327: str.append("\nReferenced Key UUID:\t\t" + refUUID);
328: str.append("\nReferenced Key ConglomNum:\t"
329: + refConglomNumber);
330:
331: str.append("\nForeign Key Names:\t\t(");
332: for (int i = 0; i < fkUUIDs.length; i++) {
333: if (i > 0)
334: str.append(",");
335:
336: str.append(fkConstraintNames[i]);
337: }
338: str.append(")");
339:
340: str.append("\nForeign Key UUIDS:\t\t(");
341: for (int i = 0; i < fkUUIDs.length; i++) {
342: if (i > 0)
343: str.append(",");
344:
345: str.append(fkUUIDs[i]);
346: }
347: str.append(")");
348:
349: str.append("\nForeign Key Conglom Nums:\t(");
350: for (int i = 0; i < fkConglomNumbers.length; i++) {
351: if (i > 0)
352: str.append(",");
353:
354: str.append(fkConglomNumbers[i]);
355: }
356: str.append(")");
357:
358: str.append("\nForeign Key isSelfRef:\t\t(");
359: for (int i = 0; i < fkIsSelfReferencing.length; i++) {
360: if (i > 0)
361: str.append(",");
362:
363: str.append(fkIsSelfReferencing[i]);
364: }
365: str.append(")");
366:
367: str.append("\ncolumn Array:\t\t\t(");
368: for (int i = 0; i < colArray.length; i++) {
369: if (i > 0)
370: str.append(",");
371:
372: str.append(colArray[i]);
373: }
374: str.append(")\n");
375:
376: return str.toString();
377: } else {
378: return "";
379: }
380: }
381: }
|