@@ -1109,6 +1109,76 @@ backup_cleanup(bool fatal, void *userdata)
11091109}
11101110}
11111111
1112+ /* Count bytes in file */
1113+ static long
1114+ file_size (const char * file )
1115+ {
1116+ long r ;
1117+ FILE * f = fopen (file ,"r" );
1118+
1119+ if (!f )
1120+ {
1121+ elog (ERROR ,"pg_probackup: could not open file \"%s\" for reading: %s\n" ,
1122+ file ,strerror (errno ));
1123+ return -1 ;
1124+ }
1125+ fseek (f ,0 ,SEEK_END );
1126+ r = ftell (f );
1127+ fclose (f );
1128+ return r ;
1129+ }
1130+
1131+ /*
1132+ * Find corresponding file in previous backup.
1133+ * Compare generations and return true if we don't need full copy
1134+ * of the file, but just part of it.
1135+ *
1136+ * skip_size - size of the file in previous backup. We can skip it
1137+ * and copy just remaining part of the file.
1138+ */
1139+ bool
1140+ backup_compressed_file_partially (pgFile * file ,void * arg ,size_t * skip_size )
1141+ {
1142+ bool result = false;
1143+ pgFile * prev_file = NULL ;
1144+ size_t current_file_size ;
1145+ backup_files_args * arguments = (backup_files_args * )arg ;
1146+
1147+ if (arguments -> prev_files )
1148+ {
1149+ pgFile * * p = (pgFile * * )parray_bsearch (arguments -> prev_files ,
1150+ file ,pgFileComparePath );
1151+ if (p )
1152+ prev_file = * p ;
1153+
1154+ /* If file's gc generation has changed since last backup, just copy it*/
1155+ if (prev_file && prev_file -> generation == file -> generation )
1156+ {
1157+ current_file_size = file_size (file -> path );
1158+
1159+ if (prev_file -> write_size == BYTES_INVALID )
1160+ return false;
1161+
1162+ * skip_size = prev_file -> write_size ;
1163+
1164+ if (current_file_size >=prev_file -> write_size )
1165+ {
1166+ elog (LOG ,"Backup file %s partially: prev_size %lu, current_size %lu" ,
1167+ file -> path ,prev_file -> write_size ,current_file_size );
1168+ result = true;
1169+ }
1170+ else
1171+ elog (ERROR ,"Something is wrong with %s. current_file_size %lu, prev %lu" ,
1172+ file -> path ,current_file_size ,prev_file -> write_size );
1173+ }
1174+ else
1175+ elog (LOG ,"Copy full %s. Generations are different: old=%d; new=%d" ,
1176+ file -> path ,prev_file -> generation ,file -> generation );
1177+ }
1178+
1179+ return result ;
1180+ }
1181+
11121182/*
11131183 * Take differential backup at page level.
11141184 */
@@ -1207,9 +1277,47 @@ backup_files(void *arg)
12071277}
12081278
12091279/* copy the file into backup */
1210- if (!(file -> is_datafile
1211- ?backup_data_file (arguments -> from_root ,arguments -> to_root ,file ,arguments -> lsn )
1212- :copy_file (arguments -> from_root ,arguments -> to_root ,file )))
1280+ if (file -> is_datafile )
1281+ {
1282+ if (!backup_data_file (arguments -> from_root ,
1283+ arguments -> to_root ,file ,
1284+ arguments -> lsn ))
1285+ {
1286+ /* record as skipped file in file_xxx.txt */
1287+ file -> write_size = BYTES_INVALID ;
1288+ elog (LOG ,"skip" );
1289+ continue ;
1290+ }
1291+ }
1292+ else if (is_compressed_data_file (file ))
1293+ {
1294+ size_t skip_size = 0 ;
1295+ if (backup_compressed_file_partially (file ,arguments ,& skip_size ))
1296+ {
1297+ /* backup cfs segment partly */
1298+ if (!copy_file_partly (arguments -> from_root ,
1299+ arguments -> to_root ,
1300+ file ,skip_size ))
1301+ {
1302+ /* record as skipped file in file_xxx.txt */
1303+ file -> write_size = BYTES_INVALID ;
1304+ elog (LOG ,"skip" );
1305+ continue ;
1306+ }
1307+ }
1308+ else if (!copy_file (arguments -> from_root ,
1309+ arguments -> to_root ,
1310+ file ))
1311+ {
1312+ /* record as skipped file in file_xxx.txt */
1313+ file -> write_size = BYTES_INVALID ;
1314+ elog (LOG ,"skip" );
1315+ continue ;
1316+ }
1317+ }
1318+ else if (!copy_file (arguments -> from_root ,
1319+ arguments -> to_root ,
1320+ file ))
12131321{
12141322/* record as skipped file in file_xxx.txt */
12151323file -> write_size = BYTES_INVALID ;
@@ -1258,14 +1366,14 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
12581366relative = file -> path + strlen (root )+ 1 ;
12591367if (is_pgdata &&
12601368!path_is_prefix_of_path ("base" ,relative )&&
1261- /*!path_is_prefix_of_path("global", relative) &&*/
1369+ /*!path_is_prefix_of_path("global", relative) &&*/ //TODO What's wrong with this line?
12621370!path_is_prefix_of_path ("pg_tblspc" ,relative ))
12631371continue ;
12641372
12651373/* Get file name from path */
12661374fname = last_dir_separator (relative );
12671375
1268- /* Remove temp tables */
1376+ /* Remove temp tablesfrom the list */
12691377if (fname [0 ]== 't' && isdigit (fname [1 ]))
12701378{
12711379pgFileFree (file );
@@ -1275,7 +1383,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
12751383}
12761384
12771385path_len = strlen (file -> path );
1278- /* Get link ptrack file torealations files */
1386+ /* Get link ptrack file torelations files */
12791387if (path_len > 6 && strncmp (file -> path + (path_len - 6 ),"ptrack" ,6 )== 0 )
12801388{
12811389pgFile * search_file ;
@@ -1284,12 +1392,15 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
12841392while (true) {
12851393pgFile tmp_file ;
12861394tmp_file .path = pg_strdup (file -> path );
1287- /* I hope segno not more than 999999 */
1395+
1396+ /* Segno fits into 6 digits since it is not more than 4000 */
12881397if (segno > 0 )
12891398sprintf (tmp_file .path + path_len - 7 ,".%d" ,segno );
12901399else
12911400tmp_file .path [path_len - 7 ]= '\0' ;
1401+
12921402pre_search_file = (pgFile * * )parray_bsearch (list_file ,& tmp_file ,pgFileComparePath );
1403+
12931404if (pre_search_file != NULL )
12941405{
12951406search_file = * pre_search_file ;
@@ -1303,6 +1414,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
13031414segno ++ ;
13041415}
13051416
1417+ /* Remove ptrack file itself from backup list */
13061418pgFileFree (file );
13071419parray_remove (list_file ,i );
13081420i -- ;
@@ -1312,9 +1424,9 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
13121424/* compress map file it is not data file */
13131425if (path_len > 4 && strncmp (file -> path + (path_len - 4 ),".cfm" ,4 )== 0 )
13141426{
1315- if (current .backup_mode == BACKUP_MODE_DIFF_PTRACK ||
1316- current . backup_mode == BACKUP_MODE_DIFF_PAGE )
1317- elog ( ERROR , "You can't use incremental backup with compress tablespace " );
1427+ if (current .backup_mode == BACKUP_MODE_DIFF_PAGE )
1428+ elog ( ERROR , "You can't use PAGE mode backup with compressed tablespace.\n"
1429+ "Try FULL or PTRACK mode instead. " );
13181430continue ;
13191431}
13201432
@@ -1362,11 +1474,34 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
13621474pgFile tmp_file ;
13631475tmp_file .path = pg_strdup (file -> path );
13641476tmp_file .path [path_len - 4 ]= '\0' ;
1365- pre_search_file = (pgFile * * )parray_bsearch (list_file ,& tmp_file ,pgFileComparePath );
1477+ pre_search_file = (pgFile * * )parray_bsearch (list_file ,
1478+ & tmp_file ,pgFileComparePath );
13661479if (pre_search_file != NULL )
13671480{
1481+ FileMap * map ;
1482+ int md = open (file -> path ,O_RDWR |PG_BINARY ,0 );
1483+ if (md < 0 )
1484+ elog (ERROR ,"add_files(). cannot open cfm file '%s'" ,file -> path );
1485+
1486+ map = cfs_mmap (md );
1487+ if (map == MAP_FAILED )
1488+ {
1489+ elog (LOG ,"add_files(). cfs_compression_ration failed to map file %s: %m" ,file -> path );
1490+ close (md );
1491+ break ;
1492+ }
1493+
1494+ (* pre_search_file )-> generation = map -> generation ;
13681495(* pre_search_file )-> is_datafile = false;
1496+
1497+ if (cfs_munmap (map )< 0 )
1498+ elog (LOG ,"add_files(). CFS failed to unmap file %s: %m" ,file -> path );
1499+ if (close (md )< 0 )
1500+ elog (LOG ,"add_files(). CFS failed to close file %s: %m" ,file -> path );
13691501}
1502+ else
1503+ elog (ERROR ,"corresponding segment '%s' is not found" ,tmp_file .path );
1504+
13701505pg_free (tmp_file .path );
13711506}
13721507}
@@ -1638,3 +1773,34 @@ StreamLog(void *arg)
16381773PQfinish (conn );
16391774conn = NULL ;
16401775}
1776+
1777+
1778+ FileMap * cfs_mmap (int md )
1779+ {
1780+ FileMap * map ;
1781+ #ifdef WIN32
1782+ HANDLE mh = CreateFileMapping (_get_osfhandle (md ),NULL ,PAGE_READWRITE ,
1783+ 0 , (DWORD )sizeof (FileMap ),NULL );
1784+ if (mh == NULL )
1785+ return (FileMap * )MAP_FAILED ;
1786+
1787+ map = (FileMap * )MapViewOfFile (mh ,FILE_MAP_ALL_ACCESS ,0 ,0 ,0 );
1788+ CloseHandle (mh );
1789+ if (map == NULL )
1790+ return (FileMap * )MAP_FAILED ;
1791+
1792+ #else
1793+ map = (FileMap * )mmap (NULL ,sizeof (FileMap ),
1794+ PROT_WRITE |PROT_READ ,MAP_SHARED ,md ,0 );
1795+ #endif
1796+ return map ;
1797+ }
1798+
1799+ int cfs_munmap (FileMap * map )
1800+ {
1801+ #ifdef WIN32
1802+ return UnmapViewOfFile (map ) ?0 :-1 ;
1803+ #else
1804+ return munmap (map ,sizeof (FileMap ));
1805+ #endif
1806+ }