package com.quadcap.sql;
/* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
*
* This software is distributed under the Quadcap Free Software License.
* This software may be used or modified for any purpose, personal or
* commercial. Open Source redistributions are permitted. Commercial
* redistribution of larger works derived from, or works which bundle
* this software requires a "Commercial Redistribution License"; see
* http://www.quadcap.com/purchase.
*
* Redistributions qualify as "Open Source" under one of the following terms:
*
* Redistributions are made at no charge beyond the reasonable cost of
* materials and delivery.
*
* Redistributions are accompanied by a copy of the Source Code or by an
* irrevocable offer to provide a copy of the Source Code for up to three
* years at the cost of materials and delivery. Such redistributions
* must allow further use, modification, and redistribution of the Source
* Code under substantially the same terms as this license.
*
* Redistributions of source code must retain the copyright notices as they
* appear in each source code file, these license terms, and the
* disclaimer/limitation of liability set forth as paragraph 6 below.
*
* Redistributions in binary form must reproduce this Copyright Notice,
* these license terms, and the disclaimer/limitation of liability set
* forth as paragraph 6 below, in the documentation and/or other materials
* provided with the distribution.
*
* The Software is provided on an "AS IS" basis. No warranty is
* provided that the Software is free of defects, or fit for a
* particular purpose.
*
* Limitation of Liability. Quadcap Software shall not be liable
* for any damages suffered by the Licensee or any third party resulting
* from use of the Software.
*/
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.InputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.OutputStream;
import java.util.Vector;
import java.sql.SQLException;
import com.quadcap.sql.io.Arrays;
import com.quadcap.sql.io.ObjectInputStream;
import com.quadcap.sql.io.ObjectOutputStream;
import com.quadcap.sql.io.Extern;
import com.quadcap.sql.file.BlockFile;
import com.quadcap.sql.file.Datafile;
import com.quadcap.sql.file.Log;
import com.quadcap.sql.file.PageManager;
import com.quadcap.sql.file.SubPageManager;
import com.quadcap.sql.index.Btree;
import com.quadcap.sql.types.Value;
import com.quadcap.sql.types.ValueBlob;
import com.quadcap.util.Debug;
import com.quadcap.util.Util;
/**
* Log step to update one or more values in a table row.
*
* @author Stan Bailes
*/
public class UpdateRow extends LogStep implements Externalizable {
transient Table table = null;
String tableName = null;
Row memRow;
Row oldRow;
byte[] rowBytes = null;
byte[] oldRowBytes = null;
long rowId = -1;
/**
* Default Constructor
*/
public UpdateRow() {}
/**
* Construct update for specified table row
*/
public UpdateRow(Session session, Table table, long rowId, Row row)
throws IOException, SQLException
{
super(session);
init(session, table, rowId, row);
}
/**
* Construct update where both old and new rows are already prepared.
*/
public UpdateRow(Session session, long rowId, Row orow,
Row nrow)
throws IOException, SQLException
{
super(session);
init(session, null, rowId, nrow);
oldRow = orow;
}
/**
* Construct update for specified table row
*/
public UpdateRow(Session session, Table table, long rowId,
byte[] oldRowBytes, byte[] rowBytes)
throws IOException, SQLException
{
super(session);
if (session.getDatabase().inMemory()) {
throw new SQLException("in Memory Database: internal error");
}
initx(table, rowId, oldRowBytes, rowBytes);
}
/**
* Special re=init for add/drop column
*/
public void initx(Table table, long rowId, byte[] oldRowBytes,
byte[] rowBytes) {
this.table = table;
this.tableName = table.getName();
this.rowBytes = copy(rowBytes);
this.oldRowBytes = copy(oldRowBytes);
this.rowId = rowId;
}
final byte[] copy(byte[] b) {
byte[] r = new byte[b.length];
System.arraycopy(b, 0, r, 0, b.length);
return r;
}
/**
* Explicit init/reinit
*/
public void init(Session session, Table table, long rowId, Row row)
throws IOException, SQLException
{
this.table = table;
this.tableName = table == null ? null : table.getName();
if (session.getDatabase().inMemory()) {
this.memRow = row;
} else {
this.rowBytes = LazyRow.writeRow(session, table, row);
}
this.rowId = rowId;
}
/**
* Lazy table accessor
*/
Table getTable(Database db) throws IOException {
if (table == null && tableName != null) {
table = (Table)db.getRelation(tableName);
}
return table;
}
/**
* Instantiate the row from the serialized byte array (if necessary)
*/
final LazyRow getRow(Datafile db) throws IOException, SQLException {
LazyRow r = new LazyRow(table.getColumnCount());
r.reset(rowBytes, db);
return r;
}
/**
* LogStep.redo(): blob accounting if necessary, then update the
* row in the database
*/
public void redo(Session session) throws IOException, SQLException {
Database db = session.getDatabase();
BlockFile file = db.getFile();
if (session.getConnection().inRecovery()) {
Log log = session.getLog();
getTable(db);
// The row may contain blobs which have been mapped.
if (table.hasBlobs()) {
boolean anyChanged = false;
Row row = getRow(db);
for (int i = 1; i <= row.size(); i++) {
Value v = row.item(i);
if (v instanceof ValueBlob) {
ValueBlob vb = (ValueBlob)v;
long blk = vb.getPermBlock();
long blk2 = log.getRowMap(blk);
if (blk != blk2) {
vb.setPermBlock(blk2);
anyChanged = true;
}
}
}
if (anyChanged) {
row.set(1, row.item(1));
if (!db.inMemory()) {
this.rowBytes = LazyRow.writeRow(session, table, row);
}
}
}
rowId = log.getRowMap(rowId);
}
if (db.inMemory()) {
db.putRow(session, getTable(db), rowId, memRow);
} else {
file.updateBytes(rowId, rowBytes);
}
session.incrUpdateCount();
}
/**
* LogStep.do(): blob accounting if necessary, then restore the
* old row in the database
*/
public void undo(Session session) throws IOException, SQLException {
Database db = session.getDatabase();
BlockFile file = db.getFile();
Log log = session.getLog();
long actualRowId = log.getRowMap(rowId);
if (db.inMemory()) {
db.putRow(session, getTable(db), rowId, oldRow);
} else {
file.updateBytes(actualRowId, oldRowBytes);
}
session.decrUpdateCount();
}
/**
* Get ready...
*/
public void prepare(Session session) throws SQLException, IOException {
if (session.getDatabase().inMemory()) {
if (oldRow == null) {
oldRow = table.getRow(session.getDatabase(), rowId);
}
} else {
if (oldRowBytes == null) {
oldRowBytes = session.getFile().getBytes(rowId);
}
}
}
/**
* Read me from a stream
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
{
super.readExternal(in);
tableName = (String)in.readObject();
rowId = in.readLong();
rowBytes = Arrays.readBytes(in);
oldRowBytes = Arrays.readBytes(in);
}
/**
* Write me to a stream
*/
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeObject(tableName);
out.writeLong(rowId);
Arrays.writeBytes(out, rowBytes);
Arrays.writeBytes(out, oldRowBytes);
}
//#ifdef DEBUG
/**
* Return a string representation for debugging
*/
public String toString() {
StringBuffer sb = new StringBuffer(super.toString());
sb.append(" UpdateRow(");
sb.append(SubPageManager.toString(rowId));
if (true) {
sb.append(",[");
sb.append(Util.hexBytes(oldRowBytes));
sb.append("] -> [");
sb.append(Util.hexBytes(rowBytes));
sb.append("]");
}
sb.append(')');
return sb.toString();
}
//#endif
/**
* My class's Extern object
*/
static Extern extern;
public void setExtern(Extern extern) { UpdateRow.extern = extern; }
public Extern getExtern() { return extern; }
}
|