Fix handling of cells for removed column when reading legacy sstables

Patch by Sylvain Lebresne; reviewed by Robert Stupp for CASSANDRA-13939
This commit is contained in:
Sylvain Lebresne 2017-10-05 10:30:56 +02:00
parent c074412513
commit 5378ba27ff
2 changed files with 66 additions and 31 deletions

View File

@ -649,7 +649,7 @@ public abstract class LegacyLayout
boolean foundOne = false;
LegacyAtom atom;
while ((atom = readLegacyAtom(metadata, in, false)) != null)
while ((atom = readLegacyAtomSkippingUnknownColumn(metadata,in)) != null)
{
if (atom.isCell())
{
@ -672,6 +672,23 @@ public abstract class LegacyLayout
return foundOne ? builder.build() : Rows.EMPTY_STATIC_ROW;
}
private static LegacyAtom readLegacyAtomSkippingUnknownColumn(CFMetaData metadata, DataInputPlus in)
throws IOException
{
while (true)
{
try
{
return readLegacyAtom(metadata, in, false);
}
catch (UnknownColumnException e)
{
// Simply skip, as the method name implies.
}
}
}
private static Row getNextRow(CellGrouper grouper, PeekingIterator<? extends LegacyAtom> cells)
{
if (!cells.hasNext())
@ -1020,29 +1037,36 @@ public abstract class LegacyLayout
};
}
public static LegacyAtom readLegacyAtom(CFMetaData metadata, DataInputPlus in, boolean readAllAsDynamic) throws IOException
public static LegacyAtom readLegacyAtom(CFMetaData metadata, DataInputPlus in, boolean readAllAsDynamic)
throws IOException, UnknownColumnException
{
while (true)
{
ByteBuffer cellname = ByteBufferUtil.readWithShortLength(in);
if (!cellname.hasRemaining())
return null; // END_OF_ROW
ByteBuffer cellname = ByteBufferUtil.readWithShortLength(in);
if (!cellname.hasRemaining())
return null; // END_OF_ROW
try
{
int b = in.readUnsignedByte();
return (b & RANGE_TOMBSTONE_MASK) != 0
? readLegacyRangeTombstoneBody(metadata, in, cellname)
: readLegacyCellBody(metadata, in, cellname, b, SerializationHelper.Flag.LOCAL, readAllAsDynamic);
}
catch (UnknownColumnException e)
{
// We can get there if we read a cell for a dropped column, and ff that is the case,
// then simply ignore the cell is fine. But also not that we ignore if it's the
// system keyspace because for those table we actually remove columns without registering
// them in the dropped columns
assert metadata.ksName.equals(SystemKeyspace.NAME) || metadata.getDroppedColumnDefinition(e.columnName) != null : e.getMessage();
}
try
{
int b = in.readUnsignedByte();
return (b & RANGE_TOMBSTONE_MASK) != 0
? readLegacyRangeTombstoneBody(metadata, in, cellname)
: readLegacyCellBody(metadata, in, cellname, b, SerializationHelper.Flag.LOCAL, readAllAsDynamic);
}
catch (UnknownColumnException e)
{
// We legitimately can get here in 2 cases:
// 1) for system tables, because we've unceremoniously removed columns (without registering them as dropped)
// 2) for dropped columns.
// In any other case, there is a mismatch between the schema and the data, and we complain loudly in
// that case. Note that if we are in a legit case of an unknown column, we want to simply skip that cell,
// but we don't do this here and re-throw the exception because the calling code sometimes has to know
// about this happening. This does mean code calling this method should handle this case properly.
if (!metadata.ksName.equals(SystemKeyspace.NAME) && metadata.getDroppedColumnDefinition(e.columnName) == null)
throw new IllegalStateException(String.format("Got cell for unknown column %s in sstable of %s.%s: " +
"This suggest a problem with the schema which doesn't list " +
"this column. Even if that column was dropped, it should have " +
"been listed as such", metadata.ksName, metadata.cfName, UTF8Type.instance.compose(e.columnName)), e);
throw e;
}
}

View File

@ -274,16 +274,26 @@ public abstract class UnfilteredDeserializer
private LegacyLayout.LegacyAtom readAtom()
{
try
while (true)
{
long pos = currentPosition();
LegacyLayout.LegacyAtom atom = LegacyLayout.readLegacyAtom(metadata, in, readAllAsDynamic);
bytesReadForNextAtom = currentPosition() - pos;
return atom;
}
catch (IOException e)
{
throw new IOError(e);
try
{
long pos = currentPosition();
LegacyLayout.LegacyAtom atom = LegacyLayout.readLegacyAtom(metadata, in, readAllAsDynamic);
bytesReadForNextAtom = currentPosition() - pos;
return atom;
}
catch (UnknownColumnException e)
{
// This is ok, see LegacyLayout.readLegacyAtom() for why this only happens in case were we're ok
// skipping the cell. We do want to catch this at this level however because when that happen,
// we should *not* count the byte of that discarded cell as part of the bytes for the atom
// we will eventually return, as doing so could throw the logic bytesReadForNextAtom participates in.
}
catch (IOException e)
{
throw new IOError(e);
}
}
}
@ -407,6 +417,7 @@ public abstract class UnfilteredDeserializer
saved = null;
iterator.clearState();
lastConsumedPosition = currentPosition();
bytesReadForNextAtom = 0;
}
// Groups atoms from the input into proper Unfiltered.