Revert changes to hstore; formalize notion that jsonb and hstore are fully separate
authorPeter Geoghegan <[email protected]>
Thu, 6 Mar 2014 06:39:58 +0000 (22:39 -0800)
committerPeter Geoghegan <[email protected]>
Thu, 6 Mar 2014 06:39:58 +0000 (22:39 -0800)
18 files changed:
contrib/hstore/.gitignore
contrib/hstore/Makefile
contrib/hstore/crc32.c [new file with mode: 0644]
contrib/hstore/crc32.h [new file with mode: 0644]
contrib/hstore/hstore--1.2--1.3.sql [deleted file]
contrib/hstore/hstore--1.2.sql [moved from contrib/hstore/hstore--1.3.sql with 63% similarity]
contrib/hstore/hstore.control
contrib/hstore/hstore.h
contrib/hstore/hstore_compat.c
contrib/hstore/hstore_gin.c
contrib/hstore/hstore_gist.c
contrib/hstore/hstore_gram.y [deleted file]
contrib/hstore/hstore_io.c
contrib/hstore/hstore_op.c
contrib/hstore/hstore_scan.l [deleted file]
doc/src/sgml/hstore.sgml
src/backend/utils/adt/jsonb.c
src/backend/utils/adt/jsonb_gist.c

index b84aac6b6ae1841672f01b4210a4cbe598936183..5dcb3ff9723501c3fe639bee1c1435e47a580a6f 100644 (file)
@@ -2,6 +2,3 @@
 /log/
 /results/
 /tmp_check/
-/hstore_gram.c
-/hstore_gram.h
-/hstore_scan.c
index 3690adb910bb31fe05f36fd5a4431c25df485f64..43b7e5f9e71f83761101691f8d830788eb7fa51f 100644 (file)
@@ -2,16 +2,13 @@
 
 MODULE_big = hstore
 OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
-       hstore_gram.o
+   crc32.o
 
 EXTENSION = hstore
-DATA = hstore--1.3.sql hstore--1.0--1.1.sql hstore--1.1--1.2.sql \
-      hstore--1.2--1.3.sql hstore--unpackaged--1.0.sql
+DATA = hstore--1.2.sql hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
+   hstore--unpackaged--1.0.sql
 
-REGRESS = hstore nested types
-
-EXTRA_CLEAN = y.tab.c y.tab.h \
-               hstore_gram.c hstore_scan.c hstore_gram.h
+REGRESS = hstore
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
@@ -23,12 +20,3 @@ top_builddir = ../..
 include $(top_builddir)/src/Makefile.global
 include $(top_srcdir)/contrib/contrib-global.mk
 endif
-
-hstore_gram.o: hstore_scan.c
-
-hstore_gram.c: BISONFLAGS += -d
-
-distprep: hstore_gram.c hstore_scan.c
-
-maintainer-clean:
-   rm -f hstore_gram.c hstore_scan.c hstore_gram.h
diff --git a/contrib/hstore/crc32.c b/contrib/hstore/crc32.c
new file mode 100644 (file)
index 0000000..c82fc66
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * contrib/hstore/crc32.c
+ *
+ * Both POSIX and CRC32 checksums */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "crc32.h"
+
+/*
+ * This code implements the AUTODIN II polynomial
+ * The variable corresponding to the macro argument "crc" should
+ * be an unsigned long.
+ * Original code  by Spencer Garrett <[email protected]>
+ */
+
+#define _CRC32_(crc, ch)    (crc = (crc >> 8) ^ crc32tab[(crc ^ (ch)) & 0xff])
+
+/* generated using the AUTODIN II polynomial
+ * x^32 + x^26 + x^23 + x^22 + x^16 +
+ * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
+ */
+
+static const unsigned int crc32tab[256] = {
+   0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+   0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+   0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+   0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+   0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+   0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+   0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+   0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+   0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+   0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+   0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+   0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+   0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+   0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+   0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+   0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+   0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+   0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+   0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+   0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+   0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+   0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+   0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+   0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+   0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+   0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+   0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+   0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+   0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+   0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+   0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+   0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+   0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+   0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+   0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+   0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+   0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+   0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+   0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+   0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+   0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+   0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+   0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+   0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+   0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+   0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+   0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+   0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+   0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+   0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+   0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+   0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+   0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+   0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+   0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+   0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+   0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+   0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+   0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+   0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+   0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+   0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+   0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+   0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+unsigned int
+crc32_sz(char *buf, int size)
+{
+   unsigned int crc = ~((unsigned int) 0);
+   char       *p;
+   int         len,
+               nr;
+
+   len = 0;
+   nr = size;
+   for (len += nr, p = buf; nr--; ++p)
+       _CRC32_(crc, *p);
+   return ~crc;
+}
diff --git a/contrib/hstore/crc32.h b/contrib/hstore/crc32.h
new file mode 100644 (file)
index 0000000..f5bfd82
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * contrib/hstore/crc32.h
+ */
+#ifndef _CRC32_H
+#define _CRC32_H
+
+/* Returns crc32 of data block */
+extern unsigned int crc32_sz(char *buf, int size);
+
+/* Returns crc32 of null-terminated string */
+#define crc32(buf) crc32_sz((buf),strlen(buf))
+
+#endif
diff --git a/contrib/hstore/hstore--1.2--1.3.sql b/contrib/hstore/hstore--1.2--1.3.sql
deleted file mode 100644 (file)
index a59e42e..0000000
+++ /dev/null
@@ -1,327 +0,0 @@
-/* contrib/hstore/hstore--1.2--1.3.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "ALTER EXTENSION hstore UPDATE TO '1.2'" to load this file. \quit
-
-CREATE FUNCTION fetchval_numeric(hstore,text)
-RETURNS numeric
-AS 'MODULE_PATHNAME','hstore_fetchval_numeric'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ^> (
-   LEFTARG = hstore,
-   RIGHTARG = text,
-   PROCEDURE = fetchval_numeric
-);
-
-CREATE FUNCTION fetchval_boolean(hstore,text)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_fetchval_boolean'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?> (
-   LEFTARG = hstore,
-   RIGHTARG = text,
-   PROCEDURE = fetchval_boolean
-);
-
-CREATE FUNCTION fetchval(hstore,int)
-RETURNS text
-AS 'MODULE_PATHNAME','hstore_fetchval_n'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR -> (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = fetchval
-);
-
-CREATE FUNCTION fetchval_numeric(hstore,int)
-RETURNS numeric
-AS 'MODULE_PATHNAME','hstore_fetchval_n_numeric'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ^> (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = fetchval_numeric
-);
-
-CREATE FUNCTION fetchval_boolean(hstore,int)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_fetchval_n_boolean'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?> (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = fetchval_boolean
-);
-
-CREATE FUNCTION fetchval(hstore,text[])
-RETURNS text
-AS 'MODULE_PATHNAME','hstore_fetchval_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #> (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = fetchval
-);
-
-CREATE FUNCTION fetchval_numeric(hstore,text[])
-RETURNS numeric
-AS 'MODULE_PATHNAME','hstore_fetchval_path_numeric'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #^> (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = fetchval_numeric
-);
-
-CREATE FUNCTION fetchval_boolean(hstore,text[])
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_fetchval_path_boolean'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #?> (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = fetchval_boolean
-);
-
-CREATE FUNCTION fetchval_hstore(hstore,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_fetchval_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %> (
-   LEFTARG = hstore,
-   RIGHTARG = text,
-   PROCEDURE = fetchval_hstore
-);
-
-CREATE FUNCTION fetchval_hstore(hstore,int)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_fetchval_n_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %> (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = fetchval_hstore
-);
-
-CREATE FUNCTION fetchval_hstore(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_fetchval_path_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #%> (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = fetchval_hstore
-);
-
-CREATE FUNCTION json_to_hstore(json)
-RETURNS hstore
-AS 'MODULE_PATHNAME','json_to_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE CAST (json AS hstore)
-WITH FUNCTION json_to_hstore(json);
-
-CREATE FUNCTION isexists(hstore,int)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_idx'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION exist(hstore,int)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_idx'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ? (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = exist,
-   RESTRICT = contsel,
-   JOIN = contjoinsel
-);
-
-CREATE FUNCTION isexists(hstore,text[])
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION exist(hstore,text[])
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #? (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = exist,
-   RESTRICT = contsel,
-   JOIN = contjoinsel
-);
-
-CREATE FUNCTION delete_path(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #- (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = delete_path
-);
-
-CREATE FUNCTION delete(hstore,int)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_idx'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR - (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = delete
-);
-
-CREATE FUNCTION replace(hstore,text[],hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_replace'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION svals(hstore, text[])
-RETURNS setof text
-AS 'MODULE_PATHNAME','hstore_svals_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hvals(hstore)
-RETURNS setof hstore
-AS 'MODULE_PATHNAME','hstore_hvals'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hvals(hstore, text[])
-RETURNS setof hstore
-AS 'MODULE_PATHNAME','hstore_hvals_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION concat_path(hstore,text[],hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_deep_concat'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION each_hstore(IN hs hstore,
-   OUT key text,
-   OUT value hstore)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME','hstore_each_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_typeof(hstore)
-RETURNS text
-AS 'MODULE_PATHNAME','hstore_typeof'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore(text,bool)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_bool'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text,numeric)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_numeric'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_scalar_from_text'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(bool)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_scalar_from_bool'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(numeric)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_scalar_from_numeric'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-
--- GIN support: hash based opclass
-
-FUNCTION gin_extract_hstore_hash(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_extract_hstore_hash_query(internal, internal, int2, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_consistent_hstore_hash(internal, int2, internal, int4, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR CLASS gin_hstore_hash_ops
-FOR TYPE hstore USING gin
-AS
-   OPERATOR        7       @>,
-   FUNCTION        1       btint4cmp(int4,int4),
-   FUNCTION        2       gin_extract_hstore_hash(internal, internal),
-   FUNCTION        3       gin_extract_hstore_hash_query(internal, internal, int2, internal, internal),
-   FUNCTION        4       gin_consistent_hstore_hash(internal, int2, internal, int4, internal, internal),
-STORAGE         int4;
-
-CREATE FUNCTION array_to_hstore(anyarray)
-RETURNS hstore
-AS 'MODULE_PATHNAME','array_to_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_pretty_print()
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION hstore_array_curly_braces()
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION hstore_json()
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION hstore_root_hash_decorated()
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION hstore_loose()
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION hstore_print(hstore,
-                            pretty_print bool DEFAULT false,
-                            array_curly_braces bool DEFAULT false,
-                            root_hash_decorated bool DEFAULT false,
-                            json bool DEFAULT false,
-                            loose bool DEFAULT false)
-RETURNS text
-AS 'MODULE_PATHNAME', 'hstore_print'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE CAST (hstore AS jsonb)
-  WITHOUT FUNCTION AS IMPLICIT;
-
-CREATE CAST (jsonb AS hstore)
-  WITHOUT FUNCTION AS IMPLICIT;
similarity index 63%
rename from contrib/hstore/hstore--1.3.sql
rename to contrib/hstore/hstore--1.2.sql
index 12990af2ab4451dd235ace1ae35fba3d6bd9d093..f415a7230b440d1dd70124a971dcf364cc0e926f 100644 (file)
@@ -1,4 +1,4 @@
-/* contrib/hstore/hstore--1.3.sql */
+/* contrib/hstore/hstore--1.1.sql */
 
 -- complain if script is sourced in psql, rather than via CREATE EXTENSION
 \echo Use "CREATE EXTENSION hstore" to load this file. \quit
@@ -50,127 +50,6 @@ CREATE OPERATOR -> (
    PROCEDURE = fetchval
 );
 
-CREATE FUNCTION fetchval_numeric(hstore,text)
-RETURNS numeric
-AS 'MODULE_PATHNAME','hstore_fetchval_numeric'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ^> (
-   LEFTARG = hstore,
-   RIGHTARG = text,
-   PROCEDURE = fetchval_numeric
-);
-
-CREATE FUNCTION fetchval_boolean(hstore,text)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_fetchval_boolean'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?> (
-   LEFTARG = hstore,
-   RIGHTARG = text,
-   PROCEDURE = fetchval_boolean
-);
-
-CREATE FUNCTION fetchval(hstore,int)
-RETURNS text
-AS 'MODULE_PATHNAME','hstore_fetchval_n'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR -> (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = fetchval
-);
-
-CREATE FUNCTION fetchval_numeric(hstore,int)
-RETURNS numeric
-AS 'MODULE_PATHNAME','hstore_fetchval_n_numeric'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ^> (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = fetchval_numeric
-);
-
-CREATE FUNCTION fetchval_boolean(hstore,int)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_fetchval_n_boolean'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?> (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = fetchval_boolean
-);
-
-CREATE FUNCTION fetchval(hstore,text[])
-RETURNS text
-AS 'MODULE_PATHNAME','hstore_fetchval_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #> (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = fetchval
-);
-
-CREATE FUNCTION fetchval_numeric(hstore,text[])
-RETURNS numeric
-AS 'MODULE_PATHNAME','hstore_fetchval_path_numeric'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #^> (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = fetchval_numeric
-);
-
-CREATE FUNCTION fetchval_boolean(hstore,text[])
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_fetchval_path_boolean'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #?> (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = fetchval_boolean
-);
-
-CREATE FUNCTION fetchval_hstore(hstore,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_fetchval_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %> (
-   LEFTARG = hstore,
-   RIGHTARG = text,
-   PROCEDURE = fetchval_hstore
-);
-
-CREATE FUNCTION fetchval_hstore(hstore,int)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_fetchval_n_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %> (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = fetchval_hstore
-);
-
-CREATE FUNCTION fetchval_hstore(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_fetchval_path_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #%> (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = fetchval_hstore
-);
-
 CREATE FUNCTION slice_array(hstore,text[])
 RETURNS text[]
 AS 'MODULE_PATHNAME','hstore_slice_to_array'
@@ -205,42 +84,6 @@ CREATE OPERATOR ? (
    JOIN = contjoinsel
 );
 
-CREATE FUNCTION isexists(hstore,int)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_idx'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION exist(hstore,int)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_idx'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ? (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = exist,
-   RESTRICT = contsel,
-   JOIN = contjoinsel
-);
-
-CREATE FUNCTION isexists(hstore,text[])
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION exist(hstore,text[])
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR #? (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = exist,
-   RESTRICT = contsel,
-   JOIN = contjoinsel
-);
-
 CREATE FUNCTION exists_any(hstore,text[])
 RETURNS bool
 AS 'MODULE_PATHNAME','hstore_exists_any'
@@ -282,11 +125,6 @@ RETURNS hstore
 AS 'MODULE_PATHNAME','hstore_delete'
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION delete(hstore,int)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_idx'
-LANGUAGE C STRICT IMMUTABLE;
-
 CREATE FUNCTION delete(hstore,text[])
 RETURNS hstore
 AS 'MODULE_PATHNAME','hstore_delete_array'
@@ -297,23 +135,12 @@ RETURNS hstore
 AS 'MODULE_PATHNAME','hstore_delete_hstore'
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION delete_path(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_path'
-LANGUAGE C STRICT IMMUTABLE;
-
 CREATE OPERATOR - (
    LEFTARG = hstore,
    RIGHTARG = text,
    PROCEDURE = delete
 );
 
-CREATE OPERATOR - (
-   LEFTARG = hstore,
-   RIGHTARG = int,
-   PROCEDURE = delete
-);
-
 CREATE OPERATOR - (
    LEFTARG = hstore,
    RIGHTARG = text[],
@@ -326,17 +153,6 @@ CREATE OPERATOR - (
    PROCEDURE = delete
 );
 
-CREATE OPERATOR #- (
-   LEFTARG = hstore,
-   RIGHTARG = text[],
-   PROCEDURE = delete_path
-);
-
-CREATE FUNCTION replace(hstore,text[],hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_replace'
-LANGUAGE C STRICT IMMUTABLE;
-
 CREATE FUNCTION hs_concat(hstore,hstore)
 RETURNS hstore
 AS 'MODULE_PATHNAME','hstore_concat'
@@ -348,11 +164,6 @@ CREATE OPERATOR || (
    PROCEDURE = hs_concat
 );
 
-CREATE FUNCTION concat_path(hstore,text[],hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_deep_concat'
-LANGUAGE C STRICT IMMUTABLE;
-
 CREATE FUNCTION hs_contains(hstore,hstore)
 RETURNS bool
 AS 'MODULE_PATHNAME','hstore_contains'
@@ -410,36 +221,6 @@ RETURNS hstore
 AS 'MODULE_PATHNAME','hstore_from_text'
 LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
 
-CREATE FUNCTION hstore(text,bool)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_bool'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text,numeric)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_numeric'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text,hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_th'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_scalar_from_text'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(bool)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_scalar_from_bool'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(numeric)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_scalar_from_numeric'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
 CREATE FUNCTION hstore(text[],text[])
 RETURNS hstore
 AS 'MODULE_PATHNAME', 'hstore_from_arrays'
@@ -461,12 +242,6 @@ LANGUAGE C IMMUTABLE STRICT;
 CREATE CAST (hstore AS json)
   WITH FUNCTION hstore_to_json(hstore);
 
-CREATE CAST (hstore AS jsonb)
-  WITHOUT FUNCTION AS IMPLICIT;
-
-CREATE CAST (jsonb AS hstore)
-  WITHOUT FUNCTION AS IMPLICIT;
-
 CREATE FUNCTION hstore_to_json_loose(hstore)
 RETURNS json
 AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
@@ -517,21 +292,6 @@ RETURNS setof text
 AS 'MODULE_PATHNAME','hstore_svals'
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION svals(hstore, text[])
-RETURNS setof text
-AS 'MODULE_PATHNAME','hstore_svals_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hvals(hstore)
-RETURNS setof hstore
-AS 'MODULE_PATHNAME','hstore_hvals'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hvals(hstore, text[])
-RETURNS setof hstore
-AS 'MODULE_PATHNAME','hstore_hvals_path'
-LANGUAGE C STRICT IMMUTABLE;
-
 CREATE FUNCTION each(IN hs hstore,
     OUT key text,
     OUT value text)
@@ -539,18 +299,6 @@ RETURNS SETOF record
 AS 'MODULE_PATHNAME','hstore_each'
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION each_hstore(IN hs hstore,
-    OUT key text,
-    OUT value hstore)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME','hstore_each_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_typeof(hstore)
-RETURNS text
-AS 'MODULE_PATHNAME','hstore_typeof'
-LANGUAGE C STRICT IMMUTABLE;
-
 CREATE FUNCTION populate_record(anyelement,hstore)
 RETURNS anyelement
 AS 'MODULE_PATHNAME', 'hstore_populate_record'
@@ -562,19 +310,6 @@ CREATE OPERATOR #= (
    PROCEDURE = populate_record
 );
 
-CREATE FUNCTION json_to_hstore(json)
-RETURNS hstore
-AS 'MODULE_PATHNAME','json_to_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE CAST (json AS hstore)
-WITH FUNCTION json_to_hstore(json);
-
-CREATE FUNCTION array_to_hstore(anyarray)
-RETURNS hstore
-AS 'MODULE_PATHNAME','array_to_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
 -- btree support
 
 CREATE FUNCTION hstore_eq(hstore,hstore)
@@ -771,7 +506,7 @@ AS
         FUNCTION        7       ghstore_same (internal, internal, internal),
         STORAGE         ghstore;
 
--- GIN support: default opclass
+-- GIN support
 
 CREATE FUNCTION gin_extract_hstore(internal, internal)
 RETURNS internal
@@ -800,42 +535,3 @@ AS
    FUNCTION        3       gin_extract_hstore_query(internal, internal, int2, internal, internal),
    FUNCTION        4       gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
    STORAGE         text;
-
--- GIN support: hash based opclass
-
-CREATE FUNCTION gin_extract_hstore_hash(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_extract_hstore_hash_query(internal, internal, int2, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_consistent_hstore_hash(internal, int2, internal, int4, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR CLASS gin_hstore_hash_ops
-FOR TYPE hstore USING gin
-AS
-   OPERATOR        7       @>,
-   FUNCTION        1       btint4cmp(int4,int4),
-   FUNCTION        2       gin_extract_hstore_hash(internal, internal),
-   FUNCTION        3       gin_extract_hstore_hash_query(internal, internal, int2, internal, internal),
-   FUNCTION        4       gin_consistent_hstore_hash(internal, int2, internal, int4, internal, internal),
-   STORAGE         int4;
-
--- output
-
-CREATE FUNCTION hstore_print(hstore,
-                            pretty_print bool DEFAULT false,
-                            array_curly_braces bool DEFAULT false,
-                            root_hash_decorated bool DEFAULT false,
-                            json bool DEFAULT false,
-                            loose bool DEFAULT false)
-RETURNS text
-AS 'MODULE_PATHNAME', 'hstore_print'
-LANGUAGE C IMMUTABLE STRICT;
index dcc3b687cff46c75e524054419cb0702505d111e..9daf5e2e00a38e3597660f4e04ad60d2f39a28b2 100644 (file)
@@ -1,5 +1,5 @@
 # hstore extension
 comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.3'
+default_version = '1.2'
 module_pathname = '$libdir/hstore'
 relocatable = true
index b27f71cd497278618e01bdef30e399eb778b37a8..37e46118531ab83fd9b5863f4cf186dd2165e5c6 100644 (file)
@@ -4,7 +4,9 @@
 #ifndef __HSTORE_H__
 #define __HSTORE_H__
 
-#include "utils/jsonb.h"
+#include "fmgr.h"
+#include "utils/array.h"
+
 
 /*
  * HEntry: there is one of these for each key _and_ value in an hstore
  * by subtraction from the previous entry. the ISFIRST flag lets us tell
  * whether there is a previous entry.
  */
-
-typedef JEntry HEntry;
-
-#define HENTRY_ISFIRST     JENTRY_ISFIRST
-#define HENTRY_ISSTRING    JENTRY_ISSTRING
-#define HENTRY_ISNUMERIC   JENTRY_ISNUMERIC
-#define HENTRY_ISNEST      JENTRY_ISNEST
-#define HENTRY_ISNULL      JENTRY_ISNULL
-#define HENTRY_ISBOOL      JENTRY_ISBOOL
-#define HENTRY_ISFALSE     JENTRY_ISFALSE
-#define HENTRY_ISTRUE      JENTRY_ISTRUE
-
-/* HENTRY_ISHASH, HENTRY_ISARRAY and HENTRY_ISSCALAR is only used in send/recv */
-#define HENTRY_ISHASH      JENTRY_ISOBJECT
-#define HENTRY_ISARRAY     JENTRY_ISARRAY
-#define HENTRY_ISSCALAR        JENTRY_ISSCALAR
-
-#define HENTRY_POSMASK     JENTRY_POSMASK
-#define HENTRY_TYPEMASK    JENTRY_TYPEMASK
-
-#define HSE_ISFIRST(he_)       JBE_ISFIRST(he_)
-#define HSE_ISSTRING(he_)      JBE_ISSTRING(he_)
-#define HSE_ISNUMERIC(he_)         JBE_ISNUMERIC(he_)
-#define HSE_ISNEST(he_)        JBE_ISNEST(he_)
-#define HSE_ISNULL(he_)        JBE_ISNULL(he_)
-#define HSE_ISBOOL(he_)        JBE_ISBOOL(he_)
-#define HSE_ISBOOL_TRUE(he_)   JBE_ISBOOL_TRUE(he_)
-#define HSE_ISBOOL_FALSE(he_)  JBE_ISBOOL_FALSE(he_)
-
-#define HSE_ENDPOS(he_)        JBE_ENDPOS(he_)
-#define HSE_OFF(he_)           JBE_OFF(he_)
-#define HSE_LEN(he_)           JBE_LEN(he_)
+typedef struct
+{
+   uint32      entry;
+} HEntry;
+
+#define HENTRY_ISFIRST 0x80000000
+#define HENTRY_ISNULL  0x40000000
+#define HENTRY_POSMASK 0x3FFFFFFF
+
+/* note possible multiple evaluations, also access to prior array element */
+#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]))
 
 /*
- * determined by the size of "endpos" (ie HENTRY_POSMASK)
+ * determined by the size of "endpos" (ie HENTRY_POSMASK), 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         HENTRY_POSMASK
-#define HSTORE_MAX_VALUE_LEN   HENTRY_POSMASK
+#define HSTORE_MAX_KEY_LEN 0x3FFFFFFF
+#define HSTORE_MAX_VALUE_LEN 0x3FFFFFFF
 
-typedef Jsonb HStore;
+typedef struct
+{
+   int32       vl_len_;        /* varlena header (do not touch directly!) */
+   uint32      size_;          /* flags and number of items in hstore */
+   /* array of HEntry follows */
+} HStore;
 
 /*
  * It's not possible to get more than 2^28 items into an hstore, so we reserve
@@ -62,82 +56,126 @@ typedef Jsonb HStore;
  * limit for an hstore full of 4-byte keys and null values.  Therefore, we
  * don't explicitly check the format-imposed limit.
  */
-#define HS_FLAG_NEWVERSION         JB_FLAG_HSTORE_COMPAT
-#define HS_FLAG_ARRAY          JB_FLAG_ARRAY
-#define HS_FLAG_HASH           JB_FLAG_OBJECT
-#define HS_FLAG_SCALAR         JB_FLAG_SCALAR
-
-#define HS_COUNT_MASK          0x0FFFFFFF
-
-#define HS_ISEMPTY(hsp_)       JB_ISEMPTY(hsp_)
-#define HS_ROOT_COUNT(hsp_)    JB_ROOT_COUNT(hsp_)
-#define HS_ROOT_IS_HASH(hsp_)  JB_ROOT_IS_OBJECT(hsp_)
-#define HS_ROOT_IS_ARRAY(hsp_)     JB_ROOT_IS_ARRAY(hsp_)
-#define HS_ROOT_IS_SCALAR(hsp_) JB_ROOT_IS_SCALAR(hsp_)
+#define HS_FLAG_NEWVERSION 0x80000000
 
-/* DatumGetHStoreP includes support for reading old-format hstore values */
-extern HStore *hstoreUpgrade(Datum orig);
+#define HS_COUNT(hsp_) ((hsp_)->size_ & 0x0FFFFFFF)
+#define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION)
 
-#define DatumGetHStoreP(d) hstoreUpgrade(d)
-
-#define PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x))
-
-typedef JsonbPair HStorePair;
-typedef JsonbValue HStoreValue;
-
-/* JsonbValue.type renaming */
-#define hsvNull        jbvNull
-#define hsvString  jbvString
-#define hsvNumeric jbvNumeric
-#define hsvBool        jbvBool
-#define hsvArray   jbvArray
-#define hsvHash        jbvHash
-#define hsvBinary  jbvBinary
 
 /*
- * hstore support functions, they are mostly the same as jsonb
+ * "x" comes from an existing HS_COUNT() (as discussed, <= INT_MAX/24) or a
+ * Pairs array length (due to MaxAllocSize, <= INT_MAX/40).  "lenstr" is no
+ * more than INT_MAX, that extreme case arising in hstore_from_arrays().
+ * Therefore, this calculation is limited to about INT_MAX / 5 + INT_MAX.
  */
+#define HSHRDSIZE  (sizeof(HStore))
+#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) )
 
-#define WHS_KEY            WJB_KEY
-#define WHS_VALUE          WJB_VALUE
-#define WHS_ELEM               WJB_ELEM
-#define WHS_BEGIN_ARRAY    WJB_BEGIN_ARRAY
-#define WHS_END_ARRAY      WJB_END_ARRAY
-#define WHS_BEGIN_HASH     WJB_BEGIN_OBJECT
-#define WHS_END_HASH        WJB_END_OBJECT
-
-#define walkUncompressedHStore(v, cb, cb_arg)      walkUncompressedJsonb((v), (cb), (cb_arg))
-#define compareHStoreStringValue(a, b, arg)            compareJsonbStringValue((a), (b), (arg))
-#define compareHStorePair(a, b, arg)               compareJsonbPair((a), (b), (arg))
+/* note multiple evaluations of x */
+#define ARRPTR(x)      ( (HEntry*) ( (HStore*)(x) + 1 ) )
+#define STRPTR(x)      ( (char*)(ARRPTR(x) + HS_COUNT((HStore*)(x)) * 2) )
 
-#define compareHStoreBinaryValue(a, b)             compareJsonbBinaryValue((a), (b))
-#define compareHStoreValue(a, b)                   compareJsonbValue((a), (b))
+/* note multiple/non evaluations */
+#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]))
 
-#define findUncompressedHStoreValueByValue(buffer, flags, lowbound, key)   \
-   findUncompressedJsonbValueByValue((buffer), (flags), (lowbound), (key))
-#define findUncompressedHStoreValue(buffer, flags, lowbound, key, keylen)  \
-   findUncompressedJsonbValue((buffer), (flags), (lowbound), (key), (keylen))
+/*
+ * currently, these following macros are the _only_ places that rely
+ * on internal knowledge of HEntry. Everything else should be using
+ * the above macros. Exception: the in-place upgrade in hstore_compat.c
+ * messes with entries directly.
+ */
 
-#define getHStoreValue(buffer, flags, i)           getJsonbValue((buffer), (flags), (i))
+/*
+ * 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)
 
-typedef ToJsonbState ToHStoreState;
-#define pushHStoreValue(state, r /* WHS_* */, v)   pushJsonbValue((state), (r), (v))
+/*
+ * 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 {                                                            \
+       int buflen = (ptr_) - (buf_);                               \
+       if ((count_))                                               \
+           ARRPTR(hsp_)[0].entry |= HENTRY_ISFIRST;                \
+       if ((count_) != HS_COUNT((hsp_)))                           \
+       {                                                           \
+           HS_SETCOUNT((hsp_),(count_));                           \
+           memmove(STRPTR(hsp_), (buf_), buflen);                  \
+       }                                                           \
+       SET_VARSIZE((hsp_), CALCDATASIZE((count_), buflen));        \
+   } while (0)
+
+/* ensure the varlena size of an existing hstore is correct */
+#define HS_FIXSIZE(hsp_,count_)                                            \
+   do {                                                                \
+       int bl = (count_) ? HSE_ENDPOS(ARRPTR(hsp_)[2*(count_)-1]) : 0; \
+       SET_VARSIZE((hsp_), CALCDATASIZE((count_),bl));                 \
+   } while (0)
 
-extern bool stringIsNumber(char *string, int len, bool jsonNumber);
+/* DatumGetHStoreP includes support for reading old-format hstore values */
+extern HStore *hstoreUpgrade(Datum orig);
 
-extern uint32 compressHStore(HStoreValue *v, char *buffer);
+#define DatumGetHStoreP(d) hstoreUpgrade(d)
 
-typedef JsonbIterator HStoreIterator;
+#define PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x))
 
-#define    HStoreIteratorInit(buffer)                  JsonbIteratorInit(buffer)
 
-#define HStoreIteratorGet(it, v, skipNested)   JsonbIteratorGet((it), (v), (skipNested))
+/*
+ * Pairs is a "decompressed" representation of one key/value pair.
+ * The two strings are not necessarily null-terminated.
+ */
+typedef struct
+{
+   char       *key;
+   char       *val;
+   size_t      keylen;
+   size_t      vallen;
+   bool        isnull;         /* value is null? */
+   bool        needfree;       /* need to pfree the value? */
+} Pairs;
 
-text* HStoreValueToText(HStoreValue *v);
+extern int hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen);
+extern HStore *hstorePairs(Pairs *pairs, int32 pcount, int32 buflen);
 
-extern HStoreValue* parseHStore(const char *str, int len, bool json);
+extern size_t hstoreCheckKeyLen(size_t len);
+extern size_t hstoreCheckValLen(size_t len);
 
-#define uniqueHStoreValue(v) uniqueJsonbValue(v)
+extern int hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen);
+extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs);
 
 #define HStoreContainsStrategyNumber   7
 #define HStoreExistsStrategyNumber     9
@@ -165,18 +203,4 @@ extern HStoreValue* parseHStore(const char *str, int len, bool json);
    extern int no_such_variable
 #endif
 
-/*
- * When using a GIN/GiST index for hstore, we choose to index both keys and values.
- * The storage format is "text" values, with K, V, or N prepended to the string
- * to indicate key, value, or null values.  (As of 9.1 it might be better to
- * store null values as nulls, but we'll keep it this way for on-disk
- * compatibility.)
- */
-#define ELEMFLAG    'E'
-#define KEYFLAG     'K'
-#define VALFLAG     'V'
-#define NULLFLAG    'N'
-
-
-
 #endif   /* __HSTORE_H__ */
index a2e58f7c0608405ea582edfe74c1113accddd1d0..6327a8e8bb58a5c6e32e8884690ee5b5ece53417 100644 (file)
@@ -6,14 +6,10 @@
  * There are three formats to consider:
  * 1) old contrib/hstore (referred to as hstore-old)
  * 2) prerelease pgfoundry hstore
- * 3) new contrib/hstore (v2)
- * 4) nested contrib/hstore (v3)
+ * 3) new contrib/hstore
  *
- * (3) and (4) are upward binary compatible.
  * (2) and (3) are identical except for the HS_FLAG_NEWVERSION
  * bit, which is set in (3) but not (2).
- * (4) has HS_FLAG_ARRAY, HS_FLAG_HASH, HS_FLAG_SCALAR bits after
- * HS_FLAG_NEWVERSION
  *
  * Values that are already in format (3), or which are
  * unambiguously in format (2), are handled by the first
@@ -109,48 +105,9 @@ typedef struct
                pos:31;
 } HOldEntry;
 
-/*
- * New Old version (new not-nested version of hstore, v2 version)
- * V2 and v3 (nested) are upward binary compatible. But
- * framework was fully changed. Keep here old definitions (v2)
- */
-
-
-typedef struct
-{
-   int32       vl_len_;        /* varlena header (do not touch directly!) */
-   uint32      size_;          /* flags and number of items in hstore */
-   /* array of HEntry follows */
-} HStoreV2;
-
-static int hstoreValidNewFormat(HStoreV2 *hs);
-static int hstoreValidOldFormat(HStoreV2 *hs);
-
-#define HS_COUNT(hsp_)     (HS_ISEMPTY(hsp_) ? 0 : ((hsp_)->size_ & HS_COUNT_MASK))
-#define HS_SETCOUNT(hsp_,c_)    ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION | ((hsp_)->size_ & ~HS_COUNT_MASK))
-
-/*
- * "x" comes from an existing HS_COUNT() (as discussed, <= INT_MAX/24) or a
- * Pairs array length (due to MaxAllocSize, <= INT_MAX/40).  "lenstr" is no
- * more than INT_MAX, that extreme case arising in hstore_from_arrays().
- * Therefore, this calculation is limited to about INT_MAX / 5 + INT_MAX.
- */
-#define HSHRDSIZE   (sizeof(HStoreV2))
-#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) )
-
-/* note multiple evaluations of x */
-#define ARRPTR(x)       ( (HEntry*) ( (HStoreV2*)(x) + 1 ) )
-#define STRPTR(x)       ( (char*)(ARRPTR(x) + HS_ROOT_COUNT((HStoreV2*)(x)) * 2) )
-
-/* note multiple/non evaluations */
-#define HS_KEYLEN(arr_,i_) (HSE_LEN((arr_)[2*(i_)]))
+static int hstoreValidNewFormat(HStore *hs);
+static int hstoreValidOldFormat(HStore *hs);
 
-/* ensure the varlena size of an existing hstore is correct */
-#define HS_FIXSIZE(hsp_,count_)                                         \
-   do {                                                                \
-       int bl = (count_) ? HSE_ENDPOS(ARRPTR(hsp_)[2*(count_)-1]) : 0; \
-       SET_VARSIZE((hsp_), CALCDATASIZE((count_),bl));                 \
-   } while (0)
 
 /*
  * Validity test for a new-format hstore.
@@ -159,7 +116,7 @@ static int  hstoreValidOldFormat(HStoreV2 *hs);
  * 2 = exactly valid
  */
 static int
