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  }