-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
sql: make sure pgwire bind always happens in a transaction #71632
Conversation
393eb61
to
7d77e1e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 1 of 6 files at r1.
Reviewable status: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner and @rafiss)
pkg/sql/execute.go, line 48 at r1 (raw file):
// For user-defined types, we need to resolve the type to make sure we get // the latest changes to the type.
How does this help us in the case where the type has changed between bind and now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner)
pkg/sql/execute.go, line 48 at r1 (raw file):
Previously, ajwerner wrote…
// For user-defined types, we need to resolve the type to make sure we get // the latest changes to the type.
How does this help us in the case where the type has changed between bind and now?
the code here is for the EXECUTE sql command. binding the placeholders and executing the query is always going to be in the same implicit transaction. in other words:
- PREPARE: creates the prepared statement
- EXECUTE: binds parameters and runs the query in the same implicit txn
but if you're asking about the type changing between the pgwire Bind and the pgwire Exec (which is the code changed in conn_executor_prepare.go), this commit still helps. previously, the Bind would use whichever previous txn happened to be on the conn_executor, and now it gets its own txn. i don't see why Bind and Exec should share the same transaction. first of all, if anyone does change the type in between Bind and Exec, that seems like more of an edge case than what's being fixed here. but even so, it sounds correct to me that the type resolution during Bind
should use the version of the type as it was at the time of the Bind
command
but maybe i misunderstand you. if so, could you type out the sequence of pgwire commands and type changes that you're asking about?
I agree fully that they should not be using the same transaction. This change is good. I think the hazard is that if there's a change in the type (or, more likely, in the regclass) between the Bind and Exec then we'll use the old value. Consider the below contrived example:
Now, consider the argument is a table name and that between bind and exec we rename the table. Nothing will tell us we got it wrong. I think we can paper over that, but it's interesting. For enum values, we're a bit better off because it's highly likely that the execution plan tracked the version of the enum it used and when we check My question was to check my understanding. Thanks |
Ah thanks for explaining.
I guess I'm not really convinced that it's incorrect to use the old value. But also, turns out the PostgreSQL spec doesn't allow anything to happen in between Bind and Exec. From https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY
In other words, after
|
Filed #71665 |
7d77e1e
to
9e590ec
Compare
Ah, but we actually can get a test like the one that you're asking for by using an explicit transaction. Added one to hm i guess it's actually still different from #64140 since that issue is only talking about non-explicit txns. well, still a good test to have i think. |
In an explicit transaction, won't all three steps will be using the same transaction. We'll bypass cockroach/pkg/sql/conn_executor_prepare.go Lines 420 to 427 in 7669013
I sort of hear that. The reason I'd say it is is that we say that once return from a rename, we claim that no transaction which happens after (has a higher commit timestamp) will observe the old name. It's possible in this edge case. I think it'd be a problem in postgres if you ran under serializable.
My claim is regarding something happening of a different connection between bind and exec. The rename would occur on a separate connection, concurrently, between the bind and exec. |
9db9e35
to
f9becb3
Compare
pkg/sql/execute.go
Outdated
|
||
// For user-defined types, we need to resolve the type to make sure we get | ||
// the latest changes to the type. | ||
if _, ok := types.OidToType[typ.Oid()]; !ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe if typ.UserDefined() {
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
# TODO(ajwerner): Why are there two ReadyForQuery? | ||
|
||
until | ||
# There are two ReadyForQuerys because a simply query was followed by Sync |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: full stop at end of comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -38,15 +38,13 @@ ReadyForQuery | |||
# planner transaction but never set it. This was pretty much the only | |||
# way you could do such a thing. | |||
|
|||
send |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you comment on why this is crdb_only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
The approach of using a transaction matches what we do for pgwire Parse messages already. This is important to make sure that user-defined types are leased correctly. This also updated the SQL EXECUTE command to resolve user-defined types so that it gets the latest changes. Release note (bug fix): Adding new values to a user-defined enum type will previously would cause a prepared statement using that type to not work. This now works as expected.
f9becb3
to
01ca1e5
Compare
flaky CI tftr! bors r=otan |
Build succeeded: |
@rafiss I'm not familiar with this change, but I noticed in passing while looking at a few benchmarks that it made I know this was a bug fix and I unfortunately don't have any good suggestions (beyond #74350) to make this cheaper, but I figured I'd mention it here in case there were alternative fixes that didn't require a separate txn. |
One optimization potential here is that we only need to create/use a transaction if we end up calling |
@nvanbenschoten I made that optimization here: #74423 |
fixes #70378
and maybe #64140
The approach of using a transaction matches what we do for pgwire Parse
messages already. This is important to make sure that user-defined types
are leased correctly.
This also updated the SQL EXECUTE command to resolve user-defined types
so that it gets the latest changes.
Release note (bug fix): Adding new values to a user-defined enum type
will previously would cause a prepared statement using that type to not
work. This now works as expected.