@@ -130,6 +130,7 @@ static void dir_list_file_internal(parray *files, pgFile *parent, const char *pa
130130 bool skip_hidden , int external_dir_num , fio_location location );
131131static void opt_path_map (ConfigOption * opt , const char * arg ,
132132 TablespaceList * list , const char * type );
133+ static void cleanup_tablespace (const char * path );
133134
134135/* Tablespace mapping */
135136static TablespaceList tablespace_dirs = {NULL , NULL };
@@ -518,6 +519,8 @@ db_map_entry_free(void *entry)
518519 *
519520 * When follow_symlink is true, symbolic link is ignored and only file or
520521 * directory linked to will be listed.
522+ *
523+ * TODO: make it strictly local
521524 */
522525void
523526dir_list_file (parray * files , const char * root , bool exclude , bool follow_symlink ,
@@ -1088,7 +1091,7 @@ create_data_directories(parray *dest_files, const char *data_dir, const char *ba
10881091 const char * linked_path = get_tablespace_mapping ((* link )-> linked );
10891092
10901093 if (!is_absolute_path (linked_path ))
1091- elog (ERROR , "Tablespace directory is not an absolute path: %s\n" ,
1094+ elog (ERROR , "Tablespace directory path must be an absolute path: %s\n" ,
10921095 linked_path );
10931096
10941097 join_path_components (to_path , data_dir , dir -> rel_path );
@@ -1128,7 +1131,7 @@ create_data_directories(parray *dest_files, const char *data_dir, const char *ba
11281131 * tablespace_map or tablespace_map.txt.
11291132 */
11301133void
1131- read_tablespace_map (parray * files , const char * backup_dir )
1134+ read_tablespace_map (parray * links , const char * backup_dir )
11321135{
11331136 FILE * fp ;
11341137 char db_path [MAXPGPATH ],
@@ -1138,16 +1141,9 @@ read_tablespace_map(parray *files, const char *backup_dir)
11381141 join_path_components (db_path , backup_dir , DATABASE_DIR );
11391142 join_path_components (map_path , db_path , PG_TABLESPACE_MAP_FILE );
11401143
1141- /* Exit if database/tablespace_map doesn't exist */
1142- if (!fileExists (map_path , FIO_BACKUP_HOST ))
1143- {
1144- elog (LOG , "there is no file tablespace_map" );
1145- return ;
1146- }
1147-
11481144 fp = fio_open_stream (map_path , FIO_BACKUP_HOST );
11491145 if (fp == NULL )
1150- elog (ERROR , "cannot open \"%s\": %s" , map_path , strerror (errno ));
1146+ elog (ERROR , "Cannot open tablespace map file \"%s\": %s" , map_path , strerror (errno ));
11511147
11521148 while (fgets (buf , lengthof (buf ), fp ))
11531149 {
@@ -1166,7 +1162,7 @@ read_tablespace_map(parray *files, const char *backup_dir)
11661162 file -> linked = pgut_strdup (path );
11671163 canonicalize_path (file -> linked );
11681164
1169- parray_append (files , file );
1165+ parray_append (links , file );
11701166 }
11711167
11721168 if (ferror (fp ))
@@ -1183,30 +1179,49 @@ read_tablespace_map(parray *files, const char *backup_dir)
11831179 * If tablespace-mapping option is supplied, all OLDDIR entries must have
11841180 * entries in tablespace_map file.
11851181 *
1186- *
1187- * TODO: maybe when running incremental restore with tablespace remapping, then
1188- * new tablespace directory MUST be empty? because there is no way
1182+ * When running incremental restore with tablespace remapping, then
1183+ * new tablespace directory MUST be empty, because there is no way
11891184 * we can be sure, that files laying there belong to our instance.
1185+ * But "force" flag allows to ignore this condition, by wiping out
1186+ * the current content on the directory.
1187+ *
1188+ * Exit codes:
1189+ * 1. backup has no tablespaces
1190+ * 2. backup has tablespaces and they are empty
1191+ * 3. backup has tablespaces and some of them are not empty
11901192 */
1191- void
1192- check_tablespace_mapping (pgBackup * backup , bool incremental , bool * tblspaces_are_empty )
1193+ int
1194+ check_tablespace_mapping (pgBackup * backup , bool incremental , bool force , bool pgdata_is_empty )
11931195{
1194- // char this_backup_path[MAXPGPATH];
1195- parray * links ;
1196+ parray * links = parray_new ();
11961197 size_t i ;
11971198 TablespaceListCell * cell ;
11981199 pgFile * tmp_file = pgut_new (pgFile );
1200+ bool tblspaces_are_empty = true;
11991201
1200- links = parray_new ();
1202+ elog (LOG , "Checking tablespace directories of backup %s" ,
1203+ base36enc (backup -> start_time ));
1204+
1205+ /* validate tablespace map,
1206+ * if there are no tablespaces, then there is nothing left to do
1207+ */
1208+ if (!validate_tablespace_map (backup ))
1209+ {
1210+ /*
1211+ * Sanity check
1212+ * If there is no tablespaces in backup,
1213+ * then using the '--tablespace-mapping' option is a mistake.
1214+ */
1215+ if (tablespace_dirs .head != NULL )
1216+ elog (ERROR , "Backup %s has no tablespaceses, nothing to remap "
1217+ "via \"--tablespace-mapping\" option" , base36enc (backup -> backup_id ));
1218+ return NoTblspc ;
1219+ }
12011220
1202- // pgBackupGetPath(backup, this_backup_path, lengthof(this_backup_path), NULL);
12031221 read_tablespace_map (links , backup -> root_dir );
12041222 /* Sort links by the path of a linked file*/
12051223 parray_qsort (links , pgFileCompareLinked );
12061224
1207- elog (LOG , "check tablespace directories of backup %s" ,
1208- base36enc (backup -> start_time ));
1209-
12101225 /* 1 - each OLDDIR must have an entry in tablespace_map file (links) */
12111226 for (cell = tablespace_dirs .head ; cell ; cell = cell -> next )
12121227 {
@@ -1216,52 +1231,109 @@ check_tablespace_mapping(pgBackup *backup, bool incremental, bool *tblspaces_are
12161231 elog (ERROR , "--tablespace-mapping option's old directory "
12171232 "doesn't have an entry in tablespace_map file: \"%s\"" ,
12181233 cell -> old_dir );
1219-
1220- /* For incremental restore, check that new directory is empty */
1221- // if (incremental)
1222- // {
1223- // if (!is_absolute_path(cell->new_dir))
1224- // elog(ERROR, "tablespace directory is not an absolute path: %s\n",
1225- // cell->new_dir);
1226- //
1227- // if (!dir_is_empty(cell->new_dir, FIO_DB_HOST))
1228- // elog(ERROR, "restore tablespace destination is not empty: \"%s\"",
1229- // cell->new_dir);
1230- // }
12311234 }
12321235
1236+ /*
1237+ * There is difference between incremental restore of already existing
1238+ * tablespaceses and remapped tablespaceses.
1239+ * Former are allowed to be not empty, because we treat them like an
1240+ * extension of PGDATA.
1241+ * The latter are not, unless "--force" flag is used.
1242+ * in which case the remapped directory is nuked - just to be safe,
1243+ * because it is hard to be sure that there are no some tricky corner
1244+ * cases of pages from different systems having the same crc.
1245+ * This is a strict approach.
1246+ *
1247+ * Why can`t we not nuke it and just let it roll ?
1248+ * What if user just wants to rerun failed restore with the same
1249+ * parameters? Nuking is bad for this case.
1250+ *
1251+ * Consider the example of existing PGDATA:
1252+ * ....
1253+ * pg_tablespace
1254+ * 100500-> /somedirectory
1255+ * ....
1256+ *
1257+ * We want to remap it during restore like that:
1258+ * ....
1259+ * pg_tablespace
1260+ * 100500-> /somedirectory1
1261+ * ....
1262+ *
1263+ * Usually it is required for "/somedirectory1" to be empty, but
1264+ * in case of incremental restore with 'force' flag, which required
1265+ * of us to drop already existing content of "/somedirectory1".
1266+ *
1267+ * TODO: Ideally in case of incremental restore we must also
1268+ * drop the "/somedirectory" directory first, but currently
1269+ * we don`t do that.
1270+ */
1271+
12331272 /* 2 - all linked directories must be empty */
12341273 for (i = 0 ; i < parray_num (links ); i ++ )
12351274 {
12361275 pgFile * link = (pgFile * ) parray_get (links , i );
12371276 const char * linked_path = link -> linked ;
12381277 TablespaceListCell * cell ;
1278+ bool remapped = false;
12391279
12401280 for (cell = tablespace_dirs .head ; cell ; cell = cell -> next )
12411281 if (strcmp (link -> linked , cell -> old_dir ) == 0 )
12421282 {
12431283 linked_path = cell -> new_dir ;
1284+ remapped = true;
12441285 break ;
12451286 }
12461287
12471288 if (!is_absolute_path (linked_path ))
1248- elog (ERROR , "tablespace directory is not an absolute path: %s\n" ,
1289+ elog (ERROR , "Tablespace directory path must be an absolute path: %s\n" ,
12491290 linked_path );
12501291
12511292 if (!dir_is_empty (linked_path , FIO_DB_HOST ))
12521293 {
1294+
12531295 if (!incremental )
1254- elog (ERROR , "restore tablespace destination is not empty: \"%s\"" ,
1255- linked_path );
1256- * tblspaces_are_empty = false;
1296+ elog (ERROR , "Restore tablespace destination is not empty: \"%s\"" , linked_path );
1297+
1298+ else if (remapped && !force )
1299+ elog (ERROR , "Remapped tablespace destination is not empty: \"%s\". "
1300+ "Use \"--force\" flag if you want to automatically clean up the "
1301+ "content of new tablespace destination" ,
1302+ linked_path );
1303+
1304+ else if (pgdata_is_empty && !force )
1305+ elog (ERROR , "PGDATA is empty, but tablespace destination is not: \"%s\". "
1306+ "Use \"--force\" flag is you want to automatically clean up the "
1307+ "content of tablespace destination" ,
1308+ linked_path );
1309+
1310+ /*
1311+ * TODO: compile the list of tblspc Oids to delete later,
1312+ * similar to what we do with database_map.
1313+ */
1314+ else if (force && (pgdata_is_empty || remapped ))
1315+ {
1316+ elog (WARNING , "Cleaning up the content of %s directory: \"%s\"" ,
1317+ remapped ? "remapped tablespace" : "tablespace" , linked_path );
1318+ cleanup_tablespace (linked_path );
1319+ continue ;
1320+ }
1321+
1322+ tblspaces_are_empty = false;
12571323 }
12581324 }
12591325
12601326 free (tmp_file );
12611327 parray_walk (links , pgFileFree );
12621328 parray_free (links );
1329+
1330+ if (tblspaces_are_empty )
1331+ return EmptyTblspc ;
1332+
1333+ return NotEmptyTblspc ;
12631334}
12641335
1336+ /* TODO: Make it consistent with check_tablespace_mapping */
12651337void
12661338check_external_dir_mapping (pgBackup * backup , bool incremental )
12671339{
@@ -1854,3 +1926,34 @@ read_database_map(pgBackup *backup)
18541926
18551927 return database_map ;
18561928}
1929+
1930+ /*
1931+ * Use it to cleanup tablespaces
1932+ * TODO: Current algorihtm is not very efficient in remote mode,
1933+ * due to round-trip to delete every file.
1934+ */
1935+ void
1936+ cleanup_tablespace (const char * path )
1937+ {
1938+ int i ;
1939+ char fullpath [MAXPGPATH ];
1940+ parray * files = parray_new ();
1941+
1942+ fio_list_dir (files , path , false, false, false, false, false, 0 );
1943+
1944+ /* delete leaf node first */
1945+ parray_qsort (files , pgFileCompareRelPathWithExternalDesc );
1946+
1947+ for (i = 0 ; i < parray_num (files ); i ++ )
1948+ {
1949+ pgFile * file = (pgFile * ) parray_get (files , i );
1950+
1951+ join_path_components (fullpath , path , file -> rel_path );
1952+
1953+ fio_delete (file -> mode , fullpath , FIO_DB_HOST );
1954+ elog (VERBOSE , "Deleted file \"%s\"" , fullpath );
1955+ }
1956+
1957+ parray_walk (files , pgFileFree );
1958+ parray_free (files );
1959+ }
0 commit comments