]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/page.c
Rename pg_upgrade 'log' to 'log_opts', to avoid platform naming conflict.
[postgresql] / contrib / pg_upgrade / page.c
1 /*
2  *      page.c
3  *
4  *      per-page conversion operations
5  *
6  *      Copyright (c) 2010, PostgreSQL Global Development Group
7  *      contrib/pg_upgrade/page.c
8  */
9
10 #include "pg_upgrade.h"
11
12 #include "storage/bufpage.h"
13
14
15 #ifdef PAGE_CONVERSION
16
17
18 static const char *getPageVersion(
19                            uint16 *version, const char *pathName);
20 static pageCnvCtx *loadConverterPlugin(
21                                         uint16 newPageVersion, uint16 oldPageVersion);
22
23
24 /*
25  * setupPageConverter()
26  *
27  *      This function determines the PageLayoutVersion of the old cluster and
28  *      the PageLayoutVersion of the new cluster.  If the versions differ, this
29  *      function loads a converter plugin and returns a pointer to a pageCnvCtx
30  *      object (in *result) that knows how to convert pages from the old format
31  *      to the new format.      If the versions are identical, this function just
32  *      returns a NULL pageCnvCtx pointer to indicate that page-by-page conversion
33  *      is not required.
34  *
35  *      If successful this function sets *result and returns NULL.      If an error
36  *      occurs, this function returns an error message in the form of an null-terminated
37  *      string.
38  */
39 const char *
40 setupPageConverter(pageCnvCtx **result)
41 {
42         uint16          oldPageVersion;
43         uint16          newPageVersion;
44         pageCnvCtx *converter;
45         const char *msg;
46         char            dstName[MAXPGPATH];
47         char            srcName[MAXPGPATH];
48
49         snprintf(dstName, sizeof(dstName), "%s/global/%u", new_cluster.pgdata,
50                          new_cluster.pg_database_oid);
51         snprintf(srcName, sizeof(srcName), "%s/global/%u", old_cluster.pgdata,
52                          old_cluster.pg_database_oid);
53
54         if ((msg = getPageVersion(&oldPageVersion, srcName)) != NULL)
55                 return msg;
56
57         if ((msg = getPageVersion(&newPageVersion, dstName)) != NULL)
58                 return msg;
59
60         /*
61          * If the old cluster and new cluster use the same page layouts, then we
62          * don't need a page converter.
63          */
64         if (newPageVersion == oldPageVersion)
65         {
66                 *result = NULL;
67                 return NULL;
68         }
69
70         /*
71          * The clusters use differing page layouts, see if we can find a plugin
72          * that knows how to convert from the old page layout to the new page
73          * layout.
74          */
75
76         if ((converter = loadConverterPlugin(newPageVersion, oldPageVersion)) == NULL)
77                 return "can't find plugin to convert from old page layout to new page layout";
78         else
79         {
80                 *result = converter;
81                 return NULL;
82         }
83 }
84
85
86 /*
87  * getPageVersion()
88  *
89  *      Retrieves the PageLayoutVersion for the given relation.
90  *
91  *      Returns NULL on success (and stores the PageLayoutVersion at *version),
92  *      if an error occurs, this function returns an error message (in the form
93  *      of a null-terminated string).
94  */
95 static const char *
96 getPageVersion(uint16 *version, const char *pathName)
97 {
98         int                     relfd;
99         PageHeaderData page;
100         ssize_t         bytesRead;
101
102         if ((relfd = open(pathName, O_RDONLY, 0)) < 0)
103                 return "can't open relation";
104
105         if ((bytesRead = read(relfd, &page, sizeof(page))) != sizeof(page))
106         {
107                 close(relfd);
108                 return "can't read page header";
109         }
110
111         *version = PageGetPageLayoutVersion(&page);
112
113         close(relfd);
114
115         return NULL;
116 }
117
118
119 /*
120  * loadConverterPlugin()
121  *
122  *      This function loads a page-converter plugin library and grabs a
123  *      pointer to each of the (interesting) functions provided by that
124  *      plugin.  The name of the plugin library is derived from the given
125  *      newPageVersion and oldPageVersion.      If a plugin is found, this
126  *      function returns a pointer to a pageCnvCtx object (which will contain
127  *      a collection of plugin function pointers). If the required plugin
128  *      is not found, this function returns NULL.
129  */
130 static pageCnvCtx *
131 loadConverterPlugin(uint16 newPageVersion, uint16 oldPageVersion)
132 {
133         char            pluginName[MAXPGPATH];
134         void       *plugin;
135
136         /*
137          * Try to find a plugin that can convert pages of oldPageVersion into
138          * pages of newPageVersion.  For example, if we oldPageVersion = 3 and
139          * newPageVersion is 4, we search for a plugin named:
140          * plugins/convertLayout_3_to_4.dll
141          */
142
143         /*
144          * FIXME: we are searching for plugins relative to the current directory,
145          * we should really search relative to our own executable instead.
146          */
147         snprintf(pluginName, sizeof(pluginName), "./plugins/convertLayout_%d_to_%d%s",
148                          oldPageVersion, newPageVersion, DLSUFFIX);
149
150         if ((plugin = pg_dlopen(pluginName)) == NULL)
151                 return NULL;
152         else
153         {
154                 pageCnvCtx *result = (pageCnvCtx *) pg_malloc(sizeof(*result));
155
156                 result->old.PageVersion = oldPageVersion;
157                 result->new.PageVersion = newPageVersion;
158
159                 result->startup = (pluginStartup) pg_dlsym(plugin, "init");
160                 result->convertFile = (pluginConvertFile) pg_dlsym(plugin, "convertFile");
161                 result->convertPage = (pluginConvertPage) pg_dlsym(plugin, "convertPage");
162                 result->shutdown = (pluginShutdown) pg_dlsym(plugin, "fini");
163                 result->pluginData = NULL;
164
165                 /*
166                  * If the plugin has exported an initializer, go ahead and invoke it.
167                  */
168                 if (result->startup)
169                         result->startup(MIGRATOR_API_VERSION, &result->pluginVersion,
170                                                 newPageVersion, oldPageVersion, &result->pluginData);
171
172                 return result;
173         }
174 }
175
176
177
178 #endif