Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not swallow errors in simple query. Fixing transaction descriptors. Read envchanges correctly. #105

Merged
merged 3 commits into from
Dec 1, 2020

Conversation

pimeys
Copy link
Contributor

@pimeys pimeys commented Nov 30, 2020

Fixing three bugs.

Error swallowing

This fixes a problem where we might have warnings coming before errors in multiple resultset queries, when executing with simple_query.

Example:

CREATE UNIQUE INDEX [X] ON [table] ([column]);

In this case, if our column breaks unique validations, we'd expect simple_query to return an error.

If the column is of type varchar and the length is above threshold, the first token we get back is a warning of index size. We used to stop fetching data at this point, and our query would be successful even though we actually had an error in the wire.

Transaction descriptor reading

Only the BeginTransaction holds the descriptor. Commit, rollback et.al. do not hold any data, and we can just zero the descriptor if having these events.

This has been causing weird bugs when running MARS with transactions somewhere in the middle.

Environment change token reads

This was tricky. When we get TokenEnvChange, it's typically in the following form:

  • length
  • type
  • data, usually having another length in the beginning

What we must do is to read ALL bytes first with the first length, then use this buffer to construct whatever token we need. There might be some bytes left in the buffer, which we just throw away in the end.

This fixes a problem where we might have warnings coming before errors
in multiple resultset queries, when executing with `simple_query`.

Example:

```
CREATE UNIQUE INDEX [X] ON [table] ([column]);
```

In this case, if our column breaks unique validations, we'd expect
`simple_query` to return an error.

If the column is of type `varchar` and the length is above threshold,
the first token we get back is a warning of index size. We used to stop
fetching data at this point, and our query would be successful even
though we actually had an error in the wire.
@pimeys pimeys added this to the 2.13.0 milestone Nov 30, 2020
Only the `BeginTransaction` holds the descriptor. Commit, rollback
et.al. do not hold any data, and we can just zero the descriptor if
having these events.

This has been causing weird bugs when running MARS with transactions
somewhere in the middle.
@pimeys pimeys changed the title Do not swallow errors in simple query. Do not swallow errors in simple query. Fixing transaction descriptors. Nov 30, 2020
@pimeys pimeys changed the title Do not swallow errors in simple query. Fixing transaction descriptors. Do not swallow errors in simple query. Fixing transaction descriptors. Read envchanges correctly. Nov 30, 2020
In certain cases envchange token contains data we don't need. We still
must read all of it to not cause trouble when reading the next token.
@pimeys pimeys force-pushed the fix-errow-swallow-with-simple-query branch from f3cb73a to fcb18ef Compare November 30, 2020 20:51
Copy link
Contributor

@tomhoule tomhoule left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding of the protocol isn't deep so I can't comment on that, but in general the changes make sense and it looks right.

@@ -10,19 +10,19 @@ pub struct TokenRpcRequest<'a> {
proc_id: RpcProcIdValue<'a>,
flags: RpcOptionFlags,
params: Vec<RpcParam<'a>>,
transaction_id: u64,
transaction_desc: [u8; 8],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that change motivated by endianness concerns? I can see we write that with put_slice() instead of put_u64_le() now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just do whatever the JDBC driver does. We always just write bytes to the message header, so there is no reason to convert that to an arbitrary number.

// We read all the bytes now, due to whatever environment change tokens
// we read, they might contain padding zeroes in the end we must
// discard.
let mut bytes = vec![0; len];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one place where read-buf will come in handy — free performance improvement when it's stable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, except we don't luckily read env tokens that often, and they're all quite short.

Still excited about that RFC.

Some(ReceivedToken::DoneInProc(done))
| Some(ReceivedToken::DoneProc(done))
| Some(ReceivedToken::Done(done)) => {
if !done.has_more() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if I understand this correctly, this is the part about swallowed errors?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. We must go through the whole stream always. We might get any of the done tokens, but they have a bitflag set if there's more data to be come.

src.read_u8().await?;
let desc = src.read_u64_le().await?;
let len = buf.read_u8()?;
assert!(len == 8);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this is really needed. The JDBC code is extremely careful asserting all the things. The TDS standard dictates how the descriptor is always 8 bytes. And the only implementation is the SQL Server from Microsoft, which I highly doubt would send 9 bytes descriptor every now and then...

@pimeys pimeys merged commit 23c4b88 into master Dec 1, 2020
@pimeys pimeys deleted the fix-errow-swallow-with-simple-query branch December 1, 2020 09:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants