Skip to content

Commit 04ba0b9

Browse files
craig[bot]jordanlewis
craig[bot]
andcommitted
Merge #40206
40206: sql: add row lock modes; make FOR UPDATE a no-op r=jordanlewis a=jordanlewis This commit adds parsing for the row locking modes: FOR UPDATE, FOR NO KEY UPDATE, FOR SHARE, FOR KEY UPDATE. All return unimplemented errors, except for FOR UPDATE, which behaves as a no-op. Closes #6583. Added #40205 to track the remaining kinds of row locking. Release note (sql change): support parsing the FOR UPDATE modifier on SELECT clauses, treating it as a no-op, since CockroachDB's transactions only operate in SERIALIZABLE mode. Co-authored-by: Jordan Lewis <[email protected]>
2 parents 2168fe8 + ac86fa0 commit 04ba0b9

File tree

9 files changed

+134
-24
lines changed

9 files changed

+134
-24
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
select_stmt ::=
2-
( select_clause ( sort_clause | ) ( limit_clause | ) ( offset_clause | ) | ( 'WITH' ( ( common_table_expr ) ( ( ',' common_table_expr ) )* ) ) select_clause ( sort_clause | ) ( limit_clause | ) ( offset_clause | ) )
2+
( simple_select opt_for | select_clause sort_clause opt_for | select_clause ( sort_clause | ) ( limit_clause offset_clause | offset_clause limit_clause | limit_clause | offset_clause ) opt_for | ( 'WITH' ( ( common_table_expr ) ( ( ',' common_table_expr ) )* ) ) select_clause opt_for | ( 'WITH' ( ( common_table_expr ) ( ( ',' common_table_expr ) )* ) ) select_clause sort_clause opt_for | ( 'WITH' ( ( common_table_expr ) ( ( ',' common_table_expr ) )* ) ) select_clause ( sort_clause | ) ( limit_clause offset_clause | offset_clause limit_clause | limit_clause | offset_clause ) opt_for )
33

docs/generated/sql/bnf/stmt_block.bnf

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -434,12 +434,12 @@ scrub_database_stmt ::=
434434
'EXPERIMENTAL' 'SCRUB' 'DATABASE' database_name opt_as_of_clause
435435

436436
select_no_parens ::=
437-
simple_select
438-
| select_clause sort_clause
439-
| select_clause opt_sort_clause select_limit
440-
| with_clause select_clause
441-
| with_clause select_clause sort_clause
442-
| with_clause select_clause opt_sort_clause select_limit
437+
simple_select opt_for
438+
| select_clause sort_clause opt_for
439+
| select_clause opt_sort_clause select_limit opt_for
440+
| with_clause select_clause opt_for
441+
| with_clause select_clause sort_clause opt_for
442+
| with_clause select_clause opt_sort_clause select_limit opt_for
443443

444444
select_with_parens ::=
445445
'(' select_no_parens ')'
@@ -788,6 +788,7 @@ unreserved_keyword ::=
788788
| 'SESSION'
789789
| 'SESSIONS'
790790
| 'SET'
791+
| 'SHARE'
791792
| 'SHOW'
792793
| 'SIMPLE'
793794
| 'SMALLSERIAL'
@@ -1142,6 +1143,12 @@ simple_select ::=
11421143
| table_clause
11431144
| set_operation
11441145

1146+
opt_for ::=
1147+
'FOR' 'UPDATE'
1148+
| 'FOR' 'NO' 'KEY' 'UPDATE'
1149+
| 'FOR' 'SHARE'
1150+
| 'FOR' 'KEY' 'SHARE'
1151+
11451152
select_clause ::=
11461153
simple_select
11471154
| select_with_parens
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# LogicTest: local
22

33
statement error unimplemented
4-
SELECT * FROM system.users FOR UPDATE
4+
SELECT 'a'::INTERVAL(123)
55

