Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions edgraph/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1804,6 +1804,9 @@ func addQueryIfUnique(qctx context.Context, qc *queryContext) error {
// in the mutation, then we reject the mutation.

if !strings.HasPrefix(pred.ObjectId, "val(") {
if pred.ObjectValue == nil {
continue
}
val := strconv.Quote(fmt.Sprintf("%v", dql.TypeValFrom(pred.ObjectValue).Value))
query := fmt.Sprintf(`%v as var(func: eq(%v,"%v"))`, queryVar, predicateName, val[1:len(val)-1])
if _, err := buildQuery.WriteString(query); err != nil {
Expand Down Expand Up @@ -2218,6 +2221,9 @@ func verifyUniqueWithinMutation(qc *queryContext) error {
continue
}
pred1 := qc.gmuList[gmuIndex].Set[rdfIndex]
if pred1.ObjectValue == nil {
continue
}
pred1Value := dql.TypeValFrom(pred1.ObjectValue).Value
for j := range qc.uniqueVars {
if i == j {
Expand All @@ -2229,6 +2235,9 @@ func verifyUniqueWithinMutation(qc *queryContext) error {
continue
}
pred2 := qc.gmuList[gmuIndex2].Set[rdfIndex2]
if pred2.ObjectValue == nil {
continue
}
if pred2.Predicate == pred1.Predicate && dql.TypeValFrom(pred2.ObjectValue).Value == pred1Value &&
pred2.Subject != pred1.Subject {
return errors.Errorf("could not insert duplicate value [%v] for predicate [%v]",
Expand Down
90 changes: 90 additions & 0 deletions edgraph/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,4 +285,94 @@ func TestVerifyUniqueWithinMutationBoundsChecks(t *testing.T) {
err := verifyUniqueWithinMutation(qc)
require.NoError(t, err)
})

// Regression test for https://github.com/dgraph-io/dgraph/issues/9670
// When an NQuad has a nil ObjectValue (e.g. a UID edge on a predicate
// marked @unique), verifyUniqueWithinMutation would panic with a nil
// pointer dereference in dql.TypeValFrom.
t.Run("nil ObjectValue should not panic", func(t *testing.T) {
qc := &queryContext{
gmuList: []*dql.Mutation{
{
Set: []*api.NQuad{
{
Subject: "_:a",
Predicate: "friend",
ObjectId: "0x1",
ObjectValue: nil, // UID edge, no value
},
},
},
},
uniqueVars: map[uint64]uniquePredMeta{
encodeIndex(0, 0): {},
},
}
// Before fix: panics with "nil pointer dereference" in dql.TypeValFrom
require.NotPanics(t, func() {
_ = verifyUniqueWithinMutation(qc)
})
})

// Same issue but with two NQuads where one has a nil ObjectValue
// and the other has a real value — the inner loop comparison must
// also handle the nil case.
t.Run("nil ObjectValue mixed with value NQuad should not panic", func(t *testing.T) {
qc := &queryContext{
gmuList: []*dql.Mutation{
{
Set: []*api.NQuad{
{
Subject: "_:a",
Predicate: "email",
ObjectValue: &api.Value{
Val: &api.Value_StrVal{StrVal: "[email protected]"},
},
},
{
Subject: "_:a",
Predicate: "friend",
ObjectId: "0x2",
ObjectValue: nil, // UID edge
},
},
},
},
uniqueVars: map[uint64]uniquePredMeta{
encodeIndex(0, 0): {},
encodeIndex(0, 1): {},
},
}
require.NotPanics(t, func() {
err := verifyUniqueWithinMutation(qc)
require.NoError(t, err)
})
})

// Verify that val(...) reference edges (ObjectId="val(x)", ObjectValue=nil)
// don't panic. These go through a different code path in addQueryIfUnique
// but could still appear in uniqueVars and be iterated by
// verifyUniqueWithinMutation.
t.Run("val() reference with nil ObjectValue should not panic", func(t *testing.T) {
qc := &queryContext{
gmuList: []*dql.Mutation{
{
Set: []*api.NQuad{
{
Subject: "_:a",
Predicate: "email",
ObjectId: "val(queryVar)",
ObjectValue: nil,
},
},
},
},
uniqueVars: map[uint64]uniquePredMeta{
encodeIndex(0, 0): {},
},
}
require.NotPanics(t, func() {
_ = verifyUniqueWithinMutation(qc)
})
})
}
Loading