@@ -862,7 +862,7 @@ backup_non_data_file(pgFile *file, pgFile *prev_file,
862862 */
863863size_t
864864restore_data_file (parray * parent_chain , pgFile * dest_file , FILE * out ,
865- const char * to_fullpath , bool use_bitmap )
865+ const char * to_fullpath , bool use_bitmap , uint16 * checksum_map )
866866{
867867 size_t total_write_len = 0 ;
868868 char * in_buf = pgut_malloc (STDIO_BUFSIZE );
@@ -941,7 +941,8 @@ restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
941941 total_write_len += restore_data_file_internal (in , out , tmp_file ,
942942 parse_program_version (backup -> program_version ),
943943 from_fullpath , to_fullpath , dest_file -> n_blocks ,
944- use_bitmap ? & (dest_file )-> pagemap : NULL );
944+ use_bitmap ? & (dest_file )-> pagemap : NULL ,
945+ checksum_map , backup -> checksum_version );
945946
946947 if (fclose (in ) != 0 )
947948 elog (ERROR , "Cannot close file \"%s\": %s" , from_fullpath ,
@@ -962,7 +963,7 @@ restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
962963size_t
963964restore_data_file_internal (FILE * in , FILE * out , pgFile * file , uint32 backup_version ,
964965 const char * from_fullpath , const char * to_fullpath , int nblocks ,
965- datapagemap_t * map )
966+ datapagemap_t * map , uint16 * checksum_map , int checksum_version )
966967{
967968 BackupPageHeader header ;
968969 BlockNumber blknum = 0 ;
@@ -1100,6 +1101,43 @@ restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_vers
11001101 is_compressed = true;
11011102 }
11021103
1104+ /* Incremental restore */
1105+ if (checksum_map && checksum_map [blknum ] != 0 )
1106+ {
1107+ uint16 page_crc = 0 ;
1108+
1109+ if (is_compressed )
1110+ {
1111+ char uncompressed_buf [BLCKSZ ];
1112+ fio_decompress (uncompressed_buf , page .data , compressed_size , file -> compress_alg );
1113+
1114+ /* If checksumms are enabled, then we can trust checksumm in header */
1115+ if (checksum_version )
1116+ page_crc = ((PageHeader ) uncompressed_buf )-> pd_checksum ;
1117+ else
1118+ page_crc = pg_checksum_page (uncompressed_buf , file -> segno * RELSEG_SIZE + blknum );
1119+
1120+ // page_crc_1 = pg_checksum_page(uncompressed_buf, file->segno * RELSEG_SIZE + blknum);
1121+ // Assert(page_crc == page_crc_1);
1122+ }
1123+ else
1124+ {
1125+ /* if checksumms are enabled, then we can trust checksumm in header */
1126+ if (checksum_version )
1127+ page_crc = ((PageHeader ) page .data )-> pd_checksum ;
1128+ else
1129+ page_crc = pg_checksum_page (page .data , file -> segno + blknum );
1130+ }
1131+
1132+ /* the heart of incremental restore */
1133+ if (page_crc == checksum_map [blknum ])
1134+ {
1135+ if (map )
1136+ datapagemap_add (map , blknum );
1137+ continue ;
1138+ }
1139+ }
1140+
11031141 /*
11041142 * Seek and write the restored page.
11051143 * When restoring file from FULL backup, pages are written sequentially,
@@ -1189,7 +1227,8 @@ restore_non_data_file_internal(FILE *in, FILE *out, pgFile *file,
11891227
11901228size_t
11911229restore_non_data_file (parray * parent_chain , pgBackup * dest_backup ,
1192- pgFile * dest_file , FILE * out , const char * to_fullpath )
1230+ pgFile * dest_file , FILE * out , const char * to_fullpath ,
1231+ bool already_exists )
11931232{
11941233// int i;
11951234 char from_root [MAXPGPATH ];
@@ -1259,6 +1298,20 @@ restore_non_data_file(parray *parent_chain, pgBackup *dest_backup,
12591298 "Metadata corruption in backup %s in file: \"%s\"" ,
12601299 base36enc (tmp_backup -> start_time ), to_fullpath );
12611300
1301+ /* incremental restore */
1302+ if (already_exists )
1303+ {
1304+ /* compare checksumms of remote and local files */
1305+ pg_crc32 file_crc = fio_get_crc32 (to_fullpath , FIO_DB_HOST , false);
1306+
1307+ if (file_crc == tmp_file -> crc )
1308+ {
1309+ elog (VERBOSE , "Remote nondata file \"%s\" is unchanged, skip restore" ,
1310+ to_fullpath );
1311+ return 0 ;
1312+ }
1313+ }
1314+
12621315 if (tmp_file -> external_dir_num == 0 )
12631316 join_path_components (from_root , tmp_backup -> root_dir , DATABASE_DIR );
12641317 else
@@ -1757,3 +1810,68 @@ check_file_pages(pgFile *file, const char *fullpath, XLogRecPtr stop_lsn,
17571810
17581811 return is_valid ;
17591812}
1813+
1814+ /* read local data file and construct map with block checksums */
1815+ uint16 * get_checksum_map (const char * fullpath , uint32 checksum_version ,
1816+ int n_blocks , XLogRecPtr dest_stop_lsn , BlockNumber segmentno )
1817+ {
1818+ uint16 * checksum_map = NULL ;
1819+ FILE * in = NULL ;
1820+ BlockNumber blknum = 0 ;
1821+ XLogRecPtr page_lsn = 0 ;
1822+ char read_buffer [BLCKSZ ];
1823+ char in_buf [STDIO_BUFSIZE ];
1824+
1825+ /* truncate up to blocks */
1826+ if (truncate (fullpath , n_blocks * BLCKSZ ) != 0 )
1827+ elog (ERROR , "Cannot truncate file to blknum %u \"%s\": %s" ,
1828+ n_blocks , fullpath , strerror (errno ));
1829+
1830+ /* open file */
1831+ in = fopen (fullpath , PG_BINARY_R );
1832+ if (!in )
1833+ elog (ERROR , "Cannot open source file \"%s\": %s" , fullpath , strerror (errno ));
1834+ setvbuf (in , in_buf , _IOFBF , STDIO_BUFSIZE );
1835+
1836+ /* initialize array of checksums */
1837+ checksum_map = pgut_malloc (n_blocks * sizeof (uint16 ));
1838+ memset (checksum_map , 0 , n_blocks * sizeof (uint16 ));
1839+
1840+ for (blknum = 0 ; blknum < n_blocks ; blknum ++ )
1841+ {
1842+ size_t read_len = fread (read_buffer , 1 , BLCKSZ , in );
1843+ page_lsn = InvalidXLogRecPtr ;
1844+
1845+ /* report error */
1846+ if (ferror (in ))
1847+ elog (ERROR , "Cannot read block %u of \"%s\": %s" ,
1848+ blknum , fullpath , strerror (errno ));
1849+
1850+ if (read_len == BLCKSZ )
1851+ {
1852+ int rc = validate_one_page (read_buffer , segmentno + blknum ,
1853+ dest_stop_lsn , & page_lsn , checksum_version );
1854+
1855+ if (rc == PAGE_IS_VALID )
1856+ {
1857+ if (checksum_version )
1858+ checksum_map [blknum ] = ((PageHeader ) read_buffer )-> pd_checksum ;
1859+ else
1860+ checksum_map [blknum ] = pg_checksum_page (read_buffer , segmentno + blknum );
1861+ }
1862+ }
1863+ else
1864+ elog (ERROR , "Failed to read blknum %u from file \"%s\"" , blknum , fullpath );
1865+
1866+ if (feof (in ))
1867+ break ;
1868+
1869+ if (interrupted )
1870+ elog (ERROR , "Interrupted during page reading" );
1871+ }
1872+
1873+ if (in )
1874+ fclose (in );
1875+
1876+ return checksum_map ;
1877+ }
0 commit comments