Skip to content

Commit 71c992b

Browse files
authored
Merge pull request #145 from codesoap/ignoreUnknownFields
Add ignore-unknown-fields flag
2 parents 6f2963f + ae37910 commit 71c992b

File tree

15 files changed

+541
-34
lines changed

15 files changed

+541
-34
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ gen-testproto: get-grpc-testproto gen-wkt-testproto install
5656
--go_out=. --plugin protoc-gen-go="${GOBIN}/protoc-gen-go" \
5757
--go-vtproto_out=allow-empty=true:. --plugin protoc-gen-go-vtproto="${GOBIN}/protoc-gen-go-vtproto" \
5858
-I$(PROTOBUF_ROOT)/src \
59+
testproto/ignore_unknown_fields/opt.proto \
5960
testproto/empty/empty.proto \
6061
testproto/pool/pool.proto \
6162
testproto/pool/pool_with_slice_reuse.proto \
@@ -106,4 +107,5 @@ genall: gen-include gen-conformance gen-testproto gen-wkt
106107
test: install gen-conformance
107108
go test -short ./...
108109
go test -count=1 ./conformance/...
110+
go test -count=1 ./testproto/ignore_unknown_fields/...
109111
GOGC="off" go test -count=1 ./testproto/pool/...

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ The following features can be generated:
3737

3838
- `unmarshal`: generates a `func (p *YourProto) UnmarshalVT(data []byte)` that behaves similarly to calling `proto.Unmarshal(data, p)` on the message, except the unmarshalling is performed by unrolled codegen without using reflection and allocating as little memory as possible. If the receiver `p` is **not** fully zeroed-out, the unmarshal call will actually behave like `proto.Merge(data, p)`. This is because the `proto.Unmarshal` in the ProtoBuf API is implemented by resetting the destination message and then calling `proto.Merge` on it. To ensure proper `Unmarshal` semantics, ensure you've called `proto.Reset` on your message before calling `UnmarshalVT`, or that your message has been newly allocated.
3939

40+
- The `ignoreUnknownFields` option can be used to ignore unknown fields in protobuf messages and further reduce memory allocations.
41+
4042
- `unmarshal_unsafe` generates a `func (p *YourProto) UnmarshalVTUnsafe(data []byte)` that behaves like `UnmarshalVT`, except it unsafely casts slices of data to `bytes` and `string` fields instead of copying them to newly allocated arrays, so that it performs less allocations. **Data received from the wire has to be left untouched for the lifetime of the message.** Otherwise, the message's `bytes` and `string` fields can be corrupted.
4143

