diff --git a/interface/cassandra.thrift b/interface/cassandra.thrift index 9fb3c19b4b..a26caa3acb 100644 --- a/interface/cassandra.thrift +++ b/interface/cassandra.thrift @@ -83,9 +83,12 @@ service Cassandra { list get_slice_by_name_range(1:string tablename, 2:string key, 3:string columnFamily, 4:string start, 5:string end, 6:i32 count=-1) throws (1: InvalidRequestException ire, 2: NotFoundException nfe), -list get_slice_by_names(1:string tablename, 2:string key, 3:string columnFamily, 4:list columnNames) + list get_slice_by_names(1:string tablename, 2:string key, 3:string columnFamily, 4:list columnNames) throws (1: InvalidRequestException ire, 2: NotFoundException nfe), + list get_slice_from(1:string tablename, 2:string key, 3:string columnFamily_column, 4:bool isAscending, 5:i32 count) + throws (1: InvalidRequestException ire, 2: NotFoundException nfe), + column_t get_column(1:string tablename, 2:string key, 3:string columnFamily_column) throws (1: InvalidRequestException ire, 2: NotFoundException nfe), diff --git a/interface/gen-java/org/apache/cassandra/service/Cassandra.java b/interface/gen-java/org/apache/cassandra/service/Cassandra.java index 9e602c01d0..93ab8c44e6 100644 --- a/interface/gen-java/org/apache/cassandra/service/Cassandra.java +++ b/interface/gen-java/org/apache/cassandra/service/Cassandra.java @@ -28,6 +28,8 @@ public class Cassandra { public List get_slice_by_names(String tablename, String key, String columnFamily, List columnNames) throws InvalidRequestException, NotFoundException, TException; + public List get_slice_from(String tablename, String key, String columnFamily_column, boolean isAscending, int count) throws InvalidRequestException, NotFoundException, TException; + public column_t get_column(String tablename, String key, String columnFamily_column) throws InvalidRequestException, NotFoundException, TException; public int get_column_count(String tablename, String key, String columnFamily_column) throws InvalidRequestException, TException; @@ -218,6 +220,49 @@ public class Cassandra { throw new TApplicationException(TApplicationException.MISSING_RESULT, "get_slice_by_names failed: unknown result"); } + public List get_slice_from(String tablename, String key, String columnFamily_column, boolean isAscending, int count) throws InvalidRequestException, NotFoundException, TException + { + send_get_slice_from(tablename, key, columnFamily_column, isAscending, count); + return recv_get_slice_from(); + } + + public void send_get_slice_from(String tablename, String key, String columnFamily_column, boolean isAscending, int count) throws TException + { + oprot_.writeMessageBegin(new TMessage("get_slice_from", TMessageType.CALL, seqid_)); + get_slice_from_args args = new get_slice_from_args(); + args.tablename = tablename; + args.key = key; + args.columnFamily_column = columnFamily_column; + args.isAscending = isAscending; + args.count = count; + args.write(oprot_); + oprot_.writeMessageEnd(); + oprot_.getTransport().flush(); + } + + public List recv_get_slice_from() throws InvalidRequestException, NotFoundException, TException + { + TMessage msg = iprot_.readMessageBegin(); + if (msg.type == TMessageType.EXCEPTION) { + TApplicationException x = TApplicationException.read(iprot_); + iprot_.readMessageEnd(); + throw x; + } + get_slice_from_result result = new get_slice_from_result(); + result.read(iprot_); + iprot_.readMessageEnd(); + if (result.isSetSuccess()) { + return result.success; + } + if (result.ire != null) { + throw result.ire; + } + if (result.nfe != null) { + throw result.nfe; + } + throw new TApplicationException(TApplicationException.MISSING_RESULT, "get_slice_from failed: unknown result"); + } + public column_t get_column(String tablename, String key, String columnFamily_column) throws InvalidRequestException, NotFoundException, TException { send_get_column(tablename, key, columnFamily_column); @@ -810,6 +855,7 @@ public class Cassandra { processMap_.put("get_slice", new get_slice()); processMap_.put("get_slice_by_name_range", new get_slice_by_name_range()); processMap_.put("get_slice_by_names", new get_slice_by_names()); + processMap_.put("get_slice_from", new get_slice_from()); processMap_.put("get_column", new get_column()); processMap_.put("get_column_count", new get_column_count()); processMap_.put("insert", new insert()); @@ -943,6 +989,36 @@ public class Cassandra { } + private class get_slice_from implements ProcessFunction { + public void process(int seqid, TProtocol iprot, TProtocol oprot) throws TException + { + get_slice_from_args args = new get_slice_from_args(); + args.read(iprot); + iprot.readMessageEnd(); + get_slice_from_result result = new get_slice_from_result(); + try { + result.success = iface_.get_slice_from(args.tablename, args.key, args.columnFamily_column, args.isAscending, args.count); + } catch (InvalidRequestException ire) { + result.ire = ire; + } catch (NotFoundException nfe) { + result.nfe = nfe; + } catch (Throwable th) { + LOGGER.error("Internal error processing get_slice_from", th); + TApplicationException x = new TApplicationException(TApplicationException.INTERNAL_ERROR, "Internal error processing get_slice_from"); + oprot.writeMessageBegin(new TMessage("get_slice_from", TMessageType.EXCEPTION, seqid)); + x.write(oprot); + oprot.writeMessageEnd(); + oprot.getTransport().flush(); + return; + } + oprot.writeMessageBegin(new TMessage("get_slice_from", TMessageType.REPLY, seqid)); + result.write(oprot); + oprot.writeMessageEnd(); + oprot.getTransport().flush(); + } + + } + private class get_column implements ProcessFunction { public void process(int seqid, TProtocol iprot, TProtocol oprot) throws TException { @@ -4022,6 +4098,880 @@ public class Cassandra { } + public static class get_slice_from_args implements TBase, java.io.Serializable, Cloneable { + private static final TStruct STRUCT_DESC = new TStruct("get_slice_from_args"); + private static final TField TABLENAME_FIELD_DESC = new TField("tablename", TType.STRING, (short)1); + private static final TField KEY_FIELD_DESC = new TField("key", TType.STRING, (short)2); + private static final TField COLUMN_FAMILY_COLUMN_FIELD_DESC = new TField("columnFamily_column", TType.STRING, (short)3); + private static final TField IS_ASCENDING_FIELD_DESC = new TField("isAscending", TType.BOOL, (short)4); + private static final TField COUNT_FIELD_DESC = new TField("count", TType.I32, (short)5); + + public String tablename; + public static final int TABLENAME = 1; + public String key; + public static final int KEY = 2; + public String columnFamily_column; + public static final int COLUMNFAMILY_COLUMN = 3; + public boolean isAscending; + public static final int ISASCENDING = 4; + public int count; + public static final int COUNT = 5; + + private final Isset __isset = new Isset(); + private static final class Isset implements java.io.Serializable { + public boolean isAscending = false; + public boolean count = false; + } + + public static final Map metaDataMap = Collections.unmodifiableMap(new HashMap() {{ + put(TABLENAME, new FieldMetaData("tablename", TFieldRequirementType.DEFAULT, + new FieldValueMetaData(TType.STRING))); + put(KEY, new FieldMetaData("key", TFieldRequirementType.DEFAULT, + new FieldValueMetaData(TType.STRING))); + put(COLUMNFAMILY_COLUMN, new FieldMetaData("columnFamily_column", TFieldRequirementType.DEFAULT, + new FieldValueMetaData(TType.STRING))); + put(ISASCENDING, new FieldMetaData("isAscending", TFieldRequirementType.DEFAULT, + new FieldValueMetaData(TType.BOOL))); + put(COUNT, new FieldMetaData("count", TFieldRequirementType.DEFAULT, + new FieldValueMetaData(TType.I32))); + }}); + + static { + FieldMetaData.addStructMetaDataMap(get_slice_from_args.class, metaDataMap); + } + + public get_slice_from_args() { + } + + public get_slice_from_args( + String tablename, + String key, + String columnFamily_column, + boolean isAscending, + int count) + { + this(); + this.tablename = tablename; + this.key = key; + this.columnFamily_column = columnFamily_column; + this.isAscending = isAscending; + this.__isset.isAscending = true; + this.count = count; + this.__isset.count = true; + } + + /** + * Performs a deep copy on other. + */ + public get_slice_from_args(get_slice_from_args other) { + if (other.isSetTablename()) { + this.tablename = other.tablename; + } + if (other.isSetKey()) { + this.key = other.key; + } + if (other.isSetColumnFamily_column()) { + this.columnFamily_column = other.columnFamily_column; + } + __isset.isAscending = other.__isset.isAscending; + this.isAscending = other.isAscending; + __isset.count = other.__isset.count; + this.count = other.count; + } + + @Override + public get_slice_from_args clone() { + return new get_slice_from_args(this); + } + + public String getTablename() { + return this.tablename; + } + + public void setTablename(String tablename) { + this.tablename = tablename; + } + + public void unsetTablename() { + this.tablename = null; + } + + // Returns true if field tablename is set (has been asigned a value) and false otherwise + public boolean isSetTablename() { + return this.tablename != null; + } + + public void setTablenameIsSet(boolean value) { + if (!value) { + this.tablename = null; + } + } + + public String getKey() { + return this.key; + } + + public void setKey(String key) { + this.key = key; + } + + public void unsetKey() { + this.key = null; + } + + // Returns true if field key is set (has been asigned a value) and false otherwise + public boolean isSetKey() { + return this.key != null; + } + + public void setKeyIsSet(boolean value) { + if (!value) { + this.key = null; + } + } + + public String getColumnFamily_column() { + return this.columnFamily_column; + } + + public void setColumnFamily_column(String columnFamily_column) { + this.columnFamily_column = columnFamily_column; + } + + public void unsetColumnFamily_column() { + this.columnFamily_column = null; + } + + // Returns true if field columnFamily_column is set (has been asigned a value) and false otherwise + public boolean isSetColumnFamily_column() { + return this.columnFamily_column != null; + } + + public void setColumnFamily_columnIsSet(boolean value) { + if (!value) { + this.columnFamily_column = null; + } + } + + public boolean isIsAscending() { + return this.isAscending; + } + + public void setIsAscending(boolean isAscending) { + this.isAscending = isAscending; + this.__isset.isAscending = true; + } + + public void unsetIsAscending() { + this.__isset.isAscending = false; + } + + // Returns true if field isAscending is set (has been asigned a value) and false otherwise + public boolean isSetIsAscending() { + return this.__isset.isAscending; + } + + public void setIsAscendingIsSet(boolean value) { + this.__isset.isAscending = value; + } + + public int getCount() { + return this.count; + } + + public void setCount(int count) { + this.count = count; + this.__isset.count = true; + } + + public void unsetCount() { + this.__isset.count = false; + } + + // Returns true if field count is set (has been asigned a value) and false otherwise + public boolean isSetCount() { + return this.__isset.count; + } + + public void setCountIsSet(boolean value) { + this.__isset.count = value; + } + + public void setFieldValue(int fieldID, Object value) { + switch (fieldID) { + case TABLENAME: + if (value == null) { + unsetTablename(); + } else { + setTablename((String)value); + } + break; + + case KEY: + if (value == null) { + unsetKey(); + } else { + setKey((String)value); + } + break; + + case COLUMNFAMILY_COLUMN: + if (value == null) { + unsetColumnFamily_column(); + } else { + setColumnFamily_column((String)value); + } + break; + + case ISASCENDING: + if (value == null) { + unsetIsAscending(); + } else { + setIsAscending((Boolean)value); + } + break; + + case COUNT: + if (value == null) { + unsetCount(); + } else { + setCount((Integer)value); + } + break; + + default: + throw new IllegalArgumentException("Field " + fieldID + " doesn't exist!"); + } + } + + public Object getFieldValue(int fieldID) { + switch (fieldID) { + case TABLENAME: + return getTablename(); + + case KEY: + return getKey(); + + case COLUMNFAMILY_COLUMN: + return getColumnFamily_column(); + + case ISASCENDING: + return new Boolean(isIsAscending()); + + case COUNT: + return new Integer(getCount()); + + default: + throw new IllegalArgumentException("Field " + fieldID + " doesn't exist!"); + } + } + + // Returns true if field corresponding to fieldID is set (has been asigned a value) and false otherwise + public boolean isSet(int fieldID) { + switch (fieldID) { + case TABLENAME: + return isSetTablename(); + case KEY: + return isSetKey(); + case COLUMNFAMILY_COLUMN: + return isSetColumnFamily_column(); + case ISASCENDING: + return isSetIsAscending(); + case COUNT: + return isSetCount(); + default: + throw new IllegalArgumentException("Field " + fieldID + " doesn't exist!"); + } + } + + @Override + public boolean equals(Object that) { + if (that == null) + return false; + if (that instanceof get_slice_from_args) + return this.equals((get_slice_from_args)that); + return false; + } + + public boolean equals(get_slice_from_args that) { + if (that == null) + return false; + + boolean this_present_tablename = true && this.isSetTablename(); + boolean that_present_tablename = true && that.isSetTablename(); + if (this_present_tablename || that_present_tablename) { + if (!(this_present_tablename && that_present_tablename)) + return false; + if (!this.tablename.equals(that.tablename)) + return false; + } + + boolean this_present_key = true && this.isSetKey(); + boolean that_present_key = true && that.isSetKey(); + if (this_present_key || that_present_key) { + if (!(this_present_key && that_present_key)) + return false; + if (!this.key.equals(that.key)) + return false; + } + + boolean this_present_columnFamily_column = true && this.isSetColumnFamily_column(); + boolean that_present_columnFamily_column = true && that.isSetColumnFamily_column(); + if (this_present_columnFamily_column || that_present_columnFamily_column) { + if (!(this_present_columnFamily_column && that_present_columnFamily_column)) + return false; + if (!this.columnFamily_column.equals(that.columnFamily_column)) + return false; + } + + boolean this_present_isAscending = true; + boolean that_present_isAscending = true; + if (this_present_isAscending || that_present_isAscending) { + if (!(this_present_isAscending && that_present_isAscending)) + return false; + if (this.isAscending != that.isAscending) + return false; + } + + boolean this_present_count = true; + boolean that_present_count = true; + if (this_present_count || that_present_count) { + if (!(this_present_count && that_present_count)) + return false; + if (this.count != that.count) + return false; + } + + return true; + } + + @Override + public int hashCode() { + return 0; + } + + public void read(TProtocol iprot) throws TException { + TField field; + iprot.readStructBegin(); + while (true) + { + field = iprot.readFieldBegin(); + if (field.type == TType.STOP) { + break; + } + switch (field.id) + { + case TABLENAME: + if (field.type == TType.STRING) { + this.tablename = iprot.readString(); + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + case KEY: + if (field.type == TType.STRING) { + this.key = iprot.readString(); + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + case COLUMNFAMILY_COLUMN: + if (field.type == TType.STRING) { + this.columnFamily_column = iprot.readString(); + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + case ISASCENDING: + if (field.type == TType.BOOL) { + this.isAscending = iprot.readBool(); + this.__isset.isAscending = true; + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + case COUNT: + if (field.type == TType.I32) { + this.count = iprot.readI32(); + this.__isset.count = true; + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + default: + TProtocolUtil.skip(iprot, field.type); + break; + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + + // check for required fields of primitive type, which can't be checked in the validate method + validate(); + } + + public void write(TProtocol oprot) throws TException { + validate(); + + oprot.writeStructBegin(STRUCT_DESC); + if (this.tablename != null) { + oprot.writeFieldBegin(TABLENAME_FIELD_DESC); + oprot.writeString(this.tablename); + oprot.writeFieldEnd(); + } + if (this.key != null) { + oprot.writeFieldBegin(KEY_FIELD_DESC); + oprot.writeString(this.key); + oprot.writeFieldEnd(); + } + if (this.columnFamily_column != null) { + oprot.writeFieldBegin(COLUMN_FAMILY_COLUMN_FIELD_DESC); + oprot.writeString(this.columnFamily_column); + oprot.writeFieldEnd(); + } + oprot.writeFieldBegin(IS_ASCENDING_FIELD_DESC); + oprot.writeBool(this.isAscending); + oprot.writeFieldEnd(); + oprot.writeFieldBegin(COUNT_FIELD_DESC); + oprot.writeI32(this.count); + oprot.writeFieldEnd(); + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("get_slice_from_args("); + boolean first = true; + + sb.append("tablename:"); + if (this.tablename == null) { + sb.append("null"); + } else { + sb.append(this.tablename); + } + first = false; + if (!first) sb.append(", "); + sb.append("key:"); + if (this.key == null) { + sb.append("null"); + } else { + sb.append(this.key); + } + first = false; + if (!first) sb.append(", "); + sb.append("columnFamily_column:"); + if (this.columnFamily_column == null) { + sb.append("null"); + } else { + sb.append(this.columnFamily_column); + } + first = false; + if (!first) sb.append(", "); + sb.append("isAscending:"); + sb.append(this.isAscending); + first = false; + if (!first) sb.append(", "); + sb.append("count:"); + sb.append(this.count); + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws TException { + // check for required fields + // check that fields of type enum have valid values + } + + } + + public static class get_slice_from_result implements TBase, java.io.Serializable, Cloneable { + private static final TStruct STRUCT_DESC = new TStruct("get_slice_from_result"); + private static final TField SUCCESS_FIELD_DESC = new TField("success", TType.LIST, (short)0); + private static final TField IRE_FIELD_DESC = new TField("ire", TType.STRUCT, (short)1); + private static final TField NFE_FIELD_DESC = new TField("nfe", TType.STRUCT, (short)2); + + public List success; + public static final int SUCCESS = 0; + public InvalidRequestException ire; + public static final int IRE = 1; + public NotFoundException nfe; + public static final int NFE = 2; + + private final Isset __isset = new Isset(); + private static final class Isset implements java.io.Serializable { + } + + public static final Map metaDataMap = Collections.unmodifiableMap(new HashMap() {{ + put(SUCCESS, new FieldMetaData("success", TFieldRequirementType.DEFAULT, + new ListMetaData(TType.LIST, + new StructMetaData(TType.STRUCT, column_t.class)))); + put(IRE, new FieldMetaData("ire", TFieldRequirementType.DEFAULT, + new FieldValueMetaData(TType.STRUCT))); + put(NFE, new FieldMetaData("nfe", TFieldRequirementType.DEFAULT, + new FieldValueMetaData(TType.STRUCT))); + }}); + + static { + FieldMetaData.addStructMetaDataMap(get_slice_from_result.class, metaDataMap); + } + + public get_slice_from_result() { + } + + public get_slice_from_result( + List success, + InvalidRequestException ire, + NotFoundException nfe) + { + this(); + this.success = success; + this.ire = ire; + this.nfe = nfe; + } + + /** + * Performs a deep copy on other. + */ + public get_slice_from_result(get_slice_from_result other) { + if (other.isSetSuccess()) { + List __this__success = new ArrayList(); + for (column_t other_element : other.success) { + __this__success.add(new column_t(other_element)); + } + this.success = __this__success; + } + if (other.isSetIre()) { + this.ire = new InvalidRequestException(other.ire); + } + if (other.isSetNfe()) { + this.nfe = new NotFoundException(other.nfe); + } + } + + @Override + public get_slice_from_result clone() { + return new get_slice_from_result(this); + } + + public int getSuccessSize() { + return (this.success == null) ? 0 : this.success.size(); + } + + public java.util.Iterator getSuccessIterator() { + return (this.success == null) ? null : this.success.iterator(); + } + + public void addToSuccess(column_t elem) { + if (this.success == null) { + this.success = new ArrayList(); + } + this.success.add(elem); + } + + public List getSuccess() { + return this.success; + } + + public void setSuccess(List success) { + this.success = success; + } + + public void unsetSuccess() { + this.success = null; + } + + // Returns true if field success is set (has been asigned a value) and false otherwise + public boolean isSetSuccess() { + return this.success != null; + } + + public void setSuccessIsSet(boolean value) { + if (!value) { + this.success = null; + } + } + + public InvalidRequestException getIre() { + return this.ire; + } + + public void setIre(InvalidRequestException ire) { + this.ire = ire; + } + + public void unsetIre() { + this.ire = null; + } + + // Returns true if field ire is set (has been asigned a value) and false otherwise + public boolean isSetIre() { + return this.ire != null; + } + + public void setIreIsSet(boolean value) { + if (!value) { + this.ire = null; + } + } + + public NotFoundException getNfe() { + return this.nfe; + } + + public void setNfe(NotFoundException nfe) { + this.nfe = nfe; + } + + public void unsetNfe() { + this.nfe = null; + } + + // Returns true if field nfe is set (has been asigned a value) and false otherwise + public boolean isSetNfe() { + return this.nfe != null; + } + + public void setNfeIsSet(boolean value) { + if (!value) { + this.nfe = null; + } + } + + public void setFieldValue(int fieldID, Object value) { + switch (fieldID) { + case SUCCESS: + if (value == null) { + unsetSuccess(); + } else { + setSuccess((List)value); + } + break; + + case IRE: + if (value == null) { + unsetIre(); + } else { + setIre((InvalidRequestException)value); + } + break; + + case NFE: + if (value == null) { + unsetNfe(); + } else { + setNfe((NotFoundException)value); + } + break; + + default: + throw new IllegalArgumentException("Field " + fieldID + " doesn't exist!"); + } + } + + public Object getFieldValue(int fieldID) { + switch (fieldID) { + case SUCCESS: + return getSuccess(); + + case IRE: + return getIre(); + + case NFE: + return getNfe(); + + default: + throw new IllegalArgumentException("Field " + fieldID + " doesn't exist!"); + } + } + + // Returns true if field corresponding to fieldID is set (has been asigned a value) and false otherwise + public boolean isSet(int fieldID) { + switch (fieldID) { + case SUCCESS: + return isSetSuccess(); + case IRE: + return isSetIre(); + case NFE: + return isSetNfe(); + default: + throw new IllegalArgumentException("Field " + fieldID + " doesn't exist!"); + } + } + + @Override + public boolean equals(Object that) { + if (that == null) + return false; + if (that instanceof get_slice_from_result) + return this.equals((get_slice_from_result)that); + return false; + } + + public boolean equals(get_slice_from_result that) { + if (that == null) + return false; + + boolean this_present_success = true && this.isSetSuccess(); + boolean that_present_success = true && that.isSetSuccess(); + if (this_present_success || that_present_success) { + if (!(this_present_success && that_present_success)) + return false; + if (!this.success.equals(that.success)) + return false; + } + + boolean this_present_ire = true && this.isSetIre(); + boolean that_present_ire = true && that.isSetIre(); + if (this_present_ire || that_present_ire) { + if (!(this_present_ire && that_present_ire)) + return false; + if (!this.ire.equals(that.ire)) + return false; + } + + boolean this_present_nfe = true && this.isSetNfe(); + boolean that_present_nfe = true && that.isSetNfe(); + if (this_present_nfe || that_present_nfe) { + if (!(this_present_nfe && that_present_nfe)) + return false; + if (!this.nfe.equals(that.nfe)) + return false; + } + + return true; + } + + @Override + public int hashCode() { + return 0; + } + + public void read(TProtocol iprot) throws TException { + TField field; + iprot.readStructBegin(); + while (true) + { + field = iprot.readFieldBegin(); + if (field.type == TType.STOP) { + break; + } + switch (field.id) + { + case SUCCESS: + if (field.type == TType.LIST) { + { + TList _list47 = iprot.readListBegin(); + this.success = new ArrayList(_list47.size); + for (int _i48 = 0; _i48 < _list47.size; ++_i48) + { + column_t _elem49; + _elem49 = new column_t(); + _elem49.read(iprot); + this.success.add(_elem49); + } + iprot.readListEnd(); + } + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + case IRE: + if (field.type == TType.STRUCT) { + this.ire = new InvalidRequestException(); + this.ire.read(iprot); + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + case NFE: + if (field.type == TType.STRUCT) { + this.nfe = new NotFoundException(); + this.nfe.read(iprot); + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + default: + TProtocolUtil.skip(iprot, field.type); + break; + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + + // check for required fields of primitive type, which can't be checked in the validate method + validate(); + } + + public void write(TProtocol oprot) throws TException { + oprot.writeStructBegin(STRUCT_DESC); + + if (this.isSetSuccess()) { + oprot.writeFieldBegin(SUCCESS_FIELD_DESC); + { + oprot.writeListBegin(new TList(TType.STRUCT, this.success.size())); + for (column_t _iter50 : this.success) { + _iter50.write(oprot); + } + oprot.writeListEnd(); + } + oprot.writeFieldEnd(); + } else if (this.isSetIre()) { + oprot.writeFieldBegin(IRE_FIELD_DESC); + this.ire.write(oprot); + oprot.writeFieldEnd(); + } else if (this.isSetNfe()) { + oprot.writeFieldBegin(NFE_FIELD_DESC); + this.nfe.write(oprot); + oprot.writeFieldEnd(); + } + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("get_slice_from_result("); + boolean first = true; + + sb.append("success:"); + if (this.success == null) { + sb.append("null"); + } else { + sb.append(this.success); + } + first = false; + if (!first) sb.append(", "); + sb.append("ire:"); + if (this.ire == null) { + sb.append("null"); + } else { + sb.append(this.ire); + } + first = false; + if (!first) sb.append(", "); + sb.append("nfe:"); + if (this.nfe == null) { + sb.append("null"); + } else { + sb.append(this.nfe); + } + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws TException { + // check for required fields + // check that fields of type enum have valid values + } + + } + public static class get_column_args implements TBase, java.io.Serializable, Cloneable { private static final TStruct STRUCT_DESC = new TStruct("get_column_args"); private static final TField TABLENAME_FIELD_DESC = new TField("tablename", TType.STRING, (short)1); @@ -8191,14 +9141,14 @@ public class Cassandra { case SUCCESS: if (field.type == TType.LIST) { { - TList _list47 = iprot.readListBegin(); - this.success = new ArrayList(_list47.size); - for (int _i48 = 0; _i48 < _list47.size; ++_i48) + TList _list51 = iprot.readListBegin(); + this.success = new ArrayList(_list51.size); + for (int _i52 = 0; _i52 < _list51.size; ++_i52) { - column_t _elem49; - _elem49 = new column_t(); - _elem49.read(iprot); - this.success.add(_elem49); + column_t _elem53; + _elem53 = new column_t(); + _elem53.read(iprot); + this.success.add(_elem53); } iprot.readListEnd(); } @@ -8242,8 +9192,8 @@ public class Cassandra { oprot.writeFieldBegin(SUCCESS_FIELD_DESC); { oprot.writeListBegin(new TList(TType.STRUCT, this.success.size())); - for (column_t _iter50 : this.success) { - _iter50.write(oprot); + for (column_t _iter54 : this.success) { + _iter54.write(oprot); } oprot.writeListEnd(); } @@ -9014,14 +9964,14 @@ public class Cassandra { case SUCCESS: if (field.type == TType.LIST) { { - TList _list51 = iprot.readListBegin(); - this.success = new ArrayList(_list51.size); - for (int _i52 = 0; _i52 < _list51.size; ++_i52) + TList _list55 = iprot.readListBegin(); + this.success = new ArrayList(_list55.size); + for (int _i56 = 0; _i56 < _list55.size; ++_i56) { - superColumn_t _elem53; - _elem53 = new superColumn_t(); - _elem53.read(iprot); - this.success.add(_elem53); + superColumn_t _elem57; + _elem57 = new superColumn_t(); + _elem57.read(iprot); + this.success.add(_elem57); } iprot.readListEnd(); } @@ -9057,8 +10007,8 @@ public class Cassandra { oprot.writeFieldBegin(SUCCESS_FIELD_DESC); { oprot.writeListBegin(new TList(TType.STRUCT, this.success.size())); - for (superColumn_t _iter54 : this.success) { - _iter54.write(oprot); + for (superColumn_t _iter58 : this.success) { + _iter58.write(oprot); } oprot.writeListEnd(); } @@ -9455,13 +10405,13 @@ public class Cassandra { case SUPERCOLUMNNAMES: if (field.type == TType.LIST) { { - TList _list55 = iprot.readListBegin(); - this.superColumnNames = new ArrayList(_list55.size); - for (int _i56 = 0; _i56 < _list55.size; ++_i56) + TList _list59 = iprot.readListBegin(); + this.superColumnNames = new ArrayList(_list59.size); + for (int _i60 = 0; _i60 < _list59.size; ++_i60) { - String _elem57; - _elem57 = iprot.readString(); - this.superColumnNames.add(_elem57); + String _elem61; + _elem61 = iprot.readString(); + this.superColumnNames.add(_elem61); } iprot.readListEnd(); } @@ -9505,8 +10455,8 @@ public class Cassandra { oprot.writeFieldBegin(SUPER_COLUMN_NAMES_FIELD_DESC); { oprot.writeListBegin(new TList(TType.STRING, this.superColumnNames.size())); - for (String _iter58 : this.superColumnNames) { - oprot.writeString(_iter58); + for (String _iter62 : this.superColumnNames) { + oprot.writeString(_iter62); } oprot.writeListEnd(); } @@ -9784,14 +10734,14 @@ public class Cassandra { case SUCCESS: if (field.type == TType.LIST) { { - TList _list59 = iprot.readListBegin(); - this.success = new ArrayList(_list59.size); - for (int _i60 = 0; _i60 < _list59.size; ++_i60) + TList _list63 = iprot.readListBegin(); + this.success = new ArrayList(_list63.size); + for (int _i64 = 0; _i64 < _list63.size; ++_i64) { - superColumn_t _elem61; - _elem61 = new superColumn_t(); - _elem61.read(iprot); - this.success.add(_elem61); + superColumn_t _elem65; + _elem65 = new superColumn_t(); + _elem65.read(iprot); + this.success.add(_elem65); } iprot.readListEnd(); } @@ -9827,8 +10777,8 @@ public class Cassandra { oprot.writeFieldBegin(SUCCESS_FIELD_DESC); { oprot.writeListBegin(new TList(TType.STRUCT, this.success.size())); - for (superColumn_t _iter62 : this.success) { - _iter62.write(oprot); + for (superColumn_t _iter66 : this.success) { + _iter66.write(oprot); } oprot.writeListEnd(); } @@ -12032,13 +12982,13 @@ public class Cassandra { case SUCCESS: if (field.type == TType.LIST) { { - TList _list63 = iprot.readListBegin(); - this.success = new ArrayList(_list63.size); - for (int _i64 = 0; _i64 < _list63.size; ++_i64) + TList _list67 = iprot.readListBegin(); + this.success = new ArrayList(_list67.size); + for (int _i68 = 0; _i68 < _list67.size; ++_i68) { - String _elem65; - _elem65 = iprot.readString(); - this.success.add(_elem65); + String _elem69; + _elem69 = iprot.readString(); + this.success.add(_elem69); } iprot.readListEnd(); } @@ -12074,8 +13024,8 @@ public class Cassandra { oprot.writeFieldBegin(SUCCESS_FIELD_DESC); { oprot.writeListBegin(new TList(TType.STRING, this.success.size())); - for (String _iter66 : this.success) { - oprot.writeString(_iter66); + for (String _iter70 : this.success) { + oprot.writeString(_iter70); } oprot.writeListEnd(); } @@ -12882,13 +13832,13 @@ public class Cassandra { case SUCCESS: if (field.type == TType.LIST) { { - TList _list67 = iprot.readListBegin(); - this.success = new ArrayList(_list67.size); - for (int _i68 = 0; _i68 < _list67.size; ++_i68) + TList _list71 = iprot.readListBegin(); + this.success = new ArrayList(_list71.size); + for (int _i72 = 0; _i72 < _list71.size; ++_i72) { - String _elem69; - _elem69 = iprot.readString(); - this.success.add(_elem69); + String _elem73; + _elem73 = iprot.readString(); + this.success.add(_elem73); } iprot.readListEnd(); } @@ -12916,8 +13866,8 @@ public class Cassandra { oprot.writeFieldBegin(SUCCESS_FIELD_DESC); { oprot.writeListBegin(new TList(TType.STRING, this.success.size())); - for (String _iter70 : this.success) { - oprot.writeString(_iter70); + for (String _iter74 : this.success) { + oprot.writeString(_iter74); } oprot.writeListEnd(); } diff --git a/lib/google-collect-1.0-rc1.jar b/lib/google-collect-1.0-rc1.jar new file mode 100644 index 0000000000..bc499eea6c Binary files /dev/null and b/lib/google-collect-1.0-rc1.jar differ diff --git a/src/java/org/apache/cassandra/db/ColumnComparatorFactory.java b/src/java/org/apache/cassandra/db/ColumnComparatorFactory.java index 62dae8d86a..17b1409f6b 100644 --- a/src/java/org/apache/cassandra/db/ColumnComparatorFactory.java +++ b/src/java/org/apache/cassandra/db/ColumnComparatorFactory.java @@ -39,37 +39,15 @@ public class ColumnComparatorFactory public static Comparator getComparator(ComparatorType comparatorType) { - Comparator columnComparator = timestampComparator_; - - switch (comparatorType) - { - case NAME: - columnComparator = nameComparator_; - break; - - case TIMESTAMP: - - default: - columnComparator = timestampComparator_; - break; - } - - return columnComparator; + if (comparatorType == ComparatorType.NAME) + return nameComparator_; + assert comparatorType == ComparatorType.TIMESTAMP; + return timestampComparator_; } public static Comparator getComparator(int comparatorTypeInt) { - ComparatorType comparatorType = ComparatorType.NAME; - - if (comparatorTypeInt == ComparatorType.NAME.ordinal()) - { - comparatorType = ComparatorType.NAME; - } - else if (comparatorTypeInt == ComparatorType.TIMESTAMP.ordinal()) - { - comparatorType = ComparatorType.TIMESTAMP; - } - return getComparator(comparatorType); + return getComparator(ComparatorType.values()[comparatorTypeInt]); } } diff --git a/src/java/org/apache/cassandra/db/ColumnFamily.java b/src/java/org/apache/cassandra/db/ColumnFamily.java index d5312f937c..e88b7ac7c9 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamily.java +++ b/src/java/org/apache/cassandra/db/ColumnFamily.java @@ -247,6 +247,7 @@ public final class ColumnFamily void clear() { columns_.clear(); + size_.set(0); } /* @@ -301,21 +302,26 @@ public final class ColumnFamily columns_.remove(columnName); } - void delete(int localtime, long timestamp) + public void delete(int localtime, long timestamp) { localDeletionTime = localtime; markedForDeleteAt = timestamp; } + public void delete(ColumnFamily cf2) + { + delete(Math.max(getLocalDeletionTime(), cf2.getLocalDeletionTime()), + Math.max(getMarkedForDeleteAt(), cf2.getMarkedForDeleteAt())); + } + public boolean isMarkedForDelete() { return markedForDeleteAt > Long.MIN_VALUE; } /* - * This function will calculate the differnce between 2 column families - * the external input is considered the superset of internal - * so there are no deletes in the diff. + * This function will calculate the difference between 2 column families. + * The external input is assumed to be a superset of internal. */ ColumnFamily diff(ColumnFamily cfComposite) { @@ -446,8 +452,7 @@ public final class ColumnFamily { assert cf.name().equals(cf2.name()); cf.addColumns(cf2); - cf.delete(Math.max(cf.getLocalDeletionTime(), cf2.getLocalDeletionTime()), - Math.max(cf.getMarkedForDeleteAt(), cf2.getMarkedForDeleteAt())); + cf.delete(cf2); } return cf; } diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java index 536d8b750b..957b5ca5b9 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java @@ -45,6 +45,10 @@ import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.*; import org.apache.cassandra.concurrent.DebuggableThreadPoolExecutor; import org.apache.commons.lang.StringUtils; +import org.apache.commons.collections.IteratorUtils; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.comparators.ReverseComparator; + import org.cliffc.high_scale_lib.NonBlockingHashMap; import org.cliffc.high_scale_lib.NonBlockingHashSet; @@ -1567,4 +1571,119 @@ public final class ColumnFamilyStore implements ColumnFamilyStoreMBean { return readStats_.mean(); } + + /** + * get a list of columns starting from a given column, in a specified order + * only the latest version of a column is returned + */ + public ColumnFamily getSliceFrom(String key, String cfName, String startColumn, boolean isAscending, int count) + throws IOException, ExecutionException, InterruptedException + { + lock_.readLock().lock(); + try + { + final ColumnFamily returnCF; + ColumnIterator iter; + List iterators = new ArrayList(); + + /* add the current memtable */ + memtableLock_.readLock().lock(); + try + { + iter = memtable_.getColumnIterator(key, cfName, isAscending, startColumn); + returnCF = iter.getColumnFamily(); + } + finally + { + memtableLock_.readLock().unlock(); + } + iterators.add(iter); + + /* add the memtables being flushed */ + List memtables = getUnflushedMemtables(cfName); + for (Memtable memtable:memtables) + { + iter = memtable.getColumnIterator(key, cfName, isAscending, startColumn); + returnCF.delete(iter.getColumnFamily()); + iterators.add(iter); + } + + /* add the SSTables on disk */ + List files = new ArrayList(ssTables_); + for (String file : files) + { + // If the key is not present in the SSTable's BloomFilter, continue to the next file + if (!SSTable.isKeyInFile(key, file)) + continue; + iter = new SSTableColumnIterator(file, key, cfName, startColumn, isAscending); + if (iter.hasNext()) + { + returnCF.delete(iter.getColumnFamily()); + iterators.add(iter); + } + } + + // define a 'reduced' iterator that merges columns w/ the same name, which + // greatly simplifies computing liveColumns in the presence of tombstones. + Comparator comparator = new Comparator() + { + public int compare(IColumn c1, IColumn c2) + { + return c1.name().compareTo(c2.name()); + } + }; + if (!isAscending) + comparator = new ReverseComparator(comparator); + Iterator collated = IteratorUtils.collatedIterator(comparator, iterators); + if (!collated.hasNext()) + return new ColumnFamily(cfName, DatabaseDescriptor.getColumnFamilyType(cfName)); + List L = new ArrayList(); + CollectionUtils.addAll(L, collated); + ReducingIterator reduced = new ReducingIterator(L.iterator()) + { + ColumnFamily curCF = returnCF.cloneMeShallow(); + + protected Object getKey(IColumn o) + { + return o == null ? null : o.name(); + } + + public void reduce(IColumn current) + { + curCF.addColumn(current); + } + + protected IColumn getReduced() + { + IColumn c = curCF.getAllColumns().first(); + curCF.clear(); + return c; + } + }; + + // add unique columns to the CF container + int liveColumns = 0; + for (IColumn column : reduced) + { + if (liveColumns >= count) + { + break; + } + if (!column.isMarkedForDelete()) + liveColumns++; + + returnCF.addColumn(column); + } + + /* close remaining cursors */ + for (ColumnIterator ci : iterators) + ci.close(); + + return removeDeleted(returnCF); + } + finally + { + lock_.readLock().unlock(); + } + } } diff --git a/src/java/org/apache/cassandra/db/ColumnIterator.java b/src/java/org/apache/cassandra/db/ColumnIterator.java new file mode 100644 index 0000000000..fab378dc98 --- /dev/null +++ b/src/java/org/apache/cassandra/db/ColumnIterator.java @@ -0,0 +1,134 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.db; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import org.apache.cassandra.io.DataInputBuffer; +import org.apache.cassandra.io.DataOutputBuffer; +import org.apache.cassandra.io.SSTable; +import org.apache.cassandra.io.SequenceFile.ColumnGroupReader; +import org.apache.cassandra.service.StorageService; +import com.google.common.collect.AbstractIterator; + +public interface ColumnIterator extends Iterator +{ + /** + * returns the CF of the column being iterated. + * The CF is only guaranteed to be available after a call to next() or hasNext(). + */ + public abstract ColumnFamily getColumnFamily(); + + /** clean up any open resources */ + public void close() throws IOException; +} + +/** + * A Column Iterator over SSTable + */ +class SSTableColumnIterator extends AbstractIterator implements ColumnIterator +{ + protected boolean isAscending; + private String startColumn; + private DataOutputBuffer outBuf = new DataOutputBuffer(); + private DataInputBuffer inBuf = new DataInputBuffer(); + private int curColumnIndex; + private ColumnFamily curCF = null; + private ArrayList curColumns = new ArrayList(); + private ColumnGroupReader reader; + + public SSTableColumnIterator(String filename, String key, String cfName, String startColumn, boolean isAscending) + throws IOException + { + this.isAscending = isAscending; + SSTable ssTable = new SSTable(filename, StorageService.getPartitioner()); + reader = ssTable.getColumnGroupReader(key, cfName, startColumn, isAscending); + this.startColumn = startColumn; + curColumnIndex = isAscending ? 0 : -1; + } + + private boolean isColumnNeeded(IColumn column) + { + if (isAscending) + return (column.name().compareTo(startColumn) >= 0); + else + return (column.name().compareTo(startColumn) <= 0); + } + + private void getColumnsFromBuffer() throws IOException + { + inBuf.reset(outBuf.getData(), outBuf.getLength()); + ColumnFamily columnFamily = ColumnFamily.serializer().deserialize(inBuf); + + if (curCF == null) + curCF = columnFamily.cloneMeShallow(); + curColumns.clear(); + for (IColumn column : columnFamily.getAllColumns()) + if (isColumnNeeded(column)) + curColumns.add(column); + + if (isAscending) + curColumnIndex = 0; + else + curColumnIndex = curColumns.size() - 1; + } + + public ColumnFamily getColumnFamily() + { + return curCF; + } + + protected IColumn computeNext() + { + while (true) + { + if (isAscending) + { + if (curColumnIndex < curColumns.size()) + { + return curColumns.get(curColumnIndex++); + } + } + else + { + if (curColumnIndex >= 0) + { + return curColumns.get(curColumnIndex--); + } + } + + try + { + if (!reader.getNextBlock(outBuf)) + return endOfData(); + getColumnsFromBuffer(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + } + + public void close() throws IOException + { + reader.close(); + } +} diff --git a/src/java/org/apache/cassandra/db/FileStruct.java b/src/java/org/apache/cassandra/db/FileStruct.java index e99d0587cf..d6639b2afd 100644 --- a/src/java/org/apache/cassandra/db/FileStruct.java +++ b/src/java/org/apache/cassandra/db/FileStruct.java @@ -28,6 +28,7 @@ import org.apache.cassandra.io.SSTable; import org.apache.cassandra.io.Coordinate; import org.apache.cassandra.dht.IPartitioner; import org.apache.log4j.Logger; +import com.google.common.collect.AbstractIterator; public class FileStruct implements Comparable, Iterator @@ -171,10 +172,8 @@ public class FileStruct implements Comparable, Iterator throw new UnsupportedOperationException(); } - private class FileStructIterator + private class FileStructIterator extends AbstractIterator { - String saved; - public FileStructIterator() { if (key == null) @@ -184,14 +183,6 @@ public class FileStruct implements Comparable, Iterator forward(); } } - if (key.equals(SSTable.blockIndexKey_)) - { - saved = null; - } - else - { - saved = key; - } } private void forward() @@ -204,23 +195,17 @@ public class FileStruct implements Comparable, Iterator { throw new RuntimeException(e); } - saved = isExhausted() ? null : key; } - public boolean hasNext() + protected String computeNext() { - return saved != null; - } - - public String next() - { - if (saved == null) + if (key.equals(SSTable.blockIndexKey_)) { - throw new IndexOutOfBoundsException(); + return endOfData(); } - String key = saved; + String oldKey = key; forward(); - return key; + return oldKey; } } } diff --git a/src/java/org/apache/cassandra/db/Memtable.java b/src/java/org/apache/cassandra/db/Memtable.java index c1c113e614..74153ab744 100644 --- a/src/java/org/apache/cassandra/db/Memtable.java +++ b/src/java/org/apache/cassandra/db/Memtable.java @@ -18,6 +18,14 @@ package org.apache.cassandra.db; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.collections.comparators.ReverseComparator; +import org.apache.commons.lang.ArrayUtils; + import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.io.DataOutputBuffer; @@ -27,13 +35,6 @@ import org.apache.cassandra.utils.BloomFilter; import org.apache.cassandra.utils.DestructivePQIterator; import org.apache.log4j.Logger; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.commons.collections.MapUtils; - /** * Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com ) */ @@ -177,8 +178,7 @@ public class Memtable implements Comparable int newObjectCount = oldCf.getColumnCount(); resolveSize(oldSize, newSize); resolveCount(oldObjectCount, newObjectCount); - oldCf.delete(Math.max(oldCf.getLocalDeletionTime(), columnFamily.getLocalDeletionTime()), - Math.max(oldCf.getMarkedForDeleteAt(), columnFamily.getMarkedForDeleteAt())); + oldCf.delete(columnFamily); } else { @@ -330,4 +330,70 @@ public class Memtable implements Comparable // race conditions even though a task has been processed. return !isDirty_; } + + /** + * obtain an iterator of columns in this memtable in the specified order starting from a given column. + */ + ColumnIterator getColumnIterator(final String key, final String cfName, final boolean isAscending, String startColumn) + { + ColumnFamily cf = columnFamilies_.get(key); + final ColumnFamily columnFamily; + if (cf != null) + columnFamily = cf.cloneMeShallow(); + else + columnFamily = new ColumnFamily(cfName, DatabaseDescriptor.getColumnFamilyType(cfName)); + + final IColumn columns[] = (cf == null ? columnFamily : cf).getAllColumns().toArray(new IColumn[columnFamily.getAllColumns().size()]); + // TODO if we are dealing with supercolumns, we need to clone them while we have the read lock since they can be modified later + if (!isAscending) + ArrayUtils.reverse(columns); + IColumn startIColumn; + if (DatabaseDescriptor.getColumnFamilyType(cfName).equals("Standard")) + startIColumn = new Column(startColumn); + else + startIColumn = new SuperColumn(startColumn); + + // can't use a ColumnComparatorFactory comparator since those compare on both name and time (and thus will fail to match + // our dummy column, since the time there is arbitrary). + Comparator comparator = new Comparator() + { + public int compare(IColumn column1, IColumn column2) + { + return column1.name().compareTo(column2.name()); + } + }; + if (!isAscending) + { + comparator = new ReverseComparator(comparator); + } + int index = Arrays.binarySearch(columns, startIColumn, comparator); + final int startIndex = index < 0 ? -(index + 1) : index; + + return new ColumnIterator() + { + private int curIndex_ = startIndex; + + public ColumnFamily getColumnFamily() + { + return columnFamily; + } + + public boolean hasNext() + { + return curIndex_ < columns.length; + } + + public IColumn next() + { + return columns[curIndex_++]; + } + + public void close() throws IOException {} + + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } } diff --git a/src/java/org/apache/cassandra/db/ReadCommand.java b/src/java/org/apache/cassandra/db/ReadCommand.java index 5c5f04b734..fcb70f61d0 100644 --- a/src/java/org/apache/cassandra/db/ReadCommand.java +++ b/src/java/org/apache/cassandra/db/ReadCommand.java @@ -39,6 +39,8 @@ public abstract class ReadCommand public static final byte CMD_TYPE_GET_COLUMNS_SINCE=4; public static final byte CMD_TYPE_GET_SLICE=5; public static final byte CMD_TYPE_GET_SLICE_BY_RANGE = 6; + public static final byte CMD_TYPE_GET_SLICE_FROM=7; + public static final String EMPTY_CF = ""; private static ReadCommandSerializer serializer = new ReadCommandSerializer(); @@ -96,6 +98,7 @@ class ReadCommandSerializer implements ICompactSerializer CMD_SERIALIZER_MAP.put(ReadCommand.CMD_TYPE_GET_COLUMNS_SINCE, new ColumnsSinceReadCommandSerializer()); CMD_SERIALIZER_MAP.put(ReadCommand.CMD_TYPE_GET_SLICE, new SliceReadCommandSerializer()); CMD_SERIALIZER_MAP.put(ReadCommand.CMD_TYPE_GET_SLICE_BY_RANGE, new SliceByRangeReadCommandSerializer()); + CMD_SERIALIZER_MAP.put(ReadCommand.CMD_TYPE_GET_SLICE_FROM, new SliceFromReadCommandSerializer()); } diff --git a/src/java/org/apache/cassandra/db/SliceFromReadCommand.java b/src/java/org/apache/cassandra/db/SliceFromReadCommand.java new file mode 100644 index 0000000000..aedee597e2 --- /dev/null +++ b/src/java/org/apache/cassandra/db/SliceFromReadCommand.java @@ -0,0 +1,99 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.db; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class SliceFromReadCommand extends ReadCommand +{ + public final String columnFamilyColumn; + public final boolean isAscending; + public final int count; + + public SliceFromReadCommand(String table, String key, String columnFamilyColumn, boolean isAscending, int count) + { + super(table, key, CMD_TYPE_GET_SLICE_FROM); + this.columnFamilyColumn = columnFamilyColumn; + this.isAscending = isAscending; + this.count = count; + } + + @Override + public String getColumnFamilyName() + { + return RowMutation.getColumnAndColumnFamily(columnFamilyColumn)[0]; + } + + @Override + public ReadCommand copy() + { + ReadCommand readCommand = new SliceFromReadCommand(table, key, columnFamilyColumn, isAscending, count); + readCommand.setDigestQuery(isDigestQuery()); + return readCommand; + } + + @Override + public Row getRow(Table table) throws IOException + { + return table.getSliceFrom(key, columnFamilyColumn, isAscending, count); + } + + @Override + public String toString() + { + return "GetSliceReadMessage(" + + "table='" + table + '\'' + + ", key='" + key + '\'' + + ", columnFamily='" + columnFamilyColumn + '\'' + + ", isAscending='" + isAscending + '\'' + + ", count='" + count + '\'' + + ')'; + } +} + +class SliceFromReadCommandSerializer extends ReadCommandSerializer +{ + @Override + public void serialize(ReadCommand rm, DataOutputStream dos) throws IOException + { + SliceFromReadCommand realRM = (SliceFromReadCommand)rm; + dos.writeBoolean(realRM.isDigestQuery()); + dos.writeUTF(realRM.table); + dos.writeUTF(realRM.key); + dos.writeUTF(realRM.columnFamilyColumn); + dos.writeBoolean(realRM.isAscending); + dos.writeInt(realRM.count); + } + + @Override + public ReadCommand deserialize(DataInputStream dis) throws IOException + { + boolean isDigest = dis.readBoolean(); + String table = dis.readUTF(); + String key = dis.readUTF(); + String columnFamily = dis.readUTF(); + boolean isAscending = dis.readBoolean(); + int count = dis.readInt(); + + SliceFromReadCommand rm = new SliceFromReadCommand(table, key, columnFamily, isAscending, count); + rm.setDigestQuery(isDigest); + return rm; + } +} diff --git a/src/java/org/apache/cassandra/db/Table.java b/src/java/org/apache/cassandra/db/Table.java index b6215b1dfd..1bb4e883d6 100644 --- a/src/java/org/apache/cassandra/db/Table.java +++ b/src/java/org/apache/cassandra/db/Table.java @@ -50,10 +50,7 @@ import org.apache.cassandra.net.io.IStreamComplete; import org.apache.cassandra.net.io.StreamContextManager; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.service.CassandraServer; -import org.apache.cassandra.utils.BasicUtilities; -import org.apache.cassandra.utils.FBUtilities; -import org.apache.cassandra.utils.FileUtils; -import org.apache.cassandra.utils.LogUtil; +import org.apache.cassandra.utils.*; import org.apache.log4j.Logger; /** @@ -775,6 +772,34 @@ public class Table return row; } + /** + * Selects a list of columns in a column family from a given column for the specified key. + */ + public Row getSliceFrom(String key, String cf, boolean isAscending, int count) throws IOException + { + Row row = new Row(key); + String[] values = RowMutation.getColumnAndColumnFamily(cf); + ColumnFamilyStore cfStore = columnFamilyStores_.get(values[0]); + long start1 = System.currentTimeMillis(); + try + { + ColumnFamily columnFamily = cfStore.getSliceFrom(key, values[0], values[1], isAscending, count); + if (columnFamily != null) + row.addColumnFamily(columnFamily); + long timeTaken = System.currentTimeMillis() - start1; + dbAnalyticsSource_.updateReadStatistics(timeTaken); + return row; + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + catch (ExecutionException e) + { + throw new RuntimeException(e); + } + } + /** * This method adds the row to the Commit Log associated with this table. * Once this happens the data associated with the individual column families @@ -950,39 +975,46 @@ public class Table } } Iterator collated = IteratorUtils.collatedIterator(comparator, iterators); + Iterable reduced = new ReducingIterator(collated) { + String current; + + public void reduce(String current) + { + this.current = current; + } + + protected String getReduced() + { + return current; + } + }; try { // pull keys out of the CollatedIterator. checking tombstone status is expensive, // so we set an arbitrary limit on how many we'll do at once. List keys = new ArrayList(); - String last = null, current = null; - while (keys.size() < maxResults) + for (String current : reduced) { - if (!collated.hasNext()) + if (!stopAt.isEmpty() && comparator.compare(stopAt, current) < 0) { break; } - current = collated.next(); - if (!current.equals(last)) + // make sure there is actually non-tombstone content associated w/ this key + // TODO record the key source(s) somehow and only check that source (e.g., memtable or sstable) + for (String cfName : getApplicationColumnFamilies()) { - if (!stopAt.isEmpty() && comparator.compare(stopAt, current) < 0) + ColumnFamilyStore cfs = getColumnFamilyStore(cfName); + ColumnFamily cf = cfs.getColumnFamily(current, cfName, new IdentityFilter(), Integer.MAX_VALUE); + if (cf != null && cf.getColumns().size() > 0) { + keys.add(current); break; } - last = current; - // make sure there is actually non-tombstone content associated w/ this key - // TODO record the key source(s) somehow and only check that source (e.g., memtable or sstable) - for (String cfName : getApplicationColumnFamilies()) - { - ColumnFamilyStore cfs = getColumnFamilyStore(cfName); - ColumnFamily cf = cfs.getColumnFamily(current, cfName, new IdentityFilter(), Integer.MAX_VALUE); - if (cf != null && cf.getColumns().size() > 0) - { - keys.add(current); - break; - } - } + } + if (keys.size() >= maxResults) + { + break; } } return keys; diff --git a/src/java/org/apache/cassandra/io/SSTable.java b/src/java/org/apache/cassandra/io/SSTable.java index 61101f54c9..2fbfbfd118 100644 --- a/src/java/org/apache/cassandra/io/SSTable.java +++ b/src/java/org/apache/cassandra/io/SSTable.java @@ -42,6 +42,7 @@ import org.apache.cassandra.utils.BasicUtilities; import org.apache.cassandra.utils.BloomFilter; import org.apache.cassandra.utils.FileUtils; import org.apache.cassandra.utils.LogUtil; +import org.apache.cassandra.io.SequenceFile.ColumnGroupReader; /** * This class is built on top of the SequenceFile. It stores @@ -873,4 +874,29 @@ public class SSTable return hashtable.remove(cannonicalize(filename)); } } + + + /** + * obtain a BlockReader for the getColumnSlice call. + */ + public ColumnGroupReader getColumnGroupReader(String key, String cfName, + String startColumn, boolean isAscending) throws IOException + { + ColumnGroupReader reader = null; + IFileReader dataReader = SequenceFile.reader(dataFile_); + + try + { + /* Morph key into actual key based on the partition type. */ + String decoratedKey = partitioner_.decorateKey(key); + Coordinate fileCoordinate = getCoordinates(decoratedKey, dataReader, partitioner_); + reader = new ColumnGroupReader(dataFile_, decoratedKey, cfName, startColumn, isAscending, fileCoordinate); + } + finally + { + if (dataReader != null) + dataReader.close(); + } + return reader; + } } diff --git a/src/java/org/apache/cassandra/io/SequenceFile.java b/src/java/org/apache/cassandra/io/SequenceFile.java index afb450c088..213954d14f 100644 --- a/src/java/org/apache/cassandra/io/SequenceFile.java +++ b/src/java/org/apache/cassandra/io/SequenceFile.java @@ -28,6 +28,7 @@ import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Arrays; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.utils.BloomFilter; @@ -532,6 +533,146 @@ public class SequenceFile } } + + /** + * This is a reader that finds the block for a starting column and returns + * blocks before/after it for each next call. This function assumes that + * the CF is sorted by name and exploits the name index. + */ + public static class ColumnGroupReader extends BufferReader + { + private String key_; + private String cfName_; + private boolean isAscending_; + + private List columnIndexList_; + private long columnStartPosition_; + private int curRangeIndex_; + private int allColumnsSize_; + private int localDeletionTime_; + private long markedForDeleteAt_; + + ColumnGroupReader(String filename, String key, String cfName, String startColumn, boolean isAscending, Coordinate section) throws IOException + { + super(filename, 128 * 1024); + this.cfName_ = cfName; + this.key_ = key; + this.isAscending_ = isAscending; + init(startColumn, section); + } + + /** + * Build a list of index entries ready for search. + */ + private List getFullColumnIndexList(List columnIndexList, int totalNumCols) + { + if (columnIndexList.size() == 0) + { + /* if there is no column index, add an index entry that covers the full space. */ + return Arrays.asList(new IndexHelper.ColumnIndexInfo[]{new IndexHelper.ColumnNameIndexInfo("", 0, totalNumCols)}); + } + + List fullColIndexList = new ArrayList(); + int accumulatededCols = 0; + for (IndexHelper.ColumnIndexInfo colPosInfo : columnIndexList) + accumulatededCols += colPosInfo.count(); + int remainingCols = totalNumCols - accumulatededCols; + + fullColIndexList.add(new IndexHelper.ColumnNameIndexInfo("", 0, columnIndexList.get(0).count())); + for (int i = 0; i < columnIndexList.size() - 1; i++) + { + IndexHelper.ColumnNameIndexInfo colPosInfo = (IndexHelper.ColumnNameIndexInfo)columnIndexList.get(i); + fullColIndexList.add(new IndexHelper.ColumnNameIndexInfo(colPosInfo.name(), + colPosInfo.position(), + columnIndexList.get(i + 1).count())); + } + String columnName = ((IndexHelper.ColumnNameIndexInfo)columnIndexList.get(columnIndexList.size() - 1)).name(); + fullColIndexList.add(new IndexHelper.ColumnNameIndexInfo(columnName, + columnIndexList.get(columnIndexList.size() - 1).position(), + remainingCols)); + return fullColIndexList; + } + + private void init(String startColumn, Coordinate section) throws IOException + { + String keyInDisk = null; + if (seekTo(key_, section) >= 0) + keyInDisk = file_.readUTF(); + + if ( keyInDisk != null && keyInDisk.equals(key_)) + { + /* read off the size of this row */ + int dataSize = file_.readInt(); + /* skip the bloomfilter */ + int totalBytesRead = IndexHelper.skipBloomFilter(file_); + /* read off the index flag, it has to be true */ + boolean hasColumnIndexes = file_.readBoolean(); + totalBytesRead += 1; + + /* read the index */ + List colIndexList = new ArrayList(); + if (hasColumnIndexes) + totalBytesRead += IndexHelper.deserializeIndex(cfName_, file_, colIndexList); + + /* need to do two things here. + * 1. move the file pointer to the beginning of the list of stored columns + * 2. calculate the size of all columns */ + String cfName = file_.readUTF(); + localDeletionTime_ = file_.readInt(); + markedForDeleteAt_ = file_.readLong(); + int totalNumCols = file_.readInt(); + allColumnsSize_ = dataSize - (totalBytesRead + utfPrefix_ + cfName.length() + 4 + 8 + 4); + + columnStartPosition_ = file_.getFilePointer(); + columnIndexList_ = getFullColumnIndexList(colIndexList, totalNumCols); + + int index = Collections.binarySearch(columnIndexList_, new IndexHelper.ColumnNameIndexInfo(startColumn)); + curRangeIndex_ = index < 0 ? (++index) * (-1) - 1 : index; + } + else + { + /* no keys found in this file because of a false positive in BF */ + curRangeIndex_ = -1; + columnIndexList_ = new ArrayList(); + } + } + + private boolean getBlockFromCurIndex(DataOutputBuffer bufOut) throws IOException + { + if (curRangeIndex_ < 0 || curRangeIndex_ >= columnIndexList_.size()) + return false; + IndexHelper.ColumnIndexInfo curColPostion = columnIndexList_.get(curRangeIndex_); + long start = curColPostion.position(); + long end = curRangeIndex_ < columnIndexList_.size() - 1 + ? columnIndexList_.get(curRangeIndex_+1).position() + : allColumnsSize_; + + /* seek to the correct offset to the data, and calculate the data size */ + file_.seek(columnStartPosition_ + start); + long dataSize = end - start; + + bufOut.reset(); + // write CF info + bufOut.writeUTF(cfName_); + bufOut.writeInt(localDeletionTime_); + bufOut.writeLong(markedForDeleteAt_); + // now write the columns + bufOut.writeInt(curColPostion.count()); + bufOut.write(file_, (int)dataSize); + return true; + } + + public boolean getNextBlock(DataOutputBuffer outBuf) throws IOException + { + boolean result = getBlockFromCurIndex(outBuf); + if (isAscending_) + curRangeIndex_++; + else + curRangeIndex_--; + return result; + } + } + public static abstract class AbstractReader implements IFileReader { private static final short utfPrefix_ = 2; @@ -705,7 +846,7 @@ public class SequenceFile * @param section indicates the location of the block index. * @throws IOException */ - private long seekTo(String key, Coordinate section) throws IOException + protected long seekTo(String key, Coordinate section) throws IOException { seek(section.end_); long position = getPositionFromBlockIndex(key); diff --git a/src/java/org/apache/cassandra/service/CassandraServer.java b/src/java/org/apache/cassandra/service/CassandraServer.java index f6c60f37ab..6518986dba 100644 --- a/src/java/org/apache/cassandra/service/CassandraServer.java +++ b/src/java/org/apache/cassandra/service/CassandraServer.java @@ -35,6 +35,7 @@ import org.apache.cassandra.db.ColumnReadCommand; import org.apache.cassandra.db.ColumnsSinceReadCommand; import org.apache.cassandra.db.SliceByNamesReadCommand; import org.apache.cassandra.db.SliceByRangeReadCommand; +import org.apache.cassandra.db.SliceFromReadCommand; import org.apache.cassandra.db.SliceReadCommand; import org.apache.cassandra.db.IColumn; import org.apache.cassandra.db.Row; @@ -208,7 +209,26 @@ public class CassandraServer implements Cassandra.Iface } return thriftifyColumns(columns); } - + + public List get_slice_from(String tablename, String key, String columnFamily_column, boolean isAscending, int count) throws InvalidRequestException + { + logger.debug("get_slice_from"); + String[] values = RowMutation.getColumnAndColumnFamily(columnFamily_column); + if (values.length != 2 || DatabaseDescriptor.getColumnFamilyType(values[0]) != "Standard") + throw new InvalidRequestException("get_slice_from requires a standard CF name and a starting column name"); + if (count <= 0) + throw new InvalidRequestException("get_slice_from requires positive count"); + if ("Name".compareTo(DatabaseDescriptor.getCFMetaData(tablename, values[0]).indexProperty_) != 0) + throw new InvalidRequestException("get_slice_from requires CF indexed by name"); + ColumnFamily cfamily = readColumnFamily(new SliceFromReadCommand(tablename, key, columnFamily_column, isAscending, count)); + if (cfamily == null) + { + return EMPTY_COLUMNS; + } + Collection columns = cfamily.getAllColumns(); + return thriftifyColumns(columns); + } + public column_t get_column(String tablename, String key, String columnFamily_column) throws NotFoundException, InvalidRequestException { logger.debug("get_column"); diff --git a/src/java/org/apache/cassandra/utils/ReducingIterator.java b/src/java/org/apache/cassandra/utils/ReducingIterator.java new file mode 100644 index 0000000000..1ba3396645 --- /dev/null +++ b/src/java/org/apache/cassandra/utils/ReducingIterator.java @@ -0,0 +1,59 @@ +package org.apache.cassandra.utils; + +import java.util.Iterator; + +import com.google.common.collect.AbstractIterator; + +/** + * reduces equal values from the source iterator to a single (optionally transformed) instance. + */ +public abstract class ReducingIterator extends AbstractIterator implements Iterator, Iterable +{ + protected Iterator source; + protected T last; + + public ReducingIterator(Iterator source) + { + this.source = source; + } + + /** combine this object with the previous ones. intermediate state is up to your implementation. */ + public abstract void reduce(T current); + + /** return the last object computed by reduce */ + protected abstract T getReduced(); + + /** override this if the keys you want to base the reduce on are not the same as the object itself (but can be generated from it) */ + protected Object getKey(T o) + { + return o; + } + + protected T computeNext() + { + if (last == null && !source.hasNext()) + return endOfData(); + + boolean keyChanged = false; + while (!keyChanged) + { + if (last != null) + reduce(last); + if (!source.hasNext()) + { + last = null; + break; + } + T current = source.next(); + if (last != null && !getKey(current).equals(getKey(last))) + keyChanged = true; + last = current; + } + return getReduced(); + } + + public Iterator iterator() + { + return this; + } +} diff --git a/test/unit/org/apache/cassandra/db/TableTest.java b/test/unit/org/apache/cassandra/db/TableTest.java index 9db1a13184..b3414dfe67 100644 --- a/test/unit/org/apache/cassandra/db/TableTest.java +++ b/test/unit/org/apache/cassandra/db/TableTest.java @@ -18,6 +18,11 @@ package org.apache.cassandra.db; +import java.util.SortedSet; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; + import org.junit.Test; import static junit.framework.Assert.*; @@ -202,4 +207,117 @@ public class TableTest extends CleanupHelper rm.add(cf); return rm; } + + @Test + public void testGetSliceFromBasic() throws Throwable + { + Table table = Table.open(TABLE_NAME); + String ROW = "row1"; + RowMutation rm = new RowMutation(TABLE_NAME, ROW); + ColumnFamily cf = new ColumnFamily("Standard1", "Standard"); + cf.addColumn(new Column("col1", "val1".getBytes(), 1L)); + cf.addColumn(new Column("col3", "val3".getBytes(), 1L)); + cf.addColumn(new Column("col4", "val4".getBytes(), 1L)); + cf.addColumn(new Column("col5", "val5".getBytes(), 1L)); + cf.addColumn(new Column("col7", "val7".getBytes(), 1L)); + cf.addColumn(new Column("col9", "val9".getBytes(), 1L)); + rm.add(cf); + rm.apply(); + + rm = new RowMutation(TABLE_NAME, ROW); + rm.delete("Standard1:col4", 2L); + rm.apply(); + validateGetSliceFromBasic(table, ROW); + + // flush to disk + table.getColumnFamilyStore("Standard1").forceBlockingFlush(); + validateGetSliceFromBasic(table, ROW); + } + + @Test + public void testGetSliceFromAdvanced() throws Throwable + { + Table table = Table.open(TABLE_NAME); + String ROW = "row2"; + RowMutation rm = new RowMutation(TABLE_NAME, ROW); + ColumnFamily cf = new ColumnFamily("Standard1", "Standard"); + cf.addColumn(new Column("col1", "val1".getBytes(), 1L)); + cf.addColumn(new Column("col2", "val2".getBytes(), 1L)); + cf.addColumn(new Column("col3", "val3".getBytes(), 1L)); + cf.addColumn(new Column("col4", "val4".getBytes(), 1L)); + cf.addColumn(new Column("col5", "val5".getBytes(), 1L)); + cf.addColumn(new Column("col6", "val6".getBytes(), 1L)); + rm.add(cf); + rm.apply(); + // flush to disk + table.getColumnFamilyStore("Standard1").forceBlockingFlush(); + + rm = new RowMutation(TABLE_NAME, ROW); + cf = new ColumnFamily("Standard1", "Standard"); + cf.addColumn(new Column("col1", "valx".getBytes(), 2L)); + cf.addColumn(new Column("col2", "valx".getBytes(), 2L)); + cf.addColumn(new Column("col3", "valx".getBytes(), 2L)); + rm.add(cf); + rm.apply(); + validateGetSliceFromAdvanced(table, ROW); + + // flush to disk + table.getColumnFamilyStore("Standard1").forceBlockingFlush(); + validateGetSliceFromAdvanced(table, ROW); + } + + private void assertColumns(ColumnFamily columnFamily, String... columnFamilyNames) + { + assertNotNull(columnFamily); + SortedSet columns = columnFamily.getAllColumns(); + List L = new ArrayList(); + for (IColumn column : columns) + { + L.add(column.name()); + } + assert Arrays.equals(L.toArray(new String[columns.size()]), columnFamilyNames); + } + + private void validateGetSliceFromAdvanced(Table table, String row) throws Throwable + { + Row result; + ColumnFamily cfres; + + result = table.getSliceFrom(row, "Standard1:col2", true, 3); + cfres = result.getColumnFamily("Standard1"); + assertColumns(cfres, "col2", "col3", "col4"); + assertEquals(new String(cfres.getColumn("col2").value()), "valx"); + assertEquals(new String(cfres.getColumn("col3").value()), "valx"); + assertEquals(new String(cfres.getColumn("col4").value()), "val4"); + } + + private void validateGetSliceFromBasic(Table table, String row) throws Throwable + { + Row result; + ColumnFamily cf; + + result = table.getSliceFrom(row, "Standard1:col5", true, 2); + cf = result.getColumnFamily("Standard1"); + assertColumns(cf, "col5", "col7"); + + result = table.getSliceFrom(row, "Standard1:col4", true, 2); + cf = result.getColumnFamily("Standard1"); + assertColumns(cf, "col4", "col5", "col7"); + + result = table.getSliceFrom(row, "Standard1:col5", false, 2); + cf = result.getColumnFamily("Standard1"); + assertColumns(cf, "col3", "col4", "col5"); + + result = table.getSliceFrom(row, "Standard1:col6", false, 2); + cf = result.getColumnFamily("Standard1"); + assertColumns(cf, "col3", "col4", "col5"); + + result = table.getSliceFrom(row, "Standard1:col95", true, 2); + cf = result.getColumnFamily("Standard1"); + assertColumns(cf); + + result = table.getSliceFrom(row, "Standard1:col0", false, 2); + cf = result.getColumnFamily("Standard1"); + assertColumns(cf); + } }