@@ -98,7 +98,9 @@ ResetUnloggedRelations(int op)
98
98
MemoryContextDelete (tmpctx );
99
99
}
100
100
101
- /* Process one tablespace directory for ResetUnloggedRelations */
101
+ /*
102
+ * Process one tablespace directory for ResetUnloggedRelations
103
+ */
102
104
static void
103
105
ResetUnloggedRelationsInTablespaceDir (const char * tsdirname , int op )
104
106
{
@@ -107,29 +109,32 @@ ResetUnloggedRelationsInTablespaceDir(const char *tsdirname, int op)
107
109
char dbspace_path [MAXPGPATH * 2 ];
108
110
109
111
ts_dir = AllocateDir (tsdirname );
110
- if (ts_dir == NULL )
112
+
113
+ /*
114
+ * If we get ENOENT on a tablespace directory, log it and return. This
115
+ * can happen if a previous DROP TABLESPACE crashed between removing the
116
+ * tablespace directory and removing the symlink in pg_tblspc. We don't
117
+ * really want to prevent database startup in that scenario, so let it
118
+ * pass instead. Any other type of error will be reported by ReadDir
119
+ * (causing a startup failure).
120
+ */
121
+ if (ts_dir == NULL && errno == ENOENT )
111
122
{
112
- /* anything except ENOENT is fishy */
113
- if (errno != ENOENT )
114
- ereport (LOG ,
115
- (errcode_for_file_access (),
116
- errmsg ("could not open directory \"%s\": %m" ,
117
- tsdirname )));
123
+ ereport (LOG ,
124
+ (errcode_for_file_access (),
125
+ errmsg ("could not open directory \"%s\": %m" ,
126
+ tsdirname )));
118
127
return ;
119
128
}
120
129
121
130
while ((de = ReadDir (ts_dir , tsdirname )) != NULL )
122
131
{
123
- int i = 0 ;
124
-
125
132
/*
126
133
* We're only interested in the per-database directories, which have
127
134
* numeric names. Note that this code will also (properly) ignore "."
128
135
* and "..".
129
136
*/
130
- while (isdigit ((unsigned char ) de -> d_name [i ]))
131
- ++ i ;
132
- if (de -> d_name [i ] != '\0' || i == 0 )
137
+ if (strspn (de -> d_name , "0123456789" ) != strlen (de -> d_name ))
133
138
continue ;
134
139
135
140
snprintf (dbspace_path , sizeof (dbspace_path ), "%s/%s" ,
@@ -140,7 +145,9 @@ ResetUnloggedRelationsInTablespaceDir(const char *tsdirname, int op)
140
145
FreeDir (ts_dir );
141
146
}
142
147
143
- /* Process one per-dbspace directory for ResetUnloggedRelations */
148
+ /*
149
+ * Process one per-dbspace directory for ResetUnloggedRelations
150
+ */
144
151
static void
145
152
ResetUnloggedRelationsInDbspaceDir (const char * dbspacedirname , int op )
146
153
{
@@ -158,32 +165,23 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
158
165
*/
159
166
if ((op & UNLOGGED_RELATION_CLEANUP ) != 0 )
160
167
{
161
- HTAB * hash = NULL ;
168
+ HTAB * hash ;
162
169
HASHCTL ctl ;
163
170
164
- /* Open the directory. */
165
- dbspace_dir = AllocateDir (dbspacedirname );
166
- if (dbspace_dir == NULL )
167
- {
168
- ereport (LOG ,
169
- (errcode_for_file_access (),
170
- errmsg ("could not open directory \"%s\": %m" ,
171
- dbspacedirname )));
172
- return ;
173
- }
174
-
175
171
/*
176
172
* It's possible that someone could create a ton of unlogged relations
177
173
* in the same database & tablespace, so we'd better use a hash table
178
174
* rather than an array or linked list to keep track of which files
179
175
* need to be reset. Otherwise, this cleanup operation would be
180
176
* O(n^2).
181
177
*/
178
+ memset (& ctl , 0 , sizeof (ctl ));
182
179
ctl .keysize = sizeof (unlogged_relation_entry );
183
180
ctl .entrysize = sizeof (unlogged_relation_entry );
184
181
hash = hash_create ("unlogged hash" , 32 , & ctl , HASH_ELEM );
185
182
186
183
/* Scan the directory. */
184
+ dbspace_dir = AllocateDir (dbspacedirname );
187
185
while ((de = ReadDir (dbspace_dir , dbspacedirname )) != NULL )
188
186
{
189
187
ForkNumber forkNum ;
@@ -222,21 +220,9 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
222
220
}
223
221
224
222
/*
225
- * Now, make a second pass and remove anything that matches. First,
226
- * reopen the directory.
223
+ * Now, make a second pass and remove anything that matches.
227
224
*/
228
225
dbspace_dir = AllocateDir (dbspacedirname );
229
- if (dbspace_dir == NULL )
230
- {
231
- ereport (LOG ,
232
- (errcode_for_file_access (),
233
- errmsg ("could not open directory \"%s\": %m" ,
234
- dbspacedirname )));
235
- hash_destroy (hash );
236
- return ;
237
- }
238
-
239
- /* Scan the directory. */
240
226
while ((de = ReadDir (dbspace_dir , dbspacedirname )) != NULL )
241
227
{
242
228
ForkNumber forkNum ;
@@ -266,15 +252,11 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
266
252
{
267
253
snprintf (rm_path , sizeof (rm_path ), "%s/%s" ,
268
254
dbspacedirname , de -> d_name );
269
-
270
- /*
271
- * It's tempting to actually throw an error here, but since
272
- * this code gets run during database startup, that could
273
- * result in the database failing to start. (XXX Should we do
274
- * it anyway?)
275
- */
276
- if (unlink (rm_path ))
277
- elog (LOG , "could not unlink file \"%s\": %m" , rm_path );
255
+ if (unlink (rm_path ) < 0 )
256
+ ereport (ERROR ,
257
+ (errcode_for_file_access (),
258
+ errmsg ("could not remove file \"%s\": %m" ,
259
+ rm_path )));
278
260
else
279
261
elog (DEBUG2 , "unlinked file \"%s\"" , rm_path );
280
262
}
@@ -294,19 +276,8 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
294
276
*/
295
277
if ((op & UNLOGGED_RELATION_INIT ) != 0 )
296
278
{
297
- /* Open the directory. */
298
- dbspace_dir = AllocateDir (dbspacedirname );
299
- if (dbspace_dir == NULL )
300
- {
301
- /* we just saw this directory, so it really ought to be there */
302
- ereport (LOG ,
303
- (errcode_for_file_access (),
304
- errmsg ("could not open directory \"%s\": %m" ,
305
- dbspacedirname )));
306
- return ;
307
- }
308
-
309
279
/* Scan the directory. */
280
+ dbspace_dir = AllocateDir (dbspacedirname );
310
281
while ((de = ReadDir (dbspace_dir , dbspacedirname )) != NULL )
311
282
{
312
283
ForkNumber forkNum ;
@@ -350,16 +321,6 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
350
321
* (especially the metadata ones) at once.
351
322
*/
352
323
dbspace_dir = AllocateDir (dbspacedirname );
353
- if (dbspace_dir == NULL )
354
- {
355
- /* we just saw this directory, so it really ought to be there */
356
- ereport (LOG ,
357
- (errcode_for_file_access (),
358
- errmsg ("could not open directory \"%s\": %m" ,
359
- dbspacedirname )));
360
- return ;
361
- }
362
-
363
324
while ((de = ReadDir (dbspace_dir , dbspacedirname )) != NULL )
364
325
{
365
326
ForkNumber forkNum ;
@@ -388,6 +349,14 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
388
349
389
350
FreeDir (dbspace_dir );
390
351
352
+ /*
353
+ * Lastly, fsync the database directory itself, ensuring the
354
+ * filesystem remembers the file creations and deletions we've done.
355
+ * We don't bother with this during a call that does only
356
+ * UNLOGGED_RELATION_CLEANUP, because if recovery crashes before we
357
+ * get to doing UNLOGGED_RELATION_INIT, we'll redo the cleanup step
358
+ * too at the next startup attempt.
359
+ */
391
360
fsync_fname (dbspacedirname , true);
392
361
}
393
362
}
0 commit comments