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