1 /*-------------------------------------------------------------------------
2  *
3  * pg_lsn.c
4  *	  Operations for the pg_lsn datatype.
5  *
6  * Portions Copyright (c) 1996-2020, 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/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
pg_lsn_in_internal(const char * str,bool * have_error)29 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 	*have_error = false;
39 
40 	/* Sanity check input format. */
41 	len1 = strspn(str, "0123456789abcdefABCDEF");
42 	if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || str[len1] != '/')
43 	{
44 		*have_error = true;
45 		return InvalidXLogRecPtr;
46 	}
47 	len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
48 	if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0')
49 	{
50 		*have_error = true;
51 		return InvalidXLogRecPtr;
52 	}
53 
54 	/* Decode result. */
55 	id = (uint32) strtoul(str, NULL, 16);
56 	off = (uint32) strtoul(str + len1 + 1, NULL, 16);
57 	result = ((uint64) id << 32) | off;
58 
59 	return result;
60 }
61 
62 Datum
pg_lsn_in(PG_FUNCTION_ARGS)63 pg_lsn_in(PG_FUNCTION_ARGS)
64 {
65 	char	   *str = PG_GETARG_CSTRING(0);
66 	XLogRecPtr	result;
67 	bool		have_error = false;
68 
69 	result = pg_lsn_in_internal(str, &have_error);
70 	if (have_error)
71 		ereport(ERROR,
72 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
73 				 errmsg("invalid input syntax for type %s: \"%s\"",
74 						"pg_lsn", str)));
75 
76 	PG_RETURN_LSN(result);
77 }
78 
79 Datum
pg_lsn_out(PG_FUNCTION_ARGS)80 pg_lsn_out(PG_FUNCTION_ARGS)
81 {
82 	XLogRecPtr	lsn = PG_GETARG_LSN(0);
83 	char		buf[MAXPG_LSNLEN + 1];
84 	char	   *result;
85 	uint32		id,
86 				off;
87 
88 	/* Decode ID and offset */
89 	id = (uint32) (lsn >> 32);
90 	off = (uint32) lsn;
91 
92 	snprintf(buf, sizeof buf, "%X/%X", id, off);
93 	result = pstrdup(buf);
94 	PG_RETURN_CSTRING(result);
95 }
96 
97 Datum
pg_lsn_recv(PG_FUNCTION_ARGS)98 pg_lsn_recv(PG_FUNCTION_ARGS)
99 {
100 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
101 	XLogRecPtr	result;
102 
103 	result = pq_getmsgint64(buf);
104 	PG_RETURN_LSN(result);
105 }
106 
107 Datum
pg_lsn_send(PG_FUNCTION_ARGS)108 pg_lsn_send(PG_FUNCTION_ARGS)
109 {
110 	XLogRecPtr	lsn = PG_GETARG_LSN(0);
111 	StringInfoData buf;
112 
113 	pq_begintypsend(&buf);
114 	pq_sendint64(&buf, lsn);
115 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
116 }
117 
118 
119 /*----------------------------------------------------------
120  *	Operators for PostgreSQL LSNs
121  *---------------------------------------------------------*/
122 
123 Datum
pg_lsn_eq(PG_FUNCTION_ARGS)124 pg_lsn_eq(PG_FUNCTION_ARGS)
125 {
126 	XLogRecPtr	lsn1 = PG_GETARG_LSN(0);
127 	XLogRecPtr	lsn2 = PG_GETARG_LSN(1);
128 
129 	PG_RETURN_BOOL(lsn1 == lsn2);
130 }
131 
132 Datum
pg_lsn_ne(PG_FUNCTION_ARGS)133 pg_lsn_ne(PG_FUNCTION_ARGS)
134 {
135 	XLogRecPtr	lsn1 = PG_GETARG_LSN(0);
136 	XLogRecPtr	lsn2 = PG_GETARG_LSN(1);
137 
138 	PG_RETURN_BOOL(lsn1 != lsn2);
139 }
140 
141 Datum
pg_lsn_lt(PG_FUNCTION_ARGS)142 pg_lsn_lt(PG_FUNCTION_ARGS)
143 {
144 	XLogRecPtr	lsn1 = PG_GETARG_LSN(0);
145 	XLogRecPtr	lsn2 = PG_GETARG_LSN(1);
146 
147 	PG_RETURN_BOOL(lsn1 < lsn2);
148 }
149 
150 Datum
pg_lsn_gt(PG_FUNCTION_ARGS)151 pg_lsn_gt(PG_FUNCTION_ARGS)
152 {
153 	XLogRecPtr	lsn1 = PG_GETARG_LSN(0);
154 	XLogRecPtr	lsn2 = PG_GETARG_LSN(1);
155 
156 	PG_RETURN_BOOL(lsn1 > lsn2);
157 }
158 
159 Datum
pg_lsn_le(PG_FUNCTION_ARGS)160 pg_lsn_le(PG_FUNCTION_ARGS)
161 {
162 	XLogRecPtr	lsn1 = PG_GETARG_LSN(0);
163 	XLogRecPtr	lsn2 = PG_GETARG_LSN(1);
164 
165 	PG_RETURN_BOOL(lsn1 <= lsn2);
166 }
167 
168 Datum
pg_lsn_ge(PG_FUNCTION_ARGS)169 pg_lsn_ge(PG_FUNCTION_ARGS)
170 {
171 	XLogRecPtr	lsn1 = PG_GETARG_LSN(0);
172 	XLogRecPtr	lsn2 = PG_GETARG_LSN(1);
173 
174 	PG_RETURN_BOOL(lsn1 >= lsn2);
175 }
176 
177 Datum
pg_lsn_larger(PG_FUNCTION_ARGS)178 pg_lsn_larger(PG_FUNCTION_ARGS)
179 {
180 	XLogRecPtr	lsn1 = PG_GETARG_LSN(0);
181 	XLogRecPtr	lsn2 = PG_GETARG_LSN(1);
182 
183 	PG_RETURN_LSN((lsn1 > lsn2) ? lsn1 : lsn2);
184 }
185 
186 Datum
pg_lsn_smaller(PG_FUNCTION_ARGS)187 pg_lsn_smaller(PG_FUNCTION_ARGS)
188 {
189 	XLogRecPtr	lsn1 = PG_GETARG_LSN(0);
190 	XLogRecPtr	lsn2 = PG_GETARG_LSN(1);
191 
192 	PG_RETURN_LSN((lsn1 < lsn2) ? lsn1 : lsn2);
193 }
194 
195 /* btree index opclass support */
196 Datum
pg_lsn_cmp(PG_FUNCTION_ARGS)197 pg_lsn_cmp(PG_FUNCTION_ARGS)
198 {
199 	XLogRecPtr	a = PG_GETARG_LSN(0);
200 	XLogRecPtr	b = PG_GETARG_LSN(1);
201 
202 	if (a > b)
203 		PG_RETURN_INT32(1);
204 	else if (a == b)
205 		PG_RETURN_INT32(0);
206 	else
207 		PG_RETURN_INT32(-1);
208 }
209 
210 /* hash index opclass support */
211 Datum
pg_lsn_hash(PG_FUNCTION_ARGS)212 pg_lsn_hash(PG_FUNCTION_ARGS)
213 {
214 	/* We can use hashint8 directly */
215 	return hashint8(fcinfo);
216 }
217 
218 Datum
pg_lsn_hash_extended(PG_FUNCTION_ARGS)219 pg_lsn_hash_extended(PG_FUNCTION_ARGS)
220 {
221 	return hashint8extended(fcinfo);
222 }
223 
224 
225 /*----------------------------------------------------------
226  *	Arithmetic operators on PostgreSQL LSNs.
227  *---------------------------------------------------------*/
228 
229 Datum
pg_lsn_mi(PG_FUNCTION_ARGS)230 pg_lsn_mi(PG_FUNCTION_ARGS)
231 {
232 	XLogRecPtr	lsn1 = PG_GETARG_LSN(0);
233 	XLogRecPtr	lsn2 = PG_GETARG_LSN(1);
234 	char		buf[256];
235 	Datum		result;
236 
237 	/* Output could be as large as plus or minus 2^63 - 1. */
238 	if (lsn1 < lsn2)
239 		snprintf(buf, sizeof buf, "-" UINT64_FORMAT, lsn2 - lsn1);
240 	else
241 		snprintf(buf, sizeof buf, UINT64_FORMAT, lsn1 - lsn2);
242 
243 	/* Convert to numeric. */
244 	result = DirectFunctionCall3(numeric_in,
245 								 CStringGetDatum(buf),
246 								 ObjectIdGetDatum(0),
247 								 Int32GetDatum(-1));
248 
249 	return result;
250 }
251