PostgreSQL Source Code  git master
mac.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * mac.c
4  * PostgreSQL type definitions for 6 byte, EUI-48, MAC addresses.
5  *
6  * Portions Copyright (c) 1998-2022, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  * src/backend/utils/adt/mac.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 
14 #include "postgres.h"
15 
16 #include "common/hashfn.h"
17 #include "lib/hyperloglog.h"
18 #include "libpq/pqformat.h"
19 #include "port/pg_bswap.h"
20 #include "utils/builtins.h"
21 #include "utils/guc.h"
22 #include "utils/inet.h"
23 #include "utils/sortsupport.h"
24 
25 
26 /*
27  * Utility macros used for sorting and comparing:
28  */
29 
30 #define hibits(addr) \
31  ((unsigned long)(((addr)->a<<16)|((addr)->b<<8)|((addr)->c)))
32 
33 #define lobits(addr) \
34  ((unsigned long)(((addr)->d<<16)|((addr)->e<<8)|((addr)->f)))
35 
36 /* sortsupport for macaddr */
37 typedef struct
38 {
39  int64 input_count; /* number of non-null values seen */
40  bool estimating; /* true if estimating cardinality */
41 
42  hyperLogLogState abbr_card; /* cardinality estimator */
44 
46 static int macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup);
47 static bool macaddr_abbrev_abort(int memtupcount, SortSupport ssup);
48 static Datum macaddr_abbrev_convert(Datum original, SortSupport ssup);
49 
50 /*
51  * MAC address reader. Accepts several common notations.
52  */
53 
54 Datum
56 {
57  char *str = PG_GETARG_CSTRING(0);
58  macaddr *result;
59  int a,
60  b,
61  c,
62  d,
63  e,
64  f;
65  char junk[2];
66  int count;
67 
68  /* %1s matches iff there is trailing non-whitespace garbage */
69 
70  count = sscanf(str, "%x:%x:%x:%x:%x:%x%1s",
71  &a, &b, &c, &d, &e, &f, junk);
72  if (count != 6)
73  count = sscanf(str, "%x-%x-%x-%x-%x-%x%1s",
74  &a, &b, &c, &d, &e, &f, junk);
75  if (count != 6)
76  count = sscanf(str, "%2x%2x%2x:%2x%2x%2x%1s",
77  &a, &b, &c, &d, &e, &f, junk);
78  if (count != 6)
79  count = sscanf(str, "%2x%2x%2x-%2x%2x%2x%1s",
80  &a, &b, &c, &d, &e, &f, junk);
81  if (count != 6)
82  count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x%1s",
83  &a, &b, &c, &d, &e, &f, junk);
84  if (count != 6)
85  count = sscanf(str, "%2x%2x-%2x%2x-%2x%2x%1s",
86  &a, &b, &c, &d, &e, &f, junk);
87  if (count != 6)
88  count = sscanf(str, "%2x%2x%2x%2x%2x%2x%1s",
89  &a, &b, &c, &d, &e, &f, junk);
90  if (count != 6)
91  ereport(ERROR,
92  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
93  errmsg("invalid input syntax for type %s: \"%s\"", "macaddr",
94  str)));
95 
96  if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
97  (c < 0) || (c > 255) || (d < 0) || (d > 255) ||
98  (e < 0) || (e > 255) || (f < 0) || (f > 255))
99  ereport(ERROR,
100  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
101  errmsg("invalid octet value in \"macaddr\" value: \"%s\"", str)));
102 
103  result = (macaddr *) palloc(sizeof(macaddr));
104 
105  result->a = a;
106  result->b = b;
107  result->c = c;
108  result->d = d;
109  result->e = e;
110  result->f = f;
111 
112  PG_RETURN_MACADDR_P(result);
113 }
114 
115 /*
116  * MAC address output function. Fixed format.
117  */
118 
119 Datum
121 {
122  macaddr *addr = PG_GETARG_MACADDR_P(0);
123  char *result;
124 
125  result = (char *) palloc(32);
126 
127  snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x",
128  addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
129 
130  PG_RETURN_CSTRING(result);
131 }
132 
133 /*
134  * macaddr_recv - converts external binary format to macaddr
135  *
136  * The external representation is just the six bytes, MSB first.
137  */
138 Datum
140 {
142  macaddr *addr;
143 
144  addr = (macaddr *) palloc(sizeof(macaddr));
145 
146  addr->a = pq_getmsgbyte(buf);
147  addr->b = pq_getmsgbyte(buf);
148  addr->c = pq_getmsgbyte(buf);
149  addr->d = pq_getmsgbyte(buf);
150  addr->e = pq_getmsgbyte(buf);
151  addr->f = pq_getmsgbyte(buf);
152 
153  PG_RETURN_MACADDR_P(addr);
154 }
155 
156 /*
157  * macaddr_send - converts macaddr to binary format
158  */
159 Datum
161 {
162  macaddr *addr = PG_GETARG_MACADDR_P(0);
164 
166  pq_sendbyte(&buf, addr->a);
167  pq_sendbyte(&buf, addr->b);
168  pq_sendbyte(&buf, addr->c);
169  pq_sendbyte(&buf, addr->d);
170  pq_sendbyte(&buf, addr->e);
171  pq_sendbyte(&buf, addr->f);
173 }
174 
175 
176 /*
177  * Comparison function for sorting:
178  */
179 
180 static int
182 {
183  if (hibits(a1) < hibits(a2))
184  return -1;
185  else if (hibits(a1) > hibits(a2))
186  return 1;
187  else if (lobits(a1) < lobits(a2))
188  return -1;
189  else if (lobits(a1) > lobits(a2))
190  return 1;
191  else
192  return 0;
193 }
194 
195 Datum
197 {
200 
202 }
203 
204 /*
205  * Boolean comparisons.
206  */
207 
208 Datum
210 {
213 
215 }
216 
217 Datum
219 {
222 
224 }
225 
226 Datum
228 {
231 
233 }
234 
235 Datum
237 {
240 
242 }
243 
244 Datum
246 {
249 
251 }
252 
253 Datum
255 {
258 
260 }
261 
262 /*
263  * Support function for hash indexes on macaddr.
264  */
265 Datum
267 {
269 
270  return hash_any((unsigned char *) key, sizeof(macaddr));
271 }
272 
273 Datum
275 {
277 
278  return hash_any_extended((unsigned char *) key, sizeof(macaddr),
279  PG_GETARG_INT64(1));
280 }
281 
282 /*
283  * Arithmetic functions: bitwise NOT, AND, OR.
284  */
285 Datum
287 {
288  macaddr *addr = PG_GETARG_MACADDR_P(0);
289  macaddr *result;
290 
291  result = (macaddr *) palloc(sizeof(macaddr));
292  result->a = ~addr->a;
293  result->b = ~addr->b;
294  result->c = ~addr->c;
295  result->d = ~addr->d;
296  result->e = ~addr->e;
297  result->f = ~addr->f;
298  PG_RETURN_MACADDR_P(result);
299 }
300 
301 Datum
303 {
304  macaddr *addr1 = PG_GETARG_MACADDR_P(0);
305  macaddr *addr2 = PG_GETARG_MACADDR_P(1);
306  macaddr *result;
307 
308  result = (macaddr *) palloc(sizeof(macaddr));
309  result->a = addr1->a & addr2->a;
310  result->b = addr1->b & addr2->b;
311  result->c = addr1->c & addr2->c;
312  result->d = addr1->d & addr2->d;
313  result->e = addr1->e & addr2->e;
314  result->f = addr1->f & addr2->f;
315  PG_RETURN_MACADDR_P(result);
316 }
317 
318 Datum
320 {
321  macaddr *addr1 = PG_GETARG_MACADDR_P(0);
322  macaddr *addr2 = PG_GETARG_MACADDR_P(1);
323  macaddr *result;
324 
325  result = (macaddr *) palloc(sizeof(macaddr));
326  result->a = addr1->a | addr2->a;
327  result->b = addr1->b | addr2->b;
328  result->c = addr1->c | addr2->c;
329  result->d = addr1->d | addr2->d;
330  result->e = addr1->e | addr2->e;
331  result->f = addr1->f | addr2->f;
332  PG_RETURN_MACADDR_P(result);
333 }
334 
335 /*
336  * Truncation function to allow comparing mac manufacturers.
337  * From suggestion by Alex Pilosov <alex@pilosoft.com>
338  */
339 Datum
341 {
342  macaddr *addr = PG_GETARG_MACADDR_P(0);
343  macaddr *result;
344 
345  result = (macaddr *) palloc(sizeof(macaddr));
346 
347  result->a = addr->a;
348  result->b = addr->b;
349  result->c = addr->c;
350  result->d = 0;
351  result->e = 0;
352  result->f = 0;
353 
354  PG_RETURN_MACADDR_P(result);
355 }
356 
357 /*
358  * SortSupport strategy function. Populates a SortSupport struct with the
359  * information necessary to use comparison by abbreviated keys.
360  */
361 Datum
363 {
365 
367  ssup->ssup_extra = NULL;
368 
369  if (ssup->abbreviate)
370  {
372  MemoryContext oldcontext;
373 
374  oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
375 
376  uss = palloc(sizeof(macaddr_sortsupport_state));
377  uss->input_count = 0;
378  uss->estimating = true;
379  initHyperLogLog(&uss->abbr_card, 10);
380 
381  ssup->ssup_extra = uss;
382 
387 
388  MemoryContextSwitchTo(oldcontext);
389  }
390 
391  PG_RETURN_VOID();
392 }
393 
394 /*
395  * SortSupport "traditional" comparison function. Pulls two MAC addresses from
396  * the heap and runs a standard comparison on them.
397  */
398 static int
400 {
401  macaddr *arg1 = DatumGetMacaddrP(x);
402  macaddr *arg2 = DatumGetMacaddrP(y);
403 
404  return macaddr_cmp_internal(arg1, arg2);
405 }
406 
407 /*
408  * Callback for estimating effectiveness of abbreviated key optimization.
409  *
410  * We pay no attention to the cardinality of the non-abbreviated data, because
411  * there is no equality fast-path within authoritative macaddr comparator.
412  */
413 static bool
414 macaddr_abbrev_abort(int memtupcount, SortSupport ssup)
415 {
417  double abbr_card;
418 
419  if (memtupcount < 10000 || uss->input_count < 10000 || !uss->estimating)
420  return false;
421 
422  abbr_card = estimateHyperLogLog(&uss->abbr_card);
423 
424  /*
425  * If we have >100k distinct values, then even if we were sorting many
426  * billion rows we'd likely still break even, and the penalty of undoing
427  * that many rows of abbrevs would probably not be worth it. At this point
428  * we stop counting because we know that we're now fully committed.
429  */
430  if (abbr_card > 100000.0)
431  {
432 #ifdef TRACE_SORT
433  if (trace_sort)
434  elog(LOG,
435  "macaddr_abbrev: estimation ends at cardinality %f"
436  " after " INT64_FORMAT " values (%d rows)",
437  abbr_card, uss->input_count, memtupcount);
438 #endif
439  uss->estimating = false;
440  return false;
441  }
442 
443  /*
444  * Target minimum cardinality is 1 per ~2k of non-null inputs. 0.5 row
445  * fudge factor allows us to abort earlier on genuinely pathological data
446  * where we've had exactly one abbreviated value in the first 2k
447  * (non-null) rows.
448  */
449  if (abbr_card < uss->input_count / 2000.0 + 0.5)
450  {
451 #ifdef TRACE_SORT
452  if (trace_sort)
453  elog(LOG,
454  "macaddr_abbrev: aborting abbreviation at cardinality %f"
455  " below threshold %f after " INT64_FORMAT " values (%d rows)",
456  abbr_card, uss->input_count / 2000.0 + 0.5, uss->input_count,
457  memtupcount);
458 #endif
459  return true;
460  }
461 
462 #ifdef TRACE_SORT
463  if (trace_sort)
464  elog(LOG,
465  "macaddr_abbrev: cardinality %f after " INT64_FORMAT
466  " values (%d rows)", abbr_card, uss->input_count, memtupcount);
467 #endif
468 
469  return false;
470 }
471 
472 /*
473  * SortSupport conversion routine. Converts original macaddr representation
474  * to abbreviated key representation.
475  *
476  * Packs the bytes of a 6-byte MAC address into a Datum and treats it as an
477  * unsigned integer for purposes of comparison. On a 64-bit machine, there
478  * will be two zeroed bytes of padding. The integer is converted to native
479  * endianness to facilitate easy comparison.
480  */
481 static Datum
483 {
485  macaddr *authoritative = DatumGetMacaddrP(original);
486  Datum res;
487 
488  /*
489  * On a 64-bit machine, zero out the 8-byte datum and copy the 6 bytes of
490  * the MAC address in. There will be two bytes of zero padding on the end
491  * of the least significant bits.
492  */
493 #if SIZEOF_DATUM == 8
494  memset(&res, 0, SIZEOF_DATUM);
495  memcpy(&res, authoritative, sizeof(macaddr));
496 #else /* SIZEOF_DATUM != 8 */
497  memcpy(&res, authoritative, SIZEOF_DATUM);
498 #endif
499  uss->input_count += 1;
500 
501  /*
502  * Cardinality estimation. The estimate uses uint32, so on a 64-bit
503  * architecture, XOR the two 32-bit halves together to produce slightly
504  * more entropy. The two zeroed bytes won't have any practical impact on
505  * this operation.
506  */
507  if (uss->estimating)
508  {
509  uint32 tmp;
510 
511 #if SIZEOF_DATUM == 8
512  tmp = (uint32) res ^ (uint32) ((uint64) res >> 32);
513 #else /* SIZEOF_DATUM != 8 */
514  tmp = (uint32) res;
515 #endif
516 
518  }
519 
520  /*
521  * Byteswap on little-endian machines.
522  *
523  * This is needed so that ssup_datum_unsigned_cmp() (an unsigned integer
524  * 3-way comparator) works correctly on all platforms. Without this, the
525  * comparator would have to call memcmp() with a pair of pointers to the
526  * first byte of each abbreviated key, which is slower.
527  */
528  res = DatumBigEndianToNative(res);
529 
530  return res;
531 }
unsigned int uint32
Definition: c.h:442
#define INT64_FORMAT
Definition: c.h:484
int errcode(int sqlerrcode)
Definition: elog.c:695
int errmsg(const char *fmt,...)
Definition: elog.c:906
#define LOG
Definition: elog.h:27
#define ERROR
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:145
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:371
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:276
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:362
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:277
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
static Datum hash_uint32(uint32 k)
Definition: hashfn.h:43
static Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition: hashfn.h:37
static Datum hash_any(const unsigned char *k, int keylen)
Definition: hashfn.h:31
static const FormData_pg_attribute a1
Definition: heap.c:141
static const FormData_pg_attribute a2
Definition: heap.c:155
void initHyperLogLog(hyperLogLogState *cState, uint8 bwidth)
Definition: hyperloglog.c:66
double estimateHyperLogLog(hyperLogLogState *cState)
Definition: hyperloglog.c:186
void addHyperLogLog(hyperLogLogState *cState, uint32 hash)
Definition: hyperloglog.c:167
int y
Definition: isn.c:72
int b
Definition: isn.c:70
int x
Definition: isn.c:71
int a
Definition: isn.c:69
Datum macaddr_lt(PG_FUNCTION_ARGS)
Definition: mac.c:209
#define hibits(addr)
Definition: mac.c:30
Datum macaddr_cmp(PG_FUNCTION_ARGS)
Definition: mac.c:196
static int macaddr_cmp_internal(macaddr *a1, macaddr *a2)
Definition: mac.c:181
Datum hashmacaddrextended(PG_FUNCTION_ARGS)
Definition: mac.c:274
Datum hashmacaddr(PG_FUNCTION_ARGS)
Definition: mac.c:266
static Datum macaddr_abbrev_convert(Datum original, SortSupport ssup)
Definition: mac.c:482
Datum macaddr_or(PG_FUNCTION_ARGS)
Definition: mac.c:319
Datum macaddr_recv(PG_FUNCTION_ARGS)
Definition: mac.c:139
Datum macaddr_ne(PG_FUNCTION_ARGS)
Definition: mac.c:254
Datum macaddr_trunc(PG_FUNCTION_ARGS)
Definition: mac.c:340
Datum macaddr_eq(PG_FUNCTION_ARGS)
Definition: mac.c:227
Datum macaddr_in(PG_FUNCTION_ARGS)
Definition: mac.c:55
Datum macaddr_not(PG_FUNCTION_ARGS)
Definition: mac.c:286
static bool macaddr_abbrev_abort(int memtupcount, SortSupport ssup)
Definition: mac.c:414
Datum macaddr_and(PG_FUNCTION_ARGS)
Definition: mac.c:302
Datum macaddr_send(PG_FUNCTION_ARGS)
Definition: mac.c:160
Datum macaddr_sortsupport(PG_FUNCTION_ARGS)
Definition: mac.c:362
#define lobits(addr)
Definition: mac.c:33
Datum macaddr_ge(PG_FUNCTION_ARGS)
Definition: mac.c:236
static int macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup)
Definition: mac.c:399
Datum macaddr_le(PG_FUNCTION_ARGS)
Definition: mac.c:218
Datum macaddr_out(PG_FUNCTION_ARGS)
Definition: mac.c:120
Datum macaddr_gt(PG_FUNCTION_ARGS)
Definition: mac.c:245
void * palloc(Size size)
Definition: mcxt.c:1199
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:135
static char * buf
Definition: pg_test_fsync.c:67
#define snprintf
Definition: port.h:238
static uint32 DatumGetUInt32(Datum X)
Definition: postgres.h:570
uintptr_t Datum
Definition: postgres.h:412
#define SIZEOF_DATUM
Definition: postgres.h:429
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:328
int pq_getmsgbyte(StringInfo msg)
Definition: pqformat.c:401
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:348
static void pq_sendbyte(StringInfo buf, uint8 byt)
Definition: pqformat.h:161
char * c
e
Definition: preproc-init.c:82
struct SortSupportData * SortSupport
Definition: sortsupport.h:58
StringInfoData * StringInfo
Definition: stringinfo.h:44
int(* comparator)(Datum x, Datum y, SortSupport ssup)
Definition: sortsupport.h:106
Datum(* abbrev_converter)(Datum original, SortSupport ssup)
Definition: sortsupport.h:172
void * ssup_extra
Definition: sortsupport.h:87
MemoryContext ssup_cxt
Definition: sortsupport.h:66
int(* abbrev_full_comparator)(Datum x, Datum y, SortSupport ssup)
Definition: sortsupport.h:191
bool(* abbrev_abort)(int memtupcount, SortSupport ssup)
Definition: sortsupport.h:182
hyperLogLogState abbr_card
Definition: mac.c:42
Definition: inet.h:95
unsigned char e
Definition: inet.h:100
unsigned char b
Definition: inet.h:97
unsigned char f
Definition: inet.h:101
unsigned char c
Definition: inet.h:98
unsigned char a
Definition: inet.h:96
unsigned char d
Definition: inet.h:99
int ssup_datum_unsigned_cmp(Datum x, Datum y, SortSupport ssup)
Definition: tuplesort.c:3177
bool trace_sort
Definition: tuplesort.c:127
#define PG_GETARG_MACADDR_P(n)
Definition: inet.h:158
#define PG_RETURN_MACADDR_P(x)
Definition: inet.h:159
static macaddr * DatumGetMacaddrP(Datum X)
Definition: inet.h:147