88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.178 2006/03/0303:30:52 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.179 2006/03/0318:25:14 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -156,6 +156,7 @@ typedef struct NewColumnValue
156156}NewColumnValue ;
157157
158158
159+ static void truncate_check_rel (Relation rel );
159160static List * MergeAttributes (List * schema ,List * supers ,bool istemp ,
160161List * * supOids ,List * * supconstr ,int * supOidCount );
161162static bool change_varattnos_of_a_node (Node * node ,const AttrNumber * newattno );
@@ -539,33 +540,33 @@ void
539540ExecuteTruncate (TruncateStmt * stmt )
540541{
541542List * rels = NIL ;
542- List * directRelids = NIL ;
543+ List * relids = NIL ;
543544ListCell * cell ;
544- Oid relid ;
545- Relation rel ;
546545
547546/*
548- * Open and exclusive-lock all the explicitly-specified relations
547+ * Open, exclusive-lock, and check all the explicitly-specified relations
549548 */
550549foreach (cell ,stmt -> relations )
551550{
552551RangeVar * rv = lfirst (cell );
552+ Relation rel ;
553553
554554rel = heap_openrv (rv ,AccessExclusiveLock );
555+ truncate_check_rel (rel );
555556rels = lappend (rels ,rel );
556- directRelids = lappend_oid (directRelids ,RelationGetRelid (rel ));
557+ relids = lappend_oid (relids ,RelationGetRelid (rel ));
557558}
558559
559560/*
560561 * In CASCADE mode, suck in all referencing relations as well. This
561562 * requires multiple iterations to find indirectly-dependent relations.
562563 * At each phase, we need to exclusive-lock new rels before looking
563- * for their dependencies, else we might miss something.
564+ * for their dependencies, else we might miss something. Also, we
565+ * check each rel as soon as we open it, to avoid a faux pas such as
566+ * holding lock for a long time on a rel we have no permissions for.
564567 */
565568if (stmt -> behavior == DROP_CASCADE )
566569{
567- List * relids = list_copy (directRelids );
568-
569570for (;;)
570571{
571572List * newrelids ;
@@ -576,70 +577,20 @@ ExecuteTruncate(TruncateStmt *stmt)
576577
577578foreach (cell ,newrelids )
578579{
579- relid = lfirst_oid (cell );
580+ Oid relid = lfirst_oid (cell );
581+ Relation rel ;
582+
580583rel = heap_open (relid ,AccessExclusiveLock );
584+ ereport (NOTICE ,
585+ (errmsg ("truncate cascades to table \"%s\"" ,
586+ RelationGetRelationName (rel ))));
587+ truncate_check_rel (rel );
581588rels = lappend (rels ,rel );
582589relids = lappend_oid (relids ,relid );
583590}
584591}
585592}
586593
587- /* now check all involved relations */
588- foreach (cell ,rels )
589- {
590- rel = (Relation )lfirst (cell );
591- relid = RelationGetRelid (rel );
592-
593- /*
594- * If this table was added to the command by CASCADE, report it.
595- * We don't do this earlier because if we error out on one of the
596- * tables, it'd be confusing to list subsequently-added tables.
597- */
598- if (stmt -> behavior == DROP_CASCADE &&
599- !list_member_oid (directRelids ,relid ))
600- ereport (NOTICE ,
601- (errmsg ("truncate cascades to table \"%s\"" ,
602- RelationGetRelationName (rel ))));
603-
604- /* Only allow truncate on regular tables */
605- if (rel -> rd_rel -> relkind != RELKIND_RELATION )
606- ereport (ERROR ,
607- (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
608- errmsg ("\"%s\" is not a table" ,
609- RelationGetRelationName (rel ))));
610-
611- /* Permissions checks */
612- if (!pg_class_ownercheck (RelationGetRelid (rel ),GetUserId ()))
613- aclcheck_error (ACLCHECK_NOT_OWNER ,ACL_KIND_CLASS ,
614- RelationGetRelationName (rel ));
615-
616- if (!allowSystemTableMods && IsSystemRelation (rel ))
617- ereport (ERROR ,
618- (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
619- errmsg ("permission denied: \"%s\" is a system catalog" ,
620- RelationGetRelationName (rel ))));
621-
622- /*
623- * We can never allow truncation of shared or nailed-in-cache
624- * relations, because we can't support changing their relfilenode
625- * values.
626- */
627- if (rel -> rd_rel -> relisshared || rel -> rd_isnailed )
628- ereport (ERROR ,
629- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
630- errmsg ("cannot truncate system relation \"%s\"" ,
631- RelationGetRelationName (rel ))));
632-
633- /*
634- * Don't allow truncate on temp tables of other backends ... their
635- * local buffer manager is not going to cope.
636- */
637- if (isOtherTempNamespace (RelationGetNamespace (rel )))
638- ereport (ERROR ,
639- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
640- errmsg ("cannot truncate temporary tables of other sessions" )));
641- }
642-
643594/*
644595 * Check foreign key references. In CASCADE mode, this should be
645596 * unnecessary since we just pulled in all the references; but as
@@ -657,11 +608,10 @@ ExecuteTruncate(TruncateStmt *stmt)
657608 */
658609foreach (cell ,rels )
659610{
611+ Relation rel = (Relation )lfirst (cell );
660612Oid heap_relid ;
661613Oid toast_relid ;
662614
663- rel = (Relation )lfirst (cell );
664-
665615/*
666616 * Create a new empty storage file for the relation, and assign it as
667617 * the relfilenode value.The old storage file is scheduled for
@@ -691,6 +641,51 @@ ExecuteTruncate(TruncateStmt *stmt)
691641}
692642}
693643
644+ /*
645+ * Check that a given rel is safe to truncate. Subroutine for ExecuteTruncate
646+ */
647+ static void
648+ truncate_check_rel (Relation rel )
649+ {
650+ /* Only allow truncate on regular tables */
651+ if (rel -> rd_rel -> relkind != RELKIND_RELATION )
652+ ereport (ERROR ,
653+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
654+ errmsg ("\"%s\" is not a table" ,
655+ RelationGetRelationName (rel ))));
656+
657+ /* Permissions checks */
658+ if (!pg_class_ownercheck (RelationGetRelid (rel ),GetUserId ()))
659+ aclcheck_error (ACLCHECK_NOT_OWNER ,ACL_KIND_CLASS ,
660+ RelationGetRelationName (rel ));
661+
662+ if (!allowSystemTableMods && IsSystemRelation (rel ))
663+ ereport (ERROR ,
664+ (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
665+ errmsg ("permission denied: \"%s\" is a system catalog" ,
666+ RelationGetRelationName (rel ))));
667+
668+ /*
669+ * We can never allow truncation of shared or nailed-in-cache
670+ * relations, because we can't support changing their relfilenode
671+ * values.
672+ */
673+ if (rel -> rd_rel -> relisshared || rel -> rd_isnailed )
674+ ereport (ERROR ,
675+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
676+ errmsg ("cannot truncate system relation \"%s\"" ,
677+ RelationGetRelationName (rel ))));
678+
679+ /*
680+ * Don't allow truncate on temp tables of other backends ... their
681+ * local buffer manager is not going to cope.
682+ */
683+ if (isOtherTempNamespace (RelationGetNamespace (rel )))
684+ ereport (ERROR ,
685+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
686+ errmsg ("cannot truncate temporary tables of other sessions" )));
687+ }
688+
694689/*----------
695690 * MergeAttributes
696691 *Returns new schema given initial schema and superclasses.