@ -178,6 +178,51 @@ static pg_attribute_always_inline void ExecAggPlainTransByRef(AggState *aggstate
ExprContext * aggcontext ,
int setno ) ;
/*
* ScalarArrayOpExprHashEntry
* Hash table entry type used during EEOP_HASHED_SCALARARRAYOP
*/
typedef struct ScalarArrayOpExprHashEntry
{
Datum key ;
uint32 status ; /* hash status */
uint32 hash ; /* hash value (cached) */
} ScalarArrayOpExprHashEntry ;
# define SH_PREFIX saophash
# define SH_ELEMENT_TYPE ScalarArrayOpExprHashEntry
# define SH_KEY_TYPE Datum
# define SH_SCOPE static inline
# define SH_DECLARE
# include "lib/simplehash.h"
static bool saop_hash_element_match ( struct saophash_hash * tb , Datum key1 ,
Datum key2 ) ;
static uint32 saop_element_hash ( struct saophash_hash * tb , Datum key ) ;
/*
* ScalarArrayOpExprHashTable
* Hash table for EEOP_HASHED_SCALARARRAYOP
*/
typedef struct ScalarArrayOpExprHashTable
{
saophash_hash * hashtab ; /* underlying hash table */
struct ExprEvalStep * op ;
} ScalarArrayOpExprHashTable ;
/* Define parameters for ScalarArrayOpExpr hash table code generation. */
# define SH_PREFIX saophash
# define SH_ELEMENT_TYPE ScalarArrayOpExprHashEntry
# define SH_KEY_TYPE Datum
# define SH_KEY key
# define SH_HASH_KEY(tb, key) saop_element_hash(tb, key)
# define SH_EQUAL(tb, a, b) saop_hash_element_match(tb, a, b)
# define SH_SCOPE static inline
# define SH_STORE_HASH
# define SH_GET_HASH(tb, a) a->hash
# define SH_DEFINE
# include "lib/simplehash.h"
/*
* Prepare ExprState for interpreted execution .
*/
@ -426,6 +471,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
& & CASE_EEOP_DOMAIN_CHECK ,
& & CASE_EEOP_CONVERT_ROWTYPE ,
& & CASE_EEOP_SCALARARRAYOP ,
& & CASE_EEOP_HASHED_SCALARARRAYOP ,
& & CASE_EEOP_XMLEXPR ,
& & CASE_EEOP_AGGREF ,
& & CASE_EEOP_GROUPING_FUNC ,
@ -1436,6 +1482,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT ( ) ;
}
EEO_CASE ( EEOP_HASHED_SCALARARRAYOP )
{
/* too complex for an inline implementation */
ExecEvalHashedScalarArrayOp ( state , op , econtext ) ;
EEO_NEXT ( ) ;
}
EEO_CASE ( EEOP_DOMAIN_NOTNULL )
{
/* too complex for an inline implementation */
@ -3345,6 +3399,214 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
* op - > resnull = resultnull ;
}
/*
* Hash function for scalar array hash op elements .
*
* We use the element type ' s default hash opclass , and the column collation
* if the type is collation - sensitive .
*/
static uint32
saop_element_hash ( struct saophash_hash * tb , Datum key )
{
ScalarArrayOpExprHashTable * elements_tab = ( ScalarArrayOpExprHashTable * ) tb - > private_data ;
FunctionCallInfo fcinfo = elements_tab - > op - > d . hashedscalararrayop . hash_fcinfo_data ;
Datum hash ;
fcinfo - > args [ 0 ] . value = key ;
fcinfo - > args [ 0 ] . isnull = false ;
hash = elements_tab - > op - > d . hashedscalararrayop . hash_fn_addr ( fcinfo ) ;
return DatumGetUInt32 ( hash ) ;
}
/*
* Matching function for scalar array hash op elements , to be used in hashtable
* lookups .
*/
static bool
saop_hash_element_match ( struct saophash_hash * tb , Datum key1 , Datum key2 )
{
Datum result ;
ScalarArrayOpExprHashTable * elements_tab = ( ScalarArrayOpExprHashTable * ) tb - > private_data ;
FunctionCallInfo fcinfo = elements_tab - > op - > d . hashedscalararrayop . fcinfo_data ;
fcinfo - > args [ 0 ] . value = key1 ;
fcinfo - > args [ 0 ] . isnull = false ;
fcinfo - > args [ 1 ] . value = key2 ;
fcinfo - > args [ 1 ] . isnull = false ;
result = elements_tab - > op - > d . hashedscalararrayop . fn_addr ( fcinfo ) ;
return DatumGetBool ( result ) ;
}
/*
* Evaluate " scalar op ANY (const array) " .
*
* Similar to ExecEvalScalarArrayOp , but optimized for faster repeat lookups
* by building a hashtable on the first lookup . This hashtable will be reused
* by subsequent lookups . Unlike ExecEvalScalarArrayOp , this version only
* supports OR semantics .
*
* Source array is in our result area , scalar arg is already evaluated into
* fcinfo - > args [ 0 ] .
*
* The operator always yields boolean .
*/
void
ExecEvalHashedScalarArrayOp ( ExprState * state , ExprEvalStep * op , ExprContext * econtext )
{
ScalarArrayOpExprHashTable * elements_tab = op - > d . hashedscalararrayop . elements_tab ;
FunctionCallInfo fcinfo = op - > d . hashedscalararrayop . fcinfo_data ;
bool strictfunc = op - > d . hashedscalararrayop . finfo - > fn_strict ;
Datum scalar = fcinfo - > args [ 0 ] . value ;
bool scalar_isnull = fcinfo - > args [ 0 ] . isnull ;
Datum result ;
bool resultnull ;
bool hashfound ;
/* We don't setup a hashed scalar array op if the array const is null. */
Assert ( ! * op - > resnull ) ;
/*
* If the scalar is NULL , and the function is strict , return NULL ; no
* point in executing the search .
*/
if ( fcinfo - > args [ 0 ] . isnull & & strictfunc )
{
* op - > resnull = true ;
return ;
}
/* Build the hash table on first evaluation */
if ( elements_tab = = NULL )
{
int16 typlen ;
bool typbyval ;
char typalign ;
int nitems ;
bool has_nulls = false ;
char * s ;
bits8 * bitmap ;
int bitmask ;
MemoryContext oldcontext ;
ArrayType * arr ;
arr = DatumGetArrayTypeP ( * op - > resvalue ) ;
nitems = ArrayGetNItems ( ARR_NDIM ( arr ) , ARR_DIMS ( arr ) ) ;
get_typlenbyvalalign ( ARR_ELEMTYPE ( arr ) ,
& typlen ,
& typbyval ,
& typalign ) ;
oldcontext = MemoryContextSwitchTo ( econtext - > ecxt_per_query_memory ) ;
elements_tab = ( ScalarArrayOpExprHashTable * )
palloc ( sizeof ( ScalarArrayOpExprHashTable ) ) ;
op - > d . hashedscalararrayop . elements_tab = elements_tab ;
elements_tab - > op = op ;
/*
* Create the hash table sizing it according to the number of elements
* in the array . This does assume that the array has no duplicates .
* If the array happens to contain many duplicate values then it ' ll
* just mean that we sized the table a bit on the large side .
*/
elements_tab - > hashtab = saophash_create ( CurrentMemoryContext , nitems ,
elements_tab ) ;
MemoryContextSwitchTo ( oldcontext ) ;
s = ( char * ) ARR_DATA_PTR ( arr ) ;
bitmap = ARR_NULLBITMAP ( arr ) ;
bitmask = 1 ;
for ( int i = 0 ; i < nitems ; i + + )
{
/* Get array element, checking for NULL. */
if ( bitmap & & ( * bitmap & bitmask ) = = 0 )
{
has_nulls = true ;
}
else
{
Datum element ;
element = fetch_att ( s , typbyval , typlen ) ;
s = att_addlength_pointer ( s , typlen , s ) ;
s = ( char * ) att_align_nominal ( s , typalign ) ;
saophash_insert ( elements_tab - > hashtab , element , & hashfound ) ;
}
/* Advance bitmap pointer if any. */
if ( bitmap )
{
bitmask < < = 1 ;
if ( bitmask = = 0x100 )
{
bitmap + + ;
bitmask = 1 ;
}
}
}
/*
* Remember if we had any nulls so that we know if we need to execute
* non - strict functions with a null lhs value if no match is found .
*/
op - > d . hashedscalararrayop . has_nulls = has_nulls ;
}
/* Check the hash to see if we have a match. */
hashfound = NULL ! = saophash_lookup ( elements_tab - > hashtab , scalar ) ;
result = BoolGetDatum ( hashfound ) ;
resultnull = false ;
/*
* If we didn ' t find a match in the array , we still might need to handle
* the possibility of null values . We didn ' t put any NULLs into the
* hashtable , but instead marked if we found any when building the table
* in has_nulls .
*/
if ( ! DatumGetBool ( result ) & & op - > d . hashedscalararrayop . has_nulls )
{
if ( strictfunc )
{
/*
* We have nulls in the array so a non - null lhs and no match must
* yield NULL .
*/
result = ( Datum ) 0 ;
resultnull = true ;
}
else
{
/*
* Execute function will null rhs just once .
*
* The hash lookup path will have scribbled on the lhs argument so
* we need to set it up also ( even though we entered this function
* with it already set ) .
*/
fcinfo - > args [ 0 ] . value = scalar ;
fcinfo - > args [ 0 ] . isnull = scalar_isnull ;
fcinfo - > args [ 1 ] . value = ( Datum ) 0 ;
fcinfo - > args [ 1 ] . isnull = true ;
result = op - > d . hashedscalararrayop . fn_addr ( fcinfo ) ;
resultnull = fcinfo - > isnull ;
}
}
* op - > resvalue = result ;
* op - > resnull = resultnull ;
}
/*
* Evaluate a NOT NULL domain constraint .
*/