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 API #1057

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

wprzytula
Copy link
Collaborator

Here it is, finally!

After #1004 with preparation, #970 with lower layer and #1024 with derive macros,
this PR introduces the upper layers of the new deserialization framework.

The Big Picture

  1. Legacy structures are prefixed Legacy, deprecated and planned to be removed soon:
    • QueryResult -> LegacyQueryResult
    • (Typed)RowIterator -> Legacy(Typed)RowIterator
  2. New structures are added in the upper layer that support lazy deserialization.
    • RawRows replace Rows. They contain deserialized metadata, but rows are still in serialized form.
    • RawIterator, TypedRowIterator and TypedRowStream replace LegacyRowIterator and LegacyTypedRowIterator.
    • QueryResult replaces LegacyQueryResult.
  3. RawRowsLendingIterator is added (see details below)
  4. Both QueryResult's and paging iterators' API is changed wrt column specs inspection (see details below).
  5. Session is made generic over the deserialization API kind, so that both old and new API can be used in the transition period by the users. The choice is done on SessionBuilder's level, by choosing either build() or build_legacy() method.
  6. Internal interfaces (e.g. Connection's API, especially query_iter() widely used in topology.rs) are adjusted to use the new API.
  7. Tests, examples and doctests are adjusted to use the new API.
  8. Deserialization logic of collections is changed in a way that now it is possible to deserialize empty collections into Collection instead of Option<Collection>, even though the DB represents empty collections as nulls.
  9. A test is added based on WIP: session_test: regression test for empty collections deserialization #1007, showing that now it is indeed possible to deserialize empty collections into Collection instead of Option<Collection>.

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")?;
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:

  • RawIterator
    • 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).
  • TypedRowIterator
    • created by calling into_typed::<TargetType>() on RawIterator,
    • 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 TypedRowIterator,
    • implements Stream and hence does not support borrowed types.

TODO

  • Documentation needs to be adjusted. This is to be done in a follow-up.
  • A migration guide is planned for a follow-up.
  • Deprecation notices for the legacy API are done, but delayed for a follow-up not to make this PR too huge.

Fixes: #964
Fixes: #1001
Nearly_resolves: #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 Aug 14, 2024
@wprzytula wprzytula added this to the 0.14.0 milestone Aug 14, 2024
@wprzytula
Copy link
Collaborator Author

v1.0.1: clippy fixes

@github-actions github-actions bot added the semver-checks-breaking cargo-semver-checks reports that this PR introduces breaking API changes label Aug 14, 2024
Copy link

github-actions bot commented Aug 14, 2024

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

See the following report for details:

cargo semver-checks output
./scripts/semver-checks.sh --baseline-rev 01255af8f8f0da14a91561b34674bc89739127d5
+ cargo semver-checks -p scylla -p scylla-cql --baseline-rev 01255af8f8f0da14a91561b34674bc89739127d5
     Cloning 01255af8f8f0da14a91561b34674bc89739127d5
     Parsing scylla v0.14.0 (current)
      Parsed [  22.131s] (current)
     Parsing scylla v0.14.0 (baseline)
      Parsed [  21.279s] (baseline)
    Checking scylla v0.14.0 -> v0.14.0 (no change)
     Checked [   0.118s] 89 checks: 80 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.35.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:476
  type FirstRowError is no longer RefUnwindSafe, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:476
  type SingleRowError is no longer UnwindSafe, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:492
  type SingleRowError is no longer RefUnwindSafe, in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:492

--- 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.35.0/src/lints/derive_trait_impl_removed.ron

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

--- 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.35.0/src/lints/enum_missing.ron

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

--- 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.35.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:495
  variant SingleRowError:TypeCheckFailed in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:499
  variant SingleRowError:DeserializationFailed in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:503
  variant FirstRowError:TypeCheckFailed in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:483
  variant FirstRowError:DeserializationFailed in /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:487

--- 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.35.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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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.35.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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/scylla/src/transport/query_result.rs:150
  TypedRowIterator::get_tracing_ids, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/scylla/src/transport/iterator.rs:902
  TypedRowIterator::get_column_specs, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/scylla/src/transport/iterator.rs:907

--- 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.35.0/src/lints/struct_missing.ron

Failed in:
  struct scylla::transport::query_result::RowsNotExpectedError, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/scylla/src/transport/query_result.rs:182
  struct scylla::transport::iterator::RowIterator, previously in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/target/semver-checks/git-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/scylla/src/transport/query_result.rs:169

