306 lines
7.0 KiB
C
306 lines
7.0 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* ginarrayproc.c
|
|
* support functions for GIN's indexing of any array
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/access/gin/ginarrayproc.c
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/gin.h"
|
|
#include "access/stratnum.h"
|
|
#include "utils/array.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
|
|
|
|
#define GinOverlapStrategy 1
|
|
#define GinContainsStrategy 2
|
|
#define GinContainedStrategy 3
|
|
#define GinEqualStrategy 4
|
|
|
|
|
|
/*
|
|
* extractValue support function
|
|
*/
|
|
Datum
|
|
ginarrayextract(PG_FUNCTION_ARGS)
|
|
{
|
|
/* Make copy of array input to ensure it doesn't disappear while in use */
|
|
ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
|
|
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
|
|
bool **nullFlags = (bool **) PG_GETARG_POINTER(2);
|
|
int16 elmlen;
|
|
bool elmbyval;
|
|
char elmalign;
|
|
Datum *elems;
|
|
bool *nulls;
|
|
int nelems;
|
|
|
|
get_typlenbyvalalign(ARR_ELEMTYPE(array),
|
|
&elmlen, &elmbyval, &elmalign);
|
|
|
|
deconstruct_array(array,
|
|
ARR_ELEMTYPE(array),
|
|
elmlen, elmbyval, elmalign,
|
|
&elems, &nulls, &nelems);
|
|
|
|
*nkeys = nelems;
|
|
*nullFlags = nulls;
|
|
|
|
/* we should not free array, elems[i] points into it */
|
|
PG_RETURN_POINTER(elems);
|
|
}
|
|
|
|
/*
|
|
* Formerly, ginarrayextract had only two arguments. Now it has three,
|
|
* but we still need a pg_proc entry with two args to support reloading
|
|
* pre-9.1 contrib/intarray opclass declarations. This compatibility
|
|
* function should go away eventually.
|
|
*/
|
|
Datum
|
|
ginarrayextract_2args(PG_FUNCTION_ARGS)
|
|
{
|
|
if (PG_NARGS() < 3) /* should not happen */
|
|
elog(ERROR, "ginarrayextract requires three arguments");
|
|
return ginarrayextract(fcinfo);
|
|
}
|
|
|
|
/*
|
|
* extractQuery support function
|
|
*/
|
|
Datum
|
|
ginqueryarrayextract(PG_FUNCTION_ARGS)
|
|
{
|
|
/* Make copy of array input to ensure it doesn't disappear while in use */
|
|
ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
|
|
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
|
|
StrategyNumber strategy = PG_GETARG_UINT16(2);
|
|
|
|
/* bool **pmatch = (bool **) PG_GETARG_POINTER(3); */
|
|
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
|
|
bool **nullFlags = (bool **) PG_GETARG_POINTER(5);
|
|
int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
|
|
int16 elmlen;
|
|
bool elmbyval;
|
|
char elmalign;
|
|
Datum *elems;
|
|
bool *nulls;
|
|
int nelems;
|
|
|
|
get_typlenbyvalalign(ARR_ELEMTYPE(array),
|
|
&elmlen, &elmbyval, &elmalign);
|
|
|
|
deconstruct_array(array,
|
|
ARR_ELEMTYPE(array),
|
|
elmlen, elmbyval, elmalign,
|
|
&elems, &nulls, &nelems);
|
|
|
|
*nkeys = nelems;
|
|
*nullFlags = nulls;
|
|
|
|
switch (strategy)
|
|
{
|
|
case GinOverlapStrategy:
|
|
*searchMode = GIN_SEARCH_MODE_DEFAULT;
|
|
break;
|
|
case GinContainsStrategy:
|
|
if (nelems > 0)
|
|
*searchMode = GIN_SEARCH_MODE_DEFAULT;
|
|
else /* everything contains the empty set */
|
|
*searchMode = GIN_SEARCH_MODE_ALL;
|
|
break;
|
|
case GinContainedStrategy:
|
|
/* empty set is contained in everything */
|
|
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
|
|
break;
|
|
case GinEqualStrategy:
|
|
if (nelems > 0)
|
|
*searchMode = GIN_SEARCH_MODE_DEFAULT;
|
|
else
|
|
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
|
|
break;
|
|
default:
|
|
elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
|
|
strategy);
|
|
}
|
|
|
|
/* we should not free array, elems[i] points into it */
|
|
PG_RETURN_POINTER(elems);
|
|
}
|
|
|
|
/*
|
|
* consistent support function
|
|
*/
|
|
Datum
|
|
ginarrayconsistent(PG_FUNCTION_ARGS)
|
|
{
|
|
bool *check = (bool *) PG_GETARG_POINTER(0);
|
|
StrategyNumber strategy = PG_GETARG_UINT16(1);
|
|
|
|
/* ArrayType *query = PG_GETARG_ARRAYTYPE_P(2); */
|
|
int32 nkeys = PG_GETARG_INT32(3);
|
|
|
|
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
|
|
bool *recheck = (bool *) PG_GETARG_POINTER(5);
|
|
|
|
/* Datum *queryKeys = (Datum *) PG_GETARG_POINTER(6); */
|
|
bool *nullFlags = (bool *) PG_GETARG_POINTER(7);
|
|
bool res;
|
|
int32 i;
|
|
|
|
switch (strategy)
|
|
{
|
|
case GinOverlapStrategy:
|
|
/* result is not lossy */
|
|
*recheck = false;
|
|
/* must have a match for at least one non-null element */
|
|
res = false;
|
|
for (i = 0; i < nkeys; i++)
|
|
{
|
|
if (check[i] && !nullFlags[i])
|
|
{
|
|
res = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case GinContainsStrategy:
|
|
/* result is not lossy */
|
|
*recheck = false;
|
|
/* must have all elements in check[] true, and no nulls */
|
|
res = true;
|
|
for (i = 0; i < nkeys; i++)
|
|
{
|
|
if (!check[i] || nullFlags[i])
|
|
{
|
|
res = false;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case GinContainedStrategy:
|
|
/* we will need recheck */
|
|
*recheck = true;
|
|
/* can't do anything else useful here */
|
|
res = true;
|
|
break;
|
|
case GinEqualStrategy:
|
|
/* we will need recheck */
|
|
*recheck = true;
|
|
|
|
/*
|
|
* Must have all elements in check[] true; no discrimination
|
|
* against nulls here. This is because array_contain_compare and
|
|
* array_eq handle nulls differently ...
|
|
*/
|
|
res = true;
|
|
for (i = 0; i < nkeys; i++)
|
|
{
|
|
if (!check[i])
|
|
{
|
|
res = false;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
elog(ERROR, "ginarrayconsistent: unknown strategy number: %d",
|
|
strategy);
|
|
res = false;
|
|
}
|
|
|
|
PG_RETURN_BOOL(res);
|
|
}
|
|
|
|
/*
|
|
* triconsistent support function
|
|
*/
|
|
Datum
|
|
ginarraytriconsistent(PG_FUNCTION_ARGS)
|
|
{
|
|
GinTernaryValue *check = (GinTernaryValue *) PG_GETARG_POINTER(0);
|
|
StrategyNumber strategy = PG_GETARG_UINT16(1);
|
|
|
|
/* ArrayType *query = PG_GETARG_ARRAYTYPE_P(2); */
|
|
int32 nkeys = PG_GETARG_INT32(3);
|
|
|
|
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
|
|
/* Datum *queryKeys = (Datum *) PG_GETARG_POINTER(5); */
|
|
bool *nullFlags = (bool *) PG_GETARG_POINTER(6);
|
|
GinTernaryValue res;
|
|
int32 i;
|
|
|
|
switch (strategy)
|
|
{
|
|
case GinOverlapStrategy:
|
|
/* must have a match for at least one non-null element */
|
|
res = GIN_FALSE;
|
|
for (i = 0; i < nkeys; i++)
|
|
{
|
|
if (!nullFlags[i])
|
|
{
|
|
if (check[i] == GIN_TRUE)
|
|
{
|
|
res = GIN_TRUE;
|
|
break;
|
|
}
|
|
else if (check[i] == GIN_MAYBE && res == GIN_FALSE)
|
|
{
|
|
res = GIN_MAYBE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case GinContainsStrategy:
|
|
/* must have all elements in check[] true, and no nulls */
|
|
res = GIN_TRUE;
|
|
for (i = 0; i < nkeys; i++)
|
|
{
|
|
if (check[i] == GIN_FALSE || nullFlags[i])
|
|
{
|
|
res = GIN_FALSE;
|
|
break;
|
|
}
|
|
if (check[i] == GIN_MAYBE)
|
|
{
|
|
res = GIN_MAYBE;
|
|
}
|
|
}
|
|
break;
|
|
case GinContainedStrategy:
|
|
/* can't do anything else useful here */
|
|
res = GIN_MAYBE;
|
|
break;
|
|
case GinEqualStrategy:
|
|
|
|
/*
|
|
* Must have all elements in check[] true; no discrimination
|
|
* against nulls here. This is because array_contain_compare and
|
|
* array_eq handle nulls differently ...
|
|
*/
|
|
res = GIN_MAYBE;
|
|
for (i = 0; i < nkeys; i++)
|
|
{
|
|
if (check[i] == GIN_FALSE)
|
|
{
|
|
res = GIN_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
elog(ERROR, "ginarrayconsistent: unknown strategy number: %d",
|
|
strategy);
|
|
res = false;
|
|
}
|
|
|
|
PG_RETURN_GIN_TERNARY_VALUE(res);
|
|
}
|