*
* We violate transaction semantics here by overwriting the rel's
* existing pg_class tuple with the new values. This is reasonably
- * safe since the new values are correct whether or not this transaction
- * commits. The reason for this is that if we updated these tuples in
- * the usual way, vacuuming pg_class itself wouldn't work very well ---
- * by the time we got done with a vacuum cycle, most of the tuples in
- * pg_class would've been obsoleted. Of course, this only works for
- * fixed-size never-null columns, but these are.
- *
- * Note another assumption: that two VACUUMs/ANALYZEs on a table can't
- * run in parallel, nor can VACUUM/ANALYZE run in parallel with a
- * schema alteration such as adding an index, rule, or trigger. Otherwise
- * our updates of relhasindex etc might overwrite uncommitted updates.
+ * safe as long as we're sure that the new values are correct whether or
+ * not this transaction commits. The reason for doing this is that if
+ * we updated these tuples in the usual way, vacuuming pg_class itself
+ * wouldn't work very well --- by the time we got done with a vacuum
+ * cycle, most of the tuples in pg_class would've been obsoleted. Of
+ * course, this only works for fixed-size not-null columns, but these are.
*
* Another reason for doing it this way is that when we are in a lazy
- * VACUUM and have PROC_IN_VACUUM set, we mustn't do any updates ---
- * somebody vacuuming pg_class might think they could delete a tuple
+ * VACUUM and have PROC_IN_VACUUM set, we mustn't do any regular updates.
+ * Somebody vacuuming pg_class might think they could delete a tuple
* marked with xmin = our xid.
*
+ * In addition to fundamentally nontransactional statistics such as
+ * relpages and relallvisible, we try to maintain certain lazily-updated
+ * DDL flags such as relhasindex, by clearing them if no longer correct.
+ * It's safe to do this in VACUUM, which can't run in parallel with
+ * CREATE INDEX/RULE/TRIGGER and can't be part of a transaction block.
+ * However, it's *not* safe to do it in an ANALYZE that's within a
+ * transaction block, because for example the current transaction might
+ * have dropped the last index; then we'd think relhasindex should be
+ * cleared, but if the transaction later rolls back this would be wrong.
+ * So we refrain from updating the DDL flags if we're inside a
+ * transaction block. This is OK since postponing the flag maintenance
+ * is always allowable.
+ *
* This routine is shared by VACUUM and ANALYZE.
*/
void
relid);
pgcform = (Form_pg_class) GETSTRUCT(ctup);
- /* Apply required updates, if any, to copied tuple */
+ /* Apply statistical updates, if any, to copied tuple */
dirty = false;
if (pgcform->relpages != (int32) num_pages)
pgcform->relallvisible = (int32) num_all_visible_pages;
dirty = true;
}
- if (pgcform->relhasindex != hasindex)
- {
- pgcform->relhasindex = hasindex;
- dirty = true;
- }
- /*
- * If we have discovered that there are no indexes, then there's no
- * primary key either. This could be done more thoroughly...
- */
- if (pgcform->relhaspkey && !hasindex)
- {
- pgcform->relhaspkey = false;
- dirty = true;
- }
+ /* Apply DDL updates, but not inside a transaction block (see above) */
- /* We also clear relhasrules and relhastriggers if needed */
- if (pgcform->relhasrules && relation->rd_rules == NULL)
+ if (!IsTransactionBlock())
{
- pgcform->relhasrules = false;
- dirty = true;
- }
- if (pgcform->relhastriggers && relation->trigdesc == NULL)
- {
- pgcform->relhastriggers = false;
- dirty = true;
+ /*
+ * If we didn't find any indexes, reset relhasindex.
+ */
+ if (pgcform->relhasindex && !hasindex)
+ {
+ pgcform->relhasindex = false;
+ dirty = true;
+ }
+
+ /*
+ * If we have discovered that there are no indexes, then there's no
+ * primary key either. This could be done more thoroughly...
+ */
+ if (pgcform->relhaspkey && !hasindex)
+ {
+ pgcform->relhaspkey = false;
+ dirty = true;
+ }
+
+ /* We also clear relhasrules and relhastriggers if needed */
+ if (pgcform->relhasrules && relation->rd_rules == NULL)
+ {
+ pgcform->relhasrules = false;
+ dirty = true;
+ }
+ if (pgcform->relhastriggers && relation->trigdesc == NULL)
+ {
+ pgcform->relhastriggers = false;
+ dirty = true;
+ }
}
/*