LCOV - code coverage report
Current view: top level - src/backend/utils/adt - mac.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 87.3 % 165 144
Test Date: 2026-07-22 12:15:41 Functions: 90.9 % 22 20
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 73.1 % 52 38

             Branch data     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                 :       14896 :         return -1;
     175         [ +  + ]:       23939 :     else if (hibits(a1) > hibits(a2))
     176                 :       15650 :         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                 :       12603 : macaddr_lt(PG_FUNCTION_ARGS)
     200                 :             : {
     201                 :       12603 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     202                 :       12603 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     203                 :             : 
     204                 :       12603 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) < 0);
     205                 :             : }
     206                 :             : 
     207                 :             : Datum
     208                 :        3306 : macaddr_le(PG_FUNCTION_ARGS)
     209                 :             : {
     210                 :        3306 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     211                 :        3306 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     212                 :             : 
     213                 :        3306 :     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                 :        3469 : macaddr_ge(PG_FUNCTION_ARGS)
     227                 :             : {
     228                 :        3469 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     229                 :        3469 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     230                 :             : 
     231                 :        3469 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) >= 0);
     232                 :             : }
     233                 :             : 
     234                 :             : Datum
     235                 :        5973 : macaddr_gt(PG_FUNCTION_ARGS)
     236                 :             : {
     237                 :        5973 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     238                 :        5973 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     239                 :             : 
     240                 :        5973 :     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