From ca8100f9eb7f02f410d1a45f2d5fee8373eace84 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Mon, 12 Jan 2009 08:54:27 +0000 Subject: [PATCH] Add ONLY support to LOCK and TRUNCATE. By default, these commands are now recursive. => Note this incompatibility in the release notes. --- doc/src/sgml/ref/lock.sgml | 8 +- doc/src/sgml/ref/truncate.sgml | 9 +- src/backend/commands/lockcmds.c | 64 ++++++----- src/backend/commands/tablecmds.c | 30 +++++- src/backend/parser/gram.y | 21 +++- src/test/regress/expected/truncate.out | 144 +++++++++++++++++++++++++ src/test/regress/sql/truncate.sql | 49 +++++++++ 7 files changed, 287 insertions(+), 38 deletions(-) diff --git a/doc/src/sgml/ref/lock.sgml b/doc/src/sgml/ref/lock.sgml index f49d18618c..567f363451 100644 --- a/doc/src/sgml/ref/lock.sgml +++ b/doc/src/sgml/ref/lock.sgml @@ -1,5 +1,5 @@ @@ -21,7 +21,7 @@ PostgreSQL documentation -LOCK [ TABLE ] name [, ...] [ IN lockmode MODE ] [ NOWAIT ] +LOCK [ TABLE ] [ ONLY ] name [, ...] [ IN lockmode MODE ] [ NOWAIT ] where lockmode is one of: @@ -109,7 +109,9 @@ where lockmode is one of: The name (optionally schema-qualified) of an existing table to - lock. + lock. If ONLY is specified, only that table is + locked. If ONLY is not specified, the table and all + its descendant tables (if any) are locked. diff --git a/doc/src/sgml/ref/truncate.sgml b/doc/src/sgml/ref/truncate.sgml index d765c1bd8c..a512b9a52d 100644 --- a/doc/src/sgml/ref/truncate.sgml +++ b/doc/src/sgml/ref/truncate.sgml @@ -1,5 +1,5 @@ @@ -21,7 +21,7 @@ PostgreSQL documentation -TRUNCATE [ TABLE ] name [, ... ] +TRUNCATE [ TABLE ] [ ONLY ] name [, ... ] [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ] @@ -47,7 +47,10 @@ TRUNCATE [ TABLE ] name [, ... ] name - The name (optionally schema-qualified) of a table to be truncated. + The name (optionally schema-qualified) of a table to be + truncated. If ONLY is specified, only that table is + truncated. If ONLY is not specified, the table and + all its descendant tables (if any) are truncated. diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index 7d0879da55..27805be5d8 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -8,7 +8,7 @@ * * * 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 "commands/lockcmds.h" #include "miscadmin.h" +#include "optimizer/prep.h" +#include "parser/parse_clause.h" #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/rel.h" @@ -40,38 +42,48 @@ LockTableCommand(LockStmt *lockstmt) { RangeVar *relation = lfirst(p); Oid reloid; - AclResult aclresult; - Relation rel; + bool recurse = interpretInhOption(relation->inhOpt); + 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); - if (lockstmt->mode == AccessShareLock) - aclresult = pg_class_aclcheck(reloid, GetUserId(), - ACL_SELECT); + if (recurse) + children_and_self = find_all_inheritors(reloid); else - aclresult = pg_class_aclcheck(reloid, GetUserId(), - ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE); + children_and_self = list_make1_oid(reloid); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - get_rel_name(reloid)); + foreach(child, children_and_self) + { + Oid childreloid = lfirst_oid(child); + Relation rel; + AclResult aclresult; - if (lockstmt->nowait) - rel = relation_open_nowait(reloid, lockstmt->mode); - else - rel = relation_open(reloid, lockstmt->mode); + /* We don't want to open the relation until we've checked privilege. */ + if (lockstmt->mode == AccessShareLock) + aclresult = pg_class_aclcheck(childreloid, GetUserId(), + ACL_SELECT); + else + aclresult = pg_class_aclcheck(childreloid, GetUserId(), + ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE); - /* 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", - relation->relname))); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + get_rel_name(childreloid)); - relation_close(rel, NoLock); /* close rel, keep lock */ + if (lockstmt->nowait) + rel = relation_open_nowait(childreloid, lockstmt->mode); + else + rel = relation_open(childreloid, lockstmt->mode); + + /* 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", + get_rel_name(childreloid)))); + + relation_close(rel, NoLock); /* close rel, keep lock */ + } } } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f932b65e1f..061d45c30a 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * 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); Relation rel; + bool recurse = interpretInhOption(rv->inhOpt); + Oid myrelid; rel = heap_openrv(rv, AccessExclusiveLock); + myrelid = RelationGetRelid(rel); /* 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); continue; } truncate_check_rel(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); + } + } } /* diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 21b7498341..145e4aac5d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * 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 * AUTHOR DATE MAJOR EVENT @@ -284,6 +284,7 @@ static TypeName *TableFuncTypeName(List *columns); execute_param_clause using_clause returning_clause enum_val_list table_func_column_list create_generic_options alter_generic_options + relation_expr_list %type OptTempTableName %type into_clause create_as_target @@ -3794,7 +3795,7 @@ attrs: '.' attr_name *****************************************************************************/ 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); n->relations = $3; @@ -6558,7 +6559,15 @@ using_clause: | /*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); @@ -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 * further ahead whether the first "set" is an alias or the UPDATE's SET diff --git a/src/test/regress/expected/truncate.out b/src/test/regress/expected/truncate.out index 3055679f7e..7f43df710c 100644 --- a/src/test/regress/expected/truncate.out +++ b/src/test/regress/expected/truncate.out @@ -141,6 +141,150 @@ SELECT * FROM trunc_e; (0 rows) 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 CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text); CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text, diff --git a/src/test/regress/sql/truncate.sql b/src/test/regress/sql/truncate.sql index 3cce3ee314..b348e94c48 100644 --- a/src/test/regress/sql/truncate.sql +++ b/src/test/regress/sql/truncate.sql @@ -78,6 +78,55 @@ SELECT * FROM trunc_e; 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 CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text);