2017-03-15 16:16:25 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* mac.c
|
|
|
|
* PostgreSQL type definitions for 6 byte, EUI-48, MAC addresses.
|
|
|
|
*
|
2020-01-01 18:21:45 +01:00
|
|
|
* Portions Copyright (c) 1998-2020, PostgreSQL Global Development Group
|
2017-03-15 16:16:25 +01:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
|
|
|
* src/backend/utils/adt/mac.c
|
1998-10-03 07:41:01 +02:00
|
|
|
*
|
2017-03-15 16:16:25 +01:00
|
|
|
*-------------------------------------------------------------------------
|
1998-10-03 07:41:01 +02:00
|
|
|
*/
|
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "postgres.h"
|
2000-07-06 07:48:31 +02:00
|
|
|
|
2017-03-29 22:28:56 +02:00
|
|
|
#include "lib/hyperloglog.h"
|
2003-05-13 20:03:08 +02:00
|
|
|
#include "libpq/pqformat.h"
|
2017-03-29 22:28:56 +02:00
|
|
|
#include "port/pg_bswap.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "utils/builtins.h"
|
2017-03-29 22:28:56 +02:00
|
|
|
#include "utils/guc.h"
|
2019-03-11 17:17:50 +01:00
|
|
|
#include "utils/hashutils.h"
|
2000-08-04 01:07:51 +02:00
|
|
|
#include "utils/inet.h"
|
2017-03-29 22:28:56 +02:00
|
|
|
#include "utils/sortsupport.h"
|
2000-07-06 07:48:31 +02:00
|
|
|
|
2003-05-13 20:03:08 +02:00
|
|
|
|
1998-10-03 07:41:01 +02:00
|
|
|
/*
|
|
|
|
* Utility macros used for sorting and comparing:
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define hibits(addr) \
|
2000-08-04 01:07:51 +02:00
|
|
|
((unsigned long)(((addr)->a<<16)|((addr)->b<<8)|((addr)->c)))
|
1998-10-03 07:41:01 +02:00
|
|
|
|
|
|
|
#define lobits(addr) \
|
2000-08-04 01:07:51 +02:00
|
|
|
((unsigned long)(((addr)->d<<16)|((addr)->e<<8)|((addr)->f)))
|
1998-10-03 07:41:01 +02:00
|
|
|
|
2017-03-29 22:28:56 +02:00
|
|
|
/* sortsupport for macaddr */
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
int64 input_count; /* number of non-null values seen */
|
|
|
|
bool estimating; /* true if estimating cardinality */
|
|
|
|
|
|
|
|
hyperLogLogState abbr_card; /* cardinality estimator */
|
2017-05-17 22:31:56 +02:00
|
|
|
} macaddr_sortsupport_state;
|
2017-03-29 22:28:56 +02:00
|
|
|
|
|
|
|
static int macaddr_cmp_internal(macaddr *a1, macaddr *a2);
|
|
|
|
static int macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup);
|
|
|
|
static int macaddr_cmp_abbrev(Datum x, Datum y, SortSupport ssup);
|
|
|
|
static bool macaddr_abbrev_abort(int memtupcount, SortSupport ssup);
|
|
|
|
static Datum macaddr_abbrev_convert(Datum original, SortSupport ssup);
|
|
|
|
|
1998-10-03 07:41:01 +02:00
|
|
|
/*
|
|
|
|
* MAC address reader. Accepts several common notations.
|
|
|
|
*/
|
2000-08-23 08:04:49 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
macaddr_in(PG_FUNCTION_ARGS)
|
1998-10-03 07:41:01 +02:00
|
|
|
{
|
2000-08-04 01:07:51 +02:00
|
|
|
char *str = PG_GETARG_CSTRING(0);
|
2001-08-21 23:23:21 +02:00
|
|
|
macaddr *result;
|
1998-10-03 07:41:01 +02:00
|
|
|
int a,
|
|
|
|
b,
|
|
|
|
c,
|
|
|
|
d,
|
|
|
|
e,
|
|
|
|
f;
|
2002-10-13 17:39:17 +02:00
|
|
|
char junk[2];
|
1998-10-03 07:41:01 +02:00
|
|
|
int count;
|
|
|
|
|
2002-10-13 17:39:17 +02:00
|
|
|
/* %1s matches iff there is trailing non-whitespace garbage */
|
|
|
|
|
|
|
|
count = sscanf(str, "%x:%x:%x:%x:%x:%x%1s",
|
|
|
|
&a, &b, &c, &d, &e, &f, junk);
|
2001-08-21 23:23:21 +02:00
|
|
|
if (count != 6)
|
2002-10-13 17:39:17 +02:00
|
|
|
count = sscanf(str, "%x-%x-%x-%x-%x-%x%1s",
|
|
|
|
&a, &b, &c, &d, &e, &f, junk);
|
2001-08-21 23:23:21 +02:00
|
|
|
if (count != 6)
|
2002-10-13 17:39:17 +02:00
|
|
|
count = sscanf(str, "%2x%2x%2x:%2x%2x%2x%1s",
|
|
|
|
&a, &b, &c, &d, &e, &f, junk);
|
2001-08-21 23:23:21 +02:00
|
|
|
if (count != 6)
|
2002-10-13 17:39:17 +02:00
|
|
|
count = sscanf(str, "%2x%2x%2x-%2x%2x%2x%1s",
|
|
|
|
&a, &b, &c, &d, &e, &f, junk);
|
2001-08-21 23:23:21 +02:00
|
|
|
if (count != 6)
|
2002-10-13 17:39:17 +02:00
|
|
|
count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x%1s",
|
|
|
|
&a, &b, &c, &d, &e, &f, junk);
|
2014-10-21 22:16:39 +02:00
|
|
|
if (count != 6)
|
|
|
|
count = sscanf(str, "%2x%2x-%2x%2x-%2x%2x%1s",
|
|
|
|
&a, &b, &c, &d, &e, &f, junk);
|
2002-06-15 21:39:33 +02:00
|
|
|
if (count != 6)
|
2002-10-13 17:39:17 +02:00
|
|
|
count = sscanf(str, "%2x%2x%2x%2x%2x%2x%1s",
|
|
|
|
&a, &b, &c, &d, &e, &f, junk);
|
2001-08-21 23:23:21 +02:00
|
|
|
if (count != 6)
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
2017-01-18 20:08:20 +01:00
|
|
|
errmsg("invalid input syntax for type %s: \"%s\"", "macaddr",
|
|
|
|
str)));
|
2001-08-21 23:23:21 +02:00
|
|
|
|
|
|
|
if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
|
|
|
|
(c < 0) || (c > 255) || (d < 0) || (d > 255) ||
|
|
|
|
(e < 0) || (e > 255) || (f < 0) || (f > 255))
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
errmsg("invalid octet value in \"macaddr\" value: \"%s\"", str)));
|
1998-10-03 07:41:01 +02:00
|
|
|
|
|
|
|
result = (macaddr *) palloc(sizeof(macaddr));
|
|
|
|
|
|
|
|
result->a = a;
|
|
|
|
result->b = b;
|
|
|
|
result->c = c;
|
|
|
|
result->d = d;
|
|
|
|
result->e = e;
|
|
|
|
result->f = f;
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
PG_RETURN_MACADDR_P(result);
|
1998-10-03 07:41:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MAC address output function. Fixed format.
|
|
|
|
*/
|
2000-08-23 08:04:49 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
macaddr_out(PG_FUNCTION_ARGS)
|
1998-10-03 07:41:01 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
macaddr *addr = PG_GETARG_MACADDR_P(0);
|
1998-10-03 07:41:01 +02:00
|
|
|
char *result;
|
|
|
|
|
|
|
|
result = (char *) palloc(32);
|
|
|
|
|
2002-08-28 22:46:24 +02:00
|
|
|
snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x",
|
2002-09-04 22:31:48 +02:00
|
|
|
addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_CSTRING(result);
|
1998-10-03 07:41:01 +02:00
|
|
|
}
|
|
|
|
|
2003-05-13 20:03:08 +02:00
|
|
|
/*
|
|
|
|
* macaddr_recv - converts external binary format to macaddr
|
|
|
|
*
|
|
|
|
* The external representation is just the six bytes, MSB first.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
macaddr_recv(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
2003-08-04 02:43:34 +02:00
|
|
|
macaddr *addr;
|
2003-05-13 20:03:08 +02:00
|
|
|
|
|
|
|
addr = (macaddr *) palloc(sizeof(macaddr));
|
|
|
|
|
|
|
|
addr->a = pq_getmsgbyte(buf);
|
|
|
|
addr->b = pq_getmsgbyte(buf);
|
|
|
|
addr->c = pq_getmsgbyte(buf);
|
|
|
|
addr->d = pq_getmsgbyte(buf);
|
|
|
|
addr->e = pq_getmsgbyte(buf);
|
|
|
|
addr->f = pq_getmsgbyte(buf);
|
|
|
|
|
|
|
|
PG_RETURN_MACADDR_P(addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* macaddr_send - converts macaddr to binary format
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
macaddr_send(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
macaddr *addr = PG_GETARG_MACADDR_P(0);
|
2003-05-13 20:03:08 +02:00
|
|
|
StringInfoData buf;
|
|
|
|
|
|
|
|
pq_begintypsend(&buf);
|
|
|
|
pq_sendbyte(&buf, addr->a);
|
|
|
|
pq_sendbyte(&buf, addr->b);
|
|
|
|
pq_sendbyte(&buf, addr->c);
|
|
|
|
pq_sendbyte(&buf, addr->d);
|
|
|
|
pq_sendbyte(&buf, addr->e);
|
|
|
|
pq_sendbyte(&buf, addr->f);
|
|
|
|
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-10-03 07:41:01 +02:00
|
|
|
/*
|
2000-08-04 01:07:51 +02:00
|
|
|
* Comparison function for sorting:
|
1998-10-03 07:41:01 +02:00
|
|
|
*/
|
|
|
|
|
2017-03-29 22:28:56 +02:00
|
|
|
static int
|
2000-08-04 01:07:51 +02:00
|
|
|
macaddr_cmp_internal(macaddr *a1, macaddr *a2)
|
1998-10-03 07:41:01 +02:00
|
|
|
{
|
2000-08-04 01:07:51 +02:00
|
|
|
if (hibits(a1) < hibits(a2))
|
|
|
|
return -1;
|
|
|
|
else if (hibits(a1) > hibits(a2))
|
|
|
|
return 1;
|
|
|
|
else if (lobits(a1) < lobits(a2))
|
|
|
|
return -1;
|
|
|
|
else if (lobits(a1) > lobits(a2))
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
1998-10-26 02:05:07 +01:00
|
|
|
}
|
1998-10-03 07:41:01 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
macaddr_cmp(PG_FUNCTION_ARGS)
|
1998-10-03 07:41:01 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
macaddr *a1 = PG_GETARG_MACADDR_P(0);
|
|
|
|
macaddr *a2 = PG_GETARG_MACADDR_P(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_INT32(macaddr_cmp_internal(a1, a2));
|
1998-10-26 02:05:07 +01:00
|
|
|
}
|
1998-10-03 07:41:01 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
/*
|
|
|
|
* Boolean comparisons.
|
|
|
|
*/
|
2000-08-23 08:04:49 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
macaddr_lt(PG_FUNCTION_ARGS)
|
1998-10-03 07:41:01 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
macaddr *a1 = PG_GETARG_MACADDR_P(0);
|
|
|
|
macaddr *a2 = PG_GETARG_MACADDR_P(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) < 0);
|
1998-10-26 02:05:07 +01:00
|
|
|
}
|
1998-10-03 07:41:01 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
macaddr_le(PG_FUNCTION_ARGS)
|
1998-10-03 07:41:01 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
macaddr *a1 = PG_GETARG_MACADDR_P(0);
|
|
|
|
macaddr *a2 = PG_GETARG_MACADDR_P(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) <= 0);
|
1998-10-26 02:05:07 +01:00
|
|
|
}
|
1998-10-03 07:41:01 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
macaddr_eq(PG_FUNCTION_ARGS)
|
1998-10-03 07:41:01 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
macaddr *a1 = PG_GETARG_MACADDR_P(0);
|
|
|
|
macaddr *a2 = PG_GETARG_MACADDR_P(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) == 0);
|
1998-10-26 02:05:07 +01:00
|
|
|
}
|
1998-10-03 07:41:01 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
macaddr_ge(PG_FUNCTION_ARGS)
|
1998-10-03 07:41:01 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
macaddr *a1 = PG_GETARG_MACADDR_P(0);
|
|
|
|
macaddr *a2 = PG_GETARG_MACADDR_P(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) >= 0);
|
1998-10-26 02:05:07 +01:00
|
|
|
}
|
1998-10-03 07:41:01 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
macaddr_gt(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
macaddr *a1 = PG_GETARG_MACADDR_P(0);
|
|
|
|
macaddr *a2 = PG_GETARG_MACADDR_P(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) > 0);
|
|
|
|
}
|
1998-10-03 07:41:01 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
macaddr_ne(PG_FUNCTION_ARGS)
|
1998-10-03 07:41:01 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
macaddr *a1 = PG_GETARG_MACADDR_P(0);
|
|
|
|
macaddr *a2 = PG_GETARG_MACADDR_P(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) != 0);
|
1998-10-03 07:41:01 +02:00
|
|
|
}
|
|
|
|
|
2000-12-09 00:57:03 +01:00
|
|
|
/*
|
|
|
|
* Support function for hash indexes on macaddr.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
hashmacaddr(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
macaddr *key = PG_GETARG_MACADDR_P(0);
|
2000-12-09 00:57:03 +01:00
|
|
|
|
2002-03-09 18:35:37 +01:00
|
|
|
return hash_any((unsigned char *) key, sizeof(macaddr));
|
2000-12-09 00:57:03 +01:00
|
|
|
}
|
|
|
|
|
2017-09-01 04:21:21 +02:00
|
|
|
Datum
|
|
|
|
hashmacaddrextended(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
macaddr *key = PG_GETARG_MACADDR_P(0);
|
|
|
|
|
|
|
|
return hash_any_extended((unsigned char *) key, sizeof(macaddr),
|
|
|
|
PG_GETARG_INT64(1));
|
|
|
|
}
|
|
|
|
|
2012-01-19 21:23:04 +01:00
|
|
|
/*
|
|
|
|
* Arithmetic functions: bitwise NOT, AND, OR.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
macaddr_not(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
macaddr *addr = PG_GETARG_MACADDR_P(0);
|
|
|
|
macaddr *result;
|
2012-01-19 21:23:04 +01:00
|
|
|
|
|
|
|
result = (macaddr *) palloc(sizeof(macaddr));
|
|
|
|
result->a = ~addr->a;
|
|
|
|
result->b = ~addr->b;
|
|
|
|
result->c = ~addr->c;
|
|
|
|
result->d = ~addr->d;
|
|
|
|
result->e = ~addr->e;
|
|
|
|
result->f = ~addr->f;
|
|
|
|
PG_RETURN_MACADDR_P(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
|
|
|
macaddr_and(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
macaddr *addr1 = PG_GETARG_MACADDR_P(0);
|
|
|
|
macaddr *addr2 = PG_GETARG_MACADDR_P(1);
|
|
|
|
macaddr *result;
|
2012-01-19 21:23:04 +01:00
|
|
|
|
|
|
|
result = (macaddr *) palloc(sizeof(macaddr));
|
|
|
|
result->a = addr1->a & addr2->a;
|
|
|
|
result->b = addr1->b & addr2->b;
|
|
|
|
result->c = addr1->c & addr2->c;
|
|
|
|
result->d = addr1->d & addr2->d;
|
|
|
|
result->e = addr1->e & addr2->e;
|
|
|
|
result->f = addr1->f & addr2->f;
|
|
|
|
PG_RETURN_MACADDR_P(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
|
|
|
macaddr_or(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
macaddr *addr1 = PG_GETARG_MACADDR_P(0);
|
|
|
|
macaddr *addr2 = PG_GETARG_MACADDR_P(1);
|
|
|
|
macaddr *result;
|
2012-01-19 21:23:04 +01:00
|
|
|
|
|
|
|
result = (macaddr *) palloc(sizeof(macaddr));
|
|
|
|
result->a = addr1->a | addr2->a;
|
|
|
|
result->b = addr1->b | addr2->b;
|
|
|
|
result->c = addr1->c | addr2->c;
|
|
|
|
result->d = addr1->d | addr2->d;
|
|
|
|
result->e = addr1->e | addr2->e;
|
|
|
|
result->f = addr1->f | addr2->f;
|
|
|
|
PG_RETURN_MACADDR_P(result);
|
|
|
|
}
|
|
|
|
|
1998-10-03 07:41:01 +02:00
|
|
|
/*
|
2000-08-23 08:04:49 +02:00
|
|
|
* Truncation function to allow comparing mac manufacturers.
|
|
|
|
* From suggestion by Alex Pilosov <alex@pilosoft.com>
|
1998-10-03 07:41:01 +02:00
|
|
|
*/
|
2000-07-06 07:48:31 +02:00
|
|
|
Datum
|
2000-08-23 08:04:49 +02:00
|
|
|
macaddr_trunc(PG_FUNCTION_ARGS)
|
1998-10-03 07:41:01 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
macaddr *addr = PG_GETARG_MACADDR_P(0);
|
2001-08-21 23:23:21 +02:00
|
|
|
macaddr *result;
|
1998-10-03 07:41:01 +02:00
|
|
|
|
2000-08-23 08:04:49 +02:00
|
|
|
result = (macaddr *) palloc(sizeof(macaddr));
|
|
|
|
|
|
|
|
result->a = addr->a;
|
|
|
|
result->b = addr->b;
|
|
|
|
result->c = addr->c;
|
|
|
|
result->d = 0;
|
|
|
|
result->e = 0;
|
|
|
|
result->f = 0;
|
|
|
|
|
|
|
|
PG_RETURN_MACADDR_P(result);
|
1998-10-03 07:41:01 +02:00
|
|
|
}
|
2017-03-29 22:28:56 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* SortSupport strategy function. Populates a SortSupport struct with the
|
|
|
|
* information necessary to use comparison by abbreviated keys.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
macaddr_sortsupport(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
|
|
|
|
|
|
|
|
ssup->comparator = macaddr_fast_cmp;
|
|
|
|
ssup->ssup_extra = NULL;
|
|
|
|
|
|
|
|
if (ssup->abbreviate)
|
|
|
|
{
|
|
|
|
macaddr_sortsupport_state *uss;
|
|
|
|
MemoryContext oldcontext;
|
|
|
|
|
|
|
|
oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
|
|
|
|
|
|
|
|
uss = palloc(sizeof(macaddr_sortsupport_state));
|
|
|
|
uss->input_count = 0;
|
|
|
|
uss->estimating = true;
|
|
|
|
initHyperLogLog(&uss->abbr_card, 10);
|
|
|
|
|
|
|
|
ssup->ssup_extra = uss;
|
|
|
|
|
|
|
|
ssup->comparator = macaddr_cmp_abbrev;
|
|
|
|
ssup->abbrev_converter = macaddr_abbrev_convert;
|
|
|
|
ssup->abbrev_abort = macaddr_abbrev_abort;
|
|
|
|
ssup->abbrev_full_comparator = macaddr_fast_cmp;
|
|
|
|
|
|
|
|
MemoryContextSwitchTo(oldcontext);
|
|
|
|
}
|
|
|
|
|
|
|
|
PG_RETURN_VOID();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SortSupport "traditional" comparison function. Pulls two MAC addresses from
|
|
|
|
* the heap and runs a standard comparison on them.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup)
|
|
|
|
{
|
|
|
|
macaddr *arg1 = DatumGetMacaddrP(x);
|
|
|
|
macaddr *arg2 = DatumGetMacaddrP(y);
|
|
|
|
|
|
|
|
return macaddr_cmp_internal(arg1, arg2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SortSupport abbreviated key comparison function. Compares two MAC addresses
|
2017-04-01 02:17:47 +02:00
|
|
|
* quickly by treating them like integers, and without having to go to the
|
|
|
|
* heap.
|
2017-03-29 22:28:56 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
macaddr_cmp_abbrev(Datum x, Datum y, SortSupport ssup)
|
|
|
|
{
|
|
|
|
if (x > y)
|
|
|
|
return 1;
|
|
|
|
else if (x == y)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Callback for estimating effectiveness of abbreviated key optimization.
|
|
|
|
*
|
|
|
|
* We pay no attention to the cardinality of the non-abbreviated data, because
|
|
|
|
* there is no equality fast-path within authoritative macaddr comparator.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
macaddr_abbrev_abort(int memtupcount, SortSupport ssup)
|
|
|
|
{
|
|
|
|
macaddr_sortsupport_state *uss = ssup->ssup_extra;
|
|
|
|
double abbr_card;
|
|
|
|
|
|
|
|
if (memtupcount < 10000 || uss->input_count < 10000 || !uss->estimating)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
abbr_card = estimateHyperLogLog(&uss->abbr_card);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have >100k distinct values, then even if we were sorting many
|
|
|
|
* billion rows we'd likely still break even, and the penalty of undoing
|
|
|
|
* that many rows of abbrevs would probably not be worth it. At this point
|
|
|
|
* we stop counting because we know that we're now fully committed.
|
|
|
|
*/
|
|
|
|
if (abbr_card > 100000.0)
|
|
|
|
{
|
|
|
|
#ifdef TRACE_SORT
|
|
|
|
if (trace_sort)
|
|
|
|
elog(LOG,
|
|
|
|
"macaddr_abbrev: estimation ends at cardinality %f"
|
|
|
|
" after " INT64_FORMAT " values (%d rows)",
|
|
|
|
abbr_card, uss->input_count, memtupcount);
|
|
|
|
#endif
|
|
|
|
uss->estimating = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Target minimum cardinality is 1 per ~2k of non-null inputs. 0.5 row
|
|
|
|
* fudge factor allows us to abort earlier on genuinely pathological data
|
|
|
|
* where we've had exactly one abbreviated value in the first 2k
|
|
|
|
* (non-null) rows.
|
|
|
|
*/
|
|
|
|
if (abbr_card < uss->input_count / 2000.0 + 0.5)
|
|
|
|
{
|
|
|
|
#ifdef TRACE_SORT
|
|
|
|
if (trace_sort)
|
|
|
|
elog(LOG,
|
|
|
|
"macaddr_abbrev: aborting abbreviation at cardinality %f"
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
" below threshold %f after " INT64_FORMAT " values (%d rows)",
|
2017-03-29 22:28:56 +02:00
|
|
|
abbr_card, uss->input_count / 2000.0 + 0.5, uss->input_count,
|
|
|
|
memtupcount);
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TRACE_SORT
|
|
|
|
if (trace_sort)
|
|
|
|
elog(LOG,
|
|
|
|
"macaddr_abbrev: cardinality %f after " INT64_FORMAT
|
|
|
|
" values (%d rows)", abbr_card, uss->input_count, memtupcount);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-04-01 02:17:47 +02:00
|
|
|
* SortSupport conversion routine. Converts original macaddr representation
|
2017-03-29 22:28:56 +02:00
|
|
|
* to abbreviated key representation.
|
|
|
|
*
|
|
|
|
* Packs the bytes of a 6-byte MAC address into a Datum and treats it as an
|
|
|
|
* unsigned integer for purposes of comparison. On a 64-bit machine, there
|
|
|
|
* will be two zeroed bytes of padding. The integer is converted to native
|
|
|
|
* endianness to facilitate easy comparison.
|
|
|
|
*/
|
|
|
|
static Datum
|
|
|
|
macaddr_abbrev_convert(Datum original, SortSupport ssup)
|
|
|
|
{
|
|
|
|
macaddr_sortsupport_state *uss = ssup->ssup_extra;
|
|
|
|
macaddr *authoritative = DatumGetMacaddrP(original);
|
|
|
|
Datum res;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* On a 64-bit machine, zero out the 8-byte datum and copy the 6 bytes of
|
|
|
|
* the MAC address in. There will be two bytes of zero padding on the end
|
|
|
|
* of the least significant bits.
|
|
|
|
*/
|
|
|
|
#if SIZEOF_DATUM == 8
|
|
|
|
memset(&res, 0, SIZEOF_DATUM);
|
|
|
|
memcpy(&res, authoritative, sizeof(macaddr));
|
|
|
|
#else /* SIZEOF_DATUM != 8 */
|
|
|
|
memcpy(&res, authoritative, SIZEOF_DATUM);
|
|
|
|
#endif
|
|
|
|
uss->input_count += 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cardinality estimation. The estimate uses uint32, so on a 64-bit
|
|
|
|
* architecture, XOR the two 32-bit halves together to produce slightly
|
|
|
|
* more entropy. The two zeroed bytes won't have any practical impact on
|
|
|
|
* this operation.
|
|
|
|
*/
|
|
|
|
if (uss->estimating)
|
|
|
|
{
|
|
|
|
uint32 tmp;
|
|
|
|
|
|
|
|
#if SIZEOF_DATUM == 8
|
|
|
|
tmp = (uint32) res ^ (uint32) ((uint64) res >> 32);
|
|
|
|
#else /* SIZEOF_DATUM != 8 */
|
|
|
|
tmp = (uint32) res;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
addHyperLogLog(&uss->abbr_card, DatumGetUInt32(hash_uint32(tmp)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Byteswap on little-endian machines.
|
|
|
|
*
|
|
|
|
* This is needed so that macaddr_cmp_abbrev() (an unsigned integer 3-way
|
|
|
|
* comparator) works correctly on all platforms. Without this, the
|
|
|
|
* comparator would have to call memcmp() with a pair of pointers to the
|
|
|
|
* first byte of each abbreviated key, which is slower.
|
|
|
|
*/
|
|
|
|
res = DatumBigEndianToNative(res);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|