-hstoreValidNewFormat(HStoreV2 *hs)
+hstoreValidNewFormat(HStore *hs)
 {
    int         count = HS_COUNT(hs);
    HEntry     *entries = ARRPTR(hs);
@@ -211,7 +168,7 @@ hstoreValidNewFormat(HStoreV2 *hs)
  * 2 = exactly valid
  */
 static int
-hstoreValidOldFormat(HStoreV2 *hs)
+hstoreValidOldFormat(HStore *hs)
 {
    int         count = hs->size_;
    HOldEntry  *entries = (HOldEntry *) ARRPTR(hs);
@@ -271,32 +228,23 @@ hstoreValidOldFormat(HStoreV2 *hs)
    return 2;
 }
 
+
 /*
  * hstoreUpgrade: PG_DETOAST_DATUM plus support for conversion of old hstores
  */
 HStore *
 hstoreUpgrade(Datum orig)
 {
-   HStoreV2       *hs = (HStoreV2 *) PG_DETOAST_DATUM(orig);
+   HStore     *hs = (HStore *) PG_DETOAST_DATUM(orig);
    int         valid_new;
    int         valid_old;
    bool        writable;
 
    /* Return immediately if no conversion needed */
-   if (VARSIZE_ANY(hs) <= VARHDRSZ ||
-       (hs->size_ & HS_FLAG_NEWVERSION) ||
+   if ((hs->size_ & HS_FLAG_NEWVERSION) ||
        hs->size_ == 0 ||
        (VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0]))))
-   {
-       if (VARSIZE_ANY_EXHDR(hs) == sizeof(hs->size_))
-       {
-           /* 'new' format but not nested. And empty */
-           hs = palloc(sizeof(VARHDRSZ));
-           SET_VARSIZE(hs, VARHDRSZ);
-       }
-
-       return (HStore*)hs;
-   }
+       return hs;
 
    valid_new = hstoreValidNewFormat(hs);
    valid_old = hstoreValidOldFormat(hs);
@@ -318,7 +266,7 @@ hstoreUpgrade(Datum orig)
                HS_SETCOUNT(hs, HS_COUNT(hs));
                HS_FIXSIZE(hs, HS_COUNT(hs));
            }
-           return (HStore*)hs;
+           return hs;
        }
        else
        {
@@ -375,7 +323,7 @@ hstoreUpgrade(Datum orig)
     */
 
    if (!writable)
-       hs = (HStoreV2 *) PG_DETOAST_DATUM_COPY(orig);
+       hs = (HStore *) PG_DETOAST_DATUM_COPY(orig);
 
    {
        int         count = hs->size_;
@@ -404,7 +352,7 @@ hstoreUpgrade(Datum orig)
        HS_FIXSIZE(hs, count);
    }
 
-   return (HStore*)hs;
+   return hs;
 }
 
 
@@ -413,7 +361,7 @@ Datum       hstore_version_diag(PG_FUNCTION_ARGS);
 Datum
 hstore_version_diag(PG_FUNCTION_ARGS)
 {
-   HStoreV2       *hs = (HStoreV2 *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+   HStore     *hs = (HStore *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
    int         valid_new = hstoreValidNewFormat(hs);
    int         valid_old = hstoreValidOldFormat(hs);
 
index be59ed2abcfda9a23b51fd94ede6ae413d157423..2007801cf0c97300cc201af5a19cdfd9f3c14a04 100644 (file)
@@ -6,11 +6,21 @@
 #include "access/gin.h"
 #include "access/skey.h"
 #include "catalog/pg_type.h"
-#include "utils/builtins.h"
 
 #include "hstore.h"
 
 
+/*
+ * When using a GIN index for hstore, we choose to index both keys and values.
+ * The storage format is "text" values, with K, V, or N prepended to the string
+ * to indicate key, value, or null values. (As of 9.1 it might be better to
+ * store null values as nulls, but we'll keep it this way for on-disk
+ * compatibility.)
+ */
+#define KEYFLAG        'K'
+#define VALFLAG        'V'
+#define NULLFLAG   'N'
+
 PG_FUNCTION_INFO_V1(gin_extract_hstore);
 Datum      gin_extract_hstore(PG_FUNCTION_ARGS);
 
@@ -31,90 +41,36 @@ makeitem(char *str, int len, char flag)
    return item;
 }
 
-static text *
-makeitemFromValue(HStoreValue *v, char flag)
-{
-   text        *item;
-   char        *cstr;
-
-   switch(v->type)
-   {
-       case hsvNull:
-           item = makeitem(NULL, 0, NULLFLAG);
-           break;
-       case hsvBool:
-           item = makeitem((v->boolean) ? " t" : " f", 2, flag);
-           break;
-       case hsvNumeric:
-           /*
-            * It's needed to get some text representaion of
-            * numeric independed from locale setting and 
-            * preciosion. We use hashed value - it's safe
-            * because recheck flag will be set anyway
-            */
-           cstr = palloc(8 /* hex numbers */ + 1 /* \0 */);
-           snprintf(cstr, 9, "%08x",  DatumGetInt32(DirectFunctionCall1(hash_numeric,
-                                                   NumericGetDatum(v->numeric))));
-           item = makeitem(cstr, 8, flag);
-           pfree(cstr);
-           break;
-       case hsvString:
-           item = makeitem(v->string.val, v->string.len, flag);
-           break;
-       default:
-           elog(ERROR, "wrong hstore scalar type");
-   }
-
-   return item;
-}
-
-
 Datum
 gin_extract_hstore(PG_FUNCTION_ARGS)
 {
-   HStore          *hs = PG_GETARG_HS(0);
-   int32           *nentries = (int32 *) PG_GETARG_POINTER(1);
-   Datum           *entries = NULL;
-   int             total = 2 * HS_ROOT_COUNT(hs);
-   int             i = 0, r;
-   HStoreIterator  *it;
-   HStoreValue     v;
-
-   if (total == 0)
-   {
-       *nentries = 0;
-       PG_RETURN_POINTER(NULL);
-   }
-
-   entries = (Datum *) palloc(sizeof(Datum) * total);
+   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_COUNT(hs);
+   int         i;
 
-   it = HStoreIteratorInit(VARDATA(hs));
+   *nentries = 2 * count;
+   if (count)
+       entries = (Datum *) palloc(sizeof(Datum) * 2 * count);
 
-   while((r = HStoreIteratorGet(&it, &v, false)) != 0)
+   for (i = 0; i < count; ++i)
    {
-       if (i >= total)
-       {
-           total *= 2;
-           entries = (Datum *) repalloc(entries, sizeof(Datum) * total);
-       }
+       text       *item;
 
-       switch(r)
-       {
-           case WHS_KEY:
-               entries[i++] = PointerGetDatum(makeitemFromValue(&v, KEYFLAG));
-               break;
-           case WHS_VALUE:
-               entries[i++] = PointerGetDatum(makeitemFromValue(&v, VALFLAG));
-               break;
-           case WHS_ELEM:
-               entries[i++] = PointerGetDatum(makeitemFromValue(&v, ELEMFLAG));
-               break;
-           default:
-               break;
-       }
-   }
+       item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i),
+                       KEYFLAG);
+       entries[2 * i] = PointerGetDatum(item);
 
-   *nentries = i;
+       if (HS_VALISNULL(hsent, i))
+           item = makeitem(NULL, 0, NULLFLAG);
+       else
+           item = makeitem(HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i),
+                           VALFLAG);
+       entries[2 * i + 1] = PointerGetDatum(item);
+   }
 
    PG_RETURN_POINTER(entries);
 }
@@ -173,8 +129,7 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
            /* Nulls in the array are ignored, cf hstoreArrayToPairs */
            if (key_nulls[i])
                continue;
-           item = makeitem(VARDATA(key_datums[i]),
-                           VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG);
+           item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG);
            entries[j++] = PointerGetDatum(item);
        }
 
@@ -256,196 +211,3 @@ gin_consistent_hstore(PG_FUNCTION_ARGS)
 
    PG_RETURN_BOOL(res);
 }
-
-PG_FUNCTION_INFO_V1(gin_consistent_hstore_hash);
-Datum      gin_consistent_hstore_hash(PG_FUNCTION_ARGS);
-
-Datum
-gin_consistent_hstore_hash(PG_FUNCTION_ARGS)
-{
-   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;
-   int32       i;
-
-   if (strategy == HStoreContainsStrategyNumber)
-   {
-       /*
-        * Index doesn't have information about correspondence of keys and
-        * values, so we need recheck.  However, if not all the keys are
-        * present, we can fail at once.
-        */
-       *recheck = true;
-       for (i = 0; i < nkeys; i++)
-       {
-           if (!check[i])
-           {
-               res = false;
-               break;
-           }
-       }
-   }
-   else
-       elog(ERROR, "unrecognized strategy number: %d", strategy);
-
-   PG_RETURN_BOOL(res);
-}
-
-PG_FUNCTION_INFO_V1(gin_extract_hstore_hash);
-Datum      gin_extract_hstore_hash(PG_FUNCTION_ARGS);
-
-typedef struct PathHashStack
-{
-   pg_crc32              hash_state;
-   struct PathHashStack *next;
-} PathHashStack;
-
-#define PATH_SEPARATOR ("\0")
-
-static void
-hash_value(HStoreValue *v, PathHashStack *stack)
-{
-   switch(v->type)
-   {
-       case hsvNull:
-           COMP_CRC32(stack->hash_state, "NULL", 5 /* include trailing \0 */);
-           break;
-       case hsvBool:
-           COMP_CRC32(stack->hash_state, (v->boolean) ? " t" : " f", 2 /* include trailing \0 */);
-           break;
-       case hsvNumeric:
-           stack->hash_state ^= DatumGetInt32(DirectFunctionCall1(hash_numeric,
-                                               NumericGetDatum(v->numeric)));
-           break;
-       case hsvString:
-           COMP_CRC32(stack->hash_state, v->string.val, v->string.len);
-           break;
-       default:
-           elog(ERROR, "wrong hstore scalar type");
-           break;
-   }
-}
-
-Datum
-gin_extract_hstore_hash(PG_FUNCTION_ARGS)
-{
-   HStore          *hs = PG_GETARG_HS(0);
-   int32           *nentries = (int32 *) PG_GETARG_POINTER(1);
-   Datum           *entries = NULL;
-   int             total = 2 * HS_ROOT_COUNT(hs);
-   int             i = 0, r;
-   HStoreIterator  *it;
-   HStoreValue     v;
-   PathHashStack   tail;
-   PathHashStack   *stack, *tmp;
-   pg_crc32        path_crc32;
-
-   if (total == 0)
-   {
-       *nentries = 0;
-       PG_RETURN_POINTER(NULL);
-   }
-
-   entries = (Datum *) palloc(sizeof(Datum) * total);
-
-   it = HStoreIteratorInit(VARDATA(hs));
-
-   tail.next = NULL;
-   INIT_CRC32(tail.hash_state);
-   stack = &tail;
-
-   /*
-    * Calculate hashes of all key_1.key_2. ... .key_n.value paths as entries.
-    * Order of array elements doesn't matter so array keys are empty in path.
-    * For faster calculation of hashes use stack for precalculated hashes
-    * of prefixes.
-    */
-   while((r = HStoreIteratorGet(&it, &v, false)) != 0)
-   {
-       if (i >= total)
-       {
-           total *= 2;
-           entries = (Datum *) repalloc(entries, sizeof(Datum) * total);
-       }
-
-       switch(r)
-       {
-           case WHS_BEGIN_ARRAY:
-               tmp = stack;
-               stack = (PathHashStack *)palloc(sizeof(PathHashStack));
-               stack->next = tmp;
-               stack->hash_state = tmp->hash_state;
-               COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
-               break;
-           case WHS_BEGIN_HASH:
-               /* Preserve stack item for key */
-               tmp = stack;
-               stack = (PathHashStack *)palloc(sizeof(PathHashStack));
-               stack->next = tmp;
-               break;
-           case WHS_KEY:
-               /* Calc hash of key and separated into preserved stack item */
-               stack->hash_state = stack->next->hash_state;
-               hash_value(&v, stack);
-               COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
-               break;
-           case WHS_VALUE:
-           case WHS_ELEM:
-               hash_value(&v, stack);
-               path_crc32 = stack->hash_state;
-               FIN_CRC32(path_crc32);
-               entries[i++] = path_crc32;
-               break;
-           case WHS_END_ARRAY:
-           case WHS_END_HASH:
-               /* Pop stack item */
-               tmp = stack->next;
-               pfree(stack);
-               stack = tmp;
-               break;
-           default:
-               break;
-       }
-   }
-
-   *nentries = i;
-
-   PG_RETURN_POINTER(entries);
-}
-
-PG_FUNCTION_INFO_V1(gin_extract_hstore_hash_query);
-Datum      gin_extract_hstore_hash_query(PG_FUNCTION_ARGS);
-
-Datum
-gin_extract_hstore_hash_query(PG_FUNCTION_ARGS)
-{
-   int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
-   StrategyNumber strategy = PG_GETARG_UINT16(2);
-   int32      *searchMode = (int32 *) PG_GETARG_POINTER(6);
-   Datum      *entries;
-
-   if (strategy == HStoreContainsStrategyNumber)
-   {
-       /* Query is an hstore, so just apply gin_extract_hstore... */
-       entries = (Datum *)
-           DatumGetPointer(DirectFunctionCall2(gin_extract_hstore_hash,
-                                               PG_GETARG_DATUM(0),
-                                               PointerGetDatum(nentries)));
-       /* ... except that "contains {}" requires a full index scan */
-       if (entries == NULL)
-           *searchMode = GIN_SEARCH_MODE_ALL;
-   }
-   else
-   {
-       elog(ERROR, "unrecognized strategy number: %d", strategy);
-       entries = NULL;         /* keep compiler quiet */
-   }
-
-   PG_RETURN_POINTER(entries);
-}
index 5d62adc3a7182309434eab2793aa4a54614715b3..900118014249df96b69792581ea223e693b81b43 100644 (file)
@@ -6,9 +6,8 @@
 #include "access/gist.h"
 #include "access/skey.h"
 #include "catalog/pg_type.h"
-#include "utils/builtins.h"
-#include "utils/pg_crc.h"
 
+#include "crc32.h"
 #include "hstore.h"
 
 /* bigint defines */
@@ -79,14 +78,14 @@ Datum       ghstore_out(PG_FUNCTION_ARGS);
 Datum
 ghstore_in(PG_FUNCTION_ARGS)
 {
-   elog(ERROR, "not implemented");
+   elog(ERROR, "Not implemented");
    PG_RETURN_DATUM(0);
 }
 
 Datum
 ghstore_out(PG_FUNCTION_ARGS)
 {
-   elog(ERROR, "not implemented");
+   elog(ERROR, "Not implemented");
    PG_RETURN_DATUM(0);
 }
 
@@ -106,67 +105,6 @@ Datum      ghstore_picksplit(PG_FUNCTION_ARGS);
 Datum      ghstore_union(PG_FUNCTION_ARGS);
 Datum      ghstore_same(PG_FUNCTION_ARGS);
 
-static int
-crc32_HStoreValue(HStoreValue *v, uint32 r)
-{
-   int     crc;
-   char    flag = '\0';
-
-   INIT_CRC32(crc);
-
-   switch(r)
-   {
-       case WHS_KEY:
-           flag = KEYFLAG;
-           break;
-       case WHS_VALUE:
-           flag = VALFLAG;
-           break;
-       case WHS_ELEM:
-           flag = ELEMFLAG;
-           break;
-       default:
-           break;
-   }
-
-   COMP_CRC32(crc, &flag, 1);
-
-   switch(v->type)
-   {
-       case hsvString:
-           COMP_CRC32(crc, v->string.val, v->string.len);
-           break;
-       case hsvBool:
-           flag = (v->boolean) ? 't' : 'f';
-           COMP_CRC32(crc, &flag, 1);
-           break;
-       case hsvNumeric:
-           crc = DatumGetInt32(DirectFunctionCall1(hash_numeric,
-                               NumericGetDatum(v->numeric)));
-           break;
-       default:
-           elog(ERROR, "wrong hstore scalar type");
-   }
-
-   FIN_CRC32(crc);
-   return crc;
-}
-
-static int
-crc32_Key(char *buf, int sz)
-{
-   int     crc;
-   char    flag = KEYFLAG;
-
-   INIT_CRC32(crc);
-
-   COMP_CRC32(crc, &flag, 1);
-   COMP_CRC32(crc, buf, sz);
-
-   FIN_CRC32(crc);
-   return crc;
-}
-
 Datum
 ghstore_compress(PG_FUNCTION_ARGS)
 {
@@ -175,26 +113,25 @@ ghstore_compress(PG_FUNCTION_ARGS)
 
    if (entry->leafkey)
    {
-       GISTTYPE        *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
-       HStore          *val = DatumGetHStoreP(entry->key);
+       GISTTYPE   *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+       HStore     *val = DatumGetHStoreP(entry->key);
+       HEntry     *hsent = ARRPTR(val);
+       char       *ptr = STRPTR(val);
+       int         count = HS_COUNT(val);
+       int         i;
 
        SET_VARSIZE(res, CALCGTSIZE(0));
 
-       if (!HS_ISEMPTY(val))
+       for (i = 0; i < count; ++i)
        {
-           int             r;
-           HStoreIterator  *it = HStoreIteratorInit(VARDATA(val));
-           HStoreValue     v;
+           int         h;
 
-           while((r = HStoreIteratorGet(&it, &v, false)) != 0)
+           h = crc32_sz((char *) HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i));
+           HASH(GETSIGN(res), h);
+           if (!HS_VALISNULL(hsent, i))
            {
-               if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) &&
-                   v.type != hsvNull)
-               {
-                   int   h = crc32_HStoreValue(&v, r);
-
-                   HASH(GETSIGN(res), h);
-               }
+               h = crc32_sz((char *) HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i));
+               HASH(GETSIGN(res), h);
            }
        }
 
@@ -459,8 +396,7 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
        datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
        SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
        datum_l->flag = 0;
-       memcpy((void *) GETSIGN(datum_l),
-              (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC))
+       memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC))
            ;
    }
    if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
@@ -474,8 +410,7 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
        datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
        SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
        datum_r->flag = 0;
-       memcpy((void *) GETSIGN(datum_r),
-              (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
+       memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
    }
 
    maxoff = OffsetNumberNext(maxoff);
@@ -555,6 +490,7 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
    PG_RETURN_POINTER(v);
 }
 
+
 Datum
 ghstore_consistent(PG_FUNCTION_ARGS)
 {
@@ -577,127 +513,86 @@ ghstore_consistent(PG_FUNCTION_ARGS)
    if (strategy == HStoreContainsStrategyNumber ||
        strategy == HStoreOldContainsStrategyNumber)
    {
-       BITVECP     qe;
+       HStore     *query = PG_GETARG_HS(1);
+       HEntry     *qe = ARRPTR(query);
+       char       *qv = STRPTR(query);
+       int         count = HS_COUNT(query);
        int         i;
 
-       qe = fcinfo->flinfo->fn_extra;
-       if (qe == NULL)
+       for (i = 0; res && i < count; ++i)
        {
-           HStore          *query = PG_GETARG_HS(1);
-
-           qe = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(BITVEC));
-           memset(qe, 0, sizeof(BITVEC));
+           int         crc = crc32_sz((char *) HS_KEY(qe, qv, i), HS_KEYLEN(qe, i));
 
-           if (!HS_ISEMPTY(query))
+           if (GETBIT(sign, HASHVAL(crc)))
            {
-               int             r;
-               HStoreIterator  *it = HStoreIteratorInit(VARDATA(query));
-               HStoreValue     v;
-
-               while((r = HStoreIteratorGet(&it, &v, false)) != 0)
+               if (!HS_VALISNULL(qe, i))
                {
-                   if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) && v.type != hsvNull)
-                   {
-                       int   crc = crc32_HStoreValue(&v, r);
-
-                       HASH(qe, crc);
-                   }
+                   crc = crc32_sz((char *) HS_VAL(qe, qv, i), HS_VALLEN(qe, i));
+                   if (!GETBIT(sign, HASHVAL(crc)))
+                       res = false;
                }
            }
-
-           fcinfo->flinfo->fn_extra = qe;
-       }
-
-       LOOPBYTE
-       {
-           if ((sign[i] & qe[i]) != qe[i])
-           {
+           else
                res = false;
-               break;
-           }
        }
    }
    else if (strategy == HStoreExistsStrategyNumber)
    {
-       int     *qval;
+       text       *query = PG_GETARG_TEXT_PP(1);
+       int         crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
 
-       qval = fcinfo->flinfo->fn_extra;
-       if (qval == NULL)
-       {
-           text       *query = PG_GETARG_TEXT_PP(1);
-           int         crc = crc32_Key(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
-
-           qval = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(*qval));
-           *qval = HASHVAL(crc);
-
-           fcinfo->flinfo->fn_extra = qval;
-       }
-
-       res = (GETBIT(sign, *qval)) ? true : false;
+       res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
    }
-   else if (strategy == HStoreExistsAllStrategyNumber ||
-            strategy == HStoreExistsAnyStrategyNumber)
+   else if (strategy == HStoreExistsAllStrategyNumber)
    {
-       BITVECP arrentry;
-       int     i;
-
-       arrentry = fcinfo->flinfo->fn_extra;
-       if (arrentry == NULL)
-       {
-           ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
-           Datum      *key_datums;
-           bool       *key_nulls;
-           int         key_count;
+       ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
+       Datum      *key_datums;
+       bool       *key_nulls;
+       int         key_count;
+       int         i;
 
-           arrentry = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
-                                         sizeof(BITVEC));
-           memset(arrentry, 0, sizeof(BITVEC));
+       deconstruct_array(query,
+                         TEXTOID, -1, false, 'i',
+                         &key_datums, &key_nulls, &key_count);
 
-           deconstruct_array(query,
-                             TEXTOID, -1, false, 'i',
-                             &key_datums, &key_nulls, &key_count);
+       for (i = 0; res && i < key_count; ++i)
+       {
+           int         crc;
 
-           for (i = 0; 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;
 
-               if (key_nulls[i])
-                   continue;
-               crc = crc32_Key(VARDATA(key_datums[i]),
-                               VARSIZE(key_datums[i]) - VARHDRSZ);
-               HASH(arrentry, crc);
-           }
+       deconstruct_array(query,
+                         TEXTOID, -1, false, 'i',
+                         &key_datums, &key_nulls, &key_count);
 
-           fcinfo->flinfo->fn_extra = arrentry;
-       }
+       res = FALSE;
 
-       if (strategy == HStoreExistsAllStrategyNumber)
+       for (i = 0; !res && i < key_count; ++i)
        {
-           LOOPBYTE
-           {
-               if ((sign[i] & arrentry[i]) != arrentry[i])
-               {
-                   res = false;
-                   break;
-               }
-           }
-       }
-       else /* HStoreExistsAnyStrategyNumber */
-       {
-           res = false;
+           int         crc;
 
-           LOOPBYTE
-           {
-               if (sign[i] & arrentry[i])
-               {
-                   res = true;
-                   break;
-               }
-           }
+           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);
+       elog(ERROR, "Unsupported strategy number: %d", strategy);
 
    PG_RETURN_BOOL(res);
 }
diff --git a/contrib/hstore/hstore_gram.y b/contrib/hstore/hstore_gram.y
deleted file mode 100644 (file)
index ff6e73c..0000000
+++ /dev/null
@@ -1,341 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * hstore_gram.y
- *    Grammar definition for hstore
- *
- * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
- *
- * contrib/hstore/hstore_gram.y
- *
- *-------------------------------------------------------------------------
- */
-
-%{
-#define YYPARSE_PARAM result  /* need this to pass a pointer (void *) to yyparse */
-
-#include "postgres.h"
-
-#include "fmgr.h"
-#include "utils/builtins.h"
-#include "hstore.h"
-
-/*
- * Bison doesn't allocate anything that needs to live across parser calls,
- * so we can easily have it use palloc instead of malloc.  This prevents
- * memory leaks if we error out during parsing.  Note this only works with
- * bison >= 2.0.  However, in bison 1.875 the default is to use alloca()
- * if possible, so there's not really much problem anyhow, at least if
- * you're building with gcc.
- */
-#define YYMALLOC palloc
-#define YYFREE   pfree
-
-/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
-#undef fprintf
-#define fprintf(file, fmt, msg)  fprintf_to_ereport(fmt, msg)
-
-static bool inputJSON = false;
-
-static void
-fprintf_to_ereport(const char *fmt, const char *msg)
-{
-   ereport(ERROR, (errmsg_internal("%s", msg)));
-}
-
-/* struct string is shared between scan and gram */
-typedef struct string {
-   char    *val;
-   int     len;
-   int     total;
-} string;
-#include <hstore_gram.h>
-
-/* flex 2.5.4 doesn't bother with a decl for this */
-int hstore_yylex(YYSTYPE * yylval_param);
-int hstore_yyparse(void *result);
-void hstore_yyerror(const char *message);
-
-static HStoreValue*
-makeHStoreValueString(HStoreValue* v, string *s)
-{
-   if (v == NULL)
-       v = palloc(sizeof(*v));
-
-   if (s == NULL)
-   {
-       v->type = jbvNull;
-       v->size = sizeof(JEntry);
-   }
-   else if (s->len > JENTRY_POSMASK)
-   {
-       elog(ERROR, "string is too long");
-   }
-   else
-   {
-       v->type = jbvString;
-       v->string.val = s->val;
-       v->string.len = s->len;
-       v->size = sizeof(JEntry) + s->len;
-
-   }
-
-   return v;
-}
-
-static HStoreValue*
-makeHStoreValueNumeric(string *s)
-{
-   Numeric         n = NULL;
-   HStoreValue     *v;
-   MemoryContext   ccxt = CurrentMemoryContext;
-
-   /*
-    * ignore ERRCODE_INVALID_TEXT_REPRESENTATION in parse: our
-    * test stringIsNumber could be not agree with numeric_in
-    */
-
-   PG_TRY();
-   {
-       n = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(s->val), 0, -1));
-   }
-   PG_CATCH();
-   {
-       ErrorData       *errdata;
-       MemoryContext   ecxt;
-
-       ecxt = MemoryContextSwitchTo(ccxt);
-       errdata = CopyErrorData();
-       if (errdata->sqlerrcode == ERRCODE_INVALID_TEXT_REPRESENTATION)
-       {
-           FlushErrorState();
-           n = NULL;
-       }
-       else
-       {
-           MemoryContextSwitchTo(ecxt);
-           PG_RE_THROW();
-       }
-   }
-   PG_END_TRY();
-
-   if (n != NULL)
-   {
-       v = palloc(sizeof(*v));
-       v->type = jbvNumeric;
-       v->numeric = n;
-       v->size = 2*sizeof(JEntry) + VARSIZE_ANY(n);
-   }
-   else
-   {
-       v = makeHStoreValueString(NULL, s);
-   }
-
-   return v;
-}
-
-static HStoreValue*
-makeHStoreValueBool(bool val) {
-   HStoreValue *v = palloc(sizeof(*v));
-
-   v->type = jbvBool;
-   v->boolean = val;
-   v->size = sizeof(JEntry);
-
-   return v;
-}
-
-static HStoreValue*
-makeHStoreValueArray(List *list)
-{
-   HStoreValue *v = palloc(sizeof(*v));
-
-   v->type = jbvArray;
-   v->array.scalar = false;
-   v->array.nelems = list_length(list);
-   v->size = sizeof(uint32) /* header */ + sizeof(JEntry) /* parent's entry */ + sizeof(JEntry) - 1 /*alignment*/;
-
-   if (v->array.nelems > 0)
-   {
-       ListCell    *cell;
-       int         i = 0;
-
-       v->array.elems = palloc(sizeof(HStoreValue) * v->array.nelems);
-
-       foreach(cell, list)
-       {
-           HStoreValue *s = (HStoreValue*)lfirst(cell);
-
-           v->size += s->size;
-
-           v->array.elems[i++] = *s;
-
-           if (v->size > JENTRY_POSMASK)
-               elog(ERROR, "array is too long");
-       }
-   }
-   else
-   {
-       v->array.elems = NULL;
-   }
-
-   return v;
-}
-
-static HStoreValue*
-makeHStoreValuePairs(List *list)
-{
-   HStoreValue *v = palloc(sizeof(*v));
-
-   v->type = jbvHash;
-   v->hash.npairs = list_length(list);
-   v->size = sizeof(uint32) /* header */ + sizeof(JEntry) /* parent's entry */ + sizeof(JEntry) - 1 /*alignment*/;
-
-   if (v->hash.npairs > 0)
-   {
-       ListCell    *cell;
-       int         i = 0;
-
-       v->hash.pairs = palloc(sizeof(HStorePair) * v->hash.npairs);
-
-       foreach(cell, list)
-       {
-           HStorePair  *s = (HStorePair*)lfirst(cell);
-
-           v->size += s->key.size + s->value.size;
-           v->hash.pairs[i].order = i;
-           v->hash.pairs[i++] = *s;
-
-           if (v->size > JENTRY_POSMASK)
-               elog(ERROR, "%s is too long", inputJSON ? "json" : "hstore");
-       }
-
-       uniqueHStoreValue(v);
-   }
-   else
-   {
-       v->hash.pairs = NULL;
-   }
-
-   return v;
-}
-
-static HStorePair*
-makeHStorePair(string *key, HStoreValue *value) {
-   HStorePair  *v = palloc(sizeof(*v));
-
-   makeHStoreValueString(&v->key, key);
-   v->value = *value;
-
-   return v;
-}
-
-%}
-
-/* BISON Declarations */
-%pure-parser
-%expect 0
-%name-prefix="hstore_yy"
-%error-verbose
-
-%union {
-   string          str;
-   Numeric         numeric;
-   List            *elems;         /* list of HStoreValue */
-   List            *pairs;         /* list of HStorePair */
-
-   HStoreValue     *hvalue;
-   HStorePair      *pair;
-}
-
-%token <str>           DELIMITER_P NULL_P STRING_P TRUE_P FALSE_P
-                       NUMERIC_P
-
-%type  <hvalue>        result hstore value scalar_value
-%type  <str>           key
-
-%type  <pair>          pair
-
-%type  <elems>         value_list
-%type  <pairs>         pair_list
-
-/* Grammar follows */
-%%
-
-result:
-   pair_list                       {
-                                       if (inputJSON)
-                                           elog(ERROR, "wrong json representation");
-                                        *((HStoreValue**)result) = makeHStoreValuePairs($1);
-                                   }
-   | hstore                        {
-                                       if ($1->type == jbvNull)
-                                           *((HStoreValue**)result) = NULL;
-                                       else
-                                           *((HStoreValue**)result) = $1;
-                                   }
-   | scalar_value                  {
-                                       *((HStoreValue**)result) = makeHStoreValueArray(lappend(NIL, $1));
-                                       (*((HStoreValue**)result))->array.scalar = true;
-                                   }
-   | /* EMPTY */                   { *((HStoreValue**)result) = NULL; }
-   ;
-
-hstore:
-   '{' pair_list '}'               { $$ = makeHStoreValuePairs($2); }
-   | '[' value_list ']'            { $$ = makeHStoreValueArray($2); }
-   | '[' value ']'                 { $$ = makeHStoreValueArray(lappend(NIL, $2)); }
-   | '{' value_list '}'            {
-                                       if (inputJSON)
-                                           elog(ERROR, "wrong json representation");
-                                       $$ = makeHStoreValueArray($2);
-                                   }
-   | '{' value '}'                 {
-                                       if (inputJSON)
-                                           elog(ERROR, "wrong json representation");
-                                       $$ = makeHStoreValueArray(lappend(NIL, $2));
-                                   }
-   | '{' '}'                       { $$ = makeHStoreValuePairs(NIL); }
-   | '[' ']'                       { $$ = makeHStoreValueArray(NIL); }
-   ;
-
-scalar_value:
-   NULL_P                          { $$ = makeHStoreValueString(NULL, NULL); }
-   | STRING_P                      { $$ = makeHStoreValueString(NULL, &$1); }
-   | TRUE_P                        { $$ = makeHStoreValueBool(true); }
-   | FALSE_P                       { $$ = makeHStoreValueBool(false); }
-   | NUMERIC_P                     { $$ = makeHStoreValueNumeric(&$1); }
-   ;
-
-value:
-   scalar_value                    { $$ = $1; }
-   | hstore                        { $$ = $1; }
-   ;
-
-value_list:
-   value ',' value                 { $$ = lappend(lappend(NIL, $1), $3); }
-   | value_list ',' value          { $$ = lappend($1, $3); }
-   ;
-
-/*
- * key is always a string, not a bool or numeric
- */
-key:
-   STRING_P                        { $$ = $1; }
-   | TRUE_P                        { $$ = $1; }
-   | FALSE_P                       { $$ = $1; }
-   | NUMERIC_P                     { $$ = $1; }
-   | NULL_P                        { $$ = $1; }
-   ;
-
-pair:
-   key DELIMITER_P value           { $$ = makeHStorePair(&$1, $3); }
-   ;
-
-pair_list:
-   pair                            { $$ = lappend(NIL, $1); }
-   | pair_list ',' pair            { $$ = lappend($1, $3); }
-   ;
-
-%%
-
-#include "hstore_scan.c"
index d4f23c5062c1d37e7d81149b9536240ec81a3b7e..65e4438d3225d49ab9eb61f7b0ec627824d08f67 100644 (file)
@@ -2,23 +2,18 @@
  * contrib/hstore/hstore_io.c
  */
 #include "postgres.h"
-#include "miscadmin.h"
 
 #include <ctype.h>
 
 #include "access/htup_details.h"
 #include "catalog/pg_type.h"
-#include "catalog/pg_cast.h"
 #include "funcapi.h"
+#include "lib/stringinfo.h"
 #include "libpq/pqformat.h"
-#include "parser/parse_coerce.h"
 #include "utils/builtins.h"
-#include "utils/fmgroids.h"
 #include "utils/json.h"
-#include "utils/guc.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
-#include "utils/syscache.h"
 #include "utils/typcache.h"
 
 #include "hstore.h"
@@ -28,27 +23,332 @@ PG_MODULE_MAGIC;
 /* old names for C functions */
 HSTORE_POLLUTE(hstore_from_text, tconvert);
 
-/* GUC variables */
-static bool    pretty_print_var = false;
-#define SET_PRETTY_PRINT_VAR(x)        ((pretty_print_var) ? \
-                                    ((x) | PrettyPrint) : (x))
 