--- 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.35.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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/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.35.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:140
  field QueryResult.tracing_id in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:140
  field QueryResult.warnings in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:140
  field QueryResult.tracing_id in file /home/runner/work/scylla-rust-driver/scylla-rust-driver/scylla/src/transport/query_result.rs:140

     Summary semver requires new major version: 9 major and 0 minor checks failed
    Finished [  43.609s] scylla
     Parsing scylla-cql v0.3.0 (current)
      Parsed [  10.696s] (current)
     Parsing scylla-cql v0.3.0 (baseline)
      Parsed [  10.685s] (baseline)
    Checking scylla-cql v0.3.0 -> v0.3.0 (no change)
     Checked [   0.105s] 89 checks: 88 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.35.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-01255af8f8f0da14a91561b34674bc89739127d5/d3660899f816d752af186b03d09ae10dae0c41a9/scylla-cql/src/frame/response/result.rs:588

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

@wprzytula
Copy link
Collaborator Author

v1.0.2: docstrings fixes

@wprzytula
Copy link
Collaborator Author

v1.0.3: tests fixes.

@wprzytula
Copy link
Collaborator Author

v1.0.4: docstring fixes.

@wprzytula wprzytula modified the milestones: 0.14.0, 0.15.0 Aug 20, 2024
Copy link
Collaborator

@Lorak-mmk Lorak-mmk left a comment

Choose a reason for hiding this comment

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

Posting the comments that I already made so that they are not lost

scylla-cql/src/types/deserialize/result.rs Outdated Show resolved Hide resolved
scylla-cql/src/frame/frame_errors.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
Comment on lines 14 to 20
#[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.

Is there a reason for only deriving PartialEq and Eq in test?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I actually don't understand why this struct is introduced at all. TableSpec already has Cow's that can hold references, and so is parametrized by lifetime. What do we gain by introducing this struct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As TableSpec is defined in scylla-cql, it must have public API (a public constructor and/or public fields), for scylla crate to be able to access it. Hence, we don't want to expose TableSpec to users at all; instead, it's better to only expose types defined in scylla, because then we can truly control visibility using pub and pub(crate).

This is a pattern that I believe we should employ commonly, because then we will be able to make code adjustments to inner types, even changing their names and/or types, without introducing breaking API changes.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Is a public contructor a problem in this case? I don't see why, but probably I don't understand something.

This patterns means basically duplicating a lot of code from scylla-cql. If we have to do this then maybe scylla-cql should not exist?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

  1. scylla-cql was created specifically to allow code reuse for scylla-proxy. As long as scylla-proxy exists and we don't want to duplicate low-level ser/de code there, we need scylla-cql.
  2. You see this as duplicating code. I see this as introducing a new layer - stable abstraction. This is something that a stable driver should have, independently of its inner working. I have an impression that Rust driver has not yet developed such layer, and its API more-or-less reflects its inner implementation details. For the sake of future compatibility and more freedom to modify things after 1.0, I believe we need such layer, consisting of exposed yet extremely stable types. Even if in some cases it boils down to an identity-or-nearly-identity 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.

To be honest, I hate the dual world of scylla-cql and scylla. I feel unsafe about those pubs in scylla-cql.
Perhaps we could at least move some part of scylla-cql to scylla. It seems that scylla-proxy does not use the serialization nor deserialization framework anyway (even though I initially intended it to do so - to be capable of e.g. injecting arbitrary data).

Comment on lines 42 to 50
/// A view over specification of a column returned by the database.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct ColumnSpecView<'res> {
table_spec: TableSpecView<'res>,
name: &'res str,
typ: &'res ColumnType,
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We could put Cows in ColumnSpec instead of creating a new type.

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 answered this in the thread related to TableSpecView.

Comment on lines 79 to 85
/// A view over specification of columns returned by the database.
#[derive(Debug, Clone, Copy)]
pub struct ColumnSpecs<'res> {
specs: &'res [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 assume that this type is introduced so that user can use its methods like get_by_index etc?
If so, it will also be redundant if we get rid of TableSpecView / ColumnSpecView.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This type is intentionally introduced to provide a stable abstraction over possibly unstable inner types.

scylla/src/transport/iterator.rs Outdated Show resolved Hide resolved
scylla/src/transport/iterator.rs Show resolved Hide resolved
Comment on lines 1044 to 1075
/// Stream implementation for TypedRowStream.
///
/// It only works with owned types! For example, &str is not supported.
impl<RowT> Stream for TypedRowStream<RowT>
where
Copy link
Collaborator

Choose a reason for hiding this comment

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

So a user that uses execute_iter won't be able to deserialize to non-owned types?
Is this limitation permanent or is it possible to lift it in the future (e.g. by getting rid of fetching Tokio task as we discussed offline one day)?

Copy link
Collaborator

Choose a reason for hiding this comment

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

