Skip to content

Commit c0934ae

Browse files
craig[bot]fqazispilchendhartunian
committed
150952: changefeedccl: fix incorrect previous values for dropped columns r=fqazi a=fqazi Previously, when we adjusted when the backfill was emitted for the drop column under the declarative schema changer when schema_locked was active, we wound up emitting the previous values incorrectly. What this meant was that in the Avro format the old and new values would be the same when a column was being dropped. This was observed by the cdc/schemareg test. To address this, this patch correctly resolves the previous values in the drop column case from the last descriptor version. Fixes: #150540 Release note: None 151045: sql/catalog: fix policy dependency handling during backup/restore r=spilchen a=spilchen Previously, when performing table level restores, policies with missing dependencies (UDFs, types, or relations) were not handled correctly. Unlike triggers, which are dropped when their dependencies are missing, policies would remain in an inconsistent state, potentially causing restore failures or leaving dangling references. This change extends the existing trigger dependency handling logic to also cover RLS policies. When a policy depends on objects that aren't being restored (e.g., a UDF referenced in a USING clause), the policy is now properly dropped. The implementation also ensures that back-references are cleaned up appropriately, preventing dangling references that could interfere with subsequent operations like dropping sequences or types. Additionally, the schema changer state rewriting logic is enhanced to handle missing dependencies for policy expressions (PolicyUsingExpr, PolicyWithCheckExpr, PolicyDeps) during restore operations. Fixes #151042. Release note (bug fix): Fixed an issue where Row Level Security policies with missing dependencies during table-level restores could cause inconsistent state or restore failures. Epic: None 151487: server: fix potential nil pointer when starting flight recorder r=yuzefovich a=dhartunian When the flightt recorder constructor returns an error in `NewFlightRecorder`, we should make sure not to call `Start`. Epic: None Release note: None Co-authored-by: Faizan Qazi <[email protected]> Co-authored-by: Matt Spilchen <[email protected]> Co-authored-by: David Hartunian <[email protected]>
4 parents 0e8321e + b71b2d1 + 571e8b6 + f784eac commit c0934ae

File tree

9 files changed

+732
-61
lines changed

9 files changed

