I've been told that if the user cancels the query, our intention is to not set query_state to EXCEPTION. (Is that the desired behavior?) Adding a test case to test_cancellation.py as follows, shows that this is not the current behavior (at least not in all circumstances).
One example of how this can happen is if the cancel happens while the wait thread is still in WaitInternal, then WaitInternal will return Status::CANCELLED, and UpdateQueryStatus indiscriminately sets query_state_ to EXCEPTION if !ok(). But, I don't think that side of the execution can distinguish between user initiated cancel and error initiated cancel. Probably, QueryExecState::Cancel needs to remember which kind of cancellation it was so that the cancelled side can do the right thing.
We don't have a general workaround.