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