Skip to content
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

Introduce new deserialization framework upper layer abstractions #1093

Merged

Conversation

wprzytula
Copy link
Collaborator

@wprzytula wprzytula commented Oct 16, 2024

Contents

This PR:

  1. Introduces ResultMetadataHolder, a versatile container for ResultMetadata, for use in QueryResult etc.

  2. Introduces new lifetime-generic containers for Result:Rows response with varying degree of serialization/deserialization of metadata and rows:

  • RawMetadataAndRawRows,
  • DeserializedMetadataAndRawRows,

whose goal is to allow lazy deserialization of rows and deserializing metadata in the borrowed form (to reduce number of string allocations).

  1. Introduces RawRowsLendingIterator (see description below).

  2. Introduces two main abstractions belonging to the upper layer of the new deserialization framework:

  • (new) QueryResult,
  • (new) TypedRowStream,

and prefixes their legacy counterparts with Legacy_.

  1. Propagates new QueryResult and TypedRowStream down the internal code, converting them to legacy counterparts only when needed by the legacy Session's API.

Details

RawRowsLendingIterator

It is added in tandem with the already present (the low level, scylla_cql one, not the iterator.rs one!) RowIterator. The difference is that RawRowsLendingIterator owns the frame and the metadata, whereas RowIterator borrows it. The lending one is needed in session iterator interface, and the borrowing one is used with QueryResult.

Achieving old behaviour for dynamic deserialization purposes

If one still wants to deserialize arbitrary rows (as the old framework would do) and only then try to convert them dynamically to some proper types, this is doable by using the old Row struct as the target type, e.g.

let qr = session.query("SELECT a FROM ks.table")? // new Session's API not yet implemented in this PR
  .into_rows_result()?;
if let Some(rows_result) = qr {
  let rows = qr.rows::<Row>()?;
  for row in rows {
      // Type check and deserialization succeeds for any valid CQL type.

      // Do arbitrary dynamic magic with `row`.
  }
}

ColumnSpecs