-static void recvHStore(StringInfo buf, HStoreValue *v, uint32 level,
-                      uint32 header);
-static Oid searchCast(Oid src, Oid dst, CoercionMethod *method);
+typedef struct
+{
+   char       *begin;
+   char       *ptr;
+   char       *cur;
+   char       *word;
+   int         wordlen;
+
+   Pairs      *pairs;
+   int         pcur;
+   int         plen;
+} HSParser;
+
+#define RESIZEPRSBUF \
+do { \
+       if ( state->cur - state->word + 1 >= state->wordlen ) \
+       { \
+               int32 clen = state->cur - state->word; \
+               state->wordlen *= 2; \
+               state->word = (char*)repalloc( (void*)state->word, state->wordlen ); \
+               state->cur = state->word + clen; \
+       } \
+} while (0)
+
+
+#define GV_WAITVAL 0
+#define GV_INVAL 1
+#define GV_INESCVAL 2
+#define GV_WAITESCIN 3
+#define GV_WAITESCESCIN 4
+
+static bool
+get_val(HSParser *state, bool ignoreeq, bool *escaped)
+{
+   int         st = GV_WAITVAL;
+
+   state->wordlen = 32;
+   state->cur = state->word = palloc(state->wordlen);
+   *escaped = false;
+
+   while (1)
+   {
+       if (st == GV_WAITVAL)
+       {
+           if (*(state->ptr) == '"')
+           {
+               *escaped = true;
+               st = GV_INESCVAL;
+           }
+           else if (*(state->ptr) == '\0')
+           {
+               return false;
+           }
+           else if (*(state->ptr) == '=' && !ignoreeq)
+           {
+               elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
+           }
+           else if (*(state->ptr) == '\\')
+           {
+               st = GV_WAITESCIN;
+           }
+           else if (!isspace((unsigned char) *(state->ptr)))
+           {
+               *(state->cur) = *(state->ptr);
+               state->cur++;
+               st = GV_INVAL;
+           }
+       }
+       else if (st == GV_INVAL)
+       {
+           if (*(state->ptr) == '\\')
+           {
+               st = GV_WAITESCIN;
+           }
+           else if (*(state->ptr) == '=' && !ignoreeq)
+           {
+               state->ptr--;
+               return true;
+           }
+           else if (*(state->ptr) == ',' && ignoreeq)
+           {
+               state->ptr--;
+               return true;
+           }
+           else if (isspace((unsigned char) *(state->ptr)))
+           {
+               return true;
+           }
+           else if (*(state->ptr) == '\0')
+           {
+               state->ptr--;
+               return true;
+           }
+           else
+           {
+               RESIZEPRSBUF;
+               *(state->cur) = *(state->ptr);
+               state->cur++;
+           }
+       }
+       else if (st == GV_INESCVAL)
+       {
+           if (*(state->ptr) == '\\')
+           {
+               st = GV_WAITESCESCIN;
+           }
+           else if (*(state->ptr) == '"')
+           {
+               return true;
+           }
+           else if (*(state->ptr) == '\0')
+           {
+               elog(ERROR, "Unexpected end of string");
+           }
+           else
+           {
+               RESIZEPRSBUF;
+               *(state->cur) = *(state->ptr);
+               state->cur++;
+           }
+       }
+       else if (st == GV_WAITESCIN)
+       {
+           if (*(state->ptr) == '\0')
+               elog(ERROR, "Unexpected end of string");
+           RESIZEPRSBUF;
+           *(state->cur) = *(state->ptr);
+           state->cur++;
+           st = GV_INVAL;
+       }
+       else if (st == GV_WAITESCESCIN)
+       {
+           if (*(state->ptr) == '\0')
+               elog(ERROR, "Unexpected end of string");
+           RESIZEPRSBUF;
+           *(state->cur) = *(state->ptr);
+           state->cur++;
+           st = GV_INESCVAL;
+       }
+       else
+           elog(ERROR, "Unknown state %d at position line %d in file '%s'", st, __LINE__, __FILE__);
+
+       state->ptr++;
+   }
+}
+
+#define WKEY   0
+#define WVAL   1
+#define WEQ 2
+#define WGT 3
+#define WDEL   4
+
+
+static void
+parse_hstore(HSParser *state)
+{
+   int         st = WKEY;
+   bool        escaped = false;
+
+   state->plen = 16;
+   state->pairs = (Pairs *) palloc(sizeof(Pairs) * state->plen);
+   state->pcur = 0;
+   state->ptr = state->begin;
+   state->word = NULL;
+
+   while (1)
+   {
+       if (st == WKEY)
+       {
+           if (!get_val(state, false, &escaped))
+               return;
+           if (state->pcur >= state->plen)
+           {
+               state->plen *= 2;
+               state->pairs = (Pairs *) repalloc(state->pairs, sizeof(Pairs) * state->plen);
+           }
+           state->pairs[state->pcur].key = state->word;
+           state->pairs[state->pcur].keylen = hstoreCheckKeyLen(state->cur - state->word);
+           state->pairs[state->pcur].val = NULL;
+           state->word = NULL;
+           st = WEQ;
+       }
+       else if (st == WEQ)
+       {
+           if (*(state->ptr) == '=')
+           {
+               st = WGT;
+           }
+           else if (*(state->ptr) == '\0')
+           {
+               elog(ERROR, "Unexpected end of string");
+           }
+           else if (!isspace((unsigned char) *(state->ptr)))
+           {
+               elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
+           }
+       }
+       else if (st == WGT)
+       {
+           if (*(state->ptr) == '>')
+           {
+               st = WVAL;
+           }
+           else if (*(state->ptr) == '\0')
+           {
+               elog(ERROR, "Unexpected end of string");
+           }
+           else
+           {
+               elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
+           }
+       }
+       else if (st == WVAL)
+       {
+           if (!get_val(state, true, &escaped))
+               elog(ERROR, "Unexpected end of string");
+           state->pairs[state->pcur].val = state->word;
+           state->pairs[state->pcur].vallen = hstoreCheckValLen(state->cur - state->word);
+           state->pairs[state->pcur].isnull = false;
+           state->pairs[state->pcur].needfree = true;
+           if (state->cur - state->word == 4 && !escaped)
+           {
+               state->word[4] = '\0';
+               if (0 == pg_strcasecmp(state->word, "null"))
+                   state->pairs[state->pcur].isnull = true;
+           }
+           state->word = NULL;
+           state->pcur++;
+           st = WDEL;
+       }
+       else if (st == WDEL)
+       {
+           if (*(state->ptr) == ',')
+           {
+               st = WKEY;
+           }
+           else if (*(state->ptr) == '\0')
+           {
+               return;
+           }
+           else if (!isspace((unsigned char) *(state->ptr)))
+           {
+               elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
+           }
+       }
+       else
+           elog(ERROR, "Unknown state %d at line %d in file '%s'", st, __LINE__, __FILE__);
+
+       state->ptr++;
+   }
+}
+
+static int
+comparePairs(const void *a, const void *b)
+{
+   const Pairs *pa = a;
+   const Pairs *pb = b;
+
+   if (pa->keylen == pb->keylen)
+   {
+       int         res = memcmp(pa->key, pb->key, pa->keylen);
+
+       if (res)
+           return res;
+
+       /* guarantee that needfree will be later */
+       if (pb->needfree == pa->needfree)
+           return 0;
+       else if (pa->needfree)
+           return 1;
+       else
+           return -1;
+   }
+   return (pa->keylen > pb->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, int32 l, int32 *buflen)
+{
+   Pairs      *ptr,
+              *res;
+
+   *buflen = 0;
+   if (l < 2)
+   {
+       if (l == 1)
+           *buflen = a->keylen + ((a->isnull) ? 0 : a->vallen);
+       return l;
+   }
+
+   qsort((void *) a, l, sizeof(Pairs), comparePairs);
+   ptr = a + 1;
+   res = a;
+   while (ptr - a < l)
+   {
+       if (ptr->keylen == res->keylen &&
+           memcmp(ptr->key, res->key, res->keylen) == 0)
+       {
+           if (ptr->needfree)
+           {
+               pfree(ptr->key);
+               pfree(ptr->val);
+           }
+       }
+       else
+       {
+           *buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
+           res++;
+           memcpy(res, ptr, sizeof(Pairs));
+       }
 
-typedef enum HStoreOutputKind {
-   JsonOutput = 0x01,
-   LooseOutput = 0x02,
-   ArrayCurlyBraces = 0x04,
-   RootHashDecorated = 0x08,
-   PrettyPrint = 0x10
-} HStoreOutputKind;
+       ptr++;
+   }
 
-static char* HStoreToCString(StringInfo out, char *in,
-                            int len /* just estimation */, HStoreOutputKind kind);
+   *buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
+   return res + 1 - a;
+}
 
-static size_t
+size_t
 hstoreCheckKeyLen(size_t len)
 {
    if (len > HSTORE_MAX_KEY_LEN)
@@ -58,7 +358,7 @@ hstoreCheckKeyLen(size_t len)
    return len;
 }
 
-static size_t
+size_t
 hstoreCheckValLen(size_t len)
 {
    if (len > HSTORE_MAX_VALUE_LEN)
@@ -69,493 +369,223 @@ hstoreCheckValLen(size_t len)
 }
 
 
-static HStore*
-hstoreDump(HStoreValue *p)
+HStore *
+hstorePairs(Pairs *pairs, int32 pcount, int32 buflen)
 {
-   uint32          buflen;
-   HStore         *out;
+   HStore     *out;
+   HEntry     *entry;
+   char       *ptr;
+   char       *buf;
+   int32       len;
+   int32       i;
 
-   if (p == NULL)
-   {
-       buflen = 0;
-       out = palloc(VARHDRSZ);
-   }
-   else
-   {
-       buflen = VARHDRSZ + p->size;
-       out = palloc(buflen);
-       SET_VARSIZE(out, buflen);
+   len = CALCDATASIZE(pcount, buflen);
+   out = palloc(len);
+   SET_VARSIZE(out, len);
+   HS_SETCOUNT(out, pcount);
 
-       buflen = compressHStore(p, VARDATA(out));
-   }
-   SET_VARSIZE(out, buflen + VARHDRSZ);
+   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)
 {
-   PG_RETURN_POINTER(hstoreDump(parseHStore(PG_GETARG_CSTRING(0), -1, false)));
-}
-
-static void
-recvHStoreValue(StringInfo buf, HStoreValue *v, uint32 level, int c)
-{
-   uint32  hentry = c & HENTRY_TYPEMASK;
+   HSParser    state;
+   int32       buflen;
+   HStore     *out;
 
-   check_stack_depth();
+   state.begin = PG_GETARG_CSTRING(0);
 
-   if (c == -1 /* compatibility */ || hentry == HENTRY_ISNULL)
-   {
-       v->type = hsvNull;
-       v->size = sizeof(HEntry);
-   }
-   else if (hentry == HENTRY_ISHASH || hentry == HENTRY_ISARRAY ||
-            hentry == HENTRY_ISSCALAR)
-   {
-       recvHStore(buf, v, level + 1, (uint32)c);
-   }
-   else if (hentry == HENTRY_ISFALSE || hentry == HENTRY_ISTRUE)
-   {
-       v->type = hsvBool;
-       v->size = sizeof(HEntry);
-       v->boolean = (hentry == HENTRY_ISFALSE) ? false : true;
-   }
-   else if (hentry == HENTRY_ISNUMERIC)
-   {
-       v->type = hsvNumeric;
-       v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv,
-                                                        PointerGetDatum(buf),
-                                                        Int32GetDatum(0),
-                                                        Int32GetDatum(-1)));
-       v->size = sizeof(HEntry) * 2 + VARSIZE_ANY(v->numeric);
-   }
-   else if (hentry == HENTRY_ISSTRING)
-   {
-       v->type = hsvString;
-       v->string.val = pq_getmsgtext(buf, c & ~HENTRY_TYPEMASK, &c);
-       v->string.len = hstoreCheckKeyLen(c);
-       v->size = sizeof(HEntry) + v->string.len;
-   }
-   else
-   {
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
-                errmsg("unknown hstore value")));
-   }
-}
+   parse_hstore(&state);
 
-static void
-recvHStore(StringInfo buf, HStoreValue *v, uint32 level, uint32 header)
-{
-   uint32  hentry;
-   uint32  i;
+   state.pcur = hstoreUniquePairs(state.pairs, state.pcur, &buflen);
 
-   hentry = header & HENTRY_TYPEMASK;
+   out = hstorePairs(state.pairs, state.pcur, buflen);
 
-   if (level == 0 && hentry == 0)
-       hentry = HENTRY_ISHASH; /* old version */
+   PG_RETURN_POINTER(out);
+}
 
-   check_stack_depth();
 
-   v->size = 3 * sizeof(HEntry);
-   if (hentry == HENTRY_ISHASH)
-   {
-       v->type = hsvHash;
-       v->hash.npairs = header & HS_COUNT_MASK;
+PG_FUNCTION_INFO_V1(hstore_recv);
+Datum      hstore_recv(PG_FUNCTION_ARGS);
+Datum
+hstore_recv(PG_FUNCTION_ARGS)
+{
+   int32       buflen;
+   HStore     *out;
+   Pairs      *pairs;
+   int32       i;
+   int32       pcount;
+   StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
 
-       if (v->hash.npairs > (buf->len  - buf->cursor) / (2 * sizeof(uint32)))
-           ereport(ERROR,
-                   (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-                    errmsg("too much elements in hstore hash")));
+   pcount = pq_getmsgint(buf, 4);
 
-       if (v->hash.npairs > 0)
-       {
-           v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+   if (pcount == 0)
+   {
+       out = hstorePairs(NULL, 0, 0);
+       PG_RETURN_POINTER(out);
+   }
 
-           for(i=0; i<v->hash.npairs; i++)
-           {
-               recvHStoreValue(buf, &v->hash.pairs[i].key, level,
-                               pq_getmsgint(buf, 4));
-               if (v->hash.pairs[i].key.type != hsvString)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_DATATYPE_MISMATCH),
-                            errmsg("hstore's key could be only a string")));
-
-               recvHStoreValue(buf, &v->hash.pairs[i].value, level,
-                               pq_getmsgint(buf, 4));
-
-               v->size += v->hash.pairs[i].key.size +
-                           v->hash.pairs[i].value.size;
-           }
+   if (pcount < 0 || pcount > MaxAllocSize / sizeof(Pairs))
+       ereport(ERROR,
+               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+             errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
+                    pcount, (int) (MaxAllocSize / sizeof(Pairs)))));
+   pairs = palloc(pcount * sizeof(Pairs));
 
-           uniqueHStoreValue(v);
-       }
-   }
-   else if (hentry == HENTRY_ISARRAY || hentry == HENTRY_ISSCALAR)
+   for (i = 0; i < pcount; ++i)
    {
-       v->type = hsvArray;
-       v->array.nelems = header & HS_COUNT_MASK;
-       v->array.scalar = (hentry == HENTRY_ISSCALAR) ? true : false;
+       int         rawlen = pq_getmsgint(buf, 4);
+       int         len;
 
-       if (v->hash.npairs > (buf->len  - buf->cursor) / sizeof(uint32))
+       if (rawlen < 0)
            ereport(ERROR,
-                   (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-                    errmsg("too much elements in hstore array")));
+                   (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                    errmsg("null value not allowed for hstore key")));
 
-       if (v->array.scalar && v->array.nelems != 1)
-           ereport(ERROR,
-                   (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
-                    errmsg("wrong scalar representation")));
+       pairs[i].key = pq_getmsgtext(buf, rawlen, &len);
+       pairs[i].keylen = hstoreCheckKeyLen(len);
+       pairs[i].needfree = true;
 
-       if (v->array.nelems > 0)
+       rawlen = pq_getmsgint(buf, 4);
+       if (rawlen < 0)
        {
-           v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
-
-           for(i=0; i<v->array.nelems; i++)
-           {
-               recvHStoreValue(buf, v->array.elems + i, level,
-                               pq_getmsgint(buf, 4));
-               v->size += v->array.elems[i].size;
-           }
+           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;
        }
    }
-   else
-   {
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
-                errmsg("unknown hstore element")));
-   }
-}
 
-PG_FUNCTION_INFO_V1(hstore_recv);
-Datum      hstore_recv(PG_FUNCTION_ARGS);
-Datum
-hstore_recv(PG_FUNCTION_ARGS)
-{
-   StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
-   HStoreValue v;
+   pcount = hstoreUniquePairs(pairs, pcount, &buflen);
 
-   recvHStore(buf, &v, 0, pq_getmsgint(buf, 4));
+   out = hstorePairs(pairs, pcount, buflen);
 
-   PG_RETURN_POINTER(hstoreDump(&v));
+   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;
-   HStoreValue v;
-   HStorePair  pair;
+   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);
-   pair.key.type = hsvString;
-   pair.key.string.val = VARDATA_ANY(key);
-   pair.key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
-   pair.key.size = pair.key.string.len + sizeof(HEntry);
+   p.key = VARDATA_ANY(key);
+   p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
 
    if (PG_ARGISNULL(1))
    {
-       pair.value.type = hsvNull;
-       pair.value.size = sizeof(HEntry);
+       p.vallen = 0;
+       p.isnull = true;
    }
    else
    {
-       text        *val = NULL;
-
        val = PG_GETARG_TEXT_PP(1);
-       pair.value.type = hsvString;
-       pair.value.string.val = VARDATA_ANY(val);
-       pair.value.string.len = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
-       pair.value.size = pair.value.string.len + sizeof(HEntry);
+       p.val = VARDATA_ANY(val);
+       p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
+       p.isnull = false;
    }
 
-   v.type = hsvHash;
-   v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
-   v.hash.npairs = 1;
-   v.hash.pairs = &pair;
+   out = hstorePairs(&p, 1, p.keylen + p.vallen);
 
-   PG_RETURN_POINTER(hstoreDump(&v));
+   PG_RETURN_POINTER(out);
 }
 
-PG_FUNCTION_INFO_V1(hstore_from_bool);
-Datum      hstore_from_bool(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(hstore_from_arrays);
+Datum      hstore_from_arrays(PG_FUNCTION_ARGS);
 Datum
-hstore_from_bool(PG_FUNCTION_ARGS)
+hstore_from_arrays(PG_FUNCTION_ARGS)
 {
-   text        *key;
-   HStoreValue v;
-   HStorePair  pair;
+   int32       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 = PG_GETARG_TEXT_PP(0);
-   pair.key.type = hsvString;
-   pair.key.string.val = VARDATA_ANY(key);
-   pair.key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
-   pair.key.size = pair.key.string.len + sizeof(HEntry);
+   key_array = PG_GETARG_ARRAYTYPE_P(0);
 
-   if (PG_ARGISNULL(1))
-   {
-       pair.value.type = hsvNull;
-       pair.value.size = sizeof(HEntry);
-   }
-   else
-   {
-       pair.value.type = hsvBool;
-       pair.value.boolean = PG_GETARG_BOOL(1);
-       pair.value.size = sizeof(HEntry);
-   }
+   Assert(ARR_ELEMTYPE(key_array) == TEXTOID);
 
-   v.type = hsvHash;
-   v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
-   v.hash.npairs = 1;
-   v.hash.pairs = &pair;
+   /*
+    * must check >1 rather than != 1 because empty arrays have 0 dimensions,
+    * not 1
+    */
 
-   PG_RETURN_POINTER(hstoreDump(&v));
-}
+   if (ARR_NDIM(key_array) > 1)
+       ereport(ERROR,
+               (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+                errmsg("wrong number of array subscripts")));
 
-PG_FUNCTION_INFO_V1(hstore_from_numeric);
-Datum      hstore_from_numeric(PG_FUNCTION_ARGS);
-Datum
-hstore_from_numeric(PG_FUNCTION_ARGS)
-{
-   text        *key;
-   HStoreValue v;
-   HStorePair  pair;
+   deconstruct_array(key_array,
+                     TEXTOID, -1, false, 'i',
+                     &key_datums, &key_nulls, &key_count);
 
-   if (PG_ARGISNULL(0))
-       PG_RETURN_NULL();
+   /* see discussion in hstoreArrayToPairs() */
+   if (key_count > MaxAllocSize / sizeof(Pairs))
+       ereport(ERROR,
+               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+             errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
+                    key_count, (int) (MaxAllocSize / sizeof(Pairs)))));
 
-   key = PG_GETARG_TEXT_PP(0);
-   pair.key.type = hsvString;
-   pair.key.string.val = VARDATA_ANY(key);
-   pair.key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
-   pair.key.size = pair.key.string.len + sizeof(HEntry);
+   /* value_array might be NULL */
 
    if (PG_ARGISNULL(1))
    {
-       pair.value.type = hsvNull;
-       pair.value.size = sizeof(HEntry);
+       value_array = NULL;
+       value_count = key_count;
+       value_datums = NULL;
+       value_nulls = NULL;
    }
    else
    {
-       pair.value.type = hsvNumeric;
-       pair.value.numeric = PG_GETARG_NUMERIC(1);
-       pair.value.size = sizeof(HEntry) + sizeof(HEntry) +
-                           VARSIZE_ANY(pair.value.numeric);
-   }
+       value_array = PG_GETARG_ARRAYTYPE_P(1);
 
-   v.type = hsvHash;
-   v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
-   v.hash.npairs = 1;
-   v.hash.pairs = &pair;
+       Assert(ARR_ELEMTYPE(value_array) == TEXTOID);
 
-   PG_RETURN_POINTER(hstoreDump(&v));
-}
-
-PG_FUNCTION_INFO_V1(hstore_from_th);
-Datum      hstore_from_th(PG_FUNCTION_ARGS);
-Datum
-hstore_from_th(PG_FUNCTION_ARGS)
-{
-   text        *key;
-   HStoreValue v;
-   HStorePair  pair;
-
-   if (PG_ARGISNULL(0))
-       PG_RETURN_NULL();
-
-   key = PG_GETARG_TEXT_PP(0);
-   pair.key.type = hsvString;
-   pair.key.string.val = VARDATA_ANY(key);
-   pair.key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
-   pair.key.size = pair.key.string.len + sizeof(HEntry);
-
-   if (PG_ARGISNULL(1))
-   {
-       pair.value.type = hsvNull;
-       pair.value.size = sizeof(HEntry);
-   }
-   else
-   {
-       HStore      *val = NULL;
-
-       val = PG_GETARG_HS(1);
-       pair.value.type = hsvBinary;
-       pair.value.binary.data = VARDATA_ANY(val);
-       pair.value.binary.len = VARSIZE_ANY_EXHDR(val);
-       pair.value.size = pair.value.binary.len + sizeof(HEntry) * 2;
-   }
-
-   v.type = hsvHash;
-   v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
-   v.hash.npairs = 1;
-   v.hash.pairs = &pair;
-
-   PG_RETURN_POINTER(hstoreDump(&v));
-}
-
-PG_FUNCTION_INFO_V1(hstore_from_arrays);
-PG_FUNCTION_INFO_V1(hstore_scalar_from_text);
-Datum      hstore_scalar_from_text(PG_FUNCTION_ARGS);
-Datum
-hstore_scalar_from_text(PG_FUNCTION_ARGS)
-{
-   HStoreValue a, v;
-
-   if (PG_ARGISNULL(0))
-   {
-       v.type = hsvNull;
-       v.size = sizeof(HEntry);
-   }
-   else
-   {
-       text    *scalar;
-
-       scalar = PG_GETARG_TEXT_PP(0);
-       v.type = hsvString;
-       v.string.val = VARDATA_ANY(scalar);
-       v.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(scalar));
-       v.size = v.string.len + sizeof(HEntry);
-   }
-
-   a.type = hsvArray;
-   a.size = sizeof(HEntry) + v.size;
-   a.array.nelems = 1;
-   a.array.elems = &v;
-   a.array.scalar = true;
-
-   PG_RETURN_POINTER(hstoreDump(&a));
-}
-
-PG_FUNCTION_INFO_V1(hstore_scalar_from_bool);
-Datum      hstore_scalar_from_bool(PG_FUNCTION_ARGS);
-Datum
-hstore_scalar_from_bool(PG_FUNCTION_ARGS)
-{
-   HStoreValue a, v;
-
-   if (PG_ARGISNULL(0))
-   {
-       v.type = hsvNull;
-       v.size = sizeof(HEntry);
-   }
-   else
-   {
-       v.type = hsvBool;
-       v.boolean = PG_GETARG_BOOL(0);
-       v.size = sizeof(HEntry);
-   }
-
-   a.type = hsvArray;
-   a.size = sizeof(HEntry) + v.size;
-   a.array.nelems = 1;
-   a.array.elems = &v;
-   a.array.scalar = true;
-
-   PG_RETURN_POINTER(hstoreDump(&a));
-}
-
-PG_FUNCTION_INFO_V1(hstore_scalar_from_numeric);
-Datum      hstore_scalar_from_numeric(PG_FUNCTION_ARGS);
-Datum
-hstore_scalar_from_numeric(PG_FUNCTION_ARGS)
-{
-   HStoreValue a, v;
-
-   if (PG_ARGISNULL(0))
-   {
-       v.type = hsvNull;
-       v.size = sizeof(HEntry);
-   }
-   else
-   {
-       v.type = hsvNumeric;
-       v.numeric = PG_GETARG_NUMERIC(0);
-       v.size = VARSIZE_ANY(v.numeric) + 2*sizeof(HEntry);
-   }
-
-   a.type = hsvArray;
-   a.size = sizeof(HEntry) + v.size;
-   a.array.nelems = 1;
-   a.array.elems = &v;
-   a.array.scalar = true;
-
-   PG_RETURN_POINTER(hstoreDump(&a));
-}
-
-Datum      hstore_from_arrays(PG_FUNCTION_ARGS);
-Datum
-hstore_from_arrays(PG_FUNCTION_ARGS)
-{
-   HStoreValue v;
-   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);
-
-   /*
-    * must check >1 rather than != 1 because empty arrays have 0 dimensions,
-    * not 1
-    */
-
-   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);
-
-   /* see discussion in arrayToHStoreSortedArray() */
-   if (key_count > MaxAllocSize / sizeof(HStorePair))
-       ereport(ERROR,
-               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-             errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
-                    key_count, (int) (MaxAllocSize / sizeof(HStorePair)))));
-
-   /* 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_NDIM(value_array) > 1)
+           ereport(ERROR,
+                   (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+                    errmsg("wrong number of array subscripts")));
 
        if ((ARR_NDIM(key_array) > 0 || ARR_NDIM(value_array) > 0) &&
            (ARR_NDIM(key_array) != ARR_NDIM(value_array) ||
@@ -572,10 +602,7 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
        Assert(key_count == value_count);
    }
 
-   v.type = hsvHash;
-   v.size = 2 * sizeof(HEntry);
-   v.hash.pairs = palloc(key_count * sizeof(*v.hash.pairs));
-   v.hash.npairs = key_count;
+   pairs = palloc(key_count * sizeof(Pairs));
 
    for (i = 0; i < key_count; ++i)
    {
@@ -584,34 +611,31 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
                    (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                     errmsg("null value not allowed for hstore key")));
 
-       v.hash.pairs[i].key.type = hsvString;
-       v.hash.pairs[i].key.string.val = VARDATA_ANY(key_datums[i]);
-       v.hash.pairs[i].key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
-       v.hash.pairs[i].key.size = sizeof(HEntry) +
-                                   v.hash.pairs[i].key.string.len;
-
        if (!value_nulls || value_nulls[i])
        {
-           v.hash.pairs[i].value.type = hsvNull;
-           v.hash.pairs[i].value.size = sizeof(HEntry);
+           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
        {
-           v.hash.pairs[i].value.type = hsvString;
-           v.hash.pairs[i].value.size = sizeof(HEntry);
-           v.hash.pairs[i].value.string.val = VARDATA_ANY(value_datums[i]);
-           v.hash.pairs[i].value.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(value_datums[i]));
-           v.hash.pairs[i].value.size = sizeof(HEntry) +
-                                           v.hash.pairs[i].value.string.len;
+           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;
        }
-
-       v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
    }
 
-   uniqueHStoreValue(&v);
+   key_count = hstoreUniquePairs(pairs, key_count, &buflen);
 
+   out = hstorePairs(pairs, key_count, buflen);
 
-   PG_RETURN_POINTER(hstoreDump(&v));
+   PG_RETURN_POINTER(out);
 }
 
 
@@ -623,7 +647,9 @@ hstore_from_array(PG_FUNCTION_ARGS)
    ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
    int         ndims = ARR_NDIM(in_array);
    int         count;
-   HStoreValue v;
+   int32       buflen;
+   HStore     *out;
+   Pairs      *pairs;
    Datum      *in_datums;
    bool       *in_nulls;
    int         in_count;
@@ -634,7 +660,8 @@ hstore_from_array(PG_FUNCTION_ARGS)
    switch (ndims)
    {
        case 0:
-           PG_RETURN_POINTER(hstoreDump(NULL));
+           out = hstorePairs(NULL, 0, 0);
+           PG_RETURN_POINTER(out);
 
        case 1:
            if ((ARR_DIMS(in_array)[0]) % 2)
@@ -662,17 +689,14 @@ hstore_from_array(PG_FUNCTION_ARGS)
 
    count = in_count / 2;
 
-   /* see discussion in arrayToHStoreSortedArray() */
-   if (count > MaxAllocSize / sizeof(HStorePair))
+   /* see discussion in hstoreArrayToPairs() */
+   if (count > MaxAllocSize / sizeof(Pairs))
        ereport(ERROR,
                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
              errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
-                    count, (int) (MaxAllocSize / sizeof(HStorePair)))));
+                    count, (int) (MaxAllocSize / sizeof(Pairs)))));
 
-   v.type = hsvHash;
-   v.size = 2*sizeof(HEntry);
-   v.hash.npairs = count;
-   v.hash.pairs = palloc(count * sizeof(HStorePair));
+   pairs = palloc(count * sizeof(Pairs));
 
    for (i = 0; i < count; ++i)
    {
@@ -681,33 +705,31 @@ hstore_from_array(PG_FUNCTION_ARGS)
                    (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                     errmsg("null value not allowed for hstore key")));
 
-       v.hash.pairs[i].key.type = hsvString;
-       v.hash.pairs[i].key.string.val = VARDATA_ANY(in_datums[i * 2]);
-       v.hash.pairs[i].key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2]));
-       v.hash.pairs[i].key.size = sizeof(HEntry) +
-                                   v.hash.pairs[i].key.string.len;
-
        if (in_nulls[i * 2 + 1])
        {
-           v.hash.pairs[i].value.type = hsvNull;
-           v.hash.pairs[i].value.size = sizeof(HEntry);
+           pairs[i].key = VARDATA_ANY(in_datums[i * 2]);
+           pairs[i].val = NULL;
+           pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2]));
+           pairs[i].vallen = 4;
+           pairs[i].isnull = true;
+           pairs[i].needfree = false;
        }
        else
        {
-           v.hash.pairs[i].value.type = hsvString;
-           v.hash.pairs[i].value.size = sizeof(HEntry);
-           v.hash.pairs[i].value.string.val = VARDATA_ANY(in_datums[i * 2 + 1]);
-           v.hash.pairs[i].value.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2 + 1]));
-           v.hash.pairs[i].value.size = sizeof(HEntry) +
-                                           v.hash.pairs[i].value.string.len;
+           pairs[i].key = VARDATA_ANY(in_datums[i * 2]);
+           pairs[i].val = VARDATA_ANY(in_datums[i * 2 + 1]);
+           pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2]));
+           pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(in_datums[i * 2 + 1]));
+           pairs[i].isnull = false;
+           pairs[i].needfree = false;
        }
-
-       v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
    }
 
-   uniqueHStoreValue(&v);
+   count = hstoreUniquePairs(pairs, count, &buflen);
+
+   out = hstorePairs(pairs, count, buflen);
 
-   PG_RETURN_POINTER(hstoreDump(&v));
+   PG_RETURN_POINTER(out);
 }
 
 /* most of hstore_from_record is shamelessly swiped from record_out */
@@ -737,17 +759,19 @@ Datum
 hstore_from_record(PG_FUNCTION_ARGS)
 {
    HeapTupleHeader rec;
-   HStore         *out;
-   HStoreValue    v;
-   Oid             tupType;
-   int32           tupTypmod;
-   TupleDesc       tupdesc;
-   HeapTupleData   tuple;
-   RecordIOData   *my_extra;
-   int             ncolumns;
-   int             i;
-   Datum          *values;
-   bool           *nulls;
+   int32       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))
    {
@@ -804,10 +828,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
    }
 
    Assert(ncolumns <= MaxTupleAttributeNumber);        /* thus, no overflow */
-   v.type = hsvHash;
-   v.size = 2*sizeof(HEntry);
-   v.hash.npairs = ncolumns;
-   v.hash.pairs = palloc(ncolumns * sizeof(HStorePair));
+   pairs = palloc(ncolumns * sizeof(Pairs));
 
    if (rec)
    {
@@ -829,7 +850,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
        nulls = NULL;
    }
 
-   for (i = 0; i < ncolumns; ++i)
+   for (i = 0, j = 0; i < ncolumns; ++i)
    {
        ColumnIOData *column_info = &my_extra->columns[i];
        Oid         column_type = tupdesc->attrs[i]->atttypid;
@@ -839,82 +860,46 @@ hstore_from_record(PG_FUNCTION_ARGS)
        if (tupdesc->attrs[i]->attisdropped)
            continue;
 
-       v.hash.pairs[i].key.type = hsvString;
-       v.hash.pairs[i].key.string.val = NameStr(tupdesc->attrs[i]->attname);
-       v.hash.pairs[i].key.string.len = hstoreCheckKeyLen(strlen(v.hash.pairs[i].key.string.val));
-       v.hash.pairs[i].key.size = sizeof(HEntry) +
-                                   v.hash.pairs[i].key.string.len;
+       pairs[j].key = NameStr(tupdesc->attrs[i]->attname);
+       pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(tupdesc->attrs[i]->attname)));
 
        if (!nulls || nulls[i])
        {
-           v.hash.pairs[i].value.type = hsvNull;
-           v.hash.pairs[i].value.size = sizeof(HEntry);
+           pairs[j].val = NULL;
+           pairs[j].vallen = 4;
+           pairs[j].isnull = true;
+           pairs[j].needfree = false;
+           ++j;
+           continue;
        }
-       else
-       {
-           /*
-            * Convert the column value to hstore's values
-            */
-           if (column_type == BOOLOID)
-           {
-               v.hash.pairs[i].value.type = hsvBool;
-               v.hash.pairs[i].value.boolean = DatumGetBool(values[i]);
-               v.hash.pairs[i].value.size = sizeof(HEntry);
-           }
-           else if (TypeCategory(column_type) == TYPCATEGORY_NUMERIC)
-           {
-               Oid             castOid = InvalidOid;
-               CoercionMethod  method;
-
-               v.hash.pairs[i].value.type = hsvNumeric;
-
-               castOid = searchCast(column_type, NUMERICOID, &method);
-               if (castOid == InvalidOid)
-               {
-                   if (method != COERCION_METHOD_BINARY)
-                       elog(ERROR, "could not cast numeric category type to numeric '%c'", (char)method);
-
-                   v.hash.pairs[i].value.numeric = DatumGetNumeric(values[i]);
-               }
-               else
-               {
-                   v.hash.pairs[i].value.numeric =
-                       DatumGetNumeric(OidFunctionCall1(castOid, values[i]));
 
-               }
-               v.hash.pairs[i].value.size = 2*sizeof(HEntry) +
-                               VARSIZE_ANY(v.hash.pairs[i].value.numeric);
-           }
-           else
-           {
-               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]);
+       /*
+        * Convert the column value to text
+        */
+       if (column_info->column_type != column_type)
+       {
+           bool        typIsVarlena;
 
-               v.hash.pairs[i].value.type = hsvString;
-               v.hash.pairs[i].value.string.val = value;
-               v.hash.pairs[i].value.string.len = hstoreCheckValLen(strlen(value));
-               v.hash.pairs[i].value.size = sizeof(HEntry) +
-                                       v.hash.pairs[i].value.string.len;
-           }
+           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;
        }
 
-       v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
+       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;
    }
 
-   uniqueHStoreValue(&v);
+   ncolumns = hstoreUniquePairs(pairs, j, &buflen);
 
-   out = hstoreDump(&v);
+   out = hstorePairs(pairs, ncolumns, buflen);
 
    ReleaseTupleDesc(tupdesc);
 
@@ -929,6 +914,8 @@ 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;
@@ -974,6 +961,8 @@ hstore_populate_record(PG_FUNCTION_ARGS)
    }
 
    hs = PG_GETARG_HS(1);
+   entries = ARRPTR(hs);
+   ptr = STRPTR(hs);
 
    /*
     * if the input hstore is empty, we can only skip the rest if we were
@@ -981,7 +970,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
     * domain nulls.
     */
 
-   if (HS_ISEMPTY(hs) && rec)
+   if (HS_COUNT(hs) == 0 && rec)
        PG_RETURN_POINTER(rec);
 
    tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
@@ -1045,7 +1034,9 @@ hstore_populate_record(PG_FUNCTION_ARGS)
    {
        ColumnIOData *column_info = &my_extra->columns[i];
        Oid         column_type = tupdesc->attrs[i]->atttypid;
-       HStoreValue *v = NULL;
+       char       *value;
+       int         idx;
+       int         vallen;
 
        /* Ignore dropped columns in datatype */
        if (tupdesc->attrs[i]->attisdropped)
@@ -1054,12 +1045,9 @@ hstore_populate_record(PG_FUNCTION_ARGS)
            continue;
        }
 
-       if (!HS_ISEMPTY(hs))
-       {
-           char *key = NameStr(tupdesc->attrs[i]->attname);
-
-           v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HASH, NULL, key, strlen(key));
-       }
+       idx = hstoreFindKey(hs, 0,
+                           NameStr(tupdesc->attrs[i]->attname),
+                           strlen(NameStr(tupdesc->attrs[i]->attname)));
 
        /*
         * we can't just skip here if the key wasn't found since we might have
@@ -1069,7 +1057,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
         * then every field which we don't populate needs to be run through
         * the input function just in case it's a domain type.
         */
-       if (v == NULL && rec)
+       if (idx < 0 && rec)
            continue;
 
        /*
@@ -1085,7 +1073,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
            column_info->column_type = column_type;
        }
 
-       if (v == NULL || v->type == hsvNull)
+       if (idx < 0 || HS_VALISNULL(entries, idx))
        {
            /*
             * need InputFunctionCall to happen even for nulls, so that domain
@@ -1098,29 +1086,12 @@ hstore_populate_record(PG_FUNCTION_ARGS)
        }
        else
        {
-           char *s = NULL;
-
-           if (v->type == hsvString)
-               s = pnstrdup(v->string.val, v->string.len);
-           else if (v->type == hsvBool)
-               s = pnstrdup((v->boolean) ? "t" : "f", 1);
-           else if (v->type == hsvNumeric)
-               s = DatumGetCString(DirectFunctionCall1(numeric_out,
-                                                       PointerGetDatum(v->numeric)));
-           else if (v->type == hsvBinary &&
-                    (column_type == JSONOID || column_type == JSONBOID))
-               s = HStoreToCString(NULL, v->binary.data, v->binary.len,
-                                   SET_PRETTY_PRINT_VAR(JsonOutput | RootHashDecorated));
-           else if (v->type == hsvBinary && type_is_array(column_type))
-               s = HStoreToCString(NULL, v->binary.data, v->binary.len,
-                                   SET_PRETTY_PRINT_VAR(ArrayCurlyBraces));
-           else if (v->type == hsvBinary)
-               s = HStoreToCString(NULL, v->binary.data, v->binary.len,
-                                   SET_PRETTY_PRINT_VAR(0));
-           else
-               elog(ERROR, "wrong hstore 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, s,
+           values[i] = InputFunctionCall(&column_info->proc, value,
                                          column_info->typioparam,
                                          tupdesc->attrs[i]->atttypmod);
            nulls[i] = false;
@@ -1134,457 +1105,125 @@ hstore_populate_record(PG_FUNCTION_ARGS)
    PG_RETURN_DATUM(HeapTupleGetDatum(rettuple));
 }
 
-bool
-stringIsNumber(char *string, int len, bool jsonNumber) {
-   enum {
-       SIN_FIRSTINT,
-       SIN_ZEROINT,
-       SIN_INT,
-       SIN_SCALE,
-       SIN_MSIGN,
-       SIN_MANTISSA
-   } sinState;
-   char    *c;
-   bool    r;
-
-   if (*string == '-' || *string == '+')
-   {
-       string++;
-       len--;
-   }
-
-   c = string;
-   r = true;
-   sinState = SIN_FIRSTINT;
 
-   while(r && c - string < len)
-   {
-       switch(sinState)
-       {
-           case SIN_FIRSTINT:
-               if (*c == '0' && jsonNumber)
-                   sinState = SIN_ZEROINT;
-               else if (*c == '.')
-                   sinState = SIN_SCALE;
-               else if (isdigit(*c))
-                   sinState = SIN_INT;
-               else
-                   r = false;
-               break;
-           case SIN_ZEROINT:
-               if (*c == '.')
-                   sinState = SIN_SCALE;
-               else
-                   r = false;
-               break;
-           case SIN_INT:
-               if (*c == '.')
-                   sinState = SIN_SCALE;
-               else if (*c == 'e' || *c == 'E')
-                   sinState = SIN_MSIGN;
-               else if (!isdigit(*c))
-                   r = false;
-               break;
-           case SIN_SCALE:
-               if (*c == 'e' || *c == 'E')
-                   sinState = SIN_MSIGN;
-               else if (!isdigit(*c))
-                   r = false;
-               break;
-           case SIN_MSIGN:
-               if (*c == '-' || *c == '+' || isdigit(*c))
-                   sinState = SIN_MANTISSA;
-               else
-                   r = false;
-               break;
-           case SIN_MANTISSA:
-               if (!isdigit(*c))
-                   r = false;
-               break;
-           default:
-               abort();
-       }
-
-       c++;
-   }
-
-   if (sinState == SIN_MSIGN)
-       r = false;
-
-   return r;
-}
-
-static void
-printIndent(StringInfo out, bool isRootHash, HStoreOutputKind kind, int level)
+static char *
+cpw(char *dst, char *src, int len)
 {
-   if (kind & PrettyPrint)
-   {
-       int i;
+   char       *ptr = src;
 
-       if (isRootHash && (kind & RootHashDecorated) == 0)
-           level--;
-       for(i=0; i<4*level; i++)
-           appendStringInfoCharMacro(out, ' ');
-   }
-}
-
-static void
-printCR(StringInfo out, HStoreOutputKind kind)
-{
-   if (kind & PrettyPrint)
-       appendStringInfoCharMacro(out, '\n');
-}
-
-static void
-escape_hstore(StringInfo out, char *string, uint32 len)
-{
-   char       *ptr = string;
-
-   appendStringInfoCharMacro(out, '"');
-   while (ptr - string < len)
+   while (ptr - src < len)
    {
        if (*ptr == '"' || *ptr == '\\')
-           appendStringInfoCharMacro(out, '\\');
-       appendStringInfoCharMacro(out, *ptr);
-       ptr++;
-   }
-   appendStringInfoCharMacro(out, '"');
-}
-
-static void
-putEscapedString(StringInfo out, HStoreOutputKind kind,
-                char *string, uint32 len)
-{
-   if (kind & LooseOutput)
-   {
-       if (len == 1 && *string == 't')
-           appendStringInfoString(out, (kind & JsonOutput) ? "true" : "t" );
-       else if (len == 1 && *string == 'f')
-           appendStringInfoString(out, (kind & JsonOutput) ? "false" : "f");
-       else if (len > 0 && stringIsNumber(string, len, true))
-           appendBinaryStringInfo(out, string, len);
-       else if (kind & JsonOutput)
-           escape_json(out, pnstrdup(string, len));
-       else
-           escape_hstore(out, string, len);
-   }
-   else
-   {
-       if (kind & JsonOutput)
-           escape_json(out, pnstrdup(string, len));
-       else
-           escape_hstore(out, string, len);
+           *dst++ = '\\';
+       *dst++ = *ptr++;
    }
+   return dst;
 }
 
-static void
-putEscapedValue(StringInfo out, HStoreOutputKind kind, HStoreValue *v)
-{
-   switch(v->type)
-   {
-       case hsvNull:
-           appendBinaryStringInfo(out,
-                                  (kind & JsonOutput) ? "null" : "NULL", 4);
-           break;
-       case hsvString:
-           putEscapedString(out, kind, v->string.val, v->string.len);
-           break;
-       case hsvBool:
-           if ((kind & JsonOutput) == 0)
-               appendBinaryStringInfo(out, (v->boolean) ? "t" : "f", 1);
-           else if (v->boolean)
-               appendBinaryStringInfo(out, "true", 4);
-           else
-               appendBinaryStringInfo(out, "false", 5);
-           break;
-       case hsvNumeric:
-           appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
-           break;
-       default:
-           elog(ERROR, "wrong hstore scalar type");
-   }
-}
-
-static bool
-needBrackets(int level, bool isArray, HStoreOutputKind kind, bool isScalar)
+PG_FUNCTION_INFO_V1(hstore_out);
+Datum      hstore_out(PG_FUNCTION_ARGS);
+Datum
+hstore_out(PG_FUNCTION_ARGS)
 {
-   bool res;
-
-   if (isArray && isScalar)
-       res = false;
-   else if (level == 0)
-       res = (isArray || (kind & RootHashDecorated)) ? true : false;
-   else
-       res = true;
-
-   return res;
-}
+   HStore     *in = PG_GETARG_HS(0);
+   int         buflen,
+               i;
+   int         count = HS_COUNT(in);
+   char       *out,
+              *ptr;
+   char       *base = STRPTR(in);
+   HEntry     *entries = ARRPTR(in);
 
-static bool
-isArrayBrackets(HStoreOutputKind kind)
-{
-   return ((kind & ArrayCurlyBraces) == 0) ? true : false;
-}
+   if (count == 0)
+       PG_RETURN_CSTRING(pstrdup(""));
 
-static char*
-HStoreToCString(StringInfo out, char *in, int len /* just estimation */,
-               HStoreOutputKind kind)
-{
-   bool            first = true;
-   HStoreIterator  *it;
-   int             type;
-   HStoreValue     v;
-   int             level = 0;
-   bool            isRootHash = false;
+   buflen = 0;
 
-   if (out == NULL)
-       out = makeStringInfo();
+   /*
+    * 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.
+    */
 
-   if (in == NULL)
+   for (i = 0; i < count; i++)
    {
-       appendStringInfoString(out, "");
-       return out->data;
+       /* 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));
    }
 
-   enlargeStringInfo(out, (len >= 0) ? len : 64);
-
-   it = HStoreIteratorInit(in);
+   out = ptr = palloc(buflen);
 
-   while((type = HStoreIteratorGet(&it, &v, false)) != 0)
+   for (i = 0; i < count; i++)
    {
-reout:
-       switch(type)
+       *ptr++ = '"';
+       ptr = cpw(ptr, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
+       *ptr++ = '"';
+       *ptr++ = '=';
+       *ptr++ = '>';
+       if (HS_VALISNULL(entries, i))
        {
-           case WHS_BEGIN_ARRAY:
-               if (first == false)
-               {
-                   appendBinaryStringInfo(out, ", ", 2);
-                   printCR(out, kind);
-               }
-               first = true;
-
-               if (needBrackets(level, true, kind, v.array.scalar))
-               {
-                   printIndent(out, isRootHash, kind, level);
-                   appendStringInfoChar(out, isArrayBrackets(kind) ? '[' : '{');
-                   printCR(out, kind);
-               }
-               level++;
-               break;
-           case WHS_BEGIN_HASH:
-               if (first == false)
-               {
-                   appendBinaryStringInfo(out, ", ", 2);
-                   printCR(out, kind);
-               }
-               first = true;
-
-               if (level == 0)
-                   isRootHash = true;
-
-               if (needBrackets(level, false, kind, false))
-               {
-                   printIndent(out, isRootHash, kind, level);
-                   appendStringInfoCharMacro(out, '{');
-                   printCR(out, kind);
-               }
-
-               level++;
-               break;
-           case WHS_KEY:
-               if (first == false)
-               {
-                   appendBinaryStringInfo(out, ", ", 2);
-                   printCR(out, kind);
-               }
-               first = true;
-
-               printIndent(out, isRootHash, kind, level);
-               /* key should not be loose */
-               putEscapedValue(out, kind & ~LooseOutput, &v);
-               appendBinaryStringInfo(out,
-                                      (kind & JsonOutput) ? ": " : "=>", 2);
-
-               type = HStoreIteratorGet(&it, &v, false);
-               if (type == WHS_VALUE)
-               {
-                   first = false;
-                   putEscapedValue(out, kind, &v);
-               }
-               else
-               {
-                   Assert(type == WHS_BEGIN_HASH || type == WHS_BEGIN_ARRAY);
-                   printCR(out, kind);
-                   goto reout;
-               }
-               break;
-           case WHS_ELEM:
-               if (first == false)
-               {
-                   appendBinaryStringInfo(out, ", ", 2);
-                   printCR(out, kind);
-               }
-               else
-               {
-                   first = false;
-               }
-
-               printIndent(out, isRootHash, kind, level);
-               putEscapedValue(out, kind, &v);
-               break;
-           case WHS_END_ARRAY:
-               level--;
-               if (needBrackets(level, true, kind, v.array.scalar))
-               {
-                   printCR(out, kind);
-                   printIndent(out, isRootHash, kind, level);
-                   appendStringInfoChar(out, isArrayBrackets(kind) ? ']' : '}');
-               }
-               first = false;
-               break;
-           case WHS_END_HASH:
-               level--;
-               if (needBrackets(level, false, kind, false))
-               {
-                   printCR(out, kind);
-                   printIndent(out, isRootHash, kind, level);
-                   appendStringInfoCharMacro(out, '}');
-               }
-               first = false;
-               break;
-           default:
-               elog(ERROR, "unexpected state of hstore iterator");
+           *ptr++ = 'N';
+           *ptr++ = 'U';
+           *ptr++ = 'L';
+           *ptr++ = 'L';
+       }
+       else
+       {
+           *ptr++ = '"';
+           ptr = cpw(ptr, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
+           *ptr++ = '"';
        }
-   }
-
-   Assert(level == 0);
-
-   return out->data;
-}
-
-text*
-HStoreValueToText(HStoreValue *v)
-{
-   text        *out;
-
-   if (v == NULL || v->type == hsvNull)
-   {
-       out = NULL;
-   }
-   else if (v->type == hsvString)
-   {
-       out = cstring_to_text_with_len(v->string.val, v->string.len);
-   }
-   else if (v->type == hsvBool)
-   {
-       out = cstring_to_text_with_len((v->boolean) ? "t" : "f", 1);
-   }
-   else if (v->type == hsvNumeric)
-   {
-       out = cstring_to_text(DatumGetCString(
-               DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))
-       ));
-   }
-   else
-   {
-       StringInfo  str;
-
-       str = makeStringInfo();
-       appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
-
-       HStoreToCString(str, v->binary.data, v->binary.len, SET_PRETTY_PRINT_VAR(0));
 
-       out = (text*)str->data;
-       SET_VARSIZE(out, str->len);
+       if (i + 1 != count)
+       {
+           *ptr++ = ',';
+           *ptr++ = ' ';
+       }
    }
-
-   return out;
-}
-
-PG_FUNCTION_INFO_V1(hstore_out);
-Datum      hstore_out(PG_FUNCTION_ARGS);
-Datum
-hstore_out(PG_FUNCTION_ARGS)
-{
-   HStore  *hs = PG_GETARG_HS(0);
-   char    *out;
-
-   out = HStoreToCString(NULL, (HS_ISEMPTY(hs)) ? NULL : VARDATA(hs),
-                         VARSIZE(hs), SET_PRETTY_PRINT_VAR(0));
+   *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);
-   StringInfoData  buf;
+   HStore     *in = PG_GETARG_HS(0);
+   int         i;
+   int         count = HS_COUNT(in);
+   char       *base = STRPTR(in);
+   HEntry     *entries = ARRPTR(in);
+   StringInfoData buf;
 
    pq_begintypsend(&buf);
 
-   if (HS_ISEMPTY(in))
-   {
-       pq_sendint(&buf, 0, 4);
-   }
-   else
-   {
-       HStoreIterator  *it;
-       int             type;
-       HStoreValue     v;
-       uint32          flag;
-       bytea           *nbuf;
-
-       enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */);
+   pq_sendint(&buf, count, 4);
 
-       it = HStoreIteratorInit(VARDATA_ANY(in));
+   for (i = 0; i < count; i++)
+   {
+       int32       keylen = HS_KEYLEN(entries, i);
 
-       while((type = HStoreIteratorGet(&it, &v, false)) != 0)
+       pq_sendint(&buf, keylen, 4);
+       pq_sendtext(&buf, HS_KEY(entries, base, i), keylen);
+       if (HS_VALISNULL(entries, i))
        {
-           switch(type)
-           {
-               case WHS_BEGIN_ARRAY:
-                   flag = (v.array.scalar) ? HENTRY_ISSCALAR : HENTRY_ISARRAY;
-                   pq_sendint(&buf, v.array.nelems | flag, 4);
-                   break;
-               case WHS_BEGIN_HASH:
-                   pq_sendint(&buf, v.hash.npairs | HENTRY_ISHASH, 4);
-                   break;
-               case WHS_KEY:
-                   pq_sendint(&buf, v.string.len | HENTRY_ISSTRING, 4);
-                   pq_sendtext(&buf, v.string.val, v.string.len);
-                   break;
-               case WHS_ELEM:
-               case WHS_VALUE:
-                   switch(v.type)
-                   {
-                       case hsvNull:
-                           pq_sendint(&buf, HENTRY_ISNULL, 4);
-                           break;
-                       case hsvString:
-                           pq_sendint(&buf, v.string.len | HENTRY_ISSTRING, 4);
-                           pq_sendtext(&buf, v.string.val, v.string.len);
-                           break;
-                       case hsvBool:
-                           pq_sendint(&buf, (v.boolean) ? HENTRY_ISTRUE : HENTRY_ISFALSE, 4);
-                           break;
-                       case hsvNumeric:
-                           nbuf = DatumGetByteaP(DirectFunctionCall1(numeric_send, NumericGetDatum(v.numeric)));
-                           pq_sendint(&buf, ((int)VARSIZE_ANY_EXHDR(nbuf)) | HENTRY_ISNUMERIC, 4);
-                           pq_sendbytes(&buf, VARDATA(nbuf), (int)VARSIZE_ANY_EXHDR(nbuf));
-                           break;
-                       default:
-                           elog(ERROR, "wrong hstore scalar type");
-                   }
-                   break;
-               case WHS_END_ARRAY:
-               case WHS_END_HASH:
-                   break;
-               default:
-                   elog(ERROR, "unexpected state of hstore iterator");
-           }
+           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);
        }
    }
 
@@ -1606,28 +1245,90 @@ Datum
 hstore_to_json_loose(PG_FUNCTION_ARGS)
 {
    HStore     *in = PG_GETARG_HS(0);
-   text       *out;
-
-   if (HS_ISEMPTY(in))
-   {
-       out = cstring_to_text_with_len("{}",2);
-   }
-   else
-   {
-       StringInfo  str;
-
-       str = makeStringInfo();
-       appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
+   int         i;
+   int         count = HS_COUNT(in);
+   char       *base = STRPTR(in);
+   HEntry     *entries = ARRPTR(in);
+   bool        is_number;
+   StringInfoData tmp,
+               dst;
+
+   if (count == 0)
+       PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
+
+   initStringInfo(&tmp);
+   initStringInfo(&dst);
+
+   appendStringInfoChar(&dst, '{');
+
+   for (i = 0; i < count; i++)
+   {
+       resetStringInfo(&tmp);
+       appendBinaryStringInfo(&tmp, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
+       escape_json(&dst, tmp.data);
+       appendStringInfoString(&dst, ": ");
+       if (HS_VALISNULL(entries, i))
+           appendStringInfoString(&dst, "null");
+       /* guess that values of 't' or 'f' are booleans */
+       else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't')
+           appendStringInfoString(&dst, "true");
+       else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f')
+           appendStringInfoString(&dst, "false");
+       else
+       {
+           is_number = false;
+           resetStringInfo(&tmp);
+           appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
 
-       HStoreToCString(str, VARDATA_ANY(in), VARSIZE_ANY(in),
-                       SET_PRETTY_PRINT_VAR(JsonOutput | RootHashDecorated | LooseOutput));
+           /*
+            * don't treat something with a leading zero followed by another
+            * digit as numeric - could be a zip code or similar
+            */
+           if (tmp.len > 0 &&
+               !(tmp.data[0] == '0' &&
+                 isdigit((unsigned char) tmp.data[1])) &&
+               strspn(tmp.data, "+-0123456789Ee.") == tmp.len)
+           {
+               /*
+                * might be a number. See if we can input it as a numeric
+                * value. Ignore any actual parsed value.
+                */
+               char       *endptr = "junk";
+               long        lval;
+
+               lval = strtol(tmp.data, &endptr, 10);
+               (void) lval;
+               if (*endptr == '\0')
+               {
+                   /*
+                    * strol man page says this means the whole string is
+                    * valid
+                    */
+                   is_number = true;
+               }
+               else
+               {
+                   /* not an int - try a double */
+                   double      dval;
 
-       out = (text*)str->data;
+                   dval = strtod(tmp.data, &endptr);
+                   (void) dval;
+                   if (*endptr == '\0')
+                       is_number = true;
+               }
+           }
+           if (is_number)
+               appendBinaryStringInfo(&dst, tmp.data, tmp.len);
+           else
+               escape_json(&dst, tmp.data);
+       }
 
-       SET_VARSIZE(out, str->len);
+       if (i + 1 != count)
+           appendStringInfoString(&dst, ", ");
    }
+   appendStringInfoChar(&dst, '}');
 
-   PG_RETURN_TEXT_P(out);
+   PG_RETURN_TEXT_P(cstring_to_text(dst.data));
 }
 
 PG_FUNCTION_INFO_V1(hstore_to_json);
@@ -1636,295 +1337,40 @@ Datum
 hstore_to_json(PG_FUNCTION_ARGS)
 {
    HStore     *in = PG_GETARG_HS(0);
-   text       *out;
-
-   if (HS_ISEMPTY(in))
-   {
-       out = cstring_to_text_with_len("{}",2);
-   }
-   else
-   {
-       StringInfo  str;
-
-       str = makeStringInfo();
-       appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
-
-       HStoreToCString(str, HS_ISEMPTY(in) ? NULL : VARDATA_ANY(in),
-                       VARSIZE_ANY(in),
-                       SET_PRETTY_PRINT_VAR(JsonOutput | RootHashDecorated));
-
-       out = (text*)str->data;
-
-       SET_VARSIZE(out, str->len);
-   }
-
-   PG_RETURN_TEXT_P(out);
-}
-
-PG_FUNCTION_INFO_V1(json_to_hstore);
-Datum      json_to_hstore(PG_FUNCTION_ARGS);
-Datum
-json_to_hstore(PG_FUNCTION_ARGS)
-{
-   text    *json = PG_GETARG_TEXT_PP(0);
-
-   PG_RETURN_POINTER(hstoreDump(parseHStore(VARDATA_ANY(json),
-                                            VARSIZE_ANY_EXHDR(json), true)));
-}
-
-static Oid
-searchCast(Oid src, Oid dst, CoercionMethod *method)
-{
-   Oid             funcOid = InvalidOid,
-                   baseSrc;
-   HeapTuple       tuple;
-
-   if (src == dst)
-   {
-       *method = COERCION_METHOD_BINARY;
-       return InvalidOid;
-   }
-
-   tuple = SearchSysCache2(CASTSOURCETARGET,
-                           ObjectIdGetDatum(src),
-                           ObjectIdGetDatum(dst));
-
-   *method = 0;
-
-   if (HeapTupleIsValid(tuple))
-   {
-       Form_pg_cast    castForm = (Form_pg_cast) GETSTRUCT(tuple);
-
-       if (castForm->castmethod == COERCION_METHOD_FUNCTION)
-           funcOid = castForm->castfunc;
-
-       *method = castForm->castmethod;
-
-       ReleaseSysCache(tuple);
-   }
-   else if ((baseSrc = getBaseType(src)) != src && OidIsValid(baseSrc))
-   {
-       /* domain type */
-       funcOid = searchCast(baseSrc, dst, method);
-   }
-
-   return funcOid;
-}
-
-PG_FUNCTION_INFO_V1(array_to_hstore);
-Datum      array_to_hstore(PG_FUNCTION_ARGS);
-Datum
-array_to_hstore(PG_FUNCTION_ARGS)
-{
-   ArrayType       *array = PG_GETARG_ARRAYTYPE_P(0);
-   ArrayIterator   iterator;
-   int             i = 0;
-   Datum           datum;
-   bool            isnull;
-   int             ncounters = ARR_NDIM(array),
-                   *counters = palloc0(sizeof(*counters) * ncounters),
-                   *dims = ARR_DIMS(array);
-   ToHStoreState   *state = NULL;
-   HStoreValue     value, *result = NULL;
-   Oid             castOid = InvalidOid;
-   int             valueType = hsvString;
-   FmgrInfo        castInfo;
-   CoercionMethod  method;
-
-   if (ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)) == 0)
-       PG_RETURN_POINTER(hstoreDump(NULL));
-
-   /* see discussion in arrayToHStoreSortedArray() */
-   if (ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)) >
-       MaxAllocSize / sizeof(HStorePair))
-       ereport(ERROR,
-               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-             errmsg("number of elements (%d) exceeds the maximum allowed (%d)",
-                    ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)),
-                    (int) (MaxAllocSize / sizeof(HStorePair)))));
-
-   switch(ARR_ELEMTYPE(array))
-   {
-       case BOOLOID:
-           valueType = hsvBool;
-           break;
-       case NUMERICOID:
-           valueType = hsvNumeric;
-           break;
-       case TEXTOID:
-           valueType = hsvString;
-           break;
-       default:
-           if (TypeCategory(ARR_ELEMTYPE(array)) == TYPCATEGORY_NUMERIC)
-           {
-               castOid = searchCast(ARR_ELEMTYPE(array), NUMERICOID, &method);
-
-               if (castOid == InvalidOid && method != COERCION_METHOD_BINARY)
-                   elog(ERROR, "could not cast array's element type to numeric");
-
-               valueType = hsvNumeric;
-               break;
-           }
-           else
-           {
-               castOid = searchCast(ARR_ELEMTYPE(array), TEXTOID, &method);
-
-               if (castOid == InvalidOid && method != COERCION_METHOD_BINARY)
-                   elog(ERROR, "could not cast array's element type to text");
+   int         i;
+   int         count = HS_COUNT(in);
+   char       *base = STRPTR(in);
+   HEntry     *entries = ARRPTR(in);
+   StringInfoData tmp,
+               dst;
 
-               valueType = hsvString;
-               break;
-           }
-   }
+   if (count == 0)
+       PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
 
-   if (castOid != InvalidOid)
-       fmgr_info(castOid, &castInfo);
+   initStringInfo(&tmp);
+   initStringInfo(&dst);
 
-   iterator = array_create_iterator(array, 0);
+   appendStringInfoChar(&dst, '{');
 
-   value.type = hsvArray;
-   value.array.scalar = false;
-   for(i=0; i<ncounters; i++)
+   for (i = 0; i < count; i++)
    {
-       value.array.nelems = dims[i];
-       result = pushHStoreValue(&state, WHS_BEGIN_ARRAY, &value);
-   }
-
-   while(array_iterate(iterator, &datum, &isnull))
-   {
-       i = ncounters - 1;
-
-       if (counters[i] >= dims[i])
-       {
-           while(i>=0 && counters[i] >= dims[i])
-           {
-               counters[i] = 0;
-               result = pushHStoreValue(&state, WHS_END_ARRAY, NULL);
-               i--;
-           }
-
-           Assert(i>=0);
-
-           counters[i]++;
-
-           value.type = hsvArray;
-           value.array.scalar = false;
-           for(i = i + 1; i<ncounters; i++)
-           {
-               counters[i] = 1;
-               value.array.nelems = dims[i];
-               result = pushHStoreValue(&state, WHS_BEGIN_ARRAY, &value);
-           }
-       }
+       resetStringInfo(&tmp);
+       appendBinaryStringInfo(&tmp, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
+       escape_json(&dst, tmp.data);
+       appendStringInfoString(&dst, ": ");
+       if (HS_VALISNULL(entries, i))
+           appendStringInfoString(&dst, "null");
        else
        {
-           counters[i]++;
+           resetStringInfo(&tmp);
+           appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
+           escape_json(&dst, tmp.data);
        }
 
-       if (isnull)
-       {
-           value.type = hsvNull;
-           value.size = sizeof(HEntry);
-       }
-       else
-       {
-           value.type = valueType;
-           switch(valueType)
-           {
-               case hsvBool:
-                   value.boolean = DatumGetBool(datum);
-                   value.size = sizeof(HEntry);
-                   break;
-               case hsvString:
-                   if (castOid != InvalidOid)
-                       datum = FunctionCall1(&castInfo, datum);
-                   value.string.val = VARDATA_ANY(datum);
-                   value.string.len = VARSIZE_ANY_EXHDR(datum);
-                   value.size = sizeof(HEntry) + value.string.len;
-                   break;
-               case hsvNumeric:
-                   if (castOid != InvalidOid)
-                       datum = FunctionCall1(&castInfo, datum);
-                   value.numeric = DatumGetNumeric(datum);
-                   value.size = sizeof(HEntry)*2 + VARSIZE_ANY(value.numeric);
-                   break;
-               default:
-                   elog(ERROR, "wrong hstore scalar type");
-           }
-       }
-
-       result = pushHStoreValue(&state, WHS_ELEM, &value);
+       if (i + 1 != count)
+           appendStringInfoString(&dst, ", ");
    }
+   appendStringInfoChar(&dst, '}');
 
-   for(i=0; i<ncounters; i++)
-       result = pushHStoreValue(&state, WHS_END_ARRAY, NULL);
-
-   PG_RETURN_POINTER(hstoreDump(result));
-}
-
-PG_FUNCTION_INFO_V1(hstore_print);
-Datum      hstore_print(PG_FUNCTION_ARGS);
-Datum
-hstore_print(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   int         flags = 0;
-   text        *out;
-   StringInfo  str;
-
-   if (PG_GETARG_BOOL(1))
-       flags |= PrettyPrint;
-   if (PG_GETARG_BOOL(2))
-       flags |= ArrayCurlyBraces;
-   if (PG_GETARG_BOOL(3))
-       flags |= RootHashDecorated;
-   if (PG_GETARG_BOOL(4))
-       flags |= JsonOutput;
-   if (PG_GETARG_BOOL(5))
-       flags |= LooseOutput;
-
-   str = makeStringInfo();
-   appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
-
-   HStoreToCString(str, (HS_ISEMPTY(hs)) ? NULL : VARDATA(hs),
-                   VARSIZE(hs), flags);
-
-   out = (text*)str->data;
-   SET_VARSIZE(out, str->len);
-
-   PG_RETURN_TEXT_P(out);
-}
-
-void _PG_init(void);
-void
-_PG_init(void)
-{
-   DefineCustomBoolVariable(
-       "hstore.pretty_print",
-       "Enable pretty print",
-       "Enable pretty print of hstore type",
-       &pretty_print_var,
-       pretty_print_var,
-       PGC_USERSET,
-       GUC_NOT_IN_SAMPLE,
-       NULL,
-       NULL,
-       NULL
-   );
-
-   EmitWarningsOnPlaceholders("hstore");
-}
-
-uint32
-compressHStore(HStoreValue *v, char *buffer)
-{
-   uint32  l = compressJsonb(v, buffer);
-
-   /*
-    * Check for the future: currently jsonb has this true, but in
-    * future it could be changed
-    */
-   Assert(l < sizeof(uint32) || ( *(uint32*)buffer & HS_FLAG_NEWVERSION ));
-
-   return l;
+   PG_RETURN_TEXT_P(cstring_to_text(dst.data));
 }
index 1b00c966294229ecd087a3784e0b2ae21d352204..8ba7a05a6593cdee42137ae6ebfa6613d7477a38 100644 (file)
@@ -26,609 +26,167 @@ HSTORE_POLLUTE(hstore_skeys, skeys);
 HSTORE_POLLUTE(hstore_svals, svals);
 HSTORE_POLLUTE(hstore_each, each);
 
-static HStoreValue*
-arrayToHStoreSortedArray(ArrayType *a)
-{
-   Datum           *key_datums;
-   bool            *key_nulls;
-   int             key_count;
-   HStoreValue     *v;
-   int             i,
-                   j;
-   bool            hasNonUniq = false;
-
-   deconstruct_array(a,
-                     TEXTOID, -1, false, 'i',
-                     &key_datums, &key_nulls, &key_count);
-
-   if (key_count == 0)
-       return NULL;
-
-   /*
-    * A text array uses at least eight bytes per element, so any overflow in
-    * "key_count * sizeof(HStorePair)" is small enough for palloc() to catch.
-    * However, credible improvements to the array format could invalidate
-    * that assumption.  Therefore, use an explicit check rather than relying
-    * on palloc() to complain.
-    */
-   if (key_count > MaxAllocSize / sizeof(HStorePair))
-       ereport(ERROR,
-               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-             errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
-                    key_count, (int) (MaxAllocSize / sizeof(HStorePair)))));
-
-   v = palloc(sizeof(*v));
-   v->type = hsvArray;
-   v->array.scalar = false;
-   v->array.elems = palloc(sizeof(*v->hash.pairs) * key_count);
-
-   for (i = 0, j = 0; i < key_count; i++)
-   {
-       if (!key_nulls[i])
-       {
-           v->array.elems[j].type = hsvString;
-           v->array.elems[j].string.val = VARDATA(key_datums[i]);
-           v->array.elems[j].string.len = VARSIZE(key_datums[i]) - VARHDRSZ;
-           j++;
-       }
-   }
-   v->array.nelems = j;
-
-   if (v->array.nelems > 1)
-       qsort_arg(v->array.elems, v->array.nelems, sizeof(*v->array.elems),
-                 compareJsonbStringValue /* compareHStoreStringValue */, &hasNonUniq);
-
-   if (hasNonUniq)
-   {
-       HStoreValue *ptr = v->array.elems + 1,
-                   *res = v->array.elems;
-
-       while (ptr - v->array.elems < v->array.nelems)
-       {
-           if (!(ptr->string.len == res->string.len &&
-                 memcmp(ptr->string.val, res->string.val, ptr->string.len) == 0))
-           {
-               res++;
-               *res = *ptr;
-           }
-
-           ptr++;
-       }
-
-       v->array.nelems = res + 1 - v->array.elems;
-   }
-
-   return v;
-}
 
-static HStoreValue*
-findInHStoreSortedArray(HStoreValue *a, uint32 *lowbound,
-                       char *key, uint32 keylen)
+/*
+ * 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)
 {
-   HStoreValue     *stopLow = a->array.elems + ((lowbound) ? *lowbound : 0),
-                   *stopHigh = a->array.elems + a->array.nelems,
-                   *stopMiddle;
+   HEntry     *entries = ARRPTR(hs);
+   int         stopLow = lowbound ? *lowbound : 0;
+   int         stopHigh = HS_COUNT(hs);
+   int         stopMiddle;
+   char       *base = STRPTR(hs);
 
    while (stopLow < stopHigh)
    {
-       int diff;
+       int         difference;
 
        stopMiddle = stopLow + (stopHigh - stopLow) / 2;
 
-       if (keylen == stopMiddle->string.len)
-           diff = memcmp(stopMiddle->string.val, key, keylen);
+       if (HS_KEYLEN(entries, stopMiddle) == keylen)
+           difference = memcmp(HS_KEY(entries, base, stopMiddle), key, keylen);
        else
-           diff = (stopMiddle->string.len > keylen) ? 1 : -1;
+           difference = (HS_KEYLEN(entries, stopMiddle) > keylen) ? 1 : -1;
 
-       if (diff == 0)
+       if (difference == 0)
        {
            if (lowbound)
-               *lowbound = (stopMiddle - a->array.elems) + 1;
+               *lowbound = stopMiddle + 1;
            return stopMiddle;
        }
-       else if (diff < 0)
-       {
+       else if (difference < 0)
            stopLow = stopMiddle + 1;
-       }
        else
-       {
            stopHigh = stopMiddle;
-       }
    }
 
    if (lowbound)
-       *lowbound = (stopLow - a->array.elems) + 1;
-
-   return NULL;
-}
-
-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);
-   HStoreValue *v = NULL;
-   text        *out;
-
-   if (!HS_ISEMPTY(hs))
-       v = findUncompressedHStoreValue(VARDATA(hs),
-                                       HS_FLAG_HASH | HS_FLAG_ARRAY,
-                                       NULL,
-                                       VARDATA_ANY(key),
-                                       VARSIZE_ANY_EXHDR(key));
-
-   if ((out = HStoreValueToText(v)) == NULL)
-       PG_RETURN_NULL();
-   else
-       PG_RETURN_TEXT_P(out);
-}
-
-PG_FUNCTION_INFO_V1(hstore_fetchval_numeric);
-Datum      hstore_fetchval_numeric(PG_FUNCTION_ARGS);
-Datum
-hstore_fetchval_numeric(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   text        *key = PG_GETARG_TEXT_PP(1);
-   HStoreValue *v = NULL;
-
-   if (!HS_ISEMPTY(hs))
-       v = findUncompressedHStoreValue(VARDATA(hs),
-                                       HS_FLAG_HASH | HS_FLAG_ARRAY,
-                                       NULL,
-                                       VARDATA_ANY(key),
-                                       VARSIZE_ANY_EXHDR(key));
-
-   if (v && v->type == hsvNumeric)
-   {
-       Numeric     out = palloc(VARSIZE_ANY(v->numeric));
-
-       memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
-       PG_RETURN_NUMERIC(out);
-   }
-
-   PG_RETURN_NULL();
+       *lowbound = stopLow;
+   return -1;
 }
 
-PG_FUNCTION_INFO_V1(hstore_fetchval_boolean);
-Datum      hstore_fetchval_boolean(PG_FUNCTION_ARGS);
-Datum
-hstore_fetchval_boolean(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   text        *key = PG_GETARG_TEXT_PP(1);
-   HStoreValue *v = NULL;
-
-   if (!HS_ISEMPTY(hs))
-       v = findUncompressedHStoreValue(VARDATA(hs),
-                                       HS_FLAG_HASH | HS_FLAG_ARRAY,
-                                       NULL,
-                                       VARDATA_ANY(key),
-                                       VARSIZE_ANY_EXHDR(key));
-
-   if (v && v->type == hsvBool)
-       PG_RETURN_BOOL(v->boolean);
-
-   PG_RETURN_NULL();
-}
-
-PG_FUNCTION_INFO_V1(hstore_fetchval_n);
-Datum      hstore_fetchval_n(PG_FUNCTION_ARGS);
-Datum
-hstore_fetchval_n(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   int         i = PG_GETARG_INT32(1);
-   HStoreValue *v = NULL;
-   text        *out;
-
-   if (!HS_ISEMPTY(hs))
-       v = getHStoreValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, i);
-
-   if ((out = HStoreValueToText(v)) == NULL)
-       PG_RETURN_NULL();
-   else
-       PG_RETURN_TEXT_P(out);
-}
-
-PG_FUNCTION_INFO_V1(hstore_fetchval_n_numeric);
-Datum      hstore_fetchval_n_numeric(PG_FUNCTION_ARGS);
-Datum
-hstore_fetchval_n_numeric(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   int         i = PG_GETARG_INT32(1);
-   HStoreValue *v = NULL;
-
-   if (!HS_ISEMPTY(hs))
-       v = getHStoreValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, i);
-
-   if (v && v->type == hsvNumeric)
-   {
-       Numeric     out = palloc(VARSIZE_ANY(v->numeric));
-
-       memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
-       PG_RETURN_NUMERIC(out);
-   }
-
-   PG_RETURN_NULL();
-}
-
-PG_FUNCTION_INFO_V1(hstore_fetchval_n_boolean);
-Datum      hstore_fetchval_n_boolean(PG_FUNCTION_ARGS);
-Datum
-hstore_fetchval_n_boolean(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   int         i = PG_GETARG_INT32(1);
-   HStoreValue *v = NULL;
-
-   if (!HS_ISEMPTY(hs))
-       v = getHStoreValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, i);
-
-   if (v && v->type == hsvBool)
-       PG_RETURN_BOOL(v->boolean);
-
-   PG_RETURN_NULL();
-}
-
-static bool
-h_atoi(char *c, int l, int *acc)
+Pairs *
+hstoreArrayToPairs(ArrayType *a, int *npairs)
 {
-   bool    negative = false;
-   char    *p = c;
-
-   *acc = 0;
-
-   while(isspace(*p) && p - c < l)
-       p++;
-
-   if (p - c >= l)
-       return false;
-
-   if (*p == '-')
-   {
-       negative = true;
-       p++;
-   }
-   else if (*p == '+')
-   {
-       p++;
-   }
-
-   if (p - c >= l)
-       return false;
+   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);
 
-   while(p - c < l)
+   if (key_count == 0)
    {
-       if (!isdigit(*p))
-           return false;
-
-       *acc *= 10;
-       *acc += (*p - '0');
-       p++;
+       *npairs = 0;
+       return NULL;
    }
 
-   if (negative)
-       *acc = - *acc;
-
-   return true;
-}
-
-static HStoreValue*
-hstoreDeepFetch(HStore *in, ArrayType *path)
-{
-   HStoreValue         *v = NULL;
-   static HStoreValue  init /* could be returned */;
-   Datum               *path_elems;
-   bool                *path_nulls;
-   int                 path_len, i;
-
-   Assert(ARR_ELEMTYPE(path) == TEXTOID);
-
-   if (ARR_NDIM(path) > 1)
+   /*
+    * A text array uses at least eight bytes per element, so any overflow in
+    * "key_count * sizeof(Pairs)" is small enough for palloc() to catch.
+    * However, credible improvements to the array format could invalidate
+    * that assumption.  Therefore, use an explicit check rather than relying
+    * on palloc() to complain.
+    */
+   if (key_count > MaxAllocSize / sizeof(Pairs))
        ereport(ERROR,
-               (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-                errmsg("wrong number of array subscripts")));
-
-   if (HS_ROOT_COUNT(in) == 0)
-       return NULL;
-
-   deconstruct_array(path, TEXTOID, -1, false, 'i',
-                     &path_elems, &path_nulls, &path_len);
-
-   init.type = hsvBinary;
-   init.size = VARSIZE(in);
-   init.binary.data = VARDATA(in);
-   init.binary.len = VARSIZE_ANY_EXHDR(in);
-
-   v = &init;
+               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+             errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
+                    key_count, (int) (MaxAllocSize / sizeof(Pairs)))));
 
-   if (path_len == 0)
-       return v;
+   key_pairs = palloc(sizeof(Pairs) * key_count);
 
-   for(i=0; v != NULL && i<path_len; i++)
+   for (i = 0, j = 0; i < key_count; i++)
    {
-       uint32  header;
-
-       if (v->type != hsvBinary || path_nulls[i])
-           return NULL;
-
-       header = *(uint32*)v->binary.data;
-
-       if (header & HS_FLAG_HASH)
-       {
-           v = findUncompressedHStoreValue(v->binary.data, HS_FLAG_HASH,
-                                           NULL,
-                                           VARDATA_ANY(path_elems[i]),
-                                           VARSIZE_ANY_EXHDR(path_elems[i]));
-       }
-       else if (header & HS_FLAG_ARRAY)
-       {
-           int ith;
-
-           if (h_atoi(VARDATA_ANY(path_elems[i]),
-                      VARSIZE_ANY_EXHDR(path_elems[i]), &ith) == false)
-               return NULL;
-
-           if (ith < 0)
-           {
-               if (-ith > (int)(header & HS_COUNT_MASK))
-                   return NULL;
-               else
-                   ith = ((int)(header & HS_COUNT_MASK)) + ith;
-           }
-           else
-           {
-               if (ith >= (int)(header & HS_COUNT_MASK))
-                   return NULL;
-           }
-
-           v = getHStoreValue(v->binary.data, HS_FLAG_ARRAY, ith);
-       }
-       else
+       if (!key_nulls[i])
        {
-           elog(ERROR,"wrong hstore container type");
+           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++;
        }
    }
 
-   return v;
-}
-
-PG_FUNCTION_INFO_V1(hstore_fetchval_path);
-Datum      hstore_fetchval_path(PG_FUNCTION_ARGS);
-Datum
-hstore_fetchval_path(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   ArrayType   *path = PG_GETARG_ARRAYTYPE_P(1);
-   text        *out;
+   *npairs = hstoreUniquePairs(key_pairs, j, &bufsiz);
 
-   if ((out = HStoreValueToText(hstoreDeepFetch(hs, path))) == NULL)
-       PG_RETURN_NULL();
-   else
-       PG_RETURN_TEXT_P(out);
+   return key_pairs;
 }
 
-PG_FUNCTION_INFO_V1(hstore_fetchval_path_numeric);
-Datum      hstore_fetchval_path_numeric(PG_FUNCTION_ARGS);
-Datum
-hstore_fetchval_path_numeric(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   ArrayType   *path = PG_GETARG_ARRAYTYPE_P(1);
-   HStoreValue *v = NULL;
-
-   if (!HS_ISEMPTY(hs))
-       v = hstoreDeepFetch(hs, path);
-
-   if (v && v->type == hsvNumeric)
-   {
-       Numeric     out = palloc(VARSIZE_ANY(v->numeric));
-
-       memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
-       PG_RETURN_NUMERIC(out);
-   }
-
-   PG_RETURN_NULL();
-}
 
-PG_FUNCTION_INFO_V1(hstore_fetchval_path_boolean);
-Datum      hstore_fetchval_path_boolean(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_fetchval);
+Datum      hstore_fetchval(PG_FUNCTION_ARGS);
 Datum
-hstore_fetchval_path_boolean(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   ArrayType   *path = PG_GETARG_ARRAYTYPE_P(1);
-   HStoreValue *v = NULL;
-
-   if (!HS_ISEMPTY(hs))
-       v = hstoreDeepFetch(hs, path);
-
-   if (v && v->type == hsvBool)
-       PG_RETURN_BOOL(v->boolean);
-
-   PG_RETURN_NULL();
-}
-
-static HStore *
-HStoreValueToHStore(HStoreValue *v)
+hstore_fetchval(PG_FUNCTION_ARGS)
 {
-   HStore          *out;
-
-   if (v == NULL || v->type == hsvNull)
-   {
-       out = NULL;
-   }
-   else if (v->type == hsvString || v->type == hsvBool ||
-            v->type == hsvNumeric)
-   {
-       ToHStoreState   *state = NULL;
-       HStoreValue     *res;
-       int             r;
-       HStoreValue     scalarArray;
-
-       scalarArray.type = hsvArray;
-       scalarArray.array.scalar = true;
-       scalarArray.array.nelems = 1;
-
-       pushHStoreValue(&state, WHS_BEGIN_ARRAY, &scalarArray);
-       pushHStoreValue(&state, WHS_ELEM, v);
-       res = pushHStoreValue(&state, WHS_END_ARRAY, NULL);
-
-       out = palloc(VARHDRSZ + res->size);
-       SET_VARSIZE(out, VARHDRSZ + res->size);
-       r = compressHStore(res, VARDATA(out));
-       Assert(r <= res->size);
-       SET_VARSIZE(out, r + VARHDRSZ);
-   }
-   else
-   {
-       out = palloc(VARHDRSZ + v->size);
-
-       Assert(v->type == hsvBinary);
-       SET_VARSIZE(out, VARHDRSZ + v->binary.len);
-       memcpy(VARDATA(out), v->binary.data, v->binary.len);
-   }
-
-   return out;
-}
+   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));
 
-PG_FUNCTION_INFO_V1(hstore_fetchval_hstore);
-Datum      hstore_fetchval_hstore(PG_FUNCTION_ARGS);
-Datum
-hstore_fetchval_hstore(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   text        *key = PG_GETARG_TEXT_PP(1);
-   HStoreValue *v = NULL;
-   HStore      *out;
-
-   if (!HS_ISEMPTY(hs))
-       v = findUncompressedHStoreValue(VARDATA(hs),
-                                       HS_FLAG_HASH | HS_FLAG_ARRAY,
-                                       NULL,
-                                       VARDATA_ANY(key),
-                                       VARSIZE_ANY_EXHDR(key));
-
-   if ((out = HStoreValueToHStore(v)) == NULL)
+   if (idx < 0 || HS_VALISNULL(entries, idx))
        PG_RETURN_NULL();
-   else
-       PG_RETURN_POINTER(out);
-}
-
-PG_FUNCTION_INFO_V1(hstore_fetchval_n_hstore);
-Datum      hstore_fetchval_n_hstore(PG_FUNCTION_ARGS);
-Datum
-hstore_fetchval_n_hstore(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   int         i = PG_GETARG_INT32(1);
-   HStoreValue *v = NULL;
-   HStore      *out;
 
-   if (!HS_ISEMPTY(hs))
-       v = getHStoreValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, i);
+   out = cstring_to_text_with_len(HS_VAL(entries, STRPTR(hs), idx),
+                                  HS_VALLEN(entries, idx));
 
-   if ((out = HStoreValueToHStore(v)) == NULL)
-       PG_RETURN_NULL();
-   else
-       PG_RETURN_POINTER(out);
+   PG_RETURN_TEXT_P(out);
 }
 
-PG_FUNCTION_INFO_V1(hstore_fetchval_path_hstore);
-Datum      hstore_fetchval_path_hstore(PG_FUNCTION_ARGS);
-Datum
-hstore_fetchval_path_hstore(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   ArrayType   *path = PG_GETARG_ARRAYTYPE_P(1);
-   HStore      *out;
-
-   if ((out = HStoreValueToHStore(hstoreDeepFetch(hs, path))) == NULL)
-       PG_RETURN_NULL();
-   else
-       PG_RETURN_POINTER(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);
-   HStoreValue *v = NULL;
-
-   if (!HS_ISEMPTY(hs))
-       v = findUncompressedHStoreValue(VARDATA(hs),
-                                       HS_FLAG_HASH | HS_FLAG_ARRAY,
-                                       NULL,
-                                       VARDATA_ANY(key),
-                                       VARSIZE_ANY_EXHDR(key));
-
-   PG_RETURN_BOOL(v != NULL);
-}
-
-
-PG_FUNCTION_INFO_V1(hstore_exists_idx);
-Datum      hstore_exists_idx(PG_FUNCTION_ARGS);
-Datum
-hstore_exists_idx(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   int         ith = PG_GETARG_INT32(1);
-   HStoreValue *v = NULL;
-
-   if (!HS_ISEMPTY(hs))
-       v = getHStoreValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, ith);
-
-   PG_RETURN_BOOL(v != NULL);
-}
-
-PG_FUNCTION_INFO_V1(hstore_exists_path);
-Datum      hstore_exists_path(PG_FUNCTION_ARGS);
-Datum
-hstore_exists_path(PG_FUNCTION_ARGS)
-{
-   HStore      *hs = PG_GETARG_HS(0);
-   ArrayType   *path = PG_GETARG_ARRAYTYPE_P(1);
+   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(hstoreDeepFetch(hs, path) != NULL);
+   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);
-   HStoreValue     *v = arrayToHStoreSortedArray(keys);
-   int             i;
-   uint32          *plowbound = NULL, lowbound = 0;
-   bool            res = false;
-
-   if (HS_ISEMPTY(hs) || v == NULL || v->hash.npairs == 0)
-       PG_RETURN_BOOL(false);
-
-   if (HS_ROOT_IS_HASH(hs))
-       plowbound = &lowbound;
+   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 findUncompressedHStoreValue search; each search can
+    * 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 < v->array.nelems; i++)
+   for (i = 0; i < nkeys; i++)
    {
-       if (findUncompressedHStoreValueByValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, plowbound,
-                                              v->array.elems + i) != NULL)
+       int         idx = hstoreFindKey(hs, &lowbound,
+                                     key_pairs[i].key, key_pairs[i].keylen);
+
+       if (idx >= 0)
        {
            res = true;
            break;
@@ -644,36 +202,26 @@ 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);
-   HStoreValue     *v = arrayToHStoreSortedArray(keys);
-   int             i;
-   uint32          *plowbound = NULL, lowbound = 0;
-   bool            res = true;
-
-   if (HS_ISEMPTY(hs) || v == NULL || v->array.nelems == 0)
-   {
-
-       if (v == NULL || v->array.nelems == 0)
-           PG_RETURN_BOOL(true); /* compatibility */
-       else
-           PG_RETURN_BOOL(false);
-   }
+   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 = true;
 
-   if (HS_ROOT_IS_HASH(hs))
-       plowbound = &lowbound;
    /*
     * we exploit the fact that the pairs list is already sorted into strictly
-    * increasing order to narrow the findUncompressedHStoreValue search;
-    * each search can start one entry past the previous "found" entry,
-    * or at the lower bound of the last search.
+    * 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 < v->array.nelems; i++)
+   for (i = 0; i < nkeys; i++)
    {
-       if (findUncompressedHStoreValueByValue(VARDATA(hs),
-                                              HS_FLAG_HASH | HS_FLAG_ARRAY,
-                                              plowbound,
-                                              v->array.elems + i) == NULL)
+       int         idx = hstoreFindKey(hs, &lowbound,
+                                     key_pairs[i].key, key_pairs[i].keylen);
+
+       if (idx < 0)
        {
            res = false;
            break;
@@ -689,18 +237,14 @@ 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);
-   HStoreValue *v = NULL;
-
-   if (!HS_ISEMPTY(hs))
-       v = findUncompressedHStoreValue(VARDATA(hs),
-                                       HS_FLAG_HASH | HS_FLAG_ARRAY,
-                                       NULL,
-                                       VARDATA_ANY(key),
-                                       VARSIZE_ANY_EXHDR(key));
-
-   PG_RETURN_BOOL(!(v == NULL || v->type == hsvNull));
+   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);
 }
 
 
@@ -709,135 +253,123 @@ Datum       hstore_delete(PG_FUNCTION_ARGS);
 Datum
 hstore_delete(PG_FUNCTION_ARGS)
 {
-   HStore          *in = 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(in));
-   ToHStoreState   *toState = NULL;
-   HStoreIterator  *it;
-   uint32          r;
-   HStoreValue     v, *res = NULL;
-   bool            skipNested = false;
-
-   SET_VARSIZE(out, VARSIZE(in));
-
-   if (HS_ISEMPTY(in))
-       PG_RETURN_POINTER(out);
+   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         count = HS_COUNT(hs);
+   int         outcount = 0;
 
-   it = HStoreIteratorInit(VARDATA(in));
+   SET_VARSIZE(out, VARSIZE(hs));
+   HS_SETCOUNT(out, count);    /* temporary! */
 
-   while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+   bufs = STRPTR(hs);
+   es = ARRPTR(hs);
+   bufd = ptrd = STRPTR(out);
+   ed = ARRPTR(out);
+
+   for (i = 0; i < count; ++i)
    {
-       skipNested = true;
+       int         len = HS_KEYLEN(es, i);
+       char       *ptrs = HS_KEY(es, bufs, i);
 
-       if ((r == WHS_ELEM || r == WHS_KEY) &&
-           (v.type == hsvString && keylen == v.string.len &&
-            memcmp(keyptr, v.string.val, keylen) == 0))
+       if (!(len == keylen && memcmp(ptrs, keyptr, keylen) == 0))
        {
-           if (r == WHS_KEY)
-               /* skip corresponding value */
-               HStoreIteratorGet(&it, &v, true);
+           int         vallen = HS_VALLEN(es, i);
 
-           continue;
+           HS_COPYITEM(ed, bufd, ptrd, ptrs, len, vallen, HS_VALISNULL(es, i));
+           ++outcount;
        }
-
-       res = pushHStoreValue(&toState, r, &v);
    }
 
-   if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
-                      (res->type == hsvHash && res->hash.npairs == 0) )
-   {
-       SET_VARSIZE(out, VARHDRSZ);
-   }
-   else
-   {
-       r = compressHStore(res, VARDATA(out));
-       SET_VARSIZE(out, r + VARHDRSZ);
-   }
+   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          *in = PG_GETARG_HS(0);
-   HStore          *out = palloc(VARSIZE(in));
-   HStoreValue     *a = arrayToHStoreSortedArray(PG_GETARG_ARRAYTYPE_P(1));
-   HStoreIterator  *it;
-   ToHStoreState   *toState = NULL;
-   uint32          r, i = 0;
-   HStoreValue     v, *res = NULL;
-   bool            skipNested = false;
-   bool            isHash = false;
-
-
-   if (HS_ISEMPTY(in) || a == NULL || a->array.nelems == 0)
+   HStore     *hs = PG_GETARG_HS(0);
+   HStore     *out = palloc(VARSIZE(hs));
+   int         hs_count = HS_COUNT(hs);
+   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));
+   HS_SETCOUNT(out, hs_count); /* temporary! */
+
+   ps = STRPTR(hs);
+   es = ARRPTR(hs);
+   bufd = pd = STRPTR(out);
+   ed = ARRPTR(out);
+
+   if (nkeys == 0)
    {
-       memcpy(out, in, VARSIZE(in));
+       /* return a copy of the input, unchanged */
+       memcpy(out, hs, VARSIZE(hs));
+       HS_FIXSIZE(out, hs_count);
+       HS_SETCOUNT(out, hs_count);
        PG_RETURN_POINTER(out);
    }
 
-   it = HStoreIteratorInit(VARDATA(in));
+   /*
+    * 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
+    */
 
-   while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+   for (i = j = 0; i < hs_count;)
    {
+       int         difference;
 
-       if (skipNested == false)
+       if (j >= nkeys)
+           difference = -1;
+       else
        {
-           Assert(v.type == hsvArray || v.type == hsvHash);
-           isHash = (v.type == hsvArray) ? false : true;
-           skipNested = true;
-       }
+           int         skeylen = HS_KEYLEN(es, i);
 
-       if ((r == WHS_ELEM || r == WHS_KEY) && v.type == hsvString &&
-           i < a->array.nelems)
-       {
-           int diff;
-
-           if (isHash)
-           {
-               do {
-                   diff = compareHStoreStringValue(&v, a->array.elems + i,
-                                                   NULL);
-
-                   if (diff >= 0)
-                       i++;
-               } while(diff > 0 && i < a->array.nelems);
-           }
+           if (skeylen == key_pairs[j].keylen)
+               difference = memcmp(HS_KEY(es, ps, i),
+                                   key_pairs[j].key,
+                                   key_pairs[j].keylen);
            else
-           {
-               diff = (findInHStoreSortedArray(a, NULL,
-                                               v.string.val,
-                                               v.string.len) == NULL) ? 1 : 0;
-           }
-
-           if (diff == 0)
-           {
-               if (r == WHS_KEY)
-                   /* skip corresponding value */
-                   HStoreIteratorGet(&it, &v, true);
-
-               continue;
-           }
+               difference = (skeylen > key_pairs[j].keylen) ? 1 : -1;
        }
 
-       res = pushHStoreValue(&toState, r, &v);
+       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;
+       }
    }
 
-   if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
-                      (res->type == hsvHash && res->hash.npairs == 0) )
-   {
-       SET_VARSIZE(out, VARHDRSZ);
-   }
-   else
-   {
-       r = compressHStore(res, VARDATA(out));
-       SET_VARSIZE(out, r + VARHDRSZ);
-   }
+   HS_FINALIZE(out, outcount, bufd, pd);
 
    PG_RETURN_POINTER(out);
 }
@@ -848,1205 +380,357 @@ Datum      hstore_delete_hstore(PG_FUNCTION_ARGS);
 Datum
 hstore_delete_hstore(PG_FUNCTION_ARGS)
 {
-   HStore          *hs1 = PG_GETARG_HS(0);
-   HStore          *hs2 = PG_GETARG_HS(1);
-   HStore          *out = palloc(VARSIZE(hs1));
-   HStoreIterator  *it1, *it2;
-   ToHStoreState   *toState = NULL;
-   uint32          r1, r2;
-   HStoreValue     v1, v2, *res = NULL;
-   bool            isHash1, isHash2;
-
-   if (HS_ISEMPTY(hs1) || HS_ISEMPTY(hs2))
-   {
-       memcpy(out, hs1, VARSIZE(hs1));
+   HStore     *hs = PG_GETARG_HS(0);
+   HStore     *hs2 = PG_GETARG_HS(1);
+   HStore     *out = palloc(VARSIZE(hs));
+   int         hs_count = HS_COUNT(hs);
+   int         hs2_count = HS_COUNT(hs2);
+   char       *ps,
+              *ps2,
+              *bufd,
+              *pd;
+   HEntry     *es,
+              *es2,
+              *ed;
+   int         i,
+               j;
+   int         outcount = 0;
+
+   SET_VARSIZE(out, VARSIZE(hs));
+   HS_SETCOUNT(out, hs_count); /* 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));
+       HS_FIXSIZE(out, hs_count);
+       HS_SETCOUNT(out, hs_count);
        PG_RETURN_POINTER(out);
    }
 
-   it1 = HStoreIteratorInit(VARDATA(hs1));
-   r1 = HStoreIteratorGet(&it1, &v1, false);
-   isHash1 = (v1.type == hsvArray) ? false : true;
-
-   it2 = HStoreIteratorInit(VARDATA(hs2));
-   r2 = HStoreIteratorGet(&it2, &v2, false);
-   isHash2 = (v2.type == hsvArray) ? false : true;
-
-   res = pushHStoreValue(&toState, r1, &v1);
-
-   if (isHash1 == true && isHash2 == true)
-   {
-       bool            fin2 = false,
-                       keyIsDef = false;
-
-       while((r1 = HStoreIteratorGet(&it1, &v1, true)) != 0)
-       {
-           if (r1 == WHS_KEY && fin2 == false)
-           {
-               int diff  = 1;
-
-               if (keyIsDef)
-                   r2 = WHS_KEY;
-
-               while(keyIsDef ||
-                     (r2 = HStoreIteratorGet(&it2, &v2, true)) != 0)
-               {
-                   if (r2 != WHS_KEY)
-                       continue;
-
-                   diff = compareHStoreStringValue(&v1, &v2, NULL);
-
-                   if (diff > 0 && keyIsDef)
-                       keyIsDef = false;
-                   if (diff <= 0)
-                       break;
-               }
-
-               if (r2 == 0)
-               {
-                   fin2 = true;
-               }
-               else if (diff == 0)
-               {
-                   HStoreValue     vk;
-
-                   keyIsDef = false;
-
-                   r1 = HStoreIteratorGet(&it1, &vk, true);
-                   r2 = HStoreIteratorGet(&it2, &v2, true);
-
-                   Assert(r1 == WHS_VALUE && r2 == WHS_VALUE);
-
-                   if (compareHStoreValue(&vk, &v2) != 0)
-                   {
-                       res = pushHStoreValue(&toState, WHS_KEY, &v1);
-                       res = pushHStoreValue(&toState, WHS_VALUE, &vk);
-                   }
-
-                   continue;
-               }
-               else
-               {
-                   keyIsDef = true;
-               }
-           }
-
-           res = pushHStoreValue(&toState, r1, &v1);
-       }
-   }
-   else
-   {
-       while((r1 = HStoreIteratorGet(&it1, &v1, true)) != 0)
-       {
-
-           if (r1 == WHS_ELEM || r1 == WHS_KEY)
-           {
-               int diff = 1;
-
-               it2 = HStoreIteratorInit(VARDATA(hs2));
-
-               r2 = HStoreIteratorGet(&it2, &v2, false);
-
-               while(diff && (r2 = HStoreIteratorGet(&it2, &v2, true)) != 0)
-               {
-                   if (r2 == WHS_KEY || r2 == WHS_VALUE || r2 == WHS_ELEM)
-                       diff = compareHStoreValue(&v1, &v2);
-               }
-
-               if (diff == 0)
-               {
-                   if (r1 == WHS_KEY)
-                       HStoreIteratorGet(&it1, &v1, true);
-                   continue;
-               }
-           }
-
-           res = pushHStoreValue(&toState, r1, &v1);
-       }
-   }
-
-   if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
-                      (res->type == hsvHash && res->hash.npairs == 0) )
-   {
-       SET_VARSIZE(out, VARHDRSZ);
-   }
-   else
-   {
-       int r = compressHStore(res, VARDATA(out));
-       SET_VARSIZE(out, r + VARHDRSZ);
-   }
-
-   PG_RETURN_POINTER(out);
-}
-
-static HStoreValue*
-deletePathDo(HStoreIterator **it, Datum    *path_elems,
-            bool *path_nulls, int path_len,
-            ToHStoreState  **st, int level)
-{
-   HStoreValue v, *res = NULL;
-   int         r;
-
-   r = HStoreIteratorGet(it, &v, false);
+   /*
+    * 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
+    */
 
-   if (r == WHS_BEGIN_ARRAY)
+   for (i = j = 0; i < hs_count;)
    {
-       int     skipIdx, i;
-       uint32  n = v.array.nelems;
-
-       skipIdx = n;
-       if (level >= path_len || path_nulls[level] ||
-           h_atoi(VARDATA_ANY(path_elems[level]),
-                  VARSIZE_ANY_EXHDR(path_elems[level]), &skipIdx) == false)
-       {
-           skipIdx = n;
-       }
-       else if (skipIdx < 0)
-       {
-           if (-skipIdx > n)
-               skipIdx = n;
-           else
-               skipIdx = n + skipIdx;
-       }
-
-       if (skipIdx > n)
-           skipIdx = n;
-
-       if (skipIdx == 0 && n == 1)
-       {
-           r = HStoreIteratorGet(it, &v, true);
-           Assert(r == WHS_ELEM);
-           r = HStoreIteratorGet(it, &v, true);
-           Assert(r == WHS_END_ARRAY);
-           return NULL;
-       }
-
-       pushHStoreValue(st, r, &v);
-
-       for(i=0; i<skipIdx; i++) {
-           r = HStoreIteratorGet(it, &v, true);
-           Assert(r == WHS_ELEM);
-           res = pushHStoreValue(st, r, &v);
-       }
-
-       if (level >= path_len || skipIdx == n) {
-           r = HStoreIteratorGet(it, &v, true);
-           Assert(r == WHS_END_ARRAY);
-           res = pushHStoreValue(st, r, &v);
-           return res;
-       }
+       int         difference;
 
-       if (level == path_len - 1)
-       {
-           /* last level in path, skip all elem */
-           r = HStoreIteratorGet(it, &v, true);
-           Assert(r == WHS_ELEM);
-       }
+       if (j >= hs2_count)
+           difference = -1;
        else
        {
-           res = deletePathDo(it, path_elems, path_nulls, path_len, st,
-                              level + 1);
-       }
+           int         skeylen = HS_KEYLEN(es, i);
+           int         s2keylen = HS_KEYLEN(es2, j);
 
-       for(i = skipIdx + 1; i<n; i++) {
-           r = HStoreIteratorGet(it, &v, true);
-           Assert(r == WHS_ELEM);
-           res = pushHStoreValue(st, r, &v);
-       }
-
-       r = HStoreIteratorGet(it, &v, true);
-       Assert(r == WHS_END_ARRAY);
-       res = pushHStoreValue(st, r, &v);
-   }
-   else if (r == WHS_BEGIN_HASH)
-   {
-       int         i;
-       uint32      n = v.hash.npairs;
-       HStoreValue k;
-       bool        done = false;
-
-       if (n == 1 && level == path_len - 1)
-       {
-           r = HStoreIteratorGet(it, &k, false);
-           Assert(r == WHS_KEY);
-
-           if ( path_nulls[level] == false &&
-                k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
-                memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
-                       k.string.len) == 0)
-           {
-               r = HStoreIteratorGet(it, &v, true);
-               Assert(r == WHS_VALUE);
-               r = HStoreIteratorGet(it, &v, true);
-               Assert(r == WHS_END_HASH);
-               return NULL;
-           }
-
-           pushHStoreValue(st, WHS_BEGIN_HASH, &v);
-           pushHStoreValue(st, WHS_KEY, &k);
-           r = HStoreIteratorGet(it, &v, true);
-           Assert(r == WHS_VALUE);
-           pushHStoreValue(st, r, &v);
-           r = HStoreIteratorGet(it, &v, true);
-           Assert(r == WHS_END_HASH);
-           return pushHStoreValue(st, r, &v);
+           if (skeylen == s2keylen)
+               difference = memcmp(HS_KEY(es, ps, i),
+                                   HS_KEY(es2, ps2, j),
+                                   skeylen);
+           else
+               difference = (skeylen > s2keylen) ? 1 : -1;
        }
 
-       pushHStoreValue(st, WHS_BEGIN_HASH, &v);
-
-       if (level >= path_len || path_nulls[level])
-           done = true;
-
-       for(i=0; i<n; i++)
+       if (difference > 0)
+           ++j;
+       else if (difference == 0)
        {
-           r = HStoreIteratorGet(it, &k, false);
-           Assert(r == WHS_KEY);
+           int         svallen = HS_VALLEN(es, i);
+           int         snullval = HS_VALISNULL(es, i);
 
-           if (done == false &&
-               k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
-               memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
-                      k.string.len) == 0)
+           if (snullval != HS_VALISNULL(es2, j)
+               || (!snullval
+                   && (svallen != HS_VALLEN(es2, j)
+           || memcmp(HS_VAL(es, ps, i), HS_VAL(es2, ps2, j), svallen) != 0)))
            {
-               done = true;
-
-               if (level == path_len - 1)
-               {
-                   r = HStoreIteratorGet(it, &v, true);
-                   Assert(r == WHS_VALUE);
-               }
-               else
-               {
-                   pushHStoreValue(st, r, &k);
-                   res = deletePathDo(it, path_elems, path_nulls, path_len,
-                                      st, level + 1);
-                   if (res == NULL)
-                   {
-                       v.type = hsvNull;
-                       pushHStoreValue(st, WHS_VALUE, &v);
-                   }
-               }
-
-               continue;
+               HS_COPYITEM(ed, bufd, pd,
+                           HS_KEY(es, ps, i), HS_KEYLEN(es, i),
+                           svallen, snullval);
+               ++outcount;
            }
-
-           pushHStoreValue(st, r, &k);
-           r = HStoreIteratorGet(it, &v, true);
-           Assert(r == WHS_VALUE);
-           pushHStoreValue(st, r, &v);
+           ++i, ++j;
        }
-
-       r = HStoreIteratorGet(it, &v, true);
-       Assert(r == WHS_END_HASH);
-       res = pushHStoreValue(st, r, &v);
-   }
-   else if (r == WHS_ELEM || r == WHS_VALUE) /* just a string or null */
-   {
-       pushHStoreValue(st, r, &v);
-       res = (void*)0x01; /* dummy value */
-   }
-   else
-   {
-       elog(ERROR, "impossible state");
-   }
-
-   return res;
-}
-
-
-PG_FUNCTION_INFO_V1(hstore_delete_path);
-Datum      hstore_delete_path(PG_FUNCTION_ARGS);
-Datum
-hstore_delete_path(PG_FUNCTION_ARGS)
-{
-   HStore          *in = PG_GETARG_HS(0);
-   HStore          *out = palloc(VARSIZE(in));
-   ArrayType       *path = PG_GETARG_ARRAYTYPE_P(1);
-   HStoreValue     *res = NULL;
-   Datum           *path_elems;
-   bool            *path_nulls;
-   int             path_len;
-   HStoreIterator  *it;
-   ToHStoreState   *st = NULL;
-
-   Assert(ARR_ELEMTYPE(path) == TEXTOID);
-
-   if (ARR_NDIM(path) > 1)
-       ereport(ERROR,
-               (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-                errmsg("wrong number of array subscripts")));
-
-   if (HS_ROOT_COUNT(in) == 0)
-   {
-       memcpy(out, in, VARSIZE(in));
-       PG_RETURN_POINTER(out);
-   }
-
-   deconstruct_array(path, TEXTOID, -1, false, 'i',
-                     &path_elems, &path_nulls, &path_len);
-
-   if (path_len == 0)
-   {
-       memcpy(out, in, VARSIZE(in));
-       PG_RETURN_POINTER(out);
-   }
-
-   it = HStoreIteratorInit(VARDATA(in));
-
-   res = deletePathDo(&it, path_elems, path_nulls, path_len, &st, 0);
-
-   if (res == NULL)
-   {
-       SET_VARSIZE(out, VARHDRSZ);
-   }
-   else
-   {
-       int             sz;
-
-       sz = compressHStore(res, VARDATA(out));
-       SET_VARSIZE(out, sz + VARHDRSZ);
-   }
-
-   PG_RETURN_POINTER(out);
-}
-
-PG_FUNCTION_INFO_V1(hstore_delete_idx);
-Datum      hstore_delete_idx(PG_FUNCTION_ARGS);
-Datum
-hstore_delete_idx(PG_FUNCTION_ARGS)
-{
-   HStore          *in = PG_GETARG_HS(0);
-   int             idx = PG_GETARG_INT32(1);
-   HStore          *out = palloc(VARSIZE(in));
-   ToHStoreState   *toState = NULL;
-   HStoreIterator  *it;
-   uint32          r, i = 0, n;
-   HStoreValue     v, *res = NULL;
-
-   if (HS_ISEMPTY(in))
-   {
-       memcpy(out, in, VARSIZE(in));
-       PG_RETURN_POINTER(out);
-   }
-
-   it = HStoreIteratorInit(VARDATA(in));
-
-   r = HStoreIteratorGet(&it, &v, false);
-   if (r == WHS_BEGIN_ARRAY)
-       n = v.array.nelems;
-   else
-       n = v.hash.npairs;
-
-   if (idx < 0)
-   {
-       if (-idx > n)
-           idx = n;
        else
-           idx = n + idx;
-   }
-
-   if (idx >= n)
-   {
-       memcpy(out, in, VARSIZE(in));
-       PG_RETURN_POINTER(out);
-   }
-
-   pushHStoreValue(&toState, r, &v);
-
-   while((r = HStoreIteratorGet(&it, &v, true)) != 0)
-   {
-       if (r == WHS_ELEM || r == WHS_KEY)
        {
-           if (i++ == idx)
-           {
-               if (r == WHS_KEY)
-                   HStoreIteratorGet(&it, &v, true); /* skip value */
-               continue;
-           }
+           HS_COPYITEM(ed, bufd, pd,
+                       HS_KEY(es, ps, i), HS_KEYLEN(es, i),
+                       HS_VALLEN(es, i), HS_VALISNULL(es, i));
+           ++outcount;
+           ++i;
        }
-
-       res = pushHStoreValue(&toState, r, &v);
    }
 
-   if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
-                      (res->type == hsvHash && res->hash.npairs == 0) )
-   {
-       SET_VARSIZE(out, VARHDRSZ);
-   }
-   else
-   {
-       r = compressHStore(res, VARDATA(out));
-       SET_VARSIZE(out, r + VARHDRSZ);
-   }
+   HS_FINALIZE(out, outcount, bufd, pd);
 
    PG_RETURN_POINTER(out);
 }
 
