--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.FKInfo\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.execute;\r
+\r
+import org.apache.derby.catalog.UUID;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;\r
+\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+import org.apache.derby.iapi.services.monitor.Monitor;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.io.StoredFormatIds;\r
+import org.apache.derby.iapi.services.io.FormatIdUtil;\r
+import org.apache.derby.iapi.services.io.ArrayUtil;\r
+import org.apache.derby.iapi.services.io.Formatable;\r
+\r
+import java.io.StreamCorruptedException;\r
+import java.io.ObjectOutput;\r
+import java.io.ObjectInput;\r
+import java.io.IOException;\r
+\r
+import java.util.Vector;\r
+\r
+/**\r
+ * This is a simple class used to store the run time information\r
+ * about a foreign key. Used by DML to figure out what to\r
+ * check.\r
+ *\r
+ */\r
+public class FKInfo implements Formatable \r
+{\r
+ /********************************************************\r
+ **\r
+ ** This class implements Formatable. That means that it\r
+ ** can write itself to and from a formatted stream. If\r
+ ** you add more fields to this class, make sure that you\r
+ ** also write/read them with the writeExternal()/readExternal()\r
+ ** methods.\r
+ **\r
+ ** If, inbetween releases, you add more fields to this class,\r
+ ** then you should bump the version number emitted by the getTypeFormatId()\r
+ ** method. OR, since this is something that is used\r
+ ** in stored prepared statements, it is ok to change it\r
+ ** if you make sure that stored prepared statements are\r
+ ** invalidated across releases.\r
+ **\r
+ ********************************************************/\r
+\r
+ public static final int FOREIGN_KEY = 1;\r
+ public static final int REFERENCED_KEY = 2;\r
+\r
+ /*\r
+ ** See the constructor for the meaning of these fields\r
+ */\r
+ public String fkConstraintNames[];\r
+ public String tableName;\r
+ public int type;\r
+ public UUID refUUID;\r
+ public long refConglomNumber;\r
+ public UUID[] fkUUIDs;\r
+ public long[] fkConglomNumbers;\r
+ public boolean[] fkIsSelfReferencing;\r
+ public int[] colArray;\r
+ public int stmtType;\r
+ public RowLocation rowLocation;\r
+ public int[] raRules;\r
+\r
+ /**\r
+ * Niladic constructor for Formattable\r
+ */\r
+ public FKInfo() {}\r
+\r
+ /**\r
+ * Consructor for FKInfo\r
+ *\r
+ * @param fkConstraintNames the foreign key constraint names\r
+ * @param tableName the name of the table being modified\r
+ * @param stmtType the type of the statement: e.g. StatementType.INSERT\r
+ * @param type either FKInfo.REFERENCED_KEY or FKInfo.FOREIGN_KEY\r
+ * @param refUUID UUID of the referenced constraint\r
+ * @param refConglomNumber congomerate number of the referenced key\r
+ * @param fkUUIDs an array of fkUUIDs of backing indexes. if\r
+ * FOREIGN_KEY, then just one element, the backing\r
+ * index of the referrenced keys. if REFERENCED_KEY,\r
+ * then all the foreign keys\r
+ * @param fkConglomNumbers array of conglomerate numbers, corresponds\r
+ * to fkUUIDs\r
+ * @param fkIsSelfReferencing array of conglomerate booleans indicating\r
+ * whether the fk references a key in the same table\r
+ * @param colArray map of columns to the base row that DML\r
+ * is changing. 1 based. Note that this maps the\r
+ * constraint index to a row in the target table of\r
+ * the current dml operation.\r
+ * @param rowLocation a row location template for the target table\r
+ * used to pass in a template row to tc.openScan()\r
+ */\r
+ public FKInfo(\r
+ String[] fkConstraintNames,\r
+ String tableName,\r
+ int stmtType,\r
+ int type,\r
+ UUID refUUID,\r
+ long refConglomNumber,\r
+ UUID[] fkUUIDs,\r
+ long[] fkConglomNumbers,\r
+ boolean[] fkIsSelfReferencing,\r
+ int[] colArray,\r
+ RowLocation rowLocation,\r
+ int[] raRules\r
+ )\r
+ {\r
+ this.fkConstraintNames = fkConstraintNames;\r
+ this.tableName = tableName;\r
+ this.stmtType = stmtType;\r
+ this.type = type;\r
+ this.refUUID = refUUID;\r
+ this.refConglomNumber = refConglomNumber;\r
+ this.fkUUIDs = fkUUIDs;\r
+ this.fkConglomNumbers = fkConglomNumbers;\r
+ this.fkIsSelfReferencing = fkIsSelfReferencing;\r
+ this.colArray = colArray;\r
+ this.rowLocation = rowLocation;\r
+ this.raRules = raRules;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (fkUUIDs.length != fkConglomNumbers.length)\r
+ {\r
+ SanityManager.THROWASSERT("number of ForeignKey UUIDS ("+fkUUIDs.length+\r
+ ") doesn't match the number of conglomerate numbers"+\r
+ " ("+fkConglomNumbers.length+")");\r
+ }\r
+ if (type == FOREIGN_KEY)\r
+ {\r
+ SanityManager.ASSERT(fkUUIDs.length == 1, "unexpected number of fkUUIDs for a foreign key, should only have the uuid of the key it references");\r
+ }\r
+ else if (type == REFERENCED_KEY)\r
+ {\r
+ SanityManager.ASSERT(fkUUIDs.length >= 1, "too few fkUUIDs for a referenced key, expect at least one foreign key");\r
+ }\r
+ else\r
+ {\r
+ SanityManager.THROWASSERT("bad type: "+type);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Comb through the FKInfo structures and pick out the\r
+ * ones that have columns that intersect with the input\r
+ * columns.\r
+ *\r
+ * @param fkInfo array of fkinfos\r
+ * @param cols array of columns\r
+ * @param addAllTypeIsFK take all with type == FORIEGN_KEY\r
+ *\r
+ * @return array of relevant fkinfos\r
+ */\r
+ public static FKInfo[] chooseRelevantFKInfos\r
+ ( \r
+ FKInfo[] fkInfo, \r
+ int[] cols,\r
+ boolean addAllTypeIsFK)\r
+ {\r
+ if (fkInfo == null)\r
+ {\r
+ return (FKInfo[])null;\r
+ }\r
+\r
+ Vector newfksVector = new Vector();\r
+ FKInfo[] newfks = null;\r
+\r
+ /*\r
+ ** For each FKInfo\r
+ */\r
+ for (int i = 0; i < fkInfo.length; i++)\r
+ {\r
+ if (addAllTypeIsFK && \r
+ (fkInfo[i].type == FOREIGN_KEY))\r
+ {\r
+ newfksVector.addElement(fkInfo[i]);\r
+ continue;\r
+ }\r
+ \r
+ int fkcollen = fkInfo[i].colArray.length;\r
+ for (int fkCols = 0; fkCols < fkcollen; fkCols++)\r
+ {\r
+ for (int chcol = 0; chcol < cols.length; chcol++)\r
+ {\r
+ /*\r
+ ** If any column intersects, the FKInfo is\r
+ ** relevant.\r
+ */\r
+ if (fkInfo[i].colArray[fkCols] == cols[chcol])\r
+ {\r
+ newfksVector.addElement(fkInfo[i]);\r
+ \r
+ // go to the next fk\r
+ fkCols = fkcollen;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ \r
+ /*\r
+ ** Now convert the vector into an array.\r
+ */\r
+ int size = newfksVector.size();\r
+ if (size > 0)\r
+ {\r
+ newfks = new FKInfo[size];\r
+ for (int i = 0; i < size; i++)\r
+ {\r
+ newfks[i] = (FKInfo)newfksVector.elementAt(i);\r
+ }\r
+ }\r
+ return newfks;\r
+ }\r
+ \r
+ //////////////////////////////////////////////\r
+ //\r
+ // FORMATABLE\r
+ //\r
+ //////////////////////////////////////////////\r
+ /**\r
+ * Write this object out\r
+ *\r
+ * @param out write bytes here\r
+ *\r
+ * @exception IOException thrown on error\r
+ */\r
+ public void writeExternal(ObjectOutput out) throws IOException\r
+ {\r
+ /*\r
+ ** Row locations cannot be written unless they\r
+ ** have a valid value. So we'll just write out\r
+ ** the format id, and create a new RowLocation\r
+ ** when we read it back in.\r
+ */\r
+ FormatIdUtil.writeFormatIdInteger(out, rowLocation.getTypeFormatId());\r
+\r
+ out.writeObject(tableName);\r
+ out.writeInt(type);\r
+ out.writeInt(stmtType);\r
+ out.writeObject(refUUID);\r
+ out.writeLong(refConglomNumber);\r
+\r
+ ArrayUtil.writeArray(out, fkConstraintNames);\r
+ ArrayUtil.writeArray(out, fkUUIDs);\r
+ ArrayUtil.writeLongArray(out, fkConglomNumbers);\r
+ ArrayUtil.writeBooleanArray(out, fkIsSelfReferencing);\r
+ ArrayUtil.writeIntArray(out, colArray);\r
+ ArrayUtil.writeIntArray(out, raRules);\r
+ \r
+ }\r
+\r
+ /**\r
+ * Read this object from a stream of stored objects.\r
+ *\r
+ * @param in read this.\r
+ *\r
+ * @exception IOException thrown on error\r
+ * @exception ClassNotFoundException thrown on error\r
+ */\r
+ public void readExternal(ObjectInput in)\r
+ throws IOException, ClassNotFoundException\r
+ {\r
+ try\r
+ {\r
+ /*\r
+ ** Create a new RowLocation from the format id.\r
+ */\r
+ int formatid = FormatIdUtil.readFormatIdInteger(in);\r
+ rowLocation = (RowLocation)Monitor.newInstanceFromIdentifier(formatid);\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(rowLocation != null, "row location is null in readExternal");\r
+ }\r
+\r
+ tableName = (String)in.readObject();\r
+ type = in.readInt();\r
+ stmtType = in.readInt();\r
+ refUUID = (UUID)in.readObject();\r
+ refConglomNumber = in.readLong();\r
+\r
+ fkConstraintNames = new String[ArrayUtil.readArrayLength(in)];\r
+ ArrayUtil.readArrayItems(in, fkConstraintNames);\r
+\r
+ fkUUIDs = new UUID[ArrayUtil.readArrayLength(in)];\r
+ ArrayUtil.readArrayItems(in, fkUUIDs);\r
+\r
+ fkConglomNumbers = ArrayUtil.readLongArray(in);\r
+ fkIsSelfReferencing = ArrayUtil.readBooleanArray(in);\r
+ colArray = ArrayUtil.readIntArray(in);\r
+ raRules = ArrayUtil.readIntArray(in);\r
+ }\r
+ catch (StandardException exception)\r
+ {\r
+ throw new StreamCorruptedException(exception.toString());\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Get the formatID which corresponds to this class.\r
+ *\r
+ * @return the formatID of this class\r
+ */\r
+ public int getTypeFormatId() { return StoredFormatIds.FK_INFO_V01_ID; }\r
+\r
+ //////////////////////////////////////////////////////////////\r
+ //\r
+ // Misc\r
+ //\r
+ //////////////////////////////////////////////////////////////\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ StringBuffer str = new StringBuffer();\r
+ str.append("\nTableName:\t\t\t");\r
+ str.append(tableName);\r
+\r
+ str.append("\ntype:\t\t\t\t");\r
+ str.append((type == FOREIGN_KEY) ? "FOREIGN_KEY" : "REFERENCED_KEY");\r
+\r
+ str.append("\nReferenced Key UUID:\t\t"+refUUID);\r
+ str.append("\nReferenced Key ConglomNum:\t"+refConglomNumber);\r
+\r
+ str.append("\nForeign Key Names:\t\t(");\r
+ for (int i = 0; i < fkUUIDs.length; i++)\r
+ {\r
+ if (i > 0)\r
+ str.append(",");\r
+ \r
+ str.append(fkConstraintNames[i]);\r
+ }\r
+ str.append(")");\r
+\r
+ str.append("\nForeign Key UUIDS:\t\t(");\r
+ for (int i = 0; i < fkUUIDs.length; i++)\r
+ {\r
+ if (i > 0)\r
+ str.append(",");\r
+ \r
+ str.append(fkUUIDs[i]);\r
+ }\r
+ str.append(")");\r
+\r
+ str.append("\nForeign Key Conglom Nums:\t(");\r
+ for (int i = 0; i < fkConglomNumbers.length; i++)\r
+ {\r
+ if (i > 0)\r
+ str.append(",");\r
+ \r
+ str.append(fkConglomNumbers[i]);\r
+ }\r
+ str.append(")");\r
+ \r
+ str.append("\nForeign Key isSelfRef:\t\t(");\r
+ for (int i = 0; i < fkIsSelfReferencing.length; i++)\r
+ {\r
+ if (i > 0)\r
+ str.append(",");\r
+ \r
+ str.append(fkIsSelfReferencing[i]);\r
+ }\r
+ str.append(")");\r
+ \r
+ str.append("\ncolumn Array:\t\t\t(");\r
+ for (int i = 0; i < colArray.length; i++)\r
+ {\r
+ if (i > 0)\r
+ str.append(",");\r
+ \r
+ str.append(colArray[i]);\r
+ }\r
+ str.append(")\n");\r
+\r
+ return str.toString();\r
+ }\r
+ else\r
+ {\r
+ return "";\r
+ }\r
+ }\r
+}\r