From 494f6ebc6c06d92cb8956d6076e5569ef49f7581 Mon Sep 17 00:00:00 2001 From: mattthew Date: Fri, 6 Jun 2025 20:06:23 -0400 Subject: [PATCH 01/12] Add more context to test package generation --- dgraphtest/image.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgraphtest/image.go b/dgraphtest/image.go index 98df70b4ea0..dcf602395eb 100644 --- a/dgraphtest/image.go +++ b/dgraphtest/image.go @@ -172,7 +172,7 @@ func copyBinary(fromDir, toDir, version string) error { fromPath := filepath.Join(fromDir, binaryName) toPath := filepath.Join(toDir, "dgraph") if err := copy(fromPath, toPath); err != nil { - return errors.Wrap(err, "error while copying binary into tempBinDir") + return errors.Wrapf(err, "error while copying binary into tempBinDir [%v], from [%v]", toPath, fromPath) } return nil } From 75ba9ed0eee46c03db71fefa21cd19438498b44c Mon Sep 17 00:00:00 2001 From: mattthew Date: Fri, 6 Jun 2025 20:07:16 -0400 Subject: [PATCH 02/12] Add test to re-create panic during mutation when conditionally pruned mutations are validated unique --- dgraph/cmd/alpha/run_test.go | 117 +++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/dgraph/cmd/alpha/run_test.go b/dgraph/cmd/alpha/run_test.go index f2b34b16ed4..4d91e4f4575 100644 --- a/dgraph/cmd/alpha/run_test.go +++ b/dgraph/cmd/alpha/run_test.go @@ -1604,6 +1604,123 @@ func TestGeoValidWkbData(t *testing.T) { require.Contains(t, string(resp.Json), `{"type":"Point","coordinates":[1,2]}`) } +func TestPanicWithConditionallyPrunedJsonMutations(t *testing.T) { + schema := ` + email: string @index(term,hash) @upsert @unique . + identifier: string @index(term,hash) @upsert @unique . + location: uid . + school: uid . + username: string @index(term,hash) @upsert @unique . + locationId: string @index(term,hash) @upsert @unique . + + type User { + username + email + school + } + type TestSchool { + identifier + location + } + type Location { + locationId + } + ` + require.NoError(t, alterSchemaWithRetry(schema)) + ctx := context.Background() + require.NotNil(t, dg) + + // --- Request 1: Create initial set of entities --- + t.Logf("Executing Request 1: Initial entity creation") + query1 := `query { + q_loc1_check(func: eq(locationId, "loc_id_pass1")) { v_loc1 as uid } + q_school1_check(func: eq(identifier, "school_id_pass1")) { v_school1 as uid } + q_user1_check_un(func: eq(username, "user_pass1")) { v_user1_un as uid } + q_user1_check_em(func: eq(email, "user_pass1@example.com")) { v_user1_em as uid } + }` + mutations1 := []*api.Mutation{ + { + SetJson: []byte(`{"dgraph.type":["Location"],"locationId":"loc_id_pass1","uid":"_:loc_pass1"}`), + Cond: "@if(eq(len(v_loc1), 0))", // TRUE + }, + { + SetJson: []byte(`{"dgraph.type":["TestSchool"],"identifier":"school_id_pass1", "location":{"uid":"_:loc_pass1"},"uid":"_:school_pass1"}`), + Cond: "@if(eq(len(v_school1), 0))", // TRUE + }, + { + SetJson: []byte(`{"dgraph.type":["User"],"username":"user_pass1","email":"user_pass1@example.com", "school":{"uid":"_:school_pass1"},"uid":"_:user_pass1"}`), + Cond: "@if(eq(len(v_user1_un), 0) AND eq(len(v_user1_em), 0))", // TRUE + }, + } + req1 := &api.Request{Query: query1, Mutations: mutations1, CommitNow: true} + resp1, err1 := dg.NewTxn().Do(ctx, req1) + require.NoError(t, err1, "Request 1 failed") + require.NotNil(t, resp1) + require.NotEmpty(t, resp1.Uids["loc_pass1"], "Request 1: UID for loc_pass1 missing") + require.NotEmpty(t, resp1.Uids["school_pass1"], "Request 1: UID for school_pass1 missing") + require.NotEmpty(t, resp1.Uids["user_pass1"], "Request 1: UID for user_pass1 missing") + t.Logf("Request 1 completed. Initial entities created.") + + // --- Request 2: Loop 1 KEPT SetJson, then 2 PRUNED SetJson mutations to try and force panic --- + maxRetries := 10 + t.Logf("Looping Request 2 up to %d times (1 KEPT SetJson then 2 PRUNED SetJson), hoping for panic...", maxRetries) + + for i := 0; i < maxRetries; i++ { + t.Logf("Executing Request 2, Attempt %d/%d", i+1, maxRetries) + query2 := `query { + # For linking in KEPT SetJson and pruning conditions for other SetJson mutations + q_school1_exists(func: eq(identifier, "school_id_pass1")) { v_school1_exists as uid } + q_user1_exists_un(func: eq(username, "user_pass1")) { v_user1_exists_un as uid } + q_user1_exists_em(func: eq(email, "user_pass1@example.com")) { v_user1_exists_em as uid } + + # For KEPT SetJson condition (new user) + q_user2_new_json_un(func: eq(username, "user_pass2_new_json")) { v_user2_new_json_un as uid } + q_user2_new_json_em(func: eq(email, "user_pass2_new_json@example.com")) { v_user2_new_json_em as uid } + }` + + mutations2 := []*api.Mutation{ + { // Mutation 0: Create NEW User (KEPT SetJson) - links to existing school from Pass 1 + SetJson: []byte(`{"dgraph.type":["User"],"username":"user_pass2_new_json","email":"user_pass2_new_json@example.com", "school":{"uid":"uid(v_school1_exists)"},"uid":"_:user_pass2_new_json"}`), + Cond: "@if(eq(len(v_user2_new_json_un), 0) AND eq(len(v_user2_new_json_em), 0) AND eq(len(v_school1_exists), 1))", // TRUE (new user, school1 must exist) + }, + // --- PRUNED SetJson MUTATIONS --- + { // Mutation 1: Attempt to re-create User from Req1 (PRUNED SetJson) + SetJson: []byte(`{"dgraph.type":["User"],"username":"user_pass1","email":"user_pass1@example.com", "school":{"uid":"uid(v_school1_exists)"},"uid":"_:user_pass1_dup_json"}`), + Cond: "@if(eq(len(v_user1_exists_un), 0) AND eq(len(v_user1_exists_em), 0))", // FALSE (user1 already exists) + }, + { // Mutation 2: Attempt to re-create School from Req1 (PRUNED SetJson) + SetJson: []byte(`{"dgraph.type":["TestSchool"],"identifier":"school_id_pass1", "uid":"_:school_pass1_dup_json"}`), + Cond: "@if(eq(len(v_school1_exists), 0))", // FALSE (school1 already exists) + }, + } + req2 := &api.Request{Query: query2, Mutations: mutations2, CommitNow: true} + + // This is the call expected to trigger the panic in one of the retries. + resp2, err2 := dg.NewTxn().Do(ctx, req2) + + if err2 != nil { + // If an error occurs, assume it's the panic we're trying to reproduce. + // Fail the test immediately. + require.FailNow(t, fmt.Sprintf("Panic likely reproduced on attempt %d/%d of Request 2: %v", i+1, maxRetries, err2)) + } + + // If no error, Request 2 completed for this attempt. Check assertions. + require.NotNil(t, resp2, "Request 2, Attempt %d/%d: Response was nil without error or panic", i+1, maxRetries) + require.NotEmpty(t, resp2.Uids["user_pass2_new_json"], "Request 2, Attempt %d/%d: UID for user_pass2_new_json (from SetJson) should not be empty", i+1, maxRetries) + + createdNodeCountReq2 := 0 + for key, uid := range resp2.Uids { + if uid != "" && key == "user_pass2_new_json" { + createdNodeCountReq2++ + } + } + require.Equal(t, 1, createdNodeCountReq2, "Request 2, Attempt %d/%d: Expected 1 UID from the kept SetJson mutation (no panic occurred)", i+1, maxRetries) + t.Logf("Request 2, Attempt %d/%d completed successfully without panic.", i+1, maxRetries) + } + + t.Logf("Test completed %d attempts of Request 2 (initial create, then 1 KEPT Json / 2 PRUNED Json) without reproducing the panic.", maxRetries) +} + type Token struct { token *dgraphapi.HttpToken sync.RWMutex From 9c5d0439e85bba998f3142ac29b61ecefb19918f Mon Sep 17 00:00:00 2001 From: mattthew Date: Fri, 6 Jun 2025 20:23:18 -0400 Subject: [PATCH 03/12] Update comments --- dgraph/cmd/alpha/run_test.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/dgraph/cmd/alpha/run_test.go b/dgraph/cmd/alpha/run_test.go index 4d91e4f4575..cef65ed55ee 100644 --- a/dgraph/cmd/alpha/run_test.go +++ b/dgraph/cmd/alpha/run_test.go @@ -1604,6 +1604,10 @@ func TestGeoValidWkbData(t *testing.T) { require.Contains(t, string(resp.Json), `{"type":"Point","coordinates":[1,2]}`) } +// This test verifies that conditionally pruned json mutations do not cause a panic. +// This test is based on panics occurring during testing of the dgman package which +// uses complex conditional logic for mutations. For reference: +// https://github.com/dolan-in/dgman/blob/e9a34974ae96ee55beed293971725765f6404d6c/mutate_test.go#L307 func TestPanicWithConditionallyPrunedJsonMutations(t *testing.T) { schema := ` email: string @index(term,hash) @upsert @unique . @@ -1654,12 +1658,12 @@ func TestPanicWithConditionallyPrunedJsonMutations(t *testing.T) { } req1 := &api.Request{Query: query1, Mutations: mutations1, CommitNow: true} resp1, err1 := dg.NewTxn().Do(ctx, req1) - require.NoError(t, err1, "Request 1 failed") + require.NoError(t, err1, "First Request unexpectedly failed") require.NotNil(t, resp1) - require.NotEmpty(t, resp1.Uids["loc_pass1"], "Request 1: UID for loc_pass1 missing") - require.NotEmpty(t, resp1.Uids["school_pass1"], "Request 1: UID for school_pass1 missing") - require.NotEmpty(t, resp1.Uids["user_pass1"], "Request 1: UID for user_pass1 missing") - t.Logf("Request 1 completed. Initial entities created.") + require.NotEmpty(t, resp1.Uids["loc_pass1"], "First Request: UID for loc_pass1 missing") + require.NotEmpty(t, resp1.Uids["school_pass1"], "First Request: UID for school_pass1 missing") + require.NotEmpty(t, resp1.Uids["user_pass1"], "First Request: UID for user_pass1 missing") + t.Logf("First Request completed. Initial entities created.") // --- Request 2: Loop 1 KEPT SetJson, then 2 PRUNED SetJson mutations to try and force panic --- maxRetries := 10 From e9c4c42c4c781556ac81cd8732e847dca3ce3f2e Mon Sep 17 00:00:00 2001 From: mattthew Date: Fri, 20 Jun 2025 16:11:17 -0400 Subject: [PATCH 04/12] Add sanity/bounds checks for previously pruned mutations --- edgraph/server.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/edgraph/server.go b/edgraph/server.go index 11a960e7fa7..61cd0b66bf6 100644 --- a/edgraph/server.go +++ b/edgraph/server.go @@ -2151,18 +2151,26 @@ func verifyUniqueWithinMutation(qc *queryContext) error { for i := range qc.uniqueVars { gmuIndex, rdfIndex := decodeIndex(i) - if len(qc.gmuList[gmuIndex].Set) == 0 { - return nil + // handles cases where the mutation was pruned in updateMutations + if int(gmuIndex) >= len(qc.gmuList) || qc.gmuList[gmuIndex] == nil || int(rdfIndex) >= len(qc.gmuList[gmuIndex].Set) { + continue } pred1 := qc.gmuList[gmuIndex].Set[rdfIndex] pred1Value := dql.TypeValFrom(pred1.ObjectValue).Value for j := range qc.uniqueVars { + if i == j { + continue + } gmuIndex2, rdfIndex2 := decodeIndex(j) + // bounds check for the second predicate, which could also have been pruned + if int(gmuIndex2) >= len(qc.gmuList) || qc.gmuList[gmuIndex2] == nil || int(rdfIndex2) >= len(qc.gmuList[gmuIndex2].Set) { + continue + } pred2 := qc.gmuList[gmuIndex2].Set[rdfIndex2] - if pred2.Predicate == pred1.Predicate && dql.TypeValFrom(pred2.ObjectValue).Value == pred1Value && - pred2.Subject != pred1.Subject { + if pred1.Predicate == pred2.Predicate && dql.TypeValFrom(pred2.ObjectValue).Value == pred1Value && + pred1.Subject != pred2.Subject { return errors.Errorf("could not insert duplicate value [%v] for predicate [%v]", - dql.TypeValFrom(pred1.ObjectValue).Value, pred1.Predicate) + pred1Value, pred1.Predicate) } } } From 06ea4ab5fed134265f92af43e3248bbc2b56110c Mon Sep 17 00:00:00 2001 From: mattthew Date: Fri, 20 Jun 2025 16:12:01 -0400 Subject: [PATCH 05/12] Simplify the test to bare minimum mutations and conditions --- dgraph/cmd/alpha/run_test.go | 141 +++++++++-------------------------- 1 file changed, 34 insertions(+), 107 deletions(-) diff --git a/dgraph/cmd/alpha/run_test.go b/dgraph/cmd/alpha/run_test.go index cef65ed55ee..4c0d7b7e227 100644 --- a/dgraph/cmd/alpha/run_test.go +++ b/dgraph/cmd/alpha/run_test.go @@ -1604,125 +1604,52 @@ func TestGeoValidWkbData(t *testing.T) { require.Contains(t, string(resp.Json), `{"type":"Point","coordinates":[1,2]}`) } -// This test verifies that conditionally pruned json mutations do not cause a panic. -// This test is based on panics occurring during testing of the dgman package which -// uses complex conditional logic for mutations. For reference: -// https://github.com/dolan-in/dgman/blob/e9a34974ae96ee55beed293971725765f6404d6c/mutate_test.go#L307 -func TestPanicWithConditionallyPrunedJsonMutations(t *testing.T) { +// TestWithConditionallyPrunedMutations provides a minimal test case to verify that the server +// does not panic when a mutation with a unique predicate is conditionally pruned. +func TestWithConditionallyPrunedMutations(t *testing.T) { schema := ` - email: string @index(term,hash) @upsert @unique . - identifier: string @index(term,hash) @upsert @unique . - location: uid . - school: uid . - username: string @index(term,hash) @upsert @unique . - locationId: string @index(term,hash) @upsert @unique . - - type User { - username - email - school - } - type TestSchool { - identifier - location - } - type Location { - locationId - } + username: string @index(term,hash) @upsert @unique . + type User { + username + } ` require.NoError(t, alterSchemaWithRetry(schema)) ctx := context.Background() - require.NotNil(t, dg) - - // --- Request 1: Create initial set of entities --- - t.Logf("Executing Request 1: Initial entity creation") - query1 := `query { - q_loc1_check(func: eq(locationId, "loc_id_pass1")) { v_loc1 as uid } - q_school1_check(func: eq(identifier, "school_id_pass1")) { v_school1 as uid } - q_user1_check_un(func: eq(username, "user_pass1")) { v_user1_un as uid } - q_user1_check_em(func: eq(email, "user_pass1@example.com")) { v_user1_em as uid } + + // Create an initial user to act as an anchor for a conditional mutation. + initialMutation := &api.Mutation{ + SetJson: []byte(`{"dgraph.type":["User"],"username":"user-A","uid":"_:user-A"}`), + CommitNow: true, + } + _, err := dg.NewTxn().Mutate(ctx, initialMutation) + require.NoError(t, err, "Setup mutation to create user-A failed") + + // Send a request with one mutation to be pruned and one to be kept. + query := `query { + q(func: eq(username, "user-A")) { v as uid } }` - mutations1 := []*api.Mutation{ - { - SetJson: []byte(`{"dgraph.type":["Location"],"locationId":"loc_id_pass1","uid":"_:loc_pass1"}`), - Cond: "@if(eq(len(v_loc1), 0))", // TRUE + + mutations := []*api.Mutation{ + { // Mutation 0 (KEPT): This mutation should be processed correctly. + SetJson: []byte(`{"dgraph.type":["User"],"username":"user-C","uid":"_:user-C"}`), }, - { - SetJson: []byte(`{"dgraph.type":["TestSchool"],"identifier":"school_id_pass1", "location":{"uid":"_:loc_pass1"},"uid":"_:school_pass1"}`), - Cond: "@if(eq(len(v_school1), 0))", // TRUE - }, - { - SetJson: []byte(`{"dgraph.type":["User"],"username":"user_pass1","email":"user_pass1@example.com", "school":{"uid":"_:school_pass1"},"uid":"_:user_pass1"}`), - Cond: "@if(eq(len(v_user1_un), 0) AND eq(len(v_user1_em), 0))", // TRUE + { // Mutation 1 (PRUNED): This mutation has a unique predicate but will be pruned. + SetJson: []byte(`{"dgraph.type":["User"],"username":"user-B","uid":"_:user-B"}`), + Cond: "@if(eq(len(v), 0))", // This is FALSE, so the mutation is pruned. }, } - req1 := &api.Request{Query: query1, Mutations: mutations1, CommitNow: true} - resp1, err1 := dg.NewTxn().Do(ctx, req1) - require.NoError(t, err1, "First Request unexpectedly failed") - require.NotNil(t, resp1) - require.NotEmpty(t, resp1.Uids["loc_pass1"], "First Request: UID for loc_pass1 missing") - require.NotEmpty(t, resp1.Uids["school_pass1"], "First Request: UID for school_pass1 missing") - require.NotEmpty(t, resp1.Uids["user_pass1"], "First Request: UID for user_pass1 missing") - t.Logf("First Request completed. Initial entities created.") - - // --- Request 2: Loop 1 KEPT SetJson, then 2 PRUNED SetJson mutations to try and force panic --- - maxRetries := 10 - t.Logf("Looping Request 2 up to %d times (1 KEPT SetJson then 2 PRUNED SetJson), hoping for panic...", maxRetries) - - for i := 0; i < maxRetries; i++ { - t.Logf("Executing Request 2, Attempt %d/%d", i+1, maxRetries) - query2 := `query { - # For linking in KEPT SetJson and pruning conditions for other SetJson mutations - q_school1_exists(func: eq(identifier, "school_id_pass1")) { v_school1_exists as uid } - q_user1_exists_un(func: eq(username, "user_pass1")) { v_user1_exists_un as uid } - q_user1_exists_em(func: eq(email, "user_pass1@example.com")) { v_user1_exists_em as uid } - - # For KEPT SetJson condition (new user) - q_user2_new_json_un(func: eq(username, "user_pass2_new_json")) { v_user2_new_json_un as uid } - q_user2_new_json_em(func: eq(email, "user_pass2_new_json@example.com")) { v_user2_new_json_em as uid } - }` - - mutations2 := []*api.Mutation{ - { // Mutation 0: Create NEW User (KEPT SetJson) - links to existing school from Pass 1 - SetJson: []byte(`{"dgraph.type":["User"],"username":"user_pass2_new_json","email":"user_pass2_new_json@example.com", "school":{"uid":"uid(v_school1_exists)"},"uid":"_:user_pass2_new_json"}`), - Cond: "@if(eq(len(v_user2_new_json_un), 0) AND eq(len(v_user2_new_json_em), 0) AND eq(len(v_school1_exists), 1))", // TRUE (new user, school1 must exist) - }, - // --- PRUNED SetJson MUTATIONS --- - { // Mutation 1: Attempt to re-create User from Req1 (PRUNED SetJson) - SetJson: []byte(`{"dgraph.type":["User"],"username":"user_pass1","email":"user_pass1@example.com", "school":{"uid":"uid(v_school1_exists)"},"uid":"_:user_pass1_dup_json"}`), - Cond: "@if(eq(len(v_user1_exists_un), 0) AND eq(len(v_user1_exists_em), 0))", // FALSE (user1 already exists) - }, - { // Mutation 2: Attempt to re-create School from Req1 (PRUNED SetJson) - SetJson: []byte(`{"dgraph.type":["TestSchool"],"identifier":"school_id_pass1", "uid":"_:school_pass1_dup_json"}`), - Cond: "@if(eq(len(v_school1_exists), 0))", // FALSE (school1 already exists) - }, - } - req2 := &api.Request{Query: query2, Mutations: mutations2, CommitNow: true} - - // This is the call expected to trigger the panic in one of the retries. - resp2, err2 := dg.NewTxn().Do(ctx, req2) - if err2 != nil { - // If an error occurs, assume it's the panic we're trying to reproduce. - // Fail the test immediately. - require.FailNow(t, fmt.Sprintf("Panic likely reproduced on attempt %d/%d of Request 2: %v", i+1, maxRetries, err2)) - } + req := &api.Request{Query: query, Mutations: mutations, CommitNow: true} + resp, err := dg.NewTxn().Do(ctx, req) - // If no error, Request 2 completed for this attempt. Check assertions. - require.NotNil(t, resp2, "Request 2, Attempt %d/%d: Response was nil without error or panic", i+1, maxRetries) - require.NotEmpty(t, resp2.Uids["user_pass2_new_json"], "Request 2, Attempt %d/%d: UID for user_pass2_new_json (from SetJson) should not be empty", i+1, maxRetries) + require.NoError(t, err, "The main request failed, but it should have succeeded.") + require.NotNil(t, resp, "Response should not be nil") - createdNodeCountReq2 := 0 - for key, uid := range resp2.Uids { - if uid != "" && key == "user_pass2_new_json" { - createdNodeCountReq2++ - } - } - require.Equal(t, 1, createdNodeCountReq2, "Request 2, Attempt %d/%d: Expected 1 UID from the kept SetJson mutation (no panic occurred)", i+1, maxRetries) - t.Logf("Request 2, Attempt %d/%d completed successfully without panic.", i+1, maxRetries) - } + // The kept mutation for user-C should have created a UID. + require.NotEmpty(t, resp.Uids["user-C"], "UID for kept user-C should not be empty") - t.Logf("Test completed %d attempts of Request 2 (initial create, then 1 KEPT Json / 2 PRUNED Json) without reproducing the panic.", maxRetries) + // The pruned mutation for user-B should not have created a UID. + require.Empty(t, resp.Uids["user-B"], "UID for pruned user-B should be empty") } type Token struct { From 4fec1514bc3bf57e11dece7fd1ed6d5ee3a86e26 Mon Sep 17 00:00:00 2001 From: mattthew Date: Fri, 20 Jun 2025 16:35:52 -0400 Subject: [PATCH 06/12] Do not cast unsigned values to signed Suggestion from github cp --- edgraph/server.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/edgraph/server.go b/edgraph/server.go index 61cd0b66bf6..b004858a65f 100644 --- a/edgraph/server.go +++ b/edgraph/server.go @@ -2152,7 +2152,7 @@ func verifyUniqueWithinMutation(qc *queryContext) error { for i := range qc.uniqueVars { gmuIndex, rdfIndex := decodeIndex(i) // handles cases where the mutation was pruned in updateMutations - if int(gmuIndex) >= len(qc.gmuList) || qc.gmuList[gmuIndex] == nil || int(rdfIndex) >= len(qc.gmuList[gmuIndex].Set) { + if gmuIndex >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex] == nil || uint32(rdfIndex) >= uint32(len(qc.gmuList[gmuIndex].Set)) { continue } pred1 := qc.gmuList[gmuIndex].Set[rdfIndex] @@ -2163,12 +2163,12 @@ func verifyUniqueWithinMutation(qc *queryContext) error { } gmuIndex2, rdfIndex2 := decodeIndex(j) // bounds check for the second predicate, which could also have been pruned - if int(gmuIndex2) >= len(qc.gmuList) || qc.gmuList[gmuIndex2] == nil || int(rdfIndex2) >= len(qc.gmuList[gmuIndex2].Set) { + if gmuIndex2 >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex2] == nil || uint32(rdfIndex2) >= uint32(len(qc.gmuList[gmuIndex2].Set)) { continue } pred2 := qc.gmuList[gmuIndex2].Set[rdfIndex2] - if pred1.Predicate == pred2.Predicate && dql.TypeValFrom(pred2.ObjectValue).Value == pred1Value && - pred1.Subject != pred2.Subject { + 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]", pred1Value, pred1.Predicate) } From a2c4e295dbcea2e728ec09b584ae0818da589f1e Mon Sep 17 00:00:00 2001 From: mattthew Date: Fri, 20 Jun 2025 16:36:53 -0400 Subject: [PATCH 07/12] Update change log --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f57b6b9f37..722eb05b62c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project will adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. +## Unreleased + +- **Fixed** + + - fix(core): fix panic in verifyUniqueWithinMutation when mutation is conditionally pruned (#9450) + ## [v24.X.X] - YYYY-MM-DD - **GraphQL** From 4c88709f2db222687e97b89cb8b04b2cda5ff629 Mon Sep 17 00:00:00 2001 From: mattthew Date: Fri, 20 Jun 2025 16:42:40 -0400 Subject: [PATCH 08/12] Remove unnecessary casts --- edgraph/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/edgraph/server.go b/edgraph/server.go index b004858a65f..b91edee72a7 100644 --- a/edgraph/server.go +++ b/edgraph/server.go @@ -2152,7 +2152,7 @@ func verifyUniqueWithinMutation(qc *queryContext) error { for i := range qc.uniqueVars { gmuIndex, rdfIndex := decodeIndex(i) // handles cases where the mutation was pruned in updateMutations - if gmuIndex >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex] == nil || uint32(rdfIndex) >= uint32(len(qc.gmuList[gmuIndex].Set)) { + if gmuIndex >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex] == nil || rdfIndex >= uint32(len(qc.gmuList[gmuIndex].Set)) { continue } pred1 := qc.gmuList[gmuIndex].Set[rdfIndex] @@ -2163,7 +2163,7 @@ func verifyUniqueWithinMutation(qc *queryContext) error { } gmuIndex2, rdfIndex2 := decodeIndex(j) // bounds check for the second predicate, which could also have been pruned - if gmuIndex2 >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex2] == nil || uint32(rdfIndex2) >= uint32(len(qc.gmuList[gmuIndex2].Set)) { + if gmuIndex2 >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex2] == nil || rdfIndex2 >= uint32(len(qc.gmuList[gmuIndex2].Set)) { continue } pred2 := qc.gmuList[gmuIndex2].Set[rdfIndex2] From d0722459ffc65afc6bb2df7d8234feccd6b3348f Mon Sep 17 00:00:00 2001 From: mattthew Date: Mon, 23 Jun 2025 13:11:39 -0400 Subject: [PATCH 09/12] Simplify test for pruned mutations --- edgraph/server.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/edgraph/server.go b/edgraph/server.go index b91edee72a7..c949947725d 100644 --- a/edgraph/server.go +++ b/edgraph/server.go @@ -2152,7 +2152,10 @@ func verifyUniqueWithinMutation(qc *queryContext) error { for i := range qc.uniqueVars { gmuIndex, rdfIndex := decodeIndex(i) // handles cases where the mutation was pruned in updateMutations - if gmuIndex >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex] == nil || rdfIndex >= uint32(len(qc.gmuList[gmuIndex].Set)) { + //if gmuIndex >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex] == nil || rdfIndex >= uint32(len(qc.gmuList[gmuIndex].Set)) { + // continue + // + if qc.gmuList[gmuIndex].Set == nil { continue } pred1 := qc.gmuList[gmuIndex].Set[rdfIndex] @@ -2163,7 +2166,10 @@ func verifyUniqueWithinMutation(qc *queryContext) error { } gmuIndex2, rdfIndex2 := decodeIndex(j) // bounds check for the second predicate, which could also have been pruned - if gmuIndex2 >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex2] == nil || rdfIndex2 >= uint32(len(qc.gmuList[gmuIndex2].Set)) { + //if gmuIndex2 >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex2] == nil || rdfIndex2 >= uint32(len(qc.gmuList[gmuIndex2].Set)) { + // continue + //} + if qc.gmuList[gmuIndex2].Set == nil { continue } pred2 := qc.gmuList[gmuIndex2].Set[rdfIndex2] From 07f63e2d8f4bd507032194a506a997b814e5bb7d Mon Sep 17 00:00:00 2001 From: mattthew Date: Mon, 23 Jun 2025 14:37:17 -0400 Subject: [PATCH 10/12] Restore proper range checks --- edgraph/server.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/edgraph/server.go b/edgraph/server.go index c949947725d..6bfedb0c4c8 100644 --- a/edgraph/server.go +++ b/edgraph/server.go @@ -2152,10 +2152,7 @@ func verifyUniqueWithinMutation(qc *queryContext) error { for i := range qc.uniqueVars { gmuIndex, rdfIndex := decodeIndex(i) // handles cases where the mutation was pruned in updateMutations - //if gmuIndex >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex] == nil || rdfIndex >= uint32(len(qc.gmuList[gmuIndex].Set)) { - // continue - // - if qc.gmuList[gmuIndex].Set == nil { + if gmuIndex >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex] == nil || rdfIndex >= uint32(len(qc.gmuList[gmuIndex].Set)) { continue } pred1 := qc.gmuList[gmuIndex].Set[rdfIndex] @@ -2165,11 +2162,8 @@ func verifyUniqueWithinMutation(qc *queryContext) error { continue } gmuIndex2, rdfIndex2 := decodeIndex(j) - // bounds check for the second predicate, which could also have been pruned - //if gmuIndex2 >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex2] == nil || rdfIndex2 >= uint32(len(qc.gmuList[gmuIndex2].Set)) { - // continue - //} - if qc.gmuList[gmuIndex2].Set == nil { + // check for the second predicate, which could also have been pruned + if gmuIndex2 >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex2] == nil || rdfIndex2 >= uint32(len(qc.gmuList[gmuIndex2].Set)) { continue } pred2 := qc.gmuList[gmuIndex2].Set[rdfIndex2] From f525e26d536c1b88f6291cf1fc964a7fd74b28e9 Mon Sep 17 00:00:00 2001 From: mattthew Date: Mon, 23 Jun 2025 16:59:08 -0400 Subject: [PATCH 11/12] Add a unit test that tests references to indexes that may have been pruned --- edgraph/server_test.go | 80 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/edgraph/server_test.go b/edgraph/server_test.go index dc0222f9860..bd66950aedb 100644 --- a/edgraph/server_test.go +++ b/edgraph/server_test.go @@ -17,6 +17,7 @@ import ( "github.com/dgraph-io/badger/v4" "github.com/dgraph-io/dgo/v250/protos/api" "github.com/hypermodeinc/dgraph/v25/chunker" + "github.com/hypermodeinc/dgraph/v25/dql" "github.com/hypermodeinc/dgraph/v25/schema" "github.com/hypermodeinc/dgraph/v25/worker" "github.com/hypermodeinc/dgraph/v25/x" @@ -206,3 +207,82 @@ func TestGetHash(t *testing.T) { worker.Config.AclSecretKeyBytes = x.Sensitive("123456789") require.Equal(t, hex.EncodeToString(h.Sum(nil)), getHash(10, 20)) } + +func TestVerifyUniqueWithinMutationBoundsChecks(t *testing.T) { + t.Run("gmuIndex out of bounds", func(t *testing.T) { + qc := &queryContext{ + gmuList: []*dql.Mutation{ + { + Set: []*api.NQuad{ + { + Subject: "_:a", + Predicate: "username", + ObjectValue: &api.Value{ + Val: &api.Value_StrVal{StrVal: "user1"}, + }, + }, + }, + }, + }, + // Reference gmuList[1], which is out of bounds. + uniqueVars: map[uint64]uniquePredMeta{ + encodeIndex(1, 0): {}, + }, + } + err := verifyUniqueWithinMutation(qc) + require.NoError(t, err) + }) + + t.Run("rdfIndex out of bounds", func(t *testing.T) { + qc := &queryContext{ + gmuList: []*dql.Mutation{ + { + Set: []*api.NQuad{ + // Only one element at index 0. + { + Subject: "_:a", + Predicate: "username", + ObjectValue: &api.Value{ + Val: &api.Value_StrVal{StrVal: "user1"}, + }, + }, + }, + }, + }, + // Reference Set[1], which is out of bounds. + uniqueVars: map[uint64]uniquePredMeta{ + encodeIndex(0, 1): {}, + }, + } + err := verifyUniqueWithinMutation(qc) + require.NoError(t, err) + }) + + t.Run("gmuList element is nil", func(t *testing.T) { + qc := &queryContext{ + gmuList: []*dql.Mutation{ + nil, // Element at index 0 is nil. + }, + uniqueVars: map[uint64]uniquePredMeta{ + encodeIndex(0, 0): {}, + }, + } + err := verifyUniqueWithinMutation(qc) + require.NoError(t, err) + }) + + t.Run("Set slice is nil", func(t *testing.T) { + qc := &queryContext{ + gmuList: []*dql.Mutation{ + { + Set: nil, // Set slice is nil. + }, + }, + uniqueVars: map[uint64]uniquePredMeta{ + encodeIndex(0, 0): {}, + }, + } + err := verifyUniqueWithinMutation(qc) + require.NoError(t, err) + }) +} From dcf7fd94bcd2d9d53efb8b74d872c1cac5a56c7f Mon Sep 17 00:00:00 2001 From: mattthew Date: Wed, 25 Jun 2025 13:03:55 -0400 Subject: [PATCH 12/12] Fix formatting imposed by trunk --- CHANGELOG.md | 63 ---------------------------------------------------- 1 file changed, 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 722eb05b62c..261805c25f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,11 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. ## Unreleased - **Fixed** - - fix(core): fix panic in verifyUniqueWithinMutation when mutation is conditionally pruned (#9450) ## [v24.X.X] - YYYY-MM-DD - **GraphQL** - - feat(graphql): adds translated DQL to extensions response when the "--graphql debug" super flag is set to true (#9280) @@ -27,7 +25,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - **Fixed** - **Core** - - fix(core): fix duplicate mutation entries for count index (#9208) - **Chore** @@ -40,12 +37,10 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - **Fixed** - **Vector** - - fix(vector): fixed euclidean spelling and a bug in parameters (#9165) - fix(vector): fix index rebuild when options change (#9149) - **Core** - - fix(core): update latest badger (#9169) - fix(core): Update error msg for txn too old to give more context(#9170) - fix(core): fix sampling rate of jaeger #9147 (#9147) @@ -57,7 +52,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - feat(core): Expose APIs that can be used outside Dgraph (#9176) - **GraphQL** - - feat(graphql): adds @default directive to graphql (#8017)(#8837) - **Chore** @@ -91,14 +85,12 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. [v24.0.2]: https://github.com/hypermodeinc/dgraph/compare/v24.0.1...v24.0.2 - **Fixed** - - fix(core): Fix namespace used by unique query https://github.com/hypermodeinc/dgraph/pull/9119 - fix(core): Fix regression in computing cond variables https://github.com/hypermodeinc/dgraph/pull/9126 - fix(live): Fix derigster while retrying https://github.com/hypermodeinc/dgraph/pull/9121 - **Chore** - - chore(deps): bump certifi from 2023.7.22 to 2024.7.4 in /contrib/config/marketplace/aws/tests https://github.com/hypermodeinc/dgraph/pull/9110 - chore(deps): bump idna from 2.9 to 3.7 in /contrib/config/marketplace/aws/tests @@ -117,14 +109,12 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. > changed. - **Fixed** - - fix(core): Fix regression in parsing json empty string #9108 - fix(upgrade): fix failing upgrade tests #9042 - fix(ci): fixing health endpoint issue #9116 - Fix(graphql): issue with local variable squashing intended JWK index by @matthewmcneely in #9114 - **Chore** - - chore(deps): bump urllib3 from 1.26.18 to 1.26.19 /contrib/config/marketplace/aws/tests #9103 - chore(deps): bump requests from 2.31.0 to 2.32.0 /contrib/config/marketplace/aws/tests #9090 @@ -143,7 +133,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. **Added** - **Vector** - - feat(graphql): Add vector support to graphql (#9074) - feat(vector): add vector to schema in #9060 - feat(vector): Added similar_to in vector in #9062 @@ -154,7 +143,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - fix(vector): Update query_rewriter to fix dotproduct and cosine query conversion in #9083 - **Core** - - feat(core): Add cache to dgraph.type predicate in #9068 - [BREAKING]feat(core): add unique constraint support in schema for new predicates in #8827 - feat(debug): add parse_key to debug tool in #7640 @@ -168,7 +156,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. **Fixed** - **Core** - - Fix(debug): Close file correctly before exiting on error in #9076 - fix(restore): fix incr restore and normal restore for vector predicates in #9078 - Fix(core): Fix deadlock in runMutation and error handling in #9085 @@ -188,7 +175,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - fix(restore): do not retry restore proposal (#8058) in #9017 - **Perf** - - perf(core): Fix performance issue in type filter (#9065) in #9089 - perf(core): Update postinglistCountAndLength function to improve performance in #9088 - perf(query): use quickselect instead of sorting while pagination by in #8995 @@ -213,7 +199,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - **Fixed** - **Core Dgraph** - - perf(core): Fix performance issue in type filter (#9065) - **CI & Testing** @@ -226,7 +211,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - **Added** - **Core Dgraph** - - perf(query): Improve IntersectCompressedWithBin for UID Pack (#8941) - feat(query): add feature flag normalize-compatibility-mode (#8845) (#8929) - feat(alpha): support RDF response via http query request (#8004) (#8639) @@ -240,7 +224,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. **Fixed** - **Core Dgraph** - - docs(zero): add comments in zero and clarify naming (#8945) - fix(cdc): skip bad events in CDC (#8076) - fix(bulk): enable running bulk loader with only gql schema (#8903) @@ -254,7 +237,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - chore(raft): better logging message for cleaning banned ns pred (#7886) - **Security** - - sec(acl): convert x.Sensitive to string type for auth hash (#8931) - chore(deps): bump google.golang.org/grpc from 1.52.0 to 1.53.0 (#8900) - chore(deps): bump certifi from 2022.12.7 to 2023.7.22 in /contrib/config/marketplace/aws/tests @@ -287,7 +269,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - **Fixed** - **Core Dgraph** - - chore(restore): add log message when restore fails (#8893) - fix(zero): fix zero's health endpoint to return json response (#8858) - chore(zero): improve error message while unmarshalling WAL (#8882) @@ -297,7 +278,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - upgrade dgo to v230.0.1 (#8785) - **CI** - - ci(dql): add workflow for fuzz testing (#8874) - chore(ci): add workflow for OSS build + unit tests (#8834) @@ -316,7 +296,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - **Added** - **GraphQL** - - fix(GraphQL): pass on HTTP request headers for subscriptions (https://github.com/hypermodeinc/dgraph/pull/8574) @@ -337,12 +316,10 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. **Fixed** - **GraphQL** - - fix(GraphQL): nested Auth Rules not working properly (https://github.com/hypermodeinc/dgraph/pull/8571) - **Core Dgraph** - - Fix wal replay issue during rollup (https://github.com/hypermodeinc/dgraph/pull/8774) - security(logging): fix aes implementation in audit logging (https://github.com/hypermodeinc/dgraph/pull/8323) @@ -385,7 +362,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. (https://github.com/hypermodeinc/dgraph/pull/8638) - **Test** - - chore(tests): add upgrade tests in query package (https://github.com/hypermodeinc/dgraph/pull/8750) - simplify test setup in query package (https://github.com/hypermodeinc/dgraph/pull/8782) @@ -432,7 +408,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. (https://github.com/hypermodeinc/dgraph/pull/8617) - **CD** - - fix(build): update dockerfile to use cache busting and reduce image size (https://github.com/hypermodeinc/dgraph/pull/8652) - chore(deps): update min go build version (https://github.com/hypermodeinc/dgraph/pull/8423) @@ -440,7 +415,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. (https://github.com/hypermodeinc/dgraph/pull/8790) - **Security** - - chore(deps): bump certifi from 2020.4.5.1 to 2022.12.7 in /contrib/config/marketplace/aws/tests (https://github.com/hypermodeinc/dgraph/pull/8496) - chore(deps): bump github.com/docker/distribution from 2.7.1+incompatible to 2.8.0+incompatible @@ -453,7 +427,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. -
CVE Fixes (31 total) - - CVE-2013-4235 - CVE-2016-20013 - CVE-2016-2781 @@ -490,7 +463,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. **Changed** - **Core Dgraph** - - upgrade badger to v4.1.0 (https://github.com/hypermodeinc/dgraph/pull/8783) (https://github.com/hypermodeinc/dgraph/pull/8709) - fix(multitenancy) store namespace in predicate as a hex separated by a hyphen to prevent json @@ -524,7 +496,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - **Added** - **GraphQL** - - fix(GraphQL): pass on HTTP request headers for subscriptions (https://github.com/hypermodeinc/dgraph/pull/8574) @@ -545,12 +516,10 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. **Fixed** - **GragphQL** - - fix(GraphQL): nested Auth Rules not working properly (https://github.com/hypermodeinc/dgraph/pull/8571) - **Core Dgraph** - - Fix wal replay issue during rollup (https://github.com/hypermodeinc/dgraph/pull/8774) - security(logging): fix aes implementation in audit logging (https://github.com/hypermodeinc/dgraph/pull/8323) @@ -593,7 +562,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. (https://github.com/hypermodeinc/dgraph/pull/8638) - **Test** - - chore(tests): add upgrade tests in query package (https://github.com/hypermodeinc/dgraph/pull/8750) - simplify test setup in query package (https://github.com/hypermodeinc/dgraph/pull/8782) @@ -640,7 +608,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. (https://github.com/hypermodeinc/dgraph/pull/8617) - **CD** - - fix(build): update dockerfile to use cache busting and reduce image size (https://github.com/hypermodeinc/dgraph/pull/8652) - chore(deps): update min go build version (https://github.com/hypermodeinc/dgraph/pull/8423) @@ -661,7 +628,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. **Changed** - **Core Dgraph** - - upgrade badger to v4.1.0 (https://github.com/hypermodeinc/dgraph/pull/8783) (https://github.com/hypermodeinc/dgraph/pull/8709) - fix(multitenancy) store namespace in predicate as a hex separated by a hyphen to prevent json @@ -695,7 +661,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - **Added** - **GraphQL** - - fix(GraphQL): pass on HTTP request headers for subscriptions (https://github.com/hypermodeinc/dgraph/pull/8574) @@ -714,12 +679,10 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. **Fixed** - **GragphQL** - - fix(GraphQL): nested Auth Rules not working properly (https://github.com/hypermodeinc/dgraph/pull/8571) - **Core Dgraph** - - chore(logs): add logs to track dropped proposals (https://github.com/hypermodeinc/dgraph/pull/8568) - fix(debug): check length of wal entry before parsing @@ -753,7 +716,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. (https://github.com/hypermodeinc/dgraph/pull/8638) - **Test** - - fix(test): avoid host volume mount in minio container (https://github.com/hypermodeinc/dgraph/pull/8569) - chore(test): add tests for lex/iri.go,chunker/chunk.go @@ -771,7 +733,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. (https://github.com/hypermodeinc/dgraph/pull/8617) - **CD** - - fix(build): update dockerfile to use cache busting and reduce image size (https://github.com/hypermodeinc/dgraph/pull/8652) - chore(deps): update min go build version (https://github.com/hypermodeinc/dgraph/pull/8423) @@ -790,7 +751,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. **Changed** - **Core Dgraph** - - fix(multitenancy) store namespace in predicate as a hex separated by a hyphen to prevent json marshal issues (https://github.com/hypermodeinc/dgraph/pull/8601) - fix(query): handle bad timezone correctly (https://github.com/hypermodeinc/dgraph/pull/8657) @@ -830,12 +790,10 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. - **Fixed** - **EDgraph** - - fix(ACL): Prevents permissions overrride and merges acl cache to persist permissions across different namespaces (https://github.com/hypermodeinc/dgraph/pull/8506) - **Core Dgraph** - - Fix(badger): Upgrade badger version to fix manifest corruption (https://github.com/hypermodeinc/dgraph/pull/8365) - fix(pagination): Fix after for regexp, match functions @@ -853,17 +811,14 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. (https://github.com/hypermodeinc/dgraph/pull/8431) - **GraphQL** - - Fix(GraphQL): Make mutation rewriting tests more robust (https://github.com/hypermodeinc/dgraph/pull/8449) - **Security** - -
CVE Fixes (35 total) **CVE Fixes** (35 total) - - CVE-2013-4235 - CVE-2016-20013 - CVE-2016-2781 @@ -904,14 +859,12 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`. GHSA Fixes (2 total) **GHSE Fixes** (2 total) - - GHSA-69ch-w2m2-3vjp - GHSA-m332-53r6-2w93 **Changed** - **CI Enhancements** - - Added more unit tests (https://github.com/hypermodeinc/dgraph/pull/8470 https://github.com/hypermodeinc/dgraph/pull/8489 https://github.com/hypermodeinc/dgraph/pull/8479 @@ -968,12 +921,10 @@ https://discuss.dgraph.io/t/dgraph-v22-0-0-rc1-20221003-release-candidate/17839 - **Posting** - fix(rollups): Fix splits in roll-up (https://github.com/hypermodeinc/dgraph/pull/8297) - **Security** - -
CVE Fixes (417 total) **CVE Fixes** (417 total) - - CVE-2019-0210 - CVE-2019-0205 - CVE-2021-43565 @@ -1397,7 +1348,6 @@ https://discuss.dgraph.io/t/dgraph-v22-0-0-rc1-20221003-release-candidate/17839 GHSA Fixes (5 total) **GHSA Fixes** (5 total) - - GHSA-jq7p-26h5-w78r - GHSA-8c26-wmh5-6g9v - GHSA-h6xx-pmxh-3wgp @@ -1444,7 +1394,6 @@ https://discuss.dgraph.io/t/dgraph-v22-0-0-rc1-20221003-release-candidate/17839 - **Fixed** - GraphQL - - Handle extend keyword for Queries and Mutations ([#7923][]) - Core Dgraph @@ -1490,7 +1439,6 @@ https://discuss.dgraph.io/t/dgraph-v22-0-0-rc1-20221003-release-candidate/17839 - **Fixed** - GraphQL - - fix(GraphQL): fix @cascade with Pagination for @auth queries ([#7695][]) - Fix(GraphQL): Fix GraphQL encoding in case of empty list ([#7726][]) ([#7730][]) - Fix(GraphQL): Add filter in DQL query in case of reverse predicate ([#7728][]) ([#7733][]) @@ -1603,7 +1551,6 @@ https://discuss.dgraph.io/t/dgraph-v22-0-0-rc1-20221003-release-candidate/17839 **Added** - GraphQL - - Feat(GraphQL): Zero HTTP endpoints are now available at GraphQL admin (GraphQL-1118) ([#6649][]) ([#7670][]) - Feat(GraphQL): Webhooks on add/update/delete mutations (GraphQL-1045) ([#7494][]) ([#7616][]) @@ -1634,7 +1581,6 @@ https://discuss.dgraph.io/t/dgraph-v22-0-0-rc1-20221003-release-candidate/17839 - Feat(GraphQL): Add support for passing OAuth Bearer token as authorization JWT ([#7490][]) - Core Dgraph - - Feat(query): Add mechanism to have a limit on number of pending queries ([#7603][]) - Perf(bulk): Reuse allocator ([#7360][]) - Perf(compression): Use gzip with BestSpeed in export and backup ([#7643][]) ([#7683][]) @@ -1663,7 +1609,6 @@ https://discuss.dgraph.io/t/dgraph-v22-0-0-rc1-20221003-release-candidate/17839 **Fixed** - GraphQL - - Fix(GraphQL): Fix Execution Trace for Add and Update Mutations ([#7656][]) - Fix(GraphQL): Add error handling for unrecognized args to generate directive. ([#7612][]) - Fix(GraphQL): Fix panic when no schema exists for a new namespace ([#7630][]) @@ -1708,7 +1653,6 @@ https://discuss.dgraph.io/t/dgraph-v22-0-0-rc1-20221003-release-candidate/17839 - Fix(GraphQL): Fix panic caused by incorrect input coercion of scalar to list ([#7405][]) - Core Dgraph - - Fix(flag): Fix bulk loader flag and remove flag parsing from critical path ([#7679][]) - Fix(query): Fix pagination with match functions ([#7668][]) - Fix(postingList): Acquire lock before reading the cached posting list ([#7632][]) @@ -3350,7 +3294,6 @@ Enterprise features: **Changed** - **Breaking changes** - - **uid schema type**: The `uid` schema type now means a one-to-one relation, **not** a one-to-many relation as in Dgraph v1.1. To specify a one-to-many relation in Dgraph v1.0, use the `[uid]` schema type. ([#2895][], [#3173][], [#2921][]) @@ -3366,7 +3309,6 @@ Enterprise features: - **HTTP API**: The HTTP API has been updated to replace the custom HTTP headers with standard headers. - - Change `/commit` endpoint to accept a list of preds for conflict detection. ([#3020][]) - Remove custom HTTP Headers, cleanup API. ([#3365][]) - The startTs path parameter is now a query parameter `startTs` for the `/query`, `/mutate`, @@ -3428,7 +3370,6 @@ Enterprise features: - Allow glog flags to be set via config file. ([#3062][], [#3077][]) - Logging - - Suppress logging before `flag.Parse` from glog. ([#2970][]) - Move glog of missing value warning to verbosity level 3. ([#3092][]) - Change time threshold for Raft.Ready warning logs. ([#3901][]) @@ -3436,7 +3377,6 @@ Enterprise features: - Add additional logs to show progress of reindexing operation. ([#3746][]) - Error messages - - Output the line and column number in schema parsing error messages. ([#2986][]) - Improve error of empty block queries. ([#3015][]) - Update flag description and error messaging related to `--query_edge_limit` flag. ([#2979][]) @@ -3445,7 +3385,6 @@ Enterprise features: - Return GraphQL compliant `"errors"` field for HTTP requests. ([#3728][]) - Optimizations - - Don't read posting lists from disk when mutating indices. ([#3695][], [#3713][]) - Avoid preallocating uid slice. It was slowing down unpackBlock. - Reduce memory consumption in bulk loader. ([#3724][]) @@ -3521,7 +3460,6 @@ Dgraph Increment Tool Query - Type system - - Add `type` function to query types. ([#2933][]) - Parser for type declaration. ([#2950][]) - Add `@type` directive to enforce type constraints. ([#3003][]) @@ -3535,7 +3473,6 @@ Query - Add the `upsert` block to send "query-mutate-commit" updates as a single call to Dgraph. This is especially helpful to do upserts with the `@upsert` schema directive. Addresses [#3059][]. ([#3412][]) - - Add support for conditional mutation in Upsert Block. ([#3612][]) - Allow querying all lang values of a predicate. ([#2910][])