parseOnConflictClause parses the ON CONFLICT clause (PostgreSQL UPSERT) Syntax: ON CONFLICT [(columns)] | ON CONSTRAINT name DO NOTHING | DO UPDATE SET ...
()
| 261 | // parseOnConflictClause parses the ON CONFLICT clause (PostgreSQL UPSERT) |
| 262 | // Syntax: ON CONFLICT [(columns)] | ON CONSTRAINT name DO NOTHING | DO UPDATE SET ... |
| 263 | func (p *Parser) parseOnConflictClause() (*ast.OnConflict, error) { |
| 264 | onConflict := &ast.OnConflict{} |
| 265 | |
| 266 | // Parse optional conflict target: (column_list) or ON CONSTRAINT constraint_name |
| 267 | if p.isType(models.TokenTypeLParen) { |
| 268 | p.advance() // Consume ( |
| 269 | var targets []ast.Expression |
| 270 | |
| 271 | for { |
| 272 | if !p.isIdentifier() { |
| 273 | return nil, p.expectedError("column name in ON CONFLICT target") |
| 274 | } |
| 275 | targets = append(targets, &ast.Identifier{Name: p.currentToken.Token.Value}) |
| 276 | p.advance() |
| 277 | |
| 278 | if !p.isType(models.TokenTypeComma) { |
| 279 | break |
| 280 | } |
| 281 | p.advance() // Consume comma |
| 282 | } |
| 283 | |
| 284 | if !p.isType(models.TokenTypeRParen) { |
| 285 | return nil, p.expectedError(")") |
| 286 | } |
| 287 | p.advance() // Consume ) |
| 288 | onConflict.Target = targets |
| 289 | } else if p.isType(models.TokenTypeOn) && p.peekToken().Token.Value == "CONSTRAINT" { |
| 290 | // ON CONSTRAINT constraint_name |
| 291 | p.advance() // Consume ON |
| 292 | p.advance() // Consume CONSTRAINT |
| 293 | if !p.isIdentifier() { |
| 294 | return nil, p.expectedError("constraint name") |
| 295 | } |
| 296 | onConflict.Constraint = p.currentToken.Token.Value |
| 297 | p.advance() |
| 298 | } |
| 299 | |
| 300 | // Parse DO keyword |
| 301 | if p.currentToken.Token.Value != "DO" { |
| 302 | return nil, p.expectedError("DO") |
| 303 | } |
| 304 | p.advance() // Consume DO |
| 305 | |
| 306 | // Parse action: NOTHING or UPDATE |
| 307 | if p.currentToken.Token.Value == "NOTHING" { |
| 308 | onConflict.Action = ast.OnConflictAction{DoNothing: true} |
| 309 | p.advance() // Consume NOTHING |
| 310 | } else if p.isType(models.TokenTypeUpdate) { |
| 311 | p.advance() // Consume UPDATE |
| 312 | |
| 313 | // Parse SET keyword |
| 314 | if !p.isType(models.TokenTypeSet) { |
| 315 | return nil, p.expectedError("SET") |
| 316 | } |
| 317 | p.advance() // Consume SET |
| 318 | |
| 319 | // Parse update assignments |
| 320 | var updates []ast.UpdateExpression |
no test coverage detected