@@ -31,7 +31,7 @@ static void ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname,
3131
3232typedef struct
3333{
34- Oid reloid ; /* hash key */
34+ RelFileNumber relnumber ; /* hash key */
3535} unlogged_relation_entry ;
3636
3737/*
@@ -195,23 +195,22 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
195195 while ((de = ReadDir (dbspace_dir , dbspacedirname )) != NULL )
196196 {
197197 ForkNumber forkNum ;
198- int relnumchars ;
198+ unsigned segno ;
199199 unlogged_relation_entry ent ;
200200
201201 /* Skip anything that doesn't look like a relation data file. */
202- if (!parse_filename_for_nontemp_relation (de -> d_name , & relnumchars ,
203- & forkNum ))
202+ if (!parse_filename_for_nontemp_relation (de -> d_name ,
203+ & ent .relnumber ,
204+ & forkNum , & segno ))
204205 continue ;
205206
206207 /* Also skip it unless this is the init fork. */
207208 if (forkNum != INIT_FORKNUM )
208209 continue ;
209210
210211 /*
211- * Put the OID portion of the name into the hash table, if it
212- * isn't already.
212+ * Put the RelFileNumber into the hash table, if it isn't already.
213213 */
214- ent .reloid = atooid (de -> d_name );
215214 (void ) hash_search (hash , & ent , HASH_ENTER , NULL );
216215 }
217216
@@ -235,12 +234,13 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
235234 while ((de = ReadDir (dbspace_dir , dbspacedirname )) != NULL )
236235 {
237236 ForkNumber forkNum ;
238- int relnumchars ;
237+ unsigned segno ;
239238 unlogged_relation_entry ent ;
240239
241240 /* Skip anything that doesn't look like a relation data file. */
242- if (!parse_filename_for_nontemp_relation (de -> d_name , & relnumchars ,
243- & forkNum ))
241+ if (!parse_filename_for_nontemp_relation (de -> d_name ,
242+ & ent .relnumber ,
243+ & forkNum , & segno ))
244244 continue ;
245245
246246 /* We never remove the init fork. */
@@ -251,7 +251,6 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
251251 * See whether the OID portion of the name shows up in the hash
252252 * table. If so, nuke it!
253253 */
254- ent .reloid = atooid (de -> d_name );
255254 if (hash_search (hash , & ent , HASH_FIND , NULL ))
256255 {
257256 snprintf (rm_path , sizeof (rm_path ), "%s/%s" ,
@@ -285,14 +284,14 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
285284 while ((de = ReadDir (dbspace_dir , dbspacedirname )) != NULL )
286285 {
287286 ForkNumber forkNum ;
288- int relnumchars ;
289- char relnumbuf [ OIDCHARS + 1 ] ;
287+ RelFileNumber relNumber ;
288+ unsigned segno ;
290289 char srcpath [MAXPGPATH * 2 ];
291290 char dstpath [MAXPGPATH ];
292291
293292 /* Skip anything that doesn't look like a relation data file. */
294- if (!parse_filename_for_nontemp_relation (de -> d_name , & relnumchars ,
295- & forkNum ))
293+ if (!parse_filename_for_nontemp_relation (de -> d_name , & relNumber ,
294+ & forkNum , & segno ))
296295 continue ;
297296
298297 /* Also skip it unless this is the init fork. */
@@ -304,11 +303,12 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
304303 dbspacedirname , de -> d_name );
305304
306305 /* Construct destination pathname. */
307- memcpy (relnumbuf , de -> d_name , relnumchars );
308- relnumbuf [relnumchars ] = '\0' ;
309- snprintf (dstpath , sizeof (dstpath ), "%s/%s%s" ,
310- dbspacedirname , relnumbuf , de -> d_name + relnumchars + 1 +
311- strlen (forkNames [INIT_FORKNUM ]));
306+ if (segno == 0 )
307+ snprintf (dstpath , sizeof (dstpath ), "%s/%u" ,
308+ dbspacedirname , relNumber );
309+ else
310+ snprintf (dstpath , sizeof (dstpath ), "%s/%u.%u" ,
311+ dbspacedirname , relNumber , segno );
312312
313313 /* OK, we're ready to perform the actual copy. */
314314 elog (DEBUG2 , "copying %s to %s" , srcpath , dstpath );
@@ -327,26 +327,27 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
327327 dbspace_dir = AllocateDir (dbspacedirname );
328328 while ((de = ReadDir (dbspace_dir , dbspacedirname )) != NULL )
329329 {
330+ RelFileNumber relNumber ;
330331 ForkNumber forkNum ;
331- int relnumchars ;
332- char relnumbuf [OIDCHARS + 1 ];
332+ unsigned segno ;
333333 char mainpath [MAXPGPATH ];
334334
335335 /* Skip anything that doesn't look like a relation data file. */
336- if (!parse_filename_for_nontemp_relation (de -> d_name , & relnumchars ,
337- & forkNum ))
336+ if (!parse_filename_for_nontemp_relation (de -> d_name , & relNumber ,
337+ & forkNum , & segno ))
338338 continue ;
339339
340340 /* Also skip it unless this is the init fork. */
341341 if (forkNum != INIT_FORKNUM )
342342 continue ;
343343
344344 /* Construct main fork pathname. */
345- memcpy (relnumbuf , de -> d_name , relnumchars );
346- relnumbuf [relnumchars ] = '\0' ;
347- snprintf (mainpath , sizeof (mainpath ), "%s/%s%s" ,
348- dbspacedirname , relnumbuf , de -> d_name + relnumchars + 1 +
349- strlen (forkNames [INIT_FORKNUM ]));
345+ if (segno == 0 )
346+ snprintf (mainpath , sizeof (mainpath ), "%s/%u" ,
347+ dbspacedirname , relNumber );
348+ else
349+ snprintf (mainpath , sizeof (mainpath ), "%s/%u.%u" ,
350+ dbspacedirname , relNumber , segno );
350351
351352 fsync_fname (mainpath , false);
352353 }
@@ -371,52 +372,82 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
371372 * This function returns true if the file appears to be in the correct format
372373 * for a non-temporary relation and false otherwise.
373374 *
374- * NB: If this function returns true, the caller is entitled to assume that
375- * *relnumchars has been set to a value no more than OIDCHARS, and thus
376- * that a buffer of OIDCHARS+1 characters is sufficient to hold the
377- * RelFileNumber portion of the filename. This is critical to protect against
378- * a possible buffer overrun.
375+ * If it returns true, it sets *relnumber, *fork, and *segno to the values
376+ * extracted from the filename. If it returns false, these values are set to
377+ * InvalidRelFileNumber, InvalidForkNumber, and 0, respectively.
379378 */
380379bool
381- parse_filename_for_nontemp_relation (const char * name , int * relnumchars ,
382- ForkNumber * fork )
380+ parse_filename_for_nontemp_relation (const char * name , RelFileNumber * relnumber ,
381+ ForkNumber * fork , unsigned * segno )
383382{
384- int pos ;
383+ unsigned long n ,
384+ s ;
385+ ForkNumber f ;
386+ char * endp ;
385387
386- /* Look for a non-empty string of digits (that isn't too long). */
387- for (pos = 0 ; isdigit ((unsigned char ) name [pos ]); ++ pos )
388- ;
389- if (pos == 0 || pos > OIDCHARS )
388+ * relnumber = InvalidRelFileNumber ;
389+ * fork = InvalidForkNumber ;
390+ * segno = 0 ;
391+
392+ /*
393+ * Relation filenames should begin with a digit that is not a zero. By
394+ * rejecting cases involving leading zeroes, the caller can assume that
395+ * there's only one possible string of characters that could have produced
396+ * any given value for *relnumber.
397+ *
398+ * (To be clear, we don't expect files with names like 0017.3 to exist at
399+ * all -- but if 0017.3 does exist, it's a non-relation file, not part of
400+ * the main fork for relfilenode 17.)
401+ */
402+ if (name [0 ] < '1' || name [0 ] > '9' )
403+ return false;
404+
405+ /*
406+ * Parse the leading digit string. If the value is out of range, we
407+ * conclude that this isn't a relation file at all.
408+ */
409+ errno = 0 ;
410+ n = strtoul (name , & endp , 10 );
411+ if (errno || name == endp || n <= 0 || n > PG_UINT32_MAX )
390412 return false;
391- * relnumchars = pos ;
413+ name = endp ;
392414
393415 /* Check for a fork name. */
394- if (name [ pos ] != '_' )
395- * fork = MAIN_FORKNUM ;
416+ if (* name != '_' )
417+ f = MAIN_FORKNUM ;
396418 else
397419 {
398420 int forkchar ;
399421
400- forkchar = forkname_chars (& name [ pos + 1 ], fork );
422+ forkchar = forkname_chars (name + 1 , & f );
401423 if (forkchar <= 0 )
402424 return false;
403- pos += forkchar + 1 ;
425+ name += forkchar + 1 ;
404426 }
405427
406428 /* Check for a segment number. */
407- if (name [pos ] == '.' )
429+ if (* name != '.' )
430+ s = 0 ;
431+ else
408432 {
409- int segchar ;
433+ /* Reject leading zeroes, just like we do for RelFileNumber. */
434+ if (name [0 ] < '1' || name [0 ] > '9' )
435+ return false;
410436
411- for ( segchar = 1 ; isdigit (( unsigned char ) name [ pos + segchar ]); ++ segchar )
412- ;
413- if (segchar <= 1 )
437+ errno = 0 ;
438+ s = strtoul ( name + 1 , & endp , 10 ) ;
439+ if (errno || name + 1 == endp || s <= 0 || s > PG_UINT32_MAX )
414440 return false;
415- pos += segchar ;
441+ name = endp ;
416442 }
417443
418444 /* Now we should be at the end. */
419- if (name [ pos ] != '\0' )
445+ if (* name != '\0' )
420446 return false;
447+
448+ /* Set out parameters and return. */
449+ * relnumber = (RelFileNumber ) n ;
450+ * fork = f ;
451+ * segno = (unsigned ) s ;
421452 return true;
422453}
0 commit comments