-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
make ResultMetadata
lifetime-generic
#1082
make ResultMetadata
lifetime-generic
#1082
Conversation
See the following report for details: cargo semver-checks output
|
fddd5c7
to
86e2aa8
Compare
impl<'frame> ColumnType<'frame> { | ||
pub fn into_owned(self) -> ColumnType<'static> { | ||
match self { | ||
ColumnType::Custom(cow) => ColumnType::Custom(cow.into_owned().into()), | ||
ColumnType::Ascii => ColumnType::Ascii, | ||
ColumnType::Boolean => ColumnType::Boolean, | ||
ColumnType::Blob => ColumnType::Blob, | ||
ColumnType::Counter => ColumnType::Counter, | ||
ColumnType::Date => ColumnType::Date, | ||
ColumnType::Decimal => ColumnType::Decimal, | ||
ColumnType::Double => ColumnType::Double, | ||
ColumnType::Duration => ColumnType::Duration, | ||
ColumnType::Float => ColumnType::Float, | ||
ColumnType::Int => ColumnType::Int, | ||
ColumnType::BigInt => ColumnType::BigInt, | ||
ColumnType::Text => ColumnType::Text, | ||
ColumnType::Timestamp => ColumnType::Timestamp, | ||
ColumnType::Inet => ColumnType::Inet, | ||
ColumnType::List(elem_type) => ColumnType::List(Box::new(elem_type.into_owned())), | ||
ColumnType::Map(key_type, value_type) => ColumnType::Map( | ||
Box::new(key_type.into_owned()), | ||
Box::new(value_type.into_owned()), | ||
), | ||
ColumnType::Set(elem_type) => ColumnType::Set(Box::new(elem_type.into_owned())), | ||
ColumnType::UserDefinedType { | ||
type_name, | ||
keyspace, | ||
field_types, | ||
} => ColumnType::UserDefinedType { | ||
type_name: type_name.into_owned().into(), | ||
keyspace: keyspace.into_owned().into(), | ||
field_types: field_types | ||
.into_iter() | ||
.map(|(cow, column_type)| (cow.into_owned().into(), column_type.into_owned())) | ||
.collect(), | ||
}, | ||
ColumnType::SmallInt => ColumnType::SmallInt, | ||
ColumnType::TinyInt => ColumnType::TinyInt, | ||
ColumnType::Time => ColumnType::Time, | ||
ColumnType::Timeuuid => ColumnType::Timeuuid, | ||
ColumnType::Tuple(vec) => { | ||
ColumnType::Tuple(vec.into_iter().map(ColumnType::into_owned).collect()) | ||
} | ||
ColumnType::Uuid => ColumnType::Uuid, | ||
ColumnType::Varint => ColumnType::Varint, | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In many places in this impl you take Box<ColumnType<'frame>>
, call .into_owned()
on Box contents (thus dropping the Box) and then passing the result to Box::new
- this looks like unnecessary reallocation. I wonder if there is a way to avoid it because it is a big waste.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK this function is only going to be used in error conditions, to create owned ColumnType
for use in error types, so the waste does not matter.
scylla-cql/src/frame/value_tests.rs
Outdated
); | ||
} | ||
|
||
fn col_spec(name: &str, typ: ColumnType) -> ColumnSpec { | ||
fn col_spec(name: &str, typ: ColumnType<'static>) -> ColumnSpec { | ||
ColumnSpec { | ||
table_spec: TableSpec::owned("ks".to_string(), "tbl".to_string()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WDYT about introducing type alias ColumnTypeOwned
? It would be shorter to type and imo easier to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be done with any lifetime-generic type that we have in our codebase, and there are quite a number of them. Should we introduce such an alias for each of them?
I'm a bit reluctant about type aliases, because they tend to hide that the underlying type is the same yet differs wrt ownership. The thing is, we want to expose that fact.
/// - `Some(Some(...))` - non-null, present value | ||
pub struct UdtIterator<'frame> { | ||
all_fields: &'frame [(String, ColumnType)], | ||
all_fields: &'frame [(Cow<'frame, str>, ColumnType<'frame>)], | ||
type_name: &'frame str, | ||
keyspace: &'frame str, | ||
remaining_fields: &'frame [(String, ColumnType)], | ||
remaining_fields: &'frame [(Cow<'frame, str>, ColumnType<'frame>)], | ||
raw_iter: BytesSequenceIterator<'frame>, | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do those Cow
s have to be propagated to this files? Is there e problem with &'frame str
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a valid question. ColumnType::UserDefinedType
contains Vec<(Cow<'frame, str>, ColumnType<'frame>)>
, so when borrowing from that Vec, we get &'frame [(Cow<'frame, str>, ColumnType<'frame>)]
, which can't be cast to [(&'frame str, ColumnType<'frame>)]
. So, the answer is that Cow
s have to be propagated.
buf: &mut &[u8], | ||
global_table_spec: &Option<TableSpec<'static>>, | ||
col_count: usize, | ||
) -> StdResult<Vec<ColumnSpec>, ColumnSpecParseError> { | ||
) -> StdResult<Vec<ColumnSpec<'static>>, ColumnSpecParseError> { | ||
let mut col_specs = Vec::with_capacity(col_count); | ||
for col_idx in 0..col_count { | ||
let table_spec = if let Some(spec) = global_table_spec { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same ask for ColumnSpec: can we have ColumnSpecOwned alias instead of typing <'static>
everywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(already answered elsewhere)
It makes sense to have public getters, but crate-private fields. Thanks to that, in the future we will be able to change underlying data representation, e.g. use smart pointers like Arc or Cow, retaining API compatibility.
This is a necessary step towards non-allocating (in fact less-allocating) result metadata deserialization.
This enables us creating borrowed ColumnSpec that do not allocate, which is going to be used in further refactor for lazy metadata deserialization.
This hides internal implementation, enabling its alterations in next commits and possibly in the future.
This is a necessary step towards non-allocating (in fact less-allocating) result metadata deserialization.
Those functions are close to each other logically, so let's keep them close in code, too, for easier reading.
This is a step towards non-allocating result metadata deserialization. The allocation is moved out, to deser_col_specs.
This aids readability.
We assume that for each column, table spec is the same. As this is not guaranteed by the CQL protocol specification but only by how Cassandra and ScyllaDB work (no support for joins), we perform a sanity check.
86e2aa8
to
80027e2
Compare
v2.0:
|
ResultMetadata
lifetime genericResultMetadata
lifetime-generic
In the PR description, I listed out the changes introduced. |
Ref: #462
This is a necessary step towards non-allocating (in fact less-allocating) result metadata deserialization.
What's done
ColumnType
is made ownership-generic; there will be two distinct functions to deserialize owned and borrowedColumnType
;ColumnSpec
is made ownership-generic; in a follow-up PR, there will be a distinct function to deserialize borrowedVec<ColumnSpec<'frame>>
.ResultMetadata
is made ownership-generic;deser_table_spec
no longer allocates; it's up todeser_col_specs
to allocate or not, depending on desired ownership of deserialized metadata;deser_col_specs
verifies that table specs are the same for all columns. We assume it's true, but the CQL protocol does not enforce it. With that check, we will be able to hold only one table spec per query, not one per column; this will reduce memory footprint. TODO in a follow-up.Pre-review checklist
[] I added relevant tests for new features and bug fixes.[ ] I have provided docstrings for the public items that I want to introduce.[ ] I have adjusted the documentation in./docs/source/
.[ ] I added appropriateFixes:
annotations to PR description.