agora inbox for [email protected]
help / color / mirror / Atom feed[Proposal]: Extends VisualStudio to automatically precompile EmbeddedSQL
3+ messages / 3 participants
[nested] [flat]
* [Proposal]: Extends VisualStudio to automatically precompile EmbeddedSQL
@ 2017-05-02 09:23 Francis ANDRE <[email protected]>
0 siblings, 0 replies; 3+ messages in thread
From: Francis ANDRE @ 2017-05-02 09:23 UTC (permalink / raw)
To: pgsql-hackers
Hi
I made an extension of VisualStudio that precompiles automaticaly C or
C++ source with PostgreSQL Embedded SQL. The extension is made of the 3
files joined and I have no idea where they should be placed in the
PostgreSQL source tree.
Anybody interested in pushing this extension?
FA
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003";
<ItemGroup>
<PropertyPageSchema
Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />
<AvailableItemName
Include="Ecpg">
<Targets>EcpgTarget</Targets>
</AvailableItemName>
</ItemGroup>
<UsingTask
TaskName="Ecpg"
TaskFactory="XamlTaskFactory"
AssemblyName="Microsoft.Build.Tasks.v4.0">
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>
</UsingTask>
<Target
Name="EcpgTarget"
BeforeTargets="$(EcpgBeforeTargets)"
AfterTargets="$(EcpgAfterTargets)"
Condition="'@(Ecpg)' != ''"
DependsOnTargets="$(EcpgDependsOn);ComputeEcpgOutput"
Outputs="%(Ecpg.Outputs)"
Inputs="%(Ecpg.Identity);%(Ecpg.AdditionalDependencies);$(MSBuildProjectFile)">
<ItemGroup
Condition="'@(SelectedFiles)' != ''">
<Ecpg
Remove="@(Ecpg)"
Condition="'%(Identity)' != '@(SelectedFiles)'" />
</ItemGroup>
<ItemGroup>
<Ecpg_tlog
Include="%(Ecpg.Outputs)"
Condition="'%(Ecpg.Outputs)' != '' and '%(Ecpg.ExcludedFromBuild)' != 'true'">
<Source>@(Ecpg, '|')</Source>
</Ecpg_tlog>
</ItemGroup>
<Message
Importance="High"
Text="%(Ecpg.ExecutionDescription)" />
<WriteLinesToFile
Condition="'@(Ecpg_tlog)' != '' and '%(Ecpg_tlog.ExcludedFromBuild)' != 'true'"
File="$(IntDir)$(ProjectName).write.1.tlog"
Lines="^%(Ecpg_tlog.Source);@(Ecpg_tlog->'%(Fullpath)')" />
<Ecpg
Condition="'@(Ecpg)' != '' and '%(Ecpg.ExcludedFromBuild)' != 'true'"
CommandLineTemplate="%(Ecpg.CommandLineTemplate)"
OutputFile="%(Ecpg.OutputFile)"
Define="%(Ecpg.Define)"
Verbose="%(Ecpg.Verbose)"
Include="%(Ecpg.Include)"
AutoCommit="%(Ecpg.AutoCommit)"
ParseSystemInclude="%(Ecpg.ParseSystemInclude)"
AutoCode="%(Ecpg.AutoCode)"
RunTime="%(Ecpg.RunTime)"
CompatibilityMode="%(Ecpg.CompatibilityMode)"
AdditionalOptions="%(Ecpg.AdditionalOptions)"
Inputs="%(Ecpg.Identity)" />
</Target>
<PropertyGroup>
<ComputeLinkInputsTargets>
$(ComputeLinkInputsTargets);
ComputeEcpgOutput;
</ComputeLinkInputsTargets>
<ComputeLibInputsTargets>
$(ComputeLibInputsTargets);
ComputeEcpgOutput;
</ComputeLibInputsTargets>
</PropertyGroup>
<Target
Name="ComputeEcpgOutput"
Condition="'@(Ecpg)' != ''">
<ItemGroup>
<EcpgDirsToMake
Condition="'@(Ecpg)' != '' and '%(Ecpg.ExcludedFromBuild)' != 'true'"
Include="%(Ecpg.Outputs)" />
<Link
Include="%(EcpgDirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
<Lib
Include="%(EcpgDirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
<ImpLib
Include="%(EcpgDirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
</ItemGroup>
<MakeDir
Directories="@(EcpgDirsToMake->'%(RootDir)%(Directory)')" />
</Target>
</Project>
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[application/xml] ecpg.props (965B, ../../[email protected]/2-ecpg.props)
download
[text/plain] ecpg.targets (3.4K, ../../[email protected]/3-ecpg.targets)
download | inline:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PropertyPageSchema
Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />
<AvailableItemName
Include="Ecpg">
<Targets>EcpgTarget</Targets>
</AvailableItemName>
</ItemGroup>
<UsingTask
TaskName="Ecpg"
TaskFactory="XamlTaskFactory"
AssemblyName="Microsoft.Build.Tasks.v4.0">
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>
</UsingTask>
<Target
Name="EcpgTarget"
BeforeTargets="$(EcpgBeforeTargets)"
AfterTargets="$(EcpgAfterTargets)"
Condition="'@(Ecpg)' != ''"
DependsOnTargets="$(EcpgDependsOn);ComputeEcpgOutput"
Outputs="%(Ecpg.Outputs)"
Inputs="%(Ecpg.Identity);%(Ecpg.AdditionalDependencies);$(MSBuildProjectFile)">
<ItemGroup
Condition="'@(SelectedFiles)' != ''">
<Ecpg
Remove="@(Ecpg)"
Condition="'%(Identity)' != '@(SelectedFiles)'" />
</ItemGroup>
<ItemGroup>
<Ecpg_tlog
Include="%(Ecpg.Outputs)"
Condition="'%(Ecpg.Outputs)' != '' and '%(Ecpg.ExcludedFromBuild)' != 'true'">
<Source>@(Ecpg, '|')</Source>
</Ecpg_tlog>
</ItemGroup>
<Message
Importance="High"
Text="%(Ecpg.ExecutionDescription)" />
<WriteLinesToFile
Condition="'@(Ecpg_tlog)' != '' and '%(Ecpg_tlog.ExcludedFromBuild)' != 'true'"
File="$(IntDir)$(ProjectName).write.1.tlog"
Lines="^%(Ecpg_tlog.Source);@(Ecpg_tlog->'%(Fullpath)')" />
<Ecpg
Condition="'@(Ecpg)' != '' and '%(Ecpg.ExcludedFromBuild)' != 'true'"
CommandLineTemplate="%(Ecpg.CommandLineTemplate)"
OutputFile="%(Ecpg.OutputFile)"
Define="%(Ecpg.Define)"
Verbose="%(Ecpg.Verbose)"
Include="%(Ecpg.Include)"
AutoCommit="%(Ecpg.AutoCommit)"
ParseSystemInclude="%(Ecpg.ParseSystemInclude)"
AutoCode="%(Ecpg.AutoCode)"
RunTime="%(Ecpg.RunTime)"
CompatibilityMode="%(Ecpg.CompatibilityMode)"
AdditionalOptions="%(Ecpg.AdditionalOptions)"
Inputs="%(Ecpg.Identity)" />
</Target>
<PropertyGroup>
<ComputeLinkInputsTargets>
$(ComputeLinkInputsTargets);
ComputeEcpgOutput;
</ComputeLinkInputsTargets>
<ComputeLibInputsTargets>
$(ComputeLibInputsTargets);
ComputeEcpgOutput;
</ComputeLibInputsTargets>
</PropertyGroup>
<Target
Name="ComputeEcpgOutput"
Condition="'@(Ecpg)' != ''">
<ItemGroup>
<EcpgDirsToMake
Condition="'@(Ecpg)' != '' and '%(Ecpg.ExcludedFromBuild)' != 'true'"
Include="%(Ecpg.Outputs)" />
<Link
Include="%(EcpgDirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
<Lib
Include="%(EcpgDirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
<ImpLib
Include="%(EcpgDirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
</ItemGroup>
<MakeDir
Directories="@(EcpgDirsToMake->'%(RootDir)%(Directory)')" />
</Target>
</Project>
[text/xml] ecpg.xml (8.2K, ../../[email protected]/4-ecpg.xml)
download | inline:
<?xml version="1.0" encoding="utf-8"?>
<ProjectSchemaDefinitions xmlns="clr-namespace:Microsoft.Build.Framework.XamlTypes;assembly=Microsoft.Build.Framework" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:transformCallback="Microsoft.Cpp.Dev10.ConvertPropertyCallback">
<Rule
Name="Ecpg"
PageTemplate="tool"
DisplayName="Ecpg"
SwitchPrefix="-"
Order="200">
<Rule.DataSource>
<DataSource
Persistence="ProjectFile"
ItemType="Ecpg" />
</Rule.DataSource>
<Rule.Categories>
<Category
Name="General">
<Category.DisplayName>
<sys:String>General</sys:String>
</Category.DisplayName>
</Category>
<Category
Name="Ecpg Options">
<Category.DisplayName>
<sys:String>Ecpg Options</sys:String>
</Category.DisplayName>
</Category>
<Category
Name="Command Line"
Subtype="CommandLine">
<Category.DisplayName>
<sys:String>Command Line</sys:String>
</Category.DisplayName>
</Category>
</Rule.Categories>
<StringListProperty
Name="OutputFile"
Category="Ecpg Options"
IsRequired="true"
HelpUrl="https://www.postgresql.org/docs/9.5/static/app-ecpg.html"
DisplayName="Output File Name"
Description="Specifies that ecpg should write all its output to the given filename"
Switch="o [value]"
/>
<StringListProperty
Name="Define"
Category="Ecpg Options"
DisplayName="Define"
IsRequired="false"
HelpUrl="https://www.postgresql.org/docs/9.5/static/app-ecpg.html"
Description="Define a C preprocessor symbol."
Switch="D [value]"
/>
<StringListProperty
Name="Include"
Category="Ecpg Options"
DisplayName="Include"
IsRequired="false"
HelpUrl="https://www.postgresql.org/docs/9.5/static/app-ecpg.html"
Description="Specify an additional include path, used to find files included via EXEC SQL INCLUDE. Defaults are . (current directory), /usr/local/include, the PostgreSQL include directory which is defined at compile time (default: /usr/local/pgsql/include), and /usr/include, in that order."
Switch="D [value]"
/>
<BoolProperty
Name="Verbose"
Category="Ecpg Options"
DisplayName="Verbose"
Description="Print additional information including the version and the 'include' path."
HelpUrl="https://www.gnu.org/software/ecpg/manual/html_node/Understanding.html#Understanding"
Switch="v" />
<BoolProperty
Name="AutoCommit"
Category="Ecpg Options"
DisplayName="AutoCommit"
Description="Turn on autocommit of transactions. In this mode, each SQL command is automatically committed unless it is inside an explicit transaction block. In the default mode, commands are committed only when EXEC SQL COMMIT is issued."
HelpUrl="https://www.postgresql.org/docs/9.5/static/app-ecpg.html"
Switch="t" />
<BoolProperty
Name="AutoCode"
Category="Ecpg Options"
DisplayName="AutoCode"
Description="Automatically generate certain C code from SQL code. Currently, this works for EXEC SQL TYPE."
HelpUrl="https://www.postgresql.org/docs/9.5/static/app-ecpg.html"
Switch="c" />
<BoolProperty
Name="ParseSystemInclude"
Category="Ecpg Options"
DisplayName="ParseSystemInclude"
Description="Parse system include files as well."
HelpUrl="https://www.postgresql.org/docs/9.5/static/app-ecpg.html"
Switch="i" />
<EnumProperty
Name="RunTime"
Category="Ecpg Options"
HelpUrl="https://www.postgresql.org/docs/9.5/static/app-ecpg.html"
DisplayName="RunTime"
Description="Selects run-time behavior. Option can be one of the following:
no_indicator: Do not use indicators but instead use special values to represent null values. Historically there have been databases using this approach.
prepare: Prepare all statements before using them. Libecpg will keep a cache of prepared statements and reuse a statement if it gets executed again. If the cache runs full, libecpg will free the least used statement.
questionmarks: Allow question mark as placeholder for compatibility reasons. This used to be the default long ago.">
<EnumValue
Name="no_indicator"
DisplayName="no_indicator"
Switch="r no_indicator"/>
<EnumValue
Name="prepare"
DisplayName="prepare"
Switch="r prepare"/>
<EnumValue
Name="questionmarks"
DisplayName="questionmarks"
Switch="r questionmarks"/>
</EnumProperty>
<EnumProperty
Name="CompatibilityMode"
Category="Ecpg Options"
HelpUrl="https://www.postgresql.org/docs/9.5/static/app-ecpg.html"
DisplayName="CompatibilityMode"
Description="Set a compatibility mode. mode can be INFORMIX or INFORMIX_SE.">
<EnumValue
Name="INFORMIX "
DisplayName="INFORMIX "
Switch="C INFORMIX "/>
<EnumValue
Name="INFORMIX_SE"
DisplayName="INFORMIX_SE"
Switch="C INFORMIX_SE"/>
</EnumProperty>
<StringListProperty
Name="Inputs"
Category="Command Line"
IsRequired="true"
Switch=" ">
<StringListProperty.DataSource>
<DataSource
Persistence="ProjectFile"
ItemType="Ecpg"
SourceType="Item" />
</StringListProperty.DataSource>
</StringListProperty>
<StringProperty
Name="CommandLineTemplate"
DisplayName="Command Line"
Visible="False"
IncludeInCommandLine="False" />
<DynamicEnumProperty
Name="EcpgBeforeTargets"
Category="General"
EnumProvider="Targets"
IncludeInCommandLine="False">
<DynamicEnumProperty.DisplayName>
<sys:String>Execute Before</sys:String>
</DynamicEnumProperty.DisplayName>
<DynamicEnumProperty.Description>
<sys:String>Specifies the targets for the build customization to run before.</sys:String>
</DynamicEnumProperty.Description>
<DynamicEnumProperty.ProviderSettings>
<NameValuePair
Name="Exclude"
Value="^EcpgBeforeTargets|^Compute" />
</DynamicEnumProperty.ProviderSettings>
<DynamicEnumProperty.DataSource>
<DataSource
Persistence="ProjectFile"
HasConfigurationCondition="true" />
</DynamicEnumProperty.DataSource>
</DynamicEnumProperty>
<DynamicEnumProperty
Name="EcpgAfterTargets"
Category="General"
EnumProvider="Targets"
IncludeInCommandLine="False">
<DynamicEnumProperty.DisplayName>
<sys:String>Execute After</sys:String>
</DynamicEnumProperty.DisplayName>
<DynamicEnumProperty.Description>
<sys:String>Specifies the targets for the build customization to run after.</sys:String>
</DynamicEnumProperty.Description>
<DynamicEnumProperty.ProviderSettings>
<NameValuePair
Name="Exclude"
Value="^EcpgAfterTargets|^Compute" />
</DynamicEnumProperty.ProviderSettings>
<DynamicEnumProperty.DataSource>
<DataSource
Persistence="ProjectFile"
ItemType=""
HasConfigurationCondition="true" />
</DynamicEnumProperty.DataSource>
</DynamicEnumProperty>
<StringListProperty
Name="Outputs"
DisplayName="Outputs"
Visible="False"
IncludeInCommandLine="False" />
<StringProperty
Name="ExecutionDescription"
DisplayName="Execution Description"
Visible="False"
IncludeInCommandLine="False" />
<StringListProperty
Name="AdditionalDependencies"
DisplayName="Additional Dependencies"
IncludeInCommandLine="False"
Visible="false" />
<StringProperty
Subtype="AdditionalOptions"
Name="AdditionalOptions"
Category="Command Line">
<StringProperty.DisplayName>
<sys:String>Additional Options</sys:String>
</StringProperty.DisplayName>
<StringProperty.Description>
<sys:String>Additional Options</sys:String>
</StringProperty.Description>
</StringProperty>
</Rule>
<ItemType
Name="Ecpg"
DisplayName="Ecpg" />
<FileExtension
Name="*.pgc"
ContentType="Ecpg" />
<ContentType
Name="Ecpg"
DisplayName="Ecpg"
ItemType="Ecpg" />
</ProjectSchemaDefinitions>
^ permalink raw reply [nested|flat] 3+ messages in thread
* [PATCH v3] Allow to specify CRL directory
@ 2020-07-21 14:01 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 3+ messages in thread
From: Kyotaro Horiguchi @ 2020-07-21 14:01 UTC (permalink / raw)
We have the ssl_crl_file GUC variable and the sslcrl connection option
to specify a CRL file. X509_STORE_load_locations accepts a directory,
which leads to on-demand loading method with which method only
relevant CRLs are loaded. Allow server and client to use the hashed
directory method. We could use the existing variable and option to
specify the direcotry name but allowing to use both methods at the
same time gives operation flexibility to users.
---
doc/src/sgml/config.sgml | 21 ++++++++-
doc/src/sgml/libpq.sgml | 20 +++++++-
doc/src/sgml/runtime.sgml | 33 +++++++++++++
src/backend/libpq/be-secure-openssl.c | 27 +++++++++--
src/backend/libpq/be-secure.c | 1 +
src/backend/utils/misc/guc.c | 10 ++++
src/include/libpq/libpq.h | 1 +
src/interfaces/libpq/fe-connect.c | 6 +++
src/interfaces/libpq/fe-secure-openssl.c | 24 +++++++---
src/interfaces/libpq/libpq-int.h | 1 +
src/test/ssl/Makefile | 20 +++++++-
src/test/ssl/ssl/both-cas-1.crt | 46 +++++++++----------
src/test/ssl/ssl/both-cas-2.crt | 46 +++++++++----------
src/test/ssl/ssl/client+client_ca.crt | 28 +++++------
src/test/ssl/ssl/client-crldir/9bb9e3c3.r0 | 11 +++++
src/test/ssl/ssl/client-revoked.crt | 14 +++---
src/test/ssl/ssl/client.crl | 16 +++----
src/test/ssl/ssl/client.crt | 14 +++---
src/test/ssl/ssl/client_ca.crt | 14 +++---
.../ssl/ssl/root+client-crldir/9bb9e3c3.r0 | 11 +++++
.../ssl/ssl/root+client-crldir/a3d11bff.r0 | 11 +++++
src/test/ssl/ssl/root+client.crl | 32 ++++++-------
src/test/ssl/ssl/root+client_ca.crt | 32 ++++++-------
.../ssl/ssl/root+server-crldir/a3d11bff.r0 | 11 +++++
.../ssl/ssl/root+server-crldir/a836cc2d.r0 | 11 +++++
src/test/ssl/ssl/root+server.crl | 32 ++++++-------
src/test/ssl/ssl/root+server_ca.crt | 32 ++++++-------
src/test/ssl/ssl/root.crl | 16 +++----
src/test/ssl/ssl/root_ca.crt | 18 ++++----
src/test/ssl/ssl/server-cn-and-alt-names.crt | 14 +++---
src/test/ssl/ssl/server-cn-only.crt | 14 +++---
src/test/ssl/ssl/server-crldir/a836cc2d.r0 | 11 +++++
.../ssl/ssl/server-multiple-alt-names.crt | 14 +++---
src/test/ssl/ssl/server-no-names.crt | 16 +++----
src/test/ssl/ssl/server-revoked.crt | 14 +++---
src/test/ssl/ssl/server-single-alt-name.crt | 16 +++----
src/test/ssl/ssl/server-ss.crt | 18 ++++----
src/test/ssl/ssl/server.crl | 16 +++----
src/test/ssl/ssl/server_ca.crt | 14 +++---
src/test/ssl/t/001_ssltests.pl | 31 ++++++++++++-
src/test/ssl/t/SSLServer.pm | 14 +++++-
41 files changed, 496 insertions(+), 255 deletions(-)
create mode 100644 src/test/ssl/ssl/client-crldir/9bb9e3c3.r0
create mode 100644 src/test/ssl/ssl/root+client-crldir/9bb9e3c3.r0
create mode 100644 src/test/ssl/ssl/root+client-crldir/a3d11bff.r0
create mode 100644 src/test/ssl/ssl/root+server-crldir/a3d11bff.r0
create mode 100644 src/test/ssl/ssl/root+server-crldir/a836cc2d.r0
create mode 100644 src/test/ssl/ssl/server-crldir/a836cc2d.r0
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 82864bbb24..85d4402745 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1214,7 +1214,26 @@ include_dir 'conf.d'
Relative paths are relative to the data directory.
This parameter can only be set in the <filename>postgresql.conf</filename>
file or on the server command line.
- The default is empty, meaning no CRL file is loaded.
+ The default is empty, meaning no CRL file is loaded unless
+ <xref linkend="guc-ssl-crl-dir"/> is set.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="guc-ssl-crl-dir" xreflabel="ssl_crl_dir">
+ <term><varname>ssl_crl_dir</varname> (<type>string</type>)
+ <indexterm>
+ <primary><varname>ssl_crl_dir</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Specifies the name of the directory containing the SSL server
+ certificate revocation list (CRL). Relative paths are relative to the
+ data directory. This parameter can only be set in
+ the <filename>postgresql.conf</filename> file or on the server command
+ line. The default is empty, meaning no CRL file is loaded unless
+ <xref linkend="guc-ssl-crl-file"/> is set.
</para>
</listitem>
</varlistentry>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 2bb3bf77e4..e9bc622fca 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1723,8 +1723,24 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
This parameter specifies the file name of the SSL certificate
revocation list (CRL). Certificates listed in this file, if it
exists, will be rejected while attempting to authenticate the
- server's certificate. The default is
- <filename>~/.postgresql/root.crl</filename>.
+ server's certificate. If both <xref linkend='libpq-connect-sslcrl'/>
+ and <xref linkend='libpq-connect-sslcrldir'/> are not set, this
+ setting is assumed to be
+ <filename>~/.postgresql/root.crl</filename>. See
+ <xref linkend="ssl-crl-files"/> for details.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-connect-sslcrldir" xreflabel="sslcrldir">
+ <term><literal>sslcrldir</literal></term>
+ <listitem>
+ <para>
+ This parameter specifies the directory name of the SSL certificate
+ revocation list (CRL). Certificates listed in the files in this
+ directory, if it exists, will be rejected while attempting to
+ authenticate the server's certificate. See
+ <xref linkend="ssl-crl-files"/> for details.
</para>
</listitem>
</varlistentry>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 283352d3a4..45fc5d6678 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2550,6 +2550,39 @@ openssl x509 -req -in server.csr -text -days 365 \
</para>
</sect2>
+ <sect2 id="ssl-crl-files">
+ <title>Certification Revocation List files</title>
+
+ <para> The server setting <xref linkend="guc-ssl-crl-file"/> and
+ <xref linkend="guc-ssl-crl-dir"/>, and the connection option
+ <xref linkend="libpq-connect-sslcrl"/> and
+ <xref linkend="libpq-connect-sslcrldir"/> specify a file containing one or
+ more CRL, or a directory containing a separate file for every CRL
+ respectively. Settings for CRL file and CRL directory can be specified
+ together. In the first method, file method, the all CRLs in the file is
+ loaded at server start time or by reloading config file (<command>pg_ctl
+ reload</command>). In the second method, hashed directory method, CRL
+ files are loaded on-demand, that is, only the relevant CRL files are
+ loaded at connection time.
+ </para>
+ <para>
+ The CRL file used for the file method can contain multiple CRLs, like
+ certificates, by just concatenated if it is in PEM format. In the hashed
+ directory method, every file in the directory has the name
+ with <parameter>hash</parameter>.r<parameter>N</parameter> format,
+ where <parameter>hash</parameter> is the hash value of the issuer of the
+ CRL and <parameter>N</parameter> is a sequence number that starts at
+ zero. The hash value is calculated using openssl command. In both cases
+ the CRLs from all CAs involved in a certificate chain are needed to verify
+ a certificate, even if some or all of them are empty.
+<programlisting>
+$ openssl crl -hash -noout -in foo.crl
+98668507
+$ cp foo.crl $PGDATA/crldir/98668507.r0
+</programlisting>
+ </para>
+ </sect2>
+
</sect1>
<sect1 id="gssapi-enc">
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 0494ad7ded..90436bd847 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -285,26 +285,47 @@ be_tls_init(bool isServerStart)
* http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
*----------
*/
- if (ssl_crl_file[0])
+ if (ssl_crl_file[0] || ssl_crl_dir[0])
{
X509_STORE *cvstore = SSL_CTX_get_cert_store(context);
if (cvstore)
{
/* Set the flags to check against the complete CRL chain */
- if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
+ if (X509_STORE_load_locations(cvstore,
+ ssl_crl_file[0] ? ssl_crl_file : NULL,
+ ssl_crl_dir[0] ? ssl_crl_dir : NULL)
+ == 1)
{
X509_STORE_set_flags(cvstore,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
}
- else
+ else if (ssl_crl_dir[0] == 0)
{
+
ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("could not load SSL certificate revocation list file \"%s\": %s",
ssl_crl_file, SSLerrmessage(ERR_get_error()))));
goto error;
}
+ else if (ssl_crl_file[0] == 0)
+ {
+ ereport(isServerStart ? FATAL : LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load SSL certificate revocation list directory \"%s\": %s",
+ ssl_crl_dir, SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
+ else
+ {
+ ereport(isServerStart ? FATAL : LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load SSL certificate revocation list file \"%s\" and/or directory \"%s\": %s",
+ ssl_crl_file, ssl_crl_dir,
+ SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
}
}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 4cf139a223..3ad6890f70 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -42,6 +42,7 @@ char *ssl_cert_file;
char *ssl_key_file;
char *ssl_ca_file;
char *ssl_crl_file;
+char *ssl_crl_dir;
char *ssl_dh_params_file;
char *ssl_passphrase_command;
bool ssl_passphrase_command_supports_reload;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..df19c5318f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -4355,6 +4355,16 @@ static struct config_string ConfigureNamesString[] =
NULL, NULL, NULL
},
+ {
+ {"ssl_crl_dir", PGC_SIGHUP, CONN_AUTH_SSL,
+ gettext_noop("Location of the SSL certificate revocation list directory."),
+ NULL
+ },
+ &ssl_crl_dir,
+ "",
+ NULL, NULL, NULL
+ },
+
{
{"stats_temp_directory", PGC_SIGHUP, STATS_COLLECTOR,
gettext_noop("Writes temporary statistics files to the specified directory."),
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index a55898c85a..b41b10620a 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -82,6 +82,7 @@ extern char *ssl_cert_file;
extern char *ssl_key_file;
extern char *ssl_ca_file;
extern char *ssl_crl_file;
+extern char *ssl_crl_dir;
extern char *ssl_dh_params_file;
extern PGDLLIMPORT char *ssl_passphrase_command;
extern PGDLLIMPORT bool ssl_passphrase_command_supports_reload;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 2b78ed8ec3..cc9d801818 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -317,6 +317,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"SSL-Revocation-List", "", 64,
offsetof(struct pg_conn, sslcrl)},
+ {"sslcrldir", "PGSSLCRLDIR", NULL, NULL,
+ "SSL-Revocation-List-Dir", "", 64,
+ offsetof(struct pg_conn, sslcrldir)},
+
{"requirepeer", "PGREQUIREPEER", NULL, NULL,
"Require-Peer", "", 10,
offsetof(struct pg_conn, requirepeer)},
@@ -3997,6 +4001,8 @@ freePGconn(PGconn *conn)
free(conn->sslrootcert);
if (conn->sslcrl)
free(conn->sslcrl);
+ if (conn->sslcrldir)
+ free(conn->sslcrldir);
if (conn->sslcompression)
free(conn->sslcompression);
if (conn->requirepeer)
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 075f754e1f..e2d047ad70 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -794,7 +794,8 @@ initialize_SSL(PGconn *conn)
if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
!(conn->sslkey && strlen(conn->sslkey) > 0) ||
!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
- !(conn->sslcrl && strlen(conn->sslcrl) > 0))
+ !((conn->sslcrl && strlen(conn->sslcrl) > 0) ||
+ (conn->sslcrldir && strlen(conn->sslcrldir) > 0)))
have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
else /* won't need it */
have_homedir = false;
@@ -936,20 +937,29 @@ initialize_SSL(PGconn *conn)
if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
{
+ char *fname = NULL;
+ char *dname = NULL;
+
if (conn->sslcrl && strlen(conn->sslcrl) > 0)
- strlcpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
- else if (have_homedir)
+ fname = conn->sslcrl;
+ if (conn->sslcrldir && strlen(conn->sslcrldir) > 0)
+ dname = conn->sslcrldir;
+
+ /* defaults to use the default CRL file */
+ if (!fname && !dname && have_homedir)
+ {
snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
- else
- fnbuf[0] = '\0';
+ fname = fnbuf;
+ }
/* Set the flags to check against the complete CRL chain */
- if (fnbuf[0] != '\0' &&
- X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1)
+ if ((fname || dname) &&
+ X509_STORE_load_locations(cvstore, fname, dname) == 1)
{
X509_STORE_set_flags(cvstore,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
}
+
/* if not found, silently ignore; we do not require CRL */
ERR_clear_error();
}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4db498369c..ce36aabd25 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -362,6 +362,7 @@ struct pg_conn
char *sslpassword; /* client key file password */
char *sslrootcert; /* root certificate filename */
char *sslcrl; /* certificate revocation list filename */
+ char *sslcrldir; /* certificate revocation list directory name */
char *requirepeer; /* required peer credentials for local sockets */
char *gssencmode; /* GSS mode (require,prefer,disable) */
char *krbsrvname; /* Kerberos service name */
diff --git a/src/test/ssl/Makefile b/src/test/ssl/Makefile
index 93335b1ea2..59ebe4c364 100644
--- a/src/test/ssl/Makefile
+++ b/src/test/ssl/Makefile
@@ -30,12 +30,15 @@ SSLFILES := $(CERTIFICATES:%=ssl/%.key) $(CERTIFICATES:%=ssl/%.crt) \
ssl/client+client_ca.crt ssl/client-der.key \
ssl/client-encrypted-pem.key ssl/client-encrypted-der.key
+SSLDIRS := ssl/client-crldir ssl/server-crldir \
+ ssl/root+client-crldir ssl/root+server-crldir
+
# This target re-generates all the key and certificate files. Usually we just
# use the ones that are committed to the tree without rebuilding them.
#
# This target will fail unless preceded by sslfiles-clean.
#
-sslfiles: $(SSLFILES)
+sslfiles: $(SSLFILES) $(SSLDIRS)
# OpenSSL requires a directory to put all generated certificates in. We don't
# use this for anything, but we need a location.
@@ -146,10 +149,25 @@ ssl/root+server.crl: ssl/root.crl ssl/server.crl
cat $^ > $@
ssl/root+client.crl: ssl/root.crl ssl/client.crl
cat $^ > $@
+ssl/root+server-crldir: ssl/server.crl
+ mkdir ssl/root+server-crldir
+ cp ssl/server.crl ssl/root+server-crldir/`openssl crl -hash -noout -in ssl/server.crl`.r0
+ cp ssl/root.crl ssl/root+server-crldir/`openssl crl -hash -noout -in ssl/root.crl`.r0
+ssl/root+client-crldir: ssl/client.crl
+ mkdir ssl/root+client-crldir
+ cp ssl/client.crl ssl/root+client-crldir/`openssl crl -hash -noout -in ssl/client.crl`.r0
+ cp ssl/root.crl ssl/root+client-crldir/`openssl crl -hash -noout -in ssl/root.crl`.r0
+ssl/server-crldir: ssl/server.crl
+ mkdir ssl/server-crldir
+ cp ssl/server.crl ssl/server-crldir/`openssl crl -hash -noout -in ssl/server.crl`.r0
+ssl/client-crldir: ssl/client.crl
+ mkdir ssl/client-crldir
+ cp ssl/client.crl ssl/client-crldir/`openssl crl -hash -noout -in ssl/client.crl`.r0
.PHONY: sslfiles-clean
sslfiles-clean:
rm -f $(SSLFILES) ssl/client_ca.srl ssl/server_ca.srl ssl/client_ca-certindex* ssl/server_ca-certindex* ssl/root_ca-certindex* ssl/root_ca.srl ssl/temp_ca.crt ssl/temp_ca_signed.crt
+ rm -rf $(SSLDIRS)
clean distclean maintainer-clean:
rm -rf tmp_check
diff --git a/src/test/ssl/ssl/both-cas-1.crt b/src/test/ssl/ssl/both-cas-1.crt
index 37ffa10174..1ab329c8ab 100644
--- a/src/test/ssl/ssl/both-cas-1.crt
+++ b/src/test/ssl/ssl/both-cas-1.crt
@@ -1,8 +1,8 @@
-----BEGIN CERTIFICATE-----
-MIIDHjCCAgagAwIBAgIUEAgXJ/ibw6TVTUoomlBsPMfVTlMwDQYJKoZIhvcNAQEL
+MIIDHjCCAgagAwIBAgIUbDho+LuL8GrZ/iVFY16vdG6kNo0wDQYJKoZIhvcNAQEL
BQAwQDE+MDwGA1UEAww1VGVzdCByb290IENBIGZvciBQb3N0Z3JlU1FMIFNTTCBy
-ZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcNMTgxMTI3MTM0MDU0WhcNNDYwNDE0MTM0
-MDU0WjBAMT4wPAYDVQQDDDVUZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NM
+ZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcNMjAwODMxMDYwNzIyWhcNNDgwMTE3MDYw
+NzIyWjBAMT4wPAYDVQQDDDVUZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NM
IHJlZ3Jlc3Npb24gdGVzdCBzdWl0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALZ81vKKBJlxgjwuNoK67I4IE9zfSLb0eHbgZwZxDVzdmFejARrHlWk3
+MK7Nav7RLSJ990am33zb58CTHc7YYVlBp07+PwLXzypqWkhYfok1OYYjyjCrFDs
@@ -10,17 +10,17 @@ sjcJI3hRCZNEz+wYsG+tdYWJ+gRPQOWfh0YfO2rFgXAIMLiF6lyWzf1eOM+OjYrF
/eyzwbMaJkkGa/AyZKz3wZiPq0jTuYLVmH4MK7MBOsUfSmsBsn/ohyRCQzM+ol0v
Qlsrulj8usponRPDh9ng4PB5OSgR79YimQZnASQzJxiUvMADrKL5L6KwLxJlzbqY
R0b5mLh8KBzBQmSh3Aj2e2I7Z17hdaMCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zAN
-BgkqhkiG9w0BAQsFAAOCAQEASzA7ApbuKn8lkC706gRL37a33yTZZ8rjZ4dnvCtq
-6OltlYDJ8IndotKbLH0SUEAxrvcaFnMt7AX9pRf2sGBKbY8zcxqPfsvzVehgx4Ea
-1RYksFW4h97jj1a1RKukTKuEOEEipbxwo0rLxfjvdaf2izqchJsLGtbocIZf0bD8
-djbE9jOLkx7saL08qC8ECrf9utsee+LJCsUYbNgYyIItEy6yVnmF/ICz93Utn1cI
-RfqZr1lM2Ia2LP9eDTmiuR9m+/MzkeRvvJHonNrRJHlcggtYHICvYioh9/jALBcm
-wZ+hTUePVqy4hOCBJ975CXjfKFN4MKQAdeB3EO5eBYAD3Q==
+BgkqhkiG9w0BAQsFAAOCAQEAo5siIstgYy1MjyVaSHpxy6Cq7JT73RhYYudtAy8e
+XwVXqy3xuZtl+i2XaWnsojyHRZ83t+1rVCoesbZefyfz9d6qZJkCkWdKdjiVEu+s
+CJSo+tGyCZQ1ZTb+07WY01P97gfswP+VHN4EjxJmUnLdqJEJsdOckvL1mBfortt6
+CvL7adPwftppNivF64R0QZfOsLIaaOa0oi33DQDDNUdrx5f3xWwYmzw+f95lvbYR
+DXCytT647nc44Gu09erE4sWipIknFBvg3P1D0icFSIoEh44mU9o1lHUFsogXa09g
+8Vkho/rudYrHi/cwwzQlUjZyHPg6k1iU9oZGEaW5gVKxSw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVUZXN0
IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzdWl0
-ZTAeFw0xODExMjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMEIxQDA+BgNVBAMMN1Rl
+ZTAeFw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEIxQDA+BgNVBAMMN1Rl
c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBjbGllbnQg
Y2VydHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC75x7yuQ1+gK8j
1aO6D2nGym2x2OFniztlnx4PlWWWivIrYlxy6xAhfrVdjwjc5mMtOCrVKllsdC+S
@@ -29,17 +29,17 @@ B2yipfW1PUxM9a7/p19CRBcDQ+LNW+YFqwARByHGq1wfatJzpM8TXe+XEnRfW9KX
P3a5PqR6evFQOzjcAf+QBJ0hAEddUDhdYECbs1GrApfsEHBuwXabdCH41j0F/0yc
kctydWfBl2Vbmd3sfsFMHjde+SJhqxyq6xiSL59jnx4ZKmtn9VSOYbGmBCdBdYK7
RTcnJQv9AgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
-AB6zh/Jw+t232100Tztr7wJoKH8DL1hnSm3e7omdj0WlKfuZwTqDuMzB3H4LOSnS
-A00XpyMAGAJC6yRjS8pt+pjY5Jt6ouSqf6wNq0mF0jiIDeg1k/GNEjigx+0ITqbE
-lUJ56AcpoBNhOwBjOCRFh4JuspHZqHXgUNYTEicClbV+lZwoMIIK1e6FYRZDqMtL
-+34GtZACImqvRkP5alqQg7hJUM1zbDVf2qebY4cfSu4OfTu6Og6KrL8Cu6bqR2et
-0a/TbthHYz1QGDYRoVTSP4uWoG9M1ZbsA/bNE2eqcrQj+dJ4AmIIr8Yl8mrwo/FZ
-SvgeLMlQY7UNwLUDtwy9QkI=
+AKsQncM/5JZez/dECPg7xp8T5Sj++mJSiFsFr2Isk3HjJBI+skvZfpWkJnkEUl4a
++BspqoeAD5K+Fad0/Xg/AyZztONpLU+haA4eQCpwYHVY/DSjasjEI5BbCV8mrrYP
+SLcKGIJrPW+beO8pbjdkJNxBiDTeYzMBTbfHvarBYLYAvur8ZvUgKExDWP5kGUe8
+zA9F4FJ/Nig03QzQgxaNjxXoy892fAs6KyWFoGZDqAXlXAFNuz83joTiGNzNTiW4
+qRKpBa6aitnPVeIs8m3z/SUcm5PdMWvXVlsrlA0Gx2EM/B02EWnPk/U+CHtNey7r
+h4LJ4NA9GngE2gewd/gNItc=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIBATANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVUZXN0
IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzdWl0
-ZTAeFw0xODExMjcxMzQwNTRaFw00NjA0MTQxMzQwNTRaMEIxQDA+BgNVBAMMN1Rl
+ZTAeFw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEIxQDA+BgNVBAMMN1Rl
c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzZXJ2ZXIg
Y2VydHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiSnYZbmc9vpCt
Ku1sKV9l663JCceubhMw8Gg16kV0hXEFf/TgGC4zkiYNHN7+G45YD7Nq0kBCq3dH
@@ -48,10 +48,10 @@ t2wPCc6c8pQoI64dfprVqPkvzoe1WBpZNetkUTk20v08jNeRa7XdRbRR6we1s9VG
QW9YWdH9N5ctaUXMG6lLV2OAjs+W1smpKfpIpMCA1lPGlElu70hynon/nQQvBP77
SfQpZVc0esM18jkZpr5LEKUCw+x6LaMsqmBHpAULfCffxn2r0uMBW4L4VaGg3W6F
h6iuJwRfAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
-AFlcKTaU/Ug3Q0hr3P1UQ6dWyK4aVn9rs4jvVfFl0a0RnbBowqK2C+zQVUWYTcjo
-KHREVje65goj6VzRB6ko/9mAQ6PZP8jRuRhfCmvmvSQ/mWdgPzSRsUh9MwgEm9c2
-vNbqwaznEU8cYZnLpHiR9O5S7/qWWxehjYtxk5Eb4J006YglYfHnhrRFJvPbiqlf
-IOEivZ7gIVfvaOTbLjmN2kLOnzdlwpXGjxxg4Nu9ZhXOhfrplzUvRfmqvVsDiHXb
-USIdX+OFZZqr64IKG4drT4K4Bt2wupOEyX4ZFsUXXd+Hgq83SWmV4wzflcpmGkLC
-JZ3CEMu8/WA5uQBXdQUozlE=
+AArYO305zkNZc+vdbeXNpgi1/FrOHl2b0DvG4i2gcUVDvvjIHUnVLf2cak+81vER
+CqlCkutXxB+/fyxVXYtLKXQh0D/68QikH+ImEavrUrANXvQRvoixIjrfub/nZB75
+EiOTIR2N0/m6ndjCHJU0W8N7Tt9qob31e3bVJBZOc+9e80y8GDQiMKYACmet9zUR
+hR/yvJhUsH/u5Y7OwrvcyCs+PJXzPnZTSsatSkHI4KY22+7K+ZhscLIaBKjqlIDj
++fHBrutW9jtog1E3JUO9ZHokB1qlRLs4YhpQ8YzHRabEBaX892ZPLNwtmFLOWK1p
+9klR7/RXnu13nStNIYAHk20=
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/both-cas-2.crt b/src/test/ssl/ssl/both-cas-2.crt
index 2f2723f2b1..6669f42c92 100644
--- a/src/test/ssl/ssl/both-cas-2.crt
+++ b/src/test/ssl/ssl/both-cas-2.crt
@@ -1,8 +1,8 @@
-----BEGIN CERTIFICATE-----
-MIIDHjCCAgagAwIBAgIUEAgXJ/ibw6TVTUoomlBsPMfVTlMwDQYJKoZIhvcNAQEL
+MIIDHjCCAgagAwIBAgIUbDho+LuL8GrZ/iVFY16vdG6kNo0wDQYJKoZIhvcNAQEL
BQAwQDE+MDwGA1UEAww1VGVzdCByb290IENBIGZvciBQb3N0Z3JlU1FMIFNTTCBy
-ZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcNMTgxMTI3MTM0MDU0WhcNNDYwNDE0MTM0
-MDU0WjBAMT4wPAYDVQQDDDVUZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NM
+ZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcNMjAwODMxMDYwNzIyWhcNNDgwMTE3MDYw
+NzIyWjBAMT4wPAYDVQQDDDVUZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NM
IHJlZ3Jlc3Npb24gdGVzdCBzdWl0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALZ81vKKBJlxgjwuNoK67I4IE9zfSLb0eHbgZwZxDVzdmFejARrHlWk3
+MK7Nav7RLSJ990am33zb58CTHc7YYVlBp07+PwLXzypqWkhYfok1OYYjyjCrFDs
@@ -10,17 +10,17 @@ sjcJI3hRCZNEz+wYsG+tdYWJ+gRPQOWfh0YfO2rFgXAIMLiF6lyWzf1eOM+OjYrF
/eyzwbMaJkkGa/AyZKz3wZiPq0jTuYLVmH4MK7MBOsUfSmsBsn/ohyRCQzM+ol0v
Qlsrulj8usponRPDh9ng4PB5OSgR79YimQZnASQzJxiUvMADrKL5L6KwLxJlzbqY
R0b5mLh8KBzBQmSh3Aj2e2I7Z17hdaMCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zAN
-BgkqhkiG9w0BAQsFAAOCAQEASzA7ApbuKn8lkC706gRL37a33yTZZ8rjZ4dnvCtq
-6OltlYDJ8IndotKbLH0SUEAxrvcaFnMt7AX9pRf2sGBKbY8zcxqPfsvzVehgx4Ea
-1RYksFW4h97jj1a1RKukTKuEOEEipbxwo0rLxfjvdaf2izqchJsLGtbocIZf0bD8
-djbE9jOLkx7saL08qC8ECrf9utsee+LJCsUYbNgYyIItEy6yVnmF/ICz93Utn1cI
-RfqZr1lM2Ia2LP9eDTmiuR9m+/MzkeRvvJHonNrRJHlcggtYHICvYioh9/jALBcm
-wZ+hTUePVqy4hOCBJ975CXjfKFN4MKQAdeB3EO5eBYAD3Q==
+BgkqhkiG9w0BAQsFAAOCAQEAo5siIstgYy1MjyVaSHpxy6Cq7JT73RhYYudtAy8e
+XwVXqy3xuZtl+i2XaWnsojyHRZ83t+1rVCoesbZefyfz9d6qZJkCkWdKdjiVEu+s
+CJSo+tGyCZQ1ZTb+07WY01P97gfswP+VHN4EjxJmUnLdqJEJsdOckvL1mBfortt6
+CvL7adPwftppNivF64R0QZfOsLIaaOa0oi33DQDDNUdrx5f3xWwYmzw+f95lvbYR
+DXCytT647nc44Gu09erE4sWipIknFBvg3P1D0icFSIoEh44mU9o1lHUFsogXa09g
+8Vkho/rudYrHi/cwwzQlUjZyHPg6k1iU9oZGEaW5gVKxSw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIBATANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVUZXN0
IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzdWl0
-ZTAeFw0xODExMjcxMzQwNTRaFw00NjA0MTQxMzQwNTRaMEIxQDA+BgNVBAMMN1Rl
+ZTAeFw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEIxQDA+BgNVBAMMN1Rl
c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzZXJ2ZXIg
Y2VydHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiSnYZbmc9vpCt
Ku1sKV9l663JCceubhMw8Gg16kV0hXEFf/TgGC4zkiYNHN7+G45YD7Nq0kBCq3dH
@@ -29,17 +29,17 @@ t2wPCc6c8pQoI64dfprVqPkvzoe1WBpZNetkUTk20v08jNeRa7XdRbRR6we1s9VG
QW9YWdH9N5ctaUXMG6lLV2OAjs+W1smpKfpIpMCA1lPGlElu70hynon/nQQvBP77
SfQpZVc0esM18jkZpr5LEKUCw+x6LaMsqmBHpAULfCffxn2r0uMBW4L4VaGg3W6F
h6iuJwRfAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
-AFlcKTaU/Ug3Q0hr3P1UQ6dWyK4aVn9rs4jvVfFl0a0RnbBowqK2C+zQVUWYTcjo
-KHREVje65goj6VzRB6ko/9mAQ6PZP8jRuRhfCmvmvSQ/mWdgPzSRsUh9MwgEm9c2
-vNbqwaznEU8cYZnLpHiR9O5S7/qWWxehjYtxk5Eb4J006YglYfHnhrRFJvPbiqlf
-IOEivZ7gIVfvaOTbLjmN2kLOnzdlwpXGjxxg4Nu9ZhXOhfrplzUvRfmqvVsDiHXb
-USIdX+OFZZqr64IKG4drT4K4Bt2wupOEyX4ZFsUXXd+Hgq83SWmV4wzflcpmGkLC
-JZ3CEMu8/WA5uQBXdQUozlE=
+AArYO305zkNZc+vdbeXNpgi1/FrOHl2b0DvG4i2gcUVDvvjIHUnVLf2cak+81vER
+CqlCkutXxB+/fyxVXYtLKXQh0D/68QikH+ImEavrUrANXvQRvoixIjrfub/nZB75
+EiOTIR2N0/m6ndjCHJU0W8N7Tt9qob31e3bVJBZOc+9e80y8GDQiMKYACmet9zUR
+hR/yvJhUsH/u5Y7OwrvcyCs+PJXzPnZTSsatSkHI4KY22+7K+ZhscLIaBKjqlIDj
++fHBrutW9jtog1E3JUO9ZHokB1qlRLs4YhpQ8YzHRabEBaX892ZPLNwtmFLOWK1p
+9klR7/RXnu13nStNIYAHk20=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVUZXN0
IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzdWl0
-ZTAeFw0xODExMjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMEIxQDA+BgNVBAMMN1Rl
+ZTAeFw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEIxQDA+BgNVBAMMN1Rl
c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBjbGllbnQg
Y2VydHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC75x7yuQ1+gK8j
1aO6D2nGym2x2OFniztlnx4PlWWWivIrYlxy6xAhfrVdjwjc5mMtOCrVKllsdC+S
@@ -48,10 +48,10 @@ B2yipfW1PUxM9a7/p19CRBcDQ+LNW+YFqwARByHGq1wfatJzpM8TXe+XEnRfW9KX
P3a5PqR6evFQOzjcAf+QBJ0hAEddUDhdYECbs1GrApfsEHBuwXabdCH41j0F/0yc
kctydWfBl2Vbmd3sfsFMHjde+SJhqxyq6xiSL59jnx4ZKmtn9VSOYbGmBCdBdYK7
RTcnJQv9AgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
-AB6zh/Jw+t232100Tztr7wJoKH8DL1hnSm3e7omdj0WlKfuZwTqDuMzB3H4LOSnS
-A00XpyMAGAJC6yRjS8pt+pjY5Jt6ouSqf6wNq0mF0jiIDeg1k/GNEjigx+0ITqbE
-lUJ56AcpoBNhOwBjOCRFh4JuspHZqHXgUNYTEicClbV+lZwoMIIK1e6FYRZDqMtL
-+34GtZACImqvRkP5alqQg7hJUM1zbDVf2qebY4cfSu4OfTu6Og6KrL8Cu6bqR2et
-0a/TbthHYz1QGDYRoVTSP4uWoG9M1ZbsA/bNE2eqcrQj+dJ4AmIIr8Yl8mrwo/FZ
-SvgeLMlQY7UNwLUDtwy9QkI=
+AKsQncM/5JZez/dECPg7xp8T5Sj++mJSiFsFr2Isk3HjJBI+skvZfpWkJnkEUl4a
++BspqoeAD5K+Fad0/Xg/AyZztONpLU+haA4eQCpwYHVY/DSjasjEI5BbCV8mrrYP
+SLcKGIJrPW+beO8pbjdkJNxBiDTeYzMBTbfHvarBYLYAvur8ZvUgKExDWP5kGUe8
+zA9F4FJ/Nig03QzQgxaNjxXoy892fAs6KyWFoGZDqAXlXAFNuz83joTiGNzNTiW4
+qRKpBa6aitnPVeIs8m3z/SUcm5PdMWvXVlsrlA0Gx2EM/B02EWnPk/U+CHtNey7r
+h4LJ4NA9GngE2gewd/gNItc=
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client+client_ca.crt b/src/test/ssl/ssl/client+client_ca.crt
index 2804527f3e..154bcd58e7 100644
--- a/src/test/ssl/ssl/client+client_ca.crt
+++ b/src/test/ssl/ssl/client+client_ca.crt
@@ -1,24 +1,24 @@
-----BEGIN CERTIFICATE-----
MIICzDCCAbQCAQEwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IGNsaWVudCBjZXJ0czAe
-Fw0xODExMjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMBYxFDASBgNVBAMMC3NzbHRl
+Fw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBYxFDASBgNVBAMMC3NzbHRl
c3R1c2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtIugLqHywEAE
vyRZGMVAkdk1zCa5FFaPOEFhHiAMpwFOEIEi4Svk9kSSRecmeJcody1sLNoFqtTA
b5tYaDoGIVZfc8/kxm8sbsTE/3JOsON3CMqjOQkI1ZKjDSF1gtrGSmatgjqsMnlP
UJkFEsPhFg6NTf1ZUjFiQeWEli0fQJ2/k+7MI4S0t0pDJJJWrqF4l6eSgu8rsBoX
XHy4OLAz6j23r2k5FZs6H/poII95ia+E8hG8SrJmMa88naRdq7hHW802Z6lEhnRW
ND+tDGjt0ZaTfxx+CxN4UjgbboOJifTykVHjuzBR1+IzLHcxoZCLP1cjadSqMz5b
-ziJTGtHzYwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAcIGps6BnsRxkN5sphg6GK
-tzDvp2IUyOu5oeAHdJLT5JFZhKKzhDD4KiOv+XWzdHcSAl3xMqAqnFdSTCt2vtc+
-rk04eyVWJALyf6oPT60Vn5sFaaxlTg1+tnZMCCycDxM6lc/6onzgp6DUWGozlgSh
-eNgCyaNU73VTuMgd+s/QrZ5HCr0OPAb3aWRQy7hVZeOniNBXWrO/CC2Swfwz7jU3
-dvLAWYENUvZlE2S7HnQGclGIJb38qFCnquuSgmO9yT30Lmmwp33k5/evN9cNQMxU
-c4ChYCaabOGXUaBJNzJAYMEUHh+o+LPgFF2iB0mL7FAUip9XsjOiOwcrbusM/g+2
+ziJTGtHzYwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCQonvnvG8wlfoo+J476Kjr
+Jm4i9pPgUTYNyF4lzhXViw24bKZVsNBaeVbu6XXRamzkrLL/qYUrQAm2ZcDqnVxC
+GXLgOYwIvuN7hGyks3Jh+tWQQf5UuhbhyJrOju1Z8nTI2dDgiHjsEHVxbVM9vMYl
+IiwjoU68gR/Gc8tApiIJe/HDMmSbm2W58heXXKG7r5790u5MO0vdBiGTlj0WdktU
+dB/ltpt5sm17SoUvSDIKZkLjHv/cuetCh7tCVrs0Gi0mf2aWdlXhPDh+KnDzEhlf
+/vW2Btlq1LQtYfP0yzkrmGR2dt72pg6bN6e7qc5YDYMXQPXvEZBiQbsgBPMm75Mc
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVUZXN0
IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzdWl0
-ZTAeFw0xODExMjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMEIxQDA+BgNVBAMMN1Rl
+ZTAeFw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEIxQDA+BgNVBAMMN1Rl
c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBjbGllbnQg
Y2VydHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC75x7yuQ1+gK8j
1aO6D2nGym2x2OFniztlnx4PlWWWivIrYlxy6xAhfrVdjwjc5mMtOCrVKllsdC+S
@@ -27,10 +27,10 @@ B2yipfW1PUxM9a7/p19CRBcDQ+LNW+YFqwARByHGq1wfatJzpM8TXe+XEnRfW9KX
P3a5PqR6evFQOzjcAf+QBJ0hAEddUDhdYECbs1GrApfsEHBuwXabdCH41j0F/0yc
kctydWfBl2Vbmd3sfsFMHjde+SJhqxyq6xiSL59jnx4ZKmtn9VSOYbGmBCdBdYK7
RTcnJQv9AgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
-AB6zh/Jw+t232100Tztr7wJoKH8DL1hnSm3e7omdj0WlKfuZwTqDuMzB3H4LOSnS
-A00XpyMAGAJC6yRjS8pt+pjY5Jt6ouSqf6wNq0mF0jiIDeg1k/GNEjigx+0ITqbE
-lUJ56AcpoBNhOwBjOCRFh4JuspHZqHXgUNYTEicClbV+lZwoMIIK1e6FYRZDqMtL
-+34GtZACImqvRkP5alqQg7hJUM1zbDVf2qebY4cfSu4OfTu6Og6KrL8Cu6bqR2et
-0a/TbthHYz1QGDYRoVTSP4uWoG9M1ZbsA/bNE2eqcrQj+dJ4AmIIr8Yl8mrwo/FZ
-SvgeLMlQY7UNwLUDtwy9QkI=
+AKsQncM/5JZez/dECPg7xp8T5Sj++mJSiFsFr2Isk3HjJBI+skvZfpWkJnkEUl4a
++BspqoeAD5K+Fad0/Xg/AyZztONpLU+haA4eQCpwYHVY/DSjasjEI5BbCV8mrrYP
+SLcKGIJrPW+beO8pbjdkJNxBiDTeYzMBTbfHvarBYLYAvur8ZvUgKExDWP5kGUe8
+zA9F4FJ/Nig03QzQgxaNjxXoy892fAs6KyWFoGZDqAXlXAFNuz83joTiGNzNTiW4
+qRKpBa6aitnPVeIs8m3z/SUcm5PdMWvXVlsrlA0Gx2EM/B02EWnPk/U+CHtNey7r
+h4LJ4NA9GngE2gewd/gNItc=
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client-crldir/9bb9e3c3.r0 b/src/test/ssl/ssl/client-crldir/9bb9e3c3.r0
new file mode 100644
index 0000000000..b5c689e537
--- /dev/null
+++ b/src/test/ssl/ssl/client-crldir/9bb9e3c3.r0
@@ -0,0 +1,11 @@
+-----BEGIN X509 CRL-----
+MIIBnjCBhzANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRzFw0yMDA4
+MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBQwEgIBAhcNMjAwODMxMDYwNzIzWjAN
+BgkqhkiG9w0BAQsFAAOCAQEAQ3ZK9Bx9i2JBSR2XgEFSvy8JrtRurpGpGcnh0ann
+G/vLY+Kp/UGiVnh8jwJ35Q7VUVGYNTKv2gc+WFFmscZaoP69RrgA9fbl9gZ4yUic
+H+XXiR4mQKk03EEKuPlZdWA1PMGAoAZxA8aCrrDZobrRgXEiSRdoQl8sHEJ3f1W1
+EoL+F3w77GzirYQukfNyIfzA6YpfphrNUkDN8jjrNB5/XzTT7fysutpflVKs/tLl
+TKHmwkrCC+TXJ8P2/KaIdQ0QgJSv5XIKS0vn4GC+zgjoUC3D1fprfRqmrBeQyLXV
+eUJda/H6uldOPpAJ2yLNR7S7COvFkGvIxunG7uPZiGq1eg==
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/client-revoked.crt b/src/test/ssl/ssl/client-revoked.crt
index 14857a33a2..1a9047dc63 100644
--- a/src/test/ssl/ssl/client-revoked.crt
+++ b/src/test/ssl/ssl/client-revoked.crt
@@ -1,17 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICzDCCAbQCAQIwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IGNsaWVudCBjZXJ0czAe
-Fw0xODExMjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMBYxFDASBgNVBAMMC3NzbHRl
+Fw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBYxFDASBgNVBAMMC3NzbHRl
c3R1c2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoBcmY2Z+qa+l
UB5YSYnGLt96S7axkoDvIzLJkwJugGqw1U72A6lAUTyAPVntsmbhoMpDEHK6ylg8
U4HC3L1hbhSpFriTITJ3TzH4+wdDH1KZYlM2k0gfrKrksJyZ7ftAyuBvzBRlFbBe
xopR9VQjqgAuNKByJswldOe0KwP0nmb/TtT9lkAt7XjrSut5MUezFVnvTxabm7tQ
ciDG+8QqE0b8lH3N3VOXWZWCeXPRrwboO3baAmcue4V20N0ALARP+QZNElBa7Jq+
l77VNjneRk07jjaE7PCGVlWfPggppZos1Ay1sb2JhK0S9pZrynQT/ck3qhG4QuKp
-cmn/Bbe/8wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBySTwOO9zSFCtfRjbbblDx
-AK2ttILR0ZJXnvzjNjuErsT9qeXaq2t/iG/vmhH5XDjaefXFLCLqFunvcg6cIz1A
-HhAw+JInfyk3TUpDaX6M0X8qj184e4kXzVc83Afa3LiP5JkirzCSv6ErqAHw2VVd
-bZbZUwMfQLpWHVqXK89Pb7q791H4VeEx9CLxtZ2PSr2GCdpFbVMJvdBPChD2Re1A
-ELcbMZ9iOq2AUN/gxrt7HnE3dRoGQk6AJOfvhi2eZcVWiLtITScdPk1nYcNxGid3
-BWW+tdLbjmSe2FXNfDwBTvuHh5A9S399X5l/nLAng2iTGSvIm1OgUnC2oWsok3EI
+cmn/Bbe/8wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAQzzp5yTHFan/LkTXHkvuU
+XEqQbUzD7iUuEop7I6BY8vzLkijylCIXDOg7WmD2Sysb3+7nJ8JG8BZzXnXoS+hz
+sdEF/lFIZltx6S9wvC6QMK8vJat+XM0FBO7C27cswD929Loiqy0CxOdbrED8kwWf
+oN7Kv01wdtEmd+xK6zqtDB/vm8Dq89zlBDHnJeM5iJi+BIMt1HM+FAlxDwgm18Xa
+K9u7xrmvpycfXFZ+nLM0B8gxJAQ8djxgB/hOAM9CDSEhzN5BJIZ4slZRq9bGVLjy
+dvrW0LoNKhitgS/Pe400Ej9LGXSsBrVP6FXHcL4qvHqdwKl0R3QiodX6ro2H0vBY
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client.crl b/src/test/ssl/ssl/client.crl
index a667680e04..b5c689e537 100644
--- a/src/test/ssl/ssl/client.crl
+++ b/src/test/ssl/ssl/client.crl
@@ -1,11 +1,11 @@
-----BEGIN X509 CRL-----
MIIBnjCBhzANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
-b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRzFw0xODEx
-MjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMBQwEgIBAhcNMTgxMTI3MTM0MDU1WjAN
-BgkqhkiG9w0BAQsFAAOCAQEAXjLxA9Qc6gAudwUHBxMIq5EHBcuNEX5e3GNlkyNf
-8I0DtHTPfJPvmAG+i6lYz//hHmmjxK0dR2ucg79XgXI/6OpDqlxS/TG1Xv52wA1p
-xz6GaJ2hC8Lk4/vbJo/Rrzme2QsI7xqBWya0JWVrehttqhFxPzWA5wID8X7G4Kb4
-pjVnzqYzn8A9FBiV9t10oZg60aVLqt3kbyy+U3pefvjhj8NmQc7uyuVjWvYZA0vG
-nnDUo4EKJzHNIYLk+EfpzKWO2XAWBLOT9SyyNCeMuQ5p/2pdAt9jtWHenms2ajo9
-2iUsHS91e3TooP9yNYuNcN8/wXY6H2Xm+dCLcEnkcr7EEw==
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRzFw0yMDA4
+MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBQwEgIBAhcNMjAwODMxMDYwNzIzWjAN
+BgkqhkiG9w0BAQsFAAOCAQEAQ3ZK9Bx9i2JBSR2XgEFSvy8JrtRurpGpGcnh0ann
+G/vLY+Kp/UGiVnh8jwJ35Q7VUVGYNTKv2gc+WFFmscZaoP69RrgA9fbl9gZ4yUic
+H+XXiR4mQKk03EEKuPlZdWA1PMGAoAZxA8aCrrDZobrRgXEiSRdoQl8sHEJ3f1W1
+EoL+F3w77GzirYQukfNyIfzA6YpfphrNUkDN8jjrNB5/XzTT7fysutpflVKs/tLl
+TKHmwkrCC+TXJ8P2/KaIdQ0QgJSv5XIKS0vn4GC+zgjoUC3D1fprfRqmrBeQyLXV
+eUJda/H6uldOPpAJ2yLNR7S7COvFkGvIxunG7uPZiGq1eg==
-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/client.crt b/src/test/ssl/ssl/client.crt
index 4d0a6ef419..b46de4fa9b 100644
--- a/src/test/ssl/ssl/client.crt
+++ b/src/test/ssl/ssl/client.crt
@@ -1,17 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICzDCCAbQCAQEwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IGNsaWVudCBjZXJ0czAe
-Fw0xODExMjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMBYxFDASBgNVBAMMC3NzbHRl
+Fw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBYxFDASBgNVBAMMC3NzbHRl
c3R1c2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtIugLqHywEAE
vyRZGMVAkdk1zCa5FFaPOEFhHiAMpwFOEIEi4Svk9kSSRecmeJcody1sLNoFqtTA
b5tYaDoGIVZfc8/kxm8sbsTE/3JOsON3CMqjOQkI1ZKjDSF1gtrGSmatgjqsMnlP
UJkFEsPhFg6NTf1ZUjFiQeWEli0fQJ2/k+7MI4S0t0pDJJJWrqF4l6eSgu8rsBoX
XHy4OLAz6j23r2k5FZs6H/poII95ia+E8hG8SrJmMa88naRdq7hHW802Z6lEhnRW
ND+tDGjt0ZaTfxx+CxN4UjgbboOJifTykVHjuzBR1+IzLHcxoZCLP1cjadSqMz5b
-ziJTGtHzYwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAcIGps6BnsRxkN5sphg6GK
-tzDvp2IUyOu5oeAHdJLT5JFZhKKzhDD4KiOv+XWzdHcSAl3xMqAqnFdSTCt2vtc+
-rk04eyVWJALyf6oPT60Vn5sFaaxlTg1+tnZMCCycDxM6lc/6onzgp6DUWGozlgSh
-eNgCyaNU73VTuMgd+s/QrZ5HCr0OPAb3aWRQy7hVZeOniNBXWrO/CC2Swfwz7jU3
-dvLAWYENUvZlE2S7HnQGclGIJb38qFCnquuSgmO9yT30Lmmwp33k5/evN9cNQMxU
-c4ChYCaabOGXUaBJNzJAYMEUHh+o+LPgFF2iB0mL7FAUip9XsjOiOwcrbusM/g+2
+ziJTGtHzYwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCQonvnvG8wlfoo+J476Kjr
+Jm4i9pPgUTYNyF4lzhXViw24bKZVsNBaeVbu6XXRamzkrLL/qYUrQAm2ZcDqnVxC
+GXLgOYwIvuN7hGyks3Jh+tWQQf5UuhbhyJrOju1Z8nTI2dDgiHjsEHVxbVM9vMYl
+IiwjoU68gR/Gc8tApiIJe/HDMmSbm2W58heXXKG7r5790u5MO0vdBiGTlj0WdktU
+dB/ltpt5sm17SoUvSDIKZkLjHv/cuetCh7tCVrs0Gi0mf2aWdlXhPDh+KnDzEhlf
+/vW2Btlq1LQtYfP0yzkrmGR2dt72pg6bN6e7qc5YDYMXQPXvEZBiQbsgBPMm75Mc
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client_ca.crt b/src/test/ssl/ssl/client_ca.crt
index 1ef3771261..23bac28a0c 100644
--- a/src/test/ssl/ssl/client_ca.crt
+++ b/src/test/ssl/ssl/client_ca.crt
@@ -1,7 +1,7 @@
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVUZXN0
IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzdWl0
-ZTAeFw0xODExMjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMEIxQDA+BgNVBAMMN1Rl
+ZTAeFw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEIxQDA+BgNVBAMMN1Rl
c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBjbGllbnQg
Y2VydHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC75x7yuQ1+gK8j
1aO6D2nGym2x2OFniztlnx4PlWWWivIrYlxy6xAhfrVdjwjc5mMtOCrVKllsdC+S
@@ -10,10 +10,10 @@ B2yipfW1PUxM9a7/p19CRBcDQ+LNW+YFqwARByHGq1wfatJzpM8TXe+XEnRfW9KX
P3a5PqR6evFQOzjcAf+QBJ0hAEddUDhdYECbs1GrApfsEHBuwXabdCH41j0F/0yc
kctydWfBl2Vbmd3sfsFMHjde+SJhqxyq6xiSL59jnx4ZKmtn9VSOYbGmBCdBdYK7
RTcnJQv9AgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
-AB6zh/Jw+t232100Tztr7wJoKH8DL1hnSm3e7omdj0WlKfuZwTqDuMzB3H4LOSnS
-A00XpyMAGAJC6yRjS8pt+pjY5Jt6ouSqf6wNq0mF0jiIDeg1k/GNEjigx+0ITqbE
-lUJ56AcpoBNhOwBjOCRFh4JuspHZqHXgUNYTEicClbV+lZwoMIIK1e6FYRZDqMtL
-+34GtZACImqvRkP5alqQg7hJUM1zbDVf2qebY4cfSu4OfTu6Og6KrL8Cu6bqR2et
-0a/TbthHYz1QGDYRoVTSP4uWoG9M1ZbsA/bNE2eqcrQj+dJ4AmIIr8Yl8mrwo/FZ
-SvgeLMlQY7UNwLUDtwy9QkI=
+AKsQncM/5JZez/dECPg7xp8T5Sj++mJSiFsFr2Isk3HjJBI+skvZfpWkJnkEUl4a
++BspqoeAD5K+Fad0/Xg/AyZztONpLU+haA4eQCpwYHVY/DSjasjEI5BbCV8mrrYP
+SLcKGIJrPW+beO8pbjdkJNxBiDTeYzMBTbfHvarBYLYAvur8ZvUgKExDWP5kGUe8
+zA9F4FJ/Nig03QzQgxaNjxXoy892fAs6KyWFoGZDqAXlXAFNuz83joTiGNzNTiW4
+qRKpBa6aitnPVeIs8m3z/SUcm5PdMWvXVlsrlA0Gx2EM/B02EWnPk/U+CHtNey7r
+h4LJ4NA9GngE2gewd/gNItc=
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/root+client-crldir/9bb9e3c3.r0 b/src/test/ssl/ssl/root+client-crldir/9bb9e3c3.r0
new file mode 100644
index 0000000000..b5c689e537
--- /dev/null
+++ b/src/test/ssl/ssl/root+client-crldir/9bb9e3c3.r0
@@ -0,0 +1,11 @@
+-----BEGIN X509 CRL-----
+MIIBnjCBhzANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRzFw0yMDA4
+MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBQwEgIBAhcNMjAwODMxMDYwNzIzWjAN
+BgkqhkiG9w0BAQsFAAOCAQEAQ3ZK9Bx9i2JBSR2XgEFSvy8JrtRurpGpGcnh0ann
+G/vLY+Kp/UGiVnh8jwJ35Q7VUVGYNTKv2gc+WFFmscZaoP69RrgA9fbl9gZ4yUic
+H+XXiR4mQKk03EEKuPlZdWA1PMGAoAZxA8aCrrDZobrRgXEiSRdoQl8sHEJ3f1W1
+EoL+F3w77GzirYQukfNyIfzA6YpfphrNUkDN8jjrNB5/XzTT7fysutpflVKs/tLl
+TKHmwkrCC+TXJ8P2/KaIdQ0QgJSv5XIKS0vn4GC+zgjoUC3D1fprfRqmrBeQyLXV
+eUJda/H6uldOPpAJ2yLNR7S7COvFkGvIxunG7uPZiGq1eg==
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/root+client-crldir/a3d11bff.r0 b/src/test/ssl/ssl/root+client-crldir/a3d11bff.r0
new file mode 100644
index 0000000000..8cca69ba40
--- /dev/null
+++ b/src/test/ssl/ssl/root+client-crldir/a3d11bff.r0
@@ -0,0 +1,11 @@
+-----BEGIN X509 CRL-----
+MIIBhTBvMA0GCSqGSIb3DQEBCwUAMEAxPjA8BgNVBAMMNVRlc3Qgcm9vdCBDQSBm
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0yMDA4MzEw
+NjA3MjNaFw00ODAxMTcwNjA3MjNaMA0GCSqGSIb3DQEBCwUAA4IBAQB/pWC8moSA
+DL36OWwOig7/yEmbVdpFk2mPmAullbH0A6I/lWkrSrKbb0wk8tE04N2Pw0WDvtqd
+aWq5bx1mL1ve0su2SUkuM5Tb26ZVakI3WDCdDmJIEXAE32g6gmdnAZqaaK2Sy58y
+fEvCTe8Zxe0+8eHNv0VYfhl1ALUwMoO/VrVd453O1FWfXBopm+kkHPB6BDyo9ZbN
+PzLHGnGAP2L+1Ps5TP1AMk2ZEZ87QysB4G2qm1x4A6R4Jf6XBOC5y4YI+vsncUZ2
+UCajgatsFN6LZVHO+q1h5LhsVpSCs9yX8gjaoenJtOrY6XFis1DNHrm0QFSN/yNu
+Muuf3P8i5zl0
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/root+client.crl b/src/test/ssl/ssl/root+client.crl
index 854d77b71e..fdc00efebb 100644
--- a/src/test/ssl/ssl/root+client.crl
+++ b/src/test/ssl/ssl/root+client.crl
@@ -1,22 +1,22 @@
-----BEGIN X509 CRL-----
MIIBhTBvMA0GCSqGSIb3DQEBCwUAMEAxPjA8BgNVBAMMNVRlc3Qgcm9vdCBDQSBm
-b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0xODExMjcx
-MzQwNTVaFw00NjA0MTQxMzQwNTVaMA0GCSqGSIb3DQEBCwUAA4IBAQB8OSDym4/a
-qbZOrZvOOhmKrd7AJSTgAadtdK0CX3v58Ym3EmZK7gQFdBuFCXnvbue/x6avZHgz
-4pYFlJmL0IiD4QuTzsoo+LzifrmTzteO9oEJNLd2bjfEnpE5Wdaw6Yuy2Xb5edy5
-lQhNZdc8w3FiXhPOEUAi7EbdfDwn4G/fvEjpzyVb2wCujDUUePUGGayjKIM4PUu4
-pixM6gt9FFL27l47lQ3g0PbvB3TnU3oqcB3Y17FjbxjFc6AsGXholNetoEE2/49E
-PEYzOH7/PtxlZUtoCqZM+741LuI6Q7z4/P2X/IY33lMy6Iiyc41C94l/P7fCkMLG
-AlO+O0a4SpYS
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0yMDA4MzEw
+NjA3MjNaFw00ODAxMTcwNjA3MjNaMA0GCSqGSIb3DQEBCwUAA4IBAQB/pWC8moSA
+DL36OWwOig7/yEmbVdpFk2mPmAullbH0A6I/lWkrSrKbb0wk8tE04N2Pw0WDvtqd
+aWq5bx1mL1ve0su2SUkuM5Tb26ZVakI3WDCdDmJIEXAE32g6gmdnAZqaaK2Sy58y
+fEvCTe8Zxe0+8eHNv0VYfhl1ALUwMoO/VrVd453O1FWfXBopm+kkHPB6BDyo9ZbN
+PzLHGnGAP2L+1Ps5TP1AMk2ZEZ87QysB4G2qm1x4A6R4Jf6XBOC5y4YI+vsncUZ2
+UCajgatsFN6LZVHO+q1h5LhsVpSCs9yX8gjaoenJtOrY6XFis1DNHrm0QFSN/yNu
+Muuf3P8i5zl0
-----END X509 CRL-----
-----BEGIN X509 CRL-----
MIIBnjCBhzANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
-b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRzFw0xODEx
-MjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMBQwEgIBAhcNMTgxMTI3MTM0MDU1WjAN
-BgkqhkiG9w0BAQsFAAOCAQEAXjLxA9Qc6gAudwUHBxMIq5EHBcuNEX5e3GNlkyNf
-8I0DtHTPfJPvmAG+i6lYz//hHmmjxK0dR2ucg79XgXI/6OpDqlxS/TG1Xv52wA1p
-xz6GaJ2hC8Lk4/vbJo/Rrzme2QsI7xqBWya0JWVrehttqhFxPzWA5wID8X7G4Kb4
-pjVnzqYzn8A9FBiV9t10oZg60aVLqt3kbyy+U3pefvjhj8NmQc7uyuVjWvYZA0vG
-nnDUo4EKJzHNIYLk+EfpzKWO2XAWBLOT9SyyNCeMuQ5p/2pdAt9jtWHenms2ajo9
-2iUsHS91e3TooP9yNYuNcN8/wXY6H2Xm+dCLcEnkcr7EEw==
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRzFw0yMDA4
+MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBQwEgIBAhcNMjAwODMxMDYwNzIzWjAN
+BgkqhkiG9w0BAQsFAAOCAQEAQ3ZK9Bx9i2JBSR2XgEFSvy8JrtRurpGpGcnh0ann
+G/vLY+Kp/UGiVnh8jwJ35Q7VUVGYNTKv2gc+WFFmscZaoP69RrgA9fbl9gZ4yUic
+H+XXiR4mQKk03EEKuPlZdWA1PMGAoAZxA8aCrrDZobrRgXEiSRdoQl8sHEJ3f1W1
+EoL+F3w77GzirYQukfNyIfzA6YpfphrNUkDN8jjrNB5/XzTT7fysutpflVKs/tLl
+TKHmwkrCC+TXJ8P2/KaIdQ0QgJSv5XIKS0vn4GC+zgjoUC3D1fprfRqmrBeQyLXV
+eUJda/H6uldOPpAJ2yLNR7S7COvFkGvIxunG7uPZiGq1eg==
-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/root+client_ca.crt b/src/test/ssl/ssl/root+client_ca.crt
index 1867cd9c31..c7c024eeba 100644
--- a/src/test/ssl/ssl/root+client_ca.crt
+++ b/src/test/ssl/ssl/root+client_ca.crt
@@ -1,8 +1,8 @@
-----BEGIN CERTIFICATE-----
-MIIDHjCCAgagAwIBAgIUEAgXJ/ibw6TVTUoomlBsPMfVTlMwDQYJKoZIhvcNAQEL
+MIIDHjCCAgagAwIBAgIUbDho+LuL8GrZ/iVFY16vdG6kNo0wDQYJKoZIhvcNAQEL
BQAwQDE+MDwGA1UEAww1VGVzdCByb290IENBIGZvciBQb3N0Z3JlU1FMIFNTTCBy
-ZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcNMTgxMTI3MTM0MDU0WhcNNDYwNDE0MTM0
-MDU0WjBAMT4wPAYDVQQDDDVUZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NM
+ZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcNMjAwODMxMDYwNzIyWhcNNDgwMTE3MDYw
+NzIyWjBAMT4wPAYDVQQDDDVUZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NM
IHJlZ3Jlc3Npb24gdGVzdCBzdWl0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALZ81vKKBJlxgjwuNoK67I4IE9zfSLb0eHbgZwZxDVzdmFejARrHlWk3
+MK7Nav7RLSJ990am33zb58CTHc7YYVlBp07+PwLXzypqWkhYfok1OYYjyjCrFDs
@@ -10,17 +10,17 @@ sjcJI3hRCZNEz+wYsG+tdYWJ+gRPQOWfh0YfO2rFgXAIMLiF6lyWzf1eOM+OjYrF
/eyzwbMaJkkGa/AyZKz3wZiPq0jTuYLVmH4MK7MBOsUfSmsBsn/ohyRCQzM+ol0v
Qlsrulj8usponRPDh9ng4PB5OSgR79YimQZnASQzJxiUvMADrKL5L6KwLxJlzbqY
R0b5mLh8KBzBQmSh3Aj2e2I7Z17hdaMCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zAN
-BgkqhkiG9w0BAQsFAAOCAQEASzA7ApbuKn8lkC706gRL37a33yTZZ8rjZ4dnvCtq
-6OltlYDJ8IndotKbLH0SUEAxrvcaFnMt7AX9pRf2sGBKbY8zcxqPfsvzVehgx4Ea
-1RYksFW4h97jj1a1RKukTKuEOEEipbxwo0rLxfjvdaf2izqchJsLGtbocIZf0bD8
-djbE9jOLkx7saL08qC8ECrf9utsee+LJCsUYbNgYyIItEy6yVnmF/ICz93Utn1cI
-RfqZr1lM2Ia2LP9eDTmiuR9m+/MzkeRvvJHonNrRJHlcggtYHICvYioh9/jALBcm
-wZ+hTUePVqy4hOCBJ975CXjfKFN4MKQAdeB3EO5eBYAD3Q==
+BgkqhkiG9w0BAQsFAAOCAQEAo5siIstgYy1MjyVaSHpxy6Cq7JT73RhYYudtAy8e
+XwVXqy3xuZtl+i2XaWnsojyHRZ83t+1rVCoesbZefyfz9d6qZJkCkWdKdjiVEu+s
+CJSo+tGyCZQ1ZTb+07WY01P97gfswP+VHN4EjxJmUnLdqJEJsdOckvL1mBfortt6
+CvL7adPwftppNivF64R0QZfOsLIaaOa0oi33DQDDNUdrx5f3xWwYmzw+f95lvbYR
+DXCytT647nc44Gu09erE4sWipIknFBvg3P1D0icFSIoEh44mU9o1lHUFsogXa09g
+8Vkho/rudYrHi/cwwzQlUjZyHPg6k1iU9oZGEaW5gVKxSw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVUZXN0
IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzdWl0
-ZTAeFw0xODExMjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMEIxQDA+BgNVBAMMN1Rl
+ZTAeFw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEIxQDA+BgNVBAMMN1Rl
c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBjbGllbnQg
Y2VydHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC75x7yuQ1+gK8j
1aO6D2nGym2x2OFniztlnx4PlWWWivIrYlxy6xAhfrVdjwjc5mMtOCrVKllsdC+S
@@ -29,10 +29,10 @@ B2yipfW1PUxM9a7/p19CRBcDQ+LNW+YFqwARByHGq1wfatJzpM8TXe+XEnRfW9KX
P3a5PqR6evFQOzjcAf+QBJ0hAEddUDhdYECbs1GrApfsEHBuwXabdCH41j0F/0yc
kctydWfBl2Vbmd3sfsFMHjde+SJhqxyq6xiSL59jnx4ZKmtn9VSOYbGmBCdBdYK7
RTcnJQv9AgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
-AB6zh/Jw+t232100Tztr7wJoKH8DL1hnSm3e7omdj0WlKfuZwTqDuMzB3H4LOSnS
-A00XpyMAGAJC6yRjS8pt+pjY5Jt6ouSqf6wNq0mF0jiIDeg1k/GNEjigx+0ITqbE
-lUJ56AcpoBNhOwBjOCRFh4JuspHZqHXgUNYTEicClbV+lZwoMIIK1e6FYRZDqMtL
-+34GtZACImqvRkP5alqQg7hJUM1zbDVf2qebY4cfSu4OfTu6Og6KrL8Cu6bqR2et
-0a/TbthHYz1QGDYRoVTSP4uWoG9M1ZbsA/bNE2eqcrQj+dJ4AmIIr8Yl8mrwo/FZ
-SvgeLMlQY7UNwLUDtwy9QkI=
+AKsQncM/5JZez/dECPg7xp8T5Sj++mJSiFsFr2Isk3HjJBI+skvZfpWkJnkEUl4a
++BspqoeAD5K+Fad0/Xg/AyZztONpLU+haA4eQCpwYHVY/DSjasjEI5BbCV8mrrYP
+SLcKGIJrPW+beO8pbjdkJNxBiDTeYzMBTbfHvarBYLYAvur8ZvUgKExDWP5kGUe8
+zA9F4FJ/Nig03QzQgxaNjxXoy892fAs6KyWFoGZDqAXlXAFNuz83joTiGNzNTiW4
+qRKpBa6aitnPVeIs8m3z/SUcm5PdMWvXVlsrlA0Gx2EM/B02EWnPk/U+CHtNey7r
+h4LJ4NA9GngE2gewd/gNItc=
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/root+server-crldir/a3d11bff.r0 b/src/test/ssl/ssl/root+server-crldir/a3d11bff.r0
new file mode 100644
index 0000000000..8cca69ba40
--- /dev/null
+++ b/src/test/ssl/ssl/root+server-crldir/a3d11bff.r0
@@ -0,0 +1,11 @@
+-----BEGIN X509 CRL-----
+MIIBhTBvMA0GCSqGSIb3DQEBCwUAMEAxPjA8BgNVBAMMNVRlc3Qgcm9vdCBDQSBm
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0yMDA4MzEw
+NjA3MjNaFw00ODAxMTcwNjA3MjNaMA0GCSqGSIb3DQEBCwUAA4IBAQB/pWC8moSA
+DL36OWwOig7/yEmbVdpFk2mPmAullbH0A6I/lWkrSrKbb0wk8tE04N2Pw0WDvtqd
+aWq5bx1mL1ve0su2SUkuM5Tb26ZVakI3WDCdDmJIEXAE32g6gmdnAZqaaK2Sy58y
+fEvCTe8Zxe0+8eHNv0VYfhl1ALUwMoO/VrVd453O1FWfXBopm+kkHPB6BDyo9ZbN
+PzLHGnGAP2L+1Ps5TP1AMk2ZEZ87QysB4G2qm1x4A6R4Jf6XBOC5y4YI+vsncUZ2
+UCajgatsFN6LZVHO+q1h5LhsVpSCs9yX8gjaoenJtOrY6XFis1DNHrm0QFSN/yNu
+Muuf3P8i5zl0
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/root+server-crldir/a836cc2d.r0 b/src/test/ssl/ssl/root+server-crldir/a836cc2d.r0
new file mode 100644
index 0000000000..9588d1e524
--- /dev/null
+++ b/src/test/ssl/ssl/root+server-crldir/a836cc2d.r0
@@ -0,0 +1,11 @@
+-----BEGIN X509 CRL-----
+MIIBnjCBhzANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRzFw0yMDA4
+MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBQwEgIBBhcNMjAwODMxMDYwNzIzWjAN
+BgkqhkiG9w0BAQsFAAOCAQEA2CBoLuLCpXcVSHqQtKnTcz25FyPsGkSO3luiUiG5
+jnI9HvZ9OzeQGGho6XBhVHAmEtwKOo6pcxqDe8Qkf6X7v0AUc19BWkxOXT+sCCdM
+yOvRhNPAhxJToGgKqp41noTSMhZPLsvFEfbbFTZEABScfX2K+XdvQlAYXa3U375E
+71jFenrNh8fNTKfcikmio1rsybQ4PG/ASsrUflQ5LAz3Nm3awdw01auGzRFezq3z
+Ivnq3z5b2q+m653PUsygNRMFVxCAgrvqAadKci7MK/QthCbocrLIhuc9Mmx1Nbkm
+Wnv+La3b5HeH8Zi9Q89IBr12rH70Y6K3hggCUAo9CoK3zw==
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/root+server.crl b/src/test/ssl/ssl/root+server.crl
index 9720b3023c..ba7994d1fa 100644
--- a/src/test/ssl/ssl/root+server.crl
+++ b/src/test/ssl/ssl/root+server.crl
@@ -1,22 +1,22 @@
-----BEGIN X509 CRL-----
MIIBhTBvMA0GCSqGSIb3DQEBCwUAMEAxPjA8BgNVBAMMNVRlc3Qgcm9vdCBDQSBm
-b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0xODExMjcx
-MzQwNTVaFw00NjA0MTQxMzQwNTVaMA0GCSqGSIb3DQEBCwUAA4IBAQB8OSDym4/a
-qbZOrZvOOhmKrd7AJSTgAadtdK0CX3v58Ym3EmZK7gQFdBuFCXnvbue/x6avZHgz
-4pYFlJmL0IiD4QuTzsoo+LzifrmTzteO9oEJNLd2bjfEnpE5Wdaw6Yuy2Xb5edy5
-lQhNZdc8w3FiXhPOEUAi7EbdfDwn4G/fvEjpzyVb2wCujDUUePUGGayjKIM4PUu4
-pixM6gt9FFL27l47lQ3g0PbvB3TnU3oqcB3Y17FjbxjFc6AsGXholNetoEE2/49E
-PEYzOH7/PtxlZUtoCqZM+741LuI6Q7z4/P2X/IY33lMy6Iiyc41C94l/P7fCkMLG
-AlO+O0a4SpYS
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0yMDA4MzEw
+NjA3MjNaFw00ODAxMTcwNjA3MjNaMA0GCSqGSIb3DQEBCwUAA4IBAQB/pWC8moSA
+DL36OWwOig7/yEmbVdpFk2mPmAullbH0A6I/lWkrSrKbb0wk8tE04N2Pw0WDvtqd
+aWq5bx1mL1ve0su2SUkuM5Tb26ZVakI3WDCdDmJIEXAE32g6gmdnAZqaaK2Sy58y
+fEvCTe8Zxe0+8eHNv0VYfhl1ALUwMoO/VrVd453O1FWfXBopm+kkHPB6BDyo9ZbN
+PzLHGnGAP2L+1Ps5TP1AMk2ZEZ87QysB4G2qm1x4A6R4Jf6XBOC5y4YI+vsncUZ2
+UCajgatsFN6LZVHO+q1h5LhsVpSCs9yX8gjaoenJtOrY6XFis1DNHrm0QFSN/yNu
+Muuf3P8i5zl0
-----END X509 CRL-----
-----BEGIN X509 CRL-----
MIIBnjCBhzANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
-b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRzFw0xODEx
-MjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMBQwEgIBBhcNMTgxMTI3MTM0MDU1WjAN
-BgkqhkiG9w0BAQsFAAOCAQEAbVuJXemxM6HLlIHGWlQvVmsmG4ZTQWiDnZjfmrND
-xB4XsvZNPXnFkjdBENDROrbDRwm60SJDW73AbDbfq1IXAzSpuEyuRz61IyYKo0wq
-nmObJtVdIu3bVlWIlDXaP5Emk3d7ouCj5f8Kyeb8gm4pL3N6e0eI63hCaS39hhE6
-RLGh9HU9ht1kKfgcTwmB5b2HTPb4M6z1AmSIaMVqZTjIspsUgNF2+GBm3fOnOaiZ
-SEXWtgjMRXiIHbtU0va3LhSH5OSW0mh+L9oGUQDYnyuudnWGpulhqIp4qVkJRDDu
-41HpD83dV2uRtBLvc25AFHj7kXBflbO3gvGZVPYf1zVghQ==
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRzFw0yMDA4
+MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBQwEgIBBhcNMjAwODMxMDYwNzIzWjAN
+BgkqhkiG9w0BAQsFAAOCAQEA2CBoLuLCpXcVSHqQtKnTcz25FyPsGkSO3luiUiG5
+jnI9HvZ9OzeQGGho6XBhVHAmEtwKOo6pcxqDe8Qkf6X7v0AUc19BWkxOXT+sCCdM
+yOvRhNPAhxJToGgKqp41noTSMhZPLsvFEfbbFTZEABScfX2K+XdvQlAYXa3U375E
+71jFenrNh8fNTKfcikmio1rsybQ4PG/ASsrUflQ5LAz3Nm3awdw01auGzRFezq3z
+Ivnq3z5b2q+m653PUsygNRMFVxCAgrvqAadKci7MK/QthCbocrLIhuc9Mmx1Nbkm
+Wnv+La3b5HeH8Zi9Q89IBr12rH70Y6K3hggCUAo9CoK3zw==
-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/root+server_ca.crt b/src/test/ssl/ssl/root+server_ca.crt
index 861eba80d0..2c5c2d9a76 100644
--- a/src/test/ssl/ssl/root+server_ca.crt
+++ b/src/test/ssl/ssl/root+server_ca.crt
@@ -1,8 +1,8 @@
-----BEGIN CERTIFICATE-----
-MIIDHjCCAgagAwIBAgIUEAgXJ/ibw6TVTUoomlBsPMfVTlMwDQYJKoZIhvcNAQEL
+MIIDHjCCAgagAwIBAgIUbDho+LuL8GrZ/iVFY16vdG6kNo0wDQYJKoZIhvcNAQEL
BQAwQDE+MDwGA1UEAww1VGVzdCByb290IENBIGZvciBQb3N0Z3JlU1FMIFNTTCBy
-ZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcNMTgxMTI3MTM0MDU0WhcNNDYwNDE0MTM0
-MDU0WjBAMT4wPAYDVQQDDDVUZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NM
+ZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcNMjAwODMxMDYwNzIyWhcNNDgwMTE3MDYw
+NzIyWjBAMT4wPAYDVQQDDDVUZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NM
IHJlZ3Jlc3Npb24gdGVzdCBzdWl0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALZ81vKKBJlxgjwuNoK67I4IE9zfSLb0eHbgZwZxDVzdmFejARrHlWk3
+MK7Nav7RLSJ990am33zb58CTHc7YYVlBp07+PwLXzypqWkhYfok1OYYjyjCrFDs
@@ -10,17 +10,17 @@ sjcJI3hRCZNEz+wYsG+tdYWJ+gRPQOWfh0YfO2rFgXAIMLiF6lyWzf1eOM+OjYrF
/eyzwbMaJkkGa/AyZKz3wZiPq0jTuYLVmH4MK7MBOsUfSmsBsn/ohyRCQzM+ol0v
Qlsrulj8usponRPDh9ng4PB5OSgR79YimQZnASQzJxiUvMADrKL5L6KwLxJlzbqY
R0b5mLh8KBzBQmSh3Aj2e2I7Z17hdaMCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zAN
-BgkqhkiG9w0BAQsFAAOCAQEASzA7ApbuKn8lkC706gRL37a33yTZZ8rjZ4dnvCtq
-6OltlYDJ8IndotKbLH0SUEAxrvcaFnMt7AX9pRf2sGBKbY8zcxqPfsvzVehgx4Ea
-1RYksFW4h97jj1a1RKukTKuEOEEipbxwo0rLxfjvdaf2izqchJsLGtbocIZf0bD8
-djbE9jOLkx7saL08qC8ECrf9utsee+LJCsUYbNgYyIItEy6yVnmF/ICz93Utn1cI
-RfqZr1lM2Ia2LP9eDTmiuR9m+/MzkeRvvJHonNrRJHlcggtYHICvYioh9/jALBcm
-wZ+hTUePVqy4hOCBJ975CXjfKFN4MKQAdeB3EO5eBYAD3Q==
+BgkqhkiG9w0BAQsFAAOCAQEAo5siIstgYy1MjyVaSHpxy6Cq7JT73RhYYudtAy8e
+XwVXqy3xuZtl+i2XaWnsojyHRZ83t+1rVCoesbZefyfz9d6qZJkCkWdKdjiVEu+s
+CJSo+tGyCZQ1ZTb+07WY01P97gfswP+VHN4EjxJmUnLdqJEJsdOckvL1mBfortt6
+CvL7adPwftppNivF64R0QZfOsLIaaOa0oi33DQDDNUdrx5f3xWwYmzw+f95lvbYR
+DXCytT647nc44Gu09erE4sWipIknFBvg3P1D0icFSIoEh44mU9o1lHUFsogXa09g
+8Vkho/rudYrHi/cwwzQlUjZyHPg6k1iU9oZGEaW5gVKxSw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIBATANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVUZXN0
IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzdWl0
-ZTAeFw0xODExMjcxMzQwNTRaFw00NjA0MTQxMzQwNTRaMEIxQDA+BgNVBAMMN1Rl
+ZTAeFw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEIxQDA+BgNVBAMMN1Rl
c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzZXJ2ZXIg
Y2VydHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiSnYZbmc9vpCt
Ku1sKV9l663JCceubhMw8Gg16kV0hXEFf/TgGC4zkiYNHN7+G45YD7Nq0kBCq3dH
@@ -29,10 +29,10 @@ t2wPCc6c8pQoI64dfprVqPkvzoe1WBpZNetkUTk20v08jNeRa7XdRbRR6we1s9VG
QW9YWdH9N5ctaUXMG6lLV2OAjs+W1smpKfpIpMCA1lPGlElu70hynon/nQQvBP77
SfQpZVc0esM18jkZpr5LEKUCw+x6LaMsqmBHpAULfCffxn2r0uMBW4L4VaGg3W6F
h6iuJwRfAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
-AFlcKTaU/Ug3Q0hr3P1UQ6dWyK4aVn9rs4jvVfFl0a0RnbBowqK2C+zQVUWYTcjo
-KHREVje65goj6VzRB6ko/9mAQ6PZP8jRuRhfCmvmvSQ/mWdgPzSRsUh9MwgEm9c2
-vNbqwaznEU8cYZnLpHiR9O5S7/qWWxehjYtxk5Eb4J006YglYfHnhrRFJvPbiqlf
-IOEivZ7gIVfvaOTbLjmN2kLOnzdlwpXGjxxg4Nu9ZhXOhfrplzUvRfmqvVsDiHXb
-USIdX+OFZZqr64IKG4drT4K4Bt2wupOEyX4ZFsUXXd+Hgq83SWmV4wzflcpmGkLC
-JZ3CEMu8/WA5uQBXdQUozlE=
+AArYO305zkNZc+vdbeXNpgi1/FrOHl2b0DvG4i2gcUVDvvjIHUnVLf2cak+81vER
+CqlCkutXxB+/fyxVXYtLKXQh0D/68QikH+ImEavrUrANXvQRvoixIjrfub/nZB75
+EiOTIR2N0/m6ndjCHJU0W8N7Tt9qob31e3bVJBZOc+9e80y8GDQiMKYACmet9zUR
+hR/yvJhUsH/u5Y7OwrvcyCs+PJXzPnZTSsatSkHI4KY22+7K+ZhscLIaBKjqlIDj
++fHBrutW9jtog1E3JUO9ZHokB1qlRLs4YhpQ8YzHRabEBaX892ZPLNwtmFLOWK1p
+9klR7/RXnu13nStNIYAHk20=
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/root.crl b/src/test/ssl/ssl/root.crl
index e879cf25a7..8cca69ba40 100644
--- a/src/test/ssl/ssl/root.crl
+++ b/src/test/ssl/ssl/root.crl
@@ -1,11 +1,11 @@
-----BEGIN X509 CRL-----
MIIBhTBvMA0GCSqGSIb3DQEBCwUAMEAxPjA8BgNVBAMMNVRlc3Qgcm9vdCBDQSBm
-b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0xODExMjcx
-MzQwNTVaFw00NjA0MTQxMzQwNTVaMA0GCSqGSIb3DQEBCwUAA4IBAQB8OSDym4/a
-qbZOrZvOOhmKrd7AJSTgAadtdK0CX3v58Ym3EmZK7gQFdBuFCXnvbue/x6avZHgz
-4pYFlJmL0IiD4QuTzsoo+LzifrmTzteO9oEJNLd2bjfEnpE5Wdaw6Yuy2Xb5edy5
-lQhNZdc8w3FiXhPOEUAi7EbdfDwn4G/fvEjpzyVb2wCujDUUePUGGayjKIM4PUu4
-pixM6gt9FFL27l47lQ3g0PbvB3TnU3oqcB3Y17FjbxjFc6AsGXholNetoEE2/49E
-PEYzOH7/PtxlZUtoCqZM+741LuI6Q7z4/P2X/IY33lMy6Iiyc41C94l/P7fCkMLG
-AlO+O0a4SpYS
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0yMDA4MzEw
+NjA3MjNaFw00ODAxMTcwNjA3MjNaMA0GCSqGSIb3DQEBCwUAA4IBAQB/pWC8moSA
+DL36OWwOig7/yEmbVdpFk2mPmAullbH0A6I/lWkrSrKbb0wk8tE04N2Pw0WDvtqd
+aWq5bx1mL1ve0su2SUkuM5Tb26ZVakI3WDCdDmJIEXAE32g6gmdnAZqaaK2Sy58y
+fEvCTe8Zxe0+8eHNv0VYfhl1ALUwMoO/VrVd453O1FWfXBopm+kkHPB6BDyo9ZbN
+PzLHGnGAP2L+1Ps5TP1AMk2ZEZ87QysB4G2qm1x4A6R4Jf6XBOC5y4YI+vsncUZ2
+UCajgatsFN6LZVHO+q1h5LhsVpSCs9yX8gjaoenJtOrY6XFis1DNHrm0QFSN/yNu
+Muuf3P8i5zl0
-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/root_ca.crt b/src/test/ssl/ssl/root_ca.crt
index 402d7dab89..92000763a9 100644
--- a/src/test/ssl/ssl/root_ca.crt
+++ b/src/test/ssl/ssl/root_ca.crt
@@ -1,8 +1,8 @@
-----BEGIN CERTIFICATE-----
-MIIDHjCCAgagAwIBAgIUEAgXJ/ibw6TVTUoomlBsPMfVTlMwDQYJKoZIhvcNAQEL
+MIIDHjCCAgagAwIBAgIUbDho+LuL8GrZ/iVFY16vdG6kNo0wDQYJKoZIhvcNAQEL
BQAwQDE+MDwGA1UEAww1VGVzdCByb290IENBIGZvciBQb3N0Z3JlU1FMIFNTTCBy
-ZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcNMTgxMTI3MTM0MDU0WhcNNDYwNDE0MTM0
-MDU0WjBAMT4wPAYDVQQDDDVUZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NM
+ZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcNMjAwODMxMDYwNzIyWhcNNDgwMTE3MDYw
+NzIyWjBAMT4wPAYDVQQDDDVUZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NM
IHJlZ3Jlc3Npb24gdGVzdCBzdWl0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALZ81vKKBJlxgjwuNoK67I4IE9zfSLb0eHbgZwZxDVzdmFejARrHlWk3
+MK7Nav7RLSJ990am33zb58CTHc7YYVlBp07+PwLXzypqWkhYfok1OYYjyjCrFDs
@@ -10,10 +10,10 @@ sjcJI3hRCZNEz+wYsG+tdYWJ+gRPQOWfh0YfO2rFgXAIMLiF6lyWzf1eOM+OjYrF
/eyzwbMaJkkGa/AyZKz3wZiPq0jTuYLVmH4MK7MBOsUfSmsBsn/ohyRCQzM+ol0v
Qlsrulj8usponRPDh9ng4PB5OSgR79YimQZnASQzJxiUvMADrKL5L6KwLxJlzbqY
R0b5mLh8KBzBQmSh3Aj2e2I7Z17hdaMCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zAN
-BgkqhkiG9w0BAQsFAAOCAQEASzA7ApbuKn8lkC706gRL37a33yTZZ8rjZ4dnvCtq
-6OltlYDJ8IndotKbLH0SUEAxrvcaFnMt7AX9pRf2sGBKbY8zcxqPfsvzVehgx4Ea
-1RYksFW4h97jj1a1RKukTKuEOEEipbxwo0rLxfjvdaf2izqchJsLGtbocIZf0bD8
-djbE9jOLkx7saL08qC8ECrf9utsee+LJCsUYbNgYyIItEy6yVnmF/ICz93Utn1cI
-RfqZr1lM2Ia2LP9eDTmiuR9m+/MzkeRvvJHonNrRJHlcggtYHICvYioh9/jALBcm
-wZ+hTUePVqy4hOCBJ975CXjfKFN4MKQAdeB3EO5eBYAD3Q==
+BgkqhkiG9w0BAQsFAAOCAQEAo5siIstgYy1MjyVaSHpxy6Cq7JT73RhYYudtAy8e
+XwVXqy3xuZtl+i2XaWnsojyHRZ83t+1rVCoesbZefyfz9d6qZJkCkWdKdjiVEu+s
+CJSo+tGyCZQ1ZTb+07WY01P97gfswP+VHN4EjxJmUnLdqJEJsdOckvL1mBfortt6
+CvL7adPwftppNivF64R0QZfOsLIaaOa0oi33DQDDNUdrx5f3xWwYmzw+f95lvbYR
+DXCytT647nc44Gu09erE4sWipIknFBvg3P1D0icFSIoEh44mU9o1lHUFsogXa09g
+8Vkho/rudYrHi/cwwzQlUjZyHPg6k1iU9oZGEaW5gVKxSw==
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-cn-and-alt-names.crt b/src/test/ssl/ssl/server-cn-and-alt-names.crt
index 2bb206e413..406d50dbca 100644
--- a/src/test/ssl/ssl/server-cn-and-alt-names.crt
+++ b/src/test/ssl/ssl/server-cn-and-alt-names.crt
@@ -1,7 +1,7 @@
-----BEGIN CERTIFICATE-----
MIIDTjCCAjagAwIBAgIBATANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0
IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNl
-cnRzMB4XDTE4MTEyNzEzNDA1NFoXDTQ2MDQxNDEzNDA1NFowRjEeMBwGA1UECwwV
+cnRzMB4XDTIwMDgzMTA2MDcyM1oXDTQ4MDExNzA2MDcyM1owRjEeMBwGA1UECwwV
UG9zdGdyZVNRTCB0ZXN0IHN1aXRlMSQwIgYDVQQDDBtjb21tb24tbmFtZS5wZy1z
c2x0ZXN0LnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBURL6
YPWJjVQEZY0uy4HEaTI4ZMjVf+xdwJRntos4aRcdhq2JRNwitI00BLnIK9ur8D8L
@@ -11,10 +11,10 @@ YsWwHgcuEIZk3z287hxuyU3j5isYoRwd5cZZFrG/qBJdukSBRil5/PP5AHsB6lTl
pae2bdf4TXB7kActIpyTBR0G5Pm5iCZlxgD+QILj/1FLTaNOW7hV+t1J6YfC6jZR
Dk4MnHMCFasSXcXhAgMBAAGjSzBJMEcGA1UdEQRAMD6CHWRuczEuYWx0LW5hbWUu
cGctc3NsdGVzdC50ZXN0gh1kbnMyLmFsdC1uYW1lLnBnLXNzbHRlc3QudGVzdDAN
-BgkqhkiG9w0BAQsFAAOCAQEAQeKHXoyipubldf4HUDCXrcIk6KiEs9DMH1DXRk7L
-z2Hr0TljmKoGG5P6OrF4eP82bhXZlQmwHVclB7Pfo5DvoMYmYjSHxcEAeyJ7etxb
-pV11yEkMkCbQpBVtdMqyTpckXM49GTwqD9US5p1E350snq4Duj3O7fSpE4HMfSd8
-dCkYdaCHq2NWH4/MfEBRy096oOIFxqgm6tRCU95ZI8KeeK4WwPXiGV2mb2rHj1kv
-uBRC+sJGSnsLdbZzkpdAN1qnWrJoLezAMdhTmNRUJ7Cq8hAkroFIp+LE+JyxR9Nw
-m6jD3eEwCAQi0pPGLEn4Rq4B8kxzL5F/jTq7PONRvOAdIg==
+BgkqhkiG9w0BAQsFAAOCAQEAdXbTpv6xPsy7YMB5AWwmV50aWJ1J7cNSF2mEhQxK
+LNYQi5Z1Ov/MuWxCYbW5EeMQlSS/XJbBnFJR8dFgUXd36uQoe8jHDjf5ceG/CeVb
+AFCvMu2dgbA/PisONnlJJ5jL3eEHA0hIg7EvBzPA0Y2GseeZfTECPrXYOF8kJHyN
+cQjsLsOJuhkeKXsvpASlAuRx+e/7FebXw2Ak/9tMo2TekiFokuVymhcZbH4YrcGO
+dT60+cMm8+Cd9to/uatbF/f0LOKFgQ8LNDb+ZAytJ4NxXw7DB6LwAXyXQlPS6iMg
+q7A/owJO3mtuHgy5XGhtKnP/0l9pKlGASykYJNIMmSCNgQ==
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-cn-only.crt b/src/test/ssl/ssl/server-cn-only.crt
index 67bf0b1645..a185b50b0a 100644
--- a/src/test/ssl/ssl/server-cn-only.crt
+++ b/src/test/ssl/ssl/server-cn-only.crt
@@ -1,7 +1,7 @@
-----BEGIN CERTIFICATE-----
MIIC/DCCAeQCAQIwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNlcnZlciBjZXJ0czAe
-Fw0xODExMjcxMzQwNTRaFw00NjA0MTQxMzQwNTRaMEYxHjAcBgNVBAsMFVBvc3Rn
+Fw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEYxHjAcBgNVBAsMFVBvc3Rn
cmVTUUwgdGVzdCBzdWl0ZTEkMCIGA1UEAwwbY29tbW9uLW5hbWUucGctc3NsdGVz
dC50ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1bNU8yTuLl/5
bR4Rp1ET5NMC2wgrTwKQsxSeOzvMmTGeebpEYFc/Hq8rcCQAiL7AbhiZeNg/ca4y
@@ -9,10 +9,10 @@ JdouOdaHaTMFJ8hFDI1tNOGeFK7ecOMBWQ97GxKs/KIqKYW42AN+QZ7l1Apr0CDZ
K5VTi931JjE4wCIgUgLi2zgwtZYl/kP896F1K5zR7kx773U2dvP3SeV2ziUe+4NH
5oqdmVMeZyHfB+Fe/uU1AiYgHN/CXfop39tYHR8ARUWx7eJlaaKBoj/0UqH/9Yir
jdM0vGfrw4JCjIx78caNkNH9fCjesTqODr+IDBJaJv3Jpt9g8puqrYagXLppiaYS
-q5oOAu5fuQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCTxkqpNNuO15osb+Lyw2aQ
-RikYZiSJ4uJcOIya6DqSYSNf8wrgGMJAKz9TkCGEG7SszLCASaSIS/s+sR1+bE19
-f6BxoBnppPW8uIkTtQvmGhhcWHO13zMUs8bmg9OY7MvFYJdQAmSqfYebUCYR73So
-OthALxV8h+boW5/xc2XM1NObpcuShQ9/uynm2dL3EbrNjvcoXOwu865FmVMffEn9
-+zhE8xl4kMKObQvB3r2utCmlAmJLaU2ejADncS9Y4ZwRMa7x+vfvekF/FvLEXUal
-QcDlfrZ0xsw/HK7n0/UFXf5fUXq3hgUGcXEdWW7/yTA43qNxednfa+fMqlFztupt
+q5oOAu5fuQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB8TNuHgAscHJWExsswCCFX
+qfKjpnF/SKD5DGSj+NKfI2CGxWg4X9D67V470OkgHtQqujKb0sIOrbXz2tCg0Z6u
+rbeNctGXKATCawae1nmlz81xBV5cIjlB2mNMQLY0c0zjWozyEq8kEk6bDZ7Nj3bZ
+bf7QK9xdBfWXRhXWSfk45KasxZU/MN+sdIu/xFyBwSEtqVQsfbBK7kOOmDG2SU48
+MA4km6tPVf86BHQshu8vDkB2zWAdECkMVKudkdPT9sT3u26FSGmboXnWNOlfR7TP
+G9BRh+r0zOntkGhJsqUZcEdoOIShKy+69ly2QP7K78EaWvw5Q2ZUqekAvaA7McPb
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-crldir/a836cc2d.r0 b/src/test/ssl/ssl/server-crldir/a836cc2d.r0
new file mode 100644
index 0000000000..9588d1e524
--- /dev/null
+++ b/src/test/ssl/ssl/server-crldir/a836cc2d.r0
@@ -0,0 +1,11 @@
+-----BEGIN X509 CRL-----
+MIIBnjCBhzANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRzFw0yMDA4
+MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBQwEgIBBhcNMjAwODMxMDYwNzIzWjAN
+BgkqhkiG9w0BAQsFAAOCAQEA2CBoLuLCpXcVSHqQtKnTcz25FyPsGkSO3luiUiG5
+jnI9HvZ9OzeQGGho6XBhVHAmEtwKOo6pcxqDe8Qkf6X7v0AUc19BWkxOXT+sCCdM
+yOvRhNPAhxJToGgKqp41noTSMhZPLsvFEfbbFTZEABScfX2K+XdvQlAYXa3U375E
+71jFenrNh8fNTKfcikmio1rsybQ4PG/ASsrUflQ5LAz3Nm3awdw01auGzRFezq3z
+Ivnq3z5b2q+m653PUsygNRMFVxCAgrvqAadKci7MK/QthCbocrLIhuc9Mmx1Nbkm
+Wnv+La3b5HeH8Zi9Q89IBr12rH70Y6K3hggCUAo9CoK3zw==
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/server-multiple-alt-names.crt b/src/test/ssl/ssl/server-multiple-alt-names.crt
index 158153d10c..3a392bc7bc 100644
--- a/src/test/ssl/ssl/server-multiple-alt-names.crt
+++ b/src/test/ssl/ssl/server-multiple-alt-names.crt
@@ -1,7 +1,7 @@
-----BEGIN CERTIFICATE-----
MIIDRDCCAiygAwIBAgIBBDANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0
IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNl
-cnRzMB4XDTE4MTEyNzEzNDA1NFoXDTQ2MDQxNDEzNDA1NFowIDEeMBwGA1UECwwV
+cnRzMB4XDTIwMDgzMTA2MDcyM1oXDTQ4MDExNzA2MDcyM1owIDEeMBwGA1UECwwV
UG9zdGdyZVNRTCB0ZXN0IHN1aXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA10iQpfVf4nCqjkRcLXP9ONQqdhMPMdjHasKqmsFTx83SZLKUzKMOb56j
3bg83stqGoId4MIxtqnDKaSg1+kseQ1HCi0cu/E3lHLEkPibl9dyVGhXVnPDBdOp
@@ -11,10 +11,10 @@ YXOKjP4+fWr18HdZLrzsa4xPRa9XcsDNyffjWvQTfptR/2vFKN8Ffv3XUqxUx6av
VCyRKa8xAUS/pHVGnzFQY+oqWREn2wIDAQABo2cwZTBjBgNVHREEXDBagh1kbnMx
LmFsdC1uYW1lLnBnLXNzbHRlc3QudGVzdIIdZG5zMi5hbHQtbmFtZS5wZy1zc2x0
ZXN0LnRlc3SCGioud2lsZGNhcmQucGctc3NsdGVzdC50ZXN0MA0GCSqGSIb3DQEB
-CwUAA4IBAQAuV+4TNADB/AinkjQVyzPtmeWDWZJDByRSGIzGlrYtzzrJzdRkohlL
-svZi0QQWbYq3pkRoUncYXXp/JvS48Ft1jRi87RpLRiPRxJC9Eq77kMS5UKCIs86W
-0nuYQ2tNmgHb7gnLHEr2t9gFEXcLwUAnRfNJK56KVmCl/v3/4kcVDLlL6L+pL80r
-WGKGvixNy3bDCJ/YGJu6NH+H7NMlqFcg07nEWUHgMzETGGycTcPy3S6mY5P/1Ep1
-MCSTucUKoBIte2t5p9vM6bsFIioQDAYABhacmC62Z5xNW8evmNVtBDPLR1THsWhq
-UjzdIzjpDgv1KUCVEPc1uVrZ5eju+aoU
+CwUAA4IBAQBORmS+8OIlqJVyquIl4El7OHuC4f2e4s0/uLnEMgIzuXFyi1E7XgXr
+vqHFNIux6/jFnkgT1kvR6I2yZc2UTmU88cjGp2nLb4qglWN0TQonBpm3Ibd3q6Zk
+h2/Z3z2d0CVZKVeKwmX6sWDx3QBmalLTI9UfmnF+3PM7NXWAEyh+HAUAsQFnq7g0
+hAGZnAcGTpVMPDgw6RPwS0LyRW7+pGUWqunoz5ORgC4kPlS/xDlmtCXJNp1Elar9
+Pt/ovjkHyNMMIQV2HgWqnTtfqUoFQsAejFvVmNHVRBrJwi5+tG10f1UI4ST+Ky+I
+4ECOGan/YOKxWzXMy6B2QInFAcaTflQQ
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-no-names.crt b/src/test/ssl/ssl/server-no-names.crt
index 97e11176f6..6682d9f25e 100644
--- a/src/test/ssl/ssl/server-no-names.crt
+++ b/src/test/ssl/ssl/server-no-names.crt
@@ -1,18 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC1jCCAb4CAQUwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNlcnZlciBjZXJ0czAe
-Fw0xODExMjcxMzQwNTRaFw00NjA0MTQxMzQwNTRaMCAxHjAcBgNVBAsMFVBvc3Rn
+Fw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMCAxHjAcBgNVBAsMFVBvc3Rn
cmVTUUwgdGVzdCBzdWl0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AJ85/vh52iDZAeQmWt47o0kR7VVlRLGf4sF4cfxPl+eIWIzhib1fajdcqFiy81LC
+t9bAyRqMyR3dve9RGK6IDFjMH/0DBf3tFSRnxN+5TdSAhKIJslmtUdOl8kS/smF
4+BikCg509aq0U+ac79e/q42OyvH9X/cI6i9SPd4hzJDMCX54LZT1Of/90nSQX6E
Oc5Hcj/d7psBugmMBW8uXYAGvJpq14e5RoK78F/mYbUNqtc1c8pi4/4quSMeEfQp
Dgmzee7ts8SIbQT8mYJHjnaPvZYpv4+Ikc7F0wzLO1neTpsYaVvDrSMLBCdQkCU8
-vgb1T6WlVgbp/sfE5okSxx8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAh+erODlf
-+mEK2toZhaAmikNJ3Toj9P7I0C0Mo/tl2aVz8jQc/ft5t3blwZHfRzC9WmgnZLdY
-yiCVgUlf9Kwhi836Btbczj3cK6MrngQneFBzSnCzsj40CuQAw5TOI8XRFFGL+fpl
-o7ZnbmMZRhkPqDmNWXfpu7CYOFQyExkDoo0lTfqM+tF8zuKVTmsuWWvZpjuvqWFQ
-/L+XRXi0cvhh+DY9vJiKNRg4exF7/tSedTJmLA8skuaXgAVez4rqzX4k1XnQo6Vi
-YpAIQ4dGiijY24fDq2I/6pO3xlWtN+Lwu44Mnn2vWRtXijT69P5R12W8XS7+ciTU
-NXu/iOo8f7mNDA==
+vgb1T6WlVgbp/sfE5okSxx8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAvRq8eCJl
+gAu2LT21OKmuhiMLPWyNVbyHo+A8UOKBXo1WwRbU4W0u2Ki5LenriekpW2A4qiZ1
+HDxpLaSquSIzPwtDvnMqtQ4BDEaikwmGxEl5EIR2+75riV9BNFgA0g+zVTPN+I33
+/OdNbI9XvIVkyOfxgaoE9kcWF6wRLB70oOYtuInUajEuG8GEKR5vrWXPa6sZoUxh
+ziGM/FHSNs3XqLDJxcUx7FMJH7iz9IlhJVN4aEEYX/L3cVnIWCnlNYp1UMHBTBqD
+aaGVTS7I8cIqOT1I0MxRqKBnTDXgfKb6yHYCgdQUzuFH3BcM08D7G6iuH6DCL3ib
+w5kP06Ufd7oOlA==
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-revoked.crt b/src/test/ssl/ssl/server-revoked.crt
index 1ca5e6d3ef..2fa1a6ea71 100644
--- a/src/test/ssl/ssl/server-revoked.crt
+++ b/src/test/ssl/ssl/server-revoked.crt
@@ -1,7 +1,7 @@
-----BEGIN CERTIFICATE-----
MIIC/DCCAeQCAQYwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNlcnZlciBjZXJ0czAe
-Fw0xODExMjcxMzQwNTRaFw00NjA0MTQxMzQwNTRaMEYxHjAcBgNVBAsMFVBvc3Rn
+Fw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEYxHjAcBgNVBAsMFVBvc3Rn
cmVTUUwgdGVzdCBzdWl0ZTEkMCIGA1UEAwwbY29tbW9uLW5hbWUucGctc3NsdGVz
dC50ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAooPO8lSz434p
4PBYBbTN8jkLW3cHEpTCH4yvC0V40hzGEl30HPLp82e+kxr+Q0+gd82fvc4Yth5I
@@ -9,10 +9,10 @@ PKINznp28GMs5/E9cUU3hMK4jFhKLMiOeIve3M/9ryHK874qpNjJoSxxPz7+s2eq
WoFc2px0KFIamTTLfi7Ju9aPb/AMlZNsUnbRsj7fQc7EJ8rwOnezw2Wy5VK4soX+
qpuJ0Nm44ApzT8YmjYX/kAX0yQxgQuYbpcBWr9cOQjegu3FAqHqRh9ye7d8jQzCv
34Wg/ar4rkqyQDcokuWAE7KQbnk51t7omzhM8eswFOAL1pas/8jWBvy0VjYVU34P
-9aXxP8GiHQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAS9abT/PhJgwAnm46Rzu16
-lL7tDb3SeR1RL25xZLzCexHcYJFi7aDZix3QlLRvf6HPqqUPuPYRICTBF4+fieEh
-r5LotdAnadfYONwoB5GiYy2d93VGqlLosI27R6/tVvImXupviPpIYMDgBBRr1pZc
-ykQOjog6T+xk9TqsfFQDe2/VKF7a5RxOA/V77GZ5qge5Nlx9jSXQ/WUG9vDQj9BA
-d4nOwvjauKlcSqUU/3uVKntXQTNjmyq7S75eBitS920LLfjTL9LInLugDikFa/J/
-yPBkJLa/+rNMPikcnF3ci4Oi/XwLA8kGdGZAADuiIOeyORMuLFoTk7KpOYGKS5/U
+9aXxP8GiHQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQANC+ABgpTg8mHipdTBhhUH
+klkvnmPwzUyfTMfjAMpM/aMXY12R2pcKbDm7uSFZQPyL4w2W6sa56rbFzXLER9U1
+woOdlUfpREtISaC+wI+plhPLvERW9Id57IWNuOpPaAP2KWOVa9gtRVIBj/Z09+3w
+nB3aqHxCl7GKI78ruqR8VGAhSl58KAVzG7TS25848nYIijZ4Aeoqr99x6EuHAVhf
+47xShRQTQZTzANaXqMaqeR4UoPxb5GUt1I2+4Q9Q0FC3M4CVgCbxgaKqmNms88k9
+yrXx+BA5fDXMhcyX/R09t+aPBnKhC+dmLohmB9cV0jgQLAGaN81+ZXyyghXk3iCV
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-single-alt-name.crt b/src/test/ssl/ssl/server-single-alt-name.crt
index e7403f3a6b..6637fa467b 100644
--- a/src/test/ssl/ssl/server-single-alt-name.crt
+++ b/src/test/ssl/ssl/server-single-alt-name.crt
@@ -1,7 +1,7 @@
-----BEGIN CERTIFICATE-----
MIIDCzCCAfOgAwIBAgIBAzANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0
IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNl
-cnRzMB4XDTE4MTEyNzEzNDA1NFoXDTQ2MDQxNDEzNDA1NFowIDEeMBwGA1UECwwV
+cnRzMB4XDTIwMDgzMTA2MDcyM1oXDTQ4MDExNzA2MDcyM1owIDEeMBwGA1UECwwV
UG9zdGdyZVNRTCB0ZXN0IHN1aXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAxYocLWWuiDsDzJ7wLc0zfwkGJAEy4hlHjTA5GXSEnGPlOnx1fxejZOGL
1HLff5h8zB+SQXrplHCcwwRrxVgGY7P59kXMXX1akTwXUJHc/EoTtqLO+6fHLygz
@@ -9,11 +9,11 @@ F1d0i5NPO3xrk1wMt7bYLhiPbWpplWiHXzbJy8wf3dXgzCwtxXf8Z1UqjtCnA/Zk
J/kPWuHJxzH5OvDJvZsq+Fbkl3catFpwUlAV9TKsC78W/K5I+afzppsmSvsIKAWW
Dp7g71IVjvJeI6Aui2yhDn9iuJMuKe9RMYIwJLFqiX3urHcjaBSkJm6Lsf7gO30v
kVwIyyGXRNTfZ2yPDoSXVZvOnq+gKwIDAQABoy4wLDAqBgNVHREEIzAhgh9zaW5n
-bGUuYWx0LW5hbWUucGctc3NsdGVzdC50ZXN0MA0GCSqGSIb3DQEBCwUAA4IBAQBU
-8wp8KZfS8vClx2gYSRlbXu3J1oAu4EBh45OuuRuLOJUhQZYcjFB3d/s0R1kcCQkB
-EekV9X1iQSzk/HQq4uWi6ViUzxTR67Q6TXEFo8iuqJ6Rag7R7G6fhRD1upf1lev+
-rz7F9GsoWLyLAg8//DUfq1kfQUyy6TxamoRs0vipZ4s0p4G8rbRCxKT1WTRLJFdd
-fSDVuMNuQQKTQXNdp6cYn+ikEhbUv/gG2S7Xiy2UM8oR7DR54nZBAKxgujWJZPfX
-/ieSwLxnLFyePwtwgk9xMmywFBjHWTxSdyI1UnJwWC917BSw4M00djsRv5COsBX7
-v/Co7oiMyTrCqyCsWOBu
+bGUuYWx0LW5hbWUucGctc3NsdGVzdC50ZXN0MA0GCSqGSIb3DQEBCwUAA4IBAQBP
+tJo8pTdcrsvooz15feSdJpxxmUut7/ub4ddRZQj08jL7SrUtAfmiUFBqaR618lr7
++7L30XkVv4j0p6U5jaE6V0L/r/GH6XyFLoH7ygTa4mmSrK8BWU1h2PvcqswxbxBY
+5Bu7pTajip2JuQ+6+rKfEvchGsELtWc1526QIa3LFsHTL8eWCFny16K7zlMkfFB1
+w58suL8ucTWfEcRByKkLD7wcZpAFvQD80BU7TvErr4ydEiNfH5NwrrUzycracPnc
+WKTjbAkRO615ht1z5E/TTLXENPlmibu08/g+Y8wu61S2Bjhn5LM33zKb/OrpVraY
+vVEKKU2NkD8bb+ztzu/H
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-ss.crt b/src/test/ssl/ssl/server-ss.crt
index d775e6029a..6662afe3af 100644
--- a/src/test/ssl/ssl/server-ss.crt
+++ b/src/test/ssl/ssl/server-ss.crt
@@ -1,8 +1,8 @@
-----BEGIN CERTIFICATE-----
-MIIDGDCCAgCgAwIBAgIUVR71MjsbvBO6T1gJQaL/6hMwhqQwDQYJKoZIhvcNAQEL
+MIIDGDCCAgCgAwIBAgIUby7HZZ6t7KHCu15JC/MVcj8VEYcwDQYJKoZIhvcNAQEL
BQAwRjEkMCIGA1UEAwwbY29tbW9uLW5hbWUucGctc3NsdGVzdC50ZXN0MR4wHAYD
-VQQLDBVQb3N0Z3JlU1FMIHRlc3Qgc3VpdGUwHhcNMTgxMTI3MTM0MDU0WhcNNDYw
-NDE0MTM0MDU0WjBGMSQwIgYDVQQDDBtjb21tb24tbmFtZS5wZy1zc2x0ZXN0LnRl
+VQQLDBVQb3N0Z3JlU1FMIHRlc3Qgc3VpdGUwHhcNMjAwODMxMDYwNzIzWhcNNDgw
+MTE3MDYwNzIzWjBGMSQwIgYDVQQDDBtjb21tb24tbmFtZS5wZy1zc2x0ZXN0LnRl
c3QxHjAcBgNVBAsMFVBvc3RncmVTUUwgdGVzdCBzdWl0ZTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBANWzVPMk7i5f+W0eEadRE+TTAtsIK08CkLMUnjs7
zJkxnnm6RGBXPx6vK3AkAIi+wG4YmXjYP3GuMiXaLjnWh2kzBSfIRQyNbTThnhSu
@@ -10,10 +10,10 @@ zJkxnnm6RGBXPx6vK3AkAIi+wG4YmXjYP3GuMiXaLjnWh2kzBSfIRQyNbTThnhSu
Jf5D/PehdSuc0e5Me+91Nnbz90nlds4lHvuDR+aKnZlTHmch3wfhXv7lNQImIBzf
wl36Kd/bWB0fAEVFse3iZWmigaI/9FKh//WIq43TNLxn68OCQoyMe/HGjZDR/Xwo
3rE6jg6/iAwSWib9yabfYPKbqq2GoFy6aYmmEquaDgLuX7kCAwEAATANBgkqhkiG
-9w0BAQsFAAOCAQEAtHR6o4UIO/aWEAzcmnJKsQDC999jbQGiqs9+v62mz5TvCk/1
-gL9/s/yfGY+pnDGW1ijI2xiL9KCJzjd8YB+F8iUViVQ6uHBxghxC1H2qOIr2UPFQ
-gQRu7d0DByQBsiXMOdw10luGo1oHhqMe5J7VyMVG/7aRpr6zYKrH7PzsB8ucvxzv
-Lm8ez0WBPebV69sim431iJcVcxxBbFd4qUJ9cHIc7VO2mSaazsIOzbd400POF/vk
-gfpDs48GfnZ+X3hgoQA4u7eudLqttI+j1xV+IHlCtaa1nDHymUrN/FhI1x+6c1SU
-V12eHqVatPMe0d+OCJPqIL9lbe+sGXlxDkMqAQ==
+9w0BAQsFAAOCAQEAO68c0amf+x5U7o6nZfNcwwMEY3wPt/NYWnfwp0+/5R50KY2L
+ZKqKxN26BQPFID2j/H84ve5idcJoilzy3W4/P5hs7R53sTyID/fFz6xB7p3eQnJO
+I6YT8D+dcNnipjK3O0O4Htqq7L25idkmYM8HxeSVWC65MzbUI9nLzqg4FRv3pM+m
+AM9Cpq41j8mhN3NS2vhpgy9T6qrM8v0usJuoAMMnwp0yXo3/ZfpoT80BaGhlWR5g
+Wm36rA50Z0Vz1zgRJb/xXl9SEnySWAM/WIuRiAHRw9J5K3ye8U8aW+xV3/uQEtG2
+s7h6mW3YqdIh2o5Gc83rLEvPOHLFohKMvFmJTA==
-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server.crl b/src/test/ssl/ssl/server.crl
index 717951c26a..9588d1e524 100644
--- a/src/test/ssl/ssl/server.crl
+++ b/src/test/ssl/ssl/server.crl
@@ -1,11 +1,11 @@
-----BEGIN X509 CRL-----
MIIBnjCBhzANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
-b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRzFw0xODEx
-MjcxMzQwNTVaFw00NjA0MTQxMzQwNTVaMBQwEgIBBhcNMTgxMTI3MTM0MDU1WjAN
-BgkqhkiG9w0BAQsFAAOCAQEAbVuJXemxM6HLlIHGWlQvVmsmG4ZTQWiDnZjfmrND
-xB4XsvZNPXnFkjdBENDROrbDRwm60SJDW73AbDbfq1IXAzSpuEyuRz61IyYKo0wq
-nmObJtVdIu3bVlWIlDXaP5Emk3d7ouCj5f8Kyeb8gm4pL3N6e0eI63hCaS39hhE6
-RLGh9HU9ht1kKfgcTwmB5b2HTPb4M6z1AmSIaMVqZTjIspsUgNF2+GBm3fOnOaiZ
-SEXWtgjMRXiIHbtU0va3LhSH5OSW0mh+L9oGUQDYnyuudnWGpulhqIp4qVkJRDDu
-41HpD83dV2uRtBLvc25AFHj7kXBflbO3gvGZVPYf1zVghQ==
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRzFw0yMDA4
+MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMBQwEgIBBhcNMjAwODMxMDYwNzIzWjAN
+BgkqhkiG9w0BAQsFAAOCAQEA2CBoLuLCpXcVSHqQtKnTcz25FyPsGkSO3luiUiG5
+jnI9HvZ9OzeQGGho6XBhVHAmEtwKOo6pcxqDe8Qkf6X7v0AUc19BWkxOXT+sCCdM
+yOvRhNPAhxJToGgKqp41noTSMhZPLsvFEfbbFTZEABScfX2K+XdvQlAYXa3U375E
+71jFenrNh8fNTKfcikmio1rsybQ4PG/ASsrUflQ5LAz3Nm3awdw01auGzRFezq3z
+Ivnq3z5b2q+m653PUsygNRMFVxCAgrvqAadKci7MK/QthCbocrLIhuc9Mmx1Nbkm
+Wnv+La3b5HeH8Zi9Q89IBr12rH70Y6K3hggCUAo9CoK3zw==
-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/server_ca.crt b/src/test/ssl/ssl/server_ca.crt
index 9f727bf9e9..94da6cd092 100644
--- a/src/test/ssl/ssl/server_ca.crt
+++ b/src/test/ssl/ssl/server_ca.crt
@@ -1,7 +1,7 @@
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIBATANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVUZXN0
IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzdWl0
-ZTAeFw0xODExMjcxMzQwNTRaFw00NjA0MTQxMzQwNTRaMEIxQDA+BgNVBAMMN1Rl
+ZTAeFw0yMDA4MzEwNjA3MjNaFw00ODAxMTcwNjA3MjNaMEIxQDA+BgNVBAMMN1Rl
c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzZXJ2ZXIg
Y2VydHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiSnYZbmc9vpCt
Ku1sKV9l663JCceubhMw8Gg16kV0hXEFf/TgGC4zkiYNHN7+G45YD7Nq0kBCq3dH
@@ -10,10 +10,10 @@ t2wPCc6c8pQoI64dfprVqPkvzoe1WBpZNetkUTk20v08jNeRa7XdRbRR6we1s9VG
QW9YWdH9N5ctaUXMG6lLV2OAjs+W1smpKfpIpMCA1lPGlElu70hynon/nQQvBP77
SfQpZVc0esM18jkZpr5LEKUCw+x6LaMsqmBHpAULfCffxn2r0uMBW4L4VaGg3W6F
h6iuJwRfAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
-AFlcKTaU/Ug3Q0hr3P1UQ6dWyK4aVn9rs4jvVfFl0a0RnbBowqK2C+zQVUWYTcjo
-KHREVje65goj6VzRB6ko/9mAQ6PZP8jRuRhfCmvmvSQ/mWdgPzSRsUh9MwgEm9c2
-vNbqwaznEU8cYZnLpHiR9O5S7/qWWxehjYtxk5Eb4J006YglYfHnhrRFJvPbiqlf
-IOEivZ7gIVfvaOTbLjmN2kLOnzdlwpXGjxxg4Nu9ZhXOhfrplzUvRfmqvVsDiHXb
-USIdX+OFZZqr64IKG4drT4K4Bt2wupOEyX4ZFsUXXd+Hgq83SWmV4wzflcpmGkLC
-JZ3CEMu8/WA5uQBXdQUozlE=
+AArYO305zkNZc+vdbeXNpgi1/FrOHl2b0DvG4i2gcUVDvvjIHUnVLf2cak+81vER
+CqlCkutXxB+/fyxVXYtLKXQh0D/68QikH+ImEavrUrANXvQRvoixIjrfub/nZB75
+EiOTIR2N0/m6ndjCHJU0W8N7Tt9qob31e3bVJBZOc+9e80y8GDQiMKYACmet9zUR
+hR/yvJhUsH/u5Y7OwrvcyCs+PJXzPnZTSsatSkHI4KY22+7K+ZhscLIaBKjqlIDj
++fHBrutW9jtog1E3JUO9ZHokB1qlRLs4YhpQ8YzHRabEBaX892ZPLNwtmFLOWK1p
+9klR7/RXnu13nStNIYAHk20=
-----END CERTIFICATE-----
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index fd2727b568..4f36bf9dc0 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -13,7 +13,7 @@ use SSLServer;
if ($ENV{with_openssl} eq 'yes')
{
- plan tests => 93;
+ plan tests => 100;
}
else
{
@@ -214,6 +214,12 @@ test_connect_fails(
"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl",
qr/SSL error/,
"CRL belonging to a different CA");
+# The same for CRL directory, fails
+test_connect_fails(
+ $common_connstr,
+ "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrldir=ssl/client-crldir",
+ qr/SSL error/,
+ "directory CRL belonging to a different CA");
# With the correct CRL, succeeds (this cert is not revoked)
test_connect_ok(
@@ -221,6 +227,12 @@ test_connect_ok(
"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl",
"CRL with a non-revoked cert");
+# With the correct server CRL directory, succeeds (this cert is not revoked)
+test_connect_ok(
+ $common_connstr,
+ "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrldir=ssl/root+server-crldir",
+ "directory CRL with a non-revoked cert");
+
# Check that connecting with verify-full fails, when the hostname doesn't
# match the hostname in the server's certificate.
$common_connstr =
@@ -346,7 +358,12 @@ test_connect_fails(
$common_connstr,
"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl",
qr/SSL error/,
- "does not connect with client-side CRL");
+ "does not connect with client-side CRL file");
+test_connect_fails(
+ $common_connstr,
+ "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrldir=ssl/root+server-crldir",
+ qr/SSL error/,
+ "does not connect with client-side CRL directory");
# pg_stat_ssl
command_like(
@@ -545,6 +562,16 @@ test_connect_ok(
test_connect_fails($common_connstr, "sslmode=require sslcert=ssl/client.crt",
qr/SSL error/, "intermediate client certificate is missing");
+# test server-side CRL directory
+switch_server_cert($node, 'server-cn-only', undef, undef, 'root+client-crldir');
+
+# revoked client cert
+test_connect_fails(
+ $common_connstr,
+ "user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked_tmp.key",
+ qr/SSL error/,
+ "certificate authorization fails with revoked client cert with server-side CRL directory");
+
# clean up
foreach my $key (@keys)
{
diff --git a/src/test/ssl/t/SSLServer.pm b/src/test/ssl/t/SSLServer.pm
index f5987a003e..8b23e18497 100644
--- a/src/test/ssl/t/SSLServer.pm
+++ b/src/test/ssl/t/SSLServer.pm
@@ -150,6 +150,8 @@ sub configure_test_server_for_ssl
copy_files("ssl/root+client_ca.crt", $pgdata);
copy_files("ssl/root_ca.crt", $pgdata);
copy_files("ssl/root+client.crl", $pgdata);
+ mkdir("$pgdata/root+client-crldir");
+ copy_files("ssl/root+client-crldir/*", "$pgdata/root+client-crldir/");
# Stop and restart server to load new listen_addresses.
$node->restart;
@@ -167,14 +169,24 @@ sub switch_server_cert
my $node = $_[0];
my $certfile = $_[1];
my $cafile = $_[2] || "root+client_ca";
+ my $crlfile = "root+client.crl";
+ my $crldir;
my $pgdata = $node->data_dir;
+ # defaults to use crl file
+ if (defined $_[3] || defined $_[4])
+ {
+ $crlfile = $_[3];
+ $crldir = $_[4];
+ }
+
open my $sslconf, '>', "$pgdata/sslconfig.conf";
print $sslconf "ssl=on\n";
print $sslconf "ssl_ca_file='$cafile.crt'\n";
print $sslconf "ssl_cert_file='$certfile.crt'\n";
print $sslconf "ssl_key_file='$certfile.key'\n";
- print $sslconf "ssl_crl_file='root+client.crl'\n";
+ print $sslconf "ssl_crl_file='$crlfile'\n" if (defined $crlfile);
+ print $sslconf "ssl_crl_dir='$crldir'\n" if (defined $crldir);
close $sslconf;
$node->restart;
--
2.27.0
----Next_Part(Tue_Jan_19_17_32_00_2021_330)----
^ permalink raw reply [nested|flat] 3+ messages in thread
* [PATCH 1/6] Add REPACK command
@ 2026-01-27 10:48 Antonin Houska <[email protected]>
0 siblings, 0 replies; 3+ messages in thread
From: Antonin Houska @ 2026-01-27 10:48 UTC (permalink / raw)
REPACK absorbs the functionality of VACUUM FULL and CLUSTER in a single
command. Because this functionality is completely different from
regular VACUUM, having it separate from VACUUM makes it easier for users
to understand; as for CLUSTER, the term is heavily overloaded in the
IT world and even in Postgres itself, so it's good that we can avoid it.
This also adds pg_repackdb, a new utility that can invoke the new
commands. This is heavily based on vacuumdb.
Author: Antonin Houska <[email protected]>
Co-authored-by: =C3=81lvaro Herrera <[email protected]>
Reviewed-by: Mihail Nikalayeu <[email protected]>
Reviewed-by: Robert Treat <[email protected]>
Reviewed-by: Euler Taveira <[email protected]>
Reviewed-by: Matheus Alcantara <[email protected]>
Reviewed-by: Junwang Zhao <[email protected]>
Reviewed-by: jian he <[email protected]>
Discussion: https://postgr.es/m/82651.1720540558@antos
Discussion: https://postgr.es/m/[email protected]
---
doc/src/sgml/monitoring.sgml | 223 +++++-
doc/src/sgml/ref/allfiles.sgml | 2 +
doc/src/sgml/ref/cluster.sgml | 97 +--
doc/src/sgml/ref/clusterdb.sgml | 5 +
doc/src/sgml/ref/pg_repackdb.sgml | 488 +++++++++++++
doc/src/sgml/ref/repack.sgml | 328 +++++++++
doc/src/sgml/ref/vacuum.sgml | 33 +-
doc/src/sgml/reference.sgml | 2 +
src/backend/access/heap/heapam_handler.c | 32 +-
src/backend/catalog/index.c | 2 +-
src/backend/catalog/system_views.sql | 29 +-
src/backend/commands/cluster.c | 842 +++++++++++++++--------
src/backend/commands/vacuum.c | 6 +-
src/backend/parser/gram.y | 86 ++-
src/backend/tcop/utility.c | 23 +-
src/backend/utils/adt/pgstatfuncs.c | 4 +-
src/bin/psql/tab-complete.in.c | 42 +-
src/bin/scripts/Makefile | 3 +
src/bin/scripts/meson.build | 2 +
src/bin/scripts/pg_repackdb.c | 240 +++++++
src/bin/scripts/t/103_repackdb.pl | 47 ++
src/bin/scripts/vacuuming.c | 102 ++-
src/bin/scripts/vacuuming.h | 3 +
src/include/commands/cluster.h | 8 +-
src/include/commands/progress.h | 50 +-
src/include/nodes/parsenodes.h | 35 +-
src/include/parser/kwlist.h | 1 +
src/include/tcop/cmdtaglist.h | 1 +
src/include/utils/backend_progress.h | 2 +-
src/test/regress/expected/cluster.out | 134 +++-
src/test/regress/expected/rules.out | 72 +-
src/test/regress/sql/cluster.sql | 70 +-
src/tools/pgindent/typedefs.list | 2 +
33 files changed, 2480 insertions(+), 536 deletions(-)
create mode 100644 doc/src/sgml/ref/pg_repackdb.sgml
create mode 100644 doc/src/sgml/ref/repack.sgml
create mode 100644 src/bin/scripts/pg_repackdb.c
create mode 100644 src/bin/scripts/t/103_repackdb.pl
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index b77d189a500..71c92ed53ef 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -405,6 +405,14 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss =
11:34 0:00 postgres: ser
</entry>
</row>
=20
+ <row>
+ <entry><structname>pg_stat_progress_repack</structname><indexterm><p=
rimary>pg_stat_progress_repack</primary></indexterm></entry>
+ <entry>One row for each backend running
+ <command>REPACK</command>, showing current progress. See
+ <xref linkend=3D"repack-progress-reporting"/>.
+ </entry>
+ </row>
+
<row>
<entry><structname>pg_stat_progress_basebackup</structname><indexter=
m><primary>pg_stat_progress_basebackup</primary></indexterm></entry>
<entry>One row for each WAL sender process streaming a base backup,
@@ -5646,7 +5654,8 @@ FROM pg_stat_get_backend_idset() AS backendid;
certain commands during command execution. Currently, the only commands
which support progress reporting are <command>ANALYZE</command>,
<command>CLUSTER</command>,
- <command>CREATE INDEX</command>, <command>VACUUM</command>,
+ <command>CREATE INDEX</command>, <command>REPACK</command>,
+ <command>VACUUM</command>,
<command>COPY</command>,
and <xref linkend=3D"protocol-replication-base-backup"/> (i.e., replica=
tion
command that <xref linkend=3D"app-pgbasebackup"/> issues to take
@@ -6130,6 +6139,218 @@ FROM pg_stat_get_backend_idset() AS backendid;
</table>
</sect2>
=20
+ <sect2 id=3D"repack-progress-reporting">
+ <title>REPACK Progress Reporting</title>
+
+ <indexterm>
+ <primary>pg_stat_progress_repack</primary>
+ </indexterm>
+
+ <para>
+ Whenever <command>REPACK</command> is running,
+ the <structname>pg_stat_progress_repack</structname> view will contain a
+ row for each backend that is currently running the command. The tables
+ below describe the information that will be reported and provide
+ information about how to interpret it.
+ </para>
+
+ <table id=3D"pg-stat-progress-repack-view" xreflabel=3D"pg_stat_progress=
_repack">
+ <title><structname>pg_stat_progress_repack</structname> View</title>
+ <tgroup cols=3D"1">
+ <thead>
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>pid</structfield> <type>integer</type>
+ </para>
+ <para>
+ Process ID of backend.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>datid</structfield> <type>oid</type>
+ </para>
+ <para>
+ OID of the database to which this backend is connected.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>datname</structfield> <type>name</type>
+ </para>
+ <para>
+ Name of the database to which this backend is connected.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>relid</structfield> <type>oid</type>
+ </para>
+ <para>
+ OID of the table being repacked.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>phase</structfield> <type>text</type>
+ </para>
+ <para>
+ Current processing phase. See <xref linkend=3D"repack-phases"/>.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>repack_index_relid</structfield> <type>oid</type>
+ </para>
+ <para>
+ If the table is being scanned using an index, this is the OID of the
+ index being used; otherwise, it is zero.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>heap_tuples_scanned</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of heap tuples scanned.
+ This counter only advances when the phase is
+ <literal>seq scanning heap</literal>,
+ <literal>index scanning heap</literal>
+ or <literal>writing new heap</literal>.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>heap_tuples_written</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of heap tuples written.
+ This counter only advances when the phase is
+ <literal>seq scanning heap</literal>,
+ <literal>index scanning heap</literal>
+ or <literal>writing new heap</literal>.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>heap_blks_total</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Total number of heap blocks in the table. This number is reported
+ as of the beginning of <literal>seq scanning heap</literal>.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>heap_blks_scanned</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of heap blocks scanned. This counter only advances when the
+ phase is <literal>seq scanning heap</literal>.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role=3D"catalog_table_entry"><para role=3D"column_definition">
+ <structfield>index_rebuild_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of indexes rebuilt. This counter only advances when the pha=
se
+ is <literal>rebuilding index</literal>.
+ </para></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table id=3D"repack-phases">
+ <title>REPACK Phases</title>
+ <tgroup cols=3D"2">
+ <colspec colname=3D"col1" colwidth=3D"1*"/>
+ <colspec colname=3D"col2" colwidth=3D"2*"/>
+ <thead>
+ <row>
+ <entry>Phase</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal>initializing</literal></entry>
+ <entry>
+ The command is preparing to begin scanning the heap. This phase is
+ expected to be very brief.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>seq scanning heap</literal></entry>
+ <entry>
+ The command is currently scanning the table using a sequential scan.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>index scanning heap</literal></entry>
+ <entry>
+ <command>REPACK</command> is currently scanning the table using an =
index scan.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>sorting tuples</literal></entry>
+ <entry>
+ <command>REPACK</command> is currently sorting tuples.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>writing new heap</literal></entry>
+ <entry>
+ <command>REPACK</command> is currently writing the new heap.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>swapping relation files</literal></entry>
+ <entry>
+ The command is currently swapping newly-built files into place.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>rebuilding index</literal></entry>
+ <entry>
+ The command is currently rebuilding an index.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>performing final cleanup</literal></entry>
+ <entry>
+ The command is performing final cleanup. When this phase is
+ completed, <command>REPACK</command> will end.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect2>
+
<sect2 id=3D"copy-progress-reporting">
<title>COPY Progress Reporting</title>
=20
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index e167406c744..5df944d13ca 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -167,6 +167,7 @@ Complete list of usable sgml source files in this direc=
tory.
<!ENTITY refreshMaterializedView SYSTEM "refresh_materialized_view.sgml">
<!ENTITY reindex SYSTEM "reindex.sgml">
<!ENTITY releaseSavepoint SYSTEM "release_savepoint.sgml">
+<!ENTITY repack SYSTEM "repack.sgml">
<!ENTITY reset SYSTEM "reset.sgml">
<!ENTITY revoke SYSTEM "revoke.sgml">
<!ENTITY rollback SYSTEM "rollback.sgml">
@@ -213,6 +214,7 @@ Complete list of usable sgml source files in this direc=
tory.
<!ENTITY pgIsready SYSTEM "pg_isready.sgml">
<!ENTITY pgReceivewal SYSTEM "pg_receivewal.sgml">
<!ENTITY pgRecvlogical SYSTEM "pg_recvlogical.sgml">
+<!ENTITY pgRepackdb SYSTEM "pg_repackdb.sgml">
<!ENTITY pgResetwal SYSTEM "pg_resetwal.sgml">
<!ENTITY pgRestore SYSTEM "pg_restore.sgml">
<!ENTITY pgRewind SYSTEM "pg_rewind.sgml">
diff --git a/doc/src/sgml/ref/cluster.sgml b/doc/src/sgml/ref/cluster.sgml
index 0b47460080b..2cda711bc9f 100644
--- a/doc/src/sgml/ref/cluster.sgml
+++ b/doc/src/sgml/ref/cluster.sgml
@@ -33,51 +33,13 @@ CLUSTER [ ( <replaceable class=3D"parameter">option</re=
placeable> [, ...] ) ] [ <r
<title>Description</title>
=20
<para>
- <command>CLUSTER</command> instructs <productname>PostgreSQL</productna=
me>
- to cluster the table specified
- by <replaceable class=3D"parameter">table_name</replaceable>
- based on the index specified by
- <replaceable class=3D"parameter">index_name</replaceable>. The index mu=
st
- already have been defined on
- <replaceable class=3D"parameter">table_name</replaceable>.
+ The <command>CLUSTER</command> command is equivalent to
+ <xref linkend=3D"sql-repack"/> with an <literal>USING INDEX</literal>
+ clause. See there for more details.
</para>
=20
- <para>
- When a table is clustered, it is physically reordered
- based on the index information. Clustering is a one-time operation:
- when the table is subsequently updated, the changes are
- not clustered. That is, no attempt is made to store new or
- updated rows according to their index order. (If one wishes, one can
- periodically recluster by issuing the command again. Also, setting
- the table's <literal>fillfactor</literal> storage parameter to less than
- 100% can aid in preserving cluster ordering during updates, since updat=
ed
- rows are kept on the same page if enough space is available there.)
- </para>
-
- <para>
- When a table is clustered, <productname>PostgreSQL</productname>
- remembers which index it was clustered by. The form
- <command>CLUSTER <replaceable class=3D"parameter">table_name</replaceab=
le></command>
- reclusters the table using the same index as before. You can also
- use the <literal>CLUSTER</literal> or <literal>SET WITHOUT CLUSTER</lit=
eral>
- forms of <link linkend=3D"sql-altertable"><command>ALTER TABLE</command=
></link> to set the index to be used for
- future cluster operations, or to clear any previous setting.
- </para>
-
- <para>
- <command>CLUSTER</command> without a
- <replaceable class=3D"parameter">table_name</replaceable> reclusters al=
l the
- previously-clustered tables in the current database that the calling us=
er
- has privileges for. This form of <command>CLUSTER</command> cannot be
- executed inside a transaction block.
- </para>
+<!-- Do we need to describe exactly which options map to what? They seem =
obvious to me. -->
=20
- <para>
- When a table is being clustered, an <literal>ACCESS
- EXCLUSIVE</literal> lock is acquired on it. This prevents any other
- database operations (both reads and writes) from operating on the
- table until the <command>CLUSTER</command> is finished.
- </para>
</refsect1>
=20
<refsect1>
@@ -136,63 +98,12 @@ CLUSTER [ ( <replaceable class=3D"parameter">option</r=
eplaceable> [, ...] ) ] [ <r
on the table.
</para>
=20
- <para>
- In cases where you are accessing single rows randomly
- within a table, the actual order of the data in the
- table is unimportant. However, if you tend to access some
- data more than others, and there is an index that groups
- them together, you will benefit from using <command>CLUSTER</command>.
- If you are requesting a range of indexed values from a table, or a
- single indexed value that has multiple rows that match,
- <command>CLUSTER</command> will help because once the index identifies=
the
- table page for the first row that matches, all other rows
- that match are probably already on the same table page,
- and so you save disk accesses and speed up the query.
- </para>
-
- <para>
- <command>CLUSTER</command> can re-sort the table using either an index=
scan
- on the specified index, or (if the index is a b-tree) a sequential
- scan followed by sorting. It will attempt to choose the method that
- will be faster, based on planner cost parameters and available statist=
ical
- information.
- </para>
-
<para>
While <command>CLUSTER</command> is running, the <xref
linkend=3D"guc-search-path"/> is temporarily changed to <literal>pg_ca=
talog,
pg_temp</literal>.
</para>
=20
- <para>
- When an index scan is used, a temporary copy of the table is created t=
hat
- contains the table data in the index order. Temporary copies of each
- index on the table are created as well. Therefore, you need free spac=
e on
- disk at least equal to the sum of the table size and the index sizes.
- </para>
-
- <para>
- When a sequential scan and sort is used, a temporary sort file is
- also created, so that the peak temporary space requirement is as much
- as double the table size, plus the index sizes. This method is often
- faster than the index scan method, but if the disk space requirement is
- intolerable, you can disable this choice by temporarily setting <xref
- linkend=3D"guc-enable-sort"/> to <literal>off</literal>.
- </para>
-
- <para>
- It is advisable to set <xref linkend=3D"guc-maintenance-work-mem"/> to
- a reasonably large value (but not more than the amount of RAM you can
- dedicate to the <command>CLUSTER</command> operation) before clusterin=
g.
- </para>
-
- <para>
- Because the planner records statistics about the ordering of
- tables, it is advisable to run <link linkend=3D"sql-analyze"><command>=
ANALYZE</command></link>
- on the newly clustered table.
- Otherwise, the planner might make poor choices of query plans.
- </para>
-
<para>
Because <command>CLUSTER</command> remembers which indexes are cluster=
ed,
one can cluster the tables one wants clustered manually the first time,
diff --git a/doc/src/sgml/ref/clusterdb.sgml b/doc/src/sgml/ref/clusterdb.s=
gml
index 0d2051bf6f1..b50c9581a98 100644
--- a/doc/src/sgml/ref/clusterdb.sgml
+++ b/doc/src/sgml/ref/clusterdb.sgml
@@ -64,6 +64,11 @@ PostgreSQL documentation
this utility and via other methods for accessing the server.
</para>
=20
+ <para>
+ <application>clusterdb</application> has been superseded by
+ <application>pg_repackdb</application>.
+ </para>
+
</refsect1>
=20
=20
diff --git a/doc/src/sgml/ref/pg_repackdb.sgml b/doc/src/sgml/ref/pg_repack=
db.sgml
new file mode 100644
index 00000000000..a5ea513c6b1
--- /dev/null
+++ b/doc/src/sgml/ref/pg_repackdb.sgml
@@ -0,0 +1,488 @@
+<!--
+doc/src/sgml/ref/pg_repackdb.sgml
+PostgreSQL documentation
+-->
+
+<refentry id=3D"app-pgrepackdb">
+ <indexterm zone=3D"app-pgrepackdb">
+ <primary>pg_repackdb</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle><application>pg_repackdb</application></refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo>Application</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>pg_repackdb</refname>
+ <refpurpose>repack and analyze a <productname>PostgreSQL</productname>
+ database</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>pg_repackdb</command>
+ <arg rep=3D"repeat"><replaceable>connection-option</replaceable></arg>
+ <arg rep=3D"repeat"><replaceable>option</replaceable></arg>
+
+ <arg choice=3D"plain" rep=3D"repeat">
+ <arg choice=3D"opt">
+ <group choice=3D"plain">
+ <arg choice=3D"plain"><option>-t</option></arg>
+ <arg choice=3D"plain"><option>--table</option></arg>
+ </group>
+ <replaceable>table</replaceable>
+ <arg choice=3D"opt">( <replaceable class=3D"parameter">column</replac=
eable> [,...] )</arg>
+ </arg>
+ </arg>
+
+ <arg choice=3D"opt">
+ <group choice=3D"plain">
+ <arg choice=3D"plain"><replaceable>dbname</replaceable></arg>
+ <arg choice=3D"plain"><option>-a</option></arg>
+ <arg choice=3D"plain"><option>--all</option></arg>
+ </group>
+ </arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>pg_repackdb</command>
+ <arg rep=3D"repeat"><replaceable>connection-option</replaceable></arg>
+ <arg rep=3D"repeat"><replaceable>option</replaceable></arg>
+
+ <arg choice=3D"plain" rep=3D"repeat">
+ <arg choice=3D"opt">
+ <group choice=3D"plain">
+ <arg choice=3D"plain"><option>-n</option></arg>
+ <arg choice=3D"plain"><option>--schema</option></arg>
+ </group>
+ <replaceable>schema</replaceable>
+ </arg>
+ </arg>
+
+ <arg choice=3D"opt">
+ <group choice=3D"plain">
+ <arg choice=3D"plain"><replaceable>dbname</replaceable></arg>
+ <arg choice=3D"plain"><option>-a</option></arg>
+ <arg choice=3D"plain"><option>--all</option></arg>
+ </group>
+ </arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>pg_repackdb</command>
+ <arg rep=3D"repeat"><replaceable>connection-option</replaceable></arg>
+ <arg rep=3D"repeat"><replaceable>option</replaceable></arg>
+
+ <arg choice=3D"plain" rep=3D"repeat">
+ <arg choice=3D"opt">
+ <group choice=3D"plain">
+ <arg choice=3D"plain"><option>-N</option></arg>
+ <arg choice=3D"plain"><option>--exclude-schema</option></arg>
+ </group>
+ <replaceable>schema</replaceable>
+ </arg>
+ </arg>
+
+ <arg choice=3D"opt">
+ <group choice=3D"plain">
+ <arg choice=3D"plain"><replaceable>dbname</replaceable></arg>
+ <arg choice=3D"plain"><option>-a</option></arg>
+ <arg choice=3D"plain"><option>--all</option></arg>
+ </group>
+ </arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <application>pg_repackdb</application> is a utility for repacking a
+ <productname>PostgreSQL</productname> database.
+ <application>pg_repackdb</application> will also generate internal
+ statistics used by the <productname>PostgreSQL</productname> query
+ optimizer.
+ </para>
+
+ <para>
+ <application>pg_repackdb</application> is a wrapper around the SQL
+ command <link linkend=3D"sql-repack"><command>REPACK</command></link>. =
There
+ is no effective difference between repacking and analyzing databases via
+ this utility and via other methods for accessing the server.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>
+ <application>pg_repackdb</application> accepts the following command-l=
ine arguments:
+ <variablelist>
+ <varlistentry>
+ <term><option>-a</option></term>
+ <term><option>--all</option></term>
+ <listitem>
+ <para>
+ Repack all databases.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option><optional>-d</optional> <replaceable class=3D"paramete=
r">dbname</replaceable></option></term>
+ <term><option><optional>--dbname=3D</optional><replaceable class=3D"=
parameter">dbname</replaceable></option></term>
+ <listitem>
+ <para>
+ Specifies the name of the database to be repacked or analyzed,
+ when <option>-a</option>/<option>--all</option> is not used. If t=
his
+ is not specified, the database name is read from the environment
+ variable <envar>PGDATABASE</envar>. If that is not set, the user =
name
+ specified for the connection is used.
+ The <replaceable>dbname</replaceable> can be
+ a <link linkend=3D"libpq-connstring">connection string</link>. If=
so,
+ connection string parameters will override any conflicting command
+ line options.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-e</option></term>
+ <term><option>--echo</option></term>
+ <listitem>
+ <para>
+ Echo the commands that <application>pg_repackdb</application>
+ generates and sends to the server.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--index<optional>=3D<replaceable class=3D"parameter">i=
ndex_name</replaceable></optional></option></term>
+ <listitem>
+ <para>
+ Pass the <literal>USING INDEX</literal> clause to <literal>REPACK<=
/literal>,
+ and optionally the index name to specify.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-j <replaceable class=3D"parameter">njobs</replaceable=
></option></term>
+ <term><option>--jobs=3D<replaceable class=3D"parameter">njobs</repla=
ceable></option></term>
+ <listitem>
+ <para>
+ Execute the repack or analyze commands in parallel by running
+ <replaceable class=3D"parameter">njobs</replaceable>
+ commands simultaneously. This option may reduce the processing ti=
me
+ but it also increases the load on the database server.
+ </para>
+ <para>
+ <application>pg_repackdb</application> will open
+ <replaceable class=3D"parameter">njobs</replaceable> connections t=
o the
+ database, so make sure your <xref linkend=3D"guc-max-connections"/>
+ setting is high enough to accommodate all connections.
+ </para>
+ <para>
+ Note that using this mode might cause deadlock failures if certain
+ system catalogs are processed in parallel.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-n <replaceable class=3D"parameter">schema</replaceabl=
e></option></term>
+ <term><option>--schema=3D<replaceable class=3D"parameter">schema</re=
placeable></option></term>
+ <listitem>
+ <para>
+ Repack or analyze all tables in
+ <replaceable class=3D"parameter">schema</replaceable> only. Multi=
ple
+ schemas can be repacked by writing multiple <option>-n</option>
+ switches.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-N <replaceable class=3D"parameter">schema</replaceabl=
e></option></term>
+ <term><option>--exclude-schema=3D<replaceable class=3D"parameter">sc=
hema</replaceable></option></term>
+ <listitem>
+ <para>
+ Do not repack or analyze any tables in
+ <replaceable class=3D"parameter">schema</replaceable>. Multiple s=
chemas
+ can be excluded by writing multiple <option>-N</option> switches.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-q</option></term>
+ <term><option>--quiet</option></term>
+ <listitem>
+ <para>
+ Do not display progress messages.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-t <replaceable class=3D"parameter">table</replaceable=
> [ (<replaceable class=3D"parameter">column</replaceable> [,...]) ]</optio=
n></term>
+ <term><option>--table=3D<replaceable class=3D"parameter">table</repl=
aceable> [ (<replaceable class=3D"parameter">column</replaceable> [,...]) ]=
</option></term>
+ <listitem>
+ <para>
+ Repack or analyze <replaceable class=3D"parameter">table</replacea=
ble>
+ only. Column names can be specified only in conjunction with
+ the <option>--analyze</option> option. Multiple tables can be
+ repacked by writing multiple
+ <option>-t</option> switches.
+ </para>
+ <tip>
+ <para>
+ If you specify columns, you probably have to escape the parenthes=
es
+ from the shell. (See examples below.)
+ </para>
+ </tip>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-v</option></term>
+ <term><option>--verbose</option></term>
+ <listitem>
+ <para>
+ Print detailed information during processing.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-V</option></term>
+ <term><option>--version</option></term>
+ <listitem>
+ <para>
+ Print the <application>pg_repackdb</application> version and exit.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-z</option></term>
+ <term><option>--analyze</option></term>
+ <listitem>
+ <para>
+ Also calculate statistics for use by the optimizer. If a column n=
ame
+ list is given, only compute statistics for those columns.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-?</option></term>
+ <term><option>--help</option></term>
+ <listitem>
+ <para>
+ Show help about <application>pg_repackdb</application> command line
+ arguments, and exit.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </para>
+
+ <para>
+ <application>pg_repackdb</application> also accepts
+ the following command-line arguments for connection parameters:
+ <variablelist>
+ <varlistentry>
+ <term><option>-h <replaceable class=3D"parameter">host</replaceable>=
</option></term>
+ <term><option>--host=3D<replaceable class=3D"parameter">host</replac=
eable></option></term>
+ <listitem>
+ <para>
+ Specifies the host name of the machine on which the server
+ is running. If the value begins with a slash, it is used
+ as the directory for the Unix domain socket.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-p <replaceable class=3D"parameter">port</replaceable>=
</option></term>
+ <term><option>--port=3D<replaceable class=3D"parameter">port</replac=
eable></option></term>
+ <listitem>
+ <para>
+ Specifies the TCP port or local Unix domain socket file
+ extension on which the server
+ is listening for connections.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-U <replaceable class=3D"parameter">username</replacea=
ble></option></term>
+ <term><option>--username=3D<replaceable class=3D"parameter">username=
</replaceable></option></term>
+ <listitem>
+ <para>
+ User name to connect as.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-w</option></term>
+ <term><option>--no-password</option></term>
+ <listitem>
+ <para>
+ Never issue a password prompt. If the server requires
+ password authentication and a password is not available by
+ other means such as a <filename>.pgpass</filename> file, the
+ connection attempt will fail. This option can be useful in
+ batch jobs and scripts where no user is present to enter a
+ password.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-W</option></term>
+ <term><option>--password</option></term>
+ <listitem>
+ <para>
+ Force <application>pg_repackdb</application> to prompt for a
+ password before connecting to a database.
+ </para>
+
+ <para>
+ This option is never essential, since
+ <application>pg_repackdb</application> will automatically prompt
+ for a password if the server demands password authentication.
+ However, <application>pg_repackdb</application> will waste a
+ connection attempt finding out that the server wants a password.
+ In some cases it is worth typing <option>-W</option> to avoid the =
extra
+ connection attempt.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--maintenance-db=3D<replaceable class=3D"parameter">db=
name</replaceable></option></term>
+ <listitem>
+ <para>
+ When the <option>-a</option>/<option>--all</option> is used, conne=
ct
+ to this database to gather the list of databases to repack.
+ If not specified, the <literal>postgres</literal> database will be=
used,
+ or if that does not exist, <literal>template1</literal> will be us=
ed.
+ This can be a <link linkend=3D"libpq-connstring">connection
+ string</link>. If so, connection string parameters will override =
any
+ conflicting command line options. Also, connection string paramet=
ers
+ other than the database name itself will be re-used when connecting
+ to other databases.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+
+
+ <refsect1>
+ <title>Environment</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><envar>PGDATABASE</envar></term>
+ <term><envar>PGHOST</envar></term>
+ <term><envar>PGPORT</envar></term>
+ <term><envar>PGUSER</envar></term>
+
+ <listitem>
+ <para>
+ Default connection parameters
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><envar>PG_COLOR</envar></term>
+ <listitem>
+ <para>
+ Specifies whether to use color in diagnostic messages. Possible valu=
es
+ are <literal>always</literal>, <literal>auto</literal> and
+ <literal>never</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ This utility, like most other <productname>PostgreSQL</productname> uti=
lities,
+ also uses the environment variables supported by <application>libpq</ap=
plication>
+ (see <xref linkend=3D"libpq-envars"/>).
+ </para>
+
+ </refsect1>
+
+
+ <refsect1>
+ <title>Diagnostics</title>
+
+ <para>
+ In case of difficulty, see
+ <xref linkend=3D"sql-repack"/> and <xref linkend=3D"app-psql"/> for
+ discussions of potential problems and error messages.
+ The database server must be running at the
+ targeted host. Also, any default connection settings and environment
+ variables used by the <application>libpq</application> front-end
+ library will apply.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To repack the database <literal>test</literal>:
+<screen>
+<prompt>$ </prompt><userinput>pg_repackdb test</userinput>
+</screen>
+ </para>
+
+ <para>
+ To repack and analyze for the optimizer a database named
+ <literal>bigdb</literal>:
+<screen>
+<prompt>$ </prompt><userinput>pg_repackdb --analyze bigdb</userinput>
+</screen>
+ </para>
+
+ <para>
+ To repack a single table
+ <literal>foo</literal> in a database named
+ <literal>xyzzy</literal>, and analyze a single column
+ <literal>bar</literal> of the table for the optimizer:
+<screen>
+<prompt>$ </prompt><userinput>pg_repackdb --analyze --verbose --table=3D'f=
oo(bar)' xyzzy</userinput>
+</screen></para>
+
+ <para>
+ To repack all tables in the <literal>foo</literal> and <literal>bar</l=
iteral> schemas
+ in a database named <literal>xyzzy</literal>:
+<screen>
+<prompt>$ </prompt><userinput>pg_repackdb --schema=3D'foo' --schema=3D'bar=
' xyzzy</userinput>
+</screen></para>
+
+
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type=3D"inline">
+ <member><xref linkend=3D"sql-repack"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/repack.sgml b/doc/src/sgml/ref/repack.sgml
new file mode 100644
index 00000000000..61d5c2cdef1
--- /dev/null
+++ b/doc/src/sgml/ref/repack.sgml
@@ -0,0 +1,328 @@
+<!--
+doc/src/sgml/ref/repack.sgml
+PostgreSQL documentation
+-->
+
+<refentry id=3D"sql-repack">
+ <indexterm zone=3D"sql-repack">
+ <primary>REPACK</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>REPACK</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>REPACK</refname>
+ <refpurpose>rewrite a table to reclaim disk space</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+REPACK [ ( <replaceable class=3D"parameter">option</replaceable> [, ...] )=
] [ <replaceable class=3D"parameter">table_and_columns</replaceable> [ USI=
NG INDEX [ <replaceable class=3D"parameter">index_name</replaceable> ] ] ]
+REPACK [ ( <replaceable class=3D"parameter">option</replaceable> [, ...] )=
] USING INDEX
+
+<phrase>where <replaceable class=3D"parameter">option</replaceable> can be=
one of:</phrase>
+
+ VERBOSE [ <replaceable class=3D"parameter">boolean</replaceable> ]
+ ANALYZE [ <replaceable class=3D"parameter">boolean</replaceable> ]
+
+<phrase>and <replaceable class=3D"parameter">table_and_columns</replaceabl=
e> is:</phrase>
+
+ <replaceable class=3D"parameter">table_name</replaceable> [ ( <replace=
able class=3D"parameter">column_name</replaceable> [, ...] ) ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>REPACK</command> reclaims storage occupied by dead
+ tuples. Unlike <command>VACUUM</command>, it does so by rewriting the
+ entire contents of the table specified
+ by <replaceable class=3D"parameter">table_name</replaceable> into a new=
disk
+ file with no extra space (except for the space guaranteed by
+ the <literal>fillfactor</literal> storage parameter), allowing unused s=
pace
+ to be returned to the operating system.
+ </para>
+
+ <para>
+ Without
+ a <replaceable class=3D"parameter">table_name</replaceable>, <command>R=
EPACK</command>
+ processes every table and materialized view in the current database that
+ the current user has the <literal>MAINTAIN</literal> privilege on. This
+ form of <command>REPACK</command> cannot be executed inside a transacti=
on
+ block.
+ </para>
+
+ <para>
+ If a <literal>USING INDEX</literal> clause is specified, the rows are
+ physically reordered based on information from an index. Please see the
+ notes on clustering below.
+ </para>
+
+ <para>
+ When a table is being repacked, an <literal>ACCESS EXCLUSIVE</literal> =
lock
+ is acquired on it. This prevents any other database operations (both re=
ads
+ and writes) from operating on the table until the <command>REPACK</comm=
and>
+ is finished.
+ </para>
+
+ <refsect2 id=3D"sql-repack-notes-on-clustering" xreflabel=3D"Notes on Cl=
ustering">
+ <title>Notes on Clustering</title>
+
+ <para>
+ If the <literal>USING INDEX</literal> clause is specified, the rows in
+ the table are physically reordered following an index: if an index name
+ is specified in the command, then that index is used; if no index name
+ is specified, then the index that has been configured as the index to
+ cluster on. If no index has been configured in this way, an error is
+ thrown. The index given in the <literal>USING INDEX</literal> clause
+ is configured as the index to cluster on, as well as an index given
+ to the <command>CLUSTER</command> command. An index can be set
+ manually using <command>ALTER TABLE ... CLUSTER ON</command>, and reset
+ with <command>ALTER TABLE ... SET WITHOUT CLUSTER</command>.
+ </para>
+
+ <para>
+ If no table name is specified in <command>REPACK USING INDEX</command>,
+ all tables which have a clustering index defined and which the calling
+ user has privileges for are processed.
+ </para>
+
+ <para>
+ Clustering is a one-time operation: when the table is
+ subsequently updated, the changes are not clustered. That is, no atte=
mpt
+ is made to store new or updated rows according to their index order. =
(If
+ one wishes, one can periodically recluster by issuing the command agai=
n.
+ Also, setting the table's <literal>fillfactor</literal> storage parame=
ter
+ to less than 100% can aid in preserving cluster ordering during update=
s,
+ since updated rows are kept on the same page if enough space is availa=
ble
+ there.)
+ </para>
+
+ <para>
+ In cases where you are accessing single rows randomly within a table, =
the
+ actual order of the data in the table is unimportant. However, if you =
tend
+ to access some data more than others, and there is an index that groups
+ them together, you will benefit from using clustering. If
+ you are requesting a range of indexed values from a table, or a single
+ indexed value that has multiple rows that match,
+ <command>REPACK</command> will help because once the index identifies =
the
+ table page for the first row that matches, all other rows that match a=
re
+ probably already on the same table page, and so you save disk accesses=
and
+ speed up the query.
+ </para>
+
+ <para>
+ <command>REPACK</command> can re-sort the table using either an index =
scan
+ on the specified index (if the index is a b-tree), or a sequential scan
+ followed by sorting. It will attempt to choose the method that will be
+ faster, based on planner cost parameters and available statistical
+ information.
+ </para>
+
+ <para>
+ Because the planner records statistics about the ordering of tables, i=
t is
+ advisable to
+ run <link linkend=3D"sql-analyze"><command>ANALYZE</command></link> on=
the
+ newly repacked table. Otherwise, the planner might make poor choices =
of
+ query plans.
+ </para>
+ </refsect2>
+
+ <refsect2 id=3D"sql-repack-notes-on-resources" xreflabel=3D"Notes on Res=
ources">
+ <title>Notes on Resources</title>
+
+ <para>
+ When an index scan or a sequential scan without sort is used, a tempor=
ary
+ copy of the table is created that contains the table data in the index
+ order. Temporary copies of each index on the table are created as wel=
l.
+ Therefore, you need free space on disk at least equal to the sum of the
+ table size and the index sizes.
+ </para>
+
+ <para>
+ When a sequential scan and sort is used, a temporary sort file is also
+ created, so that the peak temporary space requirement is as much as do=
uble
+ the table size, plus the index sizes. This method is often faster than
+ the index scan method, but if the disk space requirement is intolerabl=
e,
+ you can disable this choice by temporarily setting
+ <xref linkend=3D"guc-enable-sort"/> to <literal>off</literal>.
+ </para>
+
+ <para>
+ It is advisable to set <xref linkend=3D"guc-maintenance-work-mem"/> to=
a
+ reasonably large value (but not more than the amount of RAM you can
+ dedicate to the <command>REPACK</command> operation) before repacking.
+ </para>
+ </refsect2>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class=3D"parameter">table_name</replaceable></term>
+ <listitem>
+ <para>
+ The name (possibly schema-qualified) of a table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class=3D"parameter">column_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of a specific column to analyze. Defaults to all columns.
+ If a column list is specific, <literal>ANALYZE</literal> must also
+ be specified.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class=3D"parameter">index_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of an index.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>VERBOSE</literal></term>
+ <listitem>
+ <para>
+ Prints a progress report as each table is repacked
+ at <literal>INFO</literal> level.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ANALYZE</literal></term>
+ <term><literal>ANALYSE</literal></term>
+ <listitem>
+ <para>
+ Applies <xref linkend=3D"sql-analyze"/> on the table after repacking=
. This is
+ currently only supported when a single (non-partitioned) table is sp=
ecified.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class=3D"parameter">boolean</replaceable></term>
+ <listitem>
+ <para>
+ Specifies whether the selected option should be turned on or off.
+ You can write <literal>TRUE</literal>, <literal>ON</literal>, or
+ <literal>1</literal> to enable the option, and <literal>FALSE</liter=
al>,
+ <literal>OFF</literal>, or <literal>0</literal> to disable it. The
+ <replaceable class=3D"parameter">boolean</replaceable> value can also
+ be omitted, in which case <literal>TRUE</literal> is assumed.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ To repack a table, one must have the <literal>MAINTAIN</literal> privi=
lege
+ on the table.
+ </para>
+
+ <para>
+ While <command>REPACK</command> is running, the <xref
+ linkend=3D"guc-search-path"/> is temporarily changed to <literal>pg_ca=
talog,
+ pg_temp</literal>.
+ </para>
+
+ <para>
+ Each backend running <command>REPACK</command> will report its progress
+ in the <structname>pg_stat_progress_repack</structname> view. See
+ <xref linkend=3D"repack-progress-reporting"/> for details.
+ </para>
+
+ <para>
+ Repacking a partitioned table repacks each of its partitions. If an in=
dex
+ is specified, each partition is repacked using the partition of that
+ index. <command>REPACK</command> on a partitioned table cannot be exec=
uted
+ inside a transaction block.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Repack the table <literal>employees</literal>:
+<programlisting>
+REPACK employees;
+</programlisting>
+ </para>
+
+ <para>
+ Repack the table <literal>employees</literal> on the basis of its
+ index <literal>employees_ind</literal> (Since index is used here, this =
is
+ effectively clustering):
+<programlisting>
+REPACK employees USING INDEX employees_ind;
+</programlisting>
+ </para>
+
+ <para>
+ Repack the table <literal>cases</literal> on physical ordering,
+ running an <command>ANALYZE</command> on the given columns once
+ repacking is done, showing informational messages:
+<programlisting>
+REPACK (ANALYZE, VERBOSE) cases (district, case_nr);
+</programlisting>
+ </para>
+
+ <para>
+ Repack all tables in the database on which you have
+ the <literal>MAINTAIN</literal> privilege:
+<programlisting>
+REPACK;
+</programlisting>
+ </para>
+
+ <para>
+ Repack all tables for which a clustering index has previously been
+ configured on which you have the <literal>MAINTAIN</literal> privilege,
+ showing informational messages:
+<programlisting>
+REPACK (VERBOSE) USING INDEX;
+</programlisting>
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ There is no <command>REPACK</command> statement in the SQL standard.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type=3D"inline">
+ <member><xref linkend=3D"app-pgrepackdb"/></member>
+ <member><xref linkend=3D"repack-progress-reporting"/></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml
index 6d0fdd43cfb..ac5d083d468 100644
--- a/doc/src/sgml/ref/vacuum.sgml
+++ b/doc/src/sgml/ref/vacuum.sgml
@@ -25,7 +25,6 @@ VACUUM [ ( <replaceable class=3D"parameter">option</repla=
ceable> [, ...] ) ] [ <re
=20
<phrase>where <replaceable class=3D"parameter">option</replaceable> can be=
one of:</phrase>
=20
- FULL [ <replaceable class=3D"parameter">boolean</replaceable> ]
FREEZE [ <replaceable class=3D"parameter">boolean</replaceable> ]
VERBOSE [ <replaceable class=3D"parameter">boolean</replaceable> ]
ANALYZE [ <replaceable class=3D"parameter">boolean</replaceable> ]
@@ -39,6 +38,7 @@ VACUUM [ ( <replaceable class=3D"parameter">option</repla=
ceable> [, ...] ) ] [ <re
SKIP_DATABASE_STATS [ <replaceable class=3D"parameter">boolean</replac=
eable> ]
ONLY_DATABASE_STATS [ <replaceable class=3D"parameter">boolean</replac=
eable> ]
BUFFER_USAGE_LIMIT <replaceable class=3D"parameter">size</replaceable>
+ FULL [ <replaceable class=3D"parameter">boolean</replaceable> ]
=20
<phrase>and <replaceable class=3D"parameter">table_and_columns</replaceabl=
e> is:</phrase>
=20
@@ -95,20 +95,6 @@ VACUUM [ ( <replaceable class=3D"parameter">option</repl=
aceable> [, ...] ) ] [ <re
<title>Parameters</title>
=20
<variablelist>
- <varlistentry>
- <term><literal>FULL</literal></term>
- <listitem>
- <para>
- Selects <quote>full</quote> vacuum, which can reclaim more
- space, but takes much longer and exclusively locks the table.
- This method also requires extra disk space, since it writes a
- new copy of the table and doesn't release the old copy until
- the operation is complete. Usually this should only be used when a
- significant amount of space needs to be reclaimed from within the ta=
ble.
- </para>
- </listitem>
- </varlistentry>
-
<varlistentry>
<term><literal>FREEZE</literal></term>
<listitem>
@@ -362,6 +348,23 @@ VACUUM [ ( <replaceable class=3D"parameter">option</re=
placeable> [, ...] ) ] [ <re
</listitem>
</varlistentry>
=20
+ <varlistentry>
+ <term><literal>FULL</literal></term>
+ <listitem>
+ <para>
+ This option, which is deprecated, makes <command>VACUUM</command>
+ behave like <command>REPACK</command> without a
+ <literal>USING INDEX</literal> clause.
+ This method of compacting the table takes much longer than
+ <command>VACUUM</command> and exclusively locks the table.
+ This method also requires extra disk space, since it writes a
+ new copy of the table and doesn't release the old copy until
+ the operation is complete. Usually this should only be used when a
+ significant amount of space needs to be reclaimed from within the ta=
ble.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><replaceable class=3D"parameter">boolean</replaceable></term>
<listitem>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 2cf02c37b17..5d9a8a25a02 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -195,6 +195,7 @@
&refreshMaterializedView;
&reindex;
&releaseSavepoint;
+ &repack;
&reset;
&revoke;
&rollback;
@@ -258,6 +259,7 @@
&pgIsready;
&pgReceivewal;
&pgRecvlogical;
+ &pgRepackdb;
&pgRestore;
&pgVerifyBackup;
&psqlRef;
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/=
heap/heapam_handler.c
index cbef73e5d4b..7d4b48e5a97 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -741,13 +741,13 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Re=
lation NewHeap,
if (OldIndex !=3D NULL && !use_sort)
{
const int ci_index[] =3D {
- PROGRESS_CLUSTER_PHASE,
- PROGRESS_CLUSTER_INDEX_RELID
+ PROGRESS_REPACK_PHASE,
+ PROGRESS_REPACK_INDEX_RELID
};
int64 ci_val[2];
=20
/* Set phase and OIDOldIndex to columns */
- ci_val[0] =3D PROGRESS_CLUSTER_PHASE_INDEX_SCAN_HEAP;
+ ci_val[0] =3D PROGRESS_REPACK_PHASE_INDEX_SCAN_HEAP;
ci_val[1] =3D RelationGetRelid(OldIndex);
pgstat_progress_update_multi_param(2, ci_index, ci_val);
=20
@@ -759,15 +759,15 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Re=
lation NewHeap,
else
{
/* In scan-and-sort mode and also VACUUM FULL, set phase */
- pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
- PROGRESS_CLUSTER_PHASE_SEQ_SCAN_HEAP);
+ pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
+ PROGRESS_REPACK_PHASE_SEQ_SCAN_HEAP);
=20
tableScan =3D table_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
heapScan =3D (HeapScanDesc) tableScan;
indexScan =3D NULL;
=20
/* Set total heap blocks */
- pgstat_progress_update_param(PROGRESS_CLUSTER_TOTAL_HEAP_BLKS,
+ pgstat_progress_update_param(PROGRESS_REPACK_TOTAL_HEAP_BLKS,
heapScan->rs_nblocks);
}
=20
@@ -809,7 +809,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Rela=
tion NewHeap,
* is manually updated to the correct value when the table
* scan finishes.
*/
- pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_BLKS_SCANNED,
+ pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED,
heapScan->rs_nblocks);
break;
}
@@ -825,7 +825,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Rela=
tion NewHeap,
*/
if (prev_cblock !=3D heapScan->rs_cblock)
{
- pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_BLKS_SCANNED,
+ pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED,
(heapScan->rs_cblock +
heapScan->rs_nblocks -
heapScan->rs_startblock
@@ -926,14 +926,14 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Re=
lation NewHeap,
* In scan-and-sort mode, report increase in number of tuples
* scanned
*/
- pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED,
+ pgstat_progress_update_param(PROGRESS_REPACK_HEAP_TUPLES_SCANNED,
*num_tuples);
}
else
{
const int ct_index[] =3D {
- PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED,
- PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN
+ PROGRESS_REPACK_HEAP_TUPLES_SCANNED,
+ PROGRESS_REPACK_HEAP_TUPLES_WRITTEN
};
int64 ct_val[2];
=20
@@ -966,14 +966,14 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Re=
lation NewHeap,
double n_tuples =3D 0;
=20
/* Report that we are now sorting tuples */
- pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
- PROGRESS_CLUSTER_PHASE_SORT_TUPLES);
+ pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
+ PROGRESS_REPACK_PHASE_SORT_TUPLES);
=20
tuplesort_performsort(tuplesort);
=20
/* Report that we are now writing new heap */
- pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
- PROGRESS_CLUSTER_PHASE_WRITE_NEW_HEAP);
+ pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
+ PROGRESS_REPACK_PHASE_WRITE_NEW_HEAP);
=20
for (;;)
{
@@ -991,7 +991,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Rela=
tion NewHeap,
values, isnull,
rwstate);
/* Report n_tuples */
- pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN,
+ pgstat_progress_update_param(PROGRESS_REPACK_HEAP_TUPLES_WRITTEN,
n_tuples);
}
=20
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 43de42ce39e..5ee6389d39c 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -4077,7 +4077,7 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, =
int flags,
Assert(!ReindexIsProcessingIndex(indexOid));
=20
/* Set index rebuild count */
- pgstat_progress_update_param(PROGRESS_CLUSTER_INDEX_REBUILD_COUNT,
+ pgstat_progress_update_param(PROGRESS_REPACK_INDEX_REBUILD_COUNT,
i);
i++;
}
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/sys=
tem_views.sql
index 7553f31fef0..3f05ba3083a 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1283,14 +1283,15 @@ CREATE VIEW pg_stat_progress_vacuum AS
FROM pg_stat_get_progress_info('VACUUM') AS S
LEFT JOIN pg_database D ON S.datid =3D D.oid;
=20
-CREATE VIEW pg_stat_progress_cluster AS
+CREATE VIEW pg_stat_progress_repack AS
SELECT
S.pid AS pid,
S.datid AS datid,
D.datname AS datname,
S.relid AS relid,
CASE S.param1 WHEN 1 THEN 'CLUSTER'
- WHEN 2 THEN 'VACUUM FULL'
+ WHEN 2 THEN 'REPACK'
+ WHEN 3 THEN 'VACUUM FULL'
END AS command,
CASE S.param2 WHEN 0 THEN 'initializing'
WHEN 1 THEN 'seq scanning heap'
@@ -1301,15 +1302,35 @@ CREATE VIEW pg_stat_progress_cluster AS
WHEN 6 THEN 'rebuilding index'
WHEN 7 THEN 'performing final cleanup'
END AS phase,
- CAST(S.param3 AS oid) AS cluster_index_relid,
+ CAST(S.param3 AS oid) AS repack_index_relid,
S.param4 AS heap_tuples_scanned,
S.param5 AS heap_tuples_written,
S.param6 AS heap_blks_total,
S.param7 AS heap_blks_scanned,
S.param8 AS index_rebuild_count
- FROM pg_stat_get_progress_info('CLUSTER') AS S
+ FROM pg_stat_get_progress_info('REPACK') AS S
LEFT JOIN pg_database D ON S.datid =3D D.oid;
=20
+-- This view is as the one above, except for renaming a column and avoiding
+-- 'REPACK' as a command name to report.
+CREATE VIEW pg_stat_progress_cluster AS
+ SELECT
+ pid,
+ datid,
+ datname,
+ relid,
+ CASE WHEN command IN ('CLUSTER', 'VACUUM FULL') THEN command
+ WHEN repack_index_relid =3D 0 THEN 'VACUUM FULL'
+ ELSE 'CLUSTER' END AS command,
+ phase,
+ repack_index_relid AS cluster_index_relid,
+ heap_tuples_scanned,
+ heap_tuples_written,
+ heap_blks_total,
+ heap_blks_scanned,
+ index_rebuild_count
+ FROM pg_stat_progress_repack;
+
CREATE VIEW pg_stat_progress_create_index AS
SELECT
S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 60a4617a585..e19675a6d05 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -1,7 +1,8 @@
/*-------------------------------------------------------------------------
*
* cluster.c
- * CLUSTER a table on an index. This is now also used for VACUUM FULL.
+ * CLUSTER a table on an index. This is now also used for VACUUM FULL a=
nd
+ * REPACK.
*
* There is hardly anything left of Paul Brown's original implementation...
*
@@ -67,27 +68,35 @@ typedef struct
Oid indexOid;
} RelToCluster;
=20
-
-static void cluster_multiple_rels(List *rtcs, ClusterParams *params);
+static bool cluster_rel_recheck(RepackCommand cmd, Relation OldHeap,
+ Oid indexOid, Oid userid, int options);
static void rebuild_relation(Relation OldHeap, Relation index, bool verbos=
e);
static void copy_table_data(Relation NewHeap, Relation OldHeap, Relation O=
ldIndex,
bool verbose, bool *pSwapToastByContent,
TransactionId *pFreezeXid, MultiXactId *pCutoffMulti);
-static List *get_tables_to_cluster(MemoryContext cluster_context);
-static List *get_tables_to_cluster_partitioned(MemoryContext cluster_conte=
xt,
- Oid indexOid);
-static bool cluster_is_permitted_for_relation(Oid relid, Oid userid);
+static List *get_tables_to_repack(RepackCommand cmd, bool usingindex,
+ MemoryContext permcxt);
+static List *get_tables_to_repack_partitioned(RepackCommand cmd,
+ Oid relid, bool rel_is_index,
+ MemoryContext permcxt);
+static bool cluster_is_permitted_for_relation(RepackCommand cmd,
+ Oid relid, Oid userid);
+static Relation process_single_relation(RepackStmt *stmt,
+ ClusterParams *params);
+static Oid determine_clustered_index(Relation rel, bool usingindex,
+ const char *indexname);
+static const char *RepackCommandAsString(RepackCommand cmd);
=20
=20
-/*------------------------------------------------------------------------=
---
- * This cluster code allows for clustering multiple tables at once. Because
+/*
+ * The repack code allows for processing multiple tables at once. Because
* of this, we cannot just run everything on a single transaction, or we
* would be forced to acquire exclusive locks on all the tables being
* clustered, simultaneously --- very likely leading to deadlock.
*
- * To solve this we follow a similar strategy to VACUUM code,
- * clustering each relation in a separate transaction. For this to work,
- * we need to:
+ * To solve this we follow a similar strategy to VACUUM code, processing e=
ach
+ * relation in a separate transaction. For this to work, we need to:
+ *
* - provide a separate memory context so that we can pass information in
* a way that survives across transactions
* - start a new transaction every time a new relation is clustered
@@ -98,197 +107,166 @@ static bool cluster_is_permitted_for_relation(Oid re=
lid, Oid userid);
*
* The single-relation case does not have any such overhead.
*
- * We also allow a relation to be specified without index. In that case,
- * the indisclustered bit will be looked up, and an ERROR will be thrown
- * if there is no index with the bit set.
- *------------------------------------------------------------------------=
---
+ * We also allow a relation to be repacked following an index, but without
+ * naming a specific one. In that case, the indisclustered bit will be
+ * looked up, and an ERROR will be thrown if no so-marked index is found.
*/
void
-cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
+ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel)
{
- ListCell *lc;
ClusterParams params =3D {0};
- bool verbose =3D false;
Relation rel =3D NULL;
- Oid indexOid =3D InvalidOid;
- MemoryContext cluster_context;
+ MemoryContext repack_context;
List *rtcs;
=20
/* Parse option list */
- foreach(lc, stmt->params)
+ foreach_node(DefElem, opt, stmt->params)
{
- DefElem *opt =3D (DefElem *) lfirst(lc);
-
if (strcmp(opt->defname, "verbose") =3D=3D 0)
- verbose =3D defGetBoolean(opt);
+ params.options |=3D defGetBoolean(opt) ? CLUOPT_VERBOSE : 0;
+ else if (strcmp(opt->defname, "analyze") =3D=3D 0 ||
+ strcmp(opt->defname, "analyse") =3D=3D 0)
+ params.options |=3D defGetBoolean(opt) ? CLUOPT_ANALYZE : 0;
else
ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("unrecognized %s option \"%s\"",
- "CLUSTER", opt->defname),
- parser_errposition(pstate, opt->location)));
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized %s option \"%s\"",
+ RepackCommandAsString(stmt->command),
+ opt->defname),
+ parser_errposition(pstate, opt->location));
}
=20
- params.options =3D (verbose ? CLUOPT_VERBOSE : 0);
-
+ /*
+ * If a single relation is specified, process it and we're done ... unless
+ * the relation is a partitioned table, in which case we fall through.
+ */
if (stmt->relation !=3D NULL)
{
- /* This is the single-relation case. */
- Oid tableOid;
-
- /*
- * Find, lock, and check permissions on the table. We obtain
- * AccessExclusiveLock right away to avoid lock-upgrade hazard in the
- * single-transaction case.
- */
- tableOid =3D RangeVarGetRelidExtended(stmt->relation,
- AccessExclusiveLock,
- 0,
- RangeVarCallbackMaintainsTable,
- NULL);
- rel =3D table_open(tableOid, NoLock);
-
- /*
- * Reject clustering a remote temp table ... their local buffer
- * manager is not going to cope.
- */
- if (RELATION_IS_OTHER_TEMP(rel))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot cluster temporary tables of other sessions")));
-
- if (stmt->indexname =3D=3D NULL)
- {
- ListCell *index;
-
- /* We need to find the index that has indisclustered set. */
- foreach(index, RelationGetIndexList(rel))
- {
- indexOid =3D lfirst_oid(index);
- if (get_index_isclustered(indexOid))
- break;
- indexOid =3D InvalidOid;
- }
-
- if (!OidIsValid(indexOid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("there is no previously clustered index for table \"%s\"",
- stmt->relation->relname)));
- }
- else
- {
- /*
- * The index is expected to be in the same namespace as the
- * relation.
- */
- indexOid =3D get_relname_relid(stmt->indexname,
- rel->rd_rel->relnamespace);
- if (!OidIsValid(indexOid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("index \"%s\" for table \"%s\" does not exist",
- stmt->indexname, stmt->relation->relname)));
- }
-
- /* For non-partitioned tables, do what we came here to do. */
- if (rel->rd_rel->relkind !=3D RELKIND_PARTITIONED_TABLE)
- {
- cluster_rel(rel, indexOid, ¶ms);
- /* cluster_rel closes the relation, but keeps lock */
-
- return;
- }
+ rel =3D process_single_relation(stmt, ¶ms);
+ if (rel =3D=3D NULL)
+ return; /* all done */
}
=20
+ /*
+ * Don't allow ANALYZE in the multiple-relation case for now. Maybe we
+ * can add support for this later.
+ */
+ if (params.options & CLUOPT_ANALYZE)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot %s multiple tables", "REPACK (ANALYZE)"));
+
/*
* By here, we know we are in a multi-table situation. In order to avoid
* holding locks for too long, we want to process each table in its own
* transaction. This forces us to disallow running inside a user
* transaction block.
*/
- PreventInTransactionBlock(isTopLevel, "CLUSTER");
+ PreventInTransactionBlock(isTopLevel, RepackCommandAsString(stmt->command=
));
=20
/* Also, we need a memory context to hold our list of relations */
- cluster_context =3D AllocSetContextCreate(PortalContext,
- "Cluster",
- ALLOCSET_DEFAULT_SIZES);
+ repack_context =3D AllocSetContextCreate(PortalContext,
+ "Repack",
+ ALLOCSET_DEFAULT_SIZES);
+
+ params.options |=3D CLUOPT_RECHECK;
=20
/*
- * Either we're processing a partitioned table, or we were not given any
- * table name at all. In either case, obtain a list of relations to
- * process.
- *
- * In the former case, an index name must have been given, so we don't
- * need to recheck its "indisclustered" bit, but we have to check that it
- * is an index that we can cluster on. In the latter case, we set the
- * option bit to have indisclustered verified.
- *
- * Rechecking the relation itself is necessary here in all cases.
+ * If we don't have a relation yet, determine a relation list. If we do,
+ * then it must be a partitioned table, and we want to process its
+ * partitions.
*/
- params.options |=3D CLUOPT_RECHECK;
- if (rel !=3D NULL)
+ if (rel =3D=3D NULL)
{
- Assert(rel->rd_rel->relkind =3D=3D RELKIND_PARTITIONED_TABLE);
- check_index_is_clusterable(rel, indexOid, AccessShareLock);
- rtcs =3D get_tables_to_cluster_partitioned(cluster_context, indexOid);
-
- /* close relation, releasing lock on parent table */
- table_close(rel, AccessExclusiveLock);
+ Assert(stmt->indexname =3D=3D NULL);
+ rtcs =3D get_tables_to_repack(stmt->command, stmt->usingindex,
+ repack_context);
+ params.options |=3D CLUOPT_RECHECK_ISCLUSTERED;
}
else
{
- rtcs =3D get_tables_to_cluster(cluster_context);
- params.options |=3D CLUOPT_RECHECK_ISCLUSTERED;
- }
+ Oid relid;
+ bool rel_is_index;
=20
- /* Do the job. */
- cluster_multiple_rels(rtcs, ¶ms);
+ Assert(rel->rd_rel->relkind =3D=3D RELKIND_PARTITIONED_TABLE);
=20
- /* Start a new transaction for the cleanup work. */
- StartTransactionCommand();
+ /*
+ * If USING INDEX was specified, resolve the index name now and pass
+ * it down.
+ */
+ if (stmt->usingindex)
+ {
+ /*
+ * If no index name was specified when repacking a partitioned
+ * table, punt for now. Maybe we can improve this later.
+ */
+ if (!stmt->indexname)
+ ereport(ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("there is no previously clustered index for table \"%s\"",
+ RelationGetRelationName(rel)));
+
+ relid =3D determine_clustered_index(rel, stmt->usingindex,
+ stmt->indexname);
+ if (!OidIsValid(relid))
+ elog(ERROR, "unable to determine index to cluster on");
+ /* XXX is this the right place for this check? */
+ check_index_is_clusterable(rel, relid, AccessExclusiveLock);
+ rel_is_index =3D true;
+ }
+ else
+ {
+ relid =3D RelationGetRelid(rel);
+ rel_is_index =3D false;
+ }
=20
- /* Clean up working storage */
- MemoryContextDelete(cluster_context);
-}
+ rtcs =3D get_tables_to_repack_partitioned(stmt->command,
+ relid, rel_is_index,
+ repack_context);
=20
-/*
- * Given a list of relations to cluster, process each of them in a separate
- * transaction.
- *
- * We expect to be in a transaction at start, but there isn't one when we
- * return.
- */
-static void
-cluster_multiple_rels(List *rtcs, ClusterParams *params)
-{
- ListCell *lc;
+ /* close parent relation, releasing lock on it */
+ table_close(rel, AccessExclusiveLock);
+ rel =3D NULL;
+ }
=20
/* Commit to get out of starting transaction */
PopActiveSnapshot();
CommitTransactionCommand();
=20
/* Cluster the tables, each in a separate transaction */
- foreach(lc, rtcs)
+ Assert(rel =3D=3D NULL);
+ foreach_ptr(RelToCluster, rtc, rtcs)
{
- RelToCluster *rtc =3D (RelToCluster *) lfirst(lc);
- Relation rel;
-
/* Start a new transaction for each relation. */
StartTransactionCommand();
=20
+ /*
+ * Open the target table, coping with the case where it has been
+ * dropped.
+ */
+ rel =3D try_table_open(rtc->tableOid, AccessExclusiveLock);
+ if (rel =3D=3D NULL)
+ {
+ CommitTransactionCommand();
+ continue;
+ }
+
/* functions in indexes may want a snapshot set */
PushActiveSnapshot(GetTransactionSnapshot());
=20
- rel =3D table_open(rtc->tableOid, AccessExclusiveLock);
-
/* Process this table */
- cluster_rel(rel, rtc->indexOid, params);
+ cluster_rel(stmt->command, rel, rtc->indexOid, ¶ms);
/* cluster_rel closes the relation, but keeps lock */
=20
PopActiveSnapshot();
CommitTransactionCommand();
}
+
+ /* Start a new transaction for the cleanup work. */
+ StartTransactionCommand();
+
+ /* Clean up working storage */
+ MemoryContextDelete(repack_context);
}
=20
/*
@@ -304,11 +282,14 @@ cluster_multiple_rels(List *rtcs, ClusterParams *para=
ms)
* them incrementally while we load the table.
*
* If indexOid is InvalidOid, the table will be rewritten in physical order
- * instead of index order. This is the new implementation of VACUUM FULL,
- * and error messages should refer to the operation as VACUUM not CLUSTER.
+ * instead of index order.
+ *
+ * 'cmd' indicates which command is being executed, to be used for error
+ * messages.
*/
void
-cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params)
+cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid,
+ ClusterParams *params)
{
Oid tableOid =3D RelationGetRelid(OldHeap);
Oid save_userid;
@@ -323,13 +304,8 @@ cluster_rel(Relation OldHeap, Oid indexOid, ClusterPar=
ams *params)
/* Check for user-requested abort. */
CHECK_FOR_INTERRUPTS();
=20
- pgstat_progress_start_command(PROGRESS_COMMAND_CLUSTER, tableOid);
- if (OidIsValid(indexOid))
- pgstat_progress_update_param(PROGRESS_CLUSTER_COMMAND,
- PROGRESS_CLUSTER_COMMAND_CLUSTER);
- else
- pgstat_progress_update_param(PROGRESS_CLUSTER_COMMAND,
- PROGRESS_CLUSTER_COMMAND_VACUUM_FULL);
+ pgstat_progress_start_command(PROGRESS_COMMAND_REPACK, tableOid);
+ pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd);
=20
/*
* Switch to the table owner's userid, so that any index functions are run
@@ -350,86 +326,38 @@ cluster_rel(Relation OldHeap, Oid indexOid, ClusterPa=
rams *params)
* *must* skip the one on indisclustered since it would reject an attempt
* to cluster a not-previously-clustered index.
*/
- if (recheck)
- {
- /* Check that the user still has privileges for the relation */
- if (!cluster_is_permitted_for_relation(tableOid, save_userid))
- {
- relation_close(OldHeap, AccessExclusiveLock);
- goto out;
- }
-
- /*
- * Silently skip a temp table for a remote session. Only doing this
- * check in the "recheck" case is appropriate (which currently means
- * somebody is executing a database-wide CLUSTER or on a partitioned
- * table), because there is another check in cluster() which will stop
- * any attempt to cluster remote temp tables by name. There is
- * another check in cluster_rel which is redundant, but we leave it
- * for extra safety.
- */
- if (RELATION_IS_OTHER_TEMP(OldHeap))
- {
- relation_close(OldHeap, AccessExclusiveLock);
- goto out;
- }
-
- if (OidIsValid(indexOid))
- {
- /*
- * Check that the index still exists
- */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(indexOid)))
- {
- relation_close(OldHeap, AccessExclusiveLock);
- goto out;
- }
-
- /*
- * Check that the index is still the one with indisclustered set,
- * if needed.
- */
- if ((params->options & CLUOPT_RECHECK_ISCLUSTERED) !=3D 0 &&
- !get_index_isclustered(indexOid))
- {
- relation_close(OldHeap, AccessExclusiveLock);
- goto out;
- }
- }
- }
+ if (recheck &&
+ !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid,
+ params->options))
+ goto out;
=20
/*
- * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER
- * would work in most respects, but the index would only get marked as
- * indisclustered in the current database, leading to unexpected behavior
- * if CLUSTER were later invoked in another database.
+ * We allow repacking shared catalogs only when not using an index. It
+ * would work to use an index in most respects, but the index would only
+ * get marked as indisclustered in the current database, leading to
+ * unexpected behavior if CLUSTER were later invoked in another database.
*/
if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot cluster a shared catalog")));
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot run %s on a shared catalog",
+ RepackCommandAsString(cmd)));
=20
/*
* Don't process temp tables of other backends ... their local buffer
* manager is not going to cope.
*/
if (RELATION_IS_OTHER_TEMP(OldHeap))
- {
- if (OidIsValid(indexOid))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot cluster temporary tables of other sessions")));
- else
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot vacuum temporary tables of other sessions")));
- }
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot run %s on temporary tables of other sessions",
+ RepackCommandAsString(cmd)));
=20
/*
* Also check for active uses of the relation in the current transaction,
* including open scans and pending AFTER trigger events.
*/
- CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM");
+ CheckTableNotInUse(OldHeap, RepackCommandAsString(cmd));
=20
/* Check heap and index are valid to cluster on */
if (OidIsValid(indexOid))
@@ -442,6 +370,24 @@ cluster_rel(Relation OldHeap, Oid indexOid, ClusterPar=
ams *params)
else
index =3D NULL;
=20
+ /*
+ * When allow_system_table_mods is turned off, we disallow repacking a
+ * catalog on a particular index unless that's already the clustered index
+ * for that catalog.
+ *
+ * XXX We don't check for this in CLUSTER, because it's historically been
+ * allowed.
+ */
+ if (cmd !=3D REPACK_COMMAND_CLUSTER &&
+ !allowSystemTableMods && OidIsValid(indexOid) &&
+ IsCatalogRelation(OldHeap) && !index->rd_index->indisclustered)
+ ereport(ERROR,
+ errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied: \"%s\" is a system catalog",
+ RelationGetRelationName(OldHeap)),
+ errdetail("System catalogs can only be clustered by the index they're =
already clustered on, if any, unless \"%s\" is enabled.",
+ "allow_system_table_mods"));
+
/*
* Quietly ignore the request if this is a materialized view which has not
* been populated from its query. No harm is done because there is no data
@@ -482,6 +428,63 @@ out:
pgstat_progress_end_command();
}
=20
+/*
+ * Check if the table (and its index) still meets the requirements of
+ * cluster_rel().
+ */
+static bool
+cluster_rel_recheck(RepackCommand cmd, Relation OldHeap, Oid indexOid,
+ Oid userid, int options)
+{
+ Oid tableOid =3D RelationGetRelid(OldHeap);
+
+ /* Check that the user still has privileges for the relation */
+ if (!cluster_is_permitted_for_relation(cmd, tableOid, userid))
+ {
+ relation_close(OldHeap, AccessExclusiveLock);
+ return false;
+ }
+
+ /*
+ * Silently skip a temp table for a remote session. Only doing this check
+ * in the "recheck" case is appropriate (which currently means somebody is
+ * executing a database-wide CLUSTER or on a partitioned table), because
+ * there is another check in cluster() which will stop any attempt to
+ * cluster remote temp tables by name. There is another check in
+ * cluster_rel which is redundant, but we leave it for extra safety.
+ */
+ if (RELATION_IS_OTHER_TEMP(OldHeap))
+ {
+ relation_close(OldHeap, AccessExclusiveLock);
+ return false;
+ }
+
+ if (OidIsValid(indexOid))
+ {
+ /*
+ * Check that the index still exists
+ */
+ if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(indexOid)))
+ {
+ relation_close(OldHeap, AccessExclusiveLock);
+ return false;
+ }
+
+ /*
+ * Check that the index is still the one with indisclustered set, if
+ * needed.
+ */
+ if ((options & CLUOPT_RECHECK_ISCLUSTERED) !=3D 0 &&
+ !get_index_isclustered(indexOid))
+ {
+ relation_close(OldHeap, AccessExclusiveLock);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/*
* Verify that the specified heap and index are valid to cluster on
*
@@ -642,8 +645,8 @@ rebuild_relation(Relation OldHeap, Relation index, bool=
verbose)
Assert(CheckRelationLockedByMe(OldHeap, AccessExclusiveLock, false) &&
(index =3D=3D NULL || CheckRelationLockedByMe(index, AccessExclusiveL=
ock, false)));
=20
- if (index)
- /* Mark the correct index as clustered */
+ /* for CLUSTER or REPACK USING INDEX, mark the index as the one to use */
+ if (index !=3D NULL)
mark_index_clustered(OldHeap, RelationGetRelid(index), true);
=20
/* Remember info about rel before closing OldHeap */
@@ -958,20 +961,20 @@ copy_table_data(Relation NewHeap, Relation OldHeap, R=
elation OldIndex, bool verb
/* Log what we're doing */
if (OldIndex !=3D NULL && !use_sort)
ereport(elevel,
- (errmsg("clustering \"%s.%s\" using index scan on \"%s\"",
- nspname,
- RelationGetRelationName(OldHeap),
- RelationGetRelationName(OldIndex))));
+ errmsg("repacking \"%s.%s\" using index scan on \"%s\"",
+ nspname,
+ RelationGetRelationName(OldHeap),
+ RelationGetRelationName(OldIndex)));
else if (use_sort)
ereport(elevel,
- (errmsg("clustering \"%s.%s\" using sequential scan and sort",
- nspname,
- RelationGetRelationName(OldHeap))));
+ errmsg("repacking \"%s.%s\" using sequential scan and sort",
+ nspname,
+ RelationGetRelationName(OldHeap)));
else
ereport(elevel,
- (errmsg("vacuuming \"%s.%s\"",
- nspname,
- RelationGetRelationName(OldHeap))));
+ errmsg("repacking \"%s.%s\" in physical order",
+ nspname,
+ RelationGetRelationName(OldHeap)));
=20
/*
* Hand off the actual copying to AM specific function, the generic code
@@ -1458,8 +1461,8 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
int i;
=20
/* Report that we are now swapping relation files */
- pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
- PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES);
+ pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
+ PROGRESS_REPACK_PHASE_SWAP_REL_FILES);
=20
/* Zero out possible results from swapped_relation_files */
memset(mapped_tables, 0, sizeof(mapped_tables));
@@ -1509,14 +1512,14 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
reindex_flags |=3D REINDEX_REL_FORCE_INDEXES_PERMANENT;
=20
/* Report that we are now reindexing relations */
- pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
- PROGRESS_CLUSTER_PHASE_REBUILD_INDEX);
+ pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
+ PROGRESS_REPACK_PHASE_REBUILD_INDEX);
=20
reindex_relation(NULL, OIDOldHeap, reindex_flags, &reindex_params);
=20
/* Report that we are now doing clean up */
- pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
- PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP);
+ pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
+ PROGRESS_REPACK_PHASE_FINAL_CLEANUP);
=20
/*
* If the relation being rebuilt is pg_class, swap_relation_files()
@@ -1632,106 +1635,191 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
}
}
=20
-
/*
- * Get a list of tables that the current user has privileges on and
- * have indisclustered set. Return the list in a List * of RelToCluster
- * (stored in the specified memory context), each one giving the tableOid
- * and the indexOid on which the table is already clustered.
+ * Determine which relations to process, when REPACK/CLUSTER is called
+ * without specifying a table name. The exact process depends on whether
+ * USING INDEX was given or not, and in any case we only return tables and
+ * materialized views that the current user has privileges to repack/clust=
er.
+ *
+ * If USING INDEX was given, we scan pg_index to find those that have
+ * indisclustered set; if it was not given, scan pg_class and return all
+ * tables.
+ *
+ * Return it as a list of RelToCluster in the given memory context.
*/
static List *
-get_tables_to_cluster(MemoryContext cluster_context)
+get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext per=
mcxt)
{
- Relation indRelation;
+ Relation catalog;
TableScanDesc scan;
- ScanKeyData entry;
- HeapTuple indexTuple;
- Form_pg_index index;
- MemoryContext old_context;
+ HeapTuple tuple;
List *rtcs =3D NIL;
=20
- /*
- * Get all indexes that have indisclustered set and that the current user
- * has the appropriate privileges for.
- */
- indRelation =3D table_open(IndexRelationId, AccessShareLock);
- ScanKeyInit(&entry,
- Anum_pg_index_indisclustered,
- BTEqualStrategyNumber, F_BOOLEQ,
- BoolGetDatum(true));
- scan =3D table_beginscan_catalog(indRelation, 1, &entry);
- while ((indexTuple =3D heap_getnext(scan, ForwardScanDirection)) !=3D NUL=
L)
+ if (usingindex)
{
- RelToCluster *rtc;
+ ScanKeyData entry;
+
+ catalog =3D table_open(IndexRelationId, AccessShareLock);
+ ScanKeyInit(&entry,
+ Anum_pg_index_indisclustered,
+ BTEqualStrategyNumber, F_BOOLEQ,
+ BoolGetDatum(true));
+ scan =3D table_beginscan_catalog(catalog, 1, &entry);
+ while ((tuple =3D heap_getnext(scan, ForwardScanDirection)) !=3D NULL)
+ {
+ RelToCluster *rtc;
+ Form_pg_index index;
+ MemoryContext oldcxt;
=20
- index =3D (Form_pg_index) GETSTRUCT(indexTuple);
+ index =3D (Form_pg_index) GETSTRUCT(tuple);
=20
- if (!cluster_is_permitted_for_relation(index->indrelid, GetUserId()))
- continue;
+ /*
+ * Try to obtain a light lock on the index's table, to ensure it
+ * doesn't go away while we collect the list. If we cannot, just
+ * disregard it.
+ */
+ if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
+ continue;
=20
- /* Use a permanent memory context for the result list */
- old_context =3D MemoryContextSwitchTo(cluster_context);
+ /* Verify that the table still exists */
+ if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(index->indrelid)))
+ {
+ /* Release useless lock */
+ UnlockRelationOid(index->indrelid, AccessShareLock);
+ continue;
+ }
=20
- rtc =3D palloc_object(RelToCluster);
- rtc->tableOid =3D index->indrelid;
- rtc->indexOid =3D index->indexrelid;
- rtcs =3D lappend(rtcs, rtc);
+ if (!cluster_is_permitted_for_relation(cmd, index->indrelid,
+ GetUserId()))
+ continue;
+
+ /* Use a permanent memory context for the result list */
+ oldcxt =3D MemoryContextSwitchTo(permcxt);
+ rtc =3D palloc_object(RelToCluster);
+ rtc->tableOid =3D index->indrelid;
+ rtc->indexOid =3D index->indexrelid;
+ rtcs =3D lappend(rtcs, rtc);
+ MemoryContextSwitchTo(oldcxt);
+ }
+ }
+ else
+ {
+ catalog =3D table_open(RelationRelationId, AccessShareLock);
+ scan =3D table_beginscan_catalog(catalog, 0, NULL);
+
+ while ((tuple =3D heap_getnext(scan, ForwardScanDirection)) !=3D NULL)
+ {
+ RelToCluster *rtc;
+ Form_pg_class class;
+ MemoryContext oldcxt;
+
+ class =3D (Form_pg_class) GETSTRUCT(tuple);
+
+ /*
+ * Try to obtain a light lock on the table, to ensure it doesn't
+ * go away while we collect the list. If we cannot, just
+ * disregard the table.
+ */
+ if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
+ continue;
=20
- MemoryContextSwitchTo(old_context);
+ /* Verify that the table still exists */
+ if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
+ {
+ /* Release useless lock */
+ UnlockRelationOid(class->oid, AccessShareLock);
+ continue;
+ }
+
+ /* Can only process plain tables and matviews */
+ if (class->relkind !=3D RELKIND_RELATION &&
+ class->relkind !=3D RELKIND_MATVIEW)
+ continue;
+
+ /* noisily skip rels which the user can't process */
+ if (!cluster_is_permitted_for_relation(cmd, class->oid,
+ GetUserId()))
+ continue;
+
+ /* Use a permanent memory context for the result list */
+ oldcxt =3D MemoryContextSwitchTo(permcxt);
+ rtc =3D palloc_object(RelToCluster);
+ rtc->tableOid =3D class->oid;
+ rtc->indexOid =3D InvalidOid;
+ rtcs =3D lappend(rtcs, rtc);
+ MemoryContextSwitchTo(oldcxt);
+ }
}
- table_endscan(scan);
=20
- relation_close(indRelation, AccessShareLock);
+ table_endscan(scan);
+ relation_close(catalog, AccessShareLock);
=20
return rtcs;
}
=20
/*
- * Given an index on a partitioned table, return a list of RelToCluster for
+ * Given a partitioned table or its index, return a list of RelToCluster f=
or
* all the children leaves tables/indexes.
*
* Like expand_vacuum_rel, but here caller must hold AccessExclusiveLock
* on the table containing the index.
+ *
+ * 'rel_is_index' tells whether 'relid' is that of an index (true) or of t=
he
+ * owning relation.
*/
static List *
-get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid index=
Oid)
+get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
+ bool rel_is_index, MemoryContext permcxt)
{
List *inhoids;
- ListCell *lc;
List *rtcs =3D NIL;
- MemoryContext old_context;
-
- /* Do not lock the children until they're processed */
- inhoids =3D find_all_inheritors(indexOid, NoLock, NULL);
=20
- foreach(lc, inhoids)
+ /*
+ * Do not lock the children until they're processed. Note that we do hold
+ * a lock on the parent partitioned table.
+ */
+ inhoids =3D find_all_inheritors(relid, NoLock, NULL);
+ foreach_oid(child_oid, inhoids)
{
- Oid indexrelid =3D lfirst_oid(lc);
- Oid relid =3D IndexGetRelation(indexrelid, false);
+ Oid table_oid,
+ index_oid;
RelToCluster *rtc;
+ MemoryContext oldcxt;
=20
- /* consider only leaf indexes */
- if (get_rel_relkind(indexrelid) !=3D RELKIND_INDEX)
- continue;
+ if (rel_is_index)
+ {
+ /* consider only leaf indexes */
+ if (get_rel_relkind(child_oid) !=3D RELKIND_INDEX)
+ continue;
+
+ table_oid =3D IndexGetRelation(child_oid, false);
+ index_oid =3D child_oid;
+ }
+ else
+ {
+ /* consider only leaf relations */
+ if (get_rel_relkind(child_oid) !=3D RELKIND_RELATION)
+ continue;
+
+ table_oid =3D child_oid;
+ index_oid =3D InvalidOid;
+ }
=20
/*
* It's possible that the user does not have privileges to CLUSTER the
- * leaf partition despite having such privileges on the partitioned
- * table. We skip any partitions which the user is not permitted to
- * CLUSTER.
+ * leaf partition despite having them on the partitioned table. Skip
+ * if so.
*/
- if (!cluster_is_permitted_for_relation(relid, GetUserId()))
+ if (!cluster_is_permitted_for_relation(cmd, table_oid, GetUserId()))
continue;
=20
/* Use a permanent memory context for the result list */
- old_context =3D MemoryContextSwitchTo(cluster_context);
-
+ oldcxt =3D MemoryContextSwitchTo(permcxt);
rtc =3D palloc_object(RelToCluster);
- rtc->tableOid =3D relid;
- rtc->indexOid =3D indexrelid;
+ rtc->tableOid =3D table_oid;
+ rtc->indexOid =3D index_oid;
rtcs =3D lappend(rtcs, rtc);
-
- MemoryContextSwitchTo(old_context);
+ MemoryContextSwitchTo(oldcxt);
}
=20
return rtcs;
@@ -1742,13 +1830,167 @@ get_tables_to_cluster_partitioned(MemoryContext cl=
uster_context, Oid indexOid)
* function emits a WARNING.
*/
static bool
-cluster_is_permitted_for_relation(Oid relid, Oid userid)
+cluster_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ Assert(cmd =3D=3D REPACK_COMMAND_CLUSTER || cmd =3D=3D REPACK_COMMAND_REP=
ACK);
+
if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) =3D=3D ACLCHECK_OK)
return true;
=20
ereport(WARNING,
- (errmsg("permission denied to cluster \"%s\", skipping it",
- get_rel_name(relid))));
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd),
+ get_rel_name(relid)));
+
return false;
}
+
+
+/*
+ * Given a RepackStmt with an indicated relation name, resolve the relation
+ * name, obtain lock on it, then determine what to do based on the relation
+ * type: if it's table and not partitioned, repack it as indicated (using =
an
+ * existing clustered index, or following the given one), and return NULL.
+ *
+ * On the other hand, if the table is partitioned, do nothing further and
+ * instead return the opened and locked relcache entry, so that caller can
+ * process the partitions using the multiple-table handling code. In this
+ * case, if an index name is given, it's up to the caller to resolve it.
+ */
+static Relation
+process_single_relation(RepackStmt *stmt, ClusterParams *params)
+{
+ Relation rel;
+ Oid tableOid;
+
+ Assert(stmt->relation !=3D NULL);
+ Assert(stmt->command =3D=3D REPACK_COMMAND_CLUSTER ||
+ stmt->command =3D=3D REPACK_COMMAND_REPACK);
+
+ /*
+ * Find, lock, and check permissions on the table. We obtain
+ * AccessExclusiveLock right away to avoid lock-upgrade hazard in the
+ * single-transaction case.
+ */
+ tableOid =3D RangeVarGetRelidExtended(stmt->relation->relation,
+ AccessExclusiveLock,
+ 0,
+ RangeVarCallbackMaintainsTable,
+ NULL);
+ rel =3D table_open(tableOid, NoLock);
+
+ /*
+ * Reject clustering a remote temp table ... their local buffer manager is
+ * not going to cope.
+ */
+ if (RELATION_IS_OTHER_TEMP(rel))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot execute %s on temporary tables of other sessions",
+ RepackCommandAsString(stmt->command)));
+
+ /*
+ * Make sure ANALYZE is specified if a column list is present.
+ */
+ if ((params->options & CLUOPT_ANALYZE) =3D=3D 0 && stmt->relation->va_col=
s !=3D NIL)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("ANALYZE option must be specified when a column list is provide=
d"));
+
+ /*
+ * For partitioned tables, let caller handle this. Otherwise, process it
+ * here and we're done.
+ */
+ if (rel->rd_rel->relkind =3D=3D RELKIND_PARTITIONED_TABLE)
+ return rel;
+ else
+ {
+ Oid indexOid;
+
+ indexOid =3D determine_clustered_index(rel, stmt->usingindex,
+ stmt->indexname);
+ if (OidIsValid(indexOid))
+ check_index_is_clusterable(rel, indexOid, AccessExclusiveLock);
+ cluster_rel(stmt->command, rel, indexOid, params);
+
+ /* Do an analyze, if requested */
+ if (params->options & CLUOPT_ANALYZE)
+ {
+ VacuumParams vac_params =3D {0};
+
+ vac_params.options |=3D VACOPT_ANALYZE;
+ if (params->options & CLUOPT_VERBOSE)
+ vac_params.options |=3D VACOPT_VERBOSE;
+ analyze_rel(tableOid, NULL, vac_params,
+ stmt->relation->va_cols, true, NULL);
+ }
+
+ return NULL;
+ }
+}
+
+/*
+ * Given a relation and the usingindex/indexname options in a
+ * REPACK USING INDEX or CLUSTER command, return the OID of the
+ * index to use for clustering the table.
+ *
+ * Caller must hold lock on the relation so that the set of indexes
+ * doesn't change, and must call check_index_is_clusterable.
+ */
+static Oid
+determine_clustered_index(Relation rel, bool usingindex, const char *index=
name)
+{
+ Oid indexOid;
+
+ if (indexname =3D=3D NULL && usingindex)
+ {
+ /*
+ * If USING INDEX with no name is given, find a clustered index, or
+ * error out if none.
+ */
+ indexOid =3D InvalidOid;
+ foreach_oid(idxoid, RelationGetIndexList(rel))
+ {
+ if (get_index_isclustered(idxoid))
+ {
+ indexOid =3D idxoid;
+ break;
+ }
+ }
+
+ if (!OidIsValid(indexOid))
+ ereport(ERROR,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("there is no previously clustered index for table \"%s\"",
+ RelationGetRelationName(rel)));
+ }
+ else if (indexname !=3D NULL)
+ {
+ /* An index was specified; obtain its OID. */
+ indexOid =3D get_relname_relid(indexname, rel->rd_rel->relnamespace);
+ if (!OidIsValid(indexOid))
+ ereport(ERROR,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("index \"%s\" for table \"%s\" does not exist",
+ indexname, RelationGetRelationName(rel)));
+ }
+ else
+ indexOid =3D InvalidOid;
+
+ return indexOid;
+}
+
+static const char *
+RepackCommandAsString(RepackCommand cmd)
+{
+ switch (cmd)
+ {
+ case REPACK_COMMAND_REPACK:
+ return "REPACK";
+ case REPACK_COMMAND_VACUUMFULL:
+ return "VACUUM";
+ case REPACK_COMMAND_CLUSTER:
+ return "CLUSTER";
+ }
+ return "???"; /* keep compiler quiet */
+}
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 03932f45c8a..aea998260e1 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -351,7 +351,6 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, boo=
l isTopLevel)
}
}
=20
-
/*
* Sanity check DISABLE_PAGE_SKIPPING option.
*/
@@ -2289,8 +2288,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParam=
s params,
if ((params.options & VACOPT_VERBOSE) !=3D 0)
cluster_params.options |=3D CLUOPT_VERBOSE;
=20
- /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
- cluster_rel(rel, InvalidOid, &cluster_params);
+ /* VACUUM FULL is a variant of REPACK; see cluster.c */
+ cluster_rel(REPACK_COMMAND_VACUUMFULL, rel, InvalidOid,
+ &cluster_params);
/* cluster_rel closes the relation, but keeps lock */
=20
rel =3D NULL;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 713ee5c10a2..54d37c10447 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -287,7 +287,7 @@ static Node *makeRecursiveViewSelect(char *relname, Lis=
t *aliases, Node *query);
AlterCompositeTypeStmt AlterUserMappingStmt
AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt AlterStatsStmt
AlterDefaultPrivilegesStmt DefACLAction
- AnalyzeStmt CallStmt ClosePortalStmt ClusterStmt CommentStmt
+ AnalyzeStmt CallStmt ClosePortalStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
@@ -304,7 +304,7 @@ static Node *makeRecursiveViewSelect(char *relname, Lis=
t *aliases, Node *query);
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
ListenStmt LoadStmt LockStmt MergeStmt NotifyStmt ExplainableStmt Prepar=
ableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
- RemoveFuncStmt RemoveOperStmt RenameStmt ReturnStmt RevokeStmt RevokeRol=
eStmt
+ RemoveFuncStmt RemoveOperStmt RenameStmt RepackStmt ReturnStmt RevokeStm=
t RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
SecLabelStmt SelectStmt TransactionStmt TransactionStmtLegacy TruncateSt=
mt
UnlistenStmt UpdateStmt VacuumStmt
@@ -323,7 +323,7 @@ static Node *makeRecursiveViewSelect(char *relname, Lis=
t *aliases, Node *query);
=20
%type <str> opt_single_name
%type <list> opt_qualified_name
-%type <boolean> opt_concurrently
+%type <boolean> opt_concurrently opt_usingindex
%type <dbehavior> opt_drop_behavior
%type <list> opt_utility_option_list
%type <list> opt_wait_with_clause
@@ -773,7 +773,7 @@ static Node *makeRecursiveViewSelect(char *relname, Lis=
t *aliases, Node *query);
QUOTE QUOTES
=20
RANGE READ REAL REASSIGN RECURSIVE REF_P REFERENCES REFERENCING
- REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
+ REFRESH REINDEX RELATIVE_P RELEASE RENAME REPACK REPEATABLE REPLACE REPLI=
CA
RESET RESPECT_P RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT RO=
LE ROLLBACK ROLLUP
ROUTINE ROUTINES ROW ROWS RULE
=20
@@ -1035,7 +1035,6 @@ stmt:
| CallStmt
| CheckPointStmt
| ClosePortalStmt
- | ClusterStmt
| CommentStmt
| ConstraintsSetStmt
| CopyStmt
@@ -1109,6 +1108,7 @@ stmt:
| RemoveFuncStmt
| RemoveOperStmt
| RenameStmt
+ | RepackStmt
| RevokeStmt
| RevokeRoleStmt
| RuleStmt
@@ -1146,6 +1146,11 @@ opt_concurrently:
| /*EMPTY*/ { $$ =3D false; }
;
=20
+opt_usingindex:
+ USING INDEX { $$ =3D true; }
+ | /* EMPTY */ { $$ =3D false; }
+ ;
+
opt_drop_behavior:
CASCADE { $$ =3D DROP_CASCADE; }
| RESTRICT { $$ =3D DROP_RESTRICT; }
@@ -12036,38 +12041,82 @@ CreateConversionStmt:
/*************************************************************************=
****
*
* QUERY:
+ * REPACK [ (options) ] [ <qualified_name> [ <name_list> ] [ USING INDE=
X <index_name> ] ]
+ *
+ * obsolete variants:
* CLUSTER (options) [ <qualified_name> [ USING <index_name> ] ]
* CLUSTER [VERBOSE] [ <qualified_name> [ USING <index_name> ] ]
* CLUSTER [VERBOSE] <index_name> ON <qualified_name> (for pre-8.3)
*
*************************************************************************=
****/
=20
-ClusterStmt:
- CLUSTER '(' utility_option_list ')' qualified_name cluster_index_specif=
ication
+RepackStmt:
+ REPACK opt_utility_option_list vacuum_relation USING INDEX name
{
- ClusterStmt *n =3D makeNode(ClusterStmt);
+ RepackStmt *n =3D makeNode(RepackStmt);
=20
- n->relation =3D $5;
+ n->command =3D REPACK_COMMAND_REPACK;
+ n->relation =3D (VacuumRelation *) $3;
n->indexname =3D $6;
+ n->usingindex =3D true;
+ n->params =3D $2;
+ $$ =3D (Node *) n;
+ }
+ | REPACK opt_utility_option_list vacuum_relation opt_usingindex
+ {
+ RepackStmt *n =3D makeNode(RepackStmt);
+
+ n->command =3D REPACK_COMMAND_REPACK;
+ n->relation =3D (VacuumRelation *) $3;
+ n->indexname =3D NULL;
+ n->usingindex =3D $4;
+ n->params =3D $2;
+ $$ =3D (Node *) n;
+ }
+ | REPACK opt_utility_option_list opt_usingindex
+ {
+ RepackStmt *n =3D makeNode(RepackStmt);
+
+ n->command =3D REPACK_COMMAND_REPACK;
+ n->relation =3D NULL;
+ n->indexname =3D NULL;
+ n->usingindex =3D $3;
+ n->params =3D $2;
+ $$ =3D (Node *) n;
+ }
+ | CLUSTER '(' utility_option_list ')' qualified_name cluster_index_spec=
ification
+ {
+ RepackStmt *n =3D makeNode(RepackStmt);
+
+ n->command =3D REPACK_COMMAND_CLUSTER;
+ n->relation =3D makeNode(VacuumRelation);
+ n->relation->relation =3D $5;
+ n->indexname =3D $6;
+ n->usingindex =3D true;
n->params =3D $3;
$$ =3D (Node *) n;
}
| CLUSTER opt_utility_option_list
{
- ClusterStmt *n =3D makeNode(ClusterStmt);
+ RepackStmt *n =3D makeNode(RepackStmt);
=20
+ n->command =3D REPACK_COMMAND_CLUSTER;
n->relation =3D NULL;
n->indexname =3D NULL;
+ n->usingindex =3D true;
n->params =3D $2;
$$ =3D (Node *) n;
}
/* unparenthesized VERBOSE kept for pre-14 compatibility */
| CLUSTER opt_verbose qualified_name cluster_index_specification
{
- ClusterStmt *n =3D makeNode(ClusterStmt);
+ RepackStmt *n =3D makeNode(RepackStmt);
=20
- n->relation =3D $3;
+ n->command =3D REPACK_COMMAND_CLUSTER;
+ n->relation =3D makeNode(VacuumRelation);
+ n->relation->relation =3D $3;
n->indexname =3D $4;
+ n->usingindex =3D true;
if ($2)
n->params =3D list_make1(makeDefElem("verbose", NULL, @2));
$$ =3D (Node *) n;
@@ -12075,20 +12124,25 @@ ClusterStmt:
/* unparenthesized VERBOSE kept for pre-17 compatibility */
| CLUSTER VERBOSE
{
- ClusterStmt *n =3D makeNode(ClusterStmt);
+ RepackStmt *n =3D makeNode(RepackStmt);
=20
+ n->command =3D REPACK_COMMAND_CLUSTER;
n->relation =3D NULL;
n->indexname =3D NULL;
+ n->usingindex =3D true;
n->params =3D list_make1(makeDefElem("verbose", NULL, @2));
$$ =3D (Node *) n;
}
/* kept for pre-8.3 compatibility */
| CLUSTER opt_verbose name ON qualified_name
{
- ClusterStmt *n =3D makeNode(ClusterStmt);
+ RepackStmt *n =3D makeNode(RepackStmt);
=20
- n->relation =3D $5;
+ n->command =3D REPACK_COMMAND_CLUSTER;
+ n->relation =3D makeNode(VacuumRelation);
+ n->relation->relation =3D $5;
n->indexname =3D $3;
+ n->usingindex =3D true;
if ($2)
n->params =3D list_make1(makeDefElem("verbose", NULL, @2));
$$ =3D (Node *) n;
@@ -18127,6 +18181,7 @@ unreserved_keyword:
| RELATIVE_P
| RELEASE
| RENAME
+ | REPACK
| REPEATABLE
| REPLACE
| REPLICA
@@ -18764,6 +18819,7 @@ bare_label_keyword:
| RELATIVE_P
| RELEASE
| RENAME
+ | REPACK
| REPEATABLE
| REPLACE
| REPLICA
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 34dd6e18df5..ca737b05115 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -279,9 +279,9 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree)
return COMMAND_OK_IN_RECOVERY | COMMAND_OK_IN_READ_ONLY_TXN;
}
=20
- case T_ClusterStmt:
case T_ReindexStmt:
case T_VacuumStmt:
+ case T_RepackStmt:
{
/*
* These commands write WAL, so they're not strictly
@@ -856,14 +856,14 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ExecuteCallStmt(castNode(CallStmt, parsetree), params, isAtomicContext,=
dest);
break;
=20
- case T_ClusterStmt:
- cluster(pstate, (ClusterStmt *) parsetree, isTopLevel);
- break;
-
case T_VacuumStmt:
ExecVacuum(pstate, (VacuumStmt *) parsetree, isTopLevel);
break;
=20
+ case T_RepackStmt:
+ ExecRepack(pstate, (RepackStmt *) parsetree, isTopLevel);
+ break;
+
case T_ExplainStmt:
ExplainQuery(pstate, (ExplainStmt *) parsetree, params, dest);
break;
@@ -2865,10 +2865,6 @@ CreateCommandTag(Node *parsetree)
tag =3D CMDTAG_CALL;
break;
=20
- case T_ClusterStmt:
- tag =3D CMDTAG_CLUSTER;
- break;
-
case T_VacuumStmt:
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
tag =3D CMDTAG_VACUUM;
@@ -2876,6 +2872,13 @@ CreateCommandTag(Node *parsetree)
tag =3D CMDTAG_ANALYZE;
break;
=20
+ case T_RepackStmt:
+ if (((RepackStmt *) parsetree)->command =3D=3D REPACK_COMMAND_CLUSTER)
+ tag =3D CMDTAG_CLUSTER;
+ else
+ tag =3D CMDTAG_REPACK;
+ break;
+
case T_ExplainStmt:
tag =3D CMDTAG_EXPLAIN;
break;
@@ -3517,7 +3520,7 @@ GetCommandLogLevel(Node *parsetree)
lev =3D LOGSTMT_ALL;
break;
=20
- case T_ClusterStmt:
+ case T_RepackStmt:
lev =3D LOGSTMT_DDL;
break;
=20
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pg=
statfuncs.c
index 73ca0bb0b7f..55a69bf681d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -287,8 +287,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
cmdtype =3D PROGRESS_COMMAND_VACUUM;
else if (pg_strcasecmp(cmd, "ANALYZE") =3D=3D 0)
cmdtype =3D PROGRESS_COMMAND_ANALYZE;
- else if (pg_strcasecmp(cmd, "CLUSTER") =3D=3D 0)
- cmdtype =3D PROGRESS_COMMAND_CLUSTER;
+ else if (pg_strcasecmp(cmd, "REPACK") =3D=3D 0)
+ cmdtype =3D PROGRESS_COMMAND_REPACK;
else if (pg_strcasecmp(cmd, "CREATE INDEX") =3D=3D 0)
cmdtype =3D PROGRESS_COMMAND_CREATE_INDEX;
else if (pg_strcasecmp(cmd, "BASEBACKUP") =3D=3D 0)
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 8b91bc00062..2a1bb47ff03 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -1267,7 +1267,7 @@ static const char *const sql_commands[] =3D {
"DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
"FETCH", "GRANT", "IMPORT FOREIGN SCHEMA", "INSERT INTO", "LISTEN", "LOAD=
", "LOCK",
"MERGE INTO", "MOVE", "NOTIFY", "PREPARE",
- "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
+ "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE", "REPACK",
"RESET", "REVOKE", "ROLLBACK",
"SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
"TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES",
@@ -5086,6 +5086,46 @@ match_previous_words(int pattern_id,
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
}
=20
+/* REPACK */
+ else if (Matches("REPACK"))
+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_clusterables,
+ "(", "USING INDEX");
+ else if (Matches("REPACK", "(*)"))
+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_clusterables,
+ "USING INDEX");
+ else if (Matches("REPACK", MatchAnyExcept("(")))
+ COMPLETE_WITH("USING INDEX");
+ else if (Matches("REPACK", "(*)", MatchAnyExcept("(")))
+ COMPLETE_WITH("USING INDEX");
+ else if (Matches("REPACK", MatchAny, "USING", "INDEX") ||
+ Matches("REPACK", "(*)", MatchAny, "USING", "INDEX"))
+ {
+ set_completion_reference(prev3_wd);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
+ }
+ /*
+ * Complete ... [ (*) ] <sth> USING INDEX, with a list of indexes for
+ * <sth>.
+ */
+ else if (TailMatches(MatchAny, "USING", "INDEX"))
+ {
+ set_completion_reference(prev3_wd);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
+ }
+ else if (HeadMatches("REPACK", "(*") &&
+ !HeadMatches("REPACK", "(*)"))
+ {
+ /*
+ * This fires if we're in an unfinished parenthesized option list.
+ * get_previous_words treats a completed parenthesized option list as
+ * one word, so the above test is correct.
+ */
+ if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
+ COMPLETE_WITH("ANALYZE", "VERBOSE");
+ else if (TailMatches("ANALYZE", "VERBOSE"))
+ COMPLETE_WITH("ON", "OFF");
+ }
+
/* SECURITY LABEL */
else if (Matches("SECURITY"))
COMPLETE_WITH("LABEL");
diff --git a/src/bin/scripts/Makefile b/src/bin/scripts/Makefile
index e6cd9ef4af5..f964a5803ca 100644
--- a/src/bin/scripts/Makefile
+++ b/src/bin/scripts/Makefile
@@ -23,6 +23,7 @@ PROGRAMS =3D \
dropdb \
dropuser \
pg_isready \
+ pg_repackdb \
reindexdb \
vacuumdb
=20
@@ -39,6 +40,7 @@ clusterdb: clusterdb.o common.o $(WIN32RES) | submake-lib=
pq submake-libpgport su
vacuumdb: vacuumdb.o vacuuming.o common.o $(WIN32RES) | submake-libpq subm=
ake-libpgport submake-libpgfeutils
reindexdb: reindexdb.o common.o $(WIN32RES) | submake-libpq submake-libpgp=
ort submake-libpgfeutils
pg_isready: pg_isready.o common.o $(WIN32RES) | submake-libpq submake-libp=
gport submake-libpgfeutils
+pg_repackdb: pg_repackdb.o vacuuming.o common.o $(WIN32RES) | submake-libp=
q submake-libpgport submake-libpgfeutils
=20
install: all installdirs
$(INSTALL_PROGRAM) createdb$(X) '$(DESTDIR)$(bindir)'/createdb$(X)
@@ -49,6 +51,7 @@ install: all installdirs
$(INSTALL_PROGRAM) vacuumdb$(X) '$(DESTDIR)$(bindir)'/vacuumdb$(X)
$(INSTALL_PROGRAM) reindexdb$(X) '$(DESTDIR)$(bindir)'/reindexdb$(X)
$(INSTALL_PROGRAM) pg_isready$(X) '$(DESTDIR)$(bindir)'/pg_isready$(X)
+ $(INSTALL_PROGRAM) pg_repackdb$(X) '$(DESTDIR)$(bindir)'/pg_repackdb$(X)
=20
installdirs:
$(MKDIR_P) '$(DESTDIR)$(bindir)'
diff --git a/src/bin/scripts/meson.build b/src/bin/scripts/meson.build
index c083ec38099..1e88bacebba 100644
--- a/src/bin/scripts/meson.build
+++ b/src/bin/scripts/meson.build
@@ -42,6 +42,7 @@ vacuuming_common =3D static_library('libvacuuming_common',
=20
binaries =3D [
'vacuumdb',
+ 'pg_repackdb',
]
foreach binary : binaries
binary_sources =3D files('@[email protected]'.format(binary))
@@ -80,6 +81,7 @@ tests +=3D {
't/100_vacuumdb.pl',
't/101_vacuumdb_all.pl',
't/102_vacuumdb_stages.pl',
+ 't/103_repackdb.pl',
't/200_connstr.pl',
],
},
diff --git a/src/bin/scripts/pg_repackdb.c b/src/bin/scripts/pg_repackdb.c
new file mode 100644
index 00000000000..2765d1e97b8
--- /dev/null
+++ b/src/bin/scripts/pg_repackdb.c
@@ -0,0 +1,240 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_repackdb
+ * An utility to run REPACK
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * FIXME: this is missing a way to specify the index to use to repack one
+ * table, or whether to pass a WITH INDEX clause when multiple tables are
+ * used. Something like --index[=3Dindexname]. Adding that bleeds into
+ * vacuuming.c as well.
+ *
+ * src/bin/scripts/pg_repackdb.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <limits.h>
+
+#include "common.h"
+#include "common/logging.h"
+#include "fe_utils/option_utils.h"
+#include "vacuuming.h"
+
+static void help(const char *progname);
+static void check_objfilter(bits32 objfilter);
+
+int
+main(int argc, char *argv[])
+{
+ static struct option long_options[] =3D {
+ {"host", required_argument, NULL, 'h'},
+ {"port", required_argument, NULL, 'p'},
+ {"username", required_argument, NULL, 'U'},
+ {"no-password", no_argument, NULL, 'w'},
+ {"password", no_argument, NULL, 'W'},
+ {"echo", no_argument, NULL, 'e'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"dbname", required_argument, NULL, 'd'},
+ {"analyze", no_argument, NULL, 'z'},
+ {"all", no_argument, NULL, 'a'},
+ /* XXX this could be 'i', but optional_arg is messy */
+ {"index", optional_argument, NULL, 1},
+ {"table", required_argument, NULL, 't'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"jobs", required_argument, NULL, 'j'},
+ {"schema", required_argument, NULL, 'n'},
+ {"exclude-schema", required_argument, NULL, 'N'},
+ {"maintenance-db", required_argument, NULL, 2},
+ {NULL, 0, NULL, 0}
+ };
+
+ const char *progname;
+ int optindex;
+ int c;
+ const char *dbname =3D NULL;
+ const char *maintenance_db =3D NULL;
+ ConnParams cparams;
+ vacuumingOptions vacopts;
+ SimpleStringList objects =3D {NULL, NULL};
+ int concurrentCons =3D 1;
+ int tbl_count =3D 0;
+ int ret;
+
+ /* initialize options */
+ memset(&vacopts, 0, sizeof(vacopts));
+ vacopts.mode =3D MODE_REPACK;
+
+ /* the same for connection parameters */
+ memset(&cparams, 0, sizeof(cparams));
+ cparams.prompt_password =3D TRI_DEFAULT;
+
+ pg_logging_init(argv[0]);
+ progname =3D get_progname(argv[0]);
+ set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts"));
+
+ handle_help_version_opts(argc, argv, progname, help);
+
+ while ((c =3D getopt_long(argc, argv, "ad:eh:j:n:N:p:qt:U:vwWz",
+ long_options, &optindex)) !=3D -1)
+ {
+ switch (c)
+ {
+ case 'a':
+ vacopts.objfilter |=3D OBJFILTER_ALL_DBS;
+ break;
+ case 'd':
+ vacopts.objfilter |=3D OBJFILTER_DATABASE;
+ dbname =3D pg_strdup(optarg);
+ break;
+ case 'e':
+ vacopts.echo =3D true;
+ break;
+ case 'h':
+ cparams.pghost =3D pg_strdup(optarg);
+ break;
+ case 'j':
+ if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX,
+ &concurrentCons))
+ exit(1);
+ break;
+ case 'n':
+ vacopts.objfilter |=3D OBJFILTER_SCHEMA;
+ simple_string_list_append(&objects, optarg);
+ break;
+ case 'N':
+ vacopts.objfilter |=3D OBJFILTER_SCHEMA_EXCLUDE;
+ simple_string_list_append(&objects, optarg);
+ break;
+ case 'p':
+ cparams.pgport =3D pg_strdup(optarg);
+ break;
+ case 'q':
+ vacopts.quiet =3D true;
+ break;
+ case 't':
+ vacopts.objfilter |=3D OBJFILTER_TABLE;
+ simple_string_list_append(&objects, optarg);
+ tbl_count++;
+ break;
+ case 'U':
+ cparams.pguser =3D pg_strdup(optarg);
+ break;
+ case 'v':
+ vacopts.verbose =3D true;
+ break;
+ case 'w':
+ cparams.prompt_password =3D TRI_NO;
+ break;
+ case 'W':
+ cparams.prompt_password =3D TRI_YES;
+ break;
+ case 'z':
+ vacopts.and_analyze =3D true;
+ break;
+ case 1:
+ vacopts.using_index =3D true;
+ if (optarg)
+ vacopts.indexname =3D pg_strdup(optarg);
+ else
+ vacopts.indexname =3D NULL;
+ break;
+ case 2:
+ maintenance_db =3D pg_strdup(optarg);
+ break;
+ default:
+ /* getopt_long already emitted a complaint */
+ pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ exit(1);
+ }
+ }
+
+ /*
+ * Non-option argument specifies database name as long as it wasn't
+ * already specified with -d / --dbname
+ */
+ if (optind < argc && dbname =3D=3D NULL)
+ {
+ vacopts.objfilter |=3D OBJFILTER_DATABASE;
+ dbname =3D argv[optind];
+ optind++;
+ }
+
+ if (optind < argc)
+ {
+ pg_log_error("too many command-line arguments (first is \"%s\")",
+ argv[optind]);
+ pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ exit(1);
+ }
+
+ /*
+ * Validate the combination of filters specified in the command-line
+ * options.
+ */
+ check_objfilter(vacopts.objfilter);
+
+ ret =3D vacuuming_main(&cparams, dbname, maintenance_db, &vacopts,
+ &objects, tbl_count, concurrentCons,
+ progname);
+ exit(ret);
+}
+
+/*
+ * Verify that the filters used at command line are compatible.
+ */
+void
+check_objfilter(bits32 objfilter)
+{
+ if ((objfilter & OBJFILTER_ALL_DBS) &&
+ (objfilter & OBJFILTER_DATABASE))
+ pg_fatal("cannot repack all databases and a specific one at the same tim=
e");
+
+ if ((objfilter & OBJFILTER_TABLE) &&
+ (objfilter & OBJFILTER_SCHEMA))
+ pg_fatal("cannot repack all tables in schema(s) and specific table(s) at=
the same time");
+
+ if ((objfilter & OBJFILTER_TABLE) &&
+ (objfilter & OBJFILTER_SCHEMA_EXCLUDE))
+ pg_fatal("cannot repack specific table(s) and exclude schema(s) at the s=
ame time");
+
+ if ((objfilter & OBJFILTER_SCHEMA) &&
+ (objfilter & OBJFILTER_SCHEMA_EXCLUDE))
+ pg_fatal("cannot repack all tables in schema(s) and exclude schema(s) at=
the same time");
+}
+
+static void
+help(const char *progname)
+{
+ printf(_("%s repacks a PostgreSQL database.\n\n"), progname);
+ printf(_("Usage:\n"));
+ printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
+ printf(_("\nOptions:\n"));
+ printf(_(" -a, --all repack all databases\n"));
+ printf(_(" -d, --dbname=3DDBNAME database to repack\n"));
+ printf(_(" -e, --echo show the commands being sent =
to the server\n"));
+ printf(_(" --index[=3DINDEX] repack following an index\n=
"));
+ printf(_(" -j, --jobs=3DNUM use this many concurrent co=
nnections to repack\n"));
+ printf(_(" -n, --schema=3DSCHEMA repack tables in the specif=
ied schema(s) only\n"));
+ printf(_(" -N, --exclude-schema=3DSCHEMA do not repack tables in the=
specified schema(s)\n"));
+ printf(_(" -q, --quiet don't write any messages\n"));
+ printf(_(" -t, --table=3D'TABLE[(COLUMNS)]' repack specific table(s) on=
ly\n"));
+ printf(_(" -v, --verbose write a lot of output\n"));
+ printf(_(" -V, --version output version information, t=
hen exit\n"));
+ printf(_(" -z, --analyze update optimizer statistics\n=
"));
+ printf(_(" -?, --help show this help, then exit\n")=
);
+ printf(_("\nConnection options:\n"));
+ printf(_(" -h, --host=3DHOSTNAME database server host or socket di=
rectory\n"));
+ printf(_(" -p, --port=3DPORT database server port\n"));
+ printf(_(" -U, --username=3DUSERNAME user name to connect as\n"));
+ printf(_(" -w, --no-password never prompt for password\n"));
+ printf(_(" -W, --password force password prompt\n"));
+ printf(_(" --maintenance-db=3DDBNAME alternate maintenance database\n"=
));
+ printf(_("\nRead the description of the SQL command REPACK for details.\n=
"));
+ printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
+}
diff --git a/src/bin/scripts/t/103_repackdb.pl b/src/bin/scripts/t/103_repa=
ckdb.pl
new file mode 100644
index 00000000000..44113367a61
--- /dev/null
+++ b/src/bin/scripts/t/103_repackdb.pl
@@ -0,0 +1,47 @@
+# Copyright (c) 2021-2026, PostgreSQL Global Development Group
+
+use strict;
+use warnings FATAL =3D> 'all';
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+program_help_ok('pg_repackdb');
+program_version_ok('pg_repackdb');
+program_options_handling_ok('pg_repackdb');
+
+my $node =3D PostgreSQL::Test::Cluster->new('main');
+$node->init;
+$node->start;
+
+$node->issues_sql_like(
+ [ 'pg_repackdb', 'postgres', '-t', 'pg_class'],
+ qr/statement: REPACK.*pg_class;/,
+ 'pg_repackdb processes a single table');
+
+$node->safe_psql('postgres', 'CREATE USER testusr;
+ GRANT CREATE ON SCHEMA public TO testusr');
+$node->safe_psql('postgres',
+ 'CREATE TABLE cluster_1 (a int primary key);
+ ALTER TABLE cluster_1 CLUSTER ON cluster_1_pkey;
+ CREATE TABLE cluster_2 (a int unique);
+ ALTER TABLE cluster_2 CLUSTER ON cluster_2_a_key;',
+ extra_params =3D> ['-U' =3D> 'testusr']);
+
+$node->issues_sql_like(
+ [ 'pg_repackdb', 'postgres', '-U', 'testusr' ],
+ qr/statement: REPACK.*;/,
+ 'SQL REPACK run');
+
+$node->issues_sql_like(
+ [ 'pg_repackdb', 'postgres', '--index'],
+ qr/statement: REPACK.*cluster_1 USING INDEX.*statement: REPACK.*cluster_2=
USING INDEX/ms,
+ 'pg_repackdb --index chooses multiple tables');
+
+$node->issues_sql_like(
+ [ 'pg_repackdb', 'postgres', '--analyze', '-t', 'cluster_1'],
+ qr/statement: REPACK \(ANALYZE\) public.cluster_1/,
+ 'pg_repackdb --analyze works');
+
+done_testing();
diff --git a/src/bin/scripts/vacuuming.c b/src/bin/scripts/vacuuming.c
index faac9089a01..0fa7be3c0c3 100644
--- a/src/bin/scripts/vacuuming.c
+++ b/src/bin/scripts/vacuuming.c
@@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
* vacuuming.c
- * Helper routines for vacuumdb
+ * Helper routines for vacuumdb and pg_repackdb
*
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -194,6 +194,14 @@ vacuum_one_database(ConnParams *cparams,
=20
conn =3D connectDatabase(cparams, progname, vacopts->echo, false, true);
=20
+ if (vacopts->mode =3D=3D MODE_REPACK && PQserverVersion(conn) < 190000)
+ {
+ /* XXX arguably, here we should use VACUUM FULL instead of failing */
+ PQfinish(conn);
+ pg_fatal("cannot use the \"%s\" command on server versions older than Po=
stgreSQL %s",
+ "REPACK", "19");
+ }
+
if (vacopts->disable_page_skipping && PQserverVersion(conn) < 90600)
{
PQfinish(conn);
@@ -286,9 +294,18 @@ vacuum_one_database(ConnParams *cparams,
if (vacopts->mode =3D=3D MODE_ANALYZE_IN_STAGES)
printf(_("%s: processing database \"%s\": %s\n"),
progname, PQdb(conn), _(stage_messages[stage]));
- else
+ else if (vacopts->mode =3D=3D MODE_ANALYZE)
+ printf(_("%s: analyzing database \"%s\"\n"),
+ progname, PQdb(conn));
+ else if (vacopts->mode =3D=3D MODE_VACUUM)
printf(_("%s: vacuuming database \"%s\"\n"),
progname, PQdb(conn));
+ else
+ {
+ Assert(vacopts->mode =3D=3D MODE_REPACK);
+ printf(_("%s: repacking database \"%s\"\n"),
+ progname, PQdb(conn));
+ }
fflush(stdout);
}
=20
@@ -640,6 +657,35 @@ retrieve_objects(PGconn *conn, vacuumingOptions *vacop=
ts,
" AND listed_objects.object_oid IS NOT NULL\n");
}
=20
+ /*
+ * In REPACK mode, if the 'using_index' option was given but no index
+ * name, filter only tables that have an index with indisclustered set.
+ * (If an index name is given, we trust the user to pass a reasonable list
+ * of tables.)
+ *
+ * XXX it may be worth printing an error if an index name is given with no
+ * list of tables.
+ */
+ if (vacopts->mode =3D=3D MODE_REPACK &&
+ vacopts->using_index && !vacopts->indexname)
+ {
+ appendPQExpBufferStr(&catalog_query,
+ " AND EXISTS (SELECT 1 FROM pg_catalog.pg_index\n"
+ " WHERE indrelid =3D c.oid AND indisclustered)\n");
+ }
+
+ /*
+ * In REPACK mode, only consider the tables that the current user has
+ * MAINTAIN privileges on. XXX maybe we should do this in all cases, not
+ * just REPACK. The vacuumdb output is too noisy for no reason.
+ */
+ if (vacopts->mode =3D=3D MODE_REPACK)
+ {
+ appendPQExpBufferStr(&catalog_query,
+ " AND pg_catalog.has_table_privilege(current_user, "
+ "c.oid, 'MAINTAIN')\n");
+ }
+
/*
* If no tables were listed, filter for the relevant relation types. If
* tables were given via --table, don't bother filtering by relation type.
@@ -878,8 +924,10 @@ prepare_vacuum_command(PGconn *conn, PQExpBuffer sql,
if (vacopts->verbose)
appendPQExpBufferStr(sql, " VERBOSE");
}
+
+ appendPQExpBuffer(sql, " %s", table);
}
- else
+ else if (vacopts->mode =3D=3D MODE_VACUUM)
{
appendPQExpBufferStr(sql, "VACUUM");
=20
@@ -993,9 +1041,39 @@ prepare_vacuum_command(PGconn *conn, PQExpBuffer sql,
if (vacopts->and_analyze)
appendPQExpBufferStr(sql, " ANALYZE");
}
+
+ appendPQExpBuffer(sql, " %s", table);
}
+ else if (vacopts->mode =3D=3D MODE_REPACK)
+ {
+ appendPQExpBufferStr(sql, "REPACK");
=20
- appendPQExpBuffer(sql, " %s;", table);
+ if (vacopts->verbose)
+ {
+ appendPQExpBuffer(sql, "%sVERBOSE", sep);
+ sep =3D comma;
+ }
+ if (vacopts->and_analyze)
+ {
+ appendPQExpBuffer(sql, "%sANALYZE", sep);
+ sep =3D comma;
+ }
+
+ if (sep !=3D paren)
+ appendPQExpBufferChar(sql, ')');
+
+ appendPQExpBuffer(sql, " %s", table);
+
+ if (vacopts->using_index)
+ {
+ appendPQExpBuffer(sql, " USING INDEX");
+ if (vacopts->indexname)
+ appendPQExpBuffer(sql, " %s", fmtIdEnc(vacopts->indexname,
+ PQclientEncoding(conn)));
+ }
+ }
+
+ appendPQExpBufferChar(sql, ';');
}
=20
/*
@@ -1024,13 +1102,21 @@ run_vacuum_command(ParallelSlot *free_slot, vacuumi=
ngOptions *vacopts,
{
if (table)
{
- pg_log_error("vacuuming of table \"%s\" in database \"%s\" failed: %s",
- table, PQdb(conn), PQerrorMessage(conn));
+ if (vacopts->mode =3D=3D MODE_VACUUM)
+ pg_log_error("vacuuming of table \"%s\" in database \"%s\" failed: %s",
+ table, PQdb(conn), PQerrorMessage(conn));
+ else
+ pg_log_error("repacking of table \"%s\" in database \"%s\" failed: %s",
+ table, PQdb(conn), PQerrorMessage(conn));
}
else
{
- pg_log_error("vacuuming of database \"%s\" failed: %s",
- PQdb(conn), PQerrorMessage(conn));
+ if (vacopts->mode =3D=3D MODE_VACUUM)
+ pg_log_error("vacuuming of database \"%s\" failed: %s",
+ PQdb(conn), PQerrorMessage(conn));
+ else
+ pg_log_error("repacking of database \"%s\" failed: %s",
+ PQdb(conn), PQerrorMessage(conn));
}
}
}
diff --git a/src/bin/scripts/vacuuming.h b/src/bin/scripts/vacuuming.h
index be4a75ef2f4..6e8abe00a4f 100644
--- a/src/bin/scripts/vacuuming.h
+++ b/src/bin/scripts/vacuuming.h
@@ -20,6 +20,7 @@
typedef enum
{
MODE_VACUUM,
+ MODE_REPACK,
MODE_ANALYZE,
MODE_ANALYZE_IN_STAGES
} RunMode;
@@ -37,6 +38,8 @@ typedef struct vacuumingOptions
bool and_analyze;
bool full;
bool freeze;
+ bool using_index;
+ char *indexname;
bool disable_page_skipping;
bool skip_locked;
int min_xid_age;
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index 8ea81622f9d..28741988478 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -24,6 +24,7 @@
#define CLUOPT_RECHECK 0x02 /* recheck relation state */
#define CLUOPT_RECHECK_ISCLUSTERED 0x04 /* recheck relation state for
* indisclustered */
+#define CLUOPT_ANALYZE 0x08 /* do an ANALYZE */
=20
/* options for CLUSTER */
typedef struct ClusterParams
@@ -31,8 +32,11 @@ typedef struct ClusterParams
bits32 options; /* bitmask of CLUOPT_* */
} ClusterParams;
=20
-extern void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel=
);
-extern void cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *par=
ams);
+
+extern void ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLev=
el);
+
+extern void cluster_rel(RepackCommand command, Relation OldHeap, Oid index=
Oid,
+ ClusterParams *params);
extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
LOCKMODE lockmode);
extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_inter=
nal);
diff --git a/src/include/commands/progress.h b/src/include/commands/progres=
s.h
index 359221dc296..f00e39b937d 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -73,28 +73,34 @@
#define PROGRESS_ANALYZE_STARTED_BY_MANUAL 1
#define PROGRESS_ANALYZE_STARTED_BY_AUTOVACUUM 2
=20
-/* Progress parameters for cluster */
-#define PROGRESS_CLUSTER_COMMAND 0
-#define PROGRESS_CLUSTER_PHASE 1
-#define PROGRESS_CLUSTER_INDEX_RELID 2
-#define PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED 3
-#define PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN 4
-#define PROGRESS_CLUSTER_TOTAL_HEAP_BLKS 5
-#define PROGRESS_CLUSTER_HEAP_BLKS_SCANNED 6
-#define PROGRESS_CLUSTER_INDEX_REBUILD_COUNT 7
-
-/* Phases of cluster (as advertised via PROGRESS_CLUSTER_PHASE) */
-#define PROGRESS_CLUSTER_PHASE_SEQ_SCAN_HEAP 1
-#define PROGRESS_CLUSTER_PHASE_INDEX_SCAN_HEAP 2
-#define PROGRESS_CLUSTER_PHASE_SORT_TUPLES 3
-#define PROGRESS_CLUSTER_PHASE_WRITE_NEW_HEAP 4
-#define PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES 5
-#define PROGRESS_CLUSTER_PHASE_REBUILD_INDEX 6
-#define PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP 7
-
-/* Commands of PROGRESS_CLUSTER */
-#define PROGRESS_CLUSTER_COMMAND_CLUSTER 1
-#define PROGRESS_CLUSTER_COMMAND_VACUUM_FULL 2
+/*
+ * Progress parameters for REPACK.
+ *
+ * Values for PROGRESS_REPACK_COMMAND are defined as in RepackCommand.
+ *
+ * Note: Since REPACK shares code with CLUSTER, these values are also
+ * used by CLUSTER. (CLUSTER is now deprecated, so it makes little sense to
+ * introduce a separate set of constants.)
+ */
+#define PROGRESS_REPACK_COMMAND 0
+#define PROGRESS_REPACK_PHASE 1
+#define PROGRESS_REPACK_INDEX_RELID 2
+#define PROGRESS_REPACK_HEAP_TUPLES_SCANNED 3
+#define PROGRESS_REPACK_HEAP_TUPLES_WRITTEN 4
+#define PROGRESS_REPACK_TOTAL_HEAP_BLKS 5
+#define PROGRESS_REPACK_HEAP_BLKS_SCANNED 6
+#define PROGRESS_REPACK_INDEX_REBUILD_COUNT 7
+
+/*
+ * Phases of repack (as advertised via PROGRESS_REPACK_PHASE).
+ */
+#define PROGRESS_REPACK_PHASE_SEQ_SCAN_HEAP 1
+#define PROGRESS_REPACK_PHASE_INDEX_SCAN_HEAP 2
+#define PROGRESS_REPACK_PHASE_SORT_TUPLES 3
+#define PROGRESS_REPACK_PHASE_WRITE_NEW_HEAP 4
+#define PROGRESS_REPACK_PHASE_SWAP_REL_FILES 5
+#define PROGRESS_REPACK_PHASE_REBUILD_INDEX 6
+#define PROGRESS_REPACK_PHASE_FINAL_CLEANUP 7
=20
/* Progress parameters for CREATE INDEX */
/* 3, 4 and 5 reserved for "waitfor" metrics */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 646d6ced763..c15ba5e6f29 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3980,18 +3980,6 @@ typedef struct AlterSystemStmt
VariableSetStmt *setstmt; /* SET subcommand */
} AlterSystemStmt;
=20
-/* ----------------------
- * Cluster Statement (support pbrown's cluster index implementation)
- * ----------------------
- */
-typedef struct ClusterStmt
-{
- NodeTag type;
- RangeVar *relation; /* relation being indexed, or NULL if all */
- char *indexname; /* original index defined */
- List *params; /* list of DefElem nodes */
-} ClusterStmt;
-
/* ----------------------
* Vacuum and Analyze Statements
*
@@ -4004,7 +3992,7 @@ typedef struct VacuumStmt
NodeTag type;
List *options; /* list of DefElem nodes */
List *rels; /* list of VacuumRelation, or NIL for all */
- bool is_vacuumcmd; /* true for VACUUM, false for ANALYZE */
+ bool is_vacuumcmd; /* true for VACUUM, false otherwise */
} VacuumStmt;
=20
/*
@@ -4022,6 +4010,27 @@ typedef struct VacuumRelation
List *va_cols; /* list of column names, or NIL for all */
} VacuumRelation;
=20
+/* ----------------------
+ * Repack Statement
+ * ----------------------
+ */
+typedef enum RepackCommand
+{
+ REPACK_COMMAND_CLUSTER =3D 1,
+ REPACK_COMMAND_REPACK,
+ REPACK_COMMAND_VACUUMFULL,
+} RepackCommand;
+
+typedef struct RepackStmt
+{
+ NodeTag type;
+ RepackCommand command; /* type of command being run */
+ VacuumRelation *relation; /* relation being repacked */
+ char *indexname; /* order tuples by this index */
+ bool usingindex; /* whether USING INDEX is specified */
+ List *params; /* list of DefElem nodes */
+} RepackStmt;
+
/* ----------------------
* Explain Statement
*
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f7753c5c8a8..6f74a8c05c7 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -377,6 +377,7 @@ PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD, BARE=
_LABEL)
PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("release", RELEASE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("repack", REPACK, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("replica", REPLICA, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/include/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h
index 1290c9bab68..652dc61b834 100644
--- a/src/include/tcop/cmdtaglist.h
+++ b/src/include/tcop/cmdtaglist.h
@@ -196,6 +196,7 @@ PG_CMDTAG(CMDTAG_REASSIGN_OWNED, "REASSIGN OWNED", fals=
e, false, false)
PG_CMDTAG(CMDTAG_REFRESH_MATERIALIZED_VIEW, "REFRESH MATERIALIZED VIEW", t=
rue, false, false)
PG_CMDTAG(CMDTAG_REINDEX, "REINDEX", true, false, false)
PG_CMDTAG(CMDTAG_RELEASE, "RELEASE", false, false, false)
+PG_CMDTAG(CMDTAG_REPACK, "REPACK", false, false, false)
PG_CMDTAG(CMDTAG_RESET, "RESET", false, false, false)
PG_CMDTAG(CMDTAG_REVOKE, "REVOKE", true, false, false)
PG_CMDTAG(CMDTAG_REVOKE_ROLE, "REVOKE ROLE", false, false, false)
diff --git a/src/include/utils/backend_progress.h b/src/include/utils/backe=
nd_progress.h
index 19f63b41431..6300dbd15d5 100644
--- a/src/include/utils/backend_progress.h
+++ b/src/include/utils/backend_progress.h
@@ -24,10 +24,10 @@ typedef enum ProgressCommandType
PROGRESS_COMMAND_INVALID,
PROGRESS_COMMAND_VACUUM,
PROGRESS_COMMAND_ANALYZE,
- PROGRESS_COMMAND_CLUSTER,
PROGRESS_COMMAND_CREATE_INDEX,
PROGRESS_COMMAND_BASEBACKUP,
PROGRESS_COMMAND_COPY,
+ PROGRESS_COMMAND_REPACK,
} ProgressCommandType;
=20
#define PGSTAT_NUM_PROGRESS_PARAM 20
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expec=
ted/cluster.out
index 4d40a6809ab..277854418fa 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -495,6 +495,43 @@ ALTER TABLE clstrpart SET WITHOUT CLUSTER;
ERROR: cannot mark index clustered in partitioned table
ALTER TABLE clstrpart CLUSTER ON clstrpart_idx;
ERROR: cannot mark index clustered in partitioned table
+-- Check that REPACK sets new relfilenodes: it should process exactly the =
same
+-- tables as CLUSTER did.
+DROP TABLE old_cluster_info;
+DROP TABLE new_cluster_info;
+CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, =
relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class=
c ON c.oid=3Dtree.relid ;
+REPACK clstrpart USING INDEX clstrpart_idx;
+CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, =
relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class=
c ON c.oid=3Dtree.relid ;
+SELECT relname, old.level, old.relkind, old.relfilenode =3D new.relfilenod=
e FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname)=
ORDER BY relname COLLATE "C";
+ relname | level | relkind | ?column?=20
+-------------+-------+---------+----------
+ clstrpart | 0 | p | t
+ clstrpart1 | 1 | p | t
+ clstrpart11 | 2 | r | f
+ clstrpart12 | 2 | p | t
+ clstrpart2 | 1 | r | f
+ clstrpart3 | 1 | p | t
+ clstrpart33 | 2 | r | f
+(7 rows)
+
+-- And finally the same for REPACK w/o index.
+DROP TABLE old_cluster_info;
+DROP TABLE new_cluster_info;
+CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, =
relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class=
c ON c.oid=3Dtree.relid ;
+REPACK clstrpart;
+CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, =
relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class=
c ON c.oid=3Dtree.relid ;
+SELECT relname, old.level, old.relkind, old.relfilenode =3D new.relfilenod=
e FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname)=
ORDER BY relname COLLATE "C";
+ relname | level | relkind | ?column?=20
+-------------+-------+---------+----------
+ clstrpart | 0 | p | t
+ clstrpart1 | 1 | p | t
+ clstrpart11 | 2 | r | f
+ clstrpart12 | 2 | p | t
+ clstrpart2 | 1 | r | f
+ clstrpart3 | 1 | p | t
+ clstrpart33 | 2 | r | f
+(7 rows)
+
DROP TABLE clstrpart;
-- Ownership of partitions is checked
CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
@@ -513,7 +550,7 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=3Dtree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
-WARNING: permission denied to cluster "ptnowner2", skipping it
+WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=3Db.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -665,6 +702,101 @@ SELECT * FROM clstr_expression WHERE -a =3D -3 ORDER =
BY -a, b;
(4 rows)
=20
COMMIT;
+----------------------------------------------------------------------
+--
+-- REPACK
+--
+----------------------------------------------------------------------
+-- REPACK handles individual tables identically to CLUSTER, but it's worth
+-- checking if it handles table hierarchies identically as well.
+REPACK clstr_tst USING INDEX clstr_tst_c;
+-- Verify that inheritance link still works
+INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table 2');
+SELECT a,b,c,substring(d for 30), length(d) from clstr_tst;
+ a | b | c | substring | length=20
+----+-----+------------------+--------------------------------+--------
+ 10 | 14 | catorce | |=20=20=20=
=20=20=20=20
+ 18 | 5 | cinco | |=20=20=20=
=20=20=20=20
+ 9 | 4 | cuatro | |=20=20=20=
=20=20=20=20
+ 26 | 19 | diecinueve | |=20=20=20=
=20=20=20=20
+ 12 | 18 | dieciocho | |=20=20=20=
=20=20=20=20
+ 30 | 16 | dieciseis | |=20=20=20=
=20=20=20=20
+ 24 | 17 | diecisiete | |=20=20=20=
=20=20=20=20
+ 2 | 10 | diez | |=20=20=20=
=20=20=20=20
+ 23 | 12 | doce | |=20=20=20=
=20=20=20=20
+ 11 | 2 | dos | |=20=20=20=
=20=20=20=20
+ 25 | 9 | nueve | |=20=20=20=
=20=20=20=20
+ 31 | 8 | ocho | |=20=20=20=
=20=20=20=20
+ 1 | 11 | once | |=20=20=20=
=20=20=20=20
+ 28 | 15 | quince | |=20=20=20=
=20=20=20=20
+ 32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
+ 29 | 7 | siete | |=20=20=20=
=20=20=20=20
+ 15 | 13 | trece | |=20=20=20=
=20=20=20=20
+ 22 | 30 | treinta | |=20=20=20=
=20=20=20=20
+ 17 | 32 | treinta y dos | |=20=20=20=
=20=20=20=20
+ 3 | 31 | treinta y uno | |=20=20=20=
=20=20=20=20
+ 5 | 3 | tres | |=20=20=20=
=20=20=20=20
+ 20 | 1 | uno | |=20=20=20=
=20=20=20=20
+ 6 | 20 | veinte | |=20=20=20=
=20=20=20=20
+ 14 | 25 | veinticinco | |=20=20=20=
=20=20=20=20
+ 21 | 24 | veinticuatro | |=20=20=20=
=20=20=20=20
+ 4 | 22 | veintidos | |=20=20=20=
=20=20=20=20
+ 19 | 29 | veintinueve | |=20=20=20=
=20=20=20=20
+ 16 | 28 | veintiocho | |=20=20=20=
=20=20=20=20
+ 27 | 26 | veintiseis | |=20=20=20=
=20=20=20=20
+ 13 | 27 | veintisiete | |=20=20=20=
=20=20=20=20
+ 7 | 23 | veintitres | |=20=20=20=
=20=20=20=20
+ 8 | 21 | veintiuno | |=20=20=20=
=20=20=20=20
+ 0 | 100 | in child table | |=20=20=20=
=20=20=20=20
+ 0 | 100 | in child table 2 | |=20=20=20=
=20=20=20=20
+(34 rows)
+
+-- Verify that foreign key link still works
+INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail');
+ERROR: insert or update on table "clstr_tst" violates foreign key constra=
int "clstr_tst_con"
+DETAIL: Key (b)=3D(1111) is not present in table "clstr_tst_s".
+SELECT conname FROM pg_constraint WHERE conrelid =3D 'clstr_tst'::regclass
+ORDER BY 1;
+ conname=20=20=20=20=20=20=20=20
+----------------------
+ clstr_tst_a_not_null
+ clstr_tst_con
+ clstr_tst_pkey
+(3 rows)
+
+-- Verify partial analyze works
+REPACK (ANALYZE) clstr_tst (a);
+REPACK (ANALYZE) clstr_tst;
+REPACK (VERBOSE) clstr_tst (a);
+ERROR: ANALYZE option must be specified when a column list is provided
+-- REPACK w/o argument performs no ordering, so we can only check which ta=
bles
+-- have the relfilenode changed.
+RESET SESSION AUTHORIZATION;
+CREATE TEMP TABLE relnodes_old AS
+(SELECT relname, relfilenode
+FROM pg_class
+WHERE relname IN ('clstr_1', 'clstr_2', 'clstr_3'));
+SET SESSION AUTHORIZATION regress_clstr_user;
+SET client_min_messages =3D ERROR; -- order of "skipping" warnings may va=
ry
+REPACK;
+RESET client_min_messages;
+RESET SESSION AUTHORIZATION;
+CREATE TEMP TABLE relnodes_new AS
+(SELECT relname, relfilenode
+FROM pg_class
+WHERE relname IN ('clstr_1', 'clstr_2', 'clstr_3'));
+-- Do the actual comparison. Unlike CLUSTER, clstr_3 should have been
+-- processed because there is nothing like clustering index here.
+SELECT o.relname FROM relnodes_old o
+JOIN relnodes_new n ON o.relname =3D n.relname
+WHERE o.relfilenode <> n.relfilenode
+ORDER BY o.relname;
+ relname=20
+---------
+ clstr_1
+ clstr_3
+(2 rows)
+
-- clean up
DROP TABLE clustertest;
DROP TABLE clstr_1;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expecte=
d/rules.out
index f4ee2bd7459..48461550636 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2002,34 +2002,23 @@ pg_stat_progress_basebackup| SELECT pid,
ELSE NULL::text
END AS backup_type
FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid,=
param1, param2, param3, param4, param5, param6, param7, param8, param9, pa=
ram10, param11, param12, param13, param14, param15, param16, param17, param=
18, param19, param20);
-pg_stat_progress_cluster| SELECT s.pid,
- s.datid,
- d.datname,
- s.relid,
- CASE s.param1
- WHEN 1 THEN 'CLUSTER'::text
- WHEN 2 THEN 'VACUUM FULL'::text
- ELSE NULL::text
+pg_stat_progress_cluster| SELECT pid,
+ datid,
+ datname,
+ relid,
+ CASE
+ WHEN (command =3D ANY (ARRAY['CLUSTER'::text, 'VACUUM FULL'::t=
ext])) THEN command
+ WHEN (repack_index_relid =3D (0)::oid) THEN 'VACUUM FULL'::text
+ ELSE 'CLUSTER'::text
END AS command,
- CASE s.param2
- WHEN 0 THEN 'initializing'::text
- WHEN 1 THEN 'seq scanning heap'::text
- WHEN 2 THEN 'index scanning heap'::text
- WHEN 3 THEN 'sorting tuples'::text
- WHEN 4 THEN 'writing new heap'::text
- WHEN 5 THEN 'swapping relation files'::text
- WHEN 6 THEN 'rebuilding index'::text
- WHEN 7 THEN 'performing final cleanup'::text
- ELSE NULL::text
- END AS phase,
- (s.param3)::oid AS cluster_index_relid,
- s.param4 AS heap_tuples_scanned,
- s.param5 AS heap_tuples_written,
- s.param6 AS heap_blks_total,
- s.param7 AS heap_blks_scanned,
- s.param8 AS index_rebuild_count
- FROM (pg_stat_get_progress_info('CLUSTER'::text) s(pid, datid, relid, p=
aram1, param2, param3, param4, param5, param6, param7, param8, param9, para=
m10, param11, param12, param13, param14, param15, param16, param17, param18=
, param19, param20)
- LEFT JOIN pg_database d ON ((s.datid =3D d.oid)));
+ phase,
+ repack_index_relid AS cluster_index_relid,
+ heap_tuples_scanned,
+ heap_tuples_written,
+ heap_blks_total,
+ heap_blks_scanned,
+ index_rebuild_count
+ FROM pg_stat_progress_repack;
pg_stat_progress_copy| SELECT s.pid,
s.datid,
d.datname,
@@ -2089,6 +2078,35 @@ pg_stat_progress_create_index| SELECT s.pid,
s.param15 AS partitions_done
FROM (pg_stat_get_progress_info('CREATE INDEX'::text) s(pid, datid, rel=
id, param1, param2, param3, param4, param5, param6, param7, param8, param9,=
param10, param11, param12, param13, param14, param15, param16, param17, pa=
ram18, param19, param20)
LEFT JOIN pg_database d ON ((s.datid =3D d.oid)));
+pg_stat_progress_repack| SELECT s.pid,
+ s.datid,
+ d.datname,
+ s.relid,
+ CASE s.param1
+ WHEN 1 THEN 'CLUSTER'::text
+ WHEN 2 THEN 'REPACK'::text
+ WHEN 3 THEN 'VACUUM FULL'::text
+ ELSE NULL::text
+ END AS command,
+ CASE s.param2
+ WHEN 0 THEN 'initializing'::text
+ WHEN 1 THEN 'seq scanning heap'::text
+ WHEN 2 THEN 'index scanning heap'::text
+ WHEN 3 THEN 'sorting tuples'::text
+ WHEN 4 THEN 'writing new heap'::text
+ WHEN 5 THEN 'swapping relation files'::text
+ WHEN 6 THEN 'rebuilding index'::text
+ WHEN 7 THEN 'performing final cleanup'::text
+ ELSE NULL::text
+ END AS phase,
+ (s.param3)::oid AS repack_index_relid,
+ s.param4 AS heap_tuples_scanned,
+ s.param5 AS heap_tuples_written,
+ s.param6 AS heap_blks_total,
+ s.param7 AS heap_blks_scanned,
+ s.param8 AS index_rebuild_count
+ FROM (pg_stat_get_progress_info('REPACK'::text) s(pid, datid, relid, pa=
ram1, param2, param3, param4, param5, param6, param7, param8, param9, param=
10, param11, param12, param13, param14, param15, param16, param17, param18,=
param19, param20)
+ LEFT JOIN pg_database d ON ((s.datid =3D d.oid)));
pg_stat_progress_vacuum| SELECT s.pid,
s.datid,
d.datname,
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluste=
r.sql
index b7115f86104..c976823a3cb 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -76,7 +76,6 @@ INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should f=
ail');
SELECT conname FROM pg_constraint WHERE conrelid =3D 'clstr_tst'::regclass
ORDER BY 1;
=20
-
SELECT relname, relkind,
EXISTS(SELECT 1 FROM pg_class WHERE oid =3D c.reltoastrelid) AS hastoa=
st
FROM pg_class c WHERE relname LIKE 'clstr_tst%' ORDER BY relname;
@@ -229,6 +228,24 @@ SELECT relname, old.level, old.relkind, old.relfilenod=
e =3D new.relfilenode FROM o
CLUSTER clstrpart;
ALTER TABLE clstrpart SET WITHOUT CLUSTER;
ALTER TABLE clstrpart CLUSTER ON clstrpart_idx;
+
+-- Check that REPACK sets new relfilenodes: it should process exactly the =
same
+-- tables as CLUSTER did.
+DROP TABLE old_cluster_info;
+DROP TABLE new_cluster_info;
+CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, =
relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class=
c ON c.oid=3Dtree.relid ;
+REPACK clstrpart USING INDEX clstrpart_idx;
+CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, =
relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class=
c ON c.oid=3Dtree.relid ;
+SELECT relname, old.level, old.relkind, old.relfilenode =3D new.relfilenod=
e FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname)=
ORDER BY relname COLLATE "C";
+
+-- And finally the same for REPACK w/o index.
+DROP TABLE old_cluster_info;
+DROP TABLE new_cluster_info;
+CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, =
relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class=
c ON c.oid=3Dtree.relid ;
+REPACK clstrpart;
+CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, =
relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class=
c ON c.oid=3Dtree.relid ;
+SELECT relname, old.level, old.relkind, old.relfilenode =3D new.relfilenod=
e FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname)=
ORDER BY relname COLLATE "C";
+
DROP TABLE clstrpart;
=20
-- Ownership of partitions is checked
@@ -313,6 +330,57 @@ EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHE=
RE -a =3D -3 ORDER BY -a, b;
SELECT * FROM clstr_expression WHERE -a =3D -3 ORDER BY -a, b;
COMMIT;
=20
+----------------------------------------------------------------------
+--
+-- REPACK
+--
+----------------------------------------------------------------------
+
+-- REPACK handles individual tables identically to CLUSTER, but it's worth
+-- checking if it handles table hierarchies identically as well.
+REPACK clstr_tst USING INDEX clstr_tst_c;
+
+-- Verify that inheritance link still works
+INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table 2');
+SELECT a,b,c,substring(d for 30), length(d) from clstr_tst;
+
+-- Verify that foreign key link still works
+INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail');
+
+SELECT conname FROM pg_constraint WHERE conrelid =3D 'clstr_tst'::regclass
+ORDER BY 1;
+
+-- Verify partial analyze works
+REPACK (ANALYZE) clstr_tst (a);
+REPACK (ANALYZE) clstr_tst;
+REPACK (VERBOSE) clstr_tst (a);
+
+-- REPACK w/o argument performs no ordering, so we can only check which ta=
bles
+-- have the relfilenode changed.
+RESET SESSION AUTHORIZATION;
+CREATE TEMP TABLE relnodes_old AS
+(SELECT relname, relfilenode
+FROM pg_class
+WHERE relname IN ('clstr_1', 'clstr_2', 'clstr_3'));
+
+SET SESSION AUTHORIZATION regress_clstr_user;
+SET client_min_messages =3D ERROR; -- order of "skipping" warnings may va=
ry
+REPACK;
+RESET client_min_messages;
+
+RESET SESSION AUTHORIZATION;
+CREATE TEMP TABLE relnodes_new AS
+(SELECT relname, relfilenode
+FROM pg_class
+WHERE relname IN ('clstr_1', 'clstr_2', 'clstr_3'));
+
+-- Do the actual comparison. Unlike CLUSTER, clstr_3 should have been
+-- processed because there is nothing like clustering index here.
+SELECT o.relname FROM relnodes_old o
+JOIN relnodes_new n ON o.relname =3D n.relname
+WHERE o.relfilenode <> n.relfilenode
+ORDER BY o.relname;
+
-- clean up
DROP TABLE clustertest;
DROP TABLE clstr_1;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs=
.list
index ddbe4c64971..c0a4c9ed1c4 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2576,6 +2576,8 @@ ReorderBufferTupleCidKey
ReorderBufferUpdateProgressTxnCB
ReorderTuple
RepOriginId
+RepackCommand
+RepackStmt
ReparameterizeForeignPathByChild_function
ReplaceVarsFromTargetList_context
ReplaceVarsNoMatchOption
--=20
2.47.3
--=-=-=
Content-Type: text/x-diff
Content-Disposition: attachment;
filename=v32-0002-Refactor-index_concurrently_create_copy-for-use-with.patch
^ permalink raw reply [nested|flat] 3+ messages in thread
end of thread, other threads:[~2026-01-27 10:48 UTC | newest]
Thread overview: 3+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2017-05-02 09:23 [Proposal]: Extends VisualStudio to automatically precompile EmbeddedSQL Francis ANDRE <[email protected]>
2020-07-21 14:01 [PATCH v3] Allow to specify CRL directory Kyotaro Horiguchi <[email protected]>
2026-01-27 10:48 [PATCH 1/6] Add REPACK command Antonin Houska <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox