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.file.ByteUtil;
053:
054: import com.quadcap.sql.index.Btree;
055: import com.quadcap.sql.index.Comparator;
056:
057: import com.quadcap.sql.file.BlockFile;
058:
059: import com.quadcap.util.Debug;
060: import com.quadcap.util.Util;
061:
062: /**
063: * Base class for all index constraints.
064: *
065: * @author Stan Bailes
066: */
067: abstract public class IndexConstraint extends Constraint implements
068: Externalizable {
069: Btree index = null;
070: long indexRoot = -1;
071: protected boolean useComparator = true;
072:
073: /**
074: * Default constructor (required for Externalizable)
075: */
076: public IndexConstraint() {
077: }
078:
079: /**
080: * Explicit constructor for named index
081: */
082: public IndexConstraint(String name) {
083: super (name);
084: }
085:
086: public IndexConstraint(String name, Vector names) {
087: super (name, names);
088: }
089:
090: //------------------------------------------------------------------
091: // Abstract interface: implementation required
092: /**
093: * Make a serialized key appropriate to the constraint type, based on
094: * the row and row id information
095: */
096: abstract public byte[] makeKey(Session session, Row row, long rowId)
097: throws SQLException;
098:
099: /**
100: * Derived classes must implement this function, which returns the
101: * constraint type as a human readable string.
102: */
103: abstract public String constraintType();
104:
105: /**
106: * Constraint.add(): We need to build an index!
107: */
108: public void add(Session session) throws SQLException, IOException {
109: index = null;
110: indexRoot = -1;
111: resetColumns();
112: getIndex(session.getDatabase());
113:
114: getColumns();
115: IndexCursor c = table.getCursor(session, this );
116: if (c != null) {
117: try {
118: while (c.next()) {
119: Row row = c.getRow();
120: long rowId = c.getRowId();
121: byte[] key = makeKey(session, row, rowId);
122: if (index.get(key) != null) {
123: throw new SQLException(constraintType()
124: + " constraint violated");
125: }
126: if (session.getConnection().inRecovery()) {
127: rowId = session.getLog().getRowMap(rowId);
128: }
129: index.set(key, key.length, session.getBuf8(rowId),
130: 0, 8);
131: }
132: } finally {
133: c.close();
134: }
135: }
136:
137: }
138:
139: /**
140: * Constraint.delete(): We destroy our index
141: */
142: public void delete(Session session) throws IOException {
143: if (indexRoot > 0) {
144: Database db = session.getDatabase();
145: getIndex(db);
146: if (index != null) {
147: index.free();
148: }
149: }
150: }
151:
152: /**
153: * Externalizable.readExternal(): Read me from a stream
154: */
155: public void readExternal(ObjectInput in) throws IOException,
156: ClassNotFoundException {
157: super .readExternal(in);
158: indexRoot = in.readLong();
159: useComparator = in.read() == 1;
160: }
161:
162: /**
163: * Externalizable.writeExternal(): Write me to a stream
164: */
165: public void writeExternal(ObjectOutput out) throws IOException {
166: super .writeExternal(out);
167: out.writeLong(indexRoot);
168: out.write(useComparator ? 1 : 0);
169: }
170:
171: //---------------------------------
172: // Constraint.checkNNN() functions
173:
174: /**
175: * Constraint.checkInsert(): We could check for dups
176: */
177: public void checkInsert(Session session, Row row)
178: throws SQLException, IOException {
179: }
180:
181: public void checkUpdate(Session session, byte[] oldKey, Row row,
182: Row oldRow, long rowId, Constraint activeIndex)
183: throws SQLException, IOException {
184: }
185:
186: public void checkDelete(Session session, Row row, long rowId)
187: throws SQLException, IOException {
188: }
189:
190: //---------------------------------
191: // Constraint.applyXXX() functions
192:
193: /**
194: * Constraint.applyInsert(): Add an index entry
195: */
196: public void applyInsert(Session session, Row row, long rowId,
197: Constraint activeIndex) throws SQLException, IOException {
198:
199: Database db = session.getDatabase();
200: byte[] key = makeKey(session, row, rowId);
201: AddIndexEntry add = new AddIndexEntry(session, this , key, rowId);
202: if (activeIndex == this ) {
203: session.addPendingAction(add);
204: } else {
205: session.doStep(add);
206: }
207: }
208:
209: // careful, 'oldKey' only applies to the 'activeIndex', which may not
210: // be this constraint.
211: public void applyUpdate(Session session, byte[] oldKey, Row row,
212: Row oldRow, long rowId, Constraint activeIndex)
213: throws SQLException, IOException {
214: byte[] key = makeKey(session, row, rowId);
215: if (activeIndex != this ) {
216: oldKey = makeKey(session, oldRow, rowId);
217: }
218: if (Util.compareBytes(key, oldKey) != 0) {
219: UpdateIndex ui = (UpdateIndex) session.getContext(this ,
220: isDeferred());
221: if (ui == null) {
222: ui = new UpdateIndex(session, this );
223: session.putContext(this , isDeferred(), ui);
224: }
225: ui.addEntry(key, oldKey, rowId);
226: } else {
227: }
228: }
229:
230: public void applyDelete(Session session, Row row, long rowId,
231: Constraint activeIndex) throws SQLException, IOException {
232: Database db = session.getDatabase();
233: byte[] key = makeKey(session, row, rowId);
234:
235: if (index == null)
236: getIndex(db);
237: DeleteIndexEntry del = new DeleteIndexEntry(session, this , key);
238: if (activeIndex == this ) {
239: session.addPendingAction(del);
240: } else {
241: session.doStep(del);
242: }
243: }
244:
245: /**
246: * Does this constraint have enough information to uniquely identify
247: * each row?
248: */
249: public boolean isUnique() {
250: return true;
251: }
252:
253: /**
254: * Return the number of columns in the index implementing this constraint
255: */
256: public int getIndexColumnCount() {
257: return getColumnCount();
258: }
259:
260: /**
261: * Lazy getter for underlying Index object (a Btree, in this case)
262: */
263: public Btree getIndex(Database db) throws IOException {
264: if (index == null) {
265: BlockFile file = db.getFile();
266: boolean create = false;
267: if (indexRoot == -1) {
268: indexRoot = file.newPage();
269: db.updateRelation(table);
270: create = true;
271: }
272: if (useComparator) {
273: Comparator compare = new Key(getIndexColumnCount());
274: index = new Btree(file, compare, indexRoot, create);
275: } else {
276: index = new Btree(file, indexRoot, create);
277: }
278: }
279: return index;
280: }
281:
282: /**
283: * Return a string representation of the constraint
284: */
285: public String toString() {
286: StringBuffer sb = new StringBuffer();
287: sb.append(getName());
288: if (table != null) {
289: sb.append("(");
290: try {
291: Vector v = getColumnNames();
292: for (int i = 0; i < v.size(); i++) {
293: if (i > 0)
294: sb.append(',');
295: sb.append(v.elementAt(i).toString());
296: }
297: sb.append(")");
298: } catch (SQLException e) {
299: sb.append("Exception: " + e.toString());
300: }
301: }
302: return sb.toString();
303: }
304: }
|