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)