Skip to content

perf(spanner): add ToValue for &[u8] - #6070

Open
fornwall wants to merge 4 commits into
googleapis:mainfrom
fornwall:up-2-tovalue-for-byte-slice
Open

perf(spanner): add ToValue for &[u8]#6070
fornwall wants to merge 4 commits into
googleapis:mainfrom
fornwall:up-2-tovalue-for-byte-slice

Conversation

@fornwall

@fornwall fornwall commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

ToValue was implemented for Vec<u8> but not for byte slices, so a caller holding a &[u8] had to allocate an owned Vec<u8> purely to encode it, adding a copy per binary cell on potentially hot binding paths.

Add impl ToValue for &[u8] holding the BYTES/base64 encoding, and have the Vec<u8> impl delegate to it via self.as_slice().

@fornwall
fornwall requested review from a team as code owners July 16, 2026 12:56
@product-auto-label product-auto-label Bot added the api: spanner Issues related to the Spanner API. label Jul 16, 2026
@fornwall fornwall changed the title feat(spanner): add ToValue for &[u8] perf(spanner): add ToValue for &[u8] Jul 16, 2026
@fornwall
fornwall force-pushed the up-2-tovalue-for-byte-slice branch from f651f16 to 47d507f Compare July 16, 2026 12:56

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements the ToValue trait for byte slices (&[u8]) in the Spanner crate, enabling them to be encoded as base64 strings. It also adds a corresponding unit test to verify the correctness of this implementation. There are no review comments, and I have no feedback to provide.

ToValue was implemented for Vec<u8> but not for byte slices, so a caller
holding a &[u8] had to allocate an owned Vec<u8> (via .to_vec()) purely to
encode it, adding a copy per binary cell on potentially hot binding paths.

Add impl ToValue for &[u8] holding the BYTES/base64 encoding, and have the
Vec<u8> impl delegate to it via self.as_slice(). This keeps a single source
of truth for the encoding (no risk of the two drifting) while both produce
an identical Value.

Signed-off-by: Fredrik Fornwall <fredrik@fornwall.net>
@fornwall
fornwall force-pushed the up-2-tovalue-for-byte-slice branch from 47d507f to f26e3bc Compare July 16, 2026 12:59
Comment thread src/spanner/src/to_value.rs Outdated
@fornwall
fornwall requested a review from olavloite July 17, 2026 11:04
@olavloite

Copy link
Copy Markdown
Contributor

/gcbrun

@olavloite
olavloite requested a review from sakthivelmanii July 27, 2026 12:09
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.58%. Comparing base (b3a930b) to head (6f53535).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6070   +/-   ##
=======================================
  Coverage   96.57%   96.58%           
=======================================
  Files         264      264           
  Lines       66577    66597   +20     
=======================================
+ Hits        64297    64320   +23     
+ Misses       2280     2277    -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

olavloite added a commit to olavloite/google-cloud-rust that referenced this pull request Jul 27, 2026
Changes `StatementBuilder::add_param`, `StatementBuilder::add_typed_param`, and `ValueBinder::to` to accept `T: Into<Value>` instead of `&T: ToValue`.

This allows callers to pass owned values directly (like `String`, `Value`, or `Option<T>`) zero-copy without unnecessary cloning, while maintaining complete backward compatibility for references (`&str`, `&i64`, `&Option<T>`).

### Key Changes
- **Direct `From<T> for Value` Implementations**: Added zero-copy `From` conversions for owned types (`String`, `i64`, `i32`, `bool`, `f64`, `f32`, `Decimal`, `Date`, `OffsetDateTime`, `SystemTime`, `wkt::Timestamp`, `Vec<u8>`, `Option<T>`, `Vec<T>`, `ProtoValue`).
- **Decoupled `ToValue`**: All `ToValue` trait implementations now delegate directly to `From` / `.into()`, decoupling conversion logic from `ToValue` and paving the way for eventual deprecation of the `ToValue` trait.
- **Unsized Slice Support (`[u8]` and `str`)**: Implemented `ToValue` for `[u8]` and `str` (and `&[u8]`, `&str`), enabling zero-copy slice conversions and generic trait bounds `T: ToValue + ?Sized`. This supersedes both PR googleapis#6070 and PR googleapis#6087.
- **Untyped NULL Support**: Added `Value::null()`, `From<()>` and `ToValue for ()` to easily support untyped NULL parameters (`Value::null()`, `None::<()>`, `None::<Value>`).
- **Cleaned up Examples & Tests**: Updated example scripts and tests to pass owned values directly, eliminating needless reference borrows (`clippy::needless_borrows_for_generic_args`).

BREAKING CHANGE: This is a minor breaking change only for code that passes inline `&None` with a method-level turbofish:

```rust
// Old (fails to compile):
builder.set("Col").to::<Option<bool>>(&None);

// Works both before and after this change:
builder.set("Col").to(&None::<bool>);

// New (remove the &):
builder.set("Col").to::<Option<bool>>(None);
// Or:
builder.set("Col").to(None::<bool>);
builder.set("Col").to(Value::null());
```