The new ColumnSpecs struct is introduced that provides view over columns specification, but exposes only TableSpecView and ColumnSpecView structs instead of TableSpec and ColumnSpec, allowing us to modify the latter in the future without introducing breaking changes. One planned change is moving table_spec out of column_spec for reduced memory footprint (see #956), but as CQL protocol allows different table specs for different columns and we can't predict whether joins won't be introduced at some point in the future, we retain this flexibility by imposing a new layer of abstraction (the views).

New paging iterators

Instead of RowIterator (returning Rows) and TypedRowIterator (returning instances of the target type) both implementing Stream, now we have the following:

  • RawLendingStream
    • cannot implement Stream, because returns ColumnIterators that borrow from it,
    • provide type_check() and next() methods that can be used for low-level, manual deserialization (not recommended for ordinary users)
    • supports deserializing manually borrowed types (such as &str).
  • TypedRowLendingStream
    • created by calling into_typed::<TargetType>() on RawLendingStream,
    • type checks upon creation,
    • supports deserializing automatically borrowed types (such as &str),
    • does not implement Stream in order to support borrowed types.
  • TypedRowStream
    • created by calling into_stream() on TypedRowLendingStream,
    • implements Stream and hence does not support borrowed types.

TODO

Another step towards: #462

Pre-review checklist

  • I have split my patch into logically separate commits.
  • All commit messages clearly explain what they change and why.
  • I added relevant tests for new features and bug fixes.
  • All commits compile, pass static checks and pass test.
  • PR description sums up the changes and reasons why they should be introduced.
  • I have provided docstrings for the public items that I want to introduce.
  • I have adjusted the documentation in ./docs/source/.
  • I added appropriate Fixes: annotations to PR description.

@wprzytula wprzytula self-assigned this Oct 16, 2024
@wprzytula wprzytula added this to the 0.15.0 milestone Oct 16, 2024
@github-actions github-actions bot added the semver-checks-breaking cargo-semver-checks reports that this PR introduces breaking API changes label Oct 16, 2024
Copy link

github-actions bot commented Oct 16, 2024

cargo semver-checks detected some API incompatibilities in this PR.
Checked commit: b0fabe5

See the following report for details:

cargo semver-checks output
./scripts/semver-checks.sh --baseline-rev 64b4afcdb4286b21f6cc1acb55266d6607f250e0
+ cargo semver-checks -p scylla -p scylla-cql --baseline-rev 64b4afcdb4286b21f6cc1acb55266d6607f250e0
     Cloning 64b4afcdb4286b21f6cc1acb55266d6607f250e0
     Parsing scylla v0.14.0 (current)
      Parsed [  26.171s] (current)
     Parsing scylla v0.14.0 (baseline)
      Parsed [  23.720s] (baseline)
    Checking scylla v0.14.0 -> v0.14.0 (no change)
     Checked [   0.119s] 94 checks: 85 pass, 9 fail, 0 warn, 0 skip

--- failure auto_trait_impl_removed: auto trait no longer implemented ---

Description:
A public type has stopped implementing one or more auto traits. This can break downstream code that depends on the traits being implemented.
        ref: https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.36.0/src/lints/auto_trait_impl_removed.ron

Failed in:
  type FirstRowError is no longer UnwindSafe, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:442
  type FirstRowError is no longer RefUnwindSafe, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:442
  type SingleRowError is no longer UnwindSafe, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:458
  type SingleRowError is no longer RefUnwindSafe, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:458

--- failure derive_trait_impl_removed: built-in derived trait no longer implemented ---

Description:
A public type has stopped deriving one or more traits. This can break downstream code that depends on those types implementing those traits.
        ref: https://doc.rust-lang.org/reference/attributes/derive.html#derive
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.36.0/src/lints/derive_trait_impl_removed.ron

Failed in:
  type FirstRowError no longer derives Clone, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:442
  type FirstRowError no longer derives PartialEq, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:442
  type FirstRowError no longer derives Eq, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:442
  type SingleRowError no longer derives PartialEq, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:458
  type SingleRowError no longer derives Eq, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:458

--- failure enum_missing: pub enum removed or renamed ---

Description:
A publicly-visible enum cannot be imported by its prior path. A `pub use` may have been removed, or the enum itself may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.36.0/src/lints/enum_missing.ron

Failed in:
  enum scylla::transport::query_result::MaybeFirstRowTypedError, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:217
  enum scylla::transport::query_result::FirstRowTypedError, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:199
  enum scylla::transport::query_result::SingleRowTypedError, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:245

--- failure enum_variant_added: enum variant added on exhaustive enum ---

Description:
A publicly-visible enum without #[non_exhaustive] has a new variant.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#enum-variant-new
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.36.0/src/lints/enum_variant_added.ron

Failed in:
  variant SingleRowError:UnexpectedRowCount in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:461
  variant SingleRowError:TypeCheckFailed in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:465
  variant SingleRowError:DeserializationFailed in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:469
  variant FirstRowError:TypeCheckFailed in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:449
  variant FirstRowError:DeserializationFailed in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:453

--- failure enum_variant_missing: pub enum variant removed or renamed ---

Description:
A publicly-visible enum has at least one variant that is no longer available under its prior name. It may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.36.0/src/lints/enum_variant_missing.ron

Failed in:
  variant FirstRowError::RowsExpected, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:191
  variant SingleRowError::RowsExpected, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:237
  variant SingleRowError::BadNumberOfRows, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:241

--- failure inherent_method_missing: pub method removed or renamed ---

Description:
A publicly-visible method or associated fn is no longer available under its prior name. It may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.36.0/src/lints/inherent_method_missing.ron

Failed in:
  QueryResult::rows_num, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:42
  QueryResult::rows, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:52
  QueryResult::rows_typed, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:62
  QueryResult::rows_or_empty, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:78
  QueryResult::rows_typed_or_empty, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:85
  QueryResult::first_row, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:91
  QueryResult::first_row_typed, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:100
  QueryResult::maybe_first_row, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:106
  QueryResult::maybe_first_row_typed, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:112
  QueryResult::single_row, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:123
  QueryResult::single_row_typed, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:135
  QueryResult::col_specs, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:141
  QueryResult::get_column_spec, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:150
  QueryResult::rows_num, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:42
  QueryResult::rows, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:52
  QueryResult::rows_typed, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:62
  QueryResult::rows_or_empty, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:78
  QueryResult::rows_typed_or_empty, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:85
  QueryResult::first_row, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:91
  QueryResult::first_row_typed, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:100
  QueryResult::maybe_first_row, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:106
  QueryResult::maybe_first_row_typed, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:112
  QueryResult::single_row, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:123
  QueryResult::single_row_typed, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:135
  QueryResult::col_specs, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:141
  QueryResult::get_column_spec, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:150

--- failure struct_missing: pub struct removed or renamed ---

Description:
A publicly-visible struct cannot be imported by its prior path. A `pub use` may have been removed, or the struct itself may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.36.0/src/lints/struct_missing.ron

Failed in:
  struct scylla::transport::iterator::RowIterator, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/iterator.rs:41
  struct scylla::transport::query_result::RowsExpectedError, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:169
  struct scylla::transport::iterator::TypedRowIterator, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/iterator.rs:895
  struct scylla::transport::query_result::RowsNotExpectedError, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:182

--- failure struct_pub_field_missing: pub struct's pub field removed or renamed ---

Description:
A publicly-visible struct has at least one public field that is no longer available under its prior name. It may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.36.0/src/lints/struct_pub_field_missing.ron

Failed in:
  field rows of struct QueryResult, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:18
  field warnings of struct QueryResult, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:20
  field tracing_id of struct QueryResult, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:22
  field serialized_size of struct QueryResult, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:26
  field rows of struct QueryResult, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:18
  field warnings of struct QueryResult, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:20
  field tracing_id of struct QueryResult, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:22
  field serialized_size of struct QueryResult, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla/src/transport/query_result.rs:26

--- failure struct_pub_field_now_doc_hidden: pub struct field is now #[doc(hidden)] ---

Description:
A pub field of a pub struct is now marked #[doc(hidden)] and is no longer part of the public API.
        ref: https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.36.0/src/lints/struct_pub_field_now_doc_hidden.ron

Failed in:
  field QueryResult.warnings in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:137
  field QueryResult.tracing_id in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:137
  field QueryResult.warnings in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:137
  field QueryResult.tracing_id in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:137

     Summary semver requires new major version: 9 major and 0 minor checks failed
    Finished [  50.072s] scylla
     Parsing scylla-cql v0.3.0 (current)
      Parsed [  11.178s] (current)
     Parsing scylla-cql v0.3.0 (baseline)
      Parsed [  10.109s] (baseline)
    Checking scylla-cql v0.3.0 -> v0.3.0 (no change)
     Checked [   0.106s] 94 checks: 93 pass, 1 fail, 0 warn, 0 skip

--- failure struct_missing: pub struct removed or renamed ---

Description:
A publicly-visible struct cannot be imported by its prior path. A `pub use` may have been removed, or the struct itself may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.36.0/src/lints/struct_missing.ron

Failed in:
  struct scylla_cql::frame::response::result::Rows, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla-cql/src/frame/response/result.rs:588
  struct scylla_cql::types::deserialize::result::RowIterator, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-64b4afcdb4286b21f6cc1acb55266d6607f250e0/7cd63be660774e034f17246d4d786d0cc0c76c91/scylla-cql/src/types/deserialize/result.rs:9

     Summary semver requires new major version: 1 major and 0 minor checks failed
    Finished [  21.441s] scylla-cql
make: *** [Makefile:61: semver-rev] Error 1

scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/types/deserialize/result.rs Show resolved Hide resolved
scylla-cql/src/types/deserialize/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/types.rs Show resolved Hide resolved
scylla-cql/src/frame/types.rs Show resolved Hide resolved
scylla/src/transport/iterator.rs Outdated Show resolved Hide resolved
scylla/src/transport/iterator.rs Outdated Show resolved Hide resolved
@@ -47,7 +47,7 @@ use std::{
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commit: iterator: adjust to the new deserialization framework

Unfortunately, due to the limitations of the Stream trait, currently the TypedRowIterator cannot deserialize types that need to borrow from the serialized frame contents.

I've played around with this, by removing RowT: 'static bounds and trying to create a TypedRowStream over some borrowed type (tried with &str and my custom wrapper over &str). I think that compiler error messages are not really insightful.. But this is only my opinion.

Funny thing is that compiler returns two different messages. For simply &str, with the following snippet:

let typed_iter = raw_iter.into_typed::<(&str,)>().unwrap();
let mut typed_stream = typed_iter.into_stream();
let s = typed_stream.next().await;

what I get is:

error: implementation of `DeserializeRow` is not general enough
    --> scylla/src/transport/connection.rs:1270:17
     |
1270 |         let s = typed_stream.next().await;
     |                 ^^^^^^^^^^^^^^^^^^^ implementation of `DeserializeRow` is not general enough
     |
     = note: `(&str,)` must implement `DeserializeRow<'0>`, for any lifetime `'0`...
     = note: ...but it actually implements `DeserializeRow<'1>`, for some specific lifetime `'1`

error: implementation of `DeserializeRow` is not general enough
    --> scylla/src/transport/connection.rs:1270:37
     |
1270 |         let s = typed_stream.next().await;
     |                                     ^^^^^ implementation of `DeserializeRow` is not general enough
     |
     = note: `(&str,)` must implement `DeserializeRow<'0>`, for any lifetime `'0`...
     = note: ...but it actually implements `DeserializeRow<'1>`, for some specific lifetime `'1`

, while for the snippet with my custom wrapper:

struct StrWrapper<'a> {
    s: &'a str,
}

// implementations of DeserializeRow and DeserializeValue for StrWrapper

...


let typed_iter = raw_iter.into_typed::<(StrWrapper,)>().unwrap();
let mut typed_stream = typed_iter.into_stream();
let s = typed_stream.next().await;

I get:

error[E0599]: no method named `next` found for struct `TypedRowStream` in the current scope
    --> scylla/src/transport/connection.rs:1270:30
     |
1270 |         let s = typed_stream.next().await;
     |                              ^^^^ method not found in `TypedRowStream<(StrWrapper<'_>,)>`
     |
    ::: scylla/src/transport/iterator.rs:1068:1
     |
1068 | pub struct TypedRowStream<RowT> {
     | ------------------------------- method `next` not found for this struct
     |
     = help: items from traits can only be used if the trait is implemented and in scope
     = note: the following traits define an item `next`, perhaps you need to implement one of them:
             candidate #1: `Iterator`
             candidate #2: `StreamExt`
help: trait `StreamExt` which provides `next` is implemented but not in scope; perhaps you want to import it
     |
1    + use futures::StreamExt;
     |

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the second case (your wrapper), you must have forgotten to use futures::StreamExt as _, correct?

Copy link
Contributor

@muzarski muzarski Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Previously, I did not include the warning about unused import which appeared as well.

error[E0599]: no method named `next` found for struct `TypedRowStream` in the current scope
    --> scylla/src/transport/connection.rs:1238:20
     |
1238 |         let s = ts.next().await;
     |                    ^^^^ method not found in `TypedRowStream<(StrWrapper<'_>,)>`
     |
    ::: scylla/src/transport/iterator.rs:1065:1
     |
1065 | pub struct TypedRowStream<RowT> {
     | ---------------------------------------- method `next` not found for this struct
     |
     = help: items from traits can only be used if the trait is implemented and in scope
     = note: the following traits define an item `next`, perhaps you need to implement one of them:
             candidate #1: `Iterator`
             candidate #2: `StreamExt`
help: trait `StreamExt` which provides `next` is implemented but not in scope; perhaps you want to import it
     |
1    + use futures::StreamExt;
     |

warning: unused import: `futures::StreamExt`
    --> scylla/src/transport/connection.rs:1235:13
     |
1235 |         use futures::StreamExt as _;
     |             ^^^^^^^^^^^^^^^^^^
     |
     = note: `#[warn(unused_imports)]` on by default

@wprzytula wprzytula force-pushed the new-deserialization-api-upper-layer-heroes branch from d25a3d8 to 2a76aa4 Compare October 20, 2024 20:18
@wprzytula
Copy link
Collaborator Author

v1.1: addressed @muzarski comments - thanks for them!

@wprzytula wprzytula marked this pull request as draft October 21, 2024 19:38
@wprzytula
Copy link
Collaborator Author

Marked as draft, because #1101 must be merged first, and then this PR will be adjusted to two lifetime parameters in the deserialization framework.

@wprzytula wprzytula force-pushed the new-deserialization-api-upper-layer-heroes branch from 2a76aa4 to ea163d6 Compare October 22, 2024 14:00
@wprzytula
Copy link
Collaborator Author

v1.2: Rebased on main.

@wprzytula wprzytula force-pushed the new-deserialization-api-upper-layer-heroes branch from ea163d6 to 1e045c2 Compare October 22, 2024 19:41
@wprzytula
Copy link
Collaborator Author

v2.0:

@wprzytula wprzytula marked this pull request as ready for review October 22, 2024 19:46
@wprzytula wprzytula force-pushed the new-deserialization-api-upper-layer-heroes branch from 1e045c2 to a2123cf Compare October 22, 2024 19:55
Copy link
Contributor

@muzarski muzarski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm impressed - mostly some questions/nits

scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla/src/transport/query_result.rs Outdated Show resolved Hide resolved
scylla/src/transport/query_result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla/src/transport/iterator.rs Outdated Show resolved Hide resolved
scylla/src/transport/iterator.rs Outdated Show resolved Hide resolved
let rows = pages_received
.rows
.into_legacy_rows(PagingStateResponse::NoMorePages)?;
let page_received = receiver.recv().await.unwrap()?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea why this variable was previously called pages_received (plural)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, no.

scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla/src/transport/session.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/response/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/types/deserialize/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/types/deserialize/result.rs Outdated Show resolved Hide resolved
Comment on lines 19 to 22
/// A view over specification of a table in the database.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct TableSpecView<'res> {
table_name: &'res str,
ks_name: &'res str,
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the need for this and ColumnSpecView.

We will be able to modify TableSpec and ColumnSpec themselves if there is ever a need for it, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The need for this is precisely caused by your requirement about moving TableSpecs back into ColumnSpec.

If you remember, I suggested that we move TableSpec out of ColumnSpec (as in the current implementation of Scylla and Cassandra it's always the same for all columns - no support for joins). You agreed only on condition that we will be able to revert this without introducing breaking changes once Scylla or Cassandra introduces joins.

ColumnSpecView will always borrow the keyspace name, no matter whether it's contained in ResultMetadata directly or in a ColumnSpec. Therefore, this abstraction layer defends us from the API breakage threat.
Conversely, if we returned ColumnSpec to the user instead of ColumnSpecView, then once we remove TableSpec from ColumnSpec, users will no longer be able to get table name and keyspace name from ColumnSpec, so this breaks the API.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the benefit of removing TableSpec from ColumnSpec - it is borrowed anyway. Can we just keep it there and get rid of this additional layer?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is only borrowed (i.e., it contains Cow::Borrowed variants) if the metadata itself is borrowed as a whole, which is not possible for e.g. RowIterator (query_iter() API) and for RowsDeserializer in the owned variant. So, the additional layer saves possible allocations in such scenarios.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer true now that metadata is always borrowed, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Apart from when being shared with PreparedStatement's cached metadata, result metadata is always deserialized in the borrowed form.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remember that you mentioned that taking TableSpec out of ColumnSpec will also have positive impact on errors' size? I mean, there were more arguments to move TableSpec out of ColumnSpec.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember that very well. In case of errors we probably still want to have the table name stored there - but I'm not sure. We can merge this PR as-is, and discuss later (but before release!) if it's worth it to introduce the Views.

scylla/src/transport/query_result.rs Outdated Show resolved Hide resolved
@wprzytula
Copy link
Collaborator Author

Which could still be a problem if there is a lot of columns, right?

I don't know what this is about. GitHub displays it as an alone message without reasonable context.

@Lorak-mmk
Copy link
Collaborator

Which could still be a problem if there is a lot of columns, right?

I don't know what this is about. GitHub displays it as an alone message without reasonable context.

That happens if, as a part of review, you respond in a discussion in some other review. In that case this comment is a response here: #1093

@Lorak-mmk Lorak-mmk force-pushed the new-deserialization-api-upper-layer-heroes branch from 88447e8 to a2123cf Compare October 25, 2024 13:56
@wprzytula
Copy link
Collaborator Author

Which could still be a problem if there is a lot of columns, right?

I don't know what this is about. GitHub displays it as an alone message without reasonable context.

That happens if, as a part of review, you respond in a discussion in some other review. In that case this comment is a response here: #1093

The link points me to the PR as a whole, so again no luck in determining the context.

@wprzytula wprzytula force-pushed the new-deserialization-api-upper-layer-heroes branch from a2123cf to d149059 Compare October 28, 2024 07:25
@wprzytula
Copy link
Collaborator Author

v2.1: addressed another bunch of @muzarski comments.

@wprzytula wprzytula force-pushed the new-deserialization-api-upper-layer-heroes branch from d149059 to c1cfcdb Compare October 28, 2024 07:26
@wprzytula
Copy link
Collaborator Author

v2.2: rebased on main.

@wprzytula wprzytula force-pushed the new-deserialization-api-upper-layer-heroes branch 2 times, most recently from 1451865 to 2deb9ee Compare October 28, 2024 08:23
piodul and others added 15 commits November 6, 2024 15:26
Soon, a new QueryResult will be introduced with a slightly different
API. The old one will be preserved as LegacyQueryResult, in order to
make migration easier.

This commit renames the existing QueryResult to LegacyQueryResult, as
well as the query_result module to legacy_query_result. The new
QueryResult will be introduced in later commits.
IntoTypedRows and TypedRowIter, being helper items for
LegacyQueryResult, are moved to legacy_query_result.rs from session.rs
not to clutter session.rs. They are still re-exported in session.rs not
to break compatibility.
This has the following consequences:
1) Legacy deserialization framework upper layer abstractions
   (LegacyQueryResult, LegacyRowIterator, etc.) will benefit from the
   lazy metadata deserialization, reducing number of allocations for
   metadata (but it's not going to have any impact, because the legacy
   deserialization framework allocates `Row`s anyway);
2) Legacy deserialization framework abstractions can no longer return
   `&[ColumnSpec<'static>]`, because the SelfBorrowedMetadataContainer
   contains borrowed `ColumnSpec`s;
3) In the next commits, conversion from the new abstractions to the
   legacy abstractions will be cheaper (wrt efficiency) and easier
   (wrt implementing it).
(Re)-introduces QueryResult. It is quite similar to the old
(Legacy)QueryResult, but it keeps rows and metadata in an unparsed
state (via Bytes) and has methods that allow parsing the contents
using the new API.

Helper method names are similar to what the old QueryResult had, just
with the `_typed` suffix dropped - as now it is always required to
provide the type of rows when parsing them, this suffix sounded
redundant.

There is one crucial change to the API. Motivation is as follows:
`QueryResult` can represent a non-Rows response, so every rows-related
operation on `QueryResult` may return "NonRowsResponse" error, which is
inconvenient.

The solution is to extract a new struct, `QueryRowsResult`. Thanks to
that, one has to handle "NonRowsResponse" error only once, upon
`QueryResult::into_rows_result()`. All further methods (`rows(),
`single_row()`, etc.) may no longer fail with that error, which provides
a clean step of conversion from any Result frame to Result:Rows frame.

The drawback is that now there is a new call required in the call chain
to deserialize a result, namely `.into_rows_result()`.

Co-authored-by: Wojciech Przytuła <[email protected]>
New QueryResult tests are going to require result metadata serialization
capabilities, as RawRows keep result metadata in a serialized form.
After the deserialization refactor with lazy result metadata
deserialization, we will no longer have access to rows serialized size
and rows count in Session and in RowIteratorWorker. Therefore, traces
can no longer record that information.

A further commit in this PR brings back serialized size to the span,
but there is an important change: now the size is of both raw metadata
and raw rows; before, metadata was not accounted.
Now, `result::deser_rows` returns RawRows instead of Rows, postponing
any actual deserialization of response contents to a later time. The
RawRows are pushed down the call stack, converted to the new QueryResult
at some point and only converted to LegacyQueryResult where the API
requires it.

Co-authored-by: Wojciech Przytuła <[email protected]>
Like in the case of LegacyQueryResult, TypedRowIterator will be
replaced with a new one, and the old one preserved for compatibility
reasons. As a first step, it is renamed to LegacyTypedRowIterator.

Similarly, RowIterator is renamed to LegacyRowIterator to mark that it
is the part of the old API. There won't be a new RowIterator, though.

Co-authored-by: Wojciech Przytuła <[email protected]>
In order to make further commits easier on the eyes, contents of the
LegacyRowIterator::poll_next method are moved to the new
poll_next_internal method.

Co-authored-by: Wojciech Przytuła <[email protected]>
The iterator module has a small number of functions which return
Poll<Option<Result<T, E>>>, call other functions that return such a
stacked type and are support to continue if value obtained is not of
form Ready(Some(Ok(x))). This requires a multiline pattern match
instruction, which is basically boilerplate.

This commit introduces the ready_some_ok! macro which hides the
aforementioned boilerplate. Conceptually, it is similar to the existing
std::task::ready! macro.

Co-authored-by: Wojciech Przytuła <[email protected]>
This commit refactors the part responsible for acquiring the next page
by the LegacyRowIterator to a different function. This change is a
preparation necessary to support the new deserialization interface -
there will be a method that can return deserialized type that borrows
from the current iterator, and - for "lifetimes reasons" - acquiring the
next page must be put into a separate method.

Co-authored-by: Wojciech Przytuła <[email protected]>
Now, worker-related code comes strictly before iterator/stream-related
code. This locality aid readability.
This commit makes the RowIteratorWorker pass raw rows to the main tokio
task, instead of the eagerly deserialized Rows.

The equivalent of the old RowIterator is now QueryPager. It cannot be
conveniently iterated on, as it does not have any information about the
column types. It features (yet not exposes) a `next()` method for
deserializing consecutive `ColumnIterator`s. Users cannot manually
perform deserialization using this method directly, because will be able
to utilise the preferred (typed) API that will be added in the next
commit. If they prefer manual deserialization, they will be be able to
specify `ColumnIterator` as the deserialized row type, and proceed with
manual deserialization from there.

The legacy iterators are preserved by wrapping around QueryPager.

Co-authored-by: Wojciech Przytuła <[email protected]>
Not to clutter iterator.rs with legacy abstractions, they are moved to
a new module: `legacy`.

I recommend viewing this commit **without** whitespace changes.
@wprzytula wprzytula force-pushed the new-deserialization-api-upper-layer-heroes branch from a47c6a4 to 6e4309e Compare November 6, 2024 15:27
@Lorak-mmk
Copy link
Collaborator

This is no longer true now that metadata is always borrowed, right?

Actually, metadata is still always borrowed in scylla crate, unless the one cached in PreparedStatement is used. Both RawRowLendingIterator and QueryRowsResult hold ResultMetadataHolder, so we get that feature thanks to yoke.

I don't understand how this comment relates to the discussion that my comment was posted in.

@wprzytula
Copy link
Collaborator Author

I don't understand how this comment relates to the discussion that my comment was posted in.

GitHub again displayed your comment as standalone, so I didn't know that it refers to any particular discussion 🙈

@Lorak-mmk
Copy link
Collaborator

I don't understand how this comment relates to the discussion that my comment was posted in.

GitHub again displayed your comment as standalone, so I didn't know that it refers to any particular discussion 🙈

If you see that it is not possible to respond to a comment (there is no "Reply" text field) that means this comment was posted in some other thread which you need to find.

wprzytula and others added 3 commits November 6, 2024 17:14
This commit finishes the work related to adjusting the iterators module
to the new deserialization framework.

The previous commit brought RawRowsLendingStream, which can deserialize
ColumnIterators. This commit introduces new TypedRowLendingStream, which
type-checks once and then deserializes from ColumnIterators into rows.
RawRowsLendingStream can be converted to TypedRowLendingStream by
calling the `into_typed()` method.

Unfortunately, due to the limitations of the Stream trait (no support
for lending streams, analogous to lending iterators in case of
RawRowsLendingIterator), a Stream cannot be used to deserialize borrowed
types (i.e. those that borrow from the frame serialized contents).

In order to give users both capabilities:
1) deserializing borrowed types (for efficiency),
2) deserializing using Stream (for convienience),
two distinct types are used: TypedRowLendingStream and TypedRowStream.
The first supports borrowed types and the second implements Stream.

To sum up, instead of `RowIterator` (returning `Row`s) and
`TypedRowIterator` (returning instances of the target type) both
implementing `Stream`, now we have the following:
- `RawRowsLendingStream`
  - cannot implement `Stream`, because returns `ColumnIterator`s that
    borrow from it,
  - provide `type_check()` and `next()` methods that can be used for
    low-level, manual deserialization (not recommended for ordinary
    users)
  - supports deserializing manually borrowed types (such as `&str`).
- `TypedRowLendingStream`
  - created by calling `into_typed::<TargetType>()` on `RawIterator`,
  - type checks upon creation,
  - supports deserializing borrowed types (such as `&str`),
  - does not implement `Stream` in order to support borrowed types,
  - provides basic Stream-like methods (`next()`, `try_next()`),
- `TypedRowStream`
  - created by calling `into_stream()` on `TypedRowLendingStream`,
  - implements `Stream` and hence does not support borrowed types.

Co-authored-by: Piotr Dulikowski <[email protected]>
It is no longer needed. For compatibility with LegacyQueryResult and
LegacyRowIterator, higher-layer conversions suffice.
Even though we can no longer record rows serialized size without
accounting metadata, we can record their serialized size together.
@wprzytula wprzytula force-pushed the new-deserialization-api-upper-layer-heroes branch from 6e4309e to b0fabe5 Compare November 6, 2024 16:15
@wprzytula
Copy link
Collaborator Author

v3.2.1: exposed column_specs() and tracing_ids() on TypedRowStream too, similarly to TypedRowLendingStream.

@wprzytula wprzytula requested a review from Lorak-mmk November 6, 2024 16:16
@wprzytula wprzytula merged commit dce9e9f into scylladb:main Nov 6, 2024
10 of 11 checks passed
@wprzytula wprzytula deleted the new-deserialization-api-upper-layer-heroes branch November 6, 2024 16:29
@wprzytula wprzytula mentioned this pull request Nov 6, 2024
8 tasks
@wprzytula wprzytula mentioned this pull request Nov 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/deserialization semver-checks-breaking cargo-semver-checks reports that this PR introduces breaking API changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants