hstore patch
Started by Andrew Gierthover 16 years ago1 messages
This patch is the updated-to-current-HEAD version of the hstore stuff
I did some months back. It is believed to be fully API-compatible with
the old hstore, though it has a different storage layout. Dump and
restore of an old db should work (the new module provides all the
function names the old one did), though the new functionality
obviously won't be available that way.
Short list of new functionality:
- unlimited key/value length
- binary I/O
- btree and hash support
- construction of hstores from key/value arrays
- hstore(record) and populate_record(record,hstore) for dynamic
manipulations of records
- many operators for hstore difference, slices, etc.; very useful
for determining changes
--
Andrew (irc:RhodiumToad)
Attachments:
hstore_20090708.patch.gztext/x-patchDownload
Index: contrib/hstore/hstore.h
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/hstore/hstore.h,v
retrieving revision 1.8
diff -c -r1.8 hstore.h
*** contrib/hstore/hstore.h 11 Jun 2009 14:48:51 -0000 1.8
--- contrib/hstore/hstore.h 8 Jul 2009 18:38:56 -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.11
diff -c -r1.11 hstore.sql.in
*** contrib/hstore/hstore.sql.in 11 Jun 2009 18:30:03 -0000 1.11
--- contrib/hstore/hstore.sql.in 8 Jul 2009 18:38:57 -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; -- not STRICT
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; -- not STRICT; needs to allow (key,NULL)
!
! CREATE OR REPLACE FUNCTION hstore(text,text)
! RETURNS hstore
! AS 'MODULE_PATHNAME','hstore_from_text'
! LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
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; -- not STRICT; allows (keys,null)
!
! 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; -- not STRICT; allows (null::recordtype)
+
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; -- not STRICT; allows (null::rectype,hstore)
+ 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
***************
*** 194,200 ****
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
! CREATE OR REPLACE FUNCTION ghstore_penalty(internal,internal,internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
--- 432,438 ----
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
! CREATE OR REPLACE FUNCTION ghstore_penalty(internal, internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
***************
*** 214,220 ****
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
! CREATE OR REPLACE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
--- 452,458 ----
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
! CREATE OR REPLACE FUNCTION ghstore_consistent(internal, internal, int, oid, internal)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
***************
*** 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, internal, internal),
FUNCTION 4 gin_consistent_hstore(internal, int2, internal, int4, 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, internal, internal),
FUNCTION 4 gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
! STORAGE text;
!
! --
Index: contrib/hstore/hstore_gin.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/hstore/hstore_gin.c,v
retrieving revision 1.6
diff -c -r1.6 hstore_gin.c
*** contrib/hstore/hstore_gin.c 11 Jun 2009 14:48:51 -0000 1.6
--- contrib/hstore/hstore_gin.c 8 Jul 2009 18:38:57 -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,129 ****
{
bool *check = (bool *) PG_GETARG_POINTER(0);
StrategyNumber strategy = PG_GETARG_UINT16(1);
! HStore *query = PG_GETARG_HS(2);
!
! /* int32 nkeys = PG_GETARG_INT32(3); */
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
bool *recheck = (bool *) PG_GETARG_POINTER(5);
bool res = true;
--- 145,151 ----
{
bool *check = (bool *) PG_GETARG_POINTER(0);
StrategyNumber strategy = PG_GETARG_UINT16(1);
! int32 nkeys = PG_GETARG_INT32(3);
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
bool *recheck = (bool *) PG_GETARG_POINTER(5);
bool res = true;
***************
*** 137,143 ****
* we need recheck
*/
*recheck = true;
! for (i = 0; res && i < 2 * query->size; i++)
if (check[i] == false)
res = false;
}
--- 159,165 ----
* we need recheck
*/
*recheck = true;
! for (i = 0; res && i < nkeys; i++)
if (check[i] == false)
res = false;
}
***************
*** 147,152 ****
--- 169,191 ----
*recheck = false;
res = true;
}
+ else if (strategy == HStoreExistsAnyStrategyNumber)
+ {
+ /* Existence of key is guaranteed */
+ *recheck = false;
+ res = true;
+ }
+ else if (strategy == HStoreExistsAllStrategyNumber)
+ {
+ int i;
+
+ for (i = 0; res && i < nkeys; ++i)
+ if (!check[i])
+ 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.10
diff -c -r1.10 hstore_gist.c
*** contrib/hstore/hstore_gist.c 11 Jun 2009 14:48:51 -0000 1.10
--- contrib/hstore/hstore_gist.c 8 Jul 2009 18:38:58 -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 ----
***************
*** 533,564 ****
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);
--- 530,608 ----
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.11
diff -c -r1.11 hstore_io.c
*** contrib/hstore/hstore_io.c 11 Jun 2009 14:48:51 -0000 1.11
--- contrib/hstore/hstore_io.c 8 Jul 2009 18:38:59 -0000
***************
*** 2,7 ****
--- 2,14 ----
* $PostgreSQL: pgsql/contrib/hstore/hstore_io.c,v 1.11 2009/06/11 14:48:51 momjian Exp $
*/
#include "postgres.h"
+ #include "catalog/pg_type.h"
+ #include "utils/lsyscache.h"
+ #include "utils/typcache.h"
+ #include "access/htup.h"
+ #include "access/heapam.h"
+ #include "libpq/pqformat.h"
+ #include "funcapi.h"
#include <ctype.h>
***************
*** 9,14 ****
--- 16,26 ----
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)
--- 275,281 ----
}
}
! 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;
--- 298,311 ----
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)
{
--- 323,330 ----
res = a;
while (ptr - a < l)
{
! if (ptr->keylen == res->keylen
! && strncmp(ptr->key, res->key, res->keylen) == 0)
{
if (ptr->needfree)
{
***************
*** 327,332 ****
--- 346,352 ----
return res + 1 - a;
}
+ #if 0 /* this superfluous code was >15% of hstore_in's runtime. */
static void
freeHSParse(HSParser *state)
{
***************
*** 344,349 ****
--- 364,370 ----
}
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)
{
--- 387,993 ----
}
+ 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;
! ArrayType *value_array;
! int i;
!
! if (PG_ARGISNULL(0))
! PG_RETURN_NULL();
!
! key_array = PG_GETARG_ARRAYTYPE_P(0);
!
! Assert(ARR_ELEMTYPE(key_array) == TEXTOID);
!
! if (ARR_NDIM(key_array) != 1)
! ereport(ERROR,
! (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
! errmsg("wrong number of array subscripts")));
!
! deconstruct_array(key_array,
! TEXTOID, -1, false, 'i',
! &key_datums, &key_nulls, &key_count);
!
! /* value_array might be NULL */
!
! if (PG_ARGISNULL(1))
! {
! value_array = NULL;
! value_count = key_count;
! value_datums = NULL;
! value_nulls = NULL;
! }
! else
! {
! value_array = PG_GETARG_ARRAYTYPE_P(1);
!
! Assert(ARR_ELEMTYPE(value_array) == TEXTOID);
!
! if (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(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 || 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;
! 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;
!
! if (PG_ARGISNULL(0))
! {
! Oid argtype = get_fn_expr_argtype(fcinfo->flinfo,0);
!
! /* 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;
!
! rec = NULL;
! }
! else
! {
! rec = PG_GETARG_HEAPTUPLEHEADER(0);
!
! /* Extract type info from the tuple itself */
! tupType = HeapTupleHeaderGetTypeId(rec);
! tupTypmod = HeapTupleHeaderGetTypMod(rec);
! }
!
! tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
! ncolumns = tupdesc->natts;
!
! /*
! * 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;
! }
!
! pairs = palloc(ncolumns * sizeof(Pairs));
!
! 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;
!
! values = (Datum *) palloc(ncolumns * sizeof(Datum));
! nulls = (bool *) palloc(ncolumns * sizeof(bool));
!
! /* Break down the tuple into fields */
! heap_deform_tuple(&tuple, tupdesc, values, nulls);
! }
! else
! {
! values = NULL;
! nulls = NULL;
! }
!
! 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 || 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);
+
+ if (PG_ARGISNULL(1))
+ PG_RETURN_POINTER(rec);
+
+ /* Extract type info from the tuple itself */
+ tupType = HeapTupleHeaderGetTypeId(rec);
+ tupTypmod = HeapTupleHeaderGetTypMod(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)
{
***************
*** 446,453 ****
{
HStore *in = PG_GETARG_HS(0);
int buflen,
! i,
! nnulls = 0;
char *out,
*ptr;
char *base = STRPTR(in);
--- 1009,1016 ----
{
HStore *in = PG_GETARG_HS(0);
int buflen,
! i;
! int count = in->size;
char *out,
*ptr;
char *base = STRPTR(in);
***************
*** 457,485 ****
{
out = palloc(1);
*out = '\0';
- PG_FREE_IF_COPY(in, 0);
PG_RETURN_CSTRING(out);
}
! for (i = 0; i < in->size; i++)
! if (entries[i].valisnull)
! nnulls++;
!
! buflen = (4 /* " */ + 2 /* => */ ) * (in->size - nnulls) +
! (2 /* " */ + 2 /* => */ + 4 /* NULL */ ) * nnulls +
! 2 /* , */ * (in->size - 1) +
! 2 /* esc */ * (VARSIZE(in) - CALCDATASIZE(in->size, 0)) +
! 1 /* \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';
--- 1020,1057 ----
{
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';
***************
*** 489,495 ****
else
{
*ptr++ = '"';
! ptr = cpw(ptr, base + entries[i].pos + entries[i].keylen, entries[i].vallen);
*ptr++ = '"';
}
--- 1061,1067 ----
else
{
*ptr++ = '"';
! ptr = cpw(ptr, HS_VAL(entries,base,i), HS_VALLEN(entries,i));
*ptr++ = '"';
}
***************
*** 501,506 ****
}
*ptr = '\0';
- PG_FREE_IF_COPY(in, 0);
PG_RETURN_CSTRING(out);
}
--- 1073,1114 ----
}
*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.13
diff -c -r1.13 hstore_op.c
*** contrib/hstore/hstore_op.c 11 Jun 2009 14:48:51 -0000 1.13
--- contrib/hstore/hstore_op.c 8 Jul 2009 18:39:00 -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,834 ----
/*
! * $PostgreSQL$
*/
#include "postgres.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "utils/builtins.h"
+ #include "access/hash.h"
+ #include "access/htup.h"
+ #include "access/heapam.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);
}
--- 838,1137 ----
{
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.8
diff -c -r1.8 uninstall_hstore.sql
*** contrib/hstore/uninstall_hstore.sql 25 Mar 2009 22:19:01 -0000 1.8
--- contrib/hstore/uninstall_hstore.sql 8 Jul 2009 18:39:00 -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 8 Jul 2009 18:39:01 -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,825 ----
"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 ARRAY['aaa','bb','c','d'] => 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 hstore(null::testhstore1);
+ hstore
+ --------------------------------------------
+ "a"=>NULL, "b"=>NULL, "c"=>NULL, "d"=>NULL
+ (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
***************
*** 440,448 ****
(1 row)
select avals('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>NULL');
! avals
! ------------
! {g,1,l,""}
(1 row)
select avals('""=>1');
--- 846,854 ----
(1 row)
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 ****
--- 989,1006 ----
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 ****
--- 1027,1044 ----
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 ****
--- 1066,1083 ----
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 ****
--- 1111,1163 ----
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 8 Jul 2009 18:39:01 -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,183 ----
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 ARRAY['aaa','bb','c','d'] => 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 hstore(null::testhstore1);
+ 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 ****
--- 213,220 ----
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 ****
--- 223,230 ----
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 ****
--- 234,265 ----
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 8 Jul 2009 18:39:03 -0000
***************
*** 15,23 ****
</para>
<para>
! 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.
</para>
<sect2>
--- 15,23 ----
</para>
<para>
! 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).
</para>
<sect2>
***************
*** 88,93 ****
--- 88,100 ----
</row>
<row>
+ <entry><type>hstore</> <literal>-></> <type>text[]</></entry>
+ <entry>get values for keys (null if not present)</entry>
+ <entry><literal>'a=>x, b=>y, c=>z'::hstore -> ARRAY['c','a']</literal></entry>
+ <entry><literal>{"z","x"}</literal></entry>
+ </row>
+
+ <row>
<entry><type>text</> <literal>=></> <type>text</></entry>
<entry>make single-item <type>hstore</></entry>
<entry><literal>'a' => 'b'</literal></entry>
***************
*** 95,100 ****
--- 102,121 ----
</row>
<row>
+ <entry><type>text[]</> <literal>=></> <type>text[]</></entry>
+ <entry>construct an <type>hstore</> value from separate key/value arrays</entry>
+ <entry><literal>ARRAY['a','b'] => ARRAY['1','2']</literal></entry>
+ <entry><literal>"a"=>"1","b"=>"2"</literal></entry>
+ </row>
+
+ <row>
+ <entry><type>hstore</> <literal>=></> <type>text[]</></entry>
+ <entry>construct an <type>hstore</> value as a slice of an existing <type>hstore</></entry>
+ <entry><literal>'a=>1,b=>2,c=>3'::hstore => ARRAY['b','c']</literal></entry>
+ <entry><literal>"b"=>"2", "c"=>"3"</literal></entry>
+ </row>
+
+ <row>
<entry><type>hstore</> <literal>||</> <type>hstore</></entry>
<entry>concatenation</entry>
<entry><literal>'a=>b, c=>d'::hstore || 'c=>x, d=>q'::hstore</literal></entry>
***************
*** 109,114 ****
--- 130,149 ----
</row>
<row>
+ <entry><type>hstore</> <literal>?&</> <type>text[]</></entry>
+ <entry>does <type>hstore</> contain all specified keys?</entry>
+ <entry><literal>'a=>1,b=>2'::hstore ?& ARRAY['a','b']</literal></entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry><type>hstore</> <literal>?|</> <type>text[]</></entry>
+ <entry>does <type>hstore</> contain any of the specified keys?</entry>
+ <entry><literal>'a=>1,b=>2'::hstore ? ARRAY['b','c']</literal></entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
<entry><type>hstore</> <literal>@></> <type>hstore</></entry>
<entry>does left operand contain right?</entry>
<entry><literal>'a=>b, b=>1, c=>NULL'::hstore @> 'b=>1'</literal></entry>
***************
*** 122,127 ****
--- 157,190 ----
<entry><literal>f</literal></entry>
</row>
+ <row>
+ <entry><type>hstore</> <literal>-</> <type>text</></entry>
+ <entry>delete key from left operand</entry>
+ <entry><literal>'a=>1, b=>2, c=>3'::hstore - 'b'::text</literal></entry>
+ <entry><literal>"a"=>"1", "c"=>"3"</literal></entry>
+ </row>
+
+ <row>
+ <entry><type>hstore</> <literal>-</> <type>text[]</></entry>
+ <entry>delete keys from left operand</entry>
+ <entry><literal>'a=>1, b=>2, c=>3'::hstore - ARRAY['a','b']</literal></entry>
+ <entry><literal>"c"=>"3"</literal></entry>
+ </row>
+
+ <row>
+ <entry><type>hstore</> <literal>-</> <type>hstore</></entry>
+ <entry>delete matching key/value pairs from left operand</entry>
+ <entry><literal>'a=>1, b=>2, c=>3'::hstore - 'a=>4, b=>2'::hstore</literal></entry>
+ <entry><literal>"a"=>"1", "c"=>"3"</literal></entry>
+ </row>
+
+ <row>
+ <entry><type>record</> <literal>#=</> <type>hstore</></entry>
+ <entry>modify fields in record according to values from hstore</entry>
+ <entry><literal>see below under <function>populate_record()</></literal></entry>
+ <entry><literal></literal></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
***************
*** 150,155 ****
--- 213,226 ----
<tbody>
<row>
+ <entry><function>hstore(record)</function></entry>
+ <entry><type>hstore</type></entry>
+ <entry>construct an <type>hstore</> from a record or row</entry>
+ <entry><literal>hstore(ROW(1,2))</literal></entry>
+ <entry><literal>f1=>1,f2=>2</literal></entry>
+ </row>
+
+ <row>
<entry><function>akeys(hstore)</function></entry>
<entry><type>text[]</type></entry>
<entry>get <type>hstore</>'s keys as array</entry>
***************
*** 227,248 ****
<entry><literal>"a"=>"1"</literal></entry>
</row>
</tbody>
</tgroup>
</table>
</sect2>
<sect2>
<title>Indexes</title>
<para>
! <type>hstore</> has index support for <literal>@></> and <literal>?</>
! operators. You can use either GiST or GIN index types. For example:
</para>
<programlisting>
! CREATE INDEX hidx ON testhstore USING GIST(h);
! CREATE INDEX hidx ON testhstore USING GIN(h);
</programlisting>
</sect2>
--- 298,351 ----
<entry><literal>"a"=>"1"</literal></entry>
</row>
+ <row>
+ <entry><function>delete(hstore,text[])</function></entry>
+ <entry><type>hstore</type></entry>
+ <entry>delete any item matching any of the keys</entry>
+ <entry><literal>delete('a=>1,b=>2,c=>3',ARRAY['a','b'])</literal></entry>
+ <entry><literal>"c"=>"3"</literal></entry>
+ </row>
+
+ <row>
+ <entry><function>delete(hstore,hstore)</function></entry>
+ <entry><type>hstore</type></entry>
+ <entry>delete any key/value pair exactly matching in the second argument</entry>
+ <entry><literal>delete('a=>1,b=>2','a=>4,b=>2'::hstore)</literal></entry>
+ <entry><literal>"a"=>"1"</literal></entry>
+ </row>
+
+ <row>
+ <entry><function>populate_record(record,hstore)</function></entry>
+ <entry><type>record</type></entry>
+ <entry>delete any key/value pair exactly matching in the second argument</entry>
+ <entry><literal>delete('a=>1,b=>2','a=>4,b=>2'::hstore)</literal></entry>
+ <entry><literal>"a"=>"1"</literal></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
+
+ <para>
+ NOTE: the function <function>populate_record</function> actually
+ uses the pseudo-type <type>anyelement</>, not <type>record</>, but
+ it will reject non-record types with an error.
+ </para>
+
</sect2>
<sect2>
<title>Indexes</title>
<para>
! <type>hstore</> has index support for <literal>@></>, <literal>?</>,
! <literal>?&</> and <literal>?|</> operators. You can use either
! GiST or GIN index types. For example:
</para>
<programlisting>
! CREATE INDEX hidx ON testhstore USING GIST (h);
! CREATE INDEX hidx ON testhstore USING GIN (h);
</programlisting>
</sect2>
***************
*** 321,326 ****
--- 424,433 ----
<para>
Teodor Sigaev <email>teodor@sigaev.ru</email>, Moscow, Delta-Soft Ltd., Russia
</para>
+
+ <para>
+ With additional enhancements by Andrew Gierth <email>andrew@tao11.riddles.org.uk</email>, United Kingdom
+ </para>
</sect2>
</sect1>