@@ -777,7 +777,7 @@ sub backup_fs_cold
777777
778778=pod
779779
780- =item $node->init_from_backup(root_node, backup_name)
780+ =item $node->init_from_backup(root_node, backup_name, %params )
781781
782782Initialize a node from a backup, which may come from this node or a different
783783node. root_node must be a PostgreSQL::Test::Cluster reference, backup_name the string name
@@ -787,8 +787,13 @@ Does not start the node after initializing it.
787787
788788By default, the backup is assumed to be plain format. To restore from
789789a tar-format backup, pass the name of the tar program to use in the
790- keyword parameter tar_program. Note that tablespace tar files aren't
791- handled here.
790+ keyword parameter tar_program.
791+
792+ If there are tablespace present in the backup, include tablespace_map as
793+ a keyword parameter whose values is a hash. When tar_program is used, the
794+ hash keys are tablespace OIDs; otherwise, they are the tablespace pathnames
795+ used in the backup. In either case, the values are the tablespace pathnames
796+ that should be used for the target cluster.
792797
793798To restore from an incremental backup, pass the parameter combine_with_prior
794799as a reference to an array of prior backup names with which this backup
@@ -843,24 +848,98 @@ sub init_from_backup
843848 }
844849
845850 local %ENV = $self -> _get_env();
846- PostgreSQL::Test::Utils::system_or_bail(' pg_combinebackup' , ' -d' ,
847- @prior_backup_path , $backup_path , ' -o' , $data_path );
851+ my @combineargs = (' pg_combinebackup' , ' -d' );
852+ if (exists $params {tablespace_map })
853+ {
854+ while (my ($olddir , $newdir ) = each %{$params {tablespace_map }})
855+ {
856+ push @combineargs , " -T$olddir =$newdir " ;
857+ }
858+ }
859+ push @combineargs , @prior_backup_path , $backup_path , ' -o' , $data_path ;
860+ PostgreSQL::Test::Utils::system_or_bail(@combineargs );
848861 }
849862 elsif (defined $params {tar_program })
850863 {
851- mkdir ($data_path );
864+ mkdir ($data_path ) || die " mkdir $data_path : $! " ;
852865 PostgreSQL::Test::Utils::system_or_bail($params {tar_program }, ' xf' ,
853866 $backup_path . ' /base.tar' ,
854867 ' -C' , $data_path );
855868 PostgreSQL::Test::Utils::system_or_bail(
856869 $params {tar_program }, ' xf' ,
857870 $backup_path . ' /pg_wal.tar' , ' -C' ,
858871 $data_path . ' /pg_wal' );
872+
873+ # We need to generate a tablespace_map file.
874+ open (my $tsmap , " >" , " $data_path /tablespace_map" )
875+ || die " $data_path /tablespace_map: $! " ;
876+
877+ # Extract tarfiles and add tablespace_map entries
878+ my @tstars = grep { / ^\d +.tar/ }
879+ PostgreSQL::Test::Utils::slurp_dir($backup_path );
880+ for my $tstar (@tstars )
881+ {
882+ my $tsoid = $tstar ;
883+ $tsoid =~ s /\. tar$// ;
884+
885+ die " no tablespace mapping for $tstar "
886+ if !exists $params {tablespace_map } ||
887+ !exists $params {tablespace_map }{$tsoid };
888+ my $newdir = $params {tablespace_map }{$tsoid };
889+
890+ mkdir ($newdir ) || die " mkdir $newdir : $! " ;
891+ PostgreSQL::Test::Utils::system_or_bail($params {tar_program }, ' xf' ,
892+ $backup_path . ' /' . $tstar , ' -C' , $newdir );
893+
894+ my $escaped_newdir = $newdir ;
895+ $escaped_newdir =~ s /\\ / \\\\ / g ;
896+ print $tsmap " $tsoid $escaped_newdir \n " ;
897+ }
898+
899+ # Close tablespace_map.
900+ close ($tsmap );
859901 }
860902 else
861903 {
904+ my @tsoids ;
862905 rmdir ($data_path );
863- PostgreSQL::Test::RecursiveCopy::copypath($backup_path , $data_path );
906+
907+ # Copy the main backup. Exclude tablespace links, but remember them.
908+ PostgreSQL::Test::RecursiveCopy::copypath($backup_path , $data_path ,
909+ ' filterfn' => sub {
910+ my ($path ) = @_ ;
911+ if ($path =~ / ^pg_tblspc\/ (\d +)$ / && -l " $backup_path /$path " )
912+ {
913+ push @tsoids , $1 ;
914+ return 0;
915+ }
916+ return 1;
917+ });
918+
919+ # We need to generate a tablespace_map file.
920+ open (my $tsmap , " >" , " $data_path /tablespace_map" )
921+ || die " $data_path /tablespace_map: $! " ;
922+
923+ # Now use the list of tablespace links to copy each tablespace.
924+ for my $tsoid (@tsoids )
925+ {
926+ my $olddir = readlink (" $backup_path /pg_tblspc/$tsoid " )
927+ || die " readlink $backup_path /pg_tblspc/$tsoid : $! " ;
928+
929+ die " no tablespace mapping for $olddir "
930+ if !exists $params {tablespace_map } ||
931+ !exists $params {tablespace_map }{$olddir };
932+
933+ my $newdir = $params {tablespace_map }{$olddir };
934+ PostgreSQL::Test::RecursiveCopy::copypath($olddir , $newdir );
935+
936+ my $escaped_newdir = $newdir ;
937+ $escaped_newdir =~ s /\\ / \\\\ / g ;
938+ print $tsmap " $tsoid $escaped_newdir \n " ;
939+ }
940+
941+ # Close tablespace_map.
942+ close ($tsmap );
864943 }
865944 chmod (0700, $data_path ) or die $! ;
866945
0 commit comments