Tom Lane wrote:
+1 for making TRUNCATE and LOCK support ONLY.

Patch attached.

I don't care much about
ALTER TABLE SET SCHEMA, but perhaps there's a use-case for recursion
on that.

I have added this to the Todo list for later reconsideration.

Index: doc/src/sgml/ref/lock.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/lock.sgml,v
retrieving revision 1.51
diff -u -3 -p -c -r1.51 lock.sgml
*** doc/src/sgml/ref/lock.sgml	14 Nov 2008 10:22:47 -0000	1.51
--- doc/src/sgml/ref/lock.sgml	8 Jan 2009 13:27:47 -0000
*************** PostgreSQL documentation
*** 21,27 ****
  
   <refsynopsisdiv>
  <synopsis>
! LOCK [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ IN <replaceable class="PARAMETER">lockmode</replaceable> MODE ] [ NOWAIT ]
  
  where <replaceable class="PARAMETER">lockmode</replaceable> is one of:
  
--- 21,27 ----
  
   <refsynopsisdiv>
  <synopsis>
! 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">loc
*** 109,115 ****
      <listitem>
       <para>
        The name (optionally schema-qualified) of an existing table to
!       lock.
       </para>
  
       <para>
--- 109,117 ----
      <listitem>
       <para>
        The name (optionally schema-qualified) of an existing table to
!       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>
Index: doc/src/sgml/ref/truncate.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/truncate.sgml,v
retrieving revision 1.31
diff -u -3 -p -c -r1.31 truncate.sgml
*** doc/src/sgml/ref/truncate.sgml	18 Dec 2008 10:45:00 -0000	1.31
--- doc/src/sgml/ref/truncate.sgml	8 Jan 2009 13:27:47 -0000
*************** PostgreSQL documentation
*** 21,27 ****
  
   <refsynopsisdiv>
  <synopsis>
! TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ... ]
      [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]
  </synopsis>
   </refsynopsisdiv>
--- 21,27 ----
  
   <refsynopsisdiv>
  <synopsis>
! TRUNCATE [ TABLE ] [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [, ... ]
      [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]
  </synopsis>
   </refsynopsisdiv>
*************** TRUNCATE [ TABLE ] <replaceable class="P
*** 47,53 ****
      <term><replaceable class="PARAMETER">name</replaceable></term>
      <listitem>
       <para>
!       The name (optionally schema-qualified) of a table to be truncated.
       </para>
      </listitem>
     </varlistentry>
--- 47,56 ----
      <term><replaceable class="PARAMETER">name</replaceable></term>
      <listitem>
       <para>
!       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>
      </listitem>
     </varlistentry>
Index: src/backend/commands/lockcmds.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/lockcmds.c,v
retrieving revision 1.20
diff -u -3 -p -c -r1.20 lockcmds.c
*** src/backend/commands/lockcmds.c	1 Jan 2009 17:23:38 -0000	1.20
--- src/backend/commands/lockcmds.c	8 Jan 2009 13:27:47 -0000
***************
*** 18,23 ****
--- 18,25 ----
  #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"
*************** LockTableCommand(LockStmt *lockstmt)
*** 40,77 ****
  	{
  		RangeVar   *relation = lfirst(p);
  		Oid			reloid;
! 		AclResult	aclresult;
! 		Relation	rel;
  
- 		/*
- 		 * 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);
  		else
! 			aclresult = pg_class_aclcheck(reloid, GetUserId(),
! 										  ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE);
  
! 		if (aclresult != ACLCHECK_OK)
! 			aclcheck_error(aclresult, ACL_KIND_CLASS,
! 						   get_rel_name(reloid));
  
! 		if (lockstmt->nowait)
! 			rel = relation_open_nowait(reloid, lockstmt->mode);
! 		else
! 			rel = relation_open(reloid, 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",
! 							relation->relname)));
! 
! 		relation_close(rel, NoLock);	/* close rel, keep lock */
  	}
  }
--- 42,89 ----
  	{
  		RangeVar   *relation = lfirst(p);
  		Oid			reloid;
! 		bool		recurse = interpretInhOption(relation->inhOpt);
! 		List	   *children_and_self;
! 		ListCell   *child;
  
  		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)
! 				aclresult = pg_class_aclcheck(childreloid, GetUserId(),
! 											  ACL_SELECT);
! 			else
! 				aclresult = pg_class_aclcheck(childreloid, GetUserId(),
! 											  ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE);
! 
! 			if (aclresult != ACLCHECK_OK)
! 				aclcheck_error(aclresult, ACL_KIND_CLASS,
! 							   get_rel_name(childreloid));
! 
! 			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 */
! 		}
  	}
  }
