summaryrefslogtreecommitdiff
path: root/src/backend/commands/lockcmds.c
blob: 043d68ac7a07228eafb7d3fd964736535df0216c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*-------------------------------------------------------------------------
 *
 * lockcmds.c
 *	  LOCK command support code
 *
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $PostgreSQL: pgsql/src/backend/commands/lockcmds.c,v 1.25 2009/06/11 14:48:56 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/heapam.h"
#include "catalog/namespace.h"
#include "catalog/pg_inherits_fn.h"
#include "commands/lockcmds.h"
#include "miscadmin.h"
#include "parser/parse_clause.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"

static void LockTableRecurse(Oid reloid, RangeVar *rv,
				 LOCKMODE lockmode, bool nowait, bool recurse);


/*
 * LOCK TABLE
 */
void
LockTableCommand(LockStmt *lockstmt)
{
	ListCell   *p;

	/*
	 * Iterate over the list and process the named relations one at a time
	 */
	foreach(p, lockstmt->relations)
	{
		RangeVar   *relation = (RangeVar *) lfirst(p);
		bool		recurse = interpretInhOption(relation->inhOpt);
		Oid			reloid;

		reloid = RangeVarGetRelid(relation, false);

		LockTableRecurse(reloid, relation,
						 lockstmt->mode, lockstmt->nowait, recurse);
	}
}

/*
 * Apply LOCK TABLE recursively over an inheritance tree
 *
 * At top level, "rv" is the original command argument; we use it to throw
 * an appropriate error message if the relation isn't there.  Below top level,
 * "rv" is NULL and we should just silently ignore any dropped child rel.
 */
static void
LockTableRecurse(Oid reloid, RangeVar *rv,
				 LOCKMODE lockmode, bool nowait, bool recurse)
{
	Relation	rel;
	AclResult	aclresult;

	/*
	 * Acquire the lock.  We must do this first to protect against concurrent
	 * drops.  Note that a lock against an already-dropped relation's OID
	 * won't fail.
	 */
	if (nowait)
	{
		if (!ConditionalLockRelationOid(reloid, lockmode))
		{
			/* try to throw error by name; relation could be deleted... */
			char	   *relname = rv ? rv->relname : get_rel_name(reloid);

			if (relname)
				ereport(ERROR,
						(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
						 errmsg("could not obtain lock on relation \"%s\"",
								relname)));
			else
				ereport(ERROR,
						(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
					  errmsg("could not obtain lock on relation with OID %u",
							 reloid)));
		}
	}
	else
		LockRelationOid(reloid, lockmode);

	/*
	 * Now that we have the lock, check to see if the relation really exists
	 * or not.
	 */
	rel = try_relation_open(reloid, NoLock);

	if (!rel)
	{
		/* Release useless lock */
		UnlockRelationOid(reloid, lockmode);

		/* At top level, throw error; otherwise, ignore this child rel */
		if (rv)
		{
			if (rv->schemaname)
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_TABLE),
						 errmsg("relation \"%s.%s\" does not exist",
								rv->schemaname, rv->relname)));
			else
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_TABLE),
						 errmsg("relation \"%s\" does not exist",
								rv->relname)));
		}

		return;
	}

	/* Verify adequate privilege */
	if (lockmode == AccessShareLock)
		aclresult = pg_class_aclcheck(reloid, GetUserId(),
									  ACL_SELECT);
	else
		aclresult = pg_class_aclcheck(reloid, GetUserId(),
									  ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE);
	if (aclresult != ACLCHECK_OK)
		aclcheck_error(aclresult, ACL_KIND_CLASS,
					   RelationGetRelationName(rel));

	/* Currently, we only allow plain tables to be locked */
	if (rel->rd_rel->relkind != RELKIND_RELATION)
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is not a table",
						RelationGetRelationName(rel))));

	/*
	 * If requested, recurse to children.  We use find_inheritance_children
	 * not find_all_inheritors to avoid taking locks far in advance of
	 * checking privileges.  This means we'll visit multiply-inheriting
	 * children more than once, but that's no problem.
	 */
	if (recurse)
	{
		List	   *children = find_inheritance_children(reloid, NoLock);
		ListCell   *lc;

		foreach(lc, children)
		{
			Oid			childreloid = lfirst_oid(lc);

			LockTableRecurse(childreloid, NULL, lockmode, nowait, recurse);
		}
	}

	relation_close(rel, NoLock);	/* close rel, keep lock */
}