@@ -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 combine_with_prior is used,
794+ the hash keys are the tablespace pathnames used in the backup; otherwise,
795+ they are tablespace OIDs. In either case, the values are the tablespace
796+ pathnames 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,101 @@ 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. If we see a tablespace directory for which we
908+ # have a tablespace mapping, skip it, but remember that we saw it.
909+ PostgreSQL::Test::RecursiveCopy::copypath($backup_path , $data_path ,
910+ ' filterfn' => sub {
911+ my ($path ) = @_ ;
912+ if ($path =~ / ^pg_tblspc\/ (\d +)$ / &&
913+ exists $params {tablespace_map }{$1 })
914+ {
915+ push @tsoids , $1 ;
916+ return 0;
917+ }
918+ return 1;
919+ });
920+
921+ if (@tsoids > 0)
922+ {
923+ # We need to generate a tablespace_map file.
924+ open (my $tsmap , " >" , " $data_path /tablespace_map" )
925+ || die " $data_path /tablespace_map: $! " ;
926+
927+ # Now use the list of tablespace links to copy each tablespace.
928+ for my $tsoid (@tsoids )
929+ {
930+ die " no tablespace mapping for $tsoid "
931+ if !exists $params {tablespace_map } ||
932+ !exists $params {tablespace_map }{$tsoid };
933+
934+ my $olddir = $backup_path . ' /pg_tblspc/' . $tsoid ;
935+ my $newdir = $params {tablespace_map }{$tsoid };
936+ PostgreSQL::Test::RecursiveCopy::copypath($olddir , $newdir );
937+
938+ my $escaped_newdir = $newdir ;
939+ $escaped_newdir =~ s /\\ / \\\\ / g ;
940+ print $tsmap " $tsoid $escaped_newdir \n " ;
941+ }
942+
943+ # Close tablespace_map.
944+ close ($tsmap );
945+ }
864946 }
865947 chmod (0700, $data_path ) or die $! ;
866948
0 commit comments