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

Unify macro attributes between serialization and deserialization derive macros #1119

Merged
merged 12 commits into from
Nov 14, 2024

Conversation

wprzytula
Copy link
Collaborator

@wprzytula wprzytula commented Nov 11, 2024

Motivation

When deserialization macros were introduced in #1024, their attributes differed in naming and usage from those introduced in #851 for serialization derive macros.

This PR aims to unify attributes between SerializeValue and DeserializeValue, and between SerializeRow and DeserializeRow, so that both traits can be derived for a struct without a clash in attribute parsing.

Fixes: #462
FINALLY!

What's done

  1. In SerializeValue: force_exact_match -> forbid_excess_udt_fields (attribute renamed for consistency with DeserializeValue);
  2. Flavor is shared between serialization and deserialization derive macros, and both have the same syntax for choosing the flavor: flavor = "enforce_order";
  3. allow_missing is supported for SerializeValue, as it is in DeserializeValue;
  4. default_when_null is parsed and ignored in SerializeValue, as it's a no-op for serialization (it's only useful for deserialization, as it says: "If I got NULL for a non-Option Rust field, let's fill it with Default::default()"; currently, it would cause a parse error in SerializeValue.
  5. Doctests are added (analogous to those for Deserialize{Value,Row}) for sanity verification of provided attributes.
  6. Tests are added for Ser/De macros integration. Those make sure that those macros don't conflict and their attributes' syntax and behaviour are consistent.

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 Nov 11, 2024
Copy link

github-actions bot commented Nov 11, 2024

cargo semver-checks found no API-breaking changes in this PR! 🎉🥳
Checked commit: 0c9a8cc

@wprzytula wprzytula added this to the 0.15.0 milestone Nov 12, 2024
@wprzytula wprzytula added area/proc-macros Related to procedural macros area/deserialization labels Nov 12, 2024
@wprzytula wprzytula force-pushed the unify-ser-deser-macro-attrs branch 2 times, most recently from a504b58 to 3711afc Compare November 13, 2024 09:32
@wprzytula wprzytula marked this pull request as ready for review November 13, 2024 09:41
@wprzytula wprzytula force-pushed the unify-ser-deser-macro-attrs branch from b6a7b6d to d9e0c6b Compare November 13, 2024 15:46
@wprzytula
Copy link
Collaborator Author

Rebased on main after #1117 was merged.

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 stopped review at: macros: SerializeValue supports #[allow_missing]. Will continue later.

Also a small request: could you please add missing comments for fields of Attributes and FieldAttributes? I remembered what each attribute is responsible for when I reviewed your PR for deserialization macros. I managed to forget it since then, thus such comments would be helpful during review.

scylla-macros/src/serialize/value.rs Outdated Show resolved Hide resolved
This unifies the name of the attribute across SerializeValue and
DeserializeValue. `forbid_excess_udt_fields` is arguably a better name
for what it does than `force_exact_match`.
@wprzytula wprzytula force-pushed the unify-ser-deser-macro-attrs branch from 607cc78 to 7d4d91d Compare November 13, 2024 20:41
@wprzytula
Copy link
Collaborator Author

Rebased on main.

wprzytula and others added 11 commits November 13, 2024 21:46
Instead of taking `enforce_order` as boolean flag, DeserializeValue now
takes a string (e.g., `flavor = "enforce_order"`) and converts it to
a variant of the Flavor enum. This way DeserializeValue is unified with
SerializeValue wrt flavor selection.
Instead of taking `enforce_order` as boolean flag, DeserializeRow now
takes a string (e.g., `flavor = "enforce_order"`) and converts it to
a variant of the Flavor enum. This way DeserializeRow is unified with
SerializeRow wrt flavor selection.
The `#[allow_missing]` attibute is intented to allow a flexible
transition period when schema is altered. Namely, if a UDT is extended
with a new field and the transition begins with migrating clients to the
new schema (extending the Rust struct with a new field), then server may
still have the old schema and provide metadata that is missing the new
field. In such case, `#[allow_missing]` can be attached to the new Rust
struct's field and this way handle the situation:
- in deserialization, the field missing from DB metadata will be
  default-initialized,
- in serialization, such a field will be ignored and not serialized.

This commit adds support for this attribute: for parsing it using
`darling`, and for its semantics in both `match_by_name` and
`enforce_order` flavors.

Also, corresponding (doc)tests for attribute sanity verification are
added, fully based on those from DeserializeValue.
The `#[default_when_null]` attibute is intented to allow a flexible
transition period when schema is altered. Namely, if a UDT is extended
with a new field, then server will populate the new column with NULLs.
If the data model does not abstractly permit NULLs in that column, one
may not want to represent the field with `Option<T>`, not to have to
handle the case of `None`. In such case, `#[default_when_null]` can be
attached to the new Rust struct's field and this way handle the
situation:
- in deserialization, the NULL field received from the DB will be
     default-initialized,
- in serialization, there is no corresponding situation, so this
  attribute does nothing.

This commit adds support for this attribute: for parsing it using
`darling`, and that's it - because it's a no-op.

Also, a corresponding (doc)test is added to confirm that the attibute is
parsed correctly.
As those macros are complex and feature many switches and flavors,
tests are added that verify that when a UDT struct is derived both
SerializeValue and DeserializeValue:
- attributes are parsed correctly and trait impls are generated without
  errors,
- after serialization and deserialization, the result is what is
  expected.
Even though these macros are less complex and feature fewer switches and
flavors than their {Ser,De}Value counterparts, tests are added that
verify that when a row struct is derived both SerializeRow and
DeserializeRow:
- attributes are parsed correctly and trait impls are generated without
  errors,
- after serialization and deserialization, the result is what is
  expected.
Also, no longer mention the limitations of old derive macros.
The new macros have no limitations compared to Serialize macros.
Some comments were missing, so I imported them from DeserializeValue.
@wprzytula wprzytula force-pushed the unify-ser-deser-macro-attrs branch from 7d4d91d to 0c9a8cc Compare November 13, 2024 20:56
@wprzytula
Copy link
Collaborator Author

wprzytula commented Nov 13, 2024

Addressed @muzarski comments:

  • fixed the bug in #[allow_missing] validation,
  • fixed the bug in validation doctests,
  • added more comments.

scylla-macros/src/serialize/value.rs Outdated Show resolved Hide resolved
scylla-cql/src/types/deserialize/mod.rs Show resolved Hide resolved
@wprzytula wprzytula merged commit 98fc02a into scylladb:main Nov 14, 2024
12 checks passed
@wprzytula wprzytula deleted the unify-ser-deser-macro-attrs branch November 14, 2024 13:56
@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 area/proc-macros Related to procedural macros
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Switch deserialization to an iterator-based interface to avoid allocations
3 participants