Skip to content

Commit

Permalink
add omitempty options
Browse files Browse the repository at this point in the history
This change adds a `gogoproto.omitempty` option that can be used (in
conjunction with `(gogoproto.nullable) = false`) for non-nullable
message fields.

When a message field is not nullable, its tag is always encoded - even
if all message fields are unset. The `omitempty` option changes the
marshalling code to check an `Empty()` method on that message, which
must be defined by the user. The result is that when the message is
empty, the encoding matches the one we would get if the field was
nullable and the pointer is unset.
  • Loading branch information
RaduBerinde committed Jul 22, 2024
1 parent 063c154 commit 6f86aea
Show file tree
Hide file tree
Showing 61 changed files with 17,830 additions and 11,416 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ regenerate:
make -C test/issue620 regenerate
make -C test/protobuffer regenerate
make -C test/issue630 regenerate
make -C test/omitempty regenerate

make gofmt

Expand Down
179 changes: 95 additions & 84 deletions gogoproto/gogo.pb.go

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions gogoproto/gogo.proto
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,11 @@ extend google.protobuf.FieldOptions {
optional bool stdduration = 65011;
optional bool wktpointer = 65012;


// omitempty is used to skip marshaling non-nullable fields which are empty
// (unset). The field's type must implement an `.Empty()` method which will be
// consulted prior to marshaling.
//
// Added by cockroach.
optional bool omitempty = 65013;
}
4 changes: 4 additions & 0 deletions gogoproto/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ func IsNullable(field *google_protobuf.FieldDescriptorProto) bool {
return proto.GetBoolExtension(field.Options, E_Nullable, true)
}

func IsOmitEmpty(field *google_protobuf.FieldDescriptorProto) bool {
return proto.GetBoolExtension(field.Options, E_Omitempty, false)
}

