|
| 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 | +---- |
0 commit comments