mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-25 18:37:20 +02:00
By project convention, these names should include "P" when dealing with a pointer type; that is, if the result of a GETARG macro is of type FOO *, it should be called PG_GETARG_FOO_P not just PG_GETARG_FOO. Some newer types such as JSONB and ranges had not followed the convention, and a number of contrib modules hadn't gotten that memo either. Rename the offending macros to improve consistency. In passing, fix a few places that thought PG_DETOAST_DATUM() returns a Datum; it does not, it returns "struct varlena *". Applying DatumGetPointer to that happens not to cause any bad effects today, but it's formally wrong. Also, adjust an ltree macro that was designed without any thought for what pgindent would do with it. This is all cosmetic and shouldn't have any impact on generated code. Mark Dilger, some further tweaks by me Discussion: https://postgr.es/m/EA5676F4-766F-4F38-8348-ECC7DB427C6A@gmail.com
213 lines
5.0 KiB
C
213 lines
5.0 KiB
C
/*
|
|
* contrib/hstore/hstore_gin.c
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/gin.h"
|
|
#include "access/stratnum.h"
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "hstore.h"
|
|
|
|
|
|
/*
|
|
* When using a GIN index for hstore, we choose to index both keys and values.
|
|
* The storage format is "text" values, with K, V, or N prepended to the string
|
|
* to indicate key, value, or null values. (As of 9.1 it might be better to
|
|
* store null values as nulls, but we'll keep it this way for on-disk
|
|
* compatibility.)
|
|
*/
|
|
#define KEYFLAG 'K'
|
|
#define VALFLAG 'V'
|
|
#define NULLFLAG 'N'
|
|
|
|
PG_FUNCTION_INFO_V1(gin_extract_hstore);
|
|
|
|
/* Build an indexable text value */
|
|
static text *
|
|
makeitem(char *str, int len, char flag)
|
|
{
|
|
text *item;
|
|
|
|
item = (text *) palloc(VARHDRSZ + len + 1);
|
|
SET_VARSIZE(item, VARHDRSZ + len + 1);
|
|
|
|
*VARDATA(item) = flag;
|
|
|
|
if (str && len > 0)
|
|
memcpy(VARDATA(item) + 1, str, len);
|
|
|
|
return item;
|
|
}
|
|
|
|
Datum
|
|
gin_extract_hstore(PG_FUNCTION_ARGS)
|
|
{
|
|
HStore *hs = PG_GETARG_HSTORE_P(0);
|
|
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
|
|
Datum *entries = NULL;
|
|
HEntry *hsent = ARRPTR(hs);
|
|
char *ptr = STRPTR(hs);
|
|
int count = HS_COUNT(hs);
|
|
int i;
|
|
|
|
*nentries = 2 * count;
|
|
if (count)
|
|
entries = (Datum *) palloc(sizeof(Datum) * 2 * count);
|
|
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
text *item;
|
|
|
|
item = makeitem(HSTORE_KEY(hsent, ptr, i),
|
|
HSTORE_KEYLEN(hsent, i),
|
|
KEYFLAG);
|
|
entries[2 * i] = PointerGetDatum(item);
|
|
|
|
if (HSTORE_VALISNULL(hsent, i))
|
|
item = makeitem(NULL, 0, NULLFLAG);
|
|
else
|
|
item = makeitem(HSTORE_VAL(hsent, ptr, i),
|
|
HSTORE_VALLEN(hsent, i),
|
|
VALFLAG);
|
|
entries[2 * i + 1] = PointerGetDatum(item);
|
|
}
|
|
|
|
PG_RETURN_POINTER(entries);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(gin_extract_hstore_query);
|
|
|
|
Datum
|
|
gin_extract_hstore_query(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
|
|
StrategyNumber strategy = PG_GETARG_UINT16(2);
|
|
int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
|
|
Datum *entries;
|
|
|
|
if (strategy == HStoreContainsStrategyNumber)
|
|
{
|
|
/* Query is an hstore, so just apply gin_extract_hstore... */
|
|
entries = (Datum *)
|
|
DatumGetPointer(DirectFunctionCall2(gin_extract_hstore,
|
|
PG_GETARG_DATUM(0),
|
|
PointerGetDatum(nentries)));
|
|
/* ... except that "contains {}" requires a full index scan */
|
|
if (entries == NULL)
|
|
*searchMode = GIN_SEARCH_MODE_ALL;
|
|
}
|
|
else if (strategy == HStoreExistsStrategyNumber)
|
|
{
|
|
text *query = PG_GETARG_TEXT_PP(0);
|
|
text *item;
|
|
|
|
*nentries = 1;
|
|
entries = (Datum *) palloc(sizeof(Datum));
|
|
item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query), KEYFLAG);
|
|
entries[0] = PointerGetDatum(item);
|
|
}
|
|
else if (strategy == HStoreExistsAnyStrategyNumber ||
|
|
strategy == HStoreExistsAllStrategyNumber)
|
|
{
|
|
ArrayType *query = PG_GETARG_ARRAYTYPE_P(0);
|
|
Datum *key_datums;
|
|
bool *key_nulls;
|
|
int key_count;
|
|
int i,
|
|
j;
|
|
text *item;
|
|
|
|
deconstruct_array(query,
|
|
TEXTOID, -1, false, 'i',
|
|
&key_datums, &key_nulls, &key_count);
|
|
|
|
entries = (Datum *) palloc(sizeof(Datum) * key_count);
|
|
|
|
for (i = 0, j = 0; i < key_count; ++i)
|
|
{
|
|
/* Nulls in the array are ignored, cf hstoreArrayToPairs */
|
|
if (key_nulls[i])
|
|
continue;
|
|
item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG);
|
|
entries[j++] = PointerGetDatum(item);
|
|
}
|
|
|
|
*nentries = j;
|
|
/* ExistsAll with no keys should match everything */
|
|
if (j == 0 && strategy == HStoreExistsAllStrategyNumber)
|
|
*searchMode = GIN_SEARCH_MODE_ALL;
|
|
}
|
|
else
|
|
{
|
|
elog(ERROR, "unrecognized strategy number: %d", strategy);
|
|
entries = NULL; /* keep compiler quiet */
|
|
}
|
|
|
|
PG_RETURN_POINTER(entries);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(gin_consistent_hstore);
|
|
|
|
Datum
|
|
gin_consistent_hstore(PG_FUNCTION_ARGS)
|
|
{
|
|
bool *check = (bool *) PG_GETARG_POINTER(0);
|
|
StrategyNumber strategy = PG_GETARG_UINT16(1);
|
|
|
|
/* HStore *query = PG_GETARG_HSTORE_P(2); */
|
|
int32 nkeys = PG_GETARG_INT32(3);
|
|
|
|
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
|
|
bool *recheck = (bool *) PG_GETARG_POINTER(5);
|
|
bool res = true;
|
|
int32 i;
|
|
|
|
if (strategy == HStoreContainsStrategyNumber)
|
|
{
|
|
/*
|
|
* Index doesn't have information about correspondence of keys and
|
|
* values, so we need recheck. However, if not all the keys are
|
|
* present, we can fail at once.
|
|
*/
|
|
*recheck = true;
|
|
for (i = 0; i < nkeys; i++)
|
|
{
|
|
if (!check[i])
|
|
{
|
|
res = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (strategy == HStoreExistsStrategyNumber)
|
|
{
|
|
/* Existence of key is guaranteed in default search mode */
|
|
*recheck = false;
|
|
res = true;
|
|
}
|
|
else if (strategy == HStoreExistsAnyStrategyNumber)
|
|
{
|
|
/* Existence of key is guaranteed in default search mode */
|
|
*recheck = false;
|
|
res = true;
|
|
}
|
|
else if (strategy == HStoreExistsAllStrategyNumber)
|
|
{
|
|
/* Testing for all the keys being present gives an exact result */
|
|
*recheck = false;
|
|
for (i = 0; i < nkeys; i++)
|
|
{
|
|
if (!check[i])
|
|
{
|
|
res = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
elog(ERROR, "unrecognized strategy number: %d", strategy);
|
|
|
|
PG_RETURN_BOOL(res);
|
|
}
|