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