-static void
-convertScalarToString(HStoreValue *v)
-{
-   switch(v->type) {
-       case hsvNull:
-           elog(ERROR, "key in hstore type could not be a NULL");
-           break;
-       case hsvBool:
-           v->type = hsvString;
-           v->string.val = pnstrdup((v->boolean) ? "t" : "f", 1);
-           v->string.len = 1;
-           v->size = sizeof(HEntry) + v->string.len;
-           break;
-       case hsvNumeric:
-           v->type = hsvString;
-           v->string.val = DatumGetCString(
-                           DirectFunctionCall1(numeric_out,
-                                               PointerGetDatum(v->numeric)));
-           v->string.len = strlen(v->string.val);
-           v->size = sizeof(HEntry) + v->string.len;
-           break;
-       case hsvString:
-           break;
-       default:
-           elog(ERROR,"wrong hstore scalar type");
-   }
-}
-
-static HStoreValue *
-IteratorConcat(HStoreIterator **it1, HStoreIterator **it2,
-              ToHStoreState **toState)
-{
-   uint32          r1, r2, rk1, rk2;
-   HStoreValue     v1, v2, *res = NULL;
-
-   r1 = rk1 = HStoreIteratorGet(it1, &v1, false);
-   r2 = rk2 = HStoreIteratorGet(it2, &v2, false);
-
-   if (rk1 == WHS_BEGIN_HASH && rk2 == WHS_BEGIN_HASH)
-   {
-       bool            fin2 = false,
-                       keyIsDef = false;
-
-       res = pushHStoreValue(toState, r1, &v1);
-
-       for(;;)
-       {
-           r1 = HStoreIteratorGet(it1, &v1, true);
-
-           Assert(r1 == WHS_KEY || r1 == WHS_VALUE || r1 == WHS_END_HASH);
-
-           if (r1 == WHS_KEY && fin2 == false)
-           {
-               int diff  = 1;
-
-               if (keyIsDef)
-                   r2 = WHS_KEY;
-
-               while(keyIsDef || (r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
-               {
-                   if (r2 != WHS_KEY)
-                       continue;
-
-                   diff = compareHStoreStringValue(&v1, &v2, NULL);
-
-                   if (diff > 0)
-                   {
-                       if (keyIsDef)
-                           keyIsDef = false;
-
-                       pushHStoreValue(toState, r2, &v2);
-                       r2 = HStoreIteratorGet(it2, &v2, true);
-                       Assert(r2 == WHS_VALUE);
-                       pushHStoreValue(toState, r2, &v2);
-                   }
-                   else if (diff <= 0)
-                   {
-                       break;
-                   }
-               }
-
-               if (r2 == 0)
-               {
-                   fin2 = true;
-               }
-               else if (diff == 0)
-               {
-                   keyIsDef = false;
-
-                   pushHStoreValue(toState, r1, &v1);
-
-                   r1 = HStoreIteratorGet(it1, &v1, true); /* ignore */
-                   r2 = HStoreIteratorGet(it2, &v2, true); /* new val */
-
-                   Assert(r1 == WHS_VALUE && r2 == WHS_VALUE);
-                   pushHStoreValue(toState, r2, &v2);
-
-                   continue;
-               }
-               else
-               {
-                   keyIsDef = true;
-               }
-           }
-           else if (r1 == WHS_END_HASH)
-           {
-               if (r2 != 0)
-               {
-                   if (keyIsDef)
-                       r2 = WHS_KEY;
-
-                   while(keyIsDef ||
-                         (r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
-                   {
-                       if (r2 != WHS_KEY)
-                           continue;
-
-                       pushHStoreValue(toState, r2, &v2);
-                       r2 = HStoreIteratorGet(it2, &v2, true);
-                       Assert(r2 == WHS_VALUE);
-                       pushHStoreValue(toState, r2, &v2);
-                       keyIsDef = false;
-                   }
-               }
-
-               res = pushHStoreValue(toState, r1, &v1);
-               break;
-           }
-
-           res = pushHStoreValue(toState, r1, &v1);
-       }
-   }
-   else if ((rk1 == WHS_BEGIN_HASH || rk1 == WHS_BEGIN_ARRAY) &&
-            (rk2 == WHS_BEGIN_HASH || rk2 == WHS_BEGIN_ARRAY))
-   {
-       if (rk1 == WHS_BEGIN_HASH && rk2 == WHS_BEGIN_ARRAY &&
-           v2.array.nelems % 2 != 0)
-           elog(ERROR, "hstore's array must have even number of elements");
-
-       res = pushHStoreValue(toState, r1, &v1);
-
-       for(;;)
-       {
-           r1 = HStoreIteratorGet(it1, &v1, true);
-           if (r1 == WHS_END_HASH || r1 == WHS_END_ARRAY)
-               break;
-           Assert(r1 == WHS_KEY || r1 == WHS_VALUE || r1 == WHS_ELEM);
-           pushHStoreValue(toState, r1, &v1);
-       }
-
-       while((r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
-       {
-           if (!(r2 == WHS_END_HASH || r2 == WHS_END_ARRAY))
-           {
-               if (rk1 == WHS_BEGIN_HASH)
-               {
-                   convertScalarToString(&v2);
-                   pushHStoreValue(toState, WHS_KEY, &v2);
-                   r2 = HStoreIteratorGet(it2, &v2, true);
-                   Assert(r2 == WHS_ELEM);
-                   pushHStoreValue(toState, WHS_VALUE, &v2);
-               }
-               else
-               {
-                   pushHStoreValue(toState, WHS_ELEM, &v2);
-               }
-           }
-       }
-
-       res = pushHStoreValue(toState,
-                             (rk1 == WHS_BEGIN_HASH) ? WHS_END_HASH : WHS_END_ARRAY,
-                             NULL/* signal to sort */);
-   }
-   else if ((rk1 & (WHS_VALUE | WHS_ELEM)) != 0)
-   {
-       if (v2.type == hsvArray && v2.array.scalar)
-       {
-           Assert(v2.array.nelems == 1);
-           r2 = HStoreIteratorGet(it2, &v2, false);
-           pushHStoreValue(toState, r1, &v2);
-       }
-       else
-       {
-           res = pushHStoreValue(toState, r2, &v2);
-           while((r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
-               res = pushHStoreValue(toState, r2, &v2);
-       }
-   }
-   else
-   {
-       elog(ERROR, "invalid concatnation of hstores");
-   }
-
-   return res;
-}
 
 PG_FUNCTION_INFO_V1(hstore_concat);
 Datum      hstore_concat(PG_FUNCTION_ARGS);
 Datum
-hstore_concat(PG_FUNCTION_ARGS)
-{
-   HStore          *hs1 = PG_GETARG_HS(0);
-   HStore          *hs2 = PG_GETARG_HS(1);
-   HStore          *out = palloc(VARSIZE(hs1) + VARSIZE(hs2));
-   ToHStoreState   *toState = NULL;
-   HStoreValue     *res;
-   HStoreIterator  *it1, *it2;
-
-   if (HS_ISEMPTY(hs1))
-   {
-       memcpy(out, hs2, VARSIZE(hs2));
-       PG_RETURN_POINTER(out);
-   }
-   else if (HS_ISEMPTY(hs2))
-   {
-       memcpy(out, hs1, VARSIZE(hs1));
-       PG_RETURN_POINTER(out);
-   }
-
-   it1 = HStoreIteratorInit(VARDATA(hs1));
-   it2 = HStoreIteratorInit(VARDATA(hs2));
-
-   res = IteratorConcat(&it1, &it2, &toState);
-
-   if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
-                      (res->type == hsvHash && res->hash.npairs == 0) )
-   {
-       SET_VARSIZE(out, VARHDRSZ);
-   }
-   else
-   {
-       uint32 r;
-
-       if (res->type == hsvArray && res->array.nelems > 1)
-           res->array.scalar = false;
-
-       r = compressHStore(res, VARDATA(out));
-       SET_VARSIZE(out, r + VARHDRSZ);
-   }
-
-   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);
-   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 || HS_ISEMPTY(hs))
-   {
-       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]);
-       HStoreValue *v = NULL;
-
-       if (key_nulls[i] == false)
-           v = findUncompressedHStoreValue(VARDATA(hs),
-                                           HS_FLAG_HASH | HS_FLAG_ARRAY,
-                                           NULL,
-                                           VARDATA(key),
-                                           VARSIZE(key) - VARHDRSZ);
-
-       out_datums[i] = PointerGetDatum(HStoreValueToText(v));
-       out_nulls[i] = (DatumGetPointer(out_datums[i]) == NULL) ? true : 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);
-   HStoreValue    *a = arrayToHStoreSortedArray(PG_GETARG_ARRAYTYPE_P(1));
-   uint32          lowbound = 0,
-                  *plowbound;
-   HStoreValue     *res = NULL;
-   ToHStoreState   *state = NULL;
-   text            *out;
-   uint32          i;
-
-   out = palloc(VARSIZE(hs));
-
-   if (a == NULL || a->array.nelems == 0 || HS_ISEMPTY(hs))
-   {
-       memcpy(out, hs, VARSIZE(hs));
-       PG_RETURN_POINTER(out);
-   }
-
-   if (HS_ROOT_IS_HASH(hs))
-   {
-       plowbound = &lowbound;
-       pushHStoreValue(&state, WHS_BEGIN_HASH, NULL);
-   }
-   else
-   {
-       plowbound = NULL;
-       pushHStoreValue(&state, WHS_BEGIN_ARRAY, NULL);
-   }
-
-   for (i = 0; i < a->array.nelems; ++i)
-   {
-       HStoreValue *v = findUncompressedHStoreValueByValue(VARDATA(hs),
-                                                           HS_FLAG_HASH | HS_FLAG_ARRAY,
-                                                           plowbound,
-                                                           a->array.elems + i);
-
-       if (v)
-       {
-           if (plowbound)
-           {
-               pushHStoreValue(&state, WHS_KEY, a->array.elems + i);
-               pushHStoreValue(&state, WHS_VALUE, v);
-           }
-           else
-           {
-               pushHStoreValue(&state, WHS_ELEM, v);
-           }
-       }
-   }
-
-   if (plowbound)
-       res = pushHStoreValue(&state, WHS_END_HASH, a /* any non-null value */);
-   else
-       res = pushHStoreValue(&state, WHS_END_ARRAY, NULL);
-
-
-   if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
-                       (res->type == hsvHash && res->hash.npairs == 0) )
-   {
-       SET_VARSIZE(out, VARHDRSZ);
-   }
-   else
-   {
-       int r = compressHStore(res, VARDATA(out));
-       SET_VARSIZE(out, r + VARHDRSZ);
-   }
-
-   PG_RETURN_POINTER(out);
-}
-
-static HStoreValue*
-replacePathDo(HStoreIterator **it, Datum *path_elems,
-             bool *path_nulls, int path_len,
-             ToHStoreState  **st, int level, HStoreValue *newval)
-{
-   HStoreValue v, *res = NULL;
-   int         r;
-
-   r = HStoreIteratorGet(it, &v, false);
-
-   if (r == WHS_BEGIN_ARRAY)
-   {
-       int     idx, i;
-       uint32  n = v.array.nelems;
-
-       idx = n;
-       if (level >= path_len || path_nulls[level] ||
-           h_atoi(VARDATA_ANY(path_elems[level]),
-                  VARSIZE_ANY_EXHDR(path_elems[level]), &idx) == false)
-       {
-           idx = n;
-       }
-       else if (idx < 0)
-       {
-           if (-idx > n)
-               idx = n;
-           else
-               idx = n + idx;
-       }
-
-       if (idx > n)
-           idx = n;
-
-       pushHStoreValue(st, r, &v);
-
-       for(i=0; i<n; i++)
-       {
-           if (i == idx && level < path_len)
-           {
-               if (level == path_len - 1)
-               {
-                   r = HStoreIteratorGet(it, &v, true); /* skip */
-                   Assert(r == WHS_ELEM);
-                   res = pushHStoreValue(st, r, newval);
-               }
-               else
-               {
-                   res = replacePathDo(it, path_elems, path_nulls, path_len,
-                                       st, level + 1, newval);
-               }
-           }
-           else
-           {
-               r = HStoreIteratorGet(it, &v, true);
-               Assert(r == WHS_ELEM);
-               res = pushHStoreValue(st, r, &v);
-           }
-       }
-
-       r = HStoreIteratorGet(it, &v, true);
-       Assert(r == WHS_END_ARRAY);
-       res = pushHStoreValue(st, r, &v);
-   }
-   else if (r == WHS_BEGIN_HASH)
-   {
-       int         i;
-       uint32      n = v.hash.npairs;
-       HStoreValue k;
-       bool        done = false;
-
-       pushHStoreValue(st, WHS_BEGIN_HASH, &v);
-
-       if (level >= path_len || path_nulls[level])
-           done = true;
-
-       for(i=0; i<n; i++)
-       {
-           r = HStoreIteratorGet(it, &k, false);
-           Assert(r == WHS_KEY);
-           res = pushHStoreValue(st, r, &k);
-
-           if (done == false &&
-               k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
-               memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
-                      k.string.len) == 0)
-           {
-               if (level == path_len - 1)
-               {
-                   r = HStoreIteratorGet(it, &v, true); /* skip */
-                   Assert(r == WHS_VALUE);
-                   res = pushHStoreValue(st, r, newval);
-               }
-               else
-               {
-                   res = replacePathDo(it, path_elems, path_nulls, path_len,
-                                       st, level + 1, newval);
-               }
-           }
-           else
-           {
-               r = HStoreIteratorGet(it, &v, true);
-               Assert(r == WHS_VALUE);
-               res = pushHStoreValue(st, r, &v);
-           }
-       }
-
-       r = HStoreIteratorGet(it, &v, true);
-       Assert(r == WHS_END_HASH);
-       res = pushHStoreValue(st, r, &v);
-   }
-   else if (r == WHS_ELEM || r == WHS_VALUE)
-   {
-       pushHStoreValue(st, r, &v);
-       res = (void*)0x01; /* dummy value */
-   }
-   else
-   {
-       elog(ERROR, "impossible state");
-   }
-
-   return res;
-}
-
-PG_FUNCTION_INFO_V1(hstore_replace);
-Datum      hstore_replace(PG_FUNCTION_ARGS);
-Datum
-hstore_replace(PG_FUNCTION_ARGS)
+hstore_concat(PG_FUNCTION_ARGS)
 {
-   HStore          *in = PG_GETARG_HS(0);
-   ArrayType       *path = PG_GETARG_ARRAYTYPE_P(1);
-   HStore          *newval = PG_GETARG_HS(2);
-   HStore          *out = palloc(VARSIZE(in) + VARSIZE(newval));
-   HStoreValue     *res = NULL;
-   HStoreValue     value;
-   Datum           *path_elems;
-   bool            *path_nulls;
-   int             path_len;
-   HStoreIterator  *it;
-   ToHStoreState   *st = NULL;
-
-   Assert(ARR_ELEMTYPE(path) == TEXTOID);
-
-   if (ARR_NDIM(path) > 1)
-       ereport(ERROR,
-               (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-                errmsg("wrong number of array subscripts")));
-
-   if (HS_ROOT_COUNT(in) == 0)
-   {
-       memcpy(out, in, VARSIZE(in));
+   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 = HS_COUNT(s1);
+   int         s2count = HS_COUNT(s2);
+   int         outcount = 0;
+
+   SET_VARSIZE(out, VARSIZE(s1) + VARSIZE(s2) - HSHRDSIZE);
+   HS_SETCOUNT(out, s1count + s2count);
+
+   if (s1count == 0)
+   {
+       /* return a copy of the input, unchanged */
+       memcpy(out, s2, VARSIZE(s2));
+       HS_FIXSIZE(out, s2count);
+       HS_SETCOUNT(out, s2count);
        PG_RETURN_POINTER(out);
    }
 
-   deconstruct_array(path, TEXTOID, -1, false, 'i',
-                     &path_elems, &path_nulls, &path_len);
-
-   if (path_len == 0)
+   if (s2count == 0)
    {
-       memcpy(out, in, VARSIZE(in));
+       /* return a copy of the input, unchanged */
+       memcpy(out, s1, VARSIZE(s1));
+       HS_FIXSIZE(out, s1count);
+       HS_SETCOUNT(out, s1count);
        PG_RETURN_POINTER(out);
    }
 
-   if (HS_ROOT_COUNT(newval) == 0)
-   {
-       value.type = hsvNull;
-       value.size = sizeof(HEntry);
-   }
-   else
+   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)
    {
-       value.type = hsvBinary;
-       value.binary.data = VARDATA(newval);
-       value.binary.len = VARSIZE_ANY_EXHDR(newval);
-       value.size = value.binary.len + sizeof(HEntry);
-   }
+       int         difference;
 
-   it = HStoreIteratorInit(VARDATA(in));
+       if (s1idx >= s1count)
+           difference = 1;
+       else if (s2idx >= s2count)
+           difference = -1;
+       else
+       {
+           int         s1keylen = HS_KEYLEN(es1, s1idx);
+           int         s2keylen = HS_KEYLEN(es2, s2idx);
 
-   res = replacePathDo(&it, path_elems, path_nulls, path_len, &st, 0, &value);
+           if (s1keylen == s2keylen)
+               difference = memcmp(HS_KEY(es1, ps1, s1idx),
+                                   HS_KEY(es2, ps2, s2idx),
+                                   s1keylen);
+           else
+               difference = (s1keylen > s2keylen) ? 1 : -1;
+       }
 
-   if (res == NULL)
-   {
-       SET_VARSIZE(out, VARHDRSZ);
+       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;
+       }
    }
-   else
-   {
-       int             sz;
 
-       sz = compressHStore(res, VARDATA(out));
-       SET_VARSIZE(out, sz + VARHDRSZ);
-   }
+   HS_FINALIZE(out, outcount, bufd, pd);
 
    PG_RETURN_POINTER(out);
 }
 
-static HStoreValue*
-concatPathDo(HStoreIterator **it, Datum *path_elems,
-            bool *path_nulls, int path_len,
-            ToHStoreState  **st, int level, HStoreIterator *toConcat)
+
+PG_FUNCTION_INFO_V1(hstore_slice_to_array);
+Datum      hstore_slice_to_array(PG_FUNCTION_ARGS);
+Datum
+hstore_slice_to_array(PG_FUNCTION_ARGS)
 {
-   HStoreValue v, *res = NULL;
-   int         r;
+   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;
 
-   r = HStoreIteratorGet(it, &v, false);
+   deconstruct_array(key_array,
+                     TEXTOID, -1, false, 'i',
+                     &key_datums, &key_nulls, &key_count);
 
-   if (r == WHS_BEGIN_ARRAY)
+   if (key_count == 0)
    {
-       int     idx, i;
-       uint32  n = v.array.nelems;
+       aout = construct_empty_array(TEXTOID);
+       PG_RETURN_POINTER(aout);
+   }
 
-       idx = n;
-       if (level >= path_len || path_nulls[level] ||
-           h_atoi(VARDATA_ANY(path_elems[level]),
-                  VARSIZE_ANY_EXHDR(path_elems[level]), &idx) == false)
-       {
-           idx = n;
-       }
-       else if (idx < 0)
-       {
-           if (-idx > n)
-               idx = n;
-           else
-               idx = n + idx;
-       }
+   out_datums = palloc(sizeof(Datum) * key_count);
+   out_nulls = palloc(sizeof(bool) * key_count);
 
-       if (idx > n)
-           idx = n;
+   for (i = 0; i < key_count; ++i)
+   {
+       text       *key = (text *) DatumGetPointer(key_datums[i]);
+       int         idx;
 
-       pushHStoreValue(st, r, &v);
+       if (key_nulls[i])
+           idx = -1;
+       else
+           idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ);
 
-       for(i=0; i<n; i++)
+       if (idx < 0 || HS_VALISNULL(entries, idx))
        {
-           if (i == idx && level < path_len)
-           {
-               if (level == path_len - 1)
-                   res = IteratorConcat(it, &toConcat, st);
-               else
-                   res = concatPathDo(it, path_elems, path_nulls, path_len,
-                                      st, level + 1, toConcat);
-           }
-           else
-           {
-               r = HStoreIteratorGet(it, &v, true);
-               Assert(r == WHS_ELEM);
-               res = pushHStoreValue(st, r, &v);
-           }
+           out_nulls[i] = true;
+           out_datums[i] = (Datum) 0;
        }
-
-       r = HStoreIteratorGet(it, &v, true);
-       Assert(r == WHS_END_ARRAY);
-       res = pushHStoreValue(st, r, &v);
-   }
-   else if (r == WHS_BEGIN_HASH)
-   {
-       int         i;
-       uint32      n = v.hash.npairs;
-       HStoreValue k;
-       bool        done = false;
-
-       pushHStoreValue(st, WHS_BEGIN_HASH, &v);
-
-       if (level >= path_len || path_nulls[level])
-           done = true;
-
-       for(i=0; i<n; i++)
+       else
        {
-           r = HStoreIteratorGet(it, &k, false);
-           Assert(r == WHS_KEY);
-           res = pushHStoreValue(st, r, &k);
-
-           if (done == false && level < path_len &&
-               k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
-               memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
-                      k.string.len) == 0)
-           {
-               if (level == path_len - 1)
-                   res = IteratorConcat(it, &toConcat, st);
-               else
-                   res = concatPathDo(it, path_elems, path_nulls, path_len,
-                                      st, level + 1, toConcat);
-           }
-           else
-           {
-               r = HStoreIteratorGet(it, &v, true);
-               Assert(r == WHS_VALUE);
-               res = pushHStoreValue(st, r, &v);
-           }
+           out_datums[i] = PointerGetDatum(
+                         cstring_to_text_with_len(HS_VAL(entries, ptr, idx),
+                                                  HS_VALLEN(entries, idx)));
+           out_nulls[i] = false;
        }
-
-       r = HStoreIteratorGet(it, &v, true);
-       Assert(r == WHS_END_HASH);
-       res = pushHStoreValue(st, r, &v);
-   }
-   else if (r == WHS_ELEM || r == WHS_VALUE)
-   {
-       pushHStoreValue(st, r, &v);
-       res = (void*)0x01; /* dummy value */
-   }
-   else
-   {
-       elog(ERROR, "impossible state");
    }
 
