globalsign / mgo

The MongoDB driver for Go
Other
1.97k stars 232 forks source link

Nil slices and empty slices are considered the same #366

Open Elojah opened 5 years ago

Elojah commented 5 years ago

Probably related to: https://github.com/globalsign/mgo/pull/147

I have a struct with:

type Foo struct {
  Bar []string
}

I need to differentiate bar == nil or bar == []string{}.

When insert + findOne with a nil bar:

                                expected: []string(nil)
                                actual  : []string{}

When adding omitempty tag previous test will be okay but test on insert + findOne with a []string{} bar:

                                expected: []string{}
                                actual  : []string(nil)

I have to add a layer above to handle those cases properly, it would be more convenient (and not breaking ?) to handle this directly in the driver.


What version of MongoDB are you using (mongod --version)?

db version v3.6.3
git version: 9586e557d54ef70f9ca4b43c26892cd55257e1a5
OpenSSL version: OpenSSL 1.1.1  11 Sep 2018
allocator: tcmalloc
modules: none
build environment:
    distarch: x86_64
    target_arch: x86_64

What version of Go are you using (go version)?

go version go1.12.2 linux/amd64

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN="/home/foo/go/bin"
GOCACHE="/home/foo/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/foo/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build942599151=/tmp/go-build -gno-record-gcc-switches"

What did you do?

db.Insert + db.Find on an embed slice.

Can you reproduce the issue on the latest development branch?

yes

KidLinus commented 4 years ago

Any update on this? This creates wierd behaviour and requires us to write cutsom marshal code for many structs.

carb commented 3 years ago

Possibly related, and a quick way to see the error:

package main

import (
    "testing"

    "github.com/globalsign/mgo/bson"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

type TestStruct struct {
    Inner []bool
}

func TestUnmarshal(t *testing.T) {
    a := TestStruct{}
    aBSON, err := bson.Marshal(a)
    require.NoError(t, err)

    b := TestStruct{}
    require.NoError(t, bson.Unmarshal(aBSON, &b))

    assert.Equal(t, a, b)
}

Will give the following test failure:

=== RUN   TestUnmarshal
    prog.go:23: 
            Error Trace:    prog.go:23
            Error:          Not equal: 
                            expected: main.TestStruct{Inner:[]bool(nil)}
                            actual  : main.TestStruct{Inner:[]bool{}}

                            Diff:
                            --- Expected
                            +++ Actual
                            @@ -1,3 +1,4 @@
                             (main.TestStruct) {
                            - Inner: ([]bool) <nil>
                            + Inner: ([]bool) {
                            + }
                             }
            Test:           TestUnmarshal
--- FAIL: TestUnmarshal (0.00s)
FAIL
carb commented 3 years ago

Oh, this isn't even an accident. This is on purpose.

bson.SetRespectNilValues(true) will fix this behavior.

carb commented 3 years ago

Workaround:

Wrap the bson package in your own local bson package and add this chunk top-level. Alternatively, add it wherever you use the bson library.

func init() {
       bson.SetRespectNilValues(true)
}