github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/query/mutation.go (about) 1 /* 2 * Copyright 2017-2018 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package query 18 19 import ( 20 "context" 21 "strings" 22 "time" 23 24 otrace "go.opencensus.io/trace" 25 26 "github.com/dgraph-io/dgo/protos/api" 27 "github.com/dgraph-io/dgraph/gql" 28 "github.com/dgraph-io/dgraph/protos/pb" 29 "github.com/dgraph-io/dgraph/types/facets" 30 "github.com/dgraph-io/dgraph/worker" 31 "github.com/dgraph-io/dgraph/x" 32 "github.com/golang/glog" 33 "github.com/pkg/errors" 34 ) 35 36 // ApplyMutations performs the required edge expansions and forwards the results to the 37 // worker to perform the mutations. 38 func ApplyMutations(ctx context.Context, m *pb.Mutations) (*api.TxnContext, error) { 39 edges, err := expandEdges(ctx, m) 40 if err != nil { 41 return nil, errors.Wrapf(err, "While adding pb.edges") 42 } 43 m.Edges = edges 44 45 tctx, err := worker.MutateOverNetwork(ctx, m) 46 if err != nil { 47 if span := otrace.FromContext(ctx); span != nil { 48 span.Annotatef(nil, "MutateOverNetwork Error: %v. Mutation: %v.", err, m) 49 } 50 } 51 return tctx, err 52 } 53 54 func expandEdges(ctx context.Context, m *pb.Mutations) ([]*pb.DirectedEdge, error) { 55 edges := make([]*pb.DirectedEdge, 0, 2*len(m.Edges)) 56 for _, edge := range m.Edges { 57 x.AssertTrue(edge.Op == pb.DirectedEdge_DEL || edge.Op == pb.DirectedEdge_SET) 58 59 var preds []string 60 if edge.Attr != x.Star { 61 preds = []string{edge.Attr} 62 } else { 63 sg := &SubGraph{} 64 sg.DestUIDs = &pb.List{Uids: []uint64{edge.GetEntity()}} 65 sg.ReadTs = m.StartTs 66 67 types, err := getNodeTypes(ctx, sg) 68 if err != nil { 69 return nil, err 70 } 71 preds = append(preds, getPredicatesFromTypes(types)...) 72 preds = append(preds, x.ReservedPredicates()...) 73 } 74 75 for _, pred := range preds { 76 edgeCopy := *edge 77 edgeCopy.Attr = pred 78 edges = append(edges, &edgeCopy) 79 } 80 } 81 82 return edges, nil 83 } 84 85 func verifyUid(ctx context.Context, uid uint64) error { 86 if uid <= worker.MaxLeaseId() { 87 return nil 88 } 89 deadline := time.Now().Add(3 * time.Second) 90 ticker := time.NewTicker(100 * time.Millisecond) 91 defer ticker.Stop() 92 93 for { 94 select { 95 case <-ticker.C: 96 lease := worker.MaxLeaseId() 97 if uid <= lease { 98 return nil 99 } 100 if time.Now().After(deadline) { 101 err := errors.Errorf("Uid: [%d] cannot be greater than lease: [%d]", uid, lease) 102 glog.V(2).Infof("verifyUid returned error: %v", err) 103 return err 104 } 105 case <-ctx.Done(): 106 return ctx.Err() 107 } 108 } 109 } 110 111 // AssignUids tries to assign unique ids to each identity in the subjects and objects in the 112 // format of _:xxx. An identity, e.g. _:a, will only be assigned one uid regardless how many times 113 // it shows up in the subjects or objects 114 func AssignUids(ctx context.Context, nquads []*api.NQuad) (map[string]uint64, error) { 115 newUids := make(map[string]uint64) 116 num := &pb.Num{} 117 var err error 118 for _, nq := range nquads { 119 // We dont want to assign uids to these. 120 if nq.Subject == x.Star && nq.ObjectValue.GetDefaultVal() == x.Star { 121 return newUids, errors.New("Predicate deletion should be called via alter") 122 } 123 124 if len(nq.Subject) == 0 { 125 return nil, errors.Errorf("Subject must not be empty for nquad: %+v", nq) 126 } 127 var uid uint64 128 if strings.HasPrefix(nq.Subject, "_:") { 129 newUids[nq.Subject] = 0 130 } else if uid, err = gql.ParseUid(nq.Subject); err != nil { 131 return newUids, err 132 } 133 if err = verifyUid(ctx, uid); err != nil { 134 return newUids, err 135 } 136 137 if len(nq.ObjectId) > 0 { 138 var uid uint64 139 if strings.HasPrefix(nq.ObjectId, "_:") { 140 newUids[nq.ObjectId] = 0 141 } else if uid, err = gql.ParseUid(nq.ObjectId); err != nil { 142 return newUids, err 143 } 144 if err = verifyUid(ctx, uid); err != nil { 145 return newUids, err 146 } 147 } 148 } 149 150 num.Val = uint64(len(newUids)) 151 if int(num.Val) > 0 { 152 var res *pb.AssignedIds 153 // TODO: Optimize later by prefetching. Also consolidate all the UID requests into a single 154 // pending request from this server to zero. 155 if res, err = worker.AssignUidsOverNetwork(ctx, num); err != nil { 156 return newUids, err 157 } 158 curId := res.StartId 159 // assign generated ones now 160 for k := range newUids { 161 x.AssertTruef(curId != 0 && curId <= res.EndId, "not enough uids generated") 162 newUids[k] = curId 163 curId++ 164 } 165 } 166 return newUids, nil 167 } 168 169 // ToDirectedEdges converts the gql.Mutation input into a set of directed edges. 170 func ToDirectedEdges(gmu *gql.Mutation, 171 newUids map[string]uint64) (edges []*pb.DirectedEdge, err error) { 172 173 // Wrapper for a pointer to protos.Nquad 174 var wnq *gql.NQuad 175 176 parse := func(nq *api.NQuad, op pb.DirectedEdge_Op) error { 177 wnq = &gql.NQuad{NQuad: nq} 178 if len(nq.Subject) == 0 { 179 return nil 180 } 181 // Get edge from nquad using newUids. 182 var edge *pb.DirectedEdge 183 edge, err = wnq.ToEdgeUsing(newUids) 184 if err != nil { 185 return errors.Wrap(err, "") 186 } 187 edge.Op = op 188 edges = append(edges, edge) 189 return nil 190 } 191 192 for _, nq := range gmu.Set { 193 if err := facets.SortAndValidate(nq.Facets); err != nil { 194 return edges, err 195 } 196 if err := parse(nq, pb.DirectedEdge_SET); err != nil { 197 return edges, err 198 } 199 } 200 for _, nq := range gmu.Del { 201 if nq.Subject == x.Star && nq.ObjectValue.GetDefaultVal() == x.Star { 202 return edges, errors.New("Predicate deletion should be called via alter") 203 } 204 if err := parse(nq, pb.DirectedEdge_DEL); err != nil { 205 return edges, err 206 } 207 } 208 209 return edges, nil 210 }