Skip to content

Commit cc4cc90

Browse files
craig[bot]stevendannaspilchen
committed
143272: kvclient: serve some DeleteRequest's from write buffer r=yuzefovich a=stevendanna In order to ensure that a DELETE returns the correct number of deleted rows, it must be integrated with the write buffer so that it observes any previous deletes or writes. Note that all DELETE statements are not yet handled since we don't yet have DeleteRange support. Epic: none Release note: None 143356: sql/schemachanger: support ALTER POLICY DDL r=spilchen a=spilchen Previously, attempts to execute ALTER POLICY DDL statements failed with an unimplemented error. This change enables support for ALTER POLICY by leveraging the existing policy-related scpb elements, which already accounted for alteration scenarios. Closes #136996 Closes #136997 Epic: CRDB-11724 Release note: none Co-authored-by: Steven Danna <[email protected]> Co-authored-by: Matt Spilchen <[email protected]>
3 parents 7d8f937 + d913fee + 098c75b commit cc4cc90

File tree

10 files changed

+979
-57
lines changed

10 files changed

+979
-57
lines changed

pkg/kv/kvclient/kvcoord/txn_interceptor_write_buffer.go

+33-8
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,15 @@ func (twb *txnWriteBuffer) applyTransformations(
396396
twb.addToBuffer(t.Key, t.Value, t.Sequence)
397397

398398
case *kvpb.DeleteRequest:
399+
// To correctly populate FoundKey in the response, we need to look in our
400+
// write buffer to see if there is a tombstone.
401+
var foundKey bool
402+
val, served := twb.maybeServeRead(t.Key, t.Sequence)
403+
if served {
404+
log.VEventf(ctx, 2, "serving read portion of %s on key %s from the buffer", t.Method(), t.Key)
405+
foundKey = val.IsPresent()
406+
}
407+
399408
// If MustAcquireExclusiveLock flag is set on the DeleteRequest, then we
400409
// need to add a locking Get to the BatchRequest, including if the key
401410
// doesn't exist.
@@ -414,10 +423,20 @@ func (twb *txnWriteBuffer) applyTransformations(
414423
baRemote.Requests = append(baRemote.Requests, getReqU)
415424
}
416425

426+
// If we found a key in our write buffer we use that
427+
// result regardless of what the GetResponse that we
428+
// might have sent says.
429+
//
430+
// NOTE(ssd): We are assuming that callers who care
431+
// about an accurate value of FoundKey also set
432+
// MustAcquireExclusiveLock.
417433
var ru kvpb.ResponseUnion
418-
ru.MustSetInner(&kvpb.DeleteResponse{
419-
FoundKey: false,
420-
})
434+
if served || !t.MustAcquireExclusiveLock {
435+
ru.MustSetInner(&kvpb.DeleteResponse{
436+
FoundKey: foundKey,
437+
})
438+
}
439+
421440
ts = append(ts, transformation{
422441
stripped: !t.MustAcquireExclusiveLock,
423442
index: i,
@@ -813,13 +832,19 @@ func (t transformation) toResp(
813832
ru = t.resp
814833

815834
case *kvpb.DeleteRequest:
816-
getResp := br.GetInner().(*kvpb.GetResponse)
817835
ru = t.resp
818-
if log.ExpensiveLogEnabled(ctx, 2) {
819-
log.Eventf(ctx, "synthesizing DeleteResponse from GetResponse: %#v", getResp)
836+
// If the deletion response is already set, it means we served response from
837+
// the write buffer. We can still be here because we happened to need to
838+
// send a GetRequest solely for the locking behaviour.
839+
if ru.GetDelete() == nil {
840+
getResp := br.GetInner().(*kvpb.GetResponse)
841+
if log.ExpensiveLogEnabled(ctx, 2) {
842+
log.Eventf(ctx, "synthesizing DeleteResponse from GetResponse: %#v", getResp)
843+
}
844+
ru.MustSetInner(&kvpb.DeleteResponse{
845+
FoundKey: getResp.Value.IsPresent(),
846+
})
820847
}
821-
ru.GetDelete().FoundKey = getResp.Value.IsPresent()
822-
823848
case *kvpb.GetRequest:
824849
// Get requests must be served from the local buffer if a transaction
825850
// performed a previous write to the key being read. However, Get requests

pkg/kv/kvpb/api.proto

+5-1
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,11 @@ message DeleteRequest {
408408
RequestHeader header = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
409409
// MustAcquireExclusiveLock, if set, indicates that a lock with the strength
410410
// no lower than "exclusive" needs to be acquired on the key, even if it
411-
// doesn't exist.
411+
// doesn't exist. Note that when MustAcquireExclusiveLock is false, the
412+
// FoundKey field in the DeleteResponse may be incorrect when the kvclient has
413+
// been configured to buffer writes.
414+
//
415+
// TODO(ssd): Separate the behaviour of FoundKey to not depend on this flag.
412416
bool must_acquire_exclusive_lock = 2;
413417
}
414418

pkg/sql/logictest/logic.go

+17
Original file line numberDiff line numberDiff line change
@@ -2092,6 +2092,21 @@ func (c clusterOptIgnoreStrictGCForTenants) apply(args *base.TestServerArgs) {
20922092
args.Knobs.Store.(*kvserver.StoreTestingKnobs).IgnoreStrictGCEnforcement = true
20932093
}
20942094

2095+
// clusterOptDisableUseMVCCRangeTombstonesForPointDeletes corresponds
2096+
// to the disable-mvcc-range-tombstones-for-point-deletes directive.
2097+
type clusterOptDisableUseMVCCRangeTombstonesForPointDeletes struct{}
2098+
2099+
var _ clusterOpt = clusterOptDisableUseMVCCRangeTombstonesForPointDeletes{}
2100+
2101+
// apply implements the clusterOpt interface.
2102+
func (c clusterOptDisableUseMVCCRangeTombstonesForPointDeletes) apply(args *base.TestServerArgs) {
2103+
_, ok := args.Knobs.Store.(*kvserver.StoreTestingKnobs)
2104+
if !ok {
2105+
args.Knobs.Store = &kvserver.StoreTestingKnobs{}
2106+
}
2107+
args.Knobs.Store.(*kvserver.StoreTestingKnobs).EvalKnobs.UseRangeTombstonesForPointDeletes = false
2108+
}
2109+
20952110
// knobOptDisableCorpusGeneration disables corpus generation for declarative
20962111
// schema changer.
20972112
type knobOptDisableCorpusGeneration struct{}
@@ -2236,6 +2251,8 @@ func readClusterOptions(t *testing.T, path string) []clusterOpt {
22362251
res = append(res, clusterOptTracingOff{})
22372252
case "ignore-tenant-strict-gc-enforcement":
22382253
res = append(res, clusterOptIgnoreStrictGCForTenants{})
2254+
case "disable-mvcc-range-tombstones-for-point-deletes":
2255+
res = append(res, clusterOptDisableUseMVCCRangeTombstonesForPointDeletes{})
22392256
default:
22402257
t.Fatalf("unrecognized cluster option: %s", opt)
22412258
}

pkg/sql/logictest/testdata/logic_test/buffered_writes

+42-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
2-
subtest point_delete
1+
# cluster-opt: disable-mvcc-range-tombstones-for-point-deletes
32

43
statement ok
54
SET kv_transaction_buffered_writes_enabled=true
65

76
statement ok
8-
CREATE TABLE t1 (pk int primary key, v int)
7+
CREATE TABLE t1 (pk int primary key, v int, FAMILY (pk, v))
8+
9+
subtest point_delete
910

1011
statement ok
1112
INSERT INTO t1 VALUES (1,1)
@@ -21,3 +22,41 @@ DELETE FROM t1 WHERE pk = 3
2122

2223
statement ok
2324
COMMIT
25+
26+
subtest repeated_point_delete
27+
28+
statement ok
29+
INSERT INTO t1 VALUES (1,1)
30+
31+
statement ok
32+
BEGIN
33+
34+
statement count 1
35+
DELETE FROM t1 WHERE pk = 1
36+
37+
# The second delete should be served from the write buffer and observe
38+
# the buffered tombstone.
39+
statement count 0
40+
DELETE FROM t1 WHERE pk = 1
41+
42+
statement ok
43+
COMMIT
44+
45+
subtest point_delete_after_write
46+
47+
statement ok
48+
BEGIN
49+
50+
statement ok
51+
INSERT INTO t1 VALUES (1,1)
52+
53+
statement count 1
54+
DELETE FROM t1 WHERE pk = 1
55+
56+
# The second delete should be served from the write buffer and observe
57+
# the buffered tombstone.
58+
statement count 0
59+
DELETE FROM t1 WHERE pk = 1
60+
61+
statement ok
62+
COMMIT

pkg/sql/logictest/testdata/logic_test/row_level_security

+128-19
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,124 @@ DROP FUNCTION my_non_sec_definer_reader_function;
12801280
statement ok
12811281
DROP TABLE sensitive_data_table CASCADE;
12821282

1283+
subtest alter_policy
1284+
1285+
statement ok
1286+
CREATE TABLE alter_policy_table (c1 INT NOT NULL PRIMARY KEY, c2 TEXT, FAMILY (c1, c2));
1287+
1288+
statement ok
1289+
ALTER TABLE alter_policy_table ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY;
1290+
1291+
statement ok
1292+
CREATE ROLE alter_policy_role;
1293+
1294+
statement ok
1295+
CREATE ROLE aux1;
1296+
1297+
statement ok
1298+
CREATE USER aux2;
1299+
1300+
statement ok
1301+
CREATE SEQUENCE seq1;
1302+
1303+
statement ok
1304+
GRANT ALL ON seq1 TO alter_policy_role;
1305+
1306+
statement ok
1307+
ALTER TABLE alter_policy_table OWNER TO alter_policy_role;
1308+
1309+
statement ok
1310+
SET ROLE alter_policy_role;
1311+
1312+
statement ok
1313+
CREATE POLICY p ON alter_policy_table FOR INSERT WITH CHECK (false);
1314+
1315+
statement error pq: new row violates row-level security policy for table "alter_policy_table"
1316+
INSERT INTO alter_policy_table VALUES (1, 'one'), (2, 'two'), (3, 'three');
1317+
1318+
statement error pq: only WITH CHECK expression allowed for INSERT
1319+
ALTER POLICY p ON alter_policy_table USING (true);
1320+
1321+
statement ok
1322+
ALTER POLICY p ON alter_policy_table WITH CHECK (nextval('seq1') < 10000);
1323+
1324+
statement ok
1325+
INSERT INTO alter_policy_table VALUES (1, 'one'), (2, 'two'), (3, 'three');
1326+
1327+
query I
1328+
SELECT c1 FROM alter_policy_table ORDER BY c1;
1329+
----
1330+
1331+
statement ok
1332+
ALTER POLICY p ON alter_policy_table RENAME TO p_ins;
1333+
1334+
statement ok
1335+
CREATE POLICY p ON alter_policy_table FOR SELECT TO aux1 USING (c1 > 0);
1336+
1337+
query I
1338+
SELECT c1 FROM alter_policy_table ORDER BY c1;
1339+
----
1340+
1341+
statement error pq: policy "p_sel" for table "alter_policy_table" does not exist
1342+
ALTER POLICY p_sel ON alter_policy_table WITH CHECK (true);
1343+
1344+
statement error pq: WITH CHECK cannot be applied to SELECT or DELETE
1345+
ALTER POLICY p ON alter_policy_table WITH CHECK (true);
1346+
1347+
statement ok
1348+
ALTER POLICY p ON alter_policy_table TO alter_policy_role,aux1,aux2 USING (c1 != 1);
1349+
1350+
query I
1351+
SELECT c1 FROM alter_policy_table ORDER BY c1;
1352+
----
1353+
2
1354+
3
1355+
1356+
statement ok
1357+
ALTER POLICY p ON alter_policy_table RENAME TO p_sel;
1358+
1359+
query TT
1360+
SHOW CREATE TABLE alter_policy_table;
1361+
----
1362+
alter_policy_table CREATE TABLE public.alter_policy_table (
1363+
c1 INT8 NOT NULL,
1364+
c2 STRING NULL,
1365+
CONSTRAINT alter_policy_table_pkey PRIMARY KEY (c1 ASC),
1366+
FAMILY fam_0_c1_c2 (c1, c2)
1367+
);
1368+
ALTER TABLE public.alter_policy_table ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY;
1369+
CREATE POLICY p_ins ON public.alter_policy_table AS PERMISSIVE FOR INSERT TO public WITH CHECK (nextval('public.seq1'::REGCLASS) < 10000:::INT8);
1370+
CREATE POLICY p_sel ON public.alter_policy_table AS PERMISSIVE FOR SELECT TO aux1, alter_policy_role, aux2 USING (c1 != 1:::INT8)
1371+
1372+
# TODO(143358): Include roles in the SHOW POLICIES output.
1373+
query TTTTT colnames
1374+
SELECT name,cmd,type,using_expr,with_check_expr
1375+
FROM [SHOW POLICIES FOR alter_policy_table]
1376+
ORDER BY name DESC;
1377+
----
1378+
name cmd type using_expr with_check_expr
1379+
p_sel SELECT permissive c1 != 1:::INT8 ·
1380+
p_ins INSERT permissive · nextval('public.seq1'::REGCLASS) < 10000:::INT8
1381+
1382+
statement ok
1383+
SET ROLE root;
1384+
1385+
statement error pq: cannot drop sequence seq1 because other objects depend on it
1386+
DROP SEQUENCE seq1;
1387+
1388+
# Change the policy so there isn't a dependency on seq1 anymore.
1389+
statement ok
1390+
ALTER POLICY p_ins ON alter_policy_table WITH CHECK (true);
1391+
1392+
statement ok
1393+
DROP SEQUENCE seq1;
1394+
1395+
statement ok
1396+
DROP TABLE alter_policy_table;
1397+
1398+
statement ok
1399+
DROP ROLE alter_policy_role, aux1, aux2;
1400+
12831401
# Verify that you need to be the table owner to do any of the RLS DDLs
12841402
subtest table_owner_and_rls_ddl
12851403

@@ -1313,14 +1431,14 @@ DROP POLICY p1 on table_owner_test;
13131431
statement ok
13141432
CREATE POLICY new_p1 on table_owner_test;
13151433

1316-
statement error pq: unimplemented: ALTER POLICY is not yet implemented
1434+
statement ok
13171435
ALTER POLICY new_p1 on table_owner_test RENAME TO p1;
13181436

1319-
statement error pq: unimplemented: ALTER POLICY is not yet implemented
1437+
statement ok
13201438
ALTER POLICY p1 on table_owner_test RENAME TO new_p1;
13211439

1322-
statement error pq: unimplemented: ALTER POLICY is not yet implemented
1323-
ALTER POLICY p1 on table_owner_test USING (true);
1440+
statement ok
1441+
ALTER POLICY new_p1 on table_owner_test USING (true);
13241442

13251443
statement ok
13261444
SET ROLE nontab_owner;
@@ -1337,10 +1455,10 @@ CREATE POLICY p2 on table_owner_test;
13371455
statement error pq: must be owner of relation table_owner_test
13381456
DROP POLICY new_p1 on table_owner_test;
13391457

1340-
statement error pq: unimplemented: ALTER POLICY is not yet implemented
1458+
statement error pq: must be owner of relation table_owner_test
13411459
ALTER POLICY new_p1 on table_owner_test WITH CHECK (true);
13421460

1343-
statement error pq: unimplemented: ALTER POLICY is not yet implemented
1461+
statement error pq: must be owner of relation table_owner_test
13441462
ALTER POLICY new_p1 on table_owner_test RENAME TO p1;
13451463

13461464
statement ok
@@ -2634,12 +2752,9 @@ query I
26342752
UPDATE cnt SET counter = counter + 1 RETURNING counter;
26352753
----
26362754

2637-
# Now replace the UPDATE policy with one that allows everything.
2755+
# Now alter the UPDATE policy with one that allows everything.
26382756
statement ok
2639-
DROP POLICY upd1 ON cnt;
2640-
2641-
statement ok
2642-
CREATE POLICY upd1 ON cnt FOR UPDATE USING (true);
2757+
ALTER POLICY upd1 ON cnt USING (true);
26432758

26442759
query I
26452760
UPDATE cnt SET counter = counter + 1 RETURNING counter;
@@ -2648,10 +2763,7 @@ UPDATE cnt SET counter = counter + 1 RETURNING counter;
26482763

26492764
# Update the UPDATE policy so that it allows old rows but blocks all new rows.
26502765
statement ok
2651-
DROP POLICY upd1 ON cnt;
2652-
2653-
statement ok
2654-
CREATE POLICY upd1 ON cnt FOR UPDATE USING (true) WITH CHECK (false);
2766+
ALTER POLICY upd1 ON cnt USING (true) WITH CHECK (false);
26552767

26562768
# We are able to read the row but cannot write a new row as it violates the
26572769
# update policy.
@@ -2673,10 +2785,7 @@ select counter from cnt;
26732785

26742786
# Now change the select policy to be always false, and delete policy to be always true.
26752787
statement ok
2676-
DROP POLICY sel1 ON cnt;
2677-
2678-
statement ok
2679-
CREATE POLICY sel1 ON cnt FOR SELECT USING (false);
2788+
ALTER POLICY sel1 ON cnt USING (false);
26802789

26812790
statement ok
26822791
CREATE POLICY del1 ON cnt FOR DELETE USING (true);

pkg/sql/schemachanger/dml_injection_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,30 @@ func TestAlterTableDMLInjection(t *testing.T) {
416416
setup: []string{"CREATE INDEX idx ON tbl (val) USING HASH"},
417417
schemaChange: "DROP INDEX idx",
418418
},
419+
{
420+
desc: "alter policy name",
421+
setup: []string{
422+
"SET enable_row_level_security=on",
423+
"CREATE POLICY p ON tbl FOR SELECT USING (val > 0)",
424+
"ALTER TABLE tbl ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY",
425+
"CREATE USER foo",
426+
"ALTER TABLE tbl OWNER TO foo",
427+
"SET ROLE foo",
428+
},
429+
schemaChange: "ALTER POLICY p ON tbl RENAME TO policy_1",
430+
},
431+
{
432+
desc: "alter policy using expression",
433+
setup: []string{
434+
"SET enable_row_level_security=on",
435+
"CREATE POLICY p ON tbl FOR SELECT USING (val > 0)",
436+
"ALTER TABLE tbl ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY",
437+
"CREATE USER foo",
438+
"ALTER TABLE tbl OWNER TO foo",
439+
"SET ROLE foo",
440+
},
441+
schemaChange: "ALTER POLICY p ON tbl USING (val > -10)",
442+
},
419443
{
420444
desc: "drop column with index using hash cascade",
421445
setup: []string{

0 commit comments

Comments
 (0)