[ 
https://issues.apache.org/jira/browse/THRIFT-6204?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Jens Geyer resolved THRIFT-6204.
--------------------------------
    Fix Version/s: 0.26.0
         Assignee: Sylwester Lachiewicz
       Resolution: Fixed

> Go writes an unset default-requiredness struct field instead of omitting it
> ---------------------------------------------------------------------------
>
>                 Key: THRIFT-6204
>                 URL: https://issues.apache.org/jira/browse/THRIFT-6204
>             Project: Thrift
>          Issue Type: Bug
>          Components: Go - Compiler
>            Reporter: Sylwester Lachiewicz
>            Assignee: Sylwester Lachiewicz
>            Priority: Major
>             Fix For: 0.26.0
>
>          Time Spent: 0.5h
>  Remaining Estimate: 0h
>
> For a struct field with default requiredness whose type is a struct, union or 
> exception, the Go generator emits an unconditional write. When such a field 
> is nil at runtime, neither outcome matches the IDL specification:
> * if the field's struct has at least one member, the write path dereferences 
> the nil pointer and panics;
> * if it has no members, the field goes onto the wire as an empty struct.
> [doc/specs/idl.md|https://github.com/apache/thrift/blob/master/doc/specs/idl.md]
>  gives default requiredness "write if set" semantics, which Python, Java, 
> Node.js and C# implement by omitting the field.
> h3. Why this is not a drop-in fix
> Guarding the write with the generated {{IsSet}} helper is a one-line change 
> in 
> [t_go_generator.cc|https://github.com/apache/thrift/blob/master/compiler/cpp/src/thrift/generate/t_go_generator.cc],
>  but it changes the bytes on the wire for the second case above, where 
> nothing crashes today.
> Holder with a nil zero-member struct field 1 and {{i32 n = 7}} in field 2, 
> binary protocol:
> {noformat}
> master:  0c 00 01 00 08 00 02 00 00 00 07 00    field 1 present, as an empty 
> struct
> guarded:       08 00 02 00 00 00 07 00          field 1 absent
> {noformat}
> A peer that declares field 1 as {{required}} accepts the first and rejects 
> the second:
> {noformat}
> master bytes  -> err=<nil>
> guarded bytes -> err=Required field E is not set
> {noformat}
> h3. Blast radius
> Regenerating every IDL under {{test/}}, {{lib/go/test/}} and {{tutorial/}} 
> with and without the guard:
> || Measure || Count ||
> | generated files changed | 75 of 650 |
> | write sites newly guarded | 227 |
> | changed files containing service {{Args}}/{{Result}} structs | 59 |
> The last row is the one that matters: RPC argument and result encoding is in 
> scope, not just user-declared structs.
> h3. Scope note
> {{is_pointer_field()}} returns true for every struct, union and exception 
> field regardless of requiredness, and for every field carrying a {{cpp.ref}} 
> annotation. A guard keyed on it therefore reaches considerably further than 
> the union case that first surfaced this.
> Because working code changes shape, this looks like a change for a major 
> version, or one behind a {{go:}} generator option, rather than something to 
> fold into a bug fix. The nil-union half, which only turns a panic into an 
> error and touches no wire bytes, is split off as THRIFT-5806.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to