mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Several functions in the codebase accept "Datum *" parameters but do not modify the pointed-to data. These have been updated to take "const Datum *" instead, improving type safety and making the interfaces clearer about their intent. This change helps the compiler catch accidental modifications and better documents immutability of arguments. Most of "Datum *" parameters have a pairing "bool *isnull" parameter, they are constified as well. No functional behavior is changed by this patch. Author: Chao Li <lic@highgo.com> Discussion: https://www.postgresql.org/message-id/flat/CAEoWx2msfT0knvzUa72ZBwu9LR_RLY4on85w2a9YpE-o2By5HQ@mail.gmail.com
		
			
				
	
	
		
			973 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			973 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*-------------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * network_selfuncs.c
 | 
						|
 *	  Functions for selectivity estimation of inet/cidr operators
 | 
						|
 *
 | 
						|
 * This module provides estimators for the subnet inclusion and overlap
 | 
						|
 * operators.  Estimates are based on null fraction, most common values,
 | 
						|
 * and histogram of inet/cidr columns.
 | 
						|
 *
 | 
						|
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 | 
						|
 * Portions Copyright (c) 1994, Regents of the University of California
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * IDENTIFICATION
 | 
						|
 *	  src/backend/utils/adt/network_selfuncs.c
 | 
						|
 *
 | 
						|
 *-------------------------------------------------------------------------
 | 
						|
 */
 | 
						|
#include "postgres.h"
 | 
						|
 | 
						|
#include <math.h>
 | 
						|
 | 
						|
#include "access/htup_details.h"
 | 
						|
#include "catalog/pg_operator.h"
 | 
						|
#include "catalog/pg_statistic.h"
 | 
						|
#include "utils/fmgrprotos.h"
 | 
						|
#include "utils/inet.h"
 | 
						|
#include "utils/lsyscache.h"
 | 
						|
#include "utils/selfuncs.h"
 | 
						|
 | 
						|
 | 
						|
/* Default selectivity for the inet overlap operator */
 | 
						|
#define DEFAULT_OVERLAP_SEL 0.01
 | 
						|
 | 
						|
/* Default selectivity for the various inclusion operators */
 | 
						|
#define DEFAULT_INCLUSION_SEL 0.005
 | 
						|
 | 
						|
/* Default selectivity for specified operator */
 | 
						|
#define DEFAULT_SEL(operator) \
 | 
						|
	((operator) == OID_INET_OVERLAP_OP ? \
 | 
						|
	 DEFAULT_OVERLAP_SEL : DEFAULT_INCLUSION_SEL)
 | 
						|
 | 
						|
/* Maximum number of items to consider in join selectivity calculations */
 | 
						|
#define MAX_CONSIDERED_ELEMS 1024
 | 
						|
 | 
						|
static Selectivity networkjoinsel_inner(Oid operator,
 | 
						|
										VariableStatData *vardata1, VariableStatData *vardata2);
 | 
						|
static Selectivity networkjoinsel_semi(Oid operator,
 | 
						|
									   VariableStatData *vardata1, VariableStatData *vardata2);
 | 
						|
static Selectivity mcv_population(float4 *mcv_numbers, int mcv_nvalues);
 | 
						|
static Selectivity inet_hist_value_sel(const Datum *values, int nvalues,
 | 
						|
									   Datum constvalue, int opr_codenum);
 | 
						|
static Selectivity inet_mcv_join_sel(Datum *mcv1_values,
 | 
						|
									 float4 *mcv1_numbers, int mcv1_nvalues, Datum *mcv2_values,
 | 
						|
									 float4 *mcv2_numbers, int mcv2_nvalues, Oid operator);
 | 
						|
static Selectivity inet_mcv_hist_sel(const Datum *mcv_values, float4 *mcv_numbers,
 | 
						|
									 int mcv_nvalues, const Datum *hist_values, int hist_nvalues,
 | 
						|
									 int opr_codenum);
 | 
						|
static Selectivity inet_hist_inclusion_join_sel(const Datum *hist1_values,
 | 
						|
												int hist1_nvalues,
 | 
						|
												const Datum *hist2_values, int hist2_nvalues,
 | 
						|
												int opr_codenum);
 | 
						|
static Selectivity inet_semi_join_sel(Datum lhs_value,
 | 
						|
									  bool mcv_exists, Datum *mcv_values, int mcv_nvalues,
 | 
						|
									  bool hist_exists, Datum *hist_values, int hist_nvalues,
 | 
						|
									  double hist_weight,
 | 
						|
									  FmgrInfo *proc, int opr_codenum);
 | 
						|
static int	inet_opr_codenum(Oid operator);
 | 
						|
static int	inet_inclusion_cmp(inet *left, inet *right, int opr_codenum);
 | 
						|
static int	inet_masklen_inclusion_cmp(inet *left, inet *right,
 | 
						|
									   int opr_codenum);
 | 
						|
static int	inet_hist_match_divider(inet *boundary, inet *query,
 | 
						|
									int opr_codenum);
 | 
						|
 | 
						|
/*
 | 
						|
 * Selectivity estimation for the subnet inclusion/overlap operators
 | 
						|
 */
 | 
						|
Datum
 | 
						|
