LCOV - code coverage report
Current view: top level - src/backend/utils/adt - mac.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 87.3 % 165 144
Test Date: 2026-04-07 14:16:30 Functions: 90.9 % 22 20
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * mac.c
       4              :  *    PostgreSQL type definitions for 6 byte, EUI-48, MAC addresses.
       5              :  *
       6              :  * Portions Copyright (c) 1998-2026, 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 "libpq/pqformat.h"
      18              : #include "port/pg_bswap.h"
      19              : #include "utils/fmgrprotos.h"
      20              : #include "utils/inet.h"
      21              : #include "utils/sortsupport.h"
      22              : 
      23              : 
      24              : /*
      25              :  *  Utility macros used for sorting and comparing:
      26              :  */
      27              : 
      28              : #define hibits(addr) \
      29              :   ((unsigned long)(((addr)->a<<16)|((addr)->b<<8)|((addr)->c)))
      30              : 
      31              : #define lobits(addr) \
      32              :   ((unsigned long)(((addr)->d<<16)|((addr)->e<<8)|((addr)->f)))
      33              : 
      34              : static int  macaddr_cmp_internal(macaddr *a1, macaddr *a2);
      35              : static int  macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup);
      36              : static bool macaddr_abbrev_abort(int memtupcount, SortSupport ssup);
      37              : static Datum macaddr_abbrev_convert(Datum original, SortSupport ssup);
      38              : 
      39              : /*
      40              :  *  MAC address reader.  Accepts several common notations.
      41              :  */
      42              : 
      43              : Datum
      44         2276 : macaddr_in(PG_FUNCTION_ARGS)
      45              : {
      46         2276 :     char       *str = PG_GETARG_CSTRING(0);
      47         2276 :     Node       *escontext = fcinfo->context;
      48              :     macaddr    *result;
      49              :     int         a,
      50              :                 b,
      51              :                 c,
      52              :                 d,
      53              :                 e,
      54              :                 f;
      55              :     char        junk[2];
      56              :     int         count;
      57              : 
      58              :     /* %1s matches iff there is trailing non-whitespace garbage */
      59              : 
      60         2276 :     count = sscanf(str, "%x:%x:%x:%x:%x:%x%1s",
      61              :                    &a, &b, &c, &d, &e, &f, junk);
      62         2276 :     if (count != 6)
      63           48 :         count = sscanf(str, "%x-%x-%x-%x-%x-%x%1s",
      64              :                        &a, &b, &c, &d, &e, &f, junk);
      65         2276 :     if (count != 6)
      66           44 :         count = sscanf(str, "%2x%2x%2x:%2x%2x%2x%1s",
      67              :                        &a, &b, &c, &d, &e, &f, junk);
      68         2276 :     if (count != 6)
      69           40 :         count = sscanf(str, "%2x%2x%2x-%2x%2x%2x%1s",
      70              :                        &a, &b, &c, &d, &e, &f, junk);
      71         2276 :     if (count != 6)
      72           36 :         count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x%1s",
      73              :                        &a, &b, &c, &d, &e, &f, junk);
      74         2276 :     if (count != 6)
      75           32 :         count = sscanf(str, "%2x%2x-%2x%2x-%2x%2x%1s",
      76              :                        &a, &b, &c, &d, &e, &f, junk);
      77         2276 :     if (count != 6)
      78           28 :         count = sscanf(str, "%2x%2x%2x%2x%2x%2x%1s",
      79              :                        &a, &b, &c, &d, &e, &f, junk);
      80         2276 :     if (count != 6)
      81           24 :         ereturn(escontext, (Datum) 0,
      82              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      83              :                  errmsg("invalid input syntax for type %s: \"%s\"", "macaddr",
      84              :                         str)));
      85              : 
      86         2252 :     if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
      87         2252 :         (c < 0) || (c > 255) || (d < 0) || (d > 255) ||
      88         2252 :         (e < 0) || (e > 255) || (f < 0) || (f > 255))
      89            0 :         ereturn(escontext, (Datum) 0,
      90              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
      91              :                  errmsg("invalid octet value in \"macaddr\" value: \"%s\"", str)));
      92              : 
      93         2252 :     result = palloc_object(macaddr);
      94              : 
      95         2252 :     result->a = a;
      96         2252 :     result->b = b;
      97         2252 :     result->c = c;
      98         2252 :     result->d = d;
      99         2252 :     result->e = e;
     100         2252 :     result->f = f;
     101              : 
     102         2252 :     PG_RETURN_MACADDR_P(result);
     103              : }
     104              : 
     105              : /*
     106              :  *  MAC address output function.  Fixed format.
     107              :  */
     108              : 
     109              : Datum
     110         1949 : macaddr_out(PG_FUNCTION_ARGS)
     111              : {
     112         1949 :     macaddr    *addr = PG_GETARG_MACADDR_P(0);
     113              :     char       *result;
     114              : 
     115         1949 :     result = (char *) palloc(32);
     116              : 
     117         1949 :     snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x",
     118         1949 :              addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
     119              : 
     120         1949 :     PG_RETURN_CSTRING(result);
     121              : }
     122              : 
     123              : /*
     124              :  *      macaddr_recv            - converts external binary format to macaddr
     125              :  *
     126              :  * The external representation is just the six bytes, MSB first.
     127              :  */
     128              : Datum
     129            0 : macaddr_recv(PG_FUNCTION_ARGS)
     130              : {
     131            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     132              :     macaddr    *addr;
     133              : 
     134            0 :     addr = palloc_object(macaddr);
     135              : 
     136            0 :     addr->a = pq_getmsgbyte(buf);
     137            0 :     addr->b = pq_getmsgbyte(buf);
     138            0 :     addr->c = pq_getmsgbyte(buf);
     139            0 :     addr->d = pq_getmsgbyte(buf);
     140            0 :     addr->e = pq_getmsgbyte(buf);
     141            0 :     addr->f = pq_getmsgbyte(buf);
     142              : 
     143            0 :     PG_RETURN_MACADDR_P(addr);
     144              : }
     145              : 
     146              : /*
     147              :  *      macaddr_send            - converts macaddr to binary format
     148              :  */
     149              : Datum
     150            0 : macaddr_send(PG_FUNCTION_ARGS)
     151              : {
     152            0 :     macaddr    *addr = PG_GETARG_MACADDR_P(0);
     153              :     StringInfoData buf;
     154              : 
     155            0 :     pq_begintypsend(&buf);
     156            0 :     pq_sendbyte(&buf, addr->a);
     157            0 :     pq_sendbyte(&buf, addr->b);
     158            0 :     pq_sendbyte(&buf, addr->c);
     159            0 :     pq_sendbyte(&buf, addr->d);
     160            0 :     pq_sendbyte(&buf, addr->e);
     161            0 :     pq_sendbyte(&buf, addr->f);
     162            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     163              : }
     164              : 
     165              : 
     166              : /*
     167              :  *  Comparison function for sorting:
     168              :  */
     169              : 
     170              : static int
     171        38835 : macaddr_cmp_internal(macaddr *a1, macaddr *a2)
     172              : {
     173        38835 :     if (hibits(a1) < hibits(a2))
     174        16115 :         return -1;
     175        22720 :     else if (hibits(a1) > hibits(a2))
     176        14431 :         return 1;
     177         8289 :     else if (lobits(a1) < lobits(a2))
     178           37 :         return -1;
     179         8252 :     else if (lobits(a1) > lobits(a2))
     180           25 :         return 1;
     181              :     else
     182         8227 :         return 0;
     183              : }
     184              : 
     185              : Datum
     186         7929 : macaddr_cmp(PG_FUNCTION_ARGS)
     187              : {
     188         7929 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     189         7929 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     190              : 
     191         7929 :     PG_RETURN_INT32(macaddr_cmp_internal(a1, a2));
     192              : }
     193              : 
     194              : /*
     195              :  *  Boolean comparisons.
     196              :  */
     197              : 
     198              : Datum
     199        12903 : macaddr_lt(PG_FUNCTION_ARGS)
     200              : {
     201        12903 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     202        12903 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     203              : 
     204        12903 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) < 0);
     205              : }
     206              : 
     207              : Datum
     208         3753 : macaddr_le(PG_FUNCTION_ARGS)
     209              : {
     210         3753 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     211         3753 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     212              : 
     213         3753 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) <= 0);
     214              : }
     215              : 
     216              : Datum
     217         3921 : macaddr_eq(PG_FUNCTION_ARGS)
     218              : {
     219         3921 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     220         3921 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     221              : 
     222         3921 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) == 0);
     223              : }
     224              : 
     225              : Datum
     226         3022 : macaddr_ge(PG_FUNCTION_ARGS)
     227              : {
     228         3022 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     229         3022 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     230              : 
     231         3022 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) >= 0);
     232              : }
     233              : 
     234              : Datum
     235         5673 : macaddr_gt(PG_FUNCTION_ARGS)
     236              : {
     237         5673 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     238         5673 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     239              : 
     240         5673 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) > 0);
     241              : }
     242              : 
     243              : Datum
     244           16 : macaddr_ne(PG_FUNCTION_ARGS)
     245              : {
     246           16 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     247           16 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     248              : 
     249           16 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) != 0);
     250              : }
     251              : 
     252              : /*
     253              :  * Support function for hash indexes on macaddr.
     254              :  */
     255              : Datum
     256         1558 : hashmacaddr(PG_FUNCTION_ARGS)
     257              : {
     258         1558 :     macaddr    *key = PG_GETARG_MACADDR_P(0);
     259              : 
     260         1558 :     return hash_any((unsigned char *) key, sizeof(macaddr));
     261              : }
     262              : 
     263              : Datum
     264           40 : hashmacaddrextended(PG_FUNCTION_ARGS)
     265              : {
     266           40 :     macaddr    *key = PG_GETARG_MACADDR_P(0);
     267              : 
     268           40 :     return hash_any_extended((unsigned char *) key, sizeof(macaddr),
     269           40 :                              PG_GETARG_INT64(1));
     270              : }
     271              : 
     272              : /*
     273              :  * Arithmetic functions: bitwise NOT, AND, OR.
     274              :  */
     275              : Datum
     276           48 : macaddr_not(PG_FUNCTION_ARGS)
     277              : {
     278           48 :     macaddr    *addr = PG_GETARG_MACADDR_P(0);
     279              :     macaddr    *result;
     280              : 
     281           48 :     result = palloc_object(macaddr);
     282           48 :     result->a = ~addr->a;
     283           48 :     result->b = ~addr->b;
     284           48 :     result->c = ~addr->c;
     285           48 :     result->d = ~addr->d;
     286           48 :     result->e = ~addr->e;
     287           48 :     result->f = ~addr->f;
     288           48 :     PG_RETURN_MACADDR_P(result);
     289              : }
     290              : 
     291              : Datum
     292           48 : macaddr_and(PG_FUNCTION_ARGS)
     293              : {
     294           48 :     macaddr    *addr1 = PG_GETARG_MACADDR_P(0);
     295           48 :     macaddr    *addr2 = PG_GETARG_MACADDR_P(1);
     296              :     macaddr    *result;
     297              : 
     298           48 :     result = palloc_object(macaddr);
     299           48 :     result->a = addr1->a & addr2->a;
     300           48 :     result->b = addr1->b & addr2->b;
     301           48 :     result->c = addr1->c & addr2->c;
     302           48 :     result->d = addr1->d & addr2->d;
     303           48 :     result->e = addr1->e & addr2->e;
     304           48 :     result->f = addr1->f & addr2->f;
     305           48 :     PG_RETURN_MACADDR_P(result);
     306              : }
     307              : 
     308              : Datum
     309           48 : macaddr_or(PG_FUNCTION_ARGS)
     310              : {
     311           48 :     macaddr    *addr1 = PG_GETARG_MACADDR_P(0);
     312           48 :     macaddr    *addr2 = PG_GETARG_MACADDR_P(1);
     313              :     macaddr    *result;
     314              : 
     315           48 :     result = palloc_object(macaddr);
     316           48 :     result->a = addr1->a | addr2->a;
     317           48 :     result->b = addr1->b | addr2->b;
     318           48 :     result->c = addr1->c | addr2->c;
     319           48 :     result->d = addr1->d | addr2->d;
     320           48 :     result->e = addr1->e | addr2->e;
     321           48 :     result->f = addr1->f | addr2->f;
     322           48 :     PG_RETURN_MACADDR_P(result);
     323              : }
     324              : 
     325              : /*
     326              :  *  Truncation function to allow comparing mac manufacturers.
     327              :  *  From suggestion by Alex Pilosov <alex@pilosoft.com>
     328              :  */
     329              : Datum
     330           48 : macaddr_trunc(PG_FUNCTION_ARGS)
     331              : {
     332           48 :     macaddr    *addr = PG_GETARG_MACADDR_P(0);
     333              :     macaddr    *result;
     334              : 
     335           48 :     result = palloc_object(macaddr);
     336              : 
     337           48 :     result->a = addr->a;
     338           48 :     result->b = addr->b;
     339           48 :     result->c = addr->c;
     340           48 :     result->d = 0;
     341           48 :     result->e = 0;
     342           48 :     result->f = 0;
     343              : 
     344           48 :     PG_RETURN_MACADDR_P(result);
     345              : }
     346              : 
     347              : /*
     348              :  * SortSupport strategy function. Populates a SortSupport struct with the
     349              :  * information necessary to use comparison by abbreviated keys.
     350              :  */
     351              : Datum
     352           14 : macaddr_sortsupport(PG_FUNCTION_ARGS)
     353              : {
     354           14 :     SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
     355              : 
     356           14 :     ssup->comparator = macaddr_fast_cmp;
     357           14 :     ssup->ssup_extra = NULL;
     358              : 
     359           14 :     if (ssup->abbreviate)
     360              :     {
     361           12 :         ssup->comparator = ssup_datum_unsigned_cmp;
     362           12 :         ssup->abbrev_converter = macaddr_abbrev_convert;
     363           12 :         ssup->abbrev_abort = macaddr_abbrev_abort;
     364           12 :         ssup->abbrev_full_comparator = macaddr_fast_cmp;
     365              :     }
     366              : 
     367           14 :     PG_RETURN_VOID();
     368              : }
     369              : 
     370              : /*
     371              :  * SortSupport "traditional" comparison function. Pulls two MAC addresses from
     372              :  * the heap and runs a standard comparison on them.
     373              :  */
     374              : static int
     375         1618 : macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup)
     376              : {
     377         1618 :     macaddr    *arg1 = DatumGetMacaddrP(x);
     378         1618 :     macaddr    *arg2 = DatumGetMacaddrP(y);
     379              : 
     380         1618 :     return macaddr_cmp_internal(arg1, arg2);
     381              : }
     382              : 
     383              : /*
     384              :  * Abbreviation is never aborted for macaddr because the 6-byte MAC address
     385              :  * fits entirely within a 64-bit Datum, making the abbreviated key
     386              :  * authoritative.
     387              :  */
     388              : static bool
     389            8 : macaddr_abbrev_abort(int memtupcount, SortSupport ssup)
     390              : {
     391            8 :     return false;
     392              : }
     393              : 
     394              : /*
     395              :  * SortSupport conversion routine. Converts original macaddr representation
     396              :  * to abbreviated key representation.
     397              :  *
     398              :  * Packs the bytes of a 6-byte MAC address into a Datum and treats it as an
     399              :  * unsigned integer for purposes of comparison. There will be two zeroed bytes
     400              :  * of padding. The integer is converted to native endianness to facilitate
     401              :  * easy comparison.
     402              :  */
     403              : static Datum
     404          108 : macaddr_abbrev_convert(Datum original, SortSupport ssup)
     405              : {
     406          108 :     macaddr    *authoritative = DatumGetMacaddrP(original);
     407              :     Datum       res;
     408              : 
     409              :     /*
     410              :      * Zero out the 8-byte Datum and copy in the 6 bytes of the MAC address.
     411              :      * There will be two bytes of zero padding on the end of the least
     412              :      * significant bits.
     413              :      */
     414              :     StaticAssertDecl(sizeof(res) >= sizeof(macaddr),
     415              :                      "Datum is too small for macaddr");
     416          108 :     memset(&res, 0, sizeof(res));
     417          108 :     memcpy(&res, authoritative, sizeof(macaddr));
     418              : 
     419              :     /*
     420              :      * Byteswap on little-endian machines.
     421              :      *
     422              :      * This is needed so that ssup_datum_unsigned_cmp() (an unsigned integer
     423              :      * 3-way comparator) works correctly on all platforms. Without this, the
     424              :      * comparator would have to call memcmp() with a pair of pointers to the
     425              :      * first byte of each abbreviated key, which is slower.
     426              :      */
     427          108 :     res = DatumBigEndianToNative(res);
     428              : 
     429          108 :     return res;
     430              : }
        

Generated by: LCOV version 2.0-1