11/*-------------------------------------------------------------------------
22 *
33 * lockcmds.c
4- *Lock command support code
4+ *LOCK command support code
55 *
66 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
77 * Portions Copyright (c) 1994, Regents of the University of California
88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/commands/lockcmds.c,v 1.23 2009/05/1203:11:01 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/commands/lockcmds.c,v 1.24 2009/05/1216:43:32 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
2020#include "commands/lockcmds.h"
2121#include "miscadmin.h"
2222#include "parser/parse_clause.h"
23+ #include "storage/lmgr.h"
2324#include "utils/acl.h"
2425#include "utils/lsyscache.h"
25- #include "utils/rel.h"
26+
27+ static void LockTableRecurse (Oid reloid ,RangeVar * rv ,
28+ LOCKMODE lockmode ,bool nowait ,bool recurse );
2629
2730
2831/*
@@ -34,57 +37,127 @@ LockTableCommand(LockStmt *lockstmt)
3437ListCell * p ;
3538
3639/*
37- * Iterate over the list and open, lock, and close the relations one at a
38- * time
40+ * Iterate over the list and process the named relations one at a time
3941 */
40-
4142foreach (p ,lockstmt -> relations )
4243{
43- RangeVar * relation = lfirst (p );
44- Oid reloid ;
44+ RangeVar * relation = (RangeVar * )lfirst (p );
4545bool recurse = interpretInhOption (relation -> inhOpt );
46- List * children_and_self ;
47- ListCell * child ;
46+ Oid reloid ;
4847
4948reloid = RangeVarGetRelid (relation , false);
5049
51- /* XXX NoLock here is not really a good idea */
52- if (recurse )
53- children_and_self = find_all_inheritors (reloid ,NoLock );
54- else
55- children_and_self = list_make1_oid (reloid );
50+ LockTableRecurse (reloid ,relation ,
51+ lockstmt -> mode ,lockstmt -> nowait ,recurse );
52+ }
53+ }
5654
57- foreach (child ,children_and_self )
55+ /*
56+ * Apply LOCK TABLE recursively over an inheritance tree
57+ *
58+ * At top level, "rv" is the original command argument; we use it to throw
59+ * an appropriate error message if the relation isn't there. Below top level,
60+ * "rv" is NULL and we should just silently ignore any dropped child rel.
61+ */
62+ static void
63+ LockTableRecurse (Oid reloid ,RangeVar * rv ,
64+ LOCKMODE lockmode ,bool nowait ,bool recurse )
65+ {
66+ Relation rel ;
67+ AclResult aclresult ;
68+
69+ /*
70+ * Acquire the lock. We must do this first to protect against
71+ * concurrent drops. Note that a lock against an already-dropped
72+ * relation's OID won't fail.
73+ */
74+ if (nowait )
75+ {
76+ if (!ConditionalLockRelationOid (reloid ,lockmode ))
5877{
59- Oid childreloid = lfirst_oid ( child );
60- Relation rel ;
61- AclResult aclresult ;
62-
63- /* We don't want to open the relation until we've checked privilege. */
64- if ( lockstmt -> mode == AccessShareLock )
65- aclresult = pg_class_aclcheck ( childreloid , GetUserId () ,
66- ACL_SELECT );
78+ /* try to throw error by name; relation could be deleted... */
79+ char * relname = rv ? rv -> relname : get_rel_name ( reloid ) ;
80+
81+ if ( relname )
82+ ereport ( ERROR ,
83+ ( errcode ( ERRCODE_LOCK_NOT_AVAILABLE ),
84+ errmsg ( "could not obtain lock on relation \"%s\"" ,
85+ relname )) );
6786else
68- aclresult = pg_class_aclcheck (childreloid ,GetUserId (),
69- ACL_UPDATE |ACL_DELETE |ACL_TRUNCATE );
87+ ereport (ERROR ,
88+ (errcode (ERRCODE_LOCK_NOT_AVAILABLE ),
89+ errmsg ("could not obtain lock on relation with OID %u" ,
90+ reloid )));
91+ }
92+ }
93+ else
94+ LockRelationOid (reloid ,lockmode );
7095
71- if (aclresult != ACLCHECK_OK )
72- aclcheck_error (aclresult ,ACL_KIND_CLASS ,
73- get_rel_name (childreloid ));
96+ /*
97+ * Now that we have the lock, check to see if the relation really exists
98+ * or not.
99+ */
100+ rel = try_relation_open (reloid ,NoLock );
74101
75- if (lockstmt -> nowait )
76- rel = relation_open_nowait ( childreloid , lockstmt -> mode );
77- else
78- rel = relation_open ( childreloid , lockstmt -> mode );
102+ if (! rel )
103+ {
104+ /* Release useless lock */
105+ UnlockRelationOid ( reloid , lockmode );
79106
80- /* Currently, we only allow plain tables to be locked */
81- if (rel -> rd_rel -> relkind != RELKIND_RELATION )
107+ /* At top level, throw error; otherwise, ignore this child rel */
108+ if (rv )
109+ {
110+ if (rv -> schemaname )
82111ereport (ERROR ,
83- (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
84- errmsg ("\"%s\" is not a table" ,
85- get_rel_name (childreloid ))));
112+ (errcode (ERRCODE_UNDEFINED_TABLE ),
113+ errmsg ("relation \"%s.%s\" does not exist" ,
114+ rv -> schemaname ,rv -> relname )));
115+ else
116+ ereport (ERROR ,
117+ (errcode (ERRCODE_UNDEFINED_TABLE ),
118+ errmsg ("relation \"%s\" does not exist" ,
119+ rv -> relname )));
120+ }
121+
122+ return ;
123+ }
86124
87- relation_close (rel ,NoLock );/* close rel, keep lock */
125+ /* Verify adequate privilege */
126+ if (lockmode == AccessShareLock )
127+ aclresult = pg_class_aclcheck (reloid ,GetUserId (),
128+ ACL_SELECT );
129+ else
130+ aclresult = pg_class_aclcheck (reloid ,GetUserId (),
131+ ACL_UPDATE |ACL_DELETE |ACL_TRUNCATE );
132+ if (aclresult != ACLCHECK_OK )
133+ aclcheck_error (aclresult ,ACL_KIND_CLASS ,
134+ RelationGetRelationName (rel ));
135+
136+ /* Currently, we only allow plain tables to be locked */
137+ if (rel -> rd_rel -> relkind != RELKIND_RELATION )
138+ ereport (ERROR ,
139+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
140+ errmsg ("\"%s\" is not a table" ,
141+ RelationGetRelationName (rel ))));
142+
143+ /*
144+ * If requested, recurse to children. We use find_inheritance_children
145+ * not find_all_inheritors to avoid taking locks far in advance of
146+ * checking privileges. This means we'll visit multiply-inheriting
147+ * children more than once, but that's no problem.
148+ */
149+ if (recurse )
150+ {
151+ List * children = find_inheritance_children (reloid ,NoLock );
152+ ListCell * lc ;
153+
154+ foreach (lc ,children )
155+ {
156+ Oid childreloid = lfirst_oid (lc );
157+
158+ LockTableRecurse (childreloid ,NULL ,lockmode ,nowait ,recurse );
88159}
89160}
161+
162+ relation_close (rel ,NoLock );/* close rel, keep lock */
90163}