networksel(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
 | 
						|
	Oid			operator = PG_GETARG_OID(1);
 | 
						|
	List	   *args = (List *) PG_GETARG_POINTER(2);
 | 
						|
	int			varRelid = PG_GETARG_INT32(3);
 | 
						|
	VariableStatData vardata;
 | 
						|
	Node	   *other;
 | 
						|
	bool		varonleft;
 | 
						|
	Selectivity selec,
 | 
						|
				mcv_selec,
 | 
						|
				non_mcv_selec;
 | 
						|
	Datum		constvalue;
 | 
						|
	Form_pg_statistic stats;
 | 
						|
	AttStatsSlot hslot;
 | 
						|
	double		sumcommon,
 | 
						|
				nullfrac;
 | 
						|
	FmgrInfo	proc;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * If expression is not (variable op something) or (something op
 | 
						|
	 * variable), then punt and return a default estimate.
 | 
						|
	 */
 | 
						|
	if (!get_restriction_variable(root, args, varRelid,
 | 
						|
								  &vardata, &other, &varonleft))
 | 
						|
		PG_RETURN_FLOAT8(DEFAULT_SEL(operator));
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Can't do anything useful if the something is not a constant, either.
 | 
						|
	 */
 | 
						|
	if (!IsA(other, Const))
 | 
						|
	{
 | 
						|
		ReleaseVariableStats(vardata);
 | 
						|
		PG_RETURN_FLOAT8(DEFAULT_SEL(operator));
 | 
						|
	}
 | 
						|
 | 
						|
	/* All of the operators handled here are strict. */
 | 
						|
	if (((Const *) other)->constisnull)
 | 
						|
	{
 | 
						|
		ReleaseVariableStats(vardata);
 | 
						|
		PG_RETURN_FLOAT8(0.0);
 | 
						|
	}
 | 
						|
	constvalue = ((Const *) other)->constvalue;
 | 
						|
 | 
						|
	/* Otherwise, we need stats in order to produce a non-default estimate. */
 | 
						|
	if (!HeapTupleIsValid(vardata.statsTuple))
 | 
						|
	{
 | 
						|
		ReleaseVariableStats(vardata);
 | 
						|
		PG_RETURN_FLOAT8(DEFAULT_SEL(operator));
 | 
						|
	}
 | 
						|
 | 
						|
	stats = (Form_pg_statistic) GETSTRUCT(vardata.statsTuple);
 | 
						|
	nullfrac = stats->stanullfrac;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * If we have most-common-values info, add up the fractions of the MCV
 | 
						|
	 * entries that satisfy MCV OP CONST.  These fractions contribute directly
 | 
						|
	 * to the result selectivity.  Also add up the total fraction represented
 | 
						|
	 * by MCV entries.
 | 
						|
	 */
 | 
						|
	fmgr_info(get_opcode(operator), &proc);
 | 
						|
	mcv_selec = mcv_selectivity(&vardata, &proc, InvalidOid,
 | 
						|
								constvalue, varonleft,
 | 
						|
								&sumcommon);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * If we have a histogram, use it to estimate the proportion of the
 | 
						|
	 * non-MCV population that satisfies the clause.  If we don't, apply the
 | 
						|
	 * default selectivity to that population.
 | 
						|
	 */
 | 
						|
	if (get_attstatsslot(&hslot, vardata.statsTuple,
 | 
						|
						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
 | 
						|
						 ATTSTATSSLOT_VALUES))
 | 
						|
	{
 | 
						|
		int			opr_codenum = inet_opr_codenum(operator);
 | 
						|
 | 
						|
		/* Commute if needed, so we can consider histogram to be on the left */
 | 
						|
		if (!varonleft)
 | 
						|
			opr_codenum = -opr_codenum;
 | 
						|
		non_mcv_selec = inet_hist_value_sel(hslot.values, hslot.nvalues,
 | 
						|
											constvalue, opr_codenum);
 | 
						|
 | 
						|
		free_attstatsslot(&hslot);
 | 
						|
	}
 | 
						|
	else
 | 
						|
		non_mcv_selec = DEFAULT_SEL(operator);
 | 
						|
 | 
						|
	/* Combine selectivities for MCV and non-MCV populations */
 | 
						|
	selec = mcv_selec + (1.0 - nullfrac - sumcommon) * non_mcv_selec;
 | 
						|
 | 
						|
	/* Result should be in range, but make sure... */
 | 
						|
	CLAMP_PROBABILITY(selec);
 | 
						|
 | 
						|
	ReleaseVariableStats(vardata);
 | 
						|
 | 
						|
	PG_RETURN_FLOAT8(selec);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Join selectivity estimation for the subnet inclusion/overlap operators
 | 
						|
 *
 | 
						|
 * This function has the same structure as eqjoinsel() in selfuncs.c.
 | 
						|
 *
 | 
						|
 * Throughout networkjoinsel and its subroutines, we have a performance issue
 | 
						|
 * in that the amount of work to be done is O(N^2) in the length of the MCV
 | 
						|
 * and histogram arrays.  To keep the runtime from getting out of hand when
 | 
						|
 * large statistics targets have been set, we arbitrarily limit the number of
 | 
						|
 * values considered to 1024 (MAX_CONSIDERED_ELEMS).  For the MCV arrays, this
 | 
						|
 * is easy: just consider at most the first N elements.  (Since the MCVs are
 | 
						|
 * sorted by decreasing frequency, this correctly gets us the first N MCVs.)
 | 
						|
 * For the histogram arrays, we decimate; that is consider only every k'th
 | 
						|
 * element, where k is chosen so that no more than MAX_CONSIDERED_ELEMS
 | 
						|
 * elements are considered.  This should still give us a good random sample of
 | 
						|
 * the non-MCV population.  Decimation is done on-the-fly in the loops that
 | 
						|
 * iterate over the histogram arrays.
 | 
						|
 */
 | 
						|
Datum
 | 
						|
networkjoinsel(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
 | 
						|
	Oid			operator = PG_GETARG_OID(1);
 | 
						|
	List	   *args = (List *) PG_GETARG_POINTER(2);
 | 
						|
#ifdef NOT_USED
 | 
						|
	JoinType	jointype = (JoinType) PG_GETARG_INT16(3);
 | 
						|
#endif
 | 
						|
	SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) PG_GETARG_POINTER(4);
 | 
						|
	double		selec;
 | 
						|
	VariableStatData vardata1;
 | 
						|
	VariableStatData vardata2;
 | 
						|
	bool		join_is_reversed;
 | 
						|
 | 
						|
	get_join_variables(root, args, sjinfo,
 | 
						|
					   &vardata1, &vardata2, &join_is_reversed);
 | 
						|
 | 
						|
	switch (sjinfo->jointype)
 | 
						|
	{
 | 
						|
		case JOIN_INNER:
 | 
						|
		case JOIN_LEFT:
 | 
						|
		case JOIN_FULL:
 | 
						|
 | 
						|
			/*
 | 
						|
			 * Selectivity for left/full join is not exactly the same as inner
 | 
						|
			 * join, but we neglect the difference, as eqjoinsel does.
 | 
						|
			 */
 | 
						|
			selec = networkjoinsel_inner(operator, &vardata1, &vardata2);
 | 
						|
			break;
 | 
						|
		case JOIN_SEMI:
 | 
						|
		case JOIN_ANTI:
 | 
						|
			/* Here, it's important that we pass the outer var on the left. */
 | 
						|
			if (!join_is_reversed)
 | 
						|
				selec = networkjoinsel_semi(operator, &vardata1, &vardata2);
 | 
						|
			else
 | 
						|
				selec = networkjoinsel_semi(get_commutator(operator),
 | 
						|
											&vardata2, &vardata1);
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			/* other values not expected here */
 | 
						|
			elog(ERROR, "unrecognized join type: %d",
 | 
						|
				 (int) sjinfo->jointype);
 | 
						|
			selec = 0;			/* keep compiler quiet */
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	ReleaseVariableStats(vardata1);
 | 
						|
	ReleaseVariableStats(vardata2);
 | 
						|
 | 
						|
	CLAMP_PROBABILITY(selec);
 | 
						|
 | 
						|
	PG_RETURN_FLOAT8((float8) selec);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Inner join selectivity estimation for subnet inclusion/overlap operators
 | 
						|
 *
 | 
						|
 * Calculates MCV vs MCV, MCV vs histogram and histogram vs histogram
 | 
						|
 * selectivity for join using the subnet inclusion operators.  Unlike the
 | 
						|
 * join selectivity function for the equality operator, eqjoinsel_inner(),
 | 
						|
 * one to one matching of the values is not enough.  Network inclusion
 | 
						|
 * operators are likely to match many to many, so we must check all pairs.
 | 
						|
 * (Note: it might be possible to exploit understanding of the histogram's
 | 
						|
 * btree ordering to reduce the work needed, but we don't currently try.)
 | 
						|
 * Also, MCV vs histogram selectivity is not neglected as in eqjoinsel_inner().
 | 
						|
 */
 | 
						|
static Selectivity
 | 
						|
networkjoinsel_inner(Oid operator,
 | 
						|
					 VariableStatData *vardata1, VariableStatData *vardata2)
 | 
						|
{
 | 
						|
	Form_pg_statistic stats;
 | 
						|
	double		nullfrac1 = 0.0,
 | 
						|
				nullfrac2 = 0.0;
 | 
						|
	Selectivity selec = 0.0,
 | 
						|
				sumcommon1 = 0.0,
 | 
						|
				sumcommon2 = 0.0;
 | 
						|
	bool		mcv1_exists = false,
 | 
						|
				mcv2_exists = false,
 | 
						|
				hist1_exists = false,
 | 
						|
				hist2_exists = false;
 | 
						|
	int			opr_codenum;
 | 
						|
	int			mcv1_length = 0,
 | 
						|
				mcv2_length = 0;
 | 
						|
	AttStatsSlot mcv1_slot;
 | 
						|
	AttStatsSlot mcv2_slot;
 | 
						|
	AttStatsSlot hist1_slot;
 | 
						|
	AttStatsSlot hist2_slot;
 | 
						|
 | 
						|
	if (HeapTupleIsValid(vardata1->statsTuple))
 | 
						|
	{
 | 
						|
		stats = (Form_pg_statistic) GETSTRUCT(vardata1->statsTuple);
 | 
						|
		nullfrac1 = stats->stanullfrac;
 | 
						|
 | 
						|
		mcv1_exists = get_attstatsslot(&mcv1_slot, vardata1->statsTuple,
 | 
						|
									   STATISTIC_KIND_MCV, InvalidOid,
 | 
						|
									   ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
 | 
						|
		hist1_exists = get_attstatsslot(&hist1_slot, vardata1->statsTuple,
 | 
						|
										STATISTIC_KIND_HISTOGRAM, InvalidOid,
 | 
						|
										ATTSTATSSLOT_VALUES);
 | 
						|
		/* Arbitrarily limit number of MCVs considered */
 | 
						|
		mcv1_length = Min(mcv1_slot.nvalues, MAX_CONSIDERED_ELEMS);
 | 
						|
		if (mcv1_exists)
 | 
						|
			sumcommon1 = mcv_population(mcv1_slot.numbers, mcv1_length);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		memset(&mcv1_slot, 0, sizeof(mcv1_slot));
 | 
						|
		memset(&hist1_slot, 0, sizeof(hist1_slot));
 | 
						|
	}
 | 
						|
 | 
						|
	if (HeapTupleIsValid(vardata2->statsTuple))
 | 
						|
	{
 | 
						|
		stats = (Form_pg_statistic) GETSTRUCT(vardata2->statsTuple);
 | 
						|
		nullfrac2 = stats->stanullfrac;
 | 
						|
 | 
						|
		mcv2_exists = get_attstatsslot(&mcv2_slot, vardata2->statsTuple,
 | 
						|
									   STATISTIC_KIND_MCV, InvalidOid,
 | 
						|
									   ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
 | 
						|
		hist2_exists = get_attstatsslot(&hist2_slot, vardata2->statsTuple,
 | 
						|
										STATISTIC_KIND_HISTOGRAM, InvalidOid,
 | 
						|
										ATTSTATSSLOT_VALUES);
 | 
						|
		/* Arbitrarily limit number of MCVs considered */
 | 
						|
		mcv2_length = Min(mcv2_slot.nvalues, MAX_CONSIDERED_ELEMS);
 | 
						|
		if (mcv2_exists)
 | 
						|
			sumcommon2 = mcv_population(mcv2_slot.numbers, mcv2_length);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		memset(&mcv2_slot, 0, sizeof(mcv2_slot));
 | 
						|
		memset(&hist2_slot, 0, sizeof(hist2_slot));
 | 
						|
	}
 | 
						|
 | 
						|
	opr_codenum = inet_opr_codenum(operator);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Calculate selectivity for MCV vs MCV matches.
 | 
						|
	 */
 | 
						|
	if (mcv1_exists && mcv2_exists)
 | 
						|
		selec += inet_mcv_join_sel(mcv1_slot.values, mcv1_slot.numbers,
 | 
						|
								   mcv1_length,
 | 
						|
								   mcv2_slot.values, mcv2_slot.numbers,
 | 
						|
								   mcv2_length,
 | 
						|
								   operator);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Add in selectivities for MCV vs histogram matches, scaling according to
 | 
						|
	 * the fractions of the populations represented by the histograms. Note
 | 
						|
	 * that the second case needs to commute the operator.
 | 
						|
	 */
 | 
						|
	if (mcv1_exists && hist2_exists)
 | 
						|
		selec += (1.0 - nullfrac2 - sumcommon2) *
 | 
						|
			inet_mcv_hist_sel(mcv1_slot.values, mcv1_slot.numbers, mcv1_length,
 | 
						|
							  hist2_slot.values, hist2_slot.nvalues,
 | 
						|
							  opr_codenum);
 | 
						|
	if (mcv2_exists && hist1_exists)
 | 
						|
		selec += (1.0 - nullfrac1 - sumcommon1) *
 | 
						|
			inet_mcv_hist_sel(mcv2_slot.values, mcv2_slot.numbers, mcv2_length,
 | 
						|
							  hist1_slot.values, hist1_slot.nvalues,
 | 
						|
							  -opr_codenum);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Add in selectivity for histogram vs histogram matches, again scaling
 | 
						|
	 * appropriately.
 | 
						|
	 */
 | 
						|
	if (hist1_exists && hist2_exists)
 | 
						|
		selec += (1.0 - nullfrac1 - sumcommon1) *
 | 
						|
			(1.0 - nullfrac2 - sumcommon2) *
 | 
						|
			inet_hist_inclusion_join_sel(hist1_slot.values, hist1_slot.nvalues,
 | 
						|
										 hist2_slot.values, hist2_slot.nvalues,
 | 
						|
										 opr_codenum);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * If useful statistics are not available then use the default estimate.
 | 
						|
	 * We can apply null fractions if known, though.
 | 
						|
	 */
 | 
						|
	if ((!mcv1_exists && !hist1_exists) || (!mcv2_exists && !hist2_exists))
 | 
						|
		selec = (1.0 - nullfrac1) * (1.0 - nullfrac2) * DEFAULT_SEL(operator);
 | 
						|
 | 
						|
	/* Release stats. */
 | 
						|
	free_attstatsslot(&mcv1_slot);
 | 
						|
	free_attstatsslot(&mcv2_slot);
 | 
						|
	free_attstatsslot(&hist1_slot);
 | 
						|
	free_attstatsslot(&hist2_slot);
 | 
						|
 | 
						|
	return selec;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Semi join selectivity estimation for subnet inclusion/overlap operators
 | 
						|
 *
 | 
						|
 * Calculates MCV vs MCV, MCV vs histogram, histogram vs MCV, and histogram vs
 | 
						|
 * histogram selectivity for semi/anti join cases.
 | 
						|
 */
 | 
						|
static Selectivity
 | 
						|
networkjoinsel_semi(Oid operator,
 | 
						|
					VariableStatData *vardata1, VariableStatData *vardata2)
 | 
						|
{
 | 
						|
	Form_pg_statistic stats;
 | 
						|
	Selectivity selec = 0.0,
 | 
						|
				sumcommon1 = 0.0,
 | 
						|
				sumcommon2 = 0.0;
 | 
						|
	double		nullfrac1 = 0.0,
 | 
						|
				nullfrac2 = 0.0,
 | 
						|
				hist2_weight = 0.0;
 | 
						|
	bool		mcv1_exists = false,
 | 
						|
				mcv2_exists = false,
 | 
						|
				hist1_exists = false,
 | 
						|
				hist2_exists = false;
 | 
						|
	int			opr_codenum;
 | 
						|
	FmgrInfo	proc;
 | 
						|
	int			i,
 | 
						|
				mcv1_length = 0,
 | 
						|
				mcv2_length = 0;
 | 
						|
	AttStatsSlot mcv1_slot;
 | 
						|
	AttStatsSlot mcv2_slot;
 | 
						|
	AttStatsSlot hist1_slot;
 | 
						|
	AttStatsSlot hist2_slot;
 | 
						|
 | 
						|
	if (HeapTupleIsValid(vardata1->statsTuple))
 | 
						|
	{
 | 
						|
		stats = (Form_pg_statistic) GETSTRUCT(vardata1->statsTuple);
 | 
						|
		nullfrac1 = stats->stanullfrac;
 | 
						|
 | 
						|
		mcv1_exists = get_attstatsslot(&mcv1_slot, vardata1->statsTuple,
 | 
						|
									   STATISTIC_KIND_MCV, InvalidOid,
 | 
						|
									   ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
 | 
						|
		hist1_exists = get_attstatsslot(&hist1_slot, vardata1->statsTuple,
 | 
						|
										STATISTIC_KIND_HISTOGRAM, InvalidOid,
 | 
						|
										ATTSTATSSLOT_VALUES);
 | 
						|
		/* Arbitrarily limit number of MCVs considered */
 | 
						|
		mcv1_length = Min(mcv1_slot.nvalues, MAX_CONSIDERED_ELEMS);
 | 
						|
		if (mcv1_exists)
 | 
						|
			sumcommon1 = mcv_population(mcv1_slot.numbers, mcv1_length);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		memset(&mcv1_slot, 0, sizeof(mcv1_slot));
 | 
						|
		memset(&hist1_slot, 0, sizeof(hist1_slot));
 | 
						|
	}
 | 
						|
 | 
						|
	if (HeapTupleIsValid(vardata2->statsTuple))
 | 
						|
	{
 | 
						|
		stats = (Form_pg_statistic) GETSTRUCT(vardata2->statsTuple);
 | 
						|
		nullfrac2 = stats->stanullfrac;
 | 
						|
 | 
						|
		mcv2_exists = get_attstatsslot(&mcv2_slot, vardata2->statsTuple,
 | 
						|
									   STATISTIC_KIND_MCV, InvalidOid,
 | 
						|
									   ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
 | 
						|
		hist2_exists = get_attstatsslot(&hist2_slot, vardata2->statsTuple,
 | 
						|
										STATISTIC_KIND_HISTOGRAM, InvalidOid,
 | 
						|
										ATTSTATSSLOT_VALUES);
 | 
						|
		/* Arbitrarily limit number of MCVs considered */
 | 
						|
		mcv2_length = Min(mcv2_slot.nvalues, MAX_CONSIDERED_ELEMS);
 | 
						|
		if (mcv2_exists)
 | 
						|
			sumcommon2 = mcv_population(mcv2_slot.numbers, mcv2_length);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		memset(&mcv2_slot, 0, sizeof(mcv2_slot));
 | 
						|
		memset(&hist2_slot, 0, sizeof(hist2_slot));
 | 
						|
	}
 | 
						|
 | 
						|
	opr_codenum = inet_opr_codenum(operator);
 | 
						|
	fmgr_info(get_opcode(operator), &proc);
 | 
						|
 | 
						|
	/* Estimate number of input rows represented by RHS histogram. */
 | 
						|
	if (hist2_exists && vardata2->rel)
 | 
						|
		hist2_weight = (1.0 - nullfrac2 - sumcommon2) * vardata2->rel->rows;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Consider each element of the LHS MCV list, matching it to whatever RHS
 | 
						|
	 * stats we have.  Scale according to the known frequency of the MCV.
 | 
						|
	 */
 | 
						|
	if (mcv1_exists && (mcv2_exists || hist2_exists))
 | 
						|
	{
 | 
						|
		for (i = 0; i < mcv1_length; i++)
 | 
						|
		{
 | 
						|
			selec += mcv1_slot.numbers[i] *
 | 
						|
				inet_semi_join_sel(mcv1_slot.values[i],
 | 
						|
								   mcv2_exists, mcv2_slot.values, mcv2_length,
 | 
						|
								   hist2_exists,
 | 
						|
								   hist2_slot.values, hist2_slot.nvalues,
 | 
						|
								   hist2_weight,
 | 
						|
								   &proc, opr_codenum);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Consider each element of the LHS histogram, except for the first and
 | 
						|
	 * last elements, which we exclude on the grounds that they're outliers
 | 
						|
	 * and thus not very representative.  Scale on the assumption that each
 | 
						|
	 * such histogram element represents an equal share of the LHS histogram
 | 
						|
	 * population (which is a bit bogus, because the members of its bucket may
 | 
						|
	 * not all act the same with respect to the join clause, but it's hard to
 | 
						|
	 * do better).
 | 
						|
	 *
 | 
						|
	 * If there are too many histogram elements, decimate to limit runtime.
 | 
						|
	 */
 | 
						|
	if (hist1_exists && hist1_slot.nvalues > 2 && (mcv2_exists || hist2_exists))
 | 
						|
	{
 | 
						|
		double		hist_selec_sum = 0.0;
 | 
						|
		int			k,
 | 
						|
					n;
 | 
						|
 | 
						|
		k = (hist1_slot.nvalues - 3) / MAX_CONSIDERED_ELEMS + 1;
 | 
						|
 | 
						|
		n = 0;
 | 
						|
		for (i = 1; i < hist1_slot.nvalues - 1; i += k)
 | 
						|
		{
 | 
						|
			hist_selec_sum +=
 | 
						|
				inet_semi_join_sel(hist1_slot.values[i],
 | 
						|
								   mcv2_exists, mcv2_slot.values, mcv2_length,
 | 
						|
								   hist2_exists,
 | 
						|
								   hist2_slot.values, hist2_slot.nvalues,
 | 
						|
								   hist2_weight,
 | 
						|
								   &proc, opr_codenum);
 | 
						|
			n++;
 | 
						|
		}
 | 
						|
 | 
						|
		selec += (1.0 - nullfrac1 - sumcommon1) * hist_selec_sum / n;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * If useful statistics are not available then use the default estimate.
 | 
						|
	 * We can apply null fractions if known, though.
 | 
						|
	 */
 | 
						|
	if ((!mcv1_exists && !hist1_exists) || (!mcv2_exists && !hist2_exists))
 | 
						|
		selec = (1.0 - nullfrac1) * (1.0 - nullfrac2) * DEFAULT_SEL(operator);
 | 
						|
 | 
						|
	/* Release stats. */
 | 
						|
	free_attstatsslot(&mcv1_slot);
 | 
						|
	free_attstatsslot(&mcv2_slot);
 | 
						|
	free_attstatsslot(&hist1_slot);
 | 
						|
	free_attstatsslot(&hist2_slot);
 | 
						|
 | 
						|
	return selec;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Compute the fraction of a relation's population that is represented
 | 
						|
 * by the MCV list.
 | 
						|
 */
 | 
						|
static Selectivity
 | 
						|
mcv_population(float4 *mcv_numbers, int mcv_nvalues)
 | 
						|
{
 | 
						|
	Selectivity sumcommon = 0.0;
 | 
						|
	int			i;
 | 
						|
 | 
						|
	for (i = 0; i < mcv_nvalues; i++)
 | 
						|
	{
 | 
						|
		sumcommon += mcv_numbers[i];
 | 
						|
	}
 | 
						|
 | 
						|
	return sumcommon;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Inet histogram vs single value selectivity estimation
 | 
						|
 *
 | 
						|
 * Estimate the fraction of the histogram population that satisfies
 | 
						|
 * "value OPR CONST".  (The result needs to be scaled to reflect the
 | 
						|
 * proportion of the total population represented by the histogram.)
 | 
						|
 *
 | 
						|
 * The histogram is originally for the inet btree comparison operators.
 | 
						|
 * Only the common bits of the network part and the length of the network part
 | 
						|
 * (masklen) are interesting for the subnet inclusion operators.  Fortunately,
 | 
						|
 * btree comparison treats the network part as the major sort key.  Even so,
 | 
						|
 * the length of the network part would not really be significant in the
 | 
						|
 * histogram.  This would lead to big mistakes for data sets with uneven
 | 
						|
 * masklen distribution.  To reduce this problem, comparisons with the left
 | 
						|
 * and the right sides of the buckets are used together.
 | 
						|
 *
 | 
						|
 * Histogram bucket matches are calculated in two forms.  If the constant
 | 
						|
 * matches both bucket endpoints the bucket is considered as fully matched.
 | 
						|
 * The second form is to match the bucket partially; we recognize this when
 | 
						|
 * the constant matches just one endpoint, or the two endpoints fall on
 | 
						|
 * opposite sides of the constant.  (Note that when the constant matches an
 | 
						|
 * interior histogram element, it gets credit for partial matches to the
 | 
						|
 * buckets on both sides, while a match to a histogram endpoint gets credit
 | 
						|
 * for only one partial match.  This is desirable.)
 | 
						|
 *
 | 
						|
 * The divider in the partial bucket match is imagined as the distance
 | 
						|
 * between the decisive bits and the common bits of the addresses.  It will
 | 
						|
 * be used as a power of two as it is the natural scale for the IP network
 | 
						|
 * inclusion.  This partial bucket match divider calculation is an empirical
 | 
						|
 * formula and subject to change with more experiment.
 | 
						|
 *
 | 
						|
 * For a partial match, we try to calculate dividers for both of the
 | 
						|
 * boundaries.  If the address family of a boundary value does not match the
 | 
						|
 * constant or comparison of the length of the network parts is not correct
 | 
						|
 * for the operator, the divider for that boundary will not be taken into
 | 
						|
 * account.  If both of the dividers are valid, the greater one will be used
 | 
						|
 * to minimize the mistake in buckets that have disparate masklens.  This
 | 
						|
 * calculation is unfair when dividers can be calculated for both of the
 | 
						|
 * boundaries but they are far from each other; but it is not a common
 | 
						|
 * situation as the boundaries are expected to share most of their significant
 | 
						|
 * bits of their masklens.  The mistake would be greater, if we would use the
 | 
						|
 * minimum instead of the maximum, and we don't know a sensible way to combine
 | 
						|
 * them.
 | 
						|
 *
 | 
						|
 * For partial match in buckets that have different address families on the
 | 
						|
 * left and right sides, only the boundary with the same address family is
 | 
						|
 * taken into consideration.  This can cause more mistakes for these buckets
 | 
						|
 * if the masklens of their boundaries are also disparate.  But this can only
 | 
						|
 * happen in one bucket, since only two address families exist.  It seems a
 | 
						|
 * better option than not considering these buckets at all.
 | 
						|
 */
 | 
						|
static Selectivity
 | 
						|
inet_hist_value_sel(const Datum *values, int nvalues, Datum constvalue,
 | 
						|
					int opr_codenum)
 | 
						|
{
 | 
						|
	Selectivity match = 0.0;
 | 
						|
	inet	   *query,
 | 
						|
			   *left,
 | 
						|
			   *right;
 | 
						|
	int			i,
 | 
						|
				k,
 | 
						|
				n;
 | 
						|
	int			left_order,
 | 
						|
				right_order,
 | 
						|
				left_divider,
 | 
						|
				right_divider;
 | 
						|
 | 
						|
	/* guard against zero-divide below */
 | 
						|
	if (nvalues <= 1)
 | 
						|
		return 0.0;
 | 
						|
 | 
						|
	/* if there are too many histogram elements, decimate to limit runtime */
 | 
						|
	k = (nvalues - 2) / MAX_CONSIDERED_ELEMS + 1;
 | 
						|
 | 
						|
	query = DatumGetInetPP(constvalue);
 | 
						|
 | 
						|
	/* "left" is the left boundary value of the current bucket ... */
 | 
						|
	left = DatumGetInetPP(values[0]);
 | 
						|
	left_order = inet_inclusion_cmp(left, query, opr_codenum);
 | 
						|
 | 
						|
	n = 0;
 | 
						|
	for (i = k; i < nvalues; i += k)
 | 
						|
	{
 | 
						|
		/* ... and "right" is the right boundary value */
 | 
						|
		right = DatumGetInetPP(values[i]);
 | 
						|
		right_order = inet_inclusion_cmp(right, query, opr_codenum);
 | 
						|
 | 
						|
		if (left_order == 0 && right_order == 0)
 | 
						|
		{
 | 
						|
			/* The whole bucket matches, since both endpoints do. */
 | 
						|
			match += 1.0;
 | 
						|
		}
 | 
						|
		else if ((left_order <= 0 && right_order >= 0) ||
 | 
						|
				 (left_order >= 0 && right_order <= 0))
 | 
						|
		{
 | 
						|
			/* Partial bucket match. */
 | 
						|
			left_divider = inet_hist_match_divider(left, query, opr_codenum);
 | 
						|
			right_divider = inet_hist_match_divider(right, query, opr_codenum);
 | 
						|
 | 
						|
			if (left_divider >= 0 || right_divider >= 0)
 | 
						|
				match += 1.0 / pow(2.0, Max(left_divider, right_divider));
 | 
						|
		}
 | 
						|
 | 
						|
		/* Shift the variables. */
 | 
						|
		left = right;
 | 
						|
		left_order = right_order;
 | 
						|
 | 
						|
		/* Count the number of buckets considered. */
 | 
						|
		n++;
 | 
						|
	}
 | 
						|
 | 
						|
	return match / n;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Inet MCV vs MCV join selectivity estimation
 | 
						|
 *
 | 
						|
 * We simply add up the fractions of the populations that satisfy the clause.
 | 
						|
 * The result is exact and does not need to be scaled further.
 | 
						|
 */
 | 
						|
static Selectivity
 | 
						|
inet_mcv_join_sel(Datum *mcv1_values, float4 *mcv1_numbers, int mcv1_nvalues,
 | 
						|
				  Datum *mcv2_values, float4 *mcv2_numbers, int mcv2_nvalues,
 | 
						|
				  Oid operator)
 | 
						|
{
 | 
						|
	Selectivity selec = 0.0;
 | 
						|
	FmgrInfo	proc;
 | 
						|
	int			i,
 | 
						|
				j;
 | 
						|
 | 
						|
	fmgr_info(get_opcode(operator), &proc);
 | 
						|
 | 
						|
	for (i = 0; i < mcv1_nvalues; i++)
 | 
						|
	{
 | 
						|
		for (j = 0; j < mcv2_nvalues; j++)
 | 
						|
			if (DatumGetBool(FunctionCall2(&proc,
 | 
						|
										   mcv1_values[i],
 | 
						|
										   mcv2_values[j])))
 | 
						|
				selec += mcv1_numbers[i] * mcv2_numbers[j];
 | 
						|
	}
 | 
						|
	return selec;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Inet MCV vs histogram join selectivity estimation
 | 
						|
 *
 | 
						|
 * For each MCV on the lefthand side, estimate the fraction of the righthand's
 | 
						|
 * histogram population that satisfies the join clause, and add those up,
 | 
						|
 * scaling by the MCV's frequency.  The result still needs to be scaled
 | 
						|
 * according to the fraction of the righthand's population represented by
 | 
						|
 * the histogram.
 | 
						|
 */
 | 
						|
static Selectivity
 | 
						|
inet_mcv_hist_sel(const Datum *mcv_values, float4 *mcv_numbers, int mcv_nvalues,
 | 
						|
				  const Datum *hist_values, int hist_nvalues,
 | 
						|
				  int opr_codenum)
 | 
						|
{
 | 
						|
	Selectivity selec = 0.0;
 | 
						|
	int			i;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * We'll call inet_hist_value_selec with the histogram on the left, so we
 | 
						|
	 * must commute the operator.
 | 
						|
	 */
 | 
						|
	opr_codenum = -opr_codenum;
 | 
						|
 | 
						|
	for (i = 0; i < mcv_nvalues; i++)
 | 
						|
	{
 | 
						|
		selec += mcv_numbers[i] *
 | 
						|
			inet_hist_value_sel(hist_values, hist_nvalues, mcv_values[i],
 | 
						|
								opr_codenum);
 | 
						|
	}
 | 
						|
	return selec;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Inet histogram vs histogram join selectivity estimation
 | 
						|
 *
 | 
						|
 * Here, we take all values listed in the second histogram (except for the
 | 
						|
 * first and last elements, which are excluded on the grounds of possibly
 | 
						|
 * not being very representative) and treat them as a uniform sample of
 | 
						|
 * the non-MCV population for that relation.  For each one, we apply
 | 
						|
 * inet_hist_value_selec to see what fraction of the first histogram
 | 
						|
 * it matches.
 | 
						|
 *
 | 
						|
 * We could alternatively do this the other way around using the operator's
 | 
						|
 * commutator.  XXX would it be worthwhile to do it both ways and take the
 | 
						|
 * average?  That would at least avoid non-commutative estimation results.
 | 
						|
 */
 | 
						|
static Selectivity
 | 
						|
inet_hist_inclusion_join_sel(const Datum *hist1_values, int hist1_nvalues,
 | 
						|
							 const Datum *hist2_values, int hist2_nvalues,
 | 
						|
							 int opr_codenum)
 | 
						|
{
 | 
						|
	double		match = 0.0;
 | 
						|
	int			i,
 | 
						|
				k,
 | 
						|
				n;
 | 
						|
 | 
						|
	if (hist2_nvalues <= 2)
 | 
						|
		return 0.0;				/* no interior histogram elements */
 | 
						|
 | 
						|
	/* if there are too many histogram elements, decimate to limit runtime */
 | 
						|
	k = (hist2_nvalues - 3) / MAX_CONSIDERED_ELEMS + 1;
 | 
						|
 | 
						|
	n = 0;
 | 
						|
	for (i = 1; i < hist2_nvalues - 1; i += k)
 | 
						|
	{
 | 
						|
		match += inet_hist_value_sel(hist1_values, hist1_nvalues,
 | 
						|
									 hist2_values[i], opr_codenum);
 | 
						|
		n++;
 | 
						|
	}
 | 
						|
 | 
						|
	return match / n;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Inet semi join selectivity estimation for one value
 | 
						|
 *
 | 
						|
 * The function calculates the probability that there is at least one row
 | 
						|
 * in the RHS table that satisfies the "lhs_value op column" condition.
 | 
						|
 * It is used in semi join estimation to check a sample from the left hand
 | 
						|
 * side table.
 | 
						|
 *
 | 
						|
 * The MCV and histogram from the right hand side table should be provided as
 | 
						|
 * arguments with the lhs_value from the left hand side table for the join.
 | 
						|
 * hist_weight is the total number of rows represented by the histogram.
 | 
						|
 * For example, if the table has 1000 rows, and 10% of the rows are in the MCV
 | 
						|
 * list, and another 10% are NULLs, hist_weight would be 800.
 | 
						|
 *
 | 
						|
 * First, the lhs_value will be matched to the most common values.  If it
 | 
						|
 * matches any of them, 1.0 will be returned, because then there is surely
 | 
						|
 * a match.
 | 
						|
 *
 | 
						|
 * Otherwise, the histogram will be used to estimate the number of rows in
 | 
						|
 * the second table that match the condition.  If the estimate is greater
 | 
						|
 * than 1.0, 1.0 will be returned, because it means there is a greater chance
 | 
						|
 * that the lhs_value will match more than one row in the table.  If it is
 | 
						|
 * between 0.0 and 1.0, it will be returned as the probability.
 | 
						|
 */
 | 
						|
static Selectivity
 | 
						|
inet_semi_join_sel(Datum lhs_value,
 | 
						|
				   bool mcv_exists, Datum *mcv_values, int mcv_nvalues,
 | 
						|
				   bool hist_exists, Datum *hist_values, int hist_nvalues,
 | 
						|
				   double hist_weight,
 | 
						|
				   FmgrInfo *proc, int opr_codenum)
 | 
						|
{
 | 
						|
	if (mcv_exists)
 | 
						|
	{
 | 
						|
		int			i;
 | 
						|
 | 
						|
		for (i = 0; i < mcv_nvalues; i++)
 | 
						|
		{
 | 
						|
			if (DatumGetBool(FunctionCall2(proc,
 | 
						|
										   lhs_value,
 | 
						|
										   mcv_values[i])))
 | 
						|
				return 1.0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (hist_exists && hist_weight > 0)
 | 
						|
	{
 | 
						|
		Selectivity hist_selec;
 | 
						|
 | 
						|
		/* Commute operator, since we're passing lhs_value on the right */
 | 
						|
		hist_selec = inet_hist_value_sel(hist_values, hist_nvalues,
 | 
						|
										 lhs_value, -opr_codenum);
 | 
						|
 | 
						|
		if (hist_selec > 0)
 | 
						|
			return Min(1.0, hist_weight * hist_selec);
 | 
						|
	}
 | 
						|
 | 
						|
	return 0.0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Assign useful code numbers for the subnet inclusion/overlap operators
 | 
						|
 *
 | 
						|
 * Only inet_masklen_inclusion_cmp() and inet_hist_match_divider() depend
 | 
						|
 * on the exact codes assigned here; but many other places in this file
 | 
						|
 * know that they can negate a code to obtain the code for the commutator
 | 
						|
 * operator.
 | 
						|
 */
 | 
						|
static int
 | 
						|
inet_opr_codenum(Oid operator)
 | 
						|
{
 | 
						|
	switch (operator)
 | 
						|
	{
 | 
						|
		case OID_INET_SUP_OP:
 | 
						|
			return -2;
 | 
						|
		case OID_INET_SUPEQ_OP:
 | 
						|
			return -1;
 | 
						|
		case OID_INET_OVERLAP_OP:
 | 
						|
			return 0;
 | 
						|
		case OID_INET_SUBEQ_OP:
 | 
						|
			return 1;
 | 
						|
		case OID_INET_SUB_OP:
 | 
						|
			return 2;
 | 
						|
		default:
 | 
						|
			elog(ERROR, "unrecognized operator %u for inet selectivity",
 | 
						|
				 operator);
 | 
						|
	}
 | 
						|
	return 0;					/* unreached, but keep compiler quiet */
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Comparison function for the subnet inclusion/overlap operators
 | 
						|
 *
 | 
						|
 * If the comparison is okay for the specified inclusion operator, the return
 | 
						|
 * value will be 0.  Otherwise the return value will be less than or greater
 | 
						|
 * than 0 as appropriate for the operator.
 | 
						|
 *
 | 
						|
 * Comparison is compatible with the basic comparison function for the inet
 | 
						|
 * type.  See network_cmp_internal() in network.c for the original.  Basic
 | 
						|
 * comparison operators are implemented with the network_cmp_internal()
 | 
						|
 * function.  It is possible to implement the subnet inclusion operators with
 | 
						|
 * this function.
 | 
						|
 *
 | 
						|
 * Comparison is first on the common bits of the network part, then on the
 | 
						|
 * length of the network part (masklen) as in the network_cmp_internal()
 | 
						|
 * function.  Only the first part is in this function.  The second part is
 | 
						|
 * separated to another function for reusability.  The difference between the
 | 
						|
 * second part and the original network_cmp_internal() is that the inclusion
 | 
						|
 * operator is considered while comparing the lengths of the network parts.
 | 
						|
 * See the inet_masklen_inclusion_cmp() function below.
 | 
						|
 */
 | 
						|
static int
 | 
						|
inet_inclusion_cmp(inet *left, inet *right, int opr_codenum)
 | 
						|
{
 | 
						|
	if (ip_family(left) == ip_family(right))
 | 
						|
	{
 | 
						|
		int			order;
 | 
						|
 | 
						|
		order = bitncmp(ip_addr(left), ip_addr(right),
 | 
						|
						Min(ip_bits(left), ip_bits(right)));
 | 
						|
		if (order != 0)
 | 
						|
			return order;
 | 
						|
 | 
						|
		return inet_masklen_inclusion_cmp(left, right, opr_codenum);
 | 
						|
	}
 | 
						|
 | 
						|
	return ip_family(left) - ip_family(right);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Masklen comparison function for the subnet inclusion/overlap operators
 | 
						|
 *
 | 
						|
 * Compares the lengths of the network parts of the inputs.  If the comparison
 | 
						|
 * is okay for the specified inclusion operator, the return value will be 0.
 | 
						|
 * Otherwise the return value will be less than or greater than 0 as
 | 
						|
 * appropriate for the operator.
 | 
						|
 */
 | 
						|
static int
 | 
						|
inet_masklen_inclusion_cmp(inet *left, inet *right, int opr_codenum)
 | 
						|
{
 | 
						|
	int			order;
 | 
						|
 | 
						|
	order = (int) ip_bits(left) - (int) ip_bits(right);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Return 0 if the operator would accept this combination of masklens.
 | 
						|
	 * Note that opr_codenum zero (overlaps) will accept all cases.
 | 
						|
	 */
 | 
						|
	if ((order > 0 && opr_codenum >= 0) ||
 | 
						|
		(order == 0 && opr_codenum >= -1 && opr_codenum <= 1) ||
 | 
						|
		(order < 0 && opr_codenum <= 0))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Otherwise, return a negative value for sup/supeq (notionally, the RHS
 | 
						|
	 * needs to have a larger masklen than it has, which would make it sort
 | 
						|
	 * later), or a positive value for sub/subeq (vice versa).
 | 
						|
	 */
 | 
						|
	return opr_codenum;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Inet histogram partial match divider calculation
 | 
						|
 *
 | 
						|
 * First the families and the lengths of the network parts are compared using
 | 
						|
 * the subnet inclusion operator.  If those are acceptable for the operator,
 | 
						|
 * the divider will be calculated using the masklens and the common bits of
 | 
						|
 * the addresses.  -1 will be returned if it cannot be calculated.
 | 
						|
 *
 | 
						|
 * See commentary for inet_hist_value_sel() for some rationale for this.
 | 
						|
 */
 | 
						|
static int
 | 
						|
inet_hist_match_divider(inet *boundary, inet *query, int opr_codenum)
 | 
						|
{
 | 
						|
	if (ip_family(boundary) == ip_family(query) &&
 | 
						|
		inet_masklen_inclusion_cmp(boundary, query, opr_codenum) == 0)
 | 
						|
	{
 | 
						|
		int			min_bits,
 | 
						|
					decisive_bits;
 | 
						|
 | 
						|
		min_bits = Min(ip_bits(boundary), ip_bits(query));
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Set decisive_bits to the masklen of the one that should contain the
 | 
						|
		 * other according to the operator.
 | 
						|
		 */
 | 
						|
		if (opr_codenum < 0)
 | 
						|
			decisive_bits = ip_bits(boundary);
 | 
						|
		else if (opr_codenum > 0)
 | 
						|
			decisive_bits = ip_bits(query);
 | 
						|
		else
 | 
						|
			decisive_bits = min_bits;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Now return the number of non-common decisive bits.  (This will be
 | 
						|
		 * zero if the boundary and query in fact match, else positive.)
 | 
						|
		 */
 | 
						|
		if (min_bits > 0)
 | 
						|
			return decisive_bits - bitncommon(ip_addr(boundary),
 | 
						|
											  ip_addr(query),
 | 
						|
											  min_bits);
 | 
						|
		return decisive_bits;
 | 
						|
	}
 | 
						|
 | 
						|
	return -1;
 | 
						|
}
 |