Line data Source code
1 : /*
2 : * version.c
3 : *
4 : * Postgres-version-specific routines
5 : *
6 : * Copyright (c) 2010-2026, PostgreSQL Global Development Group
7 : * src/bin/pg_upgrade/version.c
8 : */
9 :
10 : #include "postgres_fe.h"
11 :
12 : #include "fe_utils/string_utils.h"
13 : #include "pg_upgrade.h"
14 :
15 : /*
16 : * version_hook functions for check_for_data_types_usage in order to determine
17 : * whether a data type check should be executed for the cluster in question or
18 : * not.
19 : */
20 : bool
21 15 : jsonb_9_4_check_applicable(ClusterInfo *cluster)
22 : {
23 : /* JSONB changed its storage format during 9.4 beta */
24 15 : if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
25 0 : cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
26 0 : return true;
27 :
28 15 : return false;
29 : }
30 :
31 : /*
32 : * Older servers can't support newer protocol versions, so their connection
33 : * strings will need to lock max_protocol_version to 3.0.
34 : */
35 : bool
36 712 : protocol_negotiation_supported(const ClusterInfo *cluster)
37 : {
38 : /*
39 : * The February 2018 patch release (9.3.21, 9.4.16, 9.5.11, 9.6.7, and
40 : * 10.2) added support for NegotiateProtocolVersion. But ClusterInfo only
41 : * has information about the major version number. To ensure we can still
42 : * upgrade older unpatched servers, just assume anything prior to PG11
43 : * can't negotiate. It's not possible for those servers to make use of
44 : * newer protocols anyway, so nothing is lost.
45 : */
46 712 : return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
47 : }
48 :
49 : /*
50 : * old_9_6_invalidate_hash_indexes()
51 : * 9.6 -> 10
52 : * Hash index binary format has changed from 9.6->10.0
53 : */
54 : void
55 0 : old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
56 : {
57 : int dbnum;
58 0 : FILE *script = NULL;
59 0 : bool found = false;
60 0 : char *output_path = "reindex_hash.sql";
61 :
62 0 : prep_status("Checking for hash indexes");
63 :
64 0 : for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
65 : {
66 : PGresult *res;
67 0 : bool db_used = false;
68 : int ntups;
69 : int rowno;
70 : int i_nspname,
71 : i_relname;
72 0 : DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
73 0 : PGconn *conn = connectToServer(cluster, active_db->db_name);
74 :
75 : /* find hash indexes */
76 0 : res = executeQueryOrDie(conn,
77 : "SELECT n.nspname, c.relname "
78 : "FROM pg_catalog.pg_class c, "
79 : " pg_catalog.pg_index i, "
80 : " pg_catalog.pg_am a, "
81 : " pg_catalog.pg_namespace n "
82 : "WHERE i.indexrelid = c.oid AND "
83 : " c.relam = a.oid AND "
84 : " c.relnamespace = n.oid AND "
85 : " a.amname = 'hash'"
86 : );
87 :
88 0 : ntups = PQntuples(res);
89 0 : i_nspname = PQfnumber(res, "nspname");
90 0 : i_relname = PQfnumber(res, "relname");
91 0 : for (rowno = 0; rowno < ntups; rowno++)
92 : {
93 0 : found = true;
94 0 : if (!check_mode)
95 : {
96 0 : if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
97 0 : pg_fatal("could not open file \"%s\": %m", output_path);
98 0 : if (!db_used)
99 : {
100 : PQExpBufferData connectbuf;
101 :
102 0 : initPQExpBuffer(&connectbuf);
103 0 : appendPsqlMetaConnect(&connectbuf, active_db->db_name);
104 0 : fputs(connectbuf.data, script);
105 0 : termPQExpBuffer(&connectbuf);
106 0 : db_used = true;
107 : }
108 0 : fprintf(script, "REINDEX INDEX %s.%s;\n",
109 0 : quote_identifier(PQgetvalue(res, rowno, i_nspname)),
110 0 : quote_identifier(PQgetvalue(res, rowno, i_relname)));
111 : }
112 : }
113 :
114 0 : PQclear(res);
115 :
116 0 : if (!check_mode && db_used)
117 : {
118 : /* mark hash indexes as invalid */
119 0 : PQclear(executeQueryOrDie(conn,
120 : "UPDATE pg_catalog.pg_index i "
121 : "SET indisvalid = false "
122 : "FROM pg_catalog.pg_class c, "
123 : " pg_catalog.pg_am a, "
124 : " pg_catalog.pg_namespace n "
125 : "WHERE i.indexrelid = c.oid AND "
126 : " c.relam = a.oid AND "
127 : " c.relnamespace = n.oid AND "
128 : " a.amname = 'hash'"));
129 : }
130 :
131 0 : PQfinish(conn);
132 : }
133 :
134 0 : if (script)
135 0 : fclose(script);
136 :
137 0 : if (found)
138 : {
139 0 : report_status(PG_WARNING, "warning");
140 0 : if (check_mode)
141 0 : pg_log(PG_WARNING, "\n"
142 : "Your installation contains hash indexes. These indexes have different\n"
143 : "internal formats between your old and new clusters, so they must be\n"
144 : "reindexed with the REINDEX command. After upgrading, you will be given\n"
145 : "REINDEX instructions.");
146 : else
147 0 : pg_log(PG_WARNING, "\n"
148 : "Your installation contains hash indexes. These indexes have different\n"
149 : "internal formats between your old and new clusters, so they must be\n"
150 : "reindexed with the REINDEX command. The file\n"
151 : " %s\n"
152 : "when executed by psql by the database superuser will recreate all invalid\n"
153 : "indexes; until then, none of these indexes will be used.",
154 : output_path);
155 : }
156 : else
157 0 : check_ok();
158 0 : }
159 :
160 : /*
161 : * Callback function for processing results of query for
162 : * report_extension_updates()'s UpgradeTask. If the query returned any rows,
163 : * write the details to the report file.
164 : */
165 : static void
166 32 : process_extension_updates(DbInfo *dbinfo, PGresult *res, void *arg)
167 : {
168 32 : int ntups = PQntuples(res);
169 32 : int i_name = PQfnumber(res, "name");
170 32 : UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
171 : PQExpBufferData connectbuf;
172 :
173 32 : if (ntups == 0)
174 32 : return;
175 :
176 0 : if (report->file == NULL &&
177 0 : (report->file = fopen_priv(report->path, "w")) == NULL)
178 0 : pg_fatal("could not open file \"%s\": %m", report->path);
179 :
180 0 : initPQExpBuffer(&connectbuf);
181 0 : appendPsqlMetaConnect(&connectbuf, dbinfo->db_name);
182 0 : fputs(connectbuf.data, report->file);
183 0 : termPQExpBuffer(&connectbuf);
184 :
185 0 : for (int rowno = 0; rowno < ntups; rowno++)
186 0 : fprintf(report->file, "ALTER EXTENSION %s UPDATE;\n",
187 0 : quote_identifier(PQgetvalue(res, rowno, i_name)));
188 : }
189 :
190 : /*
191 : * report_extension_updates()
192 : * Report extensions that should be updated.
193 : */
194 : void
195 10 : report_extension_updates(ClusterInfo *cluster)
196 : {
197 : UpgradeTaskReport report;
198 10 : UpgradeTask *task = upgrade_task_create();
199 10 : const char *query = "SELECT name "
200 : "FROM pg_available_extensions "
201 : "WHERE installed_version != default_version";
202 :
203 10 : prep_status("Checking for extension updates");
204 :
205 10 : report.file = NULL;
206 10 : strcpy(report.path, "update_extensions.sql");
207 :
208 10 : upgrade_task_add_step(task, query, process_extension_updates,
209 : true, &report);
210 :
211 10 : upgrade_task_run(task, cluster);
212 10 : upgrade_task_free(task);
213 :
214 10 : if (report.file)
215 : {
216 0 : fclose(report.file);
217 0 : report_status(PG_REPORT, "notice");
218 0 : pg_log(PG_REPORT, "\n"
219 : "Your installation contains extensions that should be updated\n"
220 : "with the ALTER EXTENSION command. The file\n"
221 : " %s\n"
222 : "when executed by psql by the database superuser will update\n"
223 : "these extensions.",
224 : report.path);
225 : }
226 : else
227 10 : check_ok();
228 10 : }
|