Coverage Report

Created: 2025-08-12 06:43

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