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.Externalizable;
042: import java.io.IOException;
043: import java.io.ObjectInput;
044: import java.io.ObjectOutput;
045:
046: import java.util.Vector;
047:
048: import java.sql.SQLException;
049:
050: import com.quadcap.sql.index.Btree;
051:
052: import com.quadcap.util.Debug;
053:
054: /**
055: * A constraint is a condition which must be satisfied relative to
056: * the rows of a table. Constraint checking hooks are available for
057: * both before and after table-modifying operations.
058: *
059: * @author Stan Bailes
060: */
061: public abstract class Constraint implements Externalizable {
062: transient Table table;
063: transient int[] columns;
064: Vector colNames = new Vector();
065: String name = null;
066: int spec = 0;
067:
068: // bits 0..1 of the constraint
069: public static final int FULL = (1 << 0);
070: public static final int PARTIAL = (1 << 1);
071:
072: // two actions bits for update and delete
073: public static final int NOACTION = 0;
074: public static final int CASCADE = 1;
075: public static final int SETNULL = 2;
076: public static final int SETDEFAULT = 3;
077:
078: // bits 2..3 (action)
079: public static final int UPDATE = 2;
080: // bits 4..5 (action)
081: public static final int DELETE = 4;
082:
083: // bit 6
084: public static final int DEFERRABLE = (1 << 6);
085: // bit 7
086: public static final int INIT_DEFERRED = (1 << 7);
087: // bit 8
088: public static final int GLOBAL = (1 << 8);
089:
090: /**
091: * Default constructor
092: */
093: public Constraint() {
094: }
095:
096: /**
097: * Construct a named constraint
098: */
099: public Constraint(String name) {
100: this .name = name;
101: }
102:
103: /**
104: * Construct a named constraint with a list of columns
105: */
106: public Constraint(String name, Vector colNames) {
107: this .name = name;
108: this .colNames = colNames;
109: }
110:
111: /**
112: * Return the constraint's name
113: */
114: public String getName() {
115: return name;
116: }
117:
118: /**
119: * Set the constraint's name
120: */
121: public void setName(String name) {
122: this .name = name;
123: }
124:
125: /**
126: * Get the referential action for the specified operation (UPD OR DEL)
127: */
128: public int getRefAction(int opType) {
129: return (spec >> opType) & 0x3;
130: }
131:
132: /**
133: * String names for Various referential actions
134: */
135: static String[] refActions = { "NO ACTION", "CASCADE", "SET NULL",
136: "SET DEFAULT" };
137:
138: /**
139: * Return the referential action for the specified operation
140: */
141: public String getRefActionString(int opType) {
142: return refActions[getRefAction(opType)];
143: }
144:
145: /**
146: * Set the 'global' flag
147: */
148: public void setGlobal(boolean g) {
149: if (g) {
150: spec |= GLOBAL;
151: } else {
152: spec &= ~GLOBAL;
153: }
154: }
155:
156: /**
157: * Get the 'global' flag
158: */
159: public boolean isGlobal() {
160: return (spec & GLOBAL) != 0;
161: }
162:
163: /**---INSERT
164: * Called before a row is INSERTED, with an opportunity to signal
165: * a constraint violation if one can be detected.
166: */
167: abstract public void checkInsert(Session session, Row row)
168: throws SQLException, IOException;
169:
170: /**
171: * Called after the specified row has been inserted, with the resulting
172: * row ID, and with the active index. Constraints which maintain state
173: * (such as an index) would extend this class and implement this
174: * method to update the index
175: */
176: abstract public void applyInsert(Session session, Row row,
177: long rowId, Constraint activeIndex) throws SQLException,
178: IOException;
179:
180: /**----DELETE
181: * Called before a row is DELETED, with an opportunity to signal a
182: * constraint violation if one can be detected.
183: */
184: abstract public void checkDelete(Session session, Row row,
185: long rowId) throws SQLException, IOException;
186:
187: /**
188: * Called after a row has been deleted, with the old row and row ID.
189: * Constraints which maintain state (such as an index) would extend
190: * this class and implement this method to update the index.
191: */
192: abstract public void applyDelete(Session session, Row row,
193: long rowId, Constraint activeIndex) throws SQLException,
194: IOException;
195:
196: /**----UPDATE
197: * Called before a row is UPDATED, with an opportunity to signal a
198: * constraint violation if one can be detected.
199: *
200: * Because the oldRow may be 'lazy', it's important to instantiate
201: * whatever items are going to be needed later by applyUpdate *now*,
202: * otherwise, apply may get the 'new' versions of those items, because
203: * the underlying byte stream is modified by the time applyUpdate
204: * gets called.
205: */
206: abstract public void checkUpdate(Session session, byte[] oldKey,
207: Row row, Row oldRow, long rowId, Constraint activeIndex)
208: throws SQLException, IOException;
209:
210: /**
211: * Called after a row has been updated, with the old row, old key,
212: * new row and row ID.
213: * Constraints which maintain state (such as an index) would extend
214: * this class and implement this method to update the index.
215: */
216: abstract public void applyUpdate(Session session, byte[] oldKey,
217: Row row, Row oldRow, long rowId, Constraint activeIndex)
218: throws SQLException, IOException;
219:
220: /**
221: * Called when the constraint itself is being removed. Constraints which
222: * allocate resources of any kind should release them here since they
223: * are about to be discarded and gc'ed.
224: */
225: abstract public void delete(Session session) throws SQLException,
226: IOException;
227:
228: /**
229: * Called when the constraint is added. Constraints which maintain
230: * state (e.g., indexes) can build their initial data structures (or
231: * whatever it is that they do at this time)
232: */
233: abstract public void add(Session session) throws SQLException,
234: IOException;
235:
236: /**
237: * Called to undo a constraint-add operation.
238: */
239: public void undoAdd(Session session) throws SQLException,
240: IOException {
241: delete(session);
242: }
243:
244: /**
245: * Called to undo a constraint-add operation.
246: */
247: public void undoDelete(Session session) throws SQLException,
248: IOException {
249: add(session);
250: }
251:
252: /**
253: * Set the deferrable flags
254: */
255: public void setDeferrable(int def) {
256: this .spec |= def;
257: }
258:
259: /**
260: * Set the referential integrity flags
261: */
262: public void setRefSpec(int ref) {
263: this .spec |= ref;
264: }
265:
266: /**
267: * Set the contstraint's table
268: */
269: public void setTable(Table table) throws SQLException {
270: this .table = table;
271: this .columns = null;
272: getColumns();
273: }
274:
275: /**
276: * Get the constraint's table
277: */
278: public Table getTable() {
279: return this .table;
280: }
281:
282: /**
283: * Return the referential integrity and deferrability flags
284: */
285: public int getSpec() {
286: return spec;
287: }
288:
289: /**
290: * Convenience method for setting single column constraints.
291: */
292: public void setColumn(Column column) {
293: if (colNames.size() != 0) {
294: colNames = new Vector();
295: }
296: colNames.add(column.getName());
297: this .columns = null;
298: }
299:
300: /**
301: * For the simple, single column constraint, the constraint's column
302: *
303: * @exception SQLException if called for a multi-column constraint.
304: */
305: public Column getColumn() throws SQLException {
306: getColumns();
307: if (columns.length < 1)
308: throw new SQLException("No column", "Q0000");
309: if (columns.length > 1)
310: throw new SQLException("Not a single column constraint: "
311: + this , "Q0000");
312: return table.getColumn(columns[0]);
313: }
314:
315: /**
316: * Return the number of columns described by the constraint.
317: */
318: public int getColumnCount() {
319: return colNames.size();
320: }
321:
322: /**
323: * Zero based!!!!
324: */
325: public Column getColumn(int c) throws SQLException {
326: return table.getColumn(columns[c]);
327: }
328:
329: /**
330: * Read me from a stream
331: */
332: public void readExternal(ObjectInput in) throws IOException,
333: ClassNotFoundException {
334: spec = in.readInt();
335: name = (String) in.readObject();
336: colNames = (Vector) in.readObject();
337: columns = null;
338: }
339:
340: /**
341: * Write me to a stream
342: */
343: public void writeExternal(ObjectOutput out) throws IOException {
344: out.writeInt(spec);
345: out.writeObject(name);
346: out.writeObject(colNames);
347: }
348:
349: /**
350: * Return the integer indexes in the table's column list of the
351: * columns in this constraint. We return a copy of our internal
352: * array, for speed, and trust the caller to not mess with the
353: * array...
354: */
355: public int[] getColumns() throws SQLException {
356: if (columns == null) {
357: columns = table.mapColumns(colNames);
358: }
359: return columns;
360: }
361:
362: /**
363: * Reset any mapped columns (e.g. in case a column is added or deleted)
364: */
365: public void resetColumns() throws SQLException {
366: columns = null;
367: }
368:
369: /**
370: * Return a vector containing all of the column names
371: */
372: public Vector getColumnNames() throws SQLException {
373: return colNames;
374: }
375:
376: /**
377: * Funny, but a lot of constraints have indexes. For the ones that
378: * do, this can be a nifty little function.
379: */
380: public Btree getIndex(Database db) throws IOException {
381: return null;
382: }
383:
384: /**
385: * Higher priority (larger numbers) execute first.
386: */
387: public int getPriority() {
388: return 3;
389: }
390:
391: /**
392: * Is this constraint 'deferred'?
393: */
394: public boolean isDeferred() {
395: return false; // only foreign key constraints are deferrable...
396: }
397:
398: /**
399: * Return a displayable representation for debugging
400: */
401: public String toString() {
402: StringBuffer sb = new StringBuffer("Constraint ");
403: sb.append(name);
404: sb.append(": ");
405: if (table == null) {
406: sb.append("<null table>");
407: } else {
408: sb.append(table.getName());
409: }
410: sb.append("(");
411: if (colNames != null) {
412: for (int i = 0; i < colNames.size(); i++) {
413: if (i > 0)
414: sb.append(',');
415: sb.append(colNames.elementAt(i).toString());
416: }
417: }
418: sb.append(")");
419: if ((spec & FULL) != 0)
420: sb.append(" FULL");
421: if ((spec & PARTIAL) != 0)
422: sb.append(" PARTIAL");
423:
424: if (getRefAction(UPDATE) != NOACTION) {
425: sb.append(" ON UPDATE ");
426: sb.append(getRefActionString(UPDATE));
427: }
428: if (getRefAction(DELETE) != NOACTION) {
429: sb.append(" ON DELETE ");
430: sb.append(getRefActionString(DELETE));
431: }
432: if ((spec & DEFERRABLE) != 0)
433: sb.append(" DEFERRABLE");
434: if ((spec & INIT_DEFERRED) != 0)
435: sb.append(" INITIALLY DEFERRED");
436:
437: return sb.toString();
438: }
439: }
|