Add ONLY support to LOCK and TRUNCATE. By default, these commands are now

recursive.

=> Note this incompatibility in the release notes.
This commit is contained in:
Peter Eisentraut 2009-01-12 08:54:27 +00:00
parent b7b8f0b609
commit ca8100f9eb
7 changed files with 287 additions and 38 deletions

View File

@ -1,5 +1,5 @@
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/lock.sgml,v 1.51 2008/11/14 10:22:47 petere Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/lock.sgml,v 1.52 2009/01/12 08:54:25 petere Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
LOCK [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ IN <replaceable class="PARAMETER">lockmode</replaceable> MODE ] [ NOWAIT ] LOCK [ TABLE ] [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ IN <replaceable class="PARAMETER">lockmode</replaceable> MODE ] [ NOWAIT ]
where <replaceable class="PARAMETER">lockmode</replaceable> is one of: where <replaceable class="PARAMETER">lockmode</replaceable> is one of:
@ -109,7 +109,9 @@ where <replaceable class="PARAMETER">lockmode</replaceable> is one of:
<listitem> <listitem>
<para> <para>
The name (optionally schema-qualified) of an existing table to The name (optionally schema-qualified) of an existing table to
lock. lock. If <literal>ONLY</> is specified, only that table is
locked. If <literal>ONLY</> is not specified, the table and all
its descendant tables (if any) are locked.
</para> </para>
<para> <para>

View File

