Skip to content

Commit 0303051

Browse files
committed
Add more infinite recursion detection while locking a view.
Also add regression test cases for detecting infinite recursion in locking view tests. Some document enhancements. Patch by Yugo Nagata.
1 parent 47c91b5 commit 0303051

File tree

4 files changed

+63
-31
lines changed

4 files changed

+63
-31
lines changed

doc/src/sgml/ref/lock.sgml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ LOCK [ TABLE ] [ ONLY ] <replaceable class="parameter">name</replaceable> [ * ]
4646
</para>
4747

4848
<para>
49-
When a view is specified to be locked, all relations appearing in the view
50-
definition query are also locked recursively with the same lock mode.
49+
When a view is locked, all relations appearing in the view definition
50+
query are also locked recursively with the same lock mode.
5151
</para>
5252

5353
<para>
@@ -173,6 +173,13 @@ LOCK [ TABLE ] [ ONLY ] <replaceable class="parameter">name</replaceable> [ * ]
173173
or <literal>TRUNCATE</literal> privileges.
174174
</para>
175175

176+
<para>
177+
The user performing the lock on the view must have the corresponding privilege
178+
on the view. In addition the view's owner must have the relevant privileges on
179+
the underlying base relations, but the user performing the lock does
180+
not need any permissions on the underlying base relations.
181+
</para>
182+
176183
<para>
177184
<command>LOCK TABLE</command> is useless outside a transaction block: the lock
178185
would remain held only to the completion of the statement. Therefore

src/backend/commands/lockcmds.c

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid use
3131
static AclResult LockTableAclCheck(Oid relid, LOCKMODE lockmode, Oid userid);
3232
static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid,
3333
Oid oldrelid, void *arg);
34-
static void LockViewRecurse(Oid reloid, Oid root_reloid, LOCKMODE lockmode, bool nowait);
34+
static void LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views);
3535

3636
/*
3737
* LOCK TABLE
@@ -67,7 +67,7 @@ LockTableCommand(LockStmt *lockstmt)
6767
(void *) &lockstmt->mode);
6868

6969
if (get_rel_relkind(reloid) == RELKIND_VIEW)
70-
LockViewRecurse(reloid, reloid, lockstmt->mode, lockstmt->nowait);
70+
LockViewRecurse(reloid, lockstmt->mode, lockstmt->nowait, NIL);
7171
else if (recurse)
7272
LockTableRecurse(reloid, lockstmt->mode, lockstmt->nowait, GetUserId());
7373
}
@@ -92,7 +92,6 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
9292
return; /* woops, concurrently dropped; no permissions
9393
* check */
9494

95-
9695
/* Currently, we only allow plain tables or views to be locked */
9796
if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE &&
9897
relkind != RELKIND_VIEW)
@@ -178,11 +177,11 @@ LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid userid)
178177

179178
typedef struct
180179
{
181-
Oid root_reloid;
182-
LOCKMODE lockmode;
183-
bool nowait;
184-
Oid viewowner;
185-
Oid viewoid;
180+
LOCKMODE lockmode; /* lock mode to use */
181+
bool nowait; /* no wait mode */
182+
Oid viewowner; /* view owner for checking the privilege */
183+
Oid viewoid; /* OID of the view to be locked */
184+
List *ancestor_views; /* OIDs of ancestor views */
186185
} LockViewRecurse_context;
187186

188187
static bool
@@ -193,19 +192,22 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
193192

194193
if (IsA(node, Query))
195194
{
196-
Query *query = (Query *) node;
197-
ListCell *rtable;
195+
Query *query = (Query *) node;
196+
ListCell *rtable;
198197

199198
foreach(rtable, query->rtable)
200199
{
201-
RangeTblEntry *rte = lfirst(rtable);
202-
AclResult aclresult;
200+
RangeTblEntry *rte = lfirst(rtable);
201+
AclResult aclresult;
203202

204-
Oid relid = rte->relid;
205-
char relkind = rte->relkind;
206-
char *relname = get_rel_name(relid);
203+
Oid relid = rte->relid;
204+
char relkind = rte->relkind;
205+
char *relname = get_rel_name(relid);
207206

208-
/* The OLD and NEW placeholder entries in the view's rtable are skipped. */
207+
/*
208+
* The OLD and NEW placeholder entries in the view's rtable are
209+
* skipped.
210+
*/
209211
if (relid == context->viewoid &&
210212
(!strcmp(rte->eref->aliasname, "old") || !strcmp(rte->eref->aliasname, "new")))
211213
continue;
@@ -216,11 +218,11 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
216218
continue;
217219

218220
/* Check infinite recursion in the view definition. */
219-
if (relid == context->root_reloid)
221+
if (list_member_oid(context->ancestor_views, relid))
220222
ereport(ERROR,
221223
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
222-
errmsg("infinite recursion detected in rules for relation \"%s\"",
223-
get_rel_name(context->root_reloid))));
224+
errmsg("infinite recursion detected in rules for relation \"%s\"",
225+
get_rel_name(relid))));
224226

