Index: contrib/hstore/hstore.h =================================================================== RCS file: /projects/cvsroot/pgsql/contrib/hstore/hstore.h,v retrieving revision 1.7 diff -c -r1.7 hstore.h *** contrib/hstore/hstore.h 15 Mar 2009 22:05:17 -0000 1.7 --- contrib/hstore/hstore.h 24 Mar 2009 15:14:17 -0000 *************** *** 5,38 **** #define __HSTORE_H__ #include "fmgr.h" typedef struct { ! uint16 keylen; ! uint16 vallen; ! uint32 ! valisnull:1, ! pos:31; } HEntry; ! /* these are determined by the sizes of the keylen and vallen fields */ ! /* in struct HEntry and struct Pairs */ ! #define HSTORE_MAX_KEY_LEN 65535 ! #define HSTORE_MAX_VALUE_LEN 65535 ! typedef struct { int32 vl_len_; /* varlena header (do not touch directly!) */ int4 size; ! char data[1]; } HStore; ! #define HSHRDSIZE (VARHDRSZ + sizeof(int4)) ! #define CALCDATASIZE(x, lenstr) ( (x) * sizeof(HEntry) + HSHRDSIZE + (lenstr) ) ! #define ARRPTR(x) ( (HEntry*) ( (char*)(x) + HSHRDSIZE ) ) ! #define STRPTR(x) ( (char*)(x) + HSHRDSIZE + ( sizeof(HEntry) * ((HStore*)x)->size ) ) #define PG_GETARG_HS(x) ((HStore*)PG_DETOAST_DATUM(PG_GETARG_DATUM(x))) --- 5,114 ---- #define __HSTORE_H__ #include "fmgr.h" + #include "utils/array.h" + /* there is one of these for each key _and_ value */ + /* the value points to the _end_ so that we can get the length + * by subtraction from the previous entry. + */ typedef struct { ! uint32 entry; } HEntry; ! #define HENTRY_ISFIRST 0x80000000 ! #define HENTRY_ISNULL 0x40000000 ! #define HENTRY_POSMASK 0x3FFFFFFF ! ! /* determined by the size of "endpos", though this is a bit academic ! * since currently varlenas (and hence both the input and the whole hstore) ! * have the same limit ! */ ! #define HSTORE_MAX_KEY_LEN 0x3FFFFFFF ! #define HSTORE_MAX_VALUE_LEN 0x3FFFFFFF typedef struct { int32 vl_len_; /* varlena header (do not touch directly!) */ int4 size; ! /* array of HEntry follows */ } HStore; ! #define HSHRDSIZE (sizeof(HStore)) ! #define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) ) ! ! /* note multiple evaluations of x */ ! #define ARRPTR(x) ( (HEntry*) ( (HStore*)(x) + 1 ) ) ! #define STRPTR(x) ( (char*)(ARRPTR(x) + ((HStore*)x)->size * 2) ) ! ! /* note multiple/non evaluations */ ! #define HSE_ISFIRST(he_) (((he_).entry & HENTRY_ISFIRST) != 0) ! #define HSE_ISNULL(he_) (((he_).entry & HENTRY_ISNULL) != 0) ! #define HSE_ENDPOS(he_) ((he_).entry & HENTRY_POSMASK) ! #define HSE_OFF(he_) (HSE_ISFIRST(he_) ? 0 : HSE_ENDPOS((&(he_))[-1])) ! #define HSE_LEN(he_) (HSE_ISFIRST(he_) \ ! ? HSE_ENDPOS(he_) \ ! : HSE_ENDPOS(he_) - HSE_ENDPOS((&(he_))[-1])) ! ! #define HS_KEY(arr_,str_,i_) ((str_) + HSE_OFF((arr_)[2*(i_)])) ! #define HS_VAL(arr_,str_,i_) ((str_) + HSE_OFF((arr_)[2*(i_)+1])) ! #define HS_KEYLEN(arr_,i_) (HSE_LEN((arr_)[2*(i_)])) ! #define HS_VALLEN(arr_,i_) (HSE_LEN((arr_)[2*(i_)+1])) ! #define HS_VALISNULL(arr_,i_) (HSE_ISNULL((arr_)[2*(i_)+1])) ! ! /* currently, these following macros are the _only_ places that rely ! * on internal knowledge of HEntry. Everything else should be using ! * the above macros. ! */ ! ! /* copy one key/value pair (which must be contiguous starting at ! * sptr_) into an under-construction hstore; dent_ is an HEntry*, ! * dbuf_ is the destination's string buffer, dptr_ is the current ! * position in the destination. lots of modification and multiple ! * evaluation here. ! */ ! #define HS_COPYITEM(dent_,dbuf_,dptr_,sptr_,klen_,vlen_,vnull_) \ ! do { \ ! memcpy((dptr_), (sptr_), (klen_)+(vlen_)); \ ! (dptr_) += (klen_)+(vlen_); \ ! (dent_)++->entry = ((dptr_) - (dbuf_) - (vlen_)) & HENTRY_POSMASK; \ ! (dent_)++->entry = ((((dptr_) - (dbuf_)) & HENTRY_POSMASK) \ ! | ((vnull_) ? HENTRY_ISNULL : 0)); \ ! } while(0) ! ! /* add one key/item pair, from a Pairs structure, into an ! * under-construction hstore ! */ ! #define HS_ADDITEM(dent_,dbuf_,dptr_,pair_) \ ! do { \ ! memcpy((dptr_), (pair_).key, (pair_).keylen); \ ! (dptr_) += (pair_).keylen; \ ! (dent_)++->entry = ((dptr_) - (dbuf_)) & HENTRY_POSMASK; \ ! if ((pair_).isnull) \ ! (dent_)++->entry = ((((dptr_) - (dbuf_)) & HENTRY_POSMASK) \ ! | HENTRY_ISNULL); \ ! else \ ! { \ ! memcpy((dptr_), (pair_).val, (pair_).vallen); \ ! (dptr_) += (pair_).vallen; \ ! (dent_)++->entry = ((dptr_) - (dbuf_)) & HENTRY_POSMASK; \ ! } \ ! } while (0) ! ! /* finalize a newly-constructed hstore */ ! #define HS_FINALIZE(hsp_,count_,buf_,ptr_) \ ! do { \ ! if ((count_)) \ ! ARRPTR(hsp_)[0].entry |= HENTRY_ISFIRST; \ ! if ((count_) != (hsp_)->size) \ ! { \ ! int buflen = (ptr_) - (buf_); \ ! (hsp_)->size = (count_); \ ! memmove(STRPTR(hsp_), (buf_), buflen); \ ! SET_VARSIZE((hsp_), CALCDATASIZE((count_), buflen)); \ ! } \ ! } while (0) #define PG_GETARG_HS(x) ((HStore*)PG_DETOAST_DATUM(PG_GETARG_DATUM(x))) *************** *** 41,59 **** { char *key; char *val; ! uint16 keylen; ! uint16 vallen; bool isnull; bool needfree; } Pairs; ! int comparePairs(const void *a, const void *b); ! int uniquePairs(Pairs * a, int4 l, int4 *buflen); size_t hstoreCheckKeyLen(size_t len); size_t hstoreCheckValLen(size_t len); #define HStoreContainsStrategyNumber 7 #define HStoreExistsStrategyNumber 9 #endif /* __HSTORE_H__ */ --- 117,157 ---- { char *key; char *val; ! size_t keylen; ! size_t vallen; bool isnull; bool needfree; } Pairs; ! int hstoreUniquePairs(Pairs * a, int4 l, int4 *buflen); ! HStore * hstorePairs(Pairs *pairs, int4 pcount, int4 buflen); size_t hstoreCheckKeyLen(size_t len); size_t hstoreCheckValLen(size_t len); + int hstoreFindKey(HStore * hs, int *lowbound, char *key, int keylen); + Pairs * hstoreArrayToPairs(ArrayType *a, int* npairs); + #define HStoreContainsStrategyNumber 7 #define HStoreExistsStrategyNumber 9 + #define HStoreExistsAnyStrategyNumber 10 + #define HStoreExistsAllStrategyNumber 11 + + /* defining HSTORE_POLLUTE_NAMESPACE=0 will prevent this; for now, we default + * to on for the benefit of people restoring old dumps + */ + #ifndef HSTORE_POLLUTE_NAMESPACE + #define HSTORE_POLLUTE_NAMESPACE 1 + #endif + + #if HSTORE_POLLUTE_NAMESPACE + #define HSTORE_POLLUTE(newname_,oldname_) \ + PG_FUNCTION_INFO_V1(oldname_); \ + Datum oldname_(PG_FUNCTION_ARGS); \ + Datum newname_(PG_FUNCTION_ARGS); \ + Datum oldname_(PG_FUNCTION_ARGS) { return newname_(fcinfo); } + #else + #define HSTORE_POLLUTE(newname_,oldname_) + #endif #endif /* __HSTORE_H__ */ Index: contrib/hstore/hstore.sql.in =================================================================== RCS file: /projects/cvsroot/pgsql/contrib/hstore/hstore.sql.in,v retrieving revision 1.9 diff -c -r1.9 hstore.sql.in *** contrib/hstore/hstore.sql.in 14 Apr 2008 17:05:32 -0000 1.9 --- contrib/hstore/hstore.sql.in 24 Mar 2009 15:14:18 -0000 *************** *** 15,30 **** AS 'MODULE_PATHNAME' LANGUAGE C STRICT; CREATE TYPE hstore ( INTERNALLENGTH = -1, INPUT = hstore_in, OUTPUT = hstore_out, STORAGE = extended ); CREATE OR REPLACE FUNCTION fetchval(hstore,text) RETURNS text ! AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OPERATOR -> ( --- 15,42 ---- AS 'MODULE_PATHNAME' LANGUAGE C STRICT; + CREATE OR REPLACE FUNCTION hstore_recv(internal) + RETURNS hstore + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT; + + CREATE OR REPLACE FUNCTION hstore_send(hstore) + RETURNS bytea + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT; + CREATE TYPE hstore ( INTERNALLENGTH = -1, INPUT = hstore_in, OUTPUT = hstore_out, + RECEIVE = hstore_recv, + SEND = hstore_send, STORAGE = extended ); CREATE OR REPLACE FUNCTION fetchval(hstore,text) RETURNS text ! AS 'MODULE_PATHNAME','hstore_fetchval' LANGUAGE C STRICT IMMUTABLE; CREATE OPERATOR -> ( *************** *** 33,46 **** PROCEDURE = fetchval ); CREATE OR REPLACE FUNCTION isexists(hstore,text) RETURNS bool ! AS 'MODULE_PATHNAME','exists' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION exist(hstore,text) RETURNS bool ! AS 'MODULE_PATHNAME','exists' LANGUAGE C STRICT IMMUTABLE; CREATE OPERATOR ? ( --- 45,80 ---- PROCEDURE = fetchval ); + CREATE OR REPLACE FUNCTION slice_array(hstore,text[]) + RETURNS text[] + AS 'MODULE_PATHNAME','hstore_slice_to_array' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OPERATOR -> ( + LEFTARG = hstore, + RIGHTARG = text[], + PROCEDURE = slice_array + ); + + CREATE OR REPLACE FUNCTION slice_hstore(hstore,text[]) + RETURNS hstore + AS 'MODULE_PATHNAME','hstore_slice_to_hstore' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OPERATOR => ( + LEFTARG = hstore, + RIGHTARG = text[], + PROCEDURE = slice_hstore + ); + CREATE OR REPLACE FUNCTION isexists(hstore,text) RETURNS bool ! AS 'MODULE_PATHNAME','hstore_exists' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION exist(hstore,text) RETURNS bool ! AS 'MODULE_PATHNAME','hstore_exists' LANGUAGE C STRICT IMMUTABLE; CREATE OPERATOR ? ( *************** *** 51,74 **** JOIN = contjoinsel ); CREATE OR REPLACE FUNCTION isdefined(hstore,text) RETURNS bool ! AS 'MODULE_PATHNAME','defined' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION defined(hstore,text) RETURNS bool ! AS 'MODULE_PATHNAME','defined' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION delete(hstore,text) RETURNS hstore ! AS 'MODULE_PATHNAME','delete' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION hs_concat(hstore,hstore) RETURNS hstore ! AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OPERATOR || ( --- 85,162 ---- JOIN = contjoinsel ); + CREATE OR REPLACE FUNCTION exists_any(hstore,text[]) + RETURNS bool + AS 'MODULE_PATHNAME','hstore_exists_any' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OPERATOR ?| ( + LEFTARG = hstore, + RIGHTARG = text[], + PROCEDURE = exists_any, + RESTRICT = contsel, + JOIN = contjoinsel + ); + + CREATE OR REPLACE FUNCTION exists_all(hstore,text[]) + RETURNS bool + AS 'MODULE_PATHNAME','hstore_exists_all' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OPERATOR ?& ( + LEFTARG = hstore, + RIGHTARG = text[], + PROCEDURE = exists_all, + RESTRICT = contsel, + JOIN = contjoinsel + ); + CREATE OR REPLACE FUNCTION isdefined(hstore,text) RETURNS bool ! AS 'MODULE_PATHNAME','hstore_defined' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION defined(hstore,text) RETURNS bool ! AS 'MODULE_PATHNAME','hstore_defined' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION delete(hstore,text) RETURNS hstore ! AS 'MODULE_PATHNAME','hstore_delete' LANGUAGE C STRICT IMMUTABLE; + CREATE OR REPLACE FUNCTION delete(hstore,text[]) + RETURNS hstore + AS 'MODULE_PATHNAME','hstore_delete_array' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OR REPLACE FUNCTION delete(hstore,hstore) + RETURNS hstore + AS 'MODULE_PATHNAME','hstore_delete_hstore' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OPERATOR - ( + LEFTARG = hstore, + RIGHTARG = text, + PROCEDURE = delete + ); + + CREATE OPERATOR - ( + LEFTARG = hstore, + RIGHTARG = text[], + PROCEDURE = delete + ); + + CREATE OPERATOR - ( + LEFTARG = hstore, + RIGHTARG = hstore, + PROCEDURE = delete + ); + CREATE OR REPLACE FUNCTION hs_concat(hstore,hstore) RETURNS hstore ! AS 'MODULE_PATHNAME','hstore_concat' LANGUAGE C STRICT IMMUTABLE; CREATE OPERATOR || ( *************** *** 79,90 **** CREATE OR REPLACE FUNCTION hs_contains(hstore,hstore) RETURNS bool ! AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION hs_contained(hstore,hstore) RETURNS bool ! AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OPERATOR @> ( --- 167,178 ---- CREATE OR REPLACE FUNCTION hs_contains(hstore,hstore) RETURNS bool ! AS 'MODULE_PATHNAME','hstore_contains' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION hs_contained(hstore,hstore) RETURNS bool ! AS 'MODULE_PATHNAME','hstore_contained' LANGUAGE C STRICT IMMUTABLE; CREATE OPERATOR @> ( *************** *** 126,168 **** CREATE OR REPLACE FUNCTION tconvert(text,text) RETURNS hstore ! AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE; CREATE OPERATOR => ( LEFTARG = text, RIGHTARG = text, ! PROCEDURE = tconvert ); CREATE OR REPLACE FUNCTION akeys(hstore) RETURNS _text ! AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION avals(hstore) RETURNS _text ! AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION skeys(hstore) RETURNS setof text ! AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION svals(hstore) RETURNS setof text ! AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION each(IN hs hstore, OUT key text, OUT value text) RETURNS SETOF record ! AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -- define the GiST support methods --- 214,406 ---- CREATE OR REPLACE FUNCTION tconvert(text,text) RETURNS hstore ! AS 'MODULE_PATHNAME','hstore_from_text' ! LANGUAGE C IMMUTABLE; ! ! CREATE OR REPLACE FUNCTION hstore(text,text) ! RETURNS hstore ! AS 'MODULE_PATHNAME','hstore_from_text' LANGUAGE C IMMUTABLE; CREATE OPERATOR => ( LEFTARG = text, RIGHTARG = text, ! PROCEDURE = hstore ); + CREATE OR REPLACE FUNCTION hstore(text[],text[]) + RETURNS hstore + AS 'MODULE_PATHNAME', 'hstore_from_arrays' + LANGUAGE C IMMUTABLE; + + CREATE OPERATOR => ( + LEFTARG = text[], + RIGHTARG = text[], + PROCEDURE = hstore + ); + + CREATE OR REPLACE FUNCTION hstore(record) + RETURNS hstore + AS 'MODULE_PATHNAME', 'hstore_from_record' + LANGUAGE C IMMUTABLE; + CREATE OR REPLACE FUNCTION akeys(hstore) RETURNS _text ! AS 'MODULE_PATHNAME','hstore_akeys' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION avals(hstore) RETURNS _text ! AS 'MODULE_PATHNAME','hstore_avals' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION skeys(hstore) RETURNS setof text ! AS 'MODULE_PATHNAME','hstore_skeys' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION svals(hstore) RETURNS setof text ! AS 'MODULE_PATHNAME','hstore_svals' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION each(IN hs hstore, OUT key text, OUT value text) RETURNS SETOF record ! AS 'MODULE_PATHNAME','hstore_each' LANGUAGE C STRICT IMMUTABLE; + CREATE OR REPLACE FUNCTION populate_record(anyelement,hstore) + RETURNS anyelement + AS 'MODULE_PATHNAME', 'hstore_populate_record' + LANGUAGE C IMMUTABLE; + + CREATE OPERATOR #= ( + LEFTARG = anyelement, + RIGHTARG = hstore, + PROCEDURE = populate_record + ); + + -- btree + CREATE OR REPLACE FUNCTION hstore_eq(hstore,hstore) + RETURNS boolean + AS 'MODULE_PATHNAME','hstore_eq' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OR REPLACE FUNCTION hstore_ne(hstore,hstore) + RETURNS boolean + AS 'MODULE_PATHNAME','hstore_ne' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OR REPLACE FUNCTION hstore_gt(hstore,hstore) + RETURNS boolean + AS 'MODULE_PATHNAME','hstore_gt' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OR REPLACE FUNCTION hstore_ge(hstore,hstore) + RETURNS boolean + AS 'MODULE_PATHNAME','hstore_ge' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OR REPLACE FUNCTION hstore_lt(hstore,hstore) + RETURNS boolean + AS 'MODULE_PATHNAME','hstore_lt' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OR REPLACE FUNCTION hstore_le(hstore,hstore) + RETURNS boolean + AS 'MODULE_PATHNAME','hstore_le' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OR REPLACE FUNCTION hstore_cmp(hstore,hstore) + RETURNS integer + AS 'MODULE_PATHNAME','hstore_cmp' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OPERATOR = ( + LEFTARG = hstore, + RIGHTARG = hstore, + PROCEDURE = hstore_eq, + COMMUTATOR = =, + NEGATOR = <>, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES, + HASHES + ); + CREATE OPERATOR <> ( + LEFTARG = hstore, + RIGHTARG = hstore, + PROCEDURE = hstore_ne, + COMMUTATOR = <>, + NEGATOR = =, + RESTRICT = neqsel, + JOIN = neqjoinsel + ); + + -- the comparison operators have funky names (and are undocumented) + -- in an attempt to discourage anyone from actually using them. they + -- only exist to support the btree opclass + + CREATE OPERATOR #<# ( + LEFTARG = hstore, + RIGHTARG = hstore, + PROCEDURE = hstore_lt, + COMMUTATOR = #>#, + NEGATOR = #>=#, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel + ); + CREATE OPERATOR #<=# ( + LEFTARG = hstore, + RIGHTARG = hstore, + PROCEDURE = hstore_le, + COMMUTATOR = #>=#, + NEGATOR = #>#, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel + ); + CREATE OPERATOR #># ( + LEFTARG = hstore, + RIGHTARG = hstore, + PROCEDURE = hstore_gt, + COMMUTATOR = #<#, + NEGATOR = #<=#, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel + ); + CREATE OPERATOR #>=# ( + LEFTARG = hstore, + RIGHTARG = hstore, + PROCEDURE = hstore_ge, + COMMUTATOR = #<=#, + NEGATOR = #<#, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel + ); + + CREATE OPERATOR CLASS btree_hstore_ops + DEFAULT FOR TYPE hstore USING btree + AS + OPERATOR 1 #<# , + OPERATOR 2 #<=# , + OPERATOR 3 = , + OPERATOR 4 #>=# , + OPERATOR 5 #># , + FUNCTION 1 hstore_cmp(hstore,hstore); + + CREATE OR REPLACE FUNCTION hstore_hash(hstore) + RETURNS integer + AS 'MODULE_PATHNAME','hstore_hash' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OPERATOR CLASS hash_hstore_ops + DEFAULT FOR TYPE hstore USING hash + AS + OPERATOR 1 = , + FUNCTION 1 hstore_hash(hstore); -- define the GiST support methods *************** *** 225,230 **** --- 463,470 ---- AS OPERATOR 7 @> , OPERATOR 9 ?(hstore,text) , + OPERATOR 10 ?|(hstore,text[]), + OPERATOR 11 ?&(hstore,text[]), --OPERATOR 8 <@ , OPERATOR 13 @ , --OPERATOR 14 ~ , *************** *** 259,266 **** AS OPERATOR 7 @> , OPERATOR 9 ?(hstore,text), FUNCTION 1 bttextcmp(text,text), FUNCTION 2 gin_extract_hstore(internal, internal), FUNCTION 3 gin_extract_hstore_query(internal, internal, int2), FUNCTION 4 gin_consistent_hstore(internal, int2, internal, internal), ! STORAGE text; --- 499,510 ---- AS OPERATOR 7 @> , OPERATOR 9 ?(hstore,text), + OPERATOR 10 ?|(hstore,text[]), + OPERATOR 11 ?&(hstore,text[]), FUNCTION 1 bttextcmp(text,text), FUNCTION 2 gin_extract_hstore(internal, internal), FUNCTION 3 gin_extract_hstore_query(internal, internal, int2), FUNCTION 4 gin_consistent_hstore(internal, int2, internal, internal), ! STORAGE text; ! ! -- Index: contrib/hstore/hstore_gin.c =================================================================== RCS file: /projects/cvsroot/pgsql/contrib/hstore/hstore_gin.c,v retrieving revision 1.4 diff -c -r1.4 hstore_gin.c *** contrib/hstore/hstore_gin.c 12 May 2008 00:00:42 -0000 1.4 --- contrib/hstore/hstore_gin.c 24 Mar 2009 15:14:18 -0000 *************** *** 4,9 **** --- 4,10 ---- #include "postgres.h" #include "access/gin.h" + #include "catalog/pg_type.h" #include "hstore.h" *************** *** 35,77 **** HStore *hs = PG_GETARG_HS(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); Datum *entries = NULL; ! *nentries = 2 * hs->size; ! ! if (hs->size > 0) { ! HEntry *ptr = ARRPTR(hs); ! char *words = STRPTR(hs); ! int i = 0; ! entries = (Datum *) palloc(sizeof(Datum) * 2 * hs->size); ! while (ptr - ARRPTR(hs) < hs->size) { ! text *item; ! ! item = makeitem(words + ptr->pos, ptr->keylen); ! *VARDATA(item) = KEYFLAG; ! entries[i++] = PointerGetDatum(item); ! ! if (ptr->valisnull) ! { ! item = makeitem(NULL, 0); ! *VARDATA(item) = NULLFLAG; ! ! } ! else ! { ! item = makeitem(words + ptr->pos + ptr->keylen, ptr->vallen); ! *VARDATA(item) = VALFLAG; ! } ! entries[i++] = PointerGetDatum(item); ! ! ptr++; } } - PG_FREE_IF_COPY(hs, 0); PG_RETURN_POINTER(entries); } --- 36,71 ---- HStore *hs = PG_GETARG_HS(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); Datum *entries = NULL; + HEntry *hsent = ARRPTR(hs); + char *ptr = STRPTR(hs); + int count = hs->size; + int i; + + *nentries = 2 * count; + if (count) + entries = (Datum *) palloc(sizeof(Datum) * 2 * count); ! for (i = 0; i < count; ++i) { ! text *item; ! item = makeitem(HS_KEY(hsent,ptr,i), HS_KEYLEN(hsent,i)); ! *VARDATA(item) = KEYFLAG; ! entries[2*i] = PointerGetDatum(item); ! if (HS_VALISNULL(hsent,i)) { ! item = makeitem(NULL, 0); ! *VARDATA(item) = NULLFLAG; ! } ! else ! { ! item = makeitem(HS_VAL(hsent,ptr,i), HS_VALLEN(hsent,i)); ! *VARDATA(item) = VALFLAG; } + entries[2*i+1] = PointerGetDatum(item); } PG_RETURN_POINTER(entries); } *************** *** 85,92 **** if (strategy == HStoreContainsStrategyNumber) { ! PG_RETURN_DATUM(DirectFunctionCall2( ! gin_extract_hstore, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1) )); --- 79,85 ---- if (strategy == HStoreContainsStrategyNumber) { ! PG_RETURN_DATUM(DirectFunctionCall2(gin_extract_hstore, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1) )); *************** *** 94,112 **** else if (strategy == HStoreExistsStrategyNumber) { text *item, ! *q = PG_GETARG_TEXT_P(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); Datum *entries = NULL; *nentries = 1; entries = (Datum *) palloc(sizeof(Datum)); ! item = makeitem(VARDATA(q), VARSIZE(q) - VARHDRSZ); *VARDATA(item) = KEYFLAG; entries[0] = PointerGetDatum(item); PG_RETURN_POINTER(entries); } else elog(ERROR, "Unsupported strategy number: %d", strategy); --- 87,136 ---- else if (strategy == HStoreExistsStrategyNumber) { text *item, ! *query = PG_GETARG_TEXT_PP(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); Datum *entries = NULL; *nentries = 1; entries = (Datum *) palloc(sizeof(Datum)); ! item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query)); *VARDATA(item) = KEYFLAG; entries[0] = PointerGetDatum(item); PG_RETURN_POINTER(entries); } + 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; + int32 *nentries = (int32 *) PG_GETARG_POINTER(1); + Datum *entries = NULL; + 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) + { + if (key_nulls[i]) + continue; + item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ); + *VARDATA(item) = KEYFLAG; + entries[j++] = PointerGetDatum(item); + } + + *nentries = j ? j : -1; + + PG_RETURN_POINTER(entries); + } else elog(ERROR, "Unsupported strategy number: %d", strategy); *************** *** 121,132 **** { bool *check = (bool *) PG_GETARG_POINTER(0); StrategyNumber strategy = PG_GETARG_UINT16(1); - HStore *query = PG_GETARG_HS(2); bool *recheck = (bool *) PG_GETARG_POINTER(3); bool res = true; if (strategy == HStoreContainsStrategyNumber) { int i; /* --- 145,156 ---- { bool *check = (bool *) PG_GETARG_POINTER(0); StrategyNumber strategy = PG_GETARG_UINT16(1); bool *recheck = (bool *) PG_GETARG_POINTER(3); bool res = true; if (strategy == HStoreContainsStrategyNumber) { + HStore *query = PG_GETARG_HS(2); int i; /* *************** *** 144,149 **** --- 168,209 ---- *recheck = false; res = true; } + else if (strategy == HStoreExistsAnyStrategyNumber) + { + /* Existence of key is guaranteed */ + *recheck = false; + res = true; + } + else if (strategy == HStoreExistsAllStrategyNumber) + { + ArrayType *a = PG_GETARG_ARRAYTYPE_P(2); + Datum *key_datums; + bool *key_nulls; + int key_count; + int i,j; + + /* we have to do a full-on deconstruct here for no reason other + * than silly deficiencies of the GIN interface; we have no other + * way to get the count of entries in the "check" array. Yuk! + * (we can't just use the array size, since we dropped out any + * null values) + */ + + deconstruct_array(a, + TEXTOID, -1, false, 'i', + &key_datums, &key_nulls, &key_count); + + for (i = 0, j = 0; res && i < key_count; ++i) + { + if (key_nulls[i]) + continue; + if (!check[j++]) + res = false; + } + + /* Existence of key is guaranteed */ + *recheck = false; + } else elog(ERROR, "Unsupported strategy number: %d", strategy); Index: contrib/hstore/hstore_gist.c =================================================================== RCS file: /projects/cvsroot/pgsql/contrib/hstore/hstore_gist.c,v retrieving revision 1.9 diff -c -r1.9 hstore_gist.c *** contrib/hstore/hstore_gist.c 12 May 2008 00:00:42 -0000 1.9 --- contrib/hstore/hstore_gist.c 24 Mar 2009 15:14:18 -0000 *************** *** 3,8 **** --- 3,9 ---- */ #include "postgres.h" + #include "catalog/pg_type.h" #include "access/gist.h" #include "access/itup.h" #include "access/skey.h" *************** *** 114,143 **** if (entry->leafkey) { GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0)); - HStore *toastedval = (HStore *) DatumGetPointer(entry->key); HStore *val = (HStore *) DatumGetPointer(PG_DETOAST_DATUM(entry->key)); ! HEntry *ptr = ARRPTR(val); ! char *words = STRPTR(val); SET_VARSIZE(res, CALCGTSIZE(0)); ! while (ptr - ARRPTR(val) < val->size) { ! int h; ! h = crc32_sz((char *) (words + ptr->pos), ptr->keylen); HASH(GETSIGN(res), h); ! if (!ptr->valisnull) { ! h = crc32_sz((char *) (words + ptr->pos + ptr->keylen), ptr->vallen); HASH(GETSIGN(res), h); } - ptr++; } - if (val != toastedval) - pfree(val); - retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(res), entry->rel, entry->page, --- 115,141 ---- if (entry->leafkey) { GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0)); HStore *val = (HStore *) DatumGetPointer(PG_DETOAST_DATUM(entry->key)); ! HEntry *hsent = ARRPTR(val); ! char *ptr = STRPTR(val); ! int count = val->size; ! int i; SET_VARSIZE(res, CALCGTSIZE(0)); ! for (i = 0; i < count; ++i) { ! int h; ! h = crc32_sz((char *) HS_KEY(hsent,ptr,i), HS_KEYLEN(hsent,i)); HASH(GETSIGN(res), h); ! if (!HS_VALISNULL(hsent,i)) { ! h = crc32_sz((char *) HS_VAL(hsent,ptr,i), HS_VALLEN(hsent,i)); HASH(GETSIGN(res), h); } } retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(res), entry->rel, entry->page, *************** *** 500,506 **** } *right = *left = FirstOffsetNumber; - pfree(costvector); v->spl_ldatum = PointerGetDatum(datum_l); v->spl_rdatum = PointerGetDatum(datum_r); --- 498,503 ---- *************** *** 532,563 **** HStore *query = PG_GETARG_HS(1); HEntry *qe = ARRPTR(query); char *qv = STRPTR(query); ! while (res && qe - ARRPTR(query) < query->size) { ! int crc = crc32_sz((char *) (qv + qe->pos), qe->keylen); if (GETBIT(sign, HASHVAL(crc))) { ! if (!qe->valisnull) { ! crc = crc32_sz((char *) (qv + qe->pos + qe->keylen), qe->vallen); if (!GETBIT(sign, HASHVAL(crc))) res = false; } } else res = false; - qe++; } } else if (strategy == HStoreExistsStrategyNumber) { ! text *query = PG_GETARG_TEXT_P(1); ! int crc = crc32_sz(VARDATA(query), VARSIZE(query) - VARHDRSZ); res = (GETBIT(sign, HASHVAL(crc))) ? true : false; } else elog(ERROR, "Unsupported strategy number: %d", strategy); --- 529,607 ---- HStore *query = PG_GETARG_HS(1); HEntry *qe = ARRPTR(query); char *qv = STRPTR(query); + int count = query->size; + int i; ! for (i = 0; res && i < count; ++i) { ! int crc = crc32_sz((char *) HS_KEY(qe,qv,i), HS_KEYLEN(qe,i)); if (GETBIT(sign, HASHVAL(crc))) { ! if (!HS_VALISNULL(qe,i)) { ! crc = crc32_sz((char *) HS_VAL(qe,qv,i), HS_VALLEN(qe,i)); if (!GETBIT(sign, HASHVAL(crc))) res = false; } } else res = false; } } else if (strategy == HStoreExistsStrategyNumber) { ! text *query = PG_GETARG_TEXT_PP(1); ! int crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query)); res = (GETBIT(sign, HASHVAL(crc))) ? true : false; } + else if (strategy == HStoreExistsAllStrategyNumber) + { + ArrayType *query = PG_GETARG_ARRAYTYPE_P(1); + Datum *key_datums; + bool *key_nulls; + int key_count; + int i; + + deconstruct_array(query, + TEXTOID, -1, false, 'i', + &key_datums, &key_nulls, &key_count); + + for (i = 0; res && i < key_count; ++i) + { + int crc; + if (key_nulls[i]) + continue; + crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ); + if (!(GETBIT(sign, HASHVAL(crc)))) + res = FALSE; + } + } + else if (strategy == HStoreExistsAnyStrategyNumber) + { + ArrayType *query = PG_GETARG_ARRAYTYPE_P(1); + Datum *key_datums; + bool *key_nulls; + int key_count; + int i; + + deconstruct_array(query, + TEXTOID, -1, false, 'i', + &key_datums, &key_nulls, &key_count); + + res = FALSE; + + for (i = 0; !res && i < key_count; ++i) + { + int crc; + if (key_nulls[i]) + continue; + crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ); + if (GETBIT(sign, HASHVAL(crc))) + res = TRUE; + } + } else elog(ERROR, "Unsupported strategy number: %d", strategy); Index: contrib/hstore/hstore_io.c =================================================================== RCS file: /projects/cvsroot/pgsql/contrib/hstore/hstore_io.c,v retrieving revision 1.9 diff -c -r1.9 hstore_io.c *** contrib/hstore/hstore_io.c 15 Mar 2009 22:05:17 -0000 1.9 --- contrib/hstore/hstore_io.c 24 Mar 2009 15:14:19 -0000 *************** *** 2,7 **** --- 2,12 ---- * $PostgreSQL: pgsql/contrib/hstore/hstore_io.c,v 1.9 2009/03/15 22:05:17 tgl Exp $ */ #include "postgres.h" + #include "catalog/pg_type.h" + #include "utils/lsyscache.h" + #include "utils/typcache.h" + #include "libpq/pqformat.h" + #include "funcapi.h" #include *************** *** 9,14 **** --- 14,24 ---- PG_MODULE_MAGIC; + #if HSTORE_POLLUTE_NAMESPACE + HSTORE_POLLUTE(hstore_from_text,tconvert) + #endif + + typedef struct { char *begin; *************** *** 263,269 **** } } ! int comparePairs(const void *a, const void *b) { if (((Pairs *) a)->keylen == ((Pairs *) b)->keylen) --- 273,279 ---- } } ! static int comparePairs(const void *a, const void *b) { if (((Pairs *) a)->keylen == ((Pairs *) b)->keylen) *************** *** 286,293 **** return (((Pairs *) a)->keylen > ((Pairs *) b)->keylen) ? 1 : -1; } int ! uniquePairs(Pairs * a, int4 l, int4 *buflen) { Pairs *ptr, *res; --- 296,309 ---- return (((Pairs *) a)->keylen > ((Pairs *) b)->keylen) ? 1 : -1; } + /* this code still respects pairs.needfree, even though in general + * it should never be called in a context where anything needs freeing. + * we keep it because (a) those calls are in a rare code path anyway, + * and (b) who knows whether they might be needed by some caller. + */ + int ! hstoreUniquePairs(Pairs * a, int4 l, int4 *buflen) { Pairs *ptr, *res; *************** *** 305,311 **** res = a; while (ptr - a < l) { ! if (ptr->keylen == res->keylen && strncmp(ptr->key, res->key, res->keylen) == 0) { if (ptr->needfree) { --- 321,328 ---- res = a; while (ptr - a < l) { ! if (ptr->keylen == res->keylen ! && strncmp(ptr->key, res->key, res->keylen) == 0) { if (ptr->needfree) { *************** *** 327,332 **** --- 344,350 ---- return res + 1 - a; } + #if 0 /* this superfluous code was >15% of hstore_in's runtime. */ static void freeHSParse(HSParser * state) { *************** *** 344,349 **** --- 362,368 ---- } pfree(state->pairs); } + #endif size_t hstoreCheckKeyLen(size_t len) *************** *** 366,430 **** } PG_FUNCTION_INFO_V1(hstore_in); Datum hstore_in(PG_FUNCTION_ARGS); Datum hstore_in(PG_FUNCTION_ARGS) { HSParser state; ! int4 len, ! buflen, ! i; HStore *out; - HEntry *entries; - char *ptr; state.begin = PG_GETARG_CSTRING(0); parse_hstore(&state); ! if (state.pcur == 0) { ! freeHSParse(&state); ! len = CALCDATASIZE(0, 0); ! out = palloc(len); ! SET_VARSIZE(out, len); ! out->size = 0; PG_RETURN_POINTER(out); } ! state.pcur = uniquePairs(state.pairs, state.pcur, &buflen); ! len = CALCDATASIZE(state.pcur, buflen); ! out = palloc(len); ! SET_VARSIZE(out, len); ! out->size = state.pcur; - entries = ARRPTR(out); - ptr = STRPTR(out); ! for (i = 0; i < out->size; i++) { ! entries[i].keylen = state.pairs[i].keylen; ! entries[i].pos = ptr - STRPTR(out); ! memcpy(ptr, state.pairs[i].key, state.pairs[i].keylen); ! ptr += entries[i].keylen; ! entries[i].valisnull = state.pairs[i].isnull; ! if (entries[i].valisnull) ! entries[i].vallen = 4; /* null */ else { ! entries[i].vallen = state.pairs[i].vallen; ! memcpy(ptr, state.pairs[i].val, state.pairs[i].vallen); ! ptr += entries[i].vallen; } } ! freeHSParse(&state); PG_RETURN_POINTER(out); } static char * cpw(char *dst, char *src, int len) { --- 385,937 ---- } + HStore * + hstorePairs(Pairs *pairs, int4 pcount, int4 buflen) + { + HStore *out; + HEntry *entry; + char *ptr; + char *buf; + int4 len; + int4 i; + + len = CALCDATASIZE(pcount, buflen); + out = palloc(len); + SET_VARSIZE(out, len); + out->size = pcount; + + if (pcount == 0) + return out; + + entry = ARRPTR(out); + buf = ptr = STRPTR(out); + + for (i = 0; i < pcount; i++) + HS_ADDITEM(entry,buf,ptr,pairs[i]); + + HS_FINALIZE(out,pcount,buf,ptr); + + return out; + } + + PG_FUNCTION_INFO_V1(hstore_in); Datum hstore_in(PG_FUNCTION_ARGS); Datum hstore_in(PG_FUNCTION_ARGS) { HSParser state; ! int4 buflen; HStore *out; state.begin = PG_GETARG_CSTRING(0); parse_hstore(&state); ! state.pcur = hstoreUniquePairs(state.pairs, state.pcur, &buflen); ! ! out = hstorePairs(state.pairs, state.pcur, buflen); ! ! #if 0 /* see comment above */ ! freeHSParse(&state); ! #endif ! ! PG_RETURN_POINTER(out); ! } ! ! ! PG_FUNCTION_INFO_V1(hstore_recv); ! Datum hstore_recv(PG_FUNCTION_ARGS); ! Datum ! hstore_recv(PG_FUNCTION_ARGS) ! { ! int4 buflen; ! HStore *out; ! Pairs *pairs; ! int4 i; ! int4 pcount; ! StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); ! ! pcount = pq_getmsgint(buf, 4); ! ! if (pcount == 0) { ! out = hstorePairs(NULL, 0, 0); PG_RETURN_POINTER(out); } ! pairs = palloc(pcount * sizeof(Pairs)); ! for (i = 0; i < pcount; ++i) ! { ! int rawlen = pq_getmsgint(buf, 4); ! int len; ! ! if (rawlen < 0) ! ereport(ERROR, ! (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), ! errmsg("null value not allowed for hstore key"))); ! ! pairs[i].key = pq_getmsgtext(buf, rawlen, &len); ! pairs[i].keylen = hstoreCheckKeyLen(len); ! pairs[i].needfree = true; ! ! rawlen = pq_getmsgint(buf, 4); ! if (rawlen < 0) ! { ! pairs[i].val = NULL; ! pairs[i].vallen = 0; ! pairs[i].isnull = true; ! } ! else ! { ! pairs[i].val = pq_getmsgtext(buf, rawlen, &len); ! pairs[i].vallen = hstoreCheckValLen(len); ! pairs[i].isnull = false; ! } ! } ! ! pcount = hstoreUniquePairs(pairs, pcount, &buflen); ! ! out = hstorePairs(pairs, pcount, buflen); ! ! PG_RETURN_POINTER(out); ! } ! PG_FUNCTION_INFO_V1(hstore_from_text); ! Datum hstore_from_text(PG_FUNCTION_ARGS); ! Datum ! hstore_from_text(PG_FUNCTION_ARGS) ! { ! text *key; ! text *val = NULL; ! Pairs p; ! HStore *out; ! ! if (PG_ARGISNULL(0)) ! PG_RETURN_NULL(); ! ! p.needfree = false; ! key = PG_GETARG_TEXT_PP(0); ! p.key = VARDATA_ANY(key); ! p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key)); ! ! if (PG_ARGISNULL(1)) { ! p.vallen = 0; ! p.isnull = true; ! } ! else ! { ! val = PG_GETARG_TEXT_PP(1); ! p.val = VARDATA_ANY(val); ! p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val)); ! p.isnull = false; ! } ! ! out = hstorePairs(&p, 1, p.keylen + p.vallen); ! ! PG_RETURN_POINTER(out); ! } ! ! ! PG_FUNCTION_INFO_V1(hstore_from_arrays); ! Datum hstore_from_arrays(PG_FUNCTION_ARGS); ! Datum ! hstore_from_arrays(PG_FUNCTION_ARGS) ! { ! int4 buflen; ! HStore *out; ! Pairs *pairs; ! Datum *key_datums; ! bool *key_nulls; ! int key_count; ! Datum *value_datums; ! bool *value_nulls; ! int value_count; ! ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(0); ! ArrayType *value_array = PG_GETARG_ARRAYTYPE_P(1); ! int i; ! ! Assert(ARR_ELEMTYPE(key_array) == TEXTOID); ! Assert(ARR_ELEMTYPE(value_array) == TEXTOID); ! ! if (ARR_NDIM(key_array) != 1 || ARR_NDIM(value_array) != 1) ! ereport(ERROR, ! (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), ! errmsg("wrong number of array subscripts"))); ! ! if (ARR_DIMS(key_array)[0] != ARR_DIMS(value_array)[0] ! || ARR_LBOUND(key_array)[0] != ARR_LBOUND(value_array)[0]) ! ereport(ERROR, ! (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), ! errmsg("cannot construct hstore from arrays of differing bounds"))); ! ! deconstruct_array(key_array, ! TEXTOID, -1, false, 'i', ! &key_datums, &key_nulls, &key_count); ! deconstruct_array(value_array, ! TEXTOID, -1, false, 'i', ! &value_datums, &value_nulls, &value_count); ! ! Assert(key_count == value_count); ! ! pairs = palloc(key_count * sizeof(Pairs)); ! ! for (i = 0; i < key_count; ++i) ! { ! if (key_nulls[i]) ! ereport(ERROR, ! (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), ! errmsg("null value not allowed for hstore key"))); ! ! if (value_nulls[i]) ! { ! pairs[i].key = VARDATA_ANY(key_datums[i]); ! pairs[i].val = NULL; ! pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); ! pairs[i].vallen = 4; ! pairs[i].isnull = true; ! pairs[i].needfree = false; ! } else { ! pairs[i].key = VARDATA_ANY(key_datums[i]); ! pairs[i].val = VARDATA_ANY(value_datums[i]); ! pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); ! pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i])); ! pairs[i].isnull = false; ! pairs[i].needfree = false; } } ! key_count = hstoreUniquePairs(pairs, key_count, &buflen); ! ! out = hstorePairs(pairs, key_count, buflen); ! ! PG_RETURN_POINTER(out); ! } ! ! /* most of hstore_from_record is shamelessly swiped from record_out */ ! ! /* ! * structure to cache metadata needed for record I/O ! */ ! typedef struct ColumnIOData ! { ! Oid column_type; ! Oid typiofunc; ! Oid typioparam; ! FmgrInfo proc; ! } ColumnIOData; ! ! typedef struct RecordIOData ! { ! Oid record_type; ! int32 record_typmod; ! int ncolumns; ! ColumnIOData columns[1]; /* VARIABLE LENGTH ARRAY */ ! } RecordIOData; ! ! PG_FUNCTION_INFO_V1(hstore_from_record); ! Datum hstore_from_record(PG_FUNCTION_ARGS); ! Datum ! hstore_from_record(PG_FUNCTION_ARGS) ! { ! HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); ! int4 buflen; ! HStore *out; ! Pairs *pairs; ! Oid tupType; ! int32 tupTypmod; ! TupleDesc tupdesc; ! HeapTupleData tuple; ! RecordIOData *my_extra; ! int ncolumns; ! int i,j; ! Datum *values; ! bool *nulls; ! ! /* Extract type info from the tuple itself */ ! tupType = HeapTupleHeaderGetTypeId(rec); ! tupTypmod = HeapTupleHeaderGetTypMod(rec); ! tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); ! ncolumns = tupdesc->natts; ! ! /* Build a temporary HeapTuple control structure */ ! tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ! ItemPointerSetInvalid(&(tuple.t_self)); ! tuple.t_tableOid = InvalidOid; ! tuple.t_data = rec; ! ! /* ! * We arrange to look up the needed I/O info just once per series of ! * calls, assuming the record type doesn't change underneath us. ! */ ! my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; ! if (my_extra == NULL || ! my_extra->ncolumns != ncolumns) ! { ! fcinfo->flinfo->fn_extra = ! MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, ! sizeof(RecordIOData) - sizeof(ColumnIOData) ! + ncolumns * sizeof(ColumnIOData)); ! my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; ! my_extra->record_type = InvalidOid; ! my_extra->record_typmod = 0; ! } ! ! if (my_extra->record_type != tupType || ! my_extra->record_typmod != tupTypmod) ! { ! MemSet(my_extra, 0, ! sizeof(RecordIOData) - sizeof(ColumnIOData) ! + ncolumns * sizeof(ColumnIOData)); ! my_extra->record_type = tupType; ! my_extra->record_typmod = tupTypmod; ! my_extra->ncolumns = ncolumns; ! } ! ! values = (Datum *) palloc(ncolumns * sizeof(Datum)); ! nulls = (bool *) palloc(ncolumns * sizeof(bool)); ! pairs = palloc(ncolumns * sizeof(Pairs)); ! ! /* Break down the tuple into fields */ ! heap_deform_tuple(&tuple, tupdesc, values, nulls); ! ! for (i = 0, j = 0; i < ncolumns; ++i) ! { ! ColumnIOData *column_info = &my_extra->columns[i]; ! Oid column_type = tupdesc->attrs[i]->atttypid; ! char *value; ! ! /* Ignore dropped columns in datatype */ ! if (tupdesc->attrs[i]->attisdropped) ! continue; ! ! pairs[j].key = NameStr(tupdesc->attrs[i]->attname); ! pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(tupdesc->attrs[i]->attname))); ! ! if (nulls[i]) ! { ! pairs[j].val = NULL; ! pairs[j].vallen = 4; ! pairs[j].isnull = true; ! pairs[j].needfree = false; ! ++j; ! continue; ! } ! ! /* ! * Convert the column value to text ! */ ! if (column_info->column_type != column_type) ! { ! bool typIsVarlena; ! ! getTypeOutputInfo(column_type, ! &column_info->typiofunc, ! &typIsVarlena); ! fmgr_info_cxt(column_info->typiofunc, &column_info->proc, ! fcinfo->flinfo->fn_mcxt); ! column_info->column_type = column_type; ! } ! ! value = OutputFunctionCall(&column_info->proc, values[i]); ! ! pairs[j].val = value; ! pairs[j].vallen = hstoreCheckValLen(strlen(value)); ! pairs[j].isnull = false; ! pairs[j].needfree = false; ! ++j; ! } ! ! ncolumns = hstoreUniquePairs(pairs, j, &buflen); ! ! out = hstorePairs(pairs, ncolumns, buflen); ! ! ReleaseTupleDesc(tupdesc); ! PG_RETURN_POINTER(out); } + + PG_FUNCTION_INFO_V1(hstore_populate_record); + Datum hstore_populate_record(PG_FUNCTION_ARGS); + Datum + hstore_populate_record(PG_FUNCTION_ARGS) + { + Oid argtype = get_fn_expr_argtype(fcinfo->flinfo,0); + HStore *hs; + HEntry *entries; + char *ptr; + HeapTupleHeader rec; + Oid tupType; + int32 tupTypmod; + TupleDesc tupdesc; + HeapTupleData tuple; + HeapTuple rettuple; + RecordIOData *my_extra; + int ncolumns; + int i; + Datum *values; + bool *nulls; + + if (!type_is_rowtype(argtype)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument must be a rowtype"))); + + if (PG_ARGISNULL(0)) + { + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); + rec = NULL; + + /* have no tuple to look at, so the only source of type info + * is the argtype. The lookup_rowtype_tupdesc call below will + * error out if we don't have a known composite type oid here. + */ + tupType = argtype; + tupTypmod = -1; + } + else + { + rec = PG_GETARG_HEAPTUPLEHEADER(0); + + /* Extract type info from the tuple itself */ + tupType = HeapTupleHeaderGetTypeId(rec); + tupTypmod = HeapTupleHeaderGetTypMod(rec); + } + + if (PG_ARGISNULL(1)) + PG_RETURN_POINTER(rec); + + hs = PG_GETARG_HS(1); + entries = ARRPTR(hs); + ptr = STRPTR(hs); + + if (hs->size == 0) + PG_RETURN_POINTER(rec); + + tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + ncolumns = tupdesc->natts; + + if (rec) + { + /* Build a temporary HeapTuple control structure */ + tuple.t_len = HeapTupleHeaderGetDatumLength(rec); + ItemPointerSetInvalid(&(tuple.t_self)); + tuple.t_tableOid = InvalidOid; + tuple.t_data = rec; + } + + /* + * We arrange to look up the needed I/O info just once per series of + * calls, assuming the record type doesn't change underneath us. + */ + my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; + if (my_extra == NULL || + my_extra->ncolumns != ncolumns) + { + fcinfo->flinfo->fn_extra = + MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(RecordIOData) - sizeof(ColumnIOData) + + ncolumns * sizeof(ColumnIOData)); + my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; + my_extra->record_type = InvalidOid; + my_extra->record_typmod = 0; + } + + if (my_extra->record_type != tupType || + my_extra->record_typmod != tupTypmod) + { + MemSet(my_extra, 0, + sizeof(RecordIOData) - sizeof(ColumnIOData) + + ncolumns * sizeof(ColumnIOData)); + my_extra->record_type = tupType; + my_extra->record_typmod = tupTypmod; + my_extra->ncolumns = ncolumns; + } + + values = (Datum *) palloc(ncolumns * sizeof(Datum)); + nulls = (bool *) palloc(ncolumns * sizeof(bool)); + + if (rec) + { + /* Break down the tuple into fields */ + heap_deform_tuple(&tuple, tupdesc, values, nulls); + } + else + { + for (i = 0; i < ncolumns; ++i) + { + values[i] = (Datum) 0; + nulls[i] = true; + } + } + + for (i = 0; i < ncolumns; ++i) + { + ColumnIOData *column_info = &my_extra->columns[i]; + Oid column_type = tupdesc->attrs[i]->atttypid; + char *value; + int idx; + int vallen; + + /* Ignore dropped columns in datatype */ + if (tupdesc->attrs[i]->attisdropped) + { + nulls[i] = true; + continue; + } + + idx = hstoreFindKey(hs, 0, + NameStr(tupdesc->attrs[i]->attname), + strlen(NameStr(tupdesc->attrs[i]->attname))); + if (idx < 0) + continue; + + if (HS_VALISNULL(entries,idx)) + { + nulls[i] = true; + continue; + } + + /* + * Prepare to convert the column value from text + */ + if (column_info->column_type != column_type) + { + getTypeInputInfo(column_type, + &column_info->typiofunc, + &column_info->typioparam); + fmgr_info_cxt(column_info->typiofunc, &column_info->proc, + fcinfo->flinfo->fn_mcxt); + column_info->column_type = column_type; + } + + vallen = HS_VALLEN(entries,idx); + value = palloc(1 + vallen); + memcpy(value, HS_VAL(entries,ptr,idx), vallen); + value[vallen] = 0; + + values[i] = InputFunctionCall(&column_info->proc, value, + column_info->typioparam, + tupdesc->attrs[i]->atttypmod); + nulls[i] = false; + } + + rettuple = heap_form_tuple(tupdesc, values, nulls); + + ReleaseTupleDesc(tupdesc); + + PG_RETURN_DATUM(HeapTupleGetDatum(rettuple)); + } + + static char * cpw(char *dst, char *src, int len) { *************** *** 447,452 **** --- 954,960 ---- HStore *in = PG_GETARG_HS(0); int buflen, i; + int count = in->size; char *out, *ptr; char *base = STRPTR(in); *************** *** 456,477 **** { out = palloc(1); *out = '\0'; - PG_FREE_IF_COPY(in, 0); PG_RETURN_CSTRING(out); } ! buflen = (4 /* " */ + 2 /* => */ + 2 /* , */ ) * in->size + ! 2 /* esc */ * (VARSIZE(in) - CALCDATASIZE(in->size, 0)); out = ptr = palloc(buflen); ! for (i = 0; i < in->size; i++) { *ptr++ = '"'; ! ptr = cpw(ptr, base + entries[i].pos, entries[i].keylen); *ptr++ = '"'; *ptr++ = '='; *ptr++ = '>'; ! if (entries[i].valisnull) { *ptr++ = 'N'; *ptr++ = 'U'; --- 964,1001 ---- { out = palloc(1); *out = '\0'; PG_RETURN_CSTRING(out); } ! buflen = 0; ! ! /* this loop overestimates due to pessimistic assumptions about ! * escaping, so very large hstore values can't be output. this ! * could be fixed, but many other data types probably have the ! * same issue. This replaced code that used the original varlena ! * size for calculations, which was wrong in some subtle ways. ! */ ! ! for (i = 0; i < count; i++) ! { ! /* include "" and => and comma-space */ ! buflen += 6 + 2 * HS_KEYLEN(entries,i); ! /* include "" only if nonnull */ ! buflen += 2 + (HS_VALISNULL(entries,i) ! ? 2 ! : 2 * HS_VALLEN(entries,i)); ! } out = ptr = palloc(buflen); ! ! for (i = 0; i < count; i++) { *ptr++ = '"'; ! ptr = cpw(ptr, HS_KEY(entries,base,i), HS_KEYLEN(entries,i)); *ptr++ = '"'; *ptr++ = '='; *ptr++ = '>'; ! if (HS_VALISNULL(entries,i)) { *ptr++ = 'N'; *ptr++ = 'U'; *************** *** 481,487 **** else { *ptr++ = '"'; ! ptr = cpw(ptr, base + entries[i].pos + entries[i].keylen, entries[i].vallen); *ptr++ = '"'; } --- 1005,1011 ---- else { *ptr++ = '"'; ! ptr = cpw(ptr, HS_VAL(entries,base,i), HS_VALLEN(entries,i)); *ptr++ = '"'; } *************** *** 493,498 **** } *ptr = '\0'; - PG_FREE_IF_COPY(in, 0); PG_RETURN_CSTRING(out); } --- 1017,1058 ---- } *ptr = '\0'; PG_RETURN_CSTRING(out); } + + + PG_FUNCTION_INFO_V1(hstore_send); + Datum hstore_send(PG_FUNCTION_ARGS); + Datum + hstore_send(PG_FUNCTION_ARGS) + { + HStore *in = PG_GETARG_HS(0); + int i; + int count = in->size; + char *base = STRPTR(in); + HEntry *entries = ARRPTR(in); + StringInfoData buf; + + pq_begintypsend(&buf); + + pq_sendint(&buf, count, 4); + + for (i = 0; i < count; i++) + { + int32 keylen = HS_KEYLEN(entries,i); + pq_sendint(&buf, keylen, 4); + pq_sendtext(&buf, HS_KEY(entries,base,i), keylen); + if (HS_VALISNULL(entries,i)) + { + pq_sendint(&buf, -1, 4); + } + else + { + int32 vallen = HS_VALLEN(entries,i); + pq_sendint(&buf, vallen, 4); + pq_sendtext(&buf, HS_VAL(entries,base,i), vallen); + } + } + + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); + } Index: contrib/hstore/hstore_op.c =================================================================== RCS file: /projects/cvsroot/pgsql/contrib/hstore/hstore_op.c,v retrieving revision 1.12 diff -c -r1.12 hstore_op.c *** contrib/hstore/hstore_op.c 15 Mar 2009 22:05:17 -0000 1.12 --- contrib/hstore/hstore_op.c 24 Mar 2009 15:14:20 -0000 *************** *** 1,498 **** /* ! * $PostgreSQL */ #include "postgres.h" #include "catalog/pg_type.h" #include "funcapi.h" - #include "utils/array.h" #include "utils/builtins.h" #include "hstore.h" ! static HEntry * ! findkey(HStore * hs, char *key, int keylen) { ! HEntry *StopLow = ARRPTR(hs); ! HEntry *StopHigh = StopLow + hs->size; ! HEntry *StopMiddle; ! int difference; char *base = STRPTR(hs); ! while (StopLow < StopHigh) { ! StopMiddle = StopLow + (StopHigh - StopLow) / 2; ! if (StopMiddle->keylen == keylen) ! difference = strncmp(base + StopMiddle->pos, key, StopMiddle->keylen); else ! difference = (StopMiddle->keylen > keylen) ? 1 : -1; if (difference == 0) ! return StopMiddle; else if (difference < 0) ! StopLow = StopMiddle + 1; else ! StopHigh = StopMiddle; } ! return NULL; } ! PG_FUNCTION_INFO_V1(fetchval); ! Datum fetchval(PG_FUNCTION_ARGS); Datum ! fetchval(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); ! text *key = PG_GETARG_TEXT_P(1); ! HEntry *entry; text *out; ! if ((entry = findkey(hs, VARDATA(key), VARSIZE(key) - VARHDRSZ)) == NULL || entry->valisnull) ! { ! PG_FREE_IF_COPY(hs, 0); ! PG_FREE_IF_COPY(key, 1); PG_RETURN_NULL(); - } ! out = cstring_to_text_with_len(STRPTR(hs) + entry->pos + entry->keylen, ! entry->vallen); - PG_FREE_IF_COPY(hs, 0); - PG_FREE_IF_COPY(key, 1); PG_RETURN_TEXT_P(out); } ! PG_FUNCTION_INFO_V1(exists); ! Datum exists(PG_FUNCTION_ARGS); Datum ! exists(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); ! text *key = PG_GETARG_TEXT_P(1); ! HEntry *entry; ! entry = findkey(hs, VARDATA(key), VARSIZE(key) - VARHDRSZ); ! PG_FREE_IF_COPY(hs, 0); ! PG_FREE_IF_COPY(key, 1); ! PG_RETURN_BOOL(entry); } ! PG_FUNCTION_INFO_V1(defined); ! Datum defined(PG_FUNCTION_ARGS); Datum ! defined(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); ! text *key = PG_GETARG_TEXT_P(1); ! HEntry *entry; ! bool res; ! entry = findkey(hs, VARDATA(key), VARSIZE(key) - VARHDRSZ); - res = (entry && !entry->valisnull) ? true : false; ! PG_FREE_IF_COPY(hs, 0); ! PG_FREE_IF_COPY(key, 1); PG_RETURN_BOOL(res); } ! PG_FUNCTION_INFO_V1(delete); ! Datum delete(PG_FUNCTION_ARGS); Datum ! delete(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); ! text *key = PG_GETARG_TEXT_P(1); HStore *out = palloc(VARSIZE(hs)); ! char *ptrs, *ptrd; HEntry *es, *ed; SET_VARSIZE(out, VARSIZE(hs)); out->size = hs->size; /* temporary! */ ! ptrs = STRPTR(hs); es = ARRPTR(hs); ! ptrd = STRPTR(out); ed = ARRPTR(out); ! while (es - ARRPTR(hs) < hs->size) { ! if (!(es->keylen == VARSIZE(key) - VARHDRSZ && strncmp(ptrs, VARDATA(key), es->keylen) == 0)) { ! memcpy(ed, es, sizeof(HEntry)); ! memcpy(ptrd, ptrs, es->keylen + ((es->valisnull) ? 0 : es->vallen)); ! ed->pos = ptrd - STRPTR(out); ! ptrd += es->keylen + ((es->valisnull) ? 0 : es->vallen); ! ed++; } - ptrs += es->keylen + ((es->valisnull) ? 0 : es->vallen); - es++; } ! if (ed - ARRPTR(out) != out->size) { ! int buflen = ptrd - STRPTR(out); ! ptrd = STRPTR(out); ! out->size = ed - ARRPTR(out); ! memmove(STRPTR(out), ptrd, buflen); ! SET_VARSIZE(out, CALCDATASIZE(out->size, buflen)); } ! PG_FREE_IF_COPY(hs, 0); ! PG_FREE_IF_COPY(key, 1); PG_RETURN_POINTER(out); } ! PG_FUNCTION_INFO_V1(hs_concat); ! Datum hs_concat(PG_FUNCTION_ARGS); Datum ! hs_concat(PG_FUNCTION_ARGS) { HStore *s1 = PG_GETARG_HS(0); HStore *s2 = PG_GETARG_HS(1); HStore *out = palloc(VARSIZE(s1) + VARSIZE(s2)); char *ps1, *ps2, *pd; HEntry *es1, *es2, *ed; SET_VARSIZE(out, VARSIZE(s1) + VARSIZE(s2)); ! out->size = s1->size + s2->size; ps1 = STRPTR(s1); ps2 = STRPTR(s2); ! pd = STRPTR(out); es1 = ARRPTR(s1); es2 = ARRPTR(s2); ed = ARRPTR(out); ! while (es1 - ARRPTR(s1) < s1->size && es2 - ARRPTR(s2) < s2->size) ! { ! int difference; ! ! if (es1->keylen == es2->keylen) ! difference = strncmp(ps1, ps2, es1->keylen); else - difference = (es1->keylen > es2->keylen) ? 1 : -1; - - if (difference == 0) { ! memcpy(ed, es2, sizeof(HEntry)); ! memcpy(pd, ps2, es2->keylen + ((es2->valisnull) ? 0 : es2->vallen)); ! ed->pos = pd - STRPTR(out); ! pd += es2->keylen + ((es2->valisnull) ? 0 : es2->vallen); ! ed++; ! ! ps1 += es1->keylen + ((es1->valisnull) ? 0 : es1->vallen); ! es1++; ! ps2 += es2->keylen + ((es2->valisnull) ? 0 : es2->vallen); ! es2++; } - else if (difference > 0) - { - memcpy(ed, es2, sizeof(HEntry)); - memcpy(pd, ps2, es2->keylen + ((es2->valisnull) ? 0 : es2->vallen)); - ed->pos = pd - STRPTR(out); - pd += es2->keylen + ((es2->valisnull) ? 0 : es2->vallen); - ed++; ! ps2 += es2->keylen + ((es2->valisnull) ? 0 : es2->vallen); ! es2++; } else { ! memcpy(ed, es1, sizeof(HEntry)); ! memcpy(pd, ps1, es1->keylen + ((es1->valisnull) ? 0 : es1->vallen)); ! ed->pos = pd - STRPTR(out); ! pd += es1->keylen + ((es1->valisnull) ? 0 : es1->vallen); ! ed++; ! ! ps1 += es1->keylen + ((es1->valisnull) ? 0 : es1->vallen); ! es1++; } } ! while (es1 - ARRPTR(s1) < s1->size) ! { ! memcpy(ed, es1, sizeof(HEntry)); ! memcpy(pd, ps1, es1->keylen + ((es1->valisnull) ? 0 : es1->vallen)); ! ed->pos = pd - STRPTR(out); ! pd += es1->keylen + ((es1->valisnull) ? 0 : es1->vallen); ! ed++; ! ps1 += es1->keylen + ((es1->valisnull) ? 0 : es1->vallen); ! es1++; ! } - while (es2 - ARRPTR(s2) < s2->size) - { - memcpy(ed, es2, sizeof(HEntry)); - memcpy(pd, ps2, es2->keylen + ((es2->valisnull) ? 0 : es2->vallen)); - ed->pos = pd - STRPTR(out); - pd += es2->keylen + ((es2->valisnull) ? 0 : es2->vallen); - ed++; ! ps2 += es2->keylen + ((es2->valisnull) ? 0 : es2->vallen); ! es2++; ! } ! if (ed - ARRPTR(out) != out->size) { ! int buflen = pd - STRPTR(out); ! pd = STRPTR(out); ! out->size = ed - ARRPTR(out); ! memmove(STRPTR(out), pd, buflen); ! SET_VARSIZE(out, CALCDATASIZE(out->size, buflen)); } ! PG_FREE_IF_COPY(s1, 0); ! PG_FREE_IF_COPY(s2, 1); ! PG_RETURN_POINTER(out); } ! PG_FUNCTION_INFO_V1(tconvert); ! Datum tconvert(PG_FUNCTION_ARGS); Datum ! tconvert(PG_FUNCTION_ARGS) { ! text *key; ! text *val = NULL; ! int len; ! HStore *out; ! ! if (PG_ARGISNULL(0)) ! PG_RETURN_NULL(); ! ! key = PG_GETARG_TEXT_P(0); ! if (PG_ARGISNULL(1)) ! len = CALCDATASIZE(1, VARSIZE(key)); ! else { ! val = PG_GETARG_TEXT_P(1); ! len = CALCDATASIZE(1, VARSIZE(key) + VARSIZE(val) - 2 * VARHDRSZ); ! } ! out = palloc(len); ! SET_VARSIZE(out, len); ! out->size = 1; ! ARRPTR(out)->keylen = hstoreCheckKeyLen(VARSIZE(key) - VARHDRSZ); ! if (PG_ARGISNULL(1)) ! { ! ARRPTR(out)->vallen = 0; ! ARRPTR(out)->valisnull = true; ! } ! else ! { ! ARRPTR(out)->vallen = hstoreCheckValLen(VARSIZE(val) - VARHDRSZ); ! ARRPTR(out)->valisnull = false; } - ARRPTR(out)->pos = 0; ! memcpy(STRPTR(out), VARDATA(key), ARRPTR(out)->keylen); ! if (!PG_ARGISNULL(1)) ! { ! memcpy(STRPTR(out) + ARRPTR(out)->keylen, VARDATA(val), ARRPTR(out)->vallen); ! PG_FREE_IF_COPY(val, 1); ! } ! PG_FREE_IF_COPY(key, 0); PG_RETURN_POINTER(out); } ! PG_FUNCTION_INFO_V1(akeys); ! Datum akeys(PG_FUNCTION_ARGS); Datum ! akeys(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); Datum *d; ArrayType *a; ! HEntry *ptr = ARRPTR(hs); char *base = STRPTR(hs); ! d = (Datum *) palloc(sizeof(Datum) * (hs->size + 1)); ! while (ptr - ARRPTR(hs) < hs->size) { ! text *item; ! ! item = cstring_to_text_with_len(base + ptr->pos, ptr->keylen); ! d[ptr - ARRPTR(hs)] = PointerGetDatum(item); ! ptr++; } ! a = construct_array(d, ! hs->size, ! TEXTOID, ! -1, ! false, ! 'i'); ! ptr = ARRPTR(hs); ! while (ptr - ARRPTR(hs) < hs->size) { ! pfree(DatumGetPointer(d[ptr - ARRPTR(hs)])); ! ptr++; } ! pfree(d); ! PG_FREE_IF_COPY(hs, 0); PG_RETURN_POINTER(a); } ! PG_FUNCTION_INFO_V1(avals); ! Datum avals(PG_FUNCTION_ARGS); Datum ! avals(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); Datum *d; ArrayType *a; ! HEntry *ptr = ARRPTR(hs); char *base = STRPTR(hs); ! d = (Datum *) palloc(sizeof(Datum) * (hs->size + 1)); ! while (ptr - ARRPTR(hs) < hs->size) { ! text *item; ! ! item = cstring_to_text_with_len(base + ptr->pos + ptr->keylen, ! (ptr->valisnull) ? 0 : ptr->vallen); ! d[ptr - ARRPTR(hs)] = PointerGetDatum(item); ! ptr++; } ! a = construct_array(d, ! hs->size, ! TEXTOID, ! -1, ! false, ! 'i'); ! ptr = ARRPTR(hs); ! while (ptr - ARRPTR(hs) < hs->size) { ! pfree(DatumGetPointer(d[ptr - ARRPTR(hs)])); ! ptr++; } ! pfree(d); ! PG_FREE_IF_COPY(hs, 0); PG_RETURN_POINTER(a); } ! typedef struct ! { ! HStore *hs; ! int i; ! } AKStore; static void ! setup_firstcall(FuncCallContext *funcctx, HStore * hs) { MemoryContext oldcontext; ! AKStore *st; oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); ! st = (AKStore *) palloc(sizeof(AKStore)); ! st->i = 0; ! st->hs = (HStore *) palloc(VARSIZE(hs)); ! memcpy(st->hs, hs, VARSIZE(hs)); funcctx->user_fctx = (void *) st; MemoryContextSwitchTo(oldcontext); } ! PG_FUNCTION_INFO_V1(skeys); ! Datum skeys(PG_FUNCTION_ARGS); Datum ! skeys(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; ! AKStore *st; if (SRF_IS_FIRSTCALL()) { ! HStore *hs = PG_GETARG_HS(0); ! funcctx = SRF_FIRSTCALL_INIT(); ! setup_firstcall(funcctx, hs); ! PG_FREE_IF_COPY(hs, 0); } funcctx = SRF_PERCALL_SETUP(); ! st = (AKStore *) funcctx->user_fctx; ! if (st->i < st->hs->size) { ! HEntry *ptr = &(ARRPTR(st->hs)[st->i]); text *item; ! item = cstring_to_text_with_len(STRPTR(st->hs) + ptr->pos, ! ptr->keylen); ! st->i++; SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); } - pfree(st->hs); - pfree(st); - SRF_RETURN_DONE(funcctx); } ! PG_FUNCTION_INFO_V1(svals); ! Datum svals(PG_FUNCTION_ARGS); Datum ! svals(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; ! AKStore *st; if (SRF_IS_FIRSTCALL()) { ! HStore *hs = PG_GETARG_HS(0); ! funcctx = SRF_FIRSTCALL_INIT(); ! setup_firstcall(funcctx, hs); ! PG_FREE_IF_COPY(hs, 0); } funcctx = SRF_PERCALL_SETUP(); ! st = (AKStore *) funcctx->user_fctx; ! if (st->i < st->hs->size) { ! HEntry *ptr = &(ARRPTR(st->hs)[st->i]); ! if (ptr->valisnull) { ReturnSetInfo *rsi; ! st->i++; (funcctx)->call_cntr++; rsi = (ReturnSetInfo *) fcinfo->resultinfo; rsi->isDone = ExprMultipleResult; --- 1,832 ---- /* ! * $PostgreSQL$ */ #include "postgres.h" #include "catalog/pg_type.h" #include "funcapi.h" #include "utils/builtins.h" + #include "access/hash.h" #include "hstore.h" + #if HSTORE_POLLUTE_NAMESPACE + HSTORE_POLLUTE(hstore_fetchval,fetchval) + HSTORE_POLLUTE(hstore_exists,exists) + HSTORE_POLLUTE(hstore_defined,defined) + HSTORE_POLLUTE(hstore_delete,delete) + HSTORE_POLLUTE(hstore_concat,hs_concat) + HSTORE_POLLUTE(hstore_contains,hs_contains) + HSTORE_POLLUTE(hstore_contained,hs_contained) + HSTORE_POLLUTE(hstore_akeys,akeys) + HSTORE_POLLUTE(hstore_avals,avals) + HSTORE_POLLUTE(hstore_skeys,skeys) + HSTORE_POLLUTE(hstore_svals,svals) + HSTORE_POLLUTE(hstore_each,each) + #endif + + /* we're often finding a sequence of keys in ascending order. The + * "lowbound" parameter is used to cache lower bounds of searches + * between calls, based on this assumption. Pass NULL for it for + * one-off or unordered searches. + */ ! int ! hstoreFindKey(HStore * hs, int *lowbound, char *key, int keylen) { ! HEntry *entries = ARRPTR(hs); ! int stopLow = lowbound ? *lowbound : 0; ! int stopHigh = hs->size; ! int stopMiddle; char *base = STRPTR(hs); ! while (stopLow < stopHigh) { ! int difference; ! ! stopMiddle = stopLow + (stopHigh - stopLow) / 2; ! if (HS_KEYLEN(entries,stopMiddle) == keylen) ! difference = strncmp(HS_KEY(entries,base,stopMiddle), key, keylen); else ! difference = (HS_KEYLEN(entries,stopMiddle) > keylen) ? 1 : -1; if (difference == 0) ! { ! if (lowbound) ! *lowbound = stopMiddle + 1; ! return stopMiddle; ! } else if (difference < 0) ! stopLow = stopMiddle + 1; else ! stopHigh = stopMiddle; ! } ! ! if (lowbound) ! *lowbound = stopLow; ! return -1; ! } ! ! Pairs * ! hstoreArrayToPairs(ArrayType *a, int* npairs) ! { ! Datum *key_datums; ! bool *key_nulls; ! int key_count; ! Pairs *key_pairs; ! int bufsiz; ! int i,j; ! ! deconstruct_array(a, ! TEXTOID, -1, false, 'i', ! &key_datums, &key_nulls, &key_count); ! ! if (key_count == 0) ! { ! *npairs = 0; ! return NULL; ! } ! ! key_pairs = palloc(sizeof(Pairs) * key_count); ! ! for (i = 0, j = 0; i < key_count; ++i) ! { ! if (!key_nulls[i]) ! { ! key_pairs[j].key = VARDATA(key_datums[i]); ! key_pairs[j].keylen = VARSIZE(key_datums[i]) - VARHDRSZ; ! key_pairs[j].val = NULL; ! key_pairs[j].vallen = 0; ! key_pairs[j].needfree = 0; ! key_pairs[j].isnull = 1; ! ++j; ! } } ! *npairs = hstoreUniquePairs(key_pairs, j, &bufsiz); ! ! return key_pairs; } ! ! PG_FUNCTION_INFO_V1(hstore_fetchval); ! Datum hstore_fetchval(PG_FUNCTION_ARGS); Datum ! hstore_fetchval(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); ! text *key = PG_GETARG_TEXT_PP(1); ! HEntry *entries = ARRPTR(hs); text *out; + int idx = hstoreFindKey(hs, NULL, + VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); ! if (idx < 0 || HS_VALISNULL(entries,idx)) PG_RETURN_NULL(); ! out = cstring_to_text_with_len(HS_VAL(entries,STRPTR(hs),idx), ! HS_VALLEN(entries,idx)); PG_RETURN_TEXT_P(out); } ! ! PG_FUNCTION_INFO_V1(hstore_exists); ! Datum hstore_exists(PG_FUNCTION_ARGS); ! Datum ! hstore_exists(PG_FUNCTION_ARGS) ! { ! HStore *hs = PG_GETARG_HS(0); ! text *key = PG_GETARG_TEXT_PP(1); ! int idx = hstoreFindKey(hs, NULL, ! VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); ! ! PG_RETURN_BOOL(idx >= 0); ! } ! ! ! PG_FUNCTION_INFO_V1(hstore_exists_any); ! Datum hstore_exists_any(PG_FUNCTION_ARGS); Datum ! hstore_exists_any(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); ! ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); ! int nkeys; ! Pairs *key_pairs = hstoreArrayToPairs(keys, &nkeys); ! int i; ! int lowbound = 0; ! bool res = false; ! ! /* we exploit the fact that the pairs list is already sorted into ! * strictly increasing order to narrow the hstoreFindKey search; ! * each search can start one entry past the previous "found" ! * entry, or at the lower bound of the last search. ! */ ! for (i = 0; !res && i < nkeys; ++i) ! { ! int idx = hstoreFindKey(hs, &lowbound, ! key_pairs[i].key, key_pairs[i].keylen); ! if (idx >= 0) ! res = true; ! } ! PG_RETURN_BOOL(res); } ! ! PG_FUNCTION_INFO_V1(hstore_exists_all); ! Datum hstore_exists_all(PG_FUNCTION_ARGS); Datum ! hstore_exists_all(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); ! ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); ! int nkeys; ! Pairs *key_pairs = hstoreArrayToPairs(keys, &nkeys); ! int i; ! int lowbound = 0; ! bool res = nkeys ? true : false; ! ! /* we exploit the fact that the pairs list is already sorted into ! * strictly increasing order to narrow the hstoreFindKey search; ! * each search can start one entry past the previous "found" ! * entry, or at the lower bound of the last search. ! */ ! ! for (i = 0; res && i < nkeys; ++i) ! { ! int idx = hstoreFindKey(hs, &lowbound, ! key_pairs[i].key, key_pairs[i].keylen); ! if (idx < 0) ! res = false; ! } ! ! PG_RETURN_BOOL(res); ! } ! PG_FUNCTION_INFO_V1(hstore_defined); ! Datum hstore_defined(PG_FUNCTION_ARGS); ! Datum ! hstore_defined(PG_FUNCTION_ARGS) ! { ! HStore *hs = PG_GETARG_HS(0); ! text *key = PG_GETARG_TEXT_PP(1); ! HEntry *entries = ARRPTR(hs); ! int idx = hstoreFindKey(hs, NULL, ! VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); ! bool res = (idx >= 0 && !HS_VALISNULL(entries,idx)); PG_RETURN_BOOL(res); } ! ! PG_FUNCTION_INFO_V1(hstore_delete); ! Datum hstore_delete(PG_FUNCTION_ARGS); Datum ! hstore_delete(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); ! text *key = PG_GETARG_TEXT_PP(1); ! char *keyptr = VARDATA_ANY(key); ! int keylen = VARSIZE_ANY_EXHDR(key); HStore *out = palloc(VARSIZE(hs)); ! char *bufs, ! *bufd, *ptrd; HEntry *es, *ed; + int i; + int outcount = 0; SET_VARSIZE(out, VARSIZE(hs)); out->size = hs->size; /* temporary! */ ! bufs = STRPTR(hs); es = ARRPTR(hs); ! bufd = ptrd = STRPTR(out); ed = ARRPTR(out); ! for (i = 0; i < hs->size; ++i) { ! int len = HS_KEYLEN(es,i); ! char *ptrs = HS_KEY(es,bufs,i); ! ! if (!(len == keylen && strncmp(ptrs, keyptr, keylen) == 0)) { ! int vallen = HS_VALLEN(es,i); ! HS_COPYITEM(ed, bufd, ptrd, ptrs, len, vallen, HS_VALISNULL(es,i)); ! ++outcount; } } ! HS_FINALIZE(out,outcount,bufd,ptrd); ! ! PG_RETURN_POINTER(out); ! } ! ! ! PG_FUNCTION_INFO_V1(hstore_delete_array); ! Datum hstore_delete_array(PG_FUNCTION_ARGS); ! Datum ! hstore_delete_array(PG_FUNCTION_ARGS) ! { ! HStore *hs = PG_GETARG_HS(0); ! HStore *out = palloc(VARSIZE(hs)); ! int hs_count = hs->size; ! char *ps, ! *bufd, ! *pd; ! HEntry *es, ! *ed; ! int i,j; ! int outcount = 0; ! ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1); ! int nkeys; ! Pairs *key_pairs = hstoreArrayToPairs(key_array, &nkeys); ! ! SET_VARSIZE(out, VARSIZE(hs)); ! out->size = hs->size; /* temporary! */ ! ! ps = STRPTR(hs); ! es = ARRPTR(hs); ! bufd = pd = STRPTR(out); ! ed = ARRPTR(out); ! ! if (nkeys == 0) ! { ! /* return a copy of the input, unchanged */ ! memcpy(out, hs, VARSIZE(hs)); ! PG_RETURN_POINTER(out); ! } ! ! /* this is in effect a merge between hs and key_pairs, both of ! * which are already sorted by (keylen,key); we take keys from ! * hs only ! */ ! ! for (i = j = 0; i < hs_count; ) { ! int difference; ! ! if (j >= nkeys) ! difference = -1; ! else ! { ! int skeylen = HS_KEYLEN(es,i); ! if (skeylen == key_pairs[j].keylen) ! difference = strncmp(HS_KEY(es,ps,i), ! key_pairs[j].key, ! key_pairs[j].keylen); ! else ! difference = (skeylen > key_pairs[j].keylen) ? 1 : -1; ! } ! ! if (difference > 0) ! ++j; ! else if (difference == 0) ! ++i, ++j; ! else ! { ! HS_COPYITEM(ed, bufd, pd, ! HS_KEY(es,ps,i), HS_KEYLEN(es,i), ! HS_VALLEN(es,i), HS_VALISNULL(es,i)); ! ++outcount; ! ++i; ! } ! } ! ! HS_FINALIZE(out,outcount,bufd,pd); ! ! PG_RETURN_POINTER(out); ! } ! ! ! PG_FUNCTION_INFO_V1(hstore_delete_hstore); ! Datum hstore_delete_hstore(PG_FUNCTION_ARGS); ! Datum ! hstore_delete_hstore(PG_FUNCTION_ARGS) ! { ! HStore *hs = PG_GETARG_HS(0); ! HStore *hs2 = PG_GETARG_HS(1); ! HStore *out = palloc(VARSIZE(hs)); ! int hs_count = hs->size; ! int hs2_count = hs2->size; ! char *ps, ! *ps2, ! *bufd, ! *pd; ! HEntry *es, ! *es2, ! *ed; ! int i,j; ! int outcount = 0; ! SET_VARSIZE(out, VARSIZE(hs)); ! out->size = hs->size; /* temporary! */ ! ps = STRPTR(hs); ! es = ARRPTR(hs); ! ps2 = STRPTR(hs2); ! es2 = ARRPTR(hs2); ! bufd = pd = STRPTR(out); ! ed = ARRPTR(out); ! if (hs2_count == 0) ! { ! /* return a copy of the input, unchanged */ ! memcpy(out, hs, VARSIZE(hs)); ! PG_RETURN_POINTER(out); } + /* this is in effect a merge between hs and hs2, both of + * which are already sorted by (keylen,key); we take keys from + * hs only; for equal keys, we take the value from hs unless the + * values are equal + */ ! for (i = j = 0; i < hs_count; ) ! { ! int difference; ! ! if (j >= hs2_count) ! difference = -1; ! else ! { ! int skeylen = HS_KEYLEN(es,i); ! int s2keylen = HS_KEYLEN(es2,j); ! if (skeylen == s2keylen) ! difference = strncmp(HS_KEY(es,ps,i), ! HS_KEY(es2,ps2,j), ! skeylen); ! else ! difference = (skeylen > s2keylen) ? 1 : -1; ! } ! ! if (difference > 0) ! ++j; ! else if (difference == 0) ! { ! int svallen = HS_VALLEN(es,i); ! int snullval = HS_VALISNULL(es,i); ! if (snullval != HS_VALISNULL(es2,j) ! || (!snullval ! && (svallen != HS_VALLEN(es2,j) ! || strncmp(HS_VAL(es,ps,i), HS_VAL(es2,ps2,j), svallen) != 0))) ! { ! HS_COPYITEM(ed, bufd, pd, ! HS_KEY(es,ps,i), HS_KEYLEN(es,i), ! svallen, snullval); ! ++outcount; ! } ! ++i, ++j; ! } ! else ! { ! HS_COPYITEM(ed, bufd, pd, ! HS_KEY(es,ps,i), HS_KEYLEN(es,i), ! HS_VALLEN(es,i), HS_VALISNULL(es,i)); ! ++outcount; ! ++i; ! } ! } ! ! HS_FINALIZE(out,outcount,bufd,pd); PG_RETURN_POINTER(out); } ! ! PG_FUNCTION_INFO_V1(hstore_concat); ! Datum hstore_concat(PG_FUNCTION_ARGS); Datum ! hstore_concat(PG_FUNCTION_ARGS) { HStore *s1 = PG_GETARG_HS(0); HStore *s2 = PG_GETARG_HS(1); HStore *out = palloc(VARSIZE(s1) + VARSIZE(s2)); char *ps1, *ps2, + *bufd, *pd; HEntry *es1, *es2, *ed; + int s1idx; + int s2idx; + int s1count = s1->size; + int s2count = s2->size; + int outcount = 0; SET_VARSIZE(out, VARSIZE(s1) + VARSIZE(s2)); ! out->size = s1count + s2count; ps1 = STRPTR(s1); ps2 = STRPTR(s2); ! bufd = pd = STRPTR(out); es1 = ARRPTR(s1); es2 = ARRPTR(s2); ed = ARRPTR(out); ! /* this is in effect a merge between s1 and s2, both of which ! * are already sorted by (keylen,key); we take s2 for equal keys ! */ ! ! for (s1idx = s2idx = 0; s1idx < s1count || s2idx < s2count; ++outcount) ! { ! int difference; ! ! if (s1idx >= s1count) ! difference = 1; ! else if (s2idx >= s2count) ! difference = -1; else { ! int s1keylen = HS_KEYLEN(es1,s1idx); ! int s2keylen = HS_KEYLEN(es2,s2idx); ! if (s1keylen == s2keylen) ! difference = strncmp(HS_KEY(es1,ps1,s1idx), ! HS_KEY(es2,ps2,s2idx), ! s1keylen); ! else ! difference = (s1keylen > s2keylen) ? 1 : -1; } ! if (difference >= 0) ! { ! HS_COPYITEM(ed, bufd, pd, ! HS_KEY(es2,ps2,s2idx), HS_KEYLEN(es2,s2idx), ! HS_VALLEN(es2,s2idx), HS_VALISNULL(es2,s2idx)); ! ++s2idx; ! if (difference == 0) ! ++s1idx; } else { ! HS_COPYITEM(ed, bufd, pd, ! HS_KEY(es1,ps1,s1idx), HS_KEYLEN(es1,s1idx), ! HS_VALLEN(es1,s1idx), HS_VALISNULL(es1,s1idx)); ! ++s1idx; } } ! HS_FINALIZE(out,outcount,bufd,pd); ! PG_RETURN_POINTER(out); ! } ! PG_FUNCTION_INFO_V1(hstore_slice_to_array); ! Datum hstore_slice_to_array(PG_FUNCTION_ARGS); ! Datum ! hstore_slice_to_array(PG_FUNCTION_ARGS) ! { ! HStore *hs = PG_GETARG_HS(0); ! HEntry *entries = ARRPTR(hs); ! char *ptr = STRPTR(hs); ! ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1); ! ArrayType *aout; ! Datum *key_datums; ! bool *key_nulls; ! Datum *out_datums; ! bool *out_nulls; ! int key_count; ! int i; ! ! deconstruct_array(key_array, ! TEXTOID, -1, false, 'i', ! &key_datums, &key_nulls, &key_count); ! if (key_count == 0) { ! aout = construct_empty_array(TEXTOID); ! PG_RETURN_POINTER(aout); ! } ! ! out_datums = palloc(sizeof(Datum) * key_count); ! out_nulls = palloc(sizeof(bool) * key_count); ! for (i = 0; i < key_count; ++i) ! { ! text *key = (text*) DatumGetPointer(key_datums[i]); ! int idx; ! if (key_nulls[i]) ! idx = -1; ! else ! idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ); ! if (idx < 0 || HS_VALISNULL(entries,idx)) ! { ! out_nulls[i] = true; ! out_datums[i] = (Datum) 0; ! } ! else ! { ! out_datums[i] = PointerGetDatum( ! cstring_to_text_with_len(HS_VAL(entries,ptr,idx), ! HS_VALLEN(entries,idx))); ! out_nulls[i] = false; ! } } ! aout = construct_md_array(out_datums, out_nulls, ! ARR_NDIM(key_array), ! ARR_DIMS(key_array), ! ARR_LBOUND(key_array), ! TEXTOID, -1, false, 'i'); ! PG_RETURN_POINTER(aout); } ! ! PG_FUNCTION_INFO_V1(hstore_slice_to_hstore); ! Datum hstore_slice_to_hstore(PG_FUNCTION_ARGS); Datum ! hstore_slice_to_hstore(PG_FUNCTION_ARGS) { ! HStore *hs = PG_GETARG_HS(0); ! HEntry *entries = ARRPTR(hs); ! char *ptr = STRPTR(hs); ! ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1); ! HStore *out; ! int nkeys; ! Pairs *key_pairs = hstoreArrayToPairs(key_array, &nkeys); ! Pairs *out_pairs; ! int bufsiz; ! int lastidx = 0; ! int i; ! int out_count = 0; ! ! if (nkeys == 0) ! { ! out = hstorePairs(NULL, 0, 0); ! PG_RETURN_POINTER(out); ! } ! ! out_pairs = palloc(sizeof(Pairs) * nkeys); ! bufsiz = 0; ! ! /* we exploit the fact that the pairs list is already sorted into ! * strictly increasing order to narrow the hstoreFindKey search; ! * each search can start one entry past the previous "found" ! * entry, or at the lower bound of the last search. ! */ ! for (i = 0; i < nkeys; ++i) { ! int idx = hstoreFindKey(hs, &lastidx, ! key_pairs[i].key, key_pairs[i].keylen); ! if (idx >= 0) ! { ! out_pairs[out_count].key = key_pairs[i].key; ! bufsiz += (out_pairs[out_count].keylen = key_pairs[i].keylen); ! out_pairs[out_count].val = HS_VAL(entries,ptr,idx); ! bufsiz += (out_pairs[out_count].vallen = HS_VALLEN(entries,idx)); ! out_pairs[out_count].isnull = HS_VALISNULL(entries,idx); ! out_pairs[out_count].needfree = false; ! ++out_count; ! } } ! /* we don't use uniquePairs here because we know that the ! * pairs list is already sorted and uniq'ed. ! */ ! out = hstorePairs(out_pairs, out_count, bufsiz); PG_RETURN_POINTER(out); } ! ! PG_FUNCTION_INFO_V1(hstore_akeys); ! Datum hstore_akeys(PG_FUNCTION_ARGS); Datum ! hstore_akeys(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); Datum *d; ArrayType *a; ! HEntry *entries = ARRPTR(hs); char *base = STRPTR(hs); + int count = hs->size; + int i; ! if (count == 0) { ! a = construct_empty_array(TEXTOID); ! PG_RETURN_POINTER(a); } ! d = (Datum *) palloc(sizeof(Datum) * count); ! for (i = 0; i < count; ++i) { ! text *item = cstring_to_text_with_len(HS_KEY(entries,base,i), ! HS_KEYLEN(entries,i)); ! d[i] = PointerGetDatum(item); } ! a = construct_array(d, count, ! TEXTOID, -1, false, 'i'); PG_RETURN_POINTER(a); } ! ! PG_FUNCTION_INFO_V1(hstore_avals); ! Datum hstore_avals(PG_FUNCTION_ARGS); Datum ! hstore_avals(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); Datum *d; + bool *nulls; ArrayType *a; ! HEntry *entries = ARRPTR(hs); char *base = STRPTR(hs); + int count = hs->size; + int lb = 1; + int i; ! if (count == 0) { ! a = construct_empty_array(TEXTOID); ! PG_RETURN_POINTER(a); } ! d = (Datum *) palloc(sizeof(Datum) * count); ! nulls = (bool *) palloc(sizeof(bool) * count); ! for (i = 0; i < count; ++i) { ! if (HS_VALISNULL(entries,i)) ! { ! d[i] = (Datum) 0; ! nulls[i] = true; ! } ! else ! { ! text *item = cstring_to_text_with_len(HS_VAL(entries,base,i), ! HS_VALLEN(entries,i)); ! d[i] = PointerGetDatum(item); ! nulls[i] = false; ! } } ! a = construct_md_array(d, nulls, 1, &count, &lb, ! TEXTOID, -1, false, 'i'); PG_RETURN_POINTER(a); } ! /* Common initialization function for the various set-returning ! * funcs. fcinfo is only passed if the function is to return a ! * composite; it will be used to look up the return tupledesc. ! * we stash a copy of the hstore in the multi-call context in ! * case it was originally toasted. (At least I assume that's why; ! * there was no explanatory comment in the original code. --AG) ! */ static void ! setup_firstcall(FuncCallContext *funcctx, HStore * hs, ! FunctionCallInfoData *fcinfo) { MemoryContext oldcontext; ! HStore *st; oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); ! st = (HStore *) palloc(VARSIZE(hs)); ! memcpy(st, hs, VARSIZE(hs)); funcctx->user_fctx = (void *) st; + + if (fcinfo) + { + TupleDesc tupdesc; + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + funcctx->tuple_desc = BlessTupleDesc(tupdesc); + } + MemoryContextSwitchTo(oldcontext); } ! ! PG_FUNCTION_INFO_V1(hstore_skeys); ! Datum hstore_skeys(PG_FUNCTION_ARGS); Datum ! hstore_skeys(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; ! HStore *hs; ! int i; if (SRF_IS_FIRSTCALL()) { ! hs = PG_GETARG_HS(0); funcctx = SRF_FIRSTCALL_INIT(); ! setup_firstcall(funcctx, hs, NULL); } funcctx = SRF_PERCALL_SETUP(); ! hs = (HStore *) funcctx->user_fctx; ! i = funcctx->call_cntr; ! if (i < hs->size) { ! HEntry *entries = ARRPTR(hs); text *item; ! item = cstring_to_text_with_len(HS_KEY(entries,STRPTR(hs),i), ! HS_KEYLEN(entries,i)); SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); } SRF_RETURN_DONE(funcctx); } ! ! PG_FUNCTION_INFO_V1(hstore_svals); ! Datum hstore_svals(PG_FUNCTION_ARGS); Datum ! hstore_svals(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; ! HStore *hs; ! int i; if (SRF_IS_FIRSTCALL()) { ! hs = PG_GETARG_HS(0); funcctx = SRF_FIRSTCALL_INIT(); ! setup_firstcall(funcctx, hs, NULL); } funcctx = SRF_PERCALL_SETUP(); ! hs = (HStore *) funcctx->user_fctx; ! i = funcctx->call_cntr; ! if (i < hs->size) { ! HEntry *entries = ARRPTR(hs); ! if (HS_VALISNULL(entries,i)) { ReturnSetInfo *rsi; ! /* ugly ugly ugly. why no macro for this? */ (funcctx)->call_cntr++; rsi = (ReturnSetInfo *) fcinfo->resultinfo; rsi->isDone = ExprMultipleResult; *************** *** 502,645 **** { text *item; ! item = cstring_to_text_with_len(STRPTR(st->hs) + ptr->pos + ptr->keylen, ! ptr->vallen); ! st->i++; SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); } } - pfree(st->hs); - pfree(st); - SRF_RETURN_DONE(funcctx); } ! PG_FUNCTION_INFO_V1(hs_contains); ! Datum hs_contains(PG_FUNCTION_ARGS); Datum ! hs_contains(PG_FUNCTION_ARGS) { HStore *val = PG_GETARG_HS(0); HStore *tmpl = PG_GETARG_HS(1); bool res = true; HEntry *te = ARRPTR(tmpl); ! char *vv = STRPTR(val); ! char *tv = STRPTR(tmpl); ! while (res && te - ARRPTR(tmpl) < tmpl->size) { ! HEntry *entry = findkey(val, tv + te->pos, te->keylen); ! if (entry) { ! if (te->valisnull || entry->valisnull) ! { ! if (!(te->valisnull && entry->valisnull)) ! res = false; ! } ! else if (te->vallen != entry->vallen || ! strncmp(vv + entry->pos + entry->keylen, ! tv + te->pos + te->keylen, ! te->vallen)) res = false; } else res = false; - te++; } - PG_FREE_IF_COPY(val, 0); - PG_FREE_IF_COPY(tmpl, 1); - PG_RETURN_BOOL(res); } ! PG_FUNCTION_INFO_V1(hs_contained); ! Datum hs_contained(PG_FUNCTION_ARGS); Datum ! hs_contained(PG_FUNCTION_ARGS) { ! PG_RETURN_DATUM(DirectFunctionCall2( ! hs_contains, PG_GETARG_DATUM(1), PG_GETARG_DATUM(0) )); } ! PG_FUNCTION_INFO_V1(each); ! Datum each(PG_FUNCTION_ARGS); Datum ! each(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; ! AKStore *st; if (SRF_IS_FIRSTCALL()) { ! TupleDesc tupdesc; ! MemoryContext oldcontext; ! HStore *hs = PG_GETARG_HS(0); ! funcctx = SRF_FIRSTCALL_INIT(); ! oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); ! st = (AKStore *) palloc(sizeof(AKStore)); ! st->i = 0; ! st->hs = (HStore *) palloc(VARSIZE(hs)); ! memcpy(st->hs, hs, VARSIZE(hs)); ! funcctx->user_fctx = (void *) st; ! ! /* Build a tuple descriptor for our result type */ ! if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) ! elog(ERROR, "return type must be a row type"); ! ! funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); ! ! MemoryContextSwitchTo(oldcontext); ! PG_FREE_IF_COPY(hs, 0); } funcctx = SRF_PERCALL_SETUP(); ! st = (AKStore *) funcctx->user_fctx; ! if (st->i < st->hs->size) { ! HEntry *ptr = &(ARRPTR(st->hs)[st->i]); Datum res, dvalues[2]; bool nulls[2] = {false, false}; text *item; HeapTuple tuple; ! item = cstring_to_text_with_len(STRPTR(st->hs) + ptr->pos, ptr->keylen); dvalues[0] = PointerGetDatum(item); ! if (ptr->valisnull) { dvalues[1] = (Datum) 0; nulls[1] = true; } else { ! item = cstring_to_text_with_len(STRPTR(st->hs) + ptr->pos + ptr->keylen, ! ptr->vallen); dvalues[1] = PointerGetDatum(item); } - st->i++; ! tuple = heap_form_tuple(funcctx->attinmeta->tupdesc, dvalues, nulls); res = HeapTupleGetDatum(tuple); - pfree(DatumGetPointer(dvalues[0])); - if (!nulls[1]) - pfree(DatumGetPointer(dvalues[1])); - SRF_RETURN_NEXT(funcctx, PointerGetDatum(res)); } - pfree(st->hs); - pfree(st); - SRF_RETURN_DONE(funcctx); } --- 836,1135 ---- { text *item; ! item = cstring_to_text_with_len(HS_VAL(entries,STRPTR(hs),i), ! HS_VALLEN(entries,i)); SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); } } SRF_RETURN_DONE(funcctx); } ! ! PG_FUNCTION_INFO_V1(hstore_contains); ! Datum hstore_contains(PG_FUNCTION_ARGS); Datum ! hstore_contains(PG_FUNCTION_ARGS) { HStore *val = PG_GETARG_HS(0); HStore *tmpl = PG_GETARG_HS(1); bool res = true; HEntry *te = ARRPTR(tmpl); ! char *tstr = STRPTR(tmpl); ! HEntry *ve = ARRPTR(val); ! char *vstr = STRPTR(val); ! int tcount = tmpl->size; ! int lastidx = 0; ! int i; ! ! /* we exploit the fact that keys in "tmpl" are in strictly ! * increasing order to narrow the hstoreFindKey search; each search ! * can start one entry past the previous "found" entry, or at the ! * lower bound of the search ! */ ! for (i = 0; res && i < tcount; ++i) { ! int idx = hstoreFindKey(val, &lastidx, ! HS_KEY(te,tstr,i), HS_KEYLEN(te,i)); ! if (idx >= 0) { ! bool nullval = HS_VALISNULL(te,i); ! int vallen = HS_VALLEN(te,i); ! ! if (nullval != HS_VALISNULL(ve,idx) ! || (!nullval ! && (vallen != HS_VALLEN(ve,idx) ! || strncmp(HS_VAL(te,tstr,i), HS_VAL(ve,vstr,idx), vallen)))) res = false; } else res = false; } PG_RETURN_BOOL(res); } ! ! PG_FUNCTION_INFO_V1(hstore_contained); ! Datum hstore_contained(PG_FUNCTION_ARGS); Datum ! hstore_contained(PG_FUNCTION_ARGS) { ! PG_RETURN_DATUM(DirectFunctionCall2(hstore_contains, PG_GETARG_DATUM(1), PG_GETARG_DATUM(0) )); } ! ! PG_FUNCTION_INFO_V1(hstore_each); ! Datum hstore_each(PG_FUNCTION_ARGS); Datum ! hstore_each(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; ! HStore *hs; ! int i; if (SRF_IS_FIRSTCALL()) { ! hs = PG_GETARG_HS(0); funcctx = SRF_FIRSTCALL_INIT(); ! setup_firstcall(funcctx, hs, fcinfo); } funcctx = SRF_PERCALL_SETUP(); ! hs = (HStore *) funcctx->user_fctx; ! i = funcctx->call_cntr; ! if (i < hs->size) { ! HEntry *entries = ARRPTR(hs); ! char *ptr = STRPTR(hs); Datum res, dvalues[2]; bool nulls[2] = {false, false}; text *item; HeapTuple tuple; ! item = cstring_to_text_with_len(HS_KEY(entries,ptr,i), ! HS_KEYLEN(entries,i)); dvalues[0] = PointerGetDatum(item); ! if (HS_VALISNULL(entries,i)) { dvalues[1] = (Datum) 0; nulls[1] = true; } else { ! item = cstring_to_text_with_len(HS_VAL(entries,ptr,i), ! HS_VALLEN(entries,i)); dvalues[1] = PointerGetDatum(item); } ! tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls); res = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, PointerGetDatum(res)); } SRF_RETURN_DONE(funcctx); } + + + /* btree sort order for hstores isn't intended to be useful; + * we compare the entire string buffer first, then the entry + * pos array. + */ + + PG_FUNCTION_INFO_V1(hstore_cmp); + Datum hstore_cmp(PG_FUNCTION_ARGS); + Datum + hstore_cmp(PG_FUNCTION_ARGS) + { + HStore *hs1 = PG_GETARG_HS(0); + HStore *hs2 = PG_GETARG_HS(1); + int hcount1 = hs1->size; + int hcount2 = hs2->size; + char *str1 = STRPTR(hs1); + char *str2 = STRPTR(hs2); + HEntry *ent1 = ARRPTR(hs1); + HEntry *ent2 = ARRPTR(hs2); + int res = 0; + + if (hcount1 == 0 || hcount2 == 0) + { + /* if either operand is empty, and the other is nonempty, the + * nonempty one is larger. If both are empty they are equal. + */ + + if (hcount1 > 0) + res = 1; + else if (hcount2 > 0) + res = -1; + } + else + { + /* here we know both operands are nonempty */ + size_t len1 = HSE_ENDPOS(ent1[2*hcount1 - 1]); + size_t len2 = HSE_ENDPOS(ent2[2*hcount2 - 1]); + + res = memcmp(str1, str2, Min(len1,len2)); + + if (res == 0) + { + if (len1 > len2) + res = 1; + else if (len1 < len2) + res = -1; + else if (hcount1 > hcount2) + res = 1; + else if (hcount2 > hcount1) + res = -1; + else + { + int count = hcount1 * 2; + int i; + for (i = 0; i < count; ++i) + if (HSE_ENDPOS(ent1[i]) != HSE_ENDPOS(ent2[i]) + || HSE_ISNULL(ent1[i]) != HSE_ISNULL(ent2[i])) + break; + if (i < count) + { + if (HSE_ENDPOS(ent1[i]) < HSE_ENDPOS(ent2[i])) + res = -1; + else if (HSE_ENDPOS(ent1[i]) > HSE_ENDPOS(ent2[i])) + res = 1; + else if (HSE_ISNULL(ent1[i])) + res = 1; + else if (HSE_ISNULL(ent2[i])) + res = -1; + } + } + } + else + { + res = (res > 0) ? 1 : -1; + } + } + + /* this is a btree support function; this is one of the few + * places where memory needs to be explicitly freed. + */ + PG_FREE_IF_COPY(hs1,0); + PG_FREE_IF_COPY(hs2,1); + PG_RETURN_INT32(res); + } + + + PG_FUNCTION_INFO_V1(hstore_eq); + Datum hstore_eq(PG_FUNCTION_ARGS); + Datum + hstore_eq(PG_FUNCTION_ARGS) + { + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + PG_RETURN_BOOL(res == 0); + } + + PG_FUNCTION_INFO_V1(hstore_ne); + Datum hstore_ne(PG_FUNCTION_ARGS); + Datum + hstore_ne(PG_FUNCTION_ARGS) + { + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + PG_RETURN_BOOL(res != 0); + } + + PG_FUNCTION_INFO_V1(hstore_gt); + Datum hstore_gt(PG_FUNCTION_ARGS); + Datum + hstore_gt(PG_FUNCTION_ARGS) + { + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + PG_RETURN_BOOL(res > 0); + } + + PG_FUNCTION_INFO_V1(hstore_ge); + Datum hstore_ge(PG_FUNCTION_ARGS); + Datum + hstore_ge(PG_FUNCTION_ARGS) + { + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + PG_RETURN_BOOL(res >= 0); + } + + PG_FUNCTION_INFO_V1(hstore_lt); + Datum hstore_lt(PG_FUNCTION_ARGS); + Datum + hstore_lt(PG_FUNCTION_ARGS) + { + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + PG_RETURN_BOOL(res < 0); + } + + PG_FUNCTION_INFO_V1(hstore_le); + Datum hstore_le(PG_FUNCTION_ARGS); + Datum + hstore_le(PG_FUNCTION_ARGS) + { + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + PG_RETURN_BOOL(res <= 0); + } + + + PG_FUNCTION_INFO_V1(hstore_hash); + Datum hstore_hash(PG_FUNCTION_ARGS); + Datum + hstore_hash(PG_FUNCTION_ARGS) + { + HStore *hs = PG_GETARG_HS(0); + Datum hval = hash_any((unsigned char *)VARDATA(hs), + VARSIZE(hs) - VARHDRSZ); + + /* this is the only place in the code that cares whether the + * overall varlena size exactly matches the true data size; + * this assertion should be maintained by all the other code, + * but we make it explicit here. + */ + Assert(VARSIZE(hs) + == CALCDATASIZE(hs->size, HSE_ENDPOS(ARRPTR(hs)[2*hs->size - 1]))); + + PG_FREE_IF_COPY(hs,0); + PG_RETURN_DATUM(hval); + } Index: contrib/hstore/uninstall_hstore.sql =================================================================== RCS file: /projects/cvsroot/pgsql/contrib/hstore/uninstall_hstore.sql,v retrieving revision 1.7 diff -c -r1.7 uninstall_hstore.sql *** contrib/hstore/uninstall_hstore.sql 14 Apr 2008 17:05:32 -0000 1.7 --- contrib/hstore/uninstall_hstore.sql 24 Mar 2009 15:14:20 -0000 *************** *** 5,36 **** DROP OPERATOR CLASS gist_hstore_ops USING gist CASCADE; DROP OPERATOR CLASS gin_hstore_ops USING gin CASCADE; ! DROP OPERATOR ? ( hstore, text ); ! DROP OPERATOR ->( hstore, text ); ! DROP OPERATOR ||( hstore, hstore ); ! DROP OPERATOR @>( hstore, hstore ); ! DROP OPERATOR <@( hstore, hstore ); ! DROP OPERATOR @( hstore, hstore ); ! DROP OPERATOR ~( hstore, hstore ); ! DROP OPERATOR =>( text, text ); ! DROP FUNCTION fetchval(hstore,text); DROP FUNCTION isexists(hstore,text); DROP FUNCTION exist(hstore,text); DROP FUNCTION isdefined(hstore,text); DROP FUNCTION defined(hstore,text); DROP FUNCTION delete(hstore,text); DROP FUNCTION hs_concat(hstore,hstore); DROP FUNCTION hs_contains(hstore,hstore); DROP FUNCTION hs_contained(hstore,hstore); DROP FUNCTION tconvert(text,text); DROP FUNCTION akeys(hstore); DROP FUNCTION avals(hstore); DROP FUNCTION skeys(hstore); DROP FUNCTION svals(hstore); DROP FUNCTION each(hstore); DROP FUNCTION ghstore_compress(internal); DROP FUNCTION ghstore_decompress(internal); DROP FUNCTION ghstore_penalty(internal,internal,internal); --- 5,70 ---- DROP OPERATOR CLASS gist_hstore_ops USING gist CASCADE; DROP OPERATOR CLASS gin_hstore_ops USING gin CASCADE; + DROP OPERATOR CLASS hash_hstore_ops USING hash CASCADE; + DROP OPERATOR CLASS btree_hstore_ops USING btree CASCADE; ! DROP OPERATOR - ( hstore, text ); ! DROP OPERATOR - ( hstore, text[] ); ! DROP OPERATOR - ( hstore, hstore ); ! DROP OPERATOR ? ( hstore, text ); ! DROP OPERATOR ?& ( hstore, text[] ); ! DROP OPERATOR ?| ( hstore, text[] ); ! DROP OPERATOR -> ( hstore, text ); ! DROP OPERATOR -> ( hstore, text[] ); ! DROP OPERATOR || ( hstore, hstore ); ! DROP OPERATOR @> ( hstore, hstore ); ! DROP OPERATOR <@ ( hstore, hstore ); ! DROP OPERATOR @ ( hstore, hstore ); ! DROP OPERATOR ~ ( hstore, hstore ); ! DROP OPERATOR => ( text, text ); ! DROP OPERATOR => ( text[], text[] ); ! DROP OPERATOR => ( hstore, text[] ); ! DROP OPERATOR #= ( anyelement, hstore ); ! DROP OPERATOR = ( hstore, hstore ); ! DROP OPERATOR <> ( hstore, hstore ); ! DROP OPERATOR #<# ( hstore, hstore ); ! DROP OPERATOR #<=# ( hstore, hstore ); ! DROP OPERATOR #># ( hstore, hstore ); ! DROP OPERATOR #>=# ( hstore, hstore ); + DROP FUNCTION hstore_eq(hstore,hstore); + DROP FUNCTION hstore_ne(hstore,hstore); + DROP FUNCTION hstore_gt(hstore,hstore); + DROP FUNCTION hstore_ge(hstore,hstore); + DROP FUNCTION hstore_lt(hstore,hstore); + DROP FUNCTION hstore_le(hstore,hstore); + DROP FUNCTION hstore_cmp(hstore,hstore); + DROP FUNCTION hstore_hash(hstore); + DROP FUNCTION slice_array(hstore,text[]); + DROP FUNCTION slice_hstore(hstore,text[]); + DROP FUNCTION hstore(text[],text[]); + DROP FUNCTION hstore(record); DROP FUNCTION fetchval(hstore,text); DROP FUNCTION isexists(hstore,text); DROP FUNCTION exist(hstore,text); + DROP FUNCTION exists_any(hstore,text[]); + DROP FUNCTION exists_all(hstore,text[]); DROP FUNCTION isdefined(hstore,text); DROP FUNCTION defined(hstore,text); DROP FUNCTION delete(hstore,text); + DROP FUNCTION delete(hstore,text[]); + DROP FUNCTION delete(hstore,hstore); DROP FUNCTION hs_concat(hstore,hstore); DROP FUNCTION hs_contains(hstore,hstore); DROP FUNCTION hs_contained(hstore,hstore); DROP FUNCTION tconvert(text,text); + DROP FUNCTION hstore(text,text); DROP FUNCTION akeys(hstore); DROP FUNCTION avals(hstore); DROP FUNCTION skeys(hstore); DROP FUNCTION svals(hstore); DROP FUNCTION each(hstore); + DROP FUNCTION populate_record(anyelement,hstore); DROP FUNCTION ghstore_compress(internal); DROP FUNCTION ghstore_decompress(internal); DROP FUNCTION ghstore_penalty(internal,internal,internal); Index: contrib/hstore/expected/hstore.out =================================================================== RCS file: /projects/cvsroot/pgsql/contrib/hstore/expected/hstore.out,v retrieving revision 1.4 diff -c -r1.4 hstore.out *** contrib/hstore/expected/hstore.out 14 Mar 2007 14:21:53 -0000 1.4 --- contrib/hstore/expected/hstore.out 24 Mar 2009 15:14:21 -0000 *************** *** 278,283 **** --- 278,308 ---- f (1 row) + -- -> array operator + select 'aa=>"NULL", c=>d , b=>16'::hstore -> ARRAY['aa','c']; + ?column? + ------------ + {"NULL",d} + (1 row) + + select 'aa=>"NULL", c=>d , b=>16'::hstore -> ARRAY['c','aa']; + ?column? + ------------ + {d,"NULL"} + (1 row) + + select 'aa=>NULL, c=>d , b=>16'::hstore -> ARRAY['aa','c',null]; + ?column? + --------------- + {NULL,d,NULL} + (1 row) + + select 'aa=>1, c=>3, b=>2, d=>4'::hstore -> ARRAY[['b','d'],['aa','c']]; + ?column? + --------------- + {{2,4},{1,3}} + (1 row) + -- exists/defined select exist('a=>NULL, b=>qq', 'a'); exist *************** *** 327,332 **** --- 352,441 ---- t (1 row) + select hstore 'a=>NULL, b=>qq' ? 'a'; + ?column? + ---------- + t + (1 row) + + select hstore 'a=>NULL, b=>qq' ? 'b'; + ?column? + ---------- + t + (1 row) + + select hstore 'a=>NULL, b=>qq' ? 'c'; + ?column? + ---------- + f + (1 row) + + select hstore 'a=>"NULL", b=>qq' ? 'a'; + ?column? + ---------- + t + (1 row) + + select hstore 'a=>NULL, b=>qq' ?| ARRAY['a','b']; + ?column? + ---------- + t + (1 row) + + select hstore 'a=>NULL, b=>qq' ?| ARRAY['b','a']; + ?column? + ---------- + t + (1 row) + + select hstore 'a=>NULL, b=>qq' ?| ARRAY['c','a']; + ?column? + ---------- + t + (1 row) + + select hstore 'a=>NULL, b=>qq' ?| ARRAY['c','d']; + ?column? + ---------- + f + (1 row) + + select hstore 'a=>NULL, b=>qq' ?| '{}'::text[]; + ?column? + ---------- + f + (1 row) + + select hstore 'a=>NULL, b=>qq' ?& ARRAY['a','b']; + ?column? + ---------- + t + (1 row) + + select hstore 'a=>NULL, b=>qq' ?& ARRAY['b','a']; + ?column? + ---------- + t + (1 row) + + select hstore 'a=>NULL, b=>qq' ?& ARRAY['c','a']; + ?column? + ---------- + f + (1 row) + + select hstore 'a=>NULL, b=>qq' ?& ARRAY['c','d']; + ?column? + ---------- + f + (1 row) + + select hstore 'a=>NULL, b=>qq' ?& '{}'::text[]; + ?column? + ---------- + f + (1 row) + -- delete select delete('a=>1 , b=>2, c=>3'::hstore, 'a'); delete *************** *** 358,363 **** --- 467,624 ---- "a"=>"1", "b"=>"2", "c"=>"3" (1 row) + select 'a=>1 , b=>2, c=>3'::hstore - 'a'::text; + ?column? + -------------------- + "b"=>"2", "c"=>"3" + (1 row) + + select 'a=>null , b=>2, c=>3'::hstore - 'a'::text; + ?column? + -------------------- + "b"=>"2", "c"=>"3" + (1 row) + + select 'a=>1 , b=>2, c=>3'::hstore - 'b'::text; + ?column? + -------------------- + "a"=>"1", "c"=>"3" + (1 row) + + select 'a=>1 , b=>2, c=>3'::hstore - 'c'::text; + ?column? + -------------------- + "a"=>"1", "b"=>"2" + (1 row) + + select 'a=>1 , b=>2, c=>3'::hstore - 'd'::text; + ?column? + ------------------------------ + "a"=>"1", "b"=>"2", "c"=>"3" + (1 row) + + -- delete (array) + select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','e']); + delete + ------------------------------ + "a"=>"1", "b"=>"2", "c"=>"3" + (1 row) + + select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','b']); + delete + -------------------- + "a"=>"1", "c"=>"3" + (1 row) + + select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['a','c']); + delete + ---------- + "b"=>"2" + (1 row) + + select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]); + delete + -------- + + (1 row) + + select delete('a=>1 , b=>2, c=>3'::hstore, '{}'::text[]); + delete + ------------------------------ + "a"=>"1", "b"=>"2", "c"=>"3" + (1 row) + + select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','e']; + ?column? + ------------------------------ + "a"=>"1", "b"=>"2", "c"=>"3" + (1 row) + + select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','b']; + ?column? + -------------------- + "a"=>"1", "c"=>"3" + (1 row) + + select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c']; + ?column? + ---------- + "b"=>"2" + (1 row) + + select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']]; + ?column? + ---------- + + (1 row) + + select 'a=>1 , b=>2, c=>3'::hstore - '{}'::text[]; + ?column? + ------------------------------ + "a"=>"1", "b"=>"2", "c"=>"3" + (1 row) + + -- delete (hstore) + select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>4, b=>2'::hstore); + delete + --------------------- + "c"=>"3", "aa"=>"1" + (1 row) + + select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>NULL, c=>3'::hstore); + delete + --------------------- + "b"=>"2", "aa"=>"1" + (1 row) + + select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore); + delete + -------- + + (1 row) + + select delete('aa=>1 , b=>2, c=>3'::hstore, 'b=>2'::hstore); + delete + --------------------- + "c"=>"3", "aa"=>"1" + (1 row) + + select delete('aa=>1 , b=>2, c=>3'::hstore, ''::hstore); + delete + ------------------------------- + "b"=>"2", "c"=>"3", "aa"=>"1" + (1 row) + + select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>4, b=>2'::hstore; + ?column? + --------------------- + "c"=>"3", "aa"=>"1" + (1 row) + + select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>NULL, c=>3'::hstore; + ?column? + --------------------- + "b"=>"2", "aa"=>"1" + (1 row) + + select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore; + ?column? + ---------- + + (1 row) + + select 'aa=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore; + ?column? + --------------------- + "c"=>"3", "aa"=>"1" + (1 row) + + select 'aa=>1 , b=>2, c=>3'::hstore - ''::hstore; + ?column? + ------------------------------- + "b"=>"2", "c"=>"3", "aa"=>"1" + (1 row) + -- || select 'aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f'; ?column? *************** *** 414,419 **** --- 675,813 ---- "a"=>"g", "b"=>NULL (1 row) + -- => arrays + select ARRAY['a','b','asd'] => ARRAY['g','h','i']; + ?column? + -------------------------------- + "a"=>"g", "b"=>"h", "asd"=>"i" + (1 row) + + select ARRAY['a','b','asd'] => ARRAY['g','h',NULL]; + ?column? + --------------------------------- + "a"=>"g", "b"=>"h", "asd"=>NULL + (1 row) + + select ARRAY['z','y','x'] => ARRAY['1','2','3']; + ?column? + ------------------------------ + "x"=>"3", "y"=>"2", "z"=>"1" + (1 row) + + select ARRAY['aaa','bb','c','d'] => ARRAY[null::text,null,null,null]; + ?column? + ----------------------------------------------- + "c"=>NULL, "d"=>NULL, "bb"=>NULL, "aaa"=>NULL + (1 row) + + select hstore 'aa=>1, b=>2, c=>3' => ARRAY['g','h','i']; + ?column? + ---------- + + (1 row) + + select hstore 'aa=>1, b=>2, c=>3' => ARRAY['c','b']; + ?column? + -------------------- + "b"=>"2", "c"=>"3" + (1 row) + + select hstore 'aa=>1, b=>2, c=>3' => ARRAY['aa','b']; + ?column? + --------------------- + "b"=>"2", "aa"=>"1" + (1 row) + + select hstore 'aa=>1, b=>2, c=>3' => ARRAY['c','b','aa']; + ?column? + ------------------------------- + "b"=>"2", "c"=>"3", "aa"=>"1" + (1 row) + + -- records + select hstore(v) from (values (1, 'foo', 1.2, 3::float8)) v(a,b,c,d); + hstore + ------------------------------------------------ + "f1"=>"1", "f2"=>"foo", "f3"=>"1.2", "f4"=>"3" + (1 row) + + create table testhstore1 (a integer, b text, c numeric, d float8); + insert into testhstore1 values (1, 'foo', 1.2, 3::float8); + select hstore(v) from testhstore1 v; + hstore + -------------------------------------------- + "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3" + (1 row) + + select populate_record(v, ('c' => '3.45')) from testhstore1 v; + populate_record + ----------------- + (1,foo,3.45,3) + (1 row) + + select populate_record(v, ('d' => '3.45')) from testhstore1 v; + populate_record + ------------------ + (1,foo,1.2,3.45) + (1 row) + + select populate_record(v, ('c' => null)) from testhstore1 v; + populate_record + ----------------- + (1,foo,,3) + (1 row) + + select populate_record(v, ('b' => 'foo') || ('a' => '123')) from testhstore1 v; + populate_record + ----------------- + (123,foo,1.2,3) + (1 row) + + select populate_record(null::testhstore1, ('c' => '3.45') || ('a' => '123')) from testhstore1 v; + populate_record + ----------------- + (123,,3.45,) + (1 row) + + select v #= ('c' => '3.45') from testhstore1 v; + ?column? + ---------------- + (1,foo,3.45,3) + (1 row) + + select v #= ('d' => '3.45') from testhstore1 v; + ?column? + ------------------ + (1,foo,1.2,3.45) + (1 row) + + select v #= ('c' => null) from testhstore1 v; + ?column? + ------------ + (1,foo,,3) + (1 row) + + select v #= (('b' => 'foo') || ('a' => '123')) from testhstore1 v; + ?column? + ----------------- + (123,foo,1.2,3) + (1 row) + + select null::testhstore1 #= (('c' => '3.45') || ('a' => '123')) from testhstore1 v; + ?column? + -------------- + (123,,3.45,) + (1 row) + + select v #= h from testhstore1 v, (values (hstore 'a=>123',1),('b=>foo,c=>3.21',2),('a=>null',3),('e=>blah',4)) x(h,i) order by i; + ?column? + ----------------- + (123,foo,1.2,3) + (1,foo,3.21,3) + (,foo,1.2,3) + (1,foo,1.2,3) + (4 rows) + -- keys/values select akeys('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f'); akeys *************** *** 441,448 **** select avals('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>NULL'); avals ! ------------ ! {g,1,l,""} (1 row) select avals('""=>1'); --- 835,842 ---- select avals('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>NULL'); avals ! -------------- ! {g,1,l,NULL} (1 row) select avals('""=>1'); *************** *** 583,588 **** --- 977,994 ---- 194 (1 row) + select count(*) from testhstore where h ?| ARRAY['public','disabled']; + count + ------- + 337 + (1 row) + + select count(*) from testhstore where h ?& ARRAY['public','disabled']; + count + ------- + 42 + (1 row) + create index hidx on testhstore using gist(h); set enable_seqscan=off; select count(*) from testhstore where h @> 'wait=>NULL'; *************** *** 609,614 **** --- 1015,1032 ---- 194 (1 row) + select count(*) from testhstore where h ?| ARRAY['public','disabled']; + count + ------- + 337 + (1 row) + + select count(*) from testhstore where h ?& ARRAY['public','disabled']; + count + ------- + 42 + (1 row) + drop index hidx; create index hidx on testhstore using gin (h); set enable_seqscan=off; *************** *** 636,641 **** --- 1054,1071 ---- 194 (1 row) + select count(*) from testhstore where h ?| ARRAY['public','disabled']; + count + ------- + 337 + (1 row) + + select count(*) from testhstore where h ?& ARRAY['public','disabled']; + count + ------- + 42 + (1 row) + select count(*) from (select (each(h)).key from testhstore) as wow ; count ------- *************** *** 669,671 **** --- 1099,1151 ---- abstract | 161 (22 rows) + -- btree opclass transitivity + set enable_seqscan=on; + create table testhstore2 as select * from testhstore limit 50; + create function bool_imp(boolean,boolean) returns boolean language sql as $f$ select (not $1) or $2; $f$; + create operator => (leftarg = boolean, rightarg = boolean, procedure = bool_imp); + select every( ((h1.h #># h2.h AND h2.h #># h3.h) => (h1.h #># h3.h)) AND ((h1.h #>=# h2.h AND h2.h #>=# h3.h) => (h1.h #>=# h3.h)) AND ((h1.h #<# h2.h AND h2.h #<# h3.h) => (h1.h #<# h3.h)) AND ((h1.h #<=# h2.h AND h2.h #<=# h3.h) => (h1.h #<=# h3.h)) AND ((h1.h = h2.h AND h2.h = h3.h) => (h1.h = h3.h)) ) from testhstore2 h1, testhstore2 h2, testhstore2 h3; + every + ------- + t + (1 row) + + -- sort/hash + select count(distinct h) from testhstore; + count + ------- + 885 + (1 row) + + set enable_hashagg = false; + select count(*) from (select h from (select * from testhstore union all select * from testhstore) hs group by h) hs2; + count + ------- + 885 + (1 row) + + set enable_hashagg = true; + set enable_sort = false; + select count(*) from (select h from (select * from testhstore union all select * from testhstore) hs group by h) hs2; + count + ------- + 885 + (1 row) + + set enable_sort = true; + -- btree + drop index hidx; + create index hidx on testhstore using btree (h); + set enable_seqscan=off; + select count(*) from testhstore where h #># 'p=>1'; + count + ------- + 125 + (1 row) + + select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t'; + count + ------- + 1 + (1 row) + Index: contrib/hstore/sql/hstore.sql =================================================================== RCS file: /projects/cvsroot/pgsql/contrib/hstore/sql/hstore.sql,v retrieving revision 1.4 diff -c -r1.4 hstore.sql *** contrib/hstore/sql/hstore.sql 14 Mar 2007 14:21:53 -0000 1.4 --- contrib/hstore/sql/hstore.sql 24 Mar 2009 15:14:21 -0000 *************** *** 65,70 **** --- 65,77 ---- select ('aa=>NULL, c=>d , b=>16'::hstore->'aa') is null; select ('aa=>"NULL", c=>d , b=>16'::hstore->'aa') is null; + -- -> array operator + + select 'aa=>"NULL", c=>d , b=>16'::hstore -> ARRAY['aa','c']; + select 'aa=>"NULL", c=>d , b=>16'::hstore -> ARRAY['c','aa']; + select 'aa=>NULL, c=>d , b=>16'::hstore -> ARRAY['aa','c',null]; + select 'aa=>1, c=>3, b=>2, d=>4'::hstore -> ARRAY[['b','d'],['aa','c']]; + -- exists/defined select exist('a=>NULL, b=>qq', 'a'); *************** *** 75,80 **** --- 82,101 ---- select defined('a=>NULL, b=>qq', 'b'); select defined('a=>NULL, b=>qq', 'c'); select defined('a=>"NULL", b=>qq', 'a'); + select hstore 'a=>NULL, b=>qq' ? 'a'; + select hstore 'a=>NULL, b=>qq' ? 'b'; + select hstore 'a=>NULL, b=>qq' ? 'c'; + select hstore 'a=>"NULL", b=>qq' ? 'a'; + select hstore 'a=>NULL, b=>qq' ?| ARRAY['a','b']; + select hstore 'a=>NULL, b=>qq' ?| ARRAY['b','a']; + select hstore 'a=>NULL, b=>qq' ?| ARRAY['c','a']; + select hstore 'a=>NULL, b=>qq' ?| ARRAY['c','d']; + select hstore 'a=>NULL, b=>qq' ?| '{}'::text[]; + select hstore 'a=>NULL, b=>qq' ?& ARRAY['a','b']; + select hstore 'a=>NULL, b=>qq' ?& ARRAY['b','a']; + select hstore 'a=>NULL, b=>qq' ?& ARRAY['c','a']; + select hstore 'a=>NULL, b=>qq' ?& ARRAY['c','d']; + select hstore 'a=>NULL, b=>qq' ?& '{}'::text[]; -- delete *************** *** 83,88 **** --- 104,140 ---- select delete('a=>1 , b=>2, c=>3'::hstore, 'b'); select delete('a=>1 , b=>2, c=>3'::hstore, 'c'); select delete('a=>1 , b=>2, c=>3'::hstore, 'd'); + select 'a=>1 , b=>2, c=>3'::hstore - 'a'::text; + select 'a=>null , b=>2, c=>3'::hstore - 'a'::text; + select 'a=>1 , b=>2, c=>3'::hstore - 'b'::text; + select 'a=>1 , b=>2, c=>3'::hstore - 'c'::text; + select 'a=>1 , b=>2, c=>3'::hstore - 'd'::text; + + -- delete (array) + + select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','e']); + select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','b']); + select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['a','c']); + select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]); + select delete('a=>1 , b=>2, c=>3'::hstore, '{}'::text[]); + select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','e']; + select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','b']; + select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c']; + select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']]; + select 'a=>1 , b=>2, c=>3'::hstore - '{}'::text[]; + + -- delete (hstore) + + select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>4, b=>2'::hstore); + select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>NULL, c=>3'::hstore); + select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore); + select delete('aa=>1 , b=>2, c=>3'::hstore, 'b=>2'::hstore); + select delete('aa=>1 , b=>2, c=>3'::hstore, ''::hstore); + select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>4, b=>2'::hstore; + select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>NULL, c=>3'::hstore; + select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore; + select 'aa=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore; + select 'aa=>1 , b=>2, c=>3'::hstore - ''::hstore; -- || select 'aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f'; *************** *** 97,102 **** --- 149,181 ---- select 'a=>g, b=>c'::hstore || ( 'b'=>'NULL' ); select 'a=>g, b=>c'::hstore || ( 'b'=>NULL ); + -- => arrays + select ARRAY['a','b','asd'] => ARRAY['g','h','i']; + select ARRAY['a','b','asd'] => ARRAY['g','h',NULL]; + select ARRAY['z','y','x'] => ARRAY['1','2','3']; + select ARRAY['aaa','bb','c','d'] => ARRAY[null::text,null,null,null]; + select hstore 'aa=>1, b=>2, c=>3' => ARRAY['g','h','i']; + select hstore 'aa=>1, b=>2, c=>3' => ARRAY['c','b']; + select hstore 'aa=>1, b=>2, c=>3' => ARRAY['aa','b']; + select hstore 'aa=>1, b=>2, c=>3' => ARRAY['c','b','aa']; + + -- records + select hstore(v) from (values (1, 'foo', 1.2, 3::float8)) v(a,b,c,d); + create table testhstore1 (a integer, b text, c numeric, d float8); + insert into testhstore1 values (1, 'foo', 1.2, 3::float8); + select hstore(v) from testhstore1 v; + select populate_record(v, ('c' => '3.45')) from testhstore1 v; + select populate_record(v, ('d' => '3.45')) from testhstore1 v; + select populate_record(v, ('c' => null)) from testhstore1 v; + select populate_record(v, ('b' => 'foo') || ('a' => '123')) from testhstore1 v; + select populate_record(null::testhstore1, ('c' => '3.45') || ('a' => '123')) from testhstore1 v; + select v #= ('c' => '3.45') from testhstore1 v; + select v #= ('d' => '3.45') from testhstore1 v; + select v #= ('c' => null) from testhstore1 v; + select v #= (('b' => 'foo') || ('a' => '123')) from testhstore1 v; + select null::testhstore1 #= (('c' => '3.45') || ('a' => '123')) from testhstore1 v; + select v #= h from testhstore1 v, (values (hstore 'a=>123',1),('b=>foo,c=>3.21',2),('a=>null',3),('e=>blah',4)) x(h,i) order by i; + -- keys/values select akeys('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f'); select akeys('""=>1'); *************** *** 132,137 **** --- 211,218 ---- select count(*) from testhstore where h @> 'wait=>CC'; select count(*) from testhstore where h @> 'wait=>CC, public=>t'; select count(*) from testhstore where h ? 'public'; + select count(*) from testhstore where h ?| ARRAY['public','disabled']; + select count(*) from testhstore where h ?& ARRAY['public','disabled']; create index hidx on testhstore using gist(h); set enable_seqscan=off; *************** *** 140,145 **** --- 221,228 ---- select count(*) from testhstore where h @> 'wait=>CC'; select count(*) from testhstore where h @> 'wait=>CC, public=>t'; select count(*) from testhstore where h ? 'public'; + select count(*) from testhstore where h ?| ARRAY['public','disabled']; + select count(*) from testhstore where h ?& ARRAY['public','disabled']; drop index hidx; create index hidx on testhstore using gin (h); *************** *** 149,154 **** --- 232,263 ---- select count(*) from testhstore where h @> 'wait=>CC'; select count(*) from testhstore where h @> 'wait=>CC, public=>t'; select count(*) from testhstore where h ? 'public'; + select count(*) from testhstore where h ?| ARRAY['public','disabled']; + select count(*) from testhstore where h ?& ARRAY['public','disabled']; select count(*) from (select (each(h)).key from testhstore) as wow ; select key, count(*) from (select (each(h)).key from testhstore) as wow group by key order by count desc, key; + + -- btree opclass transitivity + set enable_seqscan=on; + create table testhstore2 as select * from testhstore limit 50; + create function bool_imp(boolean,boolean) returns boolean language sql as $f$ select (not $1) or $2; $f$; + create operator => (leftarg = boolean, rightarg = boolean, procedure = bool_imp); + select every( ((h1.h #># h2.h AND h2.h #># h3.h) => (h1.h #># h3.h)) AND ((h1.h #>=# h2.h AND h2.h #>=# h3.h) => (h1.h #>=# h3.h)) AND ((h1.h #<# h2.h AND h2.h #<# h3.h) => (h1.h #<# h3.h)) AND ((h1.h #<=# h2.h AND h2.h #<=# h3.h) => (h1.h #<=# h3.h)) AND ((h1.h = h2.h AND h2.h = h3.h) => (h1.h = h3.h)) ) from testhstore2 h1, testhstore2 h2, testhstore2 h3; + + -- sort/hash + select count(distinct h) from testhstore; + set enable_hashagg = false; + select count(*) from (select h from (select * from testhstore union all select * from testhstore) hs group by h) hs2; + set enable_hashagg = true; + set enable_sort = false; + select count(*) from (select h from (select * from testhstore union all select * from testhstore) hs group by h) hs2; + set enable_sort = true; + + -- btree + drop index hidx; + create index hidx on testhstore using btree (h); + set enable_seqscan=off; + + select count(*) from testhstore where h #># 'p=>1'; + select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t'; Index: doc/src/sgml/hstore.sgml =================================================================== RCS file: /projects/cvsroot/pgsql/doc/src/sgml/hstore.sgml,v retrieving revision 1.3 diff -c -r1.3 hstore.sgml *** doc/src/sgml/hstore.sgml 15 Mar 2009 22:05:17 -0000 1.3 --- doc/src/sgml/hstore.sgml 24 Mar 2009 15:14:23 -0000 *************** *** 15,23 **** ! In the current implementation, neither the key nor the value ! string can exceed 65535 bytes in length; an error will be thrown if this ! limit is exceeded. These maximum lengths may change in future releases. --- 15,23 ---- ! In the current implementation, there are no limits on the length of keys ! and values, other than the 1GB limit that applies to all variable-length ! types (including the hstore value itself). *************** *** 88,93 **** --- 88,100 ---- + hstore -> text[] + get values for keys (null if not present) + 'a=>x, b=>y, c=>z'::hstore -> ARRAY['c','a'] + {"z","x"} + + + text => text make single-item hstore 'a' => 'b' *************** *** 95,100 **** --- 102,121 ---- + text[] => text[] + construct an hstore value from separate key/value arrays + ARRAY['a','b'] => ARRAY['1','2'] + "a"=>"1","b"=>"2" + + + + hstore => text[] + construct an hstore value as a slice of an existing hstore + 'a=>1,b=>2,c=>3'::hstore => ARRAY['b','c'] + "b"=>"2", "c"=>"3" + + + hstore || hstore concatenation 'a=>b, c=>d'::hstore || 'c=>x, d=>q'::hstore *************** *** 109,114 **** --- 130,149 ---- + hstore ?& text[] + does hstore contain all specified keys? + 'a=>1,b=>2'::hstore ?& ARRAY['a','b'] + t + + + + hstore ?| text[] + does hstore contain any of the specified keys? + 'a=>1,b=>2'::hstore ? ARRAY['b','c'] + t + + + hstore @> hstore does left operand contain right? 'a=>b, b=>1, c=>NULL'::hstore @> 'b=>1' *************** *** 122,127 **** --- 157,190 ---- f + + hstore - text + delete key from left operand + 'a=>1, b=>2, c=>3'::hstore - 'b'::text + "a"=>"1", "c"=>"3" + + + + hstore - text[] + delete keys from left operand + 'a=>1, b=>2, c=>3'::hstore - ARRAY['a','b'] + "c"=>"3" + + + + hstore - hstore + delete matching key/value pairs from left operand + 'a=>1, b=>2, c=>3'::hstore - 'a=>4, b=>2'::hstore + "a"=>"1", "c"=>"3" + + + + record #= hstore + modify fields in record according to values from hstore + see below under populate_record() + + + *************** *** 150,155 **** --- 213,226 ---- + hstore(record) + hstore + construct an hstore from a record or row + hstore(ROW(1,2)) + f1=>1,f2=>2 + + + akeys(hstore) text[] get hstore's keys as array *************** *** 227,248 **** "a"=>"1" Indexes ! hstore has index support for @> and ? ! operators. You can use either GiST or GIN index types. For example: ! CREATE INDEX hidx ON testhstore USING GIST(h); ! CREATE INDEX hidx ON testhstore USING GIN(h); --- 298,351 ---- "a"=>"1" + + delete(hstore,text[]) + hstore + delete any item matching any of the keys + delete('a=>1,b=>2,c=>3',ARRAY['a','b']) + "c"=>"3" + + + + delete(hstore,hstore) + hstore + delete any key/value pair exactly matching in the second argument + delete('a=>1,b=>2','a=>4,b=>2'::hstore) + "a"=>"1" + + + + populate_record(record,hstore) + record + delete any key/value pair exactly matching in the second argument + delete('a=>1,b=>2','a=>4,b=>2'::hstore) + "a"=>"1" + + + + + NOTE: the function populate_record actually + uses the pseudo-type anyelement, not record, but + it will reject non-record types with an error. + + Indexes ! hstore has index support for @>, ?, ! ?& and ?| operators. You can use either ! GiST or GIN index types. For example: ! CREATE INDEX hidx ON testhstore USING GIST (h); ! CREATE INDEX hidx ON testhstore USING GIN (h);