4244
- `pool`: generates the following helper methods
@@ -122,7 +124,14 @@ message Label {
122124
--go-vtproto_opt=pool=vitess.io/vitess/go/vt/proto/query.Row \
123125
--go-vtproto_opt=pool=vitess.io/vitess/go/vt/proto/binlogdata.VStreamRowsResponse \
124126
```
125-
6. (Optional) if you want to selectively compile the generate `vtprotobuf` files, the `--vtproto_opt=buildTag=<tag>` can be used.
127+
128+
6. (Optional) If you are handling messages containing unknown fields and don't intend to forward these messages to a tool that might expect these fields, you can ignore them using the `ignoreUnknownFields` option.
129+
130+
- You can tag messages explicitly in the `.proto` files with `option (vtproto.ignore_unknown_fields)`. Take a look at the example using `option (vtproto.mempool)` above.
131+
132+
- Alternatively, you can enumerate the objects with `--go-vtproto_opt=ignoreUnknownFields=<import>.<message>` flags passed via the CLI. Take a look at the example using `--go-vtproto_opt=pool=...` above.
133+
134+
7. (Optional) if you want to selectively compile the generate `vtprotobuf` files, the `--vtproto_opt=buildTag=<tag>` can be used.
126135
127136
When using this option, the generated code will only be compiled in if a build tag is provided.
128137
@@ -133,9 +142,9 @@ message Label {
133142
This can be done with type assertions before using `vtprotobuf` generated methods.
134143
The `grpc.Codec{}` object (discussed below) shows an example.
135144
136-
7. Compile the `.proto` files in your project. You should see `_vtproto.pb.go` files next to the `.pb.go` and `_grpc.pb.go` files that were already being generated.
145+
8. Compile the `.proto` files in your project. You should see `_vtproto.pb.go` files next to the `.pb.go` and `_grpc.pb.go` files that were already being generated.
137146
138-
8. (Optional) Switch your RPC framework to use the optimized helpers (see following sections)
147+
9. (Optional) Switch your RPC framework to use the optimized helpers (see following sections)
139148
140149
## `vtprotobuf` package and well-known types
141150

cmd/protoc-gen-go-vtproto/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ func main() {
2424
f.BoolVar(&cfg.AllowEmpty, "allow-empty", false, "allow generation of empty files")
2525
cfg.Poolable = generator.NewObjectSet()
2626
cfg.PoolableExclude = generator.NewObjectSet()
27+
cfg.IgnoreUnknownFields = generator.NewObjectSet()
2728
f.Var(&cfg.Poolable, "pool", "use memory pooling for this object")
2829
f.Var(&cfg.PoolableExclude, "pool-exclude", "do not use memory pooling for this object")
30+
f.Var(&cfg.IgnoreUnknownFields, "ignoreUnknownFields", "ignore unknown fields instead of saving them")
2931
f.BoolVar(&cfg.Wrap, "wrap", false, "generate wrapper types")
3032
f.StringVar(&features, "features", "all", "list of features to generate (separated by '+')")
3133
f.StringVar(&cfg.BuildTag, "buildTag", "", "the go:build tag to set on generated files")

features/clone/clone.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func (p *clone) cloneField(lhsBase, rhsBase string, allFieldsNullable bool, fiel
154154
func (p *clone) generateCloneMethodsForMessage(proto3 bool, message *protogen.Message) {
155155
ccTypeName := message.GoIdent.GoName
156156
p.P(`func (m *`, ccTypeName, `) `, cloneName, `() *`, ccTypeName, ` {`)
157-
p.body(!proto3, ccTypeName, message, true)
157+
p.body(!proto3, ccTypeName, message)
158158
p.P(`}`)
159159
p.P()
160160

@@ -169,7 +169,7 @@ func (p *clone) generateCloneMethodsForMessage(proto3 bool, message *protogen.Me
169169
// body generates the code for the actual cloning logic of a structure containing the given fields.
170170
// In practice, those can be the fields of a message.
171171
// The object to be cloned is assumed to be called "m".
172-
func (p *clone) body(allFieldsNullable bool, ccTypeName string, message *protogen.Message, cloneUnknownFields bool) {
172+
func (p *clone) body(allFieldsNullable bool, ccTypeName string, message *protogen.Message) {
173173
// The method body for a message or a oneof wrapper always starts with a nil check.
174174
p.P(`if m == nil {`)
175175
// We use an explicitly typed nil to avoid returning the nil interface in the oneof wrapper
@@ -220,7 +220,7 @@ func (p *clone) body(allFieldsNullable bool, ccTypeName string, message *protoge
220220
p.cloneField("r", "m", allFieldsNullable, field)
221221
}
222222

223-
if cloneUnknownFields && !p.Wrapper() {
223+
if !p.Wrapper() && !p.ShouldIgnoreUnknownFields(message) {
224224
// Clone unknown fields, if any
225225
p.P(`if len(m.unknownFields) > 0 {`)
226226
p.P(`r.unknownFields = make([]byte, len(m.unknownFields))`)

features/equal/equal.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func (p *equal) message(proto3 bool, message *protogen.Message) {
113113
}
114114
}
115115

116-
if p.Wrapper() {
116+
if p.Wrapper() || p.ShouldIgnoreUnknownFields(message) {
117117
p.P(`return true`)
118118
} else {
119119
p.P(`return string(this.unknownFields) == string(that.unknownFields)`)

features/marshal/marshalto.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ func (p *marshal) message(message *protogen.Message) {
599599
p.P(`var l int`)
600600
p.P(`_ = l`)
601601

602-
if !p.Wrapper() {
602+
if !p.Wrapper() && !p.ShouldIgnoreUnknownFields(message) {
603603
p.P(`if m.unknownFields != nil {`)
604604
p.P(`i -= len(m.unknownFields)`)
605605
p.P(`copy(dAtA[i:], m.unknownFields)`)

features/size/size.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ func (p *size) message(message *protogen.Message) {
320320
}
321321
}
322322
}
323-
if !p.Wrapper() {
323+
if !p.Wrapper() && !p.ShouldIgnoreUnknownFields(message) {
324324
p.P(`n+=len(m.unknownFields)`)
325325
}
326326
p.P(`return n`)

features/unmarshal/unmarshal.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ func (p *unmarshal) message(proto3 bool, message *protogen.Message) {
856856
p.P(`iNdEx += skippy`)
857857
p.P(`} else {`)
858858
}
859-
if !p.Wrapper() {
859+
if !p.Wrapper() && !p.ShouldIgnoreUnknownFields(message) {
860860
p.P(`m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)`)
861861
}
862862
p.P(`iNdEx += skippy`)

generator/generatedfile.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ func (b *GeneratedFile) ShouldPool(message *protogen.Message) bool {
4141
return false
4242
}
4343

44+
func (b *GeneratedFile) ShouldIgnoreUnknownFields(message *protogen.Message) bool {
45+
if b.Config.IgnoreUnknownFields.Contains(message.GoIdent) {
46+
return true
47+
}
48+
49+
ext := proto.GetExtension(message.Desc.Options(), vtproto.E_IgnoreUnknownFields)
50+
ignoreUnknownFields, ok := ext.(bool)
51+
return ok && ignoreUnknownFields
52+
}
53+
4454
func (b *GeneratedFile) Alloc(vname string, message *protogen.Message, isQualifiedIdent bool) {
4555
ident := message.GoIdent.GoName
4656
if isQualifiedIdent {

generator/generator.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,12 @@ type Config struct {
5656
Poolable ObjectSet
5757
// PoolableExclude rules determines if pool feature disabled for particular message
5858
PoolableExclude ObjectSet
59-
Wrap bool
60-
WellKnownTypes bool
61-
AllowEmpty bool
62-
BuildTag string
59+
// IgnoreUnknownFields contains messages for which unknown fields shall be ignored
60+
IgnoreUnknownFields ObjectSet
61+
Wrap bool
62+
WellKnownTypes bool
63+
AllowEmpty bool
64+
BuildTag string
6365
}
6466

6567
type Generator struct {

include/github.com/planetscale/vtprotobuf/vtproto/ext.proto

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ option go_package = "github.com/planetscale/vtprotobuf/vtproto";
99

1010
extend google.protobuf.MessageOptions {
1111
optional bool mempool = 64101;
12+
optional bool ignore_unknown_fields = 64102;
1213
}
1314

1415
extend google.protobuf.FieldOptions {
@@ -19,4 +20,4 @@ extend google.protobuf.FieldOptions {
1920
// applying them to some of the fields in protobuf
2021
message Opts {
2122
optional bool unique = 1;
22-
}
23+
}

testproto/ignore_unknown_fields/opt.pb.go

Lines changed: 149 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
syntax = "proto3";
2+
option go_package = "testproto/ignore_unknown_fields";
3+
4+
import "github.com/planetscale/vtprotobuf/vtproto/ext.proto";
5+
6+
message IgnoreUnknownFieldsExtension {
7+
option (vtproto.ignore_unknown_fields) = true;
8+
string foo = 1;
9+
}

0 commit comments

Comments
 (0)