Code passing existing `Option` variables (like `.to(&my_var)`) is unaffected and continues to compile unchanged.
olavloite added a commit to olavloite/google-cloud-rust that referenced this pull request Jul 27, 2026
Changes `StatementBuilder::add_param`, `StatementBuilder::add_typed_param`, and `ValueBinder::to` to accept `T: Into<Value>` instead of `&T: ToValue`.

This allows callers to pass owned values directly (like `String`, `Value`, or `Option<T>`) zero-copy without unnecessary cloning, while maintaining complete backward compatibility for references (`&str`, `&i64`, `&Option<T>`).

- **Direct `From<T> for Value` Implementations**: Added zero-copy `From` conversions for owned types (`String`, `i64`, `i32`, `bool`, `f64`, `f32`, `Decimal`, `Date`, `OffsetDateTime`, `SystemTime`, `wkt::Timestamp`, `Vec<u8>`, `Option<T>`, `Vec<T>`, `ProtoValue`).
- **Decoupled `ToValue`**: All `ToValue` trait implementations now delegate directly to `From` / `.into()`, decoupling conversion logic from `ToValue` and paving the way for eventual deprecation of the `ToValue` trait.
- **Unsized Slice Support (`[u8]` and `str`)**: Implemented `ToValue` for `[u8]` and `str` (and `&[u8]`, `&str`), enabling zero-copy slice conversions and generic trait bounds `T: ToValue + ?Sized`. This supersedes both PR googleapis#6070 and PR googleapis#6087.
- **Untyped NULL Support**: Added `Value::null()`, `From<()>` and `ToValue for ()` to easily support untyped NULL parameters (`Value::null()`, `None::<()>`, `None::<Value>`).
- **Cleaned up Examples & Tests**: Updated example scripts and tests to pass owned values directly, eliminating needless reference borrows (`clippy::needless_borrows_for_generic_args`).

BREAKING CHANGE: This is a minor breaking change only for code that passes inline `&None` with a method-level turbofish:

```rust
// Old (fails to compile):
builder.set("Col").to::<Option<bool>>(&None);

// Works both before and after this change:
builder.set("Col").to(&None::<bool>);

// New (remove the &):
builder.set("Col").to::<Option<bool>>(None);
// Or:
builder.set("Col").to(None::<bool>);
builder.set("Col").to(Value::null());
```

Code passing existing `Option` variables (like `.to(&my_var)`) is unaffected and continues to compile unchanged.
olavloite added a commit to olavloite/google-cloud-rust that referenced this pull request Jul 27, 2026
Changes `StatementBuilder::add_param`, `StatementBuilder::add_typed_param`, and `ValueBinder::to` to accept `T: Into<Value>` instead of `&T: ToValue`.

This allows callers to pass owned values directly (like `String`, `Value`, or `Option<T>`) zero-copy without unnecessary cloning, while maintaining complete backward compatibility for references (`&str`, `&i64`, `&Option<T>`).

- **Direct `From<T> for Value` Implementations**: Added zero-copy `From` conversions for owned types (`String`, `i64`, `i32`, `bool`, `f64`, `f32`, `Decimal`, `Date`, `OffsetDateTime`, `SystemTime`, `wkt::Timestamp`, `Vec<u8>`, `Option<T>`, `Vec<T>`, `ProtoValue`).
- **Decoupled `ToValue`**: All `ToValue` trait implementations now delegate directly to `From` / `.into()`, decoupling conversion logic from `ToValue` and paving the way for eventual deprecation of the `ToValue` trait.
- **Unsized Slice Support (`[u8]` and `str`)**: Implemented `ToValue` for `[u8]` and `str` (and `&[u8]`, `&str`), enabling zero-copy slice conversions and generic trait bounds `T: ToValue + ?Sized`. This supersedes both PR googleapis#6070 and PR googleapis#6087.
- **Untyped NULL Support**: Added `Value::null()`, `From<()>` and `ToValue for ()` to easily support untyped NULL parameters (`Value::null()`, `None::<()>`, `None::<Value>`).
- **Cleaned up Examples & Tests**: Updated example scripts and tests to pass owned values directly, eliminating needless reference borrows (`clippy::needless_borrows_for_generic_args`).

BREAKING CHANGE: This is a minor breaking change only for code that passes inline `&None` with a method-level turbofish:

```rust
// Old (fails to compile):
builder.set("Col").to::<Option<bool>>(&None);

// Works both before and after this change:
builder.set("Col").to(&None::<bool>);

// New (remove the &):
builder.set("Col").to::<Option<bool>>(None);
// Or:
builder.set("Col").to(None::<bool>);
builder.set("Col").to(Value::null());
```

Code passing existing `Option` variables (like `.to(&my_var)`) is unaffected and continues to compile unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: spanner Issues related to the Spanner API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants