Repository navigation
Description
Summary
google-cloud-bigquery-derive (#[derive(FromRow)] and #[derive(FromSql)]) silently accepts ambiguous and invalid #[bigquery(...)] attribute configurations instead of rejecting them with compile-time errors.
Because rejecting input that previously compiled is a breaking change to accepted macro input under the proc-macro compatibility policy (src/bigquery-derive/src/lib.rs), fixing these validation gaps before google-cloud-bigquery 1.0.0 is released (#7424) avoids requiring a major bump of google-cloud-bigquery later.
Problem Details
Per doc/contributor/howto-guide-handwritten-client-api.md:
Ambiguous input (for example, two fields mapping to the same property name after flattening) is an error, never resolved silently.
Currently in src/bigquery-derive/src/lib.rs:
- Duplicate target column /
STRUCTfield names compile silently and exhibit transport-dependent runtime behavior:derive_from_row_implandderive_from_sql_implresolve each named field's column/field name independently without checking for duplicates across the struct.- For example:
(or two fields both marked
#[derive(FromRow)] struct AmbiguousRow { col: Option<String>, #[bigquery(rename = "col")] other: Option<String>, }
#[bigquery(rename = "same")], orr#typecolliding with#[bigquery(rename = "type")]). - At runtime,
FromRowemits tworow.take("col")?calls:- On JSON-backed rows (
RowInner::Jsoninsrc/bigquery/src/query/row.rs), the firsttakereplaces cell"col"withSqlValueInner::Null, soothersilently receivesNone(or fails withConvertError::NotNullfor non-optional types). - On Arrow-backed rows (
RowInner::Arrowinsrc/bigquery/src/query/row.rs),takereads from theRecordBatchwithout mutating it, so bothcolandotherreceive"col"'s value.
- On JSON-backed rows (
FromSql(SqlValue::take) has the same discrepancy betweenSqlValueInner::Struct(replaces slot withNull) andSqlValueInner::Arrow(reads without mutating).
- Multiple
#[bigquery(...)]attributes on the same field short-circuit after the firstrename:- In
get_field_name(src/bigquery-derive/src/lib.rs),if let Some(name) = renamed { return Ok(name); }returns inside thefor attr in &field.attrsloop. - A second
#[bigquery(rename = "...")]on the same field is silently ignored instead of reporting a duplicaterenameerror, and a subsequent invalid attribute (e.g.,#[bigquery(rename = "a")] #[bigquery(typo = "b")]) is never parsed at all.
- In
- Duplicate
renameoptions inside a single attribute overwrite silently:#[bigquery(rename = "a", rename = "b")]overwritesrenamed = Some(lit.value())without checking ifrenamewas already specified, silently using"b".
- Container-level
#[bigquery(...)]attributes on structs are silently ignored:- Neither
derive_from_row_implnorderive_from_sql_implinspectsinput.attrs, so unsupported container attributes such as#[bigquery(rename_all = "camelCase")]on a struct compile without error and have no effect.
- Neither
Expected Behavior
- Reject structs where two or more fields resolve to the same BigQuery column /
STRUCTfield name with a spannedsyn::Error. - Inspect all
#[bigquery(...)]attributes on every field and reject duplicaterenameoptions (whether in one#[bigquery(rename = "a", rename = "b")]list or across multiple#[bigquery(...)]attributes on the same field). - Reject
#[bigquery(...)]attributes placed on the container (struct/enum/union) itself until/unless container options are explicitly supported. - Add unit tests in
src/bigquery-derive/src/lib.rscovering each error case, and take a breaking version bump ongoogle-cloud-bigquery-deriveif needed per the compatibility policy.
Activity