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