-
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: add support for DECLARE, FETCH, CLOSE CURSOR using internal executors #74006
Conversation
0ddbf1f
to
67ae61a
Compare
Thank you to @otan for inspiring me to take a look at this on flex friday! |
a0d2577
to
dd80080
Compare
2d248de
to
6ea630d
Compare
acc722f
to
a7066fe
Compare
Alright, I think this is ready for a look. |
My first instinct is that this is going to have bad interactions with schema changes in the transaction. That is to say, it won't see them. That's pretty gross. I wonder what happens when you create cursors over various pg_catalog things and then modify them while holding the cursor open. I'm not even sure what the right thing is. What sort of isolation are cursors supposed to have? I think because you're using the transaction object itself, it's going to be weird. Maybe it should be at some fixed sequence in the transaction? |
This is a good point. I will test to see how it works in PG.
Cursors are supposed to see a snapshot at the point of time that they were created in the transaction. That is, they're not supposed to be able to read other writes from the same transaction that happen after they were created. This behavior is implemented by the current PR, though to be honest, I don't know why it works. I assume when we make an InternalExecutor, we create a sequence point, or something? There's a logic test for it. |
a7066fe
to
208bb1d
Compare
It'll work for queries where we construct a leaf transaction if you have flows. The leaf transaction occurs at a snapshot. However, it's really important that when this happens we make sure that we properly allow the flows to finish so that the final leaf state flows back to the root txn before committing. Otherwise you'll subtly permit serializability violations. cockroach/pkg/sql/distsql/server.go Lines 289 to 291 in 775c2b0
I don't know enough to say how fragile this is. |
Hmm, but the tests in this PR work even without leaf transactions - we don't use those in the |
It'd be interesting to look at traces. Are all of the rows being fetched by the row fetcher in the first request? What if you add a lot of rows or something like that? |
It doesn't appear to have to do with that. I'll pull KV trace next, it's a bit annoying due to internal executor plumbing.
|
Ah, I forgot that we changed the KV protocol to not stop sending until it hit a bytes limit as well, not just a row limit. I think your hypothesis is correct. |
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 16 of 16 files at r1, 6 of 6 files at r2, 4 of 4 files at r3, 6 of 6 files at r4, all commit messages.
Reviewable status: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner, @jordanlewis, @otan, and @rafiss)
-- commits, line 5 at r1:
nit: missing release note.
pkg/sql/sql_cursor.go, line 132 at r2 (raw file):
return true, nil case tree.FetchRelative: for i := int64(0); i < f.offset; i++ {
Relative fetch can also be backward, should we check that f.offset
is not negative?
pkg/sql/sql_cursor.go, line 175 at r2 (raw file):
} func (s *sqlCursor) Next(ctx context.Context) (bool, error) {
nit: this probably could be unexported.
pkg/sql/sql_cursor.go, line 55 at r3 (raw file):
cursor := &sqlCursor{InternalRows: rows} if err := p.sqlCursors.addCursor(cursorName, cursor); err != nil { // thread at a time. But let's be diligent and clean up if it somehow does
nit: the comment is cut off.
pkg/sql/sql_cursor.go, line 182 at r3 (raw file):
// closeAll closes all cursors in the set. closeAll() // closeCursors closes the named cursor, returning an error if that cursor
nit: s/closeCursors/closeCursor/
.
pkg/sql/sql_cursor.go, line 185 at r3 (raw file):
// didn't exist in the set. closeCursor(string) error // closeCursors returns the named cursor, returning an error if that cursor
nit: s/closeCursors/getCursor/
.
pkg/sql/parser/sql.y, line 5092 at r1 (raw file):
// %SeeAlso: CLOSE, FETCH declare_cursor_stmt: DECLARE cursor_name opt_binary opt_sensitivity opt_scroll CURSOR opt_hold FOR select_stmt
PG docs say that
The key words ASENSITIVE, BINARY, INSENSITIVE, and SCROLL can appear in any order.
it looks like we currently don't support that, right? Is the only way to support that is to have all permutations defined explicitly?
pkg/sql/parser/sql.y, line 5195 at r1 (raw file):
Name: tree.Name($3), Count: $1.int64(), }
nit: there appear to be tabs for seven options in a row here.
pkg/sql/sem/tree/cursor.go, line 42 at r1 (raw file):
} ctx.WriteString("CURSOR ") if node.Hold {
Does it not matter to distinguish between unspecified hold and explicit "without hold"?
208bb1d
to
f716dfe
Compare
8bb65f6
to
fc0cf41
Compare
Added the "no schema changes" limitation in another commit. This is ready for another review! @otan any further review from you is definitely appreciated. I'm at least somewhat convinced that the txn seqnum implementation I've created here makes sense. Using a LeafTxn seems to be impractical without a fairly large refactor of LeafTxn due to limitations I've touched on above. @knz, as far as the test case enumeration you've proposed, I've not done it yet because I don't yet see what an interesting condition would be besides the simple cases of modify-after-cursor-creation and modify-before-cursor-creation-in-same-txn which I've already exercised. Concurrent txn isn't interesting because it's already exercised exactly the same by any of our other concurrent txn testing. |
@nvanbenschoten can you help ascertain that the seqnum dance performed here makes sense? |
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.
looks good to me, i'm unsure how to sign off on the seq num stuff though.
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.
The seqnum dance here makes sense to me. I think it would be worthwhile to add some kv-level testing for it, like we have for stepping modes in pkg/kv/kvclient/kvcoord/txn_interceptor_seq_num_allocator_test.go
.
Reviewed 12 of 16 files at r1, 2 of 15 files at r17, 15 of 15 files at r23, 6 of 6 files at r24, 4 of 4 files at r25, 7 of 7 files at r26, 6 of 6 files at r27, 2 of 2 files at r28, 3 of 3 files at r29, all commit messages.
Reviewable status: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner, @jordanlewis, @otan, @rafiss, and @yuzefovich)
pkg/kv/kvclient/kvcoord/txn_coord_sender.go, line 1309 at r27 (raw file):
tc.mu.Lock() defer tc.mu.Unlock() tc.interceptorAlloc.txnSeqNumAllocator.readSeq = seq
Should we add any validation here, like that this is >= 0 and <= writeSeq?
pkg/sql/sql_cursor.go, line 52 at r25 (raw file):
Previously, otan (Oliver Tan) wrote…
missing a comment line by the looks of it
Yes, this looks off.
pkg/sql/sql_cursor.go, line 180 at r27 (raw file):
// running FETCH on a CURSOR does not close it. // Reset the trasnaction's read sequence number to what it was before the
s/trasnaction/transaction/
pkg/sql/parser/sql.y, line 5122 at r23 (raw file):
$$.val = tree.Insensitive } | ASENSITIVE
Do we like that we won't even parse SENSITIVE? Would it be better to include that in the grammar but then throw a descriptive error?
pkg/sql/sem/tree/cursor.go, line 51 at r23 (raw file):
// CursorScrollOption represents the scroll option, if one was given, for a // DECLARE statement. type CursorScrollOption int
nit: should we make these enum types int8
? That will allow DeclareCursor
to pack better.
pkg/sql/logictest/testdata/logic_test/cursor, line 99 at r27 (raw file):
query IT FETCH RELATIVE 12 foo
Should we perform another INSERT after this first fetch as well, to make sure the cursor is insensitive to that as well?
pkg/sql/logictest/testdata/logic_test/cursor, line 99 at r27 (raw file):
query IT FETCH RELATIVE 12 foo
Should we perform a read-only operation outside of the cursor to demonstrate that it is sensitive to these INSERTs and to demonstrate the cursor hasn't corrupted the read seq num state?
fc0cf41
to
8b04cfb
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.
TFTR!
I added a test. I don't think it's that great. Basically it just makes sure that the txnSeqNumAllocator doesn't choke when you edit the read seqnum and send more commands. I'm just not sure if this is testing much. Did you have something else in mind as well?
Reviewable status: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner, @jordanlewis, @nvanbenschoten, @otan, @rafiss, and @yuzefovich)
pkg/kv/kvclient/kvcoord/txn_coord_sender.go, line 1309 at r27 (raw file):
Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
Should we add any validation here, like that this is >= 0 and <= writeSeq?
Done.
pkg/sql/sql_cursor.go, line 52 at r25 (raw file):
Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
Yes, this looks off.
Done.
pkg/sql/parser/sql.y, line 5122 at r23 (raw file):
Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
Do we like that we won't even parse SENSITIVE? Would it be better to include that in the grammar but then throw a descriptive error?
SENSITIVE isn't accepted by pg either so this would be a custom extension. I feel comfortable omitting it.
pkg/sql/sem/tree/cursor.go, line 51 at r23 (raw file):
Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
nit: should we make these enum types
int8
? That will allowDeclareCursor
to pack better.
Done.
pkg/sql/logictest/testdata/logic_test/cursor, line 25 at r24 (raw file):
Previously, otan (Oliver Tan) wrote…
can you add a test for a cursor which errors, e.g. selects a column which doesn't exist in a table?
Done.
pkg/sql/logictest/testdata/logic_test/cursor, line 99 at r27 (raw file):
Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
Should we perform another INSERT after this first fetch as well, to make sure the cursor is insensitive to that as well?
Good idea, done.
pkg/sql/logictest/testdata/logic_test/cursor, line 99 at r27 (raw file):
Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
Should we perform a read-only operation outside of the cursor to demonstrate that it is sensitive to these INSERTs and to demonstrate the cursor hasn't corrupted the read seq num state?
Good idea, done.
1301f7f
to
8c0a90e
Compare
This commit adds parser, AST, and no-op execution support for the DECLARE, FETCH, and CLOSE SQL cursor statements.
So far, only the simplest form of DECLARE is supported: forward-only cursors that are not able to be held open past the end of a transaction. And FETCH only supports the simple form that returns the next n rows. Release note (sql change): add support for DECLARE, FETCH and CLOSE commands for creating, using and deleting SQL cursors.
Move the map of session cursors to extraTxnState, which is where similar state exists, like the list of currently active prepared statements. Release note: None
Add open SQL cursors to pg_catalog.pg_cursors. Release note (sql change): SQL cursors now appear in pg_catalog.pg_cursors.
This commit causes cursors to be "insensitive", aka not reading writes that happen after they were declared in a transaction, by fixing the transaction sequence number for cursor fetches, even if subsequent writes happen and change that sequence number in the user's transaction. Release note: None
Cursors are not allowed to run mutations via common-table expressions. Ban them by running the optimizer on the statement up front. Also, this allows us to return any plan errors eagerly on DECLARE, rather than waiting for FETCH. Release note: None
This commit prevents schema changes from being initiated in open transactions that have open cursors inside. This is to prevent issues with cursors that remain open on a table that might get modified by a schema change. In Postgres, only schema changes that modify objects used by open cursors are barred. For now, we just bar all schema changes because we don't expect that this will affect many users, and it's a bit annoying to do this precisely until the new schema changer lands. Release note: None
8c0a90e
to
df20522
Compare
I'm going to go ahead and merge this as I've resolved all outstanding comments, see no show-stopping issues with the KV interaction thanks to @nvanbenschoten's review, and have approval from @otan. Thanks for the reviews, all! bors r+ |
Build succeeded: |
Looks like this caused a panic in #76883 (comment) It seems related to using a DECLARE statement in the extended protocol. One thing I noticed is this condition: cockroach/pkg/sql/sql_cursor.go Line 33 in 96d102a
It probably is not what we want to be checking when in the extended protocol. |
Touches #41412.
This PR implements the SQL CURSOR methods DECLARE, FETCH and CLOSE using streaming internal executors.
Here's the Postgres docs on DECLARE: https://www.postgresql.org/docs/current/sql-declare.html
Things it implements:
Things it's missing:
SAVEPOINT
. Cursor definitions will not disappear if rolled back to a savepoint before they were created. But we also don't do this right for settings (see sql: SET does not properly rollback in a transaction #69396) so I don't think this is a major problem.