Support raise exception/error on upsert block

Moved from GitHub dgraph/4500

Posted by ChStark:

What you wanted to do

If we see the documentation, we can find an example of an upsert like this

upsert {
  query {
    q(func: eq(email, "user@company1.io")) {
      v as uid
      name
    }
  }

  mutation{
    set {
      uid(v) <name> "first last" .
      uid(v) <email> "user@company1.io" .
    }
  }
}

Wich is ok if you want to do the actual upsert, but most often than not, we need to not insert data if the data is present, according to the docs of the conditional upsert The upsert block also allows specifying conditional mutation blocks using an @if directive. The mutation is executed only when the specified condition is true. If the condition is false, the mutation is silently ignored

What you actually did

Since we can ensure to not store repeated information with the @if directive, we need to look up on the response to see if the mutation created a new node or the query to see if the query got a result.

what I did

upsert {
  query {
    q(func: eq(email, "user@company1.io")) {
      v as uid
      name
    }
  }

  mutation @if( eq( len( v ) , 0 ) ){
    set {
      _:new <name> "first last" .
      _:new <email> "user@company1.io" .
    }
  }
}

And then look on the response if data.uids.new contains a new id

Why that wasn’t great, with examples

Is not great to need to inspect for each possible outcome of the mutation, it would be great if upsert block support intentional fails, something like

upsert {
  query {
    q(func: eq(email, "user@company1.io")) {
      v as uid
      name
    }
  }

  raise @if( lt( len( v ) , 0 ) ) {
    exception("Email was already on the database") #and rollback the transaction automatically
   #notice( "..." ) if it shouldn't stop transaction, but the message should be included on the response
  }

  mutation{ #we no longer need to add the @if directive here
    set {
      _:new <name> "first last" .
      _:new <email> "user@company1.io" .
    }
  }
}

Or something like that

Any external references to support your case

We can take the RAISE keyword from plpsql docs
https://www.postgresql.org/docs/9.3/plpgsql-errors-and-messages.html

mangalaman93 commented :

It is not really straight forward to raise an exception, there are challenges in doing this. For example, if we raise an error during the execution of the transaction, we discard the transaction and you cannot commit the transaction any more. I wonder whether that’d be acceptable.

We recently added the response of the query that you specify in upsert, see the docs https://docs.dgraph.io/mutations/#upsert-block. The result of the query is on the data that is before the execution of the mutation. That will help you figure out which if statement was executed in the upsert. Let us know if this solves your requirement.

ChStark commented :

From all my years working with transactional databases that is the exact use case, I don’t want to commit the transaction if something goes wrong. And being able to raise the exception only under some circunstances ( if statement ) will give the developer total control over the flow.

Can you name a case when you need to commit the transaction if something is wrong ?