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.BufferedOutputStream;
042: import java.io.ByteArrayOutputStream;
043: import java.io.Externalizable;
044: import java.io.InputStream;
045: import java.io.IOException;
046: import java.io.ObjectInput;
047: import java.io.ObjectOutput;
048: import java.io.OutputStream;
049:
050: import java.util.Vector;
051:
052: import java.sql.SQLException;
053:
054: import com.quadcap.sql.io.Arrays;
055: import com.quadcap.sql.io.ObjectInputStream;
056: import com.quadcap.sql.io.ObjectOutputStream;
057: import com.quadcap.sql.io.Extern;
058:
059: import com.quadcap.sql.file.BlockFile;
060: import com.quadcap.sql.file.Datafile;
061: import com.quadcap.sql.file.Log;
062: import com.quadcap.sql.file.PageManager;
063: import com.quadcap.sql.file.SubPageManager;
064:
065: import com.quadcap.sql.index.Btree;
066:
067: import com.quadcap.sql.types.Value;
068: import com.quadcap.sql.types.ValueBlob;
069:
070: import com.quadcap.util.Debug;
071: import com.quadcap.util.Util;
072:
073: /**
074: * Log step to update one or more values in a table row.
075: *
076: * @author Stan Bailes
077: */
078: public class UpdateRow extends LogStep implements Externalizable {
079: transient Table table = null;
080:
081: String tableName = null;
082: Row memRow;
083: Row oldRow;
084: byte[] rowBytes = null;
085: byte[] oldRowBytes = null;
086: long rowId = -1;
087:
088: /**
089: * Default Constructor
090: */
091: public UpdateRow() {
092: }
093:
094: /**
095: * Construct update for specified table row
096: */
097: public UpdateRow(Session session, Table table, long rowId, Row row)
098: throws IOException, SQLException {
099: super (session);
100: init(session, table, rowId, row);
101: }
102:
103: /**
104: * Construct update where both old and new rows are already prepared.
105: */
106: public UpdateRow(Session session, long rowId, Row orow, Row nrow)
107: throws IOException, SQLException {
108: super (session);
109: init(session, null, rowId, nrow);
110: oldRow = orow;
111: }
112:
113: /**
114: * Construct update for specified table row
115: */
116: public UpdateRow(Session session, Table table, long rowId,
117: byte[] oldRowBytes, byte[] rowBytes) throws IOException,
118: SQLException {
119: super (session);
120: if (session.getDatabase().inMemory()) {
121: throw new SQLException("in Memory Database: internal error");
122: }
123: initx(table, rowId, oldRowBytes, rowBytes);
124: }
125:
126: /**
127: * Special re=init for add/drop column
128: */
129: public void initx(Table table, long rowId, byte[] oldRowBytes,
130: byte[] rowBytes) {
131: this .table = table;
132: this .tableName = table.getName();
133: this .rowBytes = copy(rowBytes);
134: this .oldRowBytes = copy(oldRowBytes);
135: this .rowId = rowId;
136: }
137:
138: final byte[] copy(byte[] b) {
139: byte[] r = new byte[b.length];
140: System.arraycopy(b, 0, r, 0, b.length);
141: return r;
142: }
143:
144: /**
145: * Explicit init/reinit
146: */
147: public void init(Session session, Table table, long rowId, Row row)
148: throws IOException, SQLException {
149: this .table = table;
150: this .tableName = table == null ? null : table.getName();
151: if (session.getDatabase().inMemory()) {
152: this .memRow = row;
153: } else {
154: this .rowBytes = LazyRow.writeRow(session, table, row);
155: }
156: this .rowId = rowId;
157: }
158:
159: /**
160: * Lazy table accessor
161: */
162: Table getTable(Database db) throws IOException {
163: if (table == null && tableName != null) {
164: table = (Table) db.getRelation(tableName);
165: }
166: return table;
167: }
168:
169: /**
170: * Instantiate the row from the serialized byte array (if necessary)
171: */
172: final LazyRow getRow(Datafile db) throws IOException, SQLException {
173: LazyRow r = new LazyRow(table.getColumnCount());
174: r.reset(rowBytes, db);
175: return r;
176: }
177:
178: /**
179: * LogStep.redo(): blob accounting if necessary, then update the
180: * row in the database
181: */
182: public void redo(Session session) throws IOException, SQLException {
183: Database db = session.getDatabase();
184: BlockFile file = db.getFile();
185:
186: if (session.getConnection().inRecovery()) {
187: Log log = session.getLog();
188: getTable(db);
189:
190: // The row may contain blobs which have been mapped.
191: if (table.hasBlobs()) {
192: boolean anyChanged = false;
193: Row row = getRow(db);
194: for (int i = 1; i <= row.size(); i++) {
195: Value v = row.item(i);
196: if (v instanceof ValueBlob) {
197: ValueBlob vb = (ValueBlob) v;
198: long blk = vb.getPermBlock();
199: long blk2 = log.getRowMap(blk);
200: if (blk != blk2) {
201: vb.setPermBlock(blk2);
202: anyChanged = true;
203: }
204: }
205: }
206: if (anyChanged) {
207: row.set(1, row.item(1));
208: if (!db.inMemory()) {
209: this .rowBytes = LazyRow.writeRow(session,
210: table, row);
211: }
212: }
213: }
214: rowId = log.getRowMap(rowId);
215: }
216: if (db.inMemory()) {
217: db.putRow(session, getTable(db), rowId, memRow);
218: } else {
219: file.updateBytes(rowId, rowBytes);
220: }
221: session.incrUpdateCount();
222: }
223:
224: /**
225: * LogStep.do(): blob accounting if necessary, then restore the
226: * old row in the database
227: */
228: public void undo(Session session) throws IOException, SQLException {
229: Database db = session.getDatabase();
230: BlockFile file = db.getFile();
231: Log log = session.getLog();
232:
233: long actualRowId = log.getRowMap(rowId);
234: if (db.inMemory()) {
235: db.putRow(session, getTable(db), rowId, oldRow);
236: } else {
237: file.updateBytes(actualRowId, oldRowBytes);
238: }
239: session.decrUpdateCount();
240: }
241:
242: /**
243: * Get ready...
244: */
245: public void prepare(Session session) throws SQLException,
246: IOException {
247: if (session.getDatabase().inMemory()) {
248: if (oldRow == null) {
249: oldRow = table.getRow(session.getDatabase(), rowId);
250: }
251: } else {
252: if (oldRowBytes == null) {
253: oldRowBytes = session.getFile().getBytes(rowId);
254: }
255: }
256: }
257:
258: /**
259: * Read me from a stream
260: */
261: public void readExternal(ObjectInput in) throws IOException,
262: ClassNotFoundException {
263: super .readExternal(in);
264: tableName = (String) in.readObject();
265: rowId = in.readLong();
266:
267: rowBytes = Arrays.readBytes(in);
268: oldRowBytes = Arrays.readBytes(in);
269:
270: }
271:
272: /**
273: * Write me to a stream
274: */
275: public void writeExternal(ObjectOutput out) throws IOException {
276: super .writeExternal(out);
277: out.writeObject(tableName);
278: out.writeLong(rowId);
279:
280: Arrays.writeBytes(out, rowBytes);
281: Arrays.writeBytes(out, oldRowBytes);
282:
283: }
284:
285: //#ifdef DEBUG
286: /**
287: * Return a string representation for debugging
288: */
289: public String toString() {
290: StringBuffer sb = new StringBuffer(super .toString());
291: sb.append(" UpdateRow(");
292: sb.append(SubPageManager.toString(rowId));
293: if (true) {
294: sb.append(",[");
295: sb.append(Util.hexBytes(oldRowBytes));
296: sb.append("] -> [");
297: sb.append(Util.hexBytes(rowBytes));
298: sb.append("]");
299: }
300: sb.append(')');
301: return sb.toString();
302: }
303:
304: //#endif
305:
306: /**
307: * My class's Extern object
308: */
309: static Extern extern;
310:
311: public void setExtern(Extern extern) {
312: UpdateRow.extern = extern;
313: }
314:
315: public Extern getExtern() {
316: return extern;
317: }
318: }
|