-   return res;
+   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_deep_concat);
-Datum      hstore_deep_concat(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(hstore_slice_to_hstore);
+Datum      hstore_slice_to_hstore(PG_FUNCTION_ARGS);
 Datum
-hstore_deep_concat(PG_FUNCTION_ARGS)
+hstore_slice_to_hstore(PG_FUNCTION_ARGS)
 {
-   HStore          *in = PG_GETARG_HS(0);
-   ArrayType       *path = PG_GETARG_ARRAYTYPE_P(1);
-   HStore          *newval = PG_GETARG_HS(2);
-   HStore          *out = palloc(VARSIZE(in) + VARSIZE(newval));
-   HStoreValue     *res = NULL;
-   Datum           *path_elems;
-   bool            *path_nulls;
-   int             path_len;
-   HStoreIterator  *it1, *it2;
-   ToHStoreState   *st = NULL;
-
-   Assert(ARR_ELEMTYPE(path) == TEXTOID);
-
-   if (ARR_NDIM(path) > 1)
-       ereport(ERROR,
-               (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-                errmsg("wrong number of array subscripts")));
+   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 (HS_ROOT_COUNT(in) == 0 || HS_ROOT_COUNT(newval) == 0)
+   if (nkeys == 0)
    {
-       memcpy(out, in, VARSIZE(in));
+       out = hstorePairs(NULL, 0, 0);
        PG_RETURN_POINTER(out);
    }
 
-   deconstruct_array(path, TEXTOID, -1, false, 'i',
-                     &path_elems, &path_nulls, &path_len);
+   /* hstoreArrayToPairs() checked overflow */
+   out_pairs = palloc(sizeof(Pairs) * nkeys);
+   bufsiz = 0;
 
-   it1 = HStoreIteratorInit(VARDATA(in));
-   it2 = HStoreIteratorInit(VARDATA(newval));
-
-   if (path_len == 0)
-       res = IteratorConcat(&it1, &it2, &st);
-   else
-       res = concatPathDo(&it1, path_elems, path_nulls, path_len, &st, 0, it2);
+   /*
+    * 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.
+    */
 
-   if (res == NULL)
+   for (i = 0; i < nkeys; ++i)
    {
-       SET_VARSIZE(out, VARHDRSZ);
+       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;
+       }
    }
-   else
-   {
-       int             sz;
 
-       if (res->type == hsvArray && res->array.nelems > 1)
-           res->array.scalar = false;
+   /*
+    * we don't use uniquePairs here because we know that the pairs list is
+    * already sorted and uniq'ed.
+    */
 
-       sz = compressHStore(res, VARDATA(out));
-       SET_VARSIZE(out, sz + VARHDRSZ);
-   }
+   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;
-   int             i = 0, r = 0;
-   HStoreIterator  *it;
-   HStoreValue     v;
-   bool            skipNested = false;
-
-   if (HS_ISEMPTY(hs))
+   HStore     *hs = PG_GETARG_HS(0);
+   Datum      *d;
+   ArrayType  *a;
+   HEntry     *entries = ARRPTR(hs);
+   char       *base = STRPTR(hs);
+   int         count = HS_COUNT(hs);
+   int         i;
+
+   if (count == 0)
    {
        a = construct_empty_array(TEXTOID);
        PG_RETURN_POINTER(a);
    }
 
-   d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
-
-   it = HStoreIteratorInit(VARDATA(hs));
+   d = (Datum *) palloc(sizeof(Datum) * count);
 
-   while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+   for (i = 0; i < count; ++i)
    {
-       skipNested = true;
+       text       *item = cstring_to_text_with_len(HS_KEY(entries, base, i),
+                                                   HS_KEYLEN(entries, i));
 
-       if ((r == WHS_ELEM && v.type != hsvNull) || r == WHS_KEY)
-           d[i++] = PointerGetDatum(HStoreValueToText(&v));
+       d[i] = PointerGetDatum(item);
    }
 
-   a = construct_array(d, i,
+   a = construct_array(d, count,
                        TEXTOID, -1, false, 'i');
 
    PG_RETURN_POINTER(a);
@@ -2058,40 +742,43 @@ Datum        hstore_avals(PG_FUNCTION_ARGS);
 Datum
 hstore_avals(PG_FUNCTION_ARGS)
 {
-   HStore          *hs = PG_GETARG_HS(0);
-   Datum           *d;
-   ArrayType       *a;
-   int             i = 0, r = 0;
-   HStoreIterator  *it;
-   HStoreValue     v;
-   bool            skipNested = false;
-   bool           *nulls;
-   int             lb = 1;
-
-   if (HS_ISEMPTY(hs))
+   HStore     *hs = PG_GETARG_HS(0);
+   Datum      *d;
+   bool       *nulls;
+   ArrayType  *a;
+   HEntry     *entries = ARRPTR(hs);
+   char       *base = STRPTR(hs);
+   int         count = HS_COUNT(hs);
+   int         lb = 1;
+   int         i;
+
+   if (count == 0)
    {
        a = construct_empty_array(TEXTOID);
        PG_RETURN_POINTER(a);
    }
 
-   d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
-   nulls = (bool *) palloc(sizeof(bool) * HS_ROOT_COUNT(hs));
-
-   it = HStoreIteratorInit(VARDATA(hs));
+   d = (Datum *) palloc(sizeof(Datum) * count);
+   nulls = (bool *) palloc(sizeof(bool) * count);
 
-   while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+   for (i = 0; i < count; ++i)
    {
-       skipNested = true;
-
-       if (r == WHS_ELEM || r == WHS_VALUE)
+       if (HS_VALISNULL(entries, i))
+       {
+           d[i] = (Datum) 0;
+           nulls[i] = true;
+       }
+       else
        {
-           d[i] = PointerGetDatum(HStoreValueToText(&v));
-           nulls[i] = (DatumGetPointer(d[i]) == NULL) ? true : false;
-           i++;
+           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, &i, &lb,
+   a = construct_md_array(d, nulls, 1, &count, &lb,
                           TEXTOID, -1, false, 'i');
 
    PG_RETURN_POINTER(a);
@@ -2101,53 +788,44 @@ hstore_avals(PG_FUNCTION_ARGS)
 static ArrayType *
 hstore_to_array_internal(HStore *hs, int ndims)
 {
-   int             count = HS_ROOT_COUNT(hs);
-   int             out_size[2] = {0, 2};
-   int             lb[2] = {1, 1};
-   Datum          *out_datums;
-   bool            *out_nulls;
-   bool            isHash = HS_ROOT_IS_HASH(hs) ? true : false;
-   int             i = 0, r = 0;
-   HStoreIterator  *it;
-   HStoreValue     v;
-   bool            skipNested = false;
+   HEntry     *entries = ARRPTR(hs);
+   char       *base = STRPTR(hs);
+   int         count = HS_COUNT(hs);
+   int         out_size[2] = {0, 2};
+   int         lb[2] = {1, 1};
+   Datum      *out_datums;
+   bool       *out_nulls;
+   int         i;
 
    Assert(ndims < 3);
 
    if (count == 0 || ndims == 0)
        return construct_empty_array(TEXTOID);
 
-   if (isHash == false && ndims == 2 && count % 2 != 0)
-       elog(ERROR, "hstore's array should have even number of elements");
-
-   out_size[0] = count * (isHash ? 2 : 1) / ndims;
+   out_size[0] = count * 2 / ndims;
    out_datums = palloc(sizeof(Datum) * count * 2);
    out_nulls = palloc(sizeof(bool) * count * 2);
 
-   it = HStoreIteratorInit(VARDATA(hs));
-
-   while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+   for (i = 0; i < count; ++i)
    {
-       skipNested = true;
+       text       *key = cstring_to_text_with_len(HS_KEY(entries, base, i),
+                                                  HS_KEYLEN(entries, i));
 
-       switch(r)
+       out_datums[i * 2] = PointerGetDatum(key);
+       out_nulls[i * 2] = false;
+
+       if (HS_VALISNULL(entries, i))
+       {
+           out_datums[i * 2 + 1] = (Datum) 0;
+           out_nulls[i * 2 + 1] = true;
+       }
+       else
        {
-           case WHS_ELEM:
-               out_datums[i] = PointerGetDatum(HStoreValueToText(&v));
-               out_nulls[i] = (DatumGetPointer(out_datums[i]) == NULL) ? true : false;
-               i++;
-               break;
-           case WHS_KEY:
-               out_datums[i * 2] = PointerGetDatum(HStoreValueToText(&v));
-               out_nulls[i * 2] = (DatumGetPointer(out_datums[i * 2]) == NULL) ? true : false;
-               break;
-           case WHS_VALUE:
-               out_datums[i * 2 + 1] = PointerGetDatum(HStoreValueToText(&v));
-               out_nulls[i * 2 + 1] = (DatumGetPointer(out_datums[i * 2 + 1]) == NULL) ? true : false;
-               i++;
-               break;
-           default:
-               break;
+           text       *item = cstring_to_text_with_len(HS_VAL(entries, base, i),
+                                                     HS_VALLEN(entries, i));
+
+           out_datums[i * 2 + 1] = PointerGetDatum(item);
+           out_nulls[i * 2 + 1] = false;
        }
    }
 
@@ -2184,529 +862,225 @@ hstore_to_matrix(PG_FUNCTION_ARGS)
  * 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)
- */
-
-typedef struct SetReturningState
-{
-   HStore          *hs;
-   HStoreIterator  *it;
-   MemoryContext   ctx;
-
-   HStoreValue     init;
-   int             path_len;
-   int             level;
-   struct {
-       HStoreValue     v;
-       Datum           varStr;
-       int             varInt;
-       enum {
-           pathStr,
-           pathInt,
-           pathAny
-       }               varKind;
-       int             i;
-   }               *path;
-} SetReturningState;
-
-static SetReturningState*
-setup_firstcall(FuncCallContext *funcctx, HStore *hs, ArrayType *path,
-               FunctionCallInfoData *fcinfo)
-{
-   MemoryContext           oldcontext;
-   SetReturningState      *st;
-
-   oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
-
-   st = palloc(sizeof(*st));
-
-   st->ctx = funcctx->multi_call_memory_ctx;
-
-   st->hs = (HStore *) palloc(VARSIZE(hs));
-   memcpy(st->hs, hs, VARSIZE(hs));
-   if (HS_ISEMPTY(hs) || path)
-       st->it = NULL;
-   else
-       st->it = HStoreIteratorInit(VARDATA(st->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);
-   }
-
-   st->path_len = st->level = 0;
-   if (path)
-   {
-       Datum       *path_elems;
-       bool        *path_nulls;
-       int         i;
-
-       Assert(ARR_ELEMTYPE(path) == TEXTOID);
-       if (ARR_NDIM(path) > 1)
-           ereport(ERROR,
-                   (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-                    errmsg("wrong number of array subscripts")));
-
-       deconstruct_array(path, TEXTOID, -1, false, 'i',
-                         &path_elems, &path_nulls, &st->path_len);
-
-       st->init.type = hsvBinary;
-       st->init.size = VARSIZE(st->hs);
-       st->init.binary.data = VARDATA(st->hs);
-       st->init.binary.len = VARSIZE_ANY_EXHDR(st->hs);
-
-       if (st->path_len > 0)
-       {
-           st->path = palloc(sizeof(*st->path) * st->path_len);
-           st->path[0].v = st->init;
-       }
-
-       for(i=0; i<st->path_len; i++)
-       {
-           st->path[i].varStr = path_elems[i];
-           st->path[i].i = 0;
-
-           if (path_nulls[i])
-               st->path[i].varKind = pathAny;
-           else if (h_atoi(VARDATA_ANY(path_elems[i]),
-                           VARSIZE_ANY_EXHDR(path_elems[i]),
-                           &st->path[i].varInt))
-               st->path[i].varKind = pathInt;
-           else
-               st->path[i].varKind = pathStr;
-       }
-   }
-
-   MemoryContextSwitchTo(oldcontext);
-
-   return st;
-}
-
-static uint32
-HStoreIteratorGetCtx(SetReturningState *st, HStoreValue *v, bool skipNested)
-{
-   int             r;
-   MemoryContext   oldctx;
-
-   oldctx = MemoryContextSwitchTo(st->ctx);
-   r = HStoreIteratorGet(&st->it, v, skipNested);
-   MemoryContextSwitchTo(oldctx);
-
-   return r;
-}
-
-PG_FUNCTION_INFO_V1(hstore_skeys);
-Datum      hstore_skeys(PG_FUNCTION_ARGS);
-Datum
-hstore_skeys(PG_FUNCTION_ARGS)
-{
-   FuncCallContext     *funcctx;
-   SetReturningState   *st;
-   int                 r;
-   HStoreValue         v;
-
-   if (SRF_IS_FIRSTCALL())
-   {
-       funcctx = SRF_FIRSTCALL_INIT();
-       st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL);
-   }
-
-   funcctx = SRF_PERCALL_SETUP();
-   st = (SetReturningState *) funcctx->user_fctx;
-
-   while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
-   {
-       if (r == WHS_KEY || r == WHS_ELEM)
-       {
-           text       *item = HStoreValueToText(&v);
-
-           if (item == NULL)
-               SRF_RETURN_NEXT_NULL(funcctx);
-           else
-               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;
-   SetReturningState   *st;
-   int                 r;
-   HStoreValue         v;
-
-   if (SRF_IS_FIRSTCALL())
-   {
-       funcctx = SRF_FIRSTCALL_INIT();
-       st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL);
-   }
-
-   funcctx = SRF_PERCALL_SETUP();
-   st = (SetReturningState *) funcctx->user_fctx;
-
-   while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
-   {
-       if (r == WHS_VALUE || r == WHS_ELEM)
-       {
-           text       *item = HStoreValueToText(&v);
-
-           if (item == NULL)
-               SRF_RETURN_NEXT_NULL(funcctx);
-           else
-               SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
-       }
-   }
-
-   SRF_RETURN_DONE(funcctx);
-}
-
-PG_FUNCTION_INFO_V1(hstore_hvals);
-Datum      hstore_hvals(PG_FUNCTION_ARGS);
-Datum
-hstore_hvals(PG_FUNCTION_ARGS)
-{
-   FuncCallContext     *funcctx;
-   SetReturningState   *st;
-   int                 r;
-   HStoreValue         v;
-
-   if (SRF_IS_FIRSTCALL())
-   {
-       funcctx = SRF_FIRSTCALL_INIT();
-       st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL);
-   }
-
-   funcctx = SRF_PERCALL_SETUP();
-   st = (SetReturningState *) funcctx->user_fctx;
-
-   while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
-   {
-       if (r == WHS_VALUE || r == WHS_ELEM)
-       {
-           HStore     *item = HStoreValueToHStore(&v);
-
-           if (item == NULL)
-               SRF_RETURN_NEXT_NULL(funcctx);
-           else
-               SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
-       }
-   }
-
-   SRF_RETURN_DONE(funcctx);
-}
-
-static HStoreValue*
-getNextValsPath(SetReturningState *st)
-{
-   HStoreValue         *v = NULL;
-
-   if (st->path_len == 0)
-   {
-       /* empty path */
-       if (st->level == 0)
-       {
-           v = &st->init;
-           st->level ++;
-       }
-
-       return v;
-   }
+ * there was no explanatory comment in the original code. --AG)
+ */
 
-   while(st->level >= 0)
-   {
-       uint32  header;
+static void
+setup_firstcall(FuncCallContext *funcctx, HStore *hs,
+               FunctionCallInfoData *fcinfo)
+{
+   MemoryContext oldcontext;
+   HStore     *st;
 
-       v = NULL;
-       if (st->path[st->level].v.type != hsvBinary)
-       {
-           st->level--;
-           continue;
-       }
+   oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
-       header = *(uint32*)st->path[st->level].v.binary.data;
+   st = (HStore *) palloc(VARSIZE(hs));
+   memcpy(st, hs, VARSIZE(hs));
 
-       if (header & HS_FLAG_HASH)
-       {
-           if (st->path[st->level].varKind == pathAny)
-           {
-               v = getHStoreValue(st->path[st->level].v.binary.data,
-                                  HS_FLAG_HASH,
-                                  st->path[st->level].i++);
-           }
-           else
-           {
-               v = findUncompressedHStoreValue(st->path[st->level].v.binary.data,
-                                               HS_FLAG_HASH, NULL,
-                                               VARDATA_ANY(st->path[st->level].varStr),
-                                               VARSIZE_ANY_EXHDR(st->path[st->level].varStr));
-           }
-       }
-       else if (header & HS_FLAG_ARRAY)
-       {
-           if (st->path[st->level].varKind == pathAny)
-           {
-               v = getHStoreValue(st->path[st->level].v.binary.data,
-                                  HS_FLAG_ARRAY, st->path[st->level].i++);
-           }
-           else if (st->path[st->level].varKind == pathInt)
-           {
-               int ith = st->path[st->level].varInt;
+   funcctx->user_fctx = (void *) st;
 
-               if (ith < 0)
-               {
-                   if (-ith > (int)(header & HS_COUNT_MASK))
-                   {
-                       st->level--;
-                       continue;
-                   }
-                   else
-                   {
-                       ith = ((int)(header & HS_COUNT_MASK)) + ith;
-                   }
-               }
-               else
-               {
-                   if (ith >= (int)(header & HS_COUNT_MASK))
-                   {
-                       st->level--;
-                       continue;
-                   }
-               }
+   if (fcinfo)
+   {
+       TupleDesc   tupdesc;
 
-               v = getHStoreValue(st->path[st->level].v.binary.data,
-                                  HS_FLAG_ARRAY, ith);
-           }
-           else
-           {
-               st->level--;
-               continue;
-           }
-       }
-       else
-       {
-           elog(ERROR, "wrong hstore container type");
-       }
+       /* 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");
 
-       if (v == NULL)
-       {
-           st->level--;
-       }
-       else if (st->level == st->path_len - 1)
-       {
-           if (st->path[st->level].varKind != pathAny)
-           {
-               st->path[st->level].v.type = hsvNull;
-               st->level--;
-           }
-           break;
-       }
-       else
-       {
-           if (st->path[st->level].varKind != pathAny)
-               st->path[st->level].v.type = hsvNull;
-           st->level++;
-           st->path[st->level].v = *v;
-           st->path[st->level].i = 0;
-       }
+       funcctx->tuple_desc = BlessTupleDesc(tupdesc);
    }
 
-   return v;
+   MemoryContextSwitchTo(oldcontext);
 }
 
-PG_FUNCTION_INFO_V1(hstore_svals_path);
-Datum      hstore_svals_path(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(hstore_skeys);
+Datum      hstore_skeys(PG_FUNCTION_ARGS);
 Datum
-hstore_svals_path(PG_FUNCTION_ARGS)
+hstore_skeys(PG_FUNCTION_ARGS)
 {
-   FuncCallContext     *funcctx;
-   SetReturningState   *st;
-   HStoreValue         *v;
+   FuncCallContext *funcctx;
+   HStore     *hs;
+   int         i;
 
    if (SRF_IS_FIRSTCALL())
    {
+       hs = PG_GETARG_HS(0);
        funcctx = SRF_FIRSTCALL_INIT();
-       st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL);
+       setup_firstcall(funcctx, hs, NULL);
    }
 
    funcctx = SRF_PERCALL_SETUP();
-   st = (SetReturningState *) funcctx->user_fctx;
+   hs = (HStore *) funcctx->user_fctx;
+   i = funcctx->call_cntr;
 
-   if ((v = getNextValsPath(st)) != NULL)
+   if (i < HS_COUNT(hs))
    {
-       text    *item = HStoreValueToText(v);
+       HEntry     *entries = ARRPTR(hs);
+       text       *item;
 
-       if (item == NULL)
-           SRF_RETURN_NEXT_NULL(funcctx);
-       else
-           SRF_RETURN_NEXT(funcctx, PointerGetDatum(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_hvals_path);
-Datum      hstore_hvals_path(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(hstore_svals);
+Datum      hstore_svals(PG_FUNCTION_ARGS);
 Datum
-hstore_hvals_path(PG_FUNCTION_ARGS)
+hstore_svals(PG_FUNCTION_ARGS)
 {
-   FuncCallContext     *funcctx;
-   SetReturningState   *st;
-   HStoreValue         *v;
+   FuncCallContext *funcctx;
+   HStore     *hs;
+   int         i;
 
    if (SRF_IS_FIRSTCALL())
    {
+       hs = PG_GETARG_HS(0);
        funcctx = SRF_FIRSTCALL_INIT();
-       st = setup_firstcall(funcctx, PG_GETARG_HS(0),
-                            PG_GETARG_ARRAYTYPE_P(1), NULL);
+       setup_firstcall(funcctx, hs, NULL);
    }
 
    funcctx = SRF_PERCALL_SETUP();
-   st = (SetReturningState *) funcctx->user_fctx;
+   hs = (HStore *) funcctx->user_fctx;
+   i = funcctx->call_cntr;
 
-   if ((v = getNextValsPath(st)) != NULL)
+   if (i < HS_COUNT(hs))
    {
-       HStore     *item = HStoreValueToHStore(v);
+       HEntry     *entries = ARRPTR(hs);
+
+       if (HS_VALISNULL(entries, i))
+       {
+           ReturnSetInfo *rsi;
 
-       if (item == NULL)
-           SRF_RETURN_NEXT_NULL(funcctx);
+           /* ugly ugly ugly. why no macro for this? */
+           (funcctx)->call_cntr++;
+           rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+           rsi->isDone = ExprMultipleResult;
+           PG_RETURN_NULL();
+       }
        else
+       {
+           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_each);
-Datum      hstore_each(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(hstore_contains);
+Datum      hstore_contains(PG_FUNCTION_ARGS);
 Datum
-hstore_each(PG_FUNCTION_ARGS)
+hstore_contains(PG_FUNCTION_ARGS)
 {
-   FuncCallContext     *funcctx;
-   SetReturningState   *st;
-   int                 r;
-   HStoreValue         v;
-
-   if (SRF_IS_FIRSTCALL())
-   {
-       funcctx = SRF_FIRSTCALL_INIT();
-       st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
-   }
+   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 = HS_COUNT(tmpl);
+   int         lastidx = 0;
+   int         i;
 
-   funcctx = SRF_PERCALL_SETUP();
-   st = (SetReturningState *) funcctx->user_fctx;
+   /*
+    * 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
+    */
 
-   while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
+   for (i = 0; res && i < tcount; ++i)
    {
-       Datum       res,
-                   dvalues[2] = {0, 0};
-       bool        nulls[2] = {false, false};
-       text       *item;
-       HeapTuple   tuple;
+       int         idx = hstoreFindKey(val, &lastidx,
+                                     HS_KEY(te, tstr, i), HS_KEYLEN(te, i));
 
-       if (r == WHS_ELEM)
+       if (idx >= 0)
        {
-           nulls[0] = true;
+           bool        nullval = HS_VALISNULL(te, i);
+           int         vallen = HS_VALLEN(te, i);
 
-           item = HStoreValueToText(&v);
-           if (item == NULL)
-               nulls[1] = true;
-           else
-               dvalues[1] = PointerGetDatum(item);
-       }
-       else if (r == WHS_KEY)
-       {
-           item = HStoreValueToText(&v);
-           dvalues[0] = PointerGetDatum(item);
-
-           r = HStoreIteratorGetCtx(st, &v, true);
-           Assert(r == WHS_VALUE);
-           item = HStoreValueToText(&v);
-           if (item == NULL)
-               nulls[1] = true;
-           else
-               dvalues[1] = PointerGetDatum(item);
+           if (nullval != HS_VALISNULL(ve, idx)
+               || (!nullval
+                   && (vallen != HS_VALLEN(ve, idx)
+            || memcmp(HS_VAL(te, tstr, i), HS_VAL(ve, vstr, idx), vallen))))
+               res = false;
        }
        else
-       {
-           continue;
-       }
+           res = false;
+   }
 
-       tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
-       res = HeapTupleGetDatum(tuple);
+   PG_RETURN_BOOL(res);
+}
 
-       SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
-   }
 
-   SRF_RETURN_DONE(funcctx);
+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_hstore);
-Datum      hstore_each_hstore(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(hstore_each);
+Datum      hstore_each(PG_FUNCTION_ARGS);
 Datum
-hstore_each_hstore(PG_FUNCTION_ARGS)
+hstore_each(PG_FUNCTION_ARGS)
 {
-   FuncCallContext     *funcctx;
-   SetReturningState   *st;
-   int                 r;
-   HStoreValue         v;
+   FuncCallContext *funcctx;
+   HStore     *hs;
+   int         i;
 
    if (SRF_IS_FIRSTCALL())
    {
+       hs = PG_GETARG_HS(0);
        funcctx = SRF_FIRSTCALL_INIT();
-       st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
+       setup_firstcall(funcctx, hs, fcinfo);
    }
 
    funcctx = SRF_PERCALL_SETUP();
-   st = (SetReturningState *) funcctx->user_fctx;
+   hs = (HStore *) funcctx->user_fctx;
+   i = funcctx->call_cntr;
 
-   while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
+   if (i < HS_COUNT(hs))
    {
+       HEntry     *entries = ARRPTR(hs);
+       char       *ptr = STRPTR(hs);
        Datum       res,
-                   dvalues[2] = {0, 0};
+                   dvalues[2];
        bool        nulls[2] = {false, false};
        text       *item;
-       HStore      *hitem;
        HeapTuple   tuple;
 
-       if (r == WHS_ELEM)
-       {
-           nulls[0] = true;
+       item = cstring_to_text_with_len(HS_KEY(entries, ptr, i),
+                                       HS_KEYLEN(entries, i));
+       dvalues[0] = PointerGetDatum(item);
 
-           hitem = HStoreValueToHStore(&v);
-           if (hitem == NULL)
-               nulls[1] = true;
-           else
-               dvalues[1] = PointerGetDatum(hitem);
-       }
-       else if (r == WHS_KEY)
+       if (HS_VALISNULL(entries, i))
        {
-           item = HStoreValueToText(&v);
-           dvalues[0] = PointerGetDatum(item);
-
-           r = HStoreIteratorGetCtx(st, &v, true);
-           Assert(r == WHS_VALUE);
-           hitem = HStoreValueToHStore(&v);
-           if (hitem == NULL)
-               nulls[1] = true;
-           else
-               dvalues[1] = PointerGetDatum(hitem);
+           dvalues[1] = (Datum) 0;
+           nulls[1] = true;
        }
        else
        {
-           continue;
+           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);
@@ -2718,183 +1092,6 @@ hstore_each_hstore(PG_FUNCTION_ARGS)
    SRF_RETURN_DONE(funcctx);
 }
 
-static bool
-deepContains(HStoreIterator **it1, HStoreIterator **it2)
-{
-   uint32          r1, r2;
-   HStoreValue     v1, v2;
-   bool            res = true;
-
-   r1 = HStoreIteratorGet(it1, &v1, false);
-   r2 = HStoreIteratorGet(it2, &v2, false);
-
-   if (r1 != r2)
-   {
-       res = false;
-   }
-   else if (r1 == WHS_BEGIN_HASH)
-   {
-       uint32      lowbound = 0;
-       HStoreValue *v;
-
-       for(;;) {
-           r2 = HStoreIteratorGet(it2, &v2, false);
-           if (r2 == WHS_END_HASH)
-               break;
-
-           Assert(r2 == WHS_KEY);
-
-           v = findUncompressedHStoreValueByValue((*it1)->buffer,
-                                                  HS_FLAG_HASH,
-                                                  &lowbound, &v2);
-
-           if (v == NULL)
-           {
-               res = false;
-               break;
-           }
-
-           r2 = HStoreIteratorGet(it2, &v2, true);
-           Assert(r2 == WHS_VALUE);
-
-           if (v->type != v2.type)
-           {
-               res = false;
-               break;
-           }
-           else if (v->type == hsvString || v->type == hsvNull ||
-                    v->type == hsvBool || v->type == hsvNumeric)
-           {
-               if (compareHStoreValue(v, &v2) != 0)
-               {
-                   res = false;
-                   break;
-               }
-           }
-           else
-           {
-               HStoreIterator  *it1a, *it2a;
-
-               Assert(v2.type == hsvBinary);
-               Assert(v->type == hsvBinary);
-
-               it1a = HStoreIteratorInit(v->binary.data);
-               it2a = HStoreIteratorInit(v2.binary.data);
-
-               if ((res = deepContains(&it1a, &it2a)) == false)
-                   break;
-           }
-       }
-   }
-   else if (r1 == WHS_BEGIN_ARRAY)
-   {
-       HStoreValue     *v;
-       HStoreValue     *av = NULL;
-       uint32          nelems = v1.array.nelems;
-
-       for(;;) {
-           r2 = HStoreIteratorGet(it2, &v2, true);
-           if (r2 == WHS_END_ARRAY)
-               break;
-
-           Assert(r2 == WHS_ELEM);
-
-           if (v2.type == hsvString || v2.type == hsvNull ||
-               v2.type == hsvBool || v2.type == hsvNumeric)
-           {
-               v = findUncompressedHStoreValueByValue((*it1)->buffer,
-                                                      HS_FLAG_ARRAY, NULL,
-                                                      &v2);
-               if (v == NULL)
-               {
-                   res = false;
-                   break;
-               }
-           }
-           else
-           {
-               uint32          i;
-
-               if (av == NULL)
-               {
-                   uint32          j = 0;
-
-                   av = palloc(sizeof(*av) * nelems);
-
-                   for(i=0; i<nelems; i++)
-                   {
-                       r2 = HStoreIteratorGet(it1, &v1, true);
-                       Assert(r2 == WHS_ELEM);
-
-                       if (v1.type == hsvBinary)
-                           av[j++] = v1;
-                   }
-
-                   if (j == 0)
-                   {
-                       res = false;
-                       break;
-                   }
-
-                   nelems = j;
-               }
-
-               res = false;
-               for(i = 0; res == false && i<nelems; i++)
-               {
-                   HStoreIterator  *it1a, *it2a;
-
-                   it1a = HStoreIteratorInit(av[i].binary.data);
-                   it2a = HStoreIteratorInit(v2.binary.data);
-
-                   res = deepContains(&it1a, &it2a);
-               }
-
-               if (res == false)
-                   break;
-           }
-       }
-   }
-   else
-   {
-       elog(ERROR, "wrong hstore container type");
-   }
-
-   return res;
-}
-
-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;
-   HStoreIterator  *it1, *it2;
-
-   if (HS_ROOT_COUNT(val) < HS_ROOT_COUNT(tmpl) ||
-       HS_ROOT_IS_HASH(val) != HS_ROOT_IS_HASH(tmpl))
-       PG_RETURN_BOOL(false);
-
-   it1 = HStoreIteratorInit(VARDATA(val));
-   it2 = HStoreIteratorInit(VARDATA(tmpl));
-   res = deepContains(&it1, &it2);
-
-   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)
-                                       ));
-}
 
 /*
  * btree sort order for hstores isn't intended to be useful; we really only
@@ -2907,28 +1104,72 @@ 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             res;
+   HStore     *hs1 = PG_GETARG_HS(0);
+   HStore     *hs2 = PG_GETARG_HS(1);
+   int         hcount1 = HS_COUNT(hs1);
+   int         hcount2 = HS_COUNT(hs2);
+   int         res = 0;
 
-   if (HS_ISEMPTY(hs1) || HS_ISEMPTY(hs2))
+   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
    {
-       if (HS_ISEMPTY(hs1))
+       /* here we know both operands are nonempty */
+       char       *str1 = STRPTR(hs1);
+       char       *str2 = STRPTR(hs2);
+       HEntry     *ent1 = ARRPTR(hs1);
+       HEntry     *ent2 = ARRPTR(hs2);
+       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 (HS_ISEMPTY(hs2))
-               res = 0;
-           else
+           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 = 1;
+           res = (res > 0) ? 1 : -1;
        }
    }
-   else
-   {
-       res = compareHStoreBinaryValue(VARDATA(hs1), VARDATA(hs2));
-   }
 
    /*
     * this is a btree support function; this is one of the few places where
@@ -3022,60 +1263,17 @@ hstore_hash(PG_FUNCTION_ARGS)
    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) ==
+          (HS_COUNT(hs) != 0 ?
+           CALCDATASIZE(HS_COUNT(hs),
+                        HSE_ENDPOS(ARRPTR(hs)[2 * HS_COUNT(hs) - 1])) :
+           HSHRDSIZE));
+
    PG_FREE_IF_COPY(hs, 0);
    PG_RETURN_DATUM(hval);
 }
-
-PG_FUNCTION_INFO_V1(hstore_typeof);
-Datum      hstore_typeof(PG_FUNCTION_ARGS);
-Datum
-hstore_typeof(PG_FUNCTION_ARGS)
-{
-   HStore          *hs = PG_GETARG_HS(0);
-   HStoreIterator  *it;
-   HStoreValue     v;
-   uint32          r;
-
-   if (HS_ISEMPTY(hs))
-       PG_RETURN_NULL();
-
-   it = HStoreIteratorInit(VARDATA(hs));
-   r = HStoreIteratorGet(&it, &v, false);
-
-   switch(r)
-   {
-       case WHS_BEGIN_ARRAY:
-           if (v.array.scalar)
-           {
-               Assert(v.array.nelems == 1);
-               r = HStoreIteratorGet(&it, &v, false);
-               Assert(r == WHS_ELEM);
-
-               switch(v.type)
-               {
-                   case hsvNull:
-                       PG_RETURN_TEXT_P(cstring_to_text("null"));
-                   case hsvBool:
-                       PG_RETURN_TEXT_P(cstring_to_text("bool"));
-                   case hsvNumeric:
-                       PG_RETURN_TEXT_P(cstring_to_text("numeric"));
-                   case hsvString:
-                       PG_RETURN_TEXT_P(cstring_to_text("string"));
-                   default:
-                       elog(ERROR, "bogus hstore");
-               }
-           }
-           else
-           {
-               PG_RETURN_TEXT_P(cstring_to_text("array"));
-           }
-       case WHS_BEGIN_HASH:
-           PG_RETURN_TEXT_P(cstring_to_text("hash"));
-       case 0:
-           PG_RETURN_NULL();
-       default:
-           elog(ERROR, "bogus hstore");
-   }
-
-   PG_RETURN_NULL();
-}
diff --git a/contrib/hstore/hstore_scan.l b/contrib/hstore/hstore_scan.l
deleted file mode 100644 (file)
index 41df590..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * hstore_scan.l
- *    Lexer definition for hstore
- *
- * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
- *
- * contrib/hstore/hstore_scan.l
- *
- *-------------------------------------------------------------------------
- */
-
-%{
-static string scanstring;
-
-/* No reason to constrain amount of data slurped */
-/* #define YY_READ_BUF_SIZE 16777216 */
-
-/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-static char *scanbuf;
-static int scanbuflen;
-
-static void addstring(bool init, char *s, int l);
-static void addchar(bool init, char s);
-static int checkSpecialVal(void); /* examine scanstring for the special value */
-
-%}
-
-%option 8bit
-%option never-interactive
-%option nodefault
-%option noinput
-%option nounput
-%option noyywrap
-%option warn
-%option prefix="hstore_yy"
-%option bison-bridge
-
-%x xQUOTED
-%x xNONQUOTED
-
-any            [^\,\[\]\{\}\"\=\> \t\n\r\f\\\:]
-
-
-%%
-
-<INITIAL>[\,\{\}\[\]]          { return *yytext; }
-
-<INITIAL>\=\>                  {
-                                   if (inputJSON)
-                                       elog(ERROR, "syntax error");
-                                   return DELIMITER_P;
-                               }
-
-<INITIAL>\:                        {
-                                   if (inputJSON)
-                                   {
-                                       return DELIMITER_P;
-                                   }
-                                   else
-                                   {
-                                       addchar(true, ':');
-                                       BEGIN xNONQUOTED;
-                                   }
-                               }
-
-<INITIAL>[ \t\n\r\f]+          { /* ignore */ }
-
-<INITIAL>\=/[^\>]              {
-                                   addchar(true, '=');
-                                   BEGIN xNONQUOTED;
-                               }
-
-<INITIAL>\>                        {
-                                   addchar(true, yytext[0]);
-                                   BEGIN xNONQUOTED;
-                               }
-<INITIAL>\\.                   {
-                                   addchar(true, yytext[1]);
-                                   BEGIN xNONQUOTED;
-                               }
-
-<INITIAL>({any}|\>)+           {
-                                   addstring(true, yytext, yyleng);
-                                   BEGIN xNONQUOTED;
-                               }
-
-<INITIAL>\"                    {
-                                   addchar(true, '\0');
-                                   BEGIN xQUOTED;
-                               }
-
-<INITIAL>\=                        {   /* =<<EOF>> */
-                                   addchar(true, '=');
-                                   yylval->str = scanstring;
-                                   if (inputJSON)
-                                       elog(ERROR, "syntax error");
-                                   return STRING_P;
-                               }
-
-<xNONQUOTED>({any}|[\>\"\:])+  {
-                                   addstring(false, yytext, yyleng);
-                               }
-
-<xNONQUOTED>\=/[^\>]           { addchar(false, *yytext); }
-
-<xNONQUOTED>[ \t\n\r\f]+       {
-                                   yylval->str = scanstring;
-                                   BEGIN INITIAL;
-                                   return checkSpecialVal();
-                               }
-
-<xNONQUOTED>\=                 {   /* =<<EOF>> */
-                                   addchar(false, '=');
-                                   yylval->str = scanstring;
-                                   BEGIN INITIAL;
-                                   if (inputJSON)
-                                       elog(ERROR, "syntax error");
-                                   return STRING_P;
-                               }
-
-<xNONQUOTED>[\,\{\}\[\]]       {
-                                   yylval->str = scanstring;
-                                   yyless(0);
-                                   BEGIN INITIAL;
-                                   return checkSpecialVal();
-                               }
-
-<xNONQUOTED><<EOF>>                {
-                                   yylval->str = scanstring;
-                                   BEGIN INITIAL;
-                                   return checkSpecialVal();
-                               }
-
-<xNONQUOTED>\=\>               {
-                                   yylval->str = scanstring;
-                                   yyless(0);
-                                   BEGIN INITIAL;
-                                   return checkSpecialVal();
-                               }
-
-
-<xNONQUOTED,xQUOTED>\\.        { addchar(false, yytext[1]); }
-
-<INITIAL,xNONQUOTED,xQUOTED>\\     { yyerror("Unexpected end after backslesh"); }
-
-<xQUOTED><<EOF>>               { yyerror("Unexpected end of quoted string"); }
-
-<xQUOTED>\"                        {
-                                   yylval->str = scanstring;
-                                   BEGIN INITIAL;
-                                   return STRING_P;
-                               }
-
-<xQUOTED>[^\\\"]+              { addstring(false, yytext, yyleng); }
-
-<INITIAL><<EOF>>               { yyterminate(); }
-
-%%
-
-void
-yyerror(const char *message)
-{
-   if (*yytext == YY_END_OF_BUFFER_CHAR)
-   {
-       ereport(ERROR,
-               (errcode(ERRCODE_SYNTAX_ERROR),
-                errmsg("bad %s representation", inputJSON ? "json" : "hstore"),
-                /* translator: %s is typically "syntax error" */
-                errdetail("%s at end of input", message)));
-   }
-   else
-   {
-       ereport(ERROR,
-               (errcode(ERRCODE_SYNTAX_ERROR),
-                errmsg("bad %s representation", inputJSON ? "json" : "hstore"),
-                /* translator: first %s is typically "syntax error" */
-                errdetail("%s at or near \"%s\"", message, yytext)));
-   }
-}
-
-static int
-checkSpecialVal()
-{
-   int res = STRING_P;
-
-   if (stringIsNumber(scanstring.val, scanstring.len, inputJSON))
-   {
-       /* for numeric_in() call we need to make a correct C-string */
-       addchar(false, '\0');
-       res = NUMERIC_P;
-   }
-   else if (scanstring.len == 1)
-   {
-       if (*scanstring.val == 't')
-           res = TRUE_P;
-       else if (*scanstring.val == 'f')
-           res = FALSE_P;
-   }
-   else if (scanstring.len == 4)
-   {
-       if (pg_strncasecmp("null", scanstring.val, scanstring.len) == 0)
-           res = NULL_P;
-       else if (pg_strncasecmp("true", scanstring.val, scanstring.len) == 0)
-           res = TRUE_P;
-   }
-   else if (scanstring.len == 5)
-   {
-       if (pg_strncasecmp("false", scanstring.val, scanstring.len) == 0)
-           res = FALSE_P;
-   }
-
-   if (inputJSON && res == STRING_P)
-       elog(ERROR, "syntax error");
-
-   return res;
-}
-/*
- * Called before any actual parsing is done
- */
-static void
-hstore_scanner_init(const char *str, int slen)
-{
-   if (slen <= 0)
-       slen = strlen(str);
-
-   /*
-    * Might be left over after ereport()
-    */
-   if (YY_CURRENT_BUFFER)
-       yy_delete_buffer(YY_CURRENT_BUFFER);
-
-   /*
-    * Make a scan buffer with special termination needed by flex.
-    */
-
-   scanbuflen = slen;
-   scanbuf = palloc(slen + 2);
-   memcpy(scanbuf, str, slen);
-   scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
-   scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
-   BEGIN(INITIAL);
-}
-
-
-/*
- * Called after parsing is done to clean up after hstore_scanner_init()
- */
-static void
-hstore_scanner_finish(void)
-{
-   yy_delete_buffer(scanbufhandle);
-   pfree(scanbuf);
-}
-
-static void
-addstring(bool init, char *s, int l) {
-   if (init) {
-       scanstring.total = 32;
-       scanstring.val = palloc(scanstring.total);
-       scanstring.len = 0;
-   }
-
-   if (s && l) {
-       while(scanstring.len + l + 1 >= scanstring.total) {
-           scanstring.total *= 2;
-           scanstring.val = repalloc(scanstring.val, scanstring.total);
-       }
-
-       memcpy(scanstring.val+scanstring.len, s, l);
-       scanstring.len+=l;
-   }
-}
-
-static void
-addchar(bool init, char s) {
-   if (init)
-   {
-       scanstring.total = 32;
-       scanstring.val = palloc(scanstring.total);
-       scanstring.len = 0;
-   }
-   else if(scanstring.len + 1 >= scanstring.total)
-   {
-       scanstring.total*=2;
-       scanstring.val=repalloc(scanstring.val, scanstring.total);
-   }
-
-   scanstring.val[ scanstring.len ] = s;
-   if (s != '\0')
-       scanstring.len++;
-}
-
-HStoreValue*
-parseHStore(const char *str, int len, bool json) {
-   HStoreValue     *parseresult;
-
-   inputJSON = json;
-
-   hstore_scanner_init(str, len);
-
-   if (hstore_yyparse((void*)&parseresult) != 0)
-       hstore_yyerror("bugus input");
-
-   hstore_scanner_finish();
-
-   return parseresult;
-}
index a4ebfcdecf0e3b3bde3d562aff4820c6a517f467..fbe9543dfea1be82a21538147a1abaa658e16bfa 100644 (file)
  </indexterm>
 
  <para>
-  This module implements the <type>hstore</> data type for storing arbitrarily
-  nested key/value pairs and arrays within a single <productname>PostgreSQL</> value.
+  This module implements the <type>hstore</> data type for storing sets of
+  key/value pairs within a single <productname>PostgreSQL</> value.
   This can be useful in various scenarios, such as rows with many attributes
-  that are rarely examined, or semi-structured data. Keys are strings, while values
-  can be strings, numbers, booleans, or <literal>NULL</>.
- </para>
-
- <para>
-  The <type>hstore</> type is similar to the core <type>json</> data type, but,
-  in the current implementation, differs in a few key ways:
- </para>
-
- <itemizedlist>
-
-  <listitem>
-   <para>
-    It's faster. <type>hstore</> is stored in a binary representation, whereas
-    <type>json</> is stored as text, and so needs to be parsed every time it's
-    accessed.
-   </para>
-  </listitem>
-
-  <listitem>
-   <para>
-    Better index support. <type>hstore</> can be used in
-    <link linkend="GiST"><acronym>GiST</></link> and
-    <link linkend="GIN"><acronym>GIN</></link> indexes to allow searches
-    on keys or even key paths.
-   </para>
-  </listitem>
-
- </itemizedlist>
-
- <para>
-  That said, <type>hstore</> includes interfaces to transparently convert values
-  to and from <type>json</>. These allow the best of both worlds: store and
-  query <type>hstore</> values, but convert them to <type>json</> when fetching
-  them, for easy parsing in your client application code.
+  that are rarely examined, or semi-structured data.  Keys and values are
+  simply text strings.
  </para>
 
  <sect2>
   <title><type>hstore</> External Representation</title>
 
   <para>
-   The text representation of an <type>hstore</>, used for input and output,
-   may be formatted as scalar values, hash-like values, array-like values, and
-   nested array and hash values. Scalar values are simply strings, numeric
-   values, booleans, or <literal>NULL</>. Strings containing whitespace,
-   commas, <literal>=</>s or <literal>&gt;</>s must be double-quoted. To
-   include a double quote or a backslash in a key or value, escape it with a
-   backslash. Boolean values may be represented as <literal>true</>, <literal>t</>,
-   <literal>false</>, or <literal>f</>. Use quotation marks to represent these
-   values as strings. The <literal>NULL</> keyword is case-insensitive.
-   Double-quote the <literal>NULL</> to treat it as the ordinary string
-   <quote>NULL</quote>. Some examples:
-
-<programlisting>
-=% SELECT 'foo'::hstore, '"hi \"bob\""'::hstore, '1.0'::hstore, 'true'::hstore, NULL::hstore;
- hstore |    hstore    | hstore | hstore | hstore 
---------+--------------+--------+--------+--------
- "foo"  | "hi \"bob\"" | 1.0    | t      | 
-</programlisting>
-
-  </para>
-
-  <para>
-   Arrays of values of any supported type may be constructed as
-   square-bracketed comma-separated lists. Some examples:
-
-<programlisting>
-=% SELECT '[k,v]'::hstore, '[1.0, "hi there", false, null]'::hstore;
-   hstore   |           hstore           
-------------+----------------------------
- ["k", "v"] | [1.0, "hi there", f, NULL]
-</programlisting>
-
-  </para>
 
-  <para>
-   Hashes include zero or more
-   <replaceable>key</> <literal>=&gt;</> <replaceable>value</> pairs separated
-   by commas, optionally bracketed by curly braces. Keys must be strings and
-   may not be <literal>NULL</>; values may be any <type>hstore</> type,
-   including <literal>NULL</>. Examples:
+   The text representation of an <type>hstore</>, used for input and output,
+   includes zero or more <replaceable>key</> <literal>=&gt;</>
+   <replaceable>value</> pairs separated by commas. Some examples:
 
-<programlisting>
-=% SELECT 'k =&gt; v'::hstore
--%      , '{foo =&gt; "hi there"}'::hstore
--%      , '{one =&gt; 1, two =&gt; 2.0, three =&gt; true, four =&gt; null}'::hstore;
-  hstore  |      hstore       |                     hstore                     
-----------+-------------------+------------------------------------------------
- "k"=&gt;"v" | "foo"=&gt;"hi there" | "one"=&gt;1, "two"=&gt;2.0, "four"=&gt;NULL, "three"=&gt;t
-</programlisting>
+<synopsis>
+k =&gt; v
+foo =&gt; bar, baz =&gt; whatever
+"1-a" =&gt; "anything at all"
+</synopsis>
 
    The order of the pairs is not significant (and may not be reproduced on
-   output).
+   output). Whitespace between pairs or around the <literal>=&gt;</> sign is
+   ignored. Double-quote keys and values that include whitespace, commas,
+   <literal>=</>s or <literal>&gt;</>s. To include a double quote or a
+   backslash in a key or value, escape it with a backslash.
   </para>
 
   <para>
-   Each key in an <type>hstore</> hash is unique. If you declare an
-   <type>hstore</> hash with duplicate keys, only one will be stored in
-   the <type>hstore</> and there is no guarantee as to which will be kept:
+   Each key in an <type>hstore</> is unique. If you declare an <type>hstore</>
+   with duplicate keys, only one will be stored in the <type>hstore</> and
+   there is no guarantee as to which will be kept:
 
 <programlisting>
 SELECT 'a=&gt;1,a=&gt;2'::hstore;
   hstore
 ----------
- "a"=&gt;1
+ "a"=&gt;"1"
 </programlisting>
   </para>
 
   <para>
-   Hashes and arrays may be arbitrarily nested. In this case, brackets are
-   required for hash values. Here's an example adapted from the
-   <ulink url="http://geojson.org/geojson-spec.html">GeoJSON spec</ulink>:
+   A value (but not a key) can be an SQL <literal>NULL</>. For example:
 
 <programlisting>
-=% SET hstore.pretty_print=true;
-=% SELECT '{
-  "type" =&gt; "Feature",
-  "bbox" =&gt; [-180.0, -90.0, 180.0, 90.0],
-  "geometry" =&gt; {
-    "type" =&gt; "Polygon",
-    "coordinates" =&gt; [[
-      [-180.0, 10.0], [20.0, 90.0], [180.0, -5.0], [-30.0, -90.0]
-      ]]
-    }
-}'::hstore;
-          hstore          
---------------------------
- "bbox"=>                +
- [                       +
-     -180.0,             +
-     -90.0,              +
-     180.0,              +
-     90.0                +
- ],                      +
- "type"=>"Feature",      +
- "geometry"=>            +
- {                       +
-     "type"=>"Polygon",  +
-     "coordinates"=>     +
-     [                   +
-         [               +
-             [           +
-                 -180.0, +
-                 10.0    +
-             ],          +
-             [           +
-                 20.0,   +
-                 90.0    +
-             ],          +
-             [           +
-                 180.0,  +
-                 -5.0    +
-             ],          +
-             [           +
-                 -30.0,  +
-                 -90.0   +
-             ]           +
-         ]               +
-     ]                   +
- }
- </programlisting>
+key =&gt; NULL
+</programlisting>
+
+   The <literal>NULL</> keyword is case-insensitive. Double-quote the
+   <literal>NULL</> to treat it as the ordinary string <quote>NULL</quote>.
   </para>
 
   <note>