@ -1,5 +1,5 @@
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.31 2008/12/18 10:45:00 petere Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.32 2009/01/12 08:54:25 petere Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ... ] TRUNCATE [ TABLE ] [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [, ... ]
[ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ] [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
@ -47,7 +47,10 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ... ]
<term><replaceable class="PARAMETER">name</replaceable></term> <term><replaceable class="PARAMETER">name</replaceable></term>
<listitem> <listitem>
<para> <para>
The name (optionally schema-qualified) of a table to be truncated. The name (optionally schema-qualified) of a table to be
truncated. If <literal>ONLY</> is specified, only that table is
truncated. If <literal>ONLY</> is not specified, the table and
all its descendant tables (if any) are truncated.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/lockcmds.c,v 1.20 2009/01/01 17:23:38 momjian Exp $ * $PostgreSQL: pgsql/src/backend/commands/lockcmds.c,v 1.21 2009/01/12 08:54:26 petere Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -18,6 +18,8 @@
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "commands/lockcmds.h" #include "commands/lockcmds.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "optimizer/prep.h"
#include "parser/parse_clause.h"
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/rel.h" #include "utils/rel.h"
@ -40,38 +42,48 @@ LockTableCommand(LockStmt *lockstmt)
{ {
RangeVar *relation = lfirst(p); RangeVar *relation = lfirst(p);
Oid reloid; Oid reloid;
AclResult aclresult; bool recurse = interpretInhOption(relation->inhOpt);
Relation rel; List *children_and_self;
ListCell *child;
/*
* We don't want to open the relation until we've checked privilege.
* So, manually get the relation OID.
*/
reloid = RangeVarGetRelid(relation, false); reloid = RangeVarGetRelid(relation, false);
if (recurse)
children_and_self = find_all_inheritors(reloid);
else
children_and_self = list_make1_oid(reloid);
foreach(child, children_and_self)
{
Oid childreloid = lfirst_oid(child);
Relation rel;
AclResult aclresult;
/* We don't want to open the relation until we've checked privilege. */
if (lockstmt->mode == AccessShareLock) if (lockstmt->mode == AccessShareLock)
aclresult = pg_class_aclcheck(reloid, GetUserId(), aclresult = pg_class_aclcheck(childreloid, GetUserId(),
ACL_SELECT); ACL_SELECT);
else else
aclresult = pg_class_aclcheck(reloid, GetUserId(), aclresult = pg_class_aclcheck(childreloid, GetUserId(),
ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE); ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE);
if (aclresult != ACLCHECK_OK) if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_CLASS, aclcheck_error(aclresult, ACL_KIND_CLASS,
get_rel_name(reloid)); get_rel_name(childreloid));
if (lockstmt->nowait) if (lockstmt->nowait)
rel = relation_open_nowait(reloid, lockstmt->mode); rel = relation_open_nowait(childreloid, lockstmt->mode);
else else
rel = relation_open(reloid, lockstmt->mode); rel = relation_open(childreloid, lockstmt->mode);
/* Currently, we only allow plain tables to be locked */ /* Currently, we only allow plain tables to be locked */
if (rel->rd_rel->relkind != RELKIND_RELATION) if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table", errmsg("\"%s\" is not a table",
relation->relname))); get_rel_name(childreloid))));
relation_close(rel, NoLock); /* close rel, keep lock */ relation_close(rel, NoLock); /* close rel, keep lock */
} }
}
} }

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.276 2009/01/01 17:23:39 momjian Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.277 2009/01/12 08:54:26 petere Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -772,17 +772,41 @@ ExecuteTruncate(TruncateStmt *stmt)
{ {
RangeVar *rv = lfirst(cell); RangeVar *rv = lfirst(cell);
Relation rel; Relation rel;
bool recurse = interpretInhOption(rv->inhOpt);
Oid myrelid;
rel = heap_openrv(rv, AccessExclusiveLock); rel = heap_openrv(rv, AccessExclusiveLock);
myrelid = RelationGetRelid(rel);
/* don't throw error for "TRUNCATE foo, foo" */ /* don't throw error for "TRUNCATE foo, foo" */
if (list_member_oid(relids, RelationGetRelid(rel))) if (list_member_oid(relids, myrelid))
{ {
heap_close(rel, AccessExclusiveLock); heap_close(rel, AccessExclusiveLock);
continue; continue;
} }
truncate_check_rel(rel); truncate_check_rel(rel);
rels = lappend(rels, rel); rels = lappend(rels, rel);
relids = lappend_oid(relids, RelationGetRelid(rel)); relids = lappend_oid(relids, myrelid);
if (recurse)
{
ListCell *child;
List *children;
children = find_all_inheritors(myrelid);
foreach(child, children)
{
Oid childrelid = lfirst_oid(child);
if (list_member_oid(relids, childrelid))
continue;
rel = heap_open(childrelid, AccessExclusiveLock);
truncate_check_rel(rel);
rels = lappend(rels, rel);
relids = lappend_oid(relids, childrelid);
}
}
} }
/* /*

View File

@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.652 2009/01/07 22:54:45 momjian Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.653 2009/01/12 08:54:26 petere Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
@ -284,6 +284,7 @@ static TypeName *TableFuncTypeName(List *columns);
execute_param_clause using_clause returning_clause execute_param_clause using_clause returning_clause
enum_val_list table_func_column_list enum_val_list table_func_column_list
create_generic_options alter_generic_options create_generic_options alter_generic_options
relation_expr_list
%type <range> OptTempTableName %type <range> OptTempTableName
%type <into> into_clause create_as_target %type <into> into_clause create_as_target
@ -3794,7 +3795,7 @@ attrs: '.' attr_name
*****************************************************************************/ *****************************************************************************/
TruncateStmt: TruncateStmt:
TRUNCATE opt_table qualified_name_list opt_restart_seqs opt_drop_behavior TRUNCATE opt_table relation_expr_list opt_restart_seqs opt_drop_behavior
{ {
TruncateStmt *n = makeNode(TruncateStmt); TruncateStmt *n = makeNode(TruncateStmt);
n->relations = $3; n->relations = $3;
@ -6558,7 +6559,15 @@ using_clause:
| /*EMPTY*/ { $$ = NIL; } | /*EMPTY*/ { $$ = NIL; }
; ;
LockStmt: LOCK_P opt_table qualified_name_list opt_lock opt_nowait
/*****************************************************************************
*
* QUERY:
* LOCK TABLE
*
*****************************************************************************/
LockStmt: LOCK_P opt_table relation_expr_list opt_lock opt_nowait
{ {
LockStmt *n = makeNode(LockStmt); LockStmt *n = makeNode(LockStmt);
@ -7487,6 +7496,12 @@ relation_expr:
; ;
relation_expr_list:
relation_expr { $$ = list_make1($1); }
| relation_expr_list ',' relation_expr { $$ = lappend($1, $3); }
;
/* /*
* Given "UPDATE foo set set ...", we have to decide without looking any * Given "UPDATE foo set set ...", we have to decide without looking any
* further ahead whether the first "set" is an alias or the UPDATE's SET * further ahead whether the first "set" is an alias or the UPDATE's SET

View File

@ -141,6 +141,150 @@ SELECT * FROM trunc_e;
(0 rows) (0 rows)
DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE; DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;
-- Test TRUNCATE with inheritance
CREATE TABLE trunc_f (col1 integer primary key);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "trunc_f_pkey" for table "trunc_f"
INSERT INTO trunc_f VALUES (1);
INSERT INTO trunc_f VALUES (2);
CREATE TABLE trunc_fa (col2a text) INHERITS (trunc_f);
INSERT INTO trunc_fa VALUES (3, 'three');
CREATE TABLE trunc_fb (col2b int) INHERITS (trunc_f);
INSERT INTO trunc_fb VALUES (4, 444);
CREATE TABLE trunc_faa (col3 text) INHERITS (trunc_fa);
INSERT INTO trunc_faa VALUES (5, 'five', 'FIVE');
BEGIN;
SELECT * FROM trunc_f;
col1
------
1
2
3
4
5
(5 rows)
TRUNCATE trunc_f;
SELECT * FROM trunc_f;
col1
------
(0 rows)
ROLLBACK;
BEGIN;
SELECT * FROM trunc_f;
col1
------
1
2
3
4
5
(5 rows)
TRUNCATE ONLY trunc_f;
SELECT * FROM trunc_f;
col1
------
3
4
5
(3 rows)
ROLLBACK;
BEGIN;
SELECT * FROM trunc_f;
col1
------
1
2
3
4
5
(5 rows)
SELECT * FROM trunc_fa;
col1 | col2a
------+-------
3 | three
5 | five
(2 rows)
SELECT * FROM trunc_faa;
col1 | col2a | col3
------+-------+------
5 | five | FIVE
(1 row)
TRUNCATE ONLY trunc_fb, ONLY trunc_fa;
SELECT * FROM trunc_f;
col1
------
1
2
5
(3 rows)
SELECT * FROM trunc_fa;
col1 | col2a
------+-------
5 | five
(1 row)
SELECT * FROM trunc_faa;
col1 | col2a | col3
------+-------+------
5 | five | FIVE
(1 row)
ROLLBACK;
BEGIN;
SELECT * FROM trunc_f;
col1
------
1
2
3
4
5
(5 rows)
SELECT * FROM trunc_fa;
col1 | col2a
------+-------
3 | three
5 | five
(2 rows)
SELECT * FROM trunc_faa;
col1 | col2a | col3
------+-------+------
5 | five | FIVE
(1 row)
TRUNCATE ONLY trunc_fb, trunc_fa;
SELECT * FROM trunc_f;
col1
------
1
2
(2 rows)
SELECT * FROM trunc_fa;
col1 | col2a
------+-------
(0 rows)
SELECT * FROM trunc_faa;
col1 | col2a | col3
------+-------+------
(0 rows)
ROLLBACK;
DROP TABLE trunc_f CASCADE;
NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to table trunc_fa
drop cascades to table trunc_faa
drop cascades to table trunc_fb
-- Test ON TRUNCATE triggers -- Test ON TRUNCATE triggers
CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text); CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text);
CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text, CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text,

View File

@ -78,6 +78,55 @@ SELECT * FROM trunc_e;
DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE; DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;
-- Test TRUNCATE with inheritance
CREATE TABLE trunc_f (col1 integer primary key);
INSERT INTO trunc_f VALUES (1);
INSERT INTO trunc_f VALUES (2);
CREATE TABLE trunc_fa (col2a text) INHERITS (trunc_f);
INSERT INTO trunc_fa VALUES (3, 'three');
CREATE TABLE trunc_fb (col2b int) INHERITS (trunc_f);
INSERT INTO trunc_fb VALUES (4, 444);
CREATE TABLE trunc_faa (col3 text) INHERITS (trunc_fa);
INSERT INTO trunc_faa VALUES (5, 'five', 'FIVE');
BEGIN;
SELECT * FROM trunc_f;
TRUNCATE trunc_f;
SELECT * FROM trunc_f;
ROLLBACK;
BEGIN;
SELECT * FROM trunc_f;
TRUNCATE ONLY trunc_f;
SELECT * FROM trunc_f;
ROLLBACK;
BEGIN;
SELECT * FROM trunc_f;
SELECT * FROM trunc_fa;
SELECT * FROM trunc_faa;
TRUNCATE ONLY trunc_fb, ONLY trunc_fa;
SELECT * FROM trunc_f;
SELECT * FROM trunc_fa;
SELECT * FROM trunc_faa;
ROLLBACK;
BEGIN;
SELECT * FROM trunc_f;
SELECT * FROM trunc_fa;
SELECT * FROM trunc_faa;
TRUNCATE ONLY trunc_fb, trunc_fa;
SELECT * FROM trunc_f;
SELECT * FROM trunc_fa;
SELECT * FROM trunc_faa;
ROLLBACK;
DROP TABLE trunc_f CASCADE;
-- Test ON TRUNCATE triggers -- Test ON TRUNCATE triggers
CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text); CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text);