github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/gql/parser_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 gql
    18  
    19  import (
    20  	"github.com/dgraph-io/dgo/protos/api"
    21  	"github.com/dgraph-io/dgraph/lex"
    22  )
    23  
    24  // ParseMutation parses a block into a mutation. Returns an object with a mutation or
    25  // an upsert block with mutation, otherwise returns nil with an error.
    26  func ParseMutation(mutation string) (req *api.Request, err error) {
    27  	var lexer lex.Lexer
    28  	lexer.Reset(mutation)
    29  	lexer.Run(lexIdentifyBlock)
    30  	if err := lexer.ValidateResult(); err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	it := lexer.NewIterator()
    35  	if !it.Next() {
    36  		return nil, it.Errorf("Invalid mutation")
    37  	}
    38  
    39  	item := it.Item()
    40  	switch item.Typ {
    41  	case itemUpsertBlock:
    42  		if req, err = parseUpsertBlock(it); err != nil {
    43  			return nil, err
    44  		}
    45  	case itemLeftCurl:
    46  		mu, err := parseMutationBlock(it)
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  		req = &api.Request{Mutations: []*api.Mutation{mu}}
    51  	default:
    52  		return nil, it.Errorf("Unexpected token: [%s]", item.Val)
    53  	}
    54  
    55  	// mutations must be enclosed in a single block.
    56  	if it.Next() && it.Item().Typ != lex.ItemEOF {
    57  		return nil, it.Errorf("Unexpected %s after the end of the block", it.Item().Val)
    58  	}
    59  
    60  	return req, nil
    61  }
    62  
    63  // parseUpsertBlock parses the upsert block
    64  func parseUpsertBlock(it *lex.ItemIterator) (*api.Request, error) {
    65  	var req *api.Request
    66  	var queryText, condText string
    67  	var queryFound, condFound bool
    68  
    69  	// ===>upsert<=== {...}
    70  	if !it.Next() {
    71  		return nil, it.Errorf("Unexpected end of upsert block")
    72  	}
    73  
    74  	// upsert ===>{<=== ....}
    75  	item := it.Item()
    76  	if item.Typ != itemLeftCurl {
    77  		return nil, it.Errorf("Expected { at the start of block. Got: [%s]", item.Val)
    78  	}
    79  
    80  	for it.Next() {
    81  		item = it.Item()
    82  		switch {
    83  		// upsert {... ===>}<===
    84  		case item.Typ == itemRightCurl:
    85  			if req == nil {
    86  				return nil, it.Errorf("Empty mutation block")
    87  			} else if !queryFound {
    88  				return nil, it.Errorf("Query op not found in upsert block")
    89  			} else {
    90  				req.Query = queryText
    91  				return req, nil
    92  			}
    93  
    94  		// upsert { mutation{...} ===>query<==={...}}
    95  		case item.Typ == itemUpsertBlockOp && item.Val == "query":
    96  			if queryFound {
    97  				return nil, it.Errorf("Multiple query ops inside upsert block")
    98  			}
    99  			queryFound = true
   100  			if !it.Next() {
   101  				return nil, it.Errorf("Unexpected end of upsert block")
   102  			}
   103  			item = it.Item()
   104  			if item.Typ != itemUpsertBlockOpContent {
   105  				return nil, it.Errorf("Expecting brace, found '%s'", item.Val)
   106  			}
   107  			queryText += item.Val
   108  
   109  		// upsert { ===>mutation<=== {...} query{...}}
   110  		case item.Typ == itemUpsertBlockOp && item.Val == "mutation":
   111  			if !it.Next() {
   112  				return nil, it.Errorf("Unexpected end of upsert block")
   113  			}
   114  
   115  			// upsert { mutation ===>@if(...)<=== {....} query{...}}
   116  			item = it.Item()
   117  			if item.Typ == itemUpsertBlockOpContent {
   118  				if condFound {
   119  					return nil, it.Errorf("Multiple @if directive inside upsert block")
   120  				}
   121  				condFound = true
   122  				condText = item.Val
   123  				if !it.Next() {
   124  					return nil, it.Errorf("Unexpected end of upsert block")
   125  				}
   126  			}
   127  
   128  			// upsert @if(...) ===>{<=== ....}
   129  			mu, err := parseMutationBlock(it)
   130  			if err != nil {
   131  				return nil, err
   132  			}
   133  			mu.Cond = condText
   134  			req = &api.Request{Mutations: []*api.Mutation{mu}}
   135  
   136  		// upsert { mutation{...} ===>fragment<==={...}}
   137  		case item.Typ == itemUpsertBlockOp && item.Val == "fragment":
   138  			if !it.Next() {
   139  				return nil, it.Errorf("Unexpected end of upsert block")
   140  			}
   141  			item = it.Item()
   142  			if item.Typ != itemUpsertBlockOpContent {
   143  				return nil, it.Errorf("Expecting brace, found '%s'", item.Val)
   144  			}
   145  			queryText += "fragment" + item.Val
   146  
   147  		default:
   148  			return nil, it.Errorf("Unexpected token in upsert block [%s]", item.Val)
   149  		}
   150  	}
   151  
   152  	return nil, it.Errorf("Invalid upsert block")
   153  }
   154  
   155  // parseMutationBlock parses the mutation block
   156  func parseMutationBlock(it *lex.ItemIterator) (*api.Mutation, error) {
   157  	var mu api.Mutation
   158  
   159  	item := it.Item()
   160  	if item.Typ != itemLeftCurl {
   161  		return nil, it.Errorf("Expected { at the start of block. Got: [%s]", item.Val)
   162  	}
   163  
   164  	for it.Next() {
   165  		item := it.Item()
   166  		if item.Typ == itemText {
   167  			continue
   168  		}
   169  		if item.Typ == itemRightCurl {
   170  			return &mu, nil
   171  		}
   172  		if item.Typ == itemMutationOp {
   173  			if err := parseMutationOp(it, item.Val, &mu); err != nil {
   174  				return nil, err
   175  			}
   176  		}
   177  	}
   178  	return nil, it.Errorf("Invalid mutation.")
   179  }
   180  
   181  // parseMutationOp parses and stores set or delete operation string in Mutation.
   182  func parseMutationOp(it *lex.ItemIterator, op string, mu *api.Mutation) error {
   183  	parse := false
   184  	for it.Next() {
   185  		item := it.Item()
   186  		if item.Typ == itemText {
   187  			continue
   188  		}
   189  		if item.Typ == itemLeftCurl {
   190  			if parse {
   191  				return it.Errorf("Too many left curls in set mutation.")
   192  			}
   193  			parse = true
   194  		}
   195  		if item.Typ == itemMutationOpContent {
   196  			if !parse {
   197  				return it.Errorf("Mutation syntax invalid.")
   198  			}
   199  			if op == "set" {
   200  				mu.SetNquads = []byte(item.Val)
   201  			} else if op == "delete" {
   202  				mu.DelNquads = []byte(item.Val)
   203  			} else if op == "schema" {
   204  				return it.Errorf("Altering schema not supported through http client.")
   205  			} else if op == "dropall" {
   206  				return it.Errorf("Dropall not supported through http client.")
   207  			} else {
   208  				return it.Errorf("Invalid mutation operation.")
   209  			}
   210  		}
   211  		if item.Typ == itemRightCurl {
   212  			return nil
   213  		}
   214  	}
   215  	return it.Errorf("Invalid mutation formatting.")
   216  }