Age Owner Branch data TLA 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
1 michael@paquier.xyz 32 :GNC 4114 : 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. */
4217 rhaas@postgresql.org 41 :CBC 4114 : len1 = strspn(str, "0123456789abcdefABCDEF");
42 [ + + + - : 4114 : if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || str[len1] != '/')
+ + ]
1 michael@paquier.xyz 43 :GNC 18 : goto syntax_error;
44 : :
4217 rhaas@postgresql.org 45 :CBC 4096 : len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
46 [ + + + - : 4096 : if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0')
- + ]
1 michael@paquier.xyz 47 :GNC 3 : goto syntax_error;
48 : :
49 : : /* Decode result. */
4217 rhaas@postgresql.org 50 :CBC 4093 : id = (uint32) strtoul(str, NULL, 16);
51 : 4093 : off = (uint32) strtoul(str + len1 + 1, NULL, 16);
52 : 4093 : result = ((uint64) id << 32) | off;
53 : :
2260 peter@eisentraut.org 54 : 4093 : return result;
55 : :
1 michael@paquier.xyz 56 :GNC 21 : syntax_error:
57 [ + + ]: 21 : 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
2260 peter@eisentraut.org 64 :CBC 4105 : pg_lsn_in(PG_FUNCTION_ARGS)
65 : : {
66 : 4105 : char *str = PG_GETARG_CSTRING(0);
67 : : XLogRecPtr result;
68 : :
1 michael@paquier.xyz 69 :GNC 4105 : result = pg_lsn_in_safe(str, fcinfo->context);
70 : :
2260 peter@eisentraut.org 71 :CBC 4090 : PG_RETURN_LSN(result);
72 : : }
73 : :
74 : : Datum
4217 rhaas@postgresql.org 75 : 3234 : pg_lsn_out(PG_FUNCTION_ARGS)
76 : : {
77 : 3234 : XLogRecPtr lsn = PG_GETARG_LSN(0);
78 : : char buf[MAXPG_LSNLEN + 1];
79 : : char *result;
80 : :
61 alvherre@kurilemu.de 81 :GNC 3234 : snprintf(buf, sizeof buf, "%X/%08X", LSN_FORMAT_ARGS(lsn));
4217 rhaas@postgresql.org 82 :CBC 3234 : result = pstrdup(buf);
83 : 3234 : PG_RETURN_CSTRING(result);
84 : : }
85 : :
86 : : Datum
4217 rhaas@postgresql.org 87 :UBC 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 : : {
4141 bruce@momjian.us 99 : 0 : XLogRecPtr lsn = PG_GETARG_LSN(0);
100 : : StringInfoData buf;
101 : :
4217 rhaas@postgresql.org 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
4217 rhaas@postgresql.org 113 :CBC 22566 : pg_lsn_eq(PG_FUNCTION_ARGS)
114 : : {
4141 bruce@momjian.us 115 : 22566 : XLogRecPtr lsn1 = PG_GETARG_LSN(0);
116 : 22566 : XLogRecPtr lsn2 = PG_GETARG_LSN(1);
117 : :
4217 rhaas@postgresql.org 118 : 22566 : PG_RETURN_BOOL(lsn1 == lsn2);
119 : : }
120 : :
121 : : Datum
122 : 6 : pg_lsn_ne(PG_FUNCTION_ARGS)
123 : : {
4141 bruce@momjian.us 124 : 6 : XLogRecPtr lsn1 = PG_GETARG_LSN(0);
125 : 6 : XLogRecPtr lsn2 = PG_GETARG_LSN(1);
126 : :
4217 rhaas@postgresql.org 127 : 6 : PG_RETURN_BOOL(lsn1 != lsn2);
128 : : }
129 : :
130 : : Datum
131 : 67471 : pg_lsn_lt(PG_FUNCTION_ARGS)
132 : : {
4141 bruce@momjian.us 133 : 67471 : XLogRecPtr lsn1 = PG_GETARG_LSN(0);
134 : 67471 : XLogRecPtr lsn2 = PG_GETARG_LSN(1);
135 : :
4217 rhaas@postgresql.org 136 : 67471 : PG_RETURN_BOOL(lsn1 < lsn2);
137 : : }
138 : :
139 : : Datum
140 : 2246 : pg_lsn_gt(PG_FUNCTION_ARGS)
141 : : {
4141 bruce@momjian.us 142 : 2246 : XLogRecPtr lsn1 = PG_GETARG_LSN(0);
143 : 2246 : XLogRecPtr lsn2 = PG_GETARG_LSN(1);
144 : :
4217 rhaas@postgresql.org 145 : 2246 : PG_RETURN_BOOL(lsn1 > lsn2);
146 : : }
147 : :
148 : : Datum
149 : 2525 : pg_lsn_le(PG_FUNCTION_ARGS)
150 : : {
4141 bruce@momjian.us 151 : 2525 : XLogRecPtr lsn1 = PG_GETARG_LSN(0);
152 : 2525 : XLogRecPtr lsn2 = PG_GETARG_LSN(1);
153 : :
4217 rhaas@postgresql.org 154 : 2525 : PG_RETURN_BOOL(lsn1 <= lsn2);
155 : : }
156 : :
157 : : Datum
158 : 1695 : pg_lsn_ge(PG_FUNCTION_ARGS)
159 : : {
4141 bruce@momjian.us 160 : 1695 : XLogRecPtr lsn1 = PG_GETARG_LSN(0);
161 : 1695 : XLogRecPtr lsn2 = PG_GETARG_LSN(1);
162 : :
4217 rhaas@postgresql.org 163 : 1695 : PG_RETURN_BOOL(lsn1 >= lsn2);
164 : : }
165 : :
166 : : Datum
2255 michael@paquier.xyz 167 : 10 : pg_lsn_larger(PG_FUNCTION_ARGS)
168 : : {
169 : 10 : XLogRecPtr lsn1 = PG_GETARG_LSN(0);
170 : 10 : XLogRecPtr lsn2 = PG_GETARG_LSN(1);
171 : :
172 : 10 : PG_RETURN_LSN((lsn1 > lsn2) ? lsn1 : lsn2);
173 : : }
174 : :
175 : : Datum
176 : 3 : pg_lsn_smaller(PG_FUNCTION_ARGS)
177 : : {
178 : 3 : XLogRecPtr lsn1 = PG_GETARG_LSN(0);
179 : 3 : XLogRecPtr lsn2 = PG_GETARG_LSN(1);
180 : :
181 : 3 : PG_RETURN_LSN((lsn1 < lsn2) ? lsn1 : lsn2);
182 : : }
183 : :
184 : : /* btree index opclass support */
185 : : Datum
4112 tgl@sss.pgh.pa.us 186 : 6157 : pg_lsn_cmp(PG_FUNCTION_ARGS)
187 : : {
188 : 6157 : XLogRecPtr a = PG_GETARG_LSN(0);
189 : 6157 : XLogRecPtr b = PG_GETARG_LSN(1);
190 : :
191 [ + + ]: 6157 : if (a > b)
192 : 3127 : PG_RETURN_INT32(1);
193 [ + + ]: 3030 : else if (a == b)
194 : 42 : PG_RETURN_INT32(0);
195 : : else
196 : 2988 : PG_RETURN_INT32(-1);
197 : : }
198 : :
199 : : /* hash index opclass support */
200 : : Datum
201 : 2661 : pg_lsn_hash(PG_FUNCTION_ARGS)
202 : : {
203 : : /* We can use hashint8 directly */
204 : 2661 : return hashint8(fcinfo);
205 : : }
206 : :
207 : : Datum
2928 rhaas@postgresql.org 208 : 30 : pg_lsn_hash_extended(PG_FUNCTION_ARGS)
209 : : {
210 : 30 : return hashint8extended(fcinfo);
211 : : }
212 : :
213 : :
214 : : /*----------------------------------------------------------
215 : : * Arithmetic operators on PostgreSQL LSNs.
216 : : *---------------------------------------------------------*/
217 : :
218 : : Datum
4217 219 : 1565 : pg_lsn_mi(PG_FUNCTION_ARGS)
220 : : {
4141 bruce@momjian.us 221 : 1565 : XLogRecPtr lsn1 = PG_GETARG_LSN(0);
222 : 1565 : 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. */
4217 rhaas@postgresql.org 227 [ + + ]: 1565 : if (lsn1 < lsn2)
228 : 24 : snprintf(buf, sizeof buf, "-" UINT64_FORMAT, lsn2 - lsn1);
229 : : else
230 : 1541 : snprintf(buf, sizeof buf, UINT64_FORMAT, lsn1 - lsn2);
231 : :
232 : : /* Convert to numeric. */
233 : 1565 : result = DirectFunctionCall3(numeric_in,
234 : : CStringGetDatum(buf),
235 : : ObjectIdGetDatum(0),
236 : : Int32GetDatum(-1));
237 : :
238 : 1565 : 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
1894 fujii@postgresql.org 246 : 30 : pg_lsn_pli(PG_FUNCTION_ARGS)
247 : : {
248 : 30 : XLogRecPtr lsn = PG_GETARG_LSN(0);
249 : 30 : Numeric nbytes = PG_GETARG_NUMERIC(1);
250 : : Datum num;
251 : : Datum res;
252 : : char buf[32];
253 : :
254 [ + + ]: 30 : if (numeric_is_nan(nbytes))
255 [ + - ]: 3 : ereport(ERROR,
256 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
257 : : errmsg("cannot add NaN to pg_lsn")));
258 : :
259 : : /* Convert to numeric */
260 : 27 : snprintf(buf, sizeof(buf), UINT64_FORMAT, lsn);
261 : 27 : num = DirectFunctionCall3(numeric_in,
262 : : CStringGetDatum(buf),
263 : : ObjectIdGetDatum(0),
264 : : Int32GetDatum(-1));
265 : :
266 : : /* Add two numerics */
267 : 27 : res = DirectFunctionCall2(numeric_add,
268 : : num,
269 : : NumericGetDatum(nbytes));
270 : :
271 : : /* Convert to pg_lsn */
272 : 27 : 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 : 18 : pg_lsn_mii(PG_FUNCTION_ARGS)
281 : : {
282 : 18 : XLogRecPtr lsn = PG_GETARG_LSN(0);
283 : 18 : Numeric nbytes = PG_GETARG_NUMERIC(1);
284 : : Datum num;
285 : : Datum res;
286 : : char buf[32];
287 : :
288 [ + + ]: 18 : if (numeric_is_nan(nbytes))
289 [ + - ]: 3 : ereport(ERROR,
290 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
291 : : errmsg("cannot subtract NaN from pg_lsn")));
292 : :
293 : : /* Convert to numeric */
294 : 15 : snprintf(buf, sizeof(buf), UINT64_FORMAT, lsn);
295 : 15 : num = DirectFunctionCall3(numeric_in,
296 : : CStringGetDatum(buf),
297 : : ObjectIdGetDatum(0),
298 : : Int32GetDatum(-1));
299 : :
300 : : /* Subtract two numerics */
301 : 15 : res = DirectFunctionCall2(numeric_sub,
302 : : num,
303 : : NumericGetDatum(nbytes));
304 : :
305 : : /* Convert to pg_lsn */
306 : 15 : return DirectFunctionCall1(numeric_pg_lsn, res);
307 : : }
|