1 /*
2  * This file and its contents are licensed under the Apache License 2.0.
3  * Please see the included NOTICE for copyright information and
4  * LICENSE-APACHE for a copy of the license.
5  */
6 #ifndef TIMESCALEDB_COMPAT_H
7 #define TIMESCALEDB_COMPAT_H
8 
9 #include <postgres.h>
10 #include <commands/cluster.h>
11 #include <commands/explain.h>
12 #include <commands/trigger.h>
13 #include <executor/executor.h>
14 #include <executor/tuptable.h>
15 #include <nodes/execnodes.h>
16 #include <nodes/nodes.h>
17 #include <optimizer/restrictinfo.h>
18 #include <pgstat.h>
19 #include <utils/lsyscache.h>
20 #include <utils/rel.h>
21 
22 #include "export.h"
23 
24 #define is_supported_pg_version_12(version) ((version >= 120000) && (version < 130000))
25 #define is_supported_pg_version_13(version) ((version >= 130002) && (version < 140000))
26 #define is_supported_pg_version_14(version) ((version >= 140000) && (version < 150000))
27 
28 #define is_supported_pg_version(version)                                                           \
29 	(is_supported_pg_version_12(version) || is_supported_pg_version_13(version) ||                 \
30 	 is_supported_pg_version_14(version))
31 
32 #define PG12 is_supported_pg_version_12(PG_VERSION_NUM)
33 #define PG13 is_supported_pg_version_13(PG_VERSION_NUM)
34 #define PG14 is_supported_pg_version_14(PG_VERSION_NUM)
35 
36 #define PG13_LT (PG_VERSION_NUM < 130000)
37 #define PG13_GE (PG_VERSION_NUM >= 130000)
38 #define PG14_LT (PG_VERSION_NUM < 140000)
39 #define PG14_GE (PG_VERSION_NUM >= 140000)
40 
41 #if !(is_supported_pg_version(PG_VERSION_NUM))
42 #error "Unsupported PostgreSQL version"
43 #endif
44 
45 /*
46  * The following are compatibility functions for different versions of
47  * PostgreSQL. Each compatibility function (or group) has its own logic for
48  * which versions get different behavior and is separated from others by a
49  * comment with its name and any clarifying notes about supported behavior. Each
50  * compatibility define is separated out by function so that it is easier to see
51  * what changed about its behavior, and at what version, but closely related
52  * functions that changed at the same time may be grouped together into a single
53  * block. Compatibility functions are organized in alphabetical order.
54  *
55  * Wherever reasonable, we try to achieve forwards compatibility so that we can
56  * take advantage of features added in newer PG versions. This avoids some
57  * future tech debt, though may not always be possible.
58  *
59  * We append "compat" to the name of the function or define if we change the behavior
60  * of something that existed in a previous version. If we are merely backpatching
61  * behavior from a later version to an earlier version and not changing the
62  * behavior of the new version we simply adopt the new version's name.
63  */
64 
65 #if PG12
66 #define ExecComputeStoredGeneratedCompat(rri, estate, slot, cmd_type)                              \
67 	ExecComputeStoredGenerated(estate, slot)
68 #elif PG13
69 #define ExecComputeStoredGeneratedCompat(rri, estate, slot, cmd_type)                              \
70 	ExecComputeStoredGenerated(estate, slot, cmd_type)
71 #else
72 #define ExecComputeStoredGeneratedCompat(rri, estate, slot, cmd_type)                              \
73 	ExecComputeStoredGenerated(rri, estate, slot, cmd_type)
74 #endif
75 
76 #if PG14_LT
77 #define ExecInsertIndexTuplesCompat(rri,                                                           \
78 									slot,                                                          \
79 									estate,                                                        \
80 									update,                                                        \
81 									noDupErr,                                                      \
82 									specConflict,                                                  \
83 									arbiterIndexes)                                                \
84 	ExecInsertIndexTuples(slot, estate, noDupErr, specConflict, arbiterIndexes)
85 #else
86 #define ExecInsertIndexTuplesCompat(rri,                                                           \
87 									slot,                                                          \
88 									estate,                                                        \
89 									update,                                                        \
90 									noDupErr,                                                      \
91 									specConflict,                                                  \
92 									arbiterIndexes)                                                \
93 	ExecInsertIndexTuples(rri, slot, estate, update, noDupErr, specConflict, arbiterIndexes)
94 #endif
95 
96 /* PG14 fixes a bug in miscomputation of relids set in pull_varnos. The bugfix
97  * got backported to PG12 and PG13 but changes the signature of pull_varnos,
98  * make_simple_restrictinfo and make_restrictinfo. To not break existing code
99  * the modified functions get added under different name in PG12 and PG13.
100  * We add a compatibility macro that uses the modified functions when compiling
101  * against a postgres version that has them available.
102  * PG14 also adds PlannerInfo as argument to make_restrictinfo,
103  * make_simple_restrictinfo and pull_varnos.
104  *
105  * https://github.com/postgres/postgres/commit/1cce024fd2
106  * https://github.com/postgres/postgres/commit/73fc2e5bab
107  * https://github.com/postgres/postgres/commit/55dc86eca7
108  */
109 
110 #if (PG12 && PG_VERSION_NUM < 120006) || (PG13 && PG_VERSION_NUM < 130002)
111 #define pull_varnos_compat(root, expr) pull_varnos(expr)
112 #define make_simple_restrictinfo_compat(root, expr) make_simple_restrictinfo(expr)
113 #define make_restrictinfo_compat(root, a, b, c, d, e, f, g, h)                                     \
114 	make_restrictinfo(a, b, c, d, e, f, g, h)
115 #elif PG14_LT
116 #define pull_varnos_compat(root, expr) pull_varnos_new(root, expr)
117 #define make_simple_restrictinfo_compat(root, expr)                                                \
118 	make_restrictinfo_new(root, expr, true, false, false, 0, NULL, NULL, NULL)
119 #define make_restrictinfo_compat(root, a, b, c, d, e, f, g, h)                                     \
120 	make_restrictinfo_new(root, a, b, c, d, e, f, g, h)
121 #else
122 #define pull_varnos_compat(root, expr) pull_varnos(root, expr)
123 #define make_simple_restrictinfo_compat(root, clause) make_simple_restrictinfo(root, clause)
124 #define make_restrictinfo_compat(root, a, b, c, d, e, f, g, h)                                     \
125 	make_restrictinfo(root, a, b, c, d, e, f, g, h)
126 #endif
127 
128 /* PG14 renames predefined roles
129  *
130  * https://github.com/postgres/postgres/commit/c9c41c7a33
131  */
132 #if PG14_LT
133 #define ROLE_PG_READ_ALL_SETTINGS DEFAULT_ROLE_READ_ALL_SETTINGS
134 #endif
135 
136 /* fmgr
137  * In a9c35cf postgres changed how it calls SQL functions so that the number of
138  * argument-slots allocated is chosen dynamically, instead of being fixed. This
139  * change was ABI-breaking, so we cannot backport this optimization, however,
140  * we do backport the interface, so that all our code will be compatible with
141  * new versions.
142  */
143 
144 /* convenience macro to allocate FunctionCallInfoData on the heap */
145 #define HEAP_FCINFO(nargs) palloc(SizeForFunctionCallInfo(nargs))
146 
147 /* getting arguments has a different API, so these macros unify the versions */
148 #define FC_ARG(fcinfo, n) ((fcinfo)->args[(n)].value)
149 #define FC_NULL(fcinfo, n) ((fcinfo)->args[(n)].isnull)
150 
151 #define FC_FN_OID(fcinfo) ((fcinfo)->flinfo->fn_oid)
152 
153 /* convenience setters */
154 #define FC_SET_ARG(fcinfo, n, val)                                                                 \
155 	do                                                                                             \
156 	{                                                                                              \
157 		short _n = (n);                                                                            \
158 		FunctionCallInfo _fcinfo = (fcinfo);                                                       \
159 		FC_ARG(_fcinfo, _n) = (val);                                                               \
160 		FC_NULL(_fcinfo, _n) = false;                                                              \
161 	} while (0)
162 
163 #define FC_SET_NULL(fcinfo, n)                                                                     \
164 	do                                                                                             \
165 	{                                                                                              \
166 		short _n = (n);                                                                            \
167 		FunctionCallInfo _fcinfo = (fcinfo);                                                       \
168 		FC_ARG(_fcinfo, _n) = 0;                                                                   \
169 		FC_NULL(_fcinfo, _n) = true;                                                               \
170 	} while (0)
171 
172 /* create this function for symmetry with pq_sendint32 */
173 #define pq_getmsgint32(buf) pq_getmsgint(buf, 4)
174 
175 #define ts_tuptableslot_set_table_oid(slot, table_oid) (slot)->tts_tableOid = table_oid
176 
177 #include <commands/vacuum.h>
178 #include <commands/defrem.h>
179 
180 static inline int
get_vacuum_options(const VacuumStmt * stmt)181 get_vacuum_options(const VacuumStmt *stmt)
182 {
183 	/* In PG12, the vacuum options is a list of DefElems and require
184 	 * parsing. Here we only parse the options we might be interested in since
185 	 * PostgreSQL itself will parse the options fully when it executes the
186 	 * vacuum. */
187 	ListCell *lc;
188 	bool analyze = false;
189 	bool verbose = false;
190 
191 	foreach (lc, stmt->options)
192 	{
193 		DefElem *opt = (DefElem *) lfirst(lc);
194 
195 		/* Parse common options for VACUUM and ANALYZE */
196 		if (strcmp(opt->defname, "verbose") == 0)
197 			verbose = defGetBoolean(opt);
198 		/* Parse options available on VACUUM */
199 		else if (strcmp(opt->defname, "analyze") == 0)
200 			analyze = defGetBoolean(opt);
201 	}
202 
203 	return (stmt->is_vacuumcmd ? VACOPT_VACUUM : VACOPT_ANALYZE) | (verbose ? VACOPT_VERBOSE : 0) |
204 		   (analyze ? VACOPT_ANALYZE : 0);
205 }
206 
207 #if PG14_LT
208 static inline int
get_cluster_options(const ClusterStmt * stmt)209 get_cluster_options(const ClusterStmt *stmt)
210 {
211 	return stmt->options;
212 }
213 #else
214 static inline ClusterParams *
get_cluster_options(const ClusterStmt * stmt)215 get_cluster_options(const ClusterStmt *stmt)
216 {
217 	ListCell *lc;
218 	ClusterParams *params = palloc0(sizeof(ClusterParams));
219 	bool verbose = false;
220 
221 	/* Parse option list */
222 	foreach (lc, stmt->params)
223 	{
224 		DefElem *opt = (DefElem *) lfirst(lc);
225 		if (strcmp(opt->defname, "verbose") == 0)
226 			verbose = defGetBoolean(opt);
227 		else
228 			ereport(ERROR,
229 					(errcode(ERRCODE_SYNTAX_ERROR),
230 					 errmsg("unrecognized CLUSTER option \"%s\"", opt->defname),
231 					 parser_errposition(NULL, opt->location)));
232 	}
233 
234 	params->options = (verbose ? CLUOPT_VERBOSE : 0);
235 
236 	return params;
237 }
238 #endif
239 
240 #include <catalog/index.h>
241 
242 static inline int
get_reindex_options(ReindexStmt * stmt)243 get_reindex_options(ReindexStmt *stmt)
244 {
245 #if PG14_LT
246 	return stmt->options;
247 #else
248 	ListCell *lc;
249 	bool concurrently = false;
250 	bool verbose = false;
251 
252 	/* Parse option list */
253 	foreach (lc, stmt->params)
254 	{
255 		DefElem *opt = (DefElem *) lfirst(lc);
256 		if (strcmp(opt->defname, "verbose") == 0)
257 			verbose = defGetBoolean(opt);
258 		else if (strcmp(opt->defname, "concurrently") == 0)
259 			concurrently = defGetBoolean(opt);
260 		else
261 			ereport(ERROR,
262 					(errcode(ERRCODE_SYNTAX_ERROR),
263 					 errmsg("unrecognized REINDEX option \"%s\"", opt->defname),
264 					 parser_errposition(NULL, opt->location)));
265 	}
266 	return (verbose ? REINDEXOPT_VERBOSE : 0) | (concurrently ? REINDEXOPT_CONCURRENTLY : 0);
267 #endif
268 }
269 
270 /* PG14 splits Copy code into separate code for COPY FROM and COPY TO
271  * since we were only interested in the COPY FROM parts we macro CopyFromState
272  * to CopyState for versions < 14
273  * https://github.com/postgres/postgres/commit/c532d15ddd
274  */
275 #if PG14_LT
276 #define CopyFromState CopyState
277 #endif
278 
279 #if PG14_LT
280 #define estimate_hashagg_tablesize_compat(root, path, agg_costs, num_groups)                       \
281 	estimate_hashagg_tablesize(path, agg_costs, num_groups)
282 #else
283 #define estimate_hashagg_tablesize_compat(root, path, agg_costs, num_groups)                       \
284 	estimate_hashagg_tablesize(root, path, agg_costs, num_groups)
285 #endif
286 
287 #if PG14_LT
288 #define get_agg_clause_costs_compat(root, clause, split, costs)                                    \
289 	get_agg_clause_costs(root, clause, split, costs)
290 #else
291 #define get_agg_clause_costs_compat(root, clause, split, costs)                                    \
292 	get_agg_clause_costs(root, split, costs)
293 #endif
294 
295 /* PG13 added a dstlen parameter to pg_b64_decode and pg_b64_encode */
296 #if PG13_LT
297 #define pg_b64_encode_compat(src, srclen, dst, dstlen) pg_b64_encode((src), (srclen), (dst))
298 #define pg_b64_decode_compat(src, srclen, dst, dstlen) pg_b64_decode((src), (srclen), (dst))
299 #else
300 #define pg_b64_encode_compat(src, srclen, dst, dstlen)                                             \
301 	pg_b64_encode((src), (srclen), (dst), (dstlen))
302 #define pg_b64_decode_compat(src, srclen, dst, dstlen)                                             \
303 	pg_b64_decode((src), (srclen), (dst), (dstlen))
304 #endif
305 
306 /* PG13 changes the List implementation from a linked list to an array
307  * while most of the API functions did not change a few them have slightly
308  * different signature in PG13, additionally the list_make5 functions
309  * got removed. PG14 adds the list_make5 macros back. */
310 #if PG13_LT
311 #define lnext_compat(l, lc) lnext((lc))
312 #define list_delete_cell_compat(l, lc, prev) list_delete_cell((l), (lc), (prev))
313 #define for_each_cell_compat(cell, list, initcell) for_each_cell ((cell), (initcell))
314 #else
315 #define lnext_compat(l, lc) lnext((l), (lc))
316 #define list_delete_cell_compat(l, lc, prev) list_delete_cell((l), (lc))
317 #define for_each_cell_compat(cell, list, initcell) for_each_cell (cell, list, initcell)
318 #endif
319 
320 #if PG13
321 #define list_make5(x1, x2, x3, x4, x5) lappend(list_make4(x1, x2, x3, x4), x5)
322 #define list_make5_oid(x1, x2, x3, x4, x5) lappend_oid(list_make4_oid(x1, x2, x3, x4), x5)
323 #define list_make5_int(x1, x2, x3, x4, x5) lappend_int(list_make4_int(x1, x2, x3, x4), x5)
324 #endif
325 
326 /* PG13 removes the natts parameter from map_variable_attnos */
327 #if PG13_LT
328 #define map_variable_attnos_compat(node, varno, sublevels_up, map, natts, rowtype, found_wholerow) \
329 	map_variable_attnos((node),                                                                    \
330 						(varno),                                                                   \
331 						(sublevels_up),                                                            \
332 						(map),                                                                     \
333 						(natts),                                                                   \
334 						(rowtype),                                                                 \
335 						(found_wholerow))
336 #else
337 #define map_variable_attnos_compat(node, varno, sublevels_up, map, natts, rowtype, found_wholerow) \
338 	map_variable_attnos((node), (varno), (sublevels_up), (map), (rowtype), (found_wholerow))
339 #endif
340 
341 /* PG13 removes msg parameter from convert_tuples_by_name */
342 #if PG12
343 #define convert_tuples_by_name_compat(in, out, msg) convert_tuples_by_name(in, out, msg)
344 #else
345 #define convert_tuples_by_name_compat(in, out, msg) convert_tuples_by_name(in, out)
346 #endif
347 
348 /* PG14 adds estinfo parameter to estimate_num_groups for additional context
349  * about the estimation
350  * https://github.com/postgres/postgres/commit/ed934d4fa3
351  */
352 #if PG14_LT
353 #define estimate_num_groups_compat(root, exprs, rows, pgset, estinfo)                              \
354 	estimate_num_groups(root, exprs, rows, pgset)
355 #else
356 #define estimate_num_groups_compat(root, exprs, rows, pgset, estinfo)                              \
357 	estimate_num_groups(root, exprs, rows, pgset, estinfo)
358 #endif
359 
360 /* PG14 removes partitioned_rels from create_append_path and create_merge_append_path
361  *
362  * https://github.com/postgres/postgres/commit/f003a7522b
363  */
364 #if PG14_LT
365 #define create_append_path_compat(root,                                                            \
366 								  rel,                                                             \
367 								  subpaths,                                                        \
368 								  partial_subpaths,                                                \
369 								  pathkeys,                                                        \
370 								  required_outer,                                                  \
371 								  parallel_worker,                                                 \
372 								  parallel_aware,                                                  \
373 								  partitioned_rels,                                                \
374 								  rows)                                                            \
375 	create_append_path(root,                                                                       \
376 					   rel,                                                                        \
377 					   subpaths,                                                                   \
378 					   partial_subpaths,                                                           \
379 					   pathkeys,                                                                   \
380 					   required_outer,                                                             \
381 					   parallel_worker,                                                            \
382 					   parallel_aware,                                                             \
383 					   partitioned_rels,                                                           \
384 					   rows)
385 #else
386 #define create_append_path_compat(root,                                                            \
387 								  rel,                                                             \
388 								  subpaths,                                                        \
389 								  partial_subpaths,                                                \
390 								  pathkeys,                                                        \
391 								  required_outer,                                                  \
392 								  parallel_worker,                                                 \
393 								  parallel_aware,                                                  \
394 								  partitioned_rels,                                                \
395 								  rows)                                                            \
396 	create_append_path(root,                                                                       \
397 					   rel,                                                                        \
398 					   subpaths,                                                                   \
399 					   partial_subpaths,                                                           \
400 					   pathkeys,                                                                   \
401 					   required_outer,                                                             \
402 					   parallel_worker,                                                            \
403 					   parallel_aware,                                                             \
404 					   rows)
405 #endif
406 
407 #if PG14_LT
408 #define create_merge_append_path_compat(root,                                                      \
409 										rel,                                                       \
410 										subpaths,                                                  \
411 										pathkeys,                                                  \
412 										required_outer,                                            \
413 										partitioned_rels)                                          \
414 	create_merge_append_path(root, rel, subpaths, pathkeys, required_outer, partitioned_rels)
415 #else
416 #define create_merge_append_path_compat(root,                                                      \
417 										rel,                                                       \
418 										subpaths,                                                  \
419 										pathkeys,                                                  \
420 										required_outer,                                            \
421 										partitioned_rels)                                          \
422 	create_merge_append_path(root, rel, subpaths, pathkeys, required_outer)
423 #endif
424 
425 /* PG14 adds a parse mode argument to raw_parser.
426  *
427  * https://github.com/postgres/postgres/commit/844fe9f159
428  */
429 #if PG14_LT
430 #define raw_parser_compat(cmd) raw_parser(cmd)
431 #else
432 #define raw_parser_compat(cmd) raw_parser(cmd, RAW_PARSE_DEFAULT)
433 #endif
434 
435 #if PG14_LT
436 #define expand_function_arguments_compat(args, result_type, func_tuple)                            \
437 	expand_function_arguments(args, result_type, func_tuple)
438 #else
439 #define expand_function_arguments_compat(args, result_type, func_tuple)                            \
440 	expand_function_arguments(args, false, result_type, func_tuple)
441 #endif
442 
443 /* find_em_expr_for_rel was in postgres_fdw in PG12 but got
444  * moved out of contrib in PG13. So we map to our own function
445  * for PG12 only and use postgres implementation when it is
446  * available.
447  */
448 #if PG12
449 #define find_em_expr_for_rel ts_find_em_expr_for_rel
450 #endif
451 
452 /* PG13 added macros for typalign and typstorage constants
453  *
454  * https://github.com/postgres/postgres/commit/3ed2005ff59
455  */
456 #if PG12
457 #define TYPALIGN_CHAR 'c'   /* char alignment (i.e. unaligned) */
458 #define TYPALIGN_SHORT 's'  /* short alignment (typically 2 bytes) */
459 #define TYPALIGN_INT 'i'	/* int alignment (typically 4 bytes) */
460 #define TYPALIGN_DOUBLE 'd' /* double alignment (often 8 bytes) */
461 #endif
462 
463 #endif /* TIMESCALEDB_COMPAT_H */
464