Index: src/backend/commands/tablecmds.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v
retrieving revision 1.276
diff -u -3 -p -c -r1.276 tablecmds.c
*** src/backend/commands/tablecmds.c	1 Jan 2009 17:23:39 -0000	1.276
--- src/backend/commands/tablecmds.c	8 Jan 2009 13:27:47 -0000
*************** ExecuteTruncate(TruncateStmt *stmt)
*** 772,788 ****
  	{
  		RangeVar   *rv = lfirst(cell);
  		Relation	rel;
  
  		rel = heap_openrv(rv, AccessExclusiveLock);
  		/* don't throw error for "TRUNCATE foo, foo" */
! 		if (list_member_oid(relids, RelationGetRelid(rel)))
  		{
  			heap_close(rel, AccessExclusiveLock);
  			continue;
  		}
  		truncate_check_rel(rel);
  		rels = lappend(rels, rel);
! 		relids = lappend_oid(relids, RelationGetRelid(rel));
  	}
  
  	/*
--- 772,812 ----
  	{
  		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, myrelid))
  		{
  			heap_close(rel, AccessExclusiveLock);
  			continue;
  		}
  		truncate_check_rel(rel);
  		rels = lappend(rels, 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);
! 			}
! 		}
  	}
  
  	/*
Index: src/backend/parser/gram.y
===================================================================
RCS file: /cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.652
diff -u -3 -p -c -r2.652 gram.y
*** src/backend/parser/gram.y	7 Jan 2009 22:54:45 -0000	2.652
--- src/backend/parser/gram.y	8 Jan 2009 13:27:47 -0000
*************** static TypeName *TableFuncTypeName(List 
*** 284,289 ****
--- 284,290 ----
  				execute_param_clause using_clause returning_clause
  				enum_val_list table_func_column_list
  				create_generic_options alter_generic_options
+ 				relation_expr_list
  
  %type <range>	OptTempTableName
  %type <into>	into_clause create_as_target
*************** attrs:		'.' attr_name
*** 3794,3800 ****
   *****************************************************************************/
  
  TruncateStmt:
! 			TRUNCATE opt_table qualified_name_list opt_restart_seqs opt_drop_behavior
  				{
  					TruncateStmt *n = makeNode(TruncateStmt);
  					n->relations = $3;
--- 3795,3801 ----
   *****************************************************************************/
  
  TruncateStmt:
! 			TRUNCATE opt_table relation_expr_list opt_restart_seqs opt_drop_behavior
  				{
  					TruncateStmt *n = makeNode(TruncateStmt);
  					n->relations = $3;
*************** using_clause:
*** 6558,6564 ****
  			| /*EMPTY*/								{ $$ = NIL; }
  		;
  
! LockStmt:	LOCK_P opt_table qualified_name_list opt_lock opt_nowait
  				{
  					LockStmt *n = makeNode(LockStmt);
  
--- 6559,6573 ----
  			| /*EMPTY*/								{ $$ = NIL; }
  		;
  
! 
! /*****************************************************************************
!  *
!  *		QUERY:
!  *				LOCK TABLE
!  *
!  *****************************************************************************/
! 
! LockStmt:	LOCK_P opt_table relation_expr_list opt_lock opt_nowait
  				{
  					LockStmt *n = makeNode(LockStmt);
  
*************** relation_expr:
*** 7487,7492 ****
--- 7496,7507 ----
  		;
  
  
+ 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
Index: src/test/regress/expected/truncate.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/truncate.out,v
retrieving revision 1.18
diff -u -3 -p -c -r1.18 truncate.out
*** src/test/regress/expected/truncate.out	1 Sep 2008 20:42:46 -0000	1.18
--- src/test/regress/expected/truncate.out	8 Jan 2009 13:27:48 -0000
*************** SELECT * FROM trunc_e;
*** 141,146 ****
--- 141,290 ----
  (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,
Index: src/test/regress/sql/truncate.sql
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/sql/truncate.sql,v
retrieving revision 1.7
diff -u -3 -p -c -r1.7 truncate.sql
*** src/test/regress/sql/truncate.sql	16 May 2008 23:36:05 -0000	1.7
--- src/test/regress/sql/truncate.sql	8 Jan 2009 13:27:48 -0000
*************** SELECT * FROM trunc_e;
*** 78,83 ****
--- 78,132 ----
  
  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);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to