001: package com.quadcap.sql;
002:
003: /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.ByteArrayOutputStream;
042: import java.io.Externalizable;
043: import java.io.IOException;
044: import java.io.ObjectInput;
045: import java.io.ObjectOutput;
046:
047: import java.util.Vector;
048:
049: import java.sql.ResultSet;
050: import java.sql.SQLException;
051:
052: import com.quadcap.sql.index.Btree;
053: import com.quadcap.sql.index.BCursor;
054:
055: import com.quadcap.util.Debug;
056: import com.quadcap.util.Util;
057:
058: /**
059: * A hidden 'ExportedKeyConstraint' is created for tables that
060: * are referenced as foreign keys by other tables.
061: *
062: * @author Stan Bailes
063: */
064: public class ExportedKeyConstraint extends ForeignKeyConstraint
065: implements Externalizable {
066: transient ImportedKeyConstraint iConstraint;
067: transient String iConstraintName;
068:
069: String uConstraintName;
070:
071: /**
072: * Default constructor
073: */
074: public ExportedKeyConstraint() {
075: }
076:
077: /**
078: * Explicit constructor for matching ImportedKeyConstraint.
079: */
080: public ExportedKeyConstraint(String name, Vector colNames,
081: String fTableName, Vector fColNames,
082: ImportedKeyConstraint iConstraint,
083: UniqueConstraint uConstraint) throws SQLException {
084: super (name, colNames, fTableName, fColNames);
085: this .iConstraint = iConstraint;
086: this .iConstraintName = iConstraint.getName();
087: this .uConstraintName = uConstraint.getName();
088: uConstraint.addExportConstraint(this );
089: if (this .colNames == null) {
090: this .colNames = uConstraint.getColumnNames();
091: }
092: }
093:
094: public void delete(Session session) throws SQLException,
095: IOException {
096: UniqueConstraint uc = (UniqueConstraint) table
097: .getConstraint(uConstraintName);
098: if (uc != null) {
099: uc.removeExportConstraint(name);
100: }
101: }
102:
103: void setForeignKeyCols(int[] fCols) {
104: this .fCols = fCols;
105: }
106:
107: public void checkUpdate(Session session, byte[] oldKey, Row row,
108: Row oldRow, long rowId, Constraint activeIndex)
109: throws SQLException, IOException {
110: getComparator();
111: Database db = session.getDatabase();
112: byte[] newKey = makeKey(session, row);
113: if (activeIndex != this )
114: oldKey = makeKey(session, oldRow);
115: if (compare.compare(newKey, oldKey) != 0) {
116: ExportedKeys ek = getExportedKeys(session);
117: if (isSelfReferencing(db)) {
118: byte[] oldfKey = makeFKey(session, oldRow);
119: byte[] newfKey = makeFKey(session, row);
120: ek.addSelfRefEntry(oldKey, newKey, oldfKey, newfKey);
121: } else {
122: ek.addEntry(oldKey, newKey);
123: }
124: }
125: }
126:
127: public void checkDelete(Session session, Row row, long rowId)
128: throws SQLException, IOException {
129: byte[] fkey = getImportedKeyConstraint(session.getDatabase())
130: .makeFKey(session, row);
131: if (isSelfReferencing(session.getDatabase())) {
132: ExportedKeys ek = getExportedKeys(session);
133: byte[] key = makeKey(session, row);
134: ek.addDeleteSelfRef(key, fkey);
135: } else {
136: checkKeyRemoval(session, fkey);
137: }
138: }
139:
140: public void checkKeyRemoval(Session session, byte[] oldkey)
141: throws SQLException, IOException {
142: getComparator();
143: Database db = session.getDatabase();
144: Btree fTree = getForeignIndex(db);
145: int count = 0;
146: boolean match = false;
147: BCursor cursor = fTree.getCursor(false);
148: try {
149: boolean kvalid = cursor.seek(oldkey);
150: while (!match && (kvalid || cursor.next())) {
151: byte[] key = cursor.getKey();
152: int cmp = compare.compare(key, oldkey);
153: match = cmp == 0;
154: kvalid = false;
155: }
156: } finally {
157: cursor.release();
158: }
159: if (match) {
160: int refSpec = iConstraint.getRefAction(DELETE);
161: switch (refSpec) {
162: case NOACTION:
163: throw new SQLException(
164: "Foreign key constraint violation: children exist",
165: "23000");
166:
167: case CASCADE:
168: ExportedKeys ek = getExportedKeys(session);
169: ek.removeKey(refSpec, oldkey);
170: break;
171: case SETNULL:
172: case SETDEFAULT:
173: // XXX TODO: set null, set default
174: throw new SQLException(
175: "Foreign key constraint violation and SET "
176: + "{NULL|DEFAULT} not implemented",
177: "23000");
178: }
179: }
180: }
181:
182: /**
183: * Utility function to get / lazy-create the session context used
184: * to keep track of key additions and removals generated by the current
185: * statement.
186: */
187: ExportedKeys getExportedKeys(Session session) throws IOException {
188: ExportedKeys ek = (ExportedKeys) session.getContext(this ,
189: isDeferred());
190: if (ek == null) {
191: ek = new ExportedKeys(session, this );
192: session.putContext(this , isDeferred(), ek);
193: }
194: return ek;
195: }
196:
197: ImportedKeyConstraint getImportedKeyConstraint(Database db)
198: throws SQLException, IOException {
199: if (iConstraint == null) {
200: getFTable(db);
201: iConstraint = (ImportedKeyConstraint) fTable
202: .getConstraint(iConstraintName);
203: }
204: return iConstraint;
205: }
206:
207: Btree getForeignIndex(Database db) throws SQLException, IOException {
208: return getImportedKeyConstraint(db).getIndex(db);
209: }
210:
211: public void readExternal(ObjectInput in) throws IOException,
212: ClassNotFoundException {
213: super .readExternal(in);
214: iConstraintName = (String) in.readObject();
215: uConstraintName = (String) in.readObject();
216: }
217:
218: public void writeExternal(ObjectOutput out) throws IOException {
219: super.writeExternal(out);
220: out.writeObject(iConstraintName);
221: out.writeObject(uConstraintName);
222: }
223: }
|