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