Replace regular expression used to detect relation files.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 8 Jan 2015 11:48:56 +0000 (13:48 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 16 Jan 2015 11:37:07 +0000 (13:37 +0200)
Windows, for example, does not have regcomp() and friends.

Also use pgwin32_is_junction on Windows, and check for HAVE_READLINK before
calling readlink().

Also change "#include <libpq-fe.h>" to make it build on Windows.

contrib/pg_rewind/Makefile
contrib/pg_rewind/copy_fetch.c
contrib/pg_rewind/filemap.c
contrib/pg_rewind/libpq_fetch.c
src/tools/msvc/Mkvcbuild.pm

index ab778002d05e733e7f41df5fab4ba2ef0b76b2b0..5577d8f92d491820e5ca10cb052a2e094e679d47 100644 (file)
@@ -8,10 +8,7 @@ PGAPPICON = win32
 
 PROGRAM = pg_rewind
 OBJS   = pg_rewind.o parsexlog.o xlogreader.o util.o datapagemap.o timeline.o \
-       fetch.o copy_fetch.o libpq_fetch.o filemap.o
-
-#REGRESS = basictest extrafiles databases
-#REGRESS_OPTS=--use-existing --launcher=./launcher
+       fetch.o copy_fetch.o libpq_fetch.o filemap.o $(WIN32RES)
 
 PG_CPPFLAGS = -I$(libpq_srcdir)
 PG_LIBS = $(libpq_pgport)
index 5c40ec71f4a2cd976fb3fea0d72a9dff305141a6..adb9beaa9df052233c57adb0104acea2ad826c2a 100644 (file)
@@ -109,8 +109,13 @@ recurse_dir(const char *datadir, const char *parentpath,
                        /* recurse to handle subdirectories */
                        recurse_dir(datadir, path, callback);
                }
+#ifndef WIN32
                else if (S_ISLNK(fst.st_mode))
+#else
+               else if (pgwin32_is_junction(fullpath))
+#endif
                {
+#if defined(HAVE_READLINK) || defined(WIN32)
                        char            link_target[MAXPGPATH];
                        ssize_t         len;
 
@@ -137,6 +142,10 @@ recurse_dir(const char *datadir, const char *parentpath,
                         */
                        if (strcmp(parentpath, "pg_tblspc") == 0)
                                recurse_dir(datadir, path, callback);
+#else
+                       fprintf(stderr, "\"%s\" is a symbolic link, but symbolic links are not supported on this platform\n", fullpath);
+                       exit(1);
+#endif /* HAVE_READLINK */
                }
        }
        closedir(xldir);
@@ -428,17 +437,27 @@ void
 truncate_target_file(const char *path, off_t newsize)
 {
        char            dstpath[MAXPGPATH];
+       int                     fd;
 
        if (dry_run)
                return;
 
        snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
-       if (truncate(dstpath, newsize) != 0)
+
+       fd = open(path, O_WRONLY, 0);
+       if (fd < 0)
+       {
+               fprintf(stderr, "could not open file \"%s\" for truncation: %s\n",
+                               dstpath, strerror(errno));
+               exit(1);
+       }
+       if (ftruncate(fd, newsize) != 0)
        {
                fprintf(stderr, "could not truncate file \"%s\" to %u bytes: %s\n",
                                dstpath, (unsigned int) newsize, strerror(errno));
                exit(1);
        }
+       close(fd);
 }
 
 static void
index 1daac0c23edf1bcc201c29ab3a00df8525d71de8..0c19368683e047b2d9643c1c2ee9f5ebeddc766f 100644 (file)
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <regex.h>
 
 #include "datapagemap.h"
 #include "filemap.h"
 #include "util.h"
 #include "pg_rewind.h"
+#include "catalog/pg_tablespace.h"
 #include "storage/fd.h"
 
 filemap_t *filemap = NULL;
@@ -143,7 +143,13 @@ process_remote_file(const char *path, file_type_t type, size_t newsize,
                        break;
 
                case FILE_TYPE_SYMLINK:
-                       if (exists && !S_ISLNK(statbuf.st_mode))
+                       if (exists &&
+#ifndef WIN32
+                               !S_ISLNK(statbuf.st_mode)
+#else
+                               !pgwin32_is_junction(localpath)
+#endif
+                               )
                        {
                                /* it's a symbolic link in target, but not in source. Strange.. */
                                fprintf(stderr, "\"%s\" is not a symbolic link.\n", localpath);
@@ -490,66 +496,90 @@ print_filemap(void)
 static bool
 isRelDataFile(const char *path)
 {
-       static bool     regexps_compiled = false;
-       static regex_t datasegment_regex;
-       int                     rc;
+       char            buf[20 + 1];
+       Oid                     spcNode;
+       Oid                     dbNode;
+       Oid                     relNode;
+       unsigned int segNo;
+       int                     nmatch;
+       bool            matched;
 
-       /* Compile the regexp if not compiled yet. */
-       if (!regexps_compiled)
+       /*
+        * Relation data files can be in one of the following directories:
+        *
+        * global/
+        *              shared relations
+        *
+        * base/<db oid>/
+        *              regular relations, default tablespace
+        *
+        * pg_tblspc/<tblspc oid>/PG_9.4_201403261/
+        *              within a non-default tablespace (the name of the directory
+        *              depends on version)
+        *
+        * And the relation data files themselves have a filename like:
+        *
+        * <oid>.<segment number>
+        *
+        */
+       spcNode = InvalidOid;
+       dbNode = InvalidOid;
+       relNode = InvalidOid;
+       segNo = 0;
+       matched = false;
+
+       nmatch = sscanf(path, "global/%u.%u", &relNode, &segNo);
+       if (nmatch == 1 || nmatch == 2)
        {
-               /*
-                * Relation data files can be in one of the following directories:
-                *
-                * global/
-                *              shared relations
-                *
-                * base/<db oid>/
-                *              regular relations, default tablespace
-                *
-                * pg_tblspc/<tblspc oid>/PG_9.4_201403261/
-                *              within a non-default tablespace (the name of the directory
-                *              depends on version)
-                *
-                * And the relation data files themselves have a filename like:
-                *
-                * <oid>.<segment number>
-                *
-                * This regular expression tries to capture all of above.
-                */
-               const char *datasegment_regex_str =
-                       "("
-                       "global"
-                       "|"
-                       "base/[0-9]+"
-                       "|"
-                       "pg_tblspc/[0-9]+/[PG_0-9.0-9_0-9]+/[0-9]+"
-                       ")/"
-                       "[0-9]+(\\.[0-9]+)?$";
-               rc = regcomp(&datasegment_regex, datasegment_regex_str, REG_NOSUB | REG_EXTENDED);
-               if (rc != 0)
+               spcNode = GLOBALTABLESPACE_OID;
+               dbNode = 0;
+               matched = true;
+       }
+       else
+       {
+               nmatch = sscanf(path, "base/%u/%u.%u", &dbNode, &relNode, &segNo);
+               if (nmatch == 2 || nmatch == 3)
                {
-                       char errmsg[100];
-                       regerror(rc, &datasegment_regex, errmsg, sizeof(errmsg));
-                       fprintf(stderr, "could not compile regular expression: %s\n",
-                                       errmsg);
-                       exit(1);
+                       spcNode = DEFAULTTABLESPACE_OID;
+                       matched = true;
+               }
+               else
+               {
+                       nmatch = sscanf(path, "pg_tblspc/%u/PG_%20s/%u/%u.%u",
+                                                       &spcNode, buf, &dbNode, &relNode, &segNo);
+                       if (nmatch == 4 || nmatch == 5)
+                               matched = true;
                }
        }
 
-       rc = regexec(&datasegment_regex, path, 0, NULL, 0);
-       if (rc == 0)
-       {
-               /* it's a data segment file */
-               return true;
-       }
-       else if (rc != REG_NOMATCH)
+       /*
+        * The sscanf tests above can match files that have extra characters
+        * at the end, and the last check can also match a path belonging to
+        * a different version (different TABLESPACE_VERSION_DIRECTORY). To
+        * make eliminate such cases, cross-check that GetRelationPath creates
+        * the exact same filename, when passed the RelFileNode information we
+        * extracted from the filename.
+        */
+       if (matched)
        {
-               char errmsg[100];
-               regerror(rc, &datasegment_regex, errmsg, sizeof(errmsg));
-               fprintf(stderr, "could not execute regular expression: %s\n", errmsg);
-               exit(1);
+               char *check_path;
+               char *check_path_with_seg;
+
+               check_path = GetRelationPath(dbNode, spcNode, relNode, InvalidBackendId,
+                                                                        MAIN_FORKNUM);
+               if (segNo != 0)
+               {
+                       check_path_with_seg = psprintf("%s.%u", check_path, segNo);
+                       pfree(check_path);
+               }
+               else
+                       check_path_with_seg = check_path;
+
+               if (strcmp(check_path_with_seg, path) != 0)
+                       matched = false;
        }
-       return false;
+
+       return matched;
 }
 
 static int
index 716814bb1b5ea390bee6b87f99ad2f04fe21b8bc..cf366b6faf84327e63306581f6d2eabfc51c7638 100644 (file)
 #include <fcntl.h>
 #include <unistd.h>
 
-#include <libpq-fe.h>
+/* for ntohl/htonl */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "libpq-fe.h"
 
 #include "pg_rewind.h"
 #include "fetch.h"
index 4336f2eabb888b5c4898f4985b1f4cf7c211db73..43d1729527f9105bf8619dbd5f37dd37fd9d5d61 100644 (file)
@@ -30,21 +30,26 @@ my $postgres;
 my $libpq;
 
 my $contrib_defines = { 'refint' => 'REFINT_VERBOSE' };
-my @contrib_uselibpq =
-  ('dblink', 'oid2name', 'pgbench', 'pg_upgrade', 'postgres_fdw', 'vacuumlo');
+my @contrib_uselibpq = (
+   'dblink',         'oid2name',
+   'pgbench',        'pg_upgrade',
+   'postgres_fdw',   'vacuumlo',
+   'pg_rewind');
 my @contrib_uselibpgport = (
        'oid2name',      'pgbench',
        'pg_standby',    'pg_archivecleanup',
        'pg_test_fsync', 'pg_test_timing',
        'pg_upgrade',    'pg_xlogdump',
-       'vacuumlo');
+       'pg_rewind',     'vacuumlo');
 my @contrib_uselibpgcommon = (
        'oid2name',      'pgbench',
        'pg_standby',    'pg_archivecleanup',
        'pg_test_fsync', 'pg_test_timing',
        'pg_upgrade',    'pg_xlogdump',
-       'vacuumlo');
-my $contrib_extralibs = { 'pgbench' => ['ws2_32.lib'] };
+       'pg_rewind',     'vacuumlo');
+my $contrib_extralibs = {
+    'pgbench' => ['ws2_32.lib'],
+    'pg_rewind' => ['ws2_32.lib'] };
 my $contrib_extraincludes =
   { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
 my $contrib_extrasource = {