LCOV - code coverage report
Current view: top level - src/backend/utils/adt - mac.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15beta1 Lines: 156 190 82.1 %
Date: 2022-05-18 04:09:35 Functions: 20 22 90.9 %
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-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 */
      43             : } macaddr_sortsupport_state;
      44             : 
      45             : static int  macaddr_cmp_internal(macaddr *a1, macaddr *a2);
      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
      55        3704 : macaddr_in(PG_FUNCTION_ARGS)
      56             : {
      57        3704 :     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        3704 :     count = sscanf(str, "%x:%x:%x:%x:%x:%x%1s",
      71             :                    &a, &b, &c, &d, &e, &f, junk);
      72        3704 :     if (count != 6)
      73          48 :         count = sscanf(str, "%x-%x-%x-%x-%x-%x%1s",
      74             :                        &a, &b, &c, &d, &e, &f, junk);
      75        3704 :     if (count != 6)
      76          42 :         count = sscanf(str, "%2x%2x%2x:%2x%2x%2x%1s",
      77             :                        &a, &b, &c, &d, &e, &f, junk);
      78        3704 :     if (count != 6)
      79          36 :         count = sscanf(str, "%2x%2x%2x-%2x%2x%2x%1s",
      80             :                        &a, &b, &c, &d, &e, &f, junk);
      81        3704 :     if (count != 6)
      82          30 :         count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x%1s",
      83             :                        &a, &b, &c, &d, &e, &f, junk);
      84        3704 :     if (count != 6)
      85          24 :         count = sscanf(str, "%2x%2x-%2x%2x-%2x%2x%1s",
      86             :                        &a, &b, &c, &d, &e, &f, junk);
      87        3704 :     if (count != 6)
      88          18 :         count = sscanf(str, "%2x%2x%2x%2x%2x%2x%1s",
      89             :                        &a, &b, &c, &d, &e, &f, junk);
      90        3704 :     if (count != 6)
      91          12 :         ereport(ERROR,
      92             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      93             :                  errmsg("invalid input syntax for type %s: \"%s\"", "macaddr",
      94             :                         str)));
      95             : 
      96        3692 :     if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
      97        3692 :         (c < 0) || (c > 255) || (d < 0) || (d > 255) ||
      98        3692 :         (e < 0) || (e > 255) || (f < 0) || (f > 255))
      99           0 :         ereport(ERROR,
     100             :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     101             :                  errmsg("invalid octet value in \"macaddr\" value: \"%s\"", str)));
     102             : 
     103        3692 :     result = (macaddr *) palloc(sizeof(macaddr));
     104             : 
     105        3692 :     result->a = a;
     106        3692 :     result->b = b;
     107        3692 :     result->c = c;
     108        3692 :     result->d = d;
     109        3692 :     result->e = e;
     110        3692 :     result->f = f;
     111             : 
     112        3692 :     PG_RETURN_MACADDR_P(result);
     113             : }
     114             : 
     115             : /*
     116             :  *  MAC address output function.  Fixed format.
     117             :  */
     118             : 
     119             : Datum
     120        3208 : macaddr_out(PG_FUNCTION_ARGS)
     121             : {
     122        3208 :     macaddr    *addr = PG_GETARG_MACADDR_P(0);
     123             :     char       *result;
     124             : 
     125        3208 :     result = (char *) palloc(32);
     126             : 
     127        3208 :     snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x",
     128        3208 :              addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
     129             : 
     130        3208 :     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
     139           0 : macaddr_recv(PG_FUNCTION_ARGS)
     140             : {
     141           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     142             :     macaddr    *addr;
     143             : 
     144           0 :     addr = (macaddr *) palloc(sizeof(macaddr));
     145             : 
     146           0 :     addr->a = pq_getmsgbyte(buf);
     147           0 :     addr->b = pq_getmsgbyte(buf);
     148           0 :     addr->c = pq_getmsgbyte(buf);
     149           0 :     addr->d = pq_getmsgbyte(buf);
     150           0 :     addr->e = pq_getmsgbyte(buf);
     151           0 :     addr->f = pq_getmsgbyte(buf);
     152             : 
     153           0 :     PG_RETURN_MACADDR_P(addr);
     154             : }
     155             : 
     156             : /*
     157             :  *      macaddr_send            - converts macaddr to binary format
     158             :  */
     159             : Datum
     160           0 : macaddr_send(PG_FUNCTION_ARGS)
     161             : {
     162           0 :     macaddr    *addr = PG_GETARG_MACADDR_P(0);
     163             :     StringInfoData buf;
     164             : 
     165           0 :     pq_begintypsend(&buf);
     166           0 :     pq_sendbyte(&buf, addr->a);
     167           0 :     pq_sendbyte(&buf, addr->b);
     168           0 :     pq_sendbyte(&buf, addr->c);
     169           0 :     pq_sendbyte(&buf, addr->d);
     170           0 :     pq_sendbyte(&buf, addr->e);
     171           0 :     pq_sendbyte(&buf, addr->f);
     172           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     173             : }
     174             : 
     175             : 
     176             : /*
     177             :  *  Comparison function for sorting:
     178             :  */
     179             : 
     180             : static int
     181       63330 : macaddr_cmp_internal(macaddr *a1, macaddr *a2)
     182             : {
     183       63330 :     if (hibits(a1) < hibits(a2))
     184       27794 :         return -1;
     185       35536 :     else if (hibits(a1) > hibits(a2))
     186       26624 :         return 1;
     187        8912 :     else if (lobits(a1) < lobits(a2))
     188          58 :         return -1;
     189        8854 :     else if (lobits(a1) > lobits(a2))
     190          50 :         return 1;
     191             :     else
     192        8804 :         return 0;
     193             : }
     194             : 
     195             : Datum
     196       13278 : macaddr_cmp(PG_FUNCTION_ARGS)
     197             : {
     198       13278 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     199       13278 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     200             : 
     201       13278 :     PG_RETURN_INT32(macaddr_cmp_internal(a1, a2));
     202             : }
     203             : 
     204             : /*
     205             :  *  Boolean comparisons.
     206             :  */
     207             : 
     208             : Datum
     209       18778 : macaddr_lt(PG_FUNCTION_ARGS)
     210             : {
     211       18778 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     212       18778 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     213             : 
     214       18778 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) < 0);
     215             : }
     216             : 
     217             : Datum
     218        6236 : macaddr_le(PG_FUNCTION_ARGS)
     219             : {
     220        6236 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     221        6236 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     222             : 
     223        6236 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) <= 0);
     224             : }
     225             : 
     226             : Datum
     227        6400 : macaddr_eq(PG_FUNCTION_ARGS)
     228             : {
     229        6400 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     230        6400 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     231             : 
     232        6400 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) == 0);
     233             : }
     234             : 
     235             : Datum
     236        4914 : macaddr_ge(PG_FUNCTION_ARGS)
     237             : {
     238        4914 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     239        4914 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     240             : 
     241        4914 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) >= 0);
     242             : }
     243             : 
     244             : Datum
     245        7768 : macaddr_gt(PG_FUNCTION_ARGS)
     246             : {
     247        7768 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     248        7768 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     249             : 
     250        7768 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) > 0);
     251             : }
     252             : 
     253             : Datum
     254          24 : macaddr_ne(PG_FUNCTION_ARGS)
     255             : {
     256          24 :     macaddr    *a1 = PG_GETARG_MACADDR_P(0);
     257          24 :     macaddr    *a2 = PG_GETARG_MACADDR_P(1);
     258             : 
     259          24 :     PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) != 0);
     260             : }
     261             : 
     262             : /*
     263             :  * Support function for hash indexes on macaddr.
     264             :  */
     265             : Datum
     266        1082 : hashmacaddr(PG_FUNCTION_ARGS)
     267             : {
     268        1082 :     macaddr    *key = PG_GETARG_MACADDR_P(0);
     269             : 
     270        1082 :     return hash_any((unsigned char *) key, sizeof(macaddr));
     271             : }
     272             : 
     273             : Datum
     274          60 : hashmacaddrextended(PG_FUNCTION_ARGS)
     275             : {
     276          60 :     macaddr    *key = PG_GETARG_MACADDR_P(0);
     277             : 
     278         120 :     return hash_any_extended((unsigned char *) key, sizeof(macaddr),
     279          60 :                              PG_GETARG_INT64(1));
     280             : }
     281             : 
     282             : /*
     283             :  * Arithmetic functions: bitwise NOT, AND, OR.
     284             :  */
     285             : Datum
     286          72 : macaddr_not(PG_FUNCTION_ARGS)
     287             : {
     288          72 :     macaddr    *addr = PG_GETARG_MACADDR_P(0);
     289             :     macaddr    *result;
     290             : 
     291          72 :     result = (macaddr *) palloc(sizeof(macaddr));
     292          72 :     result->a = ~addr->a;
     293          72 :     result->b = ~addr->b;
     294          72 :     result->c = ~addr->c;
     295          72 :     result->d = ~addr->d;
     296          72 :     result->e = ~addr->e;
     297          72 :     result->f = ~addr->f;
     298          72 :     PG_RETURN_MACADDR_P(result);
     299             : }
     300             : 
     301             : Datum
     302          72 : macaddr_and(PG_FUNCTION_ARGS)
     303             : {
     304          72 :     macaddr    *addr1 = PG_GETARG_MACADDR_P(0);
     305          72 :     macaddr    *addr2 = PG_GETARG_MACADDR_P(1);
     306             :     macaddr    *result;
     307             : 
     308          72 :     result = (macaddr *) palloc(sizeof(macaddr));
     309          72 :     result->a = addr1->a & addr2->a;
     310          72 :     result->b = addr1->b & addr2->b;
     311          72 :     result->c = addr1->c & addr2->c;
     312          72 :     result->d = addr1->d & addr2->d;
     313          72 :     result->e = addr1->e & addr2->e;
     314          72 :     result->f = addr1->f & addr2->f;
     315          72 :     PG_RETURN_MACADDR_P(result);
     316             : }
     317             : 
     318             : Datum
     319          72 : macaddr_or(PG_FUNCTION_ARGS)
     320             : {
     321          72 :     macaddr    *addr1 = PG_GETARG_MACADDR_P(0);
     322          72 :     macaddr    *addr2 = PG_GETARG_MACADDR_P(1);
     323             :     macaddr    *result;
     324             : 
     325          72 :     result = (macaddr *) palloc(sizeof(macaddr));
     326          72 :     result->a = addr1->a | addr2->a;
     327          72 :     result->b = addr1->b | addr2->b;
     328          72 :     result->c = addr1->c | addr2->c;
     329          72 :     result->d = addr1->d | addr2->d;
     330          72 :     result->e = addr1->e | addr2->e;
     331          72 :     result->f = addr1->f | addr2->f;
     332          72 :     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
     340          72 : macaddr_trunc(PG_FUNCTION_ARGS)
     341             : {
     342          72 :     macaddr    *addr = PG_GETARG_MACADDR_P(0);
     343             :     macaddr    *result;
     344             : 
     345          72 :     result = (macaddr *) palloc(sizeof(macaddr));
     346             : 
     347          72 :     result->a = addr->a;
     348          72 :     result->b = addr->b;
     349          72 :     result->c = addr->c;
     350          72 :     result->d = 0;
     351          72 :     result->e = 0;
     352          72 :     result->f = 0;
     353             : 
     354          72 :     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
     362          28 : macaddr_sortsupport(PG_FUNCTION_ARGS)
     363             : {
     364          28 :     SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
     365             : 
     366          28 :     ssup->comparator = macaddr_fast_cmp;
     367          28 :     ssup->ssup_extra = NULL;
     368             : 
     369          28 :     if (ssup->abbreviate)
     370             :     {
     371             :         macaddr_sortsupport_state *uss;
     372             :         MemoryContext oldcontext;
     373             : 
     374          20 :         oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
     375             : 
     376          20 :         uss = palloc(sizeof(macaddr_sortsupport_state));
     377          20 :         uss->input_count = 0;
     378          20 :         uss->estimating = true;
     379          20 :         initHyperLogLog(&uss->abbr_card, 10);
     380             : 
     381          20 :         ssup->ssup_extra = uss;
     382             : 
     383          20 :         ssup->comparator = ssup_datum_unsigned_cmp;
     384          20 :         ssup->abbrev_converter = macaddr_abbrev_convert;
     385          20 :         ssup->abbrev_abort = macaddr_abbrev_abort;
     386          20 :         ssup->abbrev_full_comparator = macaddr_fast_cmp;
     387             : 
     388          20 :         MemoryContextSwitchTo(oldcontext);
     389             :     }
     390             : 
     391          28 :     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
     399        5932 : macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup)
     400             : {
     401        5932 :     macaddr    *arg1 = DatumGetMacaddrP(x);
     402        5932 :     macaddr    *arg2 = DatumGetMacaddrP(y);
     403             : 
     404        5932 :     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          12 : macaddr_abbrev_abort(int memtupcount, SortSupport ssup)
     415             : {
     416          12 :     macaddr_sortsupport_state *uss = ssup->ssup_extra;
     417             :     double      abbr_card;
     418             : 
     419          12 :     if (memtupcount < 10000 || uss->input_count < 10000 || !uss->estimating)
     420          12 :         return false;
     421             : 
     422           0 :     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           0 :     if (abbr_card > 100000.0)
     431             :     {
     432             : #ifdef TRACE_SORT
     433           0 :         if (trace_sort)
     434           0 :             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           0 :         uss->estimating = false;
     440           0 :         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           0 :     if (abbr_card < uss->input_count / 2000.0 + 0.5)
     450             :     {
     451             : #ifdef TRACE_SORT
     452           0 :         if (trace_sort)
     453           0 :             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           0 :         return true;
     460             :     }
     461             : 
     462             : #ifdef TRACE_SORT
     463           0 :     if (trace_sort)
     464           0 :         elog(LOG,
     465             :              "macaddr_abbrev: cardinality %f after " INT64_FORMAT
     466             :              " values (%d rows)", abbr_card, uss->input_count, memtupcount);
     467             : #endif
     468             : 
     469           0 :     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
     482         168 : macaddr_abbrev_convert(Datum original, SortSupport ssup)
     483             : {
     484         168 :     macaddr_sortsupport_state *uss = ssup->ssup_extra;
     485         168 :     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         168 :     memcpy(&res, authoritative, sizeof(macaddr));
     496             : #else                           /* SIZEOF_DATUM != 8 */
     497             :     memcpy(&res, authoritative, SIZEOF_DATUM);
     498             : #endif
     499         168 :     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         168 :     if (uss->estimating)
     508             :     {
     509             :         uint32      tmp;
     510             : 
     511             : #if SIZEOF_DATUM == 8
     512         168 :         tmp = (uint32) res ^ (uint32) ((uint64) res >> 32);
     513             : #else                           /* SIZEOF_DATUM != 8 */
     514             :         tmp = (uint32) res;
     515             : #endif
     516             : 
     517         168 :         addHyperLogLog(&uss->abbr_card, DatumGetUInt32(hash_uint32(tmp)));
     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         168 :     res = DatumBigEndianToNative(res);
     529             : 
     530         168 :     return res;
     531             : }

Generated by: LCOV version 1.14