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