If it is a permanent limitation then it is a severe one which is quite sad...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So a user that uses execute_iter won't be able to deserialize to non-owned types?

Fortunately, no!

The @piodul's version of the refactor indeed had that limitation. The reason is that Stream, analogously to Iterator, does not use GATs and hence does not permit lending from itself. Therefore, if we want to have Stream implemented, we can't return borrowed items.
But then I discovered that we can introduce two separate types:

  • TypedRowIterator - does not implement Stream, implements next() method that permits lending from itself;
  • TypedRowStream - implements Stream, but has a limitation for owned types only.

TypedRowIterator is convertible to TypedRowStream for free. This solves the problem.

@wprzytula wprzytula marked this pull request as draft August 22, 2024 16:58
@wprzytula
Copy link
Collaborator Author

v2.0:

  • rebased on main, incorporating paging-related changes,
  • resolved some Karol's comments.

@wprzytula
Copy link
Collaborator Author

v2.1:

  • rebased on main.

@wprzytula
Copy link
Collaborator Author

v2.1.1:

  • fixed doctests.

This is a tiny refactor, which makes further commits easier on eyes.
@wprzytula wprzytula force-pushed the new-deserialization-api branch 3 times, most recently from 01883f7 to 6b713c1 Compare October 15, 2024 18:08
piodul and others added 3 commits October 16, 2024 13:47
Introduces new structs for lazy deserialization of RESULT:Rows frames.

Introduces `ResultMetadataHolder`, which is, unsurprisingly, a
versatile holder for ResultMetadata. It allows 3 types of ownership:
1) borrowing it from somewhere, be it the RESULT:Rows frame
   or the cached metadata in PreparedStatement;
2) owning it after deserializing from RESULT:Rows;
3) sharing ownership of metadata cached in PreparedStatement.

Introduces new structs representing the RESULT:Rows CQL frame body,
in various phases of deserialization:
- `RawRows` only deserializes (flags and) paging size in order to pass
  it directly to the user; keeps metadata and rows in serialized,
  unparsed form;
- `RawRowsWithDeserializedMetadata` deserializes metadata, but keeps
  rows serialized;

`RawRowsWithDeserializedMetadata` is lifetime-generic and can be
deserialized from `RawRows` to borrowed or owned form by corresponding
methods on `RawRows`.

Co-authored-by: Wojciech Przytuła <[email protected]>
The iterator is analogous to RowIterator, but instead of borrowing from
external frame bytes and metadata with 'frame lifetime, it owns them
and lends them from itself. Thus, it cannot implement Iterator trait.
It does not, however, prevent us from exposing a `next()` method on it.

The iterator is going to be used in new iterator API for queries
(i.e., the one connected to `{query,execute}_iter`), where borrowing is
not suitable (or even possible) due to the design of that API.

Tests of RowIterator are shared with the new RawRowsLendingIterator,
by introducing a new LendingIterator trait using GATs.
Due to a bug/limitation in the compiler, some leaking was needed in
tests. This is not a problem IMO, however, because tiny structures are
leaked, only constant amount of memory is leaked, and these are only
tests.
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.
piodul and others added 12 commits October 16, 2024 15:15
(Re)-introduces QueryResult. It is quite similar to the old
(Legacy)QueryResult, but it keeps rows and metadata  in an unparsed
state (via RawRows) 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:
1) `QueryResult` can represent a non-Rows response, so every
   rows-related operation on `QueryResult` may return "NonRowsResponse"
   error, which is inconvenient;
2) `QueryResult` is an owned type, so it cannot deserialize metadata
   in the borrowed flavour (i.e., using strings from the frame bytes
   directly) and it must allocate metadata (mainly ColumnSpecs) on the
   heap.
The solution for both is to extract a new struct, `RowsDeserializer`,
which is parametrized by a lifetime and hence can borrow metadata from
the frame. Moreover, one has to handle "NonRowsResponse" error only
once, upon `RowsDeserializer` creation. 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 `.row_deserializer()`.

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.
This reverts commit 6e6e85ea60962512daf992d01872faf730958193.
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.
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 finishes the work related to adjusting the iterators module
to the new deserialization framework.

The equivalent of the old RowIterator is now RawIterator (notice the
vowel change). Despite the name, it cannot actually be iterated on, as
it does not have any information about the column types. After calling
the `into_typed()` method it gets converted into TypedRowIterator that
can actually be iterated on.

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

Co-authored-by: Wojciech Przytuła <[email protected]>
It is no longer needed. For compatibility with LegacyQueryResult and
LegacyRowIterator, higher-layer conversions suffice.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/deserialization enhancement New feature or request semver-checks-breaking cargo-semver-checks reports that this PR introduces breaking API changes
Projects
None yet
3 participants