mirror of
https://github.com/postgres/postgres.git
synced 2025-07-31 22:04:40 +03:00
config
contrib
adminpack
amcheck
auth_delay
auto_explain
bloom
bool_plperl
btree_gin
btree_gist
citext
cube
dblink
dict_int
dict_xsyn
earthdistance
file_fdw
fuzzystrmatch
hstore
data
expected
sql
.gitignore
Makefile
hstore--1.1--1.2.sql
hstore--1.2--1.3.sql
hstore--1.3--1.4.sql
hstore--1.4--1.5.sql
hstore--1.4.sql
hstore--1.5--1.6.sql
hstore--1.6--1.7.sql
hstore--1.7--1.8.sql
hstore.control
hstore.h
hstore_compat.c
hstore_gin.c
hstore_gist.c
hstore_io.c
hstore_op.c
hstore_subs.c
hstore_plperl
hstore_plpython
intagg
intarray
isn
jsonb_plperl
jsonb_plpython
lo
ltree
ltree_plpython
oid2name
old_snapshot
pageinspect
passwordcheck
pg_buffercache
pg_freespacemap
pg_prewarm
pg_stat_statements
pg_surgery
pg_trgm
pg_visibility
pgcrypto
pgrowlocks
pgstattuple
postgres_fdw
seg
sepgsql
spi
sslinfo
start-scripts
tablefunc
tcn
test_decoding
tsm_system_rows
tsm_system_time
unaccent
uuid-ossp
vacuumlo
xml2
Makefile
README
contrib-global.mk
doc
src
.dir-locals.el
.editorconfig
.git-blame-ignore-revs
.gitattributes
.gitignore
COPYRIGHT
GNUmakefile.in
HISTORY
Makefile
README
README.git
aclocal.m4
configure
configure.ac
Our usual practice for "poor man's enum" catalog columns is to define macros for the possible values and use those, not literal constants, in C code. But for some reason lost in the mists of time, this was never done for typalign/attalign or typstorage/attstorage. It's never too late to make it better though, so let's do that. The reason I got interested in this right now is the need to duplicate some uses of the TYPSTORAGE constants in an upcoming ALTER TYPE patch. But in general, this sort of change aids greppability and readability, so it's a good idea even without any specific motivation. I may have missed a few places that could be converted, and it's even more likely that pending patches will re-introduce some hard-coded references. But that's not fatal --- there's no expectation that we'd actually change any of these values. We can clean up stragglers over time. Discussion: https://postgr.es/m/16457.1583189537@sss.pgh.pa.us
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, TYPALIGN_INT,
|
|
&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);
|
|
}
|