@@ -130,6 +130,7 @@ static void dir_list_file_internal(parray *files, pgFile *parent, const char *pa
130
130
bool skip_hidden , int external_dir_num , fio_location location );
131
131
static void opt_path_map (ConfigOption * opt , const char * arg ,
132
132
TablespaceList * list , const char * type );
133
+ static void cleanup_tablespace (const char * path );
133
134
134
135
/* Tablespace mapping */
135
136
static TablespaceList tablespace_dirs = {NULL , NULL };
@@ -518,6 +519,8 @@ db_map_entry_free(void *entry)
518
519
*
519
520
* When follow_symlink is true, symbolic link is ignored and only file or
520
521
* directory linked to will be listed.
522
+ *
523
+ * TODO: make it strictly local
521
524
*/
522
525
void
523
526
dir_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
1088
1091
const char * linked_path = get_tablespace_mapping ((* link )-> linked );
1089
1092
1090
1093
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" ,
1092
1095
linked_path );
1093
1096
1094
1097
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
1128
1131
* tablespace_map or tablespace_map.txt.
1129
1132
*/
1130
1133
void
1131
- read_tablespace_map (parray * files , const char * backup_dir )
1134
+ read_tablespace_map (parray * links , const char * backup_dir )
1132
1135
{
1133
1136
FILE * fp ;
1134
1137
char db_path [MAXPGPATH ],
@@ -1138,16 +1141,9 @@ read_tablespace_map(parray *files, const char *backup_dir)
1138
1141
join_path_components (db_path , backup_dir , DATABASE_DIR );
1139
1142
join_path_components (map_path , db_path , PG_TABLESPACE_MAP_FILE );
1140
1143
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
-
1148
1144
fp = fio_open_stream (map_path , FIO_BACKUP_HOST );
1149
1145
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 ));
1151
1147
1152
1148
while (fgets (buf , lengthof (buf ), fp ))
1153
1149
{
@@ -1166,7 +1162,7 @@ read_tablespace_map(parray *files, const char *backup_dir)
1166
1162
file -> linked = pgut_strdup (path );
1167
1163
canonicalize_path (file -> linked );
1168
1164
1169
- parray_append (files , file );
1165
+ parray_append (links , file );
1170
1166
}
1171
1167
1172
1168
if (ferror (fp ))
@@ -1183,30 +1179,49 @@ read_tablespace_map(parray *files, const char *backup_dir)
1183
1179
* If tablespace-mapping option is supplied, all OLDDIR entries must have
1184
1180
* entries in tablespace_map file.
1185
1181
*
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
1189
1184
* 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
1190
1192
*/
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 )
1193
1195
{
1194
- // char this_backup_path[MAXPGPATH];
1195
- parray * links ;
1196
+ parray * links = parray_new ();
1196
1197
size_t i ;
1197
1198
TablespaceListCell * cell ;
1198
1199
pgFile * tmp_file = pgut_new (pgFile );
1200
+ bool tblspaces_are_empty = true;
1199
1201
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
+ }
1201
1220
1202
- // pgBackupGetPath(backup, this_backup_path, lengthof(this_backup_path), NULL);
1203
1221
read_tablespace_map (links , backup -> root_dir );
1204
1222
/* Sort links by the path of a linked file*/
1205
1223
parray_qsort (links , pgFileCompareLinked );
1206
1224
1207
- elog (LOG , "check tablespace directories of backup %s" ,
1208
- base36enc (backup -> start_time ));
1209
-
1210
1225
/* 1 - each OLDDIR must have an entry in tablespace_map file (links) */
1211
1226
for (cell = tablespace_dirs .head ; cell ; cell = cell -> next )
1212
1227
{
@@ -1216,52 +1231,109 @@ check_tablespace_mapping(pgBackup *backup, bool incremental, bool *tblspaces_are
1216
1231
elog (ERROR , "--tablespace-mapping option's old directory "
1217
1232
"doesn't have an entry in tablespace_map file: \"%s\"" ,
1218
1233
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
- // }
1231
1234
}
1232
1235
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
+
1233
1272
/* 2 - all linked directories must be empty */
1234
1273
for (i = 0 ; i < parray_num (links ); i ++ )
1235
1274
{
1236
1275
pgFile * link = (pgFile * ) parray_get (links , i );
1237
1276
const char * linked_path = link -> linked ;
1238
1277
TablespaceListCell * cell ;
1278
+ bool remapped = false;
1239
1279
1240
1280
for (cell = tablespace_dirs .head ; cell ; cell = cell -> next )
1241
1281
if (strcmp (link -> linked , cell -> old_dir ) == 0 )
1242
1282
{
1243
1283
linked_path = cell -> new_dir ;
1284
+ remapped = true;
1244
1285
break ;
1245
1286
}
1246
1287
1247
1288
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" ,
1249
1290
linked_path );
1250
1291
1251
1292
if (!dir_is_empty (linked_path , FIO_DB_HOST ))
1252
1293
{
1294
+
1253
1295
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;
1257
1323
}
1258
1324
}
1259
1325
1260
1326
free (tmp_file );
1261
1327
parray_walk (links , pgFileFree );
1262
1328
parray_free (links );
1329
+
1330
+ if (tblspaces_are_empty )
1331
+ return EmptyTblspc ;
1332
+
1333
+ return NotEmptyTblspc ;
1263
1334
}
1264
1335
1336
+ /* TODO: Make it consistent with check_tablespace_mapping */
1265
1337
void
1266
1338
check_external_dir_mapping (pgBackup * backup , bool incremental )
1267
1339
{
@@ -1854,3 +1926,34 @@ read_database_map(pgBackup *backup)
1854
1926
1855
1927
return database_map ;
1856
1928
}
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