Fix recently-understood problems with handling of XID freezing, particularly
in PITR scenarios. We now WAL-log the replacement of old XIDs with FrozenTransactionId, so that such replacement is guaranteed to propagate to PITR slave databases. Also, rather than relying on hint-bit updates to be preserved, pg_clog is not truncated until all instances of an XID are known to have been replaced by FrozenTransactionId. Add new GUC variables and pg_autovacuum columns to allow management of the freezing policy, so that users can trade off the size of pg_clog against the amount of freezing work done. Revise the already-existing code that forces autovacuum of tables approaching the wraparound point to make it more bulletproof; also, revise the autovacuum logic so that anti-wraparound vacuuming is done per-table rather than per-database. initdb forced because of changes in pg_class, pg_database, and pg_autovacuum catalogs. Heikki Linnakangas, Simon Riggs, and Tom Lane.
This commit is contained in:
parent
10c70b8602
commit
48188e1621
|
@ -1,4 +1,4 @@
|
|||
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.135 2006/10/23 18:10:30 petere Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.136 2006/11/05 22:42:06 tgl Exp $ -->
|
||||
<!--
|
||||
Documentation of the system catalogs, directed toward PostgreSQL developers
|
||||
-->
|
||||
|
@ -1241,6 +1241,20 @@
|
|||
<entry></entry>
|
||||
<entry>Custom <varname>vacuum_cost_limit</> parameter</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>freeze_min_age</structfield></entry>
|
||||
<entry><type>integer</type></entry>
|
||||
<entry></entry>
|
||||
<entry>Custom <varname>vacuum_freeze_min_age</> parameter</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>freeze_max_age</structfield></entry>
|
||||
<entry><type>integer</type></entry>
|
||||
<entry></entry>
|
||||
<entry>Custom <varname>autovacuum_freeze_max_age</> parameter</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
@ -1258,6 +1272,17 @@
|
|||
live tuples currently estimated to be in the relation.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Also, the autovacuum daemon will perform a <command>VACUUM</> operation
|
||||
to prevent transaction ID wraparound if the table's
|
||||
<structname>pg_class</>.<structfield>relfrozenxid</> field attains an age
|
||||
of more than <structfield>freeze_max_age</> transactions, whether the table
|
||||
has been changed or not. The system will launch autovacuum to perform
|
||||
such <command>VACUUM</>s even if autovacuum is otherwise disabled.
|
||||
See <xref linkend="vacuum-for-wraparound"> for more about wraparound
|
||||
prevention.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Any of the numerical fields can contain <literal>-1</> (or indeed
|
||||
any negative value) to indicate that the system-wide default should
|
||||
|
@ -1266,6 +1291,10 @@
|
|||
<varname>autovacuum_vacuum_cost_delay</> configuration parameter,
|
||||
or from <varname>vacuum_cost_delay</> if the former is set to a negative
|
||||
value. The same applies to <structfield>vac_cost_limit</>.
|
||||
Also, autovacuum will ignore attempts to set a per-table
|
||||
freeze_max_age larger than the system-wide setting (it can only be set
|
||||
smaller), and the freeze_min_age value will be limited to half the
|
||||
system-wide <varname>autovacuum_freeze_max_age</> setting.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
@ -1633,26 +1662,15 @@
|
|||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>relminxid</structfield></entry>
|
||||
<entry><structfield>relfrozenxid</structfield></entry>
|
||||
<entry><type>xid</type></entry>
|
||||
<entry></entry>
|
||||
<entry>
|
||||
The minimum transaction ID present in all rows in this table. This
|
||||
value is used to determine the database-global
|
||||
<structname>pg_database</>.<structfield>datminxid</> value.
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>relvacuumxid</structfield></entry>
|
||||
<entry><type>xid</type></entry>
|
||||
<entry></entry>
|
||||
<entry>
|
||||
The transaction ID that was used as cleaning point as of the last vacuum
|
||||
operation. All rows inserted, updated or deleted in this table by
|
||||
transactions whose IDs are below this one have been marked as known good
|
||||
or deleted. This is used to determine the database-global
|
||||
<structname>pg_database</>.<structfield>datvacuumxid</> value.
|
||||
All transaction IDs before this one have been replaced with a permanent
|
||||
(<quote>frozen</>) transaction ID in this table. This is used to track
|
||||
whether the table needs to be vacuumed in order to prevent transaction
|
||||
ID wraparound or to allow <literal>pg_clog</> to be shrunk. Zero
|
||||
(<symbol>InvalidTransactionId</symbol>) if the relation is not a table.
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
|
@ -2035,31 +2053,16 @@
|
|||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>datvacuumxid</structfield></entry>
|
||||
<entry><structfield>datfrozenxid</structfield></entry>
|
||||
<entry><type>xid</type></entry>
|
||||
<entry></entry>
|
||||
<entry>
|
||||
The transaction ID that was used as cleaning point as of the last vacuum
|
||||
operation. All rows inserted or deleted by transaction IDs before this one
|
||||
have been marked as known good or deleted. This
|
||||
is used to determine when commit-log space can be recycled.
|
||||
If <symbol>InvalidTransactionId</symbol>, then the minimum is unknown and can be
|
||||
determined by scanning <structname>pg_class</>.<structfield>relvacuumxid</>.
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>datminxid</structfield></entry>
|
||||
<entry><type>xid</type></entry>
|
||||
<entry></entry>
|
||||
<entry>
|
||||
The minimum transaction ID present in all tables in this database.
|
||||
All rows inserted by transaction IDs before this one have been
|
||||
relabeled with a permanent (<quote>frozen</>) transaction ID in this
|
||||
database. This is useful to check whether a database must be
|
||||
vacuumed soon to avoid transaction ID wrap-around problems.
|
||||
If <symbol>InvalidTransactionId</symbol>, then the minimum is unknown and can be
|
||||
determined by scanning <structname>pg_class</>.<structfield>relminxid</>.
|
||||
All transaction IDs before this one have been replaced with a permanent
|
||||
(<quote>frozen</>) transaction ID in this database. This is used to
|
||||
track whether the database needs to be vacuumed in order to prevent
|
||||
transaction ID wraparound or to allow <literal>pg_clog</> to be shrunk.
|
||||
It is the minimum of the per-table
|
||||
<structname>pg_class</>.<structfield>relfrozenxid</> values.
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.93 2006/11/04 18:20:27 tgl Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.94 2006/11/05 22:42:07 tgl Exp $ -->
|
||||
|
||||
<chapter Id="runtime-config">
|
||||
<title>Server Configuration</title>
|
||||
|
@ -3217,6 +3217,28 @@ SELECT * FROM parent WHERE key = 2400;
|
|||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="guc-autovacuum-freeze-max-age" xreflabel="autovacuum_freeze_max_age">
|
||||
<term><varname>autovacuum_freeze_max_age</varname> (<type>integer</type>)</term>
|
||||
<indexterm>
|
||||
<primary><varname>autovacuum_freeze_max_age</> configuration parameter</primary>
|
||||
</indexterm>
|
||||
<listitem>
|
||||
<para>
|
||||
Specifies the maximum age (in transactions) that a table's
|
||||
<structname>pg_class</>.<structfield>relfrozenxid</> field can
|
||||
attain before a <command>VACUUM</> operation is forced to prevent
|
||||
transaction ID wraparound within the table. Note that the system
|
||||
will launch autovacuum processes to prevent wraparound even when
|
||||
autovacuum is otherwise disabled.
|
||||
The default is 200000000 (200 million).
|
||||
This parameter can only be set at server start, but the setting
|
||||
can be reduced for individual tables by entries in
|
||||
<structname>pg_autovacuum</>.
|
||||
For more information see <xref linkend="vacuum-for-wraparound">.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="guc-autovacuum-vacuum-cost-delay" xreflabel="autovacuum_vacuum_cost_delay">
|
||||
<term><varname>autovacuum_vacuum_cost_delay</varname> (<type>integer</type>)</term>
|
||||
<indexterm>
|
||||
|
@ -3427,7 +3449,7 @@ SELECT * FROM parent WHERE key = 2400;
|
|||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry id="guc-statement-timeout" xreflabel="statement_timeout">
|
||||
<term><varname>statement_timeout</varname> (<type>integer</type>)</term>
|
||||
<indexterm>
|
||||
|
@ -3444,6 +3466,26 @@ SELECT * FROM parent WHERE key = 2400;
|
|||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="guc-vacuum-freeze-min-age" xreflabel="vacuum_freeze_min_age">
|
||||
<term><varname>vacuum_freeze_min_age</varname> (<type>integer</type>)</term>
|
||||
<indexterm>
|
||||
<primary><varname>vacuum_freeze_min_age</> configuration parameter</primary>
|
||||
</indexterm>
|
||||
<listitem>
|
||||
<para>
|
||||
Specifies the cutoff age (in transactions) that <command>VACUUM</>
|
||||
should use to decide whether to replace transaction IDs with
|
||||
<literal>FrozenXID</> while scanning a table.
|
||||
The default is 100000000 (100 million). Although users can set this
|
||||
value anywhere from zero to 1000000000, <command>VACUUM</> will
|
||||
silently limit the effective value to half the value of <xref
|
||||
linkend="guc-autovacuum-freeze-max-age">, so that there is not an
|
||||
unreasonably short time between forced autovacuums.
|
||||
For more information see <xref linkend="vacuum-for-wraparound">.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
</sect2>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!-- $PostgreSQL: pgsql/doc/src/sgml/maintenance.sgml,v 1.63 2006/10/23 18:10:31 petere Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/maintenance.sgml,v 1.64 2006/11/05 22:42:07 tgl Exp $ -->
|
||||
|
||||
<chapter id="maintenance">
|
||||
<title>Routine Database Maintenance Tasks</title>
|
||||
|
@ -34,7 +34,7 @@
|
|||
<para>
|
||||
The other main category of maintenance task is periodic <quote>vacuuming</>
|
||||
of the database. This activity is discussed in
|
||||
<xref linkend="routine-vacuuming">. Closely related to this updating
|
||||
<xref linkend="routine-vacuuming">. Closely related to this is updating
|
||||
the statistics that will be used by the query planner, as discussed in
|
||||
<xref linkend="vacuum-for-statistics">.
|
||||
</para>
|
||||
|
@ -95,9 +95,10 @@
|
|||
will continue to function as normal, though you will not be able to modify the
|
||||
definition of a table with commands such as <command>ALTER TABLE ADD COLUMN</command>
|
||||
while it is being vacuumed.
|
||||
Beginning in <productname>PostgreSQL</productname> 8.0, there are
|
||||
configuration parameters that can be adjusted to further reduce the
|
||||
performance impact of background vacuuming. See
|
||||
Also, <command>VACUUM</command> requires a substantial amount of I/O
|
||||
traffic, which can cause poor performance for other active sessions.
|
||||
There are configuration parameters that can be adjusted to reduce the
|
||||
performance impact of background vacuuming — see
|
||||
<xref linkend="runtime-config-resource-vacuum-cost">.
|
||||
</para>
|
||||
|
||||
|
@ -179,9 +180,9 @@
|
|||
Recommended practice for most sites is to schedule a database-wide
|
||||
<command>VACUUM</> once a day at a low-usage time of day,
|
||||
supplemented by more frequent vacuuming of heavily-updated tables
|
||||
if necessary. (Some installations with an extremely high
|
||||
rate of data modification <command>VACUUM</command> busy tables as
|
||||
often as once every few minutes.) If you have multiple databases
|
||||
if necessary. (Some installations with extremely high update rates
|
||||
vacuum their busiest tables as often as once every few minutes.)
|
||||
If you have multiple databases
|
||||
in a cluster, don't forget to <command>VACUUM</command> each one;
|
||||
the program <xref linkend="app-vacuumdb" endterm="app-vacuumdb-title">
|
||||
may be helpful.
|
||||
|
@ -296,29 +297,15 @@
|
|||
transactions that were in the past appear to be in the future — which
|
||||
means their outputs become invisible. In short, catastrophic data loss.
|
||||
(Actually the data is still there, but that's cold comfort if you can't
|
||||
get at it.) To avoid this, it is <emphasis>necessary to vacuum every table
|
||||
in every database at least once every billion transactions</emphasis>.
|
||||
get at it.) To avoid this, it is necessary to vacuum every table
|
||||
in every database at least once every two billion transactions.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In practice this isn't an onerous requirement, but since the
|
||||
consequences of failing to meet it can be complete data loss (not
|
||||
just wasted disk space or slow performance), some special provisions
|
||||
have been made to help database administrators avoid disaster.
|
||||
For each database in the cluster, <productname>PostgreSQL</productname>
|
||||
keeps track of the time of the last database-wide <command>VACUUM</>.
|
||||
When any database approaches the billion-transaction danger level,
|
||||
the system begins to emit warning messages. If nothing is done, it
|
||||
will eventually shut down normal operations until appropriate
|
||||
manual maintenance is done. The remainder of this
|
||||
section gives the details.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The new approach to XID comparison distinguishes two special XIDs,
|
||||
numbers 1 and 2 (<literal>BootstrapXID</> and
|
||||
<literal>FrozenXID</>). These two XIDs are always considered older
|
||||
than every normal XID. Normal XIDs (those greater than 2) are
|
||||
The reason that periodic vacuuming solves the problem is that
|
||||
<productname>PostgreSQL</productname> distinguishes a special XID
|
||||
<literal>FrozenXID</>. This XID is always considered older
|
||||
than every normal XID. Normal XIDs are
|
||||
compared using modulo-2<superscript>31</> arithmetic. This means
|
||||
that for every normal XID, there are two billion XIDs that are
|
||||
<quote>older</> and two billion that are <quote>newer</>; another
|
||||
|
@ -333,78 +320,128 @@
|
|||
two-billion-transactions-old mark. Once they are assigned this
|
||||
special XID, they will appear to be <quote>in the past</> to all
|
||||
normal transactions regardless of wraparound issues, and so such
|
||||
row versions will be good until deleted, no matter how long that is. This
|
||||
reassignment of XID is handled by <command>VACUUM</>.
|
||||
row versions will be good until deleted, no matter how long that is.
|
||||
This reassignment of old XIDs is handled by <command>VACUUM</>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<command>VACUUM</>'s normal policy is to reassign <literal>FrozenXID</>
|
||||
to any row version with a normal XID more than one billion transactions in the
|
||||
past. This policy preserves the original insertion XID until it is not
|
||||
likely to be of interest anymore. (In fact, most row versions will probably
|
||||
live and die without ever being <quote>frozen</>.) With this policy,
|
||||
the maximum safe interval between <command>VACUUM</> runs on any table
|
||||
is exactly one billion transactions: if you wait longer, it's possible
|
||||
that a row version that was not quite old enough to be reassigned last time
|
||||
is now more than two billion transactions old and has wrapped around
|
||||
into the future — i.e., is lost to you. (Of course, it'll reappear
|
||||
after another two billion transactions, but that's no help.)
|
||||
<command>VACUUM</>'s behavior is controlled by the configuration parameter
|
||||
<xref linkend="guc-vacuum-freeze-min-age">: any XID older than
|
||||
<varname>vacuum_freeze_min_age</> transactions is replaced by
|
||||
<literal>FrozenXID</>. Larger values of <varname>vacuum_freeze_min_age</>
|
||||
preserve transactional information longer, while smaller values increase
|
||||
the number of transactions that can elapse before the table must be
|
||||
vacuumed again.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Since periodic <command>VACUUM</> runs are needed anyway for the reasons
|
||||
described earlier, it's unlikely that any table would not be vacuumed
|
||||
for as long as a billion transactions. But to help administrators ensure
|
||||
this constraint is met, <command>VACUUM</> stores transaction ID
|
||||
statistics in the system table <literal>pg_database</>. In particular,
|
||||
the <literal>datfrozenxid</> column of a database's
|
||||
<literal>pg_database</> row is updated at the completion of any
|
||||
database-wide <command>VACUUM</command> operation (i.e.,
|
||||
<command>VACUUM</> that does not
|
||||
name a specific table). The value stored in this field is the freeze
|
||||
cutoff XID that was used by that <command>VACUUM</> command. All normal
|
||||
The maximum time that a table can go unvacuumed is two billion
|
||||
transactions minus the <varname>vacuum_freeze_min_age</> that was used
|
||||
when it was last vacuumed.
|
||||
If it were to go unvacuumed for longer than that,
|
||||
data loss could result. To ensure that this does not
|
||||
happen, the <firstterm>autovacuum</> facility described in
|
||||
<xref linkend="autovacuum"> is invoked on any table
|
||||
that might contain XIDs older than the age specified by the
|
||||
configuration parameter
|
||||
<xref linkend="guc-autovacuum-freeze-max-age">. (This will happen
|
||||
even if autovacuum is otherwise disabled.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This implies that if a table is not otherwise vacuumed,
|
||||
autovacuum will be invoked on it approximately once every
|
||||
<varname>autovacuum_freeze_max_age</> minus
|
||||
<varname>vacuum_freeze_min_age</> transactions.
|
||||
For tables that are regularly vacuumed for space reclamation purposes,
|
||||
this is of little importance. However, for static tables
|
||||
(including tables that receive inserts, but no updates or deletes),
|
||||
there is no need for vacuuming for space reclamation, and so it can
|
||||
be useful to try to maximize the interval between forced autovacuums
|
||||
on very large static tables. Obviously one can do this either by
|
||||
increasing <varname>autovacuum_freeze_max_age</> or by decreasing
|
||||
<varname>vacuum_freeze_min_age</>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The sole disadvantage of increasing <varname>autovacuum_freeze_max_age</>
|
||||
is that the <filename>pg_clog</> subdirectory of the database cluster
|
||||
will take more space, because it must store the commit status for all
|
||||
transactions back to the <varname>autovacuum_freeze_max_age</> horizon.
|
||||
The commit status uses two bits per transaction, so if
|
||||
<varname>autovacuum_freeze_max_age</> has its maximum allowed value of
|
||||
a little less than two billion, <filename>pg_clog</> can be expected to
|
||||
grow to about half a gigabyte. If this is trivial compared to your
|
||||
total database size, setting <varname>autovacuum_freeze_max_age</> to
|
||||
its maximum allowed value is recommended. Otherwise, set it depending
|
||||
on what you are willing to allow for <filename>pg_clog</> storage.
|
||||
(The default, 200 million transactions, translates to about 50MB of
|
||||
<filename>pg_clog</> storage.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
One disadvantage of decreasing <varname>vacuum_freeze_min_age</> is that
|
||||
it may cause <command>VACUUM</> to do useless work: changing a table row's
|
||||
XID to <literal>FrozenXID</> is a waste of time if the row is modified
|
||||
soon thereafter (causing it to acquire a new XID). So the setting should
|
||||
be large enough that rows are not frozen until they are unlikely to change
|
||||
any more. Another disadvantage of decreasing this setting is
|
||||
that details about exactly which transaction inserted or modified a
|
||||
row will be lost sooner. This information sometimes comes in handy,
|
||||
particularly when trying to analyze what went wrong after a database
|
||||
failure. For these two reasons, decreasing this setting is not
|
||||
recommended except for completely static tables.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To track the age of the oldest XIDs in a database,
|
||||
<command>VACUUM</> stores XID
|
||||
statistics in the system tables <structname>pg_class</> and
|
||||
<structname>pg_database</>. In particular,
|
||||
the <structfield>relfrozenxid</> column of a table's
|
||||
<structname>pg_class</> row contains the freeze cutoff XID that was used
|
||||
by the last <command>VACUUM</> for that table. All normal
|
||||
XIDs older than this cutoff XID are guaranteed to have been replaced by
|
||||
<literal>FrozenXID</> within that database. A convenient way to
|
||||
examine this information is to execute the query
|
||||
<literal>FrozenXID</> within the table. Similarly,
|
||||
the <structfield>datfrozenxid</> column of a database's
|
||||
<structname>pg_database</> row is a lower bound on the normal XIDs
|
||||
appearing in that database — it is just the minimum of the
|
||||
per-table <structfield>relfrozenxid</> values within the database.
|
||||
A convenient way to
|
||||
examine this information is to execute queries such as
|
||||
|
||||
<programlisting>
|
||||
SELECT relname, age(relfrozenxid) FROM pg_class WHERE relkind = 'r';
|
||||
SELECT datname, age(datfrozenxid) FROM pg_database;
|
||||
</programlisting>
|
||||
|
||||
The <literal>age</> column measures the number of transactions from the
|
||||
cutoff XID to the current transaction's XID.
|
||||
cutoff XID to the current transaction's XID. Immediately after a
|
||||
<command>VACUUM</>, <literal>age(relfrozenxid)</> should be a little
|
||||
more than the <varname>vacuum_freeze_min_age</> setting that was used
|
||||
(more by the number of transactions started since the <command>VACUUM</>
|
||||
started). If <literal>age(relfrozenxid)</> exceeds
|
||||
<varname>autovacuum_freeze_max_age</>, an autovacuum will soon be forced
|
||||
for the table.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
With the standard freezing policy, the <literal>age</> column will start
|
||||
at one billion for a freshly-vacuumed database. When the <literal>age</>
|
||||
approaches two billion, the database must be vacuumed again to avoid
|
||||
risk of wraparound failures. Recommended practice is to <command>VACUUM</command> each
|
||||
database at least once every half-a-billion (500 million) transactions,
|
||||
so as to provide plenty of safety margin. To help meet this rule,
|
||||
each database-wide <command>VACUUM</> automatically delivers a warning
|
||||
if there are any <literal>pg_database</> entries showing an
|
||||
<literal>age</> of more than 1.5 billion transactions, for example:
|
||||
If for some reason autovacuum fails to clear old XIDs from a table,
|
||||
the system will begin to emit warning messages like this when the
|
||||
database's oldest XIDs reach ten million transactions from the wraparound
|
||||
point:
|
||||
|
||||
<programlisting>
|
||||
play=# VACUUM;
|
||||
WARNING: database "mydb" must be vacuumed within 177009986 transactions
|
||||
HINT: To avoid a database shutdown, execute a full-database VACUUM in "mydb".
|
||||
VACUUM
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the warnings emitted by <command>VACUUM</> go ignored, then
|
||||
<productname>PostgreSQL</productname> will begin to emit a warning
|
||||
like the above on every transaction start once there are fewer than 10
|
||||
million transactions left until wraparound. If those warnings also are
|
||||
If these warnings are
|
||||
ignored, the system will shut down and refuse to execute any new
|
||||
transactions once there are fewer than 1 million transactions left
|
||||
until wraparound:
|
||||
|
||||
<programlisting>
|
||||
play=# select 2+2;
|
||||
ERROR: database is shut down to avoid wraparound data loss in database "mydb"
|
||||
HINT: Stop the postmaster and use a standalone backend to VACUUM in "mydb".
|
||||
</programlisting>
|
||||
|
@ -419,32 +456,6 @@ HINT: Stop the postmaster and use a standalone backend to VACUUM in "mydb".
|
|||
page for details about using a single-user backend.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<command>VACUUM</> with the <command>FREEZE</> option uses a more
|
||||
aggressive freezing policy: row versions are frozen if they are old enough
|
||||
to be considered good by all open transactions. In particular, if a
|
||||
<command>VACUUM FREEZE</> is performed in an otherwise-idle
|
||||
database, it is guaranteed that <emphasis>all</> row versions in that
|
||||
database will be frozen. Hence, as long as the database is not
|
||||
modified in any way, it will not need subsequent vacuuming to avoid
|
||||
transaction ID wraparound problems. This technique is used by
|
||||
<command>initdb</> to prepare the <literal>template0</> database.
|
||||
It should also be used to prepare any user-created databases that
|
||||
are to be marked <literal>datallowconn</> = <literal>false</> in
|
||||
<literal>pg_database</>, since there isn't any convenient way to
|
||||
<command>VACUUM</command> a database that you can't connect to.
|
||||
</para>
|
||||
|
||||
<warning>
|
||||
<para>
|
||||
A database that is marked <literal>datallowconn</> = <literal>false</>
|
||||
in <literal>pg_database</> is assumed to be properly frozen; the
|
||||
automatic warnings and wraparound protection shutdown do not take
|
||||
such databases into account. Therefore it's up to you to ensure
|
||||
you've correctly frozen a database before you mark it with
|
||||
<literal>datallowconn</> = <literal>false</>.
|
||||
</para>
|
||||
</warning>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="autovacuum">
|
||||
|
@ -471,19 +482,17 @@ HINT: Stop the postmaster and use a standalone backend to VACUUM in "mydb".
|
|||
|
||||
<para>
|
||||
The autovacuum daemon, when enabled, runs every <xref
|
||||
linkend="guc-autovacuum-naptime"> seconds and determines which database
|
||||
to process. Any database which is close to transaction ID wraparound
|
||||
is immediately processed. In this case, autovacuum issues a
|
||||
database-wide <command>VACUUM</command> call, or <command>VACUUM
|
||||
FREEZE</command> if it's a template database, and then terminates. If
|
||||
no database fulfills this criterion, the one that was least recently
|
||||
processed by autovacuum is chosen. In this case each table in
|
||||
the selected database is checked, and individual <command>VACUUM</command>
|
||||
or <command>ANALYZE</command> commands are issued as needed.
|
||||
linkend="guc-autovacuum-naptime"> seconds. On each run, it selects
|
||||
one database to process and checks each table within that database.
|
||||
<command>VACUUM</command> or <command>ANALYZE</command> commands are
|
||||
issued as needed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For each table, two conditions are used to determine which operation(s)
|
||||
Tables whose <structfield>relfrozenxid</> value is more than
|
||||
<varname>autovacuum_freeze_max_age</> transactions old are always
|
||||
vacuumed. Otherwise,
|
||||
two conditions are used to determine which operation(s)
|
||||
to apply. If the number of obsolete tuples since the last
|
||||
<command>VACUUM</command> exceeds the <quote>vacuum threshold</quote>, the
|
||||
table is vacuumed. The vacuum threshold is defined as:
|
||||
|
@ -521,21 +530,28 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
|
|||
</para>
|
||||
|
||||
<para>
|
||||
Besides the base threshold values and scale factors, there are three
|
||||
Besides the base threshold values and scale factors, there are five
|
||||
more parameters that can be set for each table in
|
||||
<structname>pg_autovacuum</structname>.
|
||||
The first, <structname>pg_autovacuum</>.<structfield>enabled</>,
|
||||
can be set to <literal>false</literal> to instruct the autovacuum daemon
|
||||
to skip that particular table entirely. In this case
|
||||
autovacuum will only touch the table when it vacuums the entire database
|
||||
autovacuum will only touch the table if it must do so
|
||||
to prevent transaction ID wraparound.
|
||||
The other two parameters, the vacuum cost delay
|
||||
The next two parameters, the vacuum cost delay
|
||||
(<structname>pg_autovacuum</structname>.<structfield>vac_cost_delay</structfield>)
|
||||
and the vacuum cost limit
|
||||
(<structname>pg_autovacuum</structname>.<structfield>vac_cost_limit</structfield>),
|
||||
are used to set table-specific values for the
|
||||
<xref linkend="runtime-config-resource-vacuum-cost" endterm="runtime-config-resource-vacuum-cost-title">
|
||||
feature.
|
||||
The last two parameters,
|
||||
(<structname>pg_autovacuum</structname>.<structfield>freeze_min_age</structfield>)
|
||||
and
|
||||
(<structname>pg_autovacuum</structname>.<structfield>freeze_max_age</structfield>),
|
||||
are used to set table-specific values for
|
||||
<xref linkend="guc-vacuum-freeze-min-age"> and
|
||||
<xref linkend="guc-autovacuum-freeze-max-age"> respectively.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!-- $PostgreSQL: pgsql/doc/src/sgml/manage-ag.sgml,v 2.48 2006/09/16 00:30:14 momjian Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/manage-ag.sgml,v 2.49 2006/11/05 22:42:07 tgl Exp $ -->
|
||||
|
||||
<chapter id="managing-databases">
|
||||
<title>Managing Databases</title>
|
||||
|
@ -249,19 +249,6 @@ createdb -T template0 <replaceable>dbname</>
|
|||
should always be marked with <literal>datistemplate = true</>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
After preparing a template database, or making any changes to one,
|
||||
it is a good idea to perform <command>VACUUM FREEZE</> in that
|
||||
database. If this is done when there are no other open transactions
|
||||
in the same database, then it is guaranteed that all rows in the
|
||||
database are <quote>frozen</> and will not be subject to transaction
|
||||
ID wraparound problems. This is particularly important for a database
|
||||
that will have <literal>datallowconn</literal> set to false, since it
|
||||
will be impossible to do routine maintenance <command>VACUUM</> in
|
||||
such a database.
|
||||
See <xref linkend="vacuum-for-wraparound"> for more information.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
<literal>template1</> and <literal>template0</> do not have any special
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
$PostgreSQL: pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.42 2006/10/31 01:52:31 neilc Exp $
|
||||
$PostgreSQL: pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.43 2006/11/05 22:42:07 tgl Exp $
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
|
@ -20,8 +20,8 @@ PostgreSQL documentation
|
|||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ <replaceable class="PARAMETER">table</replaceable> ]
|
||||
VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table</replaceable> [ (<replaceable class="PARAMETER">column</replaceable> [, ...] ) ] ]
|
||||
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ <replaceable class="PARAMETER">table</replaceable> ]
|
||||
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table</replaceable> [ (<replaceable class="PARAMETER">column</replaceable> [, ...] ) ] ]
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
@ -62,21 +62,6 @@ VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">ta
|
|||
blocks. This form is much slower and requires an exclusive lock on each
|
||||
table while it is being processed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>FREEZE</literal> is a special-purpose option that
|
||||
causes tuples to be marked <quote>frozen</quote> as soon as possible,
|
||||
rather than waiting until they are quite old. If this is done when there
|
||||
are no other open transactions in the same database, then it is guaranteed
|
||||
that all tuples in the database are <quote>frozen</> and will not be
|
||||
subject to transaction ID wraparound problems, no matter how long the
|
||||
database is left unvacuumed.
|
||||
<literal>FREEZE</literal> is not recommended for routine use. Its only
|
||||
intended usage is in connection with preparation of user-defined template
|
||||
databases, or other databases that are completely read-only and will not
|
||||
receive routine maintenance <command>VACUUM</> operations.
|
||||
See <xref linkend="maintenance"> for details.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -98,6 +83,11 @@ VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">ta
|
|||
<listitem>
|
||||
<para>
|
||||
Selects aggressive <quote>freezing</quote> of tuples.
|
||||
Specifying <literal>FREEZE</literal> is equivalent to performing
|
||||
<command>VACUUM</command> with the
|
||||
<xref linkend="guc-vacuum-freeze-min-age"> parameter
|
||||
set to zero. The <literal>FREEZE</literal> option is deprecated and
|
||||
will be removed in a future release; set the parameter instead.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -185,6 +175,13 @@ VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">ta
|
|||
it is sometimes advisable to use the cost-based vacuum delay feature.
|
||||
See <xref linkend="runtime-config-resource-vacuum-cost"> for details.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<productname>PostgreSQL</productname> includes an <quote>autovacuum</>
|
||||
facility which can automate routine vacuum maintenance. For more
|
||||
information about automatic and manual vacuuming, see
|
||||
<xref linkend="routine-vacuuming">.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.220 2006/10/04 00:29:48 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.221 2006/11/05 22:42:07 tgl Exp $
|
||||
*
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
|
@ -2809,6 +2809,166 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* heap_freeze_tuple
|
||||
*
|
||||
* Check to see whether any of the XID fields of a tuple (xmin, xmax, xvac)
|
||||
* are older than the specified cutoff XID. If so, replace them with
|
||||
* FrozenTransactionId or InvalidTransactionId as appropriate, and return
|
||||
* TRUE. Return FALSE if nothing was changed.
|
||||
*
|
||||
* It is assumed that the caller has checked the tuple with
|
||||
* HeapTupleSatisfiesVacuum() and determined that it is not HEAPTUPLE_DEAD
|
||||
* (else we should be removing the tuple, not freezing it).
|
||||
*
|
||||
* NB: cutoff_xid *must* be <= the current global xmin, to ensure that any
|
||||
* XID older than it could neither be running nor seen as running by any
|
||||
* open transaction. This ensures that the replacement will not change
|
||||
* anyone's idea of the tuple state. Also, since we assume the tuple is
|
||||
* not HEAPTUPLE_DEAD, the fact that an XID is not still running allows us
|
||||
* to assume that it is either committed good or aborted, as appropriate;
|
||||
* so we need no external state checks to decide what to do. (This is good
|
||||
* because this function is applied during WAL recovery, when we don't have
|
||||
* access to any such state, and can't depend on the hint bits to be set.)
|
||||
*
|
||||
* In lazy VACUUM, we call this while initially holding only a shared lock
|
||||
* on the tuple's buffer. If any change is needed, we trade that in for an
|
||||
* exclusive lock before making the change. Caller should pass the buffer ID
|
||||
* if shared lock is held, InvalidBuffer if exclusive lock is already held.
|
||||
*
|
||||
* Note: it might seem we could make the changes without exclusive lock, since
|
||||
* TransactionId read/write is assumed atomic anyway. However there is a race
|
||||
* condition: someone who just fetched an old XID that we overwrite here could
|
||||
* conceivably not finish checking the XID against pg_clog before we finish
|
||||
* the VACUUM and perhaps truncate off the part of pg_clog he needs. Getting
|
||||
* exclusive lock ensures no other backend is in process of checking the
|
||||
* tuple status. Also, getting exclusive lock makes it safe to adjust the
|
||||
* infomask bits.
|
||||
*/
|
||||
bool
|
||||
heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
|
||||
Buffer buf)
|
||||
{
|
||||
bool changed = false;
|
||||
TransactionId xid;
|
||||
|
||||
xid = HeapTupleHeaderGetXmin(tuple);
|
||||
if (TransactionIdIsNormal(xid) &&
|
||||
TransactionIdPrecedes(xid, cutoff_xid))
|
||||
{
|
||||
if (buf != InvalidBuffer)
|
||||
{
|
||||
/* trade in share lock for exclusive lock */
|
||||
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
|
||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||
buf = InvalidBuffer;
|
||||
}
|
||||
HeapTupleHeaderSetXmin(tuple, FrozenTransactionId);
|
||||
/*
|
||||
* Might as well fix the hint bits too; usually XMIN_COMMITTED will
|
||||
* already be set here, but there's a small chance not.
|
||||
*/
|
||||
Assert(!(tuple->t_infomask & HEAP_XMIN_INVALID));
|
||||
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we release shared lock, it's possible for someone else to change
|
||||
* xmax before we get the lock back, so repeat the check after acquiring
|
||||
* exclusive lock. (We don't need this pushup for xmin, because only
|
||||
* VACUUM could be interested in changing an existing tuple's xmin,
|
||||
* and there's only one VACUUM allowed on a table at a time.)
|
||||
*/
|
||||
recheck_xmax:
|
||||
if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
|
||||
{
|
||||
xid = HeapTupleHeaderGetXmax(tuple);
|
||||
if (TransactionIdIsNormal(xid) &&
|
||||
TransactionIdPrecedes(xid, cutoff_xid))
|
||||
{
|
||||
if (buf != InvalidBuffer)
|
||||
{
|
||||
/* trade in share lock for exclusive lock */
|
||||
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
|
||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||
buf = InvalidBuffer;
|
||||
goto recheck_xmax; /* see comment above */
|
||||
}
|
||||
HeapTupleHeaderSetXmax(tuple, InvalidTransactionId);
|
||||
/*
|
||||
* The tuple might be marked either XMAX_INVALID or
|
||||
* XMAX_COMMITTED + LOCKED. Normalize to INVALID just to be
|
||||
* sure no one gets confused.
|
||||
*/
|
||||
tuple->t_infomask &= ~HEAP_XMAX_COMMITTED;
|
||||
tuple->t_infomask |= HEAP_XMAX_INVALID;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*----------
|
||||
* XXX perhaps someday we should zero out very old MultiXactIds here?
|
||||
*
|
||||
* The only way a stale MultiXactId could pose a problem is if a
|
||||
* tuple, having once been multiply-share-locked, is not touched by
|
||||
* any vacuum or attempted lock or deletion for just over 4G MultiXact
|
||||
* creations, and then in the probably-narrow window where its xmax
|
||||
* is again a live MultiXactId, someone tries to lock or delete it.
|
||||
* Even then, another share-lock attempt would work fine. An
|
||||
* exclusive-lock or delete attempt would face unexpected delay, or
|
||||
* in the very worst case get a deadlock error. This seems an
|
||||
* extremely low-probability scenario with minimal downside even if
|
||||
* it does happen, so for now we don't do the extra bookkeeping that
|
||||
* would be needed to clean out MultiXactIds.
|
||||
*----------
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Although xvac per se could only be set by VACUUM, it shares physical
|
||||
* storage space with cmax, and so could be wiped out by someone setting
|
||||
* xmax. Hence recheck after changing lock, same as for xmax itself.
|
||||
*/
|
||||
recheck_xvac:
|
||||
if (tuple->t_infomask & HEAP_MOVED)
|
||||
{
|
||||
xid = HeapTupleHeaderGetXvac(tuple);
|
||||
if (TransactionIdIsNormal(xid) &&
|
||||
TransactionIdPrecedes(xid, cutoff_xid))
|
||||
{
|
||||
if (buf != InvalidBuffer)
|
||||
{
|
||||
/* trade in share lock for exclusive lock */
|
||||
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
|
||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||
buf = InvalidBuffer;
|
||||
goto recheck_xvac; /* see comment above */
|
||||
}
|
||||
/*
|
||||
* If a MOVED_OFF tuple is not dead, the xvac transaction must
|
||||
* have failed; whereas a non-dead MOVED_IN tuple must mean the
|
||||
* xvac transaction succeeded.
|
||||
*/
|
||||
if (tuple->t_infomask & HEAP_MOVED_OFF)
|
||||
HeapTupleHeaderSetXvac(tuple, InvalidTransactionId);
|
||||
else
|
||||
HeapTupleHeaderSetXvac(tuple, FrozenTransactionId);
|
||||
/*
|
||||
* Might as well fix the hint bits too; usually XMIN_COMMITTED will
|
||||
* already be set here, but there's a small chance not.
|
||||
*/
|
||||
Assert(!(tuple->t_infomask & HEAP_XMIN_INVALID));
|
||||
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------
|
||||
* heap_markpos - mark scan position
|
||||
* ----------------
|
||||
|
@ -2877,6 +3037,9 @@ heap_restrpos(HeapScanDesc scan)
|
|||
/*
|
||||
* Perform XLogInsert for a heap-clean operation. Caller must already
|
||||
* have modified the buffer and marked it dirty.
|
||||
*
|
||||
* Note: for historical reasons, the entries in the unused[] array should
|
||||
* be zero-based tuple indexes, not one-based.
|
||||
*/
|
||||
XLogRecPtr
|
||||
log_heap_clean(Relation reln, Buffer buffer, OffsetNumber *unused, int uncnt)
|
||||
|
@ -2920,6 +3083,57 @@ log_heap_clean(Relation reln, Buffer buffer, OffsetNumber *unused, int uncnt)
|
|||
return recptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform XLogInsert for a heap-freeze operation. Caller must already
|
||||
* have modified the buffer and marked it dirty.
|
||||
*
|
||||
* Unlike log_heap_clean(), the offsets[] entries are one-based.
|
||||
*/
|
||||
XLogRecPtr
|
||||
log_heap_freeze(Relation reln, Buffer buffer,
|
||||
TransactionId cutoff_xid,
|
||||
OffsetNumber *offsets, int offcnt)
|
||||
{
|
||||
xl_heap_freeze xlrec;
|
||||
XLogRecPtr recptr;
|
||||
XLogRecData rdata[2];
|
||||
|
||||
/* Caller should not call me on a temp relation */
|
||||
Assert(!reln->rd_istemp);
|
||||
|
||||
xlrec.node = reln->rd_node;
|
||||
xlrec.block = BufferGetBlockNumber(buffer);
|
||||
xlrec.cutoff_xid = cutoff_xid;
|
||||
|
||||
rdata[0].data = (char *) &xlrec;
|
||||
rdata[0].len = SizeOfHeapFreeze;
|
||||
rdata[0].buffer = InvalidBuffer;
|
||||
rdata[0].next = &(rdata[1]);
|
||||
|
||||
/*
|
||||
* The tuple-offsets array is not actually in the buffer, but pretend
|
||||
* that it is. When XLogInsert stores the whole buffer, the offsets array
|
||||
* need not be stored too.
|
||||
*/
|
||||
if (offcnt > 0)
|
||||
{
|
||||
rdata[1].data = (char *) offsets;
|
||||
rdata[1].len = offcnt * sizeof(OffsetNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
rdata[1].data = NULL;
|
||||
rdata[1].len = 0;
|
||||
}
|
||||
rdata[1].buffer = buffer;
|
||||
rdata[1].buffer_std = true;
|
||||
rdata[1].next = NULL;
|
||||
|
||||
recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_FREEZE, rdata);
|
||||
|
||||
return recptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform XLogInsert for a heap-update operation. Caller must already
|
||||
* have modified the buffer(s) and marked them dirty.
|
||||
|
@ -3057,6 +3271,7 @@ heap_xlog_clean(XLogRecPtr lsn, XLogRecord *record)
|
|||
|
||||
while (unused < unend)
|
||||
{
|
||||
/* unused[] entries are zero-based */
|
||||
lp = PageGetItemId(page, *unused + 1);
|
||||
lp->lp_flags &= ~LP_USED;
|
||||
unused++;
|
||||
|
@ -3071,6 +3286,55 @@ heap_xlog_clean(XLogRecPtr lsn, XLogRecord *record)
|
|||
UnlockReleaseBuffer(buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
heap_xlog_freeze(XLogRecPtr lsn, XLogRecord *record)
|
||||
{
|
||||
xl_heap_freeze *xlrec = (xl_heap_freeze *) XLogRecGetData(record);
|
||||
TransactionId cutoff_xid = xlrec->cutoff_xid;
|
||||
Relation reln;
|
||||
Buffer buffer;
|
||||
Page page;
|
||||
|
||||
if (record->xl_info & XLR_BKP_BLOCK_1)
|
||||
return;
|
||||
|
||||
reln = XLogOpenRelation(xlrec->node);
|
||||
buffer = XLogReadBuffer(reln, xlrec->block, false);
|
||||
if (!BufferIsValid(buffer))
|
||||
return;
|
||||
page = (Page) BufferGetPage(buffer);
|
||||
|
||||
if (XLByteLE(lsn, PageGetLSN(page)))
|
||||
{
|
||||
UnlockReleaseBuffer(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (record->xl_len > SizeOfHeapFreeze)
|
||||
{
|
||||
OffsetNumber *offsets;
|
||||
OffsetNumber *offsets_end;
|
||||
|
||||
offsets = (OffsetNumber *) ((char *) xlrec + SizeOfHeapFreeze);
|
||||
offsets_end = (OffsetNumber *) ((char *) xlrec + record->xl_len);
|
||||
|
||||
while (offsets < offsets_end)
|
||||
{
|
||||
/* offsets[] entries are one-based */
|
||||
ItemId lp = PageGetItemId(page, *offsets);
|
||||
HeapTupleHeader tuple = (HeapTupleHeader) PageGetItem(page, lp);
|
||||
|
||||
(void) heap_freeze_tuple(tuple, cutoff_xid, InvalidBuffer);
|
||||
offsets++;
|
||||
}
|
||||
}
|
||||
|
||||
PageSetLSN(page, lsn);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
MarkBufferDirty(buffer);
|
||||
UnlockReleaseBuffer(buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
heap_xlog_newpage(XLogRecPtr lsn, XLogRecord *record)
|
||||
{
|
||||
|
@ -3546,6 +3810,18 @@ heap_redo(XLogRecPtr lsn, XLogRecord *record)
|
|||
elog(PANIC, "heap_redo: unknown op code %u", info);
|
||||
}
|
||||
|
||||
void
|
||||
heap2_redo(XLogRecPtr lsn, XLogRecord *record)
|
||||
{
|
||||
uint8 info = record->xl_info & ~XLR_INFO_MASK;
|
||||
|
||||
info &= XLOG_HEAP_OPMASK;
|
||||
if (info == XLOG_HEAP2_FREEZE)
|
||||
heap_xlog_freeze(lsn, record);
|
||||
else
|
||||
elog(PANIC, "heap2_redo: unknown op code %u", info);
|
||||
}
|
||||
|
||||
static void
|
||||
out_target(StringInfo buf, xl_heaptid *target)
|
||||
{
|
||||
|
@ -3645,3 +3921,22 @@ heap_desc(StringInfo buf, uint8 xl_info, char *rec)
|
|||
else
|
||||
appendStringInfo(buf, "UNKNOWN");
|
||||
}
|
||||
|
||||
void
|
||||
heap2_desc(StringInfo buf, uint8 xl_info, char *rec)
|
||||
{
|
||||
uint8 info = xl_info & ~XLR_INFO_MASK;
|
||||
|
||||
info &= XLOG_HEAP_OPMASK;
|
||||
if (info == XLOG_HEAP2_FREEZE)
|
||||
{
|
||||
xl_heap_freeze *xlrec = (xl_heap_freeze *) rec;
|
||||
|
||||
appendStringInfo(buf, "freeze: rel %u/%u/%u; blk %u; cutoff %u",
|
||||
xlrec->node.spcNode, xlrec->node.dbNode,
|
||||
xlrec->node.relNode, xlrec->block,
|
||||
xlrec->cutoff_xid);
|
||||
}
|
||||
else
|
||||
appendStringInfo(buf, "UNKNOWN");
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.40 2006/10/04 00:29:49 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.41 2006/11/05 22:42:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -69,6 +69,7 @@ static SlruCtlData ClogCtlData;
|
|||
static int ZeroCLOGPage(int pageno, bool writeXlog);
|
||||
static bool CLOGPagePrecedes(int page1, int page2);
|
||||
static void WriteZeroPageXlogRec(int pageno);
|
||||
static void WriteTruncateXlogRec(int pageno);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -309,16 +310,17 @@ ExtendCLOG(TransactionId newestXact)
|
|||
/*
|
||||
* Remove all CLOG segments before the one holding the passed transaction ID
|
||||
*
|
||||
* When this is called, we know that the database logically contains no
|
||||
* reference to transaction IDs older than oldestXact. However, we must
|
||||
* not truncate the CLOG until we have performed a checkpoint, to ensure
|
||||
* that no such references remain on disk either; else a crash just after
|
||||
* the truncation might leave us with a problem. Since CLOG segments hold
|
||||
* a large number of transactions, the opportunity to actually remove a
|
||||
* segment is fairly rare, and so it seems best not to do the checkpoint
|
||||
* unless we have confirmed that there is a removable segment. Therefore
|
||||
* we issue the checkpoint command here, not in higher-level code as might
|
||||
* seem cleaner.
|
||||
* Before removing any CLOG data, we must flush XLOG to disk, to ensure
|
||||
* that any recently-emitted HEAP_FREEZE records have reached disk; otherwise
|
||||
* a crash and restart might leave us with some unfrozen tuples referencing
|
||||
* removed CLOG data. We choose to emit a special TRUNCATE XLOG record too.
|
||||
* Replaying the deletion from XLOG is not critical, since the files could
|
||||
* just as well be removed later, but doing so prevents a long-running hot
|
||||
* standby server from acquiring an unreasonably bloated CLOG directory.
|
||||
*
|
||||
* Since CLOG segments hold a large number of transactions, the opportunity to
|
||||
* actually remove a segment is fairly rare, and so it seems best not to do
|
||||
* the XLOG flush unless we have confirmed that there is a removable segment.
|
||||
*/
|
||||
void
|
||||
TruncateCLOG(TransactionId oldestXact)
|
||||
|
@ -335,8 +337,8 @@ TruncateCLOG(TransactionId oldestXact)
|
|||
if (!SlruScanDirectory(ClogCtl, cutoffPage, false))
|
||||
return; /* nothing to remove */
|
||||
|
||||
/* Perform a CHECKPOINT */
|
||||
RequestCheckpoint(true, false);
|
||||
/* Write XLOG record and flush XLOG to disk */
|
||||
WriteTruncateXlogRec(cutoffPage);
|
||||
|
||||
/* Now we can remove the old CLOG segment(s) */
|
||||
SimpleLruTruncate(ClogCtl, cutoffPage);
|
||||
|
@ -386,6 +388,29 @@ WriteZeroPageXlogRec(int pageno)
|
|||
(void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a TRUNCATE xlog record
|
||||
*
|
||||
* We must flush the xlog record to disk before returning --- see notes
|
||||
* in TruncateCLOG().
|
||||
*
|
||||
* Note: xlog record is marked as outside transaction control, since we
|
||||
* want it to be redone whether the invoking transaction commits or not.
|
||||
*/
|
||||
static void
|
||||
WriteTruncateXlogRec(int pageno)
|
||||
{
|
||||
XLogRecData rdata;
|
||||
XLogRecPtr recptr;
|
||||
|
||||
rdata.data = (char *) (&pageno);
|
||||
rdata.len = sizeof(int);
|
||||
rdata.buffer = InvalidBuffer;
|
||||
rdata.next = NULL;
|
||||
recptr = XLogInsert(RM_CLOG_ID, CLOG_TRUNCATE | XLOG_NO_TRAN, &rdata);
|
||||
XLogFlush(recptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* CLOG resource manager's routines
|
||||
*/
|
||||
|
@ -409,6 +434,22 @@ clog_redo(XLogRecPtr lsn, XLogRecord *record)
|
|||
|
||||
LWLockRelease(CLogControlLock);
|
||||
}
|
||||
else if (info == CLOG_TRUNCATE)
|
||||
{
|
||||
int pageno;
|
||||
|
||||
memcpy(&pageno, XLogRecGetData(record), sizeof(int));
|
||||
|
||||
/*
|
||||
* During XLOG replay, latest_page_number isn't set up yet; insert
|
||||
* a suitable value to bypass the sanity test in SimpleLruTruncate.
|
||||
*/
|
||||
ClogCtl->shared->latest_page_number = pageno;
|
||||
|
||||
SimpleLruTruncate(ClogCtl, pageno);
|
||||
}
|
||||
else
|
||||
elog(PANIC, "clog_redo: unknown op code %u", info);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -423,6 +464,13 @@ clog_desc(StringInfo buf, uint8 xl_info, char *rec)
|
|||
memcpy(&pageno, rec, sizeof(int));
|
||||
appendStringInfo(buf, "zeropage: %d", pageno);
|
||||
}
|
||||
else if (info == CLOG_TRUNCATE)
|
||||
{
|
||||
int pageno;
|
||||
|
||||
memcpy(&pageno, rec, sizeof(int));
|
||||
appendStringInfo(buf, "truncate before: %d", pageno);
|
||||
}
|
||||
else
|
||||
appendStringInfo(buf, "UNKNOWN");
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Resource managers definition
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/rmgr.c,v 1.24 2006/08/07 16:57:56 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/rmgr.c,v 1.25 2006/11/05 22:42:07 tgl Exp $
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
|
@ -32,7 +32,7 @@ const RmgrData RmgrTable[RM_MAX_ID + 1] = {
|
|||
{"MultiXact", multixact_redo, multixact_desc, NULL, NULL, NULL},
|
||||
{"Reserved 7", NULL, NULL, NULL, NULL, NULL},
|
||||
{"Reserved 8", NULL, NULL, NULL, NULL, NULL},
|
||||
{"Reserved 9", NULL, NULL, NULL, NULL, NULL},
|
||||
{"Heap2", heap2_redo, heap2_desc, NULL, NULL, NULL},
|
||||
{"Heap", heap_redo, heap_desc, NULL, NULL, NULL},
|
||||
{"Btree", btree_redo, btree_desc, btree_xlog_startup, btree_xlog_cleanup, btree_safe_restartpoint},
|
||||
{"Hash", hash_redo, hash_desc, NULL, NULL, NULL},
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.75 2006/10/04 00:29:49 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.76 2006/11/05 22:42:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -17,6 +17,8 @@
|
|||
#include "access/subtrans.h"
|
||||
#include "access/transam.h"
|
||||
#include "miscadmin.h"
|
||||
#include "postmaster/autovacuum.h"
|
||||
#include "storage/pmsignal.h"
|
||||
#include "storage/proc.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
|
@ -47,20 +49,31 @@ GetNewTransactionId(bool isSubXact)
|
|||
|
||||
xid = ShmemVariableCache->nextXid;
|
||||
|
||||
/*
|
||||
/*----------
|
||||
* Check to see if it's safe to assign another XID. This protects against
|
||||
* catastrophic data loss due to XID wraparound. The basic rules are:
|
||||
* warn if we're past xidWarnLimit, and refuse to execute transactions if
|
||||
* we're past xidStopLimit, unless we are running in a standalone backend
|
||||
* (which gives an escape hatch to the DBA who ignored all those
|
||||
* warnings).
|
||||
*
|
||||
* If we're past xidVacLimit, start trying to force autovacuum cycles.
|
||||
* If we're past xidWarnLimit, start issuing warnings.
|
||||
* If we're past xidStopLimit, refuse to execute transactions, unless
|
||||
* we are running in a standalone backend (which gives an escape hatch
|
||||
* to the DBA who somehow got past the earlier defenses).
|
||||
*
|
||||
* Test is coded to fall out as fast as possible during normal operation,
|
||||
* ie, when the warn limit is set and we haven't violated it.
|
||||
* ie, when the vac limit is set and we haven't violated it.
|
||||
*----------
|
||||
*/
|
||||
if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidWarnLimit) &&
|
||||
TransactionIdIsValid(ShmemVariableCache->xidWarnLimit))
|
||||
if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit) &&
|
||||
TransactionIdIsValid(ShmemVariableCache->xidVacLimit))
|
||||
{
|
||||
/*
|
||||
* To avoid swamping the postmaster with signals, we issue the
|
||||
* autovac request only once per 64K transaction starts. This
|
||||
* still gives plenty of chances before we get into real trouble.
|
||||
*/
|
||||
if (IsUnderPostmaster && (xid % 65536) == 0)
|
||||
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC);
|
||||
|
||||
if (IsUnderPostmaster &&
|
||||
TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidStopLimit))
|
||||
ereport(ERROR,
|
||||
|
@ -69,7 +82,7 @@ GetNewTransactionId(bool isSubXact)
|
|||
NameStr(ShmemVariableCache->limit_datname)),
|
||||
errhint("Stop the postmaster and use a standalone backend to vacuum database \"%s\".",
|
||||
NameStr(ShmemVariableCache->limit_datname))));
|
||||
else
|
||||
else if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidWarnLimit))
|
||||
ereport(WARNING,
|
||||
(errmsg("database \"%s\" must be vacuumed within %u transactions",
|
||||
NameStr(ShmemVariableCache->limit_datname),
|
||||
|
@ -178,28 +191,29 @@ ReadNewTransactionId(void)
|
|||
|
||||
/*
|
||||
* Determine the last safe XID to allocate given the currently oldest
|
||||
* datminxid (ie, the oldest XID that might exist in any database
|
||||
* datfrozenxid (ie, the oldest XID that might exist in any database
|
||||
* of our cluster).
|
||||
*/
|
||||
void
|
||||
SetTransactionIdLimit(TransactionId oldest_datminxid,
|
||||
SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
|
||||
Name oldest_datname)
|
||||
{
|
||||
TransactionId xidVacLimit;
|
||||
TransactionId xidWarnLimit;
|
||||
TransactionId xidStopLimit;
|
||||
TransactionId xidWrapLimit;
|
||||
TransactionId curXid;
|
||||
|
||||
Assert(TransactionIdIsValid(oldest_datminxid));
|
||||
Assert(TransactionIdIsNormal(oldest_datfrozenxid));
|
||||
|
||||
/*
|
||||
* The place where we actually get into deep trouble is halfway around
|
||||
* from the oldest existing XID. (This calculation is probably off by one
|
||||
* or two counts, because the special XIDs reduce the size of the loop a
|
||||
* little bit. But we throw in plenty of slop below, so it doesn't
|
||||
* matter.)
|
||||
* from the oldest potentially-existing XID. (This calculation is
|
||||
* probably off by one or two counts, because the special XIDs reduce the
|
||||
* size of the loop a little bit. But we throw in plenty of slop below,
|
||||
* so it doesn't matter.)
|
||||
*/
|
||||
xidWrapLimit = oldest_datminxid + (MaxTransactionId >> 1);
|
||||
xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);
|
||||
if (xidWrapLimit < FirstNormalTransactionId)
|
||||
xidWrapLimit += FirstNormalTransactionId;
|
||||
|
||||
|
@ -229,8 +243,28 @@ SetTransactionIdLimit(TransactionId oldest_datminxid,
|
|||
if (xidWarnLimit < FirstNormalTransactionId)
|
||||
xidWarnLimit -= FirstNormalTransactionId;
|
||||
|
||||
/*
|
||||
* We'll start trying to force autovacuums when oldest_datfrozenxid
|
||||
* gets to be more than autovacuum_freeze_max_age transactions old.
|
||||
*
|
||||
* Note: guc.c ensures that autovacuum_freeze_max_age is in a sane
|
||||
* range, so that xidVacLimit will be well before xidWarnLimit.
|
||||
*
|
||||
* Note: autovacuum_freeze_max_age is a PGC_POSTMASTER parameter so that
|
||||
* we don't have to worry about dealing with on-the-fly changes in its
|
||||
* value. It doesn't look practical to update shared state from a GUC
|
||||
* assign hook (too many processes would try to execute the hook,
|
||||
* resulting in race conditions as well as crashes of those not
|
||||
* connected to shared memory). Perhaps this can be improved someday.
|
||||
*/
|
||||
xidVacLimit = oldest_datfrozenxid + autovacuum_freeze_max_age;
|
||||
if (xidVacLimit < FirstNormalTransactionId)
|
||||
xidVacLimit += FirstNormalTransactionId;
|
||||
|
||||
/* Grab lock for just long enough to set the new limit values */
|
||||
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
|
||||
ShmemVariableCache->oldestXid = oldest_datfrozenxid;
|
||||
ShmemVariableCache->xidVacLimit = xidVacLimit;
|
||||
ShmemVariableCache->xidWarnLimit = xidWarnLimit;
|
||||
ShmemVariableCache->xidStopLimit = xidStopLimit;
|
||||
ShmemVariableCache->xidWrapLimit = xidWrapLimit;
|
||||
|
@ -242,6 +276,18 @@ SetTransactionIdLimit(TransactionId oldest_datminxid,
|
|||
ereport(DEBUG1,
|
||||
(errmsg("transaction ID wrap limit is %u, limited by database \"%s\"",
|
||||
xidWrapLimit, NameStr(*oldest_datname))));
|
||||
|
||||
/*
|
||||
* If past the autovacuum force point, immediately signal an autovac
|
||||
* request. The reason for this is that autovac only processes one
|
||||
* database per invocation. Once it's finished cleaning up the oldest
|
||||
* database, it'll call here, and we'll signal the postmaster to start
|
||||
* another iteration immediately if there are still any old databases.
|
||||
*/
|
||||
if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) &&
|
||||
IsUnderPostmaster)
|
||||
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC);
|
||||
|
||||
/* Give an immediate warning if past the wrap warn point */
|
||||
if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit))
|
||||
ereport(WARNING,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.227 2006/10/04 00:29:49 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.228 2006/11/05 22:42:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -468,8 +468,12 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
|
|||
* is what we need during bootstrap. (Bootstrap mode only inserts tuples,
|
||||
* it never updates or deletes them, so all tuples can be presumed good
|
||||
* immediately.)
|
||||
*
|
||||
* Likewise, InvalidTransactionId and FrozenTransactionId are certainly
|
||||
* not my transaction ID, so we can just return "false" immediately for
|
||||
* any non-normal XID.
|
||||
*/
|
||||
if (xid == BootstrapTransactionId)
|
||||
if (!TransactionIdIsNormal(xid))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.252 2006/10/18 22:44:11 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.253 2006/11/05 22:42:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -5343,36 +5343,6 @@ GetLastSegSwitchTime(void)
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* GetRecentNextXid - get the nextXid value saved by the most recent checkpoint
|
||||
*
|
||||
* This is currently used only by the autovacuum daemon. To check for
|
||||
* impending XID wraparound, autovac needs an approximate idea of the current
|
||||
* XID counter, and it needs it before choosing which DB to attach to, hence
|
||||
* before it sets up a PGPROC, hence before it can take any LWLocks. But it
|
||||
* has attached to shared memory, and so we can let it reach into the shared
|
||||
* ControlFile structure and pull out the last checkpoint nextXID.
|
||||
*
|
||||
* Since we don't take any sort of lock, we have to assume that reading a
|
||||
* TransactionId is atomic ... but that assumption is made elsewhere, too,
|
||||
* and in any case the worst possible consequence of a bogus result is that
|
||||
* autovac issues an unnecessary database-wide VACUUM.
|
||||
*
|
||||
* Note: we could also choose to read ShmemVariableCache->nextXid in an
|
||||
* unlocked fashion, thus getting a more up-to-date result; but since that
|
||||
* changes far more frequently than the controlfile checkpoint copy, it would
|
||||
* pose a far higher risk of bogus result if we did have a nonatomic-read
|
||||
* problem.
|
||||
*
|
||||
* A (theoretically) completely safe answer is to read the actual pg_control
|
||||
* file into local process memory, but that certainly seems like overkill.
|
||||
*/
|
||||
TransactionId
|
||||
GetRecentNextXid(void)
|
||||
{
|
||||
return ControlFile->checkPointCopy.nextXid;
|
||||
}
|
||||
|
||||
/*
|
||||
* GetNextXidAndEpoch - get the current nextXid value and associated epoch
|
||||
*
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.313 2006/10/04 00:29:50 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.314 2006/11/05 22:42:08 tgl Exp $
|
||||
*
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
|
@ -595,8 +595,7 @@ InsertPgClassTuple(Relation pg_class_desc,
|
|||
values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey);
|
||||
values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
|
||||
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
|
||||
values[Anum_pg_class_relminxid - 1] = TransactionIdGetDatum(rd_rel->relminxid);
|
||||
values[Anum_pg_class_relvacuumxid - 1] = TransactionIdGetDatum(rd_rel->relvacuumxid);
|
||||
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
|
||||
/* start out with empty permissions */
|
||||
nulls[Anum_pg_class_relacl - 1] = 'n';
|
||||
if (reloptions != (Datum) 0)
|
||||
|
@ -644,35 +643,6 @@ AddNewRelationTuple(Relation pg_class_desc,
|
|||
*/
|
||||
new_rel_reltup = new_rel_desc->rd_rel;
|
||||
|
||||
/* Initialize relminxid and relvacuumxid */
|
||||
if (relkind == RELKIND_RELATION ||
|
||||
relkind == RELKIND_TOASTVALUE)
|
||||
{
|
||||
/*
|
||||
* Only real tables have Xids stored in them; initialize our known
|
||||
* value to the minimum Xid that could put tuples in the new table.
|
||||
*/
|
||||
if (!IsBootstrapProcessingMode())
|
||||
{
|
||||
new_rel_reltup->relminxid = RecentXmin;
|
||||
new_rel_reltup->relvacuumxid = RecentXmin;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_rel_reltup->relminxid = FirstNormalTransactionId;
|
||||
new_rel_reltup->relvacuumxid = FirstNormalTransactionId;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Other relations will not have Xids in them, so set the initial
|
||||
* value to InvalidTransactionId.
|
||||
*/
|
||||
new_rel_reltup->relminxid = InvalidTransactionId;
|
||||
new_rel_reltup->relvacuumxid = InvalidTransactionId;
|
||||
}
|
||||
|
||||
switch (relkind)
|
||||
{
|
||||
case RELKIND_RELATION:
|
||||
|
@ -694,6 +664,31 @@ AddNewRelationTuple(Relation pg_class_desc,
|
|||
break;
|
||||
}
|
||||
|
||||
/* Initialize relfrozenxid */
|
||||
if (relkind == RELKIND_RELATION ||
|
||||
relkind == RELKIND_TOASTVALUE)
|
||||
{
|
||||
/*
|
||||
* Initialize to the minimum XID that could put tuples in the table.
|
||||
* We know that no xacts older than RecentXmin are still running,
|
||||
* so that will do.
|
||||
*/
|
||||
if (!IsBootstrapProcessingMode())
|
||||
new_rel_reltup->relfrozenxid = RecentXmin;
|
||||
else
|
||||
new_rel_reltup->relfrozenxid = FirstNormalTransactionId;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Other relation types will not contain XIDs, so set relfrozenxid
|
||||
* to InvalidTransactionId. (Note: a sequence does contain a tuple,
|
||||
* but we force its xmin to be FrozenTransactionId always; see
|
||||
* commands/sequence.c.)
|
||||
*/
|
||||
new_rel_reltup->relfrozenxid = InvalidTransactionId;
|
||||
}
|
||||
|
||||
new_rel_reltup->relowner = relowner;
|
||||
new_rel_reltup->reltype = new_type_oid;
|
||||
new_rel_reltup->relkind = relkind;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.100 2006/10/05 17:57:40 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.101 2006/11/05 22:42:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -421,7 +421,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
|||
vac_update_relstats(RelationGetRelid(onerel),
|
||||
RelationGetNumberOfBlocks(onerel),
|
||||
totalrows, hasindex,
|
||||
InvalidTransactionId, InvalidTransactionId);
|
||||
InvalidTransactionId);
|
||||
|
||||
for (ind = 0; ind < nindexes; ind++)
|
||||
{
|
||||
|
@ -432,7 +432,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
|||
vac_update_relstats(RelationGetRelid(Irel[ind]),
|
||||
RelationGetNumberOfBlocks(Irel[ind]),
|
||||
totalindexrows, false,
|
||||
InvalidTransactionId, InvalidTransactionId);
|
||||
InvalidTransactionId);
|
||||
}
|
||||
|
||||
/* report results to the stats collector, too */
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.186 2006/10/18 22:44:12 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.187 2006/11/05 22:42:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -53,8 +53,7 @@
|
|||
static bool get_db_info(const char *name, LOCKMODE lockmode,
|
||||
Oid *dbIdP, Oid *ownerIdP,
|
||||
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
|
||||
Oid *dbLastSysOidP,
|
||||
TransactionId *dbVacuumXidP, TransactionId *dbMinXidP,
|
||||
Oid *dbLastSysOidP, TransactionId *dbFrozenXidP,
|
||||
Oid *dbTablespace);
|
||||
static bool have_createdb_privilege(void);
|
||||
static void remove_dbtablespaces(Oid db_id);
|
||||
|
@ -75,8 +74,7 @@ createdb(const CreatedbStmt *stmt)
|
|||
bool src_istemplate;
|
||||
bool src_allowconn;
|
||||
Oid src_lastsysoid;
|
||||
TransactionId src_vacuumxid;
|
||||
TransactionId src_minxid;
|
||||
TransactionId src_frozenxid;
|
||||
Oid src_deftablespace;
|
||||
volatile Oid dst_deftablespace;
|
||||
Relation pg_database_rel;
|
||||
|
@ -228,7 +226,7 @@ createdb(const CreatedbStmt *stmt)
|
|||
if (!get_db_info(dbtemplate, ShareLock,
|
||||
&src_dboid, &src_owner, &src_encoding,
|
||||
&src_istemplate, &src_allowconn, &src_lastsysoid,
|
||||
&src_vacuumxid, &src_minxid, &src_deftablespace))
|
||||
&src_frozenxid, &src_deftablespace))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
||||
errmsg("template database \"%s\" does not exist",
|
||||
|
@ -366,8 +364,7 @@ createdb(const CreatedbStmt *stmt)
|
|||
new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true);
|
||||
new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
|
||||
new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid);
|
||||
new_record[Anum_pg_database_datvacuumxid - 1] = TransactionIdGetDatum(src_vacuumxid);
|
||||
new_record[Anum_pg_database_datminxid - 1] = TransactionIdGetDatum(src_minxid);
|
||||
new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
|
||||
new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace);
|
||||
|
||||
/*
|
||||
|
@ -565,7 +562,7 @@ dropdb(const char *dbname, bool missing_ok)
|
|||
pgdbrel = heap_open(DatabaseRelationId, RowExclusiveLock);
|
||||
|
||||
if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL,
|
||||
&db_istemplate, NULL, NULL, NULL, NULL, NULL))
|
||||
&db_istemplate, NULL, NULL, NULL, NULL))
|
||||
{
|
||||
if (!missing_ok)
|
||||
{
|
||||
|
@ -689,7 +686,7 @@ RenameDatabase(const char *oldname, const char *newname)
|
|||
rel = heap_open(DatabaseRelationId, RowExclusiveLock);
|
||||
|
||||
if (!get_db_info(oldname, AccessExclusiveLock, &db_id, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL))
|
||||
NULL, NULL, NULL, NULL, NULL))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
||||
errmsg("database \"%s\" does not exist", oldname)));
|
||||
|
@ -1067,8 +1064,7 @@ static bool
|
|||
get_db_info(const char *name, LOCKMODE lockmode,
|
||||
Oid *dbIdP, Oid *ownerIdP,
|
||||
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
|
||||
Oid *dbLastSysOidP,
|
||||
TransactionId *dbVacuumXidP, TransactionId *dbMinXidP,
|
||||
Oid *dbLastSysOidP, TransactionId *dbFrozenXidP,
|
||||
Oid *dbTablespace)
|
||||
{
|
||||
bool result = false;
|
||||
|
@ -1154,12 +1150,9 @@ get_db_info(const char *name, LOCKMODE lockmode,
|
|||
/* last system OID used in database */
|
||||
if (dbLastSysOidP)
|
||||
*dbLastSysOidP = dbform->datlastsysoid;
|
||||
/* limit of vacuumed XIDs */
|
||||
if (dbVacuumXidP)
|
||||
*dbVacuumXidP = dbform->datvacuumxid;
|
||||
/* limit of min XIDs */
|
||||
if (dbMinXidP)
|
||||
*dbMinXidP = dbform->datminxid;
|
||||
/* limit of frozen XIDs */
|
||||
if (dbFrozenXidP)
|
||||
*dbFrozenXidP = dbform->datfrozenxid;
|
||||
/* default tablespace for this database */
|
||||
if (dbTablespace)
|
||||
*dbTablespace = dbform->dattablespace;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.341 2006/10/04 00:29:51 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.342 2006/11/05 22:42:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -25,7 +25,6 @@
|
|||
#include "access/clog.h"
|
||||
#include "access/genam.h"
|
||||
#include "access/heapam.h"
|
||||
#include "access/multixact.h"
|
||||
#include "access/transam.h"
|
||||
#include "access/xact.h"
|
||||
#include "catalog/namespace.h"
|
||||
|
@ -36,7 +35,6 @@
|
|||
#include "miscadmin.h"
|
||||
#include "postmaster/autovacuum.h"
|
||||
#include "storage/freespace.h"
|
||||
#include "storage/pmsignal.h"
|
||||
#include "storage/proc.h"
|
||||
#include "storage/procarray.h"
|
||||
#include "utils/acl.h"
|
||||
|
@ -52,6 +50,11 @@
|
|||
#include "pgstat.h"
|
||||
|
||||
|
||||
/*
|
||||
* GUC parameters
|
||||
*/
|
||||
int vacuum_freeze_min_age;
|
||||
|
||||
/*
|
||||
* VacPage structures keep track of each page on which we find useful
|
||||
* amounts of free space.
|
||||
|
@ -125,7 +128,6 @@ typedef struct VRelStats
|
|||
Size min_tlen;
|
||||
Size max_tlen;
|
||||
bool hasindex;
|
||||
TransactionId minxid; /* Minimum Xid present anywhere on table */
|
||||
/* vtlinks array for tuple chain following - sorted by new_tid */
|
||||
int num_vtlinks;
|
||||
VTupleLink vtlinks;
|
||||
|
@ -193,22 +195,21 @@ static MemoryContext vac_context = NULL;
|
|||
|
||||
static int elevel = -1;
|
||||
|
||||
static TransactionId OldestXmin;
|
||||
static TransactionId FreezeLimit;
|
||||
|
||||
|
||||
/* non-export function prototypes */
|
||||
static List *get_rel_oids(List *relids, const RangeVar *vacrel,
|
||||
const char *stmttype);
|
||||
static void vac_update_dbminxid(Oid dbid,
|
||||
TransactionId *minxid,
|
||||
TransactionId *vacuumxid);
|
||||
static void vac_truncate_clog(TransactionId myminxid, TransactionId myvacxid);
|
||||
static void vac_truncate_clog(TransactionId frozenXID);
|
||||
static void vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind);
|
||||
static void full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);
|
||||
static void scan_heap(VRelStats *vacrelstats, Relation onerel,
|
||||
VacPageList vacuum_pages, VacPageList fraged_pages,
|
||||
TransactionId FreezeLimit, TransactionId OldestXmin);
|
||||
VacPageList vacuum_pages, VacPageList fraged_pages);
|
||||
static void repair_frag(VRelStats *vacrelstats, Relation onerel,
|
||||
VacPageList vacuum_pages, VacPageList fraged_pages,
|
||||
int nindexes, Relation *Irel, TransactionId OldestXmin);
|
||||
int nindexes, Relation *Irel);
|
||||
static void move_chain_tuple(Relation rel,
|
||||
Buffer old_buf, Page old_page, HeapTuple old_tup,
|
||||
Buffer dst_buf, Page dst_page, VacPage dst_vacpage,
|
||||
|
@ -298,27 +299,6 @@ vacuum(VacuumStmt *vacstmt, List *relids)
|
|||
else
|
||||
in_outer_xact = IsInTransactionChain((void *) vacstmt);
|
||||
|
||||
/*
|
||||
* Disallow the combination VACUUM FULL FREEZE; although it would mostly
|
||||
* work, VACUUM FULL's ability to move tuples around means that it is
|
||||
* injecting its own XID into tuple visibility checks. We'd have to
|
||||
* guarantee that every moved tuple is properly marked XMIN_COMMITTED or
|
||||
* XMIN_INVALID before the end of the operation. There are corner cases
|
||||
* where this does not happen, and getting rid of them all seems hard (not
|
||||
* to mention fragile to maintain). On the whole it's not worth it
|
||||
* compared to telling people to use two operations. See pgsql-hackers
|
||||
* discussion of 27-Nov-2004, and comments below for update_hint_bits().
|
||||
*
|
||||
* Note: this is enforced here, and not in the grammar, since (a) we can
|
||||
* give a better error message, and (b) we might want to allow it again
|
||||
* someday.
|
||||
*/
|
||||
if (vacstmt->vacuum && vacstmt->full && vacstmt->freeze)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("VACUUM FULL FREEZE is not supported"),
|
||||
errhint("Use VACUUM FULL, then VACUUM FREEZE.")));
|
||||
|
||||
/*
|
||||
* Send info about dead objects to the statistics collector, unless we are
|
||||
* in autovacuum --- autovacuum.c does this for itself.
|
||||
|
@ -492,23 +472,21 @@ vacuum(VacuumStmt *vacstmt, List *relids)
|
|||
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
|
||||
}
|
||||
|
||||
if (vacstmt->vacuum)
|
||||
if (vacstmt->vacuum && !IsAutoVacuumProcess())
|
||||
{
|
||||
TransactionId minxid,
|
||||
vacuumxid;
|
||||
/*
|
||||
* Update pg_database.datfrozenxid, and truncate pg_clog if possible.
|
||||
* (autovacuum.c does this for itself.)
|
||||
*/
|
||||
vac_update_datfrozenxid();
|
||||
|
||||
/*
|
||||
* If it was a database-wide VACUUM, print FSM usage statistics (we
|
||||
* don't make you be superuser to see these).
|
||||
* don't make you be superuser to see these). We suppress this in
|
||||
* autovacuum, too.
|
||||
*/
|
||||
if (all_rels)
|
||||
PrintFreeSpaceMapStatistics(elevel);
|
||||
|
||||
/* Update pg_database.datminxid and datvacuumxid */
|
||||
vac_update_dbminxid(MyDatabaseId, &minxid, &vacuumxid);
|
||||
|
||||
/* Try to truncate pg_clog. */
|
||||
vac_truncate_clog(minxid, vacuumxid);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -591,12 +569,14 @@ vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,
|
|||
TransactionId *oldestXmin,
|
||||
TransactionId *freezeLimit)
|
||||
{
|
||||
int freezemin;
|
||||
TransactionId limit;
|
||||
TransactionId safeLimit;
|
||||
|
||||
/*
|
||||
* We can always ignore processes running lazy vacuum. This is because we
|
||||
* use these values only for deciding which tuples we must keep in the
|
||||
* tables. Since lazy vacuum doesn't write its xid to the table, it's
|
||||
* tables. Since lazy vacuum doesn't write its XID anywhere, it's
|
||||
* safe to ignore it. In theory it could be problematic to ignore lazy
|
||||
* vacuums on a full vacuum, but keep in mind that only one vacuum process
|
||||
* can be working on a particular table at any time, and that each vacuum
|
||||
|
@ -606,30 +586,35 @@ vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,
|
|||
|
||||
Assert(TransactionIdIsNormal(*oldestXmin));
|
||||
|
||||
if (vacstmt->freeze)
|
||||
{
|
||||
/* FREEZE option: use oldest Xmin as freeze cutoff too */
|
||||
limit = *oldestXmin;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Normal case: freeze cutoff is well in the past, to wit, about
|
||||
* halfway to the wrap horizon
|
||||
*/
|
||||
limit = GetCurrentTransactionId() - (MaxTransactionId >> 2);
|
||||
}
|
||||
/*
|
||||
* Determine the minimum freeze age to use: as specified in the vacstmt,
|
||||
* or vacuum_freeze_min_age, but in any case not more than half
|
||||
* autovacuum_freeze_max_age, so that autovacuums to prevent XID
|
||||
* wraparound won't occur too frequently.
|
||||
*/
|
||||
freezemin = vacstmt->freeze_min_age;
|
||||
if (freezemin < 0)
|
||||
freezemin = vacuum_freeze_min_age;
|
||||
freezemin = Min(freezemin, autovacuum_freeze_max_age / 2);
|
||||
Assert(freezemin >= 0);
|
||||
|
||||
/*
|
||||
* Be careful not to generate a "permanent" XID
|
||||
* Compute the cutoff XID, being careful not to generate a "permanent" XID
|
||||
*/
|
||||
limit = *oldestXmin - freezemin;
|
||||
if (!TransactionIdIsNormal(limit))
|
||||
limit = FirstNormalTransactionId;
|
||||
|
||||
/*
|
||||
* Ensure sane relationship of limits
|
||||
* If oldestXmin is very far back (in practice, more than
|
||||
* autovacuum_freeze_max_age / 2 XIDs old), complain and force a
|
||||
* minimum freeze age of zero.
|
||||
*/
|
||||
if (TransactionIdFollows(limit, *oldestXmin))
|
||||
safeLimit = ReadNewTransactionId() - autovacuum_freeze_max_age;
|
||||
if (!TransactionIdIsNormal(safeLimit))
|
||||
safeLimit = FirstNormalTransactionId;
|
||||
|
||||
if (TransactionIdPrecedes(limit, safeLimit))
|
||||
{
|
||||
ereport(WARNING,
|
||||
(errmsg("oldest xmin is far in the past"),
|
||||
|
@ -668,8 +653,7 @@ vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,
|
|||
*/
|
||||
void
|
||||
vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
|
||||
bool hasindex, TransactionId minxid,
|
||||
TransactionId vacuumxid)
|
||||
bool hasindex, TransactionId frozenxid)
|
||||
{
|
||||
Relation rd;
|
||||
HeapTuple ctup;
|
||||
|
@ -718,14 +702,15 @@ vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
|
|||
dirty = true;
|
||||
}
|
||||
}
|
||||
if (TransactionIdIsValid(minxid) && pgcform->relminxid != minxid)
|
||||
|
||||
/*
|
||||
* relfrozenxid should never go backward. Caller can pass
|
||||
* InvalidTransactionId if it has no new data.
|
||||
*/
|
||||
if (TransactionIdIsNormal(frozenxid) &&
|
||||
TransactionIdPrecedes(pgcform->relfrozenxid, frozenxid))
|
||||
{
|
||||
pgcform->relminxid = minxid;
|
||||
dirty = true;
|
||||
}
|
||||
if (TransactionIdIsValid(vacuumxid) && pgcform->relvacuumxid != vacuumxid)
|
||||
{
|
||||
pgcform->relvacuumxid = vacuumxid;
|
||||
pgcform->relfrozenxid = frozenxid;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
@ -740,34 +725,41 @@ vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
|
|||
|
||||
|
||||
/*
|
||||
* vac_update_dbminxid() -- update the minimum Xid present in one database
|
||||
* vac_update_datfrozenxid() -- update pg_database.datfrozenxid for our DB
|
||||
*
|
||||
* Update pg_database's datminxid and datvacuumxid, and the flat-file copy
|
||||
* of it. datminxid is updated to the minimum of all relminxid found in
|
||||
* pg_class. datvacuumxid is updated to the minimum of all relvacuumxid
|
||||
* found in pg_class. The values are also returned in minxid and
|
||||
* vacuumxid, respectively.
|
||||
* Update pg_database's datfrozenxid entry for our database to be the
|
||||
* minimum of the pg_class.relfrozenxid values. If we are able to
|
||||
* advance pg_database.datfrozenxid, also try to truncate pg_clog.
|
||||
*
|
||||
* We violate transaction semantics here by overwriting the database's
|
||||
* existing pg_database tuple with the new values. This is reasonably
|
||||
* safe since the new values are correct whether or not this transaction
|
||||
* existing pg_database tuple with the new value. This is reasonably
|
||||
* safe since the new value is correct whether or not this transaction
|
||||
* commits. As with vac_update_relstats, this avoids leaving dead tuples
|
||||
* behind after a VACUUM.
|
||||
*
|
||||
* This routine is shared by full and lazy VACUUM.
|
||||
*/
|
||||
static void
|
||||
vac_update_dbminxid(Oid dbid, TransactionId *minxid, TransactionId *vacuumxid)
|
||||
void
|
||||
vac_update_datfrozenxid(void)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
Form_pg_database dbform;
|
||||
Relation relation;
|
||||
SysScanDesc scan;
|
||||
HeapTuple classTup;
|
||||
TransactionId newMinXid = InvalidTransactionId;
|
||||
TransactionId newVacXid = InvalidTransactionId;
|
||||
TransactionId newFrozenXid;
|
||||
bool dirty = false;
|
||||
|
||||
/*
|
||||
* Initialize the "min" calculation with RecentGlobalXmin. Any
|
||||
* not-yet-committed pg_class entries for new tables must have
|
||||
* relfrozenxid at least this high, because any other open xact must have
|
||||
* RecentXmin >= its PGPROC.xmin >= our RecentGlobalXmin; see
|
||||
* AddNewRelationTuple(). So we cannot produce a wrong minimum by
|
||||
* starting with this.
|
||||
*/
|
||||
newFrozenXid = RecentGlobalXmin;
|
||||
|
||||
/*
|
||||
* We must seqscan pg_class to find the minimum Xid, because there is no
|
||||
* index that can help us here.
|
||||
|
@ -779,60 +771,46 @@ vac_update_dbminxid(Oid dbid, TransactionId *minxid, TransactionId *vacuumxid)
|
|||
|
||||
while ((classTup = systable_getnext(scan)) != NULL)
|
||||
{
|
||||
Form_pg_class classForm;
|
||||
|
||||
classForm = (Form_pg_class) GETSTRUCT(classTup);
|
||||
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);
|
||||
|
||||
/*
|
||||
* Only consider heap and TOAST tables (anything else should have
|
||||
* InvalidTransactionId in both fields anyway.)
|
||||
* InvalidTransactionId in relfrozenxid anyway.)
|
||||
*/
|
||||
if (classForm->relkind != RELKIND_RELATION &&
|
||||
classForm->relkind != RELKIND_TOASTVALUE)
|
||||
continue;
|
||||
|
||||
Assert(TransactionIdIsNormal(classForm->relminxid));
|
||||
Assert(TransactionIdIsNormal(classForm->relvacuumxid));
|
||||
Assert(TransactionIdIsNormal(classForm->relfrozenxid));
|
||||
|
||||
/*
|
||||
* Compute the minimum relminxid in all the tables in the database.
|
||||
*/
|
||||
if ((!TransactionIdIsValid(newMinXid) ||
|
||||
TransactionIdPrecedes(classForm->relminxid, newMinXid)))
|
||||
newMinXid = classForm->relminxid;
|
||||
|
||||
/* ditto, for relvacuumxid */
|
||||
if ((!TransactionIdIsValid(newVacXid) ||
|
||||
TransactionIdPrecedes(classForm->relvacuumxid, newVacXid)))
|
||||
newVacXid = classForm->relvacuumxid;
|
||||
if (TransactionIdPrecedes(classForm->relfrozenxid, newFrozenXid))
|
||||
newFrozenXid = classForm->relfrozenxid;
|
||||
}
|
||||
|
||||
/* we're done with pg_class */
|
||||
systable_endscan(scan);
|
||||
heap_close(relation, AccessShareLock);
|
||||
|
||||
Assert(TransactionIdIsNormal(newMinXid));
|
||||
Assert(TransactionIdIsNormal(newVacXid));
|
||||
Assert(TransactionIdIsNormal(newFrozenXid));
|
||||
|
||||
/* Now fetch the pg_database tuple we need to update. */
|
||||
relation = heap_open(DatabaseRelationId, RowExclusiveLock);
|
||||
|
||||
/* Fetch a copy of the tuple to scribble on */
|
||||
tuple = SearchSysCacheCopy(DATABASEOID,
|
||||
ObjectIdGetDatum(dbid),
|
||||
ObjectIdGetDatum(MyDatabaseId),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "could not find tuple for database %u", dbid);
|
||||
elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
|
||||
dbform = (Form_pg_database) GETSTRUCT(tuple);
|
||||
|
||||
if (TransactionIdPrecedes(dbform->datminxid, newMinXid))
|
||||
/*
|
||||
* Don't allow datfrozenxid to go backward (probably can't happen anyway);
|
||||
* and detect the common case where it doesn't go forward either.
|
||||
*/
|
||||
if (TransactionIdPrecedes(dbform->datfrozenxid, newFrozenXid))
|
||||
{
|
||||
dbform->datminxid = newMinXid;
|
||||
dirty = true;
|
||||
}
|
||||
if (TransactionIdPrecedes(dbform->datvacuumxid, newVacXid))
|
||||
{
|
||||
dbform->datvacuumxid = newVacXid;
|
||||
dbform->datfrozenxid = newFrozenXid;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
@ -842,56 +820,57 @@ vac_update_dbminxid(Oid dbid, TransactionId *minxid, TransactionId *vacuumxid)
|
|||
heap_freetuple(tuple);
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
|
||||
/* set return values */
|
||||
*minxid = newMinXid;
|
||||
*vacuumxid = newVacXid;
|
||||
|
||||
/* Mark the flat-file copy of pg_database for update at commit */
|
||||
database_file_update_needed();
|
||||
/*
|
||||
* If we were able to advance datfrozenxid, mark the flat-file copy of
|
||||
* pg_database for update at commit, and see if we can truncate
|
||||
* pg_clog.
|
||||
*/
|
||||
if (dirty)
|
||||
{
|
||||
database_file_update_needed();
|
||||
vac_truncate_clog(newFrozenXid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* vac_truncate_clog() -- attempt to truncate the commit log
|
||||
*
|
||||
* Scan pg_database to determine the system-wide oldest datvacuumxid,
|
||||
* Scan pg_database to determine the system-wide oldest datfrozenxid,
|
||||
* and use it to truncate the transaction commit log (pg_clog).
|
||||
* Also update the XID wrap limit point maintained by varsup.c.
|
||||
* Also update the XID wrap limit info maintained by varsup.c.
|
||||
*
|
||||
* We also generate a warning if the system-wide oldest datfrozenxid
|
||||
* seems to be in danger of wrapping around. This is a long-in-advance
|
||||
* warning; if we start getting uncomfortably close, GetNewTransactionId
|
||||
* will generate more-annoying warnings, and ultimately refuse to issue
|
||||
* any more new XIDs.
|
||||
* The passed XID is simply the one I just wrote into my pg_database
|
||||
* entry. It's used to initialize the "min" calculation.
|
||||
*
|
||||
* The passed XIDs are simply the ones I just wrote into my pg_database
|
||||
* entry. They're used to initialize the "min" calculations.
|
||||
*
|
||||
* This routine is shared by full and lazy VACUUM. Note that it is only
|
||||
* applied after a database-wide VACUUM operation.
|
||||
* This routine is shared by full and lazy VACUUM. Note that it's
|
||||
* only invoked when we've managed to change our DB's datfrozenxid
|
||||
* entry.
|
||||
*/
|
||||
static void
|
||||
vac_truncate_clog(TransactionId myminxid, TransactionId myvacxid)
|
||||
vac_truncate_clog(TransactionId frozenXID)
|
||||
{
|
||||
TransactionId myXID = GetCurrentTransactionId();
|
||||
TransactionId minXID;
|
||||
TransactionId vacuumXID;
|
||||
Relation relation;
|
||||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
int32 age;
|
||||
NameData oldest_datname;
|
||||
bool vacuumAlreadyWrapped = false;
|
||||
bool minAlreadyWrapped = false;
|
||||
bool frozenAlreadyWrapped = false;
|
||||
|
||||
/* Initialize the minimum values. */
|
||||
minXID = myminxid;
|
||||
vacuumXID = myvacxid;
|
||||
/* init oldest_datname to sync with my frozenXID */
|
||||
namestrcpy(&oldest_datname, get_database_name(MyDatabaseId));
|
||||
|
||||
/*
|
||||
* Note: the "already wrapped" cases should now be impossible due to the
|
||||
* defenses in GetNewTransactionId, but we keep them anyway.
|
||||
* Scan pg_database to compute the minimum datfrozenxid
|
||||
*
|
||||
* Note: we need not worry about a race condition with new entries being
|
||||
* inserted by CREATE DATABASE. Any such entry will have a copy of some
|
||||
* existing DB's datfrozenxid, and that source DB cannot be ours because
|
||||
* of the interlock against copying a DB containing an active backend.
|
||||
* Hence the new entry will not reduce the minimum. Also, if two
|
||||
* VACUUMs concurrently modify the datfrozenxid's of different databases,
|
||||
* the worst possible outcome is that pg_clog is not truncated as
|
||||
* aggressively as it could be.
|
||||
*/
|
||||
relation = heap_open(DatabaseRelationId, AccessShareLock);
|
||||
|
||||
|
@ -901,19 +880,13 @@ vac_truncate_clog(TransactionId myminxid, TransactionId myvacxid)
|
|||
{
|
||||
Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
|
||||
|
||||
Assert(TransactionIdIsNormal(dbform->datvacuumxid));
|
||||
Assert(TransactionIdIsNormal(dbform->datminxid));
|
||||
Assert(TransactionIdIsNormal(dbform->datfrozenxid));
|
||||
|
||||
if (TransactionIdPrecedes(myXID, dbform->datvacuumxid))
|
||||
vacuumAlreadyWrapped = true;
|
||||
else if (TransactionIdPrecedes(dbform->datvacuumxid, vacuumXID))
|
||||
vacuumXID = dbform->datvacuumxid;
|
||||
|
||||
if (TransactionIdPrecedes(myXID, dbform->datminxid))
|
||||
minAlreadyWrapped = true;
|
||||
else if (TransactionIdPrecedes(dbform->datminxid, minXID))
|
||||
if (TransactionIdPrecedes(myXID, dbform->datfrozenxid))
|
||||
frozenAlreadyWrapped = true;
|
||||
else if (TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
|
||||
{
|
||||
minXID = dbform->datminxid;
|
||||
frozenXID = dbform->datfrozenxid;
|
||||
namecpy(&oldest_datname, &dbform->datname);
|
||||
}
|
||||
}
|
||||
|
@ -924,9 +897,11 @@ vac_truncate_clog(TransactionId myminxid, TransactionId myvacxid)
|
|||
|
||||
/*
|
||||
* Do not truncate CLOG if we seem to have suffered wraparound already;
|
||||
* the computed minimum XID might be bogus.
|
||||
* the computed minimum XID might be bogus. This case should now be
|
||||
* impossible due to the defenses in GetNewTransactionId, but we keep the
|
||||
* test anyway.
|
||||
*/
|
||||
if (vacuumAlreadyWrapped)
|
||||
if (frozenAlreadyWrapped)
|
||||
{
|
||||
ereport(WARNING,
|
||||
(errmsg("some databases have not been vacuumed in over 2 billion transactions"),
|
||||
|
@ -934,55 +909,14 @@ vac_truncate_clog(TransactionId myminxid, TransactionId myvacxid)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Truncate CLOG to the oldest vacuumxid */
|
||||
TruncateCLOG(vacuumXID);
|
||||
/* Truncate CLOG to the oldest frozenxid */
|
||||
TruncateCLOG(frozenXID);
|
||||
|
||||
/*
|
||||
* Do not update varsup.c if we seem to have suffered wraparound already;
|
||||
* the computed XID might be bogus.
|
||||
* Update the wrap limit for GetNewTransactionId. Note: this function
|
||||
* will also signal the postmaster for an(other) autovac cycle if needed.
|
||||
*/
|
||||
if (minAlreadyWrapped)
|
||||
{
|
||||
ereport(WARNING,
|
||||
(errmsg("some databases have not been vacuumed in over 1 billion transactions"),
|
||||
errhint("Better vacuum them soon, or you may have a wraparound failure.")));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the wrap limit for GetNewTransactionId */
|
||||
SetTransactionIdLimit(minXID, &oldest_datname);
|
||||
|
||||
/* Give warning about impending wraparound problems */
|
||||
age = (int32) (myXID - minXID);
|
||||
if (age > (int32) ((MaxTransactionId >> 3) * 3))
|
||||
ereport(WARNING,
|
||||
(errmsg("database \"%s\" must be vacuumed within %u transactions",
|
||||
NameStr(oldest_datname),
|
||||
(MaxTransactionId >> 1) - age),
|
||||
errhint("To avoid a database shutdown, execute a full-database VACUUM in \"%s\".",
|
||||
NameStr(oldest_datname))));
|
||||
|
||||
/*
|
||||
* Have the postmaster start an autovacuum iteration. If the user has
|
||||
* autovacuum configured, this is not needed; otherwise, we need to make
|
||||
* sure we have some mechanism to cope with transaction Id wraparound.
|
||||
* Ideally this would only be needed for template databases, because all
|
||||
* other databases should be kept nicely pruned by regular vacuuming.
|
||||
*
|
||||
* XXX -- the test we use here is fairly arbitrary. Note that in the
|
||||
* autovacuum database-wide code, a template database is always processed
|
||||
* with VACUUM FREEZE, so we can be sure that it will be truly frozen so
|
||||
* it won't be need to be processed here again soon.
|
||||
*
|
||||
* FIXME -- here we could get into a kind of loop if the database being
|
||||
* chosen is not actually a template database, because we'll not freeze
|
||||
* it, so its age may not really decrease if there are any live
|
||||
* non-freezable tuples. Consider forcing a vacuum freeze if autovacuum
|
||||
* is invoked by a backend. On the other hand, forcing a vacuum freeze on
|
||||
* a user database may not a be a very polite thing to do.
|
||||
*/
|
||||
if (!AutoVacuumingActive() && age > (int32) ((MaxTransactionId >> 3) * 3))
|
||||
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC);
|
||||
SetTransactionIdLimit(frozenXID, &oldest_datname);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1118,7 +1052,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
|
|||
{
|
||||
relation_close(onerel, lmode);
|
||||
CommitTransactionCommand();
|
||||
return; /* assume no long-lived data in temp tables */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1208,8 +1142,6 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
|||
int nindexes,
|
||||
i;
|
||||
VRelStats *vacrelstats;
|
||||
TransactionId FreezeLimit,
|
||||
OldestXmin;
|
||||
|
||||
vacuum_set_xid_limits(vacstmt, onerel->rd_rel->relisshared,
|
||||
&OldestXmin, &FreezeLimit);
|
||||
|
@ -1222,21 +1154,9 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
|||
vacrelstats->rel_tuples = 0;
|
||||
vacrelstats->hasindex = false;
|
||||
|
||||
/*
|
||||
* Set initial minimum Xid, which will be updated if a smaller Xid is
|
||||
* found in the relation by scan_heap.
|
||||
*
|
||||
* We use RecentXmin here (the minimum Xid that belongs to a transaction
|
||||
* that is still open according to our snapshot), because it is the
|
||||
* earliest transaction that could insert new tuples in the table after
|
||||
* our VACUUM is done.
|
||||
*/
|
||||
vacrelstats->minxid = RecentXmin;
|
||||
|
||||
/* scan the heap */
|
||||
vacuum_pages.num_pages = fraged_pages.num_pages = 0;
|
||||
scan_heap(vacrelstats, onerel, &vacuum_pages, &fraged_pages, FreezeLimit,
|
||||
OldestXmin);
|
||||
scan_heap(vacrelstats, onerel, &vacuum_pages, &fraged_pages);
|
||||
|
||||
/* Now open all indexes of the relation */
|
||||
vac_open_indexes(onerel, AccessExclusiveLock, &nindexes, &Irel);
|
||||
|
@ -1264,7 +1184,7 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
|||
{
|
||||
/* Try to shrink heap */
|
||||
repair_frag(vacrelstats, onerel, &vacuum_pages, &fraged_pages,
|
||||
nindexes, Irel, OldestXmin);
|
||||
nindexes, Irel);
|
||||
vac_close_indexes(nindexes, Irel, NoLock);
|
||||
}
|
||||
else
|
||||
|
@ -1283,7 +1203,7 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
|||
/* update statistics in pg_class */
|
||||
vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages,
|
||||
vacrelstats->rel_tuples, vacrelstats->hasindex,
|
||||
vacrelstats->minxid, OldestXmin);
|
||||
FreezeLimit);
|
||||
|
||||
/* report results to the stats collector, too */
|
||||
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
|
||||
|
@ -1299,14 +1219,10 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
|||
* deleted tuples), constructs fraged_pages (list of pages with free
|
||||
* space that tuples could be moved into), and calculates statistics
|
||||
* on the number of live tuples in the heap.
|
||||
*
|
||||
* It also updates the minimum Xid found anywhere on the table in
|
||||
* vacrelstats->minxid, for later storing it in pg_class.relminxid.
|
||||
*/
|
||||
static void
|
||||
scan_heap(VRelStats *vacrelstats, Relation onerel,
|
||||
VacPageList vacuum_pages, VacPageList fraged_pages,
|
||||
TransactionId FreezeLimit, TransactionId OldestXmin)
|
||||
VacPageList vacuum_pages, VacPageList fraged_pages)
|
||||
{
|
||||
BlockNumber nblocks,
|
||||
blkno;
|
||||
|
@ -1357,8 +1273,9 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
|
|||
Buffer buf;
|
||||
OffsetNumber offnum,
|
||||
maxoff;
|
||||
bool pgchanged,
|
||||
notup;
|
||||
bool notup;
|
||||
OffsetNumber frozen[MaxOffsetNumber];
|
||||
int nfrozen;
|
||||
|
||||
vacuum_delay_point();
|
||||
|
||||
|
@ -1414,7 +1331,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
|
|||
continue;
|
||||
}
|
||||
|
||||
pgchanged = false;
|
||||
nfrozen = 0;
|
||||
notup = true;
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
for (offnum = FirstOffsetNumber;
|
||||
|
@ -1446,24 +1363,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
|
|||
tupgone = true; /* we can delete the tuple */
|
||||
break;
|
||||
case HEAPTUPLE_LIVE:
|
||||
|
||||
/*
|
||||
* Tuple is good. Consider whether to replace its xmin
|
||||
* value with FrozenTransactionId.
|
||||
*/
|
||||
if (TransactionIdIsNormal(HeapTupleHeaderGetXmin(tuple.t_data)) &&
|
||||
TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),
|
||||
FreezeLimit))
|
||||
{
|
||||
HeapTupleHeaderSetXmin(tuple.t_data, FrozenTransactionId);
|
||||
/* infomask should be okay already */
|
||||
Assert(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED);
|
||||
pgchanged = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Other checks...
|
||||
*/
|
||||
/* Tuple is good --- but let's do some validity checks */
|
||||
if (onerel->rd_rel->relhasoids &&
|
||||
!OidIsValid(HeapTupleGetOid(&tuple)))
|
||||
elog(WARNING, "relation \"%s\" TID %u/%u: OID is invalid",
|
||||
|
@ -1559,8 +1459,6 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
|
|||
}
|
||||
else
|
||||
{
|
||||
TransactionId min;
|
||||
|
||||
num_tuples += 1;
|
||||
notup = false;
|
||||
if (tuple.t_len < min_tlen)
|
||||
|
@ -1569,13 +1467,12 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
|
|||
max_tlen = tuple.t_len;
|
||||
|
||||
/*
|
||||
* If the tuple is alive, we consider it for the "minxid"
|
||||
* calculations.
|
||||
* Each non-removable tuple must be checked to see if it
|
||||
* needs freezing.
|
||||
*/
|
||||
min = vactuple_get_minxid(&tuple);
|
||||
if (TransactionIdIsValid(min) &&
|
||||
TransactionIdPrecedes(min, vacrelstats->minxid))
|
||||
vacrelstats->minxid = min;
|
||||
if (heap_freeze_tuple(tuple.t_data, FreezeLimit,
|
||||
InvalidBuffer))
|
||||
frozen[nfrozen++] = offnum;
|
||||
}
|
||||
} /* scan along page */
|
||||
|
||||
|
@ -1627,8 +1524,26 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
|
|||
else
|
||||
empty_end_pages = 0;
|
||||
|
||||
if (pgchanged)
|
||||
/*
|
||||
* If we froze any tuples, mark the buffer dirty, and write a WAL
|
||||
* record recording the changes. We must log the changes to be
|
||||
* crash-safe against future truncation of CLOG.
|
||||
*/
|
||||
if (nfrozen > 0)
|
||||
{
|
||||
MarkBufferDirty(buf);
|
||||
/* no XLOG for temp tables, though */
|
||||
if (!onerel->rd_istemp)
|
||||
{
|
||||
XLogRecPtr recptr;
|
||||
|
||||
recptr = log_heap_freeze(onerel, buf, FreezeLimit,
|
||||
frozen, nfrozen);
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
}
|
||||
}
|
||||
|
||||
UnlockReleaseBuffer(buf);
|
||||
}
|
||||
|
||||
|
@ -1701,63 +1616,6 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
|
|||
pg_rusage_show(&ru0))));
|
||||
}
|
||||
|
||||
/*
|
||||
* vactuple_get_minxid
|
||||
*
|
||||
* Get the minimum relevant Xid for a tuple, not considering FrozenXid.
|
||||
* Return InvalidXid if none (i.e., xmin=FrozenXid, xmax=InvalidXid).
|
||||
* This is for the purpose of calculating pg_class.relminxid for a table
|
||||
* we're vacuuming.
|
||||
*/
|
||||
TransactionId
|
||||
vactuple_get_minxid(HeapTuple tuple)
|
||||
{
|
||||
TransactionId min = InvalidTransactionId;
|
||||
|
||||
/*
|
||||
* Initialize calculations with Xmin. NB -- may be FrozenXid and we don't
|
||||
* want that one.
|
||||
*/
|
||||
if (TransactionIdIsNormal(HeapTupleHeaderGetXmin(tuple->t_data)))
|
||||
min = HeapTupleHeaderGetXmin(tuple->t_data);
|
||||
|
||||
/*
|
||||
* If Xmax is not marked INVALID, we assume it's valid without making
|
||||
* further checks on it --- it must be recently obsoleted or still
|
||||
* running, else HeapTupleSatisfiesVacuum would have deemed it removable.
|
||||
*/
|
||||
if (!(tuple->t_data->t_infomask | HEAP_XMAX_INVALID))
|
||||
{
|
||||
TransactionId xmax = HeapTupleHeaderGetXmax(tuple->t_data);
|
||||
|
||||
/* If xmax is a plain Xid, consider it by itself */
|
||||
if (!(tuple->t_data->t_infomask | HEAP_XMAX_IS_MULTI))
|
||||
{
|
||||
if (!TransactionIdIsValid(min) ||
|
||||
(TransactionIdIsNormal(xmax) &&
|
||||
TransactionIdPrecedes(xmax, min)))
|
||||
min = xmax;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If it's a MultiXactId, consider each of its members */
|
||||
TransactionId *members;
|
||||
int nmembers,
|
||||
membno;
|
||||
|
||||
nmembers = GetMultiXactIdMembers(xmax, &members);
|
||||
|
||||
for (membno = 0; membno < nmembers; membno++)
|
||||
{
|
||||
if (!TransactionIdIsValid(min) ||
|
||||
TransactionIdPrecedes(members[membno], min))
|
||||
min = members[membno];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
/*
|
||||
* repair_frag() -- try to repair relation's fragmentation
|
||||
|
@ -1772,7 +1630,7 @@ vactuple_get_minxid(HeapTuple tuple)
|
|||
static void
|
||||
repair_frag(VRelStats *vacrelstats, Relation onerel,
|
||||
VacPageList vacuum_pages, VacPageList fraged_pages,
|
||||
int nindexes, Relation *Irel, TransactionId OldestXmin)
|
||||
int nindexes, Relation *Irel)
|
||||
{
|
||||
TransactionId myXID = GetCurrentTransactionId();
|
||||
Buffer dst_buffer = InvalidBuffer;
|
||||
|
@ -2903,8 +2761,8 @@ move_plain_tuple(Relation rel,
|
|||
* update_hint_bits() -- update hint bits in destination pages
|
||||
*
|
||||
* Scan all the pages that we moved tuples onto and update tuple status bits.
|
||||
* This is normally not really necessary, but it will save time for future
|
||||
* transactions examining these tuples.
|
||||
* This is not really necessary, but it will save time for future transactions
|
||||
* examining these tuples.
|
||||
*
|
||||
* This pass guarantees that all HEAP_MOVED_IN tuples are marked as
|
||||
* XMIN_COMMITTED, so that future tqual tests won't need to check their XVAC.
|
||||
|
@ -2919,13 +2777,9 @@ move_plain_tuple(Relation rel,
|
|||
* To completely ensure that no MOVED_OFF tuples remain unmarked, we'd have
|
||||
* to remember and revisit those pages too.
|
||||
*
|
||||
* Because of this omission, VACUUM FULL FREEZE is not a safe combination;
|
||||
* it's possible that the VACUUM's own XID remains exposed as something that
|
||||
* tqual tests would need to check.
|
||||
*
|
||||
* For the non-freeze case, one wonders whether it wouldn't be better to skip
|
||||
* this work entirely, and let the tuple status updates happen someplace
|
||||
* that's not holding an exclusive lock on the relation.
|
||||
* One wonders whether it wouldn't be better to skip this work entirely,
|
||||
* and let the tuple status updates happen someplace that's not holding an
|
||||
* exclusive lock on the relation.
|
||||
*/
|
||||
static void
|
||||
update_hint_bits(Relation rel, VacPageList fraged_pages, int num_fraged_pages,
|
||||
|
@ -3114,7 +2968,7 @@ scan_index(Relation indrel, double num_tuples)
|
|||
/* now update statistics in pg_class */
|
||||
vac_update_relstats(RelationGetRelid(indrel),
|
||||
stats->num_pages, stats->num_index_tuples,
|
||||
false, InvalidTransactionId, InvalidTransactionId);
|
||||
false, InvalidTransactionId);
|
||||
|
||||
ereport(elevel,
|
||||
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
|
||||
|
@ -3183,7 +3037,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
|
|||
/* now update statistics in pg_class */
|
||||
vac_update_relstats(RelationGetRelid(indrel),
|
||||
stats->num_pages, stats->num_index_tuples,
|
||||
false, InvalidTransactionId, InvalidTransactionId);
|
||||
false, InvalidTransactionId);
|
||||
|
||||
ereport(elevel,
|
||||
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.80 2006/10/04 00:29:52 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.81 2006/11/05 22:42:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -78,7 +78,6 @@ typedef struct LVRelStats
|
|||
double tuples_deleted;
|
||||
BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
|
||||
Size threshold; /* minimum interesting free space */
|
||||
TransactionId minxid; /* minimum Xid present anywhere in table */
|
||||
/* List of TIDs of tuples we intend to delete */
|
||||
/* NB: this list is ordered by TID address */
|
||||
int num_dead_tuples; /* current # of entries */
|
||||
|
@ -96,11 +95,13 @@ typedef struct LVRelStats
|
|||
|
||||
static int elevel = -1;
|
||||
|
||||
static TransactionId OldestXmin;
|
||||
static TransactionId FreezeLimit;
|
||||
|
||||
|
||||
/* non-export function prototypes */
|
||||
static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
Relation *Irel, int nindexes, TransactionId FreezeLimit,
|
||||
TransactionId OldestXmin);
|
||||
Relation *Irel, int nindexes);
|
||||
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
|
||||
static void lazy_vacuum_index(Relation indrel,
|
||||
IndexBulkDeleteResult **stats,
|
||||
|
@ -110,10 +111,9 @@ static void lazy_cleanup_index(Relation indrel,
|
|||
LVRelStats *vacrelstats);
|
||||
static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
|
||||
int tupindex, LVRelStats *vacrelstats);
|
||||
static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
TransactionId OldestXmin);
|
||||
static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats);
|
||||
static BlockNumber count_nondeletable_pages(Relation onerel,
|
||||
LVRelStats *vacrelstats, TransactionId OldestXmin);
|
||||
LVRelStats *vacrelstats);
|
||||
static void lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks);
|
||||
static void lazy_record_dead_tuple(LVRelStats *vacrelstats,
|
||||
ItemPointer itemptr);
|
||||
|
@ -129,8 +129,7 @@ static int vac_cmp_page_spaces(const void *left, const void *right);
|
|||
* lazy_vacuum_rel() -- perform LAZY VACUUM for one heap relation
|
||||
*
|
||||
* This routine vacuums a single heap, cleans out its indexes, and
|
||||
* updates its relpages and reltuples statistics, as well as the
|
||||
* relminxid and relvacuumxid information.
|
||||
* updates its relpages and reltuples statistics.
|
||||
*
|
||||
* At entry, we have already established a transaction and opened
|
||||
* and locked the relation.
|
||||
|
@ -142,8 +141,6 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
|||
Relation *Irel;
|
||||
int nindexes;
|
||||
BlockNumber possibly_freeable;
|
||||
TransactionId OldestXmin,
|
||||
FreezeLimit;
|
||||
|
||||
if (vacstmt->verbose)
|
||||
elevel = INFO;
|
||||
|
@ -159,23 +156,12 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
|||
/* XXX should we scale it up or down? Adjust vacuum.c too, if so */
|
||||
vacrelstats->threshold = GetAvgFSMRequestSize(&onerel->rd_node);
|
||||
|
||||
/*
|
||||
* Set initial minimum Xid, which will be updated if a smaller Xid is
|
||||
* found in the relation by lazy_scan_heap.
|
||||
*
|
||||
* We use RecentXmin here (the minimum Xid that belongs to a transaction
|
||||
* that is still open according to our snapshot), because it is the
|
||||
* earliest transaction that could concurrently insert new tuples in the
|
||||
* table.
|
||||
*/
|
||||
vacrelstats->minxid = RecentXmin;
|
||||
|
||||
/* Open all indexes of the relation */
|
||||
vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel);
|
||||
vacrelstats->hasindex = (nindexes > 0);
|
||||
|
||||
/* Do the vacuuming */
|
||||
lazy_scan_heap(onerel, vacrelstats, Irel, nindexes, FreezeLimit, OldestXmin);
|
||||
lazy_scan_heap(onerel, vacrelstats, Irel, nindexes);
|
||||
|
||||
/* Done with indexes */
|
||||
vac_close_indexes(nindexes, Irel, NoLock);
|
||||
|
@ -189,7 +175,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
|||
possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages;
|
||||
if (possibly_freeable >= REL_TRUNCATE_MINIMUM ||
|
||||
possibly_freeable >= vacrelstats->rel_pages / REL_TRUNCATE_FRACTION)
|
||||
lazy_truncate_heap(onerel, vacrelstats, OldestXmin);
|
||||
lazy_truncate_heap(onerel, vacrelstats);
|
||||
|
||||
/* Update shared free space map with final free space info */
|
||||
lazy_update_fsm(onerel, vacrelstats);
|
||||
|
@ -199,7 +185,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
|||
vacrelstats->rel_pages,
|
||||
vacrelstats->rel_tuples,
|
||||
vacrelstats->hasindex,
|
||||
vacrelstats->minxid, OldestXmin);
|
||||
FreezeLimit);
|
||||
|
||||
/* report results to the stats collector, too */
|
||||
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
|
||||
|
@ -215,16 +201,12 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
|||
* of live tuples in the heap. When done, or when we run low on space
|
||||
* for dead-tuple TIDs, invoke vacuuming of indexes and heap.
|
||||
*
|
||||
* It also updates the minimum Xid found anywhere on the table in
|
||||
* vacrelstats->minxid, for later storing it in pg_class.relminxid.
|
||||
*
|
||||
* If there are no indexes then we just vacuum each dirty page as we
|
||||
* process it, since there's no point in gathering many tuples.
|
||||
*/
|
||||
static void
|
||||
lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
Relation *Irel, int nindexes, TransactionId FreezeLimit,
|
||||
TransactionId OldestXmin)
|
||||
Relation *Irel, int nindexes)
|
||||
{
|
||||
BlockNumber nblocks,
|
||||
blkno;
|
||||
|
@ -266,10 +248,11 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
|||
Page page;
|
||||
OffsetNumber offnum,
|
||||
maxoff;
|
||||
bool pgchanged,
|
||||
tupgone,
|
||||
bool tupgone,
|
||||
hastup;
|
||||
int prev_dead_count;
|
||||
OffsetNumber frozen[MaxOffsetNumber];
|
||||
int nfrozen;
|
||||
|
||||
vacuum_delay_point();
|
||||
|
||||
|
@ -293,7 +276,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
|||
|
||||
buf = ReadBuffer(onerel, blkno);
|
||||
|
||||
/* In this phase we only need shared access to the buffer */
|
||||
/* Initially, we only need shared access to the buffer */
|
||||
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
||||
|
||||
page = BufferGetPage(buf);
|
||||
|
@ -349,7 +332,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
|||
continue;
|
||||
}
|
||||
|
||||
pgchanged = false;
|
||||
nfrozen = 0;
|
||||
hastup = false;
|
||||
prev_dead_count = vacrelstats->num_dead_tuples;
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
|
@ -379,31 +362,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
|||
tupgone = true; /* we can delete the tuple */
|
||||
break;
|
||||
case HEAPTUPLE_LIVE:
|
||||
|
||||
/*
|
||||
* Tuple is good. Consider whether to replace its xmin
|
||||
* value with FrozenTransactionId.
|
||||
*
|
||||
* NB: Since we hold only a shared buffer lock here, we
|
||||
* are assuming that TransactionId read/write is atomic.
|
||||
* This is not the only place that makes such an
|
||||
* assumption. It'd be possible to avoid the assumption by
|
||||
* momentarily acquiring exclusive lock, but for the
|
||||
* moment I see no need to.
|
||||
*/
|
||||
if (TransactionIdIsNormal(HeapTupleHeaderGetXmin(tuple.t_data)) &&
|
||||
TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),
|
||||
FreezeLimit))
|
||||
{
|
||||
HeapTupleHeaderSetXmin(tuple.t_data, FrozenTransactionId);
|
||||
/* infomask should be okay already */
|
||||
Assert(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED);
|
||||
pgchanged = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Other checks...
|
||||
*/
|
||||
/* Tuple is good --- but let's do some validity checks */
|
||||
if (onerel->rd_rel->relhasoids &&
|
||||
!OidIsValid(HeapTupleGetOid(&tuple)))
|
||||
elog(WARNING, "relation \"%s\" TID %u/%u: OID is invalid",
|
||||
|
@ -435,22 +394,40 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
|||
}
|
||||
else
|
||||
{
|
||||
TransactionId min;
|
||||
|
||||
num_tuples += 1;
|
||||
hastup = true;
|
||||
|
||||
/*
|
||||
* If the tuple is alive, we consider it for the "minxid"
|
||||
* calculations.
|
||||
* Each non-removable tuple must be checked to see if it
|
||||
* needs freezing. If we already froze anything, then
|
||||
* we've already switched the buffer lock to exclusive.
|
||||
*/
|
||||
min = vactuple_get_minxid(&tuple);
|
||||
if (TransactionIdIsValid(min) &&
|
||||
TransactionIdPrecedes(min, vacrelstats->minxid))
|
||||
vacrelstats->minxid = min;
|
||||
if (heap_freeze_tuple(tuple.t_data, FreezeLimit,
|
||||
(nfrozen > 0) ? InvalidBuffer : buf))
|
||||
frozen[nfrozen++] = offnum;
|
||||
}
|
||||
} /* scan along page */
|
||||
|
||||
/*
|
||||
* If we froze any tuples, mark the buffer dirty, and write a WAL
|
||||
* record recording the changes. We must log the changes to be
|
||||
* crash-safe against future truncation of CLOG.
|
||||
*/
|
||||
if (nfrozen > 0)
|
||||
{
|
||||
MarkBufferDirty(buf);
|
||||
/* no XLOG for temp tables, though */
|
||||
if (!onerel->rd_istemp)
|
||||
{
|
||||
XLogRecPtr recptr;
|
||||
|
||||
recptr = log_heap_freeze(onerel, buf, FreezeLimit,
|
||||
frozen, nfrozen);
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are no indexes then we can vacuum the page right now
|
||||
* instead of doing a second scan.
|
||||
|
@ -485,8 +462,6 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
|||
if (hastup)
|
||||
vacrelstats->nonempty_pages = blkno + 1;
|
||||
|
||||
if (pgchanged)
|
||||
MarkBufferDirty(buf);
|
||||
UnlockReleaseBuffer(buf);
|
||||
}
|
||||
|
||||
|
@ -710,7 +685,7 @@ lazy_cleanup_index(Relation indrel,
|
|||
vac_update_relstats(RelationGetRelid(indrel),
|
||||
stats->num_pages,
|
||||
stats->num_index_tuples,
|
||||
false, InvalidTransactionId, InvalidTransactionId);
|
||||
false, InvalidTransactionId);
|
||||
|
||||
ereport(elevel,
|
||||
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
|
||||
|
@ -731,8 +706,7 @@ lazy_cleanup_index(Relation indrel,
|
|||
* lazy_truncate_heap - try to truncate off any empty pages at the end
|
||||
*/
|
||||
static void
|
||||
lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
TransactionId OldestXmin)
|
||||
lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
|
||||
{
|
||||
BlockNumber old_rel_pages = vacrelstats->rel_pages;
|
||||
BlockNumber new_rel_pages;
|
||||
|
@ -773,7 +747,7 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats,
|
|||
* because other backends could have added tuples to these pages whilst we
|
||||
* were vacuuming.
|
||||
*/
|
||||
new_rel_pages = count_nondeletable_pages(onerel, vacrelstats, OldestXmin);
|
||||
new_rel_pages = count_nondeletable_pages(onerel, vacrelstats);
|
||||
|
||||
if (new_rel_pages >= old_rel_pages)
|
||||
{
|
||||
|
@ -837,8 +811,7 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats,
|
|||
* Returns number of nondeletable pages (last nonempty page + 1).
|
||||
*/
|
||||
static BlockNumber
|
||||
count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats,
|
||||
TransactionId OldestXmin)
|
||||
count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
|
||||
{
|
||||
BlockNumber blkno;
|
||||
HeapTupleData tuple;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.156 2006/10/04 00:29:53 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.157 2006/11/05 22:42:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -1004,16 +1004,14 @@ load_hba(void)
|
|||
* dbname: gets database name (must be of size NAMEDATALEN bytes)
|
||||
* dboid: gets database OID
|
||||
* dbtablespace: gets database's default tablespace's OID
|
||||
* dbminxid: gets database's minimum XID
|
||||
* dbvacuumxid: gets database's vacuum XID
|
||||
* dbfrozenxid: gets database's frozen XID
|
||||
*
|
||||
* This is not much related to the other functions in hba.c, but we put it
|
||||
* here because it uses the next_token() infrastructure.
|
||||
*/
|
||||
bool
|
||||
read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
|
||||
Oid *dbtablespace, TransactionId *dbminxid,
|
||||
TransactionId *dbvacuumxid)
|
||||
Oid *dbtablespace, TransactionId *dbfrozenxid)
|
||||
{
|
||||
char buf[MAX_TOKEN];
|
||||
|
||||
|
@ -1035,11 +1033,7 @@ read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
|
|||
next_token(fp, buf, sizeof(buf));
|
||||
if (!isdigit((unsigned char) buf[0]))
|
||||
elog(FATAL, "bad data in flat pg_database file");
|
||||
*dbminxid = atoxid(buf);
|
||||
next_token(fp, buf, sizeof(buf));
|
||||
if (!isdigit((unsigned char) buf[0]))
|
||||
elog(FATAL, "bad data in flat pg_database file");
|
||||
*dbvacuumxid = atoxid(buf);
|
||||
*dbfrozenxid = atoxid(buf);
|
||||
/* expect EOL next */
|
||||
if (next_token(fp, buf, sizeof(buf)))
|
||||
elog(FATAL, "bad data in flat pg_database file");
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.352 2006/10/13 21:43:18 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.353 2006/11/05 22:42:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -2350,8 +2350,8 @@ _copyVacuumStmt(VacuumStmt *from)
|
|||
COPY_SCALAR_FIELD(vacuum);
|
||||
COPY_SCALAR_FIELD(full);
|
||||
COPY_SCALAR_FIELD(analyze);
|
||||
COPY_SCALAR_FIELD(freeze);
|
||||
COPY_SCALAR_FIELD(verbose);
|
||||
COPY_SCALAR_FIELD(freeze_min_age);
|
||||
COPY_NODE_FIELD(relation);
|
||||
COPY_NODE_FIELD(va_cols);
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.286 2006/10/13 21:43:18 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.287 2006/11/05 22:42:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -1215,8 +1215,8 @@ _equalVacuumStmt(VacuumStmt *a, VacuumStmt *b)
|
|||
COMPARE_SCALAR_FIELD(vacuum);
|
||||
COMPARE_SCALAR_FIELD(full);
|
||||
COMPARE_SCALAR_FIELD(analyze);
|
||||
COMPARE_SCALAR_FIELD(freeze);
|
||||
COMPARE_SCALAR_FIELD(verbose);
|
||||
COMPARE_SCALAR_FIELD(freeze_min_age);
|
||||
COMPARE_NODE_FIELD(relation);
|
||||
COMPARE_NODE_FIELD(va_cols);
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.567 2006/10/13 21:43:19 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.568 2006/11/05 22:42:09 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
|
@ -5158,7 +5158,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
|
|||
n->vacuum = true;
|
||||
n->analyze = false;
|
||||
n->full = $2;
|
||||
n->freeze = $3;
|
||||
n->freeze_min_age = $3 ? 0 : -1;
|
||||
n->verbose = $4;
|
||||
n->relation = NULL;
|
||||
n->va_cols = NIL;
|
||||
|
@ -5170,7 +5170,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
|
|||
n->vacuum = true;
|
||||
n->analyze = false;
|
||||
n->full = $2;
|
||||
n->freeze = $3;
|
||||
n->freeze_min_age = $3 ? 0 : -1;
|
||||
n->verbose = $4;
|
||||
n->relation = $5;
|
||||
n->va_cols = NIL;
|
||||
|
@ -5181,7 +5181,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
|
|||
VacuumStmt *n = (VacuumStmt *) $5;
|
||||
n->vacuum = true;
|
||||
n->full = $2;
|
||||
n->freeze = $3;
|
||||
n->freeze_min_age = $3 ? 0 : -1;
|
||||
n->verbose |= $4;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
|
@ -5194,7 +5194,7 @@ AnalyzeStmt:
|
|||
n->vacuum = false;
|
||||
n->analyze = true;
|
||||
n->full = false;
|
||||
n->freeze = false;
|
||||
n->freeze_min_age = -1;
|
||||
n->verbose = $2;
|
||||
n->relation = NULL;
|
||||
n->va_cols = NIL;
|
||||
|
@ -5206,7 +5206,7 @@ AnalyzeStmt:
|
|||
n->vacuum = false;
|
||||
n->analyze = true;
|
||||
n->full = false;
|
||||
n->freeze = false;
|
||||
n->freeze_min_age = -1;
|
||||
n->verbose = $2;
|
||||
n->relation = $3;
|
||||
n->va_cols = $4;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.27 2006/10/04 00:29:56 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.28 2006/11/05 22:42:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -59,6 +59,7 @@ int autovacuum_vac_thresh;
|
|||
double autovacuum_vac_scale;
|
||||
int autovacuum_anl_thresh;
|
||||
double autovacuum_anl_scale;
|
||||
int autovacuum_freeze_max_age;
|
||||
|
||||
int autovacuum_vac_cost_delay;
|
||||
int autovacuum_vac_cost_limit;
|
||||
|
@ -70,6 +71,12 @@ static bool am_autovacuum = false;
|
|||
static time_t last_autovac_start_time = 0;
|
||||
static time_t last_autovac_stop_time = 0;
|
||||
|
||||
/* Comparison point for determining whether freeze_max_age is exceeded */
|
||||
static TransactionId recentXid;
|
||||
|
||||
/* Default freeze_min_age to use for autovacuum (varies by database) */
|
||||
static int default_freeze_min_age;
|
||||
|
||||
/* Memory context for long-lived data */
|
||||
static MemoryContext AutovacMemCxt;
|
||||
|
||||
|
@ -78,10 +85,8 @@ typedef struct autovac_dbase
|
|||
{
|
||||
Oid oid;
|
||||
char *name;
|
||||
TransactionId minxid;
|
||||
TransactionId vacuumxid;
|
||||
TransactionId frozenxid;
|
||||
PgStat_StatDBEntry *entry;
|
||||
int32 age;
|
||||
} autovac_dbase;
|
||||
|
||||
/* struct to keep track of tables to vacuum and/or analyze */
|
||||
|
@ -91,6 +96,7 @@ typedef struct autovac_table
|
|||
Oid toastrelid;
|
||||
bool dovacuum;
|
||||
bool doanalyze;
|
||||
int freeze_min_age;
|
||||
int vacuum_cost_delay;
|
||||
int vacuum_cost_limit;
|
||||
} autovac_table;
|
||||
|
@ -100,7 +106,6 @@ typedef struct autovac_table
|
|||
static pid_t autovac_forkexec(void);
|
||||
#endif
|
||||
NON_EXEC_STATIC void AutoVacMain(int argc, char *argv[]);
|
||||
static void process_whole_db(void);
|
||||
static void do_autovacuum(PgStat_StatDBEntry *dbentry);
|
||||
static List *autovac_get_database_list(void);
|
||||
static void test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
|
||||
|
@ -108,10 +113,9 @@ static void test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
|
|||
Form_pg_autovacuum avForm,
|
||||
List **vacuum_tables,
|
||||
List **toast_table_ids);
|
||||
static void autovacuum_do_vac_analyze(List *relids, bool dovacuum,
|
||||
bool doanalyze, bool freeze);
|
||||
static void autovac_report_activity(VacuumStmt *vacstmt,
|
||||
List *relids);
|
||||
static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum,
|
||||
bool doanalyze, int freeze_min_age);
|
||||
static void autovac_report_activity(VacuumStmt *vacstmt, Oid relid);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -222,9 +226,9 @@ AutoVacMain(int argc, char *argv[])
|
|||
{
|
||||
ListCell *cell;
|
||||
List *dblist;
|
||||
TransactionId nextXid;
|
||||
autovac_dbase *db;
|
||||
bool whole_db;
|
||||
TransactionId xidForceLimit;
|
||||
bool for_xid_wrap;
|
||||
sigjmp_buf local_sigjmp_buf;
|
||||
|
||||
/* we are a postmaster subprocess now */
|
||||
|
@ -315,29 +319,28 @@ AutoVacMain(int argc, char *argv[])
|
|||
dblist = autovac_get_database_list();
|
||||
|
||||
/*
|
||||
* Get the next Xid that was current as of the last checkpoint. We need it
|
||||
* to determine whether databases are about to need database-wide vacuums.
|
||||
* Determine the oldest datfrozenxid/relfrozenxid that we will allow
|
||||
* to pass without forcing a vacuum. (This limit can be tightened for
|
||||
* particular tables, but not loosened.)
|
||||
*/
|
||||
nextXid = GetRecentNextXid();
|
||||
recentXid = ReadNewTransactionId();
|
||||
xidForceLimit = recentXid - autovacuum_freeze_max_age;
|
||||
/* ensure it's a "normal" XID, else TransactionIdPrecedes misbehaves */
|
||||
if (xidForceLimit < FirstNormalTransactionId)
|
||||
xidForceLimit -= FirstNormalTransactionId;
|
||||
|
||||
/*
|
||||
* Choose a database to connect to. We pick the database that was least
|
||||
* recently auto-vacuumed, or one that needs database-wide vacuum (to
|
||||
* prevent Xid wraparound-related data loss).
|
||||
* recently auto-vacuumed, or one that needs vacuuming to prevent Xid
|
||||
* wraparound-related data loss. If any db at risk of wraparound is
|
||||
* found, we pick the one with oldest datfrozenxid,
|
||||
* independently of autovacuum times.
|
||||
*
|
||||
* Note that a database with no stats entry is not considered, except for
|
||||
* Xid wraparound purposes. The theory is that if no one has ever
|
||||
* connected to it since the stats were last initialized, it doesn't need
|
||||
* vacuuming.
|
||||
*
|
||||
* Note that if we are called when autovacuum is nominally disabled in
|
||||
* postgresql.conf, we assume the postmaster has invoked us because a
|
||||
* database is in danger of Xid wraparound. In that case, we only
|
||||
* consider vacuuming whole databases, not individual tables; and we pick
|
||||
* the oldest one, regardless of it's true age. So the criteria for
|
||||
* deciding that a database needs a database-wide vacuum is elsewhere
|
||||
* (currently in vac_truncate_clog).
|
||||
*
|
||||
* XXX This could be improved if we had more info about whether it needs
|
||||
* vacuuming before connecting to it. Perhaps look through the pgstats
|
||||
* data for the database's tables? One idea is to keep track of the
|
||||
|
@ -346,84 +349,40 @@ AutoVacMain(int argc, char *argv[])
|
|||
* starvation for less busy databases.
|
||||
*/
|
||||
db = NULL;
|
||||
whole_db = false;
|
||||
|
||||
if (AutoVacuumingActive())
|
||||
for_xid_wrap = false;
|
||||
foreach(cell, dblist)
|
||||
{
|
||||
/*
|
||||
* We look for the database that most urgently needs a database-wide
|
||||
* vacuum. We decide that a database-wide vacuum is needed 100000
|
||||
* transactions sooner than vacuum.c's vac_truncate_clog() would
|
||||
* decide to start giving warnings. If any such db is found, we
|
||||
* ignore all other dbs.
|
||||
*
|
||||
* Unlike vacuum.c, we also look at vacuumxid. This is so that
|
||||
* pg_clog can be kept trimmed to a reasonable size.
|
||||
*/
|
||||
foreach(cell, dblist)
|
||||
autovac_dbase *tmp = lfirst(cell);
|
||||
|
||||
/* Find pgstat entry if any */
|
||||
tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid);
|
||||
|
||||
/* Check to see if this one is at risk of wraparound */
|
||||
if (TransactionIdPrecedes(tmp->frozenxid, xidForceLimit))
|
||||
{
|
||||
autovac_dbase *tmp = lfirst(cell);
|
||||
bool this_whole_db;
|
||||
int32 true_age,
|
||||
vacuum_age;
|
||||
|
||||
true_age = (int32) (nextXid - tmp->minxid);
|
||||
vacuum_age = (int32) (nextXid - tmp->vacuumxid);
|
||||
tmp->age = Max(true_age, vacuum_age);
|
||||
|
||||
this_whole_db = (tmp->age >
|
||||
(int32) ((MaxTransactionId >> 3) * 3 - 100000));
|
||||
|
||||
if (whole_db || this_whole_db)
|
||||
{
|
||||
if (!this_whole_db)
|
||||
continue;
|
||||
if (db == NULL || tmp->age > db->age)
|
||||
{
|
||||
db = tmp;
|
||||
whole_db = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, skip a database with no pgstat entry; it means it
|
||||
* hasn't seen any activity.
|
||||
*/
|
||||
tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid);
|
||||
if (!tmp->entry)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Remember the db with oldest autovac time.
|
||||
*/
|
||||
if (db == NULL ||
|
||||
tmp->entry->last_autovac_time < db->entry->last_autovac_time)
|
||||
TransactionIdPrecedes(tmp->frozenxid, db->frozenxid))
|
||||
db = tmp;
|
||||
for_xid_wrap = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else if (for_xid_wrap)
|
||||
continue; /* ignore not-at-risk DBs */
|
||||
|
||||
/*
|
||||
* If autovacuuming is not active, we must have gotten here because a
|
||||
* backend signalled the postmaster. Pick up the database with the
|
||||
* greatest age, and apply a database-wide vacuum on it.
|
||||
* Otherwise, skip a database with no pgstat entry; it means it
|
||||
* hasn't seen any activity.
|
||||
*/
|
||||
int32 oldest = 0;
|
||||
if (!tmp->entry)
|
||||
continue;
|
||||
|
||||
whole_db = true;
|
||||
foreach(cell, dblist)
|
||||
{
|
||||
autovac_dbase *tmp = lfirst(cell);
|
||||
int32 age = (int32) (nextXid - tmp->minxid);
|
||||
|
||||
if (age > oldest)
|
||||
{
|
||||
oldest = age;
|
||||
db = tmp;
|
||||
}
|
||||
}
|
||||
Assert(db);
|
||||
/*
|
||||
* Remember the db with oldest autovac time. (If we are here,
|
||||
* both tmp->entry and db->entry must be non-null.)
|
||||
*/
|
||||
if (db == NULL ||
|
||||
tmp->entry->last_autovac_time < db->entry->last_autovac_time)
|
||||
db = tmp;
|
||||
}
|
||||
|
||||
if (db)
|
||||
|
@ -460,10 +419,7 @@ AutoVacMain(int argc, char *argv[])
|
|||
/*
|
||||
* And do an appropriate amount of work
|
||||
*/
|
||||
if (whole_db)
|
||||
process_whole_db();
|
||||
else
|
||||
do_autovacuum(db->entry);
|
||||
do_autovacuum(db->entry);
|
||||
}
|
||||
|
||||
/* One iteration done, go away */
|
||||
|
@ -485,8 +441,7 @@ autovac_get_database_list(void)
|
|||
FILE *db_file;
|
||||
Oid db_id;
|
||||
Oid db_tablespace;
|
||||
TransactionId db_minxid;
|
||||
TransactionId db_vacuumxid;
|
||||
TransactionId db_frozenxid;
|
||||
|
||||
filename = database_getflatfilename();
|
||||
db_file = AllocateFile(filename, "r");
|
||||
|
@ -496,8 +451,7 @@ autovac_get_database_list(void)
|
|||
errmsg("could not open file \"%s\": %m", filename)));
|
||||
|
||||
while (read_pg_database_line(db_file, thisname, &db_id,
|
||||
&db_tablespace, &db_minxid,
|
||||
&db_vacuumxid))
|
||||
&db_tablespace, &db_frozenxid))
|
||||
{
|
||||
autovac_dbase *db;
|
||||
|
||||
|
@ -505,11 +459,9 @@ autovac_get_database_list(void)
|
|||
|
||||
db->oid = db_id;
|
||||
db->name = pstrdup(thisname);
|
||||
db->minxid = db_minxid;
|
||||
db->vacuumxid = db_vacuumxid;
|
||||
/* these get set later: */
|
||||
db->frozenxid = db_frozenxid;
|
||||
/* this gets set later: */
|
||||
db->entry = NULL;
|
||||
db->age = 0;
|
||||
|
||||
dblist = lappend(dblist, db);
|
||||
}
|
||||
|
@ -520,60 +472,12 @@ autovac_get_database_list(void)
|
|||
return dblist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a whole database. If it's a template database or is disallowing
|
||||
* connection by means of datallowconn=false, then issue a VACUUM FREEZE.
|
||||
* Else use a plain VACUUM.
|
||||
*/
|
||||
static void
|
||||
process_whole_db(void)
|
||||
{
|
||||
HeapTuple tup;
|
||||
Form_pg_database dbForm;
|
||||
bool freeze;
|
||||
|
||||
/* Start a transaction so our commands have one to play into. */
|
||||
StartTransactionCommand();
|
||||
|
||||
/* functions in indexes may want a snapshot set */
|
||||
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
|
||||
|
||||
/*
|
||||
* Clean up any dead statistics collector entries for this DB.
|
||||
*/
|
||||
pgstat_vacuum_tabstat();
|
||||
|
||||
/* Look up the pg_database entry and decide whether to FREEZE */
|
||||
tup = SearchSysCache(DATABASEOID,
|
||||
ObjectIdGetDatum(MyDatabaseId),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
|
||||
|
||||
dbForm = (Form_pg_database) GETSTRUCT(tup);
|
||||
|
||||
if (!dbForm->datallowconn || dbForm->datistemplate)
|
||||
freeze = true;
|
||||
else
|
||||
freeze = false;
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
elog(DEBUG2, "autovacuum: VACUUM%s whole database",
|
||||
(freeze) ? " FREEZE" : "");
|
||||
|
||||
autovacuum_do_vac_analyze(NIL, true, false, freeze);
|
||||
|
||||
/* Finally close out the last transaction. */
|
||||
CommitTransactionCommand();
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a database table-by-table
|
||||
*
|
||||
* dbentry must be a valid pointer to the database entry in the stats
|
||||
* databases' hash table, and it will be used to determine whether vacuum or
|
||||
* analyze is needed on a per-table basis.
|
||||
* dbentry is either a pointer to the database entry in the stats databases
|
||||
* hash table, or NULL if we couldn't find any entry (the latter case occurs
|
||||
* only if we are forcing a vacuum for anti-wrap purposes).
|
||||
*
|
||||
* Note that CHECK_FOR_INTERRUPTS is supposed to be used in certain spots in
|
||||
* order not to ignore shutdown commands for too long.
|
||||
|
@ -585,6 +489,7 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
|
|||
avRel;
|
||||
HeapTuple tuple;
|
||||
HeapScanDesc relScan;
|
||||
Form_pg_database dbForm;
|
||||
List *vacuum_tables = NIL;
|
||||
List *toast_table_ids = NIL;
|
||||
ListCell *cell;
|
||||
|
@ -603,6 +508,25 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
|
|||
*/
|
||||
pgstat_vacuum_tabstat();
|
||||
|
||||
/*
|
||||
* Find the pg_database entry and select the default freeze_min_age.
|
||||
* We use zero in template and nonconnectable databases,
|
||||
* else the system-wide default.
|
||||
*/
|
||||
tuple = SearchSysCache(DATABASEOID,
|
||||
ObjectIdGetDatum(MyDatabaseId),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
|
||||
dbForm = (Form_pg_database) GETSTRUCT(tuple);
|
||||
|
||||
if (dbForm->datistemplate || !dbForm->datallowconn)
|
||||
default_freeze_min_age = 0;
|
||||
else
|
||||
default_freeze_min_age = vacuum_freeze_min_age;
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/*
|
||||
* StartTransactionCommand and CommitTransactionCommand will automatically
|
||||
* switch to other contexts. We need this one to keep the list of
|
||||
|
@ -676,9 +600,11 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
|
|||
if (classForm->relisshared && PointerIsValid(shared))
|
||||
tabentry = hash_search(shared->tables, &relid,
|
||||
HASH_FIND, NULL);
|
||||
else
|
||||
else if (PointerIsValid(dbentry))
|
||||
tabentry = hash_search(dbentry->tables, &relid,
|
||||
HASH_FIND, NULL);
|
||||
else
|
||||
tabentry = NULL;
|
||||
|
||||
test_rel_for_autovac(relid, tabentry, classForm, avForm,
|
||||
&vacuum_tables, &toast_table_ids);
|
||||
|
@ -719,12 +645,18 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
|
|||
VacuumCostDelay = tab->vacuum_cost_delay;
|
||||
VacuumCostLimit = tab->vacuum_cost_limit;
|
||||
|
||||
autovacuum_do_vac_analyze(list_make1_oid(tab->relid),
|
||||
autovacuum_do_vac_analyze(tab->relid,
|
||||
tab->dovacuum,
|
||||
tab->doanalyze,
|
||||
false);
|
||||
tab->freeze_min_age);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update pg_database.datfrozenxid, and truncate pg_clog if possible.
|
||||
* We only need to do this once, not after each table.
|
||||
*/
|
||||
vac_update_datfrozenxid();
|
||||
|
||||
/* Finally close out the last transaction. */
|
||||
CommitTransactionCommand();
|
||||
}
|
||||
|
@ -746,10 +678,13 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
|
|||
* the number of tuples (both live and dead) that there were as of the last
|
||||
* analyze. This is asymmetric to the VACUUM case.
|
||||
*
|
||||
* We also force vacuum if the table's relfrozenxid is more than freeze_max_age
|
||||
* transactions back.
|
||||
*
|
||||
* A table whose pg_autovacuum.enabled value is false, is automatically
|
||||
* skipped. Thus autovacuum can be disabled for specific tables. Also,
|
||||
* when the stats collector does not have data about a table, it will be
|
||||
* skipped.
|
||||
* skipped (unless we have to vacuum it due to freeze_max_age). Thus
|
||||
* autovacuum can be disabled for specific tables. Also, when the stats
|
||||
* collector does not have data about a table, it will be skipped.
|
||||
*
|
||||
* A table whose vac_base_thresh value is <0 takes the base value from the
|
||||
* autovacuum_vacuum_threshold GUC variable. Similarly, a vac_scale_factor
|
||||
|
@ -763,44 +698,28 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
|
|||
List **vacuum_tables,
|
||||
List **toast_table_ids)
|
||||
{
|
||||
bool force_vacuum;
|
||||
bool dovacuum;
|
||||
bool doanalyze;
|
||||
float4 reltuples; /* pg_class.reltuples */
|
||||
|
||||
/* constants from pg_autovacuum or GUC variables */
|
||||
int vac_base_thresh,
|
||||
anl_base_thresh;
|
||||
float4 vac_scale_factor,
|
||||
anl_scale_factor;
|
||||
|
||||
/* thresholds calculated from above constants */
|
||||
float4 vacthresh,
|
||||
anlthresh;
|
||||
|
||||
/* number of vacuum (resp. analyze) tuples at this time */
|
||||
float4 vactuples,
|
||||
anltuples;
|
||||
|
||||
/* freeze parameters */
|
||||
int freeze_min_age;
|
||||
int freeze_max_age;
|
||||
TransactionId xidForceLimit;
|
||||
/* cost-based vacuum delay parameters */
|
||||
int vac_cost_limit;
|
||||
int vac_cost_delay;
|
||||
bool dovacuum;
|
||||
bool doanalyze;
|
||||
|
||||
/* User disabled it in pg_autovacuum? */
|
||||
if (avForm && !avForm->enabled)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Skip a table not found in stat hash. If it's not acted upon, there's
|
||||
* no need to vacuum it. (Note that database-level check will take care
|
||||
* of Xid wraparound.)
|
||||
*/
|
||||
if (!PointerIsValid(tabentry))
|
||||
return;
|
||||
|
||||
reltuples = classForm->reltuples;
|
||||
vactuples = tabentry->n_dead_tuples;
|
||||
anltuples = tabentry->n_live_tuples + tabentry->n_dead_tuples -
|
||||
tabentry->last_anl_tuples;
|
||||
|
||||
/*
|
||||
* If there is a tuple in pg_autovacuum, use it; else, use the GUC
|
||||
|
@ -819,6 +738,12 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
|
|||
anl_base_thresh = (avForm->anl_base_thresh >= 0) ?
|
||||
avForm->anl_base_thresh : autovacuum_anl_thresh;
|
||||
|
||||
freeze_min_age = (avForm->freeze_min_age >= 0) ?
|
||||
avForm->freeze_min_age : default_freeze_min_age;
|
||||
freeze_max_age = (avForm->freeze_max_age >= 0) ?
|
||||
Min(avForm->freeze_max_age, autovacuum_freeze_max_age) :
|
||||
autovacuum_freeze_max_age;
|
||||
|
||||
vac_cost_limit = (avForm->vac_cost_limit >= 0) ?
|
||||
avForm->vac_cost_limit :
|
||||
((autovacuum_vac_cost_limit >= 0) ?
|
||||
|
@ -837,6 +762,9 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
|
|||
anl_scale_factor = autovacuum_anl_scale;
|
||||
anl_base_thresh = autovacuum_anl_thresh;
|
||||
|
||||
freeze_min_age = default_freeze_min_age;
|
||||
freeze_max_age = autovacuum_freeze_max_age;
|
||||
|
||||
vac_cost_limit = (autovacuum_vac_cost_limit >= 0) ?
|
||||
autovacuum_vac_cost_limit : VacuumCostLimit;
|
||||
|
||||
|
@ -844,22 +772,51 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
|
|||
autovacuum_vac_cost_delay : VacuumCostDelay;
|
||||
}
|
||||
|
||||
vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples;
|
||||
anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
|
||||
/* Force vacuum if table is at risk of wraparound */
|
||||
xidForceLimit = recentXid - freeze_max_age;
|
||||
if (xidForceLimit < FirstNormalTransactionId)
|
||||
xidForceLimit -= FirstNormalTransactionId;
|
||||
force_vacuum = (TransactionIdIsNormal(classForm->relfrozenxid) &&
|
||||
TransactionIdPrecedes(classForm->relfrozenxid,
|
||||
xidForceLimit));
|
||||
|
||||
/*
|
||||
* Note that we don't need to take special consideration for stat reset,
|
||||
* because if that happens, the last vacuum and analyze counts will be
|
||||
* reset too.
|
||||
*/
|
||||
/* User disabled it in pg_autovacuum? (But ignore if at risk) */
|
||||
if (avForm && !avForm->enabled && !force_vacuum)
|
||||
return;
|
||||
|
||||
elog(DEBUG3, "%s: vac: %.0f (threshold %.0f), anl: %.0f (threshold %.0f)",
|
||||
NameStr(classForm->relname),
|
||||
vactuples, vacthresh, anltuples, anlthresh);
|
||||
if (PointerIsValid(tabentry))
|
||||
{
|
||||
reltuples = classForm->reltuples;
|
||||
vactuples = tabentry->n_dead_tuples;
|
||||
anltuples = tabentry->n_live_tuples + tabentry->n_dead_tuples -
|
||||
tabentry->last_anl_tuples;
|
||||
|
||||
/* Determine if this table needs vacuum or analyze. */
|
||||
dovacuum = (vactuples > vacthresh);
|
||||
doanalyze = (anltuples > anlthresh);
|
||||
vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples;
|
||||
anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
|
||||
|
||||
/*
|
||||
* Note that we don't need to take special consideration for stat
|
||||
* reset, because if that happens, the last vacuum and analyze counts
|
||||
* will be reset too.
|
||||
*/
|
||||
elog(DEBUG3, "%s: vac: %.0f (threshold %.0f), anl: %.0f (threshold %.0f)",
|
||||
NameStr(classForm->relname),
|
||||
vactuples, vacthresh, anltuples, anlthresh);
|
||||
|
||||
/* Determine if this table needs vacuum or analyze. */
|
||||
dovacuum = force_vacuum || (vactuples > vacthresh);
|
||||
doanalyze = (anltuples > anlthresh);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Skip a table not found in stat hash, unless we have to force
|
||||
* vacuum for anti-wrap purposes. If it's not acted upon, there's
|
||||
* no need to vacuum it.
|
||||
*/
|
||||
dovacuum = force_vacuum;
|
||||
doanalyze = false;
|
||||
}
|
||||
|
||||
/* ANALYZE refuses to work with pg_statistics */
|
||||
if (relid == StatisticRelationId)
|
||||
|
@ -888,6 +845,7 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
|
|||
tab->toastrelid = classForm->reltoastrelid;
|
||||
tab->dovacuum = dovacuum;
|
||||
tab->doanalyze = doanalyze;
|
||||
tab->freeze_min_age = freeze_min_age;
|
||||
tab->vacuum_cost_limit = vac_cost_limit;
|
||||
tab->vacuum_cost_delay = vac_cost_delay;
|
||||
|
||||
|
@ -904,11 +862,11 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
|
|||
|
||||
/*
|
||||
* autovacuum_do_vac_analyze
|
||||
* Vacuum and/or analyze a list of tables; or all tables if relids = NIL
|
||||
* Vacuum and/or analyze the specified table
|
||||
*/
|
||||
static void
|
||||
autovacuum_do_vac_analyze(List *relids, bool dovacuum, bool doanalyze,
|
||||
bool freeze)
|
||||
autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze,
|
||||
int freeze_min_age)
|
||||
{
|
||||
VacuumStmt *vacstmt;
|
||||
MemoryContext old_cxt;
|
||||
|
@ -932,15 +890,15 @@ autovacuum_do_vac_analyze(List *relids, bool dovacuum, bool doanalyze,
|
|||
vacstmt->vacuum = dovacuum;
|
||||
vacstmt->full = false;
|
||||
vacstmt->analyze = doanalyze;
|
||||
vacstmt->freeze = freeze;
|
||||
vacstmt->freeze_min_age = freeze_min_age;
|
||||
vacstmt->verbose = false;
|
||||
vacstmt->relation = NULL; /* all tables, or not used if relids != NIL */
|
||||
vacstmt->relation = NULL; /* not used since we pass relids list */
|
||||
vacstmt->va_cols = NIL;
|
||||
|
||||
/* Let pgstat know what we're doing */
|
||||
autovac_report_activity(vacstmt, relids);
|
||||
autovac_report_activity(vacstmt, relid);
|
||||
|
||||
vacuum(vacstmt, relids);
|
||||
vacuum(vacstmt, list_make1_oid(relid));
|
||||
|
||||
pfree(vacstmt);
|
||||
MemoryContextSwitchTo(old_cxt);
|
||||
|
@ -958,48 +916,35 @@ autovacuum_do_vac_analyze(List *relids, bool dovacuum, bool doanalyze,
|
|||
* bother to report "<IDLE>" or some such.
|
||||
*/
|
||||
static void
|
||||
autovac_report_activity(VacuumStmt *vacstmt, List *relids)
|
||||
autovac_report_activity(VacuumStmt *vacstmt, Oid relid)
|
||||
{
|
||||
char *relname = get_rel_name(relid);
|
||||
char *nspname = get_namespace_name(get_rel_namespace(relid));
|
||||
#define MAX_AUTOVAC_ACTIV_LEN (NAMEDATALEN * 2 + 32)
|
||||
char activity[MAX_AUTOVAC_ACTIV_LEN];
|
||||
|
||||
/*
|
||||
* This case is not currently exercised by the autovac code. Fill it in
|
||||
* if needed.
|
||||
*/
|
||||
if (list_length(relids) > 1)
|
||||
elog(WARNING, "vacuuming >1 rel unsupported");
|
||||
|
||||
/* Report the command and possible options */
|
||||
if (vacstmt->vacuum)
|
||||
snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
|
||||
"VACUUM%s%s%s",
|
||||
vacstmt->full ? " FULL" : "",
|
||||
vacstmt->freeze ? " FREEZE" : "",
|
||||
"VACUUM%s",
|
||||
vacstmt->analyze ? " ANALYZE" : "");
|
||||
else if (vacstmt->analyze)
|
||||
else
|
||||
snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
|
||||
"ANALYZE");
|
||||
|
||||
/* Report the qualified name of the first relation, if any */
|
||||
if (relids)
|
||||
/*
|
||||
* Report the qualified name of the relation.
|
||||
*
|
||||
* Paranoia is appropriate here in case relation was recently dropped
|
||||
* --- the lsyscache routines we just invoked will return NULL rather
|
||||
* than failing.
|
||||
*/
|
||||
if (relname && nspname)
|
||||
{
|
||||
Oid relid = linitial_oid(relids);
|
||||
char *relname = get_rel_name(relid);
|
||||
char *nspname = get_namespace_name(get_rel_namespace(relid));
|
||||
int len = strlen(activity);
|
||||
|
||||
/*
|
||||
* Paranoia is appropriate here in case relation was recently dropped
|
||||
* --- the lsyscache routines we just invoked will return NULL rather
|
||||
* than failing.
|
||||
*/
|
||||
if (relname && nspname)
|
||||
{
|
||||
int len = strlen(activity);
|
||||
|
||||
snprintf(activity + len, MAX_AUTOVAC_ACTIV_LEN - len,
|
||||
" %s.%s", nspname, relname);
|
||||
}
|
||||
snprintf(activity + len, MAX_AUTOVAC_ACTIV_LEN - len,
|
||||
" %s.%s", nspname, relname);
|
||||
}
|
||||
|
||||
pgstat_report_activity(activity);
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.500 2006/10/04 00:29:56 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.501 2006/11/05 22:42:09 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
|
@ -217,6 +217,8 @@ static bool FatalError = false; /* T if recovering from backend crash */
|
|||
bool ClientAuthInProgress = false; /* T during new-client
|
||||
* authentication */
|
||||
|
||||
static bool force_autovac = false; /* received START_AUTOVAC signal */
|
||||
|
||||
/*
|
||||
* State for assigning random salts and cancel keys.
|
||||
* Also, the global MyCancelKey passes the cancel key assigned to a given
|
||||
|
@ -1231,9 +1233,13 @@ ServerLoop(void)
|
|||
* (It'll die relatively quickly.) We check that it's not started too
|
||||
* frequently in autovac_start.
|
||||
*/
|
||||
if (AutoVacuumingActive() && AutoVacPID == 0 &&
|
||||
if ((AutoVacuumingActive() || force_autovac) && AutoVacPID == 0 &&
|
||||
StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
|
||||
{
|
||||
AutoVacPID = autovac_start();
|
||||
if (AutoVacPID != 0)
|
||||
force_autovac = false; /* signal successfully processed */
|
||||
}
|
||||
|
||||
/* If we have lost the archiver, try to start a new one */
|
||||
if (XLogArchivingActive() && PgArchPID == 0 &&
|
||||
|
@ -2100,9 +2106,7 @@ reaper(SIGNAL_ARGS)
|
|||
/*
|
||||
* Was it the autovacuum process? Normal exit can be ignored; we'll
|
||||
* start a new one at the next iteration of the postmaster's main
|
||||
* loop, if necessary.
|
||||
*
|
||||
* An unexpected exit must crash the system.
|
||||
* loop, if necessary. An unexpected exit is treated as a crash.
|
||||
*/
|
||||
if (AutoVacPID != 0 && pid == AutoVacPID)
|
||||
{
|
||||
|
@ -3424,12 +3428,16 @@ sigusr1_handler(SIGNAL_ARGS)
|
|||
|
||||
if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC))
|
||||
{
|
||||
/* start one iteration of the autovacuum daemon */
|
||||
if (Shutdown == NoShutdown)
|
||||
{
|
||||
Assert(!AutoVacuumingActive());
|
||||
AutoVacPID = autovac_start();
|
||||
}
|
||||
/*
|
||||
* Start one iteration of the autovacuum daemon, even if autovacuuming
|
||||
* is nominally not enabled. This is so we can have an active defense
|
||||
* against transaction ID wraparound. We set a flag for the main loop
|
||||
* to do it rather than trying to do it here --- this is because the
|
||||
* autovac process itself may send the signal, and we want to handle
|
||||
* that by launching another iteration as soon as the current one
|
||||
* completes.
|
||||
*/
|
||||
force_autovac = true;
|
||||
}
|
||||
|
||||
PG_SETMASK(&UnBlockSig);
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.18 2006/10/04 00:29:57 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.19 2006/11/05 22:42:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -213,7 +213,9 @@ TransactionIdIsInProgress(TransactionId xid)
|
|||
|
||||
/*
|
||||
* Don't bother checking a transaction older than RecentXmin; it could not
|
||||
* possibly still be running.
|
||||
* possibly still be running. (Note: in particular, this guarantees
|
||||
* that we reject InvalidTransactionId, FrozenTransactionId, etc as
|
||||
* not running.)
|
||||
*/
|
||||
if (TransactionIdPrecedes(xid, RecentXmin))
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.21 2006/07/14 14:52:25 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.22 2006/11/05 22:42:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -163,7 +163,7 @@ name_okay(const char *str)
|
|||
/*
|
||||
* write_database_file: update the flat database file
|
||||
*
|
||||
* A side effect is to determine the oldest database's datminxid
|
||||
* A side effect is to determine the oldest database's datfrozenxid
|
||||
* so we can set or update the XID wrap limit.
|
||||
*/
|
||||
static void
|
||||
|
@ -177,7 +177,7 @@ write_database_file(Relation drel)
|
|||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
NameData oldest_datname;
|
||||
TransactionId oldest_datminxid = InvalidTransactionId;
|
||||
TransactionId oldest_datfrozenxid = InvalidTransactionId;
|
||||
|
||||
/*
|
||||
* Create a temporary filename to be renamed later. This prevents the
|
||||
|
@ -208,27 +208,23 @@ write_database_file(Relation drel)
|
|||
char *datname;
|
||||
Oid datoid;
|
||||
Oid dattablespace;
|
||||
TransactionId datminxid,
|
||||
datvacuumxid;
|
||||
TransactionId datfrozenxid;
|
||||
|
||||
datname = NameStr(dbform->datname);
|
||||
datoid = HeapTupleGetOid(tuple);
|
||||
dattablespace = dbform->dattablespace;
|
||||
datminxid = dbform->datminxid;
|
||||
datvacuumxid = dbform->datvacuumxid;
|
||||
datfrozenxid = dbform->datfrozenxid;
|
||||
|
||||
/*
|
||||
* Identify the oldest datminxid, ignoring databases that are not
|
||||
* connectable (we assume they are safely frozen). This must match
|
||||
* Identify the oldest datfrozenxid. This must match
|
||||
* the logic in vac_truncate_clog() in vacuum.c.
|
||||
*/
|
||||
if (dbform->datallowconn &&
|
||||
TransactionIdIsNormal(datminxid))
|
||||
if (TransactionIdIsNormal(datfrozenxid))
|
||||
{
|
||||
if (oldest_datminxid == InvalidTransactionId ||
|
||||
TransactionIdPrecedes(datminxid, oldest_datminxid))
|
||||
if (oldest_datfrozenxid == InvalidTransactionId ||
|
||||
TransactionIdPrecedes(datfrozenxid, oldest_datfrozenxid))
|
||||
{
|
||||
oldest_datminxid = datminxid;
|
||||
oldest_datfrozenxid = datfrozenxid;
|
||||
namestrcpy(&oldest_datname, datname);
|
||||
}
|
||||
}
|
||||
|
@ -244,14 +240,14 @@ write_database_file(Relation drel)
|
|||
}
|
||||
|
||||
/*
|
||||
* The file format is: "dbname" oid tablespace minxid vacuumxid
|
||||
* The file format is: "dbname" oid tablespace frozenxid
|
||||
*
|
||||
* The xids are not needed for backend startup, but are of use to
|
||||
* autovacuum, and might also be helpful for forensic purposes.
|
||||
*/
|
||||
fputs_quote(datname, fp);
|
||||
fprintf(fp, " %u %u %u %u\n",
|
||||
datoid, dattablespace, datminxid, datvacuumxid);
|
||||
fprintf(fp, " %u %u %u\n",
|
||||
datoid, dattablespace, datfrozenxid);
|
||||
}
|
||||
heap_endscan(scan);
|
||||
|
||||
|
@ -272,10 +268,10 @@ write_database_file(Relation drel)
|
|||
tempname, filename)));
|
||||
|
||||
/*
|
||||
* Set the transaction ID wrap limit using the oldest datminxid
|
||||
* Set the transaction ID wrap limit using the oldest datfrozenxid
|
||||
*/
|
||||
if (oldest_datminxid != InvalidTransactionId)
|
||||
SetTransactionIdLimit(oldest_datminxid, &oldest_datname);
|
||||
if (oldest_datfrozenxid != InvalidTransactionId)
|
||||
SetTransactionIdLimit(oldest_datfrozenxid, &oldest_datname);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.171 2006/10/04 00:30:02 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.172 2006/11/05 22:42:09 tgl Exp $
|
||||
*
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
|
@ -77,7 +77,7 @@ FindMyDatabase(const char *name, Oid *db_id, Oid *db_tablespace)
|
|||
char *filename;
|
||||
FILE *db_file;
|
||||
char thisname[NAMEDATALEN];
|
||||
TransactionId dummyxid;
|
||||
TransactionId db_frozenxid;
|
||||
|
||||
filename = database_getflatfilename();
|
||||
db_file = AllocateFile(filename, "r");
|
||||
|
@ -87,8 +87,7 @@ FindMyDatabase(const char *name, Oid *db_id, Oid *db_tablespace)
|
|||
errmsg("could not open file \"%s\": %m", filename)));
|
||||
|
||||
while (read_pg_database_line(db_file, thisname, db_id,
|
||||
db_tablespace, &dummyxid,
|
||||
&dummyxid))
|
||||
db_tablespace, &db_frozenxid))
|
||||
{
|
||||
if (strcmp(thisname, name) == 0)
|
||||
{
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.357 2006/10/19 18:32:47 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.358 2006/11/05 22:42:09 tgl Exp $
|
||||
*
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -1330,6 +1330,15 @@ static struct config_int ConfigureNamesInt[] =
|
|||
0, 0, INT_MAX, NULL, NULL
|
||||
},
|
||||
|
||||
{
|
||||
{"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
|
||||
gettext_noop("Minimum age at which VACUUM should freeze a table row."),
|
||||
NULL
|
||||
},
|
||||
&vacuum_freeze_min_age,
|
||||
100000000, 0, 1000000000, NULL, NULL
|
||||
},
|
||||
|
||||
{
|
||||
{"max_fsm_relations", PGC_POSTMASTER, RESOURCES_FSM,
|
||||
gettext_noop("Sets the maximum number of tables and indexes for which free space is tracked."),
|
||||
|
@ -1576,6 +1585,15 @@ static struct config_int ConfigureNamesInt[] =
|
|||
&autovacuum_anl_thresh,
|
||||
250, 0, INT_MAX, NULL, NULL
|
||||
},
|
||||
{
|
||||
/* see varsup.c for why this is PGC_POSTMASTER not PGC_SIGHUP */
|
||||
{"autovacuum_freeze_max_age", PGC_POSTMASTER, AUTOVACUUM,
|
||||
gettext_noop("Age at which to autovacuum a table to prevent transacion ID wraparound."),
|
||||
NULL
|
||||
},
|
||||
&autovacuum_freeze_max_age,
|
||||
200000000, 100000000, 2000000000, NULL, NULL
|
||||
},
|
||||
|
||||
{
|
||||
{"tcp_keepalives_idle", PGC_USERSET, CLIENT_CONN_OTHER,
|
||||
|
|
|
@ -373,6 +373,8 @@
|
|||
# vacuum
|
||||
#autovacuum_analyze_scale_factor = 0.1 # fraction of rel size before
|
||||
# analyze
|
||||
#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
|
||||
# (change requires restart)
|
||||
#autovacuum_vacuum_cost_delay = -1 # default vacuum cost delay for
|
||||
# autovacuum, -1 means use
|
||||
# vacuum_cost_delay
|
||||
|
@ -394,6 +396,7 @@
|
|||
#default_transaction_isolation = 'read committed'
|
||||
#default_transaction_read_only = off
|
||||
#statement_timeout = 0 # 0 is disabled
|
||||
#vacuum_freeze_min_age = 100000000
|
||||
|
||||
# - Locale and Formatting -
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.98 2006/10/04 00:30:04 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.99 2006/11/05 22:42:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -1099,9 +1099,11 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
|
|||
{
|
||||
/*
|
||||
* "Deleting" xact really only locked it, so the tuple is live in any
|
||||
* case. However, we must make sure that either XMAX_COMMITTED or
|
||||
* XMAX_INVALID gets set once the xact is gone; otherwise it is unsafe
|
||||
* to recycle CLOG status after vacuuming.
|
||||
* case. However, we should make sure that either XMAX_COMMITTED or
|
||||
* XMAX_INVALID gets set once the xact is gone, to reduce the costs
|
||||
* of examining the tuple for future xacts. Also, marking dead
|
||||
* MultiXacts as invalid here provides defense against MultiXactId
|
||||
* wraparound (see also comments in heap_freeze_tuple()).
|
||||
*/
|
||||
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/access/clog.h,v 1.17 2006/03/24 04:32:13 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/access/clog.h,v 1.18 2006/11/05 22:42:09 tgl Exp $
|
||||
*/
|
||||
#ifndef CLOG_H
|
||||
#define CLOG_H
|
||||
|
@ -46,6 +46,7 @@ extern void TruncateCLOG(TransactionId oldestXact);
|
|||
|
||||
/* XLOG stuff */
|
||||
#define CLOG_ZEROPAGE 0x00
|
||||
#define CLOG_TRUNCATE 0x10
|
||||
|
||||
extern void clog_redo(XLogRecPtr lsn, XLogRecord *record);
|
||||
extern void clog_desc(StringInfo buf, uint8 xl_info, char *rec);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.116 2006/10/04 00:30:07 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.117 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -170,6 +170,8 @@ extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
|
|||
TransactionId *update_xmax, CommandId cid,
|
||||
LockTupleMode mode, bool nowait);
|
||||
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
|
||||
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
|
||||
Buffer buf);
|
||||
|
||||
extern Oid simple_heap_insert(Relation relation, HeapTuple tup);
|
||||
extern void simple_heap_delete(Relation relation, ItemPointer tid);
|
||||
|
@ -181,11 +183,17 @@ extern void heap_restrpos(HeapScanDesc scan);
|
|||
|
||||
extern void heap_redo(XLogRecPtr lsn, XLogRecord *rptr);
|
||||
extern void heap_desc(StringInfo buf, uint8 xl_info, char *rec);
|
||||
extern XLogRecPtr log_heap_clean(Relation reln, Buffer buffer,
|
||||
OffsetNumber *unused, int uncnt);
|
||||
extern void heap2_redo(XLogRecPtr lsn, XLogRecord *rptr);
|
||||
extern void heap2_desc(StringInfo buf, uint8 xl_info, char *rec);
|
||||
|
||||
extern XLogRecPtr log_heap_move(Relation reln, Buffer oldbuf,
|
||||
ItemPointerData from,
|
||||
Buffer newbuf, HeapTuple newtup);
|
||||
extern XLogRecPtr log_heap_clean(Relation reln, Buffer buffer,
|
||||
OffsetNumber *unused, int uncnt);
|
||||
extern XLogRecPtr log_heap_freeze(Relation reln, Buffer buffer,
|
||||
TransactionId cutoff_xid,
|
||||
OffsetNumber *offsets, int offcnt);
|
||||
|
||||
/* in common/heaptuple.c */
|
||||
extern Size heap_compute_data_size(TupleDesc tupleDesc,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/access/htup.h,v 1.86 2006/10/04 00:30:07 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/access/htup.h,v 1.87 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -510,6 +510,13 @@ typedef HeapTupleData *HeapTuple;
|
|||
* we can (and we do) restore entire page in redo
|
||||
*/
|
||||
#define XLOG_HEAP_INIT_PAGE 0x80
|
||||
/*
|
||||
* We ran out of opcodes, so heapam.c now has a second RmgrId. These opcodes
|
||||
* are associated with RM_HEAP2_ID, but are not logically different from
|
||||
* the ones above associated with RM_HEAP_ID. We apply XLOG_HEAP_OPMASK,
|
||||
* although currently XLOG_HEAP_INIT_PAGE is not used for any of these.
|
||||
*/
|
||||
#define XLOG_HEAP2_FREEZE 0x00
|
||||
|
||||
/*
|
||||
* All what we need to find changed tuple
|
||||
|
@ -613,4 +620,15 @@ typedef struct xl_heap_inplace
|
|||
|
||||
#define SizeOfHeapInplace (offsetof(xl_heap_inplace, target) + SizeOfHeapTid)
|
||||
|
||||
/* This is what we need to know about tuple freezing during vacuum */
|
||||
typedef struct xl_heap_freeze
|
||||
{
|
||||
RelFileNode node;
|
||||
BlockNumber block;
|
||||
TransactionId cutoff_xid;
|
||||
/* TUPLE OFFSET NUMBERS FOLLOW AT THE END */
|
||||
} xl_heap_freeze;
|
||||
|
||||
#define SizeOfHeapFreeze (offsetof(xl_heap_freeze, cutoff_xid) + sizeof(TransactionId))
|
||||
|
||||
#endif /* HTUP_H */
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Resource managers definition
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/access/rmgr.h,v 1.16 2006/05/02 11:28:55 teodor Exp $
|
||||
* $PostgreSQL: pgsql/src/include/access/rmgr.h,v 1.17 2006/11/05 22:42:10 tgl Exp $
|
||||
*/
|
||||
#ifndef RMGR_H
|
||||
#define RMGR_H
|
||||
|
@ -12,6 +12,9 @@ typedef uint8 RmgrId;
|
|||
|
||||
/*
|
||||
* Built-in resource managers
|
||||
*
|
||||
* Note: RM_MAX_ID could be as much as 255 without breaking the XLOG file
|
||||
* format, but we keep it small to minimize the size of RmgrTable[].
|
||||
*/
|
||||
#define RM_XLOG_ID 0
|
||||
#define RM_XACT_ID 1
|
||||
|
@ -20,6 +23,7 @@ typedef uint8 RmgrId;
|
|||
#define RM_DBASE_ID 4
|
||||
#define RM_TBLSPC_ID 5
|
||||
#define RM_MULTIXACT_ID 6
|
||||
#define RM_HEAP2_ID 9
|
||||
#define RM_HEAP_ID 10
|
||||
#define RM_BTREE_ID 11
|
||||
#define RM_HASH_ID 12
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/access/transam.h,v 1.58 2006/07/10 16:20:51 alvherre Exp $
|
||||
* $PostgreSQL: pgsql/src/include/access/transam.h,v 1.59 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -23,7 +23,7 @@
|
|||
* always be considered valid.
|
||||
*
|
||||
* FirstNormalTransactionId is the first "normal" transaction id.
|
||||
* Note: if you need to change it, you must change it in pg_class.h as well.
|
||||
* Note: if you need to change it, you must change pg_class.h as well.
|
||||
* ----------------
|
||||
*/
|
||||
#define InvalidTransactionId ((TransactionId) 0)
|
||||
|
@ -88,6 +88,9 @@ typedef struct VariableCacheData
|
|||
Oid nextOid; /* next OID to assign */
|
||||
uint32 oidCount; /* OIDs available before must do XLOG work */
|
||||
TransactionId nextXid; /* next XID to assign */
|
||||
|
||||
TransactionId oldestXid; /* cluster-wide minimum datfrozenxid */
|
||||
TransactionId xidVacLimit; /* start forcing autovacuums here */
|
||||
TransactionId xidWarnLimit; /* start complaining here */
|
||||
TransactionId xidStopLimit; /* refuse to advance nextXid beyond here */
|
||||
TransactionId xidWrapLimit; /* where the world ends */
|
||||
|
@ -124,7 +127,7 @@ extern bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2);
|
|||
/* in transam/varsup.c */
|
||||
extern TransactionId GetNewTransactionId(bool isSubXact);
|
||||
extern TransactionId ReadNewTransactionId(void);
|
||||
extern void SetTransactionIdLimit(TransactionId oldest_datminxid,
|
||||
extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
|
||||
Name oldest_datname);
|
||||
extern Oid GetNewObjectId(void);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/access/xlog.h,v 1.74 2006/08/21 16:16:31 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/access/xlog.h,v 1.75 2006/11/05 22:42:10 tgl Exp $
|
||||
*/
|
||||
#ifndef XLOG_H
|
||||
#define XLOG_H
|
||||
|
@ -165,7 +165,6 @@ extern void InitXLOGAccess(void);
|
|||
extern void CreateCheckPoint(bool shutdown, bool force);
|
||||
extern void XLogPutNextOid(Oid nextOid);
|
||||
extern XLogRecPtr GetRedoRecPtr(void);
|
||||
extern TransactionId GetRecentNextXid(void);
|
||||
extern void GetNextXidAndEpoch(TransactionId *xid, uint32 *epoch);
|
||||
|
||||
#endif /* XLOG_H */
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.358 2006/09/18 22:40:38 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.359 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -53,6 +53,6 @@
|
|||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200609181
|
||||
#define CATALOG_VERSION_NO 200611051
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.125 2006/10/04 00:30:07 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.126 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* the genbki.sh script reads this file and generates .bki
|
||||
|
@ -404,10 +404,9 @@ DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
|
|||
{ 1259, {"relhaspkey"}, 16, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
|
||||
{ 1259, {"relhasrules"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
|
||||
{ 1259, {"relhassubclass"},16, -1, 1, 24, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
|
||||
{ 1259, {"relminxid"}, 28, -1, 4, 25, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
|
||||
{ 1259, {"relvacuumxid"}, 28, -1, 4, 26, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
|
||||
{ 1259, {"relacl"}, 1034, -1, -1, 27, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
|
||||
{ 1259, {"reloptions"}, 1009, -1, -1, 28, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
|
||||
{ 1259, {"relfrozenxid"}, 28, -1, 4, 25, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
|
||||
{ 1259, {"relacl"}, 1034, -1, -1, 26, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
|
||||
{ 1259, {"reloptions"}, 1009, -1, -1, 27, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
|
||||
|
||||
DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
|
||||
DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
|
||||
|
@ -433,10 +432,9 @@ DATA(insert ( 1259 relhasoids 16 -1 1 21 0 -1 -1 t p c t f f t 0));
|
|||
DATA(insert ( 1259 relhaspkey 16 -1 1 22 0 -1 -1 t p c t f f t 0));
|
||||
DATA(insert ( 1259 relhasrules 16 -1 1 23 0 -1 -1 t p c t f f t 0));
|
||||
DATA(insert ( 1259 relhassubclass 16 -1 1 24 0 -1 -1 t p c t f f t 0));
|
||||
DATA(insert ( 1259 relminxid 28 -1 4 25 0 -1 -1 t p i t f f t 0));
|
||||
DATA(insert ( 1259 relvacuumxid 28 -1 4 26 0 -1 -1 t p i t f f t 0));
|
||||
DATA(insert ( 1259 relacl 1034 -1 -1 27 1 -1 -1 f x i f f f t 0));
|
||||
DATA(insert ( 1259 reloptions 1009 -1 -1 28 1 -1 -1 f x i f f f t 0));
|
||||
DATA(insert ( 1259 relfrozenxid 28 -1 4 25 0 -1 -1 t p i t f f t 0));
|
||||
DATA(insert ( 1259 relacl 1034 -1 -1 26 1 -1 -1 f x i f f f t 0));
|
||||
DATA(insert ( 1259 reloptions 1009 -1 -1 27 1 -1 -1 f x i f f f t 0));
|
||||
DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
|
||||
DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
|
||||
DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_autovacuum.h,v 1.4 2006/03/05 15:58:54 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_autovacuum.h,v 1.5 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -28,14 +28,16 @@
|
|||
#define AutovacuumRelationId 1248
|
||||
CATALOG(pg_autovacuum,1248) BKI_WITHOUT_OIDS
|
||||
{
|
||||
Oid vacrelid; /* OID of table */
|
||||
bool enabled; /* enabled for this table? */
|
||||
Oid vacrelid; /* OID of table */
|
||||
bool enabled; /* enabled for this table? */
|
||||
int4 vac_base_thresh; /* base threshold value */
|
||||
float4 vac_scale_factor; /* reltuples scaling factor */
|
||||
float4 vac_scale_factor; /* reltuples scaling factor */
|
||||
int4 anl_base_thresh; /* base threshold value */
|
||||
float4 anl_scale_factor; /* reltuples scaling factor */
|
||||
int4 vac_cost_delay; /* vacuum cost-based delay */
|
||||
int4 vac_cost_limit; /* vacuum cost limit */
|
||||
float4 anl_scale_factor; /* reltuples scaling factor */
|
||||
int4 vac_cost_delay; /* vacuum cost-based delay */
|
||||
int4 vac_cost_limit; /* vacuum cost limit */
|
||||
int4 freeze_min_age; /* vacuum min freeze age */
|
||||
int4 freeze_max_age; /* max age before forcing vacuum */
|
||||
} FormData_pg_autovacuum;
|
||||
|
||||
/* ----------------
|
||||
|
@ -49,7 +51,7 @@ typedef FormData_pg_autovacuum *Form_pg_autovacuum;
|
|||
* compiler constants for pg_autovacuum
|
||||
* ----------------
|
||||
*/
|
||||
#define Natts_pg_autovacuum 8
|
||||
#define Natts_pg_autovacuum 10
|
||||
#define Anum_pg_autovacuum_vacrelid 1
|
||||
#define Anum_pg_autovacuum_enabled 2
|
||||
#define Anum_pg_autovacuum_vac_base_thresh 3
|
||||
|
@ -58,6 +60,8 @@ typedef FormData_pg_autovacuum *Form_pg_autovacuum;
|
|||
#define Anum_pg_autovacuum_anl_scale_factor 6
|
||||
#define Anum_pg_autovacuum_vac_cost_delay 7
|
||||
#define Anum_pg_autovacuum_vac_cost_limit 8
|
||||
#define Anum_pg_autovacuum_freeze_min_age 9
|
||||
#define Anum_pg_autovacuum_freeze_max_age 10
|
||||
|
||||
/* There are no preloaded tuples in pg_autovacuum.h */
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.96 2006/10/04 00:30:07 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.97 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* the genbki.sh script reads this file and generates .bki
|
||||
|
@ -65,8 +65,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP
|
|||
bool relhaspkey; /* has PRIMARY KEY index */
|
||||
bool relhasrules; /* has associated rules */
|
||||
bool relhassubclass; /* has derived classes */
|
||||
TransactionId relminxid; /* minimum Xid present in table */
|
||||
TransactionId relvacuumxid; /* Xid used as last vacuum OldestXmin */
|
||||
TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
|
||||
|
||||
/*
|
||||
* VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
|
||||
|
@ -80,7 +79,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP
|
|||
|
||||
/* Size of fixed part of pg_class tuples, not counting var-length fields */
|
||||
#define CLASS_TUPLE_SIZE \
|
||||
(offsetof(FormData_pg_class,relvacuumxid) + sizeof(TransactionId))
|
||||
(offsetof(FormData_pg_class,relfrozenxid) + sizeof(TransactionId))
|
||||
|
||||
/* ----------------
|
||||
* Form_pg_class corresponds to a pointer to a tuple with
|
||||
|
@ -94,7 +93,7 @@ typedef FormData_pg_class *Form_pg_class;
|
|||
* ----------------
|
||||
*/
|
||||
|
||||
#define Natts_pg_class 28
|
||||
#define Natts_pg_class 27
|
||||
#define Anum_pg_class_relname 1
|
||||
#define Anum_pg_class_relnamespace 2
|
||||
#define Anum_pg_class_reltype 3
|
||||
|
@ -119,27 +118,27 @@ typedef FormData_pg_class *Form_pg_class;
|
|||
#define Anum_pg_class_relhaspkey 22
|
||||
#define Anum_pg_class_relhasrules 23
|
||||
#define Anum_pg_class_relhassubclass 24
|
||||
#define Anum_pg_class_relminxid 25
|
||||
#define Anum_pg_class_relvacuumxid 26
|
||||
#define Anum_pg_class_relacl 27
|
||||
#define Anum_pg_class_reloptions 28
|
||||
#define Anum_pg_class_relfrozenxid 25
|
||||
#define Anum_pg_class_relacl 26
|
||||
#define Anum_pg_class_reloptions 27
|
||||
|
||||
/* ----------------
|
||||
* initial contents of pg_class
|
||||
*
|
||||
* NOTE: only "bootstrapped" relations need to be declared here. Be sure that
|
||||
* the OIDs listed here match those given in their CATALOG macros.
|
||||
* the OIDs listed here match those given in their CATALOG macros, and that
|
||||
* the relnatts values are correct.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
/* Note: the "3" here stands for FirstNormalTransactionId */
|
||||
DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 23 0 0 0 0 0 t f f f 3 3 _null_ _null_ ));
|
||||
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
|
||||
DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 23 0 0 0 0 0 t f f f 3 _null_ _null_ ));
|
||||
DESCR("");
|
||||
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 0 0 0 0 f f f f 3 3 _null_ _null_ ));
|
||||
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 0 0 0 0 f f f f 3 _null_ _null_ ));
|
||||
DESCR("");
|
||||
DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 18 0 0 0 0 0 t f f f 3 3 _null_ _null_ ));
|
||||
DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 18 0 0 0 0 0 t f f f 3 _null_ _null_ ));
|
||||
DESCR("");
|
||||
DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 28 0 0 0 0 0 t f f f 3 3 _null_ _null_ ));
|
||||
DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 27 0 0 0 0 0 t f f f 3 _null_ _null_ ));
|
||||
DESCR("");
|
||||
|
||||
#define RELKIND_INDEX 'i' /* secondary index */
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_database.h,v 1.41 2006/07/10 16:20:51 alvherre Exp $
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_database.h,v 1.42 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* the genbki.sh script reads this file and generates .bki
|
||||
|
@ -42,8 +42,7 @@ CATALOG(pg_database,1262) BKI_SHARED_RELATION
|
|||
bool datallowconn; /* new connections allowed? */
|
||||
int4 datconnlimit; /* max connections allowed (-1=no limit) */
|
||||
Oid datlastsysoid; /* highest OID to consider a system OID */
|
||||
TransactionId datvacuumxid; /* all XIDs before this are vacuumed */
|
||||
TransactionId datminxid; /* minimum XID present anywhere in the DB */
|
||||
TransactionId datfrozenxid; /* all Xids < this are frozen in this DB */
|
||||
Oid dattablespace; /* default table space for this DB */
|
||||
text datconfig[1]; /* database-specific GUC (VAR LENGTH) */
|
||||
aclitem datacl[1]; /* access permissions (VAR LENGTH) */
|
||||
|
@ -60,7 +59,7 @@ typedef FormData_pg_database *Form_pg_database;
|
|||
* compiler constants for pg_database
|
||||
* ----------------
|
||||
*/
|
||||
#define Natts_pg_database 12
|
||||
#define Natts_pg_database 11
|
||||
#define Anum_pg_database_datname 1
|
||||
#define Anum_pg_database_datdba 2
|
||||
#define Anum_pg_database_encoding 3
|
||||
|
@ -68,13 +67,12 @@ typedef FormData_pg_database *Form_pg_database;
|
|||
#define Anum_pg_database_datallowconn 5
|
||||
#define Anum_pg_database_datconnlimit 6
|
||||
#define Anum_pg_database_datlastsysoid 7
|
||||
#define Anum_pg_database_datvacuumxid 8
|
||||
#define Anum_pg_database_datminxid 9
|
||||
#define Anum_pg_database_dattablespace 10
|
||||
#define Anum_pg_database_datconfig 11
|
||||
#define Anum_pg_database_datacl 12
|
||||
#define Anum_pg_database_datfrozenxid 8
|
||||
#define Anum_pg_database_dattablespace 9
|
||||
#define Anum_pg_database_datconfig 10
|
||||
#define Anum_pg_database_datacl 11
|
||||
|
||||
DATA(insert OID = 1 ( template1 PGUID ENCODING t t -1 0 0 0 1663 _null_ _null_ ));
|
||||
DATA(insert OID = 1 ( template1 PGUID ENCODING t t -1 0 0 1663 _null_ _null_ ));
|
||||
SHDESCR("Default template database");
|
||||
#define TemplateDbOid 1
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.67 2006/07/13 18:01:02 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.68 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -104,8 +104,9 @@ typedef struct VacAttrStats
|
|||
} VacAttrStats;
|
||||
|
||||
|
||||
/* Default statistics target (GUC parameter) */
|
||||
/* GUC parameters */
|
||||
extern DLLIMPORT int default_statistics_target; /* DLLIMPORT for PostGIS */
|
||||
extern int vacuum_freeze_min_age;
|
||||
|
||||
|
||||
/* in commands/vacuum.c */
|
||||
|
@ -117,14 +118,13 @@ extern void vac_update_relstats(Oid relid,
|
|||
BlockNumber num_pages,
|
||||
double num_tuples,
|
||||
bool hasindex,
|
||||
TransactionId minxid,
|
||||
TransactionId vacuumxid);
|
||||
TransactionId frozenxid);
|
||||
extern void vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,
|
||||
TransactionId *oldestXmin,
|
||||
TransactionId *freezeLimit);
|
||||
extern void vac_update_datfrozenxid(void);
|
||||
extern bool vac_is_partial_index(Relation indrel);
|
||||
extern void vacuum_delay_point(void);
|
||||
extern TransactionId vactuple_get_minxid(HeapTuple tuple);
|
||||
|
||||
/* in commands/vacuumlazy.c */
|
||||
extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Interface to hba.c
|
||||
*
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.44 2006/10/04 00:30:08 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.45 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -40,7 +40,6 @@ extern void load_role(void);
|
|||
extern int hba_getauthmethod(hbaPort *port);
|
||||
extern int authident(hbaPort *port);
|
||||
extern bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
|
||||
Oid *dbtablespace, TransactionId *dbminxid,
|
||||
TransactionId *dbvacuumxid);
|
||||
Oid *dbtablespace, TransactionId *dbfrozenxid);
|
||||
|
||||
#endif /* HBA_H */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.333 2006/10/13 21:43:19 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.334 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -1756,8 +1756,8 @@ typedef struct VacuumStmt
|
|||
bool vacuum; /* do VACUUM step */
|
||||
bool full; /* do FULL (non-concurrent) vacuum */
|
||||
bool analyze; /* do ANALYZE step */
|
||||
bool freeze; /* early-freeze option */
|
||||
bool verbose; /* print progress info */
|
||||
int freeze_min_age; /* min freeze age, or -1 to use default */
|
||||
RangeVar *relation; /* single table to process, or NULL */
|
||||
List *va_cols; /* list of column names, or NIL for all */
|
||||
} VacuumStmt;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/postmaster/autovacuum.h,v 1.4 2006/03/05 15:58:58 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/postmaster/autovacuum.h,v 1.5 2006/11/05 22:42:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -21,6 +21,7 @@ extern int autovacuum_vac_thresh;
|
|||
extern double autovacuum_vac_scale;
|
||||
extern int autovacuum_anl_thresh;
|
||||
extern double autovacuum_anl_scale;
|
||||
extern int autovacuum_freeze_max_age;
|
||||
extern int autovacuum_vac_cost_delay;
|
||||
extern int autovacuum_vac_cost_limit;
|
||||
|
||||
|
|
Loading…
Reference in New Issue