1 /*-------------------------------------------------------------------------
2 *
3 * pg_lsn.c
4 * Operations for the pg_lsn datatype.
5 *
6 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * IDENTIFICATION
10 * src/backend/utils/adt/pg_lsn.c
11 *
12 *-------------------------------------------------------------------------
13 */
14 #include "postgres.h"
15
16 #include "funcapi.h"
17 #include "libpq/pqformat.h"
18 #include "utils/builtins.h"
19 #include "utils/numeric.h"
20 #include "utils/pg_lsn.h"
21
22 #define MAXPG_LSNLEN 17
23 #define MAXPG_LSNCOMPONENT 8
24
25 /*----------------------------------------------------------
26 * Formatting and conversion routines.
27 *---------------------------------------------------------*/
28
29 XLogRecPtr
pg_lsn_in_internal(const char * str,bool * have_error)30 pg_lsn_in_internal(const char *str, bool *have_error)
31 {
32 int len1,
33 len2;
34 uint32 id,
35 off;
36 XLogRecPtr result;
37
38 Assert(have_error != NULL);
39 *have_error = false;
40
41 /* Sanity check input format. */
42 len1 = strspn(str, "0123456789abcdefABCDEF");
43 if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || str[len1] != '/')
44 {
45 *have_error = true;
46 return InvalidXLogRecPtr;
47 }
48 len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
49 if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0')
50 {
51 *have_error = true;
52 return InvalidXLogRecPtr;
53 }
54
55 /* Decode result. */
56 id = (uint32) strtoul(str, NULL, 16);
57 off = (uint32) strtoul(str + len1 + 1, NULL, 16);
58 result = ((uint64) id << 32) | off;
59
60 return result;
61 }
62
63 Datum
pg_lsn_in(PG_FUNCTION_ARGS)64 pg_lsn_in(PG_FUNCTION_ARGS)
65 {
66 char *str = PG_GETARG_CSTRING(0);
67 XLogRecPtr result;
68 bool have_error = false;
69
70 result = pg_lsn_in_internal(str, &have_error);
71 if (have_error)
72 ereport(ERROR,
73 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
74 errmsg("invalid input syntax for type %s: \"%s\"",
75 "pg_lsn", str)));
76
77 PG_RETURN_LSN(result);
78 }
79
80 Datum
pg_lsn_out(PG_FUNCTION_ARGS)81 pg_lsn_out(PG_FUNCTION_ARGS)
82 {
83 XLogRecPtr lsn = PG_GETARG_LSN(0);
84 char buf[MAXPG_LSNLEN + 1];
85 char *result;
86
87 snprintf(buf, sizeof buf, "%X/%X", LSN_FORMAT_ARGS(lsn));
88 result = pstrdup(buf);
89 PG_RETURN_CSTRING(result);
90 }
91
92 Datum
pg_lsn_recv(PG_FUNCTION_ARGS)93 pg_lsn_recv(PG_FUNCTION_ARGS)
94 {
95 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
96 XLogRecPtr result;
97
98 result = pq_getmsgint64(buf);
99 PG_RETURN_LSN(result);
100 }
101
102 Datum
pg_lsn_send(PG_FUNCTION_ARGS)103 pg_lsn_send(PG_FUNCTION_ARGS)
104 {
105 XLogRecPtr lsn = PG_GETARG_LSN(0);
106 StringInfoData buf;
107
108 pq_begintypsend(&buf);
109 pq_sendint64(&buf, lsn);
110 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
111 }
112
113
114 /*----------------------------------------------------------
115 * Operators for PostgreSQL LSNs
116 *---------------------------------------------------------*/
117
118 Datum
pg_lsn_eq(PG_FUNCTION_ARGS)119 pg_lsn_eq(PG_FUNCTION_ARGS)
120 {
121 XLogRecPtr lsn1 = PG_GETARG_LSN(0);
122 XLogRecPtr lsn2 = PG_GETARG_LSN(1);
123
124 PG_RETURN_BOOL(lsn1 == lsn2);
125 }
126
127 Datum
pg_lsn_ne(PG_FUNCTION_ARGS)128 pg_lsn_ne(PG_FUNCTION_ARGS)
129 {
130 XLogRecPtr lsn1 = PG_GETARG_LSN(0);
131 XLogRecPtr lsn2 = PG_GETARG_LSN(1);
132
133 PG_RETURN_BOOL(lsn1 != lsn2);
134 }
135
136 Datum
pg_lsn_lt(PG_FUNCTION_ARGS)137 pg_lsn_lt(PG_FUNCTION_ARGS)
138 {
139 XLogRecPtr lsn1 = PG_GETARG_LSN(0);
140 XLogRecPtr lsn2 = PG_GETARG_LSN(1);
141
142 PG_RETURN_BOOL(lsn1 < lsn2);
143 }
144
145 Datum
pg_lsn_gt(PG_FUNCTION_ARGS)146 pg_lsn_gt(PG_FUNCTION_ARGS)
147 {
148 XLogRecPtr lsn1 = PG_GETARG_LSN(0);
149 XLogRecPtr lsn2 = PG_GETARG_LSN(1);
150
151 PG_RETURN_BOOL(lsn1 > lsn2);
152 }
153
154 Datum
pg_lsn_le(PG_FUNCTION_ARGS)155 pg_lsn_le(PG_FUNCTION_ARGS)
156 {
157 XLogRecPtr lsn1 = PG_GETARG_LSN(0);
158 XLogRecPtr lsn2 = PG_GETARG_LSN(1);
159
160 PG_RETURN_BOOL(lsn1 <= lsn2);
161 }
162
163 Datum
pg_lsn_ge(PG_FUNCTION_ARGS)164 pg_lsn_ge(PG_FUNCTION_ARGS)
165 {
166 XLogRecPtr lsn1 = PG_GETARG_LSN(0);
167 XLogRecPtr lsn2 = PG_GETARG_LSN(1);
168
169 PG_RETURN_BOOL(lsn1 >= lsn2);
170 }
171
172 Datum
pg_lsn_larger(PG_FUNCTION_ARGS)173 pg_lsn_larger(PG_FUNCTION_ARGS)
174 {
175 XLogRecPtr lsn1 = PG_GETARG_LSN(0);
176 XLogRecPtr lsn2 = PG_GETARG_LSN(1);
177
178 PG_RETURN_LSN((lsn1 > lsn2) ? lsn1 : lsn2);
179 }
180
181 Datum
pg_lsn_smaller(PG_FUNCTION_ARGS)182 pg_lsn_smaller(PG_FUNCTION_ARGS)
183 {
184 XLogRecPtr lsn1 = PG_GETARG_LSN(0);
185 XLogRecPtr lsn2 = PG_GETARG_LSN(1);
186
187 PG_RETURN_LSN((lsn1 < lsn2) ? lsn1 : lsn2);
188 }
189
190 /* btree index opclass support */
191 Datum
pg_lsn_cmp(PG_FUNCTION_ARGS)192 pg_lsn_cmp(PG_FUNCTION_ARGS)
193 {
194 XLogRecPtr a = PG_GETARG_LSN(0);
195 XLogRecPtr b = PG_GETARG_LSN(1);
196
197 if (a > b)
198 PG_RETURN_INT32(1);
199 else if (a == b)
200 PG_RETURN_INT32(0);
201 else
202 PG_RETURN_INT32(-1);
203 }
204
205 /* hash index opclass support */
206 Datum
pg_lsn_hash(PG_FUNCTION_ARGS)207 pg_lsn_hash(PG_FUNCTION_ARGS)
208 {
209 /* We can use hashint8 directly */
210 return hashint8(fcinfo);
211 }
212
213 Datum
pg_lsn_hash_extended(PG_FUNCTION_ARGS)214 pg_lsn_hash_extended(PG_FUNCTION_ARGS)
215 {
216 return hashint8extended(fcinfo);
217 }
218
219
220 /*----------------------------------------------------------
221 * Arithmetic operators on PostgreSQL LSNs.
222 *---------------------------------------------------------*/
223
224 Datum
pg_lsn_mi(PG_FUNCTION_ARGS)225 pg_lsn_mi(PG_FUNCTION_ARGS)
226 {
227 XLogRecPtr lsn1 = PG_GETARG_LSN(0);
228 XLogRecPtr lsn2 = PG_GETARG_LSN(1);
229 char buf[256];
230 Datum result;
231
232 /* Output could be as large as plus or minus 2^63 - 1. */
233 if (lsn1 < lsn2)
234 snprintf(buf, sizeof buf, "-" UINT64_FORMAT, lsn2 - lsn1);
235 else
236 snprintf(buf, sizeof buf, UINT64_FORMAT, lsn1 - lsn2);
237
238 /* Convert to numeric. */
239 result = DirectFunctionCall3(numeric_in,
240 CStringGetDatum(buf),
241 ObjectIdGetDatum(0),
242 Int32GetDatum(-1));
243
244 return result;
245 }
246
247 /*
248 * Add the number of bytes to pg_lsn, giving a new pg_lsn.
249 * Must handle both positive and negative numbers of bytes.
250 */
251 Datum
pg_lsn_pli(PG_FUNCTION_ARGS)252 pg_lsn_pli(PG_FUNCTION_ARGS)
253 {
254 XLogRecPtr lsn = PG_GETARG_LSN(0);
255 Numeric nbytes = PG_GETARG_NUMERIC(1);
256 Datum num;
257 Datum res;
258 char buf[32];
259
260 if (numeric_is_nan(nbytes))
261 ereport(ERROR,
262 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
263 errmsg("cannot add NaN to pg_lsn")));
264
265 /* Convert to numeric */
266 snprintf(buf, sizeof(buf), UINT64_FORMAT, lsn);
267 num = DirectFunctionCall3(numeric_in,
268 CStringGetDatum(buf),
269 ObjectIdGetDatum(0),
270 Int32GetDatum(-1));
271
272 /* Add two numerics */
273 res = DirectFunctionCall2(numeric_add,
274 NumericGetDatum(num),
275 NumericGetDatum(nbytes));
276
277 /* Convert to pg_lsn */
278 return DirectFunctionCall1(numeric_pg_lsn, res);
279 }
280
281 /*
282 * Subtract the number of bytes from pg_lsn, giving a new pg_lsn.
283 * Must handle both positive and negative numbers of bytes.
284 */
285 Datum
pg_lsn_mii(PG_FUNCTION_ARGS)286 pg_lsn_mii(PG_FUNCTION_ARGS)
287 {
288 XLogRecPtr lsn = PG_GETARG_LSN(0);
289 Numeric nbytes = PG_GETARG_NUMERIC(1);
290 Datum num;
291 Datum res;
292 char buf[32];
293
294 if (numeric_is_nan(nbytes))
295 ereport(ERROR,
296 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
297 errmsg("cannot subtract NaN from pg_lsn")));
298
299 /* Convert to numeric */
300 snprintf(buf, sizeof(buf), UINT64_FORMAT, lsn);
301 num = DirectFunctionCall3(numeric_in,
302 CStringGetDatum(buf),
303 ObjectIdGetDatum(0),
304 Int32GetDatum(-1));
305
306 /* Subtract two numerics */
307 res = DirectFunctionCall2(numeric_sub,
308 NumericGetDatum(num),
309 NumericGetDatum(nbytes));
310
311 /* Convert to pg_lsn */
312 return DirectFunctionCall1(numeric_pg_lsn, res);
313 }
314