225227
/* Check permissions with the view owner's privilege. */
226228
aclresult = LockTableAclCheck(relid, context->lockmode, context->viewowner);
@@ -233,11 +235,11 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
233235
else if (!ConditionalLockRelationOid(relid, context->lockmode))
234236
ereport(ERROR,
235237
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
236-
errmsg("could not obtain lock on relation \"%s\"",
238+
errmsg("could not obtain lock on relation \"%s\"",
237239
relname)));
238240

239241
if (relkind == RELKIND_VIEW)
240-
LockViewRecurse(relid, context->root_reloid, context->lockmode, context->nowait);
242+
LockViewRecurse(relid, context->lockmode, context->nowait, context->ancestor_views);
241243
else if (rte->inh)
242244
LockTableRecurse(relid, context->lockmode, context->nowait, context->viewowner);
243245
}
@@ -254,24 +256,26 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
254256
}
255257

256258
static void
257-
LockViewRecurse(Oid reloid, Oid root_reloid, LOCKMODE lockmode, bool nowait)
259+
LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views)
258260
{
259261
LockViewRecurse_context context;
260262

261-
Relation view;
262-
Query *viewquery;
263+
Relation view;
264+
Query *viewquery;
263265

264266
view = heap_open(reloid, NoLock);
265267
viewquery = get_view_query(view);
266268

267-
context.root_reloid = root_reloid;
268269
context.lockmode = lockmode;
269270
context.nowait = nowait;
270271
context.viewowner = view->rd_rel->relowner;
271272
context.viewoid = reloid;
273+
context.ancestor_views = lcons_oid(reloid, ancestor_views);
272274

273275
LockViewRecurse_walker((Node *) viewquery, &context);
274276

277+
ancestor_views = list_delete_oid(ancestor_views, reloid);
278+
275279
heap_close(view, NoLock);
276280
}
277281

src/test/regress/expected/lock.out

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ select relname from pg_locks l, pg_class c
120120
lock_view6
121121
(2 rows)
122122

123+
ROLLBACK;
124+
-- detecting infinite recursions in view definitions
125+
CREATE OR REPLACE VIEW lock_view2 AS SELECT * from lock_view3;
126+
BEGIN TRANSACTION;
127+
LOCK TABLE lock_view2 IN EXCLUSIVE MODE;
128+
ERROR: infinite recursion detected in rules for relation "lock_view2"
129+
ROLLBACK;
130+
CREATE VIEW lock_view7 AS SELECT * from lock_view2;
131+
BEGIN TRANSACTION;
132+
LOCK TABLE lock_view7 IN EXCLUSIVE MODE;
133+
ERROR: infinite recursion detected in rules for relation "lock_view2"
123134
ROLLBACK;
124135
-- Verify that we can lock a table with inheritance children.
125136
CREATE TABLE lock_tbl2 (b BIGINT) INHERITS (lock_tbl1);
@@ -142,11 +153,12 @@ RESET ROLE;
142153
--
143154
-- Clean up
144155
--
156+
DROP VIEW lock_view7;
145157
DROP VIEW lock_view6;
146158
DROP VIEW lock_view5;
147159
DROP VIEW lock_view4;
148-
DROP VIEW lock_view3;
149-
DROP VIEW lock_view2;
160+
DROP VIEW lock_view3 CASCADE;
161+
NOTICE: drop cascades to view lock_view2
150162
DROP VIEW lock_view1;
151163
DROP TABLE lock_tbl3;
152164
DROP TABLE lock_tbl2;

src/test/regress/sql/lock.sql

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ select relname from pg_locks l, pg_class c
8484
where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock'
8585
order by relname;
8686
ROLLBACK;
87+
-- detecting infinite recursions in view definitions
88+
CREATE OR REPLACE VIEW lock_view2 AS SELECT * from lock_view3;
89+
BEGIN TRANSACTION;
90+
LOCK TABLE lock_view2 IN EXCLUSIVE MODE;
91+
ROLLBACK;
92+
CREATE VIEW lock_view7 AS SELECT * from lock_view2;
93+
BEGIN TRANSACTION;
94+
LOCK TABLE lock_view7 IN EXCLUSIVE MODE;
95+
ROLLBACK;
8796

8897
-- Verify that we can lock a table with inheritance children.
8998
CREATE TABLE lock_tbl2 (b BIGINT) INHERITS (lock_tbl1);
@@ -107,11 +116,11 @@ RESET ROLE;
107116
--
108117
-- Clean up
109118
--
119+
DROP VIEW lock_view7;
110120
DROP VIEW lock_view6;
111121
DROP VIEW lock_view5;
112122
DROP VIEW lock_view4;
113-
DROP VIEW lock_view3;
114-
DROP VIEW lock_view2;
123+
DROP VIEW lock_view3 CASCADE;
115124
DROP VIEW lock_view1;
116125
DROP TABLE lock_tbl3;
117126
DROP TABLE lock_tbl2;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy