return(logOverflowRecord(slot, spaceAvailable, out));
}
int numberFields = 0;
StoredRecordHeader recordHeader;
if (forInsert)
{
recordHeader = new StoredRecordHeader();
}
else
{
// Get a full copy of the record header since we might change
// it, and we can't modify the one on the page
recordHeader =
new StoredRecordHeader(getHeaderAtSlot(slot));
// an update always starts at the first column on this page
startColumn = recordHeader.getFirstField();
}
if (validColumns == null)
{
// all columns in row[] are valid, we will be logging them all.
numberFields = row.length - startColumn;
}
else
{
// RESOLVE (mikem) - counting on validColumns.length may be bad
// for performance.
for (int i = validColumns.getLength() - 1;
i >= startColumn;
i--)
{
if (validColumns.isSet(i))
{
numberFields = i + 1 - startColumn;
break;
}
}
}
int onPageNumberFields = -1; // only valid for update
if (forInsert)
{
recordHeader.setId(recordId);
recordHeader.setNumberFields(numberFields);
}
else
{
// an update
onPageNumberFields = recordHeader.getNumberFields();
if (numberFields > onPageNumberFields)
{
// number of fields *might* be increasing
if (recordHeader.hasOverflow())
{
// other fields will be handled in next portion update
numberFields = onPageNumberFields;
}
else
{
// number of fields is increasing
recordHeader.setNumberFields(numberFields);
}
}
else if (numberFields < onPageNumberFields)
{
if (validColumns == null)
{
// number of fields is decreasing,
// but only allowed when the complete
// row is being updated.
recordHeader.setNumberFields(numberFields);
// RESOLVE -
// need some post commit work if row has overflow
// if (recordHeader.hasOverflow()) {
// remove overflow portion after commit.
// }
}
else
{
// we process all the fields, the unchanged ones
// at the end will have a single byte written out
// indicating they are unchanged (nonexistent)
numberFields = onPageNumberFields;
}
}
}
int endFieldExclusive = startColumn + numberFields;
if (realStartColumn >= endFieldExclusive)
{
// The realStartColumn is greater than the last column we need
// to log, so we are done.
return (-1);
}
if ((insertFlag & Page.INSERT_DEFAULT) != Page.INSERT_DEFAULT)
{
// if this is not logging the part of the row being inserted
// on the main page, then use startColumn as first field.
recordHeader.setFirstField(startColumn);
}
// what column to start with?
int firstColumn = realStartColumn;
if (realStartColumn == (-1))
{
// logging on the head page.
int recordHeaderLength = recordHeader.write(logicalDataOut);
spaceAvailable -= recordHeaderLength;
if (spaceAvailable < 0)
{
// ran out of space just writing the record header.
throw new NoSpaceOnPage(isOverflowPage());
}
firstColumn = startColumn;
}
boolean monitoringOldFields = false;
int validColumnsSize =
(validColumns == null) ? 0 : validColumns.getLength();
if (validColumns != null)
{
if (!forInsert)
{
// we monitor the length of the old fields by skipping them
// but only on a partial update.
if ((validColumns != null) &&
(firstColumn < (startColumn + onPageNumberFields)))
{
rawDataIn.setPosition(
getFieldOffset(slot, firstColumn));
monitoringOldFields = true;
}
}
}
int lastSpaceAvailable = spaceAvailable;
int recordSize = 0;
int lastColumnPositionAllowOverflow = out.getPosition();
int lastColumnAllowOverflow = startColumn;
if (spaceAvailable > OVERFLOW_POINTER_SIZE)
lastColumnPositionAllowOverflow = -1;
int columnFlag = COLUMN_FIRST;
for (int i = firstColumn; i < endFieldExclusive; i++)
{
Object ref = null;
boolean ignoreColumn = false;
// should we log this column or not?
if ((validColumns == null) ||
(validColumnsSize > i && validColumns.isSet(i)))
{
if (i < row.length)
ref = row[i];
}
else if (!forInsert)
{
// field is not supplied, log as non-existent
ignoreColumn = true;
}
if (spaceAvailable > OVERFLOW_POINTER_SIZE)
{
lastColumnPositionAllowOverflow = out.getPosition();
lastColumnAllowOverflow = i;
}
lastSpaceAvailable = spaceAvailable;
if (ignoreColumn)
{
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(
ref == null,
"ref should be null for an ignored column");
SanityManager.ASSERT(
validColumns != null,
"validColumns should be non-null for ignored col");
}
if (i < (startColumn + onPageNumberFields))
{
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(
monitoringOldFields,
"monitoringOldFields must be true");
}
// need to keep track of the old field lengths
// as they are remaining in the row.
int oldOffset = rawDataIn.getPosition();
skipField(rawDataIn);
int oldFieldLength =
rawDataIn.getPosition() - oldOffset;
if (oldFieldLength <= spaceAvailable)
{
// if field doesn't fit,
// spaceAvailable must be left unchanged.
logColumn(
null, 0, out, Integer.MAX_VALUE,
COLUMN_NONE, overflowThreshold);
spaceAvailable -= oldFieldLength;
}
}
else
{
// this is an update that is increasing the number of
// columns but not providing any value, strange ...
spaceAvailable =
logColumn(
null, 0, out, spaceAvailable,
columnFlag, overflowThreshold);
}
}
else
{
// ignoreColumn is false, we are logging this column.
if (monitoringOldFields &&
(i < (startColumn + onPageNumberFields)))
{
// skip the old version of the field so that
// rawDataIn is correctly positioned.
skipField(rawDataIn);
}
try
{
if (ref == null)
{
// no new value to provide, use the on page value.
spaceAvailable =
logColumn(
null, 0, out, spaceAvailable,
columnFlag, overflowThreshold);
}
else
{
// log the value provided in the row[i]
spaceAvailable =
logColumn(
row, i, out, spaceAvailable,
columnFlag, overflowThreshold);
}
}
catch (LongColumnException lce)
{
// logColumn determined that the column would not fit
// and that the column length exceeded the long column
// threshold so turn this column into a long column.
if ((insertFlag & Page.INSERT_DEFAULT) ==
Page.INSERT_DEFAULT)
{
// if default insert, just throw no space exception.
// if the lce has throw the column as an InputStream,
// in the following 2 situations
// 1. If column came in 'row[i]' as InputStream
// 2. If the object stream of 'row[i]' is not
// null, which means that the object state of
// the column is null.
//
// we need to set the original InputStream column to
// the column that has been thrown by lce. It is a
// store formated InputStream which remembers all
// the bytes that has been read, but not yet stored.
// Therefore, we will not lose any bytes.
//
// In any other situation, we should not change the
// state of the column,
// i.e. if 'row[i]' has an object state, it should
// not be turned into an InputStream.
if ((lce.getColumn() instanceof InputStream)
&& (row[i] instanceof StreamStorable) )
{
if ((row[i] instanceof InputStream) ||
(((StreamStorable) row[i]).returnStream()
!= null) )
{
// change state of stream so that it uses
// the stream just created by the lce -
// which is remembering the bytes it has
// already read from the stream but couldn't
// log as there was not enough room on
// current page.
((StreamStorable) row[i]).setStream(
(InputStream) lce.getColumn());
}
}
throw new NoSpaceOnPage(isOverflowPage());
}
// When one of the following two conditions is true,
// we will allow the insert of the long column:
//
// 1. if this is the last field,
// and overflow field header fits on page.
// 2. if it is not the last field,
// and overflow field header fits on page (for col)
// and another overflow ptr fits (for row).
//
//
if (((spaceAvailable >= OVERFLOW_PTR_FIELD_SIZE) &&
(i == (endFieldExclusive - 1))) ||
((spaceAvailable >= (OVERFLOW_PTR_FIELD_SIZE * 2))&&
(i < (endFieldExclusive - 1))))
{
// If the column is a long column, it must be a
// InputStream. We have made the input stream into
// a RememberBytesInputStream, have to set the
// column to that, in order to preserve the bytes
// we already read off the stream.
// caught a long column exception,
// set the variables, and rethrow the error
out.setBeginPosition(beginPosition);
lce.setExceptionInfo(out, i, spaceAvailable);
throw (lce);
}
}
}
int nextColumn;
recordSize += (lastSpaceAvailable - spaceAvailable);
boolean recordIsLong =
(overflowThreshold == 100) ?
false : isLong(recordSize, overflowThreshold);
// get the no overflow case out of the way asap
if ((lastSpaceAvailable == spaceAvailable) || recordIsLong)
{
if ((insertFlag & Page.INSERT_DEFAULT) ==
Page.INSERT_DEFAULT)
{
throw new NoSpaceOnPage(isOverflowPage());
}
if (recordIsLong)
{
// if the record is long because of threshold,
// then, we need to reset the logicalOut.
// set position to the end of the previous field
out.setPosition(out.getPosition() - recordSize);
}
// did not write this column
nextColumn = i;
}
else
{
// assume that all fields will be written to this page.
nextColumn = endFieldExclusive;
}
// See if we have enough room to write an overflow field if the
// row needs to overflow. We need overflow if we need to
// write another portion or another portion already exists and
// we will need to point to it.
if ((lastSpaceAvailable == spaceAvailable) ||
((insertFlag & Page.INSERT_FOR_SPLIT) ==
Page.INSERT_FOR_SPLIT))
{
// The current row has filled the page.
if (spaceAvailable <= OVERFLOW_POINTER_SIZE)
{
if ((i == startColumn) ||
(lastColumnPositionAllowOverflow < 0))
{
// not enough room for the overflow recordheader,
// and this is the first column on this page so
// need to try another page.
throw new NoSpaceOnPage(isOverflowPage());
}
else
{
// we need to go back to the last column
// that left enough room for an overflow pointer.
out.setPosition(lastColumnPositionAllowOverflow);
nextColumn = lastColumnAllowOverflow;
}
}
}
if (nextColumn < endFieldExclusive)
{
// If the number of cols has been reduced.
int actualNumberFields = nextColumn - startColumn;
// go back and update that numberFields in recordHeader.
// no need to update spaceAvailable here, because if we are
// here, we will be returning any way, and spaceAvailable
// will be thrown away.
int oldSize = recordHeader.size();
recordHeader.setNumberFields(actualNumberFields);
int newSize = recordHeader.size();
// now we are ready to write the new record header.
int endPosition = out.getPosition();
if (oldSize > newSize)
{
// if the old size is bigger than the new size, then
// leave extra bytes at the beginning of byte stream.
int delta = oldSize - newSize;
out.setBeginPosition(beginPosition + delta);
out.setPosition(beginPosition + delta);
}
else if (newSize > oldSize)
{
out.setPosition(beginPosition);
}
else
{
out.setBeginPosition(beginPosition);
out.setPosition(beginPosition);
}
int realLen = recordHeader.write(logicalDataOut);
if (SanityManager.DEBUG)
{
if ((realLen + (oldSize - newSize)) != oldSize)
{
SanityManager.THROWASSERT(