Repository: cassandra Updated Branches: refs/heads/cassandra-2.0 926f639a5 -> 6d8be12a1 refs/heads/cassandra-2.1 ca7e9d5c4 -> 992eff295 refs/heads/trunk c4248c7c3 -> 8b49af0b7
Support nul/non-existant fields in UDT patch by slebresne; reviewed by iamaleskey for CASSANDRA-7206 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/ca7e9d5c Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/ca7e9d5c Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/ca7e9d5c Branch: refs/heads/trunk Commit: ca7e9d5c49b08fa4981d5986ef68c82b3777e282 Parents: 0b9b1fc Author: Sylvain Lebresne <sylv...@datastax.com> Authored: Fri May 23 18:49:44 2014 +0200 Committer: Sylvain Lebresne <sylv...@datastax.com> Committed: Fri May 23 18:56:29 2014 +0200 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../org/apache/cassandra/cql3/UserTypes.java | 16 +++----- .../apache/cassandra/db/marshal/UserType.java | 39 ++++++++++++++------ 3 files changed, 34 insertions(+), 22 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca7e9d5c/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 108c859..9bde1f3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -24,6 +24,7 @@ * Use prepared statements internally (CASSANDRA-6975) * Fix broken paging state with prepared statement (CASSANDRA-7120) * Fix IllegalArgumentException in CqlStorage (CASSANDRA-7287) + * Allow nulls/non-existant fields in UDT (CASSANDRA-7206) Merged from 2.0: * Always reallocate buffers in HSHA (CASSANDRA-6285) * (Hadoop) support authentication in CqlRecordReader (CASSANDRA-7221) http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca7e9d5c/src/java/org/apache/cassandra/cql3/UserTypes.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java index e9da6e5..ecffe31 100644 --- a/src/java/org/apache/cassandra/cql3/UserTypes.java +++ b/src/java/org/apache/cassandra/cql3/UserTypes.java @@ -20,7 +20,6 @@ package org.apache.cassandra.cql3; import java.nio.ByteBuffer; import java.util.*; -import org.apache.cassandra.db.marshal.CompositeType; import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.exceptions.InvalidRequestException; @@ -59,7 +58,10 @@ public abstract class UserTypes for (int i = 0; i < ut.fieldTypes.size(); i++) { ColumnIdentifier field = new ColumnIdentifier(ut.fieldNames.get(i), UTF8Type.instance); - Term value = entries.get(field).prepare(keyspace, fieldSpecOf(receiver, i)); + Term.Raw raw = entries.get(field); + if (raw == null) + raw = Constants.NULL_LITERAL; + Term value = raw.prepare(keyspace, fieldSpecOf(receiver, i)); if (value instanceof Term.NonTerminal) allTerminal = false; @@ -81,7 +83,7 @@ public abstract class UserTypes ColumnIdentifier field = new ColumnIdentifier(ut.fieldNames.get(i), UTF8Type.instance); Term.Raw value = entries.get(field); if (value == null) - throw new InvalidRequestException(String.format("Invalid user type literal for %s: missing field %s", receiver, field)); + continue; ColumnSpecification fieldSpec = fieldSpecOf(receiver, i); if (!value.isAssignableTo(keyspace, fieldSpec)) @@ -154,13 +156,7 @@ public abstract class UserTypes ByteBuffer[] buffers = new ByteBuffer[values.size()]; for (int i = 0; i < type.fieldTypes.size(); i++) - { - ByteBuffer buffer = values.get(i).bindAndGet(options); - if (buffer == null) - throw new InvalidRequestException("null is not supported inside user type literals"); - - buffers[i] = buffer; - } + buffers[i] = values.get(i).bindAndGet(options); return buffers; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca7e9d5c/src/java/org/apache/cassandra/db/marshal/UserType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/UserType.java b/src/java/org/apache/cassandra/db/marshal/UserType.java index 50b3fbb..6656fd6 100644 --- a/src/java/org/apache/cassandra/db/marshal/UserType.java +++ b/src/java/org/apache/cassandra/db/marshal/UserType.java @@ -126,9 +126,10 @@ public class UserType extends AbstractType<ByteBuffer> throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, fieldNames.get(i))); int size = input.getInt(); - // We don't handle null just yet, but we should fix that soon (CASSANDRA-7206) + + // size < 0 means null value if (size < 0) - throw new MarshalException("Nulls are not yet supported inside UDT values"); + continue; if (input.remaining() < size) throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, fieldNames.get(i))); @@ -164,13 +165,20 @@ public class UserType extends AbstractType<ByteBuffer> { int totalLength = 0; for (ByteBuffer field : fields) - totalLength += 4 + field.remaining(); + totalLength += 4 + (field == null ? 0 : field.remaining()); ByteBuffer result = ByteBuffer.allocate(totalLength); for (ByteBuffer field : fields) { - result.putInt(field.remaining()); - result.put(field.duplicate()); + if (field == null) + { + result.putInt(-1); + } + else + { + result.putInt(field.remaining()); + result.put(field.duplicate()); + } } result.rewind(); return result; @@ -191,11 +199,15 @@ public class UserType extends AbstractType<ByteBuffer> AbstractType<?> type = fieldTypes.get(i); int size = input.getInt(); - assert size >= 0; // We don't support nulls yet, but we will likely do with #7206 and we'll need - // a way to represent it as a string (without it conflicting with a user value) + if (size < 0) + { + sb.append("@"); + continue; + } + ByteBuffer field = ByteBufferUtil.readBytes(input, size); - // We use ':' as delimiter so escape it if it's in the generated string - sb.append(field == null ? "null" : type.getString(value).replaceAll(":", "\\\\:")); + // We use ':' as delimiter, and @ to represent null, so escape them in the generated string + sb.append(type.getString(field).replaceAll(":", "\\\\:").replaceAll("@", "\\\\@")); } return sb.toString(); } @@ -207,10 +219,13 @@ public class UserType extends AbstractType<ByteBuffer> ByteBuffer[] fields = new ByteBuffer[fieldStrings.size()]; for (int i = 0; i < fieldStrings.size(); i++) { + String fieldString = fieldStrings.get(i); + // We use @ to represent nulls + if (fieldString.equals("@")) + continue; + AbstractType<?> type = fieldTypes.get(i); - // TODO: we'll need to handle null somehow here once we support them - String fieldString = fieldStrings.get(i).replaceAll("\\\\:", ":"); - fields[i] = type.fromString(fieldString); + fields[i] = type.fromString(fieldString.replaceAll("\\\\:", ":").replaceAll("\\\\@", "@")); } return buildValue(fields); }