@@ -195,36 +82,6 @@ SELECT 'a=&gt;1,a=&gt;2'::hstore;
 
  </sect2>
 
- <sect2>
-  <title>Ouput Format Configuration Parameters</title>
-
-  <para>
-   There are several configuration parameters that control the output formatting of
-   <type>hstore</> values.
-  </para>
-
-  <variablelist>
-   <varlistentry>
-    <term>
-     <varname>hstore.pretty_print</varname> (<type>boolean</type>)
-    </term>
-    <indexterm>
-     <primary><varname>hstore.pretty_print</> configuration parameter</primary>
-    </indexterm>
-    <listitem>
-     <para>
-      By default, the text representation of <type>hstore</> values includes no
-      whitespace between the values it contains. Set <varname>hstore.pretty_print</varname>
-      to <literal>true</> to add newlines between values and to indent nested
-      hashes and arrays.
-     </para>
-    </listitem>
-   </varlistentry>
-
-  </variablelist>
-
- </sect2>
-
  <sect2>
   <title><type>hstore</> Operators and Functions</title>
 
@@ -241,7 +98,6 @@ SELECT 'a=&gt;1,a=&gt;2'::hstore;
     <thead>
      <row>
       <entry>Operator</entry>
-      <entry>Returns</entry>
       <entry>Description</entry>
       <entry>Example</entry>
       <entry>Result</entry>
@@ -251,111 +107,20 @@ SELECT 'a=&gt;1,a=&gt;2'::hstore;
     <tbody>
      <row>
       <entry><type>hstore</> <literal>-&gt;</> <type>text</></entry>
-      <entry><type>text</></entry>
       <entry>get value for key (<literal>NULL</> if not present)</entry>
       <entry><literal>'a=&gt;x, b=&gt;y'::hstore -&gt; 'a'</literal></entry>
       <entry><literal>x</literal></entry>
      </row>
 
-     <row>
-      <entry><type>hstore</> <literal>-&gt;</> <type>integer</></entry>
-      <entry><type>text</></entry>
-      <entry>get value for array index (<literal>NULL</> if not present)</entry>
-      <entry><literal>'[foo,bar,baz]'::hstore -&gt; 1</literal></entry>
-      <entry><literal>bar</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>^&gt;</> <type>text</></entry>
-      <entry><type>numeric</></entry>
-      <entry>get numeric value for key (<literal>NULL</> if not numeric or not present)</entry>
-      <entry><literal>'a=&gt;42.0, b=&gt;y'::hstore ^&gt; 'a'</literal></entry>
-      <entry><literal>42.0</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>^&gt;</> <type>integer</></entry>
-      <entry><type>numeric</></entry>
-      <entry>get numeric value for array index (<literal>NULL</> if not numeric or not present)</entry>
-      <entry><literal>'[foo,null,44]'::hstore ^&gt; 2</literal></entry>
-      <entry><literal>44</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>?&gt;</> <type>text</></entry>
-      <entry><type>boolean</></entry>
-      <entry>get boolean value for key (<literal>NULL</> if not boolean or not present)</entry>
-      <entry><literal>'a =&gt; 42.0, b =&gt; true'::hstore ?&gt; 'b'</literal></entry>
-      <entry><literal>true</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>?&gt;</> <type>integer</></entry>
-      <entry><type>boolean</></entry>
-      <entry>get boolean value for array index (<literal>NULL</> if not boolean or not present)</entry>
-      <entry><literal>'[false,null,44]'::hstore ?&gt; 0</literal></entry>
-      <entry><literal>false</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>#&gt;</> <type>text[]</></entry>
-      <entry><type>text</></entry>
-      <entry>get value for key path (<literal>NULL</> if not present)</entry>
-      <entry><literal>'foo =&gt; {bar =&gt; yellow}'::hstore #&gt; '{foo,bar}'</literal></entry>
-      <entry><literal>yellow</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>#^&gt;</> <type>text[]</></entry>
-      <entry><type>numeric</></entry>
-      <entry>get numeric value for key path (<literal>NULL</> if not numeric or not present)</entry>
-      <entry><literal>'foo =&gt; {bar =&gt; 99}'::hstore #^&gt; '{foo,bar}'</literal></entry>
-      <entry><literal>99</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>#?&gt;</> <type>text[]</></entry>
-      <entry><type>boolean</></entry>
-      <entry>get boolean value for key path (<literal>NULL</> if not boolean or not present)</entry>
-      <entry><literal>'foo =&gt; {bar =&gt; true}'::hstore #?&gt; '{foo,bar}'</literal></entry>
-      <entry><literal>true</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>%&gt;</> <type>text</></entry>
-      <entry><type>hstore</></entry>
-      <entry>get hstore value for key (<literal>NULL</> if not present)</entry>
-      <entry><literal>'foo =&gt; {bar =&gt; 99}'::hstore %&gt; 'foo'</literal></entry>
-      <entry><literal>"bar"=&gt;99</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>%&gt;</> <type>integer</></entry>
-      <entry><type>hstore</></entry>
-      <entry>get hstore value array index (<literal>NULL</> if not present)</entry>
-      <entry><literal>'[1, 2, {foo=>hi}]'::hstore %> 2</literal></entry>
-      <entry><literal>"foo"=&gt;"hi"</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>#%&gt;</> <type>text[]</></entry>
-      <entry><type>hstore</></entry>
-      <entry>get hstore value for key path (<literal>NULL</> if not present)</entry>
-      <entry><literal>'a =&gt; 1, b =&gt; {c =&gt; [44,44]}'::hstore #%&gt; '{b,c}'</literal></entry>
-      <entry><literal>[44, 44]</literal></entry>
-     </row>
-
      <row>
       <entry><type>hstore</> <literal>-&gt;</> <type>text[]</></entry>
-      <entry><type>text[]</></entry>
       <entry>get values for keys (<literal>NULL</> if not present)</entry>
       <entry><literal>'a=&gt;x, b=&gt;y, c=&gt;z'::hstore -&gt; ARRAY['c','a']</literal></entry>
-      <entry><literal>{z,x}</literal></entry>
+      <entry><literal>{"z","x"}</literal></entry>
      </row>
 
      <row>
       <entry><type>hstore</> <literal>||</> <type>hstore</></entry>
-      <entry><type>hstore</></entry>
       <entry>concatenate <type>hstore</>s</entry>
       <entry><literal>'a=&gt;b, c=&gt;d'::hstore || 'c=&gt;x, d=&gt;q'::hstore</literal></entry>
       <entry><literal>"a"=&gt;"b", "c"=&gt;"x", "d"=&gt;"q"</literal></entry>
@@ -363,103 +128,62 @@ SELECT 'a=&gt;1,a=&gt;2'::hstore;
 
      <row>
       <entry><type>hstore</> <literal>?</> <type>text</></entry>
-      <entry><type>boolean</></entry>
       <entry>does <type>hstore</> contain key?</entry>
       <entry><literal>'a=&gt;1'::hstore ? 'a'</literal></entry>
-      <entry><literal>true</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>?</> <type>integer</></entry>
-      <entry><type>boolean</></entry>
-      <entry>does <type>hstore</> contain array index?</entry>
-      <entry><literal>'[a,b,c]'::hstore ? 2</literal></entry>
-      <entry><literal>true</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>#?</> <type>text[]</></entry>
-      <entry><type>boolean</></entry>
-      <entry>does <type>hstore</> contain key path?</entry>
-      <entry><literal>'[1, 2, {foo=&gt;hi}]'::hstore #? '{2,foo}'</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
       <entry><type>hstore</> <literal>?&amp;</> <type>text[]</></entry>
-      <entry><type>boolean</></entry>
       <entry>does <type>hstore</> contain all specified keys?</entry>
       <entry><literal>'a=&gt;1,b=&gt;2'::hstore ?&amp; ARRAY['a','b']</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
       <entry><type>hstore</> <literal>?|</> <type>text[]</></entry>
-      <entry><type>boolean</></entry>
       <entry>does <type>hstore</> contain any of the specified keys?</entry>
       <entry><literal>'a=&gt;1,b=&gt;2'::hstore ?| ARRAY['b','c']</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
       <entry><type>hstore</> <literal>@&gt;</> <type>hstore</></entry>
-      <entry><type>boolean</></entry>
       <entry>does left operand contain right?</entry>
       <entry><literal>'a=&gt;b, b=&gt;1, c=&gt;NULL'::hstore @&gt; 'b=&gt;1'</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
       <entry><type>hstore</> <literal>&lt;@</> <type>hstore</></entry>
-      <entry><type>boolean</></entry>
       <entry>is left operand contained in right?</entry>
       <entry><literal>'a=&gt;c'::hstore &lt;@ 'a=&gt;b, b=&gt;1, c=&gt;NULL'</literal></entry>
-      <entry><literal>false</literal></entry>
+      <entry><literal>f</literal></entry>
      </row>
 
      <row>
       <entry><type>hstore</> <literal>-</> <type>text</></entry>
-      <entry><type>hstore</></entry>
       <entry>delete key from left operand</entry>
       <entry><literal>'a=&gt;1, b=&gt;2, c=&gt;3'::hstore - 'b'::text</literal></entry>
-      <entry><literal>"a"=&gt;1, "c"=&gt;3</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>-</> <type>integer</></entry>
-      <entry><type>hstore</></entry>
-      <entry>delete index from left operand</entry>
-      <entry><literal>'[2, 3, 4, 6, 8]'::hstore - 1</literal></entry>
-      <entry><literal>[2, 4, 6, 8]</literal></entry>
+      <entry><literal>"a"=&gt;"1", "c"=&gt;"3"</literal></entry>
      </row>
 
      <row>
       <entry><type>hstore</> <literal>-</> <type>text[]</></entry>
-      <entry><type>hstore</></entry>
       <entry>delete keys from left operand</entry>
       <entry><literal>'a=&gt;1, b=&gt;2, c=&gt;3'::hstore - ARRAY['a','b']</literal></entry>
-      <entry><literal>"c"=&gt;3</literal></entry>
+      <entry><literal>"c"=&gt;"3"</literal></entry>
      </row>
 
      <row>
       <entry><type>hstore</> <literal>-</> <type>hstore</></entry>
-      <entry><type>hstore</></entry>
       <entry>delete matching pairs from left operand</entry>
       <entry><literal>'a=&gt;1, b=&gt;2, c=&gt;3'::hstore - 'a=&gt;4, b=&gt;2'::hstore</literal></entry>
-      <entry><literal>"a"=&gt;1, "c"=&gt;3</literal></entry>
-     </row>
-
-     <row>
-      <entry><type>hstore</> <literal>#-</> <type>text[]</></entry>
-      <entry><type>hstore</></entry>
-      <entry>delete key path from left operand</entry>
-      <entry><literal>'{a =&gt; {b =&gt; { c =&gt; [1,2]}}}'::hstore #- '{a,b,c,0}'</literal></entry>
-      <entry><literal>"a"=&gt;{"b"=&gt;{"c"=&gt;[2]}}</literal></entry>
+      <entry><literal>"a"=&gt;"1", "c"=&gt;"3"</literal></entry>
      </row>
 
      <row>
       <entry><type>record</> <literal>#=</> <type>hstore</></entry>
-      <entry><type>record</></entry>
       <entry>replace fields in <type>record</> with matching values from <type>hstore</></entry>
       <entry>see Examples section</entry>
       <entry></entry>
@@ -467,7 +191,6 @@ SELECT 'a=&gt;1,a=&gt;2'::hstore;
 
      <row>
       <entry><literal>%%</> <type>hstore</></entry>
-      <entry><type>text[]</></entry>
       <entry>convert <type>hstore</> to array of alternating keys and values</entry>
       <entry><literal>%% 'a=&gt;foo, b=&gt;bar'::hstore</literal></entry>
       <entry><literal>{a,foo,b,bar}</literal></entry>
@@ -475,7 +198,6 @@ SELECT 'a=&gt;1,a=&gt;2'::hstore;
 
      <row>
       <entry><literal>%#</> <type>hstore</></entry>
-      <entry><type>text[]</></entry>
       <entry>convert <type>hstore</> to two-dimensional key/value array</entry>
       <entry><literal>%# 'a=&gt;foo, b=&gt;bar'::hstore</literal></entry>
       <entry><literal>{{a,foo},{b,bar}}</literal></entry>
@@ -486,22 +208,12 @@ SELECT 'a=&gt;1,a=&gt;2'::hstore;
   </table>
 
   <note>
-   <para>
-    As of PostgreSQL 8.4, the <literal>@&gt;</> and <literal>@&lt;</> operators can go deep:
-<programlisting>
-postgres=# SELECT 'a=&gt;[1,2,{c=&gt;3, x=&gt;4}], c=&gt;b'::hstore @&gt; 'a=&gt;[{c=&gt;3}]';
- ?column? 
-----------
- t
-</programlisting>
-   </para>
-
-   <para>
-    Prior to PostgreSQL 8.2, the containment operators <literal>@&gt;</>
-    and <literal>&lt;@</> were called <literal>@</> and <literal>~</>,
-    respectively. These names are still available, but are deprecated and will
-    eventually be removed. Notice that the old names are reversed from the
-    convention formerly followed by the core geometric data types!
+  <para>
+   Prior to PostgreSQL 8.2, the containment operators <literal>@&gt;</>
+   and <literal>&lt;@</> were called <literal>@</> and <literal>~</>,
+   respectively. These names are still available, but are deprecated and will
+   eventually be removed. Notice that the old names are reversed from the
+   convention formerly followed by the core geometric data types!
    </para>
   </note>
 
@@ -534,7 +246,7 @@ postgres=# SELECT 'a=&gt;[1,2,{c=&gt;3, x=&gt;4}], c=&gt;b'::hstore @&gt; 'a=&gt
       <entry>construct an <type>hstore</> from an array, which may be either
        a key/value array, or a two-dimensional array</entry>
       <entry><literal>hstore(ARRAY['a','1','b','2']) || hstore(ARRAY[['c','3'],['d','4']])</literal></entry>
-      <entry><literal>a=&gt;"1", b=&gt;"2", c=&gt;"3", d=&gt;"4"</literal></entry>
+      <entry><literal>a=&gt;1, b=&gt;2, c=&gt;3, d=&gt;4</literal></entry>
      </row>
 
      <row>
@@ -545,14 +257,6 @@ postgres=# SELECT 'a=&gt;[1,2,{c=&gt;3, x=&gt;4}], c=&gt;b'::hstore @&gt; 'a=&gt
       <entry><literal>"a"=&gt;"1","b"=&gt;"2"</literal></entry>
      </row>
 
-     <row>
-      <entry><function>hstore(text, hstore)</function></entry>
-      <entry><type>hstore</type></entry>
-      <entry>make a nested <type>hstore</></entry>
-      <entry><literal>hstore('xxx', 'foo=&gt;t, bar=&gt;3.14'::hstore)</literal></entry>
-      <entry><literal>"xxx"=&gt;{"bar"=&gt;3.14, "foo"=&gt;t}</literal></entry>
-     </row>
-
      <row>
       <entry><function>hstore(text, text)</function></entry>
       <entry><type>hstore</type></entry>
@@ -561,54 +265,6 @@ postgres=# SELECT 'a=&gt;[1,2,{c=&gt;3, x=&gt;4}], c=&gt;b'::hstore @&gt; 'a=&gt
       <entry><literal>"a"=&gt;"b"</literal></entry>
      </row>
 
-     <row>
-      <entry><function>hstore(text, numeric)</function></entry>
-      <entry><type>hstore</type></entry>
-      <entry>make single-item <type>hstore</></entry>
-      <entry><literal>hstore('a', 3.14)</literal></entry>
-      <entry><literal>"a"=&gt;3.14</literal></entry>
-     </row>
-
-     <row>
-      <entry><function>hstore(text, boolean)</function></entry>
-      <entry><type>hstore</type></entry>
-      <entry>make single-item <type>hstore</></entry>
-      <entry><literal>hstore('a', true)</literal></entry>
-      <entry><literal>"a"=&gt;t</literal></entry>
-     </row>
-
-     <row>
-      <entry><function>hstore(text)</function></entry>
-      <entry><type>hstore</type></entry>
-      <entry>make scalar string <type>hstore</></entry>
-      <entry><literal>hstore('foo')</literal></entry>
-      <entry><literal>"foo"</literal></entry>
-     </row>
-
-     <row>
-      <entry><function>hstore(numeric)</function></entry>
-      <entry><type>hstore</type></entry>
-      <entry>make scalar numeric <type>hstore</></entry>
-      <entry><literal>hstore(42)</literal></entry>
-      <entry><literal>42</literal></entry>
-     </row>
-
-     <row>
-      <entry><function>hstore(boolean)</function></entry>
-      <entry><type>hstore</type></entry>
-      <entry>make scalar boolean <type>hstore</></entry>
-      <entry><literal>hstore(false)</literal></entry>
-      <entry><literal>f</literal></entry>
-     </row>
-
-     <row>
-      <entry><function>array_to_hstore(anyarray)</function></entry>
-      <entry><type>hstore</type></entry>
-      <entry>construct an array <type>hstore</> from an array</entry>
-      <entry><literal>array_to_hstore('{{1,1,4},{23,3,5}}'::int[])</literal></entry>
-      <entry><literal>[[1, 1, 4], [23, 3, 5]]</literal></entry>
-     </row>
-
      <row>
       <entry><function>akeys(hstore)</function><indexterm><primary>akeys</primary></indexterm></entry>
       <entry><type>text[]</type></entry>
@@ -649,18 +305,6 @@ b
 </programlisting></entry>
      </row>
 
-     <row>
-      <entry><function>hvals(hstore)</function><indexterm><primary>hvals</primary></indexterm></entry>
-      <entry><type>setof hstore</type></entry>
-      <entry>get <type>hstore</>'s values as a set of <type>hstore</>s</entry>
-      <entry><literal>hvals('a=&gt;[1,2],b=&gt;{foo=&gt;1}')</literal></entry>
-      <entry>
-<programlisting>
-[1, 2]
-"foo"=&gt;1
-</programlisting></entry>
-     </row>
-
      <row>
       <entry><function>hstore_to_array(hstore)</function><indexterm><primary>hstore_to_array</primary></indexterm></entry>
       <entry><type>text[]</type></entry>
@@ -683,7 +327,7 @@ b
       <entry><type>json</type></entry>
       <entry>get <type>hstore</type> as a <type>json</type> value</entry>
       <entry><literal>hstore_to_json('"a key"=&gt;1, b=&gt;t, c=&gt;null, d=&gt;12345, e=&gt;012345, f=&gt;1.234, g=&gt;2.345e+4')</literal></entry>
-      <entry><literal>{ "b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}</literal></entry>
+      <entry><literal>{"a key": "1", "b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4"}</literal></entry>
      </row>
 
      <row>
@@ -691,15 +335,7 @@ b
       <entry><type>json</type></entry>
       <entry>get <type>hstore</type> as a <type>json</type> value, but attempt to distinguish numerical and Boolean values so they are unquoted in the JSON</entry>
       <entry><literal>hstore_to_json_loose('"a key"=&gt;1, b=&gt;t, c=&gt;null, d=&gt;12345, e=&gt;012345, f=&gt;1.234, g=&gt;2.345e+4')</literal></entry>
-      <entry><literal>{ "b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}</literal></entry>
-     </row>
-
-     <row>
-      <entry><function>json_to_hstore(json)</function><indexterm><primary>json_to_hstore</primary></indexterm></entry>
-      <entry><type>hstore</type></entry>
-      <entry>get <type>json</type> as an <type>hstore</type> value</entry>
-      <entry><literal>json_to_hstore('{"a key": "1", "b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4"}')</literal></entry>
-      <entry><literal>"b"=&gt;"t", "c"=&gt;NULL, "d"=&gt;"12345", "e"=&gt;"012345", "f"=&gt;"1.234", "g"=&gt;"2.345e+4", "a key"=&gt;"1"</literal></entry>
+      <entry><literal>{"a key": 1, "b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4}</literal></entry>
      </row>
 
      <row>
@@ -707,7 +343,7 @@ b
       <entry><type>hstore</type></entry>
       <entry>extract a subset of an <type>hstore</></entry>
       <entry><literal>slice('a=&gt;1,b=&gt;2,c=&gt;3'::hstore, ARRAY['b','c','x'])</literal></entry>
-      <entry><literal>"b"=&gt;2, "c"=&gt;3</literal></entry>
+      <entry><literal>"b"=&gt;"2", "c"=&gt;"3"</literal></entry>
      </row>
 
      <row>
@@ -724,20 +360,6 @@ b
 </programlisting></entry>
      </row>
 
-     <row>
-      <entry><function>each_hstore(hstore)</function><indexterm><primary>each_hstore</primary></indexterm></entry>
-      <entry><type>setof(key text, value text)</type></entry>
-      <entry>get <type>hstore</>'s keys and values as a set</entry>
-      <entry><literal>select * from each_hstore('a=&gt;1,b=&gt;2')</literal></entry>
-      <entry>
-<programlisting>
- key | value
------+-------
- a   | 1
- b   | 2
-</programlisting></entry>
-     </row>
-
      <row>
       <entry><function>exist(hstore,text)</function><indexterm><primary>exist</primary></indexterm></entry>
       <entry><type>boolean</type></entry>
@@ -754,36 +376,12 @@ b
       <entry><literal>f</literal></entry>
      </row>
 
-     <row>
-      <entry><function>hstore_typeof(hstore)</function><indexterm><primary>hstore_typeof</primary></indexterm></entry>
-      <entry><type>text</type></entry>
-      <entry>get the type of an <type>hstore</> value, one of <literal>hash</>, <literal>array</>, <literal>string</>, <literal>numeric</>, <literal>bool</>, or <literal>null</></entry>
-      <entry><literal>hstore_typeof('[1]')</literal></entry>
-      <entry><literal>array</literal></entry>
-     </row>
-
-     <row>
-      <entry><function>replace(hstore,text[],hstore)</function><indexterm><primary>replace</primary></indexterm></entry>
-      <entry><type>hstore</type></entry>
-      <entry>replace value at the specified path</entry>
-      <entry><literal>replace('a=&gt;1,b=&gt;{c=&gt;3,d=&gt;[4,5,6]}'::hstore,'{b,d}', '1')</literal></entry>
-      <entry><literal>"a"=&gt;1, "b"=&gt;{"c"=&gt;3, "d"=&gt;1}</literal></entry>
-     </row>
-
-     <row>
-      <entry><function>concat_path(hstore,text[],hstore)</function><indexterm><primary>concat_path</primary></indexterm></entry>
-      <entry><type>hstore</type></entry>
-      <entry>concatenate <type>hstore</> value at the specified path</entry>
-      <entry><literal>concat_path('b=&gt;{c=&gt;3,d=&gt;[4,5,6]}'::hstore,'{b,d}', '1')</literal></entry>
-      <entry><literal>"b"=&gt;{"c"=&gt;3, "d"=&gt;[4, 5, 6, 1]}</literal></entry>
-     </row>
-
      <row>
       <entry><function>delete(hstore,text)</function><indexterm><primary>delete</primary></indexterm></entry>
       <entry><type>hstore</type></entry>
       <entry>delete pair with matching key</entry>
       <entry><literal>delete('a=&gt;1,b=&gt;2','b')</literal></entry>
-      <entry><literal>"a"=>1</literal></entry>
+      <entry><literal>"a"=>"1"</literal></entry>
      </row>
 
      <row>
@@ -791,7 +389,7 @@ b
       <entry><type>hstore</type></entry>
       <entry>delete pairs with matching keys</entry>
       <entry><literal>delete('a=&gt;1,b=&gt;2,c=&gt;3',ARRAY['a','b'])</literal></entry>
-      <entry><literal>"c"=>3</literal></entry>
+      <entry><literal>"c"=>"3"</literal></entry>
      </row>
 
      <row>
@@ -799,22 +397,14 @@ b
       <entry><type>hstore</type></entry>
       <entry>delete pairs matching those in the second argument</entry>
       <entry><literal>delete('a=&gt;1,b=&gt;2','a=&gt;4,b=&gt;2'::hstore)</literal></entry>
-      <entry><literal>"a"=>1</literal></entry>
+      <entry><literal>"a"=>"1"</literal></entry>
      </row>
 
      <row>
       <entry><function>populate_record(record,hstore)</function><indexterm><primary>populate_record</primary></indexterm></entry>
       <entry><type>record</type></entry>
       <entry>replace fields in <type>record</> with matching values from <type>hstore</></entry>
-      <entry>see Populating Records section</entry>
-      <entry></entry>
-     </row>
-
-     <row>
-      <entry><function>hstore_print(hstore,bool,bool,bool,bool,bool)</function></entry>
-      <entry><type>text</type></entry>
-      <entry>Format an <type>hstore</> value as text with various formatting options</entry>
-      <entry>see Printing section</entry>
+      <entry>see Examples section</entry>
       <entry></entry>
      </row>
 
@@ -825,8 +415,7 @@ b
   <note>
    <para>
      The function <function>hstore_to_json</function> is used when an <type>hstore</type>
-     value is cast to <type>json</type>. Conversely, the function <function>json_to_hstore</function>
-     is used when a <type>json</type> value is cast to <type>hstore</type>.
+     value is cast to <type>json</type>.
    </para>
   </note>
 
@@ -837,14 +426,6 @@ b
     but it will reject non-record types with a run-time error.
    </para>
   </note>
-
-  <note>
-    <para>
-      The <literal>hstore_typeof</> function's <literal>null</> return value should not be confused
-      with a SQL NULL.  While calling <literal>hstore_typeof('null'::hstore)</> will return
-       <literal>null</>, calling <literal>hstore_typeof(NULL::hstore)</> will return a SQL NULL.
-    </para>
-  </note>
  </sect2>
 
  <sect2>
@@ -858,13 +439,6 @@ b
 CREATE INDEX hidx ON testhstore USING GIST (h);
 
 CREATE INDEX hidx ON testhstore USING GIN (h);
-</programlisting>
-
-<para>
-GIN index opclass gin_hstore_hash_ops supports <literal>@&gt;</> operator.
-</para>
-<programlisting>
-CREATE INDEX hidx ON testhstore USING GIN (h gin_hstore_hash_ops);
 </programlisting>
 
   <para>
@@ -883,155 +457,6 @@ CREATE INDEX hidx ON testhstore USING HASH (h);
 </programlisting>
  </sect2>
 
- <sect2>
-  <title>Printing</title>
-
-  <para>
-   The <literal>hstore_print()</> function takes a single <type>hstore</>
-   value and formats it as text. By default, the returned value is identical
-   to the text format used to return <type>hstore</> values in queries.
-   However, <literal>hstore_print()</> also accepts a number of optional
-   parameters, passed as <type>boolean</> values, to format an <type>hstore</>
-   in various ways. The parameters include:
-  </para>
-
-  <table id="hstore-print-table">
-   <title><literal>hstore_print()</> Parameters</title>
-
-   <tgroup cols="4">
-    <thead>
-     <row>
-      <entry>Parameter</entry>
-      <entry>Description</entry>
-      <entry>Example</entry>
-      <entry>Result</entry>
-     </row>
-    </thead>
-
-    <tbody>
-
-     <row>
-      <entry><literal>pretty_print</></entry>
-      <entry>Adds newlines between values and indents nested hashes and arrays.</entry>
-      <entry><literal>hstore_print('a=&gt;t, t=&gt;"f", arr=&gt;[1,2,"3"]', pretty_print := true)</literal></entry>
-      <entry>
-<programlisting>
- hstore_print 
---------------
- "a"=&gt;t,     +
- "t"=&gt;"f",   +
- "arr"=&gt;     +
- [           +
-     1,      +
-     2,      +
-     "3"     +
- ]
-</programlisting></entry>
-     </row>
-
-     <row>
-      <entry><literal>array_curly_braces</></entry>
-      <entry>Wraps arrays in curly braces instead of brackets</entry>
-      <entry><literal>hstore_print('arr=&gt;[1,2,"3"]', array_curly_braces := true)</literal></entry>
-      <entry><literal>"arr"=&gt;{1, 2, "3"}</literal></entry>
-     </row>
-
-     <row>
-      <entry><literal>root_hash_decorated</></entry>
-      <entry>Wraps the root has object, if three is one, in curly braces</entry>
-      <entry><literal>hstore_print('arr=&gt;[1,2,"3"]', root_hash_decorated := true)</literal></entry>
-      <entry><literal>{"arr"=&gt;[1, 2, "3"]}</literal></entry>
-     </row>
-
-     <row>
-      <entry><literal>json</></entry>
-      <entry>Returns the value as a JSON string</entry>
-      <entry><literal>hstore_print('arr=&gt;[1,2,"3"]', json := true)</literal></entry>
-      <entry><literal>"arr": [1, 2, "3"]</literal></entry>
-     </row>
-
-     <row>
-      <entry><literal>loose</></entry>
-      <entry>Try to parse numbers and booleans</entry>
-      <entry><literal>hstore_print('arr=&gt;[1,"2","t"]', loose := true)</literal></entry>
-      <entry><literal>"arr"=>[1, 2, t]</literal></entry>
-     </row>
-
-    </tbody>
-   </tgroup>
-  </table>
-
-  <para>
-    These options can be combined for different effects. For example, to pretty-print
-    an <type>hstore</> value with the root hash decorated and array curly braces,
-    simply pass all three values:
-<programlisting>
-# SELECT hstore_print(
-    'arr=&gt;[1,2,"3"]',
-    root_hash_decorated := true,
-    array_curly_braces  := true,
-    pretty_print        := true
-);
- hstore_print 
---------------
- {           +
-     "arr"=&gt; +
-     {       +
-         1,  +
-         2,  +
-         "3" +
-     }       +
- }
-(1 row)
-</programlisting>
-  </para>
-
- </sect2>
-
-
- <sect2>
-  <title>Populating Records</title>
-
-  <para>
-   The <literal>populate_record()</> converts an <type>hstore</> hash value to
-   a pre-defined record type. Pass any record value (even <literal>NULL</>) as
-   the first argument and the <type>hstore</> to convert to that type as the
-   second argument. At its simplest <literal>populate_record()</> simply maps
-   keys to column names and values to record values:
-<programlisting>
-CREATE TABLE test (col1 integer, col2 text, col3 text);
-
-SELECT * FROM populate_record(
-    null::test,
-    '"col1"=&gt;"456", "col2"=&gt;"zzz"'
-);
- col1 | col2 | col3 
-------+------+------
-  456 | zzz  | 
-(1 row)
-</programlisting>
-  </para>
-
-  <para>
-   But <literal>populate_record()</> supports more complicated records and nested
-   <type>hstore</> values, as well. It makes an effort to convert
-   from <type>hstore</> data types to PostgreSQL types, including arrays,
-   <type>json</>, and <type>hstore</> values:
-<programlisting>
-CREATE type stuff AS (i int, h hstore, a int[], j json);
-
-SELECT * FROM populate_record(
-    null::stuff,
-    'i=&gt;2, h=&gt;{b=&gt;3}, a=&gt;{7,8,9}, j=&gt;{a=&gt;{1,2,3}}'
-);
- i |   h    |    a    |        j         
----+--------+---------+------------------
- 2 | "b"=&gt;3 | {7,8,9} | {"a": [1, 2, 3]}
-</programlisting>
-  </para>
-
- </sect2>
-
  <sect2>
   <title>Examples</title>
 
@@ -1058,7 +483,21 @@ INSERT INTO test VALUES (123, 'foo', 'bar');
 SELECT hstore(t) FROM test AS t;
                    hstore                    
 ---------------------------------------------
- "col1"=&gt;123, "col2"=&gt;"foo", "col3"=&gt;"bar"
+ "col1"=&gt;"123", "col2"=&gt;"foo", "col3"=&gt;"bar"
+(1 row)
+</programlisting>
+  </para>
+
+  <para>
+   Convert an <type>hstore</> to a predefined <type>record</> type:
+<programlisting>
+CREATE TABLE test (col1 integer, col2 text, col3 text);
+
+SELECT * FROM populate_record(null::test,
+                              '"col1"=&gt;"456", "col2"=&gt;"zzz"');
+ col1 | col2 | col3 
+------+------+------
+  456 | zzz  | 
 (1 row)
 </programlisting>
   </para>
@@ -1128,14 +567,11 @@ SELECT key, count(*) FROM
  <sect2>
   <title>Compatibility</title>
 
-  <para>The internal representation of <type>hstore</> has been updated
-   a couple of times in its history. Data types and nested structures were
-   added in PostgreSQL 9.4, while capacity and improved index support were
-   introduced in Postgrsql 9.0. These changes present no obstacle for
+  <para>
+   As of PostgreSQL 9.0, <type>hstore</> uses a different internal
+   representation than previous versions. This presents no obstacle for
    dump/restore upgrades since the text representation (used in the dump) is
-   unchanged. However, <type>hstore</> values dumped from 9.4 cannot be
-   loaded into earlier versions of PostgreSQL if they contain nested values
-   or typed data.
+   unchanged.
   </para>
 
   <para>
index fc015e31bd1c1047adfbef60bbb632c8d5cb82a2..3760f06c4de2fa34cd15a15fc5664283304fb8a7 100644 (file)
@@ -5,8 +5,6 @@
  *
  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
  *
- * NOTE. JSONB type is designed to be binary compatible with hstore.
- *
  * src/backend/utils/adt/jsonb_support.c
  *
  *-------------------------------------------------------------------------
index 42dd069a9f2b7181c8722416c5d01460532fe862..8e71cabf3e57274ac0636aa04a9afbc1b0683571 100644 (file)
@@ -130,7 +130,7 @@ crc32_JsonbValue(JsonbValue *v, uint32 r)
                                NumericGetDatum(v->numeric)));
            break;
        default:
-           elog(ERROR, "wrong hstore scalar type");
+           elog(ERROR, "wrong jsonb scalar type");
    }
 
    FIN_CRC32(crc);