66
query TI colnames
77
SELECT *
88
FROM crdb_internal.feature_usage
9-
WHERE feature_name LIKE '%syntax.#6583%'
9+
WHERE feature_name LIKE '%syntax.#32564%'
1010
----
1111
feature_name usage_count
12-
unimplemented.syntax.#6583 1
12+
unimplemented.syntax.#32564 1
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Cockroach currently supports all of the row locking modes as no-ops, so just
2+
# test that they parse and run.
3+
query I
4+
SELECT 1 FOR UPDATE
5+
----
6+
1
7+
8+
query I
9+
SELECT 1 FOR NO KEY UPDATE
10+
----
11+
1
12+
13+
query I
14+
SELECT 1 FOR SHARE
15+
----
16+
1
17+
18+
query I
19+
SELECT 1 FOR KEY SHARE
20+
----
21+
1

pkg/sql/opt/optbuilder/select.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,20 @@ func (b *Builder) buildSelect(
676676
orderBy := stmt.OrderBy
677677
limit := stmt.Limit
678678
with := stmt.With
679+
forLocked := stmt.ForLocked
680+
681+
switch forLocked {
682+
case tree.ForNone:
683+
case tree.ForUpdate:
684+
case tree.ForNoKeyUpdate:
685+
case tree.ForShare:
686+
case tree.ForKeyShare:
687+
// CockroachDB treats all of the FOR LOCKED modes as no-ops. Since all
688+
// transactions are serializable in CockroachDB, clients can't observe
689+
// whether or not FOR UPDATE (or any of the other weaker modes) actually
690+
// created a lock. This behavior may improve as the transaction model gains
691+
// more capabilities.
692+
}
679693

680694
for s, ok := wrapped.(*tree.ParenSelect); ok; s, ok = wrapped.(*tree.ParenSelect) {
681695
stmt = s.Select

pkg/sql/parser/parse_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,11 @@ func TestParse(t *testing.T) {
10251025
{`SELECT (i.keys).*`},
10261026
{`SELECT (ARRAY['a', 'b', 'c']).name`},
10271027

1028+
{`SELECT 1 FOR UPDATE`},
1029+
{`SELECT 1 FOR NO KEY UPDATE`},
1030+
{`SELECT 1 FOR SHARE`},
1031+
{`SELECT 1 FOR KEY SHARE`},
1032+
10281033
{`TABLE a`}, // Shorthand for: SELECT * FROM a; used e.g. in CREATE VIEW v AS TABLE t
10291034
{`EXPLAIN TABLE a`},
10301035
{`TABLE [123 AS a]`},
@@ -3025,7 +3030,6 @@ func TestUnimplementedSyntax(t *testing.T) {
30253030
{`INSERT INTO foo(a, a.b) VALUES (1,2)`, 27792, ``},
30263031
{`INSERT INTO foo VALUES (1,2) ON CONFLICT ON CONSTRAINT a DO NOTHING`, 28161, ``},
30273032

3028-
{`SELECT * FROM a FOR UPDATE`, 6583, ``},
30293033
{`SELECT * FROM ROWS FROM (a(b) AS (d))`, 0, `ROWS FROM with col_def_list`},
30303034

30313035
{`SELECT 'a'::INTERVAL SECOND`, 0, `interval with unit qualifier`},

pkg/sql/parser/sql.y

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ func (u *sqlSymUnion) when() *tree.When {
290290
func (u *sqlSymUnion) whens() []*tree.When {
291291
return u.val.([]*tree.When)
292292
}
293+
func (u *sqlSymUnion) forLocked() tree.ForLocked {
294+
return u.val.(tree.ForLocked)
295+
}
293296
func (u *sqlSymUnion) updateExpr() *tree.UpdateExpr {
294297
return u.val.(*tree.UpdateExpr)
295298
}
@@ -554,7 +557,7 @@ func newNameFromStr(s string) *tree.Name {
554557
%token <str> SAVEPOINT SCATTER SCHEMA SCHEMAS SCRUB SEARCH SECOND SELECT SEQUENCE SEQUENCES
555558
%token <str> SERIAL SERIAL2 SERIAL4 SERIAL8
556559
%token <str> SERIALIZABLE SERVER SESSION SESSIONS SESSION_USER SET SETTING SETTINGS
557-
%token <str> SHOW SIMILAR SIMPLE SMALLINT SMALLSERIAL SNAPSHOT SOME SPLIT SQL
560+
%token <str> SHARE SHOW SIMILAR SIMPLE SMALLINT SMALLSERIAL SNAPSHOT SOME SPLIT SQL
558561

559562
%token <str> START STATISTICS STATUS STDIN STRICT STRING STORE STORED STORING SUBSTRING
560563
%token <str> SYMMETRIC SYNTAX SYSTEM SUBSCRIPTION
@@ -773,6 +776,7 @@ func newNameFromStr(s string) *tree.Name {
773776

774777
%type <*tree.Select> select_no_parens
775778
%type <tree.SelectStatement> select_clause select_with_parens simple_select values_clause table_clause simple_select_clause
779+
%type <tree.ForLocked> opt_for
776780
%type <tree.SelectStatement> set_operation
777781

778782
%type <tree.Expr> alter_column_default
@@ -5750,32 +5754,35 @@ select_with_parens:
57505754
select_no_parens:
57515755
simple_select opt_for
57525756
{
5753-
$$.val = &tree.Select{Select: $1.selectStmt()}
5757+
$$.val = &tree.Select{Select: $1.selectStmt(), ForLocked: $2.forLocked()}
57545758
}
57555759
| select_clause sort_clause opt_for
57565760
{
5757-
$$.val = &tree.Select{Select: $1.selectStmt(), OrderBy: $2.orderBy()}
5761+
$$.val = &tree.Select{Select: $1.selectStmt(), OrderBy: $2.orderBy(), ForLocked: $3.forLocked()}
57585762
}
57595763
| select_clause opt_sort_clause select_limit opt_for
57605764
{
5761-
$$.val = &tree.Select{Select: $1.selectStmt(), OrderBy: $2.orderBy(), Limit: $3.limit()}
5765+
$$.val = &tree.Select{Select: $1.selectStmt(), OrderBy: $2.orderBy(), Limit: $3.limit(), ForLocked: $4.forLocked()}
57625766
}
57635767
| with_clause select_clause opt_for
57645768
{
5765-
$$.val = &tree.Select{With: $1.with(), Select: $2.selectStmt()}
5769+
$$.val = &tree.Select{With: $1.with(), Select: $2.selectStmt(), ForLocked: $3.forLocked()}
57665770
}
57675771
| with_clause select_clause sort_clause opt_for
57685772
{
5769-
$$.val = &tree.Select{With: $1.with(), Select: $2.selectStmt(), OrderBy: $3.orderBy()}
5773+
$$.val = &tree.Select{With: $1.with(), Select: $2.selectStmt(), OrderBy: $3.orderBy(), ForLocked: $4.forLocked()}
57705774
}
57715775
| with_clause select_clause opt_sort_clause select_limit opt_for
57725776
{
5773-
$$.val = &tree.Select{With: $1.with(), Select: $2.selectStmt(), OrderBy: $3.orderBy(), Limit: $4.limit()}
5777+
$$.val = &tree.Select{With: $1.with(), Select: $2.selectStmt(), OrderBy: $3.orderBy(), Limit: $4.limit(), ForLocked: $5.forLocked()}
57745778
}
57755779

57765780
opt_for:
5777-
/* EMPTY */ { /* no error */ }
5778-
| FOR error { return unimplementedWithIssue(sqllex, 6583) }
5781+
/* EMPTY */ { $$.val = tree.ForNone }
5782+
| FOR UPDATE { $$.val = tree.ForUpdate }
5783+
| FOR NO KEY UPDATE { $$.val = tree.ForNoKeyUpdate }
5784+
| FOR SHARE { $$.val = tree.ForShare }
5785+
| FOR KEY SHARE { $$.val = tree.ForKeyShare }
57795786

57805787
select_clause:
57815788
// We only provide help if an open parenthesis is provided, because
@@ -9351,6 +9358,7 @@ unreserved_keyword:
93519358
| SESSION
93529359
| SESSIONS
93539360
| SET
9361+
| SHARE
93549362
| SHOW
93559363
| SIMPLE
93569364
| SMALLSERIAL

pkg/sql/sem/tree/pretty.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,9 +538,31 @@ func (node *Select) docTable(p *PrettyCfg) []pretty.TableRow {
538538
}
539539
items = append(items, node.OrderBy.docRow(p))
540540
items = append(items, node.Limit.docTable(p)...)
541+
items = append(items, node.ForLocked.docTable(p)...)
541542
return items
542543
}
543544

545+
func (node ForLocked) doc(p *PrettyCfg) pretty.Doc {
546+
return p.rlTable(node.docTable(p)...)
547+
}
548+
549+
func (node ForLocked) docTable(p *PrettyCfg) []pretty.TableRow {
550+
var keyword string
551+
switch node {
552+
case ForNone:
553+
return nil
554+
case ForUpdate:
555+
keyword = "FOR UPDATE"
556+
case ForNoKeyUpdate:
557+
keyword = "FOR NO KEY UPDATE"
558+
case ForShare:
559+
keyword = "FOR SHARE"
560+
case ForKeyShare:
561+
keyword = "FOR KEY SHARE"
562+
}
563+
return []pretty.TableRow{p.row("", pretty.Keyword(keyword))}
564+
}
565+
544566
func (node *SelectClause) doc(p *PrettyCfg) pretty.Doc {
545567
return p.rlTable(node.docTable(p)...)
546568
}

pkg/sql/sem/tree/select.go

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,43 @@ func (*ValuesClause) selectStatement() {}
4141

4242
// Select represents a SelectStatement with an ORDER and/or LIMIT.
4343
type Select struct {
44-
With *With
45-
Select SelectStatement
46-
OrderBy OrderBy
47-
Limit *Limit
44+
With *With
45+
Select SelectStatement
46+
OrderBy OrderBy
47+
Limit *Limit
48+
ForLocked ForLocked
49+
}
50+
51+
// ForLocked represents the possible row-level lock modes for a SELECT
52+
// statement.
53+
type ForLocked byte
54+
55+
const (
56+
// ForNone represents the default - no for statement at all.
57+
ForNone ForLocked = iota
58+
// ForUpdate represents FOR UPDATE.
59+
ForUpdate
60+
// ForNoKeyUpdate represents FOR NO KEY UPDATE.
61+
ForNoKeyUpdate
62+
// ForShare represents FOR SHARE.
63+
ForShare
64+
// ForKeyShare represents FOR KEY SHARE.
65+
ForKeyShare
66+
)
67+
68+
// Format implements the NodeFormatter interface.
69+
func (f ForLocked) Format(ctx *FmtCtx) {
70+
switch f {
71+
case ForNone:
72+
case ForUpdate:
73+
ctx.WriteString(" FOR UPDATE")
74+
case ForNoKeyUpdate:
75+
ctx.WriteString(" FOR NO KEY UPDATE")
76+
case ForShare:
77+
ctx.WriteString(" FOR SHARE")
78+
case ForKeyShare:
79+
ctx.WriteString(" FOR KEY SHARE")
80+
}
4881
}
4982

5083
// Format implements the NodeFormatter interface.
@@ -59,6 +92,7 @@ func (node *Select) Format(ctx *FmtCtx) {
5992
ctx.WriteByte(' ')
6093
ctx.FormatNode(node.Limit)
6194
}
95+
ctx.FormatNode(node.ForLocked)
6296
}
6397

6498
// ParenSelect represents a parenthesized SELECT/UNION/VALUES statement.

0 commit comments

Comments
 (0)