func IsStdTime(field *google_protobuf.FieldDescriptorProto) bool {
return proto.GetBoolExtension(field.Options, E_Stdtime, false)
}
Expand Down
14 changes: 12 additions & 2 deletions plugin/marshalto/marshalto.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ func (p *marshalto) generateField(proto3 bool, numGen NumGen, file *generator.Fi

protoSizer := gogoproto.IsProtoSizer(file.FileDescriptorProto, message.DescriptorProto)
doNilCheck := gogoproto.NeedsNilCheck(proto3, field)
omitEmpty := gogoproto.IsOmitEmpty(field)
if required && nullable {
p.P(`if m.`, fieldname, `== nil {`)
p.In()
Expand All @@ -354,6 +355,10 @@ func (p *marshalto) generateField(proto3 bool, numGen NumGen, file *generator.Fi
} else if doNilCheck {
p.P(`if m.`, fieldname, ` != nil {`)
p.In()
} else if omitEmpty {
p.P(`// Field has gogoproto.omitempty set.`)
p.P(`if !m.`, fieldname, `.Empty() {`)
p.In()
}
packed := field.IsPacked() || (proto3 && field.IsPacked3())
wireType := field.WireType()
Expand Down Expand Up @@ -689,6 +694,7 @@ func (p *marshalto) generateField(proto3 bool, numGen NumGen, file *generator.Fi
nullableMsg := nullable && (m.ValueField.GetType() == descriptor.FieldDescriptorProto_TYPE_MESSAGE ||
gogoproto.IsCustomType(field) && m.ValueField.IsBytes())
plainBytes := m.ValueField.IsBytes() && !gogoproto.IsCustomType(field)
omitEmpty := gogoproto.IsOmitEmpty(field)
if nullableMsg {
p.P(`if `, accessor, ` != nil { `)
p.In()
Expand All @@ -699,10 +705,14 @@ func (p *marshalto) generateField(proto3 bool, numGen NumGen, file *generator.Fi
p.P(`if `, accessor, ` != nil {`)
}
p.In()
} else if omitEmpty {
p.P(`// Field has gogoproto.omitempty set.`)
p.P(`if !`, accessor, `.Empty() {`)
p.In()
}
p.mapField(numGen, field, m.ValueAliasField, accessor, protoSizer)
p.encodeKey(2, wireToType(valuewire))
if nullableMsg || plainBytes {
if nullableMsg || plainBytes || omitEmpty {
p.Out()
p.P(`}`)
}
Expand Down Expand Up @@ -877,7 +887,7 @@ func (p *marshalto) generateField(proto3 bool, numGen NumGen, file *generator.Fi
default:
panic("not implemented")
}
if (required && nullable) || repeated || doNilCheck {
if (required && nullable) || repeated || doNilCheck || omitEmpty {
p.Out()
p.P(`}`)
}
Expand Down
5 changes: 5 additions & 0 deletions protoc-gen-gogo/generator/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@ func (g *Generator) GetMapValueField(field, valField *descriptor.FieldDescriptor
if err := proto.SetExtension(valField.Options, gogoproto.E_Nullable, &nullable); err != nil {
g.Fail(err.Error())
}

omitEmpty := gogoproto.IsOmitEmpty(field)
if err := proto.SetExtension(valField.Options, gogoproto.E_Omitempty, &omitEmpty); err != nil {
g.Fail(err.Error())
}
return valField
}

Expand Down
551 changes: 276 additions & 275 deletions test/casttype/combos/both/casttype.pb.go

Large diffs are not rendered by default.

551 changes: 276 additions & 275 deletions test/casttype/combos/marshaler/casttype.pb.go

Large diffs are not rendered by default.

541 changes: 271 additions & 270 deletions test/casttype/combos/neither/casttype.pb.go

Large diffs are not rendered by default.

533 changes: 267 additions & 266 deletions test/casttype/combos/unmarshaler/casttype.pb.go

Large diffs are not rendered by default.

505 changes: 253 additions & 252 deletions test/castvalue/castvalue.pb.go

Large diffs are not rendered by default.

509 changes: 255 additions & 254 deletions test/castvalue/combos/both/castvalue.pb.go

Large diffs are not rendered by default.

499 changes: 250 additions & 249 deletions test/castvalue/combos/marshaler/castvalue.pb.go

Large diffs are not rendered by default.

499 changes: 250 additions & 249 deletions test/castvalue/combos/unmarshaler/castvalue.pb.go

Large diffs are not rendered by default.

849 changes: 425 additions & 424 deletions test/combos/both/thetest.pb.go

Large diffs are not rendered by default.

849 changes: 425 additions & 424 deletions test/combos/marshaler/thetest.pb.go

Large diffs are not rendered by default.

839 changes: 420 additions & 419 deletions test/combos/unmarshaler/thetest.pb.go

Large diffs are not rendered by default.

517 changes: 259 additions & 258 deletions test/example/example.pb.go

Large diffs are not rendered by default.

483 changes: 242 additions & 241 deletions test/filedotname/file.dot.pb.go

Large diffs are not rendered by default.

487 changes: 244 additions & 243 deletions test/group/group.pb.go

Large diffs are not rendered by default.

495 changes: 248 additions & 247 deletions test/mapdefaults/combos/both/map.pb.go

Large diffs are not rendered by default.

505 changes: 253 additions & 252 deletions test/mapdefaults/combos/marshaler/map.pb.go

Large diffs are not rendered by default.

505 changes: 253 additions & 252 deletions test/mapdefaults/combos/neither/map.pb.go

Large diffs are not rendered by default.

505 changes: 253 additions & 252 deletions test/mapdefaults/combos/unmarshaler/map.pb.go

Large diffs are not rendered by default.

607 changes: 304 additions & 303 deletions test/mapsproto2/combos/both/mapsproto2.pb.go

Large diffs are not rendered by default.

605 changes: 303 additions & 302 deletions test/mapsproto2/combos/marshaler/mapsproto2.pb.go

Large diffs are not rendered by default.

608 changes: 305 additions & 303 deletions test/mapsproto2/combos/neither/mapsproto2.pb.go

Large diffs are not rendered by default.

591 changes: 296 additions & 295 deletions test/mapsproto2/combos/unmarshaler/mapsproto2.pb.go

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions test/omitempty/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Protocol Buffers for Go with Gadgets
#
# Copyright (c) 2015, The GoGo Authors. All rights reserved.
# http://github.com/gogo/protobuf
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

regenerate:
go install github.com/gogo/protobuf/protoc-gen-combo
go install github.com/gogo/protobuf/protoc-gen-gogo
protoc-gen-combo --gogo_out=. --version="3.0.0" --proto_path=../../protobuf/:../../../../../:. omitempty.proto
5 changes: 5 additions & 0 deletions test/omitempty/combos/both/mymethod.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package omitempty

func (o *OmitEmpty_Inner) Empty() bool {
return o.Foo == 0
}
Loading

0 comments on commit 6f86aea

Please sign in to comment.