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 }