+732
-61
lines changed
Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
# Test backing up and restoring tables and databases with policies that have
2+
# dependencies on functions, types, and sequences.
3+
new-cluster name=s
4+
----
5+
6+
exec-sql
7+
CREATE DATABASE testdb;
8+
----
9+
10+
exec-sql
11+
USE testdb;
12+
----
13+
14+
# ==============================================================================
15+
# Test 1: Cross-table dependencies (types and functions)
16+
# ==============================================================================
17+
18+
# Create the dependencies: types and functions
19+
exec-sql
20+
CREATE TYPE status AS ENUM ('active', 'inactive');
21+
----
22+
23+
exec-sql
24+
CREATE TABLE users (id INT, status string);
25+
----
26+
27+
exec-sql
28+
CREATE FUNCTION f1() RETURNS INT LANGUAGE PLpgSQL AS $$
29+
BEGIN
30+
RETURN 1;
31+
END
32+
$$;
33+
----
34+
35+
# Create policy with both type and function dependencies
36+
exec-sql
37+
CREATE POLICY cross_table_policy ON users
38+
FOR SELECT
39+
USING (status::status = 'active'::status and f1() = 1);
40+
----
41+
42+
# Backup everything
43+
exec-sql
44+
BACKUP DATABASE testdb INTO 'nodelocal://1/backup_directory';
45+
----
46+
47+
# Test 1a: Restore table to a new database with skip_missing_udfs
48+
exec-sql
49+
CREATE DATABASE newdb;
50+
----
51+
52+
exec-sql
53+
RESTORE TABLE users
54+
FROM LATEST IN 'nodelocal://1/backup_directory'
55+
WITH into_db = 'newdb', skip_missing_udfs;
56+
----
57+
58+
exec-sql
59+
USE newdb;
60+
----
61+
62+
# The table should be restored without the policy since type and function dependencies are missing
63+
query-sql
64+
SHOW POLICIES FOR users;
65+
----
66+
67+
# Test 1b: Drop original dependencies and restore with skip_missing_udfs in same database
68+
exec-sql
69+
USE testdb;
70+
----
71+
72+
exec-sql
73+
DROP TABLE users;
74+
----
75+
76+
exec-sql
77+
DROP TYPE status;
78+
----
79+
80+
exec-sql
81+
RESTORE TABLE users
82+
FROM LATEST IN 'nodelocal://1/backup_directory'
83+
WITH skip_missing_udfs;
84+
----
85+
86+
# Verify the table was restored without the policy
87+
query-sql
88+
SHOW POLICIES FOR users;
89+
----
90+
91+
# Verify we can manipulate the table now that policies are gone
92+
exec-sql
93+
ALTER TABLE users RENAME TO users_no_policy;
94+
----
95+
96+
exec-sql
97+
INSERT INTO users_no_policy (id, status) VALUES (1, 'active');
98+
----
99+
100+
query-sql
101+
SELECT * FROM users_no_policy;
102+
----
103+
1 active
104+
105+
# Clean up for next test
106+
exec-sql
107+
DROP TABLE users_no_policy;
108+
----
109+
110+
exec-sql
111+
DROP FUNCTION f1;
112+
----
113+
114+
exec-sql
115+
DROP DATABASE newdb;
116+
----
117+
118+
# ==============================================================================
119+
# Test 2: Comprehensive dependencies (functions, types, and sequences)
120+
# ==============================================================================
121+
122+
# Set up the dependencies: functions, types, and sequences
123+
exec-sql
124+
CREATE FUNCTION is_admin(role text) RETURNS BOOL LANGUAGE SQL AS $$ SELECT role = 'admin' $$;
125+
----
126+
127+
exec-sql
128+
CREATE TYPE user_role AS ENUM ('admin', 'user', 'guest');
129+
----
130+
131+
exec-sql
132+
CREATE SEQUENCE user_counter;
133+
----
134+
135+
exec-sql
136+
CREATE FUNCTION get_next_id() RETURNS INT LANGUAGE SQL AS $$ SELECT nextval('user_counter') $$;
137+
----
138+
139+
# Create the table with dependencies
140+
exec-sql
141+
CREATE TABLE accounts (id INT PRIMARY KEY DEFAULT get_next_id(), name TEXT, role user_role DEFAULT 'user');
142+
----
143+
144+
exec-sql
145+
ALTER TABLE accounts ENABLE ROW LEVEL SECURITY;
146+
----
147+
148+
# Create the policy with dependencies on functions, types, and sequences
149+
exec-sql
150+
CREATE POLICY admin_policy ON accounts
151+
FOR ALL
152+
USING (is_admin(role::text))
153+
WITH CHECK (role = 'admin'::user_role AND id > 0 AND nextval('user_counter') < 10000);
154+
----
155+
156+
# Verify the policy was created
157+
query-sql
158+
SHOW POLICIES FOR accounts;
159+
----
160+
admin_policy ALL permissive {public} public.is_admin("role"::STRING) (("role" = 'admin':::public.user_role) AND (id > 0:::INT8)) AND (nextval('public.user_counter'::REGCLASS) < 10000:::INT8)
161+
162+
# Test data insertion
163+
exec-sql
164+
INSERT INTO accounts (name, role) VALUES ('test_admin', 'admin'::user_role);
165+
----
166+
167+
exec-sql
168+
INSERT INTO accounts (name, role) VALUES ('test_user', 'user'::user_role);
169+
----
170+
171+
# Backup the database again
172+
exec-sql
173+
BACKUP DATABASE testdb INTO 'nodelocal://1/test/';
174+
----
175+
176+
# Verify what was backed up
177+
query-sql
178+
WITH descs AS (
179+
SHOW BACKUP LATEST IN 'nodelocal://1/test/'
180+
)
181+
SELECT database_name, parent_schema_name, object_name, object_type, is_full_cluster FROM descs
182+
WHERE object_name NOT LIKE '%users%' OR object_name IS NULL
183+
ORDER BY database_name, parent_schema_name, object_name
184+
----
185+
<nil> <nil> testdb database false
186+
testdb <nil> public schema false
187+
testdb public _status type false
188+
testdb public _user_role type false
189+
testdb public accounts table false
190+
testdb public get_next_id function false
191+
testdb public is_admin function false
192+
testdb public status type false
193+
testdb public user_counter table false
194+
testdb public user_role type false
195+
196+
# Test 2a: Full database restore with new name
197+
exec-sql
198+
RESTORE DATABASE testdb FROM LATEST IN 'nodelocal://1/test/' WITH new_db_name = testdb_full;
199+
----
200+
201+
exec-sql
202+
USE testdb_full;
203+
----
204+
205+
# Verify the policy was restored correctly
206+
query-sql
207+
SHOW POLICIES FOR accounts;
208+
----
209+
admin_policy ALL permissive {public} public.is_admin("role"::STRING) (("role" = 'admin':::public.user_role) AND (id > 0:::INT8)) AND (nextval('public.user_counter'::REGCLASS) < 10000:::INT8)
210+
211+
# Test that dependencies are correctly maintained - should fail to drop due to policy dependencies
212+
exec-sql
213+
DROP FUNCTION is_admin
214+
----
215+
pq: cannot drop function "is_admin" because other objects ([testdb_full.public.accounts]) still depend on it
216+
217+
exec-sql
218+
DROP TYPE user_role
219+
----
220+
pq: cannot drop type "user_role" because other objects ([testdb_full.public.accounts]) still depend on it
221+
222+
exec-sql
223+
DROP SEQUENCE user_counter
224+
----
225+
pq: cannot drop sequence user_counter because other objects depend on it
226+
227+
# Test that the policy still works after restore
228+
exec-sql
229+
INSERT INTO accounts (name, role) VALUES ('restored_admin', 'admin'::user_role);
230+
----
231+
232+
exec-sql
233+
INSERT INTO accounts (name, role) VALUES ('restored_user', 'user'::user_role);
234+
----
235+
236+
# Verify data was restored and new data can be inserted
237+
query-sql
238+
SELECT count(*) FROM accounts;
239+
----
240+
4
241+
242+
# Test 2b: Partial restore with missing dependencies
243+
exec-sql
244+
USE testdb;
245+
----
246+
247+
exec-sql
248+
CREATE DATABASE db2;
249+
----
250+
251+
# Restore with skip_missing_udfs - the table should be restored without the policy
252+
exec-sql
253+
RESTORE TABLE accounts, user_counter FROM LATEST IN 'nodelocal://1/test/' WITH into_db = 'db2', skip_missing_udfs;
254+
----
255+
256+
exec-sql
257+
USE db2;
258+
----
259+
260+
# The table should be restored without the policy since dependencies are missing
261+
query-sql
262+
SHOW POLICIES FOR accounts;
263+
----
264+
265+
266+
exec-sql
267+
DROP TABLE accounts;
268+
----
269+
270+
271+
exec-sql
272+
DROP SEQUENCE user_counter;
273+
----
274+
275+
276+
# ==============================================================================
277+
# Test 3: Sequence dependency verification after policy removal
278+
# ==============================================================================
279+
280+
exec-sql
281+
USE testdb;
282+
----
283+
284+
exec-sql
285+
CREATE SEQUENCE counter;
286+
----
287+
288+
exec-sql
289+
CREATE TABLE sequence_test (id INT, status string);
290+
----
291+
292+
exec-sql
293+
CREATE FUNCTION f2() RETURNS INT LANGUAGE PLpgSQL AS $$
294+
BEGIN
295+
RETURN 1;
296+
END
297+
$$;
298+
----
299+
300+
# Create policy with both sequence and function dependencies
301+
exec-sql
302+
CREATE POLICY sequence_policy ON sequence_test
303+
FOR SELECT
304+
USING (nextval('counter') < 10000 and f2() = 1);
305+
----
306+
307+
# Backup the database with the new objects
308+
exec-sql
309+
BACKUP DATABASE testdb INTO 'nodelocal://1/sequence_test/';
310+
----
311+
312+
exec-sql
313+
CREATE DATABASE seqdb;
314+
----
315+
316+
# Restore table and sequence but not the function - policy should be dropped
317+
exec-sql
318+
RESTORE TABLE sequence_test, counter
319+
FROM LATEST IN 'nodelocal://1/sequence_test/'
320+
WITH into_db = 'seqdb', skip_missing_udfs;
321+
----
322+
323+
exec-sql
324+
USE seqdb;
325+
----
326+
327+
# Verify the policy was not restored
328+
query-sql
329+
SHOW POLICIES FOR sequence_test;
330+
----
331+
332+
# Verify the sequence exists
333+
query-sql
334+
SELECT 'counter'::REGCLASS::OID > 0 as sequence_exists;
335+
----
336+
true
337+
338+
# The sequence should be droppable because there are no dependencies from the table
339+
# since the policy was dropped due to missing function dependency
340+
exec-sql
341+
DROP SEQUENCE counter;
342+
----
343+
344+
exec-sql
345+
DROP TABLE sequence_test;
346+
----
347+
348+
# Clean up
349+
exec-sql
350+
DROP DATABASE testdb;
351+
----
352+
353+
exec-sql
354+
DROP DATABASE testdb_full;
355+
----
356+
357+
exec-sql
358+
DROP DATABASE db2;
359+
----
360+
361+
exec-sql
362+
DROP DATABASE seqdb;
363+
----

pkg/ccl/changefeedccl/cdcevent/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ go_library(
3030
"//pkg/sql/pgwire/pgerror",
3131
"//pkg/sql/row",
3232
"//pkg/sql/rowenc",
33+
"//pkg/sql/sem/catid",
3334
"//pkg/sql/sem/eval",
3435
"//pkg/sql/sem/tree",
3536
"//pkg/sql/sessiondatapb",

0 